feat:water

This commit is contained in:
Jerry 2025-09-02 13:49:10 +08:00
parent c7108833ed
commit 3200c39a7d
21 changed files with 56 additions and 73 deletions

View File

@ -21,14 +21,14 @@ export class AutoUpdateService {
/** /**
* *
*/ */
async checkForUpdates(): Promise<boolean> { public async checkForUpdates(): Promise<boolean> {
return this.checkRepoForUpdates(this.repoPath, 'crystelf-core'); return this.checkRepoForUpdates(this.repoPath, 'crystelf-core');
} }
/** /**
* *
*/ */
async checkRepoForUpdates( public async checkRepoForUpdates(
folderPath: string, folderPath: string,
label = '子仓库', label = '子仓库',
): Promise<boolean> { ): Promise<boolean> {

View File

@ -16,7 +16,7 @@ export class FilesService {
* @param targetPath * @param targetPath
* @param includeFile * @param includeFile
*/ */
async createDir(targetPath = '', includeFile = false): Promise<void> { public async createDir(targetPath = '', includeFile = false): Promise<void> {
const root = this.paths.get('root'); const root = this.paths.get('root');
try { try {
const dirToCreate = path.isAbsolute(targetPath) const dirToCreate = path.isAbsolute(targetPath)

View File

@ -17,7 +17,7 @@ export class PathService {
* *
* @param type * @param type
*/ */
get(type?: PathType): string { public get(type?: PathType): string {
const mappings: Record<PathType, string> = { const mappings: Record<PathType, string> = {
root: this.baseDir, root: this.baseDir,
public: path.join(this.baseDir, 'public'), public: path.join(this.baseDir, 'public'),
@ -64,7 +64,7 @@ export class PathService {
* @param targetPath * @param targetPath
* @param includeFile * @param includeFile
*/ */
createDir(targetPath: string, includeFile: boolean = false): void { public createDir(targetPath: string, includeFile: boolean = false): void {
try { try {
const dirToCreate = includeFile ? path.dirname(targetPath) : targetPath; const dirToCreate = includeFile ? path.dirname(targetPath) : targetPath;
fs.mkdirSync(dirToCreate, { recursive: true }); fs.mkdirSync(dirToCreate, { recursive: true });
@ -74,30 +74,6 @@ export class PathService {
throw err; throw err;
} }
} }
/**
*
* @param paths
*/
join(...paths: string[]): string {
return path.join(...paths);
}
/**
*
* @param filePath
*/
getExtension(filePath: string): string {
return path.extname(filePath);
}
/**
*
* @param filePath
*/
getBasename(filePath: string): string {
return path.basename(filePath, path.extname(filePath));
}
} }
export type PathType = export type PathType =

View File

@ -21,7 +21,7 @@ export class RedisService implements OnModuleInit {
private readonly Persistence: PersistenceService, private readonly Persistence: PersistenceService,
) {} ) {}
async onModuleInit() { public async onModuleInit() {
await this.connectWithRetry(); await this.connectWithRetry();
this.setupEventListeners(); this.setupEventListeners();
} }

View File

@ -37,7 +37,7 @@ export class SystemService {
/** /**
* *
*/ */
checkRestartTime(): number | null { public checkRestartTime(): number | null {
if (fs.existsSync(this.restartFile)) { if (fs.existsSync(this.restartFile)) {
const prev = Number(fs.readFileSync(this.restartFile, 'utf-8')); const prev = Number(fs.readFileSync(this.restartFile, 'utf-8'));
const duration = ((Date.now() - prev) / 1000 - 5).toFixed(2); const duration = ((Date.now() - prev) / 1000 - 5).toFixed(2);
@ -51,7 +51,7 @@ export class SystemService {
/** /**
* *
*/ */
async restart(): Promise<void> { public async restart(): Promise<void> {
this.markRestartTime(); this.markRestartTime();
this.logger.warn('服务即将重启..'); this.logger.warn('服务即将重启..');
await new Promise((resolve) => setTimeout(resolve, 300)); await new Promise((resolve) => setTimeout(resolve, 300));
@ -61,7 +61,7 @@ export class SystemService {
/** /**
* *
*/ */
async checkUpdate(): Promise<void> { public async checkUpdate(): Promise<void> {
this.logger.debug('检查系统代码更新..'); this.logger.debug('检查系统代码更新..');
const updated = await this.autoUpdateService.checkForUpdates(); const updated = await this.autoUpdateService.checkForUpdates();
if (updated) { if (updated) {

View File

@ -19,7 +19,7 @@ export class TokenAuthGuard implements CanActivate {
@Inject(ToolsService) private readonly toolsService: ToolsService, @Inject(ToolsService) private readonly toolsService: ToolsService,
) {} ) {}
canActivate(context: ExecutionContext): boolean { public canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest(); const request = context.switchToHttp().getRequest();
const token = request.body?.token || request.headers['x-token']; //两种传入方式 const token = request.body?.token || request.headers['x-token']; //两种传入方式

View File

@ -19,7 +19,7 @@ export class ToolsService {
* @param operation * @param operation
* @param options * @param options
*/ */
async retry<T>( public async retry<T>(
operation: () => Promise<T>, operation: () => Promise<T>,
options: RetryOptions, options: RetryOptions,
): Promise<T> { ): Promise<T> {
@ -47,14 +47,14 @@ export class ToolsService {
/** /**
* *
*/ */
getRandomItem<T>(list: T[]): T { public getRandomItem<T>(list: T[]): T {
return list[Math.floor(Math.random() * list.length)]; return list[Math.floor(Math.random() * list.length)];
} }
/** /**
* *
*/ */
getRandomDelay(min: number, max: number): number { public getRandomDelay(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min; return Math.floor(Math.random() * (max - min + 1)) + min;
} }
@ -62,7 +62,7 @@ export class ToolsService {
* token * token
* @param token token * @param token token
*/ */
checkToken(token: string): boolean { public checkToken(token: string): boolean {
const expected = this.config.get<string>('TOKEN'); const expected = this.config.get<string>('TOKEN');
if (!expected) { if (!expected) {
this.logger.error('环境变量 TOKEN 未配置,无法进行验证!'); this.logger.error('环境变量 TOKEN 未配置,无法进行验证!');
@ -75,7 +75,7 @@ export class ToolsService {
* token * token
* @param token token * @param token token
*/ */
tokenCheckFailed(token: string): never { public tokenCheckFailed(token: string): never {
this.logger.warn(`有个小可爱使用了错误的 token: ${JSON.stringify(token)}`); this.logger.warn(`有个小可爱使用了错误的 token: ${JSON.stringify(token)}`);
throw new UnauthorizedException('token 验证失败..'); throw new UnauthorizedException('token 验证失败..');
} }

View File

@ -17,7 +17,7 @@ export class WsClientManager {
* @param id * @param id
* @param socket * @param socket
*/ */
add(id: ClientID, socket: WebSocket) { public add(id: ClientID, socket: WebSocket) {
this.clients.set(id, socket); this.clients.set(id, socket);
} }
@ -25,7 +25,7 @@ export class WsClientManager {
* *
* @param id * @param id
*/ */
remove(id: ClientID) { public remove(id: ClientID) {
this.clients.delete(id); this.clients.delete(id);
} }
@ -33,7 +33,7 @@ export class WsClientManager {
* *
* @param id * @param id
*/ */
get(id: ClientID): WebSocket | undefined { public get(id: ClientID): WebSocket | undefined {
return this.clients.get(id); return this.clients.get(id);
} }
@ -42,7 +42,7 @@ export class WsClientManager {
* @param id * @param id
* @param data * @param data
*/ */
async send(id: ClientID, data: any): Promise<boolean> { public async send(id: ClientID, data: any): Promise<boolean> {
const socket = this.clients.get(id); const socket = this.clients.get(id);
if (!socket || socket.readyState !== WebSocket.OPEN) return false; if (!socket || socket.readyState !== WebSocket.OPEN) return false;
return this.safeSend(socket, data); return this.safeSend(socket, data);
@ -54,7 +54,11 @@ export class WsClientManager {
* @param data * @param data
* @param timeout * @param timeout
*/ */
async sendAndWait(id: ClientID, data: any, timeout = 5000): Promise<any> { public async sendAndWait(
id: ClientID,
data: any,
timeout = 5000,
): Promise<any> {
const socket = this.clients.get(id); const socket = this.clients.get(id);
if (!socket) return; if (!socket) return;
@ -86,7 +90,7 @@ export class WsClientManager {
* @param requestId id * @param requestId id
* @param data * @param data
*/ */
resolvePendingRequest(requestId: string, data: any): boolean { public resolvePendingRequest(requestId: string, data: any): boolean {
const callback = pendingRequests.get(requestId); const callback = pendingRequests.get(requestId);
if (callback) { if (callback) {
pendingRequests.delete(requestId); pendingRequests.delete(requestId);
@ -100,7 +104,7 @@ export class WsClientManager {
* 广 * 广
* @param data * @param data
*/ */
async broadcast(data: any): Promise<void> { public async broadcast(data: any): Promise<void> {
const tasks = Array.from(this.clients.values()).map((socket) => { const tasks = Array.from(this.clients.values()).map((socket) => {
if (socket.readyState === WebSocket.OPEN) { if (socket.readyState === WebSocket.OPEN) {
return this.safeSend(socket, data); return this.safeSend(socket, data);

View File

@ -17,7 +17,7 @@ export class WsMessageHandler {
this.logger.log(`已注册 ${handlers.length} 个 WS handler`); this.logger.log(`已注册 ${handlers.length} 个 WS handler`);
} }
async handle(socket: AuthenticatedSocket, clientId: string, msg: any) { public async handle(socket: AuthenticatedSocket, clientId: string, msg: any) {
try { try {
// 如果是 pendingRequests 的回包 // 如果是 pendingRequests 的回包
if ( if (

View File

@ -43,7 +43,7 @@ export class WsGateway implements OnGatewayConnection, OnGatewayDisconnect {
* @param client * @param client
* @param req * @param req
*/ */
async handleConnection(client: AuthenticatedSocket, req: any) { public async handleConnection(client: AuthenticatedSocket, req: any) {
const ip = req.socket.remoteAddress || 'unknown'; const ip = req.socket.remoteAddress || 'unknown';
this.logger.log(`收到来自 ${ip} 的 WebSocket 连接请求..`); this.logger.log(`收到来自 ${ip} 的 WebSocket 连接请求..`);
@ -67,7 +67,7 @@ export class WsGateway implements OnGatewayConnection, OnGatewayDisconnect {
* *
* @param client * @param client
*/ */
async handleDisconnect(client: AuthenticatedSocket) { public async handleDisconnect(client: AuthenticatedSocket) {
if (client.heartbeat) clearInterval(client.heartbeat); if (client.heartbeat) clearInterval(client.heartbeat);
if (client.clientId) { if (client.clientId) {
this.wsClientManager.remove(client.clientId); this.wsClientManager.remove(client.clientId);

View File

@ -23,7 +23,7 @@ export class BotController {
@Post('getBotId') @Post('getBotId')
@UseGuards(TokenAuthGuard) @UseGuards(TokenAuthGuard)
@ApiOperation({ summary: '获取当前连接到核心的全部 botId 数组' }) @ApiOperation({ summary: '获取当前连接到核心的全部 botId 数组' })
async postBotsId(@Body() dto: TokenDto) { public async postBotsId(@Body() dto: TokenDto) {
return this.botService.getBotId(); return this.botService.getBotId();
} }
@ -31,14 +31,14 @@ export class BotController {
@UseGuards(TokenAuthGuard) @UseGuards(TokenAuthGuard)
@ApiOperation({ summary: '获取群聊信息' }) @ApiOperation({ summary: '获取群聊信息' })
@ApiBody({ type: GroupInfoDto }) @ApiBody({ type: GroupInfoDto })
async postGroupInfo(@Body() dto: GroupInfoDto) { public async postGroupInfo(@Body() dto: GroupInfoDto) {
return this.botService.getGroupInfo({ groupId: dto.groupId }); return this.botService.getGroupInfo({ groupId: dto.groupId });
} }
@Post('reportBots') @Post('reportBots')
@UseGuards(TokenAuthGuard) @UseGuards(TokenAuthGuard)
@ApiOperation({ summary: '广播:要求同步群聊信息和 bot 连接情况' }) @ApiOperation({ summary: '广播:要求同步群聊信息和 bot 连接情况' })
async reportBots(@Body() dto: TokenDto) { public async reportBots(@Body() dto: TokenDto) {
const sendMessage = { const sendMessage = {
type: 'reportBots', type: 'reportBots',
data: {}, data: {},
@ -51,7 +51,7 @@ export class BotController {
@UseGuards(TokenAuthGuard) @UseGuards(TokenAuthGuard)
@ApiOperation({ summary: '发送消息到群聊', description: '自动选择bot发送' }) @ApiOperation({ summary: '发送消息到群聊', description: '自动选择bot发送' })
@ApiBody({ type: SendMessageDto }) @ApiBody({ type: SendMessageDto })
async sendMessage(@Body() dto: SendMessageDto) { public async sendMessage(@Body() dto: SendMessageDto) {
const flag = await this.botService.sendMessage(dto.groupId, dto.message); const flag = await this.botService.sendMessage(dto.groupId, dto.message);
if (!flag) { if (!flag) {
return { message: '消息发送失败' }; return { message: '消息发送失败' };
@ -63,7 +63,7 @@ export class BotController {
@UseGuards(TokenAuthGuard) @UseGuards(TokenAuthGuard)
@ApiOperation({ summary: '广播消息到全部群聊', description: '随机延迟' }) @ApiOperation({ summary: '广播消息到全部群聊', description: '随机延迟' })
@ApiBody({ type: BroadcastDto }) @ApiBody({ type: BroadcastDto })
async smartBroadcast(@Body() dto: BroadcastDto) { public async smartBroadcast(@Body() dto: BroadcastDto) {
await this.botService.broadcastToAllGroups(dto.message); await this.botService.broadcastToAllGroups(dto.message);
return { message: '广播任务已开始,正在后台执行..' }; return { message: '广播任务已开始,正在后台执行..' };
} }

View File

@ -24,7 +24,7 @@ export class BotService {
/** /**
* botId数组 * botId数组
*/ */
async getBotId(): Promise<{ uin: number; nickName: string }[]> { public async getBotId(): Promise<{ uin: number; nickName: string }[]> {
this.logger.debug('正在请求获取在线的bot..'); this.logger.debug('正在请求获取在线的bot..');
const userPath = this.paths.get('userData'); const userPath = this.paths.get('userData');
const botsPath = path.join(userPath, '/crystelfBots'); const botsPath = path.join(userPath, '/crystelfBots');
@ -56,7 +56,7 @@ export class BotService {
* *
* @param data * @param data
*/ */
async getGroupInfo(data: { public async getGroupInfo(data: {
botId?: number; botId?: number;
groupId: number; groupId: number;
clientId?: string; clientId?: string;
@ -93,7 +93,7 @@ export class BotService {
* @param groupId * @param groupId
* @param message * @param message
*/ */
async sendMessage(groupId: number, message: string): Promise<boolean> { public async sendMessage(groupId: number, message: string): Promise<boolean> {
this.logger.log(`发送${message}${groupId}..`); this.logger.log(`发送${message}${groupId}..`);
const sendBot = await this.getGroupBot(groupId); const sendBot = await this.getGroupBot(groupId);
if (!sendBot) { if (!sendBot) {
@ -117,7 +117,7 @@ export class BotService {
* 广 * 广
* @param message 广 * @param message 广
*/ */
async broadcastToAllGroups(message: string): Promise<void> { public async broadcastToAllGroups(message: string): Promise<void> {
const userPath = this.paths.get('userData'); const userPath = this.paths.get('userData');
const botsPath = path.join(userPath, '/crystelfBots'); const botsPath = path.join(userPath, '/crystelfBots');
const dirData = await fs.readdir(botsPath); const dirData = await fs.readdir(botsPath);

View File

@ -54,13 +54,13 @@ export class CdnController {
summary: '获取资源', summary: '获取资源',
description: '由晶灵资源分发服务器(CDN)提供支持', description: '由晶灵资源分发服务器(CDN)提供支持',
}) })
async getFile(@Res() res: Response, @Req() req: Request) { public async getFile(@Res() res: Response, @Req() req: Request) {
const relativePath = req.url.replace('/cdn/', ''); //params.path; const relativePath = req.url.replace('/cdn/', ''); //params.path;
return this.deliverFile(relativePath, res); return this.deliverFile(relativePath, res);
} }
@Get('public/files/*') @Get('public/files/*')
async fromPublicFiles(@Res() res: Response, @Req() req: Request) { public async fromPublicFiles(@Res() res: Response, @Req() req: Request) {
const relativePath = req.url.replace('/public/files/', ''); const relativePath = req.url.replace('/public/files/', '');
this.logger.debug( this.logger.debug(
`请求 /public/files/${relativePath} → 代理到 /cdn/${relativePath}`, `请求 /public/files/${relativePath} → 代理到 /cdn/${relativePath}`,
@ -69,7 +69,7 @@ export class CdnController {
} }
@Get('public/cdn/*') @Get('public/cdn/*')
async fromPublicCdn(@Req() req: Request, @Res() res: Response) { public async fromPublicCdn(@Req() req: Request, @Res() res: Response) {
const relativePath = req.url.replace('/public/cdn/', ''); const relativePath = req.url.replace('/public/cdn/', '');
this.logger.debug( this.logger.debug(
`请求 /public/cdn/${relativePath} → 代理到 /cdn/${relativePath}`, `请求 /public/cdn/${relativePath} → 代理到 /cdn/${relativePath}`,

View File

@ -17,7 +17,7 @@ export class CdnService {
* *
* @param relativePath * @param relativePath
*/ */
async getFile(relativePath: string): Promise<string | null> { public async getFile(relativePath: string): Promise<string | null> {
if (!this.filePath) this.filePath = this.paths.get('public'); if (!this.filePath) this.filePath = this.paths.get('public');
if ( if (
!this.isValidPath(relativePath) && !this.isValidPath(relativePath) &&

View File

@ -31,7 +31,10 @@ export class MemeController {
@Post('getRandom') @Post('getRandom')
@ApiOperation({ summary: '获取随机表情包' }) @ApiOperation({ summary: '获取随机表情包' })
@ApiBody({ type: MemeRequestDto }) @ApiBody({ type: MemeRequestDto })
async getRandomMeme(@Body() dto: MemeRequestDto, @Res() res: Response) { public async getRandomMeme(
@Body() dto: MemeRequestDto,
@Res() res: Response,
) {
try { try {
const memePath = await this.memeService.getRandomMemePath( const memePath = await this.memeService.getRandomMemePath(
dto.character, dto.character,

View File

@ -14,7 +14,7 @@ export class MemeService {
* @param character * @param character
* @param status * @param status
*/ */
async getRandomMemePath( public async getRandomMemePath(
character?: string, character?: string,
status?: string, status?: string,
): Promise<string | null> { ): Promise<string | null> {

View File

@ -32,7 +32,7 @@ export class SystemWebController {
}) })
@UseGuards(TokenAuthGuard) @UseGuards(TokenAuthGuard)
@ApiBody({ type: WebServerDto }) @ApiBody({ type: WebServerDto })
async systemRestart(@Param('token') token: string): Promise<string> { public async systemRestart(@Param('token') token: string): Promise<string> {
this.systemService.systemRestart(); this.systemService.systemRestart();
return '核心正在重启..'; return '核心正在重启..';
} }
@ -47,7 +47,7 @@ export class SystemWebController {
}) })
@UseGuards(TokenAuthGuard) @UseGuards(TokenAuthGuard)
@ApiBody({ type: WebServerDto }) @ApiBody({ type: WebServerDto })
async getRestartTime(@Param('token') token: string): Promise<string> { public async getRestartTime(@Param('token') token: string): Promise<string> {
return await this.systemService.getRestartTime(); return await this.systemService.getRestartTime();
} }
} }

View File

@ -15,7 +15,7 @@ export class SystemWebService {
/** /**
* *
*/ */
async systemRestart(): Promise<void> { public async systemRestart(): Promise<void> {
this.logger.debug(`有个小可爱正在请求重启核心..`); this.logger.debug(`有个小可爱正在请求重启核心..`);
await this.system.restart(); await this.system.restart();
} }
@ -23,7 +23,7 @@ export class SystemWebService {
/** /**
* *
*/ */
async getRestartTime(): Promise<string> { public async getRestartTime(): Promise<string> {
this.logger.debug(`有个小可爱想知道核心重启花了多久..`); this.logger.debug(`有个小可爱想知道核心重启花了多久..`);
const restartTimePath = path.join( const restartTimePath = path.join(
this.pathService.get('temp'), this.pathService.get('temp'),

View File

@ -46,7 +46,7 @@ export class WordsController {
@Post('getText') @Post('getText')
@ApiOperation({ summary: '获取随机文案' }) @ApiOperation({ summary: '获取随机文案' })
@ApiBody({ type: WordsDto }) @ApiBody({ type: WordsDto })
async getText(@Body() dto: WordsDto) { public async getText(@Body() dto: WordsDto) {
try { try {
const texts = await this.wordsService.loadWord(dto.type, dto.id); const texts = await this.wordsService.loadWord(dto.type, dto.id);
if (!texts || texts.length === 0) { if (!texts || texts.length === 0) {
@ -76,7 +76,7 @@ export class WordsController {
@ApiOperation({ summary: '重载某条文案' }) @ApiOperation({ summary: '重载某条文案' })
@UseGuards(TokenAuthGuard) @UseGuards(TokenAuthGuard)
@ApiBody({ type: WordsReloadDto }) @ApiBody({ type: WordsReloadDto })
async reloadWord(@Body() dto: WordsReloadDto) { public async reloadWord(@Body() dto: WordsReloadDto) {
try { try {
const success = await this.wordsService.reloadWord(dto.type, dto.id); const success = await this.wordsService.reloadWord(dto.type, dto.id);
if (success) { if (success) {

View File

@ -47,7 +47,7 @@ export class WordsService {
/** /**
* *
*/ */
async loadWord(type: string, name: string): Promise<string[] | null> { public async loadWord(type: string, name: string): Promise<string[] | null> {
const cacheKey = `${type}/${name}`; const cacheKey = `${type}/${name}`;
this.logger.log(`加载文案 ${cacheKey}..`); this.logger.log(`加载文案 ${cacheKey}..`);
if (this.wordCache[cacheKey]) return this.wordCache[cacheKey]; if (this.wordCache[cacheKey]) return this.wordCache[cacheKey];
@ -71,7 +71,7 @@ export class WordsService {
/** /**
* *
*/ */
async reloadWord(type: string, name: string): Promise<boolean> { public async reloadWord(type: string, name: string): Promise<boolean> {
const cacheKey = `${type}/${name}`; const cacheKey = `${type}/${name}`;
this.logger.log(`重载文案: ${cacheKey}..`); this.logger.log(`重载文案: ${cacheKey}..`);
const filePath = path.join(this.paths.get('words'), type, `${name}.json`); const filePath = path.join(this.paths.get('words'), type, `${name}.json`);

View File

@ -3,7 +3,7 @@ import { Controller, Get } from '@nestjs/common';
@Controller() @Controller()
export class RootController { export class RootController {
@Get() @Get()
getWelcome() { public getWelcome() {
return { return {
message: '欢迎使用晶灵核心', message: '欢迎使用晶灵核心',
}; };