Skip to content

🤖 Robotic process automation (RPA) for WeChat, WeCom, etc. Support multiple clients and keep the computer unlocked when disconnect from the remote desktop.

License

Notifications You must be signed in to change notification settings

yihleego/robotic-process-automation

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Robotic Process Automation

机器人流程自动化 (RPA),像人类工作者一样,通过跨各种应用程序运行的软件或硬件系统实现任务自动化。软件或机器人可以学习具有多个步骤和应用程序的工作流程,例如获取收到的表格、发送回执消息、检查表格的完整性、将表格归档到文件夹中以及使用表格名称更新电子表格,提交日期等。RPA软件旨在减轻员工完成重复性简单任务的负担。

Usage

客户端自动化使用介绍

请确保本地计算机已安装对应客户端,且rpa-clientrpa-server均已启动。

当前系统支持以下客户端:

AppId Name
wechat 微信
wecom 企业微信
qq 腾讯QQ
tim TIM
dingtalk 钉钉
lark 飞书

由于任务必须通过指定用户去执行,所以在执行任务之前,需要确保用户存在。

1. 创建用户

通过HTTP接口创建用户
  • 接口:http://<host>:<port>/users
  • 方法:POST
  • 格式:JSON
  • 参数:Body
Property Type Required Description
users User[] 必填 用户对象数组。
└ id String 可选 用户ID。为空时服务端会自动生成ID。
└ appId String 必填 关联AppID。
└ account String 必填 用户账号,用于匹配客户端。
└ nickname String 可选 用户昵称,用于展示。
└ realname String 可选 用户真实名称,用于展示。
└ company String 可选 用户所属公司名称,用于展示。

注意,企业微信客户端中无法直接获取当前登录人的account,当前是通过${realname}_${company}组合匹配客户端。

接口示例:

curl -X POST --location "http://localhost:8080/users" \
    -H "Content-Type: application/json" \
    -d "{
          \"users\": [
            {
              \"id\": \"uid\",
              \"appId\": \"wechat\",
              \"account\": \"account\",
              \"nickname\": \"nickname\"
            }
          ]
        }"
通过SQL脚本创建用户
insert into user (id, app_id, account, nickname, realname, company, status, created_time, updated_time)
values ('uid', 'wechat', 'account', 'nickname', 'realname', 'company', 1, now(), null);

2. 创建任务

通过HTTP接口创建任务
  • 接口:http://<host>:<port>/tasks
  • 方法:POST
  • 格式:JSON
  • 参数:Body
Property Type Required Description
tasks Task[] 必填 任务对象数组。
└ id String 可选 任务ID。为空时服务端会自动生成ID。
└ userId String 必填 关联用户ID。
└ type String 必填 任务类型,参考任务类型字典表
└ priority Integer 可选 任务优先级,值越小越优先。为空时取默认配置的优先级。
└ data String 可选 任务数据,格式为JSON字符串。
└ scheduleTime DateTime 可选 任务执行时间,例如:2022-01-01 10:00:00。为空时会被立即执行。

示例:

curl -X PATCH --location "http://localhost:8080/tasks" \
    -H "Content-Type: application/json" \
    -d "{
          \"tasks\": [
            {
              \"id\": \"tid\",
              \"userId\": \"uid\",
              \"type\": \"login\",
              \"priority\": \"0\",
              \"data\": \"\",
              \"scheduleTime\": \"2022-01-01 10:00:00\",
            }
          ]
        }"
通过SQL脚本创建任务
insert into task (id, user_id, app_id, type, priority, data, status, created_time, updated_time, schedule_time)
values ('tid', 'uid', 'wechat', 'login', 0, null, 0, now(), null, '2022-01-01 10:00:00');

微信客户端自动化

服务端提供了一个运行时的测试页面,开发者可以用它在本地测试简单任务。 打开浏览器访问http://<host>:<port>/index.html,然后选择待测试的客户端即可。

微信客户端登录任务

登录客户端不需要参数

task_wechat_login

微信客户端登出任务

