最近公司需要做个qq机器人获取qq好友列表,并且能够自动向选定的qq好友定时发送消息。没有头绪,硬着头皮上

甘甜的心情瞬间变得苦涩了 哇 多捞吆

1.WEBQQ3.0登陆协议

进入WEBQQ, http://web.qq.com/
通过工具分析,可以知道,用户在输入密码之前(也就是输入帐号后),会首先GET一个请求过去

https://ssl.ptlogin2.qq.com/check?uin=1432334894&appid=1003903&r=0.5534069868735969

我们只详细分析下这一个请求,看看,这个请求到底携带了什么样的数据

这个GET请求返回ptui_checkVC(’0′,’!TMX’,'\x00\x00\x00\x00\x0e\xe9\x41\xc1′);这样的字符串,其中第一个字符串,’0′代表不需要验证码,’!TMX’这个数据,是等会登陆需要的,第三个字符串加密密码的时候会用到。
附PHP代码如下:

  1. <?php
  2. /**
  3. * 获取验证码
  4. *
  5. * @access public
  6. * @param int $uid
  7. * @return array
  8. */
  9. function check_verify($uid)
  10. {
  11. $ch = curl_init("https://ssl.ptlogin2.qq.com/check?uin={$uid}&appid=1003903&r=0.14233942252344134");
  12. $cookie = "confirmuin=0; ptvfsession=b1235b1729e7808d5530df1dcfda2edd94aabec43bf450d8cf037510802aa1a7dbed494c66577479895c62efa3ef35ab; ptisp=cnc";
  13. curl_setopt($ch, CURLOPT_COOKIE, $cookie);
  14. curl_setopt($ch, CURLOPT_COOKIEFILE, temp_dir."cookie");
  15. curl_setopt($ch, CURLOPT_COOKIEJAR, temp_dir."cookie");
  16. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  17. $data = curl_exec($ch);
  18. if (preg_match("/ptui_checkVC\('(.*)','(.*)','(.*)'\);/", $data, $verify))
  19. {
  20. return array_slice($verify, 1);
  21. }
  22. }
  23. /* WebQQ3.0 core part end of */

密码登陆后,监控到这样一个请求

http://ptlogin2.qq.com/login?u={$uid}&p={$passwd}&verifycode={$verify}&webqq_type=10&remember_uin=1&login2qq=1&aid=1003903&u1=http%3A%2F%2Fweb.qq.com%2Floginproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&h=1&ptredirect=0&ptlang=2052&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=8-38-447467&mibao_css=m_webqq&t=3&g=1

其中有三个参数需要解释一下
u:QQ号
p:加密后的密码
verifycode:验证码
附PHP版登录函数以及加密函数代码如下:

  1. <?php
  2. /**
  3. * WEBQQ3.0 新版登陆加密函数
  4. *
  5. * @access public
  6. * @param string $p
  7. * @param string $pt
  8. * @param string $vc
  9. * @param boolean $md5
  10. * @return string
  11. */
  12. function jspassword($p,$pt,$vc,$md5 = true)
  13. {
  14. if ($md5)
  15. {
  16. $p = strtoupper(md5($p));
  17. }
  18. $len = strlen($p);
  19. $temp = null;
  20. for ($i=0; $i < $len ; $i = $i + 2)
  21. {
  22. $temp .= '\x'.substr($p, $i,2);
  23. }
  24. return strtoupper(md5(strtoupper(md5(hex2asc($temp).hex2asc($pt))).$vc));
  25. }
  26.  
  27. /**
  28. * 十六进制转字符
  29. *
  30. * @access private
  31. * @param string $str
  32. * @return string
  33. */
  34. function hex2asc($str)
  35. {
  36. $str = join('', explode('\x', $str));
  37. $len = strlen($str);
  38. $data = null;
  39. for ($i=0;$i<$len;$i+=2)
  40. {
  41. $data .= chr(hexdec(substr($str,$i,2)));
  42. }
  43. return $data;
  44. }
  45.  
  46. /**
  47. * 登录
  48. *
  49. * @access public
  50. * @param int $uid
  51. * @param string $passwd
  52. * @param string $verify
  53. * @return array
  54. */
  55. function login($uid, $passwd, $verify)
  56. {
  57. $url = "http://ptlogin2.qq.com/login?u={$uid}&p={$passwd}&verifycode={$verify}&webqq_type=10&remember_uin=1&login2qq=1&aid=1003903&u1=http%3A%2F%2Fweb.qq.com%2Floginproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&h=1&ptredirect=0&ptlang=2052&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=8-38-447467&mibao_css=m_webqq&t=3&g=1";
  58. $ch = curl_init($url);
  59. curl_setopt($ch, CURLOPT_COOKIEFILE, temp_dir."cookie");
  60. curl_setopt($ch, CURLOPT_COOKIEJAR, temp_dir."cookie");
  61. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  62. $data = curl_exec($ch);
  63. if (preg_match("/ptuiCB\('(.*)','(.*)','(.*)','(.*)','(.*)',\s'(.*)'\);/U", $data, $verify))
  64. {
  65. return array_slice($verify, 1);
  66. }
  67. }
  68. /* WebQQ3.0 core part end of */

登录成功后返回值类似:
ptuiCB(’0′,’0′,’http://web.qq.com/loginproxy.html?login2qq=1&webqq_type=10′,’0′,’登录成功!’, ‘秋风’);
还有一组COOKIE,COOKIE值全都保存起来,待会儿会用到。
到这一步其实还没有真正的登录QQ的聊天接口,继续往下看。

第一次登录成功后,紧接着发送一个POST到http://d.web2.qq.com/channel/login2
POST值(请把参数值用urlencode函数编码)如下:
r={“status”:”online”,”ptwebqq”:”{$ptwebqq}”,”passwd_sig”:”",”clientid”:”{$clientid}”,”psessionid”:null}&clientid={$clientid}&psessionid=null
其中ptwebqq的值来自第一次登录时候的COOKIE值ptwebqq
clientid是个随机数,自己定义就行了
请求后的返回值是一个JSON格式的值,保存起来,后边收发信息时会用到:
到此为止,登陆就完成了。
附PHP版解析Cookie File函数以及登录函数代码如下:

  1. <?php
  2. /**
  3. * 解析cookie
  4. *
  5. * @access public
  6. * @return array
  7. */
  8. function parse_cookie()
  9. {
  10. // Netscape HTTP Cookie File
  11. $cookies = file(temp_dir."cookie");
  12.  
  13. $data = array();
  14. foreach ($cookies as $v)
  15. {
  16. if (preg_match("/(.*\.qq\.com)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\n/U", $v, $p))
  17. {
  18. $data[] = array_slice($p, 1);
  19. }
  20. }
  21.  
  22. return $data;
  23. }
  24.  
  25. /**
  26. * 获取cookie
  27. *
  28. * public
  29. * @param array $cookie
  30. * @return array
  31. */
  32. function get_cookie($cookie = NULL)
  33. {
  34. if ($cookie === NULL)
  35. {
  36. $cookie = parse_cookie();
  37. }
  38.  
  39. if (is_array($cookie) && count($cookie)<=6)
  40. {
  41. return FALSE;
  42. }
  43.  
  44. foreach ($cookie as $v)
  45. {
  46. $data[$v[5]] =$v[6];
  47. }
  48.  
  49. return $data;
  50. }
  51.  
  52. /**
  53. * 真正的登录(上线)
  54. *
  55. * @access public
  56. * @param string $ptwebqq
  57. * @return string
  58. */
  59. function login2($ptwebqq,$clientid)
  60. {
  61. $url = "http://d.web2.qq.com/channel/login2";
  62. $ch = curl_init($url);
  63. curl_setopt($ch, CURLOPT_POSTFIELDS, "r=%7B%22status%22%3A%22online%22%2C%22ptwebqq%22%3A%22{$ptwebqq}%22%2C%22passwd_sig%22%3A%22%22%2C%22clientid%22%3A%22{$clientid}%22%2C%22psessionid%22%3Anull%7D&clientid={$clientid}&psessionid=null");
  64. // 必须要来路域名
  65. curl_setopt($ch, CURLOPT_REFERER, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=2");
  66. // curl_setopt($ch, CURLOPT_HEADER, TRUE);
  67. curl_setopt($ch, CURLOPT_COOKIEFILE, temp_dir."cookie");
  68. curl_setopt($ch, CURLOPT_COOKIEJAR, temp_dir."cookie");
  69. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  70. return curl_exec($ch);
  71. }
  72. /* WebQQ3.0 core part end of */

2.传说中的心跳包

顾名思义,心跳包,就是维持一个长连接,让WEBQQ保持在线的一种机制,.这个心跳包非常简单,只需要每隔几秒,或者写一个死循环发起请求就好(长时间不触发此步骤,会导致QQ掉线)
POST地址:http://d.web2.qq.com/channel/poll2
POST值(请把参数值用urlencode函数编码)如下:
r={“clientid”:”{$clientid}”,”psessionid”:”{$psessionid}”,”key”:0,”ids”:[]}&clientid={$clientid}&psessionid={$psessionid}
其中psessionid的值,在第二次登录返回的JSON信息里边能找到
clientid与第二次登录时候的clientid相同
附PHP版心跳请求函数代码如下:

  1. <?php
  2. /**
  3. * 心跳包(获取消息)
  4. *
  5. * @access public
  6. * @param string $psessionid
  7. * @param int $clientid
  8. * @return string
  9. */
  10. function poll($psessionid,$clientid)
  11. {
  12. $post = "r=%7B%22clientid%22%3A%22{$clientid}%22%2C%22psessionid%22%3A%22{$psessionid}%22%2C%22key%22%3A0%2C%22ids%22%3A%5B%5D%7D&clientid={$clientid}&psessionid={$psessionid}";
  13. $ch = curl_init("http://d.web2.qq.com/channel/poll2");
  14. // 必须要来路域名
  15. curl_setopt($ch, CURLOPT_REFERER, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3");
  16. curl_setopt($ch, CURLOPT_COOKIEFILE, temp_dir."cookie");
  17. curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
  18. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  19.  
  20. return curl_exec($ch);
  21. }
  22. /* WebQQ3.0 core part end of */

3.获取QQ群列表

POST地址:http://s.web2.qq.com/api/get_group_name_list_mask2
POST值(请把参数值用urlencode函数编码)如下:
r={“vfwebqq”:”{$vfwebqq}”}
只有一个参数,很爽对吧?这个值在第二次登录的时候可得到,回头去找找看吧
附PHP版获取群列表函数代码如下:

  1. <?php
  2. /**
  3. * 获取群列表
  4. *
  5. * @access public
  6. * @param string $vfwebqq
  7. * @return string
  8. */
  9. function get_group_name_list_mask($vfwebqq)
  10. {
  11. $post = "r=%7B%22vfwebqq%22%3A%22{$vfwebqq}%22%7D";
  12. $ch = curl_init("http://s.web2.qq.com/api/get_group_name_list_mask2");
  13. curl_setopt($ch, CURLOPT_REFERER, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3");
  14. curl_setopt($ch, CURLOPT_COOKIEFILE, temp_dir."cookie");
  15. curl_setopt($ch, CURLOPT_POSTFIELDS,$post);
  16. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  17. return curl_exec($ch);
  18. }
  19. /* WebQQ3.0 core part end of */

4.获取好友列表

POST地址:http://s.web2.qq.com/api/get_user_friends2
POST值(请把参数值用urlencode函数编码)如下:
r={“h”:”hello”,”vfwebqq”:”{$vfwebqq}”}
vfwebqq的值在第二次登录的时候可得到,回头去找找看吧
(ps:在编辑本文时,发现这个请求链接的POST值已经加了一个参数hash,分析出来源后尽快补上,这对全局影响并不大)
附PHP版获取好友列表函数代码如下:

  1. <?php
  2. /**
  3. * 获取好友列表
  4. *
  5. * @access public
  6. * @param string $vfwebqq
  7. * @return string
  8. */
  9. function get_user_friend($vfwebqq)
  10. {
  11. $post = "r=%7B%22h%22%3A%22hello%22%2C%22vfwebqq%22%3A%22{$vfwebqq}%22%7D";
  12. $ch = curl_init("http://s.web2.qq.com/api/get_user_friends2");
  13. curl_setopt($ch, CURLOPT_REFERER, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3");
  14. curl_setopt($ch, CURLOPT_COOKIEFILE, temp_dir."cookie");
  15. curl_setopt($ch, CURLOPT_POSTFIELDS,$post);
  16. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  17. return curl_exec($ch);
  18. }
  19. /* WebQQ3.0 core part end of */

5.发送QQ消息

POST地址:http://d.web2.qq.com/channel/send_buddy_msg2
POST值(请把参数值用urlencode函数编码)如下:
r={“to”:{$from_uin},”face”:606,”content”:”[\"{$msg}\\n\",[\"font\",{\"name\":\"宋体\",\"size\":\"10\",\"style\":[0,0,0],\”color\”:\”000000\”}]]”,”msg_id”:{$msg_id},”clientid”:”{$clientid}”,”psessionid”:”{$psessionid}”}&clientid={$clientid}&psessionid={$psessionid}
部分参数解释:
to:好友的uin(非QQ号)
content:发送的消息内容
psessionid:在第二次登录返回的JSON信息里边能找到
clientid:与第二次登录时候的clientid相同
附PHP版发送QQ消息函数代码如下:

  1. <?php
  2. /**
  3. * 发送QQ消息
  4. *
  5. * @access public
  6. * @param int $from_uin
  7. * @param string $msg
  8. * @param string $psessionid
  9. * @param int $clientid
  10. * @return string
  11. */
  12. function send_buddy_msg($from_uin, $msg, $psessionid, $clientid)
  13. {
  14. static $msg_id=71830055;
  15. $msg_id++;
  16. $post = "r=%7B%22to%22%3A{$from_uin}%2C%22face%22%3A606%2C%22content%22%3A%22%5B%5C%22{$msg}%5C%5Cn%5C%22%2C%5B%5C%22font%5C%22%2C%7B%5C%22name%5C%22%3A%5C%22%E5%AE%8B%E4%BD%93%5C%22%2C%5C%22size%5C%22%3A%5C%2210%5C%22%2C%5C%22style%5C%22%3A%5B0%2C0%2C0%5D%2C%5C%22color%5C%22%3A%5C%22000000%5C%22%7D%5D%5D%22%2C%22msg_id%22%3A{$msg_id}%2C%22clientid%22%3A%22{$clientid}%22%2C%22psessionid%22%3A%22{$psessionid}%22%7D&clientid={$clientid}&psessionid={$psessionid}";
  17. $ch = curl_init("http://d.web2.qq.com/channel/send_buddy_msg2");
  18. // 必须要来路域名
  19. curl_setopt($ch, CURLOPT_REFERER, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3");
  20. curl_setopt($ch, CURLOPT_COOKIEFILE, "cookie");
  21. curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
  22. // curl_setopt($ch, CURLOPT_HEADER, TRUE);
  23. curl_exec($ch);
  24. }
  25. /* WebQQ3.0 core part end of */

6.发送QQ群消息

POST地址:http://d.web2.qq.com/channel/send_qun_msg2
POST值(请把参数值用urlencode函数编码)如下:
r={“group_uin”:{$group_id},”content”:”[\"{$msg}\\n\",[\"font\",{\"name\":\"宋体\",\"size\":\"10\",\"style\":[0,0,0],\”color\”:\”000000\”}]]”,”msg_id”:{$msg_id},”clientid”:”{$clientid}”,”psessionid”:”{$psessionid}”}&clientid={$clientid}&psessionid={$psessionid}
部分参数解释:
group_uin:群的uin(非QQ群号)
content:发送的消息内容
psessionid:在第二次登录返回的JSON信息里边能找到
clientid:与第二次登录时候的clientid相同
附PHP版发送QQ群消息函数代码如下:

  1. <?php
  2. /**
  3. * 发送群消息
  4. *
  5. * @access public
  6. * @param int $group_id
  7. * @param string $msg
  8. * @param string $psessionid
  9. * @param int $clientid
  10. * @return string
  11. */
  12. function send_qun_msg($group_id, $msg, $psessionid, $clientid)
  13. {
  14. static $msg_id = 77860003;
  15. $msg_id++;
  16. $post = "r=%7B%22group_uin%22%3A{$group_id}%2C%22content%22%3A%22%5B%5C%22{$msg}%5C%5Cn%5C%22%2C%5B%5C%22font%5C%22%2C%7B%5C%22name%5C%22%3A%5C%22%E5%AE%8B%E4%BD%93%5C%22%2C%5C%22size%5C%22%3A%5C%2210%5C%22%2C%5C%22style%5C%22%3A%5B0%2C0%2C0%5D%2C%5C%22color%5C%22%3A%5C%22000000%5C%22%7D%5D%5D%22%2C%22msg_id%22%3A{$msg_id}%2C%22clientid%22%3A%22{$clientid}%22%2C%22psessionid%22%3A%22{$psessionid}%22%7D&clientid={$clientid}&psessionid={$psessionid}";
  17. $ch = curl_init("http://d.web2.qq.com/channel/send_qun_msg2");
  18. curl_setopt($ch, CURLOPT_REFERER, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3");
  19. curl_setopt($ch, CURLOPT_COOKIEFILE, "cookie");
  20. curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
  21. // curl_setopt($ch, CURLOPT_HEADER, TRUE);
  22. curl_exec($ch);
  23. }
  24. /* WebQQ3.0 core part end of */

事实上,做到心跳包的时候,再往下已经没有技术含量了,已经属于体力活了,有兴趣的朋友可以研究研究。

基于WebQQ3.0协议写一个QQ机器人的更多相关文章

  1. 使用C# 开始第一个QQ机器人

    本示例将会使用”嘤鹉学舌”这个小插件的实现来演示如何使用Newbe.Mahua实现第一个机器人插件. 插件功能 自动将发送者的消息回发给发送人,嘤鹉(Parrot,其实是说嘤嘤嘤怪)学舌. 开发环境要 ...

  2. 从0开始写一个简单的vite hmr 插件

    从0开始写一个简单的vite hmr 插件 0. 写在前面 在构建前端项目的时候,除开基本的资源格式(图片,json)以外,还常常会需要导入一些其他格式的资源,这些资源如果没有第三方vite插件的支持 ...

  3. (转)如何基于FFMPEG和SDL写一个少于1000行代码的视频播放器

    原文地址:http://www.dranger.com/ffmpeg/ FFMPEG是一个很好的库,可以用来创建视频应用或者生成特定的工具.FFMPEG几乎为你把所有的繁重工作都做了,比如解码.编码. ...

  4. [闲的蛋疼系列]从零开始用TypeScript写React的UI组件(0)-先写一个Button??

    0.咸鱼要说的 一入前端深似海,咸鱼入海更加咸. 最近闲的蛋疼,手上年前的事也完成了7788了,借助[PG1]的话来说,我们要keep real. 咸鱼肯定不real 了,因为我们都活在梦里,所以咱们 ...

  5. QQ联合登录(基于Oauth2.0协议)

    1. 获取授权码Authorization Code https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id= ...

  6. 用python写一个预警机器人(支持微信和钉钉)

    背景 线上的系统在运行中,发生故障时怎么及时的通过手机通知到相关人员?当然这是个很简单的需求,现有的方法有很多,例如: 如果我们用的云产品,那么一般都会有配套对应的监控预警功能,根据需要配置一下即可, ...

  7. 基于滑动窗口协议写的程序(UDP实现) .

    正好有一个大作业关于用socket实现滑动窗口协议,所以写了一个,模拟接收方与发送方窗口都是2,用两个线程实现. 下面是代码,注释的比较详细了. socket_udp.h #include<st ...

  8. 基于Vue2.0+Vue-router构建一个简单的单页应用

    爱编程爱分享,原创文章,转载请注明出处,谢谢!http://www.cnblogs.com/fozero/p/6185492.html 一.介绍 vue.js 是 目前 最火的前端框架,vue.js ...

  9. [网络分析]WEBQQ3.0协议分析---good good study

    声明:研究学习使用,严禁商业化~~噗嗤,估计也没有商业化的 本文地址:http://blog.csdn.net/sushengmiyan/article/details/11906101 作者:sus ...

随机推荐

  1. day2----python的基本类型

    本文档的大致内容:(python使用版本3.6.4) 1 数字--int 2 布尔--bool 3 字符串--str 4 元祖--() 5  列表---['a','b'] 6 字典--{} 运算符: ...

  2. 安装Anaconda3进行python版本管理

    1.下载Anaconda3,我选择了python3的64位版本 2.windows安装,选择加入了系统目录 3.进入命令行进行版本安装 // 安装一个指定版本conda create --name p ...

  3. 转载 :实例详解Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化(一)

    在数据库有外键的时候,使用 select_related() 和 prefetch_related() 可以很好的减少数据库请求的次数,从而提高性能.本文通过一个简单的例子详解这两个函数的作用.虽然Q ...

  4. leetcode701

    class Solution: def insertIntoBST(self, root, val): """ Time: O(log(n)) [average case ...

  5. Nginx 服务器搭建

    什么是Nginx ? Nginx与Apache IIS等软件一样,是一款服务器软件,为web站点提供服务 除此之外,Nginx 还是一款反向代理服务器,我们可以利用Nginx实现负载均衡 所谓负载均衡 ...

  6. <基础> PHP 进阶之 类(Class)

    属性 类的变量成员叫做“属性”,或者叫“字段”.“特征”,在本文档统一称为“属性”.属性声明是由关键字 public,protected 或者 private 开头,然后跟一个普通的变量声明来组成.属 ...

  7. ThinkPHP模板内使用U方法

    为了配合所使用的URL模式,我们需要能够动态的根据当前的URL设置生成对应的URL地址,为此,ThinkPHP内置提供了U方法,用于URL的动态生成,可以确保项目在移植过程中不受环境的影响. 三种携带 ...

  8. 我的母校zbvc试做

    一.观察分析页面布局 可以从上至下分为6大部分 logo栏 menu菜单栏 slide幻灯片 news新闻区域 other其他 bottom底部 二.logo 分为三部分 ①左侧logo ②中间log ...

  9. ActiveMQ 学习

    链接:  http://www.cnblogs.com/zhuxiaojie/p/5564187.html#autoid-1-0-0

  10. WDA-5-VIEW视图切换

    这一部分介绍同一窗口下不同视图之间的链接跳转. 前提:完成上一步骤MAIN视图ALV显示. 1.效果展示 点击ALV物料下划线链接,页面跳转到物料明细页面. 2.实现过程 基于上一步骤在MAIN页面显 ...