Sharkey/Misskey抽選ツール | 一片痴心俱成灰

Sharkey/Misskey抽選ツール

2025-03-13 Views 砺石录926字5 min read

基于ChatGPT的Sharkey/Misskey抽奖脚本。

仅使用所在实例测试能跑通,反正只是我自己用的(你)过低版本Misskey应该用不了。

思路仿照Mastodon抽奖脚本,先拉取通知,填入相关帖子链接,选取抽奖条件,输入结果。

抽奖方式有四种。

  1. 仅转发
  2. 仅表情回应(Mastodon端为点赞)
  3. 1+2
  4. 1或2

使用方法:打开域名/my/notifications页面,确保界面语言为中文,按F12-Console-粘贴脚本,根据提示信息填入,最后结果会自动复制到剪贴板上。

也放一下to-do-list看我有没有空继续做……

(async () => {
    // **滚动加载所有通知**
    async function scrollToLoadMoreNotifications() {
        const timeout = Date.now() + 10000; // 最长等待 10 秒
        let lastHeight = document.body.scrollHeight;

        while (Date.now() < timeout) {
            window.scrollTo(0, document.body.scrollHeight);
            await new Promise(resolve => setTimeout(resolve, 1000));

            if (document.body.scrollHeight === lastHeight) break;
            lastHeight = document.body.scrollHeight;
        }

        if (Date.now() >= timeout) {
            alert("⏳ 加载通知超时,可能未加载完所有通知。");
        }
    }

    // **等待通知加载**
    async function waitForNotifications() {
        const timeout = Date.now() + 10000;
        while (document.querySelectorAll('article').length === 0 && Date.now() < timeout) {
            await new Promise(resolve => setTimeout(resolve, 500));
        }

        if (Date.now() >= timeout) {
            alert("⏳ 无法加载通知,可能没有新通知。");
        } else {
            console.log("通知加载完成!");
        }
    }

    // **确保在 Misskey 通知页面**
    if (!window.location.pathname.startsWith("/my/notifications")) {
        alert("❌ 请先打开 Misskey 的通知页面(/my/notifications)再运行此脚本!");
        return;
    }

    await scrollToLoadMoreNotifications();
    await waitForNotifications();

    // **获取帖子 ID**
    function validatePostUrl(url) {
        if (!url || !url.includes("/notes/")) {
            alert("❌ 请输入正确的帖子链接!");
            return null;
        }
        return url.split("/notes/").pop().split("?")[0];
    }

    const postId = validatePostUrl(prompt("请输入帖子链接(如 https://misskey.io/notes/xxxxx):"));
    if (!postId) return;

    console.log("指定帖子 ID:", postId);

    // **获取抽奖人数**
    function validateDrawCount(count) {
        return count >= 1 ? count : null;
    }

    const drawCount = validateDrawCount(parseInt(prompt("请输入中奖人数(默认 1):") || "1"));
    if (!drawCount) {
        alert("❌ 中奖人数必须至少为 1!");
        return;
    }

    // **选择筛选方式**
    const filterType = prompt(
        "请输入筛选方式:\n" +
        "1️⃣ 仅抽转发(Renote)用户\n" +
        "2️⃣ 仅抽回应(Reaction)用户\n" +
        "3️⃣ 转发 + 回应的用户\n" +
        "4️⃣ 转发或回应的用户\n" +
        "(输入 1、2、3 或 4,默认 1)"
    ) || "1";

    // **获取互动用户**
    function getUsersByType(type) {
        const users = new Set();
        document.querySelectorAll('.MkNotification-root-9Bba').forEach(notification => {
            const text = notification.innerText;
            const noteLink = notification.querySelector(`a[href*="/notes/${postId}"]`);
            if (!noteLink) return;

            notification.querySelectorAll('a[href^="/@"]').forEach(userLink => {
                const username = userLink.getAttribute("title")?.split('@')[0];
                if (!username) return;

                if (type === "renote" && /转发了|Renote/.test(text)) users.add(username);
                if (type === "reaction" && /回应了|Reaction/.test(text)) users.add(username);
            });

            notification.querySelectorAll('.MkNotification-reactionsItem-ENB4 a[title]').forEach(item => {
                users.add(item.getAttribute("title")?.split('@')[0]);
            });
        });

        console.log(type === "renote" ? "转发用户:" : "回应用户:", users);
        return [...users];
    }

    const renoteUsers = getUsersByType("renote");
    const reactionUsers = getUsersByType("reaction");

    // **筛选符合条件的用户**
    let eligibleUsers = [];
    if (filterType === "1") {
        eligibleUsers = renoteUsers;
    } else if (filterType === "2") {
        eligibleUsers = reactionUsers;
    } else if (filterType === "3") {
        eligibleUsers = renoteUsers.filter(user => reactionUsers.includes(user));
    } else {
        eligibleUsers = [...new Set([...renoteUsers, ...reactionUsers])];
    }

    if (eligibleUsers.length === 0) {
        alert("❌ 没有符合条件的用户!");
        return;
    }

    console.log("符合条件的用户:", eligibleUsers);

    // **随机抽取中奖者**
    function pickWinners(users, count) {
        const shuffled = [...users];
        for (let i = shuffled.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
        }
        return shuffled.slice(0, count);
    }

    const winners = pickWinners(eligibleUsers, drawCount);
    if (winners.length === 0) return;

    // **生成中奖公告**
    const winnerText = `🎉 抽奖结果 🎉\n\n` +
        `感谢大家的参与,本次中奖者是:\n@${winners.join("\n@")}` +
        `\n\n请私信领奖!🎁 #Misskey抽奖`;

    alert(winnerText);

    // **自动复制到剪贴板**
    try {
        await navigator.clipboard.writeText(winnerText);
        alert("✅ 中奖信息已复制!");
    } catch {
        alert("❌ 复制失败,请手动复制!");
    }
})();
EOF