Greasy Fork is available in English.

微信网页版导出:通讯录、群成员名单、聊天列表

rt, 课代表核对名单用,入口藏在头像右边的下拉菜单里,以及群标题下拉的群成员列表的第一项

// ==UserScript==
// @name         微信网页版导出:通讯录、群成员名单、聊天列表
// @namespace    https://userscript.snomiao.com/
// @version      0.2
// @description  rt, 课代表核对名单用,入口藏在头像右边的下拉菜单里,以及群标题下拉的群成员列表的第一项
// @author       snomiao@gmail.com
// @icon         https://res.wx.qq.com/zh_CN/htmledition/v2/images/favicon31e225.ico
// @match        https://wx.qq.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    var 取元素列表 = e => [...document.querySelectorAll(e)]
    var 取元素文字 = e => e.textContent
    var 睡 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
    var 直到 = async(ms, fn) => {
        var ret, exit = 0;
        setTimeout(() => exit = 1, ms);
        while (!exit && !(ret = await fn()))
            await 睡(1)
        return ret
    }
    var 创建元素 = (html) => {
        var div = document.createElement("div")
        div.innerHTML = html
        return div.firstChild
    }
    var 下载文件 = (文件名, 内容) => {
        const a = document.createElement('a');
        a.setAttribute('download', 文件名);
        a.setAttribute('href', 内容);
        a.click();
    }
    var 下载文本文件 = (文件名, 文本) => 下载文件(文件名, URL.createObjectURL(new Blob([文本])))
    var 请复制 = (text) => {
        window.prompt("Copy to clipboard: Ctrl+C, Enter", text);
    }
    var 功能列表 = [
        { 类型: "群成员", 名称: "群成员列表", 选: ".member.ng-scope>p.nickname" },
        { 类型: "下拉", 名称: "聊天列表", 选: ".chat_list .nickname_text" },
        { 类型: "下拉", 名称: "通讯录", 选: ".contact_list .nickname" },
    ];
    功能列表.forEach(功能 => {
        功能.按钮标题 = "导出" + 功能.名称 + (功能.备注 || "")
        功能.触发函数 = () => {
            var 导出文本 = [功能.名称].concat(取元素列表(功能.选).map(取元素文字)).join('\n')
                // var 文件名 = 功能.名称 + ".csv"
                // 下载文本文件(文件名, 导出文本)
                // console.log(导出文本)
            请复制(导出文本)
        }
    })
    document.addEventListener("click", async() => {
            var 菜单 = await 直到(200, () => document.querySelector("#mmpop_system_menu .dropdown_menu"))
            菜单 && 功能列表.filter(e => e.类型 == "下拉").reverse().map(({ 按钮标题, 触发函数 }) => {
                if (菜单["功能_" + 按钮标题])
                    return;
                菜单["功能_" + 按钮标题] = 1
                var 新元素 = 创建元素(`<li><a href="javascript:;" title="${按钮标题}"><i class="menuicon_feedback"></i>${按钮标题}</a></li>`)
                新元素.querySelector("a").addEventListener("click", 触发函数)
                菜单.insertBefore(新元素, 菜单.firstChild)
            })

            var 群成员列表 = await 直到(200, () => document.querySelector(".members .members_inner.scroll-content"))
            群成员列表 && 功能列表.filter(e => e.类型 == "群成员").reverse().map(({ 按钮标题, 触发函数 }) => {
                if (群成员列表["功能_" + 按钮标题])
                    return;
                群成员列表["功能_" + 按钮标题] = 1
                var 新元素 = 创建元素(`<div class="member opt">` +
                    `<i class="web_wechat_add_friends" title="${按钮标题}"></i>` +
                    `<p class="nickname ng-binding">${按钮标题}</p></div>`)
                新元素.addEventListener("click", 触发函数)
                群成员列表.insertBefore(新元素, 群成员列表.firstChild)
            })
        })
        // Your code here...
})();