数据来源:服务端标记
限制信息通过 MTProto 协议同步到客户端,分为两个层面:
1. 消息级标记 CopyProtected
- 定义:
MessageFlags.CopyProtected = MessageFlags(rawValue: 512)(bit 512)submodules/Postbox/Sources/Message.swift:495submodules/Postbox/Sources/Message.swift:866(StoreMessageFlags)
- 解析来源: MTProto 消息 flag 第 26 位
submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift:1044-1046,1201-1203
2. 对话级标记 copyProtectionEnabled
- 频道:
TelegramChannelFlags.copyProtectionEnabled(bit 8)submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift:181- 从 MTProto 第 27 位解析:
submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift:125-126
- 群组:
TelegramGroupFlags.copyProtectionEnabled(bit 4)submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramGroup.swift:122- 从 MTProto 第 25 位解析:
submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift:56-57
3. 服务端切换 API
submodules/TelegramCore/Sources/TelegramEngine/Peers/CopyProtection.swift:7-17- 调用
messages.toggleNoForwards接口
核心判断逻辑
通用判断 Message.isCopyProtected()
submodules/TelegramCore/Sources/Utils/MessageUtils.swift:382-392
func isCopyProtected() -> Bool {
if self.flags.contains(.CopyProtected) { return true } // 消息级标记
if group.flags.contains(.copyProtectionEnabled) { return true } // 群组级标记
if channel.flags.contains(.copyProtectionEnabled) { return true } // 频道级标记
return false
}
Peer 级判断 Peer.isCopyProtectionEnabled
submodules/TelegramCore/Sources/Utils/PeerUtils.swift:236-245
var isCopyProtectionEnabled: Bool {
switch self {
case let group as TelegramGroup: return group.flags.contains(.copyProtectionEnabled)
case let channel as TelegramChannel: return channel.flags.contains(.copyProtectionEnabled)
default: return false
}
}
macOS 特有判断 Peer.isCopyProtected(对群主/管理员豁免)
Telegram-Mac/CoreExtension.swift:3810-3818
var isCopyProtected: Bool {
if let peer = self as? TelegramGroup {
return peer.flags.contains(.copyProtectionEnabled) && !peer.groupAccess.isCreator
} else if let peer = self as? TelegramChannel {
return peer.flags.contains(.copyProtectionEnabled) && !(peer.adminRights != nil || peer.groupAccess.isCreator)
} else {
return false
}
}
macOS 层执行点
| 功能 | 文件 | 位置 | 行为 |
|---|---|---|---|
| Cmd+C 复制文本 | ChatSelectText.swift | L110-115 | 拦截复制、弹 alert |
| 右键菜单-复制文本 | ChatMessageMenuItems.swift | L690 | 不添加菜单项 |
| 右键菜单-复制链接 | ChatMessageMenuItems.swift | L701 | 不添加菜单项 |
| 转发到其他聊天 | ChatInputAccessory.swift | L297, L328 | 不添加菜单项 |
| 转发能力 | ChatRowItem.swift | L1076-1078 | canForward 返回 false |
| Gallery 保存按钮 | GalleryModernControls.swift | L190-220 | fastSaveControl.isHidden = true |
| Gallery 右键-另存为 | GalleryViewer.swift | L1362-1369 | 不添加菜单项 |
| Gallery 分享 | GalleryViewer.swift | L377-384 | canShare 返回 false |
| MGalleryItem 分享 | MGalleryItem.swift | L156 | canShare 返回 false |
| MGalleryItem 保存 | MGalleryItem.swift | L326 | canSave 返回 false |
| Gallery 文本识别 | GalleryRecognition.swift | L27 | 跳过 OCR 识别 |
| 列表缩略图防截屏 | ChatListRowView.swift | L702 | preventsCapture = true |
| 管理员开关 UI | ChannelVisibilityController.swift | L719-730 | Restrict Copying 开关 |
iOS 层关键执行点(供参考)
| 功能 | 文件 | 位置 | 行为 |
|---|---|---|---|
| 文本选择复制 | ChatMessageTextBubbleContentNode.swift | L1408-1409 | textSelectionNode.enableCopy = false |
| 上下文菜单复制 | ChatInterfaceStateContextMenus.swift | L1259-1263 | Copy 菜单项不添加 |
| 上下文菜单保存到文件 | ChatInterfaceStateContextMenus.swift | L1369 | 不添加菜单项 |
| 选中面板-转发/分享 | ChatMessageSelectionInputPanelNode.swift | L218-233 | 弹 copyProtectionTip |
| 另一聊天回复 | ChatMessageActionOptions.swift | L424-425 | canReplyInAnotherChat = false |
| Gallery 分享/编辑 | ChatItemGalleryFooterContentNode.swift | L943-946 | canShare=false, canEdit=false |
| Gallery 防截屏 | ChatImageGalleryItem.swift | L456 | setLayerDisableScreenshots |
| Sticker 分享 | ChatMessageStickerItemNode.swift | L566 | needsShareButton = false |
| 动画 Sticker 分享 | ChatMessageAnimatedStickerItemNode.swift | L945 | needsShareButton = false |
| 视频消息分享 | ChatMessageInstantVideoItemNode.swift | L384 | needsShareButton = false |
| Bubble 分享 | ChatMessageBubbleItemNode.swift | L1783 | needsShareButton = false |
| Video captureProtect | NativeVideoContent.swift | L431-432 | captureProtected = true |
| PeerInfo 防截屏 | PeerInfoScreen.swift | L5378-5382 | setLayerDisableScreenshots |
| Story 防截屏 | StoryItemImageView.swift | L48-56 | UITextField.secureTextEntry 方式 |
| 防截屏底层实现 | UIKitRuntimeUtils/UIKitUtils.m | L247-269 | setLayerDisableScreenshots() |
架构流程图
服务端 (MTProto)
│
▼
Parsing Layer
├── StoreMessage_Telegram.swift → MessageFlags.CopyProtected (bit 512)
└── ApiGroupOrChannel.swift → TelegramChannelFlags / TelegramGroupFlags .copyProtectionEnabled
│
▼
Data Model Layer
├── Message.swift → flags.contains(.CopyProtected)
├── SyncCore_TelegramChannel.swift → channel.flags.contains(.copyProtectionEnabled)
└── SyncCore_TelegramGroup.swift → group.flags.contains(.copyProtectionEnabled)
│
▼
Business Logic Layer
├── MessageUtils.swift (isCopyProtected) → 三者 OR 判断
├── PeerUtils.swift (isCopyProtectionEnabled) → peer 级判断
└── CoreExtension.swift (macOS Peer.isCopyProtected) → 对管理员豁免
│
▼
UI Enforcement Layer
├── ChatSelectText.swift → Cmd+C 拦截
├── ChatMessageMenuItems.swift → 右键菜单项控制
├── GalleryModernControls.swift → 保存按钮隐藏
├── GalleryViewer.swift → 分享/另存为控制
├── ChatInputAccessory.swift → 转发菜单控制
├── ChatRowItem.swift → 转发能力控制
├── ChatListRowView.swift → 防截屏
└── GalleryRecognition.swift → 文本识别