Node.js 的桌面自动化框架nut.js :使用js或者ts控制鼠标键盘「终于解决」

Node.js 的桌面自动化框架nut.js :使用js或者ts控制鼠标键盘「终于解决」为了使用这些预编译的绑定,必须满足某些运行时条件。如果您运行的是 Windows 10 N 并且想要使用 ImageFinder 插件,请确保安

欢迎大家来到IT世界,在知识的湖畔探索吧!

nut.js 是 Node.js 的桌面自动化框架,允许您使用 JavaScript 或 TypeScript 对鼠标和键盘进行编程!

Node.js 的桌面自动化框架nut.js :使用js或者ts控制鼠标键盘「终于解决」

第一步

nut.js 的第一步——从安装到自动化鼠标移动

先决条件


nut.js 为您各自的目标平台提供了预构建版本的 OpenCV。为了使用这些预编译的绑定,必须满足某些运行时条件。

Windows


如果您运行的是 Windows 10 N 并且想要使用 ImageFinder 插件,请确保安装了媒体功能包。

macOS


在 macOS 上,需要 Xcode 命令行工具。您可以通过运行以下命令来安装它们:

xcode-select --install

欢迎大家来到IT世界,在知识的湖畔探索吧!

注意:

如果您遇到鼠标不动或键盘不打字等问题,请确保为您执行测试的进程提供可访问权限。

如果缺少权限,nut.js 会给你一个提示:

##### WARNING! The application running this script is not a trusted process! Please visit https://github.com/nut-tree/nut.js#macos #####

当应用程序想要使用辅助功能时,应该显示权限弹出窗口。如果没有,您可以尝试手动添加运行脚本的应用程序。

设置 -> 安全和隐私 -> 隐私选项卡 -> 辅助功能 -> 添加…

例如,如果你想在iTerm2执行你的node脚本,您必须将 iTerm.app 添加到列表中。从内置终端运行脚本时,例如 VSCode 或 IntelliJ,您必须添加相应的 IDE。

Node.js 的桌面自动化框架nut.js :使用js或者ts控制鼠标键盘「终于解决」

Linux


根据您的发行版,Linux 设置可能会有所不同。

一般来说,nut.js 需要 libXtst

在 *buntu 发行版上安装:

欢迎大家来到IT世界,在知识的湖畔探索吧!sudo apt-get install build-essential libxtst-dev

其他linux发行版的设置可能不同。

node.js


nut.js 针对各种版本的node.js构建和测试的。但是,为了获得最佳兼容性,建议运行node.js的最新可用 LTS 版本(在撰写本文时为 lts/gallium)并使用像 nvm 这样的版本管理器。

安装


满足我们的先决条件后,让我们继续安装。以下步骤将在您选择的目录中执行。我们将此目录简称为工作目录。

npm project


让我们首先通过执行以下命令在我们的工作目录中初始化一个新的 npm 项目:

npm init

随意填写交互式对话,但这并不是继续初始设置的硬性要求。

(你可以通过运行 npm init -y 来接受所有默认值)

安装nut.js


运行

欢迎大家来到IT世界,在知识的湖畔探索吧!npm i @nut-tree/nut-js

yarn add @nut-tree/nut-js

在我们新创建的 npm 项目中,将安装 nut.js 及其所需的依赖项。

快照发布版


nut.js 还提供快照版本,允许测试即将推出的功能。

运行

npm i @nut-tree/nut-js@next

或者

yarn add @nut-tree/nut-js@next

将安装最新的 nut.js 开发版本。

注意:虽然快照版本非常适合在新的稳定版本之前与即将推出的功能一起使用,但它仍然是快照版本。请记住,快照版本在将来可能会发生变化和/或中断,因此不建议在生产中使用它们 .

动起来


现在都已准备就绪,是时候开始行动了!

在我们的工作目录中,让我们创建一个新文件 index.js。

在您喜欢的编辑器中打开它并添加以下行以开始使用:

const { mouse } = require("@nut-tree/nut-js");

(async () => {
    
})();

mouse 让您可以控制您的鼠标,让我们来玩一玩吧!

注意:nut.js 是完全异步的,所以在大多数示例中你会看到类似上面代码片段的内容,这是一个异步操作 IIFE,用作在顶层使用 async / await 的变通方法。

简单移动鼠标


const { mouse, left, right, up, down } = require("@nut-tree/nut-js");

