| 383 | } |
| 384 | |
| 385 | class CyPlugin extends Plugin { |
| 386 | description = `词云 |
| 387 | <code>${mainPrefix}cy [消息数]</code> - 当前群立即生成 |
| 388 | <code>${mainPrefix}cy help</code> - 查看词云帮助 |
| 389 | <code>${mainPrefix}cy target here</code> - 设置当前群为定时目标 |
| 390 | <code>${mainPrefix}cy time 09:00 500</code> - 设置定时发送 |
| 391 | <code>${mainPrefix}cy on|off|status</code> - 开关/查看定时`; |
| 392 | private timer?: NodeJS.Timeout; |
| 393 | private lastRuns = new Set<string>(); |
| 394 | private runningRuns = new Set<string>(); |
| 395 | |
| 396 | constructor() { |
| 397 | super(); |
| 398 | this.timer = setInterval(() => { |
| 399 | this.tickSchedule().catch((error) => console.error("[cy] 定时词云失败:", error)); |
| 400 | }, 30_000); |
| 401 | } |
| 402 | |
| 403 | cleanup(): void { |
| 404 | if (this.timer) clearInterval(this.timer); |
| 405 | } |
| 406 | |
| 407 | private async tickSchedule(): Promise<void> { |
| 408 | const config = readScheduleConfig(); |
| 409 | if (!config.enabled || !config.target || !config.times.length) return; |
| 410 | const now = new Date(); |
| 411 | const hh = String(now.getHours()).padStart(2, "0"); |
| 412 | const mm = String(now.getMinutes()).padStart(2, "0"); |
| 413 | const current = `${hh}:${mm}`; |
| 414 | if (!config.times.includes(current)) return; |
| 415 | const day = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`; |
| 416 | const key = `${day}:${current}`; |
| 417 | if (this.lastRuns.has(key) || this.runningRuns.has(key)) return; |
| 418 | this.runningRuns.add(key); |
| 419 | if (this.lastRuns.size > 200) this.lastRuns = new Set([...this.lastRuns].slice(-80)); |
| 420 | try { |
| 421 | const client = await getGlobalClient(); |
| 422 | if (!client) return; |
| 423 | await sendCloudToTarget(client, config.target, config.limit); |
| 424 | this.lastRuns.add(key); |
| 425 | } finally { |
| 426 | this.runningRuns.delete(key); |
| 427 | } |
| 428 | } |
| 429 | |
| 430 | cmdHandlers: Record<string, (msg: Api.Message, trigger?: Api.Message) => Promise<void>> = { |
| 431 | cy: async (msg) => { |
| 432 | const args = getArgs(String(msg.text || msg.message || "")); |
| 433 | const [subCommand = "", ...restParts] = args.split(/\s+/).filter(Boolean); |
| 434 | const rest = restParts.join(" "); |
| 435 | const config = readScheduleConfig(); |
| 436 | |
| 437 | if (["help", "?"].includes(subCommand)) { |
| 438 | await msg.edit({ text: buildHelpText() }); |
| 439 | return; |
| 440 | } |
| 441 | if (["target", "chat", "group"].includes(subCommand)) { |
| 442 | const target = rest.trim() === "here" || !rest.trim() ? targetFromCurrentChat(msg) : rest.trim(); |
nothing calls this directly
no test coverage detected