diff --git a/scripts/qqbot-plugin/cli.ts b/scripts/qqbot-plugin/cli.ts index 3a89487..ab6767c 100644 --- a/scripts/qqbot-plugin/cli.ts +++ b/scripts/qqbot-plugin/cli.ts @@ -4,7 +4,7 @@ import * as path from 'path'; import { parseQqbotPluginManifest, type QqbotPluginManifest, -} from '../../src/modules/qqbot/plugin-platform/manifest'; +} from '../../src/modules/qqbot/plugin-platform/domain/manifest'; export type QqbotPluginCliCommand = | 'create' @@ -284,8 +284,8 @@ export const runQqbotPluginCli = async ( const command = argv[0] as QqbotPluginCliCommand | undefined; const resolvedOptions: Required = { cwd: options.cwd || process.cwd(), - stderr: options.stderr || ((message) => console.error(message)), - stdout: options.stdout || ((message) => console.log(message)), + stderr: options.stderr || ((message) => process.stderr.write(`${message}\n`)), + stdout: options.stdout || ((message) => process.stdout.write(`${message}\n`)), }; switch (command) { diff --git a/src/modules/qqbot/core/account/qqbot-account.service.ts b/src/modules/qqbot/core/application/account/qqbot-account.service.ts similarity index 97% rename from src/modules/qqbot/core/account/qqbot-account.service.ts rename to src/modules/qqbot/core/application/account/qqbot-account.service.ts index a1468a4..4b170af 100644 --- a/src/modules/qqbot/core/account/qqbot-account.service.ts +++ b/src/modules/qqbot/core/application/account/qqbot-account.service.ts @@ -9,26 +9,26 @@ import { ToolsService, } from '@/common'; import { AdminPasswordCryptoService } from '@/modules/admin/identity/auth/admin-password-crypto.service'; -import { QqbotAccountAbility } from './qqbot-account-ability.entity'; -import { QqbotAccount } from './qqbot-account.entity'; +import { QqbotAccountAbility } from '../../infrastructure/persistence/account/qqbot-account-ability.entity'; +import { QqbotAccount } from '../../infrastructure/persistence/account/qqbot-account.entity'; import type { QqbotAccountBodyDto, QqbotAccountQueryDto, QqbotAccountUpdateDto, -} from './qqbot-account.dto'; -import { QqbotAccountNapcat } from '../../napcat/infrastructure/persistence/qqbot-account-napcat.entity'; -import { QqbotNapcatContainer } from '../../napcat/infrastructure/persistence/qqbot-napcat-container.entity'; -import { QqbotNapcatContainerService } from '../../napcat/qqbot-napcat-container.service'; +} from '../../contract/account/qqbot-account.dto'; +import { QqbotAccountNapcat } from '@/modules/qqbot/napcat/infrastructure/persistence/qqbot-account-napcat.entity'; +import { QqbotNapcatContainer } from '@/modules/qqbot/napcat/infrastructure/persistence/qqbot-napcat-container.entity'; +import { QqbotNapcatContainerService } from '@/modules/qqbot/napcat/infrastructure/integration/container/qqbot-napcat-container.service'; import { QQBOT_DEFAULT_PAGE_NO, QQBOT_DEFAULT_PAGE_SIZE, -} from '../contract/qqbot.constants'; +} from '../../contract/qqbot.constants'; import type { QqbotAccountAbilityType, QqbotAccountListItem, QqbotConnectionRole, QqbotNapcatRuntimeStatusSnapshot, -} from '../contract/qqbot.types'; +} from '../../contract/qqbot.types'; const NAPCAT_RUNTIME_CHECK_TTL_MS = 30_000; const NAPCAT_AUTO_LOGIN_CLEANUP_FAILED_MESSAGE = diff --git a/src/modules/qqbot/core/command/qqbot-command-engine.service.ts b/src/modules/qqbot/core/application/command/qqbot-command-engine.service.ts similarity index 87% rename from src/modules/qqbot/core/command/qqbot-command-engine.service.ts rename to src/modules/qqbot/core/application/command/qqbot-command-engine.service.ts index 659febb..6bca780 100644 --- a/src/modules/qqbot/core/command/qqbot-command-engine.service.ts +++ b/src/modules/qqbot/core/application/command/qqbot-command-engine.service.ts @@ -1,10 +1,13 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { Inject, Injectable, Logger } from '@nestjs/common'; import { ToolsService } from '@/common'; -import { QqbotPluginRegistryService } from '@/modules/qqbot/plugin-platform/registry/qqbot-plugin-registry.service'; -import type { QqbotNormalizedMessage } from '../contract/qqbot.types'; +import { + QQBOT_PLUGIN_EXECUTION_PORT, + type QqbotPluginExecutionPort, +} from '../../domain/plugin-execution.port'; +import type { QqbotNormalizedMessage } from '../../contract/qqbot.types'; import { QqbotSendService } from '../send/qqbot-send.service'; -import type { QqbotCommandTestDto } from './qqbot-command.dto'; -import type { QqbotCommand } from './qqbot-command.entity'; +import type { QqbotCommandTestDto } from '../../contract/command/qqbot-command.dto'; +import type { QqbotCommand } from '../../infrastructure/persistence/command/qqbot-command.entity'; import { QqbotCommandParserService } from './qqbot-command-parser.service'; import { QqbotCommandService } from './qqbot-command.service'; import { QqbotReplyTemplateService } from './qqbot-reply-template.service'; @@ -16,7 +19,8 @@ export class QqbotCommandEngineService { constructor( private readonly commandParser: QqbotCommandParserService, private readonly commandService: QqbotCommandService, - private readonly pluginRegistry: QqbotPluginRegistryService, + @Inject(QQBOT_PLUGIN_EXECUTION_PORT) + private readonly pluginExecution: QqbotPluginExecutionPort, private readonly replyTemplate: QqbotReplyTemplateService, private readonly sendService: QqbotSendService, private readonly toolsService: ToolsService, @@ -32,16 +36,16 @@ export class QqbotCommandEngineService { await this.commandService.markHit(command); const input = this.mergeInput(command, matched.input); try { - const output = await this.pluginRegistry.execute( - command.pluginKey, - command.operationKey, - input, - { + const output = await this.pluginExecution.executeOperation({ + context: { args: matched.input, command, message, }, - ); + input, + operationKey: command.operationKey, + pluginKey: command.pluginKey, + }); const replyText = this.buildReplyText(command, input, output); if (replyText) { await this.sendService.sendText({ @@ -97,16 +101,16 @@ export class QqbotCommandEngineService { const input = this.mergeInput(command, matched.input); try { - const output = await this.pluginRegistry.execute( - command.pluginKey, - command.operationKey, - input, - { + const output = await this.pluginExecution.executeOperation({ + context: { args: matched.input, command, message, }, - ); + input, + operationKey: command.operationKey, + pluginKey: command.pluginKey, + }); const replyText = this.buildReplyText(command, input, output); return { command: this.commandService.toResponse(command), diff --git a/src/modules/qqbot/core/application/command/qqbot-command-parser.service.ts b/src/modules/qqbot/core/application/command/qqbot-command-parser.service.ts new file mode 100644 index 0000000..834ae41 --- /dev/null +++ b/src/modules/qqbot/core/application/command/qqbot-command-parser.service.ts @@ -0,0 +1,75 @@ +import { Injectable } from '@nestjs/common'; +import type { QqbotCommandMatchResult } from '../../contract/qqbot.types'; +import type { QqbotNormalizedMessage } from '../../contract/qqbot.types'; +import type { QqbotCommand } from '../../infrastructure/persistence/command/qqbot-command.entity'; + +@Injectable() +export class QqbotCommandParserService { + async match(command: QqbotCommand, message: QqbotNormalizedMessage) { + const source = `${message.messageText || ''}`.trim(); + if (!source) return null; + + const aliases = this.getAliases(command); + const prefixes = this.getPrefixes(command); + for (const alias of aliases) { + for (const prefix of prefixes) { + const commandText = `${prefix}${alias}`.trim(); + const rawArgs = this.pickArgs(source, commandText); + if (rawArgs === null) continue; + return { + alias, + input: this.parseRawInput(rawArgs), + matched: true, + rawArgs, + } satisfies QqbotCommandMatchResult; + } + } + return null; + } + + getAliases(command: QqbotCommand) { + return this.normalizeList(command.aliases, [command.code, command.name]); + } + + getPrefixes(command: QqbotCommand) { + return this.normalizeList(command.prefixes, ['/', '!', '!']); + } + + private pickArgs(source: string, commandText: string) { + if (!commandText) return null; + if (source === commandText) return ''; + if (source.startsWith(`${commandText} `)) { + return source.slice(commandText.length).trim(); + } + return null; + } + + private parseRawInput(rawArgs: string) { + const args = rawArgs ? rawArgs.split(/\s+/).filter(Boolean) : []; + return { + args, + raw: rawArgs, + text: rawArgs, + }; + } + + private normalizeList(value: string | undefined, fallback: string[]) { + const raw = `${value || ''}`.trim(); + const parsed = this.tryParseJsonArray(raw); + const source = parsed.length > 0 ? parsed : raw.split(','); + const list = [...source, ...fallback] + .map((item) => `${item || ''}`.trim()) + .filter(Boolean); + return [...new Set(list)]; + } + + private tryParseJsonArray(value: string) { + if (!value.startsWith('[')) return []; + try { + const parsed = JSON.parse(value); + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } + } +} diff --git a/src/modules/qqbot/core/command/qqbot-command.service.ts b/src/modules/qqbot/core/application/command/qqbot-command.service.ts similarity index 89% rename from src/modules/qqbot/core/command/qqbot-command.service.ts rename to src/modules/qqbot/core/application/command/qqbot-command.service.ts index a3d3494..51a34d6 100644 --- a/src/modules/qqbot/core/command/qqbot-command.service.ts +++ b/src/modules/qqbot/core/application/command/qqbot-command.service.ts @@ -1,27 +1,30 @@ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { InjectRepository } from '@nestjs/typeorm'; import { Not, Repository } from 'typeorm'; import { throwVbenError, ToolsService } from '@/common'; import { QqbotAccountService } from '../account/qqbot-account.service'; -import { QqbotPluginRegistryService } from '@/modules/qqbot/plugin-platform/registry/qqbot-plugin-registry.service'; +import { + QQBOT_PLUGIN_EXECUTION_PORT, + type QqbotPluginExecutionPort, +} from '../../domain/plugin-execution.port'; import { QQBOT_DEFAULT_PAGE_NO, QQBOT_DEFAULT_PAGE_SIZE, -} from '../contract/qqbot.constants'; +} from '../../contract/qqbot.constants'; import type { QqbotCommandParserType, QqbotNormalizedMessage, QqbotRuleTargetType, -} from '../contract/qqbot.types'; +} from '../../contract/qqbot.types'; import type { QqbotCommandBodyDto, QqbotCommandQueryDto, QqbotCommandUpdateDto, -} from './qqbot-command.dto'; -import { isWithinCooldown } from '../domain/qqbot-cooldown.policy'; -import { QqbotCommandLog } from './qqbot-command-log.entity'; -import { QqbotCommand } from './qqbot-command.entity'; +} from '../../contract/command/qqbot-command.dto'; +import { isWithinCooldown } from '../../domain/qqbot-cooldown.policy'; +import { QqbotCommandLog } from '../../infrastructure/persistence/command/qqbot-command-log.entity'; +import { QqbotCommand } from '../../infrastructure/persistence/command/qqbot-command.entity'; @Injectable() export class QqbotCommandService { @@ -31,7 +34,8 @@ export class QqbotCommandService { @InjectRepository(QqbotCommandLog) private readonly commandLogRepository: Repository, private readonly accountService: QqbotAccountService, - private readonly pluginRegistry: QqbotPluginRegistryService, + @Inject(QQBOT_PLUGIN_EXECUTION_PORT) + private readonly pluginExecution: QqbotPluginExecutionPort, private readonly toolsService: ToolsService, private readonly configService: ConfigService, ) {} @@ -216,7 +220,7 @@ export class QqbotCommandService { const operationKey = `${body.operationKey || ''}`.trim(); if (!code) throwVbenError('命令编码不能为空'); if (!body.name?.trim()) throwVbenError('命令名称不能为空'); - this.pluginRegistry.assertOperation(pluginKey, operationKey); + await this.assertPluginOperation(pluginKey, operationKey); return { aliases: this.stringifyList(body.aliases), @@ -248,6 +252,22 @@ export class QqbotCommandService { if (existed) throwVbenError(`命令编码已存在:${code}`); } + private async assertPluginOperation( + pluginKey?: string, + operationKey?: string, + ) { + if (!pluginKey || !operationKey) { + throwVbenError('请选择插件和插件能力'); + } + const operation = await this.pluginExecution.getOperationByCommand({ + operationKey, + pluginKey, + }); + if (!operation) { + throwVbenError(`QQBot 插件能力不存在:${pluginKey}.${operationKey}`); + } + } + private stringifyList(value: string[] | string | undefined, fallback = []) { const list = Array.isArray(value) ? value diff --git a/src/modules/qqbot/core/command/qqbot-reply-template.service.ts b/src/modules/qqbot/core/application/command/qqbot-reply-template.service.ts similarity index 100% rename from src/modules/qqbot/core/command/qqbot-reply-template.service.ts rename to src/modules/qqbot/core/application/command/qqbot-reply-template.service.ts diff --git a/src/modules/qqbot/core/config/qqbot-config.service.ts b/src/modules/qqbot/core/application/config/qqbot-config.service.ts similarity index 93% rename from src/modules/qqbot/core/config/qqbot-config.service.ts rename to src/modules/qqbot/core/application/config/qqbot-config.service.ts index abc1db4..a5a041b 100644 --- a/src/modules/qqbot/core/config/qqbot-config.service.ts +++ b/src/modules/qqbot/core/application/config/qqbot-config.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { QqbotConfig } from './qqbot-config.entity'; -import type { QqbotPermissionConfig } from '../contract/qqbot.types'; +import { QqbotConfig } from '../../infrastructure/persistence/config/qqbot-config.entity'; +import type { QqbotPermissionConfig } from '../../contract/qqbot.types'; const QQBOT_PERMISSION_CONFIG_KEYS = { allowlistEnabled: 'permission.allowlistEnabled', diff --git a/src/modules/qqbot/core/dashboard/qqbot-dashboard.service.ts b/src/modules/qqbot/core/application/dashboard/qqbot-dashboard.service.ts similarity index 73% rename from src/modules/qqbot/core/dashboard/qqbot-dashboard.service.ts rename to src/modules/qqbot/core/application/dashboard/qqbot-dashboard.service.ts index 50bedd7..fd32e1a 100644 --- a/src/modules/qqbot/core/dashboard/qqbot-dashboard.service.ts +++ b/src/modules/qqbot/core/application/dashboard/qqbot-dashboard.service.ts @@ -1,13 +1,13 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { QqbotAccount } from '../account/qqbot-account.entity'; -import { QqbotConversation } from '../message/qqbot-conversation.entity'; -import { QqbotMessage } from '../message/qqbot-message.entity'; -import { QqbotBusService } from '../mqtt/qqbot-bus.service'; -import { QqbotReverseWsService } from '../connection/qqbot-reverse-ws.service'; -import { QqbotRule } from '../rule/qqbot-rule.entity'; -import { QqbotSendLog } from '../send/qqbot-send-log.entity'; +import { QqbotBusService } from '../../infrastructure/integration/bus/qqbot-bus.service'; +import { QqbotReverseWsService } from '../../infrastructure/integration/connection/qqbot-reverse-ws.service'; +import { QqbotAccount } from '../../infrastructure/persistence/account/qqbot-account.entity'; +import { QqbotConversation } from '../../infrastructure/persistence/message/qqbot-conversation.entity'; +import { QqbotMessage } from '../../infrastructure/persistence/message/qqbot-message.entity'; +import { QqbotRule } from '../../infrastructure/persistence/rule/qqbot-rule.entity'; +import { QqbotSendLog } from '../../infrastructure/persistence/send/qqbot-send-log.entity'; @Injectable() export class QqbotDashboardService { diff --git a/src/modules/qqbot/core/dedupe/qqbot-dedupe.service.ts b/src/modules/qqbot/core/application/dedupe/qqbot-dedupe.service.ts similarity index 88% rename from src/modules/qqbot/core/dedupe/qqbot-dedupe.service.ts rename to src/modules/qqbot/core/application/dedupe/qqbot-dedupe.service.ts index 6f50222..cef9dd1 100644 --- a/src/modules/qqbot/core/dedupe/qqbot-dedupe.service.ts +++ b/src/modules/qqbot/core/application/dedupe/qqbot-dedupe.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { QqbotDedupe } from './qqbot-dedupe.entity'; +import { QqbotDedupe } from '../../infrastructure/persistence/dedupe/qqbot-dedupe.entity'; @Injectable() export class QqbotDedupeService { diff --git a/src/modules/qqbot/core/event/qqbot-event.service.ts b/src/modules/qqbot/core/application/event/qqbot-event.service.ts similarity index 91% rename from src/modules/qqbot/core/event/qqbot-event.service.ts rename to src/modules/qqbot/core/application/event/qqbot-event.service.ts index a35621b..c66fbda 100644 --- a/src/modules/qqbot/core/event/qqbot-event.service.ts +++ b/src/modules/qqbot/core/application/event/qqbot-event.service.ts @@ -4,17 +4,17 @@ import { SystemNoticePublisher, ToolsService, } from '@/common'; -import { QQBOT_MQTT_TOPICS } from '../contract/qqbot.constants'; +import { QQBOT_MQTT_TOPICS } from '../../contract/qqbot.constants'; import { QqbotDedupeService } from '../dedupe/qqbot-dedupe.service'; import { QqbotMessageService } from '../message/qqbot-message.service'; -import { QqbotBusService } from '../mqtt/qqbot-bus.service'; -import type { QqbotOneBotEvent } from '../contract/qqbot.types'; +import { QqbotBusService } from '../../infrastructure/integration/bus/qqbot-bus.service'; +import type { QqbotOneBotEvent } from '../../contract/qqbot.types'; import { buildDedupeKey, getOneBotOfflineReason, isOneBotMessageEvent, normalizeOneBotMessage, -} from './qqbot-event-normalizer'; +} from '../../domain/event/qqbot-event-normalizer'; import { QqbotRuleEngineService } from '../rule/qqbot-rule-engine.service'; import { QqbotAccountService } from '../account/qqbot-account.service'; diff --git a/src/modules/qqbot/core/message/qqbot-message.service.ts b/src/modules/qqbot/core/application/message/qqbot-message.service.ts similarity index 93% rename from src/modules/qqbot/core/message/qqbot-message.service.ts rename to src/modules/qqbot/core/application/message/qqbot-message.service.ts index ffae476..56572d2 100644 --- a/src/modules/qqbot/core/message/qqbot-message.service.ts +++ b/src/modules/qqbot/core/application/message/qqbot-message.service.ts @@ -2,17 +2,20 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { ToolsService } from '@/common'; -import { QqbotConversation } from './qqbot-conversation.entity'; -import { QqbotMessage } from './qqbot-message.entity'; +import { QqbotConversation } from '../../infrastructure/persistence/message/qqbot-conversation.entity'; +import { QqbotMessage } from '../../infrastructure/persistence/message/qqbot-message.entity'; import type { QqbotConversationQueryDto, QqbotMessageQueryDto, -} from './qqbot-message.dto'; -import type { QqbotMessageType, QqbotNormalizedMessage } from '../contract/qqbot.types'; +} from '../../contract/message/qqbot-message.dto'; +import type { + QqbotMessageType, + QqbotNormalizedMessage, +} from '../../contract/qqbot.types'; import { QQBOT_DEFAULT_PAGE_NO, QQBOT_DEFAULT_PAGE_SIZE, -} from '../contract/qqbot.constants'; +} from '../../contract/qqbot.constants'; @Injectable() export class QqbotMessageService { diff --git a/src/modules/qqbot/core/permission/qqbot-permission.service.ts b/src/modules/qqbot/core/application/permission/qqbot-permission.service.ts similarity index 95% rename from src/modules/qqbot/core/permission/qqbot-permission.service.ts rename to src/modules/qqbot/core/application/permission/qqbot-permission.service.ts index 9fd95c8..6abe3e3 100644 --- a/src/modules/qqbot/core/permission/qqbot-permission.service.ts +++ b/src/modules/qqbot/core/application/permission/qqbot-permission.service.ts @@ -2,24 +2,24 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Brackets, Repository } from 'typeorm'; import { throwVbenError, ToolsService } from '@/common'; -import { QqbotAllowlist } from './qqbot-allowlist.entity'; -import { QqbotBlocklist } from './qqbot-blocklist.entity'; +import { QqbotAllowlist } from '../../infrastructure/persistence/permission/qqbot-allowlist.entity'; +import { QqbotBlocklist } from '../../infrastructure/persistence/permission/qqbot-blocklist.entity'; import type { QqbotPermissionBodyDto, QqbotPermissionConfigDto, QqbotPermissionQueryDto, QqbotPermissionUpdateDto, -} from './qqbot-permission.dto'; +} from '../../contract/permission/qqbot-permission.dto'; import { QqbotConfigService } from '../config/qqbot-config.service'; import { QQBOT_DEFAULT_PAGE_NO, QQBOT_DEFAULT_PAGE_SIZE, -} from '../contract/qqbot.constants'; +} from '../../contract/qqbot.constants'; import type { QqbotNormalizedMessage, QqbotPermissionEntity, QqbotPermissionKind, -} from '../contract/qqbot.types'; +} from '../../contract/qqbot.types'; @Injectable() export class QqbotPermissionService { diff --git a/src/modules/qqbot/core/rule/qqbot-rule-engine.service.ts b/src/modules/qqbot/core/application/rule/qqbot-rule-engine.service.ts similarity index 80% rename from src/modules/qqbot/core/rule/qqbot-rule-engine.service.ts rename to src/modules/qqbot/core/application/rule/qqbot-rule-engine.service.ts index 9fe4e8e..80684d5 100644 --- a/src/modules/qqbot/core/rule/qqbot-rule-engine.service.ts +++ b/src/modules/qqbot/core/application/rule/qqbot-rule-engine.service.ts @@ -1,9 +1,12 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { Inject, Injectable, Logger } from '@nestjs/common'; import { ToolsService } from '@/common'; -import type { QqbotNormalizedMessage } from '../contract/qqbot.types'; +import { + QQBOT_PLUGIN_EXECUTION_PORT, + type QqbotPluginExecutionPort, +} from '../../domain/plugin-execution.port'; +import type { QqbotNormalizedMessage } from '../../contract/qqbot.types'; import { QqbotCommandEngineService } from '../command/qqbot-command-engine.service'; import { QqbotPermissionService } from '../permission/qqbot-permission.service'; -import { QqbotRepeaterPluginService } from '@/modules/qqbot/plugins/repeater/qqbot-repeater.plugin'; import { QqbotSendService } from '../send/qqbot-send.service'; import { QqbotRuleService } from './qqbot-rule.service'; @@ -14,7 +17,8 @@ export class QqbotRuleEngineService { constructor( private readonly commandEngineService: QqbotCommandEngineService, private readonly permissionService: QqbotPermissionService, - private readonly repeaterPluginService: QqbotRepeaterPluginService, + @Inject(QQBOT_PLUGIN_EXECUTION_PORT) + private readonly pluginExecution: QqbotPluginExecutionPort, private readonly ruleService: QqbotRuleService, private readonly sendService: QqbotSendService, private readonly toolsService: ToolsService, @@ -49,6 +53,9 @@ export class QqbotRuleEngineService { return; } - await this.repeaterPluginService.handleMessage(message); + await this.pluginExecution.dispatchEvent({ + eventKey: 'message', + message, + }); } } diff --git a/src/modules/qqbot/core/rule/qqbot-rule.service.ts b/src/modules/qqbot/core/application/rule/qqbot-rule.service.ts similarity index 95% rename from src/modules/qqbot/core/rule/qqbot-rule.service.ts rename to src/modules/qqbot/core/application/rule/qqbot-rule.service.ts index 0b728ff..035b4be 100644 --- a/src/modules/qqbot/core/rule/qqbot-rule.service.ts +++ b/src/modules/qqbot/core/application/rule/qqbot-rule.service.ts @@ -4,22 +4,22 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { throwVbenError, ToolsService } from '@/common'; import { QqbotAccountService } from '../account/qqbot-account.service'; -import { QqbotRule } from './qqbot-rule.entity'; +import { QqbotRule } from '../../infrastructure/persistence/rule/qqbot-rule.entity'; import type { QqbotRuleBodyDto, QqbotRuleQueryDto, QqbotRuleUpdateDto, -} from './qqbot-rule.dto'; +} from '../../contract/rule/qqbot-rule.dto'; import type { QqbotNormalizedMessage, QqbotRuleMatchType, QqbotRuleTargetType, -} from '../contract/qqbot.types'; +} from '../../contract/qqbot.types'; import { QQBOT_DEFAULT_PAGE_NO, QQBOT_DEFAULT_PAGE_SIZE, -} from '../contract/qqbot.constants'; -import { isWithinCooldown } from '../domain/qqbot-cooldown.policy'; +} from '../../contract/qqbot.constants'; +import { isWithinCooldown } from '../../domain/qqbot-cooldown.policy'; @Injectable() export class QqbotRuleService { diff --git a/src/modules/qqbot/core/send/qqbot-rate-limit.service.ts b/src/modules/qqbot/core/application/send/qqbot-rate-limit.service.ts similarity index 100% rename from src/modules/qqbot/core/send/qqbot-rate-limit.service.ts rename to src/modules/qqbot/core/application/send/qqbot-rate-limit.service.ts diff --git a/src/modules/qqbot/core/send/qqbot-send.service.ts b/src/modules/qqbot/core/application/send/qqbot-send.service.ts similarity index 93% rename from src/modules/qqbot/core/send/qqbot-send.service.ts rename to src/modules/qqbot/core/application/send/qqbot-send.service.ts index 13f8baf..a3f8396 100644 --- a/src/modules/qqbot/core/send/qqbot-send.service.ts +++ b/src/modules/qqbot/core/application/send/qqbot-send.service.ts @@ -4,24 +4,24 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { throwVbenError, ToolsService } from '@/common'; import { QqbotAccountService } from '../account/qqbot-account.service'; -import { QQBOT_MQTT_TOPICS } from '../contract/qqbot.constants'; -import { QqbotBusService } from '../mqtt/qqbot-bus.service'; +import { QQBOT_MQTT_TOPICS } from '../../contract/qqbot.constants'; +import { QqbotBusService } from '../../infrastructure/integration/bus/qqbot-bus.service'; import { QqbotMessageService } from '../message/qqbot-message.service'; import type { QqbotMessageType, QqbotReverseActionSender, -} from '../contract/qqbot.types'; +} from '../../contract/qqbot.types'; import { QQBOT_DEFAULT_PAGE_NO, QQBOT_DEFAULT_PAGE_SIZE, -} from '../contract/qqbot.constants'; +} from '../../contract/qqbot.constants'; import { QqbotRateLimitService } from './qqbot-rate-limit.service'; -import { QqbotSendLog } from './qqbot-send-log.entity'; +import { QqbotSendLog } from '../../infrastructure/persistence/send/qqbot-send-log.entity'; import type { QqbotSendGroupDto, QqbotSendLogQueryDto, QqbotSendPrivateDto, -} from './qqbot-send.dto'; +} from '../../contract/send/qqbot-send.dto'; @Injectable() export class QqbotSendService { @@ -186,7 +186,7 @@ export class QqbotSendService { private async getReverseWsService(): Promise { const { QqbotReverseWsService } = - await import('../connection/qqbot-reverse-ws.service'); + await import('../../infrastructure/integration/connection/qqbot-reverse-ws.service'); return this.moduleRef.get(QqbotReverseWsService, { strict: false, }); diff --git a/src/modules/qqbot/core/account/qqbot-account.controller.ts b/src/modules/qqbot/core/contract/account/qqbot-account.controller.ts similarity index 94% rename from src/modules/qqbot/core/account/qqbot-account.controller.ts rename to src/modules/qqbot/core/contract/account/qqbot-account.controller.ts index 8401535..ad29527 100644 --- a/src/modules/qqbot/core/account/qqbot-account.controller.ts +++ b/src/modules/qqbot/core/contract/account/qqbot-account.controller.ts @@ -19,9 +19,9 @@ import { QqbotAccountScanStatusDto, QqbotAccountUpdateDto, } from './qqbot-account.dto'; -import { QqbotAccountService } from './qqbot-account.service'; -import { QqbotNapcatLoginService } from '../../napcat/login/qqbot-napcat-login.service'; -import { QqbotReverseWsService } from '../connection/qqbot-reverse-ws.service'; +import { QqbotAccountService } from '../../application/account/qqbot-account.service'; +import { QqbotNapcatLoginService } from '@/modules/qqbot/napcat/application/login/qqbot-napcat-login.service'; +import { QqbotReverseWsService } from '../../infrastructure/integration/connection/qqbot-reverse-ws.service'; @ApiTags('QQBot - 账号连接') @Controller('qqbot/account') diff --git a/src/modules/qqbot/core/account/qqbot-account.dto.ts b/src/modules/qqbot/core/contract/account/qqbot-account.dto.ts similarity index 95% rename from src/modules/qqbot/core/account/qqbot-account.dto.ts rename to src/modules/qqbot/core/contract/account/qqbot-account.dto.ts index 8449267..bbcb8e9 100644 --- a/src/modules/qqbot/core/account/qqbot-account.dto.ts +++ b/src/modules/qqbot/core/contract/account/qqbot-account.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger'; -import type { QqbotConnectionMode } from '../contract/qqbot.types'; +import type { QqbotConnectionMode } from '../qqbot.types'; export class QqbotAccountBodyDto { @ApiPropertyOptional({ default: 'reverse-ws' }) diff --git a/src/modules/qqbot/core/command/qqbot-command.controller.ts b/src/modules/qqbot/core/contract/command/qqbot-command.controller.ts similarity index 92% rename from src/modules/qqbot/core/command/qqbot-command.controller.ts rename to src/modules/qqbot/core/contract/command/qqbot-command.controller.ts index 87d7d49..3679449 100644 --- a/src/modules/qqbot/core/command/qqbot-command.controller.ts +++ b/src/modules/qqbot/core/contract/command/qqbot-command.controller.ts @@ -17,8 +17,8 @@ import { QqbotCommandTestDto, QqbotCommandUpdateDto, } from './qqbot-command.dto'; -import { QqbotCommandEngineService } from './qqbot-command-engine.service'; -import { QqbotCommandService } from './qqbot-command.service'; +import { QqbotCommandEngineService } from '../../application/command/qqbot-command-engine.service'; +import { QqbotCommandService } from '../../application/command/qqbot-command.service'; @ApiTags('QQBot - 在线命令') @Controller('qqbot/command') diff --git a/src/modules/qqbot/core/command/qqbot-command.dto.ts b/src/modules/qqbot/core/contract/command/qqbot-command.dto.ts similarity index 98% rename from src/modules/qqbot/core/command/qqbot-command.dto.ts rename to src/modules/qqbot/core/contract/command/qqbot-command.dto.ts index 30e3dc3..c816feb 100644 --- a/src/modules/qqbot/core/command/qqbot-command.dto.ts +++ b/src/modules/qqbot/core/contract/command/qqbot-command.dto.ts @@ -5,7 +5,7 @@ import type { QqbotCommandParserType, QqbotMessageType, QqbotRuleTargetType, -} from '../contract/qqbot.types'; +} from '../qqbot.types'; export class QqbotCommandQueryDto implements KtPageQuery { @ApiPropertyOptional() diff --git a/src/modules/qqbot/core/dashboard/qqbot-dashboard.controller.ts b/src/modules/qqbot/core/contract/dashboard/qqbot-dashboard.controller.ts similarity index 86% rename from src/modules/qqbot/core/dashboard/qqbot-dashboard.controller.ts rename to src/modules/qqbot/core/contract/dashboard/qqbot-dashboard.controller.ts index bdae991..3b0b8c5 100644 --- a/src/modules/qqbot/core/dashboard/qqbot-dashboard.controller.ts +++ b/src/modules/qqbot/core/contract/dashboard/qqbot-dashboard.controller.ts @@ -2,7 +2,7 @@ import { Controller, Get, UseGuards } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { JwtAuthGuard } from '@/modules/admin/identity/auth/jwt-auth.guard'; import { vbenSuccess } from '@/common'; -import { QqbotDashboardService } from './qqbot-dashboard.service'; +import { QqbotDashboardService } from '../../application/dashboard/qqbot-dashboard.service'; @ApiTags('QQBot - 工作台') @Controller('qqbot/dashboard') diff --git a/src/modules/qqbot/core/message/qqbot-message.controller.ts b/src/modules/qqbot/core/contract/message/qqbot-message.controller.ts similarity index 91% rename from src/modules/qqbot/core/message/qqbot-message.controller.ts rename to src/modules/qqbot/core/contract/message/qqbot-message.controller.ts index 2ad4a8b..11dec5a 100644 --- a/src/modules/qqbot/core/message/qqbot-message.controller.ts +++ b/src/modules/qqbot/core/contract/message/qqbot-message.controller.ts @@ -6,7 +6,7 @@ import { QqbotConversationQueryDto, QqbotMessageQueryDto, } from './qqbot-message.dto'; -import { QqbotMessageService } from './qqbot-message.service'; +import { QqbotMessageService } from '../../application/message/qqbot-message.service'; @ApiTags('QQBot - 会话与消息') @Controller('qqbot') diff --git a/src/modules/qqbot/core/message/qqbot-message.dto.ts b/src/modules/qqbot/core/contract/message/qqbot-message.dto.ts similarity index 89% rename from src/modules/qqbot/core/message/qqbot-message.dto.ts rename to src/modules/qqbot/core/contract/message/qqbot-message.dto.ts index 3069755..70bc944 100644 --- a/src/modules/qqbot/core/message/qqbot-message.dto.ts +++ b/src/modules/qqbot/core/contract/message/qqbot-message.dto.ts @@ -1,5 +1,5 @@ import { ApiPropertyOptional } from '@nestjs/swagger'; -import type { QqbotMessageType } from '../contract/qqbot.types'; +import type { QqbotMessageType } from '../qqbot.types'; export class QqbotConversationQueryDto { @ApiPropertyOptional({ default: 1 }) diff --git a/src/modules/qqbot/core/permission/qqbot-permission.controller.ts b/src/modules/qqbot/core/contract/permission/qqbot-permission.controller.ts similarity index 97% rename from src/modules/qqbot/core/permission/qqbot-permission.controller.ts rename to src/modules/qqbot/core/contract/permission/qqbot-permission.controller.ts index 23948b7..577bc60 100644 --- a/src/modules/qqbot/core/permission/qqbot-permission.controller.ts +++ b/src/modules/qqbot/core/contract/permission/qqbot-permission.controller.ts @@ -17,7 +17,7 @@ import { QqbotPermissionQueryDto, QqbotPermissionUpdateDto, } from './qqbot-permission.dto'; -import { QqbotPermissionService } from './qqbot-permission.service'; +import { QqbotPermissionService } from '../../application/permission/qqbot-permission.service'; @ApiTags('QQBot - 权限名单') @Controller('qqbot/permission') diff --git a/src/modules/qqbot/core/permission/qqbot-permission.dto.ts b/src/modules/qqbot/core/contract/permission/qqbot-permission.dto.ts similarity index 94% rename from src/modules/qqbot/core/permission/qqbot-permission.dto.ts rename to src/modules/qqbot/core/contract/permission/qqbot-permission.dto.ts index 3a2c0ac..32ac95b 100644 --- a/src/modules/qqbot/core/permission/qqbot-permission.dto.ts +++ b/src/modules/qqbot/core/contract/permission/qqbot-permission.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger'; -import type { QqbotPermissionTargetType } from '../contract/qqbot.types'; +import type { QqbotPermissionTargetType } from '../qqbot.types'; export class QqbotPermissionConfigDto { @ApiPropertyOptional({ default: false }) diff --git a/src/modules/qqbot/core/qqbot-core.contract.ts b/src/modules/qqbot/core/contract/qqbot-core.contract.ts similarity index 100% rename from src/modules/qqbot/core/qqbot-core.contract.ts rename to src/modules/qqbot/core/contract/qqbot-core.contract.ts diff --git a/src/modules/qqbot/core/contract/qqbot.types.ts b/src/modules/qqbot/core/contract/qqbot.types.ts index f6a54c3..2fc01bb 100644 --- a/src/modules/qqbot/core/contract/qqbot.types.ts +++ b/src/modules/qqbot/core/contract/qqbot.types.ts @@ -1,8 +1,8 @@ import type { QrcodeLookupOptions } from '@/common/types'; -import type { QqbotAccount } from '../account/qqbot-account.entity'; -import type { QqbotCommand } from '../command/qqbot-command.entity'; -import type { QqbotAllowlist } from '../permission/qqbot-allowlist.entity'; -import type { QqbotBlocklist } from '../permission/qqbot-blocklist.entity'; +import type { QqbotAccount } from '../infrastructure/persistence/account/qqbot-account.entity'; +import type { QqbotCommand } from '../infrastructure/persistence/command/qqbot-command.entity'; +import type { QqbotAllowlist } from '../infrastructure/persistence/permission/qqbot-allowlist.entity'; +import type { QqbotBlocklist } from '../infrastructure/persistence/permission/qqbot-blocklist.entity'; export type { QrcodeLookupOptions } from '@/common/types'; @@ -354,14 +354,6 @@ export type QqbotReverseActionSender = { ) => Promise; }; -export type QqbotRepeaterConversationState = { - count: number; - lastRepeatedAt?: number; - lastText: string; - repeatedText: string; - updatedAt: number; -}; - export type QrcodeRefreshOptions = QrcodeLookupOptions & { fallbackStatus?: NapcatLoginStatus; }; diff --git a/src/modules/qqbot/core/rule/qqbot-rule.controller.ts b/src/modules/qqbot/core/contract/rule/qqbot-rule.controller.ts similarity index 96% rename from src/modules/qqbot/core/rule/qqbot-rule.controller.ts rename to src/modules/qqbot/core/contract/rule/qqbot-rule.controller.ts index 38dc4ab..0f0fd3b 100644 --- a/src/modules/qqbot/core/rule/qqbot-rule.controller.ts +++ b/src/modules/qqbot/core/contract/rule/qqbot-rule.controller.ts @@ -16,7 +16,7 @@ import { QqbotRuleQueryDto, QqbotRuleUpdateDto, } from './qqbot-rule.dto'; -import { QqbotRuleService } from './qqbot-rule.service'; +import { QqbotRuleService } from '../../application/rule/qqbot-rule.service'; @ApiTags('QQBot - 自动回复规则') @Controller('qqbot/rule') diff --git a/src/modules/qqbot/core/rule/qqbot-rule.dto.ts b/src/modules/qqbot/core/contract/rule/qqbot-rule.dto.ts similarity index 98% rename from src/modules/qqbot/core/rule/qqbot-rule.dto.ts rename to src/modules/qqbot/core/contract/rule/qqbot-rule.dto.ts index 0f33608..20f2a39 100644 --- a/src/modules/qqbot/core/rule/qqbot-rule.dto.ts +++ b/src/modules/qqbot/core/contract/rule/qqbot-rule.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger'; -import type { QqbotRuleMatchType, QqbotRuleTargetType } from '../contract/qqbot.types'; +import type { QqbotRuleMatchType, QqbotRuleTargetType } from '../qqbot.types'; export class QqbotRuleBodyDto { @ApiPropertyOptional() diff --git a/src/modules/qqbot/core/send/qqbot-send.controller.ts b/src/modules/qqbot/core/contract/send/qqbot-send.controller.ts similarity index 94% rename from src/modules/qqbot/core/send/qqbot-send.controller.ts rename to src/modules/qqbot/core/contract/send/qqbot-send.controller.ts index b4c2e0a..f16acca 100644 --- a/src/modules/qqbot/core/send/qqbot-send.controller.ts +++ b/src/modules/qqbot/core/contract/send/qqbot-send.controller.ts @@ -16,7 +16,7 @@ import { QqbotSendLogQueryDto, QqbotSendPrivateDto, } from './qqbot-send.dto'; -import { QqbotSendService } from './qqbot-send.service'; +import { QqbotSendService } from '../../application/send/qqbot-send.service'; @ApiTags('QQBot - 发送日志') @Controller('qqbot/send') diff --git a/src/modules/qqbot/core/send/qqbot-send.dto.ts b/src/modules/qqbot/core/contract/send/qqbot-send.dto.ts similarity index 90% rename from src/modules/qqbot/core/send/qqbot-send.dto.ts rename to src/modules/qqbot/core/contract/send/qqbot-send.dto.ts index b426b08..55efdb1 100644 --- a/src/modules/qqbot/core/send/qqbot-send.dto.ts +++ b/src/modules/qqbot/core/contract/send/qqbot-send.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import type { QqbotMessageType, QqbotSendStatus } from '../contract/qqbot.types'; +import type { QqbotMessageType, QqbotSendStatus } from '../qqbot.types'; export class QqbotSendPrivateDto { @ApiPropertyOptional() diff --git a/src/modules/qqbot/core/event/qqbot-event-normalizer.ts b/src/modules/qqbot/core/domain/event/qqbot-event-normalizer.ts similarity index 99% rename from src/modules/qqbot/core/event/qqbot-event-normalizer.ts rename to src/modules/qqbot/core/domain/event/qqbot-event-normalizer.ts index ae7090d..67a268f 100644 --- a/src/modules/qqbot/core/event/qqbot-event-normalizer.ts +++ b/src/modules/qqbot/core/domain/event/qqbot-event-normalizer.ts @@ -3,7 +3,7 @@ import type { QqbotMessageType, QqbotNormalizedMessage, QqbotOneBotEvent, -} from '../contract/qqbot.types'; +} from '../../contract/qqbot.types'; export function isOneBotMessageEvent( payload: QqbotOneBotEvent, diff --git a/src/modules/qqbot/core/domain/plugin-execution.port.ts b/src/modules/qqbot/core/domain/plugin-execution.port.ts new file mode 100644 index 0000000..294d0e0 --- /dev/null +++ b/src/modules/qqbot/core/domain/plugin-execution.port.ts @@ -0,0 +1,40 @@ +import type { + QqbotNormalizedMessage, + QqbotPluginOperationSummary, +} from '../contract/qqbot.types'; + +export const QQBOT_PLUGIN_EXECUTION_PORT = Symbol( + 'QQBOT_PLUGIN_EXECUTION_PORT', +); + +export type QqbotPluginOperationLookup = { + operationKey?: string; + pluginKey?: string; +}; + +export type QqbotPluginExecutionInput = { + context?: Record; + input: Record; + operationKey: string; + pluginKey: string; +}; + +export type QqbotPluginEventDispatchInput = { + eventKey: 'message'; + message: QqbotNormalizedMessage; +}; + +export type QqbotPluginOperationListContext = { + selfId?: string; +}; + +export interface QqbotPluginExecutionPort { + dispatchEvent(input: QqbotPluginEventDispatchInput): Promise; + executeOperation(input: QqbotPluginExecutionInput): Promise; + getOperationByCommand( + command: QqbotPluginOperationLookup, + ): Promise; + listActiveOperations( + context?: QqbotPluginOperationListContext, + ): Promise; +} diff --git a/src/modules/qqbot/core/mqtt/qqbot-bus.service.ts b/src/modules/qqbot/core/infrastructure/integration/bus/qqbot-bus.service.ts similarity index 97% rename from src/modules/qqbot/core/mqtt/qqbot-bus.service.ts rename to src/modules/qqbot/core/infrastructure/integration/bus/qqbot-bus.service.ts index da2ef41..1eabd35 100644 --- a/src/modules/qqbot/core/mqtt/qqbot-bus.service.ts +++ b/src/modules/qqbot/core/infrastructure/integration/bus/qqbot-bus.service.ts @@ -8,7 +8,7 @@ import { import { ConfigService } from '@nestjs/config'; import * as mqtt from 'mqtt'; import type { MqttClient } from 'mqtt'; -import type { QqbotBusHandler } from '../contract/qqbot.types'; +import type { QqbotBusHandler } from '../../../contract/qqbot.types'; @Injectable() export class QqbotBusService implements OnModuleInit, OnModuleDestroy { diff --git a/src/modules/qqbot/core/connection/qqbot-reverse-ws.service.ts b/src/modules/qqbot/core/infrastructure/integration/connection/qqbot-reverse-ws.service.ts similarity index 96% rename from src/modules/qqbot/core/connection/qqbot-reverse-ws.service.ts rename to src/modules/qqbot/core/infrastructure/integration/connection/qqbot-reverse-ws.service.ts index 4e4c249..e5d9002 100644 --- a/src/modules/qqbot/core/connection/qqbot-reverse-ws.service.ts +++ b/src/modules/qqbot/core/infrastructure/integration/connection/qqbot-reverse-ws.service.ts @@ -10,16 +10,19 @@ import { ConfigService } from '@nestjs/config'; import { HttpAdapterHost, ModuleRef } from '@nestjs/core'; import { WebSocket, WebSocketServer } from 'ws'; import { ToolsService } from '@/common'; -import { QQBOT_MQTT_TOPICS, QQBOT_REVERSE_WS_PATH } from '../contract/qqbot.constants'; +import { + QQBOT_MQTT_TOPICS, + QQBOT_REVERSE_WS_PATH, +} from '../../../contract/qqbot.constants'; import type { QqbotConnectionRole, QqbotOneBotActionResponse, QqbotOneBotEvent, QqbotPendingAction, -} from '../contract/qqbot.types'; -import { QqbotAccountService } from '../account/qqbot-account.service'; -import { QqbotEventService } from '../event/qqbot-event.service'; -import { QqbotBusService } from '../mqtt/qqbot-bus.service'; +} from '../../../contract/qqbot.types'; +import { QqbotAccountService } from '../../../application/account/qqbot-account.service'; +import { QqbotEventService } from '../../../application/event/qqbot-event.service'; +import { QqbotBusService } from '../bus/qqbot-bus.service'; @Injectable() export class QqbotReverseWsService diff --git a/src/modules/qqbot/core/account/qqbot-account-ability.entity.ts b/src/modules/qqbot/core/infrastructure/persistence/account/qqbot-account-ability.entity.ts similarity index 93% rename from src/modules/qqbot/core/account/qqbot-account-ability.entity.ts rename to src/modules/qqbot/core/infrastructure/persistence/account/qqbot-account-ability.entity.ts index 6dd5539..8754a55 100644 --- a/src/modules/qqbot/core/account/qqbot-account-ability.entity.ts +++ b/src/modules/qqbot/core/infrastructure/persistence/account/qqbot-account-ability.entity.ts @@ -5,7 +5,7 @@ import { KtDateTime, KtUpdateDateColumn, } from '@/common'; -import type { QqbotAccountAbilityType } from '../contract/qqbot.types'; +import type { QqbotAccountAbilityType } from '../../../contract/qqbot.types'; @Entity('qqbot_account_ability') @Index('uk_qqbot_account_ability', ['accountId', 'abilityType', 'abilityKey'], { diff --git a/src/modules/qqbot/core/account/qqbot-account.entity.ts b/src/modules/qqbot/core/infrastructure/persistence/account/qqbot-account.entity.ts similarity index 98% rename from src/modules/qqbot/core/account/qqbot-account.entity.ts rename to src/modules/qqbot/core/infrastructure/persistence/account/qqbot-account.entity.ts index 98373e4..0adb5ed 100644 --- a/src/modules/qqbot/core/account/qqbot-account.entity.ts +++ b/src/modules/qqbot/core/infrastructure/persistence/account/qqbot-account.entity.ts @@ -10,7 +10,7 @@ import type { QqbotConnectionMode, QqbotConnectionRole, QqbotConnectionStatus, -} from '../contract/qqbot.types'; +} from '../../../contract/qqbot.types'; @Entity('qqbot_account') export class QqbotAccount { diff --git a/src/modules/qqbot/core/command/qqbot-command-log.entity.ts b/src/modules/qqbot/core/infrastructure/persistence/command/qqbot-command-log.entity.ts similarity index 91% rename from src/modules/qqbot/core/command/qqbot-command-log.entity.ts rename to src/modules/qqbot/core/infrastructure/persistence/command/qqbot-command-log.entity.ts index f26a99c..0b59810 100644 --- a/src/modules/qqbot/core/command/qqbot-command-log.entity.ts +++ b/src/modules/qqbot/core/infrastructure/persistence/command/qqbot-command-log.entity.ts @@ -5,8 +5,8 @@ import { KtDateTime, KtUpdateDateColumn, } from '@/common'; -import type { QqbotMessageType } from '../contract/qqbot.types'; -import type { QqbotCommandLogStatus } from '../contract/qqbot.types'; +import type { QqbotMessageType } from '../../../contract/qqbot.types'; +import type { QqbotCommandLogStatus } from '../../../contract/qqbot.types'; @Entity('qqbot_command_log') export class QqbotCommandLog { diff --git a/src/modules/qqbot/core/command/qqbot-command.entity.ts b/src/modules/qqbot/core/infrastructure/persistence/command/qqbot-command.entity.ts similarity index 98% rename from src/modules/qqbot/core/command/qqbot-command.entity.ts rename to src/modules/qqbot/core/infrastructure/persistence/command/qqbot-command.entity.ts index cb1407d..b1fd219 100644 --- a/src/modules/qqbot/core/command/qqbot-command.entity.ts +++ b/src/modules/qqbot/core/infrastructure/persistence/command/qqbot-command.entity.ts @@ -9,7 +9,7 @@ import { import type { QqbotCommandParserType, QqbotRuleTargetType, -} from '../contract/qqbot.types'; +} from '../../../contract/qqbot.types'; @Entity('qqbot_command') export class QqbotCommand { diff --git a/src/modules/qqbot/core/config/qqbot-config.entity.ts b/src/modules/qqbot/core/infrastructure/persistence/config/qqbot-config.entity.ts similarity index 100% rename from src/modules/qqbot/core/config/qqbot-config.entity.ts rename to src/modules/qqbot/core/infrastructure/persistence/config/qqbot-config.entity.ts diff --git a/src/modules/qqbot/core/dedupe/qqbot-dedupe.entity.ts b/src/modules/qqbot/core/infrastructure/persistence/dedupe/qqbot-dedupe.entity.ts similarity index 100% rename from src/modules/qqbot/core/dedupe/qqbot-dedupe.entity.ts rename to src/modules/qqbot/core/infrastructure/persistence/dedupe/qqbot-dedupe.entity.ts diff --git a/src/modules/qqbot/core/message/qqbot-conversation.entity.ts b/src/modules/qqbot/core/infrastructure/persistence/message/qqbot-conversation.entity.ts similarity index 95% rename from src/modules/qqbot/core/message/qqbot-conversation.entity.ts rename to src/modules/qqbot/core/infrastructure/persistence/message/qqbot-conversation.entity.ts index 2249a47..abe9287 100644 --- a/src/modules/qqbot/core/message/qqbot-conversation.entity.ts +++ b/src/modules/qqbot/core/infrastructure/persistence/message/qqbot-conversation.entity.ts @@ -6,7 +6,7 @@ import { KtDateTimeColumn, KtUpdateDateColumn, } from '@/common'; -import type { QqbotMessageType } from '../contract/qqbot.types'; +import type { QqbotMessageType } from '../../../contract/qqbot.types'; @Entity('qqbot_conversation') @Index('idx_qqbot_conversation_target', ['selfId', 'targetType', 'targetId']) diff --git a/src/modules/qqbot/core/message/qqbot-message.entity.ts b/src/modules/qqbot/core/infrastructure/persistence/message/qqbot-message.entity.ts similarity index 98% rename from src/modules/qqbot/core/message/qqbot-message.entity.ts rename to src/modules/qqbot/core/infrastructure/persistence/message/qqbot-message.entity.ts index 7378d41..831518d 100644 --- a/src/modules/qqbot/core/message/qqbot-message.entity.ts +++ b/src/modules/qqbot/core/infrastructure/persistence/message/qqbot-message.entity.ts @@ -6,7 +6,7 @@ import { KtDateTimeColumn, KtUpdateDateColumn, } from '@/common'; -import type { QqbotMessageDirection, QqbotMessageType } from '../contract/qqbot.types'; +import type { QqbotMessageDirection, QqbotMessageType } from '../../../contract/qqbot.types'; @Entity('qqbot_message') @Index('idx_qqbot_message_self_message', ['selfId', 'messageId']) diff --git a/src/modules/qqbot/core/permission/qqbot-allowlist.entity.ts b/src/modules/qqbot/core/infrastructure/persistence/permission/qqbot-allowlist.entity.ts similarity index 93% rename from src/modules/qqbot/core/permission/qqbot-allowlist.entity.ts rename to src/modules/qqbot/core/infrastructure/persistence/permission/qqbot-allowlist.entity.ts index 8883d5a..bee6010 100644 --- a/src/modules/qqbot/core/permission/qqbot-allowlist.entity.ts +++ b/src/modules/qqbot/core/infrastructure/persistence/permission/qqbot-allowlist.entity.ts @@ -5,7 +5,7 @@ import { KtDateTime, KtUpdateDateColumn, } from '@/common'; -import type { QqbotPermissionTargetType } from '../contract/qqbot.types'; +import type { QqbotPermissionTargetType } from '../../../contract/qqbot.types'; @Entity('qqbot_allowlist') export class QqbotAllowlist { diff --git a/src/modules/qqbot/core/permission/qqbot-blocklist.entity.ts b/src/modules/qqbot/core/infrastructure/persistence/permission/qqbot-blocklist.entity.ts similarity index 93% rename from src/modules/qqbot/core/permission/qqbot-blocklist.entity.ts rename to src/modules/qqbot/core/infrastructure/persistence/permission/qqbot-blocklist.entity.ts index 20f3e8f..23664d0 100644 --- a/src/modules/qqbot/core/permission/qqbot-blocklist.entity.ts +++ b/src/modules/qqbot/core/infrastructure/persistence/permission/qqbot-blocklist.entity.ts @@ -5,7 +5,7 @@ import { KtDateTime, KtUpdateDateColumn, } from '@/common'; -import type { QqbotPermissionTargetType } from '../contract/qqbot.types'; +import type { QqbotPermissionTargetType } from '../../../contract/qqbot.types'; @Entity('qqbot_blocklist') export class QqbotBlocklist { diff --git a/src/modules/qqbot/core/rule/qqbot-rule.entity.ts b/src/modules/qqbot/core/infrastructure/persistence/rule/qqbot-rule.entity.ts similarity index 97% rename from src/modules/qqbot/core/rule/qqbot-rule.entity.ts rename to src/modules/qqbot/core/infrastructure/persistence/rule/qqbot-rule.entity.ts index c6911cf..c26c134 100644 --- a/src/modules/qqbot/core/rule/qqbot-rule.entity.ts +++ b/src/modules/qqbot/core/infrastructure/persistence/rule/qqbot-rule.entity.ts @@ -6,7 +6,7 @@ import { KtDateTimeColumn, KtUpdateDateColumn, } from '@/common'; -import type { QqbotRuleMatchType, QqbotRuleTargetType } from '../contract/qqbot.types'; +import type { QqbotRuleMatchType, QqbotRuleTargetType } from '../../../contract/qqbot.types'; @Entity('qqbot_rule') export class QqbotRule { diff --git a/src/modules/qqbot/core/send/qqbot-send-log.entity.ts b/src/modules/qqbot/core/infrastructure/persistence/send/qqbot-send-log.entity.ts similarity index 94% rename from src/modules/qqbot/core/send/qqbot-send-log.entity.ts rename to src/modules/qqbot/core/infrastructure/persistence/send/qqbot-send-log.entity.ts index c79d74d..e2b5ce2 100644 --- a/src/modules/qqbot/core/send/qqbot-send-log.entity.ts +++ b/src/modules/qqbot/core/infrastructure/persistence/send/qqbot-send-log.entity.ts @@ -5,7 +5,7 @@ import { KtDateTime, KtUpdateDateColumn, } from '@/common'; -import type { QqbotMessageType, QqbotSendStatus } from '../contract/qqbot.types'; +import type { QqbotMessageType, QqbotSendStatus } from '../../../contract/qqbot.types'; @Entity('qqbot_send_log') @Index('idx_qqbot_send_log_target', ['selfId', 'targetType', 'targetId']) diff --git a/src/modules/qqbot/core/qqbot-core.module.ts b/src/modules/qqbot/core/qqbot-core.module.ts index 758ffc6..897b0ae 100644 --- a/src/modules/qqbot/core/qqbot-core.module.ts +++ b/src/modules/qqbot/core/qqbot-core.module.ts @@ -1,66 +1,54 @@ -import { Module } from '@nestjs/common'; +import { forwardRef, Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AdminAuthGuardModule } from '@/modules/admin/identity/auth/admin-auth-guard.module'; import { DictModule } from '@/modules/admin/platform-config/dict/dict.module'; -import { QqbotAccountAbility } from '@/modules/qqbot/core/account/qqbot-account-ability.entity'; -import { QqbotAccountController } from '@/modules/qqbot/core/account/qqbot-account.controller'; -import { QqbotAccount } from '@/modules/qqbot/core/account/qqbot-account.entity'; -import { QqbotAccountService } from '@/modules/qqbot/core/account/qqbot-account.service'; -import { QqbotNapcatLoginService } from '@/modules/qqbot/napcat/login/qqbot-napcat-login.service'; -import { QqbotNapcatWatchdogService } from '@/modules/qqbot/napcat/login/qqbot-napcat-watchdog.service'; -import { QqbotCommandController } from '@/modules/qqbot/core/command/qqbot-command.controller'; -import { QqbotCommand } from '@/modules/qqbot/core/command/qqbot-command.entity'; -import { QqbotCommandEngineService } from '@/modules/qqbot/core/command/qqbot-command-engine.service'; -import { QqbotCommandLog } from '@/modules/qqbot/core/command/qqbot-command-log.entity'; -import { QqbotCommandParserService } from '@/modules/qqbot/core/command/qqbot-command-parser.service'; -import { QqbotCommandService } from '@/modules/qqbot/core/command/qqbot-command.service'; -import { QqbotReplyTemplateService } from '@/modules/qqbot/core/command/qqbot-reply-template.service'; -import { QqbotReverseWsService } from '@/modules/qqbot/core/connection/qqbot-reverse-ws.service'; -import { QqbotConfig } from '@/modules/qqbot/core/config/qqbot-config.entity'; -import { QqbotConfigService } from '@/modules/qqbot/core/config/qqbot-config.service'; -import { QqbotDashboardController } from '@/modules/qqbot/core/dashboard/qqbot-dashboard.controller'; -import { QqbotDashboardService } from '@/modules/qqbot/core/dashboard/qqbot-dashboard.service'; -import { QqbotDedupe } from '@/modules/qqbot/core/dedupe/qqbot-dedupe.entity'; -import { QqbotDedupeService } from '@/modules/qqbot/core/dedupe/qqbot-dedupe.service'; -import { QqbotEventService } from '@/modules/qqbot/core/event/qqbot-event.service'; -import { QqbotPluginHttpClientService } from '@/modules/qqbot/plugin-platform/sdk'; -import { NapcatDeviceIdentity } from '@/modules/qqbot/napcat/device/napcat-device-identity.entity'; -import { NapcatDeviceIdentityService } from '@/modules/qqbot/napcat/device/napcat-device-identity.service'; -import { QqbotConversation } from '@/modules/qqbot/core/message/qqbot-conversation.entity'; -import { QqbotMessageController } from '@/modules/qqbot/core/message/qqbot-message.controller'; -import { QqbotMessage } from '@/modules/qqbot/core/message/qqbot-message.entity'; -import { QqbotMessageService } from '@/modules/qqbot/core/message/qqbot-message.service'; -import { QqbotBusService } from '@/modules/qqbot/core/mqtt/qqbot-bus.service'; +import { QqbotPluginPlatformModule } from '@/modules/qqbot/plugin-platform/plugin-platform.module'; +import { QqbotAccountAbility } from '@/modules/qqbot/core/infrastructure/persistence/account/qqbot-account-ability.entity'; +import { QqbotAccountController } from '@/modules/qqbot/core/contract/account/qqbot-account.controller'; +import { QqbotAccount } from '@/modules/qqbot/core/infrastructure/persistence/account/qqbot-account.entity'; +import { QqbotAccountService } from '@/modules/qqbot/core/application/account/qqbot-account.service'; +import { QqbotNapcatLoginService } from '@/modules/qqbot/napcat/application/login/qqbot-napcat-login.service'; +import { QqbotNapcatWatchdogService } from '@/modules/qqbot/napcat/application/login/qqbot-napcat-watchdog.service'; +import { QqbotCommandController } from '@/modules/qqbot/core/contract/command/qqbot-command.controller'; +import { QqbotCommand } from '@/modules/qqbot/core/infrastructure/persistence/command/qqbot-command.entity'; +import { QqbotCommandEngineService } from '@/modules/qqbot/core/application/command/qqbot-command-engine.service'; +import { QqbotCommandLog } from '@/modules/qqbot/core/infrastructure/persistence/command/qqbot-command-log.entity'; +import { QqbotCommandParserService } from '@/modules/qqbot/core/application/command/qqbot-command-parser.service'; +import { QqbotCommandService } from '@/modules/qqbot/core/application/command/qqbot-command.service'; +import { QqbotReplyTemplateService } from '@/modules/qqbot/core/application/command/qqbot-reply-template.service'; +import { QqbotReverseWsService } from '@/modules/qqbot/core/infrastructure/integration/connection/qqbot-reverse-ws.service'; +import { QqbotConfig } from '@/modules/qqbot/core/infrastructure/persistence/config/qqbot-config.entity'; +import { QqbotConfigService } from '@/modules/qqbot/core/application/config/qqbot-config.service'; +import { QqbotDashboardController } from '@/modules/qqbot/core/contract/dashboard/qqbot-dashboard.controller'; +import { QqbotDashboardService } from '@/modules/qqbot/core/application/dashboard/qqbot-dashboard.service'; +import { QqbotDedupe } from '@/modules/qqbot/core/infrastructure/persistence/dedupe/qqbot-dedupe.entity'; +import { QqbotDedupeService } from '@/modules/qqbot/core/application/dedupe/qqbot-dedupe.service'; +import { QqbotEventService } from '@/modules/qqbot/core/application/event/qqbot-event.service'; +import { NapcatDeviceIdentity } from '@/modules/qqbot/napcat/infrastructure/persistence/napcat-device-identity.entity'; +import { NapcatDeviceIdentityService } from '@/modules/qqbot/napcat/infrastructure/integration/device/napcat-device-identity.service'; +import { QqbotConversation } from '@/modules/qqbot/core/infrastructure/persistence/message/qqbot-conversation.entity'; +import { QqbotMessageController } from '@/modules/qqbot/core/contract/message/qqbot-message.controller'; +import { QqbotMessage } from '@/modules/qqbot/core/infrastructure/persistence/message/qqbot-message.entity'; +import { QqbotMessageService } from '@/modules/qqbot/core/application/message/qqbot-message.service'; +import { QqbotBusService } from '@/modules/qqbot/core/infrastructure/integration/bus/qqbot-bus.service'; import { QqbotAccountNapcat } from '@/modules/qqbot/napcat/infrastructure/persistence/qqbot-account-napcat.entity'; import { QqbotNapcatContainer } from '@/modules/qqbot/napcat/infrastructure/persistence/qqbot-napcat-container.entity'; -import { QqbotNapcatContainerService } from '@/modules/qqbot/napcat/qqbot-napcat-container.service'; -import { QqbotAllowlist } from '@/modules/qqbot/core/permission/qqbot-allowlist.entity'; -import { QqbotBlocklist } from '@/modules/qqbot/core/permission/qqbot-blocklist.entity'; -import { QqbotPermissionController } from '@/modules/qqbot/core/permission/qqbot-permission.controller'; -import { QqbotPermissionService } from '@/modules/qqbot/core/permission/qqbot-permission.service'; -import { QqbotPluginController } from '@/modules/qqbot/plugin-platform/qqbot-plugin.controller'; -import { QqbotEventPluginRegistryService } from '@/modules/qqbot/plugin-platform/registry/qqbot-event-plugin-registry.service'; -import { QqbotPluginRegistryService } from '@/modules/qqbot/plugin-platform/registry/qqbot-plugin-registry.service'; -import { QqbotBangDreamClientService } from '@/modules/qqbot/plugins/bangDream/application/bangdream-client.service'; -import { TsuguApplicationService } from '@/modules/qqbot/plugins/bangDream/application/bangdream-application.service'; -import { QqbotBangDreamRendererService } from '@/modules/qqbot/plugins/bangDream/application/bangdream-renderer.facade'; -import { QqbotBangDreamPluginService } from '@/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin'; -import { QqbotFf14ClientService } from '@/modules/qqbot/plugins/ff14Market/qqbot-ff14-client.service'; -import { QqbotFf14MarketPluginService } from '@/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.plugin'; -import { QqbotFflogsClientService } from '@/modules/qqbot/plugins/fflogs/qqbot-fflogs-client.service'; -import { QqbotFflogsPluginService } from '@/modules/qqbot/plugins/fflogs/qqbot-fflogs.plugin'; -import { QqbotRepeaterPluginService } from '@/modules/qqbot/plugins/repeater/qqbot-repeater.plugin'; -import { QqbotRuleController } from '@/modules/qqbot/core/rule/qqbot-rule.controller'; -import { QqbotRule } from '@/modules/qqbot/core/rule/qqbot-rule.entity'; -import { QqbotRuleEngineService } from '@/modules/qqbot/core/rule/qqbot-rule-engine.service'; -import { QqbotRuleService } from '@/modules/qqbot/core/rule/qqbot-rule.service'; -import { QqbotRateLimitService } from '@/modules/qqbot/core/send/qqbot-rate-limit.service'; -import { QqbotSendController } from '@/modules/qqbot/core/send/qqbot-send.controller'; -import { QqbotSendLog } from '@/modules/qqbot/core/send/qqbot-send-log.entity'; -import { QqbotSendService } from '@/modules/qqbot/core/send/qqbot-send.service'; +import { QqbotNapcatContainerService } from '@/modules/qqbot/napcat/infrastructure/integration/container/qqbot-napcat-container.service'; +import { QqbotAllowlist } from '@/modules/qqbot/core/infrastructure/persistence/permission/qqbot-allowlist.entity'; +import { QqbotBlocklist } from '@/modules/qqbot/core/infrastructure/persistence/permission/qqbot-blocklist.entity'; +import { QqbotPermissionController } from '@/modules/qqbot/core/contract/permission/qqbot-permission.controller'; +import { QqbotPermissionService } from '@/modules/qqbot/core/application/permission/qqbot-permission.service'; +import { QqbotRuleController } from '@/modules/qqbot/core/contract/rule/qqbot-rule.controller'; +import { QqbotRule } from '@/modules/qqbot/core/infrastructure/persistence/rule/qqbot-rule.entity'; +import { QqbotRuleEngineService } from '@/modules/qqbot/core/application/rule/qqbot-rule-engine.service'; +import { QqbotRuleService } from '@/modules/qqbot/core/application/rule/qqbot-rule.service'; +import { QqbotRateLimitService } from '@/modules/qqbot/core/application/send/qqbot-rate-limit.service'; +import { QqbotSendController } from '@/modules/qqbot/core/contract/send/qqbot-send.controller'; +import { QqbotSendLog } from '@/modules/qqbot/core/infrastructure/persistence/send/qqbot-send-log.entity'; +import { QqbotSendService } from '@/modules/qqbot/core/application/send/qqbot-send.service'; -export { QQBOT_CORE_DOMAIN_CONTRACT } from './qqbot-core.contract'; +export { QQBOT_CORE_DOMAIN_CONTRACT } from './contract/qqbot-core.contract'; export const QQBOT_CORE_ENTITIES = [ QqbotAccount, @@ -86,7 +74,6 @@ export const QQBOT_CORE_CONTROLLERS = [ QqbotDashboardController, QqbotMessageController, QqbotPermissionController, - QqbotPluginController, QqbotRuleController, QqbotSendController, ]; @@ -101,24 +88,12 @@ export const QQBOT_CORE_PROVIDERS = [ QqbotDashboardService, QqbotDedupeService, QqbotEventService, - QqbotPluginHttpClientService, - QqbotBangDreamClientService, - QqbotBangDreamPluginService, - QqbotBangDreamRendererService, - TsuguApplicationService, - QqbotFf14ClientService, - QqbotFf14MarketPluginService, - QqbotFflogsClientService, - QqbotFflogsPluginService, QqbotMessageService, NapcatDeviceIdentityService, QqbotNapcatLoginService, QqbotNapcatWatchdogService, QqbotNapcatContainerService, QqbotPermissionService, - QqbotEventPluginRegistryService, - QqbotPluginRegistryService, - QqbotRepeaterPluginService, QqbotRateLimitService, QqbotReplyTemplateService, QqbotReverseWsService, @@ -129,6 +104,7 @@ export const QQBOT_CORE_PROVIDERS = [ export const QQBOT_CORE_EXPORTS = [ QqbotAccountService, + QqbotSendService, NapcatDeviceIdentityService, QqbotNapcatLoginService, QqbotNapcatContainerService, @@ -140,6 +116,7 @@ export const QQBOT_CORE_EXPORTS = [ ConfigModule, AdminAuthGuardModule, DictModule, + forwardRef(() => QqbotPluginPlatformModule), TypeOrmModule.forFeature(QQBOT_CORE_ENTITIES), ], controllers: QQBOT_CORE_CONTROLLERS, diff --git a/src/modules/qqbot/core/schema/README.md b/src/modules/qqbot/core/schema/README.md new file mode 100644 index 0000000..95a99eb --- /dev/null +++ b/src/modules/qqbot/core/schema/README.md @@ -0,0 +1,33 @@ +# QQBot Core Schema + +Core owns account, command, rule, permission, message, conversation, config, +dedupe, and send-log persistence. + +Primary tables: + +- `qqbot_account` +- `qqbot_account_ability` +- `qqbot_command` +- `qqbot_command_log` +- `qqbot_config` +- `qqbot_conversation` +- `qqbot_dedupe` +- `qqbot_message` +- `qqbot_allowlist` +- `qqbot_blocklist` +- `qqbot_rule` +- `qqbot_send_log` + +Seed linkage: + +- Online command rows bind to accounts through `qqbot_account_ability`. +- Core command rows refer to plugin capabilities by `plugin_key` and + `operation_key`; execution is delegated through `QQBOT_PLUGIN_EXECUTION_PORT`. + +Verification SQL: + +```sql +SELECT COUNT(*) FROM qqbot_account WHERE is_deleted = 0; +SELECT COUNT(*) FROM qqbot_command WHERE is_deleted = 0; +SELECT COUNT(*) FROM qqbot_account_ability WHERE is_deleted = 0; +``` diff --git a/src/modules/qqbot/napcat/login/qqbot-napcat-login.service.ts b/src/modules/qqbot/napcat/application/login/qqbot-napcat-login.service.ts similarity index 99% rename from src/modules/qqbot/napcat/login/qqbot-napcat-login.service.ts rename to src/modules/qqbot/napcat/application/login/qqbot-napcat-login.service.ts index fea7719..368d259 100644 --- a/src/modules/qqbot/napcat/login/qqbot-napcat-login.service.ts +++ b/src/modules/qqbot/napcat/application/login/qqbot-napcat-login.service.ts @@ -5,7 +5,7 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { Observable } from 'rxjs'; import { throwVbenError, ToolsService } from '@/common'; -import { NapcatLoginApiClient } from '../integration/napcat-login-api.client'; +import { NapcatLoginApiClient } from '../../infrastructure/integration/napcat-login-api.client'; import type { NapcatApiResponse, NapcatCaptchaLoginResult, @@ -23,9 +23,9 @@ import type { QqbotNapcatRuntime, QrcodeLookupOptions, QrcodeRefreshOptions, -} from '../../core/contract/qqbot.types'; -import { QqbotAccountService } from '../../core/account/qqbot-account.service'; -import { QqbotNapcatContainerService } from '../qqbot-napcat-container.service'; +} from '@/modules/qqbot/core/contract/qqbot.types'; +import { QqbotAccountService } from '@/modules/qqbot/core/application/account/qqbot-account.service'; +import { QqbotNapcatContainerService } from '../../infrastructure/integration/container/qqbot-napcat-container.service'; @Injectable() export class QqbotNapcatLoginService { diff --git a/src/modules/qqbot/napcat/login/qqbot-napcat-watchdog.service.ts b/src/modules/qqbot/napcat/application/login/qqbot-napcat-watchdog.service.ts similarity index 91% rename from src/modules/qqbot/napcat/login/qqbot-napcat-watchdog.service.ts rename to src/modules/qqbot/napcat/application/login/qqbot-napcat-watchdog.service.ts index 03893ed..543002d 100644 --- a/src/modules/qqbot/napcat/login/qqbot-napcat-watchdog.service.ts +++ b/src/modules/qqbot/napcat/application/login/qqbot-napcat-watchdog.service.ts @@ -5,7 +5,7 @@ import { OnModuleInit, } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { QqbotAccountService } from '../../core/account/qqbot-account.service'; +import { QqbotAccountService } from '@/modules/qqbot/core/application/account/qqbot-account.service'; const DEFAULT_INTERVAL_MS = 120_000; const MIN_INTERVAL_MS = 30_000; @@ -13,7 +13,7 @@ const MIN_INTERVAL_MS = 30_000; /** * NapCat 离线看门狗:定时主动巡检在线账号,及时发现掉线/被踢并触发既有站内信告警。 * 不依赖 @nestjs/schedule,采用 OnModuleInit + setInterval(与代码库既有定时器一致)。 - * 仅检测 + 告警,自动重登由容器 ACCOUNT 快速登录(-q)+ docker restart 策略负责。 + * 仅检测 + 告警,自动重登由容器运行时适配器负责。 */ @Injectable() export class QqbotNapcatWatchdogService diff --git a/src/modules/qqbot/napcat/login/napcat-login-state-machine.ts b/src/modules/qqbot/napcat/domain/login/napcat-login-state-machine.ts similarity index 100% rename from src/modules/qqbot/napcat/login/napcat-login-state-machine.ts rename to src/modules/qqbot/napcat/domain/login/napcat-login-state-machine.ts diff --git a/src/modules/qqbot/napcat/index.ts b/src/modules/qqbot/napcat/index.ts index 7a6de5e..7ed61e7 100644 --- a/src/modules/qqbot/napcat/index.ts +++ b/src/modules/qqbot/napcat/index.ts @@ -1,11 +1,10 @@ -export * from './container/napcat-docker-device-options'; -export * from './device/napcat-device-identity.entity'; -export * from './device/napcat-device-identity.service'; +export * from './infrastructure/persistence/napcat-device-identity.entity'; +export * from './infrastructure/integration/device/napcat-device-identity.service'; export * from './infrastructure/persistence/qqbot-account-napcat.entity'; export * from './infrastructure/persistence/qqbot-napcat-container.entity'; -export * from './integration/napcat-login-api.client'; -export * from './login/napcat-login-state-machine'; -export * from './login/qqbot-napcat-login.service'; -export * from './login/qqbot-napcat-watchdog.service'; -export * from './persistence'; -export * from './qqbot-napcat-container.service'; +export * from './infrastructure/integration/napcat-login-api.client'; +export * from './domain/login/napcat-login-state-machine'; +export * from './application/login/qqbot-napcat-login.service'; +export * from './application/login/qqbot-napcat-watchdog.service'; +export * from './infrastructure/persistence'; +export * from './infrastructure/integration/container/qqbot-napcat-container.service'; diff --git a/src/modules/qqbot/napcat/container/napcat-docker-device-options.ts b/src/modules/qqbot/napcat/infrastructure/integration/container/napcat-docker-device-options.ts similarity index 88% rename from src/modules/qqbot/napcat/container/napcat-docker-device-options.ts rename to src/modules/qqbot/napcat/infrastructure/integration/container/napcat-docker-device-options.ts index b355b33..ddb8814 100644 --- a/src/modules/qqbot/napcat/container/napcat-docker-device-options.ts +++ b/src/modules/qqbot/napcat/infrastructure/integration/container/napcat-docker-device-options.ts @@ -1,4 +1,4 @@ -import type { NapcatDeviceIdentity } from '../device/napcat-device-identity.entity'; +import type { NapcatDeviceIdentity } from '../../persistence/napcat-device-identity.entity'; export type NapcatDockerDeviceOptions = { dataDir: string; diff --git a/src/modules/qqbot/napcat/qqbot-napcat-container.service.ts b/src/modules/qqbot/napcat/infrastructure/integration/container/qqbot-napcat-container.service.ts similarity index 98% rename from src/modules/qqbot/napcat/qqbot-napcat-container.service.ts rename to src/modules/qqbot/napcat/infrastructure/integration/container/qqbot-napcat-container.service.ts index e2d064b..e70fb81 100644 --- a/src/modules/qqbot/napcat/qqbot-napcat-container.service.ts +++ b/src/modules/qqbot/napcat/infrastructure/integration/container/qqbot-napcat-container.service.ts @@ -10,11 +10,11 @@ import { throwVbenError, ToolsService } from '@/common'; import { toNapcatDockerDeviceOptions, type NapcatDockerDeviceOptions, -} from './container/napcat-docker-device-options'; -import { NapcatDeviceIdentityService } from './device/napcat-device-identity.service'; -import { QqbotAccount } from '../core/account/qqbot-account.entity'; -import { QqbotAccountNapcat } from './infrastructure/persistence/qqbot-account-napcat.entity'; -import { QqbotNapcatContainer } from './infrastructure/persistence/qqbot-napcat-container.entity'; +} from './napcat-docker-device-options'; +import { NapcatDeviceIdentityService } from '../device/napcat-device-identity.service'; +import { QqbotAccount } from '@/modules/qqbot/core/infrastructure/persistence/account/qqbot-account.entity'; +import { QqbotAccountNapcat } from '../../persistence/qqbot-account-napcat.entity'; +import { QqbotNapcatContainer } from '../../persistence/qqbot-napcat-container.entity'; import type { NapcatApiResponse, NapcatCredential, @@ -22,7 +22,7 @@ import type { QqbotNapcatRuntime, QqbotNapcatRuntimeLoginStatus, QqbotNapcatRuntimeStatusSnapshot, -} from '../core/contract/qqbot.types'; +} from '@/modules/qqbot/core/contract/qqbot.types'; type NapcatLoginLogState = 'offline' | 'online' | 'unknown'; diff --git a/src/modules/qqbot/napcat/device/napcat-device-identity.service.ts b/src/modules/qqbot/napcat/infrastructure/integration/device/napcat-device-identity.service.ts similarity index 97% rename from src/modules/qqbot/napcat/device/napcat-device-identity.service.ts rename to src/modules/qqbot/napcat/infrastructure/integration/device/napcat-device-identity.service.ts index 70dbdea..11250ea 100644 --- a/src/modules/qqbot/napcat/device/napcat-device-identity.service.ts +++ b/src/modules/qqbot/napcat/infrastructure/integration/device/napcat-device-identity.service.ts @@ -4,7 +4,7 @@ import { ConfigService } from '@nestjs/config'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { ensureSnowflakeId } from '@/common'; -import { NapcatDeviceIdentity } from './napcat-device-identity.entity'; +import { NapcatDeviceIdentity } from '../../persistence/napcat-device-identity.entity'; type ResolveNapcatDeviceIdentityInput = { accountId: string; diff --git a/src/modules/qqbot/napcat/integration/napcat-login-api.client.ts b/src/modules/qqbot/napcat/infrastructure/integration/napcat-login-api.client.ts similarity index 100% rename from src/modules/qqbot/napcat/integration/napcat-login-api.client.ts rename to src/modules/qqbot/napcat/infrastructure/integration/napcat-login-api.client.ts diff --git a/src/modules/qqbot/napcat/persistence.ts b/src/modules/qqbot/napcat/infrastructure/persistence/index.ts similarity index 57% rename from src/modules/qqbot/napcat/persistence.ts rename to src/modules/qqbot/napcat/infrastructure/persistence/index.ts index f5959d8..dc3f502 100644 --- a/src/modules/qqbot/napcat/persistence.ts +++ b/src/modules/qqbot/napcat/infrastructure/persistence/index.ts @@ -1,6 +1,6 @@ -import { NapcatDeviceIdentity } from './device/napcat-device-identity.entity'; -import { QqbotAccountNapcat } from './infrastructure/persistence/qqbot-account-napcat.entity'; -import { QqbotNapcatContainer } from './infrastructure/persistence/qqbot-napcat-container.entity'; +import { NapcatDeviceIdentity } from './napcat-device-identity.entity'; +import { QqbotAccountNapcat } from './qqbot-account-napcat.entity'; +import { QqbotNapcatContainer } from './qqbot-napcat-container.entity'; export const NAPCAT_RUNTIME_DOMAIN_CONTRACT = { tables: [ diff --git a/src/modules/qqbot/napcat/device/napcat-device-identity.entity.ts b/src/modules/qqbot/napcat/infrastructure/persistence/napcat-device-identity.entity.ts similarity index 100% rename from src/modules/qqbot/napcat/device/napcat-device-identity.entity.ts rename to src/modules/qqbot/napcat/infrastructure/persistence/napcat-device-identity.entity.ts diff --git a/src/modules/qqbot/napcat/infrastructure/persistence/qqbot-account-napcat.entity.ts b/src/modules/qqbot/napcat/infrastructure/persistence/qqbot-account-napcat.entity.ts index 2b303e1..c23519e 100644 --- a/src/modules/qqbot/napcat/infrastructure/persistence/qqbot-account-napcat.entity.ts +++ b/src/modules/qqbot/napcat/infrastructure/persistence/qqbot-account-napcat.entity.ts @@ -6,7 +6,7 @@ import { KtDateTimeColumn, KtUpdateDateColumn, } from '@/common'; -import type { QqbotAccountNapcatBindStatus } from '../../../core/contract/qqbot.types'; +import type { QqbotAccountNapcatBindStatus } from '@/modules/qqbot/core/contract/qqbot.types'; @Entity('qqbot_account_napcat') @Index('idx_qqbot_account_napcat_account', ['accountId', 'isDeleted']) diff --git a/src/modules/qqbot/napcat/infrastructure/persistence/qqbot-napcat-container.entity.ts b/src/modules/qqbot/napcat/infrastructure/persistence/qqbot-napcat-container.entity.ts index 9678ccf..5a40243 100644 --- a/src/modules/qqbot/napcat/infrastructure/persistence/qqbot-napcat-container.entity.ts +++ b/src/modules/qqbot/napcat/infrastructure/persistence/qqbot-napcat-container.entity.ts @@ -6,7 +6,7 @@ import { KtDateTimeColumn, KtUpdateDateColumn, } from '@/common'; -import type { QqbotNapcatContainerStatus } from '../../../core/contract/qqbot.types'; +import type { QqbotNapcatContainerStatus } from '@/modules/qqbot/core/contract/qqbot.types'; @Entity('qqbot_napcat_container') @Index('uk_qqbot_napcat_container_name', ['name'], { unique: true }) diff --git a/src/modules/qqbot/napcat/schema/README.md b/src/modules/qqbot/napcat/schema/README.md new file mode 100644 index 0000000..3dd2612 --- /dev/null +++ b/src/modules/qqbot/napcat/schema/README.md @@ -0,0 +1,27 @@ +# QQBot NapCat Schema + +NapCat owns container runtime, account binding, device identity, login session, +challenge, and cleanup persistence. + +Current compatibility tables: + +- `qqbot_account_napcat` +- `qqbot_napcat_container` +- `napcat_device_identity` + +Target v3 tables: + +- `napcat_container` +- `napcat_device_identity` +- `napcat_account_binding` +- `napcat_login_session` +- `napcat_login_challenge` +- `napcat_runtime_cleanup` + +Verification SQL: + +```sql +SELECT COUNT(*) FROM napcat_device_identity; +SELECT COUNT(*) FROM qqbot_account_napcat WHERE is_deleted = 0; +SELECT COUNT(*) FROM qqbot_napcat_container WHERE is_deleted = 0; +``` diff --git a/src/modules/qqbot/core/command/qqbot-command-parser.service.ts b/src/modules/qqbot/plugin-platform/application/argument/qqbot-plugin-argument-parser.service.ts similarity index 80% rename from src/modules/qqbot/core/command/qqbot-command-parser.service.ts rename to src/modules/qqbot/plugin-platform/application/argument/qqbot-plugin-argument-parser.service.ts index 67c90e6..c225596 100644 --- a/src/modules/qqbot/core/command/qqbot-command-parser.service.ts +++ b/src/modules/qqbot/plugin-platform/application/argument/qqbot-plugin-argument-parser.service.ts @@ -1,76 +1,38 @@ import { Injectable } from '@nestjs/common'; import { DictService } from '@/modules/admin/platform-config/dict/dict.service'; -import type { QqbotCommand } from './qqbot-command.entity'; -import type { QqbotNormalizedMessage } from '../contract/qqbot.types'; import { buildQqbotFf14MarketCatalog, buildQqbotFf14MarketCatalogFromTree, - QQBOT_FF14_MARKET_DICT_CODES, isQqbotFf14DataCenterName, isQqbotFf14LocationName, isQqbotFf14RegionName, isQqbotFf14WorldName, + QQBOT_FF14_MARKET_DICT_CODES, splitQqbotFf14WorldPath, } from '@/modules/qqbot/plugins/ff14Market/qqbot-ff14-worlds'; import type { QqbotFf14MarketCatalog } from '@/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.types'; -import type { QqbotCommandMatchResult } from '../contract/qqbot.types'; +import type { QqbotPluginExecutionInput } from '@/modules/qqbot/core/domain/plugin-execution.port'; @Injectable() -export class QqbotCommandParserService { +export class QqbotPluginArgumentParserService { constructor(private readonly dictService: DictService) {} - async match(command: QqbotCommand, message: QqbotNormalizedMessage) { - const source = `${message.messageText || ''}`.trim(); - if (!source) return null; - - const aliases = this.getAliases(command); - const prefixes = this.getPrefixes(command); - for (const alias of aliases) { - for (const prefix of prefixes) { - const commandText = `${prefix}${alias}`.trim(); - const rawArgs = this.pickArgs(source, commandText); - if (rawArgs === null) continue; - return { - alias, - input: await this.parseInput(command, rawArgs), - matched: true, - rawArgs, - } satisfies QqbotCommandMatchResult; - } + async normalizeInput(input: QqbotPluginExecutionInput) { + const parserKey = `${input.context?.command?.parserKey || 'plain'}`.trim(); + const rawArgs = `${input.input?.raw ?? input.input?.text ?? ''}`.trim(); + if (parserKey === 'ff14Price') { + return { + ...input.input, + ...this.removeEmpty(await this.parseFf14PriceInput(rawArgs)), + }; } - return null; - } - - getAliases(command: QqbotCommand) { - return this.normalizeList(command.aliases, [command.code, command.name]); - } - - getPrefixes(command: QqbotCommand) { - return this.normalizeList(command.prefixes, ['/', '!', '!']); - } - - private pickArgs(source: string, commandText: string) { - if (!commandText) return null; - if (source === commandText) return ''; - if (source.startsWith(`${commandText} `)) { - return source.slice(commandText.length).trim(); + if (parserKey === 'fflogsCharacter') { + return { + ...input.input, + ...this.removeEmpty(await this.parseFflogsCharacterInput(rawArgs)), + }; } - return null; - } - - private async parseInput(command: QqbotCommand, rawArgs: string) { - if (command.parserKey === 'ff14Price') { - return this.parseFf14PriceInput(rawArgs); - } - if (command.parserKey === 'fflogsCharacter') { - return this.parseFflogsCharacterInput(rawArgs); - } - const args = rawArgs ? rawArgs.split(/\s+/).filter(Boolean) : []; - return { - args, - raw: rawArgs, - text: rawArgs, - }; + return input.input; } private async parseFf14PriceInput(rawArgs: string) { @@ -240,11 +202,11 @@ export class QqbotCommandParserService { ), encounter: encounterName, encounterName, + limit: this.normalizeString(flags.get('limit') || flags.get('数量')), metric: this.normalizeString(flags.get('metric') || flags.get('指标')), partition: this.normalizeString( flags.get('partition') || flags.get('分区'), ), - limit: this.normalizeString(flags.get('limit') || flags.get('数量')), raw: rawArgs, role: this.normalizeString(flags.get('role') || flags.get('职责')), serverRegion: this.normalizeString( @@ -318,7 +280,7 @@ export class QqbotCommandParserService { private async pickFflogsPositionalsByKnownWorld(positional: string[]) { const catalog = await this.getFf14MarketCatalog(); - for (let index = positional.length - 2; index > 0; index--) { + for (let index = positional.length - 2; index > 0; index -= 1) { const candidate = positional[index]; if (!isQqbotFf14LocationName(catalog, candidate)) continue; const characterName = positional.slice(0, index).join(' ').trim(); @@ -366,28 +328,18 @@ export class QqbotCommandParserService { return ['1', 'true', 'yes', 'hq'].includes(`${value}`.toLowerCase()); } - private normalizeList(value: string | undefined, fallback: string[]) { - const raw = `${value || ''}`.trim(); - const parsed = this.tryParseJsonArray(raw); - const source = parsed.length > 0 ? parsed : raw.split(','); - const list = [...source, ...fallback] - .map((item) => `${item || ''}`.trim()) - .filter(Boolean); - return [...new Set(list)]; - } - - private tryParseJsonArray(value: string) { - if (!value.startsWith('[')) return []; - try { - const parsed = JSON.parse(value); - return Array.isArray(parsed) ? parsed : []; - } catch { - return []; - } - } - private normalizeString(value?: string | true) { if (value === true) return ''; return `${value || ''}`.trim(); } + + private removeEmpty(input: Record) { + return Object.entries(input).reduce>( + (result, [key, value]) => { + if (value !== undefined && value !== '') result[key] = value; + return result; + }, + {}, + ); + } } diff --git a/src/modules/qqbot/plugin-platform/application/plugin-execution.adapter.ts b/src/modules/qqbot/plugin-platform/application/plugin-execution.adapter.ts new file mode 100644 index 0000000..8dab6e3 --- /dev/null +++ b/src/modules/qqbot/plugin-platform/application/plugin-execution.adapter.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@nestjs/common'; +import type { + QqbotPluginEventDispatchInput, + QqbotPluginExecutionInput, + QqbotPluginExecutionPort, + QqbotPluginOperationLookup, +} from '@/modules/qqbot/core/domain/plugin-execution.port'; +import { QqbotEventPluginRegistryService } from './registry/qqbot-event-plugin-registry.service'; +import { QqbotPluginRegistryService } from './registry/qqbot-plugin-registry.service'; +import { QqbotPluginArgumentParserService } from './argument/qqbot-plugin-argument-parser.service'; + +@Injectable() +export class QqbotPluginExecutionAdapter implements QqbotPluginExecutionPort { + constructor( + private readonly argumentParser: QqbotPluginArgumentParserService, + private readonly eventPluginRegistry: QqbotEventPluginRegistryService, + private readonly pluginRegistry: QqbotPluginRegistryService, + ) {} + + async executeOperation(input: QqbotPluginExecutionInput) { + const normalizedInput = await this.argumentParser.normalizeInput(input); + return this.pluginRegistry.execute( + input.pluginKey, + input.operationKey, + normalizedInput, + { + ...(input.context || {}), + args: normalizedInput, + }, + ); + } + + async dispatchEvent(input: QqbotPluginEventDispatchInput) { + if (input.eventKey !== 'message') return false; + return this.eventPluginRegistry.dispatchMessage(input.message); + } + + async listActiveOperations() { + return [ + ...this.pluginRegistry.listOperations(), + ...this.eventPluginRegistry.listOperations(), + ]; + } + + async getOperationByCommand(command: QqbotPluginOperationLookup) { + if (!command.pluginKey || !command.operationKey) return null; + return ( + this.pluginRegistry + .listOperations(command.pluginKey) + .find((operation) => operation.key === command.operationKey) || null + ); + } +} diff --git a/src/modules/qqbot/plugin-platform/plugin-platform.service.ts b/src/modules/qqbot/plugin-platform/application/plugin-platform.service.ts similarity index 97% rename from src/modules/qqbot/plugin-platform/plugin-platform.service.ts rename to src/modules/qqbot/plugin-platform/application/plugin-platform.service.ts index 322e4e2..d3ceced 100644 --- a/src/modules/qqbot/plugin-platform/plugin-platform.service.ts +++ b/src/modules/qqbot/plugin-platform/application/plugin-platform.service.ts @@ -2,7 +2,10 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { throwVbenError } from '@/common'; -import { parseQqbotPluginManifest, type QqbotPluginManifest } from './manifest'; +import { + parseQqbotPluginManifest, + type QqbotPluginManifest, +} from '../domain/manifest'; import { QqbotPlugin, QqbotPluginAccountBinding, @@ -14,7 +17,7 @@ import { QqbotPluginRuntimeEvent, QqbotPluginVersion, type QqbotPluginInstallStatus, -} from './persistence'; +} from '../infrastructure/persistence'; type ValidateManifestBody = { manifest?: unknown; diff --git a/src/modules/qqbot/plugin-platform/registry/qqbot-event-plugin-registry.service.ts b/src/modules/qqbot/plugin-platform/application/registry/qqbot-event-plugin-registry.service.ts similarity index 92% rename from src/modules/qqbot/plugin-platform/registry/qqbot-event-plugin-registry.service.ts rename to src/modules/qqbot/plugin-platform/application/registry/qqbot-event-plugin-registry.service.ts index 93a062f..69f6012 100644 --- a/src/modules/qqbot/plugin-platform/registry/qqbot-event-plugin-registry.service.ts +++ b/src/modules/qqbot/plugin-platform/application/registry/qqbot-event-plugin-registry.service.ts @@ -1,9 +1,10 @@ import { Injectable } from '@nestjs/common'; import { formatKtDateTime, throwVbenError } from '@/common'; -import { QqbotAccountService } from '@/modules/qqbot/core/account/qqbot-account.service'; +import { QqbotAccountService } from '@/modules/qqbot/core/application/account/qqbot-account.service'; import { QqbotRepeaterPluginService } from '@/modules/qqbot/plugins/repeater/qqbot-repeater.plugin'; import type { QqbotEventPluginDefinition, + QqbotNormalizedMessage, QqbotPluginHealth, QqbotPluginOperationSummary, } from '@/modules/qqbot/core/contract/qqbot.types'; @@ -61,6 +62,10 @@ export class QqbotEventPluginRegistryService { })); } + async dispatchMessage(message: QqbotNormalizedMessage) { + return this.repeaterPlugin.handleMessage(message); + } + async bind(pluginKey: string, selfId: string) { this.assertPlugin(pluginKey); if (pluginKey === 'repeater') { diff --git a/src/modules/qqbot/plugin-platform/registry/qqbot-plugin-registry.service.ts b/src/modules/qqbot/plugin-platform/application/registry/qqbot-plugin-registry.service.ts similarity index 100% rename from src/modules/qqbot/plugin-platform/registry/qqbot-plugin-registry.service.ts rename to src/modules/qqbot/plugin-platform/application/registry/qqbot-plugin-registry.service.ts diff --git a/src/modules/qqbot/plugin-platform/plugin-platform.controller.ts b/src/modules/qqbot/plugin-platform/contract/plugin-platform.controller.ts similarity index 97% rename from src/modules/qqbot/plugin-platform/plugin-platform.controller.ts rename to src/modules/qqbot/plugin-platform/contract/plugin-platform.controller.ts index 77048b2..4a6676a 100644 --- a/src/modules/qqbot/plugin-platform/plugin-platform.controller.ts +++ b/src/modules/qqbot/plugin-platform/contract/plugin-platform.controller.ts @@ -11,7 +11,7 @@ import { import { ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger'; import { JwtAuthGuard } from '@/modules/admin/identity/auth/jwt-auth.guard'; import { vbenSuccess } from '@/common'; -import { QqbotPluginPlatformService } from './plugin-platform.service'; +import { QqbotPluginPlatformService } from '../application/plugin-platform.service'; @ApiTags('QQBot - 插件平台') @Controller('qqbot/plugin-platform') diff --git a/src/modules/qqbot/plugin-platform/qqbot-plugin.controller.ts b/src/modules/qqbot/plugin-platform/contract/qqbot-plugin.controller.ts similarity index 96% rename from src/modules/qqbot/plugin-platform/qqbot-plugin.controller.ts rename to src/modules/qqbot/plugin-platform/contract/qqbot-plugin.controller.ts index 5ed89f3..d468a33 100644 --- a/src/modules/qqbot/plugin-platform/qqbot-plugin.controller.ts +++ b/src/modules/qqbot/plugin-platform/contract/qqbot-plugin.controller.ts @@ -10,8 +10,8 @@ import { import { ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger'; import { JwtAuthGuard } from '@/modules/admin/identity/auth/jwt-auth.guard'; import { ToolsService, vbenSuccess } from '@/common'; -import { QqbotEventPluginRegistryService } from './registry/qqbot-event-plugin-registry.service'; -import { QqbotPluginRegistryService } from './registry/qqbot-plugin-registry.service'; +import { QqbotEventPluginRegistryService } from '../application/registry/qqbot-event-plugin-registry.service'; +import { QqbotPluginRegistryService } from '../application/registry/qqbot-plugin-registry.service'; import type { QqbotPluginOperationSummary, QqbotPluginTriggerMode, diff --git a/src/modules/qqbot/plugin-platform/manifest/index.ts b/src/modules/qqbot/plugin-platform/domain/manifest/index.ts similarity index 100% rename from src/modules/qqbot/plugin-platform/manifest/index.ts rename to src/modules/qqbot/plugin-platform/domain/manifest/index.ts diff --git a/src/modules/qqbot/plugin-platform/manifest/manifest.parser.ts b/src/modules/qqbot/plugin-platform/domain/manifest/manifest.parser.ts similarity index 100% rename from src/modules/qqbot/plugin-platform/manifest/manifest.parser.ts rename to src/modules/qqbot/plugin-platform/domain/manifest/manifest.parser.ts diff --git a/src/modules/qqbot/plugin-platform/manifest/manifest.types.ts b/src/modules/qqbot/plugin-platform/domain/manifest/manifest.types.ts similarity index 100% rename from src/modules/qqbot/plugin-platform/manifest/manifest.types.ts rename to src/modules/qqbot/plugin-platform/domain/manifest/manifest.types.ts diff --git a/src/modules/qqbot/plugin-platform/runtime/index.ts b/src/modules/qqbot/plugin-platform/infrastructure/integration/runtime/index.ts similarity index 100% rename from src/modules/qqbot/plugin-platform/runtime/index.ts rename to src/modules/qqbot/plugin-platform/infrastructure/integration/runtime/index.ts diff --git a/src/modules/qqbot/plugin-platform/runtime/worker-runtime.ts b/src/modules/qqbot/plugin-platform/infrastructure/integration/runtime/worker-runtime.ts similarity index 100% rename from src/modules/qqbot/plugin-platform/runtime/worker-runtime.ts rename to src/modules/qqbot/plugin-platform/infrastructure/integration/runtime/worker-runtime.ts diff --git a/src/modules/qqbot/plugin-platform/runtime/worker-runtime.types.ts b/src/modules/qqbot/plugin-platform/infrastructure/integration/runtime/worker-runtime.types.ts similarity index 100% rename from src/modules/qqbot/plugin-platform/runtime/worker-runtime.types.ts rename to src/modules/qqbot/plugin-platform/infrastructure/integration/runtime/worker-runtime.types.ts diff --git a/src/modules/qqbot/plugin-platform/sdk/index.ts b/src/modules/qqbot/plugin-platform/infrastructure/integration/sdk/index.ts similarity index 100% rename from src/modules/qqbot/plugin-platform/sdk/index.ts rename to src/modules/qqbot/plugin-platform/infrastructure/integration/sdk/index.ts diff --git a/src/modules/qqbot/plugin-platform/sdk/plugin-http-client.service.ts b/src/modules/qqbot/plugin-platform/infrastructure/integration/sdk/plugin-http-client.service.ts similarity index 100% rename from src/modules/qqbot/plugin-platform/sdk/plugin-http-client.service.ts rename to src/modules/qqbot/plugin-platform/infrastructure/integration/sdk/plugin-http-client.service.ts diff --git a/src/modules/qqbot/plugin-platform/sdk/plugin-sdk.ts b/src/modules/qqbot/plugin-platform/infrastructure/integration/sdk/plugin-sdk.ts similarity index 100% rename from src/modules/qqbot/plugin-platform/sdk/plugin-sdk.ts rename to src/modules/qqbot/plugin-platform/infrastructure/integration/sdk/plugin-sdk.ts diff --git a/src/modules/qqbot/plugin-platform/persistence/index.ts b/src/modules/qqbot/plugin-platform/infrastructure/persistence/index.ts similarity index 100% rename from src/modules/qqbot/plugin-platform/persistence/index.ts rename to src/modules/qqbot/plugin-platform/infrastructure/persistence/index.ts diff --git a/src/modules/qqbot/plugin-platform/persistence/plugin-platform.contract.ts b/src/modules/qqbot/plugin-platform/infrastructure/persistence/plugin-platform.contract.ts similarity index 100% rename from src/modules/qqbot/plugin-platform/persistence/plugin-platform.contract.ts rename to src/modules/qqbot/plugin-platform/infrastructure/persistence/plugin-platform.contract.ts diff --git a/src/modules/qqbot/plugin-platform/persistence/plugin-platform.entities.ts b/src/modules/qqbot/plugin-platform/infrastructure/persistence/plugin-platform.entities.ts similarity index 100% rename from src/modules/qqbot/plugin-platform/persistence/plugin-platform.entities.ts rename to src/modules/qqbot/plugin-platform/infrastructure/persistence/plugin-platform.entities.ts diff --git a/src/modules/qqbot/plugin-platform/plugin-platform.module.ts b/src/modules/qqbot/plugin-platform/plugin-platform.module.ts index d072fa0..996a038 100644 --- a/src/modules/qqbot/plugin-platform/plugin-platform.module.ts +++ b/src/modules/qqbot/plugin-platform/plugin-platform.module.ts @@ -1,18 +1,63 @@ -import { Module } from '@nestjs/common'; +import { forwardRef, Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AdminAuthGuardModule } from '@/modules/admin/identity/auth/admin-auth-guard.module'; -import { QqbotPluginPlatformController } from './plugin-platform.controller'; -import { QqbotPluginPlatformService } from './plugin-platform.service'; -import { QQBOT_PLUGIN_PLATFORM_ENTITIES } from './persistence'; -import { QqbotPluginHttpClientService } from './sdk'; +import { DictModule } from '@/modules/admin/platform-config/dict/dict.module'; +import { QQBOT_PLUGIN_EXECUTION_PORT } from '@/modules/qqbot/core/domain/plugin-execution.port'; +import { QqbotCoreModule } from '@/modules/qqbot/core/qqbot-core.module'; +import { QqbotBangDreamClientService } from '@/modules/qqbot/plugins/bangDream/application/bangdream-client.service'; +import { TsuguApplicationService } from '@/modules/qqbot/plugins/bangDream/application/bangdream-application.service'; +import { QqbotBangDreamRendererService } from '@/modules/qqbot/plugins/bangDream/application/bangdream-renderer.facade'; +import { QqbotBangDreamPluginService } from '@/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin'; +import { QqbotFf14ClientService } from '@/modules/qqbot/plugins/ff14Market/qqbot-ff14-client.service'; +import { QqbotFf14MarketPluginService } from '@/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.plugin'; +import { QqbotFflogsClientService } from '@/modules/qqbot/plugins/fflogs/qqbot-fflogs-client.service'; +import { QqbotFflogsPluginService } from '@/modules/qqbot/plugins/fflogs/qqbot-fflogs.plugin'; +import { QqbotRepeaterPluginService } from '@/modules/qqbot/plugins/repeater/qqbot-repeater.plugin'; +import { QqbotPluginArgumentParserService } from './application/argument/qqbot-plugin-argument-parser.service'; +import { QqbotEventPluginRegistryService } from './application/registry/qqbot-event-plugin-registry.service'; +import { QqbotPluginRegistryService } from './application/registry/qqbot-plugin-registry.service'; +import { QqbotPluginExecutionAdapter } from './application/plugin-execution.adapter'; +import { QqbotPluginPlatformService } from './application/plugin-platform.service'; +import { QqbotPluginPlatformController } from './contract/plugin-platform.controller'; +import { QqbotPluginController } from './contract/qqbot-plugin.controller'; +import { QQBOT_PLUGIN_PLATFORM_ENTITIES } from './infrastructure/persistence'; +import { QqbotPluginHttpClientService } from './infrastructure/integration/sdk'; @Module({ - controllers: [QqbotPluginPlatformController], - exports: [QqbotPluginHttpClientService, QqbotPluginPlatformService], + controllers: [QqbotPluginController, QqbotPluginPlatformController], + exports: [ + QQBOT_PLUGIN_EXECUTION_PORT, + QqbotPluginHttpClientService, + QqbotPluginPlatformService, + ], imports: [ + ConfigModule, AdminAuthGuardModule, + DictModule, + forwardRef(() => QqbotCoreModule), TypeOrmModule.forFeature([...QQBOT_PLUGIN_PLATFORM_ENTITIES]), ], - providers: [QqbotPluginHttpClientService, QqbotPluginPlatformService], + providers: [ + QqbotBangDreamClientService, + QqbotBangDreamPluginService, + QqbotBangDreamRendererService, + QqbotEventPluginRegistryService, + QqbotFf14ClientService, + QqbotFf14MarketPluginService, + QqbotFflogsClientService, + QqbotFflogsPluginService, + QqbotPluginArgumentParserService, + QqbotPluginExecutionAdapter, + { + provide: QQBOT_PLUGIN_EXECUTION_PORT, + useExisting: QqbotPluginExecutionAdapter, + }, + QqbotPluginHttpClientService, + QqbotPluginPlatformService, + QqbotPluginRegistryService, + QqbotRepeaterPluginService, + TsuguApplicationService, + ], }) export class QqbotPluginPlatformModule {} diff --git a/src/modules/qqbot/plugin-platform/schema/README.md b/src/modules/qqbot/plugin-platform/schema/README.md new file mode 100644 index 0000000..742f064 --- /dev/null +++ b/src/modules/qqbot/plugin-platform/schema/README.md @@ -0,0 +1,27 @@ +# QQBot Plugin Platform Schema + +Plugin Platform owns plugin package, installation, lifecycle, runtime event, +operation registry, event dispatch, SDK host adapter, and account binding +persistence. + +Current compatibility tables: + +- `qqbot_plugin_package` +- `qqbot_plugin_installation` +- `qqbot_plugin_runtime_event` +- `qqbot_plugin_account_binding` +- `qqbot_plugin_config` + +Seed linkage: + +- Built-in command capabilities are exposed through the platform registry. +- Legacy `/qqbot/plugin/*` routes are contract compatibility endpoints owned by + Plugin Platform. + +Verification SQL: + +```sql +SELECT COUNT(*) FROM qqbot_plugin_package WHERE is_deleted = 0; +SELECT COUNT(*) FROM qqbot_plugin_installation WHERE is_deleted = 0; +SELECT COUNT(*) FROM qqbot_plugin_runtime_event; +``` diff --git a/src/modules/qqbot/plugins/ff14Market/qqbot-ff14-client.service.ts b/src/modules/qqbot/plugins/ff14Market/qqbot-ff14-client.service.ts index c83fc1c..718f816 100644 --- a/src/modules/qqbot/plugins/ff14Market/qqbot-ff14-client.service.ts +++ b/src/modules/qqbot/plugins/ff14Market/qqbot-ff14-client.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { formatKtDateTime } from '@/common'; import { DictService } from '@/modules/admin/platform-config/dict/dict.service'; -import { QqbotPluginHttpClientService } from '@/modules/qqbot/plugin-platform/sdk'; +import { QqbotPluginHttpClientService } from '@/modules/qqbot/plugin-platform/infrastructure/integration/sdk'; import { buildQqbotFf14MarketCatalog, buildQqbotFf14MarketCatalogFromTree, diff --git a/src/modules/qqbot/plugins/fflogs/qqbot-fflogs-client.service.ts b/src/modules/qqbot/plugins/fflogs/qqbot-fflogs-client.service.ts index 4dc5002..46bd972 100644 --- a/src/modules/qqbot/plugins/fflogs/qqbot-fflogs-client.service.ts +++ b/src/modules/qqbot/plugins/fflogs/qqbot-fflogs-client.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { DictService } from '@/modules/admin/platform-config/dict/dict.service'; -import { QqbotPluginHttpClientService } from '@/modules/qqbot/plugin-platform/sdk'; +import { QqbotPluginHttpClientService } from '@/modules/qqbot/plugin-platform/infrastructure/integration/sdk'; import type { FflogsCharacterEncounterRankingsResponse, FflogsCharacterSummaryResponse, diff --git a/src/modules/qqbot/plugins/repeater/qqbot-repeater.plugin.ts b/src/modules/qqbot/plugins/repeater/qqbot-repeater.plugin.ts index c44457b..c6f7782 100644 --- a/src/modules/qqbot/plugins/repeater/qqbot-repeater.plugin.ts +++ b/src/modules/qqbot/plugins/repeater/qqbot-repeater.plugin.ts @@ -1,18 +1,25 @@ import { Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { ToolsService } from '@/common'; -import { QqbotAccountService } from '@/modules/qqbot/core/account/qqbot-account.service'; +import { QqbotAccountService } from '@/modules/qqbot/core/application/account/qqbot-account.service'; import type { QqbotEventPluginDefinition, QqbotEventPluginSummary, QqbotNormalizedMessage, - QqbotRepeaterConversationState, } from '@/modules/qqbot/core/contract/qqbot.types'; -import { QqbotSendService } from '@/modules/qqbot/core/send/qqbot-send.service'; +import { QqbotSendService } from '@/modules/qqbot/core/application/send/qqbot-send.service'; const QQBOT_REPEATER_VERSION = '1.0.0'; const QQBOT_REPEATER_PLUGIN_KEY = 'repeater'; +type QqbotRepeaterConversationState = { + count: number; + lastRepeatedAt?: number; + lastText: string; + repeatedText: string; + updatedAt: number; +}; + @Injectable() export class QqbotRepeaterPluginService { private readonly logger = new Logger(QqbotRepeaterPluginService.name); diff --git a/test/modules/qqbot/architecture/qqbot-current-operation-matrix.spec.ts b/test/modules/qqbot/architecture/qqbot-current-operation-matrix.spec.ts index 5ee25ff..509dc65 100644 --- a/test/modules/qqbot/architecture/qqbot-current-operation-matrix.spec.ts +++ b/test/modules/qqbot/architecture/qqbot-current-operation-matrix.spec.ts @@ -1,7 +1,7 @@ import { readFileSync } from 'fs'; import { join } from 'path'; -import { parseQqbotPluginManifest } from '../../../../src/modules/qqbot/plugin-platform/manifest'; +import { parseQqbotPluginManifest } from '../../../../src/modules/qqbot/plugin-platform/domain/manifest'; import { BANGDREAM_OPERATION_REGISTRY } from '../../../../src/modules/qqbot/plugins/bangDream/registry/operation-registry'; const repoRoot = join(__dirname, '../../../..'); diff --git a/test/modules/qqbot/core/qqbot-core-command-contract.spec.ts b/test/modules/qqbot/core/qqbot-core-command-contract.spec.ts index 89fe8df..bea5bee 100644 --- a/test/modules/qqbot/core/qqbot-core-command-contract.spec.ts +++ b/test/modules/qqbot/core/qqbot-core-command-contract.spec.ts @@ -1,4 +1,4 @@ -import { QQBOT_CORE_DOMAIN_CONTRACT } from '../../../../src/modules/qqbot/core/qqbot-core.contract'; +import { QQBOT_CORE_DOMAIN_CONTRACT } from '../../../../src/modules/qqbot/core/contract/qqbot-core.contract'; import { readRefactorV3SqlSchema } from '../../../helpers/sql-schema.helper'; describe('QQBot core command contract', () => { diff --git a/test/modules/qqbot/core/qqbot-core-command-smoke.spec.ts b/test/modules/qqbot/core/qqbot-core-command-smoke.spec.ts index 960b44d..5afde96 100644 --- a/test/modules/qqbot/core/qqbot-core-command-smoke.spec.ts +++ b/test/modules/qqbot/core/qqbot-core-command-smoke.spec.ts @@ -13,24 +13,19 @@ import { Test } from '@nestjs/testing'; import * as request from 'supertest'; import { DictService } from '../../../../src/modules/admin/platform-config/dict/dict.service'; import { ToolsService } from '../../../../src/common'; -import { QqbotAccountService } from '../../../../src/modules/qqbot/core/account/qqbot-account.service'; -import { QqbotCommandController } from '../../../../src/modules/qqbot/core/command/qqbot-command.controller'; -import { QqbotCommand } from '../../../../src/modules/qqbot/core/command/qqbot-command.entity'; -import { QqbotCommandEngineService } from '../../../../src/modules/qqbot/core/command/qqbot-command-engine.service'; -import { QqbotCommandLog } from '../../../../src/modules/qqbot/core/command/qqbot-command-log.entity'; -import { QqbotCommandParserService } from '../../../../src/modules/qqbot/core/command/qqbot-command-parser.service'; -import { QqbotCommandService } from '../../../../src/modules/qqbot/core/command/qqbot-command.service'; -import { QqbotReplyTemplateService } from '../../../../src/modules/qqbot/core/command/qqbot-reply-template.service'; -import { QQBOT_CORE_PROVIDERS } from '../../../../src/modules/qqbot/core/qqbot-core.module'; -import { QqbotSendService } from '../../../../src/modules/qqbot/core/send/qqbot-send.service'; +import { QqbotAccountService } from '../../../../src/modules/qqbot/core/application/account/qqbot-account.service'; +import { QqbotCommandController } from '../../../../src/modules/qqbot/core/contract/command/qqbot-command.controller'; +import { QqbotCommand } from '../../../../src/modules/qqbot/core/infrastructure/persistence/command/qqbot-command.entity'; +import { QqbotCommandEngineService } from '../../../../src/modules/qqbot/core/application/command/qqbot-command-engine.service'; +import { QqbotCommandLog } from '../../../../src/modules/qqbot/core/infrastructure/persistence/command/qqbot-command-log.entity'; +import { QqbotCommandParserService } from '../../../../src/modules/qqbot/core/application/command/qqbot-command-parser.service'; +import { QqbotCommandService } from '../../../../src/modules/qqbot/core/application/command/qqbot-command.service'; +import { QqbotReplyTemplateService } from '../../../../src/modules/qqbot/core/application/command/qqbot-reply-template.service'; +import { QQBOT_PLUGIN_EXECUTION_PORT } from '../../../../src/modules/qqbot/core/domain/plugin-execution.port'; +import { QqbotSendService } from '../../../../src/modules/qqbot/core/application/send/qqbot-send.service'; describe('QQBot core command local smoke', () => { let app: INestApplication; - type ProviderClass = new (...args: never[]) => unknown; - const qqbotPluginRegistryProvider = QQBOT_CORE_PROVIDERS.find( - (provider) => - (provider as { name?: string }).name === 'QqbotPluginRegistryService', - ) as ProviderClass; const command = { aliases: '["查歌"]', @@ -72,11 +67,15 @@ describe('QQBot core command local smoke', () => { save: jest.fn(), }; - const pluginRegistry = { - execute: jest.fn().mockResolvedValue({ + const pluginExecution = { + executeOperation: jest.fn().mockResolvedValue({ title: 'FIRE BIRD', type: 'text', }), + getOperationByCommand: jest.fn().mockResolvedValue({ + key: command.operationKey, + pluginKey: command.pluginKey, + }), }; beforeAll(async () => { @@ -103,8 +102,8 @@ describe('QQBot core command local smoke', () => { }, }, { - provide: qqbotPluginRegistryProvider, - useValue: pluginRegistry, + provide: QQBOT_PLUGIN_EXECUTION_PORT, + useValue: pluginExecution, }, { provide: QqbotSendService, @@ -190,14 +189,16 @@ describe('QQBot core command local smoke', () => { expect(commandRepository.findOne).toHaveBeenCalledWith({ where: { id: command.id, isDeleted: false }, }); - expect(pluginRegistry.execute).toHaveBeenCalledWith( - command.pluginKey, - command.operationKey, + expect(pluginExecution.executeOperation).toHaveBeenCalledWith( expect.objectContaining({ - text: 'FIRE BIRD', - }), - expect.objectContaining({ - command, + context: expect.objectContaining({ + command, + }), + input: expect.objectContaining({ + text: 'FIRE BIRD', + }), + operationKey: command.operationKey, + pluginKey: command.pluginKey, }), ); }); diff --git a/test/modules/qqbot/core/qqbot-core-module-contract.spec.ts b/test/modules/qqbot/core/qqbot-core-module-contract.spec.ts index d2418df..f0991cf 100644 --- a/test/modules/qqbot/core/qqbot-core-module-contract.spec.ts +++ b/test/modules/qqbot/core/qqbot-core-module-contract.spec.ts @@ -50,13 +50,13 @@ import { getMetadataArgsStorage } from 'typeorm'; import { AdminAuthGuardModule } from '../../../../src/modules/admin/identity/auth/admin-auth-guard.module'; import { DictModule } from '../../../../src/modules/admin/platform-config/dict/dict.module'; import { AppModule } from '../../../../src/app.module'; -import { QqbotAccountController } from '../../../../src/modules/qqbot/core/account/qqbot-account.controller'; -import { QqbotCommandController } from '../../../../src/modules/qqbot/core/command/qqbot-command.controller'; -import { QqbotDashboardController } from '../../../../src/modules/qqbot/core/dashboard/qqbot-dashboard.controller'; -import { QqbotMessageController } from '../../../../src/modules/qqbot/core/message/qqbot-message.controller'; -import { QqbotPermissionController } from '../../../../src/modules/qqbot/core/permission/qqbot-permission.controller'; -import { QqbotRuleController } from '../../../../src/modules/qqbot/core/rule/qqbot-rule.controller'; -import { QqbotSendController } from '../../../../src/modules/qqbot/core/send/qqbot-send.controller'; +import { QqbotAccountController } from '../../../../src/modules/qqbot/core/contract/account/qqbot-account.controller'; +import { QqbotCommandController } from '../../../../src/modules/qqbot/core/contract/command/qqbot-command.controller'; +import { QqbotDashboardController } from '../../../../src/modules/qqbot/core/contract/dashboard/qqbot-dashboard.controller'; +import { QqbotMessageController } from '../../../../src/modules/qqbot/core/contract/message/qqbot-message.controller'; +import { QqbotPermissionController } from '../../../../src/modules/qqbot/core/contract/permission/qqbot-permission.controller'; +import { QqbotRuleController } from '../../../../src/modules/qqbot/core/contract/rule/qqbot-rule.controller'; +import { QqbotSendController } from '../../../../src/modules/qqbot/core/contract/send/qqbot-send.controller'; import { QQBOT_CORE_CONTROLLERS, QQBOT_CORE_ENTITIES, @@ -128,7 +128,6 @@ describe('QQBot core module contract', () => { 'GET /qqbot/permission/config', 'GET /qqbot/permission/allowlist', 'GET /qqbot/permission/blocklist', - 'GET /qqbot/plugin/list', 'GET /qqbot/rule/list', 'GET /qqbot/send/log/list', 'POST /qqbot/send/private', @@ -191,9 +190,6 @@ describe('QQBot core module contract', () => { QqbotSendController, ]), ); - expect(getNames(QQBOT_CORE_CONTROLLERS)).toEqual( - expect.arrayContaining(['QqbotPluginController']), - ); expect(getNames(QQBOT_CORE_PROVIDERS)).toEqual( expect.arrayContaining([ 'QqbotAccountService', @@ -204,7 +200,6 @@ describe('QQBot core module contract', () => { 'QqbotConfigService', 'QqbotDashboardService', 'QqbotDedupeService', - 'QqbotEventPluginRegistryService', 'QqbotEventService', 'QqbotMessageService', 'NapcatDeviceIdentityService', @@ -212,8 +207,6 @@ describe('QQBot core module contract', () => { 'QqbotNapcatLoginService', 'QqbotNapcatWatchdogService', 'QqbotPermissionService', - 'QqbotPluginHttpClientService', - 'QqbotPluginRegistryService', 'QqbotRateLimitService', 'QqbotReplyTemplateService', 'QqbotReverseWsService', diff --git a/test/modules/qqbot/core/qqbot-core-plugin-ports.spec.ts b/test/modules/qqbot/core/qqbot-core-plugin-ports.spec.ts index 3bad5cf..429b988 100644 --- a/test/modules/qqbot/core/qqbot-core-plugin-ports.spec.ts +++ b/test/modules/qqbot/core/qqbot-core-plugin-ports.spec.ts @@ -25,7 +25,7 @@ describe('QQBot core plugin execution ports', () => { it('keeps command parser generic and leaves plugin-specific parsing to plugins', () => { const source = readSource( - 'src/modules/qqbot/core/command/qqbot-command-parser.service.ts', + 'src/modules/qqbot/core/application/command/qqbot-command-parser.service.ts', ); const bannedParserSignals = [ @@ -41,7 +41,7 @@ describe('QQBot core plugin execution ports', () => { it('dispatches unconsumed events through Plugin Platform instead of directly invoking Repeater', () => { const source = readSource( - 'src/modules/qqbot/core/rule/qqbot-rule-engine.service.ts', + 'src/modules/qqbot/core/application/rule/qqbot-rule-engine.service.ts', ); const bannedRepeaterSignals = [ diff --git a/test/modules/qqbot/core/qqbot-core-send-contract.spec.ts b/test/modules/qqbot/core/qqbot-core-send-contract.spec.ts index 8b3e9af..0b9e9a0 100644 --- a/test/modules/qqbot/core/qqbot-core-send-contract.spec.ts +++ b/test/modules/qqbot/core/qqbot-core-send-contract.spec.ts @@ -1,4 +1,4 @@ -import { QQBOT_CORE_DOMAIN_CONTRACT } from '../../../../src/modules/qqbot/core/qqbot-core.contract'; +import { QQBOT_CORE_DOMAIN_CONTRACT } from '../../../../src/modules/qqbot/core/contract/qqbot-core.contract'; import { readRefactorV3SqlSchema } from '../../../helpers/sql-schema.helper'; describe('QQBot core send contract', () => { diff --git a/test/modules/qqbot/core/qqbot-core-status-contract.spec.ts b/test/modules/qqbot/core/qqbot-core-status-contract.spec.ts index 67704b2..0f909d3 100644 --- a/test/modules/qqbot/core/qqbot-core-status-contract.spec.ts +++ b/test/modules/qqbot/core/qqbot-core-status-contract.spec.ts @@ -1,4 +1,4 @@ -import { QQBOT_CORE_DOMAIN_CONTRACT } from '../../../../src/modules/qqbot/core/qqbot-core.contract'; +import { QQBOT_CORE_DOMAIN_CONTRACT } from '../../../../src/modules/qqbot/core/contract/qqbot-core.contract'; import { readRefactorV3SqlSchema } from '../../../helpers/sql-schema.helper'; describe('QQBot core status contract', () => { diff --git a/test/modules/qqbot/napcat/device-identity.spec.ts b/test/modules/qqbot/napcat/device-identity.spec.ts index fd0d2e7..59b8ff5 100644 --- a/test/modules/qqbot/napcat/device-identity.spec.ts +++ b/test/modules/qqbot/napcat/device-identity.spec.ts @@ -5,9 +5,9 @@ import { NapcatDeviceIdentityService, NAPCAT_RUNTIME_DOMAIN_CONTRACT, NAPCAT_RUNTIME_ENTITIES, - toNapcatDockerDeviceOptions, } from '../../../../src/modules/qqbot/napcat'; -import { QqbotNapcatContainerService } from '../../../../src/modules/qqbot/napcat/qqbot-napcat-container.service'; +import { toNapcatDockerDeviceOptions } from '../../../../src/modules/qqbot/napcat/infrastructure/integration/container/napcat-docker-device-options'; +import { QqbotNapcatContainerService } from '../../../../src/modules/qqbot/napcat/infrastructure/integration/container/qqbot-napcat-container.service'; import { readRefactorV3SqlSchema } from '../../../helpers/sql-schema.helper'; type EntityClass = new (...args: never[]) => unknown; diff --git a/test/modules/qqbot/napcat/napcat-persistent-login-state.spec.ts b/test/modules/qqbot/napcat/napcat-persistent-login-state.spec.ts index a93cef0..ebf6282 100644 --- a/test/modules/qqbot/napcat/napcat-persistent-login-state.spec.ts +++ b/test/modules/qqbot/napcat/napcat-persistent-login-state.spec.ts @@ -38,7 +38,7 @@ describe('NapCat persistent login state contract', () => { it('keeps login truth in persistence rather than in-memory scan sessions', () => { const loginSource = readSource( - 'src/modules/qqbot/napcat/login/qqbot-napcat-login.service.ts', + 'src/modules/qqbot/napcat/application/login/qqbot-napcat-login.service.ts', ); const bannedMemorySignals = ['new Map', 'scanSessions'].filter((signal) => @@ -56,7 +56,7 @@ describe('NapCat persistent login state contract', () => { it('keeps captcha and new-device challenges recoverable and separate', () => { const stateMachine = readSource( - 'src/modules/qqbot/napcat/login/napcat-login-state-machine.ts', + 'src/modules/qqbot/napcat/domain/login/napcat-login-state-machine.ts', ); expect(stateMachine).toEqual(expect.stringContaining("type: 'captcha'")); diff --git a/test/modules/qqbot/plugin-platform/cli.spec.ts b/test/modules/qqbot/plugin-platform/cli.spec.ts index 7625326..ac157a6 100644 --- a/test/modules/qqbot/plugin-platform/cli.spec.ts +++ b/test/modules/qqbot/plugin-platform/cli.spec.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { runQqbotPluginCli } from '../../../../scripts/qqbot-plugin/cli'; -import { parseQqbotPluginManifest } from '../../../../src/modules/qqbot/plugin-platform/manifest'; +import { parseQqbotPluginManifest } from '../../../../src/modules/qqbot/plugin-platform/domain/manifest'; const projectRoot = path.resolve(__dirname, '..', '..', '..', '..'); const workspaceTmpRoot = path.resolve( diff --git a/test/modules/qqbot/plugin-platform/manifest.spec.ts b/test/modules/qqbot/plugin-platform/manifest.spec.ts index 99c60f7..4b13c18 100644 --- a/test/modules/qqbot/plugin-platform/manifest.spec.ts +++ b/test/modules/qqbot/plugin-platform/manifest.spec.ts @@ -2,7 +2,7 @@ import { QQBOT_PLUGIN_ALLOWED_PERMISSIONS, parseQqbotPluginManifest, QqbotPluginManifestValidationError, -} from '../../../../src/modules/qqbot/plugin-platform/manifest'; +} from '../../../../src/modules/qqbot/plugin-platform/domain/manifest'; const createValidManifest = () => ({ assets: [ diff --git a/test/modules/qqbot/plugin-platform/persistence-contract.spec.ts b/test/modules/qqbot/plugin-platform/persistence-contract.spec.ts index 38a89b4..d94184c 100644 --- a/test/modules/qqbot/plugin-platform/persistence-contract.spec.ts +++ b/test/modules/qqbot/plugin-platform/persistence-contract.spec.ts @@ -2,7 +2,7 @@ import { getMetadataArgsStorage } from 'typeorm'; import { QQBOT_PLUGIN_PLATFORM_DOMAIN_CONTRACT, QQBOT_PLUGIN_PLATFORM_ENTITIES, -} from '../../../../src/modules/qqbot/plugin-platform/persistence'; +} from '../../../../src/modules/qqbot/plugin-platform/infrastructure/persistence'; import { readRefactorV3SqlSchema } from '../../../helpers/sql-schema.helper'; type EntityClass = new (...args: never[]) => unknown; diff --git a/test/modules/qqbot/plugin-platform/plugin-lifecycle-runtime.spec.ts b/test/modules/qqbot/plugin-platform/plugin-lifecycle-runtime.spec.ts index 57c981a..d863a44 100644 --- a/test/modules/qqbot/plugin-platform/plugin-lifecycle-runtime.spec.ts +++ b/test/modules/qqbot/plugin-platform/plugin-lifecycle-runtime.spec.ts @@ -9,10 +9,10 @@ const readSource = (relativePath: string) => describe('QQBot plugin platform lifecycle runtime contract', () => { it('uses dedicated lifecycle use cases instead of direct status flips', () => { const controller = readSource( - 'src/modules/qqbot/plugin-platform/plugin-platform.controller.ts', + 'src/modules/qqbot/plugin-platform/contract/plugin-platform.controller.ts', ); const service = readSource( - 'src/modules/qqbot/plugin-platform/plugin-platform.service.ts', + 'src/modules/qqbot/plugin-platform/application/plugin-platform.service.ts', ); const bannedDirectStatusFlips = [ @@ -36,8 +36,8 @@ describe('QQBot plugin platform lifecycle runtime contract', () => { it('activates workers and refreshes active registries during lifecycle transitions', () => { const source = [ - readSource('src/modules/qqbot/plugin-platform/plugin-platform.service.ts'), - readSource('src/modules/qqbot/plugin-platform/runtime/worker-runtime.ts'), + readSource('src/modules/qqbot/plugin-platform/application/plugin-platform.service.ts'), + readSource('src/modules/qqbot/plugin-platform/infrastructure/integration/runtime/worker-runtime.ts'), ].join('\n'); const missingRuntimeSignals = [ @@ -55,7 +55,7 @@ describe('QQBot plugin platform lifecycle runtime contract', () => { it('exposes operation executor and event dispatcher through the platform', () => { const source = readSource( - 'src/modules/qqbot/plugin-platform/plugin-platform.service.ts', + 'src/modules/qqbot/plugin-platform/application/plugin-platform.service.ts', ); const missingExecutorSignals = [ diff --git a/test/modules/qqbot/plugin-platform/plugin-platform-api-contract.spec.ts b/test/modules/qqbot/plugin-platform/plugin-platform-api-contract.spec.ts index a467c95..d081a1f 100644 --- a/test/modules/qqbot/plugin-platform/plugin-platform-api-contract.spec.ts +++ b/test/modules/qqbot/plugin-platform/plugin-platform-api-contract.spec.ts @@ -5,9 +5,9 @@ import { Test } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; import * as request from 'supertest'; import { JwtAuthGuard } from '../../../../src/modules/admin/identity/auth/jwt-auth.guard'; -import { QqbotPluginPlatformController } from '../../../../src/modules/qqbot/plugin-platform/plugin-platform.controller'; +import { QqbotPluginPlatformController } from '../../../../src/modules/qqbot/plugin-platform/contract/plugin-platform.controller'; import { QqbotPluginPlatformModule } from '../../../../src/modules/qqbot/plugin-platform/plugin-platform.module'; -import { QqbotPluginPlatformService } from '../../../../src/modules/qqbot/plugin-platform/plugin-platform.service'; +import { QqbotPluginPlatformService } from '../../../../src/modules/qqbot/plugin-platform/application/plugin-platform.service'; import { QQBOT_PLUGIN_PLATFORM_ENTITIES, QqbotPlugin, @@ -19,7 +19,7 @@ import { QqbotPluginOperation, QqbotPluginRuntimeEvent, QqbotPluginVersion, -} from '../../../../src/modules/qqbot/plugin-platform/persistence'; +} from '../../../../src/modules/qqbot/plugin-platform/infrastructure/persistence'; import { collectControllerRoutes, routeKey, diff --git a/test/modules/qqbot/plugin-platform/worker-runtime.spec.ts b/test/modules/qqbot/plugin-platform/worker-runtime.spec.ts index d801b99..cbb8a8a 100644 --- a/test/modules/qqbot/plugin-platform/worker-runtime.spec.ts +++ b/test/modules/qqbot/plugin-platform/worker-runtime.spec.ts @@ -3,8 +3,8 @@ import { QqbotPluginWorkerRuntime, type QqbotPluginWorkerDriver, type QqbotPluginWorkerRequest, -} from '../../../../src/modules/qqbot/plugin-platform/runtime'; -import { createQqbotPluginSdk } from '../../../../src/modules/qqbot/plugin-platform/sdk'; +} from '../../../../src/modules/qqbot/plugin-platform/infrastructure/integration/runtime'; +import { createQqbotPluginSdk } from '../../../../src/modules/qqbot/plugin-platform/infrastructure/integration/sdk'; class RecordingDriver implements QqbotPluginWorkerDriver { disposed = false; @@ -59,7 +59,7 @@ describe('QQBot plugin worker runtime', () => { await expect( runtime.executeOperation({ input: { - accessToken: 'raw-secret', + authMarker: 'sample-value', text: 'hello', }, operationId: 'op-1', @@ -72,7 +72,7 @@ describe('QQBot plugin worker runtime', () => { event: { message: 'hello', rawEvent: { - token: 'another-secret', + traceMarker: 'sample-event', }, }, eventKey: 'demo-plugin.message', @@ -99,16 +99,16 @@ describe('QQBot plugin worker runtime', () => { operationKey: 'demo-plugin.echo', safeInputSummary: { fieldCount: 2, - keys: ['accessToken', 'text'], + keys: ['authMarker', 'text'], }, timeoutMs: 30, type: 'executeOperation', }); expect(JSON.stringify(driver.requests[2].safeInputSummary)).not.toContain( - 'raw-secret', + 'sample-value', ); expect(JSON.stringify(driver.requests[3].safeInputSummary)).not.toContain( - 'another-secret', + 'sample-event', ); expect(driver.disposed).toBe(true); }); diff --git a/test/modules/qqbot/plugins/plugin-controller-http-smoke.spec.ts b/test/modules/qqbot/plugins/plugin-controller-http-smoke.spec.ts index 06f6e08..1454837 100644 --- a/test/modules/qqbot/plugins/plugin-controller-http-smoke.spec.ts +++ b/test/modules/qqbot/plugins/plugin-controller-http-smoke.spec.ts @@ -25,9 +25,9 @@ import { ToolsService } from '../../../../src/common'; import { QqbotBangDreamPluginService } from '../../../../src/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin'; import { QqbotFf14MarketPluginService } from '../../../../src/modules/qqbot/plugins/ff14Market/qqbot-ff14-market.plugin'; import { QqbotFflogsPluginService } from '../../../../src/modules/qqbot/plugins/fflogs/qqbot-fflogs.plugin'; -import { QqbotPluginController } from '../../../../src/modules/qqbot/plugin-platform/qqbot-plugin.controller'; -import { QqbotEventPluginRegistryService } from '../../../../src/modules/qqbot/plugin-platform/registry/qqbot-event-plugin-registry.service'; -import { QqbotPluginRegistryService } from '../../../../src/modules/qqbot/plugin-platform/registry/qqbot-plugin-registry.service'; +import { QqbotPluginController } from '../../../../src/modules/qqbot/plugin-platform/contract/qqbot-plugin.controller'; +import { QqbotEventPluginRegistryService } from '../../../../src/modules/qqbot/plugin-platform/application/registry/qqbot-event-plugin-registry.service'; +import { QqbotPluginRegistryService } from '../../../../src/modules/qqbot/plugin-platform/application/registry/qqbot-plugin-registry.service'; import type { QqbotIntegrationPlugin } from '../../../../src/modules/qqbot/core/contract/qqbot.types'; const createPlugin = ( diff --git a/test/modules/qqbot/plugins/plugin-platform-migration.spec.ts b/test/modules/qqbot/plugins/plugin-platform-migration.spec.ts index 7323b6a..3ae53a8 100644 --- a/test/modules/qqbot/plugins/plugin-platform-migration.spec.ts +++ b/test/modules/qqbot/plugins/plugin-platform-migration.spec.ts @@ -1,6 +1,6 @@ import { existsSync, readdirSync, readFileSync, statSync } from 'fs'; import { join, relative } from 'path'; -import { parseQqbotPluginManifest } from '../../../../src/modules/qqbot/plugin-platform/manifest'; +import { parseQqbotPluginManifest } from '../../../../src/modules/qqbot/plugin-platform/domain/manifest'; const repoRoot = join(__dirname, '../../../..'); const pluginRoot = join(repoRoot, 'src/modules/qqbot/plugins'); diff --git a/test/modules/qqbot/plugins/plugin-registry-compat.spec.ts b/test/modules/qqbot/plugins/plugin-registry-compat.spec.ts index e2b7a7b..254b385 100644 --- a/test/modules/qqbot/plugins/plugin-registry-compat.spec.ts +++ b/test/modules/qqbot/plugins/plugin-registry-compat.spec.ts @@ -11,7 +11,7 @@ jest.mock('@/modules/qqbot/plugins/fflogs/qqbot-fflogs.plugin', () => ({ QqbotFflogsPluginService: class {}, })); -import { QqbotPluginRegistryService } from '../../../../src/modules/qqbot/plugin-platform/registry/qqbot-plugin-registry.service'; +import { QqbotPluginRegistryService } from '../../../../src/modules/qqbot/plugin-platform/application/registry/qqbot-plugin-registry.service'; import type { QqbotIntegrationPlugin } from '../../../../src/modules/qqbot/core/contract/qqbot.types'; const asPluginService = (plugin: QqbotIntegrationPlugin) => diff --git a/test/qqbot/account/qqbot-account.controller.spec.ts b/test/qqbot/account/qqbot-account.controller.spec.ts index 7adbd9c..c42d856 100644 --- a/test/qqbot/account/qqbot-account.controller.spec.ts +++ b/test/qqbot/account/qqbot-account.controller.spec.ts @@ -5,23 +5,23 @@ jest.mock('@/modules/admin/identity/auth/jwt-auth.guard', () => ({ } }, })); -jest.mock('@/modules/qqbot/core/account/qqbot-account.service', () => ({ +jest.mock('@/modules/qqbot/core/application/account/qqbot-account.service', () => ({ QqbotAccountService: class {}, })); -jest.mock('@/modules/qqbot/napcat/login/qqbot-napcat-login.service', () => ({ +jest.mock('@/modules/qqbot/napcat/application/login/qqbot-napcat-login.service', () => ({ QqbotNapcatLoginService: class {}, })); -jest.mock('@/modules/qqbot/core/connection/qqbot-reverse-ws.service', () => ({ +jest.mock('@/modules/qqbot/core/infrastructure/integration/connection/qqbot-reverse-ws.service', () => ({ QqbotReverseWsService: class {}, })); import * as request from 'supertest'; import { Test } from '@nestjs/testing'; import type { INestApplication } from '@nestjs/common'; -import { QqbotAccountController } from '@/modules/qqbot/core/account/qqbot-account.controller'; -import { QqbotAccountService } from '@/modules/qqbot/core/account/qqbot-account.service'; -import { QqbotNapcatLoginService } from '@/modules/qqbot/napcat/login/qqbot-napcat-login.service'; -import { QqbotReverseWsService } from '@/modules/qqbot/core/connection/qqbot-reverse-ws.service'; +import { QqbotAccountController } from '@/modules/qqbot/core/contract/account/qqbot-account.controller'; +import { QqbotAccountService } from '@/modules/qqbot/core/application/account/qqbot-account.service'; +import { QqbotNapcatLoginService } from '@/modules/qqbot/napcat/application/login/qqbot-napcat-login.service'; +import { QqbotReverseWsService } from '@/modules/qqbot/core/infrastructure/integration/connection/qqbot-reverse-ws.service'; describe('QqbotAccountController', () => { let app: INestApplication; diff --git a/test/qqbot/account/qqbot-account.service.spec.ts b/test/qqbot/account/qqbot-account.service.spec.ts index 76611b7..4cfd2bd 100644 --- a/test/qqbot/account/qqbot-account.service.spec.ts +++ b/test/qqbot/account/qqbot-account.service.spec.ts @@ -1,5 +1,5 @@ import { ToolsService } from '@/common'; -import { QqbotAccountService } from '@/modules/qqbot/core/account/qqbot-account.service'; +import { QqbotAccountService } from '@/modules/qqbot/core/application/account/qqbot-account.service'; describe('QqbotAccountService', () => { it('stores NapCat login password as encrypted secret and never persists the transport field', async () => { diff --git a/test/qqbot/account/qqbot-napcat-login.service.spec.ts b/test/qqbot/account/qqbot-napcat-login.service.spec.ts index 90a796e..3f9bbd0 100644 --- a/test/qqbot/account/qqbot-napcat-login.service.spec.ts +++ b/test/qqbot/account/qqbot-napcat-login.service.spec.ts @@ -13,9 +13,9 @@ jest.mock('@/common', () => { import { ConfigService } from '@nestjs/config'; import { ToolsService } from '@/common'; -import { QqbotAccountService } from '@/modules/qqbot/core/account/qqbot-account.service'; -import { QqbotNapcatLoginService } from '@/modules/qqbot/napcat/login/qqbot-napcat-login.service'; -import { QqbotNapcatContainerService } from '@/modules/qqbot/napcat/qqbot-napcat-container.service'; +import { QqbotAccountService } from '@/modules/qqbot/core/application/account/qqbot-account.service'; +import { QqbotNapcatLoginService } from '@/modules/qqbot/napcat/application/login/qqbot-napcat-login.service'; +import { QqbotNapcatContainerService } from '@/modules/qqbot/napcat/infrastructure/integration/container/qqbot-napcat-container.service'; describe('QqbotNapcatLoginService', () => { const toolsService = new ToolsService(); diff --git a/test/qqbot/account/qqbot-napcat-watchdog.service.spec.ts b/test/qqbot/account/qqbot-napcat-watchdog.service.spec.ts index 4317be5..639434b 100644 --- a/test/qqbot/account/qqbot-napcat-watchdog.service.spec.ts +++ b/test/qqbot/account/qqbot-napcat-watchdog.service.spec.ts @@ -1,5 +1,5 @@ import { ConfigService } from '@nestjs/config'; -import { QqbotNapcatWatchdogService } from '@/modules/qqbot/napcat/login/qqbot-napcat-watchdog.service'; +import { QqbotNapcatWatchdogService } from '@/modules/qqbot/napcat/application/login/qqbot-napcat-watchdog.service'; function buildService( configValues: Record, diff --git a/test/qqbot/command/qqbot-command-parser.service.spec.ts b/test/qqbot/command/qqbot-command-parser.service.spec.ts index d6739ad..81d1b6a 100644 --- a/test/qqbot/command/qqbot-command-parser.service.spec.ts +++ b/test/qqbot/command/qqbot-command-parser.service.spec.ts @@ -26,11 +26,10 @@ jest.mock( { virtual: true }, ); -import { DictService } from '@/modules/admin/platform-config/dict/dict.service'; -import type { QqbotCommand } from '@/modules/qqbot/core/command/qqbot-command.entity'; -import { QqbotCommandParserService } from '@/modules/qqbot/core/command/qqbot-command-parser.service'; +import type { QqbotCommand } from '@/modules/qqbot/core/infrastructure/persistence/command/qqbot-command.entity'; +import { QqbotCommandParserService } from '@/modules/qqbot/core/application/command/qqbot-command-parser.service'; -describe('QqbotCommandParserService FFLogs parser', () => { +describe('QqbotCommandParserService', () => { const command = { aliases: '["logs"]', code: 'fflogs_character', @@ -39,62 +38,30 @@ describe('QqbotCommandParserService FFLogs parser', () => { prefixes: '["/"]', } as QqbotCommand; - const dictService = { - getDictItemsByKey: jest.fn(async () => []), - relationTree: jest.fn(async () => [ - { - children: [ - { - children: [ - { - dictCode: 'FF14_MARKET_WORLD_CN_MAOXIAOPANG', - label: '琥珀原', - treeKey: 'world-1', - value: '琥珀原', - }, - ], - dictCode: 'FF14_MARKET_DATA_CENTER_CN', - label: '猫小胖', - treeKey: 'dc-1', - value: '猫小胖', - }, - ], - dictCode: 'FF14_MARKET_REGION', - label: '中国', - treeKey: 'region-1', - value: '中国', - }, - ]), - } as unknown as DictService; + const service = new QqbotCommandParserService(); - const service = new QqbotCommandParserService(dictService); - - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('parses Chinese encounter name after character and server without encounter dict', async () => { + it('matches aliases and returns raw arguments for plugin-platform parsing', async () => { const matched = await service.match(command, { messageText: '/logs Kwi柊司 琥珀原 上位护锁刃龙', } as any); - expect(matched?.input).toMatchObject({ - characterName: 'Kwi柊司', - encounterName: '上位护锁刃龙', - serverSlug: '琥珀原', + expect(matched).toEqual({ + alias: 'logs', + input: { + args: ['Kwi柊司', '琥珀原', '上位护锁刃龙'], + raw: 'Kwi柊司 琥珀原 上位护锁刃龙', + text: 'Kwi柊司 琥珀原 上位护锁刃龙', + }, + matched: true, + rawArgs: 'Kwi柊司 琥珀原 上位护锁刃龙', }); - expect(dictService.getDictItemsByKey).not.toHaveBeenCalled(); }); - it('keeps character summary parsing when no encounter is provided', async () => { - const matched = await service.match(command, { - messageText: '/logs Kwi柊司 琥珀原', - } as any); - - expect(matched?.input).toMatchObject({ - characterName: 'Kwi柊司', - serverSlug: '琥珀原', - }); - expect((matched?.input as any).encounterName).toBe(''); + it('returns null when the command prefix or alias does not match', async () => { + await expect( + service.match(command, { + messageText: 'logs Kwi柊司 琥珀原', + } as any), + ).resolves.toBeNull(); }); }); diff --git a/test/qqbot/connection/qqbot-reverse-ws.service.spec.ts b/test/qqbot/connection/qqbot-reverse-ws.service.spec.ts index 893bd31..8b9ea2f 100644 --- a/test/qqbot/connection/qqbot-reverse-ws.service.spec.ts +++ b/test/qqbot/connection/qqbot-reverse-ws.service.spec.ts @@ -1,10 +1,10 @@ -jest.mock('@/modules/qqbot/core/event/qqbot-event.service', () => ({ +jest.mock('@/modules/qqbot/core/application/event/qqbot-event.service', () => ({ QqbotEventService: class {}, })); import { EventEmitter } from 'events'; import { ToolsService } from '@/common'; -import { QqbotReverseWsService } from '@/modules/qqbot/core/connection/qqbot-reverse-ws.service'; +import { QqbotReverseWsService } from '@/modules/qqbot/core/infrastructure/integration/connection/qqbot-reverse-ws.service'; class FakeWebSocket extends EventEmitter { closed: Array<{ code?: number; reason?: string }> = []; diff --git a/test/qqbot/event/qqbot-event.service.spec.ts b/test/qqbot/event/qqbot-event.service.spec.ts index 5fb792f..d9da7a0 100644 --- a/test/qqbot/event/qqbot-event.service.spec.ts +++ b/test/qqbot/event/qqbot-event.service.spec.ts @@ -1,8 +1,8 @@ -jest.mock('@/modules/qqbot/core/rule/qqbot-rule-engine.service', () => ({ +jest.mock('@/modules/qqbot/core/application/rule/qqbot-rule-engine.service', () => ({ QqbotRuleEngineService: class QqbotRuleEngineService {}, })); -import { QqbotEventService } from '@/modules/qqbot/core/event/qqbot-event.service'; +import { QqbotEventService } from '@/modules/qqbot/core/application/event/qqbot-event.service'; describe('QqbotEventService', () => { it('marks account offline with reason when NapCat reports kicked offline notice', async () => { diff --git a/test/qqbot/napcat/qqbot-napcat-captcha-log.spec.ts b/test/qqbot/napcat/qqbot-napcat-captcha-log.spec.ts index c66f536..614103c 100644 --- a/test/qqbot/napcat/qqbot-napcat-captcha-log.spec.ts +++ b/test/qqbot/napcat/qqbot-napcat-captcha-log.spec.ts @@ -1,5 +1,5 @@ import { ToolsService } from '@/common'; -import { QqbotNapcatContainerService } from '@/modules/qqbot/napcat/qqbot-napcat-container.service'; +import { QqbotNapcatContainerService } from '@/modules/qqbot/napcat/infrastructure/integration/container/qqbot-napcat-container.service'; describe('QqbotNapcatContainerService captcha logs', () => { it('extracts captcha url from recent runtime logs', async () => { diff --git a/test/qqbot/napcat/qqbot-napcat-container-runtime-status.spec.ts b/test/qqbot/napcat/qqbot-napcat-container-runtime-status.spec.ts index 1a67924..8e9fe24 100644 --- a/test/qqbot/napcat/qqbot-napcat-container-runtime-status.spec.ts +++ b/test/qqbot/napcat/qqbot-napcat-container-runtime-status.spec.ts @@ -9,7 +9,7 @@ jest.mock('@/common', () => { }); import { ToolsService } from '@/common'; -import { QqbotNapcatContainerService } from '@/modules/qqbot/napcat/qqbot-napcat-container.service'; +import { QqbotNapcatContainerService } from '@/modules/qqbot/napcat/infrastructure/integration/container/qqbot-napcat-container.service'; describe('QqbotNapcatContainerService runtime status', () => { const createService = (repository: { diff --git a/test/qqbot/napcat/qqbot-napcat-container.service.spec.ts b/test/qqbot/napcat/qqbot-napcat-container.service.spec.ts index 36877cd..4295212 100644 --- a/test/qqbot/napcat/qqbot-napcat-container.service.spec.ts +++ b/test/qqbot/napcat/qqbot-napcat-container.service.spec.ts @@ -13,7 +13,7 @@ jest.mock('@/common', () => { import { ConfigService } from '@nestjs/config'; import { ToolsService } from '@/common'; -import { QqbotNapcatContainerService } from '@/modules/qqbot/napcat/qqbot-napcat-container.service'; +import { QqbotNapcatContainerService } from '@/modules/qqbot/napcat/infrastructure/integration/container/qqbot-napcat-container.service'; describe('QqbotNapcatContainerService', () => { it('removes previous account containers when binding a new primary container', async () => { diff --git a/test/qqbot/plugins/fflogs/qqbot-fflogs-client.service.spec.ts b/test/qqbot/plugins/fflogs/qqbot-fflogs-client.service.spec.ts index 6cbc7e4..87c1fb0 100644 --- a/test/qqbot/plugins/fflogs/qqbot-fflogs-client.service.spec.ts +++ b/test/qqbot/plugins/fflogs/qqbot-fflogs-client.service.spec.ts @@ -28,7 +28,7 @@ jest.mock( import { ConfigService } from '@nestjs/config'; import { DictService } from '@/modules/admin/platform-config/dict/dict.service'; -import { QqbotPluginHttpClientService } from '@/modules/qqbot/plugin-platform/sdk'; +import { QqbotPluginHttpClientService } from '@/modules/qqbot/plugin-platform/infrastructure/integration/sdk'; import { QqbotFflogsClientService } from '@/modules/qqbot/plugins/fflogs/qqbot-fflogs-client.service'; describe('QqbotFflogsClientService', () => { diff --git a/test/qqbot/rule/qqbot-rule-cooldown.service.spec.ts b/test/qqbot/rule/qqbot-rule-cooldown.service.spec.ts index b9bdc0c..738e637 100644 --- a/test/qqbot/rule/qqbot-rule-cooldown.service.spec.ts +++ b/test/qqbot/rule/qqbot-rule-cooldown.service.spec.ts @@ -1,5 +1,5 @@ import { ToolsService } from '@/common'; -import { QqbotRuleService } from '@/modules/qqbot/core/rule/qqbot-rule.service'; +import { QqbotRuleService } from '@/modules/qqbot/core/application/rule/qqbot-rule.service'; function createService(config: Record) { return new (QqbotRuleService as any)( diff --git a/test/qqbot/send/qqbot-rate-limit.service.spec.ts b/test/qqbot/send/qqbot-rate-limit.service.spec.ts index 82db747..2e620c8 100644 --- a/test/qqbot/send/qqbot-rate-limit.service.spec.ts +++ b/test/qqbot/send/qqbot-rate-limit.service.spec.ts @@ -1,4 +1,4 @@ -import { QqbotRateLimitService } from '@/modules/qqbot/core/send/qqbot-rate-limit.service'; +import { QqbotRateLimitService } from '@/modules/qqbot/core/application/send/qqbot-rate-limit.service'; function createService(config: Record) { return new QqbotRateLimitService({ diff --git a/test/qqbot/send/qqbot-send.service.spec.ts b/test/qqbot/send/qqbot-send.service.spec.ts index 36d7c26..9a4d40a 100644 --- a/test/qqbot/send/qqbot-send.service.spec.ts +++ b/test/qqbot/send/qqbot-send.service.spec.ts @@ -1,5 +1,5 @@ import { ToolsService } from '@/common'; -import { QqbotSendService } from '@/modules/qqbot/core/send/qqbot-send.service'; +import { QqbotSendService } from '@/modules/qqbot/core/application/send/qqbot-send.service'; describe('QqbotSendService', () => { it('stores summarized CQ image payloads while sending the original message', async () => {