You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

539 lines
13 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package xyz.wbsite.jmacro;
import cn.hutool.core.util.RandomUtil;
import org.sikuli.basics.Settings;
import org.sikuli.script.FindFailed;
import org.sikuli.script.Image;
import org.sikuli.script.Key;
import org.sikuli.script.KeyModifier;
import org.sikuli.script.Location;
import org.sikuli.script.Match;
import org.sikuli.script.Mouse;
import org.sikuli.script.Pattern;
import org.sikuli.script.Region;
import org.sikuli.script.Screen;
import xyz.wbsite.jmacro.base.ColorLocation;
import xyz.wbsite.jmacro.base.Legend;
import xyz.wbsite.jmacro.util.Logger;
import xyz.wbsite.jmacro.util.MousePathUtil;
import xyz.wbsite.jmacro.util.TaskUtil;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 脚本
*
* @author wangbing
* @version 0.0.1
* @since 1.8
*/
public abstract class JMacro {
/**
* 脚本执行状态
*/
private boolean run;
/**
* 默认超时秒数
*/
private int defaultTimeOut = 10;
/**
* 工作区域
*/
protected Region workRegion;
/**
* 屏幕对象
*/
protected Screen screen = new Screen();
public JMacro() {
this.startFocus();
// 禁止漂移,所有拟人操作都提前计算好路径,此处的随机要关闭
Mouse.setRandom(0);
// 鼠标移动延迟(秒),默认值约为 0.5
Settings.MoveMouseDelay = 0.0f;
// 忽略鼠标移动事件
Mouse.setMouseMovedAction(0);
}
public boolean isRun() {
return run;
}
public void setRun(boolean run) {
this.run = run;
}
public int getDefaultTimeOut() {
return defaultTimeOut;
}
public void setDefaultTimeOut(int defaultTimeOut) {
this.defaultTimeOut = defaultTimeOut;
}
/**
* 开始聚焦
*/
public void startFocus() {
TaskUtil.asyncTask(() -> {
workRegion = TaskUtil.timeTask(this::focus, defaultTimeOut, TimeUnit.SECONDS);
if (workRegion != null) {
Logger.info("聚焦成功");
} else {
Logger.error("聚焦失败");
}
});
}
/**
* 获取工作区域
*
* @return 工作区域
*/
public abstract Region focus();
/**
* 执行脚本
*/
public void start() {
if (this.run) {
Logger.error("脚本正在运行中");
return;
}
this.run = true;
this.task();
this.stop();
}
/**
* 执行脚本
*/
public abstract void task();
/**
* 停止脚本
*/
public void stop() {
if (!this.run) {
Logger.error("脚本未运行");
return;
}
this.run = false;
}
/**
* 键盘按键组
*/
public void keyInput(String keys) {
this.screen.type(keys);
}
/**
* 键盘按键
* keycode Key to press (e.g. <code>KeyEvent.VK_A)
*/
public void keyPress(String key) {
this.screen.type(key);
}
/**
* 鼠标移动
*
* @param location 坐标点
*/
public void mouseMove(Location location) {
List<int[]> path = MousePathUtil.generateBezierPath(Mouse.at(), location);
Settings.MoveMouseDelay = 0.0f;
// 暂停点索引(-1为不暂停
int pause = -1;
// 通过概率决断本次是否需要产生暂停
if (RandomUtil.randomInt(0, 100) < 50) {
int startIdx = path.size() / 3;
int endIdx = 2 * path.size() / 3;
pause = RandomUtil.randomInt(startIdx, endIdx);
}
for (int[] p : path) {
delay(7 + RandomUtil.randomInt(-3, 3));
if (pause == path.indexOf(p)) {
Logger.info("拟人操作,停顿片刻", pause);
delay(200 + RandomUtil.randomInt(-100, 200));
}
Mouse.move(new Location(p[0], p[1]));
}
}
/**
* 鼠标移动到这个区域
* <p>
* 增加拟人操作,随机移动鼠标到区域中心附近(而非直接移动到中心)
*
* @param region 区域
*/
public void mouseMove(Region region) {
mouseMove(randomCenter(region));
}
/**
* 鼠标左键单击
*/
public void mouseLeftClick() {
Mouse.at().click();
}
/**
* 鼠标左键单击
*
* @param region 区域
*/
public void mouseLeftClick(Region region) {
mouseMove(randomCenter(region));
mouseLeftClick();
}
/**
* 鼠标左键单击
*
* @param location 坐标点
*/
public void mouseLeftClick(Location location) {
mouseMove(location);
mouseLeftClick();
}
/**
* 鼠标左键双击
*/
public void mouseLeftDoubleClick() {
Mouse.at().doubleClick();
}
/**
* 鼠标左键双击
*/
public void mouseLeftDoubleClick(Region region) {
mouseMove(randomCenter(region));
mouseLeftDoubleClick();
}
/**
* 鼠标左键双击
*/
public void mouseLeftDoubleClick(Location location) {
mouseMove(location);
mouseLeftDoubleClick();
}
/**
* 鼠标右键单击
*/
public void mouseRightClick() {
mouseRightClick(Mouse.at());
}
/**
* 鼠标右键单击
*
* @param region 区域
*/
public void mouseRightClick(Region region) {
mouseMove(randomCenter(region));
mouseRightClick();
}
/**
* 鼠标右键单击
*
* @param location 坐标点
*/
public void mouseRightClick(Location location) {
mouseMove(location);
mouseRightClick();
}
/**
* 插入剪贴板
*/
public void insertClipboard(String text) {
StringSelection selection = new StringSelection(text);
// 获取系统剪贴板
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
// 将文本设置到剪贴板
clipboard.setContents(selection, null);
}
/**
* 执行复制命令
*/
public void sendCopyCommand() {
screen.type("c", KeyModifier.CTRL);
}
/**
* 执行粘贴命令
*/
public void sendPasteCommand() {
screen.type("v", KeyModifier.CTRL);
}
/**
* 执行回车命令
*/
public void sendEnterCommand() {
screen.type(Key.ENTER);
}
/**
* 捕获指定区域屏幕
*/
public Image capture(Region region) {
return region.getImage();
}
/**
* 获取聚焦区域
*/
public Region getWorkRegion() {
return workRegion;
}
/**
* 获取聚焦区域
*/
public Region getScreenRect() {
if (workRegion != null) {
return workRegion;
}
return screen;
}
/**
* 延迟
*/
public void delay() {
delay(100);
}
/**
* 延迟
*/
public void delay(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
/**
* 抖动延迟
*/
public void delayUnstable() {
delayUnstable(800);
}
/**
* 抖动延迟
*/
public void delayUnstable(long millis) {
if (millis < 200) {
delay(millis);
return;
}
delay(RandomUtil.randomLong(millis - 100, millis + 100));
}
/**
* 查找图片
*
* @param region 区域范围(不设时,取全屏)
* @param minSimilar 相似度
* @return 匹配区域
*/
public Match findPic(Region region, String legend, double minSimilar) {
Pattern pattern = new Pattern(legend).similar(minSimilar);
return region.findBest(pattern);
}
/**
* 查找图片
*
* @param region 区域范围(不设时,取全屏)
* @param minSimilar 相似度
* @return 匹配区域
*/
public boolean clickPic(Region region, String legend, double minSimilar) {
try {
Pattern pattern = new Pattern(legend).similar(minSimilar);
region.click(pattern);
return true;
} catch (FindFailed e) {
return false;
}
}
/**
* 查找图片
*
* @param legend 图例
* @param minSimilar 最低相似度
* @return 匹配区域
*/
public Region findLegend(String legend, double minSimilar) {
Pattern pattern = new Pattern(of(legend).getFile().getAbsolutePath())
.similar(minSimilar);
Match match = workRegion.findBest(pattern);
if (match == null) {
return null;
}
return new Region(match.getRect());
}
/**
* 等待并查找图例
*
* @param legend 图例
* @param minSimilar 最低相似度
* @param timeout 超时时间(秒)
* @return 匹配区域
*/
public Region waitAndFindLegend(String legend, double minSimilar, long timeout) {
Logger.info("等待并查找图例:{}", legend);
return TaskUtil.timeTask(() -> {
while (JMainService.getInstance().run) {
Region result = findLegend(legend, minSimilar);
if (result != null) {
return result;
}
}
return null;
}, timeout, TimeUnit.SECONDS);
}
/**
* 查找并点击图例
*
* @param legend 图例
* @param minSimilar 最低相似度
* @return 匹配区域
*/
public boolean clickLegend(String legend, double minSimilar) {
Pattern pattern = new Pattern(of(legend).getFile().getAbsolutePath())
.similar(minSimilar);
Match match = workRegion.findBest(pattern);
if (match == null) {
return false;
}
mouseLeftClick(match.getCenter());
return true;
}
/**
* 获取图例
*
* @return 图例
*/
public Legend of(String legend) {
return Legend.inflate(legend);
}
/**
* 将相对坐标转为绝对坐标
*
* @return 绝对坐标
*/
public Location of(int x, int y) {
int ox = getWorkRegion().getX();
int oy = getWorkRegion().getY();
return new Location(x + ox, y + oy);
}
/**
* 将相对坐标转为绝对坐标
*
* @param relativePoint 相对坐标
* @return 绝对区域
*/
public Location of(Location relativePoint) {
return of(relativePoint.getX(), relativePoint.getY());
}
/**
* 将相对坐标转为色值坐标
*
* @return 绝对坐标
*/
public ColorLocation of(int x, int y, String hexColor) {
int ox = getWorkRegion().getX();
int oy = getWorkRegion().getY();
return new ColorLocation(x + ox, y + oy, hexColor);
}
/**
* 将相对坐标转为色值坐标
*
* @return 绝对坐标
*/
public String[] of(String... legends) {
return legends;
}
/**
* 将相对坐标转为色值坐标
*
* @param relativePoint 相对坐标
* @return 绝对区域
*/
public ColorLocation of(Location relativePoint, String hexColor) {
return of(relativePoint.getX(), relativePoint.getY(), hexColor);
}
/**
* 获取随机中心坐标
*
* @param region 区域
* @return 随机中心坐标
*/
public Location randomCenter(Region region) {
// 随机移动到区域中心附近(而非直接移动到中心)
Location center = region.getCenter();
// 得到随机移动到区域中心附近的半径
int radius = (int) (Math.min(region.getW(), region.getH()) / 2 * 0.9f);
int x = center.getX() + RandomUtil.randomInt(-radius, radius);
int y = center.getY() + RandomUtil.randomInt(-radius, radius);
return new Location(x, y);
}
/**
* 输入文本
*
* @param text 文本
*/
public void input(String text) {
// 纯英文输入以type输入其他情况使用paste输入
if (text.matches("^[a-zA-Z0-9]+$")) {
for (char c : text.toCharArray()) {
try {
// 每个字符输入间隔随机100-500 毫秒)
long delay = RandomUtil.randomLong(100, 500);
screen.type(String.valueOf(c));
Thread.sleep(delay);
} catch (InterruptedException e) {
throw new IllegalArgumentException(e);
}
}
workRegion.type(text);
} else {
workRegion.paste(text);
}
}
}

Powered by TurnKey Linux.