NAME

Mojo::Webqq - A Webqq Client Framework base on Mojolicious

SYNOPSIS

#注意: 
#程序内部数据全部使用UTF8编码,因此二次开发源代码也请尽量使用UTF8编码进行编写,否则需要自己做编码处理
#在终端上执行程序,会自动检查终端的编码进行转换,以防止乱码
#如果在某些IDE的控制台中查看执行结果,程序无法自动检测输出编码,可能会出现乱码,可以手动设置输出编码
#手动设置输出编码参考文档中关于 log_encoding 的说明

#帐号可能进入保护模式的原因:
#多次发言中包含网址
#短时间内多次发言中包含敏感词汇
#短时间多次发送相同内容
#频繁异地登陆

#推荐手机安装[QQ安全中心]APP,方便随时掌握自己帐号的情况
#通过本程序登录QQ之前请先关闭帐号的密保功能

use Mojo::Webqq;
use Mojo::Util qw(md5_sum);

my $qq = 12345678;           #登录的QQ号
my $pwd = "your password";   #使用帐号密码方式登录时需要
my $pwd_md5 = md5_sum($pwd); #得到原始密码的32位长度md5

#初始化一个客户端对象,设置登录的qq号
my $client=Mojo::Webqq->new(
    ua_debug    =>  0,         #是否打印详细的debug信息
    log_level   =>  "info"     #日志打印级别,debug|info|warn|error|fatal
    qq          =>  $qq,       #必选,登录的qq帐号,用于帐号密码登录或保存登录cookie使用
    pwd         =>  $pwd_md5,  #可选,如果选择帐号密码登录方式,必须指定帐号密码的md5值
    login_type  =>  "qrlogin", #"qrlogin"表示二维码登录,"login"表示帐号密码登录
);
#注意: 腾讯可能已经关闭了帐号密码的登录方式,这种情况下只能使用二维码扫描登录

#客户端进行登录
$client->login(); #请关闭帐号的密保功能,不支持密保登录

#客户端加载ShowMsg插件,用于打印发送和接收的消息到终端
$client->load("ShowMsg");

#设置接收消息事件的回调函数,在回调函数中对消息以相同内容进行回复
$client->on(receive_message=>sub{
    my ($client,$msg)=@_;
    $msg->reply($msg->content); #已以相同内容回复接收到的消息
    #你也可以使用$msg->dump() 来打印消息结构
});

#ready事件触发时 表示客户端一切准备就绪:已经成功登录、已经加载完个人/好友/群信息等
#你的代码建议尽量写在 ready 事件中
$client->on(ready=>sub{
    my $client = shift;

    #你的代码写在此处 

});

#客户端开始运行
$client->run();

#run相当于执行一个死循环,不会跳出循环之外
#所以run应该总是放在代码最后执行,并且不要在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::Model::Base具有dump的方法,这些数据类型均由系统自动创建和维护

一般情况下,你只需要对对象进行只读操作

Mojo::Webqq::User 个人

属性:

birthday        #生日
phone           #电话
occupation      #职业
college         #大学
type            #固定值"user"
qq              #qq帐号
id              #本次登录唯一标识,发送消息时需要用到,id和qq号是两个不同的概念
blood           #血型
constel         #星座
homepage        #主页
state           #在线状态 online|away|busy|callme|silent|hidden
country         #国家
city            #城市
nick            #昵称    
displayname     #当前和昵称完全一样
shengxiao       #生效
email           #邮箱
client_type     #固定值"web"
province        #省份
gender          #性别
mobile          #手机
signature       #个性签名

方法:
dump
is_me           #判断对象是否是自己

实际想要获取该对象信息时,是通过客户端提供的 user 属性来获取

$client->user       #返回的是一个Mojo::Webqq::User对象
$client->user->nick #获取帐号昵称
$client->user->id   #获取帐号id
$client->user->dump

Mojo::Webqq::Friend 好友

属性:

