mirror of
https://github.com/Sunnyyoung/WeChatTweak-macOS.git
synced 2025-05-22 22:36:07 +08:00
Refactor into standalone modules
This commit is contained in:
parent
4de5ca9fec
commit
3f3ec79543
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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>
|
|
@ -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()
|
||||
|
||||
|
@ -19,6 +19,10 @@
|
|||
|
||||
static int port = 48065;
|
||||
|
||||
+ (void)load {
|
||||
[AlfredManager.sharedInstance startListener];
|
||||
}
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
static AlfredManager *shared;
|
|
@ -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
|
|
@ -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
48
WeChatTweak/ContactData.m
Normal 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
156
WeChatTweak/ContextMenu.m
Normal 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
|
|
@ -6,7 +6,7 @@
|
|||
// Copyright © 2017年 Sunnyyoung. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WeChatTweakHeaders.h"
|
||||
#import "WeChatTweak.h"
|
||||
|
||||
@interface TweakPreferencesController : NSViewController
|
||||
|
||||
|
|
|
@ -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
41
WeChatTweak/Directory.m
Normal 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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
50
WeChatTweak/MultipleInstances.m
Normal file
50
WeChatTweak/MultipleInstances.m
Normal 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
|
31
WeChatTweak/PreferencesWindow.m
Normal file
31
WeChatTweak/PreferencesWindow.m
Normal 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
|
||||
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user