MSG 审核发送逻辑

坂道46 MSG 平台消息从提交到粉丝收到的完整流程

整体流程图
┌─────────────────────────────────────────────────────────────────────┐ │ MSG 消息生命周期 │ └─────────────────────────────────────────────────────────────────────┘ 成员 (MSG App) │ │ 写消息 + 提交 │ 系统分配 message_id (递增整数) │ 记录 created_at ▼ ┌──────────────────────┐ │ Staff 审核 (或跳过) │ │ │ │ 56-74% 秒审秒发 │ │ 26-44% 延迟审核 │ └──────────┬───────────┘ │ ┌──────┴──────┐ │ │ ▼ ▼ ┌─────────────┐ ┌──────────────────┐ │ 手动发布 (手打) │ │ 定时发布 (定时) │ │ │ │ │ │ staff手点发送 │ │ 成员设定 HH:MM │ │ 秒数=随机值 │ │ 系统在HH:MM:XX发 │ │ ID降=0 │ │ XX=指纹秒(固定) │ │ 立即推送 │ │ ID降=提前量 │ └──────┬──────┘ └────────┬─────────┘ │ │ └────────┬─────────┘ │ ▼ 粉丝收到消息 (published_at) │ ▼ msg-pusher 轮询API (getTimeline) │ ▼ ┌────────────────┐ │ 推送到QQ/TG/Discord │ │ 媒体下载+文件名记录 │ └────────────────┘
分步说明
STEP 1 — 成员提交消息
成员在 MSG App 写消息并提交
系统分配 message_id(递增整数,全局唯一),记录 created_at(提交时间)。此时消息还未发布,粉丝看不到。
STEP 2 — Staff 审核(或秒审秒发)
大部分消息秒审秒发,少量延迟审核
从 ID下降数据看:56-74% 的消息 ID降=0(提交后立即发布,基本没审核或秒审),26-44% 有审核延迟(ID降1~100+,延迟几秒到几小时不等)。
审核不是必经步骤——很多消息 staff 看一眼就过,甚至可能自动放行。真正卡审核的是少数(可能涉及敏感内容或需要确认的消息)。
注意:D1 API 的 created_at 和 published_at 78%相同,不暴露真实审核延迟,只记录最终发布时间。
STEP 3a — 手动发布(手打)
staff 审核通过后手点发送按钮
消息立即发布。published_at 的秒数是随机的(取决于 staff 手点的时刻)。
特征:秒数随机、ID降=0(提交后立即发布,没有其他消息插队)。
STEP 3b — 定时发布(定时)
成员设定 HH:MM,系统到点自动发布
系统在 HH:MM:XX 发布,其中 XX 是系统级固定偏移(指纹秒)。成员看不到也改不了这个秒数。
特征:秒数=指纹秒、ID降>0(提交后等了一段时间才发布,期间其他消息先发了)。
STEP 4 — msg-pusher 轮询
msg-pusher 轮询 MSG 平台 API
msg-pusher 每 15 秒轮询 /v2/groups/{groupId}/timeline,获取最新消息的 published_at(UTC时间)。
下载媒体文件,文件名格式:成员名_YYYYMMDD_HH-mm-ss_msgId.ext(时间取自 published_at 的 UTC 值)。
推送到 QQ群(NapCat)、Telegram、Discord Webhook。
ID下降原理

定时消息提交时就分配了 ID,但发布时间在设定的时间点。期间其他成员的消息先发布,导致定时消息的 ID 比发布时相邻消息的 ID 小。这个差值叫「ID下降」,提前量越大,ID下降越大。