qq              #好友的qq号码
id              #好友的id,仅在本次登录期间唯一,多次登录可能会发生变化,记住 id不是qq号
type            #固定值"friend"
category        #好友所属的分组
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            #给好友对象发送消息
is_friend       #判断对象是否是好友
dump

代码示例:

my $friend = $client->search_friend(nick=>xxx);
$client->print("好友分组为: ",$friend->category,"\n"); #使用$client->print()可以自动检测终端编码,防止乱码
$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 分别表示创建的群|加入的群|管理的群
type            #固定值"group"
gnumber         #群号码
gname           #群名称
gmemo           #群说明
gcreatetime     #群创建时间
glevel          #群等级
gowner          #群拥有者的id
gmarkname       #群备注名称
displayname     #如果设置了gmarkname 就返回gmarkname 否则返回gname
member          #群成员,此属性是一个数组引用,数组中每个元素是一个Mojo::Webqq::Group::Member的对象

方法:
search_group_member     #根据群成员属性搜索,标量上下文返回第一个群成员对象,列表上下文返回全部,搜索失败返回undef
each_group_member       #在群中遍历所有的群成员
invite_friend           #邀请好友入群
kick_group_member       #踢出群成员
set_group_admin         #设置群成员为管理员
remove_group_admin      #取消群成员的管理员身份
set_group_member_card   #设置群名片
shutup_group_member     #对群成员进行禁言
speakup_group_member    #取消群成员禁言
members                 #返回群成员对象列表  
send                    #给群对象发送消息
me                      #返回自己在群成员中的对象 (Mojo::Webqq::Group::Member)
is_group                #判断对象是否是一个群
dump                    #打印群对象结构,方便调试

示例代码:
my $group = $client->search_group(gname=>"PERL学习交流群"); #返回一个Mojo::Webqq::Group对象
$group->send("hello world"); #在指定的群中发送消息

my $member = $group->search_group_member(id=>xxx,nick=>xxx); #返回一个Mojo::Webqq::Group::Member对象
$member->send("hello world"); #给群中指定的群成员发送临时消息

$group->each_group_member(sub{
    my($client,$member) = @_;
    ...;
});

for my $group ($client->groups){
    ...;
}

#获取自己在群中的显示名称
$group->me->displayname 

#邀请好友加入群,
$group->invite_friend( $friend1 [,$friend2,...] ); 

#踢出群成员,需要管理员权限
$group->kick_group_member($member1 [,$member2,...]);

#设置指定成员为管理员,需要群主身份
$group->set_group_admin($member1 [,$member2,...]);

#取消指定成员的管理员身份,需要群主身份
$group->remove_group_admin($member1 [,$member2,...]);

#对指定的群成员进行禁言,禁言时间单位为秒,建议尽量是分钟的整数倍,最小60秒
$group->shutup_group_member(60,$member1 [,$member2,...]);

#对指定的群成员取消禁言操作
$group->speakup_group_member($member1 [,$member2,...]);

#设置自己的群名片
$group->set_group_member_card($group->me, $card);
$group->me->set_card($card);

#取消群名片
$group->me->set_card(undef);

#设置群成员的群名片,需要管理员权限
$group->set_group_member_card($member,$card); 

Mojo::Webqq::Group::Member 群成员

属性:

nick            #群成员昵称
type            #固定值"group_member"
province        #省份
gender          #性别
id              #唯一id,仅在本次登录期间唯一,多次登录可能会发生变化
country         #国家
city            #城市
card            #群名片
displayname     #设置了card就返回card否则返回nick
state           #状态
client_type     #客户端类型
qq              #qq号码,当前获取qq号第一次都需要实时调用api获取,效率较低,应避免使用
qage            #q龄
join_time       #入群时间
last_speak_time #最后发言时间
level           #等级 灌水|传说|潜水 等
role            #角色 admin|owner|member
bad_record      #是否包含不良记录 0|1
gid             #所在群的id
gtype           #所在群的类型
gnumber         #所在群的群号码
gname           #所在群的名称
gmemo           #所在群的说明
gcreatetime     #所在群的创建时间
glevel          #所在群的等级
gowner          #所在群的拥有者id
gmarkname       #所在群的备注名称