(async () => {
    await mouse.move(left(500));
    await mouse.move(up(500));
    await mouse.move(right(500));
    await mouse.move(down(500))
})();

nut.js 提供了一个声明式 API,因此我们可以使用 MovementApi 函数将光标相对于当前位置移动,而不是明确指明光标移动到的位置。

当通过 node index.js 执行时,你会看到你的光标沿着一个正方形的变移动,并结束在它的初始位置。

针对特定点


能够向上、向下、向左或向右移动光标是一个好的开始,但我们并不是坐在画板前素描。让我们看看如何在屏幕上定位特定点。

const { mouse, straightTo, Point } = require("@nut-tree/nut-js");

(async () => {
    const target = new Point(500, 350);
    
    await mouse.move(straightTo(target));
})();

straightTo 是另一个 MovementApi 函数,它接受一个目标点并计算一条朝向它的直线,从我们当前的光标位置开始到目标点。

加速/减速


如果您想将鼠标移动速度配置得更快/更慢,nut.js 公开的每个实例都提供了一个配置对象。

鼠标配置对象允许您配置以像素/秒为单位的移动速度。

const { mouse, straightTo, Point } = require("@nut-tree/nut-js");

(async () => {
    mouse.config.mouseSpeed = 2000;
    const fast = new Point(500, 350);
    await mouse.move(straightTo(fast));
    mouse.config.mouseSpeed = 100;
    const slow = new Point(100, 150);
    await mouse.move(straightTo(slow));
})();

瞬移


有时我们不想沿着路径移动到某个点。

在这种情况下,我们可以依靠 setPosition 立即将光标位置更改为提供的点。

const { mouse, Point } = require("@nut-tree/nut-js");

(async () => {
    const target = new Point(500, 350);
    await mouse.setPosition(target);
})();

总结


  • nut.js 提供了mouse实例来控制你的光标。
  • 以像素/秒为单位的移动速度可通过配置对象进行配置。
  • 它提供了一个高级 MovementApi 以方便相对鼠标起始点移动。
  • 对于快速更改光标位置,setPosition 是正确的方法。

屏幕搜索

了解如何在屏幕上搜索图像以将其用于自动化

图像搜索


“张图片胜过千言万语”

nut.js 允许您在屏幕上定位图像,这是自动化的一个关键功能。

ImageFinder 提供程序


为此,我们必须安装一个额外的包,提供执行图像比较的功能。否则,所有依赖图像匹配的函数都会抛出错误,如 Error: No ImageFinder registered。

一个可用的选项是@nut-tree/template-matcher:

npm i @nut-tree/template-matcher

要使用此提供程序包,只需在您的代码中引入它,例如 index.js:

const { screen, imageResource } = require("@nut-tree/nut-js");
require("@nut-tree/template-matcher"); // 这是新加的

(async () => {
  try {
    await screen.find(imageResource("img.png"));
  } catch (e) {
    console.error(e);
  }
})();

提供程序的功能


find、findAll 和 waitFor 是图像搜索的主要函数。

当 find 和 findAll 尝试立即在屏幕上定位图像时,waitFor 将重复扫描屏幕以查找图像,直到达到特定的超时时间。

默认情况下,图像搜索是在多个尺度上进行的。

在自动化更复杂的任务时,上述所有功能都是非常强大的帮手,所以让我们看看如何利用它们来发挥我们的优势!

使用模板图像


为了在您的屏幕上搜索图像,我们必须提供模板图像。

这些图像可以通过它们的完整路径和 loadImage 加载,也可以相对于可配置的资源目录加载。

资源目录


使用资源目录时,您可以通过文件名引用模板图像,省略完整路径。在加载模板图像时,这些文件名是相对于 screen.config.resourceDirectory 的。

screen.config.resourceDirectory = "/path/to/my/template/images"

如果未明确配置,screen.config.resourceDirectory 将设置为当前工作目录。

从资源目录加载图像


不使用 loadImage,而是通过 imageResource 加载所谓的图像资源。

从远程主机获取图像


fetchFromUrl 允许您将 URL 传递给位于远程主机上的图像,该图像将被获取并作为 nut.js 图像返回。

find


模板图像是使用其完整路径直接加载的图像,相对于可配置的资源目录或通过 fetchFromUrl 从远程主机加载。

寻找图像


