视频号直播无人推送工具

自动推送小工具 with frame checking using React and Bootstrap without JSX

// ==UserScript==
// @name         视频号直播无人推送工具
// @namespace    http://tampermonkey.net/
// @version      1.8
// @description  自动推送小工具 with frame checking using React and Bootstrap without JSX
// @author       万叶
// @match        https://channels.weixin.qq.com/*
// @grant        none
// ==/UserScript==

/* global React, ReactDOM, bootstrap */

(function() {
    'use strict';

    console.log("脚本开始执行"); // 控制台调试信息

    // 引入React和ReactDOM
    const reactScript = document.createElement('script');
    reactScript.src = 'https://unpkg.com/react/umd/react.production.min.js';
    const reactDOMScript = document.createElement('script');
    reactDOMScript.src = 'https://unpkg.com/react-dom/umd/react-dom.production.min.js';

    // 引入Bootstrap CSS
    const bootstrapCSS = document.createElement('link');
    bootstrapCSS.rel = 'stylesheet';
    bootstrapCSS.href = 'https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css';
    document.head.appendChild(bootstrapCSS);

    // 引入Bootstrap JavaScript
    const bootstrapScript = document.createElement('script');
    bootstrapScript.src = 'https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js';
    document.body.appendChild(bootstrapScript);

    document.head.appendChild(reactScript);
    document.head.appendChild(reactDOMScript);

    console.log("所有脚本注入完成"); // 控制台调试信息

    // 用于检查 React 和 ReactDOM 是否加载完成的函数
    const isLibLoaded = () => (
        typeof React !== 'undefined' &&
        typeof ReactDOM !== 'undefined' &&
        typeof bootstrap !== 'undefined'
    );

    // 检查库是否加载完成,如果完成则执行回调
    const checkAndExecute = (callback) => {
        if (isLibLoaded()) {
            console.log("库加载完成,执行回调函数"); // 控制台调试信息
            callback();
        } else {
            setTimeout(() => checkAndExecute(callback), 100);
            console.log("等待库加载..."); // 控制台调试信息
        }
    };

    // 等待 React 和 Bootstrap 加载完成
    checkAndExecute(() => {
        // 插入 React 渲染的 DOM 节点
        const appContainer = document.createElement('div');
        appContainer.id = 'root';
        appContainer.style.position = 'fixed';
        appContainer.style.right = '10px';
        appContainer.style.top = '100px';
        appContainer.style.zIndex = '1000';
        document.body.appendChild(appContainer);

        const { useState, useEffect } = React;

        // 定义 React 组件
        const PushTool = () => {
            const [frequency, setFrequency] = useState(60);
            const [isAutoPushEnabled, setIsAutoPushEnabled] = useState(false);

            useEffect(() => {
                let autoPushInterval;

                if (isAutoPushEnabled) {
                    triggerPush();

                    autoPushInterval = setInterval(() => {
                        document.querySelector('a.link.remove-link')?.click();
                        setTimeout(triggerPush, 3000);
                    }, frequency * 1000);
                }

                return () => clearInterval(autoPushInterval);
            }, [isAutoPushEnabled, frequency]);

            const triggerPush = () => {
                const pushButton = Array.from(
                    document.querySelectorAll('.weui-desktop-btn.weui-desktop-btn_default')
                ).find(a => a.textContent.includes('推送'));
                pushButton?.click();
            };

            return React.createElement(
                'div',
                { className: 'p-3 bg-white rounded shadow' },
                React.createElement('div', { className: 'mb-3' },
                    React.createElement('label', { className: 'form-label' }, '推送频率 (s):'),
                    React.createElement('input', {
                        type: 'number',
                        min: 2,
                        max: 600,
                        value: frequency,
                        onChange: (e) => setFrequency(parseInt(e.target.value, 10)),
                        className: 'form-control'
                    })
                ),
                React.createElement('div', { className: '' }, '自动推送:'),
                React.createElement('div', { className: 'form-switch' },
                    React.createElement('input', {
                        className: 'form-check-input',
                        type: 'checkbox',
                        checked: isAutoPushEnabled,
                        onChange: (e) => setIsAutoPushEnabled(e.target.checked),
                        style: { width: '40px', height: '20px' }
                    }),
                )
            );
        };

        // 渲染 React 组件
        ReactDOM.render(React.createElement(PushTool), document.getElementById('root'));

        console.log("组件渲染完成"); // 控制台调试信息

        // 定义检测视频帧黑色方框的函数
        const checkBlackBoxInVideo = () => {
            const video = document.querySelector('video');
            if (!video) {
                console.log("未找到视频标签");
                return;
            }

            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');

            // 每5秒轮询一次
            setInterval(() => {
                // 只有在视频播放时才进行截取
                if (!video.paused && !video.ended) {
                    canvas.width = video.videoWidth;
                    canvas.height = video.videoHeight;

                    // 将视频当前帧绘制到Canvas
                    ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

                    // 获取右下角5x5像素区域的像素数据
                    const imageData = ctx.getImageData(canvas.width - 5, canvas.height - 5, 5, 5);
                    const pixels = imageData.data;

                    let isBlackBox = true;

                    // 遍历每个像素,检查是否为黑色(R=0, G=0, B=0, A=255)
                    for (let i = 0; i < pixels.length; i += 4) {
                        const r = pixels[i];
                        const g = pixels[i + 1];
                        const b = pixels[i + 2];
                        const a = pixels[i + 3];

                        if (!(r === 0 && g === 0 && b === 0 && a === 255)) {
                            isBlackBox = false;
                            break;
                        }
                    }

                    if (isBlackBox) {
                        console.log("The black box is present in the bottom right corner.");
                    } else {
                        console.log("The black box is not present.");
                    }
                }
            }, 5000);
        };

        // 开始检测视频帧
        checkBlackBoxInVideo();
    });

})();