方法:
send            #给群成员对象发送消息 $member->send("hello world")
dump
is_me           #判断对象是否是群中的自己
is_group_member #判断是否是一个群对象
set_card        #设置群名片,设置其他群成员需要管理员权限
group           #获取成员所属的群对象

Mojo::Webqq::Discuss 讨论组

属性:

did         #讨论组id
type        #固定值"discuss"
dname       #讨论组名称
displayname #当前返回的和dname一样
downer      #讨论组建立者id
member      #讨论组成员,一个数组引用,每个元素是一个Mojo::Webqq::Discuss::Member对象

方法:

search_discuss_member   #搜索讨论组成员
each_discuss_member     #遍历讨论组成员
members                 #返回讨论组成员对象列表
send                    #给讨论组对象发送消息
me                      #获取自己在讨论组中的对象 (Mojo::Webqq::Discuss::Member)
dump
is_discuss          

Mojo::Webqq::Discuss::Member 讨论组成员

属性:

nick
type                    #固定值"discuss_member"
id
qq
ruin
state
client_type
dname
displayname             #当前和dname一样
did
downer

方法:
send                    #给讨论组成员发送临时消息
dump
is_me                   #判断是否是讨论组中的自己
is_discuss_member
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         #消息内容
text            #纯文本消息内容,不包含表情图片等
raw_content     #消息原始内容 一个数组引用

方法:
dump            #打印消息结构,主要用于调试
to_json_hash    #将消息转换为json兼容的hash,方便和外部系统交互
                #常见的接收和发送消息都支持该方法,下文不再赘述
is_at           #消息是否艾特某人,常见的接收和发送消息都支持该方法,下文不再赘述
faces           #返回消息中的表情内容
images          #获取到消息中图片内容后执行指定回调,适合对具体某个消息进行图片处理
                #参考 receive_pic 事件,可以设置全局接收图片的处理回调
reply           #对消息进行回复
                #如果msg_class为recv,相当于发送内容给消息发送者,如果msg_class为send,相当于发送内容给消息的接收者

示例代码:
$msg->sender->nick; #从接收到的好友消息中 获取好友的昵称 更多好友相关的属性,参考Mojo::Webqq::Friend

use Mojo::JSON qw(encode_json);
my $json_hash = $msg->to_json_hash();    #获取到经过utf8 decode的hash引用
my $json_text = encode_json($json_hash); #将hash转换为json字符串

$msg->dump();
$msg->reply("hello world"); #比$client->reply_message($msg,"hello world")更简洁

$msg->is_at();          #判断发送或接收的消息中是否有艾特自己
$msg->is_at($member);   #判断发送或接收的消息中是否有艾特某个成员
$msg->is_at($friend);   #判断发送或接收的消息中是否有艾特某个好友

my @faces = $msg->faces(); #("[微笑]","[流泪]","[害羞]")

$msg->images(sub{
    my($client,$image_path,$sender) = @_;
});

#全局图片处理事件
$client->on(receive_pic=>sub{
    my($client,$image_path,$sender) = @_;
});

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         #消息内容
text            #纯文本消息内容,不包含表情图片等
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
eeceiver        #群中的自己 Mojo::Webqq::Group::Member对象
group           #群对象 Mojo::Webqq::Group
msg_time        #消息时间
content         #消息内容
text            #纯文本消息内容,不包含表情图片等
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         
text            #纯文本消息内容,不包含表情图片等
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         
text            #纯文本消息内容,不包含表情图片等
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->encrypt_method        #perl|js 登录加密算法,系统会会自动选择

客户端方法

new()

qq              #登录的qq号,必选
pwd             #使用帐号密码登录时,qq号对应密码的32位md5
security        #设置该参数,将使得发送和接收消息使用https加密
state           #设置登录状态,默认是online,支持online|away|busy|silent|hidden|offline
ua_debug        #设置该参数,打印调试信息
keep_cookie     #默认为1,0表示不保存登录cookie,1表示保存登录cookie方便下次直接恢复登录

