@elizaos/plugin-discord 1.3.2 → 1.3.7
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/README.md +460 -45
- package/dist/index.d.ts +939 -2
- package/dist/index.js +5410 -1542
- package/dist/index.js.map +1 -1
- package/package.json +16 -4
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/joinChannel.ts","../src/constants.ts","../src/actions/leaveChannel.ts","../src/actions/listChannels.ts","../src/actions/readChannel.ts","../src/actions/sendDM.ts","../src/actions/summarizeConversation.ts","../src/actions/transcribeMedia.ts","../src/actions/searchMessages.ts","../src/actions/createPoll.ts","../src/actions/getUserInfo.ts","../src/actions/reactToMessage.ts","../src/actions/pinMessage.ts","../src/actions/unpinMessage.ts","../src/actions/serverInfo.ts","../src/providers/channelState.ts","../src/types.ts","../src/providers/voiceState.ts","../src/service.ts","../src/environment.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 joinChannel from './actions/joinChannel';\nimport leaveChannel from './actions/leaveChannel';\nimport listChannels from './actions/listChannels';\nimport readChannel from './actions/readChannel';\nimport sendDM from './actions/sendDM';\nimport { summarize } from './actions/summarizeConversation';\nimport { transcribeMedia } from './actions/transcribeMedia';\nimport searchMessages from './actions/searchMessages';\nimport createPoll from './actions/createPoll';\nimport getUserInfo from './actions/getUserInfo';\nimport reactToMessage from './actions/reactToMessage';\nimport pinMessage from './actions/pinMessage';\nimport unpinMessage from './actions/unpinMessage';\nimport serverInfo from './actions/serverInfo';\n\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: [\n chatWithAttachments,\n downloadMedia,\n joinChannel,\n leaveChannel,\n listChannels,\n readChannel,\n sendDM,\n summarize,\n transcribeMedia,\n searchMessages,\n createPoll,\n getUserInfo,\n reactToMessage,\n pinMessage,\n unpinMessage,\n serverInfo,\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: \"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 {\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 parseJSONObjectFromText,\n createUniqueUuid,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\nimport type { TextChannel, BaseGuildVoiceChannel } from 'discord.js';\nimport { ChannelType as DiscordChannelType } from 'discord.js';\nimport type { VoiceManager } from '../voice';\n\n/**\n * Template for extracting channel information from the user's request to join a channel.\n *\n * @type {string}\n * @description This template is used to determine which channel the user wants the bot to start listening to or join.\n *\n * @param {string} recentMessages - Placeholder for recent messages related to the request.\n * @param {string} senderName - Name of the sender requesting to join a channel.\n *\n * @returns {string} - Formatted template with instructions and JSON structure for response.\n */\nexport const joinChannelTemplate = `# Messages we are searching for channel join information\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting the bot to join a specific Discord channel (text or voice). Your goal is to determine which channel they want to join.\n\nExtract the channel identifier from their request:\n- If they mention a channel like #general or <#channelid>, extract that\n- If they provide a channel name, extract that\n- If they provide a channel ID (long number), extract that\n- If they mention \"voice\", \"vc\", \"voice channel\", include that as a hint\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"channelIdentifier\": \"<channel-name|channel-id|#mention>\",\n \"isVoiceChannel\": true/false\n}\n\\`\\`\\`\n`;\n\n/**\n * Get channel information from the user's request\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<{channelIdentifier: string, isVoiceChannel: boolean} | null>} Channel info or null if not parseable.\n */\nconst getJoinChannelInfo = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<{ channelIdentifier: string; isVoiceChannel: boolean } | null> => {\n const prompt = composePromptFromState({\n state,\n template: joinChannelTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response) as {\n channelIdentifier: string;\n isVoiceChannel: boolean;\n } | null;\n\n if (parsedResponse?.channelIdentifier) {\n return parsedResponse;\n }\n }\n return null;\n};\n\n/**\n * Find a Discord channel by various identifiers\n * @param {DiscordService} discordService - The Discord service instance\n * @param {string} identifier - The channel identifier (name, ID, or mention)\n * @param {string} currentServerId - The current server ID to search in\n * @param {boolean} isVoiceChannel - Whether to look for voice channels\n * @returns {Promise<TextChannel | BaseGuildVoiceChannel | null>} The found channel or null\n */\nconst findChannel = async (\n discordService: DiscordService,\n identifier: string,\n currentServerId?: string,\n isVoiceChannel?: boolean\n): Promise<TextChannel | BaseGuildVoiceChannel | null> => {\n if (!discordService.client) return null;\n\n // Remove channel mention formatting if present\n const cleanId = identifier.replace(/[<#>]/g, '');\n\n try {\n // Try to fetch by ID first\n if (/^\\d+$/.test(cleanId)) {\n try {\n const channel = await discordService.client.channels.fetch(cleanId);\n if (isVoiceChannel && channel?.type === DiscordChannelType.GuildVoice) {\n return channel as BaseGuildVoiceChannel;\n } else if (!isVoiceChannel && channel?.isTextBased() && !channel.isVoiceBased()) {\n return channel as TextChannel;\n }\n } catch (e) {\n // ID not found, continue to name search\n }\n }\n\n // Search in the current server if available\n if (currentServerId) {\n const guild = await discordService.client.guilds.fetch(currentServerId);\n const channels = await guild.channels.fetch();\n\n // Search by channel name\n const channel = channels.find((ch) => {\n const nameMatch =\n ch?.name.toLowerCase() === identifier.toLowerCase() ||\n ch?.name.toLowerCase().replace(/[^a-z0-9 ]/g, '') ===\n identifier.toLowerCase().replace(/[^a-z0-9 ]/g, '');\n\n if (isVoiceChannel) {\n return nameMatch && ch.type === DiscordChannelType.GuildVoice;\n } else {\n return nameMatch && ch.isTextBased() && !ch.isVoiceBased();\n }\n });\n\n if (channel) {\n return channel as TextChannel | BaseGuildVoiceChannel;\n }\n }\n\n // Search in all guilds the bot is in\n const guilds = Array.from(discordService.client.guilds.cache.values());\n for (const guild of guilds) {\n try {\n const channels = await guild.channels.fetch();\n const channel = channels.find((ch) => {\n const nameMatch =\n ch?.name.toLowerCase() === identifier.toLowerCase() ||\n ch?.name.toLowerCase().replace(/[^a-z0-9 ]/g, '') ===\n identifier.toLowerCase().replace(/[^a-z0-9 ]/g, '');\n\n if (isVoiceChannel) {\n return nameMatch && ch.type === DiscordChannelType.GuildVoice;\n } else {\n return nameMatch && ch.isTextBased() && !ch.isVoiceBased();\n }\n });\n\n if (channel) {\n return channel as TextChannel | BaseGuildVoiceChannel;\n }\n } catch (e) {\n // Continue searching in other guilds\n }\n }\n\n return null;\n } catch (error) {\n console.error('Error finding channel:', error);\n return null;\n }\n};\n\nexport const joinChannel: Action = {\n name: 'JOIN_CHANNEL',\n similes: [\n 'START_LISTENING_CHANNEL',\n 'LISTEN_TO_CHANNEL',\n 'ADD_CHANNEL',\n 'WATCH_CHANNEL',\n 'MONITOR_CHANNEL',\n 'JOIN_TEXT_CHANNEL',\n 'JOIN_VOICE',\n 'JOIN_VC',\n 'JOIN_VOICE_CHAT',\n 'JOIN_VOICE_CHANNEL',\n 'HOP_IN_VOICE',\n 'ENTER_VOICE_CHANNEL',\n ],\n description:\n 'Join a Discord channel - either text (to monitor messages) or voice (to participate in voice chat). You have full voice capabilities!',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n if (message.content.source !== 'discord') {\n return false;\n }\n return true;\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n console.error('Discord service not found or not initialized');\n return;\n }\n\n const channelInfo = await getJoinChannelInfo(runtime, message, state);\n if (!channelInfo) {\n console.error(\"Couldn't parse channel information from message\");\n await callback({\n text: \"I couldn't understand which channel you want me to join. Please specify the channel name or ID.\",\n source: 'discord',\n });\n return;\n }\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n const currentServerId = room?.serverId;\n\n // First, try the user's approach - if they said voice/vc, look for voice channels\n const messageText = message.content.text?.toLowerCase() || '';\n const isVoiceRequest =\n channelInfo.isVoiceChannel ||\n messageText.includes('voice') ||\n messageText.includes('vc') ||\n messageText.includes('hop in');\n\n // Find the channel (try voice first if it's a voice request)\n let targetChannel = isVoiceRequest\n ? await findChannel(discordService, channelInfo.channelIdentifier, currentServerId, true)\n : await findChannel(discordService, channelInfo.channelIdentifier, currentServerId, false);\n\n // If not found, try the opposite type\n if (!targetChannel) {\n targetChannel = isVoiceRequest\n ? await findChannel(discordService, channelInfo.channelIdentifier, currentServerId, false)\n : await findChannel(discordService, channelInfo.channelIdentifier, currentServerId, true);\n }\n\n if (!targetChannel) {\n // If the user is in a voice channel and no specific channel was found, join their voice channel\n if (isVoiceRequest && currentServerId) {\n const guild = discordService.client.guilds.cache.get(currentServerId);\n const members = guild?.members.cache;\n const member = members?.find(\n (member) => createUniqueUuid(runtime, member.id) === message.entityId\n );\n\n if (member?.voice?.channel) {\n targetChannel = member.voice.channel as BaseGuildVoiceChannel;\n }\n }\n }\n\n if (!targetChannel) {\n await callback({\n text: `I couldn't find a channel with the identifier \"${channelInfo.channelIdentifier}\". Please make sure the channel name or ID is correct and I have access to it.`,\n source: 'discord',\n });\n return;\n }\n\n // Handle voice channels\n if (targetChannel.type === DiscordChannelType.GuildVoice) {\n const voiceChannel = targetChannel as BaseGuildVoiceChannel;\n const voiceManager = discordService.voiceManager as VoiceManager;\n\n if (!voiceManager) {\n await callback({\n text: 'Voice functionality is not available at the moment.',\n source: 'discord',\n });\n return;\n }\n\n // Join the voice channel\n await voiceManager.joinChannel(voiceChannel);\n\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 ${voiceChannel.name}`,\n actions: ['JOIN_VOICE_STARTED'],\n },\n metadata: {\n type: 'JOIN_VOICE',\n },\n },\n 'messages'\n );\n\n const response: Content = {\n text: `I've joined the voice channel ${voiceChannel.name}!`,\n actions: ['JOIN_CHANNEL_RESPONSE'],\n source: message.content.source,\n };\n\n await callback(response);\n } else {\n // Handle text channels\n const textChannel = targetChannel as TextChannel;\n\n // Check if we're already listening to this channel\n const currentChannels = discordService.getAllowedChannels();\n if (currentChannels.includes(textChannel.id)) {\n await callback({\n text: `I'm already listening to ${textChannel.name} (<#${textChannel.id}>).`,\n source: 'discord',\n });\n return;\n }\n\n // Add the channel to the allowed list\n const success = discordService.addAllowedChannel(textChannel.id);\n\n if (success) {\n const response: Content = {\n text: `I've started listening to ${textChannel.name} (<#${textChannel.id}>). I'll now respond to messages in that channel.`,\n actions: ['JOIN_CHANNEL_RESPONSE'],\n source: message.content.source,\n };\n\n await callback(response);\n } else {\n await callback({\n text: `I couldn't add ${textChannel.name} to my listening list. Please try again.`,\n source: 'discord',\n });\n }\n }\n } catch (error) {\n console.error('Error joining channel:', error);\n await callback({\n text: 'I encountered an error while trying to join the channel. Please make sure I have the necessary permissions.',\n source: 'discord',\n });\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Start listening to #general',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll start listening to the #general channel.\",\n actions: ['JOIN_CHANNEL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'join the dev-voice channel',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll join the dev-voice channel right away!\",\n actions: ['JOIN_CHANNEL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'hop in vc',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Joining your voice channel now!',\n actions: ['JOIN_CHANNEL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Can you join the announcements channel?',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll join the announcements channel and start monitoring messages there.\",\n actions: ['JOIN_CHANNEL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Please monitor channel 123456789012345678',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll start monitoring that channel for messages.\",\n actions: ['JOIN_CHANNEL'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default joinChannel;\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 type Action,\n type ActionExample,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n type ActionResult,\n ModelType,\n type State,\n composePromptFromState,\n parseJSONObjectFromText,\n logger,\n createUniqueUuid,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\nimport { type TextChannel, BaseGuildVoiceChannel } from 'discord.js';\nimport { ChannelType as DiscordChannelType } from 'discord.js';\nimport type { VoiceManager } from '../voice';\n\n/**\n * Template for extracting channel information from the user's request to leave a channel.\n *\n * @type {string}\n * @description This template is used to determine which channel the user wants the bot to stop listening to or leave.\n *\n * @param {string} recentMessages - Placeholder for recent messages related to the request.\n * @param {string} senderName - Name of the sender requesting to leave a channel.\n *\n * @returns {string} - Formatted template with instructions and JSON structure for response.\n */\nexport const leaveChannelTemplate = `# Messages we are searching for channel leave information\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting the bot to leave a specific Discord channel (text or voice). Your goal is to determine which channel they want to leave.\n\nExtract the channel identifier from their request:\n- If they mention a channel like #general or <#channelid>, extract that\n- If they provide a channel name (like \"dev-voice\" or \"general\"), extract just the name\n- If they provide a channel ID (long number), extract that\n- If they say \"this channel\" or \"here\", use \"current\"\n- If they don't specify a channel but mention \"voice\", \"vc\", use \"current\" and mark as voice\n\nExamples:\n- \"leave the dev-voice channel\" -> channelIdentifier: \"dev-voice\", isVoiceChannel: true\n- \"leave #general\" -> channelIdentifier: \"general\", isVoiceChannel: false\n- \"leave voice\" -> channelIdentifier: \"current\", isVoiceChannel: true\n- \"stop listening to this channel\" -> channelIdentifier: \"current\", isVoiceChannel: false\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"channelIdentifier\": \"<actual-channel-name-or-id-or-current>\",\n \"isVoiceChannel\": true/false\n}\n\\`\\`\\`\n`;\n\n/**\n * Get channel information from the user's request\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<{channelIdentifier: string, isVoiceChannel: boolean} | null>} Channel info or null if not parseable.\n */\nconst getLeaveChannelInfo = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<{ channelIdentifier: string; isVoiceChannel: boolean } | null> => {\n const prompt = composePromptFromState({\n state,\n template: leaveChannelTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response) as {\n channelIdentifier: string;\n isVoiceChannel: boolean;\n } | null;\n\n if (parsedResponse?.channelIdentifier) {\n return parsedResponse;\n }\n }\n return null;\n};\n\n/**\n * Find a Discord channel by various identifiers\n * @param {DiscordService} discordService - The Discord service instance\n * @param {string} identifier - The channel identifier (name, ID, or mention)\n * @param {string} currentChannelId - The current channel ID if \"current\" is specified\n * @param {string} currentServerId - The current server ID to search in\n * @param {boolean} isVoiceChannel - Whether to look for voice channels\n * @returns {Promise<TextChannel | BaseGuildVoiceChannel | null>} The found channel or null\n */\nconst findChannel = async (\n discordService: DiscordService,\n identifier: string,\n currentChannelId?: string,\n currentServerId?: string,\n isVoiceChannel?: boolean\n): Promise<TextChannel | BaseGuildVoiceChannel | null> => {\n if (!discordService.client) return null;\n\n // Handle \"current\" channel\n if (identifier === 'current' && currentChannelId) {\n try {\n const channel = await discordService.client.channels.fetch(currentChannelId);\n if (isVoiceChannel && channel?.type === DiscordChannelType.GuildVoice) {\n return channel as BaseGuildVoiceChannel;\n } else if (!isVoiceChannel && channel?.isTextBased() && !channel.isVoiceBased()) {\n return channel as TextChannel;\n }\n } catch (e) {\n // Current channel not found\n }\n }\n\n // Remove channel mention formatting if present\n const cleanId = identifier.replace(/[<#>]/g, '');\n\n try {\n // Try to fetch by ID first\n if (/^\\d+$/.test(cleanId)) {\n try {\n const channel = await discordService.client.channels.fetch(cleanId);\n if (isVoiceChannel && channel?.type === DiscordChannelType.GuildVoice) {\n return channel as BaseGuildVoiceChannel;\n } else if (!isVoiceChannel && channel?.isTextBased() && !channel.isVoiceBased()) {\n return channel as TextChannel;\n }\n } catch (e) {\n // ID not found, continue to name search\n }\n }\n\n // Search in the current server if available\n if (currentServerId) {\n const guild = await discordService.client.guilds.fetch(currentServerId);\n const channels = await guild.channels.fetch();\n\n // Search by channel name\n const channel = channels.find((ch) => {\n const nameMatch =\n ch?.name.toLowerCase() === identifier.toLowerCase() ||\n ch?.name.toLowerCase().replace(/[^a-z0-9 ]/g, '') ===\n identifier.toLowerCase().replace(/[^a-z0-9 ]/g, '');\n\n if (isVoiceChannel) {\n return nameMatch && ch.type === DiscordChannelType.GuildVoice;\n } else {\n return nameMatch && ch.isTextBased() && !ch.isVoiceBased();\n }\n });\n\n if (channel) {\n return channel as TextChannel | BaseGuildVoiceChannel;\n }\n }\n\n // Search in all guilds the bot is in\n const guilds = Array.from(discordService.client.guilds.cache.values());\n for (const guild of guilds) {\n try {\n const channels = await guild.channels.fetch();\n const channel = channels.find((ch) => {\n const nameMatch =\n ch?.name.toLowerCase() === identifier.toLowerCase() ||\n ch?.name.toLowerCase().replace(/[^a-z0-9 ]/g, '') ===\n identifier.toLowerCase().replace(/[^a-z0-9 ]/g, '');\n\n if (isVoiceChannel) {\n return nameMatch && ch.type === DiscordChannelType.GuildVoice;\n } else {\n return nameMatch && ch.isTextBased() && !ch.isVoiceBased();\n }\n });\n\n if (channel) {\n return channel as TextChannel | BaseGuildVoiceChannel;\n }\n } catch (e) {\n // Continue searching in other guilds\n }\n }\n\n return null;\n } catch (error) {\n console.error('Error finding channel:', error);\n return null;\n }\n};\n\nexport const leaveChannel: Action = {\n name: 'LEAVE_CHANNEL',\n similes: [\n 'LEAVE_CHANNEL',\n 'STOP_LISTENING_CHANNEL',\n 'STOP_MONITORING_CHANNEL',\n 'REMOVE_CHANNEL',\n 'UNWATCH_CHANNEL',\n 'LEAVE_TEXT_CHANNEL',\n 'IGNORE_CHANNEL',\n 'LEAVE_VOICE',\n 'LEAVE_VC',\n 'LEAVE_VOICE_CHAT',\n 'LEAVE_VOICE_CHANNEL',\n 'LEAVE_CALL',\n 'EXIT_VOICE',\n 'DISCONNECT_VOICE',\n 'LEAVE_DISCORD_CHANNEL',\n 'EXIT_CHANNEL',\n ],\n description:\n 'Leave a Discord channel - either text (stop monitoring messages) or voice (disconnect from voice chat). Use this when asked to leave, exit, or disconnect from any Discord channel.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State): Promise<boolean> => {\n logger.debug(`[LEAVE_CHANNEL] Validating message: ${message.content.text}`);\n\n if (message.content.source !== 'discord') {\n logger.debug('[LEAVE_CHANNEL] Not a discord message');\n return false;\n }\n\n logger.debug('[LEAVE_CHANNEL] Validation passed');\n return true;\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ): Promise<void | ActionResult | undefined> => {\n logger.info(`[LEAVE_CHANNEL] Handler called with message: ${message.content.text}`);\n\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n console.error('Discord service not found or not initialized');\n await callback({\n text: 'Discord service is not available.',\n source: 'discord',\n });\n return undefined;\n }\n\n const channelInfo = await getLeaveChannelInfo(runtime, message, state);\n logger.debug(`[LEAVE_CHANNEL] Parsed channel info:`, channelInfo);\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n const currentServerId = room?.serverId;\n const currentChannelId = room?.channelId;\n\n // Check if trying to leave voice without specifying channel\n const messageText = message.content.text?.toLowerCase() || '';\n const isVoiceRequest =\n channelInfo?.isVoiceChannel ||\n messageText.includes('voice') ||\n messageText.includes('vc') ||\n messageText.includes('call');\n\n // If it's a generic voice leave request, handle current voice channel\n if (isVoiceRequest && (!channelInfo || channelInfo.channelIdentifier === 'current')) {\n const voiceManager = discordService.voiceManager as VoiceManager;\n\n if (!voiceManager) {\n await callback({\n text: 'Voice functionality is not available at the moment.',\n source: 'discord',\n });\n return undefined;\n }\n\n if (currentServerId) {\n const guild = discordService.client.guilds.cache.get(currentServerId);\n const voiceChannel = guild?.members.me?.voice.channel;\n\n if (!voiceChannel || !(voiceChannel instanceof BaseGuildVoiceChannel)) {\n await callback({\n text: \"I'm not currently in a voice channel.\",\n source: 'discord',\n });\n return undefined;\n }\n\n const connection = voiceManager.getVoiceConnection(guild.id);\n if (!connection) {\n await callback({\n text: 'No active voice connection found.',\n source: 'discord',\n });\n return undefined;\n }\n\n voiceManager.leaveChannel(voiceChannel);\n\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 await callback({\n text: `I've left the voice channel ${voiceChannel.name}.`,\n source: 'discord',\n });\n return;\n }\n }\n\n if (!channelInfo) {\n console.error(\"Couldn't parse channel information from message\");\n await callback({\n text: \"I couldn't understand which channel you want me to leave. Please specify the channel name or ID.\",\n source: 'discord',\n });\n return undefined;\n }\n\n // Find the channel (try voice first if it's a voice request)\n let targetChannel = isVoiceRequest\n ? await findChannel(\n discordService,\n channelInfo.channelIdentifier,\n currentChannelId,\n currentServerId,\n true\n )\n : await findChannel(\n discordService,\n channelInfo.channelIdentifier,\n currentChannelId,\n currentServerId,\n false\n );\n\n // If not found, try the opposite type\n if (!targetChannel) {\n targetChannel = isVoiceRequest\n ? await findChannel(\n discordService,\n channelInfo.channelIdentifier,\n currentChannelId,\n currentServerId,\n false\n )\n : await findChannel(\n discordService,\n channelInfo.channelIdentifier,\n currentChannelId,\n currentServerId,\n true\n );\n }\n\n if (!targetChannel) {\n await callback({\n text: `I couldn't find a channel with the identifier \"${channelInfo.channelIdentifier}\". Please make sure the channel name or ID is correct.`,\n source: 'discord',\n });\n return undefined;\n }\n\n // Handle voice channels\n if (targetChannel.type === DiscordChannelType.GuildVoice) {\n const voiceChannel = targetChannel as BaseGuildVoiceChannel;\n const voiceManager = discordService.voiceManager as VoiceManager;\n\n if (!voiceManager) {\n await callback({\n text: 'Voice functionality is not available at the moment.',\n source: 'discord',\n });\n return undefined;\n }\n\n const guild = voiceChannel.guild;\n const currentVoiceChannel = guild.members.me?.voice.channel;\n\n if (!currentVoiceChannel || currentVoiceChannel.id !== voiceChannel.id) {\n await callback({\n text: `I'm not currently in the voice channel ${voiceChannel.name}.`,\n source: 'discord',\n });\n return undefined;\n }\n\n voiceManager.leaveChannel(voiceChannel);\n\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 const response: Content = {\n text: `I've left the voice channel ${voiceChannel.name}.`,\n actions: ['LEAVE_CHANNEL_RESPONSE'],\n source: message.content.source,\n };\n\n await callback(response);\n return;\n } else {\n // Handle text channels\n const textChannel = targetChannel as TextChannel;\n\n // Check if we're listening to this channel\n const currentChannels = discordService.getAllowedChannels();\n if (!currentChannels.includes(textChannel.id)) {\n await callback({\n text: `I'm not currently listening to ${textChannel.name} (<#${textChannel.id}>).`,\n source: 'discord',\n });\n return undefined;\n }\n\n // Remove the channel from the allowed list\n const success = discordService.removeAllowedChannel(textChannel.id);\n\n if (success) {\n const response: Content = {\n text: `I've stopped listening to ${textChannel.name} (<#${textChannel.id}>). I will no longer respond to messages in that channel.`,\n actions: ['LEAVE_CHANNEL_RESPONSE'],\n source: message.content.source,\n };\n\n await callback(response);\n return;\n } else {\n await callback({\n text: `I couldn't remove ${textChannel.name} from my listening list. This channel might be configured in my environment settings and cannot be removed dynamically.`,\n source: 'discord',\n });\n return undefined;\n }\n }\n\n return;\n } catch (error) {\n console.error('Error leaving channel:', error);\n await callback({\n text: 'I encountered an error while trying to leave the channel. Please try again.',\n source: 'discord',\n });\n return undefined;\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'leave the dev-voice channel',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll leave the dev-voice channel.\",\n actions: ['LEAVE_CHANNEL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: '{{name2}} leave the dev-voice channel in discord',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Leaving the dev-voice channel now.',\n actions: ['LEAVE_CHANNEL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Stop listening to #general',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll stop listening to the #general channel.\",\n actions: ['LEAVE_CHANNEL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'leave voice',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll leave the voice channel.\",\n actions: ['LEAVE_CHANNEL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Leave this channel',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll stop monitoring messages in this channel.\",\n actions: ['LEAVE_CHANNEL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'hop off vc',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Leaving the voice channel now.',\n actions: ['LEAVE_CHANNEL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Please stop monitoring the spam channel',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll stop monitoring the spam channel.\",\n actions: ['LEAVE_CHANNEL'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default leaveChannel;\n","import {\n type Action,\n type ActionExample,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n type State,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\n\nexport const listChannels: Action = {\n name: 'LIST_CHANNELS',\n similes: [\n 'SHOW_CHANNELS',\n 'LIST_LISTENING_CHANNELS',\n 'SHOW_MONITORED_CHANNELS',\n 'GET_CHANNELS',\n 'WHICH_CHANNELS',\n 'CHANNELS_LIST',\n ],\n description: 'Lists all Discord channels the bot is currently listening to and responding in.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n if (message.content.source !== 'discord') {\n return false;\n }\n return true;\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n console.error('Discord service not found or not initialized');\n return;\n }\n\n try {\n // Get all allowed channels\n const allowedChannelIds = discordService.getAllowedChannels();\n\n if (allowedChannelIds.length === 0) {\n await callback({\n text: \"I'm currently listening to all channels (no restrictions are set).\",\n source: 'discord',\n });\n return;\n }\n\n // Fetch channel information for each allowed channel\n const channelInfoPromises = allowedChannelIds.map(async (channelId) => {\n try {\n const channel = await discordService.client!.channels.fetch(channelId);\n if (channel && channel.isTextBased() && !channel.isVoiceBased()) {\n const guild = 'guild' in channel ? channel.guild : null;\n return {\n id: channelId,\n name: 'name' in channel ? channel.name : 'DM',\n mention: `<#${channelId}>`,\n server: guild?.name || 'Direct Message',\n };\n }\n } catch (e) {\n // Channel might have been deleted or bot lost access\n return {\n id: channelId,\n name: 'Unknown',\n mention: channelId,\n server: 'Unknown or Deleted',\n };\n }\n return null;\n });\n\n const channelInfos = (await Promise.all(channelInfoPromises)).filter(Boolean);\n\n // Format the response\n let responseText = `I'm currently listening to ${channelInfos.length} channel${channelInfos.length !== 1 ? 's' : ''}:\\n\\n`;\n\n // Group by server\n const channelsByServer = channelInfos.reduce(\n (acc, channel) => {\n if (!channel) return acc;\n if (!acc[channel.server]) {\n acc[channel.server] = [];\n }\n acc[channel.server].push(channel);\n return acc;\n },\n {} as Record<string, typeof channelInfos>\n );\n\n // Format by server\n for (const [serverName, channels] of Object.entries(channelsByServer)) {\n responseText += `**${serverName}**\\n`;\n for (const channel of channels) {\n if (channel) {\n responseText += `• ${channel.name} (${channel.mention})\\n`;\n }\n }\n responseText += '\\n';\n }\n\n // Check if CHANNEL_IDS is set\n const envChannelIds = runtime.getSetting('CHANNEL_IDS') as string;\n if (envChannelIds) {\n responseText += `\\n*Note: Some channels are configured in my environment settings and cannot be removed dynamically.*`;\n }\n\n const response: Content = {\n text: responseText.trim(),\n actions: ['LIST_CHANNELS_RESPONSE'],\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n console.error('Error listing channels:', error);\n await callback({\n text: 'I encountered an error while trying to list the channels. Please try again.',\n source: 'discord',\n });\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Which channels are you listening to?',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"Let me show you all the channels I'm currently monitoring.\",\n actions: ['LIST_CHANNELS'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'List all monitored channels',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll list all the channels I'm currently listening to.\",\n actions: ['LIST_CHANNELS'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Show me your channel list',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"Here are all the channels I'm monitoring.\",\n actions: ['LIST_CHANNELS'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default listChannels;\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 parseJSONObjectFromText,\n logger,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\nimport { PermissionsBitField, type TextChannel } from 'discord.js';\n\n/**\n * Template for extracting channel information from the user's request.\n *\n * @type {string}\n * @description This template is used to determine which channel the user wants to read messages from,\n * and optionally how many messages to retrieve.\n *\n * @param {string} recentMessages - Placeholder for recent messages related to the request.\n * @param {string} senderName - Name of the sender requesting channel messages.\n *\n * @returns {string} - Formatted template with instructions and JSON structure for response.\n */\nexport const channelInfoTemplate = `# Messages we are searching for channel information\n {{recentMessages}}\n \n # Instructions: {{senderName}} is requesting to read messages from a specific Discord channel. Your goal is to determine:\n 1. The channel they want to read from (could be the current channel or a mentioned channel)\n 2. How many messages they want to read (default to 10 if not specified)\n 3. Whether they want a summary or just the messages\n 4. If they're looking for messages from a specific person\n \n If they say \"this channel\" or \"here\", use the current channel.\n If they mention a specific channel name or ID, extract that.\n If they ask to \"summarize\" or mention what someone is \"talking about\", set summarize to true.\n \n Your response must be formatted as a JSON block with this structure:\n \\`\\`\\`json\n {\n \"channelIdentifier\": \"<current|channel-name|channel-id>\",\n \"messageCount\": <number between 1 and 50>,\n \"summarize\": true/false,\n \"focusUser\": \"<username or null>\"\n }\n \\`\\`\\`\n `;\n\n/**\n * Get channel information from the user's request\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<{channelIdentifier: string, messageCount: number} | null>} Channel info or null if not parseable.\n */\nconst getChannelInfo = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<{\n channelIdentifier: string;\n messageCount: number;\n summarize: boolean;\n focusUser: string | null;\n} | null> => {\n const prompt = composePromptFromState({\n state,\n template: channelInfoTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response) as {\n channelIdentifier: string;\n messageCount: number;\n summarize?: boolean;\n focusUser?: string | null;\n } | null;\n\n if (parsedResponse?.channelIdentifier) {\n // Ensure messageCount is within bounds\n const messageCount = Math.min(Math.max(parsedResponse.messageCount || 10, 1), 50);\n return {\n channelIdentifier: parsedResponse.channelIdentifier,\n messageCount,\n summarize: parsedResponse.summarize || false,\n focusUser: parsedResponse.focusUser || null,\n };\n }\n }\n return null;\n};\n\nexport const readChannel: Action = {\n name: 'READ_CHANNEL',\n similes: [\n 'READ_MESSAGES',\n 'GET_CHANNEL_MESSAGES',\n 'FETCH_MESSAGES',\n 'SHOW_CHANNEL_HISTORY',\n 'GET_CHAT_HISTORY',\n 'READ_CHAT',\n ],\n description:\n 'Reads recent messages from a Discord channel and either returns them or provides a summary. Can focus on messages from a specific user.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n if (message.content.source !== 'discord') {\n return false;\n }\n return true;\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n console.error('Discord service not found or not initialized');\n return;\n }\n\n const channelInfo = await getChannelInfo(runtime, message, state);\n if (!channelInfo) {\n console.error(\"Couldn't parse channel information from message\");\n await callback({\n text: \"I couldn't understand which channel you want me to read from. Please specify the channel name or say 'this channel' for the current channel.\",\n source: 'discord',\n });\n return;\n }\n\n try {\n let targetChannel: TextChannel | null = null;\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n\n // Determine the target channel\n if (\n channelInfo.channelIdentifier === 'current' ||\n channelInfo.channelIdentifier === 'this' ||\n channelInfo.channelIdentifier === 'here'\n ) {\n // Use current channel\n if (room?.channelId) {\n targetChannel = (await discordService.client.channels.fetch(\n room.channelId\n )) as TextChannel;\n }\n } else if (channelInfo.channelIdentifier.match(/^\\d+$/)) {\n // It's a channel ID\n targetChannel = (await discordService.client.channels.fetch(\n channelInfo.channelIdentifier\n )) as TextChannel;\n } else if (room?.serverId) {\n // It's a channel name - search in the current server\n const guild = await discordService.client.guilds.fetch(room.serverId);\n const channels = await guild.channels.fetch();\n\n targetChannel =\n (channels.find(\n (channel) =>\n channel?.name.toLowerCase().includes(channelInfo.channelIdentifier.toLowerCase()) &&\n channel.isTextBased()\n ) as TextChannel | undefined) || null;\n }\n\n if (!targetChannel || !targetChannel.isTextBased()) {\n await callback({\n text: \"I couldn't find that channel or I don't have access to it. Make sure the channel exists and I have permission to read messages there.\",\n source: 'discord',\n });\n return;\n }\n\n // Check permissions\n const botMember = targetChannel.guild?.members.cache.get(discordService.client.user!.id);\n if (botMember) {\n const permissions = targetChannel.permissionsFor(botMember);\n if (!permissions?.has(PermissionsBitField.Flags.ReadMessageHistory)) {\n await callback({\n text: \"I don't have permission to read message history in that channel.\",\n source: 'discord',\n });\n return;\n }\n }\n\n // Fetch messages - get more for summarization to have better context\n // Discord API limits to 100 messages per fetch\n const requestedLimit = channelInfo.summarize\n ? Math.max(channelInfo.messageCount * 2, 50)\n : channelInfo.messageCount;\n const fetchLimit = Math.min(requestedLimit, 100);\n\n logger.debug(\n `[READ_CHANNEL] Fetching ${fetchLimit} messages from ${targetChannel.name} (requested: ${requestedLimit}), summarize: ${channelInfo.summarize}, focusUser: ${channelInfo.focusUser}`\n );\n\n const messages = await targetChannel.messages.fetch({\n limit: fetchLimit,\n });\n\n if (messages.size === 0) {\n await callback({\n text: `No messages found in <#${targetChannel.id}>.`,\n source: 'discord',\n });\n return;\n }\n\n // If summarization is requested\n if (channelInfo.summarize) {\n const sortedMessages = Array.from(messages.values()).reverse();\n\n // Filter by user if specified\n const relevantMessages = channelInfo.focusUser\n ? sortedMessages.filter((msg) => {\n const focusUserLower = channelInfo.focusUser!.toLowerCase();\n return (\n msg.author.username.toLowerCase().includes(focusUserLower) ||\n msg.member?.displayName?.toLowerCase().includes(focusUserLower)\n );\n })\n : sortedMessages;\n\n if (channelInfo.focusUser && relevantMessages.length === 0) {\n await callback({\n text: `I couldn't find any messages from \"${channelInfo.focusUser}\" in the recent messages from <#${targetChannel.id}>.`,\n source: 'discord',\n });\n return;\n }\n\n // Prepare messages for summarization\n const messagesToSummarize = relevantMessages\n .slice(0, channelInfo.messageCount)\n .map((msg) => ({\n author: msg.author.username,\n content: msg.content || '[No text content]',\n timestamp: new Date(msg.createdTimestamp).toLocaleString(),\n }));\n\n // Create a summary prompt\n const summaryPrompt = channelInfo.focusUser\n ? `Please summarize what ${channelInfo.focusUser} has been discussing based on these messages from the Discord channel \"${targetChannel.name}\":\\n\\n${messagesToSummarize\n .map((m) => `${m.author} (${m.timestamp}): ${m.content}`)\n .join(\n '\\n\\n'\n )}\\n\\nProvide a concise summary focusing on:\\n1. Main topics ${channelInfo.focusUser} discussed\\n2. Key points or proposals they made\\n3. Any questions they asked or issues they raised\\n\\nIf ${channelInfo.focusUser} didn't appear in these messages, please note that.`\n : `Please summarize the recent conversation in the Discord channel \"${targetChannel.name}\" based on these messages:\\n\\n${messagesToSummarize\n .map((m) => `${m.author} (${m.timestamp}): ${m.content}`)\n .join(\n '\\n\\n'\n )}\\n\\nProvide a concise summary that includes:\\n1. Main topics discussed\\n2. Key decisions or conclusions\\n3. Who contributed what (mention specific usernames)\\n4. Any action items or next steps mentioned`;\n\n const summary = await runtime.useModel(ModelType.TEXT_LARGE, {\n prompt: summaryPrompt,\n });\n\n const response: Content = {\n text: channelInfo.focusUser\n ? `Summary of what ${channelInfo.focusUser} has been discussing in <#${targetChannel.id}>:\\n\\n${summary}`\n : `Summary of recent conversation in <#${targetChannel.id}>:\\n\\n${summary}`,\n actions: ['READ_CHANNEL_RESPONSE'],\n source: message.content.source,\n };\n\n await callback(response);\n } else {\n // Format messages for display (original behavior)\n const formattedMessages = Array.from(messages.values())\n .reverse() // Show oldest first\n .map((msg) => {\n const timestamp = new Date(msg.createdTimestamp).toLocaleString();\n const author = msg.author.username;\n const content = msg.content || '[No text content]';\n const attachments =\n msg.attachments.size > 0\n ? `\\n📎 Attachments: ${msg.attachments.map((a) => a.name || 'unnamed').join(', ')}`\n : '';\n\n return `**${author}** (${timestamp}):\\n${content}${attachments}`;\n })\n .join('\\n\\n---\\n\\n');\n\n const response: Content = {\n text: `Here are the last ${messages.size} messages from <#${targetChannel.id}>:\\n\\n${formattedMessages}`,\n actions: ['READ_CHANNEL_RESPONSE'],\n source: message.content.source,\n };\n\n await callback(response);\n }\n } catch (error) {\n console.error('Error reading channel:', error);\n await callback({\n text: 'I encountered an error while trying to read the channel messages. Please make sure I have the necessary permissions and try again.',\n source: 'discord',\n });\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Can you read the last 20 messages from this channel?',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll read the last 20 messages from this channel for you.\",\n actions: ['READ_CHANNEL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'read the core-devs channel and summarize what shaw talking about',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll read the core-devs channel and summarize shaw's discussion.\",\n actions: ['READ_CHANNEL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: \"Show me what's been said in #general\",\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Let me fetch the recent messages from #general.',\n actions: ['READ_CHANNEL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Summarize the recent conversation in this channel',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll summarize the recent conversation in this channel.\",\n actions: ['READ_CHANNEL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Read messages here',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll read the recent messages from this channel.\",\n actions: ['READ_CHANNEL'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default readChannel;\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 parseJSONObjectFromText,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\nimport type { User } from 'discord.js';\n\n/**\n * Template for extracting DM recipient and message information from the user's request.\n *\n * @type {string}\n * @description This template is used to determine who the user wants to send a DM to and what message to send.\n *\n * @param {string} recentMessages - Placeholder for recent messages related to the request.\n * @param {string} senderName - Name of the sender requesting to send a DM.\n *\n * @returns {string} - Formatted template with instructions and JSON structure for response.\n */\nexport const dmInfoTemplate = `# Messages we are searching for DM information\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting to send a direct message to a specific Discord user. Your goal is to determine:\n1. The recipient they want to message (could be a username, user ID, or mentioned user)\n2. The message content they want to send\n\nExtract the recipient identifier and the message content from their request.\n- If they mention a user like @username or <@userid>, extract that\n- If they provide a username or display name, extract that\n- If they provide a user ID (long number), extract that\n- Extract the complete message they want to send\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"recipientIdentifier\": \"<username|user-id|@mention>\",\n \"messageContent\": \"<the message to send>\"\n}\n\\`\\`\\`\n`;\n\n/**\n * Get DM information from the user's request\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<{recipientIdentifier: string, messageContent: string} | null>} DM info or null if not parseable.\n */\nconst getDMInfo = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<{ recipientIdentifier: string; messageContent: string } | null> => {\n const prompt = composePromptFromState({\n state,\n template: dmInfoTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response) as {\n recipientIdentifier: string;\n messageContent: string;\n } | null;\n\n if (parsedResponse?.recipientIdentifier && parsedResponse?.messageContent) {\n return parsedResponse;\n }\n }\n return null;\n};\n\n/**\n * Find a Discord user by various identifiers\n * @param {DiscordService} discordService - The Discord service instance\n * @param {string} identifier - The user identifier (username, ID, or mention)\n * @param {string} currentServerId - The current server ID to search in\n * @returns {Promise<User | null>} The found user or null\n */\nconst findUser = async (\n discordService: DiscordService,\n identifier: string,\n currentServerId?: string\n): Promise<User | null> => {\n if (!discordService.client) return null;\n\n // Remove mention formatting if present\n const cleanId = identifier.replace(/[<@!>]/g, '');\n\n try {\n // Try to fetch by ID first\n if (/^\\d+$/.test(cleanId)) {\n try {\n return await discordService.client.users.fetch(cleanId);\n } catch (e) {\n // ID not found, continue to username search\n }\n }\n\n // Search in the current server if available\n if (currentServerId) {\n const guild = await discordService.client.guilds.fetch(currentServerId);\n const members = await guild.members.fetch();\n\n // Search by username or display name\n const member = members.find(\n (m) =>\n m.user.username.toLowerCase() === identifier.toLowerCase() ||\n m.displayName.toLowerCase() === identifier.toLowerCase() ||\n m.user.tag.toLowerCase() === identifier.toLowerCase()\n );\n\n if (member) {\n return member.user;\n }\n }\n\n // Search in all guilds the bot is in\n const guilds = Array.from(discordService.client.guilds.cache.values());\n for (const guild of guilds) {\n try {\n const members = await guild.members.fetch();\n const member = members.find(\n (m) =>\n m.user.username.toLowerCase() === identifier.toLowerCase() ||\n m.displayName.toLowerCase() === identifier.toLowerCase() ||\n m.user.tag.toLowerCase() === identifier.toLowerCase()\n );\n\n if (member) {\n return member.user;\n }\n } catch (e) {\n // Continue searching in other guilds\n }\n }\n\n return null;\n } catch (error) {\n console.error('Error finding user:', error);\n return null;\n }\n};\n\nexport const sendDM: Action = {\n name: 'SEND_DM',\n similes: [\n 'SEND_DIRECT_MESSAGE',\n 'DM_USER',\n 'MESSAGE_USER',\n 'PRIVATE_MESSAGE',\n 'SEND_PRIVATE_MESSAGE',\n 'DM',\n 'SEND_MESSAGE_TO_USER',\n ],\n description: 'Sends a direct message to a specific Discord user.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n if (message.content.source !== 'discord') {\n return false;\n }\n return true;\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n console.error('Discord service not found or not initialized');\n return;\n }\n\n const dmInfo = await getDMInfo(runtime, message, state);\n if (!dmInfo) {\n console.error(\"Couldn't parse DM information from message\");\n await callback({\n text: \"I couldn't understand who you want me to message or what to send. Please specify the recipient and the message content.\",\n source: 'discord',\n });\n return;\n }\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n const currentServerId = room?.serverId;\n\n // Find the user\n const targetUser = await findUser(\n discordService,\n dmInfo.recipientIdentifier,\n currentServerId\n );\n\n if (!targetUser) {\n await callback({\n text: `I couldn't find a user with the identifier \"${dmInfo.recipientIdentifier}\". Please make sure the username or ID is correct.`,\n source: 'discord',\n });\n return;\n }\n\n // Check if we can send DMs to this user\n if (targetUser.bot) {\n await callback({\n text: 'I cannot send direct messages to other bots.',\n source: 'discord',\n });\n return;\n }\n\n // Create or get DM channel\n const dmChannel = await targetUser.createDM();\n\n // Send the message\n await dmChannel.send(dmInfo.messageContent);\n\n const response: Content = {\n text: `I've sent your message to ${targetUser.username}: \"${dmInfo.messageContent}\"`,\n actions: ['SEND_DM_RESPONSE'],\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n console.error('Error sending DM:', error);\n\n // Handle specific Discord API errors\n if (error instanceof Error) {\n if (error.message.includes('Cannot send messages to this user')) {\n await callback({\n text: \"I couldn't send a message to that user. They may have DMs disabled or we don't share a server.\",\n source: 'discord',\n });\n } else {\n await callback({\n text: 'I encountered an error while trying to send the direct message. Please make sure I have the necessary permissions.',\n source: 'discord',\n });\n }\n }\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Send a DM to @alice saying hello how are you today?',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll send a direct message to alice right away.\",\n actions: ['SEND_DM'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Can you message john_doe and tell him the meeting is at 3pm?',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll send john_doe a DM about the meeting time.\",\n actions: ['SEND_DM'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'DM user 123456789012345678 with: Thanks for your help!',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll send that thank you message as a DM right now.\",\n actions: ['SEND_DM'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default sendDM;\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 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","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 parseJSONObjectFromText,\n logger,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\nimport { type TextChannel, type Message, Collection } from 'discord.js';\n\n/**\n * Template for extracting search parameters from the user's request.\n */\nexport const searchMessagesTemplate = `# Searching for Discord messages\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting to search for messages in Discord. Extract:\n1. The search query/keywords\n2. The channel to search in (current if not specified)\n3. Optional filters like author, time range, or message count\n\nExamples:\n- \"search for messages containing 'meeting'\" -> query: \"meeting\", channelIdentifier: \"current\", NO author field\n- \"find messages from @user about bugs\" -> query: \"bugs\", channelIdentifier: \"current\", author: \"user\"\n- \"search #general for links from last week\" -> query: \"links\", channelIdentifier: \"general\", timeRange: \"week\"\n- \"search for messages about 'spartan' in this channel\" -> query: \"spartan\", channelIdentifier: \"current\"\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"query\": \"<search keywords>\",\n \"channelIdentifier\": \"<channel-name|channel-id|current>\",\n \"author\": \"<username>\", // ONLY include this field if a specific author was mentioned\n \"timeRange\": \"<hour|day|week|month>\", // ONLY include if a time range was specified\n \"limit\": <number between 1-100, default 20>\n}\n\\`\\`\\`\n`;\n\nconst getSearchParams = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<{\n query: string;\n channelIdentifier: string;\n author: string | null;\n timeRange: string | null;\n limit: number;\n} | null> => {\n const prompt = composePromptFromState({\n state,\n template: searchMessagesTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response);\n if (parsedResponse?.query) {\n // Remove quotes from query if present\n const cleanQuery = parsedResponse.query.replace(/^[\"']|[\"']$/g, '');\n\n return {\n query: cleanQuery,\n channelIdentifier: parsedResponse.channelIdentifier || 'current',\n author: parsedResponse.author || null,\n timeRange: parsedResponse.timeRange || null,\n limit: Math.min(Math.max(parsedResponse.limit || 20, 1), 100),\n };\n }\n }\n return null;\n};\n\nconst searchInMessages = (\n messages: Collection<string, Message>,\n query: string,\n author?: string | null\n): Message[] => {\n const queryLower = query.toLowerCase().trim();\n const isLinkSearch = queryLower.includes('link') || queryLower.includes('url');\n\n return Array.from(messages.values()).filter((msg) => {\n // Skip system messages\n if (msg.system) return false;\n\n // Filter by author if specified\n if (author && author !== 'null' && author !== 'undefined') {\n const authorLower = author.toLowerCase();\n const matchesUsername = msg.author.username.toLowerCase().includes(authorLower);\n const matchesDisplayName =\n msg.member?.displayName?.toLowerCase().includes(authorLower) || false;\n if (!matchesUsername && !matchesDisplayName) {\n return false;\n }\n }\n\n // Special handling for link searches\n if (isLinkSearch) {\n const urlRegex = /(https?:\\/\\/[^\\s]+)/g;\n return urlRegex.test(msg.content);\n }\n\n // Search in message content (case-insensitive)\n const contentMatch = msg.content.toLowerCase().includes(queryLower);\n\n // Search in embeds\n const embedMatch = msg.embeds.some(\n (embed) =>\n embed.title?.toLowerCase().includes(queryLower) ||\n embed.description?.toLowerCase().includes(queryLower) ||\n embed.author?.name?.toLowerCase().includes(queryLower) ||\n embed.fields?.some(\n (field) =>\n field.name?.toLowerCase().includes(queryLower) ||\n field.value?.toLowerCase().includes(queryLower)\n )\n );\n\n // Search in attachments\n const attachmentMatch = msg.attachments.some(\n (att) =>\n att.name?.toLowerCase().includes(queryLower) ||\n att.description?.toLowerCase().includes(queryLower)\n );\n\n return contentMatch || embedMatch || attachmentMatch;\n });\n};\n\nexport const searchMessages: Action = {\n name: 'SEARCH_MESSAGES',\n similes: [\n 'SEARCH_MESSAGES',\n 'FIND_MESSAGES',\n 'SEARCH_CHAT',\n 'LOOK_FOR_MESSAGES',\n 'FIND_IN_CHAT',\n 'SEARCH_CHANNEL',\n 'SEARCH_DISCORD',\n ],\n description: 'Search for messages in Discord channels based on keywords, author, or time range.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n return message.content.source === 'discord';\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n await callback({\n text: 'Discord service is not available.',\n source: 'discord',\n });\n return;\n }\n\n const searchParams = await getSearchParams(runtime, message, state);\n if (!searchParams) {\n await callback({\n text: \"I couldn't understand what you want to search for. Please specify what to search.\",\n source: 'discord',\n });\n return;\n }\n\n try {\n let targetChannel: TextChannel | null = null;\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n\n // Determine the target channel\n if (searchParams.channelIdentifier === 'current') {\n if (room?.channelId) {\n targetChannel = (await discordService.client.channels.fetch(\n room.channelId\n )) as TextChannel;\n }\n } else if (searchParams.channelIdentifier.match(/^\\d+$/)) {\n targetChannel = (await discordService.client.channels.fetch(\n searchParams.channelIdentifier\n )) as TextChannel;\n } else if (room?.serverId) {\n const guild = await discordService.client.guilds.fetch(room.serverId);\n const channels = await guild.channels.fetch();\n targetChannel =\n (channels.find(\n (channel) =>\n channel?.name.toLowerCase().includes(searchParams.channelIdentifier.toLowerCase()) &&\n channel.isTextBased()\n ) as TextChannel | undefined) || null;\n }\n\n if (!targetChannel || !targetChannel.isTextBased()) {\n await callback({\n text: \"I couldn't find that channel or I don't have access to it.\",\n source: 'discord',\n });\n return;\n }\n\n // Calculate time limit\n let before: number | undefined = undefined;\n if (searchParams.timeRange) {\n const now = Date.now();\n const timeMap: Record<string, number> = {\n hour: 60 * 60 * 1000,\n day: 24 * 60 * 60 * 1000,\n week: 7 * 24 * 60 * 60 * 1000,\n month: 30 * 24 * 60 * 60 * 1000,\n };\n if (timeMap[searchParams.timeRange]) {\n before = now - timeMap[searchParams.timeRange];\n }\n }\n\n // Fetch messages - Discord API limit is 100 per request\n const messages = await targetChannel.messages.fetch({\n limit: 100, // Discord API max limit\n before: before?.toString(),\n });\n\n logger.debug(\n `[SEARCH_MESSAGES] Fetched ${messages.size} messages from channel ${targetChannel.name}`\n );\n logger.debug(\n `[SEARCH_MESSAGES] Searching for: \"${searchParams.query}\", author: ${searchParams.author || 'any'}`\n );\n\n // Search through messages\n const results = searchInMessages(messages, searchParams.query, searchParams.author);\n logger.debug(`[SEARCH_MESSAGES] Found ${results.length} matching messages`);\n\n // Sort by timestamp (newest first) and limit\n const sortedResults = results.sort((a, b) => b.createdTimestamp - a.createdTimestamp);\n const limitedResults = sortedResults.slice(0, searchParams.limit);\n\n if (limitedResults.length === 0) {\n await callback({\n text: `No messages found matching \"${searchParams.query}\" in <#${targetChannel.id}>.`,\n source: 'discord',\n });\n return;\n }\n\n // Format results\n const formattedResults = limitedResults\n .map((msg, index) => {\n const timestamp = new Date(msg.createdTimestamp).toLocaleString();\n const preview =\n msg.content.length > 100 ? msg.content.substring(0, 100) + '...' : msg.content;\n const attachments =\n msg.attachments.size > 0 ? `\\n📎 ${msg.attachments.size} attachment(s)` : '';\n\n return `**${index + 1}.** ${msg.author.username} (${timestamp})\\n${preview}${attachments}\\n[Jump to message](${msg.url})`;\n })\n .join('\\n\\n');\n\n const response: Content = {\n text: `Found ${limitedResults.length} message${limitedResults.length !== 1 ? 's' : ''} matching \"${searchParams.query}\" in <#${targetChannel.id}>:\\n\\n${formattedResults}`,\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n logger.error('Error searching messages:', error);\n await callback({\n text: 'I encountered an error while searching for messages. Please try again.',\n source: 'discord',\n });\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: \"search for messages containing 'meeting'\",\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll search for messages containing 'meeting'.\",\n actions: ['SEARCH_MESSAGES'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'find all links shared in #general from last week',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Let me search for links in #general from the past week.',\n actions: ['SEARCH_MESSAGES'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'search for messages from @john about the bug',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll look for messages from john about the bug.\",\n actions: ['SEARCH_MESSAGES'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default searchMessages;\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 parseJSONObjectFromText,\n logger,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\nimport { type TextChannel } from 'discord.js';\n\n/**\n * Template for extracting poll information from the user's request.\n */\nexport const createPollTemplate = `# Creating a Discord poll\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting to create a poll. Extract:\n1. The poll question\n2. The poll options (2-10 options)\n3. Whether to use emoji reactions (default: true)\n\nExamples:\n- \"create a poll: What game should we play? Options: Minecraft, Fortnite, Among Us\" \n -> question: \"What game should we play?\", options: [\"Minecraft\", \"Fortnite\", \"Among Us\"]\n- \"poll: Should we have a meeting tomorrow? Yes/No\"\n -> question: \"Should we have a meeting tomorrow?\", options: [\"Yes\", \"No\"]\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"question\": \"<poll question>\",\n \"options\": [\"<option1>\", \"<option2>\", ...],\n \"useEmojis\": true/false\n}\n\\`\\`\\`\n`;\n\nconst getPollInfo = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<{\n question: string;\n options: string[];\n useEmojis: boolean;\n} | null> => {\n const prompt = composePromptFromState({\n state,\n template: createPollTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response);\n if (\n parsedResponse?.question &&\n Array.isArray(parsedResponse.options) &&\n parsedResponse.options.length >= 2\n ) {\n return {\n question: parsedResponse.question,\n options: parsedResponse.options.slice(0, 10), // Max 10 options\n useEmojis: parsedResponse.useEmojis !== false, // Default to true\n };\n }\n }\n return null;\n};\n\n// Number emojis for poll options\nconst numberEmojis = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟'];\nconst letterEmojis = ['🇦', '🇧', '🇨', '🇩', '🇪', '🇫', '🇬', '🇭', '🇮', '🇯'];\nconst yesNoEmojis = ['✅', '❌'];\n\nexport const createPoll: Action = {\n name: 'CREATE_POLL',\n similes: [\n 'CREATE_POLL',\n 'MAKE_POLL',\n 'START_POLL',\n 'CREATE_VOTE',\n 'MAKE_VOTE',\n 'START_VOTE',\n 'CREATE_SURVEY',\n ],\n description: 'Create a poll in Discord with emoji reactions for voting.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n return message.content.source === 'discord';\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n await callback({\n text: 'Discord service is not available.',\n source: 'discord',\n });\n return;\n }\n\n const pollInfo = await getPollInfo(runtime, message, state);\n if (!pollInfo) {\n await callback({\n text: \"I couldn't understand the poll details. Please specify a question and at least 2 options.\",\n source: 'discord',\n });\n return;\n }\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n if (!room?.channelId) {\n await callback({\n text: \"I couldn't determine the current channel.\",\n source: 'discord',\n });\n return;\n }\n\n const channel = await discordService.client.channels.fetch(room.channelId);\n if (!channel || !channel.isTextBased()) {\n await callback({\n text: 'I can only create polls in text channels.',\n source: 'discord',\n });\n return;\n }\n\n const textChannel = channel as TextChannel;\n\n // Determine which emojis to use\n let emojis: string[];\n if (\n pollInfo.options.length === 2 &&\n pollInfo.options.some((opt) => opt.toLowerCase().includes('yes')) &&\n pollInfo.options.some((opt) => opt.toLowerCase().includes('no'))\n ) {\n emojis = yesNoEmojis;\n } else if (pollInfo.useEmojis) {\n emojis = numberEmojis.slice(0, pollInfo.options.length);\n } else {\n emojis = letterEmojis.slice(0, pollInfo.options.length);\n }\n\n // Format the poll message\n const pollMessage = [\n `📊 **POLL: ${pollInfo.question}**`,\n '',\n ...pollInfo.options.map((option, index) => `${emojis[index]} ${option}`),\n '',\n '_React to vote!_',\n ].join('\\n');\n\n // Send the poll message\n const sentMessage = await textChannel.send(pollMessage);\n\n // Add reactions\n for (let i = 0; i < pollInfo.options.length; i++) {\n try {\n await sentMessage.react(emojis[i]);\n // Small delay to avoid rate limits\n await new Promise((resolve) => setTimeout(resolve, 250));\n } catch (error) {\n logger.error(`Failed to add reaction ${emojis[i]}:`, error);\n }\n }\n\n const response: Content = {\n text: `I've created a poll with ${pollInfo.options.length} options. Users can vote by clicking the reaction emojis!`,\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n logger.error('Error creating poll:', error);\n await callback({\n text: 'I encountered an error while creating the poll. Please make sure I have permission to send messages and add reactions.',\n source: 'discord',\n });\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'create a poll: What game should we play tonight? Options: Minecraft, Fortnite, Valorant',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll create a poll for game selection with those options.\",\n actions: ['CREATE_POLL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'make a vote: Should we have the meeting at 3pm? Yes/No',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Creating a yes/no poll about the meeting time.',\n actions: ['CREATE_POLL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'start a poll asking what day works best for everyone: Monday, Tuesday, Wednesday, Thursday, Friday',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll create a poll to find the best day for everyone.\",\n actions: ['CREATE_POLL'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default createPoll;\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 parseJSONObjectFromText,\n logger,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\nimport { type GuildMember } from 'discord.js';\n\n/**\n * Template for extracting user identifier from the request.\n */\nexport const getUserInfoTemplate = `# Getting Discord user information\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting information about a Discord user. Extract:\n1. The user identifier (username, user ID, or mention)\n2. Whether they want detailed server-specific info\n\nExamples:\n- \"who is @john?\" -> userIdentifier: \"john\", detailed: false\n- \"tell me about user 123456789\" -> userIdentifier: \"123456789\", detailed: false \n- \"get detailed info on @admin\" -> userIdentifier: \"admin\", detailed: true\n- \"who am I?\" -> userIdentifier: \"self\", detailed: false\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"userIdentifier\": \"<username|user-id|mention|self>\",\n \"detailed\": true/false\n}\n\\`\\`\\`\n`;\n\nconst getUserIdentifier = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<{\n userIdentifier: string;\n detailed: boolean;\n} | null> => {\n const prompt = composePromptFromState({\n state,\n template: getUserInfoTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response);\n if (parsedResponse?.userIdentifier) {\n return {\n userIdentifier: parsedResponse.userIdentifier,\n detailed: parsedResponse.detailed === true,\n };\n }\n }\n return null;\n};\n\nconst formatUserInfo = (member: GuildMember, detailed: boolean = false): string => {\n const user = member.user;\n const joinedAt = member.joinedAt ? new Date(member.joinedAt).toLocaleDateString() : 'Unknown';\n const createdAt = new Date(user.createdAt).toLocaleDateString();\n const roles =\n member.roles.cache\n .filter((role) => role.name !== '@everyone')\n .map((role) => role.name)\n .join(', ') || 'No roles';\n\n const basicInfo = [\n `👤 **User Information**`,\n `**Username:** ${user.username}${user.discriminator !== '0' ? `#${user.discriminator}` : ''}`,\n `**Display Name:** ${member.displayName}`,\n `**ID:** ${user.id}`,\n `**Bot:** ${user.bot ? 'Yes' : 'No'}`,\n `**Account Created:** ${createdAt}`,\n ];\n\n if (detailed) {\n const serverInfo = [\n '',\n `🏛️ **Server Information**`,\n `**Nickname:** ${member.nickname || 'None'}`,\n `**Joined Server:** ${joinedAt}`,\n `**Roles:** ${roles}`,\n `**Highest Role:** ${member.roles.highest.name}`,\n `**Permissions:** ${member.permissions.toArray().slice(0, 5).join(', ')}${member.permissions.toArray().length > 5 ? '...' : ''}`,\n `**Voice Channel:** ${member.voice.channel ? member.voice.channel.name : 'Not in voice'}`,\n `**Status:** ${member.presence?.status || 'offline'}`,\n ];\n return [...basicInfo, ...serverInfo].join('\\n');\n }\n\n return basicInfo.join('\\n');\n};\n\nexport const getUserInfo: Action = {\n name: 'GET_USER_INFO',\n similes: [\n 'GET_USER_INFO',\n 'USER_INFO',\n 'WHO_IS',\n 'ABOUT_USER',\n 'USER_DETAILS',\n 'MEMBER_INFO',\n 'CHECK_USER',\n ],\n description:\n 'Get detailed information about a Discord user including their roles, join date, and permissions.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n return message.content.source === 'discord';\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n await callback({\n text: 'Discord service is not available.',\n source: 'discord',\n });\n return;\n }\n\n const userInfo = await getUserIdentifier(runtime, message, state);\n if (!userInfo) {\n await callback({\n text: \"I couldn't understand which user you want information about. Please specify a username or mention.\",\n source: 'discord',\n });\n return;\n }\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n if (!room?.serverId) {\n await callback({\n text: \"I couldn't determine the current server.\",\n source: 'discord',\n });\n return;\n }\n\n const guild = await discordService.client.guilds.fetch(room.serverId);\n\n let member: GuildMember | null = null;\n\n // Handle \"self\" request\n if (userInfo.userIdentifier === 'self') {\n const authorId = (message.content as any).user_id || (message.content as any).userId;\n if (authorId && typeof authorId === 'string') {\n const cleanId = authorId.replace('discord:', '');\n try {\n member = await guild.members.fetch(cleanId);\n } catch (e) {\n // User not found\n }\n }\n } else {\n // Remove mention formatting if present\n const cleanIdentifier = userInfo.userIdentifier.replace(/[<@!>]/g, '');\n\n // Try to fetch by ID first\n if (/^\\d+$/.test(cleanIdentifier)) {\n try {\n member = await guild.members.fetch(cleanIdentifier);\n } catch (e) {\n // Not an ID or user not found\n }\n }\n\n // If not found by ID, search by username or display name\n if (!member) {\n const members = await guild.members.fetch();\n member =\n members.find(\n (m) =>\n m.user.username.toLowerCase() === userInfo.userIdentifier.toLowerCase() ||\n m.displayName.toLowerCase() === userInfo.userIdentifier.toLowerCase() ||\n (m.user.discriminator !== '0' &&\n `${m.user.username}#${m.user.discriminator}`.toLowerCase() ===\n userInfo.userIdentifier.toLowerCase())\n ) || null;\n }\n }\n\n if (!member) {\n await callback({\n text: `I couldn't find a user with the identifier \"${userInfo.userIdentifier}\" in this server.`,\n source: 'discord',\n });\n return;\n }\n\n const infoText = formatUserInfo(member, userInfo.detailed);\n\n const response: Content = {\n text: infoText,\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n logger.error('Error getting user info:', error);\n await callback({\n text: 'I encountered an error while getting user information. Please try again.',\n source: 'discord',\n });\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'who is @john?',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll get information about john.\",\n actions: ['GET_USER_INFO'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'tell me about myself',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll get your user information.\",\n actions: ['GET_USER_INFO'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'get detailed info on the admin user',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll get detailed information about the admin.\",\n actions: ['GET_USER_INFO'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default getUserInfo;\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 parseJSONObjectFromText,\n logger,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\nimport { type TextChannel, type Message } from 'discord.js';\n\n/**\n * Template for extracting reaction information from the user's request.\n */\nexport const reactToMessageTemplate = `# Adding reactions to Discord messages\n{{recentMessages}}\n\n# Instructions: {{senderName}} wants to add a reaction to a message. Extract:\n1. Which message to react to (last, specific message reference, or by content)\n2. What emoji/reaction to add\n\nExamples:\n- \"react with 👍 to the last message\" -> messageRef: \"last\", emoji: \"👍\"\n- \"add :fire: reaction\" -> messageRef: \"last\", emoji: \"🔥\" or \":fire:\"\n- \"react to that message with ❤️\" -> messageRef: \"previous\", emoji: \"❤️\"\n- \"add a thumbs up to john's message about the meeting\" -> messageRef: \"john meeting\", emoji: \"👍\"\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"messageRef\": \"<last|previous|message-id|search-text>\",\n \"emoji\": \"<emoji-character|:emoji-name:>\"\n}\n\\`\\`\\`\n`;\n\nconst getReactionInfo = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<{\n messageRef: string;\n emoji: string;\n} | null> => {\n const prompt = composePromptFromState({\n state,\n template: reactToMessageTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response);\n if (parsedResponse?.emoji) {\n return {\n messageRef: parsedResponse.messageRef || 'last',\n emoji: parsedResponse.emoji,\n };\n }\n }\n return null;\n};\n\n// Common Discord emoji mappings\nconst emojiMap: Record<string, string> = {\n ':thumbsup:': '👍',\n ':thumbs_up:': '👍',\n ':+1:': '👍',\n ':thumbsdown:': '👎',\n ':thumbs_down:': '👎',\n ':-1:': '👎',\n ':heart:': '❤️',\n ':fire:': '🔥',\n ':star:': '⭐',\n ':check:': '✅',\n ':white_check_mark:': '✅',\n ':x:': '❌',\n ':cross:': '❌',\n ':smile:': '😄',\n ':laughing:': '😆',\n ':thinking:': '🤔',\n ':eyes:': '👀',\n ':clap:': '👏',\n ':wave:': '👋',\n ':ok:': '👌',\n ':ok_hand:': '👌',\n ':raised_hands:': '🙌',\n ':pray:': '🙏',\n ':100:': '💯',\n ':rocket:': '🚀',\n};\n\nconst normalizeEmoji = (emoji: string): string => {\n // Check if it's already a valid emoji character\n if (/\\p{Emoji}/u.test(emoji)) {\n return emoji;\n }\n\n // Check if it's in our emoji map\n const mapped = emojiMap[emoji.toLowerCase()];\n if (mapped) {\n return mapped;\n }\n\n // Try to extract custom emoji ID for Discord custom emojis\n const customMatch = emoji.match(/<:(\\w+):(\\d+)>/);\n if (customMatch) {\n return emoji; // Return as-is for Discord to handle\n }\n\n // Remove colons and return\n return emoji.replace(/:/g, '');\n};\n\nexport const reactToMessage: Action = {\n name: 'REACT_TO_MESSAGE',\n similes: [\n 'REACT_TO_MESSAGE',\n 'ADD_REACTION',\n 'REACT_MESSAGE',\n 'ADD_EMOJI',\n 'EMOJI_REACT',\n 'MESSAGE_REACTION',\n ],\n description: 'Add an emoji reaction to a Discord message.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n return message.content.source === 'discord';\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n await callback({\n text: 'Discord service is not available.',\n source: 'discord',\n });\n return;\n }\n\n const reactionInfo = await getReactionInfo(runtime, message, state);\n if (!reactionInfo) {\n await callback({\n text: \"I couldn't understand which message to react to or what emoji to use. Please specify both.\",\n source: 'discord',\n });\n return;\n }\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n if (!room?.channelId) {\n await callback({\n text: \"I couldn't determine the current channel.\",\n source: 'discord',\n });\n return;\n }\n\n const channel = await discordService.client.channels.fetch(room.channelId);\n if (!channel || !channel.isTextBased()) {\n await callback({\n text: 'I can only react to messages in text channels.',\n source: 'discord',\n });\n return;\n }\n\n const textChannel = channel as TextChannel;\n\n let targetMessage: Message | null = null;\n\n // Find the target message\n if (reactionInfo.messageRef === 'last' || reactionInfo.messageRef === 'previous') {\n // Get the last few messages - fetch max allowed by Discord API\n const messages = await textChannel.messages.fetch({ limit: 100 });\n const sortedMessages = Array.from(messages.values()).sort(\n (a, b) => b.createdTimestamp - a.createdTimestamp\n );\n\n // Skip the bot's own message and the command message\n targetMessage =\n sortedMessages.find(\n (msg) =>\n msg.id !== message.content.id && msg.author.id !== discordService.client!.user!.id\n ) || null;\n } else if (/^\\d+$/.test(reactionInfo.messageRef)) {\n // It's a message ID\n try {\n targetMessage = await textChannel.messages.fetch(reactionInfo.messageRef);\n } catch (e) {\n // Message not found\n }\n } else {\n // Search for message by content/author - fetch max allowed by Discord API\n const messages = await textChannel.messages.fetch({ limit: 100 });\n const searchLower = reactionInfo.messageRef.toLowerCase();\n\n targetMessage =\n Array.from(messages.values()).find((msg) => {\n const contentMatch = msg.content.toLowerCase().includes(searchLower);\n const authorMatch = msg.author.username.toLowerCase().includes(searchLower);\n return contentMatch || authorMatch;\n }) || null;\n }\n\n if (!targetMessage) {\n await callback({\n text: \"I couldn't find the message you want me to react to. Try being more specific or use 'last message'.\",\n source: 'discord',\n });\n return;\n }\n\n // Normalize the emoji\n const emoji = normalizeEmoji(reactionInfo.emoji);\n\n // Add the reaction\n try {\n await targetMessage.react(emoji);\n\n const response: Content = {\n text: `I've added a ${emoji} reaction to the message.`,\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n logger.error('Failed to add reaction:', error);\n await callback({\n text: `I couldn't add that reaction. Make sure the emoji \"${reactionInfo.emoji}\" is valid and I have permission to add reactions.`,\n source: 'discord',\n });\n }\n } catch (error) {\n logger.error('Error in react to message:', error);\n await callback({\n text: 'I encountered an error while trying to react to the message. Please make sure I have the necessary permissions.',\n source: 'discord',\n });\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'react with 👍 to the last message',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll add a thumbs up reaction to the last message.\",\n actions: ['REACT_TO_MESSAGE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'add a fire emoji to that',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Adding a 🔥 reaction.',\n actions: ['REACT_TO_MESSAGE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: \"react to john's message about the meeting with a checkmark\",\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll find john's message about the meeting and add a ✅ reaction.\",\n actions: ['REACT_TO_MESSAGE'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default reactToMessage;\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 parseJSONObjectFromText,\n logger,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\nimport { type TextChannel, type Message, PermissionsBitField } from 'discord.js';\n\n/**\n * Template for extracting message reference for pinning.\n */\nexport const pinMessageTemplate = `# Pinning a Discord message\n{{recentMessages}}\n\n# Instructions: {{senderName}} wants to pin a message. Extract which message they want to pin.\n\nExamples:\n- \"pin that message\" -> messageRef: \"last\"\n- \"pin the last message\" -> messageRef: \"last\"\n- \"pin john's message about the meeting\" -> messageRef: \"john meeting\"\n- \"pin message 123456789\" -> messageRef: \"123456789\"\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"messageRef\": \"<last|previous|message-id|search-text>\"\n}\n\\`\\`\\`\n`;\n\nconst getMessageRef = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<{\n messageRef: string;\n} | null> => {\n const prompt = composePromptFromState({\n state,\n template: pinMessageTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response);\n if (parsedResponse?.messageRef) {\n return {\n messageRef: parsedResponse.messageRef,\n };\n }\n }\n return null;\n};\n\nexport const pinMessage: Action = {\n name: 'PIN_MESSAGE',\n similes: ['PIN_MESSAGE', 'PIN_MSG', 'PIN_THIS', 'PIN_THAT', 'MAKE_PINNED', 'ADD_PIN'],\n description: 'Pin an important message in a Discord channel.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n return message.content.source === 'discord';\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n await callback({\n text: 'Discord service is not available.',\n source: 'discord',\n });\n return;\n }\n\n const messageInfo = await getMessageRef(runtime, message, state);\n if (!messageInfo) {\n await callback({\n text: \"I couldn't understand which message you want to pin. Please be more specific.\",\n source: 'discord',\n });\n return;\n }\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n if (!room?.channelId) {\n await callback({\n text: \"I couldn't determine the current channel.\",\n source: 'discord',\n });\n return;\n }\n\n const channel = await discordService.client.channels.fetch(room.channelId);\n if (!channel || !channel.isTextBased()) {\n await callback({\n text: 'I can only pin messages in text channels.',\n source: 'discord',\n });\n return;\n }\n\n const textChannel = channel as TextChannel;\n\n // Check bot permissions\n const botMember = textChannel.guild?.members.cache.get(discordService.client.user!.id);\n if (botMember) {\n const permissions = textChannel.permissionsFor(botMember);\n if (!permissions?.has(PermissionsBitField.Flags.ManageMessages)) {\n await callback({\n text: \"I don't have permission to pin messages in this channel. I need the 'Manage Messages' permission.\",\n source: 'discord',\n });\n return;\n }\n }\n\n let targetMessage: Message | null = null;\n\n // Find the target message\n if (messageInfo.messageRef === 'last' || messageInfo.messageRef === 'previous') {\n // Get the last few messages - fetch max allowed by Discord API\n const messages = await textChannel.messages.fetch({ limit: 100 });\n const sortedMessages = Array.from(messages.values()).sort(\n (a, b) => b.createdTimestamp - a.createdTimestamp\n );\n\n // Skip the bot's own message and the command message\n targetMessage =\n sortedMessages.find(\n (msg) =>\n msg.id !== message.content.id && msg.author.id !== discordService.client!.user!.id\n ) || null;\n } else if (/^\\d+$/.test(messageInfo.messageRef)) {\n // It's a message ID\n try {\n targetMessage = await textChannel.messages.fetch(messageInfo.messageRef);\n } catch (e) {\n // Message not found\n }\n } else {\n // Search for message by content/author - fetch max allowed by Discord API\n const messages = await textChannel.messages.fetch({ limit: 100 });\n const searchLower = messageInfo.messageRef.toLowerCase();\n\n targetMessage =\n Array.from(messages.values()).find((msg) => {\n const contentMatch = msg.content.toLowerCase().includes(searchLower);\n const authorMatch = msg.author.username.toLowerCase().includes(searchLower);\n return contentMatch || authorMatch;\n }) || null;\n }\n\n if (!targetMessage) {\n await callback({\n text: \"I couldn't find the message you want to pin. Try being more specific or use 'last message'.\",\n source: 'discord',\n });\n return;\n }\n\n // Check if already pinned\n if (targetMessage.pinned) {\n await callback({\n text: 'That message is already pinned.',\n source: 'discord',\n });\n return;\n }\n\n // Pin the message\n try {\n await targetMessage.pin();\n\n const response: Content = {\n text: `I've pinned the message from ${targetMessage.author.username}.`,\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n logger.error('Failed to pin message:', error);\n await callback({\n text: \"I couldn't pin that message. The channel might have reached the maximum number of pinned messages (50).\",\n source: 'discord',\n });\n }\n } catch (error) {\n logger.error('Error pinning message:', error);\n await callback({\n text: 'I encountered an error while trying to pin the message. Please make sure I have the necessary permissions.',\n source: 'discord',\n });\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'pin that message',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll pin that message for you.\",\n actions: ['PIN_MESSAGE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'pin the announcement john just made',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll find and pin john's announcement.\",\n actions: ['PIN_MESSAGE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: \"pin the last message, it's important\",\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Pinning the last message to keep it visible.',\n actions: ['PIN_MESSAGE'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default pinMessage;\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 parseJSONObjectFromText,\n logger,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\nimport { type TextChannel, type Message, PermissionsBitField } from 'discord.js';\n\n/**\n * Template for extracting message reference for unpinning.\n */\nexport const unpinMessageTemplate = `# Unpinning a Discord message\n{{recentMessages}}\n\n# Instructions: {{senderName}} wants to unpin a message. Extract which message they want to unpin.\n\nExamples:\n- \"unpin that message\" -> messageRef: \"last_pinned\"\n- \"unpin the last pinned message\" -> messageRef: \"last_pinned\"\n- \"unpin john's message\" -> messageRef: \"john\"\n- \"unpin message about the meeting\" -> messageRef: \"meeting\"\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"messageRef\": \"<last_pinned|message-id|search-text>\"\n}\n\\`\\`\\`\n`;\n\nconst getMessageRef = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<{\n messageRef: string;\n} | null> => {\n const prompt = composePromptFromState({\n state,\n template: unpinMessageTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response);\n if (parsedResponse?.messageRef) {\n return {\n messageRef: parsedResponse.messageRef,\n };\n }\n }\n return null;\n};\n\nexport const unpinMessage: Action = {\n name: 'UNPIN_MESSAGE',\n similes: ['UNPIN_MESSAGE', 'UNPIN_MSG', 'UNPIN_THIS', 'UNPIN_THAT', 'REMOVE_PIN', 'DELETE_PIN'],\n description: 'Unpin a message in a Discord channel.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n return message.content.source === 'discord';\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n await callback({\n text: 'Discord service is not available.',\n source: 'discord',\n });\n return;\n }\n\n const messageInfo = await getMessageRef(runtime, message, state);\n if (!messageInfo) {\n await callback({\n text: \"I couldn't understand which message you want to unpin. Please be more specific.\",\n source: 'discord',\n });\n return;\n }\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n if (!room?.channelId) {\n await callback({\n text: \"I couldn't determine the current channel.\",\n source: 'discord',\n });\n return;\n }\n\n const channel = await discordService.client.channels.fetch(room.channelId);\n if (!channel || !channel.isTextBased()) {\n await callback({\n text: 'I can only unpin messages in text channels.',\n source: 'discord',\n });\n return;\n }\n\n const textChannel = channel as TextChannel;\n\n // Check bot permissions\n const botMember = textChannel.guild?.members.cache.get(discordService.client.user!.id);\n if (botMember) {\n const permissions = textChannel.permissionsFor(botMember);\n if (!permissions?.has(PermissionsBitField.Flags.ManageMessages)) {\n await callback({\n text: \"I don't have permission to unpin messages in this channel. I need the 'Manage Messages' permission.\",\n source: 'discord',\n });\n return;\n }\n }\n\n let targetMessage: Message | null = null;\n\n // Get pinned messages\n const pinnedMessages = await textChannel.messages.fetchPinned();\n\n if (pinnedMessages.size === 0) {\n await callback({\n text: 'There are no pinned messages in this channel.',\n source: 'discord',\n });\n return;\n }\n\n // Find the target message\n if (messageInfo.messageRef === 'last_pinned' || messageInfo.messageRef === 'last') {\n // Get the most recently created pinned message (since we can't sort by pin time)\n targetMessage = Array.from(pinnedMessages.values()).sort(\n (a, b) => b.createdTimestamp - a.createdTimestamp\n )[0];\n } else if (/^\\d+$/.test(messageInfo.messageRef)) {\n // It's a message ID\n targetMessage = pinnedMessages.get(messageInfo.messageRef) || null;\n } else {\n // Search for message by content/author in pinned messages\n const searchLower = messageInfo.messageRef.toLowerCase();\n\n targetMessage =\n Array.from(pinnedMessages.values()).find((msg) => {\n const contentMatch = msg.content.toLowerCase().includes(searchLower);\n const authorMatch = msg.author.username.toLowerCase().includes(searchLower);\n return contentMatch || authorMatch;\n }) || null;\n }\n\n if (!targetMessage) {\n await callback({\n text: \"I couldn't find a pinned message matching your description.\",\n source: 'discord',\n });\n return;\n }\n\n // Unpin the message\n try {\n await targetMessage.unpin();\n\n const response: Content = {\n text: `I've unpinned the message from ${targetMessage.author.username}.`,\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n logger.error('Failed to unpin message:', error);\n await callback({\n text: \"I couldn't unpin that message. Please try again.\",\n source: 'discord',\n });\n }\n } catch (error) {\n logger.error('Error unpinning message:', error);\n await callback({\n text: 'I encountered an error while trying to unpin the message. Please make sure I have the necessary permissions.',\n source: 'discord',\n });\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'unpin the last pinned message',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll unpin the most recent pinned message.\",\n actions: ['UNPIN_MESSAGE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'unpin the message about the old meeting schedule',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll find and unpin the message about the meeting schedule.\",\n actions: ['UNPIN_MESSAGE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: \"remove the pin from john's announcement\",\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll unpin john's announcement.\",\n actions: ['UNPIN_MESSAGE'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default unpinMessage;\n","import {\n type Action,\n type ActionExample,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n type State,\n logger,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\nimport { type Guild } from 'discord.js';\n\nconst formatServerInfo = (guild: Guild, detailed: boolean = false): string => {\n const createdAt = new Date(guild.createdAt).toLocaleDateString();\n const memberCount = guild.memberCount;\n const channelCount = guild.channels.cache.size;\n const roleCount = guild.roles.cache.size;\n const emojiCount = guild.emojis.cache.size;\n const boostLevel = guild.premiumTier;\n const boostCount = guild.premiumSubscriptionCount || 0;\n\n const basicInfo = [\n `🏛️ **Server Information for ${guild.name}**`,\n `**ID:** ${guild.id}`,\n `**Owner:** <@${guild.ownerId}>`,\n `**Created:** ${createdAt}`,\n `**Members:** ${memberCount}`,\n `**Channels:** ${channelCount}`,\n `**Roles:** ${roleCount}`,\n `**Server Level:** ${boostLevel} (${boostCount} boosts)`,\n ];\n\n if (detailed) {\n const textChannels = guild.channels.cache.filter((ch) => ch.isTextBased()).size;\n const voiceChannels = guild.channels.cache.filter((ch) => ch.isVoiceBased()).size;\n const categories = guild.channels.cache.filter((ch) => ch.type === 4).size; // CategoryChannel type\n const activeThreads = guild.channels.cache.filter((ch) => ch.isThread() && !ch.archived).size;\n\n const features =\n guild.features.length > 0\n ? guild.features.map((f) => f.toLowerCase().replace(/_/g, ' ')).join(', ')\n : 'None';\n\n const detailedInfo = [\n '',\n `📊 **Detailed Statistics**`,\n `**Text Channels:** ${textChannels}`,\n `**Voice Channels:** ${voiceChannels}`,\n `**Categories:** ${categories}`,\n `**Active Threads:** ${activeThreads}`,\n `**Custom Emojis:** ${emojiCount}`,\n `**Stickers:** ${guild.stickers.cache.size}`,\n '',\n `🎯 **Server Features**`,\n `**Verification Level:** ${guild.verificationLevel}`,\n `**Content Filter:** ${guild.explicitContentFilter}`,\n `**2FA Requirement:** ${guild.mfaLevel === 1 ? 'Enabled' : 'Disabled'}`,\n `**Features:** ${features}`,\n ];\n\n if (guild.description) {\n detailedInfo.push(`**Description:** ${guild.description}`);\n }\n\n if (guild.vanityURLCode) {\n detailedInfo.push(`**Vanity URL:** discord.gg/${guild.vanityURLCode}`);\n }\n\n return [...basicInfo, ...detailedInfo].join('\\n');\n }\n\n return basicInfo.join('\\n');\n};\n\nexport const serverInfo: Action = {\n name: 'SERVER_INFO',\n similes: [\n 'SERVER_INFO',\n 'GUILD_INFO',\n 'SERVER_STATS',\n 'SERVER_DETAILS',\n 'ABOUT_SERVER',\n 'SERVER_INFORMATION',\n 'CHECK_SERVER',\n ],\n description:\n 'Get information about the current Discord server including member count, creation date, and other statistics.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n return message.content.source === 'discord';\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n await callback({\n text: 'Discord service is not available.',\n source: 'discord',\n });\n return;\n }\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n if (!room?.serverId) {\n await callback({\n text: \"I couldn't determine the current server.\",\n source: 'discord',\n });\n return;\n }\n\n const guild = await discordService.client.guilds.fetch(room.serverId);\n\n // Check if the request is for detailed info\n const messageText = message.content.text?.toLowerCase() || '';\n const isDetailed =\n messageText.includes('detailed') ||\n messageText.includes('full') ||\n messageText.includes('stats') ||\n messageText.includes('statistics');\n\n const infoText = formatServerInfo(guild, isDetailed);\n\n const response: Content = {\n text: infoText,\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n logger.error('Error getting server info:', error);\n await callback({\n text: 'I encountered an error while getting server information. Please try again.',\n source: 'discord',\n });\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'show server info',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll get the server information for you.\",\n actions: ['SERVER_INFO'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'what are the server stats?',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Let me fetch the server statistics.',\n actions: ['SERVER_INFO'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'give me detailed server information',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll provide detailed information about this server.\",\n actions: ['SERVER_INFO'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default serverInfo;\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 type { Character, EntityPayload, MessagePayload, WorldPayload } from '@elizaos/core';\nimport type {\n Client as DiscordJsClient,\n Interaction,\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 // /start event\n SLASH_START = 'DISCORD_SLASH_START',\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 * Discord-specific /start command payload\n */\nexport interface DiscordSlashStartPayload {\n interaction: Interaction;\n client: DiscordJsClient;\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.SLASH_START]: DiscordSlashStartPayload;\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\n// maybe discord character settings makes more sense?\nexport interface DiscordSettings {\n allowedChannelIds?: string[];\n shouldIgnoreBotMessages?: boolean;\n shouldIgnoreDirectMessages?: boolean;\n shouldRespondOnlyToMentions?: boolean;\n //[key: string]: any; // still allows extension\n}\n","import { getVoiceConnection } from '@discordjs/voice';\nimport type { IAgentRuntime, Memory, Provider, State, UUID } 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 as UUID);\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 { getDiscordSettings } from './environment';\nimport { MessageManager } from './messages';\nimport { DiscordEventTypes, type IDiscordService, type DiscordSettings } 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 discordSettings: DiscordSettings;\n private userSelections: Map<string, { [key: string]: any }> = new Map();\n private timeouts: NodeJS.Timeout[] = [];\n private clientReadyPromise: Promise<void>;\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 * Set of dynamically added channel IDs through joinChannel action.\n * These are merged with allowedChannelIds for runtime channel management.\n */\n private dynamicChannelIds: Set<string> = new Set();\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 // Load Discord settings with proper priority (env vars > character settings > defaults)\n this.discordSettings = getDiscordSettings(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?.trim && channelIdsRaw.trim()) {\n this.allowedChannelIds = channelIdsRaw\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n this.runtime.logger.debug('Locking down discord to', this.allowedChannelIds)\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 && token.trim() === '') {\n this.runtime.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.clientReadyPromise = new Promise(resolver => {\n this.client.once(Events.ClientReady, (readyClient) => {\n resolver()\n this.onReady(readyClient)\n });\n this.client.login(token).catch((error) => {\n this.runtime.logger.error(\n `Failed to login to Discord: ${error instanceof Error ? error.message : String(error)}`\n );\n this.client = null;\n });\n })\n\n this.setupEventListeners();\n this.registerSendHandler(); // Register handler during construction\n } catch (error) {\n runtime.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 // why we have this.runtime on the agent itself and this isn't a static\n runtime: IAgentRuntime,\n target: TargetInfo,\n content: Content\n ): Promise<void> {\n if (!this.client?.isReady()) {\n runtime.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 (target.channelId && this.allowedChannelIds && !this.isChannelAllowed(target.channelId)) {\n runtime.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 runtime.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 runtime.logger.error(\n `[Discord SendHandler] Error sending message: ${error instanceof Error ? error.message : String(error)} - Target: ${JSON.stringify(target)}, Content: ${JSON.stringify(content)}`\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 const listenCidsRaw: string | string[] | undefined = this.runtime.getSetting('DISCORD_LISTEN_CHANNEL_IDS');\n const listenCids = Array.isArray(listenCidsRaw)\n ? listenCidsRaw\n : (listenCidsRaw && typeof listenCidsRaw === 'string' && listenCidsRaw.trim())\n ? listenCidsRaw.trim().split(',').map(s => s.trim()).filter(s => s.length > 0)\n : []\n const talkCids = this.allowedChannelIds ?? [] // CHANNEL_IDS\n const allowedCids = [...listenCids, ...talkCids]\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 && this.discordSettings.shouldIgnoreBotMessages)\n ) {\n this.runtime.logger.info(\n `Got message where author is ${\n message.author.bot && this.discordSettings.shouldIgnoreBotMessages\n ? 'a bot. To reply anyway, set \\`shouldIgnoreBotMessages=true\\`.'\n : 'the current user. Ignore!'\n }`\n );\n return;\n }\n\n if (listenCids.includes(message.channel.id)) {\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 this.runtime.logger.warn('null channel type, discord message', message);\n }\n serverId = guild.id;\n } else {\n type = ChannelType.DM;\n // really can't be undefined because bootstrap's choice action\n serverId = message.channel.id;\n }\n\n // is this needed? just track who's in what room\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\n // only we just need to remember these messages\n const { processedContent, attachments } = await this.messageManager.processMessage(message);\n\n const messageId = createUniqueUuid(this.runtime, message.id);\n const sourceId = entityId; // needs to be based on message.author.id\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 fromBot: message.author.bot,\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 // do we need to duplicate this, we have it in content\n // source: \"discord\",\n sourceId,\n // why message? all Memories contain content (which is basically a message)\n // what are the other types? see MemoryType\n type: 'message', // MemoryType.MESSAGE\n // scope: `shared`, `private`, or `room\n // timestamp\n // tags\n },\n createdAt: message.createdTimestamp,\n };\n\n // and then you can handle these anyway you want\n this.runtime.emitEvent('DISCORD_LISTEN_CHANNEL_MESSAGE', {\n runtime: this.runtime,\n message: newMessage,\n });\n }\n\n // Skip if channel restrictions are set and this channel is not allowed\n if (this.allowedChannelIds && !this.isChannelAllowed(message.channel.id)) {\n // check first whether the channel is a thread...\n const channel = await this.client?.channels.fetch(message.channel.id);\n\n this.runtime.emitEvent('DISCORD_NOT_IN_CHANNELS_MESSAGE', {\n runtime: this.runtime,\n message: message,\n });\n\n if (!channel) {\n this.runtime.logger.error(`Channel id ${message.channel.id} not found. Ignore!`);\n return;\n }\n if (channel.isThread()) {\n if (!channel.parentId || !this.isChannelAllowed(channel.parentId)) {\n this.runtime.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 if (channel?.isTextBased()) {\n const channelLabel = 'name' in channel ? channel.name : channel.id;\n this.runtime.logger.debug(\n `Channel ${channelLabel} not allowed. Add the channel ${channel.id} to CHANNEL_IDS to enable replies.`\n );\n }\n return;\n }\n }\n\n try {\n // Ensure messageManager exists\n this.messageManager?.handleMessage(message);\n } catch (error) {\n this.runtime.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.isChannelAllowed(reaction.message.channel.id)\n ) {\n return;\n }\n try {\n await this.handleReactionAdd(reaction, user);\n } catch (error) {\n this.runtime.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.isChannelAllowed(reaction.message.channel.id)\n ) {\n return;\n }\n try {\n await this.handleReactionRemove(reaction, user);\n } catch (error) {\n this.runtime.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 this.runtime.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 this.runtime.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.isChannelAllowed(interaction.channelId)\n ) {\n return;\n }\n try {\n await this.handleInteractionCreate(interaction);\n } catch (error) {\n this.runtime.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 this.runtime.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 this.runtime.logger.log(`Joined guild ${guild.name}`);\n const fullGuild = await guild.fetch();\n // Disabled automatic voice joining - now controlled by joinVoiceChannel action\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 'start':\n // acknowledge it so it doesn't time out\n await interaction.deferReply(); // can't editReply unless we await\n this.runtime.emitEvent([DiscordEventTypes.SLASH_START], {\n interaction,\n client: this.client,\n });\n break;\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 this.runtime.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 this.runtime.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 this.runtime.logger.info(`Values selected: ${JSON.stringify(interaction.values)}`);\n this.runtime.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 this.runtime.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 this.runtime.logger.info('Button interaction detected');\n this.runtime.logger.info(`Button pressed by user ${userId}: ${interaction.customId}`);\n const formSelections = userSelections[messageId] || {};\n\n this.runtime.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 this.runtime.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 this.runtime.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 this.runtime.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 this.runtime.logger.warn(\n `Failed to get participants for channel ${channel.name}: ${error instanceof Error ? error.message : String(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 this.runtime.logger.info(\n `Using optimized user sync for large guild ${guild.name} (${guild.memberCount.toLocaleString()} 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 this.runtime.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 this.runtime.logger.error(`Error fetching members for ${guild.name}: ${error instanceof Error ? error.message : String(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 this.runtime.logger.error(`Error fetching members for ${guild.name}: ${error instanceof Error ? error.message : String(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(readyClient) {\n this.runtime.logger.success('DISCORD ON READY');\n\n // Register slash commands\n const commands = [\n {\n name: 'start',\n description: 'Perhaps get bot information',\n },\n // actions control access better\n /*\n {\n name: \"joinchannel\",\n description: \"Join a voice channel\",\n options: [\n {\n name: \"channel\",\n type: 7, // CHANNEL type\n description: \"The voice channel to join\",\n required: true,\n channel_types: [2], // GuildVoice type\n },\n ],\n },\n {\n name: \"leavechannel\",\n description: \"Leave the current voice channel\",\n },\n */\n ];\n try {\n if (this.client?.application) {\n // has 1 hour cache delay\n await this.client.application.commands.set(commands);\n }\n this.runtime.logger.success('Slash commands registered');\n } catch (error) {\n console.error('Error registering slash commands:', error);\n }\n\n // Required permissions for the bot\n const requiredPermissions = [\n // Text Permissions\n PermissionsBitField.Flags.ViewChannel,\n PermissionsBitField.Flags.SendMessages,\n PermissionsBitField.Flags.SendMessagesInThreads,\n PermissionsBitField.Flags.CreatePrivateThreads,\n PermissionsBitField.Flags.CreatePublicThreads,\n PermissionsBitField.Flags.EmbedLinks,\n PermissionsBitField.Flags.AttachFiles,\n PermissionsBitField.Flags.AddReactions,\n PermissionsBitField.Flags.UseExternalEmojis,\n PermissionsBitField.Flags.UseExternalStickers,\n PermissionsBitField.Flags.MentionEveryone,\n PermissionsBitField.Flags.ManageMessages,\n PermissionsBitField.Flags.ReadMessageHistory,\n // Voice Permissions\n PermissionsBitField.Flags.Connect,\n PermissionsBitField.Flags.Speak,\n PermissionsBitField.Flags.UseVAD,\n PermissionsBitField.Flags.PrioritySpeaker,\n ].reduce((a, b) => a | b, 0n);\n\n this.runtime.logger.log('Use this URL to add the bot to your server:');\n this.runtime.logger.log(\n `https://discord.com/api/oauth2/authorize?client_id=${readyClient.user?.id}&permissions=${requiredPermissions}&scope=bot%20applications.commands`\n );\n\n const guilds = await this.client?.guilds.fetch();\n if (!guilds) {\n this.runtime.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\n // accelerate updating commands\n await fullGuild.commands.set(commands);\n\n // Disabled automatic voice joining - now controlled by joinVoiceChannel action\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 this.runtime.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 this.runtime.logger.error(`Error during Discord world connection: ${error instanceof Error ? error.message : String(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 runtime.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 this.runtime.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(channelId)) as TextChannel;\n\n // Validate channel\n if (!channel) {\n this.runtime.logger.error(`Channel not found: ${channelId}`);\n return [];\n }\n\n if (channel.type !== DiscordChannelType.GuildText) {\n this.runtime.logger.error(`Channel ${channelId} is not a text channel`);\n return [];\n }\n\n const guild = channel.guild;\n if (!guild) {\n this.runtime.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 this.runtime.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 this.runtime.logger.info(`Using cached members (${guild.members.cache.size} members)`);\n members = guild.members.cache;\n } else {\n this.runtime.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 this.runtime.logger.error(`Error fetching members: ${error}`);\n // Fallback to cache if fetch fails\n members = guild.members.cache;\n this.runtime.logger.info(`Fallback to cache with ${members.size} members`);\n }\n }\n\n // Filter members by permission to view the channel\n this.runtime.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 this.runtime.logger.info(\n `Found ${channelMembers.length} members with access to channel ${channel.name}`\n );\n return channelMembers;\n } catch (error) {\n this.runtime.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 this.runtime.logger.log('Reaction added');\n\n // Early returns\n if (!reaction || !user) {\n this.runtime.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 this.runtime.logger.error(`Failed to fetch partial reaction: ${error instanceof Error ? error.message : String(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 this.runtime.logger.error(`Invalid user ID or room ID: ${entityId} ${roomId}`);\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 this.runtime.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 this.runtime.logger.error(`Error handling reaction: ${error instanceof Error ? error.message : String(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 this.runtime.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 this.runtime.logger.error(`Something went wrong when fetching the message: ${error instanceof Error ? error.message : String(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 this.runtime.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 this.runtime.logger.error(`Error handling reaction removal: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n /**\n * Checks if a channel ID is allowed based on both env config and dynamic additions.\n * @param {string} channelId - The channel ID to check\n * @returns {boolean} Whether the channel is allowed\n */\n public isChannelAllowed(channelId: string): boolean {\n // If no restrictions are set, allow all channels\n if (!this.allowedChannelIds) {\n return true;\n }\n\n // Check if channel is in the env-configured list or dynamically added\n return this.allowedChannelIds.includes(channelId) || this.dynamicChannelIds.has(channelId);\n }\n\n /**\n * Adds a channel to the dynamic allowed list.\n * @param {string} channelId - The channel ID to add\n * @returns {boolean} Whether the channel was successfully added\n */\n public addAllowedChannel(channelId: string): boolean {\n // Validate the channel exists\n if (!this.client?.channels.cache.has(channelId)) {\n return false;\n }\n\n this.dynamicChannelIds.add(channelId);\n return true;\n }\n\n /**\n * Removes a channel from the dynamic allowed list.\n * @param {string} channelId - The channel ID to remove\n * @returns {boolean} Whether the channel was in the list and removed\n */\n public removeAllowedChannel(channelId: string): boolean {\n // Don't allow removing channels that are in the env config\n if (this.allowedChannelIds?.includes(channelId)) {\n return false;\n }\n\n return this.dynamicChannelIds.delete(channelId);\n }\n\n /**\n * Gets the list of all allowed channels (env + dynamic).\n * @returns {string[]} Array of allowed channel IDs\n */\n public getAllowedChannels(): string[] {\n const envChannels = this.allowedChannelIds || [];\n const dynamicChannels = Array.from(this.dynamicChannelIds);\n return [...new Set([...envChannels, ...dynamicChannels])];\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 this.runtime.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 this.runtime.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 this.runtime.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 this.runtime.logger.warn(`Discord unhandled channel type: ${channel.type}`);\n return ChannelType.GROUP;\n }\n }\n}\n\nexport default DiscordService;\n","import type { IAgentRuntime } from '@elizaos/core';\nimport { parseBooleanFromText } from '@elizaos/core';\nimport { z } from 'zod';\nimport type { DiscordSettings } from './types';\n\n/**\n * Helper functions to get environment variables with proper defaults\n */\nfunction getEnvBoolean(name: string, fallback: boolean): boolean {\n const value = process.env?.[name];\n if (!value) return fallback;\n return value.toLowerCase() === 'true';\n}\n\nfunction getEnvArray(name: string, fallback: string[]): string[] {\n const value = process.env?.[name];\n if (!value || value.trim() === '') return fallback;\n return value.split(',').map(item => item.trim()).filter(item => item.length > 0);\n}\n\n/**\n * Default values that can be overridden by environment variables\n */\nexport const DISCORD_DEFAULTS = {\n SHOULD_IGNORE_BOT_MESSAGES: getEnvBoolean('DISCORD_SHOULD_IGNORE_BOT_MESSAGES', false),\n SHOULD_IGNORE_DIRECT_MESSAGES: getEnvBoolean('DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES', false),\n SHOULD_RESPOND_ONLY_TO_MENTIONS: getEnvBoolean('DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS', false),\n ALLOWED_CHANNEL_IDS: getEnvArray('CHANNEL_IDS', []),\n} as const;\n\nexport const discordEnvSchema = z.object({\n DISCORD_API_TOKEN: z.string().min(1, 'Discord API token is required'),\n /**\n * Comma-separated list of channel IDs to restrict the bot to.\n * If not set, the bot operates in all channels as usual.\n * These channels cannot be removed via the leaveChannel action.\n * Additional channels can be added dynamically via the joinChannel action.\n */\n CHANNEL_IDS: z\n .string()\n .nullish()\n .transform((val) =>\n val\n ? val\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0)\n : undefined\n ),\n DISCORD_SHOULD_IGNORE_BOT_MESSAGES: z\n .string()\n .nullish()\n .transform((val) => (val ? parseBooleanFromText(val) : undefined)),\n DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES: z\n .string()\n .nullish()\n .transform((val) => (val ? parseBooleanFromText(val) : undefined)),\n DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS: z\n .string()\n .nullish()\n .transform((val) => (val ? parseBooleanFromText(val) : undefined)),\n});\n\n/**\n * Represents the type of Discord configuration settings inferred from the discordEnvSchema.\n */\nexport type DiscordConfig = z.infer<typeof discordEnvSchema>;\n\n/**\n * Get Discord settings with proper priority:\n * 1. Runtime settings (environment variables via getSetting)\n * 2. Character settings\n * 3. Default values\n *\n * @param runtime - ElizaOS agent runtime instance\n * @returns Merged Discord settings\n */\nexport function getDiscordSettings(runtime: IAgentRuntime): DiscordSettings {\n const characterSettings = runtime.character.settings?.discord as DiscordSettings || {};\n\n // Helper to resolve setting value with priority: runtime > character > default\n const resolveSetting = <T>(\n envKey: string,\n characterValue: T | undefined,\n defaultValue: T,\n transform?: (value: string) => T\n ): T => {\n const runtimeValue = runtime.getSetting(envKey);\n // Treat null the same as undefined (some runtimes return null for missing settings)\n if (runtimeValue !== undefined && runtimeValue !== null) {\n // Coerce to string before transforming to handle non-string runtime values\n const normalized =\n typeof runtimeValue === 'string' ? runtimeValue : String(runtimeValue);\n return transform ? transform(normalized) : (runtimeValue as T);\n }\n return characterValue ?? defaultValue;\n };\n\n // Resolve allowedChannelIds separately to handle empty array case\n const resolvedAllowedChannelIds = resolveSetting<string[]>(\n 'CHANNEL_IDS',\n characterSettings.allowedChannelIds,\n DISCORD_DEFAULTS.ALLOWED_CHANNEL_IDS,\n (value: string) =>\n value\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0)\n );\n\n return {\n ...characterSettings,\n shouldIgnoreBotMessages: resolveSetting(\n 'DISCORD_SHOULD_IGNORE_BOT_MESSAGES',\n characterSettings.shouldIgnoreBotMessages,\n DISCORD_DEFAULTS.SHOULD_IGNORE_BOT_MESSAGES,\n parseBooleanFromText\n ),\n\n shouldIgnoreDirectMessages: resolveSetting(\n 'DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES',\n characterSettings.shouldIgnoreDirectMessages,\n DISCORD_DEFAULTS.SHOULD_IGNORE_DIRECT_MESSAGES,\n parseBooleanFromText\n ),\n\n shouldRespondOnlyToMentions: resolveSetting(\n 'DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS',\n characterSettings.shouldRespondOnlyToMentions,\n DISCORD_DEFAULTS.SHOULD_RESPOND_ONLY_TO_MENTIONS,\n parseBooleanFromText\n ),\n\n // Collapse empty allow-lists back to undefined to keep default open behavior\n allowedChannelIds:\n resolvedAllowedChannelIds.length > 0 ? resolvedAllowedChannelIds : undefined,\n };\n}\n\n/**\n * Validates the Discord configuration by retrieving the Discord API token from the runtime settings\n * and parsing it with the Discord environment schema.\n *\n * @param {IAgentRuntime} runtime The agent runtime instance.\n * @returns {Promise<DiscordConfig>} A promise that resolves with the validated Discord configuration.\n * @throws {Error} If the Discord configuration validation fails, an error with detailed error messages is thrown.\n */\nexport async function validateDiscordConfig(runtime: IAgentRuntime): Promise<DiscordConfig> {\n try {\n const config = {\n DISCORD_API_TOKEN: runtime.getSetting('DISCORD_API_TOKEN'),\n CHANNEL_IDS: runtime.getSetting('CHANNEL_IDS'),\n DISCORD_SHOULD_IGNORE_BOT_MESSAGES: runtime.getSetting('DISCORD_SHOULD_IGNORE_BOT_MESSAGES'),\n DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES: runtime.getSetting('DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES'),\n DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS: runtime.getSetting('DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS'),\n };\n\n return discordEnvSchema.parse(config);\n } catch (error) {\n if (error instanceof z.ZodError) {\n const errorMessages = error.issues\n .map((err) => `${err.path.join('.')}: ${err.message}`)\n .join('\\n');\n throw new Error(`Discord configuration validation failed:\\n${errorMessages}`);\n }\n throw error;\n }\n}\n","import {\n ChannelType,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Media,\n type Memory,\n ServiceType,\n type UUID,\n MemoryType,\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 { getDiscordSettings } from './environment';\nimport { DiscordSettings } 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 private discordSettings: DiscordSettings;\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 // Load Discord settings with proper priority (env vars > character settings > defaults)\n this.discordSettings = getDiscordSettings(this.runtime);\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 // this filtering is already done in setupEventListeners\n /*\n if (\n this.discordSettings.allowedChannelIds?.length &&\n !this.discordSettings.allowedChannelIds.some((id: string) => id === message.channel.id)\n ) {\n return;\n }\n */\n\n if (message.interaction || message.author.id === this.client.user?.id) {\n return;\n }\n\n if (this.discordSettings.shouldIgnoreBotMessages && message.author?.bot) {\n return;\n }\n\n if (\n this.discordSettings.shouldIgnoreDirectMessages &&\n message.channel.type === DiscordChannelType.DM\n ) {\n return;\n }\n\n const isBotMentioned = !!(\n this.client.user?.id && message.mentions.users?.has(this.client.user.id)\n );\n const isReplyToBot =\n !!message.reference?.messageId && message.mentions.repliedUser?.id === this.client.user?.id;\n const isInThread = message.channel.isThread();\n const isDM = message.channel.type === DiscordChannelType.DM;\n\n if (this.discordSettings.shouldRespondOnlyToMentions) {\n const shouldProcess = isDM || isBotMentioned || isReplyToBot;\n\n if (!shouldProcess) {\n logger.debug('[Discord] Strict mode: ignoring message (no @mention or reply)');\n return;\n }\n\n logger.debug('[Discord] Strict mode: processing message (has @mention or reply)');\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.id);\n }\n serverId = guild.id;\n } else {\n type = ChannelType.DM;\n // really can't be undefined because bootstrap's choice action\n serverId = message.channel.id;\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.reason || undefined\n );\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 messageId = createUniqueUuid(this.runtime, message.id);\n\n const channel = message.channel as TextChannel;\n\n // Store the typing data to be used by the callback\n const typingData = {\n interval: null as NodeJS.Timeout | null,\n cleared: false,\n started: false,\n };\n\n const sourceId = entityId; // needs to be based on message.author.id\n\n const newMessage: Memory = {\n id: messageId,\n entityId: entityId,\n agentId: this.runtime.agentId,\n roomId: roomId,\n content: {\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 mentionContext: {\n isMention: isBotMentioned,\n isReply: isReplyToBot,\n isThread: isInThread,\n mentionType: isBotMentioned\n ? 'platform_mention'\n : isReplyToBot\n ? 'reply'\n : isInThread\n ? 'thread'\n : 'none',\n },\n },\n // metadata of memory\n metadata: {\n entityName: name,\n fromBot: message.author.bot,\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 // do we need to duplicate this, we have it in content\n // source: \"discord\",\n sourceId,\n // why message? all Memories contain content (which is basically a message)\n // what are the other types? see MemoryType\n type: MemoryType.MESSAGE,\n // scope: `shared`, `private`, or `room\n // timestamp\n // tags\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 // not addressed to us\n if (\n content.target &&\n typeof content.target === 'string' &&\n content.target.toLowerCase() !== 'discord'\n ) {\n return [];\n }\n\n // Start typing indicator only when we're actually going to respond\n if (!typingData.started) {\n typingData.started = true;\n\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:', String(err));\n }\n };\n\n // Start typing immediately\n startTyping();\n\n // Create interval to keep the typing indicator active while processing\n typingData.interval = setInterval(startTyping, 8000);\n\n // Add a small delay to ensure typing indicator is visible\n // This simulates the bot \"thinking\" before responding\n await new Promise((resolve) => setTimeout(resolve, 1500));\n }\n\n if (message.id && !content.inReplyTo) {\n content.inReplyTo = createUniqueUuid(this.runtime, message.id);\n }\n\n let messages: any[] = [];\n if (content?.channelType === '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 await 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 when done\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 // Clear typing indicator on error\n if (typingData.interval && !typingData.cleared) {\n clearInterval(typingData.interval);\n typingData.cleared = true;\n }\n return [];\n }\n };\n\n // Call the message handler directly instead of emitting events\n // This provides a clearer, more traceable flow for message processing\n await this.runtime.messageService.handleMessage(this.runtime, newMessage, callback);\n\n // Failsafe: clear typing indicator after 30 seconds if it was started and something goes wrong\n setTimeout(() => {\n if (typingData.started && typingData.interval && !typingData.cleared) {\n clearInterval(typingData.interval);\n typingData.cleared = true;\n logger.warn('Typing indicator failsafe timeout triggered');\n }\n }, 30000);\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 if (message.embeds.length) {\n for (const i in message.embeds) {\n const embed = message.embeds[i];\n // type: rich\n processedContent += '\\nEmbed #' + (parseInt(i) + 1) + ':\\n';\n processedContent += ' Title:' + (embed.title ?? '(none)') + '\\n';\n processedContent += ' Description:' + (embed.description ?? '(none)') + '\\n';\n }\n }\n if (message.reference && message.reference.messageId) {\n const messageId = createUniqueUuid(this.runtime, message.reference.messageId);\n // context currently doesn't know message ID\n processedContent +=\n '\\nReferencing MessageID ' + messageId + ' (discord: ' + message.reference.messageId + ')';\n // in our channel\n if (message.reference.channelId !== message.channel.id) {\n const roomId = createUniqueUuid(this.runtime, message.reference.channelId);\n processedContent += ' in channel ' + roomId;\n }\n // in our guild\n if (\n message.reference.guildId &&\n message.guild &&\n message.reference.guildId !== message.guild.id\n ) {\n processedContent += ' in guild ' + message.reference.guildId;\n }\n processedContent += '\\n';\n }\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 {\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: { channels: number; rate: number; frameSize: number }) {\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(\n 'Could not generate dependency report:',\n reportError instanceof Error ? reportError.message : String(reportError)\n );\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((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(\n 'Voice connection error:',\n error instanceof Error ? error.message : String(error)\n );\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(\n 'Failed to modify voice state:',\n error instanceof Error ? error.message : String(error)\n );\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(\n 'Failed to establish voice connection:',\n error instanceof Error ? error.message : String(error)\n );\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 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(`[monitorMember] Failed to create opus decoder for user ${entityId}: ${error}`);\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(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\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 // Process voice message through message service\n await this.runtime.messageService.handleMessage(this.runtime, memory, callback);\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","ModelType","composePromptFromState","parseJSONObjectFromText","composePromptFromState","ModelType","parseJSONObjectFromText","member","ModelType","composePromptFromState","parseJSONObjectFromText","createUniqueUuid","DiscordChannelType","composePromptFromState","ModelType","parseJSONObjectFromText","findChannel","createUniqueUuid","ModelType","composePromptFromState","parseJSONObjectFromText","logger","composePromptFromState","ModelType","parseJSONObjectFromText","logger","ModelType","composePromptFromState","parseJSONObjectFromText","composePromptFromState","ModelType","parseJSONObjectFromText","fs","ModelType","composePromptFromState","parseJSONObjectFromText","trimTokens","summarizationTemplate","ModelType","composePromptFromState","parseJSONObjectFromText","composePromptFromState","ModelType","parseJSONObjectFromText","attachment","ModelType","composePromptFromState","parseJSONObjectFromText","logger","composePromptFromState","ModelType","parseJSONObjectFromText","logger","ModelType","composePromptFromState","parseJSONObjectFromText","logger","composePromptFromState","ModelType","parseJSONObjectFromText","logger","ModelType","composePromptFromState","parseJSONObjectFromText","logger","composePromptFromState","ModelType","parseJSONObjectFromText","serverInfo","logger","ModelType","composePromptFromState","parseJSONObjectFromText","logger","composePromptFromState","ModelType","parseJSONObjectFromText","logger","ModelType","composePromptFromState","parseJSONObjectFromText","logger","PermissionsBitField","composePromptFromState","ModelType","parseJSONObjectFromText","logger","ModelType","composePromptFromState","parseJSONObjectFromText","logger","PermissionsBitField","getMessageRef","composePromptFromState","ModelType","parseJSONObjectFromText","logger","logger","logger","ChannelType","ServiceType","ChannelType","ServiceType","ChannelType","ChannelType","createUniqueUuid","logger","DiscordChannelType","PermissionsBitField","ChannelType","ServiceType","createUniqueUuid","logger","DiscordChannelType","fs","trimTokens","parseJSONObjectFromText","ModelType","ServiceType","ModelType","logger","parseJSONObjectFromText","trimTokens","ChannelType","PermissionsBitField","logger","ChannelType","PermissionsBitField","DiscordChannelType","logger","createUniqueUuid","ChannelType","match","ServiceType","ChannelType","ModelType","createUniqueUuid","logger","DiscordChannelType","connection","isValidTranscription","createUniqueUuid","ChannelType","DiscordChannelType","PermissionsBitField","fullGuild","logger","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/joinChannel.ts","../src/constants.ts","../src/actions/leaveChannel.ts","../src/actions/listChannels.ts","../src/actions/readChannel.ts","../src/actions/sendDM.ts","../src/actions/summarizeConversation.ts","../src/actions/transcribeMedia.ts","../src/actions/searchMessages.ts","../src/actions/createPoll.ts","../src/actions/getUserInfo.ts","../src/actions/reactToMessage.ts","../src/actions/pinMessage.ts","../src/actions/unpinMessage.ts","../src/actions/serverInfo.ts","../src/providers/channelState.ts","../src/types.ts","../src/providers/voiceState.ts","../src/service.ts","../src/environment.ts","../src/messages.ts","../src/attachments.ts","../src/utils.ts","../src/permissions.ts","../src/voice.ts","../src/permissionEvents.ts","../src/compat.ts","../src/tests.ts","../src/banner.ts"],"sourcesContent":["import { type IAgentRuntime, type Plugin, logger } from '@elizaos/core';\nimport chatWithAttachments from './actions/chatWithAttachments';\nimport { downloadMedia } from './actions/downloadMedia';\nimport joinChannel from './actions/joinChannel';\nimport leaveChannel from './actions/leaveChannel';\nimport listChannels from './actions/listChannels';\nimport readChannel from './actions/readChannel';\nimport sendDM from './actions/sendDM';\nimport { summarize } from './actions/summarizeConversation';\nimport { transcribeMedia } from './actions/transcribeMedia';\nimport searchMessages from './actions/searchMessages';\nimport createPoll from './actions/createPoll';\nimport getUserInfo from './actions/getUserInfo';\nimport reactToMessage from './actions/reactToMessage';\nimport pinMessage from './actions/pinMessage';\nimport unpinMessage from './actions/unpinMessage';\nimport serverInfo from './actions/serverInfo';\n\nimport { channelStateProvider } from './providers/channelState';\nimport { voiceStateProvider } from './providers/voiceState';\nimport { DiscordService } from './service';\nimport { DiscordTestSuite } from './tests';\nimport { printBanner } from './banner';\nimport { getPermissionValues } from './permissions';\n\nconst discordPlugin: Plugin = {\n name: 'discord',\n description: 'Discord service plugin for integration with Discord servers and channels',\n services: [DiscordService],\n actions: [\n chatWithAttachments,\n downloadMedia,\n joinChannel,\n leaveChannel,\n listChannels,\n readChannel,\n sendDM,\n summarize,\n transcribeMedia,\n searchMessages,\n createPoll,\n getUserInfo,\n reactToMessage,\n pinMessage,\n unpinMessage,\n serverInfo,\n ],\n providers: [channelStateProvider, voiceStateProvider],\n tests: [new DiscordTestSuite()],\n init: async (_config: Record<string, string>, runtime: IAgentRuntime) => {\n // Gather ALL Discord settings\n const token = runtime.getSetting('DISCORD_API_TOKEN') as string;\n const applicationId = runtime.getSetting('DISCORD_APPLICATION_ID') as string;\n const voiceChannelId = runtime.getSetting('DISCORD_VOICE_CHANNEL_ID') as string;\n const channelIds = runtime.getSetting('CHANNEL_IDS') as string;\n const listenChannelIds = runtime.getSetting('DISCORD_LISTEN_CHANNEL_IDS') as string;\n const ignoreBotMessages = runtime.getSetting('DISCORD_SHOULD_IGNORE_BOT_MESSAGES') as string;\n const ignoreDirectMessages = runtime.getSetting('DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES') as string;\n const respondOnlyToMentions = runtime.getSetting('DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS') as string;\n\n // Print beautiful settings banner with ALL settings\n // Includes tiered permission matrix for Discord invite URLs:\n // - Basic / Moderator / Admin (role levels)\n // - With or without voice permissions\n printBanner({\n pluginName: 'plugin-discord',\n description: 'Discord bot integration for servers and channels',\n applicationId: applicationId || undefined,\n discordPermissions: applicationId ? getPermissionValues() : undefined,\n settings: [\n {\n name: 'DISCORD_API_TOKEN',\n value: token,\n sensitive: true,\n required: true,\n },\n {\n name: 'DISCORD_APPLICATION_ID',\n value: applicationId,\n },\n {\n name: 'DISCORD_VOICE_CHANNEL_ID',\n value: voiceChannelId,\n },\n {\n name: 'CHANNEL_IDS',\n value: channelIds,\n },\n {\n name: 'DISCORD_LISTEN_CHANNEL_IDS',\n value: listenChannelIds,\n },\n {\n name: 'DISCORD_SHOULD_IGNORE_BOT_MESSAGES',\n value: ignoreBotMessages,\n defaultValue: 'false',\n },\n {\n name: 'DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES',\n value: ignoreDirectMessages,\n defaultValue: 'false',\n },\n {\n name: 'DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS',\n value: respondOnlyToMentions,\n defaultValue: 'false',\n },\n ],\n runtime,\n });\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\n// Export additional items for use by other plugins\n// IDiscordService? from ./types\nexport { DISCORD_SERVICE_NAME } from './constants';\nexport { DiscordService } from './service';\nexport type { DiscordService as IDiscordService } from './service';\n\n// Export event types and payload interfaces for external consumers\nexport { DiscordEventTypes } from './types';\nexport type {\n PermissionState,\n PermissionDiff,\n AuditInfo,\n PermissionPayloadRuntime,\n ChannelPermissionsChangedPayload,\n RolePermissionsChangedPayload,\n MemberRolesChangedPayload,\n RoleLifecyclePayload,\n} from './types';\n\n// Export permission utilities for external consumers\nexport {\n ELEVATED_PERMISSIONS,\n isElevatedRole,\n hasElevatedPermissions,\n} from './permissionEvents';\n\n// Export permission tier system for invite URL generation\nexport {\n DiscordPermissionTiers,\n generateInviteUrl,\n generateAllInviteUrls,\n getPermissionValues,\n type DiscordPermissionTier,\n type DiscordPermissionValues,\n} from './permissions';\n","import fs from 'node:fs';\nimport {\n type Action,\n type ActionExample,\n ChannelType,\n ContentType,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Media,\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 runtime.logger.warn({ src: 'plugin:discord:action:chat-with-attachments', agentId: runtime.agentId }, 'Could not 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 runtime.logger.warn({ src: 'plugin:discord:action:chat-with-attachments', agentId: runtime.agentId }, 'No summary found');\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 ...callbackData,\n text: 'I\\'ve attached the summary of the requested attachments as a text file.',\n attachments: [\n ...(callbackData.attachments || []),\n {\n id: summaryFilename,\n url: summaryFilename,\n title: 'Summary',\n source: 'discord',\n contentType: ContentType.DOCUMENT,\n } as Media,\n ],\n });\n } catch (error) {\n runtime.logger.error({ src: 'plugin:discord:action:chat-with-attachments', agentId: runtime.agentId, error: error instanceof Error ? error.message : String(error) }, 'Error in file/cache process');\n throw error;\n }\n } else {\n runtime.logger.warn({ src: 'plugin:discord:action:chat-with-attachments', agentId: runtime.agentId }, 'Empty response from chat with attachments action');\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 ContentType,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Media,\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 runtime.logger.error({ src: 'plugin:discord:action:download-media', agentId: runtime.agentId }, 'Video service not found');\n return;\n }\n\n const mediaUrl = await getMediaUrl(runtime, message, state);\n if (!mediaUrl) {\n runtime.logger.warn({ src: 'plugin:discord:action:download-media', agentId: runtime.agentId }, 'Could not 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 ...response,\n attachments: [\n ...(response.attachments || []),\n {\n id: mediaPath,\n url: mediaPath,\n title: 'Downloaded Media',\n source: 'discord',\n contentType: ContentType.DOCUMENT,\n } as Media,\n ],\n });\n break;\n } catch (error) {\n retries++;\n runtime.logger.error({ src: 'plugin:discord:action:download-media', agentId: runtime.agentId, attempt: retries, error: error instanceof Error ? error.message : String(error) }, 'Error sending message');\n\n if (retries === maxRetries) {\n runtime.logger.error({ src: 'plugin:discord:action:download-media', agentId: runtime.agentId, maxRetries }, '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 {\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 parseJSONObjectFromText,\n createUniqueUuid,\n} from \"@elizaos/core\";\nimport { DiscordService } from \"../service\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport type { TextChannel, BaseGuildVoiceChannel } from \"discord.js\";\nimport { ChannelType as DiscordChannelType } from \"discord.js\";\nimport type { VoiceManager } from \"../voice\";\n\n/**\n * Template for extracting channel information from the user's request to join a channel.\n *\n * @type {string}\n * @description This template is used to determine which channel the user wants the bot to start listening to or join.\n *\n * @param {string} recentMessages - Placeholder for recent messages related to the request.\n * @param {string} senderName - Name of the sender requesting to join a channel.\n *\n * @returns {string} - Formatted template with instructions and JSON structure for response.\n */\nexport const joinChannelTemplate = `# Messages we are searching for channel join information\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting the bot to join a specific Discord channel (text or voice). Your goal is to determine which channel they want to join.\n\nExtract the channel identifier from their request:\n- If they mention a channel like #general or <#channelid>, extract that\n- If they provide a channel name, extract that\n- If they provide a channel ID (long number), extract that\n- If they mention \"voice\", \"vc\", \"voice channel\", include that as a hint\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"channelIdentifier\": \"<channel-name|channel-id|#mention>\",\n \"isVoiceChannel\": true/false\n}\n\\`\\`\\`\n`;\n\n/**\n * Get channel information from the user's request\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<{channelIdentifier: string, isVoiceChannel: boolean} | null>} Channel info or null if not parseable.\n */\nconst getJoinChannelInfo = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State,\n): Promise<{ channelIdentifier: string; isVoiceChannel: boolean } | null> => {\n const prompt = composePromptFromState({\n state,\n template: joinChannelTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response) as {\n channelIdentifier: string;\n isVoiceChannel: boolean;\n } | null;\n\n if (parsedResponse?.channelIdentifier) {\n return parsedResponse;\n }\n }\n return null;\n};\n\n/**\n * Find a Discord channel by various identifiers\n * @param {DiscordService} discordService - The Discord service instance\n * @param {string} identifier - The channel identifier (name, ID, or mention)\n * @param {string} currentServerId - The current server ID to search in\n * @param {boolean} isVoiceChannel - Whether to look for voice channels\n * @returns {Promise<TextChannel | BaseGuildVoiceChannel | null>} The found channel or null\n */\nconst findChannel = async (\n discordService: DiscordService,\n identifier: string,\n currentServerId?: string,\n isVoiceChannel?: boolean,\n): Promise<TextChannel | BaseGuildVoiceChannel | null> => {\n if (!discordService.client) {\n return null;\n }\n\n // Remove channel mention formatting if present\n const cleanId = identifier.replace(/[<#>]/g, \"\");\n\n try {\n // Try to fetch by ID first\n if (/^\\d+$/.test(cleanId)) {\n try {\n const channel = await discordService.client.channels.fetch(cleanId);\n if (isVoiceChannel && channel?.type === DiscordChannelType.GuildVoice) {\n return channel as BaseGuildVoiceChannel;\n } else if (\n !isVoiceChannel &&\n channel?.isTextBased() &&\n !channel.isVoiceBased()\n ) {\n return channel as TextChannel;\n }\n } catch (e) {\n // ID not found, continue to name search\n }\n }\n\n // Search in the current server if available\n if (currentServerId) {\n const guild = await discordService.client.guilds.fetch(currentServerId);\n const channels = await guild.channels.fetch();\n\n // Search by channel name\n const channel = channels.find((ch) => {\n const nameMatch =\n ch?.name.toLowerCase() === identifier.toLowerCase() ||\n ch?.name.toLowerCase().replace(/[^a-z0-9 ]/g, \"\") ===\n identifier.toLowerCase().replace(/[^a-z0-9 ]/g, \"\");\n\n if (isVoiceChannel) {\n return nameMatch && ch.type === DiscordChannelType.GuildVoice;\n } else {\n return nameMatch && ch.isTextBased() && !ch.isVoiceBased();\n }\n });\n\n if (channel) {\n return channel as TextChannel | BaseGuildVoiceChannel;\n }\n }\n\n // Search in all guilds the bot is in\n const guilds = Array.from(discordService.client.guilds.cache.values());\n for (const guild of guilds) {\n try {\n const channels = await guild.channels.fetch();\n const channel = channels.find((ch) => {\n const nameMatch =\n ch?.name.toLowerCase() === identifier.toLowerCase() ||\n ch?.name.toLowerCase().replace(/[^a-z0-9 ]/g, \"\") ===\n identifier.toLowerCase().replace(/[^a-z0-9 ]/g, \"\");\n\n if (isVoiceChannel) {\n return nameMatch && ch.type === DiscordChannelType.GuildVoice;\n } else {\n return nameMatch && ch.isTextBased() && !ch.isVoiceBased();\n }\n });\n\n if (channel) {\n return channel as TextChannel | BaseGuildVoiceChannel;\n }\n } catch (e) {\n // Continue searching in other guilds\n }\n }\n\n return null;\n } catch (error) {\n // Note: Standalone function without runtime context - error handled by caller\n return null;\n }\n};\n\nexport const joinChannel: Action = {\n name: \"JOIN_CHANNEL\",\n similes: [\n \"START_LISTENING_CHANNEL\",\n \"LISTEN_TO_CHANNEL\",\n \"ADD_CHANNEL\",\n \"WATCH_CHANNEL\",\n \"MONITOR_CHANNEL\",\n \"JOIN_TEXT_CHANNEL\",\n \"JOIN_VOICE\",\n \"JOIN_VC\",\n \"JOIN_VOICE_CHAT\",\n \"JOIN_VOICE_CHANNEL\",\n \"HOP_IN_VOICE\",\n \"ENTER_VOICE_CHANNEL\",\n ],\n description:\n \"Join a Discord channel - either text (to monitor messages) or voice (to participate in voice chat). You have full voice capabilities!\",\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n if (message.content.source !== \"discord\") {\n return false;\n }\n return true;\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback,\n ) => {\n const discordService = runtime.getService(\n DISCORD_SERVICE_NAME,\n ) as DiscordService;\n\n if (!discordService || !discordService.client) {\n runtime.logger.error(\n { src: \"plugin:discord:action:join-channel\", agentId: runtime.agentId },\n \"Discord service not found or not initialized\",\n );\n return;\n }\n\n const channelInfo = await getJoinChannelInfo(runtime, message, state);\n if (!channelInfo) {\n runtime.logger.warn(\n { src: \"plugin:discord:action:join-channel\", agentId: runtime.agentId },\n \"Could not parse channel information from message\",\n );\n await callback({\n text: \"I couldn't understand which channel you want me to join. Please specify the channel name or ID.\",\n source: \"discord\",\n });\n return;\n }\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n const currentServerId = room?.messageServerId;\n\n // First, try the user's approach - if they said voice/vc, look for voice channels\n const messageText = message.content.text?.toLowerCase() || \"\";\n const isVoiceRequest =\n channelInfo.isVoiceChannel ||\n messageText.includes(\"voice\") ||\n messageText.includes(\"vc\") ||\n messageText.includes(\"hop in\");\n\n // Find the channel (try voice first if it's a voice request)\n let targetChannel = isVoiceRequest\n ? await findChannel(\n discordService,\n channelInfo.channelIdentifier,\n currentServerId,\n true,\n )\n : await findChannel(\n discordService,\n channelInfo.channelIdentifier,\n currentServerId,\n false,\n );\n\n // If not found, try the opposite type\n if (!targetChannel) {\n targetChannel = isVoiceRequest\n ? await findChannel(\n discordService,\n channelInfo.channelIdentifier,\n currentServerId,\n false,\n )\n : await findChannel(\n discordService,\n channelInfo.channelIdentifier,\n currentServerId,\n true,\n );\n }\n\n if (!targetChannel) {\n // If the user is in a voice channel and no specific channel was found, join their voice channel\n if (isVoiceRequest && currentServerId) {\n const guild = discordService.client.guilds.cache.get(currentServerId);\n const members = guild?.members.cache;\n const member = members?.find(\n (member) =>\n createUniqueUuid(runtime, member.id) === message.entityId,\n );\n\n if (member?.voice?.channel) {\n targetChannel = member.voice.channel as BaseGuildVoiceChannel;\n }\n }\n }\n\n if (!targetChannel) {\n await callback({\n text: `I couldn't find a channel with the identifier \"${channelInfo.channelIdentifier}\". Please make sure the channel name or ID is correct and I have access to it.`,\n source: \"discord\",\n });\n return;\n }\n\n // Handle voice channels\n if (targetChannel.type === DiscordChannelType.GuildVoice) {\n const voiceChannel = targetChannel as BaseGuildVoiceChannel;\n const voiceManager = discordService.voiceManager as VoiceManager;\n\n if (!voiceManager) {\n await callback({\n text: \"Voice functionality is not available at the moment.\",\n source: \"discord\",\n });\n return;\n }\n\n // Join the voice channel\n await voiceManager.joinChannel(voiceChannel);\n\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 ${voiceChannel.name}`,\n actions: [\"JOIN_VOICE_STARTED\"],\n },\n metadata: {\n type: \"JOIN_VOICE\",\n },\n },\n \"messages\",\n );\n\n const response: Content = {\n text: `I've joined the voice channel ${voiceChannel.name}!`,\n actions: [\"JOIN_CHANNEL_RESPONSE\"],\n source: message.content.source,\n };\n\n await callback(response);\n } else {\n // Handle text channels\n const textChannel = targetChannel as TextChannel;\n\n // Check if we're already listening to this channel\n const currentChannels = discordService.getAllowedChannels();\n if (currentChannels.includes(textChannel.id)) {\n await callback({\n text: `I'm already listening to ${textChannel.name} (<#${textChannel.id}>).`,\n source: \"discord\",\n });\n return;\n }\n\n // Add the channel to the allowed list\n const success = discordService.addAllowedChannel(textChannel.id);\n\n if (success) {\n const response: Content = {\n text: `I've started listening to ${textChannel.name} (<#${textChannel.id}>). I'll now respond to messages in that channel.`,\n actions: [\"JOIN_CHANNEL_RESPONSE\"],\n source: message.content.source,\n };\n\n await callback(response);\n } else {\n await callback({\n text: `I couldn't add ${textChannel.name} to my listening list. Please try again.`,\n source: \"discord\",\n });\n }\n }\n } catch (error) {\n runtime.logger.error(\n {\n src: \"plugin:discord:action:join-channel\",\n agentId: runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error joining channel\",\n );\n await callback({\n text: \"I encountered an error while trying to join the channel. Please make sure I have the necessary permissions.\",\n source: \"discord\",\n });\n }\n },\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Start listening to #general\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll start listening to the #general channel.\",\n actions: [\"JOIN_CHANNEL\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"join the dev-voice channel\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll join the dev-voice channel right away!\",\n actions: [\"JOIN_CHANNEL\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"hop in vc\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Joining your voice channel now!\",\n actions: [\"JOIN_CHANNEL\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Can you join the announcements channel?\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll join the announcements channel and start monitoring messages there.\",\n actions: [\"JOIN_CHANNEL\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Please monitor channel 123456789012345678\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll start monitoring that channel for messages.\",\n actions: [\"JOIN_CHANNEL\"],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default joinChannel;\n","export const DISCORD_SERVICE_NAME = 'discord';\n","import {\n type Action,\n type ActionExample,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n type ActionResult,\n ModelType,\n type State,\n composePromptFromState,\n parseJSONObjectFromText,\n createUniqueUuid,\n} from \"@elizaos/core\";\nimport { DiscordService } from \"../service\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport { type TextChannel, BaseGuildVoiceChannel } from \"discord.js\";\nimport { ChannelType as DiscordChannelType } from \"discord.js\";\nimport type { VoiceManager } from \"../voice\";\n\n/**\n * Template for extracting channel information from the user's request to leave a channel.\n *\n * @type {string}\n * @description This template is used to determine which channel the user wants the bot to stop listening to or leave.\n *\n * @param {string} recentMessages - Placeholder for recent messages related to the request.\n * @param {string} senderName - Name of the sender requesting to leave a channel.\n *\n * @returns {string} - Formatted template with instructions and JSON structure for response.\n */\nexport const leaveChannelTemplate = `# Messages we are searching for channel leave information\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting the bot to leave a specific Discord channel (text or voice). Your goal is to determine which channel they want to leave.\n\nExtract the channel identifier from their request:\n- If they mention a channel like #general or <#channelid>, extract that\n- If they provide a channel name (like \"dev-voice\" or \"general\"), extract just the name\n- If they provide a channel ID (long number), extract that\n- If they say \"this channel\" or \"here\", use \"current\"\n- If they don't specify a channel but mention \"voice\", \"vc\", use \"current\" and mark as voice\n\nExamples:\n- \"leave the dev-voice channel\" -> channelIdentifier: \"dev-voice\", isVoiceChannel: true\n- \"leave #general\" -> channelIdentifier: \"general\", isVoiceChannel: false\n- \"leave voice\" -> channelIdentifier: \"current\", isVoiceChannel: true\n- \"stop listening to this channel\" -> channelIdentifier: \"current\", isVoiceChannel: false\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"channelIdentifier\": \"<actual-channel-name-or-id-or-current>\",\n \"isVoiceChannel\": true/false\n}\n\\`\\`\\`\n`;\n\n/**\n * Get channel information from the user's request\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<{channelIdentifier: string, isVoiceChannel: boolean} | null>} Channel info or null if not parseable.\n */\nconst getLeaveChannelInfo = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State,\n): Promise<{ channelIdentifier: string; isVoiceChannel: boolean } | null> => {\n const prompt = composePromptFromState({\n state,\n template: leaveChannelTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response) as {\n channelIdentifier: string;\n isVoiceChannel: boolean;\n } | null;\n\n if (parsedResponse?.channelIdentifier) {\n return parsedResponse;\n }\n }\n return null;\n};\n\n/**\n * Find a Discord channel by various identifiers\n * @param {DiscordService} discordService - The Discord service instance\n * @param {string} identifier - The channel identifier (name, ID, or mention)\n * @param {string} currentChannelId - The current channel ID if \"current\" is specified\n * @param {string} currentServerId - The current server ID to search in\n * @param {boolean} isVoiceChannel - Whether to look for voice channels\n * @returns {Promise<TextChannel | BaseGuildVoiceChannel | null>} The found channel or null\n */\nconst findChannel = async (\n discordService: DiscordService,\n identifier: string,\n currentChannelId?: string,\n currentServerId?: string,\n isVoiceChannel?: boolean,\n): Promise<TextChannel | BaseGuildVoiceChannel | null> => {\n if (!discordService.client) {\n return null;\n }\n\n // Handle \"current\" channel\n if (identifier === \"current\" && currentChannelId) {\n try {\n const channel =\n await discordService.client.channels.fetch(currentChannelId);\n if (isVoiceChannel && channel?.type === DiscordChannelType.GuildVoice) {\n return channel as BaseGuildVoiceChannel;\n } else if (\n !isVoiceChannel &&\n channel?.isTextBased() &&\n !channel.isVoiceBased()\n ) {\n return channel as TextChannel;\n }\n } catch (e) {\n // Current channel not found\n }\n }\n\n // Remove channel mention formatting if present\n const cleanId = identifier.replace(/[<#>]/g, \"\");\n\n try {\n // Try to fetch by ID first\n if (/^\\d+$/.test(cleanId)) {\n try {\n const channel = await discordService.client.channels.fetch(cleanId);\n if (isVoiceChannel && channel?.type === DiscordChannelType.GuildVoice) {\n return channel as BaseGuildVoiceChannel;\n } else if (\n !isVoiceChannel &&\n channel?.isTextBased() &&\n !channel.isVoiceBased()\n ) {\n return channel as TextChannel;\n }\n } catch (e) {\n // ID not found, continue to name search\n }\n }\n\n // Search in the current server if available\n if (currentServerId) {\n const guild = await discordService.client.guilds.fetch(currentServerId);\n const channels = await guild.channels.fetch();\n\n // Search by channel name\n const channel = channels.find((ch) => {\n const nameMatch =\n ch?.name.toLowerCase() === identifier.toLowerCase() ||\n ch?.name.toLowerCase().replace(/[^a-z0-9 ]/g, \"\") ===\n identifier.toLowerCase().replace(/[^a-z0-9 ]/g, \"\");\n\n if (isVoiceChannel) {\n return nameMatch && ch.type === DiscordChannelType.GuildVoice;\n } else {\n return nameMatch && ch.isTextBased() && !ch.isVoiceBased();\n }\n });\n\n if (channel) {\n return channel as TextChannel | BaseGuildVoiceChannel;\n }\n }\n\n // Search in all guilds the bot is in\n const guilds = Array.from(discordService.client.guilds.cache.values());\n for (const guild of guilds) {\n try {\n const channels = await guild.channels.fetch();\n const channel = channels.find((ch) => {\n const nameMatch =\n ch?.name.toLowerCase() === identifier.toLowerCase() ||\n ch?.name.toLowerCase().replace(/[^a-z0-9 ]/g, \"\") ===\n identifier.toLowerCase().replace(/[^a-z0-9 ]/g, \"\");\n\n if (isVoiceChannel) {\n return nameMatch && ch.type === DiscordChannelType.GuildVoice;\n } else {\n return nameMatch && ch.isTextBased() && !ch.isVoiceBased();\n }\n });\n\n if (channel) {\n return channel as TextChannel | BaseGuildVoiceChannel;\n }\n } catch (e) {\n // Continue searching in other guilds\n }\n }\n\n return null;\n } catch (error) {\n // Note: Standalone function without runtime context - error handled by caller\n return null;\n }\n};\n\nexport const leaveChannel: Action = {\n name: \"LEAVE_CHANNEL\",\n similes: [\n \"LEAVE_CHANNEL\",\n \"STOP_LISTENING_CHANNEL\",\n \"STOP_MONITORING_CHANNEL\",\n \"REMOVE_CHANNEL\",\n \"UNWATCH_CHANNEL\",\n \"LEAVE_TEXT_CHANNEL\",\n \"IGNORE_CHANNEL\",\n \"LEAVE_VOICE\",\n \"LEAVE_VC\",\n \"LEAVE_VOICE_CHAT\",\n \"LEAVE_VOICE_CHANNEL\",\n \"LEAVE_CALL\",\n \"EXIT_VOICE\",\n \"DISCONNECT_VOICE\",\n \"LEAVE_DISCORD_CHANNEL\",\n \"EXIT_CHANNEL\",\n ],\n description:\n \"Leave a Discord channel - either text (stop monitoring messages) or voice (disconnect from voice chat). Use this when asked to leave, exit, or disconnect from any Discord channel.\",\n validate: async (\n _runtime: IAgentRuntime,\n message: Memory,\n _state: State,\n ): Promise<boolean> => {\n if (message.content.source !== \"discord\") {\n return false;\n }\n return true;\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback,\n ): Promise<void | ActionResult | undefined> => {\n const discordService = runtime.getService(\n DISCORD_SERVICE_NAME,\n ) as DiscordService;\n\n if (!discordService || !discordService.client) {\n runtime.logger.error(\n {\n src: \"plugin:discord:action:leave-channel\",\n agentId: runtime.agentId,\n },\n \"Discord service not found or not initialized\",\n );\n await callback({\n text: \"Discord service is not available.\",\n source: \"discord\",\n });\n return undefined;\n }\n\n const channelInfo = await getLeaveChannelInfo(runtime, message, state);\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n const currentServerId = room?.messageServerId;\n const currentChannelId = room?.channelId;\n\n // Check if trying to leave voice without specifying channel\n const messageText = message.content.text?.toLowerCase() || \"\";\n const isVoiceRequest =\n channelInfo?.isVoiceChannel ||\n messageText.includes(\"voice\") ||\n messageText.includes(\"vc\") ||\n messageText.includes(\"call\");\n\n // If it's a generic voice leave request, handle current voice channel\n if (\n isVoiceRequest &&\n (!channelInfo || channelInfo.channelIdentifier === \"current\")\n ) {\n const voiceManager = discordService.voiceManager as VoiceManager;\n\n if (!voiceManager) {\n await callback({\n text: \"Voice functionality is not available at the moment.\",\n source: \"discord\",\n });\n return undefined;\n }\n\n if (currentServerId) {\n const guild = discordService.client.guilds.cache.get(currentServerId);\n const voiceChannel = guild?.members.me?.voice.channel;\n\n if (\n !voiceChannel ||\n !(voiceChannel instanceof BaseGuildVoiceChannel)\n ) {\n await callback({\n text: \"I'm not currently in a voice channel.\",\n source: \"discord\",\n });\n return undefined;\n }\n\n const connection = voiceManager.getVoiceConnection(guild.id);\n if (!connection) {\n await callback({\n text: \"No active voice connection found.\",\n source: \"discord\",\n });\n return undefined;\n }\n\n voiceManager.leaveChannel(voiceChannel);\n\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 await callback({\n text: `I've left the voice channel ${voiceChannel.name}.`,\n source: \"discord\",\n });\n return;\n }\n }\n\n if (!channelInfo) {\n runtime.logger.warn(\n {\n src: \"plugin:discord:action:leave-channel\",\n agentId: runtime.agentId,\n },\n \"Could not parse channel information from message\",\n );\n await callback({\n text: \"I couldn't understand which channel you want me to leave. Please specify the channel name or ID.\",\n source: \"discord\",\n });\n return undefined;\n }\n\n // Find the channel (try voice first if it's a voice request)\n let targetChannel = isVoiceRequest\n ? await findChannel(\n discordService,\n channelInfo.channelIdentifier,\n currentChannelId,\n currentServerId,\n true,\n )\n : await findChannel(\n discordService,\n channelInfo.channelIdentifier,\n currentChannelId,\n currentServerId,\n false,\n );\n\n // If not found, try the opposite type\n if (!targetChannel) {\n targetChannel = isVoiceRequest\n ? await findChannel(\n discordService,\n channelInfo.channelIdentifier,\n currentChannelId,\n currentServerId,\n false,\n )\n : await findChannel(\n discordService,\n channelInfo.channelIdentifier,\n currentChannelId,\n currentServerId,\n true,\n );\n }\n\n if (!targetChannel) {\n await callback({\n text: `I couldn't find a channel with the identifier \"${channelInfo.channelIdentifier}\". Please make sure the channel name or ID is correct.`,\n source: \"discord\",\n });\n return undefined;\n }\n\n // Handle voice channels\n if (targetChannel.type === DiscordChannelType.GuildVoice) {\n const voiceChannel = targetChannel as BaseGuildVoiceChannel;\n const voiceManager = discordService.voiceManager as VoiceManager;\n\n if (!voiceManager) {\n await callback({\n text: \"Voice functionality is not available at the moment.\",\n source: \"discord\",\n });\n return undefined;\n }\n\n const guild = voiceChannel.guild;\n const currentVoiceChannel = guild.members.me?.voice.channel;\n\n if (\n !currentVoiceChannel ||\n currentVoiceChannel.id !== voiceChannel.id\n ) {\n await callback({\n text: `I'm not currently in the voice channel ${voiceChannel.name}.`,\n source: \"discord\",\n });\n return undefined;\n }\n\n voiceManager.leaveChannel(voiceChannel);\n\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 const response: Content = {\n text: `I've left the voice channel ${voiceChannel.name}.`,\n actions: [\"LEAVE_CHANNEL_RESPONSE\"],\n source: message.content.source,\n };\n\n await callback(response);\n } else {\n // Handle text channels\n const textChannel = targetChannel as TextChannel;\n\n // Check if we're listening to this channel\n const currentChannels = discordService.getAllowedChannels();\n if (!currentChannels.includes(textChannel.id)) {\n await callback({\n text: `I'm not currently listening to ${textChannel.name} (<#${textChannel.id}>).`,\n source: \"discord\",\n });\n return undefined;\n }\n\n // Remove the channel from the allowed list\n const success = discordService.removeAllowedChannel(textChannel.id);\n\n if (success) {\n const response: Content = {\n text: `I've stopped listening to ${textChannel.name} (<#${textChannel.id}>). I will no longer respond to messages in that channel.`,\n actions: [\"LEAVE_CHANNEL_RESPONSE\"],\n source: message.content.source,\n };\n\n await callback(response);\n } else {\n await callback({\n text: `I couldn't remove ${textChannel.name} from my listening list. This channel might be configured in my environment settings and cannot be removed dynamically.`,\n source: \"discord\",\n });\n return undefined;\n }\n }\n } catch (error) {\n runtime.logger.error(\n {\n src: \"plugin:discord:action:leave-channel\",\n agentId: runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error leaving channel\",\n );\n await callback({\n text: \"I encountered an error while trying to leave the channel. Please try again.\",\n source: \"discord\",\n });\n return undefined;\n }\n },\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"leave the dev-voice channel\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll leave the dev-voice channel.\",\n actions: [\"LEAVE_CHANNEL\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"{{name2}} leave the dev-voice channel in discord\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Leaving the dev-voice channel now.\",\n actions: [\"LEAVE_CHANNEL\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Stop listening to #general\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll stop listening to the #general channel.\",\n actions: [\"LEAVE_CHANNEL\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"leave voice\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll leave the voice channel.\",\n actions: [\"LEAVE_CHANNEL\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Leave this channel\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll stop monitoring messages in this channel.\",\n actions: [\"LEAVE_CHANNEL\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"hop off vc\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Leaving the voice channel now.\",\n actions: [\"LEAVE_CHANNEL\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Please stop monitoring the spam channel\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll stop monitoring the spam channel.\",\n actions: [\"LEAVE_CHANNEL\"],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default leaveChannel;\n","import {\n type Action,\n type ActionExample,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n type State,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\n\nexport const listChannels: Action = {\n name: 'LIST_CHANNELS',\n similes: [\n 'SHOW_CHANNELS',\n 'LIST_LISTENING_CHANNELS',\n 'SHOW_MONITORED_CHANNELS',\n 'GET_CHANNELS',\n 'WHICH_CHANNELS',\n 'CHANNELS_LIST',\n ],\n description: 'Lists all Discord channels the bot is currently listening to and responding in.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n if (message.content.source !== 'discord') {\n return false;\n }\n return true;\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n runtime.logger.error({ src: 'plugin:discord:action:list-channels', agentId: runtime.agentId }, 'Discord service not found or not initialized');\n return;\n }\n\n try {\n // Get all allowed channels\n const allowedChannelIds = discordService.getAllowedChannels();\n\n if (allowedChannelIds.length === 0) {\n await callback({\n text: \"I'm currently listening to all channels (no restrictions are set).\",\n source: 'discord',\n });\n return;\n }\n\n // Fetch channel information for each allowed channel\n const channelInfoPromises = allowedChannelIds.map(async (channelId) => {\n try {\n const channel = await discordService.client!.channels.fetch(channelId);\n if (channel && channel.isTextBased() && !channel.isVoiceBased()) {\n const guild = 'guild' in channel ? channel.guild : null;\n return {\n id: channelId,\n name: 'name' in channel ? channel.name : 'DM',\n mention: `<#${channelId}>`,\n server: guild?.name || 'Direct Message',\n };\n }\n } catch (e) {\n // Channel might have been deleted or bot lost access\n return {\n id: channelId,\n name: 'Unknown',\n mention: channelId,\n server: 'Unknown or Deleted',\n };\n }\n return null;\n });\n\n const channelInfos = (await Promise.all(channelInfoPromises)).filter(Boolean);\n\n // Format the response\n let responseText = `I'm currently listening to ${channelInfos.length} channel${channelInfos.length !== 1 ? 's' : ''}:\\n\\n`;\n\n // Group by server\n const channelsByServer = channelInfos.reduce(\n (acc, channel) => {\n if (!channel) {return acc;}\n if (!acc[channel.server]) {\n acc[channel.server] = [];\n }\n acc[channel.server].push(channel);\n return acc;\n },\n {} as Record<string, typeof channelInfos>\n );\n\n // Format by server\n for (const [serverName, channels] of Object.entries(channelsByServer)) {\n responseText += `**${serverName}**\\n`;\n for (const channel of channels) {\n if (channel) {\n responseText += `• ${channel.name} (${channel.mention})\\n`;\n }\n }\n responseText += '\\n';\n }\n\n // Check if CHANNEL_IDS is set\n const envChannelIds = runtime.getSetting('CHANNEL_IDS') as string;\n if (envChannelIds) {\n responseText += '\\n*Note: Some channels are configured in my environment settings and cannot be removed dynamically.*';\n }\n\n const response: Content = {\n text: responseText.trim(),\n actions: ['LIST_CHANNELS_RESPONSE'],\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n runtime.logger.error({ src: 'plugin:discord:action:list-channels', agentId: runtime.agentId, error: error instanceof Error ? error.message : String(error) }, 'Error listing channels');\n await callback({\n text: 'I encountered an error while trying to list the channels. Please try again.',\n source: 'discord',\n });\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Which channels are you listening to?',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"Let me show you all the channels I'm currently monitoring.\",\n actions: ['LIST_CHANNELS'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'List all monitored channels',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll list all the channels I'm currently listening to.\",\n actions: ['LIST_CHANNELS'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'Show me your channel list',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"Here are all the channels I'm monitoring.\",\n actions: ['LIST_CHANNELS'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default listChannels;\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 parseJSONObjectFromText,\n} from \"@elizaos/core\";\nimport { DiscordService } from \"../service\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport { PermissionsBitField, type TextChannel } from \"discord.js\";\n\n/**\n * Template for extracting channel information from the user's request.\n *\n * @type {string}\n * @description This template is used to determine which channel the user wants to read messages from,\n * and optionally how many messages to retrieve.\n *\n * @param {string} recentMessages - Placeholder for recent messages related to the request.\n * @param {string} senderName - Name of the sender requesting channel messages.\n *\n * @returns {string} - Formatted template with instructions and JSON structure for response.\n */\nexport const channelInfoTemplate = `# Messages we are searching for channel information\n {{recentMessages}}\n \n # Instructions: {{senderName}} is requesting to read messages from a specific Discord channel. Your goal is to determine:\n 1. The channel they want to read from (could be the current channel or a mentioned channel)\n 2. How many messages they want to read (default to 10 if not specified)\n 3. Whether they want a summary or just the messages\n 4. If they're looking for messages from a specific person\n \n If they say \"this channel\" or \"here\", use the current channel.\n If they mention a specific channel name or ID, extract that.\n If they ask to \"summarize\" or mention what someone is \"talking about\", set summarize to true.\n \n Your response must be formatted as a JSON block with this structure:\n \\`\\`\\`json\n {\n \"channelIdentifier\": \"<current|channel-name|channel-id>\",\n \"messageCount\": <number between 1 and 50>,\n \"summarize\": true/false,\n \"focusUser\": \"<username or null>\"\n }\n \\`\\`\\`\n `;\n\n/**\n * Get channel information from the user's request\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<{channelIdentifier: string, messageCount: number} | null>} Channel info or null if not parseable.\n */\nconst getChannelInfo = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State,\n): Promise<{\n channelIdentifier: string;\n messageCount: number;\n summarize: boolean;\n focusUser: string | null;\n} | null> => {\n const prompt = composePromptFromState({\n state,\n template: channelInfoTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response) as {\n channelIdentifier: string;\n messageCount: number;\n summarize?: boolean;\n focusUser?: string | null;\n } | null;\n\n if (parsedResponse?.channelIdentifier) {\n // Ensure messageCount is within bounds\n const messageCount = Math.min(\n Math.max(parsedResponse.messageCount || 10, 1),\n 50,\n );\n return {\n channelIdentifier: parsedResponse.channelIdentifier,\n messageCount,\n summarize: parsedResponse.summarize || false,\n focusUser: parsedResponse.focusUser || null,\n };\n }\n }\n return null;\n};\n\nexport const readChannel: Action = {\n name: \"READ_CHANNEL\",\n similes: [\n \"READ_MESSAGES\",\n \"GET_CHANNEL_MESSAGES\",\n \"FETCH_MESSAGES\",\n \"SHOW_CHANNEL_HISTORY\",\n \"GET_CHAT_HISTORY\",\n \"READ_CHAT\",\n ],\n description:\n \"Reads recent messages from a Discord channel and either returns them or provides a summary. Can focus on messages from a specific user.\",\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n if (message.content.source !== \"discord\") {\n return false;\n }\n return true;\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback,\n ) => {\n const discordService = runtime.getService(\n DISCORD_SERVICE_NAME,\n ) as DiscordService;\n\n if (!discordService || !discordService.client) {\n runtime.logger.error(\n { src: \"plugin:discord:action:read-channel\", agentId: runtime.agentId },\n \"Discord service not found or not initialized\",\n );\n return;\n }\n\n const channelInfo = await getChannelInfo(runtime, message, state);\n if (!channelInfo) {\n runtime.logger.warn(\n { src: \"plugin:discord:action:read-channel\", agentId: runtime.agentId },\n \"Could not parse channel information from message\",\n );\n await callback({\n text: \"I couldn't understand which channel you want me to read from. Please specify the channel name or say 'this channel' for the current channel.\",\n source: \"discord\",\n });\n return;\n }\n\n try {\n let targetChannel: TextChannel | null = null;\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n\n // Determine the target channel\n if (\n channelInfo.channelIdentifier === \"current\" ||\n channelInfo.channelIdentifier === \"this\" ||\n channelInfo.channelIdentifier === \"here\"\n ) {\n // Use current channel\n if (room?.channelId) {\n targetChannel = (await discordService.client.channels.fetch(\n room.channelId,\n )) as TextChannel;\n }\n } else if (channelInfo.channelIdentifier.match(/^\\d+$/)) {\n // It's a channel ID\n targetChannel = (await discordService.client.channels.fetch(\n channelInfo.channelIdentifier,\n )) as TextChannel;\n } else {\n // It's a channel name - search in the current server\n const serverId = room?.messageServerId;\n if (!serverId) {\n await callback({\n text: \"I couldn't determine which server to search for that channel.\",\n source: \"discord\",\n });\n return;\n }\n const guild = await discordService.client.guilds.fetch(serverId);\n const channels = await guild.channels.fetch();\n\n targetChannel =\n (channels.find(\n (channel) =>\n channel?.name\n .toLowerCase()\n .includes(channelInfo.channelIdentifier.toLowerCase()) &&\n channel.isTextBased(),\n ) as TextChannel | undefined) || null;\n }\n\n if (!targetChannel || !targetChannel.isTextBased()) {\n await callback({\n text: \"I couldn't find that channel or I don't have access to it. Make sure the channel exists and I have permission to read messages there.\",\n source: \"discord\",\n });\n return;\n }\n\n // Check permissions\n const botMember = targetChannel.guild?.members.cache.get(\n discordService.client.user!.id,\n );\n if (botMember) {\n const permissions = targetChannel.permissionsFor(botMember);\n if (!permissions?.has(PermissionsBitField.Flags.ReadMessageHistory)) {\n await callback({\n text: \"I don't have permission to read message history in that channel.\",\n source: \"discord\",\n });\n return;\n }\n }\n\n // Fetch messages - get more for summarization to have better context\n // Discord API limits to 100 messages per fetch\n const requestedLimit = channelInfo.summarize\n ? Math.max(channelInfo.messageCount * 2, 50)\n : channelInfo.messageCount;\n const fetchLimit = Math.min(requestedLimit, 100);\n\n runtime.logger.debug(\n {\n src: \"plugin:discord:action:read-channel\",\n agentId: runtime.agentId,\n channelName: targetChannel.name,\n fetchLimit,\n requestedLimit,\n summarize: channelInfo.summarize,\n focusUser: channelInfo.focusUser,\n },\n \"Fetching messages\",\n );\n\n const messages = await targetChannel.messages.fetch({\n limit: fetchLimit,\n });\n\n if (messages.size === 0) {\n await callback({\n text: `No messages found in <#${targetChannel.id}>.`,\n source: \"discord\",\n });\n return;\n }\n\n // If summarization is requested\n if (channelInfo.summarize) {\n const sortedMessages = Array.from(messages.values()).reverse();\n\n // Filter by user if specified\n const relevantMessages = channelInfo.focusUser\n ? sortedMessages.filter((msg) => {\n const focusUserLower = channelInfo.focusUser!.toLowerCase();\n return (\n msg.author.username.toLowerCase().includes(focusUserLower) ||\n msg.member?.displayName?.toLowerCase().includes(focusUserLower)\n );\n })\n : sortedMessages;\n\n if (channelInfo.focusUser && relevantMessages.length === 0) {\n await callback({\n text: `I couldn't find any messages from \"${channelInfo.focusUser}\" in the recent messages from <#${targetChannel.id}>.`,\n source: \"discord\",\n });\n return;\n }\n\n // Prepare messages for summarization\n const messagesToSummarize = relevantMessages\n .slice(0, channelInfo.messageCount)\n .map((msg) => ({\n author: msg.author.username,\n content: msg.content || \"[No text content]\",\n timestamp: new Date(msg.createdTimestamp).toLocaleString(),\n }));\n\n // Create a summary prompt\n const summaryPrompt = channelInfo.focusUser\n ? `Please summarize what ${channelInfo.focusUser} has been discussing based on these messages from the Discord channel \"${targetChannel.name}\":\\n\\n${messagesToSummarize\n .map((m) => `${m.author} (${m.timestamp}): ${m.content}`)\n .join(\n \"\\n\\n\",\n )}\\n\\nProvide a concise summary focusing on:\\n1. Main topics ${channelInfo.focusUser} discussed\\n2. Key points or proposals they made\\n3. Any questions they asked or issues they raised\\n\\nIf ${channelInfo.focusUser} didn't appear in these messages, please note that.`\n : `Please summarize the recent conversation in the Discord channel \"${targetChannel.name}\" based on these messages:\\n\\n${messagesToSummarize\n .map((m) => `${m.author} (${m.timestamp}): ${m.content}`)\n .join(\n \"\\n\\n\",\n )}\\n\\nProvide a concise summary that includes:\\n1. Main topics discussed\\n2. Key decisions or conclusions\\n3. Who contributed what (mention specific usernames)\\n4. Any action items or next steps mentioned`;\n\n const summary = await runtime.useModel(ModelType.TEXT_LARGE, {\n prompt: summaryPrompt,\n });\n\n const response: Content = {\n text: channelInfo.focusUser\n ? `Summary of what ${channelInfo.focusUser} has been discussing in <#${targetChannel.id}>:\\n\\n${summary}`\n : `Summary of recent conversation in <#${targetChannel.id}>:\\n\\n${summary}`,\n actions: [\"READ_CHANNEL_RESPONSE\"],\n source: message.content.source,\n };\n\n await callback(response);\n } else {\n // Format messages for display (original behavior)\n const formattedMessages = Array.from(messages.values())\n .reverse() // Show oldest first\n .map((msg) => {\n const timestamp = new Date(msg.createdTimestamp).toLocaleString();\n const author = msg.author.username;\n const content = msg.content || \"[No text content]\";\n const attachments =\n msg.attachments.size > 0\n ? `\\n📎 Attachments: ${msg.attachments.map((a) => a.name || \"unnamed\").join(\", \")}`\n : \"\";\n\n return `**${author}** (${timestamp}):\\n${content}${attachments}`;\n })\n .join(\"\\n\\n---\\n\\n\");\n\n const response: Content = {\n text: `Here are the last ${messages.size} messages from <#${targetChannel.id}>:\\n\\n${formattedMessages}`,\n actions: [\"READ_CHANNEL_RESPONSE\"],\n source: message.content.source,\n };\n\n await callback(response);\n }\n } catch (error) {\n runtime.logger.error(\n {\n src: \"plugin:discord:action:read-channel\",\n agentId: runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error reading channel\",\n );\n await callback({\n text: \"I encountered an error while trying to read the channel messages. Please make sure I have the necessary permissions and try again.\",\n source: \"discord\",\n });\n }\n },\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Can you read the last 20 messages from this channel?\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll read the last 20 messages from this channel for you.\",\n actions: [\"READ_CHANNEL\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"read the core-devs channel and summarize what shaw talking about\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll read the core-devs channel and summarize shaw's discussion.\",\n actions: [\"READ_CHANNEL\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Show me what's been said in #general\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Let me fetch the recent messages from #general.\",\n actions: [\"READ_CHANNEL\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Summarize the recent conversation in this channel\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll summarize the recent conversation in this channel.\",\n actions: [\"READ_CHANNEL\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Read messages here\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll read the recent messages from this channel.\",\n actions: [\"READ_CHANNEL\"],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default readChannel;\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 parseJSONObjectFromText,\n} from \"@elizaos/core\";\nimport { DiscordService } from \"../service\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport type { User } from \"discord.js\";\n\n/**\n * Template for extracting DM recipient and message information from the user's request.\n *\n * @type {string}\n * @description This template is used to determine who the user wants to send a DM to and what message to send.\n *\n * @param {string} recentMessages - Placeholder for recent messages related to the request.\n * @param {string} senderName - Name of the sender requesting to send a DM.\n *\n * @returns {string} - Formatted template with instructions and JSON structure for response.\n */\nexport const dmInfoTemplate = `# Messages we are searching for DM information\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting to send a direct message to a specific Discord user. Your goal is to determine:\n1. The recipient they want to message (could be a username, user ID, or mentioned user)\n2. The message content they want to send\n\nExtract the recipient identifier and the message content from their request.\n- If they mention a user like @username or <@userid>, extract that\n- If they provide a username or display name, extract that\n- If they provide a user ID (long number), extract that\n- Extract the complete message they want to send\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"recipientIdentifier\": \"<username|user-id|@mention>\",\n \"messageContent\": \"<the message to send>\"\n}\n\\`\\`\\`\n`;\n\n/**\n * Get DM information from the user's request\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<{recipientIdentifier: string, messageContent: string} | null>} DM info or null if not parseable.\n */\nconst getDMInfo = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State,\n): Promise<{ recipientIdentifier: string; messageContent: string } | null> => {\n const prompt = composePromptFromState({\n state,\n template: dmInfoTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response) as {\n recipientIdentifier: string;\n messageContent: string;\n } | null;\n\n if (parsedResponse?.recipientIdentifier && parsedResponse?.messageContent) {\n return parsedResponse;\n }\n }\n return null;\n};\n\n/**\n * Find a Discord user by various identifiers\n * @param {DiscordService} discordService - The Discord service instance\n * @param {string} identifier - The user identifier (username, ID, or mention)\n * @param {string} currentServerId - The current server ID to search in\n * @returns {Promise<User | null>} The found user or null\n */\nconst findUser = async (\n discordService: DiscordService,\n identifier: string,\n currentServerId?: string,\n): Promise<User | null> => {\n if (!discordService.client) {\n return null;\n }\n\n // Remove mention formatting if present\n const cleanId = identifier.replace(/[<@!>]/g, \"\");\n\n try {\n // Try to fetch by ID first\n if (/^\\d+$/.test(cleanId)) {\n try {\n return await discordService.client.users.fetch(cleanId);\n } catch (e) {\n // ID not found, continue to username search\n }\n }\n\n // Search in the current server if available\n if (currentServerId) {\n const guild = await discordService.client.guilds.fetch(currentServerId);\n const members = await guild.members.fetch();\n\n // Search by username or display name\n const member = members.find(\n (m) =>\n m.user.username.toLowerCase() === identifier.toLowerCase() ||\n m.displayName.toLowerCase() === identifier.toLowerCase() ||\n m.user.tag.toLowerCase() === identifier.toLowerCase(),\n );\n\n if (member) {\n return member.user;\n }\n }\n\n // Search in all guilds the bot is in\n const guilds = Array.from(discordService.client.guilds.cache.values());\n for (const guild of guilds) {\n try {\n const members = await guild.members.fetch();\n const member = members.find(\n (m) =>\n m.user.username.toLowerCase() === identifier.toLowerCase() ||\n m.displayName.toLowerCase() === identifier.toLowerCase() ||\n m.user.tag.toLowerCase() === identifier.toLowerCase(),\n );\n\n if (member) {\n return member.user;\n }\n } catch (e) {\n // Continue searching in other guilds\n }\n }\n\n return null;\n } catch (error) {\n // Note: Using global logger here as this is a standalone function without runtime context\n return null;\n }\n};\n\nexport const sendDM: Action = {\n name: \"SEND_DM\",\n similes: [\n \"SEND_DIRECT_MESSAGE\",\n \"DM_USER\",\n \"MESSAGE_USER\",\n \"PRIVATE_MESSAGE\",\n \"SEND_PRIVATE_MESSAGE\",\n \"DM\",\n \"SEND_MESSAGE_TO_USER\",\n ],\n description: \"Sends a direct message to a specific Discord user.\",\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n if (message.content.source !== \"discord\") {\n return false;\n }\n return true;\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback,\n ) => {\n const discordService = runtime.getService(\n DISCORD_SERVICE_NAME,\n ) as DiscordService;\n\n if (!discordService || !discordService.client) {\n runtime.logger.error(\n { src: \"plugin:discord:action:send-dm\", agentId: runtime.agentId },\n \"Discord service not found or not initialized\",\n );\n return;\n }\n\n const dmInfo = await getDMInfo(runtime, message, state);\n if (!dmInfo) {\n runtime.logger.warn(\n { src: \"plugin:discord:action:send-dm\", agentId: runtime.agentId },\n \"Could not parse DM information from message\",\n );\n await callback({\n text: \"I couldn't understand who you want me to message or what to send. Please specify the recipient and the message content.\",\n source: \"discord\",\n });\n return;\n }\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n const currentServerId = room?.messageServerId;\n\n // Find the user\n const targetUser = await findUser(\n discordService,\n dmInfo.recipientIdentifier,\n currentServerId,\n );\n\n if (!targetUser) {\n await callback({\n text: `I couldn't find a user with the identifier \"${dmInfo.recipientIdentifier}\". Please make sure the username or ID is correct.`,\n source: \"discord\",\n });\n return;\n }\n\n // Check if we can send DMs to this user\n if (targetUser.bot) {\n await callback({\n text: \"I cannot send direct messages to other bots.\",\n source: \"discord\",\n });\n return;\n }\n\n // Create or get DM channel\n const dmChannel = await targetUser.createDM();\n\n // Send the message\n await dmChannel.send(dmInfo.messageContent);\n\n const response: Content = {\n text: `I've sent your message to ${targetUser.username}: \"${dmInfo.messageContent}\"`,\n actions: [\"SEND_DM_RESPONSE\"],\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n runtime.logger.error(\n {\n src: \"plugin:discord:action:send-dm\",\n agentId: runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error sending DM\",\n );\n\n // Handle specific Discord API errors\n if (error instanceof Error) {\n if (error.message.includes(\"Cannot send messages to this user\")) {\n await callback({\n text: \"I couldn't send a message to that user. They may have DMs disabled or we don't share a server.\",\n source: \"discord\",\n });\n } else {\n await callback({\n text: \"I encountered an error while trying to send the direct message. Please make sure I have the necessary permissions.\",\n source: \"discord\",\n });\n }\n }\n }\n },\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Send a DM to @alice saying hello how are you today?\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll send a direct message to alice right away.\",\n actions: [\"SEND_DM\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Can you message john_doe and tell him the meeting is at 3pm?\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll send john_doe a DM about the meeting time.\",\n actions: [\"SEND_DM\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"DM user 123456789012345678 with: Thanks for your help!\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll send that thank you message as a DM right now.\",\n actions: [\"SEND_DM\"],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default sendDM;\n","import fs from 'node:fs';\nimport {\n type Action,\n type ActionExample,\n ContentType,\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 logger,\n} from '@elizaos/core';\n\n/**\n * Normalizes a numeric timestamp to milliseconds.\n * Detects whether the input is likely in seconds or milliseconds based on magnitude.\n *\n * Heuristic: Unix timestamps in seconds are ~10 digits (e.g., 1703001600 for 2023)\n * Unix timestamps in milliseconds are ~13 digits (e.g., 1703001600000 for 2023)\n * We use a threshold: if the number represents a date before year 2000 when interpreted\n * as milliseconds, it's likely in seconds and needs conversion.\n *\n * @param {number} timestamp - The numeric timestamp to normalize\n * @returns {number} Timestamp in milliseconds\n */\nfunction normalizeTimestamp(timestamp: number): number {\n // Threshold: Jan 1, 2000 in milliseconds = 946684800000\n // If timestamp is less than this, it's likely in seconds (or an invalid/ancient date)\n // A Unix timestamp in seconds for year 2000+ would be > 946684800 (~10 digits)\n // which when treated as ms would be < Jan 12, 1970\n const year2000InMs = 946684800000;\n\n if (timestamp > 0 && timestamp < year2000InMs) {\n // Likely in seconds - convert to milliseconds\n // Additional sanity check: result should be a reasonable date (after 2000, before 2100)\n const asMs = timestamp * 1000;\n const year2100InMs = 4102444800000;\n if (asMs >= year2000InMs && asMs <= year2100InMs) {\n return asMs;\n }\n }\n\n return timestamp;\n}\n\n/**\n * Parses various time formats into a Unix timestamp (milliseconds).\n * Supports:\n * - Absolute timestamps (number or numeric string): 1234567890000 or 1234567890 (auto-detects seconds vs ms)\n * - Relative time strings: \"5 minutes ago\", \"2 hours ago\", \"3 days ago\"\n * - ISO date strings: \"2024-01-15T10:30:00Z\"\n *\n * Note: Month and year calculations use approximate values (30 days and 365 days respectively).\n * This is intentional for conversation summarization to ensure inclusive time ranges.\n * For example, \"1 month ago\" may include 28-31 days of conversation depending on the actual month.\n *\n * @param {string | number} input - The time value to parse\n * @returns {number} Unix timestamp in milliseconds\n */\nfunction parseTimeToTimestamp(input: string | number): number {\n // If already a number, normalize and return\n if (typeof input === 'number') {\n return normalizeTimestamp(input);\n }\n\n // Try parsing as a direct numeric string (timestamp)\n const asNumber = Number(input);\n if (!Number.isNaN(asNumber) && asNumber > 0) {\n return normalizeTimestamp(asNumber);\n }\n\n // Try parsing as ISO date\n const isoDate = Date.parse(input);\n if (!Number.isNaN(isoDate)) {\n return isoDate;\n }\n\n // Parse relative time format: \"<number> <unit> ago\", e.g. \"5 minutes ago\", \"2 hours ago\"\n const relativeMatch = input.match(/(\\d+\\.?\\d*)\\s*(second|minute|hour|day|week|month|year)s?\\s+ago/i);\n if (relativeMatch) {\n const value = parseFloat(relativeMatch[1]);\n const unit = relativeMatch[2].toLowerCase();\n\n // Approximate multipliers for time units\n // Month = 30 days, Year = 365 days (no leap year handling)\n // This provides consistent, inclusive time ranges for conversation retrieval\n const multipliers: Record<string, number> = {\n second: 1000,\n minute: 60 * 1000,\n hour: 3600 * 1000,\n day: 86400 * 1000,\n week: 7 * 86400 * 1000,\n month: 30 * 86400 * 1000, // Approximation: actual months vary 28-31 days\n year: 365 * 86400 * 1000, // Approximation: ignores leap years\n };\n\n const milliseconds = value * (multipliers[unit] || 0);\n\n // \"<number> <unit> ago\" means subtract from now\n return Date.now() - milliseconds;\n }\n\n // Fallback: return current time if we can't parse\n // Log warning for malformed model output\n logger.warn(`[parseTimeToTimestamp] Could not parse time value, using current time: ${input}`);\n return Date.now();\n}\n\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: number; end: number; } | null>} Parsed user input containing objective, start, and end timestamps, or null.\n */\nconst getDateRange = async (runtime: IAgentRuntime, _message: Memory, state: State): Promise<{ objective: string; start: number; end: number; } | null> => {\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 // Parse start and end into proper timestamps (returns numbers)\n const startRaw = parseTimeToTimestamp(parsedResponse.start);\n const endRaw = parseTimeToTimestamp(parsedResponse.end);\n\n // Validate that both timestamps are finite numbers\n if (!Number.isFinite(startRaw) || !Number.isFinite(endRaw)) {\n logger.warn(`[getDateRange] Invalid timestamps parsed: start=${startRaw}, end=${endRaw}, retrying...`);\n continue;\n }\n\n // Normalize: ensure start <= end (swap if model returned them inverted)\n let start = startRaw <= endRaw ? startRaw : endRaw;\n const end = startRaw <= endRaw ? endRaw : startRaw;\n\n // If start === end, widen the window by 1 hour to avoid empty queries\n if (start === end) {\n start = end - 3600 * 1000; // 1 hour before end\n }\n\n return {\n objective: parsedResponse.objective,\n start,\n end,\n };\n }\n }\n }\n return null;\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 runtime.logger.warn({ src: 'plugin:discord:action:summarize-conversation', agentId: runtime.agentId }, 'Could not 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 // Note: start and end are absolute timestamps (milliseconds since epoch)\n // returned by parseTimeToTimestamp from the user's date range request\n const memories = await runtime.getMemories({\n tableName: 'messages',\n roomId,\n start,\n end,\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 runtime.logger.warn({ src: 'plugin:discord:action:summarize-conversation', agentId: runtime.agentId }, 'No summary found');\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 ...callbackData,\n text: `I've attached the summary of the conversation from \\`${new Date(start).toString()}\\` to \\`${new Date(end).toString()}\\` as a text file.`,\n attachments: [\n ...(callbackData.attachments || []),\n {\n id: summaryFilename,\n url: summaryFilename,\n title: 'Conversation Summary',\n source: 'discord',\n contentType: ContentType.DOCUMENT,\n } as Media,\n ],\n });\n } else {\n runtime.logger.warn({ src: 'plugin:discord:action:summarize-conversation', agentId: runtime.agentId }, 'Empty response from summarize conversation action');\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 ContentType,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Media,\n type Memory,\n ModelType,\n type State,\n composePromptFromState,\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 runtime.logger.warn({ src: 'plugin:discord:action:transcribe-media', agentId: runtime.agentId }, 'Could not 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 runtime.logger.warn({ src: 'plugin:discord:action:transcribe-media', agentId: runtime.agentId, attachmentId }, 'Could not find attachment');\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 ...callbackData,\n text: 'I\\'ve attached the transcript as a text file.',\n attachments: [\n ...(callbackData.attachments || []),\n {\n id: transcriptFilename,\n url: transcriptFilename,\n title: 'Transcript',\n source: 'discord',\n contentType: ContentType.DOCUMENT,\n } as Media,\n ],\n });\n } else {\n runtime.logger.warn({ src: 'plugin:discord:action:transcribe-media', agentId: runtime.agentId }, 'Empty response from transcribe media action');\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","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 parseJSONObjectFromText,\n} from \"@elizaos/core\";\nimport { DiscordService } from \"../service\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport { type TextChannel, type Message, Collection } from \"discord.js\";\n\n/**\n * Template for extracting search parameters from the user's request.\n */\nexport const searchMessagesTemplate = `# Searching for Discord messages\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting to search for messages in Discord. Extract:\n1. The search query/keywords\n2. The channel to search in (current if not specified)\n3. Optional filters like author, time range, or message count\n\nExamples:\n- \"search for messages containing 'meeting'\" -> query: \"meeting\", channelIdentifier: \"current\", NO author field\n- \"find messages from @user about bugs\" -> query: \"bugs\", channelIdentifier: \"current\", author: \"user\"\n- \"search #general for links from last week\" -> query: \"links\", channelIdentifier: \"general\", timeRange: \"week\"\n- \"search for messages about 'spartan' in this channel\" -> query: \"spartan\", channelIdentifier: \"current\"\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"query\": \"<search keywords>\",\n \"channelIdentifier\": \"<channel-name|channel-id|current>\",\n \"author\": \"<username>\", // ONLY include this field if a specific author was mentioned\n \"timeRange\": \"<hour|day|week|month>\", // ONLY include if a time range was specified\n \"limit\": <number between 1-100, default 20>\n}\n\\`\\`\\`\n`;\n\nconst getSearchParams = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State,\n): Promise<{\n query: string;\n channelIdentifier: string;\n author: string | null;\n timeRange: string | null;\n limit: number;\n} | null> => {\n const prompt = composePromptFromState({\n state,\n template: searchMessagesTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response);\n if (parsedResponse?.query) {\n // Remove quotes from query if present\n const cleanQuery = parsedResponse.query.replace(/^[\"']|[\"']$/g, \"\");\n\n return {\n query: cleanQuery,\n channelIdentifier: parsedResponse.channelIdentifier || \"current\",\n author: parsedResponse.author || null,\n timeRange: parsedResponse.timeRange || null,\n limit: Math.min(Math.max(parsedResponse.limit || 20, 1), 100),\n };\n }\n }\n return null;\n};\n\nconst searchInMessages = (\n messages: Collection<string, Message>,\n query: string,\n author?: string | null,\n): Message[] => {\n const queryLower = query.toLowerCase().trim();\n const isLinkSearch =\n queryLower.includes(\"link\") || queryLower.includes(\"url\");\n\n return Array.from(messages.values()).filter((msg) => {\n // Skip system messages\n if (msg.system) {\n return false;\n }\n\n // Filter by author if specified\n if (author && author !== \"null\" && author !== \"undefined\") {\n const authorLower = author.toLowerCase();\n const matchesUsername = msg.author.username\n .toLowerCase()\n .includes(authorLower);\n const matchesDisplayName =\n msg.member?.displayName?.toLowerCase().includes(authorLower) || false;\n if (!matchesUsername && !matchesDisplayName) {\n return false;\n }\n }\n\n // Special handling for link searches\n if (isLinkSearch) {\n const urlRegex = /(https?:\\/\\/[^\\s]+)/g;\n return urlRegex.test(msg.content);\n }\n\n // Search in message content (case-insensitive)\n const contentMatch = msg.content.toLowerCase().includes(queryLower);\n\n // Search in embeds\n const embedMatch = msg.embeds.some(\n (embed) =>\n embed.title?.toLowerCase().includes(queryLower) ||\n embed.description?.toLowerCase().includes(queryLower) ||\n embed.author?.name?.toLowerCase().includes(queryLower) ||\n embed.fields?.some(\n (field) =>\n field.name?.toLowerCase().includes(queryLower) ||\n field.value?.toLowerCase().includes(queryLower),\n ),\n );\n\n // Search in attachments\n const attachmentMatch = msg.attachments.some(\n (att) =>\n att.name?.toLowerCase().includes(queryLower) ||\n att.description?.toLowerCase().includes(queryLower),\n );\n\n return contentMatch || embedMatch || attachmentMatch;\n });\n};\n\nexport const searchMessages: Action = {\n name: \"SEARCH_MESSAGES\",\n similes: [\n \"SEARCH_MESSAGES\",\n \"FIND_MESSAGES\",\n \"SEARCH_CHAT\",\n \"LOOK_FOR_MESSAGES\",\n \"FIND_IN_CHAT\",\n \"SEARCH_CHANNEL\",\n \"SEARCH_DISCORD\",\n ],\n description:\n \"Search for messages in Discord channels based on keywords, author, or time range.\",\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n return message.content.source === \"discord\";\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback,\n ) => {\n const discordService = runtime.getService(\n DISCORD_SERVICE_NAME,\n ) as DiscordService;\n\n if (!discordService || !discordService.client) {\n await callback({\n text: \"Discord service is not available.\",\n source: \"discord\",\n });\n return;\n }\n\n const searchParams = await getSearchParams(runtime, message, state);\n if (!searchParams) {\n await callback({\n text: \"I couldn't understand what you want to search for. Please specify what to search.\",\n source: \"discord\",\n });\n return;\n }\n\n try {\n let targetChannel: TextChannel | null = null;\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n\n // Determine the target channel\n if (searchParams.channelIdentifier === \"current\") {\n if (room?.channelId) {\n targetChannel = (await discordService.client.channels.fetch(\n room.channelId,\n )) as TextChannel;\n }\n } else if (searchParams.channelIdentifier.match(/^\\d+$/)) {\n targetChannel = (await discordService.client.channels.fetch(\n searchParams.channelIdentifier,\n )) as TextChannel;\n } else {\n // It's a channel name - search in the current server\n const serverId = room?.messageServerId;\n if (!serverId) {\n await callback({\n text: \"I couldn't determine which server to search for that channel.\",\n source: \"discord\",\n });\n return;\n }\n const guild = await discordService.client.guilds.fetch(serverId);\n const channels = await guild.channels.fetch();\n targetChannel =\n (channels.find(\n (channel) =>\n channel?.name\n .toLowerCase()\n .includes(searchParams.channelIdentifier.toLowerCase()) &&\n channel.isTextBased(),\n ) as TextChannel | undefined) || null;\n }\n\n if (!targetChannel || !targetChannel.isTextBased()) {\n await callback({\n text: \"I couldn't find that channel or I don't have access to it.\",\n source: \"discord\",\n });\n return;\n }\n\n // Calculate time limit\n let before: number | undefined = undefined;\n if (searchParams.timeRange) {\n const now = Date.now();\n const timeMap: Record<string, number> = {\n hour: 60 * 60 * 1000,\n day: 24 * 60 * 60 * 1000,\n week: 7 * 24 * 60 * 60 * 1000,\n month: 30 * 24 * 60 * 60 * 1000,\n };\n if (timeMap[searchParams.timeRange]) {\n before = now - timeMap[searchParams.timeRange];\n }\n }\n\n // Fetch messages - Discord API limit is 100 per request\n const messages = await targetChannel.messages.fetch({\n limit: 100, // Discord API max limit\n before: before?.toString(),\n });\n\n // Search through messages\n const results = searchInMessages(\n messages,\n searchParams.query,\n searchParams.author,\n );\n runtime.logger.debug(\n {\n src: \"plugin:discord:action:search-messages\",\n agentId: runtime.agentId,\n query: searchParams.query,\n resultsCount: results.length,\n channelName: targetChannel.name,\n },\n \"Search completed\",\n );\n\n // Sort by timestamp (newest first) and limit\n const sortedResults = results.sort(\n (a, b) => b.createdTimestamp - a.createdTimestamp,\n );\n const limitedResults = sortedResults.slice(0, searchParams.limit);\n\n if (limitedResults.length === 0) {\n await callback({\n text: `No messages found matching \"${searchParams.query}\" in <#${targetChannel.id}>.`,\n source: \"discord\",\n });\n return;\n }\n\n // Format results\n const formattedResults = limitedResults\n .map((msg, index) => {\n const timestamp = new Date(msg.createdTimestamp).toLocaleString();\n const preview =\n msg.content.length > 100\n ? `${msg.content.substring(0, 100)}...`\n : msg.content;\n const attachments =\n msg.attachments.size > 0\n ? `\\n📎 ${msg.attachments.size} attachment(s)`\n : \"\";\n\n return `**${index + 1}.** ${msg.author.username} (${timestamp})\\n${preview}${attachments}\\n[Jump to message](${msg.url})`;\n })\n .join(\"\\n\\n\");\n\n const response: Content = {\n text: `Found ${limitedResults.length} message${limitedResults.length !== 1 ? \"s\" : \"\"} matching \"${searchParams.query}\" in <#${targetChannel.id}>:\\n\\n${formattedResults}`,\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n runtime.logger.error(\n {\n src: \"plugin:discord:action:search-messages\",\n agentId: runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error searching messages\",\n );\n await callback({\n text: \"I encountered an error while searching for messages. Please try again.\",\n source: \"discord\",\n });\n }\n },\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"search for messages containing 'meeting'\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll search for messages containing 'meeting'.\",\n actions: [\"SEARCH_MESSAGES\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"find all links shared in #general from last week\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Let me search for links in #general from the past week.\",\n actions: [\"SEARCH_MESSAGES\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"search for messages from @john about the bug\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll look for messages from john about the bug.\",\n actions: [\"SEARCH_MESSAGES\"],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default searchMessages;\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 parseJSONObjectFromText,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\nimport { type TextChannel } from 'discord.js';\n\n/**\n * Template for extracting poll information from the user's request.\n */\nexport const createPollTemplate = `# Creating a Discord poll\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting to create a poll. Extract:\n1. The poll question\n2. The poll options (2-10 options)\n3. Whether to use emoji reactions (default: true)\n\nExamples:\n- \"create a poll: What game should we play? Options: Minecraft, Fortnite, Among Us\" \n -> question: \"What game should we play?\", options: [\"Minecraft\", \"Fortnite\", \"Among Us\"]\n- \"poll: Should we have a meeting tomorrow? Yes/No\"\n -> question: \"Should we have a meeting tomorrow?\", options: [\"Yes\", \"No\"]\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"question\": \"<poll question>\",\n \"options\": [\"<option1>\", \"<option2>\", ...],\n \"useEmojis\": true/false\n}\n\\`\\`\\`\n`;\n\nconst getPollInfo = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<{\n question: string;\n options: string[];\n useEmojis: boolean;\n} | null> => {\n const prompt = composePromptFromState({\n state,\n template: createPollTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response);\n if (\n parsedResponse?.question &&\n Array.isArray(parsedResponse.options) &&\n parsedResponse.options.length >= 2\n ) {\n return {\n question: parsedResponse.question,\n options: parsedResponse.options.slice(0, 10), // Max 10 options\n useEmojis: parsedResponse.useEmojis !== false, // Default to true\n };\n }\n }\n return null;\n};\n\n// Number emojis for poll options\nconst numberEmojis = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟'];\nconst letterEmojis = ['🇦', '🇧', '🇨', '🇩', '🇪', '🇫', '🇬', '🇭', '🇮', '🇯'];\nconst yesNoEmojis = ['✅', '❌'];\n\nexport const createPoll: Action = {\n name: 'CREATE_POLL',\n similes: [\n 'CREATE_POLL',\n 'MAKE_POLL',\n 'START_POLL',\n 'CREATE_VOTE',\n 'MAKE_VOTE',\n 'START_VOTE',\n 'CREATE_SURVEY',\n ],\n description: 'Create a poll in Discord with emoji reactions for voting.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n return message.content.source === 'discord';\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n await callback({\n text: 'Discord service is not available.',\n source: 'discord',\n });\n return;\n }\n\n const pollInfo = await getPollInfo(runtime, message, state);\n if (!pollInfo) {\n await callback({\n text: \"I couldn't understand the poll details. Please specify a question and at least 2 options.\",\n source: 'discord',\n });\n return;\n }\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n if (!room?.channelId) {\n await callback({\n text: \"I couldn't determine the current channel.\",\n source: 'discord',\n });\n return;\n }\n\n const channel = await discordService.client.channels.fetch(room.channelId);\n if (!channel || !channel.isTextBased()) {\n await callback({\n text: 'I can only create polls in text channels.',\n source: 'discord',\n });\n return;\n }\n\n const textChannel = channel as TextChannel;\n\n // Determine which emojis to use\n let emojis: string[];\n if (\n pollInfo.options.length === 2 &&\n pollInfo.options.some((opt) => opt.toLowerCase().includes('yes')) &&\n pollInfo.options.some((opt) => opt.toLowerCase().includes('no'))\n ) {\n emojis = yesNoEmojis;\n } else if (pollInfo.useEmojis) {\n emojis = numberEmojis.slice(0, pollInfo.options.length);\n } else {\n emojis = letterEmojis.slice(0, pollInfo.options.length);\n }\n\n // Format the poll message\n const pollMessage = [\n `📊 **POLL: ${pollInfo.question}**`,\n '',\n ...pollInfo.options.map((option, index) => `${emojis[index]} ${option}`),\n '',\n '_React to vote!_',\n ].join('\\n');\n\n // Send the poll message\n const sentMessage = await textChannel.send(pollMessage);\n\n // Add reactions\n for (let i = 0; i < pollInfo.options.length; i++) {\n try {\n await sentMessage.react(emojis[i]);\n // Small delay to avoid rate limits\n await new Promise((resolve) => setTimeout(resolve, 250));\n } catch (error) {\n runtime.logger.error({ src: 'plugin:discord:action:create-poll', agentId: runtime.agentId, emoji: emojis[i], error: error instanceof Error ? error.message : String(error) }, 'Failed to add reaction');\n }\n }\n\n const response: Content = {\n text: `I've created a poll with ${pollInfo.options.length} options. Users can vote by clicking the reaction emojis!`,\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n runtime.logger.error({ src: 'plugin:discord:action:create-poll', agentId: runtime.agentId, error: error instanceof Error ? error.message : String(error) }, 'Error creating poll');\n await callback({\n text: 'I encountered an error while creating the poll. Please make sure I have permission to send messages and add reactions.',\n source: 'discord',\n });\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'create a poll: What game should we play tonight? Options: Minecraft, Fortnite, Valorant',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll create a poll for game selection with those options.\",\n actions: ['CREATE_POLL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'make a vote: Should we have the meeting at 3pm? Yes/No',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Creating a yes/no poll about the meeting time.',\n actions: ['CREATE_POLL'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'start a poll asking what day works best for everyone: Monday, Tuesday, Wednesday, Thursday, Friday',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll create a poll to find the best day for everyone.\",\n actions: ['CREATE_POLL'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default createPoll;\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 parseJSONObjectFromText,\n} from \"@elizaos/core\";\nimport { DiscordService } from \"../service\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport { type GuildMember } from \"discord.js\";\n\n/**\n * Template for extracting user identifier from the request.\n */\nexport const getUserInfoTemplate = `# Getting Discord user information\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting information about a Discord user. Extract:\n1. The user identifier (username, user ID, or mention)\n2. Whether they want detailed server-specific info\n\nExamples:\n- \"who is @john?\" -> userIdentifier: \"john\", detailed: false\n- \"tell me about user 123456789\" -> userIdentifier: \"123456789\", detailed: false \n- \"get detailed info on @admin\" -> userIdentifier: \"admin\", detailed: true\n- \"who am I?\" -> userIdentifier: \"self\", detailed: false\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"userIdentifier\": \"<username|user-id|mention|self>\",\n \"detailed\": true/false\n}\n\\`\\`\\`\n`;\n\nconst getUserIdentifier = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State,\n): Promise<{\n userIdentifier: string;\n detailed: boolean;\n} | null> => {\n const prompt = composePromptFromState({\n state,\n template: getUserInfoTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response);\n if (parsedResponse?.userIdentifier) {\n return {\n userIdentifier: parsedResponse.userIdentifier,\n detailed: parsedResponse.detailed === true,\n };\n }\n }\n return null;\n};\n\nconst formatUserInfo = (\n member: GuildMember,\n detailed: boolean = false,\n): string => {\n const user = member.user;\n const joinedAt = member.joinedAt\n ? new Date(member.joinedAt).toLocaleDateString()\n : \"Unknown\";\n const createdAt = new Date(user.createdAt).toLocaleDateString();\n const roles =\n member.roles.cache\n .filter((role) => role.name !== \"@everyone\")\n .map((role) => role.name)\n .join(\", \") || \"No roles\";\n\n const basicInfo = [\n \"👤 **User Information**\",\n `**Username:** ${user.username}${user.discriminator !== \"0\" ? `#${user.discriminator}` : \"\"}`,\n `**Display Name:** ${member.displayName}`,\n `**ID:** ${user.id}`,\n `**Bot:** ${user.bot ? \"Yes\" : \"No\"}`,\n `**Account Created:** ${createdAt}`,\n ];\n\n if (detailed) {\n const serverInfo = [\n \"\",\n \"🏛️ **Server Information**\",\n `**Nickname:** ${member.nickname || \"None\"}`,\n `**Joined Server:** ${joinedAt}`,\n `**Roles:** ${roles}`,\n `**Highest Role:** ${member.roles.highest.name}`,\n `**Permissions:** ${member.permissions.toArray().slice(0, 5).join(\", \")}${member.permissions.toArray().length > 5 ? \"...\" : \"\"}`,\n `**Voice Channel:** ${member.voice.channel ? member.voice.channel.name : \"Not in voice\"}`,\n `**Status:** ${member.presence?.status || \"offline\"}`,\n ];\n return [...basicInfo, ...serverInfo].join(\"\\n\");\n }\n\n return basicInfo.join(\"\\n\");\n};\n\nexport const getUserInfo: Action = {\n name: \"GET_USER_INFO\",\n similes: [\n \"GET_USER_INFO\",\n \"USER_INFO\",\n \"WHO_IS\",\n \"ABOUT_USER\",\n \"USER_DETAILS\",\n \"MEMBER_INFO\",\n \"CHECK_USER\",\n ],\n description:\n \"Get detailed information about a Discord user including their roles, join date, and permissions.\",\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n return message.content.source === \"discord\";\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback,\n ) => {\n const discordService = runtime.getService(\n DISCORD_SERVICE_NAME,\n ) as DiscordService;\n\n if (!discordService || !discordService.client) {\n await callback({\n text: \"Discord service is not available.\",\n source: \"discord\",\n });\n return;\n }\n\n const userInfo = await getUserIdentifier(runtime, message, state);\n if (!userInfo) {\n await callback({\n text: \"I couldn't understand which user you want information about. Please specify a username or mention.\",\n source: \"discord\",\n });\n return;\n }\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n const serverId = room?.messageServerId;\n if (!serverId) {\n await callback({\n text: \"I couldn't determine the current server.\",\n source: \"discord\",\n });\n return;\n }\n\n const guild = await discordService.client.guilds.fetch(serverId);\n\n let member: GuildMember | null = null;\n\n // Handle \"self\" request\n if (userInfo.userIdentifier === \"self\") {\n const authorId =\n (message.content as any).user_id || (message.content as any).userId;\n if (authorId && typeof authorId === \"string\") {\n const cleanId = authorId.replace(\"discord:\", \"\");\n try {\n member = await guild.members.fetch(cleanId);\n } catch (e) {\n // User not found\n }\n }\n } else {\n // Remove mention formatting if present\n const cleanIdentifier = userInfo.userIdentifier.replace(/[<@!>]/g, \"\");\n\n // Try to fetch by ID first\n if (/^\\d+$/.test(cleanIdentifier)) {\n try {\n member = await guild.members.fetch(cleanIdentifier);\n } catch (e) {\n // Not an ID or user not found\n }\n }\n\n // If not found by ID, search by username or display name\n if (!member) {\n const members = await guild.members.fetch();\n member =\n members.find(\n (m) =>\n m.user.username.toLowerCase() ===\n userInfo.userIdentifier.toLowerCase() ||\n m.displayName.toLowerCase() ===\n userInfo.userIdentifier.toLowerCase() ||\n (m.user.discriminator !== \"0\" &&\n `${m.user.username}#${m.user.discriminator}`.toLowerCase() ===\n userInfo.userIdentifier.toLowerCase()),\n ) || null;\n }\n }\n\n if (!member) {\n await callback({\n text: `I couldn't find a user with the identifier \"${userInfo.userIdentifier}\" in this server.`,\n source: \"discord\",\n });\n return;\n }\n\n const infoText = formatUserInfo(member, userInfo.detailed);\n\n const response: Content = {\n text: infoText,\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n runtime.logger.error(\n {\n src: \"plugin:discord:action:get-user-info\",\n agentId: runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error getting user info\",\n );\n await callback({\n text: \"I encountered an error while getting user information. Please try again.\",\n source: \"discord\",\n });\n }\n },\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"who is @john?\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll get information about john.\",\n actions: [\"GET_USER_INFO\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"tell me about myself\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll get your user information.\",\n actions: [\"GET_USER_INFO\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"get detailed info on the admin user\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll get detailed information about the admin.\",\n actions: [\"GET_USER_INFO\"],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default getUserInfo;\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 parseJSONObjectFromText,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\nimport { type TextChannel, type Message } from 'discord.js';\n\n/**\n * Template for extracting reaction information from the user's request.\n */\nexport const reactToMessageTemplate = `# Adding reactions to Discord messages\n{{recentMessages}}\n\n# Instructions: {{senderName}} wants to add a reaction to a message. Extract:\n1. Which message to react to (last, specific message reference, or by content)\n2. What emoji/reaction to add\n\nExamples:\n- \"react with 👍 to the last message\" -> messageRef: \"last\", emoji: \"👍\"\n- \"add :fire: reaction\" -> messageRef: \"last\", emoji: \"🔥\" or \":fire:\"\n- \"react to that message with ❤️\" -> messageRef: \"previous\", emoji: \"❤️\"\n- \"add a thumbs up to john's message about the meeting\" -> messageRef: \"john meeting\", emoji: \"👍\"\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"messageRef\": \"<last|previous|message-id|search-text>\",\n \"emoji\": \"<emoji-character|:emoji-name:>\"\n}\n\\`\\`\\`\n`;\n\n/**\n * Extracts emoji tokens from a string in the order they appear.\n *\n * Captures standard Unicode emoji sequences (including multi-codepoint sequences joined by zero-width joiners) and Discord custom emoji tokens in the form `<:name:id>` or `<a:name:id>`.\n *\n * @param text - The input text to scan for emojis\n * @returns An array of emoji strings found in `text`, in the order they appear (empty if none)\n */\nfunction extractEmojisFromText(text: string): string[] {\n if (!text) {return [];}\n\n // Collect all emoji matches with their positions to preserve order\n const matches: { index: number; emoji: string }[] = [];\n\n // Match Unicode emojis (including multi-codepoint sequences)\n const unicodeEmojiRegex = /(?:\\p{Emoji_Presentation}|\\p{Extended_Pictographic})(?:\\uFE0F)?(?:\\u200D(?:\\p{Emoji_Presentation}|\\p{Extended_Pictographic})(?:\\uFE0F)?)*/gu;\n let match;\n while ((match = unicodeEmojiRegex.exec(text)) !== null) {\n matches.push({ index: match.index, emoji: match[0] });\n }\n\n // Match Discord custom emojis <:name:id> or <a:name:id>\n const customEmojiRegex = /<a?:\\w+:\\d+>/g;\n while ((match = customEmojiRegex.exec(text)) !== null) {\n matches.push({ index: match.index, emoji: match[0] });\n }\n\n // Sort by position and return just the emojis\n return matches.sort((a, b) => a.index - b.index).map(m => m.emoji);\n}\n\n/**\n * Determines whether a message explicitly requests adding a reaction or specifies a target.\n *\n * @param text - The message text to inspect for reaction-related keywords or target patterns\n * @returns `true` if the text contains reaction keywords OR specifies a message target; `false` otherwise.\n */\nfunction isExplicitReactionRequest(text: string): boolean {\n if (!text) {return false;}\n const lower = text.toLowerCase();\n\n // Keywords indicating user explicitly requested a reaction\n if (/\\b(react|reaction|emoji)\\b/.test(lower)) {\n return true;\n }\n\n // Patterns indicating a specific message target (e.g., \"add thumbs up to john's message\")\n // These require LLM to extract the correct messageRef\n if (/\\w+'s\\s+message\\b/.test(lower)) {return true;} // \"john's message\"\n if (/message\\s+(about|from|where)\\b/.test(lower)) {return true;} // \"message about X\"\n if (/\\bto\\s+\\w+'s\\b/.test(lower)) {return true;} // \"to john's\"\n if (/\\bthat\\s+message\\b/.test(lower)) {return true;} // \"that message\"\n\n return false;\n}\n\n// Common Discord emoji mappings\nconst emojiMap: Record<string, string> = {\n ':thumbsup:': '👍',\n ':thumbs_up:': '👍',\n ':+1:': '👍',\n ':thumbsdown:': '👎',\n ':thumbs_down:': '👎',\n ':-1:': '👎',\n ':heart:': '❤️',\n ':fire:': '🔥',\n ':star:': '⭐',\n ':check:': '✅',\n ':white_check_mark:': '✅',\n ':x:': '❌',\n ':cross:': '❌',\n ':smile:': '😄',\n ':laughing:': '😆',\n ':thinking:': '🤔',\n ':eyes:': '👀',\n ':clap:': '👏',\n ':wave:': '👋',\n ':ok:': '👌',\n ':ok_hand:': '👌',\n ':raised_hands:': '🙌',\n ':pray:': '🙏',\n ':100:': '💯',\n ':rocket:': '🚀',\n};\n\nexport const reactToMessage: Action = {\n name: 'REACT_TO_MESSAGE',\n similes: [\n 'REACT_TO_MESSAGE',\n 'ADD_REACTION',\n 'REACT_MESSAGE',\n 'ADD_EMOJI',\n 'EMOJI_REACT',\n 'MESSAGE_REACTION',\n ],\n description: 'Add an emoji reaction to a Discord message.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n return message.content.source === 'discord';\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n await callback({\n text: 'Discord service is not available.',\n source: 'discord',\n });\n return;\n }\n\n // ============================================================================\n // Extract reaction info - use fast path when appropriate, LLM otherwise\n // ============================================================================\n let reactionInfo: { messageRef: string; emoji: string } | null = null;\n\n // Check if user explicitly requested a reaction (needs LLM for accuracy)\n // vs agent spontaneously reacting (fast path to \"last message\" is correct)\n const userText = message.content?.text || '';\n const needsLLM = isExplicitReactionRequest(userText);\n\n if (!needsLLM) {\n // FAST PATH: Agent spontaneously reacting - target is always \"last message\"\n const responseText = state.data?.responseText ||\n state.data?.text ||\n (state as any).responseText ||\n '';\n\n if (responseText) {\n const emojis = extractEmojisFromText(responseText);\n if (emojis.length > 0) {\n runtime.logger.debug(\n { src: 'plugin:discord:action:react', emoji: emojis[0], source: 'responseText' },\n '[REACT_TO_MESSAGE] Found emoji in response text (fast path)'\n );\n reactionInfo = { messageRef: 'last', emoji: emojis[0] };\n }\n }\n\n if (!reactionInfo) {\n // Check recent messages for this agent's last message\n const recentMessages = (state.data?.recentMessages || []) as Memory[];\n const agentLastMessage = recentMessages\n .filter(m => m.entityId === runtime.agentId)\n .pop();\n\n if (agentLastMessage?.content?.text) {\n const emojis = extractEmojisFromText(agentLastMessage.content.text);\n if (emojis.length > 0) {\n runtime.logger.debug(\n { src: 'plugin:discord:action:react', emoji: emojis[0], source: 'agentLastMessage' },\n '[REACT_TO_MESSAGE] Found emoji in agent\\'s last message (fast path)'\n );\n reactionInfo = { messageRef: 'last', emoji: emojis[0] };\n }\n }\n }\n }\n\n if (!reactionInfo) {\n // LLM PATH: Use when fast path fails or user specified a specific target\n const prompt = composePromptFromState({\n state,\n template: reactToMessageTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response);\n if (parsedResponse?.emoji) {\n reactionInfo = {\n messageRef: parsedResponse.messageRef || 'last',\n emoji: parsedResponse.emoji,\n };\n break;\n }\n }\n }\n\n if (!reactionInfo) {\n runtime.logger.debug(\n { src: 'plugin:discord:action:react' },\n '[REACT_TO_MESSAGE] Could not extract reaction info'\n );\n // Only show error to user if they explicitly requested a reaction\n // Silent failure is appropriate when agent spontaneously decides to react\n if (needsLLM) {\n await callback({\n text: \"I couldn't understand which message to react to or what emoji to use. Try being more specific, like 'react with 👍 to the last message'.\",\n source: 'discord',\n });\n }\n return;\n }\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n if (!room?.channelId) {\n await callback({\n text: \"I couldn't determine the current channel.\",\n source: 'discord',\n });\n return;\n }\n\n const channel = await discordService.client.channels.fetch(room.channelId);\n if (!channel || !channel.isTextBased()) {\n await callback({\n text: 'I can only react to messages in text channels.',\n source: 'discord',\n });\n return;\n }\n\n const textChannel = channel as TextChannel;\n\n let targetMessage: Message | null = null;\n\n // Find the target message\n if (reactionInfo.messageRef === 'last' || reactionInfo.messageRef === 'previous') {\n // Get the last few messages - fetch max allowed by Discord API\n const messages = await textChannel.messages.fetch({ limit: 100 });\n const sortedMessages = Array.from(messages.values()).sort(\n (a, b) => b.createdTimestamp - a.createdTimestamp\n );\n\n // Skip the bot's own message and the command message\n targetMessage =\n sortedMessages.find(\n (msg) =>\n msg.id !== message.content.id && msg.author.id !== discordService.client!.user!.id\n ) || null;\n } else if (/^\\d+$/.test(reactionInfo.messageRef)) {\n // It's a message ID\n try {\n targetMessage = await textChannel.messages.fetch(reactionInfo.messageRef);\n } catch (e) {\n // Message not found\n }\n } else {\n // Search for message by content/author - fetch max allowed by Discord API\n const messages = await textChannel.messages.fetch({ limit: 100 });\n const searchLower = reactionInfo.messageRef.toLowerCase();\n\n targetMessage =\n Array.from(messages.values()).find((msg) => {\n const contentMatch = msg.content.toLowerCase().includes(searchLower);\n const authorMatch = msg.author.username.toLowerCase().includes(searchLower);\n return contentMatch || authorMatch;\n }) || null;\n }\n\n if (!targetMessage) {\n await callback({\n text: \"I couldn't find the message you want me to react to. Try being more specific or use 'last message'.\",\n source: 'discord',\n });\n return;\n }\n\n // Normalize the emoji\n let emoji = reactionInfo.emoji;\n if (!/\\p{Emoji}/u.test(emoji)) {\n const mapped = emojiMap[emoji.toLowerCase()];\n if (mapped) {\n emoji = mapped;\n } else if (!/<a?:\\w+:\\d+>/.test(emoji)) {\n // Not a custom emoji, remove colons\n emoji = emoji.replace(/:/g, '');\n }\n }\n\n // Add the reaction\n try {\n await targetMessage.react(emoji);\n\n const response: Content = {\n text: `I've added a ${emoji} reaction to the message.`,\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n runtime.logger.error({ src: 'plugin:discord:action:react-to-message', agentId: runtime.agentId, emoji: reactionInfo.emoji, error: error instanceof Error ? error.message : String(error) }, 'Failed to add reaction');\n await callback({\n text: `I couldn't add that reaction. Make sure the emoji \"${reactionInfo.emoji}\" is valid and I have permission to add reactions.`,\n source: 'discord',\n });\n }\n } catch (error) {\n runtime.logger.error({ src: 'plugin:discord:action:react-to-message', agentId: runtime.agentId, error: error instanceof Error ? error.message : String(error) }, 'Error in react to message');\n await callback({\n text: 'I encountered an error while trying to react to the message. Please make sure I have the necessary permissions.',\n source: 'discord',\n });\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'react with 👍 to the last message',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll add a thumbs up reaction to the last message.\",\n actions: ['REACT_TO_MESSAGE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'add a fire emoji to that',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Adding a 🔥 reaction.',\n actions: ['REACT_TO_MESSAGE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: \"react to john's message about the meeting with a checkmark\",\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll find john's message about the meeting and add a ✅ reaction.\",\n actions: ['REACT_TO_MESSAGE'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default reactToMessage;\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 parseJSONObjectFromText,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\nimport { type TextChannel, type Message, PermissionsBitField } from 'discord.js';\n\n/**\n * Template for extracting message reference for pinning.\n */\nexport const pinMessageTemplate = `# Pinning a Discord message\n{{recentMessages}}\n\n# Instructions: {{senderName}} wants to pin a message. Extract which message they want to pin.\n\nExamples:\n- \"pin that message\" -> messageRef: \"last\"\n- \"pin the last message\" -> messageRef: \"last\"\n- \"pin john's message about the meeting\" -> messageRef: \"john meeting\"\n- \"pin message 123456789\" -> messageRef: \"123456789\"\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"messageRef\": \"<last|previous|message-id|search-text>\"\n}\n\\`\\`\\`\n`;\n\nconst getMessageRef = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<{\n messageRef: string;\n} | null> => {\n const prompt = composePromptFromState({\n state,\n template: pinMessageTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response);\n if (parsedResponse?.messageRef) {\n return {\n messageRef: parsedResponse.messageRef,\n };\n }\n }\n return null;\n};\n\nexport const pinMessage: Action = {\n name: 'PIN_MESSAGE',\n similes: ['PIN_MESSAGE', 'PIN_MSG', 'PIN_THIS', 'PIN_THAT', 'MAKE_PINNED', 'ADD_PIN'],\n description: 'Pin an important message in a Discord channel.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n return message.content.source === 'discord';\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n await callback({\n text: 'Discord service is not available.',\n source: 'discord',\n });\n return;\n }\n\n const messageInfo = await getMessageRef(runtime, message, state);\n if (!messageInfo) {\n await callback({\n text: \"I couldn't understand which message you want to pin. Please be more specific.\",\n source: 'discord',\n });\n return;\n }\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n if (!room?.channelId) {\n await callback({\n text: \"I couldn't determine the current channel.\",\n source: 'discord',\n });\n return;\n }\n\n const channel = await discordService.client.channels.fetch(room.channelId);\n if (!channel || !channel.isTextBased()) {\n await callback({\n text: 'I can only pin messages in text channels.',\n source: 'discord',\n });\n return;\n }\n\n const textChannel = channel as TextChannel;\n\n // Check bot permissions\n const botMember = textChannel.guild?.members.cache.get(discordService.client.user!.id);\n if (botMember) {\n const permissions = textChannel.permissionsFor(botMember);\n if (!permissions?.has(PermissionsBitField.Flags.ManageMessages)) {\n await callback({\n text: \"I don't have permission to pin messages in this channel. I need the 'Manage Messages' permission.\",\n source: 'discord',\n });\n return;\n }\n }\n\n let targetMessage: Message | null = null;\n\n // Find the target message\n if (messageInfo.messageRef === 'last' || messageInfo.messageRef === 'previous') {\n // Get the last few messages - fetch max allowed by Discord API\n const messages = await textChannel.messages.fetch({ limit: 100 });\n const sortedMessages = Array.from(messages.values()).sort(\n (a, b) => b.createdTimestamp - a.createdTimestamp\n );\n\n // Skip the bot's own message and the command message\n targetMessage =\n sortedMessages.find(\n (msg) =>\n msg.id !== message.content.id && msg.author.id !== discordService.client!.user!.id\n ) || null;\n } else if (/^\\d+$/.test(messageInfo.messageRef)) {\n // It's a message ID\n try {\n targetMessage = await textChannel.messages.fetch(messageInfo.messageRef);\n } catch (e) {\n // Message not found\n }\n } else {\n // Search for message by content/author - fetch max allowed by Discord API\n const messages = await textChannel.messages.fetch({ limit: 100 });\n const searchLower = messageInfo.messageRef.toLowerCase();\n\n targetMessage =\n Array.from(messages.values()).find((msg) => {\n const contentMatch = msg.content.toLowerCase().includes(searchLower);\n const authorMatch = msg.author.username.toLowerCase().includes(searchLower);\n return contentMatch || authorMatch;\n }) || null;\n }\n\n if (!targetMessage) {\n await callback({\n text: \"I couldn't find the message you want to pin. Try being more specific or use 'last message'.\",\n source: 'discord',\n });\n return;\n }\n\n // Check if already pinned\n if (targetMessage.pinned) {\n await callback({\n text: 'That message is already pinned.',\n source: 'discord',\n });\n return;\n }\n\n // Pin the message\n try {\n await targetMessage.pin();\n\n const response: Content = {\n text: `I've pinned the message from ${targetMessage.author.username}.`,\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n runtime.logger.error({ src: 'plugin:discord:action:pin-message', agentId: runtime.agentId, error: error instanceof Error ? error.message : String(error) }, 'Failed to pin message');\n await callback({\n text: \"I couldn't pin that message. The channel might have reached the maximum number of pinned messages (50).\",\n source: 'discord',\n });\n }\n } catch (error) {\n runtime.logger.error({ src: 'plugin:discord:action:pin-message', agentId: runtime.agentId, error: error instanceof Error ? error.message : String(error) }, 'Error pinning message');\n await callback({\n text: 'I encountered an error while trying to pin the message. Please make sure I have the necessary permissions.',\n source: 'discord',\n });\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'pin that message',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll pin that message for you.\",\n actions: ['PIN_MESSAGE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'pin the announcement john just made',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll find and pin john's announcement.\",\n actions: ['PIN_MESSAGE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: \"pin the last message, it's important\",\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: 'Pinning the last message to keep it visible.',\n actions: ['PIN_MESSAGE'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default pinMessage;\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 parseJSONObjectFromText,\n} from '@elizaos/core';\nimport { DiscordService } from '../service';\nimport { DISCORD_SERVICE_NAME } from '../constants';\nimport { type TextChannel, type Message, PermissionsBitField } from 'discord.js';\n\n/**\n * Template for extracting message reference for unpinning.\n */\nexport const unpinMessageTemplate = `# Unpinning a Discord message\n{{recentMessages}}\n\n# Instructions: {{senderName}} wants to unpin a message. Extract which message they want to unpin.\n\nExamples:\n- \"unpin that message\" -> messageRef: \"last_pinned\"\n- \"unpin the last pinned message\" -> messageRef: \"last_pinned\"\n- \"unpin john's message\" -> messageRef: \"john\"\n- \"unpin message about the meeting\" -> messageRef: \"meeting\"\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"messageRef\": \"<last_pinned|message-id|search-text>\"\n}\n\\`\\`\\`\n`;\n\nconst getMessageRef = async (\n runtime: IAgentRuntime,\n _message: Memory,\n state: State\n): Promise<{\n messageRef: string;\n} | null> => {\n const prompt = composePromptFromState({\n state,\n template: unpinMessageTemplate,\n });\n\n for (let i = 0; i < 3; i++) {\n const response = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n\n const parsedResponse = parseJSONObjectFromText(response);\n if (parsedResponse?.messageRef) {\n return {\n messageRef: parsedResponse.messageRef,\n };\n }\n }\n return null;\n};\n\nexport const unpinMessage: Action = {\n name: 'UNPIN_MESSAGE',\n similes: ['UNPIN_MESSAGE', 'UNPIN_MSG', 'UNPIN_THIS', 'UNPIN_THAT', 'REMOVE_PIN', 'DELETE_PIN'],\n description: 'Unpin a message in a Discord channel.',\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n return message.content.source === 'discord';\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback\n ) => {\n const discordService = runtime.getService(DISCORD_SERVICE_NAME) as DiscordService;\n\n if (!discordService || !discordService.client) {\n await callback({\n text: 'Discord service is not available.',\n source: 'discord',\n });\n return;\n }\n\n const messageInfo = await getMessageRef(runtime, message, state);\n if (!messageInfo) {\n await callback({\n text: \"I couldn't understand which message you want to unpin. Please be more specific.\",\n source: 'discord',\n });\n return;\n }\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n if (!room?.channelId) {\n await callback({\n text: \"I couldn't determine the current channel.\",\n source: 'discord',\n });\n return;\n }\n\n const channel = await discordService.client.channels.fetch(room.channelId);\n if (!channel || !channel.isTextBased()) {\n await callback({\n text: 'I can only unpin messages in text channels.',\n source: 'discord',\n });\n return;\n }\n\n const textChannel = channel as TextChannel;\n\n // Check bot permissions\n const botMember = textChannel.guild?.members.cache.get(discordService.client.user!.id);\n if (botMember) {\n const permissions = textChannel.permissionsFor(botMember);\n if (!permissions?.has(PermissionsBitField.Flags.ManageMessages)) {\n await callback({\n text: \"I don't have permission to unpin messages in this channel. I need the 'Manage Messages' permission.\",\n source: 'discord',\n });\n return;\n }\n }\n\n let targetMessage: Message | null = null;\n\n // Get pinned messages\n const pinnedMessages = await textChannel.messages.fetchPinned();\n\n if (pinnedMessages.size === 0) {\n await callback({\n text: 'There are no pinned messages in this channel.',\n source: 'discord',\n });\n return;\n }\n\n // Find the target message\n if (messageInfo.messageRef === 'last_pinned' || messageInfo.messageRef === 'last') {\n // Get the most recently created pinned message (since we can't sort by pin time)\n targetMessage = Array.from(pinnedMessages.values()).sort(\n (a, b) => b.createdTimestamp - a.createdTimestamp\n )[0];\n } else if (/^\\d+$/.test(messageInfo.messageRef)) {\n // It's a message ID\n targetMessage = pinnedMessages.get(messageInfo.messageRef) || null;\n } else {\n // Search for message by content/author in pinned messages\n const searchLower = messageInfo.messageRef.toLowerCase();\n\n targetMessage =\n Array.from(pinnedMessages.values()).find((msg) => {\n const contentMatch = msg.content.toLowerCase().includes(searchLower);\n const authorMatch = msg.author.username.toLowerCase().includes(searchLower);\n return contentMatch || authorMatch;\n }) || null;\n }\n\n if (!targetMessage) {\n await callback({\n text: \"I couldn't find a pinned message matching your description.\",\n source: 'discord',\n });\n return;\n }\n\n // Unpin the message\n try {\n await targetMessage.unpin();\n\n const response: Content = {\n text: `I've unpinned the message from ${targetMessage.author.username}.`,\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n runtime.logger.error({ src: 'plugin:discord:action:unpin-message', agentId: runtime.agentId, error: error instanceof Error ? error.message : String(error) }, 'Failed to unpin message');\n await callback({\n text: \"I couldn't unpin that message. Please try again.\",\n source: 'discord',\n });\n }\n } catch (error) {\n runtime.logger.error({ src: 'plugin:discord:action:unpin-message', agentId: runtime.agentId, error: error instanceof Error ? error.message : String(error) }, 'Error unpinning message');\n await callback({\n text: 'I encountered an error while trying to unpin the message. Please make sure I have the necessary permissions.',\n source: 'discord',\n });\n }\n },\n examples: [\n [\n {\n name: '{{name1}}',\n content: {\n text: 'unpin the last pinned message',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll unpin the most recent pinned message.\",\n actions: ['UNPIN_MESSAGE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: 'unpin the message about the old meeting schedule',\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll find and unpin the message about the meeting schedule.\",\n actions: ['UNPIN_MESSAGE'],\n },\n },\n ],\n [\n {\n name: '{{name1}}',\n content: {\n text: \"remove the pin from john's announcement\",\n },\n },\n {\n name: '{{name2}}',\n content: {\n text: \"I'll unpin john's announcement.\",\n actions: ['UNPIN_MESSAGE'],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default unpinMessage;\n","import {\n type Action,\n type ActionExample,\n type Content,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n type State,\n} from \"@elizaos/core\";\nimport { DiscordService } from \"../service\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport { type Guild } from \"discord.js\";\n\nconst formatServerInfo = (guild: Guild, detailed: boolean = false): string => {\n const createdAt = new Date(guild.createdAt).toLocaleDateString();\n const memberCount = guild.memberCount.toLocaleString();\n const channelCount = guild.channels.cache.size.toLocaleString();\n const roleCount = guild.roles.cache.size.toLocaleString();\n const emojiCount = guild.emojis.cache.size.toLocaleString();\n const boostLevel = guild.premiumTier;\n const boostCount = (guild.premiumSubscriptionCount || 0).toLocaleString();\n\n const basicInfo = [\n `🏛️ **Server Information for ${guild.name}**`,\n `**ID:** ${guild.id}`,\n `**Owner:** <@${guild.ownerId}>`,\n `**Created:** ${createdAt}`,\n `**Members:** ${memberCount}`,\n `**Channels:** ${channelCount}`,\n `**Roles:** ${roleCount}`,\n `**Server Level:** ${boostLevel} (${boostCount} boosts)`,\n ];\n\n if (detailed) {\n const textChannels = guild.channels.cache\n .filter((ch) => ch.isTextBased())\n .size.toLocaleString();\n const voiceChannels = guild.channels.cache\n .filter((ch) => ch.isVoiceBased())\n .size.toLocaleString();\n const categories = guild.channels.cache\n .filter((ch) => ch.type === 4)\n .size.toLocaleString(); // CategoryChannel type\n const activeThreads = guild.channels.cache\n .filter((ch) => ch.isThread() && !ch.archived)\n .size.toLocaleString();\n const stickerCount = guild.stickers.cache.size.toLocaleString();\n\n const features =\n guild.features.length > 0\n ? guild.features\n .map((f) => f.toLowerCase().replace(/_/g, \" \"))\n .join(\", \")\n : \"None\";\n\n const detailedInfo = [\n \"\",\n \"📊 **Detailed Statistics**\",\n `**Text Channels:** ${textChannels}`,\n `**Voice Channels:** ${voiceChannels}`,\n `**Categories:** ${categories}`,\n `**Active Threads:** ${activeThreads}`,\n `**Custom Emojis:** ${emojiCount}`,\n `**Stickers:** ${stickerCount}`,\n \"\",\n \"🎯 **Server Features**\",\n `**Verification Level:** ${guild.verificationLevel}`,\n `**Content Filter:** ${guild.explicitContentFilter}`,\n `**2FA Requirement:** ${guild.mfaLevel === 1 ? \"Enabled\" : \"Disabled\"}`,\n `**Features:** ${features}`,\n ];\n\n if (guild.description) {\n detailedInfo.push(`**Description:** ${guild.description}`);\n }\n\n if (guild.vanityURLCode) {\n detailedInfo.push(`**Vanity URL:** discord.gg/${guild.vanityURLCode}`);\n }\n\n return [...basicInfo, ...detailedInfo].join(\"\\n\");\n }\n\n return basicInfo.join(\"\\n\");\n};\n\nexport const serverInfo: Action = {\n name: \"SERVER_INFO\",\n similes: [\n \"SERVER_INFO\",\n \"GUILD_INFO\",\n \"SERVER_STATS\",\n \"SERVER_DETAILS\",\n \"ABOUT_SERVER\",\n \"SERVER_INFORMATION\",\n \"CHECK_SERVER\",\n ],\n description:\n \"Get information about the current Discord server including member count, creation date, and other statistics.\",\n validate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n return message.content.source === \"discord\";\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options: any,\n callback: HandlerCallback,\n ) => {\n const discordService = runtime.getService(\n DISCORD_SERVICE_NAME,\n ) as DiscordService;\n\n if (!discordService || !discordService.client) {\n await callback({\n text: \"Discord service is not available.\",\n source: \"discord\",\n });\n return;\n }\n\n try {\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n const serverId = room?.messageServerId;\n if (!serverId) {\n await callback({\n text: \"I couldn't determine the current server.\",\n source: \"discord\",\n });\n return;\n }\n\n const guild = await discordService.client.guilds.fetch(serverId);\n\n // Check if the request is for detailed info\n const messageText = message.content.text?.toLowerCase() || \"\";\n const isDetailed =\n messageText.includes(\"detailed\") ||\n messageText.includes(\"full\") ||\n messageText.includes(\"stats\") ||\n messageText.includes(\"statistics\");\n\n const infoText = formatServerInfo(guild, isDetailed);\n\n const response: Content = {\n text: infoText,\n source: message.content.source,\n };\n\n await callback(response);\n } catch (error) {\n runtime.logger.error(\n {\n src: \"plugin:discord:action:server-info\",\n agentId: runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error getting server info\",\n );\n await callback({\n text: \"I encountered an error while getting server information. Please try again.\",\n source: \"discord\",\n });\n }\n },\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"show server info\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll get the server information for you.\",\n actions: [\"SERVER_INFO\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"what are the server stats?\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Let me fetch the server statistics.\",\n actions: [\"SERVER_INFO\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"give me detailed server information\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll provide detailed information about this server.\",\n actions: [\"SERVER_INFO\"],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default serverInfo;\n","import type { IAgentRuntime, Memory, Provider, State } from '@elizaos/core';\nimport { ChannelType } from '@elizaos/core';\nimport type { GuildChannel } from 'discord.js';\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 const channelId = room.channelId ?? '';\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 (!channelId) {\n runtime.logger.error({ src: 'plugin:discord:provider:channelState', agentId: runtime.agentId, roomId: room.id }, 'No channel ID found');\n return {\n data: {\n room,\n channelType,\n },\n values: {\n channelType,\n },\n text: '',\n };\n }\n\n const discordService = runtime.getService(ServiceType.DISCORD) as DiscordService;\n if (!discordService) {\n runtime.logger.warn({ src: 'plugin:discord:provider:channelState', agentId: runtime.agentId, channelId }, 'No discord client found');\n return {\n data: {\n room,\n channelType,\n channelId,\n },\n values: {\n channelType,\n channelId,\n },\n text: '',\n };\n }\n\n // Look up guild via channel instead of serverId (which is now a UUID)\n // Try cache first, then fetch if not cached (handles cold start / partial cache scenarios)\n let channel = discordService.client?.channels.cache.get(channelId) as GuildChannel | undefined;\n if (!channel && discordService.client) {\n try {\n channel = await discordService.client.channels.fetch(channelId) as GuildChannel | undefined;\n } catch (fetchError) {\n runtime.logger.debug({ src: 'plugin:discord:provider:channelState', agentId: runtime.agentId, channelId, error: fetchError instanceof Error ? fetchError.message : String(fetchError) }, 'Failed to fetch channel');\n }\n }\n const guild = channel?.guild;\n if (!guild) {\n runtime.logger.warn({ src: 'plugin:discord:provider:channelState', agentId: runtime.agentId, channelId }, 'Guild not found for channel (not in cache and fetch failed)');\n return {\n data: {\n room,\n channelType,\n channelId,\n },\n values: {\n channelType,\n channelId,\n },\n text: '',\n };\n }\n serverName = guild.name;\n\n responseText = `${agentName} is currently having a conversation in the channel \\`#${channel?.name || channelId}\\` in the server \\`${serverName}\\``;\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 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 type {\n Character,\n EntityPayload,\n EventPayload,\n MessagePayload,\n WorldPayload,\n Memory,\n Media,\n ChannelType,\n IAgentRuntime,\n} from \"@elizaos/core\";\nimport type {\n Channel,\n Client as DiscordJsClient,\n Interaction,\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 // slash commands event\n SLASH_COMMAND = \"DISCORD_SLASH_COMMAND\",\n MODAL_SUBMIT = \"DISCORD_MODAL_SUBMIT\",\n\n // Reaction events\n REACTION_RECEIVED = \"DISCORD_REACTION_RECEIVED\",\n REACTION_REMOVED = \"DISCORD_REACTION_REMOVED\",\n\n // Server/World events\n WORLD_JOINED = \"DISCORD_WORLD_JOINED\",\n WORLD_CONNECTED = \"DISCORD_SERVER_CONNECTED\",\n\n // User/Entity events\n // Note: ENTITY_JOINED is emitted when a user joins a Discord guild (server).\n // This is different from the core EventType.ENTITY_JOINED which requires a roomId.\n // In Discord terms: guild membership != channel membership. Users join the \"world\"\n // (guild) but only join specific \"rooms\" (channels) when they first interact there.\n // Use this event for Discord-specific handling like welcome messages or role checks.\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 // Permission audit events\n CHANNEL_PERMISSIONS_CHANGED = \"DISCORD_CHANNEL_PERMISSIONS_CHANGED\",\n ROLE_PERMISSIONS_CHANGED = \"DISCORD_ROLE_PERMISSIONS_CHANGED\",\n MEMBER_ROLES_CHANGED = \"DISCORD_MEMBER_ROLES_CHANGED\",\n ROLE_CREATED = \"DISCORD_ROLE_CREATED\",\n ROLE_DELETED = \"DISCORD_ROLE_DELETED\",\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 *\n * Emitted via `DiscordEventTypes.ENTITY_JOINED` when a user joins a Discord guild.\n *\n * **Important:** This event represents guild membership, not room/channel membership.\n * The payload contains `worldId` (the guild) but no `roomId` because the user hasn't\n * joined any specific channel yet. The entity will be synced to specific rooms when\n * they first interact (send a message, join voice, etc.).\n *\n * Use this event for Discord-specific handling like:\n * - Sending welcome messages\n * - Assigning default roles\n * - Moderation checks (account age, etc.)\n * - Logging new member joins\n */\nexport interface DiscordUserJoinedPayload extends EntityPayload {\n /** The original Discord.js GuildMember object for full Discord API access */\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// Permission Audit Types\n// ============================================================================\n\n/**\n * Permission state in an overwrite or role\n */\nexport type PermissionState = \"ALLOW\" | \"DENY\" | \"NEUTRAL\";\n\n/**\n * A single permission change\n */\nexport interface PermissionDiff {\n /** The permission name (e.g., 'ManageMessages', 'Administrator') */\n permission: string;\n /** Previous state */\n oldState: PermissionState;\n /** New state */\n newState: PermissionState;\n}\n\n/**\n * Information about who made a change, from audit logs\n */\nexport interface AuditInfo {\n /** Discord user ID of the executor */\n executorId: string;\n /** Discord username#discriminator or username of the executor */\n executorTag: string;\n /** Reason provided for the action, if any */\n reason: string | null;\n}\n\n/**\n * Minimal runtime interface for permission payloads.\n * Using a minimal interface avoids version mismatches across packages.\n */\nexport interface PermissionPayloadRuntime {\n getService(name: string): unknown;\n getSetting(key: string): unknown;\n logger: {\n debug(msg: string): void;\n info(msg: string): void;\n warn(msg: string): void;\n error(msg: string): void;\n };\n}\n\n/**\n * Payload for DISCORD_CHANNEL_PERMISSIONS_CHANGED event\n * Emitted when channel permission overwrites are created, updated, or deleted\n */\nexport interface ChannelPermissionsChangedPayload {\n /** Runtime instance */\n runtime: PermissionPayloadRuntime;\n /** Guild information */\n guild: { id: string; name: string };\n /** Channel where permissions changed */\n channel: { id: string; name: string };\n /** Target of the permission overwrite (role or user) */\n target: { type: \"role\" | \"user\"; id: string; name: string };\n /** What happened to the overwrite */\n action: \"CREATE\" | \"UPDATE\" | \"DELETE\";\n /** List of permission changes */\n changes: PermissionDiff[];\n /** Audit log info (null if unavailable) */\n audit: AuditInfo | null;\n}\n\n/**\n * Payload for DISCORD_ROLE_PERMISSIONS_CHANGED event\n * Emitted when a role's permissions are modified\n */\nexport interface RolePermissionsChangedPayload {\n /** Runtime instance */\n runtime: PermissionPayloadRuntime;\n /** Guild information */\n guild: { id: string; name: string };\n /** Role that was modified */\n role: { id: string; name: string };\n /** List of permission changes */\n changes: PermissionDiff[];\n /** Audit log info (null if unavailable) */\n audit: AuditInfo | null;\n}\n\n/**\n * Payload for DISCORD_MEMBER_ROLES_CHANGED event\n * Emitted when roles are added or removed from a member\n */\nexport interface MemberRolesChangedPayload {\n /** Runtime instance */\n runtime: PermissionPayloadRuntime;\n /** Guild information */\n guild: { id: string; name: string };\n /** Member whose roles changed */\n member: { id: string; tag: string };\n /** Roles that were added */\n added: Array<{ id: string; name: string; permissions: string[] }>;\n /** Roles that were removed */\n removed: Array<{ id: string; name: string; permissions: string[] }>;\n /** Audit log info (null if unavailable) */\n audit: AuditInfo | null;\n}\n\n/**\n * Payload for DISCORD_ROLE_CREATED and DISCORD_ROLE_DELETED events\n */\nexport interface RoleLifecyclePayload {\n /** Runtime instance */\n runtime: PermissionPayloadRuntime;\n /** Guild information */\n guild: { id: string; name: string };\n /** Role that was created or deleted */\n role: { id: string; name: string; permissions: string[] };\n /** Audit log info (null if unavailable) */\n audit: AuditInfo | null;\n}\n\n/**\n * Discord slash command definition with hybrid permission system.\n *\n * This interface combines Discord's native permission features with ElizaOS-specific\n * controls to provide a flexible, developer-friendly API for command permissions.\n *\n * ## Design Philosophy\n * - **Zero config = works everywhere** (default behavior)\n * - **Simple flags** for common use cases (guild-only, admin-only, etc.)\n * - **Native Discord features** where possible (leverages Discord's permission system)\n * - **Programmatic control** for advanced scenarios (custom validators)\n *\n * ## Permission Layers\n *\n * Commands go through multiple permission checks in this order:\n * 1. **Discord native checks** (handled by Discord before interaction fires):\n * - `requiredPermissions`: User must have these Discord permissions\n * - `guildOnly`/`contexts`: Command availability in guilds vs DMs\n * 2. **ElizaOS channel whitelist** (CHANNEL_IDS env var):\n * - If set, commands only work in whitelisted channels\n * - Unless `bypassChannelWhitelist: true` is set\n * 3. **Custom validator** (if provided):\n * - Runs after all other checks\n * - Full programmatic control for complex logic\n *\n * @example\n * // Default: works everywhere\n * { name: 'help', description: 'Show help' }\n *\n * @example\n * // Guild-only command\n * { name: 'serverinfo', description: 'Show server info', guildOnly: true }\n *\n * @example\n * // Requires Discord permission\n * {\n * name: 'config',\n * description: 'Configure bot',\n * requiredPermissions: PermissionFlagsBits.ManageGuild\n * }\n *\n * @example\n * // Bypasses channel whitelist (works in all channels)\n * {\n * name: 'dumpchannel',\n * description: 'Export channel',\n * bypassChannelWhitelist: true\n * }\n *\n * @example\n * // Advanced: custom validation\n * {\n * name: 'admin',\n * description: 'Admin command',\n * validator: async (interaction, runtime) => {\n * // Custom logic here\n * return interaction.user.id === runtime.getSetting('ADMIN_USER_ID');\n * }\n * }\n */\nexport interface DiscordSlashCommand {\n /** Command name (must be lowercase, no spaces) */\n name: string;\n\n /** Command description shown in Discord UI */\n description: string;\n\n /** Command options/parameters */\n options?: Array<{\n name: string;\n type: number;\n description: string;\n required?: boolean;\n channel_types?: number[];\n }>;\n\n // ==================== Simple Permission Flags ====================\n\n /**\n * If true, command only works in guilds (not DMs).\n * Transformed to Discord's `contexts: [0]` during registration.\n *\n * Use this for commands that need server context (e.g., server info, moderation).\n */\n guildOnly?: boolean;\n\n /**\n * If true, command bypasses CHANNEL_IDS whitelist restrictions.\n *\n * Use this for utility commands that should work everywhere regardless of\n * channel restrictions (e.g., help, export, diagnostics).\n *\n * Note: This is an ElizaOS-specific feature, not a Discord native feature.\n * Discord handles this via Server Settings > Integrations UI, but we provide\n * programmatic control for better developer experience.\n */\n bypassChannelWhitelist?: boolean;\n\n // ==================== Discord Native Permissions ====================\n\n /**\n * Discord permission bitfield required to use this command.\n * Transformed to `default_member_permissions` during registration.\n *\n * Common values (from Discord.js PermissionFlagsBits):\n * - `ManageGuild`: Server settings\n * - `ManageChannels`: Channel management\n * - `ManageMessages`: Delete messages\n * - `BanMembers`: Ban users\n * - `KickMembers`: Kick users\n * - `ModerateMembers`: Timeout users\n * - `ManageRoles`: Role management\n * - `Administrator`: Full access\n *\n * Set to `null` to explicitly allow everyone (overrides Discord's defaults).\n *\n * @example\n * requiredPermissions: PermissionFlagsBits.ManageGuild\n *\n * @example\n * // Multiple permissions (combine with bitwise OR)\n * requiredPermissions: PermissionFlagsBits.ManageMessages | PermissionFlagsBits.ModerateMembers\n */\n requiredPermissions?: bigint | string | null;\n\n // ==================== Advanced Options ====================\n\n /**\n * Raw Discord contexts array. Overrides `guildOnly` if provided.\n * - 0 = Guild (server channels)\n * - 1 = BotDM (DMs with the bot)\n * - 2 = PrivateChannel (group DMs)\n *\n * Most developers should use `guildOnly` instead of this.\n */\n contexts?: number[];\n\n /**\n * If provided, register this command only in specific guilds (servers).\n * Otherwise, command is registered globally and appears in all guilds.\n *\n * Guild-specific commands update instantly, while global commands can take\n * up to 1 hour to propagate. Use this for testing or server-specific features.\n *\n * @example\n * guildIds: ['123456789012345678', '987654321098765432']\n */\n guildIds?: string[];\n\n /**\n * Custom validation function for advanced permission logic.\n *\n * Called after Discord's native checks and channel whitelist checks.\n * Return `true` to allow the command, `false` to block it.\n *\n * **Important**: If your validator returns `false`, you should respond to the interaction\n * before returning to provide context to the user. If you don't respond, a generic\n * \"You do not have permission to use this command.\" message will be sent automatically.\n *\n * This is useful for:\n * - ElizaOS-specific permission systems (when implemented)\n * - Complex business logic (e.g., rate limiting, feature flags)\n * - Dynamic permissions based on runtime state\n *\n * @param interaction - The Discord interaction object (can be used to reply/respond)\n * @param runtime - The ElizaOS runtime instance\n * @returns Promise resolving to true if command should execute, false otherwise\n *\n * @example\n * // Simple validator without custom response (uses default)\n * validator: async (interaction, runtime) => {\n * const userId = interaction.user.id;\n * const allowedUsers = runtime.getSetting('ALLOWED_USERS')?.split(',') ?? [];\n * return allowedUsers.includes(userId);\n * }\n *\n * @example\n * // Validator with custom rejection message\n * validator: async (interaction, runtime) => {\n * const userId = interaction.user.id;\n * const isAllowed = await checkUserPermission(userId);\n * if (!isAllowed) {\n * await interaction.reply({\n * content: 'This command is only available to premium users.',\n * ephemeral: true,\n * });\n * return false;\n * }\n * return true;\n * }\n */\n validator?: (\n interaction: Interaction,\n runtime: IAgentRuntime,\n ) => Promise<boolean>;\n}\n\n/**\n * Payload for DISCORD_REGISTER_COMMANDS event\n * Used to register slash commands from other plugins\n */\nexport interface DiscordRegisterCommandsPayload extends EventPayload {\n commands: DiscordSlashCommand[];\n /** @deprecated Use bypassChannelWhitelist on DiscordSlashCommand instead */\n allowAllChannels?: Record<string, boolean>;\n}\n\n/**\n * Discord-specific slash commands payload for command execution\n */\nexport interface DiscordSlashCommandPayload {\n interaction: Interaction;\n client: DiscordJsClient;\n commands: DiscordSlashCommand[];\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.SLASH_COMMAND]: DiscordSlashCommandPayload;\n [DiscordEventTypes.MODAL_SUBMIT]: DiscordSlashCommandPayload;\n [DiscordEventTypes.VOICE_STATE_CHANGED]: DiscordVoiceStateChangedPayload;\n [DiscordEventTypes.CHANNEL_PERMISSIONS_CHANGED]: ChannelPermissionsChangedPayload;\n [DiscordEventTypes.ROLE_PERMISSIONS_CHANGED]: RolePermissionsChangedPayload;\n [DiscordEventTypes.MEMBER_ROLES_CHANGED]: MemberRolesChangedPayload;\n [DiscordEventTypes.ROLE_CREATED]: RoleLifecyclePayload;\n [DiscordEventTypes.ROLE_DELETED]: RoleLifecyclePayload;\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 getChannelType: (channel: Channel) => Promise<ChannelType>;\n buildMemoryFromMessage: (\n message: Message,\n options?: {\n processedContent?: string;\n processedAttachments?: Media[];\n extraContent?: Record<string, any>;\n extraMetadata?: Record<string, any>;\n },\n ) => Promise<Memory | null>;\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\n// maybe discord character settings makes more sense?\nexport interface DiscordSettings {\n allowedChannelIds?: string[];\n shouldIgnoreBotMessages?: boolean;\n shouldIgnoreDirectMessages?: boolean;\n shouldRespondOnlyToMentions?: boolean;\n //[key: string]: any; // still allows extension\n}\n\n/**\n * State tracking for channel history spider to avoid re-fetching\n */\nexport interface ChannelSpiderState {\n /** Discord channel ID */\n channelId: string;\n /** Oldest message ID fetched (for going further back) */\n oldestMessageId?: string;\n /** Newest message ID fetched (for catching up) */\n newestMessageId?: string;\n /** Timestamp of oldest message (for comparison) */\n oldestMessageTimestamp?: number;\n /** Timestamp of newest message (for comparison) */\n newestMessageTimestamp?: number;\n /** Timestamp of last spider run */\n lastSpideredAt: number;\n /** True if we've reached the beginning of channel history */\n fullyBackfilled: boolean;\n}\n\n/**\n * Batch handler for processing messages as they arrive during history fetch\n * @returns false to stop fetching early, void/true to continue\n */\nexport type BatchHandler = (\n batch: Memory[],\n stats: { page: number; totalFetched: number; totalStored: number },\n) => Promise<boolean | void> | boolean | void;\n\n/**\n * Options for fetching channel history\n */\nexport interface ChannelHistoryOptions {\n /** Maximum number of messages to fetch (default: unlimited) */\n limit?: number;\n /** Force re-fetch, ignoring spider state */\n force?: boolean;\n /** Callback to process each batch of messages as they arrive */\n onBatch?: BatchHandler;\n /** Start fetching before this message ID */\n before?: string;\n /** Start fetching after this message ID (for catching up) */\n after?: string;\n}\n\n/**\n * Result from fetching channel history\n */\nexport interface ChannelHistoryResult {\n /** Fetched messages (empty if onBatch was used) */\n messages: Memory[];\n /** Statistics about the fetch operation */\n stats: {\n /** Total messages fetched from Discord */\n fetched: number;\n /** Total messages stored/processed */\n stored: number;\n /** Number of pages fetched */\n pages: number;\n /** Whether the channel is now fully backfilled */\n fullyBackfilled: boolean;\n };\n}\n","import { getVoiceConnection } from '@discordjs/voice';\nimport type { IAgentRuntime, Memory, Provider, State, UUID } from '@elizaos/core';\nimport { ChannelType } from '@elizaos/core';\nimport type { GuildChannel } from 'discord.js';\nimport type { DiscordService } from '../service';\nimport { ServiceType } from '../types';\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 channelId = room.channelId;\n const agentName = state?.agentName || 'The agent';\n\n if (!channelId) {\n runtime.logger.warn({ src: 'plugin:discord:provider:voiceState', roomId: room.id }, 'No channel ID found');\n return {\n data: {\n isInVoiceChannel: false,\n room,\n },\n values: {\n isInVoiceChannel: 'false',\n roomType: room.type,\n },\n text: `${agentName} is not currently in a voice channel`,\n };\n }\n\n // Look up guild via channel to get the Discord guild ID for voice connection\n const discordService = runtime.getService(ServiceType.DISCORD) as DiscordService;\n if (!discordService?.client) {\n runtime.logger.warn({ src: 'plugin:discord:provider:voiceState' }, 'Discord service not available');\n return {\n data: {\n isInVoiceChannel: false,\n room,\n },\n values: {\n isInVoiceChannel: 'false',\n },\n text: `${agentName} is not currently in a voice channel`,\n };\n }\n\n // Try cache first, then fetch if not cached (handles cold start / partial cache scenarios)\n let channel = discordService.client.channels.cache.get(channelId) as GuildChannel | undefined;\n if (!channel) {\n try {\n channel = await discordService.client.channels.fetch(channelId) as GuildChannel | undefined;\n } catch (fetchError) {\n runtime.logger.debug({ src: 'plugin:discord:provider:voiceState', channelId, error: fetchError instanceof Error ? fetchError.message : String(fetchError) }, 'Failed to fetch channel');\n }\n }\n const guildId = channel?.guild?.id;\n\n if (!guildId) {\n runtime.logger.warn({ src: 'plugin:discord:provider:voiceState', channelId }, 'Could not find guild for channel (not in cache and fetch failed)');\n return {\n data: {\n isInVoiceChannel: false,\n room,\n },\n values: {\n isInVoiceChannel: 'false',\n },\n text: `${agentName} is not currently in a voice channel`,\n };\n }\n\n const connection = getVoiceConnection(guildId);\n\n if (!connection) {\n return {\n data: {\n isInVoiceChannel: false,\n room,\n },\n values: {\n isInVoiceChannel: 'false',\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 as UUID);\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 channelName = room.name;\n\n return {\n data: {\n isInVoiceChannel: true,\n room,\n world,\n connection,\n channelId,\n channelName,\n },\n values: {\n isInVoiceChannel: 'true',\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 Media,\n type Entity,\n EventType,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n MemoryType,\n Role,\n Service,\n stringToUuid,\n type TargetInfo,\n type UUID,\n type World,\n createUniqueUuid,\n} from \"@elizaos/core\";\n\n/**\n * IMPORTANT: Discord ID Handling - Why stringToUuid() instead of asUUID()\n *\n * Discord uses \"snowflake\" IDs - large 64-bit integers represented as strings\n * (e.g., \"1253563208833433701\"). These are NOT valid UUIDs.\n *\n * UUID format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12 hex digits with dashes)\n * Discord ID: 1253563208833433701 (plain number string)\n *\n * The two UUID-related functions behave differently:\n *\n * - `asUUID(str)` - VALIDATES that the string is already a valid UUID format.\n * If not, it throws: \"Error: Invalid UUID format: 1253563208833433701\"\n * Use only when you're certain the input is already a valid UUID.\n *\n * - `stringToUuid(str)` - CONVERTS any string into a deterministic UUID by hashing it.\n * Always succeeds. The same input always produces the same UUID output.\n * Use this for Discord snowflake IDs.\n *\n * When working with Discord IDs in ElizaOS:\n *\n * 1. `stringToUuid(discordId)` - For storing Discord IDs in UUID fields (e.g., `messageServerId`).\n *\n * 2. `createUniqueUuid(runtime, discordId)` - For `worldId` and `roomId`. This adds the agent's\n * ID to the hash, ensuring each agent has its own unique namespace for the same Discord server.\n *\n * 3. `messageServerId` - The correct property name for server IDs on Room and World objects.\n * The deprecated `serverId` property should not be used.\n *\n * 4. Discord-specific events (e.g., DiscordEventTypes.VOICE_STATE_UPDATE) are not in core's\n * EventPayloadMap. When emitting these events, cast to `string[]` and payload to `any`\n * to use the generic emitEvent overload.\n */\nimport {\n AttachmentBuilder,\n AuditLogEvent,\n type Channel,\n ChannelType as DiscordChannelType,\n Client as DiscordJsClient,\n Events,\n GatewayIntentBits,\n type Guild,\n type GuildChannel,\n type GuildMember,\n type GuildTextBasedChannel,\n type Message,\n type MessageReaction,\n type PartialMessageReaction,\n type PartialUser,\n Partials,\n PermissionsBitField,\n type Role as DiscordRole,\n type TextChannel,\n type User,\n type Interaction,\n Collection,\n} from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"./constants\";\nimport { getDiscordSettings } from \"./environment\";\nimport { MessageManager } from \"./messages\";\n\nimport {\n DiscordEventTypes,\n type IDiscordService,\n type DiscordSettings,\n type DiscordSlashCommand,\n type DiscordRegisterCommandsPayload,\n type ChannelHistoryOptions,\n type ChannelHistoryResult,\n type ChannelSpiderState,\n} from \"./types\";\nimport {\n getAttachmentFileName,\n splitMessage,\n MAX_MESSAGE_LENGTH,\n} from \"./utils\";\nimport { generateInviteUrl } from \"./permissions\";\nimport { VoiceManager } from \"./voice\";\nimport {\n diffOverwrites,\n diffRolePermissions,\n diffMemberRoles,\n fetchAuditEntry,\n} from \"./permissionEvents\";\nimport {\n createCompatRuntime,\n type ICompatRuntime,\n type WorldCompat,\n} from \"./compat\";\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 // Override runtime type for messageServerId cross-core compatibility (see compat.ts)\n declare protected runtime: ICompatRuntime;\n\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 discordSettings: DiscordSettings;\n private userSelections: Map<string, { [key: string]: any }> = new Map();\n private timeouts: ReturnType<typeof setTimeout>[] = [];\n public clientReadyPromise: Promise<void> | null = null;\n private slashCommands: DiscordSlashCommand[] = [];\n private commandRegistrationQueue: Promise<void> = Promise.resolve();\n /**\n * Slash command names that should bypass allowed channel restrictions.\n */\n private allowAllSlashCommands: Set<string> = new Set();\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 * Set of dynamically added channel IDs through joinChannel action.\n * These are merged with allowedChannelIds for runtime channel management.\n */\n private dynamicChannelIds: Set<string> = new Set();\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 // Load Discord settings with proper priority (env vars > character settings > defaults)\n this.discordSettings = getDiscordSettings(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?.trim && channelIdsRaw.trim()) {\n this.allowedChannelIds = channelIdsRaw\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n allowedChannelIds: this.allowedChannelIds,\n },\n \"Channel restrictions enabled\",\n );\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 && token.trim() === \"\") || token === null) {\n this.runtime.logger.warn(\"Discord API Token not provided\");\n this.client = null;\n return;\n }\n\n try {\n const 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 this.client = client;\n\n this.runtime = createCompatRuntime(runtime);\n this.voiceManager = new VoiceManager(this, this.runtime);\n this.messageManager = new MessageManager(this, this.runtime);\n\n this.clientReadyPromise = new Promise((resolve, reject) => {\n // once logged in\n client.once(Events.ClientReady, async (readyClient) => {\n try {\n await this.onReady(readyClient);\n resolve();\n } catch (error) {\n this.runtime.logger.error(\n `Error in onReady: ${error instanceof Error ? error.message : String(error)}`,\n );\n reject(error);\n }\n });\n // Handle client errors that might prevent ready event\n client.once(Events.Error, (error) => {\n this.runtime.logger.error(\n `Discord client error: ${error instanceof Error ? error.message : String(error)}`,\n );\n reject(error);\n });\n // now start login\n client.login(token).catch((error) => {\n this.runtime.logger.error(\n `Failed to login to Discord: ${error instanceof Error ? error.message : String(error)}`,\n );\n if (this.client) {\n this.client.destroy().catch(() => {});\n }\n this.client = null;\n reject(error);\n });\n });\n\n // Attach error handler to prevent unhandled promise rejection\n // This ensures the promise rejection is handled even if no one awaits it immediately\n this.clientReadyPromise.catch((_error) => {\n // Error is already logged in the promise handlers above\n // This catch prevents unhandled promise rejection warnings\n // The promise is public and may be awaited elsewhere, but we need to handle\n // the case where it's not immediately awaited\n });\n\n this.setupEventListeners();\n // Note: send handler is registered automatically by runtime via registerSendHandlers() static method\n } catch (error) {\n runtime.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 * 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 // why we have this.runtime on the agent itself and this isn't a static\n runtime: IAgentRuntime,\n target: TargetInfo,\n content: Content,\n ): Promise<void> {\n if (!this.client?.isReady()) {\n runtime.logger.error(\"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.isChannelAllowed(target.channelId)\n ) {\n runtime.logger.warn(\n `Channel ${target.channelId} not in allowed list, 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 // user.dmChannel is a property (DMChannel | null), not a promise\n targetChannel = user.dmChannel ?? (await user.createDM());\n }\n } else {\n throw new Error(\"Discord SendHandler requires channelId or entityId.\");\n }\n\n if (!targetChannel) {\n // Safely serialize target for error message (target only contains strings, but be defensive)\n const targetStr = JSON.stringify(target, (_key, value) => {\n // Convert BigInt to string if somehow present\n if (typeof value === \"bigint\") {\n return value.toString();\n }\n return value;\n });\n throw new Error(\n `Could not find target Discord channel/DM for target: ${targetStr}`,\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 // Convert Media attachments to Discord AttachmentBuilder format\n const files: AttachmentBuilder[] = [];\n if (content.attachments && content.attachments.length > 0) {\n for (const media of content.attachments) {\n if (media.url) {\n const fileName = getAttachmentFileName(media);\n files.push(\n new AttachmentBuilder(media.url, { name: fileName }),\n );\n }\n }\n }\n\n const sentMessages: Message[] = [];\n const roomId = createUniqueUuid(runtime, targetChannel.id);\n const channelType = await this.getChannelType(\n targetChannel as Channel,\n );\n\n // Send message with text and/or attachments\n if (content.text || files.length > 0) {\n if (content.text) {\n // Split message if longer than Discord limit (uses safe buffer)\n const chunks = splitMessage(content.text, MAX_MESSAGE_LENGTH);\n if (chunks.length > 1) {\n // Send all chunks except the last one without files\n for (let i = 0; i < chunks.length - 1; i++) {\n const sent = await targetChannel.send(chunks[i]);\n sentMessages.push(sent);\n }\n // Send the last chunk with files (if any)\n const sent = await targetChannel.send({\n content: chunks[chunks.length - 1],\n files: files.length > 0 ? files : undefined,\n });\n sentMessages.push(sent);\n } else {\n // Single chunk - send with files (if any)\n const sent = await targetChannel.send({\n content: chunks[0],\n files: files.length > 0 ? files : undefined,\n });\n sentMessages.push(sent);\n }\n } else {\n // Only attachments, no text\n const sent = await targetChannel.send({\n files,\n });\n sentMessages.push(sent);\n }\n } else {\n runtime.logger.warn(\"No text content or attachments provided\");\n }\n\n // Ensure room/world/participant exist before saving to memory (FK constraints)\n const serverId =\n \"guild\" in targetChannel\n ? (targetChannel.guild?.id ?? targetChannel.id)\n : targetChannel.id;\n const worldId = createUniqueUuid(runtime, serverId) as UUID;\n const worldName =\n \"guild\" in targetChannel ? targetChannel.guild?.name : undefined;\n\n await this.runtime.ensureConnection({\n entityId: runtime.agentId,\n roomId,\n userName: this.client?.user?.username,\n name: this.client?.user?.displayName || this.client?.user?.username,\n source: \"discord\",\n channelId: targetChannel.id,\n messageServerId: stringToUuid(serverId),\n type: channelType,\n worldId,\n worldName,\n });\n\n // Save sent messages to memory\n for (const sentMsg of sentMessages) {\n try {\n // Only include attachments/actions in memory for messages that actually have attachments\n const hasAttachments = sentMsg.attachments.size > 0;\n\n const memory: Memory = {\n id: createUniqueUuid(runtime, sentMsg.id),\n entityId: runtime.agentId,\n agentId: runtime.agentId,\n roomId,\n content: {\n text: sentMsg.content || content.text || \" \",\n url: sentMsg.url,\n channelType,\n // Only include attachments for messages that actually have attachments\n ...(hasAttachments && content.attachments\n ? { attachments: content.attachments }\n : {}),\n // Include action whenever it exists, regardless of attachments\n ...(content.action ? { action: content.action } : {}),\n },\n metadata: {\n type: \"message\",\n },\n createdAt: sentMsg.createdTimestamp || Date.now(),\n };\n\n await runtime.createMemory(memory, \"messages\");\n runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: runtime.agentId,\n messageId: sentMsg.id,\n },\n \"Saved sent message to memory\",\n );\n } catch (error) {\n runtime.logger.warn(\n `Failed to save sent message ${sentMsg.id} to memory: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\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 runtime.logger.error(\n `Error sending message to ${JSON.stringify(target)}: ${error instanceof Error ? error.message : String(error)}`,\n );\n throw error;\n }\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 const listenCidsRaw = this.runtime.getSetting(\n \"DISCORD_LISTEN_CHANNEL_IDS\",\n ) as string | string[] | undefined;\n const listenCids = Array.isArray(listenCidsRaw)\n ? listenCidsRaw\n : listenCidsRaw &&\n typeof listenCidsRaw === \"string\" &&\n listenCidsRaw.trim()\n ? listenCidsRaw\n .trim()\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0)\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 && this.discordSettings.shouldIgnoreBotMessages)\n ) {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n authorId: message.author.id,\n isBot: message.author.bot,\n },\n \"Ignoring message from bot or self\",\n );\n return;\n }\n\n if (listenCids.includes(message.channel.id) && message) {\n // Use the reusable buildMemoryFromMessage method\n const newMessage = await this.buildMemoryFromMessage(message);\n\n if (!newMessage) {\n this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n messageId: message.id,\n },\n \"Failed to build memory from listen channel message\",\n );\n return;\n }\n\n // Emit event for listen channel handlers\n this.runtime.emitEvent(\n \"DISCORD_LISTEN_CHANNEL_MESSAGE\" as string,\n {\n runtime: this.runtime,\n message: newMessage,\n } as any,\n );\n }\n\n // Skip if channel restrictions are set and this channel is not allowed\n if (\n this.allowedChannelIds &&\n !this.isChannelAllowed(message.channel.id)\n ) {\n // check first whether the channel is a thread...\n const channel = await this.client?.channels.fetch(message.channel.id);\n\n this.runtime.emitEvent(\n \"DISCORD_NOT_IN_CHANNELS_MESSAGE\" as string,\n {\n runtime: this.runtime,\n message,\n } as any,\n );\n\n if (!channel) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId: message.channel.id,\n },\n \"Channel not found\",\n );\n return;\n }\n if (channel.isThread()) {\n if (!channel.parentId || !this.isChannelAllowed(channel.parentId)) {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n parentChannelId: channel.parentId,\n },\n \"Thread not in allowed channel\",\n );\n return;\n }\n } else {\n if (channel?.isTextBased()) {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId: channel.id,\n },\n \"Channel not allowed\",\n );\n }\n return;\n }\n }\n\n try {\n // Ensure messageManager exists\n this.messageManager?.handleMessage(message);\n } catch (error) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error handling message\",\n );\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.isChannelAllowed(reaction.message.channel.id)\n ) {\n return;\n }\n try {\n await this.handleReactionAdd(reaction, user);\n } catch (error) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error handling reaction add\",\n );\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.isChannelAllowed(reaction.message.channel.id)\n ) {\n return;\n }\n try {\n await this.handleReactionRemove(reaction, user);\n } catch (error) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error handling reaction remove\",\n );\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 this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error handling guild create\",\n );\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 this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error handling guild member add\",\n );\n }\n });\n\n // Interaction handlers\n //\n // Permission Check Flow for Slash Commands:\n // 1. Discord native checks (before this event fires):\n // - User has required permissions (default_member_permissions)\n // - Command is available in this context (guild vs DM)\n // 2. ElizaOS channel whitelist (here):\n // - If CHANNEL_IDS is set, check if channel is allowed\n // - Unless command has bypassChannelWhitelist flag\n // 3. Custom validator (here):\n // - Run command's validator function if provided\n // - Full programmatic control for complex logic\n //\n // Why this order?\n // - Discord's checks are free (handled before interaction fires)\n // - Channel whitelist is cheap (Set lookup)\n // - Custom validators can be expensive (async, database calls, etc.)\n this.client.on(\"interactionCreate\", async (interaction) => {\n const isSlashCommand = interaction.isCommand();\n const isModalSubmit = interaction.isModalSubmit();\n const isComponent = interaction.isMessageComponent();\n\n // Check if this slash command has bypass enabled\n const bypassChannelRestriction =\n isSlashCommand &&\n this.allowAllSlashCommands.has(interaction.commandName ?? \"\");\n\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n interactionType: interaction.type,\n commandName: isSlashCommand ? interaction.commandName : undefined,\n channelId: interaction.channelId,\n inGuild: interaction.inGuild(),\n bypassChannelRestriction,\n },\n \"[DiscordService] interactionCreate received\",\n );\n\n // ElizaOS Channel Whitelist Check\n // Follow-up interactions (modals, buttons, autocomplete) always bypass the channel whitelist\n // since they are responses to commands initiated by the user.\n // Slash commands respect the whitelist unless bypassChannelWhitelist: true.\n const isFollowUpInteraction = Boolean(\n interaction.isModalSubmit() ||\n interaction.isMessageComponent() ||\n interaction.isAutocomplete(),\n );\n\n // Skip if channel restrictions are set and this interaction is not in an allowed channel\n // - Follow-up interactions (modals, components, autocomplete) always bypass\n // - Slash commands respect whitelist unless they have bypassChannelWhitelist: true\n if (\n !isFollowUpInteraction &&\n this.allowedChannelIds &&\n interaction.channelId &&\n !this.isChannelAllowed(interaction.channelId) &&\n !bypassChannelRestriction\n ) {\n // For slash commands, send a response to avoid Discord's \"application did not respond\" error\n // Other interaction types (non-slash) can fail silently\n if (isSlashCommand && interaction.isCommand()) {\n try {\n await interaction.reply({\n content: \"This command is not available in this channel.\",\n ephemeral: true,\n });\n } catch (responseError) {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error:\n responseError instanceof Error\n ? responseError.message\n : String(responseError),\n },\n \"Could not send channel restriction response\",\n );\n }\n }\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId: interaction.channelId,\n allowedChannelIds: this.allowedChannelIds,\n isSlashCommand,\n isModalSubmit,\n isComponent,\n bypassChannelRestriction,\n },\n \"[DiscordService] interactionCreate ignored (channel not allowed)\",\n );\n return;\n }\n\n // Run custom validator if provided for slash commands\n // This is the final permission check layer, after Discord's native checks\n // and our channel whitelist checks have already passed.\n //\n // Why validators?\n // - ElizaOS-specific permission systems (when implemented)\n // - Complex business logic (rate limiting, feature flags, etc.)\n // - Dynamic permissions based on runtime state\n // - Anything that can't be expressed via Discord's native permissions\n if (isSlashCommand && interaction.commandName) {\n const command = this.slashCommands.find(\n (cmd) => cmd.name === interaction.commandName,\n );\n if (command?.validator) {\n try {\n const isValid = await command.validator(interaction, this.runtime);\n if (!isValid) {\n // Send default response if validator didn't respond\n // This prevents Discord from showing \"Interaction failed\" after 3 seconds\n // or leaving a \"thinking\" indicator if the validator called deferReply()\n if (!interaction.replied) {\n try {\n const errorMessage =\n \"You do not have permission to use this command.\";\n if (interaction.deferred) {\n // Validator called deferReply() - use editReply() to resolve the deferred state\n await interaction.editReply({ content: errorMessage });\n } else {\n await interaction.reply({\n content: errorMessage,\n ephemeral: true,\n });\n }\n } catch (responseError) {\n // Validator may have already responded or interaction expired\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n commandName: interaction.commandName,\n error:\n responseError instanceof Error\n ? responseError.message\n : String(responseError),\n },\n \"Could not send validator rejection response (may have already responded)\",\n );\n }\n }\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n commandName: interaction.commandName,\n },\n \"[DiscordService] interactionCreate ignored (custom validator returned false)\",\n );\n return;\n }\n } catch (error) {\n // Send error response if validator threw and didn't respond\n // or left a \"thinking\" indicator via deferReply()\n if (!interaction.replied) {\n try {\n const errorMessage =\n \"An error occurred while validating this command.\";\n if (interaction.deferred) {\n // Validator called deferReply() - use editReply() to resolve the deferred state\n await interaction.editReply({ content: errorMessage });\n } else {\n await interaction.reply({\n content: errorMessage,\n ephemeral: true,\n });\n }\n } catch (responseError) {\n // Validator may have already responded or interaction expired\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n commandName: interaction.commandName,\n error:\n responseError instanceof Error\n ? responseError.message\n : String(responseError),\n },\n \"Could not send validator error response (may have already responded)\",\n );\n }\n }\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n commandName: interaction.commandName,\n error: error instanceof Error ? error.message : String(error),\n },\n \"[DiscordService] Custom validator threw error\",\n );\n return;\n }\n }\n }\n\n try {\n await this.handleInteractionCreate(interaction);\n } catch (error) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error handling interaction\",\n );\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 // Permission Audit Events (controlled by DISCORD_AUDIT_LOG_ENABLED setting)\n // =========================================================================\n const auditLogSetting = this.runtime.getSetting(\n \"DISCORD_AUDIT_LOG_ENABLED\",\n );\n const isAuditLogEnabled =\n auditLogSetting !== \"false\" && auditLogSetting !== false;\n\n if (isAuditLogEnabled) {\n // Channel permission overwrites changed\n this.client.on(\"channelUpdate\", async (oldChannel, newChannel) => {\n try {\n // Handle partials\n let channel = newChannel;\n if (channel.partial) {\n channel = await channel.fetch();\n }\n\n // Only process guild channels with permission overwrites\n if (\n !(\"permissionOverwrites\" in oldChannel) ||\n !(\"guild\" in channel)\n ) {\n return;\n }\n\n const guildChannel = channel as GuildChannel;\n const oldGuildChannel = oldChannel as GuildChannel;\n const oldOverwrites = oldGuildChannel.permissionOverwrites.cache;\n const newOverwrites = guildChannel.permissionOverwrites.cache;\n\n // Check all overwrites (old and new) to catch deletions\n const allIds = new Set([\n ...oldOverwrites.keys(),\n ...newOverwrites.keys(),\n ]);\n\n for (const id of allIds) {\n const oldOw = oldOverwrites.get(id);\n const newOw = newOverwrites.get(id);\n const { changes, action } = diffOverwrites(oldOw, newOw);\n\n if (changes.length === 0) {\n continue;\n }\n\n // Determine audit log action type\n const auditAction =\n action === \"DELETE\"\n ? AuditLogEvent.ChannelOverwriteDelete\n : action === \"CREATE\"\n ? AuditLogEvent.ChannelOverwriteCreate\n : AuditLogEvent.ChannelOverwriteUpdate;\n\n const audit = await fetchAuditEntry(\n guildChannel.guild,\n auditAction,\n guildChannel.id,\n this.runtime,\n );\n\n // Skip if bot made this change\n if (audit?.executorId === this.client?.user?.id) {\n continue;\n }\n\n // Determine target info\n const targetType =\n (oldOw?.type ?? newOw?.type) === 0 ? \"role\" : \"user\";\n let targetName: string;\n if (targetType === \"role\") {\n targetName =\n guildChannel.guild.roles.cache.get(id)?.name ?? \"Unknown\";\n } else {\n const user = await this.client?.users.fetch(id).catch(() => null);\n targetName = user?.tag ?? \"Unknown\";\n }\n\n this.runtime.emitEvent(\n [DiscordEventTypes.CHANNEL_PERMISSIONS_CHANGED] as string[],\n {\n runtime: this.runtime,\n guild: {\n id: guildChannel.guild.id,\n name: guildChannel.guild.name,\n },\n channel: { id: guildChannel.id, name: guildChannel.name },\n target: { type: targetType, id, name: targetName },\n action,\n changes,\n audit,\n } as any,\n );\n }\n } catch (err) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: err instanceof Error ? err.message : String(err),\n },\n \"Error in channelUpdate handler\",\n );\n }\n });\n\n // Role permission changes\n this.client.on(\"roleUpdate\", async (oldRole, newRole) => {\n try {\n const changes = diffRolePermissions(oldRole, newRole);\n if (changes.length === 0) {\n return;\n }\n\n const audit = await fetchAuditEntry(\n newRole.guild,\n AuditLogEvent.RoleUpdate,\n newRole.id,\n this.runtime,\n );\n\n // Skip if bot made this change\n if (audit?.executorId === this.client?.user?.id) {\n return;\n }\n\n this.runtime.emitEvent(\n [DiscordEventTypes.ROLE_PERMISSIONS_CHANGED] as string[],\n {\n runtime: this.runtime,\n guild: { id: newRole.guild.id, name: newRole.guild.name },\n role: { id: newRole.id, name: newRole.name },\n changes,\n audit,\n } as any,\n );\n } catch (err) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: err instanceof Error ? err.message : String(err),\n },\n \"Error in roleUpdate handler\",\n );\n }\n });\n\n // Member role changes\n this.client.on(\"guildMemberUpdate\", async (oldMember, newMember) => {\n try {\n // oldMember can be partial, need to fetch if so\n if (!oldMember) {\n return;\n }\n\n // Fetch full member if partial\n let fullOldMember = oldMember;\n if (oldMember.partial) {\n try {\n fullOldMember = await oldMember.fetch();\n } catch {\n return; // Can't compare without full member data\n }\n }\n\n const { added, removed } = diffMemberRoles(\n fullOldMember as GuildMember,\n newMember,\n );\n if (added.length === 0 && removed.length === 0) {\n return;\n }\n\n const audit = await fetchAuditEntry(\n newMember.guild,\n AuditLogEvent.MemberRoleUpdate,\n newMember.id,\n this.runtime,\n );\n\n // Skip if bot made this change\n if (audit?.executorId === this.client?.user?.id) {\n return;\n }\n\n this.runtime.emitEvent(\n [DiscordEventTypes.MEMBER_ROLES_CHANGED] as string[],\n {\n runtime: this.runtime,\n guild: { id: newMember.guild.id, name: newMember.guild.name },\n member: { id: newMember.id, tag: newMember.user.tag },\n added: added.map((r: DiscordRole) => ({\n id: r.id,\n name: r.name,\n permissions: r.permissions.toArray(),\n })),\n removed: removed.map((r: DiscordRole) => ({\n id: r.id,\n name: r.name,\n permissions: r.permissions.toArray(),\n })),\n audit,\n } as any,\n );\n } catch (err) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: err instanceof Error ? err.message : String(err),\n },\n \"Error in guildMemberUpdate handler\",\n );\n }\n });\n\n // Role creation\n this.client.on(\"roleCreate\", async (role) => {\n try {\n const audit = await fetchAuditEntry(\n role.guild,\n AuditLogEvent.RoleCreate,\n role.id,\n this.runtime,\n );\n\n // Skip if bot made this change\n if (audit?.executorId === this.client?.user?.id) {\n return;\n }\n\n this.runtime.emitEvent(\n [DiscordEventTypes.ROLE_CREATED] as string[],\n {\n runtime: this.runtime,\n guild: { id: role.guild.id, name: role.guild.name },\n role: {\n id: role.id,\n name: role.name,\n permissions: role.permissions.toArray(),\n },\n audit,\n } as any,\n );\n } catch (err) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: err instanceof Error ? err.message : String(err),\n },\n \"Error in roleCreate handler\",\n );\n }\n });\n\n // Role deletion\n this.client.on(\"roleDelete\", async (role) => {\n try {\n const audit = await fetchAuditEntry(\n role.guild,\n AuditLogEvent.RoleDelete,\n role.id,\n this.runtime,\n );\n\n // Skip if bot made this change\n if (audit?.executorId === this.client?.user?.id) {\n return;\n }\n\n this.runtime.emitEvent(\n [DiscordEventTypes.ROLE_DELETED] as string[],\n {\n runtime: this.runtime,\n guild: { id: role.guild.id, name: role.guild.name },\n role: {\n id: role.id,\n name: role.name,\n permissions: role.permissions.toArray(),\n },\n audit,\n } as any,\n );\n } catch (err) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: err instanceof Error ? err.message : String(err),\n },\n \"Error in roleDelete handler\",\n );\n }\n });\n } // end if (isAuditLogEnabled)\n }\n\n /**\n * Handles the event when a new member joins a guild.\n *\n * **Event Design Note:**\n * We intentionally do NOT emit the standardized `EventType.ENTITY_JOINED` here.\n * In ElizaOS's abstraction model:\n * - A Discord \"guild\" maps to a \"world\" (the server/community)\n * - A Discord \"channel\" maps to a \"room\" (a specific conversation space)\n *\n * `EventType.ENTITY_JOINED` requires a `roomId` because the bootstrap plugin's\n * handler calls `syncSingleUser()` to sync the entity to a specific room. When\n * a member joins a guild, they've joined the \"world\" but haven't joined any\n * specific \"room\" yet - they're just a potential participant.\n *\n * The entity will be properly synced to rooms when they first interact:\n * - First message in a channel → message handler calls `ensureConnection()`\n * - Joining a voice channel → voice handler syncs them to that room\n *\n * We still emit the Discord-specific `DiscordEventTypes.ENTITY_JOINED` so that\n * Discord-aware plugins can react to guild member joins (e.g., welcome messages,\n * role assignment, moderation checks).\n *\n * @param {GuildMember} member - The GuildMember object representing the new member.\n * @returns {Promise<void>} - A Promise that resolves once the event handling is complete.\n * @private\n */\n private async handleGuildMemberAdd(member: GuildMember) {\n this.runtime.logger.info(\n `New member joined: ${member.user.username} (${member.id})`,\n );\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 Discord-specific event for plugins that want to handle guild member joins.\n // This is NOT the standardized EventType.ENTITY_JOINED because:\n // 1. ENTITY_JOINED requires a roomId (which channel did they join?)\n // 2. Guild membership != room membership; users join rooms when they interact\n // 3. The bootstrap handler would fail without roomId anyway\n // Discord-aware plugins can listen to DiscordEventTypes.ENTITY_JOINED instead.\n this.runtime.emitEvent(\n [DiscordEventTypes.ENTITY_JOINED] as string[],\n {\n runtime: this.runtime,\n entityId,\n worldId,\n source: \"discord\",\n metadata: {\n type: member.user.bot ? \"bot\" : \"user\",\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 member, // Include raw Discord.js member for Discord-specific handling\n } as any,\n );\n }\n\n /**\n * Registers slash commands with Discord.\n *\n * This method uses a hybrid permission system that combines:\n * 1. Discord's native permission features (default_member_permissions, contexts)\n * 2. ElizaOS channel whitelist bypass (bypassChannelWhitelist flag)\n * 3. Custom validation functions (validator callback)\n *\n * ## Design Decisions\n *\n * ### Why Hybrid Approach?\n * - Discord's native permissions are powerful but limited to role-based access\n * - ElizaOS needs programmatic control for channel restrictions and custom logic\n * - Combining both gives developers the best of both worlds\n *\n * ### Why Transform Simple Flags?\n * - Developer experience: `guildOnly: true` is clearer than `contexts: [0]`\n * - Abstraction: Shields developers from Discord API changes\n * - Sensible defaults: Zero config should \"just work\"\n *\n * ### Why Three Registration Categories?\n *\n * Commands are categorized based on where they should be available:\n *\n * 1. **Global commands** (no guildOnly, no guildIds):\n * - Registered globally via `application.commands.set()` for DM access\n * - ALSO registered per-guild for instant availability in guilds\n * - Guild version overrides global (no duplicates shown in Discord)\n * - Best of both worlds: instant in guilds + works in DMs\n *\n * 2. **Guild-only commands** (guildOnly: true or contexts: [0]):\n * - Registered per-guild via `application.commands.set(cmds, guildId)`\n * - NOT available in DMs (correct behavior)\n * - Instant availability in guilds\n * - New guilds get commands via guildCreate event\n *\n * 3. **Targeted commands** (has guildIds array):\n * - Registered only to specified guilds via `.create()` or `.edit()`\n * - Useful for testing or server-specific features\n * - Instant updates\n *\n * ### Why Register Global Commands Both Globally AND Per-Guild?\n * - Global registration alone takes up to 1 hour to propagate (Discord limitation)\n * - Per-guild registration gives instant availability\n * - Guild commands override global ones in that guild (no duplicates)\n * - Global registration still needed for DM access (no guild context in DMs)\n *\n * ### Why Not Register Everything Per-Guild Only?\n * - Commands that work in DMs MUST be registered globally\n * - There's no guild context in DMs, so per-guild commands don't appear there\n *\n * @param commands - Array of slash commands to register\n * @returns Promise that resolves when registration is complete\n * @private\n */\n private async registerSlashCommands(\n commands: DiscordSlashCommand[],\n ): Promise<void> {\n // Wait for the client to be ready before processing\n await this.clientReadyPromise;\n\n // Helper function to sanitize commands for logging (converts BigInt to string)\n const sanitizeCommandForLogging = (cmd: DiscordSlashCommand): any => {\n const sanitized: any = {\n name: cmd.name,\n description: cmd.description,\n options: cmd.options,\n contexts: cmd.contexts,\n guildOnly: cmd.guildOnly,\n bypassChannelWhitelist: cmd.bypassChannelWhitelist,\n validator: cmd.validator ? \"[Function]\" : undefined,\n };\n\n if (cmd.requiredPermissions !== undefined) {\n sanitized.requiredPermissions =\n typeof cmd.requiredPermissions === \"bigint\"\n ? cmd.requiredPermissions.toString()\n : cmd.requiredPermissions;\n }\n\n if (cmd.guildIds) {\n sanitized.guildIds = cmd.guildIds;\n }\n\n return sanitized;\n };\n\n // Sanitize commands for logging to handle BigInt values\n const sanitizedCommands = commands.map(sanitizeCommandForLogging);\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n commandCount: commands.length,\n commands: sanitizedCommands,\n },\n \"Registering Discord commands\",\n );\n\n if (!this.client?.application) {\n this.runtime.logger.warn(\n { src: \"plugin:discord\", agentId: this.runtime.agentId },\n \"Cannot register commands - Discord client application not available\",\n );\n return;\n }\n\n if (!Array.isArray(commands) || commands.length === 0) {\n this.runtime.logger.warn(\n { src: \"plugin:discord\", agentId: this.runtime.agentId },\n \"Cannot register commands - no commands provided\",\n );\n return;\n }\n\n // Validate all commands\n for (const cmd of commands) {\n if (!cmd.name || !cmd.description) {\n this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n command: sanitizeCommandForLogging(cmd),\n },\n \"Cannot register commands - invalid command (missing name or description)\",\n );\n return;\n }\n }\n\n // Queue this registration to prevent race conditions\n let registrationError: Error | null = null;\n let registrationFailed = false;\n\n this.commandRegistrationQueue = this.commandRegistrationQueue\n .then(async () => {\n // Deduplicate commands by name: merge existing and incoming commands into a map\n // Incoming commands override existing ones with the same name\n const commandMap = new Map<string, DiscordSlashCommand>();\n\n for (const cmd of this.slashCommands) {\n if (cmd.name) {\n commandMap.set(cmd.name, cmd);\n }\n }\n\n for (const cmd of commands) {\n if (cmd.name) {\n commandMap.set(cmd.name, cmd);\n }\n }\n\n this.slashCommands = Array.from(commandMap.values());\n\n // Rebuild allowAllSlashCommands from the final merged commands\n // This ensures the Set always reflects the authoritative command definitions\n // (handles cases where a command is re-registered without the bypass flag)\n this.allowAllSlashCommands.clear();\n for (const cmd of this.slashCommands) {\n if (cmd.bypassChannelWhitelist) {\n this.allowAllSlashCommands.add(cmd.name);\n }\n }\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n bypassCommands: Array.from(this.allowAllSlashCommands),\n },\n \"[DiscordService] Rebuilt bypassChannelWhitelist set from merged commands\",\n );\n\n // Categorize commands for appropriate registration strategy:\n //\n // generalCommands: Commands without specific guildIds (most commands)\n // ├── globalCommands: Can work in DMs → register globally\n // └── guildOnlyCommands: Guild-only → register per-guild for instant availability\n //\n // targetedGuildCommands: Commands with specific guildIds → register only to those guilds\n const generalCommands = this.slashCommands.filter(\n (cmd) => !cmd.guildIds || cmd.guildIds.length === 0,\n );\n const globalCommands = generalCommands.filter(\n (cmd) => !this.isGuildOnlyCommand(cmd),\n );\n const guildOnlyCommands = generalCommands.filter((cmd) =>\n this.isGuildOnlyCommand(cmd),\n );\n const targetedGuildCommands = this.slashCommands.filter(\n (cmd) => cmd.guildIds && cmd.guildIds.length > 0,\n );\n\n const transformedGlobalCommands = globalCommands.map((cmd) =>\n this.transformCommandToDiscordApi(cmd),\n );\n const transformedGuildOnlyCommands = guildOnlyCommands.map((cmd) =>\n this.transformCommandToDiscordApi(cmd),\n );\n // All general commands (global + guild-only) for per-guild registration\n const transformedAllGeneralCommands = [\n ...transformedGlobalCommands,\n ...transformedGuildOnlyCommands,\n ];\n\n if (!this.client?.application) {\n this.runtime.logger.error(\n { src: \"plugin:discord\", agentId: this.runtime.agentId },\n \"Cannot register commands - Discord client application is not available\",\n );\n throw new Error(\"Discord client application is not available\");\n }\n\n let globalCommandsRegistered = false;\n let perGuildSucceeded = 0;\n let perGuildFailed = 0;\n let targetedCommandsRegistered = 0;\n let targetedCommandsFailed = 0;\n\n // 1. Register global commands globally (for DM access)\n // Why? DMs require global registration - there's no guild context.\n // Note: Global commands take up to 1 hour to propagate (Discord limitation),\n // but we also register them per-guild below for instant availability.\n // Always call .set() even with empty array to clear stale global commands\n // (e.g., when all commands become guild-only).\n try {\n await this.client.application.commands.set(transformedGlobalCommands);\n globalCommandsRegistered = true;\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n count: transformedGlobalCommands.length,\n },\n transformedGlobalCommands.length > 0\n ? \"Global commands registered (for DM access)\"\n : \"Global commands cleared (all commands are now guild-only)\",\n );\n } catch (err) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: err instanceof Error ? err.message : String(err),\n },\n \"Failed to register/clear global commands\",\n );\n }\n\n // 2. Register ALL general commands per-guild for instant availability\n // Why both global AND guild-only?\n // - Guild-only commands: Don't work in DMs, so per-guild is the only option\n // - Global commands: Also registered per-guild for INSTANT availability in guilds\n // (guild commands override global ones, so no duplicates are shown)\n // This gives us the best of both worlds:\n // - Instant availability in current guilds\n // - DM access via global registration (step 1)\n // - New guilds get commands via guildCreate event\n // Parallel registration for performance.\n const guilds = this.client.guilds.cache;\n\n if (transformedAllGeneralCommands.length > 0) {\n const guildRegistrations: Promise<{\n guildId: string;\n guildName: string;\n success: boolean;\n }>[] = [];\n\n for (const [guildId, guild] of guilds) {\n guildRegistrations.push(\n this.client.application.commands\n .set(transformedAllGeneralCommands, guildId)\n .then(() => {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n guildId,\n guildName: guild.name,\n },\n \"Commands registered to guild\",\n );\n return { guildId, guildName: guild.name, success: true };\n })\n .catch((err) => {\n this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n guildId,\n guildName: guild.name,\n error: err.message,\n },\n \"Failed to register commands to guild\",\n );\n return { guildId, guildName: guild.name, success: false };\n }),\n );\n }\n\n const perGuildResults = await Promise.all(guildRegistrations);\n perGuildSucceeded = perGuildResults.filter((r) => r.success).length;\n perGuildFailed = perGuildResults.filter((r) => !r.success).length;\n }\n\n // 3. Register targeted guild commands (commands with specific guildIds)\n // Why individual registration? These commands only go to specific guilds,\n // so we use .create() or .edit() to add them individually rather than\n // replacing all commands in those guilds.\n if (targetedGuildCommands.length > 0) {\n const targetedRegistrations: Promise<void>[] = [];\n\n for (const cmd of targetedGuildCommands) {\n const transformedCmd = this.transformCommandToDiscordApi(cmd);\n if (cmd.guildIds) {\n for (const guildId of cmd.guildIds) {\n const guild = guilds.get(guildId);\n if (!guild) {\n this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n commandName: cmd.name,\n guildId,\n },\n \"Cannot register targeted command - bot is not a member of the specified guild\",\n );\n continue;\n }\n targetedRegistrations.push(\n (async () => {\n try {\n const fullGuild = await guild.fetch();\n const existingCommands = await fullGuild.commands.fetch();\n const existingCommand = existingCommands.find(\n (c) => c.name === cmd.name,\n );\n\n if (existingCommand) {\n await existingCommand.edit(transformedCmd);\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n commandName: cmd.name,\n guildId: fullGuild.id,\n guildName: fullGuild.name,\n },\n \"Updated existing targeted command in guild\",\n );\n } else {\n await fullGuild.commands.create(transformedCmd);\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n commandName: cmd.name,\n guildId: fullGuild.id,\n guildName: fullGuild.name,\n },\n \"Registered targeted command in guild\",\n );\n }\n targetedCommandsRegistered++;\n } catch (error) {\n targetedCommandsFailed++;\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n commandName: cmd.name,\n guildId,\n error:\n error instanceof Error\n ? error.message\n : String(error),\n },\n \"Failed to register targeted command in guild\",\n );\n }\n })(),\n );\n }\n }\n }\n\n await Promise.all(targetedRegistrations);\n }\n\n this.runtime.logger.info(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n newCommands: commands.length,\n totalCommands: this.slashCommands.length,\n globalCommands: transformedGlobalCommands.length,\n globalCommandsRegisteredForDMs: globalCommandsRegistered,\n guildOnlyCommands: transformedGuildOnlyCommands.length,\n commandsPerGuild: transformedAllGeneralCommands.length,\n guildsSucceeded: perGuildSucceeded,\n guildsFailed: perGuildFailed,\n targetedCommands: targetedGuildCommands.length,\n targetedCommandsRegistered,\n targetedCommandsFailed,\n },\n \"Commands registered\",\n );\n })\n .catch((error) => {\n registrationFailed = true;\n registrationError =\n error instanceof Error ? error : new Error(String(error));\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: registrationError.message,\n },\n \"Error registering Discord commands\",\n );\n });\n\n await this.commandRegistrationQueue;\n\n if (registrationFailed && registrationError) {\n throw registrationError;\n }\n }\n\n /**\n * Transforms an ElizaOS slash command to Discord API format.\n * This bridges our developer-friendly API with Discord's native requirements.\n * @param {DiscordSlashCommand} cmd - The ElizaOS command definition\n * @returns {object} Discord API compatible command object\n * @private\n */\n private transformCommandToDiscordApi(cmd: DiscordSlashCommand): any {\n const discordCmd: any = {\n name: cmd.name,\n description: cmd.description,\n options: cmd.options,\n };\n\n // Transform contexts and guildOnly to Discord's contexts array\n // Note: contexts overrides guildOnly if provided (as documented)\n // Discord contexts: 0=Guild, 1=BotDM, 2=PrivateChannel\n if (cmd.contexts) {\n // Allow raw contexts for advanced use cases - takes precedence over guildOnly\n discordCmd.contexts = cmd.contexts;\n } else if (cmd.guildOnly) {\n // Transform guildOnly flag to Discord's contexts array\n // Why: `guildOnly: true` is more intuitive than `contexts: [0]`\n discordCmd.contexts = [0]; // 0 = Guild only (no DMs)\n }\n\n // Transform requiredPermissions to Discord's default_member_permissions\n // Why: Leverages Discord's native permission system for role-based access\n // Discord handles the permission checks before the interaction even fires\n if (cmd.requiredPermissions !== undefined) {\n discordCmd.default_member_permissions =\n typeof cmd.requiredPermissions === \"bigint\"\n ? cmd.requiredPermissions.toString()\n : cmd.requiredPermissions;\n }\n\n return discordCmd;\n }\n\n /**\n * Checks if a command is guild-only (shouldn't appear in DMs).\n *\n * A command is considered guild-only if:\n * - `contexts: [0]` is set (Discord's native format, where 0 = Guild only)\n * - `guildOnly: true` is set AND no contexts override is provided\n *\n * Note: `contexts` takes precedence over `guildOnly` to be consistent with\n * `transformCommandToDiscordApi`. This means { guildOnly: true, contexts: [0, 1] }\n * will correctly enable DM access (not be treated as guild-only).\n *\n * @param {DiscordSlashCommand} cmd - The command to check\n * @returns {boolean} True if the command should only be available in guilds\n * @private\n */\n private isGuildOnlyCommand(cmd: DiscordSlashCommand): boolean {\n // If contexts is provided, it overrides guildOnly (consistent with transformCommandToDiscordApi)\n if (cmd.contexts) {\n // Guild-only if contexts only includes 0 (Guild)\n return cmd.contexts.length === 1 && cmd.contexts[0] === 0;\n }\n // Fall back to guildOnly flag\n return !!cmd.guildOnly;\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 this.runtime.logger.info(`Joined guild: ${guild.name} (${guild.id})`);\n const fullGuild = await guild.fetch();\n // Disabled automatic voice joining - now controlled by joinVoiceChannel action\n // this.voiceManager?.scanGuild(guild);\n\n // Register commands to the newly joined guild\n // This ensures commands are available immediately when the bot joins a new server\n if (this.slashCommands.length > 0 && this.client?.application) {\n try {\n // 1. General commands (not targeted to specific guilds) - register all of them\n // Why register global commands per-guild too?\n // - Guild commands override global ones (no duplicates shown)\n // - Instant availability vs waiting for global propagation (up to 1 hour)\n // - Global registration still needed for DM access (already done at startup)\n const generalCommands = this.slashCommands.filter(\n (cmd) => !cmd.guildIds || cmd.guildIds.length === 0,\n );\n\n // 2. Targeted commands that include this guild - these may have been skipped\n // during initial registration if the bot wasn't in this guild yet\n const targetedCommandsForThisGuild = this.slashCommands.filter(\n (cmd) => cmd.guildIds && cmd.guildIds.includes(fullGuild.id),\n );\n\n // Combine and deduplicate (in case a command appears in both somehow)\n const commandMap = new Map<string, (typeof this.slashCommands)[0]>();\n for (const cmd of [\n ...generalCommands,\n ...targetedCommandsForThisGuild,\n ]) {\n if (cmd.name) {\n commandMap.set(cmd.name, cmd);\n }\n }\n const commandsToRegister = Array.from(commandMap.values());\n\n if (commandsToRegister.length > 0) {\n // Transform to Discord API format (preserves guildOnly, requiredPermissions, contexts)\n const discordCommands = commandsToRegister.map((cmd) =>\n this.transformCommandToDiscordApi(cmd),\n );\n\n await this.client.application.commands.set(\n discordCommands,\n fullGuild.id,\n );\n this.runtime.logger.info(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n guildId: fullGuild.id,\n guildName: fullGuild.name,\n generalCount: generalCommands.length,\n targetedCount: targetedCommandsForThisGuild.length,\n totalCount: discordCommands.length,\n },\n \"Commands registered to newly joined guild\",\n );\n }\n } catch (error) {\n this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n guildId: fullGuild.id,\n guildName: fullGuild.name,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Failed to register commands to newly joined guild\",\n );\n }\n }\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 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 both Discord-specific and standardized events with the same data structure\n this.runtime.emitEvent(\n [DiscordEventTypes.WORLD_JOINED] as string[],\n {\n runtime: this.runtime,\n server: fullGuild,\n source: \"discord\",\n } as any,\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 const entityId = createUniqueUuid(this.runtime, interaction.user.id);\n //this.runtime.logger.debug(`User ${interaction.user.id} => entityId ${entityId}`);\n const userName = interaction.user.bot\n ? `${interaction.user.username}#${interaction.user.discriminator}`\n : interaction.user.username;\n const name = interaction.user.displayName;\n const roomId = createUniqueUuid(\n this.runtime,\n interaction.channel?.id || userName,\n );\n\n // can't be null\n let type: ChannelType;\n let serverId: string | undefined;\n\n if (interaction.guild) {\n const guild = await interaction.guild.fetch();\n type = await this.getChannelType(interaction.channel as Channel);\n if (type === null) {\n // usually a forum type post\n this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId: interaction.channel?.id,\n },\n \"Null channel type for interaction\",\n );\n }\n serverId = guild.id;\n } else {\n type = ChannelType.DM;\n // really can't be undefined because bootstrap's choice action\n serverId = interaction.channel?.id;\n }\n\n await this.runtime.ensureConnection({\n entityId,\n roomId,\n userName,\n name,\n source: \"discord\",\n channelId: interaction.channel?.id,\n // Discord snowflake IDs must be converted to UUIDs using stringToUuid()\n // because messageServerId expects a UUID type, not a raw string\n messageServerId: serverId ? stringToUuid(serverId) : undefined,\n type,\n worldId: createUniqueUuid(this.runtime, serverId ?? roomId) as UUID,\n worldName: interaction.guild?.name,\n });\n\n if (interaction.isCommand()) {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n commandName: interaction.commandName,\n type: interaction.commandType,\n channelId: interaction.channelId,\n inGuild: interaction.inGuild(),\n },\n \"[DiscordService] Slash command received\",\n );\n\n try {\n // can't interaction.deferReply if we want to allow custom apps (showModal)\n // Cast to string[] because DiscordEventTypes are custom events not in core's EventPayloadMap\n this.runtime.emitEvent([DiscordEventTypes.SLASH_COMMAND], {\n interaction,\n client: this.client,\n commands: this.slashCommands,\n } as any);\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n commandName: interaction.commandName,\n },\n \"[DiscordService] Slash command emitted to runtime\",\n );\n } catch (error) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n commandName: interaction.commandName,\n error: error instanceof Error ? error.message : String(error),\n },\n \"[DiscordService] Failed to emit slash command\",\n );\n throw error;\n }\n }\n\n if (interaction.isModalSubmit()) {\n // this modal.id is stored in interaction.customId\n this.runtime.emitEvent(\n [DiscordEventTypes.MODAL_SUBMIT] as string[],\n {\n interaction,\n client: this.client,\n } as any,\n );\n }\n\n // Handle message component interactions (buttons, dropdowns, etc.)\n if (interaction.isMessageComponent()) {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n customId: interaction.customId,\n },\n \"Received component interaction\",\n );\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 this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n entityId: userId,\n },\n \"User selections map unexpectedly missing\",\n );\n return; // Should not happen\n }\n\n try {\n // For select menus (type 3), store the values\n if (interaction.isStringSelectMenu()) {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n entityId: userId,\n customId: interaction.customId,\n values: interaction.values,\n },\n \"Values selected\",\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 this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n messageId,\n selections: userSelections[messageId],\n },\n \"Current selections for message\",\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 this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n entityId: userId,\n customId: interaction.customId,\n },\n \"Button pressed\",\n );\n const formSelections = userSelections[messageId] || {};\n\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n formSelections,\n },\n \"Form data being submitted\",\n );\n\n // Set up fallback acknowledgement after 2.5 seconds if handler doesn't respond\n // This prevents \"Interaction failed\" errors while still allowing handlers to show modals\n // Handlers that want to show modals should do so immediately (within 3 seconds)\n const fallbackTimeout = setTimeout(async () => {\n // Remove timeout from array after execution to prevent memory leak\n const index = this.timeouts.indexOf(fallbackTimeout);\n if (index > -1) {\n this.timeouts.splice(index, 1);\n }\n\n // Check if interaction has already been handled\n if (!interaction.replied && !interaction.deferred) {\n try {\n await interaction.deferUpdate();\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n customId: interaction.customId,\n },\n \"Acknowledged button interaction via fallback\",\n );\n } catch (ackError) {\n // Interaction may have already been acknowledged, expired, or handler responded\n // This is expected and not an error\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error:\n ackError instanceof Error\n ? ackError.message\n : String(ackError),\n },\n \"Fallback acknowledgement skipped\",\n );\n }\n }\n }, 2500);\n // Store timeout for cleanup on service stop\n this.timeouts.push(fallbackTimeout);\n\n // Set up a one-time check to clear the timeout early if interaction is acknowledged\n // This prevents unnecessary timeout execution and reduces memory usage\n const earlyCheckTimeout = setTimeout(() => {\n if (interaction.replied || interaction.deferred) {\n clearTimeout(fallbackTimeout);\n // Remove timeout from array since it's been cleared early\n const index = this.timeouts.indexOf(fallbackTimeout);\n if (index > -1) {\n this.timeouts.splice(index, 1);\n }\n }\n // Remove the early check timeout itself from the array\n const earlyIndex = this.timeouts.indexOf(earlyCheckTimeout);\n if (earlyIndex > -1) {\n this.timeouts.splice(earlyIndex, 1);\n }\n }, 2000); // Check once after 2 seconds (before fallback fires)\n // Store early check timeout for cleanup\n this.timeouts.push(earlyCheckTimeout);\n\n // Emit an event with the interaction data and stored selections\n this.runtime.emitEvent(\n [\"DISCORD_INTERACTION\"] as string[],\n {\n interaction: {\n customId: interaction.customId,\n componentType: interaction.componentType,\n type: interaction.type,\n user: userId,\n messageId,\n selections: formSelections, // seems not to be generic\n },\n // we need to be able to do things with the discord client\n // such as interaction.showModal\n discordInteraction: interaction,\n source: \"discord\",\n } as any,\n );\n\n // Clear selections for this form only\n delete userSelections[messageId];\n // No need to call set again\n this.runtime.logger.debug(\n { src: \"plugin:discord\", agentId: this.runtime.agentId, messageId },\n \"Cleared selections for message\",\n );\n\n // Note: The fallback timeout will acknowledge the interaction if the handler doesn't\n // Handlers that need to show modals must do so immediately (within 3 seconds)\n // Handlers that don't need modals can rely on the fallback or acknowledge themselves\n }\n } catch (error) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error handling component interaction\",\n );\n try {\n await interaction.followUp({\n content: \"There was an error processing your interaction.\",\n ephemeral: true,\n });\n } catch (followUpError) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error:\n followUpError instanceof Error\n ? followUpError.message\n : String(followUpError),\n },\n \"Error sending follow-up message\",\n );\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: GuildMember) =>\n channel\n .permissionsFor(member)\n ?.has(PermissionsBitField.Flags.ViewChannel),\n )\n .map((member: GuildMember) =>\n createUniqueUuid(this.runtime, member.id),\n );\n } catch (error) {\n this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId: channel.id,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Failed to get participants for channel\",\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 * Channel topic exposed via metadata for plugin-content-seeder\n *\n * WHY: Discord text channels have an optional \"topic\" field (the description\n * shown at the top of the channel). This is valuable context for content\n * seeding - it tells us what the channel is actually FOR.\n *\n * We expose it in room metadata so plugins don't need Discord-specific code\n * to access it. This maintains separation of concerns.\n */\n metadata: {\n topic:\n \"topic\" in channel ? (channel as TextChannel).topic : undefined,\n },\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 this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n guildId: guild.id,\n memberCount: guild.memberCount.toLocaleString(),\n },\n \"Using optimized user sync for large guild\",\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 this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n guildId: guild.id,\n },\n \"Adding online members\",\n );\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 this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n guildId: guild.id,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error fetching members\",\n );\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 this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n guildId: guild.id,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error fetching members\",\n );\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(readyClient) {\n this.runtime.logger.success(\"Discord client ready\");\n\n // Initialize slash commands array (empty initially - commands registered via DISCORD_REGISTER_COMMANDS)\n this.slashCommands = [];\n\n /**\n * DISCORD_REGISTER_COMMANDS event handler\n *\n * Delegates to registerSlashCommands() method.\n * Also handles deprecated allowAllChannels parameter for backward compatibility.\n *\n * @param params.commands - Array of commands to register\n * @param params.allowAllChannels - (Deprecated) Map of command names to bypass flags\n */\n this.runtime.registerEvent<DiscordRegisterCommandsPayload>(\n \"DISCORD_REGISTER_COMMANDS\",\n async (params) => {\n // Delegate to the public method first - it handles registration and bypassChannelWhitelist\n await this.registerSlashCommands(params.commands);\n\n // Handle deprecated allowAllChannels flags AFTER successful registration (backward compatibility)\n // The deprecated API can only ADD bypasses, not remove them - bypassChannelWhitelist on\n // the command definition is authoritative. This prevents legacy code from accidentally\n // overriding the new API's bypass settings.\n //\n // To survive subsequent registerSlashCommands calls (which rebuild allowAllSlashCommands\n // from this.slashCommands), we also update the command definition itself.\n const allowAllChannelsMap = params.allowAllChannels ?? {};\n for (const [commandName, shouldBypass] of Object.entries(\n allowAllChannelsMap,\n )) {\n if (shouldBypass) {\n this.allowAllSlashCommands.add(commandName);\n // Also update the command definition so bypass survives rebuild\n const cmd = this.slashCommands.find((c) => c.name === commandName);\n if (cmd) {\n cmd.bypassChannelWhitelist = true;\n }\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n commandName,\n },\n \"[DiscordService] Command registered with allowAllChannels bypass (deprecated - use bypassChannelWhitelist instead)\",\n );\n }\n // Note: We intentionally ignore shouldBypass === false here.\n // The deprecated allowAllChannels API should not remove bypasses set by\n // bypassChannelWhitelist on the command definition (which is authoritative).\n }\n },\n );\n\n // Check if audit log tracking is enabled (for permission change events)\n const auditLogSettingForInvite = this.runtime.getSetting(\n \"DISCORD_AUDIT_LOG_ENABLED\",\n );\n const isAuditLogEnabledForInvite =\n auditLogSettingForInvite !== \"false\" &&\n auditLogSettingForInvite !== false;\n\n // Generate invite URL using centralized permission tiers (MODERATOR_VOICE is recommended default)\n // Note: If audit log tracking is enabled (DISCORD_AUDIT_LOG_ENABLED), you may need to manually\n // grant ViewAuditLog permission to the bot role after it joins, as this is an elevated permission\n // that should be granted per-server rather than requested in the OAuth invite.\n const inviteUrl = readyClient.user?.id\n ? generateInviteUrl(readyClient.user.id, \"MODERATOR_VOICE\")\n : undefined;\n\n // Log a note if audit log tracking is enabled\n if (isAuditLogEnabledForInvite) {\n this.runtime.logger.info(\n { src: \"plugin:discord\", agentId: this.runtime.agentId },\n \"Audit log tracking enabled - ensure bot has ViewAuditLog permission in server settings\",\n );\n }\n\n // Use character name if available, otherwise fallback to username, then agentId\n const agentName =\n this.runtime.character.name ||\n readyClient.user?.username ||\n this.runtime.agentId;\n\n if (inviteUrl) {\n this.runtime.logger.info(\n { src: \"plugin:discord\", agentId: this.runtime.agentId, inviteUrl },\n \"Bot invite URL generated\",\n );\n this.runtime.logger.info(\n `Use this URL to add the \"${agentName}\" bot to your Discord server: ${inviteUrl}`,\n );\n } else {\n this.runtime.logger.warn(\n { src: \"plugin:discord\", agentId: this.runtime.agentId },\n \"Could not generate invite URL - bot user ID unavailable\",\n );\n }\n\n this.runtime.logger.success(\n `Discord client logged in successfully as ${readyClient.user?.username || agentName}`,\n );\n\n const guilds = await this.client?.guilds.fetch();\n if (!guilds) {\n this.runtime.logger.warn(\"Could not fetch guilds\");\n return;\n }\n for (const [, guild] of guilds) {\n // Disabled automatic voice joining - now controlled by joinVoiceChannel action\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 this.runtime.logger.info(\n `Discord server connected: ${fullGuild.name} (${fullGuild.id})`,\n );\n\n // Emit Discord-specific event with full guild object\n this.runtime.emitEvent(\n [DiscordEventTypes.WORLD_CONNECTED] as string[],\n {\n runtime: this.runtime,\n server: fullGuild,\n source: \"discord\",\n } as any,\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 this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error during Discord world connection\",\n );\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 // Validate audit log access for permission tracking (if enabled)\n const auditLogEnabled = this.runtime.getSetting(\n \"DISCORD_AUDIT_LOG_ENABLED\",\n );\n if (auditLogEnabled !== \"false\" && auditLogEnabled !== false) {\n try {\n const testGuild = guilds.first();\n if (testGuild) {\n const fullGuild = await testGuild.fetch();\n await fullGuild.fetchAuditLogs({ limit: 1 });\n this.runtime.logger.debug(\n \"Audit log access verified for permission tracking\",\n );\n }\n } catch (err) {\n this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: err instanceof Error ? err.message : String(err),\n },\n \"Cannot access audit logs - permission change alerts will not include executor info\",\n );\n }\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 runtime.logger.info(\"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 this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId,\n useCache,\n },\n \"Fetching members for text channel\",\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 this.runtime.logger.error(\n { src: \"plugin:discord\", agentId: this.runtime.agentId, channelId },\n \"Channel not found\",\n );\n return [];\n }\n\n if (channel.type !== DiscordChannelType.GuildText) {\n this.runtime.logger.error(\n { src: \"plugin:discord\", agentId: this.runtime.agentId, channelId },\n \"Channel is not a text channel\",\n );\n return [];\n }\n\n const guild = channel.guild;\n if (!guild) {\n this.runtime.logger.error(\n { src: \"plugin:discord\", agentId: this.runtime.agentId, channelId },\n \"Channel is not in a guild\",\n );\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 this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n guildId: guild.id,\n memberCount: guild.memberCount.toLocaleString(),\n },\n \"Using cached members for large guild\",\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 this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n cacheSize: guild.members.cache.size,\n },\n \"Using cached members\",\n );\n members = guild.members.cache;\n } else {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n guildId: guild.id,\n },\n \"Fetching members for guild\",\n );\n members = await guild.members.fetch();\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n memberCount: members.size.toLocaleString(),\n },\n \"Fetched members\",\n );\n }\n } catch (error) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error fetching members\",\n );\n // Fallback to cache if fetch fails\n members = guild.members.cache;\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n cacheSize: members.size,\n },\n \"Fallback to cache\",\n );\n }\n }\n\n // Filter members by permission to view the channel\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId: channel.id,\n },\n \"Filtering members for channel access\",\n );\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 this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId: channel.id,\n memberCount: channelMembers.length.toLocaleString(),\n },\n \"Found members with channel access\",\n );\n return channelMembers;\n } catch (error) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error fetching channel members\",\n );\n return [];\n }\n }\n\n /**\n * Fetches the topic/description of a Discord text channel.\n *\n * WHY THIS METHOD EXISTS:\n * =======================\n * Room metadata contains topic from initial sync, but channel topics can change.\n * This method lets plugins fetch FRESH topic data directly from Discord API.\n *\n * Used by plugin-content-seeder to get authoritative topic data for discussion seeding.\n *\n * WHY NOT JUST USE METADATA:\n * Room.metadata.topic is set at sync time and may be stale if the Discord admin\n * updates the channel topic. For plugins that care about freshness, this method\n * provides a way to get current data.\n *\n * TRADEOFF: This makes an API call, so it's slower than reading metadata.\n * Use metadata for most cases, this method when freshness matters.\n *\n * @param {string} channelId - The Discord ID of the text channel.\n * @returns {Promise<string | null>} The channel topic, or null if not available.\n */\n public async getChannelTopic(channelId: string): Promise<string | null> {\n try {\n const channel = await this.client?.channels.fetch(channelId);\n if (channel && \"topic\" in channel) {\n return (channel as TextChannel).topic;\n }\n return null;\n } catch (error) {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Failed to fetch channel topic\",\n );\n return null;\n }\n }\n\n /**\n * Generic handler for reaction events (add/remove).\n * @private\n */\n private async handleReaction(\n reaction: MessageReaction | PartialMessageReaction,\n user: User | PartialUser,\n type: \"add\" | \"remove\",\n ) {\n try {\n const actionVerb = type === \"add\" ? \"added\" : \"removed\";\n const actionText = type === \"add\" ? \"Added\" : \"Removed\";\n const preposition = type === \"add\" ? \"to\" : \"from\";\n\n this.runtime.logger.debug(\n { src: \"plugin:discord\", agentId: this.runtime.agentId, type },\n `Reaction ${actionVerb}`,\n );\n\n // Early returns\n if (!reaction || !user) {\n this.runtime.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 this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Failed to fetch partial reaction\",\n );\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 this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n entityId,\n roomId,\n },\n \"Invalid user ID or room ID\",\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 = `*${actionText} <${emoji}> ${preposition}: \\\\\"${truncatedContent}\\\\\"*`;\n\n // Get user info from the reacting user (not the message author)\n const userName =\n (\"username\" in user && (user as User).username) ||\n reaction.message.author?.username ||\n \"unknown\";\n const name =\n // Prefer any display/global name if present\n ((user as any).globalName as string | undefined) ||\n (reaction.message.author as any)?.displayName ||\n userName;\n\n // Get channel type once and reuse\n const channelType = await this.getChannelType(\n reaction.message.channel as Channel,\n );\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,\n source: \"discord\",\n channelId: reaction.message.channel.id,\n messageServerId: reaction.message.guild?.id\n ? stringToUuid(reaction.message.guild.id)\n : undefined,\n type: channelType,\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 text: reactionMessage,\n source: \"discord\",\n inReplyTo,\n channelType,\n },\n roomId,\n createdAt: timestamp,\n };\n\n const callback: HandlerCallback = async (content): Promise<Memory[]> => {\n if (!reaction.message.channel) {\n this.runtime.logger.error(\n { src: \"plugin:discord\", agentId: this.runtime.agentId },\n \"No channel found for reaction message\",\n );\n return [];\n }\n await (reaction.message.channel as TextChannel).send(\n content.text ?? \"\",\n );\n return [];\n };\n\n // Emit appropriate events based on type (both Discord-specific and core events)\n // Note: New core only has EventType.REACTION_RECEIVED (no REACTION_REMOVED).\n // one core has both. For forward compat, removals only emit Discord-specific event.\n const events =\n type === \"add\"\n ? [DiscordEventTypes.REACTION_RECEIVED, EventType.REACTION_RECEIVED]\n : [DiscordEventTypes.REACTION_REMOVED];\n\n this.runtime.emitEvent(\n events as string[],\n {\n runtime: this.runtime,\n message: memory,\n callback,\n } as any,\n );\n } catch (error) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error handling reaction\",\n );\n }\n }\n\n /**\n * Handles reaction addition.\n * @private\n */\n private async handleReactionAdd(\n reaction: MessageReaction | PartialMessageReaction,\n user: User | PartialUser,\n ) {\n await this.handleReaction(reaction, user, \"add\");\n }\n\n /**\n * Handles reaction removal.\n * @private\n */\n private async handleReactionRemove(\n reaction: MessageReaction | PartialMessageReaction,\n user: User | PartialUser,\n ) {\n await this.handleReaction(reaction, user, \"remove\");\n }\n\n /**\n * Checks if a channel ID is allowed based on both env config and dynamic additions.\n * @param {string} channelId - The channel ID to check\n * @returns {boolean} Whether the channel is allowed\n */\n public isChannelAllowed(channelId: string): boolean {\n // If no restrictions are set, allow all channels\n if (!this.allowedChannelIds) {\n return true;\n }\n\n // Check if channel is in the env-configured list or dynamically added\n return (\n this.allowedChannelIds.includes(channelId) ||\n this.dynamicChannelIds.has(channelId)\n );\n }\n\n /**\n * Adds a channel to the dynamic allowed list.\n * @param {string} channelId - The channel ID to add\n * @returns {boolean} Whether the channel was successfully added\n */\n public addAllowedChannel(channelId: string): boolean {\n // Validate the channel exists\n if (!this.client?.channels.cache.has(channelId)) {\n return false;\n }\n\n this.dynamicChannelIds.add(channelId);\n return true;\n }\n\n /**\n * Removes a channel from the dynamic allowed list.\n * @param {string} channelId - The channel ID to remove\n * @returns {boolean} Whether the channel was in the list and removed\n */\n public removeAllowedChannel(channelId: string): boolean {\n // Don't allow removing channels that are in the env config\n if (this.allowedChannelIds?.includes(channelId)) {\n return false;\n }\n\n return this.dynamicChannelIds.delete(channelId);\n }\n\n /**\n * Gets the list of all allowed channels (env + dynamic).\n * @returns {string[]} Array of allowed channel IDs\n */\n public getAllowedChannels(): string[] {\n const envChannels = this.allowedChannelIds || [];\n const dynamicChannels = Array.from(this.dynamicChannelIds);\n return [...new Set([...envChannels, ...dynamicChannels])];\n }\n\n /**\n * Type guard to check if a channel is a guild text-based channel\n * @private\n */\n private isGuildTextBasedChannel(\n channel: Channel | null,\n ): channel is GuildTextBasedChannel {\n return (\n !!channel &&\n \"isTextBased\" in channel &&\n typeof channel.isTextBased === \"function\" &&\n channel.isTextBased() &&\n \"guild\" in channel &&\n channel.guild !== null\n );\n }\n\n /**\n * Helper to delay execution\n * @private\n */\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Get spider state for a channel from the database\n * @private\n */\n private async getSpiderState(\n channelId: string,\n ): Promise<ChannelSpiderState | null> {\n try {\n // Create a deterministic UUID for this channel's spider state\n const stateId = createUniqueUuid(\n this.runtime,\n `discord-spider-state-${channelId}`,\n );\n\n // Try to get the state memory from the database\n const stateMemory = await this.runtime.getMemoryById(stateId);\n\n if (stateMemory && stateMemory.content.text) {\n const state = JSON.parse(\n stateMemory.content.text,\n ) as ChannelSpiderState;\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId,\n state,\n },\n \"Loaded spider state from database\",\n );\n return state;\n }\n } catch (error) {\n this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n channelId,\n },\n \"Failed to load spider state from database\",\n );\n }\n return null;\n }\n\n /**\n * Save spider state for a channel to the database\n * @private\n */\n private async saveSpiderState(state: ChannelSpiderState): Promise<void> {\n try {\n // Create a deterministic UUID for this channel's spider state\n const stateId = createUniqueUuid(\n this.runtime,\n `discord-spider-state-${state.channelId}`,\n );\n const roomId = createUniqueUuid(this.runtime, state.channelId);\n\n this.runtime.logger.debug(\n `[SpiderState] Saving channel=${state.channelId} stateId=${stateId}`,\n );\n\n // Check if state already exists - if so, delete it first\n let existing: Memory | null = null;\n try {\n existing = await this.runtime.getMemoryById(stateId);\n this.runtime.logger.debug(\n `[SpiderState] getMemoryById: ${existing ? \"EXISTS\" : \"NOT_FOUND\"}`,\n );\n } catch (lookupError: any) {\n this.runtime.logger.debug(\n `[SpiderState] getMemoryById error: ${lookupError?.message || lookupError}`,\n );\n }\n\n if (existing) {\n this.runtime.logger.debug(\n \"[SpiderState] Deleting existing state before insert\",\n );\n try {\n await this.runtime.deleteMemory(stateId);\n this.runtime.logger.debug(\"[SpiderState] Delete successful\");\n } catch (deleteError: any) {\n this.runtime.logger.debug(\n `[SpiderState] Delete error: ${deleteError?.message || deleteError}`,\n );\n }\n }\n\n // Ensure the world, room, entity, and connection exist before saving\n // This is required because the memories table has foreign key constraints\n // on roomId and entityId\n let serverId: string | undefined;\n let worldId: UUID;\n let channelName = state.channelId;\n\n // Try to get channel info from Discord to get serverId\n try {\n if (this.client?.isReady()) {\n const channel = await this.client.channels.fetch(state.channelId);\n if (channel && \"guild\" in channel && channel.guild) {\n serverId = channel.guild.id;\n channelName =\n \"name\" in channel\n ? (channel.name ?? state.channelId)\n : state.channelId;\n }\n }\n } catch {\n // If we can't fetch the channel, use a default serverId\n }\n\n // Create worldId based on serverId or channelId\n worldId = createUniqueUuid(this.runtime, serverId ?? state.channelId);\n\n // Ensure the entity exists (use agent as entity for spider state)\n const entityId = this.runtime.agentId;\n try {\n const entity = await this.runtime.getEntityById(entityId);\n if (!entity) {\n // Create the entity for the agent\n await this.runtime.createEntity({\n id: entityId,\n names: [\"Spider\"],\n agentId: this.runtime.agentId,\n metadata: { source: \"discord-spider\" },\n });\n this.runtime.logger.debug(\"[SpiderState] Created entity for agent\");\n }\n } catch (entityError: any) {\n // Entity might already exist (duplicate key), which is fine\n if (!entityError?.message?.includes(\"duplicate key\")) {\n this.runtime.logger.debug(\n `[SpiderState] Entity ensure error: ${entityError?.message || entityError}`,\n );\n }\n }\n\n // Ensure world exists\n try {\n await this.runtime.ensureWorldExists({\n id: worldId,\n name: serverId\n ? `Discord Server ${serverId}`\n : `Spider World ${state.channelId}`,\n agentId: this.runtime.agentId,\n messageServerId: stringToUuid(serverId ?? state.channelId),\n });\n this.runtime.logger.debug(`[SpiderState] World ensured: ${worldId}`);\n } catch (worldError: any) {\n this.runtime.logger.debug(\n `[SpiderState] World ensure error: ${worldError?.message || worldError}`,\n );\n }\n\n // Ensure room exists\n try {\n await this.runtime.ensureRoomExists({\n id: roomId,\n name: channelName,\n source: \"discord\",\n type: ChannelType.GROUP,\n channelId: state.channelId,\n messageServerId: stringToUuid(serverId ?? state.channelId),\n worldId,\n });\n this.runtime.logger.debug(`[SpiderState] Room ensured: ${roomId}`);\n } catch (roomError: any) {\n this.runtime.logger.debug(\n `[SpiderState] Room ensure error: ${roomError?.message || roomError}`,\n );\n }\n\n // Ensure participant (connection) exists\n try {\n await this.runtime.ensureParticipantInRoom(entityId, roomId);\n this.runtime.logger.debug(\"[SpiderState] Participant ensured in room\");\n } catch (participantError: any) {\n // Try addParticipant as fallback\n try {\n await this.runtime.addParticipant(entityId, roomId);\n this.runtime.logger.debug(\"[SpiderState] Participant added to room\");\n } catch {\n this.runtime.logger.debug(\n `[SpiderState] Participant ensure error: ${participantError?.message || participantError}`,\n );\n }\n }\n\n // Create the state memory\n const stateMemory: Memory = {\n id: stateId,\n agentId: this.runtime.agentId,\n entityId,\n roomId,\n content: {\n text: JSON.stringify(state),\n source: \"discord-spider\",\n },\n metadata: {\n type: MemoryType.CUSTOM,\n source: \"discord-spider-state\",\n channelId: state.channelId,\n fullyBackfilled: state.fullyBackfilled,\n } as any,\n createdAt: Date.now(),\n };\n\n // Store in the database\n this.runtime.logger.debug(\"[SpiderState] Inserting new state\");\n await this.runtime.createMemory(stateMemory, \"custom\");\n\n this.runtime.logger.debug(\n `[SpiderState] Save successful for channel ${state.channelId}`,\n );\n } catch (error: any) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n // Extract the underlying cause from DrizzleQueryError\n const causeMsg = error?.cause?.message || error?.cause || \"\";\n const causeCode = error?.cause?.code || \"\";\n const causeDetail = error?.cause?.detail || \"\";\n\n // Check if this is a duplicate key error\n if (\n errorMsg.includes(\"duplicate key\") ||\n errorMsg.includes(\"unique constraint\") ||\n String(causeMsg).includes(\"duplicate key\") ||\n String(causeMsg).includes(\"unique constraint\")\n ) {\n this.runtime.logger.debug(\n \"[SpiderState] Duplicate key - state already saved by another operation\",\n );\n } else {\n this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: errorMsg,\n cause: String(causeMsg),\n causeCode,\n causeDetail,\n channelId: state.channelId,\n },\n \"Failed to save spider state to database\",\n );\n }\n }\n }\n\n /**\n * Fetches and persists message history from a Discord channel.\n * Supports pagination, state tracking, and streaming via callback.\n *\n * Persistence behavior:\n * - When `onBatch` callback is NOT provided: Messages are automatically persisted\n * to the database and accumulated in the returned `messages` array.\n * - When `onBatch` callback IS provided: Messages are passed to the callback and\n * the caller is responsible for persistence. This allows for custom handling.\n *\n * @param {string} channelId - The Discord channel ID to fetch from\n * @param {ChannelHistoryOptions} options - Options for the fetch operation\n * @returns {Promise<ChannelHistoryResult>} The result with messages and stats\n */\n public async fetchChannelHistory(\n channelId: string,\n options: ChannelHistoryOptions = {},\n ): Promise<ChannelHistoryResult> {\n if (!this.client?.isReady()) {\n this.runtime.logger.warn(\n { src: \"plugin:discord\", agentId: this.runtime.agentId, channelId },\n \"Discord client not ready for history fetch\",\n );\n return {\n messages: [],\n stats: { fetched: 0, stored: 0, pages: 0, fullyBackfilled: false },\n };\n }\n\n // Fetch the channel\n const fetchedChannel = await this.client.channels.fetch(channelId);\n if (!this.isGuildTextBasedChannel(fetchedChannel)) {\n this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId,\n channelType: fetchedChannel?.type ?? null,\n },\n \"Channel is not a guild text-based channel\",\n );\n return {\n messages: [],\n stats: { fetched: 0, stored: 0, pages: 0, fullyBackfilled: false },\n };\n }\n\n const channel = fetchedChannel as GuildTextBasedChannel;\n const serverId =\n \"guild\" in channel && channel.guild\n ? channel.guild.id\n : \"guildId\" in channel && channel.guildId\n ? channel.guildId\n : channel.id;\n const worldId = serverId\n ? createUniqueUuid(this.runtime, serverId)\n : this.runtime.agentId;\n\n // Ensure world and room exist\n await this.runtime.ensureWorldExists({\n id: worldId,\n agentId: this.runtime.agentId,\n messageServerId: stringToUuid(serverId),\n name: (\"guild\" in channel && channel.guild?.name) || \"Discord\",\n });\n\n await this.runtime.ensureRoomExists({\n id: createUniqueUuid(this.runtime, channel.id),\n agentId: this.runtime.agentId,\n name: (\"name\" in channel && channel.name) || channel.id,\n source: \"discord\",\n type: await this.getChannelType(channel as unknown as Channel),\n channelId: channel.id,\n messageServerId: stringToUuid(serverId),\n worldId,\n });\n\n // Load spider state\n const spiderState = options.force\n ? null\n : await this.getSpiderState(channelId);\n const channelName = (\"name\" in channel && channel.name) || channelId;\n\n let consecutiveNoNew = 0;\n let totalStored = 0;\n let totalFetched = 0;\n let pagesProcessed = 0;\n const allMessages: Memory[] = [];\n const startTime = Date.now();\n // Track entity IDs we've already ensured connections for (optimization across batches)\n const ensuredEntityIds = new Set<string>();\n\n // Initialize from spider state if available, otherwise from options\n let oldestMessageId: string | undefined =\n spiderState?.oldestMessageId ?? options.before;\n let newestMessageId: string | undefined =\n spiderState?.newestMessageId ?? options.after;\n let oldestMessageTimestamp: number | undefined =\n spiderState?.oldestMessageTimestamp;\n let newestMessageTimestamp: number | undefined =\n spiderState?.newestMessageTimestamp;\n let reachedEnd = false;\n\n // Phase 1: If we have previous state, first catch up on new messages\n // This ensures we don't miss messages that arrived while spider was stopped\n // We paginate BACKWARD from the present to our known history to avoid\n // Discord's `after` pagination issues where newest messages are returned first\n if (!options.force && spiderState && spiderState.newestMessageId) {\n const lastDate = spiderState.newestMessageTimestamp\n ? new Date(spiderState.newestMessageTimestamp)\n .toISOString()\n .split(\"T\")[0]\n : \"unknown\";\n this.runtime.logger.info(\n `#${channelName}: Catching up on new messages since ${lastDate}`,\n );\n\n // Collect all catch-up batches first (paginating backward from present)\n const catchUpBatches: Message[][] = [];\n let catchUpBefore: string | undefined = undefined; // Start from present (no before = newest messages)\n let catchUpPages = 0;\n let reachedKnownHistory = false;\n\n while (!reachedKnownHistory) {\n catchUpPages++;\n const fetchParams: { limit: number; before?: string } = { limit: 100 };\n if (catchUpBefore) {\n fetchParams.before = catchUpBefore;\n }\n\n const batch = await channel.messages.fetch(fetchParams);\n if (batch.size === 0) {\n break;\n }\n\n const messages = Array.from(\n batch.values() as IterableIterator<Message>,\n ).sort((a, b) => (a.createdTimestamp ?? 0) - (b.createdTimestamp ?? 0));\n\n // Check if we've reached or passed our known newest message\n const knownNewestTimestamp = spiderState.newestMessageTimestamp ?? 0;\n const knownNewestId = spiderState.newestMessageId;\n const filteredMessages: Message[] = [];\n for (const msg of messages) {\n const msgTimestamp = msg.createdTimestamp ?? 0;\n // Include messages NEWER than our known newest, OR same timestamp but different ID\n // This handles the edge case where multiple messages share the same millisecond timestamp\n if (msgTimestamp > knownNewestTimestamp) {\n filteredMessages.push(msg);\n } else if (\n msgTimestamp === knownNewestTimestamp &&\n msg.id !== knownNewestId\n ) {\n // Same timestamp but different message - include it (could be a concurrent message)\n filteredMessages.push(msg);\n } else {\n // We've reached our known history (exact match or older)\n reachedKnownHistory = true;\n }\n }\n\n if (filteredMessages.length > 0) {\n catchUpBatches.push(filteredMessages);\n }\n\n // If batch was full and we haven't reached known history, continue backward\n if (batch.size < 100 || reachedKnownHistory) {\n break;\n }\n\n // Advance backward: get messages before the oldest in current batch\n catchUpBefore = batch.last()?.id;\n await this.delay(250);\n }\n\n // Process catch-up batches in chronological order (oldest first)\n // Reverse because we collected backward (newest batches first)\n catchUpBatches.reverse();\n\n let catchUpBatchIndex = 0;\n for (let messages of catchUpBatches) {\n catchUpBatchIndex++;\n\n // Enforce limit by slicing batch if we're close to the limit\n if (options.limit) {\n const remaining = options.limit - totalFetched;\n if (remaining <= 0) {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId,\n limit: options.limit,\n },\n \"Reached fetch limit during catch-up\",\n );\n break;\n }\n if (messages.length > remaining) {\n messages = messages.slice(0, remaining);\n }\n }\n\n totalFetched += messages.length;\n pagesProcessed++;\n\n // Update newest tracking\n if (messages.length > 0) {\n const lastMsg = messages[messages.length - 1];\n const lastTimestamp = lastMsg.createdTimestamp ?? 0;\n if (\n !newestMessageTimestamp ||\n lastTimestamp > newestMessageTimestamp\n ) {\n newestMessageId = lastMsg.id;\n newestMessageTimestamp = lastTimestamp;\n }\n }\n\n // Build and process memories, tracking new vs existing\n let catchUpNewCount = 0;\n let catchUpExistingCount = 0;\n const catchUpBatchMemories: Memory[] = [];\n\n // Build all memories first\n const allMemories: Memory[] = [];\n for (const discordMessage of messages) {\n const memory = await this.buildMemoryFromMessage(discordMessage);\n if (memory && memory.id) {\n allMemories.push(memory);\n }\n }\n\n // Batch check which memories already exist (single DB query)\n if (allMemories.length > 0) {\n const memoryIds = allMemories\n .map((m) => m.id)\n .filter((id): id is UUID => id !== undefined);\n const existingMemories = await this.runtime.getMemoriesByIds(\n memoryIds,\n \"messages\",\n );\n const existingIdSet = new Set(existingMemories.map((m) => m.id));\n\n // Filter to only new memories\n for (const memory of allMemories) {\n if (memory.id && existingIdSet.has(memory.id)) {\n catchUpExistingCount++;\n } else {\n catchUpNewCount++;\n catchUpBatchMemories.push(memory);\n }\n }\n }\n\n // Process batch via callback or persist and accumulate\n if (options.onBatch) {\n // Caller handles persistence via callback\n const shouldContinue = await options.onBatch(catchUpBatchMemories, {\n page: pagesProcessed,\n totalFetched,\n totalStored: totalStored + catchUpBatchMemories.length,\n });\n\n // Assume caller persists all memories when using onBatch\n totalStored += catchUpBatchMemories.length;\n\n if (shouldContinue === false) {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId,\n page: pagesProcessed,\n },\n \"Batch handler requested early stop during catch-up\",\n );\n break;\n }\n } else {\n // Ensure entity connections exist before persisting (prevents FK constraint failures)\n await this.ensureConnectionsForMessages(messages, ensuredEntityIds);\n\n // Persist memories to database, only count successfully persisted\n const successfullyPersisted: Memory[] = [];\n for (const memory of catchUpBatchMemories) {\n try {\n await this.runtime.createMemory(memory, \"messages\");\n successfullyPersisted.push(memory);\n } catch (error) {\n this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n memoryId: memory.id,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Failed to persist memory during catch-up\",\n );\n }\n }\n allMessages.push(...successfullyPersisted);\n totalStored += successfullyPersisted.length;\n }\n\n // Determine HIT (all existed) or MISS (had new messages)\n const catchUpHitMiss =\n catchUpExistingCount > 0 && catchUpNewCount === 0\n ? \"HIT\"\n : catchUpNewCount > 0\n ? \"MISS\"\n : \"EMPTY\";\n\n // Save progress\n await this.saveSpiderState({\n channelId,\n oldestMessageId,\n newestMessageId,\n oldestMessageTimestamp,\n newestMessageTimestamp,\n lastSpideredAt: Date.now(),\n fullyBackfilled: spiderState.fullyBackfilled,\n });\n\n // Debug log for each catch-up batch\n const newestDate = newestMessageTimestamp\n ? new Date(newestMessageTimestamp).toISOString().split(\"T\")[0]\n : \"?\";\n const elapsedSec = ((Date.now() - startTime) / 1000).toFixed(1);\n this.runtime.logger.debug(\n `#${channelName}: Catch-up batch ${catchUpBatchIndex}/${catchUpBatches.length} [${catchUpHitMiss}], ${messages.length} msgs fetched (${catchUpNewCount} new, ${catchUpExistingCount} existing), ${totalFetched} total fetched, ${totalStored} total stored, newest date ${newestDate} (${elapsedSec}s)`,\n );\n }\n\n if (catchUpBatches.length > 0) {\n this.runtime.logger.info(\n `#${channelName}: Caught up ${catchUpBatches.length} batches of new messages`,\n );\n }\n }\n\n // Phase 2: Determine backfill direction\n let before: string | undefined = options.before;\n let after: string | undefined = options.after;\n\n if (!options.force && spiderState) {\n if (spiderState.fullyBackfilled) {\n // Already caught up above, we're done with fetching\n reachedEnd = true;\n } else {\n // Continue backfilling from where we left off\n before = spiderState.oldestMessageId;\n const oldestDate = spiderState.oldestMessageTimestamp\n ? new Date(spiderState.oldestMessageTimestamp)\n .toISOString()\n .split(\"T\")[0]\n : \"unknown\";\n this.runtime.logger.info(\n `#${channelName}: Resuming backfill from ${oldestDate}`,\n );\n }\n } else if (!spiderState) {\n this.runtime.logger.info(`#${channelName}: Starting fresh history fetch`);\n }\n\n // Phase 3: Backfill older messages (skip if already fully backfilled)\n while (!reachedEnd) {\n // Check limit before fetching to avoid unnecessary API calls\n if (options.limit && totalFetched >= options.limit) {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId,\n limit: options.limit,\n },\n \"Reached fetch limit before backfill batch\",\n );\n break;\n }\n\n pagesProcessed += 1;\n // Adjust fetch limit based on remaining quota to avoid exceeding options.limit\n const remaining = options.limit ? options.limit - totalFetched : 100;\n const fetchLimit = Math.min(100, remaining);\n const fetchParams: Record<string, any> = { limit: fetchLimit };\n\n if (after) {\n fetchParams.after = after;\n } else if (before) {\n fetchParams.before = before;\n }\n\n const batch = await channel.messages.fetch(fetchParams);\n if (batch.size === 0) {\n reachedEnd = true;\n break;\n }\n\n const messages = Array.from(batch.values()).sort(\n (a, b) => (a.createdTimestamp ?? 0) - (b.createdTimestamp ?? 0),\n );\n totalFetched += messages.length;\n\n // Track oldest and newest messages by comparing timestamps\n if (messages.length > 0) {\n const firstMsg = messages[0];\n const lastMsg = messages[messages.length - 1];\n const firstTimestamp = firstMsg.createdTimestamp ?? 0;\n const lastTimestamp = lastMsg.createdTimestamp ?? 0;\n\n // Update oldest message if this is older than what we have\n if (\n !oldestMessageTimestamp ||\n firstTimestamp < oldestMessageTimestamp\n ) {\n oldestMessageId = firstMsg.id;\n oldestMessageTimestamp = firstTimestamp;\n }\n\n // Update newest message if this is newer than what we have\n if (!newestMessageTimestamp || lastTimestamp > newestMessageTimestamp) {\n newestMessageId = lastMsg.id;\n newestMessageTimestamp = lastTimestamp;\n }\n }\n\n // Build memories for this batch and check if they already exist\n const batchMemories: Memory[] = [];\n let newCount = 0;\n let existingCount = 0;\n\n // Build all memories first\n const allMemories: Memory[] = [];\n for (const discordMessage of messages) {\n const memory = await this.buildMemoryFromMessage(discordMessage);\n if (memory && memory.id) {\n allMemories.push(memory);\n }\n }\n\n // Batch check which memories already exist (single DB query)\n if (allMemories.length > 0) {\n const memoryIds = allMemories\n .map((m) => m.id)\n .filter((id): id is UUID => id !== undefined);\n const existingMemories = await this.runtime.getMemoriesByIds(\n memoryIds,\n \"messages\",\n );\n const existingIdSet = new Set(existingMemories.map((m) => m.id));\n\n // Filter to only new memories\n for (const memory of allMemories) {\n if (memory.id && existingIdSet.has(memory.id)) {\n existingCount++;\n } else {\n newCount++;\n batchMemories.push(memory);\n }\n }\n }\n\n // Determine HIT (all existed) or MISS (had new messages)\n const hitMiss =\n existingCount > 0 && newCount === 0\n ? \"HIT\"\n : newCount > 0\n ? \"MISS\"\n : \"EMPTY\";\n\n // Process batch via callback or persist and accumulate\n if (options.onBatch) {\n // Caller handles persistence via callback\n const shouldContinue = await options.onBatch(batchMemories, {\n page: pagesProcessed,\n totalFetched,\n totalStored: totalStored + batchMemories.length,\n });\n\n // Assume caller persists all memories when using onBatch\n totalStored += batchMemories.length;\n\n if (shouldContinue === false) {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId,\n page: pagesProcessed,\n },\n \"Batch handler requested early stop\",\n );\n break;\n }\n } else {\n // Ensure entity connections exist before persisting (prevents FK constraint failures)\n await this.ensureConnectionsForMessages(messages, ensuredEntityIds);\n\n // Persist memories to database, only count successfully persisted\n const successfullyPersisted: Memory[] = [];\n for (const memory of batchMemories) {\n try {\n await this.runtime.createMemory(memory, \"messages\");\n successfullyPersisted.push(memory);\n } catch (error) {\n this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n memoryId: memory.id,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Failed to persist memory during backfill\",\n );\n }\n }\n allMessages.push(...successfullyPersisted);\n totalStored += successfullyPersisted.length;\n }\n consecutiveNoNew = batchMemories.length === 0 ? consecutiveNoNew + 1 : 0;\n\n // Save state after every page so we can resume if interrupted\n const incrementalState: ChannelSpiderState = {\n channelId,\n oldestMessageId,\n newestMessageId,\n oldestMessageTimestamp,\n newestMessageTimestamp,\n lastSpideredAt: Date.now(),\n fullyBackfilled: false, // Not complete yet, still in progress\n };\n await this.saveSpiderState(incrementalState);\n\n // Debug log for each page\n const oldestDate = oldestMessageTimestamp\n ? new Date(oldestMessageTimestamp).toISOString().split(\"T\")[0]\n : \"?\";\n const newestDate = newestMessageTimestamp\n ? new Date(newestMessageTimestamp).toISOString().split(\"T\")[0]\n : \"?\";\n const elapsedSec = ((Date.now() - startTime) / 1000).toFixed(1);\n this.runtime.logger.debug(\n `#${channelName}: Page ${pagesProcessed} [${hitMiss}], ${messages.length} msgs fetched (${newCount} new, ${existingCount} existing), ${batchMemories.length} stored, ${totalFetched} total fetched, ${totalStored} total stored, dates ${oldestDate} to ${newestDate} (${elapsedSec}s)`,\n );\n\n // Log progress every 10 pages (1000 messages) or on first page at info level\n if (pagesProcessed === 1 || pagesProcessed % 10 === 0) {\n this.runtime.logger.info(\n `#${channelName}: Page ${pagesProcessed}, ${totalFetched} msgs fetched, ${totalStored} stored, dates ${oldestDate} to ${newestDate} (${elapsedSec}s)`,\n );\n }\n\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId,\n batchSize: batch.size,\n storedThisBatch: batchMemories.length,\n totalStored,\n totalFetched,\n page: pagesProcessed,\n },\n \"Processed channel history batch\",\n );\n\n // Check stop conditions\n if (options.limit && totalFetched >= options.limit) {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId,\n limit: options.limit,\n },\n \"Reached fetch limit\",\n );\n break;\n }\n\n // Check if we've reached the actual end of channel history\n if (batch.size < 100) {\n reachedEnd = true;\n break;\n }\n\n // Stop if we've hit 3 consecutive pages of existing messages (optimization)\n // But DON'T mark as fullyBackfilled - we may have more older history to fetch\n if (consecutiveNoNew >= 3) {\n this.runtime.logger.debug(\n { src: \"plugin:discord\", agentId: this.runtime.agentId, channelId },\n \"Stopping backfill: 3 consecutive pages of existing messages (will resume from oldest on next run)\",\n );\n break;\n }\n\n // Update pagination cursor using the sorted messages array (oldest-first)\n // This ensures correct cursor advancement regardless of Discord's response order\n if (after) {\n // Forward pagination: advance to the newest message we've processed\n // Use sorted array: messages[last] = newest after sorting by timestamp\n after = messages[messages.length - 1]?.id;\n } else {\n // Backward pagination: advance to the oldest message we've processed\n // Use sorted array: messages[0] = oldest after sorting by timestamp\n before = messages[0]?.id;\n }\n\n // Rate limiting\n await this.delay(250);\n }\n\n // Update spider state\n const newState: ChannelSpiderState = {\n channelId,\n oldestMessageId,\n newestMessageId,\n oldestMessageTimestamp,\n newestMessageTimestamp,\n lastSpideredAt: Date.now(),\n // Preserve fullyBackfilled if already true, or mark as backfilled if we reached the end going backwards\n fullyBackfilled: spiderState?.fullyBackfilled || (reachedEnd && !after),\n };\n await this.saveSpiderState(newState);\n\n const elapsedSec = ((Date.now() - startTime) / 1000).toFixed(1);\n const dateRange =\n oldestMessageTimestamp && newestMessageTimestamp\n ? `${new Date(oldestMessageTimestamp).toISOString().split(\"T\")[0]} to ${new Date(newestMessageTimestamp).toISOString().split(\"T\")[0]}`\n : \"no messages\";\n const status = newState.fullyBackfilled ? \"✓ complete\" : \"↻ partial\";\n this.runtime.logger.info(\n `#${channelName}: ${status} - ${totalFetched} msgs, ${pagesProcessed} pages, ${dateRange} (${elapsedSec}s)`,\n );\n\n return {\n messages: allMessages,\n stats: {\n fetched: totalFetched,\n stored: totalStored,\n pages: pagesProcessed,\n fullyBackfilled: newState.fullyBackfilled,\n },\n };\n }\n\n /**\n * Builds a Memory object from a Discord Message.\n * This is a reusable helper for converting Discord messages to ElizaOS Memory format.\n *\n * @param {Message} message - The Discord message to convert\n * @param {Object} options - Optional parameters\n * @param {string} options.processedContent - Pre-processed text content (if already processed, to avoid double-processing)\n * @param {Media[]} options.processedAttachments - Pre-processed attachments (if already processed)\n * @param {Object} options.extraContent - Additional content fields to merge into the memory content\n * @param {Object} options.extraMetadata - Additional metadata fields to merge into the memory metadata\n * @returns {Promise<Memory | null>} The Memory object, or null if the message is invalid\n */\n public async buildMemoryFromMessage(\n message: Message,\n options?: {\n processedContent?: string;\n processedAttachments?: Media[];\n extraContent?: Record<string, any>;\n extraMetadata?: Record<string, any>;\n },\n ): Promise<Memory | null> {\n if (!message.author || !message.channel) {\n return null;\n }\n\n const entityId = createUniqueUuid(this.runtime, message.author.id);\n const roomId = createUniqueUuid(this.runtime, message.channel.id);\n const channel = message.channel;\n const channelType = await this.getChannelType(channel as Channel);\n const serverId =\n \"guild\" in channel && channel.guild\n ? channel.guild.id\n : (message.guild?.id ?? message.channel.id);\n const worldId = serverId\n ? createUniqueUuid(this.runtime, serverId)\n : this.runtime.agentId;\n\n // Use pre-processed content if provided, otherwise process now\n let textContent: string;\n let attachments: Media[];\n\n if (\n options?.processedContent !== undefined ||\n options?.processedAttachments !== undefined\n ) {\n textContent = options.processedContent || \" \";\n attachments = options.processedAttachments || [];\n } else {\n const processed = this.messageManager\n ? await this.messageManager.processMessage(message)\n : { processedContent: message.content, attachments: [] };\n\n textContent =\n processed?.processedContent &&\n processed.processedContent.trim().length > 0\n ? processed.processedContent\n : message.content || \" \";\n attachments = processed?.attachments ?? [];\n }\n\n const metadata = {\n type: \"message\" as const,\n entityName:\n (message.member as any)?.displayName ??\n (message.author as any).globalName ??\n message.author.username,\n fromBot: message.author.bot,\n fromId: message.author.id,\n sourceId: entityId,\n // Raw Discord IDs for cross-agent correlation (not transformed by createUniqueUuid)\n discordMessageId: message.id,\n discordChannelId: message.channel.id,\n discordServerId:\n \"guild\" in message.channel && message.channel.guild\n ? message.channel.guild.id\n : message.guild?.id,\n tags: [] as string[],\n ...options?.extraMetadata,\n };\n\n const memory: Memory = {\n id: createUniqueUuid(this.runtime, message.id),\n entityId,\n agentId: this.runtime.agentId,\n roomId,\n content: {\n text: textContent || \" \",\n attachments,\n source: \"discord\",\n channelType,\n url: message.url,\n inReplyTo: message.reference?.messageId\n ? createUniqueUuid(this.runtime, message.reference.messageId)\n : undefined,\n ...options?.extraContent,\n },\n metadata,\n createdAt: message.createdTimestamp ?? Date.now(),\n worldId,\n };\n\n return memory;\n }\n\n /**\n * Ensures entity connections exist for a batch of Discord messages using batch API.\n * This should be called before persisting memories to avoid FK constraint failures.\n *\n * @param {Message[]} messages - The Discord messages to ensure connections for\n * @param {Set<string>} ensuredEntityIds - Optional set of already-ensured entity IDs (for caching across batches)\n * @returns {Promise<void>}\n */\n private async ensureConnectionsForMessages(\n messages: Message[],\n ensuredEntityIds: Set<string> = new Set(),\n ): Promise<void> {\n if (messages.length === 0) {\n return;\n }\n\n // Collect unique authors that haven't been ensured yet\n const uniqueAuthors = new Map<string, Message>();\n for (const message of messages) {\n if (message.author && !ensuredEntityIds.has(message.author.id)) {\n uniqueAuthors.set(message.author.id, message);\n }\n }\n\n if (uniqueAuthors.size === 0) {\n return;\n }\n\n try {\n // Use the first message to determine room and world (all messages are from the same channel)\n const firstMessage = messages[0];\n const channelType = await this.getChannelType(\n firstMessage.channel as Channel,\n );\n const serverId =\n \"guild\" in firstMessage.channel && firstMessage.channel.guild\n ? firstMessage.channel.guild.id\n : (firstMessage.guild?.id ?? firstMessage.channel.id);\n const worldId = serverId\n ? createUniqueUuid(this.runtime, serverId)\n : this.runtime.agentId;\n\n // Build entities array for batch API\n const entities = Array.from(uniqueAuthors.entries()).map(\n ([authorId, message]) => {\n const userName = message.author.username;\n const name =\n (message.member as any)?.displayName ??\n (message.author as any).globalName ??\n userName;\n return {\n id: createUniqueUuid(this.runtime, authorId),\n names: [userName, name].filter(\n (n): n is string => typeof n === \"string\" && n.length > 0,\n ),\n metadata: {\n originalId: authorId,\n username: userName,\n displayName: name,\n },\n agentId: this.runtime.agentId,\n };\n },\n );\n\n // Build rooms array (single room for history fetch)\n const rooms = [\n {\n id: createUniqueUuid(this.runtime, firstMessage.channel.id),\n channelId: firstMessage.channel.id,\n type: channelType,\n source: \"discord\",\n },\n ];\n\n // Build world object\n // For DMs, include channel ID in name for observability when debugging multiple DM worlds\n const world: WorldCompat = {\n id: worldId,\n messageServerId: stringToUuid(serverId),\n name: firstMessage.guild?.name ?? `DM-${firstMessage.channel.id}`,\n agentId: this.runtime.agentId,\n };\n\n // Use batch API for efficient database operations\n await this.runtime.ensureConnections(entities, rooms, \"discord\", world);\n\n // Mark all authors as ensured\n for (const authorId of uniqueAuthors.keys()) {\n ensuredEntityIds.add(authorId);\n }\n } catch (error) {\n // Log but don't fail - the memory creation will fail with a clearer error if needed\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n authorCount: uniqueAuthors.size,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Failed to ensure batch connections for message authors during history fetch\",\n );\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 this.runtime.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 this.runtime.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 this.runtime.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\n case DiscordChannelType.GroupDM:\n return ChannelType.DM; // Group DMs treated as DM\n\n case DiscordChannelType.GuildText:\n case DiscordChannelType.GuildNews: // Announcement channels\n case DiscordChannelType.PublicThread:\n case DiscordChannelType.PrivateThread:\n case DiscordChannelType.AnnouncementThread:\n case DiscordChannelType.GuildForum: // Forum channels\n return ChannelType.GROUP;\n\n case DiscordChannelType.GuildVoice:\n case DiscordChannelType.GuildStageVoice: // Stage channels\n return ChannelType.VOICE_GROUP;\n\n default:\n // Fallback for any unrecognized channel types\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelType: channel.type,\n },\n \"Unknown channel type, defaulting to GROUP\",\n );\n return ChannelType.GROUP;\n }\n }\n}\n\nexport default DiscordService;\n","import type { IAgentRuntime } from '@elizaos/core';\nimport { parseBooleanFromText } from '@elizaos/core';\nimport { z } from 'zod';\nimport type { DiscordSettings } from './types';\n\n/**\n * Helper functions to get environment variables with proper defaults\n */\nfunction getEnvBoolean(name: string, fallback: boolean): boolean {\n const value = process.env?.[name];\n if (!value) {return fallback;}\n return value.toLowerCase() === 'true';\n}\n\nfunction getEnvArray(name: string, fallback: string[]): string[] {\n const value = process.env?.[name];\n if (!value || value.trim() === '') {return fallback;}\n return value.split(',').map(item => item.trim()).filter(item => item.length > 0);\n}\n\n/**\n * Default values that can be overridden by environment variables\n */\nexport const DISCORD_DEFAULTS = {\n SHOULD_IGNORE_BOT_MESSAGES: getEnvBoolean('DISCORD_SHOULD_IGNORE_BOT_MESSAGES', false),\n SHOULD_IGNORE_DIRECT_MESSAGES: getEnvBoolean('DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES', false),\n SHOULD_RESPOND_ONLY_TO_MENTIONS: getEnvBoolean('DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS', false),\n ALLOWED_CHANNEL_IDS: getEnvArray('CHANNEL_IDS', []),\n} as const;\n\nexport const discordEnvSchema = z.object({\n DISCORD_API_TOKEN: z.string().min(1, 'Discord API token is required'),\n /**\n * Comma-separated list of channel IDs to restrict the bot to.\n * If not set, the bot operates in all channels as usual.\n * These channels cannot be removed via the leaveChannel action.\n * Additional channels can be added dynamically via the joinChannel action.\n */\n CHANNEL_IDS: z\n .string()\n .nullish()\n .transform((val) =>\n val\n ? val\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0)\n : undefined\n ),\n DISCORD_SHOULD_IGNORE_BOT_MESSAGES: z\n .string()\n .nullish()\n .transform((val) => (val ? parseBooleanFromText(val) : undefined)),\n DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES: z\n .string()\n .nullish()\n .transform((val) => (val ? parseBooleanFromText(val) : undefined)),\n DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS: z\n .string()\n .nullish()\n .transform((val) => (val ? parseBooleanFromText(val) : undefined)),\n});\n\n/**\n * Represents the type of Discord configuration settings inferred from the discordEnvSchema.\n */\nexport type DiscordConfig = z.infer<typeof discordEnvSchema>;\n\n/**\n * Get Discord settings with proper priority:\n * 1. Runtime settings (environment variables via getSetting)\n * 2. Character settings\n * 3. Default values\n *\n * @param runtime - ElizaOS agent runtime instance\n * @returns Merged Discord settings\n */\nexport function getDiscordSettings(runtime: IAgentRuntime): DiscordSettings {\n const characterSettings = runtime.character.settings?.discord as DiscordSettings || {};\n\n // Helper to resolve setting value with priority: runtime > character > default\n const resolveSetting = <T>(\n envKey: string,\n characterValue: T | undefined,\n defaultValue: T,\n transform?: (value: string) => T\n ): T => {\n const runtimeValue = runtime.getSetting(envKey);\n // Treat null the same as undefined (some runtimes return null for missing settings)\n if (runtimeValue !== undefined && runtimeValue !== null) {\n // Coerce to string before transforming to handle non-string runtime values\n const normalized =\n typeof runtimeValue === 'string' ? runtimeValue : String(runtimeValue);\n return transform ? transform(normalized) : (runtimeValue as T);\n }\n return characterValue ?? defaultValue;\n };\n\n // Resolve allowedChannelIds separately to handle empty array case\n const resolvedAllowedChannelIds = resolveSetting<string[]>(\n 'CHANNEL_IDS',\n characterSettings.allowedChannelIds,\n DISCORD_DEFAULTS.ALLOWED_CHANNEL_IDS,\n (value: string) =>\n value\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0)\n );\n\n return {\n ...characterSettings,\n shouldIgnoreBotMessages: resolveSetting(\n 'DISCORD_SHOULD_IGNORE_BOT_MESSAGES',\n characterSettings.shouldIgnoreBotMessages,\n DISCORD_DEFAULTS.SHOULD_IGNORE_BOT_MESSAGES,\n parseBooleanFromText\n ),\n\n shouldIgnoreDirectMessages: resolveSetting(\n 'DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES',\n characterSettings.shouldIgnoreDirectMessages,\n DISCORD_DEFAULTS.SHOULD_IGNORE_DIRECT_MESSAGES,\n parseBooleanFromText\n ),\n\n shouldRespondOnlyToMentions: resolveSetting(\n 'DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS',\n characterSettings.shouldRespondOnlyToMentions,\n DISCORD_DEFAULTS.SHOULD_RESPOND_ONLY_TO_MENTIONS,\n parseBooleanFromText\n ),\n\n // Collapse empty allow-lists back to undefined to keep default open behavior\n allowedChannelIds:\n resolvedAllowedChannelIds.length > 0 ? resolvedAllowedChannelIds : undefined,\n };\n}\n\n/**\n * Validates the Discord configuration by retrieving the Discord API token from the runtime settings\n * and parsing it with the Discord environment schema.\n *\n * @param {IAgentRuntime} runtime The agent runtime instance.\n * @returns {Promise<DiscordConfig>} A promise that resolves with the validated Discord configuration.\n * @throws {Error} If the Discord configuration validation fails, an error with detailed error messages is thrown.\n */\nexport async function validateDiscordConfig(runtime: IAgentRuntime): Promise<DiscordConfig> {\n try {\n const config = {\n DISCORD_API_TOKEN: runtime.getSetting('DISCORD_API_TOKEN'),\n CHANNEL_IDS: runtime.getSetting('CHANNEL_IDS'),\n DISCORD_SHOULD_IGNORE_BOT_MESSAGES: runtime.getSetting('DISCORD_SHOULD_IGNORE_BOT_MESSAGES'),\n DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES: runtime.getSetting('DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES'),\n DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS: runtime.getSetting('DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS'),\n };\n\n return discordEnvSchema.parse(config);\n } catch (error) {\n if (error instanceof z.ZodError) {\n const errorMessages = error.issues\n .map((err) => `${err.path.join('.')}: ${err.message}`)\n .join('\\n');\n throw new Error(`Discord configuration validation failed:\\n${errorMessages}`);\n }\n throw error;\n }\n}\n","import {\n ChannelType,\n type Content,\n EventType,\n type HandlerCallback,\n type Media,\n type Memory,\n ServiceType,\n stringToUuid,\n createUniqueUuid,\n} from \"@elizaos/core\";\n\n// See service.ts for detailed documentation on Discord ID handling.\n// Key point: Discord snowflake IDs (e.g., \"1253563208833433701\") are NOT valid UUIDs.\n// Use stringToUuid() to convert them, not asUUID() which would throw an error.\nimport type { ICompatRuntime } from \"./compat\";\nimport {\n type Channel,\n type Client,\n ChannelType as DiscordChannelType,\n type Message as DiscordMessage,\n type TextChannel,\n AttachmentBuilder,\n} from \"discord.js\";\nimport { AttachmentManager } from \"./attachments\";\nimport { getDiscordSettings } from \"./environment\";\nimport { DiscordSettings, IDiscordService } from \"./types\";\nimport {\n canSendMessage,\n extractUrls,\n getAttachmentFileName,\n getMessageService,\n getUnifiedMessagingAPI,\n sendMessageInChunks,\n} 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: ICompatRuntime;\n private attachmentManager: AttachmentManager;\n private getChannelType: (channel: Channel) => Promise<ChannelType>;\n private discordSettings: DiscordSettings;\n private discordService: IDiscordService;\n /**\n * Constructor for a new instance of MessageManager.\n * @param {IDiscordService} discordService - The Discord service instance.\n * @param {ICompatRuntime} runtime - The agent runtime instance (with cross-core compat).\n * @throws {Error} If the Discord client is not initialized\n */\n constructor(discordService: IDiscordService, runtime: ICompatRuntime) {\n // Guard against null client - fail fast with a clear error\n if (!discordService.client) {\n const errorMsg =\n \"Discord client not initialized - cannot create MessageManager\";\n runtime.logger.error(\n { src: \"plugin:discord\", agentId: runtime.agentId },\n errorMsg,\n );\n throw new Error(errorMsg);\n }\n\n this.client = discordService.client;\n this.runtime = runtime;\n this.attachmentManager = new AttachmentManager(this.runtime);\n this.getChannelType = discordService.getChannelType;\n this.discordService = discordService;\n // Load Discord settings with proper priority (env vars > character settings > defaults)\n this.discordSettings = getDiscordSettings(this.runtime);\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 // this filtering is already done in setupEventListeners\n /*\n if (\n this.discordSettings.allowedChannelIds?.length &&\n !this.discordSettings.allowedChannelIds.some((id: string) => id === message.channel.id)\n ) {\n return;\n }\n */\n\n if (message.interaction || message.author.id === this.client.user?.id) {\n return;\n }\n\n if (this.discordSettings.shouldIgnoreBotMessages && message.author?.bot) {\n return;\n }\n\n if (\n this.discordSettings.shouldIgnoreDirectMessages &&\n message.channel.type === DiscordChannelType.DM\n ) {\n return;\n }\n\n const isBotMentioned = !!(\n this.client.user?.id && message.mentions.users?.has(this.client.user.id)\n );\n const isReplyToBot =\n !!message.reference?.messageId &&\n message.mentions.repliedUser?.id === this.client.user?.id;\n const isInThread = message.channel.isThread();\n const isDM = message.channel.type === DiscordChannelType.DM;\n\n if (this.discordSettings.shouldRespondOnlyToMentions) {\n const shouldProcess = isDM || isBotMentioned || isReplyToBot;\n\n if (!shouldProcess) {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId: message.channel.id,\n },\n \"Strict mode: ignoring message (no mention or reply)\",\n );\n return;\n }\n\n this.runtime.logger.debug(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId: message.channel.id,\n },\n \"Strict mode: processing message\",\n );\n }\n\n const entityId = createUniqueUuid(this.runtime, message.author.id);\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 // Determine channel type and server ID for ensureConnection\n // Note: messageServerId is a Discord snowflake string, converted to UUID when needed\n let type: ChannelType;\n let messageServerId: 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 this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId: message.channel.id,\n },\n \"Null channel type\",\n );\n }\n messageServerId = guild.id;\n } else {\n type = ChannelType.DM;\n messageServerId = message.channel.id;\n }\n\n await this.runtime.ensureConnection({\n entityId,\n roomId,\n userName,\n name,\n source: \"discord\",\n channelId: message.channel.id,\n // Convert Discord snowflake to UUID (see service.ts header for why stringToUuid not asUUID)\n messageServerId: messageServerId\n ? stringToUuid(messageServerId)\n : undefined,\n type,\n worldId: createUniqueUuid(this.runtime, messageServerId ?? roomId),\n worldName: message.guild?.name,\n });\n try {\n const canSendResult = canSendMessage(message.channel);\n if (!canSendResult.canSend) {\n return this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n channelId: message.channel.id,\n reason: canSendResult.reason,\n },\n \"Cannot send message to channel\",\n );\n }\n\n const { processedContent, attachments } =\n await this.processMessage(message);\n\n // Note: Audio attachments are already processed in processMessage via\n // attachmentManager.processAttachments(message.attachments), so no need\n // to process them again here.\n\n if (!processedContent && !attachments?.length) {\n // Only process messages that are not empty\n return;\n }\n\n const channel = message.channel as TextChannel;\n\n // Store the typing data to be used by the callback\n const typingData = {\n interval: null as ReturnType<typeof setInterval> | null,\n cleared: false,\n started: false,\n };\n\n // Use the service's buildMemoryFromMessage method with pre-processed content\n const newMessage = await this.discordService.buildMemoryFromMessage(\n message,\n {\n processedContent,\n processedAttachments: attachments,\n extraContent: {\n mentionContext: {\n isMention: isBotMentioned,\n isReply: isReplyToBot,\n isThread: isInThread,\n mentionType: isBotMentioned\n ? \"platform_mention\"\n : isReplyToBot\n ? \"reply\"\n : isInThread\n ? \"thread\"\n : \"none\",\n },\n },\n extraMetadata: {\n // Reply attribution for cross-agent filtering\n // WHY: When user replies to another bot's message, we need to know\n // so other agents can ignore it (only the replied-to agent should respond)\n replyToAuthor: message.mentions.repliedUser\n ? {\n id: message.mentions.repliedUser.id,\n username: message.mentions.repliedUser.username,\n isBot: message.mentions.repliedUser.bot,\n }\n : undefined,\n },\n },\n );\n\n if (!newMessage) {\n this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n messageId: message.id,\n },\n \"Failed to build memory from message\",\n );\n return;\n }\n\n const messageId = newMessage.id;\n\n const callback: HandlerCallback = async (content: Content) => {\n try {\n // target is set but not addressed to us handling\n if (\n content.target &&\n typeof content.target === \"string\" &&\n content.target.toLowerCase() !== \"discord\"\n ) {\n return [];\n }\n\n // Start typing indicator only when we're actually going to respond\n if (!typingData.started) {\n typingData.started = true;\n\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 this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: err instanceof Error ? err.message : String(err),\n },\n \"Error sending typing indicator\",\n );\n }\n };\n\n // Start typing immediately\n startTyping();\n\n // Create interval to keep the typing indicator active while processing\n typingData.interval = setInterval(startTyping, 8000); // there is no stop typing, it times out after 10s\n\n // Add a small delay to ensure typing indicator is visible\n // This simulates the bot \"thinking\" before responding\n //await new Promise((resolve) => setTimeout(resolve, 1500));\n }\n\n if (message.id && !content.inReplyTo) {\n content.inReplyTo = createUniqueUuid(this.runtime, message.id);\n }\n\n let messages: any[] = [];\n if (content?.channelType === \"DM\") {\n const u = await this.client.users.fetch(message.author.id);\n if (!u) {\n this.runtime.logger.warn(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n entityId: message.author.id,\n },\n \"User not found for DM\",\n );\n return [];\n }\n\n // Convert Media attachments to Discord AttachmentBuilder format for DMs\n const files: AttachmentBuilder[] = [];\n if (content.attachments && content.attachments.length > 0) {\n for (const media of content.attachments) {\n if (media.url) {\n const fileName = getAttachmentFileName(media);\n files.push(\n new AttachmentBuilder(media.url, { name: fileName }),\n );\n }\n }\n }\n\n const textContent = content.text ?? \"\";\n const hasText = textContent.trim().length > 0;\n if (!hasText && files.length === 0) {\n this.runtime.logger.warn(\n { src: \"plugin:discord\", agentId: this.runtime.agentId },\n \"Skipping DM response: no text or attachments\",\n );\n return [];\n }\n\n const dmMessage = await u.send({\n content: textContent,\n files: files.length > 0 ? files : undefined,\n });\n messages = [dmMessage];\n } else {\n // Convert Media attachments to Discord AttachmentBuilder format\n const files: AttachmentBuilder[] = [];\n if (content.attachments && content.attachments.length > 0) {\n for (const media of content.attachments) {\n if (media.url) {\n const fileName = getAttachmentFileName(media);\n files.push(\n new AttachmentBuilder(media.url, { name: fileName }),\n );\n }\n }\n }\n // Pass runtime to enable smart (LLM-assisted) splitting for complex content\n messages = await sendMessageInChunks(\n channel,\n content.text ?? \"\",\n message.id!,\n files,\n undefined,\n this.runtime,\n );\n }\n\n const memories: Memory[] = [];\n for (const m of messages) {\n const actions = content.actions;\n // Only attach files to the memory for the message that actually carries them\n const hasAttachments = m.attachments?.size > 0;\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 text: m.content || content.text || \" \",\n actions,\n inReplyTo: messageId,\n url: m.url,\n channelType: type,\n // Only include attachments for the message chunk that actually has them\n attachments:\n hasAttachments && content.attachments\n ? content.attachments\n : undefined,\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 when done\n if (typingData.interval && !typingData.cleared) {\n clearInterval(typingData.interval);\n typingData.cleared = true;\n }\n\n return memories;\n } catch (error) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error handling message callback\",\n );\n // Clear typing indicator on error\n if (typingData.interval && !typingData.cleared) {\n clearInterval(typingData.interval);\n typingData.cleared = true;\n }\n return [];\n }\n };\n\n // Use unified messaging API if available, otherwise fall back to direct message service\n // This provides a clearer, more traceable flow for message processing\n const unifiedAPI = getUnifiedMessagingAPI(this.runtime);\n const messageService = getMessageService(this.runtime);\n\n if (unifiedAPI) {\n this.runtime.logger.debug(\n { src: \"plugin:discord\", agentId: this.runtime.agentId },\n \"Using unified messaging API\",\n );\n await unifiedAPI.sendMessage(this.runtime.agentId, newMessage, {\n onResponse: callback,\n });\n } else if (messageService) {\n // Newer core with messageService\n this.runtime.logger.debug(\n { src: \"plugin:discord\", agentId: this.runtime.agentId },\n \"Using messageService API\",\n );\n await messageService.handleMessage(this.runtime, newMessage, callback);\n } else {\n // Older core - use event-based message handling (backwards compatible)\n this.runtime.logger.debug(\n { src: \"plugin:discord\", agentId: this.runtime.agentId },\n \"Using event-based message handling\",\n );\n await this.runtime.emitEvent([EventType.MESSAGE_RECEIVED], {\n runtime: this.runtime,\n message: newMessage,\n callback,\n source: \"discord\",\n });\n }\n\n // Failsafe: clear typing indicator after 30 seconds if it was started and something goes wrong\n setTimeout(() => {\n if (typingData.started && typingData.interval && !typingData.cleared) {\n clearInterval(typingData.interval);\n typingData.cleared = true;\n this.runtime.logger.warn(\n { src: \"plugin:discord\", agentId: this.runtime.agentId },\n \"Typing indicator failsafe timeout triggered\",\n );\n }\n }, 30000);\n } catch (error) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error handling message\",\n );\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 if (message.embeds && message.embeds.length) {\n for (const i in message.embeds) {\n const embed = message.embeds[i];\n // type: rich\n processedContent += `\\nEmbed #${parseInt(i) + 1}:\\n`;\n processedContent += ` Title:${embed.title ?? \"(none)\"}\\n`;\n processedContent += ` Description:${embed.description ?? \"(none)\"}\\n`;\n }\n }\n if (message.reference) {\n let messageId;\n if (message.reference.messageId) {\n messageId = createUniqueUuid(this.runtime, message.reference.messageId);\n } else {\n // optional: try to fetch the referenced message to get a definite id\n try {\n const refMsg = await message.fetchReference(); // throws if missing\n messageId = createUniqueUuid(this.runtime, refMsg.id);\n } catch {\n // no referenced message available — handle gracefully\n }\n }\n if (messageId) {\n // context currently doesn't know message ID\n processedContent += `\\nReferencing MessageID ${messageId} (discord: ${\n message.reference.messageId\n })`;\n // in our channel\n if (message.reference.channelId !== message.channel.id) {\n const roomId = createUniqueUuid(\n this.runtime,\n message.reference.channelId,\n );\n processedContent += ` in channel ${roomId}`;\n }\n // in our guild\n if (\n message.reference.guildId &&\n message.guild &&\n message.reference.guildId !== message.guild.id\n ) {\n processedContent += ` in guild ${message.reference.guildId}`;\n }\n processedContent += \"\\n\";\n }\n }\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,\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 // Extract and clean URLs from the message content\n const urls = extractUrls(processedContent, this.runtime);\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 try {\n const videoInfo = await videoService.processVideo(url, this.runtime);\n\n attachments.push({\n id: `youtube-${Date.now()}`,\n url,\n title: videoInfo.title,\n source: \"YouTube\",\n description: videoInfo.description,\n text: videoInfo.text,\n });\n } catch (error) {\n // Handle video processing errors gracefully - the URL is still preserved in the message\n const errorMsg =\n error instanceof Error ? error.message : String(error);\n this.runtime.logger.warn(\n `Failed to process video ${url}: ${errorMsg}`,\n );\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 this.runtime.logger.warn(\n { src: \"plugin:discord\", agentId: this.runtime.agentId },\n \"Browser service not found\",\n );\n continue;\n }\n\n try {\n this.runtime.logger.debug(\n `Fetching page content for cleaned URL: \"${url}\"`,\n );\n const { title, description: summary } =\n await browserService.getPageContent(url, this.runtime);\n\n attachments.push({\n id: `webpage-${Date.now()}`,\n url,\n title: title || \"Web Page\",\n source: \"Web\",\n description: summary,\n text: summary,\n });\n } catch (error) {\n // Silently handle browser errors (certificate issues, timeouts, dead sites, etc.)\n // The URL is still preserved in the message content, just without scraped metadata\n const errorMsg =\n error instanceof Error ? error.message : String(error);\n const errorString = String(error);\n\n // Check for common expected failures that don't need logging\n const isExpectedFailure =\n errorMsg.includes(\"ERR_CERT\") ||\n errorString.includes(\"ERR_CERT\") ||\n errorMsg.includes(\"Timeout\") ||\n errorString.includes(\"Timeout\") ||\n errorMsg.includes(\"ERR_NAME_NOT_RESOLVED\") ||\n errorString.includes(\"ERR_NAME_NOT_RESOLVED\") ||\n errorMsg.includes(\"ERR_HTTP_RESPONSE_CODE_FAILURE\") ||\n errorString.includes(\"ERR_HTTP_RESPONSE_CODE_FAILURE\");\n\n if (!isExpectedFailure) {\n this.runtime.logger.warn(\n `Failed to fetch page content for ${url}: ${errorMsg}`,\n );\n }\n // Expected failures are silently handled - no logging needed\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 os from 'node:os';\nimport path from 'node:path';\nimport { type IAgentRuntime, type Media, ModelType, ServiceType } from '@elizaos/core';\nimport { type Attachment, Collection } from 'discord.js';\nimport ffmpeg from 'fluent-ffmpeg';\nimport { generateSummary } from './utils';\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 let audioFileName: string;\n let audioMimeType: string;\n\n if (attachment.contentType?.startsWith('audio/')) {\n audioBuffer = Buffer.from(audioVideoArrayBuffer);\n audioFileName = attachment.name || 'audio.mp3';\n audioMimeType = attachment.contentType;\n } else if (attachment.contentType?.startsWith('video/mp4')) {\n audioBuffer = await this.extractAudioFromMP4(audioVideoArrayBuffer);\n audioFileName = 'extracted_audio.mp3';\n audioMimeType = 'audio/mpeg';\n } else {\n throw new Error('Unsupported audio/video format');\n }\n\n // Convert Buffer to File object for transcription API\n const audioBlob = new Blob([new Uint8Array(audioBuffer)], { type: audioMimeType });\n const audioFile = new File([audioBlob], audioFileName, { type: audioMimeType });\n\n const transcription = await this.runtime.useModel(ModelType.TRANSCRIPTION, audioFile);\n\n // Assess transcription length before summarizing\n const transcriptionLength = transcription?.length || 0;\n this.runtime.logger.debug({\n src: 'plugin:discord',\n agentId: this.runtime.agentId,\n attachmentId: attachment.id,\n contentType: attachment.contentType,\n transcriptionLength\n }, 'Assessing transcription length before summarization');\n\n // Only summarize if transcription is meaningful (not empty and long enough)\n let title: string | undefined;\n let description: string | undefined;\n\n if (!transcription || transcriptionLength === 0) {\n this.runtime.logger.debug({\n src: 'plugin:discord',\n agentId: this.runtime.agentId,\n attachmentId: attachment.id\n }, 'Transcription is empty, skipping summarization');\n title = undefined;\n description = 'User-uploaded audio/video attachment (no transcription available)';\n } else if (transcriptionLength < 1000) {\n // Short transcriptions don't benefit from summarization\n this.runtime.logger.debug({\n src: 'plugin:discord',\n agentId: this.runtime.agentId,\n attachmentId: attachment.id,\n transcriptionLength\n }, 'Transcription is short, skipping summarization');\n title = undefined;\n description = transcription;\n } else {\n // Transcription is long enough to benefit from summarization\n this.runtime.logger.debug({\n src: 'plugin:discord',\n agentId: this.runtime.agentId,\n attachmentId: attachment.id,\n transcriptionLength\n }, 'Summarizing transcription');\n const summary = await generateSummary(this.runtime, transcription);\n title = summary.title;\n description = summary.description;\n }\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 this.runtime.logger.error({\n src: 'plugin:discord',\n agentId: this.runtime.agentId,\n attachmentId: attachment.id,\n contentType: attachment.contentType,\n error: error instanceof Error ? error.message : String(error),\n }, 'Error processing audio/video attachment');\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 fluent-ffmpeg to extract the audio stream from the MP4 data\n // and convert it to MP3 format\n const tmpDir = os.tmpdir();\n const timestamp = Date.now();\n const tempMP4File = path.join(tmpDir, `discord_video_${timestamp}.mp4`);\n const tempAudioFile = path.join(tmpDir, `discord_audio_${timestamp}.mp3`);\n\n try {\n // Write the MP4 data to a temporary file\n fs.writeFileSync(tempMP4File, Buffer.from(mp4Data));\n\n // Check if file has audio stream using ffprobe\n await new Promise<void>((resolve, reject) => {\n ffmpeg.ffprobe(tempMP4File, (err, metadata) => {\n if (err) {\n reject(err);\n return;\n }\n\n if (!metadata.streams || !Array.isArray(metadata.streams)) {\n reject(new Error('File metadata does not contain valid streams information'));\n return;\n }\n\n const hasAudio = metadata.streams.some(stream => stream.codec_type === 'audio');\n if (!hasAudio) {\n reject(new Error('File does not contain any audio streams'));\n return;\n }\n resolve();\n });\n });\n\n this.runtime.logger.debug({\n src: 'plugin:discord',\n agentId: this.runtime.agentId,\n tempMP4File,\n tempAudioFile,\n }, 'Extracting audio from MP4');\n\n // Extract the audio stream and convert it to MP3\n await new Promise<void>((resolve, reject) => {\n ffmpeg(tempMP4File)\n .noVideo() // Disable video output\n .audioCodec('libmp3lame') // Set audio codec to MP3\n .toFormat('mp3') // Explicitly set output format\n .on('end', () => {\n resolve();\n })\n .on('error', (err) => {\n reject(err);\n })\n .output(tempAudioFile)\n .run();\n });\n\n // Read the converted audio file and return it as a Buffer\n const audioData = fs.readFileSync(tempAudioFile);\n\n this.runtime.logger.debug({\n src: 'plugin:discord',\n agentId: this.runtime.agentId,\n audioDataSize: audioData.length,\n }, 'Successfully extracted audio from MP4');\n\n return audioData;\n } finally {\n // Clean up the temporary files\n try {\n if (fs.existsSync(tempMP4File)) {\n fs.unlinkSync(tempMP4File);\n }\n if (fs.existsSync(tempAudioFile)) {\n fs.unlinkSync(tempAudioFile);\n }\n } catch (cleanupError) {\n this.runtime.logger.warn({\n src: 'plugin:discord',\n agentId: this.runtime.agentId,\n error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError),\n }, 'Failed to cleanup temp files');\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 this.runtime.logger.debug({ src: 'plugin:discord', agentId: this.runtime.agentId, attachmentId: attachment.id, textLength: text?.length }, 'Summarizing PDF content');\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,\n };\n } catch (error) {\n this.runtime.logger.error({\n src: 'plugin:discord',\n agentId: this.runtime.agentId,\n attachmentId: attachment.id,\n contentType: attachment.contentType,\n error: error instanceof Error ? error.message : String(error),\n }, 'Error processing PDF attachment');\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 this.runtime.logger.debug({ src: 'plugin:discord', agentId: this.runtime.agentId, attachmentId: attachment.id, textLength: text?.length }, 'Summarizing plaintext content');\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,\n };\n } catch (error) {\n this.runtime.logger.error({\n src: 'plugin:discord',\n agentId: this.runtime.agentId,\n attachmentId: attachment.id,\n contentType: attachment.contentType,\n error: error instanceof Error ? error.message : String(error),\n }, 'Error processing plaintext attachment');\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 this.runtime.logger.error({\n src: 'plugin:discord',\n agentId: this.runtime.agentId,\n attachmentId: attachment.id,\n contentType: attachment.contentType,\n error: error instanceof Error ? error.message : String(error),\n }, 'Error processing image attachment');\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 type Media,\n} from '@elizaos/core';\nimport {\n ActionRowBuilder,\n AttachmentBuilder,\n ButtonBuilder,\n ChannelType,\n type Message as DiscordMessage,\n PermissionsBitField,\n StringSelectMenuBuilder,\n type TextChannel,\n ThreadChannel,\n} from 'discord.js';\nimport { type DiscordComponentOptions, type DiscordActionRow } from './types';\n\n/**\n * Type definition for the unified messaging API available on some runtime versions.\n */\nexport interface UnifiedMessagingAPI {\n sendMessage: (agentId: string, message: any, options?: { onResponse?: any }) => Promise<any>;\n}\n\n/**\n * Type definition for the message service available on newer core versions.\n */\nexport interface MessageServiceAPI {\n handleMessage: (runtime: IAgentRuntime, message: any, callback: any) => Promise<any>;\n}\n\n/**\n * Checks if the runtime has the unified messaging API (elizaOS.sendMessage).\n * @param {IAgentRuntime} runtime - The runtime to check\n * @returns {boolean} True if the unified messaging API is available\n */\nexport function hasUnifiedMessagingAPI(runtime: IAgentRuntime): boolean {\n const runtimeAny = runtime as any;\n return !!(runtimeAny.elizaOS && typeof runtimeAny.elizaOS.sendMessage === 'function');\n}\n\n/**\n * Checks if the runtime has the message service API (messageService.handleMessage).\n * @param {IAgentRuntime} runtime - The runtime to check\n * @returns {boolean} True if the message service API is available\n */\nexport function hasMessageService(runtime: IAgentRuntime): boolean {\n const runtimeAny = runtime as any;\n return !!(\n typeof runtimeAny.messageService === 'object' &&\n runtimeAny.messageService &&\n typeof runtimeAny.messageService.handleMessage === 'function'\n );\n}\n\n/**\n * Gets the unified messaging API if available.\n * @param {IAgentRuntime} runtime - The runtime to get the API from\n * @returns {UnifiedMessagingAPI | null} The unified messaging API or null if not available\n */\nexport function getUnifiedMessagingAPI(runtime: IAgentRuntime): UnifiedMessagingAPI | null {\n if (hasUnifiedMessagingAPI(runtime)) {\n return (runtime as any).elizaOS as UnifiedMessagingAPI;\n }\n return null;\n}\n\n/**\n * Gets the message service if available.\n * @param {IAgentRuntime} runtime - The runtime to get the service from\n * @returns {MessageServiceAPI | null} The message service or null if not available\n */\nexport function getMessageService(runtime: IAgentRuntime): MessageServiceAPI | null {\n if (hasMessageService(runtime)) {\n return (runtime as any).messageService as MessageServiceAPI;\n }\n return null;\n}\n\nexport const MAX_MESSAGE_LENGTH = 1900;\n\n/**\n * Cleans a URL by removing common trailing junk from Discord messages:\n * - Markdown escape backslashes (t\\.co -> t.co)\n * - Markdown link leakage (url](url -> url)\n * - Trailing punctuation and markdown (*_/.,;!>)\n * - Trailing full-width/CJK punctuation (()[]、。etc.)\n * Note: Preserves valid non-ASCII path characters for internationalized URLs\n *\n * @param {string} url - The raw URL to clean\n * @returns {string} The cleaned URL\n */\nexport function cleanUrl(url: string): string {\n let clean = url;\n\n // 1. Remove markdown escape backslashes (e.g. \"t\\.co\" -> \"t.co\")\n clean = clean.replace(/\\\\([._\\-~])/g, '$1');\n\n // 2. Handle markdown link leakage (e.g. \"url](url\" or \"](url\")\n // Only truncate if we detect the markdown link pattern \"](url\" which indicates\n // markdown syntax has leaked into the URL. Valid URLs can contain brackets\n // (e.g., query params like \"?param[0]=value\", IPv6 addresses, fragments).\n if (clean.startsWith('](')) {\n // URL starts with markdown link syntax leakage - extract the URL after \"](\"\n clean = clean.substring(2);\n } else {\n const markdownLinkPattern = /\\]\\(/;\n const markdownPatternIdx = clean.search(markdownLinkPattern);\n if (markdownPatternIdx > -1) {\n // Found markdown link pattern - truncate at the ']' character\n // This handles cases like \"text](https://example.com\" where markdown syntax leaked\n clean = clean.substring(0, markdownPatternIdx);\n }\n }\n // Note: Trailing brackets will be handled by the trailing junk removal step below\n\n // 3. Remove trailing junk in a loop - handles layered issues like:\n // - Punctuation/markdown: \"site.com**\" -> \"site.com\"\n // - Full-width punctuation: \"site.com)\" -> \"site.com\"\n // - Mixed: \"site.com/path)**\" -> \"site.com/path\"\n // NOTE: We only remove specific problematic characters, not all non-ASCII,\n // to preserve valid internationalized URLs (e.g., https://ja.wikipedia.org/wiki/日本)\n // NOTE: We don't strip forward slashes as they're valid and semantically meaningful in URLs\n let prev = '';\n while (prev !== clean) {\n prev = clean;\n // Strip trailing ASCII punctuation and markdown (but NOT forward slashes)\n clean = clean.replace(/[)\\]>.,;!*_]+$/, '');\n // Strip only specific trailing full-width/CJK punctuation characters\n // that are commonly appended as junk (NOT all non-ASCII characters)\n // Includes: full-width parens (), brackets []【】, punctuation 、。!?etc.\n clean = clean.replace(/[()[]【】{}《》〈〉「」『』、。,.;:!?~~]+$/, '');\n }\n\n return clean;\n}\n\n/**\n * Extracts and cleans URLs from text content.\n * Handles Discord-specific URL formatting issues.\n *\n * @param {string} text - The text to extract URLs from\n * @param {IAgentRuntime} [runtime] - Optional runtime for debug logging\n * @returns {string[]} Array of cleaned, valid URLs\n */\nexport function extractUrls(text: string, runtime?: IAgentRuntime): string[] {\n const urlRegex = /(https?:\\/\\/[^\\s]+)/g;\n const rawUrls = text.match(urlRegex) || [];\n\n return rawUrls\n .map(url => {\n const original = url;\n const clean = cleanUrl(url);\n\n // Debug log if URL was cleaned\n if (runtime && original !== clean) {\n runtime.logger.debug(`URL cleaned: \"${original}\" -> \"${clean}\"`);\n }\n\n return clean;\n })\n .filter(url => {\n // Basic validation to ensure it's still a valid URL after cleanup\n try {\n new URL(url);\n return true;\n } catch {\n if (runtime) {\n runtime.logger.debug(`Invalid URL after cleanup, skipping: \"${url}\"`);\n }\n return false;\n }\n });\n}\n\n/**\n * Generates a filename with proper extension from Media object.\n * Extracts extension from URL if available, otherwise infers from contentType.\n *\n * @param {Media} media - The media object to generate filename for.\n * @returns {string} A filename with appropriate extension.\n */\nexport function getAttachmentFileName(media: Media): string {\n // Try to extract extension from URL first\n let extension = '';\n try {\n const urlPath = new URL(media.url).pathname;\n const urlExtension = urlPath.substring(urlPath.lastIndexOf('.'));\n if (urlExtension && urlExtension.length > 1 && urlExtension.length <= 5) {\n extension = urlExtension;\n }\n } catch {\n // If URL parsing fails, try simple string extraction\n const lastDot = media.url.lastIndexOf('.');\n const queryStart = media.url.indexOf('?', lastDot);\n if (lastDot > 0 && (queryStart === -1 || queryStart > lastDot + 1)) {\n const potentialExt = media.url.substring(lastDot, queryStart > -1 ? queryStart : undefined);\n if (potentialExt.length > 1 && potentialExt.length <= 5) {\n extension = potentialExt;\n }\n }\n }\n\n // If no extension from URL, infer from contentType\n if (!extension && media.contentType) {\n const contentTypeMap: Record<string, string> = {\n image: '.png',\n video: '.mp4',\n audio: '.mp3',\n document: '.txt',\n link: '.html',\n };\n extension = contentTypeMap[media.contentType] || '';\n }\n\n // Default to .txt if still no extension (for text/document files)\n if (!extension) {\n extension = '.txt';\n }\n\n // Get base name from title or id\n const baseName = media.title || media.id || 'attachment';\n\n // Check if base name already has an extension\n const hasExtension = /\\.\\w{1,5}$/i.test(baseName);\n\n // Return filename with extension\n return hasExtension ? baseName : `${baseName}${extension}`;\n}\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 if (!text) {\n return {\n title: '',\n description: '',\n };\n }\n\n // Optimization: If text is short enough, do not invoke LLM for summary\n // 1000 characters is roughly 200-250 words, which is already concise enough\n if (text.length < 1000) {\n return {\n title: '', // Caller will provide default title\n description: text,\n };\n }\n\n runtime.logger.info(`[Summarization] Calling TEXT_SMALL for ${text.length} chars: \"${text.substring(0, 50).replace(/\\n/g, ' ')}...\"`);\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 * 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 (AttachmentBuilder or plain objects).\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<AttachmentBuilder | { attachment: Buffer | string; name: string }>,\n components?: DiscordActionRow[],\n runtime?: IAgentRuntime\n): Promise<DiscordMessage[]> {\n const sentMessages: DiscordMessage[] = [];\n\n // Use smart splitting if runtime available and content is complex\n let messages: string[];\n if (runtime && content.length > MAX_MESSAGE_LENGTH && needsSmartSplit(content)) {\n messages = await smartSplitMessage(runtime, content);\n } else {\n messages = splitMessage(content);\n }\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 // Reply to the specified message for the first chunk\n if (i === 0 && inReplyTo) {\n // Enable reply threading for first message chunk if inReplyTo is provided\n options.reply = {\n messageReference: inReplyTo,\n };\n }\n\n // Attach files to the last message chunk\n if (i === messages.length - 1 && files && files.length > 0) {\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 'toJSON' in components[0] &&\n typeof (components[0] as any).toJSON === 'function'\n ) {\n // If it looks like discord.js components, pass them directly\n options.components = components as any;\n } else {\n // Otherwise, build components from the assumed DiscordActionRow[] structure\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 try {\n const m = await channel.send(options);\n sentMessages.push(m);\n } catch (error: any) {\n // Handle unknown message reference error\n if (error?.code === 50035 && error?.message?.includes('Unknown message')) {\n logger.warn(\n 'Message reference no longer valid (message may have been deleted). Sending without reply threading.'\n );\n // Retry without the reply reference\n const optionsWithoutReply = { ...options };\n delete optionsWithoutReply.reply;\n try {\n const m = await channel.send(optionsWithoutReply);\n sentMessages.push(m);\n } catch (retryError) {\n logger.error(`Error sending message after removing reply reference: ${retryError}`);\n throw retryError;\n }\n } else {\n // Re-throw other errors\n throw error;\n }\n }\n }\n }\n } catch (error) {\n logger.error(`Error sending message: ${error}`);\n }\n\n return sentMessages;\n}\n\n/**\n * Detects if content needs smart (LLM-based) splitting or can use simple line-based splitting.\n * Smart splitting is useful for:\n * - Code blocks that shouldn't be split mid-block\n * - Markdown with headers and sections\n * - Numbered lists that should stay together\n *\n * @param {string} content - The content to analyze\n * @returns {boolean} True if smart splitting would be beneficial\n */\nexport function needsSmartSplit(content: string): boolean {\n // Check for code blocks - these shouldn't be split mid-block\n const codeBlockCount = (content.match(/```/g) || []).length;\n if (codeBlockCount >= 2) {return true;}\n\n // Check for markdown headers - content has structure\n if (/^#{1,3}\\s/m.test(content)) {return true;}\n\n // Check for numbered lists (1. 2. 3.) - should stay together when possible\n if (/^\\d+\\.\\s/m.test(content)) {return true;}\n\n // Check for very long lines without natural breakpoints\n const lines = content.split('\\n');\n const hasLongUnbreakableLines = lines.some(line =>\n line.length > 500 && !line.includes('. ') && !line.includes(', ')\n );\n if (hasLongUnbreakableLines) {return true;}\n\n return false;\n}\n\n/**\n * Parses a JSON array from a given text. The function looks for a JSON block wrapped in triple backticks\n * with `json` language identifier, and if not found, it attempts to parse the text directly as JSON.\n * Unlike parseJSONObjectFromText from core, this function specifically expects and returns arrays.\n *\n * @param {string} text - The input text from which to extract and parse the JSON array.\n * @returns {any[] | null} An array parsed from the JSON string if successful; otherwise, null.\n */\nfunction parseJSONArrayFromText(text: string): any[] | null {\n const jsonBlockPattern = /```json\\n([\\s\\S]*?)\\n```/;\n let jsonData = null;\n const jsonBlockMatch = text.match(jsonBlockPattern);\n\n try {\n if (jsonBlockMatch) {\n // Parse the JSON from inside the code block\n jsonData = JSON.parse(jsonBlockMatch[1].trim());\n } else {\n // Try to parse the text directly if it's not in a code block\n jsonData = JSON.parse(text.trim());\n }\n } catch (_e) {\n // If parsing fails, return null\n return null;\n }\n\n // Ensure we have an array\n if (Array.isArray(jsonData)) {\n return jsonData;\n }\n\n // Return null if not a valid array\n return null;\n}\n\n/**\n * Splits content using LLM for semantic breakpoints.\n * Only use when needsSmartSplit() returns true and runtime is available.\n *\n * @param {IAgentRuntime} runtime - The runtime for LLM calls\n * @param {string} content - The content to split\n * @param {number} maxLength - Maximum length per chunk\n * @returns {Promise<string[]>} Array of semantically-split chunks\n */\nexport async function smartSplitMessage(\n runtime: IAgentRuntime,\n content: string,\n maxLength: number = MAX_MESSAGE_LENGTH\n): Promise<string[]> {\n // If content fits, no splitting needed\n if (content.length <= maxLength) {\n return [content];\n }\n\n // Calculate approximate number of chunks needed\n const estimatedChunks = Math.ceil(content.length / (maxLength - 100));\n\n try {\n runtime.logger.debug(`Smart splitting ${content.length} chars into ~${estimatedChunks} chunks`);\n\n const prompt = `Split the following text into ${estimatedChunks} parts for Discord messages (max ${maxLength} chars each).\nKeep related content together (don't split code blocks, keep list items with their headers, etc.).\nReturn ONLY a JSON array of strings, no explanation.\n\nText to split:\n\"\"\"\n${content}\n\"\"\"\n\nReturn format: [\"chunk1\", \"chunk2\", ...]`;\n\n const response = await runtime.useModel(ModelType.TEXT_SMALL, { prompt });\n\n // Try to parse as JSON array\n const parsed = parseJSONArrayFromText(response);\n if (Array.isArray(parsed)) {\n // Filter to only valid, non-empty string chunks within size limit\n const validChunks = parsed.filter((chunk: unknown): chunk is string =>\n typeof chunk === 'string' &&\n chunk.trim().length > 0 &&\n chunk.length <= maxLength\n );\n\n // Only use LLM result if we have non-empty chunks\n // This prevents returning empty arrays from responses like [\"\", \"\"]\n if (validChunks.length > 0) {\n return validChunks;\n }\n\n runtime.logger.debug('Smart split returned empty or invalid chunks, falling back to simple split');\n }\n } catch (error) {\n runtime.logger.debug(`Smart split failed, falling back to simple split: ${error}`);\n }\n\n // Fall back to simple splitting\n return splitMessage(content, maxLength);\n}\n\n/**\n * Splits the content into an array of strings based on the maximum message length.\n * Uses simple line-based splitting. For complex content, use smartSplitMessage().\n *\n * @param {string} content - The content to split into messages\n * @param {number} maxLength - Maximum length per message (default: 1900)\n * @returns {string[]} An array of strings that represent the split messages\n */\nexport function splitMessage(content: string, maxLength: number = MAX_MESSAGE_LENGTH): string[] {\n // If content fits, no splitting needed\n if (!content || content.length <= maxLength) {\n return content ? [content] : [];\n }\n\n const messages: string[] = [];\n let currentMessage = '';\n\n const rawLines = content.split('\\n');\n // split all lines into maxLength chunks so any long lines are split\n const lines = rawLines.flatMap((line) => {\n const chunks: string[] = [];\n while (line.length > maxLength) {\n // Try to split at word boundary\n let splitIdx = maxLength;\n const lastSpace = line.lastIndexOf(' ', maxLength);\n\n if (lastSpace > maxLength * 0.7) {\n // Prefer space in the last 30% (good utilization + word boundary)\n splitIdx = lastSpace;\n } else if (lastSpace > maxLength * 0.3) {\n // Fallback: use space in the middle to avoid mid-word splits\n // Only if it's not too early (at least 30% of capacity used)\n splitIdx = lastSpace;\n }\n // Otherwise: no usable space (< 30% or -1), split at maxLength\n\n chunks.push(line.slice(0, splitIdx));\n line = line.slice(splitIdx).trimStart();\n }\n chunks.push(line);\n return chunks;\n });\n\n for (const line of lines) {\n if (currentMessage.length + line.length + 1 > maxLength) {\n if (currentMessage.trim().length > 0) {\n messages.push(currentMessage.trim());\n }\n currentMessage = '';\n }\n currentMessage += `${line}\\n`;\n }\n\n if (currentMessage.trim().length > 0) {\n messages.push(currentMessage.trim());\n }\n\n // Ensure we always return at least one element if we had content to process\n // This prevents errors when whitespace-only content is split\n if (messages.length === 0 && content.length > 0) {\n messages.push(' ');\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,\n reason:\n missingPermissions.length > 0\n ? `Missing permissions: ${missingPermissions.map((p) => String(p)).join(', ')}`\n : null,\n };\n}\n","/**\n * Discord bot permission tiers for different use cases.\n *\n * Permissions are organized in a 3x2 matrix:\n * - Role axis: Basic / Moderator / Admin\n * - Voice axis: Without voice / With voice\n *\n * Discord permissions are bit flags. We combine them using bitwise OR.\n * @see https://discord.com/developers/docs/topics/permissions\n */\n\n// Individual permission bit values (from Discord.js PermissionsBitField.Flags)\nconst Permissions = {\n // Reactions\n AddReactions: 1n << 6n, // 64\n\n // Voice (low bits)\n PrioritySpeaker: 1n << 8n, // 256\n Stream: 1n << 9n, // 512\n\n // General\n ViewChannel: 1n << 10n, // 1024 - Required to see channels\n\n // Text\n SendMessages: 1n << 11n, // 2048\n SendTTSMessages: 1n << 12n, // 4096\n ManageMessages: 1n << 13n, // 8192 - Pin/unpin, delete others' messages\n EmbedLinks: 1n << 14n, // 16384\n AttachFiles: 1n << 15n, // 32768\n ReadMessageHistory: 1n << 16n, // 65536\n MentionEveryone: 1n << 17n, // 131072\n UseExternalEmojis: 1n << 18n, // 262144\n\n // Voice\n Connect: 1n << 20n, // 1048576\n Speak: 1n << 21n, // 2097152\n MuteMembers: 1n << 22n, // 4194304\n DeafenMembers: 1n << 23n, // 8388608\n MoveMembers: 1n << 24n, // 16777216\n UseVAD: 1n << 25n, // 33554432 - Voice Activity Detection\n\n // Member management\n KickMembers: 1n << 1n, // 2\n BanMembers: 1n << 2n, // 4\n ChangeNickname: 1n << 26n, // 67108864\n ManageNicknames: 1n << 27n, // 134217728\n\n // Server management\n ManageChannels: 1n << 4n, // 16\n ManageRoles: 1n << 28n, // 268435456\n ManageWebhooks: 1n << 29n, // 536870912\n ManageGuildExpressions: 1n << 30n, // 1073741824\n\n // Advanced\n UseApplicationCommands: 1n << 31n, // 2147483648 - Slash commands\n ManageThreads: 1n << 34n, // 17179869184\n CreatePublicThreads: 1n << 35n, // 34359738368\n CreatePrivateThreads: 1n << 36n, // 68719476736\n UseExternalStickers: 1n << 37n, // 137438953472\n SendMessagesInThreads: 1n << 38n, // 274877906944\n UseEmbeddedActivities: 1n << 39n, // 549755813888\n ModerateMembers: 1n << 40n, // 1099511627776 - Timeout members\n SendVoiceMessages: 1n << 46n, // 70368744177664\n SendPolls: 1n << 47n, // 140737488355328\n} as const;\n\n// ============================================================================\n// BASE PERMISSION SETS\n// ============================================================================\n\n/**\n * Basic text permissions - minimal footprint for text interaction\n */\nconst TEXT_BASIC =\n Permissions.ViewChannel |\n Permissions.AddReactions |\n Permissions.SendMessages |\n Permissions.EmbedLinks |\n Permissions.AttachFiles |\n Permissions.UseExternalEmojis |\n Permissions.ReadMessageHistory |\n Permissions.SendMessagesInThreads |\n Permissions.UseApplicationCommands;\n\n/**\n * Moderator text permissions - adds moderation capabilities\n */\nconst TEXT_MODERATOR =\n TEXT_BASIC |\n Permissions.ManageMessages |\n Permissions.MentionEveryone |\n Permissions.CreatePublicThreads |\n Permissions.CreatePrivateThreads |\n Permissions.ManageThreads |\n Permissions.UseExternalStickers |\n Permissions.SendPolls |\n Permissions.ModerateMembers; // Timeout members\n\n/**\n * Admin text permissions - adds member/server management\n */\nconst TEXT_ADMIN =\n TEXT_MODERATOR |\n Permissions.KickMembers |\n Permissions.BanMembers |\n Permissions.ManageNicknames |\n Permissions.ManageChannels |\n Permissions.ManageRoles |\n Permissions.ManageWebhooks |\n Permissions.ManageGuildExpressions;\n\n/**\n * Voice permissions add-on\n */\nconst VOICE_ADDON =\n Permissions.Connect |\n Permissions.Speak |\n Permissions.UseVAD |\n Permissions.PrioritySpeaker |\n Permissions.Stream |\n Permissions.SendVoiceMessages;\n\n/**\n * Voice moderation add-on (for admin tier)\n */\nconst VOICE_ADMIN_ADDON =\n VOICE_ADDON |\n Permissions.MuteMembers |\n Permissions.DeafenMembers |\n Permissions.MoveMembers;\n\n// ============================================================================\n// PERMISSION TIERS (3x2 Matrix)\n// ============================================================================\n\n/**\n * Basic - Text only, no moderation, no voice\n * Good for: Simple chatbots, read-only bots, basic assistants\n */\nexport const PERMISSIONS_BASIC = TEXT_BASIC;\n\n/**\n * Basic + Voice - Text with voice, no moderation\n * Good for: Voice assistants, music bots without mod powers\n */\nexport const PERMISSIONS_BASIC_VOICE = TEXT_BASIC | VOICE_ADDON;\n\n/**\n * Moderator - Text with moderation, no voice\n * Good for: Community bots, moderation assistants, engagement bots\n */\nexport const PERMISSIONS_MODERATOR = TEXT_MODERATOR;\n\n/**\n * Moderator + Voice - Text + moderation + voice\n * Good for: Full-featured community bots with voice\n */\nexport const PERMISSIONS_MODERATOR_VOICE = TEXT_MODERATOR | VOICE_ADDON;\n\n/**\n * Admin - Text with full server management, no voice\n * Good for: Server management bots, admin tools\n */\nexport const PERMISSIONS_ADMIN = TEXT_ADMIN;\n\n/**\n * Admin + Voice - Full permissions (text + admin + voice + voice moderation)\n * Good for: Complete server integration, owner-level bots\n */\nexport const PERMISSIONS_ADMIN_VOICE = TEXT_ADMIN | VOICE_ADMIN_ADDON;\n\n// ============================================================================\n// EXPORTS\n// ============================================================================\n\n/**\n * Permission tiers as numbers for URL generation.\n *\n * Matrix (3x2):\n * | | No Voice | With Voice |\n * |------------|------------------|-----------------------|\n * | Basic | BASIC | BASIC_VOICE |\n * | Moderator | MODERATOR | MODERATOR_VOICE |\n * | Admin | ADMIN | ADMIN_VOICE |\n *\n * Note: BigInt permissions are converted to Number for URL compatibility.\n * This is safe while Discord permissions stay below bit position 53\n * (Number.MAX_SAFE_INTEGER ≈ 9 quadrillion). Current max bit is ~46.\n */\nexport const DiscordPermissionTiers = {\n /** Basic text-only permissions (no moderation, no voice) */\n BASIC: Number(PERMISSIONS_BASIC),\n /** Basic + voice permissions (no moderation) */\n BASIC_VOICE: Number(PERMISSIONS_BASIC_VOICE),\n /** Moderator permissions (text + moderation, no voice) */\n MODERATOR: Number(PERMISSIONS_MODERATOR),\n /** Moderator + voice permissions */\n MODERATOR_VOICE: Number(PERMISSIONS_MODERATOR_VOICE),\n /** Admin permissions (text + moderation + server management, no voice) */\n ADMIN: Number(PERMISSIONS_ADMIN),\n /** Admin + voice permissions (full permissions) */\n ADMIN_VOICE: Number(PERMISSIONS_ADMIN_VOICE),\n\n // Alias for backwards compatibility\n /** @deprecated Use MODERATOR_VOICE instead */\n FULL: Number(PERMISSIONS_MODERATOR_VOICE),\n} as const;\n\n// Type for tier names\nexport type DiscordPermissionTier = keyof typeof DiscordPermissionTiers;\n\n/**\n * Generate a Discord OAuth2 invite URL for the bot.\n */\nexport function generateInviteUrl(\n applicationId: string,\n tier: DiscordPermissionTier = 'MODERATOR_VOICE'\n): string {\n const permissions = DiscordPermissionTiers[tier];\n return `https://discord.com/api/oauth2/authorize?client_id=${applicationId}&permissions=${permissions}&scope=bot%20applications.commands`;\n}\n\n/**\n * Permission values for all tiers (for compact display).\n */\nexport interface DiscordPermissionValues {\n basic: number;\n basicVoice: number;\n moderator: number;\n moderatorVoice: number;\n admin: number;\n adminVoice: number;\n}\n\n/**\n * Get all permission values for the 3x2 tier matrix.\n */\nexport function getPermissionValues(): DiscordPermissionValues {\n return {\n basic: DiscordPermissionTiers.BASIC,\n basicVoice: DiscordPermissionTiers.BASIC_VOICE,\n moderator: DiscordPermissionTiers.MODERATOR,\n moderatorVoice: DiscordPermissionTiers.MODERATOR_VOICE,\n admin: DiscordPermissionTiers.ADMIN,\n adminVoice: DiscordPermissionTiers.ADMIN_VOICE,\n };\n}\n\n/**\n * Invite URLs organized by tier for display.\n */\nexport interface DiscordInviteUrls {\n basic: string;\n basicVoice: string;\n moderator: string;\n moderatorVoice: string;\n admin: string;\n adminVoice: string;\n}\n\n/**\n * Generate all tier invite URLs for display.\n */\nexport function generateAllInviteUrls(applicationId: string): DiscordInviteUrls {\n return {\n basic: generateInviteUrl(applicationId, 'BASIC'),\n basicVoice: generateInviteUrl(applicationId, 'BASIC_VOICE'),\n moderator: generateInviteUrl(applicationId, 'MODERATOR'),\n moderatorVoice: generateInviteUrl(applicationId, 'MODERATOR_VOICE'),\n admin: generateInviteUrl(applicationId, 'ADMIN'),\n adminVoice: generateInviteUrl(applicationId, 'ADMIN_VOICE'),\n };\n}\n\n// For backwards compatibility\nexport const REQUIRED_PERMISSIONS = PERMISSIONS_MODERATOR_VOICE;\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 EventType,\n type HandlerCallback,\n type Memory,\n ModelType,\n stringToUuid,\n type UUID,\n createUniqueUuid,\n logger,\n} from \"@elizaos/core\";\n\n// See service.ts for detailed documentation on Discord ID handling.\n// Key point: Discord snowflake IDs (e.g., \"1253563208833433701\") are NOT valid UUIDs.\n// Use stringToUuid() to convert them, not asUUID() which would throw an error.\nimport type { ICompatRuntime } from \"./compat\";\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 { Readable, pipeline } from \"node:stream\";\nimport prism from \"prism-media\";\nimport type { DiscordService } from \"./service\";\nimport { getMessageService } from \"./utils\";\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 // Note: Using global logger here as this is a standalone function without runtime context\n logger.warn(\n {\n src: \"plugin:discord:service:voice\",\n error: error instanceof Error ? error.message : String(error),\n },\n \"Failed to create opus decoder\",\n );\n\n // Log available opus libraries for debugging\n try {\n const { generateDependencyReport } = require(\"@discordjs/voice\");\n const report = generateDependencyReport();\n logger.debug(\n { src: \"plugin:discord:service:voice\", report },\n \"Voice dependency report\",\n );\n } catch (reportError) {\n logger.warn(\n {\n src: \"plugin:discord:service:voice\",\n error:\n reportError instanceof Error\n ? reportError.message\n : String(reportError),\n },\n \"Could not generate dependency report\",\n );\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.debug(\n { src: \"plugin:discord:service:voice\" },\n \"AudioMonitor ended\",\n );\n this.ended = true;\n if (this.lastFlagged < 0) {\n return;\n }\n callback(this.getBufferFromStart());\n this.lastFlagged = -1;\n });\n this.readable.on(\"speakingStopped\", () => {\n if (this.ended) {\n return;\n }\n logger.debug({ src: \"plugin:discord:service:voice\" }, \"Speaking stopped\");\n if (this.lastFlagged < 0) {\n return;\n }\n callback(this.getBufferFromStart());\n });\n this.readable.on(\"speakingStarted\", () => {\n if (this.ended) {\n return;\n }\n onStart();\n logger.debug({ src: \"plugin:discord:service:voice\" }, \"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: ReturnType<typeof setTimeout> | 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: ICompatRuntime;\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 {ICompatRuntime} runtime - The runtime for the agent (with cross-core compat).\n */\n constructor(service: DiscordService, runtime: ICompatRuntime) {\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 this.runtime.logger.error(\n { src: \"plugin:discord:service:voice\", agentId: this.runtime.agentId },\n \"Discord client not available 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 this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n channelId: channel.id,\n channelType: channel.type,\n },\n \"Unexpected channel type\",\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 this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n ready: this.ready,\n },\n \"VoiceManager ready status changed\",\n );\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) {\n return;\n }\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 this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error leaving voice channel\",\n );\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 this.runtime.logger.info(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n status: connection.state.status,\n },\n \"Voice connection established\",\n );\n\n // Set up ongoing state change monitoring\n connection.on(\"stateChange\", async (oldState, newState) => {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n oldState: oldState.status,\n newState: newState.status,\n },\n \"Voice connection state changed\",\n );\n\n if (newState.status === VoiceConnectionStatus.Disconnected) {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n },\n \"Handling disconnection\",\n );\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 this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n },\n \"Reconnecting to channel\",\n );\n } catch (e) {\n // Seems to be a real disconnect, destroy and cleanup\n this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n error: e instanceof Error ? e.message : String(e),\n },\n \"Disconnection confirmed - cleaning up\",\n );\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 this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Voice connection error\",\n );\n // Don't immediately destroy - let the state change handler deal with it\n this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n },\n \"Will attempt to recover\",\n );\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 this.runtime.logger.warn(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Failed to modify voice state\",\n );\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 this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n entityId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Failed to fetch user\",\n );\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 this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n channelId: channel.id,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Failed to establish voice connection\",\n );\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 this.runtime.logger.error(\n { src: \"plugin:discord:service:voice\", agentId: this.runtime.agentId },\n \"Client user ID not available\",\n );\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 this.runtime.logger.warn(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n entityId,\n },\n \"No receiveStream or empty stream\",\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 this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n entityId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Failed to create opus decoder\",\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 this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n entityId,\n error: err.message,\n },\n \"Opus decoding pipeline error\",\n );\n } else {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n entityId,\n },\n \"Opus decoding pipeline finished\",\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 this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n error: err instanceof Error ? err.message : String(err),\n },\n \"Opus decoding error\",\n );\n });\n const errorHandler = (err: any) => {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n error: err instanceof Error ? err.message : String(err),\n },\n \"Opus decoding error\",\n );\n };\n const streamCloseHandler = () => {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n member: member?.displayName,\n },\n \"Voice stream closed\",\n );\n this.streams.delete(entityId);\n this.connections.delete(entityId);\n };\n const closeHandler = () => {\n this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n member: member?.displayName,\n },\n \"Opus decoder closed\",\n );\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 this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n channelId: channel.id,\n channelName: channel.name,\n },\n \"Left voice channel\",\n );\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 this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n memberId,\n },\n \"Stopped monitoring user\",\n );\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 this.runtime.logger.debug(\n { src: \"plugin:discord:service:voice\", agentId: this.runtime.agentId },\n \"Cleaning up idle audio player\",\n );\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);\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 this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n entityId,\n },\n \"Starting audio monitor\",\n );\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 this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n entityId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error processing buffer\",\n );\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 this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n },\n \"Received empty buffer\",\n );\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) {\n return;\n }\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 this.runtime.logger.debug(\n { src: \"plugin:discord:service:voice\", agentId: this.runtime.agentId },\n \"Starting transcription\",\n );\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]\")) {\n return false;\n }\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 this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n entityId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error transcribing audio\",\n );\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,\n source: \"discord\",\n channelId,\n // Convert Discord snowflake to UUID (see service.ts header for why stringToUuid not asUUID)\n messageServerId: stringToUuid(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,\n userName,\n isVoiceMessage: true,\n channelType: type,\n },\n createdAt: Date.now(),\n };\n\n const callback: HandlerCallback = async (\n content: Content,\n _actionName?: string,\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 if (content.text) {\n const responseStream = await this.runtime.useModel(\n ModelType.TEXT_TO_SPEECH,\n content.text,\n );\n if (responseStream) {\n // Convert Buffer/ArrayBuffer to Readable stream\n const buffer = Buffer.isBuffer(responseStream)\n ? responseStream\n : Buffer.from(responseStream as ArrayBuffer);\n const readable = Readable.from(buffer);\n await this.playAudioStream(entityId, readable);\n }\n }\n }\n\n return [responseMemory];\n } catch (error) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error in voice message callback\",\n );\n return [];\n }\n };\n\n // Process voice message - try messageService first (newer core), fall back to events (older core)\n const messageService = getMessageService(this.runtime);\n if (messageService) {\n this.runtime.logger.debug(\n { src: \"plugin:discord:voice\", agentId: this.runtime.agentId },\n \"Using messageService API for voice\",\n );\n await messageService.handleMessage(this.runtime, memory, callback);\n } else {\n this.runtime.logger.debug(\n { src: \"plugin:discord:voice\", agentId: this.runtime.agentId },\n \"Using event-based handling for voice\",\n );\n await this.runtime.emitEvent([EventType.VOICE_MESSAGE_RECEIVED], {\n runtime: this.runtime,\n message: memory,\n callback,\n source: \"discord\",\n });\n }\n } catch (error) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error processing voice message\",\n );\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 this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error converting PCM to WAV\",\n );\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 this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n channelName: chosenChannel.name,\n },\n \"Joining channel\",\n );\n await this.joinChannel(chosenChannel);\n } else {\n this.runtime.logger.warn(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n },\n \"No suitable voice channel found to join\",\n );\n }\n } catch (error) {\n this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error selecting or joining a voice channel\",\n );\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 this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n entityId,\n },\n \"No connection for user\",\n );\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 this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n error: err instanceof Error ? err.message : String(err),\n },\n \"Audio player error\",\n );\n });\n\n audioPlayer.on(\n \"stateChange\",\n (_oldState: any, newState: { status: string }) => {\n if (newState.status === \"idle\") {\n const idleTime = Date.now();\n this.runtime.logger.debug(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n durationMs: idleTime - audioStartTime,\n },\n \"Audio playback completed\",\n );\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) {\n return;\n }\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 this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error joining voice channel\",\n );\n // Use editReply instead of reply for the error case\n await interaction\n .editReply(\"Failed to join the voice channel.\")\n .catch((err: Error) => {\n this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n error: err.message,\n },\n \"Failed to send error reply\",\n );\n });\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 this.runtime.logger.error(\n {\n src: \"plugin:discord:service:voice\",\n agentId: this.runtime.agentId,\n error: error instanceof Error ? error.message : String(error),\n },\n \"Error leaving voice channel\",\n );\n await interaction.reply(\"Failed to leave the voice channel.\");\n }\n }\n}\n","import { AuditLogEvent, Guild, PermissionOverwrites, Role, GuildMember } from 'discord.js';\nimport type { IAgentRuntime } from '@elizaos/core';\nimport type { PermissionDiff, PermissionState, AuditInfo } from './types';\n\n/**\n * Permissions that indicate moderation/admin capabilities.\n * Changes to these permissions are considered elevated and worth tracking.\n */\nexport const ELEVATED_PERMISSIONS = [\n 'Administrator',\n 'ManageGuild',\n 'ManageChannels',\n 'ManageRoles',\n 'KickMembers',\n 'BanMembers',\n 'ModerateMembers',\n 'ManageMessages',\n 'ManageWebhooks',\n 'ManageNicknames',\n 'MuteMembers',\n 'DeafenMembers',\n 'MoveMembers',\n 'ManageEvents',\n 'ManageThreads',\n] as const;\n\n/**\n * Check if a role has any elevated (moderation/admin) permissions\n */\nexport function isElevatedRole(role: Role): boolean {\n return ELEVATED_PERMISSIONS.some((p) => role.permissions.has(p));\n}\n\n/**\n * Check if an array of permission names contains any elevated permissions\n */\nexport function hasElevatedPermissions(permissions: string[]): boolean {\n return permissions.some((p) => ELEVATED_PERMISSIONS.includes(p as (typeof ELEVATED_PERMISSIONS)[number]));\n}\n\n/**\n * Fetch the most recent matching audit log entry for an action.\n * Matches by target ID and filters to entries within 10 seconds.\n * Returns null gracefully on errors (rate limits, missing permissions, etc.)\n */\nexport async function fetchAuditEntry(\n guild: Guild,\n actionType: AuditLogEvent,\n targetId: string,\n runtime: IAgentRuntime\n): Promise<AuditInfo | null> {\n try {\n const logs = await guild.fetchAuditLogs({ type: actionType, limit: 5 });\n const now = Date.now();\n\n for (const entry of logs.entries.values()) {\n // Match by target and ensure entry is recent (within 10 seconds)\n if (entry.targetId === targetId && now - entry.createdTimestamp < 10000) {\n return {\n executorId: entry.executor?.id ?? 'unknown',\n executorTag: entry.executor?.tag ?? 'Unknown',\n reason: entry.reason,\n };\n }\n }\n } catch (err) {\n // Graceful degradation - rate limits, missing permissions, etc.\n runtime.logger.debug(`Audit log fetch failed (non-critical): ${err}`);\n }\n return null;\n}\n\n/**\n * Get the permission state from allow/deny arrays\n */\nfunction getState(perm: string, allow: string[], deny: string[]): PermissionState {\n if (allow.includes(perm)) {return 'ALLOW';}\n if (deny.includes(perm)) {return 'DENY';}\n return 'NEUTRAL';\n}\n\n/**\n * Convert an overwrite to a list of changes (used for CREATE/DELETE)\n * For CREATE: from NEUTRAL to the actual state (ALLOW/DENY)\n * For DELETE: from the actual state (ALLOW/DENY) to NEUTRAL\n */\nfunction overwriteToChanges(\n ow: PermissionOverwrites,\n isDelete: boolean = false\n): PermissionDiff[] {\n const changes: PermissionDiff[] = [];\n for (const p of ow.allow.toArray()) {\n changes.push({\n permission: p,\n oldState: isDelete ? 'ALLOW' : 'NEUTRAL',\n newState: isDelete ? 'NEUTRAL' : 'ALLOW',\n });\n }\n for (const p of ow.deny.toArray()) {\n changes.push({\n permission: p,\n oldState: isDelete ? 'DENY' : 'NEUTRAL',\n newState: isDelete ? 'NEUTRAL' : 'DENY',\n });\n }\n return changes;\n}\n\n/**\n * Diff two permission overwrites and determine what changed.\n * Returns the list of changes and the action type (CREATE/UPDATE/DELETE).\n */\nexport function diffOverwrites(\n oldOw: PermissionOverwrites | undefined | null,\n newOw: PermissionOverwrites | undefined | null\n): { changes: PermissionDiff[]; action: 'CREATE' | 'UPDATE' | 'DELETE' } {\n // Both null - no change\n if (!oldOw && !newOw) {\n return { changes: [], action: 'UPDATE' };\n }\n\n // Created new overwrite\n if (!oldOw && newOw) {\n return { changes: overwriteToChanges(newOw, false), action: 'CREATE' };\n }\n\n // Deleted overwrite\n if (oldOw && !newOw) {\n return { changes: overwriteToChanges(oldOw, true), action: 'DELETE' };\n }\n\n // Updated overwrite - compare old and new\n const changes: PermissionDiff[] = [];\n const oldAllow = oldOw!.allow.toArray();\n const oldDeny = oldOw!.deny.toArray();\n const newAllow = newOw!.allow.toArray();\n const newDeny = newOw!.deny.toArray();\n\n // Get all permissions that exist in either old or new\n const allPerms = new Set([...oldAllow, ...oldDeny, ...newAllow, ...newDeny]);\n\n for (const perm of allPerms) {\n const oldState = getState(perm, oldAllow, oldDeny);\n const newState = getState(perm, newAllow, newDeny);\n if (oldState !== newState) {\n changes.push({ permission: perm, oldState, newState });\n }\n }\n\n return { changes, action: 'UPDATE' };\n}\n\n/**\n * Diff two role permission sets and return what changed.\n */\nexport function diffRolePermissions(oldRole: Role, newRole: Role): PermissionDiff[] {\n const oldPerms = oldRole.permissions.toArray();\n const newPerms = newRole.permissions.toArray();\n const changes: PermissionDiff[] = [];\n\n // Find added permissions\n for (const p of newPerms) {\n if (!oldPerms.includes(p)) {\n changes.push({ permission: p, oldState: 'NEUTRAL', newState: 'ALLOW' });\n }\n }\n\n // Find removed permissions\n for (const p of oldPerms) {\n if (!newPerms.includes(p)) {\n changes.push({ permission: p, oldState: 'ALLOW', newState: 'NEUTRAL' });\n }\n }\n\n return changes;\n}\n\n/**\n * Diff member roles to find added and removed roles.\n */\nexport function diffMemberRoles(\n oldMember: GuildMember,\n newMember: GuildMember\n): { added: Role[]; removed: Role[] } {\n const oldRoles = oldMember.roles.cache;\n const newRoles = newMember.roles.cache;\n\n return {\n added: [...newRoles.filter((r) => !oldRoles.has(r.id)).values()],\n removed: [...oldRoles.filter((r) => !newRoles.has(r.id)).values()],\n };\n}\n","/**\n * Runtime compatibility layer for old/new core.\n *\n * Automatically adds serverId when messageServerId is provided,\n * making plugin code work with both core versions unchanged.\n *\n * Old core expects: serverId (string)\n * New core expects: messageServerId (UUID)\n *\n * NOTE: UUID function usage for Discord IDs:\n * - `stringToUuid(str)` - CONVERTS any string to a deterministic UUID by hashing.\n * Use this for Discord snowflake IDs (always succeeds, same input = same output).\n * - `asUUID(str)` - VALIDATES that string is already a valid UUID format.\n * Throws if not a valid UUID. Only use when input is already a UUID.\n *\n * REMOVAL: Delete this file and remove createCompatRuntime() call in service.ts\n */\nimport type { IAgentRuntime, UUID, World, Room, ChannelType, Entity } from '@elizaos/core';\n\n/**\n * Extended types that support messageServerId for cross-core compatibility.\n * These allow TypeScript to accept messageServerId in object literals.\n */\nexport type WorldCompat = Omit<World, 'serverId'> & {\n serverId?: string;\n messageServerId?: UUID;\n};\n\nexport type RoomCompat = Omit<Room, 'serverId'> & {\n serverId?: string;\n messageServerId?: UUID;\n};\n\nexport interface EnsureConnectionParams {\n entityId: UUID;\n roomId: UUID;\n userName?: string;\n name?: string;\n worldName?: string;\n source?: string;\n channelId?: string;\n serverId?: string;\n messageServerId?: UUID;\n type?: ChannelType | string;\n worldId: UUID;\n userId?: UUID;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Extended runtime interface that accepts messageServerId in method parameters.\n */\nexport interface ICompatRuntime extends Omit<IAgentRuntime, 'ensureWorldExists' | 'ensureRoomExists' | 'ensureConnection' | 'ensureConnections'> {\n ensureWorldExists(world: WorldCompat): Promise<void>;\n ensureRoomExists(room: RoomCompat): Promise<void>;\n ensureConnection(params: EnsureConnectionParams): Promise<void>;\n ensureConnections(entities: Entity[], rooms: RoomCompat[], source: string, world: WorldCompat): Promise<void>;\n}\n\nfunction addServerId<T extends Record<string, unknown>>(obj: T): T {\n if (!obj?.messageServerId) {return obj;}\n return { ...obj, serverId: obj.serverId ?? obj.messageServerId };\n}\n\nexport function createCompatRuntime(runtime: IAgentRuntime): ICompatRuntime {\n return new Proxy(runtime, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof value !== 'function') {return value;}\n\n if (prop === 'ensureWorldExists') {\n return (world: unknown) =>\n value.call(target, addServerId(world as Record<string, unknown>));\n }\n if (prop === 'ensureRoomExists') {\n return (room: unknown) =>\n value.call(target, addServerId(room as Record<string, unknown>));\n }\n if (prop === 'ensureConnection') {\n return (params: unknown) =>\n value.call(target, addServerId(params as Record<string, unknown>));\n }\n if (prop === 'ensureConnections') {\n return (entities: unknown[], rooms: unknown[], source: string, world: unknown) =>\n value.call(\n target,\n entities,\n rooms.map((r) => addServerId(r as Record<string, unknown>)),\n source,\n addServerId(world as Record<string, unknown>)\n );\n }\n\n return value;\n },\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","/**\n * Discord Plugin Settings Banner\n * Beautiful ANSI art display for configuration on startup\n * Includes tiered permission system for invite URLs\n */\n\nimport type { IAgentRuntime } from '@elizaos/core';\nimport { getPermissionValues, type DiscordPermissionValues } from './permissions';\n\nconst ANSI = {\n reset: '\\x1b[0m',\n bold: '\\x1b[1m',\n dim: '\\x1b[2m',\n blue: '\\x1b[34m',\n brightRed: '\\x1b[91m',\n brightGreen: '\\x1b[92m',\n brightYellow: '\\x1b[93m',\n brightBlue: '\\x1b[94m',\n brightMagenta: '\\x1b[95m',\n brightCyan: '\\x1b[96m',\n brightWhite: '\\x1b[97m',\n};\n\nexport interface PluginSetting {\n name: string;\n value: unknown;\n defaultValue?: unknown;\n sensitive?: boolean;\n required?: boolean;\n}\n\nexport interface BannerOptions {\n pluginName: string;\n description?: string;\n settings: PluginSetting[];\n runtime: IAgentRuntime;\n /** Discord Application ID for generating invite URLs */\n applicationId?: string;\n /** Permission values for the 3x2 tier matrix */\n discordPermissions?: DiscordPermissionValues;\n /** @deprecated Use applicationId + discordPermissions instead */\n discordInviteLink?: string;\n}\n\nfunction mask(v: string): string {\n if (!v || v.length <= 8) {return '••••••••';}\n return `${v.slice(0, 4)}${'•'.repeat(Math.min(12, v.length - 8))}${v.slice(-4)}`;\n}\n\n/**\n * Format a value for display in the banner.\n *\n * @param value - The value to format; may be `undefined`, `null`, or an empty string.\n * @param sensitive - Whether the value should be obfuscated for display.\n * @param maxLen - Maximum allowed length of the returned string; longer values are truncated with an ellipsis.\n * @returns A display string: `'(not set)'` if `value` is `undefined`, `null`, or an empty string; a masked representation if `sensitive` is true; otherwise the stringified value truncated to at most `maxLen` characters (truncated strings end with `'...'`).\n */\nfunction fmtVal(value: unknown, sensitive: boolean, maxLen: number): string {\n let s: string;\n if (value === undefined || value === null || value === '') {\n s = '(not set)';\n } else if (sensitive) {\n s = mask(String(value));\n } else {\n s = String(value);\n }\n if (s.length > maxLen) {s = `${s.slice(0, maxLen - 3)}...`;}\n return s;\n}\n\nconst ANSI_PATTERN = /\\x1b\\[[0-9;]*m/g;\n\n/**\n * Pads a string with trailing spaces until its visible (ANSI-stripped) length is at least the given width.\n *\n * @param s - The input string which may contain ANSI escape sequences.\n * @param n - The target visible width (number of characters) after padding.\n * @returns The original string if its visible length is >= `n`, otherwise the string with trailing spaces appended so its visible length equals `n`.\n */\nfunction pad(s: string, n: number): string {\n const len = s.replace(ANSI_PATTERN, '').length;\n if (len >= n) {return s;}\n return s + ' '.repeat(n - len);\n}\n\nfunction line(content: string): string {\n const len = content.replace(ANSI_PATTERN, '').length;\n\n if (len <= 78) {\n return content + ' '.repeat(78 - len);\n }\n\n // Truncate based on visible character count, not raw string position\n // This avoids cutting in the middle of ANSI escape sequences\n let visibleCount = 0;\n let result = '';\n let i = 0;\n\n while (i < content.length && visibleCount < 78) {\n const remaining = content.slice(i);\n const match = remaining.match(/^\\x1b\\[[0-9;]*m/);\n\n if (match) {\n // Include ANSI sequence without counting toward visible length\n result += match[0];\n i += match[0].length;\n } else {\n // Regular visible character\n result += content[i];\n visibleCount++;\n i++;\n }\n }\n\n // Reset any unclosed ANSI sequences after truncation\n return result + ANSI.reset;\n}\n\n/**\n * Render a framed ANSI banner that displays plugin settings and, when available, tiered Discord invite URLs.\n *\n * The banner lists each setting with masked or truncated values, a status (custom/default/unset/required),\n * and an optional Discord invite section generated from `applicationId` and `discordPermissions`. For backwards\n * compatibility, a legacy `discordInviteLink` may be used when the permissions-based info is unavailable.\n *\n * @param options - Configuration for the banner, including `settings`, the `runtime` used to emit the banner,\n * and optional Discord invite data (`applicationId`, `discordPermissions`). Note: `discordInviteLink`\n * is deprecated and retained only for backwards compatibility.\n */\nexport function printBanner(options: BannerOptions): void {\n const { settings, runtime } = options;\n const R = ANSI.reset,\n D = ANSI.dim,\n B = ANSI.bold;\n const c1 = ANSI.brightBlue,\n c2 = ANSI.brightCyan,\n c3 = ANSI.brightMagenta;\n\n const top = `${c1}╔${'═'.repeat(78)}╗${R}`;\n const mid = `${c1}╠${'═'.repeat(78)}╣${R}`;\n const bot = `${c1}╚${'═'.repeat(78)}╝${R}`;\n const row = (s: string) => `${c1}║${R}${line(s)}${c1}║${R}`;\n\n const lines: string[] = [''];\n lines.push(top);\n lines.push(row(` ${B}Character: ${runtime.character.name}${R}`));\n lines.push(mid);\n lines.push(row(`${c2} ██████╗ ██╗███████╗ ██████╗ ██████╗ ██████╗ ██████╗ ${c3}◖ ◗${R}`));\n lines.push(row(`${c2} ██╔══██╗██║██╔════╝██╔════╝██╔═══██╗██╔══██╗██╔══██╗ ${c3}◖===◗${R}`));\n lines.push(row(`${c2} ██║ ██║██║███████╗██║ ██║ ██║██████╔╝██║ ██║ ${c3}╰─╯${R}`));\n lines.push(row(`${c2} ██████╔╝██║╚════██║╚██████╗╚██████╔╝██║ ██║██████╔╝ ${c3}(◠◠)${R}`));\n lines.push(row(`${c2} ╚═════╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ${c3}‿‿${R}`));\n lines.push(row(`${D} Bot Integration • Servers • Channels • Voice${R}`));\n lines.push(mid);\n\n const NW = 34,\n VW = 26,\n SW = 8;\n lines.push(row(` ${B}${pad('ENV VARIABLE', NW)} ${pad('VALUE', VW)} ${pad('STATUS', SW)}${R}`));\n lines.push(row(` ${D}${'-'.repeat(NW)} ${'-'.repeat(VW)} ${'-'.repeat(SW)}${R}`));\n\n for (const s of settings) {\n const set = s.value !== undefined && s.value !== null && s.value !== '';\n // Normalize to string for comparison (e.g., boolean false vs string 'false')\n const isDefault = set && s.defaultValue !== undefined && String(s.value) === String(s.defaultValue);\n\n let ico: string, st: string;\n if (!set && s.required) {\n ico = `${ANSI.brightRed}◆${R}`;\n st = `${ANSI.brightRed}REQUIRED${R}`;\n } else if (!set) {\n ico = `${D}○${R}`;\n st = `${D}unset${R}`;\n } else if (isDefault) {\n ico = `${ANSI.brightBlue}●${R}`;\n st = `${ANSI.brightBlue}default${R}`;\n } else {\n ico = `${ANSI.brightGreen}✓${R}`;\n st = `${ANSI.brightGreen}custom${R}`;\n }\n\n const name = pad(s.name, NW - 2);\n const val = pad(fmtVal(s.value ?? s.defaultValue, s.sensitive ?? false, VW), VW);\n const status = pad(st, SW);\n lines.push(row(` ${ico} ${c2}${name}${R} ${val} ${status}`));\n }\n\n lines.push(mid);\n lines.push(\n row(\n ` ${D}${ANSI.brightGreen}✓${D} custom ${ANSI.brightBlue}●${D} default ○ unset ${ANSI.brightRed}◆${D} required → Set in .env${R}`\n )\n );\n lines.push(bot);\n\n // Add Discord invite links organized by voice capability\n if (options.applicationId && options.discordPermissions) {\n const p = options.discordPermissions;\n const baseUrl = `https://discord.com/api/oauth2/authorize?client_id=${options.applicationId}&scope=bot%20applications.commands&permissions=`;\n\n lines.push('');\n lines.push(`${B}${ANSI.brightCyan}🔗 Discord Bot Invite${R}`);\n lines.push('');\n lines.push(` ${B}🎙️ With Voice:${R}`);\n lines.push(` ${ANSI.brightGreen}● Basic${R} ${baseUrl}${p.basicVoice}`);\n lines.push(` ${ANSI.brightYellow}● Moderator${R} ${baseUrl}${p.moderatorVoice}`);\n lines.push(` ${ANSI.brightRed}● Admin${R} ${baseUrl}${p.adminVoice}`);\n lines.push('');\n lines.push(` ${B}💬 Without Voice:${R}`);\n lines.push(` ${ANSI.brightCyan}○ Basic${R} ${baseUrl}${p.basic}`);\n lines.push(` ${ANSI.brightMagenta}○ Moderator${R} ${baseUrl}${p.moderator}`);\n lines.push(` ${ANSI.brightBlue}○ Admin${R} ${baseUrl}${p.admin}`);\n } else if (options.discordInviteLink) {\n // Backwards compatibility\n lines.push('');\n lines.push(`${B}${ANSI.brightCyan}🔗 Discord Bot Invite:${R} ${options.discordInviteLink}`);\n }\n\n lines.push('');\n\n runtime.logger.info(lines.join('\\n'));\n}\n\n/**\n * Simple banner for backwards compatibility\n * @deprecated Use printBanner with BannerOptions instead\n */\nexport function printDiscordBanner(runtime: IAgentRuntime): void {\n // Get settings\n const apiToken = runtime.getSetting('DISCORD_API_TOKEN');\n const applicationId = runtime.getSetting('DISCORD_APPLICATION_ID');\n const ignoreBots = runtime.getSetting('DISCORD_SHOULD_IGNORE_BOT_MESSAGES');\n const ignoreDMs = runtime.getSetting('DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES');\n const onlyMentions = runtime.getSetting('DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS');\n const listenChannels = runtime.getSetting('DISCORD_LISTEN_CHANNEL_IDS');\n const voiceChannelId = runtime.getSetting('DISCORD_VOICE_CHANNEL_ID');\n\n printBanner({\n pluginName: 'plugin-discord',\n description: 'Discord bot integration for servers and channels',\n applicationId: applicationId || undefined,\n discordPermissions: applicationId ? getPermissionValues() : undefined,\n settings: [\n { name: 'DISCORD_API_TOKEN', value: apiToken, sensitive: true, required: true },\n { name: 'DISCORD_APPLICATION_ID', value: applicationId },\n { name: 'DISCORD_VOICE_CHANNEL_ID', value: voiceChannelId },\n { name: 'DISCORD_LISTEN_CHANNEL_IDS', value: listenChannels },\n { name: 'DISCORD_SHOULD_IGNORE_BOT_MESSAGES', value: ignoreBots, defaultValue: 'false' },\n { name: 'DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES', value: ignoreDMs, defaultValue: 'false' },\n { name: 'DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS', value: onlyMentions, defaultValue: 'false' },\n ],\n runtime,\n });\n}\n"],"mappings":"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","names":["logger","ContentType","ModelType","composePromptFromState","parseJSONObjectFromText","ModelType","composePromptFromState","parseJSONObjectFromText","composePromptFromState","ModelType","parseJSONObjectFromText","member","ModelType","composePromptFromState","parseJSONObjectFromText","createUniqueUuid","DiscordChannelType","composePromptFromState","ModelType","parseJSONObjectFromText","findChannel","createUniqueUuid","ModelType","composePromptFromState","parseJSONObjectFromText","composePromptFromState","ModelType","parseJSONObjectFromText","ModelType","composePromptFromState","parseJSONObjectFromText","composePromptFromState","ModelType","parseJSONObjectFromText","fs","ContentType","ModelType","composePromptFromState","parseJSONObjectFromText","trimTokens","summarizationTemplate","ContentType","ModelType","composePromptFromState","parseJSONObjectFromText","composePromptFromState","ModelType","parseJSONObjectFromText","attachment","ContentType","ModelType","composePromptFromState","parseJSONObjectFromText","composePromptFromState","ModelType","parseJSONObjectFromText","ModelType","composePromptFromState","parseJSONObjectFromText","composePromptFromState","ModelType","parseJSONObjectFromText","ModelType","composePromptFromState","parseJSONObjectFromText","composePromptFromState","ModelType","parseJSONObjectFromText","serverInfo","ModelType","composePromptFromState","parseJSONObjectFromText","composePromptFromState","ModelType","parseJSONObjectFromText","ModelType","composePromptFromState","parseJSONObjectFromText","PermissionsBitField","composePromptFromState","ModelType","parseJSONObjectFromText","ModelType","composePromptFromState","parseJSONObjectFromText","PermissionsBitField","getMessageRef","composePromptFromState","ModelType","parseJSONObjectFromText","ChannelType","DiscordEventTypes","ServiceType","ChannelType","ServiceType","ChannelType","ChannelType","ServiceType","ChannelType","EventType","stringToUuid","createUniqueUuid","AttachmentBuilder","DiscordChannelType","PermissionsBitField","ChannelType","ServiceType","createUniqueUuid","DiscordChannelType","AttachmentBuilder","fs","ModelType","ServiceType","ModelType","logger","parseJSONObjectFromText","trimTokens","ChannelType","PermissionsBitField","line","ServiceType","ModelType","fs","DiscordChannelType","createUniqueUuid","ChannelType","AttachmentBuilder","match","ServiceType","ChannelType","EventType","ModelType","stringToUuid","createUniqueUuid","logger","DiscordChannelType","logger","DiscordChannelType","ChannelType","connection","isValidTranscription","ModelType","createUniqueUuid","stringToUuid","EventType","AttachmentBuilder","createUniqueUuid","sent","stringToUuid","EventType","ChannelType","DiscordChannelType","PermissionsBitField","elapsedSec","NoSubscriberBehavior","VoiceConnectionStatus","createAudioPlayer","createAudioResource","entersState","ModelType","logger","ChannelType","Events","AttachmentBuilder","ServiceType","logger","Events","ChannelType","entersState","VoiceConnectionStatus","ModelType","AttachmentBuilder","createAudioPlayer","NoSubscriberBehavior","createAudioResource","logger"]}
|