@elizaos/plugin-discord 1.0.0-alpha.20 → 1.0.0-alpha.21
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 +103 -56
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/actions/chatWithAttachments.ts","../src/actions/downloadMedia.ts","../src/actions/summarizeConversation.ts","../src/actions/transcribeMedia.ts","../src/actions/voiceJoin.ts","../src/types.ts","../src/actions/voiceLeave.ts","../src/constants.ts","../src/messages.ts","../src/attachments.ts","../src/utils.ts","../src/providers/channelState.ts","../src/providers/voiceState.ts","../src/tests.ts","../src/voice.ts"],"sourcesContent":["import {\n\tChannelType,\n\ttype Character,\n\ttype Entity,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype Memory,\n\ttype Plugin,\n\tRole,\n\tService,\n\ttype UUID,\n\ttype World,\n\tcreateUniqueUuid,\n\tlogger,\n} from \"@elizaos/core\";\nimport {\n\ttype Channel,\n\tChannelType as DiscordChannelType,\n\tClient as DiscordJsClient,\n\tEvents,\n\tGatewayIntentBits,\n\ttype Guild,\n\ttype GuildMember,\n\ttype MessageReaction,\n\ttype OAuth2Guild,\n\tPartials,\n\tPermissionsBitField,\n\ttype TextChannel,\n\ttype User,\n} from \"discord.js\";\nimport chatWithAttachments from \"./actions/chatWithAttachments\";\nimport downloadMedia from \"./actions/downloadMedia\";\nimport summarize from \"./actions/summarizeConversation\";\nimport transcribe_media from \"./actions/transcribeMedia\";\nimport joinVoice from \"./actions/voiceJoin\";\nimport leaveVoice from \"./actions/voiceLeave\";\nimport { DISCORD_SERVICE_NAME } from \"./constants\";\nimport { MessageManager } from \"./messages\";\nimport channelStateProvider from \"./providers/channelState\";\nimport voiceStateProvider from \"./providers/voiceState\";\nimport { DiscordTestSuite } from \"./tests\";\nimport type { IDiscordService } from \"./types\";\nimport { VoiceManager } from \"./voice\";\n\n/**\n * DiscordService class representing a service for interacting with Discord.\n * @extends Service\n * @implements IDiscordService\n * @property {string} serviceType - The type of service, set to DISCORD_SERVICE_NAME.\n * @property {string} capabilityDescription - A description of the service's capabilities.\n * @property {DiscordJsClient} client - The DiscordJsClient used for communication.\n * @property {Character} character - The character associated with the service.\n * @property {MessageManager} messageManager - The manager for handling messages.\n * @property {VoiceManager} voiceManager - The manager for handling voice communication.\n */\nexport class DiscordService extends Service implements IDiscordService {\n\tstatic serviceType: string = DISCORD_SERVICE_NAME;\n\tcapabilityDescription =\n\t\t\"The agent is able to send and receive messages on discord\";\n\tclient: DiscordJsClient | null;\n\tcharacter: Character;\n\tmessageManager: MessageManager;\n\tvoiceManager: VoiceManager;\n\n\t/**\n\t * Constructor for Discord client.\n\t * Initializes the Discord client with specified intents and partials,\n\t * sets up event listeners, and ensures all servers exist.\n\t *\n\t * @param {IAgentRuntime} runtime - The AgentRuntime instance\n\t */\n\n\tconstructor(runtime: IAgentRuntime) {\n\t\tsuper(runtime);\n\n\t\tlogger.log(\"Discord client constructor was engaged\");\n\n\t\t// Check if Discord API token is available and valid\n\t\tconst token = runtime.getSetting(\"DISCORD_API_TOKEN\") as string;\n\t\tif (!token || token.trim() === \"\") {\n\t\t\tlogger.warn(\n\t\t\t\t\"Discord API Token not provided - Discord functionality will be unavailable\",\n\t\t\t);\n\t\t\tthis.client = null;\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.client = new DiscordJsClient({\n\t\t\t\tintents: [\n\t\t\t\t\tGatewayIntentBits.Guilds,\n\t\t\t\t\tGatewayIntentBits.GuildMembers,\n\t\t\t\t\tGatewayIntentBits.GuildPresences,\n\t\t\t\t\tGatewayIntentBits.DirectMessages,\n\t\t\t\t\tGatewayIntentBits.GuildVoiceStates,\n\t\t\t\t\tGatewayIntentBits.MessageContent,\n\t\t\t\t\tGatewayIntentBits.GuildMessages,\n\t\t\t\t\tGatewayIntentBits.DirectMessageTyping,\n\t\t\t\t\tGatewayIntentBits.GuildMessageTyping,\n\t\t\t\t\tGatewayIntentBits.GuildMessageReactions,\n\t\t\t\t],\n\t\t\t\tpartials: [\n\t\t\t\t\tPartials.Channel,\n\t\t\t\t\tPartials.Message,\n\t\t\t\t\tPartials.User,\n\t\t\t\t\tPartials.Reaction,\n\t\t\t\t],\n\t\t\t});\n\n\t\t\tthis.runtime = runtime;\n\t\t\tthis.voiceManager = new VoiceManager(this, runtime);\n\t\t\tthis.messageManager = new MessageManager(this);\n\n\t\t\tthis.client.once(Events.ClientReady, this.onClientReady.bind(this));\n\t\t\tthis.client.login(token).catch((error) => {\n\t\t\t\tlogger.error(`Failed to login to Discord: ${error.message}`);\n\t\t\t\tthis.client = null;\n\t\t\t});\n\n\t\t\tthis.setupEventListeners();\n\n\t\t\t// give it to the\n\t\t\tconst ensureAllServersExist = async (runtime: IAgentRuntime) => {\n\t\t\t\tconst guilds = await this.client.guilds.fetch();\n\t\t\t\tfor (const [, guild] of guilds) {\n\t\t\t\t\tawait this.ensureAllChannelsExist(runtime, guild);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tensureAllServersExist(this.runtime);\n\t\t} catch (error) {\n\t\t\tlogger.error(`Error initializing Discord client: ${error.message}`);\n\t\t\tthis.client = null;\n\t\t}\n\t}\n\n\t/**\n\t * Ensures that all channels exist in the database for a given guild.\n\t * @param {IAgentRuntime} runtime - The agent runtime object.\n\t * @param {OAuth2Guild} guild - The OAuth2Guild object for which channels need to be ensured.\n\t * @returns {Promise<void>} - A Promise that resolves once all channels are ensured.\n\t */\n\tasync ensureAllChannelsExist(runtime: IAgentRuntime, guild: OAuth2Guild) {\n\t\t// fetch the owning member from the OAuth2Guild object\n\t\tconst guildObj = await guild.fetch();\n\t\tconst guildChannels = await guild.fetch();\n\t\t// for channel in channels\n\t\tfor (const [, channel] of guildChannels.channels.cache) {\n\t\t\tconst roomId = createUniqueUuid(this.runtime, channel.id);\n\t\t\tconst room = await runtime.getDatabaseAdapter().getRoom(roomId);\n\t\t\t// if the room already exists, skip\n\t\t\tif (room) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst worldId = createUniqueUuid(runtime, guild.id);\n\t\t\tconst ownerId = createUniqueUuid(this.runtime, guildObj.ownerId);\n\t\t\tawait runtime.ensureWorldExists({\n\t\t\t\tid: worldId,\n\t\t\t\tname: guild.name,\n\t\t\t\tserverId: guild.id,\n\t\t\t\tagentId: runtime.agentId,\n\t\t\t\tmetadata: {\n\t\t\t\t\townership: guildObj.ownerId ? { ownerId } : undefined,\n\t\t\t\t\troles: {\n\t\t\t\t\t\t[ownerId]: Role.OWNER,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t});\n\t\t\tawait runtime.ensureRoomExists({\n\t\t\t\tid: roomId,\n\t\t\t\tname: channel.name,\n\t\t\t\tsource: \"discord\",\n\t\t\t\ttype: ChannelType.GROUP,\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tserverId: guild.id,\n\t\t\t\tworldId,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Set up event listeners for the client\n\t */\n\tprivate setupEventListeners() {\n\t\tif (!this.client) {\n\t\t\treturn; // Skip if client is not available\n\t\t}\n\n\t\t// Setup handling for direct messages\n\t\tthis.client.on(\"messageCreate\", (message) => {\n\t\t\t// Skip if we're sending the message or in deleted state\n\t\t\tif (message.author.id === this.client?.user?.id || message.author.bot) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tthis.messageManager.handleMessage(message);\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Error handling message: ${error}`);\n\t\t\t}\n\t\t});\n\n\t\t// Setup handling for reactions\n\t\tthis.client.on(\"messageReactionAdd\", async (reaction, user) => {\n\t\t\tif (user.id === this.client?.user?.id) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait this.handleReactionAdd(reaction, user);\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Error handling reaction add: ${error}`);\n\t\t\t}\n\t\t});\n\n\t\t// Handle reaction removal\n\t\tthis.client.on(\"messageReactionRemove\", async (reaction, user) => {\n\t\t\tif (user.id === this.client?.user?.id) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait this.handleReactionRemove(reaction, user);\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Error handling reaction remove: ${error}`);\n\t\t\t}\n\t\t});\n\n\t\t// Setup guild (server) event handlers\n\t\tthis.client.on(\"guildCreate\", async (guild) => {\n\t\t\ttry {\n\t\t\t\tawait this.handleGuildCreate(guild);\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Error handling guild create: ${error}`);\n\t\t\t}\n\t\t});\n\n\t\t// Setup member (user) joining handlers\n\t\tthis.client.on(\"guildMemberAdd\", async (member) => {\n\t\t\ttry {\n\t\t\t\tawait this.handleGuildMemberAdd(member);\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Error handling guild member add: ${error}`);\n\t\t\t}\n\t\t});\n\n\t\t// Interaction handlers\n\t\tthis.client.on(\"interactionCreate\", async (interaction) => {\n\t\t\ttry {\n\t\t\t\tawait this.handleInteractionCreate(interaction);\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Error handling interaction: ${error}`);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Handles the event when a new member joins a guild.\n\t *\n\t * @param {GuildMember} member - The GuildMember object representing the new member that joined the guild.\n\t * @returns {Promise<void>} - A Promise that resolves once the event handling is complete.\n\t */\n\tprivate async handleGuildMemberAdd(member: GuildMember) {\n\t\tlogger.log(`New member joined: ${member.user.username}`);\n\n\t\tconst guild = member.guild;\n\n\t\tconst tag = member.user.bot\n\t\t\t? `${member.user.username}#${member.user.discriminator}`\n\t\t\t: member.user.username;\n\n\t\t// Emit standardized USER_JOINED event\n\t\tthis.runtime.emitEvent(\"USER_JOINED\", {\n\t\t\truntime: this.runtime,\n\t\t\tentityId: createUniqueUuid(this.runtime, member.id),\n\t\t\tuser: {\n\t\t\t\tid: member.id,\n\t\t\t\tusername: tag,\n\t\t\t\tdisplayName: member.displayName || member.user.username,\n\t\t\t},\n\t\t\tserverId: guild.id,\n\t\t\tchannelId: null, // No specific channel for server joins\n\t\t\tchannelType: ChannelType.WORLD,\n\t\t\tsource: \"discord\",\n\t\t});\n\n\t\tthis.runtime.emitEvent(\"DISCORD_USER_JOINED\", {\n\t\t\truntime: this.runtime,\n\t\t\tentityId: createUniqueUuid(this.runtime, member.id),\n\t\t\tmember,\n\t\t\tguild,\n\t\t});\n\t}\n\n\t/**\n\t *\n\t * Start the Discord service\n\t * @param {IAgentRuntime} runtime - The runtime for the agent\n\t * @returns {Promise<DiscordService>} A promise that resolves to a DiscordService instance\n\t *\n\t */\n\tstatic async start(runtime: IAgentRuntime): Promise<DiscordService> {\n\t\tconst client = new DiscordService(runtime);\n\t\treturn client;\n\t}\n\n\t/**\n\t * Stops the Discord client associated with the given runtime.\n\t *\n\t * @param {IAgentRuntime} runtime - The runtime associated with the Discord client.\n\t * @returns {void}\n\t */\n\tstatic async stop(runtime: IAgentRuntime) {\n\t\tconst client = runtime.getService(DISCORD_SERVICE_NAME);\n\t\tif (!client) {\n\t\t\tlogger.error(\"DiscordService not found\");\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\t// disconnect websocket\n\t\t\t// this unbinds all the listeners\n\t\t\tawait client.stop();\n\t\t} catch (e) {\n\t\t\tlogger.error(\"client-discord instance stop err\", e);\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously stops the client by destroying it.\n\t *\n\t * @returns {Promise<void>}\n\t */\n\tasync stop() {\n\t\tawait this.client?.destroy();\n\t}\n\n\t/**\n\t * Handle the event when the client is ready.\n\t * @param {Object} readyClient - The ready client object containing user information.\n\t * @param {string} readyClient.user.tag - The username and discriminator of the client user.\n\t * @param {string} readyClient.user.id - The user ID of the client.\n\t * @returns {Promise<void>}\n\t */\n\tprivate async onClientReady(readyClient: { user: { tag: any; id: any } }) {\n\t\tlogger.success(`Logged in as ${readyClient.user?.tag}`);\n\n\t\t// Register slash commands\n\t\tconst commands = [\n\t\t\t{\n\t\t\t\tname: \"joinchannel\",\n\t\t\t\tdescription: \"Join a voice channel\",\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"channel\",\n\t\t\t\t\t\ttype: 7, // CHANNEL type\n\t\t\t\t\t\tdescription: \"The voice channel to join\",\n\t\t\t\t\t\trequired: true,\n\t\t\t\t\t\tchannel_types: [2], // GuildVoice type\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"leavechannel\",\n\t\t\t\tdescription: \"Leave the current voice channel\",\n\t\t\t},\n\t\t];\n\n\t\ttry {\n\t\t\tawait this.client?.application?.commands.set(commands);\n\t\t\tlogger.success(\"Slash commands registered\");\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error registering slash commands:\", error);\n\t\t}\n\n\t\t// Required permissions for the bot\n\t\tconst requiredPermissions = [\n\t\t\t// Text Permissions\n\t\t\tPermissionsBitField.Flags.ViewChannel,\n\t\t\tPermissionsBitField.Flags.SendMessages,\n\t\t\tPermissionsBitField.Flags.SendMessagesInThreads,\n\t\t\tPermissionsBitField.Flags.CreatePrivateThreads,\n\t\t\tPermissionsBitField.Flags.CreatePublicThreads,\n\t\t\tPermissionsBitField.Flags.EmbedLinks,\n\t\t\tPermissionsBitField.Flags.AttachFiles,\n\t\t\tPermissionsBitField.Flags.AddReactions,\n\t\t\tPermissionsBitField.Flags.UseExternalEmojis,\n\t\t\tPermissionsBitField.Flags.UseExternalStickers,\n\t\t\tPermissionsBitField.Flags.MentionEveryone,\n\t\t\tPermissionsBitField.Flags.ManageMessages,\n\t\t\tPermissionsBitField.Flags.ReadMessageHistory,\n\t\t\t// Voice Permissions\n\t\t\tPermissionsBitField.Flags.Connect,\n\t\t\tPermissionsBitField.Flags.Speak,\n\t\t\tPermissionsBitField.Flags.UseVAD,\n\t\t\tPermissionsBitField.Flags.PrioritySpeaker,\n\t\t].reduce((a, b) => a | b, 0n);\n\n\t\tlogger.success(\"Use this URL to add the bot to your server:\");\n\t\tlogger.success(\n\t\t\t`https://discord.com/api/oauth2/authorize?client_id=${readyClient.user?.id}&permissions=${requiredPermissions}&scope=bot%20applications.commands`,\n\t\t);\n\t\tawait this.onReady();\n\t}\n\n\t/**\n\t * Asynchronously retrieves the type of a given channel.\n\t *\n\t * @param {Channel} channel - The channel for which to determine the type.\n\t * @returns {Promise<ChannelType>} A Promise that resolves with the type of the channel.\n\t */\n\tasync getChannelType(channel: Channel): Promise<ChannelType> {\n\t\tswitch (channel.type) {\n\t\t\tcase DiscordChannelType.DM:\n\t\t\t\treturn ChannelType.DM;\n\t\t\tcase DiscordChannelType.GuildText:\n\t\t\t\treturn ChannelType.GROUP;\n\t\t\tcase DiscordChannelType.GuildVoice:\n\t\t\t\treturn ChannelType.VOICE_GROUP;\n\t\t}\n\t}\n\n\t/**\n\t * Handles the addition of a reaction on a message.\n\t *\n\t * @param {MessageReaction} reaction The reaction that was added.\n\t * @param {User} user The user who added the reaction.\n\t * @returns {void}\n\t */\n\tasync handleReactionAdd(reaction: MessageReaction, user: User) {\n\t\ttry {\n\t\t\tlogger.log(\"Reaction added\");\n\n\t\t\t// Early returns\n\t\t\tif (!reaction || !user) {\n\t\t\t\tlogger.warn(\"Invalid reaction or user\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Get emoji info\n\t\t\tlet emoji = reaction.emoji.name;\n\t\t\tif (!emoji && reaction.emoji.id) {\n\t\t\t\temoji = `<:${reaction.emoji.name}:${reaction.emoji.id}>`;\n\t\t\t}\n\n\t\t\t// Fetch full message if partial\n\t\t\tif (reaction.partial) {\n\t\t\t\ttry {\n\t\t\t\t\tawait reaction.fetch();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.error(\"Failed to fetch partial reaction:\", error);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Generate IDs with timestamp to ensure uniqueness\n\t\t\tconst timestamp = Date.now();\n\t\t\tconst roomId = createUniqueUuid(\n\t\t\t\tthis.runtime,\n\t\t\t\treaction.message.channel.id,\n\t\t\t);\n\t\t\tconst entityId = createUniqueUuid(this.runtime, user.id);\n\t\t\tconst reactionUUID = createUniqueUuid(\n\t\t\t\tthis.runtime,\n\t\t\t\t`${reaction.message.id}-${user.id}-${emoji}-${timestamp}`,\n\t\t\t);\n\n\t\t\t// Validate IDs\n\t\t\tif (!entityId || !roomId) {\n\t\t\t\tlogger.error(\"Invalid user ID or room ID\", {\n\t\t\t\t\tentityId,\n\t\t\t\t\troomId,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Process message content\n\t\t\tconst messageContent = reaction.message.content || \"\";\n\t\t\tconst truncatedContent =\n\t\t\t\tmessageContent.length > 50\n\t\t\t\t\t? `${messageContent.substring(0, 50)}...`\n\t\t\t\t\t: messageContent;\n\t\t\tconst reactionMessage = `*Added <${emoji}> to: \"${truncatedContent}\"*`;\n\n\t\t\t// Get user info\n\t\t\tconst userName = reaction.message.author?.username || \"unknown\";\n\t\t\tconst name = reaction.message.author?.displayName || userName;\n\n\t\t\t// TODO: Get the type of the channel\n\n\t\t\tawait this.runtime.ensureConnection({\n\t\t\t\tentityId,\n\t\t\t\troomId,\n\t\t\t\tuserName,\n\t\t\t\tname: name,\n\t\t\t\tsource: \"discord\",\n\t\t\t\tchannelId: reaction.message.channel.id,\n\t\t\t\tserverId: reaction.message.guild?.id,\n\t\t\t\ttype: await this.getChannelType(reaction.message.channel as Channel),\n\t\t\t});\n\n\t\t\tconst inReplyTo = createUniqueUuid(this.runtime, reaction.message.id);\n\n\t\t\tconst memory: Memory = {\n\t\t\t\tid: reactionUUID,\n\t\t\t\tentityId,\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\tcontent: {\n\t\t\t\t\t// name,\n\t\t\t\t\t// userName,\n\t\t\t\t\ttext: reactionMessage,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tinReplyTo,\n\t\t\t\t\tchannelType: await this.getChannelType(\n\t\t\t\t\t\treaction.message.channel as Channel,\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t\troomId,\n\t\t\t\tcreatedAt: timestamp,\n\t\t\t};\n\n\t\t\tconst callback: HandlerCallback = async (content) => {\n\t\t\t\tif (!reaction.message.channel) {\n\t\t\t\t\tlogger.error(\"No channel found for reaction message\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait (reaction.message.channel as TextChannel).send(content.text);\n\t\t\t\treturn [];\n\t\t\t};\n\n\t\t\tthis.runtime.emitEvent(\n\t\t\t\t[\"DISCORD_REACTION_RECEIVED\", \"REACTION_RECEIVED\"],\n\t\t\t\t{\n\t\t\t\t\truntime: this.runtime,\n\t\t\t\t\tmessage: memory,\n\t\t\t\t\tcallback,\n\t\t\t\t},\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error handling reaction:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Handles the removal of a reaction on a message.\n\t *\n\t * @param {MessageReaction} reaction - The reaction that was removed.\n\t * @param {User} user - The user who removed the reaction.\n\t * @returns {Promise<void>} - A Promise that resolves after handling the reaction removal.\n\t */\n\tasync handleReactionRemove(reaction: MessageReaction, user: User) {\n\t\ttry {\n\t\t\tlogger.log(\"Reaction removed\");\n\n\t\t\tlet emoji = reaction.emoji.name;\n\t\t\tif (!emoji && reaction.emoji.id) {\n\t\t\t\temoji = `<:${reaction.emoji.name}:${reaction.emoji.id}>`;\n\t\t\t}\n\n\t\t\t// Fetch the full message if it's a partial\n\t\t\tif (reaction.partial) {\n\t\t\t\ttry {\n\t\t\t\t\tawait reaction.fetch();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t\"Something went wrong when fetching the message:\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst messageContent = reaction.message.content || \"\";\n\t\t\tconst truncatedContent =\n\t\t\t\tmessageContent.length > 50\n\t\t\t\t\t? `${messageContent.substring(0, 50)}...`\n\t\t\t\t\t: messageContent;\n\n\t\t\tconst reactionMessage = `*Removed <${emoji}> from: \"${truncatedContent}\"*`;\n\n\t\t\tconst roomId = createUniqueUuid(\n\t\t\t\tthis.runtime,\n\t\t\t\treaction.message.channel.id,\n\t\t\t);\n\n\t\t\tconst entityId = createUniqueUuid(this.runtime, user.id);\n\t\t\tconst timestamp = Date.now();\n\t\t\tconst reactionUUID = createUniqueUuid(\n\t\t\t\tthis.runtime,\n\t\t\t\t`${reaction.message.id}-${user.id}-${emoji}-${timestamp}`,\n\t\t\t);\n\n\t\t\tconst userName = reaction.message.author?.username || \"unknown\";\n\t\t\tconst name = reaction.message.author?.displayName || userName;\n\n\t\t\tawait this.runtime.ensureConnection({\n\t\t\t\tentityId,\n\t\t\t\troomId,\n\t\t\t\tuserName,\n\t\t\t\tname: name,\n\t\t\t\tsource: \"discord\",\n\t\t\t\tchannelId: reaction.message.channel.id,\n\t\t\t\tserverId: reaction.message.guild?.id,\n\t\t\t\ttype: await this.getChannelType(reaction.message.channel as Channel),\n\t\t\t});\n\n\t\t\tconst memory: Memory = {\n\t\t\t\tid: reactionUUID,\n\t\t\t\tentityId,\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\tcontent: {\n\t\t\t\t\t// name,\n\t\t\t\t\t// userName,\n\t\t\t\t\ttext: reactionMessage,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tinReplyTo: createUniqueUuid(this.runtime, reaction.message.id),\n\t\t\t\t\tchannelType: await this.getChannelType(\n\t\t\t\t\t\treaction.message.channel as Channel,\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t\troomId,\n\t\t\t\tcreatedAt: Date.now(),\n\t\t\t};\n\n\t\t\tconst callback: HandlerCallback = async (content) => {\n\t\t\t\tif (!reaction.message.channel) {\n\t\t\t\t\tlogger.error(\"No channel found for reaction message\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait (reaction.message.channel as TextChannel).send(content.text);\n\t\t\t\treturn [];\n\t\t\t};\n\n\t\t\tthis.runtime.emitEvent([\"DISCORD_REACTION_EVENT\", \"REACTION_RECEIVED\"], {\n\t\t\t\truntime: this.runtime,\n\t\t\t\tmessage: memory,\n\t\t\t\tcallback,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error handling reaction removal:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * 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\t * @param {Guild} guild - The guild that the bot has joined.\n\t * @returns {Promise<void>}\n\t */\n\tprivate async handleGuildCreate(guild: Guild) {\n\t\tlogger.log(`Joined guild ${guild.name}`);\n\t\tconst fullGuild = await guild.fetch();\n\t\tthis.voiceManager.scanGuild(guild);\n\n\t\tconst ownerId = createUniqueUuid(this.runtime, fullGuild.ownerId);\n\n\t\t// Create standardized world data structure\n\t\tconst worldId = createUniqueUuid(this.runtime, fullGuild.id);\n\t\tconst standardizedData = {\n\t\t\truntime: this.runtime,\n\t\t\trooms: await this.buildStandardizedRooms(fullGuild, worldId),\n\t\t\tusers: await this.buildStandardizedUsers(fullGuild),\n\t\t\tworld: {\n\t\t\t\tid: worldId,\n\t\t\t\tname: fullGuild.name,\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\tserverId: fullGuild.id,\n\t\t\t\tmetadata: {\n\t\t\t\t\townership: fullGuild.ownerId ? { ownerId: ownerId } : undefined,\n\t\t\t\t\troles: {\n\t\t\t\t\t\t[ownerId]: Role.OWNER,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t} as World,\n\t\t\tsource: \"discord\",\n\t\t};\n\n\t\t// Emit both Discord-specific and standardized events with the same data structure\n\t\tthis.runtime.emitEvent([\"DISCORD_SERVER_JOINED\"], {\n\t\t\truntime: this.runtime,\n\t\t\tserver: fullGuild,\n\t\t\tsource: \"discord\",\n\t\t});\n\n\t\t// Emit standardized event with the same structure as SERVER_CONNECTED\n\t\tthis.runtime.emitEvent([\"SERVER_JOINED\"], standardizedData);\n\t}\n\n\t/**\n\t * Handles interactions created by the user, specifically commands.\n\t * @param {any} interaction - The interaction object received\n\t * @returns {void}\n\t */\n\tprivate async handleInteractionCreate(interaction: any) {\n\t\tif (!interaction.isCommand()) return;\n\n\t\tswitch (interaction.commandName) {\n\t\t\tcase \"joinchannel\":\n\t\t\t\tawait this.voiceManager.handleJoinChannelCommand(interaction);\n\t\t\t\tbreak;\n\t\t\tcase \"leavechannel\":\n\t\t\t\tawait this.voiceManager.handleLeaveChannelCommand(interaction);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Builds a standardized list of rooms from Discord guild channels\n\t */\n\t/**\n\t * Build standardized rooms for a guild based on text and voice channels.\n\t *\n\t * @param {Guild} guild The guild to build rooms for.\n\t * @param {UUID} _worldId The ID of the world to associate with the rooms.\n\t * @returns {Promise<any[]>} An array of standardized room objects.\n\t */\n\tprivate async buildStandardizedRooms(\n\t\tguild: Guild,\n\t\t_worldId: UUID,\n\t): Promise<any[]> {\n\t\tconst rooms = [];\n\n\t\tfor (const [channelId, channel] of guild.channels.cache) {\n\t\t\t// Only process text and voice channels\n\t\t\tif (\n\t\t\t\tchannel.type === DiscordChannelType.GuildText ||\n\t\t\t\tchannel.type === DiscordChannelType.GuildVoice\n\t\t\t) {\n\t\t\t\tconst roomId = createUniqueUuid(this.runtime, channelId);\n\t\t\t\tlet channelType;\n\n\t\t\t\tswitch (channel.type) {\n\t\t\t\t\tcase DiscordChannelType.GuildText:\n\t\t\t\t\t\tchannelType = ChannelType.GROUP;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase DiscordChannelType.GuildVoice:\n\t\t\t\t\t\tchannelType = ChannelType.VOICE_GROUP;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tchannelType = ChannelType.GROUP;\n\t\t\t\t}\n\n\t\t\t\t// For text channels, we could potentially get member permissions\n\t\t\t\t// But for performance reasons, keep this light for large guilds\n\t\t\t\tlet participants: UUID[] = [];\n\n\t\t\t\tif (\n\t\t\t\t\tguild.memberCount < 1000 &&\n\t\t\t\t\tchannel.type === DiscordChannelType.GuildText\n\t\t\t\t) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Only attempt this for smaller guilds\n\t\t\t\t\t\t// Get members with read permissions for this channel\n\t\t\t\t\t\tparticipants = Array.from(guild.members.cache.values())\n\t\t\t\t\t\t\t.filter((member) =>\n\t\t\t\t\t\t\t\tchannel\n\t\t\t\t\t\t\t\t\t.permissionsFor(member)\n\t\t\t\t\t\t\t\t\t?.has(PermissionsBitField.Flags.ViewChannel),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.map((member) => createUniqueUuid(this.runtime, member.id));\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t`Failed to get participants for channel ${channel.name}:`,\n\t\t\t\t\t\t\terror,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\trooms.push({\n\t\t\t\t\tid: roomId,\n\t\t\t\t\tname: channel.name,\n\t\t\t\t\ttype: channelType,\n\t\t\t\t\tchannelId: channel.id,\n\t\t\t\t\tparticipants,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn rooms;\n\t}\n\n\t/**\n\t * Builds a standardized list of users from Discord guild members\n\t */\n\tprivate async buildStandardizedUsers(guild: Guild): Promise<Entity[]> {\n\t\tconst entities: Entity[] = [];\n\t\tconst botId = this.client?.user?.id;\n\n\t\t// Strategy based on guild size\n\t\tif (guild.memberCount > 1000) {\n\t\t\tlogger.info(\n\t\t\t\t`Using optimized user sync for large guild ${guild.name} (${guild.memberCount} members)`,\n\t\t\t);\n\n\t\t\t// For large guilds, prioritize members already in cache + online members\n\t\t\ttry {\n\t\t\t\t// Use cache first\n\t\t\t\tfor (const [, member] of guild.members.cache) {\n\t\t\t\t\tconst tag = member.user.bot\n\t\t\t\t\t\t? `${member.user.username}#${member.user.discriminator}`\n\t\t\t\t\t\t: member.user.username;\n\n\t\t\t\t\tif (member.id !== botId) {\n\t\t\t\t\t\tentities.push({\n\t\t\t\t\t\t\tid: createUniqueUuid(this.runtime, member.id),\n\t\t\t\t\t\t\tnames: Array.from(\n\t\t\t\t\t\t\t\tnew Set([\n\t\t\t\t\t\t\t\t\tmember.user.username,\n\t\t\t\t\t\t\t\t\tmember.displayName,\n\t\t\t\t\t\t\t\t\tmember.user.globalName,\n\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tdiscord: member.user.globalName\n\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t\t\t\tglobalName: member.user.globalName,\n\t\t\t\t\t\t\t\t\t\t\tuserId: member.id,\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t\t\t\tuserId: member.id,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If cache has very few members, try to get online members\n\t\t\t\tif (entities.length < 100) {\n\t\t\t\t\tlogger.info(`Adding online members for ${guild.name}`);\n\t\t\t\t\t// This is a more targeted fetch that is less likely to hit rate limits\n\t\t\t\t\tconst onlineMembers = await guild.members.fetch({ limit: 100 });\n\n\t\t\t\t\tfor (const [, member] of onlineMembers) {\n\t\t\t\t\t\tif (member.id !== botId) {\n\t\t\t\t\t\t\tconst entityId = createUniqueUuid(this.runtime, member.id);\n\t\t\t\t\t\t\t// Avoid duplicates\n\t\t\t\t\t\t\tif (!entities.some((u) => u.id === entityId)) {\n\t\t\t\t\t\t\t\tconst tag = member.user.bot\n\t\t\t\t\t\t\t\t\t? `${member.user.username}#${member.user.discriminator}`\n\t\t\t\t\t\t\t\t\t: member.user.username;\n\n\t\t\t\t\t\t\t\tentities.push({\n\t\t\t\t\t\t\t\t\tid: entityId,\n\t\t\t\t\t\t\t\t\tnames: Array.from(\n\t\t\t\t\t\t\t\t\t\tnew Set([\n\t\t\t\t\t\t\t\t\t\t\tmember.user.username,\n\t\t\t\t\t\t\t\t\t\t\tmember.displayName,\n\t\t\t\t\t\t\t\t\t\t\tmember.user.globalName,\n\t\t\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tdiscord: member.user.globalName\n\t\t\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t\t\t\t\t\tglobalName: member.user.globalName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tuserId: member.id,\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t\t\t\t\t\tuserId: member.id,\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Error fetching members for ${guild.name}:`, error);\n\t\t\t}\n\t\t} else {\n\t\t\t// For smaller guilds, we can fetch all members\n\t\t\ttry {\n\t\t\t\tlet members = guild.members.cache;\n\t\t\t\tif (members.size === 0) {\n\t\t\t\t\tmembers = await guild.members.fetch();\n\t\t\t\t}\n\n\t\t\t\tfor (const [, member] of members) {\n\t\t\t\t\tif (member.id !== botId) {\n\t\t\t\t\t\tconst tag = member.user.bot\n\t\t\t\t\t\t\t? `${member.user.username}#${member.user.discriminator}`\n\t\t\t\t\t\t\t: member.user.username;\n\n\t\t\t\t\t\tentities.push({\n\t\t\t\t\t\t\tid: createUniqueUuid(this.runtime, member.id),\n\t\t\t\t\t\t\tnames: Array.from(\n\t\t\t\t\t\t\t\tnew Set([\n\t\t\t\t\t\t\t\t\tmember.user.username,\n\t\t\t\t\t\t\t\t\tmember.displayName,\n\t\t\t\t\t\t\t\t\tmember.user.globalName,\n\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tdiscord: member.user.globalName\n\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t\t\t\tglobalName: member.user.globalName,\n\t\t\t\t\t\t\t\t\t\t\tuserId: member.id,\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t\t\t\tuserId: member.id,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Error fetching members for ${guild.name}:`, error);\n\t\t\t}\n\t\t}\n\n\t\treturn entities;\n\t}\n\n\tprivate async onReady() {\n\t\tlogger.log(\"DISCORD ON READY\");\n\t\tconst guilds = await this.client?.guilds.fetch();\n\t\tfor (const [, guild] of guilds) {\n\t\t\tconst fullGuild = await guild.fetch();\n\t\t\tawait this.voiceManager.scanGuild(fullGuild);\n\n\t\t\t// Send after a brief delay\n\t\t\tsetTimeout(async () => {\n\t\t\t\t// For each server the client is in, fire a connected event\n\t\t\t\tconst fullGuild = await guild.fetch();\n\t\t\t\tlogger.log(\"DISCORD SERVER CONNECTED\", fullGuild.name);\n\n\t\t\t\t// Emit Discord-specific event with full guild object\n\t\t\t\tthis.runtime.emitEvent([\"DISCORD_SERVER_CONNECTED\"], {\n\t\t\t\t\truntime: this.runtime,\n\t\t\t\t\tserver: fullGuild,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\n\t\t\t\t// Create platform-agnostic world data structure with simplified structure\n\t\t\t\tconst worldId = createUniqueUuid(this.runtime, fullGuild.id);\n\t\t\t\tconst ownerId = createUniqueUuid(this.runtime, fullGuild.ownerId);\n\n\t\t\t\tconst standardizedData = {\n\t\t\t\t\tname: fullGuild.name,\n\t\t\t\t\truntime: this.runtime,\n\t\t\t\t\trooms: await this.buildStandardizedRooms(fullGuild, worldId),\n\t\t\t\t\tusers: await this.buildStandardizedUsers(fullGuild),\n\t\t\t\t\tworld: {\n\t\t\t\t\t\tid: worldId,\n\t\t\t\t\t\tname: fullGuild.name,\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tserverId: fullGuild.id,\n\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\townership: fullGuild.ownerId ? { ownerId } : undefined,\n\t\t\t\t\t\t\troles: {\n\t\t\t\t\t\t\t\t[ownerId]: Role.OWNER,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t} as World,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t};\n\n\t\t\t\t// Emit standardized event\n\t\t\t\tthis.runtime.emitEvent([\"SERVER_CONNECTED\"], standardizedData);\n\t\t\t}, 1000);\n\t\t}\n\n\t\tthis.client?.emit(\"voiceManagerReady\");\n\t}\n}\n\nconst discordPlugin: Plugin = {\n\tname: \"discord\",\n\tdescription: \"Discord client plugin\",\n\tservices: [DiscordService],\n\tactions: [\n\t\tchatWithAttachments,\n\t\tdownloadMedia,\n\t\tjoinVoice,\n\t\tleaveVoice,\n\t\tsummarize,\n\t\ttranscribe_media,\n\t],\n\tproviders: [channelStateProvider, voiceStateProvider],\n\ttests: [new DiscordTestSuite()],\n\tinit: async (config: Record<string, string>, runtime: IAgentRuntime) => {\n\t\tconst token = runtime.getSetting(\"DISCORD_API_TOKEN\") as string;\n\n\t\tif (!token || token.trim() === \"\") {\n\t\t\tlogger.warn(\n\t\t\t\t\"Discord API Token not provided - Discord plugin is loaded but will not be functional\",\n\t\t\t);\n\t\t\tlogger.info(\n\t\t\t\t\"To enable Discord functionality, please provide DISCORD_API_TOKEN in your .eliza/.env file\",\n\t\t\t);\n\t\t}\n\t},\n};\n\nexport default discordPlugin;\n","import * as fs from \"node:fs\";\nimport {\n\ttype Action,\n\ttype ActionExample,\n\tChannelType,\n\ttype Content,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelTypes,\n\ttype State,\n\tcomposePrompt,\n\tparseJSONObjectFromText,\n\ttrimTokens,\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\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n): Promise<{ objective: string; attachmentIds: string[] } | null> => {\n\tconst prompt = composePrompt({\n\t\tstate,\n\t\ttemplate: attachmentIdsTemplate,\n\t});\n\n\tfor (let i = 0; i < 5; i++) {\n\t\tconst response = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\t\tconsole.log(\"response\", response);\n\t\t// try parsing to a json object\n\t\tconst parsedResponse = parseJSONObjectFromText(response) as {\n\t\t\tobjective: string;\n\t\t\tattachmentIds: string[];\n\t\t} | null;\n\t\t// see if it contains objective and attachmentIds\n\t\tif (parsedResponse?.objective && parsedResponse?.attachmentIds) {\n\t\t\treturn parsedResponse;\n\t\t}\n\t}\n\treturn 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\nconst summarizeAction = {\n\tname: \"CHAT_WITH_ATTACHMENTS\",\n\tsimiles: [\n\t\t\"CHAT_WITH_ATTACHMENT\",\n\t\t\"SUMMARIZE_FILES\",\n\t\t\"SUMMARIZE_FILE\",\n\t\t\"SUMMARIZE_ATACHMENT\",\n\t\t\"CHAT_WITH_PDF\",\n\t\t\"ATTACHMENT_SUMMARY\",\n\t\t\"RECAP_ATTACHMENTS\",\n\t\t\"SUMMARIZE_FILE\",\n\t\t\"SUMMARIZE_VIDEO\",\n\t\t\"SUMMARIZE_AUDIO\",\n\t\t\"SUMMARIZE_IMAGE\",\n\t\t\"SUMMARIZE_DOCUMENT\",\n\t\t\"SUMMARIZE_LINK\",\n\t\t\"ATTACHMENT_SUMMARY\",\n\t\t\"FILE_SUMMARY\",\n\t],\n\tdescription:\n\t\t\"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\tvalidate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n\t\tconst room = await _runtime.getDatabaseAdapter().getRoom(message.roomId);\n\t\tif (room?.type !== ChannelType.GROUP) {\n\t\t\treturn false;\n\t\t}\n\t\t// only show if one of the keywords are in the message\n\t\tconst keywords: string[] = [\n\t\t\t\"attachment\",\n\t\t\t\"summary\",\n\t\t\t\"summarize\",\n\t\t\t\"research\",\n\t\t\t\"pdf\",\n\t\t\t\"video\",\n\t\t\t\"audio\",\n\t\t\t\"image\",\n\t\t\t\"document\",\n\t\t\t\"link\",\n\t\t\t\"file\",\n\t\t\t\"attachment\",\n\t\t\t\"summarize\",\n\t\t\t\"code\",\n\t\t\t\"report\",\n\t\t\t\"write\",\n\t\t\t\"details\",\n\t\t\t\"information\",\n\t\t\t\"talk\",\n\t\t\t\"chat\",\n\t\t\t\"read\",\n\t\t\t\"listen\",\n\t\t\t\"watch\",\n\t\t];\n\t\treturn keywords.some((keyword) =>\n\t\t\tmessage.content.text.toLowerCase().includes(keyword.toLowerCase()),\n\t\t);\n\t},\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\tstate: State,\n\t\t_options: any,\n\t\tcallback: HandlerCallback,\n\t) => {\n\t\tconst callbackData: Content = {\n\t\t\ttext: \"\", // fill in later\n\t\t\tactions: [\"CHAT_WITH_ATTACHMENTS_RESPONSE\"],\n\t\t\tsource: message.content.source,\n\t\t\tattachments: [],\n\t\t};\n\n\t\t// 1. extract attachment IDs from the message\n\t\tconst attachmentData = await getAttachmentIds(runtime, message, state);\n\t\tif (!attachmentData) {\n\t\t\tconsole.error(\"Couldn't get attachment IDs from message\");\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: message.content.source,\n\t\t\t\t\tthought:\n\t\t\t\t\t\t\"I tried to chat with attachments but I couldn't get attachment IDs\",\n\t\t\t\t\tactions: [\"CHAT_WITH_ATTACHMENTS_FAILED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"CHAT_WITH_ATTACHMENTS\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst { objective, attachmentIds } = attachmentData;\n\n\t\t// This is pretty gross but it can catch cases where the returned generated UUID is stupidly wrong for some reason\n\t\tconst attachments = state.data.recentMessages\n\t\t\t.filter(\n\t\t\t\t(msg) => msg.content.attachments && msg.content.attachments.length > 0,\n\t\t\t)\n\t\t\t.flatMap((msg) => msg.content.attachments)\n\t\t\t// check by first 5 characters of uuid\n\t\t\t.filter(\n\t\t\t\t(attachment) =>\n\t\t\t\t\tattachmentIds\n\t\t\t\t\t\t.map((attch) => attch.toLowerCase().slice(0, 5))\n\t\t\t\t\t\t.includes(attachment.id.toLowerCase().slice(0, 5)) ||\n\t\t\t\t\t// or check the other way\n\t\t\t\t\tattachmentIds.some((id) => {\n\t\t\t\t\t\tconst attachmentId = id.toLowerCase().slice(0, 5);\n\t\t\t\t\t\treturn attachment.id.toLowerCase().includes(attachmentId);\n\t\t\t\t\t}),\n\t\t\t);\n\n\t\tconst attachmentsWithText = attachments\n\t\t\t.map((attachment) => `# ${attachment.title}\\n${attachment.text}`)\n\t\t\t.join(\"\\n\\n\");\n\n\t\tlet currentSummary = \"\";\n\n\t\tconst chunkSize = 8192;\n\n\t\tstate.values.attachmentsWithText = attachmentsWithText;\n\t\tstate.values.objective = objective;\n\t\tconst template = await trimTokens(\n\t\t\tsummarizationTemplate,\n\t\t\tchunkSize,\n\t\t\truntime,\n\t\t);\n\t\tconst prompt = composePrompt({\n\t\t\tstate,\n\t\t\t// make sure it fits, we can pad the tokens a bit\n\t\t\t// Get the model's tokenizer based on the current model being used\n\t\t\ttemplate,\n\t\t});\n\n\t\tconst summary = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\n\t\tcurrentSummary = `${currentSummary}\\n${summary}`;\n\n\t\tif (!currentSummary) {\n\t\t\tconsole.error(\"No summary found, that's not good!\");\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: message.content.source,\n\t\t\t\t\tthought:\n\t\t\t\t\t\t\"I tried to chat with attachments but I couldn't get a summary\",\n\t\t\t\t\tactions: [\"CHAT_WITH_ATTACHMENTS_FAILED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"CHAT_WITH_ATTACHMENTS\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcallbackData.text = currentSummary.trim();\n\t\tif (\n\t\t\tcallbackData.text &&\n\t\t\t(currentSummary.trim()?.split(\"\\n\").length < 4 ||\n\t\t\t\tcurrentSummary.trim()?.split(\" \").length < 100)\n\t\t) {\n\t\t\tcallbackData.text = `Here is the summary:\n\\`\\`\\`md\n${currentSummary.trim()}\n\\`\\`\\`\n`;\n\t\t\tawait callback(callbackData);\n\t\t} else if (currentSummary.trim()) {\n\t\t\tconst summaryDir = \"cache\";\n\t\t\tconst summaryFilename = `${summaryDir}/summary_${Date.now()}.md`;\n\t\t\ttry {\n\t\t\t\tawait fs.promises.mkdir(summaryDir, { recursive: true });\n\t\t\t\t// Debug: Log before file operations\n\t\t\t\tconsole.log(\"Creating summary file:\", {\n\t\t\t\t\tfilename: summaryFilename,\n\t\t\t\t\tsummaryLength: currentSummary.length,\n\t\t\t\t});\n\n\t\t\t\t// Write file directly first\n\t\t\t\tawait fs.promises.writeFile(summaryFilename, currentSummary, \"utf8\");\n\t\t\t\tconsole.log(\"File written successfully\");\n\n\t\t\t\t// Then cache it\n\t\t\t\tawait runtime\n\t\t\t\t\t.getDatabaseAdapter()\n\t\t\t\t\t.setCache<string>(summaryFilename, currentSummary);\n\t\t\t\tconsole.log(\"Cache set operation completed\");\n\n\t\t\t\tawait callback(\n\t\t\t\t\t{\n\t\t\t\t\t\t...callbackData,\n\t\t\t\t\t\ttext: `I've attached the summary of the requested attachments as a text file.`,\n\t\t\t\t\t},\n\t\t\t\t\t[summaryFilename],\n\t\t\t\t);\n\t\t\t\tconsole.log(\"Callback completed with summary file\");\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Error in file/cache process:\", error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t} else {\n\t\t\tconsole.warn(\n\t\t\t\t\"Empty response from chat with attachments action, skipping\",\n\t\t\t);\n\t\t}\n\n\t\treturn callbackData;\n\t},\n\texamples: [\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Can you summarize the attachments b3e23, c4f67, and d5a89?\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Sure thing! I'll pull up those specific attachments and provide a summary of their content.\",\n\t\t\t\t\tactions: [\"CHAT_WITH_ATTACHMENTS\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"I need a technical summary of the PDFs I sent earlier - a1b2c3.pdf, d4e5f6.pdf, and g7h8i9.pdf\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"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\t\t\t\t\tactions: [\"CHAT_WITH_ATTACHMENTS\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"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\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"sure, no problem.\",\n\t\t\t\t\tactions: [\"CHAT_WITH_ATTACHMENTS\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"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\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"great idea, give me a minute\",\n\t\t\t\t\tactions: [\"CHAT_WITH_ATTACHMENTS\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t] as ActionExample[][],\n} as Action;\n\nexport default summarizeAction;\n","import {\n\ttype Action,\n\ttype ActionExample,\n\ttype Content,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype IVideoService,\n\ttype Memory,\n\tModelTypes,\n\tServiceTypes,\n\ttype State,\n\tcomposePrompt,\n\tparseJSONObjectFromText,\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\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n): Promise<string | null> => {\n\tconst prompt = composePrompt({\n\t\tstate,\n\t\ttemplate: mediaUrlTemplate,\n\t});\n\n\tfor (let i = 0; i < 5; i++) {\n\t\tconst response = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\n\t\tconst parsedResponse = parseJSONObjectFromText(response) as {\n\t\t\tmediaUrl: string;\n\t\t} | null;\n\n\t\tif (parsedResponse?.mediaUrl) {\n\t\t\treturn parsedResponse.mediaUrl;\n\t\t}\n\t}\n\treturn null;\n};\n\nexport default {\n\tname: \"DOWNLOAD_MEDIA\",\n\tsimiles: [\n\t\t\"DOWNLOAD_VIDEO\",\n\t\t\"DOWNLOAD_AUDIO\",\n\t\t\"GET_MEDIA\",\n\t\t\"DOWNLOAD_PODCAST\",\n\t\t\"DOWNLOAD_YOUTUBE\",\n\t],\n\tdescription:\n\t\t\"Downloads a video or audio file from a URL and attaches it to the response message.\",\n\tvalidate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n\t\tif (message.content.source !== \"discord\") {\n\t\t\treturn false;\n\t\t}\n\t},\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\tstate: State,\n\t\t_options: any,\n\t\tcallback: HandlerCallback,\n\t) => {\n\t\tconst videoService = runtime.getService<IVideoService>(ServiceTypes.VIDEO);\n\n\t\tconst mediaUrl = await getMediaUrl(runtime, message, state);\n\t\tif (!mediaUrl) {\n\t\t\tconsole.error(\"Couldn't get media URL from messages\");\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought: `I couldn't find the media URL in the message`,\n\t\t\t\t\tactions: [\"DOWNLOAD_MEDIA_FAILED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"DOWNLOAD_MEDIA\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst videoInfo = await videoService.fetchVideoInfo(mediaUrl);\n\t\tconst mediaPath = await videoService.downloadVideo(videoInfo);\n\n\t\tconst response: Content = {\n\t\t\ttext: `I downloaded the video \"${videoInfo.title}\" and attached it below.`,\n\t\t\tactions: [\"DOWNLOAD_MEDIA_RESPONSE\"],\n\t\t\tsource: message.content.source,\n\t\t\tattachments: [],\n\t\t};\n\n\t\tconst maxRetries = 3;\n\t\tlet retries = 0;\n\n\t\twhile (retries < maxRetries) {\n\t\t\ttry {\n\t\t\t\tawait callback(\n\t\t\t\t\t{\n\t\t\t\t\t\t...response,\n\t\t\t\t\t},\n\t\t\t\t\t[mediaPath],\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t} catch (error) {\n\t\t\t\tretries++;\n\t\t\t\tconsole.error(`Error sending message (attempt ${retries}):`, error);\n\n\t\t\t\tif (retries === maxRetries) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"Max retries reached. Failed to send message with attachment.\",\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// Wait for a short delay before retrying\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 2000));\n\t\t\t}\n\t\t}\n\n\t\treturn response;\n\t},\n\texamples: [\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"https://www.youtube.com/watch?v=dQw4w9WgXcQ\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Downloading the YouTube video now, one sec\",\n\t\t\t\t\tactions: [\"DOWNLOAD_MEDIA\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Can you grab this video for me? https://vimeo.com/123456789\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Sure thing, I'll download that Vimeo video for you\",\n\t\t\t\t\tactions: [\"DOWNLOAD_MEDIA\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"I need this video downloaded: https://www.youtube.com/watch?v=abcdefg\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"No problem, I'm on it. I'll have that YouTube video downloaded in a jiffy\",\n\t\t\t\t\tactions: [\"DOWNLOAD_MEDIA\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t] as ActionExample[][],\n} as Action;\n","import * as fs from \"node:fs\";\nimport {\n\ttype Action,\n\ttype ActionExample,\n\ttype Content,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype Media,\n\ttype Memory,\n\tModelTypes,\n\ttype State,\n\tcomposePrompt,\n\tgetEntityDetails,\n\tparseJSONObjectFromText,\n\tsplitChunks,\n\ttrimTokens,\n} from \"@elizaos/core\";\nexport const summarizationTemplate = `# Summarized so far (we are adding to this)\n{{currentSummary}}\n\n# Current conversation chunk we are summarizing (includes attachments)\n{{memoriesWithAttachments}}\n\nSummarization objective: {{objective}}\n\n# Instructions: Summarize the conversation so far. Return the summary. Do not acknowledge this request, just summarize and continue the existing summary if there is one. Capture any important details to the objective. Only respond with the new summary text.\nYour response should be extremely detailed and include any and all relevant information.`;\n\n/**\n * Template for providing instructions and details on how to summarize conversation messages and determine the range of dates requested.\n * The template includes placeholders for recent messages, sender name, objective, start and end date range.\n * The response is expected to be formatted as a JSON block with specific structure.\n * @type {string}\n */\nexport const dateRangeTemplate = `# Messages we are summarizing (the conversation is continued after this)\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting a summary of the conversation. Your goal is to determine their objective, along with the range of dates that their request covers.\nThe \"objective\" is a detailed description of what the user wants to summarize based on the conversation. If they just ask for a general summary, you can either base it off the conversation if the summary range is very recent, or set the object to be general, like \"a detailed summary of the conversation between all users\".\nThe \"start\" and \"end\" are the range of dates that the user wants to summarize, relative to the current time. The start and end should be relative to the current time, and measured in seconds, minutes, hours and days. The format is \"2 days ago\" or \"3 hours ago\" or \"4 minutes ago\" or \"5 seconds ago\", i.e. \"<integer> <unit> ago\".\nIf you aren't sure, you can use a default range of \"0 minutes ago\" to \"2 hours ago\" or more. Better to err on the side of including too much than too little.\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"objective\": \"<What the user wants to summarize>\",\n \"start\": \"0 minutes ago\",\n \"end\": \"2 hours ago\"\n}\n\\`\\`\\`\n`;\n\n/**\n * Function to get a date range from user input.\n *\n * @param {IAgentRuntime} runtime - The Agent Runtime object.\n * @param {Memory} _message - The Memory object.\n * @param {State} state - The State object.\n * @return {Promise<{ objective: string; start: string | number; end: string | number; } | null>} Parsed user input containing objective, start, and end timestamps, or null.\n */\nconst getDateRange = async (\n\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n) => {\n\tconst prompt = composePrompt({\n\t\tstate,\n\t\ttemplate: dateRangeTemplate,\n\t});\n\n\tfor (let i = 0; i < 5; i++) {\n\t\tconst response = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\t\tconsole.log(\"response\", response);\n\t\t// try parsing to a json object\n\t\tconst parsedResponse = parseJSONObjectFromText(response) as {\n\t\t\tobjective: string;\n\t\t\tstart: string | number;\n\t\t\tend: string | number;\n\t\t} | null;\n\t\t// see if it contains objective, start and end\n\t\tif (parsedResponse) {\n\t\t\tif (\n\t\t\t\tparsedResponse.objective &&\n\t\t\t\tparsedResponse.start &&\n\t\t\t\tparsedResponse.end\n\t\t\t) {\n\t\t\t\t// TODO: parse start and end into timestamps\n\t\t\t\tconst startIntegerString = (parsedResponse.start as string).match(\n\t\t\t\t\t/\\d+/,\n\t\t\t\t)?.[0];\n\t\t\t\tconst endIntegerString = (parsedResponse.end as string).match(\n\t\t\t\t\t/\\d+/,\n\t\t\t\t)?.[0];\n\n\t\t\t\t// parse multiplier\n\t\t\t\tconst multipliers = {\n\t\t\t\t\tsecond: 1 * 1000,\n\t\t\t\t\tminute: 60 * 1000,\n\t\t\t\t\thour: 3600 * 1000,\n\t\t\t\t\tday: 86400 * 1000,\n\t\t\t\t};\n\n\t\t\t\tconst startMultiplier = (parsedResponse.start as string).match(\n\t\t\t\t\t/second|minute|hour|day/,\n\t\t\t\t)?.[0];\n\t\t\t\tconst endMultiplier = (parsedResponse.end as string).match(\n\t\t\t\t\t/second|minute|hour|day/,\n\t\t\t\t)?.[0];\n\n\t\t\t\tconst startInteger = startIntegerString\n\t\t\t\t\t? Number.parseInt(startIntegerString)\n\t\t\t\t\t: 0;\n\t\t\t\tconst endInteger = endIntegerString\n\t\t\t\t\t? Number.parseInt(endIntegerString)\n\t\t\t\t\t: 0;\n\n\t\t\t\t// multiply by multiplier\n\t\t\t\tconst startTime =\n\t\t\t\t\tstartInteger *\n\t\t\t\t\tmultipliers[startMultiplier as keyof typeof multipliers];\n\n\t\t\t\tconsole.log(\"startTime\", startTime);\n\n\t\t\t\tconst endTime =\n\t\t\t\t\tendInteger * multipliers[endMultiplier as keyof typeof multipliers];\n\n\t\t\t\tconsole.log(\"endTime\", endTime);\n\n\t\t\t\t// get the current time and subtract the start and end times\n\t\t\t\tparsedResponse.start = Date.now() - startTime;\n\t\t\t\tparsedResponse.end = Date.now() - endTime;\n\n\t\t\t\treturn parsedResponse;\n\t\t\t}\n\t\t}\n\t}\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 */\nconst summarizeAction = {\n\tname: \"SUMMARIZE_CONVERSATION\",\n\tsimiles: [\n\t\t\"RECAP\",\n\t\t\"RECAP_CONVERSATION\",\n\t\t\"SUMMARIZE_CHAT\",\n\t\t\"SUMMARIZATION\",\n\t\t\"CHAT_SUMMARY\",\n\t\t\"CONVERSATION_SUMMARY\",\n\t],\n\tdescription: \"Summarizes the conversation and attachments.\",\n\tvalidate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n\t\tif (message.content.source !== \"discord\") {\n\t\t\treturn false;\n\t\t}\n\t\t// only show if one of the keywords are in the message\n\t\tconst keywords: string[] = [\n\t\t\t\"summarize\",\n\t\t\t\"summarization\",\n\t\t\t\"summary\",\n\t\t\t\"recap\",\n\t\t\t\"report\",\n\t\t\t\"overview\",\n\t\t\t\"review\",\n\t\t\t\"rundown\",\n\t\t\t\"wrap-up\",\n\t\t\t\"brief\",\n\t\t\t\"debrief\",\n\t\t\t\"abstract\",\n\t\t\t\"synopsis\",\n\t\t\t\"outline\",\n\t\t\t\"digest\",\n\t\t\t\"abridgment\",\n\t\t\t\"condensation\",\n\t\t\t\"encapsulation\",\n\t\t\t\"essence\",\n\t\t\t\"gist\",\n\t\t\t\"main points\",\n\t\t\t\"key points\",\n\t\t\t\"key takeaways\",\n\t\t\t\"bulletpoint\",\n\t\t\t\"highlights\",\n\t\t\t\"tldr\",\n\t\t\t\"tl;dr\",\n\t\t\t\"in a nutshell\",\n\t\t\t\"bottom line\",\n\t\t\t\"long story short\",\n\t\t\t\"sum up\",\n\t\t\t\"sum it up\",\n\t\t\t\"short version\",\n\t\t\t\"bring me up to speed\",\n\t\t\t\"catch me up\",\n\t\t];\n\t\treturn keywords.some((keyword) =>\n\t\t\tmessage.content.text.toLowerCase().includes(keyword.toLowerCase()),\n\t\t);\n\t},\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\tstate: State,\n\t\t_options: any,\n\t\tcallback: HandlerCallback,\n\t) => {\n\t\tconst callbackData: Content = {\n\t\t\ttext: \"\", // fill in later\n\t\t\tactions: [\"SUMMARIZATION_RESPONSE\"],\n\t\t\tsource: message.content.source,\n\t\t\tattachments: [],\n\t\t};\n\t\tconst { roomId } = message;\n\n\t\t// 1. extract date range from the message\n\t\tconst dateRange = await getDateRange(runtime, message, state);\n\t\tif (!dateRange) {\n\t\t\tconsole.error(\"Couldn't get date range from message\");\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought: `I couldn't get the date range from the message`,\n\t\t\t\t\tactions: [\"SUMMARIZE_CONVERSATION_FAILED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"SUMMARIZE_CONVERSATION\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst { objective, start, end } = dateRange;\n\n\t\t// 2. get these memories from the database\n\t\tconst memories = await runtime.getMemoryManager(\"messages\").getMemories({\n\t\t\troomId,\n\t\t\t// subtract start from current time\n\t\t\tstart: Number.parseInt(start as string),\n\t\t\tend: Number.parseInt(end as string),\n\t\t\tcount: 10000,\n\t\t\tunique: false,\n\t\t});\n\n\t\tconst entities = await getEntityDetails({\n\t\t\truntime: runtime as IAgentRuntime,\n\t\t\troomId,\n\t\t});\n\n\t\tconst actorMap = new Map(entities.map((entity) => [entity.id, entity]));\n\n\t\tconst formattedMemories = memories\n\t\t\t.map((memory) => {\n\t\t\t\tconst attachments = memory.content.attachments\n\t\t\t\t\t?.map((attachment: Media) => {\n\t\t\t\t\t\treturn `---\\nAttachment: ${attachment.id}\\n${attachment.description}\\n${attachment.text}\\n---`;\n\t\t\t\t\t})\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\treturn `${actorMap.get(memory.entityId)?.name ?? \"Unknown User\"} (${actorMap.get(memory.entityId)?.username ?? \"\"}): ${memory.content.text}\\n${attachments}`;\n\t\t\t})\n\t\t\t.join(\"\\n\");\n\n\t\tlet currentSummary = \"\";\n\n\t\tconst chunkSize = 8000;\n\n\t\tconst chunks = await splitChunks(formattedMemories, chunkSize, 0);\n\n\t\tconst _datestr = new Date().toUTCString().replace(/:/g, \"-\");\n\n\t\tstate.values.memoriesWithAttachments = formattedMemories;\n\t\tstate.values.objective = objective;\n\n\t\tfor (let i = 0; i < chunks.length; i++) {\n\t\t\tconst chunk = chunks[i];\n\t\t\tstate.values.currentSummary = currentSummary;\n\t\t\tstate.values.currentChunk = chunk;\n\t\t\tconst template = await trimTokens(\n\t\t\t\tsummarizationTemplate,\n\t\t\t\tchunkSize + 500,\n\t\t\t\truntime,\n\t\t\t);\n\t\t\tconst prompt = composePrompt({\n\t\t\t\tstate,\n\t\t\t\t// make sure it fits, we can pad the tokens a bit\n\t\t\t\ttemplate,\n\t\t\t});\n\n\t\t\tconst summary = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\t\t\tprompt,\n\t\t\t});\n\n\t\t\tcurrentSummary = `${currentSummary}\\n${summary}`;\n\t\t}\n\n\t\tif (!currentSummary) {\n\t\t\tconsole.error(\"No summary found, that's not good!\");\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought: `I couldn't summarize the conversation`,\n\t\t\t\t\tactions: [\"SUMMARIZE_CONVERSATION_FAILED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"SUMMARIZE_CONVERSATION\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcallbackData.text = currentSummary.trim();\n\t\tif (\n\t\t\tcallbackData.text &&\n\t\t\t(currentSummary.trim()?.split(\"\\n\").length < 4 ||\n\t\t\t\tcurrentSummary.trim()?.split(\" \").length < 100)\n\t\t) {\n\t\t\tcallbackData.text = `Here is the summary:\n\\`\\`\\`md\n${currentSummary.trim()}\n\\`\\`\\`\n`;\n\t\t\tawait callback(callbackData);\n\t\t} else if (currentSummary.trim()) {\n\t\t\tconst summaryDir = \"cache\";\n\t\t\tconst summaryFilename = `${summaryDir}/conversation_summary_${Date.now()}`;\n\t\t\tawait runtime\n\t\t\t\t.getDatabaseAdapter()\n\t\t\t\t.setCache<string>(summaryFilename, currentSummary);\n\t\t\tawait fs.promises.mkdir(summaryDir, { recursive: true });\n\n\t\t\tawait fs.promises.writeFile(summaryFilename, currentSummary, \"utf8\");\n\t\t\t// save the summary to a file\n\t\t\tawait callback(\n\t\t\t\t{\n\t\t\t\t\t...callbackData,\n\t\t\t\t\ttext: `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\t\t\t\t},\n\t\t\t\t[summaryFilename],\n\t\t\t);\n\t\t} else {\n\t\t\tconsole.warn(\n\t\t\t\t\"Empty response from summarize conversation action, skipping\",\n\t\t\t);\n\t\t}\n\n\t\treturn callbackData;\n\t},\n\texamples: [\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"```js\\nconst x = 10\\n```\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"can you give me a detailed report on what we're talking about?\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"sure, no problem, give me a minute to get that together for you\",\n\t\t\t\t\tactions: [\"SUMMARIZE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"please summarize the conversation we just had and include this blogpost i'm linking (Attachment: b3e12)\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"sure, give me a sec\",\n\t\t\t\t\tactions: [\"SUMMARIZE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Can you summarize what moon and avf are talking about?\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Yeah, just hold on a second while I get that together for you...\",\n\t\t\t\t\tactions: [\"SUMMARIZE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"i need to write a blog post about farming, can you summarize the discussion from a few hours ago?\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"no problem, give me a few minutes to read through everything\",\n\t\t\t\t\tactions: [\"SUMMARIZE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t] as ActionExample[][],\n} as Action;\n\nexport default summarizeAction;\n","import {\n\ttype Action,\n\ttype ActionExample,\n\ttype Content,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelTypes,\n\ttype State,\n\tcomposePrompt,\n\tcreateUniqueUuid,\n\tparseJSONObjectFromText,\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\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n): Promise<string | null> => {\n\tconst prompt = composePrompt({\n\t\tstate,\n\t\ttemplate: mediaAttachmentIdTemplate,\n\t});\n\n\tfor (let i = 0; i < 5; i++) {\n\t\tconst response = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\t\tconsole.log(\"response\", response);\n\n\t\tconst parsedResponse = parseJSONObjectFromText(response) as {\n\t\t\tattachmentId: string;\n\t\t} | null;\n\n\t\tif (parsedResponse?.attachmentId) {\n\t\t\treturn parsedResponse.attachmentId;\n\t\t}\n\t}\n\treturn 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 */\nconst transcribeMediaAction = {\n\tname: \"TRANSCRIBE_MEDIA\",\n\tsimiles: [\n\t\t\"TRANSCRIBE_AUDIO\",\n\t\t\"TRANSCRIBE_VIDEO\",\n\t\t\"MEDIA_TRANSCRIPT\",\n\t\t\"VIDEO_TRANSCRIPT\",\n\t\t\"AUDIO_TRANSCRIPT\",\n\t],\n\tdescription:\n\t\t\"Transcribe the full text of an audio or video file that the user has attached.\",\n\tvalidate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n\t\tif (message.content.source !== \"discord\") {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst keywords: string[] = [\n\t\t\t\"transcribe\",\n\t\t\t\"transcript\",\n\t\t\t\"audio\",\n\t\t\t\"video\",\n\t\t\t\"media\",\n\t\t\t\"youtube\",\n\t\t\t\"meeting\",\n\t\t\t\"recording\",\n\t\t\t\"podcast\",\n\t\t\t\"call\",\n\t\t\t\"conference\",\n\t\t\t\"interview\",\n\t\t\t\"speech\",\n\t\t\t\"lecture\",\n\t\t\t\"presentation\",\n\t\t];\n\t\treturn keywords.some((keyword) =>\n\t\t\tmessage.content.text.toLowerCase().includes(keyword.toLowerCase()),\n\t\t);\n\t},\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\tstate: State,\n\t\t_options: any,\n\t\tcallback: HandlerCallback,\n\t) => {\n\t\tconst callbackData: Content = {\n\t\t\ttext: \"\", // fill in later\n\t\t\tactions: [\"TRANSCRIBE_MEDIA_RESPONSE\"],\n\t\t\tsource: message.content.source,\n\t\t\tattachments: [],\n\t\t};\n\n\t\tconst attachmentId = await getMediaAttachmentId(runtime, message, state);\n\t\tif (!attachmentId) {\n\t\t\tconsole.error(\"Couldn't get media attachment ID from message\");\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought: `I couldn't find the media attachment ID in the message`,\n\t\t\t\t\tactions: [\"TRANSCRIBE_MEDIA_FAILED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"TRANSCRIBE_MEDIA\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst attachment = state.data.recentMessages\n\t\t\t.filter(\n\t\t\t\t(msg) => msg.content.attachments && msg.content.attachments.length > 0,\n\t\t\t)\n\t\t\t.flatMap((msg) => msg.content.attachments)\n\t\t\t.find(\n\t\t\t\t(attachment) =>\n\t\t\t\t\tattachment.id.toLowerCase() === attachmentId.toLowerCase(),\n\t\t\t);\n\n\t\tif (!attachment) {\n\t\t\tconsole.error(`Couldn't find attachment with ID ${attachmentId}`);\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought: `I couldn't find the media attachment with ID ${attachmentId}`,\n\t\t\t\t\tactions: [\"TRANSCRIBE_MEDIA_FAILED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"TRANSCRIBE_MEDIA\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst mediaTranscript = attachment.text;\n\n\t\tcallbackData.text = mediaTranscript.trim();\n\n\t\t// if callbackData.text is < 4 lines or < 100 words, then we we callback with normal message wrapped in markdown block\n\t\tif (\n\t\t\tcallbackData.text &&\n\t\t\t(callbackData.text?.split(\"\\n\").length < 4 ||\n\t\t\t\tcallbackData.text?.split(\" \").length < 100)\n\t\t) {\n\t\t\tcallbackData.text = `Here is the transcript:\n\\`\\`\\`md\n${mediaTranscript.trim()}\n\\`\\`\\`\n`;\n\t\t\tawait callback(callbackData);\n\t\t}\n\t\t// if text is big, let's send as an attachment\n\t\telse if (callbackData.text) {\n\t\t\tconst transcriptFilename = `content/transcript_${Date.now()}`;\n\n\t\t\t// save the transcript to a file\n\t\t\tawait runtime\n\t\t\t\t.getDatabaseAdapter()\n\t\t\t\t.setCache<string>(transcriptFilename, callbackData.text);\n\n\t\t\tawait callback(\n\t\t\t\t{\n\t\t\t\t\t...callbackData,\n\t\t\t\t\ttext: `I've attached the transcript as a text file.`,\n\t\t\t\t},\n\t\t\t\t[transcriptFilename],\n\t\t\t);\n\t\t} else {\n\t\t\tconsole.warn(\"Empty response from transcribe media action, skipping\");\n\t\t}\n\n\t\treturn callbackData;\n\t},\n\texamples: [\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Please transcribe the audio file I just sent.\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Sure, I'll transcribe the full audio for you.\",\n\t\t\t\t\tactions: [\"TRANSCRIBE_MEDIA\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Can I get a transcript of that video recording?\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Absolutely, give me a moment to generate the full transcript of the video.\",\n\t\t\t\t\tactions: [\"TRANSCRIBE_MEDIA\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t] as ActionExample[][],\n} as Action;\n\nexport default transcribeMediaAction;\n","// src/actions/joinVoice\nimport {\n\ttype Action,\n\ttype ActionExample,\n\tChannelType,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelTypes,\n\ttype State,\n\tcomposePrompt,\n\tcreateUniqueUuid,\n\tlogger,\n} from \"@elizaos/core\";\nimport {\n\ttype BaseGuildVoiceChannel,\n\ttype Channel,\n\tChannelType as DiscordChannelType,\n\ttype Guild,\n} from \"discord.js\";\nimport type { DiscordService } from \"../index\";\nimport { ServiceTypes } from \"../types\";\nimport type { VoiceManager } from \"../voice\";\n\nexport default {\n\tname: \"JOIN_VOICE\",\n\tsimiles: [\n\t\t\"JOIN_VOICE\",\n\t\t\"JOIN_VC\",\n\t\t\"JOIN_VOICE_CHAT\",\n\t\t\"JOIN_VOICE_CHANNEL\",\n\t\t\"JOIN_MEETING\",\n\t\t\"JOIN_CALL\",\n\t],\n\tvalidate: async (runtime: IAgentRuntime, message: Memory, state: State) => {\n\t\tif (message.content.source !== \"discord\") {\n\t\t\t// not a discord message\n\t\t\treturn false;\n\t\t}\n\n\t\tconst room =\n\t\t\tstate.data.room ??\n\t\t\t(await runtime.getDatabaseAdapter().getRoom(message.roomId));\n\n\t\tif (room?.type !== ChannelType.GROUP) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst client = runtime.getService(ServiceTypes.DISCORD);\n\n\t\tif (!client) {\n\t\t\tlogger.error(\"Discord client not found\");\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t},\n\tdescription: \"Join a voice channel to participate in voice chat.\",\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\tstate: State,\n\t\t_options: any,\n\t\tcallback: HandlerCallback,\n\t): Promise<boolean> => {\n\t\tconst room =\n\t\t\tstate.data.room ??\n\t\t\t(await runtime.getDatabaseAdapter().getRoom(message.roomId));\n\t\tif (!room) {\n\t\t\tthrow new Error(\"No room found\");\n\t\t}\n\n\t\tif (room.type !== ChannelType.GROUP) {\n\t\t\t// only handle in a group scenario for now\n\t\t\treturn false;\n\t\t}\n\n\t\tconsole.log(\"Running handler on provider\", room.name);\n\n\t\tconst serverId = room.serverId;\n\n\t\tif (!serverId) {\n\t\t\tthrow new Error(\"No server ID found\");\n\t\t}\n\n\t\tconst discordClient = runtime.getService(\n\t\t\tServiceTypes.DISCORD,\n\t\t) as DiscordService;\n\t\tconst client = discordClient.client;\n\t\tconst voiceManager = discordClient.voiceManager as VoiceManager;\n\n\t\tif (!client) {\n\t\t\tlogger.error(\"Discord client not found\");\n\t\t\treturn false;\n\t\t}\n\n\t\tconst voiceChannels = (\n\t\t\tclient.guilds.cache.get(serverId) as Guild\n\t\t).channels.cache.filter(\n\t\t\t(channel: Channel) => channel.type === DiscordChannelType.GuildVoice,\n\t\t);\n\n\t\tconst targetChannel = voiceChannels.find((channel) => {\n\t\t\tconst name = (channel as { name: string }).name.toLowerCase();\n\t\t\tconst messageContent = message?.content?.text;\n\t\t\t// remove all non-alphanumeric characters (keep spaces between words)\n\t\t\tconst replacedName = name.replace(/[^a-z0-9 ]/g, \"\");\n\n\t\t\treturn (\n\t\t\t\tname.includes(messageContent) ||\n\t\t\t\tmessageContent.includes(name) ||\n\t\t\t\treplacedName.includes(messageContent) ||\n\t\t\t\tmessageContent.includes(replacedName)\n\t\t\t);\n\t\t});\n\n\t\tif (targetChannel) {\n\t\t\tvoiceManager.joinChannel(targetChannel as BaseGuildVoiceChannel);\n\t\t\treturn true;\n\t\t}\n\t\tconst guild = client.guilds.cache.get(serverId);\n\t\tconst members = guild?.members.cache;\n\n\t\t// get the member who's stringTouuid(id) === message userId\n\t\tconst member = members?.find(\n\t\t\t(member) => createUniqueUuid(runtime, member.id) === message.entityId,\n\t\t);\n\n\t\tif (member?.voice?.channel) {\n\t\t\tvoiceManager.joinChannel(member?.voice?.channel as BaseGuildVoiceChannel);\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought: `I joined the voice channel ${member?.voice?.channel?.name}`,\n\t\t\t\t\tactions: [\"JOIN_VOICE_STARTED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"JOIN_VOICE\",\n\t\t\t\t},\n\t\t\t});\n\n\t\t\t// save a memory for the new channel as well\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: createUniqueUuid(runtime, targetChannel.id),\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought: `I joined the voice channel ${targetChannel.name}`,\n\t\t\t\t\tactions: [\"JOIN_VOICE_STARTED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"JOIN_VOICE\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn true;\n\t\t}\n\n\t\tconst messageTemplate = `\nThe user has requested to join a voice channel.\nHere is the list of channels available in the server:\n{{voiceChannels}}\n\nHere is the user's request:\n{{userMessage}}\n\nPlease respond with the name of the voice channel which the bot should join. Try to infer what channel the user is talking about. If the user didn't specify a voice channel, respond with \"none\".\nYou should only respond with the name of the voice channel or none, no commentary or additional information should be included.\n`;\n\n\t\tconst guessState = {\n\t\t\tuserMessage: message.content.text,\n\t\t\tvoiceChannels: voiceChannels\n\t\t\t\t.map((channel) => (channel as { name: string }).name)\n\t\t\t\t.join(\"\\n\"),\n\t\t};\n\n\t\tconst prompt = composePrompt({\n\t\t\ttemplate: messageTemplate,\n\t\t\tstate: guessState as unknown as State,\n\t\t});\n\n\t\tconst responseContent = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\n\t\tif (responseContent && responseContent.trim().length > 0) {\n\t\t\t// join the voice channel\n\t\t\tconst channelName = responseContent.toLowerCase();\n\n\t\t\tconst targetChannel = voiceChannels.find((channel) => {\n\t\t\t\tconst name = (channel as { name: string }).name.toLowerCase();\n\n\t\t\t\t// remove all non-alphanumeric characters (keep spaces between words)\n\t\t\t\tconst replacedName = name.replace(/[^a-z0-9 ]/g, \"\");\n\n\t\t\t\treturn (\n\t\t\t\t\tname.includes(channelName) ||\n\t\t\t\t\tchannelName.includes(name) ||\n\t\t\t\t\treplacedName.includes(channelName) ||\n\t\t\t\t\tchannelName.includes(replacedName)\n\t\t\t\t);\n\t\t\t});\n\n\t\t\tif (targetChannel) {\n\t\t\t\tvoiceManager.joinChannel(targetChannel as BaseGuildVoiceChannel);\n\t\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\t\tentityId: message.entityId,\n\t\t\t\t\tagentId: message.agentId,\n\t\t\t\t\troomId: message.roomId,\n\t\t\t\t\tcontent: {\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\tthought: `I joined the voice channel ${member?.voice?.channel?.name}`,\n\t\t\t\t\t\tactions: [\"JOIN_VOICE_STARTED\"],\n\t\t\t\t\t},\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\ttype: \"JOIN_VOICE\",\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\t// save a memory for the new channel as well\n\t\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\t\tentityId: message.entityId,\n\t\t\t\t\tagentId: message.agentId,\n\t\t\t\t\troomId: createUniqueUuid(runtime, targetChannel.id),\n\t\t\t\t\tcontent: {\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\tthought: `I joined the voice channel ${targetChannel.name}`,\n\t\t\t\t\t\tactions: [\"JOIN_VOICE_STARTED\"],\n\t\t\t\t\t},\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\ttype: \"JOIN_VOICE\",\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tawait callback({\n\t\t\ttext: \"I couldn't figure out which channel you wanted me to join.\",\n\t\t\tsource: \"discord\",\n\t\t});\n\t\treturn false;\n\t},\n\texamples: [\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Hey, let's jump into the 'General' voice and chat\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Sounds good\",\n\t\t\t\t\tactions: [\"JOIN_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"{{name2}}, can you join the vc, I want to discuss our strat\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Sure I'll join right now\",\n\t\t\t\t\tactions: [\"JOIN_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"hey {{name2}}, we're having a team meeting in the 'conference' voice channel, plz join us\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"OK see you there\",\n\t\t\t\t\tactions: [\"JOIN_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"{{name2}}, let's have a quick voice chat in the 'Lounge' channel.\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"kk be there in a sec\",\n\t\t\t\t\tactions: [\"JOIN_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Hey {{name2}}, can you join me in the 'Music' voice channel\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Sure\",\n\t\t\t\t\tactions: [\"JOIN_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"join voice chat with us {{name2}}\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"coming\",\n\t\t\t\t\tactions: [\"JOIN_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"hop in vc {{name2}}\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"joining now\",\n\t\t\t\t\tactions: [\"JOIN_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"get in vc with us {{name2}}\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"im in\",\n\t\t\t\t\tactions: [\"JOIN_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t] as ActionExample[][],\n} as Action;\n","import type { Character, IAgentRuntime } from \"@elizaos/core\";\nimport type { Client } from \"discord.js\";\n\n/**\n * Interface representing a Discord service.\n *\n * @typedef {Object} IDiscordService\n * @property {Client} client - The Discord client object.\n * @property {Character} character - The character object.\n */\nexport interface IDiscordService {\n\tclient: Client;\n\tcharacter: Character;\n}\n\nexport const ServiceTypes = {\n\tDISCORD: \"discord\",\n} as const;\n","// src/actions/leaveVoice\nimport {\n\ttype Action,\n\ttype ActionExample,\n\tChannelType,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype Memory,\n\ttype State,\n\tcreateUniqueUuid,\n\tlogger,\n} from \"@elizaos/core\";\nimport { BaseGuildVoiceChannel } from \"discord.js\";\n\nimport type { DiscordService } from \"../index\";\nimport { ServiceTypes } from \"../types\";\nimport type { VoiceManager } from \"../voice\";\n\nexport default {\n\tname: \"LEAVE_VOICE\",\n\tsimiles: [\n\t\t\"LEAVE_VOICE\",\n\t\t\"LEAVE_VC\",\n\t\t\"LEAVE_VOICE_CHAT\",\n\t\t\"LEAVE_VOICE_CHANNEL\",\n\t\t\"LEAVE_MEETING\",\n\t\t\"LEAVE_CALL\",\n\t],\n\tvalidate: async (runtime: IAgentRuntime, message: Memory, state: State) => {\n\t\tif (message.content.source !== \"discord\") {\n\t\t\t// not a discord message\n\t\t\treturn false;\n\t\t}\n\n\t\tconst service = runtime.getService(ServiceTypes.DISCORD) as DiscordService;\n\n\t\tif (!service) {\n\t\t\tlogger.error(\"Discord client not found\");\n\t\t\treturn false;\n\t\t}\n\n\t\tconst room =\n\t\t\tstate.data.room ??\n\t\t\t(await runtime.getDatabaseAdapter().getRoom(message.roomId));\n\n\t\tif (room?.type !== ChannelType.GROUP) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check if the client is connected to any voice channel\n\t\tconst isConnectedToVoice = service.client.voice.adapters.size > 0;\n\n\t\treturn isConnectedToVoice;\n\t},\n\tdescription: \"Leave the current voice channel.\",\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\t_state: State,\n\t\t_options: any,\n\t): Promise<boolean> => {\n\t\tconst room = await runtime.getDatabaseAdapter().getRoom(message.roomId);\n\t\tif (!room) {\n\t\t\tthrow new Error(\"No room found\");\n\t\t}\n\n\t\tif (room.type !== ChannelType.GROUP) {\n\t\t\t// only handle in a group scenario for now\n\t\t\tthrow new Error(\"Not a group\");\n\t\t}\n\n\t\tconst serverId = room.serverId;\n\n\t\tif (!serverId) {\n\t\t\tthrow new Error(\"No server ID found 9\");\n\t\t}\n\t\tconst discordClient = runtime.getService(\n\t\t\tServiceTypes.DISCORD,\n\t\t) as DiscordService;\n\t\tconst voiceManager = discordClient.voiceManager as VoiceManager;\n\t\tconst client = discordClient.client;\n\n\t\tif (!client) {\n\t\t\tlogger.error(\"Discord client not found\");\n\t\t\tthrow new Error(\"Discord client not found\");\n\t\t}\n\n\t\tif (!voiceManager) {\n\t\t\tlogger.error(\"voiceManager is not available.\");\n\t\t\tthrow new Error(\"voiceManager is not available.\");\n\t\t}\n\n\t\tconst guild = client.guilds.cache.get(serverId);\n\n\t\tif (!guild) {\n\t\t\tconsole.warn(\"Bot is not in any voice channel.\");\n\t\t\t// create a memory with thought to self to self\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought:\n\t\t\t\t\t\t\"I tried to leave the voice channel but I'm not in any voice channel.\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"LEAVE_VOICE\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\n\t\tconst voiceChannel = guild.members.me?.voice.channel;\n\n\t\tif (!voiceChannel || !(voiceChannel instanceof BaseGuildVoiceChannel)) {\n\t\t\tconsole.warn(\"Could not retrieve the voice channel.\");\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought: \"I tried to leave the voice channel but I couldn't find it.\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"LEAVE_VOICE\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\n\t\tconst connection = voiceManager.getVoiceConnection(guild.id);\n\t\tif (!connection) {\n\t\t\tconsole.warn(\"No active voice connection found for the bot.\");\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought:\n\t\t\t\t\t\t\"I tried to leave the voice channel but I couldn't find the connection.\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"LEAVE_VOICE\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\n\t\tvoiceManager.leaveChannel(voiceChannel);\n\t\t// save a memory for the new channel as well\n\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\tentityId: message.entityId,\n\t\t\tagentId: message.agentId,\n\t\t\troomId: createUniqueUuid(runtime, voiceChannel.id),\n\t\t\tcontent: {\n\t\t\t\tsource: \"discord\",\n\t\t\t\tthought: `I left the voice channel ${voiceChannel.name}`,\n\t\t\t\tactions: [\"LEAVE_VOICE_STARTED\"],\n\t\t\t},\n\t\t\tmetadata: {\n\t\t\t\ttype: \"LEAVE_VOICE\",\n\t\t\t},\n\t\t});\n\n\t\treturn true;\n\t},\n\texamples: [\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Hey {{name2}} please leave the voice channel\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Sure\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"I have to go now but thanks for the chat\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"You too, talk to you later\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Great call everyone, hopping off now\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Agreed, I'll hop off too\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Hey {{name2}} I need you to step away from the voice chat for a bit\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"No worries, I'll leave the voice channel\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"{{name2}}, I think we covered everything, you can leave the voice chat now\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Sounds good, see you both later\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"leave voice {{name2}}\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"ok leaving\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"plz leave the voice chat {{name2}}\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"aight im out\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"yo {{name2}} gtfo the vc\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"sorry, talk to you later\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t] as ActionExample[][],\n} as Action;\n","export const MESSAGE_CONSTANTS = {\n\tMAX_MESSAGES: 10,\n\tRECENT_MESSAGE_COUNT: 3,\n\tCHAT_HISTORY_COUNT: 5,\n\tINTEREST_DECAY_TIME: 5 * 60 * 1000, // 5 minutes\n\tPARTIAL_INTEREST_DECAY: 3 * 60 * 1000, // 3 minutes\n\tDEFAULT_SIMILARITY_THRESHOLD: 0.3,\n\tDEFAULT_SIMILARITY_THRESHOLD_FOLLOW_UPS: 0.2,\n} as const;\n\nexport const MESSAGE_LENGTH_THRESHOLDS = {\n\tLOSE_INTEREST: 100,\n\tSHORT_MESSAGE: 10,\n\tVERY_SHORT_MESSAGE: 2,\n\tIGNORE_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\t\"shut up\",\n\t\"stop\",\n\t\"please shut up\",\n\t\"shut up please\",\n\t\"dont talk\",\n\t\"silence\",\n\t\"stop talking\",\n\t\"be quiet\",\n\t\"hush\",\n\t\"wtf\",\n\t\"chill\",\n\t\"stfu\",\n\t\"stupid bot\",\n\t\"dumb bot\",\n\t\"stop responding\",\n\t\"god damn it\",\n\t\"god damn\",\n\t\"goddamnit\",\n\t\"can you not\",\n\t\"can you stop\",\n\t\"be quiet\",\n\t\"hate you\",\n\t\"hate this\",\n\t\"fuck up\",\n] as const;\n\nexport const IGNORE_RESPONSE_WORDS = [\n\t\"lol\",\n\t\"nm\",\n\t\"uh\",\n\t\"wtf\",\n\t\"stfu\",\n\t\"dumb\",\n\t\"jfc\",\n\t\"omg\",\n] as const;\n\nexport const DISCORD_SERVICE_NAME = \"discord\";\n","import {\n\tChannelType,\n\ttype Content,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype IBrowserService,\n\ttype IVideoService,\n\ttype Media,\n\ttype Memory,\n\tServiceTypes,\n\tcreateUniqueUuid,\n\tlogger,\n} from \"@elizaos/core\";\nimport {\n\ttype Channel,\n\ttype Client,\n\tChannelType as DiscordChannelType,\n\ttype Message as DiscordMessage,\n\ttype TextChannel,\n} from \"discord.js\";\nimport { AttachmentManager } from \"./attachments\";\nimport { canSendMessage, sendMessageInChunks } from \"./utils\";\n\n/**\n * Class representing a Message Manager for handling Discord messages.\n */\n\nexport class MessageManager {\n\tprivate client: Client;\n\tprivate runtime: IAgentRuntime;\n\tprivate attachmentManager: AttachmentManager;\n\tprivate getChannelType: (channel: Channel) => Promise<ChannelType>;\n\t/**\n\t * Constructor for a new instance of MyClass.\n\t * @param {any} discordClient - The Discord client object.\n\t */\n\tconstructor(discordClient: any) {\n\t\tthis.client = discordClient.client;\n\t\tthis.runtime = discordClient.runtime;\n\t\tthis.attachmentManager = new AttachmentManager(this.runtime);\n\t\tthis.getChannelType = discordClient.getChannelType;\n\t}\n\n\t/**\n\t * Handles incoming Discord messages and processes them accordingly.\n\t *\n\t * @param {DiscordMessage} message - The Discord message to be handled\n\t */\n\tasync handleMessage(message: DiscordMessage) {\n\t\tif (\n\t\t\tthis.runtime.character.settings?.discord?.allowedChannelIds &&\n\t\t\t!this.runtime.character.settings.discord.allowedChannelIds.some(\n\t\t\t\t(id: string) => id === message.channel.id,\n\t\t\t)\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.interaction || message.author.id === this.client.user?.id) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\tthis.runtime.character.settings?.discord?.shouldIgnoreBotMessages &&\n\t\t\tmessage.author?.bot\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\tthis.runtime.character.settings?.discord?.shouldIgnoreDirectMessages &&\n\t\t\tmessage.channel.type === DiscordChannelType.DM\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst entityId = createUniqueUuid(this.runtime, message.author.id);\n\n\t\tconst userName = message.author.bot\n\t\t\t? `${message.author.username}#${message.author.discriminator}`\n\t\t\t: message.author.username;\n\t\tconst name = message.author.displayName;\n\t\tconst channelId = message.channel.id;\n\t\tconst roomId = createUniqueUuid(this.runtime, channelId);\n\n\t\tlet type: ChannelType;\n\t\tlet serverId: string | undefined;\n\n\t\tif (message.guild) {\n\t\t\tconst guild = await message.guild.fetch();\n\t\t\ttype = await this.getChannelType(message.channel as Channel);\n\t\t\tserverId = guild.id;\n\t\t} else {\n\t\t\ttype = ChannelType.DM;\n\t\t\tserverId = undefined;\n\t\t}\n\n\t\tawait this.runtime.ensureConnection({\n\t\t\tentityId: entityId,\n\t\t\troomId,\n\t\t\tuserName,\n\t\t\tname: name,\n\t\t\tsource: \"discord\",\n\t\t\tchannelId: message.channel.id,\n\t\t\tserverId,\n\t\t\ttype,\n\t\t});\n\n\t\ttry {\n\t\t\tconst canSendResult = canSendMessage(message.channel);\n\t\t\tif (!canSendResult.canSend) {\n\t\t\t\treturn logger.warn(\n\t\t\t\t\t`Cannot send message to channel ${message.channel}`,\n\t\t\t\t\tcanSendResult,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst { processedContent, attachments } =\n\t\t\t\tawait this.processMessage(message);\n\n\t\t\tconst audioAttachments = message.attachments.filter((attachment) =>\n\t\t\t\tattachment.contentType?.startsWith(\"audio/\"),\n\t\t\t);\n\n\t\t\tif (audioAttachments.size > 0) {\n\t\t\t\tconst processedAudioAttachments =\n\t\t\t\t\tawait this.attachmentManager.processAttachments(audioAttachments);\n\t\t\t\tattachments.push(...processedAudioAttachments);\n\t\t\t}\n\n\t\t\tif (!processedContent && !attachments?.length) {\n\t\t\t\t// Only process messages that are not empty\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst entityId = createUniqueUuid(this.runtime, message.author.id);\n\n\t\t\tconst messageId = createUniqueUuid(this.runtime, message.id);\n\n\t\t\tconst newMessage: Memory = {\n\t\t\t\tid: messageId,\n\t\t\t\tentityId: entityId,\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\troomId: roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\t// name: name,\n\t\t\t\t\t// userName: userName,\n\t\t\t\t\ttext: processedContent || \" \",\n\t\t\t\t\tattachments: attachments,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\turl: message.url,\n\t\t\t\t\tinReplyTo: message.reference?.messageId\n\t\t\t\t\t\t? createUniqueUuid(this.runtime, message.reference?.messageId)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t},\n\t\t\t\tcreatedAt: message.createdTimestamp,\n\t\t\t};\n\n\t\t\tconst callback: HandlerCallback = async (\n\t\t\t\tcontent: Content,\n\t\t\t\tfiles: any[],\n\t\t\t) => {\n\t\t\t\ttry {\n\t\t\t\t\tif (message.id && !content.inReplyTo) {\n\t\t\t\t\t\tcontent.inReplyTo = createUniqueUuid(this.runtime, message.id);\n\t\t\t\t\t}\n\t\t\t\t\tconst messages = await sendMessageInChunks(\n\t\t\t\t\t\tmessage.channel as TextChannel,\n\t\t\t\t\t\tcontent.text,\n\t\t\t\t\t\tmessage.id,\n\t\t\t\t\t\tfiles,\n\t\t\t\t\t);\n\n\t\t\t\t\tconst memories: Memory[] = [];\n\t\t\t\t\tfor (const m of messages) {\n\t\t\t\t\t\tconst actions = content.actions;\n\n\t\t\t\t\t\tconst memory: Memory = {\n\t\t\t\t\t\t\tid: createUniqueUuid(this.runtime, m.id),\n\t\t\t\t\t\t\tentityId: this.runtime.agentId,\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t...content,\n\t\t\t\t\t\t\t\tactions,\n\t\t\t\t\t\t\t\tinReplyTo: messageId,\n\t\t\t\t\t\t\t\turl: m.url,\n\t\t\t\t\t\t\t\tchannelType: type,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\troomId,\n\t\t\t\t\t\t\tcreatedAt: m.createdTimestamp,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tmemories.push(memory);\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const m of memories) {\n\t\t\t\t\t\tawait this.runtime.getMemoryManager(\"messages\").createMemory(m);\n\t\t\t\t\t}\n\t\t\t\t\treturn memories;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(\"Error sending message:\", error);\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tthis.runtime.emitEvent([\"DISCORD_MESSAGE_RECEIVED\", \"MESSAGE_RECEIVED\"], {\n\t\t\t\truntime: this.runtime,\n\t\t\t\tmessage: newMessage,\n\t\t\t\tcallback,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error handling message:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Processes the message content, mentions, code blocks, attachments, and URLs to generate\n\t * processed content and media attachments.\n\t *\n\t * @param {DiscordMessage} message The message to process\n\t * @returns {Promise<{ processedContent: string; attachments: Media[] }>} Processed content and media attachments\n\t */\n\tasync processMessage(\n\t\tmessage: DiscordMessage,\n\t): Promise<{ processedContent: string; attachments: Media[] }> {\n\t\tlet processedContent = message.content;\n\t\tlet attachments: Media[] = [];\n\n\t\tconst mentionRegex = /<@!?(\\d+)>/g;\n\t\tprocessedContent = processedContent.replace(\n\t\t\tmentionRegex,\n\t\t\t(match, entityId) => {\n\t\t\t\tconst user = message.mentions.users.get(entityId);\n\t\t\t\tif (user) {\n\t\t\t\t\treturn `${user.username} (@${entityId})`;\n\t\t\t\t}\n\t\t\t\treturn match;\n\t\t\t},\n\t\t);\n\n\t\tconst codeBlockRegex = /```([\\s\\S]*?)```/g;\n\t\tlet match;\n\t\twhile ((match = codeBlockRegex.exec(processedContent))) {\n\t\t\tconst codeBlock = match[1];\n\t\t\tconst lines = codeBlock.split(\"\\n\");\n\t\t\tconst title = lines[0];\n\t\t\tconst description = lines.slice(0, 3).join(\"\\n\");\n\t\t\tconst attachmentId = `code-${Date.now()}-${Math.floor(\n\t\t\t\tMath.random() * 1000,\n\t\t\t)}`.slice(-5);\n\t\t\tattachments.push({\n\t\t\t\tid: attachmentId,\n\t\t\t\turl: \"\",\n\t\t\t\ttitle: title || \"Code Block\",\n\t\t\t\tsource: \"Code\",\n\t\t\t\tdescription: description,\n\t\t\t\ttext: codeBlock,\n\t\t\t});\n\t\t\tprocessedContent = processedContent.replace(\n\t\t\t\tmatch[0],\n\t\t\t\t`Code Block (${attachmentId})`,\n\t\t\t);\n\t\t}\n\n\t\tif (message.attachments.size > 0) {\n\t\t\tattachments = await this.attachmentManager.processAttachments(\n\t\t\t\tmessage.attachments,\n\t\t\t);\n\t\t}\n\n\t\tconst urlRegex = /(https?:\\/\\/[^\\s]+)/g;\n\t\tconst urls = processedContent.match(urlRegex) || [];\n\n\t\tfor (const url of urls) {\n\t\t\tif (\n\t\t\t\tthis.runtime\n\t\t\t\t\t.getService<IVideoService>(ServiceTypes.VIDEO)\n\t\t\t\t\t?.isVideoUrl(url)\n\t\t\t) {\n\t\t\t\tconst videoService = this.runtime.getService<IVideoService>(\n\t\t\t\t\tServiceTypes.VIDEO,\n\t\t\t\t);\n\t\t\t\tif (!videoService) {\n\t\t\t\t\tthrow new Error(\"Video service not found\");\n\t\t\t\t}\n\t\t\t\tconst videoInfo = await videoService.processVideo(url, this.runtime);\n\n\t\t\t\tattachments.push({\n\t\t\t\t\tid: `youtube-${Date.now()}`,\n\t\t\t\t\turl: url,\n\t\t\t\t\ttitle: videoInfo.title,\n\t\t\t\t\tsource: \"YouTube\",\n\t\t\t\t\tdescription: videoInfo.description,\n\t\t\t\t\ttext: videoInfo.text,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tconst browserService = this.runtime.getService<IBrowserService>(\n\t\t\t\t\tServiceTypes.BROWSER,\n\t\t\t\t);\n\t\t\t\tif (!browserService) {\n\t\t\t\t\tthrow new Error(\"Browser service not found\");\n\t\t\t\t}\n\n\t\t\t\tconst { title, description: summary } =\n\t\t\t\t\tawait browserService.getPageContent(url, this.runtime);\n\n\t\t\t\tattachments.push({\n\t\t\t\t\tid: `webpage-${Date.now()}`,\n\t\t\t\t\turl: url,\n\t\t\t\t\ttitle: title || \"Web Page\",\n\t\t\t\t\tsource: \"Web\",\n\t\t\t\t\tdescription: summary,\n\t\t\t\t\ttext: summary,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn { processedContent, attachments };\n\t}\n\n\t/**\n\t * Asynchronously fetches the bot's username and discriminator from Discord API.\n\t *\n\t * @param {string} botToken The token of the bot to authenticate the request\n\t * @returns {Promise<string>} A promise that resolves with the bot's username and discriminator\n\t * @throws {Error} If there is an error while fetching the bot details\n\t */\n\n\tasync fetchBotName(botToken: string) {\n\t\tconst url = \"https://discord.com/api/v10/users/@me\";\n\t\tconst response = await fetch(url, {\n\t\t\tmethod: \"GET\",\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bot ${botToken}`,\n\t\t\t},\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Error fetching bot details: ${response.statusText}`);\n\t\t}\n\n\t\tconst data = await response.json();\n\t\tconst discriminator = data.discriminator;\n\t\treturn (\n\t\t\t(data as { username: string }).username +\n\t\t\t(discriminator ? `#${discriminator}` : \"\")\n\t\t);\n\t}\n}\n","import fs from \"node:fs\";\nimport { trimTokens } from \"@elizaos/core\";\nimport { parseJSONObjectFromText } from \"@elizaos/core\";\nimport {\n\ttype IAgentRuntime,\n\ttype IPdfService,\n\ttype IVideoService,\n\ttype Media,\n\tModelTypes,\n\tServiceTypes,\n} from \"@elizaos/core\";\nimport { type Attachment, Collection } from \"discord.js\";\nimport ffmpeg from \"fluent-ffmpeg\";\n\n/**\n * Generates a summary for the provided text using a specified model.\n *\n * @param {IAgentRuntime} runtime - The runtime environment for the agent.\n * @param {string} text - The text to generate a summary for.\n * @returns {Promise<{ title: string; description: string }>} An object containing the generated title and description.\n */\n\nasync function generateSummary(\n\truntime: IAgentRuntime,\n\ttext: string,\n): Promise<{ title: string; description: string }> {\n\t// make sure text is under 128k characters\n\ttext = await trimTokens(text, 100000, runtime);\n\n\tconst 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\tconst response = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\tprompt,\n\t});\n\n\tconst parsedResponse = parseJSONObjectFromText(response);\n\n\tif (parsedResponse?.title && parsedResponse?.summary) {\n\t\treturn {\n\t\t\ttitle: parsedResponse.title,\n\t\t\tdescription: parsedResponse.summary,\n\t\t};\n\t}\n\n\treturn {\n\t\ttitle: \"\",\n\t\tdescription: \"\",\n\t};\n}\n\n/**\n * Class representing an Attachment Manager.\n */\nexport class AttachmentManager {\n\tprivate attachmentCache: Map<string, Media> = new Map();\n\tprivate runtime: IAgentRuntime;\n\n\t/**\n\t * Constructor for creating a new instance of the class.\n\t *\n\t * @param {IAgentRuntime} runtime The runtime object to be injected into the instance.\n\t */\n\tconstructor(runtime: IAgentRuntime) {\n\t\tthis.runtime = runtime;\n\t}\n\n\t/**\n\t * Processes attachments and returns an array of Media objects.\n\t * @param {Collection<string, Attachment> | Attachment[]} attachments - The attachments to be processed\n\t * @returns {Promise<Media[]>} - An array of processed Media objects\n\t */\n\tasync processAttachments(\n\t\tattachments: Collection<string, Attachment> | Attachment[],\n\t): Promise<Media[]> {\n\t\tconst processedAttachments: Media[] = [];\n\t\tconst attachmentCollection =\n\t\t\tattachments instanceof Collection\n\t\t\t\t? attachments\n\t\t\t\t: new Collection(attachments.map((att) => [att.id, att]));\n\n\t\tfor (const [, attachment] of attachmentCollection) {\n\t\t\tconst media = await this.processAttachment(attachment);\n\t\t\tif (media) {\n\t\t\t\tprocessedAttachments.push(media);\n\t\t\t}\n\t\t}\n\n\t\treturn processedAttachments;\n\t}\n\n\t/**\n\t * Processes the provided attachment to generate a media object.\n\t * If the media for the attachment URL is already cached, it will return the cached media.\n\t * Otherwise, it will determine the type of attachment (PDF, text, audio, video, image, generic)\n\t * and call the corresponding processing method to generate the media object.\n\t *\n\t * @param attachment The attachment to process\n\t * @returns A promise that resolves to a Media object representing the attachment, or null if the attachment could not be processed\n\t */\n\tasync processAttachment(attachment: Attachment): Promise<Media | null> {\n\t\tif (this.attachmentCache.has(attachment.url)) {\n\t\t\treturn this.attachmentCache.get(attachment.url)!;\n\t\t}\n\n\t\tlet media: Media | null = null;\n\t\tif (attachment.contentType?.startsWith(\"application/pdf\")) {\n\t\t\tmedia = await this.processPdfAttachment(attachment);\n\t\t} else if (attachment.contentType?.startsWith(\"text/plain\")) {\n\t\t\tmedia = await this.processPlaintextAttachment(attachment);\n\t\t} else if (\n\t\t\tattachment.contentType?.startsWith(\"audio/\") ||\n\t\t\tattachment.contentType?.startsWith(\"video/mp4\")\n\t\t) {\n\t\t\tmedia = await this.processAudioVideoAttachment(attachment);\n\t\t} else if (attachment.contentType?.startsWith(\"image/\")) {\n\t\t\tmedia = await this.processImageAttachment(attachment);\n\t\t} else if (\n\t\t\tattachment.contentType?.startsWith(\"video/\") ||\n\t\t\tthis.runtime\n\t\t\t\t.getService<IVideoService>(ServiceTypes.VIDEO)\n\t\t\t\t.isVideoUrl(attachment.url)\n\t\t) {\n\t\t\tmedia = await this.processVideoAttachment(attachment);\n\t\t} else {\n\t\t\tmedia = await this.processGenericAttachment(attachment);\n\t\t}\n\n\t\tif (media) {\n\t\t\tthis.attachmentCache.set(attachment.url, media);\n\t\t}\n\t\treturn media;\n\t}\n\n\t/**\n\t * Asynchronously processes an audio or video attachment provided as input and returns a Media object.\n\t * @param {Attachment} attachment - The attachment object containing information about the audio/video file.\n\t * @returns {Promise<Media>} A Promise that resolves to a Media object representing the processed audio/video attachment.\n\t */\n\tprivate async processAudioVideoAttachment(\n\t\tattachment: Attachment,\n\t): Promise<Media> {\n\t\ttry {\n\t\t\tconst response = await fetch(attachment.url);\n\t\t\tconst audioVideoArrayBuffer = await response.arrayBuffer();\n\n\t\t\tlet audioBuffer: Buffer;\n\t\t\tif (attachment.contentType?.startsWith(\"audio/\")) {\n\t\t\t\taudioBuffer = Buffer.from(audioVideoArrayBuffer);\n\t\t\t} else if (attachment.contentType?.startsWith(\"video/mp4\")) {\n\t\t\t\taudioBuffer = await this.extractAudioFromMP4(audioVideoArrayBuffer);\n\t\t\t} else {\n\t\t\t\tthrow new Error(\"Unsupported audio/video format\");\n\t\t\t}\n\n\t\t\tconst transcription = await this.runtime.useModel(\n\t\t\t\tModelTypes.TRANSCRIPTION,\n\t\t\t\taudioBuffer,\n\t\t\t);\n\t\t\tconst { title, description } = await generateSummary(\n\t\t\t\tthis.runtime,\n\t\t\t\ttranscription,\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: title || \"Audio/Video Attachment\",\n\t\t\t\tsource: attachment.contentType?.startsWith(\"audio/\")\n\t\t\t\t\t? \"Audio\"\n\t\t\t\t\t: \"Video\",\n\t\t\t\tdescription:\n\t\t\t\t\tdescription ||\n\t\t\t\t\t\"User-uploaded audio/video attachment which has been transcribed\",\n\t\t\t\ttext: transcription || \"Audio/video content not available\",\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconsole.error(\n\t\t\t\t`Error processing audio/video attachment: ${error.message}`,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: \"Audio/Video Attachment\",\n\t\t\t\tsource: attachment.contentType?.startsWith(\"audio/\")\n\t\t\t\t\t? \"Audio\"\n\t\t\t\t\t: \"Video\",\n\t\t\t\tdescription: \"An audio/video attachment (transcription failed)\",\n\t\t\t\ttext: `This is an audio/video attachment. File name: ${attachment.name}, Size: ${attachment.size} bytes, Content type: ${attachment.contentType}`,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Extracts the audio stream from the provided MP4 data and converts it to MP3 format.\n\t *\n\t * @param {ArrayBuffer} mp4Data - The MP4 data to extract audio from\n\t * @returns {Promise<Buffer>} - A Promise that resolves with the converted audio data as a Buffer\n\t */\n\tprivate async extractAudioFromMP4(mp4Data: ArrayBuffer): Promise<Buffer> {\n\t\t// Use a library like 'fluent-ffmpeg' or 'ffmpeg-static' to extract the audio stream from the MP4 data\n\t\t// and convert it to MP3 or WAV format\n\t\t// Example using fluent-ffmpeg:\n\t\tconst tempMP4File = `temp_${Date.now()}.mp4`;\n\t\tconst tempAudioFile = `temp_${Date.now()}.mp3`;\n\n\t\ttry {\n\t\t\t// Write the MP4 data to a temporary file\n\t\t\tfs.writeFileSync(tempMP4File, Buffer.from(mp4Data));\n\n\t\t\t// Extract the audio stream and convert it to MP3\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tffmpeg(tempMP4File)\n\t\t\t\t\t.outputOptions(\"-vn\") // Disable video output\n\t\t\t\t\t.audioCodec(\"libmp3lame\") // Set audio codec to MP3\n\t\t\t\t\t.save(tempAudioFile) // Save the output to the specified file\n\t\t\t\t\t.on(\"end\", () => {\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t})\n\t\t\t\t\t.on(\"error\", (err) => {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t})\n\t\t\t\t\t.run();\n\t\t\t});\n\n\t\t\t// Read the converted audio file and return it as a Buffer\n\t\t\tconst audioData = fs.readFileSync(tempAudioFile);\n\t\t\treturn audioData;\n\t\t} finally {\n\t\t\t// Clean up the temporary files\n\t\t\tif (fs.existsSync(tempMP4File)) {\n\t\t\t\tfs.unlinkSync(tempMP4File);\n\t\t\t}\n\t\t\tif (fs.existsSync(tempAudioFile)) {\n\t\t\t\tfs.unlinkSync(tempAudioFile);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Processes a PDF attachment by fetching the PDF file from the specified URL,\n\t * converting it to text, generating a summary, and returning a Media object\n\t * with the extracted information.\n\t * If an error occurs during processing, a placeholder Media object is returned\n\t * with an error message.\n\t *\n\t * @param {Attachment} attachment - The PDF attachment to process.\n\t * @returns {Promise<Media>} A promise that resolves to a Media object representing\n\t * the processed PDF attachment.\n\t */\n\tprivate async processPdfAttachment(attachment: Attachment): Promise<Media> {\n\t\ttry {\n\t\t\tconst response = await fetch(attachment.url);\n\t\t\tconst pdfBuffer = await response.arrayBuffer();\n\t\t\tconst text = await this.runtime\n\t\t\t\t.getService<IPdfService>(ServiceTypes.PDF)\n\t\t\t\t.convertPdfToText(Buffer.from(pdfBuffer));\n\t\t\tconst { title, description } = await generateSummary(this.runtime, text);\n\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: title || \"PDF Attachment\",\n\t\t\t\tsource: \"PDF\",\n\t\t\t\tdescription: description || \"A PDF document\",\n\t\t\t\ttext: text,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error processing PDF attachment: ${error.message}`);\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: \"PDF Attachment (conversion failed)\",\n\t\t\t\tsource: \"PDF\",\n\t\t\t\tdescription: \"A PDF document that could not be converted to text\",\n\t\t\t\ttext: `This is a PDF attachment. File name: ${attachment.name}, Size: ${attachment.size} bytes`,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Processes a plaintext attachment by fetching its content, generating a summary, and returning a Media object.\n\t * @param {Attachment} attachment - The attachment object to process.\n\t * @returns {Promise<Media>} A promise that resolves to a Media object representing the processed plaintext attachment.\n\t */\n\tprivate async processPlaintextAttachment(\n\t\tattachment: Attachment,\n\t): Promise<Media> {\n\t\ttry {\n\t\t\tconst response = await fetch(attachment.url);\n\t\t\tconst text = await response.text();\n\t\t\tconst { title, description } = await generateSummary(this.runtime, text);\n\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: title || \"Plaintext Attachment\",\n\t\t\t\tsource: \"Plaintext\",\n\t\t\t\tdescription: description || \"A plaintext document\",\n\t\t\t\ttext: text,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error processing plaintext attachment: ${error.message}`);\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: \"Plaintext Attachment (retrieval failed)\",\n\t\t\t\tsource: \"Plaintext\",\n\t\t\t\tdescription: \"A plaintext document that could not be retrieved\",\n\t\t\t\ttext: `This is a plaintext attachment. File name: ${attachment.name}, Size: ${attachment.size} bytes`,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Process the image attachment by fetching description and title using the IMAGE_DESCRIPTION model.\n\t * If successful, returns a Media object populated with the details. If unsuccessful, creates a fallback\n\t * Media object and logs the error.\n\t *\n\t * @param {Attachment} attachment - The attachment object containing the image details.\n\t * @returns {Promise<Media>} A promise that resolves to a Media object.\n\t */\n\tprivate async processImageAttachment(attachment: Attachment): Promise<Media> {\n\t\ttry {\n\t\t\tconst { description, title } = await this.runtime.useModel(\n\t\t\t\tModelTypes.IMAGE_DESCRIPTION,\n\t\t\t\tattachment.url,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: title || \"Image Attachment\",\n\t\t\t\tsource: \"Image\",\n\t\t\t\tdescription: description || \"An image attachment\",\n\t\t\t\ttext: description || \"Image content not available\",\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error processing image attachment: ${error.message}`);\n\t\t\treturn this.createFallbackImageMedia(attachment);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a fallback Media object for image attachments that could not be recognized.\n\t *\n\t * @param {Attachment} attachment - The attachment object containing image details.\n\t * @returns {Media} - The fallback Media object with basic information about the image attachment.\n\t */\n\n\tprivate createFallbackImageMedia(attachment: Attachment): Media {\n\t\treturn {\n\t\t\tid: attachment.id,\n\t\t\turl: attachment.url,\n\t\t\ttitle: \"Image Attachment\",\n\t\t\tsource: \"Image\",\n\t\t\tdescription: \"An image attachment (recognition failed)\",\n\t\t\ttext: `This is an image attachment. File name: ${attachment.name}, Size: ${attachment.size} bytes, Content type: ${attachment.contentType}`,\n\t\t};\n\t}\n\n\t/**\n\t * Process a video attachment to extract video information.\n\t * @param {Attachment} attachment - The attachment object containing video information.\n\t * @returns {Promise<Media>} A promise that resolves to a Media object with video details.\n\t * @throws {Error} If video service is not available.\n\t */\n\tprivate async processVideoAttachment(attachment: Attachment): Promise<Media> {\n\t\tconst videoService = this.runtime.getService<IVideoService>(\n\t\t\tServiceTypes.VIDEO,\n\t\t);\n\n\t\tif (!videoService) {\n\t\t\tthrow new Error(\"Video service not found\");\n\t\t}\n\n\t\tif (videoService.isVideoUrl(attachment.url)) {\n\t\t\tconst videoInfo = await videoService.processVideo(\n\t\t\t\tattachment.url,\n\t\t\t\tthis.runtime,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: videoInfo.title,\n\t\t\t\tsource: \"YouTube\",\n\t\t\t\tdescription: videoInfo.description,\n\t\t\t\ttext: videoInfo.text,\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tid: attachment.id,\n\t\t\turl: attachment.url,\n\t\t\ttitle: \"Video Attachment\",\n\t\t\tsource: \"Video\",\n\t\t\tdescription: \"A video attachment\",\n\t\t\ttext: \"Video content not available\",\n\t\t};\n\t}\n\n\t/**\n\t * Process a generic attachment and return a Media object with specified properties.\n\t * @param {Attachment} attachment - The attachment object to process.\n\t * @returns {Promise<Media>} A Promise that resolves to a Media object with specified properties.\n\t */\n\tprivate async processGenericAttachment(\n\t\tattachment: Attachment,\n\t): Promise<Media> {\n\t\treturn {\n\t\t\tid: attachment.id,\n\t\t\turl: attachment.url,\n\t\t\ttitle: \"Generic Attachment\",\n\t\t\tsource: \"Generic\",\n\t\t\tdescription: \"A generic attachment\",\n\t\t\ttext: \"Attachment content not available\",\n\t\t};\n\t}\n}\n","import {\n\ttype IAgentRuntime,\n\tModelTypes,\n\tlogger,\n\tparseJSONObjectFromText,\n\ttrimTokens,\n} from \"@elizaos/core\";\nimport {\n\tChannelType,\n\ttype Message as DiscordMessage,\n\tPermissionsBitField,\n\ttype TextChannel,\n\tThreadChannel,\n} from \"discord.js\";\n\n/**\n * Generates a WAV file header based on the provided audio information.\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 audio channels (default is 1).\n * @param {number} [bitsPerSample=16] - The number of bits per audio sample (default is 16).\n * @returns {Buffer} The generated WAV file header as a Buffer object.\n */\nexport function getWavHeader(\n\taudioLength: number,\n\tsampleRate: number,\n\tchannelCount = 1,\n\tbitsPerSample = 16,\n): Buffer {\n\tconst wavHeader = Buffer.alloc(44);\n\twavHeader.write(\"RIFF\", 0);\n\twavHeader.writeUInt32LE(36 + audioLength, 4); // Length of entire file in bytes minus 8\n\twavHeader.write(\"WAVE\", 8);\n\twavHeader.write(\"fmt \", 12);\n\twavHeader.writeUInt32LE(16, 16); // Length of format data\n\twavHeader.writeUInt16LE(1, 20); // Type of format (1 is PCM)\n\twavHeader.writeUInt16LE(channelCount, 22); // Number of channels\n\twavHeader.writeUInt32LE(sampleRate, 24); // Sample rate\n\twavHeader.writeUInt32LE((sampleRate * bitsPerSample * channelCount) / 8, 28); // Byte rate\n\twavHeader.writeUInt16LE((bitsPerSample * channelCount) / 8, 32); // Block align ((BitsPerSample * Channels) / 8)\n\twavHeader.writeUInt16LE(bitsPerSample, 34); // Bits per sample\n\twavHeader.write(\"data\", 36); // Data chunk header\n\twavHeader.writeUInt32LE(audioLength, 40); // Data chunk size\n\treturn wavHeader;\n}\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\truntime: IAgentRuntime,\n\ttext: string,\n): Promise<{ title: string; description: string }> {\n\t// make sure text is under 128k characters\n\ttext = await trimTokens(text, 100000, runtime);\n\n\tconst 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\tconst response = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\tprompt,\n\t});\n\n\tconst parsedResponse = parseJSONObjectFromText(response);\n\n\tif (parsedResponse?.title && parsedResponse?.summary) {\n\t\treturn {\n\t\t\ttitle: parsedResponse.title,\n\t\t\tdescription: parsedResponse.summary,\n\t\t};\n\t}\n\n\treturn {\n\t\ttitle: \"\",\n\t\tdescription: \"\",\n\t};\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 * @returns {Promise<DiscordMessage[]>} - Array of sent Discord messages.\n */\nexport async function sendMessageInChunks(\n\tchannel: TextChannel,\n\tcontent: string,\n\t_inReplyTo: string,\n\tfiles: any[],\n): Promise<DiscordMessage[]> {\n\tconst sentMessages: DiscordMessage[] = [];\n\tconst messages = splitMessage(content);\n\ttry {\n\t\tfor (let i = 0; i < messages.length; i++) {\n\t\t\tconst message = messages[i];\n\t\t\tif (\n\t\t\t\tmessage.trim().length > 0 ||\n\t\t\t\t(i === messages.length - 1 && files && files.length > 0)\n\t\t\t) {\n\t\t\t\tconst options: any = {\n\t\t\t\t\tcontent: message.trim(),\n\t\t\t\t};\n\n\t\t\t\t// if (i === 0 && inReplyTo) {\n\t\t\t\t// // Reply to the specified message for the first chunk\n\t\t\t\t// options.reply = {\n\t\t\t\t// messageReference: inReplyTo,\n\t\t\t\t// };\n\t\t\t\t// }\n\n\t\t\t\tif (i === messages.length - 1 && files && files.length > 0) {\n\t\t\t\t\t// Attach files to the last message chunk\n\t\t\t\t\toptions.files = files;\n\t\t\t\t}\n\n\t\t\t\tconst m = await channel.send(options);\n\t\t\t\tsentMessages.push(m);\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tlogger.error(\"Error sending message:\", error);\n\t}\n\n\treturn 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\tconst messages: string[] = [];\n\tlet currentMessage = \"\";\n\n\tconst rawLines = content?.split(\"\\n\") || [];\n\t// split all lines into MAX_MESSAGE_LENGTH chunks so any long lines are split\n\tconst lines = rawLines.flatMap((line) => {\n\t\tconst chunks = [];\n\t\twhile (line.length > MAX_MESSAGE_LENGTH) {\n\t\t\tchunks.push(line.slice(0, MAX_MESSAGE_LENGTH));\n\t\t\tline = line.slice(MAX_MESSAGE_LENGTH);\n\t\t}\n\t\tchunks.push(line);\n\t\treturn chunks;\n\t});\n\n\tfor (const line of lines) {\n\t\tif (currentMessage.length + line.length + 1 > MAX_MESSAGE_LENGTH) {\n\t\t\tmessages.push(currentMessage.trim());\n\t\t\tcurrentMessage = \"\";\n\t\t}\n\t\tcurrentMessage += `${line}\\n`;\n\t}\n\n\tif (currentMessage.trim().length > 0) {\n\t\tmessages.push(currentMessage.trim());\n\t}\n\n\treturn 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\t// validate input\n\tif (!channel) {\n\t\treturn {\n\t\t\tcanSend: false,\n\t\t\treason: \"No channel given\",\n\t\t};\n\t}\n\t// if it is a DM channel, we can always send messages\n\tif (channel.type === ChannelType.DM) {\n\t\treturn {\n\t\t\tcanSend: true,\n\t\t\treason: null,\n\t\t};\n\t}\n\tconst botMember = channel.guild?.members.cache.get(channel.client.user.id);\n\n\tif (!botMember) {\n\t\treturn {\n\t\t\tcanSend: false,\n\t\t\treason: \"Not a guild channel or bot member not found\",\n\t\t};\n\t}\n\n\t// Required permissions for sending messages\n\tconst requiredPermissions = [\n\t\tPermissionsBitField.Flags.ViewChannel,\n\t\tPermissionsBitField.Flags.SendMessages,\n\t\tPermissionsBitField.Flags.ReadMessageHistory,\n\t];\n\n\t// Add thread-specific permission if it's a thread\n\tif (channel instanceof ThreadChannel) {\n\t\trequiredPermissions.push(PermissionsBitField.Flags.SendMessagesInThreads);\n\t}\n\n\t// Check permissions\n\tconst permissions = channel.permissionsFor(botMember);\n\n\tif (!permissions) {\n\t\treturn {\n\t\t\tcanSend: false,\n\t\t\treason: \"Could not retrieve permissions\",\n\t\t};\n\t}\n\n\t// Check each required permission\n\tconst missingPermissions = requiredPermissions.filter(\n\t\t(perm) => !permissions.has(perm),\n\t);\n\n\treturn {\n\t\tcanSend: missingPermissions.length === 0,\n\t\tmissingPermissions: missingPermissions,\n\t\treason:\n\t\t\tmissingPermissions.length > 0\n\t\t\t\t? `Missing permissions: ${missingPermissions\n\t\t\t\t\t\t.map((p) => String(p))\n\t\t\t\t\t\t.join(\", \")}`\n\t\t\t\t: null,\n\t};\n}\n","import type { IAgentRuntime, Memory, Provider, State } from \"@elizaos/core\";\nimport { ChannelType } from \"@elizaos/core\";\nimport type { DiscordService } from \"../index\";\nimport { ServiceTypes } 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 */\nconst channelStateProvider: Provider = {\n\tname: \"channelState\",\n\tget: async (runtime: IAgentRuntime, message: Memory, state?: State) => {\n\t\tconst room =\n\t\t\tstate.data?.room ??\n\t\t\t(await runtime.getDatabaseAdapter().getRoom(message.roomId));\n\t\tif (!room) {\n\t\t\tthrow new Error(\"No room found\");\n\t\t}\n\n\t\t// if message source is not discord, return\n\t\tif (message.content.source !== \"discord\") {\n\t\t\treturn {\n\t\t\t\tdata: null,\n\t\t\t\tvalues: {},\n\t\t\t\ttext: \"\",\n\t\t\t};\n\t\t}\n\n\t\tconst agentName = state?.agentName || \"The agent\";\n\t\tconst senderName = state?.senderName || \"someone\";\n\n\t\tlet responseText = \"\";\n\t\tlet channelType = \"\";\n\t\tlet serverName = \"\";\n\t\tlet channelId = \"\";\n\t\tconst serverId = room.serverId;\n\n\t\tif (room.type === ChannelType.DM) {\n\t\t\tchannelType = \"DM\";\n\t\t\tresponseText = `${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\t\t} else {\n\t\t\tchannelType = \"GROUP\";\n\n\t\t\tif (!serverId) {\n\t\t\t\tconsole.error(\"No server ID found\");\n\t\t\t\treturn {\n\t\t\t\t\tdata: {\n\t\t\t\t\t\troom,\n\t\t\t\t\t\tchannelType,\n\t\t\t\t\t},\n\t\t\t\t\tvalues: {\n\t\t\t\t\t\tchannelType,\n\t\t\t\t\t},\n\t\t\t\t\ttext: \"\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tchannelId = room.channelId;\n\n\t\t\tconst discordService = runtime.getService(\n\t\t\t\tServiceTypes.DISCORD,\n\t\t\t) as DiscordService;\n\t\t\tif (!discordService) {\n\t\t\t\tconsole.warn(\"No discord client found\");\n\t\t\t\treturn {\n\t\t\t\t\tdata: {\n\t\t\t\t\t\troom,\n\t\t\t\t\t\tchannelType,\n\t\t\t\t\t\tserverId,\n\t\t\t\t\t},\n\t\t\t\t\tvalues: {\n\t\t\t\t\t\tchannelType,\n\t\t\t\t\t\tserverId,\n\t\t\t\t\t},\n\t\t\t\t\ttext: \"\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst guild = discordService.client.guilds.cache.get(serverId);\n\t\t\tserverName = guild.name;\n\n\t\t\tresponseText = `${agentName} is currently having a conversation in the channel \\`@${channelId} in the server \\`${serverName}\\` (@${serverId})`;\n\t\t\tresponseText += `\\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\t\t}\n\n\t\treturn {\n\t\t\tdata: {\n\t\t\t\troom,\n\t\t\t\tchannelType,\n\t\t\t\tserverId,\n\t\t\t\tserverName,\n\t\t\t\tchannelId,\n\t\t\t},\n\t\t\tvalues: {\n\t\t\t\tchannelType,\n\t\t\t\tserverName,\n\t\t\t\tchannelId,\n\t\t\t},\n\t\t\ttext: responseText,\n\t\t};\n\t},\n};\n\nexport default channelStateProvider;\n","import { getVoiceConnection } from \"@discordjs/voice\";\nimport type { IAgentRuntime, Memory, Provider, State } from \"@elizaos/core\";\nimport { ChannelType } from \"@elizaos/core\";\n\n/**\n * Provides information about the voice state of the user, including whether they are currently in a voice channel.\n *\n * @param {IAgentRuntime} runtime - The runtime object for the agent\n * @param {Memory} message - The message object containing room ID\n * @param {State} [state] - Optional state object for the user\n * @returns {Object} An object containing information about the voice state of the user\n */\nconst voiceStateProvider: Provider = {\n\tname: \"voiceState\",\n\tget: async (runtime: IAgentRuntime, message: Memory, state?: State) => {\n\t\t// Voice doesn't get a discord message, so we need to use the channel for guild data\n\t\tconst room = await runtime.getDatabaseAdapter().getRoom(message.roomId);\n\t\tif (!room) {\n\t\t\tthrow new Error(\"No room found\");\n\t\t}\n\n\t\tif (room.type !== ChannelType.GROUP) {\n\t\t\t// only handle in a group scenario for now\n\t\t\treturn {\n\t\t\t\tdata: {\n\t\t\t\t\tisInVoiceChannel: false,\n\t\t\t\t\troom,\n\t\t\t\t},\n\t\t\t\tvalues: {\n\t\t\t\t\tisInVoiceChannel: \"false\",\n\t\t\t\t\troomType: room.type,\n\t\t\t\t},\n\t\t\t\ttext: \"\",\n\t\t\t};\n\t\t}\n\n\t\tconst serverId = room.serverId;\n\n\t\tif (!serverId) {\n\t\t\tthrow new Error(\"No server ID found 10\");\n\t\t}\n\n\t\tconst connection = getVoiceConnection(serverId);\n\t\tconst agentName = state?.agentName || \"The agent\";\n\n\t\tif (!connection) {\n\t\t\treturn {\n\t\t\t\tdata: {\n\t\t\t\t\tisInVoiceChannel: false,\n\t\t\t\t\troom,\n\t\t\t\t\tserverId,\n\t\t\t\t},\n\t\t\t\tvalues: {\n\t\t\t\t\tisInVoiceChannel: \"false\",\n\t\t\t\t\tserverId,\n\t\t\t\t},\n\t\t\t\ttext: `${agentName} is not currently in a voice channel`,\n\t\t\t};\n\t\t}\n\n\t\tconst worldId = room.worldId;\n\n\t\t// get the world from the runtime.getDatabaseAdapter().getWorld\n\t\tconst world = await runtime.getDatabaseAdapter().getWorld(worldId);\n\n\t\tif (!world) {\n\t\t\tthrow new Error(\"No world found\");\n\t\t}\n\n\t\tconst worldName = world.name;\n\t\tconst roomType = room.type;\n\t\tconst channelId = room.channelId;\n\t\tconst channelName = room.name;\n\n\t\tif (!channelId) {\n\t\t\treturn {\n\t\t\t\tdata: {\n\t\t\t\t\tisInVoiceChannel: true,\n\t\t\t\t\troom,\n\t\t\t\t\tserverId,\n\t\t\t\t\tworld,\n\t\t\t\t\tconnection,\n\t\t\t\t},\n\t\t\t\tvalues: {\n\t\t\t\t\tisInVoiceChannel: \"true\",\n\t\t\t\t\tserverId,\n\t\t\t\t\tworldName,\n\t\t\t\t\troomType,\n\t\t\t\t},\n\t\t\t\ttext: `${agentName} is in an invalid voice channel`,\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tdata: {\n\t\t\t\tisInVoiceChannel: true,\n\t\t\t\troom,\n\t\t\t\tserverId,\n\t\t\t\tworld,\n\t\t\t\tconnection,\n\t\t\t\tchannelId,\n\t\t\t\tchannelName,\n\t\t\t},\n\t\t\tvalues: {\n\t\t\t\tisInVoiceChannel: \"true\",\n\t\t\t\tserverId,\n\t\t\t\tworldName,\n\t\t\t\troomType,\n\t\t\t\tchannelId,\n\t\t\t\tchannelName,\n\t\t\t},\n\t\t\ttext: `${agentName} is currently in the voice channel: ${channelName} (ID: ${channelId})`,\n\t\t};\n\t},\n};\n\nexport default voiceStateProvider;\n","import {\n\tAudioPlayerStatus,\n\tNoSubscriberBehavior,\n\ttype VoiceConnection,\n\tVoiceConnectionStatus,\n\tcreateAudioPlayer,\n\tcreateAudioResource,\n\tentersState,\n} from \"@discordjs/voice\";\nimport {\n\ttype IAgentRuntime,\n\tModelTypes,\n\ttype TestSuite,\n\tlogger,\n} from \"@elizaos/core\";\nimport { ChannelType, Events, type TextChannel } from \"discord.js\";\nimport type { DiscordService } from \"./index\";\nimport { ServiceTypes } from \"./types\";\nimport { sendMessageInChunks } from \"./utils\";\n\nconst TEST_IMAGE_URL =\n\t\"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\tname = \"discord\";\n\tprivate discordClient: DiscordService | null = null;\n\ttests: { name: string; fn: (runtime: IAgentRuntime) => Promise<void> }[];\n\n\t/**\n\t * Constructor for initializing the tests array with test cases to be executed.\n\t *\n\t * @constructor\n\t * @this {TestSuite}\n\t */\n\tconstructor() {\n\t\tthis.tests = [\n\t\t\t{\n\t\t\t\tname: \"Initialize Discord Client\",\n\t\t\t\tfn: this.testCreatingDiscordClient.bind(this),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Slash Commands - Join Voice\",\n\t\t\t\tfn: this.testJoinVoiceSlashCommand.bind(this),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Voice Playback & TTS\",\n\t\t\t\tfn: this.testTextToSpeechPlayback.bind(this),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Send Message with Attachments\",\n\t\t\t\tfn: this.testSendingTextMessage.bind(this),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Handle Incoming Messages\",\n\t\t\t\tfn: this.testHandlingMessage.bind(this),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Slash Commands - Leave Voice\",\n\t\t\t\tfn: this.testLeaveVoiceSlashCommand.bind(this),\n\t\t\t},\n\t\t];\n\t}\n\n\t/**\n\t * Asynchronously tests the creation of Discord client using the provided runtime.\n\t *\n\t * @param {IAgentRuntime} runtime - The agent runtime used to obtain the Discord service.\n\t * @returns {Promise<void>} - A Promise that resolves once the Discord client is ready.\n\t * @throws {Error} - If an error occurs while creating the Discord client.\n\t */\n\tasync testCreatingDiscordClient(runtime: IAgentRuntime) {\n\t\ttry {\n\t\t\tthis.discordClient = runtime.getService(\n\t\t\t\tServiceTypes.DISCORD,\n\t\t\t) as DiscordService;\n\n\t\t\t// Wait for the bot to be ready before proceeding\n\t\t\tif (this.discordClient.client.isReady()) {\n\t\t\t\tlogger.success(\"DiscordService is already ready.\");\n\t\t\t} else {\n\t\t\t\tlogger.info(\"Waiting for DiscordService to be ready...\");\n\t\t\t\tawait new Promise((resolve, reject) => {\n\t\t\t\t\tthis.discordClient.client.once(Events.ClientReady, resolve);\n\t\t\t\t\tthis.discordClient.client.once(Events.Error, reject);\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error in test creating Discord client: ${error}`);\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously tests the join voice slash command functionality.\n\t *\n\t * @param {IAgentRuntime} runtime - The runtime environment for the agent.\n\t * @returns {Promise<void>} - A promise that resolves once the test is complete.\n\t * @throws {Error} - If there is an error in executing the slash command test.\n\t */\n\tasync testJoinVoiceSlashCommand(runtime: IAgentRuntime) {\n\t\ttry {\n\t\t\tawait this.waitForVoiceManagerReady(this.discordClient);\n\n\t\t\tconst channel = await this.getTestChannel(runtime);\n\t\t\tif (!channel || !channel.isTextBased()) {\n\t\t\t\tthrow new Error(\"Invalid test channel for slash command test.\");\n\t\t\t}\n\n\t\t\t// Simulate a join channel slash command interaction\n\t\t\tconst fakeJoinInteraction = {\n\t\t\t\tisCommand: () => true,\n\t\t\t\tcommandName: \"joinchannel\",\n\t\t\t\toptions: {\n\t\t\t\t\tget: (name: string) =>\n\t\t\t\t\t\tname === \"channel\" ? { value: channel.id } : null,\n\t\t\t\t},\n\t\t\t\tguild: (channel as TextChannel).guild,\n\t\t\t\tdeferReply: async () => {},\n\t\t\t\teditReply: async (message: string) => {\n\t\t\t\t\tlogger.info(`JoinChannel Slash Command Response: ${message}`);\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tawait this.discordClient.voiceManager.handleJoinChannelCommand(\n\t\t\t\tfakeJoinInteraction as any,\n\t\t\t);\n\n\t\t\tlogger.success(\"Slash command test completed successfully.\");\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error in slash commands test: ${error}`);\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously tests the leave voice channel slash command.\n\t *\n\t * @param {IAgentRuntime} runtime - The Agent Runtime instance.\n\t * @returns {Promise<void>} A promise that resolves when the test is complete.\n\t */\n\tasync testLeaveVoiceSlashCommand(runtime: IAgentRuntime) {\n\t\ttry {\n\t\t\tawait this.waitForVoiceManagerReady(this.discordClient);\n\n\t\t\tconst channel = await this.getTestChannel(runtime);\n\t\t\tif (!channel || !channel.isTextBased()) {\n\t\t\t\tthrow new Error(\"Invalid test channel for slash command test.\");\n\t\t\t}\n\n\t\t\t// Simulate a leave channel slash command interaction\n\t\t\tconst fakeLeaveInteraction = {\n\t\t\t\tisCommand: () => true,\n\t\t\t\tcommandName: \"leavechannel\",\n\t\t\t\tguildId: (channel as TextChannel).guildId,\n\t\t\t\treply: async (message: string) => {\n\t\t\t\t\tlogger.info(`LeaveChannel Slash Command Response: ${message}`);\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tawait this.discordClient.voiceManager.handleLeaveChannelCommand(\n\t\t\t\tfakeLeaveInteraction as any,\n\t\t\t);\n\n\t\t\tlogger.success(\"Slash command test completed successfully.\");\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error in slash commands test: ${error}`);\n\t\t}\n\t}\n\n\t/**\n\t * Test Text to Speech playback.\n\t * @param {IAgentRuntime} runtime - The Agent Runtime instance.\n\t * @throws {Error} - If voice channel is invalid, voice connection fails to become ready, or no text to speech service found.\n\t */\n\tasync testTextToSpeechPlayback(runtime: IAgentRuntime) {\n\t\ttry {\n\t\t\tawait this.waitForVoiceManagerReady(this.discordClient);\n\n\t\t\tconst channel = await this.getTestChannel(runtime);\n\t\t\tif (!channel || channel.type !== ChannelType.GuildVoice) {\n\t\t\t\tthrow new Error(\"Invalid voice channel.\");\n\t\t\t}\n\n\t\t\tawait this.discordClient.voiceManager.joinChannel(channel);\n\n\t\t\tconst guild = await this.getActiveGuild(this.discordClient);\n\t\t\tconst guildId = guild.id;\n\t\t\tconst connection =\n\t\t\t\tthis.discordClient.voiceManager.getVoiceConnection(guildId);\n\n\t\t\ttry {\n\t\t\t\tawait entersState(connection, VoiceConnectionStatus.Ready, 10_000);\n\t\t\t\tlogger.success(`Voice connection is ready in guild: ${guildId}`);\n\t\t\t} catch (error) {\n\t\t\t\tthrow new Error(`Voice connection failed to become ready: ${error}`);\n\t\t\t}\n\n\t\t\tlet responseStream = null;\n\n\t\t\ttry {\n\t\t\t\tresponseStream = await runtime.useModel(\n\t\t\t\t\tModelTypes.TEXT_TO_SPEECH,\n\t\t\t\t\t`Hi! I'm ${runtime.character.name}! How are you doing today?`,\n\t\t\t\t);\n\t\t\t} catch (_error) {\n\t\t\t\tthrow new Error(\"No text to speech service found\");\n\t\t\t}\n\n\t\t\tif (!responseStream) {\n\t\t\t\tthrow new Error(\"TTS response stream is null or undefined.\");\n\t\t\t}\n\n\t\t\tawait this.playAudioStream(responseStream, connection);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error in TTS playback test: ${error}`);\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously tests sending a text message to a specified channel.\n\t *\n\t * @param {IAgentRuntime} runtime - The runtime for the agent.\n\t * @returns {Promise<void>} A Promise that resolves when the message is sent successfully.\n\t * @throws {Error} If there is an error in sending the text message.\n\t */\n\tasync testSendingTextMessage(runtime: IAgentRuntime) {\n\t\ttry {\n\t\t\tconst channel = await this.getTestChannel(runtime);\n\n\t\t\tawait this.sendMessageToChannel(\n\t\t\t\tchannel as TextChannel,\n\t\t\t\t\"Testing Message\",\n\t\t\t\t[TEST_IMAGE_URL],\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error in sending text message: ${error}`);\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously handles sending a test message using the given runtime and mock user data.\n\t *\n\t * @param {IAgentRuntime} runtime - The agent runtime object.\n\t * @returns {Promise<void>} A Promise that resolves once the message is handled.\n\t */\n\tasync testHandlingMessage(runtime: IAgentRuntime) {\n\t\ttry {\n\t\t\tconst channel = await this.getTestChannel(runtime);\n\n\t\t\tconst fakeMessage = {\n\t\t\t\tcontent: `Hello, ${runtime.character.name}! How are you?`,\n\t\t\t\tauthor: {\n\t\t\t\t\tid: \"mock-user-id\",\n\t\t\t\t\tusername: \"MockUser\",\n\t\t\t\t\tbot: false,\n\t\t\t\t},\n\t\t\t\tchannel,\n\t\t\t\tid: \"mock-message-id\",\n\t\t\t\tcreatedTimestamp: Date.now(),\n\t\t\t\tmentions: {\n\t\t\t\t\thas: () => false,\n\t\t\t\t},\n\t\t\t\treference: null,\n\t\t\t\tattachments: [],\n\t\t\t};\n\t\t\tawait this.discordClient.messageManager.handleMessage(fakeMessage as any);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error in sending text message: ${error}`);\n\t\t}\n\t}\n\n\t// #############################\n\t// Utility Functions\n\t// #############################\n\n\t/**\n\t * Asynchronously retrieves the test channel associated with the provided runtime.\n\t *\n\t * @param {IAgentRuntime} runtime - The runtime object containing necessary information.\n\t * @returns {Promise<Channel>} The test channel retrieved from the Discord client.\n\t * @throws {Error} If no test channel is found.\n\t */\n\tasync getTestChannel(runtime: IAgentRuntime) {\n\t\tconst channelId = this.validateChannelId(runtime);\n\t\tconst channel = await this.discordClient.client.channels.fetch(channelId);\n\n\t\tif (!channel) throw new Error(\"no test channel found!\");\n\n\t\treturn channel;\n\t}\n\n\t/**\n\t * Async function to send a message to a text-based channel.\n\t *\n\t * @param {TextChannel} channel - The text-based channel the message is being sent to.\n\t * @param {string} messageContent - The content of the message being sent.\n\t * @param {any[]} files - An array of files to include in the message.\n\t * @throws {Error} If the channel is not a text-based channel or does not exist.\n\t * @throws {Error} If there is an error sending the message.\n\t */\n\tasync sendMessageToChannel(\n\t\tchannel: TextChannel,\n\t\tmessageContent: string,\n\t\tfiles: any[],\n\t) {\n\t\ttry {\n\t\t\tif (!channel || !channel.isTextBased()) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Channel is not a text-based channel or does not exist.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tawait sendMessageInChunks(\n\t\t\t\tchannel as TextChannel,\n\t\t\t\tmessageContent,\n\t\t\t\tnull,\n\t\t\t\tfiles,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error sending message: ${error}`);\n\t\t}\n\t}\n\n\t/**\n\t * Play an audio stream from a given response stream using the provided VoiceConnection.\n\t *\n\t * @param {any} responseStream - The response stream to play as audio.\n\t * @param {VoiceConnection} connection - The VoiceConnection to use for playing the audio.\n\t * @returns {Promise<void>} - A Promise that resolves when the TTS playback is finished.\n\t */\n\tasync playAudioStream(responseStream: any, connection: VoiceConnection) {\n\t\tconst audioPlayer = createAudioPlayer({\n\t\t\tbehaviors: {\n\t\t\t\tnoSubscriber: NoSubscriberBehavior.Pause,\n\t\t\t},\n\t\t});\n\n\t\tconst audioResource = createAudioResource(responseStream);\n\n\t\taudioPlayer.play(audioResource);\n\t\tconnection.subscribe(audioPlayer);\n\n\t\tlogger.success(\"TTS playback started successfully.\");\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\taudioPlayer.once(AudioPlayerStatus.Idle, () => {\n\t\t\t\tlogger.info(\"TTS playback finished.\");\n\t\t\t\tresolve();\n\t\t\t});\n\n\t\t\taudioPlayer.once(\"error\", (error) => {\n\t\t\t\treject(error);\n\t\t\t\tthrow new Error(`TTS playback error: ${error}`);\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Retrieves the active guild where the bot is currently connected to a voice channel.\n\t *\n\t * @param {DiscordService} discordClient The DiscordService instance used to interact with the Discord API.\n\t * @returns {Promise<Guild>} The active guild where the bot is currently connected to a voice channel.\n\t * @throws {Error} If no active voice connection is found for the bot.\n\t */\n\tasync getActiveGuild(discordClient: DiscordService) {\n\t\tconst guilds = await discordClient.client.guilds.fetch();\n\t\tconst fullGuilds = await Promise.all(guilds.map((guild) => guild.fetch())); // Fetch full guild data\n\n\t\tconst activeGuild = fullGuilds.find((g) => g.members.me?.voice.channelId);\n\t\tif (!activeGuild) {\n\t\t\tthrow new Error(\"No active voice connection found for the bot.\");\n\t\t}\n\t\treturn activeGuild;\n\t}\n\n\t/**\n\t * Waits for the VoiceManager in the Discord client to be ready.\n\t *\n\t * @param {DiscordService} discordClient - The Discord client to check for VoiceManager readiness.\n\t * @throws {Error} If the Discord client is not initialized.\n\t * @returns {Promise<void>} A promise that resolves when the VoiceManager is ready.\n\t */\n\tprivate async waitForVoiceManagerReady(discordClient: DiscordService) {\n\t\tif (!discordClient) {\n\t\t\tthrow new Error(\"Discord client is not initialized.\");\n\t\t}\n\n\t\tif (!discordClient.voiceManager.isReady()) {\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tdiscordClient.voiceManager.once(\"ready\", resolve);\n\t\t\t\tdiscordClient.voiceManager.once(\"error\", reject);\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Validates the Discord test channel ID by checking if it is set in the runtime or environment variables.\n\t * If the test channel ID is not set, an error is thrown.\n\t *\n\t * @param {IAgentRuntime} runtime The runtime object containing the settings and environment variables.\n\t * @returns {string} The validated Discord test channel ID.\n\t */\n\tprivate validateChannelId(runtime: IAgentRuntime) {\n\t\tconst testChannelId =\n\t\t\truntime.getSetting(\"DISCORD_TEST_CHANNEL_ID\") ||\n\t\t\tprocess.env.DISCORD_TEST_CHANNEL_ID;\n\t\tif (!testChannelId) {\n\t\t\tthrow new Error(\n\t\t\t\t\"DISCORD_TEST_CHANNEL_ID is not set. Please provide a valid channel ID in the environment variables.\",\n\t\t\t);\n\t\t}\n\t\treturn testChannelId;\n\t}\n}\n","import { EventEmitter } from \"node:events\";\nimport { type Readable, pipeline } from \"node:stream\";\nimport {\n\ttype AudioPlayer,\n\ttype AudioReceiveStream,\n\tNoSubscriberBehavior,\n\tStreamType,\n\ttype VoiceConnection,\n\tVoiceConnectionStatus,\n\tcreateAudioPlayer,\n\tcreateAudioResource,\n\tentersState,\n\tgetVoiceConnections,\n\tjoinVoiceChannel,\n} from \"@discordjs/voice\";\nimport {\n\tChannelType,\n\ttype Content,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelTypes,\n\ttype UUID,\n\tcreateUniqueUuid,\n\tlogger,\n} from \"@elizaos/core\";\nimport {\n\ttype BaseGuildVoiceChannel,\n\ttype Channel,\n\ttype Client,\n\tChannelType as DiscordChannelType,\n\ttype Guild,\n\ttype GuildMember,\n\ttype VoiceChannel,\n\ttype VoiceState,\n} from \"discord.js\";\nimport prism from \"prism-media\";\nimport type { DiscordService } from \"./index\";\nimport { getWavHeader } from \"./utils\";\n\n// These values are chosen for compatibility with picovoice components\nconst DECODE_FRAME_SIZE = 1024;\nconst DECODE_SAMPLE_RATE = 16000;\n\n/**\n * Class representing an AudioMonitor that listens for audio data from a Readable stream.\n */\nexport class AudioMonitor {\n\tprivate readable: Readable;\n\tprivate buffers: Buffer[] = [];\n\tprivate maxSize: number;\n\tprivate lastFlagged = -1;\n\tprivate ended = false;\n\n\t/**\n\t * Constructs an AudioMonitor instance.\n\t * @param {Readable} readable - The readable stream to monitor for audio data.\n\t * @param {number} maxSize - The maximum size of the audio buffer.\n\t * @param {function} onStart - The callback function to be called when audio starts.\n\t * @param {function} callback - The callback function to process audio data.\n\t */\n\tconstructor(\n\t\treadable: Readable,\n\t\tmaxSize: number,\n\t\tonStart: () => void,\n\t\tcallback: (buffer: Buffer) => void,\n\t) {\n\t\tthis.readable = readable;\n\t\tthis.maxSize = maxSize;\n\t\tthis.readable.on(\"data\", (chunk: Buffer) => {\n\t\t\t//console.log('AudioMonitor got data');\n\t\t\tif (this.lastFlagged < 0) {\n\t\t\t\tthis.lastFlagged = this.buffers.length;\n\t\t\t}\n\t\t\tthis.buffers.push(chunk);\n\t\t\tconst currentSize = this.buffers.reduce(\n\t\t\t\t(acc, cur) => acc + cur.length,\n\t\t\t\t0,\n\t\t\t);\n\t\t\twhile (currentSize > this.maxSize) {\n\t\t\t\tthis.buffers.shift();\n\t\t\t\tthis.lastFlagged--;\n\t\t\t}\n\t\t});\n\t\tthis.readable.on(\"end\", () => {\n\t\t\tlogger.log(\"AudioMonitor ended\");\n\t\t\tthis.ended = true;\n\t\t\tif (this.lastFlagged < 0) return;\n\t\t\tcallback(this.getBufferFromStart());\n\t\t\tthis.lastFlagged = -1;\n\t\t});\n\t\tthis.readable.on(\"speakingStopped\", () => {\n\t\t\tif (this.ended) return;\n\t\t\tlogger.log(\"Speaking stopped\");\n\t\t\tif (this.lastFlagged < 0) return;\n\t\t\tcallback(this.getBufferFromStart());\n\t\t});\n\t\tthis.readable.on(\"speakingStarted\", () => {\n\t\t\tif (this.ended) return;\n\t\t\tonStart();\n\t\t\tlogger.log(\"Speaking started\");\n\t\t\tthis.reset();\n\t\t});\n\t}\n\n\t/**\n\t * Stops listening to \"data\", \"end\", \"speakingStopped\", and \"speakingStarted\" events on the readable stream.\n\t */\n\tstop() {\n\t\tthis.readable.removeAllListeners(\"data\");\n\t\tthis.readable.removeAllListeners(\"end\");\n\t\tthis.readable.removeAllListeners(\"speakingStopped\");\n\t\tthis.readable.removeAllListeners(\"speakingStarted\");\n\t}\n\n\t/**\n\t * Check if the item is flagged.\n\t * @returns {boolean} True if the item was flagged, false otherwise.\n\t */\n\tisFlagged() {\n\t\treturn this.lastFlagged >= 0;\n\t}\n\n\t/**\n\t * Returns a Buffer containing all buffers starting from the last flagged index.\n\t * If the last flagged index is less than 0, returns null.\n\t *\n\t * @returns {Buffer | null} The concatenated Buffer or null\n\t */\n\tgetBufferFromFlag() {\n\t\tif (this.lastFlagged < 0) {\n\t\t\treturn null;\n\t\t}\n\t\tconst buffer = Buffer.concat(this.buffers.slice(this.lastFlagged));\n\t\treturn buffer;\n\t}\n\n\t/**\n\t * Concatenates all buffers in the array and returns a single buffer.\n\t *\n\t * @returns {Buffer} The concatenated buffer from the start.\n\t */\n\tgetBufferFromStart() {\n\t\tconst buffer = Buffer.concat(this.buffers);\n\t\treturn buffer;\n\t}\n\n\t/**\n\t * Resets the buffers array and sets lastFlagged to -1.\n\t */\n\treset() {\n\t\tthis.buffers = [];\n\t\tthis.lastFlagged = -1;\n\t}\n\n\t/**\n\t * Check if the object has ended.\n\t * @returns {boolean} Returns true if the object has ended; false otherwise.\n\t */\n\tisEnded() {\n\t\treturn this.ended;\n\t}\n}\n\n/**\n * Class representing a VoiceManager that extends EventEmitter.\n * @extends EventEmitter\n */\nexport class VoiceManager extends EventEmitter {\n\tprivate processingVoice = false;\n\tprivate transcriptionTimeout: NodeJS.Timeout | null = null;\n\tprivate userStates: Map<\n\t\tstring,\n\t\t{\n\t\t\tbuffers: Buffer[];\n\t\t\ttotalLength: number;\n\t\t\tlastActive: number;\n\t\t\ttranscriptionText: string;\n\t\t}\n\t> = new Map();\n\tprivate activeAudioPlayer: AudioPlayer | null = null;\n\tprivate client: Client;\n\tprivate runtime: IAgentRuntime;\n\tprivate streams: Map<string, Readable> = new Map();\n\tprivate connections: Map<string, VoiceConnection> = new Map();\n\tprivate activeMonitors: Map<\n\t\tstring,\n\t\t{ channel: BaseGuildVoiceChannel; monitor: AudioMonitor }\n\t> = new Map();\n\tprivate ready: boolean;\n\n\t/**\n\t * Constructor for initializing a new instance of the class.\n\t *\n\t * @param {DiscordService} service - The Discord service to use.\n\t * @param {IAgentRuntime} runtime - The runtime for the agent.\n\t */\n\tconstructor(service: DiscordService, runtime: IAgentRuntime) {\n\t\tsuper();\n\t\tthis.client = service.client;\n\t\tthis.runtime = runtime;\n\n\t\tthis.client.on(\"voiceManagerReady\", () => {\n\t\t\tthis.setReady(true);\n\t\t});\n\t}\n\n\t/**\n\t * Asynchronously retrieves the type of the channel.\n\t * @param {Channel} channel - The channel to get the type for.\n\t * @returns {Promise<ChannelType>} The type of the channel.\n\t */\n\tasync getChannelType(channel: Channel): Promise<ChannelType> {\n\t\tswitch (channel.type) {\n\t\t\tcase DiscordChannelType.GuildVoice:\n\t\t\tcase DiscordChannelType.GuildStageVoice:\n\t\t\t\treturn ChannelType.VOICE_GROUP;\n\t\t}\n\t}\n\n\t/**\n\t * Set the ready status of the VoiceManager.\n\t * @param {boolean} status - The status to set.\n\t */\n\tprivate setReady(status: boolean) {\n\t\tthis.ready = status;\n\t\tthis.emit(\"ready\");\n\t\tlogger.success(`VoiceManager is now ready: ${this.ready}`);\n\t}\n\n\t/**\n\t * Check if the object is ready.\n\t *\n\t * @returns {boolean} True if the object is ready, false otherwise.\n\t */\n\tisReady() {\n\t\treturn this.ready;\n\t}\n\n\t/**\n\t * Handle voice state update event.\n\t * @param {VoiceState} oldState - The old voice state of the member.\n\t * @param {VoiceState} newState - The new voice state of the member.\n\t * @returns {void}\n\t */\n\tasync handleVoiceStateUpdate(oldState: VoiceState, newState: VoiceState) {\n\t\tconst oldChannelId = oldState.channelId;\n\t\tconst newChannelId = newState.channelId;\n\t\tconst member = newState.member;\n\t\tif (!member) return;\n\t\tif (member.id === this.client.user?.id) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Ignore mute/unmute events\n\t\tif (oldChannelId === newChannelId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// User leaving a channel where the bot is present\n\t\tif (oldChannelId && this.connections.has(oldChannelId)) {\n\t\t\tthis.stopMonitoringMember(member.id);\n\t\t}\n\n\t\t// User joining a channel where the bot is present\n\t\tif (newChannelId && this.connections.has(newChannelId)) {\n\t\t\tawait this.monitorMember(\n\t\t\t\tmember,\n\t\t\t\tnewState.channel as BaseGuildVoiceChannel,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Joins a voice channel and sets up the necessary connection and event listeners.\n\t * @param {BaseGuildVoiceChannel} channel - The voice channel to join\n\t */\n\tasync joinChannel(channel: BaseGuildVoiceChannel) {\n\t\tconst oldConnection = this.getVoiceConnection(channel.guildId as string);\n\t\tif (oldConnection) {\n\t\t\ttry {\n\t\t\t\toldConnection.destroy();\n\t\t\t\t// Remove all associated streams and monitors\n\t\t\t\tthis.streams.clear();\n\t\t\t\tthis.activeMonitors.clear();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Error leaving voice channel:\", error);\n\t\t\t}\n\t\t}\n\n\t\tconst connection = joinVoiceChannel({\n\t\t\tchannelId: channel.id,\n\t\t\tguildId: channel.guild.id,\n\t\t\tadapterCreator: channel.guild.voiceAdapterCreator as any,\n\t\t\tselfDeaf: false,\n\t\t\tselfMute: false,\n\t\t\tgroup: this.client.user.id,\n\t\t});\n\n\t\ttry {\n\t\t\t// Wait for either Ready or Signalling state\n\t\t\tawait Promise.race([\n\t\t\t\tentersState(connection, VoiceConnectionStatus.Ready, 20_000),\n\t\t\t\tentersState(connection, VoiceConnectionStatus.Signalling, 20_000),\n\t\t\t]);\n\n\t\t\t// Log connection success\n\t\t\tlogger.log(\n\t\t\t\t`Voice connection established in state: ${connection.state.status}`,\n\t\t\t);\n\n\t\t\t// Set up ongoing state change monitoring\n\t\t\tconnection.on(\"stateChange\", async (oldState, newState) => {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`Voice connection state changed from ${oldState.status} to ${newState.status}`,\n\t\t\t\t);\n\n\t\t\t\tif (newState.status === VoiceConnectionStatus.Disconnected) {\n\t\t\t\t\tlogger.log(\"Handling disconnection...\");\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Try to reconnect if disconnected\n\t\t\t\t\t\tawait Promise.race([\n\t\t\t\t\t\t\tentersState(connection, VoiceConnectionStatus.Signalling, 5_000),\n\t\t\t\t\t\t\tentersState(connection, VoiceConnectionStatus.Connecting, 5_000),\n\t\t\t\t\t\t]);\n\t\t\t\t\t\t// Seems to be reconnecting to a new channel\n\t\t\t\t\t\tlogger.log(\"Reconnecting to channel...\");\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t// Seems to be a real disconnect, destroy and cleanup\n\t\t\t\t\t\tlogger.log(`Disconnection confirmed - cleaning up...${e}`);\n\t\t\t\t\t\tconnection.destroy();\n\t\t\t\t\t\tthis.connections.delete(channel.id);\n\t\t\t\t\t}\n\t\t\t\t} else if (newState.status === VoiceConnectionStatus.Destroyed) {\n\t\t\t\t\tthis.connections.delete(channel.id);\n\t\t\t\t} else if (\n\t\t\t\t\t!this.connections.has(channel.id) &&\n\t\t\t\t\t(newState.status === VoiceConnectionStatus.Ready ||\n\t\t\t\t\t\tnewState.status === VoiceConnectionStatus.Signalling)\n\t\t\t\t) {\n\t\t\t\t\tthis.connections.set(channel.id, connection);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconnection.on(\"error\", (error) => {\n\t\t\t\tlogger.log(\"Voice connection error:\", error);\n\t\t\t\t// Don't immediately destroy - let the state change handler deal with it\n\t\t\t\tlogger.log(\"Connection error - will attempt to recover...\");\n\t\t\t});\n\n\t\t\t// Store the connection\n\t\t\tthis.connections.set(channel.id, connection);\n\n\t\t\t// Continue with voice state modifications\n\t\t\tconst me = channel.guild.members.me;\n\t\t\tif (me?.voice && me.permissions.has(\"DeafenMembers\")) {\n\t\t\t\ttry {\n\t\t\t\t\tawait me.voice.setDeaf(false);\n\t\t\t\t\tawait me.voice.setMute(false);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.log(\"Failed to modify voice state:\", error);\n\t\t\t\t\t// Continue even if this fails\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconnection.receiver.speaking.on(\"start\", async (entityId: string) => {\n\t\t\t\tlet user = channel.members.get(entityId);\n\t\t\t\tif (!user) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuser = await channel.guild.members.fetch(entityId);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconsole.error(\"Failed to fetch user:\", error);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (user && !user?.user.bot) {\n\t\t\t\t\tthis.monitorMember(user as GuildMember, channel);\n\t\t\t\t\tthis.streams.get(entityId)?.emit(\"speakingStarted\");\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconnection.receiver.speaking.on(\"end\", async (entityId: string) => {\n\t\t\t\tconst user = channel.members.get(entityId);\n\t\t\t\tif (!user?.user.bot) {\n\t\t\t\t\tthis.streams.get(entityId)?.emit(\"speakingStopped\");\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tlogger.log(\"Failed to establish voice connection:\", error);\n\t\t\tconnection.destroy();\n\t\t\tthis.connections.delete(channel.id);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Retrieves the voice connection for a given guild ID.\n\t * @param {string} guildId - The ID of the guild to get the voice connection for.\n\t * @returns {VoiceConnection | undefined} The voice connection for the specified guild ID, or undefined if not found.\n\t */\n\tgetVoiceConnection(guildId: string) {\n\t\tconst connections = getVoiceConnections(this.client.user.id);\n\t\tif (!connections) {\n\t\t\treturn;\n\t\t}\n\t\tconst connection = [...connections.values()].find(\n\t\t\t(connection) => connection.joinConfig.guildId === guildId,\n\t\t);\n\t\treturn connection;\n\t}\n\n\t/**\n\t * Monitor a member's audio stream for volume activity and speaking thresholds.\n\t *\n\t * @param {GuildMember} member - The member whose audio stream is being monitored.\n\t * @param {BaseGuildVoiceChannel} channel - The voice channel in which the member is connected.\n\t */\n\tprivate async monitorMember(\n\t\tmember: GuildMember,\n\t\tchannel: BaseGuildVoiceChannel,\n\t) {\n\t\tconst entityId = member?.id;\n\t\tconst userName = member?.user?.username;\n\t\tconst name = member?.user?.displayName;\n\t\tconst connection = this.getVoiceConnection(member?.guild?.id);\n\t\tconst receiveStream = connection?.receiver.subscribe(entityId, {\n\t\t\tautoDestroy: true,\n\t\t\temitClose: true,\n\t\t});\n\t\tif (!receiveStream || receiveStream.readableLength === 0) {\n\t\t\treturn;\n\t\t}\n\t\tconst opusDecoder = new prism.opus.Decoder({\n\t\t\tchannels: 1,\n\t\t\trate: DECODE_SAMPLE_RATE,\n\t\t\tframeSize: DECODE_FRAME_SIZE,\n\t\t});\n\t\tconst volumeBuffer: number[] = [];\n\t\tconst VOLUME_WINDOW_SIZE = 30;\n\t\tconst SPEAKING_THRESHOLD = 0.05;\n\t\topusDecoder.on(\"data\", (pcmData: Buffer) => {\n\t\t\t// Monitor the audio volume while the agent is speaking.\n\t\t\t// If the average volume of the user's audio exceeds the defined threshold, it indicates active speaking.\n\t\t\t// When active speaking is detected, stop the agent's current audio playback to avoid overlap.\n\n\t\t\tif (this.activeAudioPlayer) {\n\t\t\t\tconst samples = new Int16Array(\n\t\t\t\t\tpcmData.buffer,\n\t\t\t\t\tpcmData.byteOffset,\n\t\t\t\t\tpcmData.length / 2,\n\t\t\t\t);\n\t\t\t\tconst maxAmplitude = Math.max(...samples.map(Math.abs)) / 32768;\n\t\t\t\tvolumeBuffer.push(maxAmplitude);\n\n\t\t\t\tif (volumeBuffer.length > VOLUME_WINDOW_SIZE) {\n\t\t\t\t\tvolumeBuffer.shift();\n\t\t\t\t}\n\t\t\t\tconst avgVolume =\n\t\t\t\t\tvolumeBuffer.reduce((sum, v) => sum + v, 0) / VOLUME_WINDOW_SIZE;\n\n\t\t\t\tif (avgVolume > SPEAKING_THRESHOLD) {\n\t\t\t\t\tvolumeBuffer.length = 0;\n\t\t\t\t\tthis.cleanupAudioPlayer(this.activeAudioPlayer);\n\t\t\t\t\tthis.processingVoice = false;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tpipeline(\n\t\t\treceiveStream as AudioReceiveStream,\n\t\t\topusDecoder as any,\n\t\t\t(err: Error | null) => {\n\t\t\t\tif (err) {\n\t\t\t\t\tconsole.log(`Opus decoding pipeline error: ${err}`);\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\t\tthis.streams.set(entityId, opusDecoder);\n\t\tthis.connections.set(entityId, connection as VoiceConnection);\n\t\topusDecoder.on(\"error\", (err: any) => {\n\t\t\tconsole.log(`Opus decoding error: ${err}`);\n\t\t});\n\t\tconst errorHandler = (err: any) => {\n\t\t\tconsole.log(`Opus decoding error: ${err}`);\n\t\t};\n\t\tconst streamCloseHandler = () => {\n\t\t\tconsole.log(`voice stream from ${member?.displayName} closed`);\n\t\t\tthis.streams.delete(entityId);\n\t\t\tthis.connections.delete(entityId);\n\t\t};\n\t\tconst closeHandler = () => {\n\t\t\tconsole.log(`Opus decoder for ${member?.displayName} closed`);\n\t\t\topusDecoder.removeListener(\"error\", errorHandler);\n\t\t\topusDecoder.removeListener(\"close\", closeHandler);\n\t\t\treceiveStream?.removeListener(\"close\", streamCloseHandler);\n\t\t};\n\t\topusDecoder.on(\"error\", errorHandler);\n\t\topusDecoder.on(\"close\", closeHandler);\n\t\treceiveStream?.on(\"close\", streamCloseHandler);\n\n\t\tthis.client.emit(\n\t\t\t\"userStream\",\n\t\t\tentityId,\n\t\t\tname,\n\t\t\tuserName,\n\t\t\tchannel,\n\t\t\topusDecoder,\n\t\t);\n\t}\n\n\t/**\n\t * Leaves the specified voice channel and stops monitoring all members in that channel.\n\t * If there is an active connection in the channel, it will be destroyed.\n\t *\n\t * @param {BaseGuildVoiceChannel} channel - The voice channel to leave.\n\t */\n\tleaveChannel(channel: BaseGuildVoiceChannel) {\n\t\tconst connection = this.connections.get(channel.id);\n\t\tif (connection) {\n\t\t\tconnection.destroy();\n\t\t\tthis.connections.delete(channel.id);\n\t\t}\n\n\t\t// Stop monitoring all members in this channel\n\t\tfor (const [memberId, monitorInfo] of this.activeMonitors) {\n\t\t\tif (\n\t\t\t\tmonitorInfo.channel.id === channel.id &&\n\t\t\t\tmemberId !== this.client.user?.id\n\t\t\t) {\n\t\t\t\tthis.stopMonitoringMember(memberId);\n\t\t\t}\n\t\t}\n\n\t\tconsole.log(`Left voice channel: ${channel.name} (${channel.id})`);\n\t}\n\n\t/**\n\t * Stop monitoring a specific member by their member ID.\n\t * @param {string} memberId - The ID of the member to stop monitoring.\n\t */\n\tstopMonitoringMember(memberId: string) {\n\t\tconst monitorInfo = this.activeMonitors.get(memberId);\n\t\tif (monitorInfo) {\n\t\t\tmonitorInfo.monitor.stop();\n\t\t\tthis.activeMonitors.delete(memberId);\n\t\t\tthis.streams.delete(memberId);\n\t\t\tconsole.log(`Stopped monitoring user ${memberId}`);\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously debounces the process transcription function to prevent rapid execution.\n\t *\n\t * @param {UUID} entityId - The ID of the entity related to the transcription.\n\t * @param {string} name - The name of the entity for transcription.\n\t * @param {string} userName - The username of the user initiating the transcription.\n\t * @param {BaseGuildVoiceChannel} channel - The voice channel where the transcription is happening.\n\t */\n\n\tasync debouncedProcessTranscription(\n\t\tentityId: UUID,\n\t\tname: string,\n\t\tuserName: string,\n\t\tchannel: BaseGuildVoiceChannel,\n\t) {\n\t\tconst DEBOUNCE_TRANSCRIPTION_THRESHOLD = 1500; // wait for 1.5 seconds of silence\n\n\t\tif (this.activeAudioPlayer?.state?.status === \"idle\") {\n\t\t\tlogger.log(\"Cleaning up idle audio player.\");\n\t\t\tthis.cleanupAudioPlayer(this.activeAudioPlayer);\n\t\t}\n\n\t\tif (this.activeAudioPlayer || this.processingVoice) {\n\t\t\tconst state = this.userStates.get(entityId);\n\t\t\tstate.buffers.length = 0;\n\t\t\tstate.totalLength = 0;\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.transcriptionTimeout) {\n\t\t\tclearTimeout(this.transcriptionTimeout);\n\t\t}\n\n\t\tthis.transcriptionTimeout = setTimeout(async () => {\n\t\t\tthis.processingVoice = true;\n\t\t\ttry {\n\t\t\t\tawait this.processTranscription(\n\t\t\t\t\tentityId,\n\t\t\t\t\tchannel.id,\n\t\t\t\t\tchannel,\n\t\t\t\t\tname,\n\t\t\t\t\tuserName,\n\t\t\t\t);\n\n\t\t\t\t// Clean all users' previous buffers\n\t\t\t\tthis.userStates.forEach((state, _) => {\n\t\t\t\t\tstate.buffers.length = 0;\n\t\t\t\t\tstate.totalLength = 0;\n\t\t\t\t});\n\t\t\t} finally {\n\t\t\t\tthis.processingVoice = false;\n\t\t\t}\n\t\t}, DEBOUNCE_TRANSCRIPTION_THRESHOLD) as unknown as NodeJS.Timeout;\n\t}\n\n\t/**\n\t * Handle user audio stream for monitoring purposes.\n\t *\n\t * @param {UUID} userId - The unique identifier of the user.\n\t * @param {string} name - The name of the user.\n\t * @param {string} userName - The username of the user.\n\t * @param {BaseGuildVoiceChannel} channel - The voice channel the user is in.\n\t * @param {Readable} audioStream - The audio stream to monitor.\n\t */\n\tasync handleUserStream(\n\t\tuserId: UUID,\n\t\tname: string,\n\t\tuserName: string,\n\t\tchannel: BaseGuildVoiceChannel,\n\t\taudioStream: Readable,\n\t) {\n\t\tconst entityId = createUniqueUuid(this.runtime, userId);\n\t\tconsole.log(`Starting audio monitor for user: ${entityId}`);\n\t\tif (!this.userStates.has(entityId)) {\n\t\t\tthis.userStates.set(entityId, {\n\t\t\t\tbuffers: [],\n\t\t\t\ttotalLength: 0,\n\t\t\t\tlastActive: Date.now(),\n\t\t\t\ttranscriptionText: \"\",\n\t\t\t});\n\t\t}\n\n\t\tconst state = this.userStates.get(entityId);\n\n\t\tconst processBuffer = async (buffer: Buffer) => {\n\t\t\ttry {\n\t\t\t\tstate?.buffers.push(buffer);\n\t\t\t\tstate!.totalLength += buffer.length;\n\t\t\t\tstate!.lastActive = Date.now();\n\t\t\t\tthis.debouncedProcessTranscription(entityId, name, userName, channel);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(`Error processing buffer for user ${entityId}:`, error);\n\t\t\t}\n\t\t};\n\n\t\tnew AudioMonitor(\n\t\t\taudioStream,\n\t\t\t10000000,\n\t\t\t() => {\n\t\t\t\tif (this.transcriptionTimeout) {\n\t\t\t\t\tclearTimeout(this.transcriptionTimeout);\n\t\t\t\t}\n\t\t\t},\n\t\t\tasync (buffer) => {\n\t\t\t\tif (!buffer) {\n\t\t\t\t\tconsole.error(\"Received empty buffer\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait processBuffer(buffer);\n\t\t\t},\n\t\t);\n\t}\n\n\t/**\n\t * Process the transcription of audio data for a user.\n\t *\n\t * @param {UUID} entityId - The unique ID of the user entity.\n\t * @param {string} channelId - The ID of the channel where the transcription is taking place.\n\t * @param {BaseGuildVoiceChannel} channel - The voice channel where the user is speaking.\n\t * @param {string} name - The name of the user.\n\t * @param {string} userName - The username of the user.\n\t * @returns {Promise<void>}\n\t */\n\tprivate async processTranscription(\n\t\tentityId: UUID,\n\t\tchannelId: string,\n\t\tchannel: BaseGuildVoiceChannel,\n\t\tname: string,\n\t\tuserName: string,\n\t) {\n\t\tconst state = this.userStates.get(entityId);\n\t\tif (!state || state.buffers.length === 0) return;\n\t\ttry {\n\t\t\tconst inputBuffer = Buffer.concat(state.buffers, state.totalLength);\n\n\t\t\tstate.buffers.length = 0; // Clear the buffers\n\t\t\tstate.totalLength = 0;\n\t\t\t// Convert Opus to WAV\n\t\t\tconst wavBuffer = await this.convertOpusToWav(inputBuffer);\n\t\t\tconsole.log(\"Starting transcription...\");\n\n\t\t\tconst transcriptionText = await this.runtime.useModel(\n\t\t\t\tModelTypes.TRANSCRIPTION,\n\t\t\t\twavBuffer,\n\t\t\t);\n\t\t\tfunction isValidTranscription(text: string): boolean {\n\t\t\t\tif (!text || text.includes(\"[BLANK_AUDIO]\")) return false;\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (transcriptionText && isValidTranscription(transcriptionText)) {\n\t\t\t\tstate.transcriptionText += transcriptionText;\n\t\t\t}\n\n\t\t\tif (state.transcriptionText.length) {\n\t\t\t\tthis.cleanupAudioPlayer(this.activeAudioPlayer);\n\t\t\t\tconst finalText = state.transcriptionText;\n\t\t\t\tstate.transcriptionText = \"\";\n\t\t\t\tawait this.handleMessage(\n\t\t\t\t\tfinalText,\n\t\t\t\t\tentityId,\n\t\t\t\t\tchannelId,\n\t\t\t\t\tchannel,\n\t\t\t\t\tname,\n\t\t\t\t\tuserName,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error transcribing audio for user ${entityId}:`, error);\n\t\t}\n\t}\n\n\t/**\n\t * Handles a voice message received in a Discord channel.\n\t *\n\t * @param {string} message - The message content.\n\t * @param {UUID} entityId - The entity ID associated with the message.\n\t * @param {string} channelId - The ID of the Discord channel where the message was received.\n\t * @param {BaseGuildVoiceChannel} channel - The Discord channel where the message was received.\n\t * @param {string} name - The name associated with the message.\n\t * @param {string} userName - The user name associated with the message.\n\t * @returns {Promise<{text: string, actions: string[]}>} Object containing the resulting text and actions.\n\t */\n\tprivate async handleMessage(\n\t\tmessage: string,\n\t\tentityId: UUID,\n\t\tchannelId: string,\n\t\tchannel: BaseGuildVoiceChannel,\n\t\tname: string,\n\t\tuserName: string,\n\t) {\n\t\ttry {\n\t\t\tif (!message || message.trim() === \"\" || message.length < 3) {\n\t\t\t\treturn { text: \"\", actions: [\"IGNORE\"] };\n\t\t\t}\n\n\t\t\tconst roomId = createUniqueUuid(this.runtime, channelId);\n\t\t\tconst type = await this.getChannelType(channel as Channel);\n\n\t\t\tawait this.runtime.ensureConnection({\n\t\t\t\tentityId,\n\t\t\t\troomId,\n\t\t\t\tuserName,\n\t\t\t\tname: name,\n\t\t\t\tsource: \"discord\",\n\t\t\t\tchannelId,\n\t\t\t\tserverId: channel.guild.id,\n\t\t\t\ttype,\n\t\t\t});\n\n\t\t\tconst memory: Memory = {\n\t\t\t\tid: createUniqueUuid(\n\t\t\t\t\tthis.runtime,\n\t\t\t\t\t`${channelId}-voice-message-${Date.now()}`,\n\t\t\t\t),\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\tentityId,\n\t\t\t\troomId,\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: message,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\turl: channel.url,\n\t\t\t\t\tname: name,\n\t\t\t\t\tuserName: userName,\n\t\t\t\t\tisVoiceMessage: true,\n\t\t\t\t\tchannelType: type,\n\t\t\t\t},\n\t\t\t\tcreatedAt: Date.now(),\n\t\t\t};\n\n\t\t\tconst callback: HandlerCallback = async (\n\t\t\t\tcontent: Content,\n\t\t\t\t_files: any[] = [],\n\t\t\t) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst responseMemory: Memory = {\n\t\t\t\t\t\tid: createUniqueUuid(\n\t\t\t\t\t\t\tthis.runtime,\n\t\t\t\t\t\t\t`${memory.id}-voice-response-${Date.now()}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tentityId: this.runtime.agentId,\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t...content,\n\t\t\t\t\t\t\tname: this.runtime.character.name,\n\t\t\t\t\t\t\tinReplyTo: memory.id,\n\t\t\t\t\t\t\tisVoiceMessage: true,\n\t\t\t\t\t\t\tchannelType: type,\n\t\t\t\t\t\t},\n\t\t\t\t\t\troomId,\n\t\t\t\t\t\tcreatedAt: Date.now(),\n\t\t\t\t\t};\n\n\t\t\t\t\tif (responseMemory.content.text?.trim()) {\n\t\t\t\t\t\tawait this.runtime\n\t\t\t\t\t\t\t.getMemoryManager(\"messages\")\n\t\t\t\t\t\t\t.createMemory(responseMemory);\n\n\t\t\t\t\t\tconst responseStream = await this.runtime.useModel(\n\t\t\t\t\t\t\tModelTypes.TEXT_TO_SPEECH,\n\t\t\t\t\t\t\tcontent.text,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (responseStream) {\n\t\t\t\t\t\t\tawait this.playAudioStream(entityId, responseStream as Readable);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn [responseMemory];\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(\"Error in voice message callback:\", error);\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// Emit voice-specific events\n\t\t\tthis.runtime.emitEvent(\n\t\t\t\t[\"DISCORD_VOICE_MESSAGE_RECEIVED\", \"VOICE_MESSAGE_RECEIVED\"],\n\t\t\t\t{\n\t\t\t\t\truntime: this.runtime,\n\t\t\t\t\tmessage: memory,\n\t\t\t\t\tcallback,\n\t\t\t\t},\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error processing voice message:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously converts an Opus audio Buffer to a WAV audio Buffer.\n\t *\n\t * @param {Buffer} pcmBuffer - The Opus audio Buffer to convert to WAV.\n\t * @returns {Promise<Buffer>} A Promise that resolves with the converted WAV audio Buffer.\n\t */\n\tprivate async convertOpusToWav(pcmBuffer: Buffer): Promise<Buffer> {\n\t\ttry {\n\t\t\t// Generate the WAV header\n\t\t\tconst wavHeader = getWavHeader(pcmBuffer.length, DECODE_SAMPLE_RATE);\n\n\t\t\t// Concatenate the WAV header and PCM data\n\t\t\tconst wavBuffer = Buffer.concat([wavHeader, pcmBuffer]);\n\n\t\t\treturn wavBuffer;\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error converting PCM to WAV:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Scans the given Discord guild to select a suitable voice channel to join.\n\t *\n\t * @param {Guild} guild The Discord guild to scan for voice channels.\n\t */\n\tasync scanGuild(guild: Guild) {\n\t\tlet chosenChannel: BaseGuildVoiceChannel | null = null;\n\n\t\ttry {\n\t\t\tconst channelId = this.runtime.getSetting(\n\t\t\t\t\"DISCORD_VOICE_CHANNEL_ID\",\n\t\t\t) as string;\n\t\t\tif (channelId) {\n\t\t\t\tconst channel = await guild.channels.fetch(channelId);\n\t\t\t\tif (channel?.isVoiceBased()) {\n\t\t\t\t\tchosenChannel = channel as BaseGuildVoiceChannel;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!chosenChannel) {\n\t\t\t\tconst channels = (await guild.channels.fetch()).filter(\n\t\t\t\t\t(channel) => channel?.type === DiscordChannelType.GuildVoice,\n\t\t\t\t);\n\t\t\t\tfor (const [, channel] of channels) {\n\t\t\t\t\tconst voiceChannel = channel as BaseGuildVoiceChannel;\n\t\t\t\t\tif (\n\t\t\t\t\t\tvoiceChannel.members.size > 0 &&\n\t\t\t\t\t\t(chosenChannel === null ||\n\t\t\t\t\t\t\tvoiceChannel.members.size > chosenChannel.members.size)\n\t\t\t\t\t) {\n\t\t\t\t\t\tchosenChannel = voiceChannel;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (chosenChannel) {\n\t\t\t\tconsole.log(`Joining channel: ${chosenChannel.name}`);\n\t\t\t\tawait this.joinChannel(chosenChannel);\n\t\t\t} else {\n\t\t\t\tconsole.warn(\"No suitable voice channel found to join.\");\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error selecting or joining a voice channel:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Play an audio stream for a given entity ID.\n\t *\n\t * @param {UUID} entityId - The ID of the entity to play the audio for.\n\t * @param {Readable} audioStream - The audio stream to play.\n\t * @returns {void}\n\t */\n\tasync playAudioStream(entityId: UUID, audioStream: Readable) {\n\t\tconst connection = this.connections.get(entityId);\n\t\tif (connection == null) {\n\t\t\tconsole.log(`No connection for user ${entityId}`);\n\t\t\treturn;\n\t\t}\n\t\tthis.cleanupAudioPlayer(this.activeAudioPlayer);\n\t\tconst audioPlayer = createAudioPlayer({\n\t\t\tbehaviors: {\n\t\t\t\tnoSubscriber: NoSubscriberBehavior.Pause,\n\t\t\t},\n\t\t});\n\t\tthis.activeAudioPlayer = audioPlayer;\n\t\tconnection.subscribe(audioPlayer);\n\n\t\tconst audioStartTime = Date.now();\n\n\t\tconst resource = createAudioResource(audioStream, {\n\t\t\tinputType: StreamType.Arbitrary,\n\t\t});\n\t\taudioPlayer.play(resource);\n\n\t\taudioPlayer.on(\"error\", (err: any) => {\n\t\t\tconsole.log(`Audio player error: ${err}`);\n\t\t});\n\n\t\taudioPlayer.on(\n\t\t\t\"stateChange\",\n\t\t\t(_oldState: any, newState: { status: string }) => {\n\t\t\t\tif (newState.status === \"idle\") {\n\t\t\t\t\tconst idleTime = Date.now();\n\t\t\t\t\tconsole.log(`Audio playback took: ${idleTime - audioStartTime}ms`);\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\t}\n\n\t/**\n\t * Cleans up the provided audio player by stopping it, removing all listeners,\n\t * and resetting the active audio player if it matches the provided player.\n\t *\n\t * @param {AudioPlayer} audioPlayer - The audio player to be cleaned up.\n\t */\n\tcleanupAudioPlayer(audioPlayer: AudioPlayer) {\n\t\tif (!audioPlayer) return;\n\n\t\taudioPlayer.stop();\n\t\taudioPlayer.removeAllListeners();\n\t\tif (audioPlayer === this.activeAudioPlayer) {\n\t\t\tthis.activeAudioPlayer = null;\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously handles the join channel command in an interaction.\n\t *\n\t * @param {any} interaction - The interaction object representing the user's input.\n\t * @returns {Promise<void>} - A promise that resolves once the join channel command is handled.\n\t */\n\tasync handleJoinChannelCommand(interaction: any) {\n\t\ttry {\n\t\t\t// Defer the reply immediately to prevent interaction timeout\n\t\t\tawait interaction.deferReply();\n\n\t\t\tconst channelId = interaction.options.get(\"channel\")?.value as string;\n\t\t\tif (!channelId) {\n\t\t\t\tawait interaction.editReply(\"Please provide a voice channel to join.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst guild = interaction.guild;\n\t\t\tif (!guild) {\n\t\t\t\tawait interaction.editReply(\"Could not find guild.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst voiceChannel = interaction.guild.channels.cache.find(\n\t\t\t\t(channel: VoiceChannel) =>\n\t\t\t\t\tchannel.id === channelId &&\n\t\t\t\t\tchannel.type === DiscordChannelType.GuildVoice,\n\t\t\t);\n\n\t\t\tif (!voiceChannel) {\n\t\t\t\tawait interaction.editReply(\"Voice channel not found!\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait this.joinChannel(voiceChannel as BaseGuildVoiceChannel);\n\t\t\tawait interaction.editReply(`Joined voice channel: ${voiceChannel.name}`);\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error joining voice channel:\", error);\n\t\t\t// Use editReply instead of reply for the error case\n\t\t\tawait interaction\n\t\t\t\t.editReply(\"Failed to join the voice channel.\")\n\t\t\t\t.catch(console.error);\n\t\t}\n\t}\n\n\t/**\n\t * Handles the leave channel command by destroying the voice connection if it exists.\n\t *\n\t * @param {any} interaction The interaction object representing the command invocation.\n\t * @returns {void}\n\t */\n\tasync handleLeaveChannelCommand(interaction: any) {\n\t\tconst connection = this.getVoiceConnection(interaction.guildId as any);\n\n\t\tif (!connection) {\n\t\t\tawait interaction.reply(\"Not currently in a voice channel.\");\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconnection.destroy();\n\t\t\tawait interaction.reply(\"Left the voice channel.\");\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error leaving voice channel:\", error);\n\t\t\tawait interaction.reply(\"Failed to leave the voice channel.\");\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["ChannelType","createUniqueUuid","logger","DiscordChannelType","Events","PermissionsBitField","ModelTypes","composePrompt","parseJSONObjectFromText","fs","ModelTypes","composePrompt","parseJSONObjectFromText","trimTokens","summarizationTemplate","summarizeAction","ModelTypes","composePrompt","parseJSONObjectFromText","composePrompt","ModelTypes","parseJSONObjectFromText","attachment","ChannelType","ModelTypes","composePrompt","createUniqueUuid","ServiceTypes","ChannelType","ServiceTypes","member","createUniqueUuid","composePrompt","ModelTypes","targetChannel","ChannelType","createUniqueUuid","logger","ServiceTypes","logger","ChannelType","createUniqueUuid","ChannelType","ServiceTypes","createUniqueUuid","logger","DiscordChannelType","fs","trimTokens","parseJSONObjectFromText","ModelTypes","ServiceTypes","ModelTypes","logger","parseJSONObjectFromText","trimTokens","ChannelType","logger","ChannelType","DiscordChannelType","createUniqueUuid","ChannelType","logger","entityId","match","ServiceTypes","ChannelType","ChannelType","ServiceTypes","ChannelType","ModelTypes","logger","ChannelType","ServiceTypes","logger","ChannelType","ModelTypes","NoSubscriberBehavior","VoiceConnectionStatus","createAudioPlayer","createAudioResource","entersState","ChannelType","ModelTypes","createUniqueUuid","logger","DiscordChannelType","logger","DiscordChannelType","ChannelType","entersState","VoiceConnectionStatus","connection","createUniqueUuid","ModelTypes","createAudioPlayer","NoSubscriberBehavior","createAudioResource","logger","Events","runtime","createUniqueUuid","ChannelType","PermissionsBitField","DiscordChannelType","fullGuild"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/actions/chatWithAttachments.ts","../src/actions/downloadMedia.ts","../src/actions/summarizeConversation.ts","../src/actions/transcribeMedia.ts","../src/actions/voiceJoin.ts","../src/types.ts","../src/actions/voiceLeave.ts","../src/constants.ts","../src/messages.ts","../src/attachments.ts","../src/utils.ts","../src/providers/channelState.ts","../src/providers/voiceState.ts","../src/tests.ts","../src/voice.ts"],"sourcesContent":["import {\n\tChannelType,\n\ttype Character,\n\ttype Entity,\n\tEventTypes,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype Memory,\n\ttype Plugin,\n\tRole,\n\tService,\n\ttype UUID,\n\ttype World,\n\tcreateUniqueUuid,\n\tlogger,\n} from \"@elizaos/core\";\nimport {\n\ttype Channel,\n\tChannelType as DiscordChannelType,\n\tClient as DiscordJsClient,\n\tEvents,\n\tGatewayIntentBits,\n\ttype Guild,\n\ttype GuildMember,\n\ttype MessageReaction,\n\ttype OAuth2Guild,\n\ttype PartialMessageReaction,\n\ttype PartialUser,\n\tPartials,\n\tPermissionsBitField,\n\ttype TextChannel,\n\ttype User,\n} from \"discord.js\";\nimport chatWithAttachments from \"./actions/chatWithAttachments\";\nimport downloadMedia from \"./actions/downloadMedia\";\nimport summarize from \"./actions/summarizeConversation\";\nimport transcribe_media from \"./actions/transcribeMedia\";\nimport joinVoice from \"./actions/voiceJoin\";\nimport leaveVoice from \"./actions/voiceLeave\";\nimport { DISCORD_SERVICE_NAME } from \"./constants\";\nimport { MessageManager } from \"./messages\";\nimport channelStateProvider from \"./providers/channelState\";\nimport voiceStateProvider from \"./providers/voiceState\";\nimport { DiscordTestSuite } from \"./tests\";\nimport { DiscordEventTypes, type IDiscordService } from \"./types\";\nimport { VoiceManager } from \"./voice\";\n\n/**\n * DiscordService class representing a service for interacting with Discord.\n * @extends Service\n * @implements IDiscordService\n * @property {string} serviceType - The type of service, set to DISCORD_SERVICE_NAME.\n * @property {string} capabilityDescription - A description of the service's capabilities.\n * @property {DiscordJsClient} client - The DiscordJsClient used for communication.\n * @property {Character} character - The character associated with the service.\n * @property {MessageManager} messageManager - The manager for handling messages.\n * @property {VoiceManager} voiceManager - The manager for handling voice communication.\n */\nexport class DiscordService extends Service implements IDiscordService {\n\tstatic serviceType: string = DISCORD_SERVICE_NAME;\n\tcapabilityDescription =\n\t\t\"The agent is able to send and receive messages on discord\";\n\tclient: DiscordJsClient | null;\n\tcharacter: Character;\n\tmessageManager: MessageManager;\n\tvoiceManager: VoiceManager;\n\n\t/**\n\t * Constructor for Discord client.\n\t * Initializes the Discord client with specified intents and partials,\n\t * sets up event listeners, and ensures all servers exist.\n\t *\n\t * @param {IAgentRuntime} runtime - The AgentRuntime instance\n\t */\n\n\tconstructor(runtime: IAgentRuntime) {\n\t\tsuper(runtime);\n\n\t\t// Check if Discord API token is available and valid\n\t\tconst token = runtime.getSetting(\"DISCORD_API_TOKEN\") as string;\n\t\tif (!token || token.trim() === \"\") {\n\t\t\tlogger.warn(\n\t\t\t\t\"Discord API Token not provided - Discord functionality will be unavailable\",\n\t\t\t);\n\t\t\tthis.client = null;\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.client = new DiscordJsClient({\n\t\t\t\tintents: [\n\t\t\t\t\tGatewayIntentBits.Guilds,\n\t\t\t\t\tGatewayIntentBits.GuildMembers,\n\t\t\t\t\tGatewayIntentBits.GuildPresences,\n\t\t\t\t\tGatewayIntentBits.DirectMessages,\n\t\t\t\t\tGatewayIntentBits.GuildVoiceStates,\n\t\t\t\t\tGatewayIntentBits.MessageContent,\n\t\t\t\t\tGatewayIntentBits.GuildMessages,\n\t\t\t\t\tGatewayIntentBits.DirectMessageTyping,\n\t\t\t\t\tGatewayIntentBits.GuildMessageTyping,\n\t\t\t\t\tGatewayIntentBits.GuildMessageReactions,\n\t\t\t\t],\n\t\t\t\tpartials: [\n\t\t\t\t\tPartials.Channel,\n\t\t\t\t\tPartials.Message,\n\t\t\t\t\tPartials.User,\n\t\t\t\t\tPartials.Reaction,\n\t\t\t\t],\n\t\t\t});\n\n\t\t\tthis.runtime = runtime;\n\t\t\tthis.voiceManager = new VoiceManager(this, runtime);\n\t\t\tthis.messageManager = new MessageManager(this);\n\n\t\t\tthis.client.once(Events.ClientReady, this.onClientReady.bind(this));\n\t\t\tthis.client.login(token).catch((error) => {\n\t\t\t\tlogger.error(`Failed to login to Discord: ${error.message}`);\n\t\t\t\tthis.client = null;\n\t\t\t});\n\n\t\t\tthis.setupEventListeners();\n\n\t\t\t// give it to the\n\t\t\tconst ensureAllServersExist = async (runtime: IAgentRuntime) => {\n\t\t\t\tconst guilds = await this.client.guilds.fetch();\n\t\t\t\tfor (const [, guild] of guilds) {\n\t\t\t\t\tawait this.ensureAllChannelsExist(runtime, guild);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tensureAllServersExist(this.runtime);\n\t\t} catch (error) {\n\t\t\tlogger.error(`Error initializing Discord client: ${error.message}`);\n\t\t\tthis.client = null;\n\t\t}\n\t}\n\n\t/**\n\t * Ensures that all channels exist in the database for a given guild.\n\t * @param {IAgentRuntime} runtime - The agent runtime object.\n\t * @param {OAuth2Guild} guild - The OAuth2Guild object for which channels need to be ensured.\n\t * @returns {Promise<void>} - A Promise that resolves once all channels are ensured.\n\t */\n\tasync ensureAllChannelsExist(runtime: IAgentRuntime, guild: OAuth2Guild) {\n\t\t// fetch the owning member from the OAuth2Guild object\n\t\tlet guildObj;\n\t\tlet guildChannels;\n\t\tlet retries = 3;\n\t\twhile (retries > 0) {\n\t\t\ttry {\n\t\t\t\tguildObj = await guild.fetch();\n\t\t\t\tguildChannels = await guild.fetch();\n\t\t\t\tbreak;\n\t\t\t} catch (error) {\n\t\t\t\tretries--;\n\t\t\t\tif (retries === 0) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tawait new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second between retries\n\t\t\t}\n\t\t}\n\t\t// for channel in channels\n\t\tfor (const [, channel] of guildChannels.channels.cache) {\n\t\t\tconst roomId = createUniqueUuid(this.runtime, channel.id);\n\t\t\tconst room = await runtime.getRoom(roomId);\n\t\t\t// if the room already exists, skip\n\t\t\tif (room) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst worldId = createUniqueUuid(runtime, guild.id);\n\t\t\tconst ownerId = createUniqueUuid(this.runtime, guildObj.ownerId);\n\t\t\tawait runtime.ensureWorldExists({\n\t\t\t\tid: worldId,\n\t\t\t\tname: guild.name,\n\t\t\t\tserverId: guild.id,\n\t\t\t\tagentId: runtime.agentId,\n\t\t\t\tmetadata: {\n\t\t\t\t\townership: guildObj.ownerId ? { ownerId } : undefined,\n\t\t\t\t\troles: {\n\t\t\t\t\t\t[ownerId]: Role.OWNER,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t});\n\t\t\tawait runtime.ensureRoomExists({\n\t\t\t\tid: roomId,\n\t\t\t\tname: channel.name,\n\t\t\t\tsource: \"discord\",\n\t\t\t\ttype: ChannelType.GROUP,\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tserverId: guild.id,\n\t\t\t\tworldId,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Set up event listeners for the client\n\t */\n\tprivate setupEventListeners() {\n\t\tif (!this.client) {\n\t\t\treturn; // Skip if client is not available\n\t\t}\n\n\t\t// Setup handling for direct messages\n\t\tthis.client.on(\"messageCreate\", (message) => {\n\t\t\t// Skip if we're sending the message or in deleted state\n\t\t\tif (message.author.id === this.client?.user?.id || message.author.bot) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tthis.messageManager.handleMessage(message);\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Error handling message: ${error}`);\n\t\t\t}\n\t\t});\n\n\t\t// Setup handling for reactions\n\t\tthis.client.on(\"messageReactionAdd\", async (reaction, user) => {\n\t\t\tif (user.id === this.client?.user?.id) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait this.handleReactionAdd(reaction, user);\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Error handling reaction add: ${error}`);\n\t\t\t}\n\t\t});\n\n\t\t// Handle reaction removal\n\t\tthis.client.on(\"messageReactionRemove\", async (reaction, user) => {\n\t\t\tif (user.id === this.client?.user?.id) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait this.handleReactionRemove(reaction, user);\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Error handling reaction remove: ${error}`);\n\t\t\t}\n\t\t});\n\n\t\t// Setup guild (server) event handlers\n\t\tthis.client.on(\"guildCreate\", async (guild) => {\n\t\t\ttry {\n\t\t\t\tawait this.handleGuildCreate(guild);\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Error handling guild create: ${error}`);\n\t\t\t}\n\t\t});\n\n\t\t// Setup member (user) joining handlers\n\t\tthis.client.on(\"guildMemberAdd\", async (member) => {\n\t\t\ttry {\n\t\t\t\tawait this.handleGuildMemberAdd(member);\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Error handling guild member add: ${error}`);\n\t\t\t}\n\t\t});\n\n\t\t// Interaction handlers\n\t\tthis.client.on(\"interactionCreate\", async (interaction) => {\n\t\t\ttry {\n\t\t\t\tawait this.handleInteractionCreate(interaction);\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Error handling interaction: ${error}`);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Handles the event when a new member joins a guild.\n\t *\n\t * @param {GuildMember} member - The GuildMember object representing the new member that joined the guild.\n\t * @returns {Promise<void>} - A Promise that resolves once the event handling is complete.\n\t */\n\tprivate async handleGuildMemberAdd(member: GuildMember) {\n\t\tlogger.log(`New member joined: ${member.user.username}`);\n\n\t\tconst guild = member.guild;\n\n\t\tconst tag = member.user.bot\n\t\t\t? `${member.user.username}#${member.user.discriminator}`\n\t\t\t: member.user.username;\n\n\t\t// Emit standardized ENTITY_JOINED event\n\t\tthis.runtime.emitEvent([EventTypes.ENTITY_JOINED], {\n\t\t\truntime: this.runtime,\n\t\t\tentityId: createUniqueUuid(this.runtime, member.id),\n\t\t\tworldId: createUniqueUuid(this.runtime, guild.id),\n\t\t\tsource: \"discord\",\n\t\t\tmetadata: {\n\t\t\t\toriginalId: member.id,\n\t\t\t\tusername: tag,\n\t\t\t\tdisplayName: member.displayName || member.user.username,\n\t\t\t\troles: member.roles.cache.map(r => r.name),\n\t\t\t\tjoinedAt: member.joinedAt?.getTime()\n\t\t\t}\n\t\t});\n\n\t\t// Emit Discord-specific event\n\t\tthis.runtime.emitEvent([DiscordEventTypes.ENTITY_JOINED], {\n\t\t\truntime: this.runtime,\n\t\t\tentityId: createUniqueUuid(this.runtime, member.id),\n\t\t\tmember,\n\t\t\tguild,\n\t\t});\n\t}\n\n\t/**\n\t *\n\t * Start the Discord service\n\t * @param {IAgentRuntime} runtime - The runtime for the agent\n\t * @returns {Promise<DiscordService>} A promise that resolves to a DiscordService instance\n\t *\n\t */\n\tstatic async start(runtime: IAgentRuntime): Promise<DiscordService> {\n\t\tconst token = runtime.getSetting(\"DISCORD_API_TOKEN\") as string;\n\t\tif (!token || token.trim() === \"\") {\n\t\t\tthrow new Error(\"Discord API Token not provided\");\n\t\t}\n\n\t\tconst maxRetries = 5;\n\t\tlet retryCount = 0;\n\t\tlet lastError: Error | null = null;\n\n\t\twhile (retryCount < maxRetries) {\n\t\t\ttry {\n\t\t\t\tconst service = new DiscordService(runtime);\n\t\t\t\tif (!service.client) {\n\t\t\t\t\tthrow new Error(\"Failed to initialize Discord client\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// Wait for client to be ready\n\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\tconst timeout = setTimeout(() => {\n\t\t\t\t\t\treject(new Error(\"Discord client ready timeout\"));\n\t\t\t\t\t}, 30000); // 30 second timeout\n\n\t\t\t\t\tservice.client?.once(\"ready\", () => {\n\t\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\t\treturn service;\n\t\t\t} catch (error) {\n\t\t\t\tlastError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tlogger.error(`Discord initialization attempt ${retryCount + 1} failed: ${lastError.message}`);\n\t\t\t\tretryCount++;\n\t\t\t\t\n\t\t\t\tif (retryCount < maxRetries) {\n\t\t\t\t\tconst delay = 2 ** retryCount * 1000; // Exponential backoff\n\t\t\t\t\tlogger.info(`Retrying Discord initialization in ${delay/1000} seconds...`);\n\t\t\t\t\tawait new Promise(resolve => setTimeout(resolve, delay));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(`Discord initialization failed after ${maxRetries} attempts. Last error: ${lastError?.message}`);\n\t}\n\n\t/**\n\t * Stops the Discord client associated with the given runtime.\n\t *\n\t * @param {IAgentRuntime} runtime - The runtime associated with the Discord client.\n\t * @returns {void}\n\t */\n\tstatic async stop(runtime: IAgentRuntime) {\n\t\tconst client = runtime.getService(DISCORD_SERVICE_NAME);\n\t\tif (!client) {\n\t\t\tlogger.error(\"DiscordService not found\");\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\t// disconnect websocket\n\t\t\t// this unbinds all the listeners\n\t\t\tawait client.stop();\n\t\t} catch (e) {\n\t\t\tlogger.error(\"client-discord instance stop err\", e);\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously stops the client by destroying it.\n\t *\n\t * @returns {Promise<void>}\n\t */\n\tasync stop() {\n\t\tawait this.client?.destroy();\n\t}\n\n\t/**\n\t * Handle the event when the client is ready.\n\t * @param {Object} readyClient - The ready client object containing user information.\n\t * @param {string} readyClient.user.tag - The username and discriminator of the client user.\n\t * @param {string} readyClient.user.id - The user ID of the client.\n\t * @returns {Promise<void>}\n\t */\n\tprivate async onClientReady(readyClient: { user: { tag: any; id: any } }) {\n\t\tlogger.success(`DISCORD: Logged in as ${readyClient.user?.tag}`);\n\n\t\t// Register slash commands\n\t\tconst commands = [\n\t\t\t{\n\t\t\t\tname: \"joinchannel\",\n\t\t\t\tdescription: \"Join a voice channel\",\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"channel\",\n\t\t\t\t\t\ttype: 7, // CHANNEL type\n\t\t\t\t\t\tdescription: \"The voice channel to join\",\n\t\t\t\t\t\trequired: true,\n\t\t\t\t\t\tchannel_types: [2], // GuildVoice type\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"leavechannel\",\n\t\t\t\tdescription: \"Leave the current voice channel\",\n\t\t\t},\n\t\t];\n\n\t\ttry {\n\t\t\tawait this.client?.application?.commands.set(commands);\n\t\t\tlogger.success(\"DISCORD: Slash commands registered\");\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error registering slash commands:\", error);\n\t\t}\n\n\t\t// Required permissions for the bot\n\t\tconst requiredPermissions = [\n\t\t\t// Text Permissions\n\t\t\tPermissionsBitField.Flags.ViewChannel,\n\t\t\tPermissionsBitField.Flags.SendMessages,\n\t\t\tPermissionsBitField.Flags.SendMessagesInThreads,\n\t\t\tPermissionsBitField.Flags.CreatePrivateThreads,\n\t\t\tPermissionsBitField.Flags.CreatePublicThreads,\n\t\t\tPermissionsBitField.Flags.EmbedLinks,\n\t\t\tPermissionsBitField.Flags.AttachFiles,\n\t\t\tPermissionsBitField.Flags.AddReactions,\n\t\t\tPermissionsBitField.Flags.UseExternalEmojis,\n\t\t\tPermissionsBitField.Flags.UseExternalStickers,\n\t\t\tPermissionsBitField.Flags.MentionEveryone,\n\t\t\tPermissionsBitField.Flags.ManageMessages,\n\t\t\tPermissionsBitField.Flags.ReadMessageHistory,\n\t\t\t// Voice Permissions\n\t\t\tPermissionsBitField.Flags.Connect,\n\t\t\tPermissionsBitField.Flags.Speak,\n\t\t\tPermissionsBitField.Flags.UseVAD,\n\t\t\tPermissionsBitField.Flags.PrioritySpeaker,\n\t\t].reduce((a, b) => a | b, 0n);\n\n\t\tlogger.success(\"Use this URL to add the bot to your server:\");\n\t\tlogger.success(\n\t\t\t`https://discord.com/api/oauth2/authorize?client_id=${readyClient.user?.id}&permissions=${requiredPermissions}&scope=bot%20applications.commands`,\n\t\t);\n\t\tawait this.onReady();\n\t}\n\n\t/**\n\t * Asynchronously retrieves the type of a given channel.\n\t *\n\t * @param {Channel} channel - The channel for which to determine the type.\n\t * @returns {Promise<ChannelType>} A Promise that resolves with the type of the channel.\n\t */\n\tasync getChannelType(channel: Channel): Promise<ChannelType> {\n\t\tswitch (channel.type) {\n\t\t\tcase DiscordChannelType.DM:\n\t\t\t\treturn ChannelType.DM;\n\t\t\tcase DiscordChannelType.GuildText:\n\t\t\t\treturn ChannelType.GROUP;\n\t\t\tcase DiscordChannelType.GuildVoice:\n\t\t\t\treturn ChannelType.VOICE_GROUP;\n\t\t}\n\t}\n\n\t/**\n\t * Handles the addition of a reaction on a message.\n\t *\n\t * @param {MessageReaction | PartialMessageReaction} reaction The reaction that was added.\n\t * @param {User | PartialUser} user The user who added the reaction.\n\t * @returns {void}\n\t */\n\tasync handleReactionAdd(reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser) {\n\t\ttry {\n\t\t\tlogger.log(\"Reaction added\");\n\n\t\t\t// Early returns\n\t\t\tif (!reaction || !user) {\n\t\t\t\tlogger.warn(\"Invalid reaction or user\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Get emoji info\n\t\t\tlet emoji = reaction.emoji.name;\n\t\t\tif (!emoji && reaction.emoji.id) {\n\t\t\t\temoji = `<:${reaction.emoji.name}:${reaction.emoji.id}>`;\n\t\t\t}\n\n\t\t\t// Fetch full message if partial\n\t\t\tif (reaction.partial) {\n\t\t\t\ttry {\n\t\t\t\t\tawait reaction.fetch();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.error(\"Failed to fetch partial reaction:\", error);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Generate IDs with timestamp to ensure uniqueness\n\t\t\tconst timestamp = Date.now();\n\t\t\tconst roomId = createUniqueUuid(\n\t\t\t\tthis.runtime,\n\t\t\t\treaction.message.channel.id,\n\t\t\t);\n\t\t\tconst entityId = createUniqueUuid(this.runtime, user.id);\n\t\t\tconst reactionUUID = createUniqueUuid(\n\t\t\t\tthis.runtime,\n\t\t\t\t`${reaction.message.id}-${user.id}-${emoji}-${timestamp}`,\n\t\t\t);\n\n\t\t\t// Validate IDs\n\t\t\tif (!entityId || !roomId) {\n\t\t\t\tlogger.error(\"Invalid user ID or room ID\", {\n\t\t\t\t\tentityId,\n\t\t\t\t\troomId,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Process message content\n\t\t\tconst messageContent = reaction.message.content || \"\";\n\t\t\tconst truncatedContent =\n\t\t\t\tmessageContent.length > 50\n\t\t\t\t\t? `${messageContent.substring(0, 50)}...`\n\t\t\t\t\t: messageContent;\n\t\t\tconst reactionMessage = `*Added <${emoji}> to: \"${truncatedContent}\"*`;\n\n\t\t\t// Get user info\n\t\t\tconst userName = reaction.message.author?.username || \"unknown\";\n\t\t\tconst name = reaction.message.author?.displayName || userName;\n\n\t\t\t// TODO: Get the type of the channel\n\n\t\t\tawait this.runtime.ensureConnection({\n\t\t\t\tentityId,\n\t\t\t\troomId,\n\t\t\t\tuserName,\n\t\t\t\tname: name,\n\t\t\t\tsource: \"discord\",\n\t\t\t\tchannelId: reaction.message.channel.id,\n\t\t\t\tserverId: reaction.message.guild?.id,\n\t\t\t\ttype: await this.getChannelType(reaction.message.channel as Channel),\n\t\t\t});\n\n\t\t\tconst inReplyTo = createUniqueUuid(this.runtime, reaction.message.id);\n\n\t\t\tconst memory: Memory = {\n\t\t\t\tid: reactionUUID,\n\t\t\t\tentityId,\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\tcontent: {\n\t\t\t\t\t// name,\n\t\t\t\t\t// userName,\n\t\t\t\t\ttext: reactionMessage,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tinReplyTo,\n\t\t\t\t\tchannelType: await this.getChannelType(\n\t\t\t\t\t\treaction.message.channel as Channel,\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t\troomId,\n\t\t\t\tcreatedAt: timestamp,\n\t\t\t};\n\n\t\t\tconst callback: HandlerCallback = async (content) => {\n\t\t\t\tif (!reaction.message.channel) {\n\t\t\t\t\tlogger.error(\"No channel found for reaction message\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait (reaction.message.channel as TextChannel).send(content.text);\n\t\t\t\treturn [];\n\t\t\t};\n\n\t\t\tthis.runtime.emitEvent(\n\t\t\t\t[\"DISCORD_REACTION_RECEIVED\", \"REACTION_RECEIVED\"],\n\t\t\t\t{\n\t\t\t\t\truntime: this.runtime,\n\t\t\t\t\tmessage: memory,\n\t\t\t\t\tcallback,\n\t\t\t\t},\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error handling reaction:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Handles the removal of a reaction on a message.\n\t *\n\t * @param {MessageReaction | PartialMessageReaction} reaction - The reaction that was removed.\n\t * @param {User | PartialUser} user - The user who removed the reaction.\n\t * @returns {Promise<void>} - A Promise that resolves after handling the reaction removal.\n\t */\n\tasync handleReactionRemove(reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser) {\n\t\ttry {\n\t\t\tlogger.log(\"Reaction removed\");\n\n\t\t\tlet emoji = reaction.emoji.name;\n\t\t\tif (!emoji && reaction.emoji.id) {\n\t\t\t\temoji = `<:${reaction.emoji.name}:${reaction.emoji.id}>`;\n\t\t\t}\n\n\t\t\t// Fetch the full message if it's a partial\n\t\t\tif (reaction.partial) {\n\t\t\t\ttry {\n\t\t\t\t\tawait reaction.fetch();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t\"Something went wrong when fetching the message:\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst messageContent = reaction.message.content || \"\";\n\t\t\tconst truncatedContent =\n\t\t\t\tmessageContent.length > 50\n\t\t\t\t\t? `${messageContent.substring(0, 50)}...`\n\t\t\t\t\t: messageContent;\n\n\t\t\tconst reactionMessage = `*Removed <${emoji}> from: \"${truncatedContent}\"*`;\n\n\t\t\tconst roomId = createUniqueUuid(\n\t\t\t\tthis.runtime,\n\t\t\t\treaction.message.channel.id,\n\t\t\t);\n\n\t\t\tconst entityId = createUniqueUuid(this.runtime, user.id);\n\t\t\tconst timestamp = Date.now();\n\t\t\tconst reactionUUID = createUniqueUuid(\n\t\t\t\tthis.runtime,\n\t\t\t\t`${reaction.message.id}-${user.id}-${emoji}-${timestamp}`,\n\t\t\t);\n\n\t\t\tconst userName = reaction.message.author?.username || \"unknown\";\n\t\t\tconst name = reaction.message.author?.displayName || userName;\n\n\t\t\tawait this.runtime.ensureConnection({\n\t\t\t\tentityId,\n\t\t\t\troomId,\n\t\t\t\tuserName,\n\t\t\t\tname: name,\n\t\t\t\tsource: \"discord\",\n\t\t\t\tchannelId: reaction.message.channel.id,\n\t\t\t\tserverId: reaction.message.guild?.id,\n\t\t\t\ttype: await this.getChannelType(reaction.message.channel as Channel),\n\t\t\t});\n\n\t\t\tconst memory: Memory = {\n\t\t\t\tid: reactionUUID,\n\t\t\t\tentityId,\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\tcontent: {\n\t\t\t\t\t// name,\n\t\t\t\t\t// userName,\n\t\t\t\t\ttext: reactionMessage,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tinReplyTo: createUniqueUuid(this.runtime, reaction.message.id),\n\t\t\t\t\tchannelType: await this.getChannelType(\n\t\t\t\t\t\treaction.message.channel as Channel,\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t\troomId,\n\t\t\t\tcreatedAt: Date.now(),\n\t\t\t};\n\n\t\t\tconst callback: HandlerCallback = async (content) => {\n\t\t\t\tif (!reaction.message.channel) {\n\t\t\t\t\tlogger.error(\"No channel found for reaction message\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait (reaction.message.channel as TextChannel).send(content.text);\n\t\t\t\treturn [];\n\t\t\t};\n\n\t\t\tthis.runtime.emitEvent([DiscordEventTypes.REACTION_RECEIVED], {\n\t\t\t\truntime: this.runtime,\n\t\t\t\tmessage: memory,\n\t\t\t\tcallback,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error handling reaction removal:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * 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\t * @param {Guild} guild - The guild that the bot has joined.\n\t * @returns {Promise<void>}\n\t */\n\tprivate async handleGuildCreate(guild: Guild) {\n\t\tlogger.log(`Joined guild ${guild.name}`);\n\t\tconst fullGuild = await guild.fetch();\n\t\tthis.voiceManager.scanGuild(guild);\n\n\t\tconst ownerId = createUniqueUuid(this.runtime, fullGuild.ownerId);\n\n\t\t// Create standardized world data structure\n\t\tconst worldId = createUniqueUuid(this.runtime, fullGuild.id);\n\t\tconst standardizedData = {\n\t\t\truntime: this.runtime,\n\t\t\trooms: await this.buildStandardizedRooms(fullGuild, worldId),\n\t\t\tusers: await this.buildStandardizedUsers(fullGuild),\n\t\t\tworld: {\n\t\t\t\tid: worldId,\n\t\t\t\tname: fullGuild.name,\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\tserverId: fullGuild.id,\n\t\t\t\tmetadata: {\n\t\t\t\t\townership: fullGuild.ownerId ? { ownerId: ownerId } : undefined,\n\t\t\t\t\troles: {\n\t\t\t\t\t\t[ownerId]: Role.OWNER,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t} as World,\n\t\t\tsource: \"discord\",\n\t\t};\n\n\t\t// Emit both Discord-specific and standardized events with the same data structure\n\t\tthis.runtime.emitEvent([DiscordEventTypes.WORLD_JOINED], {\n\t\t\truntime: this.runtime,\n\t\t\tserver: fullGuild,\n\t\t\tsource: \"discord\",\n\t\t});\n\n\t\t// Emit standardized event with the same structure as WORLD_CONNECTED\n\t\tthis.runtime.emitEvent([EventTypes.WORLD_JOINED], standardizedData);\n\t}\n\n\t/**\n\t * Handles interactions created by the user, specifically commands.\n\t * @param {any} interaction - The interaction object received\n\t * @returns {void}\n\t */\n\tprivate async handleInteractionCreate(interaction: any) {\n\t\tif (!interaction.isCommand()) return;\n\n\t\tswitch (interaction.commandName) {\n\t\t\tcase \"joinchannel\":\n\t\t\t\tawait this.voiceManager.handleJoinChannelCommand(interaction);\n\t\t\t\tbreak;\n\t\t\tcase \"leavechannel\":\n\t\t\t\tawait this.voiceManager.handleLeaveChannelCommand(interaction);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Builds a standardized list of rooms from Discord guild channels\n\t */\n\t/**\n\t * Build standardized rooms for a guild based on text and voice channels.\n\t *\n\t * @param {Guild} guild The guild to build rooms for.\n\t * @param {UUID} _worldId The ID of the world to associate with the rooms.\n\t * @returns {Promise<any[]>} An array of standardized room objects.\n\t */\n\tprivate async buildStandardizedRooms(\n\t\tguild: Guild,\n\t\t_worldId: UUID,\n\t): Promise<any[]> {\n\t\tconst rooms = [];\n\n\t\tfor (const [channelId, channel] of guild.channels.cache) {\n\t\t\t// Only process text and voice channels\n\t\t\tif (\n\t\t\t\tchannel.type === DiscordChannelType.GuildText ||\n\t\t\t\tchannel.type === DiscordChannelType.GuildVoice\n\t\t\t) {\n\t\t\t\tconst roomId = createUniqueUuid(this.runtime, channelId);\n\t\t\t\tlet channelType;\n\n\t\t\t\tswitch (channel.type) {\n\t\t\t\t\tcase DiscordChannelType.GuildText:\n\t\t\t\t\t\tchannelType = ChannelType.GROUP;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase DiscordChannelType.GuildVoice:\n\t\t\t\t\t\tchannelType = ChannelType.VOICE_GROUP;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tchannelType = ChannelType.GROUP;\n\t\t\t\t}\n\n\t\t\t\t// For text channels, we could potentially get member permissions\n\t\t\t\t// But for performance reasons, keep this light for large guilds\n\t\t\t\tlet participants: UUID[] = [];\n\n\t\t\t\tif (\n\t\t\t\t\tguild.memberCount < 1000 &&\n\t\t\t\t\tchannel.type === DiscordChannelType.GuildText\n\t\t\t\t) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Only attempt this for smaller guilds\n\t\t\t\t\t\t// Get members with read permissions for this channel\n\t\t\t\t\t\tparticipants = Array.from(guild.members.cache.values())\n\t\t\t\t\t\t\t.filter((member) =>\n\t\t\t\t\t\t\t\tchannel\n\t\t\t\t\t\t\t\t\t.permissionsFor(member)\n\t\t\t\t\t\t\t\t\t?.has(PermissionsBitField.Flags.ViewChannel),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.map((member) => createUniqueUuid(this.runtime, member.id));\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t`Failed to get participants for channel ${channel.name}:`,\n\t\t\t\t\t\t\terror,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\trooms.push({\n\t\t\t\t\tid: roomId,\n\t\t\t\t\tname: channel.name,\n\t\t\t\t\ttype: channelType,\n\t\t\t\t\tchannelId: channel.id,\n\t\t\t\t\tparticipants,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn rooms;\n\t}\n\n\t/**\n\t * Builds a standardized list of users from Discord guild members\n\t */\n\tprivate async buildStandardizedUsers(guild: Guild): Promise<Entity[]> {\n\t\tconst entities: Entity[] = [];\n\t\tconst botId = this.client?.user?.id;\n\n\t\t// Strategy based on guild size\n\t\tif (guild.memberCount > 1000) {\n\t\t\tlogger.info(\n\t\t\t\t`Using optimized user sync for large guild ${guild.name} (${guild.memberCount} members)`,\n\t\t\t);\n\n\t\t\t// For large guilds, prioritize members already in cache + online members\n\t\t\ttry {\n\t\t\t\t// Use cache first\n\t\t\t\tfor (const [, member] of guild.members.cache) {\n\t\t\t\t\tconst tag = member.user.bot\n\t\t\t\t\t\t? `${member.user.username}#${member.user.discriminator}`\n\t\t\t\t\t\t: member.user.username;\n\n\t\t\t\t\tif (member.id !== botId) {\n\t\t\t\t\t\tentities.push({\n\t\t\t\t\t\t\tid: createUniqueUuid(this.runtime, member.id),\n\t\t\t\t\t\t\tnames: Array.from(\n\t\t\t\t\t\t\t\tnew Set([\n\t\t\t\t\t\t\t\t\tmember.user.username,\n\t\t\t\t\t\t\t\t\tmember.displayName,\n\t\t\t\t\t\t\t\t\tmember.user.globalName,\n\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tdiscord: member.user.globalName\n\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t\t\t\tglobalName: member.user.globalName,\n\t\t\t\t\t\t\t\t\t\t\tuserId: member.id,\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t\t\t\tuserId: member.id,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If cache has very few members, try to get online members\n\t\t\t\tif (entities.length < 100) {\n\t\t\t\t\tlogger.info(`Adding online members for ${guild.name}`);\n\t\t\t\t\t// This is a more targeted fetch that is less likely to hit rate limits\n\t\t\t\t\tconst onlineMembers = await guild.members.fetch({ limit: 100 });\n\n\t\t\t\t\tfor (const [, member] of onlineMembers) {\n\t\t\t\t\t\tif (member.id !== botId) {\n\t\t\t\t\t\t\tconst entityId = createUniqueUuid(this.runtime, member.id);\n\t\t\t\t\t\t\t// Avoid duplicates\n\t\t\t\t\t\t\tif (!entities.some((u) => u.id === entityId)) {\n\t\t\t\t\t\t\t\tconst tag = member.user.bot\n\t\t\t\t\t\t\t\t\t? `${member.user.username}#${member.user.discriminator}`\n\t\t\t\t\t\t\t\t\t: member.user.username;\n\n\t\t\t\t\t\t\t\tentities.push({\n\t\t\t\t\t\t\t\t\tid: entityId,\n\t\t\t\t\t\t\t\t\tnames: Array.from(\n\t\t\t\t\t\t\t\t\t\tnew Set([\n\t\t\t\t\t\t\t\t\t\t\tmember.user.username,\n\t\t\t\t\t\t\t\t\t\t\tmember.displayName,\n\t\t\t\t\t\t\t\t\t\t\tmember.user.globalName,\n\t\t\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tdiscord: member.user.globalName\n\t\t\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t\t\t\t\t\tglobalName: member.user.globalName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tuserId: member.id,\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t\t\t\t\t\tuserId: member.id,\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Error fetching members for ${guild.name}:`, error);\n\t\t\t}\n\t\t} else {\n\t\t\t// For smaller guilds, we can fetch all members\n\t\t\ttry {\n\t\t\t\tlet members = guild.members.cache;\n\t\t\t\tif (members.size === 0) {\n\t\t\t\t\tmembers = await guild.members.fetch();\n\t\t\t\t}\n\n\t\t\t\tfor (const [, member] of members) {\n\t\t\t\t\tif (member.id !== botId) {\n\t\t\t\t\t\tconst tag = member.user.bot\n\t\t\t\t\t\t\t? `${member.user.username}#${member.user.discriminator}`\n\t\t\t\t\t\t\t: member.user.username;\n\n\t\t\t\t\t\tentities.push({\n\t\t\t\t\t\t\tid: createUniqueUuid(this.runtime, member.id),\n\t\t\t\t\t\t\tnames: Array.from(\n\t\t\t\t\t\t\t\tnew Set([\n\t\t\t\t\t\t\t\t\tmember.user.username,\n\t\t\t\t\t\t\t\t\tmember.displayName,\n\t\t\t\t\t\t\t\t\tmember.user.globalName,\n\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tdiscord: member.user.globalName\n\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t\t\t\tglobalName: member.user.globalName,\n\t\t\t\t\t\t\t\t\t\t\tuserId: member.id,\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\t\t\tusername: tag,\n\t\t\t\t\t\t\t\t\t\t\tname: member.displayName || member.user.username,\n\t\t\t\t\t\t\t\t\t\t\tuserId: member.id,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Error fetching members for ${guild.name}:`, error);\n\t\t\t}\n\t\t}\n\n\t\treturn entities;\n\t}\n\n\tprivate async onReady() {\n\t\tlogger.log(\"DISCORD ON READY\");\n\t\tconst guilds = await this.client?.guilds.fetch();\n\t\tfor (const [, guild] of guilds) {\n\t\t\tconst fullGuild = await guild.fetch();\n\t\t\tawait this.voiceManager.scanGuild(fullGuild);\n\n\t\t\t// Send after a brief delay\n\t\t\tsetTimeout(async () => {\n\t\t\t\t// For each server the client is in, fire a connected event\n\t\t\t\tconst fullGuild = await guild.fetch();\n\t\t\t\tlogger.log(\"DISCORD SERVER CONNECTED\", fullGuild.name);\n\n\t\t\t\t// Emit Discord-specific event with full guild object\n\t\t\t\tthis.runtime.emitEvent([DiscordEventTypes.WORLD_CONNECTED], {\n\t\t\t\t\truntime: this.runtime,\n\t\t\t\t\tserver: fullGuild,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\n\t\t\t\t// Create platform-agnostic world data structure with simplified structure\n\t\t\t\tconst worldId = createUniqueUuid(this.runtime, fullGuild.id);\n\t\t\t\tconst ownerId = createUniqueUuid(this.runtime, fullGuild.ownerId);\n\n\t\t\t\tconst standardizedData = {\n\t\t\t\t\tname: fullGuild.name,\n\t\t\t\t\truntime: this.runtime,\n\t\t\t\t\trooms: await this.buildStandardizedRooms(fullGuild, worldId),\n\t\t\t\t\tentities: await this.buildStandardizedUsers(fullGuild),\n\t\t\t\t\tworld: {\n\t\t\t\t\t\tid: worldId,\n\t\t\t\t\t\tname: fullGuild.name,\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tserverId: fullGuild.id,\n\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\townership: fullGuild.ownerId ? { ownerId } : undefined,\n\t\t\t\t\t\t\troles: {\n\t\t\t\t\t\t\t\t[ownerId]: Role.OWNER,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t} as World,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t};\n\n\t\t\t\t// Emit standardized event\n\t\t\t\tthis.runtime.emitEvent([EventTypes.WORLD_CONNECTED], standardizedData);\n\t\t\t}, 1000);\n\t\t}\n\n\t\tthis.client?.emit(\"voiceManagerReady\");\n\t}\n}\n\nconst discordPlugin: Plugin = {\n\tname: \"discord\",\n\tdescription: \"Discord client plugin\",\n\tservices: [DiscordService],\n\tactions: [\n\t\tchatWithAttachments,\n\t\tdownloadMedia,\n\t\tjoinVoice,\n\t\tleaveVoice,\n\t\tsummarize,\n\t\ttranscribe_media,\n\t],\n\tproviders: [channelStateProvider, voiceStateProvider],\n\ttests: [new DiscordTestSuite()],\n\tinit: async (config: Record<string, string>, runtime: IAgentRuntime) => {\n\t\tconst token = runtime.getSetting(\"DISCORD_API_TOKEN\") as string;\n\n\t\tif (!token || token.trim() === \"\") {\n\t\t\tlogger.warn(\n\t\t\t\t\"Discord API Token not provided - Discord plugin is loaded but will not be functional\",\n\t\t\t);\n\t\t\tlogger.info(\n\t\t\t\t\"To enable Discord functionality, please provide DISCORD_API_TOKEN in your .eliza/.env file\",\n\t\t\t);\n\t\t}\n\t},\n};\n\nexport default discordPlugin;\n","import fs from \"node:fs\";\nimport {\n\ttype Action,\n\ttype ActionExample,\n\tChannelType,\n\ttype Content,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelTypes,\n\ttype State,\n\tcomposePromptFromState,\n\tparseJSONObjectFromText,\n\ttrimTokens,\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\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n): Promise<{ objective: string; attachmentIds: string[] } | null> => {\n\tconst prompt = composePromptFromState({\n\t\tstate,\n\t\ttemplate: attachmentIdsTemplate,\n\t});\n\n\tfor (let i = 0; i < 5; i++) {\n\t\tconst response = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\t\tconsole.log(\"response\", response);\n\t\t// try parsing to a json object\n\t\tconst parsedResponse = parseJSONObjectFromText(response) as {\n\t\t\tobjective: string;\n\t\t\tattachmentIds: string[];\n\t\t} | null;\n\t\t// see if it contains objective and attachmentIds\n\t\tif (parsedResponse?.objective && parsedResponse?.attachmentIds) {\n\t\t\treturn parsedResponse;\n\t\t}\n\t}\n\treturn 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\nconst summarizeAction = {\n\tname: \"CHAT_WITH_ATTACHMENTS\",\n\tsimiles: [\n\t\t\"CHAT_WITH_ATTACHMENT\",\n\t\t\"SUMMARIZE_FILES\",\n\t\t\"SUMMARIZE_FILE\",\n\t\t\"SUMMARIZE_ATACHMENT\",\n\t\t\"CHAT_WITH_PDF\",\n\t\t\"ATTACHMENT_SUMMARY\",\n\t\t\"RECAP_ATTACHMENTS\",\n\t\t\"SUMMARIZE_FILE\",\n\t\t\"SUMMARIZE_VIDEO\",\n\t\t\"SUMMARIZE_AUDIO\",\n\t\t\"SUMMARIZE_IMAGE\",\n\t\t\"SUMMARIZE_DOCUMENT\",\n\t\t\"SUMMARIZE_LINK\",\n\t\t\"ATTACHMENT_SUMMARY\",\n\t\t\"FILE_SUMMARY\",\n\t],\n\tdescription:\n\t\t\"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\tvalidate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n\t\tconst room = await _runtime.getRoom(message.roomId);\n\t\tif (room?.type !== ChannelType.GROUP) {\n\t\t\treturn false;\n\t\t}\n\t\t// only show if one of the keywords are in the message\n\t\tconst keywords: string[] = [\n\t\t\t\"attachment\",\n\t\t\t\"summary\",\n\t\t\t\"summarize\",\n\t\t\t\"research\",\n\t\t\t\"pdf\",\n\t\t\t\"video\",\n\t\t\t\"audio\",\n\t\t\t\"image\",\n\t\t\t\"document\",\n\t\t\t\"link\",\n\t\t\t\"file\",\n\t\t\t\"attachment\",\n\t\t\t\"summarize\",\n\t\t\t\"code\",\n\t\t\t\"report\",\n\t\t\t\"write\",\n\t\t\t\"details\",\n\t\t\t\"information\",\n\t\t\t\"talk\",\n\t\t\t\"chat\",\n\t\t\t\"read\",\n\t\t\t\"listen\",\n\t\t\t\"watch\",\n\t\t];\n\t\treturn keywords.some((keyword) =>\n\t\t\tmessage.content.text.toLowerCase().includes(keyword.toLowerCase()),\n\t\t);\n\t},\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\tstate: State,\n\t\t_options: any,\n\t\tcallback: HandlerCallback,\n\t) => {\n\t\tconst callbackData: Content = {\n\t\t\ttext: \"\", // fill in later\n\t\t\tactions: [\"CHAT_WITH_ATTACHMENTS_RESPONSE\"],\n\t\t\tsource: message.content.source,\n\t\t\tattachments: [],\n\t\t};\n\n\t\t// 1. extract attachment IDs from the message\n\t\tconst attachmentData = await getAttachmentIds(runtime, message, state);\n\t\tif (!attachmentData) {\n\t\t\tconsole.error(\"Couldn't get attachment IDs from message\");\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: message.content.source,\n\t\t\t\t\tthought:\n\t\t\t\t\t\t\"I tried to chat with attachments but I couldn't get attachment IDs\",\n\t\t\t\t\tactions: [\"CHAT_WITH_ATTACHMENTS_FAILED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"CHAT_WITH_ATTACHMENTS\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst { objective, attachmentIds } = attachmentData;\n\n\t\t// This is pretty gross but it can catch cases where the returned generated UUID is stupidly wrong for some reason\n\t\tconst attachments = state.data.recentMessages\n\t\t\t.filter(\n\t\t\t\t(msg) => msg.content.attachments && msg.content.attachments.length > 0,\n\t\t\t)\n\t\t\t.flatMap((msg) => msg.content.attachments)\n\t\t\t// check by first 5 characters of uuid\n\t\t\t.filter(\n\t\t\t\t(attachment) =>\n\t\t\t\t\tattachmentIds\n\t\t\t\t\t\t.map((attch) => attch.toLowerCase().slice(0, 5))\n\t\t\t\t\t\t.includes(attachment.id.toLowerCase().slice(0, 5)) ||\n\t\t\t\t\t// or check the other way\n\t\t\t\t\tattachmentIds.some((id) => {\n\t\t\t\t\t\tconst attachmentId = id.toLowerCase().slice(0, 5);\n\t\t\t\t\t\treturn attachment.id.toLowerCase().includes(attachmentId);\n\t\t\t\t\t}),\n\t\t\t);\n\n\t\tconst attachmentsWithText = attachments\n\t\t\t.map((attachment) => `# ${attachment.title}\\n${attachment.text}`)\n\t\t\t.join(\"\\n\\n\");\n\n\t\tlet currentSummary = \"\";\n\n\t\tconst chunkSize = 8192;\n\n\t\tstate.values.attachmentsWithText = attachmentsWithText;\n\t\tstate.values.objective = objective;\n\t\tconst template = await trimTokens(\n\t\t\tsummarizationTemplate,\n\t\t\tchunkSize,\n\t\t\truntime,\n\t\t);\n\t\tconst prompt = composePromptFromState({\n\t\t\tstate,\n\t\t\t// make sure it fits, we can pad the tokens a bit\n\t\t\t// Get the model's tokenizer based on the current model being used\n\t\t\ttemplate,\n\t\t});\n\n\t\tconst summary = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\n\t\tcurrentSummary = `${currentSummary}\\n${summary}`;\n\n\t\tif (!currentSummary) {\n\t\t\tconsole.error(\"No summary found, that's not good!\");\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: message.content.source,\n\t\t\t\t\tthought:\n\t\t\t\t\t\t\"I tried to chat with attachments but I couldn't get a summary\",\n\t\t\t\t\tactions: [\"CHAT_WITH_ATTACHMENTS_FAILED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"CHAT_WITH_ATTACHMENTS\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcallbackData.text = currentSummary.trim();\n\t\tif (\n\t\t\tcallbackData.text &&\n\t\t\t(currentSummary.trim()?.split(\"\\n\").length < 4 ||\n\t\t\t\tcurrentSummary.trim()?.split(\" \").length < 100)\n\t\t) {\n\t\t\tcallbackData.text = `Here is the summary:\n\\`\\`\\`md\n${currentSummary.trim()}\n\\`\\`\\`\n`;\n\t\t\tawait callback(callbackData);\n\t\t} else if (currentSummary.trim()) {\n\t\t\tconst summaryDir = \"cache\";\n\t\t\tconst summaryFilename = `${summaryDir}/summary_${Date.now()}.md`;\n\t\t\ttry {\n\t\t\t\tawait fs.promises.mkdir(summaryDir, { recursive: true });\n\t\t\t\t// Debug: Log before file operations\n\t\t\t\tconsole.log(\"Creating summary file:\", {\n\t\t\t\t\tfilename: summaryFilename,\n\t\t\t\t\tsummaryLength: currentSummary.length,\n\t\t\t\t});\n\n\t\t\t\t// Write file directly first\n\t\t\t\tawait fs.promises.writeFile(summaryFilename, currentSummary, \"utf8\");\n\t\t\t\tconsole.log(\"File written successfully\");\n\n\t\t\t\t// Then cache it\n\t\t\t\tawait runtime\n\t\t\t\t\t\n\t\t\t\t\t.setCache<string>(summaryFilename, currentSummary);\n\t\t\t\tconsole.log(\"Cache set operation completed\");\n\n\t\t\t\tawait callback(\n\t\t\t\t\t{\n\t\t\t\t\t\t...callbackData,\n\t\t\t\t\t\ttext: `I've attached the summary of the requested attachments as a text file.`,\n\t\t\t\t\t},\n\t\t\t\t\t[summaryFilename],\n\t\t\t\t);\n\t\t\t\tconsole.log(\"Callback completed with summary file\");\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Error in file/cache process:\", error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t} else {\n\t\t\tconsole.warn(\n\t\t\t\t\"Empty response from chat with attachments action, skipping\",\n\t\t\t);\n\t\t}\n\n\t\treturn callbackData;\n\t},\n\texamples: [\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Can you summarize the attachments b3e23, c4f67, and d5a89?\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Sure thing! I'll pull up those specific attachments and provide a summary of their content.\",\n\t\t\t\t\tactions: [\"CHAT_WITH_ATTACHMENTS\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"I need a technical summary of the PDFs I sent earlier - a1b2c3.pdf, d4e5f6.pdf, and g7h8i9.pdf\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"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\t\t\t\t\tactions: [\"CHAT_WITH_ATTACHMENTS\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"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\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"sure, no problem.\",\n\t\t\t\t\tactions: [\"CHAT_WITH_ATTACHMENTS\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"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\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"great idea, give me a minute\",\n\t\t\t\t\tactions: [\"CHAT_WITH_ATTACHMENTS\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t] as ActionExample[][],\n} as Action;\n\nexport default summarizeAction;\n","import {\n\ttype Action,\n\ttype ActionExample,\n\ttype Content,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype IVideoService,\n\ttype Memory,\n\tModelTypes,\n\tServiceTypes,\n\ttype State,\n\tcomposePromptFromState,\n\tparseJSONObjectFromText,\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\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n): Promise<string | null> => {\n\tconst prompt = composePromptFromState({\n\t\tstate,\n\t\ttemplate: mediaUrlTemplate,\n\t});\n\n\tfor (let i = 0; i < 5; i++) {\n\t\tconst response = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\n\t\tconst parsedResponse = parseJSONObjectFromText(response) as {\n\t\t\tmediaUrl: string;\n\t\t} | null;\n\n\t\tif (parsedResponse?.mediaUrl) {\n\t\t\treturn parsedResponse.mediaUrl;\n\t\t}\n\t}\n\treturn null;\n};\n\nexport default {\n\tname: \"DOWNLOAD_MEDIA\",\n\tsimiles: [\n\t\t\"DOWNLOAD_VIDEO\",\n\t\t\"DOWNLOAD_AUDIO\",\n\t\t\"GET_MEDIA\",\n\t\t\"DOWNLOAD_PODCAST\",\n\t\t\"DOWNLOAD_YOUTUBE\",\n\t],\n\tdescription:\n\t\t\"Downloads a video or audio file from a URL and attaches it to the response message.\",\n\tvalidate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n\t\tif (message.content.source !== \"discord\") {\n\t\t\treturn false;\n\t\t}\n\t},\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\tstate: State,\n\t\t_options: any,\n\t\tcallback: HandlerCallback,\n\t) => {\n\t\tconst videoService = runtime.getService<IVideoService>(ServiceTypes.VIDEO);\n\n\t\tconst mediaUrl = await getMediaUrl(runtime, message, state);\n\t\tif (!mediaUrl) {\n\t\t\tconsole.error(\"Couldn't get media URL from messages\");\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought: `I couldn't find the media URL in the message`,\n\t\t\t\t\tactions: [\"DOWNLOAD_MEDIA_FAILED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"DOWNLOAD_MEDIA\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst videoInfo = await videoService.fetchVideoInfo(mediaUrl);\n\t\tconst mediaPath = await videoService.downloadVideo(videoInfo);\n\n\t\tconst response: Content = {\n\t\t\ttext: `I downloaded the video \"${videoInfo.title}\" and attached it below.`,\n\t\t\tactions: [\"DOWNLOAD_MEDIA_RESPONSE\"],\n\t\t\tsource: message.content.source,\n\t\t\tattachments: [],\n\t\t};\n\n\t\tconst maxRetries = 3;\n\t\tlet retries = 0;\n\n\t\twhile (retries < maxRetries) {\n\t\t\ttry {\n\t\t\t\tawait callback(\n\t\t\t\t\t{\n\t\t\t\t\t\t...response,\n\t\t\t\t\t},\n\t\t\t\t\t[mediaPath],\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t} catch (error) {\n\t\t\t\tretries++;\n\t\t\t\tconsole.error(`Error sending message (attempt ${retries}):`, error);\n\n\t\t\t\tif (retries === maxRetries) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"Max retries reached. Failed to send message with attachment.\",\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// Wait for a short delay before retrying\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 2000));\n\t\t\t}\n\t\t}\n\n\t\treturn response;\n\t},\n\texamples: [\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"https://www.youtube.com/watch?v=dQw4w9WgXcQ\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Downloading the YouTube video now, one sec\",\n\t\t\t\t\tactions: [\"DOWNLOAD_MEDIA\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Can you grab this video for me? https://vimeo.com/123456789\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Sure thing, I'll download that Vimeo video for you\",\n\t\t\t\t\tactions: [\"DOWNLOAD_MEDIA\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"I need this video downloaded: https://www.youtube.com/watch?v=abcdefg\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"No problem, I'm on it. I'll have that YouTube video downloaded in a jiffy\",\n\t\t\t\t\tactions: [\"DOWNLOAD_MEDIA\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t] as ActionExample[][],\n} as Action;\n","import fs from \"node:fs\";\nimport {\n\ttype Action,\n\ttype ActionExample,\n\ttype Content,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype Media,\n\ttype Memory,\n\tModelTypes,\n\ttype State,\n\tcomposePromptFromState,\n\tgetEntityDetails,\n\tparseJSONObjectFromText,\n\tsplitChunks,\n\ttrimTokens,\n} from \"@elizaos/core\";\nexport const summarizationTemplate = `# Summarized so far (we are adding to this)\n{{currentSummary}}\n\n# Current conversation chunk we are summarizing (includes attachments)\n{{memoriesWithAttachments}}\n\nSummarization objective: {{objective}}\n\n# Instructions: Summarize the conversation so far. Return the summary. Do not acknowledge this request, just summarize and continue the existing summary if there is one. Capture any important details to the objective. Only respond with the new summary text.\nYour response should be extremely detailed and include any and all relevant information.`;\n\n/**\n * Template for providing instructions and details on how to summarize conversation messages and determine the range of dates requested.\n * The template includes placeholders for recent messages, sender name, objective, start and end date range.\n * The response is expected to be formatted as a JSON block with specific structure.\n * @type {string}\n */\nexport const dateRangeTemplate = `# Messages we are summarizing (the conversation is continued after this)\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting a summary of the conversation. Your goal is to determine their objective, along with the range of dates that their request covers.\nThe \"objective\" is a detailed description of what the user wants to summarize based on the conversation. If they just ask for a general summary, you can either base it off the conversation if the summary range is very recent, or set the object to be general, like \"a detailed summary of the conversation between all users\".\nThe \"start\" and \"end\" are the range of dates that the user wants to summarize, relative to the current time. The start and end should be relative to the current time, and measured in seconds, minutes, hours and days. The format is \"2 days ago\" or \"3 hours ago\" or \"4 minutes ago\" or \"5 seconds ago\", i.e. \"<integer> <unit> ago\".\nIf you aren't sure, you can use a default range of \"0 minutes ago\" to \"2 hours ago\" or more. Better to err on the side of including too much than too little.\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"objective\": \"<What the user wants to summarize>\",\n \"start\": \"0 minutes ago\",\n \"end\": \"2 hours ago\"\n}\n\\`\\`\\`\n`;\n\n/**\n * Function to get a date range from user input.\n *\n * @param {IAgentRuntime} runtime - The Agent Runtime object.\n * @param {Memory} _message - The Memory object.\n * @param {State} state - The State object.\n * @return {Promise<{ objective: string; start: string | number; end: string | number; } | null>} Parsed user input containing objective, start, and end timestamps, or null.\n */\nconst getDateRange = async (\n\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n) => {\n\tconst prompt = composePromptFromState({\n\t\tstate,\n\t\ttemplate: dateRangeTemplate,\n\t});\n\n\tfor (let i = 0; i < 5; i++) {\n\t\tconst response = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\t\tconsole.log(\"response\", response);\n\t\t// try parsing to a json object\n\t\tconst parsedResponse = parseJSONObjectFromText(response) as {\n\t\t\tobjective: string;\n\t\t\tstart: string | number;\n\t\t\tend: string | number;\n\t\t} | null;\n\t\t// see if it contains objective, start and end\n\t\tif (parsedResponse) {\n\t\t\tif (\n\t\t\t\tparsedResponse.objective &&\n\t\t\t\tparsedResponse.start &&\n\t\t\t\tparsedResponse.end\n\t\t\t) {\n\t\t\t\t// TODO: parse start and end into timestamps\n\t\t\t\tconst startIntegerString = (parsedResponse.start as string).match(\n\t\t\t\t\t/\\d+/,\n\t\t\t\t)?.[0];\n\t\t\t\tconst endIntegerString = (parsedResponse.end as string).match(\n\t\t\t\t\t/\\d+/,\n\t\t\t\t)?.[0];\n\n\t\t\t\t// parse multiplier\n\t\t\t\tconst multipliers = {\n\t\t\t\t\tsecond: 1 * 1000,\n\t\t\t\t\tminute: 60 * 1000,\n\t\t\t\t\thour: 3600 * 1000,\n\t\t\t\t\tday: 86400 * 1000,\n\t\t\t\t};\n\n\t\t\t\tconst startMultiplier = (parsedResponse.start as string).match(\n\t\t\t\t\t/second|minute|hour|day/,\n\t\t\t\t)?.[0];\n\t\t\t\tconst endMultiplier = (parsedResponse.end as string).match(\n\t\t\t\t\t/second|minute|hour|day/,\n\t\t\t\t)?.[0];\n\n\t\t\t\tconst startInteger = startIntegerString\n\t\t\t\t\t? Number.parseInt(startIntegerString)\n\t\t\t\t\t: 0;\n\t\t\t\tconst endInteger = endIntegerString\n\t\t\t\t\t? Number.parseInt(endIntegerString)\n\t\t\t\t\t: 0;\n\n\t\t\t\t// multiply by multiplier\n\t\t\t\tconst startTime =\n\t\t\t\t\tstartInteger *\n\t\t\t\t\tmultipliers[startMultiplier as keyof typeof multipliers];\n\n\t\t\t\tconsole.log(\"startTime\", startTime);\n\n\t\t\t\tconst endTime =\n\t\t\t\t\tendInteger * multipliers[endMultiplier as keyof typeof multipliers];\n\n\t\t\t\tconsole.log(\"endTime\", endTime);\n\n\t\t\t\t// get the current time and subtract the start and end times\n\t\t\t\tparsedResponse.start = Date.now() - startTime;\n\t\t\t\tparsedResponse.end = Date.now() - endTime;\n\n\t\t\t\treturn parsedResponse;\n\t\t\t}\n\t\t}\n\t}\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 */\nconst summarizeAction = {\n\tname: \"SUMMARIZE_CONVERSATION\",\n\tsimiles: [\n\t\t\"RECAP\",\n\t\t\"RECAP_CONVERSATION\",\n\t\t\"SUMMARIZE_CHAT\",\n\t\t\"SUMMARIZATION\",\n\t\t\"CHAT_SUMMARY\",\n\t\t\"CONVERSATION_SUMMARY\",\n\t],\n\tdescription: \"Summarizes the conversation and attachments.\",\n\tvalidate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n\t\tif (message.content.source !== \"discord\") {\n\t\t\treturn false;\n\t\t}\n\t\t// only show if one of the keywords are in the message\n\t\tconst keywords: string[] = [\n\t\t\t\"summarize\",\n\t\t\t\"summarization\",\n\t\t\t\"summary\",\n\t\t\t\"recap\",\n\t\t\t\"report\",\n\t\t\t\"overview\",\n\t\t\t\"review\",\n\t\t\t\"rundown\",\n\t\t\t\"wrap-up\",\n\t\t\t\"brief\",\n\t\t\t\"debrief\",\n\t\t\t\"abstract\",\n\t\t\t\"synopsis\",\n\t\t\t\"outline\",\n\t\t\t\"digest\",\n\t\t\t\"abridgment\",\n\t\t\t\"condensation\",\n\t\t\t\"encapsulation\",\n\t\t\t\"essence\",\n\t\t\t\"gist\",\n\t\t\t\"main points\",\n\t\t\t\"key points\",\n\t\t\t\"key takeaways\",\n\t\t\t\"bulletpoint\",\n\t\t\t\"highlights\",\n\t\t\t\"tldr\",\n\t\t\t\"tl;dr\",\n\t\t\t\"in a nutshell\",\n\t\t\t\"bottom line\",\n\t\t\t\"long story short\",\n\t\t\t\"sum up\",\n\t\t\t\"sum it up\",\n\t\t\t\"short version\",\n\t\t\t\"bring me up to speed\",\n\t\t\t\"catch me up\",\n\t\t];\n\t\treturn keywords.some((keyword) =>\n\t\t\tmessage.content.text.toLowerCase().includes(keyword.toLowerCase()),\n\t\t);\n\t},\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\tstate: State,\n\t\t_options: any,\n\t\tcallback: HandlerCallback,\n\t) => {\n\t\tconst callbackData: Content = {\n\t\t\ttext: \"\", // fill in later\n\t\t\tactions: [\"SUMMARIZATION_RESPONSE\"],\n\t\t\tsource: message.content.source,\n\t\t\tattachments: [],\n\t\t};\n\t\tconst { roomId } = message;\n\n\t\t// 1. extract date range from the message\n\t\tconst dateRange = await getDateRange(runtime, message, state);\n\t\tif (!dateRange) {\n\t\t\tconsole.error(\"Couldn't get date range from message\");\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought: `I couldn't get the date range from the message`,\n\t\t\t\t\tactions: [\"SUMMARIZE_CONVERSATION_FAILED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"SUMMARIZE_CONVERSATION\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst { objective, start, end } = dateRange;\n\n\t\t// 2. get these memories from the database\n\t\tconst memories = await runtime.getMemoryManager(\"messages\").getMemories({\n\t\t\troomId,\n\t\t\t// subtract start from current time\n\t\t\tstart: Number.parseInt(start as string),\n\t\t\tend: Number.parseInt(end as string),\n\t\t\tcount: 10000,\n\t\t\tunique: false,\n\t\t});\n\n\t\tconst entities = await getEntityDetails({\n\t\t\truntime: runtime as IAgentRuntime,\n\t\t\troomId,\n\t\t});\n\n\t\tconst actorMap = new Map(entities.map((entity) => [entity.id, entity]));\n\n\t\tconst formattedMemories = memories\n\t\t\t.map((memory) => {\n\t\t\t\tconst attachments = memory.content.attachments\n\t\t\t\t\t?.map((attachment: Media) => {\n\t\t\t\t\t\treturn `---\\nAttachment: ${attachment.id}\\n${attachment.description}\\n${attachment.text}\\n---`;\n\t\t\t\t\t})\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\treturn `${actorMap.get(memory.entityId)?.name ?? \"Unknown User\"} (${actorMap.get(memory.entityId)?.username ?? \"\"}): ${memory.content.text}\\n${attachments}`;\n\t\t\t})\n\t\t\t.join(\"\\n\");\n\n\t\tlet currentSummary = \"\";\n\n\t\tconst chunkSize = 8000;\n\n\t\tconst chunks = await splitChunks(formattedMemories, chunkSize, 0);\n\n\t\tconst _datestr = new Date().toUTCString().replace(/:/g, \"-\");\n\n\t\tstate.values.memoriesWithAttachments = formattedMemories;\n\t\tstate.values.objective = objective;\n\n\t\tfor (let i = 0; i < chunks.length; i++) {\n\t\t\tconst chunk = chunks[i];\n\t\t\tstate.values.currentSummary = currentSummary;\n\t\t\tstate.values.currentChunk = chunk;\n\t\t\tconst template = await trimTokens(\n\t\t\t\tsummarizationTemplate,\n\t\t\t\tchunkSize + 500,\n\t\t\t\truntime,\n\t\t\t);\n\t\t\tconst prompt = composePromptFromState({\n\t\t\t\tstate,\n\t\t\t\t// make sure it fits, we can pad the tokens a bit\n\t\t\t\ttemplate,\n\t\t\t});\n\n\t\t\tconst summary = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\t\t\tprompt,\n\t\t\t});\n\n\t\t\tcurrentSummary = `${currentSummary}\\n${summary}`;\n\t\t}\n\n\t\tif (!currentSummary) {\n\t\t\tconsole.error(\"No summary found, that's not good!\");\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought: `I couldn't summarize the conversation`,\n\t\t\t\t\tactions: [\"SUMMARIZE_CONVERSATION_FAILED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"SUMMARIZE_CONVERSATION\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcallbackData.text = currentSummary.trim();\n\t\tif (\n\t\t\tcallbackData.text &&\n\t\t\t(currentSummary.trim()?.split(\"\\n\").length < 4 ||\n\t\t\t\tcurrentSummary.trim()?.split(\" \").length < 100)\n\t\t) {\n\t\t\tcallbackData.text = `Here is the summary:\n\\`\\`\\`md\n${currentSummary.trim()}\n\\`\\`\\`\n`;\n\t\t\tawait callback(callbackData);\n\t\t} else if (currentSummary.trim()) {\n\t\t\tconst summaryDir = \"cache\";\n\t\t\tconst summaryFilename = `${summaryDir}/conversation_summary_${Date.now()}`;\n\t\t\tawait runtime\n\t\t\t\t\n\t\t\t\t.setCache<string>(summaryFilename, currentSummary);\n\t\t\tawait fs.promises.mkdir(summaryDir, { recursive: true });\n\n\t\t\tawait fs.promises.writeFile(summaryFilename, currentSummary, \"utf8\");\n\t\t\t// save the summary to a file\n\t\t\tawait callback(\n\t\t\t\t{\n\t\t\t\t\t...callbackData,\n\t\t\t\t\ttext: `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\t\t\t\t},\n\t\t\t\t[summaryFilename],\n\t\t\t);\n\t\t} else {\n\t\t\tconsole.warn(\n\t\t\t\t\"Empty response from summarize conversation action, skipping\",\n\t\t\t);\n\t\t}\n\n\t\treturn callbackData;\n\t},\n\texamples: [\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"```js\\nconst x = 10\\n```\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"can you give me a detailed report on what we're talking about?\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"sure, no problem, give me a minute to get that together for you\",\n\t\t\t\t\tactions: [\"SUMMARIZE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"please summarize the conversation we just had and include this blogpost i'm linking (Attachment: b3e12)\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"sure, give me a sec\",\n\t\t\t\t\tactions: [\"SUMMARIZE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Can you summarize what moon and avf are talking about?\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Yeah, just hold on a second while I get that together for you...\",\n\t\t\t\t\tactions: [\"SUMMARIZE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"i need to write a blog post about farming, can you summarize the discussion from a few hours ago?\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"no problem, give me a few minutes to read through everything\",\n\t\t\t\t\tactions: [\"SUMMARIZE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t] as ActionExample[][],\n} as Action;\n\nexport default summarizeAction;\n","import {\n\ttype Action,\n\ttype ActionExample,\n\ttype Content,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelTypes,\n\ttype State,\n\tcomposePromptFromState,\n\tcreateUniqueUuid,\n\tparseJSONObjectFromText,\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\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n): Promise<string | null> => {\n\tconst prompt = composePromptFromState({\n\t\tstate,\n\t\ttemplate: mediaAttachmentIdTemplate,\n\t});\n\n\tfor (let i = 0; i < 5; i++) {\n\t\tconst response = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\t\tconsole.log(\"response\", response);\n\n\t\tconst parsedResponse = parseJSONObjectFromText(response) as {\n\t\t\tattachmentId: string;\n\t\t} | null;\n\n\t\tif (parsedResponse?.attachmentId) {\n\t\t\treturn parsedResponse.attachmentId;\n\t\t}\n\t}\n\treturn 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 */\nconst transcribeMediaAction = {\n\tname: \"TRANSCRIBE_MEDIA\",\n\tsimiles: [\n\t\t\"TRANSCRIBE_AUDIO\",\n\t\t\"TRANSCRIBE_VIDEO\",\n\t\t\"MEDIA_TRANSCRIPT\",\n\t\t\"VIDEO_TRANSCRIPT\",\n\t\t\"AUDIO_TRANSCRIPT\",\n\t],\n\tdescription:\n\t\t\"Transcribe the full text of an audio or video file that the user has attached.\",\n\tvalidate: async (_runtime: IAgentRuntime, message: Memory, _state: State) => {\n\t\tif (message.content.source !== \"discord\") {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst keywords: string[] = [\n\t\t\t\"transcribe\",\n\t\t\t\"transcript\",\n\t\t\t\"audio\",\n\t\t\t\"video\",\n\t\t\t\"media\",\n\t\t\t\"youtube\",\n\t\t\t\"meeting\",\n\t\t\t\"recording\",\n\t\t\t\"podcast\",\n\t\t\t\"call\",\n\t\t\t\"conference\",\n\t\t\t\"interview\",\n\t\t\t\"speech\",\n\t\t\t\"lecture\",\n\t\t\t\"presentation\",\n\t\t];\n\t\treturn keywords.some((keyword) =>\n\t\t\tmessage.content.text.toLowerCase().includes(keyword.toLowerCase()),\n\t\t);\n\t},\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\tstate: State,\n\t\t_options: any,\n\t\tcallback: HandlerCallback,\n\t) => {\n\t\tconst callbackData: Content = {\n\t\t\ttext: \"\", // fill in later\n\t\t\tactions: [\"TRANSCRIBE_MEDIA_RESPONSE\"],\n\t\t\tsource: message.content.source,\n\t\t\tattachments: [],\n\t\t};\n\n\t\tconst attachmentId = await getMediaAttachmentId(runtime, message, state);\n\t\tif (!attachmentId) {\n\t\t\tconsole.error(\"Couldn't get media attachment ID from message\");\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought: `I couldn't find the media attachment ID in the message`,\n\t\t\t\t\tactions: [\"TRANSCRIBE_MEDIA_FAILED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"TRANSCRIBE_MEDIA\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst attachment = state.data.recentMessages\n\t\t\t.filter(\n\t\t\t\t(msg) => msg.content.attachments && msg.content.attachments.length > 0,\n\t\t\t)\n\t\t\t.flatMap((msg) => msg.content.attachments)\n\t\t\t.find(\n\t\t\t\t(attachment) =>\n\t\t\t\t\tattachment.id.toLowerCase() === attachmentId.toLowerCase(),\n\t\t\t);\n\n\t\tif (!attachment) {\n\t\t\tconsole.error(`Couldn't find attachment with ID ${attachmentId}`);\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought: `I couldn't find the media attachment with ID ${attachmentId}`,\n\t\t\t\t\tactions: [\"TRANSCRIBE_MEDIA_FAILED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"TRANSCRIBE_MEDIA\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst mediaTranscript = attachment.text;\n\n\t\tcallbackData.text = mediaTranscript.trim();\n\n\t\t// if callbackData.text is < 4 lines or < 100 words, then we we callback with normal message wrapped in markdown block\n\t\tif (\n\t\t\tcallbackData.text &&\n\t\t\t(callbackData.text?.split(\"\\n\").length < 4 ||\n\t\t\t\tcallbackData.text?.split(\" \").length < 100)\n\t\t) {\n\t\t\tcallbackData.text = `Here is the transcript:\n\\`\\`\\`md\n${mediaTranscript.trim()}\n\\`\\`\\`\n`;\n\t\t\tawait callback(callbackData);\n\t\t}\n\t\t// if text is big, let's send as an attachment\n\t\telse if (callbackData.text) {\n\t\t\tconst transcriptFilename = `content/transcript_${Date.now()}`;\n\n\t\t\t// save the transcript to a file\n\t\t\tawait runtime\n\t\t\t\t\n\t\t\t\t.setCache<string>(transcriptFilename, callbackData.text);\n\n\t\t\tawait callback(\n\t\t\t\t{\n\t\t\t\t\t...callbackData,\n\t\t\t\t\ttext: `I've attached the transcript as a text file.`,\n\t\t\t\t},\n\t\t\t\t[transcriptFilename],\n\t\t\t);\n\t\t} else {\n\t\t\tconsole.warn(\"Empty response from transcribe media action, skipping\");\n\t\t}\n\n\t\treturn callbackData;\n\t},\n\texamples: [\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Please transcribe the audio file I just sent.\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Sure, I'll transcribe the full audio for you.\",\n\t\t\t\t\tactions: [\"TRANSCRIBE_MEDIA\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Can I get a transcript of that video recording?\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Absolutely, give me a moment to generate the full transcript of the video.\",\n\t\t\t\t\tactions: [\"TRANSCRIBE_MEDIA\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t] as ActionExample[][],\n} as Action;\n\nexport default transcribeMediaAction;\n","// src/actions/joinVoice\nimport {\n\ttype Action,\n\ttype ActionExample,\n\tChannelType,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelTypes,\n\ttype State,\n\tcomposePromptFromState,\n\tcreateUniqueUuid,\n\tlogger,\n} from \"@elizaos/core\";\nimport {\n\ttype BaseGuildVoiceChannel,\n\ttype Channel,\n\tChannelType as DiscordChannelType,\n\ttype Guild,\n} from \"discord.js\";\nimport type { DiscordService } from \"../index\";\nimport { ServiceTypes } from \"../types\";\nimport type { VoiceManager } from \"../voice\";\n\nexport default {\n\tname: \"JOIN_VOICE\",\n\tsimiles: [\n\t\t\"JOIN_VOICE\",\n\t\t\"JOIN_VC\",\n\t\t\"JOIN_VOICE_CHAT\",\n\t\t\"JOIN_VOICE_CHANNEL\",\n\t\t\"JOIN_MEETING\",\n\t\t\"JOIN_CALL\",\n\t],\n\tvalidate: async (runtime: IAgentRuntime, message: Memory, state: State) => {\n\t\tif (message.content.source !== \"discord\") {\n\t\t\t// not a discord message\n\t\t\treturn false;\n\t\t}\n\n\t\tconst room =\n\t\t\tstate.data.room ??\n\t\t\t(await runtime.getRoom(message.roomId));\n\n\t\tif (room?.type !== ChannelType.GROUP) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst client = runtime.getService(ServiceTypes.DISCORD);\n\n\t\tif (!client) {\n\t\t\tlogger.error(\"Discord client not found\");\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t},\n\tdescription: \"Join a voice channel to participate in voice chat.\",\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\tstate: State,\n\t\t_options: any,\n\t\tcallback: HandlerCallback,\n\t): Promise<boolean> => {\n\t\tconst room =\n\t\t\tstate.data.room ??\n\t\t\t(await runtime.getRoom(message.roomId));\n\t\tif (!room) {\n\t\t\tthrow new Error(\"No room found\");\n\t\t}\n\n\t\tif (room.type !== ChannelType.GROUP) {\n\t\t\t// only handle in a group scenario for now\n\t\t\treturn false;\n\t\t}\n\n\t\tconsole.log(\"Running handler on provider\", room.name);\n\n\t\tconst serverId = room.serverId;\n\n\t\tif (!serverId) {\n\t\t\tthrow new Error(\"No server ID found\");\n\t\t}\n\n\t\tconst discordClient = runtime.getService(\n\t\t\tServiceTypes.DISCORD,\n\t\t) as DiscordService;\n\t\tconst client = discordClient.client;\n\t\tconst voiceManager = discordClient.voiceManager as VoiceManager;\n\n\t\tif (!client) {\n\t\t\tlogger.error(\"Discord client not found\");\n\t\t\treturn false;\n\t\t}\n\n\t\tconst voiceChannels = (\n\t\t\tclient.guilds.cache.get(serverId) as Guild\n\t\t).channels.cache.filter(\n\t\t\t(channel: Channel) => channel.type === DiscordChannelType.GuildVoice,\n\t\t);\n\n\t\tconst targetChannel = voiceChannels.find((channel) => {\n\t\t\tconst name = (channel as { name: string }).name.toLowerCase();\n\t\t\tconst messageContent = message?.content?.text;\n\t\t\t// remove all non-alphanumeric characters (keep spaces between words)\n\t\t\tconst replacedName = name.replace(/[^a-z0-9 ]/g, \"\");\n\n\t\t\treturn (\n\t\t\t\tname.includes(messageContent) ||\n\t\t\t\tmessageContent.includes(name) ||\n\t\t\t\treplacedName.includes(messageContent) ||\n\t\t\t\tmessageContent.includes(replacedName)\n\t\t\t);\n\t\t});\n\n\t\tif (targetChannel) {\n\t\t\tvoiceManager.joinChannel(targetChannel as BaseGuildVoiceChannel);\n\t\t\treturn true;\n\t\t}\n\t\tconst guild = client.guilds.cache.get(serverId);\n\t\tconst members = guild?.members.cache;\n\n\t\t// get the member who's stringTouuid(id) === message userId\n\t\tconst member = members?.find(\n\t\t\t(member) => createUniqueUuid(runtime, member.id) === message.entityId,\n\t\t);\n\n\t\tif (member?.voice?.channel) {\n\t\t\tvoiceManager.joinChannel(member?.voice?.channel as BaseGuildVoiceChannel);\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought: `I joined the voice channel ${member?.voice?.channel?.name}`,\n\t\t\t\t\tactions: [\"JOIN_VOICE_STARTED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"JOIN_VOICE\",\n\t\t\t\t},\n\t\t\t});\n\n\t\t\t// save a memory for the new channel as well\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: createUniqueUuid(runtime, targetChannel.id),\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought: `I joined the voice channel ${targetChannel.name}`,\n\t\t\t\t\tactions: [\"JOIN_VOICE_STARTED\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"JOIN_VOICE\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn true;\n\t\t}\n\n\t\tconst messageTemplate = `\nThe user has requested to join a voice channel.\nHere is the list of channels available in the server:\n{{voiceChannels}}\n\nHere is the user's request:\n{{userMessage}}\n\nPlease respond with the name of the voice channel which the bot should join. Try to infer what channel the user is talking about. If the user didn't specify a voice channel, respond with \"none\".\nYou should only respond with the name of the voice channel or none, no commentary or additional information should be included.\n`;\n\n\t\tconst guessState = {\n\t\t\tuserMessage: message.content.text,\n\t\t\tvoiceChannels: voiceChannels\n\t\t\t\t.map((channel) => (channel as { name: string }).name)\n\t\t\t\t.join(\"\\n\"),\n\t\t};\n\n\t\tconst prompt = composePromptFromState({\n\t\t\ttemplate: messageTemplate,\n\t\t\tstate: guessState as unknown as State,\n\t\t});\n\n\t\tconst responseContent = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\n\t\tif (responseContent && responseContent.trim().length > 0) {\n\t\t\t// join the voice channel\n\t\t\tconst channelName = responseContent.toLowerCase();\n\n\t\t\tconst targetChannel = voiceChannels.find((channel) => {\n\t\t\t\tconst name = (channel as { name: string }).name.toLowerCase();\n\n\t\t\t\t// remove all non-alphanumeric characters (keep spaces between words)\n\t\t\t\tconst replacedName = name.replace(/[^a-z0-9 ]/g, \"\");\n\n\t\t\t\treturn (\n\t\t\t\t\tname.includes(channelName) ||\n\t\t\t\t\tchannelName.includes(name) ||\n\t\t\t\t\treplacedName.includes(channelName) ||\n\t\t\t\t\tchannelName.includes(replacedName)\n\t\t\t\t);\n\t\t\t});\n\n\t\t\tif (targetChannel) {\n\t\t\t\tvoiceManager.joinChannel(targetChannel as BaseGuildVoiceChannel);\n\t\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\t\tentityId: message.entityId,\n\t\t\t\t\tagentId: message.agentId,\n\t\t\t\t\troomId: message.roomId,\n\t\t\t\t\tcontent: {\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\tthought: `I joined the voice channel ${member?.voice?.channel?.name}`,\n\t\t\t\t\t\tactions: [\"JOIN_VOICE_STARTED\"],\n\t\t\t\t\t},\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\ttype: \"JOIN_VOICE\",\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\t// save a memory for the new channel as well\n\t\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\t\tentityId: message.entityId,\n\t\t\t\t\tagentId: message.agentId,\n\t\t\t\t\troomId: createUniqueUuid(runtime, targetChannel.id),\n\t\t\t\t\tcontent: {\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\tthought: `I joined the voice channel ${targetChannel.name}`,\n\t\t\t\t\t\tactions: [\"JOIN_VOICE_STARTED\"],\n\t\t\t\t\t},\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\ttype: \"JOIN_VOICE\",\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tawait callback({\n\t\t\ttext: \"I couldn't figure out which channel you wanted me to join.\",\n\t\t\tsource: \"discord\",\n\t\t});\n\t\treturn false;\n\t},\n\texamples: [\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Hey, let's jump into the 'General' voice and chat\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Sounds good\",\n\t\t\t\t\tactions: [\"JOIN_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"{{name2}}, can you join the vc, I want to discuss our strat\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Sure I'll join right now\",\n\t\t\t\t\tactions: [\"JOIN_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"hey {{name2}}, we're having a team meeting in the 'conference' voice channel, plz join us\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"OK see you there\",\n\t\t\t\t\tactions: [\"JOIN_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"{{name2}}, let's have a quick voice chat in the 'Lounge' channel.\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"kk be there in a sec\",\n\t\t\t\t\tactions: [\"JOIN_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Hey {{name2}}, can you join me in the 'Music' voice channel\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Sure\",\n\t\t\t\t\tactions: [\"JOIN_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"join voice chat with us {{name2}}\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"coming\",\n\t\t\t\t\tactions: [\"JOIN_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"hop in vc {{name2}}\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"joining now\",\n\t\t\t\t\tactions: [\"JOIN_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"get in vc with us {{name2}}\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"im in\",\n\t\t\t\t\tactions: [\"JOIN_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t] as ActionExample[][],\n} as Action;\n","import type {\n\tCharacter,\n\tEntityPayload,\n\tMessagePayload,\n\tWorldPayload\n} from \"@elizaos/core\";\nimport type { Client, Guild, GuildMember, Message, MessageReaction, User, VoiceState } from \"discord.js\";\n\n/**\n * Discord-specific event types\n */\nexport enum DiscordEventTypes {\n\t// Message events (prefixed versions of core events)\n\tMESSAGE_RECEIVED = \"DISCORD_MESSAGE_RECEIVED\",\n\tMESSAGE_SENT = \"DISCORD_MESSAGE_SENT\",\n\t\n\t// Reaction events\n\tREACTION_RECEIVED = \"DISCORD_REACTION_RECEIVED\",\n\tREACTION_REMOVED = \"DISCORD_REACTION_REMOVED\",\n\t\n\t// Server events\n\tWORLD_JOINED = \"DISCORD_WORLD_JOINED\",\n\tWORLD_CONNECTED = \"DISCORD_SERVER_CONNECTED\",\n\t\n\t// User events\n\tENTITY_JOINED = \"DISCORD_USER_JOINED\",\n\tENTITY_LEFT = \"DISCORD_USER_LEFT\",\n\t\n\t// Voice events\n\tVOICE_STATE_CHANGED = \"DISCORD_VOICE_STATE_CHANGED\",\n}\n\n/**\n * Discord-specific message received payload\n */\nexport interface DiscordMessageReceivedPayload extends MessagePayload {\n\t/** The original Discord message */\n\toriginalMessage: Message;\n}\n\n/**\n * Discord-specific message sent payload\n */\nexport interface DiscordMessageSentPayload extends MessagePayload {\n\t/** The original Discord messages sent */\n\toriginalMessages: Message[];\n}\n\n/**\n * Discord-specific reaction received payload\n */\nexport interface DiscordReactionPayload extends MessagePayload {\n\t/** The original Discord reaction */\n\toriginalReaction: MessageReaction;\n\t/** The user who reacted */\n\tuser: User;\n}\n/**\n * Discord-specific server payload\n */\nexport interface DiscordServerPayload extends WorldPayload {\n\t/** The original Discord guild */\n\tserver: Guild;\n}\n\n/**\n * Discord-specific user joined payload\n */\nexport interface DiscordUserJoinedPayload extends EntityPayload {\n\t/** The original Discord guild member */\n\tmember: GuildMember;\n}\n\n/**\n * Discord-specific user left payload\n */\nexport interface DiscordUserLeftPayload extends EntityPayload {\n\t/** The original Discord guild member */\n\tmember: GuildMember;\n}\n\n/**\n * Discord-specific voice state changed payload\n */\nexport interface DiscordVoiceStateChangedPayload {\n\t/** The original Discord voice state */\n\tvoiceState: VoiceState;\n}\n\n/**\n * Maps Discord event types to their payload interfaces\n */\nexport interface DiscordEventPayloadMap {\n\t[DiscordEventTypes.MESSAGE_RECEIVED]: DiscordMessageReceivedPayload;\n\t[DiscordEventTypes.MESSAGE_SENT]: DiscordMessageSentPayload;\n\t[DiscordEventTypes.REACTION_RECEIVED]: DiscordReactionPayload;\n\t[DiscordEventTypes.REACTION_REMOVED]: DiscordReactionPayload;\n\t[DiscordEventTypes.WORLD_JOINED]: DiscordServerPayload;\n\t[DiscordEventTypes.WORLD_CONNECTED]: DiscordServerPayload;\n\t[DiscordEventTypes.ENTITY_JOINED]: DiscordUserJoinedPayload;\n\t[DiscordEventTypes.ENTITY_LEFT]: DiscordUserLeftPayload;\n\t[DiscordEventTypes.VOICE_STATE_CHANGED]: DiscordVoiceStateChangedPayload;\n}\n\n/**\n * Interface representing a Discord service.\n *\n * @typedef {Object} IDiscordService\n * @property {Client} client - The Discord client object.\n * @property {Character} character - The character object.\n */\nexport interface IDiscordService {\n\tclient: Client;\n\tcharacter: Character;\n}\n\nexport const ServiceTypes = {\n\tDISCORD: \"discord\",\n} as const;\n","// src/actions/leaveVoice\nimport {\n\ttype Action,\n\ttype ActionExample,\n\tChannelType,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype Memory,\n\ttype State,\n\tcreateUniqueUuid,\n\tlogger,\n} from \"@elizaos/core\";\nimport { BaseGuildVoiceChannel } from \"discord.js\";\n\nimport type { DiscordService } from \"../index\";\nimport { ServiceTypes } from \"../types\";\nimport type { VoiceManager } from \"../voice\";\n\nexport default {\n\tname: \"LEAVE_VOICE\",\n\tsimiles: [\n\t\t\"LEAVE_VOICE\",\n\t\t\"LEAVE_VC\",\n\t\t\"LEAVE_VOICE_CHAT\",\n\t\t\"LEAVE_VOICE_CHANNEL\",\n\t\t\"LEAVE_MEETING\",\n\t\t\"LEAVE_CALL\",\n\t],\n\tvalidate: async (runtime: IAgentRuntime, message: Memory, state: State) => {\n\t\tif (message.content.source !== \"discord\") {\n\t\t\t// not a discord message\n\t\t\treturn false;\n\t\t}\n\n\t\tconst service = runtime.getService(ServiceTypes.DISCORD) as DiscordService;\n\n\t\tif (!service) {\n\t\t\tlogger.error(\"Discord client not found\");\n\t\t\treturn false;\n\t\t}\n\n\t\tconst room =\n\t\t\tstate.data.room ??\n\t\t\t(await runtime.getRoom(message.roomId));\n\n\t\tif (room?.type !== ChannelType.GROUP) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check if the client is connected to any voice channel\n\t\tconst isConnectedToVoice = service.client.voice.adapters.size > 0;\n\n\t\treturn isConnectedToVoice;\n\t},\n\tdescription: \"Leave the current voice channel.\",\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\t_state: State,\n\t\t_options: any,\n\t): Promise<boolean> => {\n\t\tconst room = await runtime.getRoom(message.roomId);\n\t\tif (!room) {\n\t\t\tthrow new Error(\"No room found\");\n\t\t}\n\n\t\tif (room.type !== ChannelType.GROUP) {\n\t\t\t// only handle in a group scenario for now\n\t\t\tthrow new Error(\"Not a group\");\n\t\t}\n\n\t\tconst serverId = room.serverId;\n\n\t\tif (!serverId) {\n\t\t\tthrow new Error(\"No server ID found 9\");\n\t\t}\n\t\tconst discordClient = runtime.getService(\n\t\t\tServiceTypes.DISCORD,\n\t\t) as DiscordService;\n\t\tconst voiceManager = discordClient.voiceManager as VoiceManager;\n\t\tconst client = discordClient.client;\n\n\t\tif (!client) {\n\t\t\tlogger.error(\"Discord client not found\");\n\t\t\tthrow new Error(\"Discord client not found\");\n\t\t}\n\n\t\tif (!voiceManager) {\n\t\t\tlogger.error(\"voiceManager is not available.\");\n\t\t\tthrow new Error(\"voiceManager is not available.\");\n\t\t}\n\n\t\tconst guild = client.guilds.cache.get(serverId);\n\n\t\tif (!guild) {\n\t\t\tconsole.warn(\"Bot is not in any voice channel.\");\n\t\t\t// create a memory with thought to self to self\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought:\n\t\t\t\t\t\t\"I tried to leave the voice channel but I'm not in any voice channel.\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"LEAVE_VOICE\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\n\t\tconst voiceChannel = guild.members.me?.voice.channel;\n\n\t\tif (!voiceChannel || !(voiceChannel instanceof BaseGuildVoiceChannel)) {\n\t\t\tconsole.warn(\"Could not retrieve the voice channel.\");\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought: \"I tried to leave the voice channel but I couldn't find it.\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"LEAVE_VOICE\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\n\t\tconst connection = voiceManager.getVoiceConnection(guild.id);\n\t\tif (!connection) {\n\t\t\tconsole.warn(\"No active voice connection found for the bot.\");\n\t\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\t\tentityId: message.entityId,\n\t\t\t\tagentId: message.agentId,\n\t\t\t\troomId: message.roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tthought:\n\t\t\t\t\t\t\"I tried to leave the voice channel but I couldn't find the connection.\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: \"LEAVE_VOICE\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\n\t\tvoiceManager.leaveChannel(voiceChannel);\n\t\t// save a memory for the new channel as well\n\t\tawait runtime.getMemoryManager(\"messages\").createMemory({\n\t\t\tentityId: message.entityId,\n\t\t\tagentId: message.agentId,\n\t\t\troomId: createUniqueUuid(runtime, voiceChannel.id),\n\t\t\tcontent: {\n\t\t\t\tsource: \"discord\",\n\t\t\t\tthought: `I left the voice channel ${voiceChannel.name}`,\n\t\t\t\tactions: [\"LEAVE_VOICE_STARTED\"],\n\t\t\t},\n\t\t\tmetadata: {\n\t\t\t\ttype: \"LEAVE_VOICE\",\n\t\t\t},\n\t\t});\n\n\t\treturn true;\n\t},\n\texamples: [\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Hey {{name2}} please leave the voice channel\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Sure\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"I have to go now but thanks for the chat\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"You too, talk to you later\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Great call everyone, hopping off now\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Agreed, I'll hop off too\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Hey {{name2}} I need you to step away from the voice chat for a bit\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"No worries, I'll leave the voice channel\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"{{name2}}, I think we covered everything, you can leave the voice chat now\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Sounds good, see you both later\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"leave voice {{name2}}\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"ok leaving\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"plz leave the voice chat {{name2}}\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"aight im out\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{name1}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"yo {{name2}} gtfo the vc\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{name2}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"sorry, talk to you later\",\n\t\t\t\t\tactions: [\"LEAVE_VOICE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t] as ActionExample[][],\n} as Action;\n","export const MESSAGE_CONSTANTS = {\n\tMAX_MESSAGES: 10,\n\tRECENT_MESSAGE_COUNT: 3,\n\tCHAT_HISTORY_COUNT: 5,\n\tINTEREST_DECAY_TIME: 5 * 60 * 1000, // 5 minutes\n\tPARTIAL_INTEREST_DECAY: 3 * 60 * 1000, // 3 minutes\n\tDEFAULT_SIMILARITY_THRESHOLD: 0.3,\n\tDEFAULT_SIMILARITY_THRESHOLD_FOLLOW_UPS: 0.2,\n} as const;\n\nexport const MESSAGE_LENGTH_THRESHOLDS = {\n\tLOSE_INTEREST: 100,\n\tSHORT_MESSAGE: 10,\n\tVERY_SHORT_MESSAGE: 2,\n\tIGNORE_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\t\"shut up\",\n\t\"stop\",\n\t\"please shut up\",\n\t\"shut up please\",\n\t\"dont talk\",\n\t\"silence\",\n\t\"stop talking\",\n\t\"be quiet\",\n\t\"hush\",\n\t\"wtf\",\n\t\"chill\",\n\t\"stfu\",\n\t\"stupid bot\",\n\t\"dumb bot\",\n\t\"stop responding\",\n\t\"god damn it\",\n\t\"god damn\",\n\t\"goddamnit\",\n\t\"can you not\",\n\t\"can you stop\",\n\t\"be quiet\",\n\t\"hate you\",\n\t\"hate this\",\n\t\"fuck up\",\n] as const;\n\nexport const IGNORE_RESPONSE_WORDS = [\n\t\"lol\",\n\t\"nm\",\n\t\"uh\",\n\t\"wtf\",\n\t\"stfu\",\n\t\"dumb\",\n\t\"jfc\",\n\t\"omg\",\n] as const;\n\nexport const DISCORD_SERVICE_NAME = \"discord\";\n","import {\n\tChannelType,\n\ttype Content,\n\tEventTypes,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype IBrowserService,\n\ttype IVideoService,\n\ttype Media,\n\ttype Memory,\n\tServiceTypes,\n\tcreateUniqueUuid,\n\tlogger,\n} from \"@elizaos/core\";\nimport {\n\ttype Channel,\n\ttype Client,\n\tChannelType as DiscordChannelType,\n\ttype Message as DiscordMessage,\n\ttype TextChannel,\n} from \"discord.js\";\nimport { AttachmentManager } from \"./attachments\";\nimport { DiscordEventTypes } from \"./types\";\nimport { canSendMessage, sendMessageInChunks } from \"./utils\";\n\n/**\n * Class representing a Message Manager for handling Discord messages.\n */\n\nexport class MessageManager {\n\tprivate client: Client;\n\tprivate runtime: IAgentRuntime;\n\tprivate attachmentManager: AttachmentManager;\n\tprivate getChannelType: (channel: Channel) => Promise<ChannelType>;\n\t/**\n\t * Constructor for a new instance of MyClass.\n\t * @param {any} discordClient - The Discord client object.\n\t */\n\tconstructor(discordClient: any) {\n\t\tthis.client = discordClient.client;\n\t\tthis.runtime = discordClient.runtime;\n\t\tthis.attachmentManager = new AttachmentManager(this.runtime);\n\t\tthis.getChannelType = discordClient.getChannelType;\n\t}\n\n\t/**\n\t * Handles incoming Discord messages and processes them accordingly.\n\t *\n\t * @param {DiscordMessage} message - The Discord message to be handled\n\t */\n\tasync handleMessage(message: DiscordMessage) {\n\t\tif (\n\t\t\tthis.runtime.character.settings?.discord?.allowedChannelIds &&\n\t\t\t!this.runtime.character.settings.discord.allowedChannelIds.some(\n\t\t\t\t(id: string) => id === message.channel.id,\n\t\t\t)\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.interaction || message.author.id === this.client.user?.id) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\tthis.runtime.character.settings?.discord?.shouldIgnoreBotMessages &&\n\t\t\tmessage.author?.bot\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\tthis.runtime.character.settings?.discord?.shouldIgnoreDirectMessages &&\n\t\t\tmessage.channel.type === DiscordChannelType.DM\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst entityId = createUniqueUuid(this.runtime, message.author.id);\n\n\t\tconst userName = message.author.bot\n\t\t\t? `${message.author.username}#${message.author.discriminator}`\n\t\t\t: message.author.username;\n\t\tconst name = message.author.displayName;\n\t\tconst channelId = message.channel.id;\n\t\tconst roomId = createUniqueUuid(this.runtime, channelId);\n\n\t\tlet type: ChannelType;\n\t\tlet serverId: string | undefined;\n\n\t\tif (message.guild) {\n\t\t\tconst guild = await message.guild.fetch();\n\t\t\ttype = await this.getChannelType(message.channel as Channel);\n\t\t\tserverId = guild.id;\n\t\t} else {\n\t\t\ttype = ChannelType.DM;\n\t\t\tserverId = undefined;\n\t\t}\n\n\t\tawait this.runtime.ensureConnection({\n\t\t\tentityId: entityId,\n\t\t\troomId,\n\t\t\tuserName,\n\t\t\tname: name,\n\t\t\tsource: \"discord\",\n\t\t\tchannelId: message.channel.id,\n\t\t\tserverId,\n\t\t\ttype,\n\t\t});\n\n\t\ttry {\n\t\t\tconst canSendResult = canSendMessage(message.channel);\n\t\t\tif (!canSendResult.canSend) {\n\t\t\t\treturn logger.warn(\n\t\t\t\t\t`Cannot send message to channel ${message.channel}`,\n\t\t\t\t\tcanSendResult,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst { processedContent, attachments } =\n\t\t\t\tawait this.processMessage(message);\n\n\t\t\tconst audioAttachments = message.attachments.filter((attachment) =>\n\t\t\t\tattachment.contentType?.startsWith(\"audio/\"),\n\t\t\t);\n\n\t\t\tif (audioAttachments.size > 0) {\n\t\t\t\tconst processedAudioAttachments =\n\t\t\t\t\tawait this.attachmentManager.processAttachments(audioAttachments);\n\t\t\t\tattachments.push(...processedAudioAttachments);\n\t\t\t}\n\n\t\t\tif (!processedContent && !attachments?.length) {\n\t\t\t\t// Only process messages that are not empty\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst entityId = createUniqueUuid(this.runtime, message.author.id);\n\n\t\t\tconst messageId = createUniqueUuid(this.runtime, message.id);\n\n\t\t\tconst newMessage: Memory = {\n\t\t\t\tid: messageId,\n\t\t\t\tentityId: entityId,\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\troomId: roomId,\n\t\t\t\tcontent: {\n\t\t\t\t\t// name: name,\n\t\t\t\t\t// userName: userName,\n\t\t\t\t\ttext: processedContent || \" \",\n\t\t\t\t\tattachments: attachments,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\turl: message.url,\n\t\t\t\t\tinReplyTo: message.reference?.messageId\n\t\t\t\t\t\t? createUniqueUuid(this.runtime, message.reference?.messageId)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t},\n\t\t\t\tcreatedAt: message.createdTimestamp,\n\t\t\t};\n\n\t\t\tconst callback: HandlerCallback = async (\n\t\t\t\tcontent: Content,\n\t\t\t\tfiles: any[],\n\t\t\t) => {\n\t\t\t\ttry {\n\t\t\t\t\tif (message.id && !content.inReplyTo) {\n\t\t\t\t\t\tcontent.inReplyTo = createUniqueUuid(this.runtime, message.id);\n\t\t\t\t\t}\n\t\t\t\t\tconst messages = await sendMessageInChunks(\n\t\t\t\t\t\tmessage.channel as TextChannel,\n\t\t\t\t\t\tcontent.text,\n\t\t\t\t\t\tmessage.id,\n\t\t\t\t\t\tfiles,\n\t\t\t\t\t);\n\n\t\t\t\t\tconst memories: Memory[] = [];\n\t\t\t\t\tfor (const m of messages) {\n\t\t\t\t\t\tconst actions = content.actions;\n\n\t\t\t\t\t\tconst memory: Memory = {\n\t\t\t\t\t\t\tid: createUniqueUuid(this.runtime, m.id),\n\t\t\t\t\t\t\tentityId: this.runtime.agentId,\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t...content,\n\t\t\t\t\t\t\t\tactions,\n\t\t\t\t\t\t\t\tinReplyTo: messageId,\n\t\t\t\t\t\t\t\turl: m.url,\n\t\t\t\t\t\t\t\tchannelType: type,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\troomId,\n\t\t\t\t\t\t\tcreatedAt: m.createdTimestamp,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tmemories.push(memory);\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const m of memories) {\n\t\t\t\t\t\tawait this.runtime.getMemoryManager(\"messages\").createMemory(m);\n\t\t\t\t\t}\n\t\t\t\t\treturn memories;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(\"Error sending message:\", error);\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tthis.runtime.emitEvent([DiscordEventTypes.MESSAGE_RECEIVED, EventTypes.MESSAGE_RECEIVED], {\n\t\t\t\truntime: this.runtime,\n\t\t\t\tmessage: newMessage,\n\t\t\t\tcallback,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error handling message:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Processes the message content, mentions, code blocks, attachments, and URLs to generate\n\t * processed content and media attachments.\n\t *\n\t * @param {DiscordMessage} message The message to process\n\t * @returns {Promise<{ processedContent: string; attachments: Media[] }>} Processed content and media attachments\n\t */\n\tasync processMessage(\n\t\tmessage: DiscordMessage,\n\t): Promise<{ processedContent: string; attachments: Media[] }> {\n\t\tlet processedContent = message.content;\n\t\tlet attachments: Media[] = [];\n\n\t\tconst mentionRegex = /<@!?(\\d+)>/g;\n\t\tprocessedContent = processedContent.replace(\n\t\t\tmentionRegex,\n\t\t\t(match, entityId) => {\n\t\t\t\tconst user = message.mentions.users.get(entityId);\n\t\t\t\tif (user) {\n\t\t\t\t\treturn `${user.username} (@${entityId})`;\n\t\t\t\t}\n\t\t\t\treturn match;\n\t\t\t},\n\t\t);\n\n\t\tconst codeBlockRegex = /```([\\s\\S]*?)```/g;\n\t\tlet match;\n\t\twhile ((match = codeBlockRegex.exec(processedContent))) {\n\t\t\tconst codeBlock = match[1];\n\t\t\tconst lines = codeBlock.split(\"\\n\");\n\t\t\tconst title = lines[0];\n\t\t\tconst description = lines.slice(0, 3).join(\"\\n\");\n\t\t\tconst attachmentId = `code-${Date.now()}-${Math.floor(\n\t\t\t\tMath.random() * 1000,\n\t\t\t)}`.slice(-5);\n\t\t\tattachments.push({\n\t\t\t\tid: attachmentId,\n\t\t\t\turl: \"\",\n\t\t\t\ttitle: title || \"Code Block\",\n\t\t\t\tsource: \"Code\",\n\t\t\t\tdescription: description,\n\t\t\t\ttext: codeBlock,\n\t\t\t});\n\t\t\tprocessedContent = processedContent.replace(\n\t\t\t\tmatch[0],\n\t\t\t\t`Code Block (${attachmentId})`,\n\t\t\t);\n\t\t}\n\n\t\tif (message.attachments.size > 0) {\n\t\t\tattachments = await this.attachmentManager.processAttachments(\n\t\t\t\tmessage.attachments,\n\t\t\t);\n\t\t}\n\n\t\tconst urlRegex = /(https?:\\/\\/[^\\s]+)/g;\n\t\tconst urls = processedContent.match(urlRegex) || [];\n\n\t\tfor (const url of urls) {\n\t\t\tif (\n\t\t\t\tthis.runtime\n\t\t\t\t\t.getService<IVideoService>(ServiceTypes.VIDEO)\n\t\t\t\t\t?.isVideoUrl(url)\n\t\t\t) {\n\t\t\t\tconst videoService = this.runtime.getService<IVideoService>(\n\t\t\t\t\tServiceTypes.VIDEO,\n\t\t\t\t);\n\t\t\t\tif (!videoService) {\n\t\t\t\t\tthrow new Error(\"Video service not found\");\n\t\t\t\t}\n\t\t\t\tconst videoInfo = await videoService.processVideo(url, this.runtime);\n\n\t\t\t\tattachments.push({\n\t\t\t\t\tid: `youtube-${Date.now()}`,\n\t\t\t\t\turl: url,\n\t\t\t\t\ttitle: videoInfo.title,\n\t\t\t\t\tsource: \"YouTube\",\n\t\t\t\t\tdescription: videoInfo.description,\n\t\t\t\t\ttext: videoInfo.text,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tconst browserService = this.runtime.getService<IBrowserService>(\n\t\t\t\t\tServiceTypes.BROWSER,\n\t\t\t\t);\n\t\t\t\tif (!browserService) {\n\t\t\t\t\tthrow new Error(\"Browser service not found\");\n\t\t\t\t}\n\n\t\t\t\tconst { title, description: summary } =\n\t\t\t\t\tawait browserService.getPageContent(url, this.runtime);\n\n\t\t\t\tattachments.push({\n\t\t\t\t\tid: `webpage-${Date.now()}`,\n\t\t\t\t\turl: url,\n\t\t\t\t\ttitle: title || \"Web Page\",\n\t\t\t\t\tsource: \"Web\",\n\t\t\t\t\tdescription: summary,\n\t\t\t\t\ttext: summary,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn { processedContent, attachments };\n\t}\n\n\t/**\n\t * Asynchronously fetches the bot's username and discriminator from Discord API.\n\t *\n\t * @param {string} botToken The token of the bot to authenticate the request\n\t * @returns {Promise<string>} A promise that resolves with the bot's username and discriminator\n\t * @throws {Error} If there is an error while fetching the bot details\n\t */\n\n\tasync fetchBotName(botToken: string) {\n\t\tconst url = \"https://discord.com/api/v10/users/@me\";\n\t\tconst response = await fetch(url, {\n\t\t\tmethod: \"GET\",\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bot ${botToken}`,\n\t\t\t},\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Error fetching bot details: ${response.statusText}`);\n\t\t}\n\n\t\tconst data = await response.json();\n\t\tconst discriminator = data.discriminator;\n\t\treturn (\n\t\t\t(data as { username: string }).username +\n\t\t\t(discriminator ? `#${discriminator}` : \"\")\n\t\t);\n\t}\n}\n","import fs from \"node:fs\";\nimport { trimTokens } from \"@elizaos/core\";\nimport { parseJSONObjectFromText } from \"@elizaos/core\";\nimport {\n\ttype IAgentRuntime,\n\ttype IPdfService,\n\ttype IVideoService,\n\ttype Media,\n\tModelTypes,\n\tServiceTypes,\n} from \"@elizaos/core\";\nimport { type Attachment, Collection } from \"discord.js\";\nimport ffmpeg from \"fluent-ffmpeg\";\n\n/**\n * Generates a summary for the provided text using a specified model.\n *\n * @param {IAgentRuntime} runtime - The runtime environment for the agent.\n * @param {string} text - The text to generate a summary for.\n * @returns {Promise<{ title: string; description: string }>} An object containing the generated title and description.\n */\n\nasync function generateSummary(\n\truntime: IAgentRuntime,\n\ttext: string,\n): Promise<{ title: string; description: string }> {\n\t// make sure text is under 128k characters\n\ttext = await trimTokens(text, 100000, runtime);\n\n\tconst 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\tconst response = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\tprompt,\n\t});\n\n\tconst parsedResponse = parseJSONObjectFromText(response);\n\n\tif (parsedResponse?.title && parsedResponse?.summary) {\n\t\treturn {\n\t\t\ttitle: parsedResponse.title,\n\t\t\tdescription: parsedResponse.summary,\n\t\t};\n\t}\n\n\treturn {\n\t\ttitle: \"\",\n\t\tdescription: \"\",\n\t};\n}\n\n/**\n * Class representing an Attachment Manager.\n */\nexport class AttachmentManager {\n\tprivate attachmentCache: Map<string, Media> = new Map();\n\tprivate runtime: IAgentRuntime;\n\n\t/**\n\t * Constructor for creating a new instance of the class.\n\t *\n\t * @param {IAgentRuntime} runtime The runtime object to be injected into the instance.\n\t */\n\tconstructor(runtime: IAgentRuntime) {\n\t\tthis.runtime = runtime;\n\t}\n\n\t/**\n\t * Processes attachments and returns an array of Media objects.\n\t * @param {Collection<string, Attachment> | Attachment[]} attachments - The attachments to be processed\n\t * @returns {Promise<Media[]>} - An array of processed Media objects\n\t */\n\tasync processAttachments(\n\t\tattachments: Collection<string, Attachment> | Attachment[],\n\t): Promise<Media[]> {\n\t\tconst processedAttachments: Media[] = [];\n\t\tconst attachmentCollection =\n\t\t\tattachments instanceof Collection\n\t\t\t\t? attachments\n\t\t\t\t: new Collection(attachments.map((att) => [att.id, att]));\n\n\t\tfor (const [, attachment] of attachmentCollection) {\n\t\t\tconst media = await this.processAttachment(attachment);\n\t\t\tif (media) {\n\t\t\t\tprocessedAttachments.push(media);\n\t\t\t}\n\t\t}\n\n\t\treturn processedAttachments;\n\t}\n\n\t/**\n\t * Processes the provided attachment to generate a media object.\n\t * If the media for the attachment URL is already cached, it will return the cached media.\n\t * Otherwise, it will determine the type of attachment (PDF, text, audio, video, image, generic)\n\t * and call the corresponding processing method to generate the media object.\n\t *\n\t * @param attachment The attachment to process\n\t * @returns A promise that resolves to a Media object representing the attachment, or null if the attachment could not be processed\n\t */\n\tasync processAttachment(attachment: Attachment): Promise<Media | null> {\n\t\tif (this.attachmentCache.has(attachment.url)) {\n\t\t\treturn this.attachmentCache.get(attachment.url)!;\n\t\t}\n\n\t\tlet media: Media | null = null;\n\t\tif (attachment.contentType?.startsWith(\"application/pdf\")) {\n\t\t\tmedia = await this.processPdfAttachment(attachment);\n\t\t} else if (attachment.contentType?.startsWith(\"text/plain\")) {\n\t\t\tmedia = await this.processPlaintextAttachment(attachment);\n\t\t} else if (\n\t\t\tattachment.contentType?.startsWith(\"audio/\") ||\n\t\t\tattachment.contentType?.startsWith(\"video/mp4\")\n\t\t) {\n\t\t\tmedia = await this.processAudioVideoAttachment(attachment);\n\t\t} else if (attachment.contentType?.startsWith(\"image/\")) {\n\t\t\tmedia = await this.processImageAttachment(attachment);\n\t\t} else if (\n\t\t\tattachment.contentType?.startsWith(\"video/\") ||\n\t\t\tthis.runtime\n\t\t\t\t.getService<IVideoService>(ServiceTypes.VIDEO)\n\t\t\t\t.isVideoUrl(attachment.url)\n\t\t) {\n\t\t\tmedia = await this.processVideoAttachment(attachment);\n\t\t} else {\n\t\t\tmedia = await this.processGenericAttachment(attachment);\n\t\t}\n\n\t\tif (media) {\n\t\t\tthis.attachmentCache.set(attachment.url, media);\n\t\t}\n\t\treturn media;\n\t}\n\n\t/**\n\t * Asynchronously processes an audio or video attachment provided as input and returns a Media object.\n\t * @param {Attachment} attachment - The attachment object containing information about the audio/video file.\n\t * @returns {Promise<Media>} A Promise that resolves to a Media object representing the processed audio/video attachment.\n\t */\n\tprivate async processAudioVideoAttachment(\n\t\tattachment: Attachment,\n\t): Promise<Media> {\n\t\ttry {\n\t\t\tconst response = await fetch(attachment.url);\n\t\t\tconst audioVideoArrayBuffer = await response.arrayBuffer();\n\n\t\t\tlet audioBuffer: Buffer;\n\t\t\tif (attachment.contentType?.startsWith(\"audio/\")) {\n\t\t\t\taudioBuffer = Buffer.from(audioVideoArrayBuffer);\n\t\t\t} else if (attachment.contentType?.startsWith(\"video/mp4\")) {\n\t\t\t\taudioBuffer = await this.extractAudioFromMP4(audioVideoArrayBuffer);\n\t\t\t} else {\n\t\t\t\tthrow new Error(\"Unsupported audio/video format\");\n\t\t\t}\n\n\t\t\tconst transcription = await this.runtime.useModel(\n\t\t\t\tModelTypes.TRANSCRIPTION,\n\t\t\t\taudioBuffer,\n\t\t\t);\n\t\t\tconst { title, description } = await generateSummary(\n\t\t\t\tthis.runtime,\n\t\t\t\ttranscription,\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: title || \"Audio/Video Attachment\",\n\t\t\t\tsource: attachment.contentType?.startsWith(\"audio/\")\n\t\t\t\t\t? \"Audio\"\n\t\t\t\t\t: \"Video\",\n\t\t\t\tdescription:\n\t\t\t\t\tdescription ||\n\t\t\t\t\t\"User-uploaded audio/video attachment which has been transcribed\",\n\t\t\t\ttext: transcription || \"Audio/video content not available\",\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconsole.error(\n\t\t\t\t`Error processing audio/video attachment: ${error.message}`,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: \"Audio/Video Attachment\",\n\t\t\t\tsource: attachment.contentType?.startsWith(\"audio/\")\n\t\t\t\t\t? \"Audio\"\n\t\t\t\t\t: \"Video\",\n\t\t\t\tdescription: \"An audio/video attachment (transcription failed)\",\n\t\t\t\ttext: `This is an audio/video attachment. File name: ${attachment.name}, Size: ${attachment.size} bytes, Content type: ${attachment.contentType}`,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Extracts the audio stream from the provided MP4 data and converts it to MP3 format.\n\t *\n\t * @param {ArrayBuffer} mp4Data - The MP4 data to extract audio from\n\t * @returns {Promise<Buffer>} - A Promise that resolves with the converted audio data as a Buffer\n\t */\n\tprivate async extractAudioFromMP4(mp4Data: ArrayBuffer): Promise<Buffer> {\n\t\t// Use a library like 'fluent-ffmpeg' or 'ffmpeg-static' to extract the audio stream from the MP4 data\n\t\t// and convert it to MP3 or WAV format\n\t\t// Example using fluent-ffmpeg:\n\t\tconst tempMP4File = `temp_${Date.now()}.mp4`;\n\t\tconst tempAudioFile = `temp_${Date.now()}.mp3`;\n\n\t\ttry {\n\t\t\t// Write the MP4 data to a temporary file\n\t\t\tfs.writeFileSync(tempMP4File, Buffer.from(mp4Data));\n\n\t\t\t// Extract the audio stream and convert it to MP3\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tffmpeg(tempMP4File)\n\t\t\t\t\t.outputOptions(\"-vn\") // Disable video output\n\t\t\t\t\t.audioCodec(\"libmp3lame\") // Set audio codec to MP3\n\t\t\t\t\t.save(tempAudioFile) // Save the output to the specified file\n\t\t\t\t\t.on(\"end\", () => {\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t})\n\t\t\t\t\t.on(\"error\", (err) => {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t})\n\t\t\t\t\t.run();\n\t\t\t});\n\n\t\t\t// Read the converted audio file and return it as a Buffer\n\t\t\tconst audioData = fs.readFileSync(tempAudioFile);\n\t\t\treturn audioData;\n\t\t} finally {\n\t\t\t// Clean up the temporary files\n\t\t\tif (fs.existsSync(tempMP4File)) {\n\t\t\t\tfs.unlinkSync(tempMP4File);\n\t\t\t}\n\t\t\tif (fs.existsSync(tempAudioFile)) {\n\t\t\t\tfs.unlinkSync(tempAudioFile);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Processes a PDF attachment by fetching the PDF file from the specified URL,\n\t * converting it to text, generating a summary, and returning a Media object\n\t * with the extracted information.\n\t * If an error occurs during processing, a placeholder Media object is returned\n\t * with an error message.\n\t *\n\t * @param {Attachment} attachment - The PDF attachment to process.\n\t * @returns {Promise<Media>} A promise that resolves to a Media object representing\n\t * the processed PDF attachment.\n\t */\n\tprivate async processPdfAttachment(attachment: Attachment): Promise<Media> {\n\t\ttry {\n\t\t\tconst response = await fetch(attachment.url);\n\t\t\tconst pdfBuffer = await response.arrayBuffer();\n\t\t\tconst text = await this.runtime\n\t\t\t\t.getService<IPdfService>(ServiceTypes.PDF)\n\t\t\t\t.convertPdfToText(Buffer.from(pdfBuffer));\n\t\t\tconst { title, description } = await generateSummary(this.runtime, text);\n\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: title || \"PDF Attachment\",\n\t\t\t\tsource: \"PDF\",\n\t\t\t\tdescription: description || \"A PDF document\",\n\t\t\t\ttext: text,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error processing PDF attachment: ${error.message}`);\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: \"PDF Attachment (conversion failed)\",\n\t\t\t\tsource: \"PDF\",\n\t\t\t\tdescription: \"A PDF document that could not be converted to text\",\n\t\t\t\ttext: `This is a PDF attachment. File name: ${attachment.name}, Size: ${attachment.size} bytes`,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Processes a plaintext attachment by fetching its content, generating a summary, and returning a Media object.\n\t * @param {Attachment} attachment - The attachment object to process.\n\t * @returns {Promise<Media>} A promise that resolves to a Media object representing the processed plaintext attachment.\n\t */\n\tprivate async processPlaintextAttachment(\n\t\tattachment: Attachment,\n\t): Promise<Media> {\n\t\ttry {\n\t\t\tconst response = await fetch(attachment.url);\n\t\t\tconst text = await response.text();\n\t\t\tconst { title, description } = await generateSummary(this.runtime, text);\n\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: title || \"Plaintext Attachment\",\n\t\t\t\tsource: \"Plaintext\",\n\t\t\t\tdescription: description || \"A plaintext document\",\n\t\t\t\ttext: text,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error processing plaintext attachment: ${error.message}`);\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: \"Plaintext Attachment (retrieval failed)\",\n\t\t\t\tsource: \"Plaintext\",\n\t\t\t\tdescription: \"A plaintext document that could not be retrieved\",\n\t\t\t\ttext: `This is a plaintext attachment. File name: ${attachment.name}, Size: ${attachment.size} bytes`,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Process the image attachment by fetching description and title using the IMAGE_DESCRIPTION model.\n\t * If successful, returns a Media object populated with the details. If unsuccessful, creates a fallback\n\t * Media object and logs the error.\n\t *\n\t * @param {Attachment} attachment - The attachment object containing the image details.\n\t * @returns {Promise<Media>} A promise that resolves to a Media object.\n\t */\n\tprivate async processImageAttachment(attachment: Attachment): Promise<Media> {\n\t\ttry {\n\t\t\tconst { description, title } = await this.runtime.useModel(\n\t\t\t\tModelTypes.IMAGE_DESCRIPTION,\n\t\t\t\tattachment.url,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: title || \"Image Attachment\",\n\t\t\t\tsource: \"Image\",\n\t\t\t\tdescription: description || \"An image attachment\",\n\t\t\t\ttext: description || \"Image content not available\",\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error processing image attachment: ${error.message}`);\n\t\t\treturn this.createFallbackImageMedia(attachment);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a fallback Media object for image attachments that could not be recognized.\n\t *\n\t * @param {Attachment} attachment - The attachment object containing image details.\n\t * @returns {Media} - The fallback Media object with basic information about the image attachment.\n\t */\n\n\tprivate createFallbackImageMedia(attachment: Attachment): Media {\n\t\treturn {\n\t\t\tid: attachment.id,\n\t\t\turl: attachment.url,\n\t\t\ttitle: \"Image Attachment\",\n\t\t\tsource: \"Image\",\n\t\t\tdescription: \"An image attachment (recognition failed)\",\n\t\t\ttext: `This is an image attachment. File name: ${attachment.name}, Size: ${attachment.size} bytes, Content type: ${attachment.contentType}`,\n\t\t};\n\t}\n\n\t/**\n\t * Process a video attachment to extract video information.\n\t * @param {Attachment} attachment - The attachment object containing video information.\n\t * @returns {Promise<Media>} A promise that resolves to a Media object with video details.\n\t * @throws {Error} If video service is not available.\n\t */\n\tprivate async processVideoAttachment(attachment: Attachment): Promise<Media> {\n\t\tconst videoService = this.runtime.getService<IVideoService>(\n\t\t\tServiceTypes.VIDEO,\n\t\t);\n\n\t\tif (!videoService) {\n\t\t\tthrow new Error(\"Video service not found\");\n\t\t}\n\n\t\tif (videoService.isVideoUrl(attachment.url)) {\n\t\t\tconst videoInfo = await videoService.processVideo(\n\t\t\t\tattachment.url,\n\t\t\t\tthis.runtime,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: videoInfo.title,\n\t\t\t\tsource: \"YouTube\",\n\t\t\t\tdescription: videoInfo.description,\n\t\t\t\ttext: videoInfo.text,\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tid: attachment.id,\n\t\t\turl: attachment.url,\n\t\t\ttitle: \"Video Attachment\",\n\t\t\tsource: \"Video\",\n\t\t\tdescription: \"A video attachment\",\n\t\t\ttext: \"Video content not available\",\n\t\t};\n\t}\n\n\t/**\n\t * Process a generic attachment and return a Media object with specified properties.\n\t * @param {Attachment} attachment - The attachment object to process.\n\t * @returns {Promise<Media>} A Promise that resolves to a Media object with specified properties.\n\t */\n\tprivate async processGenericAttachment(\n\t\tattachment: Attachment,\n\t): Promise<Media> {\n\t\treturn {\n\t\t\tid: attachment.id,\n\t\t\turl: attachment.url,\n\t\t\ttitle: \"Generic Attachment\",\n\t\t\tsource: \"Generic\",\n\t\t\tdescription: \"A generic attachment\",\n\t\t\ttext: \"Attachment content not available\",\n\t\t};\n\t}\n}\n","import {\n\ttype IAgentRuntime,\n\tModelTypes,\n\tlogger,\n\tparseJSONObjectFromText,\n\ttrimTokens,\n} from \"@elizaos/core\";\nimport {\n\tChannelType,\n\ttype Message as DiscordMessage,\n\tPermissionsBitField,\n\ttype TextChannel,\n\tThreadChannel,\n} from \"discord.js\";\n\n/**\n * Generates a WAV file header based on the provided audio information.\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 audio channels (default is 1).\n * @param {number} [bitsPerSample=16] - The number of bits per audio sample (default is 16).\n * @returns {Buffer} The generated WAV file header as a Buffer object.\n */\nexport function getWavHeader(\n\taudioLength: number,\n\tsampleRate: number,\n\tchannelCount = 1,\n\tbitsPerSample = 16,\n): Buffer {\n\tconst wavHeader = Buffer.alloc(44);\n\twavHeader.write(\"RIFF\", 0);\n\twavHeader.writeUInt32LE(36 + audioLength, 4); // Length of entire file in bytes minus 8\n\twavHeader.write(\"WAVE\", 8);\n\twavHeader.write(\"fmt \", 12);\n\twavHeader.writeUInt32LE(16, 16); // Length of format data\n\twavHeader.writeUInt16LE(1, 20); // Type of format (1 is PCM)\n\twavHeader.writeUInt16LE(channelCount, 22); // Number of channels\n\twavHeader.writeUInt32LE(sampleRate, 24); // Sample rate\n\twavHeader.writeUInt32LE((sampleRate * bitsPerSample * channelCount) / 8, 28); // Byte rate\n\twavHeader.writeUInt16LE((bitsPerSample * channelCount) / 8, 32); // Block align ((BitsPerSample * Channels) / 8)\n\twavHeader.writeUInt16LE(bitsPerSample, 34); // Bits per sample\n\twavHeader.write(\"data\", 36); // Data chunk header\n\twavHeader.writeUInt32LE(audioLength, 40); // Data chunk size\n\treturn wavHeader;\n}\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\truntime: IAgentRuntime,\n\ttext: string,\n): Promise<{ title: string; description: string }> {\n\t// make sure text is under 128k characters\n\ttext = await trimTokens(text, 100000, runtime);\n\n\tconst 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\tconst response = await runtime.useModel(ModelTypes.TEXT_SMALL, {\n\t\tprompt,\n\t});\n\n\tconst parsedResponse = parseJSONObjectFromText(response);\n\n\tif (parsedResponse?.title && parsedResponse?.summary) {\n\t\treturn {\n\t\t\ttitle: parsedResponse.title,\n\t\t\tdescription: parsedResponse.summary,\n\t\t};\n\t}\n\n\treturn {\n\t\ttitle: \"\",\n\t\tdescription: \"\",\n\t};\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 * @returns {Promise<DiscordMessage[]>} - Array of sent Discord messages.\n */\nexport async function sendMessageInChunks(\n\tchannel: TextChannel,\n\tcontent: string,\n\t_inReplyTo: string,\n\tfiles: any[],\n): Promise<DiscordMessage[]> {\n\tconst sentMessages: DiscordMessage[] = [];\n\tconst messages = splitMessage(content);\n\ttry {\n\t\tfor (let i = 0; i < messages.length; i++) {\n\t\t\tconst message = messages[i];\n\t\t\tif (\n\t\t\t\tmessage.trim().length > 0 ||\n\t\t\t\t(i === messages.length - 1 && files && files.length > 0)\n\t\t\t) {\n\t\t\t\tconst options: any = {\n\t\t\t\t\tcontent: message.trim(),\n\t\t\t\t};\n\n\t\t\t\t// if (i === 0 && inReplyTo) {\n\t\t\t\t// // Reply to the specified message for the first chunk\n\t\t\t\t// options.reply = {\n\t\t\t\t// messageReference: inReplyTo,\n\t\t\t\t// };\n\t\t\t\t// }\n\n\t\t\t\tif (i === messages.length - 1 && files && files.length > 0) {\n\t\t\t\t\t// Attach files to the last message chunk\n\t\t\t\t\toptions.files = files;\n\t\t\t\t}\n\n\t\t\t\tconst m = await channel.send(options);\n\t\t\t\tsentMessages.push(m);\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tlogger.error(\"Error sending message:\", error);\n\t}\n\n\treturn 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\tconst messages: string[] = [];\n\tlet currentMessage = \"\";\n\n\tconst rawLines = content?.split(\"\\n\") || [];\n\t// split all lines into MAX_MESSAGE_LENGTH chunks so any long lines are split\n\tconst lines = rawLines.flatMap((line) => {\n\t\tconst chunks = [];\n\t\twhile (line.length > MAX_MESSAGE_LENGTH) {\n\t\t\tchunks.push(line.slice(0, MAX_MESSAGE_LENGTH));\n\t\t\tline = line.slice(MAX_MESSAGE_LENGTH);\n\t\t}\n\t\tchunks.push(line);\n\t\treturn chunks;\n\t});\n\n\tfor (const line of lines) {\n\t\tif (currentMessage.length + line.length + 1 > MAX_MESSAGE_LENGTH) {\n\t\t\tmessages.push(currentMessage.trim());\n\t\t\tcurrentMessage = \"\";\n\t\t}\n\t\tcurrentMessage += `${line}\\n`;\n\t}\n\n\tif (currentMessage.trim().length > 0) {\n\t\tmessages.push(currentMessage.trim());\n\t}\n\n\treturn 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\t// validate input\n\tif (!channel) {\n\t\treturn {\n\t\t\tcanSend: false,\n\t\t\treason: \"No channel given\",\n\t\t};\n\t}\n\t// if it is a DM channel, we can always send messages\n\tif (channel.type === ChannelType.DM) {\n\t\treturn {\n\t\t\tcanSend: true,\n\t\t\treason: null,\n\t\t};\n\t}\n\tconst botMember = channel.guild?.members.cache.get(channel.client.user.id);\n\n\tif (!botMember) {\n\t\treturn {\n\t\t\tcanSend: false,\n\t\t\treason: \"Not a guild channel or bot member not found\",\n\t\t};\n\t}\n\n\t// Required permissions for sending messages\n\tconst requiredPermissions = [\n\t\tPermissionsBitField.Flags.ViewChannel,\n\t\tPermissionsBitField.Flags.SendMessages,\n\t\tPermissionsBitField.Flags.ReadMessageHistory,\n\t];\n\n\t// Add thread-specific permission if it's a thread\n\tif (channel instanceof ThreadChannel) {\n\t\trequiredPermissions.push(PermissionsBitField.Flags.SendMessagesInThreads);\n\t}\n\n\t// Check permissions\n\tconst permissions = channel.permissionsFor(botMember);\n\n\tif (!permissions) {\n\t\treturn {\n\t\t\tcanSend: false,\n\t\t\treason: \"Could not retrieve permissions\",\n\t\t};\n\t}\n\n\t// Check each required permission\n\tconst missingPermissions = requiredPermissions.filter(\n\t\t(perm) => !permissions.has(perm),\n\t);\n\n\treturn {\n\t\tcanSend: missingPermissions.length === 0,\n\t\tmissingPermissions: missingPermissions,\n\t\treason:\n\t\t\tmissingPermissions.length > 0\n\t\t\t\t? `Missing permissions: ${missingPermissions\n\t\t\t\t\t\t.map((p) => String(p))\n\t\t\t\t\t\t.join(\", \")}`\n\t\t\t\t: null,\n\t};\n}\n","import type { IAgentRuntime, Memory, Provider, State } from \"@elizaos/core\";\nimport { ChannelType } from \"@elizaos/core\";\nimport type { DiscordService } from \"../index\";\nimport { ServiceTypes } 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 */\nconst channelStateProvider: Provider = {\n\tname: \"channelState\",\n\tget: async (runtime: IAgentRuntime, message: Memory, state?: State) => {\n\t\tconst room =\n\t\t\tstate.data?.room ??\n\t\t\t(await runtime.getRoom(message.roomId));\n\t\tif (!room) {\n\t\t\tthrow new Error(\"No room found\");\n\t\t}\n\n\t\t// if message source is not discord, return\n\t\tif (message.content.source !== \"discord\") {\n\t\t\treturn {\n\t\t\t\tdata: null,\n\t\t\t\tvalues: {},\n\t\t\t\ttext: \"\",\n\t\t\t};\n\t\t}\n\n\t\tconst agentName = state?.agentName || \"The agent\";\n\t\tconst senderName = state?.senderName || \"someone\";\n\n\t\tlet responseText = \"\";\n\t\tlet channelType = \"\";\n\t\tlet serverName = \"\";\n\t\tlet channelId = \"\";\n\t\tconst serverId = room.serverId;\n\n\t\tif (room.type === ChannelType.DM) {\n\t\t\tchannelType = \"DM\";\n\t\t\tresponseText = `${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\t\t} else {\n\t\t\tchannelType = \"GROUP\";\n\n\t\t\tif (!serverId) {\n\t\t\t\tconsole.error(\"No server ID found\");\n\t\t\t\treturn {\n\t\t\t\t\tdata: {\n\t\t\t\t\t\troom,\n\t\t\t\t\t\tchannelType,\n\t\t\t\t\t},\n\t\t\t\t\tvalues: {\n\t\t\t\t\t\tchannelType,\n\t\t\t\t\t},\n\t\t\t\t\ttext: \"\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tchannelId = room.channelId;\n\n\t\t\tconst discordService = runtime.getService(\n\t\t\t\tServiceTypes.DISCORD,\n\t\t\t) as DiscordService;\n\t\t\tif (!discordService) {\n\t\t\t\tconsole.warn(\"No discord client found\");\n\t\t\t\treturn {\n\t\t\t\t\tdata: {\n\t\t\t\t\t\troom,\n\t\t\t\t\t\tchannelType,\n\t\t\t\t\t\tserverId,\n\t\t\t\t\t},\n\t\t\t\t\tvalues: {\n\t\t\t\t\t\tchannelType,\n\t\t\t\t\t\tserverId,\n\t\t\t\t\t},\n\t\t\t\t\ttext: \"\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst guild = discordService.client.guilds.cache.get(serverId);\n\t\t\tserverName = guild.name;\n\n\t\t\tresponseText = `${agentName} is currently having a conversation in the channel \\`@${channelId} in the server \\`${serverName}\\` (@${serverId})`;\n\t\t\tresponseText += `\\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\t\t}\n\n\t\treturn {\n\t\t\tdata: {\n\t\t\t\troom,\n\t\t\t\tchannelType,\n\t\t\t\tserverId,\n\t\t\t\tserverName,\n\t\t\t\tchannelId,\n\t\t\t},\n\t\t\tvalues: {\n\t\t\t\tchannelType,\n\t\t\t\tserverName,\n\t\t\t\tchannelId,\n\t\t\t},\n\t\t\ttext: responseText,\n\t\t};\n\t},\n};\n\nexport default channelStateProvider;\n","import { getVoiceConnection } from \"@discordjs/voice\";\nimport type { IAgentRuntime, Memory, Provider, State } from \"@elizaos/core\";\nimport { ChannelType } from \"@elizaos/core\";\n\n/**\n * Provides information about the voice state of the user, including whether they are currently in a voice channel.\n *\n * @param {IAgentRuntime} runtime - The runtime object for the agent\n * @param {Memory} message - The message object containing room ID\n * @param {State} [state] - Optional state object for the user\n * @returns {Object} An object containing information about the voice state of the user\n */\nconst voiceStateProvider: Provider = {\n\tname: \"voiceState\",\n\tget: async (runtime: IAgentRuntime, message: Memory, state?: State) => {\n\t\t// Voice doesn't get a discord message, so we need to use the channel for guild data\n\t\tconst room = await runtime.getRoom(message.roomId);\n\t\tif (!room) {\n\t\t\tthrow new Error(\"No room found\");\n\t\t}\n\n\t\tif (room.type !== ChannelType.GROUP) {\n\t\t\t// only handle in a group scenario for now\n\t\t\treturn {\n\t\t\t\tdata: {\n\t\t\t\t\tisInVoiceChannel: false,\n\t\t\t\t\troom,\n\t\t\t\t},\n\t\t\t\tvalues: {\n\t\t\t\t\tisInVoiceChannel: \"false\",\n\t\t\t\t\troomType: room.type,\n\t\t\t\t},\n\t\t\t\ttext: \"\",\n\t\t\t};\n\t\t}\n\n\t\tconst serverId = room.serverId;\n\n\t\tif (!serverId) {\n\t\t\tthrow new Error(\"No server ID found 10\");\n\t\t}\n\n\t\tconst connection = getVoiceConnection(serverId);\n\t\tconst agentName = state?.agentName || \"The agent\";\n\n\t\tif (!connection) {\n\t\t\treturn {\n\t\t\t\tdata: {\n\t\t\t\t\tisInVoiceChannel: false,\n\t\t\t\t\troom,\n\t\t\t\t\tserverId,\n\t\t\t\t},\n\t\t\t\tvalues: {\n\t\t\t\t\tisInVoiceChannel: \"false\",\n\t\t\t\t\tserverId,\n\t\t\t\t},\n\t\t\t\ttext: `${agentName} is not currently in a voice channel`,\n\t\t\t};\n\t\t}\n\n\t\tconst worldId = room.worldId;\n\n\t\t// get the world from the runtime.getWorld\n\t\tconst world = await runtime.getWorld(worldId);\n\n\t\tif (!world) {\n\t\t\tthrow new Error(\"No world found\");\n\t\t}\n\n\t\tconst worldName = world.name;\n\t\tconst roomType = room.type;\n\t\tconst channelId = room.channelId;\n\t\tconst channelName = room.name;\n\n\t\tif (!channelId) {\n\t\t\treturn {\n\t\t\t\tdata: {\n\t\t\t\t\tisInVoiceChannel: true,\n\t\t\t\t\troom,\n\t\t\t\t\tserverId,\n\t\t\t\t\tworld,\n\t\t\t\t\tconnection,\n\t\t\t\t},\n\t\t\t\tvalues: {\n\t\t\t\t\tisInVoiceChannel: \"true\",\n\t\t\t\t\tserverId,\n\t\t\t\t\tworldName,\n\t\t\t\t\troomType,\n\t\t\t\t},\n\t\t\t\ttext: `${agentName} is in an invalid voice channel`,\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tdata: {\n\t\t\t\tisInVoiceChannel: true,\n\t\t\t\troom,\n\t\t\t\tserverId,\n\t\t\t\tworld,\n\t\t\t\tconnection,\n\t\t\t\tchannelId,\n\t\t\t\tchannelName,\n\t\t\t},\n\t\t\tvalues: {\n\t\t\t\tisInVoiceChannel: \"true\",\n\t\t\t\tserverId,\n\t\t\t\tworldName,\n\t\t\t\troomType,\n\t\t\t\tchannelId,\n\t\t\t\tchannelName,\n\t\t\t},\n\t\t\ttext: `${agentName} is currently in the voice channel: ${channelName} (ID: ${channelId})`,\n\t\t};\n\t},\n};\n\nexport default voiceStateProvider;\n","import {\n\tAudioPlayerStatus,\n\tNoSubscriberBehavior,\n\ttype VoiceConnection,\n\tVoiceConnectionStatus,\n\tcreateAudioPlayer,\n\tcreateAudioResource,\n\tentersState,\n} from \"@discordjs/voice\";\nimport {\n\ttype IAgentRuntime,\n\tModelTypes,\n\ttype TestSuite,\n\tlogger,\n} from \"@elizaos/core\";\nimport { ChannelType, Events, type TextChannel } from \"discord.js\";\nimport type { DiscordService } from \"./index\";\nimport { ServiceTypes } from \"./types\";\nimport { sendMessageInChunks } from \"./utils\";\n\nconst TEST_IMAGE_URL =\n\t\"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\tname = \"discord\";\n\tprivate discordClient: DiscordService | null = null;\n\ttests: { name: string; fn: (runtime: IAgentRuntime) => Promise<void> }[];\n\n\t/**\n\t * Constructor for initializing the tests array with test cases to be executed.\n\t *\n\t * @constructor\n\t * @this {TestSuite}\n\t */\n\tconstructor() {\n\t\tthis.tests = [\n\t\t\t{\n\t\t\t\tname: \"Initialize Discord Client\",\n\t\t\t\tfn: this.testCreatingDiscordClient.bind(this),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Slash Commands - Join Voice\",\n\t\t\t\tfn: this.testJoinVoiceSlashCommand.bind(this),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Voice Playback & TTS\",\n\t\t\t\tfn: this.testTextToSpeechPlayback.bind(this),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Send Message with Attachments\",\n\t\t\t\tfn: this.testSendingTextMessage.bind(this),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Handle Incoming Messages\",\n\t\t\t\tfn: this.testHandlingMessage.bind(this),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Slash Commands - Leave Voice\",\n\t\t\t\tfn: this.testLeaveVoiceSlashCommand.bind(this),\n\t\t\t},\n\t\t];\n\t}\n\n\t/**\n\t * Asynchronously tests the creation of Discord client using the provided runtime.\n\t *\n\t * @param {IAgentRuntime} runtime - The agent runtime used to obtain the Discord service.\n\t * @returns {Promise<void>} - A Promise that resolves once the Discord client is ready.\n\t * @throws {Error} - If an error occurs while creating the Discord client.\n\t */\n\tasync testCreatingDiscordClient(runtime: IAgentRuntime) {\n\t\ttry {\n\t\t\tthis.discordClient = runtime.getService(\n\t\t\t\tServiceTypes.DISCORD,\n\t\t\t) as DiscordService;\n\n\t\t\t// Wait for the bot to be ready before proceeding\n\t\t\tif (this.discordClient.client.isReady()) {\n\t\t\t\tlogger.success(\"DiscordService is already ready.\");\n\t\t\t} else {\n\t\t\t\tlogger.info(\"Waiting for DiscordService to be ready...\");\n\t\t\t\tawait new Promise((resolve, reject) => {\n\t\t\t\t\tthis.discordClient.client.once(Events.ClientReady, resolve);\n\t\t\t\t\tthis.discordClient.client.once(Events.Error, reject);\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error in test creating Discord client: ${error}`);\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously tests the join voice slash command functionality.\n\t *\n\t * @param {IAgentRuntime} runtime - The runtime environment for the agent.\n\t * @returns {Promise<void>} - A promise that resolves once the test is complete.\n\t * @throws {Error} - If there is an error in executing the slash command test.\n\t */\n\tasync testJoinVoiceSlashCommand(runtime: IAgentRuntime) {\n\t\ttry {\n\t\t\tawait this.waitForVoiceManagerReady(this.discordClient);\n\n\t\t\tconst channel = await this.getTestChannel(runtime);\n\t\t\tif (!channel || !channel.isTextBased()) {\n\t\t\t\tthrow new Error(\"Invalid test channel for slash command test.\");\n\t\t\t}\n\n\t\t\t// Simulate a join channel slash command interaction\n\t\t\tconst fakeJoinInteraction = {\n\t\t\t\tisCommand: () => true,\n\t\t\t\tcommandName: \"joinchannel\",\n\t\t\t\toptions: {\n\t\t\t\t\tget: (name: string) =>\n\t\t\t\t\t\tname === \"channel\" ? { value: channel.id } : null,\n\t\t\t\t},\n\t\t\t\tguild: (channel as TextChannel).guild,\n\t\t\t\tdeferReply: async () => {},\n\t\t\t\teditReply: async (message: string) => {\n\t\t\t\t\tlogger.info(`JoinChannel Slash Command Response: ${message}`);\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tawait this.discordClient.voiceManager.handleJoinChannelCommand(\n\t\t\t\tfakeJoinInteraction as any,\n\t\t\t);\n\n\t\t\tlogger.success(\"Slash command test completed successfully.\");\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error in slash commands test: ${error}`);\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously tests the leave voice channel slash command.\n\t *\n\t * @param {IAgentRuntime} runtime - The Agent Runtime instance.\n\t * @returns {Promise<void>} A promise that resolves when the test is complete.\n\t */\n\tasync testLeaveVoiceSlashCommand(runtime: IAgentRuntime) {\n\t\ttry {\n\t\t\tawait this.waitForVoiceManagerReady(this.discordClient);\n\n\t\t\tconst channel = await this.getTestChannel(runtime);\n\t\t\tif (!channel || !channel.isTextBased()) {\n\t\t\t\tthrow new Error(\"Invalid test channel for slash command test.\");\n\t\t\t}\n\n\t\t\t// Simulate a leave channel slash command interaction\n\t\t\tconst fakeLeaveInteraction = {\n\t\t\t\tisCommand: () => true,\n\t\t\t\tcommandName: \"leavechannel\",\n\t\t\t\tguildId: (channel as TextChannel).guildId,\n\t\t\t\treply: async (message: string) => {\n\t\t\t\t\tlogger.info(`LeaveChannel Slash Command Response: ${message}`);\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tawait this.discordClient.voiceManager.handleLeaveChannelCommand(\n\t\t\t\tfakeLeaveInteraction as any,\n\t\t\t);\n\n\t\t\tlogger.success(\"Slash command test completed successfully.\");\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error in slash commands test: ${error}`);\n\t\t}\n\t}\n\n\t/**\n\t * Test Text to Speech playback.\n\t * @param {IAgentRuntime} runtime - The Agent Runtime instance.\n\t * @throws {Error} - If voice channel is invalid, voice connection fails to become ready, or no text to speech service found.\n\t */\n\tasync testTextToSpeechPlayback(runtime: IAgentRuntime) {\n\t\ttry {\n\t\t\tawait this.waitForVoiceManagerReady(this.discordClient);\n\n\t\t\tconst channel = await this.getTestChannel(runtime);\n\t\t\tif (!channel || channel.type !== ChannelType.GuildVoice) {\n\t\t\t\tthrow new Error(\"Invalid voice channel.\");\n\t\t\t}\n\n\t\t\tawait this.discordClient.voiceManager.joinChannel(channel);\n\n\t\t\tconst guild = await this.getActiveGuild(this.discordClient);\n\t\t\tconst guildId = guild.id;\n\t\t\tconst connection =\n\t\t\t\tthis.discordClient.voiceManager.getVoiceConnection(guildId);\n\n\t\t\ttry {\n\t\t\t\tawait entersState(connection, VoiceConnectionStatus.Ready, 10_000);\n\t\t\t\tlogger.success(`Voice connection is ready in guild: ${guildId}`);\n\t\t\t} catch (error) {\n\t\t\t\tthrow new Error(`Voice connection failed to become ready: ${error}`);\n\t\t\t}\n\n\t\t\tlet responseStream = null;\n\n\t\t\ttry {\n\t\t\t\tresponseStream = await runtime.useModel(\n\t\t\t\t\tModelTypes.TEXT_TO_SPEECH,\n\t\t\t\t\t`Hi! I'm ${runtime.character.name}! How are you doing today?`,\n\t\t\t\t);\n\t\t\t} catch (_error) {\n\t\t\t\tthrow new Error(\"No text to speech service found\");\n\t\t\t}\n\n\t\t\tif (!responseStream) {\n\t\t\t\tthrow new Error(\"TTS response stream is null or undefined.\");\n\t\t\t}\n\n\t\t\tawait this.playAudioStream(responseStream, connection);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error in TTS playback test: ${error}`);\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously tests sending a text message to a specified channel.\n\t *\n\t * @param {IAgentRuntime} runtime - The runtime for the agent.\n\t * @returns {Promise<void>} A Promise that resolves when the message is sent successfully.\n\t * @throws {Error} If there is an error in sending the text message.\n\t */\n\tasync testSendingTextMessage(runtime: IAgentRuntime) {\n\t\ttry {\n\t\t\tconst channel = await this.getTestChannel(runtime);\n\n\t\t\tawait this.sendMessageToChannel(\n\t\t\t\tchannel as TextChannel,\n\t\t\t\t\"Testing Message\",\n\t\t\t\t[TEST_IMAGE_URL],\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error in sending text message: ${error}`);\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously handles sending a test message using the given runtime and mock user data.\n\t *\n\t * @param {IAgentRuntime} runtime - The agent runtime object.\n\t * @returns {Promise<void>} A Promise that resolves once the message is handled.\n\t */\n\tasync testHandlingMessage(runtime: IAgentRuntime) {\n\t\ttry {\n\t\t\tconst channel = await this.getTestChannel(runtime);\n\n\t\t\tconst fakeMessage = {\n\t\t\t\tcontent: `Hello, ${runtime.character.name}! How are you?`,\n\t\t\t\tauthor: {\n\t\t\t\t\tid: \"mock-user-id\",\n\t\t\t\t\tusername: \"MockUser\",\n\t\t\t\t\tbot: false,\n\t\t\t\t},\n\t\t\t\tchannel,\n\t\t\t\tid: \"mock-message-id\",\n\t\t\t\tcreatedTimestamp: Date.now(),\n\t\t\t\tmentions: {\n\t\t\t\t\thas: () => false,\n\t\t\t\t},\n\t\t\t\treference: null,\n\t\t\t\tattachments: [],\n\t\t\t};\n\t\t\tawait this.discordClient.messageManager.handleMessage(fakeMessage as any);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error in sending text message: ${error}`);\n\t\t}\n\t}\n\n\t// #############################\n\t// Utility Functions\n\t// #############################\n\n\t/**\n\t * Asynchronously retrieves the test channel associated with the provided runtime.\n\t *\n\t * @param {IAgentRuntime} runtime - The runtime object containing necessary information.\n\t * @returns {Promise<Channel>} The test channel retrieved from the Discord client.\n\t * @throws {Error} If no test channel is found.\n\t */\n\tasync getTestChannel(runtime: IAgentRuntime) {\n\t\tconst channelId = this.validateChannelId(runtime);\n\t\tconst channel = await this.discordClient.client.channels.fetch(channelId);\n\n\t\tif (!channel) throw new Error(\"no test channel found!\");\n\n\t\treturn channel;\n\t}\n\n\t/**\n\t * Async function to send a message to a text-based channel.\n\t *\n\t * @param {TextChannel} channel - The text-based channel the message is being sent to.\n\t * @param {string} messageContent - The content of the message being sent.\n\t * @param {any[]} files - An array of files to include in the message.\n\t * @throws {Error} If the channel is not a text-based channel or does not exist.\n\t * @throws {Error} If there is an error sending the message.\n\t */\n\tasync sendMessageToChannel(\n\t\tchannel: TextChannel,\n\t\tmessageContent: string,\n\t\tfiles: any[],\n\t) {\n\t\ttry {\n\t\t\tif (!channel || !channel.isTextBased()) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Channel is not a text-based channel or does not exist.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tawait sendMessageInChunks(\n\t\t\t\tchannel as TextChannel,\n\t\t\t\tmessageContent,\n\t\t\t\tnull,\n\t\t\t\tfiles,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error sending message: ${error}`);\n\t\t}\n\t}\n\n\t/**\n\t * Play an audio stream from a given response stream using the provided VoiceConnection.\n\t *\n\t * @param {any} responseStream - The response stream to play as audio.\n\t * @param {VoiceConnection} connection - The VoiceConnection to use for playing the audio.\n\t * @returns {Promise<void>} - A Promise that resolves when the TTS playback is finished.\n\t */\n\tasync playAudioStream(responseStream: any, connection: VoiceConnection) {\n\t\tconst audioPlayer = createAudioPlayer({\n\t\t\tbehaviors: {\n\t\t\t\tnoSubscriber: NoSubscriberBehavior.Pause,\n\t\t\t},\n\t\t});\n\n\t\tconst audioResource = createAudioResource(responseStream);\n\n\t\taudioPlayer.play(audioResource);\n\t\tconnection.subscribe(audioPlayer);\n\n\t\tlogger.success(\"TTS playback started successfully.\");\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\taudioPlayer.once(AudioPlayerStatus.Idle, () => {\n\t\t\t\tlogger.info(\"TTS playback finished.\");\n\t\t\t\tresolve();\n\t\t\t});\n\n\t\t\taudioPlayer.once(\"error\", (error) => {\n\t\t\t\treject(error);\n\t\t\t\tthrow new Error(`TTS playback error: ${error}`);\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Retrieves the active guild where the bot is currently connected to a voice channel.\n\t *\n\t * @param {DiscordService} discordClient The DiscordService instance used to interact with the Discord API.\n\t * @returns {Promise<Guild>} The active guild where the bot is currently connected to a voice channel.\n\t * @throws {Error} If no active voice connection is found for the bot.\n\t */\n\tasync getActiveGuild(discordClient: DiscordService) {\n\t\tconst guilds = await discordClient.client.guilds.fetch();\n\t\tconst fullGuilds = await Promise.all(guilds.map((guild) => guild.fetch())); // Fetch full guild data\n\n\t\tconst activeGuild = fullGuilds.find((g) => g.members.me?.voice.channelId);\n\t\tif (!activeGuild) {\n\t\t\tthrow new Error(\"No active voice connection found for the bot.\");\n\t\t}\n\t\treturn activeGuild;\n\t}\n\n\t/**\n\t * Waits for the VoiceManager in the Discord client to be ready.\n\t *\n\t * @param {DiscordService} discordClient - The Discord client to check for VoiceManager readiness.\n\t * @throws {Error} If the Discord client is not initialized.\n\t * @returns {Promise<void>} A promise that resolves when the VoiceManager is ready.\n\t */\n\tprivate async waitForVoiceManagerReady(discordClient: DiscordService) {\n\t\tif (!discordClient) {\n\t\t\tthrow new Error(\"Discord client is not initialized.\");\n\t\t}\n\n\t\tif (!discordClient.voiceManager.isReady()) {\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tdiscordClient.voiceManager.once(\"ready\", resolve);\n\t\t\t\tdiscordClient.voiceManager.once(\"error\", reject);\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Validates the Discord test channel ID by checking if it is set in the runtime or environment variables.\n\t * If the test channel ID is not set, an error is thrown.\n\t *\n\t * @param {IAgentRuntime} runtime The runtime object containing the settings and environment variables.\n\t * @returns {string} The validated Discord test channel ID.\n\t */\n\tprivate validateChannelId(runtime: IAgentRuntime) {\n\t\tconst testChannelId =\n\t\t\truntime.getSetting(\"DISCORD_TEST_CHANNEL_ID\") ||\n\t\t\tprocess.env.DISCORD_TEST_CHANNEL_ID;\n\t\tif (!testChannelId) {\n\t\t\tthrow new Error(\n\t\t\t\t\"DISCORD_TEST_CHANNEL_ID is not set. Please provide a valid channel ID in the environment variables.\",\n\t\t\t);\n\t\t}\n\t\treturn testChannelId;\n\t}\n}\n","import { EventEmitter } from \"node:events\";\nimport { type Readable, pipeline } from \"node:stream\";\nimport {\n\ttype AudioPlayer,\n\ttype AudioReceiveStream,\n\tNoSubscriberBehavior,\n\tStreamType,\n\ttype VoiceConnection,\n\tVoiceConnectionStatus,\n\tcreateAudioPlayer,\n\tcreateAudioResource,\n\tentersState,\n\tgetVoiceConnections,\n\tjoinVoiceChannel,\n} from \"@discordjs/voice\";\nimport {\n\tChannelType,\n\ttype Content,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelTypes,\n\ttype UUID,\n\tcreateUniqueUuid,\n\tlogger,\n} from \"@elizaos/core\";\nimport {\n\ttype BaseGuildVoiceChannel,\n\ttype Channel,\n\ttype Client,\n\tChannelType as DiscordChannelType,\n\ttype Guild,\n\ttype GuildMember,\n\ttype VoiceChannel,\n\ttype VoiceState,\n} from \"discord.js\";\nimport prism from \"prism-media\";\nimport type { DiscordService } from \"./index\";\nimport { getWavHeader } from \"./utils\";\n\n// These values are chosen for compatibility with picovoice components\nconst DECODE_FRAME_SIZE = 1024;\nconst DECODE_SAMPLE_RATE = 16000;\n\n/**\n * Class representing an AudioMonitor that listens for audio data from a Readable stream.\n */\nexport class AudioMonitor {\n\tprivate readable: Readable;\n\tprivate buffers: Buffer[] = [];\n\tprivate maxSize: number;\n\tprivate lastFlagged = -1;\n\tprivate ended = false;\n\n\t/**\n\t * Constructs an AudioMonitor instance.\n\t * @param {Readable} readable - The readable stream to monitor for audio data.\n\t * @param {number} maxSize - The maximum size of the audio buffer.\n\t * @param {function} onStart - The callback function to be called when audio starts.\n\t * @param {function} callback - The callback function to process audio data.\n\t */\n\tconstructor(\n\t\treadable: Readable,\n\t\tmaxSize: number,\n\t\tonStart: () => void,\n\t\tcallback: (buffer: Buffer) => void,\n\t) {\n\t\tthis.readable = readable;\n\t\tthis.maxSize = maxSize;\n\t\tthis.readable.on(\"data\", (chunk: Buffer) => {\n\t\t\t//console.log('AudioMonitor got data');\n\t\t\tif (this.lastFlagged < 0) {\n\t\t\t\tthis.lastFlagged = this.buffers.length;\n\t\t\t}\n\t\t\tthis.buffers.push(chunk);\n\t\t\tconst currentSize = this.buffers.reduce(\n\t\t\t\t(acc, cur) => acc + cur.length,\n\t\t\t\t0,\n\t\t\t);\n\t\t\twhile (currentSize > this.maxSize) {\n\t\t\t\tthis.buffers.shift();\n\t\t\t\tthis.lastFlagged--;\n\t\t\t}\n\t\t});\n\t\tthis.readable.on(\"end\", () => {\n\t\t\tlogger.log(\"AudioMonitor ended\");\n\t\t\tthis.ended = true;\n\t\t\tif (this.lastFlagged < 0) return;\n\t\t\tcallback(this.getBufferFromStart());\n\t\t\tthis.lastFlagged = -1;\n\t\t});\n\t\tthis.readable.on(\"speakingStopped\", () => {\n\t\t\tif (this.ended) return;\n\t\t\tlogger.log(\"Speaking stopped\");\n\t\t\tif (this.lastFlagged < 0) return;\n\t\t\tcallback(this.getBufferFromStart());\n\t\t});\n\t\tthis.readable.on(\"speakingStarted\", () => {\n\t\t\tif (this.ended) return;\n\t\t\tonStart();\n\t\t\tlogger.log(\"Speaking started\");\n\t\t\tthis.reset();\n\t\t});\n\t}\n\n\t/**\n\t * Stops listening to \"data\", \"end\", \"speakingStopped\", and \"speakingStarted\" events on the readable stream.\n\t */\n\tstop() {\n\t\tthis.readable.removeAllListeners(\"data\");\n\t\tthis.readable.removeAllListeners(\"end\");\n\t\tthis.readable.removeAllListeners(\"speakingStopped\");\n\t\tthis.readable.removeAllListeners(\"speakingStarted\");\n\t}\n\n\t/**\n\t * Check if the item is flagged.\n\t * @returns {boolean} True if the item was flagged, false otherwise.\n\t */\n\tisFlagged() {\n\t\treturn this.lastFlagged >= 0;\n\t}\n\n\t/**\n\t * Returns a Buffer containing all buffers starting from the last flagged index.\n\t * If the last flagged index is less than 0, returns null.\n\t *\n\t * @returns {Buffer | null} The concatenated Buffer or null\n\t */\n\tgetBufferFromFlag() {\n\t\tif (this.lastFlagged < 0) {\n\t\t\treturn null;\n\t\t}\n\t\tconst buffer = Buffer.concat(this.buffers.slice(this.lastFlagged));\n\t\treturn buffer;\n\t}\n\n\t/**\n\t * Concatenates all buffers in the array and returns a single buffer.\n\t *\n\t * @returns {Buffer} The concatenated buffer from the start.\n\t */\n\tgetBufferFromStart() {\n\t\tconst buffer = Buffer.concat(this.buffers);\n\t\treturn buffer;\n\t}\n\n\t/**\n\t * Resets the buffers array and sets lastFlagged to -1.\n\t */\n\treset() {\n\t\tthis.buffers = [];\n\t\tthis.lastFlagged = -1;\n\t}\n\n\t/**\n\t * Check if the object has ended.\n\t * @returns {boolean} Returns true if the object has ended; false otherwise.\n\t */\n\tisEnded() {\n\t\treturn this.ended;\n\t}\n}\n\n/**\n * Class representing a VoiceManager that extends EventEmitter.\n * @extends EventEmitter\n */\nexport class VoiceManager extends EventEmitter {\n\tprivate processingVoice = false;\n\tprivate transcriptionTimeout: NodeJS.Timeout | null = null;\n\tprivate userStates: Map<\n\t\tstring,\n\t\t{\n\t\t\tbuffers: Buffer[];\n\t\t\ttotalLength: number;\n\t\t\tlastActive: number;\n\t\t\ttranscriptionText: string;\n\t\t}\n\t> = new Map();\n\tprivate activeAudioPlayer: AudioPlayer | null = null;\n\tprivate client: Client;\n\tprivate runtime: IAgentRuntime;\n\tprivate streams: Map<string, Readable> = new Map();\n\tprivate connections: Map<string, VoiceConnection> = new Map();\n\tprivate activeMonitors: Map<\n\t\tstring,\n\t\t{ channel: BaseGuildVoiceChannel; monitor: AudioMonitor }\n\t> = new Map();\n\tprivate ready: boolean;\n\n\t/**\n\t * Constructor for initializing a new instance of the class.\n\t *\n\t * @param {DiscordService} service - The Discord service to use.\n\t * @param {IAgentRuntime} runtime - The runtime for the agent.\n\t */\n\tconstructor(service: DiscordService, runtime: IAgentRuntime) {\n\t\tsuper();\n\t\tthis.client = service.client;\n\t\tthis.runtime = runtime;\n\n\t\tthis.client.on(\"voiceManagerReady\", () => {\n\t\t\tthis.setReady(true);\n\t\t});\n\t}\n\n\t/**\n\t * Asynchronously retrieves the type of the channel.\n\t * @param {Channel} channel - The channel to get the type for.\n\t * @returns {Promise<ChannelType>} The type of the channel.\n\t */\n\tasync getChannelType(channel: Channel): Promise<ChannelType> {\n\t\tswitch (channel.type) {\n\t\t\tcase DiscordChannelType.GuildVoice:\n\t\t\tcase DiscordChannelType.GuildStageVoice:\n\t\t\t\treturn ChannelType.VOICE_GROUP;\n\t\t}\n\t}\n\n\t/**\n\t * Set the ready status of the VoiceManager.\n\t * @param {boolean} status - The status to set.\n\t */\n\tprivate setReady(status: boolean) {\n\t\tthis.ready = status;\n\t\tthis.emit(\"ready\");\n\t\tlogger.debug(`VoiceManager is now ready: ${this.ready}`);\n\t}\n\n\t/**\n\t * Check if the object is ready.\n\t *\n\t * @returns {boolean} True if the object is ready, false otherwise.\n\t */\n\tisReady() {\n\t\treturn this.ready;\n\t}\n\n\t/**\n\t * Handle voice state update event.\n\t * @param {VoiceState} oldState - The old voice state of the member.\n\t * @param {VoiceState} newState - The new voice state of the member.\n\t * @returns {void}\n\t */\n\tasync handleVoiceStateUpdate(oldState: VoiceState, newState: VoiceState) {\n\t\tconst oldChannelId = oldState.channelId;\n\t\tconst newChannelId = newState.channelId;\n\t\tconst member = newState.member;\n\t\tif (!member) return;\n\t\tif (member.id === this.client.user?.id) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Ignore mute/unmute events\n\t\tif (oldChannelId === newChannelId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// User leaving a channel where the bot is present\n\t\tif (oldChannelId && this.connections.has(oldChannelId)) {\n\t\t\tthis.stopMonitoringMember(member.id);\n\t\t}\n\n\t\t// User joining a channel where the bot is present\n\t\tif (newChannelId && this.connections.has(newChannelId)) {\n\t\t\tawait this.monitorMember(\n\t\t\t\tmember,\n\t\t\t\tnewState.channel as BaseGuildVoiceChannel,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Joins a voice channel and sets up the necessary connection and event listeners.\n\t * @param {BaseGuildVoiceChannel} channel - The voice channel to join\n\t */\n\tasync joinChannel(channel: BaseGuildVoiceChannel) {\n\t\tconst oldConnection = this.getVoiceConnection(channel.guildId as string);\n\t\tif (oldConnection) {\n\t\t\ttry {\n\t\t\t\toldConnection.destroy();\n\t\t\t\t// Remove all associated streams and monitors\n\t\t\t\tthis.streams.clear();\n\t\t\t\tthis.activeMonitors.clear();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Error leaving voice channel:\", error);\n\t\t\t}\n\t\t}\n\n\t\tconst connection = joinVoiceChannel({\n\t\t\tchannelId: channel.id,\n\t\t\tguildId: channel.guild.id,\n\t\t\tadapterCreator: channel.guild.voiceAdapterCreator as any,\n\t\t\tselfDeaf: false,\n\t\t\tselfMute: false,\n\t\t\tgroup: this.client.user.id,\n\t\t});\n\n\t\ttry {\n\t\t\t// Wait for either Ready or Signalling state\n\t\t\tawait Promise.race([\n\t\t\t\tentersState(connection, VoiceConnectionStatus.Ready, 20_000),\n\t\t\t\tentersState(connection, VoiceConnectionStatus.Signalling, 20_000),\n\t\t\t]);\n\n\t\t\t// Log connection success\n\t\t\tlogger.log(\n\t\t\t\t`Voice connection established in state: ${connection.state.status}`,\n\t\t\t);\n\n\t\t\t// Set up ongoing state change monitoring\n\t\t\tconnection.on(\"stateChange\", async (oldState, newState) => {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`Voice connection state changed from ${oldState.status} to ${newState.status}`,\n\t\t\t\t);\n\n\t\t\t\tif (newState.status === VoiceConnectionStatus.Disconnected) {\n\t\t\t\t\tlogger.log(\"Handling disconnection...\");\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Try to reconnect if disconnected\n\t\t\t\t\t\tawait Promise.race([\n\t\t\t\t\t\t\tentersState(connection, VoiceConnectionStatus.Signalling, 5_000),\n\t\t\t\t\t\t\tentersState(connection, VoiceConnectionStatus.Connecting, 5_000),\n\t\t\t\t\t\t]);\n\t\t\t\t\t\t// Seems to be reconnecting to a new channel\n\t\t\t\t\t\tlogger.log(\"Reconnecting to channel...\");\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t// Seems to be a real disconnect, destroy and cleanup\n\t\t\t\t\t\tlogger.log(`Disconnection confirmed - cleaning up...${e}`);\n\t\t\t\t\t\tconnection.destroy();\n\t\t\t\t\t\tthis.connections.delete(channel.id);\n\t\t\t\t\t}\n\t\t\t\t} else if (newState.status === VoiceConnectionStatus.Destroyed) {\n\t\t\t\t\tthis.connections.delete(channel.id);\n\t\t\t\t} else if (\n\t\t\t\t\t!this.connections.has(channel.id) &&\n\t\t\t\t\t(newState.status === VoiceConnectionStatus.Ready ||\n\t\t\t\t\t\tnewState.status === VoiceConnectionStatus.Signalling)\n\t\t\t\t) {\n\t\t\t\t\tthis.connections.set(channel.id, connection);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconnection.on(\"error\", (error) => {\n\t\t\t\tlogger.log(\"Voice connection error:\", error);\n\t\t\t\t// Don't immediately destroy - let the state change handler deal with it\n\t\t\t\tlogger.log(\"Connection error - will attempt to recover...\");\n\t\t\t});\n\n\t\t\t// Store the connection\n\t\t\tthis.connections.set(channel.id, connection);\n\n\t\t\t// Continue with voice state modifications\n\t\t\tconst me = channel.guild.members.me;\n\t\t\tif (me?.voice && me.permissions.has(\"DeafenMembers\")) {\n\t\t\t\ttry {\n\t\t\t\t\tawait me.voice.setDeaf(false);\n\t\t\t\t\tawait me.voice.setMute(false);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.log(\"Failed to modify voice state:\", error);\n\t\t\t\t\t// Continue even if this fails\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconnection.receiver.speaking.on(\"start\", async (entityId: string) => {\n\t\t\t\tlet user = channel.members.get(entityId);\n\t\t\t\tif (!user) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuser = await channel.guild.members.fetch(entityId);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconsole.error(\"Failed to fetch user:\", error);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (user && !user?.user.bot) {\n\t\t\t\t\tthis.monitorMember(user as GuildMember, channel);\n\t\t\t\t\tthis.streams.get(entityId)?.emit(\"speakingStarted\");\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconnection.receiver.speaking.on(\"end\", async (entityId: string) => {\n\t\t\t\tconst user = channel.members.get(entityId);\n\t\t\t\tif (!user?.user.bot) {\n\t\t\t\t\tthis.streams.get(entityId)?.emit(\"speakingStopped\");\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tlogger.log(\"Failed to establish voice connection:\", error);\n\t\t\tconnection.destroy();\n\t\t\tthis.connections.delete(channel.id);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Retrieves the voice connection for a given guild ID.\n\t * @param {string} guildId - The ID of the guild to get the voice connection for.\n\t * @returns {VoiceConnection | undefined} The voice connection for the specified guild ID, or undefined if not found.\n\t */\n\tgetVoiceConnection(guildId: string) {\n\t\tconst connections = getVoiceConnections(this.client.user.id);\n\t\tif (!connections) {\n\t\t\treturn;\n\t\t}\n\t\tconst connection = [...connections.values()].find(\n\t\t\t(connection) => connection.joinConfig.guildId === guildId,\n\t\t);\n\t\treturn connection;\n\t}\n\n\t/**\n\t * Monitor a member's audio stream for volume activity and speaking thresholds.\n\t *\n\t * @param {GuildMember} member - The member whose audio stream is being monitored.\n\t * @param {BaseGuildVoiceChannel} channel - The voice channel in which the member is connected.\n\t */\n\tprivate async monitorMember(\n\t\tmember: GuildMember,\n\t\tchannel: BaseGuildVoiceChannel,\n\t) {\n\t\tconst entityId = member?.id;\n\t\tconst userName = member?.user?.username;\n\t\tconst name = member?.user?.displayName;\n\t\tconst connection = this.getVoiceConnection(member?.guild?.id);\n\t\tconst receiveStream = connection?.receiver.subscribe(entityId, {\n\t\t\tautoDestroy: true,\n\t\t\temitClose: true,\n\t\t});\n\t\tif (!receiveStream || receiveStream.readableLength === 0) {\n\t\t\treturn;\n\t\t}\n\t\tconst opusDecoder = new prism.opus.Decoder({\n\t\t\tchannels: 1,\n\t\t\trate: DECODE_SAMPLE_RATE,\n\t\t\tframeSize: DECODE_FRAME_SIZE,\n\t\t});\n\t\tconst volumeBuffer: number[] = [];\n\t\tconst VOLUME_WINDOW_SIZE = 30;\n\t\tconst SPEAKING_THRESHOLD = 0.05;\n\t\topusDecoder.on(\"data\", (pcmData: Buffer) => {\n\t\t\t// Monitor the audio volume while the agent is speaking.\n\t\t\t// If the average volume of the user's audio exceeds the defined threshold, it indicates active speaking.\n\t\t\t// When active speaking is detected, stop the agent's current audio playback to avoid overlap.\n\n\t\t\tif (this.activeAudioPlayer) {\n\t\t\t\tconst samples = new Int16Array(\n\t\t\t\t\tpcmData.buffer,\n\t\t\t\t\tpcmData.byteOffset,\n\t\t\t\t\tpcmData.length / 2,\n\t\t\t\t);\n\t\t\t\tconst maxAmplitude = Math.max(...samples.map(Math.abs)) / 32768;\n\t\t\t\tvolumeBuffer.push(maxAmplitude);\n\n\t\t\t\tif (volumeBuffer.length > VOLUME_WINDOW_SIZE) {\n\t\t\t\t\tvolumeBuffer.shift();\n\t\t\t\t}\n\t\t\t\tconst avgVolume =\n\t\t\t\t\tvolumeBuffer.reduce((sum, v) => sum + v, 0) / VOLUME_WINDOW_SIZE;\n\n\t\t\t\tif (avgVolume > SPEAKING_THRESHOLD) {\n\t\t\t\t\tvolumeBuffer.length = 0;\n\t\t\t\t\tthis.cleanupAudioPlayer(this.activeAudioPlayer);\n\t\t\t\t\tthis.processingVoice = false;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tpipeline(\n\t\t\treceiveStream as AudioReceiveStream,\n\t\t\topusDecoder as any,\n\t\t\t(err: Error | null) => {\n\t\t\t\tif (err) {\n\t\t\t\t\tconsole.log(`Opus decoding pipeline error: ${err}`);\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\t\tthis.streams.set(entityId, opusDecoder);\n\t\tthis.connections.set(entityId, connection as VoiceConnection);\n\t\topusDecoder.on(\"error\", (err: any) => {\n\t\t\tconsole.log(`Opus decoding error: ${err}`);\n\t\t});\n\t\tconst errorHandler = (err: any) => {\n\t\t\tconsole.log(`Opus decoding error: ${err}`);\n\t\t};\n\t\tconst streamCloseHandler = () => {\n\t\t\tconsole.log(`voice stream from ${member?.displayName} closed`);\n\t\t\tthis.streams.delete(entityId);\n\t\t\tthis.connections.delete(entityId);\n\t\t};\n\t\tconst closeHandler = () => {\n\t\t\tconsole.log(`Opus decoder for ${member?.displayName} closed`);\n\t\t\topusDecoder.removeListener(\"error\", errorHandler);\n\t\t\topusDecoder.removeListener(\"close\", closeHandler);\n\t\t\treceiveStream?.removeListener(\"close\", streamCloseHandler);\n\t\t};\n\t\topusDecoder.on(\"error\", errorHandler);\n\t\topusDecoder.on(\"close\", closeHandler);\n\t\treceiveStream?.on(\"close\", streamCloseHandler);\n\n\t\tthis.client.emit(\n\t\t\t\"userStream\",\n\t\t\tentityId,\n\t\t\tname,\n\t\t\tuserName,\n\t\t\tchannel,\n\t\t\topusDecoder,\n\t\t);\n\t}\n\n\t/**\n\t * Leaves the specified voice channel and stops monitoring all members in that channel.\n\t * If there is an active connection in the channel, it will be destroyed.\n\t *\n\t * @param {BaseGuildVoiceChannel} channel - The voice channel to leave.\n\t */\n\tleaveChannel(channel: BaseGuildVoiceChannel) {\n\t\tconst connection = this.connections.get(channel.id);\n\t\tif (connection) {\n\t\t\tconnection.destroy();\n\t\t\tthis.connections.delete(channel.id);\n\t\t}\n\n\t\t// Stop monitoring all members in this channel\n\t\tfor (const [memberId, monitorInfo] of this.activeMonitors) {\n\t\t\tif (\n\t\t\t\tmonitorInfo.channel.id === channel.id &&\n\t\t\t\tmemberId !== this.client.user?.id\n\t\t\t) {\n\t\t\t\tthis.stopMonitoringMember(memberId);\n\t\t\t}\n\t\t}\n\n\t\tconsole.log(`Left voice channel: ${channel.name} (${channel.id})`);\n\t}\n\n\t/**\n\t * Stop monitoring a specific member by their member ID.\n\t * @param {string} memberId - The ID of the member to stop monitoring.\n\t */\n\tstopMonitoringMember(memberId: string) {\n\t\tconst monitorInfo = this.activeMonitors.get(memberId);\n\t\tif (monitorInfo) {\n\t\t\tmonitorInfo.monitor.stop();\n\t\t\tthis.activeMonitors.delete(memberId);\n\t\t\tthis.streams.delete(memberId);\n\t\t\tconsole.log(`Stopped monitoring user ${memberId}`);\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously debounces the process transcription function to prevent rapid execution.\n\t *\n\t * @param {UUID} entityId - The ID of the entity related to the transcription.\n\t * @param {string} name - The name of the entity for transcription.\n\t * @param {string} userName - The username of the user initiating the transcription.\n\t * @param {BaseGuildVoiceChannel} channel - The voice channel where the transcription is happening.\n\t */\n\n\tasync debouncedProcessTranscription(\n\t\tentityId: UUID,\n\t\tname: string,\n\t\tuserName: string,\n\t\tchannel: BaseGuildVoiceChannel,\n\t) {\n\t\tconst DEBOUNCE_TRANSCRIPTION_THRESHOLD = 1500; // wait for 1.5 seconds of silence\n\n\t\tif (this.activeAudioPlayer?.state?.status === \"idle\") {\n\t\t\tlogger.log(\"Cleaning up idle audio player.\");\n\t\t\tthis.cleanupAudioPlayer(this.activeAudioPlayer);\n\t\t}\n\n\t\tif (this.activeAudioPlayer || this.processingVoice) {\n\t\t\tconst state = this.userStates.get(entityId);\n\t\t\tstate.buffers.length = 0;\n\t\t\tstate.totalLength = 0;\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.transcriptionTimeout) {\n\t\t\tclearTimeout(this.transcriptionTimeout);\n\t\t}\n\n\t\tthis.transcriptionTimeout = setTimeout(async () => {\n\t\t\tthis.processingVoice = true;\n\t\t\ttry {\n\t\t\t\tawait this.processTranscription(\n\t\t\t\t\tentityId,\n\t\t\t\t\tchannel.id,\n\t\t\t\t\tchannel,\n\t\t\t\t\tname,\n\t\t\t\t\tuserName,\n\t\t\t\t);\n\n\t\t\t\t// Clean all users' previous buffers\n\t\t\t\tthis.userStates.forEach((state, _) => {\n\t\t\t\t\tstate.buffers.length = 0;\n\t\t\t\t\tstate.totalLength = 0;\n\t\t\t\t});\n\t\t\t} finally {\n\t\t\t\tthis.processingVoice = false;\n\t\t\t}\n\t\t}, DEBOUNCE_TRANSCRIPTION_THRESHOLD) as unknown as NodeJS.Timeout;\n\t}\n\n\t/**\n\t * Handle user audio stream for monitoring purposes.\n\t *\n\t * @param {UUID} userId - The unique identifier of the user.\n\t * @param {string} name - The name of the user.\n\t * @param {string} userName - The username of the user.\n\t * @param {BaseGuildVoiceChannel} channel - The voice channel the user is in.\n\t * @param {Readable} audioStream - The audio stream to monitor.\n\t */\n\tasync handleUserStream(\n\t\tuserId: UUID,\n\t\tname: string,\n\t\tuserName: string,\n\t\tchannel: BaseGuildVoiceChannel,\n\t\taudioStream: Readable,\n\t) {\n\t\tconst entityId = createUniqueUuid(this.runtime, userId);\n\t\tconsole.log(`Starting audio monitor for user: ${entityId}`);\n\t\tif (!this.userStates.has(entityId)) {\n\t\t\tthis.userStates.set(entityId, {\n\t\t\t\tbuffers: [],\n\t\t\t\ttotalLength: 0,\n\t\t\t\tlastActive: Date.now(),\n\t\t\t\ttranscriptionText: \"\",\n\t\t\t});\n\t\t}\n\n\t\tconst state = this.userStates.get(entityId);\n\n\t\tconst processBuffer = async (buffer: Buffer) => {\n\t\t\ttry {\n\t\t\t\tstate?.buffers.push(buffer);\n\t\t\t\tstate!.totalLength += buffer.length;\n\t\t\t\tstate!.lastActive = Date.now();\n\t\t\t\tthis.debouncedProcessTranscription(entityId, name, userName, channel);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(`Error processing buffer for user ${entityId}:`, error);\n\t\t\t}\n\t\t};\n\n\t\tnew AudioMonitor(\n\t\t\taudioStream,\n\t\t\t10000000,\n\t\t\t() => {\n\t\t\t\tif (this.transcriptionTimeout) {\n\t\t\t\t\tclearTimeout(this.transcriptionTimeout);\n\t\t\t\t}\n\t\t\t},\n\t\t\tasync (buffer) => {\n\t\t\t\tif (!buffer) {\n\t\t\t\t\tconsole.error(\"Received empty buffer\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait processBuffer(buffer);\n\t\t\t},\n\t\t);\n\t}\n\n\t/**\n\t * Process the transcription of audio data for a user.\n\t *\n\t * @param {UUID} entityId - The unique ID of the user entity.\n\t * @param {string} channelId - The ID of the channel where the transcription is taking place.\n\t * @param {BaseGuildVoiceChannel} channel - The voice channel where the user is speaking.\n\t * @param {string} name - The name of the user.\n\t * @param {string} userName - The username of the user.\n\t * @returns {Promise<void>}\n\t */\n\tprivate async processTranscription(\n\t\tentityId: UUID,\n\t\tchannelId: string,\n\t\tchannel: BaseGuildVoiceChannel,\n\t\tname: string,\n\t\tuserName: string,\n\t) {\n\t\tconst state = this.userStates.get(entityId);\n\t\tif (!state || state.buffers.length === 0) return;\n\t\ttry {\n\t\t\tconst inputBuffer = Buffer.concat(state.buffers, state.totalLength);\n\n\t\t\tstate.buffers.length = 0; // Clear the buffers\n\t\t\tstate.totalLength = 0;\n\t\t\t// Convert Opus to WAV\n\t\t\tconst wavBuffer = await this.convertOpusToWav(inputBuffer);\n\t\t\tconsole.log(\"Starting transcription...\");\n\n\t\t\tconst transcriptionText = await this.runtime.useModel(\n\t\t\t\tModelTypes.TRANSCRIPTION,\n\t\t\t\twavBuffer,\n\t\t\t);\n\t\t\tfunction isValidTranscription(text: string): boolean {\n\t\t\t\tif (!text || text.includes(\"[BLANK_AUDIO]\")) return false;\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (transcriptionText && isValidTranscription(transcriptionText)) {\n\t\t\t\tstate.transcriptionText += transcriptionText;\n\t\t\t}\n\n\t\t\tif (state.transcriptionText.length) {\n\t\t\t\tthis.cleanupAudioPlayer(this.activeAudioPlayer);\n\t\t\t\tconst finalText = state.transcriptionText;\n\t\t\t\tstate.transcriptionText = \"\";\n\t\t\t\tawait this.handleMessage(\n\t\t\t\t\tfinalText,\n\t\t\t\t\tentityId,\n\t\t\t\t\tchannelId,\n\t\t\t\t\tchannel,\n\t\t\t\t\tname,\n\t\t\t\t\tuserName,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error transcribing audio for user ${entityId}:`, error);\n\t\t}\n\t}\n\n\t/**\n\t * Handles a voice message received in a Discord channel.\n\t *\n\t * @param {string} message - The message content.\n\t * @param {UUID} entityId - The entity ID associated with the message.\n\t * @param {string} channelId - The ID of the Discord channel where the message was received.\n\t * @param {BaseGuildVoiceChannel} channel - The Discord channel where the message was received.\n\t * @param {string} name - The name associated with the message.\n\t * @param {string} userName - The user name associated with the message.\n\t * @returns {Promise<{text: string, actions: string[]}>} Object containing the resulting text and actions.\n\t */\n\tprivate async handleMessage(\n\t\tmessage: string,\n\t\tentityId: UUID,\n\t\tchannelId: string,\n\t\tchannel: BaseGuildVoiceChannel,\n\t\tname: string,\n\t\tuserName: string,\n\t) {\n\t\ttry {\n\t\t\tif (!message || message.trim() === \"\" || message.length < 3) {\n\t\t\t\treturn { text: \"\", actions: [\"IGNORE\"] };\n\t\t\t}\n\n\t\t\tconst roomId = createUniqueUuid(this.runtime, channelId);\n\t\t\tconst type = await this.getChannelType(channel as Channel);\n\n\t\t\tawait this.runtime.ensureConnection({\n\t\t\t\tentityId,\n\t\t\t\troomId,\n\t\t\t\tuserName,\n\t\t\t\tname: name,\n\t\t\t\tsource: \"discord\",\n\t\t\t\tchannelId,\n\t\t\t\tserverId: channel.guild.id,\n\t\t\t\ttype,\n\t\t\t});\n\n\t\t\tconst memory: Memory = {\n\t\t\t\tid: createUniqueUuid(\n\t\t\t\t\tthis.runtime,\n\t\t\t\t\t`${channelId}-voice-message-${Date.now()}`,\n\t\t\t\t),\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\tentityId,\n\t\t\t\troomId,\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: message,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\turl: channel.url,\n\t\t\t\t\tname: name,\n\t\t\t\t\tuserName: userName,\n\t\t\t\t\tisVoiceMessage: true,\n\t\t\t\t\tchannelType: type,\n\t\t\t\t},\n\t\t\t\tcreatedAt: Date.now(),\n\t\t\t};\n\n\t\t\tconst callback: HandlerCallback = async (\n\t\t\t\tcontent: Content,\n\t\t\t\t_files: any[] = [],\n\t\t\t) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst responseMemory: Memory = {\n\t\t\t\t\t\tid: createUniqueUuid(\n\t\t\t\t\t\t\tthis.runtime,\n\t\t\t\t\t\t\t`${memory.id}-voice-response-${Date.now()}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tentityId: this.runtime.agentId,\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t...content,\n\t\t\t\t\t\t\tname: this.runtime.character.name,\n\t\t\t\t\t\t\tinReplyTo: memory.id,\n\t\t\t\t\t\t\tisVoiceMessage: true,\n\t\t\t\t\t\t\tchannelType: type,\n\t\t\t\t\t\t},\n\t\t\t\t\t\troomId,\n\t\t\t\t\t\tcreatedAt: Date.now(),\n\t\t\t\t\t};\n\n\t\t\t\t\tif (responseMemory.content.text?.trim()) {\n\t\t\t\t\t\tawait this.runtime\n\t\t\t\t\t\t\t.getMemoryManager(\"messages\")\n\t\t\t\t\t\t\t.createMemory(responseMemory);\n\n\t\t\t\t\t\tconst responseStream = await this.runtime.useModel(\n\t\t\t\t\t\t\tModelTypes.TEXT_TO_SPEECH,\n\t\t\t\t\t\t\tcontent.text,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (responseStream) {\n\t\t\t\t\t\t\tawait this.playAudioStream(entityId, responseStream as Readable);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn [responseMemory];\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(\"Error in voice message callback:\", error);\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// Emit voice-specific events\n\t\t\tthis.runtime.emitEvent(\n\t\t\t\t[\"DISCORD_VOICE_MESSAGE_RECEIVED\", \"VOICE_MESSAGE_RECEIVED\"],\n\t\t\t\t{\n\t\t\t\t\truntime: this.runtime,\n\t\t\t\t\tmessage: memory,\n\t\t\t\t\tcallback,\n\t\t\t\t},\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error processing voice message:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously converts an Opus audio Buffer to a WAV audio Buffer.\n\t *\n\t * @param {Buffer} pcmBuffer - The Opus audio Buffer to convert to WAV.\n\t * @returns {Promise<Buffer>} A Promise that resolves with the converted WAV audio Buffer.\n\t */\n\tprivate async convertOpusToWav(pcmBuffer: Buffer): Promise<Buffer> {\n\t\ttry {\n\t\t\t// Generate the WAV header\n\t\t\tconst wavHeader = getWavHeader(pcmBuffer.length, DECODE_SAMPLE_RATE);\n\n\t\t\t// Concatenate the WAV header and PCM data\n\t\t\tconst wavBuffer = Buffer.concat([wavHeader, pcmBuffer]);\n\n\t\t\treturn wavBuffer;\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error converting PCM to WAV:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Scans the given Discord guild to select a suitable voice channel to join.\n\t *\n\t * @param {Guild} guild The Discord guild to scan for voice channels.\n\t */\n\tasync scanGuild(guild: Guild) {\n\t\tlet chosenChannel: BaseGuildVoiceChannel | null = null;\n\n\t\ttry {\n\t\t\tconst channelId = this.runtime.getSetting(\n\t\t\t\t\"DISCORD_VOICE_CHANNEL_ID\",\n\t\t\t) as string;\n\t\t\tif (channelId) {\n\t\t\t\tconst channel = await guild.channels.fetch(channelId);\n\t\t\t\tif (channel?.isVoiceBased()) {\n\t\t\t\t\tchosenChannel = channel as BaseGuildVoiceChannel;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!chosenChannel) {\n\t\t\t\tconst channels = (await guild.channels.fetch()).filter(\n\t\t\t\t\t(channel) => channel?.type === DiscordChannelType.GuildVoice,\n\t\t\t\t);\n\t\t\t\tfor (const [, channel] of channels) {\n\t\t\t\t\tconst voiceChannel = channel as BaseGuildVoiceChannel;\n\t\t\t\t\tif (\n\t\t\t\t\t\tvoiceChannel.members.size > 0 &&\n\t\t\t\t\t\t(chosenChannel === null ||\n\t\t\t\t\t\t\tvoiceChannel.members.size > chosenChannel.members.size)\n\t\t\t\t\t) {\n\t\t\t\t\t\tchosenChannel = voiceChannel;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (chosenChannel) {\n\t\t\t\tconsole.log(`Joining channel: ${chosenChannel.name}`);\n\t\t\t\tawait this.joinChannel(chosenChannel);\n\t\t\t} else {\n\t\t\t\tconsole.warn(\"No suitable voice channel found to join.\");\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error selecting or joining a voice channel:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Play an audio stream for a given entity ID.\n\t *\n\t * @param {UUID} entityId - The ID of the entity to play the audio for.\n\t * @param {Readable} audioStream - The audio stream to play.\n\t * @returns {void}\n\t */\n\tasync playAudioStream(entityId: UUID, audioStream: Readable) {\n\t\tconst connection = this.connections.get(entityId);\n\t\tif (connection == null) {\n\t\t\tconsole.log(`No connection for user ${entityId}`);\n\t\t\treturn;\n\t\t}\n\t\tthis.cleanupAudioPlayer(this.activeAudioPlayer);\n\t\tconst audioPlayer = createAudioPlayer({\n\t\t\tbehaviors: {\n\t\t\t\tnoSubscriber: NoSubscriberBehavior.Pause,\n\t\t\t},\n\t\t});\n\t\tthis.activeAudioPlayer = audioPlayer;\n\t\tconnection.subscribe(audioPlayer);\n\n\t\tconst audioStartTime = Date.now();\n\n\t\tconst resource = createAudioResource(audioStream, {\n\t\t\tinputType: StreamType.Arbitrary,\n\t\t});\n\t\taudioPlayer.play(resource);\n\n\t\taudioPlayer.on(\"error\", (err: any) => {\n\t\t\tconsole.log(`Audio player error: ${err}`);\n\t\t});\n\n\t\taudioPlayer.on(\n\t\t\t\"stateChange\",\n\t\t\t(_oldState: any, newState: { status: string }) => {\n\t\t\t\tif (newState.status === \"idle\") {\n\t\t\t\t\tconst idleTime = Date.now();\n\t\t\t\t\tconsole.log(`Audio playback took: ${idleTime - audioStartTime}ms`);\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\t}\n\n\t/**\n\t * Cleans up the provided audio player by stopping it, removing all listeners,\n\t * and resetting the active audio player if it matches the provided player.\n\t *\n\t * @param {AudioPlayer} audioPlayer - The audio player to be cleaned up.\n\t */\n\tcleanupAudioPlayer(audioPlayer: AudioPlayer) {\n\t\tif (!audioPlayer) return;\n\n\t\taudioPlayer.stop();\n\t\taudioPlayer.removeAllListeners();\n\t\tif (audioPlayer === this.activeAudioPlayer) {\n\t\t\tthis.activeAudioPlayer = null;\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously handles the join channel command in an interaction.\n\t *\n\t * @param {any} interaction - The interaction object representing the user's input.\n\t * @returns {Promise<void>} - A promise that resolves once the join channel command is handled.\n\t */\n\tasync handleJoinChannelCommand(interaction: any) {\n\t\ttry {\n\t\t\t// Defer the reply immediately to prevent interaction timeout\n\t\t\tawait interaction.deferReply();\n\n\t\t\tconst channelId = interaction.options.get(\"channel\")?.value as string;\n\t\t\tif (!channelId) {\n\t\t\t\tawait interaction.editReply(\"Please provide a voice channel to join.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst guild = interaction.guild;\n\t\t\tif (!guild) {\n\t\t\t\tawait interaction.editReply(\"Could not find guild.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst voiceChannel = interaction.guild.channels.cache.find(\n\t\t\t\t(channel: VoiceChannel) =>\n\t\t\t\t\tchannel.id === channelId &&\n\t\t\t\t\tchannel.type === DiscordChannelType.GuildVoice,\n\t\t\t);\n\n\t\t\tif (!voiceChannel) {\n\t\t\t\tawait interaction.editReply(\"Voice channel not found!\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait this.joinChannel(voiceChannel as BaseGuildVoiceChannel);\n\t\t\tawait interaction.editReply(`Joined voice channel: ${voiceChannel.name}`);\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error joining voice channel:\", error);\n\t\t\t// Use editReply instead of reply for the error case\n\t\t\tawait interaction\n\t\t\t\t.editReply(\"Failed to join the voice channel.\")\n\t\t\t\t.catch(console.error);\n\t\t}\n\t}\n\n\t/**\n\t * Handles the leave channel command by destroying the voice connection if it exists.\n\t *\n\t * @param {any} interaction The interaction object representing the command invocation.\n\t * @returns {void}\n\t */\n\tasync handleLeaveChannelCommand(interaction: any) {\n\t\tconst connection = this.getVoiceConnection(interaction.guildId as any);\n\n\t\tif (!connection) {\n\t\t\tawait interaction.reply(\"Not currently in a voice channel.\");\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconnection.destroy();\n\t\t\tawait interaction.reply(\"Left the voice channel.\");\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error leaving voice channel:\", error);\n\t\t\tawait interaction.reply(\"Failed to leave the voice channel.\");\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["ChannelType","EventTypes","createUniqueUuid","logger","DiscordChannelType","Events","PermissionsBitField","ModelTypes","composePromptFromState","parseJSONObjectFromText","fs","ModelTypes","composePromptFromState","parseJSONObjectFromText","trimTokens","summarizationTemplate","summarizeAction","ModelTypes","composePromptFromState","parseJSONObjectFromText","composePromptFromState","ModelTypes","parseJSONObjectFromText","attachment","ChannelType","ModelTypes","composePromptFromState","createUniqueUuid","ServiceTypes","ChannelType","ServiceTypes","member","createUniqueUuid","composePromptFromState","ModelTypes","targetChannel","ChannelType","createUniqueUuid","logger","ServiceTypes","logger","ChannelType","createUniqueUuid","ChannelType","ServiceTypes","createUniqueUuid","logger","DiscordChannelType","fs","trimTokens","parseJSONObjectFromText","ModelTypes","ServiceTypes","ModelTypes","logger","parseJSONObjectFromText","trimTokens","ChannelType","logger","ChannelType","DiscordChannelType","createUniqueUuid","ChannelType","logger","entityId","match","ServiceTypes","ChannelType","ChannelType","ServiceTypes","ChannelType","ModelTypes","logger","ChannelType","ServiceTypes","logger","ChannelType","ModelTypes","NoSubscriberBehavior","VoiceConnectionStatus","createAudioPlayer","createAudioResource","entersState","ChannelType","ModelTypes","createUniqueUuid","logger","DiscordChannelType","logger","DiscordChannelType","ChannelType","entersState","VoiceConnectionStatus","connection","createUniqueUuid","ModelTypes","createAudioPlayer","NoSubscriberBehavior","createAudioResource","logger","Events","runtime","createUniqueUuid","ChannelType","EventTypes","PermissionsBitField","DiscordChannelType","fullGuild"]}
|