马来西亚#文爱 python放弃UI竣事桌面微信自动化
布景马来西亚#文爱
使用 wxpy 大概 itchat 这种第三方库通过Python放弃我方的微信号,竣事好多自动化操作,用的是微信网页版接口,不外跟着微信的发展(信息安全等方面更加垂死,这种不适应官方盼愿出现的东西,很容易就歇业。也由于itchat在 python 的 request 请求中,使用到的 headers 皆是特等轻便的 headers。而况往往应用到config.USER_AGENT ,被发现怀疑用剧本封号是虽然的
是以我操办用wxauto,用UI控件的体式操作微信
(uiautomation竣事)
图片
wxauto第三方库先容
wxauto是某GitHub某位开源的
https://github.com/cluic/wxauto功能很基础,毕竟是UI控件,竣事的功能有
(1)群发音信包括图片,文献
(2)自动申报和添加好友(有小bug,轻便修改一下就行)
(3) 监控群聊东谈主员大概个东谈主聊天框
(4)获得微信好友昵称等信息(有bug,我改了一下没奏效)
成果如下
图片
wxauto.py代码'''Author: CluicUpdate: 2024-03-10Version: 3.9.8.15''' import uiautomation as uiafrom .languages import *from .utils import *from .elements import *from .errors import *from .color import *import datetimeimport timeimport osimport retry: from typing import Literalexcept: from typing_extensions import Literal class WeChat(WeChatBase): def __init__(self, language='cn') -> None: '''微信UI自动化实例 Args: language (str, optional): 微信客户端言语版块, 可选: cn简体中语 cn_t繁体中语 en英文, 默许cn, 即简体中语 ''' self.VERSION = '3.9.8.15' self.language = language self.lastmsgid = None self.listen = dict() self._checkversion() self.UiaAPI = uia.WindowControl(ClassName='WeChatMainWndForPC', searchDepth=1) self._show() self.SessionItemList = [] MainControl1 = [i for i in self.UiaAPI.GetChildren() if not i.ClassName][0] MainControl2 = MainControl1.GetChildren()[0] # 三个布局,导航栏(A)、聊天列表(B)、聊天框(C) # _______________ # |■|———| -□×| # | |———| | # |A| B | C | <--- 微信窗口布局简图暗示 # | |———|———————| # |=|———| | # ——————————————— self.NavigationBox, self.SessionBox, self.ChatBox = MainControl2.GetChildren() # 启动化导航栏,以A发轫 | self.NavigationBox --> A_xxx self.A_MyIcon = self.NavigationBox.ButtonControl() self.A_ChatIcon = self.NavigationBox.ButtonControl(Name=self._lang('聊天')) self.A_ContactsIcon = self.NavigationBox.ButtonControl(Name=self._lang('通信录')) self.A_FavoritesIcon = self.NavigationBox.ButtonControl(Name=self._lang('储藏')) self.A_FilesIcon = self.NavigationBox.ButtonControl(Name=self._lang('聊天文献')) self.A_MomentsIcon = self.NavigationBox.ButtonControl(Name=self._lang('一又友圈')) self.A_MiniProgram = self.NavigationBox.ButtonControl(Name=self._lang('小枢纽面板')) self.A_Phone = self.NavigationBox.ButtonControl(Name=self._lang('手机')) self.A_Settings = self.NavigationBox.ButtonControl(Name=self._lang('诞生特等他')) # 启动化聊天列表,以B发轫 self.B_Search = self.SessionBox.EditControl(Name=self._lang('搜索')) # 启动化聊天栏,以C发轫 self.C_MsgList = self.ChatBox.ListControl(Name=self._lang('音信')) self.nickname = self.A_MyIcon.Name print(f'启动化奏效,获得到已登录窗口:{self.nickname}') def _checkversion(self): self.HWND = FindWindow(classname='WeChatMainWndForPC') wxpath = GetPathByHwnd(self.HWND) wxversion = GetVersionByPath(wxpath) if wxversion != self.VERSION: Warnings.lightred(self._lang('版块不一致,需要3.9.8.15版块微信,前去下载:https://github.com/tom-snow/wechat-windows-versions/releases?page=2', 'WARNING').format(wxversion, self.VERSION), stacklevel=2) return False def _show(self): self.HWND = FindWindow(classname='WeChatMainWndForPC') win32gui.ShowWindow(self.HWND, 1) win32gui.SetWindowPos(self.HWND, -1, 0, 0, 0, 0, 3) win32gui.SetWindowPos(self.HWND, -2, 0, 0, 0, 0, 3) self.UiaAPI.SwitchToThisWindow() def GetSessionAmont(self, SessionItem): '''获得聊天对象名和新音信条数 Args: SessionItem (uiautomation.ListItemControl): 聊天对象控件 Returns: sessionname (str): 聊天对象名 amount (int): 新音信条数 ''' matchobj = re.search('\d+条新音信', SessionItem.Name) amount = 0 if matchobj: try: amount = int([i for i in SessionItem.GetChildren()[0].GetChildren() if type(i) == uia.uiautomation.TextControl][0].Name) except: pass if amount: sessionname = SessionItem.Name.replace(f'{amount}条新音信','') else: sessionname = SessionItem.Name return sessionname, amount def CheckNewMessage(self): '''是否有新音信''' self._show() return IsRedPixel(self.A_ChatIcon) #self.A_ChatIcon 图像中的红色像素点来判断是否有新音信。在好多应用中,新音信未读时图标会表露为红色,因此通过检测图主义红色像素是否存在,不错迤逦地细则是否有新音信。 def GetNextNewMessage(self, savepic=False): '''获得下一个新音信''' msgs_ = self.GetAllMessage() if self.lastmsgid is not None and self.lastmsgid in [i[-1] for i in msgs_[:-1]]: print('获妥贴前窗口新音信') idx = [i[-1] for i in msgs_].index(self.lastmsgid) MsgItems = self.C_MsgList.GetChildren()[idx+1:] msgs = self._getmsgs(MsgItems, savepic) return {self.CurrentChat(): msgs} elif self.CheckNewMessage(): print('获得其他窗口新音信') while True: self.A_ChatIcon.DoubleClick(simulateMove=False) sessiondict = self.GetSessionList(newmessage=True) if sessiondict: break for session in sessiondict: self.ChatWith(session) MsgItems = self.C_MsgList.GetChildren()[-sessiondict[session]:] msgs = self._getmsgs(MsgItems, savepic) self.lastmsgid = msgs[-1][-1] return {session:msgs} else: # print('莫得新音信') return None #该函数用于获得下一个新音信。当先,它会尝试获妥贴前窗口的新音信,淌若奏效,则复返现时聊天窗口的音信列表。 #淌若现时窗口莫得新音信,它会查验是否有其他窗口有新音信。淌若有,则会一一丝击聊天图标,掀开有新音信的聊天窗口,并复返该窗口的音信列表。 #函数复返一个字典,包含每个聊天窗口的音信列表。淌若莫得新音信,则复返None。 def GetAllNewMessage(self): '''获得所有新音信''' newmessages = {} while True: if self.CheckNewMessage(): self.A_ChatIcon.DoubleClick(simulateMove=False) sessiondict = self.GetSessionList(newmessage=True) for session in sessiondict: self.ChatWith(session) newmessages[session] = self.GetAllMessage()[-sessiondict[session]:] else: break self.ChatWith(self._lang('文献传输助手')) return newmessages def GetSessionList(self, reset=False, newmessage=False): '''获妥贴前聊天列表中的所有聊天对象 Args: reset (bool): 是否重置SessionItemList newmessage (bool): 是否只获得有新音信的聊天对象 Returns: SessionList (dict): 聊天对象列表,键为聊天对象名,值为新音信条数 ''' self.SessionItem = self.SessionBox.ListItemControl() if reset: self.SessionItemList = [] SessionList = {} for i in range(100): if self.SessionItem.BoundingRectangle.width() != 0: try: name, amount = self.GetSessionAmont(self.SessionItem) except: break if name not in self.SessionItemList: self.SessionItemList.append(name) if name not in SessionList: SessionList[name] = amount self.SessionItem = self.SessionItem.GetNextSiblingControl() if not self.SessionItem: break if newmessage: return {i:SessionList[i] for i in SessionList if SessionList[i] > 0} return SessionList def ChatWith(self, who, notfound: Literal['raise', 'ignore']='ignore'): '''掀开某个聊天框 Args: who ( str ): 要掀开的聊天框好友名; * 最佳无缺匹配,不完全匹配只会选取搜索框第一个 notfound ( str, optional ): 未找到时的处理花样,可选:raise-抛出极端 ignore-忽略,默许ignore Returns: chatname ( str ): 匹配值第一个的无缺名字 ''' self._show() sessiondict = self.GetSessionList(True) if who in list(sessiondict.keys())[:-1]: if sessiondict[who] > 0: who1 = f'{who}{sessiondict[who]}条新音信' else: who1 = who self.SessionBox.ListItemControl(Name=who1).Click(simulateMove=False) return who self.UiaAPI.SendKeys('{Ctrl}f', waitTime=1) self.B_Search.SendKeys(who, waitTime=1.5) SearchResut = self.SessionBox.GetChildren()[1].GetChildren()[1] firstresult = [i for i in SearchResut.GetChildren()[0].GetChildren() if who in i.Name][0] if firstresult.Name == f'搜索 {who}': if len(self.SessionBox.GetChildren()[1].GetChildren()) > 1: self.B_Search.SendKeys('{Esc}') if notfound == 'raise': raise TargetNotFoundError(f'未查询到沟通:{who}') elif notfound == 'ignore': return None chatname = firstresult.Name firstresult.Click(simulateMove=False) return chatname def SendMsg(self, msg, who=None, clear=True): '''发送文本音信 Args: msg (str): 要发送的文本音信 who (str): 要发送给谁,淌若为None,则发送到现时聊天页面。*最佳无缺匹配,优先使用备注 clear (bool, optional): 是否废除正本的履行, ''' if who in self.listen: chat = self.listen[who] chat.SendMsg(msg) return None if not msg: return None if who: try: editbox = self.ChatBox.EditControl(searchDepth=10) if who in self.CurrentChat() and who in editbox.Name: pass else: self.ChatWith(who) editbox = self.ChatBox.EditControl(Name=who, searchDepth=10) except: self.ChatWith(who) editbox = self.ChatBox.EditControl(Name=who, searchDepth=10) else: editbox = self.ChatBox.EditControl(searchDepth=10) if clear: editbox.SendKeys('{Ctrl}a', waitTime=0) self._show() if not editbox.HasKeyboardFocus: editbox.Click(simulateMove=False) t0 = time.time() while True: if time.time() - t0 > 10: raise TimeoutError(f'发送音信超时 --> {editbox.Name} - {msg}') SetClipboardText(msg) editbox.SendKeys('{Ctrl}v') if editbox.GetValuePattern().Value: break editbox.SendKeys('{Enter}') def SendFiles(self, filepath, who=None): '''向现时聊天窗口发送文献 Args: filepath (str|list): 要复制文献的十足旅途 who (str): 要发送给谁,淌若为None,则发送到现时聊天页面。*最佳无缺匹配,优先使用备注 Returns: bool: 是否奏效发送文献 ''' if who in self.listen: chat = self.listen[who] chat.SendFiles(filepath) return None filelist = [] if isinstance(filepath, str): if not os.path.exists(filepath): Warnings.lightred(f'未找到文献:{filepath},无法奏效发送', stacklevel=2) return False else: filelist.append(os.path.realpath(filepath)) elif isinstance(filepath, (list, tuple, set)): for i in filepath: if os.path.exists(i): filelist.append(i) else: Warnings.lightred(f'未找到文献:{i}', stacklevel=2) else: Warnings.lightred(f'filepath参数神气错误:{type(filepath)},应为str、list、tuple、set神气', stacklevel=2) return False if filelist: self._show() if who: try: if who in self.CurrentChat() and who in self.ChatBox.EditControl(searchDepth=10).Name: pass else: self.ChatWith(who) except: self.ChatWith(who) editbox = self.ChatBox.EditControl(Name=who) else: editbox = self.ChatBox.EditControl() editbox.SendKeys('{Ctrl}a', waitTime=0) t0 = time.time() while True: if time.time() - t0 > 10: raise TimeoutError(f'发送文献超时 --> {filelist}') SetClipboardFiles(filelist) time.sleep(0.2) editbox.SendKeys('{Ctrl}v') if editbox.GetValuePattern().Value: break editbox.SendKeys('{Enter}') return True else: Warnings.lightred('所有文献皆无法奏效发送', stacklevel=2) return False def GetAllMessage(self, savepic=False, n=0): '''获妥贴前窗口中加载的所有聊天纪录 Args: savepic (bool): 是否自动保存聊天图片 Returns: list: 聊天纪录信息 ''' MsgItems = self.C_MsgList.GetChildren() msgs = self._getmsgs(MsgItems, savepic) return msgs def LoadMoreMessage(self): '''加载现时聊天页面更多聊天信息 Returns: bool: 是否奏效加载更多聊天信息 ''' loadmore = self.C_MsgList.GetChildren()[0] loadmore_top = loadmore.BoundingRectangle.top top = self.C_MsgList.BoundingRectangle.top while True: if loadmore.BoundingRectangle.top > top or loadmore.Name == '': isload = True break else: self.C_MsgList.WheelUp(wheelTimes=10, waitTime=0.1) if loadmore.BoundingRectangle.top == loadmore_top: isload = False break else: loadmore_top = loadmore.BoundingRectangle.top self.C_MsgList.WheelUp(wheelTimes=1, waitTime=0.1) return isload def CurrentChat(self): '''获妥贴前聊天对象名''' uia.SetGlobalSearchTimeout(1) try: currentname = self.ChatBox.TextControl(searchDepth=15).Name return currentname except: return None finally: uia.SetGlobalSearchTimeout(10) def GetNewFriends(self): '''获得新的好友苦求列表 Returns: list: 新的好友苦求列表,元素为NewFriendsElement对象,可径直调用Accept范例 Example: >>> wx = WeChat() >>> newfriends = wx.GetNewFriends() >>> tags = ['标签1', '标签2'] >>> for friend in newfriends: >>> remark = f'备注{friend.name}' >>> friend.Accept(remark=remark, tags=tags) # 收受好友请求,并诞生备注和标签 ''' self._show() self.SwitchToContact() self.SessionBox.ButtonControl(Name='ContactListItem').Click(simulateMove=False) NewFriendsList = [NewFriendsElement(i, self) for i in self.ChatBox.ListControl(Name='新的一又友').GetChildren()] AcceptableNewFriendsList = [i for i in NewFriendsList if i.acceptable] print(f'获得到 {len(AcceptableNewFriendsList)} 条新的好友苦求') return AcceptableNewFriendsList def AddListenChat(self, who, savepic=False): '''添加监听对象 Args: who (str): 要监听的聊天对象名 savepic (bool, optional): 是否自动保存聊天图片,只针对该聊天对象有用 ''' exists = uia.WindowControl(searchDepth=1, ClassName='ChatWnd', Name=who).Exists(maxSearchSeconds=0.1) if not exists: self.ChatWith(who) self.SessionBox.ListItemControl(Name=who).DoubleClick(simulateMove=False) self.listen[who] = ChatWnd(who, self.language) self.listen[who].savepic = savepic def GetListenMessage(self): '''获得监听对象的新音信''' msgs = {} for who in self.listen: chat = self.listen[who] chat._show() msg = chat.GetNewMessage(savepic=chat.savepic) # if [i for i in msg if i[0] != 'Self']: if msg: msgs[chat] = msg return msgs def SwitchToContact(self): '''切换到通信录页面''' self._show() self.A_ContactsIcon.Click(simulateMove=False) def SwitchToChat(self): '''切换到聊天页面''' self._show() self.A_ChatIcon.Click(simulateMove=False) def GetGroupMembers(self): '''获妥贴前聊天群成员 Returns: list: 现时聊天群成员列表 ''' ele = self.ChatBox.PaneControl(searchDepth=7, foundIndex=6).ButtonControl(Name='聊天信息') try: uia.SetGlobalSearchTimeout(1) rect = ele.BoundingRectangle Click(rect) except: return finally: uia.SetGlobalSearchTimeout(10) roominfoWnd = self.UiaAPI.WindowControl(ClassName='SessionChatRoomDetailWnd', searchDepth=1) more = roominfoWnd.ButtonControl(Name='稽查更多', searchDepth=8) try: uia.SetGlobalSearchTimeout(1) rect = more.BoundingRectangle Click(rect) except: pass finally: uia.SetGlobalSearchTimeout(10) members = [i.Name for i in roominfoWnd.ListControl(Name='聊天成员').GetChildren()] while members[-1] in ['添加', '移出']: members = members[:-1] roominfoWnd.SendKeys('{Esc}') return members def GetAllFriendNicknames(self, keywords=None): '''获得所有好友昵称列表 注: 1. 该范例运行技巧取决于好友数目,约每秒6~8个好友的速率 2. 该范例未进程大批测试,可能存在未知问题,如有问题请微信群内响应 Args: keywords (str, optional): 搜索关键词,只复返包含关键词的好友昵称列表 Returns: list: 所有好友昵称列表 ''' self._show() self.SwitchToContact() self.SessionBox.ListControl(Name='关系东谈主').ButtonControl(Name='通信录措置').Click(simulateMove=False) contactwnd = ContactWnd() if keywords: contactwnd.Search(keywords) friends_nicknames = contactwnd.GetAllFriendNicknames() contactwnd.Close() self.SwitchToChat() return friends_nicknames
代码赏析以及竣事旨趣
启动化:在类的启动化范例中,当先查验微信是否照旧运行,淌若莫得运行,则尝试启动微信。获得微信的窗口句柄,并诞生微信窗口的大小和位置。获得会话列表:GetSessionList 范例用于获妥贴前聊天列表中的所有聊天对象。它会遍历聊天列表,并复返每个聊天对象的称号和新音信数目。
反差切换聊天对象:ChatWith 范例用于切换到指定的聊天对象。
发送音信:SendMsg 范例用于向现时聊天对象发送文本音信。它会将音信发送到现时聊天对象的剪辑框中,并发送回车键以发送音信。
发送文献:SendFiles 范例用于向现时聊天对象发送文献。
获得聊天纪录:GetAllMessage 范例用于获妥贴前聊天对象的所有聊天纪录。它会遍历聊天纪录列表,并复返每个聊天纪录的信息。获妥贴前聊天对象称号:CurrentChat 范例用于获妥贴前聊天对象的称号。
获得新的好友苦求列表:GetNewFriends 范例用于获得新的好友苦求列表。它会遍历新的好友苦求列表,并复返每个好友苦求的信息。
添加监听对象:AddListenChat 范例用于添加监听对象。它会创建一个新的 ChatWnd 对象,并将其添加到监听对象字典中。
获得监听对象的新音信:GetListenMessage 范例用于获得监听对象的新音信。它会遍历监听对象字典,并获得每个监听对象的新音信。
切换到通信录页面:SwitchToContact 范例用于切换到通信录页面。切换到聊天页面:SwitchToChat 范例用于切换到聊天页面。
获妥贴前聊天群成员:GetGroupMembers 范例用于获妥贴前聊天群的成员列表。它会遍历聊天群成员列表,并复返每个成员的称号。
获得所有好友昵称列表:GetAllFriendNicknames 范例用于获得所有好友的昵称列表。它会打怒放信录窗口,并搜索指定的关键词(淌若提供了关键词)。然后,它会遍历通信录列表,并复返每个好友的昵称。临了,它会关闭通信录窗口,并切换回聊天页面。
————————————————马来西亚#文爱
本站仅提供存储劳动,所有履行均由用户发布,如发现存害或侵权履行,请点击举报。上一篇:匿名 文爱 app 深入信顶底战法【副图|选股】倡导 – 金倡导研习社
下一篇:没有了