fix: align private and channel update semantics
(cherry picked from commit c65f76f56278f74082c4fa792ed49104d5d33c38)
This commit is contained in:
parent
dce7b92772
commit
d84fa6e126
36 changed files with 1765 additions and 382 deletions
|
|
@ -38,7 +38,7 @@ Date: 2026-06-01
|
|||
- `channels.setStickers`:files/sticker store 未接入前只允许 megagroup `inputStickerSetEmpty` 清空 no-op 兼容;非空 sticker set 返回 `STICKERSET_INVALID`,避免 TDesktop 误以为群贴纸集已持久化。`channels.reorderUsernames/toggleUsername/deactivateAllUsernames` 仍先做权限校验型兼容响应;Fragment/多 username 入口不改主 username,主 username 只允许 `channels.updateUsername` 设置或清除。
|
||||
- TDesktop 已有调用证据但非首批真实业务的入口统一注册为显式 stub:`channels.reportSpam/editLocation/convertToGigagroup/reportAntiSpamFalsePositive/setBoostsToUnblockRestrictions/setEmojiStickers/checkSearchPostsFlood/setMainProfileTab`。`channels.searchPosts` 已提升为公开频道/超级群帖子真实搜索,`checkSearchPostsFlood` 仍保持免费额度兼容 stub;二者共享 query 长度边界。`setBoostsToUnblockRestrictions` 按 Layer225 限制 0..8。`channels.restrictSponsoredMessages/updatePaidMessagesPrice/toggleAutotranslation` 已提升为最小真实设置持久化:分别回填 `ChannelFull.restricted_sponsored`、`Channel/ChannelFull.send_paid_messages_stars` + broadcast `Channel.broadcast_messages_allowed`、`Channel.autotranslation`,其中 `updatePaidMessagesPrice` 按 TDesktop 默认 app config 限制 stars<=10000,并允许 broadcast direct messages 用 `-1` 表示关闭;真实广告投放、boost/premium 校验、paid messages/monoforum/结算后续补。`channels.exportMessageLink`、`channels.readMessageContents`、`channels.getMessageAuthor`、`channels.deleteParticipantHistory`、`getInactiveChannels`、`getChannelRecommendations`、`toggleJoinToSend`、`toggleJoinRequest`、`toggleParticipantsHidden`、`toggleForum`、`toggleViewForumAsMessages`、`toggleAntiSpam`、`getLeftChannels`、`getGroupsForDiscussion`、`setDiscussionGroup` 已从该列表提升为真实实现。
|
||||
- `channels.getGroupsForDiscussion/setDiscussionGroup` 真实维护 broadcast channel 与 megagroup 的双向 `linked_chat_id`。候选列表只返回当前用户可管理的 supergroup,不返回 legacy basic group;设置时校验 access_hash、broadcast/group 类型、管理权限与 hidden prehistory,替换链接会同步清理旧 group/old broadcast,TDesktop 通过 `Channel.has_link` 和 `ChannelFull.linked_chat_id` 刷新讨论组入口。linked broadcast 发新 post 时会在 discussion megagroup 创建一条 forwarded root message,source post 保存 `discussion_channel_id/message_id`,`messages.getDiscussionMessage/getReplies/readDiscussion` 都映射到该 root 和 target channel read state。
|
||||
- TDesktop 已有源码调用但当前业务模型尚未维护独立 media/sticker/custom-emoji/saved-message tag assignment/poll/todo/scheduled 的 `messages.reportSpam/report/reportReaction/reportMessagesDelivery/reportReadMetrics/reportMusicListen/reportSponsoredMessage/getSavedReactionTags/updateSavedReactionTag/getDefaultTagReactions/getExtendedMedia/getAttachedStickers/getCustomEmojiDocuments/searchStickerSets/searchStickers/getEmojiKeywords/getEmojiKeywordsDifference/sendVote/getPollResults/getPollVotes/addPollAnswer/deletePollAnswer/getUnreadPollVotes/readPollVotes/appendTodoList/toggleTodoCompleted/getSearchCounters/getSearchResultsCalendar/getSearchResultsPositions/getOnlines/getWebPagePreview/uploadMedia/sendMedia/sendMultiMedia/getScheduledHistory/getScheduledMessages/sendScheduledMessages/deleteScheduledMessages` 均显式注册;统一做参数上限、peer 校验和空/零兼容响应,禁止落到未知 RPC。其中 `messages.readMessageContents` 已提升为 private exact-id real-partial:只校验当前账号可见 message box 并向其它 session 推 content-read update,不维护 media/reaction content-read 状态;channel/supergroup 的 `channels.readMessageContents` 会按可见消息 ID 清理当前作者视角的 unread reaction、重算 `channel_dialogs.unread_reactions_count`,并向当前账号 session 推 `updateMessageReactions` 让 TDesktop 立即去掉 dialog reaction 角标;`messages.getMessagesViews` 已维护 channel-scoped views 去重计数并返回 replies/comment 统计;`messages.getUnreadMentions/readMentions` 已维护 channel-scoped unread mention index,readMentions 返回 channel pts;`messages.sendReaction/getMessagesReactions/getMessageReactionsList/getUnreadReactions/readReactions/getRecentReactions/clearRecentReactions/getTopReactions` 已提升为 private + channel/supergroup emoji reaction 最小真实实现:private 写 `private_message_reactions` 并按双端 owner-visible message box 返回/推送 `updateMessageReactions`,channel 写 `channel_message_reactions` 并按消息作者维护 unread reaction 状态,history/getMessages/search 均回填 `message.reactions`,`add_to_recent` 写账号级最近 channel reaction 列表且 get/clear 支持 hash/notModified,`getTopReactions` 按账号使用次数排序并优先用真实 `available_reactions` catalog 兜底;`messages.getSavedReactionTags/updateSavedReactionTag` 已持久化账号级 emoji saved reaction tag 标题并向其它 session 推 `updateSavedReactionTags`,但 peer 维度 count 与 saved-message tag assignment 仍返回空;`messages.getForumTopics/getForumTopicsByID/createForumTopic/editForumTopic/updatePinnedForumTopic/reorderPinnedForumTopics/deleteTopicHistory` 已从纯空响应提升为真实最小 topic store:虚拟 General + root service message topic + bounded delete;`messages.getCommonChats` 已提升为真实共同超级群查询并回填 `users.getFullUser.common_chats_count`;`messages.report` 保持 TDesktop 分步举报 UI 所需的 choose option/add comment/reported 形态但暂不落库;report/metrics/music/sponsored 这类 telemetry 入口只返回 BoolTrue/reported,不写业务状态;TDesktop 资料页 shared media count 会用 `messages.search(limit=0, filter=photo/video/document/url/gif/music/roundVoice/poll)` 取 `messages.channelMessages.count`,当前显式返回空页/count=0,避免纯文本 channel history 污染 photos/videos/files 等计数;search calendar 空结果会回填请求 offset date/id,避免空月份重复拉取;default tag、sticker/custom emoji 与 extended media 均只返回空/notModified 或明确错误,不伪造 paid media 或 tag 绑定状态;poll/todo mutating 入口在缺少媒体 store 时返回可解释错误,不伪造 `updateMessagePoll` 或 todo service message;web preview 返回 `messageMediaEmpty` 且不会抓外网,`sendMedia(inputMediaWebPage)` 降级为纯文本发送,真实 photo/document/poll/album、todo、scheduled store 留待后续模型。
|
||||
- TDesktop 已有源码调用但当前业务模型尚未维护独立 media/sticker/custom-emoji/saved-message tag assignment/poll/todo/scheduled 的 `messages.reportSpam/report/reportReaction/reportMessagesDelivery/reportReadMetrics/reportMusicListen/reportSponsoredMessage/getSavedReactionTags/updateSavedReactionTag/getDefaultTagReactions/getExtendedMedia/getAttachedStickers/getCustomEmojiDocuments/searchStickerSets/searchStickers/getEmojiKeywords/getEmojiKeywordsDifference/sendVote/getPollResults/getPollVotes/addPollAnswer/deletePollAnswer/getUnreadPollVotes/readPollVotes/appendTodoList/toggleTodoCompleted/getSearchCounters/getSearchResultsCalendar/getSearchResultsPositions/getOnlines/getWebPagePreview/uploadMedia/sendMedia/sendMultiMedia/getScheduledHistory/getScheduledMessages/sendScheduledMessages/deleteScheduledMessages` 均显式注册;统一做参数上限、peer 校验和空/零兼容响应,禁止落到未知 RPC。其中 `messages.readMessageContents` 已提升为 private real-content-read:清理 owner 侧 `message_boxes.media_unread/reaction_unread`,有变化才生成 durable `updateReadMessagesContents`;channel/supergroup 的 `channels.readMessageContents` 会按可见消息 ID 清理当前作者视角的 unread reaction、重算 `channel_dialogs.unread_reactions_count`,并向当前账号 session 推 `updateMessageReactions` 让 TDesktop 立即去掉 dialog reaction 角标;`messages.getMessagesViews` 已维护 channel-scoped views 去重计数并返回 replies/comment 统计;`messages.getUnreadMentions/readMentions` 已维护 channel-scoped unread mention index,history/getMessages/difference/online update 按 viewer 回填 `mentioned/media_unread`,readMentions 返回 channel pts;`messages.sendReaction/getMessagesReactions/getMessageReactionsList/getUnreadReactions/readReactions/getRecentReactions/clearRecentReactions/getTopReactions` 已提升为 private + channel/supergroup emoji reaction 最小真实实现:private 写 `private_message_reactions` 并按双端 owner-visible message box 返回/推送 `updateMessageReactions`,channel 写 `channel_message_reactions` 并按消息作者维护 unread reaction 状态,history/getMessages/search 均回填 `message.reactions`,`add_to_recent` 写账号级最近 channel reaction 列表且 get/clear 支持 hash/notModified,`getTopReactions` 按账号使用次数排序并优先用真实 `available_reactions` catalog 兜底;`messages.getSavedReactionTags/updateSavedReactionTag` 已持久化账号级 emoji saved reaction tag 标题并向其它 session 推 `updateSavedReactionTags`,但 peer 维度 count 与 saved-message tag assignment 仍返回空;`messages.getForumTopics/getForumTopicsByID/createForumTopic/editForumTopic/updatePinnedForumTopic/reorderPinnedForumTopics/deleteTopicHistory` 已从纯空响应提升为真实最小 topic store:虚拟 General + root service message topic + bounded delete;`messages.getCommonChats` 已提升为真实共同超级群查询并回填 `users.getFullUser.common_chats_count`;`messages.report` 保持 TDesktop 分步举报 UI 所需的 choose option/add comment/reported 形态但暂不落库;report/metrics/music/sponsored 这类 telemetry 入口只返回 BoolTrue/reported,不写业务状态;TDesktop 资料页 shared media count 会用 `messages.search(limit=0, filter=photo/video/document/url/gif/music/roundVoice/poll)` 取 `messages.channelMessages.count`,当前显式返回空页/count=0,避免纯文本 channel history 污染 photos/videos/files 等计数;search calendar 空结果会回填请求 offset date/id,避免空月份重复拉取;default tag、sticker/custom emoji 与 extended media 均只返回空/notModified 或明确错误,不伪造 paid media 或 tag 绑定状态;poll/todo mutating 入口在缺少媒体 store 时返回可解释错误,不伪造 `updateMessagePoll` 或 todo service message;web preview 返回 `messageMediaEmpty` 且不会抓外网,`sendMedia(inputMediaWebPage)` 降级为纯文本发送,真实 photo/document/poll/album、todo、scheduled store 留待后续模型。
|
||||
- legacy chat 管理入口 `messages.getChats/getFullChat/addChatUser/deleteChatUser/editChatTitle/editChatPhoto/editChatAdmin/editChatAbout/editChatDefaultBannedRights/editChatParticipantRank` 统一映射到 megagroup/channel 语义;其中 `about` 与 default banned rights 真实持久化,default banned rights 参与普通成员发送和邀请权限校验。`editChatCreator` 已显式注册并校验 peer/user,但账号 2FA/SRP 与所有权转移事务未接入前返回可解释的密码错误,不进入 fallback。
|
||||
|
||||
## Reference Audit
|
||||
|
|
@ -114,7 +114,7 @@ username 与管理项方面,参考实现 的 `channels.checkUsername` 只校
|
|||
|
||||
参考实现 的 `messages.toggleNoForwards` 已实现 channel 路径:先校验 access hash,再发布 `ToggleChannelNoForwardsCommand`,通过 channel 聚合返回 updates;`messages.setChatAvailableReactions` 和 `messages.setChatTheme` handler 仍未实现,但 `ChannelFullReadModel/ChannelFullMapper` 已有 `ReactionType/AvailableReactions/ReactionsLimit` 到 `chatReactions*` 的映射。telesrv 因此不把 reactions 当纯兼容噪声,而是持久化到 channel setting,并在 full channel 中恢复给 TDesktop。
|
||||
|
||||
参考实现 对 TDesktop 长尾入口多采用空/BoolTrue 响应:`readMessageContents/reportSpam/setEmojiStickers/getInactiveChannels/getChannelRecommendations/checkSearchPostsFlood/setMainProfileTab` 等直接返回兼容值;`channels.getMessageAuthor` handler 存在但未实现。参考实现 的 `messages.readMessageContents` / `channels.readMessageContents` 会先查当前用户可见消息、过滤 mention/media_unread/reaction 内容状态,再向 not-me 推 `updateReadMessagesContents` 或 `updateChannelReadMessagesContents`;未找到 `channels.getMessageAuthor` 对应实现。telesrv 当前没有 media/reaction content-read 持久表,因此 private 与 channel 都只用现有 exact-id message store 校验可见消息,并把存在的 id 推给当前用户其它在线 session;缺失 id 仍按参考项目返回当前 pts/BoolTrue。`getMessageAuthor` 则按 Layer225/TDesktop monoforum 右键菜单的最小可用语义,只查可见 channel message 的 `SenderUserID` 并返回 user,完整 monoforum 管理员权限留后续模型。`deleteParticipantHistory` 做管理员权限后按固定 page size 分批删除;`searchPosts` 按 参考实现 的 PublicPosts 语义落到本项目公开 username channel_messages 查询,只返回公开频道/超级群文本消息,满页用 `next_rate + offset_peer + offset_id` seek 翻页,付费 flood/限额继续由 `checkSearchPostsFlood` 免费 stub 承接;`getInactiveChannels` 按 TDesktop Premium 限额弹窗消费方式返回当前用户 active 频道/超级群,`dates[i]` 与 `chats[i]` 对齐并按最久未活跃排序;`getChannelRecommendations` 则从空响应提升为公开 broadcast channel 推荐,指定来源时排除 source,无来源时排除当前账号已加入频道;`getGroupsForDiscussion/setDiscussionGroup` 是讨论组链路。telesrv 借鉴其删除边界:按 sender 分页取一批 message id,生成一条 channel delete update,通过 `offset` 提示客户端续删,禁止一次性展开超大历史。
|
||||
参考实现 对 TDesktop 长尾入口多采用空/BoolTrue 响应:`readMessageContents/reportSpam/setEmojiStickers/getInactiveChannels/getChannelRecommendations/checkSearchPostsFlood/setMainProfileTab` 等直接返回兼容值;`channels.getMessageAuthor` handler 存在但未实现。参考实现 的 `messages.readMessageContents` / `channels.readMessageContents` 会先查当前用户可见消息、过滤 mention/media_unread/reaction 内容状态,再向 not-me 推 `updateReadMessagesContents` 或 `updateChannelReadMessagesContents`;未找到 `channels.getMessageAuthor` 对应实现。telesrv 已为 private 接入 owner 侧 `media_unread/reaction_unread` 持久状态与 durable `read_message_contents`,channel mention/media unread 则来自 `channel_unread_mentions` viewer-state;channel unread reaction 继续由 `channels.readMessageContents/readReactions` 清理。`getMessageAuthor` 则按 Layer225/TDesktop monoforum 右键菜单的最小可用语义,只查可见 channel message 的 `SenderUserID` 并返回 user,完整 monoforum 管理员权限留后续模型。`deleteParticipantHistory` 做管理员权限后按固定 page size 分批删除;`searchPosts` 按 参考实现 的 PublicPosts 语义落到本项目公开 username channel_messages 查询,只返回公开频道/超级群文本消息,满页用 `next_rate + offset_peer + offset_id` seek 翻页,付费 flood/限额继续由 `checkSearchPostsFlood` 免费 stub 承接;`getInactiveChannels` 按 TDesktop Premium 限额弹窗消费方式返回当前用户 active 频道/超级群,`dates[i]` 与 `chats[i]` 对齐并按最久未活跃排序;`getChannelRecommendations` 则从空响应提升为公开 broadcast channel 推荐,指定来源时排除 source,无来源时排除当前账号已加入频道;`getGroupsForDiscussion/setDiscussionGroup` 是讨论组链路。telesrv 借鉴其删除边界:按 sender 分页取一批 message id,生成一条 channel delete update,通过 `offset` 提示客户端续删,禁止一次性展开超大历史。
|
||||
|
||||
参考实现 的 `channels.getAdminLog` 当前返回空结果,但保留了 read model/query 模型:按 `channel_id`、action types、skip/limit 拉取 admin log event。telesrv 采用“有真实 event store、但只实现 TDesktop 首批可见 action”的路线,避免管理入口只能打开空页。
|
||||
|
||||
|
|
@ -165,9 +165,9 @@ username 与管理项方面,参考实现 的 `channels.checkUsername` 只校
|
|||
- `ChannelMember.AvailableMinPts`:当前成员可恢复 channel difference 的 pts 下界;新加入/导入/受邀/重新加入成员初始化为加入前 `channels.pts`,`updates.getChannelDifference(pts=0)` 也会先抬到该值,避免入群前消息类 durable event 泄漏。
|
||||
- `ChannelMember.ReadInboxDate`:当前成员最后一次推进 `read_inbox_max_id` 的时间,用于 `messages.getMessageReadParticipants` 返回 `readParticipantDate.date`;不参与 channel pts。
|
||||
- `ChannelMember.SlowmodeLastSendDate`:普通成员最近一次成功发言时间,用于服务端按 channel 维度返回 `SLOWMODE_WAIT_X`;creator/admin 不受首批 slowmode 限制。
|
||||
- `ChannelMessage`:`ChannelID`、`ID`、`RandomID`、`SenderUserID`、`From`、`SendAs`、`Date`、`EditDate`、`Post`、`Silent`、`NoForwards`、`Body`、`Entities`、`ReplyTo`、`Forward`、`Action`、`Pts`、`Deleted`。
|
||||
- `ChannelMessage`:`ChannelID`、`ID`、`RandomID`、`SenderUserID`、`From`、`SendAs`、`Date`、`EditDate`、`Post`、`Silent`、`NoForwards`、`Body`、`Entities`、`ReplyTo`、`Forward`、`Action`、`Pts`、`Deleted`;`Mentioned` / `MediaUnread` 是按当前 viewer 回填的瞬时字段,不是全局消息真值。
|
||||
- `ChannelDialog`:当前 user 对 channel 的会话摘要,保存 `FolderID`、`Pinned`、`PinnedOrder`、`TopMessageID`、`ReadInboxMaxID`、`ReadOutboxMaxID`、`UnreadCount`、`UnreadMentions`、`UnreadMark`、`ViewForumAsMessages`、`NotifySettings`。channel message id 是 channel-scoped,跨频道 dialog 排序不能只看 `top_message_id`;统一按 `pinned DESC, pinned_order DESC, top_message_date DESC, top_message_id DESC, channel_id DESC` 排序,并用 `offset_date + offset_id + offset_peer(channel_id)` 做 seek cursor。folder include/exclude/read/archive/type 条件必须在 SQL `LIMIT` 前下推,避免 TDesktop dialogs 翻页重复顶部频道或自定义分组漏掉旧频道。
|
||||
- `ChannelUnreadMention`:按 `(user_id, channel_id, message_id)` 保存未读提及,`top_message_id` 支持 thread/topic 级清除;它是 owner 视角状态,不写入 `channel_update_events`,`messages.readMentions` 返回当前 channel pts 供客户端清本地 badge。
|
||||
- `ChannelUnreadMention`:按 `(user_id, channel_id, message_id)` 保存未读提及,`top_message_id` 支持 thread/topic 级清除,`media_unread` 表示该未读提及同时对应媒体内容未读;它是 owner 视角状态,不写入 `channel_update_events`,`messages.readMentions` 返回当前 channel pts 供客户端清本地 badge。
|
||||
- `ChannelUpdateEvent`:`ChannelID`、`Pts`、`PtsCount`、`Type`、`Date`、`MessageID`、`MessageIDs`、`SenderUserID`、`UserIDs`、`Payload`。
|
||||
- `ChannelAdminLogEvent`:`ChannelID`、`ID`、`UserID`、`Date`、`Type`、前后字符串/布尔/整数、前后 participant、相关 message、`Query`;用于 `channels.getAdminLog`,不参与 channel pts。
|
||||
|
||||
|
|
@ -192,7 +192,7 @@ username 与管理项方面,参考实现 的 `channels.checkUsername` 只校
|
|||
| `channel_message_reactions` | hash(`channel_id`) | `(channel_id,message_id,reacted_user_id,reaction)` 当前 reaction 状态,支持 `sendReaction/getMessagesReactions/getMessageReactionsList` |
|
||||
| `private_message_reactions` | message id fk | `(private_message_id,user_id,reaction)` 当前私聊 reaction 状态,双端 message box 共享聚合,支持 private `sendReaction/getMessagesReactions/getMessageReactionsList` |
|
||||
| `user_saved_reaction_tags` | user_id prefix | 账号级 saved-message reaction tag 标题;支持 `messages.getSavedReactionTags/updateSavedReactionTag`,不承载 message assignment/count |
|
||||
| `channel_unread_mentions` | hash(`user_id`) | owner 视角未读提及索引,支持 `messages.getUnreadMentions/readMentions`,不复制 channel message body |
|
||||
| `channel_unread_mentions` | hash(`user_id`) | owner 视角未读提及/媒体未读索引,支持 `messages.getUnreadMentions/readMentions`,不复制 channel message body |
|
||||
| `channel_update_events` | hash(`channel_id`) | channel pts durable log,供 `updates.getChannelDifference` |
|
||||
| `channel_dialogs` | hash(`user_id`) | 当前账号对 channel 的 dialog/read/folder/pin/mute 摘要 |
|
||||
| `dialog_drafts` | hash(`user_id`) | user/channel peer 云草稿;支持 forum `top_msg_id`,不复制 channel message |
|
||||
|
|
@ -213,7 +213,7 @@ username 与管理项方面,参考实现 的 `channels.checkUsername` 只校
|
|||
- `channel_messages(discussion_channel_id, discussion_message_id) WHERE discussion_channel_id <> 0 AND discussion_message_id <> 0 AND NOT deleted` 支持 broadcast post 到 discussion root 的反查与排查。
|
||||
- `channel_message_viewers(channel_id, message_id, viewer_user_id)` 主键去重;`messages.getMessagesViews` 先按最多 100 个 id 过滤可见 `channel_messages`,只对首次插入的 viewer 更新 `channel_messages.views_count`,不按 viewer 做 count 聚合扫描。
|
||||
- `channel_message_reactions(channel_id, message_id, reaction_date DESC, reacted_user_id DESC, reaction_value ASC)` 支持最近 reaction 回填和列表 seek;`(channel_id,message_id,reaction_type,reaction_value,reaction_date DESC,reacted_user_id DESC)` 支持按单个 emoji 过滤,不使用 SQL OFFSET。
|
||||
- `channel_unread_mentions(user_id, channel_id, top_message_id, message_id DESC)` 支持当前账号 unread mentions seek;发送时只为解析出的 active/可见/未读成员插入,单条消息最多 100 个候选,清除时单批最多 1000 条。
|
||||
- `channel_unread_mentions(user_id, channel_id, top_message_id, message_id DESC)` 支持当前账号 unread mentions seek;发送/编辑时只为解析出的 active/可见/未读成员插入,单条消息最多 100 个候选,清除时单批最多 1000 条;`media_unread` 随同一行保存,供 history/getMessages/getChannelDifference/online update 按 viewer 回填 `message.media_unread`。
|
||||
- `channel_update_events(channel_id, pts)` 主键;`(channel_id, pts)` 升序扫描差量。
|
||||
- `channel_dialogs(user_id, folder_id, pinned DESC, pinned_order DESC, top_message_date DESC, top_message_id DESC, channel_id DESC)` 支持 dialogs seek;`default_send_as_peer_type/default_send_as_peer_id` 只保存当前 owner 的默认发送身份,不参与列表排序。
|
||||
- `dialog_drafts(user_id, date DESC, peer_type, peer_id, top_message_id)` 支持 `messages.getAllDrafts/clearAllDrafts` 有界扫描;draft 内容用 domain JSON 保存,业务层不持有 `tg.*`。
|
||||
|
|
@ -237,6 +237,16 @@ Redis miss 恢复来源:
|
|||
|
||||
分配必须使用 Redis Lua 的初始化+递增原子脚本;批量 delete/clear 这类 `pts_count>1` 的操作必须一次性分配连续 range,PG fallback 也要实现 `NextChannelPtsN(current+count)`,不能用多次读取 `MAX(pts)+1` 模拟。事务失败但 pts 已分配时必须写 `noop` channel update 占位,避免 TDesktop channel PtsWaiter 永久 gap。
|
||||
|
||||
## Viewer State / Difference Nudge
|
||||
|
||||
`tg.Message.mentioned` 与 `media_unread` 对 channel/supergroup 来说是 viewer-specific 状态,来源只允许是 `channel_unread_mentions` 等 owner 视角 unread 表,不写入 `channel_messages` 全局行:
|
||||
|
||||
1. 发送或编辑 channel message 时,RPC 层解析 mention-name entity / `@username`,store 只为 active、可见、未读且不是 sender 自己的成员写 unread mention。带媒体的 mention 同行保存 `media_unread=true`。
|
||||
2. `messages.getHistory`、`messages.getMessages`、`updates.getChannelDifference` 与在线 `updateNewChannelMessage/updateEditChannelMessage` 都按当前 viewer 重取 unread mention 状态,再设置 TL `mentioned/media_unread`。未被 mention 的 viewer 看到同一条消息时两个 flag 必须为 false。
|
||||
3. `messages.readMentions` 清理当前 owner 的 unread mention 后,后续 history/difference 不再带 `mentioned/media_unread`。编辑移除 mention 会删除旧 unread mention;新增 mention 会补写新 owner 的 unread mention,供在线和离线差量恢复。
|
||||
4. 账号级 `updates.getDifference` 不承载 channel message 本体。若请求 date 之后当前用户 active joined channel 有 `channel_update_events`,RPC 层追加计算型 `updateChannelTooLong(channel_id, pts)` nudge,提示 TDesktop 随后调用 `updates.getChannelDifference`;该 nudge 不写 `user_update_events`,不推进账号 pts,也不影响 account difference 连续性。
|
||||
5. create/invite/join/leave/import/hide request 等 membership/state 操作即使没有 service message pts,也必须在 RPC response 和在线推送里带 `updateChannel`。megagroup 有可见 service message 时返回/推送 `updateNewChannelMessage` 占 channel pts,同时追加 `updateChannel` 刷新 participant count/self state;broadcast invite/join/leave 不占 channel pts,但仍带 viewer-specific chats 与 `updateChannel`。
|
||||
|
||||
## Create Flow
|
||||
|
||||
`messages.createChat`:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue