Showing preview only (265K chars total). Download the full file or copy to clipboard to get everything.
Repository: liyongmeng123/TKkk-iOSer-WeChatPlugin-MacOS
Branch: master
Commit: c955404205cb
Files: 91
Total size: 232.5 KB
Directory structure:
gitextract_xb1v15h_/
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── Other/
│ ├── Install.sh
│ ├── Products/
│ │ └── Debug/
│ │ └── WeChatPlugin.framework/
│ │ ├── Headers/
│ │ │ └── WeChatPlugin.h
│ │ ├── Modules/
│ │ │ └── module.modulemap
│ │ ├── Resources/
│ │ │ ├── Info.plist
│ │ │ ├── TKAutoReplyWindowController.nib
│ │ │ ├── TKRemoteControlCommands.plist
│ │ │ ├── TKRemoteControlScript.scpt
│ │ │ └── TKRemoteControlWindowController.nib
│ │ ├── Versions/
│ │ │ ├── A/
│ │ │ │ ├── Headers/
│ │ │ │ │ └── WeChatPlugin.h
│ │ │ │ ├── Modules/
│ │ │ │ │ └── module.modulemap
│ │ │ │ ├── Resources/
│ │ │ │ │ ├── Info.plist
│ │ │ │ │ ├── TKAutoReplyWindowController.nib
│ │ │ │ │ ├── TKRemoteControlCommands.plist
│ │ │ │ │ ├── TKRemoteControlScript.scpt
│ │ │ │ │ └── TKRemoteControlWindowController.nib
│ │ │ │ └── WeChatPlugin
│ │ │ └── Current/
│ │ │ ├── Headers/
│ │ │ │ └── WeChatPlugin.h
│ │ │ ├── Modules/
│ │ │ │ └── module.modulemap
│ │ │ ├── Resources/
│ │ │ │ ├── Info.plist
│ │ │ │ ├── TKAutoReplyWindowController.nib
│ │ │ │ ├── TKRemoteControlCommands.plist
│ │ │ │ ├── TKRemoteControlScript.scpt
│ │ │ │ └── TKRemoteControlWindowController.nib
│ │ │ └── WeChatPlugin
│ │ └── WeChatPlugin
│ ├── Uninstall.sh
│ └── insert_dylib
├── Podfile
├── README.md
├── WeChatPlugin/
│ ├── Info.plist
│ ├── Sources/
│ │ ├── Category/
│ │ │ ├── MMChatsTableCellView+hook.h
│ │ │ ├── MMChatsTableCellView+hook.m
│ │ │ ├── MMStickerMessageCellView+hook.h
│ │ │ ├── MMStickerMessageCellView+hook.m
│ │ │ ├── WeChat+hook.h
│ │ │ └── WeChat+hook.m
│ │ ├── Common/
│ │ │ ├── Category/
│ │ │ │ ├── NSButton+Action.h
│ │ │ │ ├── NSButton+Action.m
│ │ │ │ ├── NSTextField+Action.h
│ │ │ │ ├── NSTextField+Action.m
│ │ │ │ ├── NSView+Action.h
│ │ │ │ └── NSView+Action.m
│ │ │ ├── Color.h
│ │ │ └── TKPrefixHeader.pch
│ │ ├── Config/
│ │ │ ├── TKWeChatPluginConfig.h
│ │ │ └── TKWeChatPluginConfig.m
│ │ ├── Controllers/
│ │ │ ├── TKRemoteControlController.h
│ │ │ └── TKRemoteControlController.m
│ │ ├── Models/
│ │ │ ├── TKAutoReplyModel.h
│ │ │ ├── TKAutoReplyModel.m
│ │ │ ├── TKBaseModel.h
│ │ │ ├── TKBaseModel.m
│ │ │ ├── TKIgnoreSessonModel.h
│ │ │ ├── TKIgnoreSessonModel.m
│ │ │ ├── TKRemoteControlModel.h
│ │ │ └── TKRemoteControlModel.m
│ │ ├── Utils/
│ │ │ ├── TKHelper.h
│ │ │ ├── TKHelper.m
│ │ │ ├── TKVersionManager.h
│ │ │ ├── TKVersionManager.m
│ │ │ ├── TKWebServerManager.h
│ │ │ ├── TKWebServerManager.m
│ │ │ ├── XMLReader.h
│ │ │ └── XMLReader.m
│ │ ├── Vendor/
│ │ │ ├── fishhook.c
│ │ │ └── fishhook.h
│ │ ├── Views/
│ │ │ ├── AutoReply/
│ │ │ │ ├── TKAutoReplyCell.h
│ │ │ │ ├── TKAutoReplyCell.m
│ │ │ │ ├── TKAutoReplyContentView.h
│ │ │ │ └── TKAutoReplyContentView.m
│ │ │ └── RemoteControl/
│ │ │ ├── TKRemoteControlCell.h
│ │ │ └── TKRemoteControlCell.m
│ │ └── WindowControllers/
│ │ ├── AutoReply/
│ │ │ ├── TKAutoReplyWindowController.h
│ │ │ ├── TKAutoReplyWindowController.m
│ │ │ └── TKAutoReplyWindowController.xib
│ │ └── RemoteControl/
│ │ ├── TKRemoteControlWindowController.h
│ │ ├── TKRemoteControlWindowController.m
│ │ └── TKRemoteControlWindowController.xib
│ ├── TKRemoteControlCommands.plist
│ ├── TKRemoteControlScript.scpt
│ ├── WeChatPlugin.h
│ └── main.mm
├── WeChatPlugin.xcodeproj/
│ ├── project.pbxproj
│ ├── project.xcworkspace/
│ │ └── contents.xcworkspacedata
│ └── xcuserdata/
│ └── TK.xcuserdatad/
│ ├── xcdebugger/
│ │ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes/
│ ├── WeChatPlugin.xcscheme
│ └── xcschememanagement.plist
└── WeChatPlugin.xcworkspace/
└── contents.xcworkspacedata
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Xcode
#
.DS_Store
*.xcuserstate
# CocoaPods
#
Pods/
Podfile.lock
================================================
FILE: CHANGELOG.md
================================================
# 更新日志
### [v1.6 (2018-03-18)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.6)
* 新增 Alfred 快捷发送消息 & 打开聊天窗口
### [v1.5.1 (2018-03-03)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.5.1)
* 新增语音远程控制 mac
* 修复快捷回复后消息未读数未更新的问题
* 优化防撤回提醒 (显示撤回人昵称 & 消息类型)
### [v1.5.0 (2018-02-24)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.5.0)
* 优化防撤回提醒
* 新增自动登录开关
* 新增小助手检测更新
* 新增通知中心快捷回复
* 新增表情包复制 & 存储
* 自动回复 & 远程控制设置存储到本地
### [v1.4.0 (2017-10-11)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.4.0)
* 新增微信窗口置顶
* 新增最近联系人多选删除
* 新增远程控制信息回调
* 新增手机端获取指令信息
### [v1.3.0 (2017-09-17)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.3.0)
* 新增最近联系人置底功能
* 新增免认证登录
### [v1.2.0 (2017-09-11)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.2.0)
* 自动回复新增 正则匹配 & 私聊开关
* 修复聊天记录消失 & `~/Documents` 生成MMappedKV、JietuSDKStat.plist 文件
### [v1.1.0 (2017-08-23)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.1.0)
* 重构 自动回复,实现多回复
* 优化 代码结构
### [v1.0.1 (2017-08-18)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.0.1)
* 修复 部分防撤回提示乱码的bug
* 新增 Install.sh & Uninstall.sh
### [v1.0.0 (2017-08-09)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.0.0)
* 新增 远程控制
* 新增 微信多开
* 优化 消息防撤回
* 优化 自动回复
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2017 TK
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Other/Install.sh
================================================
#!/bin/bash
app_name="WeChat"
shell_path="$(dirname "$0")"
wechat_path="/Applications/WeChat.app"
framework_name="WeChatPlugin"
app_bundle_path="/Applications/${app_name}.app/Contents/MacOS"
app_executable_path="${app_bundle_path}/${app_name}"
app_executable_backup_path="${app_executable_path}_backup"
framework_path="${app_bundle_path}/${framework_name}.framework"
# 对 WeChat 赋予权限
if [ ! -w "$wechat_path" ]
then
echo -e "\n\n为了将小助手写入微信, 请输入密码 : "
sudo chown -R $(whoami) "$wechat_path"
fi
# 备份 WeChat 原始可执行文件
if [ ! -f "$app_executable_backup_path" ]
then
cp "$app_executable_path" "$app_executable_backup_path"
result="y"
else
read -t 150 -p "已安装微信小助手,是否覆盖?[y/n]:" result
fi
if [[ "$result" == 'y' ]]; then
cp -r "${shell_path}/Products/Debug/${framework_name}.framework" ${app_bundle_path}
${shell_path}/insert_dylib --all-yes "${framework_path}/${framework_name}" "$app_executable_backup_path" "$app_executable_path"
fi
================================================
FILE: Other/Products/Debug/WeChatPlugin.framework/Headers/WeChatPlugin.h
================================================
//
// WeChatPlugin.h
// WeChatPlugin
//
// Created by TK on 2017/4/19.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "TKWeChatPluginConfig.h"
#import "TKHelper.h"
FOUNDATION_EXPORT double WeChatPluginVersionNumber;
FOUNDATION_EXPORT const unsigned char WeChatPluginVersionString[];
#pragma mark - 微信原始的部分类与方法
@interface MMLoginOneClickViewController : NSViewController
@property(nonatomic) NSTextField *descriptionLabel;
- (void)onLoginButtonClicked:(id)arg1;
@property(nonatomic) NSButton *loginButton;
@end
@interface AccountService : NSObject
- (id)GetLastLoginUserName;
- (id)GetLastLoginAutoAuthKey;
- (BOOL)canAutoAuth;
- (void)AutoAuth;
- (void)ManualLogin:(id)arg1 withPassword:(id)arg2;
- (void)ManualLogout;
- (void)QRCodeLoginWithUserName:(id)arg1 password:(id)arg2;
@end
@interface MMLoginViewController : NSObject
@property(retain, nonatomic) MMLoginOneClickViewController *oneClickViewController;
@end
@interface MMMainWindowController : NSWindowController
@property(retain, nonatomic) MMLoginViewController *loginViewController;
- (void)onAuthOK;
- (void)onLogOut;
@end
@interface MessageService : NSObject
- (void)onRevokeMsg:(id)arg1;
- (void)OnSyncBatchAddMsgs:(NSArray *)arg1 isFirstSync:(BOOL)arg2;
- (id)SendTextMessage:(id)arg1 toUsrName:(id)arg2 msgText:(id)arg3 atUserList:(id)arg4;
- (id)GetMsgData:(id)arg1 svrId:(long)arg2;
- (void)AddLocalMsg:(id)arg1 msgData:(id)arg2;
- (void)TranscribeVoiceMessage:(id)arg1 completion:(void (^)(void))arg2;
- (BOOL)ClearUnRead:(id)arg1 FromID:(unsigned int)arg2 ToID:(unsigned int)arg3;
@end
@interface MMServiceCenter : NSObject
+ (id)defaultCenter;
- (id)getService:(Class)arg1;
@end
@interface SKBuiltinString_t : NSObject
@property(retain, nonatomic, setter=SetString:) NSString *string; // @synthesize string;
@end
@interface AddMsg : NSObject
@property(retain, nonatomic, setter=SetContent:) SKBuiltinString_t *content; // @synthesize content;
@property(retain, nonatomic, setter=SetFromUserName:) SKBuiltinString_t *fromUserName; // @synthesize fromUserName;
@property(nonatomic, setter=SetMsgType:) int msgType; // @synthesize msgType;
@property(retain, nonatomic, setter=SetToUserName:) SKBuiltinString_t *toUserName; // @synthesize toUserName;
@property (nonatomic, assign) unsigned int createTime;
@property(nonatomic, setter=SetNewMsgId:) long long newMsgId;
@end
@interface MMChatsViewController : NSViewController <NSTableViewDataSource, NSTableViewDelegate>
@property(nonatomic) __weak NSTableView *tableView;
@end
@interface WeChat : NSObject
+ (id)sharedInstance;
@property(nonatomic) MMChatsViewController *chatsViewController;
@property(retain, nonatomic) MMMainWindowController *mainWindowController;
@property(nonatomic) BOOL isAppTerminating;
- (void)startANewChatWithContact:(id)arg1;
- (void)onAuthOK:(BOOL)arg1;
@end
@interface ContactStorage : NSObject
- (id)GetSelfContact;
- (id)GetContact:(id)arg1;
@end
@interface WCContactData : NSObject
@property(retain, nonatomic) NSString *m_nsUsrName; // @synthesize m_nsUsrName;
@property(nonatomic) unsigned int m_uiFriendScene; // @synthesize m_uiFriendScene;
@property(retain, nonatomic) NSString *m_nsNickName; // 用户昵称
@property(retain, nonatomic) NSString *m_nsRemark; // 备注
@property(retain, nonatomic) NSString *m_nsHeadImgUrl; // 头像
- (BOOL)isBrandContact;
- (BOOL)isSelf;
- (id)getGroupDisplayName;
@end
@interface MessageData : NSObject
- (id)initWithMsgType:(long long)arg1;
@property(retain, nonatomic) NSString *fromUsrName;
@property(retain, nonatomic) NSString *toUsrName;
@property(retain, nonatomic) NSString *msgContent;
@property(retain, nonatomic) NSString *msgPushContent;
@property(nonatomic) int messageType;
@property(nonatomic) int msgStatus;
@property(nonatomic) int msgCreateTime;
@property(nonatomic) int mesLocalID;
@property(nonatomic) long long mesSvrID;
@property(retain, nonatomic) NSString *msgVoiceText;
@property(copy, nonatomic) NSString *m_nsEmoticonMD5;
- (BOOL)isChatRoomMessage;
- (id)groupChatSenderDisplayName;
- (id)getRealMessageContent;
- (BOOL)isSendFromSelf;
- (BOOL)isCustomEmojiMsg;
- (BOOL)isImgMsg;
- (BOOL)isVideoMsg;
- (BOOL)isVoiceMsg;
@end
@interface CUtility : NSObject
+ (BOOL)HasWechatInstance;
+ (unsigned long long)getFreeDiskSpace;
+ (void)ReloadSessionForMsgSync;
+ (id)GetCurrentUserName;
@end
@interface MMSessionInfo : NSObject
@property(nonatomic) BOOL m_bIsTop; // @synthesize m_bIsTop;
@property(nonatomic) BOOL m_bShowUnReadAsRedDot;
@property(nonatomic) BOOL m_isMentionedUnread; // @synthesize
@property(retain, nonatomic) NSString *m_nsUserName; // @synthesize m_nsUserName;
@property(retain, nonatomic) WCContactData *m_contact;
@end
@protocol MMChatsTableCellViewDelegate <NSObject>
@optional
- (void)cellViewReloadData:(MMSessionInfo *)arg1;
@end
@interface MMChatsTableCellView : NSTableCellView
@property(nonatomic) __weak id <MMChatsTableCellViewDelegate> delegate;
@property(retain, nonatomic) MMSessionInfo *sessionInfo;
- (void)menuWillOpen:(id)arg1;
- (void)contextMenuSticky:(id)arg1;
- (void)contextMenuDelete:(id)arg1;
- (void)tableView:(NSTableView *)arg1 rowGotMouseDown:(long long)arg2;
@end
@interface MMSessionMgr : NSObject
@property(retain, nonatomic) NSMutableArray *m_arrSession;
- (id)GetSessionAtIndex:(unsigned long long)arg1;
- (void)MuteSessionByUserName:(id)arg1;
//- (void)TopSessionByUserName:(id)arg1;
- (void)UnmuteSessionByUserName:(id)arg1;
- (void)UntopSessionByUserName:(id)arg1;
- (void)deleteSessionWithoutSyncToServerWithUserName:(id)arg1;
- (void)sortSessions;
- (id)getContact:(id)arg1;
@end
@interface LogoutCGI : NSTableCellView
- (void)sendLogoutCGIWithCompletion:(id)arg1;
@end
@interface MMNotificationService : NSObject
- (id)getNotificationContentWithMsgData:(id)arg1;
- (void)userNotificationCenter:(id)arg1 didActivateNotification:(id)arg2;
@end
@interface MMChatMessageViewController : NSViewController
@end
@interface MMMessageTableItem : NSObject
@property(retain, nonatomic) MessageData *message;
@end
@interface MMStickerMessageCellView : NSObject
@property(retain, nonatomic) MMMessageTableItem *messageTableItem;
@property(nonatomic) MMChatMessageViewController *delegate;
- (BOOL)allowCopy;
- (void)contextMenuCopy;
- (id)contextMenu;
@end
@interface EmoticonMgr : NSObject
@property(retain, nonatomic) MessageData *message;
- (id)getEmotionDataWithMD5:(id)arg1;
@end
@interface MMComplexContactSearchTaskMgr : NSObject
+ (id)sharedInstance;
- (void)doComplexContactSearch:(id)arg1 searchScene:(unsigned long long)arg2 complete:(void (^)(NSArray *, NSArray *, NSArray *))arg3 cancelable:(BOOL)arg4;
@end
@interface MMComplexContactSearchResult : NSObject
@property(retain, nonatomic) NSString *fieldValue;
@property(retain, nonatomic) WCContactData *contact;
@property(nonatomic) unsigned long long fieldType; // 1:备注 3:昵称 4:微信号 8:省份 7:市 9:国家
@end
@interface MMComplexGroupContactMembersSearchResult : NSObject
@property(retain, nonatomic) NSMutableArray<MMComplexContactSearchResult *> *membersSearchReults;
@end
@interface MMComplexGroupContactSearchResult : NSObject
@property(nonatomic) unsigned long long searchType; // 1 名称 2 群成员名称
@property(retain) WCContactData *groupContact;
@property(retain, nonatomic) MMComplexGroupContactMembersSearchResult *groupMembersResult;
@end
@interface MMAvatarService : NSObject
- (NSString *)avatarCachePath;
@end
@interface NSString (MD5)
- (id)md5String;
@end
================================================
FILE: Other/Products/Debug/WeChatPlugin.framework/Modules/module.modulemap
================================================
framework module WeChatPlugin {
umbrella header "WeChatPlugin.h"
export *
module * { export * }
}
================================================
FILE: Other/Products/Debug/WeChatPlugin.framework/Resources/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>17C88</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>WeChatPlugin</string>
<key>CFBundleIdentifier</key>
<string>tk.WeChatPlugin</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>WeChatPlugin</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.6</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string>9C40b</string>
<key>DTPlatformVersion</key>
<string>GM</string>
<key>DTSDKBuild</key>
<string>17C76</string>
<key>DTSDKName</key>
<string>macosx10.13</string>
<key>DTXcode</key>
<string>0920</string>
<key>DTXcodeBuild</key>
<string>9C40b</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2017年 tk. All rights reserved.</string>
<key>versionInfo</key>
<string>👉 v1.6 (2018-03-18):\n新增 Alfred 快捷发消息 & 打开聊天窗口\n\n之前版本更新内容:\n👉 (v1.5.1) :\n👉🏻 新增语音远程控制 mac\n👉🏼 修复快捷回复后消息未读数未更新的问题\n👉🏽 优化防撤回提醒 (显示撤回人昵称 & 消息类型) \n\n👉 (v1.5.0):\n👉🏻 新增自动登录开关 \n👉🏼 新增小助手检测更新 \n👉🏽 新增通知中心快捷回复 \n👉🏾 新增表情包复制 & 存储 \n👉🏿 自动回复 & 远程控制设置存储到本地 \n\n👉🏻 优化防撤回提醒。 \n👉🏼 新增自动登录开关。 \n👉🏽 新增表情复制 & 存储。 \n👉🏾 新增通知中心快捷回复。\n👉🏿 自动回复 & 远程控制设置存储到本地。\n\n
</string>
</dict>
</plist>
================================================
FILE: Other/Products/Debug/WeChatPlugin.framework/Resources/TKRemoteControlCommands.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<array>
<dict>
<key>executeCommand</key>
<string>open /System/Library/Frameworks/ScreenSaver.framework/Versions/A/Resources/ScreenSaverEngine.app || open /System/Library/CoreServices/ScreenSaverEngine.app </string>
<key>keyword</key>
<string>ScreenSave</string>
<key>function</key>
<string>屏幕保护</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>/System/Library/CoreServices/Menu\ Extras/User.menu/Contents/Resources/CGSession -suspend</string>
<key>keyword</key>
<string>LockScreen</string>
<key>function</key>
<string>锁屏</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>sleep</string>
<key>keyword</key>
<string>Sleep</string>
<key>function</key>
<string>休眠</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>shutdown</string>
<key>keyword</key>
<string>Shutdown</string>
<key>function</key>
<string>关机</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>restart</string>
<key>keyword</key>
<string>Restart</string>
<key>function</key>
<string>重启</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>empty</string>
<key>keyword</key>
<string>EmptyTrash</string>
<key>function</key>
<string>清空废纸篓</string>
<key>enable</key>
<true/>
</dict>
</array>
<array>
<dict>
<key>executeCommand</key>
<string>killQQ</string>
<key>keyword</key>
<string>KillQQ</string>
<key>function</key>
<string>退出 QQ</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>killWeChat</string>
<key>keyword</key>
<string>KillWeChat</string>
<key>function</key>
<string>退出 WeChat</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>killChrome</string>
<key>keyword</key>
<string>KillChrome</string>
<key>function</key>
<string>退出 Chrome</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>killSafari</string>
<key>keyword</key>
<string>KillSafari</string>
<key>function</key>
<string>退出 Safari</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>killAll</string>
<key>keyword</key>
<string>KillAll</string>
<key>function</key>
<string>退出所有程序</string>
<key>enable</key>
<true/>
</dict>
</array>
<array>
<dict>
<key>executeCommand</key>
<string>musicToggle</string>
<key>keyword</key>
<string>Toggle</string>
<key>function</key>
<string>播放/暂停</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicNext</string>
<key>keyword</key>
<string>Next</string>
<key>function</key>
<string>下一首</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicPrevious</string>
<key>keyword</key>
<string>Previous</string>
<key>function</key>
<string>上一首</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicVolumeUp</string>
<key>keyword</key>
<string>VolumeUp</string>
<key>function</key>
<string>增大音量</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicVolumeDown</string>
<key>keyword</key>
<string>VolumeDown</string>
<key>function</key>
<string>减小音量</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicLike</string>
<key>keyword</key>
<string>LikeChange</string>
<key>function</key>
<string>喜欢/取消喜欢</string>
<key>enable</key>
<true/>
</dict>
</array>
</array>
</plist>
================================================
FILE: Other/Products/Debug/WeChatPlugin.framework/Versions/A/Headers/WeChatPlugin.h
================================================
//
// WeChatPlugin.h
// WeChatPlugin
//
// Created by TK on 2017/4/19.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "TKWeChatPluginConfig.h"
#import "TKHelper.h"
FOUNDATION_EXPORT double WeChatPluginVersionNumber;
FOUNDATION_EXPORT const unsigned char WeChatPluginVersionString[];
#pragma mark - 微信原始的部分类与方法
@interface MMLoginOneClickViewController : NSViewController
@property(nonatomic) NSTextField *descriptionLabel;
- (void)onLoginButtonClicked:(id)arg1;
@property(nonatomic) NSButton *loginButton;
@end
@interface AccountService : NSObject
- (id)GetLastLoginUserName;
- (id)GetLastLoginAutoAuthKey;
- (BOOL)canAutoAuth;
- (void)AutoAuth;
- (void)ManualLogin:(id)arg1 withPassword:(id)arg2;
- (void)ManualLogout;
- (void)QRCodeLoginWithUserName:(id)arg1 password:(id)arg2;
@end
@interface MMLoginViewController : NSObject
@property(retain, nonatomic) MMLoginOneClickViewController *oneClickViewController;
@end
@interface MMMainWindowController : NSWindowController
@property(retain, nonatomic) MMLoginViewController *loginViewController;
- (void)onAuthOK;
- (void)onLogOut;
@end
@interface MessageService : NSObject
- (void)onRevokeMsg:(id)arg1;
- (void)OnSyncBatchAddMsgs:(NSArray *)arg1 isFirstSync:(BOOL)arg2;
- (id)SendTextMessage:(id)arg1 toUsrName:(id)arg2 msgText:(id)arg3 atUserList:(id)arg4;
- (id)GetMsgData:(id)arg1 svrId:(long)arg2;
- (void)AddLocalMsg:(id)arg1 msgData:(id)arg2;
- (void)TranscribeVoiceMessage:(id)arg1 completion:(void (^)(void))arg2;
- (BOOL)ClearUnRead:(id)arg1 FromID:(unsigned int)arg2 ToID:(unsigned int)arg3;
@end
@interface MMServiceCenter : NSObject
+ (id)defaultCenter;
- (id)getService:(Class)arg1;
@end
@interface SKBuiltinString_t : NSObject
@property(retain, nonatomic, setter=SetString:) NSString *string; // @synthesize string;
@end
@interface AddMsg : NSObject
@property(retain, nonatomic, setter=SetContent:) SKBuiltinString_t *content; // @synthesize content;
@property(retain, nonatomic, setter=SetFromUserName:) SKBuiltinString_t *fromUserName; // @synthesize fromUserName;
@property(nonatomic, setter=SetMsgType:) int msgType; // @synthesize msgType;
@property(retain, nonatomic, setter=SetToUserName:) SKBuiltinString_t *toUserName; // @synthesize toUserName;
@property (nonatomic, assign) unsigned int createTime;
@property(nonatomic, setter=SetNewMsgId:) long long newMsgId;
@end
@interface MMChatsViewController : NSViewController <NSTableViewDataSource, NSTableViewDelegate>
@property(nonatomic) __weak NSTableView *tableView;
@end
@interface WeChat : NSObject
+ (id)sharedInstance;
@property(nonatomic) MMChatsViewController *chatsViewController;
@property(retain, nonatomic) MMMainWindowController *mainWindowController;
@property(nonatomic) BOOL isAppTerminating;
- (void)startANewChatWithContact:(id)arg1;
- (void)onAuthOK:(BOOL)arg1;
@end
@interface ContactStorage : NSObject
- (id)GetSelfContact;
- (id)GetContact:(id)arg1;
@end
@interface WCContactData : NSObject
@property(retain, nonatomic) NSString *m_nsUsrName; // @synthesize m_nsUsrName;
@property(nonatomic) unsigned int m_uiFriendScene; // @synthesize m_uiFriendScene;
@property(retain, nonatomic) NSString *m_nsNickName; // 用户昵称
@property(retain, nonatomic) NSString *m_nsRemark; // 备注
@property(retain, nonatomic) NSString *m_nsHeadImgUrl; // 头像
- (BOOL)isBrandContact;
- (BOOL)isSelf;
- (id)getGroupDisplayName;
@end
@interface MessageData : NSObject
- (id)initWithMsgType:(long long)arg1;
@property(retain, nonatomic) NSString *fromUsrName;
@property(retain, nonatomic) NSString *toUsrName;
@property(retain, nonatomic) NSString *msgContent;
@property(retain, nonatomic) NSString *msgPushContent;
@property(nonatomic) int messageType;
@property(nonatomic) int msgStatus;
@property(nonatomic) int msgCreateTime;
@property(nonatomic) int mesLocalID;
@property(nonatomic) long long mesSvrID;
@property(retain, nonatomic) NSString *msgVoiceText;
@property(copy, nonatomic) NSString *m_nsEmoticonMD5;
- (BOOL)isChatRoomMessage;
- (id)groupChatSenderDisplayName;
- (id)getRealMessageContent;
- (BOOL)isSendFromSelf;
- (BOOL)isCustomEmojiMsg;
- (BOOL)isImgMsg;
- (BOOL)isVideoMsg;
- (BOOL)isVoiceMsg;
@end
@interface CUtility : NSObject
+ (BOOL)HasWechatInstance;
+ (unsigned long long)getFreeDiskSpace;
+ (void)ReloadSessionForMsgSync;
+ (id)GetCurrentUserName;
@end
@interface MMSessionInfo : NSObject
@property(nonatomic) BOOL m_bIsTop; // @synthesize m_bIsTop;
@property(nonatomic) BOOL m_bShowUnReadAsRedDot;
@property(nonatomic) BOOL m_isMentionedUnread; // @synthesize
@property(retain, nonatomic) NSString *m_nsUserName; // @synthesize m_nsUserName;
@property(retain, nonatomic) WCContactData *m_contact;
@end
@protocol MMChatsTableCellViewDelegate <NSObject>
@optional
- (void)cellViewReloadData:(MMSessionInfo *)arg1;
@end
@interface MMChatsTableCellView : NSTableCellView
@property(nonatomic) __weak id <MMChatsTableCellViewDelegate> delegate;
@property(retain, nonatomic) MMSessionInfo *sessionInfo;
- (void)menuWillOpen:(id)arg1;
- (void)contextMenuSticky:(id)arg1;
- (void)contextMenuDelete:(id)arg1;
- (void)tableView:(NSTableView *)arg1 rowGotMouseDown:(long long)arg2;
@end
@interface MMSessionMgr : NSObject
@property(retain, nonatomic) NSMutableArray *m_arrSession;
- (id)GetSessionAtIndex:(unsigned long long)arg1;
- (void)MuteSessionByUserName:(id)arg1;
//- (void)TopSessionByUserName:(id)arg1;
- (void)UnmuteSessionByUserName:(id)arg1;
- (void)UntopSessionByUserName:(id)arg1;
- (void)deleteSessionWithoutSyncToServerWithUserName:(id)arg1;
- (void)sortSessions;
- (id)getContact:(id)arg1;
@end
@interface LogoutCGI : NSTableCellView
- (void)sendLogoutCGIWithCompletion:(id)arg1;
@end
@interface MMNotificationService : NSObject
- (id)getNotificationContentWithMsgData:(id)arg1;
- (void)userNotificationCenter:(id)arg1 didActivateNotification:(id)arg2;
@end
@interface MMChatMessageViewController : NSViewController
@end
@interface MMMessageTableItem : NSObject
@property(retain, nonatomic) MessageData *message;
@end
@interface MMStickerMessageCellView : NSObject
@property(retain, nonatomic) MMMessageTableItem *messageTableItem;
@property(nonatomic) MMChatMessageViewController *delegate;
- (BOOL)allowCopy;
- (void)contextMenuCopy;
- (id)contextMenu;
@end
@interface EmoticonMgr : NSObject
@property(retain, nonatomic) MessageData *message;
- (id)getEmotionDataWithMD5:(id)arg1;
@end
@interface MMComplexContactSearchTaskMgr : NSObject
+ (id)sharedInstance;
- (void)doComplexContactSearch:(id)arg1 searchScene:(unsigned long long)arg2 complete:(void (^)(NSArray *, NSArray *, NSArray *))arg3 cancelable:(BOOL)arg4;
@end
@interface MMComplexContactSearchResult : NSObject
@property(retain, nonatomic) NSString *fieldValue;
@property(retain, nonatomic) WCContactData *contact;
@property(nonatomic) unsigned long long fieldType; // 1:备注 3:昵称 4:微信号 8:省份 7:市 9:国家
@end
@interface MMComplexGroupContactMembersSearchResult : NSObject
@property(retain, nonatomic) NSMutableArray<MMComplexContactSearchResult *> *membersSearchReults;
@end
@interface MMComplexGroupContactSearchResult : NSObject
@property(nonatomic) unsigned long long searchType; // 1 名称 2 群成员名称
@property(retain) WCContactData *groupContact;
@property(retain, nonatomic) MMComplexGroupContactMembersSearchResult *groupMembersResult;
@end
@interface MMAvatarService : NSObject
- (NSString *)avatarCachePath;
@end
@interface NSString (MD5)
- (id)md5String;
@end
================================================
FILE: Other/Products/Debug/WeChatPlugin.framework/Versions/A/Modules/module.modulemap
================================================
framework module WeChatPlugin {
umbrella header "WeChatPlugin.h"
export *
module * { export * }
}
================================================
FILE: Other/Products/Debug/WeChatPlugin.framework/Versions/A/Resources/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>17C88</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>WeChatPlugin</string>
<key>CFBundleIdentifier</key>
<string>tk.WeChatPlugin</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>WeChatPlugin</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.6</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string>9C40b</string>
<key>DTPlatformVersion</key>
<string>GM</string>
<key>DTSDKBuild</key>
<string>17C76</string>
<key>DTSDKName</key>
<string>macosx10.13</string>
<key>DTXcode</key>
<string>0920</string>
<key>DTXcodeBuild</key>
<string>9C40b</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2017年 tk. All rights reserved.</string>
<key>versionInfo</key>
<string>👉 v1.6 (2018-03-18):\n新增 Alfred 快捷发消息 & 打开聊天窗口\n\n之前版本更新内容:\n👉 (v1.5.1) :\n👉🏻 新增语音远程控制 mac\n👉🏼 修复快捷回复后消息未读数未更新的问题\n👉🏽 优化防撤回提醒 (显示撤回人昵称 & 消息类型) \n\n👉 (v1.5.0):\n👉🏻 新增自动登录开关 \n👉🏼 新增小助手检测更新 \n👉🏽 新增通知中心快捷回复 \n👉🏾 新增表情包复制 & 存储 \n👉🏿 自动回复 & 远程控制设置存储到本地 \n\n👉🏻 优化防撤回提醒。 \n👉🏼 新增自动登录开关。 \n👉🏽 新增表情复制 & 存储。 \n👉🏾 新增通知中心快捷回复。\n👉🏿 自动回复 & 远程控制设置存储到本地。\n\n
</string>
</dict>
</plist>
================================================
FILE: Other/Products/Debug/WeChatPlugin.framework/Versions/A/Resources/TKRemoteControlCommands.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<array>
<dict>
<key>executeCommand</key>
<string>open /System/Library/Frameworks/ScreenSaver.framework/Versions/A/Resources/ScreenSaverEngine.app || open /System/Library/CoreServices/ScreenSaverEngine.app </string>
<key>keyword</key>
<string>ScreenSave</string>
<key>function</key>
<string>屏幕保护</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>/System/Library/CoreServices/Menu\ Extras/User.menu/Contents/Resources/CGSession -suspend</string>
<key>keyword</key>
<string>LockScreen</string>
<key>function</key>
<string>锁屏</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>sleep</string>
<key>keyword</key>
<string>Sleep</string>
<key>function</key>
<string>休眠</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>shutdown</string>
<key>keyword</key>
<string>Shutdown</string>
<key>function</key>
<string>关机</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>restart</string>
<key>keyword</key>
<string>Restart</string>
<key>function</key>
<string>重启</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>empty</string>
<key>keyword</key>
<string>EmptyTrash</string>
<key>function</key>
<string>清空废纸篓</string>
<key>enable</key>
<true/>
</dict>
</array>
<array>
<dict>
<key>executeCommand</key>
<string>killQQ</string>
<key>keyword</key>
<string>KillQQ</string>
<key>function</key>
<string>退出 QQ</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>killWeChat</string>
<key>keyword</key>
<string>KillWeChat</string>
<key>function</key>
<string>退出 WeChat</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>killChrome</string>
<key>keyword</key>
<string>KillChrome</string>
<key>function</key>
<string>退出 Chrome</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>killSafari</string>
<key>keyword</key>
<string>KillSafari</string>
<key>function</key>
<string>退出 Safari</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>killAll</string>
<key>keyword</key>
<string>KillAll</string>
<key>function</key>
<string>退出所有程序</string>
<key>enable</key>
<true/>
</dict>
</array>
<array>
<dict>
<key>executeCommand</key>
<string>musicToggle</string>
<key>keyword</key>
<string>Toggle</string>
<key>function</key>
<string>播放/暂停</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicNext</string>
<key>keyword</key>
<string>Next</string>
<key>function</key>
<string>下一首</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicPrevious</string>
<key>keyword</key>
<string>Previous</string>
<key>function</key>
<string>上一首</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicVolumeUp</string>
<key>keyword</key>
<string>VolumeUp</string>
<key>function</key>
<string>增大音量</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicVolumeDown</string>
<key>keyword</key>
<string>VolumeDown</string>
<key>function</key>
<string>减小音量</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicLike</string>
<key>keyword</key>
<string>LikeChange</string>
<key>function</key>
<string>喜欢/取消喜欢</string>
<key>enable</key>
<true/>
</dict>
</array>
</array>
</plist>
================================================
FILE: Other/Products/Debug/WeChatPlugin.framework/Versions/Current/Headers/WeChatPlugin.h
================================================
//
// WeChatPlugin.h
// WeChatPlugin
//
// Created by TK on 2017/4/19.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "TKWeChatPluginConfig.h"
#import "TKHelper.h"
FOUNDATION_EXPORT double WeChatPluginVersionNumber;
FOUNDATION_EXPORT const unsigned char WeChatPluginVersionString[];
#pragma mark - 微信原始的部分类与方法
@interface MMLoginOneClickViewController : NSViewController
@property(nonatomic) NSTextField *descriptionLabel;
- (void)onLoginButtonClicked:(id)arg1;
@property(nonatomic) NSButton *loginButton;
@end
@interface AccountService : NSObject
- (id)GetLastLoginUserName;
- (id)GetLastLoginAutoAuthKey;
- (BOOL)canAutoAuth;
- (void)AutoAuth;
- (void)ManualLogin:(id)arg1 withPassword:(id)arg2;
- (void)ManualLogout;
- (void)QRCodeLoginWithUserName:(id)arg1 password:(id)arg2;
@end
@interface MMLoginViewController : NSObject
@property(retain, nonatomic) MMLoginOneClickViewController *oneClickViewController;
@end
@interface MMMainWindowController : NSWindowController
@property(retain, nonatomic) MMLoginViewController *loginViewController;
- (void)onAuthOK;
- (void)onLogOut;
@end
@interface MessageService : NSObject
- (void)onRevokeMsg:(id)arg1;
- (void)OnSyncBatchAddMsgs:(NSArray *)arg1 isFirstSync:(BOOL)arg2;
- (id)SendTextMessage:(id)arg1 toUsrName:(id)arg2 msgText:(id)arg3 atUserList:(id)arg4;
- (id)GetMsgData:(id)arg1 svrId:(long)arg2;
- (void)AddLocalMsg:(id)arg1 msgData:(id)arg2;
- (void)TranscribeVoiceMessage:(id)arg1 completion:(void (^)(void))arg2;
- (BOOL)ClearUnRead:(id)arg1 FromID:(unsigned int)arg2 ToID:(unsigned int)arg3;
@end
@interface MMServiceCenter : NSObject
+ (id)defaultCenter;
- (id)getService:(Class)arg1;
@end
@interface SKBuiltinString_t : NSObject
@property(retain, nonatomic, setter=SetString:) NSString *string; // @synthesize string;
@end
@interface AddMsg : NSObject
@property(retain, nonatomic, setter=SetContent:) SKBuiltinString_t *content; // @synthesize content;
@property(retain, nonatomic, setter=SetFromUserName:) SKBuiltinString_t *fromUserName; // @synthesize fromUserName;
@property(nonatomic, setter=SetMsgType:) int msgType; // @synthesize msgType;
@property(retain, nonatomic, setter=SetToUserName:) SKBuiltinString_t *toUserName; // @synthesize toUserName;
@property (nonatomic, assign) unsigned int createTime;
@property(nonatomic, setter=SetNewMsgId:) long long newMsgId;
@end
@interface MMChatsViewController : NSViewController <NSTableViewDataSource, NSTableViewDelegate>
@property(nonatomic) __weak NSTableView *tableView;
@end
@interface WeChat : NSObject
+ (id)sharedInstance;
@property(nonatomic) MMChatsViewController *chatsViewController;
@property(retain, nonatomic) MMMainWindowController *mainWindowController;
@property(nonatomic) BOOL isAppTerminating;
- (void)startANewChatWithContact:(id)arg1;
- (void)onAuthOK:(BOOL)arg1;
@end
@interface ContactStorage : NSObject
- (id)GetSelfContact;
- (id)GetContact:(id)arg1;
@end
@interface WCContactData : NSObject
@property(retain, nonatomic) NSString *m_nsUsrName; // @synthesize m_nsUsrName;
@property(nonatomic) unsigned int m_uiFriendScene; // @synthesize m_uiFriendScene;
@property(retain, nonatomic) NSString *m_nsNickName; // 用户昵称
@property(retain, nonatomic) NSString *m_nsRemark; // 备注
@property(retain, nonatomic) NSString *m_nsHeadImgUrl; // 头像
- (BOOL)isBrandContact;
- (BOOL)isSelf;
- (id)getGroupDisplayName;
@end
@interface MessageData : NSObject
- (id)initWithMsgType:(long long)arg1;
@property(retain, nonatomic) NSString *fromUsrName;
@property(retain, nonatomic) NSString *toUsrName;
@property(retain, nonatomic) NSString *msgContent;
@property(retain, nonatomic) NSString *msgPushContent;
@property(nonatomic) int messageType;
@property(nonatomic) int msgStatus;
@property(nonatomic) int msgCreateTime;
@property(nonatomic) int mesLocalID;
@property(nonatomic) long long mesSvrID;
@property(retain, nonatomic) NSString *msgVoiceText;
@property(copy, nonatomic) NSString *m_nsEmoticonMD5;
- (BOOL)isChatRoomMessage;
- (id)groupChatSenderDisplayName;
- (id)getRealMessageContent;
- (BOOL)isSendFromSelf;
- (BOOL)isCustomEmojiMsg;
- (BOOL)isImgMsg;
- (BOOL)isVideoMsg;
- (BOOL)isVoiceMsg;
@end
@interface CUtility : NSObject
+ (BOOL)HasWechatInstance;
+ (unsigned long long)getFreeDiskSpace;
+ (void)ReloadSessionForMsgSync;
+ (id)GetCurrentUserName;
@end
@interface MMSessionInfo : NSObject
@property(nonatomic) BOOL m_bIsTop; // @synthesize m_bIsTop;
@property(nonatomic) BOOL m_bShowUnReadAsRedDot;
@property(nonatomic) BOOL m_isMentionedUnread; // @synthesize
@property(retain, nonatomic) NSString *m_nsUserName; // @synthesize m_nsUserName;
@property(retain, nonatomic) WCContactData *m_contact;
@end
@protocol MMChatsTableCellViewDelegate <NSObject>
@optional
- (void)cellViewReloadData:(MMSessionInfo *)arg1;
@end
@interface MMChatsTableCellView : NSTableCellView
@property(nonatomic) __weak id <MMChatsTableCellViewDelegate> delegate;
@property(retain, nonatomic) MMSessionInfo *sessionInfo;
- (void)menuWillOpen:(id)arg1;
- (void)contextMenuSticky:(id)arg1;
- (void)contextMenuDelete:(id)arg1;
- (void)tableView:(NSTableView *)arg1 rowGotMouseDown:(long long)arg2;
@end
@interface MMSessionMgr : NSObject
@property(retain, nonatomic) NSMutableArray *m_arrSession;
- (id)GetSessionAtIndex:(unsigned long long)arg1;
- (void)MuteSessionByUserName:(id)arg1;
//- (void)TopSessionByUserName:(id)arg1;
- (void)UnmuteSessionByUserName:(id)arg1;
- (void)UntopSessionByUserName:(id)arg1;
- (void)deleteSessionWithoutSyncToServerWithUserName:(id)arg1;
- (void)sortSessions;
- (id)getContact:(id)arg1;
@end
@interface LogoutCGI : NSTableCellView
- (void)sendLogoutCGIWithCompletion:(id)arg1;
@end
@interface MMNotificationService : NSObject
- (id)getNotificationContentWithMsgData:(id)arg1;
- (void)userNotificationCenter:(id)arg1 didActivateNotification:(id)arg2;
@end
@interface MMChatMessageViewController : NSViewController
@end
@interface MMMessageTableItem : NSObject
@property(retain, nonatomic) MessageData *message;
@end
@interface MMStickerMessageCellView : NSObject
@property(retain, nonatomic) MMMessageTableItem *messageTableItem;
@property(nonatomic) MMChatMessageViewController *delegate;
- (BOOL)allowCopy;
- (void)contextMenuCopy;
- (id)contextMenu;
@end
@interface EmoticonMgr : NSObject
@property(retain, nonatomic) MessageData *message;
- (id)getEmotionDataWithMD5:(id)arg1;
@end
@interface MMComplexContactSearchTaskMgr : NSObject
+ (id)sharedInstance;
- (void)doComplexContactSearch:(id)arg1 searchScene:(unsigned long long)arg2 complete:(void (^)(NSArray *, NSArray *, NSArray *))arg3 cancelable:(BOOL)arg4;
@end
@interface MMComplexContactSearchResult : NSObject
@property(retain, nonatomic) NSString *fieldValue;
@property(retain, nonatomic) WCContactData *contact;
@property(nonatomic) unsigned long long fieldType; // 1:备注 3:昵称 4:微信号 8:省份 7:市 9:国家
@end
@interface MMComplexGroupContactMembersSearchResult : NSObject
@property(retain, nonatomic) NSMutableArray<MMComplexContactSearchResult *> *membersSearchReults;
@end
@interface MMComplexGroupContactSearchResult : NSObject
@property(nonatomic) unsigned long long searchType; // 1 名称 2 群成员名称
@property(retain) WCContactData *groupContact;
@property(retain, nonatomic) MMComplexGroupContactMembersSearchResult *groupMembersResult;
@end
@interface MMAvatarService : NSObject
- (NSString *)avatarCachePath;
@end
@interface NSString (MD5)
- (id)md5String;
@end
================================================
FILE: Other/Products/Debug/WeChatPlugin.framework/Versions/Current/Modules/module.modulemap
================================================
framework module WeChatPlugin {
umbrella header "WeChatPlugin.h"
export *
module * { export * }
}
================================================
FILE: Other/Products/Debug/WeChatPlugin.framework/Versions/Current/Resources/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>17C88</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>WeChatPlugin</string>
<key>CFBundleIdentifier</key>
<string>tk.WeChatPlugin</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>WeChatPlugin</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.6</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string>9C40b</string>
<key>DTPlatformVersion</key>
<string>GM</string>
<key>DTSDKBuild</key>
<string>17C76</string>
<key>DTSDKName</key>
<string>macosx10.13</string>
<key>DTXcode</key>
<string>0920</string>
<key>DTXcodeBuild</key>
<string>9C40b</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2017年 tk. All rights reserved.</string>
<key>versionInfo</key>
<string>👉 v1.6 (2018-03-18):\n新增 Alfred 快捷发消息 & 打开聊天窗口\n\n之前版本更新内容:\n👉 (v1.5.1) :\n👉🏻 新增语音远程控制 mac\n👉🏼 修复快捷回复后消息未读数未更新的问题\n👉🏽 优化防撤回提醒 (显示撤回人昵称 & 消息类型) \n\n👉 (v1.5.0):\n👉🏻 新增自动登录开关 \n👉🏼 新增小助手检测更新 \n👉🏽 新增通知中心快捷回复 \n👉🏾 新增表情包复制 & 存储 \n👉🏿 自动回复 & 远程控制设置存储到本地 \n\n👉🏻 优化防撤回提醒。 \n👉🏼 新增自动登录开关。 \n👉🏽 新增表情复制 & 存储。 \n👉🏾 新增通知中心快捷回复。\n👉🏿 自动回复 & 远程控制设置存储到本地。\n\n
</string>
</dict>
</plist>
================================================
FILE: Other/Products/Debug/WeChatPlugin.framework/Versions/Current/Resources/TKRemoteControlCommands.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<array>
<dict>
<key>executeCommand</key>
<string>open /System/Library/Frameworks/ScreenSaver.framework/Versions/A/Resources/ScreenSaverEngine.app || open /System/Library/CoreServices/ScreenSaverEngine.app </string>
<key>keyword</key>
<string>ScreenSave</string>
<key>function</key>
<string>屏幕保护</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>/System/Library/CoreServices/Menu\ Extras/User.menu/Contents/Resources/CGSession -suspend</string>
<key>keyword</key>
<string>LockScreen</string>
<key>function</key>
<string>锁屏</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>sleep</string>
<key>keyword</key>
<string>Sleep</string>
<key>function</key>
<string>休眠</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>shutdown</string>
<key>keyword</key>
<string>Shutdown</string>
<key>function</key>
<string>关机</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>restart</string>
<key>keyword</key>
<string>Restart</string>
<key>function</key>
<string>重启</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>empty</string>
<key>keyword</key>
<string>EmptyTrash</string>
<key>function</key>
<string>清空废纸篓</string>
<key>enable</key>
<true/>
</dict>
</array>
<array>
<dict>
<key>executeCommand</key>
<string>killQQ</string>
<key>keyword</key>
<string>KillQQ</string>
<key>function</key>
<string>退出 QQ</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>killWeChat</string>
<key>keyword</key>
<string>KillWeChat</string>
<key>function</key>
<string>退出 WeChat</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>killChrome</string>
<key>keyword</key>
<string>KillChrome</string>
<key>function</key>
<string>退出 Chrome</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>killSafari</string>
<key>keyword</key>
<string>KillSafari</string>
<key>function</key>
<string>退出 Safari</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>killAll</string>
<key>keyword</key>
<string>KillAll</string>
<key>function</key>
<string>退出所有程序</string>
<key>enable</key>
<true/>
</dict>
</array>
<array>
<dict>
<key>executeCommand</key>
<string>musicToggle</string>
<key>keyword</key>
<string>Toggle</string>
<key>function</key>
<string>播放/暂停</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicNext</string>
<key>keyword</key>
<string>Next</string>
<key>function</key>
<string>下一首</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicPrevious</string>
<key>keyword</key>
<string>Previous</string>
<key>function</key>
<string>上一首</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicVolumeUp</string>
<key>keyword</key>
<string>VolumeUp</string>
<key>function</key>
<string>增大音量</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicVolumeDown</string>
<key>keyword</key>
<string>VolumeDown</string>
<key>function</key>
<string>减小音量</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicLike</string>
<key>keyword</key>
<string>LikeChange</string>
<key>function</key>
<string>喜欢/取消喜欢</string>
<key>enable</key>
<true/>
</dict>
</array>
</array>
</plist>
================================================
FILE: Other/Uninstall.sh
================================================
# !/bin/bash
app_name="WeChat"
framework_name="WeChatPlugin"
app_bundle_path="/Applications/${app_name}.app/Contents/MacOS"
app_executable_path="${app_bundle_path}/${app_name}"
app_executable_backup_path="${app_executable_path}_backup"
framework_path="${app_bundle_path}/${framework_name}.framework"
# 备份WeChat原始可执行文件
if [ -f "$app_executable_backup_path" ]
then
rm "$app_executable_path"
rm -rf "$framework_path"
mv "$app_executable_backup_path" "$app_executable_path"
echo "\n\t卸载成功"
else
echo "\n\t未发现微信小助手"
fi
================================================
FILE: Podfile
================================================
platform :osx, '10.12'
inhibit_all_warnings!
target 'WeChatPlugin' do
pod 'GCDWebServer', '~> 3.4.2'
end
================================================
FILE: README.md
================================================
## WeChatPlugin-MacOS
 [](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases)  [](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/blob/master/LICENSE)
