Greasy Fork

Greasy Fork is available in English.

猫耳迎宾机器人

一个简单的迎宾机器人

当前为 2024-08-28 提交的版本,查看 最新版本

// ==UserScript==
// @name         猫耳迎宾机器人
// @version      1.7
// @description  一个简单的迎宾机器人
// @author       洋子
// @match        https://fm.missevan.com/live/*
// @icon         https://static.maoercdn.com/avatars/202408/25/2bf1715cfc845d8b4da511d8f5345fec210801.jpg?x-oss-process=style/avatar
// @grant        none
// @namespace    http://greasyfork.icu/users/1357490
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/index.js
// ==/UserScript==

(function() {
    'use strict';

    // 从当前网址获取房间号
    let roomId = window.location.pathname.split("/").pop();

    let welcomedUsers = [];
    let processedMsgIds = new Set(); // 存储已处理的消息 ID
    let isWelcomingEnabled = true;
    let isHistoryFetchingEnabled = true;

    // 创建可隐藏的控制面板和按钮的容器
    function createControlPanelContainer() {
        const container = document.createElement('div');
        container.style.position = 'fixed';
        container.style.top = '10px';
        container.style.right = '10px';
        container.style.zIndex = '10000';
        container.style.cursor = 'move'; // 使容器可以拖动

        const panel = document.createElement('div');
        panel.style.backgroundColor = '#f0f0f0';
        panel.style.border = '1px solid #ccc';
        panel.style.padding = '10px';
        panel.style.display = 'none'; // 默认隐藏
        panel.style.position = 'relative'; // 使面板在容器内定位

        panel.innerHTML = `
            <div>
                <label for="roomIdInput">房间号:</label>
                <input type="text" id="roomIdInput" value="${roomId}">
            </div>
            <div>
                <label for="welcomeToggle">启用/禁用欢迎用户功能:</label>
                <input type="checkbox" id="welcomeToggle" checked>
            </div>
            <div>
                <label for="historyToggle">启用/禁用信息回应功能:</label>
                <input type="checkbox" id="historyToggle" checked>
            </div>
            <div>
                <label for="fileInput">选择文件:</label>
                <input type="file" id="fileInput">
            </div>
            <button id="saveSettingsBtn">保存设置</button>
        `;

        // 创建控制面板按钮
        const toggleButton = document.createElement('button');
        toggleButton.style.background = `url('https://static.maoercdn.com/avatars/202408/25/2bf1715cfc845d8b4da511d8f5345fec210801.jpg?x-oss-process=style/avatar') no-repeat center center`;
        toggleButton.style.backgroundSize = 'cover';
        toggleButton.style.border = 'none';
        toggleButton.style.width = '50px';
        toggleButton.style.height = '50px';
        toggleButton.style.cursor = 'pointer';
        toggleButton.textContent = ''; // 默认显示文字,便于访问性

        container.appendChild(toggleButton);
        container.appendChild(panel);
        document.body.appendChild(container);

        // 保存设置按钮点击事件
        document.getElementById('saveSettingsBtn').addEventListener('click', () => {
            roomId = document.getElementById('roomIdInput').value;
            isWelcomingEnabled = document.getElementById('welcomeToggle').checked;
            isHistoryFetchingEnabled = document.getElementById('historyToggle').checked;
            alert("设置已保存!");
        });

        // 文件选择事件处理
        document.getElementById('fileInput').addEventListener('change', (event) => {
            const file = event.target.files[0];
            if (file) {
                const reader = new FileReader();
                reader.onload = (e) => {
                    const fileContent = e.target.result;
                    console.log('文件内容:', fileContent);
                    // 在这里处理文件内容
                };
                reader.readAsText(file);
            }
        });

        // 切换控制面板显示
        toggleButton.addEventListener('click', () => {
            if (panel.style.display === 'none') {
                panel.style.display = 'block';
                toggleButton.textContent = '';
            } else {
                panel.style.display = 'none';
                toggleButton.textContent = '';
            }
        });

        // 添加拖动功能
        makeDraggable(container);

        return container;
    }

    // 使容器可以拖动
    function makeDraggable(element) {
        let isDragging = false;
        let offsetX, offsetY;

        element.addEventListener('mousedown', (e) => {
            isDragging = true;
            offsetX = e.clientX - parseInt(window.getComputedStyle(element).left, 10);
            offsetY = e.clientY - parseInt(window.getComputedStyle(element).top, 10);
            element.style.cursor = 'move';
        });

        window.addEventListener('mousemove', (e) => {
            if (isDragging) {
                element.style.left = `${e.clientX - offsetX}px`;
                element.style.top = `${e.clientY - offsetY}px`;
            }
        });

        window.addEventListener('mouseup', () => {
            isDragging = false;
            element.style.cursor = 'default';
        });
    }

    // 创建并添加控制面板容器到页面
    createControlPanelContainer();

    // 过滤消息中的屏蔽词
    function filterMessage(message, filterWords) {
        filterWords.forEach(word => {
            let regex = new RegExp(word, 'g');
            message = message.replace(regex, '***');
        });
        return message;
    }

    // 屏蔽词数组示例
    let filterWords = ["不良词1", "不良词2"];

    // 生成UUID v4
    function generateUUID() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            var r = Math.random() * 16 | 0,
                v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }

    // 封装的发送消息函数
    async function sendMessage(message) {
        let filteredMessage = filterMessage(message, filterWords);
        const url = 'https://fm.missevan.com/api/chatroom/message/send';

        const data = {
            room_id: roomId,
            message: filteredMessage,
            uuid: generateUUID()
        };

        const options = {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'user-agent': navigator.userAgent,
                'cookie': document.cookie,
                'origin': 'missevan.com',
                'referer': `https://fm.missevan.com/live/${roomId}`
            },
            body: JSON.stringify(data)
        };

        try {
            const response = await fetch(url, options);
            const responseData = await response.json();
            if (responseData.code === 0) {
                console.log('消息发送成功:', responseData);
            } else {
                console.error('发送失败:', responseData);
            }
        } catch (error) {
            console.error('请求错误:', error);
        }
    }

    // 每秒检测新用户加入
    setInterval(() => {
        if (!isWelcomingEnabled) return;

        const appElement = document.querySelector('app');
        if (appElement) {
            const appContent = appElement.innerHTML;
            const parser = new DOMParser();
            const doc = parser.parseFromString(appContent, 'text/html');
            const chatboxDiv = doc.querySelector('div#ChatBox');
            if (chatboxDiv) {
                const joinDivs = chatboxDiv.querySelectorAll('div.join-queue-effect.show.clickable');
                if (joinDivs.length > 0) {
                    joinDivs.forEach((userDiv) => {
                        let usernameDiv = userDiv.querySelector('.username');
                        let username = usernameDiv ? usernameDiv.textContent.trim() : '';
                        if (username && !welcomedUsers.includes(username)) {
                            welcomedUsers.push(username);
                            let date = new Date().toISOString().slice(0, 19).replace('T', ' ');
                            let pinyinUsername = pinyinPro.pinyin(username, { style: pinyinPro.STYLE_NORMAL });

                            let welcomeMessage = `| 欢迎${username}\n| 拼音: ${pinyinUsername}\n| 日期: ${date}`;

                            sendMessage(welcomeMessage);
                            console.log(`用户 ${username} (${pinyinUsername}) 已加入并发送欢迎消息`);
                        }
                    });
                }
            } else {
                console.log('未找到 id 为 ChatBox 的 div');
            }
        } else {
            console.log('未找到 <app> 标签');
        }
    }, 1000); // 每秒检测一次

    // 每秒获取一次历史消息并检查内容
    setInterval(async function() {
        if (!isHistoryFetchingEnabled) return;

        const url = `https://fm.missevan.com/api/v2/chatroom/history/message?room_id=${roomId}`;

        try {
            const response = await fetch(url);
            const data = await response.json();

            if (data.code === 0 && data.info && data.info.history) {
                const currentTimestamp = Date.now(); // 获取当前时间戳

                data.info.history.forEach(async messageData => {
                    const { msg_id, create_time, message, user } = messageData;
                    const timeDiff = currentTimestamp - create_time;

                    // 检查时间差,若在 1 秒内则处理
                    if (timeDiff <= 1000 && !processedMsgIds.has(msg_id)) {
                        // 检查是否匹配到“我是谁”信息
                        if (message.includes('我是谁')) {
                            const userId = user.user_id;
                            const username = user.username;
                            const level = user.titles.find(title => title.type === 'level')?.level;

                            console.log('匹配到“我是谁”信息:');
                            console.log('用户ID:', userId);
                            console.log('用户名:', username);
                            console.log('等级:', level);

                            // 将用户ID、用户名和等级传递给fetchPageData函数
                            await fetchPageData(userId, username, level);
                        }
                        processedMsgIds.add(msg_id); // 将处理过的消息 ID 添加到集合中
                    }
                });
            } else {
                console.error('无法获取消息历史:', data);
            }
        } catch (error) {
            console.error('请求失败:', error);
        }
    }, 1000); // 每秒请求一次

    // 修改fetchPageData函数以接受userId、username和level参数
    async function fetchPageData(userId, username, level) {
        try {
            // 使用模板字符串动态插入 userId
            const url = `https://www.missevan.com/${userId}/`;
            console.log('Fetching URL:', url);

            const response = await fetch(url);
            const html = await response.text();

            // 创建一个 DOMParser 来解析 HTML
            const parser = new DOMParser();
            const doc = parser.parseFromString(html, 'text/html');

            // 提取信息
            const botText = doc.querySelector('#t_u_n_a')?.textContent.trim();
            const followCount = doc.querySelector('.home-follow span')?.textContent.trim();
            const fanCount = doc.querySelector('.home-fans span')?.textContent.trim();

            // 提取注册日期,使用正则从 span 中提取日期部分
            const timeSpan = doc.querySelector('.time')?.textContent.trim();
            const dateMatch = timeSpan?.match(/注册于 (\d{4}-\d{2}-\d{2})/);
            const date = dateMatch ? dateMatch[1] : '未找到日期';

            // 提取鱼干数量
            const fishCount = doc.querySelector('.fish')?.textContent.trim();

            // 格式化消息内容
            const message = `|个性签名: ${botText}
                             |关注人数: ${followCount}
                             |粉丝人数: ${fanCount}
                             |鱼干: ${fishCount}
                             |用户名: ${username}
                             |等级: ${level}
        `;

        // 调用 sendMessage 函数发送消息
        await sendMessage(message);

    } catch (error) {
        console.error('网页抓取失败:', error);
    }
}

})();