需求1:家里有一个小店,需要订烟,中秋节前夕订烟日期调整,业务经理提前3天通知了,但是忘记了,然后就错过了,所以我要获取订烟时间,然后通过腾讯的sms,发送到几部手机上

需求2:某丝丽企业微信群需要每天活跃,每天大量的 “111” 刷屏,业务经理虽然在群里通知了,但是很快就会被刷屏,我想这个也是漏掉订烟的间接原因吧,,所以每天输入1 在群里进行活跃

实现:我先后尝试了抓包等等的方式,奈何js技术有限,未看懂参数的加密机制,所以还是进行最直接的UI操作,选型 pywinauto

过程中遇到的问题与总结:

1、winSpy (backend='win32') 定位不到工作台web的元素,使用 inspect 阔以

2、企业微信的工作台里面的内容,尝试了一下 winSpy 与 inspect 都定位不到菜单入口,所以使用坐标的方式点击

3、下面的代码中有调用某云平台的sms sdk

期望:希望大家有好的建议可以反馈一下,谢谢拉

# -*- coding: utf-8 -*-
# @Time : 2022/9/14 11:10
# @Author : CY
# @FileName: company_wx.py
import datetime
from pywinauto import mouse
import keyboard
from pywinauto.application import *
import time
from psutil import process_iter

from tencentcloud.common import credential
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tencentcloud.sms.v20210111 import sms_client, models


MSG_DATA = {
    'app_id': '',
    'sing_id': '',
    'tem_id': '',
    'msg_id': '',
    'msg_key': ''
}


def send_msg(phone, date_value, time_value):
    """发送短信"""
    try:
        china_num = '+86'
        phone = f'{china_num}{phone}'
        msg_data = MSG_DATA
        cred = credential.Credential(secret_id=msg_data.get('msg_id'), secret_key=msg_data.get('msg_key'))
        client = sms_client.SmsClient(cred, "ap-guangzhou")
        req = models.SendSmsRequest()

        # 短信应用ID: 短信SdkAppId在 [短信控制台] 应用管理--应用列表  https://console.cloud.tencent.com/smsv2/app-manage
        req.SmsSdkAppId = msg_data.get('app_id')
        # 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名,签名信息可登录 [短信控制台] 查看
        req.SignName = '我的是小程序的名字'
        req.PhoneNumberSet = [phone]
        # 模板 ID: 必须填写已审核通过的模板 ID。模板ID可登录 [短信控制台] 查看
        req.TemplateId = msg_data.get('tem_id')
        # 模板参数: 若无模板参数,则设置为空
        req.TemplateParamSet = [date_value, time_value]
        resp = client.SendSms(req)
        # 输出json格式的字符串回包
        # print(resp.to_json_string(indent=2))
        return True
    except TencentCloudSDKException as err:
        print(err)
        return False


def save_data(value, file_name, sx='pickle', read=False):
    """保存路径"""
    use_path = os.sep.join([get_cur_path(), f'{file_name}.{sx}'])
    if sx in ['pickle']:
        with open(use_path, 'wb') as file: pickle.dump(value, file)
    else: raise Exception('暂不支持的后缀名')
    if read: print("{}\n写入完成".format(value))


def get_save_data(file_name, sx='pickle'):
    """

    :param file_name: 文件名字
    :param sx: 后缀 json excel 类型

    :return:
    """
    use_path = os.sep.join([get_cur_path(), f'{file_name}.{sx}'])
    if not os.path.exists(use_path):
        save_data(value={}, file_name=file_name, sx=sx)
        return None
    if sx == 'pickle':
        with open(use_path, 'rb') as file_pickle:
            data_info = pickle.load(file_pickle)
        return data_info
    else:
        print('类型不支持')


def get_now(day=0, hours=0, minutes=0, time_type='%Y-%m-%d %H:%M:%S'):
    """
    当前日期 时间
    '%Y-%m-%d'
    '%Y-%m-%d %H:%M:%S.%f'
    """
    now_time = (datetime.datetime.now() + datetime.timedelta(days=+day, hours=+hours, minutes=+minutes)).strftime(time_type)
    return now_time


def get_cur_path():
    """获取当前path
    """
    cur_path = os.path.dirname(os.path.realpath(__file__))
    return cur_path


