Greasy Fork

Greasy Fork is available in English.

洛曦-直播弹幕监听助手 转发至本地WS服务端,巨量百应商品自动弹窗

观察指定 DOM 节点的变化以将数据发送到连接的WebSocket服务端

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         洛曦-直播弹幕监听助手 转发至本地WS服务端,巨量百应商品自动弹窗
// @namespace    http://tampermonkey.net/
// @version      3.1
// @description  观察指定 DOM 节点的变化以将数据发送到连接的WebSocket服务端
// @description  Github:https://github.com/Ikaros-521/AI-Vtuber/tree/main/Scripts/%E7%9B%B4%E6%92%ADws%E8%84%9A%E6%9C%AC
// @author       Ikaros
// @match        https://www.douyu.com/*
// @match        https://live.kuaishou.com/u/*
// @match        https://mobile.yangkeduo.com/*
// @match        https://live.1688.com/zb/play.html*
// @match        https://tbzb.taobao.com/live*
// @match        https://redlive.xiaohongshu.com/*
// @match        https://channels.weixin.qq.com/platform/live/*
// @match        https://buyin.jinritemai.com/dashboard/live/control*
// @match        https://ark.xiaohongshu.com/live_center_control*
// @match        https://www.tiktok.com/@*/live*
// @match        https://eos.douyin.com/livesite/live/current*
// @grant        none
// @namespace    http://greasyfork.icu/scripts/490966
// @license      GPL-3.0
// ==/UserScript==

