refactor: 收敛QQBot核心模块边界
This commit is contained in:
parent
8ada857f57
commit
379e5fb608
@ -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<QqbotPluginCliOptions> = {
|
||||
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) {
|
||||
|
||||
@ -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 =
|
||||
@ -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),
|
||||
@ -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 [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<QqbotCommandLog>,
|
||||
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
|
||||
@ -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',
|
||||
@ -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 {
|
||||
@ -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 {
|
||||
@ -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';
|
||||
|
||||
@ -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 {
|
||||
@ -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 {
|
||||
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
@ -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<QqbotReverseActionSender> {
|
||||
const { QqbotReverseWsService } =
|
||||
await import('../connection/qqbot-reverse-ws.service');
|
||||
await import('../../infrastructure/integration/connection/qqbot-reverse-ws.service');
|
||||
return this.moduleRef.get<QqbotReverseActionSender>(QqbotReverseWsService, {
|
||||
strict: false,
|
||||
});
|
||||
@ -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')
|
||||
@ -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' })
|
||||
@ -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')
|
||||
@ -5,7 +5,7 @@ import type {
|
||||
QqbotCommandParserType,
|
||||
QqbotMessageType,
|
||||
QqbotRuleTargetType,
|
||||
} from '../contract/qqbot.types';
|
||||
} from '../qqbot.types';
|
||||
|
||||
export class QqbotCommandQueryDto implements KtPageQuery {
|
||||
@ApiPropertyOptional()
|
||||
@ -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')
|
||||
@ -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')
|
||||
@ -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 })
|
||||
@ -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')
|
||||
@ -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 })
|
||||
@ -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<QqbotOneBotActionResponse>;
|
||||
};
|
||||
|
||||
export type QqbotRepeaterConversationState = {
|
||||
count: number;
|
||||
lastRepeatedAt?: number;
|
||||
lastText: string;
|
||||
repeatedText: string;
|
||||
updatedAt: number;
|
||||
};
|
||||
|
||||
export type QrcodeRefreshOptions = QrcodeLookupOptions & {
|
||||
fallbackStatus?: NapcatLoginStatus;
|
||||
};
|
||||
|
||||
@ -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')
|
||||
@ -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()
|
||||
@ -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')
|
||||
@ -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()
|
||||
@ -3,7 +3,7 @@ import type {
|
||||
QqbotMessageType,
|
||||
QqbotNormalizedMessage,
|
||||
QqbotOneBotEvent,
|
||||
} from '../contract/qqbot.types';
|
||||
} from '../../contract/qqbot.types';
|
||||
|
||||
export function isOneBotMessageEvent(
|
||||
payload: QqbotOneBotEvent,
|
||||
40
src/modules/qqbot/core/domain/plugin-execution.port.ts
Normal file
40
src/modules/qqbot/core/domain/plugin-execution.port.ts
Normal file
@ -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<string, any>;
|
||||
input: Record<string, any>;
|
||||
operationKey: string;
|
||||
pluginKey: string;
|
||||
};
|
||||
|
||||
export type QqbotPluginEventDispatchInput = {
|
||||
eventKey: 'message';
|
||||
message: QqbotNormalizedMessage;
|
||||
};
|
||||
|
||||
export type QqbotPluginOperationListContext = {
|
||||
selfId?: string;
|
||||
};
|
||||
|
||||
export interface QqbotPluginExecutionPort {
|
||||
dispatchEvent(input: QqbotPluginEventDispatchInput): Promise<boolean>;
|
||||
executeOperation(input: QqbotPluginExecutionInput): Promise<any>;
|
||||
getOperationByCommand(
|
||||
command: QqbotPluginOperationLookup,
|
||||
): Promise<null | QqbotPluginOperationSummary>;
|
||||
listActiveOperations(
|
||||
context?: QqbotPluginOperationListContext,
|
||||
): Promise<QqbotPluginOperationSummary[]>;
|
||||
}
|
||||
@ -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 {
|
||||
@ -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
|
||||
@ -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'], {
|
||||
@ -10,7 +10,7 @@ import type {
|
||||
QqbotConnectionMode,
|
||||
QqbotConnectionRole,
|
||||
QqbotConnectionStatus,
|
||||
} from '../contract/qqbot.types';
|
||||
} from '../../../contract/qqbot.types';
|
||||
|
||||
@Entity('qqbot_account')
|
||||
export class QqbotAccount {
|
||||
@ -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 {
|
||||
@ -9,7 +9,7 @@ import {
|
||||
import type {
|
||||
QqbotCommandParserType,
|
||||
QqbotRuleTargetType,
|
||||
} from '../contract/qqbot.types';
|
||||
} from '../../../contract/qqbot.types';
|
||||
|
||||
@Entity('qqbot_command')
|
||||
export class QqbotCommand {
|
||||
@ -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'])
|
||||
@ -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'])
|
||||
@ -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 {
|
||||
@ -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 {
|
||||
@ -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 {
|
||||
@ -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'])
|
||||
@ -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,
|
||||
|
||||
33
src/modules/qqbot/core/schema/README.md
Normal file
33
src/modules/qqbot/core/schema/README.md
Normal file
@ -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;
|
||||
```
|
||||
@ -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 {
|
||||
@ -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
|
||||
@ -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';
|
||||
|
||||
@ -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;
|
||||
@ -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';
|
||||
|
||||
@ -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;
|
||||
@ -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: [
|
||||
@ -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'])
|
||||
|
||||
@ -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 })
|
||||
|
||||
27
src/modules/qqbot/napcat/schema/README.md
Normal file
27
src/modules/qqbot/napcat/schema/README.md
Normal file
@ -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;
|
||||
```
|
||||
@ -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<string, any>) {
|
||||
return Object.entries(input).reduce<Record<string, any>>(
|
||||
(result, [key, value]) => {
|
||||
if (value !== undefined && value !== '') result[key] = value;
|
||||
return result;
|
||||
},
|
||||
{},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
@ -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') {
|
||||
@ -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')
|
||||
@ -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,
|
||||
@ -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 {}
|
||||
|
||||
27
src/modules/qqbot/plugin-platform/schema/README.md
Normal file
27
src/modules/qqbot/plugin-platform/schema/README.md
Normal file
@ -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;
|
||||
```
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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, '../../../..');
|
||||
|
||||
@ -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', () => {
|
||||
|
||||
@ -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,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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 = [
|
||||
|
||||
@ -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', () => {
|
||||
|
||||
@ -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', () => {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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'"));
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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: [
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 = [
|
||||
|
||||
@ -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,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user