鱼C论坛

 找回密码
 立即注册
查看: 3040|回复: 2

[技术交流] 实时抓取qq聊天窗口的消息

[复制链接]
发表于 2020-10-9 12:16:33 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
tx在8月初将酷q等一切工具搞掉了, 但是我的砖还得搬, 于是临时写了一个抓取消息的脚本。
原理是通过IAccessible接口抓取聊天窗口的消息再处理得到每条消息, 不才, 现成资料是用ahk调用dll然后我就直接用了而没有折腾改成Python。
使用方法:
三个文件放一起,
os.system("start QQ抓取.ahk xxx")   xxx改成要监控的窗口名, 多个窗口就复制这行
destination = ('127.0.0.1', 5888)      用socket把消息发到这个地址

  1. import os
  2. import threading
  3. import socket
  4. import json
  5. import re


  6. def parser(new_text, l_old_text):
  7.     new_text = re.sub(r"\n {2}(?=.+\(\d+\) ( |202\d-\d\d-\d\d )\d\d:\d\d:\d\d)", '-cut-', new_text.replace('\r', '\n'))  # \r改\n, 在每个用户标识前将'\n  '替换成分隔符号-cut-(不直接split是因为空格开头的消息会出错)
  8.     l_new_text = new_text.split('-cut-')
  9.     for text in l_new_text:
  10.         if text not in l_old_text:
  11.             QQID = re.search(r"(?<=\()\d+(?=\))", text).group()
  12.             message = re.search(r"(?<=\d\d:\d\d:\d\d\n ).+", text, re.DOTALL).group()
  13.             client = socket.socket()
  14.             try:
  15.                 client.connect(destination)
  16.                 client.send(json.dumps({'type': 'message', 'user_id': QQID, 'message': message}).encode('utf8'))
  17.             except Exception:
  18.                 print("异常 - 发送失败")
  19.             client.close()
  20.     return l_new_text


  21. def handle_receive(client):
  22.     receive = client.recv(65536).decode('utf8')
  23.     if "ahk-" in receive:
  24.         name = receive
  25.         l_old_text = []
  26.         print('已连接:', receive)
  27.         while True:
  28.             try:
  29.                 all_text = client.recv(65536).decode('utf8')
  30.                 if all_text:
  31.                     l_old_text = parser(all_text[2:-2], l_old_text)  # 不要前面空格和最后面\r空格
  32.                 else:
  33.                     client.close()
  34.                     print('\n已断开:', name)
  35.                     break
  36.             except Exception as e:
  37.                 print('异常 - 处理消息', name, e)


  38. if __name__ == '__main__':
  39.     server = socket.socket()
  40.     server.bind(('', 5886))
  41.     server.listen(5)
  42.     os.system("start QQ抓取.ahk xxx")
  43.     destination = ('127.0.0.1', 5888)

  44.     while True:
  45.         try:
  46.             client = server.accept()[0]
  47.             thread = threading.Thread(target=handle_receive, args=(client, ))
  48.             thread.start()
  49.         except Exception as e:
  50.             print('异常 - 主循环', e)
复制代码




