Skip to content

Commit 2f0fa30

Browse files
CoderWanFengheyWFeng
authored andcommitted
发布0.1.10
1 parent a656555 commit 2f0fa30

File tree

5 files changed

+326
-29
lines changed

5 files changed

+326
-29
lines changed
File renamed without changes.

PR/Dumogu/WeChatType.py

+312
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
# -*-coding:utf-8-*-
2+
# sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='gb18030')
3+
4+
5+
import uiautomation as uia
6+
import win32gui, win32con
7+
import win32clipboard as wc
8+
import time
9+
import os
10+
11+
PUBLISH_ID = '公众号:程序员晚枫'
12+
13+
COPYDICT = {}
14+
15+
class WxParam:
16+
SYS_TEXT_HEIGHT = 33
17+
TIME_TEXT_HEIGHT = 34
18+
RECALL_TEXT_HEIGHT = 45
19+
CHAT_TEXT_HEIGHT = 52
20+
CHAT_IMG_HEIGHT = 117
21+
SpecialTypes = ['[文件]', '[图片]', '[视频]', '[音乐]', '[链接]']
22+
23+
24+
class WxUtils:
25+
def SplitMessage(MsgItem):
26+
uia.SetGlobalSearchTimeout(0)
27+
MsgItemName = MsgItem.Name
28+
if MsgItem.BoundingRectangle.height() == WxParam.SYS_TEXT_HEIGHT:
29+
Msg = ('SYS', MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()]))
30+
elif MsgItem.BoundingRectangle.height() == WxParam.TIME_TEXT_HEIGHT:
31+
Msg = ('Time', MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()]))
32+
elif MsgItem.BoundingRectangle.height() == WxParam.RECALL_TEXT_HEIGHT:
33+
if '撤回' in MsgItemName:
34+
Msg = ('Recall', MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()]))
35+
else:
36+
Msg = ('SYS', MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()]))
37+
else:
38+
Index = 1
39+
User = MsgItem.ButtonControl(foundIndex=Index)
40+
try:
41+
while True:
42+
if User.Name == '':
43+
Index += 1
44+
User = MsgItem.ButtonControl(foundIndex=Index)
45+
else:
46+
break
47+
Msg = (User.Name, MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()]))
48+
except:
49+
Msg = ('SYS', MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()]))
50+
uia.SetGlobalSearchTimeout(10.0)
51+
return Msg
52+
53+
def SetClipboard(data, dtype='text'):
54+
'''复制文本信息或图片到剪贴板
55+
data : 要复制的内容,str 或 Image 图像'''
56+
if dtype.upper() == 'TEXT':
57+
type_data = win32con.CF_UNICODETEXT
58+
elif dtype.upper() == 'IMAGE':
59+
from io import BytesIO
60+
type_data = win32con.CF_DIB
61+
output = BytesIO()
62+
data.save(output, 'BMP')
63+
data = output.getvalue()[14:]
64+
else:
65+
raise ValueError('param (dtype) only "text" or "image" supported')
66+
wc.OpenClipboard()
67+
wc.EmptyClipboard()
68+
wc.SetClipboardData(type_data, data)
69+
wc.CloseClipboard()
70+
71+
def Screenshot(hwnd, to_clipboard=True):
72+
'''为句柄为hwnd的窗口程序截图
73+
hwnd : 句柄
74+
to_clipboard : 是否复制到剪贴板
75+
'''
76+
import pyscreenshot as shot
77+
bbox = win32gui.GetWindowRect(hwnd)
78+
win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0, \
79+
win32con.SWP_SHOWWINDOW | win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
80+
win32gui.SetWindowPos(hwnd, win32con.HWND_NOTOPMOST, 0, 0, 0, 0, \
81+
win32con.SWP_SHOWWINDOW | win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
82+
win32gui.BringWindowToTop(hwnd)
83+
im = shot.grab(bbox)
84+
if to_clipboard:
85+
WxUtils.SetClipboard(im, 'image')
86+
return im
87+
88+
def SavePic(savepath=None, filename=None):
89+
Pic = uia.WindowControl(ClassName='ImagePreviewWnd', Name='图片查看')
90+
Pic.SendKeys('{Ctrl}s')
91+
SaveAs = Pic.WindowControl(ClassName='#32770', Name='另存为...')
92+
SaveAsEdit = SaveAs.EditControl(ClassName='Edit', Name='文件名:')
93+
SaveButton = Pic.ButtonControl(ClassName='Button', Name='保存(S)')
94+
PicName, Ex = os.path.splitext(SaveAsEdit.GetValuePattern().Value)
95+
if not savepath:
96+
savepath = os.getcwd()
97+
if not filename:
98+
filename = PicName
99+
FilePath = os.path.realpath(os.path.join(savepath, filename + Ex))
100+
SaveAsEdit.SendKeys(FilePath)
101+
SaveButton.Click()
102+
Pic.SendKeys('{Esc}')
103+
104+
def ControlSize(control):
105+
locate = control.BoundingRectangle
106+
size = (locate.width(), locate.height())
107+
return size
108+
109+
def ClipboardFormats(unit=0, *units):
110+
units = list(units)
111+
wc.OpenClipboard()
112+
u = wc.EnumClipboardFormats(unit)
113+
wc.CloseClipboard()
114+
units.append(u)
115+
if u:
116+
units = WxUtils.ClipboardFormats(u, *units)
117+
return units
118+
119+
def CopyDict(self):
120+
Dict = {}
121+
for i in WxUtils.ClipboardFormats():
122+
if i == 0:
123+
continue
124+
wc.OpenClipboard()
125+
try:
126+
content = wc.GetClipboardData(i)
127+
wc.CloseClipboard()
128+
except:
129+
wc.CloseClipboard()
130+
raise ValueError
131+
if len(str(i)) >= 4:
132+
Dict[str(i)] = content
133+
return Dict
134+
135+
136+
class WeChat:
137+
def __init__(self):
138+
self.UiaAPI = uia.WindowControl(ClassName='WeChatMainWndForPC')
139+
140+
self.SessionList = self.UiaAPI.ListControl(Name='会话')
141+
self.EditMsg = self.UiaAPI.EditControl(Name='输入')
142+
self.SearchBox = self.UiaAPI.EditControl(Name='搜索')
143+
self.MsgList = self.UiaAPI.ListControl(Name='消息')
144+
self.SessionItemList = []
145+
self.Group = self.UiaAPI.ButtonControl().Name
146+
147+
def GetSessionList(self, reset=False):
148+
'''获取当前会话列表,更新会话列表'''
149+
self.SessionItem = self.SessionList.ListItemControl()
150+
SessionList = []
151+
if reset:
152+
self.SessionItemList = []
153+
for i in range(100):
154+
try:
155+
name = self.SessionItem.Name
156+
except:
157+
break
158+
if name not in self.SessionItemList:
159+
self.SessionItemList.append(name)
160+
if name not in SessionList:
161+
SessionList.append(name)
162+
self.SessionItem = self.SessionItem.GetNextSiblingControl()
163+
return SessionList
164+
165+
def Search(self, keyword):
166+
'''
167+
查找微信好友或关键词
168+
keywords: 要查找的关键词,str * 最好完整匹配,不完全匹配只会选取搜索框第一个
169+
'''
170+
self.UiaAPI.SetFocus()
171+
time.sleep(0.1)
172+
self.UiaAPI.SendKeys('{Ctrl}f{Ctrl}a', waitTime=0.2)
173+
self.SearchBox.SendKeys(keyword, waitTime=0.2)
174+
self.SearchBox.SendKeys('{Enter}')
175+
176+
def ChatWith(self, who, RollTimes=None):
177+
'''
178+
打开某个聊天框
179+
who : 要打开的聊天框好友名,str; * 最好完整匹配,不完全匹配只会选取搜索框第一个
180+
RollTimes : 默认向下滚动多少次,再进行搜索
181+
'''
182+
self.UiaAPI.SwitchToThisWindow()
183+
RollTimes = 1 if not RollTimes else RollTimes
184+
185+
def roll_to(who=who, RollTimes=RollTimes):
186+
for i in range(RollTimes):
187+
if who not in self.GetSessionList()[:-1]:
188+
self.SessionList.WheelDown(wheelTimes=1, waitTime=0.01 * i)
189+
else:
190+
time.sleep(0.1)
191+
self.SessionList.ListItemControl(Name=who).Click(simulateMove=False)
192+
return 1
193+
return 0
194+
195+
rollresult = roll_to()
196+
if rollresult:
197+
return 1
198+
else:
199+
self.Search(who)
200+
return roll_to(RollTimes=1)
201+
202+
def SendMsg(self, msg, clear=True):
203+
'''向当前窗口发送消息
204+
msg : 要发送的消息
205+
clear : 是否清除当前已编辑内容
206+
'''
207+
self.UiaAPI.SwitchToThisWindow()
208+
if clear:
209+
self.EditMsg.SendKeys('{Ctrl}a', waitTime=0)
210+
self.EditMsg.SendKeys(msg, waitTime=0)
211+
self.EditMsg.SendKeys('{Ctrl}{Enter}', waitTime=0)
212+
213+
def SendEnd(self, clear=False):
214+
'''向当前窗口发送消息
215+
msg : 要发送的消息
216+
clear : 是否清除当前已编辑内容
217+
'''
218+
self.UiaAPI.SwitchToThisWindow()
219+
self.EditMsg.SendKeys('{Enter}', waitTime=0)
220+
221+
def SendFiles(self, *filepath, not_exists='ignore'):
222+
"""向当前聊天窗口发送文件
223+
not_exists: 如果未找到指定文件,继续或终止程序
224+
*filepath: 要复制文件的绝对路径"""
225+
global COPYDICT
226+
key = ''
227+
for file in filepath:
228+
file = os.path.realpath(file)
229+
if not os.path.exists(file):
230+
if not_exists.upper() == 'IGNORE':
231+
print('File not exists:', file)
232+
continue
233+
elif not_exists.upper() == 'RAISE':
234+
raise FileExistsError('File Not Exists: %s' % file)
235+
else:
236+
raise ValueError('param not_exists only "ignore" or "raise" supported')
237+
key += '<EditElement type="3" filepath="%s" shortcut="" />' % file
238+
if not key:
239+
return 0
240+
if not COPYDICT:
241+
self.EditMsg.SendKeys(' ', waitTime=0)
242+
self.EditMsg.SendKeys('{Ctrl}a', waitTime=0)
243+
self.EditMsg.SendKeys('{Ctrl}c', waitTime=0)
244+
self.EditMsg.SendKeys('{Delete}', waitTime=0)
245+
while True:
246+
try:
247+
COPYDICT = WxUtils.CopyDict()
248+
break
249+
except:
250+
pass
251+
wc.OpenClipboard()
252+
wc.EmptyClipboard()
253+
wc.SetClipboardData(13, '')
254+
wc.SetClipboardData(16, b'\x04\x08\x00\x00')
255+
wc.SetClipboardData(1, b'')
256+
wc.SetClipboardData(7, b'')
257+
for i in COPYDICT:
258+
copydata = COPYDICT[i].replace(b'<EditElement type="0" pasteType="0"><![CDATA[ ]]></EditElement>',
259+
key.encode()).replace(b'type="0"', b'type="3"')
260+
wc.SetClipboardData(int(i), copydata)
261+
wc.CloseClipboard()
262+
self.SendClipboard()
263+
return 1
264+
265+
def SendClipboard(self):
266+
'''向当前聊天页面发送剪贴板复制的内容'''
267+
self.SendMsg('{Ctrl}v')
268+
269+
@property
270+
def GetAllMessage(self):
271+
'''获取当前窗口中加载的所有聊天记录'''
272+
MsgDocker = []
273+
MsgItems = self.MsgList.GetChildren()
274+
for MsgItem in MsgItems:
275+
MsgDocker.append(WxUtils.SplitMessage(MsgItem))
276+
return MsgDocker
277+
278+
@property
279+
def GetLastMessage(self):
280+
'''获取当前窗口中最后一条聊天记录'''
281+
try:
282+
uia.SetGlobalSearchTimeout(1.0)
283+
MsgItem = self.MsgList.GetChildren()[-1]
284+
ChatName = MsgItem.ButtonControl() # 聊天对象应当从消息子目录获取
285+
uia.SetGlobalSearchTimeout(2.0)
286+
return MsgItem.Name, ChatName.Name # 返回正确的聊天信息和聊天对象
287+
except LookupError:
288+
pass
289+
290+
def LoadMoreMessage(self, n=0.1):
291+
'''定位到当前聊天页面,并往上滚动鼠标滚轮,加载更多聊天记录到内存'''
292+
n = 0.1 if n < 0.1 else 1 if n > 1 else n
293+
self.MsgList.WheelUp(wheelTimes=int(500 * n), waitTime=0.1)
294+
295+
def SendScreenshot(self, name=None, classname=None):
296+
'''发送某个桌面程序的截图,如:微信、记事本...
297+
name : 要发送的桌面程序名字,如:微信
298+
classname : 要发送的桌面程序类别名,一般配合 spy 小工具使用,以获取类名,如:微信的类名为 WeChatMainWndForPC'''
299+
if name and classname:
300+
return 0
301+
else:
302+
hwnd = win32gui.FindWindow(classname, name)
303+
if hwnd:
304+
WxUtils.Screenshot(hwnd)
305+
self.SendClipboard()
306+
return 1
307+
else:
308+
return 0
309+
310+
def SavePic(self, savepath=None, filename=None):
311+
WxUtils.SavePic()
312+
# Pic = uia.WindowControl(ClassName='ImagePreviewWnd', Name='图片查看')

PyOfficeRobot/api/chat.py

-3
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,5 @@ def chat_by_keywords(who, keywords):
3838

3939
temp_msg = receive_msg
4040
wx.SendMsg(keywords[receive_msg]) # 向`who`发送消息
41-
else:
42-
temp_msg = receive_msg
43-
wx.SendMsg(keywords[receive_msg]) # 向`who`发送消息,向群组发送消息
4441
except:
4542
pass

0 commit comments

Comments
 (0)