用python的pywinauto组件控制微信Windows版

使用pywinauto组件可以比较容易的操纵微信Windows版进行信息发送和接受

 

前提如下

1、已经安装有关python组件。

2、微信已经打开和登录,下面的代码不负责登录操作。

3、微信版本3.9.2.23

具体实现有两个类

1、帮助类Helper

import datetime


class Helper:
    """
    帮助类
    """

    @staticmethod
    def print(msg):
        """
        打印和记录消息
        :param msg:
        :return:
        """
        time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        print(f'{msg} 当前时间:{time}')

    @staticmethod
    def get_time():
        """
        获取时间字符串%Y-%m-%d %H:%M:%S
        :return:
        """
        return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")


class AutoMsg:
    """
    定义自动化操作的结果类
    """

    def __init__(self):
        self.result = 0
        self.message = ""
        self.errcode = ""

    def get_result(self):
        """
        获取结果信息 1 成功 -1失败 0未知
        :return:
        """
        return self.result

    def get_errcode(self):
        """
        获取错误的编码
        :return:
        """
        return self.errcode


class UserNotFoundError(Exception):
    """
    微信用户没有找到异常
    """
    pass


class AppNotFoundError(Exception):
    """
    没有找到应用程序
    """
    pass


class EleNotFoundError(Exception):
    """
    没有找到应用程序
    """
    pass

 

2、微信操控类Wechat

import time
import pyautogui
from pywinauto.findwindows import ElementNotFoundError

from helper import *
from pywinauto.application import Application, ProcessNotFoundError


