写的基于http协议的QQ聊天机器人

来源:互联网 发布:icon图标软件 编辑:程序博客网 时间:2024/04/30 15:51
在家闲着也不能干闲着,总得做点事情,想起前段时间一个师兄交代给我的任务:把若干个qq群串在一起。X国的政策一向比较Bt,你说开个群还要限制人数。H大在深圳有几千校友,一个群怎么可能放的下那么多人。好多在tx工作的师兄,明确告诉:我买通tx的人是不大可能了,搞一个传话筒才是解决之道。
       事情虽小,但也着实麻烦。你说要去分析QQ具体数据包,万一哪天tx一高兴把数据给改了,或者接口开放了,也够我郁闷一壶的了。那就找个最简单的办法吧,左一顿baidu,又一顿google. “QQ接口”搜出来的结果绝大多数是关于tx qq的http接口,既然大家那么推崇,那我就从Http接口下手,于是把我以前写的AJAX给拆了,在把所谓的qq接口研究成果给鼓捣在一起,然后去webqq(http://webqq.qq.com)上大抢一遍,那家伙,天昏地暗,相当的&(&(。
      代码我是用script写的:
                   异步AJAX会话类
  1//异步AJAX会话类
  2if(typeof(AjaxSession) == 'undefined')
  3var AjaxSession = function(){
  4    
  5    //创建会话
  6    this.CreateSession = function()
  7    {
  8        if (window.ActiveXObject){  // IE
  9            try{    
10                return new ActiveXObject('Microsoft.XMLHTTP');
11            }catch(e){}
12                
13            try{
14                return new ActiveXObject('Msxml2.XMLHTTP');
15            }catch(ee){}
16        }else{                   //Mozilla, Safari
17            var s = new XMLHttpRequest();
18            if (s.readyState == null) {
19                s.readyState = 1;
20                s.addEventListener("load", function () {
21                    s.readyState = 4;
22                    if (typeof(s.onreadystatechange) == "function")
23                        s.onreadystatechange();
24                }, false);
25            }
26            
27            return s;
28        }
29                    
30        return null;
31    }
32    
33    //进行请求
34    this.Request = function(url, params, callback)
35    {    
36        
37        var s = this.CreateSession();
38        if(null == s)
39            alert("对不起,您的浏览器不支持某些特性。");
40        else
41            s.abort();
42        
43        var isAsync =  typeof(callback) == 'function';
44        var method = !params ? "GET" : "POST";
45
46        if(isAsync) s.onreadystatechange = function()
47        {
48            try{
49                alert(s.status);
50                if ((4 == s.readyState) && (200 == s.status || 304 == s.status))
51                    callback(this.Response(s));
52                else
53                    alert("请求错误,错误原因:" + s.statusText);    
54            }catch(e){}
55        }
56                
57        s.open(method, url, isAsync);        
58        s.setRequestHeader("Connection","Keep-Alive");        
59        s.setRequestHeader("Content-Type","text/html; charset=gb2312");
60        //s.setRequestHeader("Content-Type","text/plain; charset=UTF-8");
61        
62        if(method == "POST")
63        {
64            s.setRequestHeader("Content-Length",params.length)
65            s.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
66            
67        }
68
69        alert(params);
70        s.send(params);
71        
72            
73        if(!isAsync)
74            return  this.Response(s);
75    }
76    
77    //返回应答信息
78    this.Response = function(s)
79    {
80        if(s.status == 200  || 304 == s.status)
81        {
82            if(s.responseXML != null && s.responseXML.xml != null && s.responseXML.xml != '')
83                return s.responseXML;
84            else
85                return s.responseText;
86        }    
87    }
88    
89    //对字符串进行编码
90    this.UncCoding = function(s)
91    {
92        var output = '';
93        for(var i = 0 ;i< s.length;i++){
94            output = output + '%' + s.charCodeAt(i);
95        }        
96
97        return output;
98    }
99
100    //获取xml结构
101    this.GetDom = function(s){
102        var doc = new ActiveXObject('Microsoft.XMLDOM');
103        doc.async = false;
104        doc.loadXML(s) ;    
105        return doc;
106    }
107    
108    return this;
109}
//QQ会话类
1//会话类
2if(typeof(QQSession) == 'undefined')
3var QQSession = function(){
4    this.UIN = 0;
5    this.Md5PasswordStr = "";
6    this.WebQQUrl = "http://tqq.tencent.com:8000/";
7    this.qs = null;    
8    this.isLogin = false;
9    this.SeqIndex = null;
10    
11    //用户登陆
12    this.Login = function(uin,passwd,vcode,qs){
13        var m5 = new MD5();        
14        this.UIN = uin;        
15        this.Md5PasswordStr = m5.calcMD5(passwd);
16        var CmdStr = "VER=1.0&CMD=1&SEQ=" + this.GetNewSEQ() + "&UIN=" + uin + "&PS=" + this.Md5PasswordStr + "&STATUS=10&VCODE=" + vcode + "&QQSESSION=" + qs ;    
17        
18        //if(!this.qs)
19        //    this.qs = new AjaxSession();
20        window.frames["proxy"].request(this.WebQQUrl,CmdStr);
21        //this.qs.Request(this.WebQQUrl,CmdStr,this.ShowMsg);
22        
23        //document.getElementById("ssl_login").callback = this.ShowMsg;
24        //document.getElementById("ssl_login").src = "https://webqq-proxy.qq.com/webqq/l?"+ CmdStr;
25    }
26    
27    //用户信息
28    this.GetInfo = function(){
29        var CmdStr = "VER=1.0&CMD=10&SEQ=" + this.GetNewSEQ() + "&UIN=" + this.UIN + "&FUIN=" + this.UIN;
30        this.qs.Request(this.WebQQUrl,CmdStr,this.ShowMsg);    
31        //window.frames["proxy"].request(this.WebQQUrl,CmdStr);
32    }
33    
34    //获取好友列表
35    this.GetList = function(){
36        var CmdStr = "VER=1.0&CMD=2&SEQ=" + this.GetNewSEQ() + "&UIN=" + this.UIN + "&NPOS=0";
37        this.qs.Request(this.WebQQUrl,CmdStr,this.ShowMsg);    
38    }
39    
40    //获得新的SEQ
41    this.GetNewSEQ = function(){
42        if(!this.SeqIndex){
43            var d = new Date();
44            this.SeqIndex = d.getTime()
45        }
46        
47        this.SeqIndex ++;
48        return this.SeqIndex;        
49    }
50    
51    this.ShowMsg = function(s){
52        alert(s);
53    }
54    
55    return this;
56}
     我在那用天真的眼神,盼望着能从这个地方掏出点东西来。可偏偏我这个比较倒霉的孩子,碰到了极其复杂的网络问题(家穷人丑,只好用手机上网,速度回到上世纪90年代),掏了半天啥也没弄到,返回的尽是12152错误。
     去翻MSDN时,遇到几个头疼的单词(鄙人英语着实差劲,单词量屈指可数),很习惯的就去开金山词霸。等等,金山词霸能把别的窗口的信息给拽出来,为什么我就不能。于是我就抛弃了前面的工作,从QQ对话窗口下手。nndx,偶就不信了,偶还灭不了你。
  说干就干,那就先勾吧,.net好象是干不了这事,但是winapi还是能干这活的。win32编程嘛,不就几个消息循环(—(—……*(—
   WinApi调用
  1using System;
  2using System.Drawing;
  3using System.Runtime.InteropServices;
  4
  5namespace TQQ
  6{
  7    /**//// <summary>
  8    /// WinApi调用
  9    /// </summary>
10    public class WinApi
11    {
12        /**//// <summary>
13        /// 根据鼠标位置获取窗体
14        /// </summary>
15        /// <param name="lpPoint"></param>
16        /// <returns></returns>
17        [DllImport("user32.dll")]
18        public static extern IntPtr WindowFromPoint(Point lpPoint);
19
20        /**//// <summary>
21        /// 获取鼠标位置
22        /// </summary>
23        /// <param name="lpPoint"></param>
24        /// <returns></returns>
25        [DllImport("user32.dll")]
26        public static extern int GetCursorPos(out Point lpPoint);
27
28        /**//// <summary>
29        /// 获取鼠标位置下的窗体
30        /// </summary>
31        /// <returns></returns>
32        public static IntPtr GetLocalWindow()
33        {
34            Point point;
35            GetCursorPos(out point);
36            return WindowFromPoint(point);
37        }
38
39        /**//// <summary>
40        /// 申请内存空间
41        /// </summary>
42        /// <param name="hProcess"></param>
43        /// <param name="lpAddress"></param>
44        /// <param name="dwSize"></param>
45        /// <param name="flAllocationType"></param>
46        /// <param name="flProtect"></param>
47        /// <returns></returns>
48        [ DllImport( "Kernel32.dll" )]
49        public static extern Int32 VirtualAllocEx(IntPtr hProcess,Int32 lpAddress,Int32 dwSize,Int16 flAllocationType,Int16 flProtect);
50
51        /**//// <summary>
52        /// 读取内存空间
53        /// </summary>
54        /// <param name="hProcess"></param>
55        /// <param name="lpBaseAddress"></param>
56        /// <param name="lpBuffer"></param>
57        /// <param name="nSize"></param>
58        /// <param name="lpNumberOfBytesWritten"></param>
59        /// <returns></returns>
60        [ DllImport( "Kernel32.dll" )]
61        public static extern int ReadProcessMemory(IntPtr hProcess, Int32 lpBaseAddress,byte[] lpBuffer,long nSize,long lpNumberOfBytesWritten);
62
63        /**//// <summary>
64        /// 写内存空间
65        /// </summary>
66        /// <param name="hProcess"></param>
67        /// <param name="lpBaseAddress"></param>
68        /// <param name="lpBuffer"></param>
69        /// <param name="nSize"></param>
70        /// <param name="lpNumberOfBytesWritten"></param>
71        /// <returns></returns>
72        [ DllImport( "Kernel32.dll" )]
73        public static extern int WriteProcessMemory(IntPtr hProcess, Int32 lpBaseAddress,byte[] lpBuffer,long nSize,long lpNumberOfBytesWritten);
74
75        /**//// <summary>
76        /// 根据类/标题查找窗口
77        /// </summary>
78        /// <param name="lpClassName"></param>
79        /// <param name="lpWindowName"></param>
80        /// <returns></returns>
81        [DllImport("User32.dll",EntryPoint="FindWindow")]
82        public static extern IntPtr FindWindow(string lpClassName,string lpWindowName);
83
84        /**//// <summary>
85        /// 获取窗口子对象
86        /// </summary>
87        /// <param name="hwndParent"></param>
88        /// <param name="hwndChildAfter"></param>
89        /// <param name="lpszClass"></param>
90        /// <param name="lpszWindow"></param>
91        /// <returns></returns>
92        [DllImport("user32.dll",EntryPoint="FindWindowEx")]
93        public static extern IntPtr FindWindowEx(IntPtr hwndParent,IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
94
95        /**//// <summary>
96        /// 发送windows消息
97        /// </summary>
98        /// <param name="hWnd"></param>
99        /// <param name="Msg"></param>
100        /// <param name="wParam"></param>
101        /// <param name="lParam"></param>
102        /// <returns></returns>
103        [DllImport("User32.dll")]
104        public static extern IntPtr SendMessage(IntPtr hWnd,int Msg,IntPtr wParam,IntPtr lParam);
105
106        /**//// <summary>
107        /// 发送windows消息
108        /// </summary>
109        /// <param name="hWnd"></param>
110        /// <param name="Msg"></param>
111        /// <param name="wParam"></param>
112        /// <param name="lParam"></param>
113        /// <returns></returns>
114        [DllImport("User32.dll",EntryPoint="SendMessage")]
115        public static extern int SendMessage(IntPtr hWnd,int Msg, IntPtr wParam, string lParam);
116
117        /**//// <summary>
118        /// 发送windows消息
119        /// </summary>
120        /// <param name="hwnd"></param>
121        /// <param name="wMsg"></param>
122        /// <param name="wParam"></param>
123        /// <param name="lParam"></param>
124        /// <returns></returns>
125        [DllImport("user32.dll", CharSet = CharSet.Auto)]
126        public static extern int SendMessage( IntPtr hwnd, int wMsg, int wParam,string lParam);
127
128        /**//// <summary>
129        /// 发送windows消息
130        /// </summary>
131        /// <param name="hwnd"></param>
132        /// <param name="wMsg"></param>
133        /// <param name="wParam"></param>
134        /// <param name="lParam"></param>
135        /// <returns></returns>
136        [DllImport("user32.dll", CharSet = CharSet.Auto)]
137        public static extern int SendMessage( IntPtr hwnd, int wMsg, int wParam,int lParam);
138
139        /**//// <summary>
140        /// 发送windows消息
141        /// </summary>
142        /// <param name="hwnd"></param>
143        /// <param name="wMsg"></param>
144        /// <param name="wParam"></param>
145        /// <param name="lParam"></param>
146        /// <returns></returns>
147        [DllImport("user32.dll", CharSet = CharSet.Auto)]
148        public static extern int SendMessage( IntPtr hwnd, int wMsg, int wParam,System.Text.StringBuilder lParam);
149
150        public const int WM_GETTEXT = 0x000D;
151        public const int WM_GETTEXTLENGTH = 0x000E;
152        public const int WM_SETTEXT = 0x000C;
153        public const int WM_CLICK = 0x00F5;
154        public const int WM_CHAR = 0x0102;
155        public const int EM_SETSEL = 0x00B1;
156        public const int EM_REPLACESEL = 0x00C2;
157
158    }
159}QQ钩子
1/**//// <summary>
2    /// QQ钩子
3    /// </summary>
4    public class QQHooks
5    {
6        /**//// <summary>
7        /// 发送消息
8        /// </summary>
9        /// <param name="windowName">窗口标题</param>
10        /// <param name="strMsg">消息内容</param>
11        public static void SendMsg(string windowName,string strMsg)
12        {
13            string lpszParentClass = "#32770"; //整个窗口的类名
14            string lpszParentWindow = windowName; //窗口标题
15            IntPtr ip = WinApi.FindWindow(lpszParentClass,lpszParentWindow);    
16            IntPtr EdithParentWnd = WinApi.FindWindowEx(ip,new IntPtr(0),"#32770","");
17            IntPtr EdithWnd = WinApi.FindWindowEx(EdithParentWnd,new IntPtr(0),"AfxWnd42","");            
18
19            string lpszClass_Text = "RICHEDIT"; //消息输入窗口
20            string lpszName_Text = ""; //消息输入窗口
21            IntPtr THandle = WinApi.FindWindowEx(EdithWnd,new IntPtr(0),lpszClass_Text,lpszName_Text);
22            
23            StringBuilder sb = new StringBuilder(strMsg);
24            WinApi.SendMessage(THandle,WinApi.EM_SETSEL,-1,-1);//            
25            WinApi.SendMessage(THandle,WinApi.EM_REPLACESEL,0,sb);
26
27            string lpszClass_Submit = "Button"; //需要查找的Button的类名
28            string lpszName_Submit = "发送(&S)"; //需要查找的Button的标题
29            IntPtr TButtonHandle =  WinApi.FindWindowEx(EdithParentWnd,new IntPtr(0),lpszClass_Submit,lpszName_Submit);
30            WinApi.SendMessage(TButtonHandle,WinApi.WM_CLICK,new IntPtr(0),"");//发送消息到目标控件使它执行click事件
31        }
32
33        /**//// <summary>
34        /// 获取消息
35        /// </summary>
36        /// <param name="windowName">窗口标题</param>
37        /// <returns></returns>
38        public static string GetMsg(string windowName)
39        {
40            string lpszParentClass = "#32770"; //整个窗口的类名
41            string lpszParentWindow = windowName; //窗口标题
42            IntPtr ip = WinApi.FindWindow(lpszParentClass,lpszParentWindow);    
43            IntPtr EdithWnd = WinApi.FindWindowEx(ip,new IntPtr(0),"#32770","");
44
45            string lpszClass_Text = "RichEdit20A"; //查找历史记录类
46            string lpszName_Text = ""; //查找历史记录类的标题
47            IntPtr THandle = WinApi.FindWindowEx(EdithWnd,new IntPtr(0),lpszClass_Text,lpszName_Text);
48
49            StringBuilder sb = new StringBuilder(300000);
50            WinApi.SendMessage(THandle,WinApi.WM_GETTEXT,255,sb);//发送消息到目标控件        
51            WinApi.SendMessage(THandle,WinApi.WM_SETTEXT,0,"");
52            return sb.ToString();
53        }
54    }

东西到手了,传话筒的工作基本上算是完成了,接下来就让它显示出来:
1    /**//// <summary>
2        /// 时钟事件
3        /// </summary>
4        /// <param name="sender"></param>
5        /// <param name="e"></param>
6        private void tmGetMsg_Tick(object sender, System.EventArgs e)
7        {
8            //群1里的消息
9            string strMsg = QQHooks.GetMsg(txtGroupWinName1.Text);        
10            string str = "";
11            
12            if(!string.Empty.Equals(strMsg))
13            {
14                ArrayList msgList = ParseMsg.Parse(strMsg);
15
16                foreach(QQMsg msg in msgList)
17                {
18                    if("253822559" == msg.Number.ToString())continue;
19                    
20                    if(string.Empty.Equals(msg.Msg.Trim()))
21                        str =  string.Format("{0}在群1里做了个表情/r/n",msg.Name,msg.Msg);
22                    else
23                         str =  string.Format("{0}在群1里说:/r/n{1}/r/n",msg.Name,msg.Msg);
24                    
25                    lbGroupMsgList1.Items.Add(str);
26                    //save msg
27                    
28                    //发送消息
29                    QQHooks.SendMsg(txtGroupWinName2.Text,str);
30                    QQHooks.SendMsg(txtGroupWinName3.Text,str);
31                    
32                }                
33            }
34  }
这里的txtGroupWinName1是让输入qq窗口标题。以前用asm32做程序的时候就觉的微软够BT的,要是每个窗口编译的时候就给限制个GUID多好—()—*)((—
至此大功算是告成了,完美交差。拿着这东东,改天狠狠的宰我师兄几顿.

(07-01-04)续:
要的人太多了,我还是主动把东西传上来吧,要不然有骗 email地址之嫌
源代码和相关资料下载:/Files/sukyboor/Q.rar

附,说明:先介绍一下http方法:
1,qq接口.txt
   是我在网络上找的一篇“C#编写QQ接口软件--QQ协议篇” 里面有QQhttp协议访问的详细的介绍,协议标准是v1.1。但是经过我是没调通,好象是http://tqq.tencent.com:8000被封了,你有没有好的运气我就不知道了。tx的webqq用的端口是http://webqq-proxy.qq.com:8080/这个就是慢点,用还是能用的。
2,Maxthon_Appin 文件夹下面有2个文件QQTest.txt ,GetQQSession.txt。maxthon有个简易收集面板你可以动态的去调试脚本.用maxthon打开http://webqq.qq.com后装入qqtest.txt你会发现访问过程的一举一动都在你的监视之中。Log.txt就是这么来的。GetQQSession.txt操作同上,用于获取一个新的sessionid
3,log.txt是监视http://webqq.qq.com的访问过程后得来的.你可以参考qq接口.txt看。不过tx的webqq用的是v1.0的接口,但大体上是相似的,相信聪明的你是可以看懂的。
  4,webqq文件夹下放着http://webqq.qq.com被我下到本地的页面。有兴趣的可以去看看里面的脚本,尤其是js/web_cmd.js虽然写的比我还烂,但还是值得一看。
5,WQQ文件夹下放着我写的测试页面。不过我家的破电脑调不通。如果你运气好的话,在这个基础上继续干吧。不过别忘记了,到时候把做好的东西发给我一份(sukyboor·163.com)。
好了,下面说说钩子方法
  项目在TQQ文件夹下面,注释写的挺详细的,自己看吧。
就提醒4点,
1 是app.config里面的qqnum一定要配置成你的qq号,否则你就等着别人骂你sx吧。
2 主窗口上的几个输入框一定要配置成群聊窗口的标题,否则它啥也干不了
3 别告诉我你还用qq2005
4 消息被转发以后在窗口里是看不到的,得点聊天记录按纽以后才能看的见

license.txt
  里面堆的是一堆套话,有空就瞅2眼.
下面有源码下载文件 点击下载此文件