Repository: leoxiaoping/pbottleRPA Branch: master Commit: 85516c89261a Files: 120 Total size: 244.6 KB Directory structure: gitextract_s9rnej04/ ├── .gitignore ├── GPT图像解析示例.js ├── GPT问题答案AI生成演示.js ├── LICENSE ├── README.md ├── WEB增强-数据批量爬取演示.js ├── WEB增强-浏览器元素操作演示.js ├── WEB增强-账号密码登录演示.js ├── [企业版]HID硬件级键盘鼠标演示.js ├── [企业版]外部控制能力.js ├── [企业版]屏幕物体查找Ai演示.js ├── [企业版]接力执行脚本.js ├── [企业版]集群控制中心日志回传.js ├── [企业版]集群控制中心流程升级 .js ├── [企业版]集群控制中心示例.js ├── [第三方 模块卸载].bat ├── [第三方 模块安装].bat ├── [第三方] 读写Excel演示脚本.mjs ├── [第三方] 读写word演示脚本.mjs ├── docs/ │ ├── -图片测试.md │ ├── .vitepress/ │ │ └── config.mjs │ ├── AI生成流程脚本.md │ ├── APIAI图像.md │ ├── APIAI大模型.md │ ├── API办公文档.md │ ├── API压缩解压.md │ ├── API声音.md │ ├── API外部控制.md │ ├── API屏幕.md │ ├── API浏览器增强.md │ ├── API用户输入.md │ ├── API系统相关.md │ ├── API统一规范.md │ ├── API网络.md │ ├── API通用工具.md │ ├── API键盘操作.md │ ├── API键鼠硬模拟.md │ ├── API鼠标操作.md │ ├── Demo示例.md │ ├── HTTP静态服务.md │ ├── SaaS系统自动化任务.md │ ├── index.md │ ├── package.json │ ├── public/ │ │ └── index.html │ ├── win7操作系统.md │ ├── Q&A.md │ ├── 专用自动化独立软件.md │ ├── 业务管理系统.md │ ├── 中文调用.md │ ├── 信创操作系统.md │ ├── 其他功能模块.md │ ├── 定时启动.md │ ├── 开机启动.md │ ├── 手机应用的自动化.md │ ├── 无尽模式.md │ ├── 更多三方功能和拓展.md │ ├── 桌面快捷方式.md │ ├── 流程录制.md │ ├── 流程运行日志.md │ ├── 流程配置项.md │ ├── 热键和快捷方式.md │ ├── 用 js 脚本开发自动化流程.md │ ├── 用 python 脚本开发自动化流程.md │ ├── 硬件键鼠模拟.md │ ├── 老旧低配电脑.md │ ├── 视频教程.md │ ├── 集群控制中心.md │ └── 验证码自动化.md ├── package.json ├── pbottleRPA.js ├── python示例/ │ ├── GPT图像解析示例.py │ ├── GPT问题答案AI生成演示.py │ ├── WEB增强-数据批量爬取演示.py │ ├── WEB增强-浏览器元素操作演示.py │ ├── WEB增强-账号密码登录演示.py │ ├── [企业版]接力执行脚本.py │ ├── [第三方] 读写Excel演示脚本.py │ ├── [第三方] 读写word演示脚本.py │ ├── pbottleRPA.py │ ├── 上传(发送)文件演示.py │ ├── 下载文件示例演示.py │ ├── 剪切板演示脚本.py │ ├── 压缩和解压缩示例.py │ ├── 发送Email电子邮件.py │ ├── 发送运维消息手机通知.py │ ├── 图片相似度检测.py │ ├── 基础(循环、判断、等待)演示.py │ ├── 屏幕物体轮廓查找演示.py │ ├── 常用工具 Utils 演示.py │ ├── 异步子流程模板.py │ ├── 微信朋友圈自动点赞.py │ ├── 快速开始演示(3行代码).py │ ├── 截屏操作演示脚本.py │ ├── 文件基础操作演示.py │ ├── 文字提取查找OCR演示.py │ ├── 用户手动输入变量示例.py │ ├── 运维消息手机通知.py │ ├── 键盘基本操作演示脚本.py │ └── 鼠标基础操作演示.py ├── word测试文档.docx ├── 上传(发送)文件演示.js ├── 下载文件示例演示.js ├── 企业版 Demo示例说明.txt ├── 剪切板演示脚本.js ├── 压缩和解压缩示例.js ├── 发送Email电子邮件.js ├── 发送运维消息手机通知.js ├── 图片相似度检测.js ├── 基础(循环、判断、等待)演示.js ├── 屏幕物体轮廓查找演示.js ├── 常用工具 Utils 演示.js ├── 异步子流程模板.js ├── 微信朋友圈自动点赞.js ├── 快速开始演示(3行代码).js ├── 截屏操作演示脚本.js ├── 文件基础操作演示.js ├── 文字提取查找OCR演示.js ├── 用户手动输入变量示例.js ├── 键盘基础操作演示脚本.js └── 鼠标基础操作演示.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /**/node_modules /**/__pycache__ node_modules Excel测试表格.xlsx /**/Excel测试表格.xlsx 单元测试test.js test.js test.mjs 配置项.json package-lock.json node_modules/ docs/.vitepress/cache docs/.vitepress/dist package-lock.json package-lock.json ================================================ FILE: GPT图像解析示例.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的GPT图像解析功能,可以向云端AI提问关于图片内容的问题 * 通过这个示例,您可以学习如何结合AI能力分析和理解图片内容 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 // 开始RPA操作 let ask = '描述图片中有什么?' // 定义要向AI提出的问题 console.log(ask,`./input/RPAlogo128.png`); // 在控制台输出问题和要分析的图片路径 const start = Date.now() // 记录开始时间,用于计算处理耗时 pbottleRPA.log('云端 AI 生成答案:') // 将提示信息输出到日志文件中 // 调用云端GPT图像分析API,传入问题和图片路径,并输出结果内容 console.log(pbottleRPA.cloud_GPTV(ask,`./input/RPAlogo128.png`).content) console.log('图片解析耗时:(毫秒)',Date.now()-start); // 计算并输出图片解析耗时(毫秒) ================================================ FILE: GPT问题答案AI生成演示.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的GPT问题答案AI生成功能 * 通过这个示例,您可以学习如何向云端AI提问并获取答案,实现智能问答功能 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 // 定义要向AI提问的问题列表 let asks = [ '鲁迅为什么要打周树人?', // 第一个问题:关于鲁迅的幽默问题 '给我随便作一首诗吧', // 第二个问题:要求AI创作诗歌 '你是谁?', // 第三个问题:询问AI身份 ] const start = Date.now() // 记录开始时间,用于计算处理所有问题的总耗时 // 使用循环遍历问题列表,逐个向AI提问 for (let index = 0; index < asks.length; index++) { // 遍历问题数组 const ask = asks[index]; // 获取当前问题 pbottleRPA.log(`❓️ 问题 ${index+1}:`,ask); // 将问题序号和内容输出到日志文件中 pbottleRPA.tts(ask) // 使用文字转语音功能播报当前问题 // 调用云端GPT API获取问题答案 let rs = pbottleRPA.cloud_GPT(ask,0) // 向云端AI提问,获取答案 pbottleRPA.log('云端 AI 生成答案:') // 在日志中输出提示信息 pbottleRPA.log(rs.content) // 将AI生成的答案内容输出到日志文件中 pbottleRPA.log('------------') // 输出分隔线 pbottleRPA.log() // 输出空行,美化日志格式 } // 输出所有问题答案生成的总耗时 console.log('答案全部生成耗时(毫秒):',Date.now()-start); ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2023 LEO Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # 小瓶RPA 专业用户的专业RPA+AI软件。 ### 介绍 小瓶RPA,长难业务自动化流程专精。 轻量级简单全能的RPA软件,显著降本增效 & 工作100%准确 & 非侵入式集成。同时支持浏览器web应用和客户端应用的操作流程自动化。同时支持 Js 和 Python 两种脚本制作流程。 **如果好用或者帮到您,烦劳star一下。** 产品官网:[https://rpa.pbottle.com/](https://rpa.pbottle.com/)  ### 起步 ```javascript pbottleRPA.打开网址('https://www.baidu.com/') pbottleRPA.粘贴输入('小瓶RPA官网') pbottleRPA.键盘按键('enter') ``` ### 文档入口 【新】 [https://rpa.pbottle.com/docs/](https://rpa.pbottle.com/docs/) ### 小瓶RPA优势 1. 自动化、AI、大模型能力可以快速落实到工作流程,并能精细化调整。 2. 轻量级 + 开放接口能力,无缝整合现有工作系统,而非高成本替代。 3. 纯视觉模拟驱动,可以兼容操作所有应用程序。 4. 技术运维人员友好,主流脚本语言表达,高天花板。 ### 产品设计常见疑问 ❓︎ 为什么小瓶没有图形拖拽的所谓'设计器',用编写js脚本来替代? - 图形编程历史已久,上手门槛虽然更低,但同时天花板也更低,目前面对长难流程的开发管理往往不能满足和有更高综合成本。(有商业价值的自动化项目往往都是长难流程) - js脚本可以融入完整的 Nodejs(Python)生态,无缝引入万亿第三方功能包,同时可以使用 git 等代码工程管理工具和传统IT项目自然对接。 - V2023.3版本 新增加鼠标操作录制自动生成简单的脚本功能。 - 🔥 AI 对话大模型 生成小瓶RPA流程脚本 [查看](https://rpa.pbottle.com/docs/AI%E7%94%9F%E6%88%90%E6%B5%81%E7%A8%8B%E8%84%9A%E6%9C%AC.html) ❓︎ 为什么小瓶采用js作为脚本语言,可以用 python、java、c# 等吗? - 可以,小瓶RPA内核和脚本层采用http通信接口,虽然官方的demo脚本层选用了js(同时兼容性支持Python),但有经验的其他语言开发者,可以随时通过最常规的http接口接入小瓶RPA。 - JS目前是前端的主流脚本,有大量的web经验的群众基础。同时JS的前端开放性,GPT等大模型AI工具更容易输出准确的结果。 ❓︎ 可以脱网或者内网使用小瓶RPA吗? - 企业版支持,个人版启动一次联网免费授权。 ### 软件架构  ### 公司支持 北京小瓶科技有限公司,对商业版客户提供技术支持和增值服务 官网:[https://www.pbottle.com/](https://www.pbottle.com/) ### 使用须知 1. 允许个人永久免费使用本项目,包括用于个人学习、游戏娱乐、毕业设计、教学案例、公益事业和其他非商业用途;不包含个人的上班时的工作目的; 2. 必须保留版权信息,请自觉遵守; 3. 未经授权禁止将本软件、代码和其他资源以任何形式出售(包含收费项目捆绑的免费部分); # 安装教程 ### 步骤 1. 下载exe运行基座绿色版 pbottleRPA.zip [基座exe程序绿色版](https://rpa.pbottle.com/) 2. 安装 NodeJS 脚本引擎 .msi,并安装 [下载网站](https://rpa.pbottle.com/a-13943.html) 安装后请重启基座 3. 下载 [测试脚本](https://gitee.com/pbottle/pbottle-rpa/repository/archive/master.zip),运行基座选择demo脚本即可开始运行 ### 常见问题 1. 如果系统提示 缺少vcruntime140XX.dll 微软官网下载安装即可:[https://docs.microsoft.com/zh-CN/cpp/windows/latest-supported-vc-redist?view=msvc-140](https://docs.microsoft.com/zh-CN/cpp/windows/latest-supported-vc-redist?view=msvc-140) (新版本已兼容) 1. exe启动目录的路径不能含中文,直接复制到其他目录,或者文件夹copy到磁盘根目录即可 (新版本已兼容) 1. 服务端口(49888)监听不成功问题, 排查请看:[https://rpa.pbottle.com/a-13924.html](https://rpa.pbottle.com/a-13924.html) 1. 软件不再支持32位老旧版操作系统 ### 电脑要求 新!V2023版本进一步提高了AI算法库对电脑的兼容性,请下载最新版本。 win7系统注意事项:https://rpa.pbottle.com/a-13941.html ### 手机应用RPA 1. 手机应用可以采用 Android 模拟器方案 或者 真机投屏交互方案。得益于小瓶RPA采用纯图像识别的驱动方式,完全兼容各种手机应用模拟器 和 手机厂商的镜像投屏 可以使用除了web增强插件外的任意api接口能力 1. 模拟器方案: 经过我们测试的推荐: 蓝叠 已经有问题的:雷电模拟器,剪切板同步延迟,输入有问题 1. 真机投屏交互方案: vivo办公套件 华为智慧互联 ### Web应用浏览器增强 web增强可以使小瓶RPA脚本直接操作浏览器Dom元素,更方便快捷 同时支持使用Dom选择器选择元素并返回结果 _注意:此功能需要安装小瓶RPA浏览器插件,版本需求:V2023.5以后支持_ 插件安装下载地址:[https://rpa.pbottle.com/a-13942.html](https://rpa.pbottle.com/a-13942.html) ### Demo示例 自带Demo示例:(中文标题为demo示例脚本 后续会添加更多) 注意: 1. 【web增强】需要先安装小瓶RPA浏览器插件 2. 【第三方】需要先双击 _第三方模块安装.bat_ 安装所需模块。 ### 全局热键 **Ctrl + Shift + Q** - 停止当前的脚本运行 结束当前的鼠标操作录制 **Ctrl + Shift + R** - 重新启动当前的脚本运行 # AI 模型的使用 ### 本地模型 1. OCR文字识别 示例: [文字提取查找OCR演示.js](文字提取查找OCR演示.js) 2. 对象识别模型 示例:[GPT图像解析示例](GPT图像解析示例.js) ### 云端大模型 1. 大语言模型 示例: [文字提取查找OCR演示.js](文字提取查找OCR演示.js) 2. 对象识别模型 示例: [GPT图像解析示例.js](GPT图像解析示例.js) # 开发流程脚本 ### RPA脚本开发文档 文档入口: [https://rpa.pbottle.com/docs/](https://rpa.pbottle.com/docs/) ### 支持脚本语言 1. NodeJS 2. Python(Beta) ### 其它参考 1. 键盘表 [https://rpa.pbottle.com/a-13862.html](https://rpa.pbottle.com/a-13862.html) 2. 挂机定时任务 [https://rpa.pbottle.com/a-13868.html](https://rpa.pbottle.com/a-13868.html) 3. 免费手机通知 [https://rpa.pbottle.com/a-12586.html](https://rpa.pbottle.com/a-12586.html) ### 技术交流微信群 喜欢群聊的可以微信扫码加入(永不过期):  ### 官方RPA增值服务 [联系我们](https://rpa.pbottle.com/value-added.php) ### 小瓶RPA招聘 英雄不问出处,各路英才简历投递方式: 打开小瓶RPA官网,浏览器console控制台查看。 ================================================ FILE: WEB增强-数据批量爬取演示.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了使用Web增强功能批量爬取网页数据 * 需要安装小瓶RPA浏览器插件来操作网页元素,实现网页数据的自动化抓取 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 console.log(pbottleRPA.getTime()); // 在控制台输出当前格式化时间 console.log("=== ※※※※※※※※※ ==="); console.log("=== 需要安装 小瓶RPA 浏览器插件 ==="); // 提示用户需要安装浏览器插件 console.log("=== ※※※※※※※※※ ==="); // 使用文字转语音功能提示用户必须安装浏览器插件 pbottleRPA.tts('必须安装小瓶RPA浏览器增强插件,5秒后开始爬取网页数据') // 显示系统消息框再次提醒用户 pbottleRPA.showMsg('提示:','必须先安装浏览器增强插件') // 等待5秒钟给用户准备时间 pbottleRPA.wait(5) // 打开小瓶RPA官网用于演示数据爬取 pbottleRPA.openURL('https://rpa.pbottle.com/') pbottleRPA.browserCMD_waitPageReady('https://rpa.pbottle.com/'); // 等待页面加载完成 // 判断页面是否成功打开,通过检查页面中包含"小瓶RPA"的元素数量 let n_rpa = pbottleRPA.browserCMD_count('span:contains(小瓶RPA)') console.log('包含 小瓶RPA 元素数量:',n_rpa); // 输出元素数量到控制台 // 模拟滚动页面,加载更多内容 pbottleRPA.keyTap('page down') // 向下翻页 pbottleRPA.keyTap('page down') // 再次向下翻页 pbottleRPA.keyTap('page down') // 第三次向下翻页 // 开始获取网页上的数据 // 使用CSS选择器获取所有class为'list-group-item'的a标签的文本内容 let rs = pbottleRPA.browserCMD_text('a.list-group-item') // 检查是否超时(说明插件未安装或网络问题) if (rs == '20s超时') { // 显示错误消息并退出脚本 pbottleRPA.showMsg('出现错误:','必须先安装浏览器增强插件和联网') pbottleRPA.exit() } // 将获取到的JSON字符串数据解析为JavaScript对象数组 datas = JSON.parse(rs) console.log('爬取数据数量:',datas.length); // 输出爬取到的数据条数 // 使用文字转语音功能播报爬取到的数据数量 pbottleRPA.tts('爬取数据'+ datas.length +'条,请查看日志') pbottleRPA.wait(4) // 等待4秒钟 console.log('====='); // 输出分隔线 console.log('数据列表:'); // 输出提示信息 // 遍历数据数组,处理并输出每条数据 datas.forEach(element => { // 去除首尾空格并移除换行符 element = element.trim().replace(/[\r\n]/g, ''); console.log(element); // 输出处理后的数据 }); // 获取网页元素的href属性值(链接地址) rs = pbottleRPA.browserCMD_attr('a.list-group-item','href') // 将获取到的JSON字符串解析为链接数组 datas = JSON.parse(rs) console.log('===='); // 输出分隔线 console.log('链接列表:'); // 输出提示信息 // 遍历链接数组并输出每个链接 datas.forEach(element => { console.log(element); // 输出链接地址 }); // 使用文字转语音功能播报演示结束 pbottleRPA.tts('演示结束') pbottleRPA.browserCMD.alert('演示结束,数据已经批量输出到日志中,请查看控制台'); ================================================ FILE: WEB增强-浏览器元素操作演示.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了使用Web增强功能操作浏览器元素的各种方法 * 包括网址跳转、文本获取、Cookie操作、CSS样式修改、元素值设置、点击操作等 * 需要安装小瓶RPA浏览器插件来实现这些功能 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 console.log(Date()); // 在控制台输出当前日期时间 console.log("=== ※※※※※※※※※ ==="); console.log("=== 需要安装 小瓶RPA 浏览器插件 ==="); // 提示用户需要安装浏览器插件 console.log("=== ※※※※※※※※※ ==="); // 使用文字转语音功能提示用户必须安装浏览器插件 pbottleRPA.tts('必须安装小瓶RPA浏览器增强插件,手动点击确定继续') // 显示系统消息框再次提醒用户 pbottleRPA.showMsg('提示:','必须先安装浏览器增强插件') // 打开百度网站用于演示浏览器操作 pbottleRPA.openURL('https://www.baidu.com/') // 定义变量用于接收浏览器命令的返回值 let ret = "" // 使用浏览器命令显示弹窗,等待用户手动确认(20秒超时) ret = pbottleRPA.browserCMD_alert('来自小瓶RPA的问候,手动点击确定开始,20秒超时') console.log('返回操作结果 alert',ret); // 输出操作结果到控制台 // 检查浏览器插件是否正常工作 if (ret !== 'ok') { console.log('没有检测到小瓶RPA浏览器插件',ret); // 如果未检测到插件,输出错误信息 process.exit(1) // 退出脚本 } pbottleRPA.wait(1) // 等待1秒钟 pbottleRPA.tts("跳转新网址:") // 语音播报即将执行的操作 // 使用浏览器命令跳转到新的网址 pbottleRPA.browserCMD_url('https://www.baidu.com/?from=pbottleRPA') pbottleRPA.wait(2) // 等待2秒钟 // 获取指定元素的文本内容(页面标题) ret = pbottleRPA.browserCMD_text('span.title-content-title') console.log('返回操作结果【一次多个】',ret); // 输出获取到的文本内容 // Cookie操作演示 ret = pbottleRPA.browserCMD_cookie('BAIDUID') // 获取指定名称的Cookie值 console.log('返回操作结果 cookieGet',ret); // 输出获取到的Cookie值 // 设置Cookie值 ret = pbottleRPA.browserCMD_cookie('pbottleID',"good",3) console.log('返回操作结果 cookieSet',ret); // 输出设置Cookie的结果 // CSS样式操作演示 - 变换背景色 pbottleRPA.tts('变换背景色') // 语音播报即将执行的操作 // 设置body元素的背景色为蓝色 ret = pbottleRPA.browserCMD_css('body',"background-color",'blue') console.log('返回操作结果 cssSet',ret); // 输出设置结果 // 获取body元素的背景色值 ret = pbottleRPA.browserCMD_css('body',"background-color") console.log('返回操作结果【颜色值】',ret); // 输出获取到的颜色值 // 将body元素的背景色重新设置为白色 ret = pbottleRPA.browserCMD_css('body',"background-color",'white') console.log('返回操作结果 cssSet',ret); // 输出设置结果 // 文本内容操作演示 ret = pbottleRPA.browserCMD_text('title') // 获取页面标题文本 console.log('返回操作结果 textGet',ret); // 输出获取到的标题文本 pbottleRPA.tts('获取标题 ') // 语音播报操作内容 pbottleRPA.wait(3) // 等待3秒钟 // 设置页面标题演示 pbottleRPA.tts('设置页面标题 ') // 语音播报即将执行的操作 // 设置新的页面标题,加上前缀"[小瓶RPA]-" ret = pbottleRPA.browserCMD_text('title','[小瓶RPA]-'+ret) console.log('返回操作结果 textSet',ret); // 输出设置结果 ret = pbottleRPA.browserCMD_text('title') // 重新获取页面标题 console.log('当前页面标题:',ret); // 输出当前页面标题 pbottleRPA.wait(3) // 等待3秒钟 // 搜索操作演示 pbottleRPA.tts('输入搜索词 点击搜索按钮 ') // 语音播报即将执行的操作 // 在搜索框中输入搜索词"小瓶RPA" pbottleRPA.paste('小瓶RPA官网') // 输出输入操作结果 // 点击搜索按钮 ret = pbottleRPA.browserCMD_click('#su') // 点击百度搜索按钮 console.log('返回点击操作结果 click',ret); // 输出点击操作结果 pbottleRPA.wait(3) // 等待3秒钟 // 获取当前网址演示 pbottleRPA.tts('获取当前网址:') // 语音播报即将执行的操作 ret = pbottleRPA.browserCMD_url() // 获取当前页面URL console.log('获取当前网址:',ret); // 输出当前网址 pbottleRPA.wait(2) // 等待2秒钟 // 去广告操作演示 pbottleRPA.tts('开始去广告') // 语音播报即将执行的操作 // 循环执行去广告操作(示例中只执行1次) for (let index = 0; index < 1; index++) { // 移除指定的广告元素 ret = pbottleRPA.browserCMD_remove('#content_left div:first') console.log('返回点击操作结果 remove',ret); // 输出移除操作结果 pbottleRPA.wait(3) // 等待3秒钟 } // 打开网站链接演示 pbottleRPA.tts('打开网站') // 语音播报即将执行的操作 // 点击第一个搜索结果链接 pbottleRPA.browserCMD_click('div#content_left a:contains(小瓶RPA)') pbottleRPA.wait(3) // 等待默认时间 // 获取网站Logo路径演示 pbottleRPA.tts('读取 logo 路径,显示到日志') // 语音播报即将执行的操作 // 获取第一个img元素的src属性值(即图片地址) ret = pbottleRPA.browserCMD_attr('img:first','src') console.log('网站logo图片地址',ret); // 输出Logo图片地址 pbottleRPA.wait() // 等待默认时间 // 获取元素位置信息演示 let ret2 = pbottleRPA.browserCMD_offset('div:contains(小瓶RPA):first') console.log('搜索结果的位置',ret2); // 输出元素位置信息 pbottleRPA.wait() // 等待默认时间 // 演示完成提示 pbottleRPA.tts('演示完成准备退出') // 语音播报演示结束 console.log("准备结束脚本"); // 在控制台输出结束信息 // 浏览器内显示演示结束的弹窗 ret = pbottleRPA.browserCMD_alert('演示结束') ================================================ FILE: WEB增强-账号密码登录演示.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了使用Web增强功能实现网站自动登录 * 需要安装小瓶RPA浏览器插件来操作网页元素,实现完整的登录流程自动化 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 console.log(Date()); // 在控制台输出当前日期时间 console.log("=== ※※※※※※※※※ ==="); console.log("=== 需要安装 小瓶RPA 浏览器插件 ==="); // 提示用户需要安装浏览器插件 console.log("=== ※※※※※※※※※ ==="); // 使用文字转语音功能提示用户必须安装浏览器插件 pbottleRPA.tts('必须安装小瓶RPA浏览器增强插件,手动点击确定继续') // 显示系统消息框再次提醒用户 pbottleRPA.showMsg('提示:','必须先安装浏览器增强插件') // 打开指定网址用于演示登录操作 pbottleRPA.openURL('https://yun.pbottle.com/?from=rpademo') // 使用浏览器命令显示弹窗,等待用户手动确认(20秒超时) let ret = pbottleRPA.browserCMD_alert('来自小瓶RPA的问候,手动点击确定开始,20秒超时') console.log('返回操作结果',ret); // 输出操作结果到控制台 // 检查浏览器插件是否正常工作 if (ret !== 'ok') { console.log('没有检测到小瓶RPA浏览器插件',ret); // 如果未检测到插件,输出错误信息 process.exit(1) // 退出脚本 } // 点击页面上的"登录或注册"链接 pbottleRPA.browserCMD_click(`a[role='button']:contains(登录或注册)`) pbottleRPA.wait(2) // 等待2秒钟 // 点击"登录"按钮 pbottleRPA.browserCMD_click(`a[role='button']:contains(登录)`) pbottleRPA.wait() // 等待默认时间 // 输入账号密码 // 点击用户名输入框 pbottleRPA.browserCMD_click(`input[name='uname']`) // 在用户名输入框中输入用户名'test' pbottleRPA.browserCMD_val(`input[name='uname']`,'test') // 点击密码输入框 pbottleRPA.browserCMD_click(`input[name='pwd']`) // 在密码输入框中输入密码'123456' pbottleRPA.browserCMD_val(`input[name='pwd']`,'123456') pbottleRPA.wait() // 禁用登录按钮测试 pbottleRPA.browserCMD_prop(`button:contains(登录帐号)`,'disabled',true); pbottleRPA.wait() pbottleRPA.browserCMD_prop(`button:contains(登录帐号)`,'disabled',false); // 确保登录按钮可用 // 点击登录按钮 pbottleRPA.browserCMD_click(`button:contains(登录帐号)`) pbottleRPA.wait(3) // 等待3秒钟 // 模拟按下回车键确认登录 pbottleRPA.keyTap('enter') // 使用文字转语音功能播报演示结束 pbottleRPA.tts('演示结束') ================================================ FILE: [企业版]HID硬件级键盘鼠标演示.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了企业版的HID硬件级键盘鼠标操作功能 * 通过硬件级别的输入模拟,可以绕过一些软件层的限制,实现更稳定的自动化操作 * 注意:此功能仅在企业版中可用,且需要额外的硬件外设支持 * * HID 注意: * ①此模块不是必须模块 * ②此模块功能需要添加电脑硬件外设,购买装配请咨询小瓶RPA客服 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 // 监测是否开通企业版 let bufferRS = pbottleRPA.bufferSet('pbottle'); // 检查是否为个人版(个人版不支持此功能) if (bufferRS == '个人版不可用') { // 显示系统消息框提示用户 pbottleRPA.showMsg('个人版不可用','请先开通企业版') // 使用文字转语音功能播报提示信息 pbottleRPA.tts('个人版不可用,请先开通企业版') // 退出脚本并输出错误信息 pbottleRPA.exit('⚠ 个人版不可用,请先开通企业版') } console.log("=== HID 键盘模拟测试 ==="); // 在控制台输出测试开始信息 console.log(Date()); // 在控制台输出当前日期时间 // 显示系统消息框提示用户需要硬件外设 pbottleRPA.showMsg('需要硬件外设','测试需要硬件外设') // 使用文字转语音功能播报提示信息 pbottleRPA.tts('正在打开键盘测试网站,需要开启RPA硬件模拟功能') // 打开键盘测试网站用于演示硬件级输入 pbottleRPA.openURL('https://www.keyboardtest.cn/') pbottleRPA.wait(3) // 等待3秒钟让网页加载完成 // 使用HID接口模拟按下F11键,进入全屏模式 pbottleRPA.hid.keyTap('f11') // 获取当前屏幕分辨率信息 let resolution = pbottleRPA.getResolution() console.log('当前分辨率:',resolution); // 输出分辨率信息到控制台 // 使用HID接口移动鼠标到屏幕中心位置 pbottleRPA.hid.moveMouse(resolution.w/2,resolution.h/2) // 使用HID接口执行鼠标点击操作 pbottleRPA.hid.mouseClick(); // 使用HID接口控制鼠标滚轮操作 pbottleRPA.hid.mouseWheel(-2) // 向下滚动2个单位 pbottleRPA.wait() // 等待默认时间 pbottleRPA.hid.mouseWheel(1) // 向上滚动1个单位 pbottleRPA.wait() // 等待默认时间 pbottleRPA.hid.mouseWheel() // 默认向下滚动 // 使用HID接口执行不同按键的鼠标点击操作 pbottleRPA.hid.mouseClick('middle'); // 中键点击 pbottleRPA.hid.mouseClick('left',3000); // 左键长按3秒 pbottleRPA.hid.mouseClick('right'); // 右键点击 pbottleRPA.wait() // 等待默认时间 // 移动鼠标到屏幕指定位置 pbottleRPA.hid.moveMouse(resolution.w/3,resolution.h/2) pbottleRPA.hid.mouseDoubleClick() // 执行鼠标双击操作 pbottleRPA.hid.mouseClick(); // 执行鼠标单击操作 // 内容按键演示 - 逐个按下英文字母和符号键 let str = "abcdefghijklmnopqrstuvwxyz`1234567890-=[]\\;',./"; // 遍历字符串中的每个字符并模拟按键 for (let char of str) { console.log(' 按键:',char); // 输出当前按键字符到控制台 pbottleRPA.hid.keyTap(char) // 使用HID接口模拟按键 } // 控制输入按键演示 pbottleRPA.hid.keyTap('up') // 按下方向键上 pbottleRPA.hid.keyTap('down') // 按下方向键下 pbottleRPA.hid.keyTap('left') // 按下方向键左 pbottleRPA.hid.keyTap('right') // 按下方向键右 pbottleRPA.hid.keyTap('space') // 按下空格键 pbottleRPA.hid.keyTap('page up') // 按下Page Up键 pbottleRPA.hid.keyTap('page down') // 按下Page Down键 pbottleRPA.hid.keyTap('end') // 按下End键 pbottleRPA.hid.keyTap('home') // 按下Home键 pbottleRPA.hid.keyTap('tab') // 按下Tab键 pbottleRPA.hid.keyTap('shift') // 按下Shift键 pbottleRPA.hid.keyTap('backspace') // 按下退格键 pbottleRPA.hid.keyTap('enter') // 按下回车键 // 使用HID接口再次按下F11键,退出全屏模式 pbottleRPA.hid.keyTap('f11') pbottleRPA.wait(1) // 等待1秒钟 // 使用HID接口模拟按下Ctrl+Alt+Del组合键(系统级操作) pbottleRPA.hid.keyTap('ctrl + alt + del') pbottleRPA.wait(1) // 等待1秒钟 // 使用HID接口模拟按下Esc键 pbottleRPA.hid.keyTap('esc') pbottleRPA.showMsg('测试完成', 'HID 键盘模拟测试完成'); ================================================ FILE: [企业版]外部控制能力.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ */ const pbottleRPA = require('./pbottleRPA') console.log("=== 外部控制能力测试 ==="); console.log(Date()); //监测是否开通企业版 let bufferRS = pbottleRPA.bufferSet('pbottle'); if (bufferRS == '个人版不可用') { pbottleRPA.showMsg('个人版不可用','请先开通企业版') pbottleRPA.tts('个人版不可用,请先开通企业版') pbottleRPA.exit('⚠ 个人版不可用,请先开通企业版') } /** * buffers */ pbottleRPA.wait() for (let index = 0; index < 10; index++) { let content = "我是 +=<& buffer"+index pbottleRPA.bufferSet(content,index) } pbottleRPA.wait() for (let index = 0; index < 10; index++) { console.log('buffer'+ index + ' 内容:', pbottleRPA.bufferGet(index)); } let other = '其他内容' let myJson = { name:'小瓶RPA', webSite:'https://rpa.pbottle.com/', version:2, app:['pc','web','mobile'], other, } pbottleRPA.bufferSet(myJson) //默认buffer0 console.log('获取buffer: ',pbottleRPA.bufferGet()); /** * 启停 */ let path=__filename; console.log(path); let url = 'http://127.0.0.1:49888/?action=pbottleRPA_run&path='+encodeURIComponent(path) console.log(url); fetch(url).then((rs)=>{ console.log(rs.status); return rs.text() }).then((txt)=>{ console.log('启动结果:',txt); }).catch((error)=>{ console.log(error); }) //当前运行状态 let urlState = 'http://127.0.0.1:49888/?action=pbottleRPA_state' let rs = pbottleRPA.getHtml(urlState) console.log("当前运行状态(isRunning | ready):",rs); setTimeout(()=>{ // pbottleRPA.openURL('http://127.0.0.1:49888/?action=pbottleRPA_lastLog') //获取日志 //外部停止 let urlStop = 'http://127.0.0.1:49888/?action=pbottleRPA_stop' pbottleRPA.getHtml(urlStop) },3000) ================================================ FILE: [企业版]屏幕物体查找Ai演示.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了企业版的AI物体识别功能 * 可以识别屏幕指定区域内的物体类型和位置,依赖于当前的训练模型 * 注意:此功能仅在企业版中可用 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 console.log("=== 屏幕物体识别测试开始 ==="); // 在控制台输出测试开始信息 console.log(Date()); // 在控制台输出当前日期时间 // 获取设备ID以检查是否为企业版 let deviceID = pbottleRPA.deviceID(); console.log('设备号:',deviceID); // 输出设备ID到控制台 // 检查是否为个人版(个人版不支持此功能) if (deviceID == '个人版不可用') { console.log('个人版不可用,请先开通企业版'); // 输出提示信息到控制台 // 显示系统消息框提示用户 pbottleRPA.showMsg('个人版不可用','请先开通企业版') // 使用文字转语音功能播报提示信息 pbottleRPA.tts('个人版不可用,请先开通企业版') // 退出脚本并输出错误信息 pbottleRPA.exit('个人版不可用,请先开通企业版') } // 提示用户物体识别种类依赖于当前的训练模型 console.log('物体识别种类依赖于当前的训练模型'); // 使用文字转语音功能播报提示信息 pbottleRPA.tts('物体识别种类依赖于当前的训练模型') console.log("将要识别屏幕线框内物体,5秒后开始"); pbottleRPA.wait(5) // 等待5秒钟 // 使用AI物体识别功能检测屏幕指定区域内的物体 // 参数说明:置信度阈值0.5,检测区域坐标(745,291,424,565) let rs = pbottleRPA.aiObject(0.5,745,291,424,565) console.log("检测到物体结果:",rs); // 输出检测到的物体结果到控制台 ================================================ FILE: [企业版]接力执行脚本.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ */ const pbottleRPA = require('./pbottleRPA') //监测是否开通企业版 let bufferRS = pbottleRPA.bufferSet('pbottle'); if (bufferRS == '个人版不可用') { pbottleRPA.showMsg('个人版不可用','请先开通企业版') pbottleRPA.tts('个人版不可用,请先开通企业版') pbottleRPA.exit('⚠ 个人版不可用,请先开通企业版') } pbottleRPA.delaySet(__filename) //自己接力自己 pbottleRPA.log('等待3秒') pbottleRPA.wait(3) pbottleRPA.log('完成') ================================================ FILE: [企业版]集群控制中心日志回传.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ */ const pbottleRPA = require('./pbottleRPA') const serverURL = pbottleRPA.clusterCenter(); console.log('集群控制中心地址:',serverURL); const localLogURL = 'http://127.0.0.1:49888/?action=pbottleRPA_lastLog2' const deviceId = pbottleRPA.deviceID() const buffer0 = pbottleRPA.bufferGet(0) // 获取taskId let taskId; try { taskId = JSON.parse(buffer0)._taskInfo.id } catch (error) { pbottleRPA.exit('⚠ 未获取到taskId,日志回传终止') } // 获取本地运行日志 const content = pbottleRPA.getHtml(localLogURL) console.log('本地日志长度:',content.length) console.log('deviceId:',deviceId); console.log("taskId:",taskId); // 远程传输日志 fetch(serverURL+'?action=postLog', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ deviceId, taskId, content }) }) .then(res => res.text()).then(text => console.log('日志回传结果:',text)) ================================================ FILE: [企业版]集群控制中心流程升级 .js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ */ const pbottleRPA = require('./pbottleRPA') //日志回传 pbottleRPA.delaySet('./[企业版]集群控制中心日志回传.js'); const serverURL = pbottleRPA.clusterCenter(); let rs = pbottleRPA.getHtml(serverURL+'?action=update'); rsJson = JSON.parse(rs); console.log('远程服务器返回:',rsJson); const fileURL = serverURL.replace('/api/query','')+ rsJson.fileID pbottleRPA.downloadFile(fileURL,'./newVersion.zip') pbottleRPA.wait() let destPath = pbottleRPA.path.resolve(__dirname + `/../${rsJson.version}`) pbottleRPA.log('新工作空间:',destPath) pbottleRPA.fs.mkdirSync(destPath, { recursive: true }) pbottleRPA.wait() pbottleRPA.unZip('./newVersion.zip', destPath); pbottleRPA.wait() pbottleRPA.log('新版本下载并解压完成'); // 回传更新完成状态 rs = pbottleRPA.postJson(serverURL+'?action=updateFinish',{ deviceId: pbottleRPA.deviceID(), version: rsJson.version, workDir: destPath.replaceAll('\\','/')+'/', }) pbottleRPA.log('远程服务器返回:',rs); ================================================ FILE: [企业版]集群控制中心示例.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ */ const pbottleRPA = require('./pbottleRPA') //日志回传 pbottleRPA.delaySet('./[企业版]集群控制中心日志回传.js'); //监测是否开通企业版 let bufferRS = pbottleRPA.bufferSet('pbottle',5); if (bufferRS == '个人版不可用') { pbottleRPA.showMsg('个人版不可用','请先开通企业版') pbottleRPA.tts('个人版不可用,请先开通企业版') pbottleRPA.exit('⚠ 个人版不可用,请先开通企业版') } //控制中心配置参数从本地 buffer0 中直接读取 let buffer = pbottleRPA.bufferGet(0) console.log('下达任务参数(json格式):',buffer); //流程开始 let resolution = pbottleRPA.getResolution() console.log('当前电脑屏幕分辨率',resolution) console.log('集群任务执行完成 🎉🎉🎉'); ================================================ FILE: [第三方 模块卸载].bat ================================================ chcp 65001 @echo off cd . echo 正在卸载模块中 call npm uninstall exceljs mammoth docx echo. echo. echo 卸载完成!~ 按任意键退出 pause >nul ================================================ FILE: [第三方 模块安装].bat ================================================ chcp 65001 @echo off cd . echo 正在切换国内安装源 call npm config set registry https://registry.npmmirror.com/ echo. echo 正在安装模块中 call npm install exceljs mammoth docx echo. echo. echo 恭喜,安装完成!~ 按任意键退出 pause >nul ================================================ FILE: [第三方] 读写Excel演示脚本.mjs ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ */ import pbottleRPA from "./pbottleRPA.js"; //必须含 .js 后缀 import os from 'node:os' let ExcelJS // import ExcelJS from "exceljs" try { const {default:myExcelJS} = await import('exceljs') // 中文文档: https://gitee.com/mirrors/exceljs ;所有序号从1开始和excel序号保持一致 👍 ExcelJS=myExcelJS } catch { pbottleRPA.showMsg('请先安装第三方模块','双击【第三方 模块安装.bat】') pbottleRPA.tts('请先安装第三方模块' + '双击【第三方 模块安装.bat】') pbottleRPA.exit('请先安装第三方模块' + '双击【第三方 模块安装.bat】') } console.log("=== Excel 读写测试 ==="); console.log(Date()); pbottleRPA.tts('Excel 读写测试') pbottleRPA.wait(3) pbottleRPA.tts(`将当前电脑配置信息生成EXCEL文件`) pbottleRPA.wait(5) //生成excel文档 const workbook = new ExcelJS.Workbook() const sheet = workbook.addWorksheet('pbottleRPA') sheet.addRow(['项', '值']) sheet.getRow(1).font = {bold:true} //第一行粗体 sheet.getColumn(1).width = 30; //列宽 sheet.getColumn(2).width = 60; sheet.addRows([ [ '时间', pbottleRPA.getTime(), ], [ '显示器分辨率', JSON.stringify(pbottleRPA.getResolution()), ], [ 'CPU', os.cpus()[0].model, ], ]) await workbook.xlsx.writeFile(`./Excel测试表格.xlsx`) pbottleRPA.tts(`已经生成EXCEL测试表格...请查看 `) pbottleRPA.openDir(pbottleRPA.__dirname) //追加一条数据 await excelAppend(pbottleRPA.__dirname + `/Excel测试表格.xlsx`, ['项名','重新追加值']) //所有异步方法(async 返回 promise),都用 await 以形成顺序执行的流程 pbottleRPA.wait(5) //读取excel const workbook2 = new ExcelJS.Workbook(); await workbook2.xlsx.readFile(`./Excel测试表格.xlsx`); let sheet2 = workbook2.getWorksheet(1) console.log(sheet2.getSheetValues()); pbottleRPA.tts(`已经读取EXCEL测试表格到日志 `) /** * Excel 文件追加一行数据 * @param {string} filename 文件绝对路径 * @param {[]} line 行数据 */ async function excelAppend(filename,line=[]) { console.log('excel追加行',filename,line); //读取excel const workbook = new ExcelJS.Workbook(); await workbook.xlsx.readFile(filename); let sheet2 = workbook.getWorksheet(1) //excel 重新追加一行记录到已有的excel文件末尾 sheet2.addRow(line) await workbook.xlsx.writeFile(filename) //保存 } ================================================ FILE: [第三方] 读写word演示脚本.mjs ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ */ import pbottleRPA from "./pbottleRPA.js"; //必须含 .js 后缀 import fs from 'node:fs' let mammoth let docx try { mammoth = await import('mammoth') // import mammoth from "mammoth" docx = await import('docx') // import docx from "docx" } catch { pbottleRPA.showMsg('请先安装第三方模块','双击【第三方 模块安装.bat】') pbottleRPA.tts('请先安装第三方模块' + '双击【第三方 模块安装.bat】') pbottleRPA.exit('请先安装第三方模块' + '双击【第三方 模块安装.bat】') } console.log("=== word 后台读写测试 ==="); console.log(Date()); pbottleRPA.tts('word 后台读写测试') pbottleRPA.wait(3) pbottleRPA.tts(`将后台生成 word 文件`) pbottleRPA.wait(5) //生成 word 文档 更多例子:https://gitee.com/mirrors_dolanmiu/docx/tree/master/demo const doc = new docx.Document({ sections: [ { properties: {}, children: [ new docx.Paragraph({ children: [ new docx.TextRun({ text: "标题文字", bold: true, size: 40, }), ], }), new docx.Paragraph({ children: [ new docx.TextRun({ text: "小瓶RPA官网:", }), new docx.ExternalHyperlink({ children: [ new docx.TextRun({ text: "https://rpa.pbottle.com", style: "Hyperlink", }), ], link: "https://rpa.pbottle.com", }), ], }), ], }, ], }); let buffer = await docx.Packer.toBuffer(doc); fs.writeFileSync("word测试文档.docx", buffer); pbottleRPA.openDir(pbottleRPA.__dirname) //读取word文档 pbottleRPA.tts(`将后台读取 word 文件 显示到日志`) pbottleRPA.wait(3) let rs = await mammoth.extractRawText({path:"./word测试文档.docx"}) console.log('读取word文档内容:',rs.value); ================================================ FILE: docs/-图片测试.md ================================================  ================================================ FILE: docs/.vitepress/config.mjs ================================================ import { defineConfig } from 'vitepress' // https://vitepress.dev/reference/site-config export default defineConfig({ title: "小瓶 RPA 官方文档", description: "小瓶RPA,专业用户的专业RPA软件。轻量级简单全能的RPA软件,浏览器自动化增强,显著降本增效 & 工作100%准确 & 非侵入式集成。同时支持浏览器web应用和客户端应用的操作流程自动化。同时支持 Js 和 Python 两种脚本制作流程。", lang: 'zh-Hans', lastUpdated: true, base: '/docs/', // outDir: './html', locales: { root: { label: '中文', lang: 'zh-Hans' }, en: { label: 'English', lang: 'en', // 可选,将作为 `lang` 属性添加到 `html` 标签中 link: 'https://officetool.online/pbottle-rpa/docs' // 默认 /en/ -- 显示在导航栏翻译菜单上,可以是外部的 } }, sitemap: { hostname: 'https://rpa.pbottle.com/docs/' }, head: [ ['link', { rel: 'shortcut icon', href: '/rpa.ico', type: 'image/x-icon' }], ['meta', { name: 'keywords', content: '小瓶RPA, 专业RPA, 文档, 流程文档, 官方文档, RPA文档 ,机器人流程自动化文档' }], ['script', { type: 'text/javascript' }, ` var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?4f28c271b56a4be165f2b8200a58c47c"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); ` ], // ['script', { // async:'async', // crossorigin:"anonymous", // src:"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8054907953581959", // } // ], ], themeConfig: { // https://vitepress.dev/reference/default-theme-config docFooter: {// 文章翻页 prev: '上一篇', next: '下一篇' }, darkModeSwitchLabel: '外观', // 移动端 - 外观 returnToTopLabel: '返回顶部', // 移动端 - 返回顶部 sidebarMenuLabel: '菜单', // 移动端 - menu outline: { label: '本页大纲', level: [2, 6] }, nav: [ { text: '使用指南', link: '/' }, { text: '功能模块API', link: '/API鼠标操作' }, { text: '官网下载', link: 'https://rpa.pbottle.com/' }, { text: '在线客服', link: 'https://yun.pbottle.com/kefu.php' }, { text: '技术交流群', link: 'https://gitee.com/pbottle/pbottle-rpa/raw/master/input/discuss.jpg' }, ], sidebar: [ { text: '使用指南', items: [ { text: '开始', link: '/index' }, { text: 'Demo示例', link: '/Demo示例' }, { text: '视频教程', link: '/视频教程' }, { text: '用 js 脚本开发自动化流程', link: '/用 js 脚本开发自动化流程' }, { text: '用 python 脚本开发自动化流程', link: '/用 python 脚本开发自动化流程' }, { text: '中文调用', link: '/中文调用' }, { text: 'AI生成流程脚本 🔥', link: '/AI生成流程脚本' }, { text: '流程运行日志', link: '/流程运行日志' }, { text: '定时启动', link: '/定时启动' }, { text: '开机启动', link: '/开机启动' }, { text: '流程录制', link: '/流程录制' }, { text: '热键和快捷方式', link: '/热键和快捷方式' }, { text: '手机应用的自动化', link: '/手机应用的自动化' }, { text: 'win7 操作系统', link: '/win7操作系统' }, { text: '信创操作系统 🔥', link: '/信创操作系统' }, { text: 'Q&A 常见问答', link: '/Q&A' }, ] }, { text: '功能模块API', items: [ { text: '统一规范', link: '/API统一规范' }, { text: '鼠标操作模拟', link: '/API鼠标操作' }, { text: '键盘操作模拟', link: '/API键盘操作' }, { text: '系统 & 文件', link: '/API系统相关' }, { text: '屏幕画面', link: '/API屏幕' }, { text: '声音', link: '/API声音' }, { text: '网络', link: '/API网络' }, { text: '压缩解压', link: '/API压缩解压' }, { text: '用户输入', link: '/API用户输入' }, { text: '通用工具 Utils', link: '/API通用工具' }, { text: 'AI图像(本地)', link: '/APIAI图像' }, { text: 'AI大模型(云模块)', link: '/APIAI大模型' }, { text: '浏览器增强 🔥', link: '/API浏览器增强' }, { text: '办公文档', link: '/API办公文档' }, { text: '外部控制', link: '/API外部控制' }, { text: '键鼠硬模拟 hid', link: '/API键鼠硬模拟' }, { text: '其他(数据库、Excel等)', link: '/其他功能模块' }, ] }, { text: '高级指南', items: [ { text: '流程配置项', link: '/流程配置项' }, { text: '验证码自动化', link: '/验证码自动化' }, { text: '老旧低配电脑', link: '/老旧低配电脑' }, { text: '桌面快捷方式', link: '/桌面快捷方式' }, { text: 'HTTP静态服务', link: '/HTTP静态服务' }, { text: '无尽模式', link: '/无尽模式' }, { text: '硬件级键鼠模拟', link: '/硬件键鼠模拟' }, { text: '集群控制中心 🔥', link: '/集群控制中心' }, { text: 'SaaS系统自动化能力', link: '/SaaS系统自动化任务' }, ] }, { text: '其他开发服务', items: [ { text: '业务管理系统(ERP)', link: '/业务管理系统' }, { text: '独立软件定制', link: '/专用自动化独立软件' }, ] } ], socialLinks: [ { icon: 'gitee', link: 'https://gitee.com/pbottle/pbottle-rpa' }, { icon: 'github', link: 'https://github.com/leoxiaoping/pbottleRPA' } ], editLink: { pattern: 'https://gitee.com/pbottle/pbottle-rpa/tree/master/docs/:path', text: '编辑本页内容' }, footer: { message: `⭐⭐⭐⭐⭐`, copyright: 'Copyright © 2019-present 小瓶科技' } } }) ================================================ FILE: docs/AI生成流程脚本.md ================================================ # AI 对话大模型 生成小瓶RPA流程脚本 得益于小瓶RPA脚本层开源开放政策,小瓶RPA能够轻松用主流AI编程助手快速生成高质量流程脚本。 此功能目前只做参考和测试用途。 ## 小瓶RPA助手智能体 直接访问小瓶RPA助手智能体agent:(官方知识库已经更新) https://yuanqi.tencent.com/webim/#/chat/yoxhoN?appid=2021155473084382336 ``` https://yuanqi.tencent.com/webim/#/chat/yoxhoN?appid=2021155473084382336 ``` ## AI平台(beta) #### 打开AI平台 以豆包编程助手为例 https://www.doubao.com/ #### 引入小瓶RPA开源库 - 点击底部编程-》引入开源仓库  - 复制下面地址,输入并确认 ``` https://github.com/leoxiaoping/pbottleRPA ``` #### 开始对话 输入对话提问例子测试 ``` 生成js流程脚本:打开多个网址并按 ctrl+d 收藏这些网址 ``` ================================================ FILE: docs/APIAI图像.md ================================================ # 本地 AI 图像 首先在软件设置中开启本地AI识别功能模块,然后重启软件生效。  ## aiOcr 文字识别 @param {string} imagePath 空或者screen 为电脑屏幕; 或者本地图片的绝对路径; @param {number} x 可选 剪裁起始点 左上角开始 @param {number} y 可选 剪裁起始点 @param {number} width 可选 剪裁宽度 @param {number} height 可选 剪裁高度 @returns {array} AI OCR识别的json结果 包含准确率的评分和中点位置 格式: `[{text:'A',score:'0.319415',x:100,y:200},...]`  ## findText 寻找文字 findText 屏幕查找定位文字 查找文字,注:此功能受电脑性能影响,低配电脑可能速度较慢。 需要小瓶RPA客户端版本 > V2024.5 @param {string} inputTxt @param {number} fromX=0 可选,查找开始的开始横坐标 @param {number} fromY=0 可选,查找开始的开始纵坐标 @param {number} width=-1 可选,搜索宽度 @param {number} height=-1 可选,搜索高度 @returns {JSON} 返回json结果:{x,y,text} x,y坐标相对于fromX,fromY。 ## aiObject 物体识别 AI 物体识别 已经从经典算法升级为AI模型预测,企业版可脱网使用 V2024.8 以上版本有效 调试:软件根目录会生成 debug/Ai_ObjectDetect.png 文件 @param {number} imagePath 空或者screen 为电脑屏幕; 或者本地图片的绝对路径; @param {number} x 可选 剪裁起始点 左上角开始 @param {number} y 可选 剪裁起始点 @param {number} width 可选 剪裁宽度 @param {number} height 可选 剪裁高度 @returns {array} AI 物体识别的 json 结果 包含准确率的评分 格式: `[{x:100,y:100,width:150,height:150,score:0.86,class:'分类名'},...]`  ================================================ FILE: docs/APIAI大模型.md ================================================ # AI 大模型(云模块) 小瓶RPA 云端模块,AI在线大模型  1. 此模块不是必须模块 ,云端模块不影响本地模块的独立运行 2. 此模块功能需要登录并激活云端模块。碍于成本因素,部分功能需要充值计费才能使用 调用方式:pbottle.cloud.XXX() token定价:https://rpa.pbottle.com/a-14065.html ## 大语言模型GPT 小瓶RPA整合的云端大语言答案生成模型 @param {string} question 提问问题,如:'今天是xx日,你能给我写首诗吗?' @param {number} modelLevel 模型等级,不同参数大小不同定价,默认 0 为标准模型。0为低价模型;1为性价比模型;2为旗舰高智能模型; @param {string} response_format 云端模型输出格式,默认:"text",可选 "json_object" JSON格式 @returns {Answerinfo} JSON内容格式 `{content:'结果',tokens:消耗token的数量}` 示例:GPT问题答案AI生成演示.js ## 图像大模型 cloud_GPTV 小瓶RPA整合的云端图像分析大模型 @param {string} question 提问问题,如:'分析这个图片的内容' @param {string} imagePath 上传图片的路径,如:'c:/test.jpg' @param {number} modelLevel 模型等级,不同参数大小不同定价,默认 0 为标准模型。 @returns {Answerinfo} JSON内容格式 `{content:'结果',tokens:消耗token的数量}` 示例:GPT图像解析示例.js ================================================ FILE: docs/API办公文档.md ================================================ # office 办公文档 ## 常规模拟操作 办公文档的操作一般依赖常用的办公软件:微软office、金山wps 等, 小瓶 RPA 可以通过模拟操作鼠标键盘操作这些软件。 ## 后台读写快捷模式 1. Excel 文档:参考示例 [第三方] 读写Excel演示脚本.mjs 2. Word 文档:参考示例 [第三方] 读写word演示脚本.mjs 注意:请先双击 [第三方 模块安装].bat 安装模块。 ================================================ FILE: docs/API压缩解压.md ================================================ # 压缩解压缩 新版压缩解压接口支持 4GB 以上超大文件 ## 压缩 zipDir 压缩文件夹内容成一个zip文件包 v2025.0 以后版本生效 @param {string} directory 文件夹路径,输入绝对路径 @param {string} zipFilePath zip文件包 ## 解压缩 unZip 解压缩zip文件内容到指定文件夹内 v2025.0 以后版本生效 @param {string} zipFilePath zip文件包 @param {string} directory 文件夹路径,输入绝对路径 默认解压到zip文件当前目录 ================================================ FILE: docs/API声音.md ================================================ # 声音 ## beep 蜂鸣声 发出系统默认提示音 ## tts 文字转语音 从文本到语音(TextToSpeech) 语音播报 非阻塞 @param {*} text 朗读内容 ================================================ FILE: docs/API外部控制.md ================================================ # 外部控制 除了实现软件操作自动化外,外部控制功能够实现小瓶RPA自身操作的自动化。 外部控制提供一系列控制小瓶RPA本身的 **http 接口** ,可以由 第三方系统 或者 RPA集群控制器 发出。 外部控制功能只对企业版开放 ,详细查看小瓶RPA软件授权:https://rpa.pbottle.com/License.php ## 外部启动流程 从外部控制启动小瓶RPA开始某项自定义的任务 用法: http://ip:49888/?action=pbottleRPA_run&path=流程脚本路径 - 成功启动任务返回:ok - 上一个任务还未结束:isRunning ## 外部停止流程 从外部控制停止小瓶RPA正在运行的任务 用法: http://ip:49888/?action=pbottleRPA_stop ## 当前运行状态 从外部控制查询小瓶RPA正在运行的状态 用法: http://ip:49888/?action=pbottleRPA_state - 未运行状态返回:ready - 正在运行状态返回:isRunning ## 外部控制buffer存取 buffer 是小瓶RPA的跨脚本的全局状态变量的存取管理机制。 #### bufferSet 命令 设置buffer存储内容 此buffer可以跨脚本存取,RPA重启时才重置,存取多线程下安全 @param {*} n buffer编号,从0-9共10个 默认:0 第一个buffer @returns ok 表示成功 (POST方法):http://ip:49888/action=bufferSet&n=0 ,content设置到Post的body中,通常为json的字符串 #### bufferGet 命令 获取buffer存储内容 此buffer可以跨脚本存取,RPA重启时才重置,存取多线程下安全 http外部获取方式:http://ip:49888/action=bufferGet&n=0 @param {*} n buffer编号,从0-9共10个 默认:0 第一个buffer @returns 字符串 http://ip:49888/action=bufferGet&n=0 ## 外部设置定时任务 从外部控制修改小瓶RPA的计划任务(定时器) 用法: http://ip:49888/?action=pbottleRPA_plan&plan=* * 定时器规则参考: https://www.pbottle.com/a-13868.html ## 外部获取设备唯一号 外部获取当前设备RPA唯一号 用法: http://ip:49888/?action=pbottleRPA_deviceID 多用于商业加密脚本的分发控制和验证 ## 外部获取运行日志 外部获取最后一个任务的运行日志 用法: http://ip:49888/?action=pbottleRPA_lastLog 通常用来检查详细的运行过程 --- 获取倒数第二个运行日志 `http://ip:49888/?action=pbottleRPA_lastLog2` ## delaySet 设置接力任务 `pbottle.delaySet(scriptPath)` 设置接力执行的脚本 当前脚本结束后(无论正常结束还是错误退出),立刻启动的自动脚本。 http外部设置方式(GET方法):http://ip:49888/action=pbottleRPA_delay&path=MyPATH @param {string} scriptPath 接力脚本的路径 如:'D:/test.mjs' 如果路径为空,默认清除当前已经设置的接力任务。 @returns {string} ok 表示成功 - 监督任务执行 A 任务是一个复杂的长流程,在执行过程中各种问题和错误,导致一定概率不能顺利玩成到任务最后。 这时候在A任务开头设置接力任务B,B 作为 A 的监督员,可以在 A 任务退出时候,验证 A 的关键指标完成。根据验证结果做出通知人工,甚至重启A任务的流程。 ================================================ FILE: docs/API屏幕.md ================================================ # 屏幕画面 ## getResolution 获取屏幕分辨率 获取当前屏幕分辨率, ratio 为桌面缩放比例 @returns JSON 内容格式 `{ w:1920,h:1080,ratio:1.5 }` ## screenShot 屏幕截图 @param {*} savePath 保存路径默认 我的图片,图片格式为PNG;如果使用自定义路径请以 '.png' 结尾; @param {*} x 截图开始位置 @param {*} y @param {*} w 截图宽度 @param {*} h 截图长度 ## getScreenColor 获取屏幕颜色 屏幕一个点取色 @param {*} x @param {*} y @returns 返回颜色值 ## findScreen 寻找图像 屏幕查找图象定位 @param {string} tpPath 搜索的小图片,建议png格式 相对路径 @param {number} miniSimilarity 可选,指定最低相似度,默认0.9。取值0-1,1为找到完全相同的。 @param {number} fromX=0 可选,查找开始的开始横坐标 @param {number} fromY=0 可选,查找开始的开始纵坐标 @param {number} width=-1 可选,搜索宽度 @param {number} height=-1 可选,搜索高度 @returns 返回找到的结果json 格式:`{x,y}` ## waitImage 等待图像出现 等待屏幕上的图片出现 @param {string} tpPath 图片模板路径 相对路径:./image/123.png @param {Function} intervalFun 检测间隔的操作,function格式 @param {number} timeOut 等待超时时间 单位秒 @returns {position|boolean} 结果的位置信息,json格式:`{x,y}` #### 调试 等待图片超时情况,小瓶RPA会立刻全屏截图并保存到 `电脑-》我的图片` 供后续判断排查。 ## findContours 寻找轮廓 @param {number} minimumArea 轮廓最小面积 默认过滤掉 10x10 以下的元素 @param {number} fromX 开始坐标 @param {number} fromY @param {number} width 作用范围 @param {number} height @returns {array} 所有查找到的轮廓信息,包含闭合区域的起始坐标,中点坐标,面积,id。 格式:`[{ x: 250, y: 10, cx: 265.5, cy: 30.5, area: 2401, id: 42 },...]` 屏幕查找物体或者窗口轮廓 ## imgSimilar 图片相似度对比 图片相似度对比 需要小瓶RPA客户端版本 > V2025.3 @param {string} path1 图片1路径 @param {string} path2 图片2路径 @param 'SIFT' | 'ORB' | 'SSIM' checkType 对比算法 默认 'ORB' @returns {score:number, time:number} score相似度分数 0-1 ; time耗时秒 #### 调试 软件 home 目录会生成 debug/findContours.png ================================================ FILE: docs/API浏览器增强.md ================================================ # 浏览器增强 - web应用 **⚠ 浏览器插件只是一种web浏览器页面操作的快捷操作方式,不是必须。按照小瓶RPA桌面应用的操作规则一样可以操作web浏览器应用。** 需要先安装小瓶RPA浏览器插件,安装方法查看: https://rpa.pbottle.com/a-13942.html 基础调用方式: `pbottleRPA.browserCMD.xxx()` 可以参考 “web增强“ 开头的demo示例。 ## 元素选择器 小瓶RPA web增强基本遵循已经被广泛使用 **jQuery选择器** 的设计规则,可以参考jquery的选择器文档。 https://www.runoob.com/jquery/jquery-ref-selectors.html 相比 xpath 等方案优势: - 学习成本低,规则和浏览器的 document.querySelector 原生方法一致 - 兼容性强,支持虚拟DOM的前端框架,如:Vue React 等 - 支持功能丰富且强大的伪类方案,可以高级选择定位 ```javascript pbottleRPA.browserCMD_click('button:contains(登录帐号)') ``` ## 元素选择器测试工具 新版小瓶RPA增加了元素选择器测试功能,本功能旨在帮助用户在复杂网页中快速定位和选择html的元素。  **V2025.2 新增选中元素背景闪烁功能,方便用户查看元素位置。** 快速复制元素选择器步骤: 1. 鼠标放到浏览器目标元素上,F12 或者 右键菜单“检查元素” 2. 目标元素代码块上,右键弹出复制菜单 3. 选择 复制selector 【复制选择器】  ## alert 警告框 browserCMD_alert() 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 警告框 @param {*} msg 显示文本内容 @returns 无 ## url 获取|设置当前url browserCMD_url() 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 @param {string} urlStr 当前网页转向新网址,默认为空获取当前网址 【小瓶RPA浏览器增强插件V2023.8以上生效】 @returns {string} 返回当前浏览器的url网址 或者 ok ## click 点击 browserCMD_click() 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 模拟点击 参考 jQuery click() 方法,改为浏览器 native 的 click() 并自动获取焦点 @param {string} selector 元素选择器。如果选择多个元素,只触发第一个元素的click事件 @param {object} options 点击选项 可选 如:{ bubbles: false, ctrlKey: true} https://developer.mozilla.org/zh-CN/docs/Web/API/MouseEvent/MouseEvent @returns {string} ## dblclick 双击 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 V2026.2 以上版本支持 模拟双击 参考 jQuery dblclick() 方法,改为浏览器 native 的 click() 并自动获取焦点 @param {string} selector 元素选择器。如果选择多个元素,只触发第一个元素的click事件 @param {object} options 点击选项 可选 如:{ bubbles: false, ctrlKey: true} https://developer.mozilla.org/zh-CN/docs/Web/API/MouseEvent/MouseEvent @returns {string} ## closeTab 关闭标签页 browserCMD_closeTab('current'|'other') V2025.4 以上版本支持。 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 关闭浏览器标签页。打开新标签页用 pbottleRPA.openURL() @param {string} 关闭类型 'current':默认关闭当前标签页; 'other':关闭其他标签页 @returns {string} 正常返回 'ok' ## count 元素计数 browserCMD_count() 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 元素数量 参考 jQuery 选择器 @param {string} selector 元素选择器 @returns {number} 返回选择元素的数量,最优的选择结果是1 ## hide 隐藏元素 browserCMD_hide() 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 隐藏 参考 jQuery hide() 方法 @param {*} selector 元素选择器 @returns ## show 显示元素 browserCMD_show() 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 显示 参考 jQuery show() 方法 @param {*} selector 元素选择器 @returns ## offset 获取元素位置 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 2024.0 以上版本生效 获取元素定位,相对浏览器文档左上角 参考 jQuery offset() 方法 @param {string} selector 元素选择器 @returns {} 返回 json:`{"top":100,"left":100}` 像素值为软件像素,非显示器硬件像素 ## remove 移除元素 browserCMD_remove() 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 移除元素 参考 jQuery remove() 方法 @param {*} selector 元素选择器 ## text 获取|设置文本 browserCMD_text 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 获取或者设置文本 参考 jQuery text() 方法 @param {*} selector 元素选择器 @param {*} content @returns ## html 设置|获取代码 browserCMD_html() 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 获取或者设置html 参考 jQuery html() 方法 @param {*} selector 元素选择器 @param {*} content @returns ## val 获取|设置值 browserCMD_val() 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 获取或设置值 input select等 参考 jQuery val() 方法 @param {*} selector 元素选择器 @param {*} content @returns ## cookie 获取|设置 小存储 browserCMD_cookie() 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 获取或设置当前站点的 cookie @param {*} cName cookie 名称 @param {*} cValue cookie 值 留空为获取cookie的值 @param {*} expDays cookie 过期时间,单位:天, 留空为会话cookie @returns 返回 cookie的值 ## css 获取|设置样式 browserCMD_css() 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 获取或设置css样式 参考 jQuery css() 方法 @param {*} selector 元素选择器 @param {*} propertyname @param {*} value @returns ## attr 获取|设置属性 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 获取或设置attr样式 参考 jQuery attr() 方法 @param {*} selector 元素选择器 @param {*} 属性名 @param {*} value ## prop 获取|设置prop 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 获取或设置prop样式 参考 jQuery prop() 方法 @param {*} selector 元素选择器 @param {*} 属性名 @param {*} value ## fetch 网络请求 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 V2026.2 以上版本支持 fetch请求网址,从当前页面发起ajax请求并返回响应结果 https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch 默认 20 秒超时 @param {string} fetch_url 网址 @param {object} options 请求参数 @returns {string} 响应结果 ## waitPageReady 监听页面加载完成 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 V2026.2 以上版本支持 等待页面加载完成,返回页面网址 默认 20 秒超时 @param {string} readyURL 页面加载完成后的网址 @param {number} timeout 超时时间,单位秒 @returns {string} 返回当前浏览器的url网址 或者错误退出 ## 批量采集获取网页内容 当选择器结果为多个元素时候,会一次性返回所有内容,内容格式为:JSON数组。 注意:V2025.3 以上版本支持。 批量采集获取网页内容目前支持方法: - text() - html() - val() - attr() 参考示例:WEB增强-数据批量爬取演示.js ================================================ FILE: docs/API用户输入.md ================================================ # 用户输入 小瓶RPA流程执行中,流程可以暂停等待用户可以手动输入数值或者选项。 输入后流程继续执行。 ⚠ V2026.0 以上版本基座支持用户输入。 ## 等待输入 waitInput * 等待输入 V2026.0.0 新增 * @param {string} inputPrompt 输入提示词 * @param {number} timeOut 可选,等待超时时间 单位秒 默认600秒 * @returns {string} 输入内容 默认返回空字符串 ### 图片预览  ### demo示例 用户手动输入变量示例.js ================================================ FILE: docs/API系统相关.md ================================================ # 系统相关 ## wait 等待 脚本暂停等待操作响应 (秒) 注意:一次等待超过100s, 会有日志提示 @param {number} seconds 秒, 缺省值为 1 秒。支持小数。 ## setDefaultDelay 设置默认操作延时 设置RPA模拟操作的延时 包含鼠标、键盘、粘贴、打开网页操作 设置为 0 可以用 sleep() 手动管理操作延时 @param {*} millisecond 毫秒单位的数字 ## jsPath 目录路径 变量名称: 当前脚本的文件夹路径,不带最后斜杠 别名:__dirname 方便在 .mjs 中使用 ## getBasePath 基座路径 获取并设置基座平台的根目录路径 | V2025.0 以上版本启用 @returns {string} ## showMsg 显示系统消息 @param {*} title 标题 @param {*} content 内容 本信息提示框为操作系统原生提示框  系统设置  ## showRect 显示标记框 有效屏幕内显示一个彩色方框,直观提示流程操作范围和目标的当前的定位 V2024.6以上版本有效 @param {number} fromX 起始位置xy坐标,屏幕左上角为零点 @param {number} fromY @param {number} width 宽度 @param {number} height 高度 @param {string} color 颜色 红绿蓝黄4色可选:red|green|blue|yellow @param {number} msec 显示持续时间 单位毫秒 示例脚本:朋友圈点赞,截屏,文字识别 ## openFile 打开文件 用默认应用打开文件 如word、excel、pdf等 @param {*} path 文件路径 ## openDir 打开目录 用资源管理器打开展示文件夹 @param {*} path 文件夹路径 ## kill 关闭软件 (强行)关闭指定软件 @param {string} processName 进程名称,如:'WINWORD.EXE' 任务管理器 ‘进程名称’ 栏目 。注意不是 名称,如不显示,右键勾选显示这一栏目即可 @param {boolean} force 是否强制,相当于模拟任务管理器的结束任务操作。默认普通关闭,可能跟随保存确认框 ```javascript pbottleRPA.kill('WINWORD.EXE') //关闭word pbottleRPA.kill('EXCEL.EXE') //关闭word pbottleRPA.kill('msedge.exe') //关闭edge浏览器 ``` ## copyText 复制文字 模拟复制文字,相当于选择并复制文本内容 @param {string} txt 复制的文本内容 ## copyFile 复制文件 模拟复制文件操作,支持文件路径和文件夹路径,复制后在目标文件夹ctrl+V 即可粘贴 V2024.7开始生效 复制文件后,在微信发送窗口粘贴,即可发送文件 @param {string} filepath 绝对路径 ## exit 退出流程 强制退出当前脚本 @param {string} msg 退出时候输出的信息 ## log 日志输出 @param {string} text 输出日志 ## waitFile 等待文件 等待文件下载成功或者生成 @param {string} dirPath 监控文件夹目录 如:'c:/User/pbottle/download' @param {string} keyWords 过滤关键词 如:'.zip' @param {function} intervalFun 检测间隔的操作,function格式 @param {number} timeOut 等待超时时间 单位秒 ================================================ FILE: docs/API统一规范.md ================================================ # API统一规范定义 ## 屏幕坐标 以屏幕左上角为原点(坐标为 (0, 0)),水平向右为 x 轴正方向,垂直向下为 y 轴正方向。在这种坐标系中,屏幕上任意一点都可以通过一个 (x, y) 的坐标值来表示,其中 x 表示该点距离原点在水平方向的距离,y 表示在垂直方向的距离,单位通常是像素。 注意:在有缩放的屏幕上时,其中的像素单位是**物理像素**,不是软件像素。  #### 推荐截图和测量工具 snipaste 截图软件自带坐标测量。 下载地址: https://zh.snipaste.com/download.html #### 分辨率测量工具 [https://www.pbottle.com/a-13812.html](https://www.pbottle.com/a-13812.html) ## 统一路径格式 小瓶RPA脚本默认路径格式分隔符为 `/`,包含 windows 系统。 windows 系统路径规则统一到 Linux 系统路径规则。 举例: 1. 绝对路径 `D:/myPath/test.png` 2. 相对路径 `./test.png` ================================================ FILE: docs/API网络.md ================================================ # 网络 流程同步网络方法 ## openURL 打开网址 用电脑默认浏览器打开网址 @param {string} myurl 网址 示例:快速开始脚本 ## getHtml 请求网址 (同步方法) 普通请求网址,获取返回的html文本 @param {string} url 网络地址 get方法 @param {object} headersJson 请求头 Json对象 @returns {string} 返回的文本 ## postJson 提交json 向指定API网址post一个json,最常用网络接口方式 @param {string} url API网络地址 @param {object} msgJson Json对象 @param {object} headersJson 请求头 Json对象 @param {string} method e.g. GET, POST, PUT, DELETE or HEAD @returns {string} 示例:运维消息手机通知.js ## postJsonFile 提交json文件 向指定API网址post一个json文件,适合大型json内容 @param {string} url API网络地址 @param {string} msgJsonFile Json文件路径 @param {object} headersJson 请求头Json对象 @param {string} method e.g. GET, POST, PUT, DELETE or HEAD @returns {string} 示例:GPT图像解析示例.js ## downloadFile 下载文件 从网络下载一个文件到本地路径 @param {string} fileUrl 网址 @param {string} filename 本地路径文件名 @param {object} headersJson 请求头 Json对象 ## wxMessage 微信消息发送(过期废止) 通知到手机 通过小瓶云发送微信通知 (微信到达率高,并且免费) @param {string} name 消息标题 @param {string} content 消息详细内容 @param {string} key 获取key详情方法:https://www.pbottle.com/a-12586.html ## sendMail 发送邮件 * 发送邮件;注意这个方法是个异步方法,请参考示例; * @param {string} to 收件人地址 * @param {string} subject 邮件主题 * @param {string} content 邮件内容;文本文件,换行用 '\n' * @param {string} host 服务器地址(如:smtp.qq.com) * @param {number} port 服务器端口 默认是465 * @param {string} user 认证信息(用户名)一般也是发送邮件地址 * @param {string} pass 认证信息(密码) * @returns 示例:发送Email电子邮件.js ================================================ FILE: docs/API通用工具.md ================================================ # pbottleRPA.utils 工具箱 提供基础常用便利工具 调用方式: pbottleRPA.utils.xxx() demo示例: 常用工具 Utils 演示.js ## 获取格式化时间 getTime utils.getTime() 格式化的时间 getTime('Y-m-d H:i:s') 输出类似 "2023-09-17 14:30:45" 的日期时间字符串 @param {string} format 格式参考 https://www.runoob.com/php/php-date.html 仅支持 Y|y|m|d|H|i|s|n|j @param {number} timestamp 时间戳秒 @returns {string} ## 唯一数 uniqid utils.uniqid() 生成唯一符串 注意:默认只是毫秒级的 @param {string} prefix 前缀 @param {boolean} moreEntropy 是否开启更精细的随机,如果还不能满足请使用uuid @returns {string} ## 是否数字 isNumeric utils.isNumeric() 判断是否为数字化变量(包含数字化的字符串) @param {*} value 任意类型变量 @returns {boolean} ## 是否有内容 hasData utils.hasData() 判断变量中是否有数据,直接if()可用。 非零数字 或 非空字符串、数组、对象 返回 true,其他都返回 false @param {*} value 任意类型变量 @returns {boolean} ## 搜索文件 searchFile utils.searchFile() 根据关键字搜索定位具体文件 @param {string} directory 绝对路径 @param {string} words 文件名包含的关键字,过滤词,默认忽略大小写 @returns {string[]} 文件路径 || [] 未找到 ================================================ FILE: docs/API键盘操作.md ================================================ # 键盘模拟操作 ## keyToggle 键盘基础触发 模拟按键触发事件 @param {*} key 按键名称参考:https://www.pbottle.com/a-13862.html @param {*} upDown 默认按下down,up松开按键 ## keyTap 键盘按键 按一下键盘 支持组合按键 加号连接 如: keyTap('ctrl + a') @param {*} key 按键名称参考:https://www.pbottle.com/a-13862.html ## paste 粘贴输入 当前位置 粘贴(输入)文字 @param {*} text ## getClipboard 获取剪切板内容 获取当前电脑的剪切板内容,系统剪切板支持多种格式 版本 V2024.2 开始生效 - 纯文本格式:普通复制 如'小瓶RPA' - 图片格式 base64形式:浏览器复制图片 'data:image/png;base64,' 开头 - html格式:浏览器或者钉钉复制富文本综合内容 ''开头 @returns 结果文本 ================================================ FILE: docs/API键鼠硬模拟.md ================================================ # 键鼠硬模拟 API 小瓶RPA硬件增强不是必选项,需要购买额外的硬件,建议只有需要系统级模拟操作时候才启用。 开启硬件键盘鼠标模拟,不影响默认的软件键盘鼠标模拟。 功能起始版本:V2024.3 本功能只对企业版开放 ,详细查看小瓶RPA软件授权:https://rpa.pbottle.com/License.php ## pbottle.hid.XXX 接口集 调用方式:pbottle.hid.XXX() ## hid.keyToggle * 模拟按键触发事件 (硬件级) * @param {string} key 按键名称参考:https://www.pbottle.com/a-13862.html * @param {string} upDown 默认按下down,up松开按键 ## hid.keyTap * 按一下键盘(硬件级) 支持组合按键 加号连接 如: keyTap('ctrl + alt + del') * @param {string} key 按键名称参考:https://www.pbottle.com/a-13862.html ## hid.mouseCMD * 基础鼠标命令 全部为零释放 * @param {number} button 按键 1,2,4 代表鼠标的 左键,右键,中键。 * @param {number} x 按键时候移动的位置,绝对位置 x=100:向右移动 100像素,负数向左 * @param {number} y 按键时候移动的位置,拖拽相对位置 y=100:向下移动 100像素,负数向上 * @param {number} mouseWheel 滚动齿轮数 正数向下,负数向下 * @param {number} time 按下到释放时间 ## hid.moveMouse * 移动鼠标到指定位置 起点为屏幕左上角 屏幕绝对位置(硬件分辨率) * @param {number} x 横坐标 * @param {number} y 纵坐标 ## hid.mouseClick * 当前位置点击鼠标 默认左键 * @param {string} 鼠标的按键选择 left right middle 可选 ,默认左键 * @param {number} 点按时间 单位毫秒 可选 ## hid.moveAndClick * 移动鼠标到指定位置并点击 * @param {number} x 横坐标 * @param {number} y 纵坐标 ## hid.mouseDoubleClick 双击鼠标 左键 ## hid.mouseLeftDragTo * 鼠标左键拖到一段位置 * @param {number} x 位置 * @param {number} y 位置 ## hid.mouseRightDragTo * 鼠标左键拖到一段位置 * @param {number} x 位置 * @param {number} y 位置 ## hid_mouseWheel * 鼠标滚轮 * @param {number} data 滚动的量 默认为-1 向下滚动一个齿轮; 正数向上滚动; ## 参考示例 HID硬件级键盘鼠标演示.js ================================================ FILE: docs/API鼠标操作.md ================================================ # 鼠标操作模拟 ## moveMouse 鼠标移动 移动鼠标到指定位置并点击 起点为屏幕左上角 @param {number} x 横坐标 @param {number} y 纵坐标 @param {number} interval 像素间隔时间,越大移动越慢 毫秒单位,默认:0 ## moveAndClick 鼠标移动并点击 移动鼠标到指定位置并点击 起点为屏幕左上角 @param {number} x 横坐标 @param {number} y 纵坐标 ## mouseClick 鼠标点击 当前位置点击鼠标 默认左键 可选 'right' @param {*} leftRight 可选 @param {*} 点按时间 单位毫秒 可选 ## mouseDoubleClick 鼠标双击 双击鼠标 默认左键 ## mouseWheel 鼠标滚轮 鼠标滚轮 @param {*} data 滚动的量 默认为-720 向下滚动720度 ## mouseLeftDragTo 鼠标左键拖动 鼠标左键拖到指定位置 @param {*} x @param {*} y ## mouseRightDragTo 鼠标右键拖动 鼠标右键拖到指定位置 @param {*} x @param {*} y ================================================ FILE: docs/Demo示例.md ================================================ # Demo示例 小瓶RPA提供多个基础功能的demo示例,方面大家参考。具体API的使用可以参考demo,所有api接口都有demo示例。 Demo示例为 JavaScript 和 python 脚本语言,符合完整 Nodejs NPM 和 python pip 项目规则。 **官方推荐 JavaScript 版本** ## Demo运行条件 1. 安装运行小瓶RPA基座程序 2. 安装脚本引擎 3. 下载运行示例脚本 ## Demo示例完整教程 [https://rpa.pbottle.com/a-14019.html](https://rpa.pbottle.com/a-14019.html) ================================================ FILE: docs/HTTP静态服务.md ================================================ # 集成HTTP静态服务 小瓶RPA 集成了HTTP静态服务,可以快速集成静态页面,实现自动化测试、下载文件、H5页面展示等功能。 ⚠ 本功能V2026.1以上版本有效。 ## 静态文件本地目录 小瓶RPA主菜单 设置-》打开数据目录 静态文件存放目录: `[数据目录]/static` 例如:`[数据目录]/static/test.html` ## 访问网址 `http://127.0.0.1:49888/static/` 例如:`http://127.0.0.1:49888/static/test.html` ================================================ FILE: docs/SaaS系统自动化任务.md ================================================ # SaaS系统自动化任务 小瓶RPA可以为现有SaaS系统服务,升级提供自动化任务服务中心。 ## 优势 - SaaS 系统无缝平台升级改造 - SaaS 品牌用户端保持 - 本地批量集中执行,低成本 - 扩充现有 SaaS 现有自动化任务能力 ## 架构示意图  ================================================ FILE: docs/index.md ================================================ --- next: text: 'Demo示例' link: 'Demo示例.html' --- # 开始使用小瓶RPA **小瓶RPA,专业用户的专业RPA+AI软件。** 长难业务自动化流程专精,轻量级简单全能的RPA软件,显著降本增效 & 工作100%准确 & 非侵入式集成。同时支持浏览器web应用和客户端应用的操作流程自动化。同时支持 Js 和 Python 两种脚本制作流程。 下载官网:https://rpa.pbottle.com/  ## 小瓶RPA专业版的优势 1. RPA自动化优势、AI优势和传统开发生态优势三位一体。🤖 2. 超轻量级 + 开放接口能力,无缝整合现有工作系统,而非高成本替代。📗 3. 纯视觉模拟驱动,可以兼容操作所有应用程序和所有操作系统。🖥️ 4. 系统硬件级操作模拟, 可以完成所有系统级高权限模拟操作。⌨️ ## 自动流程入门示例 ```javascript const pbottleRPA = require('./pbottleRPA') //引入小瓶RPA nodejs模块 pbottleRPA.openURL('https://www.baidu.com/') // 用浏览器打开百度 pbottleRPA.paste('小瓶RPA官网') //输入搜索词 pbottleRPA.keyTap('enter') //确认搜索 ``` ```python import pbottleRPA #引入小瓶RPA python模块 pbottleRPA.openURL('https://www.baidu.com/') #用浏览器打开百度 pbottleRPA.paste('小瓶RPA官网') #输入搜索词 pbottleRPA.keyTap('enter') #确认搜索 ``` ## 系统架构图  ## 小瓶RPA基座界面 和 浏览器插件界面   ## 软件授权温馨提示 未经授权禁止出售小瓶RPA软件和其附加资源,详细参考: - [《小瓶RPA 用户协议》](https://rpa.pbottle.com/a-13944.html) - [《小瓶RPA 软件授权对比》](https://rpa.pbottle.com/License.php) ================================================ FILE: docs/package.json ================================================ { "devDependencies": { "rimraf": "^6.1.2", "vitepress": "^1.6.4" }, "scripts": { "docs:dev": "vitepress dev", "docs:clean": "rimraf .vitepress/dist", "docs:build": "vitepress build", "docs:preview": "vitepress preview" } } ================================================ FILE: docs/public/index.html ================================================ 文档静态资源目录 ================================================ FILE: docs/win7操作系统.md ================================================ # win7 操作系统上使用小瓶RPA 国内政企单位仍有大量 win7 电脑设备 小瓶 RPA 仍然花费开发资源保持对 win7 系统的全功能模块兼容。 ## win7 小瓶RPA平台基座 Win7 系统存在大量盗版精简系统,尽可能保持系统自动更新到最新版。 常见 win7 精简版因为缺乏 TTS 引擎 会导致小瓶RPA的 TTS 模块不可用。 ## win7 脚本引擎特殊安装 win7已经被很多新版本放弃,win7只能下载最新版本zip版本,才能使用mjs、fetch 等最新特性 https://mirrors.cloud.tencent.com/nodejs-release/v18.20.4/node-v18.20.4-win-x64.zip 1. 下载zip版本node并解压后添加文件夹路径 到 环境变量 path 中,参考:https://www.pbottle.com/a-14057.html 2. 如果提示:“无法定位程序输入点EventSetInformation 于动态链接库ADVAPI32.dll上 保证win7最新更新,补丁编号KB3080149, 下载地址是 https://www.microsoft.com/zh-cn/download/confirmation.aspx?id=48636 3. node 运行的提示操作系统不适配,设置环境变量 NODE_SKIP_PLATFORM_CHECK=1 即可 ================================================ FILE: docs/Q&A.md ================================================ # Q & A 常见问答 - 收集和探讨关于小瓶RPA的常见问题和官方答案。 - 更多问题可以联系在线客服。 ## 小瓶RPA 可以实现哪些流程的自动化,可以破解某个系统或软件吗? 小瓶RPA不具有破解和越权操作能力,RPA可以代替并优化人类操作电脑并有高速处理数据能力。 一个能力边界的判断方式是: 凡是你能手动操作的工作流程,小瓶RPA都能够把这个流程实现自动化。 如果你手动操作(包含使用工具软件)都无法做到的事情,大概率 小瓶RPA 也做不到。 ## 小瓶RPA的怎么使用?适用于哪个行业? 小瓶RPA是通用行业的自动化平台软件,所有现有工作流程都可以快速实现自动化。 小瓶RPA软件更像办公的ppt软件,你只安装了ppt是没有ppt文档的,没有实用价值的。流程脚本就好比制作ppt文档,好的ppt文档也不是每个人都会做的,根据用户的制作水平而定。 小瓶RPA附带了一些供参考的基础demo示例脚本,用户可以从官网下载运行。 ## 可以多个流程同时运行吗? - RPA的流程中可以大致分为 模拟操作 和 数据处理 两种指令。数据处理可以并发同时执行,模拟操作部分由于电脑只能一套鼠标键盘可以操作,所以不能同时执行。 - 一台电脑可以同时运行多个虚拟机,每个虚拟机可以同时运行一个自己的流程。 ## 为什么小瓶没有支持拖拉拽的所谓 '设计器',只能用脚本语言表达流程? - 小瓶RPA的主要定位是,专业用户的专业RPA软件。对流程实施者有基础脚本能力要求,不适合没有任何脚本基础的人员直接使用。应用场景多为自动化项目、工作站RPA应用等。 - 图形化编程(设计器)曾经风靡一时,但在脚本语言越来越傻瓜化的今天,图形的语法表现力不足这一劣势越来越明显。目前面对长难流程的开发管理往往不能满足和有更高综合成本。(有商业价值的自动化项目往往都是长难流程) - js脚本可以融入完整的 Nodejs(Python)生态,无缝引入万亿第三方功能包,可以传统IT项目自然对接,对技术运维人员更友好。 - V2023.3版本 新增加鼠标操作录制自动生成简单的脚本功能。 - 小瓶RPA可以用AI编程助手,实现流程脚本的自动化生成和编写。查看章节:`使用指南-》AI生成流程脚本`。声明:此方式目前正处于测试阶段。 ## 小瓶RPA 是否收费? - 小瓶RPA个人版本:永久免费! - 小瓶RPA企业版本:一次性软件永久授权 + 流程实施(可选)。其中流程实施包含:流程制作 + 测试 + 稳定性优化。 - 小瓶RPA云模块:各种 AI 大模型按照消耗云端 token 的量计费,自动从充值账号扣除,不使用不产生费用。 具体内容可咨询我们的销售顾问。 ## 可以脱网或者内网使用小瓶RPA吗? 可以,需要企业版授权,个人用户需要联网获取免费授权。 详见:https://rpa.pbottle.com/License.php ## 小瓶RPA可以将我制作的流程脚本打包成一个exe程序,直接在客户电脑运行吗? 小瓶RPA软件目前是个平台模式运行的软件,不支持其中一个流程导出为 exe 独立程序。 - **支持 OEM 定制化服务**,软件可以贴牌服务商公司的品牌logo,需要采购达量或者定制费。 - **支持 流程脚本加密**,适合服务商不想扩散自己开发的流程脚本情况。 - **支持 任务管理中心web版本**,可以让终端用户只通过浏览器批量管理自己的任务结果,无需接触具体任务流程。 ## 有了AI大模型,还需要小瓶RPA吗? 需要,目前AI从实用角度替代不了小瓶RPA。 - 小瓶RPA是准确优先的,AI是智能优先。目前工作流程如果全部AI,你要配双倍人力去给它纠错,最后经济上得不偿失。 - 目前小瓶RPA的自动化流程中,已经集成了多个AI本地模型,还有在线大模型AI模块,以便智能处理流程中的必要的环节。 ## 技术专员和流程实施的区别? - 流程实施服务是全包的服务,所有相关的开发、测试、优化和部署 都由我们来完成。 - 技术专员服务属于技术支持,只提供项目中具体的技术点咨询顾问服务,整体的实施工作还是以甲方为主来完成。 ## 小瓶RPA应用和传统软件应用有什么区别? 传统应用的所有功能模块都要自己开发,Rpa应用可以引用所有成品软件应用当做自己的功能模块。所以RPA应用也可以被叫做**软件的软件**。 比如传统应用要增加一个通信功能模块,你就得自己开发,顶多可以找library库来提升开发质量和速度。RPA应用可以直接连接企业微信,不但有了发消息的功能,甚至把企业微信的用户生态都串联了过来。 ================================================ FILE: docs/专用自动化独立软件.md ================================================ # 专用自动化独立软件(上位机软件定制开发) 独立自动化产品定制,硬件软件结合的独立可执行程序。 商业化友好,高性能 C/C++,AI能力集成等特点。 浏览业务官网:https://soft.pbottle.com/ ## 硬件设备支持 小瓶上位机软件可以通过: 1. 串口、CAN总线、USB、以太网、蓝牙、WIFI、MQTT等和硬件外设(下位机)进行通信。 2. PLC 设备厂商专用通信协议:欧姆龙Fins、三菱MC、西门子S7、modbus、OPC 等。 3. 定制版或者专属硬件SDK开发模式完成外设数据通信和控制。 其他设备类型: - 工业相机驱动 - 运动控制器驱动 - PLC下位机驱动 - 传感器 驱动 - 数据采集卡 - 定制外设硬件驱动SDK ## 自动化能力模块 装配 **小瓶RPA** 最新自动化操作能力模块。 ## 操作系统支持 - Windows - 信创Linux(麒麟+统信) - Android - 鸿蒙 ## 开发案例 https://soft.pbottle.com/a-13956.html *注:部分项目受合同保密条约限制不予展示,请联系客服索要跟多资料和视频演示* ================================================ FILE: docs/业务管理系统.md ================================================ # 业务管理系统(小瓶ERP业务管理系统) 市场瞬息万变,业务系统必须实时响应,小瓶ERP业务管理系统,业务敏捷开发高于一切的ERP系统,能够快速低成本实现贴合业务的量身定制。 摒弃传统 ERP 系统冗长的开发周期与高昂的定制成本,依托模块化架构,构建一套高效灵活的业务响应机制。 小瓶 RPA+AI 赋能的业务管理系统。 浏览业务官网:https://erp.pbottle.com/ ## 小瓶ERP业务管理系统优势 1. 敏捷高于一切的业务管理系统。 2. 灵活自主的私有云部署方式,同时支持小程序和APP移动运营。 3. 小瓶 RPA 提供AI数据处理能力。 ## 小瓶 RPA+AI 赋能 1. 流程自动化与效率提升 2. AI智能决策支持 3. 跨系统集成与数据互通 ## ATI 非接口数据能力 解决场景痛点: 1. 多业务数据,数据孤岛。 2. 数字化升级数据阻碍。 3. 软件数据保护,无导出途径。 4. 数据格式不通用。 5. 基础数据生涩难懂。 ================================================ FILE: docs/中文调用.md ================================================ # 中文调用 - 增加中文调用方式,更直观,更易读,更低入门 - 免去注释,快速开发流程 - 中文+英文 双重调用方式 ## API 中英文对照示意图  ## 快速开始的中文示例 ```javascript pbottleRPA.打开网址('https://www.baidu.com/') pbottleRPA.粘贴输入('小瓶RPA官网') pbottleRPA.键盘按键('enter') ``` ================================================ FILE: docs/信创操作系统.md ================================================ # 信创操作系统上使用小瓶RPA 小瓶RPA从 V2024.8 版本支持信创操作系统,已经完成信创操作系统的全功能模块原生支持。  ## 支持操作系统 1. 银河麒麟操作系统 KylinOS 2. 统信操作系统 UOS 3. 乌班图 Ubuntu OS   ## 安装 小瓶RPA 官方应用商店搜索 '小瓶RPA' (银河麒麟+统信) ## 信创操作系统平台差异 - 小瓶RPA信创版本具有和 windows 版本**完全一致的功能模块** - 可以用 windows 版本测试流程脚本 - 个人免费下载版支持 x86_64 的 CPU 架构 - 企业版支持 ARM、LoongArch、RISC-V 架构 ================================================ FILE: docs/其他功能模块.md ================================================ # 自由引入其他功能模块 JavaScript 是互联网上最流行的脚本语言之一,所有网页前端和客户端中都有它的身影,由于是一种类C语言,有着广泛的用户基础并且 JavaScript 很容易学习。 Node.js使用 `npm` 作为其默认的包管理器,该工具能够自动化处理项目依赖的安装、更新和删除。 npm 第三方功能库:https://www.npmjs.com/ 任何传统编程能够实现的功能模块,都能整合进入我们的小瓶RPA自动化流程。 ## 数据库模块 引入 mysql 数据库示例,其他数据库模块同理: ```javascript //引入小瓶RPA功能 const pbottleRPA = require('./pbottleRPA') //引入第三方mysql连接功能 事先执行命令:npm install mysql const mysql = require('mysql'); // 事先执行命令:npm install mysql var connection = mysql.createConnection({ host : 'localhost', user : 'root', password : '123456', database : 'test' }); connection.connect(); connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) { if (error) throw error; console.log('The solution is: ', results[0].solution); //使用小瓶RPA api接口 pbottleRPA.tts("mysql 获取数量为:" + results[0].solution) }); ``` ## 图片处理模块 引入 sharp 图片处理模块示例: ```javascript const pbottleRPA = require('./pbottleRPA') //引入小瓶RPA功能 const sharp = require('sharp'); // 事先执行命令:npm install sharp // 缩放到指定宽度,高度自适应 sharp('input.jpg') .resize(300, null) .toFile('output.jpg'); // 保持长宽比并限制在指定尺寸内 sharp('input.jpg') .resize(300, 300, { fit: 'inside', withoutEnlargement: true // 防止图片被放大 }) .toFile('output.jpg'); ``` ## Excel 和 Word 模块 先执行:`npm install exceljs mammoth docx` - 参考demo示例:[第三方] 读写Excel演示脚本.mjs - 参考demo示例:[第三方] 读写word演示脚本.mjs ## 生成 PDF 模块 生成一个简单的 PDF 文件的示例: ```javascript const pbottleRPA = require('./pbottleRPA') //引入小瓶RPA功能 const puppeteer = require('puppeteer'); // 事先执行命令:npm install sharp async function htmlToPdf(htmlContent, outputPath) { // 启动浏览器 const browser = await puppeteer.launch(); const page = await browser.newPage(); // 设置页面内容 await page.setContent(htmlContent); // 等待页面加载(可选) await page.waitForTimeout(1000); // 等待1秒,确保内容完全加载(如果有需要的话) // 生成 PDF await page.pdf({ path: outputPath, format: 'A4' }); // 关闭浏览器 await browser.close(); } // HTML 内容示例 const htmlContent = `
这是一个使用 Node.js 和 Puppeteer 生成的 PDF。
`; // 输出路径 const outputPath = 'output.pdf'; // 调用函数生成 PDF htmlToPdf(htmlContent, outputPath).catch(err => console.error('生成 PDF 失败:', err)); ```javascript ================================================ FILE: docs/定时启动.md ================================================ # 定时启动计划任务 ## 小瓶RPA自带定时任务规则 计划任务设置规则 1. 小时字段和分钟字段用空格隔开 2. 多个值用逗号隔开 3. 小时的取值范围 0-23,分钟取值范围0-59 4. 支持 - 表示范围,支持 * 表示任意 例子: ``` * * //表示每分钟一次 0-9 0 //表示每天0点 ... 到9点 9个小时每小时的零分钟都运营一次 3,6,9 10,40 //表示每天3点,6点,9点, 每小时运行两次,分别在10分钟和40分钟 ``` ## windows 高级多任务定时任务 操作系统原生定时器,稳定高效,并且有睡眠唤醒等高级功能 - Windows :**任务计划程序** 1. 打开windows自带的计划任务程序,并设置触发时间  2. 设置小瓶RPA启动流程  详细: https://rpa.pbottle.com/a-13985.html ## Linux 多任务定时任务 Linux **crontab** 是 Linux 系统中用于设置周期性被执行的指令的命令。 当安装完成操作系统之后,默认便会启动此任务调度命令。 crond 命令每分钟会定期检查是否有要执行的工作,如果有要执行的工作便会自动执行该工作。 Cron 表达式在线工具:https://www.jyshare.com/front-end/9444/ ================================================ FILE: docs/开机启动.md ================================================ # 开机启动 软件和流程均支持零操作,开机即可自动启动。 ## 开机启动小瓶RPA基座 设置-》基础设置-》开机自动启动  ### 信创系统 信创系统根据系统启动方式不同,设置方式不同 ## 开机启动流程任务 设置-》基础设置-》开机自动启动  ================================================ FILE: docs/手机应用的自动化.md ================================================ # 手机应用的自动化 手机应用可以采用 Android 模拟器方案 或者 真机投屏交互方案。得益于小瓶RPA采用纯图像识别的驱动方式,完全兼容各种手机应用模拟器 和 手机厂商的镜像投屏 **可以使用除了web增强插件外的任意api接口能力** ## 手机模拟器方案 经过我们测试的推荐: 蓝叠 已经有问题的:雷电模拟器,剪切板同步延迟,输入有问题 ## 真机投屏交互方案 手机厂商都提供可以息屏操作的真机电脑方案 - vivo办公套件 - 华为智慧互联  ## 手机验证码自动登录 现在很多软件权限的验证,都是通过手机短信验证码的。 小瓶RPA可以做到**自动验证码登录**,全程无需人工手动输入。 - 连接手机到电脑,开启手机调试。 - 小瓶RPA开发流程脚本,通过 ADB 读取手机中短息。 - 清洗短信数据,得到最新验证码,输入工作流程。 ================================================ FILE: docs/无尽模式.md ================================================ # 流程执行的无尽模式 依赖 delaySet() , 小瓶RPA可以稳定高效地连续执行流程任务. A 任务是一个时间长短不定的流程,如果想让A任务不停的运行,这就不能用定时器准确启动了。我们可以 A 任务中开头设置接力任务A自己。这样 7X24 小时,A任务都可以满时效的运行。 ## 对比定时器优势 - 定时器不能知道流程的具体结束时间,下次启动前会留有空隙,时效性差 - 定时器只能设置一个任务流程,无尽模式可以让多个任务流程交替接力执行 ## demo 示例 [企业版]接力执行脚本.js ================================================ FILE: docs/更多三方功能和拓展.md ================================================ --小瓶RPA兼容所有 nodejs生态 nodejs 文档:https://nodejs.org/docs/latest-v16.x/api/ npm 第三方功能库:https://www.npmjs.com/ **任何传统编程能够实现的功能模块,都能整合进入我们的小瓶RPA自动化流程。** 比如:文件处理、数据库操作、邮件发送等 ``` //引入小瓶RPA功能 const pbottleRPA = require('./pbottleRPA') //引入第三方mysql连接功能 事先执行命令:npm install mysql const mysql = require('mysql'); var connection = mysql.createConnection({ host : 'localhost', user : 'root', password : '123456', database : 'test' }); connection.connect(); connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) { if (error) throw error; console.log('The solution is: ', results[0].solution); //使用小瓶RPA api接口 pbottleRPA.tts("mysql 获取数量为:" + results[0].solution) }); ``` --联系小瓶RPA客服  ================================================ FILE: docs/桌面快捷方式.md ================================================ # 桌面快捷方式 每个流程应用都可以在桌面创建一个快捷方式。 注:本功能依赖外部控制方式,企业版可用 ## windows 桌面启动 新建文本文件,改名为: 一键启动.bat 发送到桌面快捷方式 ```bat chcp 65001 @echo off set "BAT_DIR=%cd%" set "BAT_DIR=%BAT_DIR:\=/%" @REM bat和脚本放到一个目录,然后替换下面/后面文件名即可。 一键启动脚本文件名不能有空格。 set "TASK_PATH=%BAT_DIR%/主流程示例.js" curl "http://127.0.0.1:49888/?action=pbottleRPA_run&path=%TASK_PATH%" exit @REM pause ``` ## Linux 桌面启动 新建文本文件,改名为: 一键启动.sh ```bash #!/bin/bash # 定义要访问的 URL url="http://127.0.0.1:49888/?action=pbottleRPA_run&path=TASK_PATH" # 使用 curl 访问指定路径,并将结果输出 curl -s "$url" # 检查 curl 命令的退出状态码 if [ $? -eq 0 ]; then echo "访问成功" else echo "访问失败" fi ``` ================================================ FILE: docs/流程录制.md ================================================ # 小瓶RPA流程录制 对于**简单鼠标操作**流程,小瓶RPA可以直接录制成js脚本,然后直接开始执行。 流程录制功能模块条件: 1. windows版本的小瓶RPA 2. 小瓶RPA V2024 以上版本  ## 录制注意事项 - 选择保存目录为demo目录(目录中需有 pbottleRPA.js 文件) - 录制目前只支持鼠标事件录制,不支持键盘事件的录制 - 录制结束快捷键:Ctrl+Shift+Q ## 重复执行录制的流程 操作系统原生定时器,稳定高效 - 参考定时器,设置重复执行当前任务。 [进入查看](定时启动.html) - 修改录制出的js脚本,加入循环逻辑。参考示例:基础(循环、判断、等待)演示.js ================================================ FILE: docs/流程运行日志.md ================================================ # 流程运行日志 小瓶RPA既有当前日志 和 永久硬盘日志 **新版支持:每个流程独立生成一个日志文件,形成一个清晰的运行记录。** ## 实时运行日志 小瓶RPA的流程日志系统会直接承接脚本引擎的标准输出,并会自动附加当前的详细时间: 1. javascript 用户 `console.log('流程日志输出了')` 2. python 用户 `print('流程日志输出了')` ### 图例  ## 永久硬盘日志 查看方式 - 菜单:工具-查看运行日志 ## 流程运行完整历史记录 - 每个流程独立生成一个日志文件,形成一个清晰的运行记录。 - 每天形成一个文件目录,方便按事件查找历史记录。 ================================================ FILE: docs/流程配置项.md ================================================ # 流程配置项 - 为每个流程设立一个配置项 - 日常用户只修改 excel 或者 json 即可,不需要直接修改流程脚本代码 ## 配置项为 excel 格式 建立一个excel文件,如:配置项.xlsx 参考示例: Excel读写 ## 配置项为 Json 格式 建立一个 Json 文件,如:配置项.json ```javascript global.global_config = require('./配置项.json') ``` ## 动态流程配置 流程执行时,可手动输入流程配置项目,输入后流程继续执行。 API:用户输入 waitInput 参考示例: 用户手动输入变量示例.js ================================================ FILE: docs/热键和快捷方式.md ================================================ # 热键和快捷方式 方便用户快速启动已有的任务流程,手动和自动充分结合,增效更多应用场景。 比如自动登录,自动数据处理等场景,让小瓶RPA真正成为你的办公小助手。 ## 全局流程任务热键 #### Ctrl + Shift + Q 停止当前的脚本运行 & 结束当前的鼠标操作录制 #### Ctrl + Shift + R 重新启动当前的脚本运行 ## 任务快捷方式 #### 设置  #### Ctrl + Shift + 数字(1-5) 快捷任务的全局热键 ## 任务栏菜单 快捷启动 任务栏菜单显示,右键展开快捷项目,点击快捷执行  ================================================ FILE: docs/用 js 脚本开发自动化流程.md ================================================ # 用 js 脚本开发自动化流程 JavaScript 是互联网上最流行的脚本语言之一,所有网页中都有它的身影,由于是一种类C语言,有着广泛的用户基础并且 JavaScript 很容易学习。 小瓶RPA自动流程脚本的开发只用到了最基础的js部分,建议有任何编程基础的用户直接开始开发您的流程,而不必要先深入学习javascript。 ## JavaScript 起步教程 如果您还没有任何脚本编写经验,可以参考网上的基础教程和demo来开发自己的流程,比如:https://www.runoob.com/js/js-intro.html 参考 Demo 示例:基础(循环、判断、等待)演示.js ## 推荐编辑器 VSCode 下载:https://code.visualstudio.com/ ## 推荐版本 nodeJS v18 以上 下载: https://rpa.pbottle.com/a-13943.html ## VSCode 断点调试脚本 VSCode 提供原生对js脚本的断点调试功能动态实时观察变量数值,可设置暂停断点调试。 1. 选择左侧工具栏-》运行调试 2. 选择 nodejs 3. 点击行号设置红色断点 并运行  ## 引入 NodeJS 三方功能模块 npm 第三方功能库:https://www.npmjs.com/ 任何传统编程能够实现的功能模块,都能整合进入我们的小瓶RPA自动化流程。 比如:文件处理、数据库操作、邮件发送等等 引入 nodejs mysql 数据库示例: ```javascript //引入小瓶RPA功能 const pbottleRPA = require('./pbottleRPA') //引入第三方mysql连接功能 事先执行命令:npm install mysql const mysql = require('mysql'); var connection = mysql.createConnection({ host : 'localhost', user : 'root', password : '123456', database : 'test' }); connection.connect(); connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) { if (error) throw error; console.log('The solution is: ', results[0].solution); //使用小瓶RPA api接口 pbottleRPA.tts("mysql 获取数量为:" + results[0].solution) }); ``` ================================================ FILE: docs/用 python 脚本开发自动化流程.md ================================================ # 用 python 脚本开发自动化流程(beta) Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有比其他语言更有特色语法结构。 小瓶RPA自动流程脚本的开发只用到了最基础的python部分,建议有任何编程基础的用户直接开始开发您的流程,而不必要先深入学习 Python。 ## Beta 版本状态说明 - Python 和 JavaScript 脚本层都是开源的, 流程实施方可以自由修改。 - Beta 版本相对于 JavaScript 版本,部分接口和 demo 示例存在延迟更新,用到的时候需要实施团队有自主补齐的 Python 开发能力。 - Beta 仅限于 Python 脚本层,小瓶 RPA 基础软件模块(基座平台) 和 JavaScript 一致 ,保持最新版本状态,所以有 Python 能力的实施团队完全可以正常使用。 ## python 起步教程 如果您还没有任何脚本编写经验,可以参考网上的基础教程和demo来开发自己的流程,比如:https://www.runoob.com/python3/python3-tutorial.html ## 推荐编辑器 推荐 VSCode 下载: https://code.visualstudio.com/ ## 推荐版本 推荐 python3.8 以上版本 下载: https://rpa.pbottle.com/a-14009.html ================================================ FILE: docs/硬件键鼠模拟.md ================================================ # 硬件键盘鼠标自动化模拟 ⚠ 只有商业用户获取支持 多数 RPA 采用**软件模拟**方式操作系统的键盘和鼠标。 部分网银软件、U盾软件、财务软件等,为了安全禁止其他软件对其操作。 小瓶 RPA **硬件键盘鼠标自动化模拟** 可以解决此问题 ## 硬件获取 - 邮寄获取小瓶RPA模拟器 - 电脑有空闲 USB 插口 ## 软件配置 - 菜单-》设置-》高级 -》键鼠硬件模拟 - 输入设备后,切换到当前模拟器。 - 修改当前流程脚本到 hid 作用域  参考示例: HID硬件级键盘鼠标演示.js ## 非标设备自动化 借助小瓶RPA硬件级键盘输入等可以实现非标设备自动化  ================================================ FILE: docs/老旧低配电脑.md ================================================ # 低配置老旧电脑运行自动化 小瓶RPA基座平台采用 c++ 高性能开发方案,已经最大限度的减低电脑配。 ## 平台基座系统资源占用优化 设置里取消本地 AI 引擎的启动加载。 取消全部AI引擎后,小瓶RPA主内存可以降低到 40 M 级别。  ## 流程脚本优化 - 通过 setDefaultDelay 增大默认操作时间间隔 - 通过 wait 手动增加特定的时间间隔 - waitImage 自动等待 取代 wait 固定等待 ================================================ FILE: docs/视频教程.md ================================================ # 小瓶RPA系列视频教程 已经陆续推出了免费的 《小瓶RPA系列视频教程》 请网络搜索 :小瓶RPA系列视频教程 ## B站入口 https://www.bilibili.com/video/BV1Th4y1C7np/ ================================================ FILE: docs/集群控制中心.md ================================================ # 集群控制中心 ⚠ 集群控制只有商业版本可以获取支持 **小瓶RPA 集群管理系统可以让终端用户只通过浏览器批量管理自己的任务结果,无需接触具体任务流程。** 小瓶RPA(机器人流程自动化)集群控制中心是整个 RPA 批量任务的核心管理和调度枢纽。 在一个大型自动自动任务中,RPA 集群控制中心可以根据各个部门的业务需求,将自动化任务分配给不同的机器人,同时实时监控机器人的工作状态。 如果某个机器人出现故障或异常,控制中心可以及时调整任务分配,确保业务的连续性。 通过对运行数据的分析,企业可以发现流程中的瓶颈和优化点,进一步提升 RPA 的效率和效果。它就像是一个指挥中心,确保 RPA 机器人集群高效、有序地运行,为企业的数字化转型提供有力支持。 ## 系统基础架构 支持 linux & windows 系统搭建。 一次性设备授权绑定。 **web 管理后台**,方便局域网、网络用户登录和使用。  #### 用户管理 允许新用户进行注册,填写必要的信息,如用户名、密码等。提供登录功能,验证用户身份 以授予相应权限。支持用户修改密码操作。 用户操作日志可以记录员工何时登录系统、修改了哪些业务数据等。如果发现某些数据被误 修改或存在违规操作,就可以通过操作日志快速找到责任 #### 设备终端管理 终端信息管理,详细记录每一个设备终端的基本信息,如设备信息、名称、添加时间等。 方便对大量设备进行清晰的识别和管理。 连接管理,监控和管理设备终端与控制中心的连接状态,确保稳定通信。时处理连接异常情 况,保障任务分配和数据传输的顺畅。 RPA 终端状态监测,持续监测设备终端的性能指标,如离线、连接、正在运行等。根据性能 状况进行资源调配和优化。 #### 任务管理 当有大量任务需要执行时,控制中心可以根据各节点的实时情况将任务高效地分配出去。通 过对任务状态和进度的持续监控,确保每个worker rpa 机器人都能发挥最有效的能效。 任务创建与定义,允许用户方便地创建新的 RPA 任务,明确任务的目标参数、流程开始时 间等要求。 任务分配,根据集群中各节点的状态和能力,合理地将任务分配给合适的节点执行。 可以考虑负载均衡等因素,以提高整体效率。分发模式分为设备所有终端 + 指定终端 任务参数设定,设定每个任务的具体参数,确保终端RPA客户端可以接收,并按照参数配 置来执行自动流程任务。 任务历史记录,保留任务的历史信息,包括执行时间、结果等,便于追溯和复盘。 #### 定制任务类型 基于 BS 架构的 web 管理系统,可以快速为客户定制专有流程的任务管理系统。 定制符合业务场景的,专有输入输出(input|output)文件格式的任务类型。 ## 小瓶RPA集群系统使用文档 支持 linux & windows 系统私有云搭建,终极数据安全保障。 一次性设备授权绑定,无限终端数量。 系统预览:  #### 终端配置 配置步骤: 1. 打开小瓶RPA终端 2. 设置(系统设置) 3. 高级 4. 集群控制中心服务地址,如:`http://pbottleRPA_cluster_test.com:39088/api/query` 5. 集群控制中心通信频率 如:`5`,0:关闭; 结果:软件集群模式 显示绿灯 #### 流程日志回传 日志回传需要等待业务脚本执行完成后,终端主动回传,可能会有延迟。 流程开始的地方添加回传脚本 ``` pbottleRPA.delaySet('./[企业版]集群控制中心日志回传.js'); ``` ================================================ FILE: docs/验证码自动化.md ================================================ # 验证码自动化 **温馨提示:小瓶RPA本身以模拟操作和数据处理为主,它不具有任何破解功能** 优先选择记住登录状态、U盾、定时刷新等方法保持用户权限的会话。 ## 简单图形验证码 小瓶RPA文字OCR模块可以解决简单字母数字验证码  ## 人工辅助半自动化 流程如下: 1. 小瓶RPA获取验证码截图 2. 发送系统管理员手机,人工识别 3. 识别结果发送回小瓶RPA自动输入 4. 完成验证!! *提示:一般系统都有记住登录状态功能,请优先勾选,一台电脑只需登录一次。* ## 用第三方网络API 百度搜索 “验证码识别API”,一般都是些小厂商的api云服务, 注册付费获取api权限并待用 - http://www.ttshitu.com/test.html - https://www.jfbym.com/price.html 流程: 1. 小瓶RPA获取验证码截图 2. 上传截图到三方API 3. 小瓶RPA根据返回结果生成键鼠逻辑 4. RPA 操作完成验证 ## 自有系统私有化部署 小瓶RPA增值服务之一,基于AI图像技术为你独立训练适配当前验证码的本地服务。 ================================================ FILE: package.json ================================================ { "name": "pbottle-rpa-demo", "version": "2025.4.0", "description": "小瓶RPA基础演示流程,使用教程:https://rpa.pbottle.com/a-14019.html,流程开发文档:https://rpa.pbottle.com/docs/", "main": "", "scripts": { "install-dep": "npm install exceljs mammoth docx" }, "author": "leo@pbottle.com", "license": "MIT" } ================================================ FILE: pbottleRPA.js ================================================ /** * 小瓶 RPA 标准库API NodeJS版本 * 官网:https://rpa.pbottle.com/ * 作者:leo@pbottle.com * * 欢迎各路高手将本代码转换成 python、lua、C# 等其他语言封装 * */ const path = require("node:path"); const fs = require("node:fs"); const os = require('os'); const tls = require('node:tls'); const childProcess = require('node:child_process'); /** * 当前脚本的路径,结尾无/ 如 'D:/pbottleRPAdemo' */ const jsPath = path.resolve('./'); const CppUrl = `http://127.0.0.1:49888/` let basePath = process.env.RPAbaseDir; //基座路径 let homePath = process.env.RPAhomeDir; let curlCommand = 'curl'; //优先使用系统的,如果系统不存在curl命令,使用小瓶RPA自带的 console.log("基座服务地址:(NodeJS)", CppUrl); exports.jsPath = jsPath exports.basePath = basePath exports.__dirname = jsPath exports.目录路径 = jsPath //node:fs exports.fs = fs //node:path exports.path = path let defaultDelay = 1000; //默认值一秒 /** * 设置RPA模拟操作的延时 包含鼠标、键盘、粘贴、打开网页操作 * 设置为 0 可以用 sleep() 手动管理操作延时 * @param {number} millisecond 毫秒单位的数字,系统默认 1000 毫秒 即1秒 */ let setDefaultDelay = (millisecond) => { defaultDelay = millisecond } exports.setDefaultDelay = setDefaultDelay exports.设置默认操作延时 = setDefaultDelay /** * 发出系统警告声音 * @returns */ let beep = () => { let url = `${CppUrl}?action=beep` // console.log(url) let res = getHtml(url) } exports.beep = beep exports.蜂鸣声 = beep /** * 日志输出,同时生成文件日志 */ exports.log = console.log exports.日志输出 = console.log /** * 系统原生消息提示 * @param {string} title 标题 * @param {string} content 内容 * @returns */ let showMsg = (title, content) => { title = encodeURIComponent(title) content = encodeURIComponent(content) let url = `${CppUrl}?action=showMsg&title=${title}&content=${content}` // console.log(url) let res = getHtml(url) return res; } exports.showMsg = showMsg exports.显示系统消息 = showMsg /** * (强行)关闭指定软件 * @param {string} processName 进程名称,如:'WINWORD.EXE' 任务管理器 ‘进程名称’ 栏目 。注意不是 名称,如不显示,右键勾选显示这一栏目即可 * @param {boolean} force 是否强制,相当于模拟任务管理器的结束任务操作。默认普通关闭,可能跟随保存确认框 */ let kill = (processName, force = false) => { let forceCMD = '' if (force) { forceCMD = '/F' } try { childProcess.execSync(`taskkill ${forceCMD} /IM ${processName}`, { stdio: 'ignore', encoding: 'utf8' }) } catch (error) { console.error(`关闭进程(${processName})失败,可能软件未运行`); return; } console.log('关闭进程成功:' + processName); } exports.kill = kill exports.关闭软件 = kill /** * 有效屏幕内显示一个彩色方框,直观提示流程操作范围和目标的当前的定位 * V2024.6以上版本有效 * @param {number} fromX 起始位置xy坐标,屏幕左上角为零点 * @param {number} fromY * @param {number} width 宽度 * @param {number} height 高度 * @param {string} color 颜色 红绿蓝黄4色可选:red|green|blue|yellow * @param {number} msec 显示持续时间 单位毫秒 * @returns */ let showRect = (fromX = 0, fromY = 0, width = 500, height = 500, color = 'red', msec = 500) => { fromX = Math.round(fromX) fromY = Math.round(fromY) width = Math.round(width) height = Math.round(height) color = encodeURIComponent(color) let url = `${CppUrl}?action=showRect&fromX=${fromX}&fromY=${fromY}&width=${width}&height=${height}&color=${color}&msec=${msec}` // console.log(url) let res = getHtml(url) return res; } exports.showRect = showRect exports.显示标记框 = showRect /** * 强制退出当前脚本 * @param {...any} msg 退出时候输出的信息 */ let exit = (...args) => { console.log(...args) beep() process.exit(1) } exports.exit = exit exports.退出流程 = exit /** * 脚本暂停等待操作响应 (毫秒) * 注意:一次等待上限时长两分钟内 * @param {number} milliseconds 毫秒 * @returns */ let sleep = (milliseconds) => { // childProcess.execSync(` node -e "setTimeout(() => console.log('sleep ${milliseconds} 结束'), ${milliseconds})" `, { stdio: ['ignore', 'ignore', 'ignore'], encoding: 'utf8' }) if (milliseconds < 1) { // console.log('milliseconds input error'); return; } milliseconds = Math.floor(milliseconds) //毫秒取整 if (milliseconds >= 120000) { console.log('警告:一次等待上限时长两分钟内'); } milliseconds -= 20 //减小毫秒误差,接口请求导致,大小受电脑运行速度影响 if (milliseconds < 1) { milliseconds = 1 } let url = `${CppUrl}?action=httpSleep&milliseconds=${milliseconds}` // console.log(url) let res = getHtml(url) return res; } exports.sleep = sleep exports.睡眠毫秒 = sleep /** * 脚本暂停等待操作响应 (秒) * 注意:一次等待超过100s, 会有日志提示 * @param {number} seconds 秒, 缺省值为 1 秒。支持小数。 */ let wait = (seconds = 1) => { if (seconds <= 0 || !isNumeric(seconds)) { console.log('pbottleRPA.wait:seconds input error'); return; } if (seconds > 100) { //100秒 let quotient = Math.floor(seconds / 100) for (let i = 0; i < quotient; i++) { //每100秒 sleep(100 * 1000) console.log(`提示:已等待100s...`); } seconds = seconds % 100; sleep(seconds * 1000) } else { sleep(seconds * 1000) } } exports.wait = wait exports.等待 = wait /** * 移动鼠标到指定位置并点击 起点为屏幕左上角 * @param {number} x 横坐标 * @param {number} y 纵坐标 * @param {number} interval 像素间隔时间,越大移动越慢 毫秒单位,默认:0 * @returns */ let moveMouseSmooth = (x, y, interval = 0) => { x = Math.round(x) y = Math.round(y) let url = `${CppUrl}?action=moveMouse&x=${x}&y=${y}&interval=${interval}` // console.log(url) let res = getHtml(url) sleep(defaultDelay); return res; } exports.moveMouseSmooth = moveMouseSmooth exports.moveMouse = moveMouseSmooth //增加别名 exports.鼠标移动 = moveMouseSmooth /** * 移动鼠标到指定位置并点击 * @param {number} x 横坐标 * @param {number} y 纵坐标 */ let moveAndClick = (x, y) => { // call local functions directly instead of using `this` which may not refer to module exports moveMouseSmooth(x, y) mouseClick() } exports.moveAndClick = moveAndClick exports.鼠标移动并点击 = moveAndClick /** * 当前位置点击鼠标 默认左键 可选 'right' * @param {string} leftRight 可选 * @param {number} time 点按时间 单位毫秒 可选 * @returns */ let mouseClick = (leftRight = 'left', time = 30) => { let url = `${CppUrl}?action=mouseLeftClick&time=${time}` if (leftRight == 'right') { url = `${CppUrl}?action=mouseRightClick&time=${time}` } // console.log(url) let res = getHtml(url) sleep(defaultDelay); return res; } exports.mouseClick = mouseClick exports.鼠标点击 = mouseClick /** * 双击鼠标 默认左键 * @returns */ let mouseDoubleClick = () => { let url = `${CppUrl}?action=mouseDoubleClick` // console.log(url) let res = getHtml(url) sleep(defaultDelay); return res; } exports.mouseDoubleClick = mouseDoubleClick exports.鼠标双击 = mouseDoubleClick /** * 鼠标滚轮 * @param {number} data 滚动的量 默认为-720 向下滚动720度 * @returns */ let mouseWheel = (data = -720) => { let url = `${CppUrl}?action=mouseWheel&data=${data}` // console.log(url) let res = getHtml(url) sleep(defaultDelay); return res; } exports.mouseWheel = mouseWheel exports.鼠标滚轮 = mouseWheel /** * 鼠标左键拖到指定位置 * @param {number} x * @param {number} y * @returns */ let mouseLeftDragTo = (x, y) => { x = Math.round(x) y = Math.round(y) let url = `${CppUrl}?action=mouseLeftDragTo&x=${x}&y=${y}` // console.log(url) let res = getHtml(url) sleep(defaultDelay); return res; } exports.mouseLeftDragTo = mouseLeftDragTo exports.鼠标左键拖动 = mouseLeftDragTo /** * 鼠标右键拖到指定位置 * @param {number} x * @param {number} y * @returns */ let mouseRightDragTo = (x, y) => { x = Math.round(x) y = Math.round(y) let url = `${CppUrl}?action=mouseRightDragTo&x=${x}&y=${y}` // console.log(url) let res = getHtml(url) sleep(defaultDelay); return res; } exports.mouseRightDragTo = mouseRightDragTo exports.鼠标右键拖动 = mouseRightDragTo /** * 屏幕一个点取色 * @param {number} x * @param {number} y * @returns {string} 返回颜色值 如:'#000000' */ let getScreenColor = (x, y) => { let url = `${CppUrl}?action=getScreenColor&x=${x}&y=${y}` // console.log(url) let res = getHtml(url) let jsonRes = JSON.parse(res) return jsonRes.rs; } exports.getScreenColor = getScreenColor exports.获取屏幕颜色 = getScreenColor /** * 屏幕截图 * @param {string} savePath 保存路径默认 我的图片,图片格式为PNG;如果使用自定义路径请以 '.png' 结尾; * @param {number} x 截图开始位置 * @param {number} y * @param {number} w 可选 截图宽度 * @param {number} h 可选 截图长度 * @returns */ let screenShot = (savePath = '', x = 0, y = 0, w = -1, h = -1) => { if (savePath) { //整理路径 savePath = path.resolve(savePath) savePath = encodeURIComponent(savePath) } x = parseInt(x) y = parseInt(y) w = parseInt(w) h = parseInt(h) if (x != 0 || y != 0 || w != -1 || h != -1) { showRect(x, y, w, h); } let url = `${CppUrl}?action=screenShot&savePath=${savePath}&x=${x}&y=${y}&w=${w}&h=${h}` // console.log(url) let res = getHtml(url) return res; } exports.screenShot = screenShot exports.屏幕截图 = screenShot /** * 按键名称 转成 键值 * @param {string} name * @returns {number} */ function keycode(name) { name = name.trim().toLowerCase(); const replacement_dict = { 'backspace': 8, 'tab': 9, 'enter': 13, 'shift': 16, 'ctrl': 17, 'alt': 18, 'pause/break': 19, 'caps lock': 20, 'esc': 27, 'space': 32, 'page up': 33, 'page down': 34, 'end': 35, 'home': 36, 'left': 37, 'up': 38, 'right': 39, 'down': 40, 'insert': 45, 'delete': 46, 'command': 91, 'left command': 91, 'right command': 93, 'numpad *': 106, 'numpad +': 107, 'numpad -': 109, 'numpad .': 110, 'numpad /': 111, 'num lock': 144, 'scroll lock': 145, 'my computer': 182, 'my calculator': 183, 'windows': 91, '⇧': 16, '⌥': 18, '⌃': 17, '⌘': 91, 'ctl': 17, 'control': 17, 'option': 18, 'pause': 19, 'break': 19, 'caps': 20, 'return': 13, 'escape': 27, 'spc': 32, 'spacebar': 32, 'pgup': 33, 'pgdn': 34, 'ins': 45, 'del': 46, 'cmd': 91, 'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115, 'f5': 116, 'f6': 117, 'f7': 118, 'f8': 119, 'f9': 120, 'f10': 121, 'f11': 122, 'f12': 123, ';': 186, '=': 187, ',': 188, '-': 189, '.': 190, '/': 191, '`': 192, '[': 219, '\\': 220, ']': 221, "'": 222, "0": 48, "1": 49, "2": 50, "3": 51, "4": 52, "5": 53, "6": 54, "7": 55, "8": 56, "9": 57, "a": 65, "b": 66, "c": 67, "d": 68, "e": 69, "f": 70, "g": 71, "h": 72, "i": 73, "j": 74, "k": 75, "l": 76, "m": 77, "n": 78, "o": 79, "p": 80, "q": 81, "r": 82, "s": 83, "t": 84, "u": 85, "v": 86, "w": 87, "x": 88, "y": 89, "z": 90, } return replacement_dict[name] } /** * 模拟键盘按键触发基础事件 * @param {string} key 按键名称参考:https://www.pbottle.com/a-13862.html * @param {string} "up" 或 "down" 默认按下down。up松开按键 * @returns */ let keyToggle = (key, upDown = 'down') => { let upDown_n = 0; if (upDown == 'up') { upDown_n = 2; } let key_n = keycode(key) if (key_n === undefined) { console.log(`⚠ 按键 ${key} 不存在!~`); return } let url = `${CppUrl}?action=keyToggle&key_n=${key_n}&upDown_n=${upDown_n}` // console.log(url) let res = getHtml(url) return res; } exports.keyToggle = keyToggle exports.键盘基础触发 = keyToggle /** * 模拟鼠标按键触发基础事件 * @param {string} key 鼠标 left | right | middle * @param {string} "up" 或 "down" 默认按下down。up松开按键 * @returns */ let mouseKeyToggle = (key = 'left', upDown = 'down') => { let upDown_n = 0; if (upDown == 'up') { upDown_n = 2; } let key_n = 0 switch (key) { case 'right': key_n = 1 break; case 'middle': key_n = 2 break; default: key_n = 0 break; } let url = `${CppUrl}?action=mouseKeyToggle&key_n=${key_n}&upDown_n=${upDown_n}` // console.log(url) let res = getHtml(url) return res; } exports.mouseKeyToggle = mouseKeyToggle exports.鼠标基础触发 = mouseKeyToggle /** * 按一下键盘 支持组合按键 加号连接 如: keyTap('ctrl + a') * @param {string} key 按键名称参考:https://www.pbottle.com/a-13862.html */ let keyTap = (key) => { if (key.includes('+')) { let subkeys = new Array(); subkeys = key.split('+') subkeys = subkeys.map((value) => { return value.trim() }) for (let index = 0; index < subkeys.length; index++) { const element = subkeys[index]; // keyToggle(element,"up") //净化复位 keyToggle(element, "down") } subkeys = subkeys.reverse() for (let index = 0; index < subkeys.length; index++) { const element = subkeys[index]; keyToggle(element, "up") } } else { // keyToggle(key,"up") //净化复位 keyToggle(key, "down") keyToggle(key, "up") } sleep(defaultDelay); } exports.keyTap = keyTap exports.键盘按键 = keyTap /** * 屏幕查找图象定位 * @param {string|Array} tpPaths 搜索的小图片,建议png格式 相对路径:./image/123.png * @param {number} miniSimilarity 可选,指定最低相似度,默认0.85。取值0-1,1为找到完全相同的。 * @param {number} fromX=0 可选,查找开始的开始横坐标 * @param {number} fromY=0 可选,查找开始的开始纵坐标 * @param {number} width=-1 可选,搜索宽度 * @param {number} height=-1 可选,搜索高度 * @returns {{x:number,y:number}|false} 返回找到的结果json 格式:{x,y} 相对于左上角原点 */ var findScreen = (tpPaths, miniSimilarity = 0.85, fromX = 0, fromY = 0, width = -1, height = -1) => { if (fromX < 0 || fromY < 0) { throw new Error(`错误:找图起始点不能为负,x:${fromX} y:${fromY} `); } if (fromX != 0 || fromY != 0 || width != -1 || height != -1) { showRect(fromX, fromY, width, height); } tpPaths = Array.isArray(tpPaths) ? tpPaths : [tpPaths] console.log(tpPaths); for (let index = 0; index < tpPaths.length; index++) { let tpPath = tpPaths[index]; tpPath = path.resolve(tpPath) tpPath = encodeURIComponent(tpPath) let url = `${CppUrl}?action=findScreen&imgPath=${tpPath}&fromX=${fromX}&fromY=${fromY}&width=${width}&height=${height}` // console.log(url) let res = getHtml(url) // console.log(res.getBody('utf8')); let jsonRes = JSON.parse(res); // console.log(tpPath); // console.log(jsonRes); if (jsonRes.error) { console.log(jsonRes.error); return false; } if (jsonRes.value >= miniSimilarity) { showRect(jsonRes.x - 25, jsonRes.y - 25, 50, 50, 'green'); return jsonRes; } } return false; } exports.findScreen = findScreen exports.寻找图像 = findScreen /** * 查找文字,注:此功能受电脑性能影响,低配电脑可能速度较慢。 需要小瓶RPA客户端版本 > V2024.5 * @param {string} inputTxt * @param {number} fromX=0 可选,查找开始的开始横坐标 * @param {number} fromY=0 可选,查找开始的开始纵坐标 * @param {number} width=-1 可选,搜索宽度 * @param {number} height=-1 可选,搜索高度 * @returns {{x:number,y:number,text:string}|false} 返回json结果:{x,y,text} x,y坐标相对于屏幕左上角的原点 */ var findText = (inputTxt, fromX = 0, fromY = 0, width = -1, height = -1) => { let jsonDatas = aiOcr('screen', fromX, fromY, width, height); // console.log(jsonDatas); let result = false; jsonDatas.forEach(element => { // console.log(element.text); if (element.text.includes(inputTxt)) { result = element } }); if (result !== false) { showRect(result.x - 25, result.y - 25, 50, 50, 'green'); } return result; } exports.findText = findText exports.寻找文字 = findText /** * 等待屏幕指定文字出现 * @param {string} inputTxt 搜索文字 * @param {number} fromX 可选,查找开始的开始横坐标 * @param {number} fromY 可选,查找开始的开始纵坐标 * @param {number} width 可选,搜索宽度 * @param {number} height 可选,搜索高度 * @param {function} intervalFun 回调函数,用于中途判断是否继续等待,返回值为stopWait时,停止等待 * @param {number} timeOut 超时时间,单位秒 * @returns */ var waitText = (inputTxt, fromX = 0, fromY = 0, width = -1, height = -1,intervalFun = () => {}, timeOut = 20) => { console.log('waiting Text:', inputTxt); for (let index = 0; index < timeOut; index++) { sleep(1000) let position = findText(inputTxt, fromX, fromY, width, height) if (position !== false) { return position; } if (typeof intervalFun === 'function' && intervalFun() == 'stopWait') { console.log('stopWait from intervalFun'); return false } } //debug 保存当前屏幕 console.log('已经保存超时截图到:我的图片'); screenShot(); //error throw new Error(`等待文字超时 ${inputTxt}`); } exports.waitText = waitText exports.等待文字 = waitText /** * 屏幕查找物体或者窗口轮廓 * 调试:软件根目录会生成 debug/findContours.png * * @param {number} minimumArea 轮廓最小面积 默认过滤掉 10x10 以下的元素 * @param {number} fromX 查找起点 * @param {number} fromY * @param {number} width 查找范围 * @param {number} height * @returns {[]} 所有查找到的轮廓信息,包含闭合区域的起始坐标,中点坐标,面积,id。 格式:[{ x: 250, y: 10, cx: 265.5, cy: 30.5, area: 2401, id: 42 },...] xy相对于原点 */ var findContours = (minimumArea = 1000, fromX = 0, fromY = 0, width = -1, height = -1) => { if (fromX < 0 || fromY < 0) { throw new Error(`错误:轮廓查找起始点不能为负,x:${fromX} y:${fromY} `); } if (fromX != 0 || fromY != 0 || width != -1 || height != -1) { showRect(fromX, fromY, width, height); } let url = `${CppUrl}?action=findContours&minimumArea=${minimumArea}&fromX=${fromX}&fromY=${fromY}&width=${width}&height=${height}` // console.log(url) let res = getHtml(url) // parse the response string directly (getHtml returns stdout string) let jsonRes = JSON.parse(res); for (const json of jsonRes) { json.x += fromX json.y += fromY } // console.log(jsonRes); return jsonRes; } exports.findContours = findContours exports.寻找轮廓 = findContours /** * 当前位置 粘贴(输入)文字 * @param {string} txt 复制到电脑剪切板的文本 */ var paste = (txt) => { copyText(txt) // sleep(200) keyTap('ctrl+v') sleep(defaultDelay); } exports.paste = paste exports.粘贴输入 = paste /** * 图片相似度对比 需要小瓶RPA客户端版本 > V2025.3 * @param {string} path1 图片1路径 * @param {string} path2 图片2路径 * @param {'SIFT' | 'ORB' | 'SSIM'} checkType 对比算法 默认 'ORB' * @returns {{score:number, time:number}} score相似度分数 0-1 ; time耗时秒 */ var imgSimilar = (path1, path2, checkType = 'ORB') => { path1 = encodeURIComponent(path1) path2 = encodeURIComponent(path2) let url = `${CppUrl}?action=imgSimilar&path1=${path1}&path2=${path2}&checkType=${checkType}` let res = getHtml(url) return JSON.parse(res); } exports.imgSimilar = imgSimilar exports.图片相似度对比 = imgSimilar /** * 模拟复制文字,相当于选择并复制文本内容 v2025.0以上生效 * @param {string} txt 复制的文本内容 */ var copyText = (txt) => { txt = encodeURIComponent(txt) let url = `${CppUrl}?action=copyText&txt=${txt}` // console.log(url) let res = getHtml(url) return res } exports.copyText = copyText exports.复制文字 = copyText /** * 模拟复制操作,支持文件路径和文件夹路径,复制后在目标文件夹ctrl+V 即可粘贴 V2024.7开始生效 * 复制文件后,在微信发送窗口粘贴,即可发送文件 * @param {string} filepath 绝对路径 */ var copyFile = (filepath) => { filepath = path.resolve(filepath) if (!fs.existsSync(filepath)) { console.log('copyFile警告:文件路径不存在', filepath); } filepath = filepath.replace(/\\/g, '/') filepath = encodeURIComponent(filepath) let url = `${CppUrl}?action=copyFile&path=${filepath}` // console.log(url) let res = getHtml(url) return res } exports.copyFile = copyFile exports.复制文件 = copyFile /** * 获取当前电脑的剪切板内容,系统剪切板支持多种格式 版本 V2024.2 开始生效 * ①纯文本格式:普通复制 如'小瓶RPA' * ②图片格式 base64形式:浏览器复制图片 'data:image/png;base64,' 开头 * ③html格式:浏览器或者钉钉复制富文本综合内容 ''开头 * @returns 结果文本 */ var getClipboard = () => { let url = `${CppUrl}?action=getClipboard` // console.log(url) let res = getHtml(url) return res; } exports.getClipboard = getClipboard exports.获取剪切板内容 = getClipboard /** * 通知到手机 * 通过小瓶云发送微信通知 (微信到达率高,并且免费) * @param {string} title 消息标题 * @param {string} content 消息详细内容 * @param {string} key 获取key详情方法:https://www.pbottle.com/a-12586.html */ var wxMessage = (title, content, key) => { let url = `https://yun.pbottle.com/manage/yun/?msg=${encodeURIComponent(content)}&name=${encodeURIComponent(title)}&key=${key}`; let res = getHtml(url) console.log('发送微信消息:', res); } exports.wxMessage = wxMessage exports.微信消息发送 = wxMessage /** * 向指定API网址post一个json,最常用网络接口方式 * @param {string} url API网络地址 * @param {object} msgJson Json对象 * @param {object} headersJson 请求头 Json对象 * @param {string} method e.g. GET, POST, PUT, DELETE or HEAD * @returns {string} */ var postJson = (url, msgJson, headersJson = {}, method = 'POST') => { const jsonData = JSON.stringify(msgJson); const commandArgs = [ '-X', method, '-H', 'Content-Type: application/json', "--silent", "--show-error", '-d', jsonData, url ]; if (Object.keys(headersJson).length !== 0) { for (const [key, value] of Object.entries(headersJson)) { commandArgs.push('-H', `${key}: ${value}`); } } const result = childProcess.spawnSync(curlCommand, commandArgs, { encoding: 'utf8' }); if (result.error) { throw new Error('postJson 执行 curl 命令时出错:' + result.error.message); } if (result.status !== 0) { throw new Error('postJson curl 命令执行失败: ' + result.stderr); } return result.stdout; } exports.postJson = postJson exports.提交json = postJson /** * 向指定API网址post一个json文件,适合大型json内容 * @param {string} url API网络地址 * @param {string} msgJsonFile Json文件路径 * @param {object} headersJson 请求头Json对象 * @param {string} method e.g. GET, POST, PUT, DELETE or HEAD * @returns {string} */ var postJsonFile = (url, msgJsonFile, headersJson = {}, method = 'POST') => { msgJsonFile = path.resolve(msgJsonFile); const commandArgs = [ '-X', method, '-H', 'Content-Type: application/json', '-d', `@${msgJsonFile}`, url ]; if (Object.keys(headersJson).length !== 0) { for (const [key, value] of Object.entries(headersJson)) { commandArgs.push('-H', `${key}: ${value}`); } } const result = childProcess.spawnSync(curlCommand, commandArgs, { encoding: 'utf8' }); if (result.error) { throw new Error('postJsonFile 执行 curl 命令时出错: ' + result.error.message); } if (result.status !== 0) { throw new Error('postJsonFile curl 命令执行失败:' + result.stderr); } return result.stdout; } exports.postJsonFile = postJsonFile exports.提交json文件 = postJsonFile /** * 普通请求网址,获取返回的html文本 * @param {string} url 网络地址 get方法 * @param {object} headersJson 请求头 Json对象 * @param {string} method 请求方法 :GET, POST, PUT, DELETE or HEAD * @returns {string} 返回的文本 */ function getHtml(url, headersJson = {}, method = 'GET') { let commandArgs = ['-X', method, url]; if (Object.keys(headersJson).length !== 0) { for (const [key, value] of Object.entries(headersJson)) { commandArgs.push('-H', `${key}: ${value}`); } } const result = childProcess.spawnSync(curlCommand, commandArgs, { encoding: 'utf8' }); if (result.error) { throw new Error('getHtml 执行 curl 命令时出错: ' + result.error.message); } if (result.status !== 0) { throw new Error('getHtml curl 命令执行失败: ' + result.stderr); } return result.stdout; } exports.getHtml = getHtml exports.请求网址 = getHtml /** * 发送邮件;注意这个方法是个异步方法,请参考示例; * @param {string} to 收件人地址 * @param {string} subject 邮件主题 * @param {string} content 邮件内容;文本文件,换行用 '\n' * @param {string} host 服务器地址(如:smtp.qq.com) * @param {number} port 服务器端口 默认是465 * @param {string} user 认证信息(用户名)一般也是发送邮件地址 * @param {string} pass 认证信息(密码) * @returns */ function sendMail( to, subject, content, host = 'smtp.qq.com', port = 465, user = 'leo191@foxmail.com', pass = 'fxfqtsxmwcohbcbc', ) { return new Promise((resolve, reject) => { const client = tls.connect(port, host, { rejectUnauthorized: false }, () => { console.log('✅ 已连接到 SMTP 服务器'); }); client.setEncoding('utf8'); if (user == 'leo191@foxmail.com') { content += '\n\n\ 请不要将演示测试邮箱用作实际业务,详细查看:https://rpa.pbottle.com/a-14106.html' } const commands = [ `EHLO ${host}`, `AUTH LOGIN`, Buffer.from(user).toString('base64'), Buffer.from(pass).toString('base64'), `MAIL FROM:<${user}>`, `RCPT TO:<${to}>`, `DATA`, [ `From: "小瓶RPA" ${user}`, `To: ${to}`, `Subject: =?UTF-8?B?${Buffer.from(subject).toString('base64')}?=`, `Content-Type: text/plain; charset=utf-8`, ``, `${content}`, `.` ].join('\r\n'), `QUIT` ]; let step = 0; let responseBuffer = ''; client.on('data', (data) => { responseBuffer += data; if (/(\n|\r\n)\d{3}\s/.test(data) || data.endsWith('\n')) { const code = parseInt(data.substring(0, 3)); console.log('📩 SMTP:', data.trim()); if (code >= 400) { client.end(); reject(new Error(`SMTP 错误: ${data.trim()}`)); return; } if (step < commands.length) { const cmd = commands[step++]; console.log('➡️ 发送:', cmd.split('\r\n')[0]); client.write(cmd + '\r\n'); } else { client.end(); resolve('✅ 邮件发送成功'); } } }); client.on('error', (err) => { reject(err); }); }); } exports.sendMail = sendMail exports.发送邮件 = sendMail /** * 从网络下载一个文件到本地路径 * @param {string} fileUrl 网址 * @param {string} filename 本地路径文件名 * @param {object} headersJson 请求头 Json对象 */ function downloadFile(fileUrl, filename, headersJson = {}) { const dirPath = path.dirname(filename); if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); } filename = path.resolve(filename) console.log('下载文件到:', filename) const commandArgs = [ '-o', filename, fileUrl ]; if (Object.keys(headersJson).length !== 0) { for (const [key, value] of Object.entries(headersJson)) { commandArgs.push('-H', `${key}: ${value}`); } } const result = childProcess.spawnSync(curlCommand, commandArgs, { encoding: 'utf8' }); if (result.error) { throw new Error('downloadFile 执行 curl 命令时出错' + result.error.message); } if (result.status !== 0) { throw new Error('downloadFile curl 命令执行失败' + result.stderr); } return result.stdout; } exports.downloadFile = downloadFile exports.下载文件 = downloadFile /** * 从文本到语音(TextToSpeech) 语音播报 * 非阻塞 * @param {string} text 朗读内容 */ var tts = (text) => { text = encodeURIComponent(text) let url = `${CppUrl}?action=tts&txt=${text}` // console.log(url) let res = getHtml(url) sleep(defaultDelay); } exports.tts = tts exports.文字转语音 = tts /** * 用电脑默认浏览器打开网址 * @param {string} myurl 网址 */ var openURL = (myurl) => { let clearurl = `${CppUrl}?action=setWebReadyPage` getHtml(clearurl) myurl = encodeURIComponent(myurl) let url = `${CppUrl}?action=openURL&url=${myurl}` // console.log(url) let res = getHtml(url) sleep(defaultDelay + 1000); } exports.openURL = openURL exports.打开网址 = openURL /** * 打开文件(用默认软件)或者 用资源管理器打开展示文件夹, * @param {string} filePath 文件夹绝对路径 如:'c:/input/RPAlogo128.png' Windows磁盘路径分隔符要双 '/' */ var openDir = (filePath) => { filePath = path.resolve(filePath) filePath = encodeURIComponent(filePath) let url = `${CppUrl}?action=openDir&path=${filePath}` // console.log(url) let res = getHtml(url) sleep(defaultDelay); } exports.openDir = openDir exports.openfile = openDir exports.打开目录 = openDir exports.打开文件 = openDir /** * 获取当前屏幕分辨率和缩放 * @returns {{w:number,h:number,ratio:number}} JSON内容格式 {w:1920,h:1080,ratio:1.5} ratio 为桌面缩放比例 */ var getResolution = () => { let url = `${CppUrl}?action=getResolution` // console.log(url) let res = getHtml(url) return JSON.parse(res); } exports.getResolution = getResolution exports.获取屏幕分辨率 = getResolution /** * 文字识别 OCR已经从经典算法升级为AI模型预测,永久免费可脱网使用 * * @param {string} imagePath 空或者screen 为电脑屏幕; 或者本地图片的绝对路径; * @param {number} x 可选 查找起始点 * @param {number} y 可选 查找起始点 * @param {number} width 可选 宽度范围 * @param {number} height 可选 高度范围 * @returns {{text:string,score:number,x:number,y:number}} AI OCR识别的json结果 包含准确率的评分和中点位置 格式: [{text:'A',score:'0.319415',x:100,y:200},...] xy相对于原点 */ var aiOcr = (imagePath = "screen", x = 0, y = 0, width = -1, height = -1) => { if (!imagePath) { imagePath = "screen" } if (x < 0 || y < 0) { throw new Error(`错误:OCR 起始点不能为负,x:${x} y:${y} `); } if (x != 0 || y != 0 || width != -1 || height != -1) { showRect(x, y, width, height); } if (imagePath != 'screen') { // use absolute path for local image files imagePath = path.resolve(imagePath) imagePath = encodeURIComponent(imagePath) } let url = `${CppUrl}?action=aiOcr&path=${imagePath}&x=${x}&y=${y}&width=${width}&height=${height}&onlyEn=0` // console.log(url) let res = getHtml(url) if (res == '文字识别引擎未启动') { console.log('⚠', res, '请在软件设置中开启'); exit() } let jsons = JSON.parse(res); for (const json of jsons) { json.x += x json.y += y } return jsons; } exports.aiOcr = aiOcr exports.文字识别 = aiOcr /** * AI 物体识别 已经从经典算法升级为AI模型预测,企业版可脱网使用 V2024.8 以上版本有效 * 调试:软件根目录会生成 debug/Ai_ObjectDetect.png 文件 * * @param {number} minimumScore 相似度阈值 * @param {number} x 可选 查找范围 * @param {number} y 可选 查找范围 * @param {number} width 可选 查找宽度 * @param {number} height 可选 查找高度 * @returns {array} AI 物体识别的 json 结果 包含准确率的评分 格式: [{x:100,y:100,width:150,height:150,score:0.86,class:'分类名'},...] xy相对于原点 */ var aiObject = (minimumScore = 0.5, x = 0, y = 0, width = -1, height = -1) => { if (x < 0 || y < 0) { throw new Error(`错误:OCR 起始点不能为负,x:${x} y:${y} `); } if (x != 0 || y != 0 || width != -1 || height != -1) { showRect(x, y, width, height); } let url = `${CppUrl}?action=aiObject&minimumScore=${minimumScore}&x=${x}&y=${y}&width=${width}&height=${height}&onlyEn=0` // console.log(url) let res = getHtml(url) if (res == '物体识别引擎未启动') { console.log('⚠', res, '请在软件设置中开启'); exit() } let jsons = JSON.parse(res); for (const json of jsons) { json.x += x json.y += y showRect(json.x, json.y, json.width, json.height, 'green'); } return jsons; } exports.aiObject = aiObject exports.物体识别 = aiObject /** * 压缩文件夹内容成一个zip文件包 v2025.0 以后版本生效 * @param {string} directory 文件夹路径,输入绝对路径 * @param {string} zipFilePath zip文件包 */ function zipDir(directory, zipFilePath = "") { if (!zipFilePath) { zipFilePath = path.resolve(directory, 'RPA生成的压缩包.zip') } try { zipFilePath = path.resolve(zipFilePath) directory = path.resolve(directory) let exe = path.resolve(`${basePath}/bin/7za`) const os = process.platform; if (os === 'linux') { exe = '7za' } childProcess.execSync(`"${exe}" a "${zipFilePath}" "${directory}"`, { stdio: ['ignore', 'ignore', 'pipe'], encoding: 'utf8' }) } catch (error) { if (!error.stderr.includes('Headers Error')) { //warning console.error(`压缩失败`, error); } } } exports.zipDir = zipDir exports.压缩 = zipDir /** * 解压缩zip文件内容到指定文件夹内 v2025.0 以后版本生效 * @param {string} zipFilePath zip文件包 * @param {string} directory 文件夹路径,输入绝对路径 默认解压到zip文件当前目录 */ function unZip(zipFilePath, directory = "") { if (!directory) { directory = path.dirname(zipFilePath) } try { let filePath = path.resolve(zipFilePath) directory = path.resolve(directory) let exe = path.resolve(`${basePath}/bin/7za`) const os = process.platform; if (os === 'linux') { exe = '7za' } childProcess.execSync(`"${exe}" x "${filePath}" -o"${directory}" -aoa`, { stdio: ['ignore', 'ignore', 'pipe'], encoding: 'utf8' }) } catch (error) { console.error(`解压缩失败`, error); } } exports.unZip = unZip exports.解压缩 = unZip /** * 获取buffer存储内容 * 此buffer可以跨脚本存取,RPA重启时才重置,存取多线程下安全 * http外部获取方式:http://ip:49888/action=bufferGet&n=0 * @param {number} n buffer编号,从0-9共10个 默认:0 第一个buffer * @returns {string} 返回字符串 */ var bufferGet = (n = 0) => { let url = `${CppUrl}?action=bufferGet&n=${n}` let res = getHtml(url) return res; } exports.bufferGet = bufferGet /** * 设置buffer存储内容 * 此buffer可以跨脚本存取,RPA重启时才重置,存取多线程下安全 * http外部设置方式(POST方法):http://ip:49888/action=bufferSet&n=0 ,content设置到Post的body中 * @param {string} content 存储的内容,通常为一个json,也可以字符串 * @param {number} n buffer编号,从0-9共10个 默认:0 第一个buffer * @returns {string} ok 表示成功 */ var bufferSet = (content, n = 0) => { let url = `${CppUrl}?action=bufferSet&n=${n}` let res = postJson(url, content); return res; } exports.bufferSet = bufferSet /** * 设置接力执行的脚本 * 当前脚本结束后(无论正常结束还是错误退出),立刻启动的自动脚本。 * http外部设置方式(GET方法):http://ip:49888/action=pbottleRPA_delay&path=MyPATH * @param {string} scriptPath 接力脚本的路径 如:'D:/test.mjs' 如果路径为空,默认清除当前已经设置的接力任务。 * @returns {string} ok 表示成功 */ var delaySet = (scriptPath = '') => { scriptPath = path.resolve(scriptPath) scriptPath = encodeURIComponent(scriptPath) let url = `${CppUrl}?action=pbottleRPA_delay&path=${scriptPath}` let res = getHtml(url) return res } exports.delaySet = delaySet /** * 获取当前的设备唯一号 * @returns {string} 返回字符串 */ function deviceID() { let url = `${CppUrl}?action=pbottleRPA_deviceID` let res = getHtml(url) return res } exports.deviceID = deviceID /** * 获取 * @returns {string} 返回字符串 */ function clusterCenter() { let url = `${CppUrl}?action=pbottleRPA_clusterCenter` let res = getHtml(url) return res } exports.clusterCenter = clusterCenter /** * 小瓶RPA 云端模块,AI在线大模型 * 注意: * ①此模块不是必须模块 ,云端模块不影响本地模块的独立运行 * ②此模块功能需要登录并激活云端模块。碍于成本因素,部分功能需要充值计费才能使用 */ exports.cloud = {} /** * @typedef {Object} Answerinfo AI回答结果 * @property {string} content - 回答结果 * @property {number} usage - 消耗token数量 */ /** * @typedef {Object} AiOptions AI输入选项 * @property {string} response_format 云端模型输出格式,默认:"text",可选 "json_object" JSON格式 * @property {number} temperature 模型温度,默认:0.75,取值范围 [0-2). * @property {boolean} enable_search false|true 联网搜素开关,默认关闭,开启增加token消耗。开启后,只会根据问题自动判断是否联网,可以在问题中添加联网搜素关键词,如:"联网搜素:xxxx" */ /** * 小瓶RPA整合的云端大语言答案生成模型 * @param {string} question 提问问题,如:'今天是xx日,你能给我写首诗吗?' * @param {number} modelLevel 模型等级,不同参数大小不同定价,默认 0 为标准模型。0为低价模型;1为性价比模型;2为旗舰高智能模型; * @param {AiOptions} options AI输入选项 * @returns {Answerinfo} JSON内容格式 {content:'结果',tokens:消耗token的数量} */ function cloud_GPT(question, modelLevel = 0, options = { response_format: 'text', temperature: 0.75, enable_search: false, }) { let deviceToken = deviceID() if (question.length < 3) { throw new Error('❌ 错误:问题过短,请输入至少2个字符') } let rs = postJson('https://rpa.pbottle.com/API/', { question, deviceToken, modelLevel, options }) // console.log(rs); let json = JSON.parse(rs) if (json.error) { throw new Error('❌ 错误:' + json.error) } return json } exports.cloud_GPT = cloud_GPT exports.cloud.GPT = cloud_GPT /** * 小瓶RPA整合的云端图像分析大模型 * @param {string} question 提问问题,如:'分析这个图片的内容' * @param {string} imagePath 上传图片的路径,如:'c:/test.jpg' * @param {number} modelLevel 模型等级,不同参数大小不同定价,默认 0 为标准模型。 * @returns {Answerinfo} JSON内容格式 {content:'结果',tokens:消耗token的数量} */ function cloud_GPTV(question, imagePath, modelLevel = 0) { let deviceToken = deviceID() imagePath = path.resolve(imagePath) if (!fs.existsSync(imagePath)) { throw new Error('❌输入分析图片不存在:cloud_GPTV') } let tempJsonFile = homePath + '/cloud_GPTV.json' let image_base64 = fs.readFileSync(imagePath).toString('base64') fs.writeFileSync(tempJsonFile, JSON.stringify({ question, deviceToken, modelLevel, image_base64 })) let rs = postJsonFile('https://rpa.pbottle.com/API/gptv', tempJsonFile); let json = JSON.parse(rs) if (json.error) { console.log('❌ 错误 cloud_GPTV', json.error, rs) throw new Error(json.error) } return json } exports.cloud_GPTV = cloud_GPTV exports.cloud.GPTV = cloud_GPTV /** * 小瓶RPA整合的云端图像分析大模型,直接操作屏幕 * @param {string} action '点击'|'双击'|'右键' * @param {string} question 提问问题,如:'分析这个图片的内容' * @returns */ function cloud_GPTA(action = '点击', question = "桌面微信图标") { let deviceToken = deviceID() let tempScreenShoot = homePath + '/cloud_GPT_do.png' let tempJsonFile = homePath + '/cloud_GPTV.json' screenShot(tempScreenShoot) let image_base64 = 'data:image/png;base64,' + fs.readFileSync(tempScreenShoot).toString('base64') fs.writeFileSync(tempJsonFile, JSON.stringify({ question, deviceToken, image_base64 })) let rs = postJsonFile('https://rpa.pbottle.com/API/gpta', tempJsonFile); let json = JSON.parse(rs) if (json.error) { console.log('❌ 错误 cloud_GPTA', json.error, rs) throw new Error(json.error) } console.log(json); let boxs = json.content.split('\n') for (let index = 0; index < boxs.length; index++) { const box = boxs[index]; if (!box) { continue } let box4 = JSON.parse(box) let resolution = getResolution() box4[0] = box4[0] / 1000 * resolution.w box4[1] = box4[1] / 1000 * resolution.h box4[2] = box4[2] / 1000 * resolution.w box4[3] = box4[3] / 1000 * resolution.h showRect(box4[0], box4[1], box4[2] - box4[0], box4[3] - box4[1], 'green') let x = Math.round((box4[0] + box4[2]) / 2) let y = Math.round((box4[1] + box4[3]) / 2) console.log(question + '的位置', x, y); moveMouseSmooth(x, y) if (action == '点击') { mouseClick('left') } else if (action == '双击') { mouseDoubleClick() } else if (action == '右键') { mouseClick('right') } } } exports.cloud_GPTA = cloud_GPTA exports.cloud.GPTA = cloud_GPTA /** * 小瓶RPA 浏览器增强命令 * 注意: * ①此模块不是必须模块 * ②此模块功能需要安装小瓶RPA浏览器增强插件:https://rpa.pbottle.com/a-13942.html */ exports.browserCMD = {} /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * 警告框 * @param {string} msg 显示文本内容 * @returns {string} 正常返回 'ok' */ var browserCMD_alert = function (msg) { let action = 'alert'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return res } exports.browserCMD_alert = browserCMD_alert; exports.browserCMD.alert = browserCMD_alert /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * 关闭浏览器标签页。打开新标签页用 pbottleRPA.openURL() * @param {string} 关闭类型 'current':默认关闭当前标签页; 'other':关闭其他标签页 * @returns {string} 正常返回 'ok' */ var browserCMD_closeTab = function (type = 'current') { let action = 'closeTab'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return res } exports.browserCMD_closeTab = browserCMD_closeTab exports.browserCMD.closeTab = browserCMD_closeTab /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * fetch请求网址,从当前页面发起ajax请求并返回响应结果 https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch * 默认 20 秒超时 * @param {string} fetch_url 网址 * @param {object} options 请求参数 * @returns {string} 响应结果 */ var browserCMD_fetch = function (fetch_url, options = {}) { let action = 'fetch'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return res } exports.browserCMD_fetch = browserCMD_fetch exports.browserCMD.fetch = browserCMD_fetch /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * 等待页面加载完成,返回页面网址 * 默认 20 秒超时 * @param {string} readyURL 页面加载完成后的网址 * @param {number} timeout 超时时间,单位秒 * @returns {string} 返回当前浏览器的url网址 或者错误退出 */ var browserCMD_waitPageReady = function (readyURL,timeout = 20) { let url = `${CppUrl}?action=getWebReadyPage` for (let index = 0; index < timeout; index++) { let res = getHtml(url) // console.log("结果:",res); if (res == readyURL) { return res }else{ sleep(1000); console.log(`等待页面加载完成...`); } } throw new Error('waitPageReady 等待页面加载超时') } exports.browserCMD_waitPageReady = browserCMD_waitPageReady exports.browserCMD.waitPageReady = browserCMD_waitPageReady /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * @param {string} urlStr 当前网页转向新网址,默认为空获取当前网址 【小瓶RPA浏览器增强插件V2023.8以上生效】 * @returns {string} 返回当前浏览器的url网址 或者 ok */ var browserCMD_url = function (urlStr = undefined) { let action = 'url'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return res } exports.browserCMD_url = browserCMD_url; exports.browserCMD.url = browserCMD_url /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * 元素数量 参考 jQuery 选择器 * @param {string} selector 元素选择器 * @returns {number} 返回选择元素的数量,最优的选择结果是1 */ var browserCMD_count = function (selector) { let action = 'count'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) let resStr = res if (isNumeric(resStr)) { return parseInt(resStr) } else { return 0 } } exports.browserCMD_count = browserCMD_count; exports.browserCMD.count = browserCMD_count /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * 模拟点击 参考 jQuery click() 方法,改为浏览器 native 的 click() 并自动获取焦点 * @param {string} selector 元素选择器。如果选择多个元素,只触发第一个元素的click事件 * @param {object} options 点击选项 可选 如:{ bubbles: false, ctrlKey: true} https://developer.mozilla.org/zh-CN/docs/Web/API/MouseEvent/MouseEvent * @returns {string} */ var browserCMD_click = function (selector) { let action = 'click'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return res } exports.browserCMD_click = browserCMD_click; exports.browserCMD.click = browserCMD_click; /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * 模拟双击 参考 jQuery dblclick() 方法,改为浏览器 native 的 click() 并自动获取焦点 * @param {string} selector 元素选择器。如果选择多个元素,只触发第一个元素的click事件 * @param {object} options 点击选项 可选 如:{ bubbles: false, ctrlKey: true} https://developer.mozilla.org/zh-CN/docs/Web/API/MouseEvent/MouseEvent * @returns {string} */ var browserCMD_dblclick = function (key) { let action = 'dblclick'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return res } exports.browserCMD_dblclick = browserCMD_dblclick; exports.browserCMD.dblclick = browserCMD_dblclick; /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * 显示元素 参考 jQuery show() 方法 * @param {string} selector 元素选择器 * @returns {string} */ var browserCMD_show = function (selector) { let action = 'show'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return res } exports.browserCMD_show = browserCMD_show; exports.browserCMD.show = browserCMD_show; /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * 隐藏元素 参考 jQuery hide() 方法 * @param {string} selector 元素选择器 * @returns {string} */ var browserCMD_hide = function (selector) { let action = 'hide'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return res } exports.browserCMD_hide = browserCMD_hide; exports.browserCMD.hide = browserCMD_hide; /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 2024.0 以上版本生效 * 获取元素定位,相对浏览器文档左上角 参考 jQuery offset() 方法 * @param {string} selector 元素选择器 * @returns {{left:number,top:number}} 返回 json:{"left":100,"top":100} 位置位元素的左上角顶点坐标 */ var browserCMD_offset = function (selector) { let action = 'offset'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return JSON.parse(res) } exports.browserCMD_offset = browserCMD_offset; exports.browserCMD.offset = browserCMD_offset; /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * 移除元素 参考 jQuery remove() 方法 * @param {string} selector 元素选择器 * @returns {string} */ var browserCMD_remove = function (selector) { let action = 'remove'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return res } exports.browserCMD_remove = browserCMD_remove; exports.browserCMD.remove = browserCMD_remove; /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * 获取或者设置文本 参考 jQuery text() 方法 * @param {string} selector 元素选择器 * @param {string} content 可选 * @returns {string} 选择多个元素时会返回一个数组 */ var browserCMD_text = function (selector, content = undefined) { let action = 'text'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return res } exports.browserCMD_text = browserCMD_text; exports.browserCMD.text = browserCMD_text; /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * 获取或者设置html 参考 jQuery html() 方法 * @param {string} selector 元素选择器 * @param {string} content 可选 * @returns {string} 选择多个元素时会返回一个数组 */ var browserCMD_html = function (selector, content = undefined) { let action = 'html'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return res } exports.browserCMD_html = browserCMD_html; exports.browserCMD.html = browserCMD_html; /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * 获取或设置值 input select等 参考 jQuery val() 方法 * @param {string} selector 元素选择器 * @param {string} content 可选,值 * @returns {string} 选择多个元素时会返回一个数组 */ var browserCMD_val = function (selector, content = undefined) { let action = 'val'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return res } exports.browserCMD_val = browserCMD_val; exports.browserCMD.val = browserCMD_val; /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * 获取或设置当前站点的 cookie * @param {string} cName cookie 名称 * @param {string} cValue cookie 值 留空为获取cookie的值 * @param {number} expDays cookie 过期时间,单位:天, 留空为会话cookie * @returns {string} 返回 cookie的值 */ var browserCMD_cookie = function (cName, cValue = undefined, expDays = undefined) { let action = 'cookie'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return res } exports.browserCMD_cookie = browserCMD_cookie; exports.browserCMD.cookie = browserCMD_cookie /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * 获取或设置css样式 参考 jQuery css() 方法 * @param {string} selector 元素选择器 * @param {string} propertyname 名 * @param {string} value 值 * @returns */ var browserCMD_css = function (selector, propertyname, value = undefined) { let action = 'css'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return res } exports.browserCMD_css = browserCMD_css; exports.browserCMD.css = browserCMD_css /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * 获取或设置attr属性 参考 jQuery attr() 方法 * @param {string} selector 元素选择器 * @param {string} propertyname 属性名 * @param {string} value 值 * @returns {string} */ var browserCMD_attr = function (selector, propertyname, value = undefined) { let action = 'attr'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return res } exports.browserCMD_attr = browserCMD_attr; exports.browserCMD.attr = browserCMD_attr /** * 浏览器增强命令 需要安装小瓶RPA的浏览器拓展 * 获取或设置prop属性 参考 jQuery prop() 方法 * @param {string} selector 元素选择器 * @param {string} propertyname 属性名 * @param {string} value 值 * @returns {string} */ var browserCMD_prop = function (selector, propertyname, value = undefined) { let action = 'prop'; let [...args] = arguments; let url = `${CppUrl}?action=webInject&jscode=` + encodeURIComponent(JSON.stringify({ action, args })) let res = getHtml(url) return res } exports.browserCMD_prop = browserCMD_prop; exports.browserCMD.prop = browserCMD_prop /** * 等待屏幕上的图片出现 * @param {string|Array} tpPath 图片模板路径 相对路径:./image/123.png | 数组等待多个图片 * @param {Function} intervalFun 检测间隔的操作,function格式 * @param {number} timeOut 可选,等待超时时间 单位秒 默认30秒 * @param {number} miniSimilarity 可选,指定最低相似度,默认0.85。取值0-1,1为找到完全相同的。 * @returns {position|boolean} 结果的位置信息,json格式:{x,y} 相对于屏幕左上角原点 */ function waitImage(tpPath, intervalFun = () => { }, timeOut = 30, miniSimilarity = 0.85) { console.log('waitImage', tpPath); for (let index = 0; index < timeOut; index++) { sleep(1000) let position = findScreen(tpPath, miniSimilarity) if (position !== false) { return position; } if (typeof intervalFun === 'function' && intervalFun() == 'stopWait') { console.log('stopWait from intervalFun'); return false } } //debug 保存当前屏幕 console.log('已经保存超时截图到:我的图片'); screenShot(); //error throw new Error(`waitImage 等待图片超时 ${tpPath}`) } exports.waitImage = waitImage; exports.等待图像出现 = waitImage; /** * 等待屏幕上的图片消失 * @param {string} tpPath 图片模板路径 相对路径:./image/123.png * @param {function} intervalFun 检测间隔的操作,function格式 * @param {number} timeOut 可选,等待超时时间 单位秒 默认30秒 * @param {number} miniSimilarity 可选,指定最低相似度,默认0.85。取值0-1,1为找到完全相同的。 * @returns {string|boolean} */ function waitImageDisappear(tpPath, intervalFun = () => { }, timeOut = 30, miniSimilarity = 0.85) { console.log('waitImageDisappear', tpPath); for (let index = 0; index < timeOut; index++) { sleep(1000) let position = findScreen(tpPath, miniSimilarity) if (position === false) { return 'ok'; } if (typeof intervalFun === 'function' && intervalFun() == 'stopWait') { console.log('stopWait from intervalFun'); return false } } //debug 保存当前屏幕 console.log('已经保存超时截图到:我的图片'); screenShot(); //error throw new Error(`waitImageDisappear 等待图片消失超时 ${tpPath}`) } exports.waitImageDisappear = waitImageDisappear; exports.等待图像消失 = waitImageDisappear; /** * 等待文件下载成功或者生成 * @param {string} dirPath 监控文件夹目录 如:'c:/User/pbottle/download' * @param {string} keyWords 过滤关键词 如:'.zip' * @param {function} intervalFun 检测间隔的操作,function格式 * @param {number} timeOut 等待超时时间 单位秒 * @returns {string[]} */ function waitFile(dirPath, keyWords = '', intervalFun = () => { }, timeOut = 30) { console.log('waitFile', dirPath, keyWords); for (let index = 0; index < timeOut; index++) { sleep(1000) let rs = searchFile(dirPath, keyWords) if (hasData(rs)) { return rs; } if (typeof intervalFun === 'function' && intervalFun() == 'stopWait') { console.log('stopWait from intervalFun'); return false } } //error throw new Error(`waitFile 等待文件超时: ${dirPath}`) } exports.waitFile = waitFile; exports.等待文件 = waitFile; /** * 等待文件消失或者被删除 * @param {string} dirPath 监控文件夹目录 如:'c:/User/pbottle/download' * @param {string} keyWords 过滤关键词 如:'.crdownload' * @param {function} intervalFun 检测间隔的操作,function格式 * @param {number} timeOut 等待超时时间 单位秒 * @returns {string[]} */ function waitFileDisappear(dirPath, keyWords = '', intervalFun = () => { }, timeOut = 30) { console.log('waitFileDisappear', dirPath, keyWords); for (let index = 0; index < timeOut; index++) { sleep(1000) let rs = searchFile(dirPath, keyWords) if (!hasData(rs)) { return true; } if (typeof intervalFun === 'function' && intervalFun() == 'stopWait') { console.log('stopWait from intervalFun'); return false } } //error let frame = new Error().stack.split("\n")[2]; // change to 3 for grandparent func throw new Error(`waitFileDisappear 等待文件消失错误: ${dirPath} ${frame}`) } exports.waitFileDisappear = waitFileDisappear; exports.等待文件消失 = waitFileDisappear; /** * 等待输入 V2026.0.0 新增 * @param {string} inputPrompt 输入提示词 * @param {number} timeOut 可选,等待超时时间 单位秒 默认600秒 * @returns {string} 输入内容 默认返回空字符串 */ function waitInput(inputPrompt = '输入提示词', timeOut = 600) { console.log('waitInput 等待用户输入:', inputPrompt); inputPrompt = encodeURIComponent(inputPrompt) let url = `${CppUrl}?action=waitInput&inputPrompt=${inputPrompt}` let res = getHtml(url) for (let index = 0; index < timeOut; index++) { sleep(1000) let rs = getHtml(`${CppUrl}?action=waitInputResult`) if (hasData(rs)) { showMsg('用户输入了:', rs) return rs; } else { continue; } } } exports.waitInput = waitInput; exports.等待输入 = waitInput; /** * 小瓶RPA 硬件键鼠模拟接口 * 注意: * ①此模块不是必须模块 * ②此模块功能需要添加电脑硬件外设,购买装配请咨询小瓶RPA客服 */ exports.hid = {} /** * 模拟按键触发事件 (硬件级) * @param {string} key 按键名称参考:https://www.pbottle.com/a-13862.html * @param {string} upDown 默认按下down,up松开按键 * @returns */ let hid_keyToggle = (key, upDown) => { let upDown_n = 0; if (upDown == 'up') { upDown_n = 2; } let key_n = keycode(key) if (key_n === undefined) { console.log(`⚠ 按键 ${key} 不存在!~`); return } let url = `${CppUrl}?action=keyToggleHardWare&key_n=${key_n}&upDown_n=${upDown_n}` // console.log(url) let res = getHtml(url) return res; } exports.hid.keyToggle = hid_keyToggle /** * 按一下键盘(硬件级) 支持组合按键 加号连接 如: keyTap('ctrl + alt + del') * @param {string} key 按键名称参考:https://www.pbottle.com/a-13862.html */ let hid_keyTap = (key) => { if (key.includes('+')) { let subkeys = new Array(); subkeys = key.split('+') subkeys = subkeys.map((value) => { return value.trim() }) for (let index = 0; index < subkeys.length; index++) { const element = subkeys[index]; hid_keyToggle(element, "down") } subkeys = subkeys.reverse() for (let index = 0; index < subkeys.length; index++) { const element = subkeys[index]; hid_keyToggle(element, "up") } } else { hid_keyToggle(key, "down") hid_keyToggle(key, "up") } sleep(defaultDelay); } exports.hid.keyTap = hid_keyTap /** * 基础鼠标命令 全部为零释放 * @param {number} button 按键 1,2,4 代表鼠标的 左键,右键,中键。 * @param {number} x 按键时候移动的位置,绝对位置 x=100:向右移动 100像素,负数向左 * @param {number} y 按键时候移动的位置,拖拽相对位置 y=100:向下移动 100像素,负数向上 * @param {number} mouseWheel 滚动齿轮数 正数向下,负数向下 * @param {number} time 按下到释放时间 * @returns */ let hid_mouseCMD = (button = 1, x = 0, y = 0, mouseWheel = 0, time = 10) => { let url = `${CppUrl}?action=mouseDataHardWare&bt=${button}&x=${x}&y=${y}&wheel=${mouseWheel}&time=${time}` // console.log(url) let res = getHtml(url) return res; } /** * 移动鼠标到指定位置 起点为屏幕左上角 屏幕绝对位置(硬件分辨率) * @param {number} x 横坐标 * @param {number} y 纵坐标 * @returns */ let hid_moveMouse = (x, y) => { hid_mouseCMD(0, x, y, 0, 10) } exports.hid.moveMouse = hid_moveMouse /** * 当前位置点击鼠标 默认左键 * @param {string} 鼠标的按键选择 left right middle 可选 ,默认左键 * @param {number} 点按时间 单位毫秒 可选 * @returns */ let hid_mouseClick = (button = 'left', time = 10) => { let bt = 1 switch (button) { case 'right': bt = 2 break; case 'middle': bt = 4 break default: bt = 1 break; } hid_mouseCMD(bt, 0, 0, 0, time) hid_mouseCMD(0, 0, 0, 0, 0) sleep(defaultDelay); } exports.hid.mouseClick = hid_mouseClick /** * 移动鼠标到指定位置并点击 * @param {number} x 横坐标 * @param {number} y 纵坐标 */ let hid_moveAndClick = (x, y) => { hid_moveMouse(x, y) hid_mouseClick() } exports.hid.moveAndClick = hid_moveAndClick /** * 双击鼠标 左键 * @returns */ let hid_mouseDoubleClick = () => { hid_mouseCMD(1, 0, 0, 0, 10) hid_mouseCMD(0, 0, 0, 0, 0) hid_mouseCMD(1, 0, 0, 0, 10) hid_mouseCMD(0, 0, 0, 0, 0) sleep(defaultDelay); } exports.hid.mouseDoubleClick = hid_mouseDoubleClick /** * 鼠标左键拖到一段位置 * @param {number} x 位置 * @param {number} y 位置 * @returns */ let hid_mouseLeftDragTo = (x, y) => { hid_mouseCMD(1, 0, 0, 0, 10) hid_mouseCMD(1, x, y, 0, 10) hid_mouseCMD(0, 0, 0, 0, 0) sleep(defaultDelay); } exports.hid.mouseLeftDragTo = hid_mouseLeftDragTo /** * 鼠标左键拖到一段位置 * @param {number} x 位置 * @param {number} y 位置 * @returns */ let hid_mouseRightDragTo = (x, y) => { // use hid_mouseCMD (hardware mouse command) instead of undefined mouseCMD hid_mouseCMD(2, 0, 0, 0, 10) hid_mouseCMD(2, x, y, 0, 10) hid_mouseCMD(0, 0, 0, 0, 0) sleep(defaultDelay); } exports.hid.mouseRightDragTo = hid_mouseRightDragTo /** * 鼠标滚轮 * @param {number} data 滚动的量 默认为-1 向下滚动一个齿轮; 正数向上滚动; * @returns */ let hid_mouseWheel = (data = -1) => { hid_mouseCMD(0, 0, 0, data, 0) hid_mouseCMD(0, 0, 0, 0, 0) sleep(defaultDelay); } exports.hid.mouseWheel = hid_mouseWheel /** * 公共工具类,一般和模拟操作没有直接关系的方法。 用法:pbottleRPA.utils.function(parameters) or pbottleRPA.function(parameters) * 持续添加常用工具,为流程提供快捷方法。 */ exports.utils = {} exports.工具箱 = {} /** * 常用工具 * 判断是否为数字化变量(包含数字化的字符串) * @param {*} value 任意类型变量 * @returns {boolean} */ function isNumeric(value) { return !isNaN(parseFloat(value)) && isFinite(value); } exports.isNumeric = isNumeric; exports.是否数字 = isNumeric; exports.utils.isNumeric = isNumeric; exports.工具箱.是否数字 = isNumeric; /** * 常用工具 * 判断变量中是否有数据,直接if()可用。 * 非零数字 或 非空字符串、数组、对象 返回 true,其他都返回 false * @param {*} value 任意类型变量 * @returns {boolean} */ function hasData(value) { // console.log(value); if (value === null || value === undefined) { return false; } if (typeof value === 'string' && value.trim().length === 0) { return false; } if (Array.isArray(value) && value.length === 0) { return false; } if (typeof value === 'number' && (value === 0 || isNaN(value))) { return false; } if (typeof value === 'bigint' && value === 0n) { return false; } if (typeof value === 'boolean') { return value; } if (typeof value === 'symbol' || typeof value === 'function') { return false; } if (typeof value === 'object' && Object.keys(value).length === 0) { return false; } return true; } exports.hasData = hasData; exports.是否有内容 = hasData; exports.utils.hasData = hasData; exports.工具箱.是否有内容 = hasData; /** * 常用工具 * 格式化的时间 getTime('Y-m-d H:i:s') 输出类似 "2023-09-17 14:30:45" 的日期时间字符串 * @param {string} format 格式参考 https://www.runoob.com/php/php-date.html 仅支持 Y|y|m|d|H|i|s|n|j * @param {number} timestamp 时间戳秒 * @returns {string} */ function getTime(format = 'Y-m-d H:i:s', timestamp = null) { // 如果没有提供 timestamp,使用当前时间 const date = timestamp ? new Date(timestamp * 1000) : new Date(); // 映射 PHP 的日期格式到 JavaScript 的日期方法 const formatMap = { 'Y': date.getFullYear(), // 4位数的年份 'y': (date.getFullYear() % 100).toString().padStart(2, '0').slice(-2), // 2位数的年份 'm': ('0' + (date.getMonth() + 1)).slice(-2), // 月份,01-12 'd': ('0' + date.getDate()).slice(-2), // 日期,01-31 'H': ('0' + date.getHours()).slice(-2), // 24小时制的小时,00-23 'i': ('0' + date.getMinutes()).slice(-2), // 分钟,00-59 's': ('0' + date.getSeconds()).slice(-2), // 秒,00-59 'n': date.getMonth() + 1, // 月份,1-12,没有前导零 'j': date.getDate(), // 日期,1-31,没有前导零 }; // 替换格式字符串中的占位符 return format.replace(/Y|y|m|d|H|i|s|n|j/g, (matched) => formatMap[matched]); } exports.getTime = getTime; exports.获取格式化时间 = getTime; exports.utils.getTime = getTime; exports.工具箱.获取格式化时间 = getTime; /** * 常用工具 * 根据关键字定位具体文件 * @param {string} directory 目录绝对路径 * @param {string} words 文件名包含的关键字,过滤词,默认忽略大小写 * @param {boolean} recursive 是否递归深入目录子目录查找 ,默认false * @returns {string[]} 文件路径 || [] 未找到 */ function searchFile(directory, words = '', recursive = false) { let rs = [] //全局结果 // 读取目录内容 directory = path.resolve(directory) let files = fs.readdirSync(directory) // console.log('files',files); // 遍历每个文件 words = words.toLowerCase() files.forEach((file) => { let filePath = path.resolve(directory, file); if (recursive) { //判断子目录 let stats = fs.statSync(filePath); if (stats.isDirectory()) { rsTemp = searchFile(filePath, words, recursive) rs.push(...rsTemp) } } // console.log(filePath); if (filePath.toLowerCase().includes(words)) { rs.push(filePath) } }); return rs; } exports.searchFile = searchFile; exports.搜索文件 = searchFile; exports.utils.searchFile = searchFile; exports.工具箱.搜索文件 = searchFile; /** * 常用工具 * 生成唯一符串 注意:默认只是毫秒级的 * @param {string} prefix 前缀 * @param {boolean} moreEntropy 是否开启更精细的随机,如果还不能满足请使用uuid * @returns {string} */ function uniqid(prefix = '', moreEntropy = false) { let timestamp = Date.now().toString(36); // 将时间戳转换为36进制 let randomStr = ''; if (moreEntropy) { // 如果需要更多的熵,则添加一些随机字符 randomStr = Math.random().toString(36).substring(2); } return prefix + timestamp + randomStr; } exports.uniqid = uniqid; exports.唯一数 = uniqid; exports.utils.uniqid = uniqid; exports.工具箱.唯一数 = uniqid; /** * 常用工具 * 根据起始关键词,截取一部分字符串 * @param {string} str 检索目标 * @param {string} from 开始关键词 不包含本身 空表示从头部开始 * @param {string} to 结束关键词 不包含本身 空表示到结尾结束 * @returns {string} */ function substringFromTo(str, from = '', to = '') { let fromIndex = str.indexOf(from) + from.length let toIndex = str.lastIndexOf(to) if (fromIndex == -1 || toIndex == -1) { console.log('⚠substringFromTo 没有关键词:', from, to); return '' } if (!from) { fromIndex = 0 } if (!to) { toIndex = str.length } let rs = str.substring(fromIndex, toIndex); return rs } exports.substringFromTo = substringFromTo exports.截取文本 = substringFromTo exports.utils.substringFromTo = substringFromTo exports.工具箱.截取文本 = substringFromTo //检测入口文件 if (process.argv[1] === __filename) { console.log('当前文件不能执行', "请直接执行中文名的脚本文件"); showMsg('当前文件不能执行', "请直接执行中文名的脚本文件"); process.exit(1); } //检测 win10 以下系统 curl 命令是否存在 const isWindows = process.platform === 'win32'; let command; if (isWindows) { command = 'where curl'; } else { command = 'which curl'; } try { childProcess.execSync(command, { encoding: 'utf8' }); } catch (error) { console.log('⚠️ 系统 curl 命令不存在,使用集成 curl'); curlCommand = basePath + '/bin/curl.exe'; // process.exit(1); } ================================================ FILE: python示例/GPT图像解析示例.py ================================================ """ 小瓶RPA演示demo,具体api请查看*流程开发文档* 官网:https://rpa.pbottle.com/ 流程开发文档:https://rpa.pbottle.com/docs/ 功能说明:此脚本演示了RPA中的GPT图像解析功能,可以向云端AI提问关于图片内容的问题 通过这个示例,您可以学习如何结合AI能力分析和理解图片内容 """ import time import pbottleRPA # 引入小瓶RPA的核心库,获得对RPA功能的访问权限 # 开始RPA操作 ask = "描述图片中有什么?" # 定义要向AI提出的问题 print(ask, "./input/RPAlogo128.png") # 在控制台输出问题和要分析的图片路径 start = time.time() # 记录开始时间,用于计算处理耗时 pbottleRPA.log("云端 AI 生成答案:") # 将提示信息输出到日志文件中 # 调用云端GPT图像分析API,传入问题和图片路径,并输出结果内容 print(pbottleRPA.cloud.GPTV(ask, "./input/RPAlogo128.png")["content"]) print( "图片解析耗时:(毫秒)", (time.time() - start) * 1000 ) # 计算并输出图片解析耗时(毫秒) ================================================ FILE: python示例/GPT问题答案AI生成演示.py ================================================ """ 小瓶RPA python版本(Beta) https://gitee.com/pbottle/pbottle-rpa 示例 """ import pbottleRPA #引入小瓶RPA模块 asks = [ '鲁迅为什么要打周树人?', '给我随便作一首诗吧', '你是谁?', ] for index,ask in enumerate(asks): print(f'问题 {index+1}:',ask); pbottleRPA.tts(ask) rs = pbottleRPA.cloud.GPT(ask) pbottleRPA.log('云端 AI 生成答案:',rs['content']) pbottleRPA.log('------------') pbottleRPA.log() # end ================================================ FILE: python示例/WEB增强-数据批量爬取演示.py ================================================ """ 小瓶RPA python版本(Beta) https://gitee.com/pbottle/pbottle-rpa 示例 """ import pbottleRPA #引入小瓶RPA模块 import time import json print("=== WEB增强插件-浏览器数据库批量爬取演示 ===") current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) print(current_time) print("=== ※※※※※※※※※ ==="); print("=== 需要安装 小瓶RPA 浏览器插件 ==="); print("=== ※※※※※※※※※ ==="); pbottleRPA.showMsg('提示:','必须先安装浏览器增强插件') #打开被获取数据的网页 pbottleRPA.sleep(5*1000) pbottleRPA.openURL('https://rpa.pbottle.com/') pbottleRPA.sleep(5*1000) pbottleRPA.keyTap('page down') pbottleRPA.keyTap('page down') pbottleRPA.keyTap('page down') #开始获取网页上的数据 rs = pbottleRPA.browserCMD.text('a.list-group-item') if rs == '20s超时': pbottleRPA.showMsg('出现错误:','必须先安装浏览器增强插件和联网') pbottleRPA.exit() datas = json.loads(rs) print('爬取数据数量:',len(datas)) pbottleRPA.tts('爬取数据'+ str(len(datas)) +'条,请查看日志') pbottleRPA.sleep(1000*4) print('数据列表:') for element in datas: element = element.strip().replace('\r', '').replace('\n', '') print(element); pbottleRPA.tts('演示结束') ================================================ FILE: python示例/WEB增强-浏览器元素操作演示.py ================================================ """ 小瓶RPA python版本(Beta) https://gitee.com/pbottle/pbottle-rpa 示例 """ import pbottleRPA #引入小瓶RPA模块 import time print("=== WEB增强插件-浏览器元素操作演示 ===") current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) print(current_time) print("=== ※※※※※※※※※ ===") print("=== 需要安装 小瓶RPA 浏览器插件 ===") print("=== ※※※※※※※※※ ===") pbottleRPA.tts('必须安装小瓶RPA浏览器增强插件,手动点击确定继续') pbottleRPA.showMsg('提示:','必须先安装浏览器增强插件') pbottleRPA.openURL('https://www.baidu.com') ret = pbottleRPA.browserCMD.alert('来自小瓶RPA的问候,手动点击确定开始,20秒超时') print('返回操作结果',ret) if (ret != 'ok'): print('没有检测到小瓶RPA浏览器插件',ret) pbottleRPA.exit(1) #延迟1秒 pbottleRPA.sleep(1000*1) ret = pbottleRPA.browserCMD.text('span.title-content-title') print('返回操作结果【一次多个】',ret) ret = pbottleRPA.browserCMD.cookie('BAIDUID') print('返回操作结果 cookieGet',ret) ret = pbottleRPA.browserCMD.cookie('pbottleID',"good",3) print('返回操作结果 cookieSet',ret) pbottleRPA.tts('变换背景色') ret = pbottleRPA.browserCMD.css('body',"background-color",'blue') print('返回操作结果 cssSet',ret) ret = pbottleRPA.browserCMD.css('body',"background-color") print('返回操作结果【颜色值】',ret) ret = pbottleRPA.browserCMD.css('body',"background-color",'white') print('返回操作结果 cssSet',ret) ret = pbottleRPA.browserCMD.text('title') print('返回操作结果 textGet',ret) pbottleRPA.tts('获取标题 ') pbottleRPA.sleep(1000*3) pbottleRPA.tts('设置页面标题 ') ret = pbottleRPA.browserCMD.text('title','[小瓶RPA]-'+ret) print('返回操作结果 textSet',ret) ret = pbottleRPA.browserCMD.text('title') print('当前页面标题:',ret) pbottleRPA.sleep(1000*3) pbottleRPA.tts('输入搜索词 点击搜索按钮 ') ret = pbottleRPA.browserCMD.val('#kw','小瓶RPA') print('返回点击操作结果 valSet',ret) ret = pbottleRPA.browserCMD.click('#su') print('返回点击操作结果 click',ret) pbottleRPA.sleep(1000*3) pbottleRPA.tts('开始去广告') ret = pbottleRPA.browserCMD.remove('#content_left div:first') print('返回点击操作结果 remove',ret) pbottleRPA.sleep(3000) pbottleRPA.tts('打开网站') pbottleRPA.browserCMD.click('div#content_left a:first') pbottleRPA.sleep(1500) pbottleRPA.tts('读取 logo 路径,显示到日志') ret = pbottleRPA.browserCMD.attr('img:first','src') print('网站logo图片地址',ret) pbottleRPA.sleep(1500) pbottleRPA.tts('演示完成准备退出') print("准备结束脚本") ret = pbottleRPA.browserCMD.alert('演示结束') #脚本强制退出 pbottleRPA.exit() print("已经退出了,无效") ================================================ FILE: python示例/WEB增强-账号密码登录演示.py ================================================ """ 小瓶RPA python版本(Beta) https://gitee.com/pbottle/pbottle-rpa 示例 """ import pbottleRPA #引入小瓶RPA模块 import time print("=== WEB增强插件-账号密码登录演示 ===") current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) print(current_time) print("=== ※※※※※※※※※ ==="); print("=== 需要安装 小瓶RPA 浏览器插件 ==="); print("=== ※※※※※※※※※ ==="); pbottleRPA.tts('必须安装小瓶RPA浏览器增强插件,手动点击确定继续') pbottleRPA.showMsg('提示:','必须先安装浏览器增强插件') pbottleRPA.openURL('https://yun.pbottle.com/?from=rpademo') ret = pbottleRPA.browserCMD.alert('来自小瓶RPA的问候,手动点击确定开始,20秒超时') print('返回操作结果',ret); if (ret != 'ok'): print('没有检测到小瓶RPA浏览器插件',ret); pbottleRPA.exit(1) #点击登录按钮 pbottleRPA.browserCMD.click("a[role='button']:contains(登录或注册)") pbottleRPA.sleep(2000) pbottleRPA.browserCMD.click("a[role='button']:contains(用帐号密码登录)") pbottleRPA.sleep(1000) #输入账号密码 pbottleRPA.browserCMD.click("input[name='uname']") pbottleRPA.browserCMD.val("input[name='uname']",'test') pbottleRPA.browserCMD.click("input[name='pwd']") pbottleRPA.browserCMD.val("input[name='pwd']",'123456') pbottleRPA.sleep(1000) #登录按钮 pbottleRPA.browserCMD.click("button:contains(登录帐号)") pbottleRPA.sleep(3000) pbottleRPA.keyTap('enter') pbottleRPA.tts('演示结束') ================================================ FILE: python示例/[企业版]接力执行脚本.py ================================================ """ 小瓶RPA python版本(Beta) https://gitee.com/pbottle/pbottle-rpa 示例 """ import pbottleRPA #引入小瓶RPA模块 import time pbottleRPA.delaySet(__file__) #自己接力自己 pbottleRPA.log('等待3秒') pbottleRPA.wait(3) pbottleRPA.log('完成') ================================================ FILE: python示例/[第三方] 读写Excel演示脚本.py ================================================ """ 小瓶RPA演示demo,具体api请查看*流程开发文档* 官网:https://rpa.pbottle.com/ 流程开发文档:https://rpa.pbottle.com/docs/ """ import sys import os import platform import json import time # 添加父目录到路径以导入pbottleRPA sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import pbottleRPA as rpa # 尝试导入openpyxl库 try: import openpyxl from openpyxl.styles import Font except ImportError: rpa.showMsg("请先安装第三方模块", "运行: pip install openpyxl") rpa.tts("请先安装第三方模块" + "运行: pip install openpyxl") rpa.exit("请先安装第三方模块" + "运行: pip install openpyxl") def excel_append(filename, line=None): """ Excel 文件追加一行数据 Args: filename (str): 文件绝对路径 line (list): 行数据 """ if line is None: line = [] print("excel追加行", filename, line) # 读取excel workbook = openpyxl.load_workbook(filename) sheet = workbook.active # excel 重新追加一行记录到已有的excel文件末尾 sheet.append(line) # 保存 workbook.save(filename) print("=== Excel 读写测试 ===") print(rpa.getTime()) rpa.tts("Excel 读写测试") rpa.wait(3) rpa.tts("将当前电脑配置信息生成EXCEL文件") rpa.wait(5) # 生成excel文档 workbook = openpyxl.Workbook() sheet = workbook.active sheet.title = "pbottleRPA" # 添加表头 sheet.append(["项", "值"]) # 设置第一行粗体 for cell in sheet[1]: cell.font = Font(bold=True) # 设置列宽 sheet.column_dimensions["A"].width = 30 sheet.column_dimensions["B"].width = 60 # 添加数据行 sheet.append( [ "时间", rpa.getTime(), ] ) sheet.append( [ "显示器分辨率", json.dumps(rpa.getResolution()), ] ) sheet.append( [ "CPU", platform.processor() or "Unknown", ] ) # 保存文件 excel_path = os.path.join( os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "Excel测试表格.xlsx" ) workbook.save(excel_path) rpa.tts("已经生成EXCEL测试表格...请查看") rpa.openDir(os.path.dirname(excel_path)) # 追加一条数据 excel_append(excel_path, ["项名", "重新追加值"]) rpa.wait(5) # 读取excel workbook2 = openpyxl.load_workbook(excel_path) sheet2 = workbook2.active # 获取所有数据 values = [] for row in sheet2.iter_rows(values_only=True): values.append(row) print(values) rpa.tts("已经读取EXCEL测试表格到日志") if __name__ == "__main__": pass ================================================ FILE: python示例/[第三方] 读写word演示脚本.py ================================================ """ 小瓶 RPA 演示 demo,具体 api 请查看*流程开发文档* 官网:https://rpa.pbottle.com/ 流程开发文档:https://rpa.pbottle.com/docs/ """ import sys import os import time # 添加父目录到路径以导入 pbottleRPA sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import pbottleRPA as rpa # 尝试导入 python-docx 和 mammoth 库 try: from docx import Document from docx.shared import Pt from docx.enum.text import WD_ALIGN_PARAGRAPH import mammoth except ImportError: rpa.showMsg("请先安装第三方模块", "运行:pip install python-docx mammoth") rpa.tts("请先安装第三方模块" + "运行:pip install python-docx mammoth") rpa.exit("请先安装第三方模块" + "运行:pip install python-docx mammoth") print("=== Word 后台读写测试 ===") print(rpa.getTime()) rpa.tts("Word 后台读写测试") rpa.wait(3) rpa.tts("将后台生成 Word 文件") rpa.wait(5) # 生成 Word 文档 doc = Document() # 添加标题段落 heading = doc.add_heading("标题文字", level=1) heading.alignment = WD_ALIGN_PARAGRAPH.CENTER run = heading.runs[0] run.bold = True run.font.size = Pt(20) # 添加普通段落 p = doc.add_paragraph() run1 = p.add_run("小瓶 RPA 官网:") run1.font.size = Pt(12) # 添加链接样式的文本 hyperlink_run = p.add_run("https://rpa.pbottle.com") hyperlink_run.font.size = Pt(12) hyperlink_run.font.underline = True # 添加更多示例内容 doc.add_paragraph() doc.add_paragraph("这是一个 Python 版本的 Word 读写演示。") doc.add_paragraph("小瓶 RPA 支持多种自动化操作,包括:") doc.add_paragraph("• 鼠标键盘操作", style="List Bullet") doc.add_paragraph("• 图像识别", style="List Bullet") doc.add_paragraph("• AI 能力集成", style="List Bullet") doc.add_paragraph("• 文件操作", style="List Bullet") # 保存文件 word_path = os.path.join( os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "Word测试文档.docx" ) doc.save(word_path) rpa.openDir(os.path.dirname(word_path)) rpa.tts("已经生成 Word测试文档...请查看") rpa.wait(3) # 读取 Word 文档 rpa.tts("将后台读取 Word 文件 显示到日志") rpa.wait(3) # 使用 mammoth 读取文本内容 with open(word_path, "rb") as f: result = mammoth.extract_raw_text(f) print("读取 Word 文档内容:", result.value) rpa.tts("已经读取 Word测试文档到日志") rpa.wait(2) if __name__ == "__main__": pass ================================================ FILE: python示例/pbottleRPA.py ================================================ """ 小瓶RPA python版本(Beta) https://gitee.com/pbottle/pbottle-rpa 官网:https://rpa.pbottle.com/ Nodejs 移植兼容版 beta 注:目前已完成 NodeJS 版本 API 同步 js -> python 对照表: console.log -> print 日志 json -> {} json 字典 `` -> f"" 字符串模板 encodeURIComponent -> urlencode """ import time import json import sys import io import zipfile import os import inspect import urllib.request import urllib.parse import subprocess import base64 import tempfile import smtplib import ssl import random from email.header import Header from email.mime.text import MIMEText from email.utils import formataddr # ========== 全局配置 ========== sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", line_buffering=True) sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", line_buffering=True) pyPath = os.path.dirname(os.path.abspath(__file__)) basePath = os.environ.get("RPAbaseDir", "") homePath = os.environ.get("RPAhomeDir", "") CppUrl = "http://127.0.0.1:49888/" defaultDelay = 1000 print("基座服务地址:(Python)", CppUrl, "Python版本已同步NodeJS API") # ========== 自定义异常 ========== class RPAError(Exception): """RPA 操作异常基类""" pass class TimeoutError(RPAError): """等待超时异常""" pass # ========== 工具函数 ========== def urlencode(input_str): """JS兼容的URL编码,等价于 encodeURIComponent""" return urllib.parse.quote(input_str, safe="") def isNumeric(value): """ 判断是否为数字(包含数字字符串) @param value: 任意类型变量 @return: bool """ try: float(value) return True except (ValueError, TypeError): return False def hasData(value): """ 判断变量是否包含有效数据(非零数字、非空字符串/列表/字典/对象等) @param value: 任意类型变量 @return: bool """ if value is None: return False if isinstance(value, str) and value.strip() == "": return False if isinstance(value, (list, tuple, dict)) and len(value) == 0: return False if isinstance(value, (int, float)) and (value == 0 or value != value): # NaN check return False if isinstance(value, bool): return value return True def getTime(format_str="Y-m-d H:i:s", timestamp=None): """ 格式化时间,支持 Y/y/m/d/H/i/s/n/j @param format_str: 格式字符串,如 'Y-m-d H:i:s' @param timestamp: 可选,Unix时间戳(秒) @return: 格式化后的时间字符串 """ if timestamp: date = time.localtime(timestamp) else: date = time.localtime() mapping = { "Y": date.tm_year, "y": str(date.tm_year)[-2:], "m": f"{date.tm_mon:02d}", "d": f"{date.tm_mday:02d}", "H": f"{date.tm_hour:02d}", "i": f"{date.tm_min:02d}", "s": f"{date.tm_sec:02d}", "n": date.tm_mon, "j": date.tm_mday, } result = format_str for k, v in mapping.items(): result = result.replace(k, str(v)) return result def searchFile(directory, words="", recursive=False): """ 根据关键字搜索文件(递归可选) @param directory: 目录绝对路径 @param words: 文件名包含的关键字(忽略大小写) @param recursive: 是否递归子目录 @return: 匹配的文件路径列表 """ directory = os.path.abspath(directory) result = [] words = words.lower() try: for root, dirs, files in os.walk(directory): if not recursive and root != directory: break for f in files: full_path = os.path.join(root, f) if words in full_path.lower(): result.append(full_path) except Exception: pass return result def uniqid(prefix="", moreEntropy=False): """ 生成唯一ID(毫秒级) @param prefix: 前缀字符串 @param moreEntropy: 是否增加随机熵 @return: 唯一ID字符串 """ timestamp = format(int(time.time() * 1000), "x") rand = "" if moreEntropy: rand = format(random.randint(0, 0xFFFFFFFF), "x") return prefix + timestamp + rand def substringFromTo(s, from_str="", to_str=""): """ 根据起始关键词截取字符串(不包含关键词本身) @param s: 原字符串 @param from_str: 开始关键词,空表示从头部开始 @param to_str: 结束关键词,空表示到结尾结束 @return: 截取后的子串 """ start = s.find(from_str) + len(from_str) if from_str else 0 end = s.rfind(to_str) if to_str else len(s) if (from_str and start == -1 + len(from_str)) or (to_str and end == -1): print(f"⚠substringFromTo 没有关键词: {from_str} -> {to_str}") return "" return s[start:end] # ========== 基础操作 ========== def setDefaultDelay(millisecond): """ 设置RPA模拟操作的全局延时(鼠标、键盘、粘贴、打开网页等) @param millisecond: 毫秒数,默认1000 """ global defaultDelay defaultDelay = millisecond def sleep(milliseconds): """ 脚本暂停等待(毫秒),使用 Python 自带延时机制,一次等待上限 2 分钟 @param milliseconds: 毫秒数 """ if milliseconds < 1: return if milliseconds >= 120000: print("警告:一次等待上限时长两分钟内") seconds = milliseconds / 1000.0 time.sleep(seconds) def wait(seconds=1): """ 脚本暂停等待(秒),支持小数,超过100秒会自动分段等待 @param seconds: 秒数,默认1 """ if seconds <= 0 or not isNumeric(seconds): print("pbottleRPA.wait:seconds input error") return if seconds > 100: quotient = int(seconds // 100) for _ in range(quotient): sleep(100 * 1000) print("提示:已等待100s...") seconds = seconds % 100 sleep(seconds * 1000) def beep(): """发出系统警告声音""" urllib.request.urlopen(f"{CppUrl}?action=beep") def showMsg(title, content): """ 系统原生消息提示(右下角弹窗) @param title: 标题 @param content: 内容 """ title = urlencode(title) content = urlencode(content) urllib.request.urlopen(f"{CppUrl}?action=showMsg&title={title}&content={content}") def showRect(fromX=0, fromY=0, width=500, height=500, color="red", msec=500): """ 在屏幕上显示彩色方框(用于调试定位) @param fromX: 起始X坐标 @param fromY: 起始Y坐标 @param width: 宽度 @param height: 高度 @param color: 颜色 red/green/blue/yellow @param msec: 显示毫秒数 """ fromX = int(round(fromX)) fromY = int(round(fromY)) width = int(round(width)) height = int(round(height)) color = urlencode(color) urllib.request.urlopen( f"{CppUrl}?action=showRect&fromX={fromX}&fromY={fromY}&width={width}&height={height}&color={color}&msec={msec}" ) def exit_script(*args): """ 强制退出当前脚本 @param *args: 退出时输出的信息 """ if args: print(*args) beep() sys.exit(1) # 避免与内置 exit 冲突,同时保留原名 def exit(*args): """ 强制退出当前脚本 @param *args: 退出时输出的信息 """ exit_script(*args) def kill(processName, force=False): """ 关闭指定进程(Windows taskkill) @param processName: 进程名称,如 'WINWORD.EXE' @param force: 是否强制结束 """ force_flag = "/F" if force else "" try: subprocess.run( f"taskkill {force_flag} /IM {processName}", shell=True, check=True, capture_output=True, ) print(f"关闭进程成功:{processName}") except subprocess.CalledProcessError: print(f"关闭进程({processName})失败,可能软件未运行") def moveMouseSmooth(x, y, interval=0): """ 平滑移动鼠标到指定位置(屏幕左上角为原点) @param x: 横坐标 @param y: 纵坐标 @param interval: 像素间隔时间(毫秒),越大移动越慢,默认0 """ x = int(round(x)) y = int(round(y)) urllib.request.urlopen(f"{CppUrl}?action=moveMouse&x={x}&y={y}&interval={interval}") sleep(defaultDelay) # 别名 moveMouse = moveMouseSmooth def moveAndClick(x, y): """ 移动鼠标到指定位置并点击左键 @param x: 横坐标 @param y: 纵坐标 """ moveMouseSmooth(x, y) mouseClick() def mouseClick(leftRight="left", time_ms=30): """ 当前位置点击鼠标 @param leftRight: 'left' 或 'right' @param time_ms: 点按时间(毫秒) """ action = "mouseLeftClick" if leftRight != "right" else "mouseRightClick" urllib.request.urlopen(f"{CppUrl}?action={action}&time={time_ms}") sleep(defaultDelay) def mouseDoubleClick(): """双击鼠标左键""" urllib.request.urlopen(f"{CppUrl}?action=mouseDoubleClick") sleep(defaultDelay) def mouseWheel(data=-720): """ 鼠标滚轮 @param data: 滚动量,负数向下,正数向上,默认-720 """ urllib.request.urlopen(f"{CppUrl}?action=mouseWheel&data={data}") sleep(defaultDelay) def mouseLeftDragTo(x, y): """ 鼠标左键拖拽到指定位置 @param x: 目标X坐标 @param y: 目标Y坐标 """ x = int(round(x)) y = int(round(y)) urllib.request.urlopen(f"{CppUrl}?action=mouseLeftDragTo&x={x}&y={y}") sleep(defaultDelay) def mouseRightDragTo(x, y): """ 鼠标右键拖拽到指定位置 @param x: 目标X坐标 @param y: 目标Y坐标 """ x = int(round(x)) y = int(round(y)) urllib.request.urlopen(f"{CppUrl}?action=mouseRightDragTo&x={x}&y={y}") sleep(defaultDelay) def getScreenColor(x, y): """ 获取屏幕某点颜色 @param x: 横坐标 @param y: 纵坐标 @return: 颜色值,如 '#000000' """ resp = urllib.request.urlopen(f"{CppUrl}?action=getScreenColor&x={x}&y={y}") return json.loads(resp.read().decode())["rs"] def screenShot(savePath="", x=0, y=0, w=-1, h=-1): """ 屏幕截图 @param savePath: 保存路径(应以.png结尾),默认保存到“我的图片” @param x: 截图起始X @param y: 截图起始Y @param w: 宽度,-1表示全屏 @param h: 高度,-1表示全屏 @return: 返回结果字符串 """ if savePath: savePath = os.path.abspath(savePath) savePath = urlencode(savePath) x, y, w, h = int(x), int(y), int(w), int(h) if x != 0 or y != 0 or w != -1 or h != -1: showRect(x, y, w, h) resp = urllib.request.urlopen( f"{CppUrl}?action=screenShot&savePath={savePath}&x={x}&y={y}&w={w}&h={h}" ) return resp.read().decode() def keycode(name): """ 按键名称转虚拟键码(与 JS 版本完全对齐) @param name: 按键名称(参考 https://www.pbottle.com/a-13862.html) @return: 键码整数 """ name = name.strip().lower() mapping = { "backspace": 8, "tab": 9, "enter": 13, "shift": 16, "ctrl": 17, "alt": 18, "pause/break": 19, "caps lock": 20, "esc": 27, "space": 32, "page up": 33, "page down": 34, "end": 35, "home": 36, "left": 37, "up": 38, "right": 39, "down": 40, "insert": 45, "delete": 46, "command": 91, "left command": 91, "right command": 93, "numpad *": 106, "numpad +": 107, "numpad -": 109, "numpad .": 110, "numpad /": 111, "num lock": 144, "scroll lock": 145, "my computer": 182, "my calculator": 183, "windows": 91, "⇧": 16, "⌥": 18, "⌃": 17, "⌘": 91, "ctl": 17, "control": 17, "option": 18, "pause": 19, "break": 19, "caps": 20, "return": 13, "escape": 27, "spc": 32, "spacebar": 32, "pgup": 33, "pgdn": 34, "ins": 45, "del": 46, "cmd": 91, "f1": 112, "f2": 113, "f3": 114, "f4": 115, "f5": 116, "f6": 117, "f7": 118, "f8": 119, "f9": 120, "f10": 121, "f11": 122, "f12": 123, ";": 186, "=": 187, ",": 188, "-": 189, ".": 190, "/": 191, "`": 192, "[": 219, "\\": 220, "]": 221, "'": 222, "0": 48, "1": 49, "2": 50, "3": 51, "4": 52, "5": 53, "6": 54, "7": 55, "8": 56, "9": 57, "a": 65, "b": 66, "c": 67, "d": 68, "e": 69, "f": 70, "g": 71, "h": 72, "i": 73, "j": 74, "k": 75, "l": 76, "m": 77, "n": 78, "o": 79, "p": 80, "q": 81, "r": 82, "s": 83, "t": 84, "u": 85, "v": 86, "w": 87, "x": 88, "y": 89, "z": 90, } return mapping.get(name, 0) def keyToggle(key, upDown="down"): """ 模拟键盘按键基础事件(按下或松开) @param key: 按键名称 @param upDown: 'down' 按下 / 'up' 松开 """ key_n = keycode(key) if key_n == 0: print(f"⚠ 按键 {key} 不存在!~") return upDown_n = 0 if upDown != "up" else 2 urllib.request.urlopen( f"{CppUrl}?action=keyToggle&key_n={key_n}&upDown_n={upDown_n}" ) def keyTap(key): """ 模拟键盘按键(按下并松开),支持组合键,如 'ctrl+a' @param key: 按键名称或组合键(加号连接) """ if "+" in key: parts = [p.strip() for p in key.split("+")] for p in parts: keyToggle(p, "down") for p in reversed(parts): keyToggle(p, "up") else: keyToggle(key, "down") keyToggle(key, "up") sleep(defaultDelay) def mouseKeyToggle(key="left", upDown="down"): """ 模拟鼠标按键基础事件 @param key: 'left' / 'right' / 'middle' @param upDown: 'down' 按下 / 'up' 松开 """ key_map = {"left": 0, "right": 1, "middle": 2} key_n = key_map.get(key, 0) upDown_n = 0 if upDown != "up" else 2 urllib.request.urlopen( f"{CppUrl}?action=mouseKeyToggle&key_n={key_n}&upDown_n={upDown_n}" ) def findScreen(tpPaths, miniSimilarity=0.85, fromX=0, fromY=0, width=-1, height=-1): """ 屏幕查找图像定位 @param tpPaths: 小图片路径(建议png),或图片路径列表 @param miniSimilarity: 最低相似度,0-1,默认0.85 @param fromX: 查找起始X @param fromY: 查找起始Y @param width: 搜索宽度,-1表示全屏 @param height: 搜索高度,-1表示全屏 @return: 找到返回 {'x':int, 'y':int, 'value':float},否则返回 False """ if fromX < 0 or fromY < 0: raise ValueError(f"错误:找图起始点不能为负,x:{fromX} y:{fromY}") if fromX != 0 or fromY != 0 or width != -1 or height != -1: showRect(fromX, fromY, width, height) if not isinstance(tpPaths, list): tpPaths = [tpPaths] for tpPath in tpPaths: tpPath = os.path.abspath(tpPath) tpPath = urlencode(tpPath) resp = urllib.request.urlopen( f"{CppUrl}?action=findScreen&imgPath={tpPath}&fromX={fromX}&fromY={fromY}&width={width}&height={height}" ) data = json.loads(resp.read().decode()) if "error" not in data and data.get("value", 0) >= miniSimilarity: showRect(data["x"] - 25, data["y"] - 25, 50, 50, "green") return {"x": data["x"], "y": data["y"], "value": data["value"]} return False def findText(inputTxt, fromX=0, fromY=0, width=-1, height=-1): """ 查找屏幕上的文字(基于OCR) @param inputTxt: 要查找的文字(部分匹配) @param fromX: 查找起始X @param fromY: 查找起始Y @param width: 搜索宽度 @param height: 搜索高度 @return: 找到返回 {'text':str, 'x':int, 'y':int, 'score':float},否则返回 False """ ocr_res = aiOcr("screen", fromX, fromY, width, height) for item in ocr_res: if inputTxt in item["text"]: showRect(item["x"] - 25, item["y"] - 25, 50, 50, "green") return item return False def waitText( inputTxt, fromX=0, fromY=0, width=-1, height=-1, intervalFun=None, timeOut=20 ): """ 等待屏幕指定文字出现 @param inputTxt: 搜索文字 @param fromX,fromY,width,height: 搜索范围 @param intervalFun: 回调函数,返回 'stopWait' 时停止等待 @param timeOut: 超时秒数 @return: 找到返回位置字典,超时抛出 TimeoutError """ print("waiting Text:", inputTxt) for _ in range(timeOut): sleep(1000) pos = findText(inputTxt, fromX, fromY, width, height) if pos: return pos if intervalFun and intervalFun() == "stopWait": print("stopWait from intervalFun") return False print("已经保存超时截图到:我的图片") screenShot() frame = inspect.currentframe().f_back raise TimeoutError(f"等待文字超时 {inputTxt} 位置(行):{frame.f_lineno}") def findContours(minimumArea=1000, fromX=0, fromY=0, width=-1, height=-1): """ 查找屏幕上的轮廓(物体/窗口边缘) @param minimumArea: 最小面积,默认1000(约31x31像素) @param fromX: 查找起始X @param fromY: 查找起始Y @param width: 搜索宽度 @param height: 搜索高度 @return: 轮廓列表,每个元素包含 x,y,cx,cy,area,id """ if fromX < 0 or fromY < 0: raise ValueError(f"错误:轮廓查找起始点不能为负,x:{fromX} y:{fromY}") if fromX != 0 or fromY != 0 or width != -1 or height != -1: showRect(fromX, fromY, width, height) resp = urllib.request.urlopen( f"{CppUrl}?action=findContours&minimumArea={minimumArea}&fromX={fromX}&fromY={fromY}&width={width}&height={height}" ) contours = json.loads(resp.read().decode()) for c in contours: c["x"] += fromX c["y"] += fromY return contours def imgSimilar(path1, path2, checkType="ORB"): """ 图片相似度对比 @param path1: 图片1路径 @param path2: 图片2路径 @param checkType: 算法 'SIFT'/'ORB'/'SSIM',默认 'ORB' @return: {'score':float, 'time':float} """ path1 = urlencode(os.path.abspath(path1)) path2 = urlencode(os.path.abspath(path2)) resp = urllib.request.urlopen( f"{CppUrl}?action=imgSimilar&path1={path1}&path2={path2}&checkType={checkType}" ) return json.loads(resp.read().decode()) def paste(txt): """ 在当前焦点位置粘贴输入文本(模拟 Ctrl+V) @param txt: 要输入的文本 """ copyText(txt) keyTap("ctrl+v") sleep(defaultDelay) def copyText(txt): """ 复制文本到剪贴板 @param txt: 文本内容 """ txt = urlencode(txt) urllib.request.urlopen(f"{CppUrl}?action=copyText&txt={txt}") def copyFile(filepath): """ 复制文件/文件夹到剪贴板,之后可在目标位置粘贴(如微信发送文件) @param filepath: 绝对路径 """ filepath = os.path.abspath(filepath) if not os.path.exists(filepath): print(f"copyFile警告:文件路径不存在 {filepath}") filepath = filepath.replace("\\", "/") filepath = urlencode(filepath) urllib.request.urlopen(f"{CppUrl}?action=copyFile&path={filepath}") def getClipboard(): """ 获取剪贴板内容(支持文本、图片base64、HTML) @return: 剪贴板内容字符串 """ resp = urllib.request.urlopen(f"{CppUrl}?action=getClipboard") return resp.read().decode() def wxMessage(title, content, key): """ 通过小瓶云发送微信通知(免费) @param title: 消息标题 @param content: 消息内容 @param key: 获取key详情 https://www.pbottle.com/a-12586.html """ url = f"https://yun.pbottle.com/manage/yun/?msg={urlencode(content)}&name={urlencode(title)}&key={key}" resp = urllib.request.urlopen(url) print("发送微信消息:", resp.read().decode()) def postJson(url, msgJson, headersJson=None, method="POST"): """ 向API发送JSON数据 @param url: API地址 @param msgJson: 要发送的字典对象 @param headersJson: 额外请求头字典 @param method: HTTP方法,默认POST @return: 响应文本 """ if headersJson is None: headersJson = {} data = json.dumps(msgJson).encode("utf-8") req = urllib.request.Request( url, data=data, headers={"Content-Type": "application/json", **headersJson}, method=method, ) with urllib.request.urlopen(req) as f: return f.read().decode() def postJsonFile(url, msgJsonFile, headersJson=None, method="POST"): """ 从文件读取JSON并发送到API(适合大JSON) @param url: API地址 @param msgJsonFile: JSON文件路径 @param headersJson: 额外请求头字典 @param method: HTTP方法 @return: 响应文本 """ if headersJson is None: headersJson = {} msgJsonFile = os.path.abspath(msgJsonFile) with open(msgJsonFile, "rb") as f: data = f.read() req = urllib.request.Request( url, data=data, headers={"Content-Type": "application/json", **headersJson}, method=method, ) with urllib.request.urlopen(req) as f: return f.read().decode() def getHtml(url, headersJson=None, method="GET"): """ 普通HTTP请求,返回响应文本 @param url: 网址 @param headersJson: 请求头字典 @param method: HTTP方法 @return: 响应文本 """ if headersJson is None: headersJson = {} req = urllib.request.Request(url, headers=headersJson, method=method) with urllib.request.urlopen(req) as f: return f.read().decode() def downloadFile(fileUrl, filename, headersJson=None): """ 下载文件到本地 @param fileUrl: 文件URL @param filename: 本地保存路径 @param headersJson: 请求头字典 """ if headersJson is None: headersJson = {} filename = os.path.abspath(filename) os.makedirs(os.path.dirname(filename), exist_ok=True) print("下载文件到:", filename) req = urllib.request.Request(fileUrl, headers=headersJson) with urllib.request.urlopen(req) as resp, open(filename, "wb") as out: out.write(resp.read()) def sendMail( to, subject, content, host="smtp.qq.com", port=465, user="leo191@foxmail.com", passwd="fxfqtsxmwcohbcbc", ): """ 发送邮件(同步阻塞) @param to: 收件人地址 @param subject: 主题 @param content: 正文(纯文本) @param host: SMTP服务器 @param port: 端口 @param user: 用户名 @param passwd: 密码 @return: 成功提示字符串 """ if user == "leo191@foxmail.com": content += "\n\n 请不要将演示测试邮箱用作实际业务,详细查看:https://rpa.pbottle.com/a-14106.html" msg = MIMEText(content, "plain", "utf-8") msg["From"] = formataddr(("小瓶RPA", user)) msg["To"] = formataddr(("", to)) if "@" in str(to) else to msg["Subject"] = Header(subject, "utf-8") context = ssl.create_default_context() with smtplib.SMTP_SSL(host, port, context=context) as server: server.login(user, passwd) server.sendmail(user, [to], msg.as_string()) return "✅ 邮件发送成功" def tts(text): """ 文字转语音播报(非阻塞) @param text: 要朗读的文本 """ text = urlencode(text) urllib.request.urlopen(f"{CppUrl}?action=tts&txt={text}") sleep(defaultDelay) def openURL(myurl): """ 用默认浏览器打开网址 @param myurl: 网址 """ urllib.request.urlopen(f"{CppUrl}?action=setWebReadyPage") myurl = urlencode(myurl) urllib.request.urlopen(f"{CppUrl}?action=openURL&url={myurl}") sleep(defaultDelay + 1000) def openDir(filePath): """ 用资源管理器打开文件夹或默认程序打开文件 @param filePath: 绝对路径 """ filePath = os.path.abspath(filePath) filePath = urlencode(filePath) urllib.request.urlopen(f"{CppUrl}?action=openDir&path={filePath}") sleep(defaultDelay) # 别名 openfile = openDir def getResolution(): """ 获取屏幕分辨率及缩放比例 @return: {'w':int, 'h':int, 'ratio':float} """ resp = urllib.request.urlopen(f"{CppUrl}?action=getResolution") return json.loads(resp.read().decode()) def aiOcr(imagePath="screen", x=0, y=0, width=-1, height=-1): """ AI文字识别(OCR),支持屏幕或图片文件 @param imagePath: 'screen' 或图片绝对路径 @param x: 起始X @param y: 起始Y @param width: 宽度 @param height: 高度 @return: 识别结果列表,每项含 text,score,x,y """ if not imagePath: imagePath = "screen" if x < 0 or y < 0: raise ValueError(f"错误:OCR 起始点不能为负,x:{x} y:{y}") if x != 0 or y != 0 or width != -1 or height != -1: showRect(x, y, width, height) if imagePath != "screen": imagePath = os.path.abspath(imagePath) imagePath = urlencode(imagePath) resp = urllib.request.urlopen( f"{CppUrl}?action=aiOcr&path={imagePath}&x={x}&y={y}&width={width}&height={height}&onlyEn=0" ) res = resp.read().decode() if res == "文字识别引擎未启动": print("⚠ 文字识别引擎未启动,请在软件设置中开启") exit_script() items = json.loads(res) for it in items: it["x"] += x it["y"] += y return items def aiObject(minimumScore=0.5, x=0, y=0, width=-1, height=-1): """ AI物体识别(检测常见物体) @param minimumScore: 置信度阈值,默认0.5 @param x: 起始X @param y: 起始Y @param width: 宽度 @param height: 高度 @return: 检测结果列表,每项含 x,y,width,height,score,class """ if x < 0 or y < 0: raise ValueError(f"错误:物体识别起始点不能为负,x:{x} y:{y}") if x != 0 or y != 0 or width != -1 or height != -1: showRect(x, y, width, height) resp = urllib.request.urlopen( f"{CppUrl}?action=aiObject&minimumScore={minimumScore}&x={x}&y={y}&width={width}&height={height}&onlyEn=0" ) res = resp.read().decode() if res == "物体识别引擎未启动": print("⚠ 物体识别引擎未启动,请在软件设置中开启") exit_script() items = json.loads(res) for it in items: it["x"] += x it["y"] += y showRect(it["x"], it["y"], it["width"], it["height"], "green") return items def zipDir(directory, zipFilePath=""): """ 压缩文件夹为ZIP文件(使用Python标准库zipfile) @param directory: 要压缩的文件夹路径 @param zipFilePath: 输出ZIP路径,默认在目录下生成 """ if not zipFilePath: zipFilePath = os.path.join(directory, "RPA生成的压缩包.zip") directory = os.path.abspath(directory) zipFilePath = os.path.abspath(zipFilePath) with zipfile.ZipFile(zipFilePath, "w", zipfile.ZIP_DEFLATED) as zipf: for root, dirs, files in os.walk(directory): for file in files: file_path = os.path.join(root, file) arcname = os.path.relpath(file_path, directory) zipf.write(file_path, arcname) def unZip(zipFilePath, directory=""): """ 解压ZIP文件到指定目录 @param zipFilePath: ZIP文件路径 @param directory: 解压目标目录,默认与ZIP同级 """ if not directory: directory = os.path.dirname(zipFilePath) zipFilePath = os.path.abspath(zipFilePath) directory = os.path.abspath(directory) os.makedirs(directory, exist_ok=True) with zipfile.ZipFile(zipFilePath, "r") as zipf: zipf.extractall(directory) def bufferGet(n=0): """ 获取跨脚本共享缓冲区内容(0-9共10个) @param n: 缓冲区编号 @return: 存储的字符串 """ resp = urllib.request.urlopen(f"{CppUrl}?action=bufferGet&n={n}") return resp.read().decode() def bufferSet(content, n=0): """ 设置跨脚本共享缓冲区内容 @param content: 要存储的内容(字符串) @param n: 缓冲区编号 @return: 'ok' 表示成功 """ return postJson(f"{CppUrl}?action=bufferSet&n={n}", content) def delaySet(scriptPath=""): """ 设置当前脚本结束后自动接力的脚本 @param scriptPath: 接力脚本绝对路径,为空则清除接力任务 @return: 'ok' """ if scriptPath: scriptPath = os.path.abspath(scriptPath) scriptPath = urlencode(scriptPath) resp = urllib.request.urlopen(f"{CppUrl}?action=pbottleRPA_delay&path={scriptPath}") return resp.read().decode() def deviceID(): """ 获取当前设备的唯一ID(用于云端接口) @return: 设备ID字符串 """ resp = urllib.request.urlopen(f"{CppUrl}?action=pbottleRPA_deviceID") return resp.read().decode() def clusterCenter(): """ 获取集群中心信息(企业版功能) @return: 字符串 """ resp = urllib.request.urlopen(f"{CppUrl}?action=pbottleRPA_clusterCenter") return resp.read().decode() # ========== Cloud 模块 ========== class cloud: @staticmethod def GPT(question, modelLevel=0, options=None): """ 云端大语言模型问答 @param question: 问题字符串 @param modelLevel: 0=低价模型,1=性价比,2=旗舰 @param options: 可选字典,如 {'response_format':'json_object', 'temperature':0.75, 'enable_search':False} @return: {'content':str, 'tokens':int} """ if options is None: options = { "response_format": "text", "temperature": 0.75, "enable_search": False, } if len(question) < 3: raise ValueError("问题过短,请输入至少2个字符") data = { "question": question, "deviceToken": deviceID(), "modelLevel": modelLevel, "options": options, } resp = postJson("https://rpa.pbottle.com/API/", data) result = json.loads(resp) if result.get("error"): raise RPAError(f"错误: {result['error']}") return result @staticmethod def GPTV(question, imagePath, modelLevel=0): """ 云端图像分析大模型(图片+问题) @param question: 关于图片的问题 @param imagePath: 图片本地路径 @param modelLevel: 模型等级 @return: {'content':str, 'tokens':int} """ imagePath = os.path.abspath(imagePath) if not os.path.exists(imagePath): raise FileNotFoundError("输入分析图片不存在:cloud_GPTV") with open(imagePath, "rb") as f: img_b64 = base64.b64encode(f.read()).decode() temp_json = os.path.join(tempfile.gettempdir(), "cloud_GPTV.json") with open(temp_json, "w") as f: json.dump( { "question": question, "deviceToken": deviceID(), "modelLevel": modelLevel, "image_base64": img_b64, }, f, ) resp = postJsonFile("https://rpa.pbottle.com/API/gptv", temp_json) result = json.loads(resp) if result.get("error"): raise RPAError(f"错误 cloud_GPTV: {result['error']}") return result @staticmethod def GPTA(action="点击", question="桌面微信图标"): """ 云端屏幕分析并自动执行动作(点击/双击/右键) @param action: '点击' / '双击' / '右键' @param question: 要操作的目标描述,如“桌面微信图标” """ device_token = deviceID() tmp_img = os.path.join( homePath if homePath else tempfile.gettempdir(), "cloud_GPT_do.png" ) tmp_json = os.path.join( homePath if homePath else tempfile.gettempdir(), "cloud_GPTV.json" ) screenShot(tmp_img) with open(tmp_img, "rb") as f: img_b64 = "data:image/png;base64," + base64.b64encode(f.read()).decode() with open(tmp_json, "w") as f: json.dump( { "question": question, "deviceToken": device_token, "image_base64": img_b64, }, f, ) resp = postJsonFile("https://rpa.pbottle.com/API/gpta", tmp_json) result = json.loads(resp) if result.get("error"): raise RPAError(f"错误 cloud_GPTA: {result['error']}") print(result) boxes = result["content"].split("\n") resolution = getResolution() for box_str in boxes: if not box_str.strip(): continue box = json.loads(box_str) x1 = box[0] / 1000.0 * resolution["w"] y1 = box[1] / 1000.0 * resolution["h"] x2 = box[2] / 1000.0 * resolution["w"] y2 = box[3] / 1000.0 * resolution["h"] showRect(x1, y1, x2 - x1, y2 - y1, "green") cx = int(round((x1 + x2) / 2)) cy = int(round((y1 + y2) / 2)) print(f"{question} 的位置: ({cx}, {cy})") moveMouseSmooth(cx, cy) if action == "点击": mouseClick("left") elif action == "双击": mouseDoubleClick() elif action == "右键": mouseClick("right") # ========== 浏览器增强模块 ========== class browserCMD: @staticmethod def alert(msg): """浏览器弹出警告框""" code = json.dumps({"action": "alert", "args": [msg]}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return resp @staticmethod def closeTab(type="current"): """ 关闭浏览器标签页 @param type: 'current' 关闭当前页, 'other' 关闭其他页 """ code = json.dumps({"action": "closeTab", "args": [type]}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return resp @staticmethod def fetch(fetch_url, options=None): """ 从当前页面发起fetch请求 @param fetch_url: 目标URL @param options: 请求选项字典 """ if options is None: options = {} code = json.dumps({"action": "fetch", "args": [fetch_url, options]}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return resp @staticmethod def waitPageReady(readyURL, timeout=20): """ 等待浏览器页面加载到指定URL @param readyURL: 期望的URL(完全匹配) @param timeout: 超时秒数 @return: 当前URL """ url = f"{CppUrl}?action=getWebReadyPage" for _ in range(timeout): res = getHtml(url) if res == readyURL: return res sleep(1000) print("等待页面加载完成...") raise TimeoutError("waitPageReady 等待页面加载超时") @staticmethod def url(urlStr=None): """ 获取或设置当前页面URL @param urlStr: 设置新URL时传入,不传则获取当前URL @return: 当前URL字符串 """ args = [urlStr] if urlStr is not None else [] code = json.dumps({"action": "url", "args": args}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return resp @staticmethod def count(selector): """ 统计符合选择器的元素数量 @param selector: CSS选择器 @return: 元素个数 """ code = json.dumps({"action": "count", "args": [selector]}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return int(resp) if isNumeric(resp) else 0 @staticmethod def dblclick(selector, options=None): """ 双击匹配的第一个元素 @param selector: CSS选择器 @param options: 鼠标事件选项 """ if options is None: options = {} code = json.dumps({"action": "dblclick", "args": [selector, options]}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return resp @staticmethod def offset(selector): """ 获取元素相对于文档左上角的位置 @param selector: CSS选择器 @return: {'left':int, 'top':int} """ code = json.dumps({"action": "offset", "args": [selector]}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return json.loads(resp) @staticmethod def click(selector, options=None): """ 点击匹配的第一个元素 @param selector: CSS选择器 @param options: 鼠标事件选项 """ if options is None: options = {} code = json.dumps({"action": "click", "args": [selector, options]}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return resp @staticmethod def show(selector): """显示匹配的元素(修改display样式)""" code = json.dumps({"action": "show", "args": [selector]}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return resp @staticmethod def hide(selector): """隐藏匹配的元素""" code = json.dumps({"action": "hide", "args": [selector]}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return resp @staticmethod def remove(selector): """从DOM中移除匹配的元素""" code = json.dumps({"action": "remove", "args": [selector]}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return resp @staticmethod def text(selector, content=None): """ 获取或设置元素的纯文本内容 @param selector: CSS选择器 @param content: 设置文本时传入,不传则获取文本 @return: 文本内容(多个元素返回数组) """ args = [selector] if content is None else [selector, content] code = json.dumps({"action": "text", "args": args}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return resp @staticmethod def html(selector, content=None): """ 获取或设置元素的HTML内容 @param selector: CSS选择器 @param content: 设置HTML时传入 """ args = [selector] if content is None else [selector, content] code = json.dumps({"action": "html", "args": args}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return resp @staticmethod def val(selector, content=None): """ 获取或设置表单元素的值 @param selector: CSS选择器 @param content: 设置值时传入 """ args = [selector] if content is None else [selector, content] code = json.dumps({"action": "val", "args": args}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return resp @staticmethod def cookie(cName, cValue=None, expDays=None): """ 获取或设置cookie @param cName: cookie名称 @param cValue: 设置时传入值,不传则获取 @param expDays: 过期天数,不传则为会话cookie """ args = [cName] if cValue is not None: args.append(cValue) if expDays is not None: args.append(expDays) code = json.dumps({"action": "cookie", "args": args}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return resp @staticmethod def css(selector, propertyname, value=None): """ 获取或设置CSS样式 @param selector: CSS选择器 @param propertyname: 样式属性名 @param value: 设置时传入值 """ args = ( [selector, propertyname] if value is None else [selector, propertyname, value] ) code = json.dumps({"action": "css", "args": args}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return resp @staticmethod def attr(selector, propertyname, value=None): """ 获取或设置元素属性 @param selector: CSS选择器 @param propertyname: 属性名 @param value: 设置时传入值 """ args = ( [selector, propertyname] if value is None else [selector, propertyname, value] ) code = json.dumps({"action": "attr", "args": args}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return resp @staticmethod def prop(selector, propertyname, value=None): """ 获取或设置DOM属性(如checked, disabled) @param selector: CSS选择器 @param propertyname: 属性名 @param value: 设置时传入值 """ args = ( [selector, propertyname] if value is None else [selector, propertyname, value] ) code = json.dumps({"action": "prop", "args": args}) resp = getHtml(f"{CppUrl}?action=webInject&jscode={urlencode(code)}") return resp # ========== 硬件级 hid 模块 ========== class hid: @staticmethod def keyToggle(key, upDown="down"): """硬件级按键按下/松开""" key_n = keycode(key) if key_n == 0: print(f"⚠ 按键 {key} 不存在!~") return upDown_n = 0 if upDown != "up" else 2 urllib.request.urlopen( f"{CppUrl}?action=keyToggleHardWare&key_n={key_n}&upDown_n={upDown_n}" ) @staticmethod def keyTap(key): """硬件级按键(按下并松开),支持组合键""" if "+" in key: parts = [p.strip() for p in key.split("+")] for p in parts: hid.keyToggle(p, "down") for p in reversed(parts): hid.keyToggle(p, "up") else: hid.keyToggle(key, "down") hid.keyToggle(key, "up") sleep(defaultDelay) @staticmethod def _mouseCMD(button=1, x=0, y=0, wheel=0, time_ms=10): """硬件级鼠标底层命令""" urllib.request.urlopen( f"{CppUrl}?action=mouseDataHardWare&bt={button}&x={x}&y={y}&wheel={wheel}&time={time_ms}" ) @staticmethod def moveMouse(x, y): """硬件级移动鼠标到绝对坐标""" hid._mouseCMD(0, int(round(x)), int(round(y)), 0, 10) @staticmethod def mouseClick(button="left", time_ms=10): """硬件级鼠标点击""" bt = {"left": 1, "right": 2, "middle": 4}.get(button, 1) hid._mouseCMD(bt, 0, 0, 0, time_ms) hid._mouseCMD(0, 0, 0, 0, 0) sleep(defaultDelay) @staticmethod def moveAndClick(x, y): """硬件级移动并点击""" hid.moveMouse(x, y) hid.mouseClick() @staticmethod def mouseDoubleClick(): """硬件级双击左键""" hid.mouseClick("left", 10) hid.mouseClick("left", 10) sleep(defaultDelay) @staticmethod def mouseLeftDragTo(x, y): """硬件级左键拖拽""" hid._mouseCMD(1, 0, 0, 0, 10) hid._mouseCMD(1, int(round(x)), int(round(y)), 0, 10) hid._mouseCMD(0, 0, 0, 0, 0) sleep(defaultDelay) @staticmethod def mouseRightDragTo(x, y): """硬件级右键拖拽""" hid._mouseCMD(2, 0, 0, 0, 10) hid._mouseCMD(2, int(round(x)), int(round(y)), 0, 10) hid._mouseCMD(0, 0, 0, 0, 0) sleep(defaultDelay) @staticmethod def mouseWheel(data=-1): """硬件级滚轮,-1向下滚动一格""" hid._mouseCMD(0, 0, 0, data, 0) hid._mouseCMD(0, 0, 0, 0, 0) sleep(defaultDelay) # ========== 等待函数 ========== def waitImage(tpPath, intervalFun=None, timeOut=30, miniSimilarity=0.85): """ 等待屏幕上的图片出现(每1秒检测一次) @param tpPath: 图片模板路径 相对路径:./image/123.png | 列表等待多个图片 @param intervalFun: 每次检测间隔调用的函数,若返回 'stopWait' 则提前结束 @param timeOut: 超时秒数 @param miniSimilarity: 最低相似度 @return: 找到返回位置字典,超时抛出 TimeoutError """ print("waitImage", tpPath) for _ in range(timeOut): sleep(1000) pos = findScreen(tpPath, miniSimilarity) if pos: return pos if intervalFun and intervalFun() == "stopWait": print("stopWait from intervalFun") return False print("已经保存超时截图到:我的图片") screenShot() frame = inspect.currentframe().f_back raise TimeoutError(f"等待图片超时 {tpPath} 位置(行):{frame.f_lineno}") def waitImageDisappear(tpPath, intervalFun=None, timeOut=30, miniSimilarity=0.85): """ 等待屏幕上的图片消失 @param tpPath: 图片模板路径 @param intervalFun: 检测间隔回调 @param timeOut: 超时秒数 @param miniSimilarity: 相似度阈值 @return: 'ok' 表示消失,超时抛出 TimeoutError """ print("waitImageDisappear", tpPath) for _ in range(timeOut): sleep(1000) pos = findScreen(tpPath, miniSimilarity) if not pos: return "ok" if intervalFun and intervalFun() == "stopWait": print("stopWait from intervalFun") return False print("已经保存超时截图到:我的图片") screenShot() frame = inspect.currentframe().f_back raise TimeoutError(f"等待图片消失超时 {tpPath} 位置(行):{frame.f_lineno}") def waitFile(dirPath, keyWords="", intervalFun=None, timeOut=30): """ 等待指定目录下出现包含关键词的文件 @param dirPath: 监控目录 @param keyWords: 文件名包含的关键词 @param intervalFun: 检测间隔回调 @param timeOut: 超时秒数 @return: 文件路径列表,超时抛出 TimeoutError """ print("waitFile", dirPath, keyWords) for _ in range(timeOut): sleep(1000) files = searchFile(dirPath, keyWords) if hasData(files): return files if intervalFun and intervalFun() == "stopWait": print("stopWait from intervalFun") return False frame = inspect.currentframe().f_back raise TimeoutError(f"等待文件超时:{dirPath} 位置(行):{frame.f_lineno}") def waitFileDisappear(dirPath, keyWords="", intervalFun=None, timeOut=30): """ 等待指定目录下包含关键词的文件消失 @param dirPath: 监控目录 @param keyWords: 文件名关键词 @param intervalFun: 检测间隔回调 @param timeOut: 超时秒数 @return: True 表示消失,超时抛出 TimeoutError """ print("waitFileDisappear", dirPath, keyWords) for _ in range(timeOut): sleep(1000) files = searchFile(dirPath, keyWords) if not hasData(files): return True if intervalFun and intervalFun() == "stopWait": print("stopWait from intervalFun") return False frame = inspect.currentframe().f_back raise TimeoutError(f"等待文件消失错误:{dirPath} 位置(行):{frame.f_lineno}") def waitInput(inputPrompt="输入提示词", timeOut=600): """ 等待用户输入(弹出输入框) @param inputPrompt: 提示文字 @param timeOut: 超时秒数,默认600秒 @return: 用户输入的字符串,超时返回空字符串 """ print("waitInput 等待用户输入:", inputPrompt) inputPrompt = urlencode(inputPrompt) getHtml(f"{CppUrl}?action=waitInput&inputPrompt={inputPrompt}") for _ in range(timeOut): sleep(1000) res = getHtml(f"{CppUrl}?action=waitInputResult") if hasData(res): showMsg("用户输入了:", res) return res return "" # ========== 工具箱(utils)命名空间 ========== class utils: isNumeric = isNumeric hasData = hasData getTime = getTime searchFile = searchFile uniqid = uniqid substringFromTo = substringFromTo 工具箱 = utils # ========== 中文别名 ========== 设置默认操作延时 = setDefaultDelay 蜂鸣声 = beep 显示系统消息 = showMsg 关闭软件 = kill 显示标记框 = showRect 退出流程 = exit_script # 使用退出函数别名 睡眠毫秒 = sleep 等待 = wait 鼠标移动 = moveMouseSmooth 鼠标移动并点击 = moveAndClick 鼠标点击 = mouseClick 鼠标双击 = mouseDoubleClick 鼠标滚轮 = mouseWheel 鼠标左键拖动 = mouseLeftDragTo 鼠标右键拖动 = mouseRightDragTo 获取屏幕颜色 = getScreenColor 屏幕截图 = screenShot 键盘基础触发 = keyToggle 鼠标基础触发 = mouseKeyToggle 键盘按键 = keyTap 寻找图像 = findScreen 寻找文字 = findText 等待文字 = waitText 寻找轮廓 = findContours 粘贴输入 = paste 图片相似度对比 = imgSimilar 复制文字 = copyText 复制文件 = copyFile 获取剪切板内容 = getClipboard 微信消息发送 = wxMessage 提交json = postJson 提交json文件 = postJsonFile 请求网址 = getHtml 发送邮件 = sendMail 下载文件 = downloadFile 文字转语音 = tts 打开网址 = openURL 打开目录 = openDir 打开文件 = openfile 获取屏幕分辨率 = getResolution 文字识别 = aiOcr 物体识别 = aiObject 压缩 = zipDir 解压缩 = unZip 是否数字 = isNumeric 是否有内容 = hasData 获取格式化时间 = getTime 搜索文件 = searchFile 唯一数 = uniqid 截取文本 = substringFromTo 等待图像出现 = waitImage 等待图像消失 = waitImageDisappear 等待文件 = waitFile 等待文件消失 = waitFileDisappear 等待输入 = waitInput 日志输出 = log = print 目录路径 = pyPath 文件系统 = os 路径处理 = os.path # ========== 入口检测 ========== if __name__ == "__main__": print("当前文件不能执行", "请直接执行中文名的脚本文件") showMsg("当前文件不能执行", "请直接执行中文名的脚本文件") sys.exit(1) ================================================ FILE: python示例/上传(发送)文件演示.py ================================================ """ 小瓶RPA演示demo,具体api请查看*流程开发文档* 官网:https://rpa.pbottle.com/ 流程开发文档:https://rpa.pbottle.com/docs/ 功能说明:此脚本演示了RPA中的文件上传(发送)功能 需要使用浏览器增强插件来操作网页元素,实现自动化的文件上传流程 """ import os import pbottleRPA # 引入小瓶RPA的核心库,获得对RPA功能的访问权限 # 获取当前脚本所在目录,用于构造文件路径 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) # 语音播报开始信息 pbottleRPA.tts('上传发送文件演示') # 使用文字转语音功能播报演示开始 # 显示系统消息框提示用户需要浏览器增强插件 pbottleRPA.showMsg('本演示需要浏览器增强插件','上传发送文件演示') pbottleRPA.wait(2) # 等待2秒钟 pbottleRPA.log('开始演示') # 在控制台输出开始演示信息 # 打开百度图片网站用于演示文件上传 pbottleRPA.openURL('https://image.baidu.com/') pbottleRPA.wait() # 等待页面加载完成 # 使用浏览器增强插件点击上传图标元素 # 通过CSS选择器匹配class以"img-upload-icon_"开头的span元素 pbottleRPA.browserCMD.click('span[class^="img-upload-icon_"]') # 查找页面中的"上传图片"文字位置 pos = pbottleRPA.findText('上传图片') # 移动鼠标到找到的位置并点击,打开文件选择对话框 pbottleRPA.moveAndClick(pos.x,pos.y) pbottleRPA.wait() # 等待对话框打开 # 复制要上传的文件到剪切板 pbottleRPA.copyFile(SCRIPT_DIR + '/input/RPAlogo128.png') # 使用快捷键操作选择文件上传方式 pbottleRPA.keyTap('shift+tab') # 按下Shift+Tab组合键 pbottleRPA.keyTap('ctrl+v') # 按下Ctrl+V组合键粘贴文件 pbottleRPA.wait() # 等待文件粘贴完成 pbottleRPA.keyTap('enter') # 按下回车键确认文件选择 # 确定上传操作 pbottleRPA.keyTap('enter') # 再次按下回车键开始上传文件 ================================================ FILE: python示例/下载文件示例演示.py ================================================ """ 小瓶RPA演示demo,具体api请查看*流程开发文档* 官网:https://rpa.pbottle.com/ 流程开发文档:https://rpa.pbottle.com/docs/ 功能说明:此脚本演示了RPA中的文件下载功能 通过自动触发文件下载并监测下载完成状态,实现完整的下载流程自动化 """ import pbottleRPA # 引入小瓶RPA的核心库,获得对RPA功能的访问权限 import os # 引入操作系统模块,用于获取系统信息 # 获取系统默认下载路径(用户主目录下的Downloads文件夹) 下载保存路径 = os.path.join(os.path.expanduser('~'), "Downloads") # 下载保存路径 = 'D:\\Users\\Leo\\Downloads' # 可选:修改成自己电脑浏览器默认下载路径 # 输出下载路径信息到日志,提醒用户根据实际情况修改 pbottleRPA.日志输出("电脑浏览器默认下载路径,根据自己的情况修改", 下载保存路径) # 打开微信官网页面(准备下载微信安装包) pbottleRPA.打开网址("https://pc.weixin.qq.com/") # 直接打开微信Windows客户端安装包下载链接 pbottleRPA.打开网址("https://dldir1v6.qq.com/weixin/Windows/WeChatSetup.exe") # 模拟按下回车键确认下载操作 pbottleRPA.键盘按键("enter") # 等待指定文件下载完成,超时时间为120秒 # 在等待期间会循环输出"正在下载..."提示信息 # 替代方案:使用 lambda pbottleRPA.等待文件( 下载保存路径, "WeChatSetup.exe", lambda: pbottleRPA.日志输出("正在下载..."), 120 ) # 文件下载完成后输出成功信息到日志 pbottleRPA.日志输出("文件已经下载成功") # 显示系统消息框提示用户下载已完成 pbottleRPA.showMsg("监测下载完成", "文件已经下载成功") ================================================ FILE: python示例/剪切板演示脚本.py ================================================ """ 小瓶RPA python版本(Beta) https://gitee.com/pbottle/pbottle-rpa 示例 """ import pbottleRPA #引入小瓶RPA模块 import time print("=== 剪切板演示脚本 ===") current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) print(current_time) pbottleRPA.tts('电脑剪切板演示') pbottleRPA.showMsg('超级剪切板','新版剪切板已经支持获取图片、网页格式内容') print('✅ 超级剪切板','新版剪切板已经支持获取图片、网页格式内容') #延迟5秒 pbottleRPA.sleep(1000*5) pbottleRPA.tts('已经将文字复制到您的剪切板,赶紧粘贴试试吧') print('已经将文字复制到您的剪切板,赶紧粘贴试试吧'); pbottleRPA.paste("小瓶RPA官网:https://rpa.pbottle.com/") pbottleRPA.sleep(1000*5) #延迟5秒 text = pbottleRPA.getClipboard(); print("获取当前剪切板文本:",text); print("准备结束脚本"); #脚本强制退出 pbottleRPA.exit(); ================================================ FILE: python示例/压缩和解压缩示例.py ================================================ """ 小瓶RPA演示demo,具体api请查看*流程开发文档* 官网:https://rpa.pbottle.com/ 流程开发文档:https://rpa.pbottle.com/docs/ 功能说明:此脚本演示了RPA中的压缩和解压缩功能 通过这个示例,您可以学习如何在自动化流程中处理文件压缩和解压操作 """ import pbottleRPA # 引入小瓶RPA的核心库,获得对RPA功能的访问权限 import os # 引入Python标准库os模块,用于检测文件是否存在和获取路径 # 获取当前脚本所在目录 current_dir = os.path.dirname(os.path.abspath(__file__)) pbottleRPA.log("压缩文件测试") # 在日志中输出当前操作说明 # 使用zipDir函数将指定目录压缩为ZIP文件 # 参数1:要压缩的源目录路径(input目录) # 参数2:压缩后生成的ZIP文件路径和名称 pbottleRPA.zipDir(current_dir + "/input", current_dir + "/目标压缩包.zip") pbottleRPA.wait(2) # 等待2秒钟确保压缩完成 pbottleRPA.log("监测压缩结果") # 在控制台输出检测压缩结果的信息 # 检查压缩后的ZIP文件是否存在 if not os.path.exists(current_dir + "/目标压缩包.zip"): pbottleRPA.exit("未检测到,退出!~") # 如果文件不存在,则退出脚本并输出提示信息 pbottleRPA.log("解压文件测试") # 在控制台输出解压测试的信息 # 使用unZip函数解压ZIP文件到指定目录 # 参数1:要解压的ZIP文件路径 # 参数2:解压后的目标目录路径 pbottleRPA.unZip(current_dir + "/目标压缩包.zip", current_dir + "/解压目录/") pbottleRPA.log("压缩测试完成:正在打开目录") # 在控制台输出测试完成信息 # 打开当前脚本所在目录,方便用户查看压缩和解压结果 pbottleRPA.openDir(current_dir) ================================================ FILE: python示例/发送Email电子邮件.py ================================================ """ 小瓶RPA演示demo,具体api请查看*流程开发文档* 官网:https://rpa.pbottle.com/ 流程开发文档:https://rpa.pbottle.com/docs/ 功能说明:此脚本演示了RPA中的电子邮件Email消息通知功能 通过这个示例,您可以学习如何在自动化流程中集成电子邮件Email通知功能,实现远程监控和告警 """ import pbottleRPA pbottleRPA.log("=== 电子邮件Email消息通知测试 ===") pbottleRPA.log(pbottleRPA.getTime()) to = pbottleRPA.waitInput("输入接收邮箱(测试邮件)") # 发送邮件 - 异步执行 pbottleRPA.log( pbottleRPA.sendMail( to, "小瓶RPA测试邮件", "测试邮件内容\n小瓶RPA官网:https://rpa.pbottle.com/ \n" + pbottleRPA.getTime(), ) ) ================================================ FILE: python示例/发送运维消息手机通知.py ================================================ """小瓶RPA演示demo,具体api请查看*流程开发文档* 官网:https://rpa.pbottle.com/ 流程开发文档:https://rpa.pbottle.com/docs/ 功能说明:此脚本演示了RPA中的手机消息通知功能,包括通过Webhook发送企业微信/钉钉消息和通过微信公众号发送个人通知 通过这个示例,您可以学习如何在自动化流程中集成手机通知功能,实现远程监控和告警 """ import pbottleRPA # 引入小瓶RPA的核心库,获得对RPA功能的访问权限 pbottleRPA.log("=== 运维消息通知测试 ===") # 在控制台输出测试标题 pbottleRPA.log("当前时间:", pbottleRPA.getTime()) # 在控制台输出当前日期时间 pbottleRPA.tts("准备运行手机消息通知脚本") # 使用文字转语音功能播报即将执行的操作 pbottleRPA.wait(5) # 等待5秒钟,给用户时间准备 pbottleRPA.tts("采用webhook 方式 企业微信、钉钉都支持") # 语音播报第一种通知方式 pbottleRPA.log("采用webhook 方式 企业微信、钉钉都支持") pbottleRPA.wait(5) # 等待5秒钟 # webhook方式发送消息,企业微信、钉钉都支持此方式 msgJson = { # 定义要发送的消息内容对象 "msgtype": "text", # 消息类型为文本 "text": {"content": "小瓶Rpa测试脚本运行中..."}, # 文本消息内容 # 具体的文本内容 } # 修改下方接收地址,可以用自己手机接收信息 # 参考:https://open.work.weixin.qq.com/help2/pc/14931#%E4%BA%94%E3%80%81%E7%BE%A4%E6%9C%BA%E5%99%A8%E4%BA%BAWebhook%E5%9C%B0%E5%9D%80 apiUrl = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=67bc3b43-85d1-4e0b-b7a2-d8c22d415a80" # 企业微信机器人的Webhook地址 # 使用POST请求发送JSON格式消息到指定API地址 rs = pbottleRPA.postJson(apiUrl, msgJson) pbottleRPA.log("服务器返回结果:", rs) pbottleRPA.openURL( "https://open.work.weixin.qq.com/help2/pc/14931#%E4%BA%94%E3%80%81%E7%BE%A4%E6%9C%BA%E5%99%A8%E4%BA%BAWebhook%E5%9C%B0%E5%9D%80" ) # 打开小瓶RPA官网 pbottleRPA.tts("运行结束") # 语音播报运行结束信息 pbottleRPA.log("运行结束") ================================================ FILE: python示例/图片相似度检测.py ================================================ """ 小瓶RPA演示demo,具体api请查看*流程开发文档* 官网:https://rpa.pbottle.com/ 流程开发文档:https://rpa.pbottle.com/docs/ 功能说明:此脚本演示了RPA中的图片相似度检测功能 通过这个示例,您可以学习如何比较两张图片的相似度,这在自动化测试和图像验证场景中非常有用 """ import os import json import pbottleRPA # 引入小瓶RPA的核心库,获得对RPA功能的访问权限 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) pbottleRPA.log("=== 图片相似度检测 ===") # 在控制台输出测试标题 pbottleRPA.log(pbottleRPA.getTime()) # 在控制台输出当前日期时间 pbottleRPA.setDefaultDelay(0) # 设置默认操作延时为0,手动管理所有操作延时 dir = SCRIPT_DIR + "/input/" # 定义图片文件所在目录路径 path1 = dir + "RPAlogo128.png" # 定义第一张图片的完整路径 path2 = dir + "RPAlogo128.png" # 定义第二张图片的完整路径(这里是同一张图片) # 使用imgSimilar函数比较两张图片的相似度 rs = pbottleRPA.imgSimilar(path1, path2) # 调用图片相似度检测API,传入两张图片的路径进行比较 pbottleRPA.log("图片相似度检测结果:", rs) # 在控制台输出图片相似度检测结果,值越接近1表示越相似 pbottleRPA.showMsg("图片相似度检测结果:", json.dumps(rs)) # 系统消息提示 ================================================ FILE: python示例/基础(循环、判断、等待)演示.py ================================================ """ 小瓶RPA python版本(Beta) https://gitee.com/pbottle/pbottle-rpa 示例 """ import pbottleRPA #引入小瓶RPA模块 import random #for 循环 重复操作 10 次 打开网页 for index in range(10): number = index+1 print('第' + str(number) + '次操作'); #输出到日志,文件日志永久保存 pbottleRPA.openURL('https://www.baidu.com/s?wd='+ str(number)) #操作:用默认浏览器打开网页 pbottleRPA.sleep(500) #等待500毫秒 #判断 number = random.random() #生成一个 0-1 的随机数 用于判断 if (number < 0.5) : #用 if 判断数值是否小于 0.5 print('小于 0.5。', number) pbottleRPA.tts('小于 0.5') else: print('大于或等于 0.5。', number) pbottleRPA.tts('大于等于 0.5。') pbottleRPA.wait(1) #语音播报 pbottleRPA.tts('流程结束!~') pbottleRPA.showMsg('小瓶RPA提示','流程结束!~') ================================================ FILE: python示例/屏幕物体轮廓查找演示.py ================================================ """ 小瓶RPA演示demo,具体api请查看*流程开发文档* 官网:https://rpa.pbottle.com/ 流程开发文档:https://rpa.pbottle.com/docs/ 功能说明:此脚本演示了RPA中的屏幕物体轮廓查找功能 通过这个示例,您可以学习如何使用AI视觉识别技术查找屏幕上的物体轮廓 """ import time import pbottleRPA # 引入小瓶RPA的核心库,获得对RPA功能的访问权限 pbottleRPA.log("=== 测试 ===") # 在控制台输出测试标题 pbottleRPA.log("屏幕分辨率:", pbottleRPA.getResolution()) # 输出当前屏幕分辨率信息 pbottleRPA.wait(3) # 等待3秒钟 start = time.time() # 记录开始时间,用于计算处理耗时 # 使用findContours函数查找屏幕指定区域内的物体轮廓 # 参数1:区域宽度(2000像素) # 参数2:区域高度(500像素) # 参数3:最小轮廓面积阈值(200像素) pbottleRPA.log("屏幕物体轮廓查找结果:", pbottleRPA.findContours(2000, 500, 200)) end = time.time() # 记录结束时间 pbottleRPA.log( "查找耗时:(秒)", (end - start) / 1000 ) # 计算并输出查找耗时(转换为秒) pbottleRPA.tts("已经输出 JSON 格式到运行日志") # 使用文字转语音功能播报结果已输出到日志 pbottleRPA.wait(3) # 等待3秒钟 pbottleRPA.tts("已生成调试参考图片到RPA根目录 debug目录") # 语音播报调试图片生成位置 pbottleRPA.wait(3) # 等待3秒钟 pbottleRPA.log("准备结束脚本") # 在控制台输出脚本即将结束的信息 pbottleRPA.tts("准备结束脚本") # 使用文字转语音功能播报脚本即将结束 pbottleRPA.exit("结束") # 退出RPA脚本执行并输出结束信息 ================================================ FILE: python示例/常用工具 Utils 演示.py ================================================ """ 小瓶RPA演示demo,具体api请查看*流程开发文档* 官网:https://rpa.pbottle.com/ 流程开发文档:https://rpa.pbottle.com/docs/ 功能说明:此脚本演示了RPA中的常用工具函数(utils) 通过这个示例,您可以学习如何使用pbottleRPA提供的各种实用工具函数 """ import sys import os # 添加父目录到路径以导入pbottleRPA sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import pbottleRPA def main(): print("=== 常用工具 pbottleRPA.utils ===") # 语音播报演示 pbottleRPA.tts("常用工具 utils 演示") # 显示系统消息框提示用户 pbottleRPA.showMsg("小瓶RPA提示", "常用工具 utils 演示") pbottleRPA.wait(2) # 标准格式时间演示 print("--- 时间格式化 getTime ---") # 使用工具箱中的获取格式化时间函数获取当前时间 time_str = pbottleRPA.getTime() print("标准格式时间:", time_str) # 获取自定义格式的日期(年/月/日格式) print("任意格式日期:", pbottleRPA.getTime("Y/m/d")) pbottleRPA.wait(1) # 随机数生成演示 print("--- 随机数 uniqid ---") # 生成默认格式的唯一ID(基于时间戳) print(pbottleRPA.uniqid()) # 生成带自定义前缀的唯一ID print(pbottleRPA.uniqid("myPrefix_")) # 生成带额外随机性的唯一ID print(pbottleRPA.uniqid("", True)) pbottleRPA.wait(1) # 数字字符串检测演示 print("--- 检测变量是否为数字化 isNumeric ---") # 检测各种类型数据是否为数字 print("isNumeric(10): ", pbottleRPA.isNumeric(10)) # 检测整数: True print('isNumeric("10"): ', pbottleRPA.isNumeric("10")) # 检测数字字符串: True print('isNumeric("10.5"): ', pbottleRPA.isNumeric("10.5")) # 检测小数字符串: True print('isNumeric("abc"): ', pbottleRPA.isNumeric("abc")) # 检测非数字字符串: False print("isNumeric(None): ", pbottleRPA.isNumeric(None)) # 检测None: False (JS为null) pbottleRPA.wait(1) # 变量是否包含数据检测演示 print("--- 变量是否包含数据测试 hasData ---") # 检测各种类型数据是否包含有效数据 print("hasData(None): ", pbottleRPA.hasData(None)) # None等价于JS的undefined: False print("hasData([]): ", pbottleRPA.hasData([])) # 检测空列表: False print("hasData({}): ", pbottleRPA.hasData({})) # 检测空字典: False (JS为空对象) print("hasData(0): ", pbottleRPA.hasData(0)) # 检测数字0: False print('hasData(""): ', pbottleRPA.hasData("")) # 检测空字符串: False print('hasData(" "): ', pbottleRPA.hasData(" ")) # 检测空格字符串: False print("hasData(False): ", pbottleRPA.hasData(False)) # 检测布尔值False: False print("---------------------------") print("hasData(3.14): ", pbottleRPA.hasData(3.14)) # 检测非零小数: True print('hasData("小瓶RPA "): ', pbottleRPA.hasData("小瓶RPA ")) # 检测非空字符串: True print("hasData([12,5]): ", pbottleRPA.hasData([12, 5])) # 检测非空列表: True print('hasData({"pbottle":666}): ', pbottleRPA.hasData({"pbottle": 666})) # 检测非空字典: True pbottleRPA.wait(1) # 文本截取演示 # 定义要处理的字符串 str_val = "小瓶RPA官网是 https://www.pbottle.com 输入浏览器即可访问官网" print("--- 文本截取测试 ---", str_val) # 从字符串中截取指定标记之间的内容(从"官网是"到"输入"之间的内容) sub_str = pbottleRPA.substringFromTo(str_val, "官网是", "输入") print("截取结果:", sub_str) pbottleRPA.wait(1) # 模拟资源管理器的文件搜索演示 print("--- 模拟资源管理器的文件搜索 searchFile ---") # 在指定目录中搜索指定扩展名的文件 base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) rs = pbottleRPA.searchFile(base_dir, ".png", True) # 在项目根目录搜索.png文件,包含子目录 print("当前目录搜索.png文件:", rs) if __name__ == "__main__": main() ================================================ FILE: python示例/异步子流程模板.py ================================================ """ 小瓶RPA异步子流程模板 功能说明:此脚本演示了RPA中如何使用子流程(同步/异步) 通过这个示例,您可以学习如何在主流程中调用同步和异步子流程,并处理子流程的返回结果。 """ # 引入小瓶RPA的核心库 import pbottleRPA import subprocess import sys import os import importlib.util # ========== 全局变量 ========== global_processName = "小瓶RPA——XXXX流程模板" # 流程名称 global_startTime = pbottleRPA.getTime() # 开始时间 def 同步子流程(脚本路径): """ 执行一个同步子流程脚本(阻塞等待完成) @param 脚本路径: .py 文件的绝对/相对路径 """ print("启动同步子流程:", 脚本路径) 脚本路径 = os.path.abspath(脚本路径) result = subprocess.run( [sys.executable, "-u", 脚本路径], capture_output=True, text=True, encoding="utf-8", errors="ignore", ) if result.returncode == 0: print(f"✅ 同步子流程完成: {脚本路径}") if result.stdout.strip(): print(" 子流程输出:", result.stdout.strip()) return result.stdout else: print(f"❌ 同步子流程失败 (exit code {result.returncode}): {脚本路径}") if result.stderr.strip(): print(" 错误信息:", result.stderr.strip()) return None def 异步子流程(脚本路径, *args): """ 在新进程中执行子流程脚本(非阻塞),返回 subprocess 对象 注意:Python 标准库不直接支持 async/await 风格的跨进程调用, 这里用 subprocess + 线程模拟异步行为。 @param 脚本路径: .py 文件路径 @param *args: 传递给子脚本的参数(通过命令行传入) @return: subprocess.Popen 对象,可调用 .wait() 等待完成 """ print("启动异步子流程:", 脚本路径) 脚本路径 = os.path.abspath(脚本路径) proc = subprocess.Popen( [sys.executable, "-u", 脚本路径] + list(args), stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8", errors="ignore", ) return proc def 动态导入子模块(模块路径): """ 将另一个 .py 文件作为 Python 模块动态导入并调用其函数 (等价于 JS 的 require() 返回 module.exports) @param 模块路径: .py 文件路径 @return: 模块对象,可调用其中的函数 """ 模块路径 = os.path.abspath(模块路径) 模块名 = os.path.splitext(os.path.basename(模块路径))[0] spec = importlib.util.spec_from_file_location(模块名, 模块路径) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module # ========== 子函数定义 ========== def 子流程2(params): """内联子流程函数,接收参数""" print("子流程2开始", params) # 在这里编写你的子流程逻辑... print("子流程2结束") return True # ========== 主流程 ========== def main(): """主流程入口""" try: # 记录主流程开始信息 pbottleRPA.log("主流程开始 📍", global_startTime, global_processName) # ---- 方式1:直接调用内部子函数(同步) ---- print("--- 调用内联子流程 ---") 子流程2("输入参数2") # ---- 方式2:同步调用外部 .py 脚本(阻塞) ---- # 等价于 JS 的 require('./xxx.js') 同步执行顶层代码 print("\n--- 启动同步子流程 ---") current_dir = os.path.dirname(os.path.abspath(__file__)) 同步子流程(current_dir + "/快速开始演示(3行代码).py") # ---- 方式3:动态导入外部模块并调用其函数 ---- # 等价于 JS 的 let rs = require('./test.js')(url) print("\n--- 启动动态导入子模块 ---") try: test_module = 动态导入子模块(current_dir + "/下载文件示例演示.py") # 调用模块中的函数/变量(如果该模块有导出的话) print("✅ 子模块加载成功:", dir(test_module)) except Exception as e: print(f"⚠ 子模块加载失败(可能该脚本不是模块化设计): {e}") # ---- 方式4:异步子进程(非阻塞) ---- # 等价于 JS 的 await require('./test.js')() 但为非阻塞版本 print("\n--- 启动异步子流程(子进程)----") proc = 异步子流程(current_dir + "/快速开始演示(3行代码).py") # 可以在这里做其他事情... # 等待子流程完成 proc.wait() out, err = proc.communicate() if out and out.strip(): print("异步子流程输出:", out.strip()) # 主流程完成 pbottleRPA.log("主流程完成 ✅") return True except Exception as e: # 主流程错误捕获 print("❌ 未完成,错误", e) print("准备发送消息给管理员 👉") # 这里可以添加发送微信通知/邮件的逻辑: # pbottleRPA.wxMessage("RPA流程异常", f"错误: {e}", "你的key") return False # ========== 入口 ========== if __name__ == "__main__": main() ================================================ FILE: python示例/微信朋友圈自动点赞.py ================================================ """ 小瓶RPA python版本(Beta) https://gitee.com/pbottle/pbottle-rpa 示例 """ import pbottleRPA # 引入小瓶RPA模块 import time print("=== 微信朋友圈自动点赞 ===") current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) print(current_time) pbottleRPA.showMsg("流程已开始运行", "请打开电脑声音,关注运行日志信息") pbottleRPA.tts("准备开始运行朋友圈批量点赞脚本,适配分辨率无缩放屏幕") pbottleRPA.sleep(1000 * 7) resolution = pbottleRPA.getResolution() print("当前电脑屏幕分辨率", resolution) if resolution["ratio"] != 1: pbottleRPA.tts("错误:此demo只适配分辨率无缩放屏幕") print("错误:此demo只适配无缩放屏幕") pbottleRPA.sleep(1000 * 6) pbottleRPA.exit() color = pbottleRPA.getScreenColor(1, resolution["h"] - 1) print("系统任务栏色:", color) # 输入路径中不要有自定义中文 rs = pbottleRPA.waitImage( ["./input/pengYouQuanDianZan/0.png", "./input/pengYouQuanDianZan/01.png"], lambda: print("等待中,请先打开电脑版微信界面"), 120, ) # 打开微信朋友圈 pbottleRPA.moveMouseSmooth(rs["x"], rs["y"]) pbottleRPA.mouseClick() pbottleRPA.moveMouseSmooth(resolution["w"] / 2, resolution["h"] / 2) # 点赞计数 n = 0 # 开始任务 def loop(): print("进入") global n rs = pbottleRPA.findScreen( ["./input/pengYouQuanDianZan/1.png", "./input/pengYouQuanDianZan/11.png"], 0.99, 100, 100, ) if rs == False: print("下一页") # pbottleRPA.keyTap('page down') #微信bug,按键容易加载不成功 pbottleRPA.mouseWheel() pbottleRPA.sleep(500) loop() else: pbottleRPA.moveMouseSmooth(rs["x"], rs["y"]) pbottleRPA.mouseClick() pbottleRPA.sleep(100) pbottleRPA.moveMouseSmooth(rs["x"] - 167, rs["y"]) pbottleRPA.mouseClick() # 点1000个赞就行了,不要贪杯 n += 1 if n >= 1000: pbottleRPA.exit("") pbottleRPA.sleep(100) loop() # 重复 loop() ================================================ FILE: python示例/快速开始演示(3行代码).py ================================================ """ 小瓶RPA python版本(Beta) https://gitee.com/pbottle/pbottle-rpa 示例 """ import pbottleRPA #引入小瓶RPA模块 pbottleRPA.openURL('https://www.baidu.com/') #用浏览器打开百度 pbottleRPA.paste('小瓶RPA官网') #输入搜索词 pbottleRPA.keyTap('enter') #确认搜索 ================================================ FILE: python示例/截屏操作演示脚本.py ================================================ """ 小瓶RPA python版本(Beta) https://gitee.com/pbottle/pbottle-rpa 示例 """ import pbottleRPA #引入小瓶RPA模块 import time print("=== 截屏操作演示脚本 ===") current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) print(current_time) pbottleRPA.setDefaultDelay(0) #忽略自动按键间隔 pbottleRPA.tts('开始运行小瓶RPA截屏操作演示脚本。... 快捷键 :Ctrl+shift+Q 可手动退出') #延迟5秒 pbottleRPA.sleep(1000 * 12) resolution = pbottleRPA.getResolution() print('当前电脑屏幕分辨率', resolution) pbottleRPA.tts(f'当前电脑屏幕分辨率: {resolution["w"]} 乘以 {resolution["h"]}') pbottleRPA.sleep(1000 * 6) pbottleRPA.tts('正在截屏(全屏)...') pbottleRPA.sleep(1000*3) pbottleRPA.screenShot(); pbottleRPA.tts('正在截屏(区域)...') rs = pbottleRPA.screenShot('',resolution["w"]/4,resolution["h"]/4,resolution["w"]/2,resolution["h"]/2); print('截屏结果:',rs) pbottleRPA.sleep(1000*3) pbottleRPA.tts('图片保存在我的电脑 我的图片...') pbottleRPA.sleep(1000*5) pbottleRPA.tts('演示结束') print("准备结束脚本") ================================================ FILE: python示例/文件基础操作演示.py ================================================ """ 小瓶RPA python版本(Beta) https://gitee.com/pbottle/pbottle-rpa 示例 """ import pbottleRPA #引入小瓶RPA模块 import os import shutil # 获取当前工作目录 __dirname = os.getcwd() pbottleRPA.tts('打开文件夹') print('打开文件夹'); #延迟5秒 pbottleRPA.sleep(1000*3) print(__dirname + '\\input\\'); pbottleRPA.openDir(__dirname + '\\input\\') pbottleRPA.sleep(1000*2) pbottleRPA.tts('打开图片') print('打开图片'); pbottleRPA.openDir(__dirname + '/input/RPAlogo128.png') pbottleRPA.sleep(1000*2) pbottleRPA.tts('关闭') pbottleRPA.keyTap('alt+f4') pbottleRPA.sleep(1000) pbottleRPA.tts('复制文件') print('复制文件'); shutil.copy(__dirname + '/input/RPAlogo128.png',__dirname + '/input/RPAlogo128-新复制.png') pbottleRPA.sleep(1000*3) pbottleRPA.tts('删除文件') print('删除文件') os.remove(__dirname + '/input/RPAlogo128-新复制.png') pbottleRPA.tts('演示结束') print('演示结束'); pbottleRPA.showMsg('演示结束','请查看运行日志') ================================================ FILE: python示例/文字提取查找OCR演示.py ================================================ """ 小瓶RPA演示demo,具体api请查看*流程开发文档* 官网:https://rpa.pbottle.com/ 流程开发文档:https://rpa.pbottle.com/docs/ 功能说明:此脚本演示了RPA中的OCR文字识别和查找功能 通过这些示例,您可以学习如何使用AI技术识别屏幕上的文字内容并进行查找定位 """ import time import pbottleRPA # 引入小瓶RPA的核心库,获得对RPA功能的访问权限 pbottleRPA.log("=== OCR 识别测试 ==="); # 在控制台输出测试标题 pbottleRPA.log('屏幕分辨率:',pbottleRPA.getResolution()) # 在控制台输出当前屏幕分辨率信息 pbottleRPA.tts('正在识别您的电脑屏幕左上角区域文字') # 使用文字转语音功能播报即将执行的操作 pbottleRPA.wait(5) # 等待5秒钟 start = time.time() # 记录OCR识别开始时间(用于计算耗时) pbottleRPA.log('屏幕orc结果:',pbottleRPA.aiOcr('screen',10,10,500,500)) # 对屏幕左上角区域(10,10,500,500)进行OCR文字识别,并输出结果到控制台 end = time.time(); # 记录OCR识别结束时间 pbottleRPA.log('OCR耗时:(秒)',(end-start)/1000); # 计算并输出OCR识别耗时(转换为秒) pbottleRPA.tts("已经输出 JSON 格式到运行日志") # 语音播报OCR结果已输出 pbottleRPA.wait(5); # 等待5秒钟 pbottleRPA.log("查找并点击微信") # 在控制台输出操作信息 pbottleRPA.tts("查找并点击微信") # 语音播报即将执行的操作 position = pbottleRPA.findText('微信',10,10,1000,500) # 在屏幕区域(10,10,1000,500)内查找"微信"文字,返回位置信息 pbottleRPA.log('查找文字结果:',position); # 在控制台输出查找到的文字位置信息 if position: # 如果找到了指定文字 pbottleRPA.moveAndClick(position.x,position.y) # 移动鼠标到文字位置并点击 else: # 如果没有找到指定文字 pbottleRPA.log("范围内没有找到文字:微信"); # 在控制台输出未找到的提示信息 pbottleRPA.log("准备结束脚本"); # 在控制台输出脚本即将结束的信息 pbottleRPA.tts("准备结束脚本"); # 语音播报脚本即将结束的信息 pbottleRPA.showMsg('演示结束','请查看运行日志') # 显示系统消息框提示演示结束 pbottleRPA.exit("结束") # 退出RPA脚本执行 ================================================ FILE: python示例/用户手动输入变量示例.py ================================================ """ 小瓶RPA演示demo,具体api请查看*流程开发文档* 官网:https://rpa.pbottle.com/ 流程开发文档:https://rpa.pbottle.com/docs/ 功能说明:这是一个最基础的小瓶RPA JavaScript脚本示例,展示小瓶RPA动态输入内容的方法 小瓶RPA基座 V2026.0.0 以上版本可用 """ import pbottleRPA # 引入小瓶RPA的核心库,获得对RPA功能的访问权限 content1 = pbottleRPA.waitInput("请输入第一个数字:") content2 = pbottleRPA.waitInput("请输入第二个数字:") pbottleRPA.log("输入的数字是:", content1, content2) pbottleRPA.log("输入完成 ✅️, 相加等于:", float(content1) + float(content2)) ================================================ FILE: python示例/运维消息手机通知.py ================================================ """ 小瓶RPA python版本(Beta) https://gitee.com/pbottle/pbottle-rpa 示例 """ import pbottleRPA #引入小瓶RPA模块 import time print("=== 键盘操作测试 ===") current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) print(current_time) pbottleRPA.tts('准备运行手机消息通知脚本') pbottleRPA.sleep(1000*5) pbottleRPA.tts('方式一:采用webhook 方式 企业微信、钉钉都支持') pbottleRPA.sleep(1000*5) #webhook 方式 企业微信、钉钉都支持 msgJson={ "msgtype": "text", "text": { "content": "小瓶Rpa测试脚本运行中...(Python脚本)" } }; #修改下方接收地址,可以用自己手机接收信息 参考:https://open.work.weixin.qq.com/help2/pc/14931#%E4%BA%94%E3%80%81%E7%BE%A4%E6%9C%BA%E5%99%A8%E4%BA%BAWebhook%E5%9C%B0%E5%9D%80 apiUrl = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=67bc3b43-85d1-4e0b-b7a2-d8c22d415a80'; pbottleRPA.postJson(apiUrl,msgJson); pbottleRPA.tts('方式二:个人微信通知,需要关注小瓶科技公众号') pbottleRPA.sleep(1000*5) #微信消息测试,几乎零延迟,100%到达率 详情: https://www.pbottle.com/a-12586.html pbottleRPA.wxMessage('小瓶RPA机器人消息(python脚本)',"主人,我的任务已经完成了,随时等您的吩咐",'key599a9e4136010'); pbottleRPA.tts("运行结束") print("准备结束脚本") ================================================ FILE: python示例/键盘基本操作演示脚本.py ================================================ """ 小瓶RPA python版本(Beta) https://gitee.com/pbottle/pbottle-rpa 示例 """ import pbottleRPA # 引入小瓶RPA模块 import time print("=== 键盘操作测试 ===") current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) print(current_time) pbottleRPA.setDefaultDelay(0) # 忽略自动按键间隔 pbottleRPA.tts("开始运行小瓶RPA键盘操作演示脚本。... 快捷键 :Ctrl+shift+Q 可手动退出") # 延迟5秒 pbottleRPA.sleep(1000 * 12) resolution = pbottleRPA.getResolution() print("当前电脑屏幕分辨率", resolution) pbottleRPA.tts(f'当前电脑屏幕分辨率: {resolution["w"]} 乘以 {resolution["h"]}') pbottleRPA.sleep(1000 * 6) pbottleRPA.tts("准备打开网页浏览并用快捷键进入全屏,5秒后开始") pbottleRPA.sleep(1000 * 10) # 用浏览器打开网址 pbottleRPA.openURL("https://rpa.pbottle.com?from=demo") pbottleRPA.sleep(1000 * 1) pbottleRPA.keyTap("f11") pbottleRPA.sleep(1000 * 2) pbottleRPA.tts("翻页查看") pbottleRPA.keyTap("page down") pbottleRPA.sleep(1000 * 1) pbottleRPA.keyTap("page down") pbottleRPA.sleep(1000 * 1) pbottleRPA.keyTap("page down") pbottleRPA.sleep(1000 * 1) pbottleRPA.tts("翻页回来") pbottleRPA.keyTap("page up") pbottleRPA.sleep(1000 * 1) pbottleRPA.keyTap("page up") pbottleRPA.sleep(1000 * 1) pbottleRPA.keyTap("page up") pbottleRPA.sleep(1000 * 2) pbottleRPA.tts("收藏我们吧,十分感谢") pbottleRPA.keyTap("ctrl + d") pbottleRPA.sleep(1000 * 2) pbottleRPA.keyTap("enter") pbottleRPA.sleep(1000 * 2) pbottleRPA.keyTap("f11") pbottleRPA.tts("演示结束") print("准备结束脚本") pbottleRPA.exit("脚本结束", True) ================================================ FILE: python示例/鼠标基础操作演示.py ================================================ """ 小瓶RPA python版本(Beta) https://gitee.com/pbottle/pbottle-rpa 示例 """ import pbottleRPA #引入小瓶RPA模块 import time print("=== 鼠标操作测试 ===") current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) print(current_time) pbottleRPA.setDefaultDelay(0) #忽略自动按键间隔 pbottleRPA.tts('开始运行小瓶RPA鼠标操作演示脚本。... 快捷键 :Ctrl+shift+Q 可手动退出') #延迟5秒 pbottleRPA.sleep(1000 * 12) resolution = pbottleRPA.getResolution() print('当前电脑屏幕分辨率', resolution) pbottleRPA.keyTap('windows+d') pbottleRPA.tts(f'当前电脑屏幕分辨率: {resolution["w"]} 乘以 {resolution["h"]}') pbottleRPA.sleep(1000 * 6) pbottleRPA.tts('移动指针到屏幕中点') pbottleRPA.moveMouseSmooth(resolution["w"]/2,resolution["h"]/2) pbottleRPA.sleep(1000*3) pbottleRPA.tts('长按左键') pbottleRPA.mouseClick('left',1500); pbottleRPA.sleep(1000*2) pbottleRPA.tts('鼠标双击') pbottleRPA.moveMouseSmooth(38,38) pbottleRPA.mouseDoubleClick() pbottleRPA.sleep(1000*3) pbottleRPA.tts('准备打开网页并滚动鼠标,5秒后开始') pbottleRPA.sleep(1000*10) #用浏览器打开网址 pbottleRPA.openURL('https://rpa.pbottle.com?from=demo') pbottleRPA.sleep(1000*2) pbottleRPA.keyTap('f11') pbottleRPA.sleep(1000*1) pbottleRPA.tts('滚动鼠标') pbottleRPA.mouseWheel() pbottleRPA.sleep(1000*4) pbottleRPA.tts('反向滚动鼠标') pbottleRPA.mouseWheel(360) pbottleRPA.sleep(1000*4) pbottleRPA.tts('右键页面') pbottleRPA.moveMouseSmooth(100,100) pbottleRPA.mouseClick('right') pbottleRPA.sleep(1000*3) pbottleRPA.moveMouseSmooth(35,35) pbottleRPA.tts('左键单击') pbottleRPA.mouseClick() pbottleRPA.sleep(1000*3) #测试鼠标拖拽 pbottleRPA.tts('拖拽或选区') pbottleRPA.sleep(1000*4) pbottleRPA.moveMouseSmooth(1634,143) pbottleRPA.mouseLeftDragTo(500,500) pbottleRPA.sleep(1000*5) pbottleRPA.mouseClick() pbottleRPA.keyTap('f11') pbottleRPA.tts('演示结束') print("准备结束脚本"); pbottleRPA.sleep(1000*3) #脚本强制退出 pbottleRPA.exit() print("已经退出了,无效"); ================================================ FILE: 上传(发送)文件演示.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的文件上传(发送)功能 * 需要使用浏览器增强插件来操作网页元素,实现自动化的文件上传流程 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 // 语音播报开始信息 pbottleRPA.tts('上传发送文件演示') // 使用文字转语音功能播报演示开始 // 显示系统消息框提示用户需要浏览器增强插件 pbottleRPA.showMsg('本演示需要浏览器增强插件','上传发送文件演示') pbottleRPA.wait(2) // 等待2秒钟 console.log('开始演示'); // 在控制台输出开始演示信息 // 打开百度图片网站用于演示文件上传 pbottleRPA.openURL('https://pic.sogou.com/') pbottleRPA.wait() // 等待页面加载完成 // 使用浏览器增强插件点击上传图标元素 // 通过CSS选择器匹配class以"img-upload-icon_"开头的span元素 pbottleRPA.browserCMD_click('#cameraIco') pbottleRPA.wait(2) // 查找页面中的"上传图片"文字位置 let pos = pbottleRPA.findText('本地上传') if(pos === false){ pbottleRPA.exit('未找到上传按钮'); } // 移动鼠标到找到的位置并点击,打开文件选择对话框 pbottleRPA.moveAndClick(pos.x,pos.y) pbottleRPA.wait(2) // 等待对话框打开 // 复制要上传的文件到剪切板 pbottleRPA.copyText(pbottleRPA.path.resolve('./input/RPAlogo128.png')) pbottleRPA.keyTap('ctrl+v') // 按下Ctrl+V组合键粘贴文件路径 pbottleRPA.wait() // 等待文件粘贴完成 pbottleRPA.keyTap('enter') // 按下回车键确认文件选择 // 确定上传操作 pbottleRPA.keyTap('enter') // 再次按下回车键开始上传文件 ================================================ FILE: 下载文件示例演示.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的文件下载功能 * 通过自动触发文件下载并监测下载完成状态,实现完整的下载流程自动化 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 const os = require('os'); // 引入操作系统模块,用于获取系统信息 const path = require('node:path') // 引入路径处理模块,用于处理文件路径 // 获取系统默认下载路径(用户主目录下的Downloads文件夹) let 下载保存路径 = path.join(os.homedir(),'Downloads') // 下载保存路径 = 'D:\\Users\\Leo\\Downloads' // 可选:修改成自己电脑浏览器默认下载路径 // 输出下载路径信息到日志,提醒用户根据实际情况修改 pbottleRPA.日志输出('电脑浏览器默认下载路径,根据自己的情况修改',下载保存路径) // 打开微信官网页面(准备下载微信安装包) pbottleRPA.打开网址('https://pc.weixin.qq.com/') // 直接打开微信Windows客户端安装包下载链接 pbottleRPA.打开网址('https://dldir1v6.qq.com/weixin/Windows/WeChatSetup.exe') // 模拟按下回车键确认下载操作 pbottleRPA.键盘按键('enter') // 等待指定文件下载完成,超时时间为120秒 // 在等待期间会循环输出"正在下载..."提示信息 pbottleRPA.等待文件(下载保存路径,'WeChatSetup.exe',()=>{ pbottleRPA.日志输出('正在下载...'); // 等待期间输出下载状态 },120) // 文件下载完成后输出成功信息到日志 pbottleRPA.日志输出('文件已经下载成功'); // 显示系统消息框提示用户下载已完成 pbottleRPA.showMsg('监测下载完成','文件已经下载成功') ================================================ FILE: 企业版 Demo示例说明.txt ================================================ 企业版demo示例演示运行步骤: ① 访问 https://gitee.com/pbottle/pbottle-rpa(或者:https://github.com/leoxiaoping/pbottleRPA) ② 打开以 [企业版] 开头的示例脚本,一键复制到demo示例目录运行即可 企业版demo演示列表: [企业版]外部控制能力.js [企业版]HID硬件级键盘鼠标演示.js [企业版]屏幕物体查找Ai演示.js [企业版]接力执行脚本.js [企业版]集群控制中心示例.js [企业版]集群控制中心日志回传.js [企业版]集群控制中心流程升级.js ================================================ FILE: 剪切板演示脚本.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的剪切板操作功能,包括复制文本、获取剪切板内容和复制文件 * 通过这个示例,您可以学习如何在自动化流程中使用剪切板进行数据传递 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 console.log("=== 剪切板测试 ==="); // 在控制台输出测试标题 console.log(Date()); // 在控制台输出当前日期时间 pbottleRPA.tts('电脑剪切板演示') // 使用文字转语音功能播报当前演示内容 // 显示系统消息框,提示用户新版剪切板支持获取图片、网页格式内容 pbottleRPA.showMsg('超级剪切板','新版剪切板已经支持获取图片、网页格式内容') console.log('✅ 超级剪切板','新版剪切板已经支持获取图片、网页格式内容') // 在控制台输出相同信息 pbottleRPA.wait(5) // 等待5秒钟,给用户时间阅读提示信息 pbottleRPA.tts('已经复制文字,赶紧找个地方粘贴试试吧') // 语音播报已复制文字的提示信息 console.log('已经复制文字,赶紧找个地方粘贴试试吧'); // 在控制台输出相同信息 // 将指定文本内容复制到系统剪切板中 pbottleRPA.paste("小瓶RPA官网:https://rpa.pbottle.com/") pbottleRPA.wait(10) // 等待10秒钟,给用户时间进行粘贴测试 // 从系统剪切板中获取当前文本内容 let text = pbottleRPA.getClipboard(); console.log("获取当前剪切板文本:",text); // 在控制台输出获取到的剪切板文本内容 console.log("复制文件模拟操作:") // 在控制台输出即将进行的操作说明 // 复制文件到系统剪切板(模拟文件复制操作) pbottleRPA.copyFile(__dirname + '/input/RPAlogo128.png') // 将指定图片文件复制到剪切板 let filepath = pbottleRPA.getClipboard(); // 获取剪切板中的文件路径信息 console.log("剪切板文件路径:",filepath); // 在控制台输出获取到的文件路径 pbottleRPA.tts('已经复制文件,赶紧桌面粘贴试试吧') // 语音播报已复制文件的提示信息 console.log('已经复制文件,赶紧桌面粘贴试试吧'); // 在控制台输出相同信息 ================================================ FILE: 压缩和解压缩示例.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的压缩和解压缩功能 * 通过这个示例,您可以学习如何在自动化流程中处理文件压缩和解压操作 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 const fs = require("fs"); // 引入Node.js文件系统模块,用于检测文件是否存在 pbottleRPA.log('压缩文件测试') // 在日志中输出当前操作说明 // 使用zipDir函数将指定目录压缩为ZIP文件 // 参数1:要压缩的源目录路径(input目录) // 参数2:压缩后生成的ZIP文件路径和名称 pbottleRPA.zipDir(pbottleRPA.__dirname+'/input', pbottleRPA.__dirname+'/目标压缩包.zip') pbottleRPA.wait(2) // 等待2秒钟确保压缩完成 console.log('监测压缩结果'); // 在控制台输出检测压缩结果的信息 // 检查压缩后的ZIP文件是否存在 if (!fs.existsSync(pbottleRPA.__dirname+'/目标压缩包.zip')) { pbottleRPA.exit('未检测到,退出!~') // 如果文件不存在,则退出脚本并输出提示信息 } console.log('解压文件测试') // 在控制台输出解压测试的信息 // 使用unZip函数解压ZIP文件到指定目录 // 参数1:要解压的ZIP文件路径 // 参数2:解压后的目标目录路径 pbottleRPA.unZip(pbottleRPA.__dirname+'/目标压缩包.zip', pbottleRPA.__dirname+'/解压目录/') console.log('压缩测试完成:正在打开目录'); // 在控制台输出测试完成信息 // 打开当前脚本所在目录,方便用户查看压缩和解压结果 pbottleRPA.openDir(pbottleRPA.__dirname) ================================================ FILE: 发送Email电子邮件.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的电子邮件Email消息通知功能 * 通过这个示例,您可以学习如何在自动化流程中集成电子邮件Email通知功能,实现远程监控和告警 */ const pbottleRPA = require('./pbottleRPA') console.log("=== 电子邮件Email消息通知测试 ===") console.log(Date()) let to = pbottleRPA.waitInput('输入接收邮箱(测试邮件)') // 发送邮件 - 异步执行 pbottleRPA.sendMail(to, '小瓶RPA测试邮件', '测试邮件内容\n小瓶RPA官网:https://rpa.pbottle.com/ \n'+ Date()) .then(console.log) .catch(console.error) ================================================ FILE: 发送运维消息手机通知.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的手机消息通知功能,包括通过Webhook发送企业微信/钉钉消息和通过微信公众号发送个人通知 * 通过这个示例,您可以学习如何在自动化流程中集成手机通知功能,实现远程监控和告警 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 console.log("=== 运维消息通知测试 ==="); // 在控制台输出测试标题 console.log(Date()); // 在控制台输出当前日期时间 pbottleRPA.tts('准备运行手机消息通知脚本') // 使用文字转语音功能播报即将执行的操作 pbottleRPA.wait(5) // 等待5秒钟,给用户时间准备 pbottleRPA.tts('采用webhook 方式 企业微信、钉钉都支持') // 语音播报第一种通知方式 pbottleRPA.log("采用webhook 方式 企业微信、钉钉都支持") pbottleRPA.wait(5) // 等待5秒钟 // webhook方式发送消息,企业微信、钉钉都支持此方式 let msgJson = { // 定义要发送的消息内容对象 "msgtype": "text", // 消息类型为文本 "text": { // 文本消息内容 "content": "小瓶Rpa测试脚本运行中..." // 具体的文本内容 } }; // 修改下方接收地址,可以用自己手机接收信息 // 参考:https://open.work.weixin.qq.com/help2/pc/14931#%E4%BA%94%E3%80%81%E7%BE%A4%E6%9C%BA%E5%99%A8%E4%BA%BAWebhook%E5%9C%B0%E5%9D%80 let apiUrl = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=67bc3b43-85d1-4e0b-b7a2-d8c22d415a80'; // 企业微信机器人的Webhook地址 // 使用POST请求发送JSON格式消息到指定API地址 const rs = pbottleRPA.postJson(apiUrl,msgJson); console.log('服务器返回结果:',rs); pbottleRPA.openURL("https://open.work.weixin.qq.com/help2/pc/14931#%E4%BA%94%E3%80%81%E7%BE%A4%E6%9C%BA%E5%99%A8%E4%BA%BAWebhook%E5%9C%B0%E5%9D%80"); // 打开小瓶RPA官网 pbottleRPA.tts("运行结束") // 语音播报运行结束信息 pbottleRPA.log("运行结束") ================================================ FILE: 图片相似度检测.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的图片相似度检测功能 * 通过这个示例,您可以学习如何比较两张图片的相似度,这在自动化测试和图像验证场景中非常有用 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 console.log("=== 图片相似度检测 ==="); // 在控制台输出测试标题 console.log(Date()); // 在控制台输出当前日期时间 pbottleRPA.setDefaultDelay(0); // 设置默认操作延时为0,手动管理所有操作延时 let dir = __dirname + '/input/'; // 定义图片文件所在目录路径,__dirname表示当前脚本所在的目录 let path1 = dir + 'RPAlogo128.png'; // 定义第一张图片的完整路径 let path2 = dir + 'RPAlogo128.png'; // 定义第二张图片的完整路径(这里是同一张图片) // 使用imgSimilar函数比较两张图片的相似度 let rs = pbottleRPA.imgSimilar(path1,path2); // 调用图片相似度检测API,传入两张图片的路径进行比较 console.log('图片相似度检测结果:',rs); // 在控制台输出图片相似度检测结果,值越接近1表示越相似 pbottleRPA.showMsg('图片相似度检测结果:',JSON.stringify(rs)); //系统消息提示 ================================================ FILE: 基础(循环、判断、等待)演示.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的基本编程控制结构,包括等待、循环和条件判断 * 通过这些示例,您可以学习如何在RPA流程中实现重复操作和条件分支处理 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 pbottleRPA.文字转语音('等待3秒开始') // 使用文字转语音功能播报提示信息 pbottleRPA.日志输出('等待3秒开始'); // 将指定文本输出到日志文件中(日志永久保存) pbottleRPA.等待(3) // 暂停程序执行3秒钟,等待相关操作完成 // for 循环示例:重复执行操作10次,每次打开不同的网页 for (let index = 0; index < 10; index++) { // 初始化循环计数器,设置循环条件(执行10次) let 计数器 = index+1 // 定义计数器变量,用于显示当前循环次数(从1开始) pbottleRPA.日志输出('第' + 计数器 + '次操作'); // 输出当前操作次数到日志文件中 pbottleRPA.打开网址('https://www.baidu.com/s?wd='+计数器) // 使用默认浏览器打开指定网址,每次搜索不同的关键词 pbottleRPA.等待(0.5) // 每次操作后等待0.5秒,避免操作过快 } // 条件判断示例:使用随机数进行条件判断 let 随机数 = Math.random() // 生成一个0到1之间的随机数 if (随机数 < 0.5) { // 判断随机数是否小于0.5 pbottleRPA.日志输出('小于 0.5。', 随机数) // 如果条件成立,输出相关信息到日志 pbottleRPA.文字转语音('小于 0.5') // 同时通过语音播报结果 } else { // 如果随机数大于或等于0.5 pbottleRPA.日志输出('大于或等于 0.5。', 随机数) // 输出相关信息到日志 pbottleRPA.文字转语音('大于等于 0.5。') // 通过语音播报结果 } pbottleRPA.等待(3) // 等待3秒钟,让用户有时间查看结果 // 流程结束提示 pbottleRPA.文字转语音('流程结束!~') // 使用文字转语音功能播报流程结束信息 pbottleRPA.显示系统消息('小瓶RPA提示','流程结束!~') // 显示系统级别的弹窗消息,提示用户流程已完成 ================================================ FILE: 屏幕物体轮廓查找演示.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的屏幕物体轮廓查找功能 * 通过这个示例,您可以学习如何使用AI视觉识别技术查找屏幕上的物体轮廓 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 console.log("=== 测试 ==="); // 在控制台输出测试标题 console.log('屏幕分辨率:',pbottleRPA.getResolution()) // 输出当前屏幕分辨率信息 pbottleRPA.wait(3) // 等待3秒钟 let start = Date.now() // 记录开始时间,用于计算处理耗时 // 使用findContours函数查找屏幕指定区域内的物体轮廓 // 参数1:区域宽度(2000像素) // 参数2:区域高度(500像素) // 参数3:最小轮廓面积阈值(200像素) console.log('屏幕物体轮廓查找结果:',pbottleRPA.findContours(2000,500,200)) let end = Date.now(); // 记录结束时间 console.log('查找耗时:(秒)',(end-start)/1000); // 计算并输出查找耗时(转换为秒) pbottleRPA.tts("已经输出 JSON 格式到运行日志") // 使用文字转语音功能播报结果已输出到日志 pbottleRPA.wait(3); // 等待3秒钟 pbottleRPA.tts("已生成调试参考图片到RPA根目录 debug目录") // 语音播报调试图片生成位置 pbottleRPA.wait(3); // 等待3秒钟 console.log("准备结束脚本"); // 在控制台输出脚本即将结束的信息 pbottleRPA.tts("准备结束脚本"); // 使用文字转语音功能播报脚本即将结束 pbottleRPA.exit("结束") // 退出RPA脚本执行并输出结束信息 ================================================ FILE: 常用工具 Utils 演示.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的常用工具函数(utils) * 通过这个示例,您可以学习如何使用pbottleRPA.utils提供的各种实用工具函数 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 console.log('常用工具 pbottleRPA.utils'); // 在控制台输出工具类名称 // pbottleRPA.utils // 语音播报演示 pbottleRPA.tts('常用工具 utils 演示') // 使用文字转语音功能播报演示开始 // 显示系统消息框提示用户 pbottleRPA.showMsg('小瓶RPA提示','常用工具 utils 演示') pbottleRPA.wait(2) // 等待2秒钟 // 标准格式时间演示 console.log('时间格式化:getTime'); // 在控制台输出当前演示功能 // 使用工具箱中的获取格式化时间函数获取当前时间 let timeStr = pbottleRPA.工具箱.获取格式化时间() console.log('标准格式时间:',timeStr); // 输出标准格式的时间字符串 // 获取自定义格式的日期(年/月/日格式) console.log('任意格式日期:',pbottleRPA.getTime('Y/m/d')); pbottleRPA.wait(1) // 等待1秒钟 // 随机数生成演示 console.log('随机数:uniqid'); // 在控制台输出当前演示功能 // 生成默认格式的唯一ID(基于时间戳) console.log(pbottleRPA.utils.uniqid()); // 生成带自定义前缀的唯一ID console.log(pbottleRPA.utils.uniqid('myPrefix_')); // 生成带额外随机性的唯一ID console.log(pbottleRPA.utils.uniqid('', true)); pbottleRPA.wait(1) // 等待1秒钟 // 数字字符串检测演示 console.log('检测变量是否为数字化:isNumeric'); // 在控制台输出当前演示功能 // 检测各种类型数据是否为数字 console.log(pbottleRPA.utils.isNumeric(10)); // 检测整数:true console.log(pbottleRPA.utils.isNumeric("10")); // 检测数字字符串:true console.log(pbottleRPA.utils.isNumeric("10.5")); // 检测小数字符串:true console.log(pbottleRPA.utils.isNumeric("abc")); // 检测非数字字符串:false console.log(pbottleRPA.utils.isNumeric(null)); // 检测null:false console.log(pbottleRPA.utils.isNumeric(NaN)); // 检测NaN:false pbottleRPA.wait(1) // 等待1秒钟 // 变量是否包含数据检测演示 console.log('变量是否包含数据测试:hasData'); // 在控制台输出当前演示功能 // 检测各种类型数据是否包含有效数据 console.log(pbottleRPA.hasData()); // 检测无参数:false console.log(pbottleRPA.hasData([])); // 检测空数组:false console.log(pbottleRPA.hasData({})); // 检测空对象:false console.log(pbottleRPA.hasData(0)); // 检测数字0:false console.log(pbottleRPA.hasData(Number("abc"))); // 检测NaN:false console.log(pbottleRPA.hasData("")); // 检测空字符串:false console.log(pbottleRPA.hasData(' ')); // 检测空格字符串:false console.log(pbottleRPA.hasData(false)); // 检测布尔值false:false console.log(pbottleRPA.hasData(null)); // 检测null:false console.log(pbottleRPA.hasData(undefined)); // 检测undefined:false console.log(pbottleRPA.hasData(0n)); // 检测BigInt 0:false console.log('--------------------------'); // 输出分隔线 console.log(pbottleRPA.hasData(800n)); // 检测非零BigInt:true console.log(pbottleRPA.hasData(3.14)); // 检测非零小数:true console.log(pbottleRPA.hasData('小瓶RPA ')); // 检测非空字符串:true console.log(pbottleRPA.hasData([12,5])); // 检测非空数组:true console.log(pbottleRPA.hasData({"pbottleRPA":666})); // 检测非空对象:true pbottleRPA.wait(1) // 等待1秒钟 // 文本截取演示 // 定义要处理的字符串 let str = "小瓶RPA官网是 https://www.pbottle.com 输入浏览器即可访问官网" pbottleRPA.log('文本截取测试',str) // 将原始字符串输出到日志 // 从字符串中截取指定标记之间的内容(从"官网是"到"输入"之间的内容) let sub_str = pbottleRPA.utils.substringFromTo(str,'官网是','输入') pbottleRPA.log(sub_str) // 将截取结果输出到日志 // 模拟资源管理器的文件搜索演示 console.log('模拟资源管理器的文件搜索:searchFile'); // 在控制台输出当前演示功能 // 在指定目录中搜索指定扩展名的文件 let rs = pbottleRPA.utils.searchFile('./','.png',true) // 在当前目录搜索.png文件,包含子目录 console.log('当前目录搜索.png文件',rs); // 输出搜索结果 ================================================ FILE: 异步子流程模板.js ================================================ /** * 小瓶RPA异步子流程模板 * * 功能说明:此脚本演示了RPA中如何使用异步子流程 * 通过这个示例,您可以学习如何在主流程中调用同步和异步子流程,并处理子流程的返回结果 */ // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 const pbottleRPA = require('./pbottleRPA.js') // 集中注册全局变量,其他地方使用不用加 global 前缀 global.global_processName = '小瓶RPA——XXXX流程模板' // 定义全局流程名称变量 global.global_startTime = pbottleRPA.utils.getTime() // 获取并定义全局开始时间变量 // 主流程函数,使用async关键字支持异步操作,使所有子流程序列化 async function main() { // 在日志中记录主流程开始信息,包括开始时间和流程名称 pbottleRPA.log("主流程开始 📍",global_startTime,global_processName) await 子流程2('输入参数2'); console.log('启动子流程 同步子流程'); // 通过require直接执行同步子流程(只包含顶层代码的脚本) require('./快速开始演示(3行代码).js') console.log('启动异步子流程 test.js'); // 调用异步子流程,等待其执行完成并获取返回结果 // 使用await关键字等待异步操作完成 let rs = await require('./test.js')('https://rpa.pbottle.com') // 传递参数给子流程 console.log('子流程返回结果:',rs); // 输出子流程返回的结果 // await process1() //有错误(被注释掉的错误示例) pbottleRPA.log("主流程完成 ✅️") // 在日志中记录主流程完成信息 } // 执行主流程函数,并捕获可能发生的错误 main().catch((e)=>{ // 主流程错误捕获 console.log('❌ 未完成,错误',e); // 输出错误信息 console.log('准备发送消息给管理员 👉'); // 输出后续处理信息 }) // test.js 文件内容说明(子流程): // module.exports = async (url) => { // console.log('子流程开始') // const res = await fetch(url) // pbottleRPA.log('网络请求完成!~',res.ok,res.url) // console.log('子流程结束') // return true // } async function 子流程2(params) { // 定义一个内联的异步函数作为子流程,接收参数,子流程推荐使用中文命名 console.log('子流程2开始',params); } ================================================ FILE: 微信朋友圈自动点赞.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了如何使用RPA自动给微信朋友圈点赞 * 通过图像识别技术定位界面元素,实现自动化的社交操作 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 pbottleRPA.显示系统消息('流程已开始运行','请打开电脑声音,关注运行日志信息') pbottleRPA.文字转语音('准备开始运行朋友圈批量点赞脚本,请先登录微信') pbottleRPA.等待(7) let 屏幕分辨率 = pbottleRPA.获取屏幕分辨率() pbottleRPA.日志输出('当前电脑屏幕分辨率',屏幕分辨率) if (屏幕分辨率.ratio !==1) { pbottleRPA.文字转语音('警告:此demo只适配无缩放屏幕') pbottleRPA.显示系统消息('警告:此demo只适配无缩放屏幕') pbottleRPA.日志输出('⚠ 警告:此demo只适配无缩放屏幕') pbottleRPA.等待(6) pbottleRPA.退出流程() } let 颜色1 = pbottleRPA.获取屏幕颜色(1,屏幕分辨率.h - 1); pbottleRPA.日志输出('系统任务栏色:',颜色1); pbottleRPA.键盘按键('ctrl+alt+w') // 等待指定图像出现,超时时间为120秒,期间会循环提示用户打开微信界面 let position = pbottleRPA.等待图像出现(['./input/pengYouQuanDianZan/0.png','./input/pengYouQuanDianZan/01.png'],()=>{ pbottleRPA.日志输出('等待中,请先打开电脑版微信界面'); },120) // 打开微信朋友圈 pbottleRPA.鼠标移动并点击(position.x,position.y); pbottleRPA.鼠标移动(屏幕分辨率.w/2,屏幕分辨率.h/2); let 计数器 = 0; function 重复() { pbottleRPA.日志输出('进入开始:') let 结果 = pbottleRPA.寻找图像(['./input/pengYouQuanDianZan/1.png','./input/pengYouQuanDianZan/11.png'],0.99,100,100) if (结果 === false) { pbottleRPA.日志输出('下一页') pbottleRPA.鼠标滚轮() pbottleRPA.等待(0.5) 重复() }else{ pbottleRPA.鼠标移动并点击(结果.x,结果.y); pbottleRPA.等待(0.1) pbottleRPA.鼠标移动并点击(结果.x-167,结果.y); 计数器 += 1 if (计数器 >= 1000) { pbottleRPA.退出流程('点1000个赞就行了,不要贪杯!~') } pbottleRPA.等待(0.1) 重复() } } 重复() ================================================ FILE: 快速开始演示(3行代码).js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:这是一个最基础的小瓶RPA JavaScript脚本示例,展示了如何使用RPA完成一个简单的百度搜索流程 * 此脚本会打开百度网站,在搜索框中输入"小瓶RPA官网",然后按回车键执行搜索 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 pbottleRPA.打开网址('https://www.baidu.com/') pbottleRPA.粘贴输入('小瓶RPA官网') pbottleRPA.键盘按键('enter') ================================================ FILE: 截屏操作演示脚本.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的截屏操作功能,包括全屏截图和区域截图 * 通过这些示例,您可以学习如何在RPA流程中捕获屏幕图像并保存 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 pbottleRPA.日志输出(pbottleRPA.获取格式化时间()) // 将当前格式化时间输出到日志文件中 pbottleRPA.文字转语音('开始运行小瓶RPA截屏操作演示脚本。... 快捷键 :Ctrl+shift+Q 可手动退出') // 使用文字转语音功能播报开始信息 pbottleRPA.等待(12) // 等待12秒钟,给用户时间准备 let 分辨率 = pbottleRPA.获取屏幕分辨率() // 获取当前电脑屏幕分辨率信息(宽w和高h) pbottleRPA.日志输出('当前电脑屏幕分辨率',分辨率) // 将屏幕分辨率信息输出到日志文件中 pbottleRPA.文字转语音(`当前电脑屏幕分辨率: ${分辨率.w} 乘以 ${分辨率.h}`) // 语音播报当前屏幕分辨率 pbottleRPA.等待(6) // 等待6秒钟 pbottleRPA.文字转语音('正在截屏(全屏)...') // 语音播报即将执行的操作 pbottleRPA.等待(3) // 等待3秒钟 // 执行全屏截图并保存到指定文件 pbottleRPA.屏幕截图('./小瓶RPA截图测试.png') // 对整个屏幕进行截图,并保存为PNG格式图片文件 pbottleRPA.文字转语音('图片保存在:当前目录') // 语音播报图片保存位置 pbottleRPA.日志输出('图片保存在:当前目录') // 将图片保存位置信息输出到日志文件中 pbottleRPA.等待(3) // 等待3秒钟 // 执行全屏截图但不指定保存路径(使用默认路径) pbottleRPA.屏幕截图(); // 对整个屏幕进行截图,使用系统默认保存路径 pbottleRPA.文字转语音('正在截屏(区域)...') // 语音播报即将执行的操作 // 执行区域截图(指定屏幕区域范围) let 结果 = pbottleRPA.屏幕截图('',分辨率.w/4,分辨率.h/4,分辨率.w/2,分辨率.h/2) // 对屏幕指定区域进行截图,参数依次为:保存路径、起始X坐标、起始Y坐标、宽度、高度 pbottleRPA.日志输出('截屏结果:',结果) // 将截屏操作的结果输出到日志文件中 pbottleRPA.等待(3) // 等待3秒钟 pbottleRPA.文字转语音('图片保存在:我的电脑 我的图片...') // 语音播报图片保存位置 pbottleRPA.日志输出('图片保存在:我的电脑 我的图片') // 将图片保存位置信息输出到日志文件中 pbottleRPA.等待(5) // 等待5秒钟 pbottleRPA.文字转语音('演示结束') // 语音播报演示结束信息 pbottleRPA.日志输出("准备结束脚本"); // 将脚本即将结束的信息输出到日志文件中 ================================================ FILE: 文件基础操作演示.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的基本文件操作功能,包括打开文件夹、打开文件、复制文件和删除文件 * 通过这个示例,您可以学习如何在自动化流程中进行常见的文件管理操作 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 const fs = require('fs') // 引入Node.js文件系统模块,用于执行文件操作 pbottleRPA.tts('打开文件夹') // 使用文字转语音功能播报即将执行的操作 console.log('打开文件夹'); // 在控制台输出相同信息 pbottleRPA.wait(3) // 等待3秒钟 console.log('./input/'); // 在控制台输出要打开的文件夹路径 pbottleRPA.openDir('./input/') // 使用RPA功能打开指定文件夹 pbottleRPA.wait(2) // 等待2秒钟观察效果 pbottleRPA.tts('打开图片') // 使用文字转语音功能播报即将执行的操作 console.log('打开图片'); // 在控制台输出相同信息 // 使用RPA功能打开指定图片文件(使用系统默认图片查看器) pbottleRPA.openfile('./input/RPAlogo128.png') pbottleRPA.wait(2) // 等待2秒钟观察效果 pbottleRPA.tts('关闭') // 使用文字转语音功能播报即将执行的操作 pbottleRPA.keyTap('alt+f4') // 模拟按下Alt+F4组合键关闭当前窗口 pbottleRPA.wait() // 等待默认时间 pbottleRPA.tts('复制文件') // 使用文字转语音功能播报即将执行的操作 console.log('复制文件') // 在控制台输出相同信息 // 使用Node.js文件系统API复制文件(从源文件复制到新文件) fs.copyFileSync('./input/RPAlogo128.png', './input/RPAlogo128-新复制.png') pbottleRPA.wait(3) // 等待3秒钟 pbottleRPA.tts('删除文件') // 使用文字转语音功能播报即将执行的操作 console.log('删除文件') // 在控制台输出相同信息 // 使用Node.js文件系统API删除指定文件 fs.unlinkSync('./input/RPAlogo128-新复制.png') pbottleRPA.tts('演示结束') // 使用文字转语音功能播报演示结束 console.log('演示结束') // 在控制台输出相同信息 // 显示系统消息框提示用户演示结束,并建议查看运行日志 pbottleRPA.showMsg('演示结束','请查看运行日志') ================================================ FILE: 文字提取查找OCR演示.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的OCR文字识别和查找功能 * 通过这些示例,您可以学习如何使用AI技术识别屏幕上的文字内容并进行查找定位 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 console.log("=== OCR 识别测试 ==="); // 在控制台输出测试标题 console.log('屏幕分辨率:',pbottleRPA.getResolution()) // 在控制台输出当前屏幕分辨率信息 pbottleRPA.tts('正在识别您的电脑屏幕左上角区域文字') // 使用文字转语音功能播报即将执行的操作 pbottleRPA.wait(5) // 等待5秒钟 let start = Date.now() // 记录OCR识别开始时间(用于计算耗时) console.log('屏幕orc结果:',pbottleRPA.aiOcr('screen',10,10,500,500)) // 对屏幕左上角区域(10,10,500,500)进行OCR文字识别,并输出结果到控制台 let end = Date.now(); // 记录OCR识别结束时间 console.log('OCR耗时:(秒)',(end-start)/1000); // 计算并输出OCR识别耗时(转换为秒) pbottleRPA.tts("已经输出 JSON 格式到运行日志") // 语音播报OCR结果已输出 pbottleRPA.wait(5); // 等待5秒钟 console.log("查找并点击微信") // 在控制台输出操作信息 pbottleRPA.tts("查找并点击微信") // 语音播报即将执行的操作 let position = pbottleRPA.findText('微信',100,100,500,1080) // 在屏幕区域内查找"微信"文字,返回位置信息 console.log('查找文字结果:',position); // 在控制台输出查找到的文字位置信息 if (position) { // 如果找到了指定文字 pbottleRPA.moveAndClick(position.x,position.y) // 移动鼠标到文字位置并点击 }else{ // 如果没有找到指定文字 console.log("范围内没有找到文字:微信"); // 在控制台输出未找到的提示信息 } pbottleRPA.wait(2) pbottleRPA.openURL("https://rpa.pbottle.com/") console.log("等待文字"); // 在控制台输出脚本即将结束的信息 pbottleRPA.tts("等待文字"); // 语音播报脚本即将结束的信息 position = pbottleRPA.waitText('专业用户RPA软件',300,500) // 等待微信应用加载 console.log(position); pbottleRPA.moveMouse(position.x,position.y) // 移动鼠标到文字位置 ================================================ FILE: 用户手动输入变量示例.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:这是一个最基础的小瓶RPA JavaScript脚本示例,展示小瓶RPA动态输入内容的方法 * 小瓶RPA基座 V2026.0.0 以上版本可用 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 const content1 = pbottleRPA.waitInput('请输入第一个数字:') const content2 = pbottleRPA.waitInput('请输入第二个数字:') console.log('输入的数字是:',content1,content2); pbottleRPA.log('输入完成 ✅️, 相加等于:', parseFloat(content1) + parseFloat(content2)) ================================================ FILE: 键盘基础操作演示脚本.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的各种键盘操作,包括按键、组合键、页面导航等 * 通过这些示例,您可以学习如何在RPA流程中精确控制键盘行为 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 console.log("=== 键盘操作测试 ==="); // 在控制台输出测试标题 console.log(Date()); // 在控制台输出当前日期时间 pbottleRPA.setDefaultDelay(0); // 设置默认操作延时为0,手动管理所有操作延时 pbottleRPA.tts('开始运行小瓶RPA键盘操作演示脚本。... 快捷键 :Ctrl+shift+Q 可手动退出') // 使用文字转语音功能播报开始信息 pbottleRPA.wait(12) // 等待12秒钟,给用户时间准备 let resolution = pbottleRPA.getResolution() // 获取当前电脑屏幕分辨率信息 console.log('当前电脑屏幕分辨率', resolution) // 在控制台输出屏幕分辨率信息 pbottleRPA.tts(`当前电脑屏幕分辨率: ${resolution.w} 乘以 ${resolution.h}`) // 语音播报当前屏幕分辨率 pbottleRPA.wait(6) // 等待6秒钟 pbottleRPA.tts('准备打开网页浏览并用快捷键进入全屏,5秒后开始') // 语音播报即将执行的操作 pbottleRPA.wait(10) // 等待10秒钟 pbottleRPA.openURL('https://rpa.pbottle.com?from=demo') // 使用默认浏览器打开小瓶RPA官网 pbottleRPA.wait(3) // 等待3秒钟让网页加载完成 pbottleRPA.moveAndClick(50,500) // 移动鼠标到指定坐标并点击,确保页面获得焦点 pbottleRPA.tts('缩放页面') // 语音播报即将执行的操作 // 使用组合键控制页面缩放 pbottleRPA.keyTap('ctrl + -') // 模拟按下Ctrl+-组合键,缩小页面 pbottleRPA.keyTap('ctrl + -') // 再次缩小页面 pbottleRPA.keyTap('ctrl + -') // 第三次缩小页面 pbottleRPA.keyTap('ctrl + =') // 模拟按下Ctrl+=组合键,放大页面 pbottleRPA.keyTap('ctrl + =') // 再次放大页面 pbottleRPA.keyTap('ctrl + =') // 第三次放大页面 pbottleRPA.wait(1) // 等待1秒钟 pbottleRPA.keyTap('f11') // 模拟按下F11键,进入浏览器全屏模式 pbottleRPA.wait(2) // 等待2秒钟 pbottleRPA.tts('翻页查看') // 语音播报即将执行的操作 // 使用Page Down键向下翻页浏览页面内容 pbottleRPA.keyTap('page down') // 模拟按下Page Down键,向下翻页 pbottleRPA.wait() // 等待默认时间(使用默认延时) pbottleRPA.keyTap('page down') // 再次向下翻页 pbottleRPA.wait() // 等待默认时间 pbottleRPA.keyTap('page down') // 第三次向下翻页 pbottleRPA.wait() // 等待默认时间 pbottleRPA.tts('翻页回来') // 语音播报即将执行的操作 // 使用Page Up键向上翻页回到页面顶部 pbottleRPA.keyTap('page up') // 模拟按下Page Up键,向上翻页 pbottleRPA.wait() // 等待默认时间 pbottleRPA.keyTap('page up') // 再次向上翻页 pbottleRPA.wait() // 等待默认时间 pbottleRPA.keyTap('page up') // 第三次向上翻页 pbottleRPA.wait(2) // 等待2秒钟 pbottleRPA.tts('收藏我们吧,十分感谢') // 语音播报即将执行的操作 pbottleRPA.keyTap('ctrl + d') // 模拟按下Ctrl+D组合键,打开浏览器收藏夹对话框 pbottleRPA.wait(1) // 等待1秒钟 pbottleRPA.keyTap('enter') // 模拟按下Enter键,确认收藏操作 pbottleRPA.wait(2) // 等待2秒钟 pbottleRPA.keyTap('f11') // 模拟按下F11键,退出浏览器全屏模式 pbottleRPA.tts('演示结束') // 语音播报演示结束信息 console.log("准备结束脚本"); // 在控制台输出脚本即将结束的信息 pbottleRPA.exit("脚本结束",true) // 退出RPA脚本执行,并输出结束信息 ================================================ FILE: 鼠标基础操作演示.js ================================================ /** * 小瓶RPA演示demo,具体api请查看*流程开发文档* * 官网:https://rpa.pbottle.com/ * 流程开发文档:https://rpa.pbottle.com/docs/ * * 功能说明:此脚本演示了RPA中的各种鼠标操作,包括移动、点击、双击、滚轮、拖拽等 * 通过这些示例,您可以学习如何在RPA流程中精确控制鼠标行为 */ const pbottleRPA = require('./pbottleRPA') // 引入小瓶RPA的核心库,获得对RPA功能的访问权限 console.log("=== 鼠标基础演示测试 ==="); // 在控制台输出测试标题 console.log(Date()); // 在控制台输出当前日期时间 pbottleRPA.setDefaultDelay(0); // 设置默认操作延时为0,手动管理所有操作延时 pbottleRPA.tts('开始运行小瓶RPA鼠标操作演示脚本。... 快捷键 :Ctrl+shift+Q 可手动退出') // 使用文字转语音功能播报开始信息 pbottleRPA.wait(12) // 等待12秒钟,给用户时间准备 let resolution = pbottleRPA.getResolution() // 获取当前电脑屏幕分辨率信息 console.log('当前电脑屏幕分辨率',resolution) // 在控制台输出屏幕分辨率信息 pbottleRPA.keyTap('windows+d') // 模拟按下Win+D组合键,显示桌面 pbottleRPA.tts(`当前电脑屏幕分辨率: ${resolution.w} 乘以 ${resolution.h}`) // 语音播报当前屏幕分辨率 pbottleRPA.wait(6) // 等待6秒钟 pbottleRPA.tts(`移动指针到屏幕中点`) // 语音播报即将执行的操作 pbottleRPA.log(`移动指针到屏幕中点`) // 将操作记录到日志文件中 pbottleRPA.moveMouse(resolution.w/2,resolution.h/2) // 移动鼠标到屏幕中心点位置 pbottleRPA.wait(3) // 等待3秒钟观察效果 pbottleRPA.tts(`长按左键`) // 语音播报即将执行的操作 pbottleRPA.log(`长按左键`) // 将操作记录到日志文件中 pbottleRPA.mouseClick('left',1500); // 长按鼠标左键1500毫秒(1.5秒) pbottleRPA.wait(2) // 等待2秒钟 pbottleRPA.tts(`鼠标双击`) // 语音播报即将执行的操作 pbottleRPA.log(`鼠标双击`) // 将操作记录到日志文件中 pbottleRPA.moveMouse(38,38) // 移动鼠标到屏幕左上角坐标(38,38)位置 pbottleRPA.mouseDoubleClick() // 执行鼠标双击操作 pbottleRPA.wait(3) // 等待3秒钟 pbottleRPA.tts('准备打开网页并滚动鼠标,5秒后开始') // 语音播报即将执行的操作 pbottleRPA.wait(10) // 等待10秒钟 pbottleRPA.openURL('https://rpa.pbottle.com?from=demo') // 使用默认浏览器打开小瓶RPA官网 pbottleRPA.wait(3) // 等待3秒钟让网页加载完成 pbottleRPA.keyTap('f11') // 模拟按下F11键,进入浏览器全屏模式 pbottleRPA.wait(1) // 等待1秒钟 pbottleRPA.tts('滚动鼠标') // 语音播报即将执行的操作 pbottleRPA.mouseWheel() // 执行鼠标滚轮向下滚动操作 pbottleRPA.wait(4) // 等待4秒钟观察效果 pbottleRPA.tts('反向滚动鼠标') // 语音播报即将执行的操作 pbottleRPA.mouseWheel(360) // 执行鼠标滚轮向上滚动360单位的操作 pbottleRPA.wait(4) // 等待4秒钟观察效果 pbottleRPA.tts('右键页面') // 语音播报即将执行的操作 pbottleRPA.log('右键页面') // 将操作记录到日志文件中 pbottleRPA.moveMouse(200,250) // 移动鼠标到坐标(200,250)位置 pbottleRPA.mouseClick('right') // 执行鼠标右键单击操作 pbottleRPA.wait(3) // 等待3秒钟 pbottleRPA.moveMouse(150,250) // 移动鼠标到坐标(150,250)位置 pbottleRPA.tts('左键单击') // 语音播报即将执行的操作 pbottleRPA.log('左键单击') // 将操作记录到日志文件中 pbottleRPA.mouseClick() // 执行鼠标左键单击操作(默认为左键) pbottleRPA.wait(3) // 等待3秒钟 // 测试鼠标拖拽操作 pbottleRPA.tts('拖拽或选区') // 语音播报即将执行的操作 pbottleRPA.log('拖拽或选区') // 将操作记录到日志文件中 pbottleRPA.wait(4) // 等待4秒钟 pbottleRPA.moveMouse(resolution.w*0.7,resolution.h*0.5) // 移动鼠标到屏幕右侧中间位置 pbottleRPA.wait(1) // 等待1秒钟 pbottleRPA.mouseLeftDragTo(resolution.w*0.3,resolution.h*0.2) // 从当前位置拖拽到屏幕左侧上方位置 pbottleRPA.tts('缓慢拖拽') // 语音播报即将执行的操作 pbottleRPA.log('缓慢拖拽') // 将操作记录到日志文件中 pbottleRPA.wait(4) // 等待4秒钟 pbottleRPA.mouseClick() // 执行鼠标单击操作 pbottleRPA.wait(1) // 等待1秒钟 pbottleRPA.mouseKeyToggle('left','down') // 按下鼠标左键(保持按下状态) pbottleRPA.moveMouse(resolution.w*0.7,resolution.h*0.5,3) // 缓慢移动鼠标到指定位置(耗时3秒) pbottleRPA.mouseKeyToggle('left','up') // 松开鼠标左键 pbottleRPA.wait(5) pbottleRPA.mouseClick() pbottleRPA.keyTap('f11') pbottleRPA.tts('演示结束') console.log("准备结束脚本"); pbottleRPA.wait(3) //脚本强制退出 process.exit(1) console.log("已经退出了,无效");