时间轴 ──────────────────────────────────────────▶ 手打消息A 手打消息B 定时消息X 手打消息C │ │ │ │ ▼ ▼ ▼ ▼ ID=100 ID=101 ID=098 ID=103 │ ▲ │ ▲ │ ▲ │ │ ID降=0 │ ID降=0 │ ID降=3 │ │ 即时发布 │ 即时发布 │ 定时发布 │ 提交顺序: A(100) → X(098) → B(101) → C(103) 发布顺序: A(100) → B(101) → X(098) → C(103) ↑ X的ID比B小 因为X提交时B还没提交 但X定时在B之后才发 → ID降 = 101-098 = 3 提前量越大 → 等待发布的时间越长 → ID降越大
实际例子
浅井恋乃未设定 22:53 定时发送 → 消息在 22:53:09 发布(指纹秒09)
但她提交时 ID=355000,而 22:53 时其他成员的最新 ID 已经到 355080
→ ID降 = 355080 - 355000 = 80
→ 说明她提前了约 80 条消息的时间(约几小时)提交
指纹秒原理

指纹秒 = 定时发布器的系统级固定偏移。成员设定 "22:54 发送",系统在 22:54:XX 发布,XX 是系统级固定值。系统升级或维护后指纹秒会变更。

成员设定: 22:54 发送 系统执行: 22:54:09 发布 (樱坂46 指纹秒=09) ┌──────────────────────────────────────────────────┐ │ 秒数分布 (樱坂46 全部消息) │ │ │ │ :00 :01 :02 ... :08 ::09 :10 :11 ... :58 :59 │ │ ▁ ▁ ▁ ▁ ▆▆▆▆ ▁ ▁ ▁ ▁ │ │ ↑ │ │ 788条 集中在 :09 │ │ 其他秒数只有几十条 │ │ → 只有定时发布器能产生这种集中 │ └──────────────────────────────────────────────────┘ 手打消息: 秒数随机 (staff手点时刻) 定时消息: 秒数=指纹秒 (系统固定偏移) 随机基线: 1/60 = 1.67% 的手打消息碰巧落在指纹秒
指纹秒变化历史

三团都在 2026年3~5月期间频繁换指纹秒,5月之后各自稳定。v9 分析按周动态匹配指纹秒,修正了切换前的定时消息漏判。

樱坂46 — 换了4次

2025年12月 :37
→
2026年3月 :15
→
2026年4月 :44
→
2026年5月起 :09

日向坂46 — 换了3次

2026年3月 :28
→
2026年4月 :18
→
2026年5月起 :37

乃木坂46 — 换了3次

2026年4月初 :48
→
2026年4月末 :07
→
2026年5月起 :45
定时率判定公式

总定时% = 指纹秒定时% + 非指纹秒额外定时%

指纹秒定时% = 指纹秒占比 - 1.67%(随机基线)
非指纹秒额外定时% = (非指纹秒ID降≥50率 - 手打基线) × 非指纹秒占比

指纹秒: 秒数 == 当周指纹秒 → 定时发布器发的
非指纹秒ID降≥50: 秒数不是指纹秒,但 ID 比发布时相邻消息小50+ → 走 staff 手动通道的定时
真实% = 100% - 总定时%

判定逻辑
为什么需要两层判定?
1. 指纹秒法能抓住大部分定时消息,但有两类漏网:
  • 成员设定的时间恰好落在指纹秒(极少数)
  • staff 手动操作定时发布(不走自动发布器,秒数随机)
2. 第二类用 ID降≥50 补抓:手打消息 ID降=0(秒审秒发),定时消息 ID降>0(提交后等了才发)
3. 手打基线 = 纯手打成员的 ID降≥50 率(排除审核延迟导致的误判)
数据来源与时区

媒体文件名时间:msg-pusher 轮询 MSG 平台 API(getTimeline)获取 published_at(UTC),写入文件名(toISOString())。
QQ群推送时间:NapCat 记录的推送时间,北京时间(CST, UTC+8)。
本页所有时间已统一转换为日本时间(JST, UTC+9)——即粉丝在日本收到消息的时间。

不使用 D1 归档数据的时间字段:D1 的 created_at 和 published_at 78%完全相同,不暴露真实审核延迟,不可靠。D1 仅用于成员→团映射。