让我们通过查看示例片段来剖析 screen.find 的工作原理:

const { screen, imageResource } = require("@nut-tree/nut-js");
require("@nut-tree/template-matcher");

(async () => {
    screen.config.resourceDirectory = "/resouce/path";
    try {
        const region = await screen.find(imageResource("mouse.png"));
        console.log(region);
    } catch (e) {
        console.error(e);
    }
})();

首先,在第 1 行和第 2 行设置导入。

第 5 行设置我们的资源目录,尽管最有趣的事情发生在第 7 行:实际搜索图像。

screen.find 将扫描您的主屏幕以查找提供的模板图像,如果找到匹配项,它将返回模板图像所在的区域。图像以每个像素为基础进行匹配。匹配像素的数量是可配置的, 通过配置对象的 confidence 属性设置。confidence 应为 0 到 1 之间的值,默认为 0.99(对应于 99% 匹配)。

nut.js 目前不支持多显示器设置

跨平台技巧


资源目录起初可能看起来让你很混乱,但它实际上有一个非常好的副作用。想象一下编写一个跨平台的自动化脚本,我们正在处理不同的 UI,因此需要处理不同的模板图像。

使用资源目录,我们可以根据当前平台配置目录:

screen.config.resourceDirectory = `/path/to/the/project/${process.platform}`;

这样,我们可以将所有特定于平台的模板图像保存在单独的文件夹中,这样我们不必关心我们的代码了。

通过使用依赖于平台的资源目录,我们不必处理平台特定的文件名。相同的文件名将为当前平台加载正确的模板图像,无需进一步操作!

故障排除


万一我们搞砸了,nut.js 会通过拒绝抛出错误让我们知道。

错误的资源目录


Searching for mouse.png failed. Reason: ‘Error: Failed to load /foo/bar/mouse.png. Reason: ‘Failed to load image from ‘/foo/bar/mouse.png”.’

未找到匹配图像


Searching for mouse.png failed. Reason: ‘Error: No match with required confidence 0.99. Best match: 0 at (0, 0, 477, 328)’

总结


  • nut.js 提供了一个屏幕实例来搜索屏幕上的模板图像。
  • 从中加载模板图像的目录可通过配置对象进行配置。
  • 它将在您的主屏幕上搜索模板图像,如果找到匹配项,它将返回模板图像所在的区域。
  • 匹配像素的数量可通过配置对象的置信度属性进行配置。

findAll


findAll 与 find 的用法非常相似。两者之间的主要区别在于 findAll 将在主屏幕上返回所有检测到的匹配项的列表。

find 中提到的所有其他内容也适用于 findAll。

waitFor


能够在我们的屏幕上定位图像在自动化时是一个巨大的有事,但实际上,我们必须处理时间超时。waitFor 在这里帮助我们指定一个超时时长,希望多久时间内我们的模板图像出现在屏幕上 !

模板图像是使用其完整路径直接加载的图像,相对于可配置的资源目录或通过 fetchFromUrl 从远程主机加载。

等待图像


让我们稍微调整一下查找示例中使用的代码片段:

const { screen, imageResource } = require("@nut-tree/nut-js");
require("@nut-tree/template-matcher");

(async () => {
    screen.config.resourceDirectory = "/resouce/path";
    try {
        const region = await screen.waitFor(imageResource("mouse.png"));
        console.log(region);
    } catch (e) {
        console.error(e);
    }
})();

waitFor 基本上与 find 完全相同,但在指定的时间段内执行多次。

它会扫描您的主屏幕以查找给定的模板图像,但如果找不到它,它会再试一次。这些重试发生的时间间隔也是可配置的。

const { screen, imageResource } = require("@nut-tree/nut-js");
require("@nut-tree/template-matcher");

(async () => {
    screen.config.resourceDirectory = "/resouce/path";
    try {
        const region = await screen.waitFor(imageResource("mouse.png"), 5000, 1000);
        console.log(region);
    } catch (e) {
        console.error(e);
    }
})();

在上面的代码片段中,我们告诉 waitFor 最多寻找模板图像五秒钟,每秒重试一次。

如果在配置的毫秒超时后仍然找不到图像,它将拒绝。否则,它将返回它定位图像的区域,就像查找一样。

故障排除


find 中提到的所有内容也适用于 waitFor。

超时


操作在 5000 毫秒后超时

