亲爱的朋友,在使用本脚本前,请知悉:大多数论坛将自动化发帖视为违规行为。使用本脚本可能导致账号被封禁、扣除积分等后果。本脚本仅供个人学习与技术研究使用,一切操作风险需由您自行承担。请理性使用,遵守论坛规则。
脚本装好后,我们需要告诉它一些基本信息。
访问天雪论坛(skyeysnow.com 或 skyey2.com)的任意页面,点击浏览器右上角的脚本猫图标,在菜单中点击 "打开设置"。
在弹出的面板中,你需要关注以下几个核心设置:
为了模拟真人,脚本分两种工作节奏:
填好后,点击面板底部的保存按钮即可。
设置完成后,你只需要保持浏览器打开着天雪论坛的页面,脚本就会在后台默默帮你回复了。为了确保长期挂机成功,请务必在浏览器中禁用浏览器节能模式,并关闭“自动将标签页置于睡眠状态”,并将www.skyey2.com和skyeysnow.com列入“使这些站点保持活动状态”的白名单(这些设置通常位于浏览器设置中的 系统和性能/性能 中)
如果你想确认它是否正常工作,可以:
不用担心脚本会疯狂刷屏,它内置了多重保护:
Q1:我开了电脑但脚本没回复,怎么回事?
A:脚本需要网页保持打开状态。如果你关闭了天雪论坛的网页,或者电脑进入了休眠/睡眠状态,或者在脚本猫内没有打开脚本,脚本就会停止工作。
Q2:我想换一批回复的诗句怎么办?
A:点击脚本猫图标 → "打开设置",在"诗词内容"框里删掉旧的,换上新的,记得一行一句,然后保存。
Q3:白天和夜间的间隔时间可以改吗?
A:完全可以!在设置面板里,你可以自由调整白天和夜间的"延迟下限"和"延迟上限"(单位是分钟)。
Q4:我想让脚本立刻停一会怎么办?
A:点击脚本猫图标,选择 "停止自动回复" 即可。想恢复时,刷新一下网页或者重新点击菜单触发。
| 遇到的问题 | 尝试的解决方法 |
|---|---|
| 点击图标没反应 | 刷新当前网页,或检查扩展是否被禁用 |
| 提示找不到 tid | 检查设置里的 tid 是否填写正确,且帖子确实存在 |
| 日志显示"跳过" | 这是正常的防刷机制,说明还没到发帖时间或间隔不够 |
| 设置了夜间模式但白天也发 | 检查电脑的系统时间是否准确,以及夜间开始/结束时间是否设置正确 |
最后的小建议:建议把夜间最大发送数保持在 5 条以内,白天间隔设置在 25 分钟以上,这样最接近真人的发帖习惯,也更安全哦!
以下为专业内容: 一、脚本概览
本脚本是一个 Discuz! 论坛水楼自动回复工具,核心行为链为:
读取配置 → 计算延迟 → 等待 → 自动发帖 → 记录日志 → 重启定时器
| 特性 | 说明 |
|---|---|
| 白天/夜间双模式 | 不同时段使用不同的延迟范围和发送配额 |
| 夜间限额 | 每夜最多发送 N 条,跨零点自动重置计数 |
| 防连续刷楼 | 检测自己发帖后需间隔若干条其他用户帖子才发 |
| 多标签互斥(主控选举) | 多标签页同时挂机时,仅一个标签页负责发帖,其余静默 |
| 心跳保活 | 主控每 2 秒写入心跳时间戳,6 秒未更新则其他页可抢占 |
| 设置面板 | 菜单命令 GM_registerMenuCommand 呼出 HTML 弹窗 |
| 操作日志 | GM 存储持久化,支持清空、颜色区分类型 |
| XSS 防护 | 所有用户输入经过 escapeHtml 转义 |
@run-at document-idle → 页面 DOM 解析完成后执行 IIFE
主入口逻辑(文件末尾):
所有 GM 存储键在脚本顶部集中声明,共 21 个 GM 键 + 4 个 sessionStorage 键。
| 键名 | 用途 | 默认值 | 数据类型 |
|---|---|---|---|
| skyeysnow_water_tid | 水楼帖子 ID | '' | 字符串(纯数字) |
| skyeysnow_water_set_ym | 月份检测标记 | '' | 字符串(YYYY-MM) |
| skyeysnow_reply_delay_min | 白天延迟下限(分钟) | 25 | 数字 |
| skyeysnow_reply_delay_max | 白天延迟上限(分钟) | 35 | 数字 |
| skyeysnow_auto_jump | 自动跳转开关 | true | 布尔 |
| skyeysnow_poems | 回复诗句(换行分隔) | 内置 10 句古诗 | 字符串 |
| skyeysnow_night_start | 夜间开始小时 | 23 | 数字(0-23) |
| skyeysnow_night_end | 夜间结束小时 | 8 | 数字(0-23) |
| skyeysnow_night_delay_min | 夜间延迟下限(分钟) | 60 | 数字 |
| skyeysnow_night_delay_max | 夜间延迟上限(分钟) | 120 | 数字 |
| skyeysnow_night_max_count | 夜间最多发送条数 | 3 | 数字 |
| skyeysnow_night_sent_count | 夜间已发送计数 | 0 | 数字 |
| skyeysnow_night_date | 夜间时段日期标识 | '' | 字符串(YYYY-MM-DD) |
| skyeysnow_night_warned | 夜间限额警告标记 | '' | 字符串 |
| skyeysnow_pending_target_time | 下次发帖目标时间戳 | 0 | 数字(timestamp) |
| skyeysnow_operation_log | 操作日志(JSON 数组) | [] | 字符串(JSON) |
| skyeysnow_max_log | 最大日志条数 | 100 | 数字 |
| skyeysnow_my_username | 用户自定义用户名 | '' | 字符串 |
| skyeysnow_post_gap | 防刷间隔所需其他帖子数 | 1 | 数字 |
| skyeysnow_leader_id | 主控标签页 ID | '' | 字符串(TAB_ID) |
| skyeysnow_leader_heartbeat | 主控心跳时间戳 | 0 | 数字(timestamp) |
| 键名 | 用途 | 生命周期 |
|---|---|---|
| skyeysnow_need_refresh | 标记需刷新页面以获取最新 DOM | 页面刷新即清除 |
| skyeysnow_no_username_alerted | 标记已弹出过「未检测到用户名」警告 | 页面刷新即清除 |
| skyeysnow_skip_tid_prompt | 标记跳过 tid 提示 | 页面刷新即清除 |
| skyeysnow_tab_id | 本标签页唯一标识 | 关闭标签页即清除 |
设计意图:sessionStorage 用于「一次性」标志,避免跨标签页污染,同时保证刷新页面后状态可恢复。
XSS 防护核心函数。对 & < > " ' 五个字符做 HTML 实体编码。
调用点:
从 window.location.search 中解析 tid 参数。
返回 YYYY-MM 格式字符串,用于月份检测。
统一的上限非法回退规则:Math.max(min + 10, defMax)。避免各处回退逻辑不一致。
读取白天延迟配置,含非法值回退。getDelayMax 确保上限 >= 下限 + 10。
读取自动跳转开关,兼容油猴管理器布尔返回值差异(String(v) !== 'false')。
读取诗句配置,按换行分割过滤空行。为空时返回内置 10 句古诗。
判断当前是否处于夜间时段。支持跨零点场景(如 23:00~08:00)。
if (start <= end) { 普通区间:hour >= start && hour < end }
else { 跨零点区间:hour >= start || hour < end }
计算当前夜间时段的日期标识。跨零点时(start > end 且 hour < end),日期减 1 天,使夜间时段归属到「开始那天」。
根据 isNightTime() 返回当前时段的延迟范围对象 {min, max}。
夜间发送权限检查:
夜间发送计数 +1,同时更新 NIGHT_DATE 为当前 periodKey。
返回 window.location.origin,兜底 'https://skyeysnow.com'。
多个标签页同时运行脚本时,仅一个标签页负责发帖,避免重复发帖和 GM 存储竞争。
| 变量/常量 | 说明 |
|---|---|
| TAB_ID | 本标签页唯一 ID(sessionStorage 持久化,降级为内存级) |
| LEADER_TIMEOUT_MS | 心跳超时阈值 = 6000ms |
| HEARTBEAT_INTERVAL_MS | 心跳间隔 = 2000ms |
| STORAGE_KEY_LEADER_ID | GM 存储中的主控 ID |
| STORAGE_KEY_LEADER_HB | GM 存储中的心跳时间戳 |
| _heartbeatTimer | 心跳 interval 定时器 ID |
竞态分析:两个标签页同时检测到主控超时后同时写入,后写入的会覆盖先写入的。回读校验确保只有最后写入的那个通过,另一个被降级。极端情况下两个都通过的概率极低(纳秒级窗口),最坏后果仅为多发一条帖子。
| 事件 | 主控行为 | 非主控行为 |
|---|---|---|
| 页面加载 | 尝试抢占主控 → 成功则启动心跳 | 抢占失败 → 静默等待 |
| 定时器触发 | 执行发帖逻辑 | 检查主控身份 → 非主控则跳过 |
| 页面卸载 | 不清理主控身份(等心跳超时自动失效) | 清理定时器 |
| 主控标签页关闭 | 心跳 6 秒后超时 | 检测到超时 → 抢占主控 |
设计取舍:不主动释放主控身份是为了避免刷新页面瞬间被其他页抢占导致状态不一致。代价是关闭标签页后有 6 秒的「僵尸主控」窗口。
{
"time": "2026-10-10 22:30:45",
"type": "auto|test|error|info",
"message": "[自动发送] 自动回复已触发(诗句:xxx)"
}
渲染日志到设置面板的 #auto-reply-log 区域,按类型着色:
_logDisplayCallback 是模块内私有函数指针,在脚本初始化时指向 updateLogDisplay。addLog 写入存储后自动调用回调刷新 UI,无需轮询。
getRemainingTimeString() 计算 PENDING_TARGET 与当前时间的差值,格式化为 X分Y秒 或 即将触发… 或 未启动。设置面板中每秒更新显示。
这是整个脚本的核心控制流函数,所有分支路径如下:
doReply(immediate)
│
├─ [非immediate] 主控检查
│ ├─ 抢占主控成功 → 启动心跳
│ └─ 抢占失败 → 静默跳过,重启定时器,_autoReplyPending = false,return
│
├─ [非immediate] 夜间限额检查
│ ├─ 已达上限 → 记录日志,重启定时器,_autoReplyPending = false,return
│ └─ 未达上限 → 继续
│
├─ [非immediate][gap>0] 用户名检查
│ ├─ 用户名缺失 → 暂停自动回复,弹窗警告(一次性),_autoReplyPending = false,return
│ └─ 有用户名 → 继续
│
├─ [非immediate][gap>0] 防刷前强制刷新
│ ├─ sessionStorage 有 NEED_REFRESH 标记 → 清除标记,继续(说明刚刷新完)
│ └─ 无标记 → 写入 NEED_REFRESH,location.reload(),return(页面刷新后从else分支进入)
│
├─ [非immediate] 防刷检查 canPostNow()
│ ├─ 不通过 → 记录日志,重启定时器,_autoReplyPending = false,return
│ └─ 通过 → 继续
│
├─ 内容准备
│ ├─ 诗句为空 → 记录错误,重启定时器,_autoReplyPending = false,return
│ ├─ DOM 元素缺失 → 记录错误,重启定时器,_autoReplyPending = false,return
│ └─ 正常 → 继续
│
├─ 执行发帖
│ ├─ 随机选一句诗 → 写入 #fastpostmessage
│ ├─ 调用 ensureJumpToLastPage()
│ ├─ submitEl.click() 触发提交
│ ├─ [非immediate] 设置 8 秒兜底刷新定时器
│ ├─ 记录日志(auto/test)
│ ├─ [非immediate] recordNightSent()
│ ├─ [非immediate] 3 秒后重启定时器
│ ├─ _autoReplyPending = false
│ ├─ 清除 PENDING_TARGET
│ └─ 移除 visibilitychange 监听
│
└─ catch (e)
├─ 记录错误日志
├─ [非immediate] 3 秒后重启定时器
├─ _autoReplyPending = false
└─ 清除 PENDING_TARGET
该标志防止同一时刻多个发帖请求并发执行。所有退出路径(包括异常路径)都必须将其设为 false。经逐行审查,所有 9 条退出路径均已正确处理。
防刷检查依赖 DOM 数据,而 DOM 可能因其他标签页发帖而过期。因此:
调用 cleanupTimers(true) — 保留 PENDING_TARGET(不清零),因为:
统一清理 5 个 timer/timeout + 心跳:
beforeunload 事件调用 cleanupTimers(true) — 清理定时器但保留 PENDING_TARGET,保证刷新后能恢复倒计时。
+--------------------------------------------------+
| 天雪水楼脚本设置 |
| 距离下次发帖:XX分XX秒 [倒计时] |
| ------------------------------------------------ |
| 水楼 tid |
| [________] |
| [白天延迟下限] [白天延迟上限] |
| [x] 自动跳转到水楼帖 |
| ------------------------------------------------ |
| 夜间模式 |
| [开始小时] [结束小时] |
| [夜间延迟下限] [夜间延迟上限] |
| 夜间最多发送条数 |
| [____] |
| ------------------------------------------------ |
| 防连续刷楼 |
| 我的用户名 |
| [________] |
| 间隔所需其他帖子数 |
| [____] |
| [提示文本] |
| 回复诗句(每行一句) |
| [________] (textarea) |
| ------------------------------------------------ |
| [保存] [立即发一条(测试)] [重置月份] [关闭] |
| ------------------------------------------------ |
| 操作日志 [清空日志] |
| [日志内容区域] |
+--------------------------------------------------+
关键顺序:所有 GM_setValue 在 confirm 之前执行,避免用户点取消后设置丢失(历史 bug 修复)。
清空 SK_DATE → 下次加载页面时重新触发 promptForTid。
使用 for 循环(非递归)最多提示 10 次:
历史修复:原版使用递归调用导致栈溢出风险,已改为 for 循环。
页面加载
│
├─ 未配置 tid / 月份变更 → promptForTid() → 用户输入 → 跳转至水楼帖
│
├─ 需自动跳转 → location.href 跳转
│
└─ 已在水楼帖
│
├─ 尝试成为主控 → 成功:启动心跳
│
├─ 恢复持久化定时器 / 启动新定时器
│
└─ 等待定时器触发 → doReply() → 发帖 → 3秒后重启
标签页A(主控) 标签页B(备胎)
│ │
每2秒写心跳 每25~35分钟检查
│ │
│ 检测到主控超时?
│ │
├─ 执行发帖 ──────────► 是:抢占主控
│ │
│ ├─ 抢占成功 → 启动心跳
│ └─ 抢占失败 → 静默等待
│
关闭标签页
│
心跳停止
│
6秒后超时
│
───────────────────────────► 标签页B检测到超时 → 抢占主控
每日 23:00 进入夜间模式
│
├─ 夜间延迟范围生效(60~120分钟)
├─ 每夜限额计数开始(默认3条)
├─ 每发一条 → 计数+1
└─ 达到上限 → 跳过发送 + 记录日志
│
▼
次日 08:00 进入白天模式
│
├─ 限额计数重置
├─ 白天延迟范围生效(25~35分钟)
└─ 恢复正常发送
不同油猴管理器对 GM_getValue 返回布尔值的处理不一致(有的返回 boolean,有的返回字符串 "true"/"false")。脚本统一使用以下模式:
function shouldAutoJump() {
var v = GM_getValue(STORAGE_KEY_AUTO_JUMP, true);
return v !== false && v !== 'false';
}
隐私模式/企业策略下 sessionStorage 可能抛 SecurityError。脚本在 TAB_ID 初始化时捕获异常,降级为内存级 ID(页面刷新后丢失,但脚本仍可运行)。
try { logs = JSON.parse(GM_getValue(STORAGE_KEY_LOG, '[]')); }
catch (e) { logs = []; }
防止 GM 存储损坏导致脚本静默失败。
| 注入点 | 防护方式 |
|---|---|
| 日志渲染 | escapeHtml() 转义时间戳和消息 |
| 设置面板 value 注入 | escapeHtml() 转义 tid/用户名/诗句 |
| textarea 内容注入 | escapeHtml() 转义诗句 |
设置面板保存时对所有用户输入做类型和范围校验:
| 编号 | 问题 | 影响 | 建议方案 |
|---|---|---|---|
| 1 | 非主控页定时器持续运行 | 轻微资源浪费 | 非主控页仅在主控超时后启动定时器 |
| 2 | _tryBecomeLeader 理论竞态窗口 | 极低概率双主控 | 可接受,论坛挂机场景不影响稳定性 |
| 3 | getNightPeriodKey 逻辑重复 | 代码整洁度 | 抽取为共享函数 |
| 4 | ensureJumpToLastPage 依赖 #fastpostrefresh | 某些主题找不到控件 | 降级为静默跳过(已有) |
| 5 | 主控关闭后 6 秒接管延迟 | 短时间内重复发帖风险 | 可接受 |
| 6 | 用户名自动读取可能暴露身份 | 用户隐私 | 可改为默认空值 + 提示手动填写 |
搜索 [AutoReply] 可过滤所有脚本日志。多标签相关日志含 [多标签] 子标签。
临时将夜间开始时间设为当前小时,触发夜间逻辑验证,调试完毕后再改回。
打开 2 个标签页同时挂机,观察控制台:
在控制台执行:
GM_getValue('skyeysnow_leader_id') // 当前主控ID
GM_getValue('skyeysnow_pending_target_time') // 下次发帖时间戳
GM_getValue('skyeysnow_night_sent_count') // 夜间已发送计数
基本逻辑40行,防御性编程1000行