fix: 摘要化 QQBot 图片消息日志
This commit is contained in:
parent
2c833e83f2
commit
73280d379b
@ -128,6 +128,16 @@ export class ToolsService {
|
||||
return this.toTrimmedString(value).replace(/\s+/g, ' ');
|
||||
}
|
||||
|
||||
toStoredMessageText(value: unknown, maxLength = 4000) {
|
||||
const text = this.toTrimmedString(value).replace(
|
||||
/\[CQ:image,file=base64:\/\/([A-Za-z0-9+/=]+)\]/g,
|
||||
(_match, payload: string) =>
|
||||
`[CQ:image,file=base64://<${payload.length} chars>]`,
|
||||
);
|
||||
if (text.length <= maxLength) return text;
|
||||
return `${text.slice(0, maxLength)}...<truncated ${text.length - maxLength} chars>`;
|
||||
}
|
||||
|
||||
toStringId(value: number | string | undefined | null) {
|
||||
return value === undefined || value === null ? '' : `${value}`;
|
||||
}
|
||||
|
||||
@ -179,7 +179,9 @@ export class QqbotCommandService {
|
||||
input: JSON.stringify(params.input || {}),
|
||||
operationKey: params.command.operationKey,
|
||||
output:
|
||||
params.output === undefined ? null : JSON.stringify(params.output),
|
||||
params.output === undefined
|
||||
? null
|
||||
: this.stringifyStoredOutput(params.output),
|
||||
pluginKey: params.command.pluginKey,
|
||||
rawMessage: params.message.messageText,
|
||||
selfId: params.message.selfId,
|
||||
@ -288,6 +290,21 @@ export class QqbotCommandService {
|
||||
}
|
||||
}
|
||||
|
||||
private stringifyStoredOutput(output: any) {
|
||||
if (
|
||||
output &&
|
||||
typeof output === 'object' &&
|
||||
!Array.isArray(output) &&
|
||||
typeof output.replyText === 'string'
|
||||
) {
|
||||
return JSON.stringify({
|
||||
...output,
|
||||
replyText: this.toolsService.toStoredMessageText(output.replyText),
|
||||
});
|
||||
}
|
||||
return JSON.stringify(output);
|
||||
}
|
||||
|
||||
private toRawBody(command: QqbotCommand): QqbotCommandBodyDto {
|
||||
return {
|
||||
aliases: this.parseList(command.aliases),
|
||||
|
||||
@ -103,12 +103,19 @@ export class QqbotSendService {
|
||||
this.rateLimitService.assertCanSend(account.selfId, params.targetId);
|
||||
|
||||
const { action, actionParams } = this.buildAction(params);
|
||||
const storedMessageText = this.toolsService.toStoredMessageText(
|
||||
params.message,
|
||||
);
|
||||
const storedActionParams = this.toStoredActionParams(
|
||||
actionParams,
|
||||
storedMessageText,
|
||||
);
|
||||
|
||||
const log = await this.sendLogRepository.save(
|
||||
this.sendLogRepository.create({
|
||||
action,
|
||||
messageText: params.message,
|
||||
params: actionParams,
|
||||
messageText: storedMessageText,
|
||||
params: storedActionParams,
|
||||
selfId: account.selfId,
|
||||
status: 'pending',
|
||||
targetId: params.targetId,
|
||||
@ -151,7 +158,7 @@ export class QqbotSendService {
|
||||
if (success) {
|
||||
await this.messageService.saveOutgoing({
|
||||
messageId,
|
||||
messageText: params.message,
|
||||
messageText: storedMessageText,
|
||||
messageType: params.targetType,
|
||||
selfId: account.selfId,
|
||||
targetId: params.targetId,
|
||||
@ -175,9 +182,8 @@ export class QqbotSendService {
|
||||
}
|
||||
|
||||
private async getReverseWsService(): Promise<QqbotReverseActionSender> {
|
||||
const { QqbotReverseWsService } = await import(
|
||||
'../connection/qqbot-reverse-ws.service'
|
||||
);
|
||||
const { QqbotReverseWsService } =
|
||||
await import('../connection/qqbot-reverse-ws.service');
|
||||
return this.moduleRef.get<QqbotReverseActionSender>(QqbotReverseWsService, {
|
||||
strict: false,
|
||||
});
|
||||
@ -212,4 +218,16 @@ export class QqbotSendService {
|
||||
actionParams: { message: params.message, user_id: params.targetId },
|
||||
};
|
||||
}
|
||||
|
||||
private toStoredActionParams(
|
||||
actionParams: Record<string, any>,
|
||||
storedMessageText: string,
|
||||
) {
|
||||
return {
|
||||
...actionParams,
|
||||
...(actionParams.message === undefined
|
||||
? {}
|
||||
: { message: storedMessageText }),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
21
test/common/tool.service.spec.ts
Normal file
21
test/common/tool.service.spec.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { ToolsService } from '@/common';
|
||||
|
||||
describe('ToolsService', () => {
|
||||
const service = new ToolsService();
|
||||
|
||||
it('summarizes CQ image base64 payloads before storing message text', () => {
|
||||
const stored = service.toStoredMessageText(
|
||||
`before [CQ:image,file=base64://${'a'.repeat(70000)}] after`,
|
||||
);
|
||||
|
||||
expect(stored).toBe('before [CQ:image,file=base64://<70000 chars>] after');
|
||||
expect(stored.length).toBeLessThan(100);
|
||||
});
|
||||
|
||||
it('truncates oversized stored message text after image summarization', () => {
|
||||
const stored = service.toStoredMessageText('x'.repeat(4100), 100);
|
||||
|
||||
expect(stored).toContain('<truncated 4000 chars>');
|
||||
expect(stored.length).toBeLessThan(140);
|
||||
});
|
||||
});
|
||||
73
test/qqbot/send/qqbot-send.service.spec.ts
Normal file
73
test/qqbot/send/qqbot-send.service.spec.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import { ToolsService } from '@/common';
|
||||
import { QqbotSendService } from '@/qqbot/send/qqbot-send.service';
|
||||
|
||||
describe('QqbotSendService', () => {
|
||||
it('stores summarized CQ image payloads while sending the original message', async () => {
|
||||
const originalMessage = `[CQ:image,file=base64://${'a'.repeat(70000)}]`;
|
||||
const reverseWsService = {
|
||||
sendAction: jest.fn().mockResolvedValue({
|
||||
data: { message_id: 'message-1' },
|
||||
retcode: 0,
|
||||
status: 'ok',
|
||||
}),
|
||||
};
|
||||
const sendLogRepository = {
|
||||
create: jest.fn((payload) => payload),
|
||||
save: jest.fn(async (payload) => ({ ...payload, id: 'log-1' })),
|
||||
update: jest.fn(),
|
||||
};
|
||||
const messageService = {
|
||||
saveOutgoing: jest.fn(),
|
||||
};
|
||||
const busService = {
|
||||
publish: jest.fn(),
|
||||
};
|
||||
|
||||
const service = new QqbotSendService(
|
||||
sendLogRepository as any,
|
||||
{
|
||||
getDefaultAccount: jest.fn().mockResolvedValue({ selfId: 'bot-1' }),
|
||||
} as any,
|
||||
busService as any,
|
||||
messageService as any,
|
||||
{ get: jest.fn(() => reverseWsService) } as any,
|
||||
{ assertCanSend: jest.fn() } as any,
|
||||
new ToolsService(),
|
||||
);
|
||||
(service as any).getReverseWsService = jest
|
||||
.fn()
|
||||
.mockResolvedValue(reverseWsService);
|
||||
|
||||
await service.sendText({
|
||||
message: originalMessage,
|
||||
selfId: 'bot-1',
|
||||
targetId: 'group-1',
|
||||
targetType: 'group',
|
||||
});
|
||||
|
||||
expect(sendLogRepository.save).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
messageText: '[CQ:image,file=base64://<70000 chars>]',
|
||||
params: expect.objectContaining({
|
||||
message: '[CQ:image,file=base64://<70000 chars>]',
|
||||
}),
|
||||
}),
|
||||
);
|
||||
expect(busService.publish).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.objectContaining({
|
||||
params: expect.objectContaining({ message: originalMessage }),
|
||||
}),
|
||||
);
|
||||
expect(reverseWsService.sendAction).toHaveBeenCalledWith(
|
||||
'bot-1',
|
||||
'send_group_msg',
|
||||
expect.objectContaining({ message: originalMessage }),
|
||||
);
|
||||
expect(messageService.saveOutgoing).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
messageText: '[CQ:image,file=base64://<70000 chars>]',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user