Webqq-Client/lib/Webqq/Message.pm
package Webqq::Message;
use Webqq::Message::Face;
use JSON;
use Encode;
use Webqq::Client::Util qw(console code2client);
use Scalar::Util qw(blessed);
sub reply_message{
my $client = shift;
my $msg = shift;
my $content = shift;
unless(blessed($msg)){
console "输入的msg数据非法\n";
return 0;
}
if($msg->{type} eq 'message'){
$client->send_message(
$client->create_msg(to_uin=>$msg->{from_uin},content=>$content)
);
}
elsif($msg->{type} eq 'group_message'){
$client->send_group_message(
$client->create_group_msg(
to_uin=>$msg->{from_uin},
content=>$content,
group_code=>$msg->{group_code}
)
);
}
elsif($msg->{type} eq 'discuss_message'){
$client->send_discuss_message(
$client->create_discuss_msg(
to_uin =>$msg->{did} || $msg->{from_uin},
content =>$content,
)
);
}
elsif($msg->{type} eq 'sess_message'){
#群临时消息
if($msg->{via} eq 'group'){
$client->send_sess_message(
$client->create_sess_msg(
to_uin => $msg->{from_uin},
content => $content,
group_code => $msg->{group_code},
gid => $msg->{gid},
)
);
}
#讨论组临时消息
elsif($msg->{via} eq 'discuss'){
$client->send_sess_message(
$client->create_sess_msg(
to_uin => $msg->{from_uin},
content => $content,
did => $msg->{did},
)
);
}
}
}
sub create_sess_msg{
my $client = shift;
return $client->_create_msg(@_,type=>'sess_message');
}
sub create_group_msg{
my $client = shift;
return $client->_create_msg(@_,type=>'group_message');
}
sub create_msg{
my $client = shift;
return $client->_create_msg(@_,type=>'message');
}
sub create_discuss_msg{
my $client = shift;
return $client->_create_msg(@_,type=>'discuss_message');
}
sub _create_msg {
my $client = shift;
my %p = @_;
$p{content} =~s/\r|\n/\n/g;
my %msg = (
type => $p{type},
msg_id => $p{msg_id} || ++$client->{qq_param}{send_msg_id},
from_uin => $p{from_uin} || $client->{qq_param}{from_uin},
to_uin => $p{to_uin},
content => $p{content},
msg_class => "send",
msg_time => time,
cb => $p{cb},
ttl => 5,
allow_plugin => 1,
client => $client,
);
if($p{type} eq 'sess_message'){
if(defined $p{group_code}){
$msg{group_code} = $p{group_code};
$msg{service_type} = 0;
$msg{via} = 'group';
my $id = defined $p{gid}?$p{gid}:$client->search_group($p{group_code})->{gid};
$msg{group_sig} = $client->_get_group_sig($id,$p{to_uin},$msg{service_type});
}
elsif(defined $p{gid}){
$msg{group_code} = $client->get_group_code_from_gid($p{gid});
$msg{service_type} = 0;
$msg{via} = 'group';
my $id = $p{gid};
$msg{group_sig} = $client->_get_group_sig($id,$p{to_uin},$msg{service_type});
}
elsif(defined $p{did}){
$msg{did} = $p{did};
$msg{service_type} = 1;
$msg{via} = 'discuss';
$msg{group_sig} = $client->_get_group_sig($p{did},$p{to_uin},$msg{service_type});
}
else{
console "create_sess_msg()必须设置group_code或者did\n";
return ;
}
}
elsif($p{type} eq 'group_message'){
$msg{group_code} = $p{group_code}||$client->get_group_code_from_gid($p{to_uin});
$msg{send_uin} = $msg{from_uin};
}
elsif($p{type} eq 'discuss_message'){
$msg{did} = $p{did} || $p{to_uin};
$msg{send_uin} = $msg{from_uin};
}
my $msg_pkg = "\u$p{type}::Send";
$msg_pkg=~s/_(.)/\u$1/g;
return $client->_mk_ro_accessors(\%msg,$msg_pkg);
}
sub _load_extra_accessor {
*Webqq::Message::DiscussMessage::Recv::discuss_name = sub{
my $msg = shift;
my $client = $msg->{client};
my $d = $client->search_discuss($msg->{did});
return defined $d?$d->{name}:undef ;
};
*Webqq::Message::DiscussMessage::Recv::from_dname = sub{
my $msg = shift;
my $client = $msg->{client};
my $d = $client->search_discuss($msg->{did});
return defined $d?$d->{name}:undef ;
};
*Webqq::Message::DiscussMessage::Recv::from_qq = sub{
my $msg = shift;
my $client = $msg->{client};
my $m = $client->search_member_in_discuss($msg->{did},$msg->{send_uin});
return defined $m?$m->{ruin}:$client->get_qq_from_uin($msg->{send_uin});
};
*Webqq::Message::DiscussMessage::Recv::from_nick = sub{
my $msg = shift;
my $client = $msg->{client};
my $m = $client->search_member_in_discuss($msg->{did},$msg->{send_uin});
return defined $m?$m->{nick}:undef;
};
*Webqq::Message::DiscussMessage::Send::discuss_name = sub{
my $msg = shift;
my $client = $msg->{client};
my $d = $client->search_discuss($msg->{did});
return defined $d?$d->{name}:undef;
};
*Webqq::Message::DiscussMessage::Send::to_dname = sub{
my $msg = shift;
my $client = $msg->{client};
my $d = $client->search_discuss($msg->{did});
return defined $d?$d->{name}:undef;
};
*Webqq::Message::DiscussMessage::Send::from_qq = sub{
my $msg = shift;
my $client = $msg->{client};
return $client->{qq_param}{qq};
};
*Webqq::Message::DiscussMessage::Send::from_nick = sub{
return "我";
};
*Webqq::Message::GroupMessage::Recv::group_name = sub{
my $msg = shift;
my $client = $msg->{client};
my $g = $client->search_group($msg->{group_code});
return defined $g?$g->{name}:undef ;
};
*Webqq::Message::GroupMessage::Recv::from_gname = sub{
my $msg = shift;
my $client = $msg->{client};
my $g = $client->search_group($msg->{group_code});
return defined $g?$g->{name}:undef ;
};
*Webqq::Message::GroupMessage::Recv::from_qq = sub{
my $msg = shift;
my $client = $msg->{client};
#my $m = $client->search_member_in_group($msg->{group_code},$msg->{send_uin});
#return $m->{qq} if(defined $m and defined $m->{qq});
return $client->get_qq_from_uin($msg->{send_uin});
};
*Webqq::Message::GroupMessage::Recv::from_nick = sub{
my $msg = shift;
my $client = $msg->{client};
my $m = $client->search_member_in_group($msg->{group_code},$msg->{send_uin});
return defined $m?$m->{nick}:undef;
};
*Webqq::Message::GroupMessage::Recv::from_card = sub{
my $msg = shift;
my $client = $msg->{client};
my $m = $client->search_member_in_group($msg->{group_code},$msg->{send_uin});
return defined $m?$m->{card}:undef;
};
*Webqq::Message::GroupMessage::Recv::from_city = sub{
my $msg = shift;
my $client = $msg->{client};
my $m = $client->search_member_in_group($msg->{group_code},$msg->{send_uin});
return defined $m?$m->{city}:undef;
};
*Webqq::Message::GroupMessage::Send::group_name = sub{
my $msg = shift;
my $client = $msg->{client};
my $g = $client->search_group($msg->{group_code});
return defined $g?$g->{name}:undef;
};
*Webqq::Message::GroupMessage::Send::to_gname = sub{
my $msg = shift;
my $client = $msg->{client};
my $g = $client->search_group($msg->{group_code});
return defined $g?$g->{name}:undef;
};
*Webqq::Message::GroupMessage::Send::from_qq = sub{
my $msg = shift;
my $client = $msg->{client};
return $client->{qq_param}{qq};
};
*Webqq::Message::GroupMessage::Send::from_nick = sub{
return "我";
};
*Webqq::Message::SessMessage::Recv::from_nick = sub{
my $msg = shift;
my $client = $msg->{client};
if($msg->{via} eq 'group'){
my $m = $client->search_member_in_group($msg->{group_code},$msg->{from_uin});
return defined $m?$m->{nick}:undef;
}
elsif($msg->{via} eq 'discuss'){
my $m = $client->search_member_in_discuss($msg->{did},$msg->{from_uin});
return defined $m?$m->{nick}:undef;
}
else{return undef}
};
*Webqq::Message::SessMessage::Recv::from_qq = sub {
my $msg = shift;
my $client = $msg->{client};
return $msg->{ruin};
};
*Webqq::Message::SessMessage::Recv::to_nick = sub{
return "我";
};
*Webqq::Message::SessMessage::Recv::to_qq = sub {
my $msg = shift;
my $client = $msg->{client};
return $client->{qq_param}{qq};
};
*Webqq::Message::SessMessage::Recv::via_type = sub {
my $msg = shift;
my $client = $msg->{client};
return $msg->{via} eq 'group' ? "群"
: $msg->{via} eq 'discuss' ? "讨论组"
: undef
;
};
*Webqq::Message::SessMessage::Recv::via_name = sub {
my $msg = shift;
my $client = $msg->{client};
if($msg->{via} eq 'group'){
my $g = $client->search_group($msg->{group_code});
return defined $g?$g->{name}:undef;
}
elsif($msg->{via} eq 'discuss'){
my $d = $client->search_discuss($msg->{did});
return defined $d?$d->{name}:undef;
}
else{return }
};
*Webqq::Message::SessMessage::Send::from_nick = sub{
return "我";
};
*Webqq::Message::SessMessage::Send::from_qq = sub {
my $msg = shift;
my $client = $msg->{client};
return $client->{qq_param}{qq};
};
*Webqq::Message::SessMessage::Send::to_nick = sub{
my $msg = shift;
my $client = $msg->{client};
if($msg->{via} eq 'group'){
my $m = $client->search_member_in_group($msg->{group_code},$msg->{to_uin});
return defined $m?$m->{nick}:undef;
}
elsif($msg->{via} eq 'discuss'){
my $m = $client->search_member_in_discuss($msg->{did},$msg->{to_uin});
return defined $m?$m->{nick}:undef;
}
else{return }
};
*Webqq::Message::SessMessage::Send::to_qq = sub{
my $msg = shift;
my $client = $msg->{client};
return $client->get_qq_from_uin($msg->{to_uin});
};
*Webqq::Message::SessMessage::Send::via_name = sub{
my $msg = shift;
my $client = $msg->{client};
if($msg->{via} eq 'group'){
my $g = $client->search_group($msg->{group_code});
return defined $g?$g->{name}:undef;
}
elsif($msg->{via} eq 'discuss'){
my $d = $client->search_discuss($msg->{did});
return defined $d?$d->{name}:undef;
}
else{return}
};
*Webqq::Message::SessMessage::Send::via_type = sub {
my $msg = shift;
my $client = $msg->{client};
return $msg->{via} eq 'group' ? "群"
: $msg->{via} eq 'discuss' ? "讨论组"
: undef
;
};
*Webqq::Message::Message::Recv::from_nick = sub{
my $msg = shift;
my $client = $msg->{client};
my $f = $client->search_friend($msg->{from_uin});
return defined $f?$f->{nick}:undef;
};
*Webqq::Message::Message::Recv::from_qq = sub{
my $msg = shift;
my $client = $msg->{client};
#my $f = $client->search_friend($msg->{from_uin});
#return $f->{qq} if(defined $f and defined $f->{qq});
return $client->get_qq_from_uin($msg->{from_uin});
};
*Webqq::Message::Message::Recv::from_markname = sub{
my $msg = shift;
my $client = $msg->{client};
my $f = $client->search_friend($msg->{from_uin});
return defined $f?$f->{markname}:undef;
};
*Webqq::Message::Message::Recv::from_categories = sub {
my $msg = shift;
my $client = $msg->{client};
my $f = $client->search_friend($msg->{from_uin});
return defined $f?$f->{categories}:undef;
};
*Webqq::Message::Message::Recv::from_city = sub {
my $msg = shift;
my $client = $msg->{client};
my $f = $client->search_friend($msg->{from_uin});
return defined $f?$f->{city}:undef;
};
*Webqq::Message::Message::Recv::to_nick = sub{
return "我";
};
*Webqq::Message::Message::Recv::to_qq = sub {
my $msg = shift;
my $client = $msg->{client};
return $client->{qq_param}{qq};
};
*Webqq::Message::Message::Send::from_nick = sub{
return "我";
};
*Webqq::Message::Message::Send::from_qq = sub{
my $msg = shift;
my $client = $msg->{client};
return $client->{qq_param}{qq};
};
*Webqq::Message::Message::Send::to_nick = sub{
my $msg = shift;
my $client = $msg->{client};
my $f = $client->search_friend($msg->{to_uin});
return defined $f?$f->{nick}:undef;
};
*Webqq::Message::Message::Send::to_qq = sub{
my $msg = shift;
my $client = $msg->{client};
return $client->get_qq_from_uin($msg->{to_uin});
};
*Webqq::Message::Message::Send::to_markname = sub{
my $msg = shift;
my $client = $msg->{client};
my $f = $client->search_friend($msg->{to_uin});
return defined $f?$f->{markname}:undef;
};
*Webqq::Message::Message::Send::to_categories = sub{
my $msg = shift;
my $client = $msg->{client};
my $f = $client->search_friend($msg->{to_uin});
return defined $f?$f->{categories}:undef;
};
}
sub _mk_ro_accessors {
my $client = shift;
my $msg =shift;
my $msg_pkg = shift;
no strict 'refs';
for my $field (keys %$msg){
*{"Webqq::Message::${msg_pkg}::$field"} = sub{
my $self = shift;
my $pkg = ref $self;
die "the value of \"$field\" in $pkg is read-only\n" if @_!=0;
return $self->{$field};
};
}
$msg = bless $msg,"Webqq::Message::$msg_pkg";
return $msg;
}
sub parse_send_status_msg{
my $client = shift;
my ($json_txt) = @_;
my $json = undef;
eval{$json = JSON->new->utf8->decode($json_txt)};
console "解析消息失败: $@ 对应的消息内容为: $json_txt\n" if $@ and $client->{debug};
if(ref $json eq 'HASH' and $json->{retcode}==0){
return {is_success=>1,status=>"发送成功"};
}
else{
return {is_success=>0,status=>"发送失败"};
}
}
#消息的后期处理
sub msg_put{
my $client = shift;
my $msg = shift;
$msg->{raw_content} = [];
my $msg_content;
shift @{ $msg->{content} };
for my $c (@{ $msg->{content} }){
if(ref $c eq 'ARRAY'){
if($c->[0] eq 'cface'){
push @{$msg->{raw_content}},{
type => 'cface',
content => '[图片]',
name => $c->[1]{name},
file_id => $c->[1]{file_id},
key => $c->[1]{key},
server => $c->[1]{server},
};
$c="[图片]";
}
elsif($c->[0] eq 'offpic'){
push @{$msg->{raw_content}},{
type => 'offpic',
content => '[图片]',
file_path => $c->[1]{file_path},
};
$c="[图片]";
}
elsif($c->[0] eq 'face'){
push @{$msg->{raw_content}},{
type => 'face',
content => face_to_txt($c),
id => $c->[1],
};
$c=face_to_txt($c);
}
else{
push @{$msg->{raw_content}},{
type => 'unknown',
content => '[未识别内容]',
};
$c = "[未识别内容]";
}
}
elsif($c eq " "){
next;
}
else{
$c=encode("utf8",$c);
$c=~s/ $//;
$c=~s/\r|\n/\n/g;
#{"retcode":0,"result":[{"poll_type":"group_message","value":{"msg_id":538,"from_uin":2859929324,"to_uin":3072574066,"msg_id2":545490,"msg_type":43,"reply_ip":182424361,"group_code":2904892801,"send_uin":1951767953,"seq":3024,"time":1418955773,"info_seq":390179723,"content":[["font",{"size":12,"color":"000000","style":[0,0,0],"name":"\u5FAE\u8F6F\u96C5\u9ED1"}],"[\u50BB\u7B11]\u0001 "]}}]}
#if($c=~/\[[^\[\]]+?\]\x{01}/)
push @{$msg->{raw_content}},{
type => 'txt',
content => $c,
};
}
$msg_content .= $c;
}
$msg->{content} = $msg_content;
$msg->{client} = $client;
#将整个hash从unicode转为UTF8编码
#$msg->{$_} = encode("utf8",$msg->{$_} ) for grep {$_ ne 'raw_content'} keys %$msg;
#$msg->{content}=~s/\r|\n/\n/g;
if($msg->{content}=~/\(\d+\) 被管理员禁言\d+(分钟|小时|天)$/ or $msg->{content}=~/\(\d+\) 被管理员解除禁言$/){
$msg->{type} = "sys_g_msg";
return;
}
my $msg_pkg = "\u$msg->{type}::Recv"; $msg_pkg=~s/_(.)/\u$1/g;
$msg = $client->_mk_ro_accessors($msg,$msg_pkg) ;
$client->{receive_message_queue}->put($msg);
}
sub parse_receive_msg{
my $client = shift;
return if $client->{is_stop} ;
my ($json_txt) = @_;
my $json = undef;
eval{$json = JSON->new->utf8->decode($json_txt)};
console "解析消息失败: $@ 对应的消息内容为: $json_txt\n" if $@ and $client->{debug};
if($json){
#一个普通的消息
if($json->{retcode}==0){
$client->{poll_failure_count} = 0;
for my $m (@{ $json->{result} }){
#收到群临时消息
if($m->{poll_type} eq 'sess_message'){
my $msg = {
type => 'sess_message',
msg_id => $m->{value}{msg_id},
from_uin => $m->{value}{from_uin},
to_uin => $m->{value}{to_uin},
msg_time => $m->{value}{'time'},
content => $m->{value}{content},
service_type=> $m->{value}{service_type},
ruin => $m->{value}{ruin},
msg_class => "recv",
ttl => 5,
allow_plugin => 1,
};
#service_type =0 表示群临时消息,1 表示讨论组临时消息
if($m->{value}{service_type} == 0){
$msg->{gid} = $m->{value}{id};
$msg->{group_code} = $client->get_group_code_from_gid($m->{value}{id}),
$msg->{via} = 'group';
}
elsif($m->{value}{service_type} == 1){
$msg->{did} = $m->{value}{id};
$msg->{via} = 'discuss';
}
else{return}
$client->msg_put($msg);
}
#收到的消息是普通消息
elsif($m->{poll_type} eq 'message'){
my $msg = {
type => 'message',
msg_id => $m->{value}{msg_id},
from_uin => $m->{value}{from_uin},
to_uin => $m->{value}{to_uin},
msg_time => $m->{value}{'time'},
content => $m->{value}{content},
msg_class => "recv",
ttl => 5,
allow_plugin => 1,
};
$client->msg_put($msg);
}
#收到的消息是群消息
elsif($m->{poll_type} eq 'group_message'){
my $msg = {
type => 'group_message',
msg_id => $m->{value}{msg_id},
from_uin => $m->{value}{from_uin},
to_uin => $m->{value}{to_uin},
msg_time => $m->{value}{'time'},
content => $m->{value}{content},
send_uin => $m->{value}{send_uin},
group_code => $m->{value}{group_code},
msg_class => "recv",
ttl => 5,
allow_plugin => 1,
};
$client->msg_put($msg);
}
#收到讨论组消息
elsif($m->{poll_type} eq 'discu_message'){
my $msg = {
type => 'discuss_message',
did => $m->{value}{did},
from_uin => $m->{value}{from_uin},
msg_id => $m->{value}{msg_id},
send_uin => $m->{value}{send_uin},
msg_time => $m->{value}{'time'},
to_uin => $m->{value}{'to_uin'},
content => $m->{value}{content},
msg_class => "recv",
ttl => 5,
allow_plugin => 1,
};
$client->msg_put($msg);
}
elsif($m->{poll_type} eq 'buddies_status_change'){
my $msg = {
type => 'buddies_status_change',
uin => $m->{value}{uin},
state => $m->{value}{status},
client_type => code2client($m->{value}{client_type}),
};
$client->msg_put($msg);
}
#收到系统消息
elsif($m->{poll_type} eq 'sys_g_msg'){
#my $msg = {
# type => 'sys_g_msg',
# msg_id => $m->{value}{msg_id},
# from_uin => $m->{value}{from_uin},
# to_uin => $m->{value}{to_uin},
#
#};
#$client->msg_put($msg);
}
#收到强制下线消息
elsif($m->{poll_type} eq 'kick_message'){
if($m->{value}{show_reason} ==1){
my $reason = encode("utf8",$m->{value}{reason});
console "$reason\n";
$client->stop();
}
else {console "您已被迫下线\n";$client->stop(); }
}
#还未识别和处理的消息
else{
}
}
}
#可以忽略的消息,暂时不做任何处理
elsif($json->{retcode} == 102 or $json->{retcode} == 109 or $json->{retcode} == 110 ){
$client->{poll_failure_count} = 0;
}
#更新客户端ptwebqq值
elsif($json->{retcode} == 116){
$client->{qq_param}{ptwebqq} = $json->{p};
$client->{cookie_jar}->set_cookie(0,"ptwebqq",$json->{p},"/","qq.com",);
}
#未重新登录
elsif($json->{retcode} ==100){
console "因网络或其他原因与服务器失去联系,客户端需要重新登录...\n";
$client->relogin();
}
#重新连接失败
elsif($json->{retcode} ==120 or $json->{retcode} ==121 ){
console "因网络或其他原因与服务器失去联系,客户端需要重新连接...\n";
$client->_relink();
}
#其他未知消息
else{
$client->{poll_failure_count}++;
console "获取消息失败,当前失败次数: $client->{poll_failure_count}\n";
if($client->{poll_failure_count} > $client->{poll_failure_count_max}){
console "接收消息失败次数超过最大值,尝试进行重新连接...\n";
$client->{poll_failure_count} = 0;
$client->_relink();
}
}
}
}
1;