@elizaos/plugin-discord 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +486 -177
- package/dist/index.js.map +1 -1
- package/package.json +6 -8
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/actions/chatWithAttachments.ts","../src/actions/downloadMedia.ts","../src/actions/summarizeConversation.ts","../src/actions/transcribeMedia.ts","../src/actions/voiceJoin.ts","../src/types.ts","../src/actions/voiceLeave.ts","../src/providers/channelState.ts","../src/providers/voiceState.ts","../src/service.ts","../src/constants.ts","../src/messages.ts","../src/attachments.ts","../src/utils.ts","../src/voice.ts","../src/tests.ts"],"sourcesContent":["import { type IAgentRuntime, type Plugin, logger } from '@elizaos/core';\nimport chatWithAttachments from './actions/chatWithAttachments';\nimport { downloadMedia } from './actions/downloadMedia';\nimport { summarize } from './actions/summarizeConversation';\nimport { transcribeMedia } from './actions/transcribeMedia';\nimport { joinVoice } from './actions/voiceJoin';\nimport { leaveVoice } from './actions/voiceLeave';\nimport { channelStateProvider } from './providers/channelState';\nimport { voiceStateProvider } from './providers/voiceState';\nimport { DiscordService } from './service';\nimport { DiscordTestSuite } from './tests';\n\nconst discordPlugin: Plugin = {\n name: 'discord',\n description: 'Discord service plugin for integration with Discord servers and channels',\n services: [DiscordService],\n actions: [chatWithAttachments, downloadMedia, joinVoice, leaveVoice, summarize, transcribeMedia],\n providers: [channelStateProvider, voiceStateProvider],\n tests: [new DiscordTestSuite()],\n init: async (config: Record<string, string>, runtime: IAgentRuntime) => {\n const token = runtime.getSetting('DISCORD_API_TOKEN') as string;\n\n if (!token || token.trim() === '') {\n logger.warn(\n 'Discord API Token not provided - Discord plugin is loaded but will not be functional'\n );\n logger.warn(\n 'To enable Discord functionality, please provide DISCORD_API_TOKEN in your .eliza/.env file'\n );\n }\n },\n};\n\nexport default discordPlugin;\n","import fs from 'node:fs';\nimport {\n type Action,\n type ActionExample,\n ChannelType,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n ModelType,\n type State,\n composePromptFromState,\n parseJSONObjectFromText,\n trimTokens,\n} from '@elizaos/core';\n\nexport const summarizationTemplate = `# Summarized so far (we are adding to this)\n{{currentSummary}}\n\n# Current attachments we are summarizing\n{{attachmentsWithText}}\n\nSummarization objective: {{objective}}\n\n# Instructions: Summarize the attachments. Return the summary. Do not acknowledge this request, just summarize and continue the existing summary if there is one. Capture any important details based on the objective. Only respond with the new summary text.`;\n\n/**\n * Template for generating a summary of specific attachments based on recent messages.\n * This template includes placeholders for recentMessages, senderName, objective, and attachmentIds.\n * To generate a response, the user's objective and a list of attachment IDs must be determined.\n *\n * @type {string}\n */\nexport const attachmentIdsTemplate = `# Messages we are summarizing\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting a summary of specific attachments. Your goal is to determine their objective, along with the list of attachment IDs to summarize.\nThe \"objective\" is a detailed description of what the user wants to summarize based on the conversation.\nThe \"attachmentIds\" is an array of attachment IDs that the user wants to summarize. If not specified, default to including all attachments from the conversation.\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"objective\": \"<What the user wants to summarize>\",\n \"attachmentIds\": [\"<Attachment ID 1>\", \"<Attachment ID 2>\", ...]\n}\n\\`\\`\\`\n`;\n\n/**\n * Retrieves attachment IDs from a model using a prompt generated from the current state and a template.\n * @param {IAgentRuntime} runtime - The agent runtime to use for interaction with models\n * @param {Memory} _message - The memory object\n * @param {State} state - The current state of the conversation\n * @returns {Promise<{ objective: string; attachmentIds: string[] } | null>} An object containing the objective and attachment IDs, or null if the data could not be retrieved after multiple attempts\n */\nconst getAttachmentIds = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<{ objective: string; attachmentIds: string[] } | null> => {\n const prompt = composePromptFromState({\n state,\n template: attachmentIdsTemplate,\n });\n\n for (let i = 0; i < 5; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n // try parsing to a json object\n const parsedResponse = parseJSONObjectFromText(response) as {\n objective: string;\n attachmentIds: string[];\n } | null;\n // see if it contains objective and attachmentIds\n if (parsedResponse?.objective && parsedResponse?.attachmentIds) {\n return parsedResponse;\n }\n }\n return null;\n};\n\n/**\n * Represents an action to summarize user request informed by specific attachments based on their IDs.\n * If a user asks to chat with a PDF, or wants more specific information about a link or video or anything else they've attached, this is the action to use.\n * @typedef {Object} summarizeAction\n * @property {string} name - The name of the action\n * @property {string[]} similes - Similar actions related to summarization with attachments\n * @property {string} description - Description of the action\n * @property {Function} validate - Validation function to check if the action should be triggered based on keywords in the message\n * @property {Function} handler - Handler function to process the user request, summarize attachments, and provide a summary\n * @property {Object[]} examples - Examples demonstrating how to use the action with message content and expected responses\n */\n\nexport const chatWithAttachments: Action = {\n name: 'CHAT_WITH_ATTACHMENTS',\n similes: [\n 'CHAT_WITH_ATTACHMENT',\n 'SUMMARIZE_FILES',\n 'SUMMARIZE_FILE',\n 'SUMMARIZE_ATACHMENT',\n 'CHAT_WITH_PDF',\n 'ATTACHMENT_SUMMARY',\n 'RECAP_ATTACHMENTS',\n 'SUMMARIZE_FILE',\n 'SUMMARIZE_VIDEO',\n 'SUMMARIZE_AUDIO',\n 'SUMMARIZE_IMAGE',\n 'SUMMARIZE_DOCUMENT',\n 'SUMMARIZE_LINK',\n 'ATTACHMENT_SUMMARY',\n 'FILE_SUMMARY',\n ],\n description:\n \"Answer a user request informed by specific attachments based on their IDs. If a user asks to chat with a PDF, or wants more specific information about a link or video or anything else they've attached, this is the action to use.\",\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n const room = await _runtime.getRoom(message.roomId);\n if (room?.type !== ChannelType.GROUP) {\n return false;\n }\n // only show if one of the keywords are in the message\n const keywords: string[] = [\n 'attachment',\n 'summary',\n 'summarize',\n 'research',\n 'pdf',\n 'video',\n 'audio',\n 'image',\n 'document',\n 'link',\n 'file',\n 'attachment',\n 'summarize',\n 'code',\n 'report',\n 'write',\n 'details',\n 'information',\n 'talk',\n 'chat',\n 'read',\n 'listen',\n 'watch',\n ];\n return keywords.some((keyword) =>\n message.content.text?.toLowerCase().includes(keyword.toLowerCase())\n );\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const callbackData: Content = {\n text: '', // fill in later\n actions: ['CHAT_WITH_ATTACHMENTS_RESPONSE'],\n source: message.content.source,\n attachments: [],\n };\n\n // 1. extract attachment IDs from the message\n const attachmentData = await getAttachmentIds(runtime, message, state);\n if (!attachmentData) {\n console.error(\"Couldn't get attachment IDs from message\");\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: message.content.source,\n thought: \"I tried to chat with attachments but I couldn't get attachment IDs\",\n actions: ['CHAT_WITH_ATTACHMENTS_FAILED'],\n },\n metadata: {\n type: 'CHAT_WITH_ATTACHMENTS',\n },\n },\n 'messages'\n );\n return;\n }\n\n const { objective, attachmentIds } = attachmentData;\n\n const conversationLength = runtime.getConversationLength();\n\n const recentMessages = await runtime.getMemories({\n tableName: 'messages',\n roomId: message.roomId,\n count: conversationLength,\n unique: false,\n });\n\n // This is pretty gross but it can catch cases where the returned generated UUID is stupidly wrong for some reason\n const attachments = recentMessages\n .filter((msg) => msg.content.attachments && msg.content.attachments.length > 0)\n .flatMap((msg) => msg.content.attachments)\n // Ensure attachment is not undefined before accessing properties\n .filter(\n (attachment) =>\n attachment &&\n (attachmentIds\n .map((attch) => attch.toLowerCase().slice(0, 5))\n .includes(attachment.id.toLowerCase().slice(0, 5)) ||\n // or check the other way\n attachmentIds.some((id) => {\n const attachmentId = id.toLowerCase().slice(0, 5);\n // Add check here too\n return attachment && attachment.id.toLowerCase().includes(attachmentId);\n }))\n );\n\n const attachmentsWithText = attachments\n // Ensure attachment is not undefined before accessing properties\n .filter((attachment): attachment is NonNullable<typeof attachment> => !!attachment)\n .map((attachment) => `# ${attachment.title}\\n${attachment.text}`)\n .join('\\n\\n');\n\n let currentSummary = '';\n\n const chunkSize = 8192;\n\n state.values.attachmentsWithText = attachmentsWithText;\n state.values.objective = objective;\n const template = await trimTokens(summarizationTemplate, chunkSize, runtime);\n const prompt = composePromptFromState({\n state,\n // make sure it fits, we can pad the tokens a bit\n // Get the model's tokenizer based on the current model being used\n template,\n });\n\n const summary = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n currentSummary = `${currentSummary}\\n${summary}`;\n\n if (!currentSummary) {\n console.error(\"No summary found, that's not good!\");\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: message.content.source,\n thought: \"I tried to chat with attachments but I couldn't get a summary\",\n actions: ['CHAT_WITH_ATTACHMENTS_FAILED'],\n },\n metadata: {\n type: 'CHAT_WITH_ATTACHMENTS',\n },\n },\n 'messages'\n );\n return;\n }\n\n callbackData.text = currentSummary.trim();\n if (\n callbackData.text &&\n (currentSummary.trim()?.split('\\n').length < 4 ||\n currentSummary.trim()?.split(' ').length < 100)\n ) {\n callbackData.text = `Here is the summary:\n\\`\\`\\`md\n${currentSummary.trim()}\n\\`\\`\\`\n`;\n await callback(callbackData);\n } else if (currentSummary.trim()) {\n const summaryDir = 'cache';\n const summaryFilename = `${summaryDir}/summary_${Date.now()}.md`;\n try {\n await fs.promises.mkdir(summaryDir, { recursive: true });\n\n // Write file directly first\n await fs.promises.writeFile(summaryFilename, currentSummary, 'utf8');\n\n // Then cache it\n await runtime.setCache<string>(summaryFilename, currentSummary);\n\n await callback(\n {\n ...callbackData,\n text: `I've attached the summary of the requested attachments as a text file.`,\n },\n [summaryFilename]\n );\n } catch (error) {\n console.error('Error in file/cache process:', error);\n throw error;\n }\n } else {\n console.warn('Empty response from chat with attachments action, skipping');\n }\n\n return callbackData;\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Can you summarize the attachments b3e23, c4f67, and d5a89?',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"Sure thing! I'll pull up those specific attachments and provide a summary of their content.\",\n actions: ['CHAT_WITH_ATTACHMENTS'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'I need a technical summary of the PDFs I sent earlier - a1b2c3.pdf, d4e5f6.pdf, and g7h8i9.pdf',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll take a look at those specific PDF attachments and put together a technical summary for you. Give me a few minutes to review them.\",\n actions: ['CHAT_WITH_ATTACHMENTS'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: \"Can you watch this video for me and tell me which parts you think are most relevant to the report I'm writing? (the one I attached in my last message)\",\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'sure, no problem.',\n actions: ['CHAT_WITH_ATTACHMENTS'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'can you read my blog post and give me a detailed breakdown of the key points I made, and then suggest a handful of tweets to promote it?',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'great idea, give me a minute',\n actions: ['CHAT_WITH_ATTACHMENTS'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default chatWithAttachments;\n","import {\n type Action,\n type ActionExample,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n ModelType,\n ServiceType,\n type State,\n composePromptFromState,\n parseJSONObjectFromText,\n} from '@elizaos/core';\n\n/**\n * Template for generating a media URL for a requested media file.\n *\n * @type {string}\n * @description This template is used for messages where a user is requesting to download a specific media file (video or audio). The goal is to determine the URL of the media they want to download.\n *\n * @param {string} recentMessages - Placeholder for recent messages related to the request.\n * @param {string} senderName - Name of the sender requesting the media file.\n *\n * @returns {string} - Formatted template with instructions and JSON structure for response.\n *\n * @example\n * `mediaUrlTemplate` contains the template for generating a media URL based on user request.\n */\n\nexport const mediaUrlTemplate = `# Messages we are searching for a media URL\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting to download a specific media file (video or audio). Your goal is to determine the URL of the media they want to download.\nThe \"mediaUrl\" is the URL of the media file that the user wants downloaded. If not specified, return null.\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"mediaUrl\": \"<Media URL>\"\n}\n\\`\\`\\`\n`;\n\n/**\n * Get a media URL from the user through text input using the provided runtime and state.\n * @param {IAgentRuntime} runtime - The runtime object to interact with the agent.\n * @param {Memory} _message - The memory object containing the input message.\n * @param {State} state - The state of the conversation.\n * @returns {Promise<string | null>} The media URL provided by the user or null if no valid URL is provided.\n */\nconst getMediaUrl = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<string | null> => {\n const prompt = composePromptFromState({\n state,\n template: mediaUrlTemplate,\n });\n\n for (let i = 0; i < 5; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response) as {\n mediaUrl: string;\n } | null;\n\n if (parsedResponse?.mediaUrl) {\n return parsedResponse.mediaUrl;\n }\n }\n return null;\n};\n\nexport const downloadMedia: Action = {\n name: 'DOWNLOAD_MEDIA',\n similes: [\n 'DOWNLOAD_VIDEO',\n 'DOWNLOAD_AUDIO',\n 'GET_MEDIA',\n 'DOWNLOAD_PODCAST',\n 'DOWNLOAD_YOUTUBE',\n ],\n description:\n 'Downloads a video or audio file from a URL and attaches it to the response message.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n if (message.content.source !== 'discord') {\n return false;\n }\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const videoService = runtime.getService(ServiceType.VIDEO) as any;\n\n if (!videoService) {\n console.error('Video service not found');\n return;\n }\n\n const mediaUrl = await getMediaUrl(runtime, message, state);\n if (!mediaUrl) {\n console.error(\"Couldn't get media URL from messages\");\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: 'discord',\n thought: `I couldn't find the media URL in the message`,\n actions: ['DOWNLOAD_MEDIA_FAILED'],\n },\n metadata: {\n type: 'DOWNLOAD_MEDIA',\n },\n },\n 'messages'\n );\n return;\n }\n\n const videoInfo = await videoService.fetchVideoInfo(mediaUrl);\n const mediaPath = await videoService.downloadVideo(videoInfo);\n\n const response: Content = {\n text: `I downloaded the video \"${videoInfo.title}\" and attached it below.`,\n actions: ['DOWNLOAD_MEDIA_RESPONSE'],\n source: message.content.source,\n attachments: [],\n };\n\n const maxRetries = 3;\n let retries = 0;\n\n while (retries < maxRetries) {\n try {\n await callback(\n {\n ...response,\n },\n [mediaPath]\n );\n break;\n } catch (error) {\n retries++;\n console.error(`Error sending message (attempt ${retries}):`, error);\n\n if (retries === maxRetries) {\n console.error('Max retries reached. Failed to send message with attachment.');\n break;\n }\n\n // Wait for a short delay before retrying\n await new Promise((resolve) => setTimeout(resolve, 2000));\n }\n }\n\n return response;\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Downloading the YouTube video now, one sec',\n actions: ['DOWNLOAD_MEDIA'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Can you grab this video for me? https://vimeo.com/123456789',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"Sure thing, I'll download that Vimeo video for you\",\n actions: ['DOWNLOAD_MEDIA'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'I need this video downloaded: https://www.youtube.com/watch?v=abcdefg',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"No problem, I'm on it. I'll have that YouTube video downloaded in a jiffy\",\n actions: ['DOWNLOAD_MEDIA'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default downloadMedia;\n","import fs from 'node:fs';\nimport {\n type Action,\n type ActionExample,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Media,\n type Memory,\n ModelType,\n type State,\n composePromptFromState,\n getEntityDetails,\n parseJSONObjectFromText,\n splitChunks,\n trimTokens,\n} from '@elizaos/core';\nexport const summarizationTemplate = `# Summarized so far (we are adding to this)\n{{currentSummary}}\n\n# Current conversation chunk we are summarizing (includes attachments)\n{{memoriesWithAttachments}}\n\nSummarization objective: {{objective}}\n\n# Instructions: Summarize the conversation so far. Return the summary. Do not acknowledge this request, just summarize and continue the existing summary if there is one. Capture any important details to the objective. Only respond with the new summary text.\nYour response should be extremely detailed and include any and all relevant information.`;\n\n/**\n * Template for providing instructions and details on how to summarize conversation messages and determine the range of dates requested.\n * The template includes placeholders for recent messages, sender name, objective, start and end date range.\n * The response is expected to be formatted as a JSON block with specific structure.\n * @type {string}\n */\nexport const dateRangeTemplate = `# Messages we are summarizing (the conversation is continued after this)\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting a summary of the conversation. Your goal is to determine their objective, along with the range of dates that their request covers.\nThe \"objective\" is a detailed description of what the user wants to summarize based on the conversation. If they just ask for a general summary, you can either base it off the conversation if the summary range is very recent, or set the object to be general, like \"a detailed summary of the conversation between all users\".\nThe \"start\" and \"end\" are the range of dates that the user wants to summarize, relative to the current time. The start and end should be relative to the current time, and measured in seconds, minutes, hours and days. The format is \"2 days ago\" or \"3 hours ago\" or \"4 minutes ago\" or \"5 seconds ago\", i.e. \"<integer> <unit> ago\".\nIf you aren't sure, you can use a default range of \"0 minutes ago\" to \"2 hours ago\" or more. Better to err on the side of including too much than too little.\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"objective\": \"<What the user wants to summarize>\",\n \"start\": \"0 minutes ago\",\n \"end\": \"2 hours ago\"\n}\n\\`\\`\\`\n`;\n\n/**\n * Function to get a date range from user input.\n *\n * @param {IAgentRuntime} runtime - The Agent Runtime object.\n * @param {Memory} _message - The Memory object.\n * @param {State} state - The State object.\n * @return {Promise<{ objective: string; start: string | number; end: string | number; } | null>} Parsed user input containing objective, start, and end timestamps, or null.\n */\nconst getDateRange = async (runtime: IAgentRuntime, _message: Memory, state: State) => {\n const prompt = composePromptFromState({\n state,\n template: dateRangeTemplate,\n });\n\n for (let i = 0; i < 5; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n // try parsing to a json object\n const parsedResponse = parseJSONObjectFromText(response) as {\n objective: string;\n start: string | number;\n end: string | number;\n } | null;\n // see if it contains objective, start and end\n if (parsedResponse) {\n if (parsedResponse.objective && parsedResponse.start && parsedResponse.end) {\n // TODO: parse start and end into timestamps\n const startIntegerString = (parsedResponse.start as string).match(/\\d+/)?.[0];\n const endIntegerString = (parsedResponse.end as string).match(/\\d+/)?.[0];\n\n // parse multiplier\n const multipliers = {\n second: 1 * 1000,\n minute: 60 * 1000,\n hour: 3600 * 1000,\n day: 86400 * 1000,\n };\n\n const startMultiplier = (parsedResponse.start as string).match(\n /second|minute|hour|day/\n )?.[0];\n const endMultiplier = (parsedResponse.end as string).match(/second|minute|hour|day/)?.[0];\n\n const startInteger = startIntegerString ? Number.parseInt(startIntegerString) : 0;\n const endInteger = endIntegerString ? Number.parseInt(endIntegerString) : 0;\n\n // multiply by multiplier\n const startTime = startInteger * multipliers[startMultiplier as keyof typeof multipliers];\n\n const endTime = endInteger * multipliers[endMultiplier as keyof typeof multipliers];\n\n // get the current time and subtract the start and end times\n parsedResponse.start = Date.now() - startTime;\n parsedResponse.end = Date.now() - endTime;\n\n return parsedResponse;\n }\n }\n }\n};\n\n/**\n * Action to summarize a conversation and attachments.\n *\n * @typedef {Action} summarizeAction\n * @property {string} name - The name of the action.\n * @property {string[]} similes - Array of related terms.\n * @property {string} description - Description of the action.\n * @property {Function} validate - Asynchronous function to validate the action.\n * @property {Function} handler - Asynchronous function to handle the action.\n * @property {ActionExample[][]} examples - Array of examples demonstrating the action.\n */\nexport const summarize: Action = {\n name: 'SUMMARIZE_CONVERSATION',\n similes: [\n 'RECAP',\n 'RECAP_CONVERSATION',\n 'SUMMARIZE_CHAT',\n 'SUMMARIZATION',\n 'CHAT_SUMMARY',\n 'CONVERSATION_SUMMARY',\n ],\n description: 'Summarizes the conversation and attachments.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n if (message.content.source !== 'discord') {\n return false;\n }\n // only show if one of the keywords are in the message\n const keywords: string[] = [\n 'summarize',\n 'summarization',\n 'summary',\n 'recap',\n 'report',\n 'overview',\n 'review',\n 'rundown',\n 'wrap-up',\n 'brief',\n 'debrief',\n 'abstract',\n 'synopsis',\n 'outline',\n 'digest',\n 'abridgment',\n 'condensation',\n 'encapsulation',\n 'essence',\n 'gist',\n 'main points',\n 'key points',\n 'key takeaways',\n 'bulletpoint',\n 'highlights',\n 'tldr',\n 'tl;dr',\n 'in a nutshell',\n 'bottom line',\n 'long story short',\n 'sum up',\n 'sum it up',\n 'short version',\n 'bring me up to speed',\n 'catch me up',\n ];\n return keywords.some((keyword) =>\n message.content.text?.toLowerCase().includes(keyword.toLowerCase())\n );\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const callbackData: Content = {\n text: '', // fill in later\n actions: ['SUMMARIZATION_RESPONSE'],\n source: message.content.source,\n attachments: [],\n };\n const { roomId } = message;\n\n // 1. extract date range from the message\n const dateRange = await getDateRange(runtime, message, state);\n if (!dateRange) {\n console.error(\"Couldn't get date range from message\");\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: 'discord',\n thought: `I couldn't get the date range from the message`,\n actions: ['SUMMARIZE_CONVERSATION_FAILED'],\n },\n metadata: {\n type: 'SUMMARIZE_CONVERSATION',\n },\n },\n 'messages'\n );\n return;\n }\n\n const { objective, start, end } = dateRange;\n\n // 2. get these memories from the database\n const memories = await runtime.getMemories({\n tableName: 'messages',\n roomId,\n // subtract start from current time\n start: Number.parseInt(start as string),\n end: Number.parseInt(end as string),\n count: 10000,\n unique: false,\n });\n\n const entities = await getEntityDetails({\n runtime: runtime as IAgentRuntime,\n roomId,\n });\n\n const actorMap = new Map(entities.map((entity) => [entity.id, entity]));\n\n const formattedMemories = memories\n .map((memory) => {\n const attachments = memory.content.attachments\n ?.map((attachment: Media) => {\n return `---\\nAttachment: ${attachment.id}\\n${attachment.description}\\n${attachment.text}\\n---`;\n })\n .join('\\n');\n return `${actorMap.get(memory.entityId)?.name ?? 'Unknown User'} (${actorMap.get(memory.entityId)?.username ?? ''}): ${memory.content.text}\\n${attachments}`;\n })\n .join('\\n');\n\n let currentSummary = '';\n\n const chunkSize = 8000;\n\n const chunks = await splitChunks(formattedMemories, chunkSize, 0);\n\n const _datestr = new Date().toUTCString().replace(/:/g, '-');\n\n state.values.memoriesWithAttachments = formattedMemories;\n state.values.objective = objective;\n\n for (let i = 0; i < chunks.length; i++) {\n const chunk = chunks[i];\n state.values.currentSummary = currentSummary;\n state.values.currentChunk = chunk;\n const template = await trimTokens(summarizationTemplate, chunkSize + 500, runtime);\n const prompt = composePromptFromState({\n state,\n // make sure it fits, we can pad the tokens a bit\n template,\n });\n\n const summary = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n currentSummary = `${currentSummary}\\n${summary}`;\n }\n\n if (!currentSummary) {\n console.error(\"No summary found, that's not good!\");\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: 'discord',\n thought: `I couldn't summarize the conversation`,\n actions: ['SUMMARIZE_CONVERSATION_FAILED'],\n },\n metadata: {\n type: 'SUMMARIZE_CONVERSATION',\n },\n },\n 'messages'\n );\n return;\n }\n\n callbackData.text = currentSummary.trim();\n if (\n callbackData.text &&\n (currentSummary.trim()?.split('\\n').length < 4 ||\n currentSummary.trim()?.split(' ').length < 100)\n ) {\n callbackData.text = `Here is the summary:\n\\`\\`\\`md\n${currentSummary.trim()}\n\\`\\`\\`\n`;\n await callback(callbackData);\n } else if (currentSummary.trim()) {\n const summaryDir = 'cache';\n const summaryFilename = `${summaryDir}/conversation_summary_${Date.now()}`;\n await runtime.setCache<string>(summaryFilename, currentSummary);\n await fs.promises.mkdir(summaryDir, { recursive: true });\n\n await fs.promises.writeFile(summaryFilename, currentSummary, 'utf8');\n // save the summary to a file\n await callback(\n {\n ...callbackData,\n text: `I've attached the summary of the conversation from \\`${new Date(Number.parseInt(start as string)).toString()}\\` to \\`${new Date(Number.parseInt(end as string)).toString()}\\` as a text file.`,\n },\n [summaryFilename]\n );\n } else {\n console.warn('Empty response from summarize conversation action, skipping');\n }\n\n return callbackData;\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: '```js\\nconst x = 10\\n```',\n },\n },\n {\n name: '{{name1}}',\n content: {\n text: \"can you give me a detailed report on what we're talking about?\",\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'sure, no problem, give me a minute to get that together for you',\n actions: ['SUMMARIZE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: \"please summarize the conversation we just had and include this blogpost i'm linking (Attachment: b3e12)\",\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'sure, give me a sec',\n actions: ['SUMMARIZE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Can you summarize what moon and avf are talking about?',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Yeah, just hold on a second while I get that together for you...',\n actions: ['SUMMARIZE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'i need to write a blog post about farming, can you summarize the discussion from a few hours ago?',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'no problem, give me a few minutes to read through everything',\n actions: ['SUMMARIZE'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default summarize;\n","import {\n type Action,\n type ActionExample,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n ModelType,\n type State,\n composePromptFromState,\n createUniqueUuid,\n parseJSONObjectFromText,\n} from '@elizaos/core';\n\nexport const transcriptionTemplate = `# Transcription of media file\n{{mediaTranscript}}\n\n# Instructions: Return only the full transcript of the media file without any additional prompt or commentary.`;\n\n/**\n * Template for generating media attachment ID request for transcription\n *\n * @type {string}\n */\nexport const mediaAttachmentIdTemplate = `# Messages we are transcribing\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting a transcription of a specific media file (audio or video). Your goal is to determine the ID of the attachment they want transcribed.\nThe \"attachmentId\" is the ID of the media file attachment that the user wants transcribed. If not specified, return null.\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"attachmentId\": \"<Attachment ID>\"\n}\n\\`\\`\\`\n`;\n\n/**\n * Asynchronous function to get the media attachment ID from the user input.\n *\n * @param {IAgentRuntime} runtime - The agent runtime object.\n * @param {Memory} _message - The memory object.\n * @param {State} state - The current state of the conversation.\n * @returns {Promise<string | null>} A promise that resolves with the media attachment ID or null.\n */\nconst getMediaAttachmentId = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<string | null> => {\n const prompt = composePromptFromState({\n state,\n template: mediaAttachmentIdTemplate,\n });\n\n for (let i = 0; i < 5; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response) as {\n attachmentId: string;\n } | null;\n\n if (parsedResponse?.attachmentId) {\n return parsedResponse.attachmentId;\n }\n }\n return null;\n};\n\n/**\n * Action for transcribing the full text of an audio or video file that the user has attached.\n *\n * @typedef {Object} Action\n * @property {string} name - The name of the action.\n * @property {string[]} similes - Similes associated with the action.\n * @property {string} description - Description of the action.\n * @property {Function} validate - Validation function for the action.\n * @property {Function} handler - Handler function for the action.\n * @property {ActionExample[][]} examples - Examples demonstrating the action.\n */\nexport const transcribeMedia: Action = {\n name: 'TRANSCRIBE_MEDIA',\n similes: [\n 'TRANSCRIBE_AUDIO',\n 'TRANSCRIBE_VIDEO',\n 'MEDIA_TRANSCRIPT',\n 'VIDEO_TRANSCRIPT',\n 'AUDIO_TRANSCRIPT',\n ],\n description: 'Transcribe the full text of an audio or video file that the user has attached.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n if (message.content.source !== 'discord') {\n return false;\n }\n\n const keywords: string[] = [\n 'transcribe',\n 'transcript',\n 'audio',\n 'video',\n 'media',\n 'youtube',\n 'meeting',\n 'recording',\n 'podcast',\n 'call',\n 'conference',\n 'interview',\n 'speech',\n 'lecture',\n 'presentation',\n ];\n return keywords.some((keyword) =>\n message.content.text?.toLowerCase().includes(keyword.toLowerCase())\n );\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const callbackData: Content = {\n text: '', // fill in later\n actions: ['TRANSCRIBE_MEDIA_RESPONSE'],\n source: message.content.source,\n attachments: [],\n };\n\n const attachmentId = await getMediaAttachmentId(runtime, message, state);\n if (!attachmentId) {\n console.error(\"Couldn't get media attachment ID from message\");\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: 'discord',\n thought: `I couldn't find the media attachment ID in the message`,\n actions: ['TRANSCRIBE_MEDIA_FAILED'],\n },\n metadata: {\n type: 'TRANSCRIBE_MEDIA',\n },\n },\n 'messages'\n );\n return;\n }\n\n const conversationLength = runtime.getConversationLength();\n\n const recentMessages = await runtime.getMemories({\n tableName: 'messages',\n roomId: message.roomId,\n count: conversationLength,\n unique: false,\n });\n\n const attachment = recentMessages\n .filter((msg) => msg.content.attachments && msg.content.attachments.length > 0)\n .flatMap((msg) => msg.content.attachments)\n .find((attachment) => attachment?.id.toLowerCase() === attachmentId.toLowerCase());\n\n if (!attachment) {\n console.error(`Couldn't find attachment with ID ${attachmentId}`);\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: 'discord',\n thought: `I couldn't find the media attachment with ID ${attachmentId}`,\n actions: ['TRANSCRIBE_MEDIA_FAILED'],\n },\n metadata: {\n type: 'TRANSCRIBE_MEDIA',\n },\n },\n 'messages'\n );\n return;\n }\n\n const mediaTranscript = attachment.text;\n\n callbackData.text = mediaTranscript.trim();\n\n // if callbackData.text is < 4 lines or < 100 words, then we we callback with normal message wrapped in markdown block\n if (\n callbackData.text &&\n (callbackData.text?.split('\\n').length < 4 || callbackData.text?.split(' ').length < 100)\n ) {\n callbackData.text = `Here is the transcript:\n\\`\\`\\`md\n${mediaTranscript.trim()}\n\\`\\`\\`\n`;\n await callback(callbackData);\n }\n // if text is big, let's send as an attachment\n else if (callbackData.text) {\n const transcriptFilename = `content/transcript_${Date.now()}`;\n\n // save the transcript to a file\n await runtime.setCache<string>(transcriptFilename, callbackData.text);\n\n await callback(\n {\n ...callbackData,\n text: `I've attached the transcript as a text file.`,\n },\n [transcriptFilename]\n );\n } else {\n console.warn('Empty response from transcribe media action, skipping');\n }\n\n return callbackData;\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Please transcribe the audio file I just sent.',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"Sure, I'll transcribe the full audio for you.\",\n actions: ['TRANSCRIBE_MEDIA'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Can I get a transcript of that video recording?',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Absolutely, give me a moment to generate the full transcript of the video.',\n actions: ['TRANSCRIBE_MEDIA'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default transcribeMedia;\n","// src/actions/joinVoice\nimport {\n type Action,\n type ActionExample,\n ChannelType,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n ModelType,\n type State,\n composePromptFromState,\n createUniqueUuid,\n logger,\n} from '@elizaos/core';\nimport {\n type BaseGuildVoiceChannel,\n type Channel,\n ChannelType as DiscordChannelType,\n type Guild,\n} from 'discord.js';\nimport type { DiscordService } from '../service';\nimport { ServiceType } from '../types';\nimport type { VoiceManager } from '../voice';\n\nexport const joinVoice: Action = {\n name: 'JOIN_VOICE',\n similes: [\n 'JOIN_VOICE',\n 'JOIN_VC',\n 'JOIN_VOICE_CHAT',\n 'JOIN_VOICE_CHANNEL',\n 'JOIN_MEETING',\n 'JOIN_CALL',\n ],\n validate: async (runtime: IAgentRuntime, message: Memory, state: State) => {\n if (message.content.source !== 'discord') {\n // not a discord message\n return false;\n }\n\n const room = state.data.room ?? (await runtime.getRoom(message.roomId));\n\n if (room?.type !== ChannelType.GROUP && room?.type !== ChannelType.VOICE_GROUP) {\n return false;\n }\n\n const client = runtime.getService(ServiceType.DISCORD);\n\n if (!client) {\n logger.error('Discord client not found');\n return false;\n }\n\n return true;\n },\n description: 'Join a voice channel to participate in voice chat.',\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ): Promise<boolean> => {\n const room = state.data.room ?? (await runtime.getRoom(message.roomId));\n // Ensure messageContent is a string, defaulting to empty if undefined\n const messageContent = message?.content?.text?.toLowerCase() ?? '';\n\n if (!room) {\n throw new Error('No room found');\n }\n\n if (room?.type !== ChannelType.GROUP && room?.type !== ChannelType.VOICE_GROUP) {\n return false;\n }\n\n const serverId = room.serverId;\n\n if (!serverId) {\n throw new Error('No server ID found');\n }\n\n const discordClient = runtime.getService(ServiceType.DISCORD) as DiscordService;\n const client = discordClient.client;\n const voiceManager = discordClient.voiceManager as VoiceManager;\n\n if (!client) {\n logger.error('Discord client not found');\n return false;\n }\n\n const voiceChannels = (client.guilds.cache.get(serverId) as Guild).channels.cache.filter(\n (channel: Channel) => channel.type === DiscordChannelType.GuildVoice\n );\n\n const targetChannel = voiceChannels.find((channel) => {\n const name = (channel as { name: string }).name.toLowerCase();\n // remove all non-alphanumeric characters (keep spaces between words)\n const replacedName = name.replace(/[^a-z0-9 ]/g, '');\n\n // messageContent is now guaranteed to be a string\n return (\n name.includes(messageContent) ||\n messageContent.includes(name) ||\n replacedName.includes(messageContent) ||\n messageContent.includes(replacedName)\n );\n });\n\n if (targetChannel) {\n voiceManager.joinChannel(targetChannel as BaseGuildVoiceChannel);\n return true;\n }\n const guild = client.guilds.cache.get(serverId);\n const members = guild?.members.cache;\n\n // get the member who's stringTouuid(id) === message userId\n const member = members?.find(\n (member) => createUniqueUuid(runtime, member.id) === message.entityId\n );\n\n if (member?.voice?.channel) {\n const userVoiceChannel = member.voice.channel as BaseGuildVoiceChannel;\n voiceManager.joinChannel(userVoiceChannel);\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: 'discord',\n thought: `I joined the voice channel ${userVoiceChannel.name}`,\n actions: ['JOIN_VOICE_STARTED'],\n },\n metadata: {\n type: 'JOIN_VOICE',\n },\n },\n 'messages'\n );\n\n // save a memory for the new channel as well\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: createUniqueUuid(runtime, userVoiceChannel.id),\n content: {\n source: 'discord',\n thought: `I joined the voice channel ${userVoiceChannel.name}`,\n actions: ['JOIN_VOICE_STARTED'],\n },\n metadata: {\n type: 'JOIN_VOICE',\n },\n },\n 'messages'\n );\n return true;\n }\n\n const messageTemplate = `\nThe user has requested to join a voice channel.\nHere is the list of channels available in the server:\n{{voiceChannels}}\n\nHere is the user's request:\n{{userMessage}}\n\nPlease respond with the name of the voice channel which the bot should join. Try to infer what channel the user is talking about. If the user didn't specify a voice channel, respond with \"none\".\nYou should only respond with the name of the voice channel or none, no commentary or additional information should be included.\n`;\n\n const guessState = {\n userMessage: message.content.text,\n voiceChannels: voiceChannels.map((channel) => (channel as { name: string }).name).join('\\n'),\n };\n\n const prompt = composePromptFromState({\n template: messageTemplate,\n state: guessState as unknown as State,\n });\n\n const responseContent = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n if (responseContent && responseContent.trim().length > 0) {\n // join the voice channel\n const channelName = responseContent.toLowerCase();\n\n const targetChannel = voiceChannels.find((channel) => {\n const name = (channel as { name: string }).name.toLowerCase();\n\n // remove all non-alphanumeric characters (keep spaces between words)\n const replacedName = name.replace(/[^a-z0-9 ]/g, '');\n\n return (\n name.includes(channelName) ||\n channelName.includes(name) ||\n replacedName.includes(channelName) ||\n channelName.includes(replacedName)\n );\n });\n\n if (targetChannel) {\n voiceManager.joinChannel(targetChannel as BaseGuildVoiceChannel);\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: 'discord',\n thought: `I joined the voice channel ${targetChannel.name}`,\n actions: ['JOIN_VOICE_STARTED'],\n },\n metadata: {\n type: 'JOIN_VOICE',\n },\n },\n 'messages'\n );\n\n // save a memory for the new channel as well\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: createUniqueUuid(runtime, targetChannel.id),\n content: {\n source: 'discord',\n thought: `I joined the voice channel ${targetChannel.name}`,\n actions: ['JOIN_VOICE_STARTED'],\n },\n metadata: {\n type: 'JOIN_VOICE',\n },\n },\n 'messages'\n );\n return true;\n }\n }\n\n await callback({\n text: \"I couldn't figure out which channel you wanted me to join.\",\n source: 'discord',\n });\n return false;\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: \"Hey, let's jump into the 'General' voice and chat\",\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Sounds good',\n actions: ['JOIN_VOICE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: '{{name2}}, can you join the vc, I want to discuss our strat',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"Sure I'll join right now\",\n actions: ['JOIN_VOICE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: \"hey {{name2}}, we're having a team meeting in the 'conference' voice channel, plz join us\",\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'OK see you there',\n actions: ['JOIN_VOICE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: \"{{name2}}, let's have a quick voice chat in the 'Lounge' channel.\",\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'kk be there in a sec',\n actions: ['JOIN_VOICE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: \"Hey {{name2}}, can you join me in the 'Music' voice channel\",\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Sure',\n actions: ['JOIN_VOICE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'join voice chat with us {{name2}}',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'coming',\n actions: ['JOIN_VOICE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'hop in vc {{name2}}',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'joining now',\n actions: ['JOIN_VOICE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'get in vc with us {{name2}}',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'im in',\n actions: ['JOIN_VOICE'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default joinVoice;\n","import type { Character, EntityPayload, MessagePayload, WorldPayload } from '@elizaos/core';\nimport type {\n Client as DiscordJsClient,\n Guild,\n GuildMember,\n Message,\n MessageReaction,\n User,\n VoiceState,\n} from 'discord.js';\n\n/**\n * Discord-specific event types\n */\nexport enum DiscordEventTypes {\n // Message events (prefixed versions of core events)\n MESSAGE_RECEIVED = 'DISCORD_MESSAGE_RECEIVED',\n MESSAGE_SENT = 'DISCORD_MESSAGE_SENT',\n\n // Reaction events\n REACTION_RECEIVED = 'DISCORD_REACTION_RECEIVED',\n REACTION_REMOVED = 'DISCORD_REACTION_REMOVED',\n\n // Server events\n WORLD_JOINED = 'DISCORD_WORLD_JOINED',\n WORLD_CONNECTED = 'DISCORD_SERVER_CONNECTED',\n\n // User events\n ENTITY_JOINED = 'DISCORD_USER_JOINED',\n ENTITY_LEFT = 'DISCORD_USER_LEFT',\n\n // Voice events\n VOICE_STATE_CHANGED = 'DISCORD_VOICE_STATE_CHANGED',\n}\n\n/**\n * Discord-specific message received payload\n */\nexport interface DiscordMessageReceivedPayload extends MessagePayload {\n /** The original Discord message */\n originalMessage: Message;\n}\n\n/**\n * Discord-specific message sent payload\n */\nexport interface DiscordMessageSentPayload extends MessagePayload {\n /** The original Discord messages sent */\n originalMessages: Message[];\n}\n\n/**\n * Discord-specific reaction received payload\n */\nexport interface DiscordReactionPayload extends MessagePayload {\n /** The original Discord reaction */\n originalReaction: MessageReaction;\n /** The user who reacted */\n user: User;\n}\n/**\n * Discord-specific server payload\n */\nexport interface DiscordServerPayload extends WorldPayload {\n /** The original Discord guild */\n server: Guild;\n}\n\n/**\n * Discord-specific user joined payload\n */\nexport interface DiscordUserJoinedPayload extends EntityPayload {\n /** The original Discord guild member */\n member: GuildMember;\n}\n\n/**\n * Discord-specific user left payload\n */\nexport interface DiscordUserLeftPayload extends EntityPayload {\n /** The original Discord guild member */\n member: GuildMember;\n}\n\n/**\n * Discord-specific voice state changed payload\n */\nexport interface DiscordVoiceStateChangedPayload {\n /** The original Discord voice state */\n voiceState: VoiceState;\n}\n\n/**\n * Maps Discord event types to their payload interfaces\n */\nexport interface DiscordEventPayloadMap {\n [DiscordEventTypes.MESSAGE_RECEIVED]: DiscordMessageReceivedPayload;\n [DiscordEventTypes.MESSAGE_SENT]: DiscordMessageSentPayload;\n [DiscordEventTypes.REACTION_RECEIVED]: DiscordReactionPayload;\n [DiscordEventTypes.REACTION_REMOVED]: DiscordReactionPayload;\n [DiscordEventTypes.WORLD_JOINED]: DiscordServerPayload;\n [DiscordEventTypes.WORLD_CONNECTED]: DiscordServerPayload;\n [DiscordEventTypes.ENTITY_JOINED]: DiscordUserJoinedPayload;\n [DiscordEventTypes.ENTITY_LEFT]: DiscordUserLeftPayload;\n [DiscordEventTypes.VOICE_STATE_CHANGED]: DiscordVoiceStateChangedPayload;\n}\n\n/**\n * Interface representing a Discord service.\n *\n * @typedef {Object} IDiscordService\n * @property {DiscordJsClient} client - The Discord client object.\n * @property {Character} character - The character object.\n */\nexport interface IDiscordService {\n // Allow client to be null to handle initialization failures\n client: DiscordJsClient | null;\n character: Character;\n}\n\nexport const DISCORD_SERVICE_NAME = 'discord';\n\nexport const ServiceType = {\n DISCORD: 'discord',\n} as const;\n\nexport interface DiscordComponentOptions {\n type: number;\n custom_id: string;\n label?: string;\n style?: number;\n placeholder?: string;\n min_values?: number;\n max_values?: number;\n options?: Array<{\n label: string;\n value: string;\n description?: string;\n }>;\n}\n\nexport interface DiscordActionRow {\n type: 1;\n components: DiscordComponentOptions[];\n}\n","// src/actions/leaveVoice\nimport {\n type Action,\n type ActionExample,\n ChannelType,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n type State,\n createUniqueUuid,\n logger,\n} from '@elizaos/core';\nimport { BaseGuildVoiceChannel } from 'discord.js';\n\nimport type { DiscordService } from '../service';\nimport { ServiceType } from '../types';\nimport type { VoiceManager } from '../voice';\n\nexport const leaveVoice: Action = {\n name: 'LEAVE_VOICE',\n similes: [\n 'LEAVE_VOICE',\n 'LEAVE_VC',\n 'LEAVE_VOICE_CHAT',\n 'LEAVE_VOICE_CHANNEL',\n 'LEAVE_MEETING',\n 'LEAVE_CALL',\n ],\n validate: async (runtime: IAgentRuntime, message: Memory, state: State) => {\n if (message.content.source !== 'discord') {\n // not a discord message\n return false;\n }\n\n const service = runtime.getService(ServiceType.DISCORD) as DiscordService;\n\n if (!service) {\n logger.error('Discord client not found');\n return false;\n }\n\n const room = state.data.room ?? (await runtime.getRoom(message.roomId));\n\n if (room?.type !== ChannelType.GROUP && room?.type !== ChannelType.VOICE_GROUP) {\n return false;\n }\n\n // Check if the client is connected to any voice channel\n const isConnectedToVoice = service.client.voice.adapters.size > 0;\n\n return isConnectedToVoice;\n },\n description: 'Leave the current voice channel.',\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state: State,\n _options: any\n ): Promise<boolean> => {\n const room = await runtime.getRoom(message.roomId);\n if (!room) {\n throw new Error('No room found');\n }\n\n if (room?.type !== ChannelType.GROUP && room?.type !== ChannelType.VOICE_GROUP) {\n throw new Error('Not a group');\n }\n\n const serverId = room.serverId;\n\n if (!serverId) {\n throw new Error('No server ID found 9');\n }\n const discordClient = runtime.getService(ServiceType.DISCORD) as DiscordService;\n const voiceManager = discordClient.voiceManager as VoiceManager;\n const client = discordClient.client;\n\n if (!client) {\n logger.error('Discord client not found');\n throw new Error('Discord client not found');\n }\n\n if (!voiceManager) {\n logger.error('voiceManager is not available.');\n throw new Error('voiceManager is not available.');\n }\n\n const guild = client.guilds.cache.get(serverId);\n\n if (!guild) {\n console.warn('Bot is not in any voice channel.');\n // create a memory with thought to self to self\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: 'discord',\n thought: \"I tried to leave the voice channel but I'm not in any voice channel.\",\n actions: ['LEAVE_VOICE'],\n },\n metadata: {\n type: 'LEAVE_VOICE',\n },\n },\n 'messages'\n );\n return false;\n }\n\n const voiceChannel = guild.members.me?.voice.channel;\n\n if (!voiceChannel || !(voiceChannel instanceof BaseGuildVoiceChannel)) {\n console.warn('Could not retrieve the voice channel.');\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: 'discord',\n thought: \"I tried to leave the voice channel but I couldn't find it.\",\n actions: ['LEAVE_VOICE'],\n },\n metadata: {\n type: 'LEAVE_VOICE',\n },\n },\n 'messages'\n );\n return false;\n }\n\n const connection = voiceManager.getVoiceConnection(guild.id);\n if (!connection) {\n console.warn('No active voice connection found for the bot.');\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: 'discord',\n thought: \"I tried to leave the voice channel but I couldn't find the connection.\",\n actions: ['LEAVE_VOICE'],\n },\n metadata: {\n type: 'LEAVE_VOICE',\n },\n },\n 'messages'\n );\n return false;\n }\n\n voiceManager.leaveChannel(voiceChannel);\n // save a memory for the new channel as well\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: createUniqueUuid(runtime, voiceChannel.id),\n content: {\n source: 'discord',\n thought: `I left the voice channel ${voiceChannel.name}`,\n actions: ['LEAVE_VOICE_STARTED'],\n },\n metadata: {\n type: 'LEAVE_VOICE',\n },\n },\n 'messages'\n );\n\n return true;\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Hey {{name2}} please leave the voice channel',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Sure',\n actions: ['LEAVE_VOICE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'I have to go now but thanks for the chat',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'You too, talk to you later',\n actions: ['LEAVE_VOICE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Great call everyone, hopping off now',\n actions: ['LEAVE_VOICE'],\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"Agreed, I'll hop off too\",\n actions: ['LEAVE_VOICE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Hey {{name2}} I need you to step away from the voice chat for a bit',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"No worries, I'll leave the voice channel\",\n actions: ['LEAVE_VOICE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: '{{name2}}, I think we covered everything, you can leave the voice chat now',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Sounds good, see you both later',\n actions: ['LEAVE_VOICE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'leave voice {{name2}}',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'ok leaving',\n actions: ['LEAVE_VOICE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'plz leave the voice chat {{name2}}',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'aight im out',\n actions: ['LEAVE_VOICE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'yo {{name2}} gtfo the vc',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'sorry, talk to you later',\n actions: ['LEAVE_VOICE'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default leaveVoice;\n","import type { IAgentRuntime, Memory, Provider, State } from '@elizaos/core';\nimport { ChannelType } from '@elizaos/core';\nimport type { DiscordService } from '../service';\nimport { ServiceType } from '../types';\n\n/**\n * Represents a provider for retrieving channel state information.\n * @type {Provider}\n * @property {string} name - The name of the channel state provider.\n * @property {Function} get - Asynchronous function that retrieves channel state information based on the provided runtime, message, and optional state parameters.\n * @param {IAgentRuntime} runtime - The agent runtime.\n * @param {Memory} message - The message object.\n * @param {State} [state] - Optional state object.\n * @returns {Promise<Object>} A promise that resolves to an object containing channel state data, values, and text.\n */\nexport const channelStateProvider: Provider = {\n name: 'channelState',\n get: async (runtime: IAgentRuntime, message: Memory, state: State) => {\n const room = state.data?.room ?? (await runtime.getRoom(message.roomId));\n if (!room) {\n throw new Error('No room found');\n }\n\n // if message source is not discord, return\n if (message.content.source !== 'discord') {\n return {\n data: {},\n values: {},\n text: '',\n };\n }\n\n const agentName = state?.agentName || 'The agent';\n const senderName = state?.senderName || 'someone';\n\n let responseText = '';\n let channelType = '';\n let serverName = '';\n let channelId = '';\n const serverId = room.serverId;\n\n if (room.type === ChannelType.DM) {\n channelType = 'DM';\n responseText = `${agentName} is currently in a direct message conversation with ${senderName}. ${agentName} should engage in conversation, should respond to messages that are addressed to them and only ignore messages that seem to not require a response.`;\n } else {\n channelType = 'GROUP';\n\n if (!serverId) {\n console.error('No server ID found');\n return {\n data: {\n room,\n channelType,\n },\n values: {\n channelType,\n },\n text: '',\n };\n }\n\n channelId = room.channelId;\n\n const discordService = runtime.getService(ServiceType.DISCORD) as DiscordService;\n if (!discordService) {\n console.warn('No discord client found');\n return {\n data: {\n room,\n channelType,\n serverId,\n },\n values: {\n channelType,\n serverId,\n },\n text: '',\n };\n }\n\n const guild = discordService.client?.guilds.cache.get(serverId);\n if (!guild) {\n console.warn(`Guild not found for serverId: ${serverId}`);\n return {\n data: {\n room,\n channelType,\n serverId,\n channelId,\n },\n values: {\n channelType,\n serverId,\n channelId,\n },\n text: '',\n };\n }\n serverName = guild.name;\n\n responseText = `${agentName} is currently having a conversation in the channel \\`@${channelId} in the server \\`${serverName}\\` (@${serverId})`;\n responseText += `\\n${agentName} is in a room with other users and should be self-conscious and only participate when directly addressed or when the conversation is relevant to them.`;\n }\n\n return {\n data: {\n room,\n channelType,\n serverId,\n serverName,\n channelId,\n },\n values: {\n channelType,\n serverName,\n channelId,\n },\n text: responseText,\n };\n },\n};\n\nexport default channelStateProvider;\n","import { getVoiceConnection } from '@discordjs/voice';\nimport type { IAgentRuntime, Memory, Provider, State } from '@elizaos/core';\nimport { ChannelType } from '@elizaos/core';\n\n/**\n * Provides information about the voice state of the user, including whether they are currently in a voice channel.\n *\n * @param {IAgentRuntime} runtime - The runtime object for the agent\n * @param {Memory} message - The message object containing room ID\n * @param {State} [state] - Optional state object for the user\n * @returns {Object} An object containing information about the voice state of the user\n */\nexport const voiceStateProvider: Provider = {\n name: 'voiceState',\n get: async (runtime: IAgentRuntime, message: Memory, state?: State) => {\n // Voice doesn't get a discord message, so we need to use the channel for guild data\n const room = await runtime.getRoom(message.roomId);\n if (!room) {\n throw new Error('No room found');\n }\n\n if (room.type !== ChannelType.GROUP) {\n // only handle in a group scenario for now\n return {\n data: {\n isInVoiceChannel: false,\n room,\n },\n values: {\n isInVoiceChannel: 'false',\n roomType: room.type,\n },\n text: '',\n };\n }\n\n const serverId = room.serverId;\n\n if (!serverId) {\n throw new Error('No server ID found 10');\n }\n\n const connection = getVoiceConnection(serverId);\n const agentName = state?.agentName || 'The agent';\n\n if (!connection) {\n return {\n data: {\n isInVoiceChannel: false,\n room,\n serverId,\n },\n values: {\n isInVoiceChannel: 'false',\n serverId,\n },\n text: `${agentName} is not currently in a voice channel`,\n };\n }\n\n const worldId = room.worldId;\n\n // get the world from the runtime.getWorld\n const world = await runtime.getWorld(worldId);\n\n if (!world) {\n throw new Error('No world found');\n }\n\n const worldName = world.name;\n const roomType = room.type;\n const channelId = room.channelId;\n const channelName = room.name;\n\n if (!channelId) {\n return {\n data: {\n isInVoiceChannel: true,\n room,\n serverId,\n world,\n connection,\n },\n values: {\n isInVoiceChannel: 'true',\n serverId,\n worldName,\n roomType,\n },\n text: `${agentName} is in an invalid voice channel`,\n };\n }\n\n return {\n data: {\n isInVoiceChannel: true,\n room,\n serverId,\n world,\n connection,\n channelId,\n channelName,\n },\n values: {\n isInVoiceChannel: 'true',\n serverId,\n worldName,\n roomType,\n channelId,\n channelName,\n },\n text: `${agentName} is currently in the voice channel: ${channelName} (ID: ${channelId})`,\n };\n },\n};\n\nexport default voiceStateProvider;\n","import {\n ChannelType,\n type Character,\n type Content,\n type Entity,\n EventType,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n Role,\n Service,\n type TargetInfo,\n type UUID,\n type World,\n createUniqueUuid,\n logger,\n} from '@elizaos/core';\nimport {\n type Channel,\n ChannelType as DiscordChannelType,\n Client as DiscordJsClient,\n Events,\n GatewayIntentBits,\n type Guild,\n type GuildMember,\n type MessageReaction,\n type PartialMessageReaction,\n type PartialUser,\n Partials,\n PermissionsBitField,\n type TextChannel,\n type User,\n type Interaction,\n Collection,\n} from 'discord.js';\nimport { DISCORD_SERVICE_NAME } from './constants';\nimport { MessageManager } from './messages';\nimport { DiscordEventTypes, type IDiscordService } from './types';\nimport { VoiceManager } from './voice';\n\n/**\n * DiscordService class representing a service for interacting with Discord.\n * @extends Service\n * @implements IDiscordService\n * @property {string} serviceType - The type of service, set to DISCORD_SERVICE_NAME.\n * @property {string} capabilityDescription - A description of the service's capabilities.\n * @property {DiscordJsClient} client - The DiscordJsClient used for communication.\n * @property {Character} character - The character associated with the service.\n * @property {MessageManager} messageManager - The manager for handling messages.\n * @property {VoiceManager} voiceManager - The manager for handling voice communication.\n */\n\nexport class DiscordService extends Service implements IDiscordService {\n static serviceType: string = DISCORD_SERVICE_NAME;\n capabilityDescription = 'The agent is able to send and receive messages on discord';\n client: DiscordJsClient | null;\n character: Character;\n messageManager?: MessageManager;\n voiceManager?: VoiceManager;\n private userSelections: Map<string, { [key: string]: any }> = new Map();\n private timeouts: NodeJS.Timeout[] = [];\n /**\n * List of allowed channel IDs (parsed from CHANNEL_IDS env var).\n * If undefined, all channels are allowed.\n */\n private allowedChannelIds?: string[];\n\n /**\n * Constructor for Discord client.\n * Initializes the Discord client with specified intents and partials,\n * sets up event listeners, and ensures all servers exist.\n *\n * @param {IAgentRuntime} runtime - The AgentRuntime instance\n */\n constructor(runtime: IAgentRuntime) {\n super(runtime);\n\n this.character = runtime.character;\n\n // Parse CHANNEL_IDS env var to restrict the bot to specific channels\n const channelIdsRaw = runtime.getSetting('CHANNEL_IDS') as string | undefined;\n if (channelIdsRaw && channelIdsRaw.trim()) {\n this.allowedChannelIds = channelIdsRaw\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n }\n\n // Check if Discord API token is available and valid\n const token = runtime.getSetting('DISCORD_API_TOKEN') as string;\n if (!token || token.trim() === '') {\n logger.warn('Discord API Token not provided - Discord functionality will be unavailable');\n this.client = null;\n return;\n }\n\n try {\n this.client = new DiscordJsClient({\n intents: [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMembers,\n GatewayIntentBits.GuildPresences,\n GatewayIntentBits.DirectMessages,\n GatewayIntentBits.GuildVoiceStates,\n GatewayIntentBits.MessageContent,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.DirectMessageTyping,\n GatewayIntentBits.GuildMessageTyping,\n GatewayIntentBits.GuildMessageReactions,\n ],\n partials: [Partials.Channel, Partials.Message, Partials.User, Partials.Reaction],\n });\n\n this.runtime = runtime;\n this.voiceManager = new VoiceManager(this, runtime);\n this.messageManager = new MessageManager(this);\n\n this.client.once(Events.ClientReady, this.onReady.bind(this));\n this.client.login(token).catch((error) => {\n logger.error(\n `Failed to login to Discord: ${error instanceof Error ? error.message : String(error)}`\n );\n this.client = null;\n });\n\n this.setupEventListeners();\n this.registerSendHandler(); // Register handler during construction\n } catch (error) {\n logger.error(\n `Error initializing Discord client: ${error instanceof Error ? error.message : String(error)}`\n );\n this.client = null;\n }\n }\n\n static async start(runtime: IAgentRuntime) {\n const service = new DiscordService(runtime);\n return service;\n }\n\n /**\n * Registers the send handler with the runtime.\n * @private\n */\n private registerSendHandler(): void {\n if (this.runtime) {\n this.runtime.registerSendHandler('discord', this.handleSendMessage.bind(this));\n }\n }\n\n /**\n * The SendHandlerFunction implementation for Discord.\n * @param {IAgentRuntime} runtime - The runtime instance.\n * @param {TargetInfo} target - The target information for the message.\n * @param {Content} content - The content of the message to send.\n * @returns {Promise<void>} A promise that resolves when the message is sent or rejects on error.\n * @throws {Error} If the client is not ready, target is invalid, or sending fails.\n */\n async handleSendMessage(\n runtime: IAgentRuntime,\n target: TargetInfo,\n content: Content\n ): Promise<void> {\n if (!this.client?.isReady()) {\n logger.error('[Discord SendHandler] Client not ready.');\n throw new Error('Discord client is not ready.');\n }\n\n // Skip sending if channel restrictions are set and target channel is not allowed\n if (\n target.channelId &&\n this.allowedChannelIds &&\n !this.allowedChannelIds.includes(target.channelId)\n ) {\n logger.warn(\n `[Discord SendHandler] Channel ${target.channelId} is not in allowed channels, skipping send.`\n );\n return;\n }\n\n let targetChannel: Channel | undefined | null = null;\n\n try {\n // Determine target based on provided info\n if (target.channelId) {\n targetChannel = await this.client.channels.fetch(target.channelId);\n } else if (target.entityId) {\n // Attempt to convert runtime UUID to Discord snowflake ID\n // NOTE: This assumes a mapping exists or the UUID *is* the snowflake ID\n const discordUserId = target.entityId as string; // May need more robust conversion\n const user = await this.client.users.fetch(discordUserId);\n if (user) {\n targetChannel = (await user.dmChannel) ?? (await user.createDM());\n }\n } else {\n throw new Error('Discord SendHandler requires channelId or entityId.');\n }\n\n if (!targetChannel) {\n throw new Error(\n `Could not find target Discord channel/DM for target: ${JSON.stringify(target)}`\n );\n }\n\n // Type guard to ensure the channel is text-based\n if (targetChannel.isTextBased() && !targetChannel.isVoiceBased()) {\n // Further check if it's a channel where bots can send messages\n if ('send' in targetChannel && typeof targetChannel.send === 'function') {\n if (content.text) {\n // Split message if longer than Discord limit (2000 chars)\n const chunks = this.splitMessage(content.text, 2000);\n for (const chunk of chunks) {\n await targetChannel.send(chunk);\n }\n } else {\n logger.warn('[Discord SendHandler] No text content provided to send.');\n }\n // TODO: Add attachment handling here if necessary\n } else {\n throw new Error(`Target channel ${targetChannel.id} does not have a send method.`);\n }\n } else {\n throw new Error(\n `Target channel ${targetChannel.id} is not a valid text-based channel for sending messages.`\n );\n }\n } catch (error) {\n logger.error(\n `[Discord SendHandler] Error sending message: ${error instanceof Error ? error.message : String(error)}`,\n {\n target,\n content,\n }\n );\n throw error;\n }\n }\n\n /**\n * Helper function to split a string into chunks of a maximum length.\n *\n * @param {string} text - The text to split.\n * @param {number} maxLength - The maximum length of each chunk.\n * @returns {string[]} An array of text chunks.\n * @private\n */\n // Helper to split messages\n private splitMessage(text: string, maxLength: number): string[] {\n const chunks: string[] = [];\n let currentChunk = '';\n const lines = text.split('\\n');\n for (const line of lines) {\n if (currentChunk.length + line.length + 1 <= maxLength) {\n currentChunk += (currentChunk ? '\\n' : '') + line;\n } else {\n if (currentChunk) chunks.push(currentChunk);\n // Handle lines longer than the max length (split them)\n if (line.length > maxLength) {\n for (let i = 0; i < line.length; i += maxLength) {\n chunks.push(line.substring(i, i + maxLength));\n }\n currentChunk = ''; // Reset chunk after splitting long line\n } else {\n currentChunk = line;\n }\n }\n }\n if (currentChunk) chunks.push(currentChunk);\n return chunks;\n }\n\n /**\n * Set up event listeners for the client.\n * @private\n */\n private setupEventListeners() {\n if (!this.client) {\n return; // Skip if client is not available\n }\n\n // Setup handling for direct messages\n this.client.on('messageCreate', (message) => {\n // Skip if we're sending the message or in deleted state\n if (message.author.id === this.client?.user?.id || message.author.bot) {\n return;\n }\n\n // Skip if channel restrictions are set and this channel is not allowed\n if (this.allowedChannelIds && !this.allowedChannelIds.includes(message.channel.id)) {\n return;\n }\n\n try {\n // Ensure messageManager exists\n this.messageManager?.handleMessage(message);\n } catch (error) {\n logger.error(`Error handling message: ${error}`);\n }\n });\n\n // Setup handling for reactions\n this.client.on('messageReactionAdd', async (reaction, user) => {\n if (user.id === this.client?.user?.id) {\n return;\n }\n // Skip if channel restrictions are set and this reaction is not in an allowed channel\n if (\n this.allowedChannelIds &&\n reaction.message.channel &&\n !this.allowedChannelIds.includes(reaction.message.channel.id)\n ) {\n return;\n }\n try {\n await this.handleReactionAdd(reaction, user);\n } catch (error) {\n logger.error(`Error handling reaction add: ${error}`);\n }\n });\n\n // Handle reaction removal\n this.client.on('messageReactionRemove', async (reaction, user) => {\n if (user.id === this.client?.user?.id) {\n return;\n }\n // Skip if channel restrictions are set and this reaction is not in an allowed channel\n if (\n this.allowedChannelIds &&\n reaction.message.channel &&\n !this.allowedChannelIds.includes(reaction.message.channel.id)\n ) {\n return;\n }\n try {\n await this.handleReactionRemove(reaction, user);\n } catch (error) {\n logger.error(`Error handling reaction remove: ${error}`);\n }\n });\n\n // Setup guild (server) event handlers\n this.client.on('guildCreate', async (guild) => {\n try {\n await this.handleGuildCreate(guild);\n } catch (error) {\n logger.error(`Error handling guild create: ${error}`);\n }\n });\n\n // Setup member (user) joining handlers\n this.client.on('guildMemberAdd', async (member) => {\n try {\n await this.handleGuildMemberAdd(member);\n } catch (error) {\n logger.error(`Error handling guild member add: ${error}`);\n }\n });\n\n // Interaction handlers\n this.client.on('interactionCreate', async (interaction) => {\n // Skip if channel restrictions are set and this interaction is not in an allowed channel\n if (\n this.allowedChannelIds &&\n interaction.channelId &&\n !this.allowedChannelIds.includes(interaction.channelId)\n ) {\n return;\n }\n try {\n await this.handleInteractionCreate(interaction);\n } catch (error) {\n logger.error(`Error handling interaction: ${error}`);\n }\n });\n\n this.client.on('userStream', (entityId, name, userName, channel, opusDecoder) => {\n if (entityId !== this.client?.user?.id) {\n // Ensure voiceManager exists\n this.voiceManager?.handleUserStream(entityId, name, userName, channel, opusDecoder);\n }\n });\n }\n\n /**\n * Handles the event when a new member joins a guild.\n *\n * @param {GuildMember} member - The GuildMember object representing the new member that joined the guild.\n * @returns {Promise<void>} - A Promise that resolves once the event handling is complete.\n * @private\n */\n private async handleGuildMemberAdd(member: GuildMember) {\n logger.log(`New member joined: ${member.user.username}`);\n\n const guild = member.guild;\n\n const tag = member.user.bot\n ? `${member.user.username}#${member.user.discriminator}`\n : member.user.username;\n\n const worldId = createUniqueUuid(this.runtime, guild.id);\n const entityId = createUniqueUuid(this.runtime, member.id);\n\n // Emit standardized ENTITY_JOINED event\n this.runtime.emitEvent([EventType.ENTITY_JOINED], {\n runtime: this.runtime,\n entityId,\n worldId,\n source: 'discord',\n metadata: {\n originalId: member.id,\n username: tag,\n displayName: member.displayName || member.user.username,\n roles: member.roles.cache.map((r) => r.name),\n joinedAt: member.joinedAt?.getTime(),\n },\n });\n\n // Emit Discord-specific event\n this.runtime.emitEvent([DiscordEventTypes.ENTITY_JOINED], {\n runtime: this.runtime,\n entityId,\n worldId,\n member,\n guild,\n });\n }\n\n /**\n * Handles the event when the bot joins a guild. It logs the guild name, fetches additional information about the guild, scans the guild for voice data, creates standardized world data structure, generates unique IDs, and emits events to the runtime.\n * @param {Guild} guild - The guild that the bot has joined.\n * @returns {Promise<void>} A promise that resolves when the guild creation is handled.\n * @private\n */\n private async handleGuildCreate(guild: Guild) {\n logger.log(`Joined guild ${guild.name}`);\n const fullGuild = await guild.fetch();\n this.voiceManager?.scanGuild(guild);\n\n const ownerId = createUniqueUuid(this.runtime, fullGuild.ownerId);\n\n // Create standardized world data structure\n const worldId = createUniqueUuid(this.runtime, fullGuild.id);\n const standardizedData = {\n runtime: this.runtime,\n rooms: await this.buildStandardizedRooms(fullGuild, worldId),\n users: await this.buildStandardizedUsers(fullGuild),\n world: {\n id: worldId,\n name: fullGuild.name,\n agentId: this.runtime.agentId,\n serverId: fullGuild.id,\n metadata: {\n ownership: fullGuild.ownerId ? { ownerId: ownerId } : undefined,\n roles: {\n [ownerId]: Role.OWNER,\n },\n },\n } as World,\n source: 'discord',\n };\n\n // Emit both Discord-specific and standardized events with the same data structure\n this.runtime.emitEvent([DiscordEventTypes.WORLD_JOINED], {\n runtime: this.runtime,\n server: fullGuild,\n source: 'discord',\n });\n\n // Emit standardized event with the same structure as WORLD_CONNECTED\n this.runtime.emitEvent([EventType.WORLD_JOINED], standardizedData);\n }\n\n /**\n * Handles interactions created by the user, specifically commands and message components.\n * @param {Interaction} interaction - The interaction object received.\n * @returns {Promise<void>} A promise that resolves when the interaction is handled.\n * @private\n */\n private async handleInteractionCreate(interaction: Interaction) {\n if (interaction.isCommand()) {\n switch (interaction.commandName) {\n case 'joinchannel':\n // Ensure voiceManager exists\n await this.voiceManager?.handleJoinChannelCommand(interaction);\n break;\n case 'leavechannel':\n // Ensure voiceManager exists\n await this.voiceManager?.handleLeaveChannelCommand(interaction);\n break;\n }\n }\n\n // Handle message component interactions (buttons, dropdowns, etc.)\n if (interaction.isMessageComponent()) {\n logger.info(`Received component interaction: ${interaction.customId}`);\n const userId = interaction.user?.id;\n const messageId = interaction.message?.id;\n\n // Initialize user's selections if not exists\n if (!this.userSelections.has(userId)) {\n this.userSelections.set(userId, {});\n }\n const userSelections = this.userSelections.get(userId);\n if (!userSelections) {\n logger.error(`User selections map unexpectedly missing for user ${userId}`);\n return; // Should not happen\n }\n\n try {\n // For select menus (type 3), store the values\n if (interaction.isStringSelectMenu()) {\n logger.info(`Values selected: ${JSON.stringify(interaction.values)}`);\n logger.info(\n `User ${userId} selected values for ${interaction.customId}: ${JSON.stringify(interaction.values)}`\n );\n\n // Store values with messageId to scope them to this specific form\n userSelections[messageId] = {\n ...userSelections[messageId],\n [interaction.customId]: interaction.values,\n };\n // No need to call set again, modification is in place\n\n // Log the current state of all selections for this message\n logger.info(\n `Current selections for message ${messageId}: ${JSON.stringify(userSelections[messageId])}`\n );\n\n // Acknowledge the selection\n await interaction.deferUpdate();\n // await interaction.followUp({\n // content: 'Selection saved!',\n // ephemeral: true,\n // });\n }\n\n // For button interactions (type 2), use stored values\n if (interaction.isButton()) {\n logger.info('Button interaction detected');\n logger.info(`Button pressed by user ${userId}: ${interaction.customId}`);\n const formSelections = userSelections[messageId] || {};\n\n logger.info(`Form data being submitted: ${JSON.stringify(formSelections)}`);\n\n // Emit an event with the interaction data and stored selections\n this.runtime.emitEvent(['DISCORD_INTERACTION'], {\n interaction: {\n customId: interaction.customId,\n componentType: interaction.componentType,\n type: interaction.type,\n user: userId,\n messageId: messageId,\n selections: formSelections,\n },\n source: 'discord',\n });\n\n // Clear selections for this form only\n delete userSelections[messageId];\n // No need to call set again\n logger.info(`Cleared selections for message ${messageId}`);\n\n // Acknowledge the button press\n await interaction.deferUpdate();\n await interaction.followUp({\n content: 'Form submitted successfully!',\n ephemeral: true,\n });\n }\n } catch (error) {\n logger.error(`Error handling component interaction: ${error}`);\n try {\n await interaction.followUp({\n content: 'There was an error processing your interaction.',\n ephemeral: true,\n });\n } catch (followUpError) {\n logger.error(`Error sending follow-up message: ${followUpError}`);\n }\n }\n }\n }\n\n /**\n * Builds a standardized list of rooms from Discord guild channels.\n *\n * @param {Guild} guild The guild to build rooms for.\n * @param {UUID} _worldId The ID of the world to associate with the rooms (currently unused in favor of direct channel to room mapping).\n * @returns {Promise<any[]>} An array of standardized room objects.\n * @private\n */\n private async buildStandardizedRooms(guild: Guild, _worldId: UUID): Promise<any[]> {\n const rooms: any[] = [];\n\n for (const [channelId, channel] of guild.channels.cache) {\n // Only process text and voice channels\n if (\n channel.type === DiscordChannelType.GuildText ||\n channel.type === DiscordChannelType.GuildVoice\n ) {\n const roomId = createUniqueUuid(this.runtime, channelId);\n let channelType;\n\n switch (channel.type) {\n case DiscordChannelType.GuildText:\n channelType = ChannelType.GROUP;\n break;\n case DiscordChannelType.GuildVoice:\n channelType = ChannelType.VOICE_GROUP;\n break;\n default:\n channelType = ChannelType.GROUP;\n }\n\n // For text channels, we could potentially get member permissions\n // But for performance reasons, keep this light for large guilds\n let participants: UUID[] = [];\n\n if (guild.memberCount < 1000 && channel.type === DiscordChannelType.GuildText) {\n try {\n // Only attempt this for smaller guilds\n // Get members with read permissions for this channel\n participants = Array.from(guild.members.cache.values())\n .filter((member) =>\n channel.permissionsFor(member)?.has(PermissionsBitField.Flags.ViewChannel)\n )\n .map((member) => createUniqueUuid(this.runtime, member.id));\n } catch (error) {\n logger.warn(`Failed to get participants for channel ${channel.name}:`, error);\n }\n }\n\n rooms.push({\n id: roomId,\n name: channel.name,\n type: channelType,\n channelId: channel.id,\n participants,\n });\n }\n }\n\n return rooms;\n }\n\n /**\n * Builds a standardized list of users (entities) from Discord guild members.\n * Implements different strategies based on guild size for performance.\n *\n * @param {Guild} guild - The guild from which to build the user list.\n * @returns {Promise<Entity[]>} A promise that resolves with an array of standardized entity objects.\n * @private\n */\n private async buildStandardizedUsers(guild: Guild): Promise<Entity[]> {\n const entities: Entity[] = [];\n const botId = this.client?.user?.id;\n\n // Strategy based on guild size\n if (guild.memberCount > 1000) {\n logger.info(\n `Using optimized user sync for large guild ${guild.name} (${guild.memberCount} members)`\n );\n\n // For large guilds, prioritize members already in cache + online members\n try {\n // Use cache first\n for (const [, member] of guild.members.cache) {\n const tag = member.user.bot\n ? `${member.user.username}#${member.user.discriminator}`\n : member.user.username;\n\n if (member.id !== botId) {\n entities.push({\n id: createUniqueUuid(this.runtime, member.id),\n names: Array.from(\n new Set(\n [member.user.username, member.displayName, member.user.globalName].filter(\n Boolean\n ) as string[]\n )\n ),\n agentId: this.runtime.agentId,\n metadata: {\n default: {\n username: tag,\n name: member.displayName || member.user.username,\n },\n discord: member.user.globalName\n ? {\n username: tag,\n name: member.displayName || member.user.username,\n globalName: member.user.globalName,\n userId: member.id,\n }\n : {\n username: tag,\n name: member.displayName || member.user.username,\n userId: member.id,\n },\n },\n });\n }\n }\n\n // If cache has very few members, try to get online members\n if (entities.length < 100) {\n logger.info(`Adding online members for ${guild.name}`);\n // This is a more targeted fetch that is less likely to hit rate limits\n const onlineMembers = await guild.members.fetch({ limit: 100 });\n\n for (const [, member] of onlineMembers) {\n if (member.id !== botId) {\n const entityId = createUniqueUuid(this.runtime, member.id);\n // Avoid duplicates\n if (!entities.some((u) => u.id === entityId)) {\n const tag = member.user.bot\n ? `${member.user.username}#${member.user.discriminator}`\n : member.user.username;\n\n entities.push({\n id: entityId,\n names: Array.from(\n new Set(\n [member.user.username, member.displayName, member.user.globalName].filter(\n Boolean\n ) as string[]\n )\n ),\n agentId: this.runtime.agentId,\n metadata: {\n default: {\n username: tag,\n name: member.displayName || member.user.username,\n },\n discord: member.user.globalName\n ? {\n username: tag,\n name: member.displayName || member.user.username,\n globalName: member.user.globalName,\n userId: member.id,\n }\n : {\n username: tag,\n name: member.displayName || member.user.username,\n userId: member.id,\n },\n },\n });\n }\n }\n }\n }\n } catch (error) {\n logger.error(`Error fetching members for ${guild.name}:`, error);\n }\n } else {\n // For smaller guilds, we can fetch all members\n try {\n let members = guild.members.cache;\n if (members.size === 0) {\n members = await guild.members.fetch();\n }\n\n for (const [, member] of members) {\n if (member.id !== botId) {\n const tag = member.user.bot\n ? `${member.user.username}#${member.user.discriminator}`\n : member.user.username;\n\n entities.push({\n id: createUniqueUuid(this.runtime, member.id),\n names: Array.from(\n new Set(\n [member.user.username, member.displayName, member.user.globalName].filter(\n Boolean\n ) as string[]\n )\n ),\n agentId: this.runtime.agentId,\n metadata: {\n default: {\n username: tag,\n name: member.displayName || member.user.username,\n },\n discord: member.user.globalName\n ? {\n username: tag,\n name: member.displayName || member.user.username,\n globalName: member.user.globalName,\n userId: member.id,\n }\n : {\n username: tag,\n name: member.displayName || member.user.username,\n userId: member.id,\n },\n },\n });\n }\n }\n } catch (error) {\n logger.error(`Error fetching members for ${guild.name}:`, error);\n }\n }\n\n return entities;\n }\n\n /**\n * Handles tasks to be performed once the Discord client is fully ready and connected.\n * This includes fetching guilds, scanning for voice data, and emitting connection events.\n * @private\n * @returns {Promise<void>} A promise that resolves when all on-ready tasks are completed.\n */\n private async onReady() {\n logger.log('DISCORD ON READY');\n const guilds = await this.client?.guilds.fetch();\n if (!guilds) {\n logger.warn('Could not fetch guilds, client might not be ready.');\n return;\n }\n for (const [, guild] of guilds) {\n const fullGuild = await guild.fetch();\n await this.voiceManager?.scanGuild(fullGuild);\n\n // Send after a brief delay\n const timeoutId = setTimeout(async () => {\n // For each server the client is in, fire a connected event\n try {\n const fullGuild = await guild.fetch();\n logger.log('DISCORD SERVER CONNECTED', fullGuild.name);\n\n // Emit Discord-specific event with full guild object\n this.runtime.emitEvent([DiscordEventTypes.WORLD_CONNECTED], {\n runtime: this.runtime,\n server: fullGuild,\n source: 'discord',\n });\n\n // Create platform-agnostic world data structure with simplified structure\n const worldId = createUniqueUuid(this.runtime, fullGuild.id);\n const ownerId = createUniqueUuid(this.runtime, fullGuild.ownerId);\n\n const standardizedData = {\n name: fullGuild.name,\n runtime: this.runtime,\n rooms: await this.buildStandardizedRooms(fullGuild, worldId),\n entities: await this.buildStandardizedUsers(fullGuild),\n world: {\n id: worldId,\n name: fullGuild.name,\n agentId: this.runtime.agentId,\n serverId: fullGuild.id,\n metadata: {\n ownership: fullGuild.ownerId ? { ownerId } : undefined,\n roles: {\n [ownerId]: Role.OWNER,\n },\n },\n } as World,\n source: 'discord',\n };\n\n // Emit standardized event\n this.runtime.emitEvent([EventType.WORLD_CONNECTED], standardizedData);\n } catch (error) {\n // Add error handling to prevent crashes if the client is already destroyed\n logger.error('Error during Discord world connection:', error);\n }\n }, 1000);\n\n // Store the timeout reference to be able to cancel it when stopping\n this.timeouts.push(timeoutId);\n }\n\n this.client?.emit('voiceManagerReady');\n }\n\n /**\n * Registers send handlers for the Discord service instance.\n * This allows the runtime to correctly dispatch messages to this service.\n * @param {IAgentRuntime} runtime - The agent runtime instance.\n * @param {DiscordService} serviceInstance - The instance of the DiscordService.\n * @static\n */\n static registerSendHandlers(runtime: IAgentRuntime, serviceInstance: DiscordService) {\n if (serviceInstance) {\n runtime.registerSendHandler(\n 'discord',\n serviceInstance.handleSendMessage.bind(serviceInstance)\n );\n logger.info('[Discord] Registered send handler.');\n }\n }\n\n /**\n * Fetches all members who have access to a specific text channel.\n *\n * @param {string} channelId - The Discord ID of the text channel.\n * @param {boolean} [useCache=true] - Whether to prioritize cached data. Defaults to true.\n * @returns {Promise<Array<{id: string, username: string, displayName: string}>>} A promise that resolves with an array of channel member objects, each containing id, username, and displayName.\n */\n public async getTextChannelMembers(\n channelId: string,\n useCache: boolean = true\n ): Promise<Array<{ id: string; username: string; displayName: string }>> {\n logger.info(`Fetching members for text channel ${channelId}, useCache=${useCache}`);\n\n try {\n // Fetch the channel\n const channel = (await this.client?.channels.fetch(channelId)) as TextChannel;\n\n // Validate channel\n if (!channel) {\n logger.error(`Channel not found: ${channelId}`);\n return [];\n }\n\n if (channel.type !== DiscordChannelType.GuildText) {\n logger.error(`Channel ${channelId} is not a text channel`);\n return [];\n }\n\n const guild = channel.guild;\n if (!guild) {\n logger.error(`Channel ${channelId} is not in a guild`);\n return [];\n }\n\n // Determine strategy based on guild size and cache preference\n const useCacheOnly = useCache && guild.memberCount > 1000;\n let members: Collection<string, GuildMember>;\n\n if (useCacheOnly) {\n logger.info(\n `Using cached members for large guild ${guild.name} (${guild.memberCount} members)`\n );\n members = guild.members.cache;\n } else {\n // For smaller guilds or when cache is not preferred, fetch members\n try {\n if (useCache && guild.members.cache.size > 0) {\n logger.info(`Using cached members (${guild.members.cache.size} members)`);\n members = guild.members.cache;\n } else {\n logger.info(`Fetching members for guild ${guild.name}`);\n members = await guild.members.fetch();\n logger.info(`Fetched ${members.size} members`);\n }\n } catch (error) {\n logger.error(`Error fetching members: ${error}`);\n // Fallback to cache if fetch fails\n members = guild.members.cache;\n logger.info(`Fallback to cache with ${members.size} members`);\n }\n }\n\n // Filter members by permission to view the channel\n logger.info(`Filtering members for access to channel ${channel.name}`);\n // Explicitly type the array from values()\n const memberArray: GuildMember[] = Array.from(members.values());\n const channelMembers = memberArray\n .filter((member: GuildMember) => {\n // Skip bots except our own bot\n // Add null check for client and client.user\n if (member.user.bot && member.id !== this.client?.user?.id) {\n return false;\n }\n\n // Check if the member can view the channel\n return (\n channel.permissionsFor(member)?.has(PermissionsBitField.Flags.ViewChannel) ?? false\n );\n })\n .map((member: GuildMember) => ({\n id: member.id,\n username: member.user.username,\n displayName: member.displayName || member.user.username,\n }));\n\n logger.info(`Found ${channelMembers.length} members with access to channel ${channel.name}`);\n return channelMembers;\n } catch (error) {\n logger.error(`Error fetching channel members: ${error}`);\n return [];\n }\n }\n\n /**\n * Placeholder for handling reaction addition.\n * @private\n */\n private async handleReactionAdd(\n reaction: MessageReaction | PartialMessageReaction,\n user: User | PartialUser\n ) {\n try {\n logger.log('Reaction added');\n\n // Early returns\n if (!reaction || !user) {\n logger.warn('Invalid reaction or user');\n return;\n }\n\n // Get emoji info\n let emoji = reaction.emoji.name;\n if (!emoji && reaction.emoji.id) {\n emoji = `<:${reaction.emoji.name}:${reaction.emoji.id}>`;\n }\n\n // Fetch full message if partial\n if (reaction.partial) {\n try {\n await reaction.fetch();\n } catch (error) {\n logger.error('Failed to fetch partial reaction:', error);\n return;\n }\n }\n\n // Generate IDs with timestamp to ensure uniqueness\n const timestamp = Date.now();\n const roomId = createUniqueUuid(this.runtime, reaction.message.channel.id);\n const entityId = createUniqueUuid(this.runtime, user.id);\n const reactionUUID = createUniqueUuid(\n this.runtime,\n `${reaction.message.id}-${user.id}-${emoji}-${timestamp}`\n );\n\n // Validate IDs\n if (!entityId || !roomId) {\n logger.error('Invalid user ID or room ID', {\n entityId,\n roomId,\n });\n return;\n }\n\n // Process message content\n const messageContent = reaction.message.content || '';\n const truncatedContent =\n messageContent.length > 50 ? `${messageContent.substring(0, 50)}...` : messageContent;\n const reactionMessage = `*Added <${emoji}> to: \\\\\"${truncatedContent}\\\\\"*`; // Escaped quotes\n\n // Get user info\n const userName = reaction.message.author?.username || 'unknown';\n const name = reaction.message.author?.displayName || userName;\n\n await this.runtime.ensureConnection({\n entityId,\n roomId,\n userName,\n worldId: createUniqueUuid(this.runtime, reaction.message.guild?.id ?? roomId) as UUID,\n worldName: reaction.message.guild?.name,\n name: name,\n source: 'discord',\n channelId: reaction.message.channel.id,\n serverId: reaction.message.guild?.id,\n type: await this.getChannelType(reaction.message.channel as Channel),\n });\n\n const inReplyTo = createUniqueUuid(this.runtime, reaction.message.id);\n\n const memory: Memory = {\n id: reactionUUID,\n entityId,\n agentId: this.runtime.agentId,\n content: {\n // name,\n // userName,\n text: reactionMessage,\n source: 'discord',\n inReplyTo,\n channelType: await this.getChannelType(reaction.message.channel as Channel),\n },\n roomId,\n createdAt: timestamp,\n };\n\n const callback: HandlerCallback = async (content): Promise<Memory[]> => {\n if (!reaction.message.channel) {\n logger.error('No channel found for reaction message');\n return [];\n }\n await (reaction.message.channel as TextChannel).send(content.text ?? '');\n return [];\n };\n\n this.runtime.emitEvent(['DISCORD_REACTION_RECEIVED', 'REACTION_RECEIVED'], {\n runtime: this.runtime,\n message: memory,\n callback,\n });\n } catch (error) {\n logger.error('Error handling reaction:', error);\n }\n }\n\n /**\n * Placeholder for handling reaction removal.\n * @private\n */\n private async handleReactionRemove(\n reaction: MessageReaction | PartialMessageReaction,\n user: User | PartialUser\n ) {\n try {\n logger.log('Reaction removed');\n\n let emoji = reaction.emoji.name;\n if (!emoji && reaction.emoji.id) {\n emoji = `<:${reaction.emoji.name}:${reaction.emoji.id}>`;\n }\n\n // Fetch the full message if it's a partial\n if (reaction.partial) {\n try {\n await reaction.fetch();\n } catch (error) {\n logger.error('Something went wrong when fetching the message:', error);\n return;\n }\n }\n\n const messageContent = reaction.message.content || '';\n const truncatedContent =\n messageContent.length > 50 ? `${messageContent.substring(0, 50)}...` : messageContent;\n\n const reactionMessage = `*Removed <${emoji}> from: \\\\\"${truncatedContent}\\\\\"*`; // Escaped quotes\n\n const roomId = createUniqueUuid(this.runtime, reaction.message.channel.id);\n\n const entityId = createUniqueUuid(this.runtime, user.id);\n const timestamp = Date.now();\n const reactionUUID = createUniqueUuid(\n this.runtime,\n `${reaction.message.id}-${user.id}-${emoji}-${timestamp}`\n );\n\n const userName = reaction.message.author?.username || 'unknown';\n const name = reaction.message.author?.displayName || userName;\n\n await this.runtime.ensureConnection({\n entityId,\n roomId,\n userName,\n worldId: createUniqueUuid(this.runtime, reaction.message.guild?.id ?? roomId) as UUID,\n worldName: reaction.message.guild?.name,\n name: name,\n source: 'discord',\n channelId: reaction.message.channel.id,\n serverId: reaction.message.guild?.id,\n type: await this.getChannelType(reaction.message.channel as Channel),\n });\n\n const memory: Memory = {\n id: reactionUUID,\n entityId,\n agentId: this.runtime.agentId,\n content: {\n // name,\n // userName,\n text: reactionMessage,\n source: 'discord',\n inReplyTo: createUniqueUuid(this.runtime, reaction.message.id),\n channelType: await this.getChannelType(reaction.message.channel as Channel),\n },\n roomId,\n createdAt: Date.now(),\n };\n\n const callback: HandlerCallback = async (content): Promise<Memory[]> => {\n if (!reaction.message.channel) {\n logger.error('No channel found for reaction message');\n return [];\n }\n await (reaction.message.channel as TextChannel).send(content.text ?? '');\n return [];\n };\n\n this.runtime.emitEvent([DiscordEventTypes.REACTION_RECEIVED], {\n runtime: this.runtime,\n message: memory,\n callback,\n });\n } catch (error) {\n logger.error('Error handling reaction removal:', error);\n }\n }\n\n /**\n * Stops the Discord service and cleans up resources.\n * Implements the abstract method from the Service class.\n */\n public async stop(): Promise<void> {\n logger.info('Stopping Discord service...');\n this.timeouts.forEach(clearTimeout); // Clear any pending timeouts\n this.timeouts = [];\n if (this.client) {\n await this.client.destroy();\n this.client = null;\n logger.info('Discord client destroyed.');\n }\n // Additional cleanup if needed (e.g., voice manager)\n if (this.voiceManager) {\n // Assuming voiceManager has a stop or cleanup method\n // await this.voiceManager.stop();\n }\n logger.info('Discord service stopped.');\n }\n\n /**\n * Asynchronously retrieves the type of a given channel.\n *\n * @param {Channel} channel - The channel for which to determine the type.\n * @returns {Promise<ChannelType>} A Promise that resolves with the type of the channel.\n */\n async getChannelType(channel: Channel): Promise<ChannelType> {\n switch (channel.type) {\n case DiscordChannelType.DM:\n return ChannelType.DM;\n case DiscordChannelType.GuildText:\n return ChannelType.GROUP;\n case DiscordChannelType.GuildVoice:\n return ChannelType.VOICE_GROUP;\n default:\n // Fallback or handle other channel types as needed\n logger.warn(`Unhandled channel type: ${channel.type}`);\n return ChannelType.GROUP;\n }\n }\n}\n\nexport default DiscordService;\n","export const MESSAGE_CONSTANTS = {\n MAX_MESSAGES: 10,\n RECENT_MESSAGE_COUNT: 3,\n CHAT_HISTORY_COUNT: 5,\n INTEREST_DECAY_TIME: 5 * 60 * 1000, // 5 minutes\n PARTIAL_INTEREST_DECAY: 3 * 60 * 1000, // 3 minutes\n DEFAULT_SIMILARITY_THRESHOLD: 0.3,\n DEFAULT_SIMILARITY_THRESHOLD_FOLLOW_UPS: 0.2,\n} as const;\n\nexport const MESSAGE_LENGTH_THRESHOLDS = {\n LOSE_INTEREST: 100,\n SHORT_MESSAGE: 10,\n VERY_SHORT_MESSAGE: 2,\n IGNORE_RESPONSE: 4,\n} as const;\n\n/**\n * An array of words or phrases that indicate losing interest or annoyance.\n * @type {readonly [\"shut up\", \"stop\", \"please shut up\", \"shut up please\", \"dont talk\", \"silence\", \"stop talking\", \"be quiet\", \"hush\", \"wtf\", \"chill\", \"stfu\", \"stupid bot\", \"dumb bot\", \"stop responding\", \"god damn it\", \"god damn\", \"goddamnit\", \"can you not\", \"can you stop\", \"be quiet\", \"hate you\", \"hate this\", \"fuck up\"]}\n */\nexport const LOSE_INTEREST_WORDS = [\n 'shut up',\n 'stop',\n 'please shut up',\n 'shut up please',\n 'dont talk',\n 'silence',\n 'stop talking',\n 'be quiet',\n 'hush',\n 'wtf',\n 'chill',\n 'stfu',\n 'stupid bot',\n 'dumb bot',\n 'stop responding',\n 'god damn it',\n 'god damn',\n 'goddamnit',\n 'can you not',\n 'can you stop',\n 'be quiet',\n 'hate you',\n 'hate this',\n 'fuck up',\n] as const;\n\nexport const IGNORE_RESPONSE_WORDS = [\n 'lol',\n 'nm',\n 'uh',\n 'wtf',\n 'stfu',\n 'dumb',\n 'jfc',\n 'omg',\n] as const;\n\nexport const DISCORD_SERVICE_NAME = 'discord';\n","import {\n ChannelType,\n type Content,\n EventType,\n type HandlerCallback,\n type IAgentRuntime,\n type Media,\n type Memory,\n ServiceType,\n type UUID,\n createUniqueUuid,\n logger,\n} from '@elizaos/core';\nimport {\n type Channel,\n type Client,\n ChannelType as DiscordChannelType,\n type Message as DiscordMessage,\n type TextChannel,\n} from 'discord.js';\nimport { AttachmentManager } from './attachments';\nimport { DiscordEventTypes } from './types';\nimport { canSendMessage, sendMessageInChunks } from './utils';\n\n/**\n * Class representing a Message Manager for handling Discord messages.\n */\n\nexport class MessageManager {\n private client: Client;\n private runtime: IAgentRuntime;\n private attachmentManager: AttachmentManager;\n private getChannelType: (channel: Channel) => Promise<ChannelType>;\n /**\n * Constructor for a new instance of MyClass.\n * @param {any} discordClient - The Discord client object.\n */\n constructor(discordClient: any) {\n this.client = discordClient.client;\n this.runtime = discordClient.runtime;\n this.attachmentManager = new AttachmentManager(this.runtime);\n this.getChannelType = discordClient.getChannelType;\n }\n\n /**\n * Handles incoming Discord messages and processes them accordingly.\n *\n * @param {DiscordMessage} message - The Discord message to be handled\n */\n async handleMessage(message: DiscordMessage) {\n if (\n this.runtime.character.settings?.discord?.allowedChannelIds &&\n !this.runtime.character.settings.discord.allowedChannelIds.some(\n (id: string) => id === message.channel.id\n )\n ) {\n return;\n }\n\n if (message.interaction || message.author.id === this.client.user?.id) {\n return;\n }\n\n if (this.runtime.character.settings?.discord?.shouldIgnoreBotMessages && message.author?.bot) {\n return;\n }\n\n if (\n this.runtime.character.settings?.discord?.shouldIgnoreDirectMessages &&\n message.channel.type === DiscordChannelType.DM\n ) {\n return;\n }\n\n if (\n this.runtime.character.settings?.discord?.shouldRespondOnlyToMentions &&\n (!this.client.user?.id || !message.mentions.users?.has(this.client.user.id))\n ) {\n return;\n }\n\n const entityId = createUniqueUuid(this.runtime, message.author.id);\n\n const userName = message.author.bot\n ? `${message.author.username}#${message.author.discriminator}`\n : message.author.username;\n const name = message.author.displayName;\n const channelId = message.channel.id;\n const roomId = createUniqueUuid(this.runtime, channelId);\n\n // can't be null\n let type: ChannelType;\n let serverId: string | undefined;\n\n if (message.guild) {\n const guild = await message.guild.fetch();\n type = await this.getChannelType(message.channel as Channel);\n if (type === null) {\n // usually a forum type post\n logger.warn('null channel type, discord message', message);\n }\n serverId = guild.id;\n } else {\n type = ChannelType.DM;\n serverId = undefined;\n }\n\n await this.runtime.ensureConnection({\n entityId: entityId,\n roomId,\n userName,\n name: name,\n source: 'discord',\n channelId: message.channel.id,\n serverId,\n type,\n worldId: createUniqueUuid(this.runtime, serverId ?? roomId) as UUID,\n worldName: message.guild?.name,\n });\n\n try {\n const canSendResult = canSendMessage(message.channel);\n if (!canSendResult.canSend) {\n return logger.warn(`Cannot send message to channel ${message.channel}`, canSendResult);\n }\n\n const { processedContent, attachments } = await this.processMessage(message);\n\n const audioAttachments = message.attachments.filter((attachment) =>\n attachment.contentType?.startsWith('audio/')\n );\n\n if (audioAttachments.size > 0) {\n const processedAudioAttachments =\n await this.attachmentManager.processAttachments(audioAttachments);\n attachments.push(...processedAudioAttachments);\n }\n\n if (!processedContent && !attachments?.length) {\n // Only process messages that are not empty\n return;\n }\n\n const entityId = createUniqueUuid(this.runtime, message.author.id);\n\n const messageId = createUniqueUuid(this.runtime, message.id);\n\n // Start typing indicator immediately when processing the message\n const channel = message.channel as TextChannel;\n\n // Start the typing indicator\n const startTyping = () => {\n try {\n // sendTyping is not available at test time\n if (channel.sendTyping) {\n channel.sendTyping();\n }\n } catch (err) {\n logger.warn('Error sending typing indicator:', err);\n }\n };\n\n // Initial typing indicator\n startTyping();\n\n // Create interval to keep the typing indicator active\n const typingInterval = setInterval(startTyping, 8000);\n\n // Store the interval globally to be accessed by the callback\n const typingData = {\n interval: typingInterval,\n cleared: false,\n };\n\n const newMessage: Memory = {\n id: messageId,\n entityId: entityId,\n agentId: this.runtime.agentId,\n roomId: roomId,\n content: {\n // name: name,\n // userName: userName,\n text: processedContent || ' ',\n attachments: attachments,\n source: 'discord',\n url: message.url,\n inReplyTo: message.reference?.messageId\n ? createUniqueUuid(this.runtime, message.reference?.messageId)\n : undefined,\n },\n metadata: {\n entityName: name,\n type: 'message',\n },\n createdAt: message.createdTimestamp,\n };\n\n const callback: HandlerCallback = async (\n content: Content,\n files: Array<{ attachment: Buffer | string; name: string }>\n ) => {\n try {\n if (message.id && !content.inReplyTo) {\n content.inReplyTo = createUniqueUuid(this.runtime, message.id);\n }\n\n try {\n const messages = await sendMessageInChunks(\n channel,\n content.text ?? '',\n message.id!,\n files\n );\n\n const memories: Memory[] = [];\n for (const m of messages) {\n const actions = content.actions;\n\n const memory: Memory = {\n id: createUniqueUuid(this.runtime, m.id),\n entityId: this.runtime.agentId,\n agentId: this.runtime.agentId,\n content: {\n ...content,\n actions,\n inReplyTo: messageId,\n url: m.url,\n channelType: type,\n },\n roomId,\n createdAt: m.createdTimestamp,\n };\n memories.push(memory);\n }\n\n for (const m of memories) {\n await this.runtime.createMemory(m, 'messages');\n }\n\n // Clear typing indicator\n if (typingData.interval && !typingData.cleared) {\n clearInterval(typingData.interval);\n typingData.cleared = true;\n }\n\n return memories;\n } catch (error) {\n console.error('Error sending message:', error);\n if (typingData.interval && !typingData.cleared) {\n clearInterval(typingData.interval);\n typingData.cleared = true;\n }\n return [];\n }\n } catch (error) {\n console.error('Error handling message:', error);\n if (typingData.interval && !typingData.cleared) {\n clearInterval(typingData.interval);\n typingData.cleared = true;\n }\n return [];\n }\n };\n\n this.runtime.emitEvent([DiscordEventTypes.MESSAGE_RECEIVED, EventType.MESSAGE_RECEIVED], {\n runtime: this.runtime,\n message: newMessage,\n callback,\n });\n\n setTimeout(() => {\n if (typingData.interval && !typingData.cleared) {\n clearInterval(typingData.interval);\n typingData.cleared = true;\n }\n }, 500);\n } catch (error) {\n console.error('Error handling message:', error);\n }\n }\n\n /**\n * Processes the message content, mentions, code blocks, attachments, and URLs to generate\n * processed content and media attachments.\n *\n * @param {DiscordMessage} message The message to process\n * @returns {Promise<{ processedContent: string; attachments: Media[] }>} Processed content and media attachments\n */\n async processMessage(\n message: DiscordMessage\n ): Promise<{ processedContent: string; attachments: Media[] }> {\n let processedContent = message.content;\n let attachments: Media[] = [];\n\n const mentionRegex = /<@!?(\\d+)>/g;\n processedContent = processedContent.replace(mentionRegex, (match, entityId) => {\n const user = message.mentions.users.get(entityId);\n if (user) {\n return `${user.username} (@${entityId})`;\n }\n return match;\n });\n\n const codeBlockRegex = /```([\\s\\S]*?)```/g;\n let match;\n while ((match = codeBlockRegex.exec(processedContent))) {\n const codeBlock = match[1];\n const lines = codeBlock.split('\\n');\n const title = lines[0];\n const description = lines.slice(0, 3).join('\\n');\n const attachmentId = `code-${Date.now()}-${Math.floor(Math.random() * 1000)}`.slice(-5);\n attachments.push({\n id: attachmentId,\n url: '',\n title: title || 'Code Block',\n source: 'Code',\n description: description,\n text: codeBlock,\n });\n processedContent = processedContent.replace(match[0], `Code Block (${attachmentId})`);\n }\n\n if (message.attachments.size > 0) {\n attachments = await this.attachmentManager.processAttachments(message.attachments);\n }\n\n const urlRegex = /(https?:\\/\\/[^\\s]+)/g;\n const urls = processedContent.match(urlRegex) || [];\n\n for (const url of urls) {\n // Use string literal type for getService, assume methods exist at runtime\n const videoService = this.runtime.getService(ServiceType.VIDEO) as any; // Cast to any\n if (videoService?.isVideoUrl(url)) {\n const videoInfo = await videoService.processVideo(url, this.runtime);\n\n attachments.push({\n id: `youtube-${Date.now()}`,\n url: url,\n title: videoInfo.title,\n source: 'YouTube',\n description: videoInfo.description,\n text: videoInfo.text,\n });\n } else {\n // Use string literal type for getService, assume methods exist at runtime\n const browserService = this.runtime.getService(ServiceType.BROWSER) as any; // Cast to any\n if (!browserService) {\n logger.warn('Browser service not found');\n continue;\n }\n\n const { title, description: summary } = await browserService.getPageContent(\n url,\n this.runtime\n );\n\n attachments.push({\n id: `webpage-${Date.now()}`,\n url: url,\n title: title || 'Web Page',\n source: 'Web',\n description: summary,\n text: summary,\n });\n }\n }\n\n return { processedContent, attachments };\n }\n\n /**\n * Asynchronously fetches the bot's username and discriminator from Discord API.\n *\n * @param {string} botToken The token of the bot to authenticate the request\n * @returns {Promise<string>} A promise that resolves with the bot's username and discriminator\n * @throws {Error} If there is an error while fetching the bot details\n */\n\n async fetchBotName(botToken: string) {\n const url = 'https://discord.com/api/v10/users/@me';\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n Authorization: `Bot ${botToken}`,\n },\n });\n\n if (!response.ok) {\n throw new Error(`Error fetching bot details: ${response.statusText}`);\n }\n\n const data = await response.json();\n const discriminator = data.discriminator;\n return (data as { username: string }).username + (discriminator ? `#${discriminator}` : '');\n }\n}\n","import fs from 'node:fs';\nimport { trimTokens } from '@elizaos/core';\nimport { parseJSONObjectFromText } from '@elizaos/core';\nimport { type IAgentRuntime, type Media, ModelType, ServiceType } from '@elizaos/core';\nimport { type Attachment, Collection } from 'discord.js';\nimport ffmpeg from 'fluent-ffmpeg';\n\n/**\n * Generates a summary for the provided text using a specified model.\n *\n * @param {IAgentRuntime} runtime - The runtime environment for the agent.\n * @param {string} text - The text to generate a summary for.\n * @returns {Promise<{ title: string; description: string }>} An object containing the generated title and description.\n */\n\nasync function generateSummary(\n runtime: IAgentRuntime,\n text: string\n): Promise<{ title: string; description: string }> {\n // make sure text is under 128k characters\n text = await trimTokens(text, 100000, runtime);\n\n const prompt = `Please generate a concise summary for the following text:\n\n Text: \"\"\"\n ${text}\n \"\"\"\n\n Respond with a JSON object in the following format:\n \\`\\`\\`json\n {\n \"title\": \"Generated Title\",\n \"summary\": \"Generated summary and/or description of the text\"\n }\n \\`\\`\\``;\n\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response);\n\n if (parsedResponse?.title && parsedResponse?.summary) {\n return {\n title: parsedResponse.title,\n description: parsedResponse.summary,\n };\n }\n\n return {\n title: '',\n description: '',\n };\n}\n\n/**\n * Class representing an Attachment Manager.\n */\nexport class AttachmentManager {\n private attachmentCache: Map<string, Media> = new Map();\n private runtime: IAgentRuntime;\n\n /**\n * Constructor for creating a new instance of the class.\n *\n * @param {IAgentRuntime} runtime The runtime object to be injected into the instance.\n */\n constructor(runtime: IAgentRuntime) {\n this.runtime = runtime;\n }\n\n /**\n * Processes attachments and returns an array of Media objects.\n * @param {Collection<string, Attachment> | Attachment[]} attachments - The attachments to be processed\n * @returns {Promise<Media[]>} - An array of processed Media objects\n */\n async processAttachments(\n attachments: Collection<string, Attachment> | Attachment[]\n ): Promise<Media[]> {\n const processedAttachments: Media[] = [];\n const attachmentCollection =\n attachments instanceof Collection\n ? attachments\n : new Collection(attachments.map((att) => [att.id, att]));\n\n for (const [, attachment] of attachmentCollection) {\n const media = await this.processAttachment(attachment);\n if (media) {\n processedAttachments.push(media);\n }\n }\n\n return processedAttachments;\n }\n\n /**\n * Processes the provided attachment to generate a media object.\n * If the media for the attachment URL is already cached, it will return the cached media.\n * Otherwise, it will determine the type of attachment (PDF, text, audio, video, image, generic)\n * and call the corresponding processing method to generate the media object.\n *\n * @param attachment The attachment to process\n * @returns A promise that resolves to a Media object representing the attachment, or null if the attachment could not be processed\n */\n async processAttachment(attachment: Attachment): Promise<Media | null> {\n if (this.attachmentCache.has(attachment.url)) {\n return this.attachmentCache.get(attachment.url)!;\n }\n\n let media: Media | null = null;\n if (attachment.contentType?.startsWith('application/pdf')) {\n media = await this.processPdfAttachment(attachment);\n } else if (attachment.contentType?.startsWith('text/plain')) {\n media = await this.processPlaintextAttachment(attachment);\n } else if (\n attachment.contentType?.startsWith('audio/') ||\n attachment.contentType?.startsWith('video/mp4')\n ) {\n media = await this.processAudioVideoAttachment(attachment);\n } else if (attachment.contentType?.startsWith('image/')) {\n media = await this.processImageAttachment(attachment);\n } else if (\n attachment.contentType?.startsWith('video/') ||\n (this.runtime.getService(ServiceType.VIDEO) as any)?.isVideoUrl(attachment.url)\n ) {\n media = await this.processVideoAttachment(attachment);\n } else {\n media = await this.processGenericAttachment(attachment);\n }\n\n if (media) {\n this.attachmentCache.set(attachment.url, media);\n }\n return media;\n }\n\n /**\n * Asynchronously processes an audio or video attachment provided as input and returns a Media object.\n * @param {Attachment} attachment - The attachment object containing information about the audio/video file.\n * @returns {Promise<Media>} A Promise that resolves to a Media object representing the processed audio/video attachment.\n */\n private async processAudioVideoAttachment(attachment: Attachment): Promise<Media> {\n try {\n const response = await fetch(attachment.url);\n const audioVideoArrayBuffer = await response.arrayBuffer();\n\n let audioBuffer: Buffer;\n if (attachment.contentType?.startsWith('audio/')) {\n audioBuffer = Buffer.from(audioVideoArrayBuffer);\n } else if (attachment.contentType?.startsWith('video/mp4')) {\n audioBuffer = await this.extractAudioFromMP4(audioVideoArrayBuffer);\n } else {\n throw new Error('Unsupported audio/video format');\n }\n\n const transcription = await this.runtime.useModel(ModelType.TRANSCRIPTION, audioBuffer);\n const { title, description } = await generateSummary(this.runtime, transcription);\n\n return {\n id: attachment.id,\n url: attachment.url,\n title: title || 'Audio/Video Attachment',\n source: attachment.contentType?.startsWith('audio/') ? 'Audio' : 'Video',\n description:\n description || 'User-uploaded audio/video attachment which has been transcribed',\n text: transcription || 'Audio/video content not available',\n };\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error processing audio/video attachment: ${error.message}`);\n }\n return {\n id: attachment.id,\n url: attachment.url,\n title: 'Audio/Video Attachment',\n source: attachment.contentType?.startsWith('audio/') ? 'Audio' : 'Video',\n description: 'An audio/video attachment (transcription failed)',\n text: `This is an audio/video attachment. File name: ${attachment.name}, Size: ${attachment.size} bytes, Content type: ${attachment.contentType}`,\n };\n }\n }\n\n /**\n * Extracts the audio stream from the provided MP4 data and converts it to MP3 format.\n *\n * @param {ArrayBuffer} mp4Data - The MP4 data to extract audio from\n * @returns {Promise<Buffer>} - A Promise that resolves with the converted audio data as a Buffer\n */\n private async extractAudioFromMP4(mp4Data: ArrayBuffer): Promise<Buffer> {\n // Use a library like 'fluent-ffmpeg' or 'ffmpeg-static' to extract the audio stream from the MP4 data\n // and convert it to MP3 or WAV format\n // Example using fluent-ffmpeg:\n const tempMP4File = `temp_${Date.now()}.mp4`;\n const tempAudioFile = `temp_${Date.now()}.mp3`;\n\n try {\n // Write the MP4 data to a temporary file\n fs.writeFileSync(tempMP4File, Buffer.from(mp4Data));\n\n // Extract the audio stream and convert it to MP3\n await new Promise<void>((resolve, reject) => {\n ffmpeg(tempMP4File)\n .outputOptions('-vn') // Disable video output\n .audioCodec('libmp3lame') // Set audio codec to MP3\n .save(tempAudioFile) // Save the output to the specified file\n .on('end', () => {\n resolve();\n })\n .on('error', (err) => {\n reject(err);\n })\n .run();\n });\n\n // Read the converted audio file and return it as a Buffer\n const audioData = fs.readFileSync(tempAudioFile);\n return audioData;\n } finally {\n // Clean up the temporary files\n if (fs.existsSync(tempMP4File)) {\n fs.unlinkSync(tempMP4File);\n }\n if (fs.existsSync(tempAudioFile)) {\n fs.unlinkSync(tempAudioFile);\n }\n }\n }\n\n /**\n * Processes a PDF attachment by fetching the PDF file from the specified URL,\n * converting it to text, generating a summary, and returning a Media object\n * with the extracted information.\n * If an error occurs during processing, a placeholder Media object is returned\n * with an error message.\n *\n * @param {Attachment} attachment - The PDF attachment to process.\n * @returns {Promise<Media>} A promise that resolves to a Media object representing\n * the processed PDF attachment.\n */\n private async processPdfAttachment(attachment: Attachment): Promise<Media> {\n try {\n const response = await fetch(attachment.url);\n const pdfBuffer = await response.arrayBuffer();\n const pdfService = this.runtime.getService(ServiceType.PDF) as any;\n if (!pdfService) {\n throw new Error('PDF service not found');\n }\n const text = await pdfService.convertPdfToText(Buffer.from(pdfBuffer));\n const { title, description } = await generateSummary(this.runtime, text);\n\n return {\n id: attachment.id,\n url: attachment.url,\n title: title || 'PDF Attachment',\n source: 'PDF',\n description: description || 'A PDF document',\n text: text,\n };\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error processing PDF attachment: ${error.message}`);\n }\n return {\n id: attachment.id,\n url: attachment.url,\n title: 'PDF Attachment (conversion failed)',\n source: 'PDF',\n description: 'A PDF document that could not be converted to text',\n text: `This is a PDF attachment. File name: ${attachment.name}, Size: ${attachment.size} bytes`,\n };\n }\n }\n\n /**\n * Processes a plaintext attachment by fetching its content, generating a summary, and returning a Media object.\n * @param {Attachment} attachment - The attachment object to process.\n * @returns {Promise<Media>} A promise that resolves to a Media object representing the processed plaintext attachment.\n */\n private async processPlaintextAttachment(attachment: Attachment): Promise<Media> {\n try {\n const response = await fetch(attachment.url);\n const text = await response.text();\n const { title, description } = await generateSummary(this.runtime, text);\n\n return {\n id: attachment.id,\n url: attachment.url,\n title: title || 'Plaintext Attachment',\n source: 'Plaintext',\n description: description || 'A plaintext document',\n text: text,\n };\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error processing plaintext attachment: ${error.message}`);\n } else {\n console.error(`An unknown error occurred during plaintext attachment processing`);\n }\n return {\n id: attachment.id,\n url: attachment.url,\n title: 'Plaintext Attachment (retrieval failed)',\n source: 'Plaintext',\n description: 'A plaintext document that could not be retrieved',\n text: `This is a plaintext attachment. File name: ${attachment.name}, Size: ${attachment.size} bytes`,\n };\n }\n }\n\n /**\n * Process the image attachment by fetching description and title using the IMAGE_DESCRIPTION model.\n * If successful, returns a Media object populated with the details. If unsuccessful, creates a fallback\n * Media object and logs the error.\n *\n * @param {Attachment} attachment - The attachment object containing the image details.\n * @returns {Promise<Media>} A promise that resolves to a Media object.\n */\n private async processImageAttachment(attachment: Attachment): Promise<Media> {\n try {\n const { description, title } = await this.runtime.useModel(\n ModelType.IMAGE_DESCRIPTION,\n attachment.url\n );\n return {\n id: attachment.id,\n url: attachment.url,\n title: title || 'Image Attachment',\n source: 'Image',\n description: description || 'An image attachment',\n text: description || 'Image content not available',\n };\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error processing image attachment: ${error.message}`);\n }\n return this.createFallbackImageMedia(attachment);\n }\n }\n\n /**\n * Creates a fallback Media object for image attachments that could not be recognized.\n *\n * @param {Attachment} attachment - The attachment object containing image details.\n * @returns {Media} - The fallback Media object with basic information about the image attachment.\n */\n\n private createFallbackImageMedia(attachment: Attachment): Media {\n return {\n id: attachment.id,\n url: attachment.url,\n title: 'Image Attachment',\n source: 'Image',\n description: 'An image attachment (recognition failed)',\n text: `This is an image attachment. File name: ${attachment.name}, Size: ${attachment.size} bytes, Content type: ${attachment.contentType}`,\n };\n }\n\n /**\n * Process a video attachment to extract video information.\n * @param {Attachment} attachment - The attachment object containing video information.\n * @returns {Promise<Media>} A promise that resolves to a Media object with video details.\n * @throws {Error} If video service is not available.\n */\n private async processVideoAttachment(attachment: Attachment): Promise<Media> {\n const videoService = this.runtime.getService(ServiceType.VIDEO) as any;\n\n if (!videoService) {\n return {\n id: attachment.id,\n url: attachment.url,\n title: 'Video Attachment (Service Unavailable)',\n source: 'Video',\n description:\n 'Could not process video attachment because the required service is not available.',\n text: 'Video content not available',\n };\n }\n\n if (typeof videoService.isVideoUrl === 'function' && videoService.isVideoUrl(attachment.url)) {\n const videoInfo = await videoService.processVideo(attachment.url, this.runtime);\n return {\n id: attachment.id,\n url: attachment.url,\n title: videoInfo.title,\n source: 'YouTube',\n description: videoInfo.description,\n text: videoInfo.text,\n };\n }\n return {\n id: attachment.id,\n url: attachment.url,\n title: 'Video Attachment',\n source: 'Video',\n description: 'A video attachment',\n text: 'Video content not available',\n };\n }\n\n /**\n * Process a generic attachment and return a Media object with specified properties.\n * @param {Attachment} attachment - The attachment object to process.\n * @returns {Promise<Media>} A Promise that resolves to a Media object with specified properties.\n */\n private async processGenericAttachment(attachment: Attachment): Promise<Media> {\n return {\n id: attachment.id,\n url: attachment.url,\n title: 'Generic Attachment',\n source: 'Generic',\n description: 'A generic attachment',\n text: 'Attachment content not available',\n };\n }\n}\n","import {\n type IAgentRuntime,\n ModelType,\n logger,\n parseJSONObjectFromText,\n trimTokens,\n} from '@elizaos/core';\nimport {\n ChannelType,\n type Message as DiscordMessage,\n PermissionsBitField,\n type TextChannel,\n ThreadChannel,\n} from 'discord.js';\n\nconst MAX_MESSAGE_LENGTH = 1900;\n\n/**\n * Generates a summary for a given text using a specified model.\n *\n * @param {IAgentRuntime} runtime - The IAgentRuntime instance.\n * @param {string} text - The text for which to generate a summary.\n * @returns {Promise<{ title: string; description: string }>} An object containing the generated title and summary.\n */\nexport async function generateSummary(\n runtime: IAgentRuntime,\n text: string\n): Promise<{ title: string; description: string }> {\n // make sure text is under 128k characters\n text = await trimTokens(text, 100000, runtime);\n\n const prompt = `Please generate a concise summary for the following text:\n\n Text: \"\"\"\n ${text}\n \"\"\"\n\n Respond with a JSON object in the following format:\n \\`\\`\\`json\n {\n \"title\": \"Generated Title\",\n \"summary\": \"Generated summary and/or description of the text\"\n }\n \\`\\`\\``;\n\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response);\n\n if (parsedResponse?.title && parsedResponse?.summary) {\n return {\n title: parsedResponse.title,\n description: parsedResponse.summary,\n };\n }\n\n return {\n title: '',\n description: '',\n };\n}\n\ninterface DiscordComponentOptions {\n type: number;\n custom_id: string;\n label?: string;\n style?: number;\n placeholder?: string;\n min_values?: number;\n max_values?: number;\n options?: Array<{\n label: string;\n value: string;\n description?: string;\n }>;\n}\n\ninterface DiscordActionRow {\n type: 1;\n components: DiscordComponentOptions[];\n}\n\n/**\n * Sends a message in chunks to a specified Discord TextChannel.\n * @param {TextChannel} channel - The Discord TextChannel to send the message to.\n * @param {string} content - The content of the message to be sent.\n * @param {string} _inReplyTo - The message ID to reply to (if applicable).\n * @param {any[]} files - Array of files to attach to the message.\n * @param {any[]} components - Optional components to add to the message (buttons, dropdowns, etc.).\n * @returns {Promise<DiscordMessage[]>} - Array of sent Discord messages.\n */\nexport async function sendMessageInChunks(\n channel: TextChannel,\n content: string,\n _inReplyTo: string,\n files: Array<{ attachment: Buffer | string; name: string }>,\n components?: any[]\n): Promise<DiscordMessage[]> {\n const sentMessages: DiscordMessage[] = [];\n const messages = splitMessage(content);\n try {\n for (let i = 0; i < messages.length; i++) {\n const message = messages[i];\n if (\n message.trim().length > 0 ||\n (i === messages.length - 1 && files && files.length > 0) ||\n components\n ) {\n const options: any = {\n content: message.trim(),\n };\n\n // if (i === 0 && inReplyTo) {\n // // Reply to the specified message for the first chunk\n // options.reply = {\n // messageReference: inReplyTo,\n // };\n // }\n\n if (i === messages.length - 1 && files && files.length > 0) {\n // Attach files to the last message chunk\n options.files = files;\n }\n\n // Add components to the last message or to a message with components only\n if (i === messages.length - 1 && components && components.length > 0) {\n try {\n // Safe JSON stringify that handles BigInt\n const safeStringify = (obj: any) => {\n return JSON.stringify(obj, (_, value) =>\n typeof value === 'bigint' ? value.toString() : value\n );\n };\n\n logger.info(`Components received: ${safeStringify(components)}`);\n\n if (!Array.isArray(components)) {\n logger.warn('Components is not an array, skipping component processing');\n // Instead of continue, maybe return or handle differently?\n // For now, let's proceed assuming it might be an empty message with components\n } else if (\n components.length > 0 &&\n components[0] &&\n typeof components[0].toJSON === 'function'\n ) {\n // If it looks like discord.js components, pass them directly\n options.components = components;\n } else {\n // Otherwise, build components from the assumed DiscordActionRow[] structure\n const {\n ActionRowBuilder,\n ButtonBuilder,\n StringSelectMenuBuilder,\n } = require('discord.js');\n\n const discordComponents = (components as DiscordActionRow[]) // Cast here for building logic\n .map((row: DiscordActionRow) => {\n if (!row || typeof row !== 'object' || row.type !== 1) {\n logger.warn('Invalid component row structure, skipping');\n return null;\n }\n\n if (row.type === 1) {\n const actionRow = new ActionRowBuilder();\n\n if (!Array.isArray(row.components)) {\n logger.warn('Row components is not an array, skipping');\n return null;\n }\n\n const validComponents = row.components\n .map((comp: DiscordComponentOptions) => {\n if (!comp || typeof comp !== 'object') {\n logger.warn('Invalid component, skipping');\n return null;\n }\n\n try {\n if (comp.type === 2) {\n return new ButtonBuilder()\n .setCustomId(comp.custom_id)\n .setLabel(comp.label || '')\n .setStyle(comp.style || 1);\n }\n\n if (comp.type === 3) {\n const selectMenu = new StringSelectMenuBuilder()\n .setCustomId(comp.custom_id)\n .setPlaceholder(comp.placeholder || 'Select an option');\n\n if (typeof comp.min_values === 'number')\n selectMenu.setMinValues(comp.min_values);\n if (typeof comp.max_values === 'number')\n selectMenu.setMaxValues(comp.max_values);\n\n if (Array.isArray(comp.options)) {\n selectMenu.addOptions(\n comp.options.map((option) => ({\n label: option.label,\n value: option.value,\n description: option.description,\n }))\n );\n }\n\n return selectMenu;\n }\n } catch (err) {\n logger.error(`Error creating component: ${err}`);\n return null;\n }\n return null;\n })\n .filter(Boolean);\n\n if (validComponents.length > 0) {\n actionRow.addComponents(validComponents);\n return actionRow;\n }\n }\n return null;\n })\n .filter(Boolean);\n\n if (discordComponents.length > 0) {\n options.components = discordComponents;\n }\n }\n } catch (error) {\n logger.error(`Error processing components: ${error}`);\n }\n }\n\n const m = await channel.send(options);\n sentMessages.push(m);\n }\n }\n } catch (error) {\n logger.error(`Error sending message: ${error}`);\n }\n\n return sentMessages;\n}\n\n/**\n * Splits the content into an array of strings based on the maximum message length.\n * @param {string} content - The content to split into messages\n * @returns {string[]} An array of strings that represent the split messages\n */\nfunction splitMessage(content: string): string[] {\n const messages: string[] = [];\n let currentMessage = '';\n\n const rawLines = content?.split('\\n') || [];\n // split all lines into MAX_MESSAGE_LENGTH chunks so any long lines are split\n const lines = rawLines.flatMap((line) => {\n // Explicitly type chunks as string[]\n const chunks: string[] = [];\n while (line.length > MAX_MESSAGE_LENGTH) {\n chunks.push(line.slice(0, MAX_MESSAGE_LENGTH));\n line = line.slice(MAX_MESSAGE_LENGTH);\n }\n chunks.push(line);\n return chunks;\n });\n\n for (const line of lines) {\n if (currentMessage.length + line.length + 1 > MAX_MESSAGE_LENGTH) {\n messages.push(currentMessage.trim());\n currentMessage = '';\n }\n currentMessage += `${line}\\n`;\n }\n\n if (currentMessage.trim().length > 0) {\n messages.push(currentMessage.trim());\n }\n\n return messages;\n}\n\n/**\n * Checks if the bot can send messages in a given channel by checking permissions.\n * @param {TextChannel | NewsChannel | ThreadChannel} channel - The channel to check permissions for.\n * @returns {Object} Object containing information about whether the bot can send messages or not.\n * @returns {boolean} canSend - Whether the bot can send messages in the channel.\n * @returns {string} reason - The reason why the bot cannot send messages, if applicable.\n * @returns {string[]} missingPermissions - Array of missing permissions, if any.\n */\nexport function canSendMessage(channel) {\n // validate input\n if (!channel) {\n return {\n canSend: false,\n reason: 'No channel given',\n };\n }\n // if it is a DM channel, we can always send messages\n if (channel.type === ChannelType.DM) {\n return {\n canSend: true,\n reason: null,\n };\n }\n const botMember = channel.guild?.members.cache.get(channel.client.user.id);\n\n if (!botMember) {\n return {\n canSend: false,\n reason: 'Not a guild channel or bot member not found',\n };\n }\n\n // Required permissions for sending messages\n const requiredPermissions = [\n PermissionsBitField.Flags.ViewChannel,\n PermissionsBitField.Flags.SendMessages,\n PermissionsBitField.Flags.ReadMessageHistory,\n ];\n\n // Add thread-specific permission if it's a thread\n if (channel instanceof ThreadChannel) {\n requiredPermissions.push(PermissionsBitField.Flags.SendMessagesInThreads);\n }\n\n // Check permissions\n const permissions = channel.permissionsFor(botMember);\n\n if (!permissions) {\n return {\n canSend: false,\n reason: 'Could not retrieve permissions',\n };\n }\n\n // Check each required permission\n const missingPermissions = requiredPermissions.filter((perm) => !permissions.has(perm));\n\n return {\n canSend: missingPermissions.length === 0,\n missingPermissions: missingPermissions,\n reason:\n missingPermissions.length > 0\n ? `Missing permissions: ${missingPermissions.map((p) => String(p)).join(', ')}`\n : null,\n };\n}\n","import { EventEmitter } from 'node:events';\nimport { type Readable, pipeline } from 'node:stream';\nimport {\n type AudioPlayer,\n type AudioReceiveStream,\n NoSubscriberBehavior,\n StreamType,\n type VoiceConnection,\n VoiceConnectionStatus,\n createAudioPlayer,\n createAudioResource,\n entersState,\n getVoiceConnections,\n joinVoiceChannel,\n} from '@discordjs/voice';\nimport {\n ChannelType,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n ModelType,\n type UUID,\n createUniqueUuid,\n getWavHeader,\n logger,\n} from '@elizaos/core';\nimport {\n type BaseGuildVoiceChannel,\n type Channel,\n type Client,\n ChannelType as DiscordChannelType,\n type Guild,\n type GuildMember,\n type VoiceChannel,\n type VoiceState,\n} from 'discord.js';\nimport prism from 'prism-media';\nimport type { DiscordService } from './service';\n\n// These values are chosen for compatibility with picovoice components\nconst DECODE_FRAME_SIZE = 1024;\nconst DECODE_SAMPLE_RATE = 16000;\n\n/**\n * Class representing an AudioMonitor that listens for audio data from a Readable stream.\n */\nexport class AudioMonitor {\n private readable: Readable;\n private buffers: Buffer[] = [];\n private maxSize: number;\n private lastFlagged = -1;\n private ended = false;\n\n /**\n * Constructs an AudioMonitor instance.\n * @param {Readable} readable - The readable stream to monitor for audio data.\n * @param {number} maxSize - The maximum size of the audio buffer.\n * @param {function} onStart - The callback function to be called when audio starts.\n * @param {function} callback - The callback function to process audio data.\n */\n constructor(\n readable: Readable,\n maxSize: number,\n onStart: () => void,\n callback: (buffer: Buffer) => void\n ) {\n this.readable = readable;\n this.maxSize = maxSize;\n this.readable.on('data', (chunk: Buffer) => {\n if (this.lastFlagged < 0) {\n this.lastFlagged = this.buffers.length;\n }\n this.buffers.push(chunk);\n const currentSize = this.buffers.reduce((acc, cur) => acc + cur.length, 0);\n while (currentSize > this.maxSize) {\n this.buffers.shift();\n this.lastFlagged--;\n }\n });\n this.readable.on('end', () => {\n logger.log('AudioMonitor ended');\n this.ended = true;\n if (this.lastFlagged < 0) return;\n callback(this.getBufferFromStart());\n this.lastFlagged = -1;\n });\n this.readable.on('speakingStopped', () => {\n if (this.ended) return;\n logger.log('Speaking stopped');\n if (this.lastFlagged < 0) return;\n callback(this.getBufferFromStart());\n });\n this.readable.on('speakingStarted', () => {\n if (this.ended) return;\n onStart();\n logger.log('Speaking started');\n this.reset();\n });\n }\n\n /**\n * Stops listening to \"data\", \"end\", \"speakingStopped\", and \"speakingStarted\" events on the readable stream.\n */\n stop() {\n this.readable.removeAllListeners('data');\n this.readable.removeAllListeners('end');\n this.readable.removeAllListeners('speakingStopped');\n this.readable.removeAllListeners('speakingStarted');\n }\n\n /**\n * Check if the item is flagged.\n * @returns {boolean} True if the item was flagged, false otherwise.\n */\n isFlagged() {\n return this.lastFlagged >= 0;\n }\n\n /**\n * Returns a Buffer containing all buffers starting from the last flagged index.\n * If the last flagged index is less than 0, returns null.\n *\n * @returns {Buffer | null} The concatenated Buffer or null\n */\n getBufferFromFlag() {\n if (this.lastFlagged < 0) {\n return null;\n }\n const buffer = Buffer.concat(this.buffers.slice(this.lastFlagged));\n return buffer;\n }\n\n /**\n * Concatenates all buffers in the array and returns a single buffer.\n *\n * @returns {Buffer} The concatenated buffer from the start.\n */\n getBufferFromStart() {\n const buffer = Buffer.concat(this.buffers);\n return buffer;\n }\n\n /**\n * Resets the buffers array and sets lastFlagged to -1.\n */\n reset() {\n this.buffers = [];\n this.lastFlagged = -1;\n }\n\n /**\n * Check if the object has ended.\n * @returns {boolean} Returns true if the object has ended; false otherwise.\n */\n isEnded() {\n return this.ended;\n }\n}\n\n/**\n * Class representing a VoiceManager that extends EventEmitter.\n * @extends EventEmitter\n */\nexport class VoiceManager extends EventEmitter {\n private processingVoice = false;\n private transcriptionTimeout: NodeJS.Timeout | null = null;\n private userStates: Map<\n string,\n {\n buffers: Buffer[];\n totalLength: number;\n lastActive: number;\n transcriptionText: string;\n }\n > = new Map();\n private activeAudioPlayer: AudioPlayer | null = null;\n private client: Client | null;\n private runtime: IAgentRuntime;\n private streams: Map<string, Readable> = new Map();\n private connections: Map<string, VoiceConnection> = new Map();\n private activeMonitors: Map<string, { channel: BaseGuildVoiceChannel; monitor: AudioMonitor }> =\n new Map();\n private ready: boolean;\n\n /**\n * Constructor for initializing a new instance of the class.\n *\n * @param {DiscordService} service - The Discord service to use.\n * @param {IAgentRuntime} runtime - The runtime for the agent.\n */\n constructor(service: DiscordService, runtime: IAgentRuntime) {\n super();\n this.client = service.client;\n this.runtime = runtime;\n this.ready = false;\n\n if (this.client) {\n this.client.on('voiceManagerReady', () => {\n this.setReady(true);\n });\n } else {\n logger.error(\n 'Discord client is not available in VoiceManager constructor for voiceManagerReady event'\n );\n this.ready = false;\n }\n }\n\n /**\n * Asynchronously retrieves the type of the channel.\n * @param {Channel} channel - The channel to get the type for.\n * @returns {Promise<ChannelType>} The type of the channel.\n */\n async getChannelType(channel: Channel): Promise<ChannelType> {\n switch (channel.type) {\n case DiscordChannelType.GuildVoice:\n case DiscordChannelType.GuildStageVoice:\n return ChannelType.VOICE_GROUP;\n default:\n // This function should only be called with GuildVoice or GuildStageVoice channels\n // If it receives another type, it's an unexpected error.\n logger.error(\n `getChannelType received unexpected channel type: ${channel.type} for channel ${channel.id}`\n );\n throw new Error(`Unexpected channel type encountered: ${channel.type}`);\n }\n }\n\n /**\n * Set the ready status of the VoiceManager.\n * @param {boolean} status - The status to set.\n */\n private setReady(status: boolean) {\n this.ready = status;\n this.emit('ready');\n logger.debug(`VoiceManager is now ready: ${this.ready}`);\n }\n\n /**\n * Check if the object is ready.\n *\n * @returns {boolean} True if the object is ready, false otherwise.\n */\n isReady() {\n return this.ready;\n }\n\n /**\n * Handle voice state update event.\n * @param {VoiceState} oldState - The old voice state of the member.\n * @param {VoiceState} newState - The new voice state of the member.\n * @returns {void}\n */\n async handleVoiceStateUpdate(oldState: VoiceState, newState: VoiceState) {\n const oldChannelId = oldState.channelId;\n const newChannelId = newState.channelId;\n const member = newState.member;\n if (!member) return;\n if (member.id === this.client?.user?.id) {\n return;\n }\n\n // Ignore mute/unmute events\n if (oldChannelId === newChannelId) {\n return;\n }\n\n // User leaving a channel where the bot is present\n if (oldChannelId && this.connections.has(oldChannelId)) {\n this.stopMonitoringMember(member.id);\n }\n\n // User joining a channel where the bot is present\n if (newChannelId && this.connections.has(newChannelId)) {\n await this.monitorMember(member, newState.channel as BaseGuildVoiceChannel);\n }\n }\n\n /**\n * Joins a voice channel and sets up the necessary connection and event listeners.\n * @param {BaseGuildVoiceChannel} channel - The voice channel to join\n */\n async joinChannel(channel: BaseGuildVoiceChannel) {\n const oldConnection = this.getVoiceConnection(channel.guildId as string);\n if (oldConnection) {\n try {\n oldConnection.destroy();\n // Remove all associated streams and monitors\n this.streams.clear();\n this.activeMonitors.clear();\n } catch (error) {\n console.error('Error leaving voice channel:', error);\n }\n }\n\n const connection = joinVoiceChannel({\n channelId: channel.id,\n guildId: channel.guild.id,\n adapterCreator: channel.guild.voiceAdapterCreator as any,\n selfDeaf: false,\n selfMute: false,\n group: this.client?.user?.id ?? 'default-group',\n });\n\n try {\n // Wait for either Ready or Signalling state\n await Promise.race([\n entersState(connection, VoiceConnectionStatus.Ready, 20_000),\n entersState(connection, VoiceConnectionStatus.Signalling, 20_000),\n ]);\n\n // Log connection success\n logger.log(`Voice connection established in state: ${connection.state.status}`);\n\n // Set up ongoing state change monitoring\n connection.on('stateChange', async (oldState, newState) => {\n logger.log(`Voice connection state changed from ${oldState.status} to ${newState.status}`);\n\n if (newState.status === VoiceConnectionStatus.Disconnected) {\n logger.log('Handling disconnection...');\n\n try {\n // Try to reconnect if disconnected\n await Promise.race([\n entersState(connection, VoiceConnectionStatus.Signalling, 5_000),\n entersState(connection, VoiceConnectionStatus.Connecting, 5_000),\n ]);\n // Seems to be reconnecting to a new channel\n logger.log('Reconnecting to channel...');\n } catch (e) {\n // Seems to be a real disconnect, destroy and cleanup\n logger.log(`Disconnection confirmed - cleaning up...${e}`);\n connection.destroy();\n this.connections.delete(channel.id);\n }\n } else if (newState.status === VoiceConnectionStatus.Destroyed) {\n this.connections.delete(channel.id);\n } else if (\n !this.connections.has(channel.id) &&\n (newState.status === VoiceConnectionStatus.Ready ||\n newState.status === VoiceConnectionStatus.Signalling)\n ) {\n this.connections.set(channel.id, connection);\n }\n });\n\n connection.on('error', (error) => {\n logger.log('Voice connection error:', error);\n // Don't immediately destroy - let the state change handler deal with it\n logger.log('Connection error - will attempt to recover...');\n });\n\n // Store the connection\n this.connections.set(channel.id, connection);\n\n // Continue with voice state modifications\n const me = channel.guild.members.me;\n if (me?.voice && me.permissions.has('DeafenMembers')) {\n try {\n await me.voice.setDeaf(false);\n await me.voice.setMute(false);\n } catch (error) {\n logger.log('Failed to modify voice state:', error);\n // Continue even if this fails\n }\n }\n\n connection.receiver.speaking.on('start', async (entityId: string) => {\n let user = channel.members.get(entityId);\n if (!user) {\n try {\n user = await channel.guild.members.fetch(entityId);\n } catch (error) {\n console.error('Failed to fetch user:', error);\n }\n }\n\n if (user && !user?.user.bot) {\n this.monitorMember(user as GuildMember, channel);\n this.streams.get(entityId)?.emit('speakingStarted');\n }\n });\n\n connection.receiver.speaking.on('end', async (entityId: string) => {\n const user = channel.members.get(entityId);\n if (!user?.user.bot) {\n this.streams.get(entityId)?.emit('speakingStopped');\n }\n });\n } catch (error) {\n logger.log('Failed to establish voice connection:', error);\n connection.destroy();\n this.connections.delete(channel.id);\n throw error;\n }\n }\n\n /**\n * Retrieves the voice connection for a given guild ID.\n * @param {string} guildId - The ID of the guild to get the voice connection for.\n * @returns {VoiceConnection | undefined} The voice connection for the specified guild ID, or undefined if not found.\n */\n getVoiceConnection(guildId: string) {\n const userId = this.client?.user?.id;\n if (!userId) {\n logger.error('Client user ID is not available.');\n return undefined;\n }\n const connections = getVoiceConnections(userId);\n if (!connections) {\n return;\n }\n const connection = [...connections.values()].find(\n (connection) => connection.joinConfig.guildId === guildId\n );\n return connection;\n }\n\n /**\n * Monitor a member's audio stream for volume activity and speaking thresholds.\n *\n * @param {GuildMember} member - The member whose audio stream is being monitored.\n * @param {BaseGuildVoiceChannel} channel - The voice channel in which the member is connected.\n */\n private async monitorMember(member: GuildMember, channel: BaseGuildVoiceChannel) {\n const entityId = member?.id;\n const userName = member?.user?.username;\n const name = member?.user?.displayName;\n const connection = this.getVoiceConnection(member?.guild?.id);\n\n const receiveStream = connection?.receiver.subscribe(entityId, {\n autoDestroy: true,\n emitClose: true,\n });\n if (!receiveStream || receiveStream.readableLength === 0) {\n logger.warn(`[monitorMember] No receiveStream or empty stream for user ${entityId}`);\n return;\n }\n\n const opusDecoder = new prism.opus.Decoder({\n channels: 1,\n rate: DECODE_SAMPLE_RATE,\n frameSize: DECODE_FRAME_SIZE,\n });\n\n const volumeBuffer: number[] = [];\n const VOLUME_WINDOW_SIZE = 30;\n const SPEAKING_THRESHOLD = 0.05;\n opusDecoder.on('data', (pcmData: Buffer) => {\n // Monitor the audio volume while the agent is speaking.\n // If the average volume of the user's audio exceeds the defined threshold, it indicates active speaking.\n // When active speaking is detected, stop the agent's current audio playback to avoid overlap.\n\n if (this.activeAudioPlayer) {\n const samples = new Int16Array(pcmData.buffer, pcmData.byteOffset, pcmData.length / 2);\n const maxAmplitude = Math.max(...samples.map(Math.abs)) / 32768;\n volumeBuffer.push(maxAmplitude);\n\n if (volumeBuffer.length > VOLUME_WINDOW_SIZE) {\n volumeBuffer.shift();\n }\n const avgVolume = volumeBuffer.reduce((sum, v) => sum + v, 0) / VOLUME_WINDOW_SIZE;\n\n if (avgVolume > SPEAKING_THRESHOLD) {\n volumeBuffer.length = 0;\n this.cleanupAudioPlayer(this.activeAudioPlayer);\n this.processingVoice = false;\n }\n }\n });\n pipeline(receiveStream as AudioReceiveStream, opusDecoder as any, (err: Error | null) => {\n if (err) {\n logger.debug(`[monitorMember] Opus decoding pipeline error for user ${entityId}: ${err}`);\n } else {\n logger.debug(\n `[monitorMember] Opus decoding pipeline finished successfully for user ${entityId}`\n );\n }\n });\n this.streams.set(entityId, opusDecoder);\n this.connections.set(entityId, connection as VoiceConnection);\n opusDecoder.on('error', (err: any) => {\n logger.debug(`Opus decoding error: ${err}`);\n });\n const errorHandler = (err: any) => {\n logger.debug(`Opus decoding error: ${err}`);\n };\n const streamCloseHandler = () => {\n logger.debug(`voice stream from ${member?.displayName} closed`);\n this.streams.delete(entityId);\n this.connections.delete(entityId);\n };\n const closeHandler = () => {\n logger.debug(`Opus decoder for ${member?.displayName} closed`);\n opusDecoder.removeListener('error', errorHandler);\n opusDecoder.removeListener('close', closeHandler);\n receiveStream?.removeListener('close', streamCloseHandler);\n };\n opusDecoder.on('error', errorHandler);\n opusDecoder.on('close', closeHandler);\n receiveStream?.on('close', streamCloseHandler);\n\n this.client?.emit('userStream', entityId, name, userName, channel, opusDecoder);\n }\n\n /**\n * Leaves the specified voice channel and stops monitoring all members in that channel.\n * If there is an active connection in the channel, it will be destroyed.\n *\n * @param {BaseGuildVoiceChannel} channel - The voice channel to leave.\n */\n leaveChannel(channel: BaseGuildVoiceChannel) {\n const connection = this.connections.get(channel.id);\n if (connection) {\n connection.destroy();\n this.connections.delete(channel.id);\n }\n\n // Stop monitoring all members in this channel\n for (const [memberId, monitorInfo] of this.activeMonitors) {\n if (monitorInfo.channel.id === channel.id && memberId !== this.client?.user?.id) {\n this.stopMonitoringMember(memberId);\n }\n }\n\n logger.debug(`Left voice channel: ${channel.name} (${channel.id})`);\n }\n\n /**\n * Stop monitoring a specific member by their member ID.\n * @param {string} memberId - The ID of the member to stop monitoring.\n */\n stopMonitoringMember(memberId: string) {\n const monitorInfo = this.activeMonitors.get(memberId);\n if (monitorInfo) {\n monitorInfo.monitor.stop();\n this.activeMonitors.delete(memberId);\n this.streams.delete(memberId);\n logger.debug(`Stopped monitoring user ${memberId}`);\n }\n }\n\n /**\n * Asynchronously debounces the process transcription function to prevent rapid execution.\n *\n * @param {UUID} entityId - The ID of the entity related to the transcription.\n * @param {string} name - The name of the entity for transcription.\n * @param {string} userName - The username of the user initiating the transcription.\n * @param {BaseGuildVoiceChannel} channel - The voice channel where the transcription is happening.\n */\n\n async debouncedProcessTranscription(\n entityId: UUID,\n name: string,\n userName: string,\n channel: BaseGuildVoiceChannel\n ) {\n const DEBOUNCE_TRANSCRIPTION_THRESHOLD = 1500; // wait for 1.5 seconds of silence\n\n if (this.activeAudioPlayer?.state?.status === 'idle') {\n logger.log('Cleaning up idle audio player.');\n this.cleanupAudioPlayer(this.activeAudioPlayer);\n }\n\n if (this.activeAudioPlayer || this.processingVoice) {\n const state = this.userStates.get(entityId);\n if (state) {\n state.buffers.length = 0;\n state.totalLength = 0;\n }\n return;\n }\n\n if (this.transcriptionTimeout) {\n clearTimeout(this.transcriptionTimeout);\n }\n\n this.transcriptionTimeout = setTimeout(async () => {\n this.processingVoice = true;\n try {\n await this.processTranscription(entityId, channel.id, channel, name, userName);\n\n // Clean all users' previous buffers\n this.userStates.forEach((state, _) => {\n state.buffers.length = 0;\n state.totalLength = 0;\n });\n } finally {\n this.processingVoice = false;\n }\n }, DEBOUNCE_TRANSCRIPTION_THRESHOLD) as unknown as NodeJS.Timeout;\n }\n\n /**\n * Handle user audio stream for monitoring purposes.\n *\n * @param {UUID} userId - The unique identifier of the user.\n * @param {string} name - The name of the user.\n * @param {string} userName - The username of the user.\n * @param {BaseGuildVoiceChannel} channel - The voice channel the user is in.\n * @param {Readable} audioStream - The audio stream to monitor.\n */\n async handleUserStream(\n entityId: UUID,\n name: string,\n userName: string,\n channel: BaseGuildVoiceChannel,\n audioStream: Readable\n ) {\n logger.debug(`Starting audio monitor for user: ${entityId}`);\n if (!this.userStates.has(entityId)) {\n this.userStates.set(entityId, {\n buffers: [],\n totalLength: 0,\n lastActive: Date.now(),\n transcriptionText: '',\n });\n }\n\n const state = this.userStates.get(entityId);\n\n const processBuffer = async (buffer: Buffer) => {\n try {\n state?.buffers.push(buffer);\n state!.totalLength += buffer.length;\n state!.lastActive = Date.now();\n this.debouncedProcessTranscription(entityId, name, userName, channel);\n } catch (error) {\n console.error(`Error processing buffer for user ${entityId}:`, error);\n }\n };\n\n new AudioMonitor(\n audioStream,\n 10000000,\n () => {\n if (this.transcriptionTimeout) {\n clearTimeout(this.transcriptionTimeout);\n }\n },\n async (buffer) => {\n if (!buffer) {\n console.error('Received empty buffer');\n return;\n }\n await processBuffer(buffer);\n }\n );\n }\n\n /**\n * Process the transcription of audio data for a user.\n *\n * @param {UUID} entityId - The unique ID of the user entity.\n * @param {string} channelId - The ID of the channel where the transcription is taking place.\n * @param {BaseGuildVoiceChannel} channel - The voice channel where the user is speaking.\n * @param {string} name - The name of the user.\n * @param {string} userName - The username of the user.\n * @returns {Promise<void>}\n */\n private async processTranscription(\n entityId: UUID,\n channelId: string,\n channel: BaseGuildVoiceChannel,\n name: string,\n userName: string\n ) {\n const state = this.userStates.get(entityId);\n if (!state || state.buffers.length === 0) return;\n try {\n const inputBuffer = Buffer.concat(state.buffers, state.totalLength);\n\n state.buffers.length = 0; // Clear the buffers\n state.totalLength = 0;\n // Convert Opus to WAV\n const wavBuffer = await this.convertOpusToWav(inputBuffer);\n logger.debug('Starting transcription...');\n\n const transcriptionText = await this.runtime.useModel(ModelType.TRANSCRIPTION, wavBuffer);\n function isValidTranscription(text: string): boolean {\n if (!text || text.includes('[BLANK_AUDIO]')) return false;\n return true;\n }\n\n if (transcriptionText && isValidTranscription(transcriptionText)) {\n state.transcriptionText += transcriptionText;\n }\n\n if (state.transcriptionText.length) {\n this.cleanupAudioPlayer(this.activeAudioPlayer);\n const finalText = state.transcriptionText;\n state.transcriptionText = '';\n await this.handleMessage(finalText, entityId, channelId, channel, name, userName);\n }\n } catch (error) {\n console.error(`Error transcribing audio for user ${entityId}:`, error);\n }\n }\n\n /**\n * Handles a voice message received in a Discord channel.\n *\n * @param {string} message - The message content.\n * @param {UUID} entityId - The entity ID associated with the message.\n * @param {string} channelId - The ID of the Discord channel where the message was received.\n * @param {BaseGuildVoiceChannel} channel - The Discord channel where the message was received.\n * @param {string} name - The name associated with the message.\n * @param {string} userName - The user name associated with the message.\n * @returns {Promise<{text: string, actions: string[]}>} Object containing the resulting text and actions.\n */\n private async handleMessage(\n message: string,\n entityId: UUID,\n channelId: string,\n channel: BaseGuildVoiceChannel,\n name: string,\n userName: string\n ) {\n try {\n if (!message || message.trim() === '' || message.length < 3) {\n return { text: '', actions: ['IGNORE'] };\n }\n\n const roomId = createUniqueUuid(this.runtime, channelId);\n const uniqueEntityId = createUniqueUuid(this.runtime, entityId);\n const type = await this.getChannelType(channel as Channel);\n\n await this.runtime.ensureConnection({\n entityId: uniqueEntityId,\n roomId,\n userName,\n name: name,\n source: 'discord',\n channelId,\n serverId: channel.guild.id,\n type,\n worldId: createUniqueUuid(this.runtime, channel.guild.id) as UUID,\n worldName: channel.guild.name,\n });\n\n const memory: Memory = {\n id: createUniqueUuid(this.runtime, `${channelId}-voice-message-${Date.now()}`),\n agentId: this.runtime.agentId,\n entityId: uniqueEntityId,\n roomId,\n content: {\n text: message,\n source: 'discord',\n url: channel.url,\n name: name,\n userName: userName,\n isVoiceMessage: true,\n channelType: type,\n },\n createdAt: Date.now(),\n };\n\n const callback: HandlerCallback = async (content: Content, _files: any[] = []) => {\n try {\n const responseMemory: Memory = {\n id: createUniqueUuid(this.runtime, `${memory.id}-voice-response-${Date.now()}`),\n entityId: this.runtime.agentId,\n agentId: this.runtime.agentId,\n content: {\n ...content,\n name: this.runtime.character.name,\n inReplyTo: memory.id,\n isVoiceMessage: true,\n channelType: type,\n },\n roomId,\n createdAt: Date.now(),\n };\n\n if (responseMemory.content.text?.trim()) {\n await this.runtime.createMemory(responseMemory, 'messages');\n\n const responseStream = await this.runtime.useModel(\n ModelType.TEXT_TO_SPEECH,\n content.text\n );\n if (responseStream) {\n await this.playAudioStream(entityId, responseStream as Readable);\n }\n }\n\n return [responseMemory];\n } catch (error) {\n console.error('Error in voice message callback:', error);\n return [];\n }\n };\n\n // Emit voice-specific events\n this.runtime.emitEvent(['DISCORD_VOICE_MESSAGE_RECEIVED', 'VOICE_MESSAGE_RECEIVED'], {\n runtime: this.runtime,\n message: memory,\n callback,\n });\n } catch (error) {\n console.error('Error processing voice message:', error);\n }\n }\n\n /**\n * Asynchronously converts an Opus audio Buffer to a WAV audio Buffer.\n *\n * @param {Buffer} pcmBuffer - The Opus audio Buffer to convert to WAV.\n * @returns {Promise<Buffer>} A Promise that resolves with the converted WAV audio Buffer.\n */\n private async convertOpusToWav(pcmBuffer: Buffer): Promise<Buffer> {\n try {\n // Generate the WAV header\n const wavHeader = getWavHeader(pcmBuffer.length, DECODE_SAMPLE_RATE);\n\n // Concatenate the WAV header and PCM data\n const wavBuffer = Buffer.concat([wavHeader, pcmBuffer]);\n\n return wavBuffer;\n } catch (error) {\n console.error('Error converting PCM to WAV:', error);\n throw error;\n }\n }\n\n /**\n * Scans the given Discord guild to select a suitable voice channel to join.\n *\n * @param {Guild} guild The Discord guild to scan for voice channels.\n */\n async scanGuild(guild: Guild) {\n let chosenChannel: BaseGuildVoiceChannel | null = null;\n\n try {\n const channelId = this.runtime.getSetting('DISCORD_VOICE_CHANNEL_ID') as string;\n if (channelId) {\n const channel = await guild.channels.fetch(channelId);\n if (channel?.isVoiceBased()) {\n chosenChannel = channel as BaseGuildVoiceChannel;\n }\n }\n\n if (!chosenChannel) {\n const channels = (await guild.channels.fetch()).filter(\n (channel) => channel?.type === DiscordChannelType.GuildVoice\n );\n for (const [, channel] of channels) {\n const voiceChannel = channel as BaseGuildVoiceChannel;\n if (\n voiceChannel.members.size > 0 &&\n (chosenChannel === null || voiceChannel.members.size > chosenChannel.members.size)\n ) {\n chosenChannel = voiceChannel;\n }\n }\n }\n\n if (chosenChannel) {\n logger.debug(`Joining channel: ${chosenChannel.name}`);\n await this.joinChannel(chosenChannel);\n } else {\n logger.debug('Warning: No suitable voice channel found to join.');\n }\n } catch (error) {\n console.error('Error selecting or joining a voice channel:', error);\n }\n }\n\n /**\n * Play an audio stream for a given entity ID.\n *\n * @param {UUID} entityId - The ID of the entity to play the audio for.\n * @param {Readable} audioStream - The audio stream to play.\n * @returns {void}\n */\n async playAudioStream(entityId: UUID, audioStream: Readable) {\n const connection = this.connections.get(entityId);\n if (connection == null) {\n logger.debug(`No connection for user ${entityId}`);\n return;\n }\n this.cleanupAudioPlayer(this.activeAudioPlayer);\n const audioPlayer = createAudioPlayer({\n behaviors: {\n noSubscriber: NoSubscriberBehavior.Pause,\n },\n });\n this.activeAudioPlayer = audioPlayer;\n connection.subscribe(audioPlayer);\n\n const audioStartTime = Date.now();\n\n const resource = createAudioResource(audioStream, {\n inputType: StreamType.Arbitrary,\n });\n audioPlayer.play(resource);\n\n audioPlayer.on('error', (err: any) => {\n logger.debug(`Audio player error: ${err}`);\n });\n\n audioPlayer.on('stateChange', (_oldState: any, newState: { status: string }) => {\n if (newState.status === 'idle') {\n const idleTime = Date.now();\n logger.debug(`Audio playback took: ${idleTime - audioStartTime}ms`);\n }\n });\n }\n\n /**\n * Cleans up the provided audio player by stopping it, removing all listeners,\n * and resetting the active audio player if it matches the provided player.\n *\n * @param {AudioPlayer} audioPlayer - The audio player to be cleaned up.\n */\n cleanupAudioPlayer(audioPlayer: AudioPlayer | null) {\n if (!audioPlayer) return;\n\n audioPlayer.stop();\n audioPlayer.removeAllListeners();\n if (audioPlayer === this.activeAudioPlayer) {\n this.activeAudioPlayer = null;\n }\n }\n\n /**\n * Asynchronously handles the join channel command in an interaction.\n *\n * @param {any} interaction - The interaction object representing the user's input.\n * @returns {Promise<void>} - A promise that resolves once the join channel command is handled.\n */\n async handleJoinChannelCommand(interaction: any) {\n try {\n // Defer the reply immediately to prevent interaction timeout\n await interaction.deferReply();\n\n const channelId = interaction.options.get('channel')?.value as string;\n if (!channelId) {\n await interaction.editReply('Please provide a voice channel to join.');\n return;\n }\n\n const guild = interaction.guild;\n if (!guild) {\n await interaction.editReply('Could not find guild.');\n return;\n }\n\n const voiceChannel = interaction.guild.channels.cache.find(\n (channel: VoiceChannel) =>\n channel.id === channelId && channel.type === DiscordChannelType.GuildVoice\n );\n\n if (!voiceChannel) {\n await interaction.editReply('Voice channel not found!');\n return;\n }\n\n await this.joinChannel(voiceChannel as BaseGuildVoiceChannel);\n await interaction.editReply(`Joined voice channel: ${voiceChannel.name}`);\n } catch (error) {\n console.error('Error joining voice channel:', error);\n // Use editReply instead of reply for the error case\n await interaction.editReply('Failed to join the voice channel.').catch(console.error);\n }\n }\n\n /**\n * Handles the leave channel command by destroying the voice connection if it exists.\n *\n * @param {any} interaction The interaction object representing the command invocation.\n * @returns {void}\n */\n async handleLeaveChannelCommand(interaction: any) {\n const connection = this.getVoiceConnection(interaction.guildId as any);\n\n if (!connection) {\n await interaction.reply('Not currently in a voice channel.');\n return;\n }\n\n try {\n connection.destroy();\n await interaction.reply('Left the voice channel.');\n } catch (error) {\n console.error('Error leaving voice channel:', error);\n await interaction.reply('Failed to leave the voice channel.');\n }\n }\n}\n","import {\n AudioPlayerStatus,\n NoSubscriberBehavior,\n type VoiceConnection,\n VoiceConnectionStatus,\n createAudioPlayer,\n createAudioResource,\n entersState,\n} from '@discordjs/voice';\nimport { type IAgentRuntime, ModelType, type TestSuite, logger } from '@elizaos/core';\nimport { ChannelType, Events, type TextChannel, AttachmentBuilder } from 'discord.js';\nimport type { DiscordService } from './service';\nimport { ServiceType } from './types';\nimport { sendMessageInChunks } from './utils';\n\nconst TEST_IMAGE_URL =\n 'https://github.com/elizaOS/awesome-eliza/blob/main/assets/eliza-logo.jpg?raw=true';\n\n/**\n * Represents a test suite for Discord functionality.\n * @class DiscordTestSuite\n * @implements TestSuite\n * @property {string} name - The name of the test suite\n * @property {DiscordService | null} discordClient - The Discord client instance\n * @property {Array<{ name: string; fn: (runtime: IAgentRuntime) => Promise<void> }>} tests - Array of test functions\n */\nexport class DiscordTestSuite implements TestSuite {\n name = 'discord';\n private discordClient!: DiscordService; // Use definite assignment assertion\n tests: { name: string; fn: (runtime: IAgentRuntime) => Promise<void> }[];\n\n /**\n * Constructor for initializing the tests array with test cases to be executed.\n *\n * @constructor\n * @this {TestSuite}\n */\n constructor() {\n this.tests = [\n {\n name: 'Initialize Discord Client',\n fn: this.testCreatingDiscordClient.bind(this),\n },\n {\n name: 'Slash Commands - Join Voice',\n fn: this.testJoinVoiceSlashCommand.bind(this),\n },\n {\n name: 'Voice Playback & TTS',\n fn: this.testTextToSpeechPlayback.bind(this),\n },\n {\n name: 'Send Message with Attachments',\n fn: this.testSendingTextMessage.bind(this),\n },\n {\n name: 'Handle Incoming Messages',\n fn: this.testHandlingMessage.bind(this),\n },\n {\n name: 'Slash Commands - Leave Voice',\n fn: this.testLeaveVoiceSlashCommand.bind(this),\n },\n ];\n }\n\n /**\n * Asynchronously tests the creation of Discord client using the provided runtime.\n *\n * @param {IAgentRuntime} runtime - The agent runtime used to obtain the Discord service.\n * @returns {Promise<void>} - A Promise that resolves once the Discord client is ready.\n * @throws {Error} - If an error occurs while creating the Discord client.\n */\n async testCreatingDiscordClient(runtime: IAgentRuntime) {\n try {\n this.discordClient = runtime.getService(ServiceType.DISCORD) as DiscordService;\n if (!this.discordClient) {\n throw new Error('Failed to get DiscordService from runtime.');\n }\n\n // Wait for the bot to be ready before proceeding\n if (this.discordClient.client?.isReady()) {\n logger.success('DiscordService is already ready.');\n } else {\n logger.info('Waiting for DiscordService to be ready...');\n if (!this.discordClient.client) {\n throw new Error('Discord client instance is missing within the service.');\n }\n await new Promise((resolve, reject) => {\n this.discordClient.client?.once(Events.ClientReady, resolve);\n this.discordClient.client?.once(Events.Error, reject);\n });\n }\n } catch (error) {\n throw new Error(`Error in test creating Discord client: ${error}`);\n }\n }\n\n /**\n * Asynchronously tests the join voice slash command functionality.\n *\n * @param {IAgentRuntime} runtime - The runtime environment for the agent.\n * @returns {Promise<void>} - A promise that resolves once the test is complete.\n * @throws {Error} - If there is an error in executing the slash command test.\n */\n async testJoinVoiceSlashCommand(runtime: IAgentRuntime) {\n if (!this.discordClient) throw new Error('Discord client not initialized.');\n try {\n await this.waitForVoiceManagerReady(this.discordClient);\n\n const channel = await this.getTestChannel(runtime);\n if (!channel || !channel.isTextBased()) {\n throw new Error('Invalid test channel for slash command test.');\n }\n\n // Simulate a join channel slash command interaction\n const fakeJoinInteraction = {\n isCommand: () => true,\n commandName: 'joinchannel',\n options: {\n get: (name: string) => (name === 'channel' ? { value: channel.id } : null),\n },\n guild: (channel as TextChannel).guild,\n deferReply: async () => {},\n editReply: async (message: string) => {\n logger.info(`JoinChannel Slash Command Response: ${message}`);\n },\n };\n\n if (!this.discordClient.voiceManager) {\n throw new Error('VoiceManager is not available on the Discord client.');\n }\n await this.discordClient.voiceManager.handleJoinChannelCommand(fakeJoinInteraction as any);\n\n logger.success('Join voice slash command test completed successfully.');\n } catch (error) {\n throw new Error(`Error in join voice slash commands test: ${error}`);\n }\n }\n\n /**\n * Asynchronously tests the leave voice channel slash command.\n *\n * @param {IAgentRuntime} runtime - The Agent Runtime instance.\n * @returns {Promise<void>} A promise that resolves when the test is complete.\n */\n async testLeaveVoiceSlashCommand(runtime: IAgentRuntime) {\n if (!this.discordClient) throw new Error('Discord client not initialized.');\n try {\n await this.waitForVoiceManagerReady(this.discordClient);\n\n const channel = await this.getTestChannel(runtime);\n if (!channel || !channel.isTextBased()) {\n throw new Error('Invalid test channel for slash command test.');\n }\n\n // Simulate a leave channel slash command interaction\n const fakeLeaveInteraction = {\n isCommand: () => true,\n commandName: 'leavechannel',\n guildId: (channel as TextChannel).guildId,\n reply: async (message: string) => {\n logger.info(`LeaveChannel Slash Command Response: ${message}`);\n },\n };\n\n if (!this.discordClient.voiceManager) {\n throw new Error('VoiceManager is not available on the Discord client.');\n }\n await this.discordClient.voiceManager.handleLeaveChannelCommand(fakeLeaveInteraction as any);\n\n logger.success('Leave voice slash command test completed successfully.');\n } catch (error) {\n throw new Error(`Error in leave voice slash commands test: ${error}`);\n }\n }\n\n /**\n * Test Text to Speech playback.\n * @param {IAgentRuntime} runtime - The Agent Runtime instance.\n * @throws {Error} - If voice channel is invalid, voice connection fails to become ready, or no text to speech service found.\n */\n async testTextToSpeechPlayback(runtime: IAgentRuntime) {\n if (!this.discordClient) throw new Error('Discord client not initialized.');\n try {\n await this.waitForVoiceManagerReady(this.discordClient);\n\n const channel = await this.getTestChannel(runtime);\n if (!channel || channel.type !== ChannelType.GuildVoice) {\n throw new Error('Invalid voice channel.');\n }\n\n if (!this.discordClient.voiceManager) {\n throw new Error('VoiceManager is not available on the Discord client.');\n }\n await this.discordClient.voiceManager.joinChannel(channel);\n\n const guild = await this.getActiveGuild(this.discordClient);\n const guildId = guild.id;\n\n if (!this.discordClient.voiceManager) {\n throw new Error('VoiceManager is not available on the Discord client.');\n }\n const connection = this.discordClient.voiceManager.getVoiceConnection(guildId);\n\n if (!connection) {\n throw new Error(`No voice connection found for guild: ${guildId}`);\n }\n\n try {\n await entersState(connection, VoiceConnectionStatus.Ready, 10_000);\n logger.success(`Voice connection is ready in guild: ${guildId}`);\n } catch (error) {\n throw new Error(`Voice connection failed to become ready: ${error}`);\n }\n\n let responseStream = null;\n\n try {\n responseStream = await runtime.useModel(\n ModelType.TEXT_TO_SPEECH,\n `Hi! I'm ${runtime.character.name}! How are you doing today?`\n );\n } catch (_error) {\n throw new Error('No text to speech service found');\n }\n\n if (!responseStream) {\n throw new Error('TTS response stream is null or undefined.');\n }\n\n await this.playAudioStream(responseStream, connection);\n } catch (error) {\n throw new Error(`Error in TTS playback test: ${error}`);\n }\n }\n\n /**\n * Asynchronously tests sending a text message to a specified channel.\n *\n * @param {IAgentRuntime} runtime - The runtime for the agent.\n * @returns {Promise<void>} A Promise that resolves when the message is sent successfully.\n * @throws {Error} If there is an error in sending the text message.\n */\n async testSendingTextMessage(runtime: IAgentRuntime) {\n if (!this.discordClient) throw new Error('Discord client not initialized.');\n try {\n const channel = await this.getTestChannel(runtime);\n if (!channel || !channel.isTextBased()) {\n throw new Error('Cannot send message to a non-text channel.');\n }\n const attachment = new AttachmentBuilder(TEST_IMAGE_URL);\n await this.sendMessageToChannel(channel as TextChannel, 'Testing Message', [attachment]);\n } catch (error) {\n throw new Error(`Error in sending text message: ${error}`);\n }\n }\n\n /**\n * Asynchronously handles sending a test message using the given runtime and mock user data.\n *\n * @param {IAgentRuntime} runtime - The agent runtime object.\n * @returns {Promise<void>} A Promise that resolves once the message is handled.\n */\n async testHandlingMessage(runtime: IAgentRuntime) {\n if (!this.discordClient) throw new Error('Discord client not initialized.');\n try {\n const channel = await this.getTestChannel(runtime);\n\n const fakeMessage = {\n content: `Hello, ${runtime.character.name}! How are you?`,\n author: {\n id: 'mock-user-id',\n username: 'MockUser',\n bot: false,\n },\n channel,\n id: 'mock-message-id',\n createdTimestamp: Date.now(),\n mentions: {\n has: () => false,\n },\n reference: null,\n attachments: [],\n };\n if (!this.discordClient.messageManager) {\n throw new Error('MessageManager is not available on the Discord client.');\n }\n await this.discordClient.messageManager.handleMessage(fakeMessage as any);\n } catch (error) {\n throw new Error(`Error in handling message test: ${error}`);\n }\n }\n\n // #############################\n // Utility Functions\n // #############################\n\n /**\n * Asynchronously retrieves the test channel associated with the provided runtime.\n *\n * @param {IAgentRuntime} runtime - The runtime object containing necessary information.\n * @returns {Promise<Channel>} The test channel retrieved from the Discord client.\n * @throws {Error} If no test channel is found.\n */\n async getTestChannel(runtime: IAgentRuntime) {\n if (!this.discordClient) throw new Error('Discord client not initialized.');\n const channelId = this.validateChannelId(runtime);\n const channel = await this.discordClient.client?.channels.fetch(channelId);\n\n if (!channel) throw new Error('no test channel found!');\n\n return channel;\n }\n\n /**\n * Async function to send a message to a text-based channel.\n *\n * @param {TextChannel} channel - The text-based channel the message is being sent to.\n * @param {string} messageContent - The content of the message being sent.\n * @param {any[]} files - An array of files to include in the message.\n * @throws {Error} If the channel is not a text-based channel or does not exist.\n * @throws {Error} If there is an error sending the message.\n */\n async sendMessageToChannel(channel: TextChannel, messageContent: string, files: any[]) {\n try {\n if (!channel || !channel.isTextBased()) {\n throw new Error('Channel is not a text-based channel or does not exist.');\n }\n\n // Pass empty string for _inReplyTo as it expects a string\n await sendMessageInChunks(channel as TextChannel, messageContent, '', files);\n } catch (error) {\n throw new Error(`Error sending message: ${error}`);\n }\n }\n\n /**\n * Play an audio stream from a given response stream using the provided VoiceConnection.\n *\n * @param {any} responseStream - The response stream to play as audio.\n * @param {VoiceConnection} connection - The VoiceConnection to use for playing the audio.\n * @returns {Promise<void>} - A Promise that resolves when the TTS playback is finished.\n */\n async playAudioStream(responseStream: any, connection: VoiceConnection) {\n const audioPlayer = createAudioPlayer({\n behaviors: {\n noSubscriber: NoSubscriberBehavior.Pause,\n },\n });\n\n const audioResource = createAudioResource(responseStream);\n\n audioPlayer.play(audioResource);\n connection.subscribe(audioPlayer);\n\n logger.success('TTS playback started successfully.');\n\n await new Promise<void>((resolve, reject) => {\n audioPlayer.once(AudioPlayerStatus.Idle, () => {\n logger.info('TTS playback finished.');\n resolve();\n });\n\n audioPlayer.once('error', (error) => {\n reject(error);\n throw new Error(`TTS playback error: ${error}`);\n });\n });\n }\n\n /**\n * Retrieves the active guild where the bot is currently connected to a voice channel.\n *\n * @param {DiscordService} discordClient The DiscordService instance used to interact with the Discord API.\n * @returns {Promise<Guild>} The active guild where the bot is currently connected to a voice channel.\n * @throws {Error} If no active voice connection is found for the bot.\n */\n async getActiveGuild(discordClient: DiscordService) {\n if (!discordClient.client) {\n throw new Error('Discord client instance is missing within the service.');\n }\n const guilds = await discordClient.client.guilds.fetch();\n const fullGuilds = await Promise.all(guilds.map((guild) => guild.fetch())); // Fetch full guild data\n\n const activeGuild = fullGuilds.find((g) => g.members.me?.voice.channelId);\n if (!activeGuild) {\n throw new Error('No active voice connection found for the bot.');\n }\n return activeGuild;\n }\n\n /**\n * Waits for the VoiceManager in the Discord client to be ready.\n *\n * @param {DiscordService} discordClient - The Discord client to check for VoiceManager readiness.\n * @throws {Error} If the Discord client is not initialized.\n * @returns {Promise<void>} A promise that resolves when the VoiceManager is ready.\n */\n private async waitForVoiceManagerReady(discordClient: DiscordService) {\n if (!discordClient) {\n // This check might be redundant if called after the initial test setup check, but safe to keep.\n throw new Error('Discord client is not initialized.');\n }\n\n if (!discordClient.voiceManager) {\n throw new Error('VoiceManager is not available on the Discord client.');\n }\n\n if (!discordClient.voiceManager?.isReady()) {\n await new Promise<void>((resolve, reject) => {\n discordClient.voiceManager?.once('ready', resolve);\n discordClient.voiceManager?.once('error', reject);\n });\n }\n }\n\n /**\n * Validates the Discord test channel ID by checking if it is set in the runtime or environment variables.\n * If the test channel ID is not set, an error is thrown.\n *\n * @param {IAgentRuntime} runtime The runtime object containing the settings and environment variables.\n * @returns {string} The validated Discord test channel ID.\n */\n private validateChannelId(runtime: IAgentRuntime) {\n const testChannelId =\n runtime.getSetting('DISCORD_TEST_CHANNEL_ID') || process.env.DISCORD_TEST_CHANNEL_ID;\n if (!testChannelId) {\n throw new Error(\n 'DISCORD_TEST_CHANNEL_ID is not set. Please provide a valid channel ID in the environment variables.'\n );\n }\n return testChannelId as string; // Assert as string since we check for falsy above\n }\n}\n"],"mappings":"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","names":["logger","ModelType","composePromptFromState","parseJSONObjectFromText","fs","ModelType","composePromptFromState","parseJSONObjectFromText","trimTokens","summarizationTemplate","ModelType","composePromptFromState","parseJSONObjectFromText","composePromptFromState","ModelType","parseJSONObjectFromText","attachment","ChannelType","ModelType","composePromptFromState","createUniqueUuid","ServiceType","ChannelType","ServiceType","member","createUniqueUuid","composePromptFromState","ModelType","targetChannel","ChannelType","createUniqueUuid","logger","ServiceType","logger","ChannelType","createUniqueUuid","ChannelType","ChannelType","ServiceType","ChannelType","ChannelType","EventType","createUniqueUuid","logger","DiscordChannelType","PermissionsBitField","ChannelType","ServiceType","createUniqueUuid","logger","DiscordChannelType","fs","trimTokens","parseJSONObjectFromText","ModelType","ServiceType","ModelType","logger","parseJSONObjectFromText","trimTokens","ChannelType","logger","ChannelType","DiscordChannelType","createUniqueUuid","logger","ChannelType","entityId","match","ServiceType","ChannelType","ModelType","createUniqueUuid","logger","DiscordChannelType","connection","isValidTranscription","logger","createUniqueUuid","EventType","DiscordChannelType","ChannelType","PermissionsBitField","fullGuild","NoSubscriberBehavior","VoiceConnectionStatus","createAudioPlayer","createAudioResource","entersState","ModelType","logger","ChannelType","Events","ServiceType","logger","Events","ChannelType","entersState","VoiceConnectionStatus","ModelType","createAudioPlayer","NoSubscriberBehavior","createAudioResource","logger"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/actions/chatWithAttachments.ts","../src/actions/downloadMedia.ts","../src/actions/summarizeConversation.ts","../src/actions/transcribeMedia.ts","../src/actions/voiceJoin.ts","../src/types.ts","../src/actions/voiceLeave.ts","../src/providers/channelState.ts","../src/providers/voiceState.ts","../src/service.ts","../src/constants.ts","../src/messages.ts","../src/attachments.ts","../src/utils.ts","../src/voice.ts","../src/tests.ts"],"sourcesContent":["import { type IAgentRuntime, type Plugin, logger } from \"@elizaos/core\";\nimport chatWithAttachments from \"./actions/chatWithAttachments\";\nimport { downloadMedia } from \"./actions/downloadMedia\";\nimport { summarize } from \"./actions/summarizeConversation\";\nimport { transcribeMedia } from \"./actions/transcribeMedia\";\nimport { joinVoice } from \"./actions/voiceJoin\";\nimport { leaveVoice } from \"./actions/voiceLeave\";\nimport { channelStateProvider } from \"./providers/channelState\";\nimport { voiceStateProvider } from \"./providers/voiceState\";\nimport { DiscordService } from \"./service\";\nimport { DiscordTestSuite } from \"./tests\";\n\nconst discordPlugin: Plugin = {\n name: \"discord\",\n description:\n \"Discord service plugin for integration with Discord servers and channels\",\n services: [DiscordService],\n actions: [\n chatWithAttachments,\n downloadMedia,\n joinVoice,\n leaveVoice,\n summarize,\n transcribeMedia,\n ],\n providers: [channelStateProvider, voiceStateProvider],\n tests: [new DiscordTestSuite()],\n init: async (config: Record<string, string>, runtime: IAgentRuntime) => {\n const token = runtime.getSetting(\"DISCORD_API_TOKEN\") as string;\n\n if (!token || token.trim() === \"\") {\n logger.warn(\n \"Discord API Token not provided - Discord plugin is loaded but will not be functional\"\n );\n logger.warn(\n \"To enable Discord functionality, please provide DISCORD_API_TOKEN in your .eliza/.env file\"\n );\n }\n },\n};\n\nexport default discordPlugin;\n","import fs from \"node:fs\";\nimport {\n type Action,\n type ActionExample,\n ChannelType,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n ModelType,\n type State,\n composePromptFromState,\n parseJSONObjectFromText,\n trimTokens,\n} from \"@elizaos/core\";\n\nexport const summarizationTemplate = `# Summarized so far (we are adding to this)\n{{currentSummary}}\n\n# Current attachments we are summarizing\n{{attachmentsWithText}}\n\nSummarization objective: {{objective}}\n\n# Instructions: Summarize the attachments. Return the summary. Do not acknowledge this request, just summarize and continue the existing summary if there is one. Capture any important details based on the objective. Only respond with the new summary text.`;\n\n/**\n * Template for generating a summary of specific attachments based on recent messages.\n * This template includes placeholders for recentMessages, senderName, objective, and attachmentIds.\n * To generate a response, the user's objective and a list of attachment IDs must be determined.\n *\n * @type {string}\n */\nexport const attachmentIdsTemplate = `# Messages we are summarizing\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting a summary of specific attachments. Your goal is to determine their objective, along with the list of attachment IDs to summarize.\nThe \"objective\" is a detailed description of what the user wants to summarize based on the conversation.\nThe \"attachmentIds\" is an array of attachment IDs that the user wants to summarize. If not specified, default to including all attachments from the conversation.\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"objective\": \"<What the user wants to summarize>\",\n \"attachmentIds\": [\"<Attachment ID 1>\", \"<Attachment ID 2>\", ...]\n}\n\\`\\`\\`\n`;\n\n/**\n * Retrieves attachment IDs from a model using a prompt generated from the current state and a template.\n * @param {IAgentRuntime} runtime - The agent runtime to use for interaction with models\n * @param {Memory} _message - The memory object\n * @param {State} state - The current state of the conversation\n * @returns {Promise<{ objective: string; attachmentIds: string[] } | null>} An object containing the objective and attachment IDs, or null if the data could not be retrieved after multiple attempts\n */\nconst getAttachmentIds = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<{ objective: string; attachmentIds: string[] } | null> => {\n const prompt = composePromptFromState({\n state,\n template: attachmentIdsTemplate,\n });\n\n for (let i = 0; i < 5; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n // try parsing to a json object\n const parsedResponse = parseJSONObjectFromText(response) as {\n objective: string;\n attachmentIds: string[];\n } | null;\n // see if it contains objective and attachmentIds\n if (parsedResponse?.objective && parsedResponse?.attachmentIds) {\n return parsedResponse;\n }\n }\n return null;\n};\n\n/**\n * Represents an action to summarize user request informed by specific attachments based on their IDs.\n * If a user asks to chat with a PDF, or wants more specific information about a link or video or anything else they've attached, this is the action to use.\n * @typedef {Object} summarizeAction\n * @property {string} name - The name of the action\n * @property {string[]} similes - Similar actions related to summarization with attachments\n * @property {string} description - Description of the action\n * @property {Function} validate - Validation function to check if the action should be triggered based on keywords in the message\n * @property {Function} handler - Handler function to process the user request, summarize attachments, and provide a summary\n * @property {Object[]} examples - Examples demonstrating how to use the action with message content and expected responses\n */\n\nexport const chatWithAttachments: Action = {\n name: \"CHAT_WITH_ATTACHMENTS\",\n similes: [\n \"CHAT_WITH_ATTACHMENT\",\n \"SUMMARIZE_FILES\",\n \"SUMMARIZE_FILE\",\n \"SUMMARIZE_ATACHMENT\",\n \"CHAT_WITH_PDF\",\n \"ATTACHMENT_SUMMARY\",\n \"RECAP_ATTACHMENTS\",\n \"SUMMARIZE_FILE\",\n \"SUMMARIZE_VIDEO\",\n \"SUMMARIZE_AUDIO\",\n \"SUMMARIZE_IMAGE\",\n \"SUMMARIZE_DOCUMENT\",\n \"SUMMARIZE_LINK\",\n \"ATTACHMENT_SUMMARY\",\n \"FILE_SUMMARY\",\n ],\n description:\n \"Answer a user request informed by specific attachments based on their IDs. If a user asks to chat with a PDF, or wants more specific information about a link or video or anything else they've attached, this is the action to use.\",\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n const room = await _runtime.getRoom(message.roomId);\n\n // Only validate for Discord GROUP channels - this action is Discord-specific\n if (room?.type !== ChannelType.GROUP || room?.source !== \"discord\") {\n return false;\n }\n\n // only show if one of the keywords are in the message\n const keywords: string[] = [\n \"attachment\",\n \"summary\",\n \"summarize\",\n \"research\",\n \"pdf\",\n \"video\",\n \"audio\",\n \"image\",\n \"document\",\n \"link\",\n \"file\",\n \"attachment\",\n \"summarize\",\n \"code\",\n \"report\",\n \"write\",\n \"details\",\n \"information\",\n \"talk\",\n \"chat\",\n \"read\",\n \"listen\",\n \"watch\",\n ];\n return keywords.some((keyword) =>\n message.content.text?.toLowerCase().includes(keyword.toLowerCase())\n );\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const callbackData: Content = {\n text: \"\", // fill in later\n actions: [\"CHAT_WITH_ATTACHMENTS_RESPONSE\"],\n source: message.content.source,\n attachments: [],\n };\n\n // 1. extract attachment IDs from the message\n const attachmentData = await getAttachmentIds(runtime, message, state);\n if (!attachmentData) {\n console.error(\"Couldn't get attachment IDs from message\");\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: message.content.source,\n thought:\n \"I tried to chat with attachments but I couldn't get attachment IDs\",\n actions: [\"CHAT_WITH_ATTACHMENTS_FAILED\"],\n },\n metadata: {\n type: \"CHAT_WITH_ATTACHMENTS\",\n },\n },\n \"messages\"\n );\n return;\n }\n\n const { objective, attachmentIds } = attachmentData;\n\n const conversationLength = runtime.getConversationLength();\n\n const recentMessages = await runtime.getMemories({\n tableName: \"messages\",\n roomId: message.roomId,\n count: conversationLength,\n unique: false,\n });\n\n // This is pretty gross but it can catch cases where the returned generated UUID is stupidly wrong for some reason\n const attachments = recentMessages\n .filter(\n (msg) => msg.content.attachments && msg.content.attachments.length > 0\n )\n .flatMap((msg) => msg.content.attachments)\n // Ensure attachment is not undefined before accessing properties\n .filter(\n (attachment) =>\n attachment &&\n (attachmentIds\n .map((attch) => attch.toLowerCase().slice(0, 5))\n .includes(attachment.id.toLowerCase().slice(0, 5)) ||\n // or check the other way\n attachmentIds.some((id) => {\n const attachmentId = id.toLowerCase().slice(0, 5);\n // Add check here too\n return (\n attachment && attachment.id.toLowerCase().includes(attachmentId)\n );\n }))\n );\n\n const attachmentsWithText = attachments\n // Ensure attachment is not undefined before accessing properties\n .filter(\n (attachment): attachment is NonNullable<typeof attachment> =>\n !!attachment\n )\n .map((attachment) => `# ${attachment.title}\\n${attachment.text}`)\n .join(\"\\n\\n\");\n\n let currentSummary = \"\";\n\n const chunkSize = 8192;\n\n state.values.attachmentsWithText = attachmentsWithText;\n state.values.objective = objective;\n const template = await trimTokens(\n summarizationTemplate,\n chunkSize,\n runtime\n );\n const prompt = composePromptFromState({\n state,\n // make sure it fits, we can pad the tokens a bit\n // Get the model's tokenizer based on the current model being used\n template,\n });\n\n const summary = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n currentSummary = `${currentSummary}\\n${summary}`;\n\n if (!currentSummary) {\n console.error(\"No summary found, that's not good!\");\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: message.content.source,\n thought:\n \"I tried to chat with attachments but I couldn't get a summary\",\n actions: [\"CHAT_WITH_ATTACHMENTS_FAILED\"],\n },\n metadata: {\n type: \"CHAT_WITH_ATTACHMENTS\",\n },\n },\n \"messages\"\n );\n return;\n }\n\n callbackData.text = currentSummary.trim();\n if (\n callbackData.text &&\n (currentSummary.trim()?.split(\"\\n\").length < 4 ||\n currentSummary.trim()?.split(\" \").length < 100)\n ) {\n callbackData.text = `Here is the summary:\n\\`\\`\\`md\n${currentSummary.trim()}\n\\`\\`\\`\n`;\n await callback(callbackData);\n } else if (currentSummary.trim()) {\n const summaryDir = \"cache\";\n const summaryFilename = `${summaryDir}/summary_${Date.now()}.md`;\n try {\n await fs.promises.mkdir(summaryDir, { recursive: true });\n\n // Write file directly first\n await fs.promises.writeFile(summaryFilename, currentSummary, \"utf8\");\n\n // Then cache it\n await runtime.setCache<string>(summaryFilename, currentSummary);\n\n await callback(\n {\n ...callbackData,\n text: `I've attached the summary of the requested attachments as a text file.`,\n },\n [summaryFilename]\n );\n } catch (error) {\n console.error(\"Error in file/cache process:\", error);\n throw error;\n }\n } else {\n console.warn(\n \"Empty response from chat with attachments action, skipping\"\n );\n }\n\n return callbackData;\n },\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Can you summarize the attachments b3e23, c4f67, and d5a89?\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Sure thing! I'll pull up those specific attachments and provide a summary of their content.\",\n actions: [\"CHAT_WITH_ATTACHMENTS\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"I need a technical summary of the PDFs I sent earlier - a1b2c3.pdf, d4e5f6.pdf, and g7h8i9.pdf\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll take a look at those specific PDF attachments and put together a technical summary for you. Give me a few minutes to review them.\",\n actions: [\"CHAT_WITH_ATTACHMENTS\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Can you watch this video for me and tell me which parts you think are most relevant to the report I'm writing? (the one I attached in my last message)\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"sure, no problem.\",\n actions: [\"CHAT_WITH_ATTACHMENTS\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"can you read my blog post and give me a detailed breakdown of the key points I made, and then suggest a handful of tweets to promote it?\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"great idea, give me a minute\",\n actions: [\"CHAT_WITH_ATTACHMENTS\"],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default chatWithAttachments;\n","import {\n type Action,\n type ActionExample,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n ModelType,\n ServiceType,\n type State,\n composePromptFromState,\n parseJSONObjectFromText,\n} from \"@elizaos/core\";\n\n/**\n * Template for generating a media URL for a requested media file.\n *\n * @type {string}\n * @description This template is used for messages where a user is requesting to download a specific media file (video or audio). The goal is to determine the URL of the media they want to download.\n *\n * @param {string} recentMessages - Placeholder for recent messages related to the request.\n * @param {string} senderName - Name of the sender requesting the media file.\n *\n * @returns {string} - Formatted template with instructions and JSON structure for response.\n *\n * @example\n * `mediaUrlTemplate` contains the template for generating a media URL based on user request.\n */\n\nexport const mediaUrlTemplate = `# Messages we are searching for a media URL\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting to download a specific media file (video or audio). Your goal is to determine the URL of the media they want to download.\nThe \"mediaUrl\" is the URL of the media file that the user wants downloaded. If not specified, return null.\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"mediaUrl\": \"<Media URL>\"\n}\n\\`\\`\\`\n`;\n\n/**\n * Get a media URL from the user through text input using the provided runtime and state.\n * @param {IAgentRuntime} runtime - The runtime object to interact with the agent.\n * @param {Memory} _message - The memory object containing the input message.\n * @param {State} state - The state of the conversation.\n * @returns {Promise<string | null>} The media URL provided by the user or null if no valid URL is provided.\n */\nconst getMediaUrl = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<string | null> => {\n const prompt = composePromptFromState({\n state,\n template: mediaUrlTemplate,\n });\n\n for (let i = 0; i < 5; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response) as {\n mediaUrl: string;\n } | null;\n\n if (parsedResponse?.mediaUrl) {\n return parsedResponse.mediaUrl;\n }\n }\n return null;\n};\n\nexport const downloadMedia: Action = {\n name: \"DOWNLOAD_MEDIA\",\n similes: [\n \"DOWNLOAD_VIDEO\",\n \"DOWNLOAD_AUDIO\",\n \"GET_MEDIA\",\n \"DOWNLOAD_PODCAST\",\n \"DOWNLOAD_YOUTUBE\",\n ],\n description:\n \"Downloads a video or audio file from a URL and attaches it to the response message.\",\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n if (message.content.source !== \"discord\") {\n return false;\n }\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const videoService = runtime.getService(ServiceType.VIDEO) as any;\n\n if (!videoService) {\n console.error(\"Video service not found\");\n return;\n }\n\n const mediaUrl = await getMediaUrl(runtime, message, state);\n if (!mediaUrl) {\n console.error(\"Couldn't get media URL from messages\");\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: \"discord\",\n thought: `I couldn't find the media URL in the message`,\n actions: [\"DOWNLOAD_MEDIA_FAILED\"],\n },\n metadata: {\n type: \"DOWNLOAD_MEDIA\",\n },\n },\n \"messages\"\n );\n return;\n }\n\n const videoInfo = await videoService.fetchVideoInfo(mediaUrl);\n const mediaPath = await videoService.downloadVideo(videoInfo);\n\n const response: Content = {\n text: `I downloaded the video \"${videoInfo.title}\" and attached it below.`,\n actions: [\"DOWNLOAD_MEDIA_RESPONSE\"],\n source: message.content.source,\n attachments: [],\n };\n\n const maxRetries = 3;\n let retries = 0;\n\n while (retries < maxRetries) {\n try {\n await callback(\n {\n ...response,\n },\n [mediaPath]\n );\n break;\n } catch (error) {\n retries++;\n console.error(`Error sending message (attempt ${retries}):`, error);\n\n if (retries === maxRetries) {\n console.error(\n \"Max retries reached. Failed to send message with attachment.\"\n );\n break;\n }\n\n // Wait for a short delay before retrying\n await new Promise((resolve) => setTimeout(resolve, 2000));\n }\n }\n\n return response;\n },\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"https://www.youtube.com/watch?v=dQw4w9WgXcQ\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Downloading the YouTube video now, one sec\",\n actions: [\"DOWNLOAD_MEDIA\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Can you grab this video for me? https://vimeo.com/123456789\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Sure thing, I'll download that Vimeo video for you\",\n actions: [\"DOWNLOAD_MEDIA\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"I need this video downloaded: https://www.youtube.com/watch?v=abcdefg\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"No problem, I'm on it. I'll have that YouTube video downloaded in a jiffy\",\n actions: [\"DOWNLOAD_MEDIA\"],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default downloadMedia;\n","import fs from \"node:fs\";\nimport {\n type Action,\n type ActionExample,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Media,\n type Memory,\n ModelType,\n type State,\n composePromptFromState,\n getEntityDetails,\n parseJSONObjectFromText,\n splitChunks,\n trimTokens,\n} from \"@elizaos/core\";\nexport const summarizationTemplate = `# Summarized so far (we are adding to this)\n{{currentSummary}}\n\n# Current conversation chunk we are summarizing (includes attachments)\n{{memoriesWithAttachments}}\n\nSummarization objective: {{objective}}\n\n# Instructions: Summarize the conversation so far. Return the summary. Do not acknowledge this request, just summarize and continue the existing summary if there is one. Capture any important details to the objective. Only respond with the new summary text.\nYour response should be extremely detailed and include any and all relevant information.`;\n\n/**\n * Template for providing instructions and details on how to summarize conversation messages and determine the range of dates requested.\n * The template includes placeholders for recent messages, sender name, objective, start and end date range.\n * The response is expected to be formatted as a JSON block with specific structure.\n * @type {string}\n */\nexport const dateRangeTemplate = `# Messages we are summarizing (the conversation is continued after this)\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting a summary of the conversation. Your goal is to determine their objective, along with the range of dates that their request covers.\nThe \"objective\" is a detailed description of what the user wants to summarize based on the conversation. If they just ask for a general summary, you can either base it off the conversation if the summary range is very recent, or set the object to be general, like \"a detailed summary of the conversation between all users\".\nThe \"start\" and \"end\" are the range of dates that the user wants to summarize, relative to the current time. The start and end should be relative to the current time, and measured in seconds, minutes, hours and days. The format is \"2 days ago\" or \"3 hours ago\" or \"4 minutes ago\" or \"5 seconds ago\", i.e. \"<integer> <unit> ago\".\nIf you aren't sure, you can use a default range of \"0 minutes ago\" to \"2 hours ago\" or more. Better to err on the side of including too much than too little.\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"objective\": \"<What the user wants to summarize>\",\n \"start\": \"0 minutes ago\",\n \"end\": \"2 hours ago\"\n}\n\\`\\`\\`\n`;\n\n/**\n * Function to get a date range from user input.\n *\n * @param {IAgentRuntime} runtime - The Agent Runtime object.\n * @param {Memory} _message - The Memory object.\n * @param {State} state - The State object.\n * @return {Promise<{ objective: string; start: string | number; end: string | number; } | null>} Parsed user input containing objective, start, and end timestamps, or null.\n */\nconst getDateRange = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n) => {\n const prompt = composePromptFromState({\n state,\n template: dateRangeTemplate,\n });\n\n for (let i = 0; i < 5; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n // try parsing to a json object\n const parsedResponse = parseJSONObjectFromText(response) as {\n objective: string;\n start: string | number;\n end: string | number;\n } | null;\n // see if it contains objective, start and end\n if (parsedResponse) {\n if (\n parsedResponse.objective &&\n parsedResponse.start &&\n parsedResponse.end\n ) {\n // TODO: parse start and end into timestamps\n const startIntegerString = (parsedResponse.start as string).match(\n /\\d+/\n )?.[0];\n const endIntegerString = (parsedResponse.end as string).match(\n /\\d+/\n )?.[0];\n\n // parse multiplier\n const multipliers = {\n second: 1 * 1000,\n minute: 60 * 1000,\n hour: 3600 * 1000,\n day: 86400 * 1000,\n };\n\n const startMultiplier = (parsedResponse.start as string).match(\n /second|minute|hour|day/\n )?.[0];\n const endMultiplier = (parsedResponse.end as string).match(\n /second|minute|hour|day/\n )?.[0];\n\n const startInteger = startIntegerString\n ? Number.parseInt(startIntegerString)\n : 0;\n const endInteger = endIntegerString\n ? Number.parseInt(endIntegerString)\n : 0;\n\n // multiply by multiplier\n const startTime =\n startInteger *\n multipliers[startMultiplier as keyof typeof multipliers];\n\n const endTime =\n endInteger * multipliers[endMultiplier as keyof typeof multipliers];\n\n // get the current time and subtract the start and end times\n parsedResponse.start = Date.now() - startTime;\n parsedResponse.end = Date.now() - endTime;\n\n return parsedResponse;\n }\n }\n }\n};\n\n/**\n * Action to summarize a conversation and attachments.\n *\n * @typedef {Action} summarizeAction\n * @property {string} name - The name of the action.\n * @property {string[]} similes - Array of related terms.\n * @property {string} description - Description of the action.\n * @property {Function} validate - Asynchronous function to validate the action.\n * @property {Function} handler - Asynchronous function to handle the action.\n * @property {ActionExample[][]} examples - Array of examples demonstrating the action.\n */\nexport const summarize: Action = {\n name: \"SUMMARIZE_CONVERSATION\",\n similes: [\n \"RECAP\",\n \"RECAP_CONVERSATION\",\n \"SUMMARIZE_CHAT\",\n \"SUMMARIZATION\",\n \"CHAT_SUMMARY\",\n \"CONVERSATION_SUMMARY\",\n ],\n description: \"Summarizes the conversation and attachments.\",\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n if (message.content.source !== \"discord\") {\n return false;\n }\n // only show if one of the keywords are in the message\n const keywords: string[] = [\n \"summarize\",\n \"summarization\",\n \"summary\",\n \"recap\",\n \"report\",\n \"overview\",\n \"review\",\n \"rundown\",\n \"wrap-up\",\n \"brief\",\n \"debrief\",\n \"abstract\",\n \"synopsis\",\n \"outline\",\n \"digest\",\n \"abridgment\",\n \"condensation\",\n \"encapsulation\",\n \"essence\",\n \"gist\",\n \"main points\",\n \"key points\",\n \"key takeaways\",\n \"bulletpoint\",\n \"highlights\",\n \"tldr\",\n \"tl;dr\",\n \"in a nutshell\",\n \"bottom line\",\n \"long story short\",\n \"sum up\",\n \"sum it up\",\n \"short version\",\n \"bring me up to speed\",\n \"catch me up\",\n ];\n return keywords.some((keyword) =>\n message.content.text?.toLowerCase().includes(keyword.toLowerCase())\n );\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const callbackData: Content = {\n text: \"\", // fill in later\n actions: [\"SUMMARIZATION_RESPONSE\"],\n source: message.content.source,\n attachments: [],\n };\n const { roomId } = message;\n\n // 1. extract date range from the message\n const dateRange = await getDateRange(runtime, message, state);\n if (!dateRange) {\n console.error(\"Couldn't get date range from message\");\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: \"discord\",\n thought: `I couldn't get the date range from the message`,\n actions: [\"SUMMARIZE_CONVERSATION_FAILED\"],\n },\n metadata: {\n type: \"SUMMARIZE_CONVERSATION\",\n },\n },\n \"messages\"\n );\n return;\n }\n\n const { objective, start, end } = dateRange;\n\n // 2. get these memories from the database\n const memories = await runtime.getMemories({\n tableName: \"messages\",\n roomId,\n // subtract start from current time\n start: Number.parseInt(start as string),\n end: Number.parseInt(end as string),\n count: 10000,\n unique: false,\n });\n\n const entities = await getEntityDetails({\n runtime: runtime as IAgentRuntime,\n roomId,\n });\n\n const actorMap = new Map(entities.map((entity) => [entity.id, entity]));\n\n const formattedMemories = memories\n .map((memory) => {\n const attachments = memory.content.attachments\n ?.map((attachment: Media) => {\n return `---\\nAttachment: ${attachment.id}\\n${attachment.description}\\n${attachment.text}\\n---`;\n })\n .join(\"\\n\");\n return `${actorMap.get(memory.entityId)?.name ?? \"Unknown User\"} (${actorMap.get(memory.entityId)?.username ?? \"\"}): ${memory.content.text}\\n${attachments}`;\n })\n .join(\"\\n\");\n\n let currentSummary = \"\";\n\n const chunkSize = 8000;\n\n const chunks = await splitChunks(formattedMemories, chunkSize, 0);\n\n const _datestr = new Date().toUTCString().replace(/:/g, \"-\");\n\n state.values.memoriesWithAttachments = formattedMemories;\n state.values.objective = objective;\n\n for (let i = 0; i < chunks.length; i++) {\n const chunk = chunks[i];\n state.values.currentSummary = currentSummary;\n state.values.currentChunk = chunk;\n const template = await trimTokens(\n summarizationTemplate,\n chunkSize + 500,\n runtime\n );\n const prompt = composePromptFromState({\n state,\n // make sure it fits, we can pad the tokens a bit\n template,\n });\n\n const summary = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n currentSummary = `${currentSummary}\\n${summary}`;\n }\n\n if (!currentSummary) {\n console.error(\"No summary found, that's not good!\");\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: \"discord\",\n thought: `I couldn't summarize the conversation`,\n actions: [\"SUMMARIZE_CONVERSATION_FAILED\"],\n },\n metadata: {\n type: \"SUMMARIZE_CONVERSATION\",\n },\n },\n \"messages\"\n );\n return;\n }\n\n callbackData.text = currentSummary.trim();\n if (\n callbackData.text &&\n (currentSummary.trim()?.split(\"\\n\").length < 4 ||\n currentSummary.trim()?.split(\" \").length < 100)\n ) {\n callbackData.text = `Here is the summary:\n\\`\\`\\`md\n${currentSummary.trim()}\n\\`\\`\\`\n`;\n await callback(callbackData);\n } else if (currentSummary.trim()) {\n const summaryDir = \"cache\";\n const summaryFilename = `${summaryDir}/conversation_summary_${Date.now()}`;\n await runtime.setCache<string>(summaryFilename, currentSummary);\n await fs.promises.mkdir(summaryDir, { recursive: true });\n\n await fs.promises.writeFile(summaryFilename, currentSummary, \"utf8\");\n // save the summary to a file\n await callback(\n {\n ...callbackData,\n text: `I've attached the summary of the conversation from \\`${new Date(Number.parseInt(start as string)).toString()}\\` to \\`${new Date(Number.parseInt(end as string)).toString()}\\` as a text file.`,\n },\n [summaryFilename]\n );\n } else {\n console.warn(\n \"Empty response from summarize conversation action, skipping\"\n );\n }\n\n return callbackData;\n },\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"```js\\nconst x = 10\\n```\",\n },\n },\n {\n name: \"{{name1}}\",\n content: {\n text: \"can you give me a detailed report on what we're talking about?\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"sure, no problem, give me a minute to get that together for you\",\n actions: [\"SUMMARIZE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"please summarize the conversation we just had and include this blogpost i'm linking (Attachment: b3e12)\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"sure, give me a sec\",\n actions: [\"SUMMARIZE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Can you summarize what moon and avf are talking about?\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Yeah, just hold on a second while I get that together for you...\",\n actions: [\"SUMMARIZE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"i need to write a blog post about farming, can you summarize the discussion from a few hours ago?\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"no problem, give me a few minutes to read through everything\",\n actions: [\"SUMMARIZE\"],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default summarize;\n","import {\n type Action,\n type ActionExample,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n ModelType,\n type State,\n composePromptFromState,\n createUniqueUuid,\n parseJSONObjectFromText,\n} from \"@elizaos/core\";\n\nexport const transcriptionTemplate = `# Transcription of media file\n{{mediaTranscript}}\n\n# Instructions: Return only the full transcript of the media file without any additional prompt or commentary.`;\n\n/**\n * Template for generating media attachment ID request for transcription\n *\n * @type {string}\n */\nexport const mediaAttachmentIdTemplate = `# Messages we are transcribing\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting a transcription of a specific media file (audio or video). Your goal is to determine the ID of the attachment they want transcribed.\nThe \"attachmentId\" is the ID of the media file attachment that the user wants transcribed. If not specified, return null.\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"attachmentId\": \"<Attachment ID>\"\n}\n\\`\\`\\`\n`;\n\n/**\n * Asynchronous function to get the media attachment ID from the user input.\n *\n * @param {IAgentRuntime} runtime - The agent runtime object.\n * @param {Memory} _message - The memory object.\n * @param {State} state - The current state of the conversation.\n * @returns {Promise<string | null>} A promise that resolves with the media attachment ID or null.\n */\nconst getMediaAttachmentId = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<string | null> => {\n const prompt = composePromptFromState({\n state,\n template: mediaAttachmentIdTemplate,\n });\n\n for (let i = 0; i < 5; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response) as {\n attachmentId: string;\n } | null;\n\n if (parsedResponse?.attachmentId) {\n return parsedResponse.attachmentId;\n }\n }\n return null;\n};\n\n/**\n * Action for transcribing the full text of an audio or video file that the user has attached.\n *\n * @typedef {Object} Action\n * @property {string} name - The name of the action.\n * @property {string[]} similes - Similes associated with the action.\n * @property {string} description - Description of the action.\n * @property {Function} validate - Validation function for the action.\n * @property {Function} handler - Handler function for the action.\n * @property {ActionExample[][]} examples - Examples demonstrating the action.\n */\nexport const transcribeMedia: Action = {\n name: \"TRANSCRIBE_MEDIA\",\n similes: [\n \"TRANSCRIBE_AUDIO\",\n \"TRANSCRIBE_VIDEO\",\n \"MEDIA_TRANSCRIPT\",\n \"VIDEO_TRANSCRIPT\",\n \"AUDIO_TRANSCRIPT\",\n ],\n description:\n \"Transcribe the full text of an audio or video file that the user has attached.\",\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n if (message.content.source !== \"discord\") {\n return false;\n }\n\n const keywords: string[] = [\n \"transcribe\",\n \"transcript\",\n \"audio\",\n \"video\",\n \"media\",\n \"youtube\",\n \"meeting\",\n \"recording\",\n \"podcast\",\n \"call\",\n \"conference\",\n \"interview\",\n \"speech\",\n \"lecture\",\n \"presentation\",\n ];\n return keywords.some((keyword) =>\n message.content.text?.toLowerCase().includes(keyword.toLowerCase())\n );\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const callbackData: Content = {\n text: \"\", // fill in later\n actions: [\"TRANSCRIBE_MEDIA_RESPONSE\"],\n source: message.content.source,\n attachments: [],\n };\n\n const attachmentId = await getMediaAttachmentId(runtime, message, state);\n if (!attachmentId) {\n console.error(\"Couldn't get media attachment ID from message\");\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: \"discord\",\n thought: `I couldn't find the media attachment ID in the message`,\n actions: [\"TRANSCRIBE_MEDIA_FAILED\"],\n },\n metadata: {\n type: \"TRANSCRIBE_MEDIA\",\n },\n },\n \"messages\"\n );\n return;\n }\n\n const conversationLength = runtime.getConversationLength();\n\n const recentMessages = await runtime.getMemories({\n tableName: \"messages\",\n roomId: message.roomId,\n count: conversationLength,\n unique: false,\n });\n\n const attachment = recentMessages\n .filter(\n (msg) => msg.content.attachments && msg.content.attachments.length > 0\n )\n .flatMap((msg) => msg.content.attachments)\n .find(\n (attachment) =>\n attachment?.id.toLowerCase() === attachmentId.toLowerCase()\n );\n\n if (!attachment) {\n console.error(`Couldn't find attachment with ID ${attachmentId}`);\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: \"discord\",\n thought: `I couldn't find the media attachment with ID ${attachmentId}`,\n actions: [\"TRANSCRIBE_MEDIA_FAILED\"],\n },\n metadata: {\n type: \"TRANSCRIBE_MEDIA\",\n },\n },\n \"messages\"\n );\n return;\n }\n\n const mediaTranscript = attachment.text;\n\n callbackData.text = mediaTranscript.trim();\n\n // if callbackData.text is < 4 lines or < 100 words, then we we callback with normal message wrapped in markdown block\n if (\n callbackData.text &&\n (callbackData.text?.split(\"\\n\").length < 4 ||\n callbackData.text?.split(\" \").length < 100)\n ) {\n callbackData.text = `Here is the transcript:\n\\`\\`\\`md\n${mediaTranscript.trim()}\n\\`\\`\\`\n`;\n await callback(callbackData);\n }\n // if text is big, let's send as an attachment\n else if (callbackData.text) {\n const transcriptFilename = `content/transcript_${Date.now()}`;\n\n // save the transcript to a file\n await runtime.setCache<string>(transcriptFilename, callbackData.text);\n\n await callback(\n {\n ...callbackData,\n text: `I've attached the transcript as a text file.`,\n },\n [transcriptFilename]\n );\n } else {\n console.warn(\"Empty response from transcribe media action, skipping\");\n }\n\n return callbackData;\n },\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Please transcribe the audio file I just sent.\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Sure, I'll transcribe the full audio for you.\",\n actions: [\"TRANSCRIBE_MEDIA\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Can I get a transcript of that video recording?\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Absolutely, give me a moment to generate the full transcript of the video.\",\n actions: [\"TRANSCRIBE_MEDIA\"],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default transcribeMedia;\n","// src/actions/joinVoice\nimport {\n type Action,\n type ActionExample,\n ChannelType,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n ModelType,\n type State,\n composePromptFromState,\n createUniqueUuid,\n logger,\n} from \"@elizaos/core\";\nimport {\n type BaseGuildVoiceChannel,\n type Channel,\n ChannelType as DiscordChannelType,\n type Guild,\n} from \"discord.js\";\nimport type { DiscordService } from \"../service\";\nimport { ServiceType } from \"../types\";\nimport type { VoiceManager } from \"../voice\";\n\nexport const joinVoice: Action = {\n name: \"JOIN_VOICE\",\n similes: [\n \"JOIN_VOICE\",\n \"JOIN_VC\",\n \"JOIN_VOICE_CHAT\",\n \"JOIN_VOICE_CHANNEL\",\n \"JOIN_MEETING\",\n \"JOIN_CALL\",\n ],\n validate: async (runtime: IAgentRuntime, message: Memory, state: State) => {\n if (message.content.source !== \"discord\") {\n // not a discord message\n return false;\n }\n\n const room = state.data.room ?? (await runtime.getRoom(message.roomId));\n\n if (\n room?.type !== ChannelType.GROUP &&\n room?.type !== ChannelType.VOICE_GROUP\n ) {\n return false;\n }\n\n const client = runtime.getService(ServiceType.DISCORD);\n\n if (!client) {\n logger.error(\"Discord client not found\");\n return false;\n }\n\n return true;\n },\n description: \"Join a voice channel to participate in voice chat.\",\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ): Promise<boolean> => {\n const room = state.data.room ?? (await runtime.getRoom(message.roomId));\n // Ensure messageContent is a string, defaulting to empty if undefined\n const messageContent = message?.content?.text?.toLowerCase() ?? \"\";\n\n if (!room) {\n throw new Error(\"No room found\");\n }\n\n if (\n room?.type !== ChannelType.GROUP &&\n room?.type !== ChannelType.VOICE_GROUP\n ) {\n return false;\n }\n\n const serverId = room.serverId;\n\n if (!serverId) {\n throw new Error(\"No server ID found\");\n }\n\n const discordClient = runtime.getService(\n ServiceType.DISCORD\n ) as DiscordService;\n const client = discordClient.client;\n const voiceManager = discordClient.voiceManager as VoiceManager;\n\n if (!client) {\n logger.error(\"Discord client not found\");\n return false;\n }\n\n const voiceChannels = (\n client.guilds.cache.get(serverId) as Guild\n ).channels.cache.filter(\n (channel: Channel) => channel.type === DiscordChannelType.GuildVoice\n );\n\n const targetChannel = voiceChannels.find((channel) => {\n const name = (channel as { name: string }).name.toLowerCase();\n // remove all non-alphanumeric characters (keep spaces between words)\n const replacedName = name.replace(/[^a-z0-9 ]/g, \"\");\n\n // messageContent is now guaranteed to be a string\n return (\n name.includes(messageContent) ||\n messageContent.includes(name) ||\n replacedName.includes(messageContent) ||\n messageContent.includes(replacedName)\n );\n });\n\n if (targetChannel) {\n voiceManager.joinChannel(targetChannel as BaseGuildVoiceChannel);\n return true;\n }\n const guild = client.guilds.cache.get(serverId);\n const members = guild?.members.cache;\n\n // get the member who's stringTouuid(id) === message userId\n const member = members?.find(\n (member) => createUniqueUuid(runtime, member.id) === message.entityId\n );\n\n if (member?.voice?.channel) {\n const userVoiceChannel = member.voice.channel as BaseGuildVoiceChannel;\n voiceManager.joinChannel(userVoiceChannel);\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: \"discord\",\n thought: `I joined the voice channel ${userVoiceChannel.name}`,\n actions: [\"JOIN_VOICE_STARTED\"],\n },\n metadata: {\n type: \"JOIN_VOICE\",\n },\n },\n \"messages\"\n );\n\n // save a memory for the new channel as well\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: createUniqueUuid(runtime, userVoiceChannel.id),\n content: {\n source: \"discord\",\n thought: `I joined the voice channel ${userVoiceChannel.name}`,\n actions: [\"JOIN_VOICE_STARTED\"],\n },\n metadata: {\n type: \"JOIN_VOICE\",\n },\n },\n \"messages\"\n );\n return true;\n }\n\n const messageTemplate = `\nThe user has requested to join a voice channel.\nHere is the list of channels available in the server:\n{{voiceChannels}}\n\nHere is the user's request:\n{{userMessage}}\n\nPlease respond with the name of the voice channel which the bot should join. Try to infer what channel the user is talking about. If the user didn't specify a voice channel, respond with \"none\".\nYou should only respond with the name of the voice channel or none, no commentary or additional information should be included.\n`;\n\n const guessState = {\n userMessage: message.content.text,\n voiceChannels: voiceChannels\n .map((channel) => (channel as { name: string }).name)\n .join(\"\\n\"),\n };\n\n const prompt = composePromptFromState({\n template: messageTemplate,\n state: guessState as unknown as State,\n });\n\n const responseContent = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n if (responseContent && responseContent.trim().length > 0) {\n // join the voice channel\n const channelName = responseContent.toLowerCase();\n\n const targetChannel = voiceChannels.find((channel) => {\n const name = (channel as { name: string }).name.toLowerCase();\n\n // remove all non-alphanumeric characters (keep spaces between words)\n const replacedName = name.replace(/[^a-z0-9 ]/g, \"\");\n\n return (\n name.includes(channelName) ||\n channelName.includes(name) ||\n replacedName.includes(channelName) ||\n channelName.includes(replacedName)\n );\n });\n\n if (targetChannel) {\n voiceManager.joinChannel(targetChannel as BaseGuildVoiceChannel);\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: \"discord\",\n thought: `I joined the voice channel ${targetChannel.name}`,\n actions: [\"JOIN_VOICE_STARTED\"],\n },\n metadata: {\n type: \"JOIN_VOICE\",\n },\n },\n \"messages\"\n );\n\n // save a memory for the new channel as well\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: createUniqueUuid(runtime, targetChannel.id),\n content: {\n source: \"discord\",\n thought: `I joined the voice channel ${targetChannel.name}`,\n actions: [\"JOIN_VOICE_STARTED\"],\n },\n metadata: {\n type: \"JOIN_VOICE\",\n },\n },\n \"messages\"\n );\n return true;\n }\n }\n\n await callback({\n text: \"I couldn't figure out which channel you wanted me to join.\",\n source: \"discord\",\n });\n return false;\n },\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Hey, let's jump into the 'General' voice and chat\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Sounds good\",\n actions: [\"JOIN_VOICE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"{{name2}}, can you join the vc, I want to discuss our strat\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Sure I'll join right now\",\n actions: [\"JOIN_VOICE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"hey {{name2}}, we're having a team meeting in the 'conference' voice channel, plz join us\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"OK see you there\",\n actions: [\"JOIN_VOICE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"{{name2}}, let's have a quick voice chat in the 'Lounge' channel.\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"kk be there in a sec\",\n actions: [\"JOIN_VOICE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Hey {{name2}}, can you join me in the 'Music' voice channel\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Sure\",\n actions: [\"JOIN_VOICE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"join voice chat with us {{name2}}\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"coming\",\n actions: [\"JOIN_VOICE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"hop in vc {{name2}}\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"joining now\",\n actions: [\"JOIN_VOICE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"get in vc with us {{name2}}\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"im in\",\n actions: [\"JOIN_VOICE\"],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default joinVoice;\n","import type {\n Character,\n EntityPayload,\n MessagePayload,\n WorldPayload,\n} from \"@elizaos/core\";\nimport type {\n Client as DiscordJsClient,\n Guild,\n GuildMember,\n Message,\n MessageReaction,\n User,\n VoiceState,\n} from \"discord.js\";\n\n/**\n * Discord-specific event types\n */\nexport enum DiscordEventTypes {\n // Message events (prefixed versions of core events)\n MESSAGE_RECEIVED = \"DISCORD_MESSAGE_RECEIVED\",\n MESSAGE_SENT = \"DISCORD_MESSAGE_SENT\",\n\n // Reaction events\n REACTION_RECEIVED = \"DISCORD_REACTION_RECEIVED\",\n REACTION_REMOVED = \"DISCORD_REACTION_REMOVED\",\n\n // Server events\n WORLD_JOINED = \"DISCORD_WORLD_JOINED\",\n WORLD_CONNECTED = \"DISCORD_SERVER_CONNECTED\",\n\n // User events\n ENTITY_JOINED = \"DISCORD_USER_JOINED\",\n ENTITY_LEFT = \"DISCORD_USER_LEFT\",\n\n // Voice events\n VOICE_STATE_CHANGED = \"DISCORD_VOICE_STATE_CHANGED\",\n}\n\n/**\n * Discord-specific message received payload\n */\nexport interface DiscordMessageReceivedPayload extends MessagePayload {\n /** The original Discord message */\n originalMessage: Message;\n}\n\n/**\n * Discord-specific message sent payload\n */\nexport interface DiscordMessageSentPayload extends MessagePayload {\n /** The original Discord messages sent */\n originalMessages: Message[];\n}\n\n/**\n * Discord-specific reaction received payload\n */\nexport interface DiscordReactionPayload extends MessagePayload {\n /** The original Discord reaction */\n originalReaction: MessageReaction;\n /** The user who reacted */\n user: User;\n}\n/**\n * Discord-specific server payload\n */\nexport interface DiscordServerPayload extends WorldPayload {\n /** The original Discord guild */\n server: Guild;\n}\n\n/**\n * Discord-specific user joined payload\n */\nexport interface DiscordUserJoinedPayload extends EntityPayload {\n /** The original Discord guild member */\n member: GuildMember;\n}\n\n/**\n * Discord-specific user left payload\n */\nexport interface DiscordUserLeftPayload extends EntityPayload {\n /** The original Discord guild member */\n member: GuildMember;\n}\n\n/**\n * Discord-specific voice state changed payload\n */\nexport interface DiscordVoiceStateChangedPayload {\n /** The original Discord voice state */\n voiceState: VoiceState;\n}\n\n/**\n * Maps Discord event types to their payload interfaces\n */\nexport interface DiscordEventPayloadMap {\n [DiscordEventTypes.MESSAGE_RECEIVED]: DiscordMessageReceivedPayload;\n [DiscordEventTypes.MESSAGE_SENT]: DiscordMessageSentPayload;\n [DiscordEventTypes.REACTION_RECEIVED]: DiscordReactionPayload;\n [DiscordEventTypes.REACTION_REMOVED]: DiscordReactionPayload;\n [DiscordEventTypes.WORLD_JOINED]: DiscordServerPayload;\n [DiscordEventTypes.WORLD_CONNECTED]: DiscordServerPayload;\n [DiscordEventTypes.ENTITY_JOINED]: DiscordUserJoinedPayload;\n [DiscordEventTypes.ENTITY_LEFT]: DiscordUserLeftPayload;\n [DiscordEventTypes.VOICE_STATE_CHANGED]: DiscordVoiceStateChangedPayload;\n}\n\n/**\n * Interface representing a Discord service.\n *\n * @typedef {Object} IDiscordService\n * @property {DiscordJsClient} client - The Discord client object.\n * @property {Character} character - The character object.\n */\nexport interface IDiscordService {\n // Allow client to be null to handle initialization failures\n client: DiscordJsClient | null;\n character: Character;\n}\n\nexport const DISCORD_SERVICE_NAME = \"discord\";\n\nexport const ServiceType = {\n DISCORD: \"discord\",\n} as const;\n\nexport interface DiscordComponentOptions {\n type: number;\n custom_id: string;\n label?: string;\n style?: number;\n placeholder?: string;\n min_values?: number;\n max_values?: number;\n options?: Array<{\n label: string;\n value: string;\n description?: string;\n }>;\n}\n\nexport interface DiscordActionRow {\n type: 1;\n components: DiscordComponentOptions[];\n}\n","// src/actions/leaveVoice\nimport {\n type Action,\n type ActionExample,\n ChannelType,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n type State,\n createUniqueUuid,\n logger,\n} from \"@elizaos/core\";\nimport { BaseGuildVoiceChannel } from \"discord.js\";\n\nimport type { DiscordService } from \"../service\";\nimport { ServiceType } from \"../types\";\nimport type { VoiceManager } from \"../voice\";\n\nexport const leaveVoice: Action = {\n name: \"LEAVE_VOICE\",\n similes: [\n \"LEAVE_VOICE\",\n \"LEAVE_VC\",\n \"LEAVE_VOICE_CHAT\",\n \"LEAVE_VOICE_CHANNEL\",\n \"LEAVE_MEETING\",\n \"LEAVE_CALL\",\n ],\n validate: async (runtime: IAgentRuntime, message: Memory, state: State) => {\n if (message.content.source !== \"discord\") {\n // not a discord message\n return false;\n }\n\n const service = runtime.getService(ServiceType.DISCORD) as DiscordService;\n\n if (!service) {\n logger.error(\"Discord client not found\");\n return false;\n }\n\n const room = state.data.room ?? (await runtime.getRoom(message.roomId));\n\n if (\n room?.type !== ChannelType.GROUP &&\n room?.type !== ChannelType.VOICE_GROUP\n ) {\n return false;\n }\n\n // Check if the client is connected to any voice channel\n const isConnectedToVoice = service.client.voice.adapters.size > 0;\n\n return isConnectedToVoice;\n },\n description: \"Leave the current voice channel.\",\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state: State,\n _options: any\n ): Promise<boolean> => {\n const room = await runtime.getRoom(message.roomId);\n if (!room) {\n throw new Error(\"No room found\");\n }\n\n if (\n room?.type !== ChannelType.GROUP &&\n room?.type !== ChannelType.VOICE_GROUP\n ) {\n throw new Error(\"Not a group\");\n }\n\n const serverId = room.serverId;\n\n if (!serverId) {\n throw new Error(\"No server ID found 9\");\n }\n const discordClient = runtime.getService(\n ServiceType.DISCORD\n ) as DiscordService;\n const voiceManager = discordClient.voiceManager as VoiceManager;\n const client = discordClient.client;\n\n if (!client) {\n logger.error(\"Discord client not found\");\n throw new Error(\"Discord client not found\");\n }\n\n if (!voiceManager) {\n logger.error(\"voiceManager is not available.\");\n throw new Error(\"voiceManager is not available.\");\n }\n\n const guild = client.guilds.cache.get(serverId);\n\n if (!guild) {\n console.warn(\"Bot is not in any voice channel.\");\n // create a memory with thought to self to self\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: \"discord\",\n thought:\n \"I tried to leave the voice channel but I'm not in any voice channel.\",\n actions: [\"LEAVE_VOICE\"],\n },\n metadata: {\n type: \"LEAVE_VOICE\",\n },\n },\n \"messages\"\n );\n return false;\n }\n\n const voiceChannel = guild.members.me?.voice.channel;\n\n if (!voiceChannel || !(voiceChannel instanceof BaseGuildVoiceChannel)) {\n console.warn(\"Could not retrieve the voice channel.\");\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: \"discord\",\n thought:\n \"I tried to leave the voice channel but I couldn't find it.\",\n actions: [\"LEAVE_VOICE\"],\n },\n metadata: {\n type: \"LEAVE_VOICE\",\n },\n },\n \"messages\"\n );\n return false;\n }\n\n const connection = voiceManager.getVoiceConnection(guild.id);\n if (!connection) {\n console.warn(\"No active voice connection found for the bot.\");\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: \"discord\",\n thought:\n \"I tried to leave the voice channel but I couldn't find the connection.\",\n actions: [\"LEAVE_VOICE\"],\n },\n metadata: {\n type: \"LEAVE_VOICE\",\n },\n },\n \"messages\"\n );\n return false;\n }\n\n voiceManager.leaveChannel(voiceChannel);\n // save a memory for the new channel as well\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: createUniqueUuid(runtime, voiceChannel.id),\n content: {\n source: \"discord\",\n thought: `I left the voice channel ${voiceChannel.name}`,\n actions: [\"LEAVE_VOICE_STARTED\"],\n },\n metadata: {\n type: \"LEAVE_VOICE\",\n },\n },\n \"messages\"\n );\n\n return true;\n },\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Hey {{name2}} please leave the voice channel\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Sure\",\n actions: [\"LEAVE_VOICE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"I have to go now but thanks for the chat\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"You too, talk to you later\",\n actions: [\"LEAVE_VOICE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Great call everyone, hopping off now\",\n actions: [\"LEAVE_VOICE\"],\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Agreed, I'll hop off too\",\n actions: [\"LEAVE_VOICE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Hey {{name2}} I need you to step away from the voice chat for a bit\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"No worries, I'll leave the voice channel\",\n actions: [\"LEAVE_VOICE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"{{name2}}, I think we covered everything, you can leave the voice chat now\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Sounds good, see you both later\",\n actions: [\"LEAVE_VOICE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"leave voice {{name2}}\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"ok leaving\",\n actions: [\"LEAVE_VOICE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"plz leave the voice chat {{name2}}\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"aight im out\",\n actions: [\"LEAVE_VOICE\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"yo {{name2}} gtfo the vc\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"sorry, talk to you later\",\n actions: [\"LEAVE_VOICE\"],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default leaveVoice;\n","import type { IAgentRuntime, Memory, Provider, State } from \"@elizaos/core\";\nimport { ChannelType } from \"@elizaos/core\";\nimport type { DiscordService } from \"../service\";\nimport { ServiceType } from \"../types\";\n\n/**\n * Represents a provider for retrieving channel state information.\n * @type {Provider}\n * @property {string} name - The name of the channel state provider.\n * @property {Function} get - Asynchronous function that retrieves channel state information based on the provided runtime, message, and optional state parameters.\n * @param {IAgentRuntime} runtime - The agent runtime.\n * @param {Memory} message - The message object.\n * @param {State} [state] - Optional state object.\n * @returns {Promise<Object>} A promise that resolves to an object containing channel state data, values, and text.\n */\nexport const channelStateProvider: Provider = {\n name: \"channelState\",\n get: async (runtime: IAgentRuntime, message: Memory, state: State) => {\n const room = state.data?.room ?? (await runtime.getRoom(message.roomId));\n if (!room) {\n throw new Error(\"No room found\");\n }\n\n // if message source is not discord, return\n if (message.content.source !== \"discord\") {\n return {\n data: {},\n values: {},\n text: \"\",\n };\n }\n\n const agentName = state?.agentName || \"The agent\";\n const senderName = state?.senderName || \"someone\";\n\n let responseText = \"\";\n let channelType = \"\";\n let serverName = \"\";\n let channelId = \"\";\n const serverId = room.serverId;\n\n if (room.type === ChannelType.DM) {\n channelType = \"DM\";\n responseText = `${agentName} is currently in a direct message conversation with ${senderName}. ${agentName} should engage in conversation, should respond to messages that are addressed to them and only ignore messages that seem to not require a response.`;\n } else {\n channelType = \"GROUP\";\n\n if (!serverId) {\n console.error(\"No server ID found\");\n return {\n data: {\n room,\n channelType,\n },\n values: {\n channelType,\n },\n text: \"\",\n };\n }\n\n channelId = room.channelId;\n\n const discordService = runtime.getService(\n ServiceType.DISCORD\n ) as DiscordService;\n if (!discordService) {\n console.warn(\"No discord client found\");\n return {\n data: {\n room,\n channelType,\n serverId,\n },\n values: {\n channelType,\n serverId,\n },\n text: \"\",\n };\n }\n\n const guild = discordService.client?.guilds.cache.get(serverId);\n if (!guild) {\n console.warn(`Guild not found for serverId: ${serverId}`);\n return {\n data: {\n room,\n channelType,\n serverId,\n channelId,\n },\n values: {\n channelType,\n serverId,\n channelId,\n },\n text: \"\",\n };\n }\n serverName = guild.name;\n\n responseText = `${agentName} is currently having a conversation in the channel \\`@${channelId} in the server \\`${serverName}\\` (@${serverId})`;\n responseText += `\\n${agentName} is in a room with other users and should be self-conscious and only participate when directly addressed or when the conversation is relevant to them.`;\n }\n\n return {\n data: {\n room,\n channelType,\n serverId,\n serverName,\n channelId,\n },\n values: {\n channelType,\n serverName,\n channelId,\n },\n text: responseText,\n };\n },\n};\n\nexport default channelStateProvider;\n","import { getVoiceConnection } from \"@discordjs/voice\";\nimport type { IAgentRuntime, Memory, Provider, State } from \"@elizaos/core\";\nimport { ChannelType } from \"@elizaos/core\";\n\n/**\n * Provides information about the voice state of the user, including whether they are currently in a voice channel.\n *\n * @param {IAgentRuntime} runtime - The runtime object for the agent\n * @param {Memory} message - The message object containing room ID\n * @param {State} [state] - Optional state object for the user\n * @returns {Object} An object containing information about the voice state of the user\n */\nexport const voiceStateProvider: Provider = {\n name: \"voiceState\",\n get: async (runtime: IAgentRuntime, message: Memory, state?: State) => {\n // Voice doesn't get a discord message, so we need to use the channel for guild data\n const room = await runtime.getRoom(message.roomId);\n if (!room) {\n throw new Error(\"No room found\");\n }\n\n if (room.type !== ChannelType.GROUP) {\n // only handle in a group scenario for now\n return {\n data: {\n isInVoiceChannel: false,\n room,\n },\n values: {\n isInVoiceChannel: \"false\",\n roomType: room.type,\n },\n text: \"\",\n };\n }\n\n const serverId = room.serverId;\n\n if (!serverId) {\n throw new Error(\"No server ID found 10\");\n }\n\n const connection = getVoiceConnection(serverId);\n const agentName = state?.agentName || \"The agent\";\n\n if (!connection) {\n return {\n data: {\n isInVoiceChannel: false,\n room,\n serverId,\n },\n values: {\n isInVoiceChannel: \"false\",\n serverId,\n },\n text: `${agentName} is not currently in a voice channel`,\n };\n }\n\n const worldId = room.worldId;\n\n // get the world from the runtime.getWorld\n const world = await runtime.getWorld(worldId);\n\n if (!world) {\n throw new Error(\"No world found\");\n }\n\n const worldName = world.name;\n const roomType = room.type;\n const channelId = room.channelId;\n const channelName = room.name;\n\n if (!channelId) {\n return {\n data: {\n isInVoiceChannel: true,\n room,\n serverId,\n world,\n connection,\n },\n values: {\n isInVoiceChannel: \"true\",\n serverId,\n worldName,\n roomType,\n },\n text: `${agentName} is in an invalid voice channel`,\n };\n }\n\n return {\n data: {\n isInVoiceChannel: true,\n room,\n serverId,\n world,\n connection,\n channelId,\n channelName,\n },\n values: {\n isInVoiceChannel: \"true\",\n serverId,\n worldName,\n roomType,\n channelId,\n channelName,\n },\n text: `${agentName} is currently in the voice channel: ${channelName} (ID: ${channelId})`,\n };\n },\n};\n\nexport default voiceStateProvider;\n","import {\n ChannelType,\n type Character,\n type Content,\n type Entity,\n EventType,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n Role,\n Service,\n type TargetInfo,\n type UUID,\n type World,\n createUniqueUuid,\n logger,\n} from \"@elizaos/core\";\nimport {\n type Channel,\n ChannelType as DiscordChannelType,\n Client as DiscordJsClient,\n Events,\n GatewayIntentBits,\n type Guild,\n type GuildMember,\n type MessageReaction,\n type PartialMessageReaction,\n type PartialUser,\n Partials,\n PermissionsBitField,\n type TextChannel,\n type User,\n type Interaction,\n Collection,\n} from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"./constants\";\nimport { MessageManager } from \"./messages\";\nimport { DiscordEventTypes, type IDiscordService } from \"./types\";\nimport { VoiceManager } from \"./voice\";\n\n/**\n * DiscordService class representing a service for interacting with Discord.\n * @extends Service\n * @implements IDiscordService\n * @property {string} serviceType - The type of service, set to DISCORD_SERVICE_NAME.\n * @property {string} capabilityDescription - A description of the service's capabilities.\n * @property {DiscordJsClient} client - The DiscordJsClient used for communication.\n * @property {Character} character - The character associated with the service.\n * @property {MessageManager} messageManager - The manager for handling messages.\n * @property {VoiceManager} voiceManager - The manager for handling voice communication.\n */\n\nexport class DiscordService extends Service implements IDiscordService {\n static serviceType: string = DISCORD_SERVICE_NAME;\n capabilityDescription =\n \"The agent is able to send and receive messages on discord\";\n client: DiscordJsClient | null;\n character: Character;\n messageManager?: MessageManager;\n voiceManager?: VoiceManager;\n private userSelections: Map<string, { [key: string]: any }> = new Map();\n private timeouts: NodeJS.Timeout[] = [];\n /**\n * List of allowed channel IDs (parsed from CHANNEL_IDS env var).\n * If undefined, all channels are allowed.\n */\n private allowedChannelIds?: string[];\n\n /**\n * Constructor for Discord client.\n * Initializes the Discord client with specified intents and partials,\n * sets up event listeners, and ensures all servers exist.\n *\n * @param {IAgentRuntime} runtime - The AgentRuntime instance\n */\n constructor(runtime: IAgentRuntime) {\n super(runtime);\n\n this.character = runtime.character;\n\n // Parse CHANNEL_IDS env var to restrict the bot to specific channels\n const channelIdsRaw = runtime.getSetting(\"CHANNEL_IDS\") as\n | string\n | undefined;\n if (channelIdsRaw && channelIdsRaw.trim()) {\n this.allowedChannelIds = channelIdsRaw\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n }\n\n // Check if Discord API token is available and valid\n const token = runtime.getSetting(\"DISCORD_API_TOKEN\") as string;\n if (!token || token.trim() === \"\") {\n logger.warn(\n \"Discord API Token not provided - Discord functionality will be unavailable\"\n );\n this.client = null;\n return;\n }\n\n try {\n this.client = new DiscordJsClient({\n intents: [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMembers,\n GatewayIntentBits.GuildPresences,\n GatewayIntentBits.DirectMessages,\n GatewayIntentBits.GuildVoiceStates,\n GatewayIntentBits.MessageContent,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.DirectMessageTyping,\n GatewayIntentBits.GuildMessageTyping,\n GatewayIntentBits.GuildMessageReactions,\n ],\n partials: [\n Partials.Channel,\n Partials.Message,\n Partials.User,\n Partials.Reaction,\n ],\n });\n\n this.runtime = runtime;\n this.voiceManager = new VoiceManager(this, runtime);\n this.messageManager = new MessageManager(this);\n\n this.client.once(Events.ClientReady, this.onReady.bind(this));\n this.client.login(token).catch((error) => {\n logger.error(\n `Failed to login to Discord: ${error instanceof Error ? error.message : String(error)}`\n );\n this.client = null;\n });\n\n this.setupEventListeners();\n this.registerSendHandler(); // Register handler during construction\n } catch (error) {\n logger.error(\n `Error initializing Discord client: ${error instanceof Error ? error.message : String(error)}`\n );\n this.client = null;\n }\n }\n\n static async start(runtime: IAgentRuntime) {\n const service = new DiscordService(runtime);\n return service;\n }\n\n /**\n * Registers the send handler with the runtime.\n * @private\n */\n private registerSendHandler(): void {\n if (this.runtime) {\n this.runtime.registerSendHandler(\n \"discord\",\n this.handleSendMessage.bind(this)\n );\n }\n }\n\n /**\n * The SendHandlerFunction implementation for Discord.\n * @param {IAgentRuntime} runtime - The runtime instance.\n * @param {TargetInfo} target - The target information for the message.\n * @param {Content} content - The content of the message to send.\n * @returns {Promise<void>} A promise that resolves when the message is sent or rejects on error.\n * @throws {Error} If the client is not ready, target is invalid, or sending fails.\n */\n async handleSendMessage(\n runtime: IAgentRuntime,\n target: TargetInfo,\n content: Content\n ): Promise<void> {\n if (!this.client?.isReady()) {\n logger.error(\"[Discord SendHandler] Client not ready.\");\n throw new Error(\"Discord client is not ready.\");\n }\n\n // Skip sending if channel restrictions are set and target channel is not allowed\n if (\n target.channelId &&\n this.allowedChannelIds &&\n !this.allowedChannelIds.includes(target.channelId)\n ) {\n logger.warn(\n `[Discord SendHandler] Channel ${target.channelId} is not in allowed channels, skipping send.`\n );\n return;\n }\n\n let targetChannel: Channel | undefined | null = null;\n\n try {\n // Determine target based on provided info\n if (target.channelId) {\n targetChannel = await this.client.channels.fetch(target.channelId);\n } else if (target.entityId) {\n // Attempt to convert runtime UUID to Discord snowflake ID\n // NOTE: This assumes a mapping exists or the UUID *is* the snowflake ID\n const discordUserId = target.entityId as string; // May need more robust conversion\n const user = await this.client.users.fetch(discordUserId);\n if (user) {\n targetChannel = (await user.dmChannel) ?? (await user.createDM());\n }\n } else {\n throw new Error(\"Discord SendHandler requires channelId or entityId.\");\n }\n\n if (!targetChannel) {\n throw new Error(\n `Could not find target Discord channel/DM for target: ${JSON.stringify(target)}`\n );\n }\n\n // Type guard to ensure the channel is text-based\n if (targetChannel.isTextBased() && !targetChannel.isVoiceBased()) {\n // Further check if it's a channel where bots can send messages\n if (\n \"send\" in targetChannel &&\n typeof targetChannel.send === \"function\"\n ) {\n if (content.text) {\n // Split message if longer than Discord limit (2000 chars)\n const chunks = this.splitMessage(content.text, 2000);\n for (const chunk of chunks) {\n await targetChannel.send(chunk);\n }\n } else {\n logger.warn(\n \"[Discord SendHandler] No text content provided to send.\"\n );\n }\n // TODO: Add attachment handling here if necessary\n } else {\n throw new Error(\n `Target channel ${targetChannel.id} does not have a send method.`\n );\n }\n } else {\n throw new Error(\n `Target channel ${targetChannel.id} is not a valid text-based channel for sending messages.`\n );\n }\n } catch (error) {\n logger.error(\n `[Discord SendHandler] Error sending message: ${error instanceof Error ? error.message : String(error)}`,\n {\n target,\n content,\n }\n );\n throw error;\n }\n }\n\n /**\n * Helper function to split a string into chunks of a maximum length.\n *\n * @param {string} text - The text to split.\n * @param {number} maxLength - The maximum length of each chunk.\n * @returns {string[]} An array of text chunks.\n * @private\n */\n // Helper to split messages\n private splitMessage(text: string, maxLength: number): string[] {\n const chunks: string[] = [];\n let currentChunk = \"\";\n const lines = text.split(\"\\n\");\n for (const line of lines) {\n if (currentChunk.length + line.length + 1 <= maxLength) {\n currentChunk += (currentChunk ? \"\\n\" : \"\") + line;\n } else {\n if (currentChunk) chunks.push(currentChunk);\n // Handle lines longer than the max length (split them)\n if (line.length > maxLength) {\n for (let i = 0; i < line.length; i += maxLength) {\n chunks.push(line.substring(i, i + maxLength));\n }\n currentChunk = \"\"; // Reset chunk after splitting long line\n } else {\n currentChunk = line;\n }\n }\n }\n if (currentChunk) chunks.push(currentChunk);\n return chunks;\n }\n\n /**\n * Set up event listeners for the client.\n * @private\n */\n private setupEventListeners() {\n if (!this.client) {\n return; // Skip if client is not available\n }\n\n // Setup handling for direct messages\n this.client.on(\"messageCreate\", async (message) => {\n // Skip if we're sending the message or in deleted state\n if (\n message.author.id === this.client?.user?.id ||\n (message.author.bot &&\n this.runtime.character.settings?.discord?.shouldIgnoreBotMessages)\n ) {\n logger.info(\n `Got message where author is ${\n message.author.bot &&\n this.runtime.character.settings?.discord?.shouldIgnoreBotMessages\n ? \"a bot. To reply anyway, set \\`shouldIgnoreBotMessages=true\\`.\"\n : \"the current user. Ignore!\"\n }`\n );\n return;\n }\n\n // Skip if channel restrictions are set and this channel is not allowed\n if (\n this.allowedChannelIds &&\n !this.allowedChannelIds.includes(message.channel.id)\n ) {\n // check first whether the channe is a thread...\n const channel = await this.client?.channels.fetch(message.channel.id);\n\n if (!channel) {\n logger.error(`Channel id ${message.channel.id} not found. Ignore!`);\n return;\n }\n if (channel.isThread()) {\n if (\n !channel.parentId ||\n !this.allowedChannelIds.includes(channel.parentId)\n ) {\n logger.info(\n `Thread not in an allowed channel. Add the channel ${channel.parentId} to CHANNEL_IDS to enable replies.`\n );\n return;\n }\n } else {\n logger.info(\n `Channel not allowed. Add the channel ${message.channel.id} to CHANNEL_IDS to enable replies.`\n );\n return;\n }\n }\n\n try {\n // Ensure messageManager exists\n this.messageManager?.handleMessage(message);\n } catch (error) {\n logger.error(`Error handling message: ${error}`);\n }\n });\n\n // Setup handling for reactions\n this.client.on(\"messageReactionAdd\", async (reaction, user) => {\n if (user.id === this.client?.user?.id) {\n return;\n }\n // Skip if channel restrictions are set and this reaction is not in an allowed channel\n if (\n this.allowedChannelIds &&\n reaction.message.channel &&\n !this.allowedChannelIds.includes(reaction.message.channel.id)\n ) {\n return;\n }\n try {\n await this.handleReactionAdd(reaction, user);\n } catch (error) {\n logger.error(`Error handling reaction add: ${error}`);\n }\n });\n\n // Handle reaction removal\n this.client.on(\"messageReactionRemove\", async (reaction, user) => {\n if (user.id === this.client?.user?.id) {\n return;\n }\n // Skip if channel restrictions are set and this reaction is not in an allowed channel\n if (\n this.allowedChannelIds &&\n reaction.message.channel &&\n !this.allowedChannelIds.includes(reaction.message.channel.id)\n ) {\n return;\n }\n try {\n await this.handleReactionRemove(reaction, user);\n } catch (error) {\n logger.error(`Error handling reaction remove: ${error}`);\n }\n });\n\n // Setup guild (server) event handlers\n this.client.on(\"guildCreate\", async (guild) => {\n try {\n await this.handleGuildCreate(guild);\n } catch (error) {\n logger.error(`Error handling guild create: ${error}`);\n }\n });\n\n // Setup member (user) joining handlers\n this.client.on(\"guildMemberAdd\", async (member) => {\n try {\n await this.handleGuildMemberAdd(member);\n } catch (error) {\n logger.error(`Error handling guild member add: ${error}`);\n }\n });\n\n // Interaction handlers\n this.client.on(\"interactionCreate\", async (interaction) => {\n // Skip if channel restrictions are set and this interaction is not in an allowed channel\n if (\n this.allowedChannelIds &&\n interaction.channelId &&\n !this.allowedChannelIds.includes(interaction.channelId)\n ) {\n return;\n }\n try {\n await this.handleInteractionCreate(interaction);\n } catch (error) {\n logger.error(`Error handling interaction: ${error}`);\n }\n });\n\n this.client.on(\n \"userStream\",\n (entityId, name, userName, channel, opusDecoder) => {\n if (entityId !== this.client?.user?.id) {\n // Ensure voiceManager exists\n this.voiceManager?.handleUserStream(\n entityId,\n name,\n userName,\n channel,\n opusDecoder\n );\n }\n }\n );\n }\n\n /**\n * Handles the event when a new member joins a guild.\n *\n * @param {GuildMember} member - The GuildMember object representing the new member that joined the guild.\n * @returns {Promise<void>} - A Promise that resolves once the event handling is complete.\n * @private\n */\n private async handleGuildMemberAdd(member: GuildMember) {\n logger.log(`New member joined: ${member.user.username}`);\n\n const guild = member.guild;\n\n const tag = member.user.bot\n ? `${member.user.username}#${member.user.discriminator}`\n : member.user.username;\n\n const worldId = createUniqueUuid(this.runtime, guild.id);\n const entityId = createUniqueUuid(this.runtime, member.id);\n\n // Emit standardized ENTITY_JOINED event\n this.runtime.emitEvent([EventType.ENTITY_JOINED], {\n runtime: this.runtime,\n entityId,\n worldId,\n source: \"discord\",\n metadata: {\n originalId: member.id,\n username: tag,\n displayName: member.displayName || member.user.username,\n roles: member.roles.cache.map((r) => r.name),\n joinedAt: member.joinedAt?.getTime(),\n },\n });\n\n // Emit Discord-specific event\n this.runtime.emitEvent([DiscordEventTypes.ENTITY_JOINED], {\n runtime: this.runtime,\n entityId,\n worldId,\n member,\n guild,\n });\n }\n\n /**\n * Handles the event when the bot joins a guild. It logs the guild name, fetches additional information about the guild, scans the guild for voice data, creates standardized world data structure, generates unique IDs, and emits events to the runtime.\n * @param {Guild} guild - The guild that the bot has joined.\n * @returns {Promise<void>} A promise that resolves when the guild creation is handled.\n * @private\n */\n private async handleGuildCreate(guild: Guild) {\n logger.log(`Joined guild ${guild.name}`);\n const fullGuild = await guild.fetch();\n this.voiceManager?.scanGuild(guild);\n\n const ownerId = createUniqueUuid(this.runtime, fullGuild.ownerId);\n\n // Create standardized world data structure\n const worldId = createUniqueUuid(this.runtime, fullGuild.id);\n const standardizedData = {\n runtime: this.runtime,\n rooms: await this.buildStandardizedRooms(fullGuild, worldId),\n users: await this.buildStandardizedUsers(fullGuild),\n world: {\n id: worldId,\n name: fullGuild.name,\n agentId: this.runtime.agentId,\n serverId: fullGuild.id,\n metadata: {\n ownership: fullGuild.ownerId ? { ownerId: ownerId } : undefined,\n roles: {\n [ownerId]: Role.OWNER,\n },\n },\n } as World,\n source: \"discord\",\n };\n\n // Emit both Discord-specific and standardized events with the same data structure\n this.runtime.emitEvent([DiscordEventTypes.WORLD_JOINED], {\n runtime: this.runtime,\n server: fullGuild,\n source: \"discord\",\n });\n\n // Emit standardized event with the same structure as WORLD_CONNECTED\n this.runtime.emitEvent([EventType.WORLD_JOINED], standardizedData);\n }\n\n /**\n * Handles interactions created by the user, specifically commands and message components.\n * @param {Interaction} interaction - The interaction object received.\n * @returns {Promise<void>} A promise that resolves when the interaction is handled.\n * @private\n */\n private async handleInteractionCreate(interaction: Interaction) {\n if (interaction.isCommand()) {\n switch (interaction.commandName) {\n case \"joinchannel\":\n // Ensure voiceManager exists\n await this.voiceManager?.handleJoinChannelCommand(interaction);\n break;\n case \"leavechannel\":\n // Ensure voiceManager exists\n await this.voiceManager?.handleLeaveChannelCommand(interaction);\n break;\n }\n }\n\n // Handle message component interactions (buttons, dropdowns, etc.)\n if (interaction.isMessageComponent()) {\n logger.info(`Received component interaction: ${interaction.customId}`);\n const userId = interaction.user?.id;\n const messageId = interaction.message?.id;\n\n // Initialize user's selections if not exists\n if (!this.userSelections.has(userId)) {\n this.userSelections.set(userId, {});\n }\n const userSelections = this.userSelections.get(userId);\n if (!userSelections) {\n logger.error(\n `User selections map unexpectedly missing for user ${userId}`\n );\n return; // Should not happen\n }\n\n try {\n // For select menus (type 3), store the values\n if (interaction.isStringSelectMenu()) {\n logger.info(`Values selected: ${JSON.stringify(interaction.values)}`);\n logger.info(\n `User ${userId} selected values for ${interaction.customId}: ${JSON.stringify(interaction.values)}`\n );\n\n // Store values with messageId to scope them to this specific form\n userSelections[messageId] = {\n ...userSelections[messageId],\n [interaction.customId]: interaction.values,\n };\n // No need to call set again, modification is in place\n\n // Log the current state of all selections for this message\n logger.info(\n `Current selections for message ${messageId}: ${JSON.stringify(userSelections[messageId])}`\n );\n\n // Acknowledge the selection\n await interaction.deferUpdate();\n // await interaction.followUp({\n // content: 'Selection saved!',\n // ephemeral: true,\n // });\n }\n\n // For button interactions (type 2), use stored values\n if (interaction.isButton()) {\n logger.info(\"Button interaction detected\");\n logger.info(\n `Button pressed by user ${userId}: ${interaction.customId}`\n );\n const formSelections = userSelections[messageId] || {};\n\n logger.info(\n `Form data being submitted: ${JSON.stringify(formSelections)}`\n );\n\n // Emit an event with the interaction data and stored selections\n this.runtime.emitEvent([\"DISCORD_INTERACTION\"], {\n interaction: {\n customId: interaction.customId,\n componentType: interaction.componentType,\n type: interaction.type,\n user: userId,\n messageId: messageId,\n selections: formSelections,\n },\n source: \"discord\",\n });\n\n // Clear selections for this form only\n delete userSelections[messageId];\n // No need to call set again\n logger.info(`Cleared selections for message ${messageId}`);\n\n // Acknowledge the button press\n await interaction.deferUpdate();\n await interaction.followUp({\n content: \"Form submitted successfully!\",\n ephemeral: true,\n });\n }\n } catch (error) {\n logger.error(`Error handling component interaction: ${error}`);\n try {\n await interaction.followUp({\n content: \"There was an error processing your interaction.\",\n ephemeral: true,\n });\n } catch (followUpError) {\n logger.error(`Error sending follow-up message: ${followUpError}`);\n }\n }\n }\n }\n\n /**\n * Builds a standardized list of rooms from Discord guild channels.\n *\n * @param {Guild} guild The guild to build rooms for.\n * @param {UUID} _worldId The ID of the world to associate with the rooms (currently unused in favor of direct channel to room mapping).\n * @returns {Promise<any[]>} An array of standardized room objects.\n * @private\n */\n private async buildStandardizedRooms(\n guild: Guild,\n _worldId: UUID\n ): Promise<any[]> {\n const rooms: any[] = [];\n\n for (const [channelId, channel] of guild.channels.cache) {\n // Only process text and voice channels\n if (\n channel.type === DiscordChannelType.GuildText ||\n channel.type === DiscordChannelType.GuildVoice\n ) {\n const roomId = createUniqueUuid(this.runtime, channelId);\n let channelType;\n\n switch (channel.type) {\n case DiscordChannelType.GuildText:\n channelType = ChannelType.GROUP;\n break;\n case DiscordChannelType.GuildVoice:\n channelType = ChannelType.VOICE_GROUP;\n break;\n default:\n channelType = ChannelType.GROUP;\n }\n\n // For text channels, we could potentially get member permissions\n // But for performance reasons, keep this light for large guilds\n let participants: UUID[] = [];\n\n if (\n guild.memberCount < 1000 &&\n channel.type === DiscordChannelType.GuildText\n ) {\n try {\n // Only attempt this for smaller guilds\n // Get members with read permissions for this channel\n participants = Array.from(guild.members.cache.values())\n .filter((member) =>\n channel\n .permissionsFor(member)\n ?.has(PermissionsBitField.Flags.ViewChannel)\n )\n .map((member) => createUniqueUuid(this.runtime, member.id));\n } catch (error) {\n logger.warn(\n `Failed to get participants for channel ${channel.name}:`,\n error\n );\n }\n }\n\n rooms.push({\n id: roomId,\n name: channel.name,\n type: channelType,\n channelId: channel.id,\n participants,\n });\n }\n }\n\n return rooms;\n }\n\n /**\n * Builds a standardized list of users (entities) from Discord guild members.\n * Implements different strategies based on guild size for performance.\n *\n * @param {Guild} guild - The guild from which to build the user list.\n * @returns {Promise<Entity[]>} A promise that resolves with an array of standardized entity objects.\n * @private\n */\n private async buildStandardizedUsers(guild: Guild): Promise<Entity[]> {\n const entities: Entity[] = [];\n const botId = this.client?.user?.id;\n\n // Strategy based on guild size\n if (guild.memberCount > 1000) {\n logger.info(\n `Using optimized user sync for large guild ${guild.name} (${guild.memberCount} members)`\n );\n\n // For large guilds, prioritize members already in cache + online members\n try {\n // Use cache first\n for (const [, member] of guild.members.cache) {\n const tag = member.user.bot\n ? `${member.user.username}#${member.user.discriminator}`\n : member.user.username;\n\n if (member.id !== botId) {\n entities.push({\n id: createUniqueUuid(this.runtime, member.id),\n names: Array.from(\n new Set(\n [\n member.user.username,\n member.displayName,\n member.user.globalName,\n ].filter(Boolean) as string[]\n )\n ),\n agentId: this.runtime.agentId,\n metadata: {\n default: {\n username: tag,\n name: member.displayName || member.user.username,\n },\n discord: member.user.globalName\n ? {\n username: tag,\n name: member.displayName || member.user.username,\n globalName: member.user.globalName,\n userId: member.id,\n }\n : {\n username: tag,\n name: member.displayName || member.user.username,\n userId: member.id,\n },\n },\n });\n }\n }\n\n // If cache has very few members, try to get online members\n if (entities.length < 100) {\n logger.info(`Adding online members for ${guild.name}`);\n // This is a more targeted fetch that is less likely to hit rate limits\n const onlineMembers = await guild.members.fetch({ limit: 100 });\n\n for (const [, member] of onlineMembers) {\n if (member.id !== botId) {\n const entityId = createUniqueUuid(this.runtime, member.id);\n // Avoid duplicates\n if (!entities.some((u) => u.id === entityId)) {\n const tag = member.user.bot\n ? `${member.user.username}#${member.user.discriminator}`\n : member.user.username;\n\n entities.push({\n id: entityId,\n names: Array.from(\n new Set(\n [\n member.user.username,\n member.displayName,\n member.user.globalName,\n ].filter(Boolean) as string[]\n )\n ),\n agentId: this.runtime.agentId,\n metadata: {\n default: {\n username: tag,\n name: member.displayName || member.user.username,\n },\n discord: member.user.globalName\n ? {\n username: tag,\n name: member.displayName || member.user.username,\n globalName: member.user.globalName,\n userId: member.id,\n }\n : {\n username: tag,\n name: member.displayName || member.user.username,\n userId: member.id,\n },\n },\n });\n }\n }\n }\n }\n } catch (error) {\n logger.error(`Error fetching members for ${guild.name}:`, error);\n }\n } else {\n // For smaller guilds, we can fetch all members\n try {\n let members = guild.members.cache;\n if (members.size === 0) {\n members = await guild.members.fetch();\n }\n\n for (const [, member] of members) {\n if (member.id !== botId) {\n const tag = member.user.bot\n ? `${member.user.username}#${member.user.discriminator}`\n : member.user.username;\n\n entities.push({\n id: createUniqueUuid(this.runtime, member.id),\n names: Array.from(\n new Set(\n [\n member.user.username,\n member.displayName,\n member.user.globalName,\n ].filter(Boolean) as string[]\n )\n ),\n agentId: this.runtime.agentId,\n metadata: {\n default: {\n username: tag,\n name: member.displayName || member.user.username,\n },\n discord: member.user.globalName\n ? {\n username: tag,\n name: member.displayName || member.user.username,\n globalName: member.user.globalName,\n userId: member.id,\n }\n : {\n username: tag,\n name: member.displayName || member.user.username,\n userId: member.id,\n },\n },\n });\n }\n }\n } catch (error) {\n logger.error(`Error fetching members for ${guild.name}:`, error);\n }\n }\n\n return entities;\n }\n\n /**\n * Handles tasks to be performed once the Discord client is fully ready and connected.\n * This includes fetching guilds, scanning for voice data, and emitting connection events.\n * @private\n * @returns {Promise<void>} A promise that resolves when all on-ready tasks are completed.\n */\n private async onReady() {\n logger.log(\"DISCORD ON READY\");\n const guilds = await this.client?.guilds.fetch();\n if (!guilds) {\n logger.warn(\"Could not fetch guilds, client might not be ready.\");\n return;\n }\n for (const [, guild] of guilds) {\n const fullGuild = await guild.fetch();\n await this.voiceManager?.scanGuild(fullGuild);\n\n // Send after a brief delay\n const timeoutId = setTimeout(async () => {\n // For each server the client is in, fire a connected event\n try {\n const fullGuild = await guild.fetch();\n logger.log(\"DISCORD SERVER CONNECTED\", fullGuild.name);\n\n // Emit Discord-specific event with full guild object\n this.runtime.emitEvent([DiscordEventTypes.WORLD_CONNECTED], {\n runtime: this.runtime,\n server: fullGuild,\n source: \"discord\",\n });\n\n // Create platform-agnostic world data structure with simplified structure\n const worldId = createUniqueUuid(this.runtime, fullGuild.id);\n const ownerId = createUniqueUuid(this.runtime, fullGuild.ownerId);\n\n const standardizedData = {\n name: fullGuild.name,\n runtime: this.runtime,\n rooms: await this.buildStandardizedRooms(fullGuild, worldId),\n entities: await this.buildStandardizedUsers(fullGuild),\n world: {\n id: worldId,\n name: fullGuild.name,\n agentId: this.runtime.agentId,\n serverId: fullGuild.id,\n metadata: {\n ownership: fullGuild.ownerId ? { ownerId } : undefined,\n roles: {\n [ownerId]: Role.OWNER,\n },\n },\n } as World,\n source: \"discord\",\n };\n\n // Emit standardized event\n this.runtime.emitEvent([EventType.WORLD_CONNECTED], standardizedData);\n } catch (error) {\n // Add error handling to prevent crashes if the client is already destroyed\n logger.error(\"Error during Discord world connection:\", error);\n }\n }, 1000);\n\n // Store the timeout reference to be able to cancel it when stopping\n this.timeouts.push(timeoutId);\n }\n\n this.client?.emit(\"voiceManagerReady\");\n }\n\n /**\n * Registers send handlers for the Discord service instance.\n * This allows the runtime to correctly dispatch messages to this service.\n * @param {IAgentRuntime} runtime - The agent runtime instance.\n * @param {DiscordService} serviceInstance - The instance of the DiscordService.\n * @static\n */\n static registerSendHandlers(\n runtime: IAgentRuntime,\n serviceInstance: DiscordService\n ) {\n if (serviceInstance) {\n runtime.registerSendHandler(\n \"discord\",\n serviceInstance.handleSendMessage.bind(serviceInstance)\n );\n logger.info(\"[Discord] Registered send handler.\");\n }\n }\n\n /**\n * Fetches all members who have access to a specific text channel.\n *\n * @param {string} channelId - The Discord ID of the text channel.\n * @param {boolean} [useCache=true] - Whether to prioritize cached data. Defaults to true.\n * @returns {Promise<Array<{id: string, username: string, displayName: string}>>} A promise that resolves with an array of channel member objects, each containing id, username, and displayName.\n */\n public async getTextChannelMembers(\n channelId: string,\n useCache: boolean = true\n ): Promise<Array<{ id: string; username: string; displayName: string }>> {\n logger.info(\n `Fetching members for text channel ${channelId}, useCache=${useCache}`\n );\n\n try {\n // Fetch the channel\n const channel = (await this.client?.channels.fetch(\n channelId\n )) as TextChannel;\n\n // Validate channel\n if (!channel) {\n logger.error(`Channel not found: ${channelId}`);\n return [];\n }\n\n if (channel.type !== DiscordChannelType.GuildText) {\n logger.error(`Channel ${channelId} is not a text channel`);\n return [];\n }\n\n const guild = channel.guild;\n if (!guild) {\n logger.error(`Channel ${channelId} is not in a guild`);\n return [];\n }\n\n // Determine strategy based on guild size and cache preference\n const useCacheOnly = useCache && guild.memberCount > 1000;\n let members: Collection<string, GuildMember>;\n\n if (useCacheOnly) {\n logger.info(\n `Using cached members for large guild ${guild.name} (${guild.memberCount} members)`\n );\n members = guild.members.cache;\n } else {\n // For smaller guilds or when cache is not preferred, fetch members\n try {\n if (useCache && guild.members.cache.size > 0) {\n logger.info(\n `Using cached members (${guild.members.cache.size} members)`\n );\n members = guild.members.cache;\n } else {\n logger.info(`Fetching members for guild ${guild.name}`);\n members = await guild.members.fetch();\n logger.info(`Fetched ${members.size} members`);\n }\n } catch (error) {\n logger.error(`Error fetching members: ${error}`);\n // Fallback to cache if fetch fails\n members = guild.members.cache;\n logger.info(`Fallback to cache with ${members.size} members`);\n }\n }\n\n // Filter members by permission to view the channel\n logger.info(`Filtering members for access to channel ${channel.name}`);\n // Explicitly type the array from values()\n const memberArray: GuildMember[] = Array.from(members.values());\n const channelMembers = memberArray\n .filter((member: GuildMember) => {\n // Skip bots except our own bot\n // Add null check for client and client.user\n if (member.user.bot && member.id !== this.client?.user?.id) {\n return false;\n }\n\n // Check if the member can view the channel\n return (\n channel\n .permissionsFor(member)\n ?.has(PermissionsBitField.Flags.ViewChannel) ?? false\n );\n })\n .map((member: GuildMember) => ({\n id: member.id,\n username: member.user.username,\n displayName: member.displayName || member.user.username,\n }));\n\n logger.info(\n `Found ${channelMembers.length} members with access to channel ${channel.name}`\n );\n return channelMembers;\n } catch (error) {\n logger.error(`Error fetching channel members: ${error}`);\n return [];\n }\n }\n\n /**\n * Placeholder for handling reaction addition.\n * @private\n */\n private async handleReactionAdd(\n reaction: MessageReaction | PartialMessageReaction,\n user: User | PartialUser\n ) {\n try {\n logger.log(\"Reaction added\");\n\n // Early returns\n if (!reaction || !user) {\n logger.warn(\"Invalid reaction or user\");\n return;\n }\n\n // Get emoji info\n let emoji = reaction.emoji.name;\n if (!emoji && reaction.emoji.id) {\n emoji = `<:${reaction.emoji.name}:${reaction.emoji.id}>`;\n }\n\n // Fetch full message if partial\n if (reaction.partial) {\n try {\n await reaction.fetch();\n } catch (error) {\n logger.error(\"Failed to fetch partial reaction:\", error);\n return;\n }\n }\n\n // Generate IDs with timestamp to ensure uniqueness\n const timestamp = Date.now();\n const roomId = createUniqueUuid(\n this.runtime,\n reaction.message.channel.id\n );\n const entityId = createUniqueUuid(this.runtime, user.id);\n const reactionUUID = createUniqueUuid(\n this.runtime,\n `${reaction.message.id}-${user.id}-${emoji}-${timestamp}`\n );\n\n // Validate IDs\n if (!entityId || !roomId) {\n logger.error(\"Invalid user ID or room ID\", {\n entityId,\n roomId,\n });\n return;\n }\n\n // Process message content\n const messageContent = reaction.message.content || \"\";\n const truncatedContent =\n messageContent.length > 50\n ? `${messageContent.substring(0, 50)}...`\n : messageContent;\n const reactionMessage = `*Added <${emoji}> to: \\\\\"${truncatedContent}\\\\\"*`; // Escaped quotes\n\n // Get user info\n const userName = reaction.message.author?.username || \"unknown\";\n const name = reaction.message.author?.displayName || userName;\n\n await this.runtime.ensureConnection({\n entityId,\n roomId,\n userName,\n worldId: createUniqueUuid(\n this.runtime,\n reaction.message.guild?.id ?? roomId\n ) as UUID,\n worldName: reaction.message.guild?.name,\n name: name,\n source: \"discord\",\n channelId: reaction.message.channel.id,\n serverId: reaction.message.guild?.id,\n type: await this.getChannelType(reaction.message.channel as Channel),\n });\n\n const inReplyTo = createUniqueUuid(this.runtime, reaction.message.id);\n\n const memory: Memory = {\n id: reactionUUID,\n entityId,\n agentId: this.runtime.agentId,\n content: {\n // name,\n // userName,\n text: reactionMessage,\n source: \"discord\",\n inReplyTo,\n channelType: await this.getChannelType(\n reaction.message.channel as Channel\n ),\n },\n roomId,\n createdAt: timestamp,\n };\n\n const callback: HandlerCallback = async (content): Promise<Memory[]> => {\n if (!reaction.message.channel) {\n logger.error(\"No channel found for reaction message\");\n return [];\n }\n await (reaction.message.channel as TextChannel).send(\n content.text ?? \"\"\n );\n return [];\n };\n\n this.runtime.emitEvent(\n [\"DISCORD_REACTION_RECEIVED\", \"REACTION_RECEIVED\"],\n {\n runtime: this.runtime,\n message: memory,\n callback,\n }\n );\n } catch (error) {\n logger.error(\"Error handling reaction:\", error);\n }\n }\n\n /**\n * Placeholder for handling reaction removal.\n * @private\n */\n private async handleReactionRemove(\n reaction: MessageReaction | PartialMessageReaction,\n user: User | PartialUser\n ) {\n try {\n logger.log(\"Reaction removed\");\n\n let emoji = reaction.emoji.name;\n if (!emoji && reaction.emoji.id) {\n emoji = `<:${reaction.emoji.name}:${reaction.emoji.id}>`;\n }\n\n // Fetch the full message if it's a partial\n if (reaction.partial) {\n try {\n await reaction.fetch();\n } catch (error) {\n logger.error(\n \"Something went wrong when fetching the message:\",\n error\n );\n return;\n }\n }\n\n const messageContent = reaction.message.content || \"\";\n const truncatedContent =\n messageContent.length > 50\n ? `${messageContent.substring(0, 50)}...`\n : messageContent;\n\n const reactionMessage = `*Removed <${emoji}> from: \\\\\"${truncatedContent}\\\\\"*`; // Escaped quotes\n\n const roomId = createUniqueUuid(\n this.runtime,\n reaction.message.channel.id\n );\n\n const entityId = createUniqueUuid(this.runtime, user.id);\n const timestamp = Date.now();\n const reactionUUID = createUniqueUuid(\n this.runtime,\n `${reaction.message.id}-${user.id}-${emoji}-${timestamp}`\n );\n\n const userName = reaction.message.author?.username || \"unknown\";\n const name = reaction.message.author?.displayName || userName;\n\n await this.runtime.ensureConnection({\n entityId,\n roomId,\n userName,\n worldId: createUniqueUuid(\n this.runtime,\n reaction.message.guild?.id ?? roomId\n ) as UUID,\n worldName: reaction.message.guild?.name,\n name: name,\n source: \"discord\",\n channelId: reaction.message.channel.id,\n serverId: reaction.message.guild?.id,\n type: await this.getChannelType(reaction.message.channel as Channel),\n });\n\n const memory: Memory = {\n id: reactionUUID,\n entityId,\n agentId: this.runtime.agentId,\n content: {\n // name,\n // userName,\n text: reactionMessage,\n source: \"discord\",\n inReplyTo: createUniqueUuid(this.runtime, reaction.message.id),\n channelType: await this.getChannelType(\n reaction.message.channel as Channel\n ),\n },\n roomId,\n createdAt: Date.now(),\n };\n\n const callback: HandlerCallback = async (content): Promise<Memory[]> => {\n if (!reaction.message.channel) {\n logger.error(\"No channel found for reaction message\");\n return [];\n }\n await (reaction.message.channel as TextChannel).send(\n content.text ?? \"\"\n );\n return [];\n };\n\n this.runtime.emitEvent([DiscordEventTypes.REACTION_RECEIVED], {\n runtime: this.runtime,\n message: memory,\n callback,\n });\n } catch (error) {\n logger.error(\"Error handling reaction removal:\", error);\n }\n }\n\n /**\n * Stops the Discord service and cleans up resources.\n * Implements the abstract method from the Service class.\n */\n public async stop(): Promise<void> {\n logger.info(\"Stopping Discord service...\");\n this.timeouts.forEach(clearTimeout); // Clear any pending timeouts\n this.timeouts = [];\n if (this.client) {\n await this.client.destroy();\n this.client = null;\n logger.info(\"Discord client destroyed.\");\n }\n // Additional cleanup if needed (e.g., voice manager)\n if (this.voiceManager) {\n // Assuming voiceManager has a stop or cleanup method\n // await this.voiceManager.stop();\n }\n logger.info(\"Discord service stopped.\");\n }\n\n /**\n * Asynchronously retrieves the type of a given channel.\n *\n * @param {Channel} channel - The channel for which to determine the type.\n * @returns {Promise<ChannelType>} A Promise that resolves with the type of the channel.\n */\n async getChannelType(channel: Channel): Promise<ChannelType> {\n switch (channel.type) {\n case DiscordChannelType.DM:\n return ChannelType.DM;\n case DiscordChannelType.GuildText:\n return ChannelType.GROUP;\n case DiscordChannelType.GuildVoice:\n return ChannelType.VOICE_GROUP;\n default:\n // Fallback or handle other channel types as needed\n logger.warn(`Unhandled channel type: ${channel.type}`);\n return ChannelType.GROUP;\n }\n }\n}\n\nexport default DiscordService;\n","export const MESSAGE_CONSTANTS = {\n MAX_MESSAGES: 10,\n RECENT_MESSAGE_COUNT: 3,\n CHAT_HISTORY_COUNT: 5,\n INTEREST_DECAY_TIME: 5 * 60 * 1000, // 5 minutes\n PARTIAL_INTEREST_DECAY: 3 * 60 * 1000, // 3 minutes\n DEFAULT_SIMILARITY_THRESHOLD: 0.3,\n DEFAULT_SIMILARITY_THRESHOLD_FOLLOW_UPS: 0.2,\n} as const;\n\nexport const MESSAGE_LENGTH_THRESHOLDS = {\n LOSE_INTEREST: 100,\n SHORT_MESSAGE: 10,\n VERY_SHORT_MESSAGE: 2,\n IGNORE_RESPONSE: 4,\n} as const;\n\n/**\n * An array of words or phrases that indicate losing interest or annoyance.\n * @type {readonly [\"shut up\", \"stop\", \"please shut up\", \"shut up please\", \"dont talk\", \"silence\", \"stop talking\", \"be quiet\", \"hush\", \"wtf\", \"chill\", \"stfu\", \"stupid bot\", \"dumb bot\", \"stop responding\", \"god damn it\", \"god damn\", \"goddamnit\", \"can you not\", \"can you stop\", \"be quiet\", \"hate you\", \"hate this\", \"fuck up\"]}\n */\nexport const LOSE_INTEREST_WORDS = [\n \"shut up\",\n \"stop\",\n \"please shut up\",\n \"shut up please\",\n \"dont talk\",\n \"silence\",\n \"stop talking\",\n \"be quiet\",\n \"hush\",\n \"wtf\",\n \"chill\",\n \"stfu\",\n \"stupid bot\",\n \"dumb bot\",\n \"stop responding\",\n \"god damn it\",\n \"god damn\",\n \"goddamnit\",\n \"can you not\",\n \"can you stop\",\n \"be quiet\",\n \"hate you\",\n \"hate this\",\n \"fuck up\",\n] as const;\n\nexport const IGNORE_RESPONSE_WORDS = [\n \"lol\",\n \"nm\",\n \"uh\",\n \"wtf\",\n \"stfu\",\n \"dumb\",\n \"jfc\",\n \"omg\",\n] as const;\n\nexport const DISCORD_SERVICE_NAME = \"discord\";\n","import {\n ChannelType,\n type Content,\n EventType,\n type HandlerCallback,\n type IAgentRuntime,\n type Media,\n type Memory,\n ServiceType,\n type UUID,\n createUniqueUuid,\n logger,\n} from \"@elizaos/core\";\nimport {\n type Channel,\n type Client,\n ChannelType as DiscordChannelType,\n type Message as DiscordMessage,\n type TextChannel,\n} from \"discord.js\";\nimport { AttachmentManager } from \"./attachments\";\nimport { DiscordEventTypes } from \"./types\";\nimport { canSendMessage, sendMessageInChunks } from \"./utils\";\n\n/**\n * Class representing a Message Manager for handling Discord messages.\n */\n\nexport class MessageManager {\n private client: Client;\n private runtime: IAgentRuntime;\n private attachmentManager: AttachmentManager;\n private getChannelType: (channel: Channel) => Promise<ChannelType>;\n /**\n * Constructor for a new instance of MyClass.\n * @param {any} discordClient - The Discord client object.\n */\n constructor(discordClient: any) {\n this.client = discordClient.client;\n this.runtime = discordClient.runtime;\n this.attachmentManager = new AttachmentManager(this.runtime);\n this.getChannelType = discordClient.getChannelType;\n }\n\n /**\n * Handles incoming Discord messages and processes them accordingly.\n *\n * @param {DiscordMessage} message - The Discord message to be handled\n */\n async handleMessage(message: DiscordMessage) {\n if (\n this.runtime.character.settings?.discord?.allowedChannelIds &&\n !this.runtime.character.settings.discord.allowedChannelIds.some(\n (id: string) => id === message.channel.id\n )\n ) {\n return;\n }\n\n if (message.interaction || message.author.id === this.client.user?.id) {\n return;\n }\n\n if (\n this.runtime.character.settings?.discord?.shouldIgnoreBotMessages &&\n message.author?.bot\n ) {\n return;\n }\n\n if (\n this.runtime.character.settings?.discord?.shouldIgnoreDirectMessages &&\n message.channel.type === DiscordChannelType.DM\n ) {\n return;\n }\n\n if (\n this.runtime.character.settings?.discord?.shouldRespondOnlyToMentions &&\n (!this.client.user?.id ||\n !message.mentions.users?.has(this.client.user.id))\n ) {\n return;\n }\n\n const entityId = createUniqueUuid(this.runtime, message.author.id);\n\n const userName = message.author.bot\n ? `${message.author.username}#${message.author.discriminator}`\n : message.author.username;\n const name = message.author.displayName;\n const channelId = message.channel.id;\n const roomId = createUniqueUuid(this.runtime, channelId);\n\n // can't be null\n let type: ChannelType;\n let serverId: string | undefined;\n\n if (message.guild) {\n const guild = await message.guild.fetch();\n type = await this.getChannelType(message.channel as Channel);\n if (type === null) {\n // usually a forum type post\n logger.warn(\"null channel type, discord message\", message);\n }\n serverId = guild.id;\n } else {\n type = ChannelType.DM;\n serverId = undefined;\n }\n\n await this.runtime.ensureConnection({\n entityId,\n roomId,\n userName,\n name: name,\n source: \"discord\",\n channelId: message.channel.id,\n serverId,\n type,\n worldId: createUniqueUuid(this.runtime, serverId ?? roomId) as UUID,\n worldName: message.guild?.name,\n });\n\n try {\n const canSendResult = canSendMessage(message.channel);\n if (!canSendResult.canSend) {\n return logger.warn(\n `Cannot send message to channel ${message.channel}`,\n canSendResult\n );\n }\n\n const { processedContent, attachments } =\n await this.processMessage(message);\n\n const audioAttachments = message.attachments.filter((attachment) =>\n attachment.contentType?.startsWith(\"audio/\")\n );\n\n if (audioAttachments.size > 0) {\n const processedAudioAttachments =\n await this.attachmentManager.processAttachments(audioAttachments);\n attachments.push(...processedAudioAttachments);\n }\n\n if (!processedContent && !attachments?.length) {\n // Only process messages that are not empty\n return;\n }\n\n const entityId = createUniqueUuid(this.runtime, message.author.id);\n\n const messageId = createUniqueUuid(this.runtime, message.id);\n\n // Start typing indicator immediately when processing the message\n const channel = message.channel as TextChannel;\n\n // Start the typing indicator\n const startTyping = () => {\n try {\n // sendTyping is not available at test time\n if (channel.sendTyping) {\n channel.sendTyping();\n }\n } catch (err) {\n logger.warn(\"Error sending typing indicator:\", err);\n }\n };\n\n // Store the interval globally to be accessed by the callback\n const typingData = {\n interval: null as NodeJS.Timeout | null,\n cleared: false,\n };\n\n const newMessage: Memory = {\n id: messageId,\n entityId: entityId,\n agentId: this.runtime.agentId,\n roomId: roomId,\n content: {\n // name: name,\n // userName: userName,\n text: processedContent || \" \",\n attachments: attachments,\n source: \"discord\",\n channelType: type,\n url: message.url,\n inReplyTo: message.reference?.messageId\n ? createUniqueUuid(this.runtime, message.reference?.messageId)\n : undefined,\n },\n // metadata of memory\n metadata: {\n entityName: name,\n // include very technical/exact reference to this user for security reasons\n // don't remove or change this, spartan needs this\n fromId: message.author.id,\n // why message? all Memories contain content (which is basically a message)\n // what are the other types?\n type: \"message\",\n },\n createdAt: message.createdTimestamp,\n };\n\n const callback: HandlerCallback = async (\n content: Content,\n files: Array<{ attachment: Buffer | string; name: string }>\n ) => {\n try {\n // Initial typing indicator\n startTyping();\n\n // Create interval to keep the typing indicator active\n const typingInterval = setInterval(startTyping, 8000);\n\n typingData.interval = typingInterval;\n typingData.cleared = false;\n\n if (message.id && !content.inReplyTo) {\n content.inReplyTo = createUniqueUuid(this.runtime, message.id);\n }\n\n let messages = [];\n if (content?.source === \"DM\") {\n const u = await this.client.users.fetch(message.author.id);\n if (!u) {\n logger.warn(\"Discord - User not found\", message.author.id);\n return [];\n }\n u.send(content.text);\n messages = [content];\n } else {\n messages = await sendMessageInChunks(\n channel,\n content.text ?? \"\",\n message.id!,\n files\n );\n }\n\n const memories: Memory[] = [];\n for (const m of messages) {\n const actions = content.actions;\n\n const memory: Memory = {\n id: createUniqueUuid(this.runtime, m.id),\n entityId: this.runtime.agentId,\n agentId: this.runtime.agentId,\n content: {\n ...content,\n actions,\n inReplyTo: messageId,\n url: m.url,\n channelType: type,\n },\n roomId,\n createdAt: m.createdTimestamp,\n };\n memories.push(memory);\n }\n\n for (const m of memories) {\n await this.runtime.createMemory(m, \"messages\");\n }\n\n // Clear typing indicator\n if (typingData.interval && !typingData.cleared) {\n clearInterval(typingData.interval);\n typingData.cleared = true;\n }\n\n return memories;\n } catch (error) {\n console.error(\"Error handling message:\", error);\n if (typingData.interval && !typingData.cleared) {\n clearInterval(typingData.interval);\n typingData.cleared = true;\n }\n return [];\n }\n };\n\n this.runtime.emitEvent(\n [DiscordEventTypes.MESSAGE_RECEIVED, EventType.MESSAGE_RECEIVED],\n {\n runtime: this.runtime,\n message: newMessage,\n callback,\n }\n );\n\n setTimeout(() => {\n if (typingData.interval && !typingData.cleared) {\n clearInterval(typingData.interval);\n typingData.cleared = true;\n }\n }, 500);\n } catch (error) {\n console.error(\"Error handling message:\", error);\n }\n }\n\n /**\n * Processes the message content, mentions, code blocks, attachments, and URLs to generate\n * processed content and media attachments.\n *\n * @param {DiscordMessage} message The message to process\n * @returns {Promise<{ processedContent: string; attachments: Media[] }>} Processed content and media attachments\n */\n async processMessage(\n message: DiscordMessage\n ): Promise<{ processedContent: string; attachments: Media[] }> {\n let processedContent = message.content;\n let attachments: Media[] = [];\n\n const mentionRegex = /<@!?(\\d+)>/g;\n processedContent = processedContent.replace(\n mentionRegex,\n (match, entityId) => {\n const user = message.mentions.users.get(entityId);\n if (user) {\n return `${user.username} (@${entityId})`;\n }\n return match;\n }\n );\n\n const codeBlockRegex = /```([\\s\\S]*?)```/g;\n let match;\n while ((match = codeBlockRegex.exec(processedContent))) {\n const codeBlock = match[1];\n const lines = codeBlock.split(\"\\n\");\n const title = lines[0];\n const description = lines.slice(0, 3).join(\"\\n\");\n const attachmentId =\n `code-${Date.now()}-${Math.floor(Math.random() * 1000)}`.slice(-5);\n attachments.push({\n id: attachmentId,\n url: \"\",\n title: title || \"Code Block\",\n source: \"Code\",\n description: description,\n text: codeBlock,\n });\n processedContent = processedContent.replace(\n match[0],\n `Code Block (${attachmentId})`\n );\n }\n\n if (message.attachments.size > 0) {\n attachments = await this.attachmentManager.processAttachments(\n message.attachments\n );\n }\n\n const urlRegex = /(https?:\\/\\/[^\\s]+)/g;\n const urls = processedContent.match(urlRegex) || [];\n\n for (const url of urls) {\n // Use string literal type for getService, assume methods exist at runtime\n const videoService = this.runtime.getService(ServiceType.VIDEO) as any; // Cast to any\n if (videoService?.isVideoUrl(url)) {\n const videoInfo = await videoService.processVideo(url, this.runtime);\n\n attachments.push({\n id: `youtube-${Date.now()}`,\n url: url,\n title: videoInfo.title,\n source: \"YouTube\",\n description: videoInfo.description,\n text: videoInfo.text,\n });\n } else {\n // Use string literal type for getService, assume methods exist at runtime\n const browserService = this.runtime.getService(\n ServiceType.BROWSER\n ) as any; // Cast to any\n if (!browserService) {\n logger.warn(\"Browser service not found\");\n continue;\n }\n\n const { title, description: summary } =\n await browserService.getPageContent(url, this.runtime);\n\n attachments.push({\n id: `webpage-${Date.now()}`,\n url: url,\n title: title || \"Web Page\",\n source: \"Web\",\n description: summary,\n text: summary,\n });\n }\n }\n\n return { processedContent, attachments };\n }\n\n /**\n * Asynchronously fetches the bot's username and discriminator from Discord API.\n *\n * @param {string} botToken The token of the bot to authenticate the request\n * @returns {Promise<string>} A promise that resolves with the bot's username and discriminator\n * @throws {Error} If there is an error while fetching the bot details\n */\n\n async fetchBotName(botToken: string) {\n const url = \"https://discord.com/api/v10/users/@me\";\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: `Bot ${botToken}`,\n },\n });\n\n if (!response.ok) {\n throw new Error(`Error fetching bot details: ${response.statusText}`);\n }\n\n const data = await response.json();\n const discriminator = data.discriminator;\n return (\n (data as { username: string }).username +\n (discriminator ? `#${discriminator}` : \"\")\n );\n }\n}\n","import fs from \"node:fs\";\nimport { trimTokens } from \"@elizaos/core\";\nimport { parseJSONObjectFromText } from \"@elizaos/core\";\nimport {\n type IAgentRuntime,\n type Media,\n ModelType,\n ServiceType,\n} from \"@elizaos/core\";\nimport { type Attachment, Collection } from \"discord.js\";\nimport ffmpeg from \"fluent-ffmpeg\";\n\n/**\n * Generates a summary for the provided text using a specified model.\n *\n * @param {IAgentRuntime} runtime - The runtime environment for the agent.\n * @param {string} text - The text to generate a summary for.\n * @returns {Promise<{ title: string; description: string }>} An object containing the generated title and description.\n */\n\nasync function generateSummary(\n runtime: IAgentRuntime,\n text: string\n): Promise<{ title: string; description: string }> {\n // make sure text is under 128k characters\n text = await trimTokens(text, 100000, runtime);\n\n const prompt = `Please generate a concise summary for the following text:\n\n Text: \"\"\"\n ${text}\n \"\"\"\n\n Respond with a JSON object in the following format:\n \\`\\`\\`json\n {\n \"title\": \"Generated Title\",\n \"summary\": \"Generated summary and/or description of the text\"\n }\n \\`\\`\\``;\n\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response);\n\n if (parsedResponse?.title && parsedResponse?.summary) {\n return {\n title: parsedResponse.title,\n description: parsedResponse.summary,\n };\n }\n\n return {\n title: \"\",\n description: \"\",\n };\n}\n\n/**\n * Class representing an Attachment Manager.\n */\nexport class AttachmentManager {\n private attachmentCache: Map<string, Media> = new Map();\n private runtime: IAgentRuntime;\n\n /**\n * Constructor for creating a new instance of the class.\n *\n * @param {IAgentRuntime} runtime The runtime object to be injected into the instance.\n */\n constructor(runtime: IAgentRuntime) {\n this.runtime = runtime;\n }\n\n /**\n * Processes attachments and returns an array of Media objects.\n * @param {Collection<string, Attachment> | Attachment[]} attachments - The attachments to be processed\n * @returns {Promise<Media[]>} - An array of processed Media objects\n */\n async processAttachments(\n attachments: Collection<string, Attachment> | Attachment[]\n ): Promise<Media[]> {\n const processedAttachments: Media[] = [];\n const attachmentCollection =\n attachments instanceof Collection\n ? attachments\n : new Collection(attachments.map((att) => [att.id, att]));\n\n for (const [, attachment] of attachmentCollection) {\n const media = await this.processAttachment(attachment);\n if (media) {\n processedAttachments.push(media);\n }\n }\n\n return processedAttachments;\n }\n\n /**\n * Processes the provided attachment to generate a media object.\n * If the media for the attachment URL is already cached, it will return the cached media.\n * Otherwise, it will determine the type of attachment (PDF, text, audio, video, image, generic)\n * and call the corresponding processing method to generate the media object.\n *\n * @param attachment The attachment to process\n * @returns A promise that resolves to a Media object representing the attachment, or null if the attachment could not be processed\n */\n async processAttachment(attachment: Attachment): Promise<Media | null> {\n if (this.attachmentCache.has(attachment.url)) {\n return this.attachmentCache.get(attachment.url)!;\n }\n\n let media: Media | null = null;\n if (attachment.contentType?.startsWith(\"application/pdf\")) {\n media = await this.processPdfAttachment(attachment);\n } else if (attachment.contentType?.startsWith(\"text/plain\")) {\n media = await this.processPlaintextAttachment(attachment);\n } else if (\n attachment.contentType?.startsWith(\"audio/\") ||\n attachment.contentType?.startsWith(\"video/mp4\")\n ) {\n media = await this.processAudioVideoAttachment(attachment);\n } else if (attachment.contentType?.startsWith(\"image/\")) {\n media = await this.processImageAttachment(attachment);\n } else if (\n attachment.contentType?.startsWith(\"video/\") ||\n (this.runtime.getService(ServiceType.VIDEO) as any)?.isVideoUrl(\n attachment.url\n )\n ) {\n media = await this.processVideoAttachment(attachment);\n } else {\n media = await this.processGenericAttachment(attachment);\n }\n\n if (media) {\n this.attachmentCache.set(attachment.url, media);\n }\n return media;\n }\n\n /**\n * Asynchronously processes an audio or video attachment provided as input and returns a Media object.\n * @param {Attachment} attachment - The attachment object containing information about the audio/video file.\n * @returns {Promise<Media>} A Promise that resolves to a Media object representing the processed audio/video attachment.\n */\n private async processAudioVideoAttachment(\n attachment: Attachment\n ): Promise<Media> {\n try {\n const response = await fetch(attachment.url);\n const audioVideoArrayBuffer = await response.arrayBuffer();\n\n let audioBuffer: Buffer;\n if (attachment.contentType?.startsWith(\"audio/\")) {\n audioBuffer = Buffer.from(audioVideoArrayBuffer);\n } else if (attachment.contentType?.startsWith(\"video/mp4\")) {\n audioBuffer = await this.extractAudioFromMP4(audioVideoArrayBuffer);\n } else {\n throw new Error(\"Unsupported audio/video format\");\n }\n\n const transcription = await this.runtime.useModel(\n ModelType.TRANSCRIPTION,\n audioBuffer\n );\n const { title, description } = await generateSummary(\n this.runtime,\n transcription\n );\n\n return {\n id: attachment.id,\n url: attachment.url,\n title: title || \"Audio/Video Attachment\",\n source: attachment.contentType?.startsWith(\"audio/\")\n ? \"Audio\"\n : \"Video\",\n description:\n description ||\n \"User-uploaded audio/video attachment which has been transcribed\",\n text: transcription || \"Audio/video content not available\",\n };\n } catch (error) {\n if (error instanceof Error) {\n console.error(\n `Error processing audio/video attachment: ${error.message}`\n );\n }\n return {\n id: attachment.id,\n url: attachment.url,\n title: \"Audio/Video Attachment\",\n source: attachment.contentType?.startsWith(\"audio/\")\n ? \"Audio\"\n : \"Video\",\n description: \"An audio/video attachment (transcription failed)\",\n text: `This is an audio/video attachment. File name: ${attachment.name}, Size: ${attachment.size} bytes, Content type: ${attachment.contentType}`,\n };\n }\n }\n\n /**\n * Extracts the audio stream from the provided MP4 data and converts it to MP3 format.\n *\n * @param {ArrayBuffer} mp4Data - The MP4 data to extract audio from\n * @returns {Promise<Buffer>} - A Promise that resolves with the converted audio data as a Buffer\n */\n private async extractAudioFromMP4(mp4Data: ArrayBuffer): Promise<Buffer> {\n // Use a library like 'fluent-ffmpeg' or 'ffmpeg-static' to extract the audio stream from the MP4 data\n // and convert it to MP3 or WAV format\n // Example using fluent-ffmpeg:\n const tempMP4File = `temp_${Date.now()}.mp4`;\n const tempAudioFile = `temp_${Date.now()}.mp3`;\n\n try {\n // Write the MP4 data to a temporary file\n fs.writeFileSync(tempMP4File, Buffer.from(mp4Data));\n\n // Extract the audio stream and convert it to MP3\n await new Promise<void>((resolve, reject) => {\n ffmpeg(tempMP4File)\n .outputOptions(\"-vn\") // Disable video output\n .audioCodec(\"libmp3lame\") // Set audio codec to MP3\n .save(tempAudioFile) // Save the output to the specified file\n .on(\"end\", () => {\n resolve();\n })\n .on(\"error\", (err) => {\n reject(err);\n })\n .run();\n });\n\n // Read the converted audio file and return it as a Buffer\n const audioData = fs.readFileSync(tempAudioFile);\n return audioData;\n } finally {\n // Clean up the temporary files\n if (fs.existsSync(tempMP4File)) {\n fs.unlinkSync(tempMP4File);\n }\n if (fs.existsSync(tempAudioFile)) {\n fs.unlinkSync(tempAudioFile);\n }\n }\n }\n\n /**\n * Processes a PDF attachment by fetching the PDF file from the specified URL,\n * converting it to text, generating a summary, and returning a Media object\n * with the extracted information.\n * If an error occurs during processing, a placeholder Media object is returned\n * with an error message.\n *\n * @param {Attachment} attachment - The PDF attachment to process.\n * @returns {Promise<Media>} A promise that resolves to a Media object representing\n * the processed PDF attachment.\n */\n private async processPdfAttachment(attachment: Attachment): Promise<Media> {\n try {\n const response = await fetch(attachment.url);\n const pdfBuffer = await response.arrayBuffer();\n const pdfService = this.runtime.getService(ServiceType.PDF) as any;\n if (!pdfService) {\n throw new Error(\"PDF service not found\");\n }\n const text = await pdfService.convertPdfToText(Buffer.from(pdfBuffer));\n const { title, description } = await generateSummary(this.runtime, text);\n\n return {\n id: attachment.id,\n url: attachment.url,\n title: title || \"PDF Attachment\",\n source: \"PDF\",\n description: description || \"A PDF document\",\n text: text,\n };\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error processing PDF attachment: ${error.message}`);\n }\n return {\n id: attachment.id,\n url: attachment.url,\n title: \"PDF Attachment (conversion failed)\",\n source: \"PDF\",\n description: \"A PDF document that could not be converted to text\",\n text: `This is a PDF attachment. File name: ${attachment.name}, Size: ${attachment.size} bytes`,\n };\n }\n }\n\n /**\n * Processes a plaintext attachment by fetching its content, generating a summary, and returning a Media object.\n * @param {Attachment} attachment - The attachment object to process.\n * @returns {Promise<Media>} A promise that resolves to a Media object representing the processed plaintext attachment.\n */\n private async processPlaintextAttachment(\n attachment: Attachment\n ): Promise<Media> {\n try {\n const response = await fetch(attachment.url);\n const text = await response.text();\n const { title, description } = await generateSummary(this.runtime, text);\n\n return {\n id: attachment.id,\n url: attachment.url,\n title: title || \"Plaintext Attachment\",\n source: \"Plaintext\",\n description: description || \"A plaintext document\",\n text: text,\n };\n } catch (error) {\n if (error instanceof Error) {\n console.error(\n `Error processing plaintext attachment: ${error.message}`\n );\n } else {\n console.error(\n `An unknown error occurred during plaintext attachment processing`\n );\n }\n return {\n id: attachment.id,\n url: attachment.url,\n title: \"Plaintext Attachment (retrieval failed)\",\n source: \"Plaintext\",\n description: \"A plaintext document that could not be retrieved\",\n text: `This is a plaintext attachment. File name: ${attachment.name}, Size: ${attachment.size} bytes`,\n };\n }\n }\n\n /**\n * Process the image attachment by fetching description and title using the IMAGE_DESCRIPTION model.\n * If successful, returns a Media object populated with the details. If unsuccessful, creates a fallback\n * Media object and logs the error.\n *\n * @param {Attachment} attachment - The attachment object containing the image details.\n * @returns {Promise<Media>} A promise that resolves to a Media object.\n */\n private async processImageAttachment(attachment: Attachment): Promise<Media> {\n try {\n const { description, title } = await this.runtime.useModel(\n ModelType.IMAGE_DESCRIPTION,\n attachment.url\n );\n return {\n id: attachment.id,\n url: attachment.url,\n title: title || \"Image Attachment\",\n source: \"Image\",\n description: description || \"An image attachment\",\n text: description || \"Image content not available\",\n };\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error processing image attachment: ${error.message}`);\n }\n return this.createFallbackImageMedia(attachment);\n }\n }\n\n /**\n * Creates a fallback Media object for image attachments that could not be recognized.\n *\n * @param {Attachment} attachment - The attachment object containing image details.\n * @returns {Media} - The fallback Media object with basic information about the image attachment.\n */\n\n private createFallbackImageMedia(attachment: Attachment): Media {\n return {\n id: attachment.id,\n url: attachment.url,\n title: \"Image Attachment\",\n source: \"Image\",\n description: \"An image attachment (recognition failed)\",\n text: `This is an image attachment. File name: ${attachment.name}, Size: ${attachment.size} bytes, Content type: ${attachment.contentType}`,\n };\n }\n\n /**\n * Process a video attachment to extract video information.\n * @param {Attachment} attachment - The attachment object containing video information.\n * @returns {Promise<Media>} A promise that resolves to a Media object with video details.\n * @throws {Error} If video service is not available.\n */\n private async processVideoAttachment(attachment: Attachment): Promise<Media> {\n const videoService = this.runtime.getService(ServiceType.VIDEO) as any;\n\n if (!videoService) {\n return {\n id: attachment.id,\n url: attachment.url,\n title: \"Video Attachment (Service Unavailable)\",\n source: \"Video\",\n description:\n \"Could not process video attachment because the required service is not available.\",\n text: \"Video content not available\",\n };\n }\n\n if (\n typeof videoService.isVideoUrl === \"function\" &&\n videoService.isVideoUrl(attachment.url)\n ) {\n const videoInfo = await videoService.processVideo(\n attachment.url,\n this.runtime\n );\n return {\n id: attachment.id,\n url: attachment.url,\n title: videoInfo.title,\n source: \"YouTube\",\n description: videoInfo.description,\n text: videoInfo.text,\n };\n }\n return {\n id: attachment.id,\n url: attachment.url,\n title: \"Video Attachment\",\n source: \"Video\",\n description: \"A video attachment\",\n text: \"Video content not available\",\n };\n }\n\n /**\n * Process a generic attachment and return a Media object with specified properties.\n * @param {Attachment} attachment - The attachment object to process.\n * @returns {Promise<Media>} A Promise that resolves to a Media object with specified properties.\n */\n private async processGenericAttachment(\n attachment: Attachment\n ): Promise<Media> {\n return {\n id: attachment.id,\n url: attachment.url,\n title: \"Generic Attachment\",\n source: \"Generic\",\n description: \"A generic attachment\",\n text: \"Attachment content not available\",\n };\n }\n}\n","import {\n type IAgentRuntime,\n ModelType,\n logger,\n parseJSONObjectFromText,\n trimTokens,\n} from \"@elizaos/core\";\nimport {\n ChannelType,\n type Message as DiscordMessage,\n PermissionsBitField,\n type TextChannel,\n ThreadChannel,\n} from \"discord.js\";\n\nconst MAX_MESSAGE_LENGTH = 1900;\n\n/**\n * Generates a summary for a given text using a specified model.\n *\n * @param {IAgentRuntime} runtime - The IAgentRuntime instance.\n * @param {string} text - The text for which to generate a summary.\n * @returns {Promise<{ title: string; description: string }>} An object containing the generated title and summary.\n */\nexport async function generateSummary(\n runtime: IAgentRuntime,\n text: string\n): Promise<{ title: string; description: string }> {\n // make sure text is under 128k characters\n text = await trimTokens(text, 100000, runtime);\n\n const prompt = `Please generate a concise summary for the following text:\n\n Text: \"\"\"\n ${text}\n \"\"\"\n\n Respond with a JSON object in the following format:\n \\`\\`\\`json\n {\n \"title\": \"Generated Title\",\n \"summary\": \"Generated summary and/or description of the text\"\n }\n \\`\\`\\``;\n\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response);\n\n if (parsedResponse?.title && parsedResponse?.summary) {\n return {\n title: parsedResponse.title,\n description: parsedResponse.summary,\n };\n }\n\n return {\n title: \"\",\n description: \"\",\n };\n}\n\ninterface DiscordComponentOptions {\n type: number;\n custom_id: string;\n label?: string;\n style?: number;\n placeholder?: string;\n min_values?: number;\n max_values?: number;\n options?: Array<{\n label: string;\n value: string;\n description?: string;\n }>;\n}\n\ninterface DiscordActionRow {\n type: 1;\n components: DiscordComponentOptions[];\n}\n\n/**\n * Sends a message in chunks to a specified Discord TextChannel.\n * @param {TextChannel} channel - The Discord TextChannel to send the message to.\n * @param {string} content - The content of the message to be sent.\n * @param {string} _inReplyTo - The message ID to reply to (if applicable).\n * @param {any[]} files - Array of files to attach to the message.\n * @param {any[]} components - Optional components to add to the message (buttons, dropdowns, etc.).\n * @returns {Promise<DiscordMessage[]>} - Array of sent Discord messages.\n */\nexport async function sendMessageInChunks(\n channel: TextChannel,\n content: string,\n _inReplyTo: string,\n files: Array<{ attachment: Buffer | string; name: string }>,\n components?: any[]\n): Promise<DiscordMessage[]> {\n const sentMessages: DiscordMessage[] = [];\n const messages = splitMessage(content);\n try {\n for (let i = 0; i < messages.length; i++) {\n const message = messages[i];\n if (\n message.trim().length > 0 ||\n (i === messages.length - 1 && files && files.length > 0) ||\n components\n ) {\n const options: any = {\n content: message.trim(),\n };\n\n // if (i === 0 && inReplyTo) {\n // // Reply to the specified message for the first chunk\n // options.reply = {\n // messageReference: inReplyTo,\n // };\n // }\n\n if (i === messages.length - 1 && files && files.length > 0) {\n // Attach files to the last message chunk\n options.files = files;\n }\n\n // Add components to the last message or to a message with components only\n if (i === messages.length - 1 && components && components.length > 0) {\n try {\n // Safe JSON stringify that handles BigInt\n const safeStringify = (obj: any) => {\n return JSON.stringify(obj, (_, value) =>\n typeof value === \"bigint\" ? value.toString() : value\n );\n };\n\n logger.info(`Components received: ${safeStringify(components)}`);\n\n if (!Array.isArray(components)) {\n logger.warn(\n \"Components is not an array, skipping component processing\"\n );\n // Instead of continue, maybe return or handle differently?\n // For now, let's proceed assuming it might be an empty message with components\n } else if (\n components.length > 0 &&\n components[0] &&\n typeof components[0].toJSON === \"function\"\n ) {\n // If it looks like discord.js components, pass them directly\n options.components = components;\n } else {\n // Otherwise, build components from the assumed DiscordActionRow[] structure\n const {\n ActionRowBuilder,\n ButtonBuilder,\n StringSelectMenuBuilder,\n } = require(\"discord.js\");\n\n const discordComponents = (components as DiscordActionRow[]) // Cast here for building logic\n .map((row: DiscordActionRow) => {\n if (!row || typeof row !== \"object\" || row.type !== 1) {\n logger.warn(\"Invalid component row structure, skipping\");\n return null;\n }\n\n if (row.type === 1) {\n const actionRow = new ActionRowBuilder();\n\n if (!Array.isArray(row.components)) {\n logger.warn(\"Row components is not an array, skipping\");\n return null;\n }\n\n const validComponents = row.components\n .map((comp: DiscordComponentOptions) => {\n if (!comp || typeof comp !== \"object\") {\n logger.warn(\"Invalid component, skipping\");\n return null;\n }\n\n try {\n if (comp.type === 2) {\n return new ButtonBuilder()\n .setCustomId(comp.custom_id)\n .setLabel(comp.label || \"\")\n .setStyle(comp.style || 1);\n }\n\n if (comp.type === 3) {\n const selectMenu = new StringSelectMenuBuilder()\n .setCustomId(comp.custom_id)\n .setPlaceholder(\n comp.placeholder || \"Select an option\"\n );\n\n if (typeof comp.min_values === \"number\")\n selectMenu.setMinValues(comp.min_values);\n if (typeof comp.max_values === \"number\")\n selectMenu.setMaxValues(comp.max_values);\n\n if (Array.isArray(comp.options)) {\n selectMenu.addOptions(\n comp.options.map((option) => ({\n label: option.label,\n value: option.value,\n description: option.description,\n }))\n );\n }\n\n return selectMenu;\n }\n } catch (err) {\n logger.error(`Error creating component: ${err}`);\n return null;\n }\n return null;\n })\n .filter(Boolean);\n\n if (validComponents.length > 0) {\n actionRow.addComponents(validComponents);\n return actionRow;\n }\n }\n return null;\n })\n .filter(Boolean);\n\n if (discordComponents.length > 0) {\n options.components = discordComponents;\n }\n }\n } catch (error) {\n logger.error(`Error processing components: ${error}`);\n }\n }\n\n const m = await channel.send(options);\n sentMessages.push(m);\n }\n }\n } catch (error) {\n logger.error(`Error sending message: ${error}`);\n }\n\n return sentMessages;\n}\n\n/**\n * Splits the content into an array of strings based on the maximum message length.\n * @param {string} content - The content to split into messages\n * @returns {string[]} An array of strings that represent the split messages\n */\nfunction splitMessage(content: string): string[] {\n const messages: string[] = [];\n let currentMessage = \"\";\n\n const rawLines = content?.split(\"\\n\") || [];\n // split all lines into MAX_MESSAGE_LENGTH chunks so any long lines are split\n const lines = rawLines.flatMap((line) => {\n // Explicitly type chunks as string[]\n const chunks: string[] = [];\n while (line.length > MAX_MESSAGE_LENGTH) {\n chunks.push(line.slice(0, MAX_MESSAGE_LENGTH));\n line = line.slice(MAX_MESSAGE_LENGTH);\n }\n chunks.push(line);\n return chunks;\n });\n\n for (const line of lines) {\n if (currentMessage.length + line.length + 1 > MAX_MESSAGE_LENGTH) {\n messages.push(currentMessage.trim());\n currentMessage = \"\";\n }\n currentMessage += `${line}\\n`;\n }\n\n if (currentMessage.trim().length > 0) {\n messages.push(currentMessage.trim());\n }\n\n return messages;\n}\n\n/**\n * Checks if the bot can send messages in a given channel by checking permissions.\n * @param {TextChannel | NewsChannel | ThreadChannel} channel - The channel to check permissions for.\n * @returns {Object} Object containing information about whether the bot can send messages or not.\n * @returns {boolean} canSend - Whether the bot can send messages in the channel.\n * @returns {string} reason - The reason why the bot cannot send messages, if applicable.\n * @returns {string[]} missingPermissions - Array of missing permissions, if any.\n */\nexport function canSendMessage(channel) {\n // validate input\n if (!channel) {\n return {\n canSend: false,\n reason: \"No channel given\",\n };\n }\n // if it is a DM channel, we can always send messages\n if (channel.type === ChannelType.DM) {\n return {\n canSend: true,\n reason: null,\n };\n }\n const botMember = channel.guild?.members.cache.get(channel.client.user.id);\n\n if (!botMember) {\n return {\n canSend: false,\n reason: \"Not a guild channel or bot member not found\",\n };\n }\n\n // Required permissions for sending messages\n const requiredPermissions = [\n PermissionsBitField.Flags.ViewChannel,\n PermissionsBitField.Flags.SendMessages,\n PermissionsBitField.Flags.ReadMessageHistory,\n ];\n\n // Add thread-specific permission if it's a thread\n if (channel instanceof ThreadChannel) {\n requiredPermissions.push(PermissionsBitField.Flags.SendMessagesInThreads);\n }\n\n // Check permissions\n const permissions = channel.permissionsFor(botMember);\n\n if (!permissions) {\n return {\n canSend: false,\n reason: \"Could not retrieve permissions\",\n };\n }\n\n // Check each required permission\n const missingPermissions = requiredPermissions.filter(\n (perm) => !permissions.has(perm)\n );\n\n return {\n canSend: missingPermissions.length === 0,\n missingPermissions: missingPermissions,\n reason:\n missingPermissions.length > 0\n ? `Missing permissions: ${missingPermissions.map((p) => String(p)).join(\", \")}`\n : null,\n };\n}\n","import {\n type AudioPlayer,\n type AudioReceiveStream,\n NoSubscriberBehavior,\n StreamType,\n type VoiceConnection,\n VoiceConnectionStatus,\n createAudioPlayer,\n createAudioResource,\n entersState,\n getVoiceConnections,\n joinVoiceChannel,\n} from \"@discordjs/voice\";\nimport {\n ChannelType,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n ModelType,\n type UUID,\n createUniqueUuid,\n logger,\n} from \"@elizaos/core\";\nimport {\n type BaseGuildVoiceChannel,\n type Channel,\n type Client,\n ChannelType as DiscordChannelType,\n type Guild,\n type GuildMember,\n type VoiceChannel,\n type VoiceState,\n} from \"discord.js\";\nimport { EventEmitter } from \"node:events\";\nimport { type Readable, pipeline } from \"node:stream\";\nimport prism from \"prism-media\";\nimport type { DiscordService } from \"./service\";\n\n// These values are chosen for compatibility with picovoice components\nconst DECODE_FRAME_SIZE = 1024;\nconst DECODE_SAMPLE_RATE = 16000;\n\n/**\n * Creates an opus decoder with fallback handling for different opus libraries\n * @param options - Decoder options including channels, rate, and frameSize\n * @returns An opus decoder instance or null if creation fails\n */\nfunction createOpusDecoder(options: {\n channels: number;\n rate: number;\n frameSize: number;\n}) {\n try {\n // First try to create decoder with prism-media\n return new prism.opus.Decoder(options);\n } catch (error) {\n logger.warn(`Failed to create opus decoder with prism-media: ${error}`);\n\n // Log available opus libraries for debugging\n try {\n const { generateDependencyReport } = require(\"@discordjs/voice\");\n const report = generateDependencyReport();\n logger.debug(\"Voice dependency report:\", report);\n } catch (reportError) {\n logger.warn(\"Could not generate dependency report:\", reportError);\n }\n\n throw error;\n }\n}\n\n/**\n * Generates a WAV file header based on the provided audio parameters.\n * @param {number} audioLength - The length of the audio data in bytes.\n * @param {number} sampleRate - The sample rate of the audio.\n * @param {number} [channelCount=1] - The number of channels (default is 1).\n * @param {number} [bitsPerSample=16] - The number of bits per sample (default is 16).\n * @returns {Buffer} The WAV file header as a Buffer object.\n */\nfunction getWavHeader(\n audioLength: number,\n sampleRate: number,\n channelCount = 1,\n bitsPerSample = 16\n): Buffer {\n const wavHeader = Buffer.alloc(44);\n wavHeader.write(\"RIFF\", 0);\n wavHeader.writeUInt32LE(36 + audioLength, 4); // Length of entire file in bytes minus 8\n wavHeader.write(\"WAVE\", 8);\n wavHeader.write(\"fmt \", 12);\n wavHeader.writeUInt32LE(16, 16); // Length of format data\n wavHeader.writeUInt16LE(1, 20); // Type of format (1 is PCM)\n wavHeader.writeUInt16LE(channelCount, 22); // Number of channels\n wavHeader.writeUInt32LE(sampleRate, 24); // Sample rate\n wavHeader.writeUInt32LE((sampleRate * bitsPerSample * channelCount) / 8, 28); // Byte rate\n wavHeader.writeUInt16LE((bitsPerSample * channelCount) / 8, 32); // Block align ((BitsPerSample * Channels) / 8)\n wavHeader.writeUInt16LE(bitsPerSample, 34); // Bits per sample\n wavHeader.write(\"data\", 36); // Data chunk header\n wavHeader.writeUInt32LE(audioLength, 40); // Data chunk size\n return wavHeader;\n}\n\n/**\n * Class representing an AudioMonitor that listens for audio data from a Readable stream.\n */\nexport class AudioMonitor {\n private readable: Readable;\n private buffers: Buffer[] = [];\n private maxSize: number;\n private lastFlagged = -1;\n private ended = false;\n\n /**\n * Constructs an AudioMonitor instance.\n * @param {Readable} readable - The readable stream to monitor for audio data.\n * @param {number} maxSize - The maximum size of the audio buffer.\n * @param {function} onStart - The callback function to be called when audio starts.\n * @param {function} callback - The callback function to process audio data.\n */\n constructor(\n readable: Readable,\n maxSize: number,\n onStart: () => void,\n callback: (buffer: Buffer) => void\n ) {\n this.readable = readable;\n this.maxSize = maxSize;\n this.readable.on(\"data\", (chunk: Buffer) => {\n if (this.lastFlagged < 0) {\n this.lastFlagged = this.buffers.length;\n }\n this.buffers.push(chunk);\n const currentSize = this.buffers.reduce(\n (acc, cur) => acc + cur.length,\n 0\n );\n while (currentSize > this.maxSize) {\n this.buffers.shift();\n this.lastFlagged--;\n }\n });\n this.readable.on(\"end\", () => {\n logger.log(\"AudioMonitor ended\");\n this.ended = true;\n if (this.lastFlagged < 0) return;\n callback(this.getBufferFromStart());\n this.lastFlagged = -1;\n });\n this.readable.on(\"speakingStopped\", () => {\n if (this.ended) return;\n logger.log(\"Speaking stopped\");\n if (this.lastFlagged < 0) return;\n callback(this.getBufferFromStart());\n });\n this.readable.on(\"speakingStarted\", () => {\n if (this.ended) return;\n onStart();\n logger.log(\"Speaking started\");\n this.reset();\n });\n }\n\n /**\n * Stops listening to \"data\", \"end\", \"speakingStopped\", and \"speakingStarted\" events on the readable stream.\n */\n stop() {\n this.readable.removeAllListeners(\"data\");\n this.readable.removeAllListeners(\"end\");\n this.readable.removeAllListeners(\"speakingStopped\");\n this.readable.removeAllListeners(\"speakingStarted\");\n }\n\n /**\n * Check if the item is flagged.\n * @returns {boolean} True if the item was flagged, false otherwise.\n */\n isFlagged() {\n return this.lastFlagged >= 0;\n }\n\n /**\n * Returns a Buffer containing all buffers starting from the last flagged index.\n * If the last flagged index is less than 0, returns null.\n *\n * @returns {Buffer | null} The concatenated Buffer or null\n */\n getBufferFromFlag() {\n if (this.lastFlagged < 0) {\n return null;\n }\n const buffer = Buffer.concat(this.buffers.slice(this.lastFlagged));\n return buffer;\n }\n\n /**\n * Concatenates all buffers in the array and returns a single buffer.\n *\n * @returns {Buffer} The concatenated buffer from the start.\n */\n getBufferFromStart() {\n const buffer = Buffer.concat(this.buffers);\n return buffer;\n }\n\n /**\n * Resets the buffers array and sets lastFlagged to -1.\n */\n reset() {\n this.buffers = [];\n this.lastFlagged = -1;\n }\n\n /**\n * Check if the object has ended.\n * @returns {boolean} Returns true if the object has ended; false otherwise.\n */\n isEnded() {\n return this.ended;\n }\n}\n\n/**\n * Class representing a VoiceManager that extends EventEmitter.\n * @extends EventEmitter\n */\nexport class VoiceManager extends EventEmitter {\n private processingVoice = false;\n private transcriptionTimeout: NodeJS.Timeout | null = null;\n private userStates: Map<\n string,\n {\n buffers: Buffer[];\n totalLength: number;\n lastActive: number;\n transcriptionText: string;\n }\n > = new Map();\n private activeAudioPlayer: AudioPlayer | null = null;\n private client: Client | null;\n private runtime: IAgentRuntime;\n private streams: Map<string, Readable> = new Map();\n private connections: Map<string, VoiceConnection> = new Map();\n private activeMonitors: Map<\n string,\n { channel: BaseGuildVoiceChannel; monitor: AudioMonitor }\n > = new Map();\n private ready: boolean;\n\n /**\n * Constructor for initializing a new instance of the class.\n *\n * @param {DiscordService} service - The Discord service to use.\n * @param {IAgentRuntime} runtime - The runtime for the agent.\n */\n constructor(service: DiscordService, runtime: IAgentRuntime) {\n super();\n this.client = service.client;\n this.runtime = runtime;\n this.ready = false;\n\n if (this.client) {\n this.client.on(\"voiceManagerReady\", () => {\n this.setReady(true);\n });\n } else {\n logger.error(\n \"Discord client is not available in VoiceManager constructor for voiceManagerReady event\"\n );\n this.ready = false;\n }\n }\n\n /**\n * Asynchronously retrieves the type of the channel.\n * @param {Channel} channel - The channel to get the type for.\n * @returns {Promise<ChannelType>} The type of the channel.\n */\n async getChannelType(channel: Channel): Promise<ChannelType> {\n switch (channel.type) {\n case DiscordChannelType.GuildVoice:\n case DiscordChannelType.GuildStageVoice:\n return ChannelType.VOICE_GROUP;\n default:\n // This function should only be called with GuildVoice or GuildStageVoice channels\n // If it receives another type, it's an unexpected error.\n logger.error(\n `getChannelType received unexpected channel type: ${channel.type} for channel ${channel.id}`\n );\n throw new Error(`Unexpected channel type encountered: ${channel.type}`);\n }\n }\n\n /**\n * Set the ready status of the VoiceManager.\n * @param {boolean} status - The status to set.\n */\n private setReady(status: boolean) {\n this.ready = status;\n this.emit(\"ready\");\n logger.debug(`VoiceManager is now ready: ${this.ready}`);\n }\n\n /**\n * Check if the object is ready.\n *\n * @returns {boolean} True if the object is ready, false otherwise.\n */\n isReady() {\n return this.ready;\n }\n\n /**\n * Handle voice state update event.\n * @param {VoiceState} oldState - The old voice state of the member.\n * @param {VoiceState} newState - The new voice state of the member.\n * @returns {void}\n */\n async handleVoiceStateUpdate(oldState: VoiceState, newState: VoiceState) {\n const oldChannelId = oldState.channelId;\n const newChannelId = newState.channelId;\n const member = newState.member;\n if (!member) return;\n if (member.id === this.client?.user?.id) {\n return;\n }\n\n // Ignore mute/unmute events\n if (oldChannelId === newChannelId) {\n return;\n }\n\n // User leaving a channel where the bot is present\n if (oldChannelId && this.connections.has(oldChannelId)) {\n this.stopMonitoringMember(member.id);\n }\n\n // User joining a channel where the bot is present\n if (newChannelId && this.connections.has(newChannelId)) {\n await this.monitorMember(\n member,\n newState.channel as BaseGuildVoiceChannel\n );\n }\n }\n\n /**\n * Joins a voice channel and sets up the necessary connection and event listeners.\n * @param {BaseGuildVoiceChannel} channel - The voice channel to join\n */\n async joinChannel(channel: BaseGuildVoiceChannel) {\n const oldConnection = this.getVoiceConnection(channel.guildId as string);\n if (oldConnection) {\n try {\n oldConnection.destroy();\n // Remove all associated streams and monitors\n this.streams.clear();\n this.activeMonitors.clear();\n } catch (error) {\n console.error(\"Error leaving voice channel:\", error);\n }\n }\n\n const connection = joinVoiceChannel({\n channelId: channel.id,\n guildId: channel.guild.id,\n adapterCreator: channel.guild.voiceAdapterCreator as any,\n selfDeaf: false,\n selfMute: false,\n group: this.client?.user?.id ?? \"default-group\",\n });\n\n try {\n // Wait for either Ready or Signalling state\n await Promise.race([\n entersState(connection, VoiceConnectionStatus.Ready, 20_000),\n entersState(connection, VoiceConnectionStatus.Signalling, 20_000),\n ]);\n\n // Log connection success\n logger.log(\n `Voice connection established in state: ${connection.state.status}`\n );\n\n // Set up ongoing state change monitoring\n connection.on(\"stateChange\", async (oldState, newState) => {\n logger.log(\n `Voice connection state changed from ${oldState.status} to ${newState.status}`\n );\n\n if (newState.status === VoiceConnectionStatus.Disconnected) {\n logger.log(\"Handling disconnection...\");\n\n try {\n // Try to reconnect if disconnected\n await Promise.race([\n entersState(connection, VoiceConnectionStatus.Signalling, 5_000),\n entersState(connection, VoiceConnectionStatus.Connecting, 5_000),\n ]);\n // Seems to be reconnecting to a new channel\n logger.log(\"Reconnecting to channel...\");\n } catch (e) {\n // Seems to be a real disconnect, destroy and cleanup\n logger.log(`Disconnection confirmed - cleaning up...${e}`);\n connection.destroy();\n this.connections.delete(channel.id);\n }\n } else if (newState.status === VoiceConnectionStatus.Destroyed) {\n this.connections.delete(channel.id);\n } else if (\n !this.connections.has(channel.id) &&\n (newState.status === VoiceConnectionStatus.Ready ||\n newState.status === VoiceConnectionStatus.Signalling)\n ) {\n this.connections.set(channel.id, connection);\n }\n });\n\n connection.on(\"error\", (error) => {\n logger.log(\"Voice connection error:\", error);\n // Don't immediately destroy - let the state change handler deal with it\n logger.log(\"Connection error - will attempt to recover...\");\n });\n\n // Store the connection\n this.connections.set(channel.id, connection);\n\n // Continue with voice state modifications\n const me = channel.guild.members.me;\n if (me?.voice && me.permissions.has(\"DeafenMembers\")) {\n try {\n await me.voice.setDeaf(false);\n await me.voice.setMute(false);\n } catch (error) {\n logger.log(\"Failed to modify voice state:\", error);\n // Continue even if this fails\n }\n }\n\n connection.receiver.speaking.on(\"start\", async (entityId: string) => {\n let user = channel.members.get(entityId);\n if (!user) {\n try {\n user = await channel.guild.members.fetch(entityId);\n } catch (error) {\n console.error(\"Failed to fetch user:\", error);\n }\n }\n\n if (user && !user?.user.bot) {\n this.monitorMember(user as GuildMember, channel);\n this.streams.get(entityId)?.emit(\"speakingStarted\");\n }\n });\n\n connection.receiver.speaking.on(\"end\", async (entityId: string) => {\n const user = channel.members.get(entityId);\n if (!user?.user.bot) {\n this.streams.get(entityId)?.emit(\"speakingStopped\");\n }\n });\n } catch (error) {\n logger.log(\"Failed to establish voice connection:\", error);\n connection.destroy();\n this.connections.delete(channel.id);\n throw error;\n }\n }\n\n /**\n * Retrieves the voice connection for a given guild ID.\n * @param {string} guildId - The ID of the guild to get the voice connection for.\n * @returns {VoiceConnection | undefined} The voice connection for the specified guild ID, or undefined if not found.\n */\n getVoiceConnection(guildId: string) {\n const userId = this.client?.user?.id;\n if (!userId) {\n logger.error(\"Client user ID is not available.\");\n return undefined;\n }\n const connections = getVoiceConnections(userId);\n if (!connections) {\n return;\n }\n const connection = [...connections.values()].find(\n (connection) => connection.joinConfig.guildId === guildId\n );\n return connection;\n }\n\n /**\n * Monitor a member's audio stream for volume activity and speaking thresholds.\n *\n * @param {GuildMember} member - The member whose audio stream is being monitored.\n * @param {BaseGuildVoiceChannel} channel - The voice channel in which the member is connected.\n */\n private async monitorMember(\n member: GuildMember,\n channel: BaseGuildVoiceChannel\n ) {\n const entityId = member?.id;\n const userName = member?.user?.username;\n const name = member?.user?.displayName;\n const connection = this.getVoiceConnection(member?.guild?.id);\n\n const receiveStream = connection?.receiver.subscribe(entityId, {\n autoDestroy: true,\n emitClose: true,\n });\n if (!receiveStream || receiveStream.readableLength === 0) {\n logger.warn(\n `[monitorMember] No receiveStream or empty stream for user ${entityId}`\n );\n return;\n }\n\n let opusDecoder: any;\n try {\n // Try to create opus decoder with error handling for Node.js 23 compatibility\n opusDecoder = createOpusDecoder({\n channels: 1,\n rate: DECODE_SAMPLE_RATE,\n frameSize: DECODE_FRAME_SIZE,\n });\n } catch (error) {\n logger.error(\n `[monitorMember] Failed to create opus decoder for user ${entityId}: ${error}`\n );\n // For now, log the error and return early.\n // In production, you might want to implement a PCM fallback or other audio processing\n return;\n }\n\n const volumeBuffer: number[] = [];\n const VOLUME_WINDOW_SIZE = 30;\n const SPEAKING_THRESHOLD = 0.05;\n opusDecoder.on(\"data\", (pcmData: Buffer) => {\n // Monitor the audio volume while the agent is speaking.\n // If the average volume of the user's audio exceeds the defined threshold, it indicates active speaking.\n // When active speaking is detected, stop the agent's current audio playbook to avoid overlap.\n\n if (this.activeAudioPlayer) {\n const samples = new Int16Array(\n pcmData.buffer,\n pcmData.byteOffset,\n pcmData.length / 2\n );\n const maxAmplitude = Math.max(...samples.map(Math.abs)) / 32768;\n volumeBuffer.push(maxAmplitude);\n\n if (volumeBuffer.length > VOLUME_WINDOW_SIZE) {\n volumeBuffer.shift();\n }\n const avgVolume =\n volumeBuffer.reduce((sum, v) => sum + v, 0) / VOLUME_WINDOW_SIZE;\n\n if (avgVolume > SPEAKING_THRESHOLD) {\n volumeBuffer.length = 0;\n this.cleanupAudioPlayer(this.activeAudioPlayer);\n this.processingVoice = false;\n }\n }\n });\n\n pipeline(\n receiveStream as AudioReceiveStream,\n opusDecoder as any,\n (err: Error | null) => {\n if (err) {\n logger.debug(\n `[monitorMember] Opus decoding pipeline error for user ${entityId}: ${err}`\n );\n } else {\n logger.debug(\n `[monitorMember] Opus decoding pipeline finished successfully for user ${entityId}`\n );\n }\n }\n );\n this.streams.set(entityId, opusDecoder);\n this.connections.set(entityId, connection as VoiceConnection);\n opusDecoder.on(\"error\", (err: any) => {\n logger.debug(`Opus decoding error: ${err}`);\n });\n const errorHandler = (err: any) => {\n logger.debug(`Opus decoding error: ${err}`);\n };\n const streamCloseHandler = () => {\n logger.debug(`voice stream from ${member?.displayName} closed`);\n this.streams.delete(entityId);\n this.connections.delete(entityId);\n };\n const closeHandler = () => {\n logger.debug(`Opus decoder for ${member?.displayName} closed`);\n opusDecoder.removeListener(\"error\", errorHandler);\n opusDecoder.removeListener(\"close\", closeHandler);\n receiveStream?.removeListener(\"close\", streamCloseHandler);\n };\n opusDecoder.on(\"error\", errorHandler);\n opusDecoder.on(\"close\", closeHandler);\n receiveStream?.on(\"close\", streamCloseHandler);\n\n this.client?.emit(\n \"userStream\",\n entityId,\n name,\n userName,\n channel,\n opusDecoder\n );\n }\n\n /**\n * Leaves the specified voice channel and stops monitoring all members in that channel.\n * If there is an active connection in the channel, it will be destroyed.\n *\n * @param {BaseGuildVoiceChannel} channel - The voice channel to leave.\n */\n leaveChannel(channel: BaseGuildVoiceChannel) {\n const connection = this.connections.get(channel.id);\n if (connection) {\n connection.destroy();\n this.connections.delete(channel.id);\n }\n\n // Stop monitoring all members in this channel\n for (const [memberId, monitorInfo] of this.activeMonitors) {\n if (\n monitorInfo.channel.id === channel.id &&\n memberId !== this.client?.user?.id\n ) {\n this.stopMonitoringMember(memberId);\n }\n }\n\n logger.debug(`Left voice channel: ${channel.name} (${channel.id})`);\n }\n\n /**\n * Stop monitoring a specific member by their member ID.\n * @param {string} memberId - The ID of the member to stop monitoring.\n */\n stopMonitoringMember(memberId: string) {\n const monitorInfo = this.activeMonitors.get(memberId);\n if (monitorInfo) {\n monitorInfo.monitor.stop();\n this.activeMonitors.delete(memberId);\n this.streams.delete(memberId);\n logger.debug(`Stopped monitoring user ${memberId}`);\n }\n }\n\n /**\n * Asynchronously debounces the process transcription function to prevent rapid execution.\n *\n * @param {UUID} entityId - The ID of the entity related to the transcription.\n * @param {string} name - The name of the entity for transcription.\n * @param {string} userName - The username of the user initiating the transcription.\n * @param {BaseGuildVoiceChannel} channel - The voice channel where the transcription is happening.\n */\n\n async debouncedProcessTranscription(\n entityId: UUID,\n name: string,\n userName: string,\n channel: BaseGuildVoiceChannel\n ) {\n const DEBOUNCE_TRANSCRIPTION_THRESHOLD = 1500; // wait for 1.5 seconds of silence\n\n if (this.activeAudioPlayer?.state?.status === \"idle\") {\n logger.log(\"Cleaning up idle audio player.\");\n this.cleanupAudioPlayer(this.activeAudioPlayer);\n }\n\n if (this.activeAudioPlayer || this.processingVoice) {\n const state = this.userStates.get(entityId);\n if (state) {\n state.buffers.length = 0;\n state.totalLength = 0;\n }\n return;\n }\n\n if (this.transcriptionTimeout) {\n clearTimeout(this.transcriptionTimeout);\n }\n\n this.transcriptionTimeout = setTimeout(async () => {\n this.processingVoice = true;\n try {\n await this.processTranscription(\n entityId,\n channel.id,\n channel,\n name,\n userName\n );\n\n // Clean all users' previous buffers\n this.userStates.forEach((state, _) => {\n state.buffers.length = 0;\n state.totalLength = 0;\n });\n } finally {\n this.processingVoice = false;\n }\n }, DEBOUNCE_TRANSCRIPTION_THRESHOLD) as unknown as NodeJS.Timeout;\n }\n\n /**\n * Handle user audio stream for monitoring purposes.\n *\n * @param {UUID} userId - The unique identifier of the user.\n * @param {string} name - The name of the user.\n * @param {string} userName - The username of the user.\n * @param {BaseGuildVoiceChannel} channel - The voice channel the user is in.\n * @param {Readable} audioStream - The audio stream to monitor.\n */\n async handleUserStream(\n entityId: UUID,\n name: string,\n userName: string,\n channel: BaseGuildVoiceChannel,\n audioStream: Readable\n ) {\n logger.debug(`Starting audio monitor for user: ${entityId}`);\n if (!this.userStates.has(entityId)) {\n this.userStates.set(entityId, {\n buffers: [],\n totalLength: 0,\n lastActive: Date.now(),\n transcriptionText: \"\",\n });\n }\n\n const state = this.userStates.get(entityId);\n\n const processBuffer = async (buffer: Buffer) => {\n try {\n state?.buffers.push(buffer);\n state!.totalLength += buffer.length;\n state!.lastActive = Date.now();\n this.debouncedProcessTranscription(entityId, name, userName, channel);\n } catch (error) {\n console.error(`Error processing buffer for user ${entityId}:`, error);\n }\n };\n\n new AudioMonitor(\n audioStream,\n 10000000,\n () => {\n if (this.transcriptionTimeout) {\n clearTimeout(this.transcriptionTimeout);\n }\n },\n async (buffer) => {\n if (!buffer) {\n console.error(\"Received empty buffer\");\n return;\n }\n await processBuffer(buffer);\n }\n );\n }\n\n /**\n * Process the transcription of audio data for a user.\n *\n * @param {UUID} entityId - The unique ID of the user entity.\n * @param {string} channelId - The ID of the channel where the transcription is taking place.\n * @param {BaseGuildVoiceChannel} channel - The voice channel where the user is speaking.\n * @param {string} name - The name of the user.\n * @param {string} userName - The username of the user.\n * @returns {Promise<void>}\n */\n private async processTranscription(\n entityId: UUID,\n channelId: string,\n channel: BaseGuildVoiceChannel,\n name: string,\n userName: string\n ) {\n const state = this.userStates.get(entityId);\n if (!state || state.buffers.length === 0) return;\n try {\n const inputBuffer = Buffer.concat(state.buffers, state.totalLength);\n\n state.buffers.length = 0; // Clear the buffers\n state.totalLength = 0;\n // Convert Opus to WAV\n const wavBuffer = await this.convertOpusToWav(inputBuffer);\n logger.debug(\"Starting transcription...\");\n\n const transcriptionText = await this.runtime.useModel(\n ModelType.TRANSCRIPTION,\n wavBuffer\n );\n function isValidTranscription(text: string): boolean {\n if (!text || text.includes(\"[BLANK_AUDIO]\")) return false;\n return true;\n }\n\n if (transcriptionText && isValidTranscription(transcriptionText)) {\n state.transcriptionText += transcriptionText;\n }\n\n if (state.transcriptionText.length) {\n this.cleanupAudioPlayer(this.activeAudioPlayer);\n const finalText = state.transcriptionText;\n state.transcriptionText = \"\";\n await this.handleMessage(\n finalText,\n entityId,\n channelId,\n channel,\n name,\n userName\n );\n }\n } catch (error) {\n console.error(`Error transcribing audio for user ${entityId}:`, error);\n }\n }\n\n /**\n * Handles a voice message received in a Discord channel.\n *\n * @param {string} message - The message content.\n * @param {UUID} entityId - The entity ID associated with the message.\n * @param {string} channelId - The ID of the Discord channel where the message was received.\n * @param {BaseGuildVoiceChannel} channel - The Discord channel where the message was received.\n * @param {string} name - The name associated with the message.\n * @param {string} userName - The user name associated with the message.\n * @returns {Promise<{text: string, actions: string[]}>} Object containing the resulting text and actions.\n */\n private async handleMessage(\n message: string,\n entityId: UUID,\n channelId: string,\n channel: BaseGuildVoiceChannel,\n name: string,\n userName: string\n ) {\n try {\n if (!message || message.trim() === \"\" || message.length < 3) {\n return { text: \"\", actions: [\"IGNORE\"] };\n }\n\n const roomId = createUniqueUuid(this.runtime, channelId);\n const uniqueEntityId = createUniqueUuid(this.runtime, entityId);\n const type = await this.getChannelType(channel as Channel);\n\n await this.runtime.ensureConnection({\n entityId: uniqueEntityId,\n roomId,\n userName,\n name: name,\n source: \"discord\",\n channelId,\n serverId: channel.guild.id,\n type,\n worldId: createUniqueUuid(this.runtime, channel.guild.id) as UUID,\n worldName: channel.guild.name,\n });\n\n const memory: Memory = {\n id: createUniqueUuid(\n this.runtime,\n `${channelId}-voice-message-${Date.now()}`\n ),\n agentId: this.runtime.agentId,\n entityId: uniqueEntityId,\n roomId,\n content: {\n text: message,\n source: \"discord\",\n url: channel.url,\n name: name,\n userName: userName,\n isVoiceMessage: true,\n channelType: type,\n },\n createdAt: Date.now(),\n };\n\n const callback: HandlerCallback = async (\n content: Content,\n _files: any[] = []\n ) => {\n try {\n const responseMemory: Memory = {\n id: createUniqueUuid(\n this.runtime,\n `${memory.id}-voice-response-${Date.now()}`\n ),\n entityId: this.runtime.agentId,\n agentId: this.runtime.agentId,\n content: {\n ...content,\n name: this.runtime.character.name,\n inReplyTo: memory.id,\n isVoiceMessage: true,\n channelType: type,\n },\n roomId,\n createdAt: Date.now(),\n };\n\n if (responseMemory.content.text?.trim()) {\n await this.runtime.createMemory(responseMemory, \"messages\");\n\n const responseStream = await this.runtime.useModel(\n ModelType.TEXT_TO_SPEECH,\n content.text\n );\n if (responseStream) {\n await this.playAudioStream(entityId, responseStream as Readable);\n }\n }\n\n return [responseMemory];\n } catch (error) {\n console.error(\"Error in voice message callback:\", error);\n return [];\n }\n };\n\n // Emit voice-specific events\n this.runtime.emitEvent(\n [\"DISCORD_VOICE_MESSAGE_RECEIVED\", \"VOICE_MESSAGE_RECEIVED\"],\n {\n runtime: this.runtime,\n message: memory,\n callback,\n }\n );\n } catch (error) {\n console.error(\"Error processing voice message:\", error);\n }\n }\n\n /**\n * Asynchronously converts an Opus audio Buffer to a WAV audio Buffer.\n *\n * @param {Buffer} pcmBuffer - The Opus audio Buffer to convert to WAV.\n * @returns {Promise<Buffer>} A Promise that resolves with the converted WAV audio Buffer.\n */\n private async convertOpusToWav(pcmBuffer: Buffer): Promise<Buffer> {\n try {\n // Generate the WAV header\n const wavHeader = getWavHeader(pcmBuffer.length, DECODE_SAMPLE_RATE);\n\n // Concatenate the WAV header and PCM data\n const wavBuffer = Buffer.concat([wavHeader, pcmBuffer]);\n\n return wavBuffer;\n } catch (error) {\n console.error(\"Error converting PCM to WAV:\", error);\n throw error;\n }\n }\n\n /**\n * Scans the given Discord guild to select a suitable voice channel to join.\n *\n * @param {Guild} guild The Discord guild to scan for voice channels.\n */\n async scanGuild(guild: Guild) {\n let chosenChannel: BaseGuildVoiceChannel | null = null;\n\n try {\n const channelId = this.runtime.getSetting(\n \"DISCORD_VOICE_CHANNEL_ID\"\n ) as string;\n if (channelId) {\n const channel = await guild.channels.fetch(channelId);\n if (channel?.isVoiceBased()) {\n chosenChannel = channel as BaseGuildVoiceChannel;\n }\n }\n\n if (!chosenChannel) {\n const channels = (await guild.channels.fetch()).filter(\n (channel) => channel?.type === DiscordChannelType.GuildVoice\n );\n for (const [, channel] of channels) {\n const voiceChannel = channel as BaseGuildVoiceChannel;\n if (\n voiceChannel.members.size > 0 &&\n (chosenChannel === null ||\n voiceChannel.members.size > chosenChannel.members.size)\n ) {\n chosenChannel = voiceChannel;\n }\n }\n }\n\n if (chosenChannel) {\n logger.debug(`Joining channel: ${chosenChannel.name}`);\n await this.joinChannel(chosenChannel);\n } else {\n logger.debug(\"Warning: No suitable voice channel found to join.\");\n }\n } catch (error) {\n console.error(\"Error selecting or joining a voice channel:\", error);\n }\n }\n\n /**\n * Play an audio stream for a given entity ID.\n *\n * @param {UUID} entityId - The ID of the entity to play the audio for.\n * @param {Readable} audioStream - The audio stream to play.\n * @returns {void}\n */\n async playAudioStream(entityId: UUID, audioStream: Readable) {\n const connection = this.connections.get(entityId);\n if (connection == null) {\n logger.debug(`No connection for user ${entityId}`);\n return;\n }\n this.cleanupAudioPlayer(this.activeAudioPlayer);\n const audioPlayer = createAudioPlayer({\n behaviors: {\n noSubscriber: NoSubscriberBehavior.Pause,\n },\n });\n this.activeAudioPlayer = audioPlayer;\n connection.subscribe(audioPlayer);\n\n const audioStartTime = Date.now();\n\n const resource = createAudioResource(audioStream, {\n inputType: StreamType.Arbitrary,\n });\n audioPlayer.play(resource);\n\n audioPlayer.on(\"error\", (err: any) => {\n logger.debug(`Audio player error: ${err}`);\n });\n\n audioPlayer.on(\n \"stateChange\",\n (_oldState: any, newState: { status: string }) => {\n if (newState.status === \"idle\") {\n const idleTime = Date.now();\n logger.debug(`Audio playback took: ${idleTime - audioStartTime}ms`);\n }\n }\n );\n }\n\n /**\n * Cleans up the provided audio player by stopping it, removing all listeners,\n * and resetting the active audio player if it matches the provided player.\n *\n * @param {AudioPlayer} audioPlayer - The audio player to be cleaned up.\n */\n cleanupAudioPlayer(audioPlayer: AudioPlayer | null) {\n if (!audioPlayer) return;\n\n audioPlayer.stop();\n audioPlayer.removeAllListeners();\n if (audioPlayer === this.activeAudioPlayer) {\n this.activeAudioPlayer = null;\n }\n }\n\n /**\n * Asynchronously handles the join channel command in an interaction.\n *\n * @param {any} interaction - The interaction object representing the user's input.\n * @returns {Promise<void>} - A promise that resolves once the join channel command is handled.\n */\n async handleJoinChannelCommand(interaction: any) {\n try {\n // Defer the reply immediately to prevent interaction timeout\n await interaction.deferReply();\n\n const channelId = interaction.options.get(\"channel\")?.value as string;\n if (!channelId) {\n await interaction.editReply(\"Please provide a voice channel to join.\");\n return;\n }\n\n const guild = interaction.guild;\n if (!guild) {\n await interaction.editReply(\"Could not find guild.\");\n return;\n }\n\n const voiceChannel = interaction.guild.channels.cache.find(\n (channel: VoiceChannel) =>\n channel.id === channelId &&\n channel.type === DiscordChannelType.GuildVoice\n );\n\n if (!voiceChannel) {\n await interaction.editReply(\"Voice channel not found!\");\n return;\n }\n\n await this.joinChannel(voiceChannel as BaseGuildVoiceChannel);\n await interaction.editReply(`Joined voice channel: ${voiceChannel.name}`);\n } catch (error) {\n console.error(\"Error joining voice channel:\", error);\n // Use editReply instead of reply for the error case\n await interaction\n .editReply(\"Failed to join the voice channel.\")\n .catch(console.error);\n }\n }\n\n /**\n * Handles the leave channel command by destroying the voice connection if it exists.\n *\n * @param {any} interaction The interaction object representing the command invocation.\n * @returns {void}\n */\n async handleLeaveChannelCommand(interaction: any) {\n const connection = this.getVoiceConnection(interaction.guildId as any);\n\n if (!connection) {\n await interaction.reply(\"Not currently in a voice channel.\");\n return;\n }\n\n try {\n connection.destroy();\n await interaction.reply(\"Left the voice channel.\");\n } catch (error) {\n console.error(\"Error leaving voice channel:\", error);\n await interaction.reply(\"Failed to leave the voice channel.\");\n }\n }\n}\n","import {\n AudioPlayerStatus,\n NoSubscriberBehavior,\n type VoiceConnection,\n VoiceConnectionStatus,\n createAudioPlayer,\n createAudioResource,\n entersState,\n} from \"@discordjs/voice\";\nimport {\n type IAgentRuntime,\n ModelType,\n type TestSuite,\n logger,\n} from \"@elizaos/core\";\nimport {\n ChannelType,\n Events,\n type TextChannel,\n AttachmentBuilder,\n} from \"discord.js\";\nimport type { DiscordService } from \"./service\";\nimport { ServiceType } from \"./types\";\nimport { sendMessageInChunks } from \"./utils\";\n\nconst TEST_IMAGE_URL =\n \"https://github.com/elizaOS/awesome-eliza/blob/main/assets/eliza-logo.jpg?raw=true\";\n\n/**\n * Represents a test suite for Discord functionality.\n * @class DiscordTestSuite\n * @implements TestSuite\n * @property {string} name - The name of the test suite\n * @property {DiscordService | null} discordClient - The Discord client instance\n * @property {Array<{ name: string; fn: (runtime: IAgentRuntime) => Promise<void> }>} tests - Array of test functions\n */\nexport class DiscordTestSuite implements TestSuite {\n name = \"discord\";\n private discordClient!: DiscordService; // Use definite assignment assertion\n tests: { name: string; fn: (runtime: IAgentRuntime) => Promise<void> }[];\n\n /**\n * Constructor for initializing the tests array with test cases to be executed.\n *\n * @constructor\n * @this {TestSuite}\n */\n constructor() {\n this.tests = [\n {\n name: \"Initialize Discord Client\",\n fn: this.testCreatingDiscordClient.bind(this),\n },\n {\n name: \"Slash Commands - Join Voice\",\n fn: this.testJoinVoiceSlashCommand.bind(this),\n },\n {\n name: \"Voice Playback & TTS\",\n fn: this.testTextToSpeechPlayback.bind(this),\n },\n {\n name: \"Send Message with Attachments\",\n fn: this.testSendingTextMessage.bind(this),\n },\n {\n name: \"Handle Incoming Messages\",\n fn: this.testHandlingMessage.bind(this),\n },\n {\n name: \"Slash Commands - Leave Voice\",\n fn: this.testLeaveVoiceSlashCommand.bind(this),\n },\n ];\n }\n\n /**\n * Asynchronously tests the creation of Discord client using the provided runtime.\n *\n * @param {IAgentRuntime} runtime - The agent runtime used to obtain the Discord service.\n * @returns {Promise<void>} - A Promise that resolves once the Discord client is ready.\n * @throws {Error} - If an error occurs while creating the Discord client.\n */\n async testCreatingDiscordClient(runtime: IAgentRuntime) {\n try {\n this.discordClient = runtime.getService(\n ServiceType.DISCORD\n ) as DiscordService;\n if (!this.discordClient) {\n throw new Error(\"Failed to get DiscordService from runtime.\");\n }\n\n // Wait for the bot to be ready before proceeding\n if (this.discordClient.client?.isReady()) {\n logger.success(\"DiscordService is already ready.\");\n } else {\n logger.info(\"Waiting for DiscordService to be ready...\");\n if (!this.discordClient.client) {\n throw new Error(\n \"Discord client instance is missing within the service.\"\n );\n }\n await new Promise((resolve, reject) => {\n this.discordClient.client?.once(Events.ClientReady, resolve);\n this.discordClient.client?.once(Events.Error, reject);\n });\n }\n } catch (error) {\n throw new Error(`Error in test creating Discord client: ${error}`);\n }\n }\n\n /**\n * Asynchronously tests the join voice slash command functionality.\n *\n * @param {IAgentRuntime} runtime - The runtime environment for the agent.\n * @returns {Promise<void>} - A promise that resolves once the test is complete.\n * @throws {Error} - If there is an error in executing the slash command test.\n */\n async testJoinVoiceSlashCommand(runtime: IAgentRuntime) {\n if (!this.discordClient) throw new Error(\"Discord client not initialized.\");\n try {\n await this.waitForVoiceManagerReady(this.discordClient);\n\n const channel = await this.getTestChannel(runtime);\n if (!channel || !channel.isTextBased()) {\n throw new Error(\"Invalid test channel for slash command test.\");\n }\n\n // Simulate a join channel slash command interaction\n const fakeJoinInteraction = {\n isCommand: () => true,\n commandName: \"joinchannel\",\n options: {\n get: (name: string) =>\n name === \"channel\" ? { value: channel.id } : null,\n },\n guild: (channel as TextChannel).guild,\n deferReply: async () => {},\n editReply: async (message: string) => {\n logger.info(`JoinChannel Slash Command Response: ${message}`);\n },\n };\n\n if (!this.discordClient.voiceManager) {\n throw new Error(\"VoiceManager is not available on the Discord client.\");\n }\n await this.discordClient.voiceManager.handleJoinChannelCommand(\n fakeJoinInteraction as any\n );\n\n logger.success(\"Join voice slash command test completed successfully.\");\n } catch (error) {\n throw new Error(`Error in join voice slash commands test: ${error}`);\n }\n }\n\n /**\n * Asynchronously tests the leave voice channel slash command.\n *\n * @param {IAgentRuntime} runtime - The Agent Runtime instance.\n * @returns {Promise<void>} A promise that resolves when the test is complete.\n */\n async testLeaveVoiceSlashCommand(runtime: IAgentRuntime) {\n if (!this.discordClient) throw new Error(\"Discord client not initialized.\");\n try {\n await this.waitForVoiceManagerReady(this.discordClient);\n\n const channel = await this.getTestChannel(runtime);\n if (!channel || !channel.isTextBased()) {\n throw new Error(\"Invalid test channel for slash command test.\");\n }\n\n // Simulate a leave channel slash command interaction\n const fakeLeaveInteraction = {\n isCommand: () => true,\n commandName: \"leavechannel\",\n guildId: (channel as TextChannel).guildId,\n reply: async (message: string) => {\n logger.info(`LeaveChannel Slash Command Response: ${message}`);\n },\n };\n\n if (!this.discordClient.voiceManager) {\n throw new Error(\"VoiceManager is not available on the Discord client.\");\n }\n await this.discordClient.voiceManager.handleLeaveChannelCommand(\n fakeLeaveInteraction as any\n );\n\n logger.success(\"Leave voice slash command test completed successfully.\");\n } catch (error) {\n throw new Error(`Error in leave voice slash commands test: ${error}`);\n }\n }\n\n /**\n * Test Text to Speech playback.\n * @param {IAgentRuntime} runtime - The Agent Runtime instance.\n * @throws {Error} - If voice channel is invalid, voice connection fails to become ready, or no text to speech service found.\n */\n async testTextToSpeechPlayback(runtime: IAgentRuntime) {\n if (!this.discordClient) throw new Error(\"Discord client not initialized.\");\n try {\n await this.waitForVoiceManagerReady(this.discordClient);\n\n const channel = await this.getTestChannel(runtime);\n if (!channel || channel.type !== ChannelType.GuildVoice) {\n throw new Error(\"Invalid voice channel.\");\n }\n\n if (!this.discordClient.voiceManager) {\n throw new Error(\"VoiceManager is not available on the Discord client.\");\n }\n await this.discordClient.voiceManager.joinChannel(channel);\n\n const guild = await this.getActiveGuild(this.discordClient);\n const guildId = guild.id;\n\n if (!this.discordClient.voiceManager) {\n throw new Error(\"VoiceManager is not available on the Discord client.\");\n }\n const connection =\n this.discordClient.voiceManager.getVoiceConnection(guildId);\n\n if (!connection) {\n throw new Error(`No voice connection found for guild: ${guildId}`);\n }\n\n try {\n await entersState(connection, VoiceConnectionStatus.Ready, 10_000);\n logger.success(`Voice connection is ready in guild: ${guildId}`);\n } catch (error) {\n throw new Error(`Voice connection failed to become ready: ${error}`);\n }\n\n let responseStream = null;\n\n try {\n responseStream = await runtime.useModel(\n ModelType.TEXT_TO_SPEECH,\n `Hi! I'm ${runtime.character.name}! How are you doing today?`\n );\n } catch (_error) {\n throw new Error(\"No text to speech service found\");\n }\n\n if (!responseStream) {\n throw new Error(\"TTS response stream is null or undefined.\");\n }\n\n await this.playAudioStream(responseStream, connection);\n } catch (error) {\n throw new Error(`Error in TTS playback test: ${error}`);\n }\n }\n\n /**\n * Asynchronously tests sending a text message to a specified channel.\n *\n * @param {IAgentRuntime} runtime - The runtime for the agent.\n * @returns {Promise<void>} A Promise that resolves when the message is sent successfully.\n * @throws {Error} If there is an error in sending the text message.\n */\n async testSendingTextMessage(runtime: IAgentRuntime) {\n if (!this.discordClient) throw new Error(\"Discord client not initialized.\");\n try {\n const channel = await this.getTestChannel(runtime);\n if (!channel || !channel.isTextBased()) {\n throw new Error(\"Cannot send message to a non-text channel.\");\n }\n const attachment = new AttachmentBuilder(TEST_IMAGE_URL);\n await this.sendMessageToChannel(\n channel as TextChannel,\n \"Testing Message\",\n [attachment]\n );\n } catch (error) {\n throw new Error(`Error in sending text message: ${error}`);\n }\n }\n\n /**\n * Asynchronously handles sending a test message using the given runtime and mock user data.\n *\n * @param {IAgentRuntime} runtime - The agent runtime object.\n * @returns {Promise<void>} A Promise that resolves once the message is handled.\n */\n async testHandlingMessage(runtime: IAgentRuntime) {\n if (!this.discordClient) throw new Error(\"Discord client not initialized.\");\n try {\n const channel = await this.getTestChannel(runtime);\n\n const fakeMessage = {\n content: `Hello, ${runtime.character.name}! How are you?`,\n author: {\n id: \"mock-user-id\",\n username: \"MockUser\",\n bot: false,\n },\n channel,\n id: \"mock-message-id\",\n createdTimestamp: Date.now(),\n mentions: {\n has: () => false,\n },\n reference: null,\n attachments: [],\n };\n if (!this.discordClient.messageManager) {\n throw new Error(\n \"MessageManager is not available on the Discord client.\"\n );\n }\n await this.discordClient.messageManager.handleMessage(fakeMessage as any);\n } catch (error) {\n throw new Error(`Error in handling message test: ${error}`);\n }\n }\n\n // #############################\n // Utility Functions\n // #############################\n\n /**\n * Asynchronously retrieves the test channel associated with the provided runtime.\n *\n * @param {IAgentRuntime} runtime - The runtime object containing necessary information.\n * @returns {Promise<Channel>} The test channel retrieved from the Discord client.\n * @throws {Error} If no test channel is found.\n */\n async getTestChannel(runtime: IAgentRuntime) {\n if (!this.discordClient) throw new Error(\"Discord client not initialized.\");\n const channelId = this.validateChannelId(runtime);\n const channel = await this.discordClient.client?.channels.fetch(channelId);\n\n if (!channel) throw new Error(\"no test channel found!\");\n\n return channel;\n }\n\n /**\n * Async function to send a message to a text-based channel.\n *\n * @param {TextChannel} channel - The text-based channel the message is being sent to.\n * @param {string} messageContent - The content of the message being sent.\n * @param {any[]} files - An array of files to include in the message.\n * @throws {Error} If the channel is not a text-based channel or does not exist.\n * @throws {Error} If there is an error sending the message.\n */\n async sendMessageToChannel(\n channel: TextChannel,\n messageContent: string,\n files: any[]\n ) {\n try {\n if (!channel || !channel.isTextBased()) {\n throw new Error(\n \"Channel is not a text-based channel or does not exist.\"\n );\n }\n\n // Pass empty string for _inReplyTo as it expects a string\n await sendMessageInChunks(\n channel as TextChannel,\n messageContent,\n \"\",\n files\n );\n } catch (error) {\n throw new Error(`Error sending message: ${error}`);\n }\n }\n\n /**\n * Play an audio stream from a given response stream using the provided VoiceConnection.\n *\n * @param {any} responseStream - The response stream to play as audio.\n * @param {VoiceConnection} connection - The VoiceConnection to use for playing the audio.\n * @returns {Promise<void>} - A Promise that resolves when the TTS playback is finished.\n */\n async playAudioStream(responseStream: any, connection: VoiceConnection) {\n const audioPlayer = createAudioPlayer({\n behaviors: {\n noSubscriber: NoSubscriberBehavior.Pause,\n },\n });\n\n const audioResource = createAudioResource(responseStream);\n\n audioPlayer.play(audioResource);\n connection.subscribe(audioPlayer);\n\n logger.success(\"TTS playback started successfully.\");\n\n await new Promise<void>((resolve, reject) => {\n audioPlayer.once(AudioPlayerStatus.Idle, () => {\n logger.info(\"TTS playback finished.\");\n resolve();\n });\n\n audioPlayer.once(\"error\", (error) => {\n reject(error);\n throw new Error(`TTS playback error: ${error}`);\n });\n });\n }\n\n /**\n * Retrieves the active guild where the bot is currently connected to a voice channel.\n *\n * @param {DiscordService} discordClient The DiscordService instance used to interact with the Discord API.\n * @returns {Promise<Guild>} The active guild where the bot is currently connected to a voice channel.\n * @throws {Error} If no active voice connection is found for the bot.\n */\n async getActiveGuild(discordClient: DiscordService) {\n if (!discordClient.client) {\n throw new Error(\"Discord client instance is missing within the service.\");\n }\n const guilds = await discordClient.client.guilds.fetch();\n const fullGuilds = await Promise.all(guilds.map((guild) => guild.fetch())); // Fetch full guild data\n\n const activeGuild = fullGuilds.find((g) => g.members.me?.voice.channelId);\n if (!activeGuild) {\n throw new Error(\"No active voice connection found for the bot.\");\n }\n return activeGuild;\n }\n\n /**\n * Waits for the VoiceManager in the Discord client to be ready.\n *\n * @param {DiscordService} discordClient - The Discord client to check for VoiceManager readiness.\n * @throws {Error} If the Discord client is not initialized.\n * @returns {Promise<void>} A promise that resolves when the VoiceManager is ready.\n */\n private async waitForVoiceManagerReady(discordClient: DiscordService) {\n if (!discordClient) {\n // This check might be redundant if called after the initial test setup check, but safe to keep.\n throw new Error(\"Discord client is not initialized.\");\n }\n\n if (!discordClient.voiceManager) {\n throw new Error(\"VoiceManager is not available on the Discord client.\");\n }\n\n if (!discordClient.voiceManager?.isReady()) {\n await new Promise<void>((resolve, reject) => {\n discordClient.voiceManager?.once(\"ready\", resolve);\n discordClient.voiceManager?.once(\"error\", reject);\n });\n }\n }\n\n /**\n * Validates the Discord test channel ID by checking if it is set in the runtime or environment variables.\n * If the test channel ID is not set, an error is thrown.\n *\n * @param {IAgentRuntime} runtime The runtime object containing the settings and environment variables.\n * @returns {string} The validated Discord test channel ID.\n */\n private validateChannelId(runtime: IAgentRuntime) {\n const testChannelId =\n runtime.getSetting(\"DISCORD_TEST_CHANNEL_ID\") ||\n process.env.DISCORD_TEST_CHANNEL_ID;\n if (!testChannelId) {\n throw new Error(\n \"DISCORD_TEST_CHANNEL_ID is not set. Please provide a valid channel ID in the environment variables.\"\n );\n }\n return testChannelId as string; // Assert as string since we check for falsy above\n }\n}\n"],"mappings":"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","names":["logger","ModelType","composePromptFromState","parseJSONObjectFromText","fs","ModelType","composePromptFromState","parseJSONObjectFromText","trimTokens","summarizationTemplate","ModelType","composePromptFromState","parseJSONObjectFromText","composePromptFromState","ModelType","parseJSONObjectFromText","attachment","ChannelType","ModelType","composePromptFromState","createUniqueUuid","ServiceType","ChannelType","ServiceType","member","createUniqueUuid","composePromptFromState","ModelType","targetChannel","ChannelType","createUniqueUuid","logger","ServiceType","logger","ChannelType","createUniqueUuid","ChannelType","ChannelType","ServiceType","ChannelType","ChannelType","EventType","createUniqueUuid","logger","DiscordChannelType","PermissionsBitField","ChannelType","ServiceType","createUniqueUuid","logger","DiscordChannelType","fs","trimTokens","parseJSONObjectFromText","ModelType","ServiceType","ModelType","logger","parseJSONObjectFromText","trimTokens","ChannelType","logger","ChannelType","DiscordChannelType","createUniqueUuid","logger","ChannelType","entityId","match","ServiceType","ChannelType","ModelType","createUniqueUuid","logger","DiscordChannelType","connection","isValidTranscription","logger","createUniqueUuid","EventType","DiscordChannelType","ChannelType","PermissionsBitField","fullGuild","NoSubscriberBehavior","VoiceConnectionStatus","createAudioPlayer","createAudioResource","entersState","ModelType","logger","ChannelType","Events","ServiceType","logger","Events","ChannelType","entersState","VoiceConnectionStatus","ModelType","createAudioPlayer","NoSubscriberBehavior","createAudioResource","logger"]}
|