File Coverage

blib/lib/Webqq/Client.pm
Criterion Covered Total %
statement 135 801 16.8
branch 0 302 0.0
condition 0 64 0.0
subroutine 45 122 36.8
pod 40 45 88.8
total 220 1334 16.4


line stmt bran cond sub pod time code
1             package Webqq::Client;
2 1     1   51928 use strict;
  1         2  
  1         35  
3 1     1   621 use JSON;
  1         9322  
  1         4  
4 1     1   738 use Encode;
  1         7859  
  1         77  
5 1     1   471 use Time::HiRes qw(gettimeofday);
  1         1165  
  1         4  
6 1     1   567 use LWP::Protocol::https;
  1         76322  
  1         37  
7 1     1   7 use Storable qw(dclone);
  1         2  
  1         76  
8 1     1   4 use List::Util qw(first);
  1         12  
  1         85  
9 1     1   5 use base qw(Webqq::Message Webqq::Client::Cron Webqq::Client::Plugin);
  1         1  
  1         417  
10 1     1   462 use Webqq::Client::Cache;
  1         1  
  1         28  
11 1     1   452 use Webqq::Message::Queue;
  1         2  
  1         43  
12              
13             #定义模块的版本号
14             our $VERSION = "8.3";
15              
16 1     1   3 use LWP::UserAgent;#同步HTTP请求客户端
  1         2  
  1         17  
17 1     1   303 use Webqq::UserAgent;#异步HTTP请求客户端
  1         3  
  1         34  
18              
19 1     1   5 use Webqq::Client::Util qw(console);
  1         1  
  1         52  
20              
21             #为避免在主文件中包含大量Method的代码,降低阅读性,故采用分文件加载的方式
22             #类似c语言中的.h文件和.c文件的关系
23 1     1   365 use Webqq::Client::Method::_prepare_for_login;
  1         1  
  1         21  
24 1     1   320 use Webqq::Client::Method::_check_verify_code;
  1         2  
  1         25  
25 1     1   323 use Webqq::Client::Method::_get_img_verify_code;
  1         2  
  1         24  
26 1     1   537 use Webqq::Client::Method::_login1;
  1         3  
  1         77  
27 1     1   353 use Webqq::Client::Method::_check_sig;
  1         2  
  1         21  
28 1     1   319 use Webqq::Client::Method::_login2;
  1         2  
  1         22  
29 1     1   312 use Webqq::Client::Method::_recv_message;
  1         2  
  1         22  
30 1     1   321 use Webqq::Client::Method::_get_group_info;
  1         2  
  1         36  
31 1     1   314 use Webqq::Client::Method::_get_group_sig;
  1         2  
  1         22  
32 1     1   338 use Webqq::Client::Method::_get_group_list_info;
  1         2  
  1         22  
33 1     1   322 use Webqq::Client::Method::_get_user_friends;
  1         1  
  1         23  
34 1     1   313 use Webqq::Client::Method::_get_user_info;
  1         2  
  1         21  
35 1     1   330 use Webqq::Client::Method::_get_friend_info;
  1         2  
  1         22  
36 1     1   333 use Webqq::Client::Method::_get_stranger_info;
  1         2  
  1         21  
37 1     1   388 use Webqq::Client::Method::_send_message;
  1         2  
  1         21  
38 1     1   364 use Webqq::Client::Method::_send_group_message;
  1         2  
  1         21  
39 1     1   365 use Webqq::Client::Method::_get_vfwebqq;
  1         2  
  1         22  
40 1     1   319 use Webqq::Client::Method::_send_sess_message;
  1         2  
  1         22  
41 1     1   343 use Webqq::Client::Method::logout;
  1         1  
  1         21  
42 1     1   314 use Webqq::Client::Method::get_qq_from_uin;
  1         2  
  1         22  
43 1     1   318 use Webqq::Client::Method::get_single_long_nick;
  1         2  
  1         21  
44 1     1   368 use Webqq::Client::Method::_report;
  1         2  
  1         24  
45 1     1   305 use Webqq::Client::Method::get_dwz;
  1         2  
  1         20  
46 1     1   315 use Webqq::Client::Method::_get_offpic;
  1         1  
  1         21  
47 1     1   378 use Webqq::Client::Method::_cookie_proxy;
  1         2  
  1         24  
48 1     1   302 use Webqq::Client::Method::_relink;
  1         1  
  1         22  
49 1     1   371 use Webqq::Client::Method::_get_discuss_list_info;
  1         2  
  1         22  
50 1     1   319 use Webqq::Client::Method::_get_discuss_info;
  1         1  
  1         22  
51 1     1   325 use Webqq::Client::Method::change_state;
  1         2  
  1         20  
52 1     1   317 use Webqq::Client::Method::_send_discuss_message;
  1         2  
  1         21  
53 1     1   317 use Webqq::Client::Method::_get_friends_state;
  1         3  
  1         36  
54 1     1   376 use Webqq::Client::Method::_get_recent_info;
  1         2  
  1         6027  
