├── theos ├── .gitignore ├── WeChatRedEnvelop.plist ├── Resources └── IMG_0018.JPG ├── ScreenShots ├── AliPay.jpeg ├── Setting.png ├── WechatPay.jpeg ├── WechatReward.jpeg ├── WechatPayCredit.jpg └── AssistantSetting.png ├── control ├── CHANGELOG.md ├── src ├── WBSettingViewController.h ├── WBReceiveRedEnvelopOperation.h ├── WBRedEnvelopParamQueue.h ├── WBBaseViewController.h ├── WBRedEnvelopTaskManager.h ├── WBRedEnvelopConfig.h ├── WeChatRedEnvelopParam.m ├── WeChatRedEnvelopParam.h ├── WBRedEnvelopParamQueue.m ├── WBRedEnvelopTaskManager.m ├── WBBaseViewController.m ├── WBReceiveRedEnvelopOperation.m ├── WBRedEnvelopConfig.m ├── WeChatRedEnvelop.h ├── Tweak.xm └── WBSettingViewController.m ├── Makefile └── README.md /theos: -------------------------------------------------------------------------------- 1 | /opt/theos -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | obj 3 | *.deb 4 | .theos/ 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /WeChatRedEnvelop.plist: -------------------------------------------------------------------------------- 1 | { Filter = { Bundles = ( "com.tencent.xin" ); }; } 2 | -------------------------------------------------------------------------------- /Resources/IMG_0018.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buginux/WeChatRedEnvelop/HEAD/Resources/IMG_0018.JPG -------------------------------------------------------------------------------- /ScreenShots/AliPay.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buginux/WeChatRedEnvelop/HEAD/ScreenShots/AliPay.jpeg -------------------------------------------------------------------------------- /ScreenShots/Setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buginux/WeChatRedEnvelop/HEAD/ScreenShots/Setting.png -------------------------------------------------------------------------------- /ScreenShots/WechatPay.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buginux/WeChatRedEnvelop/HEAD/ScreenShots/WechatPay.jpeg -------------------------------------------------------------------------------- /ScreenShots/WechatReward.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buginux/WeChatRedEnvelop/HEAD/ScreenShots/WechatReward.jpeg -------------------------------------------------------------------------------- /ScreenShots/WechatPayCredit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buginux/WeChatRedEnvelop/HEAD/ScreenShots/WechatPayCredit.jpg -------------------------------------------------------------------------------- /ScreenShots/AssistantSetting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buginux/WeChatRedEnvelop/HEAD/ScreenShots/AssistantSetting.png -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: com.swiftyper.wechatredenvelop 2 | Name: WeChatRedEnvelop 3 | Depends: mobilesubstrate 4 | Version: 2.0.0 5 | Architecture: iphoneos-arm 6 | Description: 微信抢红包插件 7 | Maintainer: 请叫我小锅 8 | Author: 请叫我小锅 9 | Section: Tweaks 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 更新日志 2 | 3 | **[v.2.1.0 (2019.07.02) 4 | 5 | - 更新支持 7.x 版本微信 6 | 7 | **[v.2.0.0 (2017.02.26)](https://github.com/buginux/WeChatRedEnvelop/releases/tag/2.0.0)** 8 | 9 | - 重构 代码更清晰易读,文件组织更规范 10 | - 新增 防止同时抢多个红包功能 11 | - 新增 群聊过滤功能 12 | - 优化 抢红包流程,最大限度防止系统插件识别 13 | - 优化 设置界面,操作更直观 14 | -------------------------------------------------------------------------------- /src/WBSettingViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBSettingViewController.h 3 | // WeChatRedEnvelop 4 | // 5 | // Created by 杨志超 on 2017/2/22. 6 | // Copyright © 2017年 swiftyper. All rights reserved. 7 | // 8 | 9 | #import "WBBaseViewController.h" 10 | 11 | @interface WBSettingViewController : WBBaseViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /src/WBReceiveRedEnvelopOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBReceiveRedEnvelopOperation.h 3 | // WeChatRedEnvelop 4 | // 5 | // Created by wordbeyondyoung on 17/2/22. 6 | // Copyright © 2017年 swiftyper. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class WeChatRedEnvelopParam; 12 | @interface WBReceiveRedEnvelopOperation : NSOperation 13 | 14 | - (instancetype)initWithRedEnvelopParam:(WeChatRedEnvelopParam *)param delay:(unsigned int)delaySeconds; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /src/WBRedEnvelopParamQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBRedEnvelopParamQueue.h 3 | // WeChatRedEnvelop 4 | // 5 | // Created by 杨志超 on 2017/2/22. 6 | // Copyright © 2017年 swiftyper. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class WeChatRedEnvelopParam; 12 | @interface WBRedEnvelopParamQueue : NSObject 13 | 14 | + (instancetype)sharedQueue; 15 | 16 | - (void)enqueue:(WeChatRedEnvelopParam *)param; 17 | - (WeChatRedEnvelopParam *)dequeue; 18 | - (WeChatRedEnvelopParam *)peek; 19 | - (BOOL)isEmpty; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /src/WBBaseViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBBaseViewController.h 3 | // WeChatRedEnvelop 4 | // 5 | // Created by wordbeyondyoung on 17/2/22. 6 | // Copyright © 2017年 swiftyper. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WBBaseViewController : UIViewController 12 | 13 | - (void)startLoadingBlocked; 14 | - (void)startLoadingNonBlock; 15 | - (void)startLoadingWithText:(NSString *)text; 16 | - (void)stopLoading; 17 | - (void)stopLoadingWithFailText:(NSString *)text; 18 | - (void)stopLoadingWithOKText:(NSString *)text; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /src/WBRedEnvelopTaskManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBRedEnvelopTaskManager.h 3 | // WeChatRedEnvelop 4 | // 5 | // Created by wordbeyondyoung on 17/2/22. 6 | // Copyright © 2017年 swiftyper. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class WBReceiveRedEnvelopOperation; 12 | @interface WBRedEnvelopTaskManager : NSObject 13 | 14 | + (instancetype)sharedManager; 15 | 16 | - (void)addNormalTask:(WBReceiveRedEnvelopOperation *)task; 17 | - (void)addSerialTask:(WBReceiveRedEnvelopOperation *)task; 18 | 19 | - (BOOL)serialQueueIsEmpty; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | THEOS_DEVICE_IP = localhost 2 | THEOS_DEVICE_PORT = 2222 3 | ARCHS = armv7 arm64 4 | TARGET = iphone:latest:8.0 5 | 6 | BUNDLE_NAME = com.swiftyper.wechatredenvelop 7 | com.swiftyper.wechatredenvelop_INSTALL_PATH = /Library/MobileSubstrate/DynamicLibraries 8 | 9 | include $(THEOS)/makefiles/common.mk 10 | include $(THEOS)/makefiles/bundle.mk 11 | 12 | SRC = $(wildcard src/*.m) 13 | 14 | TWEAK_NAME = WeChatRedEnvelop 15 | WeChatRedEnvelop_FILES = $(wildcard src/*.m) src/Tweak.xm 16 | WeChatRedEnvelop_FRAMEWORKS = UIKit 17 | 18 | include $(THEOS_MAKE_PATH)/tweak.mk 19 | 20 | after-install:: 21 | install.exec "killall -9 WeChat" 22 | -------------------------------------------------------------------------------- /src/WBRedEnvelopConfig.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBRedEnvelopConfig.h 3 | // WeChatRedEnvelop 4 | // 5 | // Created by 杨志超 on 2017/2/22. 6 | // Copyright © 2017年 swiftyper. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class CContact; 12 | @interface WBRedEnvelopConfig : NSObject 13 | 14 | + (instancetype)sharedConfig; 15 | 16 | @property (assign, nonatomic) BOOL autoReceiveEnable; 17 | @property (assign, nonatomic) NSInteger delaySeconds; 18 | 19 | /** Pro */ 20 | @property (assign, nonatomic) BOOL receiveSelfRedEnvelop; 21 | @property (assign, nonatomic) BOOL serialReceive; 22 | @property (strong, nonatomic) NSArray *blackList; 23 | @property (assign, nonatomic) BOOL revokeEnable; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /src/WeChatRedEnvelopParam.m: -------------------------------------------------------------------------------- 1 | // 2 | // WeChatRedEnvelopParam.m 3 | // WeChatRedEnvelop 4 | // 5 | // Created by 杨志超 on 2017/1/22. 6 | // Copyright © 2017年 swiftyper. All rights reserved. 7 | // 8 | 9 | #import "WeChatRedEnvelopParam.h" 10 | 11 | @implementation WeChatRedEnvelopParam 12 | 13 | - (NSDictionary *)toParams { 14 | return @{ 15 | @"msgType": self.msgType, 16 | @"sendId": self.sendId, 17 | @"channelId": self.channelId, 18 | @"nickName": self.nickName, 19 | @"headImg": self.headImg, 20 | @"nativeUrl": self.nativeUrl, 21 | @"sessionUserName": self.sessionUserName, 22 | @"timingIdentifier": self.timingIdentifier 23 | }; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /src/WeChatRedEnvelopParam.h: -------------------------------------------------------------------------------- 1 | // 2 | // WeChatRedEnvelopParam.h 3 | // WeChatRedEnvelop 4 | // 5 | // Created by 杨志超 on 2017/1/22. 6 | // Copyright © 2017年 swiftyper. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WeChatRedEnvelopParam : NSObject 12 | 13 | - (NSDictionary *)toParams; 14 | 15 | @property (strong, nonatomic) NSString *msgType; 16 | @property (strong, nonatomic) NSString *sendId; 17 | @property (strong, nonatomic) NSString *channelId; 18 | @property (strong, nonatomic) NSString *nickName; 19 | @property (strong, nonatomic) NSString *headImg; 20 | @property (strong, nonatomic) NSString *nativeUrl; 21 | @property (strong, nonatomic) NSString *sessionUserName; 22 | @property (strong, nonatomic) NSString *sign; 23 | @property (strong, nonatomic) NSString *timingIdentifier; 24 | 25 | @property (assign, nonatomic) BOOL isGroupSender; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /src/WBRedEnvelopParamQueue.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBRedEnvelopParamQueue.m 3 | // WeChatRedEnvelop 4 | // 5 | // Created by 杨志超 on 2017/2/22. 6 | // Copyright © 2017年 swiftyper. All rights reserved. 7 | // 8 | 9 | #import "WBRedEnvelopParamQueue.h" 10 | #import "WeChatRedEnvelopParam.h" 11 | 12 | @interface WBRedEnvelopParamQueue () 13 | 14 | @property (strong, nonatomic) NSMutableArray *queue; 15 | 16 | @end 17 | 18 | @implementation WBRedEnvelopParamQueue 19 | 20 | + (instancetype)sharedQueue { 21 | static WBRedEnvelopParamQueue *queue = nil; 22 | static dispatch_once_t onceToken; 23 | dispatch_once(&onceToken, ^{ 24 | queue = [[WBRedEnvelopParamQueue alloc] init]; 25 | }); 26 | return queue; 27 | } 28 | 29 | - (instancetype)init { 30 | if (self = [super init]) { 31 | _queue = [[NSMutableArray alloc] init]; 32 | } 33 | return self; 34 | } 35 | 36 | - (void)enqueue:(WeChatRedEnvelopParam *)param { 37 | [self.queue addObject:param]; 38 | } 39 | 40 | - (WeChatRedEnvelopParam *)dequeue { 41 | if (self.queue.count == 0 && !self.queue.firstObject) { 42 | return nil; 43 | } 44 | 45 | WeChatRedEnvelopParam *first = self.queue.firstObject; 46 | 47 | [self.queue removeObjectAtIndex:0]; 48 | 49 | return first; 50 | } 51 | 52 | - (WeChatRedEnvelopParam *)peek { 53 | if (self.queue.count == 0) { 54 | return nil; 55 | } 56 | 57 | return self.queue.firstObject; 58 | } 59 | 60 | - (BOOL)isEmpty { 61 | return self.queue.count == 0; 62 | } 63 | 64 | 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /src/WBRedEnvelopTaskManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBRedEnvelopTaskManager.m 3 | // WeChatRedEnvelop 4 | // 5 | // Created by wordbeyondyoung on 17/2/22. 6 | // Copyright © 2017年 swiftyper. All rights reserved. 7 | // 8 | 9 | #import "WBRedEnvelopTaskManager.h" 10 | #import "WBReceiveRedEnvelopOperation.h" 11 | 12 | @interface WBRedEnvelopTaskManager () 13 | 14 | @property (strong, nonatomic) NSOperationQueue *normalTaskQueue; 15 | @property (strong, nonatomic) NSOperationQueue *serialTaskQueue; 16 | 17 | @end 18 | 19 | @implementation WBRedEnvelopTaskManager 20 | 21 | + (instancetype)sharedManager { 22 | static WBRedEnvelopTaskManager *taskManager = nil; 23 | static dispatch_once_t onceToken; 24 | dispatch_once(&onceToken, ^{ 25 | taskManager = [WBRedEnvelopTaskManager new]; 26 | }); 27 | return taskManager; 28 | } 29 | 30 | - (instancetype)init { 31 | if (self = [super init]) { 32 | _serialTaskQueue = [[NSOperationQueue alloc] init]; 33 | _serialTaskQueue.maxConcurrentOperationCount = 1; 34 | 35 | _normalTaskQueue = [[NSOperationQueue alloc] init]; 36 | _normalTaskQueue.maxConcurrentOperationCount = 5; 37 | } 38 | return self; 39 | } 40 | 41 | - (void)addNormalTask:(WBReceiveRedEnvelopOperation *)task { 42 | [self.normalTaskQueue addOperation:task]; 43 | } 44 | 45 | - (void)addSerialTask:(WBReceiveRedEnvelopOperation *)task { 46 | [self.serialTaskQueue addOperation:task]; 47 | } 48 | 49 | - (BOOL)serialQueueIsEmpty { 50 | return [self.serialTaskQueue operations].count == 0; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iOS版微信抢红包插件 2 | 3 | 目前市面上最稳定、功能最全的 iOS 版微信抢红包插件。 4 | 5 | ``` 6 | 2021-05-09 已更新,支持 8.0.5 版微信 7 | 2019-07-03 已更新,支持 7.0.0 版微信。 8 | ``` 9 | 10 | ## 功能特色 11 | 12 | - **完全原生体验** 13 | 14 | 完美嵌入微信设置中,无需安装其它无关应用。开启插件后,所有抢红包工作都会默默进行,不会影响微信的正常使用流程。 15 | 16 | - **可自由设置延迟抢红包时间** 17 | 18 | 有效防止抢红包速度太快而被拉黑或者踢出群聊。 19 | 20 | - **可设置防止同时抢多个红包** 21 | 22 | 最大程度避免被系统检测出使用插件,红包抢得更安心。 23 | 24 | - **可设置过滤特定的群聊** 25 | 26 | 防止落入公司群或者点餐群抢红包的尴尬境地。 27 | 28 | - **更新速度快** 29 | 30 | 紧跟微信更新,社区活跃,失效后第一时间进行修复。 31 | 32 | - **安全、值得信赖** 33 | 34 | 源码完全公开透明,Cydia 市场超过万次下载,值得你拥有 35 | 36 | ## 安装方法 37 | 38 | - 越狱手机 39 | 1. cydia 市场:直接在 cydia 市场搜索 `WeChatRedEnvelop`,下载安装即可 40 | 2. 手动编译安装:参考 [iOS微信抢红包Tweak安装教程](http://www.swiftyper.com/2016/01/25/ios-tweak-install-guide/) 教程 41 | - 非越狱手机:参考 [免越狱版 iOS 抢红包插件](http://www.swiftyper.com/2016/12/26/wechat-redenvelop-tweak-for-non-jailbroken-iphone/) 教程 42 | 43 | ## 更新日志 44 | 45 | 完整的更新日志请参考[CHANGELOG](./CHANGELOG.md)。 46 | 47 | ## 版权及免责声明 48 | 49 | 本插件是由本人学习[iOS应用逆向工程](https://www.amazon.cn/gp/product/B00VFDVY7E/ref=as_li_tf_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B00VFDVY7E&linkCode=as2&tag=buginux-23)后并由[该书作者](https://github.com/iosre)进行指导后开发的,所有代码都由本人完成。 50 | 51 | 外挂有风险,使用需谨慎。 52 | 53 | 要使用本插件,请使用者自行承担各种状况,包括但不限于“禁用红包功能”以及“微信封号”。 54 | 55 | ## 应用截图 56 | 57 | 58 | 59 | 60 | ## 支持作者 61 | 62 | 本项目的所有逆向分析工作,代码编写工作都由本人一人完成。 63 | 64 | 插件的开发占用了我大量的时间和精力,如果你觉得这个插件对你有帮助(帮你抢到了比之前更多的红包),不妨进行一下小额捐赠,这样我会有更大的动力去完善、优化它。 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/WBBaseViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBBaseViewController.m 3 | // WeChatRedEnvelop 4 | // 5 | // Created by wordbeyondyoung on 17/2/22. 6 | // Copyright © 2017年 swiftyper. All rights reserved. 7 | // 8 | 9 | #import "WBBaseViewController.h" 10 | #import "WeChatRedEnvelop.h" 11 | #import 12 | 13 | @interface WBBaseViewController () 14 | 15 | @property (strong, nonatomic) MMLoadingView *loadingView; 16 | 17 | @end 18 | 19 | @implementation WBBaseViewController 20 | 21 | - (void)startLoadingBlocked { 22 | if (!self.loadingView) { 23 | self.loadingView = [self createDefaultLoadingView]; 24 | [self.view addSubview:self.loadingView]; 25 | } else { 26 | [self.view bringSubviewToFront:self.loadingView]; 27 | } 28 | self.loadingView.ignoreInteractionEventsWhenLoading = YES; 29 | [self.loadingView startLoading]; 30 | } 31 | 32 | - (void)startLoadingNonBlock { 33 | if (!self.loadingView) { 34 | self.loadingView = [self createDefaultLoadingView]; 35 | [self.view addSubview:self.loadingView]; 36 | } else { 37 | [self.view bringSubviewToFront:self.loadingView]; 38 | } 39 | self.loadingView.ignoreInteractionEventsWhenLoading = NO; 40 | [self.loadingView startLoading]; 41 | } 42 | 43 | - (void)startLoadingWithText:(NSString *)text { 44 | [self startLoadingNonBlock]; 45 | 46 | self.loadingView.text = text; 47 | } 48 | 49 | - (MMLoadingView *)createDefaultLoadingView { 50 | MMLoadingView *loadingView = [[objc_getClass("MMLoadingView") alloc] init]; 51 | 52 | MMContext *context = [objc_getClass("MMContext") rootContext]; 53 | MMLanguageMgr *languageMgr = [context getService:objc_getClass("MMLanguageMgr")]; 54 | NSString *loadingText = [languageMgr getStringForCurLanguage:@"Common_DefaultLoadingText"]; 55 | 56 | loadingView.text = loadingText; 57 | 58 | return loadingView; 59 | } 60 | 61 | - (void)stopLoading { 62 | [self.loadingView stopLoading]; 63 | } 64 | 65 | - (void)stopLoadingWithFailText:(NSString *)text { 66 | [self.loadingView stopLoadingAndShowError:text]; 67 | } 68 | 69 | - (void)stopLoadingWithOKText:(NSString *)text { 70 | [self.loadingView stopLoadingAndShowOK:text]; 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /src/WBReceiveRedEnvelopOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBReceiveRedEnvelopOperation.m 3 | // WeChatRedEnvelop 4 | // 5 | // Created by wordbeyondyoung on 17/2/22. 6 | // Copyright © 2017年 swiftyper. All rights reserved. 7 | // 8 | 9 | #import "WBReceiveRedEnvelopOperation.h" 10 | #import "WeChatRedEnvelopParam.h" 11 | #import "WBRedEnvelopConfig.h" 12 | #import "WeChatRedEnvelop.h" 13 | #import 14 | 15 | @interface WBReceiveRedEnvelopOperation () 16 | 17 | @property (assign, nonatomic, getter=isExecuting) BOOL executing; 18 | @property (assign, nonatomic, getter=isFinished) BOOL finished; 19 | 20 | @property (strong, nonatomic) WeChatRedEnvelopParam *redEnvelopParam; 21 | @property (assign, nonatomic) unsigned int delaySeconds; 22 | 23 | @end 24 | 25 | @implementation WBReceiveRedEnvelopOperation 26 | 27 | @synthesize executing = _executing; 28 | @synthesize finished = _finished; 29 | 30 | - (instancetype)initWithRedEnvelopParam:(WeChatRedEnvelopParam *)param delay:(unsigned int)delaySeconds { 31 | if (self = [super init]) { 32 | _redEnvelopParam = param; 33 | _delaySeconds = delaySeconds; 34 | } 35 | return self; 36 | } 37 | 38 | - (void)start { 39 | if (self.isCancelled) { 40 | self.finished = YES; 41 | self.executing = NO; 42 | return; 43 | } 44 | 45 | [self main]; 46 | 47 | self.executing = YES; 48 | self.finished = NO; 49 | } 50 | 51 | - (void)main { 52 | sleep(self.delaySeconds); 53 | 54 | MMContext *context = [objc_getClass("MMContext") activeUserContext]; 55 | WCRedEnvelopesLogicMgr *logicMgr = [context getService:objc_getClass("WCRedEnvelopesLogicMgr")]; 56 | [logicMgr OpenRedEnvelopesRequest:[self.redEnvelopParam toParams]]; 57 | 58 | self.finished = YES; 59 | self.executing = NO; 60 | } 61 | 62 | - (void)cancel { 63 | self.finished = YES; 64 | self.executing = NO; 65 | } 66 | 67 | - (void)setFinished:(BOOL)finished { 68 | [self willChangeValueForKey:@"isFinished"]; 69 | _finished = finished; 70 | [self didChangeValueForKey:@"isFinished"]; 71 | } 72 | 73 | - (void)setExecuting:(BOOL)executing { 74 | [self willChangeValueForKey:@"isExecuting"]; 75 | _executing = executing; 76 | [self didChangeValueForKey:@"isExecuting"]; 77 | } 78 | 79 | - (BOOL)isAsynchronous { 80 | return YES; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /src/WBRedEnvelopConfig.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBRedEnvelopConfig.m 3 | // WeChatRedEnvelop 4 | // 5 | // Created by 杨志超 on 2017/2/22. 6 | // Copyright © 2017年 swiftyper. All rights reserved. 7 | // 8 | 9 | #import "WBRedEnvelopConfig.h" 10 | #import "WeChatRedEnvelop.h" 11 | 12 | static NSString * const kDelaySecondsKey = @"XGDelaySecondsKey"; 13 | static NSString * const kAutoReceiveRedEnvelopKey = @"XGWeChatRedEnvelopSwitchKey"; 14 | static NSString * const kReceiveSelfRedEnvelopKey = @"WBReceiveSelfRedEnvelopKey"; 15 | static NSString * const kSerialReceiveKey = @"WBSerialReceiveKey"; 16 | static NSString * const kBlackListKey = @"WBBlackListKey"; 17 | static NSString * const kRevokeEnablekey = @"WBRevokeEnable"; 18 | 19 | @interface WBRedEnvelopConfig () 20 | 21 | @end 22 | 23 | @implementation WBRedEnvelopConfig 24 | 25 | + (instancetype)sharedConfig { 26 | static WBRedEnvelopConfig *config = nil; 27 | static dispatch_once_t onceToken; 28 | dispatch_once(&onceToken, ^{ 29 | config = [WBRedEnvelopConfig new]; 30 | }); 31 | return config; 32 | } 33 | 34 | - (instancetype)init { 35 | if (self = [super init]) { 36 | _delaySeconds = [[NSUserDefaults standardUserDefaults] integerForKey:kDelaySecondsKey]; 37 | _autoReceiveEnable = [[NSUserDefaults standardUserDefaults] boolForKey:kAutoReceiveRedEnvelopKey]; 38 | _serialReceive = [[NSUserDefaults standardUserDefaults] boolForKey:kSerialReceiveKey]; 39 | _blackList = [[NSUserDefaults standardUserDefaults] objectForKey:kBlackListKey]; 40 | _receiveSelfRedEnvelop = [[NSUserDefaults standardUserDefaults] boolForKey:kReceiveSelfRedEnvelopKey]; 41 | _revokeEnable = [[NSUserDefaults standardUserDefaults] boolForKey:kRevokeEnablekey]; 42 | } 43 | return self; 44 | } 45 | 46 | - (void)setDelaySeconds:(NSInteger)delaySeconds { 47 | _delaySeconds = delaySeconds; 48 | 49 | [[NSUserDefaults standardUserDefaults] setInteger:delaySeconds forKey:kDelaySecondsKey]; 50 | [[NSUserDefaults standardUserDefaults] synchronize]; 51 | } 52 | 53 | - (void)setAutoReceiveEnable:(BOOL)autoReceiveEnable { 54 | _autoReceiveEnable = autoReceiveEnable; 55 | 56 | [[NSUserDefaults standardUserDefaults] setBool:autoReceiveEnable forKey:kAutoReceiveRedEnvelopKey]; 57 | [[NSUserDefaults standardUserDefaults] synchronize]; 58 | } 59 | 60 | - (void)setReceiveSelfRedEnvelop:(BOOL)receiveSelfRedEnvelop { 61 | _receiveSelfRedEnvelop = receiveSelfRedEnvelop; 62 | 63 | [[NSUserDefaults standardUserDefaults] setBool:receiveSelfRedEnvelop forKey:kReceiveSelfRedEnvelopKey]; 64 | [[NSUserDefaults standardUserDefaults] synchronize]; 65 | } 66 | 67 | - (void)setSerialReceive:(BOOL)serialReceive { 68 | _serialReceive = serialReceive; 69 | 70 | [[NSUserDefaults standardUserDefaults] setBool:serialReceive forKey:kSerialReceiveKey]; 71 | [[NSUserDefaults standardUserDefaults] synchronize]; 72 | } 73 | 74 | - (void)setBlackList:(NSArray *)blackList { 75 | _blackList = blackList; 76 | 77 | [[NSUserDefaults standardUserDefaults] setObject:blackList forKey:kBlackListKey]; 78 | [[NSUserDefaults standardUserDefaults] synchronize]; 79 | } 80 | 81 | - (void)setRevokeEnable:(BOOL)revokeEnable { 82 | _revokeEnable = revokeEnable; 83 | 84 | [[NSUserDefaults standardUserDefaults] setBool:revokeEnable forKey:kRevokeEnablekey]; 85 | [[NSUserDefaults standardUserDefaults] synchronize]; 86 | } 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /src/WeChatRedEnvelop.h: -------------------------------------------------------------------------------- 1 | #define kBundlePath @"/Library/MobileSubstrate/DynamicLibraries/com.swiftyper.wechatredenvelop.bundle" 2 | 3 | #pragma mark - Util 4 | 5 | @interface WCBizUtil : NSObject 6 | 7 | + (id)dictionaryWithDecodedComponets:(id)arg1 separator:(id)arg2; 8 | 9 | @end 10 | 11 | @interface SKBuiltinBuffer_t : NSObject 12 | 13 | @property(retain, nonatomic) NSData *buffer; // @dynamic buffer; 14 | 15 | @end 16 | 17 | #pragma mark - Message 18 | 19 | @interface WCPayInfoItem: NSObject 20 | 21 | @property(retain, nonatomic) NSString *m_c2cNativeUrl; 22 | 23 | @end 24 | 25 | @interface CMessageMgr : NSObject 26 | 27 | - (void)AddLocalMsg:(id)arg1 MsgWrap:(id)arg2 fixTime:(_Bool)arg3 NewMsgArriveNotify:(_Bool)arg4; 28 | 29 | @end 30 | 31 | @interface CMessageWrap : NSObject 32 | 33 | @property (retain, nonatomic) WCPayInfoItem *m_oWCPayInfoItem; 34 | @property (assign, nonatomic) NSUInteger m_uiMesLocalID; 35 | @property (retain, nonatomic) NSString* m_nsFromUsr; ///< 发信人,可能是群或个人 36 | @property (retain, nonatomic) NSString* m_nsToUsr; ///< 收信人 37 | @property (assign, nonatomic) NSUInteger m_uiStatus; 38 | @property (retain, nonatomic) NSString* m_nsContent; ///< 消息内容 39 | @property (retain, nonatomic) NSString* m_nsRealChatUsr; ///< 群消息的发信人,具体是群里的哪个人 40 | @property (assign, nonatomic) NSUInteger m_uiMessageType; 41 | @property (assign, nonatomic) long long m_n64MesSvrID; 42 | @property (assign, nonatomic) NSUInteger m_uiCreateTime; 43 | @property (retain, nonatomic) NSString *m_nsDesc; 44 | @property (retain, nonatomic) NSString *m_nsAppExtInfo; 45 | @property (assign, nonatomic) NSUInteger m_uiAppDataSize; 46 | @property (assign, nonatomic) NSUInteger m_uiAppMsgInnerType; 47 | @property (retain, nonatomic) NSString *m_nsShareOpenUrl; 48 | @property (retain, nonatomic) NSString *m_nsShareOriginUrl; 49 | @property (retain, nonatomic) NSString *m_nsJsAppId; 50 | @property (retain, nonatomic) NSString *m_nsPrePublishId; 51 | @property (retain, nonatomic) NSString *m_nsAppID; 52 | @property (retain, nonatomic) NSString *m_nsAppName; 53 | @property (retain, nonatomic) NSString *m_nsThumbUrl; 54 | @property (retain, nonatomic) NSString *m_nsAppMediaUrl; 55 | @property (retain, nonatomic) NSData *m_dtThumbnail; 56 | @property (retain, nonatomic) NSString *m_nsTitle; 57 | @property (retain, nonatomic) NSString *m_nsMsgSource; 58 | 59 | - (id)initWithMsgType:(long long)arg1; 60 | + (_Bool)isSenderFromMsgWrap:(id)arg1; 61 | 62 | @end 63 | 64 | @interface MMContext : NSObject 65 | 66 | + (id)activeUserContext; // IMP=0x0000000109138a28 67 | + (id)rootContext; // IMP=0x0000000109138a1c 68 | + (id)lastContext; // IMP=0x0000000109138994 69 | + (id)fastCurrentContext; // IMP=0x0000000109138988 70 | + (id)currentContext; // IMP=0x0000000109138918 71 | - (id)getService:(Class)arg1; // IMP=0x000000010917b054 72 | 73 | @end 74 | 75 | @interface MMLanguageMgr: NSObject 76 | 77 | - (id)getStringForCurLanguage:(id)arg1; 78 | 79 | 80 | @end 81 | 82 | #pragma mark - RedEnvelop 83 | 84 | @interface WCRedEnvelopesControlData : NSObject 85 | 86 | @property(retain, nonatomic) CMessageWrap *m_oSelectedMessageWrap; 87 | 88 | @end 89 | 90 | @interface WCRedEnvelopesLogicMgr: NSObject 91 | 92 | - (void)OpenRedEnvelopesRequest:(id)params; 93 | - (void)ReceiverQueryRedEnvelopesRequest:(id)arg1; 94 | - (void)GetHongbaoBusinessRequest:(id)arg1 CMDID:(unsigned int)arg2 OutputType:(unsigned int)arg3; 95 | 96 | /** Added Methods */ 97 | - (unsigned int)calculateDelaySeconds; 98 | 99 | @end 100 | 101 | @interface HongBaoRes : NSObject 102 | 103 | @property(retain, nonatomic) SKBuiltinBuffer_t *retText; // @dynamic retText; 104 | @property(nonatomic) int cgiCmdid; // @dynamic cgiCmdid; 105 | 106 | @end 107 | 108 | @interface HongBaoReq : NSObject 109 | 110 | @property(retain, nonatomic) SKBuiltinBuffer_t *reqText; // @dynamic reqText; 111 | 112 | @end 113 | 114 | #pragma mark - Contact 115 | 116 | @interface CContact: NSObject 117 | 118 | @property(retain, nonatomic) NSString *m_nsUsrName; 119 | @property(retain, nonatomic) NSString *m_nsHeadImgUrl; 120 | @property(retain, nonatomic) NSString *m_nsNickName; 121 | 122 | - (id)getContactDisplayName; 123 | 124 | @end 125 | 126 | @interface CContactMgr : NSObject 127 | 128 | - (id)getSelfContact; 129 | - (id)getContactByName:(id)arg1; 130 | - (id)getContactForSearchByName:(id)arg1; 131 | - (_Bool)getContactsFromServer:(id)arg1; 132 | - (_Bool)isInContactList:(id)arg1; 133 | - (_Bool)addLocalContact:(id)arg1 listType:(unsigned int)arg2; 134 | 135 | @end 136 | 137 | 138 | #pragma mark - QRCode 139 | 140 | @interface ScanQRCodeLogicParams: NSObject 141 | 142 | - (id)initWithCodeType:(int)arg1 fromScene:(unsigned int)arg2; // IMP=0x00000001035fcec8 143 | 144 | @end 145 | 146 | @interface NewQRCodeScannerParams: NSObject 147 | 148 | - (id)initWithCodeType:(int)arg1; // IMP=0x0000000103627e3c 149 | 150 | @end 151 | 152 | @interface ScanQRCodeLogicController: NSObject 153 | 154 | @property(nonatomic) unsigned int fromScene; 155 | - (id)initWithViewController:(id)arg1 logicParams:(id)arg2; // IMP=0x00000001035fd0ec 156 | - (void)tryScanOnePicture:(id)arg1; 157 | - (void)doScanQRCode:(id)arg1; 158 | - (void)showScanResult; 159 | 160 | @end 161 | 162 | @interface NewQRCodeScanner: NSObject 163 | 164 | - (id)initWithDelegate:(id)arg1 scannerParams:(id)arg2; // IMP=0x0000000103628004 165 | - (_Bool)scanOnePicture:(id)arg1; // IMP=0x000000010362b65c 166 | 167 | @end 168 | 169 | #pragma mark - MMTableView 170 | 171 | @interface WCTableViewManager 172 | 173 | - (void)clearAllSection; 174 | - (id)getTableView; 175 | - (void)insertSection:(id)arg1 At:(unsigned int)arg2; 176 | - (void)addSection:(id)arg1; 177 | - (void)addTableViewToSuperView:(id)arg1; // IMP=0x0000000100da4684 178 | 179 | 180 | @end 181 | 182 | @interface WCTableViewSectionManager 183 | 184 | + (id)sectionInfoDefaut; 185 | + (id)sectionInfoHeader:(id)arg1; 186 | + (id)sectionInfoHeader:(id)arg1 Footer:(id)arg2; 187 | - (void)addCell:(id)arg1; 188 | 189 | @end 190 | 191 | @interface WCTableViewCellManager 192 | 193 | + (id)normalCellForSel:(SEL)arg1 target:(id)arg2 title:(id)arg3; 194 | + (id)normalCellForSel:(SEL)arg1 target:(id)arg2 title:(id)arg3 rightValue:(id)arg4 accessoryType:(long long)arg5; // IMP=0x000000010188f9e4 195 | 196 | + (id)switchCellForSel:(SEL)arg1 target:(id)arg2 title:(id)arg3 on:(_Bool)arg4; 197 | 198 | @end 199 | 200 | @interface WCTableViewNormalCellManager 201 | 202 | + (id)normalCellForTitle:(id)arg1 rightValue:(id)arg2; 203 | 204 | @end 205 | 206 | @interface MMTableView: UITableView 207 | 208 | @end 209 | 210 | #pragma mark - UI 211 | @interface MMUICommonUtil : NSObject 212 | 213 | + (id)getBarButtonWithTitle:(id)arg1 target:(id)arg2 action:(SEL)arg3 style:(int)arg4; 214 | 215 | @end 216 | 217 | @interface MMLoadingView : UIView 218 | 219 | @property(retain, nonatomic) NSString *text; // @synthesize text=_text; 220 | @property (assign, nonatomic) BOOL ignoreInteractionEventsWhenLoading; // @synthesize m_bIgnoringInteractionEventsWhenLoading; 221 | 222 | - (void)startLoading; 223 | - (void)startLoadingAfterDelay:(double)arg1; // IMP=0x0000000108685434 224 | - (void)stopLoading; 225 | - (void)stopLoadingAndShowError:(id)arg1; 226 | - (void)stopLoadingAndShowOK:(id)arg1; 227 | 228 | 229 | @end 230 | 231 | @interface MMWebViewController: NSObject 232 | 233 | - (id)initWithURL:(id)arg1 presentModal:(_Bool)arg2 extraInfo:(id)arg3; 234 | 235 | @end 236 | 237 | @interface ContactSelectView : UIView 238 | 239 | @property(nonatomic) _Bool m_bHideSearchBar; // @synthesize m_bHideSearchBar=_m_bHideSearchBar; 240 | 241 | - (void)addSelect:(id)arg1; 242 | 243 | @end 244 | @interface MMUINavigationController : UINavigationController 245 | 246 | @end 247 | 248 | #pragma mark - UtilCategory 249 | 250 | @interface NSMutableDictionary (SafeInsert) 251 | 252 | - (void)safeSetObject:(id)arg1 forKey:(id)arg2; 253 | 254 | @end 255 | 256 | @interface NSDictionary (NSDictionary_SafeJSON) 257 | 258 | - (id)arrayForKey:(id)arg1; 259 | - (id)dictionaryForKey:(id)arg1; 260 | - (double)doubleForKey:(id)arg1; 261 | - (float)floatForKey:(id)arg1; 262 | - (long long)int64ForKey:(id)arg1; 263 | - (long long)integerForKey:(id)arg1; 264 | - (id)stringForKey:(id)arg1; 265 | 266 | @end 267 | 268 | @interface NSString (NSString_SBJSON) 269 | 270 | - (id)JSONArray; 271 | - (id)JSONDictionary; 272 | - (id)JSONValue; 273 | 274 | @end 275 | 276 | #pragma mark - UICategory 277 | 278 | @interface UINavigationController (LogicController) 279 | 280 | - (void)PushViewController:(id)arg1 animated:(_Bool)arg2; 281 | 282 | @end 283 | 284 | @interface MMUIViewController : UIViewController 285 | 286 | - (void)startLoadingBlocked; 287 | - (void)startLoadingNonBlock; 288 | - (void)startLoadingWithText:(NSString *)text; 289 | - (void)stopLoading; 290 | - (void)stopLoadingWithFailText:(NSString *)text; 291 | - (void)stopLoadingWithOKText:(NSString *)text; 292 | 293 | @end 294 | 295 | @interface NewSettingViewController: MMUIViewController 296 | 297 | - (void)reloadTableData; 298 | 299 | @end 300 | 301 | @interface ContactInfoViewController : MMUIViewController 302 | 303 | @property(retain, nonatomic) CContact *m_contact; // @synthesize m_contact; 304 | 305 | @end 306 | 307 | @protocol MultiSelectContactsViewControllerDelegate 308 | - (void)onMultiSelectContactReturn:(NSArray *)arg1; 309 | 310 | @optional 311 | - (int)getFTSCommonScene; 312 | - (void)onMultiSelectContactCancelForSns; 313 | - (void)onMultiSelectContactReturnForSns:(NSArray *)arg1; 314 | @end 315 | 316 | @interface MultiSelectContactsViewController : UIViewController 317 | 318 | @property(nonatomic) _Bool m_bKeepCurViewAfterSelect; // @synthesize m_bKeepCurViewAfterSelect=_m_bKeepCurViewAfterSelect; 319 | @property(nonatomic) _Bool m_bShowHistoryGroup; // @synthesize m_bShowHistoryGroup; 320 | @property(nonatomic) unsigned int m_uiGroupScene; // @synthesize m_uiGroupScene; 321 | @property(nonatomic) int m_scene; // @synthesize m_scene=_m_scene; 322 | @property(nonatomic) _Bool m_onlyChatRoom; // @synthesize m_onlyChatRoom=_m_onlyChatRoom; 323 | 324 | @property(nonatomic, weak) id m_delegate; // @synthesize m_delegate; 325 | 326 | - (void)updatePanelBtn; 327 | 328 | @end 329 | 330 | -------------------------------------------------------------------------------- /src/Tweak.xm: -------------------------------------------------------------------------------- 1 | #import "WeChatRedEnvelop.h" 2 | #import "WeChatRedEnvelopParam.h" 3 | #import "WBSettingViewController.h" 4 | #import "WBReceiveRedEnvelopOperation.h" 5 | #import "WBRedEnvelopTaskManager.h" 6 | #import "WBRedEnvelopConfig.h" 7 | #import "WBRedEnvelopParamQueue.h" 8 | 9 | %hook MicroMessengerAppDelegate 10 | 11 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 12 | 13 | MMContext *context = [%c(MMContext) activeUserContext]; 14 | CContactMgr *contactMgr = [context getService:%c(CContactMgr)]; 15 | CContact *contact = [contactMgr getContactForSearchByName:@"gh_6e8bddcdfca3"]; 16 | if (contact) { 17 | [contactMgr addLocalContact:contact listType:2]; 18 | [contactMgr getContactsFromServer:@[contact]]; 19 | } 20 | 21 | return %orig; 22 | } 23 | 24 | %end 25 | 26 | %hook WCRedEnvelopesLogicMgr 27 | 28 | - (void)OnWCToHongbaoCommonResponse:(HongBaoRes *)arg1 Request:(HongBaoReq *)arg2 { 29 | 30 | %orig; 31 | 32 | // 非参数查询请求 33 | if (arg1.cgiCmdid != 3) { return; } 34 | 35 | NSString *(^parseRequestSign)() = ^NSString *() { 36 | NSString *requestString = [[NSString alloc] initWithData:arg2.reqText.buffer encoding:NSUTF8StringEncoding]; 37 | NSDictionary *requestDictionary = [%c(WCBizUtil) dictionaryWithDecodedComponets:requestString separator:@"&"]; 38 | NSString *nativeUrl = [[requestDictionary stringForKey:@"nativeUrl"] stringByRemovingPercentEncoding]; 39 | NSDictionary *nativeUrlDict = [%c(WCBizUtil) dictionaryWithDecodedComponets:nativeUrl separator:@"&"]; 40 | 41 | return [nativeUrlDict stringForKey:@"sign"]; 42 | }; 43 | 44 | NSDictionary *responseDict = [[[NSString alloc] initWithData:arg1.retText.buffer encoding:NSUTF8StringEncoding] JSONDictionary]; 45 | 46 | WeChatRedEnvelopParam *mgrParams = [[WBRedEnvelopParamQueue sharedQueue] dequeue]; 47 | 48 | BOOL (^shouldReceiveRedEnvelop)() = ^BOOL() { 49 | 50 | // 手动抢红包 51 | if (!mgrParams) { return NO; } 52 | 53 | // 自己已经抢过 54 | if ([responseDict[@"receiveStatus"] integerValue] == 2) { return NO; } 55 | 56 | // 红包被抢完 57 | if ([responseDict[@"hbStatus"] integerValue] == 4) { return NO; } 58 | 59 | // 没有这个字段会被判定为使用外挂 60 | if (!responseDict[@"timingIdentifier"]) { return NO; } 61 | 62 | if (mgrParams.isGroupSender) { // 自己发红包的时候没有 sign 字段 63 | return [WBRedEnvelopConfig sharedConfig].autoReceiveEnable; 64 | } else { 65 | return [parseRequestSign() isEqualToString:mgrParams.sign] && [WBRedEnvelopConfig sharedConfig].autoReceiveEnable; 66 | } 67 | }; 68 | 69 | if (shouldReceiveRedEnvelop()) { 70 | mgrParams.timingIdentifier = responseDict[@"timingIdentifier"]; 71 | 72 | unsigned int delaySeconds = [self calculateDelaySeconds]; 73 | WBReceiveRedEnvelopOperation *operation = [[WBReceiveRedEnvelopOperation alloc] initWithRedEnvelopParam:mgrParams delay:delaySeconds]; 74 | 75 | if ([WBRedEnvelopConfig sharedConfig].serialReceive) { 76 | [[WBRedEnvelopTaskManager sharedManager] addSerialTask:operation]; 77 | } else { 78 | [[WBRedEnvelopTaskManager sharedManager] addNormalTask:operation]; 79 | } 80 | } 81 | } 82 | 83 | %new 84 | - (unsigned int)calculateDelaySeconds { 85 | NSInteger configDelaySeconds = [WBRedEnvelopConfig sharedConfig].delaySeconds; 86 | 87 | if ([WBRedEnvelopConfig sharedConfig].serialReceive) { 88 | unsigned int serialDelaySeconds; 89 | if ([WBRedEnvelopTaskManager sharedManager].serialQueueIsEmpty) { 90 | serialDelaySeconds = configDelaySeconds; 91 | } else { 92 | serialDelaySeconds = 15; 93 | } 94 | 95 | return serialDelaySeconds; 96 | } else { 97 | return (unsigned int)configDelaySeconds; 98 | } 99 | } 100 | 101 | %end 102 | 103 | %hook CMessageMgr 104 | - (void)AsyncOnAddMsg:(NSString *)msg MsgWrap:(CMessageWrap *)wrap { 105 | %orig; 106 | 107 | switch(wrap.m_uiMessageType) { 108 | case 49: { // AppNode 109 | 110 | /** 是否为红包消息 */ 111 | BOOL (^isRedEnvelopMessage)() = ^BOOL() { 112 | return [wrap.m_nsContent rangeOfString:@"wxpay://"].location != NSNotFound; 113 | }; 114 | 115 | if (isRedEnvelopMessage()) { // 红包 116 | MMContext *context = [%c(MMContext) activeUserContext]; 117 | CContactMgr *contactManager = [context getService:[%c(CContactMgr) class]]; 118 | CContact *selfContact = [contactManager getSelfContact]; 119 | 120 | BOOL (^isSender)() = ^BOOL() { 121 | return [wrap.m_nsFromUsr isEqualToString:selfContact.m_nsUsrName]; 122 | }; 123 | 124 | /** 是否别人在群聊中发消息 */ 125 | BOOL (^isGroupReceiver)() = ^BOOL() { 126 | return [wrap.m_nsFromUsr rangeOfString:@"@chatroom"].location != NSNotFound; 127 | }; 128 | 129 | /** 是否自己在群聊中发消息 */ 130 | BOOL (^isGroupSender)() = ^BOOL() { 131 | return isSender() && [wrap.m_nsToUsr rangeOfString:@"chatroom"].location != NSNotFound; 132 | }; 133 | 134 | /** 是否抢自己发的红包 */ 135 | BOOL (^isReceiveSelfRedEnvelop)() = ^BOOL() { 136 | return [WBRedEnvelopConfig sharedConfig].receiveSelfRedEnvelop; 137 | }; 138 | 139 | /** 是否在黑名单中 */ 140 | BOOL (^isGroupInBlackList)() = ^BOOL() { 141 | return [[WBRedEnvelopConfig sharedConfig].blackList containsObject:wrap.m_nsFromUsr]; 142 | }; 143 | 144 | /** 是否自动抢红包 */ 145 | BOOL (^shouldReceiveRedEnvelop)() = ^BOOL() { 146 | if (![WBRedEnvelopConfig sharedConfig].autoReceiveEnable) { return NO; } 147 | if (isGroupInBlackList()) { return NO; } 148 | 149 | return isGroupReceiver() || (isGroupSender() && isReceiveSelfRedEnvelop()); 150 | }; 151 | 152 | NSDictionary *(^parseNativeUrl)(NSString *nativeUrl) = ^(NSString *nativeUrl) { 153 | nativeUrl = [nativeUrl substringFromIndex:[@"wxpay://c2cbizmessagehandler/hongbao/receivehongbao?" length]]; 154 | return [%c(WCBizUtil) dictionaryWithDecodedComponets:nativeUrl separator:@"&"]; 155 | }; 156 | 157 | /** 获取服务端验证参数 */ 158 | void (^queryRedEnvelopesReqeust)(NSDictionary *nativeUrlDict) = ^(NSDictionary *nativeUrlDict) { 159 | NSMutableDictionary *params = [@{} mutableCopy]; 160 | params[@"agreeDuty"] = @"0"; 161 | params[@"channelId"] = [nativeUrlDict stringForKey:@"channelid"]; 162 | params[@"inWay"] = @"0"; 163 | params[@"msgType"] = [nativeUrlDict stringForKey:@"msgtype"]; 164 | params[@"nativeUrl"] = [[wrap m_oWCPayInfoItem] m_c2cNativeUrl]; 165 | params[@"sendId"] = [nativeUrlDict stringForKey:@"sendid"]; 166 | 167 | MMContext *context = [objc_getClass("MMContext") activeUserContext]; 168 | WCRedEnvelopesLogicMgr *logicMgr = [context getService:objc_getClass("WCRedEnvelopesLogicMgr")]; 169 | [logicMgr ReceiverQueryRedEnvelopesRequest:params]; 170 | }; 171 | 172 | /** 储存参数 */ 173 | void (^enqueueParam)(NSDictionary *nativeUrlDict) = ^(NSDictionary *nativeUrlDict) { 174 | WeChatRedEnvelopParam *mgrParams = [[WeChatRedEnvelopParam alloc] init]; 175 | mgrParams.msgType = [nativeUrlDict stringForKey:@"msgtype"]; 176 | mgrParams.sendId = [nativeUrlDict stringForKey:@"sendid"]; 177 | mgrParams.channelId = [nativeUrlDict stringForKey:@"channelid"]; 178 | mgrParams.nickName = [selfContact getContactDisplayName]; 179 | mgrParams.headImg = [selfContact m_nsHeadImgUrl]; 180 | mgrParams.nativeUrl = [[wrap m_oWCPayInfoItem] m_c2cNativeUrl]; 181 | mgrParams.sessionUserName = isGroupSender() ? wrap.m_nsToUsr : wrap.m_nsFromUsr; 182 | mgrParams.sign = [nativeUrlDict stringForKey:@"sign"]; 183 | 184 | mgrParams.isGroupSender = isGroupSender(); 185 | 186 | [[WBRedEnvelopParamQueue sharedQueue] enqueue:mgrParams]; 187 | }; 188 | 189 | if (shouldReceiveRedEnvelop()) { 190 | NSString *nativeUrl = [[wrap m_oWCPayInfoItem] m_c2cNativeUrl]; 191 | NSDictionary *nativeUrlDict = parseNativeUrl(nativeUrl); 192 | 193 | queryRedEnvelopesReqeust(nativeUrlDict); 194 | enqueueParam(nativeUrlDict); 195 | } 196 | } 197 | break; 198 | } 199 | default: 200 | break; 201 | } 202 | 203 | } 204 | 205 | - (void)onRevokeMsg:(CMessageWrap *)arg1 { 206 | 207 | if (![WBRedEnvelopConfig sharedConfig].revokeEnable) { 208 | %orig; 209 | } else { 210 | if ([arg1.m_nsContent rangeOfString:@""].location == NSNotFound) { return; } 211 | if ([arg1.m_nsContent rangeOfString:@""].location == NSNotFound) { return; } 212 | 213 | NSString *(^parseSession)() = ^NSString *() { 214 | NSUInteger startIndex = [arg1.m_nsContent rangeOfString:@""].location + @"".length; 215 | NSUInteger endIndex = [arg1.m_nsContent rangeOfString:@""].location; 216 | NSRange range = NSMakeRange(startIndex, endIndex - startIndex); 217 | return [arg1.m_nsContent substringWithRange:range]; 218 | }; 219 | 220 | NSString *(^parseSenderName)() = ^NSString *() { 221 | NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"" options:NSRegularExpressionCaseInsensitive error:nil]; 222 | 223 | NSRange range = NSMakeRange(0, arg1.m_nsContent.length); 224 | NSTextCheckingResult *result = [regex matchesInString:arg1.m_nsContent options:0 range:range].firstObject; 225 | if (result.numberOfRanges < 2) { return nil; } 226 | 227 | return [arg1.m_nsContent substringWithRange:[result rangeAtIndex:1]]; 228 | }; 229 | 230 | CMessageWrap *msgWrap = [[%c(CMessageWrap) alloc] initWithMsgType:0x2710]; 231 | BOOL isSender = [%c(CMessageWrap) isSenderFromMsgWrap:arg1]; 232 | 233 | NSString *sendContent; 234 | if (isSender) { 235 | [msgWrap setM_nsFromUsr:arg1.m_nsToUsr]; 236 | [msgWrap setM_nsToUsr:arg1.m_nsFromUsr]; 237 | sendContent = @"你撤回一条消息"; 238 | } else { 239 | [msgWrap setM_nsToUsr:arg1.m_nsToUsr]; 240 | [msgWrap setM_nsFromUsr:arg1.m_nsFromUsr]; 241 | 242 | NSString *name = parseSenderName(); 243 | sendContent = [NSString stringWithFormat:@"拦截 %@ 的一条撤回消息", name ? name : arg1.m_nsFromUsr]; 244 | } 245 | [msgWrap setM_uiStatus:0x4]; 246 | [msgWrap setM_nsContent:sendContent]; 247 | [msgWrap setM_uiCreateTime:[arg1 m_uiCreateTime]]; 248 | 249 | [self AddLocalMsg:parseSession() MsgWrap:msgWrap fixTime:0x1 NewMsgArriveNotify:0x0]; 250 | } 251 | } 252 | 253 | %end 254 | 255 | %hook NewSettingViewController 256 | 257 | - (void)reloadTableData { 258 | %orig; 259 | 260 | [self.view layoutIfNeeded]; 261 | 262 | WCTableViewManager *tableViewMgr = MSHookIvar(self, "m_tableViewMgr"); 263 | 264 | WCTableViewSectionManager *sectionInfo = [%c(WCTableViewSectionManager) sectionInfoDefaut]; 265 | 266 | WCTableViewCellManager *settingCell = [%c(WCTableViewCellManager) normalCellForSel:@selector(setting) target:self title:@"微信小助手"]; 267 | [sectionInfo addCell:settingCell]; 268 | 269 | [tableViewMgr insertSection:sectionInfo At:0]; 270 | 271 | MMTableView *tableView = [tableViewMgr getTableView]; 272 | [tableView reloadData]; 273 | } 274 | 275 | %new 276 | - (void)setting { 277 | WBSettingViewController *settingViewController = [WBSettingViewController new]; 278 | [self.navigationController PushViewController:settingViewController animated:YES]; 279 | } 280 | 281 | %end 282 | -------------------------------------------------------------------------------- /src/WBSettingViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBSettingViewController.m 3 | // WeChatRedEnvelop 4 | // 5 | // Created by 杨志超 on 2017/2/22. 6 | // Copyright © 2017年 swiftyper. All rights reserved. 7 | // 8 | 9 | #import "WBSettingViewController.h" 10 | #import "WeChatRedEnvelop.h" 11 | #import "WBRedEnvelopConfig.h" 12 | #import 13 | 14 | @interface WBSettingViewController () 15 | 16 | @property (nonatomic, strong) WCTableViewManager *tableViewMgr; 17 | 18 | @end 19 | 20 | @implementation WBSettingViewController 21 | 22 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 23 | if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { 24 | _tableViewMgr = [[objc_getClass("WCTableViewManager") alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStyleGrouped]; 25 | } 26 | return self; 27 | } 28 | 29 | - (void)viewDidLoad { 30 | [super viewDidLoad]; 31 | 32 | [self initTitle]; 33 | [self reloadTableData]; 34 | 35 | 36 | MMTableView *tableView = [self.tableViewMgr getTableView]; 37 | if (@available(iOS 11, *)) { 38 | tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAutomatic; 39 | } 40 | [self.view addSubview:tableView]; 41 | } 42 | 43 | - (void)viewDidDisappear:(BOOL)animated { 44 | [super viewDidDisappear:animated]; 45 | 46 | [self stopLoading]; 47 | } 48 | 49 | - (void)initTitle { 50 | self.title = @"微信小助手"; 51 | } 52 | 53 | - (void)reloadTableData { 54 | [self.tableViewMgr clearAllSection]; 55 | 56 | [self addBasicSettingSection]; 57 | [self addSupportSection]; 58 | [self addAdvanceSettingSection]; 59 | [self addAboutSection]; 60 | 61 | MMTableView *tableView = [self.tableViewMgr getTableView]; 62 | [tableView reloadData]; 63 | } 64 | 65 | #pragma mark - BasicSetting 66 | 67 | - (void)addBasicSettingSection { 68 | WCTableViewSectionManager *sectionInfo = [objc_getClass("WCTableViewSectionManager") sectionInfoDefaut]; 69 | 70 | [sectionInfo addCell:[self createAutoReceiveRedEnvelopCell]]; 71 | [sectionInfo addCell:[self createDelaySettingCell]]; 72 | 73 | [self.tableViewMgr addSection:sectionInfo]; 74 | } 75 | 76 | - (WCTableViewCellManager *)createAutoReceiveRedEnvelopCell { 77 | return [objc_getClass("WCTableViewCellManager") switchCellForSel:@selector(switchRedEnvelop:) target:self title:@"自动抢红包" on:[WBRedEnvelopConfig sharedConfig].autoReceiveEnable]; 78 | } 79 | 80 | - (WCTableViewNormalCellManager *)createDelaySettingCell { 81 | NSInteger delaySeconds = [WBRedEnvelopConfig sharedConfig].delaySeconds; 82 | NSString *delayString = delaySeconds == 0 ? @"不延迟" : [NSString stringWithFormat:@"%ld 秒", (long)delaySeconds]; 83 | 84 | WCTableViewNormalCellManager *cellInfo = nil; 85 | if ([WBRedEnvelopConfig sharedConfig].autoReceiveEnable) { 86 | cellInfo = [objc_getClass("WCTableViewNormalCellManager") normalCellForSel:@selector(settingDelay) target:self title:@"延迟抢红包" rightValue:delayString accessoryType:1]; 87 | } else { 88 | cellInfo = [objc_getClass("WCTableViewNormalCellManager") normalCellForTitle:@"延迟抢红包" rightValue: @"抢红包已关闭"]; 89 | } 90 | return cellInfo; 91 | } 92 | 93 | - (void)switchRedEnvelop:(UISwitch *)envelopSwitch { 94 | [WBRedEnvelopConfig sharedConfig].autoReceiveEnable = envelopSwitch.on; 95 | 96 | [self reloadTableData]; 97 | } 98 | 99 | - (void)settingDelay { 100 | UIAlertView *alert = [UIAlertView new]; 101 | alert.title = @"延迟抢红包(秒)"; 102 | 103 | alert.alertViewStyle = UIAlertViewStylePlainTextInput; 104 | alert.delegate = self; 105 | [alert addButtonWithTitle:@"取消"]; 106 | [alert addButtonWithTitle:@"确定"]; 107 | 108 | [alert textFieldAtIndex:0].placeholder = @"延迟时长"; 109 | [alert textFieldAtIndex:0].keyboardType = UIKeyboardTypeNumberPad; 110 | [alert show]; 111 | } 112 | 113 | - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { 114 | if (buttonIndex == 1) { 115 | NSString *delaySecondsString = [alertView textFieldAtIndex:0].text; 116 | NSInteger delaySeconds = [delaySecondsString integerValue]; 117 | 118 | [WBRedEnvelopConfig sharedConfig].delaySeconds = delaySeconds; 119 | 120 | [self reloadTableData]; 121 | } 122 | } 123 | 124 | #pragma mark - ProSetting 125 | - (void)addAdvanceSettingSection { 126 | WCTableViewSectionManager *sectionInfo = [objc_getClass("WCTableViewSectionManager") sectionInfoHeader:@"高级功能"]; 127 | 128 | [sectionInfo addCell:[self createReceiveSelfRedEnvelopCell]]; 129 | [sectionInfo addCell:[self createQueueCell]]; 130 | [sectionInfo addCell:[self createAbortRemokeMessageCell]]; 131 | [sectionInfo addCell:[self createBlackListCell]]; 132 | 133 | [self.tableViewMgr addSection:sectionInfo]; 134 | } 135 | 136 | - (WCTableViewCellManager *)createReceiveSelfRedEnvelopCell { 137 | return [objc_getClass("WCTableViewCellManager") switchCellForSel:@selector(settingReceiveSelfRedEnvelop:) target:self title:@"抢自己发的红包" on:[WBRedEnvelopConfig sharedConfig].receiveSelfRedEnvelop]; 138 | } 139 | 140 | - (WCTableViewCellManager *)createQueueCell { 141 | return [objc_getClass("WCTableViewCellManager") switchCellForSel:@selector(settingReceiveByQueue:) target:self title:@"防止同时抢多个红包" on:[WBRedEnvelopConfig sharedConfig].serialReceive]; 142 | } 143 | 144 | - (WCTableViewCellManager *)createBlackListCell { 145 | 146 | if ([WBRedEnvelopConfig sharedConfig].blackList.count == 0) { 147 | return [objc_getClass("WCTableViewNormalCellManager") normalCellForSel:@selector(showBlackList) target:self title:@"群聊过滤" rightValue:@"已关闭" accessoryType:1]; 148 | } else { 149 | NSString *blackListCountStr = [NSString stringWithFormat:@"已选 %lu 个群", (unsigned long)[WBRedEnvelopConfig sharedConfig].blackList.count]; 150 | return [objc_getClass("WCTableViewNormalCellManager") normalCellForSel:@selector(showBlackList) target:self title:@"群聊过滤" rightValue:blackListCountStr accessoryType:1]; 151 | } 152 | 153 | } 154 | 155 | - (WCTableViewSectionManager *)createAbortRemokeMessageCell { 156 | return [objc_getClass("WCTableViewCellManager") switchCellForSel:@selector(settingMessageRevoke:) target:self title:@"消息防撤回" on:[WBRedEnvelopConfig sharedConfig].revokeEnable]; 157 | } 158 | 159 | - (void)settingReceiveSelfRedEnvelop:(UISwitch *)receiveSwitch { 160 | [WBRedEnvelopConfig sharedConfig].receiveSelfRedEnvelop = receiveSwitch.on; 161 | } 162 | 163 | - (void)settingReceiveByQueue:(UISwitch *)queueSwitch { 164 | [WBRedEnvelopConfig sharedConfig].serialReceive = queueSwitch.on; 165 | } 166 | 167 | - (void)showBlackList { 168 | MultiSelectContactsViewController *contactsViewController = [[objc_getClass("MultiSelectContactsViewController") alloc] init]; 169 | contactsViewController.m_scene = 5; 170 | contactsViewController.m_delegate = self; 171 | 172 | // 强制触发 viewDidLoad 调用 173 | if ([contactsViewController respondsToSelector:@selector(loadViewIfNeeded)]) { 174 | [contactsViewController loadViewIfNeeded]; 175 | } else { 176 | contactsViewController.view.alpha = 1.0; 177 | } 178 | 179 | MMContext *context = [objc_getClass("MMContext") activeUserContext]; 180 | CContactMgr *contactMgr = [context getService:objc_getClass("CContactMgr")]; 181 | 182 | ContactSelectView *selectView = (ContactSelectView *)[contactsViewController valueForKey:@"m_selectView"]; 183 | for (NSString *contactName in [WBRedEnvelopConfig sharedConfig].blackList) { 184 | CContact *contact = [contactMgr getContactByName:contactName]; 185 | [selectView addSelect:contact]; 186 | } 187 | [contactsViewController updatePanelBtn]; 188 | 189 | MMUINavigationController *navigationController = [[objc_getClass("MMUINavigationController") alloc] initWithRootViewController:contactsViewController]; 190 | 191 | [self presentViewController:navigationController animated:YES completion:nil]; 192 | } 193 | 194 | - (void)settingMessageRevoke:(UISwitch *)revokeSwitch { 195 | [WBRedEnvelopConfig sharedConfig].revokeEnable = revokeSwitch.on; 196 | } 197 | 198 | #pragma mark - About 199 | 200 | - (void)addAboutSection { 201 | WCTableViewSectionManager *sectionInfo = [objc_getClass("WCTableViewSectionManager") sectionInfoDefaut]; 202 | 203 | [sectionInfo addCell:[self createGithubCell]]; 204 | [sectionInfo addCell:[self createBlogCell]]; 205 | 206 | [self.tableViewMgr addSection:sectionInfo]; 207 | } 208 | 209 | - (WCTableViewNormalCellManager *)createGithubCell { 210 | return [objc_getClass("WCTableViewNormalCellManager") normalCellForSel:@selector(showGithub) target:self title:@"我的 Github" rightValue: @"★ star" accessoryType:1]; 211 | } 212 | 213 | - (WCTableViewNormalCellManager *)createBlogCell { 214 | return [objc_getClass("WCTableViewNormalCellManager") normalCellForSel:@selector(showBlog) target:self title:@"我的博客"]; 215 | } 216 | 217 | - (void)showGithub { 218 | NSURL *gitHubUrl = [NSURL URLWithString:@"https://github.com/buginux/WeChatRedEnvelop"]; 219 | MMWebViewController *webViewController = [[objc_getClass("MMWebViewController") alloc] initWithURL:gitHubUrl presentModal:NO extraInfo:nil]; 220 | [self.navigationController PushViewController:webViewController animated:YES]; 221 | } 222 | 223 | - (void)showBlog { 224 | NSURL *blogUrl = [NSURL URLWithString:@"http://www.swiftyper.com"]; 225 | MMWebViewController *webViewController = [[objc_getClass("MMWebViewController") alloc] initWithURL:blogUrl presentModal:NO extraInfo:nil]; 226 | [self.navigationController PushViewController:webViewController animated:YES]; 227 | } 228 | 229 | #pragma mark - Support 230 | - (void)addSupportSection { 231 | WCTableViewSectionManager *sectionInfo = [objc_getClass("WCTableViewSectionManager") sectionInfoDefaut]; 232 | 233 | [sectionInfo addCell:[self createWeChatPayingCell]]; 234 | [sectionInfo addCell:[self createOfficalAccountCell]]; 235 | 236 | [self.tableViewMgr addSection:sectionInfo]; 237 | } 238 | 239 | - (WCTableViewNormalCellManager *)createWeChatPayingCell { 240 | return [objc_getClass("WCTableViewNormalCellManager") normalCellForSel:@selector(payingToAuthor) target:self title:@"微信打赏" rightValue:@"支持作者开发" accessoryType:1]; 241 | } 242 | 243 | - (WCTableViewNormalCellManager *)createOfficalAccountCell { 244 | MMContext *context = [objc_getClass("MMContext") activeUserContext]; 245 | CContactMgr *contactMgr = [context getService:objc_getClass("CContactMgr")]; 246 | 247 | NSString *rightValue = @"未关注"; 248 | if ([contactMgr isInContactList:@"gh_6e8bddcdfca3"]) { 249 | rightValue = @"已关注"; 250 | } else { 251 | rightValue = @"未关注"; 252 | CContact *contact = [contactMgr getContactForSearchByName:@"gh_6e8bddcdfca3"]; 253 | [contactMgr addLocalContact:contact listType:2]; 254 | [contactMgr getContactsFromServer:@[contact]]; 255 | } 256 | 257 | return [objc_getClass("WCTableViewNormalCellManager") normalCellForSel:@selector(followMyOfficalAccount) target:self title:@"我的公众号" rightValue:rightValue accessoryType:1]; 258 | } 259 | 260 | - (void)payingToAuthor { 261 | ScanQRCodeLogicParams *logicParams = [[objc_getClass("ScanQRCodeLogicParams") alloc] initWithCodeType:31 fromScene:1]; 262 | ScanQRCodeLogicController *scanQRCodeLogic = [[objc_getClass("ScanQRCodeLogicController") alloc] initWithViewController:self logicParams:logicParams]; 263 | 264 | NewQRCodeScannerParams *scannerParams = [[objc_getClass("NewQRCodeScannerParams") alloc] initWithCodeType:31]; 265 | NewQRCodeScanner *qrCodeScanner = [[objc_getClass("NewQRCodeScanner") alloc] initWithDelegate:scanQRCodeLogic scannerParams:scannerParams]; 266 | 267 | NSBundle *bundle = [[NSBundle alloc] initWithPath:kBundlePath]; 268 | NSString *imagePath = [bundle pathForResource:@"IMG_0018" ofType:@"JPG"]; 269 | UIImage *qrImage = [UIImage imageWithContentsOfFile:imagePath]; 270 | 271 | NSLog(@"bundle: %@, imagePath: %@, qrImage: %@", bundle, imagePath, qrImage); 272 | 273 | if (qrImage) { 274 | [self startLoadingNonBlock]; 275 | [qrCodeScanner scanOnePicture:qrImage]; 276 | } 277 | } 278 | 279 | - (void)followMyOfficalAccount { 280 | MMContext *context = [objc_getClass("MMContext") activeUserContext]; 281 | CContactMgr *contactMgr = [context getService:objc_getClass("CContactMgr")]; 282 | 283 | CContact *contact = [contactMgr getContactByName:@"gh_6e8bddcdfca3"]; 284 | 285 | ContactInfoViewController *contactViewController = [[objc_getClass("ContactInfoViewController") alloc] init]; 286 | [contactViewController setM_contact:contact]; 287 | 288 | [self.navigationController PushViewController:contactViewController animated:YES]; 289 | } 290 | 291 | #pragma mark - MultiSelectContactsViewControllerDelegate 292 | 293 | - (void)onMultiSelectContactReturn:(NSArray *)arg1 { 294 | NSMutableArray *blackList = [NSMutableArray new]; 295 | for (CContact *contact in arg1) { 296 | NSString *contactName = contact.m_nsUsrName; 297 | if ([contactName length] > 0 && [contactName hasSuffix:@"@chatroom"]) { 298 | [blackList addObject:contactName]; 299 | } 300 | } 301 | [WBRedEnvelopConfig sharedConfig].blackList = blackList; 302 | [self reloadTableData]; 303 | [self dismissViewControllerAnimated:YES completion:nil]; 304 | } 305 | 306 | @end 307 | --------------------------------------------------------------------------------