🔥 chore: remove unused bot module, service, controller, and DTO files from the project structure

This commit is contained in:
Jerry 2025-12-05 13:40:02 +08:00
parent a055ea2407
commit 741527410c
5 changed files with 0 additions and 410 deletions

View File

@ -9,7 +9,6 @@ import { PersistenceModule } from './core/persistence/persistence.module';
import { RedisModule } from './core/redis/redis.module'; import { RedisModule } from './core/redis/redis.module';
import { WsModule } from './core/ws/ws.module'; import { WsModule } from './core/ws/ws.module';
import { SystemWebModule } from './modules/system/systemWeb.module'; import { SystemWebModule } from './modules/system/systemWeb.module';
import { BotModule } from './modules/bot/bot.module';
import { CdnModule } from './modules/cdn/cdn.module'; import { CdnModule } from './modules/cdn/cdn.module';
import { WordsModule } from './modules/words/words.module'; import { WordsModule } from './modules/words/words.module';
import { MemeModule } from './modules/meme/meme.module'; import { MemeModule } from './modules/meme/meme.module';
@ -27,7 +26,6 @@ import { OpenListModule } from './core/openlist/openlist.module';
RedisModule, RedisModule,
WsModule, WsModule,
SystemWebModule, SystemWebModule,
BotModule,
CdnModule, CdnModule,
WordsModule, WordsModule,
MemeModule, MemeModule,

View File

@ -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: '广播任务已开始,正在后台执行..' };
}
}

View File

@ -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;
}

View File

@ -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 {}

View File

@ -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;
}
}