class WXWork(object):
    def __init__(self):
        exe_path = r"C:\Program Files (x86)\WXWork\WXWork.exe"
        self.app = Application(backend="uia").start(exe_path)
        self.pid = self.get_pid
        print(self.pid)
        if not self.pid:
            print("请登录企业微信!")
            return

        # 准备连接软件
        for i, item in enumerate(self.pid):
            print('正在连接软件...')
            try:
                self.app.connect(path=exe_path, process=item)
                self.weixin_window = self.app.window(class_name="WeWorkWindow")
                self.weixin_window.set_focus()
            except Exception as e:
                print('Error>>', e)
                continue
            else:
                print('连接软件成功!')
                break
        self.weixin_window.draw_outline(colour='red')  # 在窗口周围画一个轮廓。
        self.main_position = self.weixin_window.rectangle()

    @property
    def get_pid(self):
        pid = process_iter()
        pid_list = []
        for pid_temp in pid:
            pid_dic = pid_temp.as_dict(attrs=['pid', 'name'])
            if pid_dic['name'] == 'WXWork.exe':
                pid_num = pid_dic['pid']
                pid_list.append(pid_num)
        if len(pid_list):
            return pid_list
        else:
            return False

    def print_main_elements_tree(self):
        """打印元素 这个元素是树状图,很实用
        depth 打印等级 不填写 全部打印出来
        filename 给一个文件路径,树状图信息  保存到你给的文件中去
        """
        self.weixin_window.print_control_identifiers(depth=None, filename=None)

    @classmethod
    def get_element_position(cls, element):
        """
        获取位置
        return (left:66, top:61, right:1032, bottom:711)
        """
        return element.rectangle()

    def click_cd_news(self):
        """点击消息"""
        left_num = 28  # 这个值 是使用qq截图测量的左右边距
        top_num = 96
        self.click_cd_by_percent(left_num=left_num, top_num=top_num)

    def click_news_cd_first(self):
        """点击消息中的第一个"""
        left_num = 168
        top_num = 96
        self.click_cd_by_percent(left_num=left_num, top_num=top_num)

    def click_cd_workbench(self):
        """点击菜单中的工作台,因为获取不到菜单元素,所以使用相对坐标来解决点击问题"""
        left_num = 28
        top_num = 308
        self.click_cd_by_percent(left_num=left_num, top_num=top_num)

    def click_cd_by_percent(self, left_num, top_num, timeout=0.5, **kwargs):
        """根据元素位置点击"""
        if 'position' in kwargs.keys(): main_position = kwargs.get('position')
        else: main_position = self.weixin_window.rectangle()

        min_high = main_position.top
        min_wide = main_position.left
        left = int(min_wide+left_num)
        top = int(min_high+top_num)
        mouse.click(button='left', coords=(left, top))
        time.sleep(timeout)

    def click_table_cd(self):
        """点击工作台中的菜单"""
        left_num = 185
        top_num = 308
        self.click_cd_by_percent(left_num=left_num, top_num=top_num, timeout=5)

    def click_table_windows_close(self):
        left_num = 400
        top_num = 24
        self.click_cd_by_percent(left_num=left_num, top_num=top_num)

    def click_news_input(self):
        left_num = 512
        top_num = 580
        self.click_cd_by_percent(left_num=left_num, top_num=top_num)

    def get_order_date(self):
        """获取时间"""
        if self.assert_dialog_exists(): return None
        else:
            return self.weixin_window.child_window(title="企业微信-工作台", class_name="WXworkWindow - 企业微信-工作台").child_window(
                best_match='订货时间Static').parent().children()[4].window_text()

    def assert_dialog_exists(self):
        return self.weixin_window.child_window(best_match='TitleBar').exists()

    @classmethod
    def send_operation_keyboard_text(cls, input_str):
        """键盘输入内容"""
        keyboard.write(input_str)
        time.sleep(0.5)

    @classmethod
    def send_operation_keyboard_exe(cls, input_str):
        """键盘输入执行命令"""
        keyboard.send(input_str)
        time.sleep(0.5)

    def send_news_everyday_active(self):
        """微信群每天活跃发送信息1"""
        self.click_cd_news()
        self.click_news_cd_first()
        self.click_news_input()
        self.send_operation_keyboard_text('11')
        self.send_operation_keyboard_exe('enter')


def get_order_time():
    file_name = 'data'
    cwx = WXWork()
    cwx.send_news_everyday_active()
    # cwx.print_main_elements_tree()
    cwx.click_table_windows_close()
    cwx.click_cd_workbench()
    print(cwx.weixin_window.window_text())
    cwx.click_table_cd()
    order_data = cwx.get_order_date()
    if order_data:
        print(order_data)
        format_text_list = order_data.replace('月', '-').split('~')
        time_text_list = []
        date_text = format_text_list[0].split('日')[0]
        for time_data in format_text_list:
            time_text_list.append(time_data.split('日')[-1])
        time_text = '~'.join(time_text_list)
        data_dict = {
            'assert_data': date_text,
            'date': date_text,
            'time': time_text
        }
        save_data(data_dict, file_name=file_name)

    cwx.click_table_windows_close()
    cwx.click_cd_news()
    cwx.send_operation_keyboard_exe('alt+tab')

    get_now(time_type='%m-%d')
    get_order_data = get_save_data(file_name)
    if get_now(time_type='%m-%d') == get_order_data.get('assert_data'):
        print('发送短信')
        send_phone_list = ['手机号1', '手机号2']
        for phone_num in send_phone_list:
            send_msg(phone=phone_num, date_value=get_order_data.get('date'), time_value=get_order_data.get('time'))
    else: print('不发送短信')


if __name__ == '__main__':
    get_order_time()