linuxdo保活

linuxdo自动浏览帖子,自动点赞

// ==UserScript==
// @name         linuxdo保活
// @namespace    http://tampermonkey.net/
// @version      0.1.7
// @description  linuxdo自动浏览帖子,自动点赞
// @author       zhcf1ess
// @match        https://linux.do/*
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// @icon         https://linux.do/uploads/default/original/3X/9/d/9dd49731091ce8656e94433a26a3ef36062b3994.png
// @namespace    https://github.com/zhsama/linuxdo
// @supportURL   https://github.com/zhsama/linuxdo
// @homepageURL  https://github.com/zhsama/linuxdo
// ==/UserScript==

(function () {
    'use strict';

    // 配置对象
    const config = {
        scrollInterval: 1500, // 滚动间隔(毫秒)
        scrollStep: 500, // 每次滚动的像素
        viewCountThreshold: 500, // 浏览量阈值,超过此值才会点赞
        scrollDuration: 30, // 滚动持续时间(秒)
        maxTopics: 100, // 总浏览帖子数量,达到即停
        maxRunTime: 30, // 总运行时间(分钟),达到即停
        urls: {
            base: 'https://linux.do',
            new: 'https://linux.do/new',
            connect: 'https://connect.linux.do'
        },
        // iframe 相关配置
        iframe: {
            width: '325px',  // iframe 宽度
            height: '500px', // iframe 高度
            top: '64px',     // 距离顶部距离
            left: '1px',     // 距离左侧距离
            position: 'fixed',
            zIndex: '9999'
        },
        // 日志配置
        logging: {
            enabled: false, // 是否启用日志
            level: {
                error: true,
                info: true,
                debug: false
            }
        }
    };

    // 添加日志工具
    const logger = {
        error: (...args) => {
            if (config.logging.enabled && config.logging.level.error) {
                console.error(...args);
            }
        },
        info: (...args) => {
            if (config.logging.enabled && config.logging.level.info) {
                console.log(...args);
            }
        },
        debug: (...args) => {
            if (config.logging.enabled && config.logging.level.debug) {
                console.debug(...args);
            }
        }
    };

    // 统计对象
    const stats = {
        totalViews: 0,        // 总浏览数
        totalLikes: 0,        // 总点赞数
        sessionViews: 0,      // 本次会话浏览数
        sessionLikes: 0,      // 本次会话点赞数
        startTime: Date.now() // 会话开始时间
    };

    // 加载保存的统计数据
    function loadStats() {
        const savedStats = GM_getValue('linuxdoStats', null);
        if (savedStats) {
            stats.totalViews = savedStats.totalViews || 0;
            stats.totalLikes = savedStats.totalLikes || 0;
        }
        logger.info('📊 加载历史统计数据:');
        logger.info(`📈 总浏览数:${stats.totalViews}`);
        logger.info(`💖 总点赞数:${stats.totalLikes}`);
    }

    // 保存统计数据
    function saveStats() {
        GM_setValue('linuxdoStats', {
            totalViews: stats.totalViews,
            totalLikes: stats.totalLikes
        });
    }

    // 打印统计信息
    function printStats() {
        const runTime = Math.floor((Date.now() - stats.startTime) / 1000);
        const hours = Math.floor(runTime / 3600);
        const minutes = Math.floor((runTime % 3600) / 60);
        const seconds = runTime % 60;

        logger.info('\n📊 统计信息');
        logger.info('-------------------');
        logger.info(`🕒 运行时间:${hours}时${minutes}分${seconds}秒`);
        logger.info(`👀 本次浏览:${stats.sessionViews}帖`);
        logger.info(`❤️ 本次点赞:${stats.sessionLikes}次`);
        logger.info(`📈 总浏览数:${stats.totalViews}帖`);
        logger.info(`💖 总点赞数:${stats.totalLikes}次`);
        logger.info('-------------------\n');
    }


    // 开关状态管理
    function getSwitchState() {
        return GM_getValue('linuxdoHelperEnabled', false);
    }

    function toggleSwitch() {
        const currentState = getSwitchState();
        GM_setValue('linuxdoHelperEnabled', !currentState);

        if (!currentState) {
            window.location.href = config.urls.base
        }
        logger.info(`Linuxdo助手已${!currentState ? '启用' : '禁用'}`);
    }

    // 创建开关图标
    function createSwitchIcon() {
        const iconLi = document.createElement('li');
        iconLi.className = 'header-dropdown-toggle';
        const iconLink = document.createElement('a');
        iconLink.href = 'javascript:void(0)';
        iconLink.className = 'btn no-text icon btn-flat';
        iconLink.title = getSwitchState() ? '停止Linuxdo助手' : '启动Linuxdo助手';
        iconLink.tabIndex = 0;
        const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        svg.setAttribute('class', 'fa d-icon d-icon-rocket svg-icon prefix-icon svg-string');
        svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
        const use = document.createElementNS('http://www.w3.org/2000/svg', 'use');
        use.setAttribute('href', getSwitchState() ? '#pause' : '#play');
        svg.appendChild(use);
        iconLink.appendChild(svg);
        iconLi.appendChild(iconLink);

        // 点击事件
        iconLink.addEventListener('click', () => {
            toggleSwitch();
            const currentState = getSwitchState();
            use.setAttribute('href', currentState ? '#pause' : '#play');
            iconLink.title = currentState ? '停止Linuxdo助手' : '启动Linuxdo助手';
            iconLink.classList.toggle('active', currentState);
        });

        // 找到聊天图标并插入
        const chatIconLi = document.getElementById('search-button').parentElement;
        if (chatIconLi) {
            chatIconLi.parentNode.insertBefore(iconLi, chatIconLi.nextSibling);
        } else {
            logger.error("【错误】未找到按钮!")
        }
    }

    // 检查并执行点赞
    async function checkAndLike(targetWindow = window) {
        try {
            // 获取浏览量
            const viewsElement = targetWindow.document.querySelector('.list-view-count');
            if (!viewsElement) return;

            const viewCount = parseInt(viewsElement.textContent.replace(/,/g, ''));
            if (viewCount <= config.viewCountThreshold) return;

            // 查找点赞按钮
            const likeButton = targetWindow.document.querySelector('.btn-toggle-reaction-like');
            if (!likeButton) {
                logger.info('未找到点赞按钮');
                return;
            }

            // 检查是否已经点赞
            if (likeButton.title.includes('移除此赞')) {
                logger.info('该帖子已点赞,跳过点赞操作。');
                return;
            }

            // 执行点赞
            likeButton.click();
            logger.info('点赞帖子成功');

            // 更新统计
            stats.sessionLikes++;
            stats.totalLikes++;
            saveStats();

        } catch (error) {
            logger.error('点赞操作失败:', error);
        }
    }

    // 获取帖子列表
    async function getTopicsList() {
        const topics = document.querySelectorAll('#list-area .title');
        logger.info(`共找到 ${topics.length} 个帖子`);

        const topicsList = [];
        for (let i = 0; i < topics.length; i++) {
            const topic = topics[i];
            const parentElement = topic.closest('tr');

            // 检查是否是置顶帖
            const isPinned = parentElement.querySelector('.topic-statuses .pinned');
            if (isPinned) {
                logger.debug(`跳过置顶的帖子:${topic.textContent.trim()}`);
                continue;
            }

            // 获取浏览量
            const viewsElement = parentElement.querySelector('.num.views .number');
            const viewsTitle = viewsElement.getAttribute('title');
            const viewsCount = parseInt(viewsTitle.split('此话题已被浏览 ')[1].split(' 次')[0].replace(/,/g, ''));

            topicsList.push({
                title: topic.textContent.trim(),
                url: topic.href,
                views: viewsCount
            });
        }
        return topicsList;
    }

    // 浏览单个帖子
    async function browseTopic(topic) {
        logger.info(`打开帖子:${topic.title}`);

        // 更新统计
        stats.sessionViews++;
        stats.totalViews++;
        saveStats();

        // 创建一个隐藏的 iframe 来加载帖子
        const iframe = document.createElement('iframe');
        Object.assign(iframe.style, config.iframe);
        iframe.src = topic.url;
        document.body.appendChild(iframe);

        // 等待 iframe 加载完成
        await new Promise(resolve => {
            iframe.onload = resolve;
        });

        // 如果浏览量超过阈值,执行点赞
        if (topic.views > config.viewCountThreshold) {
            logger.info(`📈 当前帖子浏览量为${topic.views}`);
            logger.info(`🥳 当前帖子浏览量大于设定值${config.viewCountThreshold},开始进行点赞操作`);
            await checkAndLike(iframe.contentWindow);
        }

        // 滚动浏览帖子内容
        await new Promise((resolve) => {
            const startTime = Date.now();
            const scrollInterval = setInterval(() => {
                if (Date.now() - startTime >= config.scrollDuration * 1000) {
                    clearInterval(scrollInterval);
                    // 移除 iframe
                    document.body.removeChild(iframe);
                    // 打印统计信息
                    printStats();
                    resolve();
                    return;
                }
                iframe.contentWindow.scrollBy(0, config.scrollStep);
            }, config.scrollInterval);
        });

        // 等待一段时间确保清理完成
        await new Promise(resolve => setTimeout(resolve, 1000));
    }

    // 检查是否需要停止脚本
    function shouldStopScript() {
        // 检查浏览数量
        if (stats.sessionViews >= config.maxTopics) {
            logger.info(`\n🛑 已达到最大浏览数量 ${config.maxTopics} 篇,停止脚本运行`);
            return true;
        }

        // 检查运行时间
        const runTime = (Date.now() - stats.startTime) / 1000;
        if (runTime >= config.maxRunTime * 60) {
            const hours = Math.floor(runTime / 3600);
            const minutes = Math.floor((runTime % 3600) / 60);
            logger.info(`\n🛑 已达到最大运行时间 ${hours}时${minutes}分,停止脚本运行`);
            return true;
        }

        return false;
    }

    // 停止脚本运行
    function stopScript() {
        GM_setValue('linuxdoHelperEnabled', false);
        printStats();
        logger.info('\n✨ 脚本已自动停止运行');
        window.location.href = config.urls.connect;
    }

    // 主要浏览逻辑
    async function browseTopics() {
        try {
            // 获取帖子列表
            const topics = await getTopicsList();

            // 遍历浏览帖子
            if (topics.length > 0) {
                // 随机打乱帖子列表顺序
                const shuffledTopics = topics.sort(() => Math.random() - 0.5);

                // 逐个浏览帖子
                for (const topic of shuffledTopics) {
                    // 检查是否需要停止脚本
                    if (shouldStopScript()) {
                        stopScript();
                        return;
                    }

                    if (!getSwitchState()) {
                        logger.info('脚本已停止');
                        return;
                    }

                    await browseTopic(topic);

                    // 在浏览下一个帖子前等待一段随机时间
                    const waitTime = Math.floor(Math.random() * 3000) + 2000; // 2-5秒
                    await new Promise(resolve => setTimeout(resolve, waitTime));
                }
            }

        } catch (error) {
            logger.error('浏览帖子时出错:', error);
        }
    }

    // 主执行函数
    async function main() {
        createSwitchIcon();
        if (!getSwitchState()) return;

        try {
            // 加载统计数据
            loadStats();

            // 如果在最新帖子页面,开始浏览帖子
            if (window.location.href.includes(config.urls.base)) {
                // 检查是否需要停止脚本
                if (shouldStopScript()) {
                    stopScript();
                    return;
                }
                await browseTopics();
            }
        } catch (error) {
            logger.error('脚本执行出错:', error);
        }
    }

    // 页面加载完成后执行
    if (document.readyState === 'complete') {
        main();
    } else {
        window.addEventListener('load', main);
    }
})();