登出客户端不需要参数

task_wechat_logout

微信客户端发送个人消息任务

参数格式:

Property Type Required Description
target String 必填 发送对象。
messages Message[] 必填 消息对象数组。
└ type String 必填 消息类型。
└ content String 必填 消息内容,文本内容或文件地址。

消息类型:

Code Name Description
text TEXT 文本
image IMAGE 图片
video VIDEO 视频
file FILE 文件

参数示例:

{
  "target": "friend",
  "messages": [
    {
      "type": "text",
      "content": "message"
    },
    {
      "type": "image",
      "content": "https://rpa.leego.io/image.png"
    },
    {
      "type": "video",
      "content": "https://rpa.leego.io/video.mp4"
    },
    {
      "type": "file",
      "content": "https://rpa.leego.io/file.zip"
    }
  ]
}

task_wechat_send_private_messages

微信客户端发送群消息任务

参数格式:

Property Type Required Description
target String 必填 群名称。
messages Message[] 必填 消息对象数组。
└ type String 必填 消息类型。
└ content String 必填 消息内容,文本内容或文件地址。

消息类型:

Code Name Description
text TEXT 文本
image IMAGE 图片
video VIDEO 视频
file FILE 文件
mention MENTION 提醒

参数示例:

{
  "target": "group",
  "messages": [
    {
      "type": "text",
      "content": "message"
    },
    {
      "type": "image",
      "content": "https://rpa.leego.io/image.png"
    },
    {
      "type": "video",
      "content": "https://rpa.leego.io/video.mp4"
    },
    {
      "type": "file",
      "content": "https://rpa.leego.io/file.zip"
    },
    {
      "type": "mention",
      "content": "member"
    }
  ]
}

task_wechat_send_group_messages

企业微信客户端自动化

企业微信客户端登录任务

登录客户端不需要参数

task_wecom_login

企业微信客户端登出任务

登出客户端不需要参数

task_wecom_logout

企业微信客户端发送个人消息任务

参数格式:

Property Type Required Description
target String 必填 发送对象。
messages Message[] 必填 消息对象数组。
└ type String 必填 消息类型。
└ content String 必填 消息内容,文本内容或文件地址。

消息类型:

Code Name Description
text TEXT 文本
image IMAGE 图片
video VIDEO 视频
file FILE 文件

参数示例:

{
  "target": "friend",
  "messages": [
    {
      "type": "text",
      "content": "message"
    },
    {
      "type": "image",
      "content": "https://rpa.leego.io/image.png"
    },
    {
      "type": "video",
      "content": "https://rpa.leego.io/video.mp4"
    },
    {
      "type": "file",
      "content": "https://rpa.leego.io/file.zip"
    }
  ]
}

task_wecom_send_private_messages

企业微信客户端发送群消息任务

参数格式:

Property Type Required Description
target String 必填 群名称。
messages Message[] 必填 消息对象数组。
└ type String 必填 消息类型。
└ content String 必填 消息内容,文本内容或文件地址。

消息类型:

Code Name Description
text TEXT 文本
image IMAGE 图片
video VIDEO 视频
file FILE 文件
mention MENTION 提醒

参数示例:

{
  "target": "group",
  "messages": [
    {
      "type": "text",
      "content": "message"
    },
    {
      "type": "image",
      "content": "https://rpa.leego.io/image.png"
    },
    {
      "type": "video",
      "content": "https://rpa.leego.io/video.mp4"
    },
    {
      "type": "file",
      "content": "https://rpa.leego.io/file.zip"
    },
    {
      "type": "mention",
      "content": "member"
    }
  ]
}

task_wecom_send_group_messages

微信客户端添加联系人任务

参数格式:

Property Type Required Description
target String 必填 群名称。
contacts Contact[] 必填 联系人对象数组。
└ target String 必填 用户手机号码或邮箱。
└ reason String 可选 添加联系人备注。

参数示例:

{
  "contacts": [
    {"target": "phone"},
    {"target": "email", "reason": "reason"}
  ]
}

