mirror of
https://github.com/Sunnyyoung/WeChatTweak-macOS.git
synced 2025-07-08 00:26:06 +08:00
Add some features
1. Improve system notification 2. Add message list notification 3. Update screenshot 4. Update README
This commit is contained in:
parent
3b615b7547
commit
d2a215865a
|
@ -10,25 +10,29 @@
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|

|
||||||
|
|
||||||
## 功能
|
## 功能
|
||||||
|
|
||||||
- 阻止消息撤回
|
- 阻止消息撤回
|
||||||
|
- 消息列表通知
|
||||||
|
- 系统通知
|
||||||
|
- 正常撤回自己发出的消息
|
||||||
- 客户端无限多开
|
- 客户端无限多开
|
||||||
- 右键 Dock Icon 登录新的微信账号
|
- 右键 Dock Icon 登录新的微信账号
|
||||||
- 命令行执行:`open -n /Applications/WeChat.app`
|
- 命令行执行:`open -n /Applications/WeChat.app`
|
||||||
- 阻止退出登录(重新打开应用无需手机认证)
|
- 重新打开应用无需手机认证
|
||||||
|
|
||||||
## 使用
|
## 使用
|
||||||
|
|
||||||
- `sudo make install` 安装动态库
|
- `sudo make install` 安装动态库
|
||||||
- `sudo make uninstall` 卸载动态库
|
- `sudo make uninstall` 卸载动态库
|
||||||
|
|
||||||
## 开发调试
|
## 开发调试
|
||||||
|
|
||||||
**Requirement: Command Line Tools**
|
**Requirement: Command Line Tools**
|
||||||
|
|
||||||
运行命令:`xcode-select --install` 安装 Command Line Tools。
|
运行命令:`xcode-select --install` 安装 Command Line Tools
|
||||||
|
|
||||||
- `make build` 编译 dylib 动态库到当前目录下
|
- `make build` 编译 dylib 动态库到当前目录下
|
||||||
- `make debug` 编译 dylib 动态库并临时注入微信 macOS 客户端
|
- `make debug` 编译 dylib 动态库并临时注入微信 macOS 客户端
|
||||||
|
|
12
README.md
12
README.md
|
@ -10,25 +10,29 @@ A dynamic library tweak for WeChat macOS.
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|