class Wechat:
    """
    微信的自动化处理
    """

    def __init__(self, path):
        """
        创建微信自动化的实例
        :param path:
        :return:
        """
        self.__path = path
        # 微信的主窗口对象
        self.__main_win = None
        # 微信输入框的对象
        self.__input_msg_box = None
        # 查询输入框对象
        self.__search_box = None
        # 微信默认显示的会话列表框
        self.__dia_list = None
        # 输入框的标题的button
        self.__input_title_btn = None
        # 右侧面板
        self.__right_panel = None

    @staticmethod
    def __get_main_win(path):
        """
        获取微信的主窗口
        :param path:传入微信的安装路径
        :return:
        """
        # 获取进程ID
        try:
            app = Application(backend='uia').connect(path=path)
        except ProcessNotFoundError:
            raise AppNotFoundError("微信程序还没有打开")
        main_win = app.window(class_name='WeChatMainWndForPC')
        # if not app.top_window():
        Helper.print(f'     获取主程序 pid为{app.process}')
        return main_win

    def __is_latest_user(self, username):
        """
        是否为最近聊天的用户
        :param username:微信聊天的用户
        :return:
        """
        result = False
        if self.__dia_list and self.__input_title_btn:
            # 检查会话框是不是选中当前用户
            item_list = self.__dia_list.get_items()
            for item in item_list:
                if item.is_selected() and item.element_info.name == username:
                    # 会话选中当前用户
                    result = True
                    break
            if result:
                # 如果输入框对象存在并且等于当前用户
                result = self.__input_title_btn.window_text().strip() == username
        if result:
            Helper.print("     ~~~~对话用户没变~~~~")
        return result

    def __auto_by_search(self, parent_ele, username):
        """
        先选择搜索框,再进行发送信息
        :param parent_ele:父窗口的对象
        :param username:发送的用户
        :return:
        """
        # 搜索框的名称
        Helper.print('      ===通过搜索查找用户===')
        # select_item = self.__search_box = select_item
        # selectItem.draw_outline(colour='red')
        self.__search_box.click_input()
        pyautogui.hotkey('ctrl', 'a')
        pyautogui.hotkey('delete')
        Helper.print('      输入筛选词语')
        self.__search_box.type_keys(username, with_spaces=False)
        Helper.print("      等待2秒出筛选结果")
        # 等待筛选结果出来
        time.sleep(2)
        # 选择下来项目
        Helper.print('      选择第一个搜索出来的用户')
        # 通过列表来查询
        select_item = None
        target_ele = parent_ele.children()[1].children()[0]
        items = target_ele.get_items()
        for item in items:
            if item.element_info.name == username:
                select_item = item
                break
        Helper.print("      ------从搜索列表中找到用户------")
        if select_item:
            Helper.print('      用户列表查找完毕,点击控件')
            select_item.click_input()
        else:
            raise UserNotFoundError(f"没有找到名称为\"{username}\"的用户")

    def __auto_by_list(self, username):
        """
        直接寻找用户的控件
        :param username:发送的用户
        :return:
        """
        if not self.__dia_list:
            self.__dia_list = self.__main_win.child_window(title="会话", control_type="List").wrapper_object()
        dia_list = self.__dia_list
        Helper.print(f"      查找\"{username}\"的控件")
        select_item = None
        for item in dia_list.get_items():
            if item.element_info.name == username:
                select_item = item
                break
        if select_item and select_item.is_visible():
            # 如果已经找到下拉控件
            select_item.click_input()
        else:
            # 根据会话框找到上级窗口(参照Inspect)
            parent_ele = dia_list.parent().parent().parent().parent()
            self.__auto_by_search(parent_ele, username)

    def __init_elements(self, username):
        """
        获取用户控件,初始化各个参数
        :param username:
        :return:
        """
        if not self.__path:
            raise Exception("微信exe路径不能为空!")
        if not self.__main_win:
            self.__main_win = Wechat.__get_main_win(self.__path)
        try:
            self.__main_win.set_focus()
        except ElementNotFoundError:
            raise EleNotFoundError("核心窗口对象没有找到,请检测微信是否已经登录")
        if not self.__search_box:
            self.__search_box = self.__main_win.child_window(title='搜索', control_type="Edit").wrapper_object()
        # 获取Edit控件的值,判断是否为空
        if len(self.__search_box.get_value().strip()) > 0:
            # 如果正在搜索中,删除按钮会出现
            del_btn = self.__search_box.parent().children()[-1]
            del_btn.click_input()
        # ----1、尝试选择下拉控件----
        if not self.__is_latest_user(username):
            self.__auto_by_list(username)
            # 等待右边的输入框的显示
            time.sleep(1)
        if not self.__input_msg_box:
            self.__input_msg_box = self.__main_win.child_window(title="输入", control_type="Edit").wrapper_object()
        if not self.__right_panel:
            # 根据元素位置来进行编程(参照inspect)
            self.__right_panel = self.__input_msg_box.parent().parent().parent().parent().parent().parent()
        # ----2、如果已经获取到输入框,则检查一下信息框是否存在----
        if not self.__input_title_btn:
            # Helper.print("      input_title_btn为空,查找一下控件")
            # 如果还没有找到这个输入框的标题控件
            item_list = self.__right_panel.children()
            if len(item_list) > 0:
                # 从右侧面板的第一个控件(右侧顶部面板)开始查找(参照inspect)
                item_list = item_list[0].descendants(title=username, control_type="Button")
                if len(item_list) == 0:
                    raise Exception("没有找到当前用户的对话框!")
                self.__input_title_btn = item_list[0]
        # Helper.print("      input_title_btn element_info.name is " + self.__input_title_btn.element_info.name)
        if self.__input_title_btn.element_info.name != username:
            # 如果输入框的标题不是当前用户
            raise Exception("没有找到当前用户的对话框!")
        # Helper.print(f"      \"{username}\"对话框已经找到,查找用户正确!")

    def __send_message(self, username, send_msg):
        """
        向特定的用户发送消息
        :param username: 对方微信的用户名称
        :param send_msg: 发送的消息
        :return:
        """
        # 打开微信的快捷键
        Helper.print("--开始发送信息--")
        self.__init_elements(username)
        # ----3、到了用户对话框,才开始输入对话信息----
        self.__input_msg_box.click_input()
        self.__input_msg_box.type_keys(send_msg, with_spaces=True)
        # 回车发送
        pyautogui.hotkey('enter')
        Helper.print("--结束发送信息--")

    def __get_message(self, username, other_side=False):
        """
        获取最后的会话信息
        :param username: 微信用户名称
        :param other_side: 只读取对方的信息,只适合双人会话
        :return:
        """
        message = None
        Helper.print("--开始查找信息--")
        self.__init_elements(username)
        dia_list = self.__right_panel.descendants(title="消息", control_type="List")
        if len(dia_list) > 0:
            # 获取List中最后一个控件
            last_item = dia_list[0].get_item(-1)
            message = last_item.element_info.name
            if other_side:
                # 检测是否为对方的输入
                btn_list = last_item.descendants(title=username, control_type="Button")
                if len(btn_list) == 0:
                    # 如果不是对方的输入信息
                    message = None
        Helper.print("--结束查找信息--")
        return message

    @staticmethod
    def __wrap_errcode(exception):
        """
        判断异常的类型,并设置错误代码
        :param exception:
        :return:
        """
        result = ""
        if isinstance(exception, UserNotFoundError):
            result = "3-01用户不存在"
        elif isinstance(exception, EleNotFoundError):
            result = "2-01控件不存在"
        elif isinstance(exception, AppNotFoundError):
            result = "1-01程序没启动"
        return result

    def get_last_msg(self, username, other_side=False):
        """
        获取最后的会话
        :param username: 微信用户名称
        :param other_side: 只读取对方的信息,只适合双人会话,适合自动回复场景
        :return:
        """
        auto_msg = AutoMsg()
        try:
            message = self.__get_message(username, other_side)
            auto_msg.result = 1
            auto_msg.message = message
        except Exception as e:
            auto_msg.result = -1
            auto_msg.message = str(e)
            auto_msg.errcode = Wechat.__wrap_errcode(e)
        if other_side and auto_msg.message is None:
            # 如果要读取对方的信息,并且读不到。
            auto_msg.result = -1
        return auto_msg

    def send_msg(self, username, send_msg):
        """
        向特定的用户发送消息
        :param username: 对方微信的用户名称
        :param send_msg: 发送的消息
        :return:
        """
        auto_msg = AutoMsg()
        try:
            self.__send_message(username, send_msg)
        except Exception as e:
            auto_msg.result = -1
            auto_msg.message = str(e)
            auto_msg.errcode = Wechat.__wrap_errcode(e)
        if auto_msg.result == 0:
            auto_msg.result = 1
        return auto_msg

 

测试代码如下:

调试代码时,对照着inspect.exe比较方便。

from wechat import *
import random

if __name__ == '__main__':
    path = "D:\\WeChat\\WeChat.exe"
    # 消息内容
    num = random.randint(10000, 40000)
    send_msg = f"2023“网聚职工正能量 争做中国好网民”活动启动.{num}"
    # pyautogui.hotkey('ctrl', 'alt', 'w')
    chat = Wechat(path)
    user_name = '文件传输助手'
    info = chat.send_msg(user_name, send_msg)
    if info.result == -1:
        print(f"错误代码是:{info.errcode},错误信息是:{info.message}")

 

欢迎沟通交流  

 

posted @ 2023-04-05 22:29  夫贝贝  阅读(2614)  评论(0编辑  收藏  举报