总结


  • waitFor 将重复搜索您的主屏幕以查找模板图像,如果找到匹配项,它将返回模板图像所在的区域。
  • 如果找不到图像,它将以可配置的时间间隔重试搜索,直到达到配置的超时(以毫秒为单位)。

取消waitFor


正如我们之前了解到的,waitFor 将重复搜索我们的屏幕以查找给定的模板图像。

这种巨大的灵活性不是免费的,因此我们可能不想等待超时触发才能取消正在进行的搜索。nut.js 遵循与浏览器获取 API 相同的取消方法,使用 AbortController。

在我们实际查看示例之前,我们必须为我们的项目安装一个额外的包:

npm i node-abort-controller

现在,让我们看一个(人为的)例子:

const { screen, Region, imageResource } = require("@nut-tree/nut-js");
require("@nut-tree/template-matcher");
const { AbortController } = require("node-abort-controller");

(async () => {
    const controller = new AbortController();
    screen.waitFor(imageResource("test.png"), 5000, 1000, {abort: controller.signal});
    setTimeout(() => controller.abort(), 2000);
})();

我们在第 6 行实例化我们的 AbortController 并将其信号作为 OptionalSearchParameter 传递给 waitFor。

waitFor 配置了 5000 毫秒的超时,1000 毫秒后重试,但 2000 毫秒后,我们在 AbortController 上调用 abort() ,这将取消正在进行的搜索:

Action aborted by signal

总结


  • waitFor 可以使用 AbortController 取消。

突出显示


特别是在开发过程中,我们可能希望直观地跟踪执行脚本时发生的情况。尤其是在当涉及到图像搜索时。 我们找到匹配项的日志,但视觉指示器会更好。

高亮就是用来解决这个问题的!

配置


通过用不透明的突出显示窗口覆盖感兴趣区域来突出显示区域。

高亮显示持续时间和不透明度成为 screen.config 对象上的可配置属性。

  • highlightDurationMs
  • highlightOpacity

突出显示区域


highlight 接收一个 Region 指定要突出显示的区域。然后它将用不透明的突出显示窗口覆盖给定的区域。

const { screen, Region } = require("@nut-tree/nut-js");

(async () => {
    screen.config.highlightDurationMs = 3000;
    const highlightRegion = new Region(50, 100, 200, 300);
    await screen.highlight(highlightRegion);
})();

自动突出显示


API 的结构方式非常容易突出显示位于例如位置的区域。 寻找:

const { screen, imageResource } = require("@nut-tree/nut-js");
require("@nut-tree/template-matcher");

(async () => {
    screen.config.resourceDirectory = "/resouce/path";
    screen.config.highlightDurationMs = 3000;
    await screen.highlight(screen.find(imageResource("image.png")));
})();

然而,手动添加突出显示不仅麻烦,而且还需要额外的工作,因为我们在生产环境中运行脚本之前要再次删除它。

因此,nut.js 提供了一种自动高亮机制,可通过配置属性切换。在开发期间突出显示,在生产中禁用它!

const { screen, imageResource } = require("@nut-tree/nut-js");
require("@nut-tree/template-matcher");

(async () => {
    screen.config.resourceDirectory = "/resouce/path";
    screen.config.autoHighlight = true;
    screen.config.highlightDurationMs = 1500;
    await screen.find(imageResource("test.png"));
})();

开启自动高亮后,我们再也不用关心手动高亮查找结果了。一旦find返回一个有效的Region,它就会被高亮显示。而且由于waitFor重用了find,自动高亮也在那里起作用!

Summary


  • nut.js 提供了一种可视化调试图像搜索结果的方法。
  • 突出显示持续时间和突出显示窗口不透明度都可以通过配置对象进行配置。
  • 自动突出显示将自动突出显示从查找返回的结果。

参数化搜索


find、findAll 和 waitFor 接受 OptionalSearchParameters 以微调搜索。

这允许例如 将搜索空间限制在屏幕的特定部分:

const { screen, Region, OptionalSearchParameters, imageResource } = require("@nut-tree/nut-js");
require("@nut-tree/template-matcher");
const { AbortController } = require("node-abort-controller");

