Mojo::Webqq - A Webqq Client Framework base on Mojolicious
#注意: #程序内部数据全部使用UTF8编码,因此二次开发源代码也请尽量使用UTF8编码进行编写,否则需要自己做编码处理 #在终端上执行程序,会自动检查终端的编码进行转换,以防止乱码 #如果在某些IDE的控制台中查看执行结果,程序无法自动检测输出编码,可能会出现乱码,可以手动设置输出编码 #手动设置输出编码参考文档中关于 log_encoding 的说明 #帐号可能进入保护模式的原因: #多次发言中包含网址 #短时间内多次发言中包含敏感词汇 #短时间多次发送相同内容 #频繁异地登陆 #推荐手机安装[QQ安全中心]APP,方便随时掌握自己帐号的情况 #通过本程序登录QQ之前请先关闭帐号的密保功能 use Mojo::Webqq; #初始化一个客户端对象,设置登录的qq号 my $client=Mojo::Webqq->new( http_debug => 0, #是否打印详细的debug信息 log_level => "info" #日志打印级别,debug|info|msg|warn|error|fatal ); #注意: 腾讯可能已经关闭了帐号密码的登录方式,这种情况下只能使用二维码扫描登录 #请关闭帐号的密保功能,不支持密保登录 #客户端加载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之后再添加任何自己的代码了
通过该项目,你可以完成基本的登录、接收和发送消息,在此基础上你可以通过插件的形式实现更多附加功能,比如:
群管理、聊天记录统计、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的方法,这些数据类型均由系统自动创建和维护
一般情况下,你只需要对对象进行只读操作
属性: id #本次登录唯一标识,发送消息时需要用到,id和qq号是两个不同的概念 uid #qq帐号 name #昵称 type #固定值"user" phone #电话 birthday #生日 occupation #职业 college #大学 blood #血型 constel #星座 homepage #主页 state #在线状态 online|away|busy|callme|silent|hidden country #国家 city #城市 displayname #当前和昵称完全一样 shengxiao #生效 email #邮箱 client_type #固定值"web" province #省份 sex #性别 mobile #手机 signature #个性签名 方法: dump is_me #判断对象是否是自己
实际想要获取该对象信息时,是通过客户端提供的 user 属性来获取
$client->user #返回的是一个Mojo::Webqq::User对象 $client->user->name #获取帐号昵称 $client->user->id #获取帐号id $client->user->dump
属性: id #好友的id,仅在本次登录期间唯一,多次登录可能会发生变化,记住 id不是qq号 uid #好友的qq号码 type #固定值"friend" category #好友所属的分组 name #好友昵称 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(name=>xxx); $client->print("好友分组为: ",$friend->category,"\n"); #使用$client->print()可以自动检测终端编码,防止乱码 $friend->dump(); $friend->send("hello world"); #比$client->send_friend_message($friend,"hello world")更简洁 #遍历好友 $client->each_friend(sub{ my($client,$friend) = @_; ...; }); for my $friend ($client->friends){ ...; }
属性: id #群的id,仅在本次登录期间唯一,多次登录可能会发生变化 type #固定值"group" uid #群号码 name #群名称 memo #群说明 role #create|attend|manage 分别表示创建的群|加入的群|管理的群 owner_id #群拥有者的id owner_uid #群拥有者的qq markname #群备注名称 displayname #如果设置了gmarkname 就返回gmarkname 否则返回gname createtime #群创建时间 level #群等级 member #群成员,此属性是一个数组引用,数组中每个元素是一个Mojo::Webqq::Group::Member的对象 max_member #最大群成员数量 max_admin #最大管理员数量 方法: 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 #返回群成员对象列表 member_count #返回当前群成员数量 send #给群对象发送消息 me #返回自己在群成员中的对象 (Mojo::Webqq::Group::Member) is_group #判断对象是否是一个群 dump #打印群对象结构,方便调试 qiandao #签到 示例代码: my $group = $client->search_group(name=>"PERL学习交流群"); #返回一个Mojo::Webqq::Group对象 $group->send("hello world"); #在指定的群中发送消息 my $member = $group->search_group_member(id=>xxx,name=>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);
属性: id #唯一id,仅在本次登录期间唯一,多次登录可能会发生变化 uid #qq号码,当前获取qq号第一次都需要实时调用api获取,效率较低,应避免使用 type #固定值"group_member" name #群成员昵称 province #省份 sex #性别 country #国家 city #城市 card #群名片 displayname #设置了card就返回card否则返回nick state #状态 client_type #客户端类型 qage #q龄 join_time #入群时间 last_speak_time #最后发言时间 level #等级 灌水|传说|潜水 等 role #角色 admin|owner|member bad_record #是否包含不良记录 0|1 方法: send #给群成员对象发送消息 $member->send("hello world") dump is_me #判断对象是否是群中的自己 is_group_member #判断是否是一个群对象 set_card #设置群名片,设置其他群成员需要管理员权限 group #获取成员所属的群对象
属性: id #讨论组id type #固定值"discuss" name #讨论组名称 displayname #当前返回的和dname一样 owner_id #讨论组建立者id member #讨论组成员,一个数组引用,每个元素是一个Mojo::Webqq::Discuss::Member对象 方法: search_discuss_member #搜索讨论组成员 each_discuss_member #遍历讨论组成员 members #返回讨论组成员对象列表 member_count #返回当前讨论组成员数量 send #给讨论组对象发送消息 me #获取自己在讨论组中的对象 (Mojo::Webqq::Discuss::Member) dump is_discuss
属性: name type #固定值"discuss_member" id uid state client_type displayname #当前和name一样 方法: send #给讨论组成员发送临时消息 dump is_me #判断是否是讨论组中的自己 is_discuss_member discuss #获取成员所属的讨论组对象
#消息主要包含好友消息、群消息、讨论组消息、群临时消息、讨论组临时消息 #所有的消息类都继承Mojo::Webqq::Message::Base,具备dump方法 #消息的对象均由系统创建和维护,你只需要对消息对象进行只读操作即可 属性: type #消息类型:friend_message|group_message|discuss_message|sess_message class #发送或接收的消息:recv|send from #消息的来源,比如来自某个插件,可自由定义,默认"none" ttl #默认值5 当ttl减为0会被消息队列丢弃 allow_plugin #默认1 id #消息id sender_id #发送者id receiver_id #接收者id sender #发送者对象,可以是好友|群成员|讨论组成员 receiver #接收者对象,好友|群成员|讨论组成员 group #消息相关的群组对象 discuss #消息相关的讨论组对象 time #消息时间 content #消息内容 text #纯文本消息内容,不包含表情图片等 raw_content #消息原始内容 一个数组引用 via #临时消息的来源:group|discuss code #发送消息状态,0表示成功,非0失败 msg #发送消息状态中文描述:发送成功|发送失败 info #发送消息更多错误描述信息 方法: dump #打印消息结构,主要用于调试 to_json_hash #将消息转换为json兼容的hash,方便和外部系统交互 #常见的接收和发送消息都支持该方法,下文不再赘述 is_at #消息是否艾特某人,常见的接收和发送消息都支持该方法,下文不再赘述 faces #返回消息中的表情内容 images #获取到消息中图片内容后执行指定回调,适合对具体某个消息进行图片处理 #参考 receive_pic 事件,可以设置全局接收图片的处理回调 reply #对消息进行回复 #如果class为recv,相当于发送内容给消息发送者,如果class为send,相当于发送内容给消息的接收者 is_success #判断发送消息是否成功 示例代码: $msg->sender->displayname; #从消息中 获取发送者的名称 my $json_hash = $msg->to_json_hash(); #获取到hash引用 my $json_text = $client->to_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) = @_; }); text #纯文本消息内容,不包含表情图片等 raw_content #消息原始内容 一个数组引用
$client->security #是否开启安全加密 $client->mode #登录状态 $client->type #类型 固定值 smartqq,Mojo::Webqq只支持smartqq $client->htto_debug #http请求是否打印debug信息 $client->log_level #日志记录等级 默认info $client->log_path #日志记录路径,默认undef,打印到STDOUT $client->version #客户端版本 $client->account #登录帐号,不必是真实的qq号码,可以自由定义,根据扫码确定实际登陆qq
account #登录帐号,默认default,不必是真实qq号码,可自由定义,根据扫码确定实际登陆qq security #设置该参数,将使得发送和接收消息使用https加密 mode #设置登录状态,默认是online,支持online|away|busy|silent|hidden|offline|callme http_debug #设置该参数,打印调试信息 keep_cookie #默认为1,0表示不保存登录cookie,1表示保存登录cookie方便下次直接恢复登录 disable_color #默认为0,是否禁用终端彩色打印 ignore_retcode #对发送消息返回这些状态码不认为发送失败不重试,参数是一个数组引用,默认 [1202,100100] #设置示例 Mojo::Webqq->new(ignore_retcode=>[1202,100100]); log_console #打印到文件的同时也打印到屏幕,方便调试,默认 1 log_level #默认级别为info,可以设置debug|info|warn|error|fatal log_path #默认客户端输出内容打印到STDOUT 设置该参数可以将客户端输出重定向到文件 log_encoding #输出日志的编码,默认自动检测,如果出现乱码可以尝试手动设置一下输出编码 #编码必须是 Encode 模块支持的格式,比如utf8 gbk等 tmpdir #程序使用的临时目录,主要用于保存一些验证码、二维码等数据,默认为系统临时目录 pic_dir #图片接收默认地址,默认为 tmpdir 参数所设置的目录 verifycode_path #图片验证码保存路径,默认是 tmpdir 目录下固定文件名 qrcode_path #二维码保存路径,默认是 tmpdir 目录下固定文件名 cookie_path #登录cookie接收保存地址,默认为 tmpdir 参数所设置的目录下固定文件名 #程序会根据实际需要自动按需加载好友、群、讨论组信息,首次登录加载这些信息是不必须的 #如果你的群组数量非常惊人,首次加载会严重影响运行速度,这种情况下建议选择性的取消某些默认加载行为 is_init_friend #是否在首次登录时初始化好友信息,默认值 1,据测试设置为0可能影响登录 is_init_group #是否在首次登录时初始化群组信息,默认值 1 is_init_discuss #是否在首次登录时初始化讨论组信息,默认值 1 #程序会定期更新个人、好友、群、讨论组信息,目的是为了可以感知新增的成员、失去的好友等 #如果你完全不关注这些特性,可以关闭定期更新 is_update_user #是否定期更新个人信息,默认值 0 is_update_group #是否定期更新群组信息,默认值 1 is_update_friend #是否定期更新好友信息,默认值 1 is_update_discuss #是否定期更新讨论组信息,默认值 1 update_interval #定时更新群组、好友、讨论组等信息的时间间隔,默认600秒 msg_ttl #发送失败的消息,重试次数,默认是5 qrcode_count_max #每个二维码都有一个过期时间,过期后会重新下载一个二维码继续等待扫描 #如果等待扫码次数超过 qrcode_count_max 客户端会停止运行,默认10次 #注意:所有参数都支持通过环境变量来设置,参数对应的环境变量格式 MOJO_WEBQQ_{参数名大写} #比如: MOJO_WEBQQ_ACCOUNT、 MOJO_WEBQQ_HTTP_DEBUG $client->new(http_debug=>0,account=>'xxxxxx',log_level=>'info');
基于Mojo::EventEmitter的事件注册方法,可支持同时设置多个事件回调
$client->on("event1"=>sub{...},"event2"=>sub{...},);
参考下文客户端支持的事件
客户端登录
$client->login();
注意:程序会自动调用该方法,一般情况下你不需要自己调用
重新登录前,会先清空登录相关cookie,好友、群等数据,客户端在必要时默认会自动尝试重新登录
$client->relogin()
指定多少秒之后执行对应的回调函数
$client->timer(10,sub{ print "hello world\n" }); #10s后打印hello world
设置每隔多少秒重复执行对应的回调函数
$client->interval(10,sub{ print "hello world\n" }); #每隔10s后打印hello world
客户端进入事件循环,正式开始运行,一般放在代码的最后,不可或缺
$client->run();
定时执行任务
#支持的时间格式为 HH:MM 或 HH:MM:SS $client->add_job("定时提醒","07:00",sub{$client->send_friend_message($friend,"起床啦");});
在单独的进程中执行代码或命令
客户端采用的是单进程异步事件驱动模式,如果在代码中执行system/exec等来调用其他命令
或者执行某些阻塞的代码,比如sleep等 均会导致客户端主进程被阻塞而影响正常接收和发送消息
这种情况下,可以采用客户端提供的spawn方法,将阻塞的代码放置到单独的进程中执行,捕获进程的标准输出和标准错误
在回调函数中获取到进程执行的结果
该方法实际上参考Mojo::Run模块 并在该模块的基础上做了进一步完善
#支持的参数: is_blocking #是否阻塞执行命令,默认0 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"); } );
该方法实际上是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(...);
更新全部好友信息,客户端会自动调用该方法,通常你不需要主动使用
更新群消息,如果设置了$group(Mojo::Webqq::Group对象) 则更新指定群消息,不设置参数更新全部群信息
客户端会自动调用该方法,通常你不需要主动使用
更新讨论组消息,如果设置了$discuss(Mojo::Webqq::Disucss对象) 则更新指定讨论组消息,不设置参数更新全部讨论组信息
更新最近联系人信息,由于较少使用到,客户端仅在首次登录时调用该方法,更新一次
遍历所有的好友
$client->each_friend(sub{ my($client,$friend) = @_; #$friend是一个Mojo::Webqq::Friend对象 ...; });
返回好友对象数组,数组中的每个元素是一个Mojo::Webqq::Friend对象
my @friends = $client->friends();
遍历所有的群
$client->each_group(sub{ my($client,$group) = @_; #$group是一个Mojo::Webqq::Group对象 ...; });
返回群对象数组,数组中的每个元素是一个Mojo::Webqq::Group对象
my @groups = $client->groups();
遍历所有群中的所有群成员
$client->each_group_member(sub{ my($client,$member) = @_; #$member是一个Mojo::Webqq::Group::Member对象 ...; }); 注意,想要遍历某个群中的所有群成员可以使用群对象中的遍历方法: $group->each_group_member(sub{ my($client,$member)=@_; ...; });
遍历所有的讨论组,和遍历好友或群类似 不再赘述
返回讨论组对象数组,数组中的每个元素是一个Mojo::Webqq::Discuss对象
my @discusss = $client->discusss;
遍历所有讨论组中的所有讨论组成员,和遍历好友或群类似 不再赘述
my $friend = $client->search_friend(id=>xxx);#$friend是一个Mojo::Webqq::Friend的对象 $client->send_friend_message($friend,"hello world");#发送内容必须是utf8编码 在callback中对发送的消息进行预处理 $client->send_friend_message($friend,"hello world",sub{ my ($client,$msg) = @_; #对即将发送的消息进行预处理 $msg->msg_from("自定义的来源标识"); #设置该条消息发送完毕后的回调函数 $msg->cb(sub{ my($client,$msg)=@_; print $msg->id . "发送失败\n" if not $msg->is_success; }); });
my $group = $client->search_group(name=>"PERL学习交流");#$group是一个Mojo::Webqq::Group的对象 $client->send_group_message($group,"hello world");
my $discuss = $client->search_discuss(name=>"讨论组");#$discuss是一个Mojo::Webqq::Discuss的对象 $client->send_discuss_message($discuss,"hello world");
注意:由于腾讯限制,当前无法成功发送讨论组消息
#先找到群,再使用群搜索到群成员 my $group = $client->search_group(name=>"PERL学习交流"); if(not defined $group){ #没有搜索到群成员,处理你的异常 ...; } else{ my $member = $group->search_group_member(name=>"小灰"); $client->send_sess_message($member,"hello world") if defined $member; }
send_friend_message()/send_group_message()/send_discuss_message()/send_sess_message()
这一类方法适合对特定的对象主动发送消息,但很多场景下,我们不需要关心对方是谁
只需要对接收到的消息进行回复,使用reply_message会比较方便
$client->reply_message($msg,$content);
该方法为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数据进行转换得到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_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分钟才会触发相关的事件,或者新增群成员一旦发言也会发送触发事件
传递给回调函数的参数是接收到的消息对象
$client->on(receive_message=>sub{my ($client,$msg)=@_;$msg->dump});
传递给回调函数的参数是 发送出去的消息对象
$client->on(send_message=>sub{ my ($client,$msg)=@_; print $msg->id,"发送成功" if $msg->is_success });
登录完成时触发事件
$client->on(login=>sub{ my($client,$is_scan) = @_; #$is_scan 值为1表示本次登录是扫描二维码登录的 好友,群组对象的id可能会发生变化 # 值为0表示本次登录是使用cookie直接恢复登录,未经过二维码扫描,对象id一般不会发生变化 });
重新登录完成时触发事件,无额外的回调参数
登录需要输入验证码,参数为验证码文件的路径
登录需要扫描二维码,参数分别为二维码文件的本地路径
客户端准备就绪事件
客户端一切准备就绪,开始进入事件循环之前
插件被执行时,参数为插件名称
插件被加载完成时,参数为插件名称
全部插件加载完成后触发的事件
新增好友时触发事件,回调参数为新增的好友对象
$client->on(new_friend=>sub{my ($client,$friend)=@_});
失去好友时触发事件,回调参数为失去的好友对象
$client->on(lose_friend=>sub{my ($client,$friend)=@_});
$client->on(new_group=>sub{my ($client,$group)=@_});
$client->on(lose_group=>sub{my ($client,$group)=@_});
$client->on(new_group_member=>sub{my ($client,$group_member)=@_});
$client->on(lose_group_member=>sub{my ($client,$group_member)=@_});
$client->on(new_discuss=>sub{my ($client,$discuss)=@_});
$client->on(lose_discuss=>sub{my ($client,$discuss)=@_});
$client->on(new_discuss_member=>sub{my ($client,$discuss_member)=@_});
$client->on(lose_discuss_member=>sub{my ($client,$discuss_member)=@_});
$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){...;} });
$client->on(receive_friend_pic=>sub{ my($client,$filepath,$friend)=@_; #$filepath 图片文件的绝对路径 #$friend 发送图片的好友对象 });
$client->on(receive_sess_pic=>sub{ my($client,$filepath,$sender)=@_; #$filepath 图片文件的绝对路径 #$sender 发送图片的群成员或讨论组成员 });
$client->on(receive_group_pic=>sub{ my($client,$filepath,$sender)=@_; #$fh 图片文件的只读句柄 #$filepath 图片文件的绝对路径 #$sender 发送图片的群成员对象 });
消息发送之前的事件,一般用于在即将发送消息前对消息进行预处理
$client->on(before_send_message=>sub{ my($client,$msg) = @_; my $content = $msg->content; $content .= "我是可爱的小尾巴"; $msg->content($content); });
$client->on(friend_property_change=>sub{ my($client,$friend,$property,$old_value,$new_value)=@_; });
$client->on(group_property_change=>sub{ my($client,$group,$property,$old_value,$new_value)=@_; });
$client->on(group_member_property_change=>sub{ my($client,$member,$property,$old_value,$new_value)=@_; });
$client->on(discuss_property_change=>sub{ my($client,$discuss,$property,$old_value,$new_value)=@_; });
$client->on(discuss_member_property_change=>sub{ my($client,$member,$property,$old_value,$new_value)=@_; });
程序目前依靠定时更新好友、群、讨论组等数据来判断新增好友、新增群成员等
但当前发现运行一段时间,可能由于cookie过期等原因无法再成功更新这些信息,连续多次更新失败会触发该事件
但并不影响消息接收和发送,只是部分功能受限
如果你很在意这些数据更新是否成功,你可以考虑在该事件触发时重新登录,便可以恢复数据更新功能
$client->on(model_update_fail=>sub{ $client = shift; $client->relogin(); });
$client->on(receive_raw_message=>sub{ my ($client,$bytes,$hash) = @_; #$bytes 是接收到的原始消息 json 格式数据,未做任何处理 #$hash 是将 $bytes 进行 Mojo::JSON::from_json 之后得到的 perl hash 结构 });
加载一个或者多个插件,多个插件使用数组引用,支持的插件参数包括:
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可以设置插件执行的优先级
数字越大,优先级越高,插件会被优先执行
手动执行一个插件、适合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的属性
这是一种建议性的插件协议,并非强制遵守
除此之外,也可以采用插件的手动执行模式,自己根据需要来执行插件
打印消息到终端
$client->load("ShowMsg",data=>{ allow_group => ["PERL学习交流"], #可选,允许插件的群,可以是群名称或群号码 ban_group => ["私人群",123456], #可选,禁用该插件的群,可以是群名称或群号码 });
查询股票信息,聊天内容中输入 "gp 000001" 或者 "股票 000001" 触发
目前仅支持上海和深圳6位数字股票代码
作者 shalk https://github.com/shalk
将qq协议转换成irc协议,启动一个本地的irc服务器,使用任意irc客户端(irc user设置为qq号)连接后即可以按照irc的方式使用qq
需要依赖模块 Mojo::IRC::Server::Chinese
$client->load("IRCShell",data=>{ listen=>[ {host=>"0.0.0.0",port=>6667},], #可选,IRC服务器监听的地址+端口,默认0.0.0.0:6667 load_friend => 0, #可选,是否初始为每个好友生成irc虚拟帐号并加入频道 #我的好友 }); #支持的参数包括: listen #监听的地址和端口,数组的形式来支持多个地址 # listen=>[ {host=>"127.0.0.1",port=>6667}, {host=>"127.0.0.1",port=>6668}] # 表示监听127.0.0.1:6667和 127.0.0.1:6668两个端口 allow_group # allow_group=>["群组1名称","群组2号码",], #可选,允许在irc上创建的QQ群名称或群号码 ban_group # ban_group=>["群组1名称","群组1号码"], #可选,禁止在irc上创建的QQ群名称 master_irc_nick # 和QQ关联的irc客户端昵称,默认按照和QQ相同的昵称或者客户端ip是本机地址作为识别规则 # 用于识别哪个连接到服务器上的irc用户是你自己 auto_join_channel #0|1 默认为1,开启该选项,收到未加入的频道消息会自动加入频道并接收消息,主要为了防止漏收 load_friend #0|1 默认是0 是否初始为每个好友生成irc虚拟帐号并加入频道 #我的好友 upload_api #图床api地址,将图片转为连接,方便在irc上查看图片 #支持兼容 http://img.vim-cn.com/ 的接口,考虑到隐私问题,默认不开启 #以下参数需要 Mojo::IRC::Server::Chinese v1.8.1及以上版本 #TLS支持 #代码示例 $client->load("IRCShell",data=>{ listen=>[ {host=>"0.0.0.0",port=>6667}, #监听普调端口 {host=>"0.0.0.0",port=>6697,tls=>1}, #监听6697端口,开启TLS加密 ], }); #支持的TLS参数: #更多详细参数使用说明参见 https://metacpan.org/pod/Mojo::IOLoop::Server#tls tls #0|1 是否启用tls加密 tls_ca #可选,本地ca证书路径 例如 /etc/tls/ca.crt tls_cert #可选,服务器证书路径,默认会生成一个测试证书 tls_ciphers #可选,加密套件,例如 AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH tls_key #可选,服务器证书key文件路径 tls_verify #可选,验证模式 tls_version #可选,协议版本,例如 TLSv1_2 create_chanserv_user #0|1 默认为1,是否创建频道时自动生成昵称为 ChanServ 的虚拟用户 #主要用于解决频道人数为空时,HexChat等某些客户端/list无法获取到频道信息的问题 auth #登录验证函数,在客户端登录后被执行,返回 1 验证通过,返回 0 验证失败 代码举例: $client->load("IRCShell",data=>{ auth => sub{ my($nick,$user,$pass) = @_; #传递给验证函数的三个参数依次是 irc客户端的NICK/USER/PASS if($nick eq '小灰'){#对于昵称"小灰"进行密码验证 return 0 if not defined $pass; #没有设置密码,验证失败 return 0 if $pass ne '123456'; #设置的密码不是 "123456" 验证失败 return 1; #验证成功,允许正常登录 } else{return 1;}#对其他登录昵称不进行验证,全部允许登录 } });
当设置了 load_friend =>1 参数,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]联通
实现通过QQ消息查询perldoc文档,支持perldoc -f|-v xxx
$client->load("Perldoc"); #由于该插件处理完的消息不应该再由其他插件处理,因此插件优先级应该设置成比其他插件优先级更高,插件默认优先级是0
通过QQ消息执行Perl代码,仅支持在linux系统上使用
$client->load("Perlcode"); #由于该插件处理完的消息不应该再由其他插件处理,因此插件优先级应该设置成比其他插件优先级更高,插件默认优先级是0
触发条件:消息以 >>> 开头,比如:
>>> print "hello world";
通过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>>>开头,比如:
code|php>>> <?php echo "Hello World\n";
code|c>>> #include <stdio.h> int main() { printf("Hello World!\n"); return 0; }
code|cpp>>> #include <iostream> using namespace std; int main() { cout << "Hello World!"; return 0; }
code|java>>> public class Main { public static void main(String[] args) { System.out.println("Hello World!"); } }
作者 limengyu1990 https://github.com/limengyu1990
通过QQ消息查询手机归属地,触发消息格式:
手机 1888888888
通过QQ消息自定义问答知识库
$client->load("KnowledgeBase");
触发条件: 消息以如下格式发送可以设定问题和答案,如果问题或答案包含空格可以使用引号 比如:
learn 今天天气怎么样 天气很好 学习 "你吃了吗" 当然吃了 learn '哈哈 你真笨' "就你聪明" learn* "你吃了吗" 当然吃了 (指令结尾有*表示添加全局知识库,默认只添加到所在的群组或好友知识库中) del 今天天气怎么样 删除 '哈哈 你真笨' del* 今天天气怎么样 (删除全局知识库)
其他更多参数设置
$client->load("KnowledgeBase",data=>{ allow_group => ["PERL学习交流"], #可选,允许插件的群,可以是群名称或群号码 ban_group => ["私人群",123456], #可选,禁用该插件的群,可以是群名称或群号码 file => './KnowledgeBase.txt', #数据库保存路径,纯文本形式,可以编辑 learn_command => 'learn', #可选,自定义学习指令关键字 delete_command =>'del', #可选,自定义删除指令关键字 learn_operator => [12345,678910], #允许学习权限的操作人qq号 delete_operator => [12345,678910], #允许删除权限的操作人qq号 mode => 'fuzzy', # fuzzy|regex|exact 分别表示模糊|正则|精确, 默认模糊 check_time => 10, #默认10秒检查一次文件变更 show_keyword => 1, #消息是否包含触发关键字信息,默认为0 });
1)知识库采用文本文件形式,可编辑,格式如下:
分组空间(群名称) # 匹配关键字(或正则表达式) # 回复内容
文件中存储的数据格式示例:
__全局__ # 你好 # 很高兴认识 __我的好友__ # 多谢 # 不客气 我的群组1 # 今天天气怎么样 # 没有雨\n不用带伞 (\n表示换行) 我的群组1 # 今天天气怎么样 # 不知道 我的群组1 # 今天天气怎么样 # 自己百度 (相同关键字可以设置多行的回复内容,随机选取) 我的群组2 # ^在吗(\?|)$ # 在呀,啥事 我的群组2 # 大(神|婶) # 大神你妹啊 我的群组3 # 井号怎么打出来 # 我发给你:\# 不客气 (井号要转义)
2)定时检查知识库变动,自动实时加载,插件中的check_time参数可以设置定时检测间隔
对qq消息中出现的"大神"关键词进行鄙视
$client->load("FuckDaShen");
多国语言翻译功能,自动检测输入输出语言,消息指令格式:
翻译 hello 翻译 你好 翻译 こんにちは
输入关键字进行猜谜,作者 limengyu1990 https://github.com/limengyu1990
$client->load("Riddle"); $client->load("Riddle",data=>{ allow_group => ["PERL学习交流"], #可选,允许插件的群,可以是群名称或群号码 ban_group => ["私人群",123456], #可选,禁用该插件的群,可以是群名称或群号码 ban_user => ["坏蛋",123456], #可选,禁用该插件的用户,可以是用户的显示名称或qq号码 command => "猜谜", #可选,触发关键字 apikey => "xxxx", #可选,参见 http://apistore.baidu.com/apiworks/servicedetail/440.html?qq-pf-to=pcqq.c2c timeout => 30, #等待答案的超时时间,超时后会自动公布答案 });
调用系统的图片查看程序来展示二维码图片,方便扫描,作者 autodataming https://github.com/autodataming
$client->load("ShowQRcode");#当前仅支持win
输入关键字进行猜谜,作者 hyvinlam https://github.com/hyvinlam
$client->load("GasPrice"); $client->load("GasPrice",data=>{ allow_group => ["PERL学习交流"], #可选,允许插件的群,可以是群名称或群号码 ban_group => ["私人群",123456], #可选,禁用该插件的群,可以是群名称或群号码 command => "油价", #可选,触发关键字 apikey => "xxxx", #可选,参见 http://apistore.baidu.com/apiworks/servicedetail/710.html msg_tail => "消息尾巴", #可选 is_need_at => 0, #是否需要艾特,默认值为0 });
QQ群每日签到插件
$client->load("Qiandao"); $client->load("Qiandao",data=>{ allow_group => ["PERL学习交流"], #可选,允许插件的群,可以是群名称或群号码 ban_group => ["私人群",123456], #可选,禁用该插件的群,可以是群名称或群号码 is_qiandao_on_login => 0 , #可选,是否登录时进行签到,默认值为0 qiandao_time => "09:30" , #可选,每日签到的时间,默认是 09:30 });
常用群管理功能,包括 新人入群欢迎、成员离开提醒、限制发言频率、限制发图频率
$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, }, });
将二维码图片上传至腾讯云对象存储,获取到公网可以访问的url地址
$client->load("UploadQRcode");
登录过程如果需要手机扫描二维码,会将二维码以邮件附件的形式发送到指定邮箱,再通过手机QQ扫描二维码
注意: 由于需要发送邮件附件,依赖模块 Mojo::SMTP::Client MIME::Lite,需要单独安装
$client->load("PostQRcode",data=>{ smtp => 'xxxx', #邮箱的smtp地址 port => 'xxxx', #smtp服务器端口,默认25 from => 'xxxx', #发件人 to => 'xxxx', #收件人 user => 'xxxx', #smtp登录帐号 pass => 'xxxx', #smtp登录密码 tls => 0, #可选,是否使用SMTPS协议,默认为0 #在没有设置的情况下,如果使用的端口为465,则该选项会自动被设置为1 }); 收到的邮件内容如下: 主题:QQ帐号 xxxx 扫描二维码 附件:webqq_qrcode_xxxx.png 正文:请使用手机QQ扫描附件中的二维码
登录过程如果需要验证码,会发邮件到指定的邮箱,通过点击邮件中的链接远程提交验证码,适合配合手机使用
$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
接收消息通过谷歌提供的GCM接口发送到android手机
$client->load("GCM",data=>{ api_url => 'https://gcm-http.googleapis.com/gcm/send', api_key =>'xxx', #需要向谷歌申请 registration_ids=>["xxx"],#android设备的注册id allow_group =>["允许推送消息的群名称1","允许推送消息的群名称2","或者群号码"], ban_group =>["禁止推送消息的群名称","或者群号码"], allow_dicuss =>["允许推送消息的讨论组名称1","允许推送消息的讨论组名称2"], ban_discuss =>["禁止推送消息的讨论组名称",], });
实现机器人的智能回复,支持好友消息、群消息、群临时消息、讨论组临时消息的自动回复
为避免对群内成员产生影响,默认群内需要使用 @帐号昵称 来触发
$client->load("SmartReply",data=>{ apikey => '4c53b48522ac4efdfe5dfb4f6149ae51', #可选,参考http://www.tuling123.com/html/doc/apikey.html allow_group => ["PERL学习交流"], #可选,允许插件的群,可以是群名称或群号码 ban_group => ["私人群",123456], #可选,禁用该插件的群,可以是群名称或群号码 ban_user => ["坏蛋",123456], #可选,禁用该插件的用户,可以是用户的显示名称或qq号码 notice_reply => ["对不起,请不要这么频繁的艾特我","对不起,您的艾特次数太多"], #可选,提醒时用语 notice_limit => 8 , #可选,达到该次数提醒对话次数太多,提醒语来自默认或 notice_reply warn_limit => 10, #可选,达到该次数,会被警告 ban_limit => 12, #可选,达到该次数会被列入黑名单不再进行回复 ban_time => 1200,#可选,拉入黑名单时间,默认1200秒 period => 600, #可选,限制周期,单位 秒 is_need_at => 1, #默认是1 是否需要艾特才触发回复 keyword => [qw(小灰 小红 小猪)], #触发智能回复的关键字,使用时请设置is_need_at=>0 });
提供HTTP API接口,方便获取客户端帐号、好友、群、讨论组信息,以及通过接口发送和接收好友消息、群消息、群临时消息和讨论组临时消息
CPAN文档可能比较旧或者表达的信息不够丰富,请参考github上API相关说明https://github.com/sjdy521/Mojo-Webqq/blob/master/API.md
#同时支持http/https请求 $client->load("Openqq",data=>{ listen => [ { host=>"0.0.0.0", port=>5000, #监听5000端口的http请求 }, { host =>"0.0.0.0", port =>443, #可选,https请求监听端口,默认443 tls =>1, #必选,开启https请求支持 tls_ca =>"/etc/tls/ca.crt" #可选,ca证书路径 tls_cert =>"/etc/tls/server.crt" #可选,服务器证书路径,默认生成一个测试证书 tls_key =>"/etc/tls/server.key" #可选,证书对应的key文件,默认生成一个测试key文件 },#更多tls参数支持参考 https://metacpan.org/pod/Mojo::IOLoop::Server#tls ], }); #data是一个HASH引用 $client->load("Openqq",data=>{ listen => [ {host=>"0.0.0.0",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_friend_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:5000/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 { "time":"1442542632", "content":"测试一下", "class":"recv", "sender":"灰灰", "sender_id":"2372835507", "sender_uid":"456789", "receiver":"小灰", "receiver_id":"4072574066", "receiver_uid":"123456", "group":"PERL学习交流", "group_id":"2617047292", "group_uid":"67890", "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 #查询讨论组信息 #消息发送,均支持GET和POST /openqq/send_friend_message #发送好友消息 参数id=xxx&content=xxx 或 uid=xxx&content=xxx /openqq/send_group_message #发送群消息 参数id=xxx&content=xxx 或 uid=xxx&content=xxx /openqq/send_discuss_message #发送讨论组消息 参数id=xxx&content=xxx (由于腾讯限制,当前无法成功发送) /openqq/send_sess_message #发送群临时消息 参数 group_id=xxx&id=xxx&content=xxx 或 group_uid=xxx&uid=xxx&content=xxx (由于腾讯限制,当前无法成功发送) /openqq/send_sess_message #发送讨论组临时消息 参数 discuss_id=xxx&id=xxx&content=xxx 或 dusciss_id=xxx&uid=xxx&content=xxx (由于腾讯限制,当前无法成功发送)
调用示例
http://127.0.0.1:5000/openqq/get_user_info http://127.0.0.1:5000/openqq/send_friend_message?id=xxx&content=hello http://127.0.0.1:5000/openqq/send_friend_message?id=xxx&content=%e4%bd%a0%e5%a5%bd (中文需要utf8编码并进行urlencode)
更多完整文档请参考github上API相关说明https://github.com/sjdy521/Mojo-Webqq/blob/master/API.md
输入关键字 "周易","占卜","算命","八卦" 发送占卜消息 作者https://github.com/bollwarm
$client->load("Pu");
输入关键字 "子曰","论语","之乎者也" 发送论语语句 作者https://github.com/bollwarm
$client->load("ZiYue");
Mojo::Weixin
https://github.com/sjdy521/Mojo-Webqq
https://github.com/sjdy521/Mojo-Weixin
sjdy521, <sjdy521@163.com>
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.
To install Mojo::Webqq, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Mojo::Webqq
CPAN shell
perl -MCPAN -e shell install Mojo::Webqq
For more information on module installation, please visit the detailed CPAN module installation guide.