@iota #70 HDSky (空)刷流脚本。使用VT的定时任务功能,进种子时候选择暂停。 分类保持sky,刷空时候尽量不要添加其他站点,只刷空: /** * ======================================================================================= * 脚本名称:HDSky (空) 极速周转引擎 * * [功能概述] * 建议设置(重要):Cron 设置 * * * * * (每分钟),RSS 任务限速 250KB/s,qB 取消预分配。 * 1. 智能流控:配合 RSS任务初始 限速250KB/s ,1小时后自动解封 + 强制汇报抢 Peer。 * 2. 精准止盈:扣除首小时无效流量,计算真实有效分享率,达标(3.1)即删。 * 3. 深度防御: * - 黑车防御 (只下不上) * - 死车清理 (3h无进度) * - 超时熔断 (24h未完成) * - 空间阶梯 (剩余空间越少,清理阈值越高) * ======================================================================================= */ async () => { const moment = require('moment'); const util = require('../libs/util'); const clients = global.runningClient; // ============================================================================== // 详细策略配置 (参数均已校对) // ============================================================================== const CONFIG = { // 目标分类 (脚本忽略大小写,填 'sky' 可匹配 'SKY', 'Sky') category: 'sky', // --- A. 进攻策略 (刷流核心) --- protectTime: 3600, // 保护期: 1小时 (秒) protectSpeed: 250 * 1024, // 保护期限速: 250 KB/s targetRatio: 3.1, // 目标有效分享率: 3.1倍 reannounceWindow: 300, // 解封后抢 Peer 窗口: 5分钟 // --- B. 防御策略 --- // 1. 黑车防御 (下载中且无上传) blackCar: { minAge: 600, // 最小观察时间: 10分钟 (防止误判刚添加的种子) minDownSpeed: 1024, // 下载速度 > 1 KB/s maxUpSpeed: 10 * 1024 // 上传速度 < 10 KB/s }, // 2. 死车清理 (3小时不开车) deadTorrent: { age: 3 * 3600, // 添加时间 > 3小时 maxUpSpeed: 50 * 1024 // 上传速度 < 50 KB/s (且进度为0) }, // 3. 超时熔断 (24小时未下载完) timeout: { age: 24 * 3600, // 添加时间 > 24小时 maxUpSpeed: 1024 * 1024 // 上传速度 < 1 MB/s (且未完成) }, // 4. 绝对生命周期 (兜底策略) maxLife: 48 * 3600, // 超过 48小时 强制删除 // 5. 空间保护:低于该值时不再自动恢复/解封,避免和清理打架 panicSpaceGB: 5, // --- C. 空间阶梯清理 --- // 逻辑:剩余空间越少,清理条件越苛刻 (从上到下匹配) spaceLadder: [ { free: 100, type: 'speed', val: 50 }, // <100G, 速度 < 50 KB/s { free: 50, type: 'upload', val: 1 }, // <50G, 总上传 < 1 MB { free: 40, type: 'upload', val: 5 }, // <40G, 总上传 < 5 MB { free: 30, type: 'upload', val: 8 }, // <30G, 总上传 < 8 MB { free: 20, type: 'upload', val: 50 }, // <20G, 总上传 < 50 MB { free: 10, type: 'upload', val: 130 }, // <10G, 总上传 < 130 MB { free: 5, type: 'upload', val: 230 } // <5G, 总上传 < 230 MB ] }; // ============================================================================== // 辅助函数:批量高效查询历史记录 const fetchHistoryRecords = async (hashList, minTime) => { if (!hashList || hashList.length === 0) return new Map(); try { const sql = `SELECT hash, upload, time FROM torrent_flow WHERE time >= ?`; const records = await util.getRecords(sql, [minTime]); const map = new Map(); for (const r of records) { if (hashList.includes(r.hash)) { if (!map.has(r.hash)) map.set(r.hash, []); map.get(r.hash).push(r); } } return map; } catch (e) { return new Map(); } }; for (const clientId of Object.keys(clients)) { const client = clients[clientId]; const maindata = client.maindata; const torrents = maindata?.torrents; const serverState = maindata?.serverState || maindata?.server_state; // 基础数据校验,防止报错 if (!torrents || !serverState) continue; const now = moment().unix(); const currentFreeSpace = serverState.free_space_on_disk || 0; const freeSpaceGB = currentFreeSpace / (1024 ** 3); const isPanicSpace = freeSpaceGB < CONFIG.panicSpaceGB; // ========================================================= // 1. 筛选目标种子 (双向忽略大小写) // ========================================================= const targetTorrents = torrents.filter(t => (t.category || '').toLowerCase().includes(CONFIG.category.toLowerCase()) ); if (targetTorrents.length === 0) continue; // 筛选过保护期的 Hash,用于查库 (减少数据库压力) const activeHashes = targetTorrents .filter(t => (now - t.addedTime) > CONFIG.protectTime) .map(t => t.hash); // 批量查库 (只查过去48小时的记录) const historyMap = await fetchHistoryRecords(activeHashes, now - CONFIG.maxLife); for (const t of targetTorrents) { const age = now - t.addedTime; const state = t.state || ''; const stateLower = String(state).toLowerCase(); const isFinished = t.progress === 1; const uploadSpeed = typeof t.uploadSpeed === 'number' ? t.uploadSpeed : (typeof t.upspeed === 'number' ? t.upspeed : 0); const downloadSpeed = typeof t.dlspeed === 'number' ? t.dlspeed : (typeof t.downloadSpeed === 'number' ? t.downloadSpeed : 0); const currentUploadLimit = t.originProp && typeof t.originProp.up_limit === 'number' ? t.originProp.up_limit : (typeof t.uploadLimit === 'number' ? t.uploadLimit : 0); // 仅忽略明确的过渡状态;不再忽略 error / missingFiles,交给后续清理逻辑处理 if (['checkingup', 'checkingdl', 'moving', 'unknown', 'metadl'].includes(stateLower)) continue; // ================= 模块 A: 计算有效产出 ================= let effectiveRatio = 0; if (age > CONFIG.protectTime) { const boundaryTime = t.addedTime + CONFIG.protectTime; const records = historyMap.get(t.hash) || []; // 找到 1小时节点之后的第一条记录作为基准 const startRecord = records.filter(r => r.time >= boundaryTime).sort((a, b) => a.time - b.time)[0]; const baseUpload = startRecord ? startRecord.upload : 0; const effectiveUpload = Math.max(0, t.uploaded - baseUpload); effectiveRatio = t.size > 0 ? (effectiveUpload / t.size) : 0; } // ================= 模块 B: 自动化流控 ================= let targetLimit = -1; let needReannounce = false; let needResume = false; const resumableStates = ['pauseddl', 'pausedup', 'stopped']; // 空间过低时,不自动恢复/不自动解封,避免和空间清理打架 if (!isPanicSpace) { if (age < CONFIG.protectTime) { targetLimit = CONFIG.protectSpeed; // 潜伏期 250K if (resumableStates.includes(stateLower)) { needResume = true; // 保护期内自动拉起 } } else { targetLimit = 0; // 爆发期 无限 if (resumableStates.includes(stateLower)) { needResume = true; // 过保护期后自动拉起并解封 } // 刚过 1小时 的窗口期强制汇报 if (age < (CONFIG.protectTime + CONFIG.reannounceWindow)) { if (['uploading', 'stalledup', 'forcedup', 'downloading', 'stalleddl'].includes(stateLower) || needResume) { if (client.reannounceTorrent && typeof client.reannounceTorrent === 'function') { needReannounce = true; } } } } // 执行流控 (仅当数值变动时调用) if (targetLimit !== -1 && currentUploadLimit !== targetLimit) { await client.setSpeedLimit(t.hash, 'upload', targetLimit); } // 拉起暂停种子 if (needResume) { if (client.resumeTorrent && typeof client.resumeTorrent === 'function') { await client.resumeTorrent(t.hash); } else if (client.startTorrent && typeof client.startTorrent === 'function') { await client.startTorrent(t.hash); } } // 1小时后自动解封后的抢 Peer 汇报 if (needReannounce) { await client.reannounceTorrent(t.hash); } } // ================= 模块 C: 防御与清理 (逻辑判定) ================= let deleteReason = null; // 0. [异常] 报错/丢失文件优先清理 if (stateLower.includes('error')) { deleteReason = '异常清理(error)'; } else if (stateLower.includes('missingfiles')) { deleteReason = '异常清理(missingFiles)'; } // 1. [止盈] 达标删除 else if (effectiveRatio >= CONFIG.targetRatio) { deleteReason = `产出达标(Eff:${effectiveRatio.toFixed(2)})`; } // 2. [兜底] 绝对生命周期结束 else if (age > CONFIG.maxLife) { deleteReason = `周期结束(${CONFIG.maxLife / 3600}h)`; } // 3. [防御] 超时熔断 (24h未下载完) else if (!isFinished && age > CONFIG.timeout.age && uploadSpeed < CONFIG.timeout.maxUpSpeed) { deleteReason = "超时熔断(24h未完)"; } // 4. [防御] 死车清理 (3h零进度) else if (t.progress === 0 && age > CONFIG.deadTorrent.age && uploadSpeed < CONFIG.deadTorrent.maxUpSpeed) { deleteReason = "死车清理(3h零进度)"; } // 5. [防御] 黑车检测 (下载中, 进度>1%, 下>1K, 上<10K) else if (stateLower === 'downloading' && t.progress > 0.01 && age > CONFIG.blackCar.minAge) { if (downloadSpeed > CONFIG.blackCar.minDownSpeed && uploadSpeed < CONFIG.blackCar.maxUpSpeed) { deleteReason = "黑车清理(只下不上)"; } } // 6. [防爆] 空间阶梯清理 (Oracle) else { for (const ladder of CONFIG.spaceLadder) { if (freeSpaceGB < ladder.free) { // 类型 1: 速度检测 (KB/s) if (ladder.type === 'speed') { // 给 1.5h 内的新种豁免权 (除非是黑车,黑车上面已处理) if (age > 1.5 * 3600 && uploadSpeed < ladder.val * 1024) { deleteReason = `空间紧缩(<${ladder.free}G): 低速(${ladder.val}K)`; break; } } // 类型 2: 总上传检测 (MB) else if (ladder.type === 'upload') { // 给 1h 内的新种豁免权 if (age > 3600 && t.uploaded < ladder.val * 1024 * 1024) { deleteReason = `空间紧缩(<${ladder.free}G): 贡献低(<${ladder.val}M)`; break; } } } } } // 执行删除 if (deleteReason) { // true 代表同时删除文件 await client.deleteTorrent(t.hash, true); } } } };
@prinze #74 目前脚本最不好搞的点就是动态判断(极限刷流/平稳保种的同时,又要根据硬件情况动态设置参数,又不能完全暴力导致卡顿封号断流),完全暴力是最简单的,毕竟只需要把各个参数调到最高就好了. 欢迎兄弟测试和反馈,脚本肯定会不断改进的。
@iota #70 HDSky (空)刷流脚本。使用VT的定时任务功能,进种子时候选择暂停。 分类保持sky,刷空时候尽量不要添加其他站点,只刷空:
@Supcutie #69 已切换V4+M1,我的猫站达成PB就指望你了
@prinze #72 我现在测试的就是两台NC 刷的V4+M1模式 在跑猫, VT规则设置好就行
@Supcutie #73 ↑ 3.04 TB ↓ 4.74 TB这几天我刷的不太好,看看未来几天上传能不能超过下载量
@prinze #74
目前脚本最不好搞的点就是动态判断(极限刷流/平稳保种的同时,又要根据硬件情况动态设置参数,又不能完全暴力导致卡顿封号断流),完全暴力是最简单的,毕竟只需要把各个参数调到最高就好了.
欢迎兄弟测试和反馈,脚本肯定会不断改进的。
更新3.5版本动态控制器完成。折腾一整天,目前在服务器上测试中。
强强强!支持大佬的干货分享!
@Supcutie #71 谢谢佬,我去试试
先滚去收藏夹吃灰
牛啊