轉之http://hfutxf.javaeye.com/blog/800866
通過webqq接口,可以實現發送qq消息接收qq消息等,這樣,想實現一個qq聊天機器人,就不是什麼難事情了了,下面開始一步步做。
1。首先調用http://ptlogin2.qq.com/check?appid=1003903&uin=qq號碼,來獲取該qq號碼驗證碼之類的信息。看返回結果決定是不是要輸入驗證碼登陸。
如果返回:ptui_checkVC('1','95ab7db15e5ab17f50f25d33598259e83ccc098c4af2f8a4');需要輸入驗證碼,這裏需要記住這個長字符串(獲取驗證碼圖片用)以及cookie
如果返回:ptui_checkVC('0','!MPG');不需要輸入驗證碼,驗證碼值用!MPG代替。可能爲其他字符串,但是以感嘆號開頭
如果需要輸入驗證碼:則調用
http://captcha.qq.com/getimage?aid=1003903&&uin=qq號碼&vc_type=95ab7db15e5ab17f50f25d33598259e83ccc098c4af2f8a4
獲取驗證碼圖片。。。
2。開始登陸,在登陸之前,需要將密碼加密,tx的加密方法很複雜,不過還好,弄到了他的js文件,然後通過java的ScriptEnginee來執行這個js來獲取加密後的字符串。。。
代碼:
- ScriptEngineManager m = new ScriptEngineManager();
- ScriptEngine se = m.getEngineByName("javascript");
- se.eval(new FileReader(new File("1.js")));
- Object t = se.eval("md5(md5_3(/""+p+"/")+/""+code.toUpperCase()+"/");");
- return t.toString();
現在調用登陸接口,以獲得相關的cookie。注意Referer。這個一定得加上,否則不成功:Referer : http://web2-b.qq.com/proxy.html
http://ptlogin2.qq.com/login?u=qq號碼&p=密碼和驗證碼加密後的字符串&verifycode=驗 證碼&remember_uin=1&aid=1003903&u1=http%3A%2F %2Fweb2.qq.com%2Floginproxy.html%3Fstrong%
3Dtrue&h=1&ptredirect=0&ptlang=2052&from_ui=1&pttype=1&dumy=&fp=loginerroralert
登陸成功後,要記住返回的cookie值。
如其中ptwebqq,skey等。
3。這還沒有完成,還需要再次登錄,這個好像就是qq聊天接口登陸了(剛纔的登陸可以理解爲登陸qq網站),只有這次登陸,纔算真正登陸qq,這個時候,如果你qq已經登陸,會把你的qq踢下線,而且此次登陸纔算上線時間。
地址:http://web2-b.qq.com/channel/login,把一個數據結構post給它就夠了
r=%7B%22status%22%3A%22%22%2C%22ptwebqq%22%3A%22{1}%22%2C%22passwd_sig%22%3A%22%22%2C%22clientid%22%3A%22{2}%22%7D
其中{1}是ptwebqq,在第2步登陸後cookie返回的,{2}clientId,自己隨便定義一個數字吧。
如果成功:
會返回一個json數據結構:
{"retcode":0,"result":
{"uin":qq號 碼,"mode":"master","index":1055,"port":38138,"status":"online","vfwebqq":"f72a8722c988252aef4e0268f1d26a3d647f06f6ff353a5c6cdaaa49abb2fcdf0cee2d8d64373ac2","psessionid":"
8368046764001D636F6E6E7365727665725F77656271714031302E3133332E332E3234300000235100000B79026E040043F60C166D0000000A404746365677767041316D00000028F72A8722C988252AEF4E0268F1D26A3D647F06F6
FF353A5C6CDAAA49ABB2FCDF0CEE2D8D64373AC2"}}
記住其中的psessionid。後面在發送消息和獲取qq消息都需要這個參數。
4。發送一個qq消息給好友
地址:http://web2-b.qq.com/channel/send_msg
同樣是post:r={"to":qq號碼,"face":0,"content":"[/"23/",[/"font/",{/"name/":/"宋體/",/"size/":/"10/",/"style/":
[0,0,0],/"color/":/"000000
/"}]]","msg_id":7780001,"clientid":"15778909","psessionid":"8368046764001D636F6E6E7365727665725F77656271714031302E3133332E332E3234300000326F00000B71026E040043F60C166D0000000A4042725946
34574676716D00000028E7D8E44718236B0C17365E824FD3817ED2EF6C879FEE88D07EA92D030CEA72EE8E59309863128A3E"}
{"retcode":0,"result":"ok"}
需要把這個json UrlEncode一下再發送,否則會返回錯誤。
發送成功返回:{"retcode":0,"result":"ok"}
5。循環獲取消息接口:
通過這個接口你可以實時的不間斷的獲取最新的消息。
http://web2-b.qq.com/channel/poll?
clientid=15778909&psessionid=8368046764001D636F6E6E7365727665725F77656271714031302E3133332E332E3234300000326F00000B71026E040043F60C166D0000000A404272594634574676716D00000028E7D8E447182
36B0C17365E824FD3817ED2EF6C879FEE88D07EA92D030CEA72EE8E59309863128A3E&t=1288591644319
返回格式:
{"retcode":0,"result":[{"poll_type":"message","value": {"msg_id":9712,"from_uin":qq號碼,"to_uin":qq號 碼,"msg_id2":217523,"msg_type":9,"reply_ip":2887452740,"time":1288591740,"content":[["font",{"size":9,"color":"000000","style":
[0,0,0],"name":"/u5B8B/u4F53"}],"hello world"],"raw_content":"hello world"}}]}
其中的poll_type表示消息格式,message就是普通的qq消息,可以看到發送人,發送時間,以及消息的內容等。
此接口很特殊,在實現時,需要循環不間斷調用,如果沒有消息返回,該接口會一直等待到,有消息,讀取完後要立即再調用該接口。
6.其他接口
獲取頭像
http://face7.qun.qq.com/cgi/svr/face/getface?cache=0&type=1&fid=0&uin=號碼
獲取個人信息
http://web2-b.qq.com/api/get_single_info?tuin=qq號碼
獲取簽名
http://web2-b.qq.com/api/get_single_long_nick?tuin=qq號碼&t=1288751545148
獲取好友列表
http://web2-b.qq.com/api/get_user_friends
r {"vfwebqq":"8f1383ba2239bb7295b100af215274aff1ee4be177b467cbc386fc53ff6606a8e5941aca61d0eb51"}
獲取在線的qq好友
http://web2-b.qq.com/channel/get_online_buddies?clientid=9547083&psessionid=8368046764001D636F6E6E7365727665725F77656271714031302E3133332E332E323430000062F000000B86026E040043F60C166D0000000A404F526B7558357668476D000000288F1383BA2239BB7295B100AF215274AFF1EE4BE177B467CBC386FC53FF6606A8E5941ACA61D0EB51&t=1288751548600
獲取最近聯繫人
http://web2-b.qq.com/api/get_recent_contact
r {"vfwebqq":"8f1383ba2239bb7295b100af215274aff1ee4be177b467cbc386fc53ff6606a8e5941aca61d0eb51"}
等等。。。
7.附件這是本人通過java寫的一個實例客戶端,啓動後,處於接受qq消息狀態,當收到好友發來的消息時,回返回"然後呢?"。。。。
類似qq聊天機器人吧。。。
1.js是qq密碼的加密js文件。
。。
完整代碼:
- import java.awt.image.BufferedImage;
- import java.io.BufferedReader;
- import java.io.File;
- import java.io.FileReader;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.net.HttpURLConnection;
- import java.net.URL;
- import java.net.URLEncoder;
- import java.util.Date;
- import java.util.Random;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- import javax.imageio.ImageIO;
- import javax.script.ScriptEngine;
- import javax.script.ScriptEngineManager;
- import atg.taglib.json.util.JSONArray;
- import atg.taglib.json.util.JSONException;
- import atg.taglib.json.util.JSONObject;
- public class QQClient {
- private int qq = -1;
- private String pwd = null;
- private int clientid = 66933334;//這個可以隨便寫
- private String psessionid = "";
- private String ptwebqq;
- private String vfwebqq;
- private String skey;
- private String refer = "http://web2-b.qq.com/proxy.html";
- private String cookie = "";
- //讀取消息線程
- private boolean isrun = false;
- private Thread poolThread =new PollThread();
- public Thread getPoolThread() {
- return poolThread;
- }
- /**
- * 記錄日誌
- */
- private void log(String msg){
- System.out.println(new Date().toLocaleString()+":"+msg);
- }
- public QQClient(int qq, String pwd) {
- this.qq = qq;
- this.pwd = pwd;
- try {
- boolean rs = checkAndLogin();
- if(rs){
- isrun = true;
- poolThread.start();//開始循環接收
- log("啓動成功");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /*****************華麗的分界線*****************/
- //測試
- public static void main(String[] args) throws Exception{
- QQClient q = new QQClient(qq號碼, "123");
- q.getPoolThread().join();
- }
- /*****************華麗的分界線*****************/
- /**
- * 給toQQ發送一個msg消息,前提是toQQ是你的好友,要不然他收不到
- */
- public boolean sendMsg(int toQQ, String message){
- try {
- JSONObject json = new JSONObject();
- json.put("to", toQQ);//要發送的人
- json.put("face", 0);
- JSONArray msg = new JSONArray();
- msg.add(message);
- JSONArray font = new JSONArray();
- font.add("font");
- JSONObject font1 = new JSONObject().put("name", "宋體").put("size", "10");
- JSONArray style = new JSONArray();
- style.add(0);
- style.add(0);
- style.add(0);
- font1.put("style", style);
- font1.put("color", "000000");
- font.add(font1);
- msg.add(font);
- json.put("content", msg.toString());
- json.put("msg_id", new Random().nextInt(10000000));
- json.put("clientid", clientid);
- json.put("psessionid", psessionid);//需要這個才能發送
- String sendMsgUrl = "http://web2-b.qq.com/channel/send_msg";
- String content = json.toString();
- content = URLEncoder.encode(content);//他要需要編碼
- content ="r="+content;
- //發送
- String res = postUrl(sendMsgUrl, content);
- //不出意外,這是返回結果:{"retcode":0,"result":"ok"}
- JSONObject rh = new JSONObject(res);
- if("ok".equals(rh.getString("result"))){
- return true;
- }
- } catch (JSONException e) {
- e.printStackTrace();
- }
- return false;
- }
- /**
- * 檢查並且登陸
- */
- private boolean checkAndLogin() throws Exception{
- if(qq == -1 || pwd == null)
- throw new IllegalArgumentException("qq和密碼不能爲空");
- String checkIdUrl = "http://ptlogin2.qq.com/check?appid=1003903&uin="+qq;
- String res = getUrl(checkIdUrl);
- //ptui_checkVC('0','!ZLE');返回這個就不需要獲取驗證碼了。驗證碼就是!ZLE
- //ptui_checkVC('1','95ab7db15e5ab17f50f25d33598259e83ccc098c4af2f8a4');這個長字符串就需要使用了
- Pattern p = Pattern. compile("//,//'([!//w]+)//'");
- Matcher m = p. matcher(res);
- String checkType = "";
- if(m.find()){
- checkType = m.group(1);
- }
- String check = "";
- if(!checkType.startsWith("!")){
- //需要輸入驗證碼
- String getCheckImageUrl = "http://captcha.qq.com/getimage?aid=1003903&uin="+qq+"&vc_type="+checkType;
- String file = readCheckImage(getCheckImageUrl);
- log("請打開"+file+",並且在這裏輸入其中的字符串,然後回車:");
- InputStreamReader ins = new InputStreamReader(System.in);
- BufferedReader br = new BufferedReader(ins);
- check = br.readLine();
- }else{
- //不需要輸入驗證碼
- check = checkType;
- }
- //開始登陸
- String loginUrl = "http://ptlogin2.qq.com/login?u="+qq+"&" +
- "p=" +mdP(pwd, check)+
- "&verifycode="+check+"&remember_uin=1&aid=1003903" +
- "&u1=http%3A%2F%2Fweb2.qq.com%2Floginproxy.html%3Fstrong%3Dtrue" +
- "&h=1&ptredirect=0&ptlang=2052&from_ui=1&pttype=1&dumy=&fp=loginerroralert";
- res = getUrl(loginUrl);
- // ptuiCB('0','0','http://web2.qq.com/loginproxy.html?strong=true','0','登錄成功!');
- // ptuiCB('4','0','','0','您輸入的驗證碼有誤,請重試。');
- p = Pattern.compile("登錄成功!");//提取最後一個字符串,看看是不是 登錄成功!
- m = p. matcher(res);
- if(m.find()){
- log("登陸成功");
- }else{
- //登陸失敗
- log(checkType);
- return false;
- }
- //從cookie中提取ptwebqq,skey
- p = Pattern.compile("ptwebqq=(//w+);");
- m = p.matcher(cookie);
- if(m.find()){
- ptwebqq = m.group(1);
- }
- p = Pattern.compile("skey=(@//w+);");
- m = p.matcher(cookie);
- if(m.find()){
- skey = m.group(1);
- }
- log("ptwebqq="+ptwebqq+",skey="+skey);
- //再次登陸,只有這次登陸,纔算真正登陸qq,這個時候,如果你qq已經登陸,會把你的qq踢下線,而且此次登陸纔算上線。
- String channelLoginUrl = "http://web2-b.qq.com/channel/login";
- String content = "{/"status/":/"/",/"ptwebqq/":/""+ptwebqq+"/",/"passwd_sig/":/"/",/"clientid/":/""+clientid+"/"}";
- content = URLEncoder.encode(content);//urlencode
- content = "r="+content;//post的數據
- res = postUrl(channelLoginUrl, content);//post
- //這次登陸基本上不會發生什麼問題
- //下面提取很重要的2個數據psessionid ,vwebqq,通用採用正則表達式,雖然結果是個json
- p = Pattern.compile("/"vfwebqq/":/"(//w+)/"");
- m = p.matcher(res);
- if(m.find()){
- vfwebqq = m.group(1);
- }
- p = Pattern.compile("/"psessionid/":/"(//w+)/"");
- m = p.matcher(res);
- if(m.find()){
- psessionid = m.group(1);
- }
- log("vwebqq="+vfwebqq+","+"psessionid="+psessionid);
- //到此,登陸就算完成了,後面可以調用發送qq信息等接口了
- return true;
- }
- /**
- * 調用tx的js來生成密鑰
- */
- public String mdP(String p, String code){
- try {
- ScriptEngineManager m = new ScriptEngineManager();
- ScriptEngine se = m.getEngineByName("javascript");
- se.eval(new FileReader(new File("1.js")));
- Object t = se.eval("md5(md5_3(/""+p+"/")+/""+code.toUpperCase()+"/");");
- return t.toString();
- }catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- /**
- * POST一個url,contents是輸入的內容
- */
- private String postUrl(String url, String contents){
- try{
- System.out.println("post>>>"+url);
- URL serverUrl = new URL(url);
- HttpURLConnection conn = (HttpURLConnection) serverUrl.openConnection();
- conn.setRequestMethod("POST");//"POST" ,"GET"
- if(refer != null){
- conn.addRequestProperty("Referer", refer);
- }
- conn.addRequestProperty("Cookie", cookie);
- conn.addRequestProperty("Accept-Charset", "UTF-8;");//GB2312,
- conn.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.8) Firefox/3.6.8");
- conn.setDoOutput(true);
- conn.connect();
- conn.getOutputStream().write(contents.getBytes());
- if(conn.getHeaderFields().get("Set-Cookie") != null){
- for(String s:conn.getHeaderFields().get("Set-Cookie")){
- cookie += s;
- }
- }
- InputStream ins = conn.getInputStream();
- String charset = "UTF-8";
- InputStreamReader inr = new InputStreamReader(ins, charset);
- BufferedReader bfr = new BufferedReader(inr);
- String line = "";
- StringBuffer res = new StringBuffer();
- do{
- res.append(line);
- line = bfr.readLine();
- //System.out.println(line);
- }while(line != null);
- System.out.println(">>>==="+res);
- return res.toString();
- }catch(Exception e){
- e.printStackTrace();
- return null;
- }
- }
- /**
- * GET 一個url
- */
- private String getUrl(String url){
- try{
- System.out.println("get>>>"+url);
- URL serverUrl = new URL(url);
- HttpURLConnection conn = (HttpURLConnection) serverUrl.openConnection();
- conn.setRequestMethod("GET");//"POST" ,"GET"
- // conn.setDoOutput(true);
- if(refer != null){
- conn.addRequestProperty("Referer", refer);
- }
- conn.addRequestProperty("Cookie", cookie);
- conn.addRequestProperty("Accept-Charset", "UTF-8;");//GB2312,
- conn.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.8) Firefox/3.6.8");
- conn.connect();
- if(conn.getHeaderFields().get("Set-Cookie") != null){
- for(String s:conn.getHeaderFields().get("Set-Cookie")){
- cookie += s;
- }
- }
- InputStream ins = conn.getInputStream();
- String charset = "UTF-8";
- InputStreamReader inr = new InputStreamReader(ins, charset);
- BufferedReader bfr = new BufferedReader(inr);
- String line = "";
- StringBuffer res = new StringBuffer();
- do{
- res.append(line);
- line = bfr.readLine();
- //System.out.println(line);
- }while(line != null);
- System.out.println(">>>==="+res);
- return res.toString();
- }catch(Exception e){
- e.printStackTrace();
- return null;
- }
- }
- /**
- * 讀取驗證碼。返回驗證碼文件保存的路徑
- */
- private String readCheckImage(String url){
- try{
- System.out.println("get>>>"+url);
- URL serverUrl = new URL(url);
- HttpURLConnection conn = (HttpURLConnection) serverUrl.openConnection();
- conn.setRequestMethod("GET");//"POST" ,"GET"
- conn.addRequestProperty("Accept-Charset", "UTF-8;");//GB2312,
- conn.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.8) Firefox/3.6.8");
- conn.connect();
- //返回的cookie
- if(conn.getHeaderFields().get("Set-Cookie") != null)
- for(String s:conn.getHeaderFields().get("Set-Cookie")){
- cookie += s;
- }
- InputStream ins = conn.getInputStream();
- BufferedImage bi = ImageIO.read(ins);
- File f =new File("qqimg.jpg");
- ImageIO.write(bi, "jpg", f);
- return f.getAbsolutePath();
- }catch(Exception e){
- e.printStackTrace();
- }
- return null;
- }
- /**
- * 當有qq消息是回調此函數
- */
- public void receiveMsg(String message, int fromQQ){
- log("qq:"+fromQQ+"說:"+message);
- //test
- sendMsg(fromQQ, "然後呢?");
- }
- /**
- * 通過poll一直等待服務器迴應。比如收到消息啊,好友上線啊,申請好友啊之類的各類消息都會通過此接口返回,在獲得數據後,應該繼續poll獲取下個數據
- * http://web2-b.qq.com/channel/poll
- */
- class PollThread extends Thread{
- private String pollUrl = "http://web2-b.qq.com/channel/poll";
- @Override
- public void run() {
- String url = pollUrl+ "?clientid="+clientid+"&psessionid="+psessionid;
- try {
- while(isrun){
- //線程一直等待知道服務器有返回數據
- String res = getUrl(url);
- JSONObject retJ = new JSONObject(res);
- if(retJ.getInt("retcode") == 0){
- JSONArray result = retJ.getJSONArray("result");
- String poll_type = result.getJSONObject(0).getString("poll_type");
- if("message".equals(poll_type)){
- //說明有人發qq消息給我,
- String raw_content = result.getJSONObject(0).getJSONObject("value").get("raw_content").toString();
- int from_uin = result.getJSONObject(0).getJSONObject("value").getInt("from_uin");
- log("收到來自:"+from_uin+":"+raw_content);
- //通知客戶端收到了
- receiveMsg(raw_content, from_uin);
- }
- //system_message 是系統消息
- }
- }
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
- }
- }