NAME
Mojo::Webqq - A Webqq Client Framework base on Mojolicious
SYNOPSIS
use Mojo::Webqq;
use Mojo::Util qw(md5_sum);
#初始化一个客户端对象
my $client=Mojo::Webqq->new(ua_debug=>0);
my $qq = 12345678;
my $pwd = "your password";
my $pwd_md5 = md5_sum($pwd);#得到原始密码的32位长度md5
#客户端进行登录
$client->login(qq=>$qq,pwd=>$pwd_md5);
#客户端加载ShowMsg插件,用于打印发送和接收的消息到终端
$client->load("ShowMsg");
#设置接收消息事件的回调函数,在回调函数中对消息以相同内容进行回复
$client->on(receive_message=>sub{
my ($client,$msg)=@_;
#已以相同内容回复接收到的消息
$client->reply_message($msg,$msg->content);
#你也可以使用$msg->dump() 来打印消息结构
});
#客户端开始运行
$client->run();
DESCRIPTION
通过该项目,你可以完成基本的登录、接收和发送消息,在此基础上你可以通过插件的形式实现更多附加功能,比如:
群管理、聊天记录统计、QQ消息报警、QQ机器人、在QQ群中执行Perl代码,查询Perldoc文档、消息转发、QQ和IRC联通等等
原先的Webqq::Client模块采用的是LWP+AnyEvent实现阻塞和非阻塞请求
代码比较混乱,存在诸多bug,易用性和可扩展性较差
此项目是Webqq::Client模块的重构,基于Mojolicious框架,具体更多良好特性,比如:
基于Mojo::Base更好的对象模型、基于Mojo::EventEmitter灵活的事件管理机制、
基于Mojo::UserAgent统一的阻塞和非阻塞HTTP请求、基于Mojo::Log轻量级的日志记录框架 等等
推荐你在使用本模块的同时也更多的了解Mojolicious
数据类型
数据类型包含 个人、好友、群组、讨论组、最近联系人 几大类型,每种类型都专门设计了类
所有的类都继承Mojo::Webqq::Base具有dump的方法,这些数据类型均由系统自动创建和维护,一般情况下,你只需要对对象进行只读操作
Mojo::Webqq::User 个人
属性:
birthday #生日
phone #电话
occupation #职业
college #大学
qq #qq帐号
id #本次登录唯一标识,发送消息时需要用到
blood #血型
constel #星座
homepage #主页
state #在线状态 online|away|busy|callme|silent|hidden
country #国家
city #城市
nick #昵称
displayname #当前和昵称完全一样
shengxiao #生效
email #邮箱
client_type #固定值"web"
province #省份
gender #性别
mobile #手机
signature #个性签名
实际想要获取该对象信息时,是通过客户端提供的 user 属性来获取
$client->user #返回的是一个Mojo::Webqq::User对象
$client->user->nick #获取帐号昵称
$client->user->id #获取帐号id
Mojo::Webqq::Friend 好友
属性:
qq #好友的qq号码,当前获取qq号第一次都需要实时调用api获取,效率较低,应避免使用
id #好友的id,仅在本次登录期间唯一,多次登录可能会发生变化
categorie #好友所属的分组
nick #好友昵称
markname #好友备注名称
displayname #如果设置了markname 就返回markname 否则返回nick
is_vip #是否是vip会员
vip_level #vip等级
state #好友状况 online|away|busy|silent|offline
client_type #好友客户端类型 pc|mobile|iphone|unknown
方法:
send #给好友对象发送消息
代码示例:
my $friend = $client->search_friend(id=>xxx);
print "好友昵称为: " ,$friend->nick,"\n";
$friend->dump();
$friend->send("hello world"); #比$client->send_message($friend,"hello world")更简洁
#遍历好友
$client->each_friend(sub{
my($client,$friend) = @_;
...;
});
for my $friend ($client->friends){
...;
}
Mojo::Webqq::Group 群组
属性:
gid #群的id,仅在本次登录期间唯一,多次登录可能会发生变化
gtype #create|attend|manage 分别表示创建的群|加入的群|管理的群,需要安装Webqq::Qun模块,暂未实现
gnumber #群号码 需要安装Webqq::Qun模块,暂未实现
gname #群名称
gmemo #群说明
gcreatetime #群创建时间 需要安装Webqq::Qun模块,暂未实现
glevel #群等级
gowner #群拥有者的id
gmarkname #群备注名称
displayname #如果设置了gmarkname 就返回gmarkname 否则返回gname
member #群成员,此属性是一个数组引用,数组中每个元素是一个Mojo::Webqq::Group::Member的对象
方法:
search_group_member #根据群成员属性搜索群,标量上下文返回第一个群成员对象,列表上下文返回全部,搜索失败返回undef
each_group_member #在群中遍历所有的群成员
members #返回群成员对象列表
send #给群对象发送消息 $group->send("hello world")
示例代码:
my $group = $client->search_group_member(gname=>"PERL学习交流群"); #返回一个Mojo::Webqq::Group对象
$group->search_group_member(id=>xxx,nick=>xxx); #返回一个Mojo::Webqq::Group::Member对象
$group->each_group_member(
my($client,$member) = @_;
...;
);
for my $group ($client->groups){
...;
}
Mojo::Webqq::Group::Member 群成员
属性:
nick #群成员昵称
province #省份
gender #性别
id #唯一id,仅在本次登录期间唯一,多次登录可能会发生变化
country #国家
city #城市
card #群名片
displayname #设置了card就返回card否则返回nick
state #状态
client_type #客户端类型
qq #qq号码,当前获取qq号第一次都需要实时调用api获取,效率较低,应避免使用
qage #q龄 需要安装Webqq::Qun模块,暂未实现
join_time #入群时间
last_speak_time #最后发言时间
level ##等级 灌水|传说|潜水 等, 需要安装Webqq::Qun模块,暂未实现
role #角色 admin|owner|member, 需要安装Webqq::Qun模块,暂未实现
bad_record #是否包含不良记录 0|1,需要安装Webqq::Qun模块,暂未实现
gid #所在群的id
gtype #所在群的类型, 需要安装Webqq::Qun模块,暂未实现
gnumber #所在群的群号码, 需要安装Webqq::Qun模块,暂未实现
gname #所在群的名称
gmemo #所在群的说明
gcreatetime #所在群的创建时间
glevel #所在群的等级
gowner #所在群的拥有者id
gmarkname #所在群的备注名称
方法:
send #给群成员对象发送消息 $member->send("hello world")
Mojo::Webqq::Discuss 讨论组
属性:
did #讨论组id
dname #讨论组名称
displayname #当前返回的和dname一样
downer #讨论组建立者id
member #讨论组成员,一个数组引用,每个元素是一个Mojo::Webqq::Discuss::Member对象
方法:
search_discuss_member #搜索讨论组成员
each_discuss_member #遍历讨论组成员
members #返回讨论组成员对象列表
send #给讨论组对象发送消息
Mojo::Webqq::Discuss::Member 讨论组成员
属性:
nick
id
qq
ruin
state
client_type
dname
displayname #当前和dname一样
did
downer
方法:
send #给讨论组成员发送临时消息
Mojo::Webqq::Recent::Friend
属性:
id
type #friend
Mojo::Webqq::Recent::Group
属性:
gid
type #group
Mojo::Webqq::Recent::Discuss
属性:
did
type #discuss
消息类型
消息类型主要包含好友消息、群消息、讨论组消息、群临时消息、讨论组临时消息
所有的消息类都继承Mojo::Webqq::Message::Base,具备dump方法
消息的对象均由系统创建和维护,你只需要对消息对象进行只读操作即可
Mojo::Webqq::Message::Recv::Message 接收到的好友消息
属性:
type #常量"message"
msg_class #常量"recv"
msg_from #消息的来源,比如来自某个插件,可自由定义,默认"none"
ttl #默认值5 当ttl减为0会被消息队列丢弃
allow_plugin #默认1
msg_id #消息id
sender_id #发送者id
receiver_id #接收者id
sender #发送者对象,Mojo::Webqq::Friend
receiver #接收者对象,Mojo::Webqq::User
msg_time #消息发送时间
content #消息内容
raw_content #消息原始内容 一个数组引用
方法:
to_json #将消息转换为json形式的字符串,方便和外部系统交互
#常见的接收和发送消息都支持该方法,下文不再赘述
示例代码:
$msg->sender->nick; #从接收到的好友消息中 获取好友的昵称 更多好友相关的属性,参考Mojo::Webqq::Friend
$msg->to_json();
$msg->dump();
Mojo::Webqq::Message::Send::Message 发送的好友消息
属性:
type #常量"message"
msg_class #常量"send"
msg_from #消息的来源,比如来自某个插件,可自由定义,默认"none"
ttl #默认值5 当ttl减为0会被消息队列丢弃
allow_plugin #默认1
msg_id #消息id
sender_id #发送者id
receiver_id #接收者id
sender #发送者对象,Mojo::Webqq::User
receiver #接收者对象,Mojo::Webqq::Friend
msg_time #消息发送时间
content #消息内容
raw_content #消息原始内容 一个数组引用
和接收的好友消息具有类似的属性,不同之处在于
msg_class 是常量"send",你可以通过每个消息的msg_class属性来判断消息是发送消息还是接收消息
对于好友消息,sender是自己(Mojo::Webqq::User) receiver是好友(Mojo::Webqq::Friend)
对于群消息,sender是群中的自己(Mojo::Webqq::Group::Member) receiver是群成员(Mojo::Webqq::Group::Member)
对于讨论组消息,sender是讨论组中自己(Mojo::Webqq::Discuss::Member) receiver是讨论组成员(Mojo::Webqq::Discuss::Member)
对于临时消息,sender是讨论组或群中的自己 receiver是讨论组或群中的成员
其他消息类型也都存在类似的情况,不再赘述
Mojo::Webqq::Message::Recv::GroupMessage 接收到的群消息
属性:
type #常量"group_message"
msg_class #常量"recv"
msg_from #消息的来源,比如来自某个插件,可自由定义,默认"none"
ttl #默认值5 当ttl减为0会被消息队列丢弃
allow_plugin #默认1
msg_id #消息id
group_id #群gid
sender_id #发送消息的群成员id
receiver_id #接收者id,也就是自己的id
sender #发送消息的群成员对象 Mojo::Webqq::Group::Member
receiver #群中的自己 Mojo::Webqq::Group::Member对象
group #群对象 Mojo::Webqq::Group
msg_time #消息时间
content
raw_content
Mojo::Webqq::Message::Send::GroupMessage 发送的群消息
Mojo::Webqq::Message::Recv::DiscussMessage 接收到的讨论组消息
属性:
type #常量"discuss_message"
msg_class #常量"recv"
msg_from #消息的来源,比如来自某个插件,可自由定义,默认"none"
ttl #默认值5 当ttl减为0会被消息队列丢弃
allow_plugin #默认1
msg_id #消息id
discuss_id #讨论组did
sender_id #发送消息的讨论组成员id
receiver_id #自己的id
sender #发送消息的讨论组成员对象 Mojo::Webqq::Discuss::Member
receiver #讨论组中的自己 Mojo::Webqq::Group::Member对象
discuss #群对象 Mojo::Webqq::Discuss
msg_time #消息时间
content
raw_content
Mojo::Webqq::Message::Send::DiscussMessage 发送的讨论组消息
Mojo::Webqq::Message::Recv::SessMessage 接收到的临时消息
属性:
type #常量"sess_message"
msg_class #常量"recv"
msg_from #消息的来源,比如来自某个插件,可自由定义,默认"none"
ttl #默认值5 当ttl减为0会被消息队列丢弃
allow_plugin #默认1
msg_id #消息id
group_id #群gid
discuss_id #讨论组did
sender_id #发送消息的讨论组成员id或群成员id
receiver_id #Mojo::Webqq::User对象
sender #发送消息的讨论组成员对象或群成员对象
group #群对象 Mojo::Webqq::Group
discuss #讨论组对象 Mojo::Webqq::Discuss
receiver #群成员对象或者讨论组成员对象
msg_time #消息时间
content
raw_content
via #如果是群临时消息 则是"group" 如果是讨论组临时消息则是"discuss"
Mojo::Webqq::Message::Send::SessMessage 发送的临时消息
Mojo::Webqq::Message::Send::Status 发送消息状态
属性:
code #0 表示成功 非零表示失败
msg #"发送成功"|"发送失败"
方法:
is_success
该消息结构主要用于判断发送消息是否成功
客户端属性
$client->security #是否开启安全加密
$client->state #登录状态
$client->type #类型 固定值 smartqq,Mojo::Webqq只支持smartqq
$client->ua_debug #http请求是否打印debug信息
$client->log_level #日志记录等级 默认info
$client->log_path #日志记录路径,默认undef,打印到STDERR
$client->version #客户端版本
$client->qq #登录帐号
$client->pwd #密码32位md5值
$client->encrypt_method #perl|js 登录加密算法,系统会会自动选择
$client->user; #获取Mojo::Webqq::User对象
$client->group; #返回客户端存储所有群组的数组引用,一般情况下你不应该直接操作该数据
$client->discuss; #返回客户端存储所有讨论组的数组引用,一般情况下你不应该直接操作该数据
$client->recent; #返回客户端存储最近联系人列表的数组引用,一般情况下你不应该直接操作该数据
$client->plugins; #存储客户端已经加载插件,一般情况下你不应该直接操作该数据
#获取登录帐号相关信息
$client->user->id; #获取登录用户id
$client->user->nick; #获取登录用户昵称
客户端方法
new()
security #设置该参数,将使得发送和接收消息使用https加密
state #设置登录状态,默认是online,支持online|away|busy|silent|hidden|offline
ua_debug #设置该参数,打印调试信息
log_level #默认级别为info,可以设置debug|info|warn|error|fatal
log_path #默认客户端输出内容打印到STDERR 设置该参数可以将客户端输出重定向到文件
encrypt_method #perl|js 系统会自动判断,一般情况下你不需要自己设置登录加密算法
ioloop #客户端使用的事件驱动,默认是Mojo::IOLoop->singleton 如果你不清楚这是什么可以不用理会
friend_pic_dir #设置好友图片保留目录,默认是临时目录,只有注册了receive_friend_pic事件 此路径才有意义
$client->new(security=>0,ua_debug=>0);
on()
基于Mojo::EventEmitter的事件注册方法,可支持同时设置多个事件回调
$client->on("event1"=>sub{...},"event2"=>sub{...},);
参考下文客户端支持的事件
login()
客户端登录
$client->login(qq=>xxxx,pwd=>xxxx,[delay=>0|1]);
qq 登录的qq帐号
pwd 必须是原始密码经过md5加密后的32位字符串
delay 0表示不延迟,立即登录 1表示在after_load_plugin事件触发后登录,即全部插件加载完毕后再执行登录
为了兼容旧版本,delay默认值为0,推荐尽量将delay设置为1
delay选项主要是为了方便配合某些插件的执行, 比如 Mojo::Webqq::Plugin::PostImgVerifycode 插件
注意:推荐安装Crypt::RSA 或者 Crypt::OpenSSL::RSA + Crypt::OpenSSL::Bignum模块,能够极大的提升登录加密算法的计算速度(Centos可以使用 yum -y install perl-Crypt-OpenSSL-* 来方便安装)
relogin()
重新登录,客户端默认会自动尝试重新登录
$client->relogin()
timer($seconds,$callback)
指定多少秒之后执行对应的回调函数
$client->timer(10,sub{ print "hello world\n" }); #10s后打印hello world
interval($seconds,$callback)
设置每隔多少秒重复执行对应的回调函数
$client->interval(10,sub{ print "hello world\n" }); #每隔10s后打印hello world
run()
客户端进入事件循环,正式开始运行,一般放在代码的最后,不可或缺
$client->run();
multi_run()
登录多个qq帐号时使用,详情参见ready()方法介绍
ready()
当你想要同时运行多个qq帐号或者当你想要自己基于事件驱动的代码(往往也是Mojo的代码)中调用该模块时
$client->run()会阻塞你的代码,导致无法同时使用多个帐号或使你其他的代码无法执行
这种情况下,你应该放弃使用$client->run() 而是使用自己的事件驱动来驱动Mojo::Webqq,你会需要ready()方法
$other_ioloop = Mojo::IOLoop->new;
$webqq_client = Mojo::Webqq->new(ioloop=>$other_ioloop);
$webqq_client->ready(); #不是 $webqq_client->run();
$other_ioloop->start; #启动你自己的事件循环
#同时登录多个qq帐号
my $client1 = Mojo::Webqq->new;
my $client2 = Mojo::Webqq->new;
$client1->login(qq=>xxx,pass=>xxx);
$client2->login(qq=>xxx,pass=>xxx);
$client1->ready;
$client2->ready;
Mojo::IOLoop->multi_run();
stop(["auto"|"noexit"])
客户端终止接收和发送消息,其他非接收和发送消息类的功能仍然可以继续执行
$client->stop(["auto"]) #默认参数是auto,当多个qq帐号客户端中的最后一个stop时,整个程序也会退出(exit)
$client->stop("noexit") #即使所有的qq帐号都stop了,整个客户端也不调用exit退出
add_job($job_name,$time,$callback);
定时执行任务
#支持的时间格式为 HH:MM
$client->add_job("定时提醒","07:00",sub{$client->send_message($friend,"起床啦");});
spawn(%opt)
在单独的进程中执行代码或命令
客户端采用的是单进程异步事件驱动模式,如果在代码中执行system/exec等来调用其他命令
或者执行某些阻塞的代码,比如sleep等 均会导致客户端主进程被阻塞而影响正常接收和发送消息
这种情况下,可以采用客户端提供的spawn方法,将阻塞的代码放置到单独的进程中执行,捕获进程的标准输出和标准错误
在回调函数中获取到进程执行的结果
该方法实际上参考Mojo::Run模块 并在该模块的基础上做了进一步完善
#支持的参数:
max_forks #产生的最大进程数
cmd #要执行的命令或代码
param #命令的参数
exec_timeout #命令或代码的执行超时时间
stdout_cb #命令或代码执行过程中 STDOUT 一旦有数据则会触发此回调
stderr_cb #命令或代码执行过程中 STDERR 一旦有数据则会触发此回调
exit_cb #命令或代码执行结束的回调
代码示例:
$client->spawn(
cmd => sub {print "hello world";return "ok"},
exec_timeout => 3,
exit_cb => sub{
my($pid,$hash) = @_;
#$pid 是执行程序的进程号
#$hash是一个执行结果的hash引用,结构如下:
#{
# 'cmd' => 'CODE', #执行的命令或代码
# 'time_stopped' => '1441011558.30559', #进程停止时间
# 'time_started' => '1441011557.30242', #进程开始时间
# 'time_duration_total' => '1.00345897674561', #进程执行总时间
# 'time_duration_exec' => '1.00317192077637', #进程执行时长
# 'is_timeout' => undef, #是否是超时退出
# 'exit_status' => 1, #进程退出返回值
# 'exit_core' => 0, #是否有core
# 'exit_signal' => 0, #进程退出信号
# 'param' => undef, #命令或代码执行参数
# 'stderr' => '', #进程的标准错误输出结果
# 'stdout' => 'hello world', #进程的标准输出结果
# 'result' => [ #代码的返回值
# 'ok'
# ]
#}
},
);
$client->spawn(
cmd => "ping www.qq.com", #或者写成 ['ping','www.qq.com']
exec_timeout => 3,
stdout_cb => sub{
my($pid,$chunk) = @_;
print "从标准输出中实时收到数据:",$chunk,"\n";
},
stderr_cb => sub {
my($pid,$chunk) = @_;
print "从标准错误中实时收到数据:",$chunk,"\n";
},
exit_cb => sub{
my($pid,$res) = @_;
print "从标准输出中接收的全部数据:",$res->{stdout},"\n";
print "从标准错误中接收的全部数据:",$res->{stderr},"\n";
}
);
mail(%opt,[$callback]) 非阻塞发送邮件
该方法实际上是Mojo::SMTP::Client的封装,使用该方法之前请确保你已经安装了Mojo::SMTP::Client模块
发送邮件需要设置的参数:
smtp #smtp服务器地址,例如smtp.163.com
port #smtp服务器端口,默认25
tls #0|1 是否使用tls,默认为 0
tls_ca #tls证书路径
tls_cert #tls公钥路径
tls_key #tls密钥路径
user #smtp帐号
pass #smtp密码
from #发送邮箱
to #接收邮箱
cc #抄送邮箱
subject #主题 utf8编码
html #邮件正文内容,html格式 utf8编码
text #邮件正文内容,纯文本格式 utf8编码
data #设置该选项表示使用MIME::Lite生成的发送数据
$client->mail(smtp=>smtp.163.com,user=>xxx,pass=>xxx,from=>xxx,to=>xxx,subject=>"邮件测试",text=>"hello world",sub{
my ($send_status,$err) = @_;
if($send_status){print "发送成功"}
else{print "发送失败"}
});
update_friend()
更新全部好友信息,客户端会自动调用该方法,通常你不需要主动使用
update_group([$group])
更新群消息,如果设置了$group(Mojo::Webqq::Group对象) 则更新指定群消息,不设置参数更新全部群信息
客户端会自动调用该方法,通常你不需要主动使用
update_discuss([$discuss])
更新讨论组消息,如果设置了$discuss(Mojo::Webqq::Disucss对象) 则更新指定讨论组消息,不设置参数更新全部讨论组信息
客户端会自动调用该方法,通常你不需要主动使用
update_recent()
更新最近联系人信息,由于较少使用到,客户端仅在首次登录时调用该方法,更新一次
each_friend(sub{...})
遍历所有的好友
$client->each_friend(sub{
my($client,$friend) = @_; #$friend是一个Mojo::Webqq::Friend对象
...;
});
friends()
返回好友对象数组,数组中的每个元素是一个Mojo::Webqq::Friend对象
my @friends = $client->friends();
each_group(sub{...})
遍历所有的群
$client->each_group(sub{
my($client,$group) = @_; #$group是一个Mojo::Webqq::Group对象
...;
});
groups()
返回群对象数组,数组中的每个元素是一个Mojo::Webqq::Group对象
my @groups = $client->groups();
each_group_member(sub{...})
遍历所有群中的所有群成员
$client->each_group_member(sub{
my($client,$member) = @_; #$member是一个Mojo::Webqq::Group::Member对象
...;
});
注意,想要遍历某个群中的所有群成员可以使用群对象中的遍历方法:
$group->each_group_member(sub{
my($client,$member)=@_;
...;
});
each_discuss(sub{...})
遍历所有的讨论组,和遍历好友或群类似 不再赘述
discusss()
返回讨论组对象数组,数组中的每个元素是一个Mojo::Webqq::Discuss对象
my @discusss = $client->discusss;
each_discuss_member(sub{...})
遍历所有讨论组中的所有讨论组成员,和遍历好友或群类似 不再赘述
each_recent(sub{...})
遍历最近联系人列表
recents()
返回最近联系人对象数组,数组中的每个元素是一个Mojo::Webqq::Recent对象
my @recents = $client->recents();
send_message($friend,$content,[$callback])
发送好友消息
my $friend = $client->search_friend(id=>xxx);#$friend是一个Mojo::Webqq::Friend的对象
$client->send_message($friend,"hello world");#发送内容必须是utf8编码
在callback中对发送的消息进行预处理
$client->send_message($friend,"hello world",sub{
my ($client,$msg) = @_;
#对即将发送的消息进行预处理
$msg->msg_from("自定义的来源标识");
#设置该条消息发送完毕后的回调函数
$msg->cb(sub{
my($client,$msg,$status)=@_;
print $msg->msg_id . "发送失败\n" if not $status->is_success;
});
});
send_group_message($group,$content,[$callback])
发送群消息
my $group = $client->search_group(gname=>"PERL学习交流");#$group是一个Mojo::Webqq::Group的对象
$client->send_group_message($group,"hello world");
send_discuss_message($discuss,$content,[$callback])
发送讨论组消息
my $discuss = $client->search_discuss(dname=>"讨论组");#$discuss是一个Mojo::Webqq::Discuss的对象
$client->send_discuss_message($discuss,"hello world");
注意:由于webqq自身限制,当前无法成功发送讨论组消息
send_sess_message($group_member|$discuss_member,$content,[$callback])
发送群临时消息或者讨论组临时消息
#直接在整个数据库中搜索指定群成员
my $group_member = $client->search_group_member(gname=>"PERL学习交流",nick=>"小灰");
#$group_member是一个Mojo::Webqq::Group::Member的对象
$client->send_sess_message($group_member,"hello world");
#先找到群,再使用群搜索到群成员,比上述的全局搜索方法更高效一些
my $group = $client->search_group(gname=>"PERL学习交流");
my $member = $group->search_group_member(nick=>"小灰");
$client->send_sess_message($member,"hello world");
my $discuss_member = $client->search_discuss_member(dname=>"讨论组",nick=>"小灰");
#$discuss_member是一个Mojo::Webqq::Discuss::Member的对象
$client->send_sess_message($discuss_member,"hello world");
reply_message($msg,$content,[$callback])
对收到的消息进行回复,send_message()/send_group_message()/send_discuss_message()/send_sess_message()
这一类方法适合对特定的对象主动发送消息,但很多场景下,我们不需要关心对方是谁
只需要对接收到的消息进行回复,使用reply_message会比较方便
$client->reply_message($msg,$content);
http_get http阻塞或非阻塞get请求
该方法为Mojo::UserAgent的get方法的封装,调用方式基本和Mojo::UserAgent->get相同,但也存在细微差别
阻塞http请求:
#标量上下文 返回http请求内容,若请求失败,返回内容为undef
my $http_body = $client->http_get($url,$header);
#列表上下文,返回http请求内容以及$ua,$tx
my ($http_body,$ua,$tx) = $client->http_get($url,$header);
#可以在http header设置一些请求相关的选项,比如:
#json=>1 表示将响应的json数据进行json decode得到perl的hash引用
#retry_times=>3 表示请求失败自动重试次数,默认使用$client->ua_retry_times的值
my $json_decode_hash = $client->http_get($url,{json=>1,retry_times=>3,Host=>"www.qq.com"});
#http post请求
$client->http_post($url,$header,form=>$post_data);
非阻塞http请求:
$client->http_get($url,$header,sub{
my($http_body,$ua,$tx) = @_;
#请求失败 $http_body 返回undef
});
注意:由于采用事件驱动,因此,你应该始终选择使用非阻塞的http请求模式,如果采用阻塞的http请求,在http请求完成之前
整个程序都是被阻塞的,无法做其他任何事(包括接收和发送消息等)
http_post http阻塞或非阻塞post请求
和 http_get 方法类似,不再赘述
客户端事件
常见的事件发生时,比如接收到消息,有人加入群中等等,客户端会设置对应的事件名称,并在事件完成时进行触发
你可以使用$client->on(event=>sub{xxx})的方式对你感兴趣的事件注册回调函数,回调函数的第一个参数永远是客户端对象本身
注意:
new_friend/new_group/new_discuss/new_group_member/new_discuss_member
lose_friend/lose_group/lose_discuss/lose_group_member/lose_discuss_member
这类事件采用的是较为特殊的处理方式,比如新增群成员时,如果你的帐号是管理员权限,你会收到相关的通知
但如果是非管理员权限,则完全不会收到任何通知提醒,以新增群成员为例,为了统一实现功能,采用两种机制:
1)客户端定期更新好友、群组、讨论组数据,和原始数据对比来发现新增或者丢失的成员,当前定期更新频率为10分钟
2)当群成员在群里发言时,客户端会马上在数据库中搜索相关群成员,如果搜索不到则判断为新增群成员,马上更新数据库,并触发事件
因此,你可能会发现当群成员加入后,过了10分钟才会触发相关的事件,或者新增群成员一旦发言也会发送触发事件
当前核心事件的触发顺序(采用延迟登录)
+->before_send_message
plugin_load -+ |->receive_message
|->after_load_plugin->input_img_verifycode->login->ready->run->|->send_message
plugin_call -+ | |->new_friend
| |->receive_friend_pic
relogin<-login<-+ |->lose_friend
...
reveive_message
接收到消息事件,传递给回调函数的参数是接收到的消息对象
$client->on(receive_message=>sub{my ($client,$msg)=@_;$msg->dump});
send_message
发送消息事件,传递给回调函数的参数是 接收到的消息对象 和 发送状态对象
发送状态对象是Mojo::Webqq::Message::Send::Status的实例
$client->on(send_message=>sub{
my ($client,$msg,$status)=@_;
#$status是一个Mojo::Webqq::Message::Send::Status的对象,主要用于判断消息是否发送成功
print $msg->msg_id,"发送成功" if $status->is_success
});
login
登录完成时触发事件,无额外的回调参数
relogin
重新登录完成时触发事件,无额外的回调参数
input_img_verifycode
登录需要输入验证码,参数为验证码文件的路径
ready
客户端准备就绪事件
run
客户端一切准备就绪,开始进入事件循环之前
plugin_call
插件被执行时,参数为插件名称
plugin_load
插件被加载完成时,参数为插件名称
after_load_plugin
全部插件加载完成后触发的事件
new_friend
新增好友时触发事件,回调参数为新增的好友对象
$client->on(new_friend=>sub{my ($client,$friend)=@_});
lose_friend
失去好友时触发事件,回调参数为失去的好友对象
$client->on(lose_friend=>sub{my ($client,$friend)=@_});
new_group
新加入群时触发事件,回调参数为新增群的对象
$client->on(new_group=>sub{my ($client,$group)=@_});
lose_group
退出群事件
$client->on(lose_group=>sub{my ($client,$group)=@_});
new_group_member
新增群成员事件
$client->on(new_group_member=>sub{my ($client,$group_member)=@_});
lose_group_member
群成员退群事件
$client->on(lose_group_member=>sub{my ($client,$group_member)=@_});
new_discuss
新加入讨论组事件
$client->on(new_discuss=>sub{my ($client,$discuss)=@_});
lose_discuss
退出讨论组事件
$client->on(lose_discuss=>sub{my ($client,$discuss)=@_});
new_discuss_member
讨论组新增成员事件
$client->on(new_discuss_member=>sub{my ($client,$discuss_member)=@_});
lose_discuss_member
讨论组成员退出事件
$client->on(lose_discuss_member=>sub{my ($client,$discuss_member)=@_});
receive_friend_pic
接收到好友发送的图片,图片默认下载到临时目录下,在回调执行结束后会自动删除
由于腾讯smartqq本身的限制,只能接收好友图片,不支持接收群图片
$client->on(receive_friend_pic=>sub{
my($client,$fh,$filepath,$friend)=@_;
#$fh 图片文件的只读句柄
#$filepath 图片文件的绝对路径
#$friend 发送图片的好友对象
});
first_talk
第一次接收到某人消息事件,第一次的定义为:收到某个用户在4小时内第一次发送的消息
$client->on(first_talk=>sub{
my($client,$sender,$msg) = @_;
#$sender 发送消息的好友 或者 群成员 或者 讨论组成员
#$msg 接收到的消息
$sender->send("怎么这么久才想起我?");
});
before_send_message
消息发送之前的事件,一般用于在即将发送消息前对消息进行预处理
$client->on(before_send_message=>sub{
my($client,$msg) = @_;
my $content = $msg->content;
$content .= "我是可爱的小尾巴";
$msg->content($content);
});
关于插件
load
加载一个或者多个插件,多个插件使用数组引用,支持的插件参数包括:
priority #可选,设置插件优先级,默认是0,较高的优先级能够使得插件优先执行
auto_call #可选,设置是否加载完成后自动执行,默认为1
data #可选,设置加载插件时可以携带的数据,将会在call的时候传递给插件本身
$client->load(["plugin1","plugin2"],data=>[1,2,3,]);
$client->load("plugin",priority=>0,auto_call=>1);
加载插件时,可以通过auto_call设置是否自动执行(默认在run的时候会执行),priority可以设置插件执行的优先级
数字越大,优先级越高,插件会被优先执行
call
手动执行一个插件、适合auto_call=>0的插件的手动执行模式,当auto_call=>1时,会自动执行call
$client->call("plugin",[可选参数]);
客户端实现了一个简单的插件管理机制,插件是一个简单的call函数,包名默认是Mojo:Webqq::Plugin::
比如,我编写一个简单的hello world插件,效果是对接收到的任意消息回复一个"hello world"
编写一个包 Mojo:Webqq::Plugin::HelloWorld
package Mojo:Webqq::Plugin::HelloWorld;
$Mojo::Webqq::Plugin::HelloWorld::PRIORITY = 10; #可省略,除了在load中使用priority设置优先级,也可以通过包变量设置
$Mojo::Webqq::Plugin::HelloWorld::AUTO_CALL = 1; #可省略,通过包变量设置插件是否默认加载后立刻执行
sub call{
my $client = shift;
my $data = shift; #可能包含的data数据
$client->on(receive_message=>sub{
my($client,$msg)=@_;
$client->reply_message($msg,"hello world");
});
}
1;
客户端加载和执行插件的操作:
#如果你的插件并非Mojo::Webqq::Plugin::相对命名规则,则可以在名称前使用"+"表示插件绝对名称
$client->load("HelloWorld",auto_call=>1);
$client->run();
当客户端运行时,插件将会被加载并自动执行,收到消息时会自动回复hello world
注意:
当多个消息处理类的插件对同一个消息进行处理时,往往存在冲突的情况
比如一个插件对消息处理完并不希望其他插件再继续处理该消息(默认情况下,receive_message事件会广播给所有订阅该事件的回调)
这种情况下,可以通过设置不同的插件优先级,使得事件被触发时,优先级较高的插件获得优先执行
执行完成后,再通过设置$msg->allow_plugin(0) 来禁止其他插件继续处理该消息,每个消息都带有一个allow_plugin的属性
这是一种建议性的插件协议,并非强制遵守
除此之外,也可以采用插件的手动执行模式,自己根据需要来执行插件
插件列表
Mojo::Webqq::Plugin::StockInfo
查询股票信息,聊天内容中输入 "gp 000001" 或者 "股票 000001" 触发
目前仅支持上海和深圳6位数字股票代码
Mojo::Webqq::Plugin::MsgSync
实现QQ群和IRC的联通,彼此的消息自动同步
欢迎加入irc.perfi.wang的#ChinaPerl,该频道已经和[PERL学习交流]群联通
欢迎加入irc.perfi.wang的#Mojolicious,该频道已经和[Mojolicious]群联通
$client->load("MsgSync",data=>{
irc=>{nick=>"xxxx",user=>"xxxx",pass=>"xxxx",server=>"irc.perfi.wang"},
pairs=>[
["#ChinaPerl",$m->search_group(gname=>"PERL学习交流")],
["#Mojolicious",$m->search_group(gname=>"Mojolicious")],
]
});
Mojo::Webqq::Plugin::IRCShell
将qq协议转换成irc协议,启动一个本地的irc服务器,使用任意irc客户端(irc user设置为qq号)连接后即可以按照irc的方式使用qq
$client->load("IRCShell",data=>{host=>"127.0.0.1",port=>6667,});#开启本地的irc server
#支持的参数包括:
host #默认0.0.0.0
port #默认6667
master_irc_user #和qq匹配的irc user帐号,默认按照和qq号相同的user或者客户端ip是本机地址作为识别规则
load_friend #0|1 默认是1 是否初始为每个好友生成irc虚拟帐号并加入频道 #我的好友
qq好友会默认加入到irc的 '#我的好友' 频道中
每个qq群也会在irc上创建对应的频道,比如qq群[PERL学习交流]对应的irc频道为'#PERL学习交流'
使用任意的irc客户端连接到服务器,你便可以在irc上完成和qq好友的聊天,群聊等
本插件更适合想要在Linux环境下使用qq的irc爱好者
Mojo::Webqq::Plugin::Perldoc
实现通过QQ消息查询perldoc文档,支持perldoc -f|-v xxx
$client->load("Perldoc");
#由于该插件处理完的消息不应该再由其他插件处理,因此插件优先级应该设置成比其他插件优先级更高,插件默认优先级是0
Mojo::Webqq::Plugin::Perlcode
通过QQ消息执行Perl代码,仅支持在linux系统上使用
$client->load("Perlcode");
#由于该插件处理完的消息不应该再由其他插件处理,因此插件优先级应该设置成比其他插件优先级更高,插件默认优先级是0
触发条件:消息以 >>> 开头,比如:
>>> print "hello world";
Mojo::Webqq::Plugin::KnowledgeBase
通过QQ消息自定义问答知识库
$client->load("KnowledgeBase");
触发条件: 消息以如下格式发送可以设定问题和答案,如果问题或答案包含空格可以使用引号 比如:
learn 今天天气怎么样 天气很好
学习 "你吃了吗" 当然吃了
learn '哈哈 你真笨' "就你聪明"
del 今天天气怎么样
删除 '哈哈 你真笨'
Mojo::Webqq::Plugin::FuckDaShen
对qq消息中出现的"大神"关键词进行鄙视
$client->load("FuckDaShen");
Mojo::Webqq::Plugin::PostImgVerifycode
登录过程如果需要验证码,会发邮件到指定的邮箱,通过点击邮件中的链接远程提交验证码,适合配合手机使用
注意: 由于需要发送邮件附件,依赖模块 Mojo::SMTP::Client MIME::Lite,需要单独安装
#插件需要使用到 input_img_verifycode 事件
#需要login中开启delay参数(设置为延迟登录)
#$client->login(qq=>xxxx,pwd=>xxxx,delay=>1);
$client->load("PostImgVerifycode",data=>{
smtp => 'xxxx', #邮箱的smtp地址
port => 'xxxx', #smtp服务器端口,默认25
from => 'xxxx', #发件人
to => 'xxxx', #收件人
user => 'xxxx', #smtp登录帐号
pass => 'xxxx', #smtp登录密码
post_host => 'xxx.xxx.xxx.xxx' , #本机公网IP地址,需要远程访问
post_port => 'xxxx' , #提交验证码的链接地址中使用的端口,默认1987
});
收到的邮件内容如下:
主题:QQ帐号 xxxx 登录验证码
附件:webqq_img_verfiy_xxxx.jpg
正文:请点击以下链接提交验证码:http://xxx.xxx.xxx.xxx:xxxx/check_code
Mojo::Webqq::Plugin::SmartReply
实现机器人的智能回复,支持好友消息、群消息、群临时消息、讨论组临时消息的自动回复
为避免对群内成员产生影响,群内需要使用 @帐号昵称 来触发
$client->load("SmartReply");
Mojo::Webqq::Plugin::Openqq
提供HTTP API接口,方便获取客户端帐号、好友、群、讨论组信息,以及通过接口发送好友消息、群消息、群临时消息和讨论组临时消息
#兼容老版本,data是一个ARRAY引用,可以设置多个监听的地址和端口
$client->load("Openqq",data=>[ {host=>"127.0.0.1",port=>5000}, ]);
#新版本,data可以是一个HASH引用
$client->load("Openqq",data=>{
listen => [ {host=>"127.0.0.1",port=>5000}, ] , #监听的地址和端口,支持多个
auth => sub {my($param,$controller) = @_}, #可选,认证回调函数,用于进行请求鉴权
post_api => 'http://xxxx', #可选,设置接收消息的上报接口
});
若data中设置了auth函数引用,则表示api接口开启认证
认证函数返回值为真,认证通过,函数返回值为假,认证失败,接口返回403
认证回调函数的第一个参数是一个HASH引用,包含get或post提交的参数信息
第二个参数是一个Mojolicious的controller对象,适合对Mojolicious比较熟悉的情况下,利用controller进行高级的认证控制
auth函数示例:
#简单的时间戳过期防盗链
#http://127.0.0.1/openqq/send_message?id=xxxx&content=xxxx&key=xxxx&exp=xxxx
sub {
my $param = shift;
my $secret = 'this is your secret key';
return 0 if time() >= $param->{exp}; #参数值的exp为过期时间,超过过期时间链接已失效
if($param->{key} eq md5_sum($secret . join "",@$param{qw(id gid did content exp)} )){
return 1; #secret和相关参数值拼接成一个字符串后计算md5 再和参数key的值进行比较
}
else{
return 0;
}
}
#利用controller允许指定的IP可以访问,更多关于controller的资料,可以参考 Mojolicious::Controller
sub{
my ($param,$controller) = @_;
if($controller->tx->remote_address eq "127.0.0.1"){
return 1;
}
return 0;
}
接收消息上报接口示例:
$client->load("Openqq",data=>{
listen => [{host=>xxx,port=>xxx}],
post_api=> 'http://127.0.0.1/post_api',
});
接收到消息后,插件会通过HTTP POST请求的方式将json格式的消息上报到http://127.0.0.1:3000/post_api
connect to 127.0.0.1 port 3000
POST /post_api
{ "receiver":"小灰",
"msg_time":"1442542632",
"content":"测试一下",
"msg_class":"recv",
"sender_id":"2372835507",
"receiver_id":"4072574066",
"group":"PERL学习交流",
"group_id":"2617047292",
"sender":"灰灰",
"msg_id":"10856",
"type":"group_message"
}
支持好友消息、群消息、讨论组消息、临时消息的上报
当前支持的信息获取和发送消息的API接口(均返回json格式数据):
#信息获取
/openqq/get_user_info #查询用户信息
/openqq/get_friend_info #查询好友信息
/openqq/get_group_info #查询群信息
/openqq/get_discuss_info #查询讨论组信息
/openqq/get_recent_info #查询最近联系人列表信息
#消息发送,均支持GET和POST
/openqq/send_message #发送好友消息 参数id=xxx&content=xxx
/openqq/send_group_message #发送群消息 参数gid=xxx&content=xxx
/openqq/send_discuss_message #发送讨论组消息 参数did=xxx&content=xxx (由于腾讯限制,当前无法成功发送)
/openqq/send_sess_message #发送群临时消息 参数 gid=xxx&id=xxx&content=xxx
/openqq/send_sess_message #发送讨论组临时消息 参数 did=xxx&id=xxx&content=xxx
调用示例
http://127.0.0.1:5000/openqq/get_user_info
http://127.0.0.1:5000/openqq/send_message?id=xxx&content=hello (中文需要utf8编码,且需要做urlencode)
SEE ALSO
AUTHOR
sjdy521, <sjdy521@163.com>
COPYRIGHT AND LICENSE
Copyright (C) 2014 by sjdy521
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.10.0 or, at your option, any later version of Perl 5 you may have available.