Add some features

1. Improve system notification
2. Add message list notification
3. Update screenshot
4. Update README
This commit is contained in:
Sunnyyoung 2017-06-24 20:40:50 +08:00
parent 3b615b7547
commit d2a215865a
8 changed files with 134 additions and 18 deletions

View File

@ -10,25 +10,29 @@
![](Screenshot/0x01.png) ![](Screenshot/0x01.png)
![](Screenshot/0x02.png) ![](Screenshot/0x02.png)
![](Screenshot/0x03.png)
## 功能 ## 功能
- 阻止消息撤回 - 阻止消息撤回
- 消息列表通知
- 系统通知
- 正常撤回自己发出的消息
- 客户端无限多开 - 客户端无限多开
- 右键 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 客户端

View File

@ -10,25 +10,29 @@ A dynamic library tweak for WeChat macOS.
![](Screenshot/0x01.png) ![](Screenshot/0x01.png)
![](Screenshot/0x02.png) ![](Screenshot/0x02.png)
![](Screenshot/0x03.png)
## 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

View File

@ -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]; class_addMethod(objc_getClass("AppDelegate"), @selector(applicationDockMenu:), method_getImplementation(class_getInstanceMethod(objc_getClass("AppDelegate"), @selector(tweak_applicationDockMenu:))), "@:@");
[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:))), "@:@");
[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
View 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