实现抢红包的基本流程
- 收到红包消息 (MessageCenter)
- 列表上出现一个红包组件( RedPocketButton )
- 点击进入红包页面
- 点击”抢红包“按钮
- loading
- 给服务器发一个请求,服务器告诉我抢到了多少钱,以及哪些人抢到了红白以及份额
##实现自动抢红包插件需要知道哪些信息
- 如何自动拆红包?代码里面拆红包的逻辑再哪里?是否能直接调用来拆红包?如果不能需要准备哪些前置条件?
- 我们什么时候触发抢红包这个动作?
开始分析
点击红包后,进入抢红包详情页, 其视图结构是:
UIWindow
–DTOpenLuckyMoneyView
----DTOpenLuckyMoneyEntityView
查看头文件信息(Flex/class-dump)
@interface DTOpenLuckyMoneyEntityView:UIView{
id <DTOpenLuckyMoneyEntityViewDelegate> *delegate;
DTBizRedEnvelopClusterPickingStatus *pickingStatus;
UIButton *_openLuckyMoneyButton;
}
- didClickOpenLuckyMoneyBtn:(id)
- openLuckyMoney:(CGFloat) stopDuration:(CGFloat) completionBlock:id
- performLoading
- stopLoading
@end
@interface DTOpenLuckyMoneyView:UIView{
id<DTOpenLuckyMoneyViewDelegte> *delegate
DTBizRedEnvelopClusterPickingStatus *statusModel
}
-(void)utOpenRedEnvelop:(id)
-(void)utSeeRedEnvelop:(id)
- (void)beginLoading
- (void)endLoading
...
- (void)didClickOpenLuckyMoney:(id)
@end
如何快速确定这个"拆"红包Button的 selector ? 会是这里的- didClickOpenLuckyMoneyBtn:(id)
吗?
在Flex中 查看UIButton的 _targetActions ,这是一个可变数组,每个item是一个UIControlTargetAction的对象。可以看到 _action = - didClickOpenLuckyMoneyBtn:(id)
, _target 是一个 DTOpenLuckyMoneyEntityView:UIView
类型的对象,说明我们的猜测是对的。
同时我们发现DTOpenLuckyMoneyEntityView
视图对象 的 delegate 是一个 DTOpenLuckyMoneyView
类型对象,也就是父 View 作为 delegate。
从目前来看,DTOpenLuckyMoneyView
有较大可能 负责拆 红包的逻辑,看下- (void)didClickOpenLuckyMoney:
方法的汇编逻辑:
大概逻辑是:
- (void)didClickOpenLuckyMoney:(id){
[self statusModel];
[self utOpenRedEnvelop:]
[self beginLoading];
id factory = [DTRedEnvelopServiceFactory defaultServiceIMP];
[[self redEnvelopCluster] sender] ->x25->x2
[self clusterId] -> x28->x3
[factory pickRedEnvelopCluster:x2 clusterId:x3 successBlock: x4, failureBlock : x5 ] //
}
看到这里,这里的pickRedEnvelopCluster:clusterId: successBlock:failureBlock:
这个方法极有可能就是拆红包的api ,通过搜索 pickRedEnvelopCluster:clusterId: successBlock:failureBlock:
这个方法,不难找出哪些类有这个方法,从而定位到这是哪个类的方法。
经过搜索,这里的factory 就是 一个DTRedEnvelopServiceIMP
对象了。我们看下方法的原型:
- (void)pickRedEnvelopCluster:(long long)arg1 clusterId:(id)arg2 successBlock:(CDUnknownBlockType)arg3 failureBlock:(CDUnknownBlockType)arg4;
- cluster: sender 从名字上看可能是发送红包的用户id 。我发了一个红包,通过查看我的个人信息用户(比如信息展示页面),查询到 我的 uid 与这里的 sender 一致。
- clusterId: 群相关 id, 形如
5PpGVx79
- successBlock:
- failureBlock:
同时我们发现,DTBizRedEnvelopClusterPickingStatus->DTBizRedEnvelopCluster
这里保存有红包的基本信息,我们可以使用这里的数据,call下这个api,测试看能否拆开红包。比如发了一红包后 ,sender = 79903973 ,clusterId = @“5PpKkC3K”
我们可以使用cycript/Flex call 下这个api,传入红包数据看下,这个api 是否能拆开红包。如下:
[[DTRedEnvelopServiceFactory defaultServiceIMP] pickRedEnvelopCluster:79903973 clusterId:@"5PpKkC3K" successBlock:nil failurelock:nil]
发现红包被领取了。因此确认pickRedEnvelopCluster:clusterId: successBlock:failureBlock:
就是拆红包的方法。
接下来
- 什么时机拆红包?
- 拆红包的参数数据 sender, clusterId 从哪里来?
但是如果没有拆红白的视图,我们就没法使用 DTBizRedEnvelopClusterPickingStatus->DTBizRedEnvelopCluster
里面的信息? 有没有可能在其它地方获得 发红包用户id
和 群id
这两个信息呢?
可以看到,每当收到消息时,在首页列表页上都能跟新到最新的消息,因此首页上一定有接收红包信息相关的线索。如果这里也有我们需要的红包数据,问题就解决了。
会话列表页是DTConversationListController
:
@interface DTConversationListController:DTViewController{
DTConversationListDataSource *dataSource;
DTTableView *tableView; // delegate/dataSource 是 DTConversationListController
...
}
- observeValueForKeyPath(id) ofObject:(id) change(id) context(void *)
- onRecvMsg:(id)
- onXXX
@end
@interface DTConversationListDataSource: NSObject{
LWFetchedResultsController *fetchController; // 其delegate 就是 DTConversationListDataSource 对象
DTTableView *tableView; // 数据更新后,在这里直接update tableView
}
- (void)asyncLoadAllLocalConversations;
- (void)loadAllConversations;
- (void)controller:(id) didChangeObject:(id) atIndex:(NSInteger) forChangeType:(NSInteger) newIndex:(NSUinteger) // 应该是 LWFetchedResultsController delegate 方法
@end
当收到一条消息时,DTConversationListDataSource 哪个方法会收到消息? 是“- (void)controller:(id) didChangeObject:(id) atIndex:(NSInteger) forChangeType:(NSInteger) newIndex:(NSUinteger)” 这个方法吗? 他的参数里面都有哪些信息?
LLDB + debugserver 断点调试如下:
可以看到,收到(红包)消息时,切实可以出发断点,查看消息内容,确实是我们想要的消息数据:
通过各种测试,我们发现:
//单人红包
{
"contentType" : 902,
"attachments" : [
{
"type" : 902,
"extension" : {
"amount" : "1.00",
"clusterid" : "3ga8WJ4X",
"size" : "1",
"congrats" : "恭喜发财,大吉大利!",
"sid" : "14524910",
...
}
}
]
}
//群红包
{
"contentType" : 901,
"attachments" : [
{
"type" : 901,
"extension" : {
"amount" : "1.00",
"clusterid" : "66dAJ8IT",
"size" : "3",
"congrats" : "恭喜发财,大吉大利!",
"sid" : "14524910",
...
}
}
]
}
//正值元旦季节,元旦红包数据格式如下
{
"contentType" : 905,
"attachments" : [
{
"extension" : {
"amount" : "0.10",
"clusterid" : "FPGjJ5No",
"p_name" : "元旦红包",
"sid" : "16201043",
"size" : "1",
"congrats" : "十分祝福!",
"sname" : “xxx"
...
},
"isPreload" : false
}
]
}
可以看到 901 是群红包,902 是个人红包, 905 是元旦红包。
Theos 工程
总结流程下:
- hook -[DTConversationListDataSource controller:didChangeObject:atIndex:forChangeType:newIndex:]
- 收到消息是得到红包数据 WKBizConversation->latestMessage->attachmentsJson
- 从attachmentsJson 中解析出抢红包需要的参数sender,clusterId
- 调用[[DTRedEnvelopServiceFactory defaultServiceIMP] pickRedEnvelopCluster:sender clusterId:clusterId successBlock:nil failureBlock:nil]; 完成自动抢红包动作
其中 bundleid = com.laiwang.DingTalk