log_level       #默认级别为info,可以设置debug|info|warn|error|fatal
log_path        #默认客户端输出内容打印到STDERR 设置该参数可以将客户端输出重定向到文件
log_encoding    #输出日志的编码,默认自动检测,如果出现乱码可以尝试手动设置一下输出编码
                #编码必须是 Encode 模块支持的格式,比如utf8 gbk等
max_recent      #保存的最近联系人个数,默认20

login_type      #"qrlogin"表示使用二维码登录,"login"表示帐号密码登录,默认为"qrlogin"
encrypt_method  #perl|js 系统会自动判断,一般情况下你不需要自己设置登录加密算法

tmpdir          #程序使用的临时目录,主要用于保存一些验证码、二维码等数据,默认为系统临时目录
cookie_dir      #登录cookie的保存目录,默认为 tmpdir 参数所设置的目录
pic_dir         #图片接收默认地址,默认为 tmpdir 参数所设置的目录

verifycode_path #图片验证码保存路径,默认是 tmpdir 目录下固定文件名
qrcode_path     #二维码保存路径,默认是 tmpdir 目录下固定文件名

#程序会根据实际需要自动按需加载好友、群、讨论组信息,首次登录加载这些信息是不必须的
#如果你的群组数量非常惊人,首次加载会严重影响运行速度,这种情况下建议选择性的取消某些默认加载行为

is_init_friend      #是否在首次登录时初始化好友信息,默认值 1
is_init_group       #是否在首次登录时初始化群组信息,默认值 1
is_init_discuss     #是否在首次登录时初始化讨论组信息,默认值 1
is_init_recent      #是否在首次登录时初始化最近联系人信息,默认值 0

#程序会定期更新个人、好友、群、讨论组信息,目的是为了可以感知新增的成员、失去的好友等
#如果你完全不关注这些特性,可以关闭定期更新
is_update_user      #是否定期更新个人信息,默认值 0
is_update_group     #是否定期更新群组信息,默认值 1
is_update_friend    #是否定期更新好友信息,默认值 1
is_update_discuss   #是否定期更新讨论组信息,默认值 1

$client->new(ua_debug=>0,qq=>xxxxxx,login_type=>"qrlogin");
$client->new(ua_debug=>0,qq=>xxxxxx,pwd=>xxxxx,login_type=>"login");

on()

基于Mojo::EventEmitter的事件注册方法,可支持同时设置多个事件回调

$client->on("event1"=>sub{...},"event2"=>sub{...},);

参考下文客户端支持的事件

login()

客户端登录

$client->login([delay=>0|1]);    

delay  可选,0表示不延迟,立即登录 1表示在after_load_plugin事件触发后登录,即全部插件加载完毕后再执行登录,默认值为0

注意:如果使用帐号密码的登录方式,需要依赖Webqq::Encryption模块

推荐安装Crypt::RSA 或者 Crypt::OpenSSL::RSA + Crypt::OpenSSL::Bignum模块

能够极大的提升Webqq::Encryption登录加密算法的计算速度(Centos可以使用 yum -y install perl-Crypt-OpenSSL-* 来方便安装)

relogin() 重新登录

重新登录前,会先清空登录相关cookie,好友、群等数据,客户端在必要时默认会自动尝试重新登录

$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帐号 $client->run()会阻塞你的代码,导致无法同时使用多个帐号

这种情况下,你应该放弃使用$client->run() 你会需要ready()方法

#同时登录多个qq帐号
my $client1 = Mojo::Webqq->new(qq=>xxx1);
my $client2 = Mojo::Webqq->new(qq=>xxx2);

$client1->login();
$client2->login();

$client1->ready;
$client2->ready;

Mojo::Webqq->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 或 HH:MM:SS
$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) = @_;
        $client->print("从标准输出中实时收到数据:",$chunk,"\n");
    },
    stderr_cb       => sub {
        my($pid,$chunk) = @_;
        $client->print("从标准错误中实时收到数据:",$chunk,"\n"); 
    },
    exit_cb => sub{
        my($pid,$res) = @_;
        $client->print("从标准输出中接收的全部数据:",$res->{stdout},"\n");
        $client->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     #主题
html        #邮件正文内容,html格式
text        #邮件正文内容,纯文本格式
charset     #主题,邮件正文的编码格式,默认UTF-8
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 "发送失败"} 
});

