感谢这位作者,解释挺详细
from pywinauto.application import Application
from pywinauto.keyboard import send_keys
import time
import psutil
import winreg
# 输入进程名,获取PID
def get_pid(p_name):
pids = psutil.pids()
for pid in pids:
p = psutil.Process(pid)
if p_name in p.name():
return pid
def get_wechat_installation_path():
key_path = r"Software\Tencent\WeChat"
try:
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path, 0, winreg.KEY_READ)
value, _ = winreg.QueryValueEx(key, "InstallPath")
return value[0].upper() + value[1:] + '\WeChat.exe'
except FileNotFoundError:
print("找不到微信程序")
return None
chat_name = "文件传输助手" # 需要发送消息的聊天名称
message = "测试" # 需要发送的消息
we_chat_path = get_wechat_installation_path() # 微信路径 C:\Program Files (x86)\Tencent\WeChat
# 获取微信PID并获取微信窗口
we_chat_id = get_pid("WeChat.exe")
app = Application(backend='uia').connect(process=we_chat_id)
we_chat_main_dialog = app.window(class_name='WeChatMainWndForPC')
# 微信挂在后台时,通过再次运行唤醒
if not we_chat_main_dialog.exists():
tmp = Application().start(we_chat_path)
# 通过先最小化,再恢复使得窗口置顶
we_chat_main_dialog.minimize()
we_chat_main_dialog.restore()
# for control in we_chat_main_dialog.print_control_identifiers():
# # 打印控件的类名和标题
# print(control)
# 通过搜索,定位聊天
search_elem = we_chat_main_dialog.child_window(control_type='Edit', title='搜索')
search_elem.click_input()
search_elem.type_keys('^a').type_keys(chat_name)
time.sleep(1)
send_keys('{ENTER}')
# 点击要发送消息的聊天
chat_list = we_chat_main_dialog.child_window(control_type='List', title='会话')
for chat_item in chat_list.items():
if chat_name in chat_item.element_info.name:
chat_item.click_input()
time.sleep(1)
# 获取聊天记录
message_list = we_chat_main_dialog.child_window(control_type='List', title='消息')
for message_item in message_list.items():
print(message_item.window_text())
# 输入并发送消息
# app["Dialog"]["Edit1"].TypeKeys("E:\\1.jpg")
# app["Dialog"]["Edit1"].TypeKeys("C:\\Users\\Administrator\\Desktop\\用印申请.pdf")
edit_elem = we_chat_main_dialog.child_window(control_type='Edit', title=chat_name)
edit_elem.click_input()
edit_elem.type_keys(message, with_spaces=True)
time.sleep(1)
send_keys('{ENTER}')
# send_keys(message)
# send_keys('{ENTER}')
# time.sleep(2)
“”"
实现自动发送消息
“”"
import time
import os
from pywinauto.keyboard import send_keys #键盘
while True:
time_now = time.strftime("%H:%M:%S", time.localtime()) # 获取当前时间
sent_time = time.strftime("%H:%M:%S", time.localtime()) # 发送时间
if time_now == sent_time: # 当前时间等于发送时间则执行以下程序
def open_app(app_dir):
os.startfile(app_dir)
# 打开微信
if __name__ == "__main__":
app_dir = r'C:\Program Files (x86)\Tencent\WeChat\WeChat.exe' # 此处为微信的绝对路径
open_app(app_dir)
time.sleep(1)
#进入微信,模拟按键Ctrl+F
send_keys('^f')
send_keys('请输入联系人')
time.sleep(1)
send_keys('{ENTER}') # 回车键必须全部大小
#需要发送的消息内容
message = '啦啦!!,是时候休息啦!Good night!!!'
time.sleep(1)
# 输入聊天内容
send_keys(message)
# 回车发送消息
send_keys('{ENTER}')
time.sleep(3)
print('退出~~~')
exit() # 退出程序
import uiautomation as uia
import win32gui, win32con
import win32clipboard as wc
import time
import os
import psutil
from pywinauto.application import Application
COPYDICT = {}
class WxParam:
SYS_TEXT_HEIGHT = 33
TIME_TEXT_HEIGHT = 34
RECALL_TEXT_HEIGHT = 45
CHAT_TEXT_HEIGHT = 52
CHAT_IMG_HEIGHT = 117
SpecialTypes = ['[文件]', '[图片]', '[视频]', '[音乐]', '[链接]']
class WxUtils:
@staticmethod
def start_we_chat():
# 输入进程名,获取PID
def get_pid(p_name):
pids = psutil.pids()
for pid in pids:
p = psutil.Process(pid)
if p_name in p.name():
return pid
we_chat_path = r"D:\Program Files (x86)\Tencent\WeChat\WeChat.exe" # 微信路径 C:\Program Files (x86)\Tencent\WeChat
# 获取微信PID并获取微信窗口
we_chat_id = get_pid("WeChat.exe")
app = Application(backend='uia').connect(process=we_chat_id)
we_chat_main_dialog = app.window(class_name='WeChatMainWndForPC')
# 微信挂在后台时,通过再次运行唤醒
if not we_chat_main_dialog.exists():
tmp = Application().start(we_chat_path)
# 通过先最小化,再恢复使得窗口置顶
we_chat_main_dialog.minimize()
we_chat_main_dialog.restore()
def SplitMessage(MsgItem):
uia.SetGlobalSearchTimeout(0)
MsgItemName = MsgItem.Name
if MsgItem.BoundingRectangle.height() == WxParam.SYS_TEXT_HEIGHT:
Msg = ('SYS', MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()]))
elif MsgItem.BoundingRectangle.height() == WxParam.TIME_TEXT_HEIGHT:
Msg = ('Time', MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()]))
elif MsgItem.BoundingRectangle.height() == WxParam.RECALL_TEXT_HEIGHT:
if '撤回' in MsgItemName:
Msg = ('Recall', MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()]))
else:
Msg = ('SYS', MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()]))
else:
Index = 1
User = MsgItem.ButtonControl(foundIndex=Index)
try:
while True:
if User.Name == '':
Index += 1
User = MsgItem.ButtonControl(foundIndex=Index)
else:
break
Msg = (User.Name, MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()]))
except:
Msg = ('SYS', MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()]))
uia.SetGlobalSearchTimeout(10.0)
return Msg
def SetClipboard(data, dtype='text'):
'''复制文本信息或图片到剪贴板
data : 要复制的内容,str 或 Image 图像'''
if dtype.upper() == 'TEXT':
type_data = win32con.CF_UNICODETEXT
elif dtype.upper() == 'IMAGE':
from io import BytesIO
type_data = win32con.CF_DIB
output = BytesIO()
data.save(output, 'BMP')
data = output.getvalue()[14:]
else:
raise ValueError('param (dtype) only "text" or "image" supported')
wc.OpenClipboard()
wc.EmptyClipboard()
wc.SetClipboardData(type_data, data)
wc.CloseClipboard()
def Screenshot(hwnd, to_clipboard=True):
'''为句柄为hwnd的窗口程序截图
hwnd : 句柄
to_clipboard : 是否复制到剪贴板
'''
import pyscreenshot as shot
bbox = win32gui.GetWindowRect(hwnd)
win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0, \
win32con.SWP_SHOWWINDOW | win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
win32gui.SetWindowPos(hwnd, win32con.HWND_NOTOPMOST, 0, 0, 0, 0, \
win32con.SWP_SHOWWINDOW | win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
win32gui.BringWindowToTop(hwnd)
im = shot.grab(bbox)
if to_clipboard:
WxUtils.SetClipboard(im, 'image')
return im
def SavePic(savepath=None, filename=None):
Pic = uia.WindowControl(ClassName='ImagePreviewWnd', Name='图片查看')
Pic.SendKeys('{Ctrl}s')
SaveAs = Pic.WindowControl(ClassName='#32770', Name='另存为...')
SaveAsEdit = SaveAs.EditControl(ClassName='Edit', Name='文件名:')
SaveButton = Pic.ButtonControl(ClassName='Button', Name='保存(S)')
PicName, Ex = os.path.splitext(SaveAsEdit.GetValuePattern().Value)
if not savepath:
savepath = os.getcwd()
if not filename:
filename = PicName
FilePath = os.path.realpath(os.path.join(savepath, filename + Ex))
SaveAsEdit.SendKeys(FilePath)
SaveButton.Click()
Pic.SendKeys('{Esc}')
def ControlSize(control):
locate = control.BoundingRectangle
size = (locate.width(), locate.height())
return size
def ClipboardFormats(unit=0, *units):
units = list(units)
wc.OpenClipboard()
u = wc.EnumClipboardFormats(unit)
wc.CloseClipboard()
units.append(u)
if u:
units = WxUtils.ClipboardFormats(u, *units)
return units
def CopyDict(self):
Dict = {}
for i in WxUtils.ClipboardFormats():
if i == 0:
continue
wc.OpenClipboard()
try:
content = wc.GetClipboardData(i)
wc.CloseClipboard()
except:
wc.CloseClipboard()
raise ValueError
if len(str(i)) >= 4:
Dict[str(i)] = content
return Dict
class WeChat:
def __init__(self):
WxUtils.start_we_chat()
self.UiaAPI = uia.WindowControl(ClassName='WeChatMainWndForPC')
self.SessionList = self.UiaAPI.ListControl(Name='会话')
self.EditMsg = self.UiaAPI.EditControl(Name='输入')
self.SearchBox = self.UiaAPI.EditControl(Name='搜索')
self.MsgList = self.UiaAPI.ListControl(Name='消息')
self.SessionItemList = []
def GetSessionList(self, reset=False):
'''获取当前会话列表,更新会话列表'''
self.SessionItem = self.SessionList.ListItemControl()
SessionList = []
if reset:
self.SessionItemList = []
for i in range(100):
try:
name = self.SessionItem.Name
except:
break
if name not in self.SessionItemList:
self.SessionItemList.append(name)
if name not in SessionList:
SessionList.append(name)
self.SessionItem = self.SessionItem.GetNextSiblingControl()
return SessionList
def Search(self, keyword):
'''
查找微信好友或关键词
keywords: 要查找的关键词,str * 最好完整匹配,不完全匹配只会选取搜索框第一个
'''
self.UiaAPI.SetFocus()
time.sleep(0.2)
self.UiaAPI.SendKeys('{Ctrl}f', waitTime=1)
self.SearchBox.SendKeys(keyword, waitTime=1.5)
self.SearchBox.SendKeys('{Enter}')
def ChatWith(self, who, RollTimes=None):
'''
打开某个聊天框
who : 要打开的聊天框好友名,str; * 最好完整匹配,不完全匹配只会选取搜索框第一个
RollTimes : 默认向下滚动多少次,再进行搜索
'''
self.UiaAPI.SwitchToThisWindow()
RollTimes = 10 if not RollTimes else RollTimes
def roll_to(who=who, RollTimes=RollTimes):
for i in range(RollTimes):
if who not in self.GetSessionList()[:-1]:
self.SessionList.WheelDown(wheelTimes=3, waitTime=0.1 * i)
else:
time.sleep(0.5)
self.SessionList.ListItemControl(Name=who).Click(simulateMove=False)
return 1
return 0
rollresult = roll_to()
if rollresult:
return 1
else:
self.Search(who)
return roll_to(RollTimes=1)
def SendMsg(self, msg, clear=True):
'''向当前窗口发送消息
msg : 要发送的消息
clear : 是否清除当前已编辑内容
'''
self.UiaAPI.SwitchToThisWindow()
if clear:
self.EditMsg.SendKeys('{Ctrl}a', waitTime=0)
self.EditMsg.SendKeys(msg, waitTime=0)
self.EditMsg.SendKeys('{Enter}', waitTime=0)
def SendFiles(self, *filepath, not_exists='ignore'):
"""向当前聊天窗口发送文件
not_exists: 如果未找到指定文件,继续或终止程序
*filepath: 要复制文件的绝对路径"""
global COPYDICT
key = ''
for file in filepath:
file = os.path.realpath(file)
if not os.path.exists(file):
if not_exists.upper() == 'IGNORE':
print('File not exists:', file)
continue
elif not_exists.upper() == 'RAISE':
raise FileExistsError('File Not Exists: %s' % file)
else:
raise ValueError('param not_exists only "ignore" or "raise" supported')
key += ' ' % file
if not key:
return 0
if not COPYDICT:
self.EditMsg.SendKeys(' ', waitTime=0)
self.EditMsg.SendKeys('{Ctrl}a', waitTime=0)
self.EditMsg.SendKeys('{Ctrl}c', waitTime=0)
self.EditMsg.SendKeys('{Delete}', waitTime=0)
while True:
try:
COPYDICT = WxUtils.CopyDict()
break
except:
pass
wc.OpenClipboard()
wc.EmptyClipboard()
wc.SetClipboardData(13, '')
wc.SetClipboardData(16, b'\x04\x08\x00\x00')
wc.SetClipboardData(1, b'')
wc.SetClipboardData(7, b'')
for i in COPYDICT:
copydata = COPYDICT[i].replace(b'', key.encode()).replace(b'type="0"',
b'type="3"')
wc.SetClipboardData(int(i), copydata)
wc.CloseClipboard()
self.SendClipboard()
return 1
def SendClipboard(self):
'''向当前聊天页面发送剪贴板复制的内容'''
self.SendMsg('{Ctrl}v')
@property
def GetAllMessage(self):
'''获取当前窗口中加载的所有聊天记录'''
MsgDocker = []
MsgItems = self.MsgList.GetChildren()
for MsgItem in MsgItems:
MsgDocker.append(WxUtils.SplitMessage(MsgItem))
return MsgDocker
@property
def GetLastMessage(self):
'''获取当前窗口中最后一条聊天记录'''
uia.SetGlobalSearchTimeout(1.0)
MsgItem = self.MsgList.GetChildren()[-1]
Msg = WxUtils.SplitMessage(MsgItem)
uia.SetGlobalSearchTimeout(10.0)
return Msg
def LoadMoreMessage(self, n=0.1):
'''定位到当前聊天页面,并往上滚动鼠标滚轮,加载更多聊天记录到内存'''
n = 0.1 if n < 0.1 else 1 if n > 1 else n
self.MsgList.WheelUp(wheelTimes=int(500 * n), waitTime=0.1)
def SendScreenshot(self, name=None, classname=None):
'''发送某个桌面程序的截图,如:微信、记事本...
name : 要发送的桌面程序名字,如:微信
classname : 要发送的桌面程序类别名,一般配合 spy 小工具使用,以获取类名,如:微信的类名为 WeChatMainWndForPC'''
if name and classname:
return 0
else:
hwnd = win32gui.FindWindow(classname, name)
if hwnd:
WxUtils.Screenshot(hwnd)
self.SendClipboard()
return 1
else:
return 0
if __name__ == '__main__':
a = WeChat()
print(a.GetAllMessage)