QQ抓取.ahk:
  1. #Persistent
  2. #SingleInstance,force
  3. #Include Socket.ahk
  4. SetBatchLines,-1

  5. Acc_Init()
  6. {
  7.         static h
  8.         If Not h
  9.                 h:=DllCall("LoadLibrary","Str","oleacc","Ptr")
  10. }
  11. Acc_ObjectFromWindow(hWnd, idObject = 0)
  12. {
  13.         Acc_Init()
  14.         If DllCall("oleacc\AccessibleObjectFromWindow", "Ptr", hWnd, "UInt", idObject&=0xFFFFFFFF, "Ptr", -VarSetCapacity(IID,16)+NumPut(idObject==0xFFFFFFF0?0x46000000000000C0:0x719B3800AA000C81,NumPut(idObject==0xFFFFFFF0?0x0000000000020400:0x11CF3C3D618736E0,IID,"Int64"),"Int64"), "Ptr*", pacc)=0
  15.         Return ComObjEnwrap(9,pacc,1)
  16. }
  17. Acc_Query(Acc) {
  18.         Try Return ComObj(9, ComObjQuery(Acc,"{618736e0-3c3d-11cf-810c-00aa00389b71}"), 1)
  19. }
  20. Acc_Children(Acc) {
  21.         If ComObjType(Acc,"Name") != "IAccessible"
  22.                 ErrorLevel := "Invalid IAccessible Object"
  23.         Else {
  24.                 Acc_Init(), cChildren:=Acc.accChildCount, Children:=[]
  25.                 If DllCall("oleacc\AccessibleChildren", "Ptr",ComObjValue(Acc), "Int",0, "Int",cChildren, "Ptr",VarSetCapacity(varChildren,cChildren*(8+2*A_PtrSize),0)*0+&varChildren, "Int*",cChildren)=0 {
  26.                         Loop %cChildren%
  27.                                 i:=(A_Index-1)*(A_PtrSize*2+8)+8, child:=NumGet(varChildren,i), Children.Insert(NumGet(varChildren,i-8)=9?Acc_Query(child):child), NumGet(varChildren,i-8)=9?ObjRelease(child):
  28.                         Return Children.MaxIndex()?Children:
  29.                 } Else
  30.                         ErrorLevel := "AccessibleChildren DllCall Failed"
  31.         }
  32. }
  33. GetElementByName(AccObj, name) {
  34.    if (AccObj.accName(0) = name)
  35.       return AccObj
  36.    
  37.    for k, v in Acc_Children(AccObj)
  38.       if IsObject(obj := GetElementByName(v, name))
  39.          return obj
  40. }


  41. title = %1%  ; start QQ抓取.ahk c

  42. client := new SocketTCP()
  43. client.connect("127.0.0.1",5886)
  44. client.sendText("ahk-" title)

  45. hwnd := WinExist(title)
  46. window := Acc_ObjectFromWindow(hwnd)
  47. widget := GetElementByName(window, "消息")

  48. old_text := widget.accValue(0)

  49. Loop, {
  50.     sleep, 100
  51.     current_text := widget.accValue(0)
  52.     if (current_text != old_text){
  53.         client.sendText(current_text)
  54.         old_text := current_text
  55.     }
  56. }
复制代码



