From 3f3ec79543ac5d63d38febaf65b783399de74a63 Mon Sep 17 00:00:00 2001 From: Sunnyyoung Date: Tue, 1 Feb 2022 19:24:04 +0800 Subject: [PATCH] Refactor into standalone modules --- WeChatTweak.xcodeproj/project.pbxproj | 56 ++-- .../{Manager/AlfredManager.h => Alfred.h} | 5 +- .../{Manager/AlfredManager.m => Alfred.m} | 10 +- WeChatTweak/AntiRevoke.h | 20 -- WeChatTweak/AntiRevoke.m | 6 +- WeChatTweak/ContactData.m | 48 +++ WeChatTweak/ContextMenu.m | 156 ++++++++++ .../Controller/TweakPreferencesController.h | 2 +- .../Controller/TweakPreferencesController.m | 10 +- WeChatTweak/Directory.m | 41 +++ WeChatTweak/Manager/WTConfigManager.h | 25 -- WeChatTweak/Manager/WTConfigManager.m | 57 ---- WeChatTweak/MultipleInstances.m | 50 ++++ WeChatTweak/PreferencesWindow.m | 31 ++ .../Supporting Files/WeChatTweakHeaders.h | 4 +- WeChatTweak/WeChatTweak.h | 18 +- WeChatTweak/WeChatTweak.m | 279 +----------------- 17 files changed, 399 insertions(+), 419 deletions(-) rename WeChatTweak/{Manager/AlfredManager.h => Alfred.h} (79%) rename WeChatTweak/{Manager/AlfredManager.m => Alfred.m} (97%) delete mode 100644 WeChatTweak/AntiRevoke.h create mode 100644 WeChatTweak/ContactData.m create mode 100644 WeChatTweak/ContextMenu.m create mode 100644 WeChatTweak/Directory.m delete mode 100644 WeChatTweak/Manager/WTConfigManager.h delete mode 100644 WeChatTweak/Manager/WTConfigManager.m create mode 100644 WeChatTweak/MultipleInstances.m create mode 100644 WeChatTweak/PreferencesWindow.m diff --git a/WeChatTweak.xcodeproj/project.pbxproj b/WeChatTweak.xcodeproj/project.pbxproj index c792559..b49cbf8 100644 --- a/WeChatTweak.xcodeproj/project.pbxproj +++ b/WeChatTweak.xcodeproj/project.pbxproj @@ -7,18 +7,20 @@ objects = { /* Begin PBXBuildFile section */ - 7D14E5A41F6447DB00D75132 /* AlfredManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 7D14E5A21F6447DB00D75132 /* AlfredManager.h */; }; - 7D14E5A51F6447DB00D75132 /* AlfredManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D14E5A31F6447DB00D75132 /* AlfredManager.m */; }; - 7D2194CB264701950068F4CC /* AntiRevoke.h in Headers */ = {isa = PBXBuildFile; fileRef = 7D2194C9264701950068F4CC /* AntiRevoke.h */; }; + 7D14E5A41F6447DB00D75132 /* Alfred.h in Headers */ = {isa = PBXBuildFile; fileRef = 7D14E5A21F6447DB00D75132 /* Alfred.h */; }; + 7D14E5A51F6447DB00D75132 /* Alfred.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D14E5A31F6447DB00D75132 /* Alfred.m */; }; 7D2194CC264701950068F4CC /* AntiRevoke.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D2194CA264701950068F4CC /* AntiRevoke.m */; }; 7D54A05C20E74D9400CB5306 /* TweakPreferencesController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D54A05E20E74D9400CB5306 /* TweakPreferencesController.xib */; }; 7D54A06A20E74FE500CB5306 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D54A06C20E74FE500CB5306 /* Localizable.strings */; }; + 7D64150827A9469900A8A398 /* ContextMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D64150627A9469900A8A398 /* ContextMenu.m */; }; + 7D64150A27A9481C00A8A398 /* ContactData.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D64150927A9481C00A8A398 /* ContactData.m */; }; + 7D64150C27A94B9600A8A398 /* Directory.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D64150B27A94B9600A8A398 /* Directory.m */; }; + 7D64150E27A94BEA00A8A398 /* MultipleInstances.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D64150D27A94BEA00A8A398 /* MultipleInstances.m */; }; + 7D64151027A94DE200A8A398 /* PreferencesWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D64150F27A94DE200A8A398 /* PreferencesWindow.m */; }; 7D9049F51F82A41A004E6370 /* fishhook.c in Sources */ = {isa = PBXBuildFile; fileRef = 7D9049F31F82A415004E6370 /* fishhook.c */; }; 7D9049F61F82A41A004E6370 /* fishhook.h in Headers */ = {isa = PBXBuildFile; fileRef = 7D9049F41F82A415004E6370 /* fishhook.h */; }; 7D9049F91F82B6FB004E6370 /* NSString+WeChatTweak.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D9049F81F82B6FB004E6370 /* NSString+WeChatTweak.m */; }; 7D9049FA1F82B708004E6370 /* NSString+WeChatTweak.h in Headers */ = {isa = PBXBuildFile; fileRef = 7D9049F71F82B6FB004E6370 /* NSString+WeChatTweak.h */; }; - 7DB8AFBE206211D900E683AE /* WTConfigManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 7DB8AFBC206211D900E683AE /* WTConfigManager.h */; }; - 7DB8AFBF206211D900E683AE /* WTConfigManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DB8AFBD206211D900E683AE /* WTConfigManager.m */; }; 7DF8422C1F40583F00D42D79 /* WeChatTweak.h in Headers */ = {isa = PBXBuildFile; fileRef = 7DF8422A1F40583F00D42D79 /* WeChatTweak.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7DF842341F4058AB00D42D79 /* WeChatTweak.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DF842331F4058AB00D42D79 /* WeChatTweak.m */; }; 7DF842521F4058C600D42D79 /* TweakPreferencesController.h in Headers */ = {isa = PBXBuildFile; fileRef = 7DF8424F1F4058C600D42D79 /* TweakPreferencesController.h */; }; @@ -32,14 +34,18 @@ /* Begin PBXFileReference section */ 153504EC5C9196C0D85213CF /* libPods-WeChatTweak.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-WeChatTweak.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 7D14E5A21F6447DB00D75132 /* AlfredManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AlfredManager.h; sourceTree = ""; }; - 7D14E5A31F6447DB00D75132 /* AlfredManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AlfredManager.m; sourceTree = ""; }; - 7D2194C9264701950068F4CC /* AntiRevoke.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AntiRevoke.h; sourceTree = ""; }; + 7D14E5A21F6447DB00D75132 /* Alfred.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Alfred.h; sourceTree = ""; }; + 7D14E5A31F6447DB00D75132 /* Alfred.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Alfred.m; sourceTree = ""; }; 7D2194CA264701950068F4CC /* AntiRevoke.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AntiRevoke.m; sourceTree = ""; }; 7D54A05F20E74E4600CB5306 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/TweakPreferencesController.xib; sourceTree = ""; }; 7D54A07020E74FFD00CB5306 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 7D54A07120E7535F00CB5306 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; 7D54A07220E7536300CB5306 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; + 7D64150627A9469900A8A398 /* ContextMenu.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContextMenu.m; sourceTree = ""; }; + 7D64150927A9481C00A8A398 /* ContactData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContactData.m; sourceTree = ""; }; + 7D64150B27A94B9600A8A398 /* Directory.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Directory.m; sourceTree = ""; }; + 7D64150D27A94BEA00A8A398 /* MultipleInstances.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MultipleInstances.m; sourceTree = ""; }; + 7D64150F27A94DE200A8A398 /* PreferencesWindow.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PreferencesWindow.m; sourceTree = ""; }; 7D64B96727853C2100A07164 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/TweakPreferencesController.strings; sourceTree = ""; }; 7D64B96827853C2800A07164 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/TweakPreferencesController.strings"; sourceTree = ""; }; 7D64B96927853C2B00A07164 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/TweakPreferencesController.strings"; sourceTree = ""; }; @@ -47,8 +53,6 @@ 7D9049F41F82A415004E6370 /* fishhook.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fishhook.h; sourceTree = ""; }; 7D9049F71F82B6FB004E6370 /* NSString+WeChatTweak.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSString+WeChatTweak.h"; sourceTree = ""; }; 7D9049F81F82B6FB004E6370 /* NSString+WeChatTweak.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSString+WeChatTweak.m"; sourceTree = ""; }; - 7DB8AFBC206211D900E683AE /* WTConfigManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WTConfigManager.h; sourceTree = ""; }; - 7DB8AFBD206211D900E683AE /* WTConfigManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WTConfigManager.m; sourceTree = ""; }; 7DF842271F40583F00D42D79 /* WeChatTweak.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WeChatTweak.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7DF8422A1F40583F00D42D79 /* WeChatTweak.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WeChatTweak.h; sourceTree = ""; }; 7DF8422B1F40583F00D42D79 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -83,17 +87,6 @@ name = Frameworks; sourceTree = ""; }; - 7D5AAF3320DF4B7B00860EEE /* Manager */ = { - isa = PBXGroup; - children = ( - 7D14E5A21F6447DB00D75132 /* AlfredManager.h */, - 7D14E5A31F6447DB00D75132 /* AlfredManager.m */, - 7DB8AFBC206211D900E683AE /* WTConfigManager.h */, - 7DB8AFBD206211D900E683AE /* WTConfigManager.m */, - ); - path = Manager; - sourceTree = ""; - }; 7D5AAF3520DF4B9700860EEE /* Controller */ = { isa = PBXGroup; children = ( @@ -157,12 +150,17 @@ children = ( 7DF8422A1F40583F00D42D79 /* WeChatTweak.h */, 7DF842331F4058AB00D42D79 /* WeChatTweak.m */, - 7D2194C9264701950068F4CC /* AntiRevoke.h */, 7D2194CA264701950068F4CC /* AntiRevoke.m */, + 7D64150B27A94B9600A8A398 /* Directory.m */, + 7D64150D27A94BEA00A8A398 /* MultipleInstances.m */, + 7D64150F27A94DE200A8A398 /* PreferencesWindow.m */, + 7D64150627A9469900A8A398 /* ContextMenu.m */, + 7D64150927A9481C00A8A398 /* ContactData.m */, + 7D14E5A21F6447DB00D75132 /* Alfred.h */, + 7D14E5A31F6447DB00D75132 /* Alfred.m */, 7D5AAF3720DF4BB300860EEE /* Vendor */, 7D5AAF3620DF4BA400860EEE /* Category */, 7D5AAF3520DF4B9700860EEE /* Controller */, - 7D5AAF3320DF4B7B00860EEE /* Manager */, 7DF842631F40594400D42D79 /* Resources */, 7D5AAF3820DF4BC400860EEE /* Supporting Files */, ); @@ -196,10 +194,8 @@ 7D9049FA1F82B708004E6370 /* NSString+WeChatTweak.h in Headers */, 7DF8422C1F40583F00D42D79 /* WeChatTweak.h in Headers */, 7DF8425B1F4058DD00D42D79 /* NSBundle+WeChatTweak.h in Headers */, - 7DB8AFBE206211D900E683AE /* WTConfigManager.h in Headers */, 7D9049F61F82A41A004E6370 /* fishhook.h in Headers */, - 7D14E5A41F6447DB00D75132 /* AlfredManager.h in Headers */, - 7D2194CB264701950068F4CC /* AntiRevoke.h in Headers */, + 7D14E5A41F6447DB00D75132 /* Alfred.h in Headers */, 7DF842601F40590500D42D79 /* WeChatTweakHeaders.h in Headers */, 7DF842521F4058C600D42D79 /* TweakPreferencesController.h in Headers */, ); @@ -320,14 +316,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7D14E5A51F6447DB00D75132 /* AlfredManager.m in Sources */, + 7D14E5A51F6447DB00D75132 /* Alfred.m in Sources */, 7DF842531F4058C600D42D79 /* TweakPreferencesController.m in Sources */, 7D9049F91F82B6FB004E6370 /* NSString+WeChatTweak.m in Sources */, + 7D64150827A9469900A8A398 /* ContextMenu.m in Sources */, 7D2194CC264701950068F4CC /* AntiRevoke.m in Sources */, 7DF842341F4058AB00D42D79 /* WeChatTweak.m in Sources */, - 7DB8AFBF206211D900E683AE /* WTConfigManager.m in Sources */, + 7D64150A27A9481C00A8A398 /* ContactData.m in Sources */, 7D9049F51F82A41A004E6370 /* fishhook.c in Sources */, + 7D64151027A94DE200A8A398 /* PreferencesWindow.m in Sources */, + 7D64150E27A94BEA00A8A398 /* MultipleInstances.m in Sources */, 7DF8425C1F4058DD00D42D79 /* NSBundle+WeChatTweak.m in Sources */, + 7D64150C27A94B9600A8A398 /* Directory.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/WeChatTweak/Manager/AlfredManager.h b/WeChatTweak/Alfred.h similarity index 79% rename from WeChatTweak/Manager/AlfredManager.h rename to WeChatTweak/Alfred.h index e462e50..64a1066 100644 --- a/WeChatTweak/Manager/AlfredManager.h +++ b/WeChatTweak/Alfred.h @@ -1,14 +1,11 @@ // -// AlfredManager.h +// Alfred.h // WeChatTweak // // Created by Sunnyyoung on 2017/9/10. // Copyright © 2017年 Sunnyyoung. All rights reserved. // -#import -#import -#import #import #import #import diff --git a/WeChatTweak/Manager/AlfredManager.m b/WeChatTweak/Alfred.m similarity index 97% rename from WeChatTweak/Manager/AlfredManager.m rename to WeChatTweak/Alfred.m index ef8e905..ba1727f 100644 --- a/WeChatTweak/Manager/AlfredManager.m +++ b/WeChatTweak/Alfred.m @@ -1,13 +1,13 @@ // -// AlfredManager.m +// Alfred.m // WeChatTweak // // Created by Sunnyyoung on 2017/9/10. // Copyright © 2017年 Sunnyyoung. All rights reserved. // -#import "AlfredManager.h" -#import "WeChatTweakHeaders.h" +#import "Alfred.h" +#import "WeChatTweak.h" @interface AlfredManager() @@ -18,6 +18,10 @@ @implementation AlfredManager static int port = 48065; + ++ (void)load { + [AlfredManager.sharedInstance startListener]; +} + (instancetype)sharedInstance { static dispatch_once_t onceToken; diff --git a/WeChatTweak/AntiRevoke.h b/WeChatTweak/AntiRevoke.h deleted file mode 100644 index a524bcc..0000000 --- a/WeChatTweak/AntiRevoke.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// AntiRevoke.h -// WeChatTweak -// -// Created by Sunny Young on 2021/5/9. -// Copyright © 2021 Sunnyyoung. All rights reserved. -// - -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface NSObject (AntiRevoke) - -@end - -NS_ASSUME_NONNULL_END diff --git a/WeChatTweak/AntiRevoke.m b/WeChatTweak/AntiRevoke.m index f3e3441..e8f88ec 100644 --- a/WeChatTweak/AntiRevoke.m +++ b/WeChatTweak/AntiRevoke.m @@ -6,9 +6,7 @@ // Copyright © 2021 Sunnyyoung. All rights reserved. // -#import "AntiRevoke.h" -#import "WeChatTweakHeaders.h" -#import "WTConfigManager.h" +#import "WeChatTweak.h" #import "NSString+WeChatTweak.h" #import "NSBundle+WeChatTweak.h" @@ -39,7 +37,7 @@ static void __attribute__((constructor)) tweak(void) { // Fallback to origin method [self tweak_FFToNameFavChatZZ:message sessionMsgList:sessionMsgList]; } else { - switch (WTConfigManager.sharedInstance.revokedMessageStyle) { + switch (WeChatTweak.revokedMessageStyle) { case WTRevokedMessageStyleClassic: [self handleRevokedMessageIntoClassicStyleWithSession:session messageData:messageData replaceMessage:replaceMessage]; break; diff --git a/WeChatTweak/ContactData.m b/WeChatTweak/ContactData.m new file mode 100644 index 0000000..716648f --- /dev/null +++ b/WeChatTweak/ContactData.m @@ -0,0 +1,48 @@ +// +// ContactData.m +// WeChatTweak +// +// Created by Sunny Young on 2022/2/1. +// Copyright © 2022 Sunnyyoung. All rights reserved. +// + +#import "WeChatTweak.h" + +@implementation NSObject (ContactData) + +static void __attribute__((constructor)) tweak(void) { + objc_property_attribute_t type = { "T", "@\"NSString\"" }; // NSString + objc_property_attribute_t atom = { "N", "" }; // nonatomic + objc_property_attribute_t ownership = { "&", "" }; // C = copy & = strong + objc_property_attribute_t backingivar = { "V", "_m_nsHeadImgUrl" }; // ivar name + objc_property_attribute_t attrs[] = { type, atom, ownership, backingivar }; + class_addProperty(objc_getClass("WCContactData"), "wt_avatarPath", attrs, 4); + class_addMethod(objc_getClass("WCContactData"), @selector(wt_avatarPath), method_getImplementation(class_getInstanceMethod(objc_getClass("WCContactData"), @selector(wt_avatarPath))), "@@:"); + class_addMethod(objc_getClass("WCContactData"), @selector(setWt_avatarPath:), method_getImplementation(class_getInstanceMethod(objc_getClass("WCContactData"), @selector(setWt_avatarPath:))), "v@:@"); + class_addMethod(objc_getClass("WCContactData"), @selector(modelPropertyWhitelist), method_getImplementation(class_getClassMethod(objc_getClass("WCContactData"), @selector(modelPropertyWhitelist))), "v@:"); +} + +- (NSString *)wt_avatarPath { + if (![objc_getClass("PathUtility") respondsToSelector:@selector(GetCurUserDocumentPath)]) { + return @""; + } + NSString *pathString = [NSString stringWithFormat:@"%@/Avatar/%@.jpg", [objc_getClass("PathUtility") GetCurUserDocumentPath], [((WCContactData *)self).m_nsUsrName md5String]]; + return [NSFileManager.defaultManager fileExistsAtPath:pathString] ? pathString : @""; +} + +- (void)setWt_avatarPath:(NSString *)avatarPath { + // For readonly + return; +} + ++ (NSArray *)modelPropertyWhitelist { + NSArray *list =@[ + @"wt_avatarPath", + @"m_nsRemark", + @"m_nsNickName", + @"m_nsUsrName" + ]; + return WeChatTweak.compressedJSONEnabled ? list : nil; +} + +@end diff --git a/WeChatTweak/ContextMenu.m b/WeChatTweak/ContextMenu.m new file mode 100644 index 0000000..dda7ddb --- /dev/null +++ b/WeChatTweak/ContextMenu.m @@ -0,0 +1,156 @@ +// +// ContextMenu.m +// WeChatTweak +// +// Created by Sunny Young on 2022/2/1. +// Copyright © 2022 Sunnyyoung. All rights reserved. +// + +#import "WeChatTweak.h" +#import "NSBundle+WeChatTweak.h" + +#import + +@implementation NSObject (ContextMenu) + +static void __attribute__((constructor)) tweak(void) { + [objc_getClass("MMMessageCellView") jr_swizzleMethod:NSSelectorFromString(@"contextMenu") withMethod:@selector(tweak_contextMenu) error:nil]; +} + +- (id)tweak_contextMenu { + NSMenu *menu = (NSMenu *)[self tweak_contextMenu]; + if ([self isKindOfClass:objc_getClass("MMMessageCellView")]) { + switch (((MMMessageCellView *)self).messageTableItem.message.messageType) { + case MessageDataTypeAppUrl: { + ReaderWrap *wrap = ({ + ReaderWrap *wrap = nil; + if ([self isKindOfClass:objc_getClass("MMAppSingleReaderMessageCellView")]) { + MMAppSingleReaderMessageCellView *cell = (MMAppSingleReaderMessageCellView *)self; + wrap = cell.readerData; + } else if ([self isKindOfClass:objc_getClass("MMAppMultipleReaderMessageCellView")]) { + MMAppMultipleReaderMessageCellView *cell = (MMAppMultipleReaderMessageCellView *)self; + wrap = (cell.selectedReaderWrapIndex < cell.readerMessages.count) ? cell.readerMessages[cell.selectedReaderWrapIndex] : nil; + } else { + wrap = nil; + } + wrap; + }); + if (wrap) { + [menu addItem:NSMenuItem.separatorItem]; + [menu addItem:({ + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[NSBundle.tweakBundle localizedStringForKey:@"Tweak.MessageMenuItem.CopyLink"] + action:@selector(tweakCopyLink:) + keyEquivalent:@"c"]; + item.target = wrap; + item; + })]; + [menu addItem:({ + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[NSBundle.tweakBundle localizedStringForKey:@"Tweak.MessageMenuItem.OpenInBrowser"] + action:@selector(tweakOpenLink:) + keyEquivalent:@"o"]; + item.target = wrap; + item; + })]; + } + break; + } + case MessageDataTypeImage: { + [menu addItem:NSMenuItem.separatorItem]; + [menu addItem:({ + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[NSBundle.tweakBundle localizedStringForKey:@"Tweak.MessageMenuItem.IdentifyQRCode"] + action:@selector(tweakIdentifyQRCode:) + keyEquivalent:@"i"]; + item.target = self; + item; + })]; + break; + } + case MessageDataTypeSticker: { + [menu addItem:NSMenuItem.separatorItem]; + [menu addItem:({ + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[NSBundle.tweakBundle localizedStringForKey:@"Tweak.MessageMenuItem.ExportSticker"] + action:@selector(tweakExportSticker:) + keyEquivalent:@"e"]; + item.target = self; + item; + })]; + break; + } + default: { + break; + } + } + } + return menu; +} + +- (void)tweakCopyLink:(NSMenuItem *)sender { + ReaderWrap *wrap = sender.target; + if (!wrap) { + return; + } + if (wrap.m_nsUrl) { + [NSPasteboard.generalPasteboard clearContents]; + [NSPasteboard.generalPasteboard setString:wrap.m_nsUrl forType:NSStringPboardType]; + } +} + +- (void)tweakOpenLink:(NSMenuItem *)sender { + ReaderWrap *wrap = sender.target; + if (!wrap) { + return; + } + if (wrap.m_nsUrl && [NSURL URLWithString:wrap.m_nsUrl]) { + [NSWorkspace.sharedWorkspace openURL:[NSURL URLWithString:wrap.m_nsUrl]]; + } +} + +- (void)tweakIdentifyQRCode:(NSMenuItem *)sender { + NSImage *image = ((MMImageMessageCellView *)self).displayedImage; + if (image) { + NSData *imageData = [image TIFFRepresentation]; + CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}]; + NSArray *results = [detector featuresInImage:[CIImage imageWithData:imageData]]; + if (results.count) { + CIQRCodeFeature *result = results.firstObject; + NSString *content = result.messageString; + if (content.length) { + NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard clearContents]; + [pasteboard setString:content forType:NSStringPboardType]; + [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:({ + NSUserNotification *notification = [[NSUserNotification alloc] init]; + notification.informativeText = [NSBundle.tweakBundle localizedStringForKey:@"Tweak.MessageMenuItem.IdentifyQRCodeNotification"]; + notification; + })]; + } + } + } +} + +- (void)tweakExportSticker:(NSMenuItem *)sender { + MMMessageCellView *cell = (MMMessageCellView *)sender.target; + MessageData *messageData = cell.messageTableItem.message; + NSString *localID = [messageData savingImageFileNameWithLocalID]; + NSString *md5 = [NSDictionary dictionaryWithXMLString:[messageData.msgContent componentsSeparatedByString:@"\n"].lastObject][@"emoji"][@"_md5"]; + if (!localID.length || !md5.length) { + return; + } + NSSavePanel *panel = [NSSavePanel savePanel]; + [panel setNameFieldStringValue:localID]; + [panel setAllowsOtherFileTypes:YES]; + [panel setAllowedFileTypes:@[@"gif"]]; + [panel setExtensionHidden:NO]; + [panel setCanCreateDirectories:YES]; + [panel beginSheetModalForWindow:cell.window completionHandler:^(NSModalResponse result) { + if (result == NSModalResponseOK) { + NSString *path = panel.URL.path; + MMServiceCenter *serviceCenter = [objc_getClass("MMServiceCenter") defaultCenter]; + EmoticonMgr *emoticonMgr = [serviceCenter getService:objc_getClass("EmoticonMgr")]; + NSData *stickerData = [emoticonMgr getEmotionDataWithMD5:md5]; + [stickerData writeToFile:path atomically:YES]; + } + }]; +} + +@end diff --git a/WeChatTweak/Controller/TweakPreferencesController.h b/WeChatTweak/Controller/TweakPreferencesController.h index 7f5f35a..d0c1b58 100644 --- a/WeChatTweak/Controller/TweakPreferencesController.h +++ b/WeChatTweak/Controller/TweakPreferencesController.h @@ -6,7 +6,7 @@ // Copyright © 2017年 Sunnyyoung. All rights reserved. // -#import "WeChatTweakHeaders.h" +#import "WeChatTweak.h" @interface TweakPreferencesController : NSViewController diff --git a/WeChatTweak/Controller/TweakPreferencesController.m b/WeChatTweak/Controller/TweakPreferencesController.m index 9441c8f..88e8c83 100644 --- a/WeChatTweak/Controller/TweakPreferencesController.m +++ b/WeChatTweak/Controller/TweakPreferencesController.m @@ -8,7 +8,6 @@ #import "TweakPreferencesController.h" #import "NSBundle+WeChatTweak.h" -#import "WTConfigManager.h" @interface TweakPreferencesController () @@ -30,12 +29,11 @@ } - (void)reloadData { - WTConfigManager *configManager = WTConfigManager.sharedInstance; NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; RevokeNotificationType notificationType = [userDefaults integerForKey:WeChatTweakPreferenceRevokeNotificationTypeKey]; [self.notificationTypeButton selectItemAtIndex:notificationType]; - [self.compressedJSONEnabledButton selectItemAtIndex:configManager.compressedJSONEnabled ? 0 : 1]; - [self.revokedMessageStyleButton selectItemAtIndex:configManager.revokedMessageStyle]; + [self.compressedJSONEnabledButton selectItemAtIndex:WeChatTweak.compressedJSONEnabled ? 0 : 1]; + [self.revokedMessageStyleButton selectItemAtIndex:WeChatTweak.revokedMessageStyle]; } #pragma mark - Event method @@ -47,11 +45,11 @@ - (IBAction)switchCompressedJSONEnabledAction:(NSPopUpButton *)sender { BOOL enabled = sender.indexOfSelectedItem == 0; - WTConfigManager.sharedInstance.compressedJSONEnabled = enabled; + WeChatTweak.compressedJSONEnabled = enabled; } - (IBAction)switchRevokedMessageStyleButton:(NSPopUpButton *)sender { - WTConfigManager.sharedInstance.revokedMessageStyle = sender.indexOfSelectedItem; + WeChatTweak.revokedMessageStyle = sender.indexOfSelectedItem; } #pragma mark - MASPreferencesViewController diff --git a/WeChatTweak/Directory.m b/WeChatTweak/Directory.m new file mode 100644 index 0000000..7cbad49 --- /dev/null +++ b/WeChatTweak/Directory.m @@ -0,0 +1,41 @@ +// +// Directory.m +// WeChatTweak +// +// Created by Sunny Young on 2022/2/1. +// Copyright © 2022 Sunnyyoung. All rights reserved. +// + +#import "WeChatTweak.h" +#import "fishhook.h" + +static NSString *(*original_NSHomeDirectory)(void); +static NSArray *(*original_NSSearchPathForDirectoriesInDomains)(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde); +NSString *tweak_NSHomeDirectory(void) { + return [original_NSHomeDirectory() stringByAppendingPathComponent:@"/Library/Containers/com.tencent.xinWeChat/Data/"]; +} +NSArray *tweak_NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde) { + if (domainMask == NSUserDomainMask) { + NSMutableArray *directories = [original_NSSearchPathForDirectoriesInDomains(directory, domainMask, expandTilde) mutableCopy]; + [directories enumerateObjectsUsingBlock:^(NSString * _Nonnull object, NSUInteger index, BOOL * _Nonnull stop) { + switch (directory) { + case NSDocumentDirectory: directories[index] = [tweak_NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]; break; + case NSLibraryDirectory: directories[index] = [tweak_NSHomeDirectory() stringByAppendingPathComponent:@"Library"]; break; + case NSApplicationSupportDirectory: directories[index] = [tweak_NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support"]; break; + case NSCachesDirectory: directories[index] = [tweak_NSHomeDirectory() stringByAppendingPathComponent:@"Library/Caches"]; break; + default: break; + } + }]; + return directories; + } else { + return original_NSSearchPathForDirectoriesInDomains(directory, domainMask, expandTilde); + } +} + +static void __attribute__((constructor)) tweak(void) { + // Global Function Hook + rebind_symbols((struct rebinding[2]) { + { "NSHomeDirectory", tweak_NSHomeDirectory, (void *)&original_NSHomeDirectory }, + { "NSSearchPathForDirectoriesInDomains", tweak_NSSearchPathForDirectoriesInDomains, (void *)&original_NSSearchPathForDirectoriesInDomains } + }, 2); +} diff --git a/WeChatTweak/Manager/WTConfigManager.h b/WeChatTweak/Manager/WTConfigManager.h deleted file mode 100644 index 31530f6..0000000 --- a/WeChatTweak/Manager/WTConfigManager.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// WTConfigManager.h -// WeChatTweak -// -// Created by Sunny Young on 21/03/2018. -// Copyright © 2018 Sunnyyoung. All rights reserved. -// - -#import -#import -#import - -typedef NS_ENUM(NSUInteger, WTRevokedMessageStyle) { - WTRevokedMessageStyleClassic = 0, - WTRevokedMessageStyleMask -}; - -@interface WTConfigManager : NSObject - -@property (nonatomic, assign) BOOL compressedJSONEnabled; -@property (nonatomic, assign) WTRevokedMessageStyle revokedMessageStyle; - -+ (instancetype)sharedInstance; - -@end diff --git a/WeChatTweak/Manager/WTConfigManager.m b/WeChatTweak/Manager/WTConfigManager.m deleted file mode 100644 index 9b928a7..0000000 --- a/WeChatTweak/Manager/WTConfigManager.m +++ /dev/null @@ -1,57 +0,0 @@ -// -// WTConfigManager.m -// WeChatTweak -// -// Created by Sunny Young on 21/03/2018. -// Copyright © 2018 Sunnyyoung. All rights reserved. -// - -#import "WTConfigManager.h" - -static NSString * const WeChatTweakCompressedJSONEnabledKey = @"WeChatTweakCompressedJSONEnabledKey"; -static NSString * const WeChatTweakRevokedMessageStyleKey = @"WeChatTweakRevokedMessageStyleKey"; - -@interface WTConfigManager() - -@end - -@implementation WTConfigManager - -+ (instancetype)sharedInstance { - static dispatch_once_t onceToken; - static WTConfigManager *shared; - dispatch_once(&onceToken, ^{ - shared = [[WTConfigManager alloc] init]; - }); - return shared; -} - -- (instancetype)init { - self = [super init]; - if (self) { - NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults; - if ([userDefaults objectForKey:WeChatTweakCompressedJSONEnabledKey]) { - _compressedJSONEnabled = [userDefaults boolForKey:WeChatTweakCompressedJSONEnabledKey]; - } else { - _compressedJSONEnabled = YES; - } - _revokedMessageStyle = [userDefaults integerForKey:WeChatTweakRevokedMessageStyleKey]; - } - return self; -} - -#pragma mark - Property method - -- (void)setCompressedJSONEnabled:(BOOL)compressedJSONEnabled { - _compressedJSONEnabled = compressedJSONEnabled; - [NSUserDefaults.standardUserDefaults setBool:compressedJSONEnabled forKey:WeChatTweakCompressedJSONEnabledKey]; - [NSUserDefaults.standardUserDefaults synchronize]; -} - -- (void)setRevokedMessageStyle:(WTRevokedMessageStyle)revokedMessageStyle { - _revokedMessageStyle = revokedMessageStyle; - [NSUserDefaults.standardUserDefaults setInteger:revokedMessageStyle forKey:WeChatTweakRevokedMessageStyleKey]; - [NSUserDefaults.standardUserDefaults synchronize]; -} - -@end diff --git a/WeChatTweak/MultipleInstances.m b/WeChatTweak/MultipleInstances.m new file mode 100644 index 0000000..9116a71 --- /dev/null +++ b/WeChatTweak/MultipleInstances.m @@ -0,0 +1,50 @@ +// +// MultipleInstances.m +// WeChatTweak +// +// Created by Sunny Young on 2022/2/1. +// Copyright © 2022 Sunnyyoung. All rights reserved. +// + +#import "WeChatTweak.h" +#import "NSBundle+WeChatTweak.h" + +@implementation NSObject (MultipleInstances) + +static void __attribute__((constructor)) tweak(void) { + [objc_getClass("CUtility") jr_swizzleClassMethod:NSSelectorFromString(@"HasWechatInstance") withClassMethod:@selector(tweak_HasWechatInstance) error:nil]; + [objc_getClass("NSRunningApplication") jr_swizzleClassMethod:NSSelectorFromString(@"runningApplicationsWithBundleIdentifier:") withClassMethod:@selector(tweak_runningApplicationsWithBundleIdentifier:) error:nil]; + class_addMethod(objc_getClass("AppDelegate"), @selector(applicationDockMenu:), method_getImplementation(class_getInstanceMethod(objc_getClass("AppDelegate"), @selector(tweak_applicationDockMenu:))), "@:@"); +} + ++ (BOOL)tweak_HasWechatInstance { + return NO; +} + ++ (NSArray *)tweak_runningApplicationsWithBundleIdentifier:(NSString *)bundleIdentifier { + if ([bundleIdentifier isEqualToString:NSBundle.mainBundle.bundleIdentifier] ) { + return @[NSRunningApplication.currentApplication]; + } else { + return [self tweak_runningApplicationsWithBundleIdentifier:bundleIdentifier]; + } +} + +- (NSMenu *)tweak_applicationDockMenu:(NSApplication *)sender { + NSMenu *menu = [[NSMenu alloc] init]; + NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:[NSBundle.tweakBundle localizedStringForKey:@"Tweak.Title.LoginAnotherAccount"] + action:@selector(openNewWeChatInstace:) + keyEquivalent:@""]; + [menu insertItem:menuItem atIndex:0]; + return menu; +} + +- (void)openNewWeChatInstace:(id)sender { + NSString *applicationPath = NSBundle.mainBundle.bundlePath; + NSTask *task = [[NSTask alloc] init]; + task.launchPath = @"/usr/bin/open"; + task.arguments = @[@"-n", applicationPath]; + [task launch]; + [task waitUntilExit]; +} + +@end diff --git a/WeChatTweak/PreferencesWindow.m b/WeChatTweak/PreferencesWindow.m new file mode 100644 index 0000000..c8f4e69 --- /dev/null +++ b/WeChatTweak/PreferencesWindow.m @@ -0,0 +1,31 @@ +// +// PreferencesWindow.m +// WeChatTweak +// +// Created by Sunny Young on 2022/2/1. +// Copyright © 2022 Sunnyyoung. All rights reserved. +// + +#import "WeChatTweak.h" +#import "NSBundle+WeChatTweak.h" +#import "TweakPreferencesController.h" + +@implementation NSObject (PreferencesWindow) + +#pragma mark - Constructor + +static void __attribute__((constructor)) tweak(void) { + [objc_getClass("MASPreferencesWindowController") jr_swizzleMethod:NSSelectorFromString(@"initWithViewControllers:") withMethod:@selector(tweak_initWithViewControllers:) error:nil]; +} + +#pragma mark - Preferences Window + +- (id)tweak_initWithViewControllers:(NSArray *)arg1 { + NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:arg1]; + TweakPreferencesController *controller = [[TweakPreferencesController alloc] initWithNibName:nil bundle:[NSBundle tweakBundle]]; + [viewControllers addObject:controller]; + return [self tweak_initWithViewControllers:viewControllers]; +} + +@end + diff --git a/WeChatTweak/Supporting Files/WeChatTweakHeaders.h b/WeChatTweak/Supporting Files/WeChatTweakHeaders.h index bf79920..8b8458c 100644 --- a/WeChatTweak/Supporting Files/WeChatTweakHeaders.h +++ b/WeChatTweak/Supporting Files/WeChatTweakHeaders.h @@ -6,10 +6,10 @@ // Copyright © 2017年 Sunnyyoung. All rights reserved. // -#import -#import +#import #import #import +#import typedef NS_ENUM(NSUInteger, RevokeNotificationType) { RevokeNotificationTypeFollow = 0, diff --git a/WeChatTweak/WeChatTweak.h b/WeChatTweak/WeChatTweak.h index f16de10..2b0060f 100644 --- a/WeChatTweak/WeChatTweak.h +++ b/WeChatTweak/WeChatTweak.h @@ -6,11 +6,19 @@ // Copyright © 2017年 Sunnyyoung. All rights reserved. // -#import -#import -#import -#import -#import +#import "WeChatTweakHeaders.h" FOUNDATION_EXPORT double WeChatTweakVersionNumber; FOUNDATION_EXPORT const unsigned char WeChatTweakVersionString[]; + +typedef NS_ENUM(NSUInteger, WTRevokedMessageStyle) { + WTRevokedMessageStyleClassic = 0, + WTRevokedMessageStyleMask +}; + +@interface WeChatTweak : NSObject + +@property (nonatomic, assign, class) BOOL compressedJSONEnabled; +@property (nonatomic, assign, class) WTRevokedMessageStyle revokedMessageStyle; + +@end diff --git a/WeChatTweak/WeChatTweak.m b/WeChatTweak/WeChatTweak.m index 4907cd2..1c31b04 100755 --- a/WeChatTweak/WeChatTweak.m +++ b/WeChatTweak/WeChatTweak.m @@ -7,277 +7,28 @@ // #import "WeChatTweak.h" -#import "WeChatTweakHeaders.h" -#import "fishhook.h" -#import "NSBundle+WeChatTweak.h" -#import "NSString+WeChatTweak.h" -#import "TweakPreferencesController.h" -#import "AlfredManager.h" -#import "WTConfigManager.h" -// Global Function -static NSString *(*original_NSHomeDirectory)(void); -static NSArray *(*original_NSSearchPathForDirectoriesInDomains)(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde); -NSString *tweak_NSHomeDirectory(void) { - return [original_NSHomeDirectory() stringByAppendingPathComponent:@"/Library/Containers/com.tencent.xinWeChat/Data/"]; -} -NSArray *tweak_NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde) { - if (domainMask == NSUserDomainMask) { - NSMutableArray *directories = [original_NSSearchPathForDirectoriesInDomains(directory, domainMask, expandTilde) mutableCopy]; - [directories enumerateObjectsUsingBlock:^(NSString * _Nonnull object, NSUInteger index, BOOL * _Nonnull stop) { - switch (directory) { - case NSDocumentDirectory: directories[index] = [tweak_NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]; break; - case NSLibraryDirectory: directories[index] = [tweak_NSHomeDirectory() stringByAppendingPathComponent:@"Library"]; break; - case NSApplicationSupportDirectory: directories[index] = [tweak_NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support"]; break; - case NSCachesDirectory: directories[index] = [tweak_NSHomeDirectory() stringByAppendingPathComponent:@"Library/Caches"]; break; - default: break; - } - }]; - return directories; - } else { - return original_NSSearchPathForDirectoriesInDomains(directory, domainMask, expandTilde); - } +static NSString * const WeChatTweakCompressedJSONEnabledKey = @"WeChatTweakCompressedJSONEnabledKey"; +static NSString * const WeChatTweakRevokedMessageStyleKey = @"WeChatTweakRevokedMessageStyleKey"; + +@implementation WeChatTweak + ++ (BOOL)compressedJSONEnabled { + return [NSUserDefaults.standardUserDefaults boolForKey:WeChatTweakCompressedJSONEnabledKey]; } -@implementation NSObject (WeChatTweak) - -#pragma mark - Constructor - -static void __attribute__((constructor)) tweak(void) { - // Global Function Hook - rebind_symbols((struct rebinding[2]) { - { "NSHomeDirectory", tweak_NSHomeDirectory, (void *)&original_NSHomeDirectory }, - { "NSSearchPathForDirectoriesInDomains", tweak_NSSearchPathForDirectoriesInDomains, (void *)&original_NSSearchPathForDirectoriesInDomains } - }, 2); - // 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("CUtility") jr_swizzleClassMethod:NSSelectorFromString(@"HasWechatInstance") 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_property_attribute_t type = { "T", "@\"NSString\"" }; // NSString - objc_property_attribute_t atom = { "N", "" }; // nonatomic - objc_property_attribute_t ownership = { "&", "" }; // C = copy & = strong - objc_property_attribute_t backingivar = { "V", "_m_nsHeadImgUrl" }; // ivar name - objc_property_attribute_t attrs[] = { type, atom, ownership, backingivar }; - class_addProperty(objc_getClass("WCContactData"), "wt_avatarPath", attrs, 4); - class_addMethod(objc_getClass("WCContactData"), @selector(wt_avatarPath), method_getImplementation(class_getInstanceMethod(objc_getClass("WCContactData"), @selector(wt_avatarPath))), "@@:"); - class_addMethod(objc_getClass("WCContactData"), @selector(setWt_avatarPath:), method_getImplementation(class_getInstanceMethod(objc_getClass("WCContactData"), @selector(setWt_avatarPath:))), "v@:@"); - class_addMethod(objc_getClass("WCContactData"), @selector(modelPropertyWhitelist), method_getImplementation(class_getClassMethod(objc_getClass("WCContactData"), @selector(modelPropertyWhitelist))), "v@:"); ++ (void)setCompressedJSONEnabled:(BOOL)compressedJSONEnabled { + [NSUserDefaults.standardUserDefaults setBool:compressedJSONEnabled forKey:WeChatTweakCompressedJSONEnabledKey]; + [NSUserDefaults.standardUserDefaults synchronize]; } -#pragma mark - AppUrlMessageMenu - -- (id)tweak_contextMenu { - NSMenu *menu = (NSMenu *)[self tweak_contextMenu]; - if ([self isKindOfClass:objc_getClass("MMMessageCellView")]) { - switch (((MMMessageCellView *)self).messageTableItem.message.messageType) { - case MessageDataTypeAppUrl: { - ReaderWrap *wrap = ({ - ReaderWrap *wrap = nil; - if ([self isKindOfClass:objc_getClass("MMAppSingleReaderMessageCellView")]) { - MMAppSingleReaderMessageCellView *cell = (MMAppSingleReaderMessageCellView *)self; - wrap = cell.readerData; - } else if ([self isKindOfClass:objc_getClass("MMAppMultipleReaderMessageCellView")]) { - MMAppMultipleReaderMessageCellView *cell = (MMAppMultipleReaderMessageCellView *)self; - wrap = (cell.selectedReaderWrapIndex < cell.readerMessages.count) ? cell.readerMessages[cell.selectedReaderWrapIndex] : nil; - } else { - wrap = nil; - } - wrap; - }); - if (wrap) { - [menu addItem:NSMenuItem.separatorItem]; - [menu addItem:({ - NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[NSBundle.tweakBundle localizedStringForKey:@"Tweak.MessageMenuItem.CopyLink"] - action:@selector(tweakCopyLink:) - keyEquivalent:@"c"]; - item.target = wrap; - item; - })]; - [menu addItem:({ - NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[NSBundle.tweakBundle localizedStringForKey:@"Tweak.MessageMenuItem.OpenInBrowser"] - action:@selector(tweakOpenLink:) - keyEquivalent:@"o"]; - item.target = wrap; - item; - })]; - } - break; - } - case MessageDataTypeImage: { - [menu addItem:NSMenuItem.separatorItem]; - [menu addItem:({ - NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[NSBundle.tweakBundle localizedStringForKey:@"Tweak.MessageMenuItem.IdentifyQRCode"] - action:@selector(tweakIdentifyQRCode:) - keyEquivalent:@"i"]; - item.target = self; - item; - })]; - break; - } - case MessageDataTypeSticker: { - [menu addItem:NSMenuItem.separatorItem]; - [menu addItem:({ - NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[NSBundle.tweakBundle localizedStringForKey:@"Tweak.MessageMenuItem.ExportSticker"] - action:@selector(tweakExportSticker:) - keyEquivalent:@"e"]; - item.target = self; - item; - })]; - break; - } - default: { - break; - } - } - } - return menu; ++ (WTRevokedMessageStyle)revokedMessageStyle { + return [NSUserDefaults.standardUserDefaults integerForKey:WeChatTweakRevokedMessageStyleKey]; } -- (void)tweakCopyLink:(NSMenuItem *)sender { - ReaderWrap *wrap = sender.target; - if (!wrap) { - return; - } - if (wrap.m_nsUrl) { - [NSPasteboard.generalPasteboard clearContents]; - [NSPasteboard.generalPasteboard setString:wrap.m_nsUrl forType:NSStringPboardType]; - } -} - -- (void)tweakOpenLink:(NSMenuItem *)sender { - ReaderWrap *wrap = sender.target; - if (!wrap) { - return; - } - if (wrap.m_nsUrl && [NSURL URLWithString:wrap.m_nsUrl]) { - [NSWorkspace.sharedWorkspace openURL:[NSURL URLWithString:wrap.m_nsUrl]]; - } -} - -- (void)tweakIdentifyQRCode:(NSMenuItem *)sender { - NSImage *image = ((MMImageMessageCellView *)self).displayedImage; - if (image) { - NSData *imageData = [image TIFFRepresentation]; - CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}]; - NSArray *results = [detector featuresInImage:[CIImage imageWithData:imageData]]; - if (results.count) { - CIQRCodeFeature *result = results.firstObject; - NSString *content = result.messageString; - if (content.length) { - NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; - [pasteboard clearContents]; - [pasteboard setString:content forType:NSStringPboardType]; - [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:({ - NSUserNotification *notification = [[NSUserNotification alloc] init]; - notification.informativeText = [NSBundle.tweakBundle localizedStringForKey:@"Tweak.MessageMenuItem.IdentifyQRCodeNotification"]; - notification; - })]; - } - } - } -} - -- (void)tweakExportSticker:(NSMenuItem *)sender { - MMMessageCellView *cell = (MMMessageCellView *)sender.target; - MessageData *messageData = cell.messageTableItem.message; - NSString *localID = [messageData savingImageFileNameWithLocalID]; - NSString *md5 = [NSDictionary dictionaryWithXMLString:[messageData.msgContent componentsSeparatedByString:@"\n"].lastObject][@"emoji"][@"_md5"]; - if (!localID.length || !md5.length) { - return; - } - NSSavePanel *panel = [NSSavePanel savePanel]; - [panel setNameFieldStringValue:localID]; - [panel setAllowsOtherFileTypes:YES]; - [panel setAllowedFileTypes:@[@"gif"]]; - [panel setExtensionHidden:NO]; - [panel setCanCreateDirectories:YES]; - [panel beginSheetModalForWindow:cell.window completionHandler:^(NSModalResponse result) { - if (result == NSModalResponseOK) { - NSString *path = panel.URL.path; - MMServiceCenter *serviceCenter = [objc_getClass("MMServiceCenter") defaultCenter]; - EmoticonMgr *emoticonMgr = [serviceCenter getService:objc_getClass("EmoticonMgr")]; - NSData *stickerData = [emoticonMgr getEmotionDataWithMD5:md5]; - [stickerData writeToFile:path atomically:YES]; - } - }]; -} - -#pragma mark - Mutiple Instance - -+ (BOOL)tweak_HasWechatInstance { - return NO; -} - -+ (NSArray *)tweak_runningApplicationsWithBundleIdentifier:(NSString *)bundleIdentifier { - if ([bundleIdentifier isEqualToString:NSBundle.mainBundle.bundleIdentifier] ) { - return @[NSRunningApplication.currentApplication]; - } else { - return [self tweak_runningApplicationsWithBundleIdentifier:bundleIdentifier]; - } -} - -- (NSMenu *)tweak_applicationDockMenu:(NSApplication *)sender { - NSMenu *menu = [[NSMenu alloc] init]; - NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:[NSBundle.tweakBundle localizedStringForKey:@"Tweak.Title.LoginAnotherAccount"] - action:@selector(openNewWeChatInstace:) - keyEquivalent:@""]; - [menu insertItem:menuItem atIndex:0]; - return menu; -} - -- (void)openNewWeChatInstace:(id)sender { - NSString *applicationPath = NSBundle.mainBundle.bundlePath; - NSTask *task = [[NSTask alloc] init]; - task.launchPath = @"/usr/bin/open"; - task.arguments = @[@"-n", applicationPath]; - [task launch]; - [task waitUntilExit]; -} - -#pragma mark - Alfred - -- (void)tweak_applicationDidFinishLaunching:(NSNotification *)notification { - [AlfredManager.sharedInstance startListener]; - [self tweak_applicationDidFinishLaunching:notification]; -} - -#pragma mark - Preferences Window - -- (id)tweak_initWithViewControllers:(NSArray *)arg1 { - NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:arg1]; - TweakPreferencesController *controller = [[TweakPreferencesController alloc] initWithNibName:nil bundle:[NSBundle tweakBundle]]; - [viewControllers addObject:controller]; - return [self tweak_initWithViewControllers:viewControllers]; -} - -#pragma mark - WCContact Data - -- (NSString *)wt_avatarPath { - if (![objc_getClass("PathUtility") respondsToSelector:@selector(GetCurUserDocumentPath)]) { - return @""; - } - NSString *pathString = [NSString stringWithFormat:@"%@/Avatar/%@.jpg", [objc_getClass("PathUtility") GetCurUserDocumentPath], [((WCContactData *)self).m_nsUsrName md5String]]; - return [NSFileManager.defaultManager fileExistsAtPath:pathString] ? pathString : @""; -} - -- (void)setWt_avatarPath:(NSString *)avatarPath { - // For readonly - return; -} - -+ (NSArray *)modelPropertyWhitelist { - NSArray *list =@[ - @"wt_avatarPath", - @"m_nsRemark", - @"m_nsNickName", - @"m_nsUsrName" - ]; - return WTConfigManager.sharedInstance.compressedJSONEnabled ? list : nil; ++ (void)setRevokedMessageStyle:(WTRevokedMessageStyle)revokedMessageStyle { + [NSUserDefaults.standardUserDefaults setInteger:revokedMessageStyle forKey:WeChatTweakRevokedMessageStyleKey]; + [NSUserDefaults.standardUserDefaults synchronize]; } @end