过于先进,不宜展示。

Documentation

通过关闭互斥体实现多客户端运行

为了防止客户端被多次启动,开发者们通常会使用创建互斥体(Mutex)对象的方式进行实现。互斥锁是一种用于多线程编程中,保护共享资源不被多个线程或进程同时访问的机制。

Windows 中创建或打开互斥对象

CreateMutexA function (synchapi.h)
HANDLE CreateMutexA(
  [in, optional] LPSECURITY_ATTRIBUTES lpMutexAttributes,
  [in]           BOOL                  bInitialOwner,
  [in, optional] LPCSTR                lpName
);
参数

[in, optional] lpMutexAttributes

指向 SECURITY_ATTRIBUTES 结构的指针。如果此参数为NULL,则句柄不能被子进程继承。

结构的lpSecurityDescriptor成员指定新互斥体的安全描述符。如果lpMutexAttributesNULL,则互斥锁将获得默认的安全描述符。互斥锁的默认安全描述符中的ACL来自创建者的主要或模拟令牌。

[in] bInitialOwner

如果此值为TRUE并且调用者创建了互斥锁,则调用线程将获得互斥锁对象的初始所有权。否则,调用线程不会获得互斥锁的所有权。要确定调用者是否创建了互斥锁,请参阅返回值部分。

[in, optional] lpName

互斥对象的名称。该名称仅限于MAX_PATH个字符。名称比较区分大小写。

如果lpName与现有的命名互斥对象的名称匹配,则此函数请求MUTEX_ALL_ACCESS访问权限。在这种情况下,将忽略bInitialOwner参数,因为它已由创建过程设置。如果lpMutexAttributes参数不为NULL,它决定句柄是否可以被继承,但它的安全描述符成员被忽略。

如果lpNameNULL,则创建互斥对象时没有名称。

如果lpName与现有事件、信号量、可等待计时器、作业或文件映射对象的名称匹配,则该函数将失败并且 GetLastError 函数返回ERROR_INVALID_HANDLE。这是因为这些对象共享相同的命名空间。

该名称可以具有“全局”或“本地”前缀,以在全局或会话命名空间中显式创建对象。名称的其余部分可以包含除反斜杠字符 () 之外的任何字符。有关详细信息,请参阅 内核对象命名空间。使用终端服务会话实现快速用户切换。内核对象名称必须遵循为终端服务概述的准则,以便应用程序可以支持多个用户。

该对象可以在私有命名空间中创建。有关详细信息,请参阅对象命名空间。

返回值

如果函数成功,则返回值是新创建的互斥对象的句柄(Handle)。

如果函数失败,则返回值为NULL。要获取扩展的错误信息,请调用 GetLastError 函数。

如果互斥锁是一个命名互斥锁并且该对象在此函数调用之前存在,则返回值是现有对象的句柄,并且 GetLastError 函数返回ERROR_ALREADY_EXISTS

通过句柄关闭互斥体

使用 Process Explorer

Process Explorer 是 Microsoft 官方提供的用于找出进程已打开或加载的HandleDLL信息的工具。

Process Explorer 官方页面:https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer

示例:

  1. 以 WeChat 为例,首先启动 WeChat,在 Process Explorer 主界面中找到名为WeChat.exe进程并选中。

  2. 然后在Lower Pane界面中找到TypeMutantName\Sessions\1\BaseNamedObjects\_WeChat_App_Instance_Identity_Mutex_NameHandle

  3. 右键单击Close Handle关闭句柄后,即可启动一个新进程。

process_explorer_wechat_1

process_explorer_wechat_2

Python代码实现
handles = handler.find_handles(process_ids=[10000], handle_names=[r'\Sessions\1\BaseNamedObjects\_WeChat_App_Instance_Identity_Mutex_Name'])
handler.close_handles(handles)

参考源码:client/handler/handler.py

通过tscon保持Windows远程桌面关闭后仍可交互的方法

使用远程桌面连接到远程计算机时,关闭远程桌面会锁定计算机并显示登录屏幕。在锁定模式下,计算机没有GUI,因此任何当前运行或计划的GUI测试都将失败。