(function () {
    "use strict";


    let wsUrl = "ws://127.0.0.1:5001";

    // 在文件开头添加一个函数,用于创建和显示消息框
    function showMessage(message, type = 'info') {
        const messageBox = document.createElement('div');
        messageBox.className = `message-box ${type}`;
        messageBox.innerText = message;

        // 设置样式,消息上方居中
        messageBox.style.position = 'fixed';
        messageBox.style.right = '40%';
        messageBox.style.transform = 'translateX(-50%)';
        messageBox.style.top = `${10 + (document.querySelectorAll('.message-box').length * 60)}px`; // 每个消息框之间的间距
        messageBox.style.zIndex = '9999';
        messageBox.style.padding = '10px';
        // 设置info、success、error、warning等多个颜色,要好看,参考element-ui
        messageBox.style.backgroundColor = type === 'info' ? '#409EFF' : type === 'success' ? '#67C23A' : type === 'warning' ? '#E6A23C' : '#F56C6C';
        messageBox.style.color = 'white';
        messageBox.style.borderRadius = '5px';
        messageBox.style.marginBottom = '10px';
        messageBox.style.transition = 'opacity 0.5s ease';
        // 字体要大
        messageBox.style.fontSize = '16px';

        document.body.appendChild(messageBox);

        // 自动消失
        setTimeout(() => {
            messageBox.style.opacity = '0';
            setTimeout(() => {
                document.body.removeChild(messageBox);
            }, 500);
        }, 3000); // 3秒后消失

        // 限制消息框数量
        const messageBoxes = document.querySelectorAll('.message-box');
        if (messageBoxes.length > 5) { // 限制最多显示5个消息框
            document.body.removeChild(messageBoxes[0]);
        }
    }

    showMessage("洛曦-直播弹幕监听助手 启动中,请稍等...", 'info');

    setTimeout(function () {
        let my_socket = null;
        let targetNode = null;
        let my_observer = null;

        const hostname = window.location.hostname;

        if (hostname === "www.douyu.com") {
            console.log("当前直播平台:斗鱼");
            showMessage("当前直播平台:斗鱼");
            wsUrl = "ws://127.0.0.1:5001";
        } else if (hostname === "live.kuaishou.com") {
            console.log("当前直播平台:快手");
            showMessage("当前直播平台:快手");
            wsUrl = "ws://127.0.0.1:5001";
        } else if (hostname === "mobile.yangkeduo.com") {
            console.log("当前直播平台:拼多多");
            showMessage("当前直播平台:拼多多");
            wsUrl = "ws://127.0.0.1:5001";
        } else if (hostname === "live.1688.com") {
            console.log("当前直播平台:1688");
            showMessage("当前直播平台:1688");
            wsUrl = "ws://127.0.0.1:5001";
        } else if (hostname === "tbzb.taobao.com") {
            console.log("当前直播平台:淘宝");
            showMessage("当前直播平台:淘宝");
            wsUrl = "ws://127.0.0.1:5001";
        } else if (hostname === "redlive.xiaohongshu.com" || hostname === "ark.xiaohongshu.com") {
            console.log("当前直播平台:小红书");
            showMessage("当前直播平台:小红书");
            wsUrl = "ws://127.0.0.1:5001";
        } else if (hostname === "channels.weixin.qq.com") {
            console.log("当前直播平台:微信视频号");
            showMessage("当前直播平台:微信视频号");
            wsUrl = "ws://127.0.0.1:5001";
        } else if (hostname === "buyin.jinritemai.com") {
            console.log("当前直播平台:巨量百应");
            showMessage("当前直播平台:巨量百应");
            wsUrl = "ws://127.0.0.1:5001";
        } else if (hostname === "www.tiktok.com") {
            console.log("当前直播平台:TikTok");
            showMessage("当前直播平台:TikTok");
            wsUrl = "ws://127.0.0.1:5001";
        } else if (hostname === "eos.douyin.com") {
            console.log("当前直播平台:抖音");
            showMessage("当前直播平台:抖音");
            wsUrl = "ws://127.0.0.1:5001";
        }

        function connectWebSocket() {
            // 创建 WebSocket 连接,适配服务端
            my_socket = new WebSocket(wsUrl);

            // 当连接建立时触发
            my_socket.addEventListener("open", (event) => {
                console.log("ws连接打开");

                // 向服务器发送一条消息
                const data = {
                    type: "info",
                    content: "ws连接成功",
                };
                console.log(data);
                my_socket.send(JSON.stringify(data));
            });

            // 当收到消息时触发
            my_socket.addEventListener("message", (event) => {
                console.log("收到服务器数据:", event.data);
                showMessage("收到服务器数据: " + event.data);
            });

            // 当连接关闭时触发
            my_socket.addEventListener("close", (event) => {
                console.log("WS连接关闭");
                showMessage("WS连接关闭", 'error');
                // 重连
                setTimeout(() => {
                    connectWebSocket();
                }, 1000); // 延迟 1 秒后重连
            });
        }

        if (hostname != "buyin.jinritemai.com") {
            // 初始连接
            connectWebSocket();
        }

        // 配置观察选项
        const config = {
            childList: true,
            subtree: true,
        };

        let timeoutId = null; // 定时器ID
        let cycleTimeoutId = null; // 循环周期定时器ID

        // 创建配置界面
        function createConfigUI() {
            const configDiv = document.createElement('div');
            configDiv.style.cssText = `
                position: fixed;
                bottom: 20px;
                right: 20px;
                background: #ffffff80;
                border-radius: 8px;
                box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
                padding: 15px;
                z-index: 1000;
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            `;

            configDiv.innerHTML = `
                <button id="toggleConfig" style="
                    width: 100%;
                    padding: 8px 15px;
                    background: #409EFF;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 14px;
                    transition: background-color 0.3s;
                ">展开配置</button>
                <div id="configPanel" style="
                    display: none;
                    margin-top: 10px;
                ">
                    <!-- WS监听配置 -->
                    <div style="
                        margin-bottom: 20px;
                        padding: 15px;
                        border: 1px solid #DCDFE6;
                        border-radius: 4px;
                        background: #F5F7FA;
                    ">
                        <h3 style="
                            margin: 0 0 15px 0;
                            color: #303133;
                            font-size: 16px;
                            font-weight: 500;
                        ">WS监听配置</h3>
                        <div style="margin-bottom: 15px;">
                            <label style="display: block; margin-bottom: 5px; color: #606266; font-size: 14px;">
                                WebSocket 地址:
                            </label>
                            <input type="text" id="wsUrl" value="${wsUrl}" style="
                                width: 100%;
                                padding: 8px;
                                border: 1px solid #DCDFE6;
                                border-radius: 4px;
                                box-sizing: border-box;
                                font-size: 14px;
                                transition: border-color 0.3s;
                            "/>
                        </div>
                        <button id="saveConfig" style="
                            width: 100%;
                            padding: 8px 15px;
                            background: #409EFF;
                            color: white;
                            border: none;
                            border-radius: 4px;
                            cursor: pointer;
                            font-size: 14px;
                            transition: background-color 0.3s;
                        ">保存WS配置</button>
                    </div>

                    <!-- 商品弹窗配置 -->
                    <div style="
                        padding: 15px;
                        border: 1px solid #DCDFE6;
                        border-radius: 4px;
                        background: #F5F7FA;
                    ">
                        <h3 style="
                            margin: 0 0 15px 0;
                            color: #303133;
                            font-size: 16px;
                            font-weight: 500;
                        ">商品弹窗配置</h3>
                        <div style="margin-bottom: 15px;">
                            <label style="display: block; margin-bottom: 5px; color: #606266; font-size: 14px;">
                                商品编号 (空格分隔):
                            </label>
                            <input type="text" id="itemIndices" style="
                                width: 100%;
                                padding: 8px;
                                border: 1px solid #DCDFE6;
                                border-radius: 4px;
                                box-sizing: border-box;
                                font-size: 14px;
                                transition: border-color 0.3s;
                            "/>
                        </div>
                        <div style="margin-bottom: 15px;">
                            <label style="display: block; margin-bottom: 5px; color: #606266; font-size: 14px;">
                                每次触发延迟 (毫秒):
                            </label>
                            <input type="number" id="delay" value="5000" style="
                                width: 100%;
                                padding: 8px;
                                border: 1px solid #DCDFE6;
                                border-radius: 4px;
                                box-sizing: border-box;
                                font-size: 14px;
                                transition: border-color 0.3s;
                            "/>
                        </div>
                        <div style="margin-bottom: 15px;">
                            <label style="display: block; margin-bottom: 5px; color: #606266; font-size: 14px;">
                                循环周期延迟 (毫秒):
                            </label>
                            <input type="number" id="cycleDelay" value="5000" style="
                                width: 100%;
                                padding: 8px;
                                border: 1px solid #DCDFE6;
                                border-radius: 4px;
                                box-sizing: border-box;
                                font-size: 14px;
                                transition: border-color 0.3s;
                            "/>
                        </div>
                        <button id="applyConfig" style="
                            width: 100%;
                            padding: 8px 15px;
                            background: #67C23A;
                            color: white;
                            border: none;
                            border-radius: 4px;
                            cursor: pointer;
                            font-size: 14px;
                            transition: background-color 0.3s;
                        ">启动自动弹窗</button>
                    </div>
                </div>
            `;
            document.body.appendChild(configDiv);

            // 添加悬停效果
            const buttons = configDiv.getElementsByTagName('button');
            for (let button of buttons) {
                button.addEventListener('mouseover', function() {
                    this.style.opacity = '0.8';
                });
                button.addEventListener('mouseout', function() {
                    this.style.opacity = '1';
                });
            }

            // 添加输入框焦点效果
            const inputs = configDiv.getElementsByTagName('input');
            for (let input of inputs) {
                input.addEventListener('focus', function() {
                    this.style.borderColor = '#409EFF';
                });
                input.addEventListener('blur', function() {
                    this.style.borderColor = '#DCDFE6';
                });
            }

            document.getElementById('toggleConfig').addEventListener('click', () => {
                const configPanel = document.getElementById('configPanel');
                configPanel.style.display = configPanel.style.display === 'none' ? 'block' : 'none';
            });

            document.getElementById('applyConfig').addEventListener('click', applyConfig);
            document.getElementById('saveConfig').addEventListener('click', saveConfig);
        }

        // 保存监听配置
        function saveConfig() {
            const newWsUrl = document.getElementById('wsUrl').value;

            // 检查WebSocket地址格式
            if (!newWsUrl.startsWith('ws://') && !newWsUrl.startsWith('wss://')) {
                showMessage('WebSocket地址格式错误,必须以ws://或wss://开头', 'error');
                return;
            }

            try {
                new URL(newWsUrl);
                wsUrl = newWsUrl; // 更新 WebSocket 地址
                showMessage('配置保存成功', 'success');
            } catch (error) {
                showMessage('WebSocket地址格式无效', 'error');
            }
        }

        // 应用配置
        function applyConfig() {
            const itemIndicesInput = document.getElementById('itemIndices').value;
            if (!itemIndicesInput.trim()) {
                showMessage('请输入商品编号', 'warning');
                return;
            }

            const delay = parseInt(document.getElementById('delay').value, 10);
            const cycleDelay = parseInt(document.getElementById('cycleDelay').value, 10);

            // 验证延迟时间
            if (delay < 0 || isNaN(delay)) {
                showMessage('触发延迟时间必须大于0', 'warning');
                return;
            }
            if (cycleDelay < 0 || isNaN(cycleDelay)) {
                showMessage('循环周期延迟时间必须大于0', 'warning');
                return;
            }

            const itemIndices = itemIndicesInput.split(' ')
                .filter(str => str.trim() !== '')
                .map(str => parseInt(str.trim(), 10));

            // 验证商品编号
            if (itemIndices.some(index => isNaN(index) || index <= 0)) {
                showMessage('商品编号必须为正整数', 'warning');
                return;
            }

            const applyBtn = document.getElementById('applyConfig');
            const isRunning = applyBtn.textContent === '停止自动弹窗';

            if (isRunning) {
                // 如果当前正在运行,则停止
                stopLoop();
                applyBtn.textContent = '启动自动弹窗';
                applyBtn.style.backgroundColor = '#67C23A';
                showMessage('自动弹窗已停止', 'info');
            } else {
                // 如果当前已停止,则启动
                startLoop(itemIndices, delay, cycleDelay);
                applyBtn.textContent = '停止自动弹窗';
                applyBtn.style.backgroundColor = '#F56C6C';
                showMessage('自动弹窗已启动', 'success');
            }
        }

        // 启动循环
        function startLoop(itemIndices, delay, cycleDelay) {
            if (itemIndices.length === 0) return;

            const triggerNext = (index = 0) => {
                if (index >= itemIndices.length) {
                    // 结束一轮后等待循环周期延迟再开始下一轮
                    cycleTimeoutId = setTimeout(() => triggerNext(0), cycleDelay);
                    return;
                }

                const itemIndex = itemIndices[index] - 1;
                if (isNaN(itemIndex) || itemIndex < 0) {
                    console.error(`商品编号 ${itemIndex + 1} 无效`);
                    triggerNext(index + 1);
                    return;
                }
                const buttonIndex = 3 + 6 * itemIndex;

                try {
                    const buttons = document.getElementsByClassName("lvc2-grey-btn");
                    if(buttons[buttonIndex]) {
                        buttons[buttonIndex].click();
                        console.log(`已触发商品编号 ${itemIndex + 1} 的弹窗`);
                    } else {
                        console.error("无法找到指定的按钮!");
                    }
                } catch (error) {
                    console.error("触发弹窗时发生错误:", error);
                }

                timeoutId = setTimeout(() => triggerNext(index + 1), delay);
            };

            triggerNext();
        }

        // 停止循环
        function stopLoop() {
            if (timeoutId !== null) {
                clearTimeout(timeoutId);
                timeoutId = null;
            }
            if (cycleTimeoutId !== null) {
                clearTimeout(cycleTimeoutId);
                cycleTimeoutId = null;
            }
        }

        // 巨量百应
        if (hostname === "buyin.jinritemai.com") {
            // 初始化
            createConfigUI();
        }

        // 添加重试观察的函数,支持最大重试次数和指数退避
        let observeRetryCount = 0;
        const maxObserveRetries = 50; // 最大重试次数
        const baseRetryDelay = 10000; // 基础重试延迟时间(ms)

        // 初始化观察器和目标节点
        function initObserver() {
            // 清理之前的资源
            if (my_observer) {
                try {
                    my_observer.disconnect();
                } catch (e) {
                    console.error("断开旧观察器连接失败:", e);
                }
            }

            if (my_socket && hostname != "buyin.jinritemai.com") {
                try {
                    my_socket.close();
                } catch (e) {
                    console.error("关闭旧WebSocket连接失败:", e);
                }
                // 重新连接WebSocket
                connectWebSocket();
            }

            // 重置变量
            my_observer = null;
            targetNode = null;

            // 根据平台初始化对应的目标节点和观察器
            const platformConfig = {
                "www.douyu.com": {
                    selector: ".Barrage-list",
                    nodeClass: "Barrage-listItem",
                    usernameClass: "Barrage-nickName",
                    contentClass: "Barrage-content"
                },
                "live.kuaishou.com": {
                    selector: ".chat-history"
                },
                "mobile.yangkeduo.com": {
                    selector: ".MYFlHgGu"
                },
                "live.1688.com": {
                    selector: ".pc-living-room-message"
                },
                "tbzb.taobao.com": {
                    selector: "#liveComment"
                },
                "redlive.xiaohongshu.com": {
                    selector: ".comments"
                },
                "ark.xiaohongshu.com": {
                    selector: ".comments"
                },
                "channels.weixin.qq.com": {
                    selector: ".vue-recycle-scroller comment__list scroller ready direction-vertical"
                },
                "buyin.jinritemai.com": {
                    selector: "#comment-list-wrapper"
                },
                "www.tiktok.com": {
                    selector: ".flex-1"
                },
                "eos.douyin.com": {
                    selector: ".list-gdqoHn"
                }
            };

            // 获取当前平台配置
            const platform = platformConfig[hostname] ||
                             (hostname === "ark.xiaohongshu.com" ? platformConfig["redlive.xiaohongshu.com"] : null);

            if (!platform) {
                console.error("未知平台:", hostname);
                return false;
            }

            // 获取目标节点
            targetNode = document.querySelector(platform.selector);
            if (!targetNode) {
                console.warn("未找到目标DOM节点,可能页面未完全加载");
                return false;
            }

            // 创建观察器实例
            my_observer = new MutationObserver((mutations) => {
                mutations.forEach((mutation) => {
                    if (mutation.type === "childList") {
                        mutation.addedNodes.forEach((node) => {
                            // 由于各平台处理逻辑不同,这里简化为通用监听
                            // 实际生产中需要根据不同平台实现完整的处理逻辑
                            processDanmaku(node);
                        });
                    }
                });
            });

            return true;
        }

        // 处理弹幕消息的通用函数
        function processDanmaku(node) {
            try {
                let username = "";
                let content = "";
                let messageType = "comment"; // 默认为评论类型
                let additionalData = {};

                // 根据不同平台解析弹幕内容
                if (hostname === "www.douyu.com" && node.classList.contains("Barrage-listItem")) {
                    // 斗鱼直播弹幕处理
                    const spans = node.getElementsByTagName("span");
                    for (let span of spans) {
                        if (span.classList.contains("Barrage-nickName")) {
                            const tmp = span.textContent.trim().slice(0, -1);
                            if (tmp) username = tmp;
                        } else if (span.classList.contains("Barrage-content")) {
                            content = span.textContent.trim();
                        }
                    }
                } else if (hostname === "live.kuaishou.com") {
                    // 快手直播弹幕处理
                    if (node.querySelector(".comment-cell")) {
                        const commentCells = node.querySelectorAll(".comment-cell");

                        commentCells.forEach((cell) => {
                            const usernameElement = cell.querySelector(".username");
                            const commentElement = cell.querySelector(".comment");
                            const giftCommentElement = cell.querySelector(".gift-comment");
                            const likeElement = cell.querySelector(".like");

                            if (usernameElement && giftCommentElement) {
                                // 礼物消息
                                username = usernameElement.textContent.trim().replace(":", "");
                                content = giftCommentElement.textContent.trim();
                                messageType = "gift";
                                additionalData = { gift: content };
                            } else if (usernameElement && likeElement) {
                                // 点赞消息
                                username = usernameElement.textContent.trim().replace(":", "");
                                content = "点了个赞";
                                messageType = "like";
                            } else if (usernameElement && commentElement) {
                                // 评论消息
                                username = usernameElement.textContent.trim().replace(":", "");

                                // 提取评论内容(包括表情图片的替换)
                                const extractContent = (element) => {
                                    let text = "";
                                    element.childNodes.forEach((child) => {
                                        if (child.nodeType === Node.TEXT_NODE) {
                                            text += child.textContent.trim();
                                        } else if (child.nodeType === Node.ELEMENT_NODE) {
                                            if (child.tagName === "IMG" && child.classList.contains("emoji")) {
                                                text += child.getAttribute("alt") || "[表情]";
                                            } else {
                                                text += extractContent(child);
                                            }
                                        }
                                    });
                                    return text;
                                };

                                content = extractContent(commentElement);
                            }
                        });
                    }
                } else if (hostname === "mobile.yangkeduo.com" && node.classList.contains("_24Qh0Jmi")) {
                    // 拼多多直播弹幕处理
                    const usernameElement = node.querySelector(".t6fCgSnz");
                    const commentElement = node.querySelector("._16_fPXYP");

                    if (usernameElement && commentElement) {
                        username = usernameElement.textContent.trim().slice(0, -1);
                        content = commentElement.textContent.trim();
                    }
                } else if (hostname === "live.1688.com" && node.classList.contains("comment-message")) {
                    // 1688直播弹幕处理
                    const usernameElement = node.querySelector(".from");
                    const commentElement = node.querySelector(".msg-text");

                    if (usernameElement && commentElement) {
                        username = usernameElement.textContent.trim().slice(0, -1);
                        content = commentElement.textContent.trim();
                    }
                } else if (hostname === "tbzb.taobao.com" && node.classList.contains("itemWrap--EcN_tFIg")) {
                    // 淘宝直播弹幕处理
                    const spans = node.getElementsByTagName("span");

                    for (let span of spans) {
                        if (span.classList.contains("authorTitle--_Dl75ZJ6")) {
                            const tmp = span.textContent.trim().slice(0, -1);
                            if (tmp) username = tmp;
                        } else if (span.classList.contains("content--pSjaTkyl")) {
                            content = span.textContent.trim();
                        }
                    }
                } else if ((hostname === "redlive.xiaohongshu.com" || hostname === "ark.xiaohongshu.com") &&
                          node.classList.contains("comment-list-item")) {
                    // 小红书直播弹幕处理
                    const spans = node.getElementsByTagName("span");

                    for (let i = 0; i < spans.length; i++) {
                        // 有些消息带有标签信息
                        if (spans[i].classList.contains("live-tag")) {
                            additionalData.tag = spans[i].textContent.trim().slice(0, -1);
                        }

                        // 通常用户名和内容是最后两个span
                        if (i == (spans.length - 2)) {
                            username = spans[i].textContent.trim().slice(0, -1);
                        } else if (i == (spans.length - 1)) {
                            content = spans[i].textContent.trim();
                        }
                    }
                } else if (hostname === "channels.weixin.qq.com" &&
                          node.classList.contains("vue-recycle-scroller__item-view")) {
                    // 微信视频号直播弹幕处理
                    const spans = node.getElementsByTagName("span");
                    let messageTypeElement = node.querySelector(".message-type");

                    if (messageTypeElement) {
                        const messageTypeText = messageTypeElement.textContent.trim();
                        if (messageTypeText.includes("礼物")) {
                            messageType = "gift";
                        } else if (messageTypeText.includes("点赞")) {
                            messageType = "like";
                        }
                    }

                    for (let i = 0; i < spans.length; i++) {
                        if (i == (spans.length - 2)) {
                            username = spans[i].textContent.trim().slice(0, -1);
                        } else if (i == (spans.length - 1)) {
                            content = spans[i].textContent.trim();
                        }
                    }
                } else if (hostname === "buyin.jinritemai.com" && node.classList.contains("commentItem-AzWZJ8")) {
                    // 巨量百应直播弹幕处理
                    const nicknameDiv = node.querySelector(".nickname-H277c7");
                    const descriptionDiv = node.querySelector(".description-ml2w_d");

                    if (nicknameDiv) {
                        // 获取用户名 - 排除头衔div,只获取直接文本
                        let textContent = '';
                        nicknameDiv.childNodes.forEach(node => {
                            // 只处理文本节点
                            if (node.nodeType === Node.TEXT_NODE) {
                                textContent += node.textContent;
                            }
                        });
                        // 清理文本
                        username = textContent.trim();
                        if (username.endsWith(':')) {
                            username = username.slice(0, -1);
                        }
                    }

                    if (descriptionDiv) {
                        content = descriptionDiv.textContent.trim();
                    }
                } else if (hostname === "www.tiktok.com" && node.classList.contains("break-words")) {
                    const nicknameDiv = node.querySelector(".truncate");
                    const commentDiv = node.querySelector(".align-middle");

                    if (nicknameDiv) {
                        // 获取用户名 - 排除头衔div,只获取直接文本
                        let textContent = '';
                        nicknameDiv.childNodes.forEach(node => {
                            // 只处理文本节点
                            if (node.nodeType === Node.TEXT_NODE) {
                                textContent += node.textContent;
                            }
                        });
                        // 清理文本
                        username = textContent.trim();
                    }

                    if (commentDiv) {
                        content = commentDiv.textContent.trim();
                    }
                } else if (hostname === "eos.douyin.com" && node.classList.contains("item-x_bazm")) {
                    // 拼多多直播弹幕处理
                    const usernameElement = node.querySelector(".item-name-qalgHb");
                    const commentElement = node.querySelector(".item-content-kHjdRK");

                    if (usernameElement && commentElement) {
                        username = usernameElement.textContent.trim().slice(0, -1);
                        content = commentElement.textContent.trim();
                    }
                }

                // 如果解析成功,发送消息
                if (username && content) {
                    // 根据消息类型显示不同类型的提示
                    let messageIcon, messageColor;
                    if (messageType === "gift") {
                        messageIcon = "[礼物消息]";
                        messageColor = "success";
                    } else if (messageType === "like") {
                        messageIcon = "[点赞消息]";
                        messageColor = "info";
                    } else {
                        messageIcon = "[弹幕消息]";
                        messageColor = "info";
                    }

                    if (content == "送" && hostname === "live.kuaishou.com") {
                        // 过滤
                    } else {
                        console.log(`${username}: ${content} (${messageType})`);
                        showMessage(`${messageIcon} ${username}: ${content}`, messageColor);

                        // 构造数据
                        const data = {
                            type: messageType,
                            username: username,
                            content: content,
                            data: additionalData
                        };

                        // 发送到WebSocket服务器
                        if (my_socket && my_socket.readyState === WebSocket.OPEN) {
                            my_socket.send(JSON.stringify(data));
                        }
                    }
                }
            } catch (error) {
                console.error("处理弹幕时出错:", error);
            }
        }

        function retryObserve() {
            try {
                // 尝试初始化观察器和目标节点
                if (!targetNode || !my_observer) {
                    console.log("初始化观察所需变量...");
                    if (!initObserver()) {
                        throw new Error("初始化失败,将在延迟后重试");
                    }
                }

                // 开始观察
                my_observer.observe(targetNode, config);

                // 重置重试计数
                observeRetryCount = 0;
                console.log("观察成功启动!");
                showMessage("观察成功启动!", 'success');
            } catch (error) {
                console.error("观察失败:", error);
                showMessage("观察失败: " + error.message, 'error');

                // 增加重试计数
                observeRetryCount++;

                if (observeRetryCount <= maxObserveRetries) {
                    // 使用更平滑的指数退避
                    const retryDelay = Math.min(
                        baseRetryDelay * (1 + (observeRetryCount - 1) * 0.5),
                        30000 // 最大延迟不超过30秒
                    );

                    console.log(`第${observeRetryCount}次重试失败,${retryDelay/1000}秒后将再次尝试...`);
                    showMessage(`第${observeRetryCount}次重试失败,${retryDelay/1000}秒后将再次尝试...`, 'warning');

                    setTimeout(retryObserve, retryDelay);
                } else {
                    console.error(`已达到最大重试次数(${maxObserveRetries}),观察启动失败!`);
                    showMessage(`已达到最大重试次数(${maxObserveRetries}),观察启动失败!如需继续,请刷新页面重试。`, 'error');
                }
            }
        }

        // 初始化观察
        retryObserve();

    }, 10000);
})();