| 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 | 6 | use strict; | |||
| 1 | 2 | ||||||
| 1 | 31 | ||||||
| 3 | 1 | 1 | 4 | 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 | 115 | ||||||
| 4 | 1 | 1 | 5 | use List::Util qw(first); | |||
| 1 | 2 | ||||||
| 1 | 67 | ||||||
| 5 | 1 | 1 | 378 | use Mojo::Weixin::Message; | |||
| 1 | 2 | ||||||
| 1 | 13 | ||||||
| 6 | 1 | 1 | 34 | use Mojo::Weixin::Const; | |||
| 1 | 2 | ||||||
| 1 | 83 | ||||||
| 7 | 1 | 1 | 6 | use Mojo::Weixin::Message; | |||
| 1 | 2 | ||||||
| 1 | 4 | ||||||
| 8 | 1 | 1 | 618 | use Mojo::Weixin::Message::Queue; | |||
| 1 | 2 | ||||||
| 1 | 29 | ||||||
| 9 | 1 | 1 | 407 | use Mojo::Weixin::Message::Remote::_upload_media; | |||
| 1 | 3 | ||||||
| 1 | 26 | ||||||
| 10 | 1 | 1 | 362 | use Mojo::Weixin::Message::Remote::_get_media; | |||
| 1 | 3 | ||||||
| 1 | 27 | ||||||
| 11 | 1 | 1 | 359 | use Mojo::Weixin::Message::Remote::_send_media_message; | |||
| 1 | 2 | ||||||
| 1 | 24 | ||||||
| 12 | 1 | 1 | 593 | use Mojo::Weixin::Message::Remote::_send_text_message; | |||
| 1 | 2 | ||||||
| 1 | 27 | ||||||
| 13 | 1 | 1 | 337 | use Mojo::Weixin::Message::Remote::_revoke_message; | |||
| 1 | 36 | ||||||
| 1 | 4111 | ||||||
| 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; |