微信机器人原理: $self->_login() $self->_init(); $self->_get_chatroom(); $self->_update_chatroom_member(); $self->update_friend(); 安装路径: /usr/local/share/perl5/Weixin my $client = Weixin::Client->new(debug=>0); $client->load("ShowMsg"); $client->login(); $client->on_receive_msg = sub{ my $msg = shift ; #打印收到的消息 $client->call("ShowMsg",$msg); #对收到的消息,以相同的内容回复 #$client->reply_msg($msg,$msg->{Content}); }; $client->on_send_msg = sub { my ($msg,$is_success,$status) = @_; #打印发送的消息 $client->call("ShowMsg",$msg); }; $client->run(); 1.问下 微信的$client->on_receive_msg = sub{ my $msg = shift ; 这个$msg 最初的来源在哪里的 最初的接收的消息 灰灰(308165330) 10:41:33 Weixin/Message.pm line 73 Client/Private/_sync.pm 接收消息 异步方式 $self->asyn_http_post() $self->http_post() 异步http请求,阻塞http请求 我封装了这两个 sub asyn_http_get { my $self = shift; my $callback = pop; my $ua = $self->{asyn_ua}; $ua->get(@_,sub{ my $response = shift; $self->{cookie_jar}->save; print $response->content(),"\n" if $self->{debug}; $callback->($response); }); } sub http_post{ my $self = shift; my $ua = $self->{ua}; my $res = $ua->post(@_); $self->{cookie_jar}->save; return (defined $res and $res->is_success)?$res->content:undef; } Client/Private/_send_text_msg.pm 发送消息 发异步请求 就是使用 Weixin::UserAgent 这个异步请求的类 这个异步请求的类 又是封装了 AnyEvent::HTTTP 这个模块 这个模块又是基于AnyEvent的异步来实现http起步请求的 my $client = Weixin::Client->new(debug=>0); 调用Weixin::Client 类的new方法如下,传入参数为debug=>0 sub new{ my $class = shift; my %p = @_; my $agent = 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062'; my $tmpdir = File::Spec->tmpdir(); my $cookie_filename = $p{login_file} || "$tmpdir/weixin_client_login.dat"; my $self = { cookie_jar => HTTP::Cookies->new(hide_cookie2=>1,file=>$cookie_filename,autosave=>1), debug => $p{debug}, _token => {}, _watchers => {}, _intervals => {}, _synccheck_error_count => 0, _synccheck_running => 0, _sync_running => 0, _sync_interval => 1, _synccheck_interval => 1, _send_msg_interval => 4, _last_sync_time => undef, _last_synccheck_time => undef, _send_message_queue => Weixin::Message::Queue->new, _receive_message_queue => Weixin::Message::Queue->new, _data => { user => {}, friend => [], chatroom => [], }, on_run => undef, on_receive_msg => undef, on_send_msg => undef, is_stop => 0, plugin_num => 0, plugins => {}, ua_retry_times => 5, tmpdir => $tmpdir, client_version => $VERSION, }; $self->{ua} = LWP::UserAgent->new( cookie_jar => $self->{cookie_jar}, agent => $agent, timeout => 300, ssl_opts => {verify_hostname => 0}, ); $self->{asyn_ua} = Weixin::UserAgent->new( cookie_jar => $self->{cookie_jar}, agent => $agent, request_timeout => 300, inactivity_timeout => 300, ); if($self->{debug}){ $self->{ua}->add_handler(request_send => sub { my($request, $ua, $h) = @_; print $request->as_string; return; }); $self->{ua}->add_handler( response_header => sub { my($response, $ua, $h) = @_; print $response->as_string; return; }); $self->{ua}->add_handler( response_done => sub { my($response, $ua, $h) = @_; print substr($response->content,0,1000),"\n" if $response->header("content-type")=~/^text/; return; }); } bless $self,$class; $self->prepare(); return $self; } 登录、获取好友信息等这些http请求是阻塞的,必须等待执行返回 接收消息、发送消息这些http请求是异步的 不需要阻塞在那里 这里传入的: $class is Weixin::Client %p is debug0 返回的对象: $self is ------ $VAR1 = bless( { 'ua' => bless( { 'max_redirect' => 7, 'protocols_forbidden' => undef, 'show_progress' => undef, 'handlers' => { 'request_prepare' => bless( [ { 'owner' => 'LWP::UserAgent::cookie_jar', 'callback' => sub { "DUMMY" }, 'line' => '/usr/share/perl5/LWP/UserAgent.pm:630' } ], 'HTTP::Config' ), 'response_header' => bless( [ { 'owner' => 'LWP::UserAgent::parse_head', 'callback' => sub { "DUMMY" }, 'm_media_type' => 'html', 'line' => '/usr/share/perl5/LWP/UserAgent.pm:609' } ], 'HTTP::Config' ), 'response_done' => bless( [ { 'owner' => 'LWP::UserAgent::cookie_jar', 'callback' => sub { "DUMMY" }, 'line' => '/usr/share/perl5/LWP/UserAgent.pm:633' } ], 'HTTP::Config' ) }, 'no_proxy' => [], 'protocols_allowed' => undef, 'use_eval' => 1, 'requests_redirectable' => [ 'GET', 'HEAD' ], 'timeout' => 300, 'cookie_jar' => bless( { 'autosave' => 1, 'COOKIES' => { 'wx.qq.com' => { '/' => { 'wxuin' => [ '0', '1284510822', undef, 1, '', 1452669645, '' ], 'webwxuvid' => [ '0', '9ee169770fb187e18f67d0876923a8c8e8fdf604d9850d7de806072c57af7ed9e9cd9c8adf120369fc1514659d14ec90', undef, 1, '', 1767754495, '' ], 'wxloadtime' => [ '0', '1452394495_expired', undef, 1, '', 1452437951, '' ], 'mm_lang' => [ '0', 'zh_CN', undef, 1, '', 1452799407, '' ], 'wxpluginkey' => [ '0', '1452396606', undef, 1, '', 1452453645, '' ] } } }, 'file' => '/tmp/weixin_client_login.dat', 'hide_cookie2' => 1 }, 'HTTP::Cookies' ), 'def_headers' => bless( { 'user-agent' => 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062' }, 'HTTP::Headers' ), 'proxy' => {}, 'max_size' => undef }, 'LWP::UserAgent' ), 'is_stop' => 0, '_send_message_queue' => bless( { 'callback_for_get_bak' => sub { "DUMMY" }, 'callback_for_get' => $VAR1->{'_send_message_queue'}{'callback_for_get_bak'}, 'queue' => [] }, 'Weixin::Message::Queue' ), '_send_msg_interval' => 4, 'tmpdir' => '/tmp', 'plugins' => {}, 'on_receive_msg' => undef, '_data' => { 'friend' => [], 'user' => {}, 'chatroom' => [] }, '_last_sync_time' => undef, '_sync_running' => 0, 'asyn_ua' => bless( { 'inactivity_timeout' => 300, 'max_redirects' => 5, 'agent' => 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062', 'cookie_jar' => $VAR1->{'ua'}{'cookie_jar'}, 'request_timeout' => 300 }, 'Weixin::UserAgent' ), '_token' => {}, '_last_synccheck_time' => undef, '_synccheck_interval' => 1, 'plugin_num' => 0, 'on_send_msg' => undef, 'debug' => 0, '_watchers' => {}, '_synccheck_error_count' => 0, '_intervals' => {}, '_receive_message_queue' => bless( { 'callback_for_get_bak' => sub { "DUMMY" }, 'callback_for_get' => $VAR1->{'_receive_message_queue'}{'callback_for_get_bak'}, 'queue' => [] }, 'Weixin::Message::Queue' ), '_synccheck_running' => 0, 'ua_retry_times' => 5, 'on_run' => undef, '_sync_interval' => 1, 'cookie_jar' => $VAR1->{'ua'}{'cookie_jar'}, 'client_version' => '2.0' }, 'Weixin::Client' ); load 方法: sub load { my $self = shift; my @module_name = @_; for my $module_name (@module_name){ my $module_function = undef; if(substr($module_name,0,1) eq '+'){ substr($module_name,0,1) = ""; $module = $module_name; } 第一个参数为对象: $msg is ------ $VAR1 = bless( { 'ToUin' => 1284510822, 'MsgClass' => 'recv', 'CreateTime' => 1452758596, 'ToNickName' => '我', 'FromId' => '@2c33cb9d541cc8e0cd7c5ec018b56cda185aa0174f181f9d4e0136b286035a02', 'FromRemarkName' => undef, 'ToRemarkName' => undef, 'Type' => 'friend_message', 'MsgType' => 'text', 'FromNickName' => undef, 'FromUin' => undef, 'Content' => '你也147258369', 'TTL' => 5, 'ToId' => '@355da879478ba362e808fa906075a50823712e71e19f4a06ceba0ff4c56d238f', 'MsgId' => '800096966135914975' }, 'Weixin::Message::Recv' ); 接收消息的类: Weixin::Message::Recv 这个类 来自于: Weixin::Message::Recv jrhmpt01:/usr/local/share/perl5/Weixin# vim Message.pm sub _mk_ro_accessors { my $self = shift; ### 第一个参数$self对象 my $msg =shift; ### my $msg_pkg = shift; no strict 'refs'; for my $field (keys %$msg){ *{__PACKAGE__ . "::${msg_pkg}::$field"} = sub{ my $obj = shift; my $pkg = ref $obj; ##获得类名字 die "the value of \"$field\" in $pkg is read-only\n" if @_!=0; return $obj->{$field}; }; } return bless $msg,__PACKAGE__."::$msg_pkg"; } 这里的当前包是Weixin::Message 1.登录接口: https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=4fbNLiMKhA==&tip=0&r=-1570013618&_=1453268946261 微信登录接口: my $api = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login'; _login.pm 方法: package Weixin::Client; use Weixin::Client::Private::_get_qrcode_uuid; use Weixin::Client::Private::_get_qrcode_image; use Weixin::Client::Private::_is_need_login; 1.1获取 uuid _get_qrcode_uuid 方法: https://login.weixin.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_=1453269277552 响应: window.QRLogin.code = 200; window.QRLogin.uuid = "4dyqQzTV_g=="; 1.2 获取二维码 https://login.weixin.qq.com/qrcode/Ab3x-F9bqw== 返回2维码 1.3 https://wx.qq.com/?&lang=zh_CN 返回帮助信息 2 _init https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-1571406762&lang=zh_CN&pass_ticket=gII3G4F43MHyoE3NXEpQGjnL%252FO4Kh9oT7L9qOJyNBqYicc6NB%252FJzkt%252F0GHZFtdRm 返回群好友信息 3._get_chatroom(); https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact?type=ex&r=1453270436253&lang=zh_CN&pass_ticket=gII3G4F43MHyoE3NXEpQGjnL%252FO4Kh9oT7L9qOJyNBqYicc6NB%252FJzkt%252F0GHZFtdRm 返回聊天群及成员 4._update_chatroom_member https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact 5._update_friend https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket=gII3G4F43MHyoE3NXEpQGjnL%252FO4Kh9oT7L9qOJyNBqYicc6NB%252FJzkt%252F0GHZFtdRm&r=1453270435381&seq=0&skey=@crypt_597aeefd_52e341350b71c7dae5c660c45c50eb31 https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact 获取好友信息 3. 收消息: Client/Private/_sync.pm 接收消息 异步方式 sub _sync { my $self = shift; return if $self->{_synccheck_running} 如果 $self->{_synccheck_running} 就直接return 什么也不干 my $callback = sub{ $self->{_sync_running} = 0; my $response = shift; print $response->content(),"\n" if $self->{debug}; my $d = $self->json_decode($response->content()); #return if $d->{BaseResponse}{Ret}!=0; $self->_parse_sync_data($d); }; my $response = shift; https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid=g+05x/2cwXtfEmV5&skey=@crypt_597aeefd_52e341350b71c7dae5c660c45c50eb31&lang=zh_CN 接收消息: "AddMsgCount": 1, "AddMsgList": [{ "MsgId": "8442920128804454773", "FromUserName": "@78a7a53674f57a5a6070221eeb8fcf706a7aaf965ce2d205b1ec8d76597953c7", "ToUserName": "@c2afb19006a336777800351b8f1a5142c5dde8dfd2f68b86b6cb196a76dddac1", "MsgType": 1, "Content": "kjk7787czcb<br/>", 4.发消息 _send_text_msg.pm https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN post 发送消息: {"BaseRequest":{"Uin":1284510822,"Sid":"g+05x/2cwXtfEmV5","Skey":"@crypt_597aeefd_52e341350b71c7dae5c660c45c50eb31" ,"DeviceID":"e200795958639167"},"Msg":{"Type":1,"Content":"xxxxxxx","FromUserName":"@c2afb19006a336777800351b8f1a5142c5dde8dfd2f68b86b6cb196a76dddac1" ,"ToUserName":"@@b866ff46bfe180745fea6a005cfeb9cad0ac90ae8e80acaca7f1553b6e5c3739","LocalID":"14532728152810691" ,"ClientMsgId":"14532728152810691"}}