diff --git a/src/modules/bot/bot.controller.ts b/src/modules/bot/bot.controller.ts index 778ddbe..aa7aa48 100644 --- a/src/modules/bot/bot.controller.ts +++ b/src/modules/bot/bot.controller.ts @@ -3,6 +3,7 @@ import response from '../../utils/core/response'; import BotService from './bot.service'; import tools from '../../utils/modules/tools'; import logger from '../../utils/core/logger'; +import wsClientManager from '../../services/ws/wsClientManager'; class BotController { private readonly router: express.Router; @@ -20,6 +21,7 @@ class BotController { this.router.post(`/getBotId`, this.postBotsId); this.router.post('/getGroupInfo', this.postGroupInfo); this.router.post('/sendMessage', this.sendMessage); + this.router.post('/reportBots', this.reportBots); } /** @@ -73,6 +75,29 @@ class BotController { } }; + /** + * 广播要求同步群聊信息和bot连接情况 + * @param req + * @param res + */ + private reportBots = async (req: express.Request, res: express.Response): Promise => { + try { + const token = req.body.token; + if (tools.checkToken(token.toString())) { + const sendMessage = { + type: 'reportBots', + data: {}, + }; + await wsClientManager.broadcast(sendMessage); + await response.success(res, {}); + } else { + await tools.tokenCheckFailed(res, token); + } + } catch (e) { + await response.error(res); + } + }; + /** * 发送消息到群聊 * @param req diff --git a/src/modules/bot/bot.service.ts b/src/modules/bot/bot.service.ts index c25b491..b0e1c33 100644 --- a/src/modules/bot/bot.service.ts +++ b/src/modules/bot/bot.service.ts @@ -4,6 +4,7 @@ import fs from 'fs/promises'; import path from 'path'; import redisService from '../../services/redis/redis'; import wsClientManager from '../../services/ws/wsClientManager'; +import tools from '../../utils/core/tool'; class BotService { /** @@ -119,6 +120,59 @@ class BotService { return false; } + public async broadcastToAllGroups(message: string): Promise { + const userPath = paths.get('userData'); + const botsPath = path.join(userPath, '/crystelfBots'); + const dirData = await fs.readdir(botsPath); + const groupMap: Map = new Map(); + + for (const fileName of dirData) { + if (!fileName.endsWith('.json')) continue; + + const clientId = path.basename(fileName, '.json'); + const botList = await redisService.fetch('crystelfBots', fileName); + if (!Array.isArray(botList)) continue; + for (const bot of botList) { + if (!bot.uin || !bot.groups) continue; + for (const group of bot.groups) { + if (!groupMap.has(group.group_id)) { + groupMap.set(group.group_id, []); + } + groupMap.get(group.group_id)?.push({ botId: bot.uin, clientId }); + } + } + } + + for (const [groupId, botEntries] of groupMap.entries()) { + const clientGroups = new Map(); + botEntries.forEach(({ botId, clientId }) => { + if (!clientGroups.has(clientId)) clientGroups.set(clientId, []); + clientGroups.get(clientId)!.push(botId); + }); + const selectedClientId = tools.getRandomItem([...clientGroups.keys()]); + const botCandidates = clientGroups.get(selectedClientId)!; + const selectedBotId = tools.getRandomItem(botCandidates); + const delay = tools.getRandomDelay(30_000, 90_000); + setTimeout(() => { + const sendData = { + type: 'sendMessage', + data: { + botId: selectedBotId, + groupId, + clientId: selectedClientId, + message, + }, + }; + logger.info( + `[广播] 向群 ${groupId} 使用Bot ${selectedBotId}(客户端 ${selectedClientId})发送消息,延迟 ${delay / 1000} 秒` + ); + wsClientManager.send(selectedClientId, sendData).catch((e) => { + logger.error(`发送到群${groupId}失败:`, e); + }); + }, delay); + } + } + /** * 获取`botId`对应的`client` * @param botId @@ -172,7 +226,6 @@ class BotService { logger.error(`读取${clientId}出错..`); } } - return undefined; } } diff --git a/src/utils/core/tool.ts b/src/utils/core/tool.ts index 82bb710..8cc740b 100644 --- a/src/utils/core/tool.ts +++ b/src/utils/core/tool.ts @@ -21,6 +21,14 @@ let tools = { } logger.error(lastError); }, + + getRandomItem(list: T[]): T { + return list[Math.floor(Math.random() * list.length)]; + }, + + getRandomDelay(min: number, max: number): number { + return Math.floor(Math.random() * (max - min + 1)) + min; + }, }; export default tools;