其实也支持阻塞发送
my ($send_status,$err) = $client->mail(...);

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::Friend/Mojo::Webqq::Group::Member/Mojo::Webqq::Dicuss::Member对象

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");

注意:由于腾讯限制,当前无法成功发送讨论组消息

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->print("妈妈再也不用担心我会乱码了...");

客户端事件

常见的事件发生时,比如接收到消息,有人加入群中等等,客户端会设置对应的事件名称,并在事件完成时进行触发

你可以使用$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
                                  +-input_qrcode                         |   |->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 等待输入验证码

登录需要输入验证码,参数为验证码文件的路径

input_qrcode 等待扫描二维码

登录需要扫描二维码,参数为二维码文件的路径

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_pic 接收图片

$client->on(receive_pic=>sub{ 
    my($client,$filepath,$sender)=@_;
    #$filepath 图片文件的绝对路径
    #$sender   发送图片的对象,可能是好友、群成员
    if($sender->is_friend){...;}
    elsif($sender->is_group_member){...;}
    elsif($sender->is_discuss_member){...;}
});

receive_friend_pic 接收好友图片

$client->on(receive_friend_pic=>sub{ 
    my($client,$filepath,$friend)=@_;
    #$filepath 图片文件的绝对路径
    #$friend   发送图片的好友对象
});

receive_sess_pic 接收陌生人图片

$client->on(receive_sess_pic=>sub{ 
    my($client,$filepath,$sender)=@_;
    #$filepath 图片文件的绝对路径
    #$sender   发送图片的群成员或讨论组成员
});

receive_group_pic 接收到群图片

$client->on(receive_group_pic=>sub{ 
    my($client,$filepath,$sender)=@_;
    #$fh       图片文件的只读句柄
    #$filepath 图片文件的绝对路径
    #$sender   发送图片的群成员对象
});

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);
});

friend_property_change 好友对象属性发生改变

$client->on(friend_property_change=>sub{
    my($client,$friend,$property,$old_value,$new_value)=@_;
});

group_property_change 群对象属性发生改变

$client->on(group_property_change=>sub{
    my($client,$group,$property,$old_value,$new_value)=@_;
});

group_member_property_change 群成员对象属性发生改变

$client->on(group_member_property_change=>sub{
    my($client,$member,$property,$old_value,$new_value)=@_;
});

discuss_property_change 讨论组属性发生改变

$client->on(discuss_property_change=>sub{
    my($client,$discuss,$property,$old_value,$new_value)=@_;
});

discuss_member_property_change 讨论组成员属性发生改变

$client->on(discuss_member_property_change=>sub{
    my($client,$member,$property,$old_value,$new_value)=@_;
});

model_update_fail 更新好友、群、讨论组信息失败

程序目前依靠定时更新好友、群、讨论组等数据来判断新增好友、新增群成员等

但当前发现运行一段时间,可能由于cookie过期等原因无法再成功更新这些信息,连续多次更新失败会触发该事件

但并不影响消息接收和发送,只是部分功能受限

如果你很在意这些数据更新是否成功,你可以考虑在该事件触发时重新登录,便可以恢复数据更新功能

$client->on(model_update_fail=>sub{
    $client = shift;
    $client->relogin();
});

receive_raw_message 接收到原始消息内容

$client->on(receive_raw_message=>sub{
    my ($client,$bytes,$hash) = @_;
    #$bytes 是接收到的原始消息 json 格式数据,未做任何处理
    #$hash 是将 $bytes 进行 Mojo::JSON::decode_json 之后得到的 perl hash 结构
});

关于插件

load

加载一个或者多个插件,多个插件使用数组引用,支持的插件参数包括:

