简介

群脚本引擎(GSTE)是一个为即时通讯群组设计的脚本执行环境。它允许开发者在安全的沙箱中编写自定义逻辑,实现自动回复、消息过滤、群管功能等。

🔒 安全沙箱

完全隔离的执行环境,禁止访问 DOM、网络请求等危险 API,防止恶意代码执行。

⚡ 事件驱动

基于生命周期的编程模型,支持进入群聊、发送消息、接收消息三种事件钩子。

🛠️ 丰富 API

提供消息发送、群信息获取、变量存储、延迟执行等实用功能,满足大部分群管需求。

📏 资源管控

内置代码长度、执行时间、嵌套深度等多重限制,确保系统稳定性。

快速开始

一个最简单的群脚本只需要导出一个事件处理函数。以下是一个自动欢迎新成员的示例:

📄 基础示例:自动欢迎
// 当用户进入群聊时触发
function onEnter(event) {
    // 发送欢迎消息
    sendGroupMessage("欢迎加入本群!请遵守群规~");

    // 显示系统提示(仅自己可见)
    WSystemMsg("新成员已进入");
}

脚本结构

群脚本通过导出特定名称的函数来响应不同事件。引擎会自动识别并调用这些函数:

函数名 触发时机 返回值
onEnter 用户切换到该群聊窗口时
onSend 用户在该群发送消息前 false 阻止发送
onMessage 接收到新消息时

生命周期

onEnter - 进入群聊

当用户打开群聊窗口时触发,适合初始化群相关数据或发送欢迎信息。

function onEnter(event) {
    // event.contact 包含群基本信息
    const group = getCurrentGroup();
    log("进入群聊:", group.groupName);

    // 读取之前保存的变量
    const count = getVariable("visitCount") || 0;
    setVariable("visitCount", count + 1);
}

onSend - 发送消息前拦截

在消息实际发送前触发,可用于内容审核、自动修正或阻止发送。

function onSend(event) {
    // event.content 是消息文本
    const text = event.content;

    // 检查敏感词
    if (text.includes("广告")) {
        infomsg("消息包含敏感词,已阻止发送");
        return false; // 阻止发送
    }

    // 自动追加签名
    if (!text.endsWith("[自动发送]")) {
        event.rawMessage.text += " [自动发送]";
    }
}

onMessage - 接收消息

当群内有新消息时触发(包括自己发送的消息),可用于自动回复或消息记录。

function onMessage(event) {
    // 简单的关键词回复
    if (event.content === "帮助") {
        sendGroupMessage("可用命令:帮助、状态、规则");
    }

    // 管理员命令
    if (event.content.startsWith("/") && event.sender.isAdmin) {
        handleAdminCommand(event.content);
    }
}

上下文对象

每个事件处理函数都会接收一个 event 对象,包含当前事件的详细信息:

事件对象结构

属性 类型 说明
$event Object 原始事件数据(各事件不同)
$utils Object 工具函数集合
$contact Object 当前联系人/群信息

onSend 事件对象

{
    content: "消息文本内容",           // 消息文本
    messageType: "text",              // 消息类型
    timestamp: 1711882800000,         // 时间戳
    sender: {
        userId: "user_123",
        name: "用户名"
    },
    rawMessage: { ... }               // 原始消息对象,可修改
}

API 参考:消息相关

sendGroupMessage(content)

向当前群聊发送文本消息。

参数:
  • content string - 消息内容,最大 500 字符

返回:无

⚠️ 注意
消息内容会自动过滤敏感信息(URL、电话号码、危险代码等)。
WSystemMsg(msg)

发送系统消息到当前会话(仅自己可见)。

参数:
  • msg string - 系统消息内容,最大 500 字符

返回:Promise<boolean> 是否发送成功

infomsg(msg)

显示提示信息(弹窗形式)。

参数:
  • msg string - 提示内容,最大 200 字符
psmute(time,reason)

禁言群用户

参数:
  • time number - 禁言时长(单位秒)最长禁言30天
  • reason string - 禁言理由,最大50字符
log(...args)
warn(...args)
error(...args)

控制台输出方法,自动添加 [群脚本] 前缀。

log("调试信息", someVariable);
warn("警告信息");
error("错误信息");

API 参考:群组信息

getCurrentGroup()

获取当前群聊的详细信息。

返回:Object | null

{
    groupId: "12345",           // 群ID
    groupName: "技术交流群",    // 群名称
    groupDesc: "群描述...",   // 群描述
    memberCount: 128,          // 成员数量
    isAdmin: true,              // 当前用户是否为管理员
    isOwner: false             // 当前用户是否为群主
}
getCurrentUser()

获取当前登录用户的信息。

返回:Object

{
    userId: "user_123",
    name: "我的昵称",
    isAdmin: true,
    isOwner: false
}

API 参考:变量存储

脚本引擎提供键值对存储功能,数据按群隔离,重启后清空(内存存储)。

setVariable(key, value)

存储变量(仅当前群有效)。

参数:
  • key string - 变量名,最大 100 字符,仅允许字母数字下划线
  • value any - 变量值,序列化后最大 10KB