55              
56             our $LAST_DISPATCH_TIME = undef;
57             our $SEND_INTERVAL = 3;
58             our $CLIENT_COUNT = 0;
59              
60             sub new {
61 0     0 1   my $class = shift;
62 0           my %p = @_;
63 0           my $agent = 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062';
64              
65 0           my ($second,$microsecond)=gettimeofday;
66 0           my $send_msg_id = $second*1000+$microsecond;
67 0           $send_msg_id=($send_msg_id-$send_msg_id%1000)/1000;
68 0           $send_msg_id=($send_msg_id%10000)*10000;
69 0 0 0       my $self = {
      0        
70             cookie_jar => HTTP::Cookies->new(hide_cookie2=>1),
71             qq_param => {
72             qq => undef,
73             pwd => undef,
74             is_https => defined $p{security}?$p{security}:0,
75             is_need_img_verifycode => 0,
76             img_verifycode_source => 'TTY', #NONE|TTY|CALLBACK
77             send_msg_id => $send_msg_id,
78             clientid => 53999199,
79             psessionid => 'null',
80             vfwebqq => undef,
81             ptwebqq => undef,
82             state => $p{state} || 'online', #online|away|busy|silent|hidden|offline,
83             passwd_sig => '',
84             verifycode => undef,
85             verifysession => undef,
86             pt_verifysession => undef,
87             md5_salt => undef,
88             cap_cd => undef,
89             isRandSalt => 0,
90             ptvfsession => undef,
91             api_check_sig => undef,
92             g_pt_version => undef,
93             g_login_sig => undef,
94             g_style => 5,
95             g_mibao_css => 'm_webqq',
96             g_daid => 164,
97             g_appid => 1003903,
98             g_pt_version => 10092,
99             rc => 1,
100             },
101             qq_database => {
102             user => {},
103             friends => [],
104             group_list => [],
105             discuss_list=> [],
106             recent => [],
107             group => [],
108             discuss => [],
109             },
110             is_first_login => -1,
111             is_stop => 0,
112             cache_for_uin_to_qq => Webqq::Client::Cache->new,
113             cache_for_group_sig => Webqq::Client::Cache->new,
114             cache_for_stranger => Webqq::Client::Cache->new,
115             cache_for_friend => Webqq::Client::Cache->new,
116             cache_for_single_long_nick => Webqq::Client::Cache->new,
117             cache_for_group => Webqq::Client::Cache->new,
118             cache_for_group_member => Webqq::Client::Cache->new,
119             cache_for_discuss => Webqq::Client::Cache->new,
120             cache_for_discuss_member => Webqq::Client::Cache->new,
121             cache_for_metacpan => Webqq::Client::Cache->new,
122             on_receive_message => undef,
123             on_receive_offpic => undef,
124             on_send_message => undef,
125             on_login => undef,
126             on_new_friend => undef,
127             on_new_group => undef,
128             on_new_discuss => undef,
129             on_new_group_member => undef,
130             on_loss_group_member => undef,
131             on_new_discuss_member => undef,
132             on_loss_discuss_member => undef,
133             on_input_img_verifycode => undef,
134             on_friend_change_state => undef,
135             on_run => undef,
136             on_ready => undef,
137             receive_message_queue => Webqq::Message::Queue->new,
138             send_message_queue => Webqq::Message::Queue->new,
139             debug => $p{debug},
140             login_state => "init",
141             watchers => {},
142             type => $p{type} || 'smartqq',#webqq or smartqq
143             plugin_num => 0,
144             plugins => {},
145             ua_retry_times => 5,
146             je => undef,
147             poll_failure_count_max => 3,
148             poll_failure_count => 0,
149             client_version => $VERSION,
150            
151             };
152 0           $self->{ua} = LWP::UserAgent->new(
153             cookie_jar => $self->{cookie_jar},
154             agent => $agent,
155             timeout => 300,
156             ssl_opts => {verify_hostname => 0},
157             );
158 0           $self->{asyn_ua} = Webqq::UserAgent->new(
159             cookie_jar => $self->{cookie_jar},
160             agent => $agent,
161             request_timeout => 300,
162             inactivity_timeout => 300,
163             );
164 0           $self->{qq_param}{from_uin} =$self->{qq_param}{qq};
165 0 0         if($self->{debug}){
166             $self->{ua}->add_handler(request_send => sub {
167 0     0     my($request, $ua, $h) = @_;
168 0           print $request->as_string;
169 0           return;
170 0           });
171              
172             $self->{ua}->add_handler(
173 0     0     response_header => sub { my($response, $ua, $h) = @_;
174 0           print $response->as_string;
175 0           return;
176 0           });
177             }
178 0           $self->{default_qq_param} = dclone($self->{qq_param});
179 0           $self->{default_qq_database} = dclone($self->{qq_database});
180              
181 0           bless $self,$class;
182 0           $self->_prepare();
183 0           return $self;
184             }
185             sub on_send_message :lvalue {
186 0     0 1   my $self = shift;
187 0           $self->{on_send_message};
188             }
189              
190             sub on_receive_message :lvalue{
191 0     0 1   my $self = shift;
192 0           $self->{on_receive_message};
193             }
194              
195             sub on_receive_offpic :lvalue{
196 0     0 1   my $self = shift;
197 0           $self->{on_receive_offpic};
198             }
199              
200             sub on_login :lvalue {
201 0     0 1   my $self = shift;
202 0           $self->{on_login};
203             }
204             sub on_ready :lvalue {
205 0     0 1   my $self = shift;
206 0           $self->{on_ready};
207             }
208             sub on_run :lvalue {
209 0     0 1   my $self = shift;
210 0           $self->{on_run};
211             }
212             sub on_friend_change_state :lvalue {
213 0     0 1   my $self = shift;
214 0           $self->{on_friend_change_state};
215             }
216              
217             sub on_new_friend :lvalue {
218 0     0 1   my $self = shift;
219 0           $self->{on_new_friend};
220             }
221              
222             sub on_new_group :lvalue {
223 0     0 1   my $self = shift;
224 0           $self->{on_new_group};
225             }
226              
227             sub on_new_group_member :lvalue {
228 0     0 1   my $self = shift;
229 0           $self->{on_new_group_member};
230             }
231              
232             sub on_loss_group_member :lvalue {
233 0     0 1   my $self = shift;
234 0           $self->{on_loss_group_member};
235             }
236              
237             sub on_new_discuss :lvalue {
238 0     0 1   my $self = shift;
239 0           $self->{on_new_discuss};
240             }
241             sub on_new_discuss_member :lvalue {
242 0     0 1   my $self = shift;
243 0           $self->{on_new_discuss_member};
244             }
245             sub on_loss_discuss_member :lvalue {
246 0     0 1   my $self = shift;
247 0           $self->{on_loss_discuss_member};
248             }
249              
250             sub on_input_img_verifycode :lvalue {
251 0     0 1   my $self = shift;
252 0           $self->{on_input_img_verifycode};
253             }
254              
255             sub login{
256 0     0 1   my $self = shift;
257 0           my %p = @_;
258            
259 0 0         if($self->{is_first_login} == -1){
    0          
260 0           $self->{is_first_login} = 1;
261             }
262             elsif($self->{is_first_login} == 1){
263 0           $self->{is_first_login} = 0;
264             }
265              
266 0           @{$self->{default_qq_param}}{qw(qq pwd)} = @p{qw(qq pwd)};
  0            
267 0           @{$self->{qq_param}}{qw(qq pwd)} = @p{qw(qq pwd)};
  0            
268 0 0         $self->{qq_param}{security} = $p{security} if defined $p{security};
269 0     0     $self->{qq_param}{state} = $p{state}
270 0 0 0       if defined $p{state} and first {$_ eq $p{state}} qw(online away busy silent hidden offline);
271 0           console "QQ账号: $self->{default_qq_param}{qq}\n";
272             #my $is_big_endian = unpack( 'xc', pack( 's', 1 ) );
273 0           $self->{qq_param}{qq} = $self->{default_qq_param}{qq};
274 0           $self->{default_qq_param}{pwd} = lc $self->{default_qq_param}{pwd};
275 0           $self->{qq_param}{pwd} = $self->{default_qq_param}{pwd} ;
276              
277 0 0 0       if(
      0        
278             $self->_prepare_for_login()
279             && $self->_check_verify_code()
280             && $self->_get_img_verify_code()
281              
282             ){
283 0           while(){
284 0           my $ret = $self->_login1();
285 0 0         if($ret == -1){
    0          
286 0           $self->_get_img_verify_code();
287 0           next;
288             }
289             elsif($ret == 1){
290 0 0 0       $self->_report()
      0        
291             && $self->_check_sig()
292             && $self->_get_vfwebqq()
293             && $self->_login2();
294 0           last;
295             }
296             else{
297 0           last;
298             }
299             }
300             }
301              
302             #登录不成功,客户端退出运行
303 0 0         if($self->{login_state} ne 'success'){
304 0           console "登录失败,客户端退出(可能网络不稳定,请多尝试几次)\n";
305 0           $self->stop();
306             }
307             else{
308 0           console "登录成功\n";
309             }
310             #获取个人资料信息
311 0           $self->update_user_info();
312             #显示欢迎信息
313 0           $self->welcome();
314             #更新好友信息
315 0           $self->update_friends_info();
316             #更新群信息
317 0           $self->update_group_info();
318             #更新讨论组信息
319 0           $self->update_discuss_info();
320             #更新最近联系人信息
321 0           $self->update_recent_info();
322             #执行on_login回调
323 0 0         if(ref $self->{on_login} eq 'CODE'){
324 0           eval{
325 0           $self->{on_login}->();
326             };
327 0 0         console $@ . "\n" if $@;
328             }
329 0           return 1;
330             }
331             sub relogin{
332 0     0 1   my $self = shift;
333 0           console "正在重新登录...\n";
334              
335 0           $self->logout();
336 0           $self->{login_state} = 'relogin';
337              
338             #清空cookie
339 0           $self->{cookie_jar} = HTTP::Cookies->new(hide_cookie2=>1);
340 0           $self->{ua}->cookie_jar($self->{cookie_jar});
341 0           $self->{asyn_ua}->{cookie_jar} = $self->{cookie_jar};
342             #重新设置初始化参数
343 0           $self->{cache_for_uin_to_qq} = Webqq::Client::Cache->new;
344 0           $self->{cache_for_group_sig} = Webqq::Client::Cache->new;
345 0           $self->{cache_for_group} = Webqq::Client::Cache->new;
346 0           $self->{cache_for_group_member} = Webqq::Client::Cache->new;
347 0           $self->{cache_for_discuss} = Webqq::Client::Cache->new;
348 0           $self->{cache_for_discuss_member} = Webqq::Client::Cache->new;
349 0           $self->{cache_for_friend} = Webqq::Client::Cache->new;
350 0           $self->{cache_for_stranger} = Webqq::Client::Cache->new;
351 0           $self->{cache_for_single_long_nick} = Webqq::Client::Cache->new;
352              
353 0           $self->{qq_param} = dclone($self->{default_qq_param});
354 0           $self->{qq_database} = dclone($self->{default_qq_database});
355 0           $self->login(qq=>$self->{default_qq_param}{qq},pwd=>$self->{default_qq_param}{pwd});
356             }
357             sub _get_vfwebqq;
358             sub _prepare_for_login;
359             sub _check_verify_code;
360             sub _get_img_verify_code;
361             sub _check_sig;
362             sub _login1;
363             sub _login2;
364             sub _get_user_info;
365             sub _get_friend_info;
366             sub _get_group_info;
367             sub _get_group_list_info;
368             sub _get_user_friends;
369             sub _get_discuss_list_info;
370             sub _send_message;
371             sub _send_group_message;
372             sub _get_msg_tip;
373             sub change_state;
374             sub get_qq_from_uin;
375             sub get_single_long_nick;
376             sub _report;
377             sub _cookie_proxy;
378             sub _get_offpic;
379             sub _relink;
380             sub _get_discuss_list_info;
381             sub _get_discuss_info;
382             sub _get_friends_state;
383             sub _get_recent_info;
384              
385             #接受一个消息,将它放到发送消息队列中
386             sub send_message{
387 0     0 1   my $self = shift;
388 0 0 0       if(@_ == 1 and ref $_[0] eq 'Webqq::Message::Message::Send'){
389 0           my $msg = shift;
390 0           $self->{send_message_queue}->put($msg);
391             }
392             else{
393 0           my $msg = $self->_create_msg(@_,type=>'message');
394 0           $self->{send_message_queue}->put($msg);
395             }
396             };
397             #接受一个群临时消息,将它放到发送消息队列中
398             sub send_sess_message{
399 0     0 1   my $self = shift;
400 0 0 0       if(@_ == 1 and ref $_[0] eq 'Webqq::Message::SessMessage::Send'){
401 0           my $msg = shift;
402 0           $self->{send_message_queue}->put($msg);
403             }
404             else{
405 0           my $msg = $self->_create_msg(@_,type=>'sess_message');
406 0           $self->{send_message_queue}->put($msg);
407             }
408             }
409              
410             sub send_discuss_message {
411 0     0 1   my $self = shift;
412 0 0 0       if(@_ == 1 and ref $_[0] eq 'Webqq::Message::DiscussMessage::Send'){
413 0           my $msg = shift;
414 0           $self->{send_message_queue}->put($msg);
415             }
416             else{
417 0           my $msg = $self->_create_msg(@_,type=>'discuss_message');
418 0           $self->{send_message_queue}->put($msg);
419             }
420             };
421              
422             #接受一个群消息,将它放到发送消息队列中
423             sub send_group_message{
424 0     0 1   my $self = shift;
425 0 0 0       if(@_ == 1 and ref $_[0] eq 'Webqq::Message::GroupMessage::Send'){
426 0           my $msg = shift;
427 0           $self->{send_message_queue}->put($msg);
428             }
429             else{
430 0           my $msg = $self->_create_msg(@_,type=>'group_message');
431 0           $self->{send_message_queue}->put($msg);
432             }
433             };
434             sub welcome{
435 0     0 1   my $self = shift;
436 0           my $w = $self->{qq_database}{user};
437 0           console "欢迎回来, $w->{nick}($w->{province})\n";
438 0 0         console "个性签名: " . ($w->{single_long_nick}?$w->{single_long_nick}:"(无)") . "\n"
439             };
440             sub logout;
441             sub _prepare {
442 0     0     my $self = shift;
443 0           $self->_load_extra_accessor();
444             #设置从接收消息队列中接收到消息后对应的处理函数
445             $self->{receive_message_queue}->get(sub{
446 0     0     my $msg = shift;
447 0 0         return if $self->{is_stop};
448             #触发on_new_friend/on_new_group_member回调
449 0 0         if($msg->{type} eq 'message'){
    0          
    0          
    0          
450 0 0         if(ref $self->{on_receive_offpic} eq 'CODE'){
451 0           for(@{$msg->{raw_content}}){
  0            
452 0 0         if($_->{type} eq 'offpic'){
453 0           $self->_get_offpic($_->{file_path},$msg->{from_uin},$self->{on_receive_offpic});
454             }
455             }
456             }
457 0           $self->_detect_new_friend($msg->{from_uin});
458             }
459             elsif($msg->{type} eq 'group_message'){
460 0           $self->_detect_new_group($msg->{group_code});
461 0           $self->_detect_new_group_member($msg->{group_code},$msg->{send_uin});
462             }
463             elsif($msg->{type} eq 'discuss_message'){
464 0           $self->_detect_new_discuss($msg->{did});
465 0           $self->_detect_new_discuss_member($msg->{did},$msg->{send_uin});
466             }
467             elsif($msg->{type} eq 'buddies_status_change'){
468 0           my $who = $self->update_friend_state_info($msg->{uin},$msg->{state},$msg->{client_type});
469 0 0 0       if(defined $who and ref $self->{on_friend_change_state} eq 'CODE'){
470 0           eval{
471 0           $self->{on_friend_change_state}->($who);
472             };
473 0 0         console "$@\n" if $@;
474             }
475             }
476            
477             #接收队列中接收到消息后,调用相关的消息处理回调,如果未设置回调,消息将丢弃
478 0 0         if(ref $self->{on_receive_message} eq 'CODE'){
479 0           eval{
480 0           $self->{on_receive_message}->($msg);
481             };
482 0 0         console $@ . "\n" if $@;
483             }
484 0           });
485              
486             #设置从发送消息队列中提取到消息后对应的处理函数
487             $self->{send_message_queue}->get(sub{
488 0     0     my $msg = shift;
489 0 0         return if $self->{is_stop};
490             #消息的ttl值减少到0则丢弃消息
491 0 0         if($msg->{ttl} <= 0){
492 0           my $status = {is_success=>0,status=>"发送失败"};
493 0 0         if(ref $msg->{cb} eq 'CODE'){
494 0           $msg->{cb}->(
495             $msg,
496             $status->{is_success},
497             $status->{status},
498             );
499             }
500 0 0         if(ref $self->{on_send_message} eq 'CODE'){
501 0           $self->{on_send_message}->(
502             $msg,
503             $status->{is_success},
504             $status->{status},
505             );
506             }
507            
508 0           return;
509             }
510 0           $msg->{ttl}--;
511              
512 0           my $rand_watcher_id = rand();
513 0           my $delay = 0;
514 0           my $now = time;
515 0 0         if(defined $LAST_DISPATCH_TIME){
516 0 0         $delay = $now<$LAST_DISPATCH_TIME+$SEND_INTERVAL?
517             $LAST_DISPATCH_TIME+$SEND_INTERVAL-$now
518             : 0;
519             }
520             $self->{watchers}{$rand_watcher_id} = AE::timer $delay,0,sub{
521 0           delete $self->{watchers}{$rand_watcher_id};
522 0           $msg->{msg_time} = time;
523 0 0         $msg->{type} eq 'message' ? $self->_send_message($msg)
    0          
    0          
    0          
524             : $msg->{type} eq 'group_message' ? $self->_send_group_message($msg)
525             : $msg->{type} eq 'sess_message' ? $self->_send_sess_message($msg)
526             : $msg->{type} eq 'discuss_message' ? $self->_send_discuss_message($msg)
527             : undef
528             ;
529 0           };
530 0           $LAST_DISPATCH_TIME = $now+$delay;
531            
532 0           });
533              
534             };
535              
536             sub ready{
537 0     0 0   my $self = shift;
538              
539             $self->{watchers}{rand()} = AE::timer 600,600,sub{
540 0     0     $self->update_group_info();
541 0           };
542              
543             $self->{watchers}{rand()} = AE::timer 600*2,600,sub{
544 0     0     $self->update_discuss_info();
545 0           };
546              
547 0           console "开始接收消息\n";
548 0           $self->_recv_message();
549              
550 0 0         if(ref $self->{on_ready} eq 'CODE'){
551 0           eval{
552 0           $self->{on_ready}->();
553             };
554 0 0         console "$@\n" if $@;
555             }
556 0           $CLIENT_COUNT++;
557             }
558              
559             sub stop {
560 0     0 1   my $self = shift;
561 0           $self->{is_stop} = 1;
562 0 0         if($CLIENT_COUNT > 1){
563 0           $CLIENT_COUNT--;
564             $self->{watchers}{rand()} = AE::timer 600,0,sub{
565 0     0     undef %$self;
566 0           };
567             }
568             else{
569 0           exit;
570             }
571             }
572              
573             sub exit {
574 0     0 1   my $self = shift;
575 0           exit;
576             }
577              
578             sub EXIT {
579 0     0 0   exit;
580             }
581              
582             sub run{
583 0     0 1   my $self = shift;
584 0           $self->ready();
585 0 0         if(ref $self->{on_run} eq 'CODE'){
586 0           eval{
587 0           $self->{on_run}->();
588             };
589 0 0         console "$@\n" if $@;
590             }
591 0           console "客户端运行中...\n";
592 0           $self->{cv} = AE::cv;
593 0           $self->{cv}->recv
594             }
595              
596             sub RUN{
597 0     0 1   console "启动全局事件循环...\n";
598 0           AE::cv->recv;
599             }
600             sub search_cookie{
601 0     0 1   my($self,$cookie) = @_;
602 0           my $result = undef;
603             $self->{cookie_jar}->scan(sub{
604 0     0     my($version,$key,$val,$path,$domain,$port,$path_spec,$secure,$expires,$discard,$rest) =@_;
605 0 0         if($key eq $cookie){
606 0           $result = $val ;
607 0           return;
608             }
609 0           });
610 0           return $result;
611             }
612              
613             #根据uin进行查询,返回一个friend的hash引用
614             #这个hash引用的结构是:
615             #{
616             # flag #标志,作用未知
617             # face #表情
618             # uin #uin
619             # categories #所属分组
620             # nick #昵称
621             # markname #备注名称
622             # is_vip #是否是vip会员
623             # vip_level #vip等级
624             #}
625             sub search_friend {
626 0     0 1   my ($self,$uin) = @_;
627 0           my $cache_data = $self->{cache_for_friend}->retrieve($uin);
628 0 0         return $cache_data if defined $cache_data;
629            
630 0     0     my $f = first {$_->{uin} eq $uin} @{ $self->{qq_database}{friends} };
  0            
  0            
631 0 0         if(defined $f){
632 0           my $f_clone = dclone($f);
633 0           $self->{cache_for_friend}->store($uin,$f_clone);
634 0           return $f_clone;
635             }
636 0           return undef;
637             }
638              
639             #根据群的gcode和群成员的uin进行查询,返回群成员相关信息
640             #返回结果是一个群成员的hash引用
641             #{
642             # nick #昵称
643             # province #省份
644             # gender #性别
645             # uin #uin
646             # country #国家
647             # city #城市
648             #}
649             sub search_member_in_group{
650 0     0 1   my ($self,$gcode,$member_uin) = @_;
651 0           my $cache_data = $self->{cache_for_group_member}->retrieve("$gcode|$member_uin");
652 0 0         return $cache_data if defined $cache_data;
653             #在现有的群中查找
654 0           for my $g (@{$self->{qq_database}{group}}){
  0            
655             #如果群是存在的
656 0 0         if($g->{ginfo}{code} eq $gcode){
657             #在群中查找指定的成员
658             #如果群数据库中包含群成员数据
659 0 0 0       if(exists $g->{minfo} and ref $g->{minfo} eq 'ARRAY'){
660 0     0     my $m = first {$_->{uin} eq $member_uin} @{$g->{minfo} };
  0            
  0            
661 0 0         if(defined $m){
662 0           my $m_clone = dclone($m);
663 0           $self->{cache_for_group_member}->store("$gcode|$member_uin",$m_clone);
664 0           return $m_clone;
665             }
666 0           return undef;
667            
668             }
669             #群数据中只有ginfo,没有minfo
670             else{
671             #尝试重新更新一下群信息,希望可以拿到minfo
672 0           my $group_info = $self->_get_group_info($g->{ginfo}{code});
673 0 0 0       if(defined $group_info and ref $group_info->{minfo} eq 'ARRAY'){
674             #终于拿到minfo了 赶紧存起来 以备下次使用
675 0           $self->update_group_info($group_info);
676             #在minfo里找群成员
677 0     0     my $m = first {$_->{uin} eq $member_uin} @{$group_info->{minfo}};
  0            
  0            
678 0 0         if(defined $m){
679 0           my $m_clone = dclone($m);
680 0           $self->{cache_for_group_member}->store("$gcode|$member_uin",$m_clone);
681 0           return $m_clone;
682             }
683             #靠 还是没找到
684 0           return undef;
685             }
686             #很可惜,还是拿不到minfo
687             else{
688 0           return undef;
689             }
690             }
691             }
692             }
693             #遍历所有的群也找不到,返回undef
694 0           return undef;
695             }
696              
697             sub search_member_in_discuss {
698 0     0 1   my ($self,$did,$member_uin) = @_;
699 0           my $cache_data = $self->{cache_for_discuss_member}->retrieve("$did|$member_uin");
700 0 0         return $cache_data if defined $cache_data;
701             #在现有的讨论组中查找
702 0           for my $d (@{$self->{qq_database}{discuss}}){
  0            
703             #如果讨论组是存在的
704 0 0         if($d->{dinfo}{did} eq $did){
705             #在讨论组中查找指定的成员
706             #如果讨论组数据库中包含讨论组成员数据
707 0 0 0       if(exists $d->{minfo} and ref $d->{minfo} eq 'ARRAY'){
708 0     0     my $m = first {$_->{uin} eq $member_uin} @{$d->{minfo} };
  0            
  0            
709 0 0         if(defined $m){
710 0           my $m_clone = dclone($m);
711 0           $self->{cache_for_discuss_member}->store("$did|$member_uin",$m_clone);
712 0           return $m_clone;
713             }
714 0           return undef;
715            
716             }
717             #群数据中只有dinfo,没有minfo
718             else{
719             #尝试重新更新一下讨论组信息,希望可以拿到minfo
720 0           my $discuss_info = $self->_get_discuss_info($did);
721 0 0 0       if(defined $discuss_info and ref $discuss_info->{minfo} eq 'ARRAY'){
722             #终于拿到minfo了 赶紧存起来 以备下次使用
723 0           $self->update_discuss_info($discuss_info);
724             #在minfo里找讨论组成员
725 0     0     my $m = first {$_->{uin} eq $member_uin} @{$discuss_info->{minfo}};
  0            
  0            
726 0 0         if(defined $m){
727 0           my $m_clone = dclone($m);
728 0           $self->{cache_for_discuss_member}->store("$did|$member_uin",$m_clone);
729 0           return $m_clone;
730             }
731             #靠 还是没找到
732 0           return undef;
733             }
734             #很可惜,还是拿不到minfo
735             else{
736 0           return undef;
737             }
738             }
739             }
740             }
741             #遍历所有的群也找不到,返回undef
742 0           return undef;
743             }
744              
745             sub search_discuss{
746 0     0 1   my $self = shift;
747 0           my $did = shift;
748 0           my $cache_data = $self->{cache_for_discuss}->retrieve($did);
749 0 0         return $cache_data if defined $cache_data;
750 0     0     my $d = first {$_->{dinfo}{did} eq $did} @{ $self->{qq_database}{discuss} };
  0            
  0            
751 0 0         if(defined $d){
752 0           my $clone = dclone($d->{dinfo});
753 0           $self->{cache_for_discuss}->store($did,$clone);
754 0           return $clone;
755             }
756 0           return undef;
757             }
758              
759             sub search_stranger{
760 0     0 1   my($self,$tuin) = @_;
761 0           my $cache_data = $self->{cache_for_stranger}->retrieve($tuin);
762 0 0         return $cache_data if defined $cache_data;
763 0           for my $g ( @{$self->{qq_database}{group}} ){
  0            
764 0           for my $m (@{ $g->{minfo} }){
  0            
765 0 0         if($m->{uin} eq $tuin){
766 0           my $m_clone = dclone($m);
767 0           $self->{cache_for_stranger}->store($tuin,$m_clone);
768 0           return $m_clone;
769             }
770             }
771             }
772            
773 0 0         $self->_get_stranger_info($tuin) or undef;
774             }
775              
776             sub search_group{
777 0     0 1   my($self,$gcode) = @_;
778 0           my $cache_data = $self->{cache_for_group}->retrieve($gcode);
779 0 0         return $cache_data if defined $cache_data;
780              
781 0     0     my $g = first {$_->{ginfo}{code} eq $gcode} @{ $self->{qq_database}{group} };
  0            
  0            
782 0 0         if(defined $g){
783 0           my $clone = dclone($g->{ginfo});
784 0           $self->{cache_for_group}->store($gcode,$clone);
785 0           return $clone;
786             }
787 0           return undef ;
788             }
789              
790             sub update_user_info{
791 0     0 1   my $self = shift;
792 0           console "更新个人信息...\n";
793 0           my $user_info = $self->_get_user_info();
794 0 0         if(defined $user_info){
  0            
795 0           for my $key (keys %{ $user_info }){
  0            
796 0 0         if($key eq 'birthday'){
797 0           $self->{qq_database}{user}{birthday} =
798 0           encode("utf8", join("-",@{ $user_info->{birthday}}{qw(year month day)} ) );
799             }
800             else{
801 0           $self->{qq_database}{user}{$key} = encode("utf8",$user_info->{$key});
802             }
803             }
804 0           my $single_long_nick = $self->get_single_long_nick($self->{qq_param}{qq});
805 0 0         if(defined $single_long_nick){
806 0           $self->{qq_database}{user}{single_long_nick} = $single_long_nick;
807             }
808             }
809             else{console "更新个人信息失败\n";}
810             }
811             sub update_friends_info{
812 0     0 1   my $self=shift;
813 0           my $friend = shift;
814 0 0         if(defined $friend){
815 0           for(@{ $self->{qq_database}{friends} }){
  0            
816 0 0         if($_->{uin} eq $friend->{uin}){
817 0           $_ = $friend;
818 0           return;
819             }
820             }
821 0           push @{ $self->{qq_database}{friends} },$friend;
  0            
822 0           return;
823             }
824 0           console "更新好友信息...\n";
825 0           my $friends_info = $self->_get_user_friends();
826 0 0         if(defined $friends_info){
  0            
827 0           $self->{qq_database}{friends} = $friends_info;
828             }
829             else{console "更新好友信息失败\n";}
830            
831             }
832              
833             sub update_discuss_info {
834 0     0 1   my $self = shift;
835 0           my $discuss = shift;
836 0 0         my $is_init = 1 if @{$self->{qq_database}{discuss}} == 0;
  0            
837 0 0         if(defined $discuss){
838 0           for( @{$self->{qq_database}{discuss}} ){
  0            
839 0 0         if($_->{dinfo}{did} eq $discuss->{dinfo}{did} ){
840 0           $self->_detect_loss_discuss_member($_,$discuss);
841 0           $self->_detect_new_discuss_member2($_,$discuss);
842 0           $_ = $discuss;
843 0           return;
844             }
845             }
846 0           push @{$self->{qq_database}{discuss}},$discuss;
  0            
847 0 0 0       if(!$is_init and ref $self->{on_new_discuss} eq 'CODE'){
848 0           eval {
849 0           $self->{on_new_discuss}->(dclone($discuss));
850             };
851 0 0         console $@ . "\n" if $@;
852             }
853 0           return;
854             }
855 0           $self->update_discuss_list_info();
856 0           for my $dl (@{ $self->{qq_database}{discuss_list} }){
  0            
857 0           console "更新[ $dl->{name} ]讨论组信息...\n";
858 0           my $discuss_info = $self->_get_discuss_info($dl->{did});
859 0 0         if(defined $discuss_info){
  0            
860 0 0         if(ref $discuss_info->{minfo} ne 'ARRAY'){
861 0           console "更新[ $dl->{name} ]讨论组成功,但暂时没有获取到讨论组成员信息...\n";
862             }
863 0           my $flag = 0;
864 0           for( @{$self->{qq_database}{discuss}} ){
  0            
865 0 0         if($_->{dinfo}{did} eq $discuss_info->{dinfo}{did} ){
866 0           $self->_detect_loss_discuss_member($_,$discuss_info);
867 0           $self->_detect_new_discuss_member2($_,$discuss_info);
868 0 0         $_ = $discuss_info if ref $discuss_info->{minfo} eq 'ARRAY';
869 0           $flag = 1;
870 0           last;
871             }
872             }
873 0 0         if($flag == 0){
874 0           push @{ $self->{qq_database}{discuss} }, $discuss_info;
  0            
875 0 0 0       if( !$is_init and ref $self->{on_new_discuss} eq 'CODE'){
876 0           eval {
877 0           $self->{on_new_discuss}->(dclone($discuss_info));
878             };
879 0 0         console $@ . "\n" if $@;
880             }
881             }
882            
883             }
884             else{console "更新[ $dl->{name} ]讨论组信息失败\n";}
885             }
886             }
887              
888             sub update_discuss_list_info {
889 0     0 0   my $self = shift;
890 0           my $discuss = shift;
891 0 0         if(defined $discuss ){
892 0           for(@{ $self->{qq_database}{discuss_list} }){
  0            
893 0 0         if($_->{did} eq $discuss->{did}){
894 0           $_ = $discuss;
895 0           return;
896             }
897             }
898 0           push @{ $self->{qq_database}{discuss_list} }, $discuss;
  0            
899 0           return;
900             }
901 0           console "更新讨论组列表信息...\n";
902 0           my $discuss_list_info = $self->_get_discuss_list_info();
903 0 0         if(defined $discuss_list_info){
  0            
904 0           $self->{qq_database}{discuss_list} = $discuss_list_info;
905             }
906             else{console "更新讨论组列表信息失败\n";}
907            
908             }
909              
910             sub update_group_info{
911 0     0 1   my $self = shift;
912 0           my $group = shift;
913 0 0         my $is_init = 1 if @{$self->{qq_database}{group}} == 0;
  0            
914 0 0         if(defined $group){
915 0           for( @{$self->{qq_database}{group}} ){
  0            
916 0 0         if($_->{ginfo}{code} eq $group->{ginfo}{code} ){
917 0           $self->_detect_loss_group_member($_,$group);
918 0           $self->_detect_new_group_member2($_,$group);
919 0           $_ = $group;
920 0           return;
921             }
922             }
923 0           push @{$self->{qq_database}{group}},$group;
  0            
924 0 0 0       if(!$is_init and ref $self->{on_new_group} eq 'CODE'){
925 0           eval {
926 0           $self->{on_new_group}->(dclone($group));
927             };
928 0 0         console $@ . "\n" if $@;
929             }
930 0           return;
931             }
932 0           $self->update_group_list_info();
933 0           for my $gl (@{ $self->{qq_database}{group_list} }){
  0            
934 0           console "更新[ $gl->{name} ]群信息...\n";
935 0           my $group_info = $self->_get_group_info($gl->{code});
936 0 0         if(defined $group_info){
  0            
937 0 0         if(ref $group_info->{minfo} ne 'ARRAY'){
938 0           console "更新[ $gl->{name} ]成功,但暂时没有获取到群成员信息...\n";
939             }
940 0           my $flag = 0;
941 0           for( @{$self->{qq_database}{group}} ){
  0            
942 0 0         if($_->{ginfo}{code} eq $group_info->{ginfo}{code} ){
943 0           $self->_detect_loss_group_member($_,$group_info);
944 0           $self->_detect_new_group_member2($_,$group_info);
945 0 0         $_ = $group_info if ref $group_info->{minfo} eq 'ARRAY';
946 0           $flag = 1;
947 0           last;
948             }
949             }
950 0 0         if($flag == 0){
951 0           push @{ $self->{qq_database}{group} }, $group_info;
  0            
952 0 0 0       if( !$is_init and ref $self->{on_new_group} eq 'CODE'){
953 0           eval {
954 0           $self->{on_new_group}->(dclone($group_info));
955             };
956 0 0         console $@ . "\n" if $@;
957             }
958             }
959             }
960             else{console "更新[ $gl->{name} ]群信息失败\n";}
961            
962             }
963             }
964             sub update_recent_info {
965 0     0 0   my $self = shift;
966 0           my $recent = $self->_get_recent_info();
967 0 0         $self->{qq_database}{recent} = $recent if defined $recent;
968             }
969             sub update_group_list_info{
970 0     0 1   my $self = shift;
971 0           my $group = shift;
972 0 0         if(defined $group ){
973 0           for(@{ $self->{qq_database}{group_list} }){
  0            
974 0 0         if($_->{code} eq $group->{code}){
975 0           $_ = $group;
976 0           return;
977             }
978             }
979 0           push @{ $self->{qq_database}{group_list} }, $group;
  0            
980 0           return;
981             }
982 0           console "更新群列表信息...\n";
983 0           my $group_list_info = $self->_get_group_list_info();
984 0 0         if(defined $group_list_info){
985 0           $self->{qq_database}{group_list} = $group_list_info->{gnamelist};
986 0           my %gmarklist;
987 0           for(@{ $group_list_info->{gmarklist} }){
  0            
988 0           $gmarklist{$_->{uin}} = $_->{markname};
989             }
990 0           for(@{ $self->{qq_database}{group_list} }){
  0            
991 0           $_->{markname} = $gmarklist{$_->{gid}};
992 0           $_->{name} = encode("utf8",$_->{name});
993             }
994             }
995             #else{console "更新群列表信息失败\n";}
996             }
997              
998             sub update_friend_state_info{
999 0     0 0   my $self = shift;
1000 0           my ($uin,$state,$client_type) = @_;
1001 0     0     my $f = first {$_->{uin} eq $uin} @{$self->{qq_database}{friends}};
  0            
  0            
1002 0 0         if(defined $f){
1003 0           $f->{state} = $state;
1004 0           $f->{client_type} = $client_type;
1005 0           return dclone($f);
1006             }
1007 0           return undef;
1008             }
1009              
1010             sub get_group_code_from_gid {
1011 0     0 1   my $self = shift;
1012 0           my $gid = shift;
1013 0     0     my $group = first {$_->{gid} eq $gid} @{ $self->{qq_database}{group_list} };
  0            
  0            
1014 0 0         return defined $group?$group->{code}:undef;
1015             }
1016              
1017             sub _detect_new_friend{
1018 0     0     my $self = shift;
1019 0           my $uin = shift;
1020 0 0         return if defined $self->search_friend($uin);
1021             #新增好友
1022 0           my $friend = $self->_get_friend_info($uin);
1023 0 0         if(defined $friend){
1024 0           $self->{cache_for_friend}->store($uin,$friend);
1025 0           push @{ $self->{qq_database}{friends} },$friend;
  0            
1026 0 0         if(ref $self->{on_new_friend} eq 'CODE'){
1027 0           eval{
1028 0           $self->{on_new_friend}->($friend);
1029             };
1030 0 0         console $@ . "\n" if $@;
1031             }
1032 0           return ;
1033             }
1034             #新增陌生好友(你是对方好友,但对方还不是你好友)
1035             else{
1036 0           my $default_friend = {
1037             uin => $uin,
1038             categories => "陌生人",
1039             nick => undef,
1040             };
1041 0           push @{ $self->{qq_database}{friends} },$default_friend;
  0            
1042 0           return ;
1043             }
1044            
1045             }
1046              
1047             sub _detect_new_group{
1048 0     0     my $self = shift;
1049 0           my $gcode = shift;
1050 0 0         return if defined $self->search_group($gcode);
1051 0           my $group_info = $self->_get_group_info($gcode);
1052 0 0         if(defined $group_info ){
1053 0           $self->update_group_list_info({
1054             name => $group_info->{ginfo}{name},
1055             gid => $group_info->{ginfo}{gid},
1056             code => $group_info->{ginfo}{code},
1057             });
1058 0           push @{$self->{qq_database}{group}},$group_info;
  0            
1059 0 0         if(ref $self->{on_new_group} eq 'CODE'){
1060 0           eval{
1061 0           $self->{on_new_group}->(dclone($group_info));
1062             };
1063 0 0         console $@ . "\n" if $@;
1064             }
1065 0           return ;
1066             }
1067             else{
1068 0           return ;
1069             }
1070             }
1071              
1072             sub _detect_new_group_member{
1073 0     0     my $self = shift;
1074 0           my ($gcode,$member_uin) = @_;
1075 0           my $default_member = {
1076             nick => undef,
1077             province => undef,
1078             gender => undef,
1079             uin => $member_uin,
1080             country => undef,
1081             city => undef,
1082             card => undef,
1083             };
1084              
1085 0     0     my $group = first {$_->{ginfo}{code} eq $gcode} @{$self->{qq_database}{group}};
  0            
  0            
1086             #群至少得存在
1087 0 0         return unless defined $group;
1088             #如果包含群成员信息
1089 0 0         if(exists $group->{minfo}){
1090 0 0         return if defined $self->search_member_in_group($gcode,$member_uin);
1091             #查不到成员信息,说明是新增的成员,重新更新一次群信息
1092 0           my $new_group_member = {};
1093 0           my $group_info = $self->_get_group_info($gcode);
1094             #更新群信息成功
1095 0 0 0       if(defined $group_info and ref $group_info->{minfo} eq 'ARRAY'){
1096             #再次查找新增的成员
1097 0     0     my $m = first {$_->{uin} eq $member_uin} @{$group_info->{minfo}};
  0            
  0            
1098 0 0         if(defined $m){
1099 0           $self->{cache_for_group_member}->store("$gcode|$member_uin",dclone($m));
1100 0           $new_group_member = $m;
1101             }
1102             else{
1103 0           $new_group_member = $default_member;
1104             }
1105             }
1106             #群成员信息更新失败
1107             else{
1108 0           $new_group_member = $default_member;
1109             }
1110              
1111 0           push @{$group->{minfo}},$new_group_member;
  0            
1112 0 0         if(ref $self->{on_new_group_member} eq 'CODE'){
1113 0           eval{
1114 0           $self->{on_new_group_member}->(dclone($group),dclone($new_group_member));
1115             };
1116 0 0         console $@ . "\n" if $@;
1117             }
1118 0           return;
1119             }
1120             else{
1121 0           return;
1122             }
1123             }
1124              
1125             sub _detect_new_group_member2 {
1126 0     0     my $self = shift;
1127 0           my($group_old,$group_new) = @_;
1128 0 0         return if ref $group_old->{minfo} ne 'ARRAY';
1129 0 0         return if ref $group_new->{minfo} ne 'ARRAY';
1130 0           my %e = map {$_->{uin} => undef} @{$group_old->{minfo}};
  0            
  0            
1131 0           for my $new (@{$group_new->{minfo}}){
  0            
1132             #旧的没有,新的有,说明是新增群成员
1133 0 0         unless(exists $e{$new->{uin}}){
1134 0 0         if(ref $self->{on_new_group_member} eq 'CODE'){
1135 0           eval{
1136 0           $self->{on_new_group_member}->(dclone($group_new),dclone($new));
1137             };
1138 0 0         console $@ . "\n" if $@;
1139             };
1140             }
1141             }
1142            
1143             }
1144              
1145             sub _detect_loss_group_member {
1146 0     0     my $self = shift;
1147 0           my($group_old,$group_new) = @_;
1148 0 0         return if ref $group_old->{minfo} ne 'ARRAY';
1149 0 0         return if ref $group_new->{minfo} ne 'ARRAY';
1150 0           my %e = map {$_->{uin} => undef} @{$group_new->{minfo}};
  0            
  0            
1151 0           for my $old (@{$group_old->{minfo}}){
  0            
1152             #旧的有,新的没有,说明是已经退群的成员
1153 0 0         unless(exists $e{$old->{uin}}){
1154 0 0         if(ref $self->{on_loss_group_member} eq 'CODE'){
1155 0           eval{
1156 0           $self->{on_loss_group_member}->(dclone($group_old),dclone($old));
1157             };
1158 0 0         console $@ . "\n" if $@;
1159             };
1160             }
1161 0           $self->{cache_for_group_member}->delete($group_old->{ginfo}{code} . "|" . $old->{uin});
1162             }
1163              
1164             }
1165              
1166             sub _detect_new_discuss{
1167 0     0     my $self = shift;
1168 0           my $did = shift;
1169 0 0         return if defined $self->search_discuss($did);
1170 0           my $discuss_info = $self->_get_discuss_info($did);
1171 0 0         if(defined $discuss_info ){
1172 0           $self->update_discuss_list_info({
1173             name => $discuss_info->{dinfo}{name},
1174             did => $discuss_info->{dinfo}{did},
1175             });
1176 0           push @{$self->{qq_database}{discuss}},$discuss_info;
  0            
1177 0 0         if(ref $self->{on_new_discuss} eq 'CODE'){
1178 0           eval{
1179 0           $self->{on_new_discuss}->(dclone($discuss_info));
1180             };
1181 0 0         console $@ . "\n" if $@;
1182             }
1183 0           return ;
1184             }
1185             else{
1186 0           return ;
1187             }
1188             }
1189             sub _detect_loss_discuss_member {
1190 0     0     my $self = shift;
1191 0           my($discuss_old,$discuss_new) = @_;
1192 0 0         return if ref $discuss_old->{minfo} ne 'ARRAY';
1193 0 0         return if ref $discuss_new->{minfo} ne 'ARRAY';
1194 0           my %e = map {$_->{uin} => undef} @{$discuss_new->{minfo}};
  0            
  0            
1195 0           for my $old (@{$discuss_old->{minfo}}){
  0            
1196             #旧的有,新的没有,说明是已经退群的成员
1197 0 0         unless(exists $e{$old->{uin}}){
1198 0 0         if(ref $self->{on_loss_discuss_member} eq 'CODE'){
1199 0           eval{
1200 0           $self->{on_loss_discuss_member}->(dclone($discuss_old),dclone($old));
1201             };
1202 0 0         console $@ . "\n" if $@;
1203             };
1204             }
1205 0           $self->{cache_for_discuss_member}->delete($discuss_old->{dinfo}{did} . "|" . $old->{uin});
1206             }
1207             }
1208             sub _detect_new_discuss_member {
1209 0     0     my $self = shift;
1210 0           my ($did,$member_uin) = @_;
1211 0           my $default_member = {
1212             nick => undef,
1213             uin => $member_uin,
1214             };
1215              
1216 0     0     my $discuss = first {$_->{dinfo}{did} eq $did} @{$self->{qq_database}{discuss} };
  0            
  0            
1217             #群至少得存在
1218 0 0         return unless defined $discuss;
1219             #如果包含成员信息
1220 0 0         if(exists $discuss->{minfo}){
1221 0 0         return if defined $self->search_member_in_discuss($did,$member_uin);
1222             #查不到成员信息,说明是新增的成员,重新更新一次群信息
1223 0           my $new_discuss_member = {};
1224 0           my $discuss_info = $self->_get_discuss_info($did);
1225             #更新群信息成功
1226 0 0 0       if(defined $discuss_info and ref $discuss_info->{minfo} eq 'ARRAY'){
1227             #再次查找新增的成员
1228 0     0     my $m = first {$_->{uin} eq $member_uin} @{$discuss_info->{minfo}};
  0            
  0            
1229 0 0         if(defined $m){
1230 0           $self->{cache_for_discuss_member}->store("$did|$member_uin",dclone($m));
1231 0           $new_discuss_member = $m;
1232             }
1233             else{
1234             #仍然找不到信息,只好直接返回空了
1235 0           $new_discuss_member = $default_member;
1236             }
1237             }
1238             #成员信息更新失败
1239             else{
1240 0           $new_discuss_member = $default_member;
1241             }
1242              
1243 0           push @{$discuss->{minfo}},$new_discuss_member;
  0            
1244 0 0         if(ref $self->{on_new_discuss_member} eq 'CODE'){
1245 0           eval{
1246 0           $self->{on_new_discuss_member}->(dclone($discuss),dclone($new_discuss_member));
1247             };
1248 0 0         console $@ . "\n" if $@;
1249             }
1250 0           return;
1251             }
1252             else{
1253 0           return;
1254             }
1255             }
1256             sub _detect_new_discuss_member2 {
1257 0     0     my $self = shift;
1258 0           my($discuss_old,$discuss_new) = @_;
1259 0 0         return if ref $discuss_old->{minfo} ne 'ARRAY';
1260 0 0         return if ref $discuss_new->{minfo} ne 'ARRAY';
1261 0           my %e = map {$_->{uin} => undef} @{$discuss_old->{minfo}};
  0            
  0            
1262 0           for my $new (@{$discuss_new->{minfo}}){
  0            
1263             #旧的没有,新的有,说明是新增群成员
1264 0 0         unless(exists $e{$new->{uin}}){
1265 0 0         if(ref $self->{on_new_discuss_member} eq 'CODE'){
1266 0           eval{
1267 0           $self->{on_new_discuss_member}->(dclone($discuss_new),dclone($new));
1268             };
1269 0 0         console $@ . "\n" if $@;
1270             };
1271             }
1272             }
1273             }
1274              
1275              
1276             1;
1277             __END__