Skip to content

bridge: project Telegram member mutes as restricted participants - #17

Open
std-external wants to merge 2 commits into
std-microblock:mainfrom
std-external:fix/telegram-mute-state
Open

std-external wants to merge 2 commits into
std-microblock:mainfrom
std-external:fix/telegram-mute-state

Conversation

@std-external

@std-external std-external commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

问题

在 Telegram 客户端里对群成员「禁言 / Restrict」之后,界面上显示成「已解除禁言」,QQ 侧也没有禁言。

原因 1(写侧,参数映射错了):一次禁言被当成解除禁言下发

  • Telegram Desktop 及其衍生客户端(materialgram 同源,Telegram/SourceFiles/data/data_chat_participant_status.h:51 的 ChatRestriction 枚举)没有 SendMessages 这一位;RestrictionsToMTP(data_chat_participant_status.cpp:147-177)把「禁止发言」写成 send_plain + send_photos/send_videos/send_roundvideos/send_audios/send_voices/send_docs/send_stickers/send_gifs/send_games/send_inline/embed_links/send_polls,从不设置 send_messages(客户端源码里 f_send_messages 出现 0 次)。
  • bridge 判断禁言只看旧的 sendMessages 位(packages/bridge/src/dialogs.ts:2090),对现代客户端永远为 false,于是把一次正常禁言映射成 { type: 'unmute' } 发给平台——客户端成功后就显示「已解除禁言」。

原因 2(读侧,状态没回传):成员投影里没有受限成员

  • 平台成员列表里没有禁言字段(packages/platform-crossgram/src/protocol.ts:517 的 WireMemberPage 只有 user 和 role),_makeChannelParticipant(packages/bridge/src/dialogs.ts:5562)只会返回 channelParticipantCreator/Admin/Self/Participant,永远不会返回 channelParticipantBanned。
  • channels.editBanned 返回的 updates 也是空的,客户端刷新成员信息后禁言状态就消失了。

修复

  1. 写侧:新增 restrictsSending()(packages/bridge/src/dialogs.ts:6386),把 send_messages、send_media、send_plain 与各媒体位一起判定。有发送限制且 until_date 为空或未过期 → 禁言(空则按永久 0x7fffffff 下发);所有位都不带、或 until_date 已过去 → 解除禁言(不会把 0 当成禁言,也不会把过去的时间当成禁言)。
  2. 读侧:DialogRpc 记住经 Telegram 下发的成员禁言,_makeChannelParticipant 把仍在有效期内的成员投影成 channelParticipantBanned(bannedRights 为「禁止全部可禁用操作」)。
  3. channels.editBanned 的返回里带上 updateChannelParticipant(禁言 / 解除禁言),客户端不用重拉成员列表就能更新状态;踢人不推送成员更新(平台成员列表随后会去掉这个人)。

验证

  • packages/bridge/src/conversation-kinds.test.ts:
    • 新增 reads Telegram Desktop granular send restrictions as a mute:只带 send_plain + 媒体位、until_date=0 → {type:'mute', untilDate:0x7fffffff};until_date 为未来 → 带该时间戳的 mute;所有位都不带 / until_date 已过去 → {type:'unmute'};投影为 channelParticipantBanned 且带回 untilDate。该用例在旧映射下失败(下发的是 unmute),新映射下通过。
    • 原有用例 projects Telegram mute and kick requests to platform moderation actions 扩展为:禁言 → getParticipant 返回 channelParticipantBanned、updateChannelParticipant 正常返回,解除禁言 → 恢复 channelParticipant。
  • vitest run packages/bridge/src --exclude='**/*.e2e.test.ts':832 通过;唯一失败是 voice-worker-client.test.ts 需要本机 cargo(环境问题)。
  • tsc -p tsconfig.test.json:改动的两个文件无错误(其余报错来自本机 WebUI 依赖未安装,与本改动无关)。

已知限制 / 未验证

  • 禁言状态只存在于 bridge 进程内:重启后丢失;在 QQ 侧设置的禁言不会出现在 Telegram 侧(需要 qqnt-bridge 在成员列表里报告禁言时长,本仓库看不到该服务的代码)。
  • 平台只支持「整个禁言」,Telegram 里只禁某类媒体的部分限制会被投影成完全禁言(以及向 QQ 下发整员禁言)。
  • 「全员禁言」走 messages.editChatDefaultBannedRights,本仓库没有注册该方法(未注册方法由 packages/mtproto/src/rpc/router.ts:43 统一返回 METHOD_NOT_IMPLEMENTED),仍然不可用。
  • 真实 QQ 群里的禁言是否真的落到 QQNT(setGroupMemberShutUp 成功与否、永久禁言换算成时长是否被 QQ 的上限拒绝)需要在有 QQNT 桥的环境实测;本次只验证了协议层、参数映射与投影层。

来源:crossgram 测试群里的反馈(禁言后显示「已解除禁言」)。

channels.editBanned 只把禁言下发给平台,随后 channels.getParticipant(s) 仍然
把这个成员投影成普通的 channelParticipant,客户端刷新后看不到任何禁言状态,
看上去就像禁言没生效。记住经 Telegram 下发的禁言并投影成
channelParticipantBanned,同时在该 RPC 的 updates 里带上
updateChannelParticipant,让客户端立刻更新成员状态。
Telegram Desktop 及其衍生客户端不再设置 chatBannedRights.send_messages,
「禁止发言」被拆成 send_plain 和各媒体位。editChannelBanned 之前只看
send_messages,于是每次正常的禁言都被判成解除禁言、给平台下发 unmute,
界面上就显示成「已解除禁言」。改为检查整组发送位,并为过期的 until_date
保留解除语义。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant