天天看点

webqq2协议分析和qq聊天机器人简单实现

转之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来获取加密后的字符串。。。

代码:

Java代码

  1. ScriptEngineManager m = new ScriptEngineManager();  
  2. ScriptEngine se = m.getEngineByName("javascript");  
  3. se.eval(new FileReader(new File("1.js")));  
  4. Object t = se.eval("md5(md5_3(/""+p+"/")+/""+code.toUpperCase()+"/");");  
  5. 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&ptretcode":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文件。

。。

完整代码:

Java代码

  1. import java.awt.image.BufferedImage;  
  2. import java.io.BufferedReader;  
  3. import java.io.File;  
  4. import java.io.FileReader;  
  5. import java.io.InputStream;  
  6. import java.io.InputStreamReader;  
  7. import java.net.HttpURLConnection;  
  8. import java.net.URL;  
  9. import java.net.URLEncoder;  
  10. import java.util.Date;  
  11. import java.util.Random;  
  12. import java.util.regex.Matcher;  
  13. import java.util.regex.Pattern;  
  14. import javax.imageio.ImageIO;  
  15. import javax.script.ScriptEngine;  
  16. import javax.script.ScriptEngineManager;  
  17. import atg.taglib.json.util.JSONArray;  
  18. import atg.taglib.json.util.JSONException;  
  19. import atg.taglib.json.util.JSONObject;  
  20. public class QQClient {  
  21.     private int qq = -1;  
  22.     private String pwd = null;  
  23.     private int clientid = 66933334;//这个可以随便写  
  24.     private String psessionid = "";  
  25.     private String ptwebqq;  
  26.     private String vfwebqq;  
  27.     private String skey;  
  28.     private String refer = "http://web2-b.qq.com/proxy.html";  
  29.     private String cookie = "";  
  30.     //读取消息线程  
  31.     private boolean isrun = false;  
  32.     private Thread poolThread =new PollThread();  
  33.     public Thread getPoolThread() {  
  34.         return poolThread;  
  35.     }  
  36.     private void log(String msg){  
  37.         System.out.println(new Date().toLocaleString()+":"+msg);  
  38.     }  
  39.     public QQClient(int qq, String pwd) {   
  40.         this.qq = qq;  
  41.         this.pwd = pwd;  
  42.         try {  
  43.             boolean rs = checkAndLogin();  
  44.             if(rs){   
  45.                 isrun = true;  
  46.                 poolThread.start();//开始循环接收  
  47.                 log("启动成功");  
  48.             }  
  49.         } catch (Exception e) {   
  50.             e.printStackTrace();  
  51.         }  
  52.     }  
  53.     //测试  
  54.     public static void main(String[] args) throws Exception{  
  55.         QQClient q = new QQClient(qq号码, "123");   
  56.         q.getPoolThread().join();  
  57.     }  
  58.     public boolean sendMsg(int toQQ, String message){  
  59.         try {  
  60.             JSONObject json = new JSONObject();  
  61.             json.put("to", toQQ);//要发送的人  
  62.             json.put("face", 0);  
  63.             JSONArray msg = new JSONArray();  
  64.             msg.add(message);  
  65.             JSONArray font = new JSONArray();  
  66.             font.add("font");  
  67.             JSONObject font1 = new JSONObject().put("name", "宋体").put("size", "10");  
  68.             JSONArray style = new JSONArray();  
  69.             style.add(0);  
  70.             style.add(0);  
  71.             style.add(0);         
  72.             font1.put("style", style);  
  73.             font1.put("color", "000000");  
  74.             font.add(font1);       
  75.             msg.add(font);  
  76.             json.put("content", msg.toString());  
  77.             json.put("msg_id", new Random().nextInt(10000000));  
  78.             json.put("clientid", clientid);  
  79.             json.put("psessionid", psessionid);//需要这个才能发送  
  80.             String sendMsgUrl = "http://web2-b.qq.com/channel/send_msg";  
  81.             String content = json.toString();  
  82.             content = URLEncoder.encode(content);//他要需要编码  
  83.             content ="r="+content;  
  84.             //发送  
  85.             String res = postUrl(sendMsgUrl, content);  
  86.             //不出意外,这是返回结果:{"retcode":0,"result":"ok"}  
  87.             JSONObject rh = new JSONObject(res);  
  88.             if("ok".equals(rh.getString("result"))){  
  89.                 return true;  
  90.             }   
  91.         } catch (JSONException e) {   
  92.             e.printStackTrace();  
  93.         }  
  94.         return false;  
  95.     }  
  96.     private boolean checkAndLogin() throws Exception{  
  97.         if(qq == -1 || pwd == null)  
  98.             throw new IllegalArgumentException("qq和密码不能为空");  
  99.         String checkIdUrl = "http://ptlogin2.qq.com/check?appid=1003903&uin="+qq;  
  100.         String res = getUrl(checkIdUrl);  
  101.         //ptui_checkVC('0','!ZLE');返回这个就不需要获取验证码了。验证码就是!ZLE   
  102.         //ptui_checkVC('1','95ab7db15e5ab17f50f25d33598259e83ccc098c4af2f8a4');这个长字符串就需要使用了  
  103.         Pattern p = Pattern. compile("//,//'([!//w]+)//'");  
  104.         Matcher m = p. matcher(res);  
  105.         String checkType = "";  
  106.         if(m.find()){  
  107.             checkType = m.group(1);   
  108.         }  
  109.         String check = "";   
  110.         if(!checkType.startsWith("!")){  
  111.             //需要输入验证码  
  112.             String getCheckImageUrl = "http://captcha.qq.com/getimage?aid=1003903&uin="+qq+"&vc_type="+checkType;  
  113.             String file = readCheckImage(getCheckImageUrl);  
  114.             log("请打开"+file+",并且在这里输入其中的字符串,然后回车:");  
  115.             InputStreamReader ins = new InputStreamReader(System.in);  
  116.             BufferedReader br = new BufferedReader(ins);  
  117.             check = br.readLine();    
  118.         }else{  
  119.             //不需要输入验证码  
  120.             check = checkType;  
  121.         }  
  122.         //开始登陆  
  123.         String loginUrl = "http://ptlogin2.qq.com/login?u="+qq+"&" +  
  124.                 "p=" +mdP(pwd, check)+  
  125.                 "&verifycode="+check+"&remember_uin=1&aid=1003903" +  
  126.                 "&u1=http%3A%2F%2Fweb2.qq.com%2Floginproxy.html%3Fstrong%3Dtrue" +  
  127.                 "&h=1&ptredirect=0&pt;  
  128.         res = getUrl(loginUrl);  
  129. //      ptuiCB('0','0','http://web2.qq.com/loginproxy.html?strong=true','0','登录成功!');  
  130. //      ptuiCB('4','0','','0','您输入的验证码有误,请重试。');  
  131.         p = Pattern.compile("登录成功!");//提取最后一个字符串,看看是不是 登录成功!  
  132.         m = p. matcher(res);  
  133.         if(m.find()){  
  134.             log("登陆成功");   
  135.         }else{  
  136.             //登陆失败  
  137.             log(checkType);  
  138.             return false;  
  139.         }  
  140.         //从cookie中提取ptwebqq,skey  
  141.         p = Pattern.compile("ptwebqq=(//w+);");  
  142.         m = p.matcher(cookie);  
  143.         if(m.find()){  
  144.             ptwebqq = m.group(1);  
  145.         }  
  146.         p = Pattern.compile("skey=(@//w+);");  
  147.         m = p.matcher(cookie);  
  148.         if(m.find()){  
  149.             skey = m.group(1);  
  150.         }  
  151.         log("ptwebqq="+ptwebqq+",skey="+skey);  
  152.         //再次登陆,只有这次登陆,才算真正登陆qq,这个时候,如果你qq已经登陆,会把你的qq踢下线,而且此次登陆才算上线。  
  153.         String channelLoginUrl = "http://web2-b.qq.com/channel/login";  
  154.         String content = "{/"status/":/"/",/"ptwebqq/":/""+ptwebqq+"/",/"passwd_sig/":/"/",/"clientid/":/""+clientid+"/"}";  
  155.         content = URLEncoder.encode(content);//urlencode   
  156.         content = "r="+content;//post的数据  
  157.         res = postUrl(channelLoginUrl, content);//post  
  158.         //这次登陆基本上不会发生什么问题  
  159.         //下面提取很重要的2个数据psessionid ,vwebqq,通用采用正则表达式,虽然结果是个json  
  160.         p = Pattern.compile("/"vfwebqq/":/"(//w+)/"");  
  161.         m = p.matcher(res);  
  162.         if(m.find()){  
  163.             vfwebqq = m.group(1);  
  164.         }  
  165.         p = Pattern.compile("/"psessionid/":/"(//w+)/"");  
  166.         m = p.matcher(res);  
  167.         if(m.find()){  
  168.             psessionid = m.group(1);  
  169.         }  
  170.         log("vwebqq="+vfwebqq+","+"psessionid="+psessionid);  
  171.         //到此,登陆就算完成了,后面可以调用发送qq信息等接口了         
  172.          return true;  
  173.     }  
  174.     public  String mdP(String p, String code){  
  175.         try {  
  176.             ScriptEngineManager m = new ScriptEngineManager();  
  177.             ScriptEngine se = m.getEngineByName("javascript");  
  178.             se.eval(new FileReader(new File("1.js")));  
  179.             Object t = se.eval("md5(md5_3(/""+p+"/")+/""+code.toUpperCase()+"/");");  
  180.             return t.toString();  
  181.         }catch (Exception e) {  
  182.             e.printStackTrace();  
  183.         }   
  184.         return null;  
  185.     }  
  186.     private  String postUrl(String url, String contents){  
  187.         try{   
  188.             System.out.println("post>>>"+url);  
  189.             URL serverUrl = new URL(url);  
  190.             HttpURLConnection conn = (HttpURLConnection) serverUrl.openConnection();   
  191.             conn.setRequestMethod("POST");//"POST" ,"GET"   
  192.             if(refer != null){  
  193.                 conn.addRequestProperty("Referer", refer);  
  194.             }  
  195.             conn.addRequestProperty("Cookie", cookie);  
  196.             conn.addRequestProperty("Accept-Charset", "UTF-8;");//GB2312,  
  197.             conn.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.8) Firefox/3.6.8");  
  198.             conn.setDoOutput(true);   
  199.             conn.connect();  
  200.             conn.getOutputStream().write(contents.getBytes());  
  201.             if(conn.getHeaderFields().get("Set-Cookie") != null){  
  202.                 for(String s:conn.getHeaderFields().get("Set-Cookie")){  
  203.                     cookie += s;  
  204.                 }  
  205.             }  
  206.             InputStream ins =  conn.getInputStream();  
  207.             String charset = "UTF-8";   
  208.             InputStreamReader inr = new InputStreamReader(ins, charset);  
  209.             BufferedReader bfr = new BufferedReader(inr);  
  210.             String line = "";  
  211.             StringBuffer res = new StringBuffer();   
  212.             do{  
  213.                 res.append(line);  
  214.                 line = bfr.readLine();  
  215.                //System.out.println(line);  
  216.             }while(line != null);  
  217.             System.out.println(">>>==="+res);  
  218.             return res.toString();  
  219.         }catch(Exception e){  
  220.             e.printStackTrace();  
  221.             return null;  
  222.         }  
  223.     }  
  224.     private  String getUrl(String url){  
  225.         try{   
  226.             System.out.println("get>>>"+url);  
  227.             URL serverUrl = new URL(url);  
  228.             HttpURLConnection conn = (HttpURLConnection) serverUrl.openConnection();   
  229.             conn.setRequestMethod("GET");//"POST" ,"GET"  
  230.            // conn.setDoOutput(true);   
  231.             if(refer != null){  
  232.                 conn.addRequestProperty("Referer", refer);  
  233.             }  
  234.             conn.addRequestProperty("Cookie", cookie);  
  235.             conn.addRequestProperty("Accept-Charset", "UTF-8;");//GB2312,  
  236.             conn.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.8) Firefox/3.6.8");  
  237.             conn.connect();  
  238.             if(conn.getHeaderFields().get("Set-Cookie") != null){  
  239.                 for(String s:conn.getHeaderFields().get("Set-Cookie")){  
  240.                     cookie += s;  
  241.                 }  
  242.             }  
  243.             InputStream ins =  conn.getInputStream();  
  244.             String charset = "UTF-8";   
  245.             InputStreamReader inr = new InputStreamReader(ins, charset);  
  246.             BufferedReader bfr = new BufferedReader(inr);  
  247.             String line = "";  
  248.             StringBuffer res = new StringBuffer();   
  249.             do{  
  250.                 res.append(line);  
  251.                 line = bfr.readLine();  
  252.                //System.out.println(line);  
  253.             }while(line != null);  
  254.             System.out.println(">>>==="+res);  
  255.             return res.toString();  
  256.         }catch(Exception e){  
  257.             e.printStackTrace();  
  258.             return null;  
  259.         }  
  260.     }  
  261.     private String readCheckImage(String url){  
  262.         try{   
  263.             System.out.println("get>>>"+url);  
  264.             URL serverUrl = new URL(url);  
  265.             HttpURLConnection conn = (HttpURLConnection) serverUrl.openConnection();   
  266.             conn.setRequestMethod("GET");//"POST" ,"GET"   
  267.             conn.addRequestProperty("Accept-Charset", "UTF-8;");//GB2312,  
  268.             conn.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.8) Firefox/3.6.8");  
  269.             conn.connect();  
  270.             //返回的cookie  
  271.             if(conn.getHeaderFields().get("Set-Cookie") != null)  
  272.                 for(String s:conn.getHeaderFields().get("Set-Cookie")){  
  273.                     cookie += s;  
  274.                 }  
  275.             InputStream ins =  conn.getInputStream();  
  276.             BufferedImage bi = ImageIO.read(ins);  
  277.             File f =new File("qqimg.jpg");  
  278.             ImageIO.write(bi, "jpg", f);  
  279.             return f.getAbsolutePath();  
  280.         }catch(Exception e){  
  281.             e.printStackTrace();   
  282.         }   
  283.         return null;  
  284.     }  
  285.     public void receiveMsg(String message, int fromQQ){  
  286.         log("qq:"+fromQQ+"说:"+message);  
  287.         //test  
  288.         sendMsg(fromQQ, "然后呢?");  
  289.     }  
  290.     class PollThread extends Thread{  
  291.         private String pollUrl = "http://web2-b.qq.com/channel/poll";  
  292.         @Override  
  293.         public void run() {   
  294.             String url = pollUrl+ "?clientid="+clientid+"&psessionid="+psessionid;   
  295.             try {  
  296.                 while(isrun){  
  297.                     //线程一直等待知道服务器有返回数据  
  298.                     String res = getUrl(url);  
  299.                     JSONObject retJ = new JSONObject(res);  
  300.                     if(retJ.getInt("retcode") == 0){  
  301.                         JSONArray result = retJ.getJSONArray("result");  
  302.                         String poll_type = result.getJSONObject(0).getString("poll_type");  
  303.                         if("message".equals(poll_type)){  
  304.                             //说明有人发qq消息给我,                            
  305.                             String raw_content = result.getJSONObject(0).getJSONObject("value").get("raw_content").toString();   
  306.                             int from_uin = result.getJSONObject(0).getJSONObject("value").getInt("from_uin");  
  307.                             log("收到来自:"+from_uin+":"+raw_content);  
  308.                             //通知客户端收到了  
  309.                             receiveMsg(raw_content, from_uin);  
  310.                         }  
  311.                         //system_message 是系统消息  
  312.                     }  
  313.                 }  
  314.             } catch (JSONException e) {   
  315.                 e.printStackTrace();  
  316.             }  
  317.         }  
  318.     }