用nodejs写一个yys挂机脚本

用nodejs写一个yys外挂

为什么要用node来写

曾经用python写过一个自用御魂脚本,作为一个前端码农,就考虑能不能用js实现,js如何来使用天使插件(TSPlug.dll)实现后台操作呢?经google,github发现,已经有大佬实现了轮子winax,能够调用通用COM组件。之后用js写脚本的想法开始付诸行动。

封装TSPlug

考虑为了更方便的调用TSPlug的一些方法,自己动手用ts封装了一个天使插件的库

export default class TSPlug {
   
  private ts: TSInstance;

  constructor() {
   
    this.ts = TSPlug.init('ts.tssoft');
  }

  private static init(COM: string): TSInstance {
   
    try {
   
      return new winax.Object(COM);
    } catch {
   
      execSync(`regsvr32 ${
     resolve(__dirname, '../lib/TSPlug.dll')}`);  // 注册插件
      return new winax.Object(COM);
    }
  }
  ...
}

封装一个TSPlug的类,包含天使插件的所有方法,详细代码ts.dll,有了通用插件之后开始正式写脚本

开始

我们主要实现的是单人多开刷业原火,御灵,组队双开输御魂

项目的目录结构

yys-robot
├── src
│   └── app
│       ├── actions 
│       │   ├── bind-window.ts
│       │   ├── find-window.ts
│       │   └── get-screen-config.ts
│       ├── config
│       │   ├── config.ts
│       │   └── shared.ts
│       ├── interfaces
│       │   └── interfaces.ts
│       ├── mode
│       │   ├── single.mode.ts
│       │   └── team.mode.ts
│       ├── workers // 工作进程
│       │   ├── single.worker.js
│       │   ├── single.worker.ts
│       │   ├── team-driver.worker.js
│       │   ├── team-driver.worker.ts
│       │   ├── team-fighter.worker.js
│       │   └── team-fighter.worker.ts
│       ├── app.single.ts  // 单人模式入口
│       ├── app.team.ts  // 组队模式入口
│       └── app.ts  // 程序入口
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── single.bat  // 批处理命令快速启动单人模式
├── team.bat  // .....快速启动组队模式
├── tsconfig.json
└── tslint.json

config基本配置文件

//config.ts

export const screenConfig = {
   
  color: {
   
    yellow: 'f3b25e', // 黄色的挑战按钮
    auto: 'f8f3e0', // 自动战斗
    fighterAutoAccept: 'edc791', // 组队模式打手自动接受邀请
    normalAccept: '54b05f', // 组队模式常规邀请
    blankBattle: '2b2b2b', // 空白的挑战按钮
    reward: 'dd7260', // 悬赏关闭按钮
    team: 'e9cd73', // 组队挑战按钮
    defaultInvitation: '725f4d', // 默认邀请按钮,
    fontCooperative: 'f8f3e0', // 协战队伍文字颜色,
    fighterReady: '221611'
  },
  position: {
    // range 为随机点击区域的范围
    singleBattle: [807, 422],
    singleBattleRange: [807, 807 + 74, 422, 422 + 17],
    teamBattle: [1091, 576],
    teamBattleRange: [1070, 1105, 572, 630],
    auto: [71, 577],
    settlement: [980, 1030, 225, 275],
    teamSettlement: [189, 333, 23, 76],
    continueInviteButton: [724, 396],
    continueInviteButtonRange: [724 - 5, 724 + 5, 396 - 5, 396 + 5],
    rejectRewardButton: [750, 458],
    rejectRewardButtonRange: [745, 755, 453, 463],
    defaultInvitationButton: [499, 321],
    defaultInvitationButtonRange: [489, 509, 311, 331],
    blankBattleButton: [1067, 585],
    autoAcceptButtonRange: [16, 366, 122, 465], // 自动接受按钮出现的区域,方便findColor找色
    fontCooperativeRange: [68, 204, 14, 60], // 协战队伍文字出现的区域,
    fighterReadyPosition: [985, 587] // 辅助位置,组队打手进入了备战界面
  }
};

// shared.ts 全局共享对象