Socket.ahk:
  1. ;~ https://autohotkey.com/board/topic/94376-socket-class-%c3%bcberarbeitet/
  2. class Socket
  3. {
  4.   static __eventMsg := 0x9987
  5.   
  6.   __New(s=-1)
  7.   {
  8.     static init
  9.     if (!init)
  10.     {
  11.       DllCall("LoadLibrary", "str", "ws2_32", "ptr")
  12.       VarSetCapacity(wsadata, 394+A_PtrSize)
  13.       DllCall("ws2_32\WSAStartup", "ushort", 0x0000, "ptr", &wsadata)
  14.       DllCall("ws2_32\WSAStartup", "ushort", NumGet(wsadata, 2, "ushort"), "ptr", &wsadata)
  15.       OnMessage(Socket.__eventMsg, "SocketEventProc")
  16.       init := 1
  17.     }
  18.     this.socket := s
  19.   }
  20.   __Delete()
  21.   {
  22.     this.disconnect()
  23.   }
  24.   __Get(k, v)
  25.   {
  26.     if (k="size")
  27.       return this.msgSize()
  28.   }
  29.   connect(host, port)
  30.   {
  31.     if ((this.socket!=-1) || (!(faddr := next := this.__getAddrInfo(host, port))))
  32.       return 0
  33.     while (next)
  34.     {
  35.       sockaddrlen := NumGet(next+0, 16, "uint")
  36.       sockaddr := NumGet(next+0, 16+(2*A_PtrSize), "ptr")
  37.       if ((this.socket := DllCall("ws2_32\socket", "int", NumGet(next+0, 4, "int"), "int", this.__socketType, "int", this.__protocolId, "ptr"))!=-1)
  38.       {
  39.         if ((r := DllCall("ws2_32\WSAConnect", "ptr", this.socket, "ptr", sockaddr, "uint", sockaddrlen, "ptr", 0, "ptr", 0, "ptr", 0, "ptr", 0, "int"))=0)
  40.         {
  41.           DllCall("ws2_32\freeaddrinfo", "ptr", faddr)
  42.           return Socket.__eventProcRegister(this, 0x21)
  43.         }
  44.         this.disconnect()
  45.       }
  46.       next := NumGet(next+0, 16+(3*A_PtrSize), "ptr")
  47.     }
  48.     this.lastError := DllCall("ws2_32\WSAGetLastError")
  49.     return 0
  50.   }
  51.   bind(host, port)
  52.   {
  53.     if ((this.socket!=-1) || (!(faddr := next := this.__getAddrInfo(host, port))))
  54.       return 0
  55.     while (next)
  56.     {
  57.       sockaddrlen := NumGet(next+0, 16, "uint")
  58.       sockaddr := NumGet(next+0, 16+(2*A_PtrSize), "ptr")
  59.       if ((this.socket := DllCall("ws2_32\socket", "int", NumGet(next+0, 4, "int"), "int", this.__socketType, "int", this.__protocolId, "ptr"))!=-1)
  60.       {
  61.         if (DllCall("ws2_32\bind", "ptr", this.socket, "ptr", sockaddr, "uint", sockaddrlen, "int")=0)
  62.         {
  63.           DllCall("ws2_32\freeaddrinfo", "ptr", faddr)
  64.           return Socket.__eventProcRegister(this, 0x29)
  65.         }
  66.         this.disconnect()
  67.       }
  68.       next := NumGet(next+0, 16+(3*A_PtrSize), "ptr")
  69.     }
  70.     this.lastError := DllCall("ws2_32\WSAGetLastError")
  71.     return 0
  72.   }
  73.   listen(backlog=32)
  74.   {
  75.     return (DllCall("ws2_32\listen", "ptr", this.socket, "int", backlog)=0) ? 1 : 0
  76.   }
  77.   accept()
  78.   {
  79.     if ((s := DllCall("ws2_32\accept", "ptr", this.socket, "ptr", 0, "int", 0, "ptr"))!=-1)
  80.     {
  81.       newsock := new Socket(s)
  82.       newsock.__protocolId := this.__protocolId
  83.       newsock.__socketType := this.__socketType
  84.       Socket.__eventProcRegister(newsock, 0x21)
  85.       return newsock
  86.     }
  87.     return 0
  88.   }
  89.   disconnect()
  90.   {
  91.     Socket.__eventProcUnregister(this)
  92.     DllCall("ws2_32\closesocket", "ptr", this.socket, "int")
  93.     this.socket := -1
  94.     return 1
  95.   }
  96.   msgSize()
  97.   {
  98.     VarSetCapacity(argp, 4, 0)
  99.     if (DllCall("ws2_32\ioctlsocket", "ptr", this.socket, "uint", 0x4004667F, "ptr", &argp)!=0)
  100.       return 0
  101.     return NumGet(argp, 0, "int")
  102.   }
  103.   send(addr, length)
  104.   {
  105.     if ((r := DllCall("ws2_32\send", "ptr", this.socket, "ptr", addr, "int", length, "int", 0, "int"))<=0)
  106.       return 0
  107.     return r
  108.   }
  109.   sendText(msg, encoding="UTF-8")
  110.   {
  111.     VarSetCapacity(buffer, length := (StrPut(msg, encoding)*(((encoding="utf-16")||(encoding="cp1200")) ? 2 : 1))-1)
  112.     StrPut(msg, &buffer, encoding)
  113.     return this.send(&buffer, length)
  114.   }
  115.   recv(byref buffer, wait=1)
  116.   {
  117.     while ((wait) && ((length := this.msgSize())=0))
  118.       sleep, 100
  119.     if (length)
  120.     {
  121.       VarSetCapacity(buffer, length)
  122.       if ((r := DllCall("ws2_32\recv", "ptr", this.socket, "ptr", &buffer, "int", length, "int", 0))<=0)
  123.         return 0
  124.       return r
  125.     }
  126.     return 0
  127.   }
  128.   recvText(wait=1, encoding="UTF-8")
  129.   {
  130.     if (length := this.recv(buffer, wait))
  131.       return StrGet(&buffer, length, encoding)
  132.     return
  133.   }
  134.   __getAddrInfo(host, port)
  135.   {
  136.     a := ["127.0.0.1", "0.0.0.0", "255.255.255.255", "::1", "::", "FF00::"]
  137.     conv := {localhost:a[1], addr_loopback:a[1], inaddr_loopback:a[1], addr_any:a[2], inaddr_any:a[2], addr_broadcast:a[3]
  138.     , inaddr_broadcast:a[3], addr_none:a[3], inaddr_none:a[3], localhost6:a[4], addr_loopback6:a[4], inaddr_loopback6:a[4]
  139.     , addr_any6:a[5], inaddr_any:a[5], addr_broadcast6:a[6], inaddr_broadcast6:a[6], addr_none6:a[6], inaddr_none6:a[6]}
  140.     if (conv[host])
  141.       host := conv[host]
  142.     VarSetCapacity(hints, 16+(4*A_PtrSize), 0)
  143.     NumPut(this.__socketType, hints, 8, "int")
  144.     NumPut(this.__protocolId, hints, 12, "int")
  145.     if ((r := DllCall("ws2_32\getaddrinfo", "astr", host, "astr", port, "ptr", &hints, "ptr*", next))!=0)
  146.     {
  147.       this.lastError := DllCall("ws2_32\WSAGetLastError")
  148.       return 0
  149.     }
  150.     return next
  151.   }
  152.   __eventProcRegister(obj, msg)
  153.   {
  154.     a := SocketEventProc(0, 0, "register", 0)
  155.     a[obj.socket] := obj
  156.     return (DllCall("ws2_32\WSAAsyncSelect", "ptr", obj.socket, "ptr", A_ScriptHwnd, "uint", Socket.__eventMsg, "uint", msg)=0) ? 1 : 0
  157.   }
  158.   __eventProcUnregister(obj)
  159.   {
  160.     a := SocketEventProc(0, 0, "register", 0)
  161.     a.remove(obj.socket)
  162.     return (DllCall("ws2_32\WSAAsyncSelect", "ptr", obj.socket, "ptr", A_ScriptHwnd, "uint", 0, "uint", 0)=0) ? 1 : 0
  163.   }
  164. }
  165. SocketEventProc(wParam, lParam, msg, hwnd)
  166. {
  167.   global Socket
  168.   static a := []
  169.   Critical
  170.   if (msg="register")
  171.     return a
  172.   if (msg=Socket.__eventMsg)
  173.   {
  174.     if (!isobject(a[wParam]))
  175.       return 0
  176.     if ((lParam & 0xFFFF) = 1)
  177.       return a[wParam].onRecv(a[wParam])
  178.     else if ((lParam & 0xFFFF) = 8)
  179.       return a[wParam].onAccept(a[wParam])
  180.     else if ((lParam & 0xFFFF) = 32)
  181.     {
  182.       a[wParam].socket := -1
  183.       return a[wParam].onDisconnect(a[wParam])
  184.     }
  185.     return 0
  186.   }
  187.   return 0
  188. }

  189. class SocketTCP extends Socket
  190. {
  191.   static __protocolId := 6 ;IPPROTO_TCP
  192.   static __socketType := 1 ;SOCK_STREAM
  193. }

  194. class SocketUDP extends Socket
  195. {
  196.   static __protocolId := 17 ;IPPROTO_UDP
  197.   static __socketType := 2 ;SOCK_DGRAM

  198.   enableBroadcast()
  199.   {
  200.     VarSetCapacity(optval, 4, 0)
  201.     NumPut(1, optval, 0, "uint")
  202.     if (DllCall("ws2_32\setsockopt", "ptr", this.socket, "int", 0xFFFF, "int", 0x0020, "ptr", &optval, "int", 4)=0)
  203.       return 1
  204.     return 0
  205.   }
  206.   disableBroadcast()
  207.   {
  208.     VarSetCapacity(optval, 4, 0)
  209.     if (DllCall("ws2_32\setsockopt", "ptr", this.socket, "int", 0xFFFF, "int", 0x0020, "ptr", &optval, "int", 4)=0)
  210.       return 1
  211.     return 0
  212.   }
  213. }
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-10-18 17:22:59 | 显示全部楼层
如果做成一个实时转发群消息的功能就更好了。比如从A群转到B群
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-10-18 17:23:31 | 显示全部楼层
作者有什么想法吗?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-6-28 10:38

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表