跳到主要内容

Threading - 线程和定时器

EasyBot 提供了完整的定时器功能,支持延时执行和间隔执行,兼容标准的 JavaScript 定时器 API。

threading 命名空间

setTimeout()

创建一个定时器,在指定的延迟后执行回调函数。

语法: threading.setTimeout(callback: () => void, delay: number): number

参数:

  • callback - 要执行的回调函数
  • delay - 延迟时间(毫秒)

返回值: 定时器ID

示例:

const timerId = threading.setTimeout(() => {
logger.info("3秒后执行");
}, 3000);

setInterval()

创建一个间隔定时器,每隔指定的时间执行回调函数。

语法: threading.setInterval(callback: () => void, interval: number): number

参数:

  • callback - 要执行的回调函数
  • interval - 间隔时间(毫秒)

返回值: 定时器ID

示例:

const intervalId = threading.setInterval(() => {
logger.info("每5秒执行一次");
}, 5000);

clearTimeout()

清除定时器。

语法: threading.clearTimeout(timerId: number): boolean

参数:

  • timerId - 定时器ID

返回值: 是否成功清除

示例:

const timerId = threading.setTimeout(() => {
logger.info("这不会执行");
}, 1000);

const success = threading.clearTimeout(timerId);
logger.info(`清除成功: ${success}`);

clearInterval()

清除间隔定时器。

语法: threading.clearInterval(timerId: number): boolean

参数:

  • timerId - 定时器ID

返回值: 是否成功清除

示例:

const intervalId = threading.setInterval(() => {
logger.info("定期执行");
}, 1000);

// 10秒后停止
threading.setTimeout(() => {
const success = threading.clearInterval(intervalId);
logger.info(`停止间隔定时器: ${success}`);
}, 10000);

全局定时器函数

EasyBot 还提供了兼容标准 JavaScript 的全局定时器函数。

setTimeout()

全局版本的 setTimeout 函数,支持额外参数传递。

语法: setTimeout(callback: Function, delay?: number, ...args: any[]): number

参数:

  • callback - 要执行的回调函数
  • delay - 延迟时间(毫秒),默认为0
  • args - 传递给回调函数的参数

返回值: 定时器ID

示例:

// 基础用法
const timerId = setTimeout(() => {
logger.info("延时执行");
}, 2000);

// 传递参数
setTimeout((name, age) => {
logger.info(`Hello ${name}, you are ${age} years old`);
}, 1000, "张三", 25);

setInterval()

全局版本的 setInterval 函数,支持额外参数传递。

语法: setInterval(callback: Function, interval?: number, ...args: any[]): number

参数:

  • callback - 要执行的回调函数
  • interval - 间隔时间(毫秒),默认为0
  • args - 传递给回调函数的参数

返回值: 定时器ID

示例:

// 基础用法
const intervalId = setInterval(() => {
logger.info("定期执行");
}, 3000);

// 传递参数
setInterval((message) => {
logger.info(message);
}, 2000, "定时消息");

clearTimeout()

全局版本的 clearTimeout 函数。

语法: clearTimeout(timerId: number): void

参数:

  • timerId - 定时器ID

示例:

const timerId = setTimeout(() => {
logger.info("这不会执行");
}, 5000);

clearTimeout(timerId);

clearInterval()

全局版本的 clearInterval 函数。

语法: clearInterval(timerId: number): void

参数:

  • timerId - 定时器ID

示例:

const intervalId = setInterval(() => {
logger.info("定期执行");
}, 1000);

// 5秒后停止
setTimeout(() => {
clearInterval(intervalId);
}, 5000);

实用示例

延时发送消息

bus.on("group_message_event", (event) => {
if (event.RawMessage === "/remind") {
event.Context.Reply(new MessageChain().Text("我会在30秒后提醒你"));

setTimeout(() => {
event.Context.Reply(new MessageChain().Text("⏰ 时间到了!"));
}, 30000);
}
});

定期任务

// 每小时发送一次群公告
const hourlyAnnouncement = setInterval(() => {
const message = new MessageChain()
.Text("📢 每小时公告:请遵守群规,文明聊天!");

// 向所有群发送消息(需要根据实际情况获取群列表)
// context.SendGroupMessageAsync(selfId, groupId, message);
}, 3600000); // 1小时 = 3600000毫秒

倒计时功能

function startCountdown(context, selfId, peerId, seconds) {
let remaining = seconds;

const countdownInterval = setInterval(() => {
if (remaining <= 0) {
clearInterval(countdownInterval);
context.SendGroupMessageAsync(
selfId,
peerId,
new MessageChain().Text("⏰ 倒计时结束!")
);
return;
}

if (remaining <= 10 || remaining % 10 === 0) {
context.SendGroupMessageAsync(
selfId,
peerId,
new MessageChain().Text(`倒计时: ${remaining}`)
);
}

remaining--;
}, 1000);

return countdownInterval;
}

// 使用示例
bus.on("group_message_event", (event) => {
if (event.RawMessage.startsWith("/countdown ")) {
const seconds = parseInt(event.RawMessage.split(" ")[1]);
if (seconds > 0 && seconds <= 300) { // 最多5分钟
startCountdown(event.AdapterContext, event.SelfId, event.PeerId, seconds);
}
}
});

定时清理任务

const activeUsers = new Map();

// 记录用户活动
bus.on("group_message_event", (event) => {
activeUsers.set(event.SenderId, Date.now());
});

// 每10分钟清理不活跃用户记录
setInterval(() => {
const now = Date.now();
const tenMinutes = 10 * 60 * 1000;

for (const [userId, lastActive] of activeUsers.entries()) {
if (now - lastActive > tenMinutes) {
activeUsers.delete(userId);
}
}

logger.info(`活跃用户数量: ${activeUsers.size}`);
}, 600000); // 10分钟

限流控制

const userLastMessage = new Map();
const MESSAGE_COOLDOWN = 5000; // 5秒冷却

bus.on("group_message_event", (event) => {
const userId = event.SenderId;
const now = Date.now();
const lastTime = userLastMessage.get(userId) || 0;

if (now - lastTime < MESSAGE_COOLDOWN) {
const remaining = Math.ceil((MESSAGE_COOLDOWN - (now - lastTime)) / 1000);
event.Context.Reply(
new MessageChain().Text(`请等待 ${remaining} 秒后再发送命令`)
);
return;
}

userLastMessage.set(userId, now);

// 处理用户命令
if (event.RawMessage === "/info") {
event.Context.Reply(new MessageChain().Text("这是信息"));
}
});

// 定期清理过期记录
setInterval(() => {
const now = Date.now();
for (const [userId, lastTime] of userLastMessage.entries()) {
if (now - lastTime > MESSAGE_COOLDOWN * 2) {
userLastMessage.delete(userId);
}
}
}, 60000); // 每分钟清理一次

注意事项

  1. 内存管理: 记得清理不再需要的定时器,避免内存泄漏
  2. 错误处理: 在定时器回调中添加错误处理
  3. 性能考虑: 避免创建过多的定时器
  4. 时间精度: 定时器的精度可能受到系统负载影响
  5. 函数差异: threading 命名空间的函数返回 boolean,全局函数返回 void

最佳实践

  1. 使用 threading 命名空间: 推荐使用 threading.setTimeout 等函数,可以获得清除操作的返回值
  2. 保存定时器ID: 将定时器ID保存到变量中,以便后续清理
  3. 异常处理: 在定时器回调中使用 try-catch
  4. 资源清理: 在插件卸载时清理所有定时器
  5. 合理间隔: 选择合适的时间间隔,避免过于频繁的执行