Skip to content

开发SWHelper插件

SWHelper 允许编写 JavaScript 插件并在Render Thread 中运行。通过 IPC 通信,插件还可以请求主进程执行更高权限的操作。

核心概念

  • 运行环境: 插件代码默认在管家的渲染进程中执行
  • 通信机制: 使用 ipcRenderer 与主进程通讯
  • 鉴权密钥: 必须使用固定的 apiKeychannel 才能通过主进程的校验(防止被屏蔽)

插件开发模板

点击展开示例插件模板
javascript
// 引入依赖 (利用 remote 模块调用系统 API)
const fs = require('electron').remote.require('fs');
const path = require('electron').remote.require('path');
const os = require('electron').remote.require('os');
const { ipcRenderer } = require('electron');

// 必须保留的固定常量,用于通过主进程校验
const apiKey = '7c5a1bae0eae82d8246c6cb70be0beb5';
const channel = 'channel-0a4efbc3';

// === 配置文件路径 ===
const configFilePath = path.join(os.homedir(), '.swhelper.config');

// === 核心功能封装 ===

/**
 * 请求主进程执行 JS 代码 (Action: exec)
 * @param {string} script 要在主进程执行的 JS 代码字符串
 */
function doEval(script) {
    ipcRenderer.send(channel, {
        apiKey: apiKey,
        action: 'exec',
        data: script
    });
}

/**
 * 请求注入代码 - 仅一次 (Action: i)
 * @param {string} script 要注入的代码
 */
function doInjectOnce(script) {
    ipcRenderer.send(channel, {
        apiKey: apiKey,
        action: 'i',
        data: script
    });
}

/**
 * 请求注入代码 - 循环注入 (Action: j)
 * 适用于页面刷新后需要重新生效的场景
 * @param {string} script 要注入的代码
 */
function doInjectLoop(script) {
    ipcRenderer.send(channel, {
        apiKey: apiKey,
        action: 'j',
        data: script
    });
}

// === 接收主进程的反馈 ===
ipcRenderer.on(channel + '-reply', (event, data) => {
    const { ok, act, err, ret } = data;
    if (!ok) {
        console.error(`[Plugin] Action ${act} failed:`, err);
    } else {
        console.log(`[Plugin] Action ${act} success. Return:`, ret);
    }
});

// === 配置读写辅助函数 ===
function loadConfig() {
    try {
        return JSON.parse(fs.readFileSync(configFilePath, 'utf-8'));
    } catch (e) {
        return {}; // 读取失败返回空对象
    }
}

function saveConfig(newConfig) {
    fs.writeFileSync(configFilePath, JSON.stringify(newConfig));
}

// ============================================
// 🚀 在此处编写你的插件逻辑
// ============================================

// 示例 1: 简单的弹窗 (在渲染进程执行)
alert("插件加载成功!Hello from SWHelper Plugin");

// 示例 2: 读取当前配置并打印
let currentConfig = loadConfig();
console.log("当前配置:", currentConfig);

// 示例 3: 让主进程弹出一个消息框 (跨进程执行)
// doEval("const { dialog } = require('electron'); dialog.showMessageBox({ message: '这是来自主进程的消息' });");

API 详解

插件通过 ipcRenderer.send 发送指令对象,结构如下:

javascript
{
  apiKey: '...', // 固定密钥
  action: '...', // 指令动作
  data: '...'    // 具体的脚本内容
}

可用 action

  • exec: 直接在Main Thread 上下文中执行 data 中的 JS 代码,可调用大部分主进程 API
  • i: 将代码注入到所有窗口中执行一次,需插件自行判断是否为目标窗口
  • j: 将代码加入循环队列,定期注入,这对于需要对抗页面刷新或重置逻辑的功能非常有用

安装插件

编写好 .js 文件后(例如 C:\Users\seewo\my-plugin.js),需要在配置文件的 plugins 字段中注册,参见:配置 (plugins)

Main Thread 插件注意事项

当你在配置文件中将插件的 loadType 设置为 mainThread 时,代码将直接在主进程中运行:

WARNING

在主进程注入的上下文中,默认无法直接使用 require 函数

你需要使用加载器特供的全局函数 global.___r_ 来加载模块,并使用特定的全局函数来向渲染进程注入代码

因此,如果你编写的是 mainThread 插件,请务必把所有的 require('xx') 替换为 global.___r_('xx')

主进程专用 API 示例:

javascript
// === 获取模块 (替代 require) ===
// 在 mainThread 中必须这样引入模块
const electron = global.___r_('electron'); 
const fs = global.___r_('fs');
const path = global.___r_('path');

// === 向渲染进程注入代码 ===

// 1. 只注入一次 (相当于 Renderer 的 doInjectOnce)
// content: 要注入的 JS 字符串
global.___i_("console.log('Main process says hello once');");

// 2. 循环注入 (相当于 Renderer 的 doInjectLoop)
// content: 要注入的 JS 字符串 (每 5ms 尝试注入一次)
global.___j_("console.log('Main process is keeping this alive');");