priority        #可选,设置插件优先级,默认是0,较高的优先级能够使得插件优先执行
auto_call       #可选,设置是否加载完成后自动执行,默认为1
call_on_load    #可选,加载完插件马上执行,默认为0
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;
our $PRIORITY = 10; #可省略,除了在load中使用priority设置优先级,也可以通过包变量设置
our $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"); 
 $client->run();

当客户端运行时,插件将会被加载并自动执行,收到消息时会自动回复hello world

注意:

当多个消息处理类的插件对同一个消息进行处理时,往往存在冲突的情况

比如一个插件对消息处理完并不希望其他插件再继续处理该消息(默认情况下,receive_message事件会广播给所有订阅该事件的回调)

这种情况下,可以通过设置不同的插件优先级,使得事件被触发时,优先级较高的插件获得优先执行

执行完成后,再通过设置$msg->allow_plugin(0) 来禁止其他插件继续处理该消息,每个消息都带有一个allow_plugin的属性

这是一种建议性的插件协议,并非强制遵守

除此之外,也可以采用插件的手动执行模式,自己根据需要来执行插件

插件列表

Mojo::Webqq::Plugin::ShowMsg

打印消息到终端

$client->load("ShowMsg",data=>{
    allow_group => ["PERL学习交流"],  #可选,允许插件的群,可以是群名称或群号码
    ban_group   => ["私人群",123456], #可选,禁用该插件的群,可以是群名称或群号码
});

Mojo::Webqq::Plugin::StockInfo

查询股票信息,聊天内容中输入 "gp 000001" 或者 "股票 000001" 触发

目前仅支持上海和深圳6位数字股票代码

作者 shalk https://github.com/shalk

Mojo::Webqq::Plugin::MsgSync

实现QQ群和IRC的联通,彼此的消息自动同步,采用的是irc bot转发的形式,需要依赖Mojo::IRC模块

该插件功能不够强大,体验也不够好,完全可以被插件 Mojo::Webqq::Plugin::IRCShell 取代了

$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

需要依赖模块 Mojo::IRC::Server::Chinese

$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虚拟帐号并加入频道 #我的好友
image_api           #兼容elimage图床api地址,将qq图片转为连接,方便在irc上查看图片,默认没有启用
                    #推荐依云的elimage http://img.vim-cn.com/

qq好友会默认加入到irc的 '#我的好友' 频道中

每个qq群也会在irc上创建对应的频道,比如qq群[PERL学习交流]对应的irc频道为'#PERL学习交流'

使用任意的irc客户端连接到服务器,你便可以在irc上完成和qq好友的聊天,群聊等

本插件更适合想要在Linux环境下使用qq的irc爱好者

欢迎加入irc.perfi.wang的#PERL学习交流,该频道已经和QQ群[PERL学习交流]联通

欢迎加入irc.perfi.wang的#Mojolicious,该频道已经和QQ群[Mojolicious]联通

欢迎加入irc.perfi.wang的#Mojo-Webqq,该频道已经和QQ群[Mojo-Webqq]联通

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::ProgramCode

通过QQ消息执行各种编程语言代码,当前支持26种编程语言:

ruby/perl/clojure/coffeescript/bash/cpp/c/assembly/java/scala

csharp/d/erlang/go/idris/rust/php/elixir/fsharp/haskell/javascript

julia/lua/nim/ocaml/python

加载插件方法:

$client->load("ProgramCode");

触发条件: 消息以 code|language>>>开头,比如:

php

code|php>>>
<?php
echo "Hello World\n";

c

code|c>>>
#include <stdio.h>
int main() {
    printf("Hello World!\n");
    return 0;
}

cpp

code|cpp>>>
#include <iostream>
using namespace std;
int main() {
    cout << "Hello World!";
    return 0;
}

java

