mirror of
https://github.com/Sunnyyoung/WeChatTweak-macOS.git
synced 2026-07-28 22:14:33 +08:00
Fix anti-revoke message cell mask style issue
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// AntiRevoke.h
|
||||
// WeChatTweak
|
||||
//
|
||||
// Created by Sunny Young on 2021/5/9.
|
||||
// Copyright © 2021 Sunnyyoung. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <objc/runtime.h>
|
||||
#import <objc/message.h>
|
||||
#import <JRSwizzle/JRSwizzle.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSObject (AntiRevoke)
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,230 @@
|
||||
//
|
||||
// AntiRevoke.m
|
||||
// WeChatTweak
|
||||
//
|
||||
// Created by Sunny Young on 2021/5/9.
|
||||
// Copyright © 2021 Sunnyyoung. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AntiRevoke.h"
|
||||
#import "WeChatTweakHeaders.h"
|
||||
#import "WTConfigManager.h"
|
||||
#import "NSString+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
|
||||
NSString *session = [message.msgContent tweak_subStringFrom:@"<session>" to:@"</session>"];
|
||||
NSUInteger newMessageID = [message.msgContent tweak_subStringFrom:@"<newmsgid>" to:@"</newmsgid>"].longLongValue;
|
||||
NSString *replaceMessage = [message.msgContent tweak_subStringFrom:@"<replacemsg><![CDATA[" to:@"]]></replacemsg>"];
|
||||
// Get message data
|
||||
MessageData *messageData = ((id (*)(id, SEL, id, unsigned long long))objc_msgSend)(self, GetMsgDataSelector, session, newMessageID);
|
||||
if (messageData.isSendFromSelf) {
|
||||
// Fallback to origin method
|
||||
[self tweak_FFToNameFavChatZZ:message sessionMsgList:sessionMsgList];
|
||||
} else {
|
||||
switch (WTConfigManager.sharedInstance.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 = @"[已撤回]";
|
||||
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
|
||||
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// RecallCacheManager.h
|
||||
// WeChatTweak
|
||||
//
|
||||
// Created by Sunny Young on 2019/8/29.
|
||||
// Copyright © 2019 Sunnyyoung. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <MMKV/MMKV.h>
|
||||
|
||||
@class MessageData;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RecallCacheManager : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
+ (void)insertRevokedMessage:(MessageData *)message;
|
||||
+ (BOOL)containsRevokedMessage:(MessageData *)message;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,47 +0,0 @@
|
||||
//
|
||||
// RecallCacheManager.m
|
||||
// WeChatTweak
|
||||
//
|
||||
// Created by Sunny Young on 2019/8/29.
|
||||
// Copyright © 2019 Sunnyyoung. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RecallCacheManager.h"
|
||||
#import "WeChatTweakHeaders.h"
|
||||
|
||||
@interface RecallCacheManager()
|
||||
|
||||
@property (nonatomic, strong) MMKV *kv;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RecallCacheManager
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
[MMKV setLogLevel:MMKVLogNone];
|
||||
_kv = [MMKV mmkvWithID:@"Recall.cache"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static RecallCacheManager *shared;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
shared = [[RecallCacheManager alloc] init];
|
||||
});
|
||||
return shared;
|
||||
}
|
||||
|
||||
+ (void)insertRevokedMessage:(MessageData *)message {
|
||||
NSString *identifer = [NSString stringWithFormat:@"%ud-%lld-%ud", message.mesLocalID, message.mesSvrID, message.msgCreateTime];
|
||||
[RecallCacheManager.sharedInstance.kv setBool:YES forKey:identifer];
|
||||
}
|
||||
|
||||
+ (BOOL)containsRevokedMessage:(MessageData *)message {
|
||||
NSString *identifer = [NSString stringWithFormat:@"%ud-%lld-%ud", message.mesLocalID, message.mesSvrID, message.msgCreateTime];
|
||||
return [RecallCacheManager.sharedInstance.kv containsKey:identifer];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -11,7 +11,7 @@
|
||||
#import <objc/message.h>
|
||||
|
||||
typedef NS_ENUM(NSUInteger, WTRevokedMessageStyle) {
|
||||
WTRevokedMessageStylePlain = 0,
|
||||
WTRevokedMessageStyleClassic = 0,
|
||||
WTRevokedMessageStyleMask
|
||||
};
|
||||
|
||||
|
||||
@@ -135,6 +135,27 @@ static NSString * const WeChatTweakPreferenceRevokeNotificationTypeKey = @"WeCha
|
||||
|
||||
@end
|
||||
|
||||
@interface RevokeMsgItem : NSObject
|
||||
|
||||
@property (nonatomic, assign) unsigned int createTime;
|
||||
@property (nonatomic, retain) NSString *svrId;
|
||||
@property (nonatomic, retain) NSString *content;
|
||||
|
||||
@end
|
||||
|
||||
@interface MMRevokeMsgDB : NSObject
|
||||
|
||||
- (BOOL)insertRevokeMsg:(id)msg;
|
||||
- (id)getRevokeMsg:(NSString *)svrId;
|
||||
|
||||
@end
|
||||
|
||||
@interface MMRevokeMsgService : NSObject
|
||||
|
||||
@property (nonatomic, strong) MMRevokeMsgDB *db;
|
||||
|
||||
@end
|
||||
|
||||
@protocol MASPreferencesViewController <NSObject>
|
||||
|
||||
@property(readonly, nonatomic) NSString *toolbarItemLabel;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#import "TweakPreferencesController.h"
|
||||
#import "AlfredManager.h"
|
||||
#import "WTConfigManager.h"
|
||||
#import "RecallCacheManager.h"
|
||||
|
||||
// Global Function
|
||||
static NSString *(*original_NSHomeDirectory)(void);
|
||||
@@ -53,19 +52,12 @@ static void __attribute__((constructor)) tweak(void) {
|
||||
// Method Swizzling
|
||||
class_addMethod(objc_getClass("AppDelegate"), @selector(applicationDockMenu:), method_getImplementation(class_getInstanceMethod(objc_getClass("AppDelegate"), @selector(tweak_applicationDockMenu:))), "@:@");
|
||||
[objc_getClass("AppDelegate") jr_swizzleMethod:NSSelectorFromString(@"applicationDidFinishLaunching:") withMethod:@selector(tweak_applicationDidFinishLaunching:) error:nil];
|
||||
[objc_getClass("MessageService") jr_swizzleMethod:NSSelectorFromString(@"onRevokeMsg:") withMethod:@selector(tweak_onRevokeMsg:) error:nil];
|
||||
[objc_getClass("MessageService") jr_swizzleMethod:NSSelectorFromString(@"FFToNameFavChatZZ:") withMethod:@selector(tweak_onRevokeMsg:) error:nil];
|
||||
[objc_getClass("MessageService") jr_swizzleMethod:NSSelectorFromString(@"FFToNameFavChatZZ:sessionMsgList:") withMethod:@selector(tweak_onRevokeMsg:sessionMessageList:) error:nil];
|
||||
[objc_getClass("FFProcessReqsvrZZ") jr_swizzleMethod:NSSelectorFromString(@"FFToNameFavChatZZ:sessionMsgList:") withMethod:@selector(tweak_onRevokeMsg:sessionMessageList:) error:nil];
|
||||
[objc_getClass("CUtility") jr_swizzleClassMethod:NSSelectorFromString(@"HasWechatInstance") withClassMethod:@selector(tweak_HasWechatInstance) error:nil];
|
||||
[objc_getClass("CUtility") jr_swizzleClassMethod:NSSelectorFromString(@"FFSvrChatInfoMsgWithImgZZ") withClassMethod:@selector(tweak_HasWechatInstance) error:nil];
|
||||
[objc_getClass("NSRunningApplication") jr_swizzleClassMethod:NSSelectorFromString(@"runningApplicationsWithBundleIdentifier:") withClassMethod:@selector(tweak_runningApplicationsWithBundleIdentifier:) error:nil];
|
||||
[objc_getClass("MASPreferencesWindowController") jr_swizzleMethod:NSSelectorFromString(@"initWithViewControllers:") withMethod:@selector(tweak_initWithViewControllers:) error:nil];
|
||||
|
||||
[objc_getClass("MMMessageCellView") jr_swizzleMethod:NSSelectorFromString(@"contextMenu") withMethod:@selector(tweak_contextMenu) 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];
|
||||
|
||||
objc_property_attribute_t type = { "T", "@\"NSString\"" }; // NSString
|
||||
objc_property_attribute_t atom = { "N", "" }; // nonatomic
|
||||
@@ -78,200 +70,6 @@ static void __attribute__((constructor)) tweak(void) {
|
||||
class_addMethod(objc_getClass("WCContactData"), @selector(modelPropertyWhitelist), method_getImplementation(class_getClassMethod(objc_getClass("WCContactData"), @selector(modelPropertyWhitelist))), "v@:");
|
||||
}
|
||||
|
||||
- (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 = @"[已撤回]";
|
||||
revokeTextField.font = [NSFont systemFontOfSize:10];
|
||||
revokeTextField.textColor = [NSColor lightGrayColor];
|
||||
[view addSubview:revokeTextField];
|
||||
return view;
|
||||
}
|
||||
|
||||
- (void)tweak_populateWithMessage:(MMMessageTableItem *)tableItem {
|
||||
[self tweak_populateWithMessage:tableItem];
|
||||
BOOL style = [RecallCacheManager containsRevokedMessage:tableItem.message] && tableItem.message.messageType != MessageDataTypePrompt;
|
||||
[((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.yellowColor colorWithAlphaComponent:0.3].CGColor : ((MMMessageCellView *)self).layer.backgroundColor;
|
||||
}
|
||||
|
||||
- (void)tweak_layout {
|
||||
[self tweak_layout];
|
||||
__block NSTextField *label = nil;
|
||||
[((MMMessageCellView *)self).subviews enumerateObjectsUsingBlock:^(__kindof NSView * _Nonnull view, NSUInteger index, BOOL * _Nonnull stop) {
|
||||
if (view.tag != 9527) {
|
||||
return ;
|
||||
}
|
||||
*stop = YES;
|
||||
label = view;
|
||||
}];
|
||||
if (label == nil) {
|
||||
return;
|
||||
}
|
||||
label.frame = ({
|
||||
NSView *avatarView = ((MMMessageCellView *)self).avatarImgView;
|
||||
CGFloat x = CGRectGetMidX(avatarView.frame) - CGRectGetWidth(label.frame) / 2.0;
|
||||
CGFloat y = CGRectGetMinY(avatarView.frame) - CGRectGetHeight(label.frame);
|
||||
NSRect fuck = [label.stringValue boundingRectWithSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX) options:kNilOptions attributes:nil];
|
||||
NSRect frame = NSMakeRect(x, y, CGRectGetWidth(fuck), CGRectGetHeight(fuck));
|
||||
frame;
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - No Revoke Message
|
||||
|
||||
- (void)tweak_onRevokeMsg:(MessageData *)message {
|
||||
[self tweak_onRevokeMsg:message sessionMessageList:nil];
|
||||
}
|
||||
|
||||
- (void)tweak_onRevokeMsg:(MessageData *)message sessionMessageList:(nullable id)sessionMessageList {
|
||||
// - (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_onRevokeMsg:message sessionMessageList:sessionMessageList];
|
||||
}
|
||||
// Decode message
|
||||
NSString *session = [message.msgContent tweak_subStringFrom:@"<session>" to:@"</session>"];
|
||||
NSUInteger newMessageID = [message.msgContent tweak_subStringFrom:@"<newmsgid>" to:@"</newmsgid>"].longLongValue;
|
||||
NSString *replaceMessage = [message.msgContent tweak_subStringFrom:@"<replacemsg><![CDATA[" to:@"]]></replacemsg>"];
|
||||
// Get message data
|
||||
MessageData *messageData = ((id (*)(id, SEL, id, unsigned long long))objc_msgSend)(self, GetMsgDataSelector, session, newMessageID);
|
||||
if (messageData.isSendFromSelf) {
|
||||
// Fallback to origin method
|
||||
[self tweak_onRevokeMsg:message sessionMessageList:sessionMessageList];
|
||||
} else {
|
||||
switch (WTConfigManager.sharedInstance.revokedMessageStyle) {
|
||||
case WTRevokedMessageStylePlain:
|
||||
[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 {
|
||||
[RecallCacheManager insertRevokedMessage:messageData];
|
||||
// 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];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - AppUrlMessageMenu
|
||||
|
||||
- (id)tweak_contextMenu {
|
||||
|
||||
Reference in New Issue
Block a user