|
|
Posted on 2007-01-03 11:24 蜈蚣 阅读(16614) 评论(69) 编辑 收藏
这个元旦假期过的比较奇怪,为了向远方的GF表示忠诚。除了1号和同学吃了个大锅饭之外,尽量做到了大门不出二门不迈,躲在闺房守岁:)。 在家闲着也不能干闲着,总得做点事情,想起前段时间一个师兄交代给我的任务:把若干个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会话类 2 if(typeof(AjaxSession) == 'undefined') 3 var 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 //会话类 2 if(typeof(QQSession) == 'undefined') 3 var 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调用 1 using System; 2 using System.Drawing; 3 using System.Runtime.InteropServices; 4 5 namespace 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
Feedback
第一种办法不是不能用,可能是我网络的问题。
我这里收了很多资料,有兴趣的朋友可以下下去看
代码写的不是一般的烂,砖头的不要
scotoma@163.com
看上去不错也想要一份谢谢了....
为什么有现成的不用。
PYTHON社区早有人写过QQ桥,以前我们就用他,几个群的消息共通。使用SVN下载。要PYTHON相关环境
Trac:http://trac.easiest.cn/cgi-bin/trac.cgi
svn:http://www.easiest.cn/svn/Python-QQ
缺省用户名和密码为Python-QQ
缺省用户名和密码只有读的权利,无写入权利。
引:
2006-12-19 公司三个站点整和到一起了,开通了论坛,大家可以到bbs.easiest.cn提出问题.
2006-7-22 修改数据包为2006,并修改了根登陆服务器地址。能正常login和list还有发送接收消息了.
2005-10-9 桥接了四个群,测试中。
2005-10-9 增加了对群消息的处理,将Python-QQ-bri做了两个群的桥接。
2005-09-26 将tea算法部分的中文注释提交上来了。
2005-09-10 能发送消息了,不过不稳定。
2005-09-09 增加了qqlib的部分注释。对qqlib的处理方法做了小调整。
2005-09-09 修改了GUI部分,能得到好友列表了,但是发送消息未完成。
2005-09-09 修改了GUI部分,能够正常登陆了。
2005-09-08 增加了对发送消息不成功的判断,但是未自动重发。
2005-08-11 增加了qq.login属性,为1时登陆成功。
2005-08-01 修改了消息处理方式,目前只处理了好友消息和陌生人消息。修正了改变当前状态的一处错误。
2005-07-30 按照limodou和其他朋友的提意,将编码改变为UTF-8,并通过os模块本程序自动在win和*uix下切换编码。将qqlib独立处理,分别进行控制台和图形界面的分工工作。hhgs.efhilt@gmail.com朋友提供了一个gui的程序,但是没有调试通过。将输入密码的地方使用了getpass模块。加上了登陆成功的提示。
2005-07-29 yxxyun@gmail.com朋友的提醒下,发现了一个命令定义错误。更改过来。
2005-07-28 因为朋友们反映在linux下不能使用,于是进行测试,发现2.3不支持某种方法的字典操作,更改update方法后,在FreeBSD 5.3下运行成功。
2005-07-27 释放出第一个版本,环境支持需要Python 2.4 以及twisted 2.01。目前实现的功能有:登陆、发送活动包、列好友、列在线好友、发送消息、接受好友消息、退出登陆。
@j[匿名]
不为什么,就因为没有google到它,咋地吧,哈哈哈哈
看来又做了个重复的轮子
不过既然是打发时间,也就无所谓了
我一开始考虑用javascript写的时候考虑过用LumaQQ源代码
就是嫌烦琐,另外java使用水平也不高
放着c++不用,用c#就是考虑到做后面消息解析的时候,用正则表达式方便一些
sifang2004@yahoo.com.cn
哥们,把资料发给我吧,谢谢,谢谢啦!
我也要一份,谢谢!
new_flar@163.com
帅哥,我要完整版的!
soulmachine@gmail.com
我也要一份,谢谢! dianshijin@gmail.com
我想研究下,xzg_del@sohu.com非常感谢
onlytiancai@gmail.com,给俺一个,谢谢。
我最近也需要这个东西,谢谢给我一份!
myxbing@hotmail.com
为什么我调不通?怎么找接口地址?我怎么都是“基础连接已关闭:接收时发生意外错误”〉?我是新手,觉得这个很好玩。怎么弄啊啊?QQ:215130459
请问牛人,具体获取一个窗体上的某个控件,有没有什么章法可循。
IntPtr ip = WinApi.FindWindow(lpszParentClass,lpszParentWindow);
IntPtr EdithParentWnd = WinApi.FindWindowEx(ip,new IntPtr(0),"#32770","");
IntPtr EdithWnd = WinApi.FindWindowEx(EdithParentWnd,new IntPtr(0),"AfxWnd42","");
如上代码,您是怎么知道需要按照这个顺序依次获取呢?
我也打算开发一个类似的软件,多谢了!
@言学
visual studio .net 工具里有个spy++
里面会列出来
string lpszParentClass = "#32770"; //整个窗口的类名
这里的 "#32770" 是 怎么得来的 我这ip 返回我总是0
string lpszClass_Text = "RichEdit20A"; //查找历史记录类
"RichEdit20A"又是什么 是一人一个样还是都一样的我信箱是
roloemail@163.com 希望能跟你交流
@rollo
visual studio .net 工具里有个spy++
里面会列出来
THandle,这个值得不到,是空的,QQ2007。。。
使用TQQ发送信息,均显示"发送内容不能为空,请重新输入"
环境:QQ2006正式版+winXP sp2
.net 2
高手,能给我发下完整的代码么?学习下
zhangbh.zj@163.com先谢谢
we chinese will fight with america
--引用-------------------------------------------------- zhangtianliang: 什么嘛,一个字:垃圾 -------------------------------------------------------- 是两个字呀!
请高手有时间的时候也帮我发一份,想研究研究是什么东东,谢谢了
楼主给俺来分源代码大餐吧!
谢谢!
zhangkai0309@163.com
提供QQ机器人,MSN机器人,GTALK机器人定制服务, 我们的网站,IM机器人:http://www.imrobots.cn
|