为避免GUI测试出现问题,可以使用tscon实用程序断开与远程桌面的连接。tscon将控制权返回到远程计算机上的原始本地会话,绕过登录屏幕。远程计算机上的所有程序继续正常运行,包括GUI测试。

什么是tscon

tscon是Windows系统提供的可用于连接到远程桌面会话主机服务器上的另一个会话的工具。

如何使用tscon

tscon {<sessionID> | <sessionname>} [/dest:<sessionname>] [/password:<pw> | /password:*] [/v]
参数 描述
<sessionID> 指定要连接的会话的ID。如果使用可选/dest:<sessionname>参数,也可以指定当前会话的名称。
<sessionname> 指定要连接的会话的名称。
/dest:<sessionname> 指定当前会话的名称。当您连接到新会话时,此会话将断开连接。您还可以使用此参数将另一个用户的会话连接到不同的会话。
/password:<pw> 指定拥有要连接到的会话的用户的密码。当连接用户不拥有会话时,需要此密码。
/password:* 提示拥有您要连接的会话的用户的密码。
/v 显示有关正在执行的操作的信息。
/? 在命令提示符处显示帮助。
手动断开远程

要从远程桌面断开连接,请以管理员身份在远程计算机上(在远程桌面连接窗口中)运行以下命令,例如,通过命令行:

%windir%\System32\tscon.exe RDP-Tcp### NNN /dest:console

其中RDP-Tcp### NNN是当前远程桌面会话的ID,例如RDP-Tcp#5。您可以在Windows任务管理器的“用户”选项卡上的“会话”列中看到它。

Windows Task Manager

您将看到您的远程桌面服务会话已结束消息,并且远程桌面客户端将关闭。但远程计算机上的所有程序和测试将继续正常运行。

提示:会话列默认隐藏。要显示它,请右键单击显示 CPU、内存等的行中的某处,然后在打开的上下文菜单中选择会话。

通过批处理文件断开连接

您可以使用批处理文件自动执行断开过程。在远程计算机上,执行以下操作:

  1. 使用以下代码创建一个批处理文件:
for /f "skip=1 tokens=3" %%s in ('query user %USERNAME%') do (
  %windir%\System32\tscon.exe %%s /dest:console
)
  1. 创建此文件的桌面快捷方式。为此,请右键单击批处理文件并选择发送到 > 桌面(创建快捷方式)。
  2. 在快捷方式属性中,单击高级并选择以管理员身份运行。
  3. 在远程计算机上双击此快捷方式(在远程桌面连接窗口中)或在测试开始时调用此批处理文件(前提是测试以管理员身份运行)。

注意事项

  • tscon使远程计算机保持解锁状态,这会降低系统安全性。测试运行结束后,您可以使用以下命令锁定计算机:

    Rundll32.exe user32.dll, LockWorkStation
  • 如果远程计算机上正在运行rdpclip.exe进程,并且当您从远程会话断开连接时剪贴板不为空,则rdpclip.exe进程可能会失败。

    为避免此问题,您可以在断开会话之前终止rdpclip.exe进程。

参考链接

功能拓展

若需要拓展自动化功能或兼容不同版本的客户端,您可以在 rpa-client/app 模块中新增或编辑任务脚本。

项目中主要使用以下两种方式实现:

  1. pywinauto

    是用于 Microsoft Windows GUI 自动化 的 python 模块。在最简单的情况下,它允许您将鼠标和键盘操作发送到 Windows 对话框和控件,但它支持更复杂的操作,例如获取文本数据。

  2. Airtest

    是由网易游戏推出的一个跨平台的、基于图像识别的UI自动化测试框架,适用于游戏和App,支持平台有Windows、Android和iOS。

Deployment

RPA客户端部署

请确保使用的操作系统为 Windows 7 及以上,Python 的版本为3.7.0及以上。

下载并安装客户端