(async () => {
    // 配置你希望搜索的区域的位置和大小
    const searchRegion = new Region(10, 10, 500, 500);
    
    // 配置您希望 Nut 在找到匹配项之前拥有的相似度比率
    const confidence = 0.88;
    
    // 配置一个 Abort controller,这样可以随时取消查找操作
    const controller = new AbortController();
    const { signal } = controller;
    
    // 将您的参数输入 OptionalSearchParameters 构造函数以确保它们符合规范
    const fullSearchOptionsConfiguration = new OptionalSearchParameters(searchRegion, confidence, signal);
    
    //.find() 将根据您的搜索参数和提供的图像数据返回找到匹配项的区域
    const matchRegion = await screen.find(imageResource("image.png"), fullSearchOptionsConfiguration);
    
    const cancelFindTimeout = setTimeout(() => {
      controller.abort();
    }, 5000);
    
    
})();

单一与多尺度搜索


多尺度图像搜索让您在多个屏幕分辨率之间切换时具有弹性,但也有代价。与在单一尺度上搜索相比,在多个尺度上搜索时可能需要更长的时间。

根据您手头的任务,您可能不需要这种额外的灵活性,而是希望从更快的执行中获益。有关示例,请参见此示例测试结果:

hyperfine --warmup 3 'node multi-scale.js' 'node single-scale.js' --show-output
Benchmark 1: node multi-scale.js
  Time (mean ± σ):     933.5 ms ±  10.4 ms    [User: 1647.4 ms, System: 433.8 ms]
  Range (min … max):   920.9 ms … 948.4 ms    10 runs

Benchmark 2: node single-scale.js
  Time (mean ± σ):     526.8 ms ±   9.3 ms    [User: 400.2 ms, System: 108.4 ms]
  Range (min … max):   514.3 ms … 544.4 ms    10 runs

Summary
  'node single-scale.js' ran
    1.77 ± 0.04 times faster than 'node multi-scale.js'

使用窗口

了解如何通过使用窗口来增强您的工作流程

getActiveWindow 允许您在函数调用时获取系统焦点窗口的 Window 引用。getActiveWindow 将作为 Promise 返回。

const { getActiveWindow } = require('@nut-tree/nut-js');

(async () => {
    const windowRef = await getActiveWindow();
})();

现在单独输出这个不会很有用,记住它只是一个参考:

// source:
//  ...
    const windowRef = await getActiveWindow();
    console.log(windowRef);
//  ...

// output:
Window {
  providerRegistry: DefaultProviderRegistry {
  // ...
  },
  windowHandle: 2165090
}

相反,我们想利用窗口的标题和区域属性。 不过要小心,这些是 getter 属性,每个属性都返回一个 Promise 而不是一个普通值——所以我们必须使用 await :

const { getActiveWindow } = require('@nut-tree/nut-js');

(async () => {
    const windowRef = await getActiveWindow();
    const title = await windowRef.title
    const region = await windowRef.region
    console.log(title, region)
})();

您还可以并行而不是顺序等待这些值,如下所示:

const { getActiveWindow } = require('@nut-tree/nut-js');

(async () => {
    const windowRef = await getActiveWindow();
    const [title, region] = await Promise.all([windowRef.title, windowRef.region])
    console.log(title, region)
})();

此外,请注意此脚本将始终详细说明您从中运行它的 shell 的窗口信息。 要尝试获取不同窗口的详细信息,请考虑在调用 getActiveWindow 之前添加延迟。 我们可以使用 nut.js 自带的 sleep 辅助函数来实现:

const { getActiveWindow, sleep } = require('@nut-tree/nut-js');

(async () => {
    await sleep(4000) //延迟 4 秒再继续
    const windowRef = await getActiveWindow();
    const [title, region] = await Promise.all([windowRef.title, windowRef.region])
    console.log(title, region)
})();

示例


在激活窗口内点击


const { getActiveWindow, centerOf, randomPointIn, mouse, sleep } = require('@nut-tree/nut-js');

(async () => {
    const windowRef = await getActiveWindow();
    const region = await windowRef.region
    await mouse.setPosition(await centerOf(region))
    await mouse.leftClick()
    await sleep(1000)
    await mouse.setPosition(await randomPointIn(region))
    await mouse.leftClick()
})();

重复记录活动窗口信息


const { getActiveWindow } = require('@nut-tree/nut-js');

(async () => {
    setInterval(async () => {
        const windowRef = await getActiveWindow()
        const [title, region] = await Promise.all([windowRef.title, windowRef.region])
        console.log(title, region.toString())
    }, 2000)
})();

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/14044.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信