line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Mojo::Webqq::Client; |
2
|
1
|
|
|
1
|
|
10
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
29
|
|
3
|
1
|
|
|
1
|
|
6
|
use POSIX (); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
17
|
|
4
|
1
|
|
|
1
|
|
506
|
use Mojo::IOLoop; |
|
1
|
|
|
|
|
130032
|
|
|
1
|
|
|
|
|
7
|
|
5
|
1
|
|
|
1
|
|
52
|
use Mojo::IOLoop::Delay; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
9
|
|
6
|
|
|
|
|
|
|
$Mojo::Webqq::Client::CLIENT_COUNT = 0; |
7
|
1
|
|
|
1
|
|
593
|
use Mojo::Webqq::Message::Handle; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
33
|
|
8
|
1
|
|
|
1
|
|
587
|
use Mojo::Webqq::Client::Remote::_prepare_for_login; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
34
|
|
9
|
1
|
|
|
1
|
|
774
|
use Mojo::Webqq::Client::Remote::_check_verify_code; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
33
|
|
10
|
1
|
|
|
1
|
|
399
|
use Mojo::Webqq::Client::Remote::_get_img_verify_code; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
30
|
|
11
|
1
|
|
|
1
|
|
663
|
use Mojo::Webqq::Client::Remote::_check_login; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
32
|
|
12
|
1
|
|
|
1
|
|
420
|
use Mojo::Webqq::Client::Remote::_get_qrlogin_pic; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
30
|
|
13
|
1
|
|
|
1
|
|
1064
|
use Mojo::Webqq::Client::Remote::_login1; |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
34
|
|
14
|
1
|
|
|
1
|
|
450
|
use Mojo::Webqq::Client::Remote::_check_sig; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
35
|
|
15
|
1
|
|
|
1
|
|
609
|
use Mojo::Webqq::Client::Remote::_login2; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
34
|
|
16
|
1
|
|
|
1
|
|
541
|
use Mojo::Webqq::Client::Remote::_get_vfwebqq; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
35
|
|
17
|
1
|
|
|
1
|
|
517
|
use Mojo::Webqq::Client::Remote::_cookie_proxy; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
32
|
|
18
|
1
|
|
|
1
|
|
500
|
use Mojo::Webqq::Client::Remote::change_state; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
31
|
|
19
|
1
|
|
|
1
|
|
409
|
use Mojo::Webqq::Client::Remote::_get_offpic; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
30
|
|
20
|
1
|
|
|
1
|
|
418
|
use Mojo::Webqq::Client::Remote::_get_group_pic; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
29
|
|
21
|
1
|
|
|
1
|
|
617
|
use Mojo::Webqq::Client::Remote::_recv_message; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
34
|
|
22
|
1
|
|
|
1
|
|
739
|
use Mojo::Webqq::Client::Remote::_relink; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
33
|
|
23
|
1
|
|
|
1
|
|
503
|
use Mojo::Webqq::Client::Remote::logout; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3475
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
sub run{ |
26
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
27
|
0
|
0
|
|
|
|
|
$self->ready() if not $self->is_ready; |
28
|
0
|
|
|
|
|
|
$self->emit("run"); |
29
|
0
|
0
|
|
|
|
|
$self->ioloop->start unless $self->ioloop->is_running; |
30
|
|
|
|
|
|
|
} |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
sub steps { |
33
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
34
|
|
|
|
|
|
|
Mojo::IOLoop::Delay->new(ioloop=>$self->ioloop)->steps(@_)->catch(sub { |
35
|
0
|
|
|
0
|
|
|
my ($delay, $err) = @_; |
36
|
0
|
|
|
|
|
|
$self->error("steps error: $err"); |
37
|
0
|
|
|
|
|
|
})->wait; |
38
|
0
|
|
|
|
|
|
$self; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
sub stop{ |
41
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
42
|
0
|
0
|
|
|
|
|
return if $self->is_stop; |
43
|
0
|
|
|
|
|
|
$self->is_stop(1); |
44
|
0
|
|
|
|
|
|
$self->state('stop'); |
45
|
0
|
|
|
|
|
|
$self->emit("stop"); |
46
|
0
|
|
|
|
|
|
$self->info("客户端停止运行"); |
47
|
0
|
|
|
|
|
|
CORE::exit; |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
sub ready{ |
50
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
51
|
0
|
|
|
|
|
|
$self->state('loading'); |
52
|
|
|
|
|
|
|
#加载插件 |
53
|
0
|
|
|
|
|
|
my $plugins = $self->plugins; |
54
|
0
|
|
|
|
|
|
for( |
55
|
0
|
|
|
|
|
|
sort {$plugins->{$b}{priority} <=> $plugins->{$a}{priority} } |
56
|
0
|
0
|
|
|
|
|
grep {defined $plugins->{$_}{auto_call} and $plugins->{$_}{auto_call} == 1} keys %{$plugins} |
|
0
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
){ |
58
|
0
|
|
|
|
|
|
$self->call($_); |
59
|
|
|
|
|
|
|
} |
60
|
0
|
|
|
|
|
|
$self->state('loading'); |
61
|
0
|
|
|
|
|
|
$self->emit("after_load_plugin"); |
62
|
0
|
0
|
|
|
|
|
$self->login() if $self->login_state ne 'success'; |
63
|
0
|
0
|
|
|
|
|
$self->relogin() if $self->get_model_status() == 0; |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
$self->interval($self->update_interval || 600,sub{ |
66
|
0
|
0
|
|
0
|
|
|
return if $self->is_stop; |
67
|
0
|
0
|
|
|
|
|
return if not $self->is_update_group; |
68
|
0
|
|
|
|
|
|
$self->update_group(is_blocking=>0,is_update_group_ext=>0,is_update_group_member=>$self->is_update_group_member,is_update_group_member_ext=>$self->is_update_group_member_ext); |
69
|
0
|
|
0
|
|
|
|
}); |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
$self->timer(60,sub{ |
72
|
|
|
|
|
|
|
$self->interval($self->update_interval || 600,sub{ |
73
|
0
|
0
|
|
|
|
|
return if $self->is_stop; |
74
|
0
|
0
|
|
|
|
|
return if not $self->is_update_discuss; |
75
|
0
|
|
|
|
|
|
$self->update_discuss(is_blocking=>0,is_update_discuss_member=>1); |
76
|
0
|
|
0
|
0
|
|
|
}); |
77
|
0
|
|
|
|
|
|
}); |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
$self->timer(60+60,sub{ |
80
|
|
|
|
|
|
|
$self->interval($self->update_interval || 600,sub{ |
81
|
0
|
0
|
|
|
|
|
return if $self->is_stop; |
82
|
0
|
0
|
|
|
|
|
return if not $self->is_update_friend; |
83
|
0
|
|
|
|
|
|
$self->update_friend(is_blocking=>0,is_update_friend_ext=>0); |
84
|
0
|
|
0
|
0
|
|
|
}); |
85
|
0
|
|
|
|
|
|
}); |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
$self->timer(60+60+60,sub{ |
88
|
|
|
|
|
|
|
$self->interval($self->update_interval || 600,sub{ |
89
|
0
|
0
|
|
|
|
|
return if $self->is_stop; |
90
|
0
|
0
|
|
|
|
|
return if not $self->is_update_user; |
91
|
0
|
|
|
|
|
|
$self->update_user(is_blocking=>0); |
92
|
0
|
|
0
|
0
|
|
|
}); |
93
|
0
|
|
|
|
|
|
}); |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
#接收消息 |
96
|
0
|
|
|
0
|
|
|
$self->on(poll_over=>sub{ $self->state('running');my $self = $_[0];$self->timer(1,sub{$self->_recv_message()}) } ); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
$self->on(run=>sub{ |
98
|
0
|
|
|
0
|
|
|
my $self = $_[0]; |
99
|
0
|
|
|
|
|
|
$self->info("开始接收消息..."); |
100
|
0
|
|
|
|
|
|
$self->state('running'); |
101
|
0
|
|
|
|
|
|
$self->_recv_message(); |
102
|
0
|
|
|
|
|
|
}); |
103
|
0
|
|
|
|
|
|
$self->is_ready(1); |
104
|
0
|
|
|
|
|
|
$self->emit("ready"); |
105
|
0
|
|
|
|
|
|
return $self; |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
sub timer { |
109
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
110
|
0
|
|
|
|
|
|
return $self->ioloop->timer(@_); |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
sub interval{ |
113
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
114
|
0
|
|
|
|
|
|
return $self->ioloop->recurring(@_); |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
sub relogin{ |
117
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
118
|
0
|
|
|
|
|
|
$self->info("正在重新登录...\n"); |
119
|
0
|
0
|
|
|
|
|
if(defined $self->poll_connection_id){ |
120
|
0
|
|
|
|
|
|
eval{ |
121
|
0
|
|
|
|
|
|
$self->ioloop->remove($self->poll_connection_id); |
122
|
0
|
|
|
|
|
|
$self->is_polling(0); |
123
|
0
|
|
|
|
|
|
$self->info("停止接收消息..."); |
124
|
|
|
|
|
|
|
}; |
125
|
0
|
0
|
|
|
|
|
$self->info("停止接收消息失败: $@") if $@; |
126
|
|
|
|
|
|
|
} |
127
|
0
|
|
|
|
|
|
$self->logout(); |
128
|
0
|
|
|
|
|
|
$self->login_state("relogin"); |
129
|
0
|
|
|
|
|
|
$self->sess_sig_cache(Mojo::Webqq::Cache->new); |
130
|
0
|
|
|
|
|
|
$self->id_to_qq_cache(Mojo::Webqq::Cache->new); |
131
|
|
|
|
|
|
|
#$self->clear_cookie(); |
132
|
0
|
|
|
|
|
|
$self->poll_failure_count(0); |
133
|
0
|
|
|
|
|
|
$self->send_failure_count(0); |
134
|
0
|
|
|
|
|
|
$self->qrcode_count(0); |
135
|
0
|
|
|
|
|
|
$self->csrf_token(undef); |
136
|
0
|
|
|
|
|
|
$self->model_ext(0); |
137
|
|
|
|
|
|
|
|
138
|
0
|
|
|
|
|
|
$self->user(+{}); |
139
|
0
|
|
|
|
|
|
$self->friend([]); |
140
|
0
|
|
|
|
|
|
$self->group([]); |
141
|
0
|
|
|
|
|
|
$self->discuss([]); |
142
|
0
|
|
|
|
|
|
$self->model_status(+{}); |
143
|
|
|
|
|
|
|
|
144
|
0
|
|
|
|
|
|
$self->login(delay=>0); |
145
|
0
|
|
|
|
|
|
$self->info("重新开始接收消息..."); |
146
|
0
|
|
|
|
|
|
$self->_recv_message(); |
147
|
0
|
|
|
|
|
|
$self->emit("relogin"); |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
sub relink { |
150
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
151
|
0
|
|
|
|
|
|
$self->info("尝试进行重新连接(1)..."); |
152
|
0
|
0
|
0
|
|
|
|
if($self->_get_vfwebqq() && $self->_login2()){ |
153
|
0
|
|
|
|
|
|
$self->info("重新连接(1)成功"); |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
else{ |
156
|
0
|
|
|
|
|
|
$self->info("重新连接(1)失败"); |
157
|
0
|
|
|
|
|
|
$self->relogin(); |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
sub login { |
161
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
162
|
0
|
0
|
|
|
|
|
return if $self->login_state eq 'success'; |
163
|
0
|
|
|
|
|
|
my %p = @_; |
164
|
0
|
|
|
|
|
|
my $is_scan = 0; |
165
|
0
|
0
|
|
|
|
|
my $delay = defined $p{delay}?$p{delay}:0; |
166
|
0
|
0
|
|
|
|
|
if($self->is_first_login == -1){ |
|
|
0
|
|
|
|
|
|
167
|
0
|
|
|
|
|
|
$self->is_first_login(1); |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
elsif($self->is_first_login == 1){ |
170
|
0
|
|
|
|
|
|
$self->is_first_login(0); |
171
|
|
|
|
|
|
|
} |
172
|
0
|
0
|
|
|
|
|
if($self->is_first_login){ |
173
|
|
|
|
|
|
|
#$self->load_cookie(); #转移到new的时候就调用,这里不再需要 |
174
|
0
|
|
|
|
|
|
my $ptwebqq = $self->search_cookie("ptwebqq"); |
175
|
0
|
|
|
|
|
|
my $skey = $self->search_cookie("skey"); |
176
|
0
|
0
|
|
|
|
|
$self->ptwebqq($ptwebqq) if defined $ptwebqq; |
177
|
0
|
0
|
|
|
|
|
$self->skey($skey) if defined $skey; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
0
|
0
|
0
|
|
|
|
if($self->_prepare_for_login() && $self->_check_login()){ |
181
|
0
|
0
|
0
|
|
|
|
if(not $self->_check_sig() && $self->_get_vfwebqq() && $self->_login2()){ |
|
|
|
0
|
|
|
|
|
182
|
0
|
|
|
|
|
|
$self->relogin(); |
183
|
0
|
|
|
|
|
|
return; |
184
|
|
|
|
|
|
|
} |
185
|
0
|
|
|
|
|
|
$is_scan = 0; |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
else{ |
188
|
0
|
0
|
|
|
|
|
if($self->login_type eq 'login'){ |
189
|
0
|
|
|
|
|
|
$is_scan = 0; |
190
|
0
|
|
0
|
|
|
|
my $ret = $self->model_ext_authorize() |
191
|
|
|
|
|
|
|
&& $self->_prepare_for_login() |
192
|
|
|
|
|
|
|
&& $self->_check_login() |
193
|
|
|
|
|
|
|
&& $self->_check_sig() |
194
|
|
|
|
|
|
|
&& $self->_get_vfwebqq() |
195
|
|
|
|
|
|
|
&& $self->_login2(); |
196
|
0
|
0
|
|
|
|
|
if($ret == 1){ |
197
|
0
|
|
|
|
|
|
$self->info("账号密码方式登录成功"); |
198
|
0
|
|
|
|
|
|
$self->uid($self->account); |
199
|
0
|
|
|
|
|
|
$self->ptwebqq($self->search_cookie('ptwebqq')); |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
else{ |
202
|
0
|
|
|
|
|
|
$self->warn("账号密码登录方式失败,尝试使用二维码登录"); |
203
|
0
|
|
|
|
|
|
$self->login_type('qrlogin'); |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
} |
206
|
0
|
0
|
0
|
|
|
|
if( |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
207
|
|
|
|
|
|
|
$self->login_type eq 'qrlogin' |
208
|
|
|
|
|
|
|
&& $self->_check_verify_code() |
209
|
|
|
|
|
|
|
&& $self->_get_img_verify_code() |
210
|
|
|
|
|
|
|
&& $self->_get_qrlogin_pic() |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
){ |
213
|
0
|
|
|
|
|
|
while(1){ |
214
|
0
|
|
|
|
|
|
$self->check_controller(); |
215
|
0
|
|
|
|
|
|
my $ret = $self->_login1(); |
216
|
|
|
|
|
|
|
#if($ret == -1){#验证码输入错误 |
217
|
|
|
|
|
|
|
# $self->_get_img_verify_code(); |
218
|
|
|
|
|
|
|
# next; |
219
|
|
|
|
|
|
|
#} |
220
|
|
|
|
|
|
|
#if($ret == -2){#帐号或密码错误 |
221
|
|
|
|
|
|
|
# $self->error("登录失败,尝试更换加密算法计算方式,重新登录..."); |
222
|
|
|
|
|
|
|
# $self->encrypt_method("js"); |
223
|
|
|
|
|
|
|
# $self->relogin(); |
224
|
|
|
|
|
|
|
# return; |
225
|
|
|
|
|
|
|
#} |
226
|
0
|
0
|
0
|
|
|
|
if($ret == -4 ){#等待二维码扫描 |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
227
|
0
|
|
|
|
|
|
sleep 3; |
228
|
0
|
|
|
|
|
|
next; |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
elsif($ret == -5 ){#二维码已经扫描 等待手机端进行授权登录 |
231
|
0
|
|
|
|
|
|
sleep 3; |
232
|
0
|
|
|
|
|
|
next; |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
elsif($ret == -3 or $ret == -6){#二维码已经过期,或临时切换到二维码登录方式,重新下载二维码 |
235
|
0
|
|
|
|
|
|
$self->emit("qrcode_expire"); |
236
|
0
|
|
|
|
|
|
$self->_get_qrlogin_pic(); |
237
|
0
|
|
|
|
|
|
next; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
elsif($ret == 1){#登录成功 |
240
|
0
|
|
|
|
|
|
$is_scan = 1; |
241
|
0
|
0
|
0
|
|
|
|
$self->_check_sig() |
242
|
|
|
|
|
|
|
&& $self->_get_vfwebqq() |
243
|
|
|
|
|
|
|
&& $self->_login2(); |
244
|
0
|
|
|
|
|
|
last; |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
else{ |
247
|
0
|
|
|
|
|
|
last; |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
#登录不成功,客户端退出运行 |
254
|
0
|
0
|
|
|
|
|
if($self->login_state ne 'success'){ |
255
|
0
|
|
|
|
|
|
$self->fatal("登录失败,客户端退出(可能网络不稳定,请多尝试几次)"); |
256
|
0
|
|
|
|
|
|
$self->stop(); |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
else{ |
259
|
0
|
|
|
|
|
|
$self->qrcode_count(0); |
260
|
0
|
|
0
|
|
|
|
$self->info("帐号(" .( $self->uid // $self->account) . ")登录成功"); |
261
|
0
|
0
|
|
|
|
|
$self->login_type eq "qrlogin"?$self->clean_qrcode():$self->clean_verifycode(); |
262
|
|
|
|
|
|
|
#$self->model_ext_authorize() if $self->login_type eq 'qrlogin' and not defined $self->model_ext; |
263
|
0
|
0
|
|
|
|
|
$self->model_ext_authorize() if not defined $self->model_ext; |
264
|
0
|
|
|
|
|
|
$self->state('updating'); |
265
|
0
|
|
|
|
|
|
$self->update_user; |
266
|
0
|
0
|
|
|
|
|
$self->update_friend(is_blocking=>1,is_update_friend_ext=>1) if $self->is_init_friend; |
267
|
0
|
0
|
|
|
|
|
$self->update_group(is_blocking=>1,is_update_group_ext=>1,is_update_group_member_ext=>0,is_update_group_member=>0) if $self->is_init_group; |
268
|
0
|
0
|
|
|
|
|
$self->update_discuss(is_blocking=>1,is_update_discuss_member=>0) if $self->is_init_discuss; |
269
|
0
|
|
|
|
|
|
$self->emit("login",$is_scan); |
270
|
|
|
|
|
|
|
} |
271
|
0
|
|
|
|
|
|
return $self; |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
sub mail{ |
275
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
276
|
0
|
|
|
|
|
|
my $callback ; |
277
|
0
|
|
|
|
|
|
my $is_blocking = 1; |
278
|
0
|
0
|
|
|
|
|
if(ref $_[-1] eq "CODE"){ |
279
|
0
|
|
|
|
|
|
$callback = pop; |
280
|
0
|
|
|
|
|
|
$is_blocking = 0; |
281
|
|
|
|
|
|
|
} |
282
|
0
|
|
|
|
|
|
my %opt = @_; |
283
|
|
|
|
|
|
|
#smtp |
284
|
|
|
|
|
|
|
#port |
285
|
|
|
|
|
|
|
#tls |
286
|
|
|
|
|
|
|
#tls_ca |
287
|
|
|
|
|
|
|
#tls_cert |
288
|
|
|
|
|
|
|
#tls_key |
289
|
|
|
|
|
|
|
#user |
290
|
|
|
|
|
|
|
#pass |
291
|
|
|
|
|
|
|
#from |
292
|
|
|
|
|
|
|
#to |
293
|
|
|
|
|
|
|
#cc |
294
|
|
|
|
|
|
|
#subject |
295
|
|
|
|
|
|
|
#charset |
296
|
|
|
|
|
|
|
#html |
297
|
|
|
|
|
|
|
#text |
298
|
|
|
|
|
|
|
#data MIME::Lite产生的发送数据 |
299
|
0
|
|
|
|
|
|
eval{ require Mojo::SMTP::Client; } ; |
|
0
|
|
|
|
|
|
|
300
|
0
|
0
|
|
|
|
|
if($@){ |
301
|
0
|
|
|
|
|
|
$self->error("发送邮件,请先安装模块 Mojo::SMTP::Client"); |
302
|
0
|
|
|
|
|
|
return; |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
my %new = ( |
305
|
|
|
|
|
|
|
address => $opt{smtp}, |
306
|
0
|
|
0
|
|
|
|
port => $opt{port} || 25, |
307
|
|
|
|
|
|
|
autodie => $is_blocking, |
308
|
|
|
|
|
|
|
); |
309
|
0
|
|
|
|
|
|
for(qw(tls tls_ca tls_cert tls_key)){ |
310
|
0
|
0
|
|
|
|
|
$new{$_} = $opt{$_} if defined $opt{$_}; |
311
|
|
|
|
|
|
|
} |
312
|
0
|
0
|
0
|
|
|
|
$new{tls} = 1 if($new{port} == 465 and !defined $new{tls}); |
313
|
0
|
|
|
|
|
|
my $smtp = Mojo::SMTP::Client->new(%new); |
314
|
0
|
0
|
|
|
|
|
unless(defined $smtp){ |
315
|
0
|
|
|
|
|
|
$self->error("Mojo::SMTP::Client客户端初始化失败"); |
316
|
0
|
|
|
|
|
|
return; |
317
|
|
|
|
|
|
|
} |
318
|
0
|
|
|
|
|
|
my $data; |
319
|
0
|
0
|
|
|
|
|
if(defined $opt{data}){$data = $opt{data}} |
|
0
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
else{ |
321
|
0
|
|
|
|
|
|
my @data; |
322
|
0
|
|
|
|
|
|
push @data,("From: $opt{from}","To: $opt{to}"); |
323
|
0
|
0
|
|
|
|
|
push @data,"Cc: $opt{cc}" if defined $opt{cc}; |
324
|
0
|
|
|
|
|
|
require MIME::Base64; |
325
|
0
|
0
|
|
|
|
|
my $charset = defined $opt{charset}?$opt{charset}:"UTF-8"; |
326
|
0
|
|
|
|
|
|
push @data,"Subject: =?$charset?B?" . MIME::Base64::encode_base64($opt{subject},"") . "?="; |
327
|
0
|
0
|
|
|
|
|
if(defined $opt{text}){ |
|
|
0
|
|
|
|
|
|
328
|
0
|
|
|
|
|
|
push @data,("Content-Type: text/plain; charset=$charset",'',$opt{text}); |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
elsif(defined $opt{html}){ |
331
|
0
|
|
|
|
|
|
push @data,("Content-Type: text/html; charset=$charset",'',$opt{html}); |
332
|
|
|
|
|
|
|
} |
333
|
0
|
|
|
|
|
|
$data = join "\r\n",@data; |
334
|
|
|
|
|
|
|
} |
335
|
0
|
0
|
|
|
|
|
if(defined $callback){#non-blocking send |
336
|
|
|
|
|
|
|
$smtp->send( |
337
|
|
|
|
|
|
|
auth => {login=>$opt{user},password=>$opt{pass}}, |
338
|
|
|
|
|
|
|
from => $opt{from}, |
339
|
|
|
|
|
|
|
to => $opt{to}, |
340
|
|
|
|
|
|
|
data => $data, |
341
|
|
|
|
|
|
|
quit => 1, |
342
|
|
|
|
|
|
|
sub{ |
343
|
0
|
|
|
0
|
|
|
my ($smtp, $resp) = @_; |
344
|
0
|
0
|
|
|
|
|
if($resp->error){ |
345
|
0
|
|
|
|
|
|
$self->error("邮件[ To: $opt{to}|Subject: $opt{subject} ]发送失败: " . $resp->error ); |
346
|
0
|
0
|
|
|
|
|
$callback->(0,$resp->error) if ref $callback eq "CODE"; |
347
|
0
|
|
|
|
|
|
return; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
else{ |
350
|
0
|
|
|
|
|
|
$self->debug("邮件[ To: $opt{to}|Subject: $opt{subject} ]发送成功"); |
351
|
0
|
0
|
|
|
|
|
$callback->(1) if ref $callback eq "CODE"; |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
}, |
354
|
0
|
|
|
|
|
|
); |
355
|
|
|
|
|
|
|
} |
356
|
|
|
|
|
|
|
else{#blocking send |
357
|
0
|
|
|
|
|
|
eval{ |
358
|
|
|
|
|
|
|
$smtp->send( |
359
|
|
|
|
|
|
|
auth => {login=>$opt{user},password=>$opt{pass}}, |
360
|
|
|
|
|
|
|
from => $opt{from}, |
361
|
|
|
|
|
|
|
to => $opt{to}, |
362
|
0
|
|
|
|
|
|
data => $data, |
363
|
|
|
|
|
|
|
quit => 1, |
364
|
|
|
|
|
|
|
); |
365
|
|
|
|
|
|
|
}; |
366
|
0
|
0
|
|
|
|
|
return $@?(0,$@):(1,); |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
sub spawn { |
372
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
373
|
0
|
|
|
|
|
|
my %opt = @_; |
374
|
0
|
|
|
|
|
|
require Mojo::Webqq::Run; |
375
|
0
|
|
|
|
|
|
my $is_blocking = delete $opt{is_blocking}; |
376
|
0
|
0
|
|
|
|
|
my $run = Mojo::Webqq::Run->new(ioloop=>($is_blocking?Mojo::IOLoop->new:$self->ioloop),log=>$self->log); |
377
|
0
|
0
|
|
|
|
|
$run->max_forks(delete $opt{max_forks}) if defined $opt{max_forks}; |
378
|
0
|
|
|
|
|
|
$run->spawn(%opt); |
379
|
0
|
0
|
|
|
|
|
$run->start if $is_blocking; |
380
|
0
|
|
|
|
|
|
$run; |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
sub clean_qrcode{ |
383
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
384
|
0
|
0
|
|
|
|
|
return if not defined $self->qrcode_path; |
385
|
0
|
0
|
|
|
|
|
return if not -f $self->qrcode_path; |
386
|
0
|
|
|
|
|
|
$self->info("清除残留的历史二维码图片"); |
387
|
0
|
0
|
|
|
|
|
unlink $self->qrcode_path or $self->warn("删除二维码图片[ " . $self->qrcode_path . " ]失败: $!"); |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
sub clean_verifycode{ |
390
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
391
|
0
|
0
|
|
|
|
|
return if not defined $self->verifycode_path; |
392
|
0
|
0
|
|
|
|
|
return if not -f $self->verifycode_path; |
393
|
0
|
|
|
|
|
|
$self->info("清除残留的历史验证码图片"); |
394
|
0
|
0
|
|
|
|
|
unlink $self->verifycode_path or $self->warn("删除验证码图片[ ". $self->verifycode_path . " ]失败: $!"); |
395
|
|
|
|
|
|
|
} |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
sub add_job { |
398
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
399
|
0
|
|
|
|
|
|
require Mojo::Webqq::Client::Cron; |
400
|
0
|
|
|
|
|
|
$self->Mojo::Webqq::Client::Cron::add_job(@_); |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
sub check_pid { |
404
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
405
|
0
|
0
|
|
|
|
|
return if not $self->pid_path; |
406
|
0
|
|
|
|
|
|
eval{ |
407
|
0
|
0
|
|
|
|
|
if(not -f $self->pid_path){ |
408
|
0
|
|
|
|
|
|
$self->spurt($$,$self->pid_path); |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
else{ |
411
|
0
|
|
|
|
|
|
my $pid = $self->slurp($self->pid_path); |
412
|
0
|
0
|
0
|
|
|
|
if( $pid=~/^\d+$/ and kill(0, $pid) ){ |
413
|
0
|
|
|
|
|
|
$self->warn("检测到该账号有其他运行中的客户端(pid:$pid), 请先将其关闭"); |
414
|
0
|
|
|
|
|
|
$self->stop(); |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
else{ |
417
|
0
|
|
|
|
|
|
$self->spurt($$,$self->pid_path); |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
}; |
421
|
0
|
0
|
|
|
|
|
$self->warn("进程检测遇到异常: $@") if $@; |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
sub clean_pid { |
427
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
428
|
0
|
0
|
|
|
|
|
return if not defined $self->pid_path; |
429
|
0
|
0
|
|
|
|
|
return if not -f $self->pid_path; |
430
|
0
|
|
|
|
|
|
$self->info("清除残留的pid文件"); |
431
|
0
|
0
|
|
|
|
|
unlink $self->pid_path or $self->warn("删除pid文件[ " . $self->pid_path . " ]失败: $!"); |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
sub save_state{ |
435
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
436
|
0
|
|
|
|
|
|
my($previous_state,$current_state) = @_; |
437
|
0
|
|
|
|
|
|
my @attr = qw( |
438
|
|
|
|
|
|
|
account |
439
|
|
|
|
|
|
|
version |
440
|
|
|
|
|
|
|
start_time |
441
|
|
|
|
|
|
|
mode |
442
|
|
|
|
|
|
|
http_debug |
443
|
|
|
|
|
|
|
log_encoding |
444
|
|
|
|
|
|
|
log_path |
445
|
|
|
|
|
|
|
log_level |
446
|
|
|
|
|
|
|
log_console |
447
|
|
|
|
|
|
|
disable_color |
448
|
|
|
|
|
|
|
tmpdir |
449
|
|
|
|
|
|
|
cookie_path |
450
|
|
|
|
|
|
|
qrcode_path |
451
|
|
|
|
|
|
|
pid_path |
452
|
|
|
|
|
|
|
state_path |
453
|
|
|
|
|
|
|
keep_cookie |
454
|
|
|
|
|
|
|
ua_retry_times |
455
|
|
|
|
|
|
|
qrcode_count_max |
456
|
|
|
|
|
|
|
state |
457
|
|
|
|
|
|
|
); |
458
|
|
|
|
|
|
|
# pid |
459
|
|
|
|
|
|
|
# os |
460
|
0
|
|
|
|
|
|
eval{ |
461
|
0
|
|
|
|
|
|
my $json = {plugin => []}; |
462
|
0
|
|
|
|
|
|
for my $attr (@attr){ |
463
|
0
|
|
|
|
|
|
$json->{$attr} = $self->$attr; |
464
|
|
|
|
|
|
|
} |
465
|
0
|
|
|
|
|
|
$json->{previous_state} = $previous_state; |
466
|
0
|
|
|
|
|
|
$json->{pid} = $$; |
467
|
0
|
|
|
|
|
|
$json->{os} = $^O; |
468
|
0
|
|
|
|
|
|
for my $p (keys %{ $self->plugins }){ |
|
0
|
|
|
|
|
|
|
469
|
0
|
|
|
|
|
|
push @{ $json->{plugin} } , { name=>$self->plugins->{$p}{name},priority=>$self->plugins->{$p}{priority},auto_call=>$self->plugins->{$p}{auto_call},call_on_load=>$self->plugins->{$p}{call_on_load} } ; |
|
0
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
} |
471
|
0
|
|
|
|
|
|
$self->spurt($self->to_json($json),$self->state_path); |
472
|
|
|
|
|
|
|
}; |
473
|
0
|
0
|
|
|
|
|
$self->warn("客户端状态信息保存失败:$@") if $@; |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
sub is_load_plugin { |
477
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
478
|
0
|
|
|
|
|
|
my $plugin = shift; |
479
|
0
|
0
|
|
|
|
|
if(substr($plugin,0,1) eq '+'){ |
480
|
0
|
|
|
|
|
|
substr($plugin,0,1) = ""; |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
else{ |
483
|
0
|
|
|
|
|
|
$plugin = "Mojo::Webqq::Plugin::$plugin"; |
484
|
|
|
|
|
|
|
} |
485
|
0
|
|
|
|
|
|
return exists $self->plugins->{$plugin}; |
486
|
|
|
|
|
|
|
} |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
sub check_controller { |
489
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
490
|
0
|
|
|
|
|
|
my $once = shift; |
491
|
0
|
0
|
0
|
|
|
|
if($^O ne 'MSWin32' and defined $self->controller_pid ){ |
492
|
0
|
0
|
|
|
|
|
if($once){ |
493
|
0
|
|
|
|
|
|
$self->info("启用Controller[". $self->controller_pid ."]状态检查"); |
494
|
|
|
|
|
|
|
$self->interval(5=>sub{ |
495
|
0
|
|
|
0
|
|
|
$self->check_controller(); |
496
|
0
|
|
|
|
|
|
}); |
497
|
|
|
|
|
|
|
} |
498
|
|
|
|
|
|
|
else{ |
499
|
0
|
|
|
|
|
|
my $ppid = POSIX::getppid(); |
500
|
0
|
0
|
0
|
|
|
|
if( $ppid=~/^\d+$/ and $ppid == 1 or $ppid != $self->controller_pid ) { |
|
|
|
0
|
|
|
|
|
501
|
0
|
|
|
|
|
|
$self->warn("检测到脱离Controller进程管理,程序即将终止"); |
502
|
0
|
|
|
|
|
|
$self->stop(); |
503
|
|
|
|
|
|
|
} |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
sub check_notice { |
509
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
510
|
0
|
0
|
|
|
|
|
return if not $self->is_fetch_notice; |
511
|
0
|
|
|
|
|
|
$self->info("获取最新公告信息..."); |
512
|
0
|
|
|
|
|
|
my $notice = $self->http_get($self->notice_api); |
513
|
0
|
0
|
|
|
|
|
if($notice){ |
514
|
0
|
|
|
|
|
|
$self->info("-" x 40); |
515
|
0
|
|
|
|
|
|
$self->info({content_color=>'green'},$notice); |
516
|
0
|
|
|
|
|
|
$self->info("-" x 40); |
517
|
|
|
|
|
|
|
} |
518
|
|
|
|
|
|
|
} |
519
|
|
|
|
|
|
|
1; |