需要注意 airtest 目前版本依赖pywinauto==0.6.3,当前项目需要pywinauto==0.6.8,在安装依赖时请加上--no-deps参数,或者在安装完依赖后手动执行一遍pip install pywinauto==0.6.8

git clone https://github.com/yihleego/robotic-process-automation.git
cd robotic-process-automation/rpa-client
pip install --no-deps -r requirements.txt

配置客户端

客户端配置文件位于 rpa-client/config.yml,开发者可以根据实际场景修改配置。

Property Description Default
server.host 服务器主机 localhost
server.port 服务器端口 18888
server.path 服务器路径 /rpa
server.ssl 是否启用SSL False
app.size 程序最大可运行数量 32
app.path.<appid> 自定义程序路径 从注册表中获取
airtest.cvstrategy 图像识别算法 [ tpl,sift,brisk ]
airtest.timeout 图像识别算法 20秒
airtest.timeout-tmp 图像识别算法 3秒
logging.level 日志级别 DEBUG
logging.format 日志格式 默认格式
logging.filename 日志文件名 ./logs/rpa-client.log

启动客户端

运行 rpa-client/main.py 即可。

RPA服务端部署

请确保使用的 Java 的版本为17及以上,服务运行时依赖 MySQL 和 Redis,请务必在部署服务前安装并启动它们。

下载并安装服务端

git clone https://github.com/yihleego/robotic-process-automation.git
cd robotic-process-automation/rpa-server
mvn clean install

配置服务端

必填配置
Property Description Default
spring.datasource.driver-class-name 数据源驱动 com.mysql.cj.jdbc.Driver
spring.datasource.url 数据源URL jdbc:mysql://localhost:3306/rpa
spring.datasource.username 数据源用户名
spring.datasource.password 数据源密码
spring.data.redis.host Redis主机 localhost
spring.data.redis.port Redis端口 6379
spring.data.redis.password Redis密码
spring.data.redis.database Redis数据库 0

上述配置可于 application.properties 文件中修改。

可选配置
Property Description Default
rpa.websocket.port WebSocket服务端口 18888
rpa.websocket.path WebSocket服务路径 /rpa
rpa.websocket.idle-timeout WebSocket服务空闲超时时间 5m
rpa.converter.date-time-pattern 全局日期时间格式 yyyy-MM-dd HH:mm:ss
rpa.converter.date-pattern 全局器日期格式 yyyy-MM-dd
rpa.converter.time-pattern 全局时间格式 HH:mm:ss
rpa.client.cache-key 客户端缓存键格式 rpa:client:<appid>:<account>
rpa.client.cache-timeout 客户端缓存超时时间 5m

详情请见 RpaProperties

启动服务端

启动服务前,请先在MySQL实例中执行以下脚本

运行 RpaApplication.java 即可。

Q&A

UiaApp 和 AirApp 的区别

  • UiaApp:基于 UIA 实现的程序,通过 UI 组件的 ID、Name、Class 等定位对应的元素,然后对其进行操作, 类似 HTML 的 DOM 树,可以通过选择器定位元素和操作,例如: $(".btn").click();
  • AirApp:是基于图像识别实现。

判断一个应用是否支持 UIA 可以使用微软官方提供的软件 Inspect, 可以从官网或者这个仓库下载:

inspect.png

示例中微信使用了 UiaApp 模式,因为它是基于 UIA 实现的; 而企业微信使用了 AirApp 模式,它类似网页中仅有存在一个 Canvas,所有元素都是通过代码绘制渲染,所以只能通过图像识别定位。

Could not find module 'libiconv.dll'

下载 Visual C++ Redistributable Packages for Visual Studio 2013 https://www.microsoft.com/zh-cn/download/confirmation.aspx?id=40784

RPA Server 问题

请参考:2#issue

Contact

Attention

本项目仅供学习参考,请勿用于生产环境。

License

This project is under the MIT license. See the LICENSE file for details.

About

🤖 Robotic process automation (RPA) for WeChat, WeCom, etc. Support multiple clients and keep the computer unlocked when disconnect from the remote desktop.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published