WX自动化

pip install uiautomation
pip install dataset
import logging
import random
import re
import sys
import time

import dataset
import uiautomation as auto
from uiautomation import WindowControl

# 这里要构建一个全局锁,因为同一时刻内,桌面只能被一个线程锁操纵。防止第一个操作未完成就开始了第二个动作,造成混乱。
LOCK = 0


class WxChat:
    """
    微信控制类
    """

    def __init__(self):
        """
        初始化窗口
        """
        logging.basicConfig(level=logging.DEBUG)

        super().__init__()
        auto.uiautomation.DEBUG_SEARCH_TIME = True
        auto.uiautomation.SetGlobalSearchTimeout(2)  # 设置全局搜索超时时间
        # 微信窗口句柄
        self.wxWindow = auto.WindowControl(searchDepth=1, ClassName='WeChatMainWndForPC',
                                           Name='微信', desc='微信窗口')
        if not self.wxWindow.Exists(0, 0):
            self.wxWindow = auto.WindowControl(
                searchDepth=1, ClassName='WeChatMainWndForPC',
                Name='微信', desc='微信窗口')

        if not self.wxWindow.Exists(0, 0):
            logging.error("微信窗口未激活!,请去登录微信,并将窗口打开")
            sys.exit()

        self.wxWindow.SetActive()
        logging.info("激活窗口")
        self.wxWindow.SetTopmost(True)
        logging.info("设置为顶层")

        self.SessionList = self.wxWindow.ListControl(Name='会话')
        self.EditMsg = self.wxWindow.EditControl(Name='输入')
        self.SearchBox = self.wxWindow.EditControl(Name='搜索')
        self.msg_list = self.wxWindow.ListControl(Name='消息')

    def send_message(self, full_name, message, chat_type='联系人') -> int:
        """
        搜索好友或群聊->打开聊天窗口->发送消息
        :param full_name: 好友全名(原微信昵称或者备注昵称都可以,但是最好要唯一,保证目标在搜索结果的第一位)
        :param message: 消息
        :param chat_type: 联系人、群聊
        :return: 1发送成功 2发送失败 3被删除好友 4已被拉黑
        """
        # 点击搜索框
        self.wxWindow.EditControl(Name="搜索").Click(waitTime=0.5)
        # 清空输入框
        self.wxWindow.ButtonControl(Name="清空", desc='点击清空输入').Click(waitTime=0.5)
        # 清空后会失去输入焦点,这里再次点击搜索框
        self.wxWindow.EditControl(Name="搜索").Click(waitTime=0.5)  # 点击搜索框
        self.wxWindow.SendKeys(full_name)  # 输入内容
        # 定位到联系人标签
        text_control = self.wxWindow.TextControl(Name=chat_type)
        rect = text_control.BoundingRectangle
        # 计算出稍微下面一点的坐标
        x = rect.left + (rect.right - rect.left) / 2
        y = rect.bottom + 10  # 20是一个例子,你可以根据实际情况调整
        # 使用 Click() 方法点击新的坐标位置,即:搜索到的第一个好友的位置
        auto.Click(x=int(x), y=y, waitTime=1)
        # 发送消息
        self.wxWindow.SendKeys(message, waitTime=1)  # 输入内容
        # self.wxWindow.ButtonControl(Name="发送(S)").Click(waitTime=get_random_second())  # 点击发送
        auto.PressKey(auto.Keys.VK_ENTER, waitTime=0.4)
        # 这里要监听一下,看看是不是已经被拉黑或者删除 TODO

        # 界面恢复到原始状态
        for i in range(5):
            self.wxWindow.ButtonControl(Name="聊天").Click(waitTime=WxChat.get_random_second())
        return 1

    def add_friend_from_group_chat(self, group_full_name: str, serial_numbers: list[int],
                                   greeting: str = "我通过了你的朋友验证请求,现在我们可以开始聊天了"):
        """
        群聊添加好友
        :param group_full_name: 群聊名称
        :param serial_numbers: 要添加的好友序号
        :param greeting: 问候语
        :return:
        """

        self.wxWindow.EditControl(Name="搜索").Click(waitTime=WxChat.get_random_second())  # 点击搜索框
        # 清空输入框
        clear_button = self.wxWindow.ButtonControl(Name="清空", desc='点击清空输入')
        if clear_button.Exists(0, 0):
            clear_button.Click(waitTime=WxChat.get_random_second())
        else:
            self.wxWindow.ButtonControl(Name="清空", desc='点击清空输入').Click(waitTime=WxChat.get_random_second())
        # 清空后会失去输入焦点,这里再次点击搜索框
        self.wxWindow.EditControl(Name="搜索").Click(waitTime=WxChat.get_random_second())  # 点击搜索框
        self.wxWindow.SendKeys(group_full_name)  # 输入内容
        time.sleep(0.5)
        # 定位到联系人标签
        text_control = self.wxWindow.TextControl(Name="群聊")
        rect = text_control.BoundingRectangle
        # 计算出稍微下面一点的坐标
        x = rect.left + (rect.right - rect.left) / 2
        y = rect.bottom + 10  # 20是一个例子,你可以根据实际情况调整
        # 使用 Click() 方法点击新的坐标位置,即:搜索到的第一个好友的位置
        auto.Click(x=int(x), y=y)
        # 打开群聊详情
        self.wxWindow.ButtonControl(Name="聊天信息", desc='点击...打开群聊详情').Click(waitTime=0.5)
        # self.wxWindow.ButtonControl(Name="查看更多", desc='展开群成员列表').Click(waitTime=0.5)
        # 定位到搜索框
        edit_control = self.wxWindow.EditControl(Name="搜索群成员", desc='定位到成员搜索框')
        rect = edit_control.BoundingRectangle
        # 计算出群主的坐标位置,成员格子中每个格子大小是34*34,每个格子横向间隔54,竖向间隔69
        x_leader = rect.left - 20 + 17
        y_leader = rect.bottom + 49
        # auto.Click(x=int(x_leader), y=y_leader)
        # 获取群成员数量
        control = self.wxWindow.TextControl(RegexName=f'^{group_full_name} \\(.*\\)', desc='点击清空输入')
        # control.Click(waitTime=0.01)
        findall = re.findall(r'[(](.*?)[)]', control.Name)
        group_number = int(findall[0])
        logging.info(f"群成员数量({group_full_name}):{group_number}")
        # 边上滑动条的位置点位
        side_position_x, side_position_y = x_leader + 3 * 54 + 40, y_leader
        # 初始化位置类
        self.groupGrid = GroupGrid(group_number=group_number, x_leader=x_leader, y_leader=y_leader)
        # 添加好友
        for serial_number in serial_numbers:
            # 点击几次输入框确保收缩详情
            for i in range(5):
                self.wxWindow.EditControl(Name=group_full_name, LocalizedControlType="编辑", desc='点击输入框').Click(
                    waitTime=0.01)

            self.wxWindow.ButtonControl(Name="聊天信息", desc='点击...打开群聊详情').Click(waitTime=0.5)
            self.wxWindow.ButtonControl(Name="查看更多", desc='展开群成员列表').Click(waitTime=0.5)
            time.sleep(2)
            b, p = self.groupGrid.get_position(serial_number)  # 计算出下滑次数和点击的位置坐标
            print(f"下滑次数{b}")
            # 下滑b次
            for i in range(b):
                time.sleep(1)
                auto.DragDrop(side_position_x, side_position_y, side_position_x, side_position_y + 45, moveSpeed=1)
                side_position_y += 45
                print(f"下滑{i + 1}次")
            # 点击位置
            auto.Click(x=p[0], y=p[1])
            # 添加好友
            button_control = self.wxWindow.ButtonControl(Name="添加到通讯录", desc='点击添加到通讯录')
            # 如果有这个按钮
            if not button_control.Exists(0, 0):
                print("未发现按钮:添加到通讯录")
                continue
            button_control.Click(waitTime=1)
            dialog: WindowControl = auto.WindowControl(Name='添加朋友请求', desc='添加朋友请求')
            if not dialog.Exists(0, 0):
                print("未检测到对话框")
                continue
            # 写入好友申请语:第一个编辑框
            edit_control = dialog.EditControl()
            # 点击聚焦
            edit_control.Click(waitTime=0.1)
            # Ctrl+A 全选
            edit_control.SendKeys('{Ctrl}a', waitTime=WxChat.get_random_second())
            # 输入问候语
            edit_control.SendKeys(greeting)
            # 确定
            dialog.ButtonControl(Name="确定").Click(waitTime=WxChat.get_random_second())
        # 界面恢复到原始状态
        for i in range(5):
            self.wxWindow.ButtonControl(Name="聊天").Click(waitTime=WxChat.get_random_second())

    def message_listening(self, chat_type, full_name):
        """
        监听指定聊天:原理是定时打开聊天框进行判断最后一条消息是不是对方发的。
        :param chat_type: 联系人、群聊
        :param full_name: 好友全名(原微信昵称或者备注昵称都可以,但是最好要唯一,保证目标在搜索结果的第一位)
        :return:
        """
        pass

    def friend_likes(self, limit: int):
        """
        朋友圈点赞
        :param limit: 点赞最新多少条。
        :return:
        """
        pass

    def send_material_to_chat(self, names: list[str], material_path_pic: str, material_content: str):
        """
        发送素材到指定人、群聊
        :param names: (好友/群聊)名称(全名且不可重复)
        :param material_path_pic: 图片地址
        :param material_content: 文字内容
        :return:
        """
        pass

    def add_friend_by_account(self, account):
        """
        根据账号添加好友,
        :param account: 账号
        :return:
        """
        self.wxWindow.ButtonControl(Name="通讯录", desc='点击通讯录').Click(waitTime=0.5)
        self.wxWindow.ButtonControl(Name="添加朋友", desc='点击添加朋友').Click(waitTime=0.5)
        edit_control = self.wxWindow.EditControl(Name="微信号/手机号", desc='点击输入框')
        edit_control.Click(waitTime=0.5)
        edit_control.SendKeys(account)
        self.wxWindow.TextControl(Name=f"搜索:{account}", desc='搜索好友').Click(waitTime=0.5)
        # 回到原始界面
        # self.wxWindow.ButtonControl(Name="聊天", desc='点击通讯录').Click(waitTime=0.5)

    def restore_face(self):
        """
        此方法已弃用
        :return:
        """
        # 界面恢复到聊天界面(可能不成功!!!)
        for i in range(5):
            self.wxWindow.ButtonControl(Name="聊天").Click(waitTime=WxChat.get_random_second())

    @staticmethod
    def get_random_second():
        return random.randrange(10, 100, 1) / 100

    @staticmethod
    def restore_minimize():
        logging.basicConfig(level=logging.INFO)
        """
        将界面恢复到最小化状态
        :return: 
        """
        for i in range(5):
            logging.info(f"最小化第{i + 1}次")
            auto.PressKey(auto.Keys.VK_ESCAPE, waitTime=0.1)

    @staticmethod
    def outbound_chat():
        logging.basicConfig(level=logging.INFO)
        """
        将微信从最小化呼出
        :return:
        """
        wxWindow = None
        for i in range(10):
            logging.info(f"尝试呼出第{i + 1}次")
            show_hide_icon = auto.ButtonControl(Name="显示隐藏的图标", desc='显示隐藏的图标')
            if show_hide_icon.Exists(0, 0):
                show_hide_icon.Click(waitTime=0.5)
            wx_icon = auto.ButtonControl(Name="微信", desc='将微信从最小化呼出')
            if wx_icon.Exists(0, 0):
                wx_icon.Click(waitTime=0.5)
            wxWindow = auto.WindowControl(searchDepth=1, ClassName='WeChatMainWndForPC',
                                          Name='微信', desc='微信窗口')
            if wxWindow.Exists(0, 0):
                logging.info("呼出成功")
                break
        if wxWindow is None or not wxWindow.Exists(0, 0):
            logging.info("呼出失败")


