631 lines
18 KiB
TypeScript
631 lines
18 KiB
TypeScript
import { readFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import type {
|
|
BangDreamCommandInput,
|
|
BangDreamOperationHandlerName,
|
|
BangDreamOperationKey,
|
|
} from '@/modules/qqbot/plugins/bangdream/src/domain/common/bangdream.types';
|
|
|
|
const mockImageBuffer = (name: string) => Buffer.from(`image:${name}`);
|
|
|
|
const mockFuzzySearch = jest.fn(() => ({ song: [136] }));
|
|
const mockSongCtor = jest.fn().mockImplementation((songId: number) => ({
|
|
songId,
|
|
}));
|
|
const mockDrawSongDetail = jest.fn(async () => [
|
|
mockImageBuffer('song-detail'),
|
|
]);
|
|
const mockDrawSongList = jest.fn(async () => [mockImageBuffer('song-list')]);
|
|
const mockDrawSongChart = jest.fn(async () => [
|
|
mockImageBuffer('song-chart'),
|
|
]);
|
|
const mockDrawSongRandom = jest.fn(async () => [
|
|
mockImageBuffer('song-random'),
|
|
]);
|
|
const mockDrawSongMetaList = jest.fn(async () => [
|
|
mockImageBuffer('song-meta'),
|
|
]);
|
|
|
|
const mockDrawCardDetail = jest.fn(async () => [
|
|
mockImageBuffer('card-detail'),
|
|
]);
|
|
const mockDrawCardList = jest.fn(async () => [mockImageBuffer('card-list')]);
|
|
const mockCardGetTrainingStatusList = jest.fn(() => [false, true]);
|
|
const mockCardGetCardIllustrationImageBuffer = jest.fn(
|
|
async (trainingStatus: boolean) =>
|
|
mockImageBuffer(`card-illustration-${trainingStatus}`),
|
|
);
|
|
const mockCardCtor = jest.fn().mockImplementation((cardId: number) => ({
|
|
cardId,
|
|
getCardIllustrationImageBuffer: mockCardGetCardIllustrationImageBuffer,
|
|
getTrainingStatusList: mockCardGetTrainingStatusList,
|
|
isExist: true,
|
|
}));
|
|
|
|
const mockDrawCharacterDetail = jest.fn(async () => [
|
|
mockImageBuffer('character-detail'),
|
|
]);
|
|
const mockDrawCharacterList = jest.fn(async () => [
|
|
mockImageBuffer('character-list'),
|
|
]);
|
|
|
|
const mockGetPresentEvent = jest.fn(() => ({ eventId: 50 }));
|
|
const mockDrawEventDetail = jest.fn(async () => [
|
|
mockImageBuffer('event-detail'),
|
|
]);
|
|
const mockDrawEventList = jest.fn(async () => [mockImageBuffer('event-list')]);
|
|
const mockDrawEventStage = jest.fn(async () =>
|
|
Array.from({ length: 5 }, (_, index) =>
|
|
mockImageBuffer(`event-stage-${index}`),
|
|
),
|
|
);
|
|
|
|
const mockDrawPlayerDetail = jest.fn(async () => [
|
|
mockImageBuffer('player-detail'),
|
|
]);
|
|
|
|
const mockDrawGachaDetail = jest.fn(async () => [
|
|
mockImageBuffer('gacha-detail'),
|
|
]);
|
|
const mockDrawRandomGacha = jest.fn(async () => [
|
|
mockImageBuffer('gacha-simulate'),
|
|
]);
|
|
const mockGachaCtor = jest.fn().mockImplementation((gachaId: number) => ({
|
|
gachaId,
|
|
type: 'normal',
|
|
}));
|
|
const mockGetPresentGachaList = jest.fn(async () => [
|
|
{
|
|
gachaId: 300,
|
|
type: 'normal',
|
|
},
|
|
]);
|
|
|
|
const mockDrawCutoffDetail = jest.fn(async () => [
|
|
mockImageBuffer('cutoff-detail'),
|
|
]);
|
|
const mockDrawCutoffEventTop = jest.fn(async () => [
|
|
mockImageBuffer('cutoff-event-top'),
|
|
]);
|
|
const mockDrawCutoffAll = jest.fn(async () => [
|
|
mockImageBuffer('cutoff-all'),
|
|
]);
|
|
const mockDrawCutoffListOfRecentEvent = jest.fn(async () => [
|
|
mockImageBuffer('cutoff-recent'),
|
|
]);
|
|
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/search/fuzzy-search',
|
|
() => ({
|
|
fuzzySearch: mockFuzzySearch,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/song/song.model',
|
|
() => ({
|
|
Song: mockSongCtor,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/song/song-detail.renderer',
|
|
() => ({
|
|
drawSongDetail: mockDrawSongDetail,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/song/song-search.renderer',
|
|
() => ({
|
|
drawSongList: mockDrawSongList,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/song/song-chart.renderer',
|
|
() => ({
|
|
drawSongChart: mockDrawSongChart,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/song/song-random.renderer',
|
|
() => ({
|
|
drawSongRandom: mockDrawSongRandom,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/song/song-meta.renderer',
|
|
() => ({
|
|
drawSongMetaList: mockDrawSongMetaList,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/card/card-detail.renderer',
|
|
() => ({
|
|
drawCardDetail: mockDrawCardDetail,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/card/card-search.renderer',
|
|
() => ({
|
|
drawCardList: mockDrawCardList,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/card/card.model',
|
|
() => ({
|
|
Card: mockCardCtor,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/character/character-detail.renderer',
|
|
() => ({
|
|
drawCharacterDetail: mockDrawCharacterDetail,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/character/character-search.renderer',
|
|
() => ({
|
|
drawCharacterList: mockDrawCharacterList,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/event/event.model',
|
|
() => ({
|
|
getPresentEvent: mockGetPresentEvent,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/event/event-detail.renderer',
|
|
() => ({
|
|
drawEventDetail: mockDrawEventDetail,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/event/event-search.renderer',
|
|
() => ({
|
|
drawEventList: mockDrawEventList,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/event/event-stage.renderer',
|
|
() => ({
|
|
drawEventStage: mockDrawEventStage,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/player/player-detail.renderer',
|
|
() => ({
|
|
drawPlayerDetail: mockDrawPlayerDetail,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-detail.renderer',
|
|
() => ({
|
|
drawGachaDetail: mockDrawGachaDetail,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha-simulate.renderer',
|
|
() => ({
|
|
drawRandomGacha: mockDrawRandomGacha,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/gacha/gacha.model',
|
|
() => ({
|
|
Gacha: mockGachaCtor,
|
|
getPresentGachaList: mockGetPresentGachaList,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/policy/gacha.policy',
|
|
() => ({
|
|
BANGDREAM_GACHA_DEFAULT_SPIN_COUNT: 10,
|
|
isBirthdayGachaType: jest.fn(() => false),
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-detail.renderer',
|
|
() => ({
|
|
drawCutoffDetail: mockDrawCutoffDetail,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-event-top.renderer',
|
|
() => ({
|
|
drawCutoffEventTop: mockDrawCutoffEventTop,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-all.renderer',
|
|
() => ({
|
|
drawCutoffAll: mockDrawCutoffAll,
|
|
}),
|
|
);
|
|
jest.mock(
|
|
'@/modules/qqbot/plugins/bangdream/src/domain/cutoff/cutoff-recent.renderer',
|
|
() => ({
|
|
drawCutoffListOfRecentEvent: mockDrawCutoffListOfRecentEvent,
|
|
}),
|
|
);
|
|
|
|
type ManifestOperation = {
|
|
handlerName: BangDreamOperationHandlerName;
|
|
key: BangDreamOperationKey;
|
|
};
|
|
|
|
type BranchCase = {
|
|
assertBranch: () => void;
|
|
expectedImageCount: number;
|
|
expectedQuery: string;
|
|
input: BangDreamCommandInput;
|
|
key: BangDreamOperationKey;
|
|
name: string;
|
|
};
|
|
|
|
const branchCases: BranchCase[] = [
|
|
{
|
|
assertBranch: () => expect(mockDrawSongDetail).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: '136',
|
|
input: { text: '136' },
|
|
key: 'bangdream.song.search',
|
|
name: 'song.search numeric detail',
|
|
},
|
|
{
|
|
assertBranch: () => expect(mockDrawSongList).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: '夏祭',
|
|
input: { text: '夏祭' },
|
|
key: 'bangdream.song.search',
|
|
name: 'song.search fuzzy list',
|
|
},
|
|
{
|
|
assertBranch: () => expect(mockDrawSongChart).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: '136 3',
|
|
input: { text: '136 expert' },
|
|
key: 'bangdream.song.chart',
|
|
name: 'song.chart explicit difficulty',
|
|
},
|
|
{
|
|
assertBranch: () => expect(mockDrawSongRandom).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: '随机曲',
|
|
input: { text: '' },
|
|
key: 'bangdream.song.random',
|
|
name: 'song.random empty query',
|
|
},
|
|
{
|
|
assertBranch: () => expect(mockDrawSongMetaList).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: 'cn',
|
|
input: { text: 'cn' },
|
|
key: 'bangdream.song.meta',
|
|
name: 'song.meta server',
|
|
},
|
|
{
|
|
assertBranch: () => expect(mockDrawCardDetail).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: '1001',
|
|
input: { text: '1001' },
|
|
key: 'bangdream.card.search',
|
|
name: 'card.search numeric detail',
|
|
},
|
|
{
|
|
assertBranch: () => expect(mockDrawCardList).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: '香澄',
|
|
input: { text: '香澄' },
|
|
key: 'bangdream.card.search',
|
|
name: 'card.search fuzzy list',
|
|
},
|
|
{
|
|
assertBranch: () => {
|
|
expect(mockCardCtor).toHaveBeenCalledWith(1001);
|
|
expect(mockCardGetCardIllustrationImageBuffer).toHaveBeenCalledTimes(2);
|
|
},
|
|
expectedImageCount: 2,
|
|
expectedQuery: '1001',
|
|
input: { text: '1001' },
|
|
key: 'bangdream.card.illustration',
|
|
name: 'card.illustration trained variants',
|
|
},
|
|
{
|
|
assertBranch: () =>
|
|
expect(mockDrawCharacterDetail).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: '1',
|
|
input: { text: '1' },
|
|
key: 'bangdream.character.search',
|
|
name: 'character.search numeric detail',
|
|
},
|
|
{
|
|
assertBranch: () => expect(mockDrawCharacterList).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: '香澄',
|
|
input: { text: '香澄' },
|
|
key: 'bangdream.character.search',
|
|
name: 'character.search fuzzy list',
|
|
},
|
|
{
|
|
assertBranch: () => expect(mockDrawEventDetail).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: '50',
|
|
input: { text: '50' },
|
|
key: 'bangdream.event.search',
|
|
name: 'event.search numeric detail',
|
|
},
|
|
{
|
|
assertBranch: () => expect(mockDrawEventList).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: 'summer',
|
|
input: { text: 'summer' },
|
|
key: 'bangdream.event.search',
|
|
name: 'event.search fuzzy list',
|
|
},
|
|
{
|
|
assertBranch: () => expect(mockDrawEventStage).toHaveBeenCalledWith(
|
|
50,
|
|
3,
|
|
true,
|
|
true,
|
|
),
|
|
expectedImageCount: 5,
|
|
expectedQuery: '50',
|
|
input: { text: '50 -m cn' },
|
|
key: 'bangdream.event.stage',
|
|
name: 'event.stage meta split output',
|
|
},
|
|
{
|
|
assertBranch: () => expect(mockDrawPlayerDetail).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: '123456',
|
|
input: { text: '123456 cn' },
|
|
key: 'bangdream.player.search',
|
|
name: 'player.search server detail',
|
|
},
|
|
{
|
|
assertBranch: () => expect(mockDrawGachaDetail).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: '300',
|
|
input: { text: '300' },
|
|
key: 'bangdream.gacha.search',
|
|
name: 'gacha.search detail',
|
|
},
|
|
{
|
|
assertBranch: () => {
|
|
expect(mockGachaCtor).toHaveBeenCalledWith(300);
|
|
expect(mockDrawRandomGacha).toHaveBeenCalledTimes(1);
|
|
},
|
|
expectedImageCount: 1,
|
|
expectedQuery: '10 300',
|
|
input: { text: '10 300' },
|
|
key: 'bangdream.gacha.simulate',
|
|
name: 'gacha.simulate explicit gacha',
|
|
},
|
|
{
|
|
assertBranch: () => {
|
|
expect(mockGetPresentGachaList).toHaveBeenCalledWith(3);
|
|
expect(mockDrawRandomGacha).toHaveBeenCalledTimes(1);
|
|
},
|
|
expectedImageCount: 1,
|
|
expectedQuery: '10',
|
|
input: { text: '10 cn' },
|
|
key: 'bangdream.gacha.simulate',
|
|
name: 'gacha.simulate present gacha fallback',
|
|
},
|
|
{
|
|
assertBranch: () => expect(mockDrawCutoffDetail).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: '1000 50',
|
|
input: { text: 'ycx 1000 cn', eventId: 50 },
|
|
key: 'bangdream.cutoff.detail',
|
|
name: 'cutoff.detail tier detail',
|
|
},
|
|
{
|
|
assertBranch: () => expect(mockDrawCutoffEventTop).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: '10 50',
|
|
input: { eventId: 50, mainServer: 'cn', tier: 10 },
|
|
key: 'bangdream.cutoff.detail',
|
|
name: 'cutoff.detail top10 branch',
|
|
},
|
|
{
|
|
assertBranch: () => expect(mockDrawCutoffAll).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: '50',
|
|
input: { eventId: 50, text: 'cn' },
|
|
key: 'bangdream.cutoff.all',
|
|
name: 'cutoff.all event',
|
|
},
|
|
{
|
|
assertBranch: () =>
|
|
expect(mockDrawCutoffListOfRecentEvent).toHaveBeenCalledTimes(1),
|
|
expectedImageCount: 1,
|
|
expectedQuery: '1000 50',
|
|
input: { text: '1000 50 cn' },
|
|
key: 'bangdream.cutoff.recent',
|
|
name: 'cutoff.recent tier',
|
|
},
|
|
];
|
|
|
|
const manifestOperations: ManifestOperation[] = JSON.parse(
|
|
readFileSync(
|
|
join(process.cwd(), 'src/modules/qqbot/plugins/bangdream/plugin.json'),
|
|
'utf8',
|
|
),
|
|
).operations;
|
|
let operationsByKey: Map<BangDreamOperationKey, any>;
|
|
|
|
describe('BangDream operation branch matrix', () => {
|
|
beforeAll(async () => {
|
|
const { getBangDreamOperationsByHandlerName } = await import(
|
|
'@/modules/qqbot/plugins/bangdream/src/operations'
|
|
);
|
|
const operationsByHandlerName = getBangDreamOperationsByHandlerName();
|
|
operationsByKey = new Map(
|
|
manifestOperations.map((operation) => [
|
|
operation.key,
|
|
operationsByHandlerName.get(operation.handlerName),
|
|
]),
|
|
);
|
|
});
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
it('covers every manifest command operation in the branch matrix', () => {
|
|
expect([...new Set(branchCases.map((item) => item.key))].sort()).toEqual(
|
|
manifestOperations.map((operation) => operation.key).sort(),
|
|
);
|
|
});
|
|
|
|
it.each(branchCases)(
|
|
'executes $name and compares output contract',
|
|
async (branchCase) => {
|
|
const operation = operationsByKey.get(branchCase.key);
|
|
expect(operation).toBeDefined();
|
|
|
|
const output = await operation!.execute(
|
|
branchCase.input,
|
|
createCommandContext(),
|
|
);
|
|
|
|
expect(output).toMatchObject({
|
|
imageCount: branchCase.expectedImageCount,
|
|
operationKey: branchCase.key,
|
|
query: branchCase.expectedQuery,
|
|
source: 'BangDream 内置插件',
|
|
});
|
|
expect(output.replyText.match(/\[CQ:image,file=base64:\/\//g)).toHaveLength(
|
|
branchCase.expectedImageCount,
|
|
);
|
|
branchCase.assertBranch();
|
|
},
|
|
);
|
|
});
|
|
|
|
function createCommandContext() {
|
|
const pickText = (input: BangDreamCommandInput) =>
|
|
`${input.query || input.text || input.raw || ''}`.trim();
|
|
const optionalNumber = (value: unknown) => {
|
|
if (value === undefined || value === null || value === '') return undefined;
|
|
const parsed = Number(value);
|
|
return Number.isInteger(parsed) ? parsed : undefined;
|
|
};
|
|
const getTokens = (input: BangDreamCommandInput) => {
|
|
if (Array.isArray(input.args)) {
|
|
return input.args.map((item) => `${item}`.trim()).filter(Boolean);
|
|
}
|
|
return pickText(input).split(/\s+/).filter(Boolean);
|
|
};
|
|
const normalizeServer = (value: unknown) => {
|
|
if (value === undefined || value === null || value === '') return undefined;
|
|
const raw = `${value}`.trim().toLowerCase();
|
|
const mapped: Record<string, number> = {
|
|
cn: 3,
|
|
en: 1,
|
|
jp: 0,
|
|
kr: 4,
|
|
tw: 2,
|
|
国服: 3,
|
|
日服: 0,
|
|
};
|
|
const numeric = optionalNumber(raw);
|
|
if (numeric !== undefined && numeric >= 0 && numeric <= 4) {
|
|
return numeric;
|
|
}
|
|
return mapped[raw];
|
|
};
|
|
const pickMainServer = (input: BangDreamCommandInput, tokens: string[]) => {
|
|
const explicit =
|
|
input.mainServer ??
|
|
input.serverName ??
|
|
input.server ??
|
|
tokens.find((item) => normalizeServer(item) !== undefined);
|
|
return normalizeServer(explicit) ?? 3;
|
|
};
|
|
|
|
return {
|
|
drawFuzzyResult: async (
|
|
_query: string,
|
|
render: (matches: Record<string, unknown>) => Promise<Array<Buffer>>,
|
|
) => render({ result: [1] }),
|
|
firstNumber: (tokens: string[]) =>
|
|
tokens.map((item) => optionalNumber(item)).find((item) => item !== undefined),
|
|
firstToken: (input: BangDreamCommandInput) => getTokens(input)[0],
|
|
getRenderOptions: (
|
|
input: BangDreamCommandInput,
|
|
defaults: { useEasyBG?: boolean } = {},
|
|
) => ({
|
|
compress: input.compress === undefined ? true : input.compress !== false,
|
|
displayedServerList: [3, 0],
|
|
mainServer: pickMainServer(input, getTokens(input)),
|
|
useEasyBG:
|
|
input.useEasyBG === undefined
|
|
? defaults.useEasyBG ?? false
|
|
: input.useEasyBG !== false,
|
|
}),
|
|
getTokens,
|
|
isInteger: (value: string) => /^(0|[1-9]\d*)$/.test(value),
|
|
normalizeBoolean: (value: unknown, fallback: boolean) => {
|
|
if (value === undefined || value === null || value === '') return fallback;
|
|
if (typeof value === 'boolean') return value;
|
|
return ['1', 'true', 'yes', 'y', '-m'].includes(
|
|
`${value}`.trim().toLowerCase(),
|
|
);
|
|
},
|
|
optionalNumber,
|
|
pickDifficulty: (value: unknown) => {
|
|
const numeric = optionalNumber(value);
|
|
if (numeric !== undefined) return numeric;
|
|
const mapped: Record<string, number> = {
|
|
easy: 0,
|
|
expert: 3,
|
|
hard: 2,
|
|
normal: 1,
|
|
special: 4,
|
|
};
|
|
return mapped[`${value || ''}`.trim().toLowerCase()];
|
|
},
|
|
pickMainServer,
|
|
pickText,
|
|
requireNumber: (explicit: unknown, fallback: unknown, message: string) => {
|
|
const value = optionalNumber(explicit) ?? optionalNumber(fallback);
|
|
if (value === undefined) throw new Error(message);
|
|
return value;
|
|
},
|
|
requireText: (input: BangDreamCommandInput, message: string) => {
|
|
const value = pickText(input);
|
|
if (!value) throw new Error(message);
|
|
return value;
|
|
},
|
|
secondNumber: (tokens: string[]) =>
|
|
tokens
|
|
.map((item) => optionalNumber(item))
|
|
.filter((item) => item !== undefined)[1],
|
|
toImageReply: (
|
|
operationKey: BangDreamOperationKey,
|
|
query: string,
|
|
list: Array<Buffer | string>,
|
|
) => {
|
|
const images = list.filter((item): item is Buffer => Buffer.isBuffer(item));
|
|
if (images.length === 0) {
|
|
throw new Error(
|
|
list.find((item): item is string => typeof item === 'string') ||
|
|
'BangDream 未返回图片',
|
|
);
|
|
}
|
|
return {
|
|
imageCount: images.length,
|
|
operationKey,
|
|
query,
|
|
replyText: images
|
|
.map((item) => `[CQ:image,file=base64://${item.toString('base64')}]`)
|
|
.join('\n'),
|
|
source: 'BangDream 内置插件',
|
|
};
|
|
},
|
|
} as any;
|
|
}
|