blib/lib/Mojo/Weixin/Message/Handle.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 36 | 424 | 8.4 |
branch | 0 | 282 | 0.0 |
condition | 0 | 85 | 0.0 |
subroutine | 12 | 27 | 44.4 |
pod | 4 | 7 | 57.1 |
total | 52 | 825 | 6.3 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | package Mojo::Weixin; | ||||||
2 | 1 | 1 | 8 | use strict; | |||
1 | 2 | ||||||
1 | 45 | ||||||
3 | 1 | 1 | 7 | use Mojo::Weixin::Const qw(%KEY_MAP_USER %KEY_MAP_GROUP %KEY_MAP_GROUP_MEMBER %KEY_MAP_FRIEND %KEY_MAP_MEDIA_CODE); | |||
1 | 2 | ||||||
1 | 111 | ||||||
4 | 1 | 1 | 7 | use List::Util qw(first); | |||
1 | 2 | ||||||
1 | 109 | ||||||
5 | 1 | 1 | 402 | use Mojo::Weixin::Message; | |||
1 | 3 | ||||||
1 | 15 | ||||||
6 | 1 | 1 | 42 | use Mojo::Weixin::Const; | |||
1 | 2 | ||||||
1 | 93 | ||||||
7 | 1 | 1 | 7 | use Mojo::Weixin::Message; | |||
1 | 3 | ||||||
1 | 4 | ||||||
8 | 1 | 1 | 795 | use Mojo::Weixin::Message::Queue; | |||
1 | 3 | ||||||
1 | 37 | ||||||
9 | 1 | 1 | 442 | use Mojo::Weixin::Message::Remote::_upload_media; | |||
1 | 3 | ||||||
1 | 31 | ||||||
10 | 1 | 1 | 432 | use Mojo::Weixin::Message::Remote::_get_media; | |||
1 | 3 | ||||||
1 | 41 | ||||||
11 | 1 | 1 | 445 | use Mojo::Weixin::Message::Remote::_send_media_message; | |||
1 | 3 | ||||||
1 | 29 | ||||||
12 | 1 | 1 | 716 | use Mojo::Weixin::Message::Remote::_send_text_message; | |||
1 | 3 | ||||||
1 | 33 | ||||||
13 | 1 | 1 | 424 | use Mojo::Weixin::Message::Remote::_revoke_message; | |||
1 | 2 | ||||||
1 | 4925 | ||||||
14 | $Mojo::Weixin::Message::LAST_DISPATCH_TIME = undef; | ||||||
15 | $Mojo::Weixin::Message::SEND_INTERVAL = 3; | ||||||
16 | |||||||
17 | sub gen_message_queue{ | ||||||
18 | 0 | 0 | 0 | my $self = shift; | |||
19 | Mojo::Weixin::Message::Queue->new(callback_for_get=>sub{ | ||||||
20 | 0 | 0 | my $msg = shift; | ||||
21 | 0 | 0 | return if $self->is_stop; | ||||
22 | 0 | 0 | if($msg->class eq "recv"){ | ||||
0 | |||||||
23 | 0 | 0 | if($msg->format eq "media"){ | ||||
24 | 0 | 0 | if($self->download_media){ | ||||
25 | $self->_get_media($msg,sub{ | ||||||
26 | 0 | my ($path,$data,$msg) = @_; | |||||
27 | 0 | 0 | 0 | if($msg->media_size == 0 and $msg->media_type eq 'emoticon'){ | |||
28 | 0 | $msg->content("[表情](获取数据为空,可能需要手机查看)"); | |||||
29 | } | ||||||
30 | else{ | ||||||
31 | 0 | $msg->content( $msg->content. "(". $msg->media_path . ")"); | |||||
32 | } | ||||||
33 | 0 | $self->emit(receive_media=>$path,$data,$msg); | |||||
34 | 0 | $self->emit(receive_message=>$msg); | |||||
35 | 0 | }); | |||||
36 | } | ||||||
37 | else{ | ||||||
38 | 0 | $self->emit(receive_message=>$msg); | |||||
39 | } | ||||||
40 | } | ||||||
41 | 0 | else{ $self->emit(receive_message=>$msg);} | |||||
42 | } | ||||||
43 | elsif($msg->class eq "send"){ | ||||||
44 | 0 | 0 | if($msg->source ne "local"){ | ||||
45 | 0 | $msg->send_status(code=>0,msg=>"发送成功",info=>"来自其他设备"); | |||||
46 | 0 | 0 | if($msg->format eq "media"){ | ||||
47 | 0 | 0 | if($self->download_media){ | ||||
48 | $self->_get_media($msg,sub{ | ||||||
49 | 0 | my ($path,$data,$msg) = @_; | |||||
50 | 0 | 0 | 0 | if($msg->media_size == 0 and $msg->media_type eq 'emoticon'){ | |||
51 | 0 | $msg->content("[表情](获取数据为空,可能需要手机查看)"); | |||||
52 | } | ||||||
53 | else{ | ||||||
54 | 0 | $msg->content( $msg->content. "(". $msg->media_path . ")"); | |||||
55 | } | ||||||
56 | 0 | 0 | $msg->cb->($self,$msg) if ref $msg->cb eq 'CODE'; | ||||
57 | 0 | $self->emit(send_media=>$path,$data,$msg); | |||||
58 | 0 | $self->emit(send_message=>$msg); | |||||
59 | 0 | }); | |||||
60 | } | ||||||
61 | else{ | ||||||
62 | 0 | 0 | $msg->cb->($self,$msg) if ref $msg->cb eq 'CODE'; | ||||
63 | 0 | $self->emit(send_message=>$msg); | |||||
64 | } | ||||||
65 | } | ||||||
66 | else{ | ||||||
67 | 0 | 0 | $msg->cb->($self,$msg) if ref $msg->cb eq 'CODE'; | ||||
68 | 0 | $self->emit(send_message=>$msg); | |||||
69 | } | ||||||
70 | 0 | return; | |||||
71 | } | ||||||
72 | #消息的ttl值减少到0则丢弃消息 | ||||||
73 | 0 | 0 | if($msg->ttl <= 0){ | ||||
74 | 0 | $self->debug("消息[ " . $msg->id. " ]已被消息队列丢弃,当前TTL: ". $msg->ttl); | |||||
75 | 0 | $msg->send_status(code=>-5,msg=>"发送失败",info=>"TTL失效"); | |||||
76 | 0 | 0 | if(ref $msg->cb eq 'CODE'){ | ||||
77 | 0 | $msg->cb->( | |||||
78 | $self, | ||||||
79 | $msg, | ||||||
80 | ); | ||||||
81 | } | ||||||
82 | 0 | $self->emit(send_message=> | |||||
83 | $msg, | ||||||
84 | ); | ||||||
85 | 0 | return; | |||||
86 | } | ||||||
87 | 0 | my $ttl = $msg->ttl; | |||||
88 | 0 | $msg->ttl(--$ttl); | |||||
89 | |||||||
90 | 0 | my $delay = 0; | |||||
91 | 0 | my $now = time; | |||||
92 | 0 | 0 | if(defined $Mojo::Weixin::Message::LAST_DISPATCH_TIME){ | ||||
93 | 0 | 0 | $delay = $now<$Mojo::Weixin::Message::LAST_DISPATCH_TIME+$Mojo::Weixin::Message::SEND_INTERVAL? | ||||
94 | $Mojo::Weixin::Message::LAST_DISPATCH_TIME+$Mojo::Weixin::Message::SEND_INTERVAL-$now | ||||||
95 | : 0; | ||||||
96 | } | ||||||
97 | $self->timer($delay,sub{ | ||||||
98 | 0 | $msg->time(time); | |||||
99 | 0 | 0 | if($msg->format eq "text"){ | ||||
0 | |||||||
100 | 0 | $self->_send_text_message($msg); | |||||
101 | } | ||||||
102 | elsif($msg->format eq "media"){ | ||||||
103 | 0 | $self->_send_media_message($msg); | |||||
104 | } | ||||||
105 | 0 | }); | |||||
106 | 0 | $Mojo::Weixin::Message::LAST_DISPATCH_TIME = $now+$delay; | |||||
107 | } | ||||||
108 | 0 | }); | |||||
109 | } | ||||||
110 | sub _parse_synccheck_data{ | ||||||
111 | 0 | 0 | my @logout_code = qw(1100 1101 1102 1205); | ||||
112 | 0 | my $self = shift; | |||||
113 | 0 | my($retcode,$selector) = @_; | |||||
114 | 0 | 0 | 0 | if(defined $retcode and defined $selector){ | |||
115 | 0 | 0 | 0 | if($retcode == 0 and $selector != 0){ | |||
0 | 0 | ||||||
0 | 0 | ||||||
0 | |||||||
0 | |||||||
116 | 0 | $self->_synccheck_error_count(0); | |||||
117 | 0 | $self->_sync(); | |||||
118 | } | ||||||
119 | elsif($retcode == 0 and $selector == 0){ | ||||||
120 | 0 | $self->_synccheck_error_count(0); | |||||
121 | } | ||||||
122 | elsif($retcode == 1101 and $self->stop_with_mobile){ | ||||||
123 | 0 | $self->stop(); | |||||
124 | } | ||||||
125 | 0 | 0 | elsif(first {$retcode == $_} @logout_code){ | ||||
126 | 0 | $self->relogin($retcode); | |||||
127 | 0 | return; | |||||
128 | } | ||||||
129 | elsif($self->_synccheck_error_count <= 10){ | ||||||
130 | 0 | my $c = $self->_synccheck_error_count; | |||||
131 | 0 | $self->_synccheck_error_count(++$c); | |||||
132 | } | ||||||
133 | else{ | ||||||
134 | 0 | $self->relogin(); | |||||
135 | 0 | return; | |||||
136 | } | ||||||
137 | } | ||||||
138 | } | ||||||
139 | sub _parse_sync_data { | ||||||
140 | 0 | 0 | my $self = shift; | ||||
141 | 0 | my $json = shift; | |||||
142 | 0 | 0 | return if not defined $json; | ||||
143 | 0 | my @logout_code = qw(1100 1102 1205); | |||||
144 | 0 | 0 | if($json->{BaseResponse}{Ret} == 1101){#手机端强制下线 或 其他设备登录Web微信 | ||||
0 | |||||||
0 | |||||||
145 | 0 | $self->info("收到下线通知"); | |||||
146 | 0 | $self->logout($json->{BaseResponse}{Ret}); | |||||
147 | 0 | $self->stop(); | |||||
148 | } | ||||||
149 | 0 | 0 | elsif(first {$json->{BaseResponse}{Ret} == $_} @logout_code ){ | ||||
150 | 0 | $self->relogin($json->{BaseResponse}{Ret}); | |||||
151 | 0 | return; | |||||
152 | } | ||||||
153 | elsif($json->{BaseResponse}{Ret} !=0){ | ||||||
154 | 0 | $self->warn("收到无法识别消息代码[$json->{BaseResponse}{Ret}],已将其忽略"); | |||||
155 | 0 | $self->emit(unknown_retcode=>$json->{BaseResponse}{Ret}); | |||||
156 | 0 | return; | |||||
157 | } | ||||||
158 | 0 | 0 | $self->sync_key($json->{SyncKey}) if $json->{SyncKey}{Count}!=0; | ||||
159 | 0 | 0 | $self->synccheck_key($json->{SyncCheckKey}) if $json->{SyncCheckKey}{Count}!=0; | ||||
160 | 0 | 0 | $self->skey($json->{SKey}) if $json->{SKey}; | ||||
161 | |||||||
162 | |||||||
163 | #群组或联系人变更 | ||||||
164 | 0 | 0 | if($json->{ModContactCount}!=0){ | ||||
165 | 0 | for my $e (@{$json->{ModContactList}}){ | |||||
0 | |||||||
166 | 0 | 0 | if($self->is_group_id($e->{UserName})){#群组 | ||||
167 | 0 | my $group = {member=>[]}; | |||||
168 | 0 | for(keys %KEY_MAP_GROUP){ | |||||
169 | 0 | 0 | $group->{$_} = $e->{$KEY_MAP_GROUP{$_}} // ""; | ||||
170 | } | ||||||
171 | 0 | 0 | if($e->{MemberCount} != 0){ | ||||
172 | 0 | for my $m (@{$e->{MemberList}}){ | |||||
0 | |||||||
173 | 0 | my $member = {}; | |||||
174 | 0 | for(keys %KEY_MAP_GROUP_MEMBER){ | |||||
175 | 0 | 0 | $member->{$_} = $m->{$KEY_MAP_GROUP_MEMBER{$_}} // ""; | ||||
176 | } | ||||||
177 | 0 | push @{ $group->{member} }, $member; | |||||
0 | |||||||
178 | } | ||||||
179 | } | ||||||
180 | 0 | my $g = $self->search_group(id=>$group->{id}); | |||||
181 | 0 | 0 | if(not defined $g){#新增群组 | ||||
182 | 0 | 0 | if(not $self->update_group($group->{id},1)){ | ||||
183 | 0 | $self->add_group(Mojo::Weixin::Group->new($group)); | |||||
184 | } | ||||||
185 | } | ||||||
186 | else{#更新已有联系人 | ||||||
187 | 0 | $g->update($group); | |||||
188 | } | ||||||
189 | } | ||||||
190 | else{#联系人 | ||||||
191 | 0 | my $friend = {}; | |||||
192 | 0 | for(keys %KEY_MAP_FRIEND){ | |||||
193 | 0 | 0 | $friend->{$_} = $e->{$KEY_MAP_FRIEND{$_}} if defined $e->{$KEY_MAP_FRIEND{$_}}; | ||||
194 | } | ||||||
195 | 0 | my $f = $self->search_friend(id=>$friend->{id}); | |||||
196 | 0 | 0 | if(not defined $f){ | ||||
197 | 0 | $self->add_friend(Mojo::Weixin::Friend->new($friend)); | |||||
198 | } | ||||||
199 | 0 | else{$f->update($friend)} | |||||
200 | } | ||||||
201 | } | ||||||
202 | } | ||||||
203 | |||||||
204 | 0 | 0 | if($json->{ModChatRoomMemberCount}!=0){ | ||||
205 | |||||||
206 | } | ||||||
207 | |||||||
208 | 0 | 0 | if($json->{DelContactCount}!=0){ | ||||
209 | 0 | for my $e (@{$json->{DelContactList}}){ | |||||
0 | |||||||
210 | 0 | 0 | if($self->is_group_id($e->{UserName})){ | ||||
211 | 0 | my $g = $self->search_group(id=>$e->{UserName}); | |||||
212 | 0 | 0 | $self->remove_group($g) if defined $g; | ||||
213 | } | ||||||
214 | else{ | ||||||
215 | 0 | my $f = $self->search_friend(id=>$e->{UserName}); | |||||
216 | 0 | 0 | $self->remove_friend($f) if defined $f; | ||||
217 | } | ||||||
218 | } | ||||||
219 | } | ||||||
220 | |||||||
221 | #有新消息 | ||||||
222 | 0 | 0 | if($json->{AddMsgCount} != 0){ | ||||
223 | 0 | for my $e (@{$json->{AddMsgList}}){ | |||||
0 | |||||||
224 | 0 | my $msg = {}; | |||||
225 | 0 | for(keys %KEY_MAP_MESSAGE){ | |||||
226 | 0 | 0 | $msg->{$_} = $e->{$KEY_MAP_MESSAGE{$_}} // ""; | ||||
227 | } | ||||||
228 | 0 | 0 | 0 | if($e->{MsgType} == 1){#好友消息或群消息 | |||
0 | 0 | ||||||
0 | 0 | ||||||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
229 | 0 | $msg->{format} = "text"; | |||||
230 | } | ||||||
231 | elsif($e->{MsgType} == 3){#图片消息 | ||||||
232 | 0 | $msg->{format} = "media"; | |||||
233 | 0 | $msg->{media_type} = "image"; | |||||
234 | 0 | $msg->{media_code} = $e->{MsgType}; | |||||
235 | 0 | $msg->{media_id} = $msg->{id} . ":" . $msg->{media_code}; | |||||
236 | } | ||||||
237 | elsif($e->{MsgType} == 47){#表情或gif图片 | ||||||
238 | 0 | $msg->{format} = "media"; | |||||
239 | 0 | $msg->{media_type} = "emoticon"; | |||||
240 | 0 | $msg->{media_code} = $e->{MsgType}; | |||||
241 | 0 | $msg->{media_id} = $msg->{id} . ":" . $msg->{media_code}; | |||||
242 | } | ||||||
243 | elsif($e->{MsgType} == 62){#小视频 | ||||||
244 | 0 | $msg->{format} = "media"; | |||||
245 | 0 | $msg->{media_type} = "microvideo"; | |||||
246 | 0 | $msg->{media_code} = $e->{MsgType}; | |||||
247 | 0 | $msg->{media_id} = $msg->{id} . ":" . $msg->{media_code}; | |||||
248 | } | ||||||
249 | elsif($e->{MsgType} == 43){#视频 | ||||||
250 | 0 | $msg->{format} = "media"; | |||||
251 | 0 | $msg->{media_type} = "video"; | |||||
252 | 0 | $msg->{media_code} = $e->{MsgType}; | |||||
253 | 0 | $msg->{media_id} = $msg->{id} . ":" . $msg->{media_code}; | |||||
254 | } | ||||||
255 | elsif($e->{MsgType} == 34){#语音 | ||||||
256 | 0 | $msg->{format} = "media"; | |||||
257 | 0 | $msg->{media_type} = "voice"; | |||||
258 | 0 | $msg->{media_code} = $e->{MsgType}; | |||||
259 | 0 | $msg->{media_id} = $msg->{id} . ":" . $msg->{media_code}; | |||||
260 | } | ||||||
261 | elsif($e->{MsgType} == 37){#好友推荐消息 | ||||||
262 | 0 | $msg->{format} = "text"; | |||||
263 | #$msg->{class} = "recv"; | ||||||
264 | #$msg->{type} = "friend_message"; | ||||||
265 | #$msg->{receiver_id} = $self->user->id; | ||||||
266 | #$msg->{sender_id} = $e->{FromUserName}; | ||||||
267 | 0 | my $id = $e->{RecommendInfo}{UserName}; | |||||
268 | 0 | my $displayname = $e->{RecommendInfo}{NickName}; | |||||
269 | 0 | my $verify = $e->{RecommendInfo}{Content}; | |||||
270 | 0 | my $ticket = $e->{RecommendInfo}{Ticket}; | |||||
271 | #$msg->data({id=>$id,verify=>$verify,ticket=>$ticket,displayname=>$displayname}); | ||||||
272 | #$msg->{content} = "收到[ " . $displayname . " ]好友验证请求:" . ($verify?$verify:"(验证内容为空)"); | ||||||
273 | 0 | $self->_webwxstatusnotify($e->{FromUserName},1); | |||||
274 | 0 | $self->emit("friend_request",$id,$displayname,$verify,$ticket); | |||||
275 | 0 | next; | |||||
276 | } | ||||||
277 | elsif($e->{MsgType} == 10000){#群提示消息 | ||||||
278 | 0 | $msg->{format} = "text"; | |||||
279 | } | ||||||
280 | elsif($e->{MsgType} == 10002){#撤回消息 | ||||||
281 | 0 | $msg->{format} = "revoke"; | |||||
282 | } | ||||||
283 | elsif($e->{MsgType} == 49 and $e->{AppMsgType} == 6) {#文件分享 | ||||||
284 | 0 | $msg->{format} = "media"; | |||||
285 | 0 | $msg->{media_type} = "file"; | |||||
286 | 0 | $msg->{media_code} = $e->{AppMsgType}; | |||||
287 | 0 | $msg->{media_id} = $e->{MediaId} . ":" . $e->{AppMsgType}; | |||||
288 | 0 | $msg->{media_name} = $e->{FileName}; | |||||
289 | 0 | $msg->{media_size} = $e->{FileSize}; | |||||
290 | } | ||||||
291 | elsif($e->{MsgType} == 49 and $e->{AppMsgType} == 5) {#应用分享 | ||||||
292 | 0 | $msg->{format} = "app"; | |||||
293 | 0 | $msg->{app_title} = $e->{FileName}; | |||||
294 | 0 | $msg->{app_url} = $e->{Url}; | |||||
295 | } | ||||||
296 | elsif($e->{MsgType} == 49 and $e->{AppMsgType} == 2000){#转账信息 | ||||||
297 | 0 | $msg->{format} = "payment"; | |||||
298 | } | ||||||
299 | elsif($e->{MsgType} == 42){#名片消息 | ||||||
300 | 0 | $msg->{format} = "card"; | |||||
301 | 0 | $msg->{card_name} = $e->{RecommendInfo}{NickName}; | |||||
302 | 0 | $msg->{card_id} = $e->{RecommendInfo}{UserName}; | |||||
303 | 0 | $msg->{card_province} = $e->{RecommendInfo}{Province}; | |||||
304 | 0 | $msg->{card_city} = $e->{RecommendInfo}{City}; | |||||
305 | 0 | $msg->{card_account} = $e->{RecommendInfo}{Alias}; | |||||
306 | 0 | $msg->{card_sex} = $self->code2sex($e->{RecommendInfo}{Sex}); | |||||
307 | #$msg->{card_avatar} = ''; | ||||||
308 | } | ||||||
309 | elsif($e->{MsgType} == 51){#会话、联系人信息同步 | ||||||
310 | 0 | 0 | 0 | if($e->{StatusNotifyCode} == 4 or $e->{StatusNotifyCode} == 2){#联系人、群组信息需要同步 | |||
311 | 0 | my @id = split /,/,$e->{StatusNotifyUserName}; | |||||
312 | 0 | my @group_ids; | |||||
313 | my @friend_ids; | ||||||
314 | 0 | for (@id){ | |||||
315 | 0 | 0 | next if $_ eq $self->user->id; | ||||
316 | 0 | 0 | if($self->is_group_id($_)){push @group_ids,$_ if not $self->search_group(id=>$_);} | ||||
0 | 0 | ||||||
317 | 0 | 0 | else{push @friend_ids,$_ if not $self->search_friend(id=>$_);} | ||||
318 | } | ||||||
319 | 0 | 0 | $self->update_group(@group_ids) if @group_ids; | ||||
320 | 0 | 0 | $self->update_friend(@friend_ids) if @friend_ids; | ||||
321 | } | ||||||
322 | 0 | next; | |||||
323 | } | ||||||
324 | 0 | else{next;} | |||||
325 | 0 | 0 | if($e->{FromUserName} eq $self->user->id){#发送的消息 | ||||
326 | 0 | $msg->{source} = 'outer'; | |||||
327 | 0 | $msg->{class} = "send"; | |||||
328 | 0 | $msg->{sender_id} = $self->user->id; | |||||
329 | 0 | 0 | if($self->is_group_id($e->{ToUserName})){ | ||||
330 | 0 | $msg->{type} = "group_message"; | |||||
331 | 0 | $msg->{group_id} = $e->{ToUserName}; | |||||
332 | } | ||||||
333 | else{ | ||||||
334 | 0 | $msg->{type} = "friend_message"; | |||||
335 | 0 | $msg->{receiver_id} = $e->{ToUserName}; | |||||
336 | } | ||||||
337 | } | ||||||
338 | #elsif($e->{ToUserName} eq $self->user->id){#接收的消息 | ||||||
339 | else{#接收的消息 | ||||||
340 | 0 | $msg->{class} = "recv"; | |||||
341 | 0 | $msg->{receiver_id} = $self->user->id; | |||||
342 | 0 | $msg->{type} = "group_message"; | |||||
343 | 0 | 0 | if($self->is_group_id($e->{FromUserName})){#接收到群组消息 | ||||
344 | 0 | $msg->{group_id} = $e->{FromUserName}; | |||||
345 | 0 | 0 | if($e->{MsgType} == 10000){#群提示信息 | ||||
0 | |||||||
346 | 0 | $msg->{type} = "group_notice"; | |||||
347 | } | ||||||
348 | elsif( $msg->{content}=~/^(\@.+?): (.*)$/s ){ |
||||||
349 | 0 | my ($member_id,$content) = ($1,$2); | |||||
350 | 0 | 0 | 0 | if(defined $member_id and defined $content){ | |||
351 | 0 | $msg->{sender_id} = $member_id; | |||||
352 | 0 | $msg->{content} = $content; | |||||
353 | } | ||||||
354 | } | ||||||
355 | } | ||||||
356 | else{#接收到的好友消息 | ||||||
357 | 0 | $msg->{type} = "friend_message"; | |||||
358 | 0 | $msg->{sender_id} = $e->{FromUserName}; | |||||
359 | } | ||||||
360 | } | ||||||
361 | 0 | 0 | if($msg->{format} eq "media"){ | ||||
0 | |||||||
362 | 0 | 0 | $msg->{content} = '[图片]' if $msg->{media_type} eq "image"; | ||||
363 | 0 | 0 | $msg->{content} = '[语音]' if $msg->{media_type} eq "voice"; | ||||
364 | 0 | 0 | $msg->{content} = '[视频]' if $msg->{media_type} eq "video"; | ||||
365 | 0 | 0 | $msg->{content} = '[小视频]' if $msg->{media_type} eq "microvideo"; | ||||
366 | 0 | 0 | $msg->{content} = '[表情]' if $msg->{media_type} eq "emoticon"; | ||||
367 | 0 | 0 | $msg->{content} = '[文件]' if $msg->{media_type} eq "file"; | ||||
368 | } | ||||||
369 | elsif(defined $msg->{content}){ | ||||||
370 | 0 | eval{$msg->{content} = Mojo::Util::html_unescape($msg->{content});}; | |||||
0 | |||||||
371 | 0 | 0 | $self->warn("html entities unescape fail: $@") if $@; | ||||
372 | } | ||||||
373 | 0 | 0 | if($msg->{format} eq "app"){ | ||||
0 | |||||||
0 | |||||||
0 | |||||||
374 | 0 | eval{ | |||||
375 | 0 | $msg->{content}=~s/ /\n/g; |
|||||
376 | 0 | require Mojo::DOM; | |||||
377 | 0 | my $dom = Mojo::DOM->new($msg->{content}); | |||||
378 | 0 | 0 | if( $dom->at('msg > appmsg > type')->content != 5){ | ||||
379 | 0 | $msg->{content} = "[应用分享]标题:$msg->{app_title}\n[应用分享]链接:$msg->{app_url}"; | |||||
380 | 0 | return; | |||||
381 | } | ||||||
382 | 0 | $msg->{app_id} = $dom->at('msg > appmsg')->attr->{appid}; | |||||
383 | 0 | $msg->{app_title} = $dom->at('msg > appmsg > title')->content; | |||||
384 | 0 | $msg->{app_name} = $dom->at('msg > appinfo > appname')->content; | |||||
385 | 0 | $msg->{app_url} = $dom->at('msg > appmsg > url')->content; | |||||
386 | 0 | $msg->{app_desc} = $dom->at('msg > appmsg > des')->content; | |||||
387 | 0 | for( ($msg->{app_title},$msg->{app_desc},$msg->{app_url},$msg->{app_name}) ){ | |||||
388 | 0 | s//$1/sg; | |||||
389 | } | ||||||
390 | 0 | $msg->{app_url} = Mojo::Util::html_unescape($msg->{app_url}); | |||||
391 | 0 | 0 | $msg->{content} = "[应用分享]标题:@{[$msg->{app_title} || '未知']}\n[应用分享]描述:@{[$msg->{app_desc} || '未知']}\n[应用分享]应用:@{[$msg->{app_name} || '未知']}\n[应用分享]链接:@{[$msg->{app_url} || '未知']}"; | ||||
0 | 0 | ||||||
0 | 0 | ||||||
0 | 0 | ||||||
0 | |||||||
392 | }; | ||||||
393 | 0 | 0 | if($@){ | ||||
394 | 0 | 0 | $self->warn("app message xml parse fail: $@") if $@; | ||||
395 | 0 | $msg->{content} = "[应用分享]标题:$msg->{app_title}\n[应用分享]链接:$msg->{app_url}"; | |||||
396 | } | ||||||
397 | } | ||||||
398 | elsif($msg->{format} eq "revoke"){ | ||||||
399 | # |
||||||
400 | 0 | eval{ | |||||
401 | 0 | require Mojo::DOM; | |||||
402 | 0 | my $dom = Mojo::DOM->new($msg->{content}); | |||||
403 | 0 | 0 | return if $dom->at('sysmsg')->attr->{type} ne 'revokemsg'; | ||||
404 | #$msg->{revoke_session} = $dom->at('sysmsg > revokemsg > session')->content; | ||||||
405 | 0 | $msg->{revoke_id} = $dom->at('sysmsg > revokemsg > msgid')->content; | |||||
406 | 0 | $msg->{content} = $dom->at('sysmsg > revokemsg > replacemsg')->content; | |||||
407 | 0 | $msg->{content}=~s//$1/g; | |||||
408 | |||||||
409 | #纠正自己撤回消息时,消息类型错乱的问题 | ||||||
410 | 0 | 0 | 0 | if($msg->{content} eq '你撤回了一条消息' and $msg->{class} eq 'recv'){ | |||
411 | 0 | $msg->{class} = 'send'; | |||||
412 | 0 | $msg->{source} = 'outer'; | |||||
413 | 0 | 0 | if($msg->{type} eq "group_message"){ | ||||
0 | |||||||
414 | 0 | $msg->{sender_id} = $msg->{receiver_id}; | |||||
415 | 0 | delete $msg->{receiver_id}; | |||||
416 | } | ||||||
417 | elsif($msg->{type} eq "friend_message"){ | ||||||
418 | 0 | ($msg->{sender_id},$msg->{receiver_id}) = ($msg->{receiver_id},$msg->{sender_id}); | |||||
419 | } | ||||||
420 | } | ||||||
421 | 0 | $msg->{content} = "[撤回消息](" . $msg->{content} . ")"; | |||||
422 | }; | ||||||
423 | 0 | 0 | if($@){ | ||||
424 | 0 | 0 | $self->warn("app message xml parse fail: $@") if $@; | ||||
425 | 0 | $msg->{content} = "[撤回消息]"; | |||||
426 | } | ||||||
427 | } | ||||||
428 | elsif($msg->{format} eq "card"){ | ||||||
429 | # |
||||||
430 | 0 | $msg->{content}=~s/ /\n/g; |
|||||
431 | 0 | eval{ | |||||
432 | 0 | require Mojo::DOM; | |||||
433 | 0 | my $dom = Mojo::DOM->new($msg->{content}); | |||||
434 | 0 | $msg->{card_avatar} = $dom->at('msg')->attr->{bigheadimgurl}; | |||||
435 | 0 | $msg->{card_name} = $dom->at('msg')->attr->{nickname}; | |||||
436 | 0 | $msg->{card_account} = $dom->at('msg')->attr->{alias}; | |||||
437 | 0 | $msg->{card_province} = $dom->at('msg')->attr->{province}; | |||||
438 | 0 | $msg->{card_city} = $dom->at('msg')->attr->{city}; | |||||
439 | 0 | $msg->{card_sex} = $self->code2sex($dom->at('msg')->attr->{sex}); | |||||
440 | }; | ||||||
441 | 0 | 0 | $self->warn("app message xml parse fail: $@") if $@; | ||||
442 | 0 | 0 | $msg->{content} = "[名片]昵称:@{[$msg->{card_name} || '未知']}\n[名片]性别:@{[$msg->{card_sex} || '未知']}\n[名片]位置:@{[$msg->{card_province} || '未知']} @{[$msg->{card_city} || '未知']}\n[名片]头像:@{[$msg->{card_avatar} || '未知']}"; | ||||
0 | 0 | ||||||
0 | 0 | ||||||
0 | 0 | ||||||
0 | 0 | ||||||
0 | |||||||
443 | } | ||||||
444 | elsif($msg->{format} eq 'payment'){ | ||||||
445 | # |
||||||
446 | 0 | $msg->{content}=~s/ /\n/g; |
|||||
447 | 0 | eval{ | |||||
448 | 0 | require Mojo::DOM; | |||||
449 | 0 | my $dom = Mojo::DOM->new($msg->{content}); | |||||
450 | 0 | $msg->{content} = $dom->at('msg > appmsg > des')->content; | |||||
451 | 0 | $msg->{content}=~s//$1/g; | |||||
452 | }; | ||||||
453 | 0 | 0 | $self->warn("payment message xml parse fail: $@") if $@; | ||||
454 | 0 | $msg->{content} = "[转账](" . $msg->{content} . ")"; | |||||
455 | } | ||||||
456 | 0 | $self->message_queue->put(Mojo::Weixin::Message->new($msg)); | |||||
457 | } | ||||||
458 | } | ||||||
459 | |||||||
460 | 0 | 0 | if($json->{ContinueFlag}!=0){ | ||||
461 | 0 | $self->_sync(); | |||||
462 | 0 | return; | |||||
463 | } | ||||||
464 | } | ||||||
465 | |||||||
466 | sub send_message{ | ||||||
467 | 0 | 0 | 1 | my $self = shift; | |||
468 | 0 | my $object = shift; | |||||
469 | 0 | my $content = shift; | |||||
470 | 0 | my $callback = shift; | |||||
471 | 0 | 0 | 0 | if( ref($object) ne "Mojo::Weixin::Friend" and ref($object) ne "Mojo::Weixin::Group") { | |||
472 | 0 | $self->error("无效的发送消息对象"); | |||||
473 | 0 | return; | |||||
474 | } | ||||||
475 | 0 | 0 | my $id = sub{my $r = sprintf "%.3f", rand();$r=~s/\.//g;my $t = $self->now() . $r;return $t}->(); | ||||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
476 | 0 | 0 | my $msg = Mojo::Weixin::Message->new( | ||||
0 | |||||||
0 | |||||||
477 | id => $id, | ||||||
478 | uid=> $id, | ||||||
479 | content => $content, | ||||||
480 | sender_id => $self->user->id, | ||||||
481 | receiver_id => (ref $object eq "Mojo::Weixin::Friend"?$object->id : undef), | ||||||
482 | group_id =>(ref $object eq "Mojo::Weixin::Group"?$object->id : undef), | ||||||
483 | type => (ref $object eq "Mojo::Weixin::Group"?"group_message":"friend_message"), | ||||||
484 | class => "send", | ||||||
485 | format => "text", | ||||||
486 | from => "code", | ||||||
487 | ); | ||||||
488 | |||||||
489 | 0 | 0 | $callback->($self,$msg) if ref $callback eq "CODE"; | ||||
490 | 0 | $self->emit(before_send_message=>$msg); | |||||
491 | 0 | $self->message_queue->put($msg); | |||||
492 | |||||||
493 | } | ||||||
494 | my %KEY_MAP_MEDIA_TYPE = reverse %KEY_MAP_MEDIA_CODE; | ||||||
495 | sub send_media { | ||||||
496 | 0 | 0 | 1 | my $self = shift; | |||
497 | 0 | my $object = shift; | |||||
498 | 0 | my $media = shift; | |||||
499 | 0 | my $callback = shift; | |||||
500 | 0 | 0 | 0 | if( ref($object) ne "Mojo::Weixin::Friend" and ref($object) ne "Mojo::Weixin::Group") { | |||
501 | 0 | $self->error("无效的发送消息对象"); | |||||
502 | 0 | return; | |||||
503 | } | ||||||
504 | 0 | my $media_info = {}; | |||||
505 | 0 | 0 | if(ref $media eq ""){ | ||||
0 | |||||||
506 | 0 | $media_info->{media_path} = $media; | |||||
507 | } | ||||||
508 | elsif(ref $media eq "HASH"){ | ||||||
509 | 0 | $media_info = $media; | |||||
510 | 0 | 0 | if(defined $media_info->{media_id}){#定义了media_id意味着不会上传文件,忽略media_path | ||||
511 | 0 | my ($id,$code) = split(/:/,$media_info->{media_id},2); | |||||
512 | 0 | 0 | $media_info->{media_id} = $id if $id; | ||||
513 | 0 | 0 | $media_info->{media_code} = $code if $code; | ||||
514 | 0 | 0 | 0 | if(!defined $media_info->{media_code} and defined $media_info->{media_type}){ | |||
0 | |||||||
515 | 0 | 0 | $media_info->{media_code} = $KEY_MAP_MEDIA_CODE{$media_info->{media_type}} // 6; | ||||
516 | } | ||||||
517 | elsif(!defined $media_info->{media_code}){ | ||||||
518 | 0 | $media_info->{media_code} = 6; | |||||
519 | } | ||||||
520 | } | ||||||
521 | 0 | 0 | 0 | if(defined $media_info->{media_code} and !defined $media_info->{media_type}){ | |||
522 | 0 | 0 | $media_info->{media_type} = $KEY_MAP_MEDIA_TYPE{$media_info->{media_code}} || 'file'; | ||||
523 | } | ||||||
524 | |||||||
525 | } | ||||||
526 | |||||||
527 | my $media_type = $media_info->{media_type} eq "image" ? "[图片]" | ||||||
528 | : $media_info->{media_type} eq "emoticon" ? "[表情]" | ||||||
529 | : $media_info->{media_type} eq "video" ? "[视频]" | ||||||
530 | : $media_info->{media_type} eq "microvideo"? "[小视频]" | ||||||
531 | : $media_info->{media_type} eq "voicce" ? "[语音]" | ||||||
532 | 0 | 0 | : $media_info->{media_type} eq "file" ? "[文件]" | ||||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
533 | : "[文件]" | ||||||
534 | ; | ||||||
535 | |||||||
536 | 0 | 0 | my $id = sub{my $r = sprintf "%.3f", rand();$r=~s/\.//g;my $t = $self->now() . $r;return $t}->(); | ||||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
537 | my $msg = Mojo::Weixin::Message->new( | ||||||
538 | id => $id, | ||||||
539 | uid=> $id, | ||||||
540 | media_id => $media_info->{media_id}, | ||||||
541 | media_name => $media_info->{media_name}, | ||||||
542 | media_type => $media_info->{media_type}, | ||||||
543 | media_code => $media_info->{media_code}, | ||||||
544 | media_path => $media_info->{media_path}, | ||||||
545 | media_data => $media_info->{media_data}, | ||||||
546 | media_mime => $media_info->{media_mime}, | ||||||
547 | media_size => $media_info->{media_size}, | ||||||
548 | media_mtime => $media_info->{media_mtime}, | ||||||
549 | media_ext => $media_info->{media_ext}, | ||||||
550 | 0 | 0 | 0 | content => "$media_type(" . ($media_info->{media_path} || $media_info->{media_id}) . ")", | |||
0 | |||||||
0 | |||||||
551 | sender_id => $self->user->id, | ||||||
552 | receiver_id => (ref $object eq "Mojo::Weixin::Friend"?$object->id : undef), | ||||||
553 | group_id =>(ref $object eq "Mojo::Weixin::Group"?$object->id : undef), | ||||||
554 | type => (ref $object eq "Mojo::Weixin::Group"?"group_message":"friend_message"), | ||||||
555 | class => "send", | ||||||
556 | format => "media", | ||||||
557 | ); | ||||||
558 | |||||||
559 | 0 | 0 | $callback->($self,$msg) if ref $callback eq "CODE"; | ||||
560 | 0 | $self->message_queue->put($msg); | |||||
561 | } | ||||||
562 | |||||||
563 | sub upload_media { | ||||||
564 | 0 | 0 | 0 | my $self = shift; | |||
565 | 0 | my $opt = shift; | |||||
566 | 0 | my $callback = pop; | |||||
567 | 0 | my $msg = Mojo::Weixin::Message->new(%$opt); | |||||
568 | $self->_upload_media($msg,sub{ | ||||||
569 | 0 | 0 | my($msg,$json) = @_; | ||||
570 | 0 | 0 | $callback->({ | ||||
571 | media_id => $msg->media_id, | ||||||
572 | media_code => $msg->media_code, | ||||||
573 | media_type => $msg->media_type, | ||||||
574 | media_path => $msg->media_path, | ||||||
575 | media_name => $msg->media_name, | ||||||
576 | media_size => $msg->media_size, | ||||||
577 | media_mime => $msg->media_mime, | ||||||
578 | media_mtime => $msg->media_mtime, | ||||||
579 | media_ext => $msg->media_ext, | ||||||
580 | }) if ref $callback eq "CODE"; | ||||||
581 | 0 | }); | |||||
582 | } | ||||||
583 | sub reply_message{ | ||||||
584 | 0 | 0 | 1 | my $self = shift; | |||
585 | 0 | my $msg = shift; | |||||
586 | 0 | my $content = shift; | |||||
587 | 0 | my $callback = shift; | |||||
588 | 0 | 0 | if($msg->class eq "recv"){ | ||||
0 | |||||||
589 | 0 | 0 | if($msg->type eq "group_message"){ | ||||
0 | |||||||
590 | 0 | $self->send_message($msg->group,$content,$callback); | |||||
591 | } | ||||||
592 | elsif($msg->type eq "friend_message"){ | ||||||
593 | 0 | $self->send_message($msg->sender,$content,$callback); | |||||
594 | } | ||||||
595 | } | ||||||
596 | elsif($msg->class eq "send"){ | ||||||
597 | 0 | 0 | if($msg->type eq "group_message"){ | ||||
0 | |||||||
598 | 0 | $self->send_message($msg->group,$content,$callback); | |||||
599 | } | ||||||
600 | elsif($msg->type eq "friend_message"){ | ||||||
601 | 0 | $self->send_message($msg->receiver,$content,$callback); | |||||
602 | } | ||||||
603 | |||||||
604 | } | ||||||
605 | } | ||||||
606 | |||||||
607 | sub reply_media_message { | ||||||
608 | 0 | 0 | 1 | my $self = shift; | |||
609 | 0 | my $msg = shift; | |||||
610 | 0 | my $media = shift; | |||||
611 | 0 | my $callback = shift; | |||||
612 | 0 | 0 | if($msg->class eq "recv"){ | ||||
0 | |||||||
613 | 0 | 0 | if($msg->type eq "group_message"){ | ||||
0 | |||||||
614 | 0 | $self->send_media($msg->group,$media,$callback); | |||||
615 | } | ||||||
616 | elsif($msg->type eq "friend_message"){ | ||||||
617 | 0 | $self->send_media($msg->sender,$media,$callback); | |||||
618 | } | ||||||
619 | } | ||||||
620 | elsif($msg->class eq "send"){ | ||||||
621 | 0 | 0 | if($msg->type eq "group_message"){ | ||||
0 | |||||||
622 | 0 | $self->send_media($msg->group,$media,$callback); | |||||
623 | } | ||||||
624 | elsif($msg->type eq "friend_message"){ | ||||||
625 | 0 | $self->send_media($msg->receiver,$callback); | |||||
626 | } | ||||||
627 | |||||||
628 | } | ||||||
629 | } | ||||||
630 | sub revoke_message { | ||||||
631 | 0 | 0 | 0 | my $self = shift; | |||
632 | 0 | my ($msg_id, $receiver_id); | |||||
633 | 0 | 0 | if(not $_[0]){ | ||||
0 | |||||||
634 | 0 | $self->error("撤回消息失败: 无效的msg对象或者msg_id"); | |||||
635 | 0 | return; | |||||
636 | } | ||||||
637 | elsif(ref $_[0] eq "Mojo::Weixin::Message"){ | ||||||
638 | 0 | 0 | if( not $_[0]->is_success){ | ||||
639 | 0 | $self->error("撤回消息失败: 无法撤回未成功发送的消息"); | |||||
640 | 0 | return; | |||||
641 | } | ||||||
642 | 0 | $_[0]->dump; | |||||
643 | 0 | $msg_id = $_[0]->id; | |||||
644 | 0 | 0 | $receiver_id = $_[0]->type eq 'group_message'?$_[0]->group_id:$_[0]->receiver_id; | ||||
645 | 0 | 0 | 0 | if(not defined $msg_id or not defined $receiver_id){ | |||
646 | 0 | $self->error("撤回消息失败: msg对象中包含无效的msg_id"); | |||||
647 | 0 | return; | |||||
648 | } | ||||||
649 | } | ||||||
650 | else{ | ||||||
651 | 0 | ($msg_id, $receiver_id) = $_[0] =~ /^([^:]+):(.+)$/; | |||||
652 | 0 | 0 | 0 | if(not defined $msg_id or not defined $receiver_id){ | |||
653 | 0 | $self->error("撤回消息失败: 无效的msg_id"); | |||||
654 | 0 | return; | |||||
655 | } | ||||||
656 | } | ||||||
657 | 0 | my $ret = $self->_revoke_message($msg_id,$receiver_id); | |||||
658 | 0 | 0 | if($ret){ | ||||
659 | 0 | $self->debug("消息[$msg_id]撤回成功"); | |||||
660 | } | ||||||
661 | else{ | ||||||
662 | 0 | $self->debug("消息[$msg_id]撤回失败"); | |||||
663 | } | ||||||
664 | 0 | return $ret; | |||||
665 | } | ||||||
666 | |||||||
667 | |||||||
668 | 1; |