微信小助手 v1.6

**相对应的 alfred workflow 请戳→_→ [wechat-alfred-workflow](https://github.com/TKkk-iOSer/wechat-alfred-workflow)**
**iOS版本请戳→_→ [WeChatPlugin-iOS](https://github.com/TKkk-iOSer/WeChatPlugin-iOS)**
~~如何制作 mac OS 插件,请参考[我的博客](http://www.tkkk.fun/2017/04/21/macOS%E9%80%86%E5%90%91-%E5%BE%AE%E4%BF%A1%E5%B0%8F%E5%8A%A9%E6%89%8B/)~~
---
### 更新日志
* [新增 Alfred 快捷发送消息 & 打开聊天窗口(2018-03-18)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.6)
* [新增语音远程控制mac & 优化撤回消息、快捷回复(2018-03-03)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.5.1)
* [新增小助手检测更新&表情包复制存储等等 (2018-02-24)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.5.0)
* [新增窗口置顶&多选删除等等 (2017-10-11)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.4.0)
* [新增置底&免认证 (2017-09-17)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.3.0)
* [修复聊天记录消失的bug (2017-09-11)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.2.0)
* [重构自动回复,实现多回复 (2017-08-23)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.1.0)
**详细内容请查看**[CHANGELOG](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/blob/master/CHANGELOG.md)
---
### 功能
* 消息自动回复
* 消息防撤回
* 远程控制(已支持语音)
* 微信多开
* 第二次登录免认证
* 聊天置底功能(~~类似置顶~~)
* 微信窗口置顶
* 会话多选删除
* 自动登录开关
* 通知中心快捷回复
* 聊天窗口表情包复制 & 存储
* 小助手检测更新提醒
* alfred 快捷发送消息 & 打开窗口 [wechat-alfred-workflow](https://github.com/TKkk-iOSer/wechat-alfred-workflow)
远程控制:
>远程控制新增指令发送成功回调、发送`获取指令`获得当前所有远程控制信息。
- [x] 屏幕保护
- [x] 清空废纸篓
- [x] 锁屏、休眠、关机、重启
- [x] 退出QQ、WeChat、Chrome、Safari、所有程序
- [x] 网易云音乐(播放、暂停、下一首、上一首、喜欢、取消喜欢)
**若想使用远程控制网易云音乐,请在“系统偏好设置 ==> 安全性与隐私 ==> 隐私 ==> 辅助功能”中添加微信**
---
### TODO
- [x] 增加`Alfred`搜索
- [ ] 查看单向好友
- [ ] 语音(视频转发)
- [ ] 增加 brew 安装方式
- [ ] 完善自动回复(指定好友回复、图灵机器人)
- [x] 完善消息防撤回(显示撤回用户昵称)
- [x] ~~清除微信缓存(官方已加)~~
- [x] 优化小助手设置(更新后保留相关设置,更新提醒)
- [x] 语音远程控制 mac
- [ ] 群聊屏蔽某成员
---
### Demo 演示
* 消息防撤回

* 自动回复

* 微信多开

* 远程控制 (测试关闭Chrome、QQ、开启屏幕保护)

* 免认证 & 置底 & 多选删除

* 通知中心快捷回复

* 聊天窗口表情复制 & 存储
<img src="./Other/ScreenShots/emotion_copy_export.png" height="400" hspace="50" />
* 语音远程控制 mac

* Alfred 快速搜索 [wechat-alfred-workflow](https://github.com/TKkk-iOSer/wechat-alfred-workflow)

---
### 安装
~~第一次安装需要输入密码,仅是为了获取写入微信文件夹的权限~~
**0. 懒癌版安装(适合非程序猿)**
打开`应用程序-实用工具-Terminal(终端)`,执行以下命令并根据提示输入密码即可。**(需要git支持)**
`cd ~/Downloads && git clone https://github.com/TKkk-iOSer/WeChatPlugin-MacOS.git --depth=1 && ./WeChatPlugin-MacOS/Other/Install.sh`
**1. 普通安装**
* 点击`clone or download`按钮下载 WeChatPlugin 并解压,打开Terminal(终端),拖动解压后`Install.sh` 文件(在 Other 文件夹中)到 Terminal 回车即可。
**2. 若想修改源码&重编译**
* 先更改微信的 owner 以获取写入微信文件夹的权限,否则会出现类似**Permission denied**的错误。
`sudo chown -R $(whoami) /Applications/WeChat.app`

* 下载 WeChatPlugin, 进行`Pod install`,用Xcode打开,先进行 Build (`command + B`),之后 Run (`command + R`)即可启动微信,此时插件注入完成。
* 若 Error,提示找不到 Framework,先进行 Build。
* 若Error, 需要配置环境,请参考[我的博客](http://www.tkkk.fun/2017/04/21/macOS%E9%80%86%E5%90%91-%E5%BE%AE%E4%BF%A1%E5%B0%8F%E5%8A%A9%E6%89%8B/)。
**3. 安装完成**
* 登录微信,在**菜单栏**中看到**微信小助手**即安装成功。

---
### 使用
* 消息防撤回:点击`开启消息防撤回`或者快捷键`command + t`,即可开启、关闭。
* 自动回复:点击`开启自动回复`或者快捷键`conmand + k`,将弹出自动回复设置的窗口,点击红色箭头的按钮设置开关。
>若关键字为 `*`,则任何信息都回复;
>若关键字为`x|y`,则 x 和 y 都回复;
>若关键字**或者**自动回复为空,则不开启该条自动回复。
>若开启正则,请确认正则表达式书写正确,[在线正则表达式测试](http://tool.oschina.net/regex/)

* 微信多开:点击`登录新微信`或者快捷键`command + shift + n`,即可多开微信。
* 远程控制:点击`远程控制 Mac OS`或者快捷键`command + shift + c`,即可打开控制窗口。

①为选择是否开启远程控制此功能。
②为能够触发远程控制的消息内容(仅向自己发送账号有效)。
* 远程控制:发送`获取指令`,手机端可查看所有指令信息。

* Alfred 使用:请查看 [wechat-alfred-workflow](https://github.com/TKkk-iOSer/wechat-alfred-workflow)
---
### 卸载
在`Terminal`(终端)打开该项目,运行 `./Other/Uninstall.sh` 即可.
~~或者在 `/Applications/WeChat.app/Contents/MacOS` 目录中,删除 `WeChat` 与 `WeChatPlugin.framework`,将`WeChat_backup` 重命名为 `WeChat` 即可。~~
---
### 依赖
* [XMLReader](https://github.com/amarcadet/XMLReader)
* [insert_dylib](https://github.com/Tyilo/insert_dylib)
* [fishhook](https://github.com/facebook/fishhook)
* [GCDWebServer](https://github.com/swisspol/GCDWebServer)
---
### Other
若有其他好的想法欢迎 Issue me
---
#### 听说你想请我喝下午茶?😏
<img src="http://upload-images.jianshu.io/upload_images/965383-cbc86dc1d75a6242.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" height="250" hspace="50"/> <img src="http://upload-images.jianshu.io/upload_images/965383-76a1c7c91b987e1a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" height="250" hspace="50" />
================================================
FILE: WeChatPlugin/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.6</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2017年 tk. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
<key>versionInfo</key>
<string>👉 v1.6 (2018-03-18):\n新增 Alfred 快捷发消息 & 打开聊天窗口\n\n之前版本更新内容:\n👉 (v1.5.1) :\n👉🏻 新增语音远程控制 mac\n👉🏼 修复快捷回复后消息未读数未更新的问题\n👉🏽 优化防撤回提醒 (显示撤回人昵称 & 消息类型) \n\n👉 (v1.5.0):\n👉🏻 新增自动登录开关 \n👉🏼 新增小助手检测更新 \n👉🏽 新增通知中心快捷回复 \n👉🏾 新增表情包复制 & 存储 \n👉🏿 自动回复 & 远程控制设置存储到本地 \n\n👉🏻 优化防撤回提醒。 \n👉🏼 新增自动登录开关。 \n👉🏽 新增表情复制 & 存储。 \n👉🏾 新增通知中心快捷回复。\n👉🏿 自动回复 & 远程控制设置存储到本地。\n\n
</string>
</dict>
</plist>
================================================
FILE: WeChatPlugin/Sources/Category/MMChatsTableCellView+hook.h
================================================
//
// MMChatsTableCellView+hook.h
// WeChatPlugin
//
// Created by TK on 2017/9/15.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface NSObject (MMChatsTableCellViewHook)
+ (void)hookMMChatsTableCellView;
@end
================================================
FILE: WeChatPlugin/Sources/Category/MMChatsTableCellView+hook.m
================================================
//
// MMChatsTableCellView+hook.m
// WeChatPlugin
//
// Created by TK on 2017/9/15.
// Copyright © 2017年 tk. All rights reserved.
//
#import "MMChatsTableCellView+hook.h"
#import "WeChatPlugin.h"
#import "TKIgnoreSessonModel.h"
@implementation NSObject (MMChatsTableCellViewHook)
+ (void)hookMMChatsTableCellView {
tk_hookMethod(objc_getClass("MMChatsTableCellView"), @selector(menuWillOpen:), [self class], @selector(hook_menuWillOpen:));
tk_hookMethod(objc_getClass("MMChatsTableCellView"), @selector(setSessionInfo:), [self class], @selector(hook_setSessionInfo:));
tk_hookMethod(objc_getClass("MMChatsTableCellView"), @selector(contextMenuSticky:), [self class], @selector(hook_contextMenuSticky:));
tk_hookMethod(objc_getClass("MMChatsTableCellView"), @selector(contextMenuDelete:), [self class], @selector(hook_contextMenuDelete:));
tk_hookMethod(objc_getClass("MMChatsViewController"), @selector(tableView:rowGotMouseDown:), [self class], @selector(hooktableView:rowGotMouseDown:));
}
- (void)hooktableView:(NSTableView *)arg1 rowGotMouseDown:(long long)arg2 {
[self hooktableView:arg1 rowGotMouseDown:arg2];
if ([[TKWeChatPluginConfig sharedConfig] multipleSelectionEnable]) {
NSMutableArray *selectSessions = [[TKWeChatPluginConfig sharedConfig] selectSessions];
MMSessionMgr *sessionMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MMSessionMgr")];
MMSessionInfo *sessionInfo = [sessionMgr GetSessionAtIndex:arg2];
if ([selectSessions containsObject:sessionInfo]) {
[selectSessions removeObject:sessionInfo];
} else {
[selectSessions addObject:sessionInfo];
}
[arg1 reloadData];
}
}
- (void)hook_setSessionInfo:(MMSessionInfo *)sessionInfo {
[self hook_setSessionInfo:sessionInfo];
MMChatsTableCellView *cellView = (MMChatsTableCellView *)self;
NSString *currentUserName = [objc_getClass("CUtility") GetCurrentUserName];
__block BOOL isIgnore = false;
NSMutableArray *ignoreSessions = [[TKWeChatPluginConfig sharedConfig] ignoreSessionModels];
[ignoreSessions enumerateObjectsUsingBlock:^(TKIgnoreSessonModel *model, NSUInteger idx, BOOL * _Nonnull stop) {
if ([model.userName isEqualToString:sessionInfo.m_nsUserName] && [model.selfContact isEqualToString:currentUserName]) {
isIgnore = true;
*stop = YES;
}
}];
NSMutableArray *selectSessions = [[TKWeChatPluginConfig sharedConfig] selectSessions];
if (isIgnore) {
cellView.layer.backgroundColor = kBG3.CGColor;
} else if ([selectSessions containsObject:sessionInfo]){
cellView.layer.backgroundColor = kBG4.CGColor;
} else {
cellView.layer.backgroundColor = [NSColor clearColor].CGColor;
}
[cellView.layer setNeedsDisplay];
}
- (void)hook_menuWillOpen:(NSMenu *)arg1 {
MMChatsTableCellView *cellView = (MMChatsTableCellView *)self;
MMSessionInfo *sessionInfo = [cellView sessionInfo];
NSString *currentUserName = [objc_getClass("CUtility") GetCurrentUserName];
__block BOOL isIgnore = false;
NSMutableArray *ignoreSessions = [[TKWeChatPluginConfig sharedConfig] ignoreSessionModels];
[ignoreSessions enumerateObjectsUsingBlock:^(TKIgnoreSessonModel *model, NSUInteger idx, BOOL * _Nonnull stop) {
if ([model.userName isEqualToString:sessionInfo.m_nsUserName] && [model.selfContact isEqualToString:currentUserName]) {
isIgnore = true;
*stop = YES;
}
}];
NSString *itemString = isIgnore ? @"取消置底" : @"置底";
NSMenuItem *preventRevokeItem = [[NSMenuItem alloc] initWithTitle:itemString action:@selector(contextMenuStickyBottom) keyEquivalent:@""];
BOOL multipleSelectionEnable = [[TKWeChatPluginConfig sharedConfig] multipleSelectionEnable];
NSString *multipleSelectionString = multipleSelectionEnable ? @"取消多选" : @"多选";
NSMenuItem *multipleSelectionItem = [[NSMenuItem alloc] initWithTitle:multipleSelectionString action:@selector(contextMenuMutipleSelection) keyEquivalent:@""];
[arg1 insertItem:preventRevokeItem atIndex:1];
[arg1 addItem:multipleSelectionItem];
[self hook_menuWillOpen:arg1];
}
- (void)contextMenuStickyBottom {
MMChatsTableCellView *cellView = (MMChatsTableCellView *)self;
MMSessionInfo *sessionInfo = [cellView sessionInfo];
NSString *currentUserName = [objc_getClass("CUtility") GetCurrentUserName];
NSMutableArray *ignoreSessions = [[TKWeChatPluginConfig sharedConfig] ignoreSessionModels];
__block NSInteger index = -1;
[ignoreSessions enumerateObjectsUsingBlock:^(TKIgnoreSessonModel *model, NSUInteger idx, BOOL * _Nonnull stop) {
if ([model.userName isEqualToString:sessionInfo.m_nsUserName] && [model.selfContact isEqualToString:currentUserName]) {
index = idx;
*stop = YES;
}
}];
MMSessionMgr *sessionMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MMSessionMgr")];
if (index == -1) {
TKIgnoreSessonModel *model = [[TKIgnoreSessonModel alloc] init];
model.userName = sessionInfo.m_nsUserName;
model.selfContact = currentUserName;
model.ignore = true;
[ignoreSessions addObject:model];
if (!sessionInfo.m_bShowUnReadAsRedDot) {
[sessionMgr MuteSessionByUserName:sessionInfo.m_nsUserName];
}
if (sessionInfo.m_bIsTop) {
[sessionMgr UntopSessionByUserName:sessionInfo.m_nsUserName];
}
} else {
[ignoreSessions removeObjectAtIndex:index];
if (sessionInfo.m_bShowUnReadAsRedDot) {
[sessionMgr UnmuteSessionByUserName:sessionInfo.m_nsUserName];
}
}
[sessionMgr sortSessions];
[[TKWeChatPluginConfig sharedConfig] saveIgnoreSessionModels];
}
- (void)contextMenuMutipleSelection {
BOOL multipleSelectionEnable = [[TKWeChatPluginConfig sharedConfig] multipleSelectionEnable];
if (multipleSelectionEnable) {
[[[TKWeChatPluginConfig sharedConfig] selectSessions] removeAllObjects];
WeChat *wechat = [objc_getClass("WeChat") sharedInstance];
[wechat.chatsViewController.tableView reloadData];
}
[[TKWeChatPluginConfig sharedConfig] setMultipleSelectionEnable:!multipleSelectionEnable];
}
- (void)hook_contextMenuSticky:(id)arg1 {
[self hook_contextMenuSticky:arg1];
MMChatsTableCellView *cellView = (MMChatsTableCellView *)self;
MMSessionInfo *sessionInfo = [cellView sessionInfo];
if (!sessionInfo.m_bIsTop) return;
NSString *currentUserName = [objc_getClass("CUtility") GetCurrentUserName];
NSMutableArray *ignoreSessions = [[TKWeChatPluginConfig sharedConfig] ignoreSessionModels];
__block NSInteger index = -1;
[ignoreSessions enumerateObjectsUsingBlock:^(TKIgnoreSessonModel *model, NSUInteger idx, BOOL * _Nonnull stop) {
if ([model.userName isEqualToString:sessionInfo.m_nsUserName] && [model.selfContact isEqual:currentUserName]) {
index = idx;
*stop = YES;
}
}];
if (index != -1) {
[ignoreSessions removeObjectAtIndex:index];
MMSessionMgr *sessionMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MMSessionMgr")];
if (sessionInfo.m_bShowUnReadAsRedDot) {
[sessionMgr UnmuteSessionByUserName:sessionInfo.m_nsUserName];
}
[sessionMgr sortSessions];
[[TKWeChatPluginConfig sharedConfig] saveIgnoreSessionModels];
}
}
- (void)hook_contextMenuDelete:(id)arg1 {
BOOL multipleSelection = [[TKWeChatPluginConfig sharedConfig] multipleSelectionEnable];
if (multipleSelection) {
MMSessionMgr *sessionMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MMSessionMgr")];
NSMutableArray *selectSessions = [[TKWeChatPluginConfig sharedConfig] selectSessions];
[selectSessions enumerateObjectsUsingBlock:^(MMSessionInfo *sessionInfo, NSUInteger idx, BOOL * _Nonnull stop) {
NSString *sessionUserName = sessionInfo.m_nsUserName;
if (sessionUserName.length != 0) {
[sessionMgr deleteSessionWithoutSyncToServerWithUserName:sessionUserName];
}
}];
WeChat *wechat = [objc_getClass("WeChat") sharedInstance];
[wechat.chatsViewController.tableView reloadData];
} else {
[self hook_contextMenuDelete:arg1];
}
}
@end
================================================
FILE: WeChatPlugin/Sources/Category/MMStickerMessageCellView+hook.h
================================================
//
// MMStickerMessageCellView+hook.m
// WeChatPlugin
//
// Created by TK on 2018/2/23.
// Copyright © 2018年 tk. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface NSObject (MMStickerMessageCellView)
+ (void)hookMMStickerMessageCellView;
@end
================================================
FILE: WeChatPlugin/Sources/Category/MMStickerMessageCellView+hook.m
================================================
//
// MMStickerMessageCellView+hook.m
// WeChatPlugin
//
// Created by TK on 2018/2/23.
// Copyright © 2018年 tk. All rights reserved.
//
#import "MMStickerMessageCellView+hook.h"
#import "WeChatPlugin.h"
@implementation NSObject (MMStickerMessageCellView)
+ (void)hookMMStickerMessageCellView {
tk_hookMethod(objc_getClass("MMStickerMessageCellView"), @selector(allowCopy), [self class], @selector(hook_allowCopy));
tk_hookMethod(objc_getClass("MMStickerMessageCellView"), @selector(contextMenuCopy), [self class], @selector(hook_contextMenuCopy));
tk_hookMethod(objc_getClass("MMStickerMessageCellView"), @selector(contextMenu), [self class], @selector(hook_contextMenu));
}
- (id)hook_contextMenu {
NSMenu *menu = [self hook_contextMenu];
if ([self.className isEqualToString:@"MMStickerMessageCellView"]) {
NSMenuItem *exportItem = [[NSMenuItem alloc] initWithTitle:@"存储…" action:@selector(contextMenuExport) keyEquivalent:@""];
[menu insertItem:exportItem atIndex:2];
}
return menu;
}
- (BOOL)hook_allowCopy {
if ([self.className isEqualToString:@"MMStickerMessageCellView"]) {
return YES;
}
return [self hook_allowCopy];
}
- (void)contextMenuExport {
MMStickerMessageCellView *currentCellView = (MMStickerMessageCellView *)self;
MMMessageTableItem *item = currentCellView.messageTableItem;
EmoticonMgr *emoticonMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("EmoticonMgr")];
NSData *imageData = [emoticonMgr getEmotionDataWithMD5:item.message.m_nsEmoticonMD5];
NSSavePanel *savePanel = ({
NSSavePanel *panel = [NSSavePanel savePanel];
[panel setDirectoryURL:[NSURL fileURLWithPath:[NSHomeDirectory() stringByAppendingPathComponent:@"Pictures"]]];
[panel setNameFieldStringValue:item.message.m_nsEmoticonMD5];
[panel setAllowedFileTypes:@[[NSObject getTypeForImageData:imageData]]];
[panel setAllowsOtherFileTypes:YES];
[panel setExtensionHidden:NO];
[panel setCanCreateDirectories:YES];
panel;
});
[savePanel beginSheetModalForWindow:currentCellView.delegate.view.window completionHandler:^(NSInteger result) {
if (result == NSModalResponseOK) {
[imageData writeToFile:[[savePanel URL] path] atomically:YES];
}
}];
}
- (void)hook_contextMenuCopy {
if ([self.className isEqualToString:@"MMStickerMessageCellView"]) {
MMMessageTableItem *item = [self valueForKey:@"messageTableItem"];
EmoticonMgr *emoticonMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("EmoticonMgr")];
NSData *imageData = [emoticonMgr getEmotionDataWithMD5:item.message.m_nsEmoticonMD5];
NSString *imageType = [NSObject getTypeForImageData:imageData];
NSString *imageName = [NSString stringWithFormat:@"temp_paste_image_%@.%@", item.message.m_nsEmoticonMD5, imageType];
NSString *tempImageFilePath = [NSTemporaryDirectory() stringByAppendingString:imageName];
NSURL *imageUrl = [NSURL fileURLWithPath:tempImageFilePath];
[imageData writeToURL:imageUrl atomically:YES];
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
[pasteboard clearContents];
[pasteboard declareTypes:@[NSFilenamesPboardType] owner:nil];
[pasteboard writeObjects:@[imageUrl]];
} else {
[self hook_contextMenuCopy];
}
}
+ (NSString *)getTypeForImageData:(NSData *)data {
uint8_t c;
[data getBytes:&c length:1];
switch (c) {
case 0x89:
return @"png";
case 0x47:
return @"gif";
default:
return @"jpg";
}
return nil;
}
@end
================================================
FILE: WeChatPlugin/Sources/Category/WeChat+hook.h
================================================
//
// WeChat+hook.h
// WeChatPlugin
//
// Created by TK on 2017/4/19.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface NSObject (WeChatHook)
+ (void)hookWeChat;
@end
================================================
FILE: WeChatPlugin/Sources/Category/WeChat+hook.m
================================================
//
// WeChat+hook.m
// WeChatPlugin
//
// Created by TK on 2017/4/19.
// Copyright © 2017年 tk. All rights reserved.
//
#import "WeChat+hook.h"
#import "WeChatPlugin.h"
#import "XMLReader.h"
#import "TKRemoteControlController.h"
#import "TKAutoReplyWindowController.h"
#import "TKRemoteControlWindowController.h"
#import "TKIgnoreSessonModel.h"
#import "fishhook.h"
#import "TKVersionManager.h"
#import "TKWebServerManager.h"
static char tkAutoReplyWindowControllerKey; // 自动回复窗口的关联 key
static char tkRemoteControlWindowControllerKey; // 远程控制窗口的关联 key
@implementation NSObject (WeChatHook)
+ (void)hookWeChat {
// 微信撤回消息
tk_hookMethod(objc_getClass("MessageService"), @selector(onRevokeMsg:), [self class], @selector(hook_onRevokeMsg:));
// 微信消息同步
tk_hookMethod(objc_getClass("MessageService"), @selector(OnSyncBatchAddMsgs:isFirstSync:), [self class], @selector(hook_OnSyncBatchAddMsgs:isFirstSync:));
// 微信多开
tk_hookClassMethod(objc_getClass("CUtility"), @selector(HasWechatInstance), [self class], @selector(hook_HasWechatInstance));
// 免认证登录
tk_hookMethod(objc_getClass("MMLoginOneClickViewController"), @selector(onLoginButtonClicked:), [self class], @selector(hook_onLoginButtonClicked:));
tk_hookMethod(objc_getClass("LogoutCGI"), @selector(sendLogoutCGIWithCompletion:), [self class], @selector(hook_sendLogoutCGIWithCompletion:));
// 自动登录
tk_hookMethod(objc_getClass("MMLoginOneClickViewController"), @selector(viewWillAppear), [self class], @selector(hook_viewWillAppear));
// 置底
tk_hookMethod(objc_getClass("MMSessionMgr"), @selector(sortSessions), [self class], @selector(hook_sortSessions));
// 窗口置顶
tk_hookMethod(objc_getClass("NSWindow"), @selector(makeKeyAndOrderFront:), [self class], @selector(hook_makeKeyAndOrderFront:));
// 快捷回复
tk_hookMethod(objc_getClass("_NSConcreteUserNotificationCenter"), @selector(deliverNotification:), [self class], @selector(hook_deliverNotification:));
tk_hookMethod(objc_getClass("MMNotificationService"), @selector(userNotificationCenter:didActivateNotification:), [self class], @selector(hook_userNotificationCenter:didActivateNotification:));
tk_hookMethod(objc_getClass("MMNotificationService"), @selector(getNotificationContentWithMsgData:), [self class], @selector(hook_getNotificationContentWithMsgData:));
// 登录逻辑
tk_hookMethod(objc_getClass("WeChat"), @selector(onAuthOK:), [self class], @selector(hook_onAuthOK:));
// 替换沙盒路径
rebind_symbols((struct rebinding[2]) {
{ "NSSearchPathForDirectoriesInDomains", swizzled_NSSearchPathForDirectoriesInDomains, (void *)&original_NSSearchPathForDirectoriesInDomains },
{ "NSHomeDirectory", swizzled_NSHomeDirectory, (void *)&original_NSHomeDirectory }
}, 2);
[self setup];
[self checkPluginVersion];
}
+ (void)setup {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self addAssistantMenuItem];
BOOL onTop = [[TKWeChatPluginConfig sharedConfig] onTop];
WeChat *wechat = [objc_getClass("WeChat") sharedInstance];
wechat.mainWindowController.window.level = onTop == NSControlStateValueOn ? NSNormalWindowLevel+2 : NSNormalWindowLevel;
});
}
+ (void)checkPluginVersion {
if ([[TKWeChatPluginConfig sharedConfig] forbidCheckVersion]) return;
[[TKVersionManager shareManager] checkVersionFinish:^(TKVersionStatus status, NSString *message) {
if (status == TKVersionStatusNew) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSAlert *alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:@"前往Github"];
[alert addButtonWithTitle:@"不再提示"];
[alert addButtonWithTitle:@"取消"];
[alert setMessageText:@"检测到新版本!主要内容:👇"];
[alert setInformativeText:message];
NSModalResponse respose = [alert runModal];
if (respose == NSAlertFirstButtonReturn) {
NSURL *url = [NSURL URLWithString:@"https://github.com/TKkk-iOSer/WeChatPlugin-MacOS"];
[[NSWorkspace sharedWorkspace] openURL:url];
} else if (respose == NSAlertSecondButtonReturn) {
[[TKWeChatPluginConfig sharedConfig] setForbidCheckVersion:YES];
}
});
}
}];
}
/**
菜单栏添加 menuItem
*/
+ (void)addAssistantMenuItem {
// 消息防撤回
NSMenuItem *preventRevokeItem = [[NSMenuItem alloc] initWithTitle:@"开启消息防撤回" action:@selector(onPreventRevoke:) keyEquivalent:@"t"];
preventRevokeItem.state = [[TKWeChatPluginConfig sharedConfig] preventRevokeEnable];
// 自动回复
NSMenuItem *autoReplyItem = [[NSMenuItem alloc] initWithTitle:@"自动回复设置" action:@selector(onAutoReply:) keyEquivalent:@"k"];
// 登录新微信
NSMenuItem *newWeChatItem = [[NSMenuItem alloc] initWithTitle:@"登录新微信" action:@selector(onNewWechatInstance:) keyEquivalent:@"N"];
// 远程控制
NSMenuItem *commandItem = [[NSMenuItem alloc] initWithTitle:@"远程控制mac" action:@selector(onRemoteControl:) keyEquivalent:@"C"];
// 微信窗口置顶
NSMenuItem *onTopItem = [[NSMenuItem alloc] initWithTitle:@"微信窗口置顶" action:@selector(onWechatOnTopControl:) keyEquivalent:@"d"];
onTopItem.state = [[TKWeChatPluginConfig sharedConfig] onTop];
// 免认证登录
NSMenuItem *autoAuthItem = [[NSMenuItem alloc] initWithTitle:@"免认证登录" action:@selector(onAutoAuthControl:) keyEquivalent:@"M"];
autoAuthItem.state = [[TKWeChatPluginConfig sharedConfig] autoAuthEnable];
// 更新小助手
NSMenuItem *updatePluginItem = [[NSMenuItem alloc] initWithTitle:@"更新小助手…" action:@selector(onUpdatePluginControl:) keyEquivalent:@""];
NSMenu *subMenu = [[NSMenu alloc] initWithTitle:@"微信小助手"];
[subMenu addItem:preventRevokeItem];
[subMenu addItem:autoReplyItem];
[subMenu addItem:commandItem];
[subMenu addItem:newWeChatItem];
[subMenu addItem:onTopItem];
[subMenu addItem:autoAuthItem];
[subMenu addItem:updatePluginItem];
NSMenuItem *menuItem = [[NSMenuItem alloc] init];
[menuItem setTitle:@"微信小助手"];
[menuItem setSubmenu:subMenu];
[[[NSApplication sharedApplication] mainMenu] addItem:menuItem];
}
#pragma mark - menuItem 的点击事件
/**
菜单栏-微信小助手-消息防撤回 设置
@param item 消息防撤回的item
*/
- (void)onPreventRevoke:(NSMenuItem *)item {
item.state = !item.state;
[[TKWeChatPluginConfig sharedConfig] setPreventRevokeEnable:item.state];
}
/**
菜单栏-微信小助手-自动回复 设置
@param item 自动回复设置的item
*/
- (void)onAutoReply:(NSMenuItem *)item {
WeChat *wechat = [objc_getClass("WeChat") sharedInstance];
TKAutoReplyWindowController *autoReplyWC = objc_getAssociatedObject(wechat, &tkAutoReplyWindowControllerKey);
if (!autoReplyWC) {
autoReplyWC = [[TKAutoReplyWindowController alloc] initWithWindowNibName:@"TKAutoReplyWindowController"];
objc_setAssociatedObject(wechat, &tkAutoReplyWindowControllerKey, autoReplyWC, OBJC_ASSOCIATION_RETAIN);
}
[autoReplyWC showWindow:autoReplyWC];
[autoReplyWC.window center];
[autoReplyWC.window makeKeyWindow];
}
/**
打开新的微信
@param item 登录新微信的item
*/
- (void)onNewWechatInstance:(NSMenuItem *)item {
[TKRemoteControlController executeShellCommand:@"open -n /Applications/WeChat.app"];
}
/**
菜单栏-帮助-远程控制 MAC OS 设置
@param item 远程控制的item
*/
- (void)onRemoteControl:(NSMenuItem *)item {
WeChat *wechat = [objc_getClass("WeChat") sharedInstance];
TKRemoteControlWindowController *remoteControlWC = objc_getAssociatedObject(wechat, &tkRemoteControlWindowControllerKey);
if (!remoteControlWC) {
remoteControlWC = [[TKRemoteControlWindowController alloc] initWithWindowNibName:@"TKRemoteControlWindowController"];
objc_setAssociatedObject(wechat, &tkRemoteControlWindowControllerKey, remoteControlWC, OBJC_ASSOCIATION_RETAIN);
}
[remoteControlWC showWindow:remoteControlWC];
[remoteControlWC.window center];
[remoteControlWC.window makeKeyWindow];
}
/**
菜单栏-微信小助手-免认证登录 设置
@param item 免认证登录的 item
*/
- (void)onAutoAuthControl:(NSMenuItem *)item {
item.state = !item.state;
[[TKWeChatPluginConfig sharedConfig] setAutoAuthEnable:item.state];
}
/**
菜单栏-微信小助手-微信窗口置顶
@param item 免认证登录的 item
*/
- (void)onWechatOnTopControl:(NSMenuItem *)item {
item.state = !item.state;
[[TKWeChatPluginConfig sharedConfig] setOnTop:item.state];
NSArray *windows = [[NSApplication sharedApplication] windows];
[windows enumerateObjectsUsingBlock:^(NSWindow *window, NSUInteger idx, BOOL * _Nonnull stop) {
if (![window.className isEqualToString:@"NSStatusBarWindow"]) {
window.level = item.state == NSControlStateValueOn ? NSNormalWindowLevel+2 : NSNormalWindowLevel;
}
}];
}
/**
菜单栏-微信小助手-更新小助手
@param item 更新小助手的 item
*/
- (void)onUpdatePluginControl:(NSMenuItem *)item {
[[TKWeChatPluginConfig sharedConfig] setForbidCheckVersion:NO];
[[TKVersionManager shareManager] checkVersionFinish:^(TKVersionStatus status, NSString *message) {
if (status == TKVersionStatusNew) {
NSAlert *alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:@"前往Github"];
[alert addButtonWithTitle:@"取消"];
[alert setMessageText:@"检测到新版本!主要内容:👇"];
[alert setInformativeText:message];
NSModalResponse respose = [alert runModal];
if (respose == NSAlertFirstButtonReturn) {
NSURL *url = [NSURL URLWithString:@"https://github.com/TKkk-iOSer/WeChatPlugin-MacOS"];
[[NSWorkspace sharedWorkspace] openURL:url];
}
} else {
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:@"当前为最新版本!主要内容:👇"];
[alert setInformativeText:message];
[alert runModal];
}
}];
}
/**
登录界面-自动登录
@param btn 自动登录按钮
*/
- (void)selectAutoLogin:(NSButton *)btn {
[[TKWeChatPluginConfig sharedConfig] setAutoLoginEnable:btn.state];
}
#pragma mark - hook 微信方法
/**
hook 微信是否已启动
*/
+ (BOOL)hook_HasWechatInstance {
return NO;
}
/**
hook 微信撤回消息
*/
- (void)hook_onRevokeMsg:(id)msg {
if (![[TKWeChatPluginConfig sharedConfig] preventRevokeEnable]) {
[self hook_onRevokeMsg:msg];
return;
}
if ([msg rangeOfString:@"<sysmsg"].length <= 0) return;
// 转换群聊的 msg
NSString *msgContent = [msg substringFromIndex:[msg rangeOfString:@"<sysmsg"].location];
// xml 转 dict
NSError *error;
NSDictionary *msgDict = [XMLReader dictionaryForXMLString:msgContent error:&error];
if (!error && msgDict && msgDict[@"sysmsg"] && msgDict[@"sysmsg"][@"revokemsg"]) {
NSString *newmsgid = msgDict[@"sysmsg"][@"revokemsg"][@"newmsgid"][@"text"];
NSString *session = msgDict[@"sysmsg"][@"revokemsg"][@"session"][@"text"];
NSMutableSet *revokeMsgSet = [[TKWeChatPluginConfig sharedConfig] revokeMsgSet];
// 该消息已进行过防撤回处理
if ([revokeMsgSet containsObject:newmsgid]) {
return;
}
[revokeMsgSet addObject:newmsgid];
// 获取原始的撤回提示消息
MessageService *msgService = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MessageService")];
MessageData *revokeMsgData = [msgService GetMsgData:session svrId:[newmsgid integerValue]];
NSString *msgContent = [revokeMsgData getRealMessageContent];
NSString *msgType;
if (revokeMsgData.messageType == 1) {
msgType = @"";
} else if ([revokeMsgData isCustomEmojiMsg]) {
msgType = @"[表情]";
} else if ([revokeMsgData isImgMsg]) {
msgType = @"[图片]";
} else if ([revokeMsgData isVideoMsg]) {
msgType = @"[视频]";
} else if ([revokeMsgData isVoiceMsg]) {
msgType = @"[语音]";
} else {
msgType = @"[非文本]";
}
NSString *newMsgContent = [NSString stringWithFormat:@"TK拦截到一条撤回消息: \n%@", msgType];
// 判断是否是自己发起撤回
if ([revokeMsgData isSendFromSelf]) {
if (revokeMsgData.messageType == 1) { // 判断是否为文本消息
newMsgContent = [NSString stringWithFormat:@"你撤回了一条消息:\n %@", msgContent];
} else {
newMsgContent = [NSString stringWithFormat:@"你撤回了一条消息:\n %@", msgType];
}
} else {
NSString *displayName = [revokeMsgData groupChatSenderDisplayName];
if (revokeMsgData.messageType == 1) {
if ([revokeMsgData isChatRoomMessage]) {
newMsgContent = [NSString stringWithFormat:@"TK拦截到一条撤回消息:\n%@ : %@",displayName, msgContent];
} else {
newMsgContent = [NSString stringWithFormat:@"TK拦截到一条撤回消息:\n%@", msgContent];
}
} else {
if ([revokeMsgData isChatRoomMessage]) {
newMsgContent = [NSString stringWithFormat:@"TK拦截到一条撤回信息: \n %@ : %@", displayName, msgType];
}
}
}
MessageData *newMsgData = ({
MessageData *msg = [[objc_getClass("MessageData") alloc] initWithMsgType:0x2710];
[msg setFromUsrName:revokeMsgData.toUsrName];
[msg setToUsrName:revokeMsgData.fromUsrName];
[msg setMsgStatus:4];
[msg setMsgContent:newMsgContent];
[msg setMsgCreateTime:[revokeMsgData msgCreateTime]];
// [msg setMesLocalID:[revokeMsgData mesLocalID]];
msg;
});
[msgService AddLocalMsg:session msgData:newMsgData];
}
}
/**
hook 微信消息同步
*/
- (void)hook_OnSyncBatchAddMsgs:(NSArray *)msgs isFirstSync:(BOOL)arg2 {
[self hook_OnSyncBatchAddMsgs:msgs isFirstSync:arg2];
[msgs enumerateObjectsUsingBlock:^(AddMsg *addMsg, NSUInteger idx, BOOL * _Nonnull stop) {
NSDate *now = [NSDate date];
NSTimeInterval nowSecond = now.timeIntervalSince1970;
if (nowSecond - addMsg.createTime > 180) { // 若是3分钟前的消息,则不进行自动回复与远程控制。
return;
}
[self autoReplyWithMsg:addMsg];
NSString *currentUserName = [objc_getClass("CUtility") GetCurrentUserName];
if ([addMsg.fromUserName.string isEqualToString:currentUserName] &&
[addMsg.toUserName.string isEqualToString:currentUserName]) {
[self remoteControlWithMsg:addMsg];
[self replySelfWithMsg:addMsg];
}
}];
}
/**
hook 微信通知消息
*/
- (id)hook_getNotificationContentWithMsgData:(MessageData *)arg1 {
[[TKWeChatPluginConfig sharedConfig] setCurrentUserName:arg1.toUsrName];
return [self hook_getNotificationContentWithMsgData:arg1];;
}
- (void)hook_deliverNotification:(NSUserNotification *)notification {
NSMutableDictionary *dict = [notification.userInfo mutableCopy];
dict[@"currnetName"] = [[TKWeChatPluginConfig sharedConfig] currentUserName];
notification.userInfo = dict;
notification.hasReplyButton = YES;
[self hook_deliverNotification:notification];
}
- (void)hook_userNotificationCenter:(id)notificationCenter didActivateNotification:(NSUserNotification *)notification {
NSString *chatName = notification.userInfo[@"ChatName"];
if (chatName && notification.response.string) {
NSString *instanceUserName = [objc_getClass("CUtility") GetCurrentUserName];
NSString *currentUserName = notification.userInfo[@"currnetName"];
if ([instanceUserName isEqualToString:currentUserName]) {
MessageService *service = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MessageService")];
[service SendTextMessage:currentUserName toUsrName:chatName msgText:notification.response.string atUserList:nil];
[service ClearUnRead:chatName FromID:0 ToID:0];
}
} else {
[self hook_userNotificationCenter:notificationCenter didActivateNotification:notification];
}
}
/**
hook 自动登录
*/
- (void)hook_onLoginButtonClicked:(NSButton *)btn {
AccountService *accountService = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("AccountService")];
BOOL autoAuthEnable = [[TKWeChatPluginConfig sharedConfig] autoAuthEnable];
if (autoAuthEnable && [accountService canAutoAuth]) {
[accountService AutoAuth];
WeChat *wechat = [objc_getClass("WeChat") sharedInstance];
MMLoginOneClickViewController *loginVC = wechat.mainWindowController.loginViewController.oneClickViewController;
loginVC.loginButton.hidden = YES;
//// [wechat.mainWindowController onAuthOK];
loginVC.descriptionLabel.stringValue = @"TK正在为你免认证登录~";
loginVC.descriptionLabel.textColor = TK_RGB(0x88, 0x88, 0x88);
loginVC.descriptionLabel.hidden = NO;
} else {
[self hook_onLoginButtonClicked:btn];
}
}
- (void)hook_onAuthOK:(BOOL)arg1 {
[self hook_onAuthOK:arg1];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[TKWebServerManager shareManager] startServer];
});
}
- (void)hook_sendLogoutCGIWithCompletion:(id)arg1 {
[[TKWebServerManager shareManager] endServer];
BOOL autoAuthEnable = [[TKWeChatPluginConfig sharedConfig] autoAuthEnable];
WeChat *wechat = [objc_getClass("WeChat") sharedInstance];
if (autoAuthEnable && wechat.isAppTerminating) return;
return [self hook_sendLogoutCGIWithCompletion:arg1];
}
- (void)hook_viewWillAppear {
[self hook_viewWillAppear];
NSButton *autoLoginButton = ({
NSButton *btn = [NSButton tk_checkboxWithTitle:@"" target:self action:@selector(selectAutoLogin:)];
btn.frame = NSMakeRect(110, 60, 80, 30);
NSMutableParagraphStyle *pghStyle = [[NSMutableParagraphStyle alloc] init];
pghStyle.alignment = NSTextAlignmentCenter;
NSDictionary *dicAtt = @{NSForegroundColorAttributeName: kBG4, NSParagraphStyleAttributeName: pghStyle};
btn.attributedTitle = [[NSAttributedString alloc] initWithString:@"自动登录" attributes:dicAtt];
btn;
});
WeChat *wechat = [objc_getClass("WeChat") sharedInstance];
MMLoginOneClickViewController *loginVC = wechat.mainWindowController.loginViewController.oneClickViewController;
[loginVC.view addSubview:autoLoginButton];
BOOL autoLogin = [[TKWeChatPluginConfig sharedConfig] autoLoginEnable];
autoLoginButton.state = autoLogin;
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
NSArray *instances = [NSRunningApplication runningApplicationsWithBundleIdentifier:bundleIdentifier];
BOOL wechatHasRun = instances.count == 1;
if (autoLogin && wechatHasRun) {
[loginVC onLoginButtonClicked:nil];
}
}
- (void)hook_sortSessions {
[self hook_sortSessions];
MMSessionMgr *sessionMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MMSessionMgr")];
NSMutableArray *arrSession = sessionMgr.m_arrSession;
NSMutableArray *ignoreSessions = [[[TKWeChatPluginConfig sharedConfig] ignoreSessionModels] mutableCopy];
NSString *currentUserName = [objc_getClass("CUtility") GetCurrentUserName];
[ignoreSessions enumerateObjectsUsingBlock:^(TKIgnoreSessonModel *model, NSUInteger index, BOOL * _Nonnull stop) {
__block NSInteger ignoreIdx = -1;
[arrSession enumerateObjectsUsingBlock:^(MMSessionInfo *sessionInfo, NSUInteger idx, BOOL * _Nonnull stop) {
if ([model.userName isEqualToString:sessionInfo.m_nsUserName] && [model.selfContact isEqualToString:currentUserName]) {
ignoreIdx = idx;
*stop = YES;
}
}];
if (ignoreIdx != -1) {
MMSessionInfo *sessionInfo = arrSession[ignoreIdx];
[arrSession removeObjectAtIndex:ignoreIdx];
[arrSession addObject:sessionInfo];
}
}];
WeChat *wechat = [objc_getClass("WeChat") sharedInstance];
[wechat.chatsViewController.tableView reloadData];
}
#pragma mark - hook 系统方法
- (void)hook_makeKeyAndOrderFront:(nullable id)sender {
BOOL top = [[TKWeChatPluginConfig sharedConfig] onTop];
((NSWindow *)self).level = top == NSControlStateValueOn ? NSNormalWindowLevel+2 : NSNormalWindowLevel;
[self hook_makeKeyAndOrderFront:sender];
}
#pragma mark - Other
/**
自动回复
@param addMsg 接收的消息
*/
- (void)autoReplyWithMsg:(AddMsg *)addMsg {
if (addMsg.msgType != 1 && addMsg.msgType != 3) return;
MMSessionMgr *sessionMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MMSessionMgr")];
WCContactData *msgContact = [sessionMgr getContact:addMsg.fromUserName.string];
if ([msgContact isBrandContact] || [msgContact isSelf]) {
// 该消息为公众号或者本人发送的消息
return;
}
MessageService *service = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MessageService")];
NSString *currentUserName = [objc_getClass("CUtility") GetCurrentUserName];
NSArray *autoReplyModels = [[TKWeChatPluginConfig sharedConfig] autoReplyModels];
[autoReplyModels enumerateObjectsUsingBlock:^(TKAutoReplyModel *model, NSUInteger idx, BOOL * _Nonnull stop) {
if (!model.enable) return;
if (!model.replyContent || model.replyContent.length == 0) return;
if ([addMsg.fromUserName.string containsString:@"@chatroom"] && !model.enableGroupReply) return;
if (![addMsg.fromUserName.string containsString:@"@chatroom"] && !model.enableSingleReply) return;
NSString *msgContent = addMsg.content.string;
if ([addMsg.fromUserName.string containsString:@"@chatroom"]) {
NSRange range = [msgContent rangeOfString:@":\n"];
if (range.length > 0) {
msgContent = [msgContent substringFromIndex:range.location + range.length];
}
}
NSArray *replyArray = [model.replyContent componentsSeparatedByString:@"|"];
int index = arc4random() % replyArray.count;
NSString *randomReplyContent = replyArray[index];
if (model.enableRegex) {
NSString *regex = model.keyword;
NSError *error;
NSRegularExpression *regular = [NSRegularExpression regularExpressionWithPattern:regex options:NSRegularExpressionCaseInsensitive error:&error];
if (error) return;
NSInteger count = [regular numberOfMatchesInString:msgContent options:NSMatchingReportCompletion range:NSMakeRange(0, msgContent.length)];
if (count > 0) {
[service SendTextMessage:currentUserName toUsrName:addMsg.fromUserName.string msgText:randomReplyContent atUserList:nil];
}
} else {
NSArray * keyWordArray = [model.keyword componentsSeparatedByString:@"|"];
[keyWordArray enumerateObjectsUsingBlock:^(NSString *keyword, NSUInteger idx, BOOL * _Nonnull stop) {
if ([keyword isEqualToString:@"*"] || [msgContent isEqualToString:keyword]) {
[service SendTextMessage:currentUserName toUsrName:addMsg.fromUserName.string msgText:randomReplyContent atUserList:nil];
}
}];
}
}];
}
/**
远程控制
@param addMsg 接收的消息
*/
- (void)remoteControlWithMsg:(AddMsg *)addMsg {
if (addMsg.msgType == 1 || addMsg.msgType == 3) {
[TKRemoteControlController executeRemoteControlCommandWithMsg:addMsg.content.string];
} else if (addMsg.msgType == 34) {
// 此为语音消息
MessageService *msgService = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MessageService")];
MessageData *msgData = [msgService GetMsgData:addMsg.fromUserName.string svrId:addMsg.newMsgId];
long long mesSvrID = msgData.mesSvrID;
NSString *sessionName = msgData.fromUsrName;
[msgService TranscribeVoiceMessage:msgData completion:^ {
MessageData *callbackMsgData = [msgService GetMsgData:sessionName svrId:mesSvrID];
dispatch_async(dispatch_get_main_queue(), ^{
[TKRemoteControlController executeRemoteControlCommandWithVoiceMsg:callbackMsgData.msgVoiceText];
});
}];
}
}
- (void)replySelfWithMsg:(AddMsg *)addMsg {
if (addMsg.msgType != 1 && addMsg.msgType != 3) return;
if ([addMsg.content.string isEqualToString:@"获取指令"]) {
NSString *currentUserName = [objc_getClass("CUtility") GetCurrentUserName];
NSString *callBack = [TKRemoteControlController remoteControlCommandsString];
MessageService *service = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MessageService")];
[service SendTextMessage:currentUserName toUsrName:currentUserName msgText:callBack atUserList:nil];
}
}
#pragma mark - 替换 NSSearchPathForDirectoriesInDomains & NSHomeDirectory
static NSArray<NSString *> *(*original_NSSearchPathForDirectoriesInDomains)(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde);
NSArray<NSString *> *swizzled_NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde) {
NSMutableArray<NSString *> *paths = [original_NSSearchPathForDirectoriesInDomains(directory, domainMask, expandTilde) mutableCopy];
NSString *sandBoxPath = [NSString stringWithFormat:@"%@/Library/Containers/com.tencent.xinWeChat/Data",original_NSHomeDirectory()];
[paths enumerateObjectsUsingBlock:^(NSString *filePath, NSUInteger idx, BOOL * _Nonnull stop) {
NSRange range = [filePath rangeOfString:original_NSHomeDirectory()];
if (range.length > 0) {
NSMutableString *newFilePath = [filePath mutableCopy];
[newFilePath replaceCharactersInRange:range withString:sandBoxPath];
paths[idx] = newFilePath;
}
}];
return paths;
}
static NSString *(*original_NSHomeDirectory)(void);
NSString *swizzled_NSHomeDirectory(void) {
return [NSString stringWithFormat:@"%@/Library/Containers/com.tencent.xinWeChat/Data",original_NSHomeDirectory()];
}
@end
================================================
FILE: WeChatPlugin/Sources/Common/Category/NSButton+Action.h
================================================
//
// NSButton+Action.h
// WeChatPlugin
//
// Created by TK on 2017/9/19.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface NSButton (Action)
+ (instancetype)tk_buttonWithTitle:(NSString *)title target:(id)target action:(SEL)action;
+ (instancetype)tk_checkboxWithTitle:(NSString *)title target:(id)target action:(SEL)action;
@end
================================================
FILE: WeChatPlugin/Sources/Common/Category/NSButton+Action.m
================================================
//
// NSButton+Action.m
// WeChatPlugin
//
// Created by TK on 2017/9/19.
// Copyright © 2017年 tk. All rights reserved.
//
#import "NSButton+Action.h"
@implementation NSButton (Action)
+ (instancetype)tk_checkboxWithTitle:(NSString *)title target:(id)target action:(SEL)action {
NSButton *btn = [NSButton tk_buttonWithTitle:title target:target action:action];
[btn setButtonType:NSButtonTypeSwitch];
return btn;
}
+ (instancetype)tk_buttonWithTitle:(NSString *)title target:(id)target action:(SEL)action {
NSButton *btn = ({
NSButton *btn = [[NSButton alloc] init];
btn.title = title;
btn.target = target;
btn.action = action;
btn;
});
return btn;
}
@end
================================================
FILE: WeChatPlugin/Sources/Common/Category/NSTextField+Action.h
================================================
//
// NSTextField+Action.h
// WeChatPlugin
//
// Created by TK on 2017/9/19.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface NSTextField (Action)
+ (instancetype)tk_labelWithString:(NSString *)stringValue;
@end
================================================
FILE: WeChatPlugin/Sources/Common/Category/NSTextField+Action.m
================================================
//
// NSTextField+Action.m
// WeChatPlugin
//
// Created by TK on 2017/9/19.
// Copyright © 2017年 tk. All rights reserved.
//
#import "NSTextField+Action.h"
@implementation NSTextField (Action)
+ (instancetype)tk_labelWithString:(NSString *)stringValue {
NSTextField *textField = ({
NSTextField *textField = [[NSTextField alloc] initWithFrame:NSMakeRect(10, 10, 200, 17)];
[textField setStringValue:stringValue];
[textField setBezeled:NO];
[textField setDrawsBackground:NO];
[textField setEditable:NO];
[textField setSelectable:NO];
textField;
});
return textField;
}
@end
================================================
FILE: WeChatPlugin/Sources/Common/Category/NSView+Action.h
================================================
//
// NSView+Action.h
// WeChatPlugin
//
// Created by TK on 2017/8/20.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface NSView (Action)
- (void)addSubviews:(NSArray *)subViews;
@end
================================================
FILE: WeChatPlugin/Sources/Common/Category/NSView+Action.m
================================================
//
// NSView+Action.m
// WeChatPlugin
//
// Created by TK on 2017/8/20.
// Copyright © 2017年 tk. All rights reserved.
//
#import "NSView+Action.h"
@implementation NSView (Action)
- (void)addSubviews:(NSArray *)subViews {
for (NSView *v in subViews) {
NSAssert([v isKindOfClass:[NSView class]], @"the elements must be a view!");
[self addSubview:v];
}
}
@end
================================================
FILE: WeChatPlugin/Sources/Common/Color.h
================================================
//
// Color.h
// WeChatPlugin
//
// Created by TK on 2017/8/20.
// Copyright © 2017年 tk. All rights reserved.
//
#ifndef Color_h
#define Color_h
#define TK_RGBA(r, g, b, a) [NSColor colorWithRed:(r) / 255.0 \
green:(g) / 255.0 \
blue:(b) / 255.0 \
alpha:(a)]
#define TK_RGB(r, g, b) TK_RGBA(r, g, b, 1.0)
#define TK_GRAYA(c, a) TK_RGBA(c, c, c, a)
#define TK_GRAY(c) TK_GRAYA(c, 1.0)
#define kBG1 TK_GRAY(0xec)
#define kBG2 TK_GRAY(0xe3)
#define kBG3 TK_GRAYA(0x2a, 0.5)
#define kBG4 TK_GRAYA(0x7a, 0.5)
#endif /* Color_h */
================================================
FILE: WeChatPlugin/Sources/Common/TKPrefixHeader.pch
================================================
//
// TKPrefixHeader.pch
// WeChatPlugin
//
// Created by TK on 2017/8/20.
// Copyright © 2017年 tk. All rights reserved.
//
#ifndef TKPrefixHeader_pch
#define TKPrefixHeader_pch
#ifdef __OBJC__
#import "Color.h"
#import "NSView+Action.h"
#import "NSButton+Action.h"
#import "NSTextField+Action.h"
#endif
#endif /* TKPrefixHeader_pch */
================================================
FILE: WeChatPlugin/Sources/Config/TKWeChatPluginConfig.h
================================================
//
// TKWeChatPluginConfig.h
// WeChatPlugin
//
// Created by TK on 2017/4/19.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface TKWeChatPluginConfig : NSObject
@property (nonatomic, assign) BOOL preventRevokeEnable; /**< 是否开启防撤回 */
@property (nonatomic, assign) BOOL autoAuthEnable; /**< 是否免认证登录 */
@property (nonatomic, assign) BOOL autoLoginEnable; /**< 是否自动登录 */
@property (nonatomic, assign) BOOL onTop; /**< 是否要置顶微信 */
@property (nonatomic, assign) BOOL multipleSelectionEnable; /**< 是否要进行多选 */
@property (nonatomic, assign) BOOL forbidCheckVersion; /**< 禁止检测版本 */
@property (nonatomic, copy) NSMutableArray *autoReplyModels; /**< 自动回复的数组 */
@property (nonatomic, copy) NSMutableArray *remoteControlModels; /**< 远程控制的数组 */
@property (nonatomic, copy) NSMutableArray *ignoreSessionModels; /**< 聊天置底的数组 */
@property (nonatomic, copy) NSMutableArray *selectSessions; /**< 已经选中的会话 */
@property (nonatomic, copy) NSMutableSet *revokeMsgSet; /**< 撤回的消息集合 */
@property (nonatomic, copy) NSString *currentUserName; /**< 当前用户的id */
@property (nonatomic, copy, readonly) NSDictionary *localInfoPlist;
@property (nonatomic, copy, readonly) NSDictionary *romoteInfoPlist;
- (void)saveAutoReplyModels;
- (void)saveRemoteControlModels;
- (void)saveIgnoreSessionModels;
+ (instancetype)sharedConfig;
@end
================================================
FILE: WeChatPlugin/Sources/Config/TKWeChatPluginConfig.m
================================================
//
// TKWeChatPluginConfig.m
// WeChatPlugin
//
// Created by TK on 2017/4/19.
// Copyright © 2017年 tk. All rights reserved.
//
#import "TKWeChatPluginConfig.h"
#import "TKRemoteControlModel.h"
#import "TKAutoReplyModel.h"
#import "TKIgnoreSessonModel.h"
static NSString * const kTKPreventRevokeEnableKey = @"kTKPreventRevokeEnableKey";
static NSString * const kTKAutoAuthEnableKey = @"kTKAutoAuthEnableKey";
static NSString * const kTKAutoLoginEnableKey = @"kTKAutoLoginEnableKey";
static NSString * const kTKOnTopKey = @"kTKOnTopKey";
static NSString * const kTKForbidCheckVersionKey = @"kTKForbidCheckVersionKey";
static NSString * const kTKWeChatResourcesPath = @"/Applications/WeChat.app/Contents/MacOS/WeChatPlugin.framework/Resources/";
@interface TKWeChatPluginConfig ()
@property (nonatomic, copy) NSString *remoteControlPlistFilePath;
@property (nonatomic, copy) NSString *autoReplyPlistFilePath;
@property (nonatomic, copy) NSString *ignoreSessionPlistFilePath;
@property (nonatomic, copy) NSDictionary *localInfoPlist;
@property (nonatomic, copy) NSDictionary *romoteInfoPlist;
@end
@implementation TKWeChatPluginConfig
+ (instancetype)sharedConfig {
static TKWeChatPluginConfig *config = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
config = [[TKWeChatPluginConfig alloc] init];
});
return config;
}
- (instancetype)init {
self = [super init];
if (self) {
_preventRevokeEnable = [[NSUserDefaults standardUserDefaults] boolForKey:kTKPreventRevokeEnableKey];
_autoAuthEnable = [[NSUserDefaults standardUserDefaults] boolForKey:kTKAutoAuthEnableKey];
_autoLoginEnable = [[NSUserDefaults standardUserDefaults] boolForKey:kTKAutoLoginEnableKey];
_onTop = [[NSUserDefaults standardUserDefaults] boolForKey:kTKOnTopKey];
_forbidCheckVersion = [[NSUserDefaults standardUserDefaults] boolForKey:kTKForbidCheckVersionKey];
}
return self;
}
- (void)setPreventRevokeEnable:(BOOL)preventRevokeEnable {
_preventRevokeEnable = preventRevokeEnable;
[[NSUserDefaults standardUserDefaults] setBool:preventRevokeEnable forKey:kTKPreventRevokeEnableKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)setAutoAuthEnable:(BOOL)autoAuthEnable {
_autoAuthEnable = autoAuthEnable;
[[NSUserDefaults standardUserDefaults] setBool:autoAuthEnable forKey:kTKAutoAuthEnableKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)setAutoLoginEnable:(BOOL)autoLoginEnable {
_autoLoginEnable = autoLoginEnable;
[[NSUserDefaults standardUserDefaults] setBool:autoLoginEnable forKey:kTKAutoLoginEnableKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)setOnTop:(BOOL)onTop {
_onTop = onTop;
[[NSUserDefaults standardUserDefaults] setBool:_onTop forKey:kTKOnTopKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)setForbidCheckVersion:(BOOL)forbidCheckVersion {
_forbidCheckVersion = forbidCheckVersion;
[[NSUserDefaults standardUserDefaults] setBool:_forbidCheckVersion forKey:kTKForbidCheckVersionKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
#pragma mark - 自动回复
- (NSArray *)autoReplyModels {
if (!_autoReplyModels) {
_autoReplyModels = [self getModelsWithClass:[TKAutoReplyModel class] filePath:self.autoReplyPlistFilePath];
}
return _autoReplyModels;
}
- (void)saveAutoReplyModels {
NSMutableArray *needSaveModels = [NSMutableArray array];
[_autoReplyModels enumerateObjectsUsingBlock:^(TKAutoReplyModel *model, NSUInteger idx, BOOL * _Nonnull stop) {
if (model.hasEmptyKeywordOrReplyContent) {
model.enable = NO;
model.enableGroupReply = NO;
}
model.replyContent = model.replyContent == nil ? @"" : model.replyContent;
model.keyword = model.keyword == nil ? @"" : model.keyword;
[needSaveModels addObject:model.dictionary];
}];
[needSaveModels writeToFile:self.autoReplyPlistFilePath atomically:YES];
}
#pragma mark - 远程控制
- (NSArray *)remoteControlModels {
if (!_remoteControlModels) {
_remoteControlModels = ({
NSArray *originModels = [NSArray arrayWithContentsOfFile:self.remoteControlPlistFilePath];
NSMutableArray *newRemoteControlModels = [NSMutableArray array];
[originModels enumerateObjectsUsingBlock:^(NSArray *subModels, NSUInteger idx, BOOL * _Nonnull stop) {
NSMutableArray *newSubModels = [NSMutableArray array];
[subModels enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
TKRemoteControlModel *model = [[TKRemoteControlModel alloc] initWithDict:obj];
[newSubModels addObject:model];
}];
[newRemoteControlModels addObject:newSubModels];
}];
newRemoteControlModels;
});
}
return _remoteControlModels;
}
- (void)saveRemoteControlModels {
NSMutableArray *needSaveModels = [NSMutableArray array];
[_remoteControlModels enumerateObjectsUsingBlock:^(NSArray *subModels, NSUInteger idx, BOOL * _Nonnull stop) {
NSMutableArray *newSubModels = [NSMutableArray array];
[subModels enumerateObjectsUsingBlock:^(TKRemoteControlModel *obj, NSUInteger idx, BOOL * _Nonnull stop) {
[newSubModels addObject:obj.dictionary];
}];
[needSaveModels addObject:newSubModels];
}];
[needSaveModels writeToFile:self.remoteControlPlistFilePath atomically:YES];
}
#pragma mark - 置底
- (NSArray *)ignoreSessionModels {
if (!_ignoreSessionModels) {
_ignoreSessionModels = [self getModelsWithClass:[TKIgnoreSessonModel class] filePath:self.ignoreSessionPlistFilePath];
}
return _ignoreSessionModels;
}
- (void)saveIgnoreSessionModels {
NSMutableArray *needSaveArray = [NSMutableArray array];
[self.ignoreSessionModels enumerateObjectsUsingBlock:^(TKBaseModel *obj, NSUInteger idx, BOOL * _Nonnull stop) {
[needSaveArray addObject:obj.dictionary];
}];
[needSaveArray writeToFile:self.ignoreSessionPlistFilePath atomically:YES];
}
#pragma mark - 选中的会话
- (NSMutableArray *)selectSessions {
if (!_selectSessions) {
_selectSessions = [NSMutableArray array];
}
return _selectSessions;
}
#pragma mark - 撤回的消息集合
- (NSMutableSet *)revokeMsgSet {
if (!_revokeMsgSet) {
_revokeMsgSet = [NSMutableSet set];
}
return _revokeMsgSet;
}
#pragma mark - 获取沙盒上的 plist 文件,包括:远程控制,自动回复,置底列表。
- (NSString *)remoteControlPlistFilePath {
if (!_remoteControlPlistFilePath) {
_remoteControlPlistFilePath = [self getSandboxFilePathWithPlistName:@"TKRemoteControlCommands.plist"];
}
return _remoteControlPlistFilePath;
}
- (NSString *)autoReplyPlistFilePath {
if (!_autoReplyPlistFilePath) {
_autoReplyPlistFilePath = [self getSandboxFilePathWithPlistName:@"TKAutoReplyModels.plist"];
}
return _autoReplyPlistFilePath;
}
- (NSString *)ignoreSessionPlistFilePath {
if (!_ignoreSessionPlistFilePath) {
_ignoreSessionPlistFilePath = [self getSandboxFilePathWithPlistName:@"TKIgnoreSessons.plist"];
}
return _ignoreSessionPlistFilePath;
}
#pragma mark - 获取本地 & github 上的小助手 info 信息
- (NSDictionary *)localInfoPlist {
if (!_localInfoPlist) {
NSString *localInfoPath = [kTKWeChatResourcesPath stringByAppendingString:@"info.plist"];
_localInfoPlist = [NSDictionary dictionaryWithContentsOfFile:localInfoPath];
}
return _localInfoPlist;
}
- (NSDictionary *)romoteInfoPlist {
if (!_romoteInfoPlist) {
NSURL *url = [NSURL URLWithString:@"https://raw.githubusercontent.com/TKkk-iOSer/WeChatPlugin-MacOS/master/Other/Products/Debug/WeChatPlugin.framework/Resources/Info.plist"];
_romoteInfoPlist = [NSDictionary dictionaryWithContentsOfURL:url];
}
return _romoteInfoPlist;
}
#pragma mark - common
- (NSMutableArray *)getModelsWithClass:(Class)class filePath:(NSString *)filePath {
NSArray *originModels = [NSArray arrayWithContentsOfFile:filePath];
NSMutableArray *newModels = [NSMutableArray array];
__weak Class weakClass = class;
[originModels enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL * _Nonnull stop) {
TKIgnoreSessonModel *model = [[weakClass alloc] initWithDict:obj];
[newModels addObject:model];
}];
return newModels;
}
- (NSString *)getSandboxFilePathWithPlistName:(NSString *)plistName {
NSFileManager *manager = [NSFileManager defaultManager];
NSString *wechatPluginDirectory = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"TKWeChatPlugin"];
NSString *plistFilePath = [wechatPluginDirectory stringByAppendingPathComponent:plistName];
if ([manager fileExistsAtPath:plistFilePath]) {
return plistFilePath;
}
[manager createDirectoryAtPath:wechatPluginDirectory withIntermediateDirectories:YES attributes:nil error:nil];
NSString *resourcesFilePath = [kTKWeChatResourcesPath stringByAppendingString:plistName];
if (![manager fileExistsAtPath:resourcesFilePath]) {
return plistFilePath;
}
NSError *error = nil;
[manager copyItemAtPath:resourcesFilePath toPath:plistFilePath error:&error];
if (!error) {
return plistFilePath;
}
return resourcesFilePath;
}
@end
================================================
FILE: WeChatPlugin/Sources/Controllers/TKRemoteControlController.h
================================================
//
// TKRemoteControlController.h
// WeChatPlugin
//
// Created by TK on 2017/8/8.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface TKRemoteControlController : NSObject
+ (void)executeRemoteControlCommandWithVoiceMsg:(NSString *)msg;
+ (void)executeRemoteControlCommandWithMsg:(NSString *)msg;
+ (void)executeShellCommand:(NSString *)msg;
+ (NSString *)remoteControlCommandsString;
@end
================================================
FILE: WeChatPlugin/Sources/Controllers/TKRemoteControlController.m
================================================
//
// TKRemoteControlController.m
// WeChatPlugin
//
// Created by TK on 2017/8/8.
// Copyright © 2017年 tk. All rights reserved.
//
#import "TKRemoteControlController.h"
#import "TKWeChatPluginConfig.h"
#import "TKRemoteControlModel.h"
#import "WeChatPlugin.h"
typedef NS_ENUM(NSUInteger, MessageDataType) {
MessageDataTypeText,
MessageDataTypeVoice
};
// 执行 AppleScript
static NSString * const kRemoteControlAppleScript = @"osascript /Applications/WeChat.app/Contents/MacOS/WeChatPlugin.framework/Resources/TKRemoteControlScript.scpt";
@implementation TKRemoteControlController
+ (void)executeRemoteControlCommandWithVoiceMsg:(NSString *)msg {
NSString *currentUserName = [objc_getClass("CUtility") GetCurrentUserName];
NSString *callBack = [NSString stringWithFormat:@"小助手收到一条语音消息,转文字后👇👇👇:\n\n\n%@",msg];
MessageService *service = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MessageService")];
[service SendTextMessage:currentUserName toUsrName:currentUserName msgText:callBack atUserList:nil];
[self executeRemoteControlCommandWithMsg:msg msgType:MessageDataTypeVoice];
}
+ (void)executeRemoteControlCommandWithMsg:(NSString *)msg {
[self executeRemoteControlCommandWithMsg:msg msgType:MessageDataTypeText];
}
+ (void)executeRemoteControlCommandWithMsg:(NSString *)msg msgType:(MessageDataType)type {
NSArray *remoteControlModels = [TKWeChatPluginConfig sharedConfig].remoteControlModels;
[remoteControlModels enumerateObjectsUsingBlock:^(NSArray *subModels, NSUInteger index, BOOL * _Nonnull stop) {
[subModels enumerateObjectsUsingBlock:^(TKRemoteControlModel *model, NSUInteger idx, BOOL * _Nonnull subStop) {
if ([self sholdExecuteRemoteControlWithModel:model msg:msg msgType:type]) {
if ([model.function isEqualToString:@"屏幕保护"] || [model.function isEqualToString:@"锁屏"]) {
// 屏幕保护 & 锁屏 通过 Shell 命令来执行即可
[self executeShellCommand:model.executeCommand];
} else {
// 拼接相关参数,执行 AppleScript
NSString *command = [NSString stringWithFormat:@"%@ %@",kRemoteControlAppleScript, model.executeCommand];
[self executeShellCommand:command];
// bug: 有些程序在第一次时会无法关闭,需要再次关闭
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if ([model.function isEqualToString:@"退出所有程序"]) {
NSString *command = [NSString stringWithFormat:@"%@ %@",kRemoteControlAppleScript, model.executeCommand];
[self executeShellCommand:command];
}
});
}
NSString *currentUserName = [objc_getClass("CUtility") GetCurrentUserName];
NSString *callBack = [NSString stringWithFormat:@"小助手收到一条指令:%@",model.function];
MessageService *service = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MessageService")];
[service SendTextMessage:currentUserName toUsrName:currentUserName msgText:callBack atUserList:nil];
[service ClearUnRead:currentUserName FromID:0 ToID:0];
*stop = YES;
*subStop = YES;
}
}];
}];
}
+ (BOOL)sholdExecuteRemoteControlWithModel:(TKRemoteControlModel *)model msg:(NSString *)msg msgType:(MessageDataType)type {
if (model.enable && ![model.keyword isEqualToString:@""]) {
if ((type == MessageDataTypeText && [msg isEqualToString:model.keyword]) || (type == MessageDataTypeVoice && [msg containsString:model.keyword])) {
return YES;
} else {
return NO;
}
} else {
return NO;
}
}
/**
通过 NSTask 执行 Shell 命令
@param cmd Terminal命令
*/
+ (void)executeShellCommand:(NSString *)cmd {
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/bin/bash"];
[task setArguments:@[@"-c", cmd]];
[task launch];
}
+ (NSString *)remoteControlCommandsString {
NSMutableString *replyContent = [NSMutableString stringWithFormat:@"远程控制指令:\n(功能-指令-是否开启)\n\n"];
NSArray *remoteControlModels = [TKWeChatPluginConfig sharedConfig].remoteControlModels;
[remoteControlModels enumerateObjectsUsingBlock:^(NSArray *subModels, NSUInteger index, BOOL * _Nonnull stop) {
switch (index) {
case 0:
[replyContent appendString:@"macbook控制:\n"];
break;
case 1:
[replyContent appendString:@"app控制:\n"];
break;
case 2:
[replyContent appendString:@"网易云音乐控制:\n"];
break;
default:
break;
}
[subModels enumerateObjectsUsingBlock:^(TKRemoteControlModel *model, NSUInteger idx, BOOL * _Nonnull stop) {
[replyContent appendFormat:@"%@-%@-%@\n", model.function, model.keyword, model.enable ? @"开启":@"关闭"];
}];
[replyContent appendString:@"\n"];
}];
return replyContent;
}
@end
================================================
FILE: WeChatPlugin/Sources/Models/TKAutoReplyModel.h
================================================
//
// TKAutoReplyModel.h
// WeChatPlugin
//
// Created by TK on 2017/8/18.
// Copyright © 2017年 tk. All rights reserved.
//
#import "TKBaseModel.h"
@interface TKAutoReplyModel : TKBaseModel
@property (nonatomic, assign) BOOL enable; /**< 是否开启自动回复 */
@property (nonatomic, copy) NSString *keyword; /**< 自动回复关键字 */
@property (nonatomic, copy) NSString *replyContent; /**< 自动回复的内容 */
@property (nonatomic, assign) BOOL enableGroupReply; /**< 是否开启群聊自动回复 */
@property (nonatomic, assign) BOOL enableSingleReply; /**< 是否开启私聊自动回复 */
@property (nonatomic, assign) BOOL enableRegex; /**< 是否开启正则匹配 */
- (BOOL)hasEmptyKeywordOrReplyContent;
@end
================================================
FILE: WeChatPlugin/Sources/Models/TKAutoReplyModel.m
================================================
//
// TKAutoReplyModel.m
// WeChatPlugin
//
// Created by TK on 2017/8/18.
// Copyright © 2017年 tk. All rights reserved.
//
#import "TKAutoReplyModel.h"
@implementation TKAutoReplyModel
- (instancetype)initWithDict:(NSDictionary *)dict {
self = [super init];
if (self) {
self.enable = [dict[@"enable"] boolValue];
self.keyword = dict[@"keyword"];
self.replyContent = dict[@"replyContent"];
self.enableGroupReply = [dict[@"enableGroupReply"] boolValue];
self.enableSingleReply = [dict[@"enableSingleReply"] boolValue];
self.enableRegex = [dict[@"enableRegex"] boolValue];
}
return self;
}
- (NSDictionary *)dictionary {
return @{@"enable": @(self.enable),
@"keyword": self.keyword,
@"replyContent": self.replyContent,
@"enableGroupReply": @(self.enableGroupReply),
@"enableSingleReply": @(self.enableSingleReply),
@"enableRegex": @(self.enableRegex)};
}
- (BOOL)hasEmptyKeywordOrReplyContent {
return (self.keyword == nil || self.replyContent == nil || [self.keyword isEqualToString:@""] || [self.replyContent isEqualToString:@""]);
}
@end
================================================
FILE: WeChatPlugin/Sources/Models/TKBaseModel.h
================================================
//
// TKBaseModel.h
// WeChatPlugin
//
// Created by TK on 2017/9/17.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface TKBaseModel : NSObject
- (instancetype)initWithDict:(NSDictionary *)dict;
- (NSDictionary *)dictionary;
@end
================================================
FILE: WeChatPlugin/Sources/Models/TKBaseModel.m
================================================
//
// TKBaseModel.m
// WeChatPlugin
//
// Created by TK on 2017/9/17.
// Copyright © 2017年 tk. All rights reserved.
//
#import "TKBaseModel.h"
@implementation TKBaseModel
- (instancetype)initWithDict:(NSDictionary *)dict {
NSAssert(NO, @"the mothed initWithDict: must be override by subclass");
return nil;
}
- (NSDictionary *)dictionary {
NSAssert(NO, @"the mothed dictionary must be override by subclass");
return nil;
}
@end
================================================
FILE: WeChatPlugin/Sources/Models/TKIgnoreSessonModel.h
================================================
//
// TKIgnoreSessonModel.h
// WeChatPlugin
//
// Created by TK on 2017/9/15.
// Copyright © 2017年 tk. All rights reserved.
//
#import "TKBaseModel.h"
@interface TKIgnoreSessonModel : TKBaseModel
@property (nonatomic, copy) NSString *selfContact;
@property (nonatomic, copy) NSString *userName;
@property (nonatomic, assign) BOOL ignore;
@end
================================================
FILE: WeChatPlugin/Sources/Models/TKIgnoreSessonModel.m
================================================
//
// TKIgnoreSessonModel.m
// WeChatPlugin
//
// Created by TK on 2017/9/15.
// Copyright © 2017年 tk. All rights reserved.
//
#import "TKIgnoreSessonModel.h"
@implementation TKIgnoreSessonModel
- (instancetype)initWithDict:(NSDictionary *)dict {
self = [super init];
if (self) {
self.selfContact = dict[@"selfContact"];
self.userName = dict[@"userName"];
self.ignore = [dict[@"ignore"] boolValue];
}
return self;
}
- (NSDictionary *)dictionary {
return @{@"selfContact": self.selfContact,
@"userName": self.userName,
@"ignore": @(self.ignore)};
}
@end
================================================
FILE: WeChatPlugin/Sources/Models/TKRemoteControlModel.h
================================================
//
// TKRemoteControlModel.h
// WeChatPlugin
//
// Created by TK on 2017/8/8.
// Copyright © 2017年 tk. All rights reserved.
//
#import "TKBaseModel.h"
@interface TKRemoteControlModel : TKBaseModel
@property (nonatomic, assign) BOOL enable;
@property (nonatomic, copy) NSString *keyword;
@property (nonatomic, copy) NSString *function;
@property (nonatomic, copy) NSString *executeCommand;
@end
================================================
FILE: WeChatPlugin/Sources/Models/TKRemoteControlModel.m
================================================
//
// TKRemoteControlModel.m
// WeChatPlugin
//
// Created by TK on 2017/8/8.
// Copyright © 2017年 tk. All rights reserved.
//
#import "TKRemoteControlModel.h"
@implementation TKRemoteControlModel
- (instancetype)initWithDict:(NSDictionary *)dict {
self = [super init];
if (self) {
self.enable = [dict[@"enable"] boolValue];
self.keyword = dict[@"keyword"];
self.function = dict[@"function"];
self.executeCommand = dict[@"executeCommand"];
}
return self;
}
- (NSDictionary *)dictionary {
return @{@"enable": @(self.enable),
@"keyword": self.keyword,
@"function": self.function,
@"executeCommand": self.executeCommand};
}
@end
================================================
FILE: WeChatPlugin/Sources/Utils/TKHelper.h
================================================
//
// TKHelper.h
// WeChatPlugin
//
// Created by TK on 2017/4/19.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface TKHelper : NSObject
/**
替换对象方法
@param originalClass 原始类
@param originalSelector 原始类的方法
@param swizzledClass 替换类
@param swizzledSelector 替换类的方法
*/
void tk_hookMethod(Class originalClass, SEL originalSelector, Class swizzledClass, SEL swizzledSelector);
/**
替换类方法
@param originalClass 原始类
@param originalSelector 原始类的类方法
@param swizzledClass 替换类
@param swizzledSelector 替换类的类方法
*/
void tk_hookClassMethod(Class originalClass, SEL originalSelector, Class swizzledClass, SEL swizzledSelector);
@end
================================================
FILE: WeChatPlugin/Sources/Utils/TKHelper.m
================================================
//
// TKHelper.m
// WeChatPlugin
//
// Created by TK on 2017/4/19.
// Copyright © 2017年 tk. All rights reserved.
//
#import "TKHelper.h"
@implementation TKHelper
/**
替换对象方法
@param originalClass 原始类
@param originalSelector 原始类的方法
@param swizzledClass 替换类
@param swizzledSelector 替换类的方法
*/
void tk_hookMethod(Class originalClass, SEL originalSelector, Class swizzledClass, SEL swizzledSelector) {
Method originalMethod = class_getInstanceMethod(originalClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(swizzledClass, swizzledSelector);
if(originalMethod && swizzledMethod) {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
/**
替换类方法
@param originalClass 原始类
@param originalSelector 原始类的类方法
@param swizzledClass 替换类
@param swizzledSelector 替换类的类方法
*/
void tk_hookClassMethod(Class originalClass, SEL originalSelector, Class swizzledClass, SEL swizzledSelector) {
Method originalMethod = class_getClassMethod(originalClass, originalSelector);
Method swizzledMethod = class_getClassMethod(swizzledClass, swizzledSelector);
if(originalMethod && swizzledMethod) {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
@end
================================================
FILE: WeChatPlugin/Sources/Utils/TKVersionManager.h
================================================
//
// TKVersionManager.h
// WeChatPlugin
//
// Created by TK on 2018/2/24.
// Copyright © 2018年 tk. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSUInteger, TKVersionStatus) {
TKVersionStatusOld,
TKVersionStatusNew,
};
@interface TKVersionManager : NSObject
+ (instancetype)shareManager;
- (void)checkVersionFinish:(void (^)(TKVersionStatus, NSString *))finish;
@end
================================================
FILE: WeChatPlugin/Sources/Utils/TKVersionManager.m
================================================
//
// TKVersionManager.m
// WeChatPlugin
//
// Created by TK on 2018/2/24.
// Copyright © 2018年 tk. All rights reserved.
//
#import "TKVersionManager.h"
#import "TKWeChatPluginConfig.h"
static NSString * const kTKVersionStatusOldToast = @"当前小助手为最新版本,无需更新";
@implementation TKVersionManager
+ (instancetype)shareManager {
static TKVersionManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[TKVersionManager alloc] init];
});
return manager;
}
- (void)checkVersionFinish:(void (^)(TKVersionStatus, NSString *))finish {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSDictionary *localInfo = [[TKWeChatPluginConfig sharedConfig] localInfoPlist];
NSDictionary *romoteInfo = [[TKWeChatPluginConfig sharedConfig] romoteInfoPlist];
NSString *localBundle = localInfo[@"CFBundleShortVersionString"];
NSString *romoteBundle = romoteInfo[@"CFBundleShortVersionString"];
dispatch_async(dispatch_get_main_queue(), ^{
if ([localBundle isEqualToString:romoteBundle]) {
NSString *versionMsg = [localInfo[@"versionInfo"] stringByReplacingOccurrencesOfString:@"\\n" withString:@"\n"];
finish(TKVersionStatusOld, versionMsg);
} else if (romoteInfo[@"versionInfo"]) {
NSString *versionMsg = [romoteInfo[@"versionInfo"] stringByReplacingOccurrencesOfString:@"\\n" withString:@"\n"];
finish(TKVersionStatusNew, versionMsg);
}
});
});
}
@end
================================================
FILE: WeChatPlugin/Sources/Utils/TKWebServerManager.h
================================================
//
// TKWebServerManager.h
// WeChatPlugin
//
// Created by TK on 2018/3/18.
// Copyright © 2018年 tk. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface TKWebServerManager : NSObject
+ (instancetype)shareManager;
- (void)startServer;
- (void)endServer;
@end
================================================
FILE: WeChatPlugin/Sources/Utils/TKWebServerManager.m
================================================
//
// TKWebServerManager.m
// WeChatPlugin
//
// Created by TK on 2018/3/18.
// Copyright © 2018年 tk. All rights reserved.
//
#import "TKWebServerManager.h"
#import "WeChatPlugin.h"
#import <GCDWebServer.h>
#import <GCDWebServerDataResponse.h>
#import <GCDWebServerURLEncodedFormRequest.h>
@interface TKWebServerManager ()
@property (nonatomic, strong) GCDWebServer *webServer;
@end
@implementation TKWebServerManager
+ (instancetype)shareManager {
static TKWebServerManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[TKWebServerManager alloc] init];
});
return manager;
}
- (void)startServer {
if (self.webServer) {
return;
}
NSDictionary *options = @{GCDWebServerOption_Port: @52700,
GCDWebServerOption_BindToLocalhost: @YES,
GCDWebServerOption_ConnectedStateCoalescingInterval: @2,
};
self.webServer = [[GCDWebServer alloc] init];
[self addHandleForSearchUser];
[self addHandleForOpenSession];
[self addHandleForSendMsg];
[self.webServer startWithOptions:options error:nil];
}
- (void)endServer {
if( [self.webServer isRunning] ) {
[self.webServer stop];
[self.webServer removeAllHandlers];
self.webServer = nil;
}
}
- (void)addHandleForSearchUser {
__weak typeof(self) weakSelf = self;
[self.webServer addHandlerForMethod:@"GET" path:@"/wechat-plugin/user" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse * _Nullable(__kindof GCDWebServerRequest * _Nonnull request) {
NSDictionary *keyword = request.query ? request.query[@"keyword"] ? request.query[@"keyword"] : @"" : @"";
__block NSMutableArray *sessionList = [NSMutableArray array];
__block BOOL hasResult = NO;
MMComplexContactSearchTaskMgr *searchMgr = [objc_getClass("MMComplexContactSearchTaskMgr") sharedInstance];
[searchMgr doComplexContactSearch:keyword searchScene:31 complete:^(NSArray<MMComplexContactSearchResult *> *contactResult, NSArray *brandSult, NSArray<MMComplexGroupContactSearchResult *> *groupResult) {
[contactResult enumerateObjectsUsingBlock:^(MMComplexContactSearchResult * _Nonnull contact, NSUInteger idx, BOOL * _Nonnull stop) {
[sessionList addObject:[weakSelf dictFromContactSearchResult:contact]];
}];
[groupResult enumerateObjectsUsingBlock:^(MMComplexGroupContactSearchResult * _Nonnull group, NSUInteger idx, BOOL * _Nonnull stop) {
[sessionList addObject:[weakSelf dictFromGroupSearchResult:group]];
}];
hasResult = YES;
} cancelable:YES];
while (!hasResult) {}
return [GCDWebServerDataResponse responseWithJSONObject:sessionList];
}];
}
- (void)addHandleForOpenSession {
[self.webServer addHandlerForMethod:@"POST" path:@"/wechat-plugin/open-session" requestClass:[GCDWebServerURLEncodedFormRequest class] processBlock:^GCDWebServerResponse * _Nullable(__kindof GCDWebServerURLEncodedFormRequest * _Nonnull request) {
NSDictionary *requestBody = [request arguments];
if (requestBody && requestBody[@"userId"]) {
dispatch_async(dispatch_get_main_queue(), ^{
MMSessionMgr *sessionMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MMSessionMgr")];
WCContactData *selectContact = [sessionMgr getContact:requestBody[@"userId"]];
WeChat *wechat = [objc_getClass("WeChat") sharedInstance];
[wechat startANewChatWithContact:selectContact];
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
});
return [GCDWebServerResponse responseWithStatusCode:200];
}
return [GCDWebServerResponse responseWithStatusCode:404];
}];
}
- (void)addHandleForSendMsg {
[self.webServer addHandlerForMethod:@"POST" path:@"/wechat-plugin/send-message" requestClass:[GCDWebServerURLEncodedFormRequest class] processBlock:^GCDWebServerResponse * _Nullable(__kindof GCDWebServerURLEncodedFormRequest * _Nonnull request) {
NSDictionary *requestBody = [request arguments];
if (requestBody && requestBody[@"userId"] && requestBody[@"content"]) {
dispatch_async(dispatch_get_main_queue(), ^{
MessageService *messageService = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MessageService")];
NSString *currentUserName = [objc_getClass("CUtility") GetCurrentUserName];
[messageService SendTextMessage:currentUserName
toUsrName:requestBody[@"userId"]
msgText:requestBody[@"content"]
atUserList:nil];
});
return [GCDWebServerResponse responseWithStatusCode:200];
}
return [GCDWebServerResponse responseWithStatusCode:404];
}];
}
- (NSDictionary *)dictFromGroupSearchResult:(MMComplexGroupContactSearchResult *)result {
WCContactData *groupContact = result.groupContact;
__block NSString *subTitle = @"";
if (result.searchType == 2) {
[result.groupMembersResult.membersSearchReults enumerateObjectsUsingBlock:^(MMComplexContactSearchResult * _Nonnull contact, NSUInteger idx, BOOL * _Nonnull stop) {
NSString *matchStr =[self matchWithContactResult:contact];
subTitle = [NSString stringWithFormat:@"成员:%@(%@)", contact.contact.m_nsNickName, matchStr];
}];
}
NSString *imgPath = [self cacheAvatarPathFromHeadImgUrl:groupContact.m_nsHeadImgUrl];
return @{@"title": [NSString stringWithFormat:@"[群聊]%@",groupContact.getGroupDisplayName],
@"subTitle": subTitle,
@"icon": imgPath,
@"userId": groupContact.m_nsUsrName
};
}
- (NSString *)matchWithContactResult:(MMComplexContactSearchResult *)result {
NSString *matchStr = @"";
NSInteger type = result.fieldType;
switch (type) { // 1:备注 3:昵称 4:微信号 7:市 8:省份 9:国家
case 1:
matchStr = [NSString stringWithFormat:@"备注:%@",result.fieldValue];
break;
case 3:
matchStr = [NSString stringWithFormat:@"昵称:%@",result.fieldValue];
break;
case 4:
matchStr = [NSString stringWithFormat:@"微信号:%@",result.fieldValue];
break;
case 7:
matchStr = [NSString stringWithFormat:@"城市:%@",result.fieldValue];
break;
case 8:
matchStr = [NSString stringWithFormat:@"省份:%@",result.fieldValue];
break;
case 9:
matchStr = [NSString stringWithFormat:@"国家:%@",result.fieldValue];
break;
default:
matchStr = [NSString stringWithFormat:@"包含:%@",result.fieldValue];
break;
}
return matchStr;
}
- (NSDictionary *)dictFromContactSearchResult:(MMComplexContactSearchResult *)result {
WCContactData *contact = result.contact;
NSString *title = contact.m_nsNickName;
if(contact.m_nsRemark && ![contact.m_nsRemark isEqualToString:@""]) {
title = [NSString stringWithFormat:@"%@(%@)",contact.m_nsRemark, contact.m_nsNickName];
}
NSString *subTitle =[self matchWithContactResult:result];
NSString *imgPath = [self cacheAvatarPathFromHeadImgUrl:contact.m_nsHeadImgUrl];
return @{@"title": title,
@"subTitle": subTitle,
@"icon": imgPath,
@"userId": contact.m_nsUsrName
};
}
// 获取本地图片缓存路径
- (NSString *)cacheAvatarPathFromHeadImgUrl:(NSString *)imgUrl {
NSString *imgPath = @"";
if ([imgUrl respondsToSelector:@selector(md5String)]) {
NSString *imgMd5Str = [imgUrl performSelector:@selector(md5String)];
MMAvatarService *avatarService = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MMAvatarService")];
imgPath = [NSString stringWithFormat:@"%@/%@",[avatarService avatarCachePath], imgMd5Str];
}
return imgPath;
}
@end
================================================
FILE: WeChatPlugin/Sources/Utils/XMLReader.h
================================================
//
// XMLReader.h
//
// Created by Troy Brant on 9/18/10.
// Updated by Antoine Marcadet on 9/23/11.
// Updated by Divan Visagie on 2012-08-26
//
#import <Foundation/Foundation.h>
enum {
XMLReaderOptionsProcessNamespaces = 1 << 0, // Specifies whether the receiver reports the namespace and the qualified name of an element.
XMLReaderOptionsReportNamespacePrefixes = 1 << 1, // Specifies whether the receiver reports the scope of namespace declarations.
XMLReaderOptionsResolveExternalEntities = 1 << 2, // Specifies whether the receiver reports declarations of external entities.
};
typedef NSUInteger XMLReaderOptions;
@interface XMLReader : NSObject <NSXMLParserDelegate>
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)errorPointer;
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)errorPointer;
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data options:(XMLReaderOptions)options error:(NSError **)errorPointer;
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string options:(XMLReaderOptions)options error:(NSError **)errorPointer;
@end
================================================
FILE: WeChatPlugin/Sources/Utils/XMLReader.m
================================================
//
// XMLReader.m
//
// Created by Troy Brant on 9/18/10.
// Updated by Antoine Marcadet on 9/23/11.
// Updated by Divan Visagie on 2012-08-26
//
#import "XMLReader.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "XMLReader requires ARC support."
#endif
NSString *const kXMLReaderTextNodeKey = @"text";
NSString *const kXMLReaderAttributePrefix = @"@";
@interface XMLReader ()
@property (nonatomic, strong) NSMutableArray *dictionaryStack;
@property (nonatomic, strong) NSMutableString *textInProgress;
@property (nonatomic, strong) NSError *errorPointer;
@end
@implementation XMLReader
#pragma mark - Public methods
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)error
{
XMLReader *reader = [[XMLReader alloc] initWithError:error];
NSDictionary *rootDictionary = [reader objectWithData:data options:0];
return rootDictionary;
}
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)error
{
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
return [XMLReader dictionaryForXMLData:data error:error];
}
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data options:(XMLReaderOptions)options error:(NSError **)error
{
XMLReader *reader = [[XMLReader alloc] initWithError:error];
NSDictionary *rootDictionary = [reader objectWithData:data options:options];
return rootDictionary;
}
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string options:(XMLReaderOptions)options error:(NSError **)error
{
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
return [XMLReader dictionaryForXMLData:data options:options error:error];
}
#pragma mark - Parsing
- (id)initWithError:(NSError **)error
{
self = [super init];
if (self)
{
self.errorPointer = *error;
}
return self;
}
- (NSDictionary *)objectWithData:(NSData *)data options:(XMLReaderOptions)options
{
// Clear out any old data
self.dictionaryStack = [[NSMutableArray alloc] init];
self.textInProgress = [[NSMutableString alloc] init];
// Initialize the stack with a fresh dictionary
[self.dictionaryStack addObject:[NSMutableDictionary dictionary]];
// Parse the XML
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
[parser setShouldProcessNamespaces:(options & XMLReaderOptionsProcessNamespaces)];
[parser setShouldReportNamespacePrefixes:(options & XMLReaderOptionsReportNamespacePrefixes)];
[parser setShouldResolveExternalEntities:(options & XMLReaderOptionsResolveExternalEntities)];
parser.delegate = self;
BOOL success = [parser parse];
// Return the stack's root dictionary on success
if (success)
{
NSDictionary *resultDict = [self.dictionaryStack objectAtIndex:0];
return resultDict;
}
return nil;
}
#pragma mark - NSXMLParserDelegate methods
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
// Get the dictionary for the current level in the stack
NSMutableDictionary *parentDict = [self.dictionaryStack lastObject];
// Create the child dictionary for the new element, and initilaize it with the attributes
NSMutableDictionary *childDict = [NSMutableDictionary dictionary];
[childDict addEntriesFromDictionary:attributeDict];
// If there's already an item for this key, it means we need to create an array
id existingValue = [parentDict objectForKey:elementName];
if (existingValue)
{
NSMutableArray *array = nil;
if ([existingValue isKindOfClass:[NSMutableArray class]])
{
// The array exists, so use it
array = (NSMutableArray *) existingValue;
}
else
{
// Create an array if it doesn't exist
array = [NSMutableArray array];
[array addObject:existingValue];
// Replace the child dictionary with an array of children dictionaries
[parentDict setObject:array forKey:elementName];
}
// Add the new child dictionary to the array
[array addObject:childDict];
}
else
{
// No existing value, so update the dictionary
[parentDict setObject:childDict forKey:elementName];
}
// Update the stack
[self.dictionaryStack addObject:childDict];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
// Update the parent dict with text info
NSMutableDictionary *dictInProgress = [self.dictionaryStack lastObject];
// Set the text property
if ([self.textInProgress length] > 0)
{
// trim after concatenating
NSString *trimmedString = [self.textInProgress stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[dictInProgress setObject:[trimmedString mutableCopy] forKey:kXMLReaderTextNodeKey];
// Reset the text
self.textInProgress = [[NSMutableString alloc] init];
}
// Pop the current dict
[self.dictionaryStack removeLastObject];
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
// Build the text value
[self.textInProgress appendString:string];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
// Set the error pointer to the parser's error object
self.errorPointer = parseError;
}
@end
================================================
FILE: WeChatPlugin/Sources/Vendor/fishhook.c
================================================
// Copyright (c) 2013, Facebook, Inc.
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name Facebook nor the names of its contributors may be used to
// endorse or promote products derived from this software without specific
// prior written permission.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import "fishhook.h"
#import <dlfcn.h>
#import <stdlib.h>
#import <string.h>
#import <sys/types.h>
#import <mach-o/dyld.h>
#import <mach-o/loader.h>
#import <mach-o/nlist.h>
#ifdef __LP64__
typedef struct mach_header_64 mach_header_t;
typedef struct segment_command_64 segment_command_t;
typedef struct section_64 section_t;
typedef struct nlist_64 nlist_t;
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64
#else
typedef struct mach_header mach_header_t;
typedef struct segment_command segment_command_t;
typedef struct section section_t;
typedef struct nlist nlist_t;
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT
#endif
#ifndef SEG_DATA_CONST
#define SEG_DATA_CONST "__DATA_CONST"
#endif
struct rebindings_entry {
struct rebinding *rebindings;
size_t rebindings_nel;
struct rebindings_entry *next;
};
static struct rebindings_entry *_rebindings_head;
static int prepend_rebindings(struct rebindings_entry **rebindings_head,
struct rebinding rebindings[],
size_t nel) {
struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry));
if (!new_entry) {
return -1;
}
new_entry->rebindings = (struct rebinding *) malloc(sizeof(struct rebinding) * nel);
if (!new_entry->rebindings) {
free(new_entry);
return -1;
}
memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel);
new_entry->rebindings_nel = nel;
new_entry->next = *rebindings_head;
*rebindings_head = new_entry;
return 0;
}
static void perform_rebinding_with_section(struct rebindings_entry *rebindings,
section_t *section,
intptr_t slide,
nlist_t *symtab,
char *strtab,
uint32_t *indirect_symtab) {
uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1;
void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr);
for (uint i = 0; i < section->size / sizeof(void *); i++) {
uint32_t symtab_index = indirect_symbol_indices[i];
if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL ||
symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) {
continue;
}
uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx;
char *symbol_name = strtab + strtab_offset;
if (strnlen(symbol_name, 2) < 2) {
continue;
}
struct rebindings_entry *cur = rebindings;
while (cur) {
for (uint j = 0; j < cur->rebindings_nel; j++) {
if (strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) {
if (cur->rebindings[j].replaced != NULL &&
indirect_symbol_bindings[i] != cur->rebindings[j].replacement) {
*(cur->rebindings[j].replaced) = indirect_symbol_bindings[i];
}
indirect_symbol_bindings[i] = cur->rebindings[j].replacement;
goto symbol_loop;
}
}
cur = cur->next;
}
symbol_loop:;
}
}
static void rebind_symbols_for_image(struct rebindings_entry *rebindings,
const struct mach_header *header,
intptr_t slide) {
Dl_info info;
if (dladdr(header, &info) == 0) {
return;
}
segment_command_t *cur_seg_cmd;
segment_command_t *linkedit_segment = NULL;
struct symtab_command* symtab_cmd = NULL;
struct dysymtab_command* dysymtab_cmd = NULL;
uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t);
for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
cur_seg_cmd = (segment_command_t *)cur;
if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) {
linkedit_segment = cur_seg_cmd;
}
} else if (cur_seg_cmd->cmd == LC_SYMTAB) {
symtab_cmd = (struct symtab_command*)cur_seg_cmd;
} else if (cur_seg_cmd->cmd == LC_DYSYMTAB) {
dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd;
}
}
if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment ||
!dysymtab_cmd->nindirectsyms) {
return;
}
// Find base symbol/string table addresses
uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
char *strtab = (char *)(linkedit_base + symtab_cmd->stroff);
// Get indirect symbol table (array of uint32_t indices into symbol table)
uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);
cur = (uintptr_t)header + sizeof(mach_header_t);
for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
cur_seg_cmd = (segment_command_t *)cur;
if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 &&
strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) {
continue;
}
for (uint j = 0; j < cur_seg_cmd->nsects; j++) {
section_t *sect =
(section_t *)(cur + sizeof(segment_command_t)) + j;
if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) {
perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
}
if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {
perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
}
}
}
}
}
static void _rebind_symbols_for_image(const struct mach_header *header,
intptr_t slide) {
rebind_symbols_for_image(_rebindings_head, header, slide);
}
int rebind_symbols_image(void *header,
intptr_t slide,
struct rebinding rebindings[],
size_t rebindings_nel) {
struct rebindings_entry *rebindings_head = NULL;
int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel);
rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide);
free(rebindings_head);
return retval;
}
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) {
int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel);
if (retval < 0) {
return retval;
}
// If this was the first call, register callback for image additions (which is also invoked for
// existing images, otherwise, just run on existing images
if (!_rebindings_head->next) {
_dyld_register_func_for_add_image(_rebind_symbols_for_image);
} else {
uint32_t c = _dyld_image_count();
for (uint32_t i = 0; i < c; i++) {
_rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i));
}
}
return retval;
}
================================================
FILE: WeChatPlugin/Sources/Vendor/fishhook.h
================================================
// Copyright (c) 2013, Facebook, Inc.
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name Facebook nor the names of its contributors may be used to
// endorse or promote products derived from this software without specific
// prior written permission.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef fishhook_h
#define fishhook_h
#include <stddef.h>
#include <stdint.h>
#if !defined(FISHHOOK_EXPORT)
#define FISHHOOK_VISIBILITY __attribute__((visibility("hidden")))
#else
#define FISHHOOK_VISIBILITY __attribute__((visibility("default")))
#endif
#ifdef __cplusplus
extern "C" {
#endif //__cplusplus
/*
* A structure representing a particular intended rebinding from a symbol
* name to its replacement
*/
struct rebinding {
const char *name;
void *replacement;
void **replaced;
};
/*
* For each rebinding in rebindings, rebinds references to external, indirect
* symbols with the specified name to instead point at replacement for each
* image in the calling process as well as for all future images that are loaded
* by the process. If rebind_functions is called more than once, the symbols to
* rebind are added to the existing list of rebindings, and if a given symbol
* is rebound more than once, the later rebinding will take precedence.
*/
FISHHOOK_VISIBILITY
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel);
/*
* Rebinds as above, but only in the specified image. The header should point
* to the mach-o header, the slide should be the slide offset. Others as above.
*/
FISHHOOK_VISIBILITY
int rebind_symbols_image(void *header,
intptr_t slide,
struct rebinding rebindings[],
size_t rebindings_nel);
#ifdef __cplusplus
}
#endif //__cplusplus
#endif //fishhook_h
================================================
FILE: WeChatPlugin/Sources/Views/AutoReply/TKAutoReplyCell.h
================================================
//
// TKAutoReplyCell.h
// WeChatPlugin
//
// Created by TK on 2017/8/21.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "TKAutoReplyModel.h"
@interface TKAutoReplyCell : NSControl
@property (nonatomic, strong) TKAutoReplyModel *model;
@property (nonatomic, copy) void (^updateModel)(void);
@end
================================================
FILE: WeChatPlugin/Sources/Views/AutoReply/TKAutoReplyCell.m
================================================
//
// TKAutoReplyCell.m
// WeChatPlugin
//
// Created by TK on 2017/8/21.
// Copyright © 2017年 tk. All rights reserved.
//
#import "TKAutoReplyCell.h"
@interface TKAutoReplyCell ()
@property (nonatomic, strong) NSButton *selectBtn;
@property (nonatomic, strong) NSTextField *keywordLabel;
@property (nonatomic, strong) NSTextField *replyContentLabel;
@property (nonatomic, strong) NSBox *bottomLine;
@end
@implementation TKAutoReplyCell
- (instancetype)init {
self = [super init];
if (self) {
[self initSubviews];
}
return self;
}
- (void)initSubviews {
self.selectBtn = ({
NSButton *btn = [NSButton tk_checkboxWithTitle:@"" target:self action:@selector(clickSelectBtn:)];
btn.frame = NSMakeRect(5, 15, 20, 20);
btn;
});
self.keywordLabel = ({
NSTextField *label = [NSTextField tk_labelWithString:@""];
label.placeholderString = @"关键字";
[[label cell] setLineBreakMode:NSLineBreakByCharWrapping];
[[label cell] setTruncatesLastVisibleLine:YES];
label.font = [NSFont systemFontOfSize:10];
label.frame = NSMakeRect(30, 30, 160, 15);
label;
});
self.replyContentLabel = ({
NSTextField *label = [NSTextField tk_labelWithString:@""];
label.placeholderString = @"回复内容";
[[label cell] setLineBreakMode:NSLineBreakByCharWrapping];
[[label cell] setTruncatesLastVisibleLine:YES];
label.frame = NSMakeRect(30, 10, 160, 15);
label;
});
self.bottomLine = ({
NSBox *v = [[NSBox alloc] init];
v.boxType = NSBoxSeparator;
v.frame = NSMakeRect(0, 0, 200, 1);
v;
});
[self addSubviews:@[self.selectBtn,
self.keywordLabel,
self.replyContentLabel,
self.bottomLine]];
}
- (void)clickSelectBtn:(NSButton *)btn {
self.model.enable = btn.state;
if (!self.model.enableSingleReply && !self.model.enableGroupReply && btn.state == YES) {
self.model.enableSingleReply = YES;
if (self.updateModel) self.updateModel();
}
}
- (void)setModel:(TKAutoReplyModel *)model {
_model = model;
if (model.keyword == nil && model.replyContent == nil) return;
self.selectBtn.state = model.enable;
self.keywordLabel.stringValue = model.keyword != nil ? model.keyword : @"";
self.replyContentLabel.stringValue = model.replyContent != nil ? model.replyContent : @"";
}
@end
================================================
FILE: WeChatPlugin/Sources/Views/AutoReply/TKAutoReplyContentView.h
================================================
//
// TKAutoReplyContentView.h
// WeChatPlugin
//
// Created by TK on 2017/8/20.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "TKAutoReplyModel.h"
@interface TKAutoReplyContentView : NSView
@property (nonatomic, strong) TKAutoReplyModel *model;
@property (nonatomic, copy) void (^endEdit)(void);
@end
================================================
FILE: WeChatPlugin/Sources/Views/AutoReply/TKAutoReplyContentView.m
================================================
//
// TKAutoReplyContentView.m
// WeChatPlugin
//
// Created by TK on 2017/8/20.
// Copyright © 2017年 tk. All rights reserved.
//
#import "TKAutoReplyContentView.h"
@interface TKAutoReplyContentView () <NSTextFieldDelegate>
@property (nonatomic, strong) NSTextField *keywordLabel;
@property (nonatomic, strong) NSTextField *keywordTextField;
@property (nonatomic, strong) NSTextField *autoReplyLabel;
@property (nonatomic, strong) NSTextField *autoReplyContentField;
@property (nonatomic, strong) NSButton *enableGroupReplyBtn;
@property (nonatomic, strong) NSButton *enableSingleReplyBtn;
@property (nonatomic, strong) NSButton *enableRegexBtn;
@end
@implementation TKAutoReplyContentView
- (instancetype)init {
self = [super init];
if (self) {
[self initSubviews];
}
return self;
}
- (void)initSubviews {
self.enableRegexBtn = ({
NSButton *btn = [NSButton tk_checkboxWithTitle:@"开启正则匹配" target:self action:@selector(clickEnableRegexBtn:)];
btn.frame = NSMakeRect(20, 15, 400, 20);
btn;
});
self.enableGroupReplyBtn = ({
NSButton *btn = [NSButton tk_checkboxWithTitle:@"开启群聊自动回复" target:self action:@selector(clickEnableGroupBtn:)];
btn.frame = NSMakeRect(20, 40, 400, 20);
btn;
});
self.enableSingleReplyBtn = ({
NSButton *btn = [NSButton tk_checkboxWithTitle:@"开启私聊自动回复" target:self action:@selector(clickEnableSingleBtn:)];
btn.frame = NSMakeRect(200, 40, 400, 20);
btn;
});
self.autoReplyContentField = ({
NSTextField *textField = [[NSTextField alloc] init];
textField.frame = NSMakeRect(20, 70, 350, 175);
textField.placeholderString = @"请输入自动回复的内容(‘|’ 为随机回复其中任一内容)";
textField.delegate = self;
textField;
});
self.autoReplyLabel = ({
NSTextField *label = [NSTextField tk_labelWithString:@"自动回复:"];
label.frame = NSMakeRect(20, 250, 350, 20);
label;
});
self.keywordTextField = ({
NSTextField *textField = [[NSTextField alloc] init];
textField.frame = NSMakeRect(20, 290, 350, 50);
textField.placeholderString = @"请输入关键字( ‘*’ 为任何消息都回复,‘|’ 为匹配多个关键字)";
textField.delegate = self;
textField;
});
self.keywordLabel = ({
NSTextField *label = [NSTextField tk_labelWithString:@"关键字:"];
label.frame = NSMakeRect(20, 345, 350, 20);
label;
});
[self addSubviews:@[self.enableRegexBtn,
self.enableGroupReplyBtn,
self.enableSingleReplyBtn,
self.autoReplyContentField,
self.autoReplyLabel,
self.keywordTextField,
self.keywordLabel]];
}
- (void)clickEnableRegexBtn:(NSButton *)btn {
self.model.enableRegex = btn.state;
}
- (void)clickEnableGroupBtn:(NSButton *)btn {
self.model.enableGroupReply = btn.state;
if (btn.state) {
self.model.enable = YES;
} else if(!self.model.enableSingleReply) {
self.model.enable = NO;
}
if (self.endEdit) self.endEdit();
}
- (void)clickEnableSingleBtn:(NSButton *)btn {
self.model.enableSingleReply = btn.state;
if (btn.state) {
self.model.enable = YES;
} else if(!self.model.enableGroupReply) {
self.model.enable = NO;
}
if (self.endEdit) self.endEdit();
}
- (void)viewDidMoveToSuperview {
[super viewDidMoveToSuperview];
self.layer.backgroundColor = [kBG2 CGColor];
self.layer.borderWidth = 1;
self.layer.borderColor = [TK_RGBA(0, 0, 0, 0.1) CGColor];
self.layer.cornerRadius = 3;
self.layer.masksToBounds = YES;
[self.layer setNeedsDisplay];
}
- (void)setModel:(TKAutoReplyModel *)model {
_model = model;
self.keywordTextField.stringValue = model.keyword != nil ? model.keyword : @"";
self.autoReplyContentField.stringValue = model.replyContent != nil ? model.replyContent : @"";
self.enableGroupReplyBtn.state = model.enableGroupReply;
self.enableSingleReplyBtn.state = model.enableSingleReply;
self.enableRegexBtn.state = model.enableRegex;
}
- (void)controlTextDidEndEditing:(NSNotification *)notification {
if (self.endEdit) self.endEdit();
}
- (void)controlTextDidChange:(NSNotification *)notification {
NSControl *control = notification.object;
if (control == self.keywordTextField) {
self.model.keyword = self.keywordTextField.stringValue;
} else if (control == self.autoReplyContentField) {
self.model.replyContent = self.autoReplyContentField.stringValue;
}
}
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
BOOL result = NO;
if (commandSelector == @selector(insertNewline:)) {
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
} else if (commandSelector == @selector(insertTab:)) {
if (control == self.keywordTextField) {
[self.autoReplyContentField becomeFirstResponder];
} else if (control == self.autoReplyContentField) {
[self.keywordTextField becomeFirstResponder];
}
}
return result;
}
@end
================================================
FILE: WeChatPlugin/Sources/Views/RemoteControl/TKRemoteControlCell.h
================================================
//
// TKRemoteControlCell.h
// WeChatPlugin
//
// Created by TK on 2017/8/8.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface TKRemoteControlCell : NSView
- (void)setupWithData:(id)data;
@end
================================================
FILE: WeChatPlugin/Sources/Views/RemoteControl/TKRemoteControlCell.m
================================================
//
// TKRemoteControlCell.m
// WeChatPlugin
//
// Created by TK on 2017/8/8.
// Copyright © 2017年 tk. All rights reserved.
//
#import "TKRemoteControlCell.h"
#import "TKRemoteControlModel.h"
@interface TKRemoteControlCell () <NSTextFieldDelegate>
@property (nonatomic, strong) NSButton *selectBtn;
@property (nonatomic, strong) NSTextField *textField;
@property (nonatomic, strong) TKRemoteControlModel *model;
@end
@implementation TKRemoteControlCell
- (instancetype)init {
self = [super init];
if (self) {
[self initSubviews];
}
return self;
}
- (void)initSubviews {
self.selectBtn = ({
NSButton *btn = [NSButton tk_checkboxWithTitle:@"" target:self action:@selector(clickSelectBtn:)];
btn.frame = NSMakeRect(50, 10, 150, 30);
btn;
});
self.textField = ({
NSTextField *v = [[NSTextField alloc] init];
v.frame = NSMakeRect(200, 10, 250, 30);
v.placeholderString = @"请输入匹配的关键词";
v.layer.cornerRadius = 10;
v.layer.masksToBounds = YES;
[v.layer setNeedsDisplay];
v.editable = YES;
v.delegate = self;
v;
});
[self addSubviews:@[self.selectBtn, self.textField]];
}
- (void)clickSelectBtn:(NSButton *)btn {
self.model.enable = btn.state;
}
- (void)setupWithData:(id)data {
TKRemoteControlModel *model = data;
self.model = model;
self.selectBtn.title = model.function;
self.selectBtn.state = model.enable;
self.textField.stringValue = model.keyword;
}
- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor {
NSString *string = control.stringValue;
self.model.keyword = string;
return YES;
}
@end
================================================
FILE: WeChatPlugin/Sources/WindowControllers/AutoReply/TKAutoReplyWindowController.h
================================================
//
// TKAutoReplyWindowController.h
// WeChatPlugin
//
// Created by TK on 2017/4/19.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "TKAutoReplyModel.h"
@interface TKAutoReplyWindowController : NSWindowController
@property (nonatomic, copy) TKAutoReplyModel *model;
@end
================================================
FILE: WeChatPlugin/Sources/WindowControllers/AutoReply/TKAutoReplyWindowController.m
================================================
//
// TKAutoReplyWindowController.m
// WeChatPlugin
//
// Created by TK on 2017/4/19.
// Copyright © 2017年 tk. All rights reserved.
//
#import "TKAutoReplyWindowController.h"
#import "TKAutoReplyContentView.h"
#import "WeChatPlugin.h"
#import "TKAutoReplyCell.h"
@interface TKAutoReplyWindowController () <NSWindowDelegate, NSTableViewDelegate, NSTableViewDataSource>
@property (nonatomic, strong) NSTableView *tableView;
@property (nonatomic, strong) TKAutoReplyContentView *contentView;
@property (nonatomic, strong) NSButton *addButton;
@property (nonatomic, strong) NSButton *reduceButton;
@property (nonatomic, strong) NSAlert *alert;
@property (nonatomic, strong) NSMutableArray *autoReplyModels;
@property (nonatomic, assign) NSInteger lastSelectIndex;
@end
@implementation TKAutoReplyWindowController
- (void)windowDidLoad {
[super windowDidLoad];
[self initSubviews];
[self setup];
}
- (void)showWindow:(id)sender {
[super showWindow:sender];
[self.tableView reloadData];
[self.contentView setHidden:YES];
if (self.autoReplyModels && self.autoReplyModels.count == 0) {
[self addModel];
}
if (self.autoReplyModels.count > 0 && self.tableView) {
[self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:self.autoReplyModels.count - 1] byExtendingSelection:YES];
}
}
- (void)initSubviews {
NSScrollView *scrollView = ({
NSScrollView *scrollView = [[NSScrollView alloc] init];
scrollView.hasVerticalScroller = YES;
scrollView.frame = NSMakeRect(30, 50, 200, 375);
scrollView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
scrollView;
});
self.tableView = ({
NSTableView *tableView = [[NSTableView alloc] init];
tableView.frame = scrollView.bounds;
tableView.allowsTypeSelect = YES;
tableView.delegate = self;
tableView.dataSource = self;
NSTableColumn *column = [[NSTableColumn alloc] init];
column.title = @"自动回复列表";
column.width = 200;
[tableView addTableColumn:column];
tableView;
});
self.contentView = ({
TKAutoReplyContentView *contentView = [[TKAutoReplyContentView alloc] init];
contentView.frame = NSMakeRect(250, 50, 400, 375);
contentView.hidden = YES;
contentView;
});
self.addButton = ({
NSButton *btn = [NSButton tk_buttonWithTitle:@"+" target:self action:@selector(addModel)];
btn.frame = NSMakeRect(30, 10, 40, 40);
btn.bezelStyle = NSBezelStyleTexturedRounded;
btn;
});
self.reduceButton = ({
NSButton *btn = [NSButton tk_buttonWithTitle:@"-" target:self action:@selector(reduceModel)];
btn.frame = NSMakeRect(80, 10, 40, 40);
btn.bezelStyle = NSBezelStyleTexturedRounded;
btn.enabled = NO;
btn;
});
self.alert = ({
NSAlert *alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:@"确定"];
[alert setMessageText:@"您还有一条自动回复设置未完成"];
[alert setInformativeText:@"请完善未完成的自动回复设置"];
alert;
});
scrollView.contentView.documentView = self.tableView;
[self.window.contentView addSubviews:@[scrollView,
self.contentView,
self.addButton,
self.reduceButton]];
}
- (void)setup {
self.window.title = @"自动回复设置";
self.window.contentView.layer.backgroundColor = [kBG1 CGColor];
[self.window.contentView.layer setNeedsDisplay];
self.lastSelectIndex = -1;
self.autoReplyModels = [[TKWeChatPluginConfig sharedConfig] autoReplyModels];
[self.tableView reloadData];
__weak typeof(self) weakSelf = self;
self.contentView.endEdit = ^(void) {
[weakSelf.tableView reloadData];
if (weakSelf.lastSelectIndex != -1) {
[weakSelf.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:weakSelf.lastSelectIndex] byExtendingSelection:YES];
}
};
}
/**
关闭窗口事件
*/
- (BOOL)windowShouldClose:(id)sender {
[[TKWeChatPluginConfig sharedConfig] saveAutoReplyModels];
return YES;
}
#pragma mark - addButton & reduceButton ClickAction
- (void)addModel {
if (self.contentView.hidden) {
self.contentView.hidden = NO;
}
__block NSInteger emptyModelIndex = -1;
[self.autoReplyModels enumerateObjectsUsingBlock:^(TKAutoReplyModel *model, NSUInteger idx, BOOL * _Nonnull stop) {
if (model.hasEmptyKeywordOrReplyContent) {
emptyModelIndex = idx;
*stop = YES;
}
}];
if (self.autoReplyModels.count > 0 && emptyModelIndex != -1) {
[self.alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
if(returnCode == NSAlertFirstButtonReturn){
if (self.tableView.selectedRow != -1) {
[self.tableView deselectRow:self.tableView.selectedRow];
}
[self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:emptyModelIndex] byExtendingSelection:YES];
}
}];
return;
};
TKAutoReplyModel *model = [[TKAutoReplyModel alloc] init];
[self.autoReplyModels addObject:model];
[self.tableView reloadData];
self.contentView.model = model;
[self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:self.autoReplyModels.count - 1] byExtendingSelection:YES];
}
- (void)reduceModel {
NSInteger index = self.tableView.selectedRow;
if (index > -1) {
[self.autoReplyModels removeObjectAtIndex:index];
[self.tableView reloadData];
if (self.autoReplyModels.count == 0) {
self.contentView.hidden = YES;
self.reduceButton.enabled = NO;
} else {
[self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:self.autoReplyModels.count - 1] byExtendingSelection:YES];
}
}
}
#pragma mark - NSTableViewDataSource && NSTableViewDelegate
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return self.autoReplyModels.count;
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
TKAutoReplyCell *cell = [[TKAutoReplyCell alloc] init];
cell.frame = NSMakeRect(0, 0, self.tableView.frame.size.width, 40);
cell.model = self.autoReplyModels[row];
__weak typeof(self) weakSelf = self;
cell.updateModel = ^{
weakSelf.contentView.model = weakSelf.autoReplyModels[row];
};
return cell;
}
- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row {
return 50;
}
- (void)tableViewSelectionDidChange:(NSNotification *)notification {
NSTableView *tableView = notification.object;
self.contentView.hidden = tableView.selectedRow == -1;
self.reduceButton.enabled = tableView.selectedRow != -1;
if (tableView.selectedRow != -1) {
TKAutoReplyModel *model = self.autoReplyModels[tableView.selectedRow];
self.contentView.model = model;
self.lastSelectIndex = tableView.selectedRow;
__block NSInteger emptyModelIndex = -1;
[self.autoReplyModels enumerateObjectsUsingBlock:^(TKAutoReplyModel *model, NSUInteger idx, BOOL * _Nonnull stop) {
if (model.hasEmptyKeywordOrReplyContent) {
emptyModelIndex = idx;
*stop = YES;
}
}];
if (emptyModelIndex != -1 && tableView.selectedRow != emptyModelIndex) {
[self.alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
if(returnCode == NSAlertFirstButtonReturn){
if (self.tableView.selectedRow != -1) {
[self.tableView deselectRow:self.tableView.selectedRow];
}
[self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:emptyModelIndex] byExtendingSelection:YES];
}
}];
}
}
}
@end
================================================
FILE: WeChatPlugin/Sources/WindowControllers/AutoReply/TKAutoReplyWindowController.xib
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12120" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12120"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="TKAutoReplyWindowController">
<connections>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="668" height="452"/>
<rect key="screenRect" x="0.0" y="0.0" width="1280" height="778"/>
<view key="contentView" wantsLayer="YES" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="668" height="452"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
<point key="canvasLocation" x="-52.5" y="145.5"/>
</window>
</objects>
</document>
================================================
FILE: WeChatPlugin/Sources/WindowControllers/RemoteControl/TKRemoteControlWindowController.h
================================================
//
// TKRemoteControlWindowController.h
// WeChatPlugin
//
// Created by TK on 2017/8/8.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface TKRemoteControlWindowController : NSWindowController
@end
================================================
FILE: WeChatPlugin/Sources/WindowControllers/RemoteControl/TKRemoteControlWindowController.m
================================================
//
// TKRemoteControlWindowController.m
// WeChatPlugin
//
// Created by TK on 2017/8/8.
// Copyright © 2017年 tk. All rights reserved.
//
#import "TKRemoteControlWindowController.h"
#import "TKRemoteControlModel.h"
#import "TKWeChatPluginConfig.h"
#import "TKRemoteControlCell.h"
@interface TKRemoteControlWindowController () <NSWindowDelegate, NSTabViewDelegate, NSTableViewDelegate, NSTableViewDataSource>
@property (nonatomic, weak) IBOutlet NSTabView *tabView;
@property (nonatomic, strong) NSTableView *tableView;
@property (nonatomic, strong) NSArray *remoteControlModels;
@end
@implementation TKRemoteControlWindowController
- (void)windowDidLoad {
[super windowDidLoad];
[self setup];
[self initSubviews];
}
- (void)initSubviews {
CGFloat tabViewWidth = self.tabView.frame.size.width;
CGFloat tabViewHeight = self.tabView.frame.size.height;
self.tableView = ({
NSTableView *tableView = [[NSTableView alloc] init];
tableView.frame = NSMakeRect(50, 50, tabViewWidth, tabViewHeight);
tableView.delegate = self;
tableView.dataSource = self;
NSTableColumn *column = [[NSTableColumn alloc] init];
column.width = tabViewWidth - 100;
[tableView addTableColumn:column];
tableView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleNone;
tableView.backgroundColor = [NSColor clearColor];
tableView;
});
[self.tabView addSubview:self.tableView];
}
- (void)setup {
self.window.contentView.layer.backgroundColor = [NSColor whiteColor].CGColor;
[self.window.contentView.layer setNeedsDisplay];
self.remoteControlModels = [[TKWeChatPluginConfig sharedConfig] remoteControlModels][0];
}
/**
关闭窗口事件
*/
- (BOOL)windowShouldClose:(id)sender {
[[TKWeChatPluginConfig sharedConfig] saveRemoteControlModels];
return YES;
}
#pragma mark - NSTableViewDataSource && NSTableViewDelegate
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
return self.remoteControlModels.count;
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
TKRemoteControlCell *cell = [[TKRemoteControlCell alloc] init];
cell.frame = NSMakeRect(0, 0, self.tabView.frame.size.width, 40);
[cell setupWithData:self.remoteControlModels[row]];
return cell;
}
- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row{
return 50;
}
#pragma mark - NSTabViewDelegate
- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem {
NSInteger selectTabIndex = [tabViewItem.identifier integerValue];
self.remoteControlModels = [[TKWeChatPluginConfig sharedConfig] remoteControlModels][selectTabIndex];
[self.tableView reloadData];
}
@end
================================================
FILE: WeChatPlugin/Sources/WindowControllers/RemoteControl/TKRemoteControlWindowController.xib
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13196" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13196"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="TKRemoteControlWindowController">
<connections>
<outlet property="tabView" destination="WoN-94-FsZ" id="RoF-ij-bGy"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="远程控制设置" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="670" height="520"/>
<rect key="screenRect" x="0.0" y="0.0" width="1280" height="778"/>
<view key="contentView" wantsLayer="YES" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="670" height="520"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<tabView fixedFrame="YES" drawsBackground="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WoN-94-FsZ">
<rect key="frame" x="20" y="43" width="630" height="445"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<font key="font" metaFont="system"/>
<tabViewItems>
<tabViewItem label="macbook" identifier="0" id="sNP-nR-n56">
<view key="view" ambiguous="YES" id="IAk-5N-FY9">
<rect key="frame" x="10" y="33" width="610" height="399"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Lv9-ri-gWd">
<rect key="frame" x="143" y="17" width="325" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="手机端发送“获取指令”,获取所有远程控制指令信息。" id="JDf-Rp-jEj">
<font key="font" size="13" name=".PingFangSC-Medium"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</view>
</tabViewItem>
<tabViewItem label="app控制" identifier="1" id="kWv-JR-wGj">
<view key="view" id="iVA-JL-GJu">
<rect key="frame" x="10" y="33" width="610" height="399"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
</tabViewItem>
<tabViewItem label="网易云音乐" identifier="2" id="pJO-yQ-9vs">
<view key="view" id="FCO-eB-j0J">
<rect key="frame" x="10" y="33" width="610" height="399"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kwl-L5-YwP">
<rect key="frame" x="26" y="17" width="559" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="若想使用该功能,请于“系统偏好设置 ==> 安全性与隐私 ==> 隐私 ==> 辅助功能”中添加微信" id="qpH-CW-UVl">
<font key="font" size="13" name=".PingFangSC-Medium"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</view>
</tabViewItem>
</tabViewItems>
<connections>
<outlet property="delegate" destination="-2" id="gLJ-15-ouy"/>
</connections>
</tabView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="J1L-Ly-Rmw">
<rect key="frame" x="25" y="16" width="60" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⚠️注意:" id="pyh-Mz-b2h">
<font key="font" size="13" name="AppleColorEmoji"/>
<color key="textColor" red="0.95428575227915313" green="0.054907046790266048" blue="0.027834490462353378" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Koj-RI-ARh">
<rect key="frame" x="77" y="20" width="495" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="为避免误触,远程控制仅在向自己发送指令有效,他人发送或者发送给他人均无效。" id="EiN-zX-6I8">
<font key="font" size="13" name=".PingFangSC-Medium"/>
<color key="textColor" name="controlShadowColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</view>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
</window>
</objects>
</document>
================================================
FILE: WeChatPlugin/TKRemoteControlCommands.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<array>
<dict>
<key>executeCommand</key>
<string>open /System/Library/Frameworks/ScreenSaver.framework/Versions/A/Resources/ScreenSaverEngine.app || open /System/Library/CoreServices/ScreenSaverEngine.app </string>
<key>keyword</key>
<string>ScreenSave</string>
<key>function</key>
<string>屏幕保护</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>/System/Library/CoreServices/Menu\ Extras/User.menu/Contents/Resources/CGSession -suspend</string>
<key>keyword</key>
<string>LockScreen</string>
<key>function</key>
<string>锁屏</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>sleep</string>
<key>keyword</key>
<string>Sleep</string>
<key>function</key>
<string>休眠</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>shutdown</string>
<key>keyword</key>
<string>Shutdown</string>
<key>function</key>
<string>关机</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>restart</string>
<key>keyword</key>
<string>Restart</string>
<key>function</key>
<string>重启</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>empty</string>
<key>keyword</key>
<string>EmptyTrash</string>
<key>function</key>
<string>清空废纸篓</string>
<key>enable</key>
<true/>
</dict>
</array>
<array>
<dict>
<key>executeCommand</key>
<string>killQQ</string>
<key>keyword</key>
<string>KillQQ</string>
<key>function</key>
<string>退出 QQ</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>killWeChat</string>
<key>keyword</key>
<string>KillWeChat</string>
<key>function</key>
<string>退出 WeChat</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>killChrome</string>
<key>keyword</key>
<string>KillChrome</string>
<key>function</key>
<string>退出 Chrome</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>killSafari</string>
<key>keyword</key>
<string>KillSafari</string>
<key>function</key>
<string>退出 Safari</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>killAll</string>
<key>keyword</key>
<string>KillAll</string>
<key>function</key>
<string>退出所有程序</string>
<key>enable</key>
<true/>
</dict>
</array>
<array>
<dict>
<key>executeCommand</key>
<string>musicToggle</string>
<key>keyword</key>
<string>Toggle</string>
<key>function</key>
<string>播放/暂停</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicNext</string>
<key>keyword</key>
<string>Next</string>
<key>function</key>
<string>下一首</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicPrevious</string>
<key>keyword</key>
<string>Previous</string>
<key>function</key>
<string>上一首</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicVolumeUp</string>
<key>keyword</key>
<string>VolumeUp</string>
<key>function</key>
<string>增大音量</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicVolumeDown</string>
<key>keyword</key>
<string>VolumeDown</string>
<key>function</key>
<string>减小音量</string>
<key>enable</key>
<true/>
</dict>
<dict>
<key>executeCommand</key>
<string>musicLike</string>
<key>keyword</key>
<string>LikeChange</string>
<key>function</key>
<string>喜欢/取消喜欢</string>
<key>enable</key>
<true/>
</dict>
</array>
</array>
</plist>
================================================
FILE: WeChatPlugin/WeChatPlugin.h
================================================
//
// WeChatPlugin.h
// WeChatPlugin
//
// Created by TK on 2017/4/19.
// Copyright © 2017年 tk. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "TKWeChatPluginConfig.h"
#import "TKHelper.h"
FOUNDATION_EXPORT double WeChatPluginVersionNumber;
FOUNDATION_EXPORT const unsigned char WeChatPluginVersionString[];
#pragma mark - 微信原始的部分类与方法
@interface MMLoginOneClickViewController : NSViewController
@property(nonatomic) NSTextField *descriptionLabel;
- (void)onLoginButtonClicked:(id)arg1;
@property(nonatomic) NSButton *loginButton;
@end
@interface AccountService : NSObject
- (id)GetLastLoginUserName;
- (id)GetLastLoginAutoAuthKey;
- (BOOL)canAutoAuth;
- (void)AutoAuth;
- (void)ManualLogin:(id)arg1 withPassword:(id)arg2;
- (void)ManualLogout;
- (void)QRCodeLoginWithUserName:(id)arg1 password:(id)arg2;
@end
@interface MMLoginViewController : NSObject
@property(retain, nonatomic) MMLoginOneClickViewController *oneClickViewController;
@end
@interface MMMainWindowController : NSWindowController
@property(retain, nonatomic) MMLoginViewController *loginViewController;
- (void)onAuthOK;
- (void)onLogOut;
@end
@interface MessageService : NSObject
- (void)onRevokeMsg:(id)arg1;
- (void)OnSyncBatchAddMsgs:(NSArray *)arg1 isFirstSync:(BOOL)arg2;
- (id)SendTextMessage:(id)arg1 toUsrName:(id)arg2 msgText:(id)arg3 atUserList:(id)arg4;
- (id)GetMsgData:(id)arg1 svrId:(long)arg2;
- (void)AddLocalMsg:(id)arg1 msgData:(id)arg2;
- (void)TranscribeVoiceMessage:(id)arg1 completion:(void (^)(void))arg2;
- (BOOL)ClearUnRead:(id)arg1 FromID:(unsigned int)arg2 ToID:(unsigned int)arg3;
@end
@interface MMServiceCenter : NSObject
+ (id)defaultCenter;
- (id)getService:(Class)arg1;
@end
@interface SKBuiltinString_t : NSObject
@property(retain, nonatomic, setter=SetString:) NSString *string; // @synthesize string;
@end
@interface AddMsg : NSObject
@property(retain, nonatomic, setter=SetContent:) SKBuiltinString_t *content; // @synthesize content;
@property(retain, nonatomic, setter=SetFromUserName:) SKBuiltinString_t *fromUserName; // @synthesize fromUserName;
@property(nonatomic, setter=SetMsgType:) int msgType; // @synthesize msgType;
@property(retain, nonatomic, setter=SetToUserName:) SKBuiltinString_t *toUserName; // @synthesize toUserName;
@property (nonatomic, assign) unsigned int createTime;
@property(nonatomic, setter=SetNewMsgId:) long long newMsgId;
@end
@interface MMChatsViewController : NSViewController <NSTableViewDataSource, NSTableViewDelegate>
@property(nonatomic) __weak NSTableView *tableView;
@end
@interface WeChat : NSObject
+ (id)sharedInstance;
@property(nonatomic) MMChatsViewController *chatsViewController;
@property(retain, nonatomic) MMMainWindowController *mainWindowController;
@property(nonatomic) BOOL isAppTerminating;
- (void)startANewChatWithContact:(id)arg1;
- (void)onAuthOK:(BOOL)arg1;
@end
@interface ContactStorage : NSObject
- (id)GetSelfContact;
- (id)GetContact:(id)arg1;
@end
@interface WCContactData : NSObject
@property(retain, nonatomic) NSString *m_nsUsrName; // @synthesize m_nsUsrName;
@property(nonatomic) unsigned int m_uiFriendScene; // @synthesize m_uiFriendScene;
@property(retain, nonatomic) NSString *m_nsNickName; // 用户昵称
@property(retain, nonatomic) NSString *m_nsRemark; // 备注
@property(retain, nonatomic) NSString *m_nsHeadImgUrl; // 头像
- (BOOL)isBrandContact;
- (BOOL)isSelf;
- (id)getGroupDisplayName;
@end
@interface MessageData : NSObject
- (id)initWithMsgType:(long long)arg1;
@property(retain, nonatomic) NSString *fromUsrName;
@property(retain, nonatomic) NSString *toUsrName;
@property(retain, nonatomic) NSString *msgContent;
@property(retain, nonatomic) NSString *msgPushContent;
@property(nonatomic) int messageType;
@property(nonatomic) int msgStatus;
@property(nonatomic) int msgCreateTime;
@property(nonatomic) int mesLocalID;
@property(nonatomic) long long mesSvrID;
@property(retain, nonatomic) NSString *msgVoiceText;
@property(copy, nonatomic) NSString *m_nsEmoticonMD5;
- (BOOL)isChatRoomMessage;
- (id)groupChatSenderDisplayName;
- (id)getRealMessageContent;
- (BOOL)isSendFromSelf;
- (BOOL)isCustomEmojiMsg;
- (BOOL)isImgMsg;
- (BOOL)isVideoMsg;
- (BOOL)isVoiceMsg;
@end
@interface CUtility : NSObject
+ (BOOL)HasWechatInstance;
+ (unsigned long long)getFreeDiskSpace;
+ (void)ReloadSessionForMsgSync;
+ (id)GetCurrentUserName;
@end
@interface MMSessionInfo : NSObject
@property(nonatomic) BOOL m_bIsTop; // @synthesize m_bIsTop;
@property(nonatomic) BOOL m_bShowUnReadAsRedDot;
@property(nonatomic) BOOL m_isMentionedUnread; // @synthesize
@property(retain, nonatomic) NSString *m_nsUserName; // @synthesize m_nsUserName;
@property(retain, nonatomic) WCContactData *m_contact;
@end
@protocol MMChatsTableCellViewDelegate <NSObject>
@optional
- (void)cellViewReloadData:(MMSessionInfo *)arg1;
@end
@interface MMChatsTableCellView : NSTableCellView
@property(nonatomic) __weak id <MMChatsTableCellViewDelegate> delegate;
@property(retain, nonatomic) MMS
gitextract_xb1v15h_/
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── Other/
│ ├── Install.sh
│ ├── Products/
│ │ └── Debug/
│ │ └── WeChatPlugin.framework/
│ │ ├── Headers/
│ │ │ └── WeChatPlugin.h
│ │ ├── Modules/
│ │ │ └── module.modulemap
│ │ ├── Resources/
│ │ │ ├── Info.plist
│ │ │ ├── TKAutoReplyWindowController.nib
│ │ │ ├── TKRemoteControlCommands.plist
│ │ │ ├── TKRemoteControlScript.scpt
│ │ │ └── TKRemoteControlWindowController.nib
│ │ ├── Versions/
│ │ │ ├── A/
│ │ │ │ ├── Headers/
│ │ │ │ │ └── WeChatPlugin.h
│ │ │ │ ├── Modules/
│ │ │ │ │ └── module.modulemap
│ │ │ │ ├── Resources/
│ │ │ │ │ ├── Info.plist
│ │ │ │ │ ├── TKAutoReplyWindowController.nib
│ │ │ │ │ ├── TKRemoteControlCommands.plist
│ │ │ │ │ ├── TKRemoteControlScript.scpt
│ │ │ │ │ └── TKRemoteControlWindowController.nib
│ │ │ │ └── WeChatPlugin
│ │ │ └── Current/
│ │ │ ├── Headers/
│ │ │ │ └── WeChatPlugin.h
│ │ │ ├── Modules/
│ │ │ │ └── module.modulemap
│ │ │ ├── Resources/
│ │ │ │ ├── Info.plist
│ │ │ │ ├── TKAutoReplyWindowController.nib
│ │ │ │ ├── TKRemoteControlCommands.plist
│ │ │ │ ├── TKRemoteControlScript.scpt
│ │ │ │ └── TKRemoteControlWindowController.nib
│ │ │ └── WeChatPlugin
│ │ └── WeChatPlugin
│ ├── Uninstall.sh
│ └── insert_dylib
├── Podfile
├── README.md
├── WeChatPlugin/
│ ├── Info.plist
│ ├── Sources/
│ │ ├── Category/
│ │ │ ├── MMChatsTableCellView+hook.h
│ │ │ ├── MMChatsTableCellView+hook.m
│ │ │ ├── MMStickerMessageCellView+hook.h
│ │ │ ├── MMStickerMessageCellView+hook.m
│ │ │ ├── WeChat+hook.h
│ │ │ └── WeChat+hook.m
│ │ ├── Common/
│ │ │ ├── Category/
│ │ │ │ ├── NSButton+Action.h
│ │ │ │ ├── NSButton+Action.m
│ │ │ │ ├── NSTextField+Action.h
│ │ │ │ ├── NSTextField+Action.m
│ │ │ │ ├── NSView+Action.h
│ │ │ │ └── NSView+Action.m
│ │ │ ├── Color.h
│ │ │ └── TKPrefixHeader.pch
│ │ ├── Config/
│ │ │ ├── TKWeChatPluginConfig.h
│ │ │ └── TKWeChatPluginConfig.m
│ │ ├── Controllers/
│ │ │ ├── TKRemoteControlController.h
│ │ │ └── TKRemoteControlController.m
│ │ ├── Models/
│ │ │ ├── TKAutoReplyModel.h
│ │ │ ├── TKAutoReplyModel.m
│ │ │ ├── TKBaseModel.h
│ │ │ ├── TKBaseModel.m
│ │ │ ├── TKIgnoreSessonModel.h
│ │ │ ├── TKIgnoreSessonModel.m
│ │ │ ├── TKRemoteControlModel.h
│ │ │ └── TKRemoteControlModel.m
│ │ ├── Utils/
│ │ │ ├── TKHelper.h
│ │ │ ├── TKHelper.m
│ │ │ ├── TKVersionManager.h
│ │ │ ├── TKVersionManager.m
│ │ │ ├── TKWebServerManager.h
│ │ │ ├── TKWebServerManager.m
│ │ │ ├── XMLReader.h
│ │ │ └── XMLReader.m
│ │ ├── Vendor/
│ │ │ ├── fishhook.c
│ │ │ └── fishhook.h
│ │ ├── Views/
│ │ │ ├── AutoReply/
│ │ │ │ ├── TKAutoReplyCell.h
│ │ │ │ ├── TKAutoReplyCell.m
│ │ │ │ ├── TKAutoReplyContentView.h
│ │ │ │ └── TKAutoReplyContentView.m
│ │ │ └── RemoteControl/
│ │ │ ├── TKRemoteControlCell.h
│ │ │ └── TKRemoteControlCell.m
│ │ └── WindowControllers/
│ │ ├── AutoReply/
│ │ │ ├── TKAutoReplyWindowController.h
│ │ │ ├── TKAutoReplyWindowController.m
│ │ │ └── TKAutoReplyWindowController.xib
│ │ └── RemoteControl/
│ │ ├── TKRemoteControlWindowController.h
│ │ ├── TKRemoteControlWindowController.m
│ │ └── TKRemoteControlWindowController.xib
│ ├── TKRemoteControlCommands.plist
│ ├── TKRemoteControlScript.scpt
│ ├── WeChatPlugin.h
│ └── main.mm
├── WeChatPlugin.xcodeproj/
│ ├── project.pbxproj
│ ├── project.xcworkspace/
│ │ └── contents.xcworkspacedata
│ └── xcuserdata/
│ └── TK.xcuserdatad/
│ ├── xcdebugger/
│ │ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes/
│ ├── WeChatPlugin.xcscheme
│ └── xcschememanagement.plist
└── WeChatPlugin.xcworkspace/
└── contents.xcworkspacedata
SYMBOL INDEX (21 symbols across 4 files)
FILE: WeChatPlugin/Sources/Utils/TKVersionManager.h
type TKVersionStatusOld (line 11) | typedef NS_ENUM(NSUInteger, TKVersionStatus) {
FILE: WeChatPlugin/Sources/Utils/XMLReader.h
type NSUInteger (line 16) | typedef NSUInteger XMLReaderOptions;
FILE: WeChatPlugin/Sources/Vendor/fishhook.c
type mach_header_t (line 35) | typedef struct mach_header_64 mach_header_t;
type segment_command_t (line 36) | typedef struct segment_command_64 segment_command_t;
type section_t (line 37) | typedef struct section_64 section_t;
type nlist_t (line 38) | typedef struct nlist_64 nlist_t;
type mach_header_t (line 41) | typedef struct mach_header mach_header_t;
type segment_command_t (line 42) | typedef struct segment_command segment_command_t;
type section_t (line 43) | typedef struct section section_t;
type nlist_t (line 44) | typedef struct nlist nlist_t;
type rebindings_entry (line 52) | struct rebindings_entry {
type rebindings_entry (line 58) | struct rebindings_entry
function prepend_rebindings (line 60) | static int prepend_rebindings(struct rebindings_entry **rebindings_head,
function perform_rebinding_with_section (line 79) | static void perform_rebinding_with_section(struct rebindings_entry *rebi...
function rebind_symbols_for_image (line 116) | static void rebind_symbols_for_image(struct rebindings_entry *rebindings,
function _rebind_symbols_for_image (line 178) | static void _rebind_symbols_for_image(const struct mach_header *header,
function rebind_symbols_image (line 183) | int rebind_symbols_image(void *header,
function rebind_symbols (line 194) | int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) {
FILE: WeChatPlugin/Sources/Vendor/fishhook.h
type rebinding (line 44) | struct rebinding {
type rebinding (line 59) | struct rebinding
type rebinding (line 68) | struct rebinding
Condensed preview — 91 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (266K chars).
[
{
"path": ".gitignore",
"chars": 68,
"preview": "# Xcode\n#\n.DS_Store\n*.xcuserstate\n\n# CocoaPods\n#\nPods/\nPodfile.lock\n"
},
{
"path": "CHANGELOG.md",
"chars": 1341,
"preview": "# 更新日志 \n\n### [v1.6 (2018-03-18)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases/tag/v1.6)\n\n* 新增 Alfred 快捷发送消息"
},
{
"path": "LICENSE",
"chars": 1059,
"preview": "MIT License\n\nCopyright (c) 2017 TK\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this"
},
{
"path": "Other/Install.sh",
"chars": 938,
"preview": "#!/bin/bash\n\napp_name=\"WeChat\"\nshell_path=\"$(dirname \"$0\")\"\nwechat_path=\"/Applications/WeChat.app\"\nframework_name=\"WeCha"
},
{
"path": "Other/Products/Debug/WeChatPlugin.framework/Headers/WeChatPlugin.h",
"chars": 7458,
"preview": "//\n// WeChatPlugin.h\n// WeChatPlugin\n//\n// Created by TK on 2017/4/19.\n// Copyright © 2017年 tk. All rights reserved."
},
{
"path": "Other/Products/Debug/WeChatPlugin.framework/Modules/module.modulemap",
"chars": 105,
"preview": "framework module WeChatPlugin {\n umbrella header \"WeChatPlugin.h\"\n\n export *\n module * { export * }\n}\n"
},
{
"path": "Other/Products/Debug/WeChatPlugin.framework/Resources/Info.plist",
"chars": 1686,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Other/Products/Debug/WeChatPlugin.framework/Resources/TKRemoteControlCommands.plist",
"chars": 4046,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Other/Products/Debug/WeChatPlugin.framework/Versions/A/Headers/WeChatPlugin.h",
"chars": 7458,
"preview": "//\n// WeChatPlugin.h\n// WeChatPlugin\n//\n// Created by TK on 2017/4/19.\n// Copyright © 2017年 tk. All rights reserved."
},
{
"path": "Other/Products/Debug/WeChatPlugin.framework/Versions/A/Modules/module.modulemap",
"chars": 105,
"preview": "framework module WeChatPlugin {\n umbrella header \"WeChatPlugin.h\"\n\n export *\n module * { export * }\n}\n"
},
{
"path": "Other/Products/Debug/WeChatPlugin.framework/Versions/A/Resources/Info.plist",
"chars": 1686,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Other/Products/Debug/WeChatPlugin.framework/Versions/A/Resources/TKRemoteControlCommands.plist",
"chars": 4046,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Other/Products/Debug/WeChatPlugin.framework/Versions/Current/Headers/WeChatPlugin.h",
"chars": 7458,
"preview": "//\n// WeChatPlugin.h\n// WeChatPlugin\n//\n// Created by TK on 2017/4/19.\n// Copyright © 2017年 tk. All rights reserved."
},
{
"path": "Other/Products/Debug/WeChatPlugin.framework/Versions/Current/Modules/module.modulemap",
"chars": 105,
"preview": "framework module WeChatPlugin {\n umbrella header \"WeChatPlugin.h\"\n\n export *\n module * { export * }\n}\n"
},
{
"path": "Other/Products/Debug/WeChatPlugin.framework/Versions/Current/Resources/Info.plist",
"chars": 1686,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Other/Products/Debug/WeChatPlugin.framework/Versions/Current/Resources/TKRemoteControlCommands.plist",
"chars": 4046,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Other/Uninstall.sh",
"chars": 515,
"preview": "# !/bin/bash\n\napp_name=\"WeChat\"\nframework_name=\"WeChatPlugin\"\napp_bundle_path=\"/Applications/${app_name}.app/Contents/Ma"
},
{
"path": "Podfile",
"chars": 108,
"preview": "platform :osx, '10.12'\ninhibit_all_warnings!\n\ntarget 'WeChatPlugin' do\n pod 'GCDWebServer', '~> 3.4.2'\nend\n"
},
{
"path": "README.md",
"chars": 6427,
"preview": "\n## WeChatPlugin-MacOS\n\n [ 2013, Facebook, Inc.\n// All rights reserved.\n// Redistribution and use in source and binary forms, with"
},
{
"path": "WeChatPlugin/Sources/Vendor/fishhook.h",
"chars": 3059,
"preview": "// Copyright (c) 2013, Facebook, Inc.\n// All rights reserved.\n// Redistribution and use in source and binary forms, with"
},
{
"path": "WeChatPlugin/Sources/Views/AutoReply/TKAutoReplyCell.h",
"chars": 338,
"preview": "//\n// TKAutoReplyCell.h\n// WeChatPlugin\n//\n// Created by TK on 2017/8/21.\n// Copyright © 2017年 tk. All rights reserv"
},
{
"path": "WeChatPlugin/Sources/Views/AutoReply/TKAutoReplyCell.m",
"chars": 2542,
"preview": "//\n// TKAutoReplyCell.m\n// WeChatPlugin\n//\n// Created by TK on 2017/8/21.\n// Copyright © 2017年 tk. All rights reserv"
},
{
"path": "WeChatPlugin/Sources/Views/AutoReply/TKAutoReplyContentView.h",
"chars": 345,
"preview": "//\n// TKAutoReplyContentView.h\n// WeChatPlugin\n//\n// Created by TK on 2017/8/20.\n// Copyright © 2017年 tk. All rights"
},
{
"path": "WeChatPlugin/Sources/Views/AutoReply/TKAutoReplyContentView.m",
"chars": 5321,
"preview": "//\n// TKAutoReplyContentView.m\n// WeChatPlugin\n//\n// Created by TK on 2017/8/20.\n// Copyright © 2017年 tk. All rights"
},
{
"path": "WeChatPlugin/Sources/Views/RemoteControl/TKRemoteControlCell.h",
"chars": 235,
"preview": "//\n// TKRemoteControlCell.h\n// WeChatPlugin\n//\n// Created by TK on 2017/8/8.\n// Copyright © 2017年 tk. All rights res"
},
{
"path": "WeChatPlugin/Sources/Views/RemoteControl/TKRemoteControlCell.m",
"chars": 1736,
"preview": "//\n// TKRemoteControlCell.m\n// WeChatPlugin\n//\n// Created by TK on 2017/8/8.\n// Copyright © 2017年 tk. All rights res"
},
{
"path": "WeChatPlugin/Sources/WindowControllers/AutoReply/TKAutoReplyWindowController.h",
"chars": 314,
"preview": "//\n// TKAutoReplyWindowController.h\n// WeChatPlugin\n//\n// Created by TK on 2017/4/19.\n// Copyright © 2017年 tk. All r"
},
{
"path": "WeChatPlugin/Sources/WindowControllers/AutoReply/TKAutoReplyWindowController.m",
"chars": 8204,
"preview": "//\n// TKAutoReplyWindowController.m\n// WeChatPlugin\n//\n// Created by TK on 2017/4/19.\n// Copyright © 2017年 tk. All r"
},
{
"path": "WeChatPlugin/Sources/WindowControllers/AutoReply/TKAutoReplyWindowController.xib",
"chars": 2007,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
},
{
"path": "WeChatPlugin/Sources/WindowControllers/RemoteControl/TKRemoteControlWindowController.h",
"chars": 238,
"preview": "//\n// TKRemoteControlWindowController.h\n// WeChatPlugin\n//\n// Created by TK on 2017/8/8.\n// Copyright © 2017年 tk. Al"
},
{
"path": "WeChatPlugin/Sources/WindowControllers/RemoteControl/TKRemoteControlWindowController.m",
"chars": 2802,
"preview": "//\n// TKRemoteControlWindowController.m\n// WeChatPlugin\n//\n// Created by TK on 2017/8/8.\n// Copyright © 2017年 tk. Al"
},
{
"path": "WeChatPlugin/Sources/WindowControllers/RemoteControl/TKRemoteControlWindowController.xib",
"chars": 8399,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
},
{
"path": "WeChatPlugin/TKRemoteControlCommands.plist",
"chars": 4046,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "WeChatPlugin/WeChatPlugin.h",
"chars": 7458,
"preview": "//\n// WeChatPlugin.h\n// WeChatPlugin\n//\n// Created by TK on 2017/4/19.\n// Copyright © 2017年 tk. All rights reserved."
},
{
"path": "WeChatPlugin/main.mm",
"chars": 484,
"preview": "//\n// main.c\n// WeChatPlugin\n//\n// Created by TK on 2017/4/19.\n// Copyright © 2017年 tk. All rights reserved.\n//\n\n#im"
},
{
"path": "WeChatPlugin.xcodeproj/project.pbxproj",
"chars": 42194,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "WeChatPlugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 157,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:WeChatPlugin.xc"
},
{
"path": "WeChatPlugin.xcodeproj/xcuserdata/TK.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist",
"chars": 91,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Bucket\n type = \"1\"\n version = \"2.0\">\n</Bucket>\n"
},
{
"path": "WeChatPlugin.xcodeproj/xcuserdata/TK.xcuserdatad/xcschemes/WeChatPlugin.xcscheme",
"chars": 3114,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"0920\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "WeChatPlugin.xcodeproj/xcuserdata/TK.xcuserdatad/xcschemes/xcschememanagement.plist",
"chars": 484,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "WeChatPlugin.xcworkspace/contents.xcworkspacedata",
"chars": 230,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"group:WeChatPlugin.x"
}
]
// ... and 14 more files (download for full content)
About this extraction
This page contains the full source code of the liyongmeng123/TKkk-iOSer-WeChatPlugin-MacOS GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 91 files (232.5 KB), approximately 71.5k tokens, and a symbol index with 21 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.