【PT】netcup 流量解决方案 Vertex版

既然都砸锅了,举报的也都举报了。那也不藏着掖着了


食用方法:

复制至vertex定时脚本模块
执行周期:0 5 * * * 每天凌晨5点执行
配置SCP账户信息和NC小🐔在Vertex的QB下载器ID(支持多🐔)
如果多个账号,那就复制一份再跑一个定时

且用且珍惜吧

另外有人有不要的vps1000翻倍甩我一只,我还没玩够!

async () => {
    // 配置区域⬇⬇⬇
    const CONFIG = {
        scp: {
            username: "your_scp_username",  // SCP登录用户名
            password: "your_scp_password",  // SCP登录密码
            rebootType: "POWERCYCLE",
        },
        clients: [
            "13310b9b",  // NC在Vertex的QB下载器ID
            "13310b99",
        ],
        delays: {
            qbitPause: 30,    // 暂停种子等待重启时间(秒)
            powerOn: 300,      // 开机等待恢复种子时间(秒)
            serverReboot: 3,  // 服务器间重启间隔(秒)
        }
    };
    // 配置区域⬆⬆⬆


    let QB_CLIENTS = null;
    let SERVERS_TO_REBOOT = []; 
    let NEED_RESUME = false;
    const util = require('../libs/util');
    class ScpClient {
        constructor(config) {
            this.config = config;
            this.headers = {
                "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
                "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
            };
            this.site_key = null;
            this.serverMap = new Map();
            this.BASE_URL = "https://www.servercontrolpanel.de";
        }

        static async create(config) {
            const client = new ScpClient(config);
            if (await client.login()) {
                if (!await client._fetchServers()) {
                    logger.sc("登录成功但获取服务器信息失败");
                }
                return client;
            }
            throw new Error("SCP登录失败");
        }

        async _findSiteKey(response) {
            if (typeof response !== 'string') throw new Error("响应不是字符串,无法查找 site_key");
            const matches = response.match(/site_key\s*=\s*"([^"]+)"/g);
            if (!matches?.length) throw new Error("未找到 site_key");
            this.site_key = matches[matches.length - 1].match(/"([^"]+)"/)[1];
            return this.site_key;
        }

        async _refreshSession() {
            const response = await util.requestPromise({
                method: 'GET',
                url: `${this.BASE_URL}/SCP/Home?site_key=${this.site_key}`,
                headers: this.headers
            });
            if (response.body.includes('SCP | Login')) {
                logger.sc('会话已过期');
                return false;
            }
            logger.sc('更新会话,site_key:', this.site_key);
            return await this._findSiteKey(response.body);
        }

        async _fetchServers() {
            try {
                if (!await this._refreshSession()) {
                    logger.sc('获取服务器列表时会话已过期');
                    return false;
                }
                const response = await util.requestPromise({
                    method: 'GET',
                    url: `${this.BASE_URL}/SCP/Home?site_key=${this.site_key}`,
                    headers: this.headers
                });
                await this._findSiteKey(response.body);
                if (!response.body || response.body.includes('SCP | Login')) {
                    logger.sc("获取服务器列表失败:未获取到有效响应或需要重新登录");
                    return false;
                }
                const linksMatch = response.body.match(/links\['[^']+'\]\s*=\s*"VServersKVM\?selectedVServerId=\d+"/g);
                if (!linksMatch) {
                    logger.sc("页面中未找到服务器信息");
                    return false;
                }
                this.serverMap.clear();
                let foundServers = false;
                for (const link of linksMatch) {
                    const [, name] = link.match(/links\['([^']+)'\]/);
                    const [, serverId] = link.match(/selectedVServerId=(\d+)/);
                    
                    if (serverId) {
                        foundServers = true;
                        logger.sc(`SCP找到服务器: ${name} (ID: ${serverId})`);
                        
                        await util.sleep(1000);
                        
                        const serverInfo = await this._getServerInfo(serverId);
                        if (serverInfo) {
                            this.serverMap.set(name, {
                                ...serverInfo,
                                name
                            });
                        }
                    }
                }
                if (!foundServers) {
                    logger.sc("未找到任何服务器信息");
                    return false;
                }
                return this.serverMap.size > 0;
            } catch (error) {
                logger.sc("获取服务器列表时出错:", error.message);
                return false;
            }
        }

        async _getServerInfo(serverId) {
            try {
                const response = await util.requestPromise({
                    method: 'GET',
                    url: `${this.BASE_URL}/SCP/VServersKVM`,
                    qs: {
                        selectedVServerId: serverId,
                        page: "vServerKVMGeneral",
                        site_key: this.site_key
                    },
                    headers: this.headers
                });

                if (!response.body) {
                    logger.sc(`服务器 ${serverId} 响应为空`);
                    return null;
                }

                await this._findSiteKey(response.body).catch(e => 
                    logger.sc(`更新 site_key 失败: ${e.message}`));

                const info = {
                    id: serverId,
                    ip: response.body.match(/<th>IPv4<\/th>[\s\S]*?<td>\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s*<br\/>/)?.[1]?.trim(),
                    hostname: response.body.match(/id="hostname"[^>]*value="([^"]+)"/)?.[1]?.trim(),
                    status: response.body.match(/>state<\/td>\s*<td>([^<]+)</)?.[1]?.trim(),
                    uptime: response.body.match(/>Uptime<\/td>\s*<td>([^<]+)</)?.[1]?.trim(),
                    traffic: response.body.match(/>Traffic[^>]*>[^>]*>([^<]+)</)?.[1]?.trim(),
                    cpu: response.body.match(/>CPU<\/td>\s*<td>(\d+)</)?.[1]?.trim(),
                    ram: response.body.match(/>RAM<\/td>\s*<td>([^<]+)</)?.[1]?.trim(),
                    disk: response.body.match(/>Disk&nbsp;1<\/td>\s*<td>([^<]+)</)?.[1]?.trim()
                };

                if (!info.ip) {
                    logger.sc(`服务器 ${serverId} 缺少关键信息:`, info);
                    logger.sc(`HTML内容:`, response.body);
                    return null;
                }

                logger.sc(`服务器 ${serverId} IP地址: ${info.ip}`);
                return info;
            } catch (error) {
                logger.sc(`获取服务器 ${serverId} 信息失败:`, error.message);
                return null;
            }
        }

        async login() {
            try {
                const loginPage = await util.requestPromise({
                    method: 'GET',
                    url: `${this.BASE_URL}/SCP/Login`,
                    headers: this.headers
                });

                if (!loginPage.body || !loginPage.headers['set-cookie']) {
                    logger.sc("登录页面获取失败");
                    return false;
                }

                const cookies = loginPage.headers['set-cookie']
                    .map(cookie => cookie.split(';')[0])
                    .filter(cookie => cookie.includes('JSESSIONID=') || cookie.includes('cookiesession1='))
                    .join('; ');

                if (!cookies) {
                    logger.sc("缺少必要cookie");
                    return false;
                }

                this.headers.Cookie = cookies;
                await this._findSiteKey(loginPage.body);
                logger.sc('初始site_key:', this.site_key);
                const loginResponse = await util.requestPromise({
                    method: 'POST',
                    url: `${this.BASE_URL}/SCP/Login`,
                    headers: this.headers,
                    form: { 
                        site_key: this.site_key, 
                        username: this.config.username, 
                        password: this.config.password 
                    }
                });

                if (loginResponse.statusCode === 302) {
                    await this._refreshSession();
                    logger.sc('SCP登录成功');
                    return true;
                }

                logger.sc(`登录失败,状态码: ${loginResponse.statusCode}`);
                logger.sc("登录响应内容:", loginResponse.body);
                return false;

            } catch (error) {
                logger.sc("登录过程中出错:", error.message);
                if (error.response) {
                    logger.sc("服务器响应:", {
                        statusCode: error.response.statusCode,
                        headers: error.response.headers,
                        body: error.response.body
                    });
                }
                return false;
            }
        }

        async rebootServer(serverId) {
            try {
                const siteKeyValid = await this._refreshSession() || await this.login();
                if (!siteKeyValid) return false;

                let success = await this._executeReboot(serverId);
                if (!success) {
                    await this._refreshSession();
                    success = await this._executeReboot(serverId);
                }
                return success;
            } catch (error) {
                logger.sc(`重启服务器 ${serverId} 时出错:`, error);
                return false;
            }
        }

        async _executeReboot(server_id) {
            const response = await util.requestPromise({
                url: `${this.BASE_URL}/SCP/VServersKVM`,
                method: 'POST',
                headers: this.headers,
                form: {
                    selectedVServerId: server_id,
                    page: "vServerKVMControl",
                    action: this.config.rebootType,
                    site_key: this.site_key
                }
            });
            await this._findSiteKey(response.body);
            const successText = this.config.rebootType === "RESET" 
                ? 'Changes successfully executed'
                : 'vServerJob_KVMControl.poweroff_text';
            return response.body.includes(successText);
        }

        findServerByIp(ip) {
            const [name, info] = [...this.serverMap].find(([, info]) => info.ip === ip) || [];
            return info ? { name, ...info } : null;
        }
    }

    async function getServersToReboot(scpClient) {
        if (!QB_CLIENTS) {
            logger.sc("下载器列表未初始化");
            return false;
        }

        SERVERS_TO_REBOOT = await Promise.all(CONFIG.clients
            .map(async clientId => {
                const client = QB_CLIENTS.find(c => c.id === clientId);
                if (!client?.clientUrl) {
                    logger.sc(`下载器 ${clientId} 无效或缺少URL信息`);
                    return null;
                }

                const ip = client.clientUrl.match(/http:\/\/([^:]+):/)?.[1];
                if (!ip) {
                    logger.sc(`下载器 ${clientId} 无法解析IP地址: ${client.clientUrl}`);
                    return null;
                }

                logger.sc(`下载器 ${clientId}(${client.alias}) URL: ${client.clientUrl}`);
                const serverInfo = scpClient.findServerByIp(ip);
                
                if (serverInfo) {
                    logger.sc(`下载器 ${clientId}(${client.alias}) 找到对应服务器: ${serverInfo.name}(${serverInfo.id})`);
                    return { clientId, ...serverInfo };
                } else {
                    logger.sc(`下载器 ${clientId}(${client.alias}) 未找到对应服务器`);
                    return null;
                }
            }))
            .then(results => results.filter(Boolean));

        return SERVERS_TO_REBOOT.length > 0;
    }

    async function controlAllTorrents(action) {
        const method = action === 'pause' ? 'pauseTorrent' : 'resumeTorrent';
        const actionText = action === 'pause' ? '暂停' : '启动';
        await Promise.all(CONFIG.clients.map(async (clientId) => {
            const client = global.runningClient[clientId];
            if (!client?.status) {
                logger.sc(`${clientId}无法连接,请检查下载器clientId是否正确,或者下载器太快跑飞了`);
                return;
            }
            await client[method]("all");
            logger.sc(`${clientId}已${actionText}所有种子`);
        }));
    }

    async function main() {
        let scpClient;
        try {
            QB_CLIENTS = util.listClient();
            logger.sc(`已获取 ${QB_CLIENTS.length} 个下载器信息`);
            scpClient = await ScpClient.create(CONFIG.scp);
            if (!await getServersToReboot(scpClient)) {
                logger.sc("没有找到需要重启的服务器");
                return;
            }
            try {
                await controlAllTorrents('pause');
                NEED_RESUME = true;
                logger.sc(`等待 ${CONFIG.delays.qbitPause} 秒后重启服务器...`);
                await util.sleep(CONFIG.delays.qbitPause * 1000);
                for (const server of SERVERS_TO_REBOOT) {
                    const success = await scpClient.rebootServer(server.id);
                    logger.sc(`${server.name}(${server.ip}) 重启${success ? '成功' : '失败'}`);
                    await util.sleep(CONFIG.delays.serverReboot * 1000);
                }
                logger.sc(`等待 ${CONFIG.delays.powerOn} 秒后重新启动下载器任务...`);
                await util.sleep(CONFIG.delays.powerOn * 1000);
                await controlAllTorrents('resume');
                NEED_RESUME = false;
            } catch (error) {
                if (NEED_RESUME) {
                    logger.sc("执行出错,尝试恢复下载器任务");
                    await controlAllTorrents('resume');
                }
                throw error;  
            }

        } catch (error) {
            logger.sc("执行过程中出错:", error);
        }
    }

    await main();
};
点赞

发表回复

电子邮件地址不会被公开。必填项已用 * 标注

×
订阅图标按钮