code|java>>>
public class Main {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

作者 limengyu1990 https://github.com/limengyu1990

Mojo::Webqq::Plugin::MobileInfo

通过QQ消息查询手机归属地,触发消息格式:

手机 1888888888

作者 limengyu1990 https://github.com/limengyu1990

Mojo::Webqq::Plugin::KnowledgeBase

通过QQ消息自定义问答知识库

$client->load("KnowledgeBase");

触发条件: 消息以如下格式发送可以设定问题和答案,如果问题或答案包含空格可以使用引号 比如:

learn 今天天气怎么样  天气很好
学习  "你吃了吗"      当然吃了
learn '哈哈 你真笨'   "就你聪明"

del   今天天气怎么样
删除  '哈哈 你真笨'

其他更多参数设置

$client->load("KnowledgeBase",data=>{
    allow_group => ["PERL学习交流"],  #可选,允许插件的群,可以是群名称或群号码
    ban_group   => ["私人群",123456], #可选,禁用该插件的群,可以是群名称或群号码
    file => './KnowledgeBase.dat', #数据库保存路径
    learn_command  => 'study',     #自定义学习指令关键字
    delete_command =>'clear',      #自定义删除指令关键字
    learn_operator => [12345,678910], #允许学习权限的操作人qq号
    delete_operator => [12345,678910], #允许删除权限的操作人qq号
});

Mojo::Webqq::Plugin::FuckDaShen

对qq消息中出现的"大神"关键词进行鄙视

$client->load("FuckDaShen");

Mojo::Webqq::Plugin::Translation

多国语言翻译功能,自动检测输入输出语言,消息指令格式:

翻译 hello
翻译 你好
翻译 こんにちは

Mojo::Webqq::Plugin::GroupManage

常用群管理功能,包括 新人入群欢迎、成员离开提醒、限制发言频率、限制发图频率

$client->load("GroupManage",data=>{ 
    allow_group => ["PERL学习交流"],  #可选,允许插件的群,可以是群名称或群号码
    ban_group   => ["私人群",123456], #可选,禁用该插件的群,可以是群名称或群号码
    new_group_member => '欢迎新成员 @%s 入群[鼓掌][鼓掌][鼓掌]', #新成员入群欢迎语,%s会被替换成群成员名称
    lose_group_member => '很遗憾 @%s 离开了本群[流泪][流泪][流泪]', #成员离群提醒
    speak_limit => {#发送消息频率限制
        period          => 10, #统计周期,单位是秒
        warn_limit      => 8, #统计周期内达到该次数,发送警告信息
        warn_message    => '@%s 警告, 您发言过于频繁,可能会被禁言或踢出本群', #警告内容
        shutup_limit    => 10, #统计周期内达到该次数,成员会被禁言
        shutup_time     => 600, #禁言时长
        #kick_limit      => 15,   #统计周期内达到该次数,成员会被踢出本群
    },
    pic_limit => {#发图频率限制
        period          => 600,
        warn_limit      => 6,
        warn_message   => '@%s 警告, 您发图过多,可能会被禁言或踢出本群',
        shutup_limit    => 8,
        kick_limit      => 10,
    },
    keyword_limit => {
        period=> 600,
        keyword=>[qw(fuck 傻逼 你妹 滚)],
        warn_limit=>3,
        shutup_limit=>5,
        #kick_limit=>undef,
    },
});

Mojo::Webqq::Plugin::PostQRcode

登录过程如果需要手机扫描二维码,会将二维码以邮件附件的形式发送到指定邮箱,再通过手机QQ扫描二维码

注意: 由于需要发送邮件附件,依赖模块 Mojo::SMTP::Client MIME::Lite,需要单独安装

#插件需要使用到login()方法执行过程中触发的input_qrcode事件,因此需要在login()执行之前加载
#插件自身设置了call_on_load=>1,因此会在加载后马上执行
$client->load("PostQRcode",data=>{
    smtp    =>  'xxxx', #邮箱的smtp地址  
    port    =>  'xxxx', #smtp服务器端口,默认25
    from    =>  'xxxx', #发件人
    to      =>  'xxxx', #收件人
    user    =>  'xxxx', #smtp登录帐号
    pass    =>  'xxxx', #smtp登录密码
});
$client->login();
...;

收到的邮件内容如下:

主题:QQ帐号 xxxx 扫描二维码
附件:webqq_qrcode_xxxx.png
正文:请使用手机QQ扫描附件中的二维码

Mojo::Webqq::Plugin::PostImgVerifycode

登录过程如果需要验证码,会发邮件到指定的邮箱,通过点击邮件中的链接远程提交验证码,适合配合手机使用

注意: 由于需要发送邮件附件,依赖模块 Mojo::SMTP::Client MIME::Lite,需要单独安装

#插件需要使用到login()方法执行过程中触发的input_img_verifycode事件,因此需要在login()执行之前加载
#插件自身设置了call_on_load=>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
});
$client->login();
...;