🚫 安全限制
变量值会被扫描危险内容,包含危险代码的变量将被拒绝存储。
getVariable(key)

读取变量(返回深拷贝)。

参数:
  • key string - 变量名

返回:any | undefined

💡 使用示例:计数器
function onMessage(event) {
    // 获取当前计数
    let count = getVariable("msgCount") || 0;
    count++;

    // 每10条消息提醒一次
    if (count % 10 === 0) {
        sendGroupMessage(`已接收 ${count} 条消息!`);
    }

    // 保存计数
    setVariable("msgCount", count);
}

API 参考:工具函数

delay(ms) async

延迟执行,返回 Promise。

参数:
  • ms number - 延迟毫秒数,最大 30000ms(30秒)
async function onEnter(event) {
    await delay(1000);  // 等待1秒
    sendGroupMessage("延迟发送的消息");
}
timeNow()

获取当前时间戳(毫秒)。

返回:number

randomGet(min, max)

生成随机数(包含小数)。

参数:
  • min number - 最小值,默认 0,最大 1000000
  • max number - 最大值,默认 1,最大 1000000

返回:number min 到 max 之间的随机数

SubString(str, start, end)

安全截取字符串(自动处理边界)。

参数:
  • str string - 原字符串
  • start number - 开始索引
  • end number - 结束索引(可选,默认字符串长度,最大 500)
Slicetion(arr, start, end)

安全截取数组。

参数:
  • arr Array - 原数组
  • start number - 开始索引
  • end number - 结束索引(可选,默认数组长度,最大 100)

API 参考:字符串扩展

引擎为 String 原型添加了安全检查的方法:

String.prototype.checkIfrom(keywordList)

检查字符串是否包含指定关键词(安全版本)。

参数:
  • keywordList string | Array - 关键词或关键词数组

返回:boolean 是否包含任一关键词

const text = "这是一段测试文本";

// 检查单个关键词
if (text.checkIfrom("测试")) {
    log("包含关键词");
}

// 检查多个关键词
const keywords = ["广告", "推广", "spam"];
if (text.checkIfrom(keywords)) {
    infomsg("检测到敏感内容");
}

API 参考:正则匹配

RegexUxs.run(pattern, text, flags)

安全执行正则表达式测试。

参数:
  • pattern string - 正则表达式模式,最大 100 字符
  • text string - 待测试文本,最大 500 字符
  • flags string - 标志位,默认 "i"(不区分大小写)

返回:boolean 是否匹配成功

if (RegexUxs.run("^帮助$", event.content)) {
    sendGroupMessage("可用命令:帮助、状态");
}
RegexUxs.check(pattern, text)

安全执行正则表达式匹配,返回匹配结果数组。

参数:
  • pattern string - 正则表达式模式,最大 100 字符
  • text string - 待匹配文本,最大 500 字符

返回:Array | null 匹配结果数组,最多返回 10 项

const matches = RegexUxs.check("\\d+", event.content);
if (matches) {
    sendGroupMessage(`提取到数字: ${matches[0]}`);
}

API 参考:HTTP 网络请求

⚠️ 网络请求限制
仅支持同源请求或已配置 CORS 的接口,所有请求会自动添加安全头验证。超时默认 10 秒,URL 仅允许 https 协议。
HttpNetServer.create()

创建 XMLHttpRequest 实例。

返回:XMLHttpRequest

const xhr = HttpNetServer.create();
HttpNetServer.open(xhr, method, url)

初始化请求。

参数:
  • xhr XMLHttpRequest - 请求实例
  • method string - 请求方法 (GET/POST)
  • url string - 请求地址,仅允许 https 协议,最大 200 字符
HttpNetServer.setHeader(xhr, key, value)

设置请求头。

参数:
  • xhr XMLHttpRequest - 请求实例
  • key string - 请求头名称
  • value string - 请求头值
HttpNetServer.send(xhr, body)

发送请求。

参数:
  • xhr XMLHttpRequest - 请求实例
  • body string | null - 请求体(可选)
HttpNetServer.abort(xhr)

中止请求。

参数:
  • xhr XMLHttpRequest - 请求实例
HttpNetServer.onState(xhr, callback)

设置状态变化回调函数。

参数:
  • xhr XMLHttpRequest - 请求实例
  • callback function - 回调函数,当 readyState 变化时触发
HttpNetServer.getReadyState(xhr)

获取当前请求状态。

参数:
  • xhr XMLHttpRequest - 请求实例

返回:number readyState 值 (0-4)

HttpNetServer.getStatus(xhr)

获取 HTTP 状态码。

参数:
  • xhr XMLHttpRequest - 请求实例

返回:number HTTP 状态码 (如 200, 404)

HttpNetServer.getResponse(xhr)

获取响应文本。

参数:
  • xhr XMLHttpRequest - 请求实例

返回:string 响应内容,最大 1MB

HttpNetServer.setTimeout(xhr, ms)

设置请求超时时间。

