mirror of
https://github.com/crystelf/crystelf-core.git
synced 2025-12-05 10:31:56 +00:00
🔥 chore: remove unused bot module, service, controller, and DTO files from the project structure
This commit is contained in:
parent
a055ea2407
commit
741527410c
@ -9,7 +9,6 @@ import { PersistenceModule } from './core/persistence/persistence.module';
|
||||
import { RedisModule } from './core/redis/redis.module';
|
||||
import { WsModule } from './core/ws/ws.module';
|
||||
import { SystemWebModule } from './modules/system/systemWeb.module';
|
||||
import { BotModule } from './modules/bot/bot.module';
|
||||
import { CdnModule } from './modules/cdn/cdn.module';
|
||||
import { WordsModule } from './modules/words/words.module';
|
||||
import { MemeModule } from './modules/meme/meme.module';
|
||||
@ -27,7 +26,6 @@ import { OpenListModule } from './core/openlist/openlist.module';
|
||||
RedisModule,
|
||||
WsModule,
|
||||
SystemWebModule,
|
||||
BotModule,
|
||||
CdnModule,
|
||||
WordsModule,
|
||||
MemeModule,
|
||||
|
||||
@ -1,117 +0,0 @@
|
||||
import { Body, Controller, Inject, Post, UseGuards } from '@nestjs/common';
|
||||
import { ApiOperation, ApiTags, ApiBody, ApiHeader } from '@nestjs/swagger';
|
||||
import { BotService } from './bot.service';
|
||||
import { WsClientManager } from 'src/core/ws/ws-client.manager';
|
||||
import { TokenAuthGuard } from 'src/core/tools/token-auth.guard';
|
||||
import {
|
||||
BroadcastDto,
|
||||
GroupInfoDto,
|
||||
SendMessageDto,
|
||||
TokenDto,
|
||||
} from './bot.dto';
|
||||
|
||||
@ApiTags('Bot相关操作')
|
||||
@Controller('bot')
|
||||
export class BotController {
|
||||
constructor(
|
||||
@Inject(BotService)
|
||||
private readonly botService: BotService,
|
||||
@Inject(WsClientManager)
|
||||
private readonly wsClientManager: WsClientManager,
|
||||
) {}
|
||||
|
||||
@Post('getBotId')
|
||||
@UseGuards(TokenAuthGuard)
|
||||
@ApiOperation({ summary: '获取当前连接到核心的全部 botId 数组' })
|
||||
@ApiHeader({ name: 'x-token', description: '身份验证token', required: true })
|
||||
@ApiBody({
|
||||
description: '获取botId请求',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
},
|
||||
})
|
||||
public async postBotsId(@Body() dto: TokenDto) {
|
||||
return this.botService.getBotId();
|
||||
}
|
||||
|
||||
@Post('getGroupInfo')
|
||||
@UseGuards(TokenAuthGuard)
|
||||
@ApiOperation({ summary: '获取群聊信息' })
|
||||
@ApiHeader({ name: 'x-token', description: '身份验证token', required: true })
|
||||
@ApiBody({
|
||||
description: '获取群聊信息参数',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
groupId: { type: 'number', description: '群号', example: 114514 },
|
||||
},
|
||||
required: ['groupId'],
|
||||
},
|
||||
})
|
||||
public async postGroupInfo(@Body() dto: GroupInfoDto) {
|
||||
return this.botService.getGroupInfo({ groupId: dto.groupId });
|
||||
}
|
||||
|
||||
@Post('reportBots')
|
||||
@UseGuards(TokenAuthGuard)
|
||||
@ApiOperation({ summary: '广播:要求同步群聊信息和 bot 连接情况' })
|
||||
@ApiHeader({ name: 'x-token', description: '身份验证token', required: true })
|
||||
@ApiBody({
|
||||
description: '广播同步请求',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
},
|
||||
})
|
||||
public async reportBots(@Body() dto: TokenDto) {
|
||||
const sendMessage = {
|
||||
type: 'reportBots',
|
||||
data: {},
|
||||
};
|
||||
await this.wsClientManager.broadcast(sendMessage);
|
||||
return { message: '正在请求同步 bot 数据..' };
|
||||
}
|
||||
|
||||
@Post('sendMessage')
|
||||
@UseGuards(TokenAuthGuard)
|
||||
@ApiOperation({ summary: '发送消息到群聊', description: '自动选择bot发送' })
|
||||
@ApiHeader({ name: 'x-token', description: '身份验证token', required: true })
|
||||
@ApiBody({
|
||||
description: '发送消息参数',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
groupId: { type: 'number', description: '群号', example: 114514 },
|
||||
message: { type: 'string', description: '要发送的消息', example: 'Ciallo~(∠・ω< )⌒★' },
|
||||
},
|
||||
required: ['groupId', 'message'],
|
||||
},
|
||||
})
|
||||
public async sendMessage(@Body() dto: SendMessageDto) {
|
||||
const flag = await this.botService.sendMessage(dto.groupId, dto.message);
|
||||
if (!flag) {
|
||||
return { message: '消息发送失败' };
|
||||
}
|
||||
return { message: '消息发送成功' };
|
||||
}
|
||||
|
||||
@Post('broadcast')
|
||||
@UseGuards(TokenAuthGuard)
|
||||
@ApiOperation({ summary: '广播消息到全部群聊', description: '随机延迟' })
|
||||
@ApiHeader({ name: 'x-token', description: '身份验证token', required: true })
|
||||
@ApiBody({
|
||||
description: '广播消息参数',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
message: { type: 'string', description: '要广播的消息', example: '全体目光向我看齐!我宣布个事儿..' },
|
||||
},
|
||||
required: ['message'],
|
||||
},
|
||||
})
|
||||
public async smartBroadcast(@Body() dto: BroadcastDto) {
|
||||
await this.botService.broadcastToAllGroups(dto.message);
|
||||
return { message: '广播任务已开始,正在后台执行..' };
|
||||
}
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class TokenDto {
|
||||
}
|
||||
|
||||
export class GroupInfoDto {
|
||||
@ApiProperty({ description: '群号', example: 114514 })
|
||||
groupId: number;
|
||||
}
|
||||
|
||||
export class SendMessageDto extends GroupInfoDto {
|
||||
@ApiProperty({ description: '要发送的消息', example: 'Ciallo~(∠・ω< )⌒★' })
|
||||
message: string;
|
||||
}
|
||||
|
||||
export class BroadcastDto {
|
||||
@ApiProperty({
|
||||
description: '要广播的消息',
|
||||
example: '全体目光向我看齐!我宣布个事儿..',
|
||||
})
|
||||
message: string;
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { RedisModule } from '../../core/redis/redis.module';
|
||||
import { WsModule } from '../../core/ws/ws.module';
|
||||
import { ToolsModule } from '../../core/tools/tools.module';
|
||||
import { PathModule } from '../../core/path/path.module';
|
||||
import { BotController } from './bot.controller';
|
||||
import { BotService } from './bot.service';
|
||||
|
||||
@Module({
|
||||
imports: [RedisModule, WsModule, ToolsModule, PathModule],
|
||||
controllers: [BotController],
|
||||
providers: [BotService],
|
||||
})
|
||||
export class BotModule {}
|
||||
@ -1,255 +0,0 @@
|
||||
import { Inject, Injectable, Logger } from '@nestjs/common';
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import { RedisService } from 'src/core/redis/redis.service';
|
||||
import { WsClientManager } from 'src/core/ws/ws-client.manager';
|
||||
import { ToolsService } from '../../core/tools/tools.service';
|
||||
import { PathService } from '../../core/path/path.service';
|
||||
|
||||
@Injectable()
|
||||
export class BotService {
|
||||
private readonly logger = new Logger(BotService.name);
|
||||
|
||||
constructor(
|
||||
@Inject(RedisService)
|
||||
private readonly redisService: RedisService,
|
||||
@Inject(WsClientManager)
|
||||
private readonly wsClientManager: WsClientManager,
|
||||
@Inject(ToolsService)
|
||||
private readonly tools: ToolsService,
|
||||
@Inject(PathService)
|
||||
private readonly paths: PathService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 获取botId数组
|
||||
*/
|
||||
public async getBotId(): Promise<{ uin: number; nickName: string }[]> {
|
||||
this.logger.debug('正在请求获取在线的bot..');
|
||||
const userPath = this.paths.get('userData');
|
||||
const botsPath = path.join(userPath, '/crystelfBots');
|
||||
const dirData = await fs.readdir(botsPath);
|
||||
const uins: { uin: number; nickName: string }[] = [];
|
||||
|
||||
for (const fileName of dirData) {
|
||||
if (!fileName.endsWith('.json')) continue;
|
||||
|
||||
try {
|
||||
const raw = await this.redisService.fetch('crystelfBots', fileName);
|
||||
if (!raw || !Array.isArray(raw)) continue;
|
||||
|
||||
for (const bot of raw) {
|
||||
const uin = Number(bot.uin);
|
||||
const nickName = bot.nickName || '';
|
||||
if (!isNaN(uin)) {
|
||||
uins.push({ uin, nickName });
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
this.logger.error(`读取或解析 ${fileName} 出错: ${err}`);
|
||||
}
|
||||
}
|
||||
return uins;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群聊信息
|
||||
* @param data
|
||||
*/
|
||||
public async getGroupInfo(data: {
|
||||
botId?: number;
|
||||
groupId: number;
|
||||
clientId?: string;
|
||||
}): Promise<any> {
|
||||
this.logger.debug(`正在尝试获取${data.groupId}的信息..)`);
|
||||
const sendBot: number | undefined =
|
||||
data.botId ?? (await this.getGroupBot(data.groupId));
|
||||
if (!sendBot) {
|
||||
this.logger.warn(`不存在能向群聊${data.groupId}发送消息的Bot!`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const sendData = {
|
||||
type: 'getGroupInfo',
|
||||
data: {
|
||||
botId: sendBot,
|
||||
groupId: data.groupId,
|
||||
clientID: data.clientId ?? (await this.getBotClient(sendBot)),
|
||||
},
|
||||
};
|
||||
|
||||
if (sendData.data.clientID) {
|
||||
const returnData = await this.wsClientManager.sendAndWait(
|
||||
sendData.data.clientID,
|
||||
sendData,
|
||||
);
|
||||
return returnData ?? undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息到群聊
|
||||
* @param groupId 群号
|
||||
* @param message 消息
|
||||
*/
|
||||
public async sendMessage(groupId: number, message: string): Promise<boolean> {
|
||||
this.logger.log(`发送${message}到${groupId}..`);
|
||||
const sendBot = await this.getGroupBot(groupId);
|
||||
if (!sendBot) {
|
||||
this.logger.warn(`不存在能向群聊${groupId}发送消息的Bot!`);
|
||||
return false;
|
||||
}
|
||||
const client = await this.getBotClient(sendBot);
|
||||
if (!client) {
|
||||
this.logger.warn(`不存在${sendBot}对应的client!`);
|
||||
return false;
|
||||
}
|
||||
const sendData = {
|
||||
type: 'sendMessage',
|
||||
data: { botId: sendBot, groupId, clientId: client, message },
|
||||
};
|
||||
await this.wsClientManager.send(client, sendData);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 广播消息
|
||||
* @param message 要广播的消息
|
||||
*/
|
||||
public async broadcastToAllGroups(message: string): Promise<void> {
|
||||
const userPath = this.paths.get('userData');
|
||||
const botsPath = path.join(userPath, '/crystelfBots');
|
||||
const dirData = await fs.readdir(botsPath);
|
||||
const groupMap: Map<number, { botId: number; clientId: string }[]> =
|
||||
new Map();
|
||||
this.logger.log(`广播消息:${message}`);
|
||||
for (const fileName of dirData) {
|
||||
if (!fileName.endsWith('.json')) continue;
|
||||
|
||||
const clientId = path.basename(fileName, '.json');
|
||||
const botList = await this.redisService.fetch('crystelfBots', fileName);
|
||||
if (!Array.isArray(botList)) continue;
|
||||
|
||||
for (const bot of botList) {
|
||||
const botId = Number(bot.uin);
|
||||
const groups = bot.groups;
|
||||
|
||||
if (!botId || !Array.isArray(groups)) continue;
|
||||
|
||||
for (const group of groups) {
|
||||
if (group.group_id === '未知') continue;
|
||||
const groupId = Number(group.group_id);
|
||||
if (isNaN(groupId)) continue;
|
||||
|
||||
if (!groupMap.has(groupId)) {
|
||||
groupMap.set(groupId, []);
|
||||
}
|
||||
groupMap.get(groupId)!.push({ botId, clientId });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [groupId, botEntries] of groupMap.entries()) {
|
||||
this.logger.debug(
|
||||
`[群 ${groupId}] 候选Bot列表: ${JSON.stringify(botEntries)}`,
|
||||
);
|
||||
|
||||
const clientGroups = new Map<string, number[]>();
|
||||
botEntries.forEach(({ botId, clientId }) => {
|
||||
if (!clientGroups.has(clientId)) clientGroups.set(clientId, []);
|
||||
clientGroups.get(clientId)!.push(botId);
|
||||
});
|
||||
|
||||
const selectedClientId = this.tools.getRandomItem([
|
||||
...clientGroups.keys(),
|
||||
]);
|
||||
const botCandidates = clientGroups.get(selectedClientId)!;
|
||||
const selectedBotId = this.tools.getRandomItem(botCandidates);
|
||||
const delay = this.tools.getRandomDelay(10_000, 150_000);
|
||||
|
||||
setTimeout(() => {
|
||||
const sendData = {
|
||||
type: 'sendMessage',
|
||||
data: {
|
||||
botId: selectedBotId,
|
||||
groupId,
|
||||
clientId: selectedClientId,
|
||||
message,
|
||||
},
|
||||
};
|
||||
this.logger.log(
|
||||
`[广播] 向群 ${groupId} 使用Bot ${selectedBotId}(客户端 ${selectedClientId})发送消息${message},延迟 ${
|
||||
delay / 1000
|
||||
} 秒`,
|
||||
);
|
||||
this.wsClientManager.send(selectedClientId, sendData).catch((e) => {
|
||||
this.logger.error(`发送到群${groupId}失败:`, e);
|
||||
});
|
||||
}, delay);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取botId对应的client
|
||||
* @param botId
|
||||
* @private
|
||||
*/
|
||||
private async getBotClient(botId: number): Promise<string | undefined> {
|
||||
const userPath = this.paths.get('userData');
|
||||
const botsPath = path.join(userPath, '/crystelfBots');
|
||||
const dirData = await fs.readdir(botsPath);
|
||||
|
||||
for (const clientId of dirData) {
|
||||
if (!clientId.endsWith('.json')) continue;
|
||||
|
||||
try {
|
||||
const raw = await this.redisService.fetch('crystelfBots', clientId);
|
||||
if (!Array.isArray(raw)) continue;
|
||||
|
||||
for (const bot of raw) {
|
||||
const uin = Number(bot.uin);
|
||||
if (!isNaN(uin) && uin === botId) {
|
||||
return path.basename(clientId, '.json');
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
this.logger.error(`读取${clientId}出错..`);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取groupId对应的botId
|
||||
* @param groupId
|
||||
* @private
|
||||
*/
|
||||
private async getGroupBot(groupId: number): Promise<number | undefined> {
|
||||
const userPath = this.paths.get('userData');
|
||||
const botsPath = path.join(userPath, '/crystelfBots');
|
||||
const dirData = await fs.readdir(botsPath);
|
||||
|
||||
for (const clientId of dirData) {
|
||||
if (!clientId.endsWith('.json')) continue;
|
||||
|
||||
try {
|
||||
const raw = await this.redisService.fetch('crystelfBots', clientId);
|
||||
if (!Array.isArray(raw)) continue;
|
||||
|
||||
for (const bot of raw) {
|
||||
const uin = Number(bot.uin);
|
||||
const groups = bot.groups;
|
||||
if (!uin || !Array.isArray(groups)) continue;
|
||||
|
||||
if (groups.find((g) => Number(g.group_id) === groupId)) {
|
||||
return uin;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
this.logger.error(`读取${clientId}出错..`);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user