旋转的五分硬币排队

旋转的五分硬币直播间深渊排队脚本

目前為 2022-01-21 提交的版本,檢視 最新版本

// ==UserScript==
// @name         旋转的五分硬币排队
// @namespace    http://tampermonkey.net/
// @version      0.0.10
// @description  旋转的五分硬币直播间深渊排队脚本
// @author       Mimiko
// @license      MIT
// @match        *://live.bilibili.com/3140454*
// @icon         http://i0.hdslb.com/bfs/activity-plat/static/20211202/dddbda27ce6f43bf18f5bca141752a99/fCo7evLooK.webp@128w
// @grant        GM_xmlhttpRequest
// ==/UserScript==

(() => {
    if (window.top !== window.self)
        return;
    const cacheId = new Set();
    const cacheName = new Map();
    const interval = 30e3;
    const observer = new MutationObserver(() => {
        pick();
        clearDanmaku();
    });
    const port = 9644;
    const setAdmin = new Set();
    const setWaiting = new Set();
    const speaker = new SpeechSynthesisUtterance();
    let isWaiting = false;
    const add = async (name, id) => {
        if (!validate(name, id))
            return;
        const data = await get(`http://localhost:${port}/queue/add?name=${name}`);
        if (!data)
            return;
        if (!data.status)
            speak(`${name}已经排过队了,序号是${data.idx}`);
        else
            speak(`${name}已经排队成功,序号是${data.idx}`);
    };
    const clearDanmaku = () => {
        const $el = document.getElementById('chat-items');
        if (!$el)
            return;
        $el.innerHTML = '';
    };
    const endWaiting = () => {
        if (!isWaiting)
            return;
        isWaiting = false;
        if (!setWaiting.size) {
            speak('没有人在等车');
            return;
        }
        const idx = Math.min(...setWaiting);
        setWaiting.clear();
        setCurrent(idx);
    };
    const find = async (name, id) => {
        if (!validate(name, id))
            return;
        const data = await get(`http://localhost:${port}/queue/find?name=${name}`);
        if (!data)
            return;
        if (!data.idx)
            speak(`${name}还没有排队`);
        else
            speak(`${name}已经排过队了,序号是${data.idx}`);
    };
    const get = (url) => new Promise(resolve => {
        GM_xmlhttpRequest({
            method: 'GET',
            onerror: () => resolve(null),
            onload: (response) => resolve(JSON.parse(response.responseText)),
            url,
        });
    });
    const getListAdmin = async () => {
        const data = await get(`http://localhost:${port}/admin/list`);
        if (!data)
            return false;
        if (!data.list.length) {
            speak('尚未为排队姬指定饲养员');
            return false;
        }
        data.list
            .filter(name => name.trim())
            .forEach(name => setAdmin.add(name.replace(/\r/g, '')));
        return true;
    };
    const log = (message) => {
        console.log(message);
        return message;
    };
    const main = async () => {
        pauseVideo();
        if (!(await ping()))
            return;
        if (!(await getListAdmin()))
            return;
        observe();
        clearDanmaku();
    };
    const observe = () => {
        const timer = window.setInterval(() => {
            const $el = document.getElementById('chat-items');
            if (!$el)
                return;
            window.clearInterval(timer);
            observer.observe($el, {
                childList: true,
                attributes: true,
                characterData: true,
            });
        }, 50);
    };
    const pauseVideo = () => document.querySelector('video')?.pause();
    const pick = () => Array.from(document.querySelectorAll('#chat-items .danmaku-item')).forEach($danmaku => {
        const content = $danmaku.getAttribute('data-danmaku')?.trim() || '';
        const id = $danmaku.getAttribute('data-ct')?.trim() || '';
        const name = $danmaku.getAttribute('data-uname')?.trim() || '';
        if (setAdmin.has(name)) {
            if (content === '开车')
                return startWaiting();
        }
        if (content === '排队')
            return add(name, id);
        if (content.startsWith('查询排队') || content.startsWith('排队查询')) {
            const name2 = content
                .replace('查询排队', '')
                .replace('排队查询', '')
                .trim() || '';
            return find(name2 || name, id);
        }
        if (isWaiting) {
            const idx = parseInt(content);
            if (idx > 0 && idx.toString() === content)
                return setWaiting.add(idx);
        }
        return;
    });
    const ping = async () => {
        const data = await get(`http://localhost:${port}/ping`);
        if (!data) {
            speak('排队姬尚未启动');
            return false;
        }
        speak('排队姬已经启动');
        return true;
    };
    const setCurrent = async (idx) => {
        const data = await get(`http://localhost:${port}/queue/setCurrent?idx=${idx}`);
        if (!data)
            return;
        if (!data.idx)
            speak('未能成功上车');
        else
            speak(`请序号为${data.idx}的${data.name}使用手机扫码上车`);
    };
    const speak = (message) => {
        log(message);
        speaker.text = message;
        window.speechSynthesis.speak(speaker);
    };
    const startWaiting = () => {
        if (isWaiting)
            return;
        isWaiting = true;
        setWaiting.clear();
        window.setTimeout(endWaiting, 25e3);
        speak('开车啦');
    };
    const validate = (name, id) => {
        if (cacheId.has(id))
            return false;
        cacheId.add(id);
        if (setAdmin.has(name))
            return true;
        const ts = cacheName.get(name) || 0;
        const now = Date.now();
        if (now - ts < interval)
            return false;
        cacheName.set(name, now);
        return true;
    };
    window.setTimeout(main, 1e3);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址