mirror of
https://github.com/Sunnyyoung/WeChatTweak-macOS.git
synced 2025-05-22 22:36:07 +08:00
229 lines
13 KiB
Objective-C
229 lines
13 KiB
Objective-C
//
|
|
// AntiRevoke.m
|
|
// WeChatTweak
|
|
//
|
|
// Created by Sunny Young on 2021/5/9.
|
|
// Copyright © 2021 Sunnyyoung. All rights reserved.
|
|
//
|
|
|
|
#import "WeChatTweak.h"
|
|
#import "NSBundle+WeChatTweak.h"
|
|
|
|
@implementation NSObject (AntiRevoke)
|
|
|
|
static void __attribute__((constructor)) tweak(void) {
|
|
[objc_getClass("FFProcessReqsvrZZ") jr_swizzleMethod:NSSelectorFromString(@"FFToNameFavChatZZ:sessionMsgList:") withMethod:@selector(tweak_FFToNameFavChatZZ:sessionMsgList:) error:nil];
|
|
|
|
[objc_getClass("MMMessageCellView") jr_swizzleMethod:NSSelectorFromString(@"initWithFrame:") withMethod:@selector(tweak_initWithFrame:) error:nil];
|
|
[objc_getClass("MMMessageCellView") jr_swizzleMethod:NSSelectorFromString(@"populateWithMessage:") withMethod:@selector(tweak_populateWithMessage:) error:nil];
|
|
[objc_getClass("MMMessageCellView") jr_swizzleMethod:NSSelectorFromString(@"layout") withMethod:@selector(tweak_layout) error:nil];
|
|
}
|
|
|
|
- (void)tweak_FFToNameFavChatZZ:(MessageData *)message sessionMsgList:(nullable id)sessionMsgList {
|
|
// - (id)GetMsgData:(id)arg1 svrId:(unsigned long long)arg2;
|
|
SEL GetMsgDataSelector = NSSelectorFromString(@"GetMsgData:svrId:");
|
|
if (![self respondsToSelector:GetMsgDataSelector]) {
|
|
// Fallback to origin method
|
|
return [self tweak_FFToNameFavChatZZ:message sessionMsgList:sessionMsgList];
|
|
}
|
|
// Decode message
|
|
NSDictionary *dictionary = [NSDictionary dictionaryWithXMLString:message.msgContent];
|
|
NSString *session = dictionary[@"revokemsg"][@"session"];
|
|
NSString *newMessageID = dictionary[@"revokemsg"][@"newmsgid"];
|
|
NSString *replaceMessage = dictionary[@"revokemsg"][@"replacemsg"];
|
|
// Get message data
|
|
MessageData *messageData = ((id (*)(id, SEL, id, unsigned long long))objc_msgSend)(self, GetMsgDataSelector, session, newMessageID.longLongValue);
|
|
if (messageData.isSendFromSelf) {
|
|
// Fallback to origin method
|
|
[self tweak_FFToNameFavChatZZ:message sessionMsgList:sessionMsgList];
|
|
} else {
|
|
switch (WeChatTweak.revokedMessageStyle) {
|
|
case WTRevokedMessageStyleClassic:
|
|
[self handleRevokedMessageIntoClassicStyleWithSession:session messageData:messageData replaceMessage:replaceMessage];
|
|
break;
|
|
case WTRevokedMessageStyleMask:
|
|
[self handleRevokedMessageIntoMaskStyleWithSession:session messageData:messageData replaceMessage:replaceMessage];
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)handleRevokedMessageIntoClassicStyleWithSession:(NSString *)session messageData:(MessageData *)messageData replaceMessage:(NSString *)replaceMessage {
|
|
// Prepare message data
|
|
MessageData *localMessageData = messageData;
|
|
MessageData *promptMessageData = ({
|
|
MessageData *data = [[objc_getClass("MessageData") alloc] initWithMsgType:10000];
|
|
data.msgStatus = 4;
|
|
data.toUsrName = localMessageData.toUsrName;
|
|
data.fromUsrName = localMessageData.fromUsrName;
|
|
data.mesSvrID = localMessageData.mesSvrID;
|
|
data.mesLocalID = localMessageData.mesLocalID;
|
|
data.msgCreateTime = localMessageData.msgCreateTime;
|
|
data.msgContent = ({
|
|
NSString *fromUserName = [replaceMessage componentsSeparatedByString:@" "].firstObject;
|
|
NSString *userRevoke = [NSString stringWithFormat:@"%@ %@ ", fromUserName, [NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.Recalled"]];
|
|
NSString *tips = [NSString stringWithFormat:[NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.InterceptedARecalledMessage"], userRevoke];
|
|
NSMutableString *msgContent = [NSMutableString stringWithString:tips];
|
|
switch (localMessageData.messageType) {
|
|
case MessageDataTypeText: {
|
|
if (localMessageData.msgContent.length) {
|
|
if ([session rangeOfString:@"@chatroom"].location == NSNotFound) {
|
|
[msgContent appendFormat:@"\"%@\"", localMessageData.msgContent];
|
|
} else {
|
|
[msgContent appendFormat:@"\"%@\"", [localMessageData.msgContent componentsSeparatedByString:@":\n"].lastObject];
|
|
}
|
|
} else {
|
|
[msgContent appendString:[NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.AMessage"]];
|
|
}
|
|
break;
|
|
}
|
|
case MessageDataTypeImage:
|
|
[msgContent appendFormat:@"<%@>", [NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.Image"]]; break;
|
|
case MessageDataTypeVoice:
|
|
[msgContent appendFormat:@"<%@>", [NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.Voice"]]; break;
|
|
case MessageDataTypeVideo:
|
|
[msgContent appendFormat:@"<%@>", [NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.Video"]]; break;
|
|
case MessageDataTypeSticker:
|
|
[msgContent appendFormat:@"<%@>", [NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.Sticker"]]; break;
|
|
case MessageDataTypeAppUrl:
|
|
[msgContent appendFormat:@"<%@>", [NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.Link"]]; break;
|
|
default:
|
|
[msgContent appendString:[NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.AMessage"]]; break;
|
|
}
|
|
msgContent.copy;
|
|
});
|
|
data;
|
|
});
|
|
// Prepare notification information
|
|
MMServiceCenter *serviceCenter = [objc_getClass("MMServiceCenter") defaultCenter];
|
|
NSUserNotification *userNotification = [[NSUserNotification alloc] init];
|
|
BOOL isChatStatusNotifyOpen = YES;
|
|
if ([session rangeOfString:@"@chatroom"].location == NSNotFound) {
|
|
ContactStorage *contactStorage = [serviceCenter getService:objc_getClass("ContactStorage")];
|
|
WCContactData *contact = [contactStorage GetContact:session];
|
|
isChatStatusNotifyOpen = [contact isChatStatusNotifyOpen];
|
|
userNotification.informativeText = replaceMessage;
|
|
} else {
|
|
GroupStorage *groupStorage = [serviceCenter getService:objc_getClass("GroupStorage")];
|
|
WCContactData *groupContact = [groupStorage GetGroupContact:session];
|
|
isChatStatusNotifyOpen = [groupContact isChatStatusNotifyOpen];
|
|
NSString *groupName = groupContact.m_nsNickName.length ? groupContact.m_nsNickName : [NSBundle.tweakBundle localizedStringForKey:@"Tweak.Title.Group"];
|
|
userNotification.informativeText = [NSString stringWithFormat:@"%@: %@", groupName, replaceMessage];
|
|
}
|
|
// - (void)AddLocalMsg:(id)arg1 msgData:(id)arg2;
|
|
SEL addMsgSelector = NSSelectorFromString(@"AddLocalMsg:msgData:");
|
|
if ([self respondsToSelector:addMsgSelector]) {
|
|
((void (*)(id, SEL, id, id))objc_msgSend)(self, addMsgSelector, session, promptMessageData);
|
|
}
|
|
// Dispatch notification
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
// Deliver notification
|
|
RevokeNotificationType notificationType = [[NSUserDefaults standardUserDefaults] integerForKey:WeChatTweakPreferenceRevokeNotificationTypeKey];
|
|
if (notificationType == RevokeNotificationTypeReceiveAll || (notificationType == RevokeNotificationTypeFollow && isChatStatusNotifyOpen)) {
|
|
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:userNotification];
|
|
}
|
|
});
|
|
}
|
|
|
|
- (void)handleRevokedMessageIntoMaskStyleWithSession:(NSString *)session messageData:(MessageData *)messageData replaceMessage:(NSString *)replaceMessage {
|
|
MMRevokeMsgService *service = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MMRevokeMsgService")];
|
|
[service.db insertRevokeMsg:({
|
|
RevokeMsgItem *item = [[objc_getClass("RevokeMsgItem") alloc] init];
|
|
item.svrId = @(messageData.mesSvrID).stringValue;
|
|
item.createTime = UINT32_MAX;
|
|
item;
|
|
})];
|
|
WeChat *wechat = [objc_getClass("WeChat") sharedInstance];
|
|
if ([wechat respondsToSelector:NSSelectorFromString(@"chatsViewController")]) {
|
|
id chatsViewController = [wechat valueForKey:@"chatsViewController"];
|
|
if ([chatsViewController respondsToSelector:NSSelectorFromString(@"chatDetailSplitViewController")]) {
|
|
id chatDetailSplitViewController = [chatsViewController valueForKey:@"chatDetailSplitViewController"];
|
|
if ([chatDetailSplitViewController respondsToSelector:NSSelectorFromString(@"chatMessageViewController")]) {
|
|
id chatMessageViewController = [chatDetailSplitViewController valueForKey:@"chatMessageViewController"];
|
|
if ([chatMessageViewController respondsToSelector:NSSelectorFromString(@"reloadTableView")]) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
((void (*)(id, SEL))objc_msgSend)(chatMessageViewController, NSSelectorFromString(@"reloadTableView"));
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Prepare notification information
|
|
MMServiceCenter *serviceCenter = [objc_getClass("MMServiceCenter") defaultCenter];
|
|
NSUserNotification *userNotification = [[NSUserNotification alloc] init];
|
|
BOOL isChatStatusNotifyOpen = YES;
|
|
if ([session rangeOfString:@"@chatroom"].location == NSNotFound) {
|
|
ContactStorage *contactStorage = [serviceCenter getService:objc_getClass("ContactStorage")];
|
|
WCContactData *contact = [contactStorage GetContact:session];
|
|
isChatStatusNotifyOpen = [contact isChatStatusNotifyOpen];
|
|
userNotification.informativeText = replaceMessage;
|
|
} else {
|
|
GroupStorage *groupStorage = [serviceCenter getService:objc_getClass("GroupStorage")];
|
|
WCContactData *groupContact = [groupStorage GetGroupContact:session];
|
|
isChatStatusNotifyOpen = [groupContact isChatStatusNotifyOpen];
|
|
NSString *groupName = groupContact.m_nsNickName.length ? groupContact.m_nsNickName : [NSBundle.tweakBundle localizedStringForKey:@"Tweak.Title.Group"];
|
|
userNotification.informativeText = [NSString stringWithFormat:@"%@: %@", groupName, replaceMessage];
|
|
}
|
|
// Dispatch notification
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
// Deliver notification
|
|
RevokeNotificationType notificationType = [[NSUserDefaults standardUserDefaults] integerForKey:WeChatTweakPreferenceRevokeNotificationTypeKey];
|
|
if (notificationType == RevokeNotificationTypeReceiveAll || (notificationType == RevokeNotificationTypeFollow && isChatStatusNotifyOpen)) {
|
|
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:userNotification];
|
|
}
|
|
});
|
|
}
|
|
|
|
- (instancetype)tweak_initWithFrame:(NSRect)arg1 {
|
|
MMMessageCellView *view = (MMMessageCellView *)[self tweak_initWithFrame:arg1];
|
|
NSTextField *revokeTextField = [[NSTextField alloc] init];
|
|
revokeTextField.hidden = YES;
|
|
revokeTextField.editable = NO;
|
|
revokeTextField.selectable = NO;
|
|
revokeTextField.bordered = NO;
|
|
revokeTextField.drawsBackground = NO;
|
|
revokeTextField.usesSingleLineMode = YES;
|
|
revokeTextField.tag = 9527;
|
|
revokeTextField.stringValue = [NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.RecalledMark"];
|
|
revokeTextField.font = [NSFont systemFontOfSize:10];
|
|
revokeTextField.textColor = [NSColor lightGrayColor];
|
|
[revokeTextField sizeToFit];
|
|
[view addSubview:revokeTextField];
|
|
return view;
|
|
}
|
|
|
|
- (void)tweak_populateWithMessage:(MMMessageTableItem *)tableItem {
|
|
[self tweak_populateWithMessage:tableItem];
|
|
MMRevokeMsgService *service = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MMRevokeMsgService")];
|
|
BOOL style = tableItem.message.messageType != MessageDataTypePrompt && [service.db getRevokeMsg:@(tableItem.message.mesSvrID).stringValue] != NULL;
|
|
[((MMMessageCellView *)self).subviews enumerateObjectsUsingBlock:^(__kindof NSView * _Nonnull view, NSUInteger index, BOOL * _Nonnull stop) {
|
|
if (view.tag != 9527) {
|
|
return ;
|
|
}
|
|
*stop = YES;
|
|
view.hidden = !style;
|
|
}];
|
|
((MMMessageCellView *)self).layer.backgroundColor = style ? [NSColor.systemYellowColor colorWithAlphaComponent:0.3].CGColor : ((MMMessageCellView *)self).layer.backgroundColor;
|
|
}
|
|
|
|
- (void)tweak_layout {
|
|
[self tweak_layout];
|
|
[((MMMessageCellView *)self).subviews enumerateObjectsUsingBlock:^(__kindof NSView * _Nonnull view, NSUInteger index, BOOL * _Nonnull stop) {
|
|
if (view.tag != 9527) {
|
|
return ;
|
|
}
|
|
*stop = YES;
|
|
view.frame = ({
|
|
NSView *avatarView = ((MMMessageCellView *)self).avatarImgView;
|
|
CGFloat x = CGRectGetMidX(avatarView.frame) - CGRectGetWidth(view.frame) / 2.0;
|
|
CGFloat y = CGRectGetMinY(avatarView.frame) - CGRectGetHeight(view.frame);
|
|
NSRect frame = NSMakeRect(x, y, CGRectGetWidth(view.frame), CGRectGetHeight(view.frame));
|
|
frame;
|
|
});
|
|
}];
|
|
}
|
|
|
|
@end
|