参数:
  • xhr XMLHttpRequest - 请求实例
  • ms number - 超时毫秒数,最大 10000ms (10秒)
🌐 HTTP 请求完整示例:获取天气
function onMessage(event) {
    if (event.content.startsWith("天气")) {
        const city = event.content.slice(2).trim();
        if (!city) {
            sendGroupMessage("请指定城市,如:天气 北京");
            return;
        }

        const xhr = HttpNetServer.create();
        HttpNetServer.setTimeout(xhr, 5000);
        HttpNetServer.open(xhr, "GET", `https://api.example.com/weather?city=${encodeURIComponent(city)}`);
        HttpNetServer.setHeader(xhr, "Content-Type", "application/json");
        HttpNetServer.onState(xhr, () => {
            if (HttpNetServer.getReadyState(xhr) === 4) {
                const status = HttpNetServer.getStatus(xhr);
                if (status === 200) {
                    const response = HttpNetServer.getResponse(xhr);
                    try {
                        const data = JSON.parse(response);
                        sendGroupMessage(`${city}天气:${data.weather},温度 ${data.temp}℃`);
                    } catch (e) {
                        sendGroupMessage("解析天气数据失败");
                    }
                } else {
                    sendGroupMessage(`请求失败,状态码:${status}`);
                }
            }
        });
        
        HttpNetServer.send(xhr);
        WSystemMsg(`正在查询 ${city} 天气...`);
    }
}
💡 提示

所有 HttpNetServer 方法都是同步调用,回调函数中可使用 getReadyStategetStatusgetResponse 获取请求状态和结果。建议始终设置超时时间避免请求卡死。

安全限制

为了保障系统安全,脚本引擎实施了严格的安全策略:

禁止访问的 API

DOM 操作

document, window, location, history, navigator 等所有浏览器 BOM/DOM 对象

网络请求

fetch, XMLHttpRequest, WebSocket, Worker 等所有网络相关 API

存储访问

localStorage, sessionStorage, indexedDB, cookies

代码执行

eval, Function, setTimeout/setInterval 传入字符串

系统交互

alert, confirm, prompt, print, open, close

危险语法

with 语句, debugger, import/export, __proto__ 访问

代码扫描规则

  • 检测 while/for 循环(防止死循环)
  • 检测 __proto__, constructor, prototype 访问
  • 检测 import/export 语句
  • 检测 with 语句
  • 检测 debugger 语句
  • 检测 new Functioneval 调用
  • 检测危险全局变量访问字符串

字符串内容检查

所有传入 WSystemMsg, infomsg, setVariable 的字符串都会经过危险内容扫描,如果包含以下模式将被拒绝:

  • window, document, location 等危险全局变量名
  • eval, Function, setTimeout 等可执行方法
  • DOM 操作相关字符串

资源限制

限制项 上限值 说明
代码长度 10,000 字符 超过此长度拒绝加载
脚本初始化超时 5 秒 获取导出函数的超时时间
事件处理超时 3 秒 单个事件处理函数超时
嵌套深度 20 层 代码块 {} 的最大嵌套深度
变量存储大小 10 KB 单个变量序列化后大小
消息长度 500 字符 sendGroupMessage 内容限制
延迟时间 30 秒 delay 函数最大延迟
数组切片 100 项 Slicetion 返回数组最大长度
深拷贝深度 5 层 getVariable 递归深度

完整示例

示例 1:消息审核系统

🛡️ 敏感词过滤 + 自动警告
// 敏感词列表
const sensitiveWords = ["广告", "推广", "加微信", "qq群"];
// 警告阈值
const WARN_LIMIT = 3;

function onSend(event) {
    const text = event.content;

    // 检查敏感词
    if (text.checkIfrom(sensitiveWords)) {
        infomsg("⚠️ 消息包含敏感词,请修改后发送");
        return false; // 阻止发送
    }

    // 检查发送频率
    const user = getCurrentUser();
    const key = `msg_count_${user.userId}`;
    let count = getVariable(key) || 0;
    count++;

    if (count > WARN_LIMIT) {
        infomsg("⚠️ 发送过于频繁,请稍后再试");
        return false;
    }

    setVariable(key, count);

    // 5秒后重置计数
    delay(5000).then(() => {
        const current = getVariable(key) || 0;
        if (current > 0) {
            setVariable(key, current - 1);
        }
    });
}

最佳实践

1. 错误处理

虽然引擎有 try-catch 包装,但建议关键逻辑自行处理异常:

function safeOperation() {
    try {
        // 可能出错的操作
        const data = JSON.parse(getVariable("config") || "{}");
        return data;
    } catch (e) {
        error("解析配置失败:", e.message);
        return {};
    }
}

2. 变量命名

  • 使用描述性名称:userWarningCount 优于 cnt
  • 添加前缀避免冲突:bot_config, bot_state
  • 常量使用大写:MAX_RETRY = 3

3. 性能优化

  • 避免在 onMessage 中频繁读写变量,可缓存到局部变量
  • 大数据量使用 Slicetion 分批处理
  • 复杂计算考虑使用 delay(0) 让出执行权