导出群成员名单

导出 QQ 名单已确定哪些成员没有加群

目前為 2025-03-12 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         导出群成员名单
// @namespace    com.junwe.qgtool
// @version      0.1
// @description  导出 QQ 名单已确定哪些成员没有加群
// @author       jw23
// @match        https://qun.qq.com/*
// @require      https://cdn.jsdelivr.net/npm/[email protected]/papaparse.min.js
// @grant        GM_xmlhttpRequest
// @license MIT
// ==/UserScript==

(async function () {
    'use strict';

    let buttonContainer = await waitUtil("._operationSection_1pula_8>div");

    let btnWrapper = document.createElement("div");
    btnWrapper.className = "t-space-item";
    let btn = document.createElement("button");
    btn.textContent = "导出群成员"
    btn.className = "t-button t-button--theme-default t-button--variant-base";
    btnWrapper.appendChild(btn);

    btn.onclick = async () => {
        let group_id = prompt("请输入群号");
        try {
            let mems = await colllectMemebers(parseInt(group_id));
            console.log(mems);
            let csv = Papa.unparse(mems)
            exportJsonFile(csv, `${group_id}-members.csv`);
        } catch (err) {
            console.log("抓取群成员失败: ", err);
        }
    }
    buttonContainer.appendChild(btnWrapper)
})();

function getBkn() {
    let t = document.cookie.match(new RegExp(`(^| )skey=([^;]*)(;|$)`));
    let skey = t ? decodeURIComponent(t[2]) : "";
    let r_ = e => {
        let t = 5381;
        for (let n = 0, r = e.length; n < r; ++n)
            t += (t << 5) + e.charAt(n).charCodeAt(0);
        return String(t & 2147483647)
    }
    return r_(skey);
}
async function getMembers(group_id, start = 0, end = 9) {
    let bkn = getBkn();
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            url: `https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?bkn=${bkn}&ts=${Date.now()}`,
            method: "POST",
            headers: {
                "content-type": "application/x-www-form-urlencoded",
                "accept": "application/json, text/plain, */*",
                "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
                "Referer": "https://qun.qq.com/",
                "Referrer-Policy": "strict-origin-when-cross-origin"
            },
            data: `st=${start}&end=${end}&sort=1&gc=${group_id}&group_id=${group_id}&bkn=${bkn}`,
            onload: function (response) {
                console.log("Request successful!");
                let data = JSON.parse(response.responseText);
                resolve({ mems: data.mems, count: data.count });
            },
            onerror: function (response) {
                console.error("Request failed!");
                console.error("Response:", response);
                reject(response);
            },
            ontimeout: function (response) {
                console.error("Request timed out!");
                console.error("Response:", response);
                reject(response);
            }
        });
    })
}
async function waitUtil(cssSelector, timeout = 5000) {
    return new Promise((resolve, reject) => {
        let task = setTimeout(() => {
            let el = document.querySelector(cssSelector);
            if (el) {
                resolve(el);
            }
        }, 500)
        setTimeout(() => {
            clearInterval(task);
            resolve("timeout");
        }, timeout)
    })
}
async function colllectMemebers(group_id) {
    let start = 0;
    let step = 9
    let total = 200;
    let memebers = [];
    while (start <= total) {
        try {
            let { mems, count } = await getMembers(group_id, start, start + step);
            total = count;
            memebers.push(...mems);
            start += step;
            start++;
        } catch (err) {
            throw new Error("请求过程出现异常,请检查代码");
        }

    }
    return memebers;
}
function exportJsonFile(data, filename = 'data.json') {
   

    const blob = new Blob([data], { type: 'application/json' }); // 创建 Blob 对象
    const url = URL.createObjectURL(blob); // 创建 Blob URL

    const downloadLink = document.createElement('a'); // 创建下载链接
    downloadLink.href = url;
    downloadLink.download = filename; // 设置下载文件名
    downloadLink.textContent = '下载 JSON 文件'; // 链接文本 (可选)
    downloadLink.style.display = 'none'; // 隐藏链接 (可选,可以设置为可见)

    document.body.appendChild(downloadLink); // 将链接添加到文档 (必须添加到文档才能触发点击)
    downloadLink.click(); // 模拟点击下载链接
    document.body.removeChild(downloadLink); // 移除链接

    URL.revokeObjectURL(url); // 释放 Blob URL (可选,但建议释放)

    console.log(`JSON 文件 "${filename}" 导出成功!`);
}