简介
群脚本引擎(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 参考:消息相关
向当前群聊发送文本消息。
contentstring - 消息内容,最大 500 字符
返回:无
发送系统消息到当前会话(仅自己可见)。
msgstring - 系统消息内容,最大 500 字符
返回:Promise<boolean> 是否发送成功
显示提示信息(弹窗形式)。
msgstring - 提示内容,最大 200 字符
禁言群用户
timenumber - 禁言时长(单位秒)最长禁言30天reasonstring - 禁言理由,最大50字符
warn(...args)
error(...args)
控制台输出方法,自动添加 [群脚本] 前缀。
log("调试信息", someVariable);
warn("警告信息");
error("错误信息");
API 参考:群组信息
获取当前群聊的详细信息。
返回:Object | null
{
groupId: "12345", // 群ID
groupName: "技术交流群", // 群名称
groupDesc: "群描述...", // 群描述
memberCount: 128, // 成员数量
isAdmin: true, // 当前用户是否为管理员
isOwner: false // 当前用户是否为群主
}
获取当前登录用户的信息。
返回:Object
{
userId: "user_123",
name: "我的昵称",
isAdmin: true,
isOwner: false
}
API 参考:变量存储
脚本引擎提供键值对存储功能,数据按群隔离,重启后清空(内存存储)。
存储变量(仅当前群有效)。
keystring - 变量名,最大 100 字符,仅允许字母数字下划线valueany - 变量值,序列化后最大 10KB
读取变量(返回深拷贝)。
keystring - 变量名
返回:any | undefined
function onMessage(event) {
// 获取当前计数
let count = getVariable("msgCount") || 0;
count++;
// 每10条消息提醒一次
if (count % 10 === 0) {
sendGroupMessage(`已接收 ${count} 条消息!`);
}
// 保存计数
setVariable("msgCount", count);
}
API 参考:工具函数
延迟执行,返回 Promise。
msnumber - 延迟毫秒数,最大 30000ms(30秒)
async function onEnter(event) {
await delay(1000); // 等待1秒
sendGroupMessage("延迟发送的消息");
}
获取当前时间戳(毫秒)。
返回:number
生成随机数(包含小数)。
minnumber - 最小值,默认 0,最大 1000000maxnumber - 最大值,默认 1,最大 1000000
返回:number min 到 max 之间的随机数
安全截取字符串(自动处理边界)。
strstring - 原字符串startnumber - 开始索引endnumber - 结束索引(可选,默认字符串长度,最大 500)
安全截取数组。
arrArray - 原数组startnumber - 开始索引endnumber - 结束索引(可选,默认数组长度,最大 100)
API 参考:字符串扩展
引擎为 String 原型添加了安全检查的方法:
检查字符串是否包含指定关键词(安全版本)。
keywordListstring | Array - 关键词或关键词数组
返回:boolean 是否包含任一关键词
const text = "这是一段测试文本";
// 检查单个关键词
if (text.checkIfrom("测试")) {
log("包含关键词");
}
// 检查多个关键词
const keywords = ["广告", "推广", "spam"];
if (text.checkIfrom(keywords)) {
infomsg("检测到敏感内容");
}
API 参考:正则匹配
安全执行正则表达式测试。
patternstring - 正则表达式模式,最大 100 字符textstring - 待测试文本,最大 500 字符flagsstring - 标志位,默认 "i"(不区分大小写)
返回:boolean 是否匹配成功
if (RegexUxs.run("^帮助$", event.content)) {
sendGroupMessage("可用命令:帮助、状态");
}
安全执行正则表达式匹配,返回匹配结果数组。
patternstring - 正则表达式模式,最大 100 字符textstring - 待匹配文本,最大 500 字符
返回:Array | null 匹配结果数组,最多返回 10 项
const matches = RegexUxs.check("\\d+", event.content);
if (matches) {
sendGroupMessage(`提取到数字: ${matches[0]}`);
}
API 参考:HTTP 网络请求
创建 XMLHttpRequest 实例。
返回:XMLHttpRequest
const xhr = HttpNetServer.create();
初始化请求。
xhrXMLHttpRequest - 请求实例methodstring - 请求方法 (GET/POST)urlstring - 请求地址,仅允许 https 协议,最大 200 字符
设置请求头。
xhrXMLHttpRequest - 请求实例keystring - 请求头名称valuestring - 请求头值
发送请求。
xhrXMLHttpRequest - 请求实例bodystring | null - 请求体(可选)
中止请求。
xhrXMLHttpRequest - 请求实例
设置状态变化回调函数。
xhrXMLHttpRequest - 请求实例callbackfunction - 回调函数,当 readyState 变化时触发
获取当前请求状态。
xhrXMLHttpRequest - 请求实例
返回:number readyState 值 (0-4)
获取 HTTP 状态码。
xhrXMLHttpRequest - 请求实例
返回:number HTTP 状态码 (如 200, 404)
获取响应文本。
xhrXMLHttpRequest - 请求实例
返回:string 响应内容,最大 1MB
设置请求超时时间。
xhrXMLHttpRequest - 请求实例msnumber - 超时毫秒数,最大 10000ms (10秒)
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 方法都是同步调用,回调函数中可使用
getReadyState、getStatus、getResponse
获取请求状态和结果。建议始终设置超时时间避免请求卡死。
安全限制
为了保障系统安全,脚本引擎实施了严格的安全策略:
禁止访问的 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 Function和eval调用 - 检测危险全局变量访问字符串
字符串内容检查
所有传入 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)让出执行权