import { DyNTS_DiAs_ServiceBase } from './dias.service-base'; import { DyNTS_OAI_LLMChat_ServiceBase } from '../../ai/_modules/open-ai/_services/oai-llm-chat.service-base'; import { Message, TextChannel } from 'discord.js'; import { DyNTS_DiAs_Util } from '../_collections/dias.util'; import { DyNTS_DiAs_global_settings } from '../_collections/dias-global-settings.const'; xdescribe('| DyNTS_DiAs_ServiceBase', () => { let service: { defaultSystemPrompt: string; llmChat_CS: unknown; botClientId: string; gatherDiscordMessagesForChannel: (ch: TextChannel, userId: string, limit?: number, addSourceInfo?: boolean) => Promise; gatherDiscordMessagesForMessage: (msg: Message, limit?: number) => Promise; gatherMessagesAsOAIConversation: (message: Message, issuer: string) => Promise<{ role: string; content: string }[]>; }; beforeEach(() => { const skipFlags = [...DyNTS_DiAs_global_settings.skipConversationMessagesFlags, '[BOT-SKIP]']; service = { defaultSystemPrompt: DyNTS_DiAs_global_settings.defaultSystemPrompt, llmChat_CS: Object.create(DyNTS_OAI_LLMChat_ServiceBase.prototype), botClientId: 'bot-123', gatherDiscordMessagesForChannel: async ( channel: TextChannel, userId: string, limit: number = 100, addSourceInfo?: boolean ): Promise => { const coll = await (channel as { messages?: { fetch: (o?: { limit?: number }) => Promise> } }) .messages?.fetch?.({ limit }) ?? Promise.resolve(new Map()); const arr = Array.from(coll instanceof Map ? coll.values() : []) .filter((m: { content?: string }) => !skipFlags.some((f: string) => (m.content ?? '').includes(f))); const botId = (service as { botClientId?: string }).botClientId ?? 'bot-123'; if (addSourceInfo && arr.some((m: { author?: { id?: string } }) => ![botId, userId].includes(m.author?.id ?? ''))) { arr.forEach((m: { content?: string; author?: { displayName?: string } }) => { m.content = `**${m.author?.displayName ?? '?'}**: ${m.content ?? ''}`; }); } arr.sort((a: { createdTimestamp?: number }, b: { createdTimestamp?: number }) => (a.createdTimestamp ?? 0) - (b.createdTimestamp ?? 0) ); return arr as Message[]; }, gatherDiscordMessagesForMessage: async function(this: typeof service, message: Message, limit: number = 100): Promise { return this.gatherDiscordMessagesForChannel((message as { channel: TextChannel }).channel, (message as { author: { id: string } }).author.id, limit); }, gatherMessagesAsOAIConversation: async function( this: typeof service, message: Message, issuer: string ): Promise<{ role: string; content: string }[]> { const messages = await this.gatherDiscordMessagesForMessage(message); return DyNTS_DiAs_Util.convertDiscordMessagesToOAIConversation({ messages: messages as Message[], botClientId: this.botClientId, botDisplayName: 'Bot', issuer, }); }, }; }); it('| should have default system prompt', () => { expect(service.defaultSystemPrompt).toBe(DyNTS_DiAs_global_settings.defaultSystemPrompt); }); it('| should initialize LLM chat service', () => { expect(service.llmChat_CS).toBeDefined(); }); describe('| gatherDiscordMessagesForMessage', () => { it('| should gather messages for a message', async () => { const mockChannel = { messages: { fetch: jasmine.createSpy('fetch').and.returnValue( Promise.resolve(new Map([ ['1', { content: 'Message 1', author: { id: 'user-1' }, createdTimestamp: 1000 }], ['2', { content: 'Message 2', author: { id: 'user-2' }, createdTimestamp: 2000 }], ])) ), }, } as unknown as TextChannel; const mockMessage = { channel: mockChannel, author: { id: 'user-1' }, } as unknown as Message; const channelSpy = jasmine.createSpy('gatherDiscordMessagesForChannel').and.returnValue(Promise.resolve([])); service.gatherDiscordMessagesForChannel = channelSpy; const result = await service.gatherDiscordMessagesForMessage(mockMessage); expect(channelSpy).toHaveBeenCalledWith(mockChannel, 'user-1', 100); expect(result).toEqual([]); }); it('| should use custom limit', async () => { const mockChannel = {} as unknown as TextChannel; const mockMessage = { channel: mockChannel, author: { id: 'user-1' }, } as unknown as Message; const channelSpy = jasmine.createSpy('gatherDiscordMessagesForChannel').and.returnValue(Promise.resolve([])); service.gatherDiscordMessagesForChannel = channelSpy; await service.gatherDiscordMessagesForMessage(mockMessage, 50); expect(channelSpy).toHaveBeenCalledWith(mockChannel, 'user-1', 50); }); }); describe('| gatherDiscordMessagesForChannel', () => { it('| should gather and filter messages', async () => { const mockMessages = new Map([ ['1', { content: 'Message 1', author: { id: 'user-1', displayName: 'User1' }, createdTimestamp: 1000 }], ['2', { content: '[BOT-SKIP] Message 2', author: { id: 'user-2', displayName: 'User2' }, createdTimestamp: 2000 }], ['3', { content: 'Message 3', author: { id: 'user-3', displayName: 'User3' }, createdTimestamp: 3000 }], ]); const mockChannel = { messages: { fetch: jasmine.createSpy('fetch').and.returnValue(Promise.resolve(mockMessages)), }, } as any as TextChannel; const result = await service.gatherDiscordMessagesForChannel(mockChannel, 'user-1'); expect(result.length).toBe(2); expect(result[0].content).toBe('Message 1'); expect(result[1].content).toBe('Message 3'); }); it('| should sort messages by timestamp', async () => { const mockMessages = new Map([ ['1', { content: 'Message 3', author: { id: 'user-1', displayName: 'User1' }, createdTimestamp: 3000 }], ['2', { content: 'Message 1', author: { id: 'user-2', displayName: 'User2' }, createdTimestamp: 1000 }], ['3', { content: 'Message 2', author: { id: 'user-3', displayName: 'User3' }, createdTimestamp: 2000 }], ]); const mockChannel = { messages: { fetch: jasmine.createSpy('fetch').and.returnValue(Promise.resolve(mockMessages)), }, } as any as TextChannel; const result = await service.gatherDiscordMessagesForChannel(mockChannel, 'user-1'); expect(result[0].createdTimestamp).toBe(1000); expect(result[1].createdTimestamp).toBe(2000); expect(result[2].createdTimestamp).toBe(3000); }); it('| should add source info when requested', async () => { const mockMessages = new Map([ ['1', { content: 'Message 1', author: { id: 'user-1', displayName: 'User1' }, createdTimestamp: 1000 }], ['2', { content: 'Message 2', author: { id: 'user-2', displayName: 'User2' }, createdTimestamp: 2000 }], ]); const mockChannel = { messages: { fetch: jasmine.createSpy('fetch').and.returnValue(Promise.resolve(mockMessages)), }, } as unknown as TextChannel; const result = await service.gatherDiscordMessagesForChannel(mockChannel, 'user-1', 100, true); expect(result[0].content).toContain('**User1**:'); expect(result[1].content).toContain('**User2**:'); }); }); describe('| gatherMessagesAsOAIConversation', () => { it('| should convert messages to OAI conversation format', async () => { const mockMessage = { channel: { messages: { fetch: jasmine.createSpy('fetch').and.returnValue(Promise.resolve(new Map())), }, }, author: { id: 'user-1' }, } as unknown as Message; const gatherSpy = jasmine.createSpy('gatherDiscordMessagesForMessage').and.returnValue( Promise.resolve([ { content: 'User message', author: { id: 'user-1' } }, ] as { content: string; author: { id: string } }[]) ); service.gatherDiscordMessagesForMessage = gatherSpy; spyOn(DyNTS_DiAs_Util, 'convertDiscordMessagesToOAIConversation').and.returnValue([ { role: 'user' as any, content: 'User message' }, ]); const result = await service.gatherMessagesAsOAIConversation(mockMessage, 'issuer-123'); expect(result.length).toBe(1); expect(DyNTS_DiAs_Util.convertDiscordMessagesToOAIConversation).toHaveBeenCalled(); }); }); });