基於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’,'\x00\x00\x00\x00\x0e\xe9\x41\xc1′);這樣的字符串,其中第一個字符串,’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)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\n/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 */

事實上,做到心跳包的時候,再往下已經沒有技術含量了,已經屬於體力活了,有興趣的朋友可以研究研究。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM