Refactor into standalone modules

This commit is contained in:
Sunnyyoung 2022-02-01 19:24:04 +08:00
parent 4de5ca9fec
commit 3f3ec79543
17 changed files with 399 additions and 419 deletions

View File

@ -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 = "<group>"; };
7D14E5A31F6447DB00D75132 /* AlfredManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AlfredManager.m; sourceTree = "<group>"; };
7D2194C9264701950068F4CC /* AntiRevoke.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AntiRevoke.h; sourceTree = "<group>"; };
7D14E5A21F6447DB00D75132 /* Alfred.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Alfred.h; sourceTree = "<group>"; };
7D14E5A31F6447DB00D75132 /* Alfred.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Alfred.m; sourceTree = "<group>"; };
7D2194CA264701950068F4CC /* AntiRevoke.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AntiRevoke.m; sourceTree = "<group>"; };
7D54A05F20E74E4600CB5306 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/TweakPreferencesController.xib; sourceTree = "<group>"; };
7D54A07020E74FFD00CB5306 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
7D54A07120E7535F00CB5306 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
7D54A07220E7536300CB5306 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
7D64150627A9469900A8A398 /* ContextMenu.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContextMenu.m; sourceTree = "<group>"; };
7D64150927A9481C00A8A398 /* ContactData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContactData.m; sourceTree = "<group>"; };
7D64150B27A94B9600A8A398 /* Directory.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Directory.m; sourceTree = "<group>"; };
7D64150D27A94BEA00A8A398 /* MultipleInstances.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MultipleInstances.m; sourceTree = "<group>"; };
7D64150F27A94DE200A8A398 /* PreferencesWindow.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PreferencesWindow.m; sourceTree = "<group>"; };
7D64B96727853C2100A07164 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/TweakPreferencesController.strings; sourceTree = "<group>"; };
7D64B96827853C2800A07164 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/TweakPreferencesController.strings"; sourceTree = "<group>"; };
7D64B96927853C2B00A07164 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/TweakPreferencesController.strings"; sourceTree = "<group>"; };
@ -47,8 +53,6 @@
7D9049F41F82A415004E6370 /* fishhook.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fishhook.h; sourceTree = "<group>"; };
7D9049F71F82B6FB004E6370 /* NSString+WeChatTweak.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSString+WeChatTweak.h"; sourceTree = "<group>"; };
7D9049F81F82B6FB004E6370 /* NSString+WeChatTweak.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSString+WeChatTweak.m"; sourceTree = "<group>"; };
7DB8AFBC206211D900E683AE /* WTConfigManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WTConfigManager.h; sourceTree = "<group>"; };
7DB8AFBD206211D900E683AE /* WTConfigManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WTConfigManager.m; sourceTree = "<group>"; };
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 = "<group>"; };
7DF8422B1F40583F00D42D79 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -83,17 +87,6 @@
name = Frameworks;
sourceTree = "<group>";
};
7D5AAF3320DF4B7B00860EEE /* Manager */ = {
isa = PBXGroup;
children = (
7D14E5A21F6447DB00D75132 /* AlfredManager.h */,
7D14E5A31F6447DB00D75132 /* AlfredManager.m */,
7DB8AFBC206211D900E683AE /* WTConfigManager.h */,
7DB8AFBD206211D900E683AE /* WTConfigManager.m */,
);
path = Manager;
sourceTree = "<group>";
};
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;
};

View File

@ -1,14 +1,11 @@
//
// AlfredManager.h
// Alfred.h
// WeChatTweak
//
// Created by Sunnyyoung on 2017/9/10.
// Copyright © 2017年 Sunnyyoung. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import <objc/runtime.h>
#import <objc/message.h>
#import <YYModel/YYModel.h>
#import <GCDWebServer/GCDWebServer.h>
#import <GCDWebServer/GCDWebServerDataResponse.h>

View File

@ -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;

View File

@ -1,20 +0,0 @@
//
// 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

View File

@ -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;

48
WeChatTweak/ContactData.m Normal file
View File

@ -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

156
WeChatTweak/ContextMenu.m Normal file
View File

@ -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 <CoreImage/CoreImage.h>
@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

View File

@ -6,7 +6,7 @@
// Copyright © 2017年 Sunnyyoung. All rights reserved.
//
#import "WeChatTweakHeaders.h"
#import "WeChatTweak.h"
@interface TweakPreferencesController : NSViewController

View File

@ -8,7 +8,6 @@
#import "TweakPreferencesController.h"
#import "NSBundle+WeChatTweak.h"
#import "WTConfigManager.h"
@interface TweakPreferencesController () <MASPreferencesViewController>
@ -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

41
WeChatTweak/Directory.m Normal file
View File

@ -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<NSString *> *(*original_NSSearchPathForDirectoriesInDomains)(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde);
NSString *tweak_NSHomeDirectory(void) {
return [original_NSHomeDirectory() stringByAppendingPathComponent:@"/Library/Containers/com.tencent.xinWeChat/Data/"];
}
NSArray<NSString *> *tweak_NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde) {
if (domainMask == NSUserDomainMask) {
NSMutableArray<NSString *> *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);
}

View File

@ -1,25 +0,0 @@
//
// WTConfigManager.h
// WeChatTweak
//
// Created by Sunny Young on 21/03/2018.
// Copyright © 2018 Sunnyyoung. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import <objc/runtime.h>
#import <objc/message.h>
typedef NS_ENUM(NSUInteger, WTRevokedMessageStyle) {
WTRevokedMessageStyleClassic = 0,
WTRevokedMessageStyleMask
};
@interface WTConfigManager : NSObject
@property (nonatomic, assign) BOOL compressedJSONEnabled;
@property (nonatomic, assign) WTRevokedMessageStyle revokedMessageStyle;
+ (instancetype)sharedInstance;
@end

View File

@ -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

View File

@ -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<NSRunningApplication *> *)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

View File

@ -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

View File

@ -6,10 +6,10 @@
// Copyright © 2017年 Sunnyyoung. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#import <AppKit/AppKit.h>
#import <objc/runtime.h>
#import <objc/message.h>
#import <JRSwizzle/JRSwizzle.h>
typedef NS_ENUM(NSUInteger, RevokeNotificationType) {
RevokeNotificationTypeFollow = 0,

View File

@ -6,11 +6,19 @@
// Copyright © 2017年 Sunnyyoung. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import <objc/runtime.h>
#import <objc/message.h>
#import <JRSwizzle/JRSwizzle.h>
#import <CoreImage/CoreImage.h>
#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

View File

@ -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<NSString *> *(*original_NSSearchPathForDirectoriesInDomains)(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde);
NSString *tweak_NSHomeDirectory(void) {
return [original_NSHomeDirectory() stringByAppendingPathComponent:@"/Library/Containers/com.tencent.xinWeChat/Data/"];
}
NSArray<NSString *> *tweak_NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde) {
if (domainMask == NSUserDomainMask) {
NSMutableArray<NSString *> *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<NSRunningApplication *> *)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