class GroupGrid:
    """
    群好友的网格排列
    """

    def __init__(self, group_number, x_leader, y_leader):
        """
        初始化
        :param group_number: 好友数量
        :param x_leader: 群主位置X
        :param y_leader: 群主位置Y
        """
        self.group_number = group_number  # 群成员数量
        self.group_row_number, self.group_row_number_remainder = divmod(group_number, 4)  # 群行数和余数
        self.positions = [
            (x_leader, y_leader),
            (x_leader + 1 * 54, y_leader),
            (x_leader + 2 * 54, y_leader),
            (x_leader + 3 * 54, y_leader)
        ]

    def get_position(self, number):
        """
        获取第number个群成员的位置和下滑次数
        :param number:
        :return:
        """
        b, y = divmod(number, 4)
        if y == 0:
            y = 4
            b = b - 1
        # 返回下滑的次数和位置坐标
        return b, self.positions[y - 1]

if __name__ == '__main__':
    WxChat.outbound_chat()

    # 向好友发送消息
    wx_chat = WxChat()

    # 发送消息
    wx_chat.send_message(full_name="Mongose", message="您好")

    # 向群聊添加好友
    wx_chat.add_friend_from_group_chat(group_full_name="浙江活动小分队", serial_numbers=[19])
    
    # 搜索微信号或手机号并添加好友
    wx_chat.add_friend_by_account('sunziwen3366')

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

文子阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值