import TSPlug from 'ts.dll';
import {
   Shared} from '../interfaces/interfaces';

export const shared: Shared = {
   
  original: new TSPlug(),
  handles: []  // all window handle
};

actions存放基本通用方法

//find-window.ts 查找窗口句柄
import TSPlug from 'ts.dll';
import {
   shared} from '../config/shared';

export function findWindow(ts: TSPlug) {
   
//通过进程id获取窗口句柄,返回逗号分隔的字符串
  const windowHandelString = ts.enumWindowByProcess('client.exe', '', '', 16);  // onmyoji.exe  进程id可能会不一样
  if (windowHandelString) {
   
    const windowHandelRaw = windowHandelString.split(',');
    // 存储全局共享的handle
    const windowHandel = shared.handles = windowHandelRaw.map(v => Number(v));
    return [...windowHandel];
  } else {
   
    console.log('not found window handle');
    return [];
  }
}

// bind-window.ts 通过窗口句柄绑定窗口实现后台运行
import TSPlug from 'ts.dll';

export function bindWindow(handle: number) {
   
  const ts = new TSPlug();
  const ret = ts.bindWindow(handle, 'dx2', 'windows', 'windows', 0);
  if (ret === 1) {
   
    console.log('bind window success');
    return ts;
  }
  throw {
   message: 'bind window failed'};
}

// get-screen-config.ts 根据传入的不同分辨率的比值返回不同的坐标配置(暂时只测试了480*852分辨率,默认分辨率自行修改代码测试)

import {
   screenConfig} from '../config/config';
export function getScreenConfig(resolution: 1 | 1.333333333 = 1.333333333) {
   
  if (resolution === 1) {
   
    return screenConfig;
  }
  return transConfig(resolution);
}

function transConfig(resolution: 1 | 1.333333333) {
   
  let i: keyof typeof screenConfig.position;
  for (i in screenConfig.position) {
   
    if (screenConfig.position.hasOwnProperty(i)) {
   
      const item = screenConfig.position[i];
      screenConfig.position[i] = item.map(v => v / resolution);
    }
  }
  screenConfig.color.auto = 'f4efdc';
  return screenConfig;
}

mode单人或双人模式

// single.mode.ts

import {
   fork} from 'child_process';
import {
   shared} from '../config/shared';
import {
   resolve} from 'path';

// 单人模式,每一个窗口应该独立运行,通过child_process 创建子进程
// node子进程不能共享主进程的object,但是可以共享数字,向子进程传输全局共享的handle
export function single() {
   
  for (const handle of shared.handles) {
   
    const worker = fork(resolve(__dirname, '../workers/single.worker.js'));
    worker.send(handle);
  }
}

// team.mode.ts 组队模式,主逻辑不需要子进程,只需要司机执行一些操作,结算的时候需要分辨创建司机和打手两个子进程来执行不同的操作

import TSPlug from 'ts.dll';
import {
   shared} from '../config/shared';
import {
   bindWindow} from '../actions/bind-window';
import {
   getScreenConfig} from '../actions/get-screen-config';
import {
   Area} from 'ts.dll/@types/modules/interface';
import {
   fork} from 'child_process';
import {
   resolve as pathResolve} from 'path';

export async function team() {
   
  if (shared.handles.length < 2) throw {
   message: 'window handles less than 2'};
  // ======================各种配置=========================
  const screenConfig = getScreenConfig();
  const {
   color, position} = screenConfig;
  const {
   team, auto, reward} = color;
  const {
   teamBattleRange, teamBattle, auto: autoButton, settlement, rejectRewardButton, rejectRewardButtonRange} = position;
  const teamBattleArea = {
   
    x1: teamBattleRange[0],
    x2: teamBattleRange[1],
    y1: teamBattleRange[2],
    y2: teamBattleRange[3]
  };
  const rejectRewardArea = {
   
    x1: rejectRewardButtonRange[0],
    x2: rejectRewardButtonRange[1],
    y1: rejectRewardButtonRange[2],
    y2: rejectRewardButtonRange[3]
  };
// ===========================================
  let driver = bindWindow(shared.handles
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值