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 #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 @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 @optional - (void)cellViewReloadData:(MMSessionInfo *)arg1; @end @interface MMChatsTableCellView : NSTableCellView @property(nonatomic) __weak id 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 *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 ================================================ BuildMachineOSBuild 17C88 CFBundleDevelopmentRegion en CFBundleExecutable WeChatPlugin CFBundleIdentifier tk.WeChatPlugin CFBundleInfoDictionaryVersion 6.0 CFBundleName WeChatPlugin CFBundlePackageType FMWK CFBundleShortVersionString 1.6 CFBundleSupportedPlatforms MacOSX CFBundleVersion 1 DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild 9C40b DTPlatformVersion GM DTSDKBuild 17C76 DTSDKName macosx10.13 DTXcode 0920 DTXcodeBuild 9C40b NSHumanReadableCopyright Copyright © 2017年 tk. All rights reserved. versionInfo 👉 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 ================================================ FILE: Other/Products/Debug/WeChatPlugin.framework/Resources/TKRemoteControlCommands.plist ================================================ executeCommand open /System/Library/Frameworks/ScreenSaver.framework/Versions/A/Resources/ScreenSaverEngine.app || open /System/Library/CoreServices/ScreenSaverEngine.app keyword ScreenSave function 屏幕保护 enable executeCommand /System/Library/CoreServices/Menu\ Extras/User.menu/Contents/Resources/CGSession -suspend keyword LockScreen function 锁屏 enable executeCommand sleep keyword Sleep function 休眠 enable executeCommand shutdown keyword Shutdown function 关机 enable executeCommand restart keyword Restart function 重启 enable executeCommand empty keyword EmptyTrash function 清空废纸篓 enable executeCommand killQQ keyword KillQQ function 退出 QQ enable executeCommand killWeChat keyword KillWeChat function 退出 WeChat enable executeCommand killChrome keyword KillChrome function 退出 Chrome enable executeCommand killSafari keyword KillSafari function 退出 Safari enable executeCommand killAll keyword KillAll function 退出所有程序 enable executeCommand musicToggle keyword Toggle function 播放/暂停 enable executeCommand musicNext keyword Next function 下一首 enable executeCommand musicPrevious keyword Previous function 上一首 enable executeCommand musicVolumeUp keyword VolumeUp function 增大音量 enable executeCommand musicVolumeDown keyword VolumeDown function 减小音量 enable executeCommand musicLike keyword LikeChange function 喜欢/取消喜欢 enable ================================================ 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 #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 @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 @optional - (void)cellViewReloadData:(MMSessionInfo *)arg1; @end @interface MMChatsTableCellView : NSTableCellView @property(nonatomic) __weak id 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 *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 ================================================ BuildMachineOSBuild 17C88 CFBundleDevelopmentRegion en CFBundleExecutable WeChatPlugin CFBundleIdentifier tk.WeChatPlugin CFBundleInfoDictionaryVersion 6.0 CFBundleName WeChatPlugin CFBundlePackageType FMWK CFBundleShortVersionString 1.6 CFBundleSupportedPlatforms MacOSX CFBundleVersion 1 DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild 9C40b DTPlatformVersion GM DTSDKBuild 17C76 DTSDKName macosx10.13 DTXcode 0920 DTXcodeBuild 9C40b NSHumanReadableCopyright Copyright © 2017年 tk. All rights reserved. versionInfo 👉 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 ================================================ FILE: Other/Products/Debug/WeChatPlugin.framework/Versions/A/Resources/TKRemoteControlCommands.plist ================================================ executeCommand open /System/Library/Frameworks/ScreenSaver.framework/Versions/A/Resources/ScreenSaverEngine.app || open /System/Library/CoreServices/ScreenSaverEngine.app keyword ScreenSave function 屏幕保护 enable executeCommand /System/Library/CoreServices/Menu\ Extras/User.menu/Contents/Resources/CGSession -suspend keyword LockScreen function 锁屏 enable executeCommand sleep keyword Sleep function 休眠 enable executeCommand shutdown keyword Shutdown function 关机 enable executeCommand restart keyword Restart function 重启 enable executeCommand empty keyword EmptyTrash function 清空废纸篓 enable executeCommand killQQ keyword KillQQ function 退出 QQ enable executeCommand killWeChat keyword KillWeChat function 退出 WeChat enable executeCommand killChrome keyword KillChrome function 退出 Chrome enable executeCommand killSafari keyword KillSafari function 退出 Safari enable executeCommand killAll keyword KillAll function 退出所有程序 enable executeCommand musicToggle keyword Toggle function 播放/暂停 enable executeCommand musicNext keyword Next function 下一首 enable executeCommand musicPrevious keyword Previous function 上一首 enable executeCommand musicVolumeUp keyword VolumeUp function 增大音量 enable executeCommand musicVolumeDown keyword VolumeDown function 减小音量 enable executeCommand musicLike keyword LikeChange function 喜欢/取消喜欢 enable ================================================ 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 #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 @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 @optional - (void)cellViewReloadData:(MMSessionInfo *)arg1; @end @interface MMChatsTableCellView : NSTableCellView @property(nonatomic) __weak id 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 *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 ================================================ BuildMachineOSBuild 17C88 CFBundleDevelopmentRegion en CFBundleExecutable WeChatPlugin CFBundleIdentifier tk.WeChatPlugin CFBundleInfoDictionaryVersion 6.0 CFBundleName WeChatPlugin CFBundlePackageType FMWK CFBundleShortVersionString 1.6 CFBundleSupportedPlatforms MacOSX CFBundleVersion 1 DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild 9C40b DTPlatformVersion GM DTSDKBuild 17C76 DTSDKName macosx10.13 DTXcode 0920 DTXcodeBuild 9C40b NSHumanReadableCopyright Copyright © 2017年 tk. All rights reserved. versionInfo 👉 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 ================================================ FILE: Other/Products/Debug/WeChatPlugin.framework/Versions/Current/Resources/TKRemoteControlCommands.plist ================================================ executeCommand open /System/Library/Frameworks/ScreenSaver.framework/Versions/A/Resources/ScreenSaverEngine.app || open /System/Library/CoreServices/ScreenSaverEngine.app keyword ScreenSave function 屏幕保护 enable executeCommand /System/Library/CoreServices/Menu\ Extras/User.menu/Contents/Resources/CGSession -suspend keyword LockScreen function 锁屏 enable executeCommand sleep keyword Sleep function 休眠 enable executeCommand shutdown keyword Shutdown function 关机 enable executeCommand restart keyword Restart function 重启 enable executeCommand empty keyword EmptyTrash function 清空废纸篓 enable executeCommand killQQ keyword KillQQ function 退出 QQ enable executeCommand killWeChat keyword KillWeChat function 退出 WeChat enable executeCommand killChrome keyword KillChrome function 退出 Chrome enable executeCommand killSafari keyword KillSafari function 退出 Safari enable executeCommand killAll keyword KillAll function 退出所有程序 enable executeCommand musicToggle keyword Toggle function 播放/暂停 enable executeCommand musicNext keyword Next function 下一首 enable executeCommand musicPrevious keyword Previous function 上一首 enable executeCommand musicVolumeUp keyword VolumeUp function 增大音量 enable executeCommand musicVolumeDown keyword VolumeDown function 减小音量 enable executeCommand musicLike keyword LikeChange function 喜欢/取消喜欢 enable ================================================ 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 ![platform](https://img.shields.io/badge/platform-macos-lightgrey.svg) [![release](https://img.shields.io/badge/release-v1.6-brightgreen.svg)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/releases) ![support](https://img.shields.io/badge/support-wechat%202.3.10-blue.svg) [![GitHub license](https://img.shields.io/github/license/TKkk-iOSer/WeChatPlugin-MacOS.svg)](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS/blob/master/LICENSE) 微信小助手 v1.6 ![微信小助手.png](./Other/ScreenShots/wechatplugin.png) **相对应的 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 演示 * 消息防撤回 ![消息防撤回.gif](http://upload-images.jianshu.io/upload_images/965383-30cbea645661e627.gif?imageMogr2/auto-orient/strip) * 自动回复 ![自动回复.gif](http://upload-images.jianshu.io/upload_images/965383-d488dce3696ba1b3.gif?imageMogr2/auto-orient/strip) * 微信多开 ![微信多开.gif](http://upload-images.jianshu.io/upload_images/965383-51d8eae02d48fda9.gif?imageMogr2/auto-orient/strip) * 远程控制 (测试关闭Chrome、QQ、开启屏幕保护) ![远程控制.gif](http://upload-images.jianshu.io/upload_images/965383-0cf50d9b22b02f2f.gif?imageMogr2/auto-orient/strip) * 免认证 & 置底 & 多选删除 ![免认证&置底&多选删除](http://upload-images.jianshu.io/upload_images/965383-170592b03781cbf4.gif?imageMogr2/auto-orient/strip) * 通知中心快捷回复 ![快捷回复](./Other/ScreenShots/notification_quick_reply.gif) * 聊天窗口表情复制 & 存储 * 语音远程控制 mac ![语音远程控制](./Other/ScreenShots/voice_remote_control.gif) * Alfred 快速搜索 [wechat-alfred-workflow](https://github.com/TKkk-iOSer/wechat-alfred-workflow) ![alfred](./Other/ScreenShots/alfred_search.gif) --- ### 安装 ~~第一次安装需要输入密码,仅是为了获取写入微信文件夹的权限~~ **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` ![Permission denied.png](http://upload-images.jianshu.io/upload_images/965383-11e4480553ba086e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) * 下载 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. 安装完成** * 登录微信,在**菜单栏**中看到**微信小助手**即安装成功。 ![微信小助手.png](http://upload-images.jianshu.io/upload_images/965383-31708af611b55ca4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) --- ### 使用 * 消息防撤回:点击`开启消息防撤回`或者快捷键`command + t`,即可开启、关闭。 * 自动回复:点击`开启自动回复`或者快捷键`conmand + k`,将弹出自动回复设置的窗口,点击红色箭头的按钮设置开关。 >若关键字为 `*`,则任何信息都回复; >若关键字为`x|y`,则 x 和 y 都回复; >若关键字**或者**自动回复为空,则不开启该条自动回复。 >若开启正则,请确认正则表达式书写正确,[在线正则表达式测试](http://tool.oschina.net/regex/) ![自动回复设置.png](http://upload-images.jianshu.io/upload_images/965383-5aa2fd8fadc545c4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) * 微信多开:点击`登录新微信`或者快捷键`command + shift + n`,即可多开微信。 * 远程控制:点击`远程控制 Mac OS`或者快捷键`command + shift + c`,即可打开控制窗口。 ![.png](http://upload-images.jianshu.io/upload_images/965383-9c67894ee7092600.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ①为选择是否开启远程控制此功能。 ②为能够触发远程控制的消息内容(仅向自己发送账号有效)。 * 远程控制:发送`获取指令`,手机端可查看所有指令信息。 ![远程控制.png](http://upload-images.jianshu.io/upload_images/965383-7c2a4b17e5a6867f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) * 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 --- #### 听说你想请我喝下午茶?😏     ================================================ FILE: WeChatPlugin/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString 1.6 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSHumanReadableCopyright Copyright © 2017年 tk. All rights reserved. NSPrincipalClass versionInfo 👉 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 ================================================ 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 @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 @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 @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:@" 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 *(*original_NSSearchPathForDirectoriesInDomains)(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde); NSArray *swizzled_NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde) { NSMutableArray *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 @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 @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 @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 @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 @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 @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 #import @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 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 @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 #import #import @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 *contactResult, NSArray *brandSult, NSArray *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 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 + (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 #import #import #import #import #import #import #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 #include #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 #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 #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 () @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 @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 () @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 #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 () @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 ================================================ ================================================ FILE: WeChatPlugin/Sources/WindowControllers/RemoteControl/TKRemoteControlWindowController.h ================================================ // // TKRemoteControlWindowController.h // WeChatPlugin // // Created by TK on 2017/8/8. // Copyright © 2017年 tk. All rights reserved. // #import @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 () @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 ================================================ ================================================ FILE: WeChatPlugin/TKRemoteControlCommands.plist ================================================ executeCommand open /System/Library/Frameworks/ScreenSaver.framework/Versions/A/Resources/ScreenSaverEngine.app || open /System/Library/CoreServices/ScreenSaverEngine.app keyword ScreenSave function 屏幕保护 enable executeCommand /System/Library/CoreServices/Menu\ Extras/User.menu/Contents/Resources/CGSession -suspend keyword LockScreen function 锁屏 enable executeCommand sleep keyword Sleep function 休眠 enable executeCommand shutdown keyword Shutdown function 关机 enable executeCommand restart keyword Restart function 重启 enable executeCommand empty keyword EmptyTrash function 清空废纸篓 enable executeCommand killQQ keyword KillQQ function 退出 QQ enable executeCommand killWeChat keyword KillWeChat function 退出 WeChat enable executeCommand killChrome keyword KillChrome function 退出 Chrome enable executeCommand killSafari keyword KillSafari function 退出 Safari enable executeCommand killAll keyword KillAll function 退出所有程序 enable executeCommand musicToggle keyword Toggle function 播放/暂停 enable executeCommand musicNext keyword Next function 下一首 enable executeCommand musicPrevious keyword Previous function 上一首 enable executeCommand musicVolumeUp keyword VolumeUp function 增大音量 enable executeCommand musicVolumeDown keyword VolumeDown function 减小音量 enable executeCommand musicLike keyword LikeChange function 喜欢/取消喜欢 enable ================================================ FILE: WeChatPlugin/WeChatPlugin.h ================================================ // // WeChatPlugin.h // WeChatPlugin // // Created by TK on 2017/4/19. // Copyright © 2017年 tk. All rights reserved. // #import #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 @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 @optional - (void)cellViewReloadData:(MMSessionInfo *)arg1; @end @interface MMChatsTableCellView : NSTableCellView @property(nonatomic) __weak id 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 *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: WeChatPlugin/main.mm ================================================ // // main.c // WeChatPlugin // // Created by TK on 2017/4/19. // Copyright © 2017年 tk. All rights reserved. // #import #import "WeChat+hook.h" #import "MMChatsTableCellView+hook.h" #import "MMStickerMessageCellView+hook.h" static void __attribute__((constructor)) initialize(void) { NSLog(@"++++++++ WeChatPlugin loaded ++++++++"); [NSObject hookWeChat]; [NSObject hookMMChatsTableCellView]; [NSObject hookMMStickerMessageCellView]; } ================================================ FILE: WeChatPlugin.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 520F2009205E1DF900A36B1E /* TKWebServerManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 520F2007205E1DF900A36B1E /* TKWebServerManager.h */; }; 520F200A205E1DF900A36B1E /* TKWebServerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 520F2008205E1DF900A36B1E /* TKWebServerManager.m */; }; 522547CF1F4AE5A700226A2A /* NSView+Action.h in Headers */ = {isa = PBXBuildFile; fileRef = 522547CD1F4AE5A700226A2A /* NSView+Action.h */; }; 522547D01F4AE5A700226A2A /* NSView+Action.m in Sources */ = {isa = PBXBuildFile; fileRef = 522547CE1F4AE5A700226A2A /* NSView+Action.m */; }; 522547DE1F4C312A00226A2A /* TKAutoReplyWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = 522547D71F4C312A00226A2A /* TKAutoReplyWindowController.h */; }; 522547DF1F4C312A00226A2A /* TKAutoReplyWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 522547D81F4C312A00226A2A /* TKAutoReplyWindowController.m */; }; 522547E01F4C312A00226A2A /* TKAutoReplyWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 522547D91F4C312A00226A2A /* TKAutoReplyWindowController.xib */; }; 522547E11F4C312A00226A2A /* TKRemoteControlWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = 522547DB1F4C312A00226A2A /* TKRemoteControlWindowController.h */; }; 522547E21F4C312A00226A2A /* TKRemoteControlWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 522547DC1F4C312A00226A2A /* TKRemoteControlWindowController.m */; }; 522547E31F4C312A00226A2A /* TKRemoteControlWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 522547DD1F4C312A00226A2A /* TKRemoteControlWindowController.xib */; }; 5226D5CA2040FC3E0013C048 /* TKVersionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 5226D5C82040FC3E0013C048 /* TKVersionManager.h */; }; 5226D5CB2040FC3E0013C048 /* TKVersionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5226D5C92040FC3E0013C048 /* TKVersionManager.m */; }; 5228EABA1F70B59500D808CF /* NSButton+Action.h in Headers */ = {isa = PBXBuildFile; fileRef = 5228EAB81F70B59500D808CF /* NSButton+Action.h */; }; 5228EABB1F70B59500D808CF /* NSButton+Action.m in Sources */ = {isa = PBXBuildFile; fileRef = 5228EAB91F70B59500D808CF /* NSButton+Action.m */; }; 523848AB1F70E73500771BA3 /* NSTextField+Action.h in Headers */ = {isa = PBXBuildFile; fileRef = 523848A91F70E73500771BA3 /* NSTextField+Action.h */; }; 523848AC1F70E73500771BA3 /* NSTextField+Action.m in Sources */ = {isa = PBXBuildFile; fileRef = 523848AA1F70E73500771BA3 /* NSTextField+Action.m */; }; 52475C2E1F6B993E00760E3E /* MMChatsTableCellView+hook.h in Headers */ = {isa = PBXBuildFile; fileRef = 52475C2C1F6B993E00760E3E /* MMChatsTableCellView+hook.h */; }; 52475C2F1F6B993E00760E3E /* MMChatsTableCellView+hook.m in Sources */ = {isa = PBXBuildFile; fileRef = 52475C2D1F6B993E00760E3E /* MMChatsTableCellView+hook.m */; }; 52475C321F6BA43800760E3E /* TKIgnoreSessonModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 52475C301F6BA43800760E3E /* TKIgnoreSessonModel.h */; }; 52475C331F6BA43800760E3E /* TKIgnoreSessonModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 52475C311F6BA43800760E3E /* TKIgnoreSessonModel.m */; }; 526F4D7D1F4AAADA00E46004 /* Color.h in Headers */ = {isa = PBXBuildFile; fileRef = 526F4D791F4AAADA00E46004 /* Color.h */; }; 526F4D801F4AAADA00E46004 /* TKPrefixHeader.pch in Headers */ = {isa = PBXBuildFile; fileRef = 526F4D7C1F4AAADA00E46004 /* TKPrefixHeader.pch */; }; 526F4D831F4AAC1900E46004 /* TKAutoReplyModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 526F4D811F4AAC1900E46004 /* TKAutoReplyModel.h */; }; 526F4D841F4AAC1900E46004 /* TKAutoReplyModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 526F4D821F4AAC1900E46004 /* TKAutoReplyModel.m */; }; 526F4D8B1F4AAC3800E46004 /* TKAutoReplyContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 526F4D861F4AAC3800E46004 /* TKAutoReplyContentView.h */; }; 526F4D8C1F4AAC3800E46004 /* TKAutoReplyContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 526F4D871F4AAC3800E46004 /* TKAutoReplyContentView.m */; }; 526F4D8D1F4AAC3800E46004 /* TKRemoteControlCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 526F4D891F4AAC3800E46004 /* TKRemoteControlCell.h */; }; 526F4D8E1F4AAC3800E46004 /* TKRemoteControlCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 526F4D8A1F4AAC3800E46004 /* TKRemoteControlCell.m */; }; 526F4D911F4AAE6F00E46004 /* TKAutoReplyCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 526F4D8F1F4AAE6F00E46004 /* TKAutoReplyCell.h */; }; 526F4D921F4AAE6F00E46004 /* TKAutoReplyCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 526F4D901F4AAE6F00E46004 /* TKAutoReplyCell.m */; }; 527E7F622018711800358D26 /* fishhook.c in Sources */ = {isa = PBXBuildFile; fileRef = 527E7F602018711800358D26 /* fishhook.c */; }; 527E7F632018711800358D26 /* fishhook.h in Headers */ = {isa = PBXBuildFile; fileRef = 527E7F612018711800358D26 /* fishhook.h */; }; 528B4F971EA7383800BC6A89 /* WeChatPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 528B4F951EA7383800BC6A89 /* WeChatPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; 528B4FB41EA7386D00BC6A89 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 528B4FAA1EA7386D00BC6A89 /* main.mm */; }; 529CDDFE1F6E6A060056DC1A /* TKBaseModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 529CDDFC1F6E6A060056DC1A /* TKBaseModel.h */; }; 529CDDFF1F6E6A060056DC1A /* TKBaseModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 529CDDFD1F6E6A060056DC1A /* TKBaseModel.m */; }; 52B5CEF21F3B4623000D9DD9 /* TKRemoteControlCommands.plist in Resources */ = {isa = PBXBuildFile; fileRef = 52B5CEF01F3B4623000D9DD9 /* TKRemoteControlCommands.plist */; }; 52B5CEF31F3B4623000D9DD9 /* TKRemoteControlScript.scpt in Resources */ = {isa = PBXBuildFile; fileRef = 52B5CEF11F3B4623000D9DD9 /* TKRemoteControlScript.scpt */; }; 52B5CF121F3B4631000D9DD9 /* WeChat+hook.h in Headers */ = {isa = PBXBuildFile; fileRef = 52B5CEF61F3B4631000D9DD9 /* WeChat+hook.h */; }; 52B5CF131F3B4631000D9DD9 /* WeChat+hook.m in Sources */ = {isa = PBXBuildFile; fileRef = 52B5CEF71F3B4631000D9DD9 /* WeChat+hook.m */; }; 52B5CF141F3B4631000D9DD9 /* TKWeChatPluginConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 52B5CEF91F3B4631000D9DD9 /* TKWeChatPluginConfig.h */; }; 52B5CF151F3B4631000D9DD9 /* TKWeChatPluginConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 52B5CEFA1F3B4631000D9DD9 /* TKWeChatPluginConfig.m */; }; 52B5CF161F3B4631000D9DD9 /* TKRemoteControlController.h in Headers */ = {isa = PBXBuildFile; fileRef = 52B5CEFC1F3B4631000D9DD9 /* TKRemoteControlController.h */; }; 52B5CF171F3B4631000D9DD9 /* TKRemoteControlController.m in Sources */ = {isa = PBXBuildFile; fileRef = 52B5CEFD1F3B4631000D9DD9 /* TKRemoteControlController.m */; }; 52B5CF181F3B4631000D9DD9 /* TKRemoteControlModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 52B5CEFF1F3B4631000D9DD9 /* TKRemoteControlModel.h */; }; 52B5CF191F3B4631000D9DD9 /* TKRemoteControlModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 52B5CF001F3B4631000D9DD9 /* TKRemoteControlModel.m */; }; 52B5CF1A1F3B4631000D9DD9 /* TKHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 52B5CF021F3B4631000D9DD9 /* TKHelper.h */; }; 52B5CF1B1F3B4631000D9DD9 /* TKHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 52B5CF031F3B4631000D9DD9 /* TKHelper.m */; }; 52B5CF1C1F3B4631000D9DD9 /* XMLReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 52B5CF041F3B4631000D9DD9 /* XMLReader.h */; }; 52B5CF1D1F3B4631000D9DD9 /* XMLReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 52B5CF051F3B4631000D9DD9 /* XMLReader.m */; }; 52EE9515203FE58500B76873 /* MMStickerMessageCellView+hook.m in Sources */ = {isa = PBXBuildFile; fileRef = 52EE9513203FE58500B76873 /* MMStickerMessageCellView+hook.m */; }; 52EE9517203FE63300B76873 /* MMStickerMessageCellView+hook.h in Headers */ = {isa = PBXBuildFile; fileRef = 52EE9516203FE63300B76873 /* MMStickerMessageCellView+hook.h */; }; 581E98663A5F0E6327516AA8 /* libPods-WeChatPlugin.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 17F7A08DC0E3A0626801414D /* libPods-WeChatPlugin.a */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 13B308B4A92CE38341762A48 /* Pods-WeChatPlugin.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WeChatPlugin.release.xcconfig"; path = "Pods/Target Support Files/Pods-WeChatPlugin/Pods-WeChatPlugin.release.xcconfig"; sourceTree = ""; }; 17F7A08DC0E3A0626801414D /* libPods-WeChatPlugin.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-WeChatPlugin.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 520F2007205E1DF900A36B1E /* TKWebServerManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TKWebServerManager.h; sourceTree = ""; }; 520F2008205E1DF900A36B1E /* TKWebServerManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TKWebServerManager.m; sourceTree = ""; }; 522547CD1F4AE5A700226A2A /* NSView+Action.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSView+Action.h"; sourceTree = ""; }; 522547CE1F4AE5A700226A2A /* NSView+Action.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSView+Action.m"; sourceTree = ""; }; 522547D71F4C312A00226A2A /* TKAutoReplyWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TKAutoReplyWindowController.h; sourceTree = ""; }; 522547D81F4C312A00226A2A /* TKAutoReplyWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TKAutoReplyWindowController.m; sourceTree = ""; }; 522547D91F4C312A00226A2A /* TKAutoReplyWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TKAutoReplyWindowController.xib; sourceTree = ""; }; 522547DB1F4C312A00226A2A /* TKRemoteControlWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TKRemoteControlWindowController.h; sourceTree = ""; }; 522547DC1F4C312A00226A2A /* TKRemoteControlWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TKRemoteControlWindowController.m; sourceTree = ""; }; 522547DD1F4C312A00226A2A /* TKRemoteControlWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TKRemoteControlWindowController.xib; sourceTree = ""; }; 5226D5C82040FC3E0013C048 /* TKVersionManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TKVersionManager.h; sourceTree = ""; }; 5226D5C92040FC3E0013C048 /* TKVersionManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TKVersionManager.m; sourceTree = ""; }; 5228EAB81F70B59500D808CF /* NSButton+Action.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSButton+Action.h"; sourceTree = ""; }; 5228EAB91F70B59500D808CF /* NSButton+Action.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSButton+Action.m"; sourceTree = ""; }; 523848A91F70E73500771BA3 /* NSTextField+Action.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTextField+Action.h"; sourceTree = ""; }; 523848AA1F70E73500771BA3 /* NSTextField+Action.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTextField+Action.m"; sourceTree = ""; }; 52475C2C1F6B993E00760E3E /* MMChatsTableCellView+hook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MMChatsTableCellView+hook.h"; sourceTree = ""; }; 52475C2D1F6B993E00760E3E /* MMChatsTableCellView+hook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MMChatsTableCellView+hook.m"; sourceTree = ""; }; 52475C301F6BA43800760E3E /* TKIgnoreSessonModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TKIgnoreSessonModel.h; sourceTree = ""; }; 52475C311F6BA43800760E3E /* TKIgnoreSessonModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TKIgnoreSessonModel.m; sourceTree = ""; }; 526F4D791F4AAADA00E46004 /* Color.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Color.h; sourceTree = ""; }; 526F4D7C1F4AAADA00E46004 /* TKPrefixHeader.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TKPrefixHeader.pch; sourceTree = ""; }; 526F4D811F4AAC1900E46004 /* TKAutoReplyModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TKAutoReplyModel.h; sourceTree = ""; }; 526F4D821F4AAC1900E46004 /* TKAutoReplyModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TKAutoReplyModel.m; sourceTree = ""; }; 526F4D861F4AAC3800E46004 /* TKAutoReplyContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TKAutoReplyContentView.h; sourceTree = ""; }; 526F4D871F4AAC3800E46004 /* TKAutoReplyContentView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TKAutoReplyContentView.m; sourceTree = ""; }; 526F4D891F4AAC3800E46004 /* TKRemoteControlCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TKRemoteControlCell.h; sourceTree = ""; }; 526F4D8A1F4AAC3800E46004 /* TKRemoteControlCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TKRemoteControlCell.m; sourceTree = ""; }; 526F4D8F1F4AAE6F00E46004 /* TKAutoReplyCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TKAutoReplyCell.h; sourceTree = ""; }; 526F4D901F4AAE6F00E46004 /* TKAutoReplyCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TKAutoReplyCell.m; sourceTree = ""; }; 527E7F602018711800358D26 /* fishhook.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fishhook.c; sourceTree = ""; }; 527E7F612018711800358D26 /* fishhook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fishhook.h; sourceTree = ""; }; 528B4F921EA7383800BC6A89 /* WeChatPlugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WeChatPlugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 528B4F951EA7383800BC6A89 /* WeChatPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WeChatPlugin.h; sourceTree = ""; }; 528B4F961EA7383800BC6A89 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 528B4FAA1EA7386D00BC6A89 /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; }; 529CDDFC1F6E6A060056DC1A /* TKBaseModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TKBaseModel.h; sourceTree = ""; }; 529CDDFD1F6E6A060056DC1A /* TKBaseModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TKBaseModel.m; sourceTree = ""; }; 52B5CEF01F3B4623000D9DD9 /* TKRemoteControlCommands.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = TKRemoteControlCommands.plist; sourceTree = ""; }; 52B5CEF11F3B4623000D9DD9 /* TKRemoteControlScript.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = TKRemoteControlScript.scpt; sourceTree = ""; }; 52B5CEF61F3B4631000D9DD9 /* WeChat+hook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WeChat+hook.h"; sourceTree = ""; }; 52B5CEF71F3B4631000D9DD9 /* WeChat+hook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WeChat+hook.m"; sourceTree = ""; }; 52B5CEF91F3B4631000D9DD9 /* TKWeChatPluginConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TKWeChatPluginConfig.h; sourceTree = ""; }; 52B5CEFA1F3B4631000D9DD9 /* TKWeChatPluginConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TKWeChatPluginConfig.m; sourceTree = ""; }; 52B5CEFC1F3B4631000D9DD9 /* TKRemoteControlController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TKRemoteControlController.h; sourceTree = ""; }; 52B5CEFD1F3B4631000D9DD9 /* TKRemoteControlController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TKRemoteControlController.m; sourceTree = ""; }; 52B5CEFF1F3B4631000D9DD9 /* TKRemoteControlModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TKRemoteControlModel.h; sourceTree = ""; }; 52B5CF001F3B4631000D9DD9 /* TKRemoteControlModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TKRemoteControlModel.m; sourceTree = ""; }; 52B5CF021F3B4631000D9DD9 /* TKHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TKHelper.h; sourceTree = ""; }; 52B5CF031F3B4631000D9DD9 /* TKHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TKHelper.m; sourceTree = ""; }; 52B5CF041F3B4631000D9DD9 /* XMLReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XMLReader.h; sourceTree = ""; }; 52B5CF051F3B4631000D9DD9 /* XMLReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMLReader.m; sourceTree = ""; }; 52EE9513203FE58500B76873 /* MMStickerMessageCellView+hook.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "MMStickerMessageCellView+hook.m"; sourceTree = ""; }; 52EE9516203FE63300B76873 /* MMStickerMessageCellView+hook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MMStickerMessageCellView+hook.h"; sourceTree = ""; }; 888B2791FA0BBCC192BADA47 /* Pods-WeChatPlugin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WeChatPlugin.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WeChatPlugin/Pods-WeChatPlugin.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 528B4F8E1EA7383800BC6A89 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 581E98663A5F0E6327516AA8 /* libPods-WeChatPlugin.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 13EC3A000F46808B50A62C6F /* Frameworks */ = { isa = PBXGroup; children = ( 17F7A08DC0E3A0626801414D /* libPods-WeChatPlugin.a */, ); name = Frameworks; sourceTree = ""; }; 3C138513A2C7760B47317A95 /* Pods */ = { isa = PBXGroup; children = ( 888B2791FA0BBCC192BADA47 /* Pods-WeChatPlugin.debug.xcconfig */, 13B308B4A92CE38341762A48 /* Pods-WeChatPlugin.release.xcconfig */, ); name = Pods; sourceTree = ""; }; 522547CC1F4AE5A700226A2A /* Category */ = { isa = PBXGroup; children = ( 522547CD1F4AE5A700226A2A /* NSView+Action.h */, 522547CE1F4AE5A700226A2A /* NSView+Action.m */, 5228EAB81F70B59500D808CF /* NSButton+Action.h */, 5228EAB91F70B59500D808CF /* NSButton+Action.m */, 523848A91F70E73500771BA3 /* NSTextField+Action.h */, 523848AA1F70E73500771BA3 /* NSTextField+Action.m */, ); path = Category; sourceTree = ""; }; 522547D11F4AE5AE00226A2A /* View */ = { isa = PBXGroup; children = ( ); path = View; sourceTree = ""; }; 522547D61F4C312A00226A2A /* AutoReply */ = { isa = PBXGroup; children = ( 522547D71F4C312A00226A2A /* TKAutoReplyWindowController.h */, 522547D81F4C312A00226A2A /* TKAutoReplyWindowController.m */, 522547D91F4C312A00226A2A /* TKAutoReplyWindowController.xib */, ); path = AutoReply; sourceTree = ""; }; 522547DA1F4C312A00226A2A /* RemoteControl */ = { isa = PBXGroup; children = ( 522547DB1F4C312A00226A2A /* TKRemoteControlWindowController.h */, 522547DC1F4C312A00226A2A /* TKRemoteControlWindowController.m */, 522547DD1F4C312A00226A2A /* TKRemoteControlWindowController.xib */, ); path = RemoteControl; sourceTree = ""; }; 526F4D781F4AAADA00E46004 /* Common */ = { isa = PBXGroup; children = ( 522547D11F4AE5AE00226A2A /* View */, 522547CC1F4AE5A700226A2A /* Category */, 526F4D791F4AAADA00E46004 /* Color.h */, 526F4D7C1F4AAADA00E46004 /* TKPrefixHeader.pch */, ); path = Common; sourceTree = ""; }; 526F4D851F4AAC3800E46004 /* AutoReply */ = { isa = PBXGroup; children = ( 526F4D861F4AAC3800E46004 /* TKAutoReplyContentView.h */, 526F4D871F4AAC3800E46004 /* TKAutoReplyContentView.m */, 526F4D8F1F4AAE6F00E46004 /* TKAutoReplyCell.h */, 526F4D901F4AAE6F00E46004 /* TKAutoReplyCell.m */, ); path = AutoReply; sourceTree = ""; }; 526F4D881F4AAC3800E46004 /* RemoteControl */ = { isa = PBXGroup; children = ( 526F4D891F4AAC3800E46004 /* TKRemoteControlCell.h */, 526F4D8A1F4AAC3800E46004 /* TKRemoteControlCell.m */, ); path = RemoteControl; sourceTree = ""; }; 527E7F5F2018711800358D26 /* Vendor */ = { isa = PBXGroup; children = ( 527E7F602018711800358D26 /* fishhook.c */, 527E7F612018711800358D26 /* fishhook.h */, ); path = Vendor; sourceTree = ""; }; 528B4F881EA7383800BC6A89 = { isa = PBXGroup; children = ( 528B4F941EA7383800BC6A89 /* WeChatPlugin */, 528B4F931EA7383800BC6A89 /* Products */, 3C138513A2C7760B47317A95 /* Pods */, 13EC3A000F46808B50A62C6F /* Frameworks */, ); sourceTree = ""; }; 528B4F931EA7383800BC6A89 /* Products */ = { isa = PBXGroup; children = ( 528B4F921EA7383800BC6A89 /* WeChatPlugin.framework */, ); name = Products; sourceTree = ""; }; 528B4F941EA7383800BC6A89 /* WeChatPlugin */ = { isa = PBXGroup; children = ( 52B5CEF41F3B4631000D9DD9 /* Sources */, 52B5CEEF1F3B45ED000D9DD9 /* Supporting Files */, ); path = WeChatPlugin; sourceTree = ""; }; 52B5CEEF1F3B45ED000D9DD9 /* Supporting Files */ = { isa = PBXGroup; children = ( 528B4FAA1EA7386D00BC6A89 /* main.mm */, 528B4F951EA7383800BC6A89 /* WeChatPlugin.h */, 528B4F961EA7383800BC6A89 /* Info.plist */, 52B5CEF01F3B4623000D9DD9 /* TKRemoteControlCommands.plist */, 52B5CEF11F3B4623000D9DD9 /* TKRemoteControlScript.scpt */, ); name = "Supporting Files"; sourceTree = ""; }; 52B5CEF41F3B4631000D9DD9 /* Sources */ = { isa = PBXGroup; children = ( 527E7F5F2018711800358D26 /* Vendor */, 526F4D781F4AAADA00E46004 /* Common */, 52B5CEF51F3B4631000D9DD9 /* Category */, 52B5CEF81F3B4631000D9DD9 /* Config */, 52B5CEFB1F3B4631000D9DD9 /* Controllers */, 52B5CEFE1F3B4631000D9DD9 /* Models */, 52B5CF011F3B4631000D9DD9 /* Utils */, 52B5CF061F3B4631000D9DD9 /* Views */, 52B5CF091F3B4631000D9DD9 /* WindowControllers */, ); path = Sources; sourceTree = ""; }; 52B5CEF51F3B4631000D9DD9 /* Category */ = { isa = PBXGroup; children = ( 52B5CEF61F3B4631000D9DD9 /* WeChat+hook.h */, 52B5CEF71F3B4631000D9DD9 /* WeChat+hook.m */, 52EE9516203FE63300B76873 /* MMStickerMessageCellView+hook.h */, 52EE9513203FE58500B76873 /* MMStickerMessageCellView+hook.m */, 52475C2C1F6B993E00760E3E /* MMChatsTableCellView+hook.h */, 52475C2D1F6B993E00760E3E /* MMChatsTableCellView+hook.m */, ); path = Category; sourceTree = ""; }; 52B5CEF81F3B4631000D9DD9 /* Config */ = { isa = PBXGroup; children = ( 52B5CEF91F3B4631000D9DD9 /* TKWeChatPluginConfig.h */, 52B5CEFA1F3B4631000D9DD9 /* TKWeChatPluginConfig.m */, ); path = Config; sourceTree = ""; }; 52B5CEFB1F3B4631000D9DD9 /* Controllers */ = { isa = PBXGroup; children = ( 52B5CEFC1F3B4631000D9DD9 /* TKRemoteControlController.h */, 52B5CEFD1F3B4631000D9DD9 /* TKRemoteControlController.m */, ); path = Controllers; sourceTree = ""; }; 52B5CEFE1F3B4631000D9DD9 /* Models */ = { isa = PBXGroup; children = ( 529CDDFC1F6E6A060056DC1A /* TKBaseModel.h */, 529CDDFD1F6E6A060056DC1A /* TKBaseModel.m */, 526F4D811F4AAC1900E46004 /* TKAutoReplyModel.h */, 526F4D821F4AAC1900E46004 /* TKAutoReplyModel.m */, 52B5CEFF1F3B4631000D9DD9 /* TKRemoteControlModel.h */, 52B5CF001F3B4631000D9DD9 /* TKRemoteControlModel.m */, 52475C301F6BA43800760E3E /* TKIgnoreSessonModel.h */, 52475C311F6BA43800760E3E /* TKIgnoreSessonModel.m */, ); path = Models; sourceTree = ""; }; 52B5CF011F3B4631000D9DD9 /* Utils */ = { isa = PBXGroup; children = ( 52B5CF021F3B4631000D9DD9 /* TKHelper.h */, 52B5CF031F3B4631000D9DD9 /* TKHelper.m */, 52B5CF041F3B4631000D9DD9 /* XMLReader.h */, 52B5CF051F3B4631000D9DD9 /* XMLReader.m */, 5226D5C82040FC3E0013C048 /* TKVersionManager.h */, 5226D5C92040FC3E0013C048 /* TKVersionManager.m */, 520F2007205E1DF900A36B1E /* TKWebServerManager.h */, 520F2008205E1DF900A36B1E /* TKWebServerManager.m */, ); path = Utils; sourceTree = ""; }; 52B5CF061F3B4631000D9DD9 /* Views */ = { isa = PBXGroup; children = ( 526F4D851F4AAC3800E46004 /* AutoReply */, 526F4D881F4AAC3800E46004 /* RemoteControl */, ); path = Views; sourceTree = ""; }; 52B5CF091F3B4631000D9DD9 /* WindowControllers */ = { isa = PBXGroup; children = ( 522547D61F4C312A00226A2A /* AutoReply */, 522547DA1F4C312A00226A2A /* RemoteControl */, ); path = WindowControllers; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 528B4F8F1EA7383800BC6A89 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 522547DE1F4C312A00226A2A /* TKAutoReplyWindowController.h in Headers */, 5228EABA1F70B59500D808CF /* NSButton+Action.h in Headers */, 526F4D7D1F4AAADA00E46004 /* Color.h in Headers */, 527E7F632018711800358D26 /* fishhook.h in Headers */, 528B4F971EA7383800BC6A89 /* WeChatPlugin.h in Headers */, 526F4D801F4AAADA00E46004 /* TKPrefixHeader.pch in Headers */, 529CDDFE1F6E6A060056DC1A /* TKBaseModel.h in Headers */, 52B5CF1A1F3B4631000D9DD9 /* TKHelper.h in Headers */, 52B5CF1C1F3B4631000D9DD9 /* XMLReader.h in Headers */, 52B5CF161F3B4631000D9DD9 /* TKRemoteControlController.h in Headers */, 52EE9517203FE63300B76873 /* MMStickerMessageCellView+hook.h in Headers */, 523848AB1F70E73500771BA3 /* NSTextField+Action.h in Headers */, 52B5CF121F3B4631000D9DD9 /* WeChat+hook.h in Headers */, 52475C321F6BA43800760E3E /* TKIgnoreSessonModel.h in Headers */, 526F4D8D1F4AAC3800E46004 /* TKRemoteControlCell.h in Headers */, 52B5CF181F3B4631000D9DD9 /* TKRemoteControlModel.h in Headers */, 5226D5CA2040FC3E0013C048 /* TKVersionManager.h in Headers */, 520F2009205E1DF900A36B1E /* TKWebServerManager.h in Headers */, 522547CF1F4AE5A700226A2A /* NSView+Action.h in Headers */, 526F4D911F4AAE6F00E46004 /* TKAutoReplyCell.h in Headers */, 52475C2E1F6B993E00760E3E /* MMChatsTableCellView+hook.h in Headers */, 526F4D8B1F4AAC3800E46004 /* TKAutoReplyContentView.h in Headers */, 522547E11F4C312A00226A2A /* TKRemoteControlWindowController.h in Headers */, 526F4D831F4AAC1900E46004 /* TKAutoReplyModel.h in Headers */, 52B5CF141F3B4631000D9DD9 /* TKWeChatPluginConfig.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 528B4F911EA7383800BC6A89 /* WeChatPlugin */ = { isa = PBXNativeTarget; buildConfigurationList = 528B4F9A1EA7383800BC6A89 /* Build configuration list for PBXNativeTarget "WeChatPlugin" */; buildPhases = ( A23204925F0A37A0EBE14B92 /* [CP] Check Pods Manifest.lock */, 528B4F8D1EA7383800BC6A89 /* Sources */, 528B4F8E1EA7383800BC6A89 /* Frameworks */, 528B4F8F1EA7383800BC6A89 /* Headers */, 528B4F901EA7383800BC6A89 /* Resources */, 528B4FB51EA738B100BC6A89 /* ShellScript */, 165746E0A28B760DEA538E0D /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = WeChatPlugin; productName = WeChatPlugin; productReference = 528B4F921EA7383800BC6A89 /* WeChatPlugin.framework */; productType = "com.apple.product-type.framework"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 528B4F891EA7383800BC6A89 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0920; ORGANIZATIONNAME = tk; TargetAttributes = { 528B4F911EA7383800BC6A89 = { CreatedOnToolsVersion = 8.2.1; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 528B4F8C1EA7383800BC6A89 /* Build configuration list for PBXProject "WeChatPlugin" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = 528B4F881EA7383800BC6A89; productRefGroup = 528B4F931EA7383800BC6A89 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 528B4F911EA7383800BC6A89 /* WeChatPlugin */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 528B4F901EA7383800BC6A89 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 522547E01F4C312A00226A2A /* TKAutoReplyWindowController.xib in Resources */, 52B5CEF21F3B4623000D9DD9 /* TKRemoteControlCommands.plist in Resources */, 52B5CEF31F3B4623000D9DD9 /* TKRemoteControlScript.scpt in Resources */, 522547E31F4C312A00226A2A /* TKRemoteControlWindowController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 165746E0A28B760DEA538E0D /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-WeChatPlugin/Pods-WeChatPlugin-resources.sh\"\n"; showEnvVarsInLog = 0; }; 528B4FB51EA738B100BC6A89 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "#!/bin/bash\napp_name=\"WeChat\"\nframework_name=\"WeChatPlugin\"\napp_bundle_path=\"/Applications/${app_name}.app/Contents/MacOS\"\napp_executable_path=\"${app_bundle_path}/${app_name}\"\napp_executable_backup_path=\"${app_executable_path}_backup\"\nframework_path=\"${app_bundle_path}/${framework_name}.framework\"\n# 备份WeChat原始可执行文件\nif [ ! -f \"$app_executable_backup_path\" ]\nthen\ncp \"$app_executable_path\" \"$app_executable_backup_path\"\nfi\n\nrm -rf \"./Other/Products/Debug/${framework_name}.framework\"\ncp -r \"${BUILT_PRODUCTS_DIR}/${framework_name}.framework\" \"./Other/Products/Debug/${framework_name}.framework\"\ncp -r \"${BUILT_PRODUCTS_DIR}/${framework_name}.framework\" ${app_bundle_path}\n./Other/insert_dylib --all-yes \"${framework_path}/${framework_name}\" \"$app_executable_backup_path\" \"$app_executable_path\""; }; A23204925F0A37A0EBE14B92 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( "$(DERIVED_FILE_DIR)/Pods-WeChatPlugin-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 528B4F8D1EA7383800BC6A89 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 52475C2F1F6B993E00760E3E /* MMChatsTableCellView+hook.m in Sources */, 526F4D921F4AAE6F00E46004 /* TKAutoReplyCell.m in Sources */, 520F200A205E1DF900A36B1E /* TKWebServerManager.m in Sources */, 52B5CF151F3B4631000D9DD9 /* TKWeChatPluginConfig.m in Sources */, 522547E21F4C312A00226A2A /* TKRemoteControlWindowController.m in Sources */, 52B5CF131F3B4631000D9DD9 /* WeChat+hook.m in Sources */, 529CDDFF1F6E6A060056DC1A /* TKBaseModel.m in Sources */, 527E7F622018711800358D26 /* fishhook.c in Sources */, 52B5CF1B1F3B4631000D9DD9 /* TKHelper.m in Sources */, 52B5CF171F3B4631000D9DD9 /* TKRemoteControlController.m in Sources */, 522547D01F4AE5A700226A2A /* NSView+Action.m in Sources */, 523848AC1F70E73500771BA3 /* NSTextField+Action.m in Sources */, 526F4D8E1F4AAC3800E46004 /* TKRemoteControlCell.m in Sources */, 526F4D841F4AAC1900E46004 /* TKAutoReplyModel.m in Sources */, 526F4D8C1F4AAC3800E46004 /* TKAutoReplyContentView.m in Sources */, 522547DF1F4C312A00226A2A /* TKAutoReplyWindowController.m in Sources */, 5226D5CB2040FC3E0013C048 /* TKVersionManager.m in Sources */, 52B5CF191F3B4631000D9DD9 /* TKRemoteControlModel.m in Sources */, 5228EABB1F70B59500D808CF /* NSButton+Action.m in Sources */, 52EE9515203FE58500B76873 /* MMStickerMessageCellView+hook.m in Sources */, 528B4FB41EA7386D00BC6A89 /* main.mm in Sources */, 52B5CF1D1F3B4631000D9DD9 /* XMLReader.m in Sources */, 52475C331F6BA43800760E3E /* TKIgnoreSessonModel.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 528B4F981EA7383800BC6A89 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 528B4F991EA7383800BC6A89 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 528B4F9B1EA7383800BC6A89 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 888B2791FA0BBCC192BADA47 /* Pods-WeChatPlugin.debug.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; GCC_INPUT_FILETYPE = automatic; GCC_PREFIX_HEADER = "$(SRCROOT)/WeChatPlugin/Sources/Common/TKPrefixHeader.pch"; INFOPLIST_FILE = WeChatPlugin/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = tk.WeChatPlugin; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; name = Debug; }; 528B4F9C1EA7383800BC6A89 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 13B308B4A92CE38341762A48 /* Pods-WeChatPlugin.release.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; GCC_INPUT_FILETYPE = automatic; GCC_PREFIX_HEADER = "$(SRCROOT)/WeChatPlugin/Sources/Common/TKPrefixHeader.pch"; INFOPLIST_FILE = WeChatPlugin/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = tk.WeChatPlugin; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 528B4F8C1EA7383800BC6A89 /* Build configuration list for PBXProject "WeChatPlugin" */ = { isa = XCConfigurationList; buildConfigurations = ( 528B4F981EA7383800BC6A89 /* Debug */, 528B4F991EA7383800BC6A89 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 528B4F9A1EA7383800BC6A89 /* Build configuration list for PBXNativeTarget "WeChatPlugin" */ = { isa = XCConfigurationList; buildConfigurations = ( 528B4F9B1EA7383800BC6A89 /* Debug */, 528B4F9C1EA7383800BC6A89 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 528B4F891EA7383800BC6A89 /* Project object */; } ================================================ FILE: WeChatPlugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: WeChatPlugin.xcodeproj/xcuserdata/TK.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist ================================================ ================================================ FILE: WeChatPlugin.xcodeproj/xcuserdata/TK.xcuserdatad/xcschemes/WeChatPlugin.xcscheme ================================================ ================================================ FILE: WeChatPlugin.xcodeproj/xcuserdata/TK.xcuserdatad/xcschemes/xcschememanagement.plist ================================================ SchemeUserState WeChatPlugin.xcscheme orderHint 0 SuppressBuildableAutocreation 528B4F911EA7383800BC6A89 primary ================================================ FILE: WeChatPlugin.xcworkspace/contents.xcworkspacedata ================================================