在前面《UI自动化轻松解决微信手工群发消息的烦恼》一文中,我演示了如何使用uiautomation控制微信发送文本消息。
前几天又有群友询问:
虽然之前觉得太简单懒得做,但今天又想起这个问题,考虑到很多网友不知道如何实现,所以今天再简单演示一下。
uiautomation控制微信发送图片
其实原理非常简单,之前无非是将需要发送的文本复制到剪切板再粘贴,那么其实对于图片也一样,我们只需要将需要发送的图片放入剪切板即可,其他地方逻辑都一样。
那么如何将图片放入剪切板呢?
uiautomation已经提供了SetClipboardBitmap
方法,实现代码如下:
from uiautomation.uiautomation import Bitmap
auto.SetClipboardBitmap(Bitmap.FromFile(path))
之前我们发送图片消息的完整代码为:
import time
import uiautomation as auto
wechatWindow = auto.WindowControl(
searchDepth=1, Name="微信", ClassName='WeChatMainWndForPC')
wechatWindow.SetActive()
search = wechatWindow.EditControl(Name='搜索')
edit = wechatWindow.EditControl(Name='输入')
messages = wechatWindow.ListControl(Name='消息')
sendButton = wechatWindow.ButtonControl(Name='发送(S)')
def send2name(name, txt, wait_time=0.1):
search.Click()
auto.SetClipboardText(name)
edit.SendKeys('{Ctrl}v')
# 等待微信索引搜索跟上
time.sleep(wait_time)
search.SendKeys("{Enter}")
auto.SetClipboardText(txt)
edit.SendKeys('{Ctrl}v')
sendButton.Click()
name = "小小明"
txt = "小小明你好,收到这条消息说明你的程序已经成功---来自自动化测试程序"
send2name(name, txt)
下面我们改造到支持图片发送:
import time
import uiautomation as auto
from uiautomation.uiautomation import Bitmap
wechatWindow = auto.WindowControl(
searchDepth=1, Name="微信", ClassName='WeChatMainWndForPC')
wechatWindow.SetActive()
search = wechatWindow.EditControl(Name='搜索')
edit = wechatWindow.EditControl(Name='输入')
messages = wechatWindow.ListControl(Name='消息')
sendButton = wechatWindow.ButtonControl(Name='发送(S)')
def selectSessionFromName(name, wait_time=0.1):
search.Click()
auto.SetClipboardText(name)
edit.SendKeys('{Ctrl}v')
# 等待微信索引搜索跟上
time.sleep(wait_time)
search.SendKeys("{Enter}")
def send_msg(content, msg_type=1):
if msg_type:
auto.SetClipboardText(content)
else:
auto.SetClipboardBitmap(Bitmap.FromFile(content))
edit.SendKeys('{Ctrl}v')
sendButton.Click()
测试一下切换并发送文本消息和图片消息:
name = "小小明"
selectSessionFromName(name)
content = "小小明你好,收到这条消息说明你的程序已经成功---来自自动化测试程序"
send_msg(content)
content = r"C:\Users\ASUS\Pictures\avatar.png"
send_msg(content, msg_type=0)
这样,我们就完成了图片消息的发送。
图片消息的转发
上文中,已经实现了转发当前最后一条文本消息的功能,但是假如最后一条消息是图片时,之前的方法就会报错退出。这到底是什么情况呢?我们看看图片与文本消息节点的异同:
为了使代码更加通用,我们必须分析出更通用的规律,发下图可以发现规律:
每一条消息由一个面板的三个子节点组成,首尾两个节点的内容互斥,首节点有按钮表示非本人发送的消息,尾节点有按钮表示自己发送的消息,中间节点是一个包括了消息内容的面板。
那么可以将之前的代码由message.EditControl().RightClick()
修改为message.GetFirstChildControl().GetChildren()[1].RightClick()
另外之前的代码可以排除最后一条消息为拍一拍的情况,但实际上最后一条还有可能是撤回,也需要排除。在向前寻找节点时可能碰到时间,也需要排除掉。
对于转发消息,我们基本只需要转发自己发送的消息,下面的代码将再当前窗口中找到,自己发送的最后一条消息,当然需要保证自己最后发送的消息在可视范围内,只要是向文件传输助手发送的消息都不会存在这个问题。
改造之前的消息发送代码,最终如下:
def relay2users(users, max_n=9, wait_time=0.1):
message = messages.GetLastChildControl()
while True:
nodes = message.GetFirstChildControl().GetChildren()
if nodes and nodes[2].ControlTypeName == "ButtonControl":
break
message = message.GetPreviousSiblingControl()
nodes[1].RightClick()
menu = wechatWindow.MenuControl()
menu_items = menu.GetLastChildControl().GetFirstChildControl().GetChildren()
for menu_item in menu_items:
if menu_item.ControlTypeName != "MenuItemControl":
continue
if menu_item.Name == "转发":
menu_item.Click()
break
send2ps = wechatWindow.WindowControl(Name="转发给", ClassName='SelectContactWnd')
receiver = send2ps.EditControl(Name="搜索")
sendB2 = send2ps.ButtonControl(Name='发送')
items = send2ps.ListControl()
for i in range(0, len(users), max_n):
users_split = users[i:i+max_n]
for user in users_split:
receiver.Click()
auto.SetClipboardText(user)
receiver.SendKeys('{Ctrl}v')
# 等待UI组件的渲染变化
time.sleep(wait_time)
items.ButtonControl().Click()
receiver.GetParentControl().GetNextSiblingControl().Click()
sendB2.Click()
下面测试一下转发这条图片消息到指定会话名称:
users = ["文件传输助手", "小小明", "Playmarker",
"数据分析与商业化设计", "道财Gdc", "云朵君MrCloudData"]
relay2users(users)
可以看到这样就实现了图片消息的转发。