|
||||||
|
|
||||||
## Feature
|
## Feature
|
||||||
|
|
||||||
- Prevent message revoked
|
- Prevent message revoked
|
||||||
|
- Message list notification
|
||||||
|
- System notification
|
||||||
|
- Revoke message you sent
|
||||||
- Multiple WeChat Instance
|
- Multiple WeChat Instance
|
||||||
- Right click on the Dock icon to login another WeChat account
|
- Right click on the Dock icon to login another WeChat account
|
||||||
- Run command: `open -n /Applications/WeChat.app`
|
- Run command: `open -n /Applications/WeChat.app`
|
||||||
- Prevent logout (Auto login without authority)
|
- Auto login without authentication
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
- `sudo make install` Inject the dylib to `WeChat`
|
- `sudo make install` Inject the dylib
|
||||||
- `sudo make uninstall` Uninstall the injection
|
- `sudo make uninstall` Uninstall the dylib
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
**Requirement: Command Line Tools**
|
**Requirement: Command Line Tools**
|
||||||
|
|
||||||
Run `xcode-select --install` to install Command Line Tools.
|
Run `xcode-select --install` to install Command Line Tools
|
||||||
|
|
||||||
- `make build` Build the dylib file to the same dicrectory
|
- `make build` Build the dylib file to the same dicrectory
|
||||||
- `make debug` Build the dylib file and run `WeChat` with dynamic injection
|
- `make debug` Build the dylib file and run `WeChat` with dynamic injection
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 105 KiB |
Binary file not shown.
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 78 KiB |
BIN
Screenshot/0x03.png
Normal file
BIN
Screenshot/0x03.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 163 KiB |
Binary file not shown.
|
@ -3,30 +3,94 @@
|
||||||
#import <objc/message.h>
|
#import <objc/message.h>
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "JRSwizzle.h"
|
#import "JRSwizzle.h"
|
||||||
|
#import "WeChatTweakHeaders.h"
|
||||||
|
|
||||||
|
@implementation NSString (WeChatTweak)
|
||||||
|
|
||||||
|
- (NSString *)tweak_sessionFromMessage {
|
||||||
|
NSRange begin = [self rangeOfString:@"<session>"];
|
||||||
|
NSRange end = [self rangeOfString:@"</session>"];
|
||||||
|
NSRange range = NSMakeRange(begin.location + begin.length,end.location - begin.location - begin.length);
|
||||||
|
return [self substringWithRange:range];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)tweak_newMessageIDFromMessage {
|
||||||
|
NSRange begin = [self rangeOfString:@"<newmsgid>"];
|
||||||
|
NSRange end = [self rangeOfString:@"</newmsgid>"];
|
||||||
|
NSRange range = NSMakeRange(begin.location + begin.length,end.location - begin.location - begin.length);
|
||||||
|
return [[self substringWithRange:range] longLongValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)tweak_replaceMessageFromMessage {
|
||||||
|
NSRange begin = [self rangeOfString:@"<replacemsg><![CDATA["];
|
||||||
|
NSRange end = [self rangeOfString:@"]]></replacemsg>"];
|
||||||
|
NSRange range = NSMakeRange(begin.location + begin.length,end.location - begin.location - begin.length);
|
||||||
|
return [self substringWithRange:range];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation NSObject (WeChatTweak)
|
@implementation NSObject (WeChatTweak)
|
||||||
|
|
||||||
#pragma mark - Constructor
|
#pragma mark - Constructor
|
||||||
|
|
||||||
static void __attribute__((constructor)) tweak(void) {
|
static void __attribute__((constructor)) tweak(void) {
|
||||||
[objc_getClass("MessageService") jr_swizzleMethod:NSSelectorFromString(@"onRevokeMsg:") withMethod:@selector(tweak_onRevokeMsg:) error:nil];
|
|
||||||
[objc_getClass("CUtility") jr_swizzleClassMethod:NSSelectorFromString(@"HasWechatInstance") withClassMethod:@selector(tweak_HasWechatInstance) error:nil];
|
|
||||||
class_addMethod(objc_getClass("AppDelegate"), @selector(applicationDockMenu:), method_getImplementation(class_getInstanceMethod(objc_getClass("AppDelegate"), @selector(tweak_applicationDockMenu:))), "@:@");
|
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("AppDelegate") jr_swizzleMethod:NSSelectorFromString(@"applicationDidFinishLaunching:") withMethod:@selector(tweak_applicationDidFinishLaunching:) error:nil];
|
||||||
[objc_getClass("AppDelegate") jr_swizzleMethod:NSSelectorFromString(@"applicationShouldTerminate:") withMethod:@selector(tweak_applicationShouldTerminate:) error:nil];
|
[objc_getClass("AppDelegate") jr_swizzleMethod:NSSelectorFromString(@"applicationShouldTerminate:") withMethod:@selector(tweak_applicationShouldTerminate:) error:nil];
|
||||||
|
[objc_getClass("MessageService") jr_swizzleMethod:NSSelectorFromString(@"onRevokeMsg:") withMethod:@selector(tweak_onRevokeMsg:) error:nil];
|
||||||
|
[objc_getClass("CUtility") jr_swizzleClassMethod:NSSelectorFromString(@"HasWechatInstance") withClassMethod:@selector(tweak_HasWechatInstance) error:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - No Revoke Message
|
#pragma mark - No Revoke Message
|
||||||
|
|
||||||
- (void)tweak_onRevokeMsg:(NSString *)message {
|
- (void)tweak_onRevokeMsg:(NSString *)message {
|
||||||
NSRange begin = [message rangeOfString:@"<replacemsg><![CDATA["];
|
// Decode message
|
||||||
NSRange end = [message rangeOfString:@"]]></replacemsg>"];
|
NSString *session = [message tweak_sessionFromMessage];
|
||||||
NSRange subRange = NSMakeRange(begin.location + begin.length,end.location - begin.location - begin.length);
|
NSUInteger newMessageID = [message tweak_newMessageIDFromMessage];
|
||||||
NSString *informativeText = [message substringWithRange:subRange];
|
NSString *replaceMessage = [message tweak_replaceMessageFromMessage];
|
||||||
|
|
||||||
|
// Prepare message data
|
||||||
|
MessageData *localMessageData = [((MessageService *)self) GetMsgData:session svrId:newMessageID];
|
||||||
|
MessageData *promptMessageData = ({
|
||||||
|
MessageData *data = [[objc_getClass("MessageData") alloc] init];
|
||||||
|
data.messageType = 10000;
|
||||||
|
data.msgStatus = 4;
|
||||||
|
data.toUsrName = localMessageData.toUsrName;
|
||||||
|
data.fromUsrName = localMessageData.fromUsrName;
|
||||||
|
data.msgCreateTime = localMessageData.msgCreateTime;
|
||||||
|
if ([localMessageData isSendFromSelf]) {
|
||||||
|
data.mesLocalID = localMessageData.mesLocalID;
|
||||||
|
data.msgContent = replaceMessage;
|
||||||
|
} else {
|
||||||
|
data.mesLocalID = localMessageData.mesLocalID + 1;
|
||||||
|
data.msgContent = [NSString stringWithFormat:@"[已拦截]\n%@", replaceMessage];
|
||||||
|
}
|
||||||
|
data;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Prepare notification information
|
||||||
|
NSUserNotification *userNotification = [[NSUserNotification alloc] init];
|
||||||
|
if ([session rangeOfString:@"@chatroom"].location == NSNotFound) {
|
||||||
|
userNotification.informativeText = replaceMessage;
|
||||||
|
} else {
|
||||||
|
GroupStorage *groupStorage = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("GroupStorage")];
|
||||||
|
WCContactData *groupContact = [groupStorage GetGroupContact: session];
|
||||||
|
NSString *groupName = groupContact.m_nsNickName.length ? groupContact.m_nsNickName : @"群组";
|
||||||
|
userNotification.informativeText = [NSString stringWithFormat:@"%@: %@", groupName, replaceMessage];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch notification
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
NSUserNotification *userNotification = [[NSUserNotification alloc] init];
|
// Delete message if is revoke from myself
|
||||||
userNotification.informativeText = informativeText;
|
if ([localMessageData isSendFromSelf]) {
|
||||||
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:userNotification];
|
[((MessageService *)self) DelMsg:session msgList:@[localMessageData] isDelAll:NO isManual:YES];
|
||||||
|
[((MessageService *)self) AddRevokePromptMsg:session msgData: promptMessageData];
|
||||||
|
} else {
|
||||||
|
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:userNotification];
|
||||||
|
[((MessageService *)self) AddRevokePromptMsg:session msgData: promptMessageData];
|
||||||
|
[((MessageService *)self) notifyAddMsgOnMainThread:session msgData: promptMessageData];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +115,7 @@ static void __attribute__((constructor)) tweak(void) {
|
||||||
[task launch];
|
[task launch];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - No Logout
|
#pragma mark - Auto auth
|
||||||
|
|
||||||
- (void)tweak_applicationDidFinishLaunching:(NSNotification *)notification {
|
- (void)tweak_applicationDidFinishLaunching:(NSNotification *)notification {
|
||||||
[self tweak_applicationDidFinishLaunching:notification];
|
[self tweak_applicationDidFinishLaunching:notification];
|
||||||
|
|
44
WeChatTweakHeaders.h
Normal file
44
WeChatTweakHeaders.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@interface MessageData: NSObject
|
||||||
|
|
||||||
|
@property(nonatomic) unsigned int messageType;
|
||||||
|
@property(nonatomic) unsigned int msgStatus;
|
||||||
|
@property(retain, nonatomic) NSString *toUsrName;
|
||||||
|
@property(retain, nonatomic) NSString *fromUsrName;
|
||||||
|
@property(retain, nonatomic) NSString *msgContent;
|
||||||
|
@property(nonatomic) unsigned int msgCreateTime;
|
||||||
|
@property(nonatomic) unsigned int mesLocalID;
|
||||||
|
|
||||||
|
- (BOOL)isSendFromSelf;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface WCContactData : NSObject
|
||||||
|
|
||||||
|
@property(retain, nonatomic) NSString *m_nsNickName;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface GroupStorage: NSObject
|
||||||
|
|
||||||
|
- (id)GetGroupContact:(id)arg1;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MMServiceCenter : NSObject
|
||||||
|
|
||||||
|
+ (id)defaultCenter;
|
||||||
|
- (id)getService:(Class)arg1;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MessageService: NSObject
|
||||||
|
|
||||||
|
- (id)GetMsgData:(id)arg1 svrId:(unsigned long long)arg2;
|
||||||
|
- (void)DelMsg:(id)arg1 msgList:(id)arg2 isDelAll:(BOOL)arg3 isManual:(BOOL)arg4;
|
||||||
|
- (void)AddRevokePromptMsg:(id)arg1 msgData:(id)arg2;
|
||||||
|
- (void)notifyAddMsgOnMainThread:(id)arg1 msgData:(id)arg2;
|
||||||
|
|
||||||
|
@end
|
Loading…
Reference in New Issue
Block a user