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,14 +10,18 @@
![](Screenshot/0x01.png)
![](Screenshot/0x02.png)
![](Screenshot/0x03.png)
## 功能
- 阻止消息撤回
- 消息列表通知
- 系统通知
- 正常撤回自己发出的消息
- 客户端无限多开
- 右键 Dock Icon 登录新的微信账号
- 命令行执行:`open -n /Applications/WeChat.app`
- 阻止退出登录(重新打开应用无需手机认证
- 重新打开应用无需手机认证
## 使用
@ -28,7 +32,7 @@
**Requirement: Command Line Tools**
运行命令:`xcode-select --install` 安装 Command Line Tools
运行命令:`xcode-select --install` 安装 Command Line Tools
- `make build` 编译 dylib 动态库到当前目录下
- `make debug` 编译 dylib 动态库并临时注入微信 macOS 客户端

View File

@ -10,25 +10,29 @@ A dynamic library tweak for WeChat macOS.
![](Screenshot/0x01.png)
![](Screenshot/0x02.png)
![](Screenshot/0x03.png)
## Feature
- Prevent message revoked
- Message list notification
- System notification
- Revoke message you sent
- Multiple WeChat Instance
- Right click on the Dock icon to login another WeChat account
- Run command: `open -n /Applications/WeChat.app`
- Prevent logout (Auto login without authority)
- Auto login without authentication
## Usage
- `sudo make install` Inject the dylib to `WeChat`
- `sudo make uninstall` Uninstall the injection
- `sudo make install` Inject the dylib
- `sudo make uninstall` Uninstall the dylib
## Development
**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 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 <Cocoa/Cocoa.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)
#pragma mark - Constructor
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:))), "@:@");
[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("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
- (void)tweak_onRevokeMsg:(NSString *)message {
NSRange begin = [message rangeOfString:@"<replacemsg><![CDATA["];
NSRange end = [message rangeOfString:@"]]></replacemsg>"];
NSRange subRange = NSMakeRange(begin.location + begin.length,end.location - begin.location - begin.length);
NSString *informativeText = [message substringWithRange:subRange];
dispatch_async(dispatch_get_main_queue(), ^{
// Decode message
NSString *session = [message tweak_sessionFromMessage];
NSUInteger newMessageID = [message tweak_newMessageIDFromMessage];
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];
userNotification.informativeText = informativeText;
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(), ^{
// Delete message if is revoke from myself
if ([localMessageData isSendFromSelf]) {
[((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];
}
#pragma mark - No Logout
#pragma mark - Auto auth
- (void)tweak_applicationDidFinishLaunching:(NSNotification *)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