最近在给朋友做个工作用的软件,需要和微信配套使用且需要实现一些自动操作。
- 通过好友请求并随机备注。
- 根据关键字自动回复,包括文字、图片及文件。
- 定时发送信息,包括单次、隔日、每周末及每日。
- 处理转账,根据金额及备注进行相应操作,包括接收转账后一系列操作或退回转账。
- 处理扫码付款,同上一条相类似。
- 手动批量发送信息
- 保存所有好友信息
上诉功能可以很容易实现,但是还存在几个不足点,例如
- 不能够后台操作,也就是说微信在工作期间,在一些操作过程中,电脑不能正常使用,其他时间无影响。针对这个情况,有一个解决方案,即将任务设置放在本地,将微信放在服务器,天翼云的低配虚拟主机,八十元/年。
- 处理转账较慢,大概最短需要3秒,最长需要5秒。这一过程暂时没有找到更合适的解决办法。
以上就是本篇文章的简要介绍,下面来具体谈一谈。
1. 工具说明
1.1 微信版本
我一直使用的是2.9.5版本。最新版本和更旧的版本我都使用过,在所有可用的版本中,这个版本是运行最合适的一个版本,旧版本有些功能不全,而最新版本过于臃肿,所以建议找到这个版本使用。
1.2 python 版本
我是用的是 python3.5,在 pycharm 中建立虚拟环境,对于库包,随用随装。
2. 总览
设置端是按照朋友需求写的,方法都是写好的,直接调用就可以。设置端可以根据自己的需求写。在此我只是介绍一下代码能够实现的功能。
2.1 好友信息列表
2.2 自动回复设置
2.3 好友转账及二维码付款
2.4 定时信息
3. 实现过程
from pywinauto.application import Application
class WeChat:
PID = get_pid()
if not self.PID:
print('未检测到微信运行')
return 0
else:
self.app = Application(backend='uia')
self.app.connect(process=self.PID)
self.win = self.app[u'微信']
print('已建立与微信链接')
整个包装成类,方便在写设置端的时候直接继承,方便使用
3.1 获取微信PID
from psutil import process_iter
def get_pid(self):
PID = process_iter()
for pid_temp in PID :
pid_dic = pid_temp .as_dict(attrs=['pid','name'])
if pid_dic['name'] == 'WeChat.exe':
return pid_dic['pid']
else:
return 0
3.2 获取好友信息
def get_text(self):
try:
return self.win.child_window(title="消息", control_type="List").child_window(control_type="Edit", found_index=0).window_text()
except:
return ''
3.3 包装点击控件
from pywinauto import mouse
def click_one(self, title='', control_type='', button='left', found_index=0, left=10, top=10, times=1):
"""
:param title: 控件名称
:param control_type: 控件类型
:param button: left right middle 对应左、右、中三个键
:param times: 点击次数
:param found_index: 选择同名同类型多个控件中的第几个
:param left: X 正方向修正
:param top: Y 正方向修正
:return:
"""
if title == '' and control_type == '':
raise ValueError('控件名和控件类型不可为空!')
position = self.win.child_window(title=title, control_type=control_type, found_index=found_index).rectangle()
for i in range(times):
mouse.click(button=button, coords=(position.left + left, position.top + top))
if times > 1:
sleep(0.2)
3.4 获取信息列表
def get_users(self):
user_lis = []
try:
users = self.win.child_window(title="会话", control_type="List").children()
for user in users:
user_lis.append(user.window_text())
except:
print('获取好友列表失败')
pass
return user_lis
3.5 点击或搜索好友
def find_user(self, user=''):
if user== '':
raise ValueError('好友名称不可为空!')
if self.win.child_window(title=user, control_type='Text').exists(timeout=0.2):
self.click_one(title=user, control_type='Text')
else:
self.click_one(title='搜索', control_type='Edit', times=2)
sleep(0.5)
self.win.type_keys(user)
sleep(0.5)
self.win.type_keys('{ENTER}')
self.click_one(title='输入', control_type='Edit')
定位到好友有两种情况,第一种是存在于聊天界面中,第二种是不在聊天列表中,所以为了方便,先判断好友是否在当前聊天框中,如果没有,再搜索,毕竟搜索有些浪费时间
3.6 删除好友聊天框
def delete_user(self, user=''):
try:
self.click_one(title=user, control_type='Text', button='right')
self.app.PopupMenu['删除聊天'].click_input('left')
except:
return None
3.7 通过好友申请
from pyautogui import hotkey
from pywinauto import mouse
def add_new_amigo(self, user=''):
self.click_one(title='通讯录', control_type='Button')
self.click_one(title='新的朋友', control_type='ListItem')
hotkey('down')
hotkey('up')
address_list = self.win.child_window(title=user, control_type="Text").parent().parent().parent().children()[2]
cords = address_list.rectangle()
mouse.click(button='left', coords=(cords.left + 10, cords.top + 10))
hotkey('esc')
hotkey('esc')
self.click_one(title='通讯录', control_type='Button')
3.8 修改用户名
def fix_user(self, old_name='', new_name=''):
self.find_user(user=old_name)
remark = self.win.child_window(title="备 注", control_type="Text").parent().children()[1]
remark.type_keys(new_name)
3.9 发送文本信息
from pyperclip import copy
from pyautogui import hotkey
def send_message_to_user(self, user='', text=''):
self.find_user(user)
copy(text.replace('|', '\n').strip())
hotkey('ctrl', 'v')
hotkey('enter')
发送文本这里建议不要使用 pywinauto 自有的 type_keys() 方法,使用这一方法会无法保证缩进格式,我尝试过以 type_keys() 为前提的多种方式来保证缩进,但是效果都不太明显,所以说我采用了粘贴的方法,即代码中的 copy() 方法
3.10 发送图片
import win32clipboard as clip
from win32con import CF_DIB
from io import BytesIO
from PIL import Image
from pyautogui import hotkey
def send_image_to_user(self, user='', image_path=''):
self.find_user(user=user)
img = Image.open(image_path)
output = BytesIO()
img.convert("RGB").save(output, "BMP")
data = output.getvalue()[14:]
output.close()
clip.OpenClipboard()
clip.EmptyClipboard()
clip.SetClipboardData(CF_DIB, data)
clip.CloseClipboard()
sleep(0.5)
hotkey('ctrl', 'v')
hotkey('enter')
remove(image_path)
发送图片较为繁琐,如果是采用微信带的发送文件功能,那样速度就慢了很多,所以就将图片储存到剪切板中,再粘贴到聊天框中,以此加快速度。同样,发送文件也是这样的思路,不过发送文件需要先判断一下文件是否在100MB内,因为微信发送文件有大小限制。
3.11 处理转账
def payment_values(self):
if not self.win.child_window(title="微信转账", control_type="Text", found_index=0).exists(timeout=0.1):
if not self.win.child_window(title="微信转账", control_type="ListItem", found_index=0).exists(timeout=0.1):
return 0
remark = self.win.child_window(title="微信转账", control_type="Text", found_index=0).parent().parent().children()[0].children()[1].children()[0].window_text()
money = self.win.child_window(title="微信转账", control_type="Text", found_index=0).parent().parent().children()[0].children()[1].children()[1].window_text()
money = int(float(money.replace('¥', '')))
return {'remark': remark, 'money': money}
- 因为要获取转账备注,所以说定位方式看着很繁琐。如果不需要对方转账填写备注内容,则定位就很简单,只需要定位到 title = ‘确认收款’ 就可以。
3.12 确认收款及退回
from pyautogui import hotkey
def payment_receive(self):
for control_type in ['Text', 'ListItem']:
if self.win.child_window(title="微信转账", control_type=control_type, found_index=0).exists(timeout=0.1):
self.click_one(title='微信转账', control_type=control_type, times=2)
self.click_one(title='确认收钱', control_type='Button')
if self.win.child_window(title="已收钱", control_type="Text", found_index=0).exists(timeout=5):
hotkey('esc')
return 1
else:
return 0
else:
return 0
def payment_refuse(self):
for control_type in ['Text', 'ListItem']:
if self.win.child_window(title="微信转账", control_type=control_type, found_index=0).exists(timeout=0.1):
self.click_one(title='微信转账', control_type=control_type)
self.click_one(title='立即退还', control_type='Button')
self.click_one(title='退还', control_type='Button')
hotkey('esc')
return 1
else:
return 0
4. 总说明
上述代码块就是实现文章开头功能的相应代码,为了解释方便,我把代码拆分开来加上注释。当然,每个功能模块都有不太合适的地方,只是提供了一个较为方便的思路,对于控制微信,还有其他更为先进的方法,比如说很热门的 hook,这个就相对高深了,有兴趣的可以自己尝试了解一下。
|