• 基于WebQQ3.0协议写一个QQ机器人


    最近公司需要做个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’,'x00x00x00x00x0exe9x41xc1′);这样的字符串,其中第一个字符串,’0′代表不需要验证码,’!TMX’这个数据,是等会登陆需要的,第三个字符串加密密码的时候会用到。
    附PHP代码如下:

    <?php
    /**
     * 获取验证码
     * 
     * @access public
     * @param int $uid
     * @return array
     */
    function check_verify($uid)
    {
        $ch = curl_init("https://ssl.ptlogin2.qq.com/check?uin={$uid}&appid=1003903&r=0.14233942252344134");
        $cookie = "confirmuin=0; ptvfsession=b1235b1729e7808d5530df1dcfda2edd94aabec43bf450d8cf037510802aa1a7dbed494c66577479895c62efa3ef35ab; ptisp=cnc";
        curl_setopt($ch, CURLOPT_COOKIE, $cookie);
        curl_setopt($ch, CURLOPT_COOKIEFILE, temp_dir."cookie");
        curl_setopt($ch, CURLOPT_COOKIEJAR, temp_dir."cookie");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $data = curl_exec($ch);
        if (preg_match("/ptui_checkVC('(.*)','(.*)','(.*)');/", $data, $verify))
        {   
            return array_slice($verify, 1);
        }       
    }
    /* 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版登录函数以及加密函数代码如下:

    <?php
    /**
     * WEBQQ3.0 新版登陆加密函数
     * 
     * @access public
     * @param string $p
     * @param string $pt
     * @param string $vc
     * @param boolean $md5
     * @return string
     */
    function jspassword($p,$pt,$vc,$md5 = true)
    {
        if ($md5)
        {
            $p = strtoupper(md5($p));
        }
        $len = strlen($p);
        $temp = null;
        for ($i=0; $i < $len ; $i = $i + 2)
        {
            $temp .= 'x'.substr($p, $i,2);
        }
        return strtoupper(md5(strtoupper(md5(hex2asc($temp).hex2asc($pt))).$vc));
    }
     
    /**
     * 十六进制转字符
     * 
     * @access private
     * @param string $str
     * @return string
     */
    function hex2asc($str)
    {
        $str = join('', explode('x', $str));
        $len = strlen($str);
        $data = null;
        for ($i=0;$i<$len;$i+=2)
        {
            $data .= chr(hexdec(substr($str,$i,2)));
        }
        return $data;
    }
     
    /**
     * 登录
     * 
     * @access public
     * @param int $uid
     * @param string $passwd
     * @param string $verify
     * @return array
     */
    function login($uid, $passwd, $verify)
    {
        $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";
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_COOKIEFILE, temp_dir."cookie");
        curl_setopt($ch, CURLOPT_COOKIEJAR, temp_dir."cookie");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $data = curl_exec($ch);
        if (preg_match("/ptuiCB('(.*)','(.*)','(.*)','(.*)','(.*)',s'(.*)');/U", $data, $verify))
        {   
            return array_slice($verify, 1);
        }       
    }
    /* 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函数以及登录函数代码如下:

    <?php
    /**
     * 解析cookie
     *
     * @access public
     * @return array
     */
    function parse_cookie()
    {
        // Netscape HTTP Cookie File
        $cookies = file(temp_dir."cookie");
     
        $data = array();
        foreach ($cookies as $v) 
        {   
            if (preg_match("/(.*.qq.com)	(.*)	(.*)	(.*)	(.*)	(.*)	(.*)
    /U", $v, $p))
            {   
                $data[] = array_slice($p, 1); 
            }   
        }   
     
        return $data;
    }
     
    /**
     * 获取cookie
     * 
     * public 
     * @param array $cookie
     * @return array
     */
    function get_cookie($cookie = NULL)
    {
        if ($cookie === NULL)
        {   
            $cookie = parse_cookie();
        }   
     
        if (is_array($cookie) && count($cookie)<=6)
        {
            return FALSE;
        }
     
        foreach ($cookie as $v)
        {
            $data[$v[5]] =$v[6];
        }
     
        return $data;
    }
     
    /**
     * 真正的登录(上线)
     * 
     * @access public
     * @param string $ptwebqq
     * @return string
     */
    function login2($ptwebqq,$clientid)
    {
        $url = "http://d.web2.qq.com/channel/login2";
        $ch = curl_init($url);
        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");
        // 必须要来路域名
        curl_setopt($ch, CURLOPT_REFERER, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=2");
    //  curl_setopt($ch, CURLOPT_HEADER, TRUE);
        curl_setopt($ch, CURLOPT_COOKIEFILE, temp_dir."cookie");
        curl_setopt($ch, CURLOPT_COOKIEJAR, temp_dir."cookie");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        return curl_exec($ch);
    }
    /* 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版心跳请求函数代码如下:

    <?php
    /**
     * 心跳包(获取消息)
     * 
     * @access public
     * @param string $psessionid
     * @param int $clientid
     * @return string
     */
    function poll($psessionid,$clientid)
    {
        $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}";
        $ch = curl_init("http://d.web2.qq.com/channel/poll2");
        // 必须要来路域名
        curl_setopt($ch, CURLOPT_REFERER, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3");
        curl_setopt($ch, CURLOPT_COOKIEFILE, temp_dir."cookie");
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
     
        return curl_exec($ch);
    }
    /* 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版获取群列表函数代码如下:

    <?php
    /**
     * 获取群列表
     * 
     * @access public
     * @param string $vfwebqq
     * @return string
     */
    function get_group_name_list_mask($vfwebqq)
    {
        $post = "r=%7B%22vfwebqq%22%3A%22{$vfwebqq}%22%7D";
        $ch = curl_init("http://s.web2.qq.com/api/get_group_name_list_mask2");
        curl_setopt($ch, CURLOPT_REFERER, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3");
        curl_setopt($ch, CURLOPT_COOKIEFILE, temp_dir."cookie");
        curl_setopt($ch, CURLOPT_POSTFIELDS,$post);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        return curl_exec($ch);
    }
    /* 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版获取好友列表函数代码如下:

    <?php
    /**
     * 获取好友列表
     * 
     * @access public
     * @param string $vfwebqq
     * @return string
     */
    function get_user_friend($vfwebqq)
    {
        $post = "r=%7B%22h%22%3A%22hello%22%2C%22vfwebqq%22%3A%22{$vfwebqq}%22%7D";
        $ch = curl_init("http://s.web2.qq.com/api/get_user_friends2");
        curl_setopt($ch, CURLOPT_REFERER, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3");
        curl_setopt($ch, CURLOPT_COOKIEFILE, temp_dir."cookie");
        curl_setopt($ch, CURLOPT_POSTFIELDS,$post);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        return curl_exec($ch);
    }
    /* 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消息函数代码如下:

    <?php
    /**
     * 发送QQ消息
     * 
     * @access public
     * @param int $from_uin
     * @param string $msg
     * @param string $psessionid
     * @param int $clientid
     * @return string
     */
    function send_buddy_msg($from_uin, $msg, $psessionid, $clientid)
    {
        static $msg_id=71830055;
        $msg_id++;
        $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}";
        $ch = curl_init("http://d.web2.qq.com/channel/send_buddy_msg2");
        // 必须要来路域名
        curl_setopt($ch, CURLOPT_REFERER, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3");
        curl_setopt($ch, CURLOPT_COOKIEFILE, "cookie");
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
    //  curl_setopt($ch, CURLOPT_HEADER, TRUE);
        curl_exec($ch);
    }
    /* 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群消息函数代码如下:

    <?php
    /**
     * 发送群消息
     * 
     * @access public
     * @param int $group_id
     * @param string $msg
     * @param string $psessionid
     * @param int $clientid
     * @return string
     */
    function send_qun_msg($group_id, $msg, $psessionid, $clientid)
    {
        static $msg_id = 77860003;
        $msg_id++;
        $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}";
        $ch = curl_init("http://d.web2.qq.com/channel/send_qun_msg2");
        curl_setopt($ch, CURLOPT_REFERER, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3");
        curl_setopt($ch, CURLOPT_COOKIEFILE, "cookie");
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
    //  curl_setopt($ch, CURLOPT_HEADER, TRUE);
        curl_exec($ch);
    }
    /* WebQQ3.0 core part end of */

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

  • 相关阅读:
    图的概述
    "《算法导论》之‘排序’":线性时间排序
    “《算法导论》之‘查找’”:散列表
    如何使用VS2013本地C++单元测试框架
    “《算法导论》之‘查找’”:顺序查找和二分查找
    查找算法概述
    第二部分 位运算符、赋值运算符、三元及一元运算符和语句分类
    LINQ 的查询_联表、分组、排序
    第二部分 关系与比较运算符 、 自增与自减运算符、条件逻辑运算符
    LINQ to Sql系列一 增,删,改
  • 原文地址:https://www.cnblogs.com/jasonxiaoqinde/p/8022189.html
Copyright © 2020-2023  润新知