@elizaos/plugin-discord 1.3.2 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +37 -29
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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/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 \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.join(', '))\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.client!.once(Events.ClientReady, (readyClient) => {\n this.onReady(readyClient)\n });\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 if (this.client) {\n this.client.destroy().catch(() => {});\n }\n this.client = null;\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\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 const roomId = createUniqueUuid(this.runtime, message.channel.id);\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.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 // 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 userName = message.author.bot\n ? `${message.author.username}#${message.author.discriminator}`\n : message.author.username;\n const name =\n message.member?.displayName ??\n message.author.displayName ??\n message.author.globalName ??\n userName;\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 this.runtime.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 (content: Content) => {\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 []\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 // Use unified messaging API if available, otherwise fall back to direct message service\n if (this.runtime.hasElizaOS()) {\n logger.debug('[Discord] Using unified messaging API');\n await this.runtime.elizaOS.sendMessage(\n this.runtime.agentId,\n newMessage,\n {\n onResponse: callback,\n }\n );\n } else {\n // Fallback to direct message service call (standalone mode)\n logger.debug('[Discord] Using direct message service');\n await this.runtime.messageService.handleMessage(this.runtime, newMessage, callback);\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 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","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","NoSubscriberBehavior","VoiceConnectionStatus","createAudioPlayer","createAudioResource","entersState","ModelType","logger","ChannelType","Events","ServiceType","logger","Events","ChannelType","entersState","VoiceConnectionStatus","ModelType","createAudioPlayer","NoSubscriberBehavior","createAudioResource","logger"]}
|