微信防撤回通知机制:WeChatTweak-macOS系统通知实现原理
一、痛点直击:撤回消息的"数字消失术"
你是否经历过这样的场景:刚看到微信消息预览,还没来得及细读,屏幕上却弹出"对方已撤回一条消息"的提示?这种"数字消失术"不仅打断阅读体验,更可能导致重要信息的永久丢失。WeChatTweak-macOS作为首款针对微信macOS客户端的功能增强工具,通过底层消息拦截与系统通知整合,彻底解决了这一痛点。本文将深入剖析其防撤回通知机制的实现原理,帮助开发者理解Objective-C运行时特性在实际项目中的应用。
二、技术原理概览:消息拦截的"三道防线"
WeChatTweak-macOS的防撤回通知功能基于三大核心技术构建:
2.1 关键技术组件对比
| 技术组件 | 作用范围 | 核心API | 风险等级 |
|---|---|---|---|
| Method Swizzling | 运行时方法替换 | jr_swizzleMethod:withMethod:error: | 高(需精确匹配选择器) |
| 消息数据篡改 | 消息状态修改 | ModifyMsgData:msgData: | 中(依赖内部数据结构) |
| 系统通知集成 | 用户提醒机制 | deliverNotification: | 低(系统标准API) |
三、深度解析:从消息拦截到通知展示的全流程
3.1 方法替换:防撤回的"第一道关卡"
WeChatTweak-macOS通过__attribute__((constructor))修饰的初始化函数,在程序启动时完成关键方法的替换:
static void __attribute__((constructor)) tweak(void) {
// 拦截消息撤回处理方法
[objc_getClass("FFProcessReqsvrZZ") jr_swizzleMethod:NSSelectorFromString(@"DelRevokedMsg:msgData:")
withMethod:@selector(tweak_DelRevokedMsg:msgData:)
error:nil];
// 拦截撤回提示消息添加方法
[objc_getClass("FFProcessReqsvrZZ") jr_swizzleMethod:NSSelectorFromString(@"notifyAddRevokePromptMsgOnMainThread:msgData:")
withMethod:@selector(tweak_notifyAddRevokePromptMsgOnMainThread:msgData:)
error:nil];
}
技术点睛:
FFProcessReqsvrZZ是微信内部负责消息处理的核心类,通过替换其DelRevokedMsg:msgData:方法,实现对撤回指令的拦截。使用NSSelectorFromString而非直接使用选择器,是为了规避编译时检查。
3.2 消息状态篡改:让撤回"失效"的核心逻辑
在替换后的tweak_DelRevokedMsg:msgData:方法中,通过修改消息ID实现撤回拦截:
- (void)tweak_DelRevokedMsg:(NSString *)session msgData:(MessageData *)messageData {
if (messageData.isSendFromSelf) {
// 自己发送的消息允许撤回
[self tweak_DelRevokedMsg:session msgData:messageData];
} else {
// 篡改消息ID,使系统认为是新消息
messageData.mesSvrID = messageData.mesLocalID;
[((FFProcessReqsvrZZ *)self) ModifyMsgData:session msgData:messageData];
// 主线程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
[((FFProcessReqsvrZZ *)self) notifyDelMsgOnMainThread:session msgData:messageData isRevoke:YES];
[((FFProcessReqsvrZZ *)self) notifyAddMsgOnMainThread:session msgData:messageData];
});
}
}
关键洞察:微信通过比较
mesSvrID(服务器ID)和mesLocalID(本地ID)来判断消息是否被撤回。将两者设为相等,使系统将撤回消息识别为正常消息,从而绕过删除逻辑。
3.3 系统通知构建:从消息数据到用户提醒
在tweak_notifyAddRevokePromptMsgOnMainThread:msgData:方法中,完成系统通知的构建与分发:
- (void)tweak_notifyAddRevokePromptMsgOnMainThread:(NSString *)session msgData:(MessageData *)messageData {
// 获取本地消息数据
MessageData *localMessage = [((FFProcessReqsvrZZ *)self) GetMsgData:session localId:messageData.mesLocalID];
if (!localMessage || localMessage.mesSvrID != messageData.mesLocalID) {
[self tweak_notifyAddRevokePromptMsgOnMainThread:session msgData:messageData];
} else {
// 创建系统通知
NSUserNotification *userNotification = [[NSUserNotification alloc] init];
MMServiceCenter *serviceCenter = [objc_getClass("MMServiceCenter") defaultCenter];
// 判断是群聊还是单聊
if ([session rangeOfString:@"@chatroom"].location == NSNotFound) {
// 单聊通知处理
ContactStorage *contactStorage = [serviceCenter getService:objc_getClass("ContactStorage")];
WCContactData *contact = [contactStorage GetContact:session];
userNotification.informativeText = messageData.msgContent;
} else {
// 群聊通知处理
GroupStorage *groupStorage = [serviceCenter getService:objc_getClass("GroupStorage")];
WCContactData *groupContact = [groupStorage GetGroupContact:session];
NSString *groupName = groupContact.m_nsNickName.length ?
groupContact.m_nsNickName :
[NSBundle.tweakBundle localizedStringForKey:@"Tweak.Title.Group"];
userNotification.informativeText = [NSString stringWithFormat:@"%@: %@", groupName, messageData.msgContent];
}
// 主线程分发通知
dispatch_async(dispatch_get_main_queue(), ^{
WeChatTweakNotificationType notificationType = WeChatTweak.notificationType;
if (notificationType == WeChatTweakNotificationTypeReceiveAll ||
(notificationType == WeChatTweakNotificationTypeInherited && isChatStatusNotifyOpen)) {
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:userNotification];
}
});
}
}
3.4 UI层修改:消息气泡的视觉标记
通过替换MMMessageCellView的三个关键方法,实现撤回消息的特殊标记:
// 添加撤回标记文本框
- (instancetype)tweak_initWithFrame:(NSRect)arg1 {
MMMessageCellView *view = (MMMessageCellView *)[self tweak_initWithFrame:arg1];
NSTextField *revokeTextField = [[NSTextField alloc] init];
revokeTextField.hidden = YES;
revokeTextField.tag = 9527; // 使用特殊标记值
revokeTextField.stringValue = [NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.RecalledMark"];
revokeTextField.font = [NSFont systemFontOfSize:7.0];
revokeTextField.textColor = [NSColor lightGrayColor];
[view addSubview:revokeTextField];
return view;
}
// 根据消息状态显示标记
- (void)tweak_populateWithMessage:(MMMessageTableItem *)tableItem {
[self tweak_populateWithMessage:tableItem];
BOOL recalled = tableItem.message.mesSvrID && tableItem.message.mesSvrID == tableItem.message.mesLocalID;
[((MMMessageCellView *)self).subviews enumerateObjectsUsingBlock:^(__kindof NSView * _Nonnull view, NSUInteger index, BOOL * _Nonnull stop) {
if (view.tag != 9527) return;
*stop = YES;
view.hidden = !recalled;
}];
// 设置背景色标记
((MMMessageCellView *)self).layer.backgroundColor = recalled ? WeChatTweak.maskColor.CGColor : nil;
}
实现细节:通过
tag = 9527标记自定义文本框,避免与微信原有视图冲突。当检测到消息被篡改(mesSvrID == mesLocalID)时,显示"已撤回"标记并更改气泡背景色。
四、通知分发流程:从消息拦截到用户提醒的完整链条
五、实战应用:基于WeChatTweak的功能扩展思路
5.1 通知类型扩展
WeChatTweak当前支持两种通知模式,开发者可通过扩展WeChatTweakNotificationType枚举添加更多模式:
typedef NS_ENUM(NSUInteger, WeChatTweakNotificationType) {
WeChatTweakNotificationTypeReceiveAll, // 接收所有撤回通知
WeChatTweakNotificationTypeInherited, // 继承微信通知设置
WeChatTweakNotificationTypeOnlyImportant, // 仅重要联系人通知(扩展)
WeChatTweakNotificationTypeCustomFilter // 自定义过滤规则(扩展)
};
5.2 通知内容增强
可通过添加富文本支持和操作按钮增强通知功能:
userNotification.hasActionButton = YES;
userNotification.actionButtonTitle = @"查看详情";
userNotification.otherButtonTitle = @"忽略";
userNotification.contentImage = [NSImage imageNamed:@"revoke_icon"];
六、风险与应对:内部API依赖的稳定性挑战
| 潜在风险 | 影响范围 | 应对策略 |
|---|---|---|
| 微信版本更新导致类名变更 | 全部功能失效 | 实现动态类名探测机制 |
| 消息数据结构变化 | 防撤回功能失效 | 添加数据结构兼容性检查 |
| 方法签名变更 | 部分功能异常 | 实现方法参数动态适配 |
稳定性建议:通过
objc_getClass和NSSelectorFromString动态获取类和方法,避免直接依赖微信内部符号;添加运行时类型检查,确保在内部API变更时能够优雅降级。
七、总结与展望
WeChatTweak-macOS的防撤回通知机制通过Objective-C运行时特性,实现了对闭源应用的功能增强。其核心价值不仅在于解决用户痛点,更为macOS应用逆向开发提供了典范:
- 技术层面:展示了Method Swizzling在实际项目中的安全应用
- 架构层面:构建了"拦截-篡改-通知"的三层处理模型
- 用户体验:平衡了功能实现与原生应用体验的一致性
未来版本可考虑添加的功能方向:
- 撤回消息的历史记录管理
- 自定义通知音效与样式
- 基于NSPasteboard的快速回复功能
掌握这些技术不仅能帮助开发者构建类似工具,更能深入理解macOS应用的内部工作机制。建议开发者在使用此类技术时,始终遵守软件使用协议和相关法律法规,仅在个人学习研究范围内使用。
如果觉得本文对你有帮助,请点赞、收藏、关注三连,下期将带来"WeChatTweak多开功能的实现原理"深度解析!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