收到的邮件内容如下:

主题:QQ帐号 xxxx 登录验证码
附件:webqq_img_verfiy_xxxx.jpg
正文:请点击以下链接提交验证码:http://xxx.xxx.xxx.xxx:xxxx/check_code

Mojo::Webqq::Plugin::SmartReply

实现机器人的智能回复,支持好友消息、群消息、群临时消息、讨论组临时消息的自动回复

为避免对群内成员产生影响,默认群内需要使用 @帐号昵称 来触发

$client->load("SmartReply",data=>{
    allow_group     => ["PERL学习交流"],  #可选,允许插件的群,可以是群名称或群号码
    ban_group       => ["私人群",123456], #可选,禁用该插件的群,可以是群名称或群号码
    notice_reply    => ["对不起,请不要这么频繁的艾特我","对不起,您的艾特次数太多"], #可选,提醒时用语
    notice_limit    => 8 ,  #可选,达到该次数提醒对话次数太多,提醒语来自默认或 notice_reply
    warn_limit      => 10,  #可选,达到该次数,会被警告
    ban_limit       => 12,  #可选,达到该次数会被列入黑名大不再进行回复
    period          => 600, #可选,限制周期,单位 秒
    is_need_at       => 1,  #默认是1 是否需要艾特才触发回复
});

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:3000/post_api',
});

#接收到消息后,插件会通过HTTP POST请求的方式将json格式的消息上报到http://127.0.0.1:3000/post_api

connect to 127.0.0.1 port 3000    
POST /post_api
Accept: */*
Content-Length: xxx
Content-Type: application/json

{   "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"
}

一般情况下,post_api接口返回的响应内容可以是随意,会被忽略
post_api接口返回的数据类型如果是 text/json 或者 application/json,并且json格式形式如下:

    {
        "reply":"xxxxx",    #要回复消息,必须包含reply的属性
        "shutup": 1,        #可选,是否对消息发送者禁言
        "shutup_time": 60,  #可选,禁言时长,默认60s
    } 

则表示希望通过post_api响应的内容来直接回复该消息,post_api的返回结果比如

HTTP/1.1 200 OK
Connection: close
Content-Type: application/json;charset=UTF-8
Date: Mon, 29 Feb 2016 05:53:31 GMT
Content-Length: 27
Server: Mojolicious (Perl)

{"reply":"你好","code":0}

则会直接对上报的消息进行回复,回复的内容为 "你好"
支持好友消息、群消息、讨论组消息、临时消息的上报
    

当前支持的信息获取和发送消息的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 或 qq=xxx&content=xxx
/openqq/send_group_message      #发送群消息   参数gid=xxx&content=xxx 或 gnumber=xxx&content=xxx
/openqq/send_discuss_message    #发送讨论组消息 参数did=xxx&content=xxx (由于腾讯限制,当前无法成功发送)
/openqq/send_sess_message       #发送群临时消息  参数 gid=xxx&id=xxx&content=xxx 或 gnumber=xxx&qq=xxx&content=xxx
/openqq/send_sess_message       #发送讨论组临时消息 参数 did=xxx&id=xxx&content=xxx 或 did=xxx&qq=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 
http://127.0.0.1:5000/openqq/send_message?id=xxx&content=%e4%bd%a0%e5%a5%bd (中文需要utf8编码并进行urlencode)

SEE ALSO

Weixin::Client

https://github.com/sjdy521/Mojo-Webqq

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.1 or, at your option, any later version of Perl 5 you may have available.