@elizaos/plugin-discord 2.0.0-alpha.4 → 2.0.0-alpha.537
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/LICENSE +21 -0
- package/README.md +549 -0
- package/dist/accounts.d.ts +164 -0
- package/dist/accounts.d.ts.map +1 -0
- package/dist/actions/actionResultSemantics.d.ts +9 -0
- package/dist/actions/actionResultSemantics.d.ts.map +1 -0
- package/dist/actions/chatWithAttachments.d.ts +4 -0
- package/dist/actions/chatWithAttachments.d.ts.map +1 -0
- package/dist/actions/createPoll.d.ts +4 -0
- package/dist/actions/createPoll.d.ts.map +1 -0
- package/dist/actions/deleteMessage.d.ts +4 -0
- package/dist/actions/deleteMessage.d.ts.map +1 -0
- package/dist/actions/downloadMedia.d.ts +4 -0
- package/dist/actions/downloadMedia.d.ts.map +1 -0
- package/dist/actions/editMessage.d.ts +4 -0
- package/dist/actions/editMessage.d.ts.map +1 -0
- package/dist/actions/getUserInfo.d.ts +4 -0
- package/dist/actions/getUserInfo.d.ts.map +1 -0
- package/dist/actions/joinChannel.d.ts +4 -0
- package/dist/actions/joinChannel.d.ts.map +1 -0
- package/dist/actions/leaveChannel.d.ts +4 -0
- package/dist/actions/leaveChannel.d.ts.map +1 -0
- package/dist/actions/listChannels.d.ts +4 -0
- package/dist/actions/listChannels.d.ts.map +1 -0
- package/dist/actions/pinMessage.d.ts +4 -0
- package/dist/actions/pinMessage.d.ts.map +1 -0
- package/dist/actions/reactToMessage.d.ts +4 -0
- package/dist/actions/reactToMessage.d.ts.map +1 -0
- package/dist/actions/readChannel.d.ts +4 -0
- package/dist/actions/readChannel.d.ts.map +1 -0
- package/dist/actions/searchMessages.d.ts +4 -0
- package/dist/actions/searchMessages.d.ts.map +1 -0
- package/dist/actions/sendDM.d.ts +4 -0
- package/dist/actions/sendDM.d.ts.map +1 -0
- package/dist/actions/sendMessage.d.ts +4 -0
- package/dist/actions/sendMessage.d.ts.map +1 -0
- package/dist/actions/serverInfo.d.ts +4 -0
- package/dist/actions/serverInfo.d.ts.map +1 -0
- package/dist/actions/setup-credentials.d.ts +25 -0
- package/dist/actions/setup-credentials.d.ts.map +1 -0
- package/dist/actions/summarizeConversation.d.ts +4 -0
- package/dist/actions/summarizeConversation.d.ts.map +1 -0
- package/dist/actions/transcribeMedia.d.ts +4 -0
- package/dist/actions/transcribeMedia.d.ts.map +1 -0
- package/dist/actions/unpinMessage.d.ts +4 -0
- package/dist/actions/unpinMessage.d.ts.map +1 -0
- package/dist/allowlist.d.ts +198 -0
- package/dist/allowlist.d.ts.map +1 -0
- package/dist/attachments.d.ts +93 -0
- package/dist/attachments.d.ts.map +1 -0
- package/dist/banner.d.ts +39 -0
- package/dist/banner.d.ts.map +1 -0
- package/dist/compat.d.ts +57 -0
- package/dist/compat.d.ts.map +1 -0
- package/dist/config.d.ts +156 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/debouncer.d.ts +25 -0
- package/dist/debouncer.d.ts.map +1 -0
- package/dist/discord-commands.d.ts +16 -0
- package/dist/discord-commands.d.ts.map +1 -0
- package/dist/discord-events.d.ts +54 -0
- package/dist/discord-events.d.ts.map +1 -0
- package/dist/discord-history.d.ts +49 -0
- package/dist/discord-history.d.ts.map +1 -0
- package/dist/discord-interactions.d.ts +45 -0
- package/dist/discord-interactions.d.ts.map +1 -0
- package/dist/discord-local-service.d.ts +94 -0
- package/dist/discord-local-service.d.ts.map +1 -0
- package/dist/discord-reactions.d.ts +29 -0
- package/dist/discord-reactions.d.ts.map +1 -0
- package/dist/draft-chunking.d.ts +9 -0
- package/dist/draft-chunking.d.ts.map +1 -0
- package/dist/draft-stream.d.ts +23 -0
- package/dist/draft-stream.d.ts.map +1 -0
- package/dist/environment.d.ts +23 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/generated/prompts/typescript/prompts.d.ts +44 -0
- package/dist/generated/prompts/typescript/prompts.d.ts.map +1 -0
- package/dist/generated/specs/spec-helpers.d.ts +49 -0
- package/dist/generated/specs/spec-helpers.d.ts.map +1 -0
- package/dist/generated/specs/specs.d.ts +90 -0
- package/dist/generated/specs/specs.d.ts.map +1 -0
- package/dist/identity.d.ts +8 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/inbound-envelope.d.ts +8 -0
- package/dist/inbound-envelope.d.ts.map +1 -0
- package/dist/index.browser.d.ts +34 -0
- package/dist/index.browser.d.ts.map +1 -0
- package/dist/index.d.ts +19 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15379 -0
- package/dist/index.js.map +72 -0
- package/dist/messages.d.ts +62 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messaging.d.ts +129 -0
- package/dist/messaging.d.ts.map +1 -0
- package/dist/native-commands.d.ts +142 -0
- package/dist/native-commands.d.ts.map +1 -0
- package/dist/owner-pairing-service.d.ts +73 -0
- package/dist/owner-pairing-service.d.ts.map +1 -0
- package/dist/permissionEvents.d.ts +42 -0
- package/dist/permissionEvents.d.ts.map +1 -0
- package/dist/permissions.d.ts +36 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/profileSync.d.ts +8 -0
- package/dist/profileSync.d.ts.map +1 -0
- package/dist/providers/channelState.d.ts +14 -0
- package/dist/providers/channelState.d.ts.map +1 -0
- package/dist/providers/guildInfo.d.ts +15 -0
- package/dist/providers/guildInfo.d.ts.map +1 -0
- package/dist/providers/voiceState.d.ts +12 -0
- package/dist/providers/voiceState.d.ts.map +1 -0
- package/dist/reasoning-tags.d.ts +2 -0
- package/dist/reasoning-tags.d.ts.map +1 -0
- package/dist/service.d.ts +207 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/setup-routes.d.ts +23 -0
- package/dist/setup-routes.d.ts.map +1 -0
- package/dist/slash-commands.d.ts +39 -0
- package/dist/slash-commands.d.ts.map +1 -0
- package/dist/status-reactions.d.ts +11 -0
- package/dist/status-reactions.d.ts.map +1 -0
- package/dist/test-setup.d.ts +1 -0
- package/dist/test-setup.d.ts.map +1 -0
- package/dist/tests.d.ts +123 -0
- package/dist/tests.d.ts.map +1 -0
- package/dist/types.d.ts +329 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/typing.d.ts +7 -0
- package/dist/typing.d.ts.map +1 -0
- package/dist/utils.d.ts +48 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/vitest.config.d.ts +3 -0
- package/dist/vitest.config.d.ts.map +1 -0
- package/dist/voice.d.ts +234 -0
- package/dist/voice.d.ts.map +1 -0
- package/package.json +25 -20
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../index.ts", "../actions/chatWithAttachments.ts", "../generated/prompts/typescript/prompts.ts", "../generated/specs/specs.ts", "../generated/specs/spec-helpers.ts", "../actions/createPoll.ts", "../constants.ts", "../actions/actionResultSemantics.ts", "../actions/deleteMessage.ts", "../actions/downloadMedia.ts", "../actions/editMessage.ts", "../actions/getUserInfo.ts", "../actions/joinChannel.ts", "../actions/leaveChannel.ts", "../actions/listChannels.ts", "../actions/pinMessage.ts", "../actions/reactToMessage.ts", "../actions/readChannel.ts", "../actions/searchMessages.ts", "../actions/sendDM.ts", "../actions/sendMessage.ts", "../actions/serverInfo.ts", "../actions/setup-credentials.ts", "../actions/summarizeConversation.ts", "../actions/transcribeMedia.ts", "../actions/unpinMessage.ts", "../permissions.ts", "../banner.ts", "../owner-pairing-service.ts", "../slash-commands.ts", "../providers/channelState.ts", "../types.ts", "../providers/guildInfo.ts", "../providers/voiceState.ts", "../service.ts", "../compat.ts", "../discord-commands.ts", "../discord-interactions.ts", "../identity.ts", "../profileSync.ts", "../discord-events.ts", "../debouncer.ts", "../permissionEvents.ts", "../discord-history.ts", "../discord-reactions.ts", "../utils.ts", "../environment.ts", "../messages.ts", "../attachments.ts", "../draft-chunking.ts", "../draft-stream.ts", "../inbound-envelope.ts", "../reasoning-tags.ts", "../status-reactions.ts", "../typing.ts", "../voice.ts", "../discord-local-service.ts", "../setup-routes.ts", "../tests.ts", "../accounts.ts", "../allowlist.ts", "../messaging.ts", "../native-commands.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { type IAgentRuntime, logger, type Plugin } from \"@elizaos/core\";\nimport chatWithAttachments from \"./actions/chatWithAttachments\";\nimport createPoll from \"./actions/createPoll\";\nimport deleteMessage from \"./actions/deleteMessage\";\nimport { downloadMedia } from \"./actions/downloadMedia\";\nimport editMessage from \"./actions/editMessage\";\nimport getUserInfo from \"./actions/getUserInfo\";\nimport joinChannel from \"./actions/joinChannel\";\nimport leaveChannel from \"./actions/leaveChannel\";\nimport listChannels from \"./actions/listChannels\";\nimport pinMessage from \"./actions/pinMessage\";\nimport reactToMessage from \"./actions/reactToMessage\";\nimport readChannel from \"./actions/readChannel\";\nimport searchMessages from \"./actions/searchMessages\";\nimport sendDM from \"./actions/sendDM\";\nimport sendMessage from \"./actions/sendMessage\";\nimport serverInfo from \"./actions/serverInfo\";\nimport setupCredentials from \"./actions/setup-credentials\";\nimport { summarize } from \"./actions/summarizeConversation\";\nimport { transcribeMedia } from \"./actions/transcribeMedia\";\nimport unpinMessage from \"./actions/unpinMessage\";\nimport { printBanner } from \"./banner\";\nimport { DiscordOwnerPairingServiceImpl } from \"./owner-pairing-service\";\nimport { getPermissionValues } from \"./permissions\";\nimport { channelStateProvider } from \"./providers/channelState\";\nimport { guildInfoProvider } from \"./providers/guildInfo\";\nimport { voiceStateProvider } from \"./providers/voiceState\";\nimport { DiscordService } from \"./service\";\nimport { discordSetupRoutes } from \"./setup-routes\";\nimport { DiscordTestSuite } from \"./tests\";\n\nconst discordPlugin: Plugin = {\n\tname: \"discord\",\n\tdescription:\n\t\t\"Discord service plugin for integration with Discord servers and channels\",\n\tservices: [DiscordService, DiscordOwnerPairingServiceImpl],\n\troutes: discordSetupRoutes,\n\tactions: [\n\t\tchatWithAttachments,\n\t\tdownloadMedia,\n\t\tjoinChannel,\n\t\tleaveChannel,\n\t\tlistChannels,\n\t\treadChannel,\n\t\tsendDM,\n\t\tsendMessage,\n\t\tsummarize,\n\t\ttranscribeMedia,\n\t\tsearchMessages,\n\t\tcreatePoll,\n\t\tgetUserInfo,\n\t\treactToMessage,\n\t\tpinMessage,\n\t\tunpinMessage,\n\t\tserverInfo,\n\t\teditMessage,\n\t\tdeleteMessage,\n\t\tsetupCredentials,\n\t],\n\tproviders: [channelStateProvider, voiceStateProvider, guildInfoProvider],\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\t\tconst botTokens = runtime.getSetting(\"DISCORD_BOT_TOKENS\") as string;\n\t\tconst applicationId = runtime.getSetting(\n\t\t\t\"DISCORD_APPLICATION_ID\",\n\t\t) as string;\n\t\tconst voiceChannelId = runtime.getSetting(\n\t\t\t\"DISCORD_VOICE_CHANNEL_ID\",\n\t\t) as string;\n\t\tconst channelIds = runtime.getSetting(\"CHANNEL_IDS\") as string;\n\t\tconst listenChannelIds = runtime.getSetting(\n\t\t\t\"DISCORD_LISTEN_CHANNEL_IDS\",\n\t\t) as string;\n\t\tconst ignoreBotMessages = runtime.getSetting(\n\t\t\t\"DISCORD_SHOULD_IGNORE_BOT_MESSAGES\",\n\t\t) as string;\n\t\tconst ignoreDirectMessages = runtime.getSetting(\n\t\t\t\"DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES\",\n\t\t) as string;\n\t\tconst respondOnlyToMentions = runtime.getSetting(\n\t\t\t\"DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS\",\n\t\t) as string;\n\n\t\tprintBanner({\n\t\t\tpluginName: \"plugin-discord\",\n\t\t\tdescription: \"Discord bot integration for servers and channels\",\n\t\t\tapplicationId: applicationId || undefined,\n\t\t\tdiscordPermissions: applicationId ? getPermissionValues() : undefined,\n\t\t\tsettings: [\n\t\t\t\t{\n\t\t\t\t\tname: \"DISCORD_API_TOKEN\",\n\t\t\t\t\tvalue: token,\n\t\t\t\t\tsensitive: true,\n\t\t\t\t\trequired: true,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"DISCORD_APPLICATION_ID\",\n\t\t\t\t\tvalue: applicationId,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"DISCORD_BOT_TOKENS\",\n\t\t\t\t\tvalue: botTokens,\n\t\t\t\t\tsensitive: true,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"DISCORD_VOICE_CHANNEL_ID\",\n\t\t\t\t\tvalue: voiceChannelId,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"CHANNEL_IDS\",\n\t\t\t\t\tvalue: channelIds,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"DISCORD_LISTEN_CHANNEL_IDS\",\n\t\t\t\t\tvalue: listenChannelIds,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"DISCORD_SHOULD_IGNORE_BOT_MESSAGES\",\n\t\t\t\t\tvalue: ignoreBotMessages,\n\t\t\t\t\tdefaultValue: \"false\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES\",\n\t\t\t\t\tvalue: ignoreDirectMessages,\n\t\t\t\t\tdefaultValue: \"false\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS\",\n\t\t\t\t\tvalue: respondOnlyToMentions,\n\t\t\t\t\tdefaultValue: \"false\",\n\t\t\t\t},\n\t\t\t],\n\t\t\truntime,\n\t\t});\n\n\t\tif (\n\t\t\t(!token || token.trim() === \"\") &&\n\t\t\t(!botTokens || botTokens.trim() === \"\")\n\t\t) {\n\t\t\tlogger.warn(\n\t\t\t\t\"Discord bot token not provided - Discord plugin is loaded but will not be functional\",\n\t\t\t);\n\t\t\tlogger.warn(\n\t\t\t\t\"To enable Discord functionality, provide DISCORD_API_TOKEN or DISCORD_BOT_TOKENS in your .env file\",\n\t\t\t);\n\t\t}\n\t},\n};\n\nexport default discordPlugin;\n\n// Account management exports (runtime utilities)\nexport {\n\tDEFAULT_ACCOUNT_ID,\n\ttype DiscordMultiAccountConfig,\n\ttype DiscordTokenResolution,\n\ttype DiscordTokenSource,\n\tisMultiAccountEnabled,\n\tlistDiscordAccountIds,\n\tlistEnabledDiscordAccounts,\n\tnormalizeAccountId,\n\tnormalizeDiscordToken,\n\ttype ResolvedDiscordAccount,\n\tresolveDefaultDiscordAccountId,\n\tresolveDiscordAccount,\n\tresolveDiscordToken,\n} from \"./accounts\";\n// Allowlist exports\nexport {\n\ttype AllowListMatchSource,\n\tallowListMatches,\n\ttype ChannelMatchSource,\n\ttype DiscordAllowList,\n\ttype DiscordAllowListMatch,\n\ttype DiscordChannelConfigResolved,\n\tformatDiscordUserTag,\n\tisDiscordAutoThreadOwnedByBot,\n\tisDiscordGroupAllowedByPolicy,\n\tnormalizeDiscordAllowList,\n\tnormalizeDiscordSlug,\n\tresolveDiscordAllowListMatch,\n\tresolveDiscordChannelConfig,\n\tresolveDiscordChannelConfigWithFallback,\n\tresolveDiscordCommandAuthorized,\n\tresolveDiscordGuildEntry,\n\tresolveDiscordShouldRequireMention,\n\tresolveDiscordUserAllowed,\n\tresolveGroupDmAllow,\n\tshouldEmitDiscordReactionNotification,\n\tvalidateMessageAllowed,\n} from \"./allowlist\";\n// Channel configuration types (comprehensive config schema)\n// Re-export config types that were in accounts.ts for backward compatibility\nexport type {\n\tDiscordAccountConfig,\n\tDiscordActionConfig,\n\tDiscordChannelConfig,\n\tDiscordConfig,\n\tDiscordDmConfig,\n\tDiscordExecApprovalConfig,\n\tDiscordGuildChannelConfig,\n\tDiscordGuildEntry,\n\tDiscordIntentsConfig,\n\tDiscordPluralKitConfig,\n\tDiscordReactionNotificationMode,\n} from \"./config\";\nexport { DISCORD_SERVICE_NAME } from \"./constants\";\n// Discord local IPC service + setup routes\nexport {\n\tDISCORD_LOCAL_PLUGIN_NAME,\n\tDISCORD_LOCAL_SERVICE_NAME,\n\tDiscordLocalService,\n\tdefault as discordLocalPlugin,\n} from \"./discord-local-service\";\n// Messaging utilities exports\nexport {\n\tbuildChannelLink,\n\tbuildMessageLink,\n\ttype ChunkDiscordTextOpts,\n\tchunkDiscordText,\n\tchunkDiscordTextWithMode,\n\tescapeDiscordMarkdown,\n\textractAllChannelMentions,\n\textractAllRoleMentions,\n\textractAllUserMentions,\n\textractChannelIdFromMention,\n\textractRoleIdFromMention,\n\textractUserIdFromMention,\n\tformatDiscordChannelMention,\n\tformatDiscordReactionEmoji,\n\tformatDiscordRoleMention,\n\tformatDiscordTimestamp,\n\tformatDiscordUserMention,\n\tformatMessageReactionEmoji,\n\tmessageContainsMention,\n\tparseMessageLink,\n\tresolveDiscordSystemLocation,\n\tresolveTimestampMs,\n\tsanitizeThreadName,\n\tstripDiscordFormatting,\n\ttruncateText,\n\ttruncateUtf16Safe,\n} from \"./messaging\";\n// Native commands utilities exports\nexport {\n\ttype BuiltCommandOption,\n\tbuildCommandArgCustomId,\n\tbuildCommandArgMenu,\n\tbuildCommandText,\n\tbuildDiscordCommandOptions,\n\tbuildDiscordSlashCommand,\n\tCOMMAND_ARG_CUSTOM_ID_KEY,\n\ttype CommandArgButton,\n\ttype CommandArgButtonRow,\n\ttype CommandArgDefinition,\n\ttype CommandArgMenu,\n\ttype CommandArgs,\n\ttype CommandArgValues,\n\tcreateCommandArgs,\n\tdecodeCommandArgValue,\n\tencodeCommandArgValue,\n\tisUnknownInteractionError,\n\ttype NativeCommandSpec,\n\tparseCommandArgCustomId,\n\tsafeInteractionCall,\n\tserializeCommandArgs,\n} from \"./native-commands\";\nexport {\n\tDISCORD_OWNER_PAIRING_SERVICE_TYPE,\n\ttype DiscordOwnerPairingService,\n\tDiscordOwnerPairingServiceImpl,\n} from \"./owner-pairing-service\";\nexport {\n\tELEVATED_PERMISSIONS,\n\thasElevatedPermissions,\n\tisElevatedRole,\n} from \"./permissionEvents\";\nexport {\n\ttype DiscordPermissionTier,\n\tDiscordPermissionTiers,\n\ttype DiscordPermissionValues,\n\tgenerateAllInviteUrls,\n\tgenerateInviteUrl,\n\tgetPermissionValues,\n} from \"./permissions\";\nexport type { DiscordService as IDiscordService } from \"./service\";\nexport { DiscordService } from \"./service\";\nexport { discordSetupRoutes } from \"./setup-routes\";\nexport type {\n\tAuditInfo,\n\tChannelPermissionsChangedPayload,\n\tMemberRolesChangedPayload,\n\tPermissionDiff,\n\tPermissionState,\n\tRoleLifecyclePayload,\n\tRolePermissionsChangedPayload,\n} from \"./types\";\nexport { DiscordEventTypes } from \"./types\";\n",
|
|
6
|
+
"import fs from \"node:fs\";\nimport {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\tChannelType,\n\ttype Content,\n\tContentType,\n\tcomposePromptFromState,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\ttype Media,\n\ttype Memory,\n\tMemoryType,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype State,\n\ttrimTokens,\n} from \"@elizaos/core\";\nimport {\n\tattachmentIdsTemplate,\n\tattachmentSummarizationTemplate as summarizationTemplate,\n} from \"../generated/prompts/typescript/prompts.js\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\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(ModelType.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\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 spec = requireActionSpec(\"CHAT_WITH_ATTACHMENTS\");\n\nexport const chatWithAttachments: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\tvalidate: async (\n\t\truntime: any,\n\t\tmessage: any,\n\t\tstate?: any,\n\t\toptions?: any,\n\t): Promise<boolean> => {\n\t\tconst __avTextRaw =\n\t\t\ttypeof message?.content?.text === \"string\" ? message.content.text : \"\";\n\t\tconst __avText = __avTextRaw.toLowerCase();\n\t\tconst __avKeywords = [\"chat\", \"with\", \"attachments\"];\n\t\tconst __avKeywordOk =\n\t\t\t__avKeywords.length > 0 &&\n\t\t\t__avKeywords.some((word) => word.length > 0 && __avText.includes(word));\n\t\tconst __avRegex = /\\b(?:chat|with|attachments)\\b/i;\n\t\tconst __avRegexOk = __avRegex.test(__avText);\n\t\tconst __avSource = String(message?.content?.source ?? \"\");\n\t\tconst __avExpectedSource = \"\";\n\t\tconst __avSourceOk = __avExpectedSource\n\t\t\t? __avSource === __avExpectedSource\n\t\t\t: Boolean(\n\t\t\t\t\t__avSource ||\n\t\t\t\t\t\tstate ||\n\t\t\t\t\t\truntime?.agentId ||\n\t\t\t\t\t\truntime?.getService ||\n\t\t\t\t\t\truntime?.getSetting,\n\t\t\t\t);\n\t\tconst __avOptions = options && typeof options === \"object\" ? options : {};\n\t\tconst __avInputOk =\n\t\t\t__avText.trim().length > 0 ||\n\t\t\tObject.keys(__avOptions as Record<string, unknown>).length > 0 ||\n\t\t\tBoolean(message?.content && typeof message.content === \"object\");\n\n\t\tif (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst __avLegacyValidate = async (\n\t\t\t_runtime: IAgentRuntime,\n\t\t\tmessage: Memory,\n\t\t\t_state?: State,\n\t\t): Promise<boolean> => {\n\t\t\tconst room = await _runtime.getRoom(message.roomId);\n\n\t\t\t// Only validate for Discord GROUP channels - this action is Discord-specific\n\t\t\tif (\n\t\t\t\t!room ||\n\t\t\t\troom.type !== ChannelType.GROUP ||\n\t\t\t\troom.source !== \"discord\"\n\t\t\t) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// only show if one of the keywords are in the message\n\t\t\tconst keywords: string[] = [\n\t\t\t\t\"attachment\",\n\t\t\t\t\"summary\",\n\t\t\t\t\"summarize\",\n\t\t\t\t\"research\",\n\t\t\t\t\"pdf\",\n\t\t\t\t\"video\",\n\t\t\t\t\"audio\",\n\t\t\t\t\"image\",\n\t\t\t\t\"document\",\n\t\t\t\t\"link\",\n\t\t\t\t\"file\",\n\t\t\t\t\"attachment\",\n\t\t\t\t\"summarize\",\n\t\t\t\t\"code\",\n\t\t\t\t\"report\",\n\t\t\t\t\"write\",\n\t\t\t\t\"details\",\n\t\t\t\t\"information\",\n\t\t\t\t\"talk\",\n\t\t\t\t\"chat\",\n\t\t\t\t\"read\",\n\t\t\t\t\"listen\",\n\t\t\t\t\"watch\",\n\t\t\t];\n\t\t\tconst messageContentText = message.content.text;\n\t\t\treturn keywords.some((keyword) =>\n\t\t\t\tmessageContentText?.toLowerCase().includes(keyword.toLowerCase()),\n\t\t\t);\n\t\t};\n\t\ttry {\n\t\t\treturn Boolean(\n\t\t\t\tawait (__avLegacyValidate as any)(runtime, message, state, options),\n\t\t\t);\n\t\t} catch {\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?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tif (!state) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"State is not available.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"State is not available\" };\n\t\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\truntime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:chat-with-attachments\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t},\n\t\t\t\t\"Could not get attachment IDs from message\",\n\t\t\t);\n\t\t\tawait runtime.createMemory(\n\t\t\t\t{\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: message.content.source,\n\t\t\t\t\t\tthought:\n\t\t\t\t\t\t\t\"I tried to chat with attachments but I couldn't get attachment IDs\",\n\t\t\t\t\t\tactions: [\"CHAT_WITH_ATTACHMENTS_FAILED\"],\n\t\t\t\t\t},\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\ttype: MemoryType.CUSTOM,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"messages\",\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"Could not get attachment IDs from message\",\n\t\t\t};\n\t\t}\n\n\t\tconst { objective, attachmentIds } = attachmentData;\n\n\t\tconst conversationLength = runtime.getConversationLength();\n\n\t\tconst recentMessages = await runtime.getMemories({\n\t\t\ttableName: \"messages\",\n\t\t\troomId: message.roomId,\n\t\t\tcount: conversationLength,\n\t\t\tunique: false,\n\t\t});\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 = 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// Ensure attachment is not undefined before accessing properties\n\t\t\t.filter(\n\t\t\t\t(attachment) =>\n\t\t\t\t\tattachment &&\n\t\t\t\t\t(attachmentIds\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\t// or check the other way\n\t\t\t\t\t\tattachmentIds.some((id) => {\n\t\t\t\t\t\t\tconst attachmentId = id.toLowerCase().slice(0, 5);\n\t\t\t\t\t\t\t// Add check here too\n\t\t\t\t\t\t\treturn attachment?.id?.toLowerCase().includes(attachmentId);\n\t\t\t\t\t\t})),\n\t\t\t);\n\n\t\tconst attachmentsWithText = attachments\n\t\t\t// Ensure attachment is not undefined before accessing properties\n\t\t\t.filter(\n\t\t\t\t(attachment): attachment is NonNullable<typeof attachment> =>\n\t\t\t\t\t!!attachment,\n\t\t\t)\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(ModelType.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\n\t\tcurrentSummary = `${currentSummary}\\n${summary}`;\n\n\t\tif (!currentSummary) {\n\t\t\truntime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:chat-with-attachments\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t},\n\t\t\t\t\"No summary found\",\n\t\t\t);\n\t\t\tawait runtime.createMemory(\n\t\t\t\t{\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: message.content.source,\n\t\t\t\t\t\tthought:\n\t\t\t\t\t\t\t\"I tried to chat with attachments but I couldn't get a summary\",\n\t\t\t\t\t\tactions: [\"CHAT_WITH_ATTACHMENTS_FAILED\"],\n\t\t\t\t\t},\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\ttype: MemoryType.CUSTOM,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"messages\",\n\t\t\t);\n\t\t\treturn { success: false, error: \"No summary found\" };\n\t\t}\n\n\t\tcallbackData.text = currentSummary.trim();\n\t\tconst trimmedSummary = currentSummary.trim();\n\t\tif (\n\t\t\tcallbackData.text &&\n\t\t\t((trimmedSummary && trimmedSummary.split(\"\\n\").length < 4) ||\n\t\t\t\t(trimmedSummary && trimmedSummary.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\tif (callback) {\n\t\t\t\tawait callback?.(callbackData);\n\t\t\t}\n\t\t\treturn { success: true, text: callbackData.text };\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\n\t\t\t\t// Write file directly first\n\t\t\t\tawait fs.promises.writeFile(summaryFilename, currentSummary, \"utf8\");\n\n\t\t\t\t// Then cache it\n\t\t\t\tawait runtime.setCache<string>(summaryFilename, currentSummary);\n\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\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\tattachments: [\n\t\t\t\t\t\t\t...(callbackData.attachments || []),\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tid: summaryFilename,\n\t\t\t\t\t\t\t\turl: summaryFilename,\n\t\t\t\t\t\t\t\ttitle: \"Summary\",\n\t\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t\t\tcontentType: ContentType.DOCUMENT,\n\t\t\t\t\t\t\t} as Media,\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn { success: true, text: `Summary saved to ${summaryFilename}` };\n\t\t\t} catch (error) {\n\t\t\t\truntime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:action:chat-with-attachments\",\n\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t},\n\t\t\t\t\t\"Error in file/cache process\",\n\t\t\t\t);\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t};\n\t\t\t}\n\t\t} else {\n\t\t\truntime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:chat-with-attachments\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t},\n\t\t\t\t\"Empty response from chat with attachments action\",\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"Empty response from chat with attachments action\",\n\t\t\t};\n\t\t}\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default chatWithAttachments;\n",
|
|
7
|
+
"/**\n * Auto-generated prompt templates\n * DO NOT EDIT - Generated from ../../../prompts/*.txt\n *\n * These prompts use Handlebars-style template syntax:\n * - {{variableName}} for simple substitution\n * - {{#each items}}...{{/each}} for iteration\n * - {{#if condition}}...{{/if}} for conditionals\n */\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\nexport const ATTACHMENT_IDS_TEMPLATE = attachmentIdsTemplate;\n\nexport const attachmentSummarizationTemplate = `# 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\nexport const ATTACHMENT_SUMMARIZATION_TEMPLATE = attachmentSummarizationTemplate;\n\nexport const channelInfoTemplate = `# Messages we are searching for channel information\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting to read messages from a specific Discord channel. Your goal is to determine:\n1. The channel they want to read from (could be the current channel or a mentioned channel)\n2. How many messages they want to read (default to 10 if not specified)\n3. Whether they want a summary or just the messages\n4. If they're looking for messages from a specific person\n\nIf they say \"this channel\" or \"here\", use the current channel.\nIf they mention a specific channel name or ID, extract that.\nIf they ask to \"summarize\" or mention what someone is \"talking about\", set summarize to true.\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"channelIdentifier\": \"<current|channel-name|channel-id>\",\n \"messageCount\": <number between 1 and 50>,\n \"summarize\": true/false,\n \"focusUser\": \"<username or null>\"\n}\n\\`\\`\\``;\n\nexport const CHANNEL_INFO_TEMPLATE = channelInfoTemplate;\n\nexport const createPollTemplate = `# Creating a Discord poll\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting to create a poll. Extract:\n1. The poll question\n2. The poll options (2-10 options)\n3. Whether to use emoji reactions (default: true)\n\nExamples:\n- \"create a poll: What game should we play? Options: Minecraft, Fortnite, Among Us\" \n -> question: \"What game should we play?\", options: [\"Minecraft\", \"Fortnite\", \"Among Us\"]\n- \"poll: Should we have a meeting tomorrow? Yes/No\"\n -> question: \"Should we have a meeting tomorrow?\", options: [\"Yes\", \"No\"]\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"question\": \"<poll question>\",\n \"options\": [\"<option1>\", \"<option2>\", ...],\n \"useEmojis\": true/false\n}\n\\`\\`\\``;\n\nexport const CREATE_POLL_TEMPLATE = createPollTemplate;\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\nexport const DATE_RANGE_TEMPLATE = dateRangeTemplate;\n\nexport const getUserInfoTemplate = `# Getting Discord user information\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting information about a Discord user. Extract:\n1. The user identifier (username, user ID, or mention)\n2. Whether they want detailed server-specific info\n\nExamples:\n- \"who is @john?\" -> userIdentifier: \"john\", detailed: false\n- \"tell me about user 123456789\" -> userIdentifier: \"123456789\", detailed: false \n- \"get detailed info on @admin\" -> userIdentifier: \"admin\", detailed: true\n- \"who am I?\" -> userIdentifier: \"self\", detailed: false\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"userIdentifier\": \"<username|user-id|mention|self>\",\n \"detailed\": true/false\n}\n\\`\\`\\``;\n\nexport const GET_USER_INFO_TEMPLATE = getUserInfoTemplate;\n\nexport const joinChannelTemplate = `# Messages we are searching for channel join information\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting the bot to join a specific Discord channel (text or voice). Your goal is to determine which channel they want to join.\n\nExtract the channel identifier from their request:\n- If they mention a channel like #general or <#channelid>, extract that\n- If they provide a channel name, extract that\n- If they provide a channel ID (long number), extract that\n- If they mention \"voice\", \"vc\", \"voice channel\", include that as a hint\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"channelIdentifier\": \"<channel-name|channel-id|#mention>\",\n \"isVoiceChannel\": true/false\n}\n\\`\\`\\``;\n\nexport const JOIN_CHANNEL_TEMPLATE = joinChannelTemplate;\n\nexport const leaveChannelTemplate = `# Messages we are searching for channel leave information\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting the bot to leave a specific Discord channel (text or voice). Your goal is to determine which channel they want to leave.\n\nExtract the channel identifier from their request:\n- If they mention a channel like #general or <#channelid>, extract that\n- If they provide a channel name (like \"dev-voice\" or \"general\"), extract just the name\n- If they provide a channel ID (long number), extract that\n- If they say \"this channel\" or \"here\", use \"current\"\n- If they don't specify a channel but mention \"voice\", \"vc\", use \"current\" and mark as voice\n\nExamples:\n- \"leave the dev-voice channel\" -> channelIdentifier: \"dev-voice\", isVoiceChannel: true\n- \"leave #general\" -> channelIdentifier: \"general\", isVoiceChannel: false\n- \"leave voice\" -> channelIdentifier: \"current\", isVoiceChannel: true\n- \"stop listening to this channel\" -> channelIdentifier: \"current\", isVoiceChannel: false\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"channelIdentifier\": \"<actual-channel-name-or-id-or-current>\",\n \"isVoiceChannel\": true/false\n}\n\\`\\`\\``;\n\nexport const LEAVE_CHANNEL_TEMPLATE = leaveChannelTemplate;\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\nexport const MEDIA_ATTACHMENT_ID_TEMPLATE = mediaAttachmentIdTemplate;\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\nexport const MEDIA_URL_TEMPLATE = mediaUrlTemplate;\n\nexport const pinMessageTemplate = `# Pinning a Discord message\n{{recentMessages}}\n\n# Instructions: {{senderName}} wants to pin a message. Extract which message they want to pin.\n\nExamples:\n- \"pin that message\" -> messageRef: \"last\"\n- \"pin the last message\" -> messageRef: \"last\"\n- \"pin john's message about the meeting\" -> messageRef: \"john meeting\"\n- \"pin message 123456789\" -> messageRef: \"123456789\"\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"messageRef\": \"<last|previous|message-id|search-text>\"\n}\n\\`\\`\\``;\n\nexport const PIN_MESSAGE_TEMPLATE = pinMessageTemplate;\n\nexport const reactToMessageTemplate = `# Adding reactions to Discord messages\n{{recentMessages}}\n\n# Instructions: {{senderName}} wants to add a reaction to a message. Extract:\n1. Which message to react to (last, specific message reference, or by content)\n2. What emoji/reaction to add\n\nExamples:\n- \"react with 👍 to the last message\" -> messageRef: \"last\", emoji: \"👍\"\n- \"add :fire: reaction\" -> messageRef: \"last\", emoji: \"🔥\" or \":fire:\"\n- \"react to that message with ❤️\" -> messageRef: \"previous\", emoji: \"❤️\"\n- \"add a thumbs up to john's message about the meeting\" -> messageRef: \"john meeting\", emoji: \"👍\"\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"messageRef\": \"<last|previous|message-id|search-text>\",\n \"emoji\": \"<emoji-character|:emoji-name:>\"\n}\n\\`\\`\\``;\n\nexport const REACT_TO_MESSAGE_TEMPLATE = reactToMessageTemplate;\n\nexport const searchMessagesTemplate = `# Searching for Discord messages\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting to search for messages in Discord. Extract:\n1. The search query/keywords\n2. The channel to search in (current if not specified)\n3. Optional filters like author, time range, or message count\n\nExamples:\n- \"search for messages containing 'meeting'\" -> query: \"meeting\", channelIdentifier: \"current\", NO author field\n- \"find messages from @user about bugs\" -> query: \"bugs\", channelIdentifier: \"current\", author: \"user\"\n- \"search #general for links from last week\" -> query: \"links\", channelIdentifier: \"general\", timeRange: \"week\"\n- \"search for messages about 'spartan' in this channel\" -> query: \"spartan\", channelIdentifier: \"current\"\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"query\": \"<search keywords>\",\n \"channelIdentifier\": \"<channel-name|channel-id|current>\",\n \"author\": \"<username>\",\n \"timeRange\": \"<hour|day|week|month>\",\n \"limit\": <number between 1-100, default 20>\n}\n\\`\\`\\``;\n\nexport const SEARCH_MESSAGES_TEMPLATE = searchMessagesTemplate;\n\nexport const sendDmTemplate = `# Messages we are searching for DM information\n{{recentMessages}}\n\n# Instructions: {{senderName}} is requesting to send a direct message to a specific Discord user. Your goal is to determine:\n1. The recipient they want to message (could be a username, user ID, or mentioned user)\n2. The message content they want to send\n\nExtract the recipient identifier and the message content from their request.\n- If they mention a user like @username or <@userid>, extract that\n- If they provide a username or display name, extract that\n- If they provide a user ID (long number), extract that\n- Extract the complete message they want to send\n\nYour response must be formatted as a JSON block with this structure:\n\\`\\`\\`json\n{\n \"recipientIdentifier\": \"<username|user-id|@mention>\",\n \"messageContent\": \"<the message to send>\"\n}\n\\`\\`\\``;\n\nexport const SEND_DM_TEMPLATE = sendDmTemplate;\n\nexport const summarizationTemplate = `# Summarized so far (we are adding to this)\n{{currentSummary}}\n\n# Current conversation chunk we are summarizing (includes attachments)\n{{memoriesWithAttachments}}\n\nSummarization objective: {{objective}}\n\n# Instructions: Summarize the conversation so far. Return the summary. Do not acknowledge this request, just summarize and continue the existing summary if there is one. Capture any important details to the objective. Only respond with the new summary text.\nYour response should be extremely detailed and include any and all relevant information.`;\n\nexport const SUMMARIZATION_TEMPLATE = summarizationTemplate;\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\nexport const TRANSCRIPTION_TEMPLATE = transcriptionTemplate;\n\nexport const unpinMessageTemplate = `# Unpinning a Discord message\n{{recentMessages}}\n\n# Instructions: {{senderName}} wants to unpin a message. Extract which message they want to unpin.\n\nExamples:\n- \"unpin that message\" -> messageRef: \"last_pinned\"\n- \"unpin the last pinned message\" -> messageRef: \"last_pinned\"\n- \"unpin john's message\" -> messageRef: \"john\"\n- \"unpin message about the meeting\" -> messageRef: \"meeting\"\n\nYour response must be formatted as a JSON block:\n\\`\\`\\`json\n{\n \"messageRef\": \"<last_pinned|message-id|search-text>\"\n}\n\\`\\`\\``;\n\nexport const UNPIN_MESSAGE_TEMPLATE = unpinMessageTemplate;\n\n",
|
|
8
|
+
"/**\n * Auto-generated canonical action/provider/evaluator docs for plugin-discord.\n * DO NOT EDIT - Generated from prompts/specs/**.\n */\n\nexport type ActionDoc = {\n\tname: string;\n\tdescription: string;\n\tdescriptionCompressed?: string;\n\tsimiles?: readonly string[];\n\tparameters?: readonly unknown[];\n\texamples?: readonly (readonly unknown[])[];\n};\n\nexport type ProviderDoc = {\n\tname: string;\n\tdescription: string;\n\tdescriptionCompressed?: string;\n\tposition?: number;\n\tdynamic?: boolean;\n};\n\nexport type EvaluatorDoc = {\n\tname: string;\n\tdescription: string;\n\tdescriptionCompressed?: string;\n\tsimiles?: readonly string[];\n\talwaysRun?: boolean;\n\texamples?: readonly unknown[];\n};\n\nexport const coreActionsSpec = {\n\tversion: \"1.0.0\",\n\tactions: [\n\t\t{\n\t\t\tname: \"name\",\n\t\t\tdescription: \"\",\n\t\t\tparameters: [],\n\t\t},\n\t],\n} as const;\nexport const allActionsSpec = {\n\tversion: \"1.0.0\",\n\tactions: [\n\t\t{\n\t\t\tname: \"name\",\n\t\t\tdescription: \"\",\n\t\t\tparameters: [],\n\t\t},\n\t],\n} as const;\nexport const coreProvidersSpec = {\n\tversion: \"1.0.0\",\n\tproviders: [\n\t\t{\n\t\t\tname: \"channelState\",\n\t\t\tdescription:\n\t\t\t\t\"Provides information about the current Discord channel state, including whether it's a DM or group channel, channel name, and server name.\",\n\t\t\tdynamic: true,\n\t\t},\n\t\t{\n\t\t\tname: \"guildInfo\",\n\t\t\tdescription:\n\t\t\t\t\"Provides information about the current Discord server/guild including member count, creation date, channels, roles, and bot permissions.\",\n\t\t\tdynamic: true,\n\t\t},\n\t\t{\n\t\t\tname: \"voiceState\",\n\t\t\tdescription:\n\t\t\t\t\"Provides information about the voice state of the agent, including whether it is currently in a voice channel.\",\n\t\t\tdynamic: true,\n\t\t},\n\t],\n} as const;\nexport const allProvidersSpec = {\n\tversion: \"1.0.0\",\n\tproviders: [\n\t\t{\n\t\t\tname: \"channelState\",\n\t\t\tdescription:\n\t\t\t\t\"Provides information about the current Discord channel state, including whether it's a DM or group channel, channel name, and server name.\",\n\t\t\tdynamic: true,\n\t\t},\n\t\t{\n\t\t\tname: \"guildInfo\",\n\t\t\tdescription:\n\t\t\t\t\"Provides information about the current Discord server/guild including member count, creation date, channels, roles, and bot permissions.\",\n\t\t\tdynamic: true,\n\t\t},\n\t\t{\n\t\t\tname: \"voiceState\",\n\t\t\tdescription:\n\t\t\t\t\"Provides information about the voice state of the agent, including whether it is currently in a voice channel.\",\n\t\t\tdynamic: true,\n\t\t},\n\t],\n} as const;\nexport const coreEvaluatorsSpec = {\n\tversion: \"1.0.0\",\n\tevaluators: [],\n} as const;\nexport const allEvaluatorsSpec = {\n\tversion: \"1.0.0\",\n\tevaluators: [],\n} as const;\n\nexport const coreActionDocs: readonly ActionDoc[] = coreActionsSpec.actions;\nexport const allActionDocs: readonly ActionDoc[] = allActionsSpec.actions;\nexport const coreProviderDocs: readonly ProviderDoc[] =\n\tcoreProvidersSpec.providers;\nexport const allProviderDocs: readonly ProviderDoc[] =\n\tallProvidersSpec.providers;\nexport const coreEvaluatorDocs: readonly EvaluatorDoc[] =\n\tcoreEvaluatorsSpec.evaluators;\nexport const allEvaluatorDocs: readonly EvaluatorDoc[] =\n\tallEvaluatorsSpec.evaluators;\n",
|
|
9
|
+
"/**\n * Helper functions to lookup action/provider/evaluator specs by name.\n * These allow language-specific implementations to import their text content\n * (description, similes, examples) from the centralized specs.\n *\n * DO NOT EDIT the spec data - update prompts/actions.json, prompts/providers.json, prompts/evaluators.json and regenerate.\n */\n\nimport {\n\ttype ActionDoc,\n\tallActionDocs,\n\tallEvaluatorDocs,\n\tallProviderDocs,\n\tcoreActionDocs,\n\tcoreEvaluatorDocs,\n\tcoreProviderDocs,\n\ttype EvaluatorDoc,\n\ttype ProviderDoc,\n} from \"./specs\";\n\n// Build lookup maps for O(1) access\nconst coreActionMap = new Map<string, ActionDoc>(\n\tcoreActionDocs.map((doc) => [doc.name, doc]),\n);\nconst allActionMap = new Map<string, ActionDoc>(\n\tallActionDocs.map((doc) => [doc.name, doc]),\n);\nconst coreProviderMap = new Map<string, ProviderDoc>(\n\tcoreProviderDocs.map((doc) => [doc.name, doc]),\n);\nconst allProviderMap = new Map<string, ProviderDoc>(\n\tallProviderDocs.map((doc) => [doc.name, doc]),\n);\nconst coreEvaluatorMap = new Map<string, EvaluatorDoc>(\n\tcoreEvaluatorDocs.map((doc) => [doc.name, doc]),\n);\nconst allEvaluatorMap = new Map<string, EvaluatorDoc>(\n\tallEvaluatorDocs.map((doc) => [doc.name, doc]),\n);\n\n/**\n * Get an action spec by name from the core specs.\n * @param name - The action name\n * @returns The action spec or undefined if not found\n */\nexport function getActionSpec(name: string): ActionDoc | undefined {\n\treturn coreActionMap.get(name) ?? allActionMap.get(name);\n}\n\n/**\n * Get an action spec by name, throwing if not found.\n * @param name - The action name\n * @returns The action spec\n * @throws Error if the action is not found\n */\nexport function requireActionSpec(name: string): ActionDoc {\n\tconst spec = getActionSpec(name);\n\tif (!spec) {\n\t\treturn {\n\t\t\tname,\n\t\t\tdescription: `${name} action`,\n\t\t\tsimiles: [],\n\t\t\texamples: [],\n\t\t};\n\t}\n\treturn spec;\n}\n\n/**\n * Get a provider spec by name from the core specs.\n * @param name - The provider name\n * @returns The provider spec or undefined if not found\n */\nexport function getProviderSpec(name: string): ProviderDoc | undefined {\n\treturn coreProviderMap.get(name) ?? allProviderMap.get(name);\n}\n\n/**\n * Get a provider spec by name, throwing if not found.\n * @param name - The provider name\n * @returns The provider spec\n * @throws Error if the provider is not found\n */\nexport function requireProviderSpec(name: string): ProviderDoc {\n\tconst spec = getProviderSpec(name);\n\tif (!spec) {\n\t\tthrow new Error(`Provider spec not found: ${name}`);\n\t}\n\treturn spec;\n}\n\n/**\n * Get an evaluator spec by name from the core specs.\n * @param name - The evaluator name\n * @returns The evaluator spec or undefined if not found\n */\nexport function getEvaluatorSpec(name: string): EvaluatorDoc | undefined {\n\treturn coreEvaluatorMap.get(name) ?? allEvaluatorMap.get(name);\n}\n\n/**\n * Get an evaluator spec by name, throwing if not found.\n * @param name - The evaluator name\n * @returns The evaluator spec\n * @throws Error if the evaluator is not found\n */\nexport function requireEvaluatorSpec(name: string): EvaluatorDoc {\n\tconst spec = getEvaluatorSpec(name);\n\tif (!spec) {\n\t\tthrow new Error(`Evaluator spec not found: ${name}`);\n\t}\n\treturn spec;\n}\n\n// Re-export types for convenience\nexport type { ActionDoc, EvaluatorDoc, ProviderDoc };\n",
|
|
10
|
+
"import {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\ttype Content,\n\tcomposePromptFromState,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype State,\n} from \"@elizaos/core\";\nimport type { TextChannel } from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\n// Import generated prompts\nimport { createPollTemplate } from \"../generated/prompts/typescript/prompts.js\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\nimport type { DiscordService } from \"../service\";\nimport {\n\tterminalActionInteractionSemantics,\n\tterminalActionResultData,\n} from \"./actionResultSemantics\";\n\nconst getPollInfo = async (\n\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n): Promise<{\n\tquestion: string;\n\toptions: string[];\n\tuseEmojis: boolean;\n} | null> => {\n\tconst prompt = composePromptFromState({\n\t\tstate,\n\t\ttemplate: createPollTemplate,\n\t});\n\n\tfor (let i = 0; i < 3; i++) {\n\t\tconst response = await runtime.useModel(ModelType.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\n\t\tconst parsedResponse = parseJSONObjectFromText(response);\n\t\tif (\n\t\t\tparsedResponse?.question &&\n\t\t\tArray.isArray(parsedResponse.options) &&\n\t\t\tparsedResponse.options.length >= 2\n\t\t) {\n\t\t\treturn {\n\t\t\t\tquestion: String(parsedResponse.question),\n\t\t\t\toptions: parsedResponse.options.slice(0, 10).map(String), // Max 10 options\n\t\t\t\tuseEmojis: parsedResponse.useEmojis !== false, // Default to true\n\t\t\t};\n\t\t}\n\t}\n\treturn null;\n};\n\n// Number emojis for poll options\nconst numberEmojis = [\"1️⃣\", \"2️⃣\", \"3️⃣\", \"4️⃣\", \"5️⃣\", \"6️⃣\", \"7️⃣\", \"8️⃣\", \"9️⃣\", \"🔟\"];\nconst letterEmojis = [\"🇦\", \"🇧\", \"🇨\", \"🇩\", \"🇪\", \"🇫\", \"🇬\", \"🇭\", \"🇮\", \"🇯\"];\nconst yesNoEmojis = [\"✅\", \"❌\"];\n\nconst spec = requireActionSpec(\"CREATE_POLL\");\n\nexport const createPoll: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\t...terminalActionInteractionSemantics,\n\tvalidate: async (\n\t\truntime: any,\n\t\tmessage: any,\n\t\tstate?: any,\n\t\toptions?: any,\n\t): Promise<boolean> => {\n\t\tconst __avTextRaw =\n\t\t\ttypeof message?.content?.text === \"string\" ? message.content.text : \"\";\n\t\tconst __avText = __avTextRaw.toLowerCase();\n\t\tconst __avKeywords = [\"create\", \"poll\"];\n\t\tconst __avKeywordOk =\n\t\t\t__avKeywords.length > 0 &&\n\t\t\t__avKeywords.some((word) => word.length > 0 && __avText.includes(word));\n\t\tconst __avRegex = /\\b(?:create|poll)\\b/i;\n\t\tconst __avRegexOk = __avRegex.test(__avText);\n\t\tconst __avSource = String(message?.content?.source ?? \"\");\n\t\tconst __avExpectedSource = \"\";\n\t\tconst __avSourceOk = __avExpectedSource\n\t\t\t? __avSource === __avExpectedSource\n\t\t\t: Boolean(\n\t\t\t\t\t__avSource ||\n\t\t\t\t\t\tstate ||\n\t\t\t\t\t\truntime?.agentId ||\n\t\t\t\t\t\truntime?.getService ||\n\t\t\t\t\t\truntime?.getSetting,\n\t\t\t\t);\n\t\tconst __avOptions = options && typeof options === \"object\" ? options : {};\n\t\tconst __avInputOk =\n\t\t\t__avText.trim().length > 0 ||\n\t\t\tObject.keys(__avOptions as Record<string, unknown>).length > 0 ||\n\t\t\tBoolean(message?.content && typeof message.content === \"object\");\n\n\t\tif (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst __avLegacyValidate = async (\n\t\t\t_runtime: IAgentRuntime,\n\t\t\tmessage: Memory,\n\t\t\t_state?: State,\n\t\t): Promise<boolean> => {\n\t\t\treturn message.content.source === \"discord\";\n\t\t};\n\t\ttry {\n\t\t\treturn Boolean(\n\t\t\t\tawait (__avLegacyValidate as any)(runtime, message, state, options),\n\t\t\t);\n\t\t} catch {\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?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tconst discordService = runtime.getService(\n\t\t\tDISCORD_SERVICE_NAME,\n\t\t) as DiscordService;\n\n\t\tif (!discordService?.client) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"Discord service is not available.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"Discord service is not available\" };\n\t\t}\n\n\t\tif (!state) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"State is not available.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"State is not available\" };\n\t\t}\n\n\t\tconst pollInfo = await getPollInfo(runtime, message, state);\n\t\tif (!pollInfo) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't understand the poll details. Please specify a question and at least 2 options.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"Could not parse poll details\" };\n\t\t}\n\n\t\ttry {\n\t\t\tconst stateData = state.data;\n\t\t\tconst room = stateData?.room || (await runtime.getRoom(message.roomId));\n\t\t\tif (!room?.channelId) {\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: \"I couldn't determine the current channel.\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn { success: false, error: \"Could not determine current channel\" };\n\t\t\t}\n\n\t\t\tconst channel = await discordService.client.channels.fetch(\n\t\t\t\troom.channelId,\n\t\t\t);\n\t\t\tif (!channel?.isTextBased()) {\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: \"I can only create polls in text channels.\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn { success: false, error: \"Channel is not a text channel\" };\n\t\t\t}\n\n\t\t\tconst textChannel = channel as TextChannel;\n\n\t\t\t// Determine which emojis to use\n\t\t\tlet emojis: string[];\n\t\t\tif (\n\t\t\t\tpollInfo.options.length === 2 &&\n\t\t\t\tpollInfo.options.some((opt) => opt.toLowerCase().includes(\"yes\")) &&\n\t\t\t\tpollInfo.options.some((opt) => opt.toLowerCase().includes(\"no\"))\n\t\t\t) {\n\t\t\t\temojis = yesNoEmojis;\n\t\t\t} else if (pollInfo.useEmojis) {\n\t\t\t\temojis = numberEmojis.slice(0, pollInfo.options.length);\n\t\t\t} else {\n\t\t\t\temojis = letterEmojis.slice(0, pollInfo.options.length);\n\t\t\t}\n\n\t\t\t// Format the poll message\n\t\t\tconst pollMessage = [\n\t\t\t\t`📊 **POLL: ${pollInfo.question}**`,\n\t\t\t\t\"\",\n\t\t\t\t...pollInfo.options.map(\n\t\t\t\t\t(option, index) => `${emojis[index]} ${option}`,\n\t\t\t\t),\n\t\t\t\t\"\",\n\t\t\t\t\"_React to vote!_\",\n\t\t\t].join(\"\\n\");\n\n\t\t\t// Send the poll message\n\t\t\tconst sentMessage = await textChannel.send(pollMessage);\n\n\t\t\t// Add reactions\n\t\t\tfor (let i = 0; i < pollInfo.options.length; i++) {\n\t\t\t\ttry {\n\t\t\t\t\tawait sentMessage.react(emojis[i]);\n\t\t\t\t\t// Small delay to avoid rate limits\n\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 250));\n\t\t\t\t} catch (error) {\n\t\t\t\t\truntime.logger.error(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord:action:create-poll\",\n\t\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t\t\temoji: emojis[i],\n\t\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Failed to add reaction\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst response: Content = {\n\t\t\t\ttext: `I've created a poll with ${pollInfo.options.length} options. Users can vote by clicking the reaction emojis!`,\n\t\t\t\tsource: message.content.source,\n\t\t\t};\n\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.(response);\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\ttext: response.text,\n\t\t\t\tdata: terminalActionResultData(),\n\t\t\t};\n\t\t} catch (error) {\n\t\t\truntime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:create-poll\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error creating poll\",\n\t\t\t);\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I encountered an error while creating the poll. Please make sure I have permission to send messages and add reactions.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default createPoll;\n",
|
|
11
|
+
"export const DISCORD_SERVICE_NAME = \"discord\";\n",
|
|
12
|
+
"import type { ActionResult } from \"@elizaos/core\";\n\ntype ActionResultData = NonNullable<ActionResult[\"data\"]>;\n\nexport const terminalActionInteractionSemantics = {\n\tsuppressPostActionContinuation: true,\n\tsuppressActionResultClipboard: true,\n} as const;\n\nexport function terminalActionResultData(\n\tdata: ActionResultData = {},\n): ActionResultData {\n\treturn {\n\t\t...data,\n\t\tsuppressVisibleCallback: true,\n\t\tsuppressActionResultClipboard: true,\n\t};\n}\n",
|
|
13
|
+
"import {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\tcomposePromptFromState,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype State,\n} from \"@elizaos/core\";\nimport type { Message, TextChannel } from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport type { DiscordService } from \"../service\";\nimport {\n\tterminalActionInteractionSemantics,\n\tterminalActionResultData,\n} from \"./actionResultSemantics\";\n\nconst deleteMessageTemplate = `You are helping to extract delete message parameters.\n\nThe user wants to delete a Discord message.\n\nRecent conversation:\n{{recentMessages}}\n\nExtract the following:\n1. messageId: The ID of the message to delete\n2. channelRef: The channel where the message is (default: \"current\")\n\nRespond with a JSON object like:\n{\n \"messageId\": \"123456789\",\n \"channelRef\": \"current\"\n}\n\nOnly respond with the JSON object, no other text.`;\n\ninterface DeleteMessageParams {\n\tmessageId: string;\n\tchannelRef?: string;\n}\n\nconst deleteMessage: Action = {\n\tname: \"DELETE_MESSAGE\",\n\tsimiles: [\"REMOVE_MESSAGE\", \"UNSEND_MESSAGE\", \"DELETE_DISCORD_MESSAGE\"],\n\tdescription: \"Delete a message from a Discord channel\",\n\tdescriptionCompressed: \"Delete Discord channel message.\",\n\t...terminalActionInteractionSemantics,\n\n\tvalidate: async (\n\t\t_runtime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\t_state?: State,\n\t): Promise<boolean> => {\n\t\treturn message.content.source === \"discord\";\n\t},\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\tstate?: State,\n\t\t_options?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tconst discordService = runtime.getService(\n\t\t\tDISCORD_SERVICE_NAME,\n\t\t) as DiscordService;\n\n\t\tif (!discordService?.client) {\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"Discord service is not available.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t\treturn { success: false, error: \"Discord service not available\" };\n\t\t}\n\n\t\t// Ensure state is available\n\t\tconst currentState = state ?? (await runtime.composeState(message));\n\n\t\t// Use LLM to extract delete parameters\n\t\tconst prompt = composePromptFromState({\n\t\t\tstate: currentState,\n\t\t\ttemplate: deleteMessageTemplate,\n\t\t});\n\n\t\tlet deleteParams: DeleteMessageParams | null = null;\n\n\t\tfor (let i = 0; i < 3; i++) {\n\t\t\tconst response = await runtime.useModel(ModelType.TEXT_SMALL, {\n\t\t\t\tprompt,\n\t\t\t});\n\n\t\t\tconst parsedResponse = parseJSONObjectFromText(response) as Record<\n\t\t\t\tstring,\n\t\t\t\tunknown\n\t\t\t> | null;\n\t\t\tif (parsedResponse && typeof parsedResponse.messageId === \"string\") {\n\t\t\t\tdeleteParams = {\n\t\t\t\t\tmessageId: parsedResponse.messageId,\n\t\t\t\t\tchannelRef:\n\t\t\t\t\t\ttypeof parsedResponse.channelRef === \"string\"\n\t\t\t\t\t\t\t? parsedResponse.channelRef\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!deleteParams) {\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"I couldn't determine which message to delete.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t\treturn { success: false, error: \"Failed to extract delete parameters\" };\n\t\t}\n\n\t\ttry {\n\t\t\t// Get the channel\n\t\t\tlet channel: TextChannel | null = null;\n\n\t\t\tif (deleteParams.channelRef === \"current\") {\n\t\t\t\tconst channelId = message.content.channelId as string;\n\t\t\t\tif (channelId) {\n\t\t\t\t\tchannel = discordService.client.channels.cache.get(\n\t\t\t\t\t\tchannelId,\n\t\t\t\t\t) as TextChannel;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Try to find channel by name or ID\n\t\t\t\tchannel = discordService.client.channels.cache.find(\n\t\t\t\t\t(c) =>\n\t\t\t\t\t\tc.id === deleteParams?.channelRef ||\n\t\t\t\t\t\t(c.isTextBased() &&\n\t\t\t\t\t\t\t\"name\" in c &&\n\t\t\t\t\t\t\tc.name === deleteParams?.channelRef),\n\t\t\t\t) as TextChannel;\n\t\t\t}\n\n\t\t\tif (!channel?.isTextBased()) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't find the channel with that message.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn { success: false, error: \"Channel not found\" };\n\t\t\t}\n\n\t\t\t// Fetch and delete the message\n\t\t\tconst targetMessage = (await channel.messages.fetch(\n\t\t\t\tdeleteParams.messageId,\n\t\t\t)) as Message;\n\n\t\t\tif (!targetMessage) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't find the message to delete.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn { success: false, error: \"Message not found\" };\n\t\t\t}\n\n\t\t\t// Check if we have permission to delete\n\t\t\t// We can delete our own messages or messages in channels where we have MANAGE_MESSAGES\n\t\t\tconst botUser = discordService.client.user;\n\t\t\tconst hasManageMessages = botUser\n\t\t\t\t? (channel.permissionsFor(botUser)?.has(\"ManageMessages\") ?? false)\n\t\t\t\t: false;\n\t\t\tconst canDelete =\n\t\t\t\ttargetMessage.author.id === botUser?.id || hasManageMessages;\n\n\t\t\tif (!canDelete) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I don't have permission to delete that message.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn { success: false, error: \"No permission to delete message\" };\n\t\t\t}\n\n\t\t\tawait targetMessage.delete();\n\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"I've deleted the message.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tdata: terminalActionResultData({\n\t\t\t\t\tmessageId: deleteParams.messageId,\n\t\t\t\t\tchannelId: channel.id,\n\t\t\t\t}),\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : String(error);\n\t\t\tawait callback?.({\n\t\t\t\ttext: `Failed to delete message: ${errorMessage}`,\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t\treturn { success: false, error: errorMessage };\n\t\t}\n\t},\n\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: \"Delete message 123456789\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{agentName}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"I'll delete that message now.\",\n\t\t\t\t\tactions: [\"DELETE_MESSAGE\"],\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: \"Remove that spam message\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{agentName}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"I'll remove that message.\",\n\t\t\t\t\tactions: [\"DELETE_MESSAGE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t] as ActionExample[][],\n};\n\nexport default deleteMessage;\n",
|
|
14
|
+
"import {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\ttype Content,\n\tContentType,\n\tcomposePromptFromState,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\ttype Media,\n\ttype Memory,\n\tMemoryType,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype Service,\n\tServiceType,\n\ttype State,\n} from \"@elizaos/core\";\nimport { mediaUrlTemplate } from \"../generated/prompts/typescript/prompts.js\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\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(ModelType.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\nconst spec = requireActionSpec(\"DOWNLOAD_MEDIA\");\n\nexport const downloadMedia: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\tvalidate: async (\n\t\truntime: any,\n\t\tmessage: any,\n\t\tstate?: any,\n\t\toptions?: any,\n\t): Promise<boolean> => {\n\t\tconst __avTextRaw =\n\t\t\ttypeof message?.content?.text === \"string\" ? message.content.text : \"\";\n\t\tconst __avText = __avTextRaw.toLowerCase();\n\t\tconst __avKeywords = [\"download\", \"media\"];\n\t\tconst __avKeywordOk =\n\t\t\t__avKeywords.length > 0 &&\n\t\t\t__avKeywords.some((word) => word.length > 0 && __avText.includes(word));\n\t\tconst __avRegex = /\\b(?:download|media)\\b/i;\n\t\tconst __avRegexOk = __avRegex.test(__avText);\n\t\tconst __avSource = String(message?.content?.source ?? \"\");\n\t\tconst __avExpectedSource = \"\";\n\t\tconst __avSourceOk = __avExpectedSource\n\t\t\t? __avSource === __avExpectedSource\n\t\t\t: Boolean(\n\t\t\t\t\t__avSource ||\n\t\t\t\t\t\tstate ||\n\t\t\t\t\t\truntime?.agentId ||\n\t\t\t\t\t\truntime?.getService ||\n\t\t\t\t\t\truntime?.getSetting,\n\t\t\t\t);\n\t\tconst __avOptions = options && typeof options === \"object\" ? options : {};\n\t\tconst __avInputOk =\n\t\t\t__avText.trim().length > 0 ||\n\t\t\tObject.keys(__avOptions as Record<string, unknown>).length > 0 ||\n\t\t\tBoolean(message?.content && typeof message.content === \"object\");\n\n\t\tif (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst __avLegacyValidate = async (\n\t\t\t_runtime: IAgentRuntime,\n\t\t\tmessage: Memory,\n\t\t\t_state?: State,\n\t\t): Promise<boolean> => {\n\t\t\treturn message.content.source === \"discord\";\n\t\t};\n\t\ttry {\n\t\t\treturn Boolean(\n\t\t\t\tawait (__avLegacyValidate as any)(runtime, message, state, options),\n\t\t\t);\n\t\t} catch {\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?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\t// Define the expected video service interface\n\t\tinterface VideoServiceInterface extends Service {\n\t\t\tfetchVideoInfo: (\n\t\t\t\turl: string,\n\t\t\t) => Promise<{ title: string; description: string }>;\n\t\t\tdownloadVideo: (videoInfo: {\n\t\t\t\ttitle: string;\n\t\t\t\tdescription: string;\n\t\t\t}) => Promise<string>;\n\t\t}\n\n\t\tconst videoService = runtime.getService<VideoServiceInterface>(\n\t\t\tServiceType.VIDEO,\n\t\t);\n\n\t\tif (!videoService) {\n\t\t\truntime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:download-media\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t},\n\t\t\t\t\"Video service not found\",\n\t\t\t);\n\t\t\treturn { success: false, error: \"Video service not available\" };\n\t\t}\n\n\t\tif (!state) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"State is not available.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"State is not available\" };\n\t\t}\n\n\t\tconst mediaUrl = await getMediaUrl(runtime, message, state);\n\t\tif (!mediaUrl) {\n\t\t\truntime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:download-media\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t},\n\t\t\t\t\"Could not get media URL from messages\",\n\t\t\t);\n\t\t\tawait runtime.createMemory(\n\t\t\t\t{\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 couldn't find the media URL in the message\",\n\t\t\t\t\t\tactions: [\"DOWNLOAD_MEDIA_FAILED\"],\n\t\t\t\t\t},\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\ttype: MemoryType.CUSTOM,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"messages\",\n\t\t\t);\n\t\t\treturn { success: false, error: \"Could not get media URL from messages\" };\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...response,\n\t\t\t\t\tattachments: [\n\t\t\t\t\t\t...(response.attachments || []),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: mediaPath,\n\t\t\t\t\t\t\turl: mediaPath,\n\t\t\t\t\t\t\ttitle: \"Downloaded Media\",\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t\tcontentType: ContentType.DOCUMENT,\n\t\t\t\t\t\t} as Media,\n\t\t\t\t\t],\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\truntime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:action:download-media\",\n\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t\tattempt: retries,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t},\n\t\t\t\t\t\"Error sending message\",\n\t\t\t\t);\n\n\t\t\t\tif (retries === maxRetries) {\n\t\t\t\t\truntime.logger.error(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord:action:download-media\",\n\t\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t\t\tmaxRetries,\n\t\t\t\t\t\t},\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 { success: true, ...response };\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default downloadMedia;\n",
|
|
15
|
+
"import {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\tcomposePromptFromState,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype State,\n} from \"@elizaos/core\";\nimport type { Message, TextChannel } from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport type { DiscordService } from \"../service\";\nimport {\n\tterminalActionInteractionSemantics,\n\tterminalActionResultData,\n} from \"./actionResultSemantics\";\n\nconst editMessageTemplate = `You are helping to extract edit message parameters.\n\nThe user wants to edit an existing Discord message.\n\nRecent conversation:\n{{recentMessages}}\n\nExtract the following:\n1. messageId: The ID of the message to edit\n2. newText: The new text content for the message\n3. channelRef: The channel where the message is (default: \"current\")\n\nRespond with a JSON object like:\n{\n \"messageId\": \"123456789\",\n \"newText\": \"The updated message text\",\n \"channelRef\": \"current\"\n}\n\nOnly respond with the JSON object, no other text.`;\n\ninterface EditMessageParams {\n\tmessageId: string;\n\tnewText: string;\n\tchannelRef?: string;\n}\n\nconst editMessage: Action = {\n\tname: \"EDIT_MESSAGE\",\n\tsimiles: [\n\t\t\"UPDATE_MESSAGE\",\n\t\t\"MODIFY_MESSAGE\",\n\t\t\"CHANGE_MESSAGE\",\n\t\t\"EDIT_DISCORD_MESSAGE\",\n\t],\n\tdescription: \"Edit an existing message in a Discord channel\",\n\tdescriptionCompressed: \"Edit existing Discord message.\",\n\t...terminalActionInteractionSemantics,\n\n\tvalidate: async (\n\t\t_runtime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\t_state?: State,\n\t): Promise<boolean> => {\n\t\treturn message.content.source === \"discord\";\n\t},\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\tstate?: State,\n\t\t_options?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tconst discordService = runtime.getService(\n\t\t\tDISCORD_SERVICE_NAME,\n\t\t) as DiscordService;\n\n\t\tif (!discordService?.client) {\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"Discord service is not available.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t\treturn { success: false, error: \"Discord service not available\" };\n\t\t}\n\n\t\t// Ensure state is available\n\t\tconst currentState = state ?? (await runtime.composeState(message));\n\n\t\t// Use LLM to extract edit parameters\n\t\tconst prompt = composePromptFromState({\n\t\t\tstate: currentState,\n\t\t\ttemplate: editMessageTemplate,\n\t\t});\n\n\t\tlet editParams: EditMessageParams | null = null;\n\n\t\tfor (let i = 0; i < 3; i++) {\n\t\t\tconst response = await runtime.useModel(ModelType.TEXT_SMALL, {\n\t\t\t\tprompt,\n\t\t\t});\n\n\t\t\tconst parsedResponse = parseJSONObjectFromText(response) as Record<\n\t\t\t\tstring,\n\t\t\t\tunknown\n\t\t\t> | null;\n\t\t\tif (\n\t\t\t\tparsedResponse &&\n\t\t\t\ttypeof parsedResponse.messageId === \"string\" &&\n\t\t\t\ttypeof parsedResponse.newText === \"string\"\n\t\t\t) {\n\t\t\t\teditParams = {\n\t\t\t\t\tmessageId: parsedResponse.messageId,\n\t\t\t\t\tnewText: parsedResponse.newText,\n\t\t\t\t\tchannelRef:\n\t\t\t\t\t\ttypeof parsedResponse.channelRef === \"string\"\n\t\t\t\t\t\t\t? parsedResponse.channelRef\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!editParams) {\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"I couldn't determine which message to edit or what to change it to.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t\treturn { success: false, error: \"Failed to extract edit parameters\" };\n\t\t}\n\n\t\ttry {\n\t\t\t// Get the channel\n\t\t\tlet channel: TextChannel | null = null;\n\n\t\t\tif (editParams.channelRef === \"current\") {\n\t\t\t\tconst channelId = message.content.channelId as string;\n\t\t\t\tif (channelId) {\n\t\t\t\t\tchannel = discordService.client.channels.cache.get(\n\t\t\t\t\t\tchannelId,\n\t\t\t\t\t) as TextChannel;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Try to find channel by name or ID\n\t\t\t\tchannel = discordService.client.channels.cache.find(\n\t\t\t\t\t(c) =>\n\t\t\t\t\t\tc.id === editParams?.channelRef ||\n\t\t\t\t\t\t(c.isTextBased() &&\n\t\t\t\t\t\t\t\"name\" in c &&\n\t\t\t\t\t\t\tc.name === editParams?.channelRef),\n\t\t\t\t) as TextChannel;\n\t\t\t}\n\n\t\t\tif (!channel?.isTextBased()) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't find the channel to edit the message in.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn { success: false, error: \"Channel not found\" };\n\t\t\t}\n\n\t\t\t// Fetch and edit the message\n\t\t\tconst targetMessage = (await channel.messages.fetch(\n\t\t\t\teditParams.messageId,\n\t\t\t)) as Message;\n\n\t\t\tif (!targetMessage) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't find the message to edit.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn { success: false, error: \"Message not found\" };\n\t\t\t}\n\n\t\t\t// Check if we can edit this message (must be our own message)\n\t\t\tif (targetMessage.author.id !== discordService.client.user?.id) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I can only edit my own messages.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: \"Cannot edit messages from other users\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tawait targetMessage.edit(editParams.newText);\n\n\t\t\tawait callback?.({\n\t\t\t\ttext: `I've edited the message to: \"${editParams.newText}\"`,\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tdata: terminalActionResultData({\n\t\t\t\t\tmessageId: editParams.messageId,\n\t\t\t\t\tchannelId: channel.id,\n\t\t\t\t\tnewText: editParams.newText,\n\t\t\t\t}),\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : String(error);\n\t\t\tawait callback?.({\n\t\t\t\ttext: `Failed to edit message: ${errorMessage}`,\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t\treturn { success: false, error: errorMessage };\n\t\t}\n\t},\n\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: \"Edit message 123456789 to say 'Hello updated!'\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{agentName}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"I'll edit that message now.\",\n\t\t\t\t\tactions: [\"EDIT_MESSAGE\"],\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: \"Update the previous message to fix the typo\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{agentName}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"I'll update that message.\",\n\t\t\t\t\tactions: [\"EDIT_MESSAGE\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t] as ActionExample[][],\n};\n\nexport default editMessage;\n",
|
|
16
|
+
"import {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\ttype Content,\n\tcomposePromptFromState,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype State,\n} from \"@elizaos/core\";\nimport type { GuildMember } from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\n// Import generated prompts\nimport { getUserInfoTemplate } from \"../generated/prompts/typescript/prompts.js\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\nimport type { DiscordService } from \"../service\";\n\nconst getUserIdentifier = async (\n\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n): Promise<{\n\tuserIdentifier: string;\n\tdetailed: boolean;\n} | null> => {\n\tconst prompt = composePromptFromState({\n\t\tstate,\n\t\ttemplate: getUserInfoTemplate,\n\t});\n\n\tfor (let i = 0; i < 3; i++) {\n\t\tconst response = await runtime.useModel(ModelType.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\n\t\tconst parsedResponse = parseJSONObjectFromText(response);\n\t\tif (parsedResponse?.userIdentifier) {\n\t\t\treturn {\n\t\t\t\tuserIdentifier: String(parsedResponse.userIdentifier),\n\t\t\t\tdetailed: parsedResponse.detailed === true,\n\t\t\t};\n\t\t}\n\t}\n\treturn null;\n};\n\nconst formatUserInfo = (\n\tmember: GuildMember,\n\tdetailed: boolean = false,\n): string => {\n\tconst user = member.user;\n\tconst joinedAt = member.joinedAt\n\t\t? new Date(member.joinedAt).toLocaleDateString()\n\t\t: \"Unknown\";\n\tconst createdAt = new Date(user.createdAt).toLocaleDateString();\n\tconst roles =\n\t\tmember.roles.cache\n\t\t\t.filter((role) => role.name !== \"@everyone\")\n\t\t\t.map((role) => role.name)\n\t\t\t.join(\", \") || \"No roles\";\n\n\tconst basicInfo = [\n\t\t\"👤 **User Information**\",\n\t\t`**Username:** ${user.username}${user.discriminator !== \"0\" ? `#${user.discriminator}` : \"\"}`,\n\t\t`**Display Name:** ${member.displayName}`,\n\t\t`**ID:** ${user.id}`,\n\t\t`**Bot:** ${user.bot ? \"Yes\" : \"No\"}`,\n\t\t`**Account Created:** ${createdAt}`,\n\t];\n\n\tif (detailed) {\n\t\tconst serverInfo = [\n\t\t\t\"\",\n\t\t\t\"🏛️ **Server Information**\",\n\t\t\t`**Nickname:** ${member.nickname || \"None\"}`,\n\t\t\t`**Joined Server:** ${joinedAt}`,\n\t\t\t`**Roles:** ${roles}`,\n\t\t\t`**Highest Role:** ${member.roles.highest.name}`,\n\t\t\t`**Permissions:** ${member.permissions.toArray().slice(0, 5).join(\", \")}${member.permissions.toArray().length > 5 ? \"...\" : \"\"}`,\n\t\t\t`**Voice Channel:** ${member.voice.channel ? member.voice.channel.name : \"Not in voice\"}`,\n\t\t\t`**Status:** ${member.presence?.status || \"offline\"}`,\n\t\t];\n\t\treturn [...basicInfo, ...serverInfo].join(\"\\n\");\n\t}\n\n\treturn basicInfo.join(\"\\n\");\n};\n\nconst spec = requireActionSpec(\"GET_USER_INFO\");\n\nexport const getUserInfo: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\tvalidate: async (\n\t\truntime: any,\n\t\tmessage: any,\n\t\tstate?: any,\n\t\toptions?: any,\n\t): Promise<boolean> => {\n\t\tconst __avTextRaw =\n\t\t\ttypeof message?.content?.text === \"string\" ? message.content.text : \"\";\n\t\tconst __avText = __avTextRaw.toLowerCase();\n\t\tconst __avKeywords = [\"get\", \"user\", \"info\"];\n\t\tconst __avKeywordOk =\n\t\t\t__avKeywords.length > 0 &&\n\t\t\t__avKeywords.some((word) => word.length > 0 && __avText.includes(word));\n\t\tconst __avRegex = /\\b(?:get|user|info)\\b/i;\n\t\tconst __avRegexOk = __avRegex.test(__avText);\n\t\tconst __avSource = String(message?.content?.source ?? \"\");\n\t\tconst __avExpectedSource = \"\";\n\t\tconst __avSourceOk = __avExpectedSource\n\t\t\t? __avSource === __avExpectedSource\n\t\t\t: Boolean(\n\t\t\t\t\t__avSource ||\n\t\t\t\t\t\tstate ||\n\t\t\t\t\t\truntime?.agentId ||\n\t\t\t\t\t\truntime?.getService ||\n\t\t\t\t\t\truntime?.getSetting,\n\t\t\t\t);\n\t\tconst __avOptions = options && typeof options === \"object\" ? options : {};\n\t\tconst __avInputOk =\n\t\t\t__avText.trim().length > 0 ||\n\t\t\tObject.keys(__avOptions as Record<string, unknown>).length > 0 ||\n\t\t\tBoolean(message?.content && typeof message.content === \"object\");\n\n\t\tif (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst __avLegacyValidate = async (\n\t\t\t_runtime: IAgentRuntime,\n\t\t\tmessage: Memory,\n\t\t\t_state?: State,\n\t\t): Promise<boolean> => {\n\t\t\treturn message.content.source === \"discord\";\n\t\t};\n\t\ttry {\n\t\t\treturn Boolean(\n\t\t\t\tawait (__avLegacyValidate as any)(runtime, message, state, options),\n\t\t\t);\n\t\t} catch {\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?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tconst discordService = runtime.getService(\n\t\t\tDISCORD_SERVICE_NAME,\n\t\t) as DiscordService;\n\n\t\tif (!discordService?.client) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"Discord service is not available.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"Discord service is not available\" };\n\t\t}\n\n\t\tif (!state) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"State is not available.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"State is not available\" };\n\t\t}\n\n\t\tconst userInfo = await getUserIdentifier(runtime, message, state);\n\t\tif (!userInfo) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't understand which user you want information about. Please specify a username or mention.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"Could not parse user identifier\" };\n\t\t}\n\n\t\ttry {\n\t\t\tconst room = state.data?.room || (await runtime.getRoom(message.roomId));\n\t\t\tconst serverId = room?.messageServerId;\n\t\t\tif (!serverId) {\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: \"I couldn't determine the current server.\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn { success: false, error: \"Could not determine current server\" };\n\t\t\t}\n\n\t\t\tconst guild = await discordService.client.guilds.fetch(serverId);\n\n\t\t\tlet member: GuildMember | null = null;\n\n\t\t\t// Handle \"self\" request\n\t\t\tif (userInfo.userIdentifier === \"self\") {\n\t\t\t\tconst content = message.content as {\n\t\t\t\t\tuser_id?: string;\n\t\t\t\t\tuserId?: string;\n\t\t\t\t};\n\t\t\t\tconst authorId = content.user_id || content.userId;\n\t\t\t\tif (authorId && typeof authorId === \"string\") {\n\t\t\t\t\tconst cleanId = authorId.replace(\"discord:\", \"\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\tmember = await guild.members.fetch(cleanId);\n\t\t\t\t\t} catch (_e) {\n\t\t\t\t\t\t// User not found\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Remove mention formatting if present\n\t\t\t\tconst cleanIdentifier = userInfo.userIdentifier.replace(/[<@!>]/g, \"\");\n\n\t\t\t\t// Try to fetch by ID first\n\t\t\t\tif (/^\\d+$/.test(cleanIdentifier)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tmember = await guild.members.fetch(cleanIdentifier);\n\t\t\t\t\t} catch (_e) {\n\t\t\t\t\t\t// Not an ID or user not found\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If not found by ID, search by username or display name\n\t\t\t\tif (!member) {\n\t\t\t\t\tconst members = await guild.members.fetch();\n\t\t\t\t\tmember =\n\t\t\t\t\t\tmembers.find(\n\t\t\t\t\t\t\t(m) =>\n\t\t\t\t\t\t\t\tm.user.username.toLowerCase() ===\n\t\t\t\t\t\t\t\t\tuserInfo.userIdentifier.toLowerCase() ||\n\t\t\t\t\t\t\t\tm.displayName.toLowerCase() ===\n\t\t\t\t\t\t\t\t\tuserInfo.userIdentifier.toLowerCase() ||\n\t\t\t\t\t\t\t\t(m.user.discriminator !== \"0\" &&\n\t\t\t\t\t\t\t\t\t`${m.user.username}#${m.user.discriminator}`.toLowerCase() ===\n\t\t\t\t\t\t\t\t\t\tuserInfo.userIdentifier.toLowerCase()),\n\t\t\t\t\t\t) || null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!member) {\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: `I couldn't find a user with the identifier \"${userInfo.userIdentifier}\" in this server.`,\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: `User not found: ${userInfo.userIdentifier}`,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst infoText = formatUserInfo(member, userInfo.detailed);\n\n\t\t\tconst response: Content = {\n\t\t\t\ttext: infoText,\n\t\t\t\tsource: message.content.source,\n\t\t\t};\n\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.(response);\n\t\t\t}\n\t\t\treturn { success: true, text: response.text };\n\t\t} catch (error) {\n\t\t\truntime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:get-user-info\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error getting user info\",\n\t\t\t);\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I encountered an error while getting user information. Please try again.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default getUserInfo;\n",
|
|
17
|
+
"import {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\ttype Content,\n\tcomposePromptFromState,\n\tcreateUniqueUuid,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tMemoryType,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype State,\n} from \"@elizaos/core\";\nimport type { BaseGuildVoiceChannel, TextChannel } from \"discord.js\";\nimport { ChannelType as DiscordChannelType } from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport { joinChannelTemplate } from \"../generated/prompts/typescript/prompts.js\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\nimport type { DiscordService } from \"../service\";\nimport type { VoiceManager } from \"../voice\";\nimport {\n\tterminalActionInteractionSemantics,\n\tterminalActionResultData,\n} from \"./actionResultSemantics\";\n\n/**\n * Get channel information from the user's request\n * @param {IAgentRuntime} runtime - The runtime object to interact with the agent.\n * @param {Memory} _message - The memory object containing the input message.\n * @param {State} state - The state of the conversation.\n * @returns {Promise<{channelIdentifier: string, isVoiceChannel: boolean} | null>} Channel info or null if not parseable.\n */\nconst getJoinChannelInfo = async (\n\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n): Promise<{ channelIdentifier: string; isVoiceChannel: boolean } | null> => {\n\tconst prompt = composePromptFromState({\n\t\tstate,\n\t\ttemplate: joinChannelTemplate,\n\t});\n\n\tfor (let i = 0; i < 3; i++) {\n\t\tconst response = await runtime.useModel(ModelType.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\n\t\tconst parsedResponse = parseJSONObjectFromText(response) as {\n\t\t\tchannelIdentifier: string;\n\t\t\tisVoiceChannel: boolean;\n\t\t} | null;\n\n\t\tif (parsedResponse?.channelIdentifier) {\n\t\t\treturn parsedResponse;\n\t\t}\n\t}\n\treturn null;\n};\n\n/**\n * Find a Discord channel by various identifiers\n * @param {DiscordService} discordService - The Discord service instance\n * @param {string} identifier - The channel identifier (name, ID, or mention)\n * @param {string} currentServerId - The current server ID to search in\n * @param {boolean} isVoiceChannel - Whether to look for voice channels\n * @returns {Promise<TextChannel | BaseGuildVoiceChannel | null>} The found channel or null\n */\nconst findChannel = async (\n\tdiscordService: DiscordService,\n\tidentifier: string,\n\tcurrentServerId?: string,\n\tisVoiceChannel?: boolean,\n): Promise<TextChannel | BaseGuildVoiceChannel | null> => {\n\tif (!discordService.client) {\n\t\treturn null;\n\t}\n\n\t// Remove channel mention formatting if present\n\tconst cleanId = identifier.replace(/[<#>]/g, \"\");\n\n\ttry {\n\t\t// Try to fetch by ID first\n\t\tif (/^\\d+$/.test(cleanId)) {\n\t\t\ttry {\n\t\t\t\tconst channel = await discordService.client.channels.fetch(cleanId);\n\t\t\t\tif (\n\t\t\t\t\tisVoiceChannel &&\n\t\t\t\t\tchannel &&\n\t\t\t\t\tchannel.type === DiscordChannelType.GuildVoice\n\t\t\t\t) {\n\t\t\t\t\treturn channel as BaseGuildVoiceChannel;\n\t\t\t\t} else if (\n\t\t\t\t\t!isVoiceChannel &&\n\t\t\t\t\tchannel &&\n\t\t\t\t\tchannel.isTextBased() &&\n\t\t\t\t\t!channel.isVoiceBased()\n\t\t\t\t) {\n\t\t\t\t\treturn channel as TextChannel;\n\t\t\t\t}\n\t\t\t} catch (_e) {\n\t\t\t\t// ID not found, continue to name search\n\t\t\t}\n\t\t}\n\n\t\t// Search in the current server if available\n\t\tif (currentServerId) {\n\t\t\tconst guild = await discordService.client.guilds.fetch(currentServerId);\n\t\t\tconst channels = await guild.channels.fetch();\n\n\t\t\t// Search by channel name\n\t\t\tconst channel = channels.find((ch) => {\n\t\t\t\tconst nameMatch =\n\t\t\t\t\tch?.name.toLowerCase() === identifier.toLowerCase() ||\n\t\t\t\t\tch?.name.toLowerCase().replace(/[^a-z0-9 ]/g, \"\") ===\n\t\t\t\t\t\tidentifier.toLowerCase().replace(/[^a-z0-9 ]/g, \"\");\n\n\t\t\t\tif (isVoiceChannel) {\n\t\t\t\t\treturn nameMatch && ch.type === DiscordChannelType.GuildVoice;\n\t\t\t\t} else {\n\t\t\t\t\treturn nameMatch && ch.isTextBased() && !ch.isVoiceBased();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (channel) {\n\t\t\t\treturn channel as TextChannel | BaseGuildVoiceChannel;\n\t\t\t}\n\t\t}\n\n\t\t// Search in all guilds the bot is in\n\t\tconst guilds = Array.from(discordService.client.guilds.cache.values());\n\t\tfor (const guild of guilds) {\n\t\t\ttry {\n\t\t\t\tconst channels = await guild.channels.fetch();\n\t\t\t\tconst channel = channels.find((ch) => {\n\t\t\t\t\tconst nameMatch =\n\t\t\t\t\t\tch?.name?.toLowerCase() === identifier.toLowerCase() ||\n\t\t\t\t\t\tch?.name?.toLowerCase().replace(/[^a-z0-9 ]/g, \"\") ===\n\t\t\t\t\t\t\tidentifier.toLowerCase().replace(/[^a-z0-9 ]/g, \"\");\n\n\t\t\t\t\tif (isVoiceChannel) {\n\t\t\t\t\t\treturn nameMatch && ch.type === DiscordChannelType.GuildVoice;\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn nameMatch && ch.isTextBased() && !ch.isVoiceBased();\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tif (channel) {\n\t\t\t\t\treturn channel as TextChannel | BaseGuildVoiceChannel;\n\t\t\t\t}\n\t\t\t} catch (_e) {\n\t\t\t\t// Continue searching in other guilds\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t} catch (_error) {\n\t\t// Standalone function - error handled by caller\n\t\treturn null;\n\t}\n};\n\nconst spec = requireActionSpec(\"JOIN_CHANNEL\");\n\nexport const joinChannel: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\t...terminalActionInteractionSemantics,\n\tvalidate: async (\n\t\truntime: any,\n\t\tmessage: any,\n\t\tstate?: any,\n\t\toptions?: any,\n\t): Promise<boolean> => {\n\t\tconst __avTextRaw =\n\t\t\ttypeof message?.content?.text === \"string\" ? message.content.text : \"\";\n\t\tconst __avText = __avTextRaw.toLowerCase();\n\t\tconst __avKeywords = [\"join\", \"channel\"];\n\t\tconst __avKeywordOk =\n\t\t\t__avKeywords.length > 0 &&\n\t\t\t__avKeywords.some((word) => word.length > 0 && __avText.includes(word));\n\t\tconst __avRegex = /\\b(?:join|channel)\\b/i;\n\t\tconst __avRegexOk = __avRegex.test(__avText);\n\t\tconst __avSource = String(message?.content?.source ?? \"\");\n\t\tconst __avExpectedSource = \"\";\n\t\tconst __avSourceOk = __avExpectedSource\n\t\t\t? __avSource === __avExpectedSource\n\t\t\t: Boolean(\n\t\t\t\t\t__avSource ||\n\t\t\t\t\t\tstate ||\n\t\t\t\t\t\truntime?.agentId ||\n\t\t\t\t\t\truntime?.getService ||\n\t\t\t\t\t\truntime?.getSetting,\n\t\t\t\t);\n\t\tconst __avOptions = options && typeof options === \"object\" ? options : {};\n\t\tconst __avInputOk =\n\t\t\t__avText.trim().length > 0 ||\n\t\t\tObject.keys(__avOptions as Record<string, unknown>).length > 0 ||\n\t\t\tBoolean(message?.content && typeof message.content === \"object\");\n\n\t\tif (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst __avLegacyValidate = async (\n\t\t\t_runtime: IAgentRuntime,\n\t\t\tmessage: Memory,\n\t\t\t_state?: State,\n\t\t): Promise<boolean> => {\n\t\t\treturn message.content.source === \"discord\";\n\t\t};\n\t\ttry {\n\t\t\treturn Boolean(\n\t\t\t\tawait (__avLegacyValidate as any)(runtime, message, state, options),\n\t\t\t);\n\t\t} catch {\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?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tconst discordService = runtime.getService(\n\t\t\tDISCORD_SERVICE_NAME,\n\t\t) as DiscordService;\n\n\t\tif (!discordService?.client) {\n\t\t\truntime.logger.error(\n\t\t\t\t{ src: \"plugin:discord:action:join-channel\", agentId: runtime.agentId },\n\t\t\t\t\"Discord service not found or not initialized\",\n\t\t\t);\n\t\t\treturn { success: false, error: \"Discord service not available\" };\n\t\t}\n\n\t\tif (!state) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"State is not available.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"State is not available\" };\n\t\t}\n\n\t\tconst channelInfo = await getJoinChannelInfo(runtime, message, state);\n\t\tif (!channelInfo) {\n\t\t\truntime.logger.warn(\n\t\t\t\t{ src: \"plugin:discord:action:join-channel\", agentId: runtime.agentId },\n\t\t\t\t\"Could not parse channel information from message\",\n\t\t\t);\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't understand which channel you want me to join. Please specify the channel name or ID.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"Could not parse channel information\" };\n\t\t}\n\n\t\ttry {\n\t\t\tconst stateData = state.data;\n\t\t\tconst room = stateData?.room || (await runtime.getRoom(message.roomId));\n\t\t\tconst currentServerId = room?.messageServerId;\n\n\t\t\t// First, try the user's approach - if they said voice/vc, look for voice channels\n\t\t\tconst messageContentText = message.content.text;\n\t\t\tconst messageText = messageContentText?.toLowerCase() || \"\";\n\t\t\tconst isVoiceRequest =\n\t\t\t\tchannelInfo.isVoiceChannel ||\n\t\t\t\tmessageText.includes(\"voice\") ||\n\t\t\t\tmessageText.includes(\"vc\") ||\n\t\t\t\tmessageText.includes(\"hop in\");\n\n\t\t\t// Find the channel (try voice first if it's a voice request)\n\t\t\tlet targetChannel = isVoiceRequest\n\t\t\t\t? await findChannel(\n\t\t\t\t\t\tdiscordService,\n\t\t\t\t\t\tchannelInfo.channelIdentifier,\n\t\t\t\t\t\tcurrentServerId,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t)\n\t\t\t\t: await findChannel(\n\t\t\t\t\t\tdiscordService,\n\t\t\t\t\t\tchannelInfo.channelIdentifier,\n\t\t\t\t\t\tcurrentServerId,\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t);\n\n\t\t\t// If not found, try the opposite type\n\t\t\tif (!targetChannel) {\n\t\t\t\ttargetChannel = isVoiceRequest\n\t\t\t\t\t? await findChannel(\n\t\t\t\t\t\t\tdiscordService,\n\t\t\t\t\t\t\tchannelInfo.channelIdentifier,\n\t\t\t\t\t\t\tcurrentServerId,\n\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t)\n\t\t\t\t\t: await findChannel(\n\t\t\t\t\t\t\tdiscordService,\n\t\t\t\t\t\t\tchannelInfo.channelIdentifier,\n\t\t\t\t\t\t\tcurrentServerId,\n\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (!targetChannel) {\n\t\t\t\t// If the user is in a voice channel and no specific channel was found, join their voice channel\n\t\t\t\tif (isVoiceRequest && currentServerId) {\n\t\t\t\t\tconst guild = discordService.client.guilds.cache.get(currentServerId);\n\t\t\t\t\tconst members = guild?.members?.cache;\n\t\t\t\t\tconst member = members?.find(\n\t\t\t\t\t\t(member) =>\n\t\t\t\t\t\t\tcreateUniqueUuid(runtime, member.id) === message.entityId,\n\t\t\t\t\t);\n\n\t\t\t\t\tconst memberVoice = member?.voice;\n\t\t\t\t\tif (memberVoice?.channel) {\n\t\t\t\t\t\ttargetChannel = member.voice.channel as BaseGuildVoiceChannel;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!targetChannel) {\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: `I couldn't find a channel with the identifier \"${channelInfo.channelIdentifier}\". Please make sure the channel name or ID is correct and I have access to it.`,\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: `Channel not found: ${channelInfo.channelIdentifier}`,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Handle voice channels\n\t\t\tif (targetChannel.type === DiscordChannelType.GuildVoice) {\n\t\t\t\tconst voiceChannel = targetChannel as BaseGuildVoiceChannel;\n\t\t\t\tconst voiceManager = discordService.voiceManager as VoiceManager;\n\n\t\t\t\tif (!voiceManager) {\n\t\t\t\t\tif (callback) {\n\t\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\t\ttext: \"Voice functionality is not available at the moment.\",\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn { success: false, error: \"Voice functionality not available\" };\n\t\t\t\t}\n\n\t\t\t\t// Join the voice channel\n\t\t\t\tawait voiceManager.joinChannel(voiceChannel);\n\n\t\t\t\tawait runtime.createMemory(\n\t\t\t\t\t{\n\t\t\t\t\t\tentityId: message.entityId,\n\t\t\t\t\t\tagentId: message.agentId,\n\t\t\t\t\t\troomId: message.roomId,\n\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t\tthought: `I joined the voice channel ${voiceChannel.name}`,\n\t\t\t\t\t\t\tactions: [\"JOIN_VOICE_STARTED\"],\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\ttype: MemoryType.CUSTOM,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t\"messages\",\n\t\t\t\t);\n\n\t\t\t\tconst response: Content = {\n\t\t\t\t\ttext: `I've joined the voice channel ${voiceChannel.name}!`,\n\t\t\t\t\tactions: [\"JOIN_CHANNEL_RESPONSE\"],\n\t\t\t\t\tsource: message.content.source,\n\t\t\t\t};\n\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.(response);\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\ttext: response.text,\n\t\t\t\t\tdata: terminalActionResultData(),\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\t// Handle text channels\n\t\t\t\tconst textChannel = targetChannel as TextChannel;\n\n\t\t\t\t// Check if we're already listening to this channel\n\t\t\t\tconst currentChannels = discordService.getAllowedChannels();\n\t\t\t\tif (currentChannels.includes(textChannel.id)) {\n\t\t\t\t\tif (callback) {\n\t\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\t\ttext: `I'm already listening to ${textChannel.name} (<#${textChannel.id}>).`,\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\ttext: `Already listening to ${textChannel.name}`,\n\t\t\t\t\t\tdata: terminalActionResultData(),\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// Add the channel to the allowed list\n\t\t\t\tconst success = discordService.addAllowedChannel(textChannel.id);\n\n\t\t\t\tif (success) {\n\t\t\t\t\tconst response: Content = {\n\t\t\t\t\t\ttext: `I've started listening to ${textChannel.name} (<#${textChannel.id}>). I'll now respond to messages in that channel.`,\n\t\t\t\t\t\tactions: [\"JOIN_CHANNEL_RESPONSE\"],\n\t\t\t\t\t\tsource: message.content.source,\n\t\t\t\t\t};\n\n\t\t\t\t\tif (callback) {\n\t\t\t\t\t\tawait callback?.(response);\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\ttext: response.text,\n\t\t\t\t\t\tdata: terminalActionResultData(),\n\t\t\t\t\t};\n\t\t\t\t} else {\n\t\t\t\t\tif (callback) {\n\t\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\t\ttext: `I couldn't add ${textChannel.name} to my listening list. Please try again.`,\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\terror: `Could not add ${textChannel.name} to listening list`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\truntime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:join-channel\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error joining channel\",\n\t\t\t);\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I encountered an error while trying to join the channel. Please make sure I have the necessary permissions.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default joinChannel;\n",
|
|
18
|
+
"import {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\ttype Content,\n\tcomposePromptFromState,\n\tcreateUniqueUuid,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tMemoryType,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype State,\n} from \"@elizaos/core\";\nimport {\n\tBaseGuildVoiceChannel,\n\tChannelType as DiscordChannelType,\n\ttype TextChannel,\n} from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport { leaveChannelTemplate } from \"../generated/prompts/typescript/prompts.js\";\nimport type { DiscordService } from \"../service\";\nimport type { VoiceManager } from \"../voice\";\nimport { terminalActionInteractionSemantics } from \"./actionResultSemantics\";\n\n/**\n * Get channel information from the user's request\n * @param {IAgentRuntime} runtime - The runtime object to interact with the agent.\n * @param {Memory} _message - The memory object containing the input message.\n * @param {State} state - The state of the conversation.\n * @returns {Promise<{channelIdentifier: string, isVoiceChannel: boolean} | null>} Channel info or null if not parseable.\n */\nconst getLeaveChannelInfo = async (\n\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n): Promise<{ channelIdentifier: string; isVoiceChannel: boolean } | null> => {\n\tconst prompt = composePromptFromState({\n\t\tstate,\n\t\ttemplate: leaveChannelTemplate,\n\t});\n\n\tfor (let i = 0; i < 3; i++) {\n\t\tconst response = await runtime.useModel(ModelType.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\n\t\tconst parsedResponse = parseJSONObjectFromText(response) as {\n\t\t\tchannelIdentifier: string;\n\t\t\tisVoiceChannel: boolean;\n\t\t} | null;\n\n\t\tif (parsedResponse?.channelIdentifier) {\n\t\t\treturn parsedResponse;\n\t\t}\n\t}\n\treturn null;\n};\n\n/**\n * Find a Discord channel by various identifiers\n * @param {DiscordService} discordService - The Discord service instance\n * @param {string} identifier - The channel identifier (name, ID, or mention)\n * @param {string} currentChannelId - The current channel ID if \"current\" is specified\n * @param {string} currentServerId - The current server ID to search in\n * @param {boolean} isVoiceChannel - Whether to look for voice channels\n * @returns {Promise<TextChannel | BaseGuildVoiceChannel | null>} The found channel or null\n */\nconst findChannel = async (\n\tdiscordService: DiscordService,\n\tidentifier: string,\n\tcurrentChannelId?: string,\n\tcurrentServerId?: string,\n\tisVoiceChannel?: boolean,\n): Promise<TextChannel | BaseGuildVoiceChannel | null> => {\n\tif (!discordService.client) {\n\t\treturn null;\n\t}\n\n\t// Handle \"current\" channel\n\tif (identifier === \"current\" && currentChannelId) {\n\t\ttry {\n\t\t\tconst channel =\n\t\t\t\tawait discordService.client.channels.fetch(currentChannelId);\n\t\t\tif (\n\t\t\t\tisVoiceChannel &&\n\t\t\t\tchannel &&\n\t\t\t\tchannel.type === DiscordChannelType.GuildVoice\n\t\t\t) {\n\t\t\t\treturn channel as BaseGuildVoiceChannel;\n\t\t\t} else if (\n\t\t\t\t!isVoiceChannel &&\n\t\t\t\tchannel &&\n\t\t\t\tchannel.isTextBased() &&\n\t\t\t\t!channel.isVoiceBased()\n\t\t\t) {\n\t\t\t\treturn channel as TextChannel;\n\t\t\t}\n\t\t} catch (_e) {\n\t\t\t// Current channel not found\n\t\t}\n\t}\n\n\t// Remove channel mention formatting if present\n\tconst cleanId = identifier.replace(/[<#>]/g, \"\");\n\n\ttry {\n\t\t// Try to fetch by ID first\n\t\tif (/^\\d+$/.test(cleanId)) {\n\t\t\ttry {\n\t\t\t\tconst channel = await discordService.client.channels.fetch(cleanId);\n\t\t\t\tif (\n\t\t\t\t\tisVoiceChannel &&\n\t\t\t\t\tchannel &&\n\t\t\t\t\tchannel.type === DiscordChannelType.GuildVoice\n\t\t\t\t) {\n\t\t\t\t\treturn channel as BaseGuildVoiceChannel;\n\t\t\t\t} else if (\n\t\t\t\t\t!isVoiceChannel &&\n\t\t\t\t\tchannel &&\n\t\t\t\t\tchannel.isTextBased() &&\n\t\t\t\t\t!channel.isVoiceBased()\n\t\t\t\t) {\n\t\t\t\t\treturn channel as TextChannel;\n\t\t\t\t}\n\t\t\t} catch (_e) {\n\t\t\t\t// ID not found, continue to name search\n\t\t\t}\n\t\t}\n\n\t\t// Search in the current server if available\n\t\tif (currentServerId) {\n\t\t\tconst guild = await discordService.client.guilds.fetch(currentServerId);\n\t\t\tconst channels = await guild.channels.fetch();\n\n\t\t\t// Search by channel name\n\t\t\tconst channel = channels.find((ch) => {\n\t\t\t\tconst nameMatch =\n\t\t\t\t\tch?.name?.toLowerCase() === identifier.toLowerCase() ||\n\t\t\t\t\tch?.name?.toLowerCase().replace(/[^a-z0-9 ]/g, \"\") ===\n\t\t\t\t\t\tidentifier.toLowerCase().replace(/[^a-z0-9 ]/g, \"\");\n\n\t\t\t\tif (isVoiceChannel) {\n\t\t\t\t\treturn nameMatch && ch.type === DiscordChannelType.GuildVoice;\n\t\t\t\t} else {\n\t\t\t\t\treturn nameMatch && ch.isTextBased() && !ch.isVoiceBased();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (channel) {\n\t\t\t\treturn channel as TextChannel | BaseGuildVoiceChannel;\n\t\t\t}\n\t\t}\n\n\t\t// Search in all guilds the bot is in\n\t\tconst guilds = Array.from(discordService.client.guilds.cache.values());\n\t\tfor (const guild of guilds) {\n\t\t\ttry {\n\t\t\t\tconst channels = await guild.channels.fetch();\n\t\t\t\tconst channel = channels.find((ch) => {\n\t\t\t\t\tconst nameMatch =\n\t\t\t\t\t\tch?.name?.toLowerCase() === identifier.toLowerCase() ||\n\t\t\t\t\t\tch?.name?.toLowerCase().replace(/[^a-z0-9 ]/g, \"\") ===\n\t\t\t\t\t\t\tidentifier.toLowerCase().replace(/[^a-z0-9 ]/g, \"\");\n\n\t\t\t\t\tif (isVoiceChannel) {\n\t\t\t\t\t\treturn nameMatch && ch.type === DiscordChannelType.GuildVoice;\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn nameMatch && ch.isTextBased() && !ch.isVoiceBased();\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tif (channel) {\n\t\t\t\t\treturn channel as TextChannel | BaseGuildVoiceChannel;\n\t\t\t\t}\n\t\t\t} catch (_e) {\n\t\t\t\t// Continue searching in other guilds\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t} catch (_error) {\n\t\t// Standalone function - error handled by caller\n\t\treturn null;\n\t}\n};\n\nimport type { HandlerOptions } from \"@elizaos/core\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\n\nconst spec = requireActionSpec(\"LEAVE_CHANNEL\");\n\nexport const leaveChannel: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\t...terminalActionInteractionSemantics,\n\tvalidate: async (\n\t\truntime: any,\n\t\tmessage: any,\n\t\tstate?: any,\n\t\toptions?: any,\n\t): Promise<boolean> => {\n\t\tconst __avTextRaw =\n\t\t\ttypeof message?.content?.text === \"string\" ? message.content.text : \"\";\n\t\tconst __avText = __avTextRaw.toLowerCase();\n\t\tconst __avKeywords = [\"leave\", \"channel\"];\n\t\tconst __avKeywordOk =\n\t\t\t__avKeywords.length > 0 &&\n\t\t\t__avKeywords.some((word) => word.length > 0 && __avText.includes(word));\n\t\tconst __avRegex = /\\b(?:leave|channel)\\b/i;\n\t\tconst __avRegexOk = __avRegex.test(__avText);\n\t\tconst __avSource = String(message?.content?.source ?? \"\");\n\t\tconst __avExpectedSource = \"\";\n\t\tconst __avSourceOk = __avExpectedSource\n\t\t\t? __avSource === __avExpectedSource\n\t\t\t: Boolean(\n\t\t\t\t\t__avSource ||\n\t\t\t\t\t\tstate ||\n\t\t\t\t\t\truntime?.agentId ||\n\t\t\t\t\t\truntime?.getService ||\n\t\t\t\t\t\truntime?.getSetting,\n\t\t\t\t);\n\t\tconst __avOptions = options && typeof options === \"object\" ? options : {};\n\t\tconst __avInputOk =\n\t\t\t__avText.trim().length > 0 ||\n\t\t\tObject.keys(__avOptions as Record<string, unknown>).length > 0 ||\n\t\t\tBoolean(message?.content && typeof message.content === \"object\");\n\n\t\tif (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst __avLegacyValidate = async (\n\t\t\t_runtime: IAgentRuntime,\n\t\t\tmessage: Memory,\n\t\t\t_state?: State,\n\t\t): Promise<boolean> => {\n\t\t\treturn message.content.source === \"discord\";\n\t\t};\n\t\ttry {\n\t\t\treturn Boolean(\n\t\t\t\tawait (__avLegacyValidate as any)(runtime, message, state, options),\n\t\t\t);\n\t\t} catch {\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?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tconst discordService = runtime.getService(\n\t\t\tDISCORD_SERVICE_NAME,\n\t\t) as DiscordService;\n\n\t\tif (!discordService?.client) {\n\t\t\truntime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:leave-channel\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t},\n\t\t\t\t\"Discord service not found or not initialized\",\n\t\t\t);\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"Discord service is not available.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst channelInfo = await getLeaveChannelInfo(runtime, message, state);\n\n\t\ttry {\n\t\t\tconst stateData = state.data;\n\t\t\tconst room = stateData?.room || (await runtime.getRoom(message.roomId));\n\t\t\tconst currentServerId = room?.messageServerId;\n\t\t\tconst currentChannelId = room?.channelId;\n\n\t\t\t// Check if trying to leave voice without specifying channel\n\t\t\tconst messageContentText = message.content.text;\n\t\t\tconst messageText = messageContentText?.toLowerCase() || \"\";\n\t\t\tconst isVoiceRequest =\n\t\t\t\tchannelInfo?.isVoiceChannel ||\n\t\t\t\tmessageText.includes(\"voice\") ||\n\t\t\t\tmessageText.includes(\"vc\") ||\n\t\t\t\tmessageText.includes(\"call\");\n\n\t\t\t// If it's a generic voice leave request, handle current voice channel\n\t\t\tif (\n\t\t\t\tisVoiceRequest &&\n\t\t\t\t(!channelInfo || channelInfo.channelIdentifier === \"current\")\n\t\t\t) {\n\t\t\t\tconst voiceManager = discordService.voiceManager as VoiceManager;\n\n\t\t\t\tif (!voiceManager) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: \"Voice functionality is not available at the moment.\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\tif (currentServerId) {\n\t\t\t\t\tconst guild = discordService.client.guilds.cache.get(currentServerId);\n\t\t\t\t\tconst guildMembers = guild?.members;\n\t\t\t\t\tconst guildMembersMe = guildMembers?.me;\n\t\t\t\t\tconst guildMembersMeVoice = guildMembersMe?.voice;\n\t\t\t\t\tconst voiceChannel = guildMembersMeVoice?.channel;\n\n\t\t\t\t\tif (\n\t\t\t\t\t\t!voiceChannel ||\n\t\t\t\t\t\t!(voiceChannel instanceof BaseGuildVoiceChannel)\n\t\t\t\t\t) {\n\t\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\t\ttext: \"I'm not currently in a voice channel.\",\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst connection = voiceManager.getVoiceConnection(guild.id);\n\t\t\t\t\tif (!connection) {\n\t\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\t\ttext: \"No active voice connection found.\",\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\n\t\t\t\t\tvoiceManager.leaveChannel(voiceChannel);\n\n\t\t\t\t\tawait runtime.createMemory(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tentityId: message.entityId,\n\t\t\t\t\t\t\tagentId: message.agentId,\n\t\t\t\t\t\t\troomId: createUniqueUuid(runtime, voiceChannel.id),\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t\t\tthought: `I left the voice channel ${voiceChannel.name}`,\n\t\t\t\t\t\t\t\tactions: [\"LEAVE_VOICE_STARTED\"],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\ttype: MemoryType.CUSTOM,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"messages\",\n\t\t\t\t\t);\n\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: `I've left the voice channel ${voiceChannel.name}.`,\n\t\t\t\t\t\tsource: \"discord\",\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\tif (!channelInfo) {\n\t\t\t\truntime.logger.warn(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:action:leave-channel\",\n\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t},\n\t\t\t\t\t\"Could not parse channel information from message\",\n\t\t\t\t);\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't understand which channel you want me to leave. Please specify the channel name or ID.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t// Find the channel (try voice first if it's a voice request)\n\t\t\tlet targetChannel = isVoiceRequest\n\t\t\t\t? await findChannel(\n\t\t\t\t\t\tdiscordService,\n\t\t\t\t\t\tchannelInfo.channelIdentifier,\n\t\t\t\t\t\tcurrentChannelId,\n\t\t\t\t\t\tcurrentServerId,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t)\n\t\t\t\t: await findChannel(\n\t\t\t\t\t\tdiscordService,\n\t\t\t\t\t\tchannelInfo.channelIdentifier,\n\t\t\t\t\t\tcurrentChannelId,\n\t\t\t\t\t\tcurrentServerId,\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t);\n\n\t\t\t// If not found, try the opposite type\n\t\t\tif (!targetChannel) {\n\t\t\t\ttargetChannel = isVoiceRequest\n\t\t\t\t\t? await findChannel(\n\t\t\t\t\t\t\tdiscordService,\n\t\t\t\t\t\t\tchannelInfo.channelIdentifier,\n\t\t\t\t\t\t\tcurrentChannelId,\n\t\t\t\t\t\t\tcurrentServerId,\n\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t)\n\t\t\t\t\t: await findChannel(\n\t\t\t\t\t\t\tdiscordService,\n\t\t\t\t\t\t\tchannelInfo.channelIdentifier,\n\t\t\t\t\t\t\tcurrentChannelId,\n\t\t\t\t\t\t\tcurrentServerId,\n\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (!targetChannel) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: `I couldn't find a channel with the identifier \"${channelInfo.channelIdentifier}\". Please make sure the channel name or ID is correct.`,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t// Handle voice channels\n\t\t\tif (targetChannel.type === DiscordChannelType.GuildVoice) {\n\t\t\t\tconst voiceChannel = targetChannel as BaseGuildVoiceChannel;\n\t\t\t\tconst voiceManager = discordService.voiceManager as VoiceManager;\n\n\t\t\t\tif (!voiceManager) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: \"Voice functionality is not available at the moment.\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\tconst guild = voiceChannel.guild;\n\t\t\t\tconst guildMembersMe = guild.members?.me;\n\t\t\t\tconst guildMembersMeVoice = guildMembersMe?.voice;\n\t\t\t\tconst currentVoiceChannel = guildMembersMeVoice?.channel;\n\n\t\t\t\tif (\n\t\t\t\t\t!currentVoiceChannel ||\n\t\t\t\t\tcurrentVoiceChannel.id !== voiceChannel.id\n\t\t\t\t) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: `I'm not currently in the voice channel ${voiceChannel.name}.`,\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\tvoiceManager.leaveChannel(voiceChannel);\n\n\t\t\t\tawait runtime.createMemory(\n\t\t\t\t\t{\n\t\t\t\t\t\tentityId: message.entityId,\n\t\t\t\t\t\tagentId: message.agentId,\n\t\t\t\t\t\troomId: createUniqueUuid(runtime, voiceChannel.id),\n\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t\tthought: `I left the voice channel ${voiceChannel.name}`,\n\t\t\t\t\t\t\tactions: [\"LEAVE_VOICE_STARTED\"],\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\ttype: MemoryType.CUSTOM,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t\"messages\",\n\t\t\t\t);\n\n\t\t\t\tconst response: Content = {\n\t\t\t\t\ttext: `I've left the voice channel ${voiceChannel.name}.`,\n\t\t\t\t\tactions: [\"LEAVE_CHANNEL_RESPONSE\"],\n\t\t\t\t\tsource: message.content.source,\n\t\t\t\t};\n\n\t\t\t\tawait callback?.(response);\n\t\t\t} else {\n\t\t\t\t// Handle text channels\n\t\t\t\tconst textChannel = targetChannel as TextChannel;\n\n\t\t\t\t// Check if we're listening to this channel\n\t\t\t\tconst currentChannels = discordService.getAllowedChannels();\n\t\t\t\tif (!currentChannels.includes(textChannel.id)) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: `I'm not currently listening to ${textChannel.name} (<#${textChannel.id}>).`,\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\t// Remove the channel from the allowed list\n\t\t\t\tconst success = discordService.removeAllowedChannel(textChannel.id);\n\n\t\t\t\tif (success) {\n\t\t\t\t\tconst response: Content = {\n\t\t\t\t\t\ttext: `I've stopped listening to ${textChannel.name} (<#${textChannel.id}>). I will no longer respond to messages in that channel.`,\n\t\t\t\t\t\tactions: [\"LEAVE_CHANNEL_RESPONSE\"],\n\t\t\t\t\t\tsource: message.content.source,\n\t\t\t\t\t};\n\n\t\t\t\t\tawait callback?.(response);\n\t\t\t\t} else {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: `I couldn't remove ${textChannel.name} from my listening list. This channel might be configured in my environment settings and cannot be removed dynamically.`,\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\truntime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:leave-channel\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error leaving channel\",\n\t\t\t);\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"I encountered an error while trying to leave the channel. Please try again.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t\treturn undefined;\n\t\t}\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default leaveChannel;\n",
|
|
19
|
+
"import type {\n\tAction,\n\tActionExample,\n\tActionResult,\n\tContent,\n\tHandlerCallback,\n\tHandlerOptions,\n\tIAgentRuntime,\n\tMemory,\n\tState,\n} from \"@elizaos/core\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\nimport type { DiscordService } from \"../service\";\n\nconst spec = requireActionSpec(\"LIST_CHANNELS\");\n\nexport const listChannels: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\tvalidate: async (\n\t\truntime: any,\n\t\tmessage: any,\n\t\tstate?: any,\n\t\toptions?: any,\n\t): Promise<boolean> => {\n\t\tconst __avTextRaw =\n\t\t\ttypeof message?.content?.text === \"string\" ? message.content.text : \"\";\n\t\tconst __avText = __avTextRaw.toLowerCase();\n\t\tconst __avKeywords = [\"list\", \"channels\"];\n\t\tconst __avKeywordOk =\n\t\t\t__avKeywords.length > 0 &&\n\t\t\t__avKeywords.some((word) => word.length > 0 && __avText.includes(word));\n\t\tconst __avRegex = /\\b(?:list|channels)\\b/i;\n\t\tconst __avRegexOk = __avRegex.test(__avText);\n\t\tconst __avSource = String(message?.content?.source ?? \"\");\n\t\tconst __avExpectedSource = \"\";\n\t\tconst __avSourceOk = __avExpectedSource\n\t\t\t? __avSource === __avExpectedSource\n\t\t\t: Boolean(\n\t\t\t\t\t__avSource ||\n\t\t\t\t\t\tstate ||\n\t\t\t\t\t\truntime?.agentId ||\n\t\t\t\t\t\truntime?.getService ||\n\t\t\t\t\t\truntime?.getSetting,\n\t\t\t\t);\n\t\tconst __avOptions = options && typeof options === \"object\" ? options : {};\n\t\tconst __avInputOk =\n\t\t\t__avText.trim().length > 0 ||\n\t\t\tObject.keys(__avOptions as Record<string, unknown>).length > 0 ||\n\t\t\tBoolean(message?.content && typeof message.content === \"object\");\n\n\t\tif (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst __avLegacyValidate = async (\n\t\t\t_runtime: IAgentRuntime,\n\t\t\tmessage: Memory,\n\t\t\t_state?: State,\n\t\t): Promise<boolean> => {\n\t\t\treturn message.content.source === \"discord\";\n\t\t};\n\t\ttry {\n\t\t\treturn Boolean(\n\t\t\t\tawait (__avLegacyValidate as any)(runtime, message, state, options),\n\t\t\t);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t},\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\t_state?: State,\n\t\t_options?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tconst discordService = runtime.getService(\n\t\t\tDISCORD_SERVICE_NAME,\n\t\t) as DiscordService;\n\n\t\tif (!discordService?.client) {\n\t\t\truntime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:list-channels\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t},\n\t\t\t\t\"Discord service not found or not initialized\",\n\t\t\t);\n\t\t\treturn { success: false, error: \"Discord service not available\" };\n\t\t}\n\n\t\ttry {\n\t\t\t// Get all allowed channels\n\t\t\tconst allowedChannelIds = discordService.getAllowedChannels();\n\n\t\t\tif (allowedChannelIds.length === 0) {\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: \"I'm currently listening to all channels (no restrictions are set).\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\ttext: \"Listening to all channels (no restrictions)\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Fetch channel information for each allowed channel\n\t\t\tconst channelInfoPromises = allowedChannelIds.map(async (channelId) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst client = discordService.client;\n\t\t\t\t\tconst channel = client && (await client.channels.fetch(channelId));\n\t\t\t\t\tif (channel?.isTextBased() && !channel.isVoiceBased()) {\n\t\t\t\t\t\tconst guild = \"guild\" in channel ? channel.guild : null;\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tid: channelId,\n\t\t\t\t\t\t\tname: \"name\" in channel ? channel.name : \"DM\",\n\t\t\t\t\t\t\tmention: `<#${channelId}>`,\n\t\t\t\t\t\t\tserver: guild?.name || \"Direct Message\",\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t} catch (_e) {\n\t\t\t\t\t// Channel might have been deleted or bot lost access\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid: channelId,\n\t\t\t\t\t\tname: \"Unknown\",\n\t\t\t\t\t\tmention: channelId,\n\t\t\t\t\t\tserver: \"Unknown or Deleted\",\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t});\n\n\t\t\tconst channelInfos = (await Promise.all(channelInfoPromises)).filter(\n\t\t\t\tBoolean,\n\t\t\t);\n\n\t\t\t// Format the response\n\t\t\tlet responseText = `I'm currently listening to ${channelInfos.length} channel${channelInfos.length !== 1 ? \"s\" : \"\"}:\\n\\n`;\n\n\t\t\t// Group by server\n\t\t\tconst channelsByServer = channelInfos.reduce(\n\t\t\t\t(acc, channel) => {\n\t\t\t\t\tif (!channel) {\n\t\t\t\t\t\treturn acc;\n\t\t\t\t\t}\n\t\t\t\t\tif (!acc[channel.server]) {\n\t\t\t\t\t\tacc[channel.server] = [];\n\t\t\t\t\t}\n\t\t\t\t\tacc[channel.server].push(channel);\n\t\t\t\t\treturn acc;\n\t\t\t\t},\n\t\t\t\t{} as Record<string, typeof channelInfos>,\n\t\t\t);\n\n\t\t\t// Format by server\n\t\t\tfor (const [serverName, channels] of Object.entries(channelsByServer)) {\n\t\t\t\tresponseText += `**${serverName}**\\n`;\n\t\t\t\tfor (const channel of channels) {\n\t\t\t\t\tif (channel) {\n\t\t\t\t\t\tresponseText += `• ${channel.name} (${channel.mention})\\n`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tresponseText += \"\\n\";\n\t\t\t}\n\n\t\t\t// Check if CHANNEL_IDS is set\n\t\t\tconst envChannelIds = runtime.getSetting(\"CHANNEL_IDS\") as string;\n\t\t\tif (envChannelIds) {\n\t\t\t\tresponseText +=\n\t\t\t\t\t\"\\n*Some channels are configured in environment settings and cannot be removed dynamically.*\";\n\t\t\t}\n\n\t\t\tconst response: Content = {\n\t\t\t\ttext: responseText.trim(),\n\t\t\t\tactions: [\"LIST_CHANNELS_RESPONSE\"],\n\t\t\t\tsource: message.content.source,\n\t\t\t};\n\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.(response);\n\t\t\t}\n\t\t\treturn { success: true, text: response.text };\n\t\t} catch (error) {\n\t\t\truntime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:list-channels\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error listing channels\",\n\t\t\t);\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I encountered an error while trying to list the channels. Please try again.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default listChannels;\n",
|
|
20
|
+
"import {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\ttype Content,\n\tcomposePromptFromState,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype State,\n} from \"@elizaos/core\";\nimport {\n\ttype Message,\n\tPermissionsBitField,\n\ttype TextChannel,\n} from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport { pinMessageTemplate } from \"../generated/prompts/typescript/prompts.js\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\nimport type { DiscordService } from \"../service\";\nimport { terminalActionInteractionSemantics } from \"./actionResultSemantics\";\n\nconst getMessageRef = async (\n\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n): Promise<{\n\tmessageRef: string;\n} | null> => {\n\tconst prompt = composePromptFromState({\n\t\tstate,\n\t\ttemplate: pinMessageTemplate,\n\t});\n\n\tfor (let i = 0; i < 3; i++) {\n\t\tconst response = await runtime.useModel(ModelType.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\n\t\tconst parsedResponse = parseJSONObjectFromText(response);\n\t\tif (parsedResponse?.messageRef) {\n\t\t\treturn {\n\t\t\t\tmessageRef: String(parsedResponse.messageRef),\n\t\t\t};\n\t\t}\n\t}\n\treturn null;\n};\n\nconst spec = requireActionSpec(\"PIN_MESSAGE\");\n\nexport const pinMessage: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\t...terminalActionInteractionSemantics,\n\tvalidate: async (\n\t\truntime: any,\n\t\tmessage: any,\n\t\tstate?: any,\n\t\toptions?: any,\n\t): Promise<boolean> => {\n\t\tconst __avTextRaw =\n\t\t\ttypeof message?.content?.text === \"string\" ? message.content.text : \"\";\n\t\tconst __avText = __avTextRaw.toLowerCase();\n\t\tconst __avKeywords = [\"pin\", \"message\"];\n\t\tconst __avKeywordOk =\n\t\t\t__avKeywords.length > 0 &&\n\t\t\t__avKeywords.some((word) => word.length > 0 && __avText.includes(word));\n\t\tconst __avRegex = /\\b(?:pin|message)\\b/i;\n\t\tconst __avRegexOk = __avRegex.test(__avText);\n\t\tconst __avSource = String(message?.content?.source ?? \"\");\n\t\tconst __avExpectedSource = \"\";\n\t\tconst __avSourceOk = __avExpectedSource\n\t\t\t? __avSource === __avExpectedSource\n\t\t\t: Boolean(\n\t\t\t\t\t__avSource ||\n\t\t\t\t\t\tstate ||\n\t\t\t\t\t\truntime?.agentId ||\n\t\t\t\t\t\truntime?.getService ||\n\t\t\t\t\t\truntime?.getSetting,\n\t\t\t\t);\n\t\tconst __avOptions = options && typeof options === \"object\" ? options : {};\n\t\tconst __avInputOk =\n\t\t\t__avText.trim().length > 0 ||\n\t\t\tObject.keys(__avOptions as Record<string, unknown>).length > 0 ||\n\t\t\tBoolean(message?.content && typeof message.content === \"object\");\n\n\t\tif (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst __avLegacyValidate = async (\n\t\t\t_runtime: IAgentRuntime,\n\t\t\tmessage: Memory,\n\t\t\t_state?: State,\n\t\t): Promise<boolean> => {\n\t\t\treturn message.content.source === \"discord\";\n\t\t};\n\t\ttry {\n\t\t\treturn Boolean(\n\t\t\t\tawait (__avLegacyValidate as any)(runtime, message, state, options),\n\t\t\t);\n\t\t} catch {\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?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tconst discordService = runtime.getService(\n\t\t\tDISCORD_SERVICE_NAME,\n\t\t) as DiscordService;\n\n\t\tif (!discordService?.client) {\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"Discord service is not available.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst messageInfo = await getMessageRef(runtime, message, state);\n\t\tif (!messageInfo) {\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"I couldn't understand which message you want to pin. Please be more specific.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst stateData = state.data;\n\t\t\tconst room = stateData?.room || (await runtime.getRoom(message.roomId));\n\t\t\tif (!room?.channelId) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't determine the current channel.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst channel = await discordService.client.channels.fetch(\n\t\t\t\troom.channelId,\n\t\t\t);\n\t\t\tif (!channel?.isTextBased()) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I can only pin messages in text channels.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst textChannel = channel as TextChannel;\n\n\t\t\t// Check bot permissions\n\t\t\tconst textChannelGuild = textChannel.guild;\n\t\t\tconst discordServiceClient = discordService.client;\n\t\t\tconst discordServiceClientUser = discordServiceClient?.user;\n\t\t\tconst botMember = textChannelGuild?.members.cache.get(\n\t\t\t\tdiscordServiceClientUser?.id,\n\t\t\t);\n\t\t\tif (botMember) {\n\t\t\t\tconst permissions = textChannel.permissionsFor(botMember);\n\t\t\t\tif (!permissions?.has(PermissionsBitField.Flags.ManageMessages)) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: \"I don't have permission to pin messages in this channel. I need the 'Manage Messages' permission.\",\n\t\t\t\t\t\tsource: \"discord\",\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\tlet targetMessage: Message | null = null;\n\n\t\t\t// Find the target message\n\t\t\tif (\n\t\t\t\tmessageInfo.messageRef === \"last\" ||\n\t\t\t\tmessageInfo.messageRef === \"previous\"\n\t\t\t) {\n\t\t\t\t// Get the last few messages - fetch max allowed by Discord API\n\t\t\t\tconst messages = await textChannel.messages.fetch({ limit: 100 });\n\t\t\t\tconst sortedMessages = Array.from(messages.values()).sort(\n\t\t\t\t\t(a, b) => b.createdTimestamp - a.createdTimestamp,\n\t\t\t\t);\n\n\t\t\t\t// Skip the bot's own message and the command message\n\t\t\t\tconst discordServiceClient = discordService.client;\n\t\t\t\tconst discordServiceClientUser = discordServiceClient?.user;\n\t\t\t\ttargetMessage =\n\t\t\t\t\tsortedMessages.find(\n\t\t\t\t\t\t(msg) =>\n\t\t\t\t\t\t\tmsg.id !== message.content.id &&\n\t\t\t\t\t\t\tmsg.author.id !== discordServiceClientUser?.id,\n\t\t\t\t\t) || null;\n\t\t\t} else if (/^\\d+$/.test(messageInfo.messageRef)) {\n\t\t\t\t// It's a message ID\n\t\t\t\ttry {\n\t\t\t\t\ttargetMessage = await textChannel.messages.fetch(\n\t\t\t\t\t\tmessageInfo.messageRef,\n\t\t\t\t\t);\n\t\t\t\t} catch (_e) {\n\t\t\t\t\t// Message not found\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Search for message by content/author - fetch max allowed by Discord API\n\t\t\t\tconst messages = await textChannel.messages.fetch({ limit: 100 });\n\t\t\t\tconst searchLower = messageInfo.messageRef.toLowerCase();\n\n\t\t\t\ttargetMessage =\n\t\t\t\t\tArray.from(messages.values()).find((msg) => {\n\t\t\t\t\t\tconst contentMatch = msg.content\n\t\t\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t\t\t.includes(searchLower);\n\t\t\t\t\t\tconst authorMatch = msg.author.username\n\t\t\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t\t\t.includes(searchLower);\n\t\t\t\t\t\treturn contentMatch || authorMatch;\n\t\t\t\t\t}) || null;\n\t\t\t}\n\n\t\t\tif (!targetMessage) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't find the message you want to pin. Try being more specific or use 'last message'.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Check if already pinned\n\t\t\tif (targetMessage.pinned) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"That message is already pinned.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Pin the message\n\t\t\ttry {\n\t\t\t\tawait targetMessage.pin();\n\n\t\t\t\tconst response: Content = {\n\t\t\t\t\ttext: `I've pinned the message from ${targetMessage.author.username}.`,\n\t\t\t\t\tsource: message.content.source,\n\t\t\t\t};\n\n\t\t\t\tawait callback?.(response);\n\t\t\t} catch (error) {\n\t\t\t\truntime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:action:pin-message\",\n\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t},\n\t\t\t\t\t\"Failed to pin message\",\n\t\t\t\t);\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't pin that message. The channel might have reached the maximum number of pinned messages (50).\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\truntime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:pin-message\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error pinning message\",\n\t\t\t);\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"I encountered an error while trying to pin the message. Please make sure I have the necessary permissions.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t}\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default pinMessage;\n",
|
|
21
|
+
"import {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\ttype Content,\n\tcomposePromptFromState,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype State,\n} from \"@elizaos/core\";\nimport type { Message, TextChannel } from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport { reactToMessageTemplate } from \"../generated/prompts/typescript/prompts.js\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\nimport type { DiscordService } from \"../service\";\nimport { terminalActionInteractionSemantics } from \"./actionResultSemantics\";\n\n/**\n * Extracts emoji tokens from a string in the order they appear.\n *\n * Captures standard Unicode emoji sequences (including multi-codepoint sequences joined by zero-width joiners) and Discord custom emoji tokens in the form `<:name:id>` or `<a:name:id>`.\n *\n * @param text - The input text to scan for emojis\n * @returns An array of emoji strings found in `text`, in the order they appear (empty if none)\n */\nfunction extractEmojisFromText(text: string): string[] {\n\tif (!text) {\n\t\treturn [];\n\t}\n\n\t// Collect all emoji matches with their positions to preserve order\n\tconst matches: { index: number; emoji: string }[] = [];\n\n\t// Match Unicode emojis (including multi-codepoint sequences)\n\tconst unicodeEmojiRegex =\n\t\t/(?:\\p{Emoji_Presentation}|\\p{Extended_Pictographic})(?:\\uFE0F)?(?:\\u200D(?:\\p{Emoji_Presentation}|\\p{Extended_Pictographic})(?:\\uFE0F)?)*/gu;\n\tlet match: RegExpExecArray | null = null;\n\tmatch = unicodeEmojiRegex.exec(text);\n\twhile (match !== null) {\n\t\tmatches.push({ index: match.index, emoji: match[0] });\n\t\tmatch = unicodeEmojiRegex.exec(text);\n\t}\n\n\t// Match Discord custom emojis <:name:id> or <a:name:id>\n\tconst customEmojiRegex = /<a?:\\w+:\\d+>/g;\n\tmatch = customEmojiRegex.exec(text);\n\twhile (match !== null) {\n\t\tmatches.push({ index: match.index, emoji: match[0] });\n\t\tmatch = customEmojiRegex.exec(text);\n\t}\n\n\t// Sort by position and return just the emojis\n\treturn matches.sort((a, b) => a.index - b.index).map((m) => m.emoji);\n}\n\n/**\n * Determines whether a message explicitly requests adding a reaction or specifies a target.\n *\n * @param text - The message text to inspect for reaction-related keywords or target patterns\n * @returns `true` if the text contains reaction keywords OR specifies a message target; `false` otherwise.\n */\nfunction isExplicitReactionRequest(text: string): boolean {\n\tif (!text) {\n\t\treturn false;\n\t}\n\tconst lower = text.toLowerCase();\n\n\t// Keywords indicating user explicitly requested a reaction\n\tif (/\\b(react|reaction|emoji)\\b/.test(lower)) {\n\t\treturn true;\n\t}\n\n\t// Patterns indicating a specific message target (e.g., \"add thumbs up to john's message\")\n\t// These require LLM to extract the correct messageRef\n\tif (/\\w+'s\\s+message\\b/.test(lower)) {\n\t\treturn true;\n\t} // \"john's message\"\n\tif (/message\\s+(about|from|where)\\b/.test(lower)) {\n\t\treturn true;\n\t} // \"message about X\"\n\tif (/\\bto\\s+\\w+'s\\b/.test(lower)) {\n\t\treturn true;\n\t} // \"to john's\"\n\tif (/\\bthat\\s+message\\b/.test(lower)) {\n\t\treturn true;\n\t} // \"that message\"\n\n\treturn false;\n}\n\n// Common Discord emoji mappings\nconst emojiMap: Record<string, string> = {\n\t\":thumbsup:\": \"👍\",\n\t\":thumbs_up:\": \"👍\",\n\t\":+1:\": \"👍\",\n\t\":thumbsdown:\": \"👎\",\n\t\":thumbs_down:\": \"👎\",\n\t\":-1:\": \"👎\",\n\t\":heart:\": \"❤️\",\n\t\":fire:\": \"🔥\",\n\t\":star:\": \"⭐\",\n\t\":check:\": \"✅\",\n\t\":white_check_mark:\": \"✅\",\n\t\":x:\": \"❌\",\n\t\":cross:\": \"❌\",\n\t\":smile:\": \"😄\",\n\t\":laughing:\": \"😆\",\n\t\":thinking:\": \"🤔\",\n\t\":eyes:\": \"👀\",\n\t\":clap:\": \"👏\",\n\t\":wave:\": \"👋\",\n\t\":ok:\": \"👌\",\n\t\":ok_hand:\": \"👌\",\n\t\":raised_hands:\": \"🙌\",\n\t\":pray:\": \"🙏\",\n\t\":100:\": \"💯\",\n\t\":rocket:\": \"🚀\",\n};\n\nconst spec = requireActionSpec(\"REACT_TO_MESSAGE\");\n\nexport const reactToMessage: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\t...terminalActionInteractionSemantics,\n\tvalidate: async (\n\t\truntime: any,\n\t\tmessage: any,\n\t\tstate?: any,\n\t\toptions?: any,\n\t): Promise<boolean> => {\n\t\tconst __avTextRaw =\n\t\t\ttypeof message?.content?.text === \"string\" ? message.content.text : \"\";\n\t\tconst __avText = __avTextRaw.toLowerCase();\n\t\tconst __avKeywords = [\"react\", \"message\"];\n\t\tconst __avKeywordOk =\n\t\t\t__avKeywords.length > 0 &&\n\t\t\t__avKeywords.some((word) => word.length > 0 && __avText.includes(word));\n\t\tconst __avRegex = /\\b(?:react|message)\\b/i;\n\t\tconst __avRegexOk = __avRegex.test(__avText);\n\t\tconst __avSource = String(message?.content?.source ?? \"\");\n\t\tconst __avExpectedSource = \"\";\n\t\tconst __avSourceOk = __avExpectedSource\n\t\t\t? __avSource === __avExpectedSource\n\t\t\t: Boolean(\n\t\t\t\t\t__avSource ||\n\t\t\t\t\t\tstate ||\n\t\t\t\t\t\truntime?.agentId ||\n\t\t\t\t\t\truntime?.getService ||\n\t\t\t\t\t\truntime?.getSetting,\n\t\t\t\t);\n\t\tconst __avOptions = options && typeof options === \"object\" ? options : {};\n\t\tconst __avInputOk =\n\t\t\t__avText.trim().length > 0 ||\n\t\t\tObject.keys(__avOptions as Record<string, unknown>).length > 0 ||\n\t\t\tBoolean(message?.content && typeof message.content === \"object\");\n\n\t\tif (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst __avLegacyValidate = async (\n\t\t\t_runtime: IAgentRuntime,\n\t\t\tmessage: Memory,\n\t\t\t_state?: State,\n\t\t): Promise<boolean> => {\n\t\t\treturn message.content.source === \"discord\";\n\t\t};\n\t\ttry {\n\t\t\treturn Boolean(\n\t\t\t\tawait (__avLegacyValidate as any)(runtime, message, state, options),\n\t\t\t);\n\t\t} catch {\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?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tconst discordService = runtime.getService(\n\t\t\tDISCORD_SERVICE_NAME,\n\t\t) as DiscordService;\n\n\t\tif (!discordService?.client) {\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"Discord service is not available.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// ============================================================================\n\t\t// Extract reaction info - use fast path when appropriate, LLM otherwise\n\t\t// ============================================================================\n\t\tlet reactionInfo: { messageRef: string; emoji: string } | null = null;\n\n\t\t// Check if user explicitly requested a reaction (needs LLM for accuracy)\n\t\t// vs agent spontaneously reacting (fast path to \"last message\" is correct)\n\t\tconst messageContent = message.content;\n\t\tconst userText = messageContent?.text || \"\";\n\t\tconst needsLLM = isExplicitReactionRequest(userText);\n\n\t\tif (!needsLLM) {\n\t\t\t// FAST PATH: Agent spontaneously reacting - target is always \"last message\"\n\t\t\tconst stateData = state.data as Record<string, unknown> | undefined;\n\t\t\tconst stateWithResponseText = state as State & { responseText?: string };\n\t\t\tconst responseText = String(\n\t\t\t\tstateData?.responseText ||\n\t\t\t\t\tstateData?.text ||\n\t\t\t\t\tstateWithResponseText.responseText ||\n\t\t\t\t\t\"\",\n\t\t\t);\n\n\t\t\tif (responseText) {\n\t\t\t\tconst emojis = extractEmojisFromText(responseText);\n\t\t\t\tif (emojis.length > 0) {\n\t\t\t\t\truntime.logger.debug(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord:action:react\",\n\t\t\t\t\t\t\temoji: emojis[0],\n\t\t\t\t\t\t\tsource: \"responseText\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"[REACT_TO_MESSAGE] Found emoji in response text (fast path)\",\n\t\t\t\t\t);\n\t\t\t\t\treactionInfo = { messageRef: \"last\", emoji: emojis[0] };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!reactionInfo) {\n\t\t\t\t// Check recent messages for this agent's last message\n\t\t\t\tconst stateData = state.data;\n\t\t\t\tconst recentMessages = (stateData?.recentMessages || []) as Memory[];\n\t\t\t\tconst agentLastMessage = recentMessages\n\t\t\t\t\t.filter((m) => m.entityId === runtime.agentId)\n\t\t\t\t\t.pop();\n\n\t\t\t\tconst agentLastMessageContent = agentLastMessage?.content;\n\t\t\t\tif (agentLastMessageContent?.text) {\n\t\t\t\t\tconst emojis = extractEmojisFromText(agentLastMessageContent.text);\n\t\t\t\t\tif (emojis.length > 0) {\n\t\t\t\t\t\truntime.logger.debug(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsrc: \"plugin:discord:action:react\",\n\t\t\t\t\t\t\t\temoji: emojis[0],\n\t\t\t\t\t\t\t\tsource: \"agentLastMessage\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"[REACT_TO_MESSAGE] Found emoji in agent's last message (fast path)\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treactionInfo = { messageRef: \"last\", emoji: emojis[0] };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!reactionInfo) {\n\t\t\t// LLM PATH: Use when fast path fails or user specified a specific target\n\t\t\tconst prompt = composePromptFromState({\n\t\t\t\tstate,\n\t\t\t\ttemplate: reactToMessageTemplate,\n\t\t\t});\n\n\t\t\tfor (let i = 0; i < 3; i++) {\n\t\t\t\tconst response = await runtime.useModel(ModelType.TEXT_SMALL, {\n\t\t\t\t\tprompt,\n\t\t\t\t});\n\n\t\t\t\tconst parsedResponse = parseJSONObjectFromText(response);\n\t\t\t\tif (parsedResponse?.emoji) {\n\t\t\t\t\treactionInfo = {\n\t\t\t\t\t\tmessageRef: String(parsedResponse.messageRef || \"last\"),\n\t\t\t\t\t\temoji: String(parsedResponse.emoji),\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!reactionInfo) {\n\t\t\truntime.logger.debug(\n\t\t\t\t{ src: \"plugin:discord:action:react\" },\n\t\t\t\t\"[REACT_TO_MESSAGE] Could not extract reaction info\",\n\t\t\t);\n\t\t\t// Only show error to user if they explicitly requested a reaction\n\t\t\t// Silent failure is appropriate when agent spontaneously decides to react\n\t\t\tif (needsLLM) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't understand which message to react to or what emoji to use. Try being more specific, like 'react with 👍 to the last message'.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst stateData = state.data;\n\t\t\tconst room = stateData?.room || (await runtime.getRoom(message.roomId));\n\t\t\tif (!room?.channelId) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't determine the current channel.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst channel = await discordService.client.channels.fetch(\n\t\t\t\troom.channelId,\n\t\t\t);\n\t\t\tif (!channel?.isTextBased()) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I can only react to messages in text channels.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst textChannel = channel as TextChannel;\n\n\t\t\tlet targetMessage: Message | null = null;\n\n\t\t\t// Find the target message\n\t\t\tif (\n\t\t\t\treactionInfo.messageRef === \"last\" ||\n\t\t\t\treactionInfo.messageRef === \"previous\"\n\t\t\t) {\n\t\t\t\t// Get the last few messages - fetch max allowed by Discord API\n\t\t\t\tconst messages = await textChannel.messages.fetch({ limit: 100 });\n\t\t\t\tconst sortedMessages = Array.from(messages.values()).sort(\n\t\t\t\t\t(a, b) => b.createdTimestamp - a.createdTimestamp,\n\t\t\t\t);\n\n\t\t\t\t// Skip the bot's own message and the command message\n\t\t\t\tconst clientUser = discordService.client.user;\n\t\t\t\ttargetMessage =\n\t\t\t\t\tsortedMessages.find(\n\t\t\t\t\t\t(msg) =>\n\t\t\t\t\t\t\tmsg.id !== message.content.id && msg.author.id !== clientUser?.id,\n\t\t\t\t\t) || null;\n\t\t\t} else if (/^\\d+$/.test(reactionInfo.messageRef)) {\n\t\t\t\t// It's a message ID\n\t\t\t\ttry {\n\t\t\t\t\ttargetMessage = await textChannel.messages.fetch(\n\t\t\t\t\t\treactionInfo.messageRef,\n\t\t\t\t\t);\n\t\t\t\t} catch (_e) {\n\t\t\t\t\t// Message not found\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Search for message by content/author - fetch max allowed by Discord API\n\t\t\t\tconst messages = await textChannel.messages.fetch({ limit: 100 });\n\t\t\t\tconst searchLower = reactionInfo.messageRef.toLowerCase();\n\n\t\t\t\ttargetMessage =\n\t\t\t\t\tArray.from(messages.values()).find((msg) => {\n\t\t\t\t\t\tconst contentMatch = msg.content\n\t\t\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t\t\t.includes(searchLower);\n\t\t\t\t\t\tconst authorMatch = msg.author.username\n\t\t\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t\t\t.includes(searchLower);\n\t\t\t\t\t\treturn contentMatch || authorMatch;\n\t\t\t\t\t}) || null;\n\t\t\t}\n\n\t\t\tif (!targetMessage) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't find the message you want me to react to. Try being more specific or use 'last message'.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Normalize the emoji\n\t\t\tlet emoji = reactionInfo.emoji;\n\t\t\tif (!/\\p{Emoji}/u.test(emoji)) {\n\t\t\t\tconst mapped = emojiMap[emoji.toLowerCase()];\n\t\t\t\tif (mapped) {\n\t\t\t\t\temoji = mapped;\n\t\t\t\t} else if (!/<a?:\\w+:\\d+>/.test(emoji)) {\n\t\t\t\t\t// Not a custom emoji, remove colons\n\t\t\t\t\temoji = emoji.replace(/:/g, \"\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add the reaction\n\t\t\ttry {\n\t\t\t\tawait targetMessage.react(emoji);\n\n\t\t\t\tconst response: Content = {\n\t\t\t\t\ttext: `I've added a ${emoji} reaction to the message.`,\n\t\t\t\t\tsource: message.content.source,\n\t\t\t\t};\n\n\t\t\t\tawait callback?.(response);\n\t\t\t} catch (error) {\n\t\t\t\truntime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:action:react-to-message\",\n\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t\temoji: reactionInfo.emoji,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t},\n\t\t\t\t\t\"Failed to add reaction\",\n\t\t\t\t);\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: `I couldn't add that reaction. Make sure the emoji \"${reactionInfo.emoji}\" is valid and I have permission to add reactions.`,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\truntime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:react-to-message\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error in react to message\",\n\t\t\t);\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"I encountered an error while trying to react to the message. Please make sure I have the necessary permissions.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t}\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default reactToMessage;\n",
|
|
22
|
+
"import {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\ttype Content,\n\tcomposePromptFromState,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype State,\n} from \"@elizaos/core\";\nimport { PermissionsBitField, type TextChannel } from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport { channelInfoTemplate } from \"../generated/prompts/typescript/prompts.js\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\nimport type { DiscordService } from \"../service\";\n\n/**\n * Get channel information from the user's request\n * @param {IAgentRuntime} runtime - The runtime object to interact with the agent.\n * @param {Memory} _message - The memory object containing the input message.\n * @param {State} state - The state of the conversation.\n * @returns {Promise<{channelIdentifier: string, messageCount: number} | null>} Channel info or null if not parseable.\n */\nconst getChannelInfo = async (\n\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n): Promise<{\n\tchannelIdentifier: string;\n\tmessageCount: number;\n\tsummarize: boolean;\n\tfocusUser: string | null;\n} | null> => {\n\tconst prompt = composePromptFromState({\n\t\tstate,\n\t\ttemplate: channelInfoTemplate,\n\t});\n\n\tfor (let i = 0; i < 3; i++) {\n\t\tconst response = await runtime.useModel(ModelType.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\n\t\tconst parsedResponse = parseJSONObjectFromText(response) as {\n\t\t\tchannelIdentifier: string;\n\t\t\tmessageCount: number;\n\t\t\tsummarize?: boolean;\n\t\t\tfocusUser?: string | null;\n\t\t} | null;\n\n\t\tif (parsedResponse?.channelIdentifier) {\n\t\t\t// Ensure messageCount is within bounds\n\t\t\tconst messageCount = Math.min(\n\t\t\t\tMath.max(parsedResponse.messageCount || 10, 1),\n\t\t\t\t50,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tchannelIdentifier: parsedResponse.channelIdentifier,\n\t\t\t\tmessageCount,\n\t\t\t\tsummarize: parsedResponse.summarize || false,\n\t\t\t\tfocusUser: parsedResponse.focusUser || null,\n\t\t\t};\n\t\t}\n\t}\n\treturn null;\n};\n\nconst fallbackSpec = {\n\tname: \"READ_CHANNEL\",\n\tdescription: \"Read recent messages from a Discord channel.\",\n\tdescriptionCompressed: \"Read recent messages from Discord channel.\",\n\tsimiles: [\"SHOW_MESSAGES\", \"CHECK_CHANNEL\"],\n\texamples: [] as ActionExample[][],\n};\n\nconst spec = (() => {\n\ttry {\n\t\treturn requireActionSpec(\"READ_CHANNEL\");\n\t} catch {\n\t\treturn fallbackSpec;\n\t}\n})();\n\nexport const readChannel: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\tvalidate: async (\n\t\truntime: any,\n\t\tmessage: any,\n\t\tstate?: any,\n\t\toptions?: any,\n\t): Promise<boolean> => {\n\t\tconst __avTextRaw =\n\t\t\ttypeof message?.content?.text === \"string\" ? message.content.text : \"\";\n\t\tconst __avText = __avTextRaw.toLowerCase();\n\t\tconst __avKeywords = [\"read\", \"channel\"];\n\t\tconst __avKeywordOk =\n\t\t\t__avKeywords.length > 0 &&\n\t\t\t__avKeywords.some((word) => word.length > 0 && __avText.includes(word));\n\t\tconst __avRegex = /\\b(?:read|channel)\\b/i;\n\t\tconst __avRegexOk = __avRegex.test(__avText);\n\t\tconst __avSource = String(message?.content?.source ?? \"\");\n\t\tconst __avExpectedSource = \"\";\n\t\tconst __avSourceOk = __avExpectedSource\n\t\t\t? __avSource === __avExpectedSource\n\t\t\t: Boolean(\n\t\t\t\t\t__avSource ||\n\t\t\t\t\t\tstate ||\n\t\t\t\t\t\truntime?.agentId ||\n\t\t\t\t\t\truntime?.getService ||\n\t\t\t\t\t\truntime?.getSetting,\n\t\t\t\t);\n\t\tconst __avOptions = options && typeof options === \"object\" ? options : {};\n\t\tconst __avInputOk =\n\t\t\t__avText.trim().length > 0 ||\n\t\t\tObject.keys(__avOptions as Record<string, unknown>).length > 0 ||\n\t\t\tBoolean(message?.content && typeof message.content === \"object\");\n\n\t\tif (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst __avLegacyValidate = async (\n\t\t\t_runtime: IAgentRuntime,\n\t\t\tmessage: Memory,\n\t\t\t_state?: State,\n\t\t): Promise<boolean> => {\n\t\t\treturn message.content.source === \"discord\";\n\t\t};\n\t\ttry {\n\t\t\treturn Boolean(\n\t\t\t\tawait (__avLegacyValidate as any)(runtime, message, state, options),\n\t\t\t);\n\t\t} catch {\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?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tconst discordService = runtime.getService(\n\t\t\tDISCORD_SERVICE_NAME,\n\t\t) as DiscordService;\n\n\t\tif (!discordService?.client) {\n\t\t\truntime.logger.error(\n\t\t\t\t{ src: \"plugin:discord:action:read-channel\", agentId: runtime.agentId },\n\t\t\t\t\"Discord service not found or not initialized\",\n\t\t\t);\n\t\t\treturn { success: false, error: \"Discord service not available\" };\n\t\t}\n\n\t\tif (!state) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"State is not available.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"State is not available\" };\n\t\t}\n\n\t\tconst channelInfo = await getChannelInfo(runtime, message, state);\n\t\tif (!channelInfo) {\n\t\t\truntime.logger.warn(\n\t\t\t\t{ src: \"plugin:discord:action:read-channel\", agentId: runtime.agentId },\n\t\t\t\t\"Could not parse channel information from message\",\n\t\t\t);\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't understand which channel you want me to read from. Please specify the channel name or say 'this channel' for the current channel.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"Could not parse channel information\" };\n\t\t}\n\n\t\ttry {\n\t\t\tlet targetChannel: TextChannel | null = null;\n\t\t\tconst stateData = state.data;\n\t\t\tconst room = stateData?.room || (await runtime.getRoom(message.roomId));\n\n\t\t\t// Determine the target channel\n\t\t\tif (\n\t\t\t\tchannelInfo.channelIdentifier === \"current\" ||\n\t\t\t\tchannelInfo.channelIdentifier === \"this\" ||\n\t\t\t\tchannelInfo.channelIdentifier === \"here\"\n\t\t\t) {\n\t\t\t\t// Use current channel\n\t\t\t\tif (room?.channelId) {\n\t\t\t\t\ttargetChannel = (await discordService.client.channels.fetch(\n\t\t\t\t\t\troom.channelId,\n\t\t\t\t\t)) as TextChannel;\n\t\t\t\t}\n\t\t\t} else if (channelInfo.channelIdentifier.match(/^\\d+$/)) {\n\t\t\t\t// It's a channel ID\n\t\t\t\ttargetChannel = (await discordService.client.channels.fetch(\n\t\t\t\t\tchannelInfo.channelIdentifier,\n\t\t\t\t)) as TextChannel;\n\t\t\t} else {\n\t\t\t\t// It's a channel name - search in the current server\n\t\t\t\tconst serverId = room?.messageServerId;\n\t\t\t\tif (!serverId) {\n\t\t\t\t\tif (callback) {\n\t\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\t\ttext: \"I couldn't determine which server to search for that channel.\",\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn { success: false, error: \"Could not determine server\" };\n\t\t\t\t}\n\t\t\t\tconst guild = await discordService.client.guilds.fetch(serverId);\n\t\t\t\tconst channels = await guild.channels.fetch();\n\n\t\t\t\ttargetChannel =\n\t\t\t\t\t(channels.find(\n\t\t\t\t\t\t(channel) =>\n\t\t\t\t\t\t\tchannel?.name\n\t\t\t\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t\t\t\t.includes(channelInfo.channelIdentifier.toLowerCase()) &&\n\t\t\t\t\t\t\tchannel.isTextBased(),\n\t\t\t\t\t) as TextChannel | undefined) || null;\n\t\t\t}\n\n\t\t\tif (!targetChannel?.isTextBased()) {\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: \"I couldn't find that channel or I don't have access to it. Make sure the channel exists and I have permission to read messages there.\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn { success: false, error: \"Channel not found or not accessible\" };\n\t\t\t}\n\n\t\t\t// Check permissions\n\t\t\tconst targetChannelGuild = targetChannel.guild;\n\t\t\tconst clientUser = discordService.client.user;\n\t\t\tconst botMember = targetChannelGuild?.members.cache.get(clientUser?.id);\n\t\t\tif (botMember) {\n\t\t\t\tconst permissions = targetChannel.permissionsFor(botMember);\n\t\t\t\tif (!permissions?.has(PermissionsBitField.Flags.ReadMessageHistory)) {\n\t\t\t\t\tif (callback) {\n\t\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\t\ttext: \"I don't have permission to read message history in that channel.\",\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\terror: \"Missing ReadMessageHistory permission\",\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Fetch messages - get more for summarization to have better context\n\t\t\t// Discord API limits to 100 messages per fetch\n\t\t\tconst requestedLimit = channelInfo.summarize\n\t\t\t\t? Math.max(channelInfo.messageCount * 2, 50)\n\t\t\t\t: channelInfo.messageCount;\n\t\t\tconst fetchLimit = Math.min(requestedLimit, 100);\n\n\t\t\truntime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:read-channel\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\tchannelName: targetChannel.name,\n\t\t\t\t\tfetchLimit,\n\t\t\t\t\trequestedLimit,\n\t\t\t\t\tsummarize: channelInfo.summarize,\n\t\t\t\t\tfocusUser: channelInfo.focusUser,\n\t\t\t\t},\n\t\t\t\t\"Fetching messages\",\n\t\t\t);\n\n\t\t\tconst messages = await targetChannel.messages.fetch({\n\t\t\t\tlimit: fetchLimit,\n\t\t\t});\n\n\t\t\tif (messages.size === 0) {\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: `No messages found in <#${targetChannel.id}>.`,\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn { success: true, text: `No messages found in channel` };\n\t\t\t}\n\n\t\t\t// If summarization is requested\n\t\t\tif (channelInfo.summarize) {\n\t\t\t\tconst sortedMessages = Array.from(messages.values()).reverse();\n\n\t\t\t\t// Filter by user if specified\n\t\t\t\tconst relevantMessages = channelInfo.focusUser\n\t\t\t\t\t? sortedMessages.filter((msg) => {\n\t\t\t\t\t\t\tconst focusUserLower = channelInfo.focusUser?.toLowerCase();\n\t\t\t\t\t\t\tconst msgMember = msg.member;\n\t\t\t\t\t\t\tconst msgMemberDisplayName = msgMember?.displayName;\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\tmsg.author.username\n\t\t\t\t\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t\t\t\t\t.includes(focusUserLower || \"\") ||\n\t\t\t\t\t\t\t\tmsgMemberDisplayName\n\t\t\t\t\t\t\t\t\t?.toLowerCase()\n\t\t\t\t\t\t\t\t\t.includes(focusUserLower || \"\")\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})\n\t\t\t\t\t: sortedMessages;\n\n\t\t\t\tif (channelInfo.focusUser && relevantMessages.length === 0) {\n\t\t\t\t\tif (callback) {\n\t\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\t\ttext: `I couldn't find any messages from \"${channelInfo.focusUser}\" in the recent messages from <#${targetChannel.id}>.`,\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\ttext: `No messages found from ${channelInfo.focusUser}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// Prepare messages for summarization\n\t\t\t\tconst messagesToSummarize = relevantMessages\n\t\t\t\t\t.slice(0, channelInfo.messageCount)\n\t\t\t\t\t.map((msg) => ({\n\t\t\t\t\t\tauthor: msg.author.username,\n\t\t\t\t\t\tcontent: msg.content || \"[No text content]\",\n\t\t\t\t\t\ttimestamp: new Date(msg.createdTimestamp).toLocaleString(),\n\t\t\t\t\t}));\n\n\t\t\t\t// Create a summary prompt\n\t\t\t\tconst summaryPrompt = channelInfo.focusUser\n\t\t\t\t\t? `Please summarize what ${channelInfo.focusUser} has been discussing based on these messages from the Discord channel \"${targetChannel.name}\":\\n\\n${messagesToSummarize\n\t\t\t\t\t\t\t.map((m) => `${m.author} (${m.timestamp}): ${m.content}`)\n\t\t\t\t\t\t\t.join(\n\t\t\t\t\t\t\t\t\"\\n\\n\",\n\t\t\t\t\t\t\t)}\\n\\nProvide a concise summary focusing on:\\n1. Main topics ${channelInfo.focusUser} discussed\\n2. Key points or proposals they made\\n3. Any questions they asked or issues they raised\\n\\nIf ${channelInfo.focusUser} didn't appear in these messages, please note that.`\n\t\t\t\t\t: `Please summarize the recent conversation in the Discord channel \"${targetChannel.name}\" based on these messages:\\n\\n${messagesToSummarize\n\t\t\t\t\t\t\t.map((m) => `${m.author} (${m.timestamp}): ${m.content}`)\n\t\t\t\t\t\t\t.join(\n\t\t\t\t\t\t\t\t\"\\n\\n\",\n\t\t\t\t\t\t\t)}\\n\\nProvide a concise summary that includes:\\n1. Main topics discussed\\n2. Key decisions or conclusions\\n3. Who contributed what (mention specific usernames)\\n4. Any action items or next steps mentioned`;\n\n\t\t\t\tconst summary = await runtime.useModel(ModelType.TEXT_LARGE, {\n\t\t\t\t\tprompt: summaryPrompt,\n\t\t\t\t});\n\n\t\t\t\tconst response: Content = {\n\t\t\t\t\ttext: channelInfo.focusUser\n\t\t\t\t\t\t? `Summary of what ${channelInfo.focusUser} has been discussing in <#${targetChannel.id}>:\\n\\n${summary}`\n\t\t\t\t\t\t: `Summary of recent conversation in <#${targetChannel.id}>:\\n\\n${summary}`,\n\t\t\t\t\tactions: [\"READ_CHANNEL_RESPONSE\"],\n\t\t\t\t\tsource: message.content.source,\n\t\t\t\t};\n\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.(response);\n\t\t\t\t}\n\t\t\t\treturn { success: true, text: response.text };\n\t\t\t} else {\n\t\t\t\t// Format messages for display (original behavior)\n\t\t\t\tconst formattedMessages = Array.from(messages.values())\n\t\t\t\t\t.reverse() // Show oldest first\n\t\t\t\t\t.map((msg) => {\n\t\t\t\t\t\tconst timestamp = new Date(msg.createdTimestamp).toLocaleString();\n\t\t\t\t\t\tconst author = msg.author.username;\n\t\t\t\t\t\tconst content = msg.content || \"[No text content]\";\n\t\t\t\t\t\tconst attachments =\n\t\t\t\t\t\t\tmsg.attachments.size > 0\n\t\t\t\t\t\t\t\t? `\\n📎 Attachments: ${msg.attachments.map((a) => a.name || \"unnamed\").join(\", \")}`\n\t\t\t\t\t\t\t\t: \"\";\n\n\t\t\t\t\t\treturn `**${author}** (${timestamp}):\\n${content}${attachments}`;\n\t\t\t\t\t})\n\t\t\t\t\t.join(\"\\n\\n---\\n\\n\");\n\n\t\t\t\tconst response: Content = {\n\t\t\t\t\ttext: `Here are the last ${messages.size} messages from <#${targetChannel.id}>:\\n\\n${formattedMessages}`,\n\t\t\t\t\tactions: [\"READ_CHANNEL_RESPONSE\"],\n\t\t\t\t\tsource: message.content.source,\n\t\t\t\t};\n\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.(response);\n\t\t\t\t}\n\t\t\t\treturn { success: true, text: response.text };\n\t\t\t}\n\t\t} catch (error) {\n\t\t\truntime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:read-channel\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error reading channel\",\n\t\t\t);\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I encountered an error while trying to read the channel messages. Please make sure I have the necessary permissions and try again.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default readChannel;\n",
|
|
23
|
+
"import {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\ttype Content,\n\tcomposePromptFromState,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype State,\n} from \"@elizaos/core\";\nimport type { Collection, Message, TextChannel } from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport { searchMessagesTemplate } from \"../generated/prompts/typescript/prompts.js\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\nimport type { DiscordService } from \"../service\";\n\nconst getSearchParams = async (\n\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n): Promise<{\n\tquery: string;\n\tchannelIdentifier: string;\n\tauthor: string | null;\n\ttimeRange: string | null;\n\tlimit: number;\n} | null> => {\n\tconst prompt = composePromptFromState({\n\t\tstate,\n\t\ttemplate: searchMessagesTemplate,\n\t});\n\n\tfor (let i = 0; i < 3; i++) {\n\t\tconst response = await runtime.useModel(ModelType.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\n\t\tconst parsedResponse = parseJSONObjectFromText(response);\n\t\tif (parsedResponse?.query) {\n\t\t\t// Remove quotes from query if present\n\t\t\tconst cleanQuery = String(parsedResponse.query).replace(\n\t\t\t\t/^[\"']|[\"']$/g,\n\t\t\t\t\"\",\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tquery: cleanQuery,\n\t\t\t\tchannelIdentifier: String(\n\t\t\t\t\tparsedResponse.channelIdentifier || \"current\",\n\t\t\t\t),\n\t\t\t\tauthor: parsedResponse.author ? String(parsedResponse.author) : null,\n\t\t\t\ttimeRange: parsedResponse.timeRange\n\t\t\t\t\t? String(parsedResponse.timeRange)\n\t\t\t\t\t: null,\n\t\t\t\tlimit: Math.min(Math.max(Number(parsedResponse.limit) || 20, 1), 100),\n\t\t\t};\n\t\t}\n\t}\n\treturn null;\n};\n\nconst searchInMessages = (\n\tmessages: Collection<string, Message>,\n\tquery: string,\n\tauthor?: string | null,\n): Message[] => {\n\tconst queryLower = query.toLowerCase().trim();\n\tconst isLinkSearch =\n\t\tqueryLower.includes(\"link\") || queryLower.includes(\"url\");\n\n\treturn Array.from(messages.values()).filter((msg) => {\n\t\t// Skip system messages\n\t\tif (msg.system) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Filter by author if specified\n\t\tif (author && author !== \"null\" && author !== \"undefined\") {\n\t\t\tconst authorLower = author.toLowerCase();\n\t\t\tconst matchesUsername = msg.author.username\n\t\t\t\t.toLowerCase()\n\t\t\t\t.includes(authorLower);\n\t\t\tconst matchesDisplayName =\n\t\t\t\tmsg.member?.displayName?.toLowerCase().includes(authorLower) || false;\n\t\t\tif (!matchesUsername && !matchesDisplayName) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// Special handling for link searches\n\t\tif (isLinkSearch) {\n\t\t\tconst urlRegex = /(https?:\\/\\/[^\\s]+)/g;\n\t\t\treturn urlRegex.test(msg.content);\n\t\t}\n\n\t\t// Search in message content (case-insensitive)\n\t\tconst contentMatch = msg.content.toLowerCase().includes(queryLower);\n\n\t\t// Search in embeds\n\t\tconst embedMatch = msg.embeds.some(\n\t\t\t(embed) =>\n\t\t\t\tembed.title?.toLowerCase().includes(queryLower) ||\n\t\t\t\tembed.description?.toLowerCase().includes(queryLower) ||\n\t\t\t\tembed.author?.name?.toLowerCase().includes(queryLower) ||\n\t\t\t\tembed.fields?.some(\n\t\t\t\t\t(field) =>\n\t\t\t\t\t\tfield.name?.toLowerCase().includes(queryLower) ||\n\t\t\t\t\t\tfield.value?.toLowerCase().includes(queryLower),\n\t\t\t\t),\n\t\t);\n\n\t\t// Search in attachments\n\t\tconst attachmentMatch = msg.attachments.some(\n\t\t\t(att) =>\n\t\t\t\tatt.name?.toLowerCase().includes(queryLower) ||\n\t\t\t\tatt.description?.toLowerCase().includes(queryLower),\n\t\t);\n\n\t\treturn contentMatch || embedMatch || attachmentMatch;\n\t});\n};\n\nconst fallbackSpec = {\n\tname: \"SEARCH_MESSAGES\",\n\tdescription: \"Search for messages in a Discord channel.\",\n\tdescriptionCompressed: \"Search messages in Discord channel.\",\n\tsimiles: [\"FIND_MESSAGES\", \"LOOKUP_MESSAGES\"],\n\texamples: [] as ActionExample[][],\n};\n\nconst spec = (() => {\n\ttry {\n\t\treturn requireActionSpec(\"SEARCH_MESSAGES\");\n\t} catch {\n\t\treturn fallbackSpec;\n\t}\n})();\n\nexport const searchMessages: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\tvalidate: async (\n\t\truntime: any,\n\t\tmessage: any,\n\t\tstate?: any,\n\t\toptions?: any,\n\t): Promise<boolean> => {\n\t\tconst __avTextRaw =\n\t\t\ttypeof message?.content?.text === \"string\" ? message.content.text : \"\";\n\t\tconst __avText = __avTextRaw.toLowerCase();\n\t\tconst __avKeywords = [\"search\", \"messages\"];\n\t\tconst __avKeywordOk =\n\t\t\t__avKeywords.length > 0 &&\n\t\t\t__avKeywords.some((word) => word.length > 0 && __avText.includes(word));\n\t\tconst __avRegex = /\\b(?:search|messages)\\b/i;\n\t\tconst __avRegexOk = __avRegex.test(__avText);\n\t\tconst __avSource = String(message?.content?.source ?? \"\");\n\t\tconst __avExpectedSource = \"\";\n\t\tconst __avSourceOk = __avExpectedSource\n\t\t\t? __avSource === __avExpectedSource\n\t\t\t: Boolean(\n\t\t\t\t\t__avSource ||\n\t\t\t\t\t\tstate ||\n\t\t\t\t\t\truntime?.agentId ||\n\t\t\t\t\t\truntime?.getService ||\n\t\t\t\t\t\truntime?.getSetting,\n\t\t\t\t);\n\t\tconst __avOptions = options && typeof options === \"object\" ? options : {};\n\t\tconst __avInputOk =\n\t\t\t__avText.trim().length > 0 ||\n\t\t\tObject.keys(__avOptions as Record<string, unknown>).length > 0 ||\n\t\t\tBoolean(message?.content && typeof message.content === \"object\");\n\n\t\tif (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst __avLegacyValidate = async (\n\t\t\t_runtime: IAgentRuntime,\n\t\t\tmessage: Memory,\n\t\t\t_state?: State,\n\t\t): Promise<boolean> => {\n\t\t\treturn message.content.source === \"discord\";\n\t\t};\n\t\ttry {\n\t\t\treturn Boolean(\n\t\t\t\tawait (__avLegacyValidate as any)(runtime, message, state, options),\n\t\t\t);\n\t\t} catch {\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?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tconst discordService = runtime.getService(\n\t\t\tDISCORD_SERVICE_NAME,\n\t\t) as DiscordService;\n\n\t\tif (!discordService?.client) {\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"Discord service is not available.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst searchParams = await getSearchParams(runtime, message, state);\n\t\tif (!searchParams) {\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"I couldn't understand what you want to search for. Please specify what to search.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tlet targetChannel: TextChannel | null = null;\n\t\t\tconst stateData = state.data;\n\t\t\tconst room = stateData?.room || (await runtime.getRoom(message.roomId));\n\n\t\t\t// Determine the target channel\n\t\t\tif (searchParams.channelIdentifier === \"current\") {\n\t\t\t\tif (room?.channelId) {\n\t\t\t\t\ttargetChannel = (await discordService.client.channels.fetch(\n\t\t\t\t\t\troom.channelId,\n\t\t\t\t\t)) as TextChannel;\n\t\t\t\t}\n\t\t\t} else if (searchParams.channelIdentifier.match(/^\\d+$/)) {\n\t\t\t\ttargetChannel = (await discordService.client.channels.fetch(\n\t\t\t\t\tsearchParams.channelIdentifier,\n\t\t\t\t)) as TextChannel;\n\t\t\t} else {\n\t\t\t\t// It's a channel name - search in the current server\n\t\t\t\tconst serverId = room?.messageServerId;\n\t\t\t\tif (!serverId) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: \"I couldn't determine which server to search for that channel.\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst guild = await discordService.client.guilds.fetch(serverId);\n\t\t\t\tconst channels = await guild.channels.fetch();\n\t\t\t\ttargetChannel =\n\t\t\t\t\t(channels.find(\n\t\t\t\t\t\t(channel) =>\n\t\t\t\t\t\t\tchannel?.name\n\t\t\t\t\t\t\t\t?.toLowerCase()\n\t\t\t\t\t\t\t\t.includes(searchParams.channelIdentifier.toLowerCase()) &&\n\t\t\t\t\t\t\tchannel.isTextBased(),\n\t\t\t\t\t) as TextChannel | undefined) || null;\n\t\t\t}\n\n\t\t\tif (!targetChannel?.isTextBased()) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't find that channel or I don't have access to it.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Calculate time limit\n\t\t\tlet before: number | undefined;\n\t\t\tif (searchParams.timeRange) {\n\t\t\t\tconst now = Date.now();\n\t\t\t\tconst timeMap: Record<string, number> = {\n\t\t\t\t\thour: 60 * 60 * 1000,\n\t\t\t\t\tday: 24 * 60 * 60 * 1000,\n\t\t\t\t\tweek: 7 * 24 * 60 * 60 * 1000,\n\t\t\t\t\tmonth: 30 * 24 * 60 * 60 * 1000,\n\t\t\t\t};\n\t\t\t\tif (timeMap[searchParams.timeRange]) {\n\t\t\t\t\tbefore = now - timeMap[searchParams.timeRange];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Fetch messages - Discord API limit is 100 per request\n\t\t\tconst messages = await targetChannel.messages.fetch({\n\t\t\t\tlimit: 100, // Discord API max limit\n\t\t\t\tbefore: before?.toString(),\n\t\t\t});\n\n\t\t\t// Search through messages\n\t\t\tconst results = searchInMessages(\n\t\t\t\tmessages,\n\t\t\t\tsearchParams.query,\n\t\t\t\tsearchParams.author,\n\t\t\t);\n\t\t\truntime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:search-messages\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\tquery: searchParams.query,\n\t\t\t\t\tresultsCount: results.length,\n\t\t\t\t\tchannelName: targetChannel.name,\n\t\t\t\t},\n\t\t\t\t\"Search completed\",\n\t\t\t);\n\n\t\t\t// Sort by timestamp (newest first) and limit\n\t\t\tconst sortedResults = results.sort(\n\t\t\t\t(a, b) => b.createdTimestamp - a.createdTimestamp,\n\t\t\t);\n\t\t\tconst limitedResults = sortedResults.slice(0, searchParams.limit);\n\n\t\t\tif (limitedResults.length === 0) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: `No messages found matching \"${searchParams.query}\" in <#${targetChannel.id}>.`,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Format results\n\t\t\tconst formattedResults = limitedResults\n\t\t\t\t.map((msg, index) => {\n\t\t\t\t\tconst timestamp = new Date(msg.createdTimestamp).toLocaleString();\n\t\t\t\t\tconst preview =\n\t\t\t\t\t\tmsg.content.length > 100\n\t\t\t\t\t\t\t? `${msg.content.substring(0, 100)}...`\n\t\t\t\t\t\t\t: msg.content;\n\t\t\t\t\tconst attachments =\n\t\t\t\t\t\tmsg.attachments.size > 0\n\t\t\t\t\t\t\t? `\\n📎 ${msg.attachments.size} attachment(s)`\n\t\t\t\t\t\t\t: \"\";\n\n\t\t\t\t\treturn `**${index + 1}.** ${msg.author.username} (${timestamp})\\n${preview}${attachments}\\n[Jump to message](${msg.url})`;\n\t\t\t\t})\n\t\t\t\t.join(\"\\n\\n\");\n\n\t\t\tconst response: Content = {\n\t\t\t\ttext: `Found ${limitedResults.length} message${limitedResults.length !== 1 ? \"s\" : \"\"} matching \"${searchParams.query}\" in <#${targetChannel.id}>:\\n\\n${formattedResults}`,\n\t\t\t\tsource: message.content.source,\n\t\t\t};\n\n\t\t\tawait callback?.(response);\n\t\t} catch (error) {\n\t\t\truntime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:search-messages\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error searching messages\",\n\t\t\t);\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"I encountered an error while searching for messages. Please try again.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t}\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default searchMessages;\n",
|
|
24
|
+
"import {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\ttype Content,\n\tcomposePromptFromState,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype State,\n} from \"@elizaos/core\";\nimport type { User } from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\n// Import generated prompts\nimport { sendDmTemplate } from \"../generated/prompts/typescript/prompts.js\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\nimport type { DiscordService } from \"../service\";\nimport {\n\tterminalActionInteractionSemantics,\n\tterminalActionResultData,\n} from \"./actionResultSemantics\";\n\n/**\n * Get DM information from the user's request\n * @param {IAgentRuntime} runtime - The runtime object to interact with the agent.\n * @param {Memory} _message - The memory object containing the input message.\n * @param {State} state - The state of the conversation.\n * @returns {Promise<{recipientIdentifier: string, messageContent: string} | null>} DM info or null if not parseable.\n */\nconst getDMInfo = async (\n\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n): Promise<{ recipientIdentifier: string; messageContent: string } | null> => {\n\tconst prompt = composePromptFromState({\n\t\tstate,\n\t\ttemplate: sendDmTemplate,\n\t});\n\n\tfor (let i = 0; i < 3; i++) {\n\t\tconst response = await runtime.useModel(ModelType.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\n\t\tconst parsedResponse = parseJSONObjectFromText(response) as {\n\t\t\trecipientIdentifier: string;\n\t\t\tmessageContent: string;\n\t\t} | null;\n\n\t\tif (parsedResponse?.recipientIdentifier && parsedResponse.messageContent) {\n\t\t\treturn parsedResponse;\n\t\t}\n\t}\n\treturn null;\n};\n\n/**\n * Find a Discord user by various identifiers\n * @param {DiscordService} discordService - The Discord service instance\n * @param {string} identifier - The user identifier (username, ID, or mention)\n * @param {string} currentServerId - The current server ID to search in\n * @returns {Promise<User | null>} The found user or null\n */\nconst findUser = async (\n\tdiscordService: DiscordService,\n\tidentifier: string,\n\tcurrentServerId?: string,\n): Promise<User | null> => {\n\tif (!discordService.client) {\n\t\treturn null;\n\t}\n\n\t// Remove mention formatting if present\n\tconst cleanId = identifier.replace(/[<@!>]/g, \"\");\n\n\ttry {\n\t\t// Try to fetch by ID first\n\t\tif (/^\\d+$/.test(cleanId)) {\n\t\t\ttry {\n\t\t\t\treturn await discordService.client.users.fetch(cleanId);\n\t\t\t} catch (_e) {\n\t\t\t\t// ID not found, continue to username search\n\t\t\t}\n\t\t}\n\n\t\t// Search in the current server if available\n\t\tif (currentServerId) {\n\t\t\tconst guild = await discordService.client.guilds.fetch(currentServerId);\n\t\t\tconst members = await guild.members.fetch();\n\n\t\t\t// Search by username or display name\n\t\t\tconst member = members.find(\n\t\t\t\t(m) =>\n\t\t\t\t\tm.user.username.toLowerCase() === identifier.toLowerCase() ||\n\t\t\t\t\tm.displayName.toLowerCase() === identifier.toLowerCase() ||\n\t\t\t\t\tm.user.tag.toLowerCase() === identifier.toLowerCase(),\n\t\t\t);\n\n\t\t\tif (member) {\n\t\t\t\treturn member.user;\n\t\t\t}\n\t\t}\n\n\t\t// Search in all guilds the bot is in\n\t\tconst guilds = Array.from(discordService.client.guilds.cache.values());\n\t\tfor (const guild of guilds) {\n\t\t\ttry {\n\t\t\t\tconst members = await guild.members.fetch();\n\t\t\t\tconst member = members.find(\n\t\t\t\t\t(m) =>\n\t\t\t\t\t\tm.user.username.toLowerCase() === identifier.toLowerCase() ||\n\t\t\t\t\t\tm.displayName.toLowerCase() === identifier.toLowerCase() ||\n\t\t\t\t\t\tm.user.tag.toLowerCase() === identifier.toLowerCase(),\n\t\t\t\t);\n\n\t\t\t\tif (member) {\n\t\t\t\t\treturn member.user;\n\t\t\t\t}\n\t\t\t} catch (_e) {\n\t\t\t\t// Continue searching in other guilds\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t} catch (_error) {\n\t\t// Standalone function - error handled by caller\n\t\treturn null;\n\t}\n};\n\nconst spec = requireActionSpec(\"SEND_DM\");\n\nexport const sendDM: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\t...terminalActionInteractionSemantics,\n\tvalidate: async (\n\t\t_runtime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\t_state?: State,\n\t): Promise<boolean> => {\n\t\treturn message.content.source === \"discord\";\n\t},\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\tstate?: State,\n\t\t_options?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tconst discordService = runtime.getService(\n\t\t\tDISCORD_SERVICE_NAME,\n\t\t) as DiscordService;\n\n\t\tif (!discordService?.client) {\n\t\t\truntime.logger.error(\n\t\t\t\t{ src: \"plugin:discord:action:send-dm\", agentId: runtime.agentId },\n\t\t\t\t\"Discord service not found or not initialized\",\n\t\t\t);\n\t\t\treturn { success: false, error: \"Discord service is not available\" };\n\t\t}\n\n\t\tif (!state) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"State is not available.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"State is not available\" };\n\t\t}\n\n\t\tconst dmInfo = await getDMInfo(runtime, message, state);\n\t\tif (!dmInfo) {\n\t\t\truntime.logger.warn(\n\t\t\t\t{ src: \"plugin:discord:action:send-dm\", agentId: runtime.agentId },\n\t\t\t\t\"Could not parse DM information from message\",\n\t\t\t);\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't understand who you want me to message or what to send. Please specify the recipient and the message content.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"Could not parse DM information\" };\n\t\t}\n\n\t\ttry {\n\t\t\tconst room = state.data?.room || (await runtime.getRoom(message.roomId));\n\t\t\tconst currentServerId = room?.messageServerId;\n\n\t\t\t// Find the user\n\t\t\tconst targetUser = await findUser(\n\t\t\t\tdiscordService,\n\t\t\t\tdmInfo.recipientIdentifier,\n\t\t\t\tcurrentServerId,\n\t\t\t);\n\n\t\t\tif (!targetUser) {\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: `I couldn't find a user with the identifier \"${dmInfo.recipientIdentifier}\". Please make sure the username or ID is correct.`,\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: `User not found: ${dmInfo.recipientIdentifier}`,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Check if we can send DMs to this user\n\t\t\tif (targetUser.bot) {\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: \"I cannot send direct messages to other bots.\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn { success: false, error: \"Cannot send DMs to bots\" };\n\t\t\t}\n\n\t\t\t// Create or get DM channel\n\t\t\tconst dmChannel = await targetUser.createDM();\n\n\t\t\t// Send the message\n\t\t\tawait dmChannel.send(dmInfo.messageContent);\n\n\t\t\tconst response: Content = {\n\t\t\t\ttext: `I've sent your message to ${targetUser.username}: \"${dmInfo.messageContent}\"`,\n\t\t\t\tactions: [\"SEND_DM_RESPONSE\"],\n\t\t\t\tsource: message.content.source,\n\t\t\t};\n\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.(response);\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\ttext: response.text,\n\t\t\t\tdata: terminalActionResultData(),\n\t\t\t};\n\t\t} catch (error) {\n\t\t\truntime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:send-dm\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error sending DM\",\n\t\t\t);\n\n\t\t\t// Handle specific Discord API errors\n\t\t\tif (error instanceof Error) {\n\t\t\t\tif (error.message.includes(\"Cannot send messages to this user\")) {\n\t\t\t\t\tif (callback) {\n\t\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\t\ttext: \"I couldn't send a message to that user. They may have DMs disabled or we don't share a server.\",\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (callback) {\n\t\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\t\ttext: \"I encountered an error while trying to send the direct message. Please make sure I have the necessary permissions.\",\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default sendDM;\n",
|
|
25
|
+
"import {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\ttype Content,\n\tcomposePromptFromState,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype State,\n} from \"@elizaos/core\";\nimport type { TextChannel } from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\nimport type { DiscordService } from \"../service\";\nimport {\n\tterminalActionInteractionSemantics,\n\tterminalActionResultData,\n} from \"./actionResultSemantics\";\n\nconst sendMessageTemplate = `You are helping to extract send message parameters.\n\nThe user wants to send a message to a Discord channel.\n\nRecent conversation:\n{{recentMessages}}\n\nExtract the following:\n1. text: The message text to send\n2. channelRef: The channel to send to (default: \"current\" for the current channel)\n\nRespond with a JSON object like:\n{\n \"text\": \"The message to send\",\n \"channelRef\": \"current\"\n}\n\nOnly respond with the JSON object, no other text.`;\n\nconst spec = requireActionSpec(\"SEND_MESSAGE\");\n\nexport const sendMessage: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\t...terminalActionInteractionSemantics,\n\tvalidate: async (\n\t\t_runtime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\t_state?: State,\n\t): Promise<boolean> => {\n\t\treturn message.content.source === \"discord\";\n\t},\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\tstate?: State,\n\t\t_options?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tconst discordService = runtime.getService(\n\t\t\tDISCORD_SERVICE_NAME,\n\t\t) as DiscordService;\n\n\t\tif (!discordService?.client) {\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"Discord service is not available.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// Use LLM to extract message parameters\n\t\tconst prompt = composePromptFromState({\n\t\t\tstate,\n\t\t\ttemplate: sendMessageTemplate,\n\t\t});\n\n\t\tlet messageInfo: { text: string; channelRef?: string } | null = null;\n\n\t\tfor (let i = 0; i < 3; i++) {\n\t\t\tconst response = await runtime.useModel(ModelType.TEXT_SMALL, {\n\t\t\t\tprompt,\n\t\t\t});\n\n\t\t\tconst parsedResponse = parseJSONObjectFromText(response);\n\t\t\tif (parsedResponse?.text) {\n\t\t\t\tmessageInfo = {\n\t\t\t\t\ttext: String(parsedResponse.text),\n\t\t\t\t\tchannelRef: parsedResponse.channelRef\n\t\t\t\t\t\t? String(parsedResponse.channelRef)\n\t\t\t\t\t\t: \"current\",\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!messageInfo?.text) {\n\t\t\truntime.logger.debug(\n\t\t\t\t{ src: \"plugin:discord:action:send-message\" },\n\t\t\t\t\"[SEND_MESSAGE] Could not extract message info\",\n\t\t\t);\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"I couldn't understand what message you want me to send. Please try again with a clearer request.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst stateData = state?.data;\n\t\t\tconst room = stateData?.room || (await runtime.getRoom(message.roomId));\n\n\t\t\tif (!room?.channelId) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't determine the current channel.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet targetChannelId = room.channelId;\n\n\t\t\t// If a specific channel was referenced (not \"current\"), try to find it\n\t\t\tif (messageInfo.channelRef && messageInfo.channelRef !== \"current\") {\n\t\t\t\tconst guild = discordService.client.guilds.cache.first();\n\t\t\t\tif (guild) {\n\t\t\t\t\tconst channels = await guild.channels.fetch();\n\t\t\t\t\tconst targetChannel = channels.find((ch) => {\n\t\t\t\t\t\tif (!ch?.isTextBased()) return false;\n\t\t\t\t\t\tconst channelName = ch.name?.toLowerCase() || \"\";\n\t\t\t\t\t\tconst searchTerm = messageInfo?.channelRef?.toLowerCase() || \"\";\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\tchannelName === searchTerm ||\n\t\t\t\t\t\t\tchannelName.includes(searchTerm) ||\n\t\t\t\t\t\t\tch.id === messageInfo?.channelRef\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\t\t\t\t\tif (targetChannel) {\n\t\t\t\t\t\ttargetChannelId = targetChannel.id;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst channel =\n\t\t\t\tawait discordService.client.channels.fetch(targetChannelId);\n\t\t\tif (!channel?.isTextBased()) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I can only send messages to text channels.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst textChannel = channel as TextChannel;\n\n\t\t\t// Send the message\n\t\t\tconst sentMessage = await textChannel.send(messageInfo.text);\n\n\t\t\tconst response: Content = {\n\t\t\t\ttext: `Message sent successfully.`,\n\t\t\t\tsource: message.content.source,\n\t\t\t};\n\n\t\t\truntime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:send-message\",\n\t\t\t\t\tmessageId: sentMessage.id,\n\t\t\t\t\tchannelId: targetChannelId,\n\t\t\t\t},\n\t\t\t\t\"[SEND_MESSAGE] Message sent successfully\",\n\t\t\t);\n\n\t\t\tawait callback?.(response);\n\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tdata: terminalActionResultData({\n\t\t\t\t\tmessageId: sentMessage.id,\n\t\t\t\t\tchannelId: targetChannelId,\n\t\t\t\t}),\n\t\t\t};\n\t\t} catch (error) {\n\t\t\truntime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:send-message\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error sending message\",\n\t\t\t);\n\t\t\tawait callback?.({\n\t\t\t\ttext: \"I encountered an error while trying to send the message. Please make sure I have the necessary permissions.\",\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t}\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default sendMessage;\n",
|
|
26
|
+
"import type {\n\tAction,\n\tActionExample,\n\tActionResult,\n\tContent,\n\tHandlerCallback,\n\tHandlerOptions,\n\tIAgentRuntime,\n\tMemory,\n\tState,\n} from \"@elizaos/core\";\nimport type { Guild } from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\nimport type { DiscordService } from \"../service\";\n\nconst formatServerInfo = (guild: Guild, detailed: boolean = false): string => {\n\tconst createdAt = new Date(guild.createdAt).toLocaleDateString();\n\tconst memberCount = guild.memberCount.toLocaleString();\n\tconst channelCount = guild.channels.cache.size.toLocaleString();\n\tconst roleCount = guild.roles.cache.size.toLocaleString();\n\tconst emojiCount = guild.emojis.cache.size.toLocaleString();\n\tconst boostLevel = guild.premiumTier;\n\tconst boostCount = (guild.premiumSubscriptionCount || 0).toLocaleString();\n\n\tconst basicInfo = [\n\t\t`🏛️ **Server Information for ${guild.name}**`,\n\t\t`**ID:** ${guild.id}`,\n\t\t`**Owner:** <@${guild.ownerId}>`,\n\t\t`**Created:** ${createdAt}`,\n\t\t`**Members:** ${memberCount}`,\n\t\t`**Channels:** ${channelCount}`,\n\t\t`**Roles:** ${roleCount}`,\n\t\t`**Server Level:** ${boostLevel} (${boostCount} boosts)`,\n\t];\n\n\tif (detailed) {\n\t\tconst textChannels = guild.channels.cache\n\t\t\t.filter((ch) => ch.isTextBased())\n\t\t\t.size.toLocaleString();\n\t\tconst voiceChannels = guild.channels.cache\n\t\t\t.filter((ch) => ch.isVoiceBased())\n\t\t\t.size.toLocaleString();\n\t\tconst categories = guild.channels.cache\n\t\t\t.filter((ch) => ch.type === 4)\n\t\t\t.size.toLocaleString(); // CategoryChannel type\n\t\tconst activeThreads = guild.channels.cache\n\t\t\t.filter((ch) => ch.isThread() && !ch.archived)\n\t\t\t.size.toLocaleString();\n\t\tconst stickerCount = guild.stickers.cache.size.toLocaleString();\n\n\t\tconst features =\n\t\t\tguild.features.length > 0\n\t\t\t\t? guild.features\n\t\t\t\t\t\t.map((f) => f.toLowerCase().replace(/_/g, \" \"))\n\t\t\t\t\t\t.join(\", \")\n\t\t\t\t: \"None\";\n\n\t\tconst detailedInfo = [\n\t\t\t\"\",\n\t\t\t\"📊 **Detailed Statistics**\",\n\t\t\t`**Text Channels:** ${textChannels}`,\n\t\t\t`**Voice Channels:** ${voiceChannels}`,\n\t\t\t`**Categories:** ${categories}`,\n\t\t\t`**Active Threads:** ${activeThreads}`,\n\t\t\t`**Custom Emojis:** ${emojiCount}`,\n\t\t\t`**Stickers:** ${stickerCount}`,\n\t\t\t\"\",\n\t\t\t\"🎯 **Server Features**\",\n\t\t\t`**Verification Level:** ${guild.verificationLevel}`,\n\t\t\t`**Content Filter:** ${guild.explicitContentFilter}`,\n\t\t\t`**2FA Requirement:** ${guild.mfaLevel === 1 ? \"Enabled\" : \"Disabled\"}`,\n\t\t\t`**Features:** ${features}`,\n\t\t];\n\n\t\tif (guild.description) {\n\t\t\tdetailedInfo.push(`**Description:** ${guild.description}`);\n\t\t}\n\n\t\tif (guild.vanityURLCode) {\n\t\t\tdetailedInfo.push(`**Vanity URL:** discord.gg/${guild.vanityURLCode}`);\n\t\t}\n\n\t\treturn [...basicInfo, ...detailedInfo].join(\"\\n\");\n\t}\n\n\treturn basicInfo.join(\"\\n\");\n};\n\nconst spec = requireActionSpec(\"SERVER_INFO\");\n\nexport const serverInfo: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\tvalidate: async (\n\t\truntime: any,\n\t\tmessage: any,\n\t\tstate?: any,\n\t\toptions?: any,\n\t): Promise<boolean> => {\n\t\tconst __avTextRaw =\n\t\t\ttypeof message?.content?.text === \"string\" ? message.content.text : \"\";\n\t\tconst __avText = __avTextRaw.toLowerCase();\n\t\tconst __avKeywords = [\"server\", \"info\"];\n\t\tconst __avKeywordOk =\n\t\t\t__avKeywords.length > 0 &&\n\t\t\t__avKeywords.some((word) => word.length > 0 && __avText.includes(word));\n\t\tconst __avRegex = /\\b(?:server|info)\\b/i;\n\t\tconst __avRegexOk = __avRegex.test(__avText);\n\t\tconst __avSource = String(message?.content?.source ?? \"\");\n\t\tconst __avExpectedSource = \"\";\n\t\tconst __avSourceOk = __avExpectedSource\n\t\t\t? __avSource === __avExpectedSource\n\t\t\t: Boolean(\n\t\t\t\t\t__avSource ||\n\t\t\t\t\t\tstate ||\n\t\t\t\t\t\truntime?.agentId ||\n\t\t\t\t\t\truntime?.getService ||\n\t\t\t\t\t\truntime?.getSetting,\n\t\t\t\t);\n\t\tconst __avOptions = options && typeof options === \"object\" ? options : {};\n\t\tconst __avInputOk =\n\t\t\t__avText.trim().length > 0 ||\n\t\t\tObject.keys(__avOptions as Record<string, unknown>).length > 0 ||\n\t\t\tBoolean(message?.content && typeof message.content === \"object\");\n\n\t\tif (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst __avLegacyValidate = async (\n\t\t\t_runtime: IAgentRuntime,\n\t\t\tmessage: Memory,\n\t\t\t_state?: State,\n\t\t): Promise<boolean> => {\n\t\t\treturn message.content.source === \"discord\";\n\t\t};\n\t\ttry {\n\t\t\treturn Boolean(\n\t\t\t\tawait (__avLegacyValidate as any)(runtime, message, state, options),\n\t\t\t);\n\t\t} catch {\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?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tconst discordService = runtime.getService(\n\t\t\tDISCORD_SERVICE_NAME,\n\t\t) as DiscordService;\n\n\t\tif (!discordService?.client) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"Discord service is not available.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"Discord service is not available\" };\n\t\t}\n\n\t\tif (!state) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"State is not available.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"State is not available\" };\n\t\t}\n\n\t\ttry {\n\t\t\tconst stateData = state.data;\n\t\t\tconst room = stateData?.room || (await runtime.getRoom(message.roomId));\n\t\t\tconst serverId = room?.messageServerId;\n\t\t\tif (!serverId) {\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: \"I couldn't determine the current server.\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn { success: false, error: \"Could not determine current server\" };\n\t\t\t}\n\n\t\t\tconst guild = await discordService.client.guilds.fetch(serverId);\n\n\t\t\t// Check if the request is for detailed info\n\t\t\tconst messageContentText = message.content.text;\n\t\t\tconst messageText = messageContentText?.toLowerCase() || \"\";\n\t\t\tconst isDetailed =\n\t\t\t\tmessageText.includes(\"detailed\") ||\n\t\t\t\tmessageText.includes(\"full\") ||\n\t\t\t\tmessageText.includes(\"stats\") ||\n\t\t\t\tmessageText.includes(\"statistics\");\n\n\t\t\tconst infoText = formatServerInfo(guild, isDetailed);\n\n\t\t\tconst response: Content = {\n\t\t\t\ttext: infoText,\n\t\t\t\tsource: message.content.source,\n\t\t\t};\n\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.(response);\n\t\t\t}\n\t\t\treturn { success: true, text: response.text };\n\t\t} catch (error) {\n\t\t\truntime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:server-info\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error getting server info\",\n\t\t\t);\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I encountered an error while getting server information. Please try again.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default serverInfo;\n",
|
|
27
|
+
"import * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\tlogger,\n\ttype Memory,\n\ttype State,\n} from \"@elizaos/core\";\nimport { ChannelType, type TextChannel } from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport type { DiscordService } from \"../service\";\nimport {\n\tterminalActionInteractionSemantics,\n\tterminalActionResultData,\n} from \"./actionResultSemantics\";\n\nexport interface CredentialPreset {\n\tname: string;\n\tdisplayName: string;\n\tfields: CredentialField[];\n\thelpUrl: string;\n\thelpText: string;\n\tvalidate: (\n\t\tcredentials: Record<string, string>,\n\t) => Promise<{ valid: boolean; identity?: string; error?: string }>;\n}\n\nexport interface CredentialField {\n\tkey: string;\n\tlabel: string;\n\tsecret: boolean;\n}\n\nconst SAFE_PRESET_NAME_RE = /^[A-Za-z0-9_-]+$/;\nconst SESSION_TIMEOUT_MS = 5 * 60 * 1000;\nconst presets = new Map<string, CredentialPreset>();\nconst activeSessions = new Map<string, SetupSession>();\n\ninterface SetupSession {\n\tpreset: CredentialPreset;\n\tcurrentFieldIndex: number;\n\tcollected: Record<string, string>;\n\tchannelId: string;\n\tstartedAt: number;\n}\n\nfunction escapeRegex(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction getCredentialsDir(): string {\n\tconst configured = process.env.CREDENTIALS_DIR?.trim();\n\tif (configured) {\n\t\treturn configured;\n\t}\n\n\tconst home =\n\t\t(typeof os.homedir === \"function\" ? os.homedir() : \"\") ||\n\t\tprocess.env.HOME ||\n\t\tprocess.env.USERPROFILE;\n\treturn home\n\t\t? path.join(home, \".credentials\")\n\t\t: path.join(process.cwd(), \".credentials\");\n}\n\nexport function registerPreset(preset: CredentialPreset): void {\n\tconst normalizedName = preset.name.trim().toLowerCase();\n\tif (!SAFE_PRESET_NAME_RE.test(normalizedName)) {\n\t\tthrow new Error(\n\t\t\t`Invalid credential preset name \"${preset.name}\". Only letters, numbers, underscores, and hyphens are allowed.`,\n\t\t);\n\t}\n\tpresets.set(normalizedName, { ...preset, name: normalizedName });\n}\n\nexport function getPreset(name: string): CredentialPreset | undefined {\n\treturn presets.get(name.toLowerCase());\n}\n\nexport function listPresets(): string[] {\n\treturn [...presets.keys()];\n}\n\nregisterPreset({\n\tname: \"github\",\n\tdisplayName: \"GitHub\",\n\tfields: [{ key: \"token\", label: \"Personal Access Token\", secret: true }],\n\thelpUrl: \"https://github.com/settings/tokens\",\n\thelpText:\n\t\t\"Create a fine-grained PAT at the link above. Give it the repository permissions you need.\",\n\tasync validate(credentials) {\n\t\ttry {\n\t\t\tconst response = await fetch(\"https://api.github.com/user\", {\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${credentials.token}`,\n\t\t\t\t\tAccept: \"application/vnd.github+json\",\n\t\t\t\t},\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tvalid: false,\n\t\t\t\t\terror: `GitHub returned ${response.status}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst data = (await response.json()) as { login?: string };\n\t\t\treturn {\n\t\t\t\tvalid: true,\n\t\t\t\tidentity: data.login ? `@${data.login}` : \"verified\",\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tvalid: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t},\n});\n\nregisterPreset({\n\tname: \"vercel\",\n\tdisplayName: \"Vercel\",\n\tfields: [{ key: \"token\", label: \"API Token\", secret: true }],\n\thelpUrl: \"https://vercel.com/account/tokens\",\n\thelpText: \"Create a token at the link above. Full Account scope works best.\",\n\tasync validate(credentials) {\n\t\ttry {\n\t\t\tconst response = await fetch(\"https://api.vercel.com/v9/projects\", {\n\t\t\t\theaders: { Authorization: `Bearer ${credentials.token}` },\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tvalid: false,\n\t\t\t\t\terror: `Vercel returned ${response.status}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst data = (await response.json()) as {\n\t\t\t\tprojects?: Array<{ name: string }>;\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tvalid: true,\n\t\t\t\tidentity: `${data.projects?.length ?? 0} project(s) accessible`,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tvalid: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t},\n});\n\nregisterPreset({\n\tname: \"cloudflare\",\n\tdisplayName: \"Cloudflare\",\n\tfields: [\n\t\t{ key: \"apiKey\", label: \"Global API Key\", secret: true },\n\t\t{ key: \"email\", label: \"Account Email\", secret: false },\n\t],\n\thelpUrl: \"https://dash.cloudflare.com/profile/api-tokens\",\n\thelpText:\n\t\t'Go to Cloudflare > Profile > API Tokens > \"Global API Key\". You will also need your account email.',\n\tasync validate(credentials) {\n\t\ttry {\n\t\t\tconst response = await fetch(\n\t\t\t\t\"https://api.cloudflare.com/client/v4/zones\",\n\t\t\t\t{\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"X-Auth-Key\": credentials.apiKey,\n\t\t\t\t\t\t\"X-Auth-Email\": credentials.email,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t\tif (!response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tvalid: false,\n\t\t\t\t\terror: `Cloudflare returned ${response.status}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst data = (await response.json()) as {\n\t\t\t\tresult?: Array<{ name: string }>;\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tvalid: true,\n\t\t\t\tidentity:\n\t\t\t\t\tdata.result && data.result.length > 0\n\t\t\t\t\t\t? `zones: ${data.result.map((zone) => zone.name).join(\", \")}`\n\t\t\t\t\t\t: \"verified\",\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tvalid: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t},\n});\n\nregisterPreset({\n\tname: \"anthropic\",\n\tdisplayName: \"Anthropic\",\n\tfields: [{ key: \"apiKey\", label: \"API Key\", secret: true }],\n\thelpUrl: \"https://console.anthropic.com/settings/keys\",\n\thelpText: \"Create an API key in the Anthropic console.\",\n\tasync validate(credentials) {\n\t\ttry {\n\t\t\tconst response = await fetch(\"https://api.anthropic.com/v1/messages\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"x-api-key\": credentials.apiKey,\n\t\t\t\t\t\"anthropic-version\": \"2023-06-01\",\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel: \"claude-3-5-haiku-20241022\",\n\t\t\t\t\tmax_tokens: 1,\n\t\t\t\t\tmessages: [{ role: \"user\", content: \"hi\" }],\n\t\t\t\t}),\n\t\t\t});\n\t\t\tif (response.ok || response.status === 429) {\n\t\t\t\treturn { valid: true, identity: \"key verified\" };\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tvalid: false,\n\t\t\t\terror: `Anthropic returned ${response.status}`,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tvalid: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t},\n});\n\nregisterPreset({\n\tname: \"openai\",\n\tdisplayName: \"OpenAI\",\n\tfields: [{ key: \"apiKey\", label: \"API Key\", secret: true }],\n\thelpUrl: \"https://platform.openai.com/api-keys\",\n\thelpText: \"Create an API key at the OpenAI platform link above.\",\n\tasync validate(credentials) {\n\t\ttry {\n\t\t\tconst response = await fetch(\"https://api.openai.com/v1/models\", {\n\t\t\t\theaders: { Authorization: `Bearer ${credentials.apiKey}` },\n\t\t\t});\n\t\t\tif (response.ok || response.status === 429) {\n\t\t\t\treturn { valid: true, identity: \"key verified\" };\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tvalid: false,\n\t\t\t\terror: `OpenAI returned ${response.status}`,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tvalid: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t},\n});\n\nregisterPreset({\n\tname: \"fal\",\n\tdisplayName: \"fal.ai\",\n\tfields: [{ key: \"apiKey\", label: \"API Key\", secret: true }],\n\thelpUrl: \"https://fal.ai/dashboard/keys\",\n\thelpText: \"Generate an API key from your fal.ai dashboard.\",\n\tasync validate(credentials) {\n\t\ttry {\n\t\t\tconst response = await fetch(\"https://rest.fal.run/fal-ai/fast-sdxl\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Key ${credentials.apiKey}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tprompt: \"test\",\n\t\t\t\t\timage_size: { width: 64, height: 64 },\n\t\t\t\t\tnum_images: 1,\n\t\t\t\t}),\n\t\t\t});\n\t\t\tif (response.ok || response.status === 422 || response.status === 429) {\n\t\t\t\treturn { valid: true, identity: \"key verified\" };\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tvalid: false,\n\t\t\t\terror: `fal.ai returned ${response.status}`,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tvalid: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t},\n});\n\nregisterPreset({\n\tname: \"generic\",\n\tdisplayName: \"Custom Credential\",\n\tfields: [\n\t\t{\n\t\t\tkey: \"envName\",\n\t\t\tlabel: \"environment variable name (for example MY_API_KEY)\",\n\t\t\tsecret: false,\n\t\t},\n\t\t{ key: \"value\", label: \"value\", secret: true },\n\t],\n\thelpUrl: \"\",\n\thelpText:\n\t\t\"I'll store this as a generic credential. Give me the env var name and value.\",\n\tasync validate() {\n\t\treturn { valid: true, identity: \"stored (unvalidated)\" };\n\t},\n});\n\nasync function ensureCredentialsDir(): Promise<void> {\n\tawait fs.promises.mkdir(getCredentialsDir(), {\n\t\trecursive: true,\n\t\tmode: 0o700,\n\t});\n}\n\nasync function storeCredentials(\n\tservice: string,\n\tcredentials: Record<string, string>,\n): Promise<void> {\n\tawait ensureCredentialsDir();\n\tconst filePath = path.join(getCredentialsDir(), `${service}.json`);\n\tawait fs.promises.writeFile(filePath, JSON.stringify(credentials, null, 2), {\n\t\tmode: 0o600,\n\t});\n}\n\nexport function loadCredentials(\n\tservice: string,\n): Record<string, string> | null {\n\tconst filePath = path.join(getCredentialsDir(), `${service}.json`);\n\tif (!fs.existsSync(filePath)) {\n\t\treturn null;\n\t}\n\ttry {\n\t\treturn JSON.parse(fs.readFileSync(filePath, \"utf8\")) as Record<\n\t\t\tstring,\n\t\t\tstring\n\t\t>;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction cleanExpiredSessions(): void {\n\tconst now = Date.now();\n\tfor (const [userId, session] of activeSessions) {\n\t\tif (now - session.startedAt > SESSION_TIMEOUT_MS) {\n\t\t\tactiveSessions.delete(userId);\n\t\t}\n\t}\n}\n\nasync function tryDeleteMessage(\n\tdiscordService: DiscordService,\n\tchannelId: string,\n\tmessageId: string,\n\tfieldName: string,\n): Promise<boolean> {\n\ttry {\n\t\tconst client = discordService.client;\n\t\tif (!client) {\n\t\t\treturn false;\n\t\t}\n\t\tconst channel = await client.channels.fetch(channelId);\n\t\tif (!channel || !(\"messages\" in channel)) {\n\t\t\treturn false;\n\t\t}\n\t\tconst message = await (channel as TextChannel).messages.fetch(messageId);\n\t\tawait message.delete();\n\t\treturn true;\n\t} catch (error) {\n\t\tlogger.warn(\n\t\t\t{\n\t\t\t\tsrc: \"setup-credentials\",\n\t\t\t\tchannelId,\n\t\t\t\tmessageId,\n\t\t\t\tfieldName,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t},\n\t\t\t\"Could not delete Discord message containing a credential\",\n\t\t);\n\t\treturn false;\n\t}\n}\n\nconst TRIGGER_PATTERNS = [\n\t/\\bsetup\\s+(github|vercel|cloudflare|anthropic|openai|fal|credentials?)\\b/i,\n\t/\\badd\\s+(my\\s+)?(api\\s+)?key\\b/i,\n\t/\\badd\\s+credentials?\\b/i,\n\t/\\bconfigure\\s+(github|vercel|cloudflare|anthropic|openai|fal)\\b/i,\n\t/\\bconnect\\s+(github|vercel|cloudflare|anthropic|openai|fal)\\b/i,\n\t/^\\/setup\\b/i,\n];\n\nfunction detectSetupIntent(text: string): string | null | undefined {\n\tconst lower = text.toLowerCase().trim();\n\n\tfor (const presetName of presets.keys()) {\n\t\tif (presetName === \"generic\") {\n\t\t\tcontinue;\n\t\t}\n\t\tconst pattern = new RegExp(\n\t\t\t`\\\\b(setup|configure|connect|add)\\\\s+(my\\\\s+)?${escapeRegex(presetName)}\\\\b`,\n\t\t\t\"i\",\n\t\t);\n\t\tif (pattern.test(lower)) {\n\t\t\treturn presetName;\n\t\t}\n\t}\n\n\tconst slashMatch = lower.match(/^\\/setup\\s+(\\w+)/);\n\tif (slashMatch) {\n\t\tconst service = slashMatch[1].toLowerCase();\n\t\tif (presets.has(service)) {\n\t\t\treturn service;\n\t\t}\n\t\tif (service === \"custom\") {\n\t\t\treturn \"generic\";\n\t\t}\n\t}\n\n\tfor (const pattern of TRIGGER_PATTERNS) {\n\t\tif (pattern.test(lower)) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nfunction isSetupTrigger(text: string): boolean {\n\treturn TRIGGER_PATTERNS.some((pattern) =>\n\t\tpattern.test(text.toLowerCase().trim()),\n\t);\n}\n\nfunction buildServiceListMessage(): string {\n\tconst services = listPresets()\n\t\t.filter((presetName) => presetName !== \"generic\")\n\t\t.map((presetName) => {\n\t\t\tconst preset = getPreset(presetName);\n\t\t\treturn `- **${preset?.displayName ?? presetName}** (\\`${presetName}\\`)`;\n\t\t});\n\treturn [\n\t\t\"Which service do you want to set up? Here's what I support:\",\n\t\t\"\",\n\t\t...services,\n\t\t\"- **Custom** (`custom`) - any env var\",\n\t\t\"\",\n\t\t\"Just tell me the name, for example `github` or `custom`.\",\n\t].join(\"\\n\");\n}\n\nfunction resolveDeletionTarget(\n\tmessage: Memory,\n\tdefaultChannelId: string,\n): { channelId: string; messageId?: string } {\n\tconst contentRecord =\n\t\tmessage.content && typeof message.content === \"object\"\n\t\t\t? (message.content as Record<string, unknown>)\n\t\t\t: null;\n\tconst metadataRecord =\n\t\tmessage.metadata && typeof message.metadata === \"object\"\n\t\t\t? (message.metadata as Record<string, unknown>)\n\t\t\t: null;\n\n\tconst channelId =\n\t\t(typeof contentRecord?.channelId === \"string\" && contentRecord.channelId) ||\n\t\t(typeof metadataRecord?.discordChannelId === \"string\" &&\n\t\t\tmetadataRecord.discordChannelId) ||\n\t\tdefaultChannelId;\n\tconst messageId =\n\t\t(typeof contentRecord?.messageId === \"string\" && contentRecord.messageId) ||\n\t\t(typeof metadataRecord?.discordMessageId === \"string\" &&\n\t\t\tmetadataRecord.discordMessageId);\n\treturn { channelId, messageId };\n}\n\nexport const setupCredentials: Action = {\n\tname: \"SETUP_CREDENTIALS\",\n\tsimiles: [\n\t\t\"ADD_CREDENTIALS\",\n\t\t\"CONFIGURE_SERVICE\",\n\t\t\"CONNECT_SERVICE\",\n\t\t\"ADD_API_KEY\",\n\t\t\"SETUP_SERVICE\",\n\t],\n\tdescription:\n\t\t\"Guide the user through setting up API credentials for supported third-party services, validate them when possible, and store them securely.\",\n\tdescriptionCompressed:\n\t\t\"Guide credential setup for third-party services, validate and store securely.\",\n\t...terminalActionInteractionSemantics,\n\tvalidate: async (_runtime, message) => {\n\t\tif (message.content.source !== \"discord\") {\n\t\t\treturn false;\n\t\t}\n\t\tconst text = message.content.text?.trim() ?? \"\";\n\t\tconst userId = message.entityId as string;\n\t\treturn activeSessions.has(userId) || isSetupTrigger(text);\n\t},\n\thandler: async (\n\t\truntime: IAgentRuntime,\n\t\tmessage: Memory,\n\t\tstate?: State,\n\t\t_options?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tconst discordService = runtime.getService(\n\t\t\tDISCORD_SERVICE_NAME,\n\t\t) as DiscordService | null;\n\t\tif (!discordService?.client) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback({\n\t\t\t\t\ttext: \"Discord service isn't available right now.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"Discord service unavailable\" };\n\t\t}\n\n\t\tconst text = message.content.text?.trim() ?? \"\";\n\t\tconst userId = message.entityId as string;\n\t\tconst room = state?.data?.room || (await runtime.getRoom(message.roomId));\n\t\tconst channelId =\n\t\t\t((room as unknown as Record<string, unknown> | undefined)?.channelId as\n\t\t\t\t| string\n\t\t\t\t| undefined) || (message.roomId as string);\n\n\t\tcleanExpiredSessions();\n\n\t\tlet isDm = false;\n\t\ttry {\n\t\t\tconst channel = await discordService.client.channels.fetch(channelId);\n\t\t\tisDm =\n\t\t\t\tchannel?.type === ChannelType.DM ||\n\t\t\t\tchannel?.type === ChannelType.GroupDM;\n\t\t} catch {\n\t\t\tisDm = false;\n\t\t}\n\n\t\tif (!isDm && !activeSessions.has(userId)) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback({\n\t\t\t\t\ttext: \"Let's do this in DMs for security. I'll message you there.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst discordUser = await discordService.client.users.fetch(userId);\n\t\t\t\tconst dmChannel = await discordUser.createDM();\n\t\t\t\tconst detectedService = detectSetupIntent(text);\n\t\t\t\tif (detectedService && presets.has(detectedService)) {\n\t\t\t\t\tconst preset = presets.get(detectedService);\n\t\t\t\t\tif (!preset) {\n\t\t\t\t\t\tawait dmChannel.send(buildServiceListMessage());\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: `Unsupported credential preset: ${detectedService}`,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tactiveSessions.set(userId, {\n\t\t\t\t\t\tpreset,\n\t\t\t\t\t\tcurrentFieldIndex: 0,\n\t\t\t\t\t\tcollected: {},\n\t\t\t\t\t\tchannelId: dmChannel.id,\n\t\t\t\t\t\tstartedAt: Date.now(),\n\t\t\t\t\t});\n\t\t\t\t\tconst firstField = preset.fields[0];\n\t\t\t\t\tconst helpLine = preset.helpUrl\n\t\t\t\t\t\t? `Here's where to get one: ${preset.helpUrl}`\n\t\t\t\t\t\t: \"\";\n\t\t\t\t\tawait dmChannel.send(\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t`Setting up **${preset.displayName}** credentials.`,\n\t\t\t\t\t\t\tpreset.helpText,\n\t\t\t\t\t\t\thelpLine,\n\t\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\t`Please paste your **${firstField.label}** here. ${firstField.secret ? \"I'll delete your message right after reading it.\" : \"\"}`,\n\t\t\t\t\t\t]\n\t\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t\t.join(\"\\n\"),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tawait dmChannel.send(buildServiceListMessage());\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"setup-credentials\",\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t},\n\t\t\t\t\t\"Could not open DM with user\",\n\t\t\t\t);\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback({\n\t\t\t\t\t\ttext: \"I couldn't send you a DM. Make sure your DMs are open, then try again.\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\ttext: \"Redirected credential setup to DMs\",\n\t\t\t\tdata: terminalActionResultData(),\n\t\t\t};\n\t\t}\n\n\t\tif (activeSessions.has(userId)) {\n\t\t\tconst session = activeSessions.get(userId);\n\t\t\tif (!session) {\n\t\t\t\treturn { success: false, error: \"Credential session not found\" };\n\t\t\t}\n\t\t\tconst currentField = session.preset.fields[session.currentFieldIndex];\n\n\t\t\tif (currentField.secret) {\n\t\t\t\tconst deletionTarget = resolveDeletionTarget(message, channelId);\n\t\t\t\tif (deletionTarget.messageId) {\n\t\t\t\t\tconst deleted = await tryDeleteMessage(\n\t\t\t\t\t\tdiscordService,\n\t\t\t\t\t\tdeletionTarget.channelId,\n\t\t\t\t\t\tdeletionTarget.messageId,\n\t\t\t\t\t\tcurrentField.label,\n\t\t\t\t\t);\n\t\t\t\t\tif (!deleted) {\n\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsrc: \"setup-credentials\",\n\t\t\t\t\t\t\t\tfieldName: currentField.label,\n\t\t\t\t\t\t\t\tchannelId: deletionTarget.channelId,\n\t\t\t\t\t\t\t\tmessageId: deletionTarget.messageId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"Credential message could not be deleted automatically\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsession.collected[currentField.key] = text;\n\t\t\tsession.currentFieldIndex += 1;\n\n\t\t\tif (session.currentFieldIndex < session.preset.fields.length) {\n\t\t\t\tconst nextField = session.preset.fields[session.currentFieldIndex];\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback({\n\t\t\t\t\t\ttext: `Got it. Now paste your **${nextField.label}**${nextField.secret ? \" (I'll delete your message)\" : \"\"}.`,\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\ttext: \"Collecting next credential field\",\n\t\t\t\t\tdata: terminalActionResultData(),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (callback) {\n\t\t\t\tawait callback({\n\t\t\t\t\ttext: \"Validating your credentials...\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst validation = await session.preset.validate(session.collected);\n\t\t\tif (validation.valid) {\n\t\t\t\tconst storageKey =\n\t\t\t\t\tsession.preset.name === \"generic\"\n\t\t\t\t\t\t? (session.collected.envName ?? \"custom\")\n\t\t\t\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t\t\t\t.replace(/[^a-z0-9_-]/g, \"_\")\n\t\t\t\t\t\t: session.preset.name;\n\t\t\t\tawait storeCredentials(storageKey, session.collected);\n\t\t\t\tactiveSessions.delete(userId);\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback({\n\t\t\t\t\t\ttext: `Connected${validation.identity ? ` as ${validation.identity}` : \"\"}. **${session.preset.displayName}** is ready.`,\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\ttext: \"Credentials stored\",\n\t\t\t\t\tdata: terminalActionResultData(),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tactiveSessions.delete(userId);\n\t\t\tif (callback) {\n\t\t\t\tawait callback({\n\t\t\t\t\ttext: `Validation failed: ${validation.error ?? \"unknown error\"}. Please check your credentials and try again with \\`/setup ${session.preset.name}\\`.`,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: validation.error ?? \"Validation failed\",\n\t\t\t};\n\t\t}\n\n\t\tconst detectedService = detectSetupIntent(text);\n\t\tif (detectedService && presets.has(detectedService)) {\n\t\t\tconst preset = presets.get(detectedService);\n\t\t\tif (!preset) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: `Unsupported credential preset: ${detectedService}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tactiveSessions.set(userId, {\n\t\t\t\tpreset,\n\t\t\t\tcurrentFieldIndex: 0,\n\t\t\t\tcollected: {},\n\t\t\t\tchannelId,\n\t\t\t\tstartedAt: Date.now(),\n\t\t\t});\n\n\t\t\tconst firstField = preset.fields[0];\n\t\t\tconst helpLine = preset.helpUrl\n\t\t\t\t? `Here's where to get one: ${preset.helpUrl}`\n\t\t\t\t: \"\";\n\t\t\tif (callback) {\n\t\t\t\tawait callback({\n\t\t\t\t\ttext: [\n\t\t\t\t\t\t`Setting up **${preset.displayName}** credentials.`,\n\t\t\t\t\t\tpreset.helpText,\n\t\t\t\t\t\thelpLine,\n\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t`Please paste your **${firstField.label}** here. ${firstField.secret ? \"I'll delete your message right after reading it.\" : \"\"}`,\n\t\t\t\t\t]\n\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t.join(\"\\n\"),\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\ttext: `Started ${preset.displayName} setup`,\n\t\t\t\tdata: terminalActionResultData(),\n\t\t\t};\n\t\t}\n\n\t\tconst serviceName = text.toLowerCase().trim();\n\t\tif (presets.has(serviceName) || serviceName === \"custom\") {\n\t\t\tconst presetKey = serviceName === \"custom\" ? \"generic\" : serviceName;\n\t\t\tconst preset = presets.get(presetKey);\n\t\t\tif (!preset) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: `Unsupported credential preset: ${presetKey}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tactiveSessions.set(userId, {\n\t\t\t\tpreset,\n\t\t\t\tcurrentFieldIndex: 0,\n\t\t\t\tcollected: {},\n\t\t\t\tchannelId,\n\t\t\t\tstartedAt: Date.now(),\n\t\t\t});\n\t\t\tconst firstField = preset.fields[0];\n\t\t\tconst helpLine = preset.helpUrl\n\t\t\t\t? `Here's where to get one: ${preset.helpUrl}`\n\t\t\t\t: \"\";\n\t\t\tif (callback) {\n\t\t\t\tawait callback({\n\t\t\t\t\ttext: [\n\t\t\t\t\t\t`Setting up **${preset.displayName}** credentials.`,\n\t\t\t\t\t\tpreset.helpText,\n\t\t\t\t\t\thelpLine,\n\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t`Please paste your **${firstField.label}** here. ${firstField.secret ? \"I'll delete your message right after reading it.\" : \"\"}`,\n\t\t\t\t\t]\n\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t.join(\"\\n\"),\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\ttext: `Started ${preset.displayName} setup`,\n\t\t\t\tdata: terminalActionResultData(),\n\t\t\t};\n\t\t}\n\n\t\tif (callback) {\n\t\t\tawait callback({\n\t\t\t\ttext: buildServiceListMessage(),\n\t\t\t\tsource: \"discord\",\n\t\t\t});\n\t\t}\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\ttext: \"Showed credential setup service list\",\n\t\t\tdata: terminalActionResultData(),\n\t\t};\n\t},\n\texamples: [\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{user1}}\",\n\t\t\t\tcontent: { text: \"setup github\" },\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{agentName}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Setting up **GitHub** credentials.\\nCreate a fine-grained PAT at the link above.\\nHere's where to get one: https://github.com/settings/tokens\\n\\nPlease paste your **Personal Access Token** here. I'll delete your message right after reading it.\",\n\t\t\t\t\taction: \"SETUP_CREDENTIALS\",\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{user1}}\",\n\t\t\t\tcontent: { text: \"add my vercel key\" },\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{agentName}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Setting up **Vercel** credentials.\\nCreate a token at the link above.\\nHere's where to get one: https://vercel.com/account/tokens\\n\\nPlease paste your **API Token** here. I'll delete your message right after reading it.\",\n\t\t\t\t\taction: \"SETUP_CREDENTIALS\",\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t{\n\t\t\t\tname: \"{{user1}}\",\n\t\t\t\tcontent: { text: \"/setup\" },\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"{{agentName}}\",\n\t\t\t\tcontent: {\n\t\t\t\t\ttext: \"Which service do you want to set up? I support GitHub, Vercel, Cloudflare, Anthropic, OpenAI, fal.ai, or a custom credential.\",\n\t\t\t\t\taction: \"SETUP_CREDENTIALS\",\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t] as ActionExample[][],\n};\n\nexport default setupCredentials;\n",
|
|
28
|
+
"import fs from \"node:fs\";\nimport {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\ttype Content,\n\tContentType,\n\tcomposePromptFromState,\n\tgetEntityDetails,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\tlogger,\n\ttype Media,\n\ttype Memory,\n\tMemoryType,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype State,\n\tsplitChunks,\n\ttrimTokens,\n} from \"@elizaos/core\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\n\n/**\n * Normalizes a numeric timestamp to milliseconds.\n * Detects whether the input is likely in seconds or milliseconds based on magnitude.\n *\n * Heuristic: Unix timestamps in seconds are ~10 digits (e.g., 1703001600 for 2023)\n * Unix timestamps in milliseconds are ~13 digits (e.g., 1703001600000 for 2023)\n * We use a threshold: if the number represents a date before year 2000 when interpreted\n * as milliseconds, it's likely in seconds and needs conversion.\n *\n * @param {number} timestamp - The numeric timestamp to normalize\n * @returns {number} Timestamp in milliseconds\n */\nfunction normalizeTimestamp(timestamp: number): number {\n\t// Threshold: Jan 1, 2000 in milliseconds = 946684800000\n\t// If timestamp is less than this, it's likely in seconds (or an invalid/ancient date)\n\t// A Unix timestamp in seconds for year 2000+ would be > 946684800 (~10 digits)\n\t// which when treated as ms would be < Jan 12, 1970\n\tconst year2000InMs = 946684800000;\n\n\tif (timestamp > 0 && timestamp < year2000InMs) {\n\t\t// Likely in seconds - convert to milliseconds\n\t\t// Additional sanity check: result should be a reasonable date (after 2000, before 2100)\n\t\tconst asMs = timestamp * 1000;\n\t\tconst year2100InMs = 4102444800000;\n\t\tif (asMs >= year2000InMs && asMs <= year2100InMs) {\n\t\t\treturn asMs;\n\t\t}\n\t}\n\n\treturn timestamp;\n}\n\n/**\n * Parses various time formats into a Unix timestamp (milliseconds).\n * Supports:\n * - Absolute timestamps (number or numeric string): 1234567890000 or 1234567890 (auto-detects seconds vs ms)\n * - Relative time strings: \"5 minutes ago\", \"2 hours ago\", \"3 days ago\"\n * - ISO date strings: \"2024-01-15T10:30:00Z\"\n *\n * Month and year calculations use approximate values (30/365 days) to ensure inclusive time ranges.\n *\n * @param {string | number} input - The time value to parse\n * @returns {number} Unix timestamp in milliseconds\n */\nfunction parseTimeToTimestamp(input: string | number): number {\n\t// If already a number, normalize and return\n\tif (typeof input === \"number\") {\n\t\treturn normalizeTimestamp(input);\n\t}\n\n\t// Try parsing as a direct numeric string (timestamp)\n\tconst asNumber = Number(input);\n\tif (!Number.isNaN(asNumber) && asNumber > 0) {\n\t\treturn normalizeTimestamp(asNumber);\n\t}\n\n\t// Try parsing as ISO date\n\tconst isoDate = Date.parse(input);\n\tif (!Number.isNaN(isoDate)) {\n\t\treturn isoDate;\n\t}\n\n\t// Parse relative time format: \"<number> <unit> ago\", e.g. \"5 minutes ago\", \"2 hours ago\"\n\tconst relativeMatch = input.match(\n\t\t/(\\d+\\.?\\d*)\\s*(second|minute|hour|day|week|month|year)s?\\s+ago/i,\n\t);\n\tif (relativeMatch) {\n\t\tconst value = parseFloat(relativeMatch[1]);\n\t\tconst unit = relativeMatch[2].toLowerCase();\n\n\t\t// Approximate multipliers for time units\n\t\t// Month = 30 days, Year = 365 days (no leap year handling)\n\t\t// This provides consistent, inclusive time ranges for conversation retrieval\n\t\tconst multipliers: Record<string, number> = {\n\t\t\tsecond: 1000,\n\t\t\tminute: 60 * 1000,\n\t\t\thour: 3600 * 1000,\n\t\t\tday: 86400 * 1000,\n\t\t\tweek: 7 * 86400 * 1000,\n\t\t\tmonth: 30 * 86400 * 1000, // Approximation: actual months vary 28-31 days\n\t\t\tyear: 365 * 86400 * 1000, // Approximation: ignores leap years\n\t\t};\n\n\t\tconst milliseconds = value * (multipliers[unit] || 0);\n\n\t\t// \"<number> <unit> ago\" means subtract from now\n\t\treturn Date.now() - milliseconds;\n\t}\n\n\t// Fallback: return current time if we can't parse\n\t// Log warning for malformed model output\n\tlogger.warn(\n\t\t`[parseTimeToTimestamp] Could not parse time value, using current time: ${input}`,\n\t);\n\treturn Date.now();\n}\n\n// Import generated prompts\nimport {\n\tdateRangeTemplate,\n\tsummarizationTemplate,\n} from \"../generated/prompts/typescript/prompts.js\";\n\n/**\n * Function to get a date range from user input.\n *\n * @param {IAgentRuntime} runtime - The Agent Runtime object.\n * @param {Memory} _message - The Memory object.\n * @param {State} state - The State object.\n * @return {Promise<{ objective: string; start: number; end: number; } | null>} Parsed user input containing objective, start, and end timestamps, or null.\n */\nconst getDateRange = async (\n\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n): Promise<{ objective: string; start: number; end: number } | null> => {\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(ModelType.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\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// Parse start and end into proper timestamps (returns numbers)\n\t\t\t\tconst startRaw = parseTimeToTimestamp(parsedResponse.start);\n\t\t\t\tconst endRaw = parseTimeToTimestamp(parsedResponse.end);\n\n\t\t\t\t// Validate that both timestamps are finite numbers\n\t\t\t\tif (!Number.isFinite(startRaw) || !Number.isFinite(endRaw)) {\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`[getDateRange] Invalid timestamps parsed: start=${startRaw}, end=${endRaw}, retrying...`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Normalize: ensure start <= end (swap if model returned them inverted)\n\t\t\t\tlet start = startRaw <= endRaw ? startRaw : endRaw;\n\t\t\t\tconst end = startRaw <= endRaw ? endRaw : startRaw;\n\n\t\t\t\t// If start === end, widen the window by 1 hour to avoid empty queries\n\t\t\t\tif (start === end) {\n\t\t\t\t\tstart = end - 3600 * 1000; // 1 hour before end\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tobjective: parsedResponse.objective,\n\t\t\t\t\tstart,\n\t\t\t\t\tend,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\treturn null;\n};\n\n/**\n * Action to summarize a conversation and attachments.\n *\n * @typedef {Action} summarizeAction\n * @property {string} name - The name of the action.\n * @property {string[]} similes - Array of related terms.\n * @property {string} description - Description of the action.\n * @property {Function} validate - Asynchronous function to validate the action.\n * @property {Function} handler - Asynchronous function to handle the action.\n * @property {ActionExample[][]} examples - Array of examples demonstrating the action.\n */\nconst spec = requireActionSpec(\"SUMMARIZE_CONVERSATION\");\n\nexport const summarize: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\tvalidate: async (\n\t\truntime: any,\n\t\tmessage: any,\n\t\tstate?: any,\n\t\toptions?: any,\n\t): Promise<boolean> => {\n\t\tconst __avTextRaw =\n\t\t\ttypeof message?.content?.text === \"string\" ? message.content.text : \"\";\n\t\tconst __avText = __avTextRaw.toLowerCase();\n\t\tconst __avKeywords = [\"summarize\", \"conversation\"];\n\t\tconst __avKeywordOk =\n\t\t\t__avKeywords.length > 0 &&\n\t\t\t__avKeywords.some((word) => word.length > 0 && __avText.includes(word));\n\t\tconst __avRegex = /\\b(?:summarize|conversation)\\b/i;\n\t\tconst __avRegexOk = __avRegex.test(__avText);\n\t\tconst __avSource = String(message?.content?.source ?? \"\");\n\t\tconst __avExpectedSource = \"\";\n\t\tconst __avSourceOk = __avExpectedSource\n\t\t\t? __avSource === __avExpectedSource\n\t\t\t: Boolean(\n\t\t\t\t\t__avSource ||\n\t\t\t\t\t\tstate ||\n\t\t\t\t\t\truntime?.agentId ||\n\t\t\t\t\t\truntime?.getService ||\n\t\t\t\t\t\truntime?.getSetting,\n\t\t\t\t);\n\t\tconst __avOptions = options && typeof options === \"object\" ? options : {};\n\t\tconst __avInputOk =\n\t\t\t__avText.trim().length > 0 ||\n\t\t\tObject.keys(__avOptions as Record<string, unknown>).length > 0 ||\n\t\t\tBoolean(message?.content && typeof message.content === \"object\");\n\n\t\tif (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst __avLegacyValidate = async (\n\t\t\t_runtime: IAgentRuntime,\n\t\t\tmessage: Memory,\n\t\t\t_state?: State,\n\t\t): Promise<boolean> => {\n\t\t\tif (message.content.source !== \"discord\") {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// only show if one of the keywords are in the message\n\t\t\tconst keywords: string[] = [\n\t\t\t\t\"summarize\",\n\t\t\t\t\"summarization\",\n\t\t\t\t\"summary\",\n\t\t\t\t\"recap\",\n\t\t\t\t\"report\",\n\t\t\t\t\"overview\",\n\t\t\t\t\"review\",\n\t\t\t\t\"rundown\",\n\t\t\t\t\"wrap-up\",\n\t\t\t\t\"brief\",\n\t\t\t\t\"debrief\",\n\t\t\t\t\"abstract\",\n\t\t\t\t\"synopsis\",\n\t\t\t\t\"outline\",\n\t\t\t\t\"digest\",\n\t\t\t\t\"abridgment\",\n\t\t\t\t\"condensation\",\n\t\t\t\t\"encapsulation\",\n\t\t\t\t\"essence\",\n\t\t\t\t\"gist\",\n\t\t\t\t\"main points\",\n\t\t\t\t\"key points\",\n\t\t\t\t\"key takeaways\",\n\t\t\t\t\"bulletpoint\",\n\t\t\t\t\"highlights\",\n\t\t\t\t\"tldr\",\n\t\t\t\t\"tl;dr\",\n\t\t\t\t\"in a nutshell\",\n\t\t\t\t\"bottom line\",\n\t\t\t\t\"long story short\",\n\t\t\t\t\"sum up\",\n\t\t\t\t\"sum it up\",\n\t\t\t\t\"short version\",\n\t\t\t\t\"bring me up to speed\",\n\t\t\t\t\"catch me up\",\n\t\t\t];\n\t\t\treturn keywords.some((keyword) =>\n\t\t\t\tmessage.content.text?.toLowerCase().includes(keyword.toLowerCase()),\n\t\t\t);\n\t\t};\n\t\ttry {\n\t\t\treturn Boolean(\n\t\t\t\tawait (__avLegacyValidate as any)(runtime, message, state, options),\n\t\t\t);\n\t\t} catch {\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?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tif (!state) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"State is not available.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"State is not available\" };\n\t\t}\n\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\truntime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:summarize-conversation\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t},\n\t\t\t\t\"Could not get date range from message\",\n\t\t\t);\n\t\t\tawait runtime.createMemory(\n\t\t\t\t{\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 couldn't get the date range from the message\",\n\t\t\t\t\t\tactions: [\"SUMMARIZE_CONVERSATION_FAILED\"],\n\t\t\t\t\t},\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\ttype: MemoryType.CUSTOM,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"messages\",\n\t\t\t);\n\t\t\treturn { success: false, error: \"Could not get date range from message\" };\n\t\t}\n\n\t\tconst { objective, start, end } = dateRange;\n\n\t\t// 2. get these memories from the database (start/end are absolute ms timestamps)\n\t\tconst memories = await runtime.getMemories({\n\t\t\ttableName: \"messages\",\n\t\t\troomId,\n\t\t\tstart,\n\t\t\tend,\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})) as Array<{ id: string; name?: string; names: string[] }>;\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 memoryAttachments = memory.content.attachments;\n\t\t\t\tconst attachments =\n\t\t\t\t\tmemoryAttachments\n\t\t\t\t\t\t?.map((attachment: Media) => {\n\t\t\t\t\t\t\treturn `---\\nAttachment: ${attachment.id}\\n${attachment.description}\\n${attachment.text}\\n---`;\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.join(\"\\n\") || \"\";\n\t\t\t\tconst entity = actorMap.get(memory.entityId);\n\t\t\t\tconst entityName = entity?.name ?? entity?.names?.[0] ?? \"Unknown User\";\n\t\t\t\tconst entityUsername = \"\";\n\t\t\t\treturn `${entityName} (${entityUsername}): ${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\t//const _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(ModelType.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\truntime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:summarize-conversation\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t},\n\t\t\t\t\"No summary found\",\n\t\t\t);\n\t\t\tawait runtime.createMemory(\n\t\t\t\t{\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 couldn't summarize the conversation\",\n\t\t\t\t\t\tactions: [\"SUMMARIZE_CONVERSATION_FAILED\"],\n\t\t\t\t\t},\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\ttype: MemoryType.CUSTOM,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"messages\",\n\t\t\t);\n\t\t\treturn { success: false, error: \"Could not summarize conversation\" };\n\t\t}\n\n\t\tcallbackData.text = currentSummary.trim();\n\t\tconst trimmedSummary = currentSummary.trim();\n\t\tif (\n\t\t\tcallbackData.text &&\n\t\t\t((trimmedSummary && trimmedSummary.split(\"\\n\").length < 4) ||\n\t\t\t\t(trimmedSummary && trimmedSummary.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\tif (callback) {\n\t\t\t\tawait callback?.(callbackData);\n\t\t\t}\n\t\t\treturn { success: true, text: callbackData.text };\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.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\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\t...callbackData,\n\t\t\t\t\ttext: `I've attached the summary of the conversation from \\`${new Date(start).toString()}\\` to \\`${new Date(end).toString()}\\` as a text file.`,\n\t\t\t\t\tattachments: [\n\t\t\t\t\t\t...(callbackData.attachments || []),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: summaryFilename,\n\t\t\t\t\t\t\turl: summaryFilename,\n\t\t\t\t\t\t\ttitle: \"Conversation Summary\",\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t\tcontentType: ContentType.DOCUMENT,\n\t\t\t\t\t\t} as Media,\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: true, text: `Summary saved to ${summaryFilename}` };\n\t\t} else {\n\t\t\truntime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:summarize-conversation\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t},\n\t\t\t\t\"Empty response from summarize conversation action\",\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"Empty response from summarize conversation action\",\n\t\t\t};\n\t\t}\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default summarize;\n",
|
|
29
|
+
"import {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\ttype Content,\n\tContentType,\n\tcomposePromptFromState,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\ttype Media,\n\ttype Memory,\n\tMemoryType,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype State,\n} from \"@elizaos/core\";\nimport { mediaAttachmentIdTemplate } from \"../generated/prompts/typescript/prompts.js\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\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(ModelType.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\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 spec = requireActionSpec(\"TRANSCRIBE_MEDIA\");\n\nexport const transcribeMedia: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\tvalidate: async (\n\t\truntime: any,\n\t\tmessage: any,\n\t\tstate?: any,\n\t\toptions?: any,\n\t): Promise<boolean> => {\n\t\tconst __avTextRaw =\n\t\t\ttypeof message?.content?.text === \"string\" ? message.content.text : \"\";\n\t\tconst __avText = __avTextRaw.toLowerCase();\n\t\tconst __avKeywords = [\"transcribe\", \"media\"];\n\t\tconst __avKeywordOk =\n\t\t\t__avKeywords.length > 0 &&\n\t\t\t__avKeywords.some((word) => word.length > 0 && __avText.includes(word));\n\t\tconst __avRegex = /\\b(?:transcribe|media)\\b/i;\n\t\tconst __avRegexOk = __avRegex.test(__avText);\n\t\tconst __avSource = String(message?.content?.source ?? \"\");\n\t\tconst __avExpectedSource = \"\";\n\t\tconst __avSourceOk = __avExpectedSource\n\t\t\t? __avSource === __avExpectedSource\n\t\t\t: Boolean(\n\t\t\t\t\t__avSource ||\n\t\t\t\t\t\tstate ||\n\t\t\t\t\t\truntime?.agentId ||\n\t\t\t\t\t\truntime?.getService ||\n\t\t\t\t\t\truntime?.getSetting,\n\t\t\t\t);\n\t\tconst __avOptions = options && typeof options === \"object\" ? options : {};\n\t\tconst __avInputOk =\n\t\t\t__avText.trim().length > 0 ||\n\t\t\tObject.keys(__avOptions as Record<string, unknown>).length > 0 ||\n\t\t\tBoolean(message?.content && typeof message.content === \"object\");\n\n\t\tif (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst __avLegacyValidate = async (\n\t\t\t_runtime: IAgentRuntime,\n\t\t\tmessage: Memory,\n\t\t\t_state?: State,\n\t\t): Promise<boolean> => {\n\t\t\tif (message.content.source !== \"discord\") {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst keywords: string[] = [\n\t\t\t\t\"transcribe\",\n\t\t\t\t\"transcript\",\n\t\t\t\t\"audio\",\n\t\t\t\t\"video\",\n\t\t\t\t\"media\",\n\t\t\t\t\"youtube\",\n\t\t\t\t\"meeting\",\n\t\t\t\t\"recording\",\n\t\t\t\t\"podcast\",\n\t\t\t\t\"call\",\n\t\t\t\t\"conference\",\n\t\t\t\t\"interview\",\n\t\t\t\t\"speech\",\n\t\t\t\t\"lecture\",\n\t\t\t\t\"presentation\",\n\t\t\t];\n\t\t\treturn keywords.some((keyword) =>\n\t\t\t\tmessage.content.text?.toLowerCase().includes(keyword.toLowerCase()),\n\t\t\t);\n\t\t};\n\t\ttry {\n\t\t\treturn Boolean(\n\t\t\t\tawait (__avLegacyValidate as any)(runtime, message, state, options),\n\t\t\t);\n\t\t} catch {\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?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\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\truntime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:transcribe-media\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t},\n\t\t\t\t\"Could not get media attachment ID from message\",\n\t\t\t);\n\t\t\tawait runtime.createMemory(\n\t\t\t\t{\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 couldn't find the media attachment ID in the message\",\n\t\t\t\t\t\tactions: [\"TRANSCRIBE_MEDIA_FAILED\"],\n\t\t\t\t\t},\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\ttype: MemoryType.CUSTOM,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"messages\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst conversationLength = runtime.getConversationLength();\n\n\t\tconst recentMessages = await runtime.getMemories({\n\t\t\ttableName: \"messages\",\n\t\t\troomId: message.roomId,\n\t\t\tcount: conversationLength,\n\t\t\tunique: false,\n\t\t});\n\n\t\tconst attachment = 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 &&\n\t\t\t\t\tattachment.id.toLowerCase() === attachmentId.toLowerCase(),\n\t\t\t);\n\n\t\tif (!attachment) {\n\t\t\truntime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:transcribe-media\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\tattachmentId,\n\t\t\t\t},\n\t\t\t\t\"Could not find attachment\",\n\t\t\t);\n\t\t\tawait runtime.createMemory(\n\t\t\t\t{\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 couldn't find the media attachment with ID ${attachmentId}`,\n\t\t\t\t\t\tactions: [\"TRANSCRIBE_MEDIA_FAILED\"],\n\t\t\t\t\t},\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\ttype: MemoryType.CUSTOM,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"messages\",\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.setCache<string>(transcriptFilename, callbackData.text);\n\n\t\t\tawait callback?.({\n\t\t\t\t...callbackData,\n\t\t\t\ttext: \"I've attached the transcript as a text file.\",\n\t\t\t\tattachments: [\n\t\t\t\t\t...(callbackData.attachments || []),\n\t\t\t\t\t{\n\t\t\t\t\t\tid: transcriptFilename,\n\t\t\t\t\t\turl: transcriptFilename,\n\t\t\t\t\t\ttitle: \"Transcript\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\tcontentType: ContentType.DOCUMENT,\n\t\t\t\t\t} as Media,\n\t\t\t\t],\n\t\t\t});\n\t\t} else {\n\t\t\truntime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:transcribe-media\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t},\n\t\t\t\t\"Empty response from transcribe media action\",\n\t\t\t);\n\t\t}\n\n\t\treturn { success: true, text: callbackData.text };\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default transcribeMedia;\n",
|
|
30
|
+
"import {\n\ttype Action,\n\ttype ActionExample,\n\ttype ActionResult,\n\ttype Content,\n\tcomposePromptFromState,\n\ttype HandlerCallback,\n\ttype HandlerOptions,\n\ttype IAgentRuntime,\n\ttype Memory,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype State,\n} from \"@elizaos/core\";\nimport {\n\ttype Message,\n\tPermissionsBitField,\n\ttype TextChannel,\n} from \"discord.js\";\nimport { DISCORD_SERVICE_NAME } from \"../constants\";\nimport { unpinMessageTemplate } from \"../generated/prompts/typescript/prompts.js\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\nimport type { DiscordService } from \"../service\";\nimport {\n\tterminalActionInteractionSemantics,\n\tterminalActionResultData,\n} from \"./actionResultSemantics\";\n\nconst getMessageRef = async (\n\truntime: IAgentRuntime,\n\t_message: Memory,\n\tstate: State,\n): Promise<{\n\tmessageRef: string;\n} | null> => {\n\tconst prompt = composePromptFromState({\n\t\tstate,\n\t\ttemplate: unpinMessageTemplate,\n\t});\n\n\tfor (let i = 0; i < 3; i++) {\n\t\tconst response = await runtime.useModel(ModelType.TEXT_SMALL, {\n\t\t\tprompt,\n\t\t});\n\n\t\tconst parsedResponse = parseJSONObjectFromText(response);\n\t\tif (parsedResponse?.messageRef) {\n\t\t\treturn {\n\t\t\t\tmessageRef: String(parsedResponse.messageRef),\n\t\t\t};\n\t\t}\n\t}\n\treturn null;\n};\n\nconst spec = requireActionSpec(\"UNPIN_MESSAGE\");\n\nexport const unpinMessage: Action = {\n\tname: spec.name,\n\tsimiles: spec.similes ? [...spec.similes] : [],\n\tdescription: spec.description,\n\tdescriptionCompressed: spec.descriptionCompressed,\n\t...terminalActionInteractionSemantics,\n\tvalidate: async (\n\t\truntime: any,\n\t\tmessage: any,\n\t\tstate?: any,\n\t\toptions?: any,\n\t): Promise<boolean> => {\n\t\tconst __avTextRaw =\n\t\t\ttypeof message?.content?.text === \"string\" ? message.content.text : \"\";\n\t\tconst __avText = __avTextRaw.toLowerCase();\n\t\tconst __avKeywords = [\"unpin\", \"message\"];\n\t\tconst __avKeywordOk =\n\t\t\t__avKeywords.length > 0 &&\n\t\t\t__avKeywords.some((word) => word.length > 0 && __avText.includes(word));\n\t\tconst __avRegex = /\\b(?:unpin|message)\\b/i;\n\t\tconst __avRegexOk = __avRegex.test(__avText);\n\t\tconst __avSource = String(message?.content?.source ?? \"\");\n\t\tconst __avExpectedSource = \"\";\n\t\tconst __avSourceOk = __avExpectedSource\n\t\t\t? __avSource === __avExpectedSource\n\t\t\t: Boolean(\n\t\t\t\t\t__avSource ||\n\t\t\t\t\t\tstate ||\n\t\t\t\t\t\truntime?.agentId ||\n\t\t\t\t\t\truntime?.getService ||\n\t\t\t\t\t\truntime?.getSetting,\n\t\t\t\t);\n\t\tconst __avOptions = options && typeof options === \"object\" ? options : {};\n\t\tconst __avInputOk =\n\t\t\t__avText.trim().length > 0 ||\n\t\t\tObject.keys(__avOptions as Record<string, unknown>).length > 0 ||\n\t\t\tBoolean(message?.content && typeof message.content === \"object\");\n\n\t\tif (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst __avLegacyValidate = async (\n\t\t\t_runtime: IAgentRuntime,\n\t\t\tmessage: Memory,\n\t\t\t_state?: State,\n\t\t): Promise<boolean> => {\n\t\t\treturn message.content.source === \"discord\";\n\t\t};\n\t\ttry {\n\t\t\treturn Boolean(\n\t\t\t\tawait (__avLegacyValidate as any)(runtime, message, state, options),\n\t\t\t);\n\t\t} catch {\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?: HandlerOptions,\n\t\tcallback?: HandlerCallback,\n\t): Promise<ActionResult | undefined> => {\n\t\tconst discordService = runtime.getService(\n\t\t\tDISCORD_SERVICE_NAME,\n\t\t) as DiscordService;\n\n\t\tif (!discordService?.client) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"Discord service is not available.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"Discord service is not available\" };\n\t\t}\n\n\t\tif (!state) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"State is not available.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"State is not available\" };\n\t\t}\n\n\t\tconst messageInfo = await getMessageRef(runtime, message, state);\n\t\tif (!messageInfo) {\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I couldn't understand which message you want to unpin. Please be more specific.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { success: false, error: \"Could not parse message reference\" };\n\t\t}\n\n\t\ttry {\n\t\t\tconst room = state.data?.room || (await runtime.getRoom(message.roomId));\n\t\t\tif (!room?.channelId) {\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: \"I couldn't determine the current channel.\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn { success: false, error: \"Could not determine current channel\" };\n\t\t\t}\n\n\t\t\tconst channel = await discordService.client.channels.fetch(\n\t\t\t\troom.channelId,\n\t\t\t);\n\t\t\tif (!channel?.isTextBased()) {\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: \"I can only unpin messages in text channels.\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn { success: false, error: \"Channel is not a text channel\" };\n\t\t\t}\n\n\t\t\tconst textChannel = channel as TextChannel;\n\n\t\t\t// Check bot permissions\n\t\t\tconst clientUser = discordService.client.user;\n\t\t\tconst botMember = textChannel.guild?.members.cache.get(clientUser?.id);\n\t\t\tif (botMember) {\n\t\t\t\tconst permissions = textChannel.permissionsFor(botMember);\n\t\t\t\tif (\n\t\t\t\t\tpermissions &&\n\t\t\t\t\t!permissions.has(PermissionsBitField.Flags.ManageMessages)\n\t\t\t\t) {\n\t\t\t\t\tif (callback) {\n\t\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\t\ttext: \"I don't have permission to unpin messages in this channel. I need the 'Manage Messages' permission.\",\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn { success: false, error: \"Missing ManageMessages permission\" };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet targetMessage: Message | null = null;\n\n\t\t\t// Get pinned messages\n\t\t\tconst pinnedMessages = await textChannel.messages.fetchPinned();\n\n\t\t\tif (pinnedMessages.size === 0) {\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: \"There are no pinned messages in this channel.\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\ttext: \"No pinned messages in channel\",\n\t\t\t\t\tdata: terminalActionResultData(),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Find the target message\n\t\t\tif (\n\t\t\t\tmessageInfo.messageRef === \"last_pinned\" ||\n\t\t\t\tmessageInfo.messageRef === \"last\"\n\t\t\t) {\n\t\t\t\t// Get the most recently created pinned message (since we can't sort by pin time)\n\t\t\t\ttargetMessage = Array.from(pinnedMessages.values()).sort(\n\t\t\t\t\t(a, b) => b.createdTimestamp - a.createdTimestamp,\n\t\t\t\t)[0];\n\t\t\t} else if (/^\\d+$/.test(messageInfo.messageRef)) {\n\t\t\t\t// It's a message ID\n\t\t\t\ttargetMessage = pinnedMessages.get(messageInfo.messageRef) || null;\n\t\t\t} else {\n\t\t\t\t// Search for message by content/author in pinned messages\n\t\t\t\tconst searchLower = messageInfo.messageRef.toLowerCase();\n\n\t\t\t\ttargetMessage =\n\t\t\t\t\tArray.from(pinnedMessages.values()).find((msg) => {\n\t\t\t\t\t\tconst contentMatch = msg.content\n\t\t\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t\t\t.includes(searchLower);\n\t\t\t\t\t\tconst authorMatch = msg.author.username\n\t\t\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t\t\t.includes(searchLower);\n\t\t\t\t\t\treturn contentMatch || authorMatch;\n\t\t\t\t\t}) || null;\n\t\t\t}\n\n\t\t\tif (!targetMessage) {\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: \"I couldn't find a pinned message matching your description.\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: \"Could not find matching pinned message\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Unpin the message\n\t\t\ttry {\n\t\t\t\tawait targetMessage.unpin();\n\n\t\t\t\tconst response: Content = {\n\t\t\t\t\ttext: `I've unpinned the message from ${targetMessage.author.username}.`,\n\t\t\t\t\tsource: message.content.source,\n\t\t\t\t};\n\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.(response);\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\ttext: response.text,\n\t\t\t\t\tdata: terminalActionResultData(),\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\truntime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:action:unpin-message\",\n\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t},\n\t\t\t\t\t\"Failed to unpin message\",\n\t\t\t\t);\n\t\t\t\tif (callback) {\n\t\t\t\t\tawait callback?.({\n\t\t\t\t\t\ttext: \"I couldn't unpin that message. Please try again.\",\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t};\n\t\t\t}\n\t\t} catch (error) {\n\t\t\truntime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:action:unpin-message\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error unpinning message\",\n\t\t\t);\n\t\t\tif (callback) {\n\t\t\t\tawait callback?.({\n\t\t\t\t\ttext: \"I encountered an error while trying to unpin the message. Please make sure I have the necessary permissions.\",\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t},\n\texamples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default unpinMessage;\n",
|
|
31
|
+
"const Permissions = {\n\tAddReactions: 1n << 6n,\n\tPrioritySpeaker: 1n << 8n,\n\tStream: 1n << 9n,\n\tViewChannel: 1n << 10n,\n\tSendMessages: 1n << 11n,\n\tSendTTSMessages: 1n << 12n,\n\tManageMessages: 1n << 13n,\n\tEmbedLinks: 1n << 14n,\n\tAttachFiles: 1n << 15n,\n\tReadMessageHistory: 1n << 16n,\n\tMentionEveryone: 1n << 17n,\n\tUseExternalEmojis: 1n << 18n,\n\tConnect: 1n << 20n,\n\tSpeak: 1n << 21n,\n\tMuteMembers: 1n << 22n,\n\tDeafenMembers: 1n << 23n,\n\tMoveMembers: 1n << 24n,\n\tUseVAD: 1n << 25n,\n\tKickMembers: 1n << 1n,\n\tBanMembers: 1n << 2n,\n\tChangeNickname: 1n << 26n,\n\tManageNicknames: 1n << 27n,\n\tManageChannels: 1n << 4n,\n\tManageRoles: 1n << 28n,\n\tManageWebhooks: 1n << 29n,\n\tManageGuildExpressions: 1n << 30n,\n\tUseApplicationCommands: 1n << 31n,\n\tManageThreads: 1n << 34n,\n\tCreatePublicThreads: 1n << 35n,\n\tCreatePrivateThreads: 1n << 36n,\n\tUseExternalStickers: 1n << 37n,\n\tSendMessagesInThreads: 1n << 38n,\n\tUseEmbeddedActivities: 1n << 39n,\n\tModerateMembers: 1n << 40n,\n\tSendVoiceMessages: 1n << 46n,\n\tSendPolls: 1n << 47n,\n} as const;\n\nconst TEXT_BASIC =\n\tPermissions.ViewChannel |\n\tPermissions.AddReactions |\n\tPermissions.SendMessages |\n\tPermissions.EmbedLinks |\n\tPermissions.AttachFiles |\n\tPermissions.UseExternalEmojis |\n\tPermissions.ReadMessageHistory |\n\tPermissions.SendMessagesInThreads |\n\tPermissions.UseApplicationCommands;\n\nconst TEXT_MODERATOR =\n\tTEXT_BASIC |\n\tPermissions.ManageMessages |\n\tPermissions.MentionEveryone |\n\tPermissions.CreatePublicThreads |\n\tPermissions.CreatePrivateThreads |\n\tPermissions.ManageThreads |\n\tPermissions.UseExternalStickers |\n\tPermissions.SendPolls |\n\tPermissions.ModerateMembers;\n\nconst TEXT_ADMIN =\n\tTEXT_MODERATOR |\n\tPermissions.KickMembers |\n\tPermissions.BanMembers |\n\tPermissions.ManageNicknames |\n\tPermissions.ManageChannels |\n\tPermissions.ManageRoles |\n\tPermissions.ManageWebhooks |\n\tPermissions.ManageGuildExpressions;\n\nconst VOICE_ADDON =\n\tPermissions.Connect |\n\tPermissions.Speak |\n\tPermissions.UseVAD |\n\tPermissions.PrioritySpeaker |\n\tPermissions.Stream |\n\tPermissions.SendVoiceMessages;\n\nconst VOICE_ADMIN_ADDON =\n\tVOICE_ADDON |\n\tPermissions.MuteMembers |\n\tPermissions.DeafenMembers |\n\tPermissions.MoveMembers;\n\nexport const PERMISSIONS_BASIC = TEXT_BASIC;\nexport const PERMISSIONS_BASIC_VOICE = TEXT_BASIC | VOICE_ADDON;\nexport const PERMISSIONS_MODERATOR = TEXT_MODERATOR;\nexport const PERMISSIONS_MODERATOR_VOICE = TEXT_MODERATOR | VOICE_ADDON;\nexport const PERMISSIONS_ADMIN = TEXT_ADMIN;\nexport const PERMISSIONS_ADMIN_VOICE = TEXT_ADMIN | VOICE_ADMIN_ADDON;\n\nexport const DiscordPermissionTiers = {\n\tBASIC: Number(PERMISSIONS_BASIC),\n\tBASIC_VOICE: Number(PERMISSIONS_BASIC_VOICE),\n\tMODERATOR: Number(PERMISSIONS_MODERATOR),\n\tMODERATOR_VOICE: Number(PERMISSIONS_MODERATOR_VOICE),\n\tADMIN: Number(PERMISSIONS_ADMIN),\n\tADMIN_VOICE: Number(PERMISSIONS_ADMIN_VOICE),\n} as const;\n\nexport type DiscordPermissionTier = keyof typeof DiscordPermissionTiers;\n\nexport function generateInviteUrl(\n\tapplicationId: string,\n\ttier: DiscordPermissionTier = \"MODERATOR_VOICE\",\n): string {\n\tconst permissions = DiscordPermissionTiers[tier];\n\treturn `https://discord.com/api/oauth2/authorize?client_id=${applicationId}&permissions=${permissions}&scope=bot%20applications.commands`;\n}\n\nexport interface DiscordPermissionValues {\n\tbasic: number;\n\tbasicVoice: number;\n\tmoderator: number;\n\tmoderatorVoice: number;\n\tadmin: number;\n\tadminVoice: number;\n}\n\nexport function getPermissionValues(): DiscordPermissionValues {\n\treturn {\n\t\tbasic: DiscordPermissionTiers.BASIC,\n\t\tbasicVoice: DiscordPermissionTiers.BASIC_VOICE,\n\t\tmoderator: DiscordPermissionTiers.MODERATOR,\n\t\tmoderatorVoice: DiscordPermissionTiers.MODERATOR_VOICE,\n\t\tadmin: DiscordPermissionTiers.ADMIN,\n\t\tadminVoice: DiscordPermissionTiers.ADMIN_VOICE,\n\t};\n}\n\nexport interface DiscordInviteUrls {\n\tbasic: string;\n\tbasicVoice: string;\n\tmoderator: string;\n\tmoderatorVoice: string;\n\tadmin: string;\n\tadminVoice: string;\n}\n\nexport function generateAllInviteUrls(\n\tapplicationId: string,\n): DiscordInviteUrls {\n\treturn {\n\t\tbasic: generateInviteUrl(applicationId, \"BASIC\"),\n\t\tbasicVoice: generateInviteUrl(applicationId, \"BASIC_VOICE\"),\n\t\tmoderator: generateInviteUrl(applicationId, \"MODERATOR\"),\n\t\tmoderatorVoice: generateInviteUrl(applicationId, \"MODERATOR_VOICE\"),\n\t\tadmin: generateInviteUrl(applicationId, \"ADMIN\"),\n\t\tadminVoice: generateInviteUrl(applicationId, \"ADMIN_VOICE\"),\n\t};\n}\n\nexport const REQUIRED_PERMISSIONS = PERMISSIONS_MODERATOR_VOICE;\n",
|
|
32
|
+
"/**\n * Discord Plugin Settings Banner\n * Beautiful ANSI art display for configuration on startup\n * Includes tiered permission system for invite URLs\n */\n\nimport type { IAgentRuntime } from \"@elizaos/core\";\nimport {\n\ttype DiscordPermissionValues,\n\tgetPermissionValues,\n} from \"./permissions\";\n\nconst ANSI = {\n\treset: \"\\x1b[0m\",\n\tbold: \"\\x1b[1m\",\n\tdim: \"\\x1b[2m\",\n\tblue: \"\\x1b[34m\",\n\tbrightRed: \"\\x1b[91m\",\n\tbrightGreen: \"\\x1b[92m\",\n\tbrightYellow: \"\\x1b[93m\",\n\tbrightBlue: \"\\x1b[94m\",\n\tbrightMagenta: \"\\x1b[95m\",\n\tbrightCyan: \"\\x1b[96m\",\n\tbrightWhite: \"\\x1b[97m\",\n};\n\nexport interface PluginSetting {\n\tname: string;\n\tvalue: unknown;\n\tdefaultValue?: unknown;\n\tsensitive?: boolean;\n\trequired?: boolean;\n}\n\nexport interface BannerOptions {\n\tpluginName: string;\n\tdescription?: string;\n\tsettings: PluginSetting[];\n\truntime: IAgentRuntime;\n\t/** Discord Application ID for generating invite URLs */\n\tapplicationId?: string;\n\t/** Permission values for the 3x2 tier matrix */\n\tdiscordPermissions?: DiscordPermissionValues;\n}\n\nfunction mask(v: string): string {\n\tif (!v || v.length <= 8) {\n\t\treturn \"••••••••\";\n\t}\n\treturn `${v.slice(0, 4)}${\"•\".repeat(Math.min(12, v.length - 8))}${v.slice(-4)}`;\n}\n\n/**\n * Format a value for display in the banner.\n *\n * @param value - The value to format; may be `undefined`, `null`, or an empty string.\n * @param sensitive - Whether the value should be obfuscated for display.\n * @param maxLen - Maximum allowed length of the returned string; longer values are truncated with an ellipsis.\n * @returns A display string: `'(not set)'` if `value` is `undefined`, `null`, or an empty string; a masked representation if `sensitive` is true; otherwise the stringified value truncated to at most `maxLen` characters (truncated strings end with `'...'`).\n */\nfunction fmtVal(value: unknown, sensitive: boolean, maxLen: number): string {\n\tlet s: string;\n\tif (value === undefined || value === null || value === \"\") {\n\t\ts = \"(not set)\";\n\t} else if (sensitive) {\n\t\ts = mask(String(value));\n\t} else {\n\t\ts = String(value);\n\t}\n\tif (s.length > maxLen) {\n\t\ts = `${s.slice(0, maxLen - 3)}...`;\n\t}\n\treturn s;\n}\n\n// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape codes are required for terminal formatting\nconst ANSI_PATTERN = /\\x1b\\[[0-9;]*m/g;\n\n/**\n * Pads a string with trailing spaces until its visible (ANSI-stripped) length is at least the given width.\n *\n * @param s - The input string which may contain ANSI escape sequences.\n * @param n - The target visible width (number of characters) after padding.\n * @returns The original string if its visible length is >= `n`, otherwise the string with trailing spaces appended so its visible length equals `n`.\n */\nfunction pad(s: string, n: number): string {\n\tconst len = s.replace(ANSI_PATTERN, \"\").length;\n\tif (len >= n) {\n\t\treturn s;\n\t}\n\treturn s + \" \".repeat(n - len);\n}\n\nfunction line(content: string): string {\n\tconst len = content.replace(ANSI_PATTERN, \"\").length;\n\n\tif (len <= 78) {\n\t\treturn content + \" \".repeat(78 - len);\n\t}\n\n\t// Truncate based on visible character count, not raw string position\n\t// This avoids cutting in the middle of ANSI escape sequences\n\tlet visibleCount = 0;\n\tlet result = \"\";\n\tlet i = 0;\n\n\twhile (i < content.length && visibleCount < 78) {\n\t\tconst remaining = content.slice(i);\n\t\t// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape codes are required for terminal formatting\n\t\tconst match = remaining.match(/^\\x1b\\[[0-9;]*m/);\n\n\t\tif (match) {\n\t\t\t// Include ANSI sequence without counting toward visible length\n\t\t\tresult += match[0];\n\t\t\ti += match[0].length;\n\t\t} else {\n\t\t\t// Regular visible character\n\t\t\tresult += content[i];\n\t\t\tvisibleCount++;\n\t\t\ti++;\n\t\t}\n\t}\n\n\t// Reset any unclosed ANSI sequences after truncation\n\treturn result + ANSI.reset;\n}\n\n/**\n * Render a framed ANSI banner that displays plugin settings and, when available, tiered Discord invite URLs.\n *\n * The banner lists each setting with masked or truncated values, a status (custom/default/unset/required),\n * and an optional Discord invite section generated from `applicationId` and `discordPermissions`.\n *\n * @param options - Configuration for the banner, including `settings`, the `runtime` used to emit the banner,\n * and optional Discord invite data (`applicationId`, `discordPermissions`).\n */\nexport function printBanner(options: BannerOptions): void {\n\tconst { settings, runtime } = options;\n\tconst R = ANSI.reset,\n\t\tD = ANSI.dim,\n\t\tB = ANSI.bold;\n\tconst c1 = ANSI.brightBlue,\n\t\tc2 = ANSI.brightCyan,\n\t\tc3 = ANSI.brightMagenta;\n\n\tconst top = `${c1}╔${\"═\".repeat(78)}╗${R}`;\n\tconst mid = `${c1}╠${\"═\".repeat(78)}╣${R}`;\n\tconst bot = `${c1}╚${\"═\".repeat(78)}╝${R}`;\n\tconst row = (s: string) => `${c1}║${R}${line(s)}${c1}║${R}`;\n\n\tconst lines: string[] = [\"\"];\n\tlines.push(top);\n\tlines.push(row(` ${B}Character: ${runtime.character.name}${R}`));\n\tlines.push(mid);\n\tlines.push(\n\t\trow(\n\t\t\t`${c2} ██████╗ ██╗███████╗ ██████╗ ██████╗ ██████╗ ██████╗ ${c3}◖ ◗${R}`,\n\t\t),\n\t);\n\tlines.push(\n\t\trow(\n\t\t\t`${c2} ██╔══██╗██║██╔════╝██╔════╝██╔═══██╗██╔══██╗██╔══██╗ ${c3}◖===◗${R}`,\n\t\t),\n\t);\n\tlines.push(\n\t\trow(\n\t\t\t`${c2} ██║ ██║██║███████╗██║ ██║ ██║██████╔╝██║ ██║ ${c3}╰─╯${R}`,\n\t\t),\n\t);\n\tlines.push(\n\t\trow(\n\t\t\t`${c2} ██████╔╝██║╚════██║╚██████╗╚██████╔╝██║ ██║██████╔╝ ${c3}(◠◠)${R}`,\n\t\t),\n\t);\n\tlines.push(\n\t\trow(\n\t\t\t`${c2} ╚═════╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ${c3}‿‿${R}`,\n\t\t),\n\t);\n\tlines.push(\n\t\trow(\n\t\t\t`${D} Bot Integration • Servers • Channels • Voice${R}`,\n\t\t),\n\t);\n\tlines.push(mid);\n\n\tconst NW = 34,\n\t\tVW = 26,\n\t\tSW = 8;\n\tlines.push(\n\t\trow(\n\t\t\t` ${B}${pad(\"ENV VARIABLE\", NW)} ${pad(\"VALUE\", VW)} ${pad(\"STATUS\", SW)}${R}`,\n\t\t),\n\t);\n\tlines.push(\n\t\trow(` ${D}${\"-\".repeat(NW)} ${\"-\".repeat(VW)} ${\"-\".repeat(SW)}${R}`),\n\t);\n\n\tfor (const s of settings) {\n\t\tconst set = s.value !== undefined && s.value !== null && s.value !== \"\";\n\t\t// Normalize to string for comparison (e.g., boolean false vs string 'false')\n\t\tconst isDefault =\n\t\t\tset &&\n\t\t\ts.defaultValue !== undefined &&\n\t\t\tString(s.value) === String(s.defaultValue);\n\n\t\tlet ico: string, st: string;\n\t\tif (!set && s.required) {\n\t\t\tico = `${ANSI.brightRed}◆${R}`;\n\t\t\tst = `${ANSI.brightRed}REQUIRED${R}`;\n\t\t} else if (!set) {\n\t\t\tico = `${D}○${R}`;\n\t\t\tst = `${D}unset${R}`;\n\t\t} else if (isDefault) {\n\t\t\tico = `${ANSI.brightBlue}●${R}`;\n\t\t\tst = `${ANSI.brightBlue}default${R}`;\n\t\t} else {\n\t\t\tico = `${ANSI.brightGreen}✓${R}`;\n\t\t\tst = `${ANSI.brightGreen}custom${R}`;\n\t\t}\n\n\t\tconst name = pad(s.name, NW - 2);\n\t\tconst val = pad(\n\t\t\tfmtVal(s.value ?? s.defaultValue, s.sensitive ?? false, VW),\n\t\t\tVW,\n\t\t);\n\t\tconst status = pad(st, SW);\n\t\tlines.push(row(` ${ico} ${c2}${name}${R} ${val} ${status}`));\n\t}\n\n\tlines.push(mid);\n\tlines.push(\n\t\trow(\n\t\t\t` ${D}${ANSI.brightGreen}✓${D} custom ${ANSI.brightBlue}●${D} default ○ unset ${ANSI.brightRed}◆${D} required → Set in .env${R}`,\n\t\t),\n\t);\n\tlines.push(bot);\n\n\t// Add Discord invite links organized by voice capability\n\tif (options.applicationId && options.discordPermissions) {\n\t\tconst p = options.discordPermissions;\n\t\tconst baseUrl = `https://discord.com/api/oauth2/authorize?client_id=${options.applicationId}&scope=bot%20applications.commands&permissions=`;\n\n\t\tlines.push(\"\");\n\t\tlines.push(`${B}${ANSI.brightCyan}🔗 Discord Bot Invite${R}`);\n\t\tlines.push(\"\");\n\t\tlines.push(` ${B}🎙️ With Voice:${R}`);\n\t\tlines.push(\n\t\t\t` ${ANSI.brightGreen}● Basic${R} ${baseUrl}${p.basicVoice}`,\n\t\t);\n\t\tlines.push(\n\t\t\t` ${ANSI.brightYellow}● Moderator${R} ${baseUrl}${p.moderatorVoice}`,\n\t\t);\n\t\tlines.push(\n\t\t\t` ${ANSI.brightRed}● Admin${R} ${baseUrl}${p.adminVoice}`,\n\t\t);\n\t\tlines.push(\"\");\n\t\tlines.push(` ${B}💬 Without Voice:${R}`);\n\t\tlines.push(` ${ANSI.brightCyan}○ Basic${R} ${baseUrl}${p.basic}`);\n\t\tlines.push(\n\t\t\t` ${ANSI.brightMagenta}○ Moderator${R} ${baseUrl}${p.moderator}`,\n\t\t);\n\t\tlines.push(` ${ANSI.brightBlue}○ Admin${R} ${baseUrl}${p.admin}`);\n\t}\n\n\tlines.push(\"\");\n\n\truntime.logger.info(lines.join(\"\\n\"));\n}\n\n/**\n * Print the Discord plugin banner with current settings.\n */\nexport function printDiscordBanner(runtime: IAgentRuntime): void {\n\t// Get settings\n\tconst apiToken = runtime.getSetting(\"DISCORD_API_TOKEN\");\n\tconst applicationId = runtime.getSetting(\"DISCORD_APPLICATION_ID\");\n\tconst ignoreBots = runtime.getSetting(\"DISCORD_SHOULD_IGNORE_BOT_MESSAGES\");\n\tconst ignoreDMs = runtime.getSetting(\"DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES\");\n\tconst onlyMentions = runtime.getSetting(\n\t\t\"DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS\",\n\t);\n\tconst listenChannels = runtime.getSetting(\"DISCORD_LISTEN_CHANNEL_IDS\");\n\tconst voiceChannelId = runtime.getSetting(\"DISCORD_VOICE_CHANNEL_ID\");\n\n\tprintBanner({\n\t\tpluginName: \"plugin-discord\",\n\t\tdescription: \"Discord bot integration for servers and channels\",\n\t\tapplicationId: applicationId ? String(applicationId) : undefined,\n\t\tdiscordPermissions: applicationId ? getPermissionValues() : undefined,\n\t\tsettings: [\n\t\t\t{\n\t\t\t\tname: \"DISCORD_API_TOKEN\",\n\t\t\t\tvalue: apiToken,\n\t\t\t\tsensitive: true,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{ name: \"DISCORD_APPLICATION_ID\", value: applicationId },\n\t\t\t{ name: \"DISCORD_VOICE_CHANNEL_ID\", value: voiceChannelId },\n\t\t\t{ name: \"DISCORD_LISTEN_CHANNEL_IDS\", value: listenChannels },\n\t\t\t{\n\t\t\t\tname: \"DISCORD_SHOULD_IGNORE_BOT_MESSAGES\",\n\t\t\t\tvalue: ignoreBots,\n\t\t\t\tdefaultValue: \"false\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES\",\n\t\t\t\tvalue: ignoreDMs,\n\t\t\t\tdefaultValue: \"false\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS\",\n\t\t\t\tvalue: onlyMentions,\n\t\t\t\tdefaultValue: \"false\",\n\t\t\t},\n\t\t],\n\t\truntime,\n\t});\n}\n",
|
|
33
|
+
"/**\n * DiscordOwnerPairingService\n *\n * Implements the connector side of the owner-pairing flow for Discord:\n * - `/eliza-pair <code>` slash command: relays a 6-digit pair code to the\n * backend `verifyOwnerBindFromConnector` service and reports the result.\n * - `sendOwnerLoginDmLink({ externalId, link })`: called by the backend's\n * `/api/auth/login/owner/dm-link/request` handler to DM a login link to\n * the Discord user identified by their snowflake ID.\n *\n * Hard rules:\n * - Backend is the authority. The connector only relays; it never decides\n * whether a binding succeeds.\n * - Fail closed: if the backend service is unreachable, we reply with an\n * explicit error message and do NOT silently succeed.\n * - Per-user rate limit on `/eliza-pair` invocations: 5 attempts per minute.\n * - DM-link sender never pre-fetches or auto-redeems the link.\n * - Webhook signature verification: Discord slash-command payloads are\n * verified by discord.js before reaching this handler (the library handles\n * it for gateway-connected bots). No additional verification needed here.\n */\n\nimport { type IAgentRuntime, logger, Service } from \"@elizaos/core\";\nimport type { ChatInputCommandInteraction } from \"discord.js\";\nimport { addCommand } from \"./slash-commands\";\n\n/** Service type string used by the backend to look up this service. */\nexport const DISCORD_OWNER_PAIRING_SERVICE_TYPE = \"OWNER_PAIRING_DISCORD\";\n\n/** Maximum pairing attempts per user per window. */\nconst RATE_LIMIT_MAX_ATTEMPTS = 5;\n/** Window length in milliseconds. */\nconst RATE_LIMIT_WINDOW_MS = 60_000;\n\n/**\n * Shape of the backend `verifyOwnerBindFromConnector` service method.\n * The backend owns this interface; we look it up via the runtime service\n * registry. If it is absent, we fail closed.\n */\ninterface OwnerBindVerifyService {\n\tverifyOwnerBindFromConnector(params: {\n\t\tconnector: \"discord\" | \"telegram\" | \"wechat\" | \"matrix\";\n\t\texternalId: string;\n\t\tdisplayHandle: string;\n\t\tcode: string;\n\t}): Promise<{ success: boolean; error?: string }>;\n}\n\n/** Audit-emit helper — best-effort, never throws. */\nasync function auditEmit(\n\truntime: IAgentRuntime,\n\taction: string,\n\toutcome: \"success\" | \"failure\",\n\tmetadata: Record<string, string | number | boolean>,\n): Promise<void> {\n\ttry {\n\t\tawait runtime.emitEvent(\n\t\t\t[\"AUTH_AUDIT\"] as string[],\n\t\t\t{\n\t\t\t\truntime,\n\t\t\t\taction,\n\t\t\t\toutcome,\n\t\t\t\tmetadata,\n\t\t\t\tsource: \"discord\",\n\t\t\t} as never,\n\t\t);\n\t} catch {\n\t\t// Audit is best-effort; a failure here must not mask the real result.\n\t}\n}\n\n/**\n * In-memory per-user rate-limit state.\n * Key: Discord user snowflake ID.\n * Value: list of attempt timestamps within the current window.\n */\nconst pairAttempts = new Map<string, number[]>();\n\n/**\n * Clears all in-memory rate-limit state. Exposed for testing only.\n * @internal\n */\nexport function _resetRateLimitStateForTesting(): void {\n\tpairAttempts.clear();\n}\n\nfunction isRateLimited(userId: string): boolean {\n\tconst now = Date.now();\n\tconst windowStart = now - RATE_LIMIT_WINDOW_MS;\n\tconst attempts = (pairAttempts.get(userId) ?? []).filter(\n\t\t(ts) => ts > windowStart,\n\t);\n\tpairAttempts.set(userId, attempts);\n\tif (attempts.length >= RATE_LIMIT_MAX_ATTEMPTS) {\n\t\treturn true;\n\t}\n\tattempts.push(now);\n\tpairAttempts.set(userId, attempts);\n\treturn false;\n}\n\n/**\n * Validates that the supplied string is a 6-digit numeric pair code.\n * The backend performs its own authoritative validation; this is a\n * pre-flight check to avoid a round-trip for obviously invalid inputs.\n */\nfunction isValidPairCode(code: string): boolean {\n\treturn /^\\d{6}$/.test(code.trim());\n}\n\n/**\n * Looks up the backend verify service from the runtime service registry.\n * Returns null when the backend has not yet registered it.\n */\nfunction resolveVerifyService(\n\truntime: IAgentRuntime,\n): OwnerBindVerifyService | null {\n\ttry {\n\t\tconst svc = runtime.getService(\"OWNER_BIND_VERIFY\") as unknown;\n\t\tif (\n\t\t\tsvc &&\n\t\t\ttypeof svc === \"object\" &&\n\t\t\ttypeof (svc as Record<string, unknown>).verifyOwnerBindFromConnector ===\n\t\t\t\t\"function\"\n\t\t) {\n\t\t\treturn svc as OwnerBindVerifyService;\n\t\t}\n\t} catch {\n\t\t// Service not registered yet.\n\t}\n\treturn null;\n}\n\n/**\n * Handler for the `/eliza-pair <code>` slash command.\n * Called by the Discord slash-command dispatcher after it has already\n * applied cooldown and role checks. Exported for unit testing.\n */\nexport async function handleElizaPairCommand(\n\tinteraction: ChatInputCommandInteraction,\n\truntime: IAgentRuntime,\n): Promise<void> {\n\tconst userId = interaction.user.id;\n\tconst displayHandle =\n\t\tinteraction.user.discriminator && interaction.user.discriminator !== \"0\"\n\t\t\t? `${interaction.user.username}#${interaction.user.discriminator}`\n\t\t\t: interaction.user.username;\n\n\t// Per-user rate limit enforced at the connector layer to reduce log spam.\n\tif (isRateLimited(userId)) {\n\t\tlogger.warn(\n\t\t\t{ src: \"plugin:discord:owner-pairing\", userId },\n\t\t\t\"Rate limit hit for /eliza-pair\",\n\t\t);\n\t\tawait auditEmit(\n\t\t\truntime,\n\t\t\t\"auth.owner.pair.discord.rate_limited\",\n\t\t\t\"failure\",\n\t\t\t{\n\t\t\t\texternalId: userId,\n\t\t\t},\n\t\t);\n\t\tawait interaction.reply({\n\t\t\tcontent:\n\t\t\t\t\"Too many pairing attempts. Please wait a moment before trying again.\",\n\t\t\tephemeral: true,\n\t\t});\n\t\treturn;\n\t}\n\n\tconst rawCode = interaction.options.getString(\"code\");\n\tif (!rawCode?.trim()) {\n\t\tawait interaction.reply({\n\t\t\tcontent:\n\t\t\t\t\"Usage: `/eliza-pair <code>` — enter the 6-digit code shown in the Eliza dashboard.\",\n\t\t\tephemeral: true,\n\t\t});\n\t\treturn;\n\t}\n\n\tconst code = rawCode.trim();\n\tif (!isValidPairCode(code)) {\n\t\tawait interaction.reply({\n\t\t\tcontent:\n\t\t\t\t\"The pairing code must be exactly 6 digits. Check the Eliza dashboard and try again.\",\n\t\t\tephemeral: true,\n\t\t});\n\t\treturn;\n\t}\n\n\tconst verifySvc = resolveVerifyService(runtime);\n\tif (!verifySvc) {\n\t\tlogger.error(\n\t\t\t{ src: \"plugin:discord:owner-pairing\", userId },\n\t\t\t\"OWNER_BIND_VERIFY service not available — cannot complete pairing\",\n\t\t);\n\t\tawait auditEmit(\n\t\t\truntime,\n\t\t\t\"auth.owner.pair.discord.service_unavailable\",\n\t\t\t\"failure\",\n\t\t\t{ externalId: userId },\n\t\t);\n\t\tawait interaction.reply({\n\t\t\tcontent:\n\t\t\t\t\"Eliza could not reach the pairing service right now. Please try again in a moment.\",\n\t\t\tephemeral: true,\n\t\t});\n\t\treturn;\n\t}\n\n\tlet result: { success: boolean; error?: string };\n\ttry {\n\t\tresult = await verifySvc.verifyOwnerBindFromConnector({\n\t\t\tconnector: \"discord\",\n\t\t\texternalId: userId,\n\t\t\tdisplayHandle,\n\t\t\tcode,\n\t\t});\n\t} catch (err) {\n\t\tlogger.error(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord:owner-pairing\",\n\t\t\t\tuserId,\n\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t},\n\t\t\t\"verifyOwnerBindFromConnector threw unexpectedly\",\n\t\t);\n\t\tawait auditEmit(\n\t\t\truntime,\n\t\t\t\"auth.owner.pair.discord.verify_error\",\n\t\t\t\"failure\",\n\t\t\t{ externalId: userId },\n\t\t);\n\t\tawait interaction.reply({\n\t\t\tcontent:\n\t\t\t\t\"Something went wrong while verifying the pairing code. Please try again.\",\n\t\t\tephemeral: true,\n\t\t});\n\t\treturn;\n\t}\n\n\tif (result.success) {\n\t\tlogger.info(\n\t\t\t{ src: \"plugin:discord:owner-pairing\", userId, displayHandle },\n\t\t\t\"Owner pairing completed successfully\",\n\t\t);\n\t\tawait auditEmit(runtime, \"auth.owner.pair.discord.success\", \"success\", {\n\t\t\texternalId: userId,\n\t\t\tdisplayHandle,\n\t\t});\n\t\tawait interaction.reply({\n\t\t\tcontent: \"Paired with Eliza. You can now log in via Discord.\",\n\t\t\tephemeral: true,\n\t\t});\n\t} else {\n\t\tlogger.warn(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord:owner-pairing\",\n\t\t\t\tuserId,\n\t\t\t\tbackendError: result.error,\n\t\t\t},\n\t\t\t\"Owner pairing rejected by backend\",\n\t\t);\n\t\tawait auditEmit(runtime, \"auth.owner.pair.discord.failure\", \"failure\", {\n\t\t\texternalId: userId,\n\t\t});\n\t\tawait interaction.reply({\n\t\t\tcontent:\n\t\t\t\t\"Pair code invalid or expired. Check the Eliza dashboard for a fresh code.\",\n\t\t\tephemeral: true,\n\t\t});\n\t}\n}\n\n/**\n * Public service interface exposed via the runtime service registry.\n * The backend's `owner-binding.ts` calls `sendOwnerLoginDmLink` when the\n * user requests a DM login link via the dashboard.\n */\nexport interface DiscordOwnerPairingService {\n\t/**\n\t * DMs the Discord user identified by `externalId` (a Discord snowflake)\n\t * with a login link. The link is presented as-is; this method never\n\t * pre-fetches or auto-redeems it.\n\t *\n\t * Throws if the DM cannot be delivered (user has DMs closed, bot lacks\n\t * permission, Discord API error). The caller is responsible for surfacing\n\t * the error to the dashboard.\n\t */\n\tsendOwnerLoginDmLink(params: {\n\t\texternalId: string;\n\t\tlink: string;\n\t}): Promise<void>;\n}\n\nexport class DiscordOwnerPairingServiceImpl\n\textends Service\n\timplements DiscordOwnerPairingService\n{\n\tstatic serviceType = DISCORD_OWNER_PAIRING_SERVICE_TYPE;\n\tcapabilityDescription =\n\t\t\"Handles Discord-side owner pairing (slash-command code verification) and DM login-link delivery for Eliza remote auth\";\n\n\tstatic async start(runtime: IAgentRuntime): Promise<Service> {\n\t\tconst service = new DiscordOwnerPairingServiceImpl(runtime);\n\t\tif (resolveVerifyService(runtime)) {\n\t\t\tservice.registerPairCommand(runtime);\n\t\t\tlogger.info(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:owner-pairing\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t},\n\t\t\t\t\"DiscordOwnerPairingService started; /eliza-pair command registered\",\n\t\t\t);\n\t\t} else {\n\t\t\tlogger.info(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:owner-pairing\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t},\n\t\t\t\t\"DiscordOwnerPairingService started without /eliza-pair because OWNER_BIND_VERIFY is not registered\",\n\t\t\t);\n\t\t}\n\t\treturn service;\n\t}\n\n\tasync stop(): Promise<void> {\n\t\t// Clear rate-limit state on shutdown to avoid stale data across restarts.\n\t\tpairAttempts.clear();\n\t}\n\n\t/**\n\t * Registers the /eliza-pair slash command with the Discord plugin's\n\t * slash-command dispatcher. Calling this multiple times is idempotent\n\t * because `addCommand` overwrites existing entries by name.\n\t */\n\tprivate registerPairCommand(runtime: IAgentRuntime): void {\n\t\taddCommand({\n\t\t\tname: \"eliza-pair\",\n\t\t\tdescription:\n\t\t\t\t\"Pair your Discord account with Eliza using the 6-digit code from the dashboard\",\n\t\t\tephemeral: true,\n\t\t\toptions: [\n\t\t\t\t{\n\t\t\t\t\tname: \"code\",\n\t\t\t\t\tdescription: \"6-digit pairing code from the Eliza dashboard\",\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\trequired: true,\n\t\t\t\t},\n\t\t\t],\n\t\t\texecute: async (interaction) => {\n\t\t\t\tawait handleElizaPairCommand(interaction, runtime);\n\t\t\t},\n\t\t});\n\t}\n\n\tasync sendOwnerLoginDmLink(params: {\n\t\texternalId: string;\n\t\tlink: string;\n\t}): Promise<void> {\n\t\tconst { externalId, link } = params;\n\n\t\t// Resolve the DiscordService to get the discord.js Client.\n\t\tconst discordSvc = this.runtime.getService(\"discord\") as unknown;\n\t\tconst client =\n\t\t\tdiscordSvc &&\n\t\t\ttypeof discordSvc === \"object\" &&\n\t\t\t\"client\" in (discordSvc as Record<string, unknown>)\n\t\t\t\t? (discordSvc as { client: unknown }).client\n\t\t\t\t: null;\n\n\t\tif (\n\t\t\t!client ||\n\t\t\ttypeof (client as Record<string, unknown>).users !== \"object\"\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Discord client is not available — cannot send DM login link\",\n\t\t\t);\n\t\t}\n\n\t\tconst discordClient = client as import(\"discord.js\").Client;\n\n\t\tlet dmChannel: import(\"discord.js\").DMChannel;\n\t\ttry {\n\t\t\tconst user = await discordClient.users.fetch(externalId);\n\t\t\tdmChannel = await user.createDM();\n\t\t} catch (err) {\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to open DM channel with Discord user ${externalId}: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\n\t\tconst message =\n\t\t\t`Click to log in to Eliza: ${link}\\n\\n` +\n\t\t\t\"_This link expires in 5 minutes. Do not share it._\";\n\n\t\ttry {\n\t\t\tawait dmChannel.send(message);\n\t\t\tlogger.info(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:owner-pairing\",\n\t\t\t\t\texternalId,\n\t\t\t\t},\n\t\t\t\t\"Login DM link sent\",\n\t\t\t);\n\t\t} catch (err) {\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to send DM login link to Discord user ${externalId}: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\t}\n}\n",
|
|
34
|
+
"import { createUniqueUuid, type IAgentRuntime } from \"@elizaos/core\";\nimport type {\n\tAutocompleteInteraction,\n\tChatInputCommandInteraction,\n} from \"discord.js\";\nimport { ApplicationCommandOptionType } from \"discord.js\";\nimport { getPreset, listPresets } from \"./actions/setup-credentials\";\nimport type { DiscordSlashCommand } from \"./types\";\n\ntype SlashCommandRole = \"OWNER\" | \"ADMIN\" | \"USER\" | \"GUEST\";\n\ninterface SlashCommand {\n\tname: string;\n\tdescription: string;\n\toptions?: SlashCommandOption[];\n\tephemeral?: boolean;\n\tcooldown?: number;\n\townerOnly?: boolean;\n\t/** Minimum elizaOS role required to execute this command. */\n\trequiredRole?: SlashCommandRole;\n\texecute: (\n\t\tinteraction: ChatInputCommandInteraction,\n\t\truntime: IAgentRuntime,\n\t) => Promise<void>;\n\tautocomplete?: (interaction: AutocompleteInteraction) => Promise<void>;\n}\n\n/** Context resolved by the Discord service before dispatching built-in commands. */\nexport interface SlashCommandContext {\n\tentityId: string;\n\troomId: string;\n}\n\ninterface SlashCommandOption {\n\tname: string;\n\tdescription: string;\n\ttype: \"string\" | \"number\" | \"boolean\" | \"user\" | \"channel\" | \"role\";\n\trequired?: boolean;\n\tchoices?: Array<{ name: string; value: string }>;\n\tautocomplete?: boolean;\n}\n\nconst OPTION_TYPE_MAP: Record<string, number> = {\n\tstring: ApplicationCommandOptionType.String,\n\tnumber: ApplicationCommandOptionType.Number,\n\tboolean: ApplicationCommandOptionType.Boolean,\n\tuser: ApplicationCommandOptionType.User,\n\tchannel: ApplicationCommandOptionType.Channel,\n\trole: ApplicationCommandOptionType.Role,\n};\n\nconst commands = new Map<string, SlashCommand>();\nconst cooldowns = new Map<string, Map<string, number>>();\n\nconst FALLBACK_KNOWN_MODELS = [\n\t\"gpt-4o\",\n\t\"gpt-4o-mini\",\n\t\"gpt-4\",\n\t\"gpt-3.5-turbo\",\n\t\"claude-sonnet-4-6\",\n\t\"claude-opus-4-7\",\n\t\"claude-3.5-haiku\",\n\t\"llama-3.1-70b\",\n\t\"llama-3.1-8b\",\n\t\"gemini-2.5-pro\",\n\t\"gemini-2.5-flash\",\n\t\"mistral-large\",\n\t\"mistral-medium\",\n] as const;\n\nfunction parseStringList(value: unknown): string[] {\n\tif (Array.isArray(value)) {\n\t\treturn value\n\t\t\t.map((entry) => (typeof entry === \"string\" ? entry.trim() : \"\"))\n\t\t\t.filter(Boolean);\n\t}\n\tif (typeof value !== \"string\") {\n\t\treturn [];\n\t}\n\tconst trimmed = value.trim();\n\tif (!trimmed) {\n\t\treturn [];\n\t}\n\ttry {\n\t\tconst parsed = JSON.parse(trimmed) as unknown;\n\t\tif (Array.isArray(parsed)) {\n\t\t\treturn parseStringList(parsed);\n\t\t}\n\t} catch {\n\t\t// Fall back to comma-separated parsing.\n\t}\n\treturn trimmed\n\t\t.split(\",\")\n\t\t.map((entry) => entry.trim())\n\t\t.filter(Boolean);\n}\n\nfunction getKnownModels(runtime: IAgentRuntime): string[] {\n\tconst configured =\n\t\tparseStringList(runtime.getSetting(\"DISCORD_KNOWN_MODELS\")) ??\n\t\tparseStringList(runtime.getSetting(\"KNOWN_MODELS\"));\n\treturn configured.length > 0 ? configured : [...FALLBACK_KNOWN_MODELS];\n}\n\nconst helpCommand: SlashCommand = {\n\tname: \"help\",\n\tdescription: \"Show available commands and usage information\",\n\tephemeral: true,\n\tasync execute(interaction) {\n\t\tconst lines: string[] = [\"**Available Commands**\\n\"];\n\t\tfor (const [name, command] of commands) {\n\t\t\tconst options = command.options\n\t\t\t\t? command.options\n\t\t\t\t\t\t.map((option) =>\n\t\t\t\t\t\t\toption.required ? `<${option.name}>` : `[${option.name}]`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join(\" \")\n\t\t\t\t: \"\";\n\t\t\tlines.push(\n\t\t\t\t`/${name}${options ? ` ${options}` : \"\"} - ${command.description}`,\n\t\t\t);\n\t\t}\n\t\tawait interaction.reply({ content: lines.join(\"\\n\"), ephemeral: true });\n\t},\n};\n\nconst statusCommand: SlashCommand = {\n\tname: \"status\",\n\tdescription: \"Show the bot's current status and uptime\",\n\tephemeral: true,\n\trequiredRole: \"USER\",\n\tasync execute(interaction, runtime) {\n\t\tconst uptimeMs = process.uptime() * 1000;\n\t\tconst hours = Math.floor(uptimeMs / 3_600_000);\n\t\tconst minutes = Math.floor((uptimeMs % 3_600_000) / 60_000);\n\t\tconst seconds = Math.floor((uptimeMs % 60_000) / 1000);\n\t\tconst memoryUsage = process.memoryUsage();\n\t\tconst heapMb = (memoryUsage.heapUsed / 1024 / 1024).toFixed(1);\n\t\tconst rssMb = (memoryUsage.rss / 1024 / 1024).toFixed(1);\n\n\t\tawait interaction.reply({\n\t\t\tcontent: [\n\t\t\t\t\"**Bot Status**\",\n\t\t\t\t`- Agent: **${runtime.character?.name ?? \"Unknown\"}**`,\n\t\t\t\t`- Uptime: **${hours}h ${minutes}m ${seconds}s**`,\n\t\t\t\t`- Memory: **${heapMb} MB** heap / **${rssMb} MB** RSS`,\n\t\t\t\t`- Guilds: **${interaction.client.guilds.cache.size}**`,\n\t\t\t\t`- Node: **${process.version}**`,\n\t\t\t\t`- Platform: **${process.platform}**`,\n\t\t\t].join(\"\\n\"),\n\t\t\tephemeral: true,\n\t\t});\n\t},\n};\n\nconst searchCommand: SlashCommand = {\n\tname: \"search\",\n\tdescription: \"Search conversation history in this channel\",\n\trequiredRole: \"USER\",\n\toptions: [\n\t\t{\n\t\t\tname: \"query\",\n\t\t\tdescription: \"The search term or phrase\",\n\t\t\ttype: \"string\",\n\t\t\trequired: true,\n\t\t},\n\t\t{\n\t\t\tname: \"limit\",\n\t\t\tdescription: \"Maximum results to return (default: 5)\",\n\t\t\ttype: \"number\",\n\t\t},\n\t],\n\tephemeral: true,\n\tcooldown: 10,\n\tasync execute(interaction, runtime) {\n\t\tconst query = interaction.options.getString(\"query\", true);\n\t\tconst limit = interaction.options.getNumber(\"limit\") || 5;\n\t\tawait interaction.deferReply({ ephemeral: true });\n\n\t\ttry {\n\t\t\tconst roomId = createUniqueUuid(runtime, interaction.channelId);\n\t\t\tconst memories = await runtime.getMemories({\n\t\t\t\ttableName: \"messages\",\n\t\t\t\troomId,\n\t\t\t\tcount: 100,\n\t\t\t});\n\t\t\tconst normalizedQuery = query.trim().toLowerCase();\n\t\t\tconst filteredMemories = memories.filter((memory) =>\n\t\t\t\t(memory.content?.text ?? \"\").toLowerCase().includes(normalizedQuery),\n\t\t\t);\n\n\t\t\tif (filteredMemories.length === 0) {\n\t\t\t\tawait interaction.editReply({\n\t\t\t\t\tcontent: `No results found for **\"${query}\"**`,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst results = filteredMemories.slice(0, limit).map((memory, index) => {\n\t\t\t\tconst text = memory.content?.text || \"(no text)\";\n\t\t\t\tconst truncated = text.length > 120 ? `${text.slice(0, 120)}...` : text;\n\t\t\t\tconst date = memory.createdAt\n\t\t\t\t\t? new Date(memory.createdAt).toLocaleDateString()\n\t\t\t\t\t: \"unknown date\";\n\t\t\t\treturn `**${index + 1}.** ${truncated}\\n_${date}_`;\n\t\t\t});\n\n\t\t\tawait interaction.editReply({\n\t\t\t\tcontent: `**Search results for \"${query}\"**\\n\\n${results.join(\"\\n\\n\")}`,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tawait interaction.editReply({\n\t\t\t\tcontent: `Search failed: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t});\n\t\t}\n\t},\n};\n\nconst clearCommand: SlashCommand = {\n\tname: \"clear\",\n\tdescription: \"Explain how context clearing works in this channel\",\n\tephemeral: true,\n\trequiredRole: \"USER\",\n\tasync execute(interaction) {\n\t\tawait interaction.reply({\n\t\t\tcontent:\n\t\t\t\t\"Context clearing is not wired up for Discord yet. I can search recent messages with `/search`, but I won't pretend existing memory was deleted.\",\n\t\t\tephemeral: true,\n\t\t});\n\t},\n};\n\nconst settingsCommand: SlashCommand = {\n\tname: \"settings\",\n\tdescription: \"View the current Discord bot settings\",\n\trequiredRole: \"ADMIN\",\n\toptions: [\n\t\t{\n\t\t\tname: \"action\",\n\t\t\tdescription: \"What to do\",\n\t\t\ttype: \"string\",\n\t\t\trequired: true,\n\t\t\tchoices: [\n\t\t\t\t{ name: \"View current settings\", value: \"view\" },\n\t\t\t\t{ name: \"Toggle response-only-on-mention\", value: \"toggle-mention\" },\n\t\t\t\t{ name: \"Toggle ignore-bots\", value: \"toggle-ignore-bots\" },\n\t\t\t],\n\t\t},\n\t],\n\tephemeral: true,\n\tasync execute(interaction, runtime) {\n\t\tconst action = interaction.options.getString(\"action\", true);\n\t\tif (action === \"view\") {\n\t\t\tawait interaction.reply({\n\t\t\t\tcontent: [\n\t\t\t\t\t\"**Current Settings**\",\n\t\t\t\t\t`- Respond only to mentions: **${runtime.getSetting(\"DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS\") ?? \"false\"}**`,\n\t\t\t\t\t`- Ignore bot messages: **${runtime.getSetting(\"DISCORD_SHOULD_IGNORE_BOT_MESSAGES\") ?? \"true\"}**`,\n\t\t\t\t\t`- Allowed channels: **${runtime.getSetting(\"CHANNEL_IDS\") ?? \"(all channels)\"}**`,\n\t\t\t\t\t`- Agent name: **${runtime.character?.name ?? \"Unknown\"}**`,\n\t\t\t\t].join(\"\\n\"),\n\t\t\t\tephemeral: true,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst content =\n\t\t\taction === \"toggle-mention\"\n\t\t\t\t? \"Respond-only-on-mention is controlled by `DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS`. Update the setting and restart to change it.\"\n\t\t\t\t: \"Ignore-bots is controlled by `DISCORD_SHOULD_IGNORE_BOT_MESSAGES`. Update the setting and restart to change it.\";\n\t\tawait interaction.reply({ content, ephemeral: true });\n\t},\n};\n\nconst setupCommand: SlashCommand = {\n\tname: \"setup\",\n\tdescription: \"Set up API credentials for third-party services\",\n\trequiredRole: \"OWNER\",\n\toptions: [\n\t\t{\n\t\t\tname: \"service\",\n\t\t\tdescription:\n\t\t\t\t\"Service to configure (github, vercel, cloudflare, anthropic, openai, fal, custom)\",\n\t\t\ttype: \"string\",\n\t\t\tchoices: [\n\t\t\t\t{ name: \"GitHub\", value: \"github\" },\n\t\t\t\t{ name: \"Vercel\", value: \"vercel\" },\n\t\t\t\t{ name: \"Cloudflare\", value: \"cloudflare\" },\n\t\t\t\t{ name: \"Anthropic\", value: \"anthropic\" },\n\t\t\t\t{ name: \"OpenAI\", value: \"openai\" },\n\t\t\t\t{ name: \"fal.ai\", value: \"fal\" },\n\t\t\t\t{ name: \"Custom\", value: \"custom\" },\n\t\t\t],\n\t\t},\n\t],\n\tephemeral: true,\n\tasync execute(interaction) {\n\t\tconst service = interaction.options.getString(\"service\");\n\t\tif (!service) {\n\t\t\tconst services = listPresets()\n\t\t\t\t.filter((presetName) => presetName !== \"generic\")\n\t\t\t\t.map((presetName) => {\n\t\t\t\t\tconst preset = getPreset(presetName);\n\t\t\t\t\treturn `- **${preset?.displayName ?? presetName}** - \\`/setup service:${presetName}\\``;\n\t\t\t\t});\n\t\t\tawait interaction.reply({\n\t\t\t\tcontent: [\n\t\t\t\t\t\"**Credential Setup**\",\n\t\t\t\t\t\"Choose a service to configure:\",\n\t\t\t\t\t\"\",\n\t\t\t\t\t...services,\n\t\t\t\t\t\"- **Custom** - `/setup service:custom`\",\n\t\t\t\t\t\"\",\n\t\t\t\t\t\"I'll walk you through it in DMs to keep your keys safe.\",\n\t\t\t\t].join(\"\\n\"),\n\t\t\t\tephemeral: true,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tawait interaction.reply({\n\t\t\tcontent: `Starting **${service}** setup. Check your DMs - I'll walk you through it there to keep your keys private.`,\n\t\t\tephemeral: true,\n\t\t});\n\n\t\ttry {\n\t\t\tconst dmChannel = await interaction.user.createDM();\n\t\t\tconst presetKey = service === \"custom\" ? \"generic\" : service;\n\t\t\tconst preset = getPreset(presetKey);\n\t\t\tif (!preset) {\n\t\t\t\tawait dmChannel.send(\n\t\t\t\t\t`I don't have a preset for \"${service}\". Try \\`/setup\\` to see available services.`,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst field = preset.fields[0];\n\t\t\tconst helpLine = preset.helpUrl\n\t\t\t\t? `Here's where to get one: ${preset.helpUrl}`\n\t\t\t\t: \"\";\n\t\t\tawait dmChannel.send(\n\t\t\t\t[\n\t\t\t\t\t`Setting up **${preset.displayName}** credentials.`,\n\t\t\t\t\tpreset.helpText,\n\t\t\t\t\thelpLine,\n\t\t\t\t\t\"\",\n\t\t\t\t\t`Please paste your **${field.label}** here. ${field.secret ? \"I'll delete your message right after reading it.\" : \"\"}`,\n\t\t\t\t\t\"\",\n\t\t\t\t\t\"(Type `cancel` to abort setup)\",\n\t\t\t\t]\n\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t.join(\"\\n\"),\n\t\t\t);\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tawait interaction.followUp({\n\t\t\t\t\tcontent:\n\t\t\t\t\t\t\"I couldn't send you a DM. Make sure your DMs are open and try again.\",\n\t\t\t\t\tephemeral: true,\n\t\t\t\t});\n\t\t\t} catch {\n\t\t\t\t// Ignore expired follow-ups.\n\t\t\t}\n\t\t}\n\t},\n};\n\nconst modelCommand: SlashCommand = {\n\tname: \"model\",\n\tdescription: \"View or change the active AI model\",\n\trequiredRole: \"ADMIN\",\n\toptions: [\n\t\t{\n\t\t\tname: \"name\",\n\t\t\tdescription: \"Model name to switch to (leave empty to view current)\",\n\t\t\ttype: \"string\",\n\t\t\tautocomplete: true,\n\t\t},\n\t],\n\tephemeral: true,\n\tasync execute(interaction, runtime) {\n\t\tconst modelName = interaction.options.getString(\"name\");\n\t\tif (!modelName) {\n\t\t\tawait interaction.reply({\n\t\t\t\tcontent: `**Current model:** \\`${runtime.getSetting(\"MODEL\") ?? runtime.getSetting(\"DEFAULT_MODEL\") ?? \"(not configured)\"}\\``,\n\t\t\t\tephemeral: true,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tawait interaction.reply({\n\t\t\tcontent: `Model switching to \\`${modelName}\\` is noted. The runtime model is still controlled by configuration, so update the setting and restart to switch permanently.`,\n\t\t\tephemeral: true,\n\t\t});\n\t},\n\tasync autocomplete(interaction) {\n\t\tconst runtime = (\n\t\t\tinteraction.client as unknown as { runtime?: IAgentRuntime }\n\t\t).runtime;\n\t\tconst models = runtime\n\t\t\t? getKnownModels(runtime)\n\t\t\t: [...FALLBACK_KNOWN_MODELS];\n\t\tconst focused = interaction.options.getFocused().toLowerCase();\n\t\tconst filtered = models\n\t\t\t.filter((model) => model.toLowerCase().includes(focused))\n\t\t\t.slice(0, 25);\n\t\tawait interaction.respond(\n\t\t\tfiltered.map((model) => ({ name: model, value: model })),\n\t\t);\n\t},\n};\n\nfunction registerBuiltins(): void {\n\tfor (const command of [\n\t\thelpCommand,\n\t\tstatusCommand,\n\t\tsearchCommand,\n\t\tclearCommand,\n\t\tsettingsCommand,\n\t\tmodelCommand,\n\t\tsetupCommand,\n\t]) {\n\t\tcommands.set(command.name, command);\n\t}\n}\n\nregisterBuiltins();\n\nfunction toDiscordSlashCommand(command: SlashCommand): DiscordSlashCommand {\n\tconst options = command.options?.map((option) => ({\n\t\tname: option.name,\n\t\tdescription: option.description,\n\t\ttype: OPTION_TYPE_MAP[option.type] ?? ApplicationCommandOptionType.String,\n\t\trequired: option.required ?? false,\n\t\t...(option.choices ? { choices: option.choices } : {}),\n\t\t...(option.autocomplete ? { autocomplete: option.autocomplete } : {}),\n\t}));\n\n\treturn {\n\t\tname: command.name,\n\t\tdescription: command.description,\n\t\toptions,\n\t};\n}\n\nexport async function registerSlashCommands(\n\truntime: IAgentRuntime,\n): Promise<void> {\n\tconst registered = [...commands.values()].map(toDiscordSlashCommand);\n\truntime.logger.info(\n\t\t{\n\t\t\tsrc: \"slash-commands\",\n\t\t\tcount: registered.length,\n\t\t\tnames: [...commands.keys()],\n\t\t},\n\t\t\"Registering built-in slash commands\",\n\t);\n\n\tawait runtime.emitEvent(\n\t\t[\"DISCORD_REGISTER_COMMANDS\"] as string[],\n\t\t{\n\t\t\truntime,\n\t\t\tsource: \"discord\",\n\t\t\tcommands: registered,\n\t\t} as never,\n\t);\n}\n\nexport async function handleSlashCommand(\n\tinteraction: ChatInputCommandInteraction,\n\truntime: IAgentRuntime,\n\tcontext?: SlashCommandContext,\n): Promise<void> {\n\tconst command = commands.get(interaction.commandName);\n\tif (!command) {\n\t\treturn;\n\t}\n\n\tif (command.cooldown && command.cooldown > 0) {\n\t\tconst userId = interaction.user.id;\n\t\tlet commandCooldowns = cooldowns.get(command.name);\n\t\tif (!commandCooldowns) {\n\t\t\tcommandCooldowns = new Map<string, number>();\n\t\t\tcooldowns.set(command.name, commandCooldowns);\n\t\t}\n\n\t\tconst lastUsed = commandCooldowns.get(userId);\n\t\tconst now = Date.now();\n\t\tif (lastUsed && now - lastUsed < command.cooldown * 1000) {\n\t\t\tconst remaining = Math.ceil(\n\t\t\t\t(command.cooldown * 1000 - (now - lastUsed)) / 1000,\n\t\t\t);\n\t\t\tawait interaction.reply({\n\t\t\t\tcontent: `Please wait **${remaining}s** before using \\`/${command.name}\\` again.`,\n\t\t\t\tephemeral: true,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcommandCooldowns.set(userId, now);\n\t\tsetTimeout(() => {\n\t\t\tif (commandCooldowns?.get(userId) === now) {\n\t\t\t\tcommandCooldowns.delete(userId);\n\t\t\t}\n\t\t}, command.cooldown * 1000);\n\t}\n\n\t// elizaOS role check — uses the agent's role hierarchy (OWNER > ADMIN > USER > GUEST)\n\tif (command.requiredRole && command.requiredRole !== \"GUEST\" && context) {\n\t\ttry {\n\t\t\tconst accessModuleId = \"@elizaos/agent/security/access\";\n\t\t\tconst { hasRoleAccess } = (await import(accessModuleId)) as unknown as {\n\t\t\t\thasRoleAccess?: (\n\t\t\t\t\truntime: unknown,\n\t\t\t\t\tmessage: unknown,\n\t\t\t\t\trequiredRole: string,\n\t\t\t\t) => Promise<boolean>;\n\t\t\t};\n\t\t\tconst memory = {\n\t\t\t\tentityId: context.entityId,\n\t\t\t\troomId: context.roomId,\n\t\t\t\tcontent: { text: `/${command.name}`, source: \"discord\" },\n\t\t\t};\n\t\t\tconst allowed =\n\t\t\t\ttypeof hasRoleAccess === \"function\"\n\t\t\t\t\t? await hasRoleAccess(runtime, memory, command.requiredRole)\n\t\t\t\t\t: true;\n\t\t\tif (!allowed) {\n\t\t\t\tawait interaction.reply({\n\t\t\t\t\tcontent: `You need at least **${command.requiredRole}** role to use \\`/${command.name}\\`.`,\n\t\t\t\t\tephemeral: true,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\truntime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"slash-commands\",\n\t\t\t\t\tcommandName: command.name,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Role check failed, falling through\",\n\t\t\t);\n\t\t}\n\t}\n\n\tif (command.ownerOnly) {\n\t\tconst guild = interaction.guild;\n\t\tif (guild && interaction.user.id !== guild.ownerId) {\n\t\t\tawait interaction.reply({\n\t\t\t\tcontent: \"This command can only be used by the server owner.\",\n\t\t\t\tephemeral: true,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t}\n\n\ttry {\n\t\tawait command.execute(interaction, runtime);\n\t} catch (error) {\n\t\tconst content = `An error occurred while running \\`/${command.name}\\`: ${error instanceof Error ? error.message : String(error)}`;\n\t\truntime.logger.error(\n\t\t\t{\n\t\t\t\tsrc: \"slash-commands\",\n\t\t\t\tcommandName: command.name,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t},\n\t\t\t\"Error executing slash command\",\n\t\t);\n\t\ttry {\n\t\t\tif (interaction.deferred) {\n\t\t\t\tawait interaction.editReply({ content });\n\t\t\t} else if (!interaction.replied) {\n\t\t\t\tawait interaction.reply({ content, ephemeral: true });\n\t\t\t}\n\t\t} catch {\n\t\t\t// Interaction may already be closed.\n\t\t}\n\t}\n}\n\nexport async function handleAutocomplete(\n\tinteraction: AutocompleteInteraction,\n): Promise<void> {\n\tconst command = commands.get(interaction.commandName);\n\tif (!command?.autocomplete) {\n\t\tawait interaction.respond([]);\n\t\treturn;\n\t}\n\n\ttry {\n\t\tawait command.autocomplete(interaction);\n\t} catch {\n\t\ttry {\n\t\t\tawait interaction.respond([]);\n\t\t} catch {\n\t\t\t// Ignore expired autocomplete interactions.\n\t\t}\n\t}\n}\n\nexport function getRegisteredCommands(): ReadonlyMap<string, SlashCommand> {\n\treturn commands;\n}\n\nexport function addCommand(command: SlashCommand): void {\n\tcommands.set(command.name, command);\n}\n\nexport function removeCommand(name: string): boolean {\n\treturn commands.delete(name);\n}\n",
|
|
35
|
+
"import type { IAgentRuntime, Memory, Provider, State } from \"@elizaos/core\";\nimport { ChannelType } from \"@elizaos/core\";\nimport type { GuildChannel } from \"discord.js\";\nimport { requireProviderSpec } from \"../generated/specs/spec-helpers\";\nimport type { DiscordService } from \"../service\";\nimport { ServiceType } from \"../types\";\n\nconst spec = requireProviderSpec(\"channelState\");\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n\treturn value && typeof value === \"object\" && !Array.isArray(value)\n\t\t? (value as Record<string, unknown>)\n\t\t: null;\n}\n\nfunction readString(value: unknown): string | undefined {\n\tif (typeof value !== \"string\") {\n\t\treturn undefined;\n\t}\n\tconst trimmed = value.trim();\n\treturn trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction normalizeName(value: string): string {\n\treturn value.trim().toLowerCase();\n}\n\nfunction formatDiscordIdentity(\n\tdisplayName: string | undefined,\n\tuserName: string | undefined,\n): string {\n\tif (\n\t\tdisplayName &&\n\t\tuserName &&\n\t\tnormalizeName(displayName) !== normalizeName(userName)\n\t) {\n\t\treturn `${displayName} (discord username: ${userName})`;\n\t}\n\treturn displayName ?? userName ?? \"someone\";\n}\n\nfunction describeCurrentSpeaker(message: Memory, fallback: string): string {\n\tconst metadata = asRecord(message.metadata);\n\tconst discordMetadata = asRecord(metadata?.discord);\n\tconst displayName =\n\t\treadString(metadata?.entityName) ??\n\t\treadString(metadata?.displayName) ??\n\t\treadString(discordMetadata?.name) ??\n\t\treadString(discordMetadata?.globalName) ??\n\t\treadString(fallback);\n\tconst userName =\n\t\treadString(metadata?.entityUserName) ??\n\t\treadString(discordMetadata?.userName) ??\n\t\treadString(discordMetadata?.username);\n\n\treturn formatDiscordIdentity(displayName, userName);\n}\n\nfunction describeAgentDiscordAccount(\n\tagentName: string,\n\tuserName: string | undefined,\n): string {\n\tconst candidate = readString(userName);\n\tif (!candidate || normalizeName(candidate) === normalizeName(agentName)) {\n\t\treturn agentName;\n\t}\n\treturn `${agentName} (discord username: ${candidate})`;\n}\n\n/**\n * Represents a provider for retrieving channel state information.\n * @type {Provider}\n * @property {string} name - The name of the channel state provider.\n * @property {Function} get - Asynchronous function that retrieves channel state information based on the provided runtime, message, and optional state parameters.\n * @param {IAgentRuntime} runtime - The agent runtime.\n * @param {Memory} message - The message object.\n * @param {State} [state] - Optional state object.\n * @returns {Promise<Object>} A promise that resolves to an object containing channel state data, values, and text.\n */\nexport const channelStateProvider: Provider = {\n\tname: spec.name,\n\tdynamic: true,\n\tget: async (runtime: IAgentRuntime, message: Memory, state: State) => {\n\t\tconst room = state.data?.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\t// if message source is not discord, return\n\t\tif (message.content.source !== \"discord\") {\n\t\t\treturn {\n\t\t\t\tdata: {},\n\t\t\t\tvalues: {},\n\t\t\t\ttext: \"\",\n\t\t\t};\n\t\t}\n\n\t\tconst stateRecord = state as Record<string, unknown> | undefined;\n\t\tconst agentName = readString(stateRecord?.agentName) ?? \"The agent\";\n\t\tconst senderName = readString(stateRecord?.senderName) ?? \"someone\";\n\t\tconst senderIdentity = describeCurrentSpeaker(message, senderName);\n\n\t\tlet responseText = \"\";\n\t\tlet channelType = \"\";\n\t\tlet serverName = \"\";\n\t\tconst channelId = room.channelId ?? \"\";\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 ${senderIdentity}. ${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 (!channelId) {\n\t\t\t\truntime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:provider:channelState\",\n\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t\troomId: room.id,\n\t\t\t\t\t},\n\t\t\t\t\t\"No channel ID found\",\n\t\t\t\t);\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\tconst discordService = runtime.getService(\n\t\t\t\tServiceType.DISCORD,\n\t\t\t) as DiscordService;\n\t\t\tif (!discordService) {\n\t\t\t\truntime.logger.warn(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:provider:channelState\",\n\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t\tchannelId,\n\t\t\t\t\t},\n\t\t\t\t\t\"No discord client found\",\n\t\t\t\t);\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\tchannelId,\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\tchannelId,\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\t// Look up guild via channel instead of serverId (which is now a UUID)\n\t\t\t// Try cache first, then fetch if not cached (handles cold start / partial cache scenarios)\n\t\t\tlet channel = discordService.client?.channels.cache.get(channelId) as\n\t\t\t\t| GuildChannel\n\t\t\t\t| undefined;\n\t\t\tif (!channel && discordService.client) {\n\t\t\t\ttry {\n\t\t\t\t\tchannel = (await discordService.client.channels.fetch(channelId)) as\n\t\t\t\t\t\t| GuildChannel\n\t\t\t\t\t\t| undefined;\n\t\t\t\t} catch (fetchError) {\n\t\t\t\t\truntime.logger.debug(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord:provider:channelState\",\n\t\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t\t\tchannelId,\n\t\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\tfetchError instanceof Error\n\t\t\t\t\t\t\t\t\t? fetchError.message\n\t\t\t\t\t\t\t\t\t: String(fetchError),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Failed to fetch channel\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst guild = channel?.guild;\n\t\t\tif (!guild) {\n\t\t\t\truntime.logger.warn(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:provider:channelState\",\n\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t\tchannelId,\n\t\t\t\t\t},\n\t\t\t\t\t\"Guild not found for channel (not in cache and fetch failed)\",\n\t\t\t\t);\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\tchannelId,\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\tchannelId,\n\t\t\t\t\t},\n\t\t\t\t\ttext: \"\",\n\t\t\t\t};\n\t\t\t}\n\t\t\tserverName = guild.name;\n\t\t\tconst agentIdentity = describeAgentDiscordAccount(\n\t\t\t\tagentName,\n\t\t\t\tdiscordService.client?.user?.username,\n\t\t\t);\n\n\t\t\tresponseText = `${agentName} is currently having a conversation in the channel \\`#${channel?.name || channelId}\\` in the server \\`${serverName}\\``;\n\t\t\tresponseText += `\\nThe current speaker is ${senderIdentity}.`;\n\t\t\tif (agentIdentity !== agentName) {\n\t\t\t\tresponseText += `\\nOn Discord, ${agentName} is logged in as ${agentIdentity}.`;\n\t\t\t}\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\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",
|
|
36
|
+
"import type {\n\tChannelType,\n\tCharacter,\n\tEntityPayload,\n\tEventPayload,\n\tIAgentRuntime,\n\tMedia,\n\tMemory,\n\tMessagePayload,\n\tReplyToMode,\n\tWorldPayload,\n} from \"@elizaos/core\";\nimport type {\n\tChannel,\n\tClient as DiscordJsClient,\n\tGuild,\n\tGuildMember,\n\tInteraction,\n\tMessage,\n\tMessageReaction,\n\tUser,\n\tVoiceState,\n} from \"discord.js\";\n\n/**\n * Discord-specific event types\n */\nexport enum DiscordEventTypes {\n\tMESSAGE_RECEIVED = \"DISCORD_MESSAGE_RECEIVED\",\n\tMESSAGE_SENT = \"DISCORD_MESSAGE_SENT\",\n\tSLASH_COMMAND = \"DISCORD_SLASH_COMMAND\",\n\tMODAL_SUBMIT = \"DISCORD_MODAL_SUBMIT\",\n\tREACTION_RECEIVED = \"DISCORD_REACTION_RECEIVED\",\n\tREACTION_REMOVED = \"DISCORD_REACTION_REMOVED\",\n\tWORLD_JOINED = \"DISCORD_WORLD_JOINED\",\n\tWORLD_CONNECTED = \"DISCORD_SERVER_CONNECTED\",\n\tENTITY_JOINED = \"DISCORD_USER_JOINED\",\n\tENTITY_LEFT = \"DISCORD_USER_LEFT\",\n\tVOICE_STATE_CHANGED = \"DISCORD_VOICE_STATE_CHANGED\",\n\tCHANNEL_PERMISSIONS_CHANGED = \"DISCORD_CHANNEL_PERMISSIONS_CHANGED\",\n\tROLE_PERMISSIONS_CHANGED = \"DISCORD_ROLE_PERMISSIONS_CHANGED\",\n\tMEMBER_ROLES_CHANGED = \"DISCORD_MEMBER_ROLES_CHANGED\",\n\tROLE_CREATED = \"DISCORD_ROLE_CREATED\",\n\tROLE_DELETED = \"DISCORD_ROLE_DELETED\",\n\tLISTEN_CHANNEL_MESSAGE = \"DISCORD_LISTEN_CHANNEL_MESSAGE\",\n\tNOT_IN_CHANNELS_MESSAGE = \"DISCORD_NOT_IN_CHANNELS_MESSAGE\",\n}\n\nexport interface DiscordMessageReceivedPayload extends MessagePayload {\n\toriginalMessage: Message;\n}\n\nexport interface DiscordMessageSentPayload extends MessagePayload {\n\toriginalMessages: Message[];\n}\n\nexport interface DiscordReactionPayload extends MessagePayload {\n\toriginalReaction: MessageReaction;\n\tuser: User;\n}\n\nexport interface DiscordServerPayload extends WorldPayload {\n\tserver: Guild;\n}\n\nexport interface DiscordUserJoinedPayload extends EntityPayload {\n\tmember: GuildMember;\n}\n\nexport interface DiscordUserLeftPayload extends EntityPayload {\n\tmember: GuildMember;\n}\n\nexport interface DiscordVoiceStateChangedPayload {\n\tvoiceState: VoiceState;\n}\n\nexport interface DiscordListenChannelPayload {\n\truntime: IAgentRuntime;\n\tmessage: Memory;\n\tsource: string;\n}\n\nexport interface DiscordNotInChannelsPayload {\n\truntime: IAgentRuntime;\n\tmessage: Message;\n\tsource: string;\n}\n\nexport type PermissionState = \"ALLOW\" | \"DENY\" | \"NEUTRAL\";\n\nexport interface PermissionDiff {\n\tpermission: string;\n\toldState: PermissionState;\n\tnewState: PermissionState;\n}\n\nexport interface AuditInfo {\n\texecutorId: string;\n\texecutorTag: string;\n\treason: string | null;\n}\n\nexport interface ChannelPermissionsChangedPayload extends EventPayload {\n\tguild: { id: string; name: string };\n\tchannel: { id: string; name: string };\n\ttarget: { type: \"role\" | \"user\"; id: string; name: string };\n\taction: \"CREATE\" | \"UPDATE\" | \"DELETE\";\n\tchanges: PermissionDiff[];\n\taudit: AuditInfo | null;\n}\n\nexport interface RolePermissionsChangedPayload extends EventPayload {\n\tguild: { id: string; name: string };\n\trole: { id: string; name: string };\n\tchanges: PermissionDiff[];\n\taudit: AuditInfo | null;\n}\n\nexport interface MemberRolesChangedPayload extends EventPayload {\n\tguild: { id: string; name: string };\n\tmember: { id: string; tag: string };\n\tadded: Array<{ id: string; name: string; permissions: string[] }>;\n\tremoved: Array<{ id: string; name: string; permissions: string[] }>;\n\taudit: AuditInfo | null;\n}\n\nexport interface RoleLifecyclePayload extends EventPayload {\n\tguild: { id: string; name: string };\n\trole: { id: string; name: string; permissions: string[] };\n\taudit: AuditInfo | null;\n}\n\nexport interface DiscordSlashCommand {\n\tname: string;\n\tdescription: string;\n\toptions?: Array<{\n\t\tname: string;\n\t\ttype: number;\n\t\tdescription: string;\n\t\trequired?: boolean;\n\t\tchannel_types?: number[];\n\t}>;\n\tguildOnly?: boolean;\n\tbypassChannelWhitelist?: boolean;\n\trequiredPermissions?: bigint | string | null;\n\tcontexts?: number[];\n\tguildIds?: string[];\n\tvalidator?: (\n\t\tinteraction: Interaction,\n\t\truntime: IAgentRuntime,\n\t) => Promise<boolean>;\n}\n\nexport interface DiscordRegisterCommandsPayload extends EventPayload {\n\tcommands: DiscordSlashCommand[];\n}\n\nexport interface DiscordSlashCommandPayload extends EventPayload {\n\tinteraction: Interaction;\n\tclient: DiscordJsClient;\n\tcommands: DiscordSlashCommand[];\n}\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.SLASH_COMMAND]: DiscordSlashCommandPayload;\n\t[DiscordEventTypes.MODAL_SUBMIT]: DiscordSlashCommandPayload;\n\t[DiscordEventTypes.VOICE_STATE_CHANGED]: DiscordVoiceStateChangedPayload;\n\t[DiscordEventTypes.CHANNEL_PERMISSIONS_CHANGED]: ChannelPermissionsChangedPayload;\n\t[DiscordEventTypes.ROLE_PERMISSIONS_CHANGED]: RolePermissionsChangedPayload;\n\t[DiscordEventTypes.MEMBER_ROLES_CHANGED]: MemberRolesChangedPayload;\n\t[DiscordEventTypes.ROLE_CREATED]: RoleLifecyclePayload;\n\t[DiscordEventTypes.ROLE_DELETED]: RoleLifecyclePayload;\n\t[DiscordEventTypes.LISTEN_CHANNEL_MESSAGE]: DiscordListenChannelPayload;\n\t[DiscordEventTypes.NOT_IN_CHANNELS_MESSAGE]: DiscordNotInChannelsPayload;\n}\n\nexport type JsonValue =\n\t| string\n\t| number\n\t| boolean\n\t| null\n\t| JsonValue[]\n\t| { [key: string]: JsonValue };\n\nexport type JsonObject = { [key: string]: JsonValue };\n\nexport interface BuildMemoryFromMessageOptions {\n\tprocessedContent?: string;\n\tprocessedAttachments?: Media[];\n\textraContent?: JsonObject;\n\textraMetadata?: JsonObject;\n}\n\nexport interface IDiscordService {\n\tclient: DiscordJsClient | null;\n\tcharacter: Character;\n\tgetChannelType: (channel: Channel) => Promise<ChannelType>;\n\tbuildMemoryFromMessage: (\n\t\tmessage: Message,\n\t\toptions?: BuildMemoryFromMessageOptions,\n\t) => Promise<Memory | null>;\n}\n\nexport const DISCORD_SERVICE_NAME = \"discord\";\n\nexport const ServiceType = {\n\tDISCORD: \"discord\",\n} as const;\n\nexport interface DiscordComponentOptions {\n\ttype: number;\n\tcustom_id: string;\n\tlabel?: string;\n\tstyle?: number;\n\tplaceholder?: string;\n\tmin_values?: number;\n\tmax_values?: number;\n\toptions?: Array<{\n\t\tlabel: string;\n\t\tvalue: string;\n\t\tdescription?: string;\n\t}>;\n}\n\nexport interface DiscordActionRow {\n\ttype: 1;\n\tcomponents: DiscordComponentOptions[];\n}\n\n/**\n * DM access policy for Discord messages.\n * - \"open\": Allow all DMs\n * - \"allowlist\": Only allow DMs from users in allowFrom list\n * - \"pairing\": Require pairing code approval for new DM senders\n * - \"disabled\": Ignore all DMs\n */\nexport type DiscordDmPolicy = \"open\" | \"allowlist\" | \"pairing\" | \"disabled\";\n\nexport interface DiscordSettings {\n\tallowedChannelIds?: string[];\n\tshouldIgnoreBotMessages?: boolean;\n\tshouldIgnoreDirectMessages?: boolean;\n\tshouldRespondOnlyToMentions?: boolean;\n\treplyToMode?: ReplyToMode;\n\t/** DM access policy (default: \"pairing\") */\n\tdmPolicy?: DiscordDmPolicy;\n\t/** List of allowed Discord user IDs for allowlist policy */\n\tallowFrom?: string[];\n\t/** Whether the connector should synchronize the bot profile on startup. */\n\tsyncProfile?: boolean;\n\t/** Optional explicit bot username override; falls back to character.name. */\n\tprofileName?: string;\n\t/** Optional avatar source (https URL, data URI, or local file path). */\n\tprofileAvatar?: string;\n\t/**\n\t * When false (default), inbound messages are ingested into memory but the\n\t * agent does NOT auto-generate a reply. Sends only happen when the user\n\t * explicitly dispatches via LifeOps or chat command. Opt in by setting\n\t * DISCORD_AUTO_REPLY=true.\n\t */\n\tautoReply?: boolean;\n}\n\nexport interface ChannelSpiderState {\n\tchannelId: string;\n\toldestMessageId?: string;\n\tnewestMessageId?: string;\n\toldestMessageTimestamp?: number;\n\tnewestMessageTimestamp?: number;\n\tlastSpideredAt: number;\n\tfullyBackfilled: boolean;\n}\n\nexport type BatchHandler = (\n\tbatch: Memory[],\n\tstats: { page: number; totalFetched: number; totalStored: number },\n) => Promise<boolean | undefined> | boolean | undefined;\n\nexport interface ChannelHistoryOptions {\n\tlimit?: number;\n\tforce?: boolean;\n\tonBatch?: BatchHandler;\n\tbefore?: string;\n\tafter?: string;\n}\n\nexport interface ChannelHistoryResult {\n\tmessages: Memory[];\n\tstats: {\n\t\tfetched: number;\n\t\tstored: number;\n\t\tpages: number;\n\t\tfullyBackfilled: boolean;\n\t};\n}\n\nexport interface DiscordApiCommand {\n\tname: string;\n\tdescription: string;\n\toptions?: DiscordCommandOption[];\n\tdefault_member_permissions?: string | null;\n\tcontexts?: number[];\n}\n\nexport interface DiscordCommandOption {\n\tname: string;\n\ttype: number;\n\tdescription: string;\n\trequired?: boolean;\n\tchannel_types?: number[];\n}\n\nexport interface UserSelectionState {\n\t[key: string]: string | number | boolean | string[];\n}\n\nexport interface DiscordMessageSendOptions {\n\tcontent: string;\n\treply?: {\n\t\tmessageReference: string;\n\t};\n\tfiles?: Array<{ attachment: Buffer | string; name: string }>;\n\tcomponents?: DiscordActionRow[];\n}\n\nexport class DiscordPluginError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly code: string,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"DiscordPluginError\";\n\t}\n}\n\nexport class DiscordServiceNotInitializedError extends DiscordPluginError {\n\tconstructor() {\n\t\tsuper(\"Discord service is not initialized\", \"SERVICE_NOT_INITIALIZED\");\n\t\tthis.name = \"DiscordServiceNotInitializedError\";\n\t}\n}\n\nexport class DiscordClientNotAvailableError extends DiscordPluginError {\n\tconstructor() {\n\t\tsuper(\"Discord client is not available\", \"CLIENT_NOT_AVAILABLE\");\n\t\tthis.name = \"DiscordClientNotAvailableError\";\n\t}\n}\n\nexport class DiscordConfigurationError extends DiscordPluginError {\n\tconstructor(missingConfig: string) {\n\t\tsuper(`Missing required configuration: ${missingConfig}`, \"MISSING_CONFIG\");\n\t\tthis.name = \"DiscordConfigurationError\";\n\t}\n}\n\nexport class DiscordApiError extends DiscordPluginError {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly apiErrorCode?: number,\n\t) {\n\t\tsuper(message, \"API_ERROR\");\n\t\tthis.name = \"DiscordApiError\";\n\t}\n}\n\nexport type DiscordSnowflake = string & {\n\treadonly __brand: \"DiscordSnowflake\";\n};\n\nexport function validateSnowflake(id: string): DiscordSnowflake {\n\tif (!/^\\d{17,19}$/.test(id)) {\n\t\tthrow new DiscordPluginError(\n\t\t\t`Invalid Discord snowflake ID: ${id}`,\n\t\t\t\"INVALID_SNOWFLAKE\",\n\t\t);\n\t}\n\treturn id as DiscordSnowflake;\n}\n\nexport function isValidSnowflake(id: string): id is DiscordSnowflake {\n\treturn /^\\d{17,19}$/.test(id);\n}\n",
|
|
37
|
+
"import type { IAgentRuntime, Memory, Provider, State } from \"@elizaos/core\";\nimport type { Guild, GuildChannel, Role } from \"discord.js\";\nimport { requireProviderSpec } from \"../generated/specs/spec-helpers\";\nimport type { DiscordService } from \"../service\";\nimport { ServiceType } from \"../types\";\n\nconst spec = requireProviderSpec(\"guildInfo\");\n\n/**\n * Represents a provider for retrieving guild/server information.\n * @type {Provider}\n * @property {string} name - The name of the guild info provider.\n * @property {Function} get - Asynchronous function that retrieves guild information\n * 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 guild data, values, and text.\n */\nexport const guildInfoProvider: Provider = {\n\tname: spec.name,\n\tdynamic: true,\n\tget: async (runtime: IAgentRuntime, message: Memory, state: State) => {\n\t\t// If message source is not discord, return empty\n\t\tif (message.content.source !== \"discord\") {\n\t\t\treturn {\n\t\t\t\tdata: {},\n\t\t\t\tvalues: {},\n\t\t\t\ttext: \"\",\n\t\t\t};\n\t\t}\n\n\t\tconst room = state.data?.room ?? (await runtime.getRoom(message.roomId));\n\t\tif (!room) {\n\t\t\treturn {\n\t\t\t\tdata: { isInGuild: false },\n\t\t\t\tvalues: { isInGuild: false },\n\t\t\t\ttext: \"\",\n\t\t\t};\n\t\t}\n\n\t\tconst channelId = room.channelId ?? \"\";\n\t\tif (!channelId) {\n\t\t\treturn {\n\t\t\t\tdata: { isInGuild: false },\n\t\t\t\tvalues: { isInGuild: false },\n\t\t\t\ttext: \"\",\n\t\t\t};\n\t\t}\n\n\t\tconst discordService = runtime.getService(\n\t\t\tServiceType.DISCORD,\n\t\t) as DiscordService;\n\t\tif (!discordService?.client) {\n\t\t\truntime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:provider:guildInfo\",\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\tchannelId,\n\t\t\t\t},\n\t\t\t\t\"No discord client found\",\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tdata: { isInGuild: false },\n\t\t\t\tvalues: { isInGuild: false },\n\t\t\t\ttext: \"\",\n\t\t\t};\n\t\t}\n\n\t\t// Try to get the channel to find the guild\n\t\tlet channel = discordService.client.channels.cache.get(channelId) as\n\t\t\t| GuildChannel\n\t\t\t| undefined;\n\t\tif (!channel) {\n\t\t\ttry {\n\t\t\t\tchannel = (await discordService.client.channels.fetch(channelId)) as\n\t\t\t\t\t| GuildChannel\n\t\t\t\t\t| undefined;\n\t\t\t} catch (fetchError) {\n\t\t\t\truntime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:provider:guildInfo\",\n\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t\tchannelId,\n\t\t\t\t\t\terror:\n\t\t\t\t\t\t\tfetchError instanceof Error\n\t\t\t\t\t\t\t\t? fetchError.message\n\t\t\t\t\t\t\t\t: String(fetchError),\n\t\t\t\t\t},\n\t\t\t\t\t\"Failed to fetch channel\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst guild = channel?.guild;\n\t\tif (!guild) {\n\t\t\treturn {\n\t\t\t\tdata: { isInGuild: false, channelId },\n\t\t\t\tvalues: { isInGuild: false },\n\t\t\t\ttext: \"\",\n\t\t\t};\n\t\t}\n\n\t\t// Fetch guild info\n\t\tconst guildInfo = await getGuildInfo(guild, discordService);\n\n\t\tconst responseText = formatGuildInfoText(guild, guildInfo);\n\n\t\treturn {\n\t\t\tdata: {\n\t\t\t\tisInGuild: true,\n\t\t\t\tguildId: guild.id,\n\t\t\t\tguild: guildInfo,\n\t\t\t},\n\t\t\tvalues: {\n\t\t\t\tisInGuild: true,\n\t\t\t\tguildId: guild.id,\n\t\t\t\tguildName: guild.name,\n\t\t\t\tmemberCount: guildInfo.memberCount,\n\t\t\t\tchannelCount: guildInfo.channelCount,\n\t\t\t},\n\t\t\ttext: responseText,\n\t\t};\n\t},\n};\n\ninterface GuildInfo {\n\tname: string;\n\tmemberCount: number;\n\tchannelCount: number;\n\troleCount: number;\n\townerId: string;\n\townerName: string;\n\tdescription: string | null;\n\tcreatedAt: string;\n\tpremiumTier: number;\n\tpremiumSubscriptionCount: number;\n\tchannels: {\n\t\ttext: Array<{ id: string; name: string }>;\n\t\tvoice: Array<{ id: string; name: string }>;\n\t\tcategories: Array<{ id: string; name: string }>;\n\t};\n\troles: Array<{ id: string; name: string; color: string }>;\n\tbotPermissions: {\n\t\tadministrator: boolean;\n\t\tmanageMessages: boolean;\n\t\tmanageChannels: boolean;\n\t\tmanageRoles: boolean;\n\t};\n}\n\nasync function getGuildInfo(\n\tguild: Guild,\n\tdiscordService: DiscordService,\n): Promise<GuildInfo> {\n\t// Get owner\n\tlet ownerName = \"Unknown\";\n\ttry {\n\t\tconst owner = await guild.fetchOwner();\n\t\townerName = owner.user.username;\n\t} catch (_e) {\n\t\t// Ignore\n\t}\n\n\t// Get bot member for permissions\n\tconst botMember = discordService.client?.user?.id\n\t\t? guild.members.cache.get(discordService.client.user.id)\n\t\t: undefined;\n\n\tconst botPermissions = {\n\t\tadministrator: botMember?.permissions.has(\"Administrator\") ?? false,\n\t\tmanageMessages: botMember?.permissions.has(\"ManageMessages\") ?? false,\n\t\tmanageChannels: botMember?.permissions.has(\"ManageChannels\") ?? false,\n\t\tmanageRoles: botMember?.permissions.has(\"ManageRoles\") ?? false,\n\t};\n\n\t// Categorize channels\n\tconst textChannels: Array<{ id: string; name: string }> = [];\n\tconst voiceChannels: Array<{ id: string; name: string }> = [];\n\tconst categories: Array<{ id: string; name: string }> = [];\n\n\tguild.channels.cache.forEach((channel) => {\n\t\tconst channelData = { id: channel.id, name: channel.name };\n\t\tif (channel.type === 0) {\n\t\t\t// GUILD_TEXT\n\t\t\ttextChannels.push(channelData);\n\t\t} else if (channel.type === 2) {\n\t\t\t// GUILD_VOICE\n\t\t\tvoiceChannels.push(channelData);\n\t\t} else if (channel.type === 4) {\n\t\t\t// GUILD_CATEGORY\n\t\t\tcategories.push(channelData);\n\t\t}\n\t});\n\n\t// Get roles (exclude @everyone)\n\tconst roles = guild.roles.cache\n\t\t.filter((role: Role) => role.name !== \"@everyone\")\n\t\t.map((role: Role) => ({\n\t\t\tid: role.id,\n\t\t\tname: role.name,\n\t\t\tcolor: role.hexColor,\n\t\t}));\n\n\treturn {\n\t\tname: guild.name,\n\t\tmemberCount: guild.memberCount,\n\t\tchannelCount: guild.channels.cache.size,\n\t\troleCount: guild.roles.cache.size,\n\t\townerId: guild.ownerId,\n\t\townerName,\n\t\tdescription: guild.description,\n\t\tcreatedAt: guild.createdAt.toISOString(),\n\t\tpremiumTier: guild.premiumTier,\n\t\tpremiumSubscriptionCount: guild.premiumSubscriptionCount ?? 0,\n\t\tchannels: {\n\t\t\ttext: textChannels,\n\t\t\tvoice: voiceChannels,\n\t\t\tcategories,\n\t\t},\n\t\troles: Array.from(roles),\n\t\tbotPermissions,\n\t};\n}\n\nfunction formatGuildInfoText(guild: Guild, info: GuildInfo): string {\n\tconst lines = [\n\t\t`The current server is \"${guild.name}\" with ${info.memberCount} members.`,\n\t\t`The server was created on ${new Date(info.createdAt).toLocaleDateString()}.`,\n\t];\n\n\tif (info.description) {\n\t\tlines.push(`Server description: ${info.description}`);\n\t}\n\n\tlines.push(\n\t\t`The server has ${info.channels.text.length} text channels, ${info.channels.voice.length} voice channels, and ${info.roleCount} roles.`,\n\t);\n\n\tif (info.premiumTier > 0) {\n\t\tlines.push(\n\t\t\t`The server is boosted to tier ${info.premiumTier} with ${info.premiumSubscriptionCount} boosts.`,\n\t\t);\n\t}\n\n\treturn lines.join(\" \");\n}\n\nexport default guildInfoProvider;\n",
|
|
38
|
+
"import type {\n\tIAgentRuntime,\n\tMemory,\n\tProvider,\n\tProviderResult,\n\tState,\n\tUUID,\n} from \"@elizaos/core\";\nimport { ChannelType } from \"@elizaos/core\";\nimport type { GuildChannel } from \"discord.js\";\nimport { requireProviderSpec } from \"../generated/specs/spec-helpers\";\nimport type { DiscordService } from \"../service\";\nimport { ServiceType } from \"../types\";\n\nconst spec = requireProviderSpec(\"voiceState\");\n\n/**\n * Provides information about the voice state of the user, including whether they are currently in a voice channel.\n *\n * @param {IAgentRuntime} runtime - The runtime object for the agent\n * @param {Memory} message - The message object containing room ID\n * @param {State} [state] - Optional state object for the user\n * @returns {Object} An object containing information about the voice state of the user\n */\nexport const voiceStateProvider: Provider = {\n\tname: spec.name,\n\tdynamic: true,\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\troomId: room.id,\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} as ProviderResult;\n\t\t}\n\n\t\tconst channelId = room.channelId;\n\t\tconst agentName = state?.agentName || \"The agent\";\n\n\t\tif (!channelId) {\n\t\t\truntime.logger.warn(\n\t\t\t\t{ src: \"plugin:discord:provider:voiceState\", roomId: room.id },\n\t\t\t\t\"No channel ID found\",\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tdata: {\n\t\t\t\t\tisInVoiceChannel: false,\n\t\t\t\t\troomId: room.id,\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: `${agentName} is not currently in a voice channel`,\n\t\t\t} as ProviderResult;\n\t\t}\n\n\t\t// Look up guild via channel to get the Discord guild ID for voice connection\n\t\tconst discordService = runtime.getService(\n\t\t\tServiceType.DISCORD,\n\t\t) as DiscordService;\n\t\tif (!discordService?.client) {\n\t\t\truntime.logger.warn(\n\t\t\t\t{ src: \"plugin:discord:provider:voiceState\" },\n\t\t\t\t\"Discord service not available\",\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tdata: {\n\t\t\t\t\tisInVoiceChannel: false,\n\t\t\t\t\troomId: room.id,\n\t\t\t\t},\n\t\t\t\tvalues: {\n\t\t\t\t\tisInVoiceChannel: \"false\",\n\t\t\t\t},\n\t\t\t\ttext: `${agentName} is not currently in a voice channel`,\n\t\t\t} as ProviderResult;\n\t\t}\n\n\t\t// Try cache first, then fetch if not cached (handles cold start / partial cache scenarios)\n\t\tlet channel = discordService.client.channels.cache.get(channelId) as\n\t\t\t| GuildChannel\n\t\t\t| undefined;\n\t\tif (!channel) {\n\t\t\ttry {\n\t\t\t\tchannel = (await discordService.client.channels.fetch(channelId)) as\n\t\t\t\t\t| GuildChannel\n\t\t\t\t\t| undefined;\n\t\t\t} catch (fetchError) {\n\t\t\t\truntime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:provider:voiceState\",\n\t\t\t\t\t\tchannelId,\n\t\t\t\t\t\terror:\n\t\t\t\t\t\t\tfetchError instanceof Error\n\t\t\t\t\t\t\t\t? fetchError.message\n\t\t\t\t\t\t\t\t: String(fetchError),\n\t\t\t\t\t},\n\t\t\t\t\t\"Failed to fetch channel\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tconst guildId = channel?.guild?.id;\n\n\t\tif (!guildId) {\n\t\t\truntime.logger.warn(\n\t\t\t\t{ src: \"plugin:discord:provider:voiceState\", channelId },\n\t\t\t\t\"Could not find guild for channel (not in cache and fetch failed)\",\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tdata: {\n\t\t\t\t\tisInVoiceChannel: false,\n\t\t\t\t\troomId: room.id,\n\t\t\t\t},\n\t\t\t\tvalues: {\n\t\t\t\t\tisInVoiceChannel: \"false\",\n\t\t\t\t},\n\t\t\t\ttext: `${agentName} is not currently in a voice channel`,\n\t\t\t} as ProviderResult;\n\t\t}\n\n\t\tconst connection = discordService.voiceManager?.getVoiceConnection(guildId);\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\troomId: room.id,\n\t\t\t\t},\n\t\t\t\tvalues: {\n\t\t\t\t\tisInVoiceChannel: \"false\",\n\t\t\t\t},\n\t\t\t\ttext: `${agentName} is not currently in a voice channel`,\n\t\t\t} as ProviderResult;\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 as UUID);\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 channelName = room.name;\n\n\t\treturn {\n\t\t\tdata: {\n\t\t\t\tisInVoiceChannel: true,\n\t\t\t\troomId: room.id,\n\t\t\t\tworldId: world.id,\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\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} as ProviderResult;\n\t},\n};\n\nexport default voiceStateProvider;\n",
|
|
39
|
+
"import {\n\tChannelType,\n\ttype Character,\n\ttype Content,\n\tcreateUniqueUuid,\n\ttype EventPayload,\n\tgetConnectorAdminWhitelist,\n\ttype IAgentRuntime,\n\ttype Media,\n\ttype Memory,\n\tMemoryType,\n\tService,\n\tsetConnectorAdminWhitelist,\n\tstringToUuid,\n\ttype TargetInfo,\n\ttype UUID,\n} from \"@elizaos/core\";\n/**\n * IMPORTANT: Discord ID Handling - Why stringToUuid() instead of asUUID()\n *\n * Discord uses \"snowflake\" IDs - large 64-bit integers represented as strings\n * (e.g., \"1253563208833433701\"). These are NOT valid UUIDs.\n *\n * UUID format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12 hex digits with dashes)\n * Discord ID: 1253563208833433701 (plain number string)\n *\n * The two UUID-related functions behave differently:\n *\n * - `asUUID(str)` - VALIDATES that the string is already a valid UUID format.\n * If not, it throws: \"Error: Invalid UUID format: 1253563208833433701\"\n * Use only when you're certain the input is already a valid UUID.\n *\n * - `stringToUuid(str)` - CONVERTS any string into a deterministic UUID by hashing it.\n * Always succeeds. The same input always produces the same UUID output.\n * Use this for Discord snowflake IDs.\n *\n * When working with Discord IDs in ElizaOS:\n *\n * 1. `stringToUuid(discordId)` - For storing Discord IDs in UUID fields (e.g., `messageServerId`).\n *\n * 2. `createUniqueUuid(runtime, discordId)` - For `worldId` and `roomId`. This adds the agent's\n * ID to the hash, ensuring each agent has its own unique namespace for the same Discord server.\n *\n * 3. `messageServerId` - The correct property name for server IDs on Room and World objects.\n *\n * 4. Discord-specific events (e.g., DiscordEventTypes.VOICE_STATE_UPDATE) are not in core's\n * EventPayloadMap. When emitting these events, cast to `string[]` and payload to `any`\n * to use the generic emitEvent overload.\n */\nimport {\n\tAttachmentBuilder,\n\ttype Channel,\n\ttype Collection,\n\tChannelType as DiscordChannelType,\n\tClient as DiscordJsClient,\n\tEvents,\n\tGatewayIntentBits,\n\ttype Guild,\n\ttype GuildMember,\n\ttype Interaction,\n\ttype Message,\n\ttype MessageReaction,\n\ttype PartialMessageReaction,\n\tPartials,\n\ttype PartialUser,\n\tPermissionsBitField,\n\ttype TextChannel,\n\ttype User,\n} from \"discord.js\";\nimport { createCompatRuntime, type ICompatRuntime } from \"./compat\";\nimport { DISCORD_SERVICE_NAME } from \"./constants\";\nimport type { ChannelDebouncer, MessageDebouncer } from \"./debouncer\";\nimport {\n\thandleGuildCreate as handleGuildCreateExtracted,\n\tisGuildOnlyCommand,\n\ttransformCommandToDiscordApi,\n} from \"./discord-commands\";\nimport { setupDiscordEventListeners } from \"./discord-events\";\nimport {\n\tbuildMemoryFromMessage as buildMemoryFromMessageExtracted,\n\tfetchChannelHistory as fetchChannelHistoryExtracted,\n} from \"./discord-history\";\nimport {\n\thandleInteractionCreate as handleInteractionCreateExtracted,\n\tonReady as onReadyExtracted,\n} from \"./discord-interactions\";\nimport {\n\thandleReactionAdd as handleReactionAddExtracted,\n\thandleReactionRemove as handleReactionRemoveExtracted,\n} from \"./discord-reactions\";\nimport { getDiscordSettings } from \"./environment\";\nimport {\n\textractDiscordOwnerUserIds,\n\tparseDiscordOwnerUserIds,\n\tresolveDiscordRuntimeEntityId,\n\tresolveElizaOwnerEntityId,\n} from \"./identity\";\nimport { MessageManager } from \"./messages\";\nimport type {\n\tChannelHistoryOptions,\n\tChannelHistoryResult,\n\tDiscordSettings,\n\tDiscordSlashCommand,\n\tIDiscordService,\n} from \"./types\";\nimport { DiscordEventTypes } from \"./types\";\nimport {\n\tgetAttachmentFileName,\n\tMAX_MESSAGE_LENGTH,\n\tnormalizeDiscordMessageText,\n\tsplitMessage,\n} from \"./utils\";\nimport { VoiceManager } from \"./voice\";\n\nconst DISCORD_SNOWFLAKE_PATTERN = /^\\d{15,20}$/;\n\nfunction normalizeDiscordTargetUserId(value: unknown): string | null {\n\tif (typeof value !== \"string\") {\n\t\treturn null;\n\t}\n\tconst trimmed = value.trim();\n\treturn DISCORD_SNOWFLAKE_PATTERN.test(trimmed) ? trimmed : null;\n}\n\nfunction extractDiscordUserIdFromMetadata(metadata: unknown): string | null {\n\tif (!metadata || typeof metadata !== \"object\") {\n\t\treturn null;\n\t}\n\n\tconst record = metadata as Record<string, unknown>;\n\tconst discord =\n\t\trecord.discord && typeof record.discord === \"object\"\n\t\t\t? (record.discord as Record<string, unknown>)\n\t\t\t: null;\n\n\treturn (\n\t\tnormalizeDiscordTargetUserId(discord?.userId) ??\n\t\tnormalizeDiscordTargetUserId(discord?.id) ??\n\t\tnormalizeDiscordTargetUserId(record.originalId)\n\t);\n}\n\n/**\n * DiscordService class representing a service for interacting with Discord.\n * @extends Service\n * @implements IDiscordService\n * @property {string} serviceType - The type of service, set to DISCORD_SERVICE_NAME.\n * @property {string} capabilityDescription - A description of the service's capabilities.\n * @property {DiscordJsClient} client - The DiscordJsClient used for communication.\n * @property {Character} character - The character associated with the service.\n * @property {MessageManager} messageManager - The manager for handling messages.\n * @property {VoiceManager} voiceManager - The manager for handling voice communication.\n */\n\nexport class DiscordService extends Service implements IDiscordService {\n\t// Override runtime type for messageServerId cross-core compatibility (see compat.ts)\n\tprotected declare runtime: ICompatRuntime;\n\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\tdiscordSettings: DiscordSettings;\n\tmessageManager?: MessageManager;\n\tvoiceManager?: VoiceManager;\n\tprivate messageDebouncer?: MessageDebouncer;\n\tprivate channelDebouncer?: ChannelDebouncer;\n\tprivate _loginFailed = false;\n\tprivate userSelections: Map<string, Record<string, unknown>> = new Map();\n\tprivate timeouts: ReturnType<typeof setTimeout>[] = [];\n\tpublic clientReadyPromise: Promise<void> | null = null;\n\t/**\n\t * List of allowed channel IDs (parsed from CHANNEL_IDS env var).\n\t * If undefined, all channels are allowed.\n\t */\n\tprivate allowedChannelIds?: string[];\n\n\t/**\n\t * Set of dynamically added channel IDs through joinChannel action.\n\t * These are merged with allowedChannelIds for runtime channel management.\n\t */\n\tprivate dynamicChannelIds: Set<string> = new Set();\n\tprivate ownerDiscordUserIds: Set<string> = new Set();\n\n\t// Slash command registration state. Mutated by registerSlashCommands and\n\t// read by onReadyExtracted via the InteractionServiceInternals contract.\n\tpublic slashCommands: DiscordSlashCommand[] = [];\n\tprivate commandRegistrationQueue: Promise<void> = Promise.resolve();\n\tpublic allowAllSlashCommands: Set<string> = new Set();\n\n\t/**\n\t * Resolves owner Discord user IDs from either the explicit\n\t * ELIZA_DISCORD_OWNER_USER_IDS_JSON setting or the Discord application's\n\t * team/owner metadata, and registers them as Discord connector admins.\n\t * Called from the extracted onReady handler once the client is ready.\n\t */\n\tpublic async refreshOwnerDiscordUserIds(\n\t\tclient: DiscordJsClient,\n\t): Promise<void> {\n\t\tconst explicitSetting = this.runtime.getSetting?.(\n\t\t\t\"ELIZA_DISCORD_OWNER_USER_IDS_JSON\",\n\t\t);\n\t\tconst hasExplicitSetting =\n\t\t\texplicitSetting !== undefined &&\n\t\t\texplicitSetting !== null &&\n\t\t\t!(typeof explicitSetting === \"string\" && explicitSetting.trim() === \"\");\n\n\t\tlet ownerIds: string[];\n\t\tif (hasExplicitSetting) {\n\t\t\townerIds = parseDiscordOwnerUserIds(\n\t\t\t\tArray.isArray(explicitSetting)\n\t\t\t\t\t? explicitSetting\n\t\t\t\t\t: typeof explicitSetting === \"string\"\n\t\t\t\t\t\t? explicitSetting\n\t\t\t\t\t\t: [String(explicitSetting)],\n\t\t\t);\n\t\t} else {\n\t\t\tlet application: unknown;\n\t\t\ttry {\n\t\t\t\tapplication =\n\t\t\t\t\tclient.application && typeof client.application.fetch === \"function\"\n\t\t\t\t\t\t? await client.application.fetch()\n\t\t\t\t\t\t: client.application;\n\t\t\t} catch (error) {\n\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t},\n\t\t\t\t\t\"Failed to fetch Discord application — owner will not be recognized. \" +\n\t\t\t\t\t\t\"Set ELIZA_DISCORD_OWNER_USER_IDS_JSON to fix this.\",\n\t\t\t\t);\n\t\t\t\tapplication = client.application;\n\t\t\t}\n\t\t\townerIds = [...new Set(extractDiscordOwnerUserIds(application))];\n\t\t}\n\n\t\tthis.ownerDiscordUserIds = new Set(ownerIds);\n\t\tif (ownerIds.length === 0) {\n\t\t\tthis.runtime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t},\n\t\t\t\t\"No Discord owner user IDs resolved — owner will not be recognized from Discord messages. \" +\n\t\t\t\t\t\"Set ELIZA_DISCORD_OWNER_USER_IDS_JSON to fix this.\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst existingWhitelist = getConnectorAdminWhitelist(this.runtime);\n\t\tconst nextDiscordAdmins = [\n\t\t\t...new Set([...(existingWhitelist.discord ?? []), ...ownerIds]),\n\t\t];\n\t\tsetConnectorAdminWhitelist(this.runtime, {\n\t\t\t...existingWhitelist,\n\t\t\tdiscord: nextDiscordAdmins,\n\t\t});\n\t\tthis.runtime.logger.info(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\townerDiscordUserIds: ownerIds,\n\t\t\t},\n\t\t\t\"Resolved Discord owner identities for canonical Eliza owner mapping\",\n\t\t);\n\t}\n\n\t/**\n\t * Registers slash commands with Discord. Called from the onReady event\n\t * handler via the DISCORD_REGISTER_COMMANDS event emitted by\n\t * registerBuiltinSlashCommands(). Merges incoming commands with the\n\t * existing set, then pushes them to Discord both globally (for DMs) and\n\t * per-guild (for instant availability).\n\t */\n\tpublic async registerSlashCommands(\n\t\tcommands: DiscordSlashCommand[],\n\t): Promise<void> {\n\t\tawait this.clientReadyPromise;\n\n\t\tconst clientApplication = this.client?.application;\n\t\tif (!clientApplication) {\n\t\t\tthis.runtime.logger.warn(\n\t\t\t\t{ src: \"plugin:discord\", agentId: this.runtime.agentId },\n\t\t\t\t\"Cannot register commands - Discord client application not available\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!Array.isArray(commands) || commands.length === 0) {\n\t\t\tthis.runtime.logger.warn(\n\t\t\t\t{ src: \"plugin:discord\", agentId: this.runtime.agentId },\n\t\t\t\t\"Cannot register commands - no commands provided\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const cmd of commands) {\n\t\t\tif (!cmd.name || !cmd.description) {\n\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t{ src: \"plugin:discord\", agentId: this.runtime.agentId },\n\t\t\t\t\t\"Cannot register commands - invalid command (missing name or description)\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tlet registrationError: Error | null = null;\n\t\tlet registrationFailed = false;\n\n\t\tthis.commandRegistrationQueue = this.commandRegistrationQueue\n\t\t\t.then(async () => {\n\t\t\t\tconst commandMap = new Map<string, DiscordSlashCommand>();\n\t\t\t\tfor (const cmd of this.slashCommands) {\n\t\t\t\t\tif (cmd.name) commandMap.set(cmd.name, cmd);\n\t\t\t\t}\n\t\t\t\tfor (const cmd of commands) {\n\t\t\t\t\tif (cmd.name) commandMap.set(cmd.name, cmd);\n\t\t\t\t}\n\t\t\t\tthis.slashCommands = Array.from(commandMap.values());\n\n\t\t\t\tthis.allowAllSlashCommands.clear();\n\t\t\t\tfor (const cmd of this.slashCommands) {\n\t\t\t\t\tif (cmd.bypassChannelWhitelist) {\n\t\t\t\t\t\tthis.allowAllSlashCommands.add(cmd.name);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst generalCommands = this.slashCommands.filter(\n\t\t\t\t\t(cmd) => cmd.guildIds.length === 0,\n\t\t\t\t);\n\t\t\t\tconst globalCommands = generalCommands.filter(\n\t\t\t\t\t(cmd) => !isGuildOnlyCommand(cmd),\n\t\t\t\t);\n\t\t\t\tconst guildOnlyCommands = generalCommands.filter((cmd) =>\n\t\t\t\t\tisGuildOnlyCommand(cmd),\n\t\t\t\t);\n\t\t\t\tconst targetedGuildCommands = this.slashCommands.filter(\n\t\t\t\t\t(cmd) => cmd.guildIds && cmd.guildIds.length > 0,\n\t\t\t\t);\n\n\t\t\t\tconst transformedGlobalCommands = globalCommands.map((cmd) =>\n\t\t\t\t\ttransformCommandToDiscordApi(cmd),\n\t\t\t\t);\n\t\t\t\tconst transformedGuildOnlyCommands = guildOnlyCommands.map((cmd) =>\n\t\t\t\t\ttransformCommandToDiscordApi(cmd),\n\t\t\t\t);\n\t\t\t\tconst transformedAllGeneralCommands = [\n\t\t\t\t\t...transformedGlobalCommands,\n\t\t\t\t\t...transformedGuildOnlyCommands,\n\t\t\t\t];\n\n\t\t\t\tconst clientApp = this.client?.application;\n\t\t\t\tif (!clientApp) {\n\t\t\t\t\tthrow new Error(\"Discord client application is not available\");\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tawait clientApp.commands.set(transformedGlobalCommands);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Failed to register/clear global commands\",\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst guilds = this.client?.guilds.cache;\n\t\t\t\tif (guilds && transformedAllGeneralCommands.length > 0) {\n\t\t\t\t\tawait Promise.all(\n\t\t\t\t\t\t[...guilds].map(async ([guildId, guild]) => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait clientApp.commands.set(\n\t\t\t\t\t\t\t\t\ttransformedAllGeneralCommands,\n\t\t\t\t\t\t\t\t\tguildId,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\t\t\t\tguildId,\n\t\t\t\t\t\t\t\t\t\tguildName: guild.name,\n\t\t\t\t\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\"Failed to register commands to guild\",\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\n\t\t\t\tif (guilds && targetedGuildCommands.length > 0) {\n\t\t\t\t\tawait Promise.all(\n\t\t\t\t\t\ttargetedGuildCommands.flatMap((cmd) => {\n\t\t\t\t\t\t\tconst transformedCmd = transformCommandToDiscordApi(cmd);\n\t\t\t\t\t\t\treturn (cmd.guildIds ?? []).map(async (guildId) => {\n\t\t\t\t\t\t\t\tconst guild = guilds.get(guildId);\n\t\t\t\t\t\t\t\tif (!guild) return;\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tconst fullGuild = await guild.fetch();\n\t\t\t\t\t\t\t\t\tconst existingCommands = await fullGuild.commands.fetch();\n\t\t\t\t\t\t\t\t\tconst existingCommand = existingCommands.find(\n\t\t\t\t\t\t\t\t\t\t(c) => c.name === cmd.name,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tif (existingCommand) {\n\t\t\t\t\t\t\t\t\t\tawait existingCommand.edit(\n\t\t\t\t\t\t\t\t\t\t\ttransformedCmd as Partial<\n\t\t\t\t\t\t\t\t\t\t\t\timport(\"discord.js\").ApplicationCommandData\n\t\t\t\t\t\t\t\t\t\t\t>,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tawait fullGuild.commands.create(transformedCmd);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\t\t\t\t\tcommandName: cmd.name,\n\t\t\t\t\t\t\t\t\t\t\tguildId,\n\t\t\t\t\t\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"Failed to register targeted command in guild\",\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\n\t\t\t\tthis.runtime.logger.info(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tnewCommands: commands.length,\n\t\t\t\t\t\ttotalCommands: this.slashCommands.length,\n\t\t\t\t\t},\n\t\t\t\t\t\"Commands registered\",\n\t\t\t\t);\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tregistrationFailed = true;\n\t\t\t\tregistrationError =\n\t\t\t\t\terror instanceof Error ? error : new Error(String(error));\n\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\terror: registrationError.message,\n\t\t\t\t\t},\n\t\t\t\t\t\"Error registering Discord commands\",\n\t\t\t\t);\n\t\t\t});\n\n\t\tawait this.commandRegistrationQueue;\n\n\t\tif (registrationFailed && registrationError) {\n\t\t\tthrow registrationError;\n\t\t}\n\t}\n\n\tprivate async resolveDiscordTargetUserId(\n\t\ttargetEntityId: string,\n\t): Promise<string | null> {\n\t\tconst directId = normalizeDiscordTargetUserId(targetEntityId);\n\t\tif (directId) {\n\t\t\treturn directId;\n\t\t}\n\n\t\tif (targetEntityId === resolveElizaOwnerEntityId(this.runtime)) {\n\t\t\tconst knownOwnerUserId = this.ownerDiscordUserIds.values().next().value;\n\t\t\tif (typeof knownOwnerUserId === \"string\" && knownOwnerUserId.length > 0) {\n\t\t\t\treturn knownOwnerUserId;\n\t\t\t}\n\t\t}\n\n\t\tconst directEntity = this.runtime.getEntityById\n\t\t\t? await this.runtime.getEntityById(targetEntityId as UUID)\n\t\t\t: null;\n\t\tconst directMetadataUserId = extractDiscordUserIdFromMetadata(\n\t\t\tdirectEntity?.metadata,\n\t\t);\n\t\tif (directMetadataUserId) {\n\t\t\treturn directMetadataUserId;\n\t\t}\n\n\t\tif (typeof this.runtime.getRelationships !== \"function\") {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst identityLinks = await this.runtime.getRelationships({\n\t\t\tentityIds: [targetEntityId as UUID],\n\t\t\ttags: [\"identity_link\"],\n\t\t});\n\t\tfor (const relationship of identityLinks) {\n\t\t\tconst metadata =\n\t\t\t\trelationship.metadata && typeof relationship.metadata === \"object\"\n\t\t\t\t\t? (relationship.metadata as Record<string, unknown>)\n\t\t\t\t\t: null;\n\t\t\tif (metadata?.status !== \"confirmed\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst linkedEntityId =\n\t\t\t\trelationship.sourceEntityId === targetEntityId\n\t\t\t\t\t? relationship.targetEntityId\n\t\t\t\t\t: relationship.targetEntityId === targetEntityId\n\t\t\t\t\t\t? relationship.sourceEntityId\n\t\t\t\t\t\t: null;\n\t\t\tif (!linkedEntityId || linkedEntityId === targetEntityId) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst linkedEntity = this.runtime.getEntityById\n\t\t\t\t? await this.runtime.getEntityById(linkedEntityId as UUID)\n\t\t\t\t: null;\n\t\t\tconst linkedMetadataUserId = extractDiscordUserIdFromMetadata(\n\t\t\t\tlinkedEntity?.metadata,\n\t\t\t);\n\t\t\tif (linkedMetadataUserId) {\n\t\t\t\treturn linkedMetadataUserId;\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\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\tconstructor(runtime: IAgentRuntime) {\n\t\tsuper(runtime);\n\n\t\t// Load Discord settings with proper priority (env vars > character settings > defaults)\n\t\tthis.discordSettings = getDiscordSettings(runtime);\n\n\t\tthis.character = runtime.character;\n\n\t\t// Parse CHANNEL_IDS env var to restrict the bot to specific channels\n\t\tconst channelIdsRaw = runtime.getSetting(\"CHANNEL_IDS\") as\n\t\t\t| string\n\t\t\t| undefined;\n\t\tif (\n\t\t\tchannelIdsRaw &&\n\t\t\ttypeof channelIdsRaw === \"string\" &&\n\t\t\tchannelIdsRaw.trim &&\n\t\t\ttypeof channelIdsRaw.trim === \"function\" &&\n\t\t\tchannelIdsRaw.trim()\n\t\t) {\n\t\t\tthis.allowedChannelIds = channelIdsRaw\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((s) => s.trim())\n\t\t\t\t.filter((s) => s.length > 0);\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tallowedChannelIds: this.allowedChannelIds,\n\t\t\t\t},\n\t\t\t\t\"Channel restrictions enabled\",\n\t\t\t);\n\t\t}\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\tconst tokenTrimmed =\n\t\t\ttoken &&\n\t\t\ttypeof token === \"string\" &&\n\t\t\ttoken.trim &&\n\t\t\ttypeof token.trim === \"function\"\n\t\t\t\t? token.trim()\n\t\t\t\t: token;\n\t\tif (!token || tokenTrimmed === \"\" || token === null) {\n\t\t\tthis.runtime.logger.warn(\"Discord API Token not provided\");\n\t\t\tthis.client = null;\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst 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\t\t\tthis.client = client;\n\n\t\t\tthis.runtime = createCompatRuntime(runtime);\n\t\t\tthis.voiceManager = new VoiceManager(this, this.runtime);\n\t\t\tthis.messageManager = new MessageManager(this, this.runtime);\n\n\t\t\tthis.clientReadyPromise = new Promise((resolve, reject) => {\n\t\t\t\t// once logged in\n\t\t\t\tclient.once(Events.ClientReady, async (readyClient) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this.onReady(readyClient);\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t\t\t`Error in onReady: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treject(error);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\t// Handle client errors that might prevent ready event\n\t\t\t\tclient.once(Events.Error, (error) => {\n\t\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t\t`Discord client error: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t\t);\n\t\t\t\t\treject(error);\n\t\t\t\t});\n\t\t\t\t// now start login\n\t\t\t\tclient.login(token).catch((error) => {\n\t\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t\t`Failed to login to Discord: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t\t);\n\t\t\t\t\tif (this.client) {\n\t\t\t\t\t\tthis.client.destroy().catch(() => {});\n\t\t\t\t\t}\n\t\t\t\t\tthis.client = null;\n\t\t\t\t\treject(error);\n\t\t\t\t});\n\t\t\t});\n\n\t\t\t// Attach error handler to prevent unhandled promise rejection\n\t\t\tthis.clientReadyPromise.catch((error) => {\n\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t},\n\t\t\t\t\t\"Discord client ready promise rejected\",\n\t\t\t\t);\n\t\t\t\tthis._loginFailed = true;\n\t\t\t});\n\n\t\t\tthis.setupEventListeners();\n\t\t} catch (error) {\n\t\t\truntime.logger.error(\n\t\t\t\t`Error initializing Discord client: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t\tthis.client = null;\n\t\t}\n\t}\n\n\tpublic isHealthy(): boolean {\n\t\tif (this._loginFailed || !this.client) {\n\t\t\treturn false;\n\t\t}\n\t\treturn this.client.isReady();\n\t}\n\n\tstatic async start(runtime: IAgentRuntime) {\n\t\tconst service = new DiscordService(runtime);\n\t\treturn service;\n\t}\n\n\t/**\n\t * The SendHandlerFunction implementation for Discord.\n\t * @param {IAgentRuntime} runtime - The runtime instance.\n\t * @param {TargetInfo} target - The target information for the message.\n\t * @param {Content} content - The content of the message to send.\n\t * @returns {Promise<void>} A promise that resolves when the message is sent or rejects on error.\n\t * @throws {Error} If the client is not ready, target is invalid, or sending fails.\n\t */\n\tasync handleSendMessage(\n\t\truntime: IAgentRuntime,\n\t\ttarget: TargetInfo,\n\t\tcontent: Content,\n\t): Promise<void> {\n\t\tif (!this.client?.isReady()) {\n\t\t\truntime.logger.error(\"Client not ready\");\n\t\t\tthrow new Error(\"Discord client is not ready.\");\n\t\t}\n\t\tconst client = this.client;\n\n\t\tlet targetChannel: Channel | undefined | null = null;\n\t\tlet resolvedChannelId: string | null = null;\n\n\t\ttry {\n\t\t\tif (target.channelId) {\n\t\t\t\tresolvedChannelId = target.channelId;\n\t\t\t\ttargetChannel = await client.channels.fetch(target.channelId);\n\t\t\t} else if (target.roomId) {\n\t\t\t\tconst room =\n\t\t\t\t\ttypeof runtime.getRoom === \"function\"\n\t\t\t\t\t\t? await runtime.getRoom(target.roomId as UUID)\n\t\t\t\t\t\t: null;\n\t\t\t\tconst roomChannelId =\n\t\t\t\t\troom?.channelId && typeof room.channelId === \"string\"\n\t\t\t\t\t\t? room.channelId\n\t\t\t\t\t\t: null;\n\t\t\t\tif (!roomChannelId) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Could not resolve Discord channel ID for room ${target.roomId}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tresolvedChannelId = roomChannelId;\n\t\t\t\ttargetChannel = await client.channels.fetch(roomChannelId);\n\t\t\t} else if (target.entityId) {\n\t\t\t\tconst discordUserId = await this.resolveDiscordTargetUserId(\n\t\t\t\t\ttarget.entityId as string,\n\t\t\t\t);\n\t\t\t\tif (!discordUserId) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Could not resolve Discord user ID for runtime entity ${target.entityId}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst user = await client.users.fetch(discordUserId);\n\t\t\t\tif (user) {\n\t\t\t\t\ttargetChannel = user.dmChannel ?? (await user.createDM());\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Discord SendHandler requires channelId, roomId, or entityId.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (!targetChannel) {\n\t\t\t\tconst targetStr = JSON.stringify(target, (_key, value) => {\n\t\t\t\t\tif (typeof value === \"bigint\") {\n\t\t\t\t\t\treturn value.toString();\n\t\t\t\t\t}\n\t\t\t\t\treturn value;\n\t\t\t\t});\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Could not find target Discord channel/DM for target: ${targetStr}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst allowedByParentThread =\n\t\t\t\ttypeof targetChannel.isThread === \"function\" &&\n\t\t\t\ttargetChannel.isThread() &&\n\t\t\t\t\"parentId\" in targetChannel &&\n\t\t\t\ttypeof targetChannel.parentId === \"string\" &&\n\t\t\t\ttargetChannel.parentId.length > 0 &&\n\t\t\t\tthis.isChannelAllowed(targetChannel.parentId);\n\t\t\tif (\n\t\t\t\tthis.allowedChannelIds &&\n\t\t\t\t!this.isChannelAllowed(targetChannel.id) &&\n\t\t\t\t!allowedByParentThread\n\t\t\t) {\n\t\t\t\tconst resolvedFromText =\n\t\t\t\t\tresolvedChannelId && resolvedChannelId !== targetChannel.id\n\t\t\t\t\t\t? ` (resolved from ${resolvedChannelId})`\n\t\t\t\t\t\t: \"\";\n\t\t\t\truntime.logger.warn(\n\t\t\t\t\t`Channel ${targetChannel.id}${resolvedFromText} not in allowed list, skipping send`,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (targetChannel.isTextBased() && !targetChannel.isVoiceBased()) {\n\t\t\t\tif (\n\t\t\t\t\t\"send\" in targetChannel &&\n\t\t\t\t\ttypeof targetChannel.send === \"function\"\n\t\t\t\t) {\n\t\t\t\t\tconst files: AttachmentBuilder[] = [];\n\t\t\t\t\tif (content.attachments && content.attachments.length > 0) {\n\t\t\t\t\t\tfor (const media of content.attachments) {\n\t\t\t\t\t\t\tif (media.url) {\n\t\t\t\t\t\t\t\tconst fileName = getAttachmentFileName(media);\n\t\t\t\t\t\t\t\tfiles.push(\n\t\t\t\t\t\t\t\t\tnew AttachmentBuilder(media.url, { name: fileName }),\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\n\t\t\t\t\tconst sentMessages: Message[] = [];\n\t\t\t\t\tconst roomId = createUniqueUuid(runtime, targetChannel.id);\n\t\t\t\t\tconst channelType = await this.getChannelType(\n\t\t\t\t\t\ttargetChannel as Channel,\n\t\t\t\t\t);\n\n\t\t\t\t\tconst textContent = normalizeDiscordMessageText(content.text);\n\t\t\t\t\tif (textContent || files.length > 0) {\n\t\t\t\t\t\tif (textContent) {\n\t\t\t\t\t\t\tconst chunks = splitMessage(textContent, MAX_MESSAGE_LENGTH);\n\t\t\t\t\t\t\tif (chunks.length > 1) {\n\t\t\t\t\t\t\t\tfor (let i = 0; i < chunks.length - 1; i++) {\n\t\t\t\t\t\t\t\t\tconst sent = await targetChannel.send(chunks[i]);\n\t\t\t\t\t\t\t\t\tsentMessages.push(sent);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst sent = await targetChannel.send({\n\t\t\t\t\t\t\t\t\tcontent: chunks[chunks.length - 1],\n\t\t\t\t\t\t\t\t\tfiles: files.length > 0 ? files : undefined,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tsentMessages.push(sent);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconst sent = await targetChannel.send({\n\t\t\t\t\t\t\t\t\tcontent: chunks[0],\n\t\t\t\t\t\t\t\t\tfiles: files.length > 0 ? files : undefined,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tsentMessages.push(sent);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst sent = await targetChannel.send({\n\t\t\t\t\t\t\t\tfiles,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tsentMessages.push(sent);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\truntime.logger.warn(\"No text content or attachments provided\");\n\t\t\t\t\t}\n\n\t\t\t\t\tconst targetChannelGuild =\n\t\t\t\t\t\t\"guild\" in targetChannel ? targetChannel.guild : null;\n\t\t\t\t\tconst serverId = targetChannelGuild?.id\n\t\t\t\t\t\t? targetChannelGuild.id\n\t\t\t\t\t\t: targetChannel.id;\n\t\t\t\t\tconst worldId = createUniqueUuid(runtime, serverId) as UUID;\n\t\t\t\t\tconst worldName = targetChannelGuild?.name\n\t\t\t\t\t\t? targetChannelGuild.name\n\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\tconst clientUser = client.user;\n\t\t\t\t\tawait this.runtime.ensureConnection({\n\t\t\t\t\t\tentityId: runtime.agentId,\n\t\t\t\t\t\troomId,\n\t\t\t\t\t\troomName:\n\t\t\t\t\t\t\t\"name\" in targetChannel && typeof targetChannel.name === \"string\"\n\t\t\t\t\t\t\t\t? targetChannel.name\n\t\t\t\t\t\t\t\t: clientUser?.displayName || clientUser?.username || undefined,\n\t\t\t\t\t\tuserName: clientUser?.username ? clientUser.username : undefined,\n\t\t\t\t\t\tname: clientUser?.displayName || clientUser?.username || undefined,\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\tchannelId: targetChannel.id,\n\t\t\t\t\t\tmessageServerId: stringToUuid(serverId),\n\t\t\t\t\t\ttype: channelType,\n\t\t\t\t\t\tworldId,\n\t\t\t\t\t\tworldName,\n\t\t\t\t\t});\n\n\t\t\t\t\tfor (const sentMsg of sentMessages) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst hasAttachments = sentMsg.attachments.size > 0;\n\n\t\t\t\t\t\t\tconst memory: Memory = {\n\t\t\t\t\t\t\t\tid: createUniqueUuid(runtime, sentMsg.id),\n\t\t\t\t\t\t\t\tentityId: runtime.agentId,\n\t\t\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t\t\t\troomId,\n\t\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\ttext: sentMsg.content || textContent || \" \",\n\t\t\t\t\t\t\t\t\turl: sentMsg.url,\n\t\t\t\t\t\t\t\t\tchannelType,\n\t\t\t\t\t\t\t\t\t...(hasAttachments && content.attachments\n\t\t\t\t\t\t\t\t\t\t? { attachments: content.attachments }\n\t\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t\t\t...(content.action ? { action: content.action } : {}),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\t\ttype: MemoryType.MESSAGE,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tcreatedAt: sentMsg.createdTimestamp || Date.now(),\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tawait runtime.createMemory(memory, \"messages\");\n\t\t\t\t\t\t\truntime.logger.debug(\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t\t\t\t\tmessageId: sentMsg.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"Saved sent message to memory\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\truntime.logger.warn(\n\t\t\t\t\t\t\t\t`Failed to save sent message ${sentMsg.id} to memory: ${error instanceof Error ? error.message : String(error)}`,\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} else {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Target channel ${targetChannel.id} does not have a send method.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Target channel ${targetChannel.id} is not a valid text-based channel for sending messages.`,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\truntime.logger.error(\n\t\t\t\t`Error sending message to ${JSON.stringify(target)}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Set up event listeners for the client.\n\t * Delegates to the extracted setupDiscordEventListeners() function.\n\t * @private\n\t */\n\tprivate setupEventListeners() {\n\t\tif (!this.client) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { messageDebouncer, channelDebouncer } = setupDiscordEventListeners(\n\t\t\tthis as any,\n\t\t);\n\n\t\tthis.messageDebouncer = messageDebouncer;\n\t\tthis.channelDebouncer = channelDebouncer;\n\t}\n\n\t/**\n\t * Handles tasks to be performed once the Discord client is fully ready. Delegates to extracted module.\n\t * @private\n\t */\n\tprivate async onReady(readyClient: any) {\n\t\treturn onReadyExtracted(this as any, readyClient);\n\t}\n\n\t/**\n\t * Registers send handlers for the Discord service instance.\n\t * @static\n\t */\n\tstatic registerSendHandlers(\n\t\truntime: IAgentRuntime,\n\t\tserviceInstance: DiscordService,\n\t) {\n\t\tif (serviceInstance) {\n\t\t\truntime.registerSendHandler(\n\t\t\t\t\"discord\",\n\t\t\t\tserviceInstance.handleSendMessage.bind(serviceInstance),\n\t\t\t);\n\t\t\truntime.logger.info(\"Registered send handler\");\n\t\t}\n\t}\n\n\t/**\n\t * Fetches all members who have access to a specific text channel.\n\t */\n\tpublic async getTextChannelMembers(\n\t\tchannelId: string,\n\t\tuseCache: boolean = true,\n\t): Promise<Array<{ id: string; username: string; displayName: string }>> {\n\t\tthis.runtime.logger.debug(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\tchannelId,\n\t\t\t\tuseCache,\n\t\t\t},\n\t\t\t\"Fetching members for text channel\",\n\t\t);\n\n\t\ttry {\n\t\t\tconst channel = this.client\n\t\t\t\t? ((await this.client.channels.fetch(channelId)) as TextChannel)\n\t\t\t\t: null;\n\n\t\t\tif (!channel) {\n\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t{ src: \"plugin:discord\", agentId: this.runtime.agentId, channelId },\n\t\t\t\t\t\"Channel not found\",\n\t\t\t\t);\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tif (channel.type !== DiscordChannelType.GuildText) {\n\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t{ src: \"plugin:discord\", agentId: this.runtime.agentId, channelId },\n\t\t\t\t\t\"Channel is not a text channel\",\n\t\t\t\t);\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst guild = channel.guild;\n\t\t\tif (!guild) {\n\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t{ src: \"plugin:discord\", agentId: this.runtime.agentId, channelId },\n\t\t\t\t\t\"Channel is not in a guild\",\n\t\t\t\t);\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst useCacheOnly = useCache && guild.memberCount > 1000;\n\t\t\tlet members: Collection<string, GuildMember>;\n\n\t\t\tif (useCacheOnly) {\n\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tguildId: guild.id,\n\t\t\t\t\t\tmemberCount: guild.memberCount.toLocaleString(),\n\t\t\t\t\t},\n\t\t\t\t\t\"Using cached members for large guild\",\n\t\t\t\t);\n\t\t\t\tmembers = guild.members.cache;\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tif (useCache && guild.members.cache.size > 0) {\n\t\t\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\t\tcacheSize: guild.members.cache.size,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"Using cached members\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tmembers = guild.members.cache;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\t\tguildId: guild.id,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"Fetching members for guild\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tmembers = await guild.members.fetch();\n\t\t\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\t\tmemberCount: members.size.toLocaleString(),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"Fetched members\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Error fetching members\",\n\t\t\t\t\t);\n\t\t\t\t\tmembers = guild.members.cache;\n\t\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\tcacheSize: members.size,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Fallback to cache\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tchannelId: channel.id,\n\t\t\t\t},\n\t\t\t\t\"Filtering members for channel access\",\n\t\t\t);\n\t\t\tconst memberArray: GuildMember[] = Array.from(members.values());\n\t\t\tconst channelMembers = memberArray\n\t\t\t\t.filter((member: GuildMember) => {\n\t\t\t\t\tconst clientUser = this.client?.user;\n\t\t\t\t\tif (member.user.bot && clientUser && member.id !== clientUser.id) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\tchannel\n\t\t\t\t\t\t\t.permissionsFor(member)\n\t\t\t\t\t\t\t?.has(PermissionsBitField.Flags.ViewChannel) || false\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.map((member: GuildMember) => ({\n\t\t\t\t\tid: member.id,\n\t\t\t\t\tusername: member.user.username,\n\t\t\t\t\tdisplayName: member.displayName || member.user.username,\n\t\t\t\t}));\n\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tchannelId: channel.id,\n\t\t\t\t\tmemberCount: channelMembers.length.toLocaleString(),\n\t\t\t\t},\n\t\t\t\t\"Found members with channel access\",\n\t\t\t);\n\t\t\treturn channelMembers;\n\t\t} catch (error) {\n\t\t\tthis.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error fetching channel members\",\n\t\t\t);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Fetches the topic/description of a Discord text channel.\n\t */\n\tpublic async getChannelTopic(channelId: string): Promise<string | null> {\n\t\ttry {\n\t\t\tconst channel = this.client\n\t\t\t\t? await this.client.channels.fetch(channelId)\n\t\t\t\t: null;\n\t\t\tif (channel && \"topic\" in channel) {\n\t\t\t\treturn (channel as TextChannel).topic;\n\t\t\t}\n\t\t\treturn null;\n\t\t} catch (error) {\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tchannelId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Failed to fetch channel topic\",\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Checks if a channel ID is allowed based on both env config and dynamic additions.\n\t */\n\tpublic isChannelAllowed(channelId: string): boolean {\n\t\tif (!this.allowedChannelIds) {\n\t\t\treturn true;\n\t\t}\n\t\treturn (\n\t\t\tthis.allowedChannelIds.includes(channelId) ||\n\t\t\tthis.dynamicChannelIds.has(channelId)\n\t\t);\n\t}\n\n\t/**\n\t * Adds a channel to the dynamic allowed list.\n\t */\n\tpublic addAllowedChannel(channelId: string): boolean {\n\t\tif (!this.client?.channels.cache.has(channelId)) {\n\t\t\treturn false;\n\t\t}\n\t\tthis.dynamicChannelIds.add(channelId);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Removes a channel from the dynamic allowed list.\n\t */\n\tpublic removeAllowedChannel(channelId: string): boolean {\n\t\tif (this.allowedChannelIds?.includes(channelId)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn this.dynamicChannelIds.delete(channelId);\n\t}\n\n\t/**\n\t * Gets the list of all allowed channels (env + dynamic).\n\t */\n\tpublic getAllowedChannels(): string[] {\n\t\tconst envChannels = this.allowedChannelIds || [];\n\t\tconst dynamicChannels = Array.from(this.dynamicChannelIds);\n\t\treturn [...new Set([...envChannels, ...dynamicChannels])];\n\t}\n\n\t/**\n\t * Fetches and persists message history from a Discord channel. Delegates to extracted module.\n\t */\n\tpublic async fetchChannelHistory(\n\t\tchannelId: string,\n\t\toptions: ChannelHistoryOptions = {},\n\t): Promise<ChannelHistoryResult> {\n\t\treturn fetchChannelHistoryExtracted(this as any, channelId, options);\n\t}\n\n\t/**\n\t * Builds a Memory object from a Discord Message. Delegates to extracted module.\n\t */\n\tpublic async buildMemoryFromMessage(\n\t\tmessage: Message,\n\t\toptions?: {\n\t\t\tprocessedContent?: string;\n\t\t\tprocessedAttachments?: Media[];\n\t\t\textraContent?: Record<string, unknown>;\n\t\t\textraMetadata?: Record<string, unknown>;\n\t\t},\n\t): Promise<Memory | null> {\n\t\treturn buildMemoryFromMessageExtracted(this as any, message, options);\n\t}\n\n\t/**\n\t * Maps a Discord snowflake user id to the runtime entity UUID, substituting\n\t * the canonical Eliza owner entity when the user is a known Discord owner.\n\t */\n\tpublic resolveDiscordEntityId(userId: string): UUID {\n\t\treturn resolveDiscordRuntimeEntityId(\n\t\t\tthis.runtime,\n\t\t\tuserId,\n\t\t\tthis.ownerDiscordUserIds,\n\t\t) as UUID;\n\t}\n\n\t/**\n\t * Handles reaction addition. Delegates to extracted module.\n\t */\n\tpublic async handleReactionAdd(\n\t\treaction: MessageReaction | PartialMessageReaction,\n\t\tuser: User | PartialUser,\n\t): Promise<void> {\n\t\tawait handleReactionAddExtracted(this as any, reaction, user);\n\t}\n\n\t/**\n\t * Handles reaction removal. Delegates to extracted module.\n\t */\n\tpublic async handleReactionRemove(\n\t\treaction: MessageReaction | PartialMessageReaction,\n\t\tuser: User | PartialUser,\n\t): Promise<void> {\n\t\tawait handleReactionRemoveExtracted(this as any, reaction, user);\n\t}\n\n\t/**\n\t * Handles guild creation (bot joined a guild). Delegates to extracted module.\n\t */\n\tpublic async handleGuildCreate(guild: Guild): Promise<void> {\n\t\tawait handleGuildCreateExtracted(this as any, guild);\n\t}\n\n\t/**\n\t * Handles interaction creation (slash commands, modals, etc). Delegates to\n\t * extracted module.\n\t */\n\tpublic async handleInteractionCreate(\n\t\tinteraction: Interaction,\n\t): Promise<void> {\n\t\tawait handleInteractionCreateExtracted(this as any, interaction);\n\t}\n\n\t/**\n\t * Handles a new guild member joining — emits an ENTITY_JOINED event so the\n\t * runtime can create the entity record.\n\t */\n\tpublic async handleGuildMemberAdd(member: GuildMember): Promise<void> {\n\t\tthis.runtime.logger.info(\n\t\t\t`New member joined: ${member.user.username} (${member.id})`,\n\t\t);\n\n\t\tconst guild = member.guild;\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\tconst worldId = createUniqueUuid(this.runtime, guild.id);\n\t\tconst entityId = this.resolveDiscordEntityId(member.id);\n\n\t\tthis.runtime.emitEvent(\n\t\t\t[DiscordEventTypes.ENTITY_JOINED] as string[],\n\t\t\t{\n\t\t\t\truntime: this.runtime,\n\t\t\t\tentityId,\n\t\t\t\tworldId,\n\t\t\t\tsource: \"discord\",\n\t\t\t\tmetadata: {\n\t\t\t\t\ttype: member.user.bot ? \"bot\" : \"user\",\n\t\t\t\t\toriginalId: member.id,\n\t\t\t\t\tusername: tag,\n\t\t\t\t\tdisplayName: member.displayName || member.user.username,\n\t\t\t\t\troles: member.roles.cache.map((r) => r.name),\n\t\t\t\t\tjoinedAt: member.joinedAt?.getTime\n\t\t\t\t\t\t? member.joinedAt.getTime()\n\t\t\t\t\t\t: undefined,\n\t\t\t\t},\n\t\t\t\tmember,\n\t\t\t} as EventPayload,\n\t\t);\n\t}\n\n\t/**\n\t * Stops the Discord service and cleans up resources.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tthis.runtime.logger.info(\"Stopping Discord service\");\n\t\tthis.timeouts.forEach(clearTimeout);\n\t\tthis.timeouts = [];\n\n\t\tthis.messageDebouncer?.destroy();\n\t\tthis.channelDebouncer?.destroy();\n\t\tthis.messageDebouncer = undefined;\n\t\tthis.channelDebouncer = undefined;\n\n\t\tthis.userSelections.clear();\n\n\t\tif (this.voiceManager) {\n\t\t\ttry {\n\t\t\t\tthis.voiceManager.stop();\n\t\t\t} catch (error) {\n\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t`Discord voice cleanup failed: ${\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t\t}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (this.client) {\n\t\t\ttry {\n\t\t\t\tawait this.client.destroy();\n\t\t\t\tthis.runtime.logger.info(\"Discord client destroyed\");\n\t\t\t} catch (error) {\n\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t`Discord client destroy failed: ${\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t\t}`,\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\tthis.client = null;\n\t\t\t}\n\t\t}\n\n\t\tthis.clientReadyPromise = null;\n\t\tthis.messageManager = undefined;\n\t\tthis.voiceManager = undefined;\n\t\tthis.runtime.logger.info(\"Discord service stopped\");\n\t}\n\n\t/**\n\t * Asynchronously retrieves the type of a given 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\n\t\t\tcase DiscordChannelType.GroupDM:\n\t\t\t\treturn ChannelType.DM;\n\n\t\t\tcase DiscordChannelType.GuildText:\n\t\t\tcase DiscordChannelType.GuildNews:\n\t\t\tcase DiscordChannelType.PublicThread:\n\t\t\tcase DiscordChannelType.PrivateThread:\n\t\t\tcase DiscordChannelType.AnnouncementThread:\n\t\t\tcase DiscordChannelType.GuildForum:\n\t\t\t\treturn ChannelType.GROUP;\n\n\t\t\tcase DiscordChannelType.GuildVoice:\n\t\t\tcase DiscordChannelType.GuildStageVoice:\n\t\t\t\treturn ChannelType.VOICE_GROUP;\n\n\t\t\tdefault:\n\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tchannelType: channel.type,\n\t\t\t\t\t},\n\t\t\t\t\t\"Unknown channel type, defaulting to GROUP\",\n\t\t\t\t);\n\t\t\t\treturn ChannelType.GROUP;\n\t\t}\n\t}\n}\n",
|
|
40
|
+
"/**\n * Runtime compatibility layer for old/new core.\n *\n * Automatically adds serverId when messageServerId is provided,\n * making plugin code work with both core versions unchanged.\n *\n * Old core expects: serverId (string)\n * New core expects: messageServerId (UUID)\n *\n * NOTE: UUID function usage for Discord IDs:\n * - `stringToUuid(str)` - CONVERTS any string to a deterministic UUID by hashing.\n * Use this for Discord snowflake IDs (always succeeds, same input = same output).\n * - `asUUID(str)` - VALIDATES that string is already a valid UUID format.\n * Throws if not a valid UUID. Only use when input is already a UUID.\n *\n * REMOVAL: Delete this file and remove createCompatRuntime() call in service.ts\n */\nimport type {\n\tChannelType,\n\tEntity,\n\tIAgentRuntime,\n\tMetadata,\n\tRoom,\n\tUUID,\n\tWorld,\n} from \"@elizaos/core\";\n\n/**\n * Extended types that support messageServerId for cross-core compatibility.\n * These allow TypeScript to accept messageServerId in object literals.\n */\nexport type WorldCompat = Omit<World, \"serverId\"> & {\n\tserverId?: string;\n\tmessageServerId?: UUID;\n};\n\nexport type RoomCompat = Omit<Room, \"serverId\"> & {\n\tserverId?: string;\n\tmessageServerId?: UUID;\n};\n\nexport interface EnsureConnectionParams {\n\tentityId: UUID;\n\troomId: UUID;\n\troomName?: string;\n\tuserName?: string;\n\tname?: string;\n\tworldName?: string;\n\tsource?: string;\n\tchannelId?: string;\n\tserverId?: string;\n\tmessageServerId?: UUID;\n\ttype?: ChannelType | string;\n\tworldId?: UUID;\n\tuserId?: UUID;\n\tmetadata?: Metadata;\n}\n\n/**\n * Extended runtime interface that accepts messageServerId in method parameters.\n */\nexport interface ICompatRuntime\n\textends Omit<\n\t\tIAgentRuntime,\n\t\t| \"ensureWorldExists\"\n\t\t| \"ensureRoomExists\"\n\t\t| \"ensureConnection\"\n\t\t| \"ensureConnections\"\n\t> {\n\tensureWorldExists(world: WorldCompat): Promise<void>;\n\tensureRoomExists(room: RoomCompat): Promise<void>;\n\tensureConnection(params: EnsureConnectionParams): Promise<void>;\n\tensureConnections(\n\t\tentities: Entity[],\n\t\trooms: RoomCompat[],\n\t\tsource: string,\n\t\tworld: WorldCompat,\n\t): Promise<void>;\n}\n\nfunction addServerId<T extends Record<string, unknown>>(obj: T): T {\n\tif (!obj?.messageServerId) {\n\t\treturn obj;\n\t}\n\treturn { ...obj, serverId: obj.serverId ?? obj.messageServerId };\n}\n\nexport function createCompatRuntime(runtime: IAgentRuntime): ICompatRuntime {\n\treturn new Proxy(runtime, {\n\t\tget(target, prop, receiver) {\n\t\t\tconst value = Reflect.get(target, prop, receiver);\n\t\t\tif (typeof value !== \"function\") {\n\t\t\t\treturn value;\n\t\t\t}\n\n\t\t\tif (prop === \"ensureWorldExists\") {\n\t\t\t\treturn (world: unknown) =>\n\t\t\t\t\tvalue.call(target, addServerId(world as Record<string, unknown>));\n\t\t\t}\n\t\t\tif (prop === \"ensureRoomExists\") {\n\t\t\t\treturn (room: unknown) =>\n\t\t\t\t\tvalue.call(target, addServerId(room as Record<string, unknown>));\n\t\t\t}\n\t\t\tif (prop === \"ensureConnection\") {\n\t\t\t\treturn (params: unknown) =>\n\t\t\t\t\tvalue.call(target, addServerId(params as Record<string, unknown>));\n\t\t\t}\n\t\t\tif (prop === \"ensureConnections\") {\n\t\t\t\treturn (\n\t\t\t\t\tentities: unknown[],\n\t\t\t\t\trooms: unknown[],\n\t\t\t\t\tsource: string,\n\t\t\t\t\tworld: unknown,\n\t\t\t\t) =>\n\t\t\t\t\tvalue.call(\n\t\t\t\t\t\ttarget,\n\t\t\t\t\t\tentities,\n\t\t\t\t\t\trooms.map((r) => addServerId(r as Record<string, unknown>)),\n\t\t\t\t\t\tsource,\n\t\t\t\t\t\taddServerId(world as Record<string, unknown>),\n\t\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn value;\n\t\t},\n\t});\n}\n",
|
|
41
|
+
"/**\n * Discord slash command registration — extracted from service.ts\n *\n * Contains transformCommandToDiscordApi(), isGuildOnlyCommand(),\n * handleGuildCreate(), and the core registerSlashCommands() logic.\n */\nimport {\n\tcreateUniqueUuid,\n\ttype EventPayload,\n\tEventType,\n\ttype World,\n} from \"@elizaos/core\";\nimport type {\n\tApplicationCommandDataResolvable,\n\tChatInputApplicationCommandData,\n\tGuild,\n} from \"discord.js\";\nimport type { InteractionServiceInternals } from \"./discord-interactions\";\nimport {\n\tbuildStandardizedRooms,\n\tbuildStandardizedUsers,\n} from \"./discord-interactions\";\nimport { buildDiscordWorldMetadata } from \"./identity\";\nimport type { DiscordSlashCommand } from \"./types\";\nimport { DiscordEventTypes } from \"./types\";\n\n/**\n * Transforms an ElizaOS slash command to Discord API format.\n */\nexport function transformCommandToDiscordApi(\n\tcmd: DiscordSlashCommand,\n): ApplicationCommandDataResolvable {\n\tconst discordCmd: ChatInputApplicationCommandData & {\n\t\tcontexts?: number[];\n\t\tdefault_member_permissions?: string;\n\t} = {\n\t\tname: cmd.name,\n\t\tdescription: cmd.description,\n\t\toptions: cmd.options,\n\t};\n\n\tif (cmd.contexts) {\n\t\tdiscordCmd.contexts = cmd.contexts;\n\t} else if (cmd.guildOnly) {\n\t\tdiscordCmd.contexts = [0]; // 0 = Guild only (no DMs)\n\t}\n\n\tif (cmd.requiredPermissions !== undefined) {\n\t\tdiscordCmd.default_member_permissions =\n\t\t\ttypeof cmd.requiredPermissions === \"bigint\"\n\t\t\t\t? cmd.requiredPermissions.toString()\n\t\t\t\t: cmd.requiredPermissions;\n\t}\n\n\treturn discordCmd;\n}\n\n/**\n * Checks if a command is guild-only.\n */\nexport function isGuildOnlyCommand(cmd: DiscordSlashCommand): boolean {\n\tif (cmd.contexts) {\n\t\treturn cmd.contexts.length === 1 && cmd.contexts[0] === 0;\n\t}\n\treturn !!cmd.guildOnly;\n}\n\n/**\n * Handles the event when the bot joins a guild.\n */\nexport async function handleGuildCreate(\n\tservice: InteractionServiceInternals,\n\tguild: Guild,\n): Promise<void> {\n\tservice.runtime.logger.info(`Joined guild: ${guild.name} (${guild.id})`);\n\tconst fullGuild = await guild.fetch();\n\n\t// Register commands to the newly joined guild\n\tconst clientApplication = service.client?.application;\n\tif (service.slashCommands.length > 0 && clientApplication) {\n\t\ttry {\n\t\t\tconst generalCommands = service.slashCommands.filter(\n\t\t\t\t(cmd) => cmd.guildIds.length === 0,\n\t\t\t);\n\n\t\t\tconst targetedCommandsForThisGuild = service.slashCommands.filter((cmd) =>\n\t\t\t\tcmd.guildIds?.includes(fullGuild.id),\n\t\t\t);\n\n\t\t\tconst commandMap = new Map<string, DiscordSlashCommand>();\n\t\t\tfor (const cmd of [...generalCommands, ...targetedCommandsForThisGuild]) {\n\t\t\t\tif (cmd.name) {\n\t\t\t\t\tcommandMap.set(cmd.name, cmd);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst commandsToRegister = Array.from(commandMap.values());\n\n\t\t\tif (commandsToRegister.length > 0) {\n\t\t\t\tconst discordCommands = commandsToRegister.map((cmd) =>\n\t\t\t\t\ttransformCommandToDiscordApi(cmd),\n\t\t\t\t);\n\n\t\t\t\tawait service.client.application.commands.set(\n\t\t\t\t\tdiscordCommands,\n\t\t\t\t\tfullGuild.id,\n\t\t\t\t);\n\t\t\t\tservice.runtime.logger.info(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\tguildId: fullGuild.id,\n\t\t\t\t\t\tguildName: fullGuild.name,\n\t\t\t\t\t\tgeneralCount: generalCommands.length,\n\t\t\t\t\t\ttargetedCount: targetedCommandsForThisGuild.length,\n\t\t\t\t\t\ttotalCount: discordCommands.length,\n\t\t\t\t\t},\n\t\t\t\t\t\"Commands registered to newly joined guild\",\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tservice.runtime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\tguildId: fullGuild.id,\n\t\t\t\t\tguildName: fullGuild.name,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Failed to register commands to newly joined guild\",\n\t\t\t);\n\t\t}\n\t}\n\n\t// Create standardized world data structure\n\tconst worldId = createUniqueUuid(service.runtime, fullGuild.id);\n\tconst standardizedData = {\n\t\truntime: service.runtime,\n\t\trooms: await buildStandardizedRooms(service, fullGuild, worldId),\n\t\tentities: await buildStandardizedUsers(service, fullGuild),\n\t\tworld: {\n\t\t\tid: worldId,\n\t\t\tname: fullGuild.name,\n\t\t\tagentId: service.runtime.agentId,\n\t\t\tserverId: fullGuild.id,\n\t\t\tmetadata: {\n\t\t\t\t...buildDiscordWorldMetadata(service.runtime, fullGuild.ownerId),\n\t\t\t},\n\t\t} as World,\n\t\tsource: \"discord\",\n\t};\n\n\tservice.runtime.emitEvent([DiscordEventTypes.WORLD_JOINED], {\n\t\truntime: service.runtime,\n\t\tsource: \"discord\",\n\t\tworld: standardizedData.world,\n\t\trooms: standardizedData.rooms,\n\t\tentities: standardizedData.entities,\n\t\tserver: fullGuild,\n\t} as EventPayload);\n\n\tservice.runtime.emitEvent([EventType.WORLD_JOINED], standardizedData);\n}\n",
|
|
42
|
+
"/**\n * Discord interaction handling — extracted from service.ts\n *\n * Contains handleInteractionCreate(), buildStandardizedRooms(),\n * buildStandardizedUsers(), and onReady().\n */\nimport {\n\tChannelType,\n\tcreateUniqueUuid,\n\ttype Entity,\n\ttype EventPayload,\n\tEventType,\n\ttype Room,\n\tstringToUuid,\n\ttype UUID,\n\ttype World,\n} from \"@elizaos/core\";\nimport {\n\ttype Channel,\n\tChannelType as DiscordChannelType,\n\ttype Guild,\n\ttype GuildMember,\n\ttype Interaction,\n\tPermissionsBitField,\n\ttype TextChannel,\n} from \"discord.js\";\nimport type { ICompatRuntime } from \"./compat\";\nimport {\n\tbuildDiscordEntityMetadata,\n\tbuildDiscordWorldMetadata,\n} from \"./identity\";\nimport { generateInviteUrl } from \"./permissions\";\nimport { syncDiscordClientProfile } from \"./profileSync\";\nimport type { DiscordService } from \"./service\";\nimport { registerSlashCommands as registerBuiltinSlashCommands } from \"./slash-commands\";\nimport {\n\tDiscordEventTypes,\n\ttype DiscordRegisterCommandsPayload,\n\ttype DiscordSettings,\n\ttype DiscordSlashCommand,\n\ttype DiscordSlashCommandPayload,\n} from \"./types\";\n\n/**\n * Subset of DiscordService fields needed by interaction handling.\n */\nexport interface InteractionServiceInternals {\n\tclient: NonNullable<DiscordService[\"client\"]>;\n\truntime: ICompatRuntime;\n\tcharacter: DiscordService[\"character\"];\n\tslashCommands: DiscordSlashCommand[];\n\tuserSelections: Map<string, Record<string, unknown>>;\n\ttimeouts: ReturnType<typeof setTimeout>[];\n\tdiscordSettings: DiscordSettings;\n\tclientReadyPromise: Promise<void> | null;\n\n\tresolveDiscordEntityId(userId: string): UUID;\n\tgetChannelType(channel: Channel): Promise<ChannelType>;\n\tregisterSlashCommands(commands: DiscordSlashCommand[]): Promise<void>;\n\trefreshOwnerDiscordUserIds(client: unknown): Promise<void>;\n}\n\n/**\n * Handles interactions created by the user (commands, message components).\n */\nexport async function handleInteractionCreate(\n\tservice: InteractionServiceInternals,\n\tinteraction: Interaction,\n): Promise<void> {\n\tconst entityId = service.resolveDiscordEntityId(interaction.user.id);\n\tconst userName = interaction.user.bot\n\t\t? `${interaction.user.username}#${interaction.user.discriminator}`\n\t\t: interaction.user.username;\n\tconst name = interaction.user.displayName;\n\tconst interactionChannelId = interaction.channel?.id;\n\tconst roomId = createUniqueUuid(\n\t\tservice.runtime,\n\t\tinteractionChannelId || userName,\n\t);\n\n\tlet type: ChannelType;\n\tlet serverId: string | undefined;\n\n\tif (interaction.guild) {\n\t\tconst guild = await interaction.guild.fetch();\n\t\ttype = await service.getChannelType(interaction.channel as Channel);\n\t\tif (type === null) {\n\t\t\tservice.runtime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\tchannelId: interactionChannelId,\n\t\t\t\t},\n\t\t\t\t\"Null channel type for interaction\",\n\t\t\t);\n\t\t}\n\t\tserverId = guild.id;\n\t} else {\n\t\ttype = ChannelType.DM;\n\t\tserverId = interactionChannelId;\n\t}\n\n\tawait service.runtime.ensureConnection({\n\t\tentityId,\n\t\troomId,\n\t\troomName:\n\t\t\tinteraction.guild &&\n\t\t\tinteraction.channel &&\n\t\t\t\"name\" in interaction.channel &&\n\t\t\ttypeof interaction.channel.name === \"string\"\n\t\t\t\t? interaction.channel.name\n\t\t\t\t: name,\n\t\tuserName,\n\t\tname,\n\t\tsource: \"discord\",\n\t\tchannelId: interactionChannelId,\n\t\tmessageServerId: serverId ? stringToUuid(serverId) : undefined,\n\t\ttype,\n\t\tworldId: createUniqueUuid(service.runtime, serverId ?? roomId) as UUID,\n\t\tworldName: interaction.guild?.name || undefined,\n\t\tuserId: interaction.user.id as unknown as UUID,\n\t\tmetadata: buildDiscordWorldMetadata(\n\t\t\tservice.runtime,\n\t\t\tinteraction.guild?.ownerId,\n\t\t),\n\t});\n\n\tif (interaction.isCommand()) {\n\t\tservice.runtime.logger.debug(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\tcommandName: interaction.commandName,\n\t\t\t\ttype: interaction.commandType,\n\t\t\t\tchannelId: interaction.channelId,\n\t\t\t\tinGuild: interaction.inGuild(),\n\t\t\t},\n\t\t\t\"[DiscordService] Slash command received\",\n\t\t);\n\n\t\ttry {\n\t\t\tif (!service.client) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst slashPayload: DiscordSlashCommandPayload = {\n\t\t\t\truntime: service.runtime,\n\t\t\t\tsource: \"discord\",\n\t\t\t\tinteraction,\n\t\t\t\tclient: service.client,\n\t\t\t\tcommands: service.slashCommands,\n\t\t\t};\n\t\t\tservice.runtime.emitEvent(DiscordEventTypes.SLASH_COMMAND, slashPayload);\n\t\t\tservice.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\tcommandName: interaction.commandName,\n\t\t\t\t},\n\t\t\t\t\"[DiscordService] Slash command emitted to runtime\",\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tservice.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\tcommandName: interaction.commandName,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"[DiscordService] Failed to emit slash command\",\n\t\t\t);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tif (interaction.isModalSubmit()) {\n\t\tif (!service.client) {\n\t\t\treturn;\n\t\t}\n\t\tconst modalPayload: DiscordSlashCommandPayload = {\n\t\t\truntime: service.runtime,\n\t\t\tsource: \"discord\",\n\t\t\tinteraction,\n\t\t\tclient: service.client,\n\t\t\tcommands: service.slashCommands,\n\t\t};\n\t\tservice.runtime.emitEvent(DiscordEventTypes.MODAL_SUBMIT, modalPayload);\n\t}\n\n\t// Handle message component interactions (buttons, dropdowns, etc.)\n\tif (interaction.isMessageComponent()) {\n\t\tservice.runtime.logger.debug(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\tcustomId: interaction.customId,\n\t\t\t},\n\t\t\t\"Received component interaction\",\n\t\t);\n\t\tconst interactionUser = interaction.user;\n\t\tconst userId = interactionUser?.id;\n\t\tconst interactionMessage = interaction.message;\n\t\tconst messageId = interactionMessage?.id;\n\n\t\tif (!service.userSelections.has(userId)) {\n\t\t\tservice.userSelections.set(userId, {});\n\t\t}\n\t\tconst userSelections = service.userSelections.get(userId);\n\t\tif (!userSelections) {\n\t\t\tservice.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\tentityId: userId,\n\t\t\t\t},\n\t\t\t\t\"User selections map unexpectedly missing\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tif (interaction.isStringSelectMenu()) {\n\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\tentityId: userId,\n\t\t\t\t\t\tcustomId: interaction.customId,\n\t\t\t\t\t\tvalues: interaction.values,\n\t\t\t\t\t},\n\t\t\t\t\t\"Values selected\",\n\t\t\t\t);\n\n\t\t\t\tconst existingSelections =\n\t\t\t\t\t(userSelections[messageId] as Record<string, unknown>) || {};\n\t\t\t\tuserSelections[messageId] = {\n\t\t\t\t\t...existingSelections,\n\t\t\t\t\t[interaction.customId]: interaction.values,\n\t\t\t\t};\n\n\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\tmessageId,\n\t\t\t\t\t\tselections: userSelections[messageId],\n\t\t\t\t\t},\n\t\t\t\t\t\"Current selections for message\",\n\t\t\t\t);\n\n\t\t\t\tawait interaction.deferUpdate();\n\t\t\t}\n\n\t\t\tif (interaction.isButton()) {\n\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\tentityId: userId,\n\t\t\t\t\t\tcustomId: interaction.customId,\n\t\t\t\t\t},\n\t\t\t\t\t\"Button pressed\",\n\t\t\t\t);\n\t\t\t\tconst formSelections = userSelections[messageId] || {};\n\n\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\tformSelections,\n\t\t\t\t\t},\n\t\t\t\t\t\"Form data being submitted\",\n\t\t\t\t);\n\n\t\t\t\tconst fallbackTimeout = setTimeout(async () => {\n\t\t\t\t\tconst index = service.timeouts.indexOf(fallbackTimeout);\n\t\t\t\t\tif (index > -1) {\n\t\t\t\t\t\tservice.timeouts.splice(index, 1);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!interaction.replied && !interaction.deferred) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait interaction.deferUpdate();\n\t\t\t\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\t\t\t\tcustomId: interaction.customId,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"Acknowledged button interaction via fallback\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} catch (ackError) {\n\t\t\t\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\t\t\tackError instanceof Error\n\t\t\t\t\t\t\t\t\t\t\t? ackError.message\n\t\t\t\t\t\t\t\t\t\t\t: String(ackError),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"Fallback acknowledgement skipped\",\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}, 2500);\n\t\t\t\tservice.timeouts.push(fallbackTimeout);\n\n\t\t\t\tconst earlyCheckTimeout = setTimeout(() => {\n\t\t\t\t\tif (interaction.replied || interaction.deferred) {\n\t\t\t\t\t\tclearTimeout(fallbackTimeout);\n\t\t\t\t\t\tconst index = service.timeouts.indexOf(fallbackTimeout);\n\t\t\t\t\t\tif (index > -1) {\n\t\t\t\t\t\t\tservice.timeouts.splice(index, 1);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tconst earlyIndex = service.timeouts.indexOf(earlyCheckTimeout);\n\t\t\t\t\tif (earlyIndex > -1) {\n\t\t\t\t\t\tservice.timeouts.splice(earlyIndex, 1);\n\t\t\t\t\t}\n\t\t\t\t}, 2000);\n\t\t\t\tservice.timeouts.push(earlyCheckTimeout);\n\n\t\t\t\tconst interactionPayload: EventPayload & {\n\t\t\t\t\tinteraction: {\n\t\t\t\t\t\tcustomId: string;\n\t\t\t\t\t\tcomponentType: number;\n\t\t\t\t\t\ttype: number;\n\t\t\t\t\t\tuser: string;\n\t\t\t\t\t\tmessageId: string;\n\t\t\t\t\t\tselections: Record<string, unknown>;\n\t\t\t\t\t};\n\t\t\t\t\tdiscordInteraction: Interaction;\n\t\t\t\t} = {\n\t\t\t\t\truntime: service.runtime,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tinteraction: {\n\t\t\t\t\t\tcustomId: interaction.customId,\n\t\t\t\t\t\tcomponentType: interaction.componentType,\n\t\t\t\t\t\ttype: interaction.type,\n\t\t\t\t\t\tuser: userId,\n\t\t\t\t\t\tmessageId,\n\t\t\t\t\t\tselections: formSelections as Record<string, unknown>,\n\t\t\t\t\t},\n\t\t\t\t\tdiscordInteraction: interaction,\n\t\t\t\t};\n\t\t\t\tservice.runtime.emitEvent([\"DISCORD_INTERACTION\"], interactionPayload);\n\n\t\t\t\tdelete userSelections[messageId];\n\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\tmessageId,\n\t\t\t\t\t},\n\t\t\t\t\t\"Cleared selections for message\",\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tservice.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error handling component interaction\",\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tawait interaction.followUp({\n\t\t\t\t\tcontent: \"There was an error processing your interaction.\",\n\t\t\t\t\tephemeral: true,\n\t\t\t\t});\n\t\t\t} catch (followUpError) {\n\t\t\t\tservice.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\terror:\n\t\t\t\t\t\t\tfollowUpError instanceof Error\n\t\t\t\t\t\t\t\t? followUpError.message\n\t\t\t\t\t\t\t\t: String(followUpError),\n\t\t\t\t\t},\n\t\t\t\t\t\"Error sending follow-up message\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Builds a standardized list of rooms from Discord guild channels.\n */\nexport async function buildStandardizedRooms(\n\tservice: InteractionServiceInternals,\n\tguild: Guild,\n\t_worldId: UUID,\n): Promise<Room[]> {\n\tconst rooms: Room[] = [];\n\n\tfor (const [channelId, channel] of guild.channels.cache) {\n\t\tif (\n\t\t\tchannel.type === DiscordChannelType.GuildText ||\n\t\t\tchannel.type === DiscordChannelType.GuildVoice\n\t\t) {\n\t\t\tconst roomId = createUniqueUuid(service.runtime, channelId);\n\t\t\tlet channelType: ChannelType;\n\n\t\t\tswitch (channel.type) {\n\t\t\t\tcase DiscordChannelType.GuildText:\n\t\t\t\t\tchannelType = ChannelType.GROUP;\n\t\t\t\t\tbreak;\n\t\t\t\tcase DiscordChannelType.GuildVoice:\n\t\t\t\t\tchannelType = ChannelType.VOICE_GROUP;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tchannelType = ChannelType.GROUP;\n\t\t\t}\n\n\t\t\tlet participants: UUID[] = [];\n\n\t\t\tif (\n\t\t\t\tguild.memberCount < 1000 &&\n\t\t\t\tchannel.type === DiscordChannelType.GuildText\n\t\t\t) {\n\t\t\t\ttry {\n\t\t\t\t\tparticipants = Array.from(guild.members.cache.values())\n\t\t\t\t\t\t.filter((member: GuildMember) =>\n\t\t\t\t\t\t\tchannel\n\t\t\t\t\t\t\t\t.permissionsFor(member)\n\t\t\t\t\t\t\t\t?.has(PermissionsBitField.Flags.ViewChannel),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.map((member: GuildMember) =>\n\t\t\t\t\t\t\tservice.resolveDiscordEntityId(member.id),\n\t\t\t\t\t\t);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tservice.runtime.logger.warn(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\t\tchannelId: channel.id,\n\t\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Failed to get participants for channel\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\trooms.push({\n\t\t\t\tid: roomId,\n\t\t\t\tname: channel.name,\n\t\t\t\ttype: channelType,\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tsource: \"discord\",\n\t\t\t\t/**\n\t\t\t\t * Channel topic exposed via metadata for plugin-content-seeder\n\t\t\t\t */\n\t\t\t\tmetadata: {\n\t\t\t\t\ttopic:\n\t\t\t\t\t\t\"topic\" in channel ? (channel as TextChannel).topic : undefined,\n\t\t\t\t\tparticipants,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t}\n\n\treturn rooms;\n}\n\n/**\n * Builds a standardized list of users (entities) from Discord guild members.\n */\nexport async function buildStandardizedUsers(\n\tservice: InteractionServiceInternals,\n\tguild: Guild,\n): Promise<Entity[]> {\n\tconst entities: Entity[] = [];\n\tconst clientUser = service.client?.user;\n\tconst botId = clientUser?.id;\n\n\tif (guild.memberCount > 1000) {\n\t\tservice.runtime.logger.debug(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\tguildId: guild.id,\n\t\t\t\tmemberCount: guild.memberCount.toLocaleString(),\n\t\t\t},\n\t\t\t\"Using optimized user sync for large guild\",\n\t\t);\n\n\t\ttry {\n\t\t\tfor (const [, member] of guild.members.cache) {\n\t\t\t\tconst tag = member.user.bot\n\t\t\t\t\t? `${member.user.username}#${member.user.discriminator}`\n\t\t\t\t\t: member.user.username;\n\n\t\t\t\tif (member.id !== botId) {\n\t\t\t\t\tentities.push({\n\t\t\t\t\t\tid: service.resolveDiscordEntityId(member.id),\n\t\t\t\t\t\tnames: Array.from(\n\t\t\t\t\t\t\tnew Set(\n\t\t\t\t\t\t\t\t[\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].filter(Boolean) as string[],\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\tmetadata: buildDiscordEntityMetadata(\n\t\t\t\t\t\t\tmember.id,\n\t\t\t\t\t\t\ttag,\n\t\t\t\t\t\t\tmember.displayName || member.user.username,\n\t\t\t\t\t\t\tmember.user.globalName ?? undefined,\n\t\t\t\t\t\t\tmember.user.displayAvatarURL(),\n\t\t\t\t\t\t),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (entities.length < 100) {\n\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\tguildId: guild.id,\n\t\t\t\t\t},\n\t\t\t\t\t\"Adding online members\",\n\t\t\t\t);\n\t\t\t\tconst onlineMembers = await guild.members.fetch({ limit: 100 });\n\n\t\t\t\tfor (const [, member] of onlineMembers) {\n\t\t\t\t\tif (member.id !== botId) {\n\t\t\t\t\t\tconst entityId = service.resolveDiscordEntityId(member.id);\n\t\t\t\t\t\tif (!entities.some((u) => u.id === entityId)) {\n\t\t\t\t\t\t\tconst tag = member.user.bot\n\t\t\t\t\t\t\t\t? `${member.user.username}#${member.user.discriminator}`\n\t\t\t\t\t\t\t\t: member.user.username;\n\n\t\t\t\t\t\t\tentities.push({\n\t\t\t\t\t\t\t\tid: entityId,\n\t\t\t\t\t\t\t\tnames: Array.from(\n\t\t\t\t\t\t\t\t\tnew Set(\n\t\t\t\t\t\t\t\t\t\t[\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].filter(Boolean) as string[],\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\tagentId: service.runtime.agentId,\n\t\t\t\t\t\t\t\tmetadata: buildDiscordEntityMetadata(\n\t\t\t\t\t\t\t\t\tmember.id,\n\t\t\t\t\t\t\t\t\ttag,\n\t\t\t\t\t\t\t\t\tmember.displayName || member.user.username,\n\t\t\t\t\t\t\t\t\tmember.user.globalName ?? undefined,\n\t\t\t\t\t\t\t\t\tmember.user.displayAvatarURL(),\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}\n\t\t} catch (error) {\n\t\t\tservice.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\tguildId: guild.id,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error fetching members\",\n\t\t\t);\n\t\t}\n\t} else {\n\t\ttry {\n\t\t\tlet members = guild.members.cache;\n\t\t\tif (members.size === 0) {\n\t\t\t\tmembers = await guild.members.fetch();\n\t\t\t}\n\n\t\t\tfor (const [, member] of members) {\n\t\t\t\tif (member.id !== botId) {\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\tentities.push({\n\t\t\t\t\t\tid: service.resolveDiscordEntityId(member.id),\n\t\t\t\t\t\tnames: Array.from(\n\t\t\t\t\t\t\tnew Set(\n\t\t\t\t\t\t\t\t[\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].filter(Boolean) as string[],\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\tmetadata: buildDiscordEntityMetadata(\n\t\t\t\t\t\t\tmember.id,\n\t\t\t\t\t\t\ttag,\n\t\t\t\t\t\t\tmember.displayName || member.user.username,\n\t\t\t\t\t\t\tmember.user.globalName ?? undefined,\n\t\t\t\t\t\t\tmember.user.displayAvatarURL(),\n\t\t\t\t\t\t),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tservice.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\tguildId: guild.id,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error fetching members\",\n\t\t\t);\n\t\t}\n\t}\n\n\treturn entities;\n}\n\n/**\n * Handles tasks to be performed once the Discord client is fully ready.\n */\nexport async function onReady(\n\tservice: InteractionServiceInternals,\n\treadyClient: any,\n): Promise<void> {\n\tservice.runtime.logger.success(\"Discord client ready\");\n\tconst discordApiToken = service.runtime.getSetting(\"DISCORD_API_TOKEN\");\n\tif (\n\t\ttypeof discordApiToken === \"string\" &&\n\t\tdiscordApiToken.trim().length > 0 &&\n\t\ttypeof readyClient.rest?.setToken === \"function\"\n\t) {\n\t\treadyClient.rest.setToken(discordApiToken.trim());\n\t}\n\tawait service.refreshOwnerDiscordUserIds(readyClient);\n\n\t// Initialize slash commands array\n\tservice.slashCommands = [];\n\n\t/**\n\t * DISCORD_REGISTER_COMMANDS event handler\n\t */\n\tservice.runtime.registerEvent(\n\t\t\"DISCORD_REGISTER_COMMANDS\",\n\t\tasync (params: DiscordRegisterCommandsPayload) => {\n\t\t\tawait service.registerSlashCommands(params.commands);\n\t\t},\n\t);\n\tawait registerBuiltinSlashCommands(service.runtime);\n\n\tconst auditLogSettingForInvite = service.runtime.getSetting(\n\t\t\"DISCORD_AUDIT_LOG_ENABLED\",\n\t);\n\tconst isAuditLogEnabledForInvite =\n\t\tauditLogSettingForInvite === \"true\" ||\n\t\tauditLogSettingForInvite === true ||\n\t\tauditLogSettingForInvite === \"1\" ||\n\t\tauditLogSettingForInvite === 1;\n\n\tconst readyClientUser = readyClient.user;\n\tif (readyClientUser) {\n\t\ttry {\n\t\t\tawait syncDiscordClientProfile(\n\t\t\t\tservice.runtime,\n\t\t\t\treadyClientUser,\n\t\t\t\tservice.discordSettings,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tservice.runtime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Failed to synchronize Discord bot profile from connector settings\",\n\t\t\t);\n\t\t}\n\t}\n\tconst inviteUrl = readyClientUser?.id\n\t\t? generateInviteUrl(readyClientUser.id, \"MODERATOR_VOICE\")\n\t\t: undefined;\n\n\tif (isAuditLogEnabledForInvite) {\n\t\tservice.runtime.logger.info(\n\t\t\t{ src: \"plugin:discord\", agentId: service.runtime.agentId },\n\t\t\t\"Audit log tracking enabled - ensure bot has ViewAuditLog permission in server settings\",\n\t\t);\n\t}\n\n\tconst agentName =\n\t\tservice.runtime.character.name ||\n\t\treadyClientUser?.username ||\n\t\tservice.runtime.agentId;\n\n\tif (inviteUrl) {\n\t\tservice.runtime.logger.info(\n\t\t\t{ src: \"plugin:discord\", agentId: service.runtime.agentId, inviteUrl },\n\t\t\t\"Bot invite URL generated\",\n\t\t);\n\t\tservice.runtime.logger.info(\n\t\t\t`Use this URL to add the \"${agentName}\" bot to your Discord server: ${inviteUrl}`,\n\t\t);\n\t} else {\n\t\tservice.runtime.logger.warn(\n\t\t\t{ src: \"plugin:discord\", agentId: service.runtime.agentId },\n\t\t\t\"Could not generate invite URL - bot user ID unavailable\",\n\t\t);\n\t}\n\n\tservice.runtime.logger.success(\n\t\t`Discord client logged in successfully as ${readyClientUser?.username || agentName}`,\n\t);\n\n\tconst guilds = service.client ? await service.client.guilds.fetch() : null;\n\tif (!guilds) {\n\t\tservice.runtime.logger.warn(\"Could not fetch guilds\");\n\t\treturn;\n\t}\n\tfor (const [, guild] of guilds) {\n\t\tconst timeoutId = setTimeout(async () => {\n\t\t\ttry {\n\t\t\t\tconst fullGuild = await guild.fetch();\n\t\t\t\tservice.runtime.logger.info(\n\t\t\t\t\t`Discord server connected: ${fullGuild.name} (${fullGuild.id})`,\n\t\t\t\t);\n\n\t\t\t\tconst worldId = createUniqueUuid(service.runtime, fullGuild.id);\n\t\t\t\tconst standardizedData = {\n\t\t\t\t\tname: fullGuild.name,\n\t\t\t\t\truntime: service.runtime,\n\t\t\t\t\trooms: await buildStandardizedRooms(service, fullGuild, worldId),\n\t\t\t\t\tentities: await buildStandardizedUsers(service, 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: service.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\t...buildDiscordWorldMetadata(service.runtime, fullGuild.ownerId),\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\tservice.runtime.emitEvent([DiscordEventTypes.WORLD_CONNECTED], {\n\t\t\t\t\truntime: service.runtime,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tworld: standardizedData.world,\n\t\t\t\t\trooms: standardizedData.rooms,\n\t\t\t\t\tentities: standardizedData.entities,\n\t\t\t\t\tserver: fullGuild,\n\t\t\t\t} as EventPayload);\n\n\t\t\t\tservice.runtime.emitEvent(\n\t\t\t\t\t[EventType.WORLD_CONNECTED],\n\t\t\t\t\tstandardizedData,\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tservice.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t},\n\t\t\t\t\t\"Error during Discord world connection\",\n\t\t\t\t);\n\t\t\t}\n\t\t}, 1000);\n\n\t\tservice.timeouts.push(timeoutId);\n\t}\n\n\t// Validate audit log access\n\tconst auditLogEnabled = service.runtime.getSetting(\n\t\t\"DISCORD_AUDIT_LOG_ENABLED\",\n\t);\n\tif (\n\t\tauditLogEnabled === \"true\" ||\n\t\tauditLogEnabled === true ||\n\t\tauditLogEnabled === \"1\" ||\n\t\tauditLogEnabled === 1\n\t) {\n\t\ttry {\n\t\t\tconst testGuild = guilds.first();\n\t\t\tif (testGuild) {\n\t\t\t\tconst fullGuild = await testGuild.fetch();\n\t\t\t\tawait fullGuild.fetchAuditLogs({ limit: 1 });\n\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t\"Audit log access verified for permission tracking\",\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconst errorMessage = err instanceof Error ? err.message : String(err);\n\t\t\tconst errorCode =\n\t\t\t\ttypeof err === \"object\" &&\n\t\t\t\terr !== null &&\n\t\t\t\t\"code\" in err &&\n\t\t\t\ttypeof err.code !== \"undefined\"\n\t\t\t\t\t? String(err.code)\n\t\t\t\t\t: \"\";\n\t\t\tconst missingAuditLogPermission =\n\t\t\t\terrorCode === \"50013\" || errorMessage.includes(\"Missing Permissions\");\n\t\t\tconst logMethod = missingAuditLogPermission\n\t\t\t\t? service.runtime.logger.info\n\t\t\t\t: service.runtime.logger.warn;\n\t\t\tlogMethod.call(\n\t\t\t\tservice.runtime.logger,\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\terror: errorMessage,\n\t\t\t\t},\n\t\t\t\tmissingAuditLogPermission\n\t\t\t\t\t? \"Audit log access unavailable - permission change alerts will not include executor info\"\n\t\t\t\t\t: \"Cannot access audit logs - permission change alerts will not include executor info\",\n\t\t\t);\n\t\t}\n\t}\n\n\tif (service.client) {\n\t\tservice.client.emit(\"voiceManagerReady\");\n\t}\n}\n",
|
|
43
|
+
"import {\n\tcreateUniqueUuid,\n\ttype IAgentRuntime,\n\ttype Metadata,\n\tRole,\n\tstringToUuid,\n} from \"@elizaos/core\";\n\nconst CANONICAL_OWNER_SETTING_KEYS = [\"ELIZA_ADMIN_ENTITY_ID\"] as const;\nconst DISCORD_SNOWFLAKE_PATTERN = /^\\d{15,20}$/;\n\nfunction getCanonicalOwnerId(runtime: IAgentRuntime): string | undefined {\n\tfor (const key of CANONICAL_OWNER_SETTING_KEYS) {\n\t\tconst value = runtime.getSetting?.(key);\n\t\tif (typeof value !== \"string\") {\n\t\t\tcontinue;\n\t\t}\n\t\tconst trimmed = value.trim();\n\t\tif (trimmed.length > 0) {\n\t\t\treturn trimmed;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n\treturn value && typeof value === \"object\" && !Array.isArray(value)\n\t\t? (value as Record<string, unknown>)\n\t\t: null;\n}\n\nfunction readDiscordSnowflake(value: unknown): string | null {\n\tif (typeof value !== \"string\") {\n\t\treturn null;\n\t}\n\tconst trimmed = value.trim();\n\treturn DISCORD_SNOWFLAKE_PATTERN.test(trimmed) ? trimmed : null;\n}\n\nfunction readUserIdFromOwnerLike(value: unknown): string | null {\n\tconst owner = asRecord(value);\n\tif (!owner) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\treadDiscordSnowflake(owner.id) ??\n\t\treadDiscordSnowflake(asRecord(owner.user)?.id) ??\n\t\treadDiscordSnowflake(owner.ownerId) ??\n\t\treadDiscordSnowflake(owner.ownerUserId)\n\t);\n}\n\nexport function resolveElizaOwnerEntityId(runtime: IAgentRuntime): string {\n\tconst configuredOwnerId = getCanonicalOwnerId(runtime);\n\tif (configuredOwnerId) {\n\t\treturn configuredOwnerId;\n\t}\n\n\tconst agentName = runtime.character?.name?.trim() || runtime.agentId;\n\treturn stringToUuid(`${agentName}-admin-entity`);\n}\n\nexport function resolveDiscordRuntimeEntityId(\n\truntime: IAgentRuntime,\n\tuserId: string,\n\townerDiscordUserIds: Iterable<string> = [],\n): string {\n\tfor (const ownerUserId of ownerDiscordUserIds) {\n\t\tif (ownerUserId === userId) {\n\t\t\treturn resolveElizaOwnerEntityId(runtime);\n\t\t}\n\t}\n\treturn createUniqueUuid(runtime, userId);\n}\n\nexport function extractDiscordOwnerUserIds(application: unknown): string[] {\n\tconst applicationRecord = asRecord(application);\n\tif (!applicationRecord) {\n\t\treturn [];\n\t}\n\n\tconst ownerCandidates = new Set<string>();\n\tconst directOwnerId = readUserIdFromOwnerLike(applicationRecord.owner);\n\tif (directOwnerId) {\n\t\townerCandidates.add(directOwnerId);\n\t}\n\n\tconst team = asRecord(applicationRecord.team);\n\tconst teamOwnerId =\n\t\treadDiscordSnowflake(team?.ownerId) ??\n\t\treadDiscordSnowflake(team?.ownerUserId);\n\tif (teamOwnerId) {\n\t\townerCandidates.add(teamOwnerId);\n\t}\n\n\tconst teamMembers = team?.members;\n\t// Discord.js returns team.members as a Collection (Map-like), not an Array.\n\t// Handle both Array and iterable (Collection/Map) shapes.\n\tconst memberIterable: Iterable<unknown> | null = Array.isArray(teamMembers)\n\t\t? teamMembers\n\t\t: teamMembers &&\n\t\t\t\ttypeof teamMembers === \"object\" &&\n\t\t\t\ttypeof (teamMembers as Iterable<unknown>)[Symbol.iterator] ===\n\t\t\t\t\t\"function\"\n\t\t\t? (teamMembers as Iterable<unknown>)\n\t\t\t: null;\n\tif (memberIterable) {\n\t\tfor (const entry of memberIterable) {\n\t\t\t// Collection/Map yields [key, value] tuples; Array yields values directly.\n\t\t\tconst member = Array.isArray(entry) ? entry[1] : entry;\n\t\t\tconst memberId = readUserIdFromOwnerLike(member);\n\t\t\tif (memberId) {\n\t\t\t\townerCandidates.add(memberId);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn [...ownerCandidates];\n}\n\nexport function parseDiscordOwnerUserIds(value: unknown): string[] {\n\tconst rawValues = (() => {\n\t\tif (Array.isArray(value)) {\n\t\t\treturn value;\n\t\t}\n\t\tif (typeof value !== \"string\" || value.trim().length === 0) {\n\t\t\treturn [];\n\t\t}\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(value) as unknown;\n\t\t\treturn Array.isArray(parsed) ? parsed : [];\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t})();\n\n\treturn rawValues\n\t\t.map((entry) => readDiscordSnowflake(entry))\n\t\t.filter((entry): entry is string => Boolean(entry));\n}\n\nexport function buildDiscordWorldMetadata(\n\truntime: IAgentRuntime,\n\tguildOwnerId: string | undefined,\n): Metadata | undefined {\n\tconst ownerId = resolveElizaOwnerEntityId(runtime);\n\tconst roles: Record<string, Role> = {\n\t\t[ownerId]: Role.OWNER,\n\t};\n\n\t// The Discord guild owner should also be recognized as an owner in their\n\t// guild. Map the guild owner's Discord snowflake to a runtime entity ID\n\t// and grant OWNER so that role resolution picks them up even when the bot-\n\t// application owner extraction didn't include them.\n\tif (guildOwnerId && DISCORD_SNOWFLAKE_PATTERN.test(guildOwnerId)) {\n\t\tconst guildOwnerEntityId = createUniqueUuid(runtime, guildOwnerId);\n\t\tif (guildOwnerEntityId !== ownerId) {\n\t\t\troles[guildOwnerEntityId] = Role.OWNER;\n\t\t}\n\t}\n\n\treturn {\n\t\townership: { ownerId },\n\t\troles,\n\t};\n}\n\nexport function buildDiscordEntityMetadata(\n\tuserId: string,\n\tuserName: string,\n\tname: string,\n\tglobalName?: string,\n\tavatarUrl?: string,\n): Metadata {\n\treturn {\n\t\tdefault: {\n\t\t\tusername: userName,\n\t\t\tname,\n\t\t\t...(typeof avatarUrl === \"string\" && avatarUrl.length > 0\n\t\t\t\t? { avatarUrl }\n\t\t\t\t: {}),\n\t\t},\n\t\tdiscord: {\n\t\t\tid: userId,\n\t\t\tuserId,\n\t\t\tuserName,\n\t\t\tusername: userName,\n\t\t\tname,\n\t\t\t...(typeof globalName === \"string\" && globalName.length > 0\n\t\t\t\t? { globalName }\n\t\t\t\t: {}),\n\t\t\t...(typeof avatarUrl === \"string\" && avatarUrl.length > 0\n\t\t\t\t? { avatarUrl }\n\t\t\t\t: {}),\n\t\t},\n\t\toriginalId: userId,\n\t\tusername: userName,\n\t\tdisplayName: name,\n\t\t...(typeof avatarUrl === \"string\" && avatarUrl.length > 0\n\t\t\t? { avatarUrl }\n\t\t\t: {}),\n\t};\n}\n",
|
|
44
|
+
"import { createHash } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport type { IAgentRuntime } from \"@elizaos/core\";\nimport type { ClientUser } from \"discord.js\";\nimport type { DiscordSettings } from \"./types\";\n\nconst MAX_PROFILE_AVATAR_BYTES = 8 * 1024 * 1024;\nconst PROFILE_SYNC_STATE_FILE = \"discord-profile-sync.v1.json\";\nconst DEFAULT_DISCORD_PROFILE_AVATAR = \"/avatars/eliza.png\";\n\ntype PersistedDiscordProfileSyncState = {\n\tavatarHash?: string;\n\tusername?: string;\n};\n\nfunction resolveUserPath(input: string): string {\n\tconst trimmed = input.trim();\n\tif (!trimmed) {\n\t\treturn trimmed;\n\t}\n\tif (trimmed.startsWith(\"~\")) {\n\t\treturn path.resolve(trimmed.replace(/^~(?=$|[\\\\/])/, os.homedir()));\n\t}\n\treturn path.resolve(trimmed);\n}\n\nfunction resolveStateDirFromEnv(env: NodeJS.ProcessEnv = process.env): string {\n\tconst override = env.ELIZA_STATE_DIR?.trim();\n\tif (override) {\n\t\treturn resolveUserPath(override);\n\t}\n\n\tconst namespace = env.ELIZA_NAMESPACE?.trim() || \"eliza\";\n\treturn path.join(os.homedir(), `.${namespace}`);\n}\n\nfunction resolveProfileSyncStatePath(\n\tenv: NodeJS.ProcessEnv = process.env,\n): string {\n\treturn path.join(\n\t\tresolveStateDirFromEnv(env),\n\t\t\"cache\",\n\t\tPROFILE_SYNC_STATE_FILE,\n\t);\n}\n\nasync function readPersistedProfileSyncState(\n\tenv: NodeJS.ProcessEnv = process.env,\n): Promise<PersistedDiscordProfileSyncState> {\n\ttry {\n\t\tconst raw = await fs.readFile(resolveProfileSyncStatePath(env), \"utf8\");\n\t\tconst parsed = JSON.parse(raw) as Record<string, unknown>;\n\t\treturn {\n\t\t\t...(typeof parsed.avatarHash === \"string\"\n\t\t\t\t? { avatarHash: parsed.avatarHash }\n\t\t\t\t: {}),\n\t\t\t...(typeof parsed.username === \"string\"\n\t\t\t\t? { username: parsed.username }\n\t\t\t\t: {}),\n\t\t};\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nasync function writePersistedProfileSyncState(\n\tstate: PersistedDiscordProfileSyncState,\n\tenv: NodeJS.ProcessEnv = process.env,\n): Promise<void> {\n\tconst statePath = resolveProfileSyncStatePath(env);\n\tawait fs.mkdir(path.dirname(statePath), { recursive: true });\n\tawait fs.writeFile(statePath, JSON.stringify(state, null, 2), {\n\t\tencoding: \"utf8\",\n\t\tmode: 0o600,\n\t});\n}\n\nfunction normalizeDesiredDiscordName(\n\truntime: IAgentRuntime,\n\tsettings: DiscordSettings,\n): string | undefined {\n\tconst configured = settings.profileName?.trim();\n\tif (configured) {\n\t\treturn configured;\n\t}\n\n\tconst characterName = runtime.character.name?.trim();\n\tif (characterName) {\n\t\treturn characterName;\n\t}\n\n\tconst characterUserName = runtime.character.username?.trim();\n\treturn characterUserName || undefined;\n}\n\nfunction readNestedOptionalString(\n\tvalue: unknown,\n\tpathSegments: string[],\n): string | undefined {\n\tlet cursor: unknown = value;\n\tfor (const segment of pathSegments) {\n\t\tif (!cursor || typeof cursor !== \"object\") {\n\t\t\treturn undefined;\n\t\t}\n\t\tcursor = (cursor as Record<string, unknown>)[segment];\n\t}\n\n\treturn typeof cursor === \"string\" && cursor.trim().length > 0\n\t\t? cursor.trim()\n\t\t: undefined;\n}\n\nfunction normalizeDesiredDiscordAvatarSource(\n\truntime: IAgentRuntime,\n\tsettings: DiscordSettings,\n): string | undefined {\n\tconst configured = settings.profileAvatar?.trim();\n\tif (configured) {\n\t\treturn configured;\n\t}\n\n\tconst character = runtime.character as Record<string, unknown> | undefined;\n\tconst fromIdentity =\n\t\treadNestedOptionalString(character, [\"identity\", \"avatar\"]) ??\n\t\treadNestedOptionalString(character, [\"settings\", \"identity\", \"avatar\"]);\n\tif (fromIdentity) {\n\t\treturn fromIdentity;\n\t}\n\n\tconst fromCharacter =\n\t\treadNestedOptionalString(character, [\"avatar\"]) ??\n\t\treadNestedOptionalString(character, [\"settings\", \"avatar\"]);\n\tif (fromCharacter) {\n\t\treturn fromCharacter;\n\t}\n\n\treturn DEFAULT_DISCORD_PROFILE_AVATAR;\n}\n\nfunction extractDataUriPayload(source: string): Buffer | null {\n\tconst match = source.match(/^data:image\\/[^;]+;base64,([a-z0-9+/=]+)$/i);\n\tif (!match) {\n\t\treturn null;\n\t}\n\treturn Buffer.from(match[1], \"base64\");\n}\n\nfunction buildLocalAvatarPathCandidates(source: string): string[] {\n\tconst candidates = new Set<string>();\n\tconst trimmed = source.trim();\n\tif (!trimmed) {\n\t\treturn [];\n\t}\n\n\tcandidates.add(resolveUserPath(trimmed));\n\n\tconst normalized = trimmed.replace(/\\\\/g, \"/\");\n\tconst withoutLeadingSlash = normalized.replace(/^\\/+/, \"\");\n\tif (!withoutLeadingSlash) {\n\t\treturn [...candidates];\n\t}\n\n\tconst repoRoot = process.cwd();\n\tconst publicRoots = [\n\t\tpath.join(repoRoot, \"cloud\", \"public\"),\n\t\tpath.join(repoRoot, \"apps\", \"web\", \"public\"),\n\t\tpath.join(repoRoot, \"public\"),\n\t];\n\n\tfor (const publicRoot of publicRoots) {\n\t\tcandidates.add(path.join(publicRoot, withoutLeadingSlash));\n\t\tif (!withoutLeadingSlash.startsWith(\"avatars/\")) {\n\t\t\tcandidates.add(path.join(publicRoot, \"avatars\", withoutLeadingSlash));\n\t\t}\n\t}\n\n\treturn [...candidates];\n}\n\nasync function readAvatarBytesFromLocalCandidates(\n\tsource: string,\n): Promise<Buffer> {\n\tlet lastError: unknown = null;\n\tfor (const candidate of buildLocalAvatarPathCandidates(source)) {\n\t\ttry {\n\t\t\treturn await fs.readFile(candidate);\n\t\t} catch (error) {\n\t\t\tlastError = error;\n\t\t}\n\t}\n\n\tif (lastError instanceof Error) {\n\t\tthrow lastError;\n\t}\n\tthrow new Error(`Unable to resolve Discord profile avatar source: ${source}`);\n}\n\nasync function loadDiscordProfileAvatarBytes(\n\tsource: string,\n\truntime: IAgentRuntime,\n): Promise<{ bytes: Buffer; hash: string } | null> {\n\tconst trimmed = source.trim();\n\tif (!trimmed) {\n\t\treturn null;\n\t}\n\n\tlet bytes: Buffer | null = extractDataUriPayload(trimmed);\n\tif (!bytes) {\n\t\tlet remoteUrl: URL | null = null;\n\t\ttry {\n\t\t\tconst parsedUrl = new URL(trimmed);\n\t\t\tif (parsedUrl.protocol === \"http:\" || parsedUrl.protocol === \"https:\") {\n\t\t\t\tremoteUrl = parsedUrl;\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (remoteUrl) {\n\t\t\tconst fetchImpl = runtime.fetch ?? globalThis.fetch;\n\t\t\tif (typeof fetchImpl !== \"function\") {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst response = await fetchImpl(trimmed, {\n\t\t\t\theaders: { Accept: \"image/*\" },\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new Error(`HTTP ${response.status}`);\n\t\t\t}\n\t\t\tconst contentType = response.headers.get(\"content-type\");\n\t\t\tif (!contentType?.toLowerCase().startsWith(\"image/\")) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Expected image content-type, got ${contentType ?? \"unknown\"}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tbytes = Buffer.from(await response.arrayBuffer());\n\t\t} else {\n\t\t\tbytes = await readAvatarBytesFromLocalCandidates(trimmed);\n\t\t}\n\t}\n\n\tif (!bytes || bytes.length === 0) {\n\t\treturn null;\n\t}\n\tif (bytes.length > MAX_PROFILE_AVATAR_BYTES) {\n\t\tthrow new Error(\n\t\t\t`Discord profile avatar exceeds ${MAX_PROFILE_AVATAR_BYTES} bytes`,\n\t\t);\n\t}\n\n\treturn {\n\t\tbytes,\n\t\thash: createHash(\"sha256\").update(bytes).digest(\"hex\"),\n\t};\n}\n\nexport async function syncDiscordClientProfile(\n\truntime: IAgentRuntime,\n\tclientUser: Pick<ClientUser, \"username\"> & {\n\t\tsetAvatar?: (avatar: Buffer | string | null) => Promise<unknown>;\n\t\tsetUsername?: (username: string) => Promise<unknown>;\n\t},\n\tsettings: DiscordSettings,\n): Promise<void> {\n\tif (settings.syncProfile === false) {\n\t\treturn;\n\t}\n\n\tconst desiredName = normalizeDesiredDiscordName(runtime, settings);\n\tconst desiredAvatarSource = normalizeDesiredDiscordAvatarSource(\n\t\truntime,\n\t\tsettings,\n\t);\n\tif (!desiredName && !desiredAvatarSource) {\n\t\treturn;\n\t}\n\n\tconst persisted = await readPersistedProfileSyncState();\n\tconst nextState: PersistedDiscordProfileSyncState = { ...persisted };\n\tlet stateChanged = false;\n\n\tif (desiredName) {\n\t\tif (persisted.username !== desiredName) {\n\t\t\tif (clientUser.username !== desiredName) {\n\t\t\t\tif (typeof clientUser.setUsername === \"function\") {\n\t\t\t\t\tawait clientUser.setUsername(desiredName);\n\t\t\t\t\truntime.logger.info(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t\t\tdiscordProfileName: desiredName,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Synchronized Discord bot username from connector settings\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tnextState.username = desiredName;\n\t\t\tstateChanged = true;\n\t\t}\n\t}\n\n\tif (desiredAvatarSource) {\n\t\tconst avatar = await loadDiscordProfileAvatarBytes(\n\t\t\tdesiredAvatarSource,\n\t\t\truntime,\n\t\t);\n\t\tif (avatar && persisted.avatarHash !== avatar.hash) {\n\t\t\tif (typeof clientUser.setAvatar === \"function\") {\n\t\t\t\tawait clientUser.setAvatar(avatar.bytes);\n\t\t\t\truntime.logger.info(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\t},\n\t\t\t\t\t\"Synchronized Discord bot avatar from connector settings\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tnextState.avatarHash = avatar.hash;\n\t\t\tstateChanged = true;\n\t\t}\n\t}\n\n\tif (stateChanged) {\n\t\tawait writePersistedProfileSyncState(nextState);\n\t}\n}\n",
|
|
45
|
+
"/**\n * Discord event listener setup — extracted from service.ts\n *\n * Contains the body of `setupEventListeners()`: all `client.on(...)` registrations\n * for messageCreate, reactions, guild events, interactions, voice streams,\n * and permission audit events.\n */\nimport {\n\tcreateUniqueUuid,\n\ttype ChannelType as ElizaChannelType,\n\ttype EventPayload,\n\ttype UUID,\n} from \"@elizaos/core\";\nimport {\n\tAuditLogEvent,\n\ttype Channel,\n\tChannelType as DiscordChannelType,\n\ttype Role as DiscordRole,\n\ttype GuildChannel,\n\ttype GuildMember,\n\ttype Interaction,\n\ttype Message,\n\ttype User,\n} from \"discord.js\";\nimport {\n\ttype ChannelDebouncer,\n\tcreateChannelDebouncer,\n\tcreateMessageDebouncer,\n\ttype MessageDebouncer,\n} from \"./debouncer\";\nimport {\n\tdiffMemberRoles,\n\tdiffOverwrites,\n\tdiffRolePermissions,\n\tfetchAuditEntry,\n} from \"./permissionEvents\";\nimport type { DiscordService } from \"./service\";\nimport {\n\thandleAutocomplete as handleBuiltinAutocomplete,\n\thandleSlashCommand as handleBuiltinSlashCommand,\n} from \"./slash-commands\";\nimport {\n\tDiscordEventTypes,\n\ttype DiscordListenChannelPayload,\n\ttype DiscordNotInChannelsPayload,\n\ttype DiscordSlashCommand,\n} from \"./types\";\n\n/**\n * Subset of DiscordService fields needed by the event listeners.\n * Because many of the relevant fields are private, the caller passes\n * `this as unknown as DiscordServiceInternals`.\n */\nexport interface DiscordServiceInternals {\n\tclient: NonNullable<DiscordService[\"client\"]>;\n\truntime: DiscordService[\"runtime\"];\n\tcharacter: DiscordService[\"character\"];\n\tmessageManager: DiscordService[\"messageManager\"];\n\tvoiceManager: DiscordService[\"voiceManager\"];\n\tmessageDebouncer: MessageDebouncer | undefined;\n\tchannelDebouncer: ChannelDebouncer | undefined;\n\tdiscordSettings: { shouldIgnoreBotMessages: boolean };\n\tallowedChannelIds: string[] | undefined;\n\tallowAllSlashCommands: Set<string>;\n\tslashCommands: DiscordSlashCommand[];\n\ttimeouts: ReturnType<typeof setTimeout>[];\n\tuserSelections: Map<string, Record<string, unknown>>;\n\n\t// Methods\n\tisChannelAllowed(channelId: string): boolean;\n\tresolveDiscordEntityId(userId: string): UUID;\n\tbuildMemoryFromMessage(\n\t\tmessage: Message,\n\t): Promise<import(\"@elizaos/core\").Memory | null>;\n\tgetChannelType(channel: Channel): Promise<ElizaChannelType>;\n\thandleInteractionCreate(interaction: Interaction): Promise<void>;\n\thandleGuildCreate(guild: import(\"discord.js\").Guild): Promise<void>;\n\thandleGuildMemberAdd(member: GuildMember): Promise<void>;\n\thandleReactionAdd(\n\t\treaction:\n\t\t\t| import(\"discord.js\").MessageReaction\n\t\t\t| import(\"discord.js\").PartialMessageReaction,\n\t\tuser: User | import(\"discord.js\").PartialUser,\n\t): Promise<void>;\n\thandleReactionRemove(\n\t\treaction:\n\t\t\t| import(\"discord.js\").MessageReaction\n\t\t\t| import(\"discord.js\").PartialMessageReaction,\n\t\tuser: User | import(\"discord.js\").PartialUser,\n\t): Promise<void>;\n}\n\n/**\n * Parsed debouncer / listen configuration for setupDiscordEventListeners.\n */\ninterface EventListenerConfig {\n\tlistenCids: string[];\n\tdebounceMs: number;\n\tchannelDebounceMs: number;\n\tresponseCooldownMs: number;\n}\n\nfunction parseEventListenerConfig(\n\tservice: DiscordServiceInternals,\n): EventListenerConfig {\n\tconst listenCidsRaw = service.runtime.getSetting(\n\t\t\"DISCORD_LISTEN_CHANNEL_IDS\",\n\t) as string | string[] | undefined;\n\tconst listenCids = Array.isArray(listenCidsRaw)\n\t\t? listenCidsRaw\n\t\t: listenCidsRaw && typeof listenCidsRaw === \"string\" && listenCidsRaw.trim()\n\t\t\t? listenCidsRaw\n\t\t\t\t\t.trim()\n\t\t\t\t\t.split(\",\")\n\t\t\t\t\t.map((s) => s.trim())\n\t\t\t\t\t.filter((s) => s.length > 0)\n\t\t\t: [];\n\n\tconst debounceMsSetting = service.runtime.getSetting(\"DISCORD_DEBOUNCE_MS\") as\n\t\t| string\n\t\t| number\n\t\t| undefined;\n\tconst debounceMs =\n\t\ttypeof debounceMsSetting === \"number\"\n\t\t\t? debounceMsSetting\n\t\t\t: typeof debounceMsSetting === \"string\" && debounceMsSetting.trim()\n\t\t\t\t? Number.parseInt(debounceMsSetting, 10) || 400\n\t\t\t\t: 400;\n\n\tconst channelDebounceMsSetting = service.runtime.getSetting(\n\t\t\"DISCORD_CHANNEL_DEBOUNCE_MS\",\n\t) as string | number | undefined;\n\tconst channelDebounceMs =\n\t\ttypeof channelDebounceMsSetting === \"number\"\n\t\t\t? channelDebounceMsSetting\n\t\t\t: typeof channelDebounceMsSetting === \"string\" &&\n\t\t\t\t\tchannelDebounceMsSetting.trim()\n\t\t\t\t? Number.parseInt(channelDebounceMsSetting, 10) || 3000\n\t\t\t\t: 3000;\n\n\tconst responseCooldownMsSetting = service.runtime.getSetting(\n\t\t\"DISCORD_RESPONSE_COOLDOWN_MS\",\n\t) as string | number | undefined;\n\tconst responseCooldownMs =\n\t\ttypeof responseCooldownMsSetting === \"number\"\n\t\t\t? responseCooldownMsSetting\n\t\t\t: typeof responseCooldownMsSetting === \"string\" &&\n\t\t\t\t\tresponseCooldownMsSetting.trim()\n\t\t\t\t? Number.parseInt(responseCooldownMsSetting, 10) || 30000\n\t\t\t\t: 30000;\n\n\treturn { listenCids, debounceMs, channelDebounceMs, responseCooldownMs };\n}\n\n/**\n * Wire up all Discord.js event listeners on `service.client`.\n *\n * Returns the created debouncers so the caller can store them on the service\n * instance (they must be destroyed on stop).\n */\nexport function setupDiscordEventListeners(service: DiscordServiceInternals): {\n\tmessageDebouncer: MessageDebouncer;\n\tchannelDebouncer: ChannelDebouncer;\n} {\n\tconst { listenCids, debounceMs, channelDebounceMs, responseCooldownMs } =\n\t\tparseEventListenerConfig(service);\n\n\t// ── Message debouncer ──────────────────────────────────────────────\n\tconst messageDebouncer = createMessageDebouncer((messages) => {\n\t\tif (!service.messageManager || messages.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (messages.length === 1) {\n\t\t\tvoid service.messageManager.handleMessage(messages[0]);\n\t\t\treturn;\n\t\t}\n\n\t\tconst anchor = messages[0];\n\t\tconst combinedText = messages.map((message) => message.content).join(\"\\n\");\n\t\tconst combined = Object.create(anchor, {\n\t\t\tcontent: { value: combinedText, writable: true, enumerable: true },\n\t\t});\n\t\tvoid service.messageManager.handleMessage(combined as Message);\n\t}, debounceMs);\n\n\t// ── Channel debouncer ──────────────────────────────────────────────\n\tconst channelDebouncer = createChannelDebouncer(\n\t\t(messages) => {\n\t\t\tif (!service.messageManager || messages.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst clientUser = service.client?.user;\n\t\t\tconst botId = clientUser?.id;\n\t\t\tconst agentName = service.character?.name?.toLowerCase();\n\n\t\t\tlet anchor: Message | undefined;\n\t\t\tif (botId) {\n\t\t\t\tanchor = messages.find(\n\t\t\t\t\t(message) =>\n\t\t\t\t\t\tmessage.mentions?.users?.has(botId) ||\n\t\t\t\t\t\tBoolean(\n\t\t\t\t\t\t\tagentName &&\n\t\t\t\t\t\t\t\tagentName.length >= 2 &&\n\t\t\t\t\t\t\t\tmessage.content?.toLowerCase().includes(agentName),\n\t\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tif (!anchor) {\n\t\t\t\t\tanchor = messages.find(\n\t\t\t\t\t\t(message) =>\n\t\t\t\t\t\t\tBoolean(message.reference?.messageId) &&\n\t\t\t\t\t\t\tmessage.mentions?.repliedUser?.id === botId,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tanchor ??= messages[messages.length - 1];\n\t\t\tif (messages.length === 1) {\n\t\t\t\tvoid service.messageManager.handleMessage(anchor);\n\t\t\t} else {\n\t\t\t\tconst contextLines = messages\n\t\t\t\t\t.filter((message) => message.id !== anchor?.id)\n\t\t\t\t\t.map(\n\t\t\t\t\t\t(message) =>\n\t\t\t\t\t\t\t`${message.member?.displayName ?? message.author.globalName ?? message.author.displayName ?? message.author.username}: ${message.content}`,\n\t\t\t\t\t);\n\t\t\t\tconst combinedText =\n\t\t\t\t\tcontextLines.length > 0\n\t\t\t\t\t\t? `[Recent channel context]\\n${contextLines.join(\"\\n\")}\\n\\n${anchor.content || \"\"}`\n\t\t\t\t\t\t: anchor.content || \"\";\n\t\t\t\tconst combined = Object.create(anchor, {\n\t\t\t\t\tcontent: { value: combinedText, writable: true, enumerable: true },\n\t\t\t\t});\n\t\t\t\tvoid service.messageManager.handleMessage(combined as Message);\n\t\t\t}\n\n\t\t\tchannelDebouncer?.markResponded(messages[0].channel.id);\n\t\t},\n\t\t{\n\t\t\tdebounceMs: channelDebounceMs,\n\t\t\tresponseCooldownMs,\n\t\t\tgetBotUserId: () => service.client?.user?.id,\n\t\t\tbotName: service.character?.name,\n\t\t},\n\t);\n\n\tservice.messageDebouncer = messageDebouncer;\n\tservice.channelDebouncer = channelDebouncer;\n\n\t// ── messageCreate ──────────────────────────────────────────────────\n\tservice.client.on(\"messageCreate\", async (message) => {\n\t\tconst clientUser = service.client?.user;\n\t\tif (\n\t\t\t(clientUser && message.author.id === clientUser.id) ||\n\t\t\t(message.author.bot && service.discordSettings.shouldIgnoreBotMessages)\n\t\t) {\n\t\t\tservice.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\tauthorId: message.author.id,\n\t\t\t\t\tisBot: message.author.bot,\n\t\t\t\t},\n\t\t\t\t\"Ignoring message from bot or self\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (listenCids.includes(message.channel.id) && message) {\n\t\t\tconst newMessage = await service.buildMemoryFromMessage(message);\n\n\t\t\tif (!newMessage) {\n\t\t\t\tservice.runtime.logger.warn(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\tmessageId: message.id,\n\t\t\t\t\t},\n\t\t\t\t\t\"Failed to build memory from listen channel message\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst listenPayload: DiscordListenChannelPayload = {\n\t\t\t\truntime: service.runtime,\n\t\t\t\tmessage: newMessage,\n\t\t\t\tsource: \"discord\",\n\t\t\t};\n\t\t\tservice.runtime.emitEvent(\n\t\t\t\tDiscordEventTypes.LISTEN_CHANNEL_MESSAGE,\n\t\t\t\tlistenPayload,\n\t\t\t);\n\t\t}\n\n\t\t// Skip if channel restrictions are set and this channel is not allowed\n\t\tif (\n\t\t\tservice.allowedChannelIds &&\n\t\t\t!service.isChannelAllowed(message.channel.id)\n\t\t) {\n\t\t\tconst channel = service.client\n\t\t\t\t? await service.client.channels.fetch(message.channel.id)\n\t\t\t\t: null;\n\n\t\t\tconst notInChannelsPayload: DiscordNotInChannelsPayload = {\n\t\t\t\truntime: service.runtime,\n\t\t\t\tmessage: message,\n\t\t\t\tsource: \"discord\",\n\t\t\t};\n\t\t\tservice.runtime.emitEvent(\n\t\t\t\tDiscordEventTypes.NOT_IN_CHANNELS_MESSAGE,\n\t\t\t\tnotInChannelsPayload,\n\t\t\t);\n\n\t\t\tif (!channel) {\n\t\t\t\tservice.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\tchannelId: message.channel.id,\n\t\t\t\t\t},\n\t\t\t\t\t\"Channel not found\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (channel.isThread()) {\n\t\t\t\tif (!channel.parentId || !service.isChannelAllowed(channel.parentId)) {\n\t\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\t\tparentChannelId: channel.parentId,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Thread not in allowed channel\",\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (\n\t\t\t\t\tchannel?.isTextBased &&\n\t\t\t\t\ttypeof channel.isTextBased === \"function\" &&\n\t\t\t\t\tchannel.isTextBased()\n\t\t\t\t) {\n\t\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\t\tchannelId: channel.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Channel not allowed\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tif (!service.messageManager) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst channelType = message.channel.type as DiscordChannelType;\n\t\t\tconst isDm =\n\t\t\t\tchannelType === DiscordChannelType.DM ||\n\t\t\t\tchannelType === DiscordChannelType.GroupDM;\n\n\t\t\tif (isDm) {\n\t\t\t\tif (service.messageDebouncer) {\n\t\t\t\t\tservice.messageDebouncer.enqueue(message);\n\t\t\t\t} else {\n\t\t\t\t\tawait service.messageManager.handleMessage(message);\n\t\t\t\t}\n\t\t\t} else if (service.channelDebouncer) {\n\t\t\t\tservice.channelDebouncer.enqueue(message);\n\t\t\t} else if (service.messageDebouncer) {\n\t\t\t\tservice.messageDebouncer.enqueue(message);\n\t\t\t} else {\n\t\t\t\tawait service.messageManager.handleMessage(message);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tservice.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error handling message\",\n\t\t\t);\n\t\t}\n\t});\n\n\t// ── messageReactionAdd ─────────────────────────────────────────────\n\tservice.client.on(\"messageReactionAdd\", async (reaction, user) => {\n\t\tconst clientUser = service.client?.user;\n\t\tif (clientUser && user.id === clientUser.id) {\n\t\t\treturn;\n\t\t}\n\t\tif (\n\t\t\tservice.allowedChannelIds &&\n\t\t\treaction.message.channel &&\n\t\t\t!service.isChannelAllowed(reaction.message.channel.id)\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tawait service.handleReactionAdd(reaction, user);\n\t\t} catch (error) {\n\t\t\tservice.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error handling reaction add\",\n\t\t\t);\n\t\t}\n\t});\n\n\t// ── messageReactionRemove ──────────────────────────────────────────\n\tservice.client.on(\"messageReactionRemove\", async (reaction, user) => {\n\t\tconst clientUser = service.client?.user;\n\t\tif (clientUser && user.id === clientUser.id) {\n\t\t\treturn;\n\t\t}\n\t\tif (\n\t\t\tservice.allowedChannelIds &&\n\t\t\treaction.message.channel &&\n\t\t\t!service.isChannelAllowed(reaction.message.channel.id)\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tawait service.handleReactionRemove(reaction, user);\n\t\t} catch (error) {\n\t\t\tservice.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error handling reaction remove\",\n\t\t\t);\n\t\t}\n\t});\n\n\t// ── guildCreate ────────────────────────────────────────────────────\n\tservice.client.on(\"guildCreate\", async (guild) => {\n\t\ttry {\n\t\t\tawait service.handleGuildCreate(guild);\n\t\t} catch (error) {\n\t\t\tservice.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error handling guild create\",\n\t\t\t);\n\t\t}\n\t});\n\n\t// ── guildMemberAdd ─────────────────────────────────────────────────\n\tservice.client.on(\"guildMemberAdd\", async (member) => {\n\t\ttry {\n\t\t\tawait service.handleGuildMemberAdd(member);\n\t\t} catch (error) {\n\t\t\tservice.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error handling guild member add\",\n\t\t\t);\n\t\t}\n\t});\n\n\t// ── interactionCreate ──────────────────────────────────────────────\n\tservice.client.on(\"interactionCreate\", async (interaction) => {\n\t\tif (interaction.isAutocomplete()) {\n\t\t\ttry {\n\t\t\t\tawait handleBuiltinAutocomplete(interaction);\n\t\t\t} catch (error) {\n\t\t\t\tservice.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t},\n\t\t\t\t\t\"Error handling Discord autocomplete interaction\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst isSlashCommand = interaction.isCommand();\n\t\tconst isModalSubmit = interaction.isModalSubmit();\n\t\tconst isComponent = interaction.isMessageComponent();\n\n\t\tconst bypassChannelRestriction =\n\t\t\tisSlashCommand &&\n\t\t\tservice.allowAllSlashCommands.has(interaction.commandName ?? \"\");\n\n\t\tservice.runtime.logger.debug(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\tinteractionType: interaction.type,\n\t\t\t\tcommandName: isSlashCommand ? interaction.commandName : undefined,\n\t\t\t\tchannelId: interaction.channelId,\n\t\t\t\tinGuild: interaction.inGuild(),\n\t\t\t\tbypassChannelRestriction,\n\t\t\t},\n\t\t\t\"[DiscordService] interactionCreate received\",\n\t\t);\n\n\t\tconst isFollowUpInteraction = Boolean(\n\t\t\tinteraction.isModalSubmit() ||\n\t\t\t\tinteraction.isMessageComponent() ||\n\t\t\t\tinteraction.isAutocomplete(),\n\t\t);\n\n\t\tif (\n\t\t\t!isFollowUpInteraction &&\n\t\t\tservice.allowedChannelIds &&\n\t\t\tinteraction.channelId &&\n\t\t\t!service.isChannelAllowed(interaction.channelId) &&\n\t\t\t!bypassChannelRestriction\n\t\t) {\n\t\t\tif (isSlashCommand && interaction.isCommand()) {\n\t\t\t\ttry {\n\t\t\t\t\tawait interaction.reply({\n\t\t\t\t\t\tcontent: \"This command is not available in this channel.\",\n\t\t\t\t\t\tephemeral: true,\n\t\t\t\t\t});\n\t\t\t\t} catch (responseError) {\n\t\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\tresponseError instanceof Error\n\t\t\t\t\t\t\t\t\t? responseError.message\n\t\t\t\t\t\t\t\t\t: String(responseError),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Could not send channel restriction response\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tservice.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\tchannelId: interaction.channelId,\n\t\t\t\t\tallowedChannelIds: service.allowedChannelIds,\n\t\t\t\t\tisSlashCommand,\n\t\t\t\t\tisModalSubmit,\n\t\t\t\t\tisComponent,\n\t\t\t\t\tbypassChannelRestriction,\n\t\t\t\t},\n\t\t\t\t\"[DiscordService] interactionCreate ignored (channel not allowed)\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Run custom validator if provided for slash commands\n\t\tif (isSlashCommand && interaction.commandName) {\n\t\t\tconst command = service.slashCommands.find(\n\t\t\t\t(cmd) => cmd.name === interaction.commandName,\n\t\t\t);\n\t\t\tif (command?.validator) {\n\t\t\t\ttry {\n\t\t\t\t\tconst isValid = await command.validator(interaction, service.runtime);\n\t\t\t\t\tif (!isValid) {\n\t\t\t\t\t\tif (!interaction.replied) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\t\t\t\t\"You do not have permission to use this command.\";\n\t\t\t\t\t\t\t\tif (interaction.deferred) {\n\t\t\t\t\t\t\t\t\tawait interaction.editReply({ content: errorMessage });\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tawait interaction.reply({\n\t\t\t\t\t\t\t\t\t\tcontent: errorMessage,\n\t\t\t\t\t\t\t\t\t\tephemeral: true,\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} catch (responseError) {\n\t\t\t\t\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\t\t\t\t\tcommandName: interaction.commandName,\n\t\t\t\t\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\t\t\t\tresponseError instanceof Error\n\t\t\t\t\t\t\t\t\t\t\t\t? responseError.message\n\t\t\t\t\t\t\t\t\t\t\t\t: String(responseError),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\"Could not send validator rejection response (may have already responded)\",\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\tservice.runtime.logger.debug(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\t\t\tcommandName: interaction.commandName,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"[DiscordService] interactionCreate ignored (custom validator returned false)\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!interaction.replied) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\t\t\t\"An error occurred while validating this command.\";\n\t\t\t\t\t\t\tif (interaction.deferred) {\n\t\t\t\t\t\t\t\tawait interaction.editReply({ content: errorMessage });\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tawait interaction.reply({\n\t\t\t\t\t\t\t\t\tcontent: errorMessage,\n\t\t\t\t\t\t\t\t\tephemeral: true,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (responseError) {\n\t\t\t\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\t\t\t\tcommandName: interaction.commandName,\n\t\t\t\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\t\t\tresponseError instanceof Error\n\t\t\t\t\t\t\t\t\t\t\t? responseError.message\n\t\t\t\t\t\t\t\t\t\t\t: String(responseError),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"Could not send validator error response (may have already responded)\",\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\tservice.runtime.logger.error(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\t\tcommandName: interaction.commandName,\n\t\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"[DiscordService] Custom validator threw error\",\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tawait service.handleInteractionCreate(interaction);\n\t\t\tif (interaction.isChatInputCommand()) {\n\t\t\t\tconst entityId = service.resolveDiscordEntityId(interaction.user.id);\n\t\t\t\tconst roomId = createUniqueUuid(\n\t\t\t\t\tservice.runtime,\n\t\t\t\t\tinteraction.channelId || interaction.user.username,\n\t\t\t\t);\n\t\t\t\tawait handleBuiltinSlashCommand(interaction, service.runtime, {\n\t\t\t\t\tentityId,\n\t\t\t\t\troomId,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tservice.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error handling interaction\",\n\t\t\t);\n\t\t}\n\t});\n\n\t// ── userStream (voice) ─────────────────────────────────────────────\n\tservice.client.on(\n\t\t\"userStream\",\n\t\t(entityId, name, userName, channel, opusDecoder) => {\n\t\t\tconst clientUser = service.client?.user;\n\t\t\tif (clientUser && entityId !== clientUser.id) {\n\t\t\t\tif (service.voiceManager) {\n\t\t\t\t\tservice.voiceManager.handleUserStream(\n\t\t\t\t\t\tentityId,\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tuserName,\n\t\t\t\t\t\tchannel,\n\t\t\t\t\t\topusDecoder,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t);\n\n\t// ── Permission Audit Events ────────────────────────────────────────\n\tconst auditLogSetting = service.runtime.getSetting(\n\t\t\"DISCORD_AUDIT_LOG_ENABLED\",\n\t);\n\tconst isAuditLogEnabled =\n\t\tauditLogSetting === \"true\" ||\n\t\tauditLogSetting === true ||\n\t\tauditLogSetting === \"1\" ||\n\t\tauditLogSetting === 1;\n\n\tif (isAuditLogEnabled) {\n\t\t// channelUpdate\n\t\tservice.client.on(\"channelUpdate\", async (oldChannel, newChannel) => {\n\t\t\ttry {\n\t\t\t\tlet channel = newChannel;\n\t\t\t\tif (channel.partial) {\n\t\t\t\t\tchannel = await channel.fetch();\n\t\t\t\t}\n\n\t\t\t\tif (!(\"permissionOverwrites\" in oldChannel) || !(\"guild\" in channel)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst guildChannel = channel as GuildChannel;\n\t\t\t\tconst oldGuildChannel = oldChannel as GuildChannel;\n\t\t\t\tconst oldOverwrites = oldGuildChannel.permissionOverwrites.cache;\n\t\t\t\tconst newOverwrites = guildChannel.permissionOverwrites.cache;\n\n\t\t\t\tconst allIds = new Set([\n\t\t\t\t\t...oldOverwrites.keys(),\n\t\t\t\t\t...newOverwrites.keys(),\n\t\t\t\t]);\n\n\t\t\t\tfor (const id of allIds) {\n\t\t\t\t\tconst oldOw = oldOverwrites.get(id);\n\t\t\t\t\tconst newOw = newOverwrites.get(id);\n\t\t\t\t\tconst { changes, action } = diffOverwrites(oldOw, newOw);\n\n\t\t\t\t\tif (changes.length === 0) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst auditAction =\n\t\t\t\t\t\taction === \"DELETE\"\n\t\t\t\t\t\t\t? AuditLogEvent.ChannelOverwriteDelete\n\t\t\t\t\t\t\t: action === \"CREATE\"\n\t\t\t\t\t\t\t\t? AuditLogEvent.ChannelOverwriteCreate\n\t\t\t\t\t\t\t\t: AuditLogEvent.ChannelOverwriteUpdate;\n\n\t\t\t\t\tconst audit = await fetchAuditEntry(\n\t\t\t\t\t\tguildChannel.guild,\n\t\t\t\t\t\tauditAction,\n\t\t\t\t\t\tguildChannel.id,\n\t\t\t\t\t\tservice.runtime,\n\t\t\t\t\t);\n\n\t\t\t\t\tconst clientUser = service.client?.user;\n\t\t\t\t\tif (\n\t\t\t\t\t\taudit?.executorId &&\n\t\t\t\t\t\tclientUser &&\n\t\t\t\t\t\taudit.executorId === clientUser.id\n\t\t\t\t\t) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst oldOwType =\n\t\t\t\t\t\toldOw && oldOw.type !== undefined ? oldOw.type : null;\n\t\t\t\t\tconst newOwType =\n\t\t\t\t\t\tnewOw && newOw.type !== undefined ? newOw.type : null;\n\t\t\t\t\tconst targetType =\n\t\t\t\t\t\t(oldOwType ?? newOwType ?? 1) === 0 ? \"role\" : \"user\";\n\t\t\t\t\tlet targetName: string;\n\t\t\t\t\tif (targetType === \"role\") {\n\t\t\t\t\t\tconst role = guildChannel.guild.roles.cache.get(id);\n\t\t\t\t\t\ttargetName = role?.name ?? \"Unknown\";\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst user = service.client\n\t\t\t\t\t\t\t? await service.client.users.fetch(id).catch(() => null)\n\t\t\t\t\t\t\t: null;\n\t\t\t\t\t\ttargetName = user?.tag ?? \"Unknown\";\n\t\t\t\t\t}\n\n\t\t\t\t\tservice.runtime.emitEvent(\n\t\t\t\t\t\tDiscordEventTypes.CHANNEL_PERMISSIONS_CHANGED,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\truntime: service.runtime,\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t\tguild: {\n\t\t\t\t\t\t\t\tid: guildChannel.guild.id,\n\t\t\t\t\t\t\t\tname: guildChannel.guild.name,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tchannel: { id: guildChannel.id, name: guildChannel.name },\n\t\t\t\t\t\t\ttarget: { type: targetType, id, name: targetName },\n\t\t\t\t\t\t\taction,\n\t\t\t\t\t\t\tchanges,\n\t\t\t\t\t\t\taudit,\n\t\t\t\t\t\t} as EventPayload,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tservice.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t},\n\t\t\t\t\t\"Error in channelUpdate handler\",\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\t// roleUpdate\n\t\tservice.client.on(\"roleUpdate\", async (oldRole, newRole) => {\n\t\t\ttry {\n\t\t\t\tconst changes = diffRolePermissions(oldRole, newRole);\n\t\t\t\tif (changes.length === 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst audit = await fetchAuditEntry(\n\t\t\t\t\tnewRole.guild,\n\t\t\t\t\tAuditLogEvent.RoleUpdate,\n\t\t\t\t\tnewRole.id,\n\t\t\t\t\tservice.runtime,\n\t\t\t\t);\n\n\t\t\t\tconst clientUser = service.client?.user;\n\t\t\t\tif (\n\t\t\t\t\taudit?.executorId &&\n\t\t\t\t\tclientUser &&\n\t\t\t\t\taudit.executorId === clientUser.id\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tservice.runtime.emitEvent(DiscordEventTypes.ROLE_PERMISSIONS_CHANGED, {\n\t\t\t\t\truntime: service.runtime,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tguild: { id: newRole.guild.id, name: newRole.guild.name },\n\t\t\t\t\trole: { id: newRole.id, name: newRole.name },\n\t\t\t\t\tchanges,\n\t\t\t\t\taudit,\n\t\t\t\t} as EventPayload);\n\t\t\t} catch (err) {\n\t\t\t\tservice.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t},\n\t\t\t\t\t\"Error in roleUpdate handler\",\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\t// guildMemberUpdate\n\t\tservice.client.on(\"guildMemberUpdate\", async (oldMember, newMember) => {\n\t\t\ttry {\n\t\t\t\tif (!oldMember) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet fullOldMember = oldMember;\n\t\t\t\tif (oldMember.partial) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfullOldMember = await oldMember.fetch();\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst { added, removed } = diffMemberRoles(\n\t\t\t\t\tfullOldMember as GuildMember,\n\t\t\t\t\tnewMember,\n\t\t\t\t);\n\t\t\t\tif (added.length === 0 && removed.length === 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst audit = await fetchAuditEntry(\n\t\t\t\t\tnewMember.guild,\n\t\t\t\t\tAuditLogEvent.MemberRoleUpdate,\n\t\t\t\t\tnewMember.id,\n\t\t\t\t\tservice.runtime,\n\t\t\t\t);\n\n\t\t\t\tconst clientUser = service.client?.user;\n\t\t\t\tif (\n\t\t\t\t\taudit?.executorId &&\n\t\t\t\t\tclientUser &&\n\t\t\t\t\taudit.executorId === clientUser.id\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tservice.runtime.emitEvent(DiscordEventTypes.MEMBER_ROLES_CHANGED, {\n\t\t\t\t\truntime: service.runtime,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tguild: { id: newMember.guild.id, name: newMember.guild.name },\n\t\t\t\t\tmember: { id: newMember.id, tag: newMember.user.tag },\n\t\t\t\t\tadded: added.map((r: DiscordRole) => ({\n\t\t\t\t\t\tid: r.id,\n\t\t\t\t\t\tname: r.name,\n\t\t\t\t\t\tpermissions: r.permissions.toArray(),\n\t\t\t\t\t})),\n\t\t\t\t\tremoved: removed.map((r: DiscordRole) => ({\n\t\t\t\t\t\tid: r.id,\n\t\t\t\t\t\tname: r.name,\n\t\t\t\t\t\tpermissions: r.permissions.toArray(),\n\t\t\t\t\t})),\n\t\t\t\t\taudit,\n\t\t\t\t} as EventPayload);\n\t\t\t} catch (err) {\n\t\t\t\tservice.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t},\n\t\t\t\t\t\"Error in guildMemberUpdate handler\",\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\t// roleCreate\n\t\tservice.client.on(\"roleCreate\", async (role) => {\n\t\t\ttry {\n\t\t\t\tconst audit = await fetchAuditEntry(\n\t\t\t\t\trole.guild,\n\t\t\t\t\tAuditLogEvent.RoleCreate,\n\t\t\t\t\trole.id,\n\t\t\t\t\tservice.runtime,\n\t\t\t\t);\n\n\t\t\t\tconst clientUser = service.client?.user;\n\t\t\t\tif (\n\t\t\t\t\taudit?.executorId &&\n\t\t\t\t\tclientUser &&\n\t\t\t\t\taudit.executorId === clientUser.id\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tservice.runtime.emitEvent(DiscordEventTypes.ROLE_CREATED, {\n\t\t\t\t\truntime: service.runtime,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tguild: { id: role.guild.id, name: role.guild.name },\n\t\t\t\t\trole: {\n\t\t\t\t\t\tid: role.id,\n\t\t\t\t\t\tname: role.name,\n\t\t\t\t\t\tpermissions: role.permissions.toArray(),\n\t\t\t\t\t},\n\t\t\t\t\taudit,\n\t\t\t\t} as EventPayload);\n\t\t\t} catch (err) {\n\t\t\t\tservice.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t},\n\t\t\t\t\t\"Error in roleCreate handler\",\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\t// roleDelete\n\t\tservice.client.on(\"roleDelete\", async (role) => {\n\t\t\ttry {\n\t\t\t\tconst audit = await fetchAuditEntry(\n\t\t\t\t\trole.guild,\n\t\t\t\t\tAuditLogEvent.RoleDelete,\n\t\t\t\t\trole.id,\n\t\t\t\t\tservice.runtime,\n\t\t\t\t);\n\n\t\t\t\tconst clientUser = service.client?.user;\n\t\t\t\tif (\n\t\t\t\t\taudit?.executorId &&\n\t\t\t\t\tclientUser &&\n\t\t\t\t\taudit.executorId === clientUser.id\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tservice.runtime.emitEvent(DiscordEventTypes.ROLE_DELETED, {\n\t\t\t\t\truntime: service.runtime,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tguild: { id: role.guild.id, name: role.guild.name },\n\t\t\t\t\trole: {\n\t\t\t\t\t\tid: role.id,\n\t\t\t\t\t\tname: role.name,\n\t\t\t\t\t\tpermissions: role.permissions.toArray(),\n\t\t\t\t\t},\n\t\t\t\t\taudit,\n\t\t\t\t} as EventPayload);\n\t\t\t} catch (err) {\n\t\t\t\tservice.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t},\n\t\t\t\t\t\"Error in roleDelete handler\",\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t} // end if (isAuditLogEnabled)\n\n\treturn { messageDebouncer, channelDebouncer };\n}\n",
|
|
46
|
+
"import type { Message as DiscordMessage } from \"discord.js\";\n\nexport type DebouncerFlushCallback = (messages: DiscordMessage[]) => void;\n\nexport interface MessageDebouncer {\n\tenqueue: (message: DiscordMessage) => void;\n\tflushAll: () => void;\n\tpendingCount: () => number;\n\tdestroy: () => void;\n}\n\ninterface PendingEntry {\n\tmessages: DiscordMessage[];\n\ttimer: ReturnType<typeof setTimeout>;\n}\n\nconst DEFAULT_DEBOUNCE_MS = 400;\nconst DEFAULT_CHANNEL_DEBOUNCE_MS = 3_000;\n\nfunction escapeRegex(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction runSafely(callback: () => void): void {\n\ttry {\n\t\tcallback();\n\t} catch {\n\t\t// Debouncer callbacks should never crash the process.\n\t}\n}\n\nexport interface ChannelDebouncerOptions {\n\tdebounceMs?: number;\n\tresponseCooldownMs?: number;\n\tbotUserId?: string;\n\tgetBotUserId?: () => string | undefined;\n\tbotName?: string;\n}\n\nexport interface ChannelDebouncer {\n\tenqueue: (message: DiscordMessage) => void;\n\tmarkResponded: (channelId: string) => void;\n\tflushAll: () => void;\n\tpendingCount: () => number;\n\tdestroy: () => void;\n}\n\ninterface ChannelPendingEntry {\n\tmessages: DiscordMessage[];\n\ttimer: ReturnType<typeof setTimeout>;\n}\n\nexport function createChannelDebouncer(\n\tonFlush: DebouncerFlushCallback,\n\toptions: ChannelDebouncerOptions = {},\n): ChannelDebouncer {\n\tconst debounceMs = options.debounceMs ?? DEFAULT_CHANNEL_DEBOUNCE_MS;\n\tconst responseCooldownMs = options.responseCooldownMs ?? 30_000;\n\tconst botName = options.botName?.trim();\n\tconst botNameRegex =\n\t\tbotName && botName.length >= 2\n\t\t\t? new RegExp(\n\t\t\t\t\t`(^|[^\\\\p{L}\\\\p{N}])${escapeRegex(botName)}(?=$|[^\\\\p{L}\\\\p{N}])`,\n\t\t\t\t\t\"iu\",\n\t\t\t\t)\n\t\t\t: null;\n\tconst pending = new Map<string, ChannelPendingEntry>();\n\tconst lastResponseTime = new Map<string, number>();\n\n\tconst isBotTargeted = (message: DiscordMessage): boolean => {\n\t\tconst botId = options.getBotUserId?.() ?? options.botUserId;\n\t\tif (botId && message.mentions?.users?.has(botId)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (\n\t\t\tbotId &&\n\t\t\tmessage.reference?.messageId &&\n\t\t\tmessage.mentions?.repliedUser?.id === botId\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\treturn Boolean(botNameRegex?.test(message.content ?? \"\"));\n\t};\n\n\tconst isInCooldown = (channelId: string): boolean => {\n\t\tconst lastRespondedAt = lastResponseTime.get(channelId);\n\t\tif (!lastRespondedAt) {\n\t\t\treturn false;\n\t\t}\n\t\tif (Date.now() - lastRespondedAt >= responseCooldownMs) {\n\t\t\tlastResponseTime.delete(channelId);\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t};\n\n\tconst flush = (channelId: string) => {\n\t\tconst entry = pending.get(channelId);\n\t\tif (!entry) {\n\t\t\treturn;\n\t\t}\n\t\tclearTimeout(entry.timer);\n\t\tpending.delete(channelId);\n\t\tif (entry.messages.length > 0) {\n\t\t\trunSafely(() => onFlush(entry.messages));\n\t\t}\n\t};\n\n\tconst enqueue = (message: DiscordMessage) => {\n\t\tconst channelId = message.channel.id;\n\t\tif (isBotTargeted(message)) {\n\t\t\tconst entry = pending.get(channelId);\n\t\t\tif (entry) {\n\t\t\t\tclearTimeout(entry.timer);\n\t\t\t\tpending.delete(channelId);\n\t\t\t\tentry.messages.push(message);\n\t\t\t\trunSafely(() => onFlush(entry.messages));\n\t\t\t} else {\n\t\t\t\trunSafely(() => onFlush([message]));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (isInCooldown(channelId)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (debounceMs <= 0) {\n\t\t\trunSafely(() => onFlush([message]));\n\t\t\treturn;\n\t\t}\n\n\t\tconst existing = pending.get(channelId);\n\t\tif (existing) {\n\t\t\tclearTimeout(existing.timer);\n\t\t\texisting.messages.push(message);\n\t\t\texisting.timer = setTimeout(() => flush(channelId), debounceMs);\n\t\t\treturn;\n\t\t}\n\n\t\tpending.set(channelId, {\n\t\t\tmessages: [message],\n\t\t\ttimer: setTimeout(() => flush(channelId), debounceMs),\n\t\t});\n\t};\n\n\treturn {\n\t\tenqueue,\n\t\tmarkResponded: (channelId: string) => {\n\t\t\tlastResponseTime.set(channelId, Date.now());\n\t\t},\n\t\tflushAll: () => {\n\t\t\tfor (const key of [...pending.keys()]) {\n\t\t\t\tflush(key);\n\t\t\t}\n\t\t},\n\t\tpendingCount: () => pending.size,\n\t\tdestroy: () => {\n\t\t\tfor (const [, entry] of pending) {\n\t\t\t\tclearTimeout(entry.timer);\n\t\t\t}\n\t\t\tpending.clear();\n\t\t\tlastResponseTime.clear();\n\t\t},\n\t};\n}\n\nexport function createMessageDebouncer(\n\tonFlush: DebouncerFlushCallback,\n\tdebounceMs: number = DEFAULT_DEBOUNCE_MS,\n): MessageDebouncer {\n\tconst pending = new Map<string, PendingEntry>();\n\n\tconst makeKey = (message: DiscordMessage) =>\n\t\t`${message.channel.id}:${message.author.id}`;\n\n\tconst flush = (key: string) => {\n\t\tconst entry = pending.get(key);\n\t\tif (!entry) {\n\t\t\treturn;\n\t\t}\n\t\tclearTimeout(entry.timer);\n\t\tpending.delete(key);\n\t\tif (entry.messages.length > 0) {\n\t\t\trunSafely(() => onFlush(entry.messages));\n\t\t}\n\t};\n\n\tconst hasMedia = (message: DiscordMessage) =>\n\t\t(message.attachments?.size ?? 0) > 0 || (message.stickers?.size ?? 0) > 0;\n\n\tconst enqueue = (message: DiscordMessage) => {\n\t\tif (debounceMs <= 0) {\n\t\t\trunSafely(() => onFlush([message]));\n\t\t\treturn;\n\t\t}\n\n\t\tconst key = makeKey(message);\n\t\tif (hasMedia(message)) {\n\t\t\tconst entry = pending.get(key);\n\t\t\tif (entry) {\n\t\t\t\tclearTimeout(entry.timer);\n\t\t\t\tpending.delete(key);\n\t\t\t\tif (entry.messages.length > 0) {\n\t\t\t\t\trunSafely(() => onFlush(entry.messages));\n\t\t\t\t}\n\t\t\t}\n\t\t\trunSafely(() => onFlush([message]));\n\t\t\treturn;\n\t\t}\n\n\t\tconst existing = pending.get(key);\n\t\tif (existing) {\n\t\t\tclearTimeout(existing.timer);\n\t\t\texisting.messages.push(message);\n\t\t\texisting.timer = setTimeout(() => flush(key), debounceMs);\n\t\t\treturn;\n\t\t}\n\n\t\tpending.set(key, {\n\t\t\tmessages: [message],\n\t\t\ttimer: setTimeout(() => flush(key), debounceMs),\n\t\t});\n\t};\n\n\treturn {\n\t\tenqueue,\n\t\tflushAll: () => {\n\t\t\tfor (const key of [...pending.keys()]) {\n\t\t\t\tflush(key);\n\t\t\t}\n\t\t},\n\t\tpendingCount: () => pending.size,\n\t\tdestroy: () => {\n\t\t\tfor (const [, entry] of pending) {\n\t\t\t\tclearTimeout(entry.timer);\n\t\t\t}\n\t\t\tpending.clear();\n\t\t},\n\t};\n}\n",
|
|
47
|
+
"import type { IAgentRuntime } from \"@elizaos/core\";\nimport type {\n\tAuditLogEvent,\n\tGuild,\n\tGuildMember,\n\tPermissionOverwrites,\n\tRole,\n} from \"discord.js\";\nimport type { AuditInfo, PermissionDiff, PermissionState } from \"./types\";\n\n/**\n * Permissions that indicate moderation/admin capabilities.\n * Changes to these permissions are considered elevated and worth tracking.\n */\nexport const ELEVATED_PERMISSIONS = [\n\t\"Administrator\",\n\t\"ManageGuild\",\n\t\"ManageChannels\",\n\t\"ManageRoles\",\n\t\"KickMembers\",\n\t\"BanMembers\",\n\t\"ModerateMembers\",\n\t\"ManageMessages\",\n\t\"ManageWebhooks\",\n\t\"ManageNicknames\",\n\t\"MuteMembers\",\n\t\"DeafenMembers\",\n\t\"MoveMembers\",\n\t\"ManageEvents\",\n\t\"ManageThreads\",\n] as const;\n\n/**\n * Check if a role has any elevated (moderation/admin) permissions\n */\nexport function isElevatedRole(role: Role): boolean {\n\treturn ELEVATED_PERMISSIONS.some((p) => role.permissions.has(p));\n}\n\n/**\n * Check if an array of permission names contains any elevated permissions\n */\nexport function hasElevatedPermissions(permissions: string[]): boolean {\n\treturn permissions.some((p) =>\n\t\tELEVATED_PERMISSIONS.includes(p as (typeof ELEVATED_PERMISSIONS)[number]),\n\t);\n}\n\n/**\n * Fetch the most recent matching audit log entry for an action.\n * Matches by target ID and filters to entries within 10 seconds.\n * Returns null gracefully on errors (rate limits, missing permissions, etc.)\n */\nexport async function fetchAuditEntry(\n\tguild: Guild,\n\tactionType: AuditLogEvent,\n\ttarget: string,\n\truntime: IAgentRuntime,\n): Promise<AuditInfo | null> {\n\ttry {\n\t\tconst logs = await guild.fetchAuditLogs({ type: actionType, limit: 5 });\n\t\tconst now = Date.now();\n\n\t\tfor (const entry of logs.entries.values()) {\n\t\t\t// Match by target and ensure entry is recent (within 10 seconds)\n\t\t\t// Type guard: entry.target can be various types, not all have 'id'\n\t\t\tconst targetId =\n\t\t\t\tentry.target && \"id\" in entry.target ? entry.target.id : undefined;\n\t\t\tif (targetId === target && now - entry.createdTimestamp < 10000) {\n\t\t\t\treturn {\n\t\t\t\t\texecutorId: entry.executor?.id ?? \"unknown\",\n\t\t\t\t\texecutorTag: entry.executor?.tag ?? \"Unknown\",\n\t\t\t\t\treason: entry.reason,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t} catch (err) {\n\t\t// Graceful degradation - rate limits, missing permissions, etc.\n\t\truntime.logger.debug(`Audit log fetch failed (non-critical): ${err}`);\n\t}\n\treturn null;\n}\n\n/**\n * Get the permission state from allow/deny arrays\n */\nfunction getState(\n\tperm: string,\n\tallow: string[],\n\tdeny: string[],\n): PermissionState {\n\tif (allow.includes(perm)) {\n\t\treturn \"ALLOW\";\n\t}\n\tif (deny.includes(perm)) {\n\t\treturn \"DENY\";\n\t}\n\treturn \"NEUTRAL\";\n}\n\n/**\n * Convert an overwrite to a list of changes (used for CREATE/DELETE)\n * For CREATE: from NEUTRAL to the actual state (ALLOW/DENY)\n * For DELETE: from the actual state (ALLOW/DENY) to NEUTRAL\n */\nfunction overwriteToChanges(\n\tow: PermissionOverwrites,\n\tisDelete: boolean = false,\n): PermissionDiff[] {\n\tconst changes: PermissionDiff[] = [];\n\tfor (const p of ow.allow.toArray()) {\n\t\tchanges.push({\n\t\t\tpermission: p,\n\t\t\toldState: isDelete ? \"ALLOW\" : \"NEUTRAL\",\n\t\t\tnewState: isDelete ? \"NEUTRAL\" : \"ALLOW\",\n\t\t});\n\t}\n\tfor (const p of ow.deny.toArray()) {\n\t\tchanges.push({\n\t\t\tpermission: p,\n\t\t\toldState: isDelete ? \"DENY\" : \"NEUTRAL\",\n\t\t\tnewState: isDelete ? \"NEUTRAL\" : \"DENY\",\n\t\t});\n\t}\n\treturn changes;\n}\n\n/**\n * Diff two permission overwrites and determine what changed.\n * Returns the list of changes and the action type (CREATE/UPDATE/DELETE).\n */\nexport function diffOverwrites(\n\toldOw: PermissionOverwrites | undefined | null,\n\tnewOw: PermissionOverwrites | undefined | null,\n): { changes: PermissionDiff[]; action: \"CREATE\" | \"UPDATE\" | \"DELETE\" } {\n\t// Both null - no change\n\tif (!oldOw && !newOw) {\n\t\treturn { changes: [], action: \"UPDATE\" };\n\t}\n\n\t// Created new overwrite\n\tif (!oldOw && newOw) {\n\t\treturn { changes: overwriteToChanges(newOw, false), action: \"CREATE\" };\n\t}\n\n\t// Deleted overwrite\n\tif (oldOw && !newOw) {\n\t\treturn { changes: overwriteToChanges(oldOw, true), action: \"DELETE\" };\n\t}\n\n\t// Updated overwrite - compare old and new\n\tconst changes: PermissionDiff[] = [];\n\tconst oldAllow = oldOw?.allow.toArray();\n\tconst oldDeny = oldOw?.deny.toArray();\n\tconst newAllow = newOw?.allow.toArray();\n\tconst newDeny = newOw?.deny.toArray();\n\n\t// Get all permissions that exist in either old or new\n\tconst allPerms = new Set([...oldAllow, ...oldDeny, ...newAllow, ...newDeny]);\n\n\tfor (const perm of allPerms) {\n\t\tconst oldState = getState(perm, oldAllow, oldDeny);\n\t\tconst newState = getState(perm, newAllow, newDeny);\n\t\tif (oldState !== newState) {\n\t\t\tchanges.push({ permission: perm, oldState, newState });\n\t\t}\n\t}\n\n\treturn { changes, action: \"UPDATE\" };\n}\n\n/**\n * Diff two role permission sets and return what changed.\n */\nexport function diffRolePermissions(\n\toldRole: Role,\n\tnewRole: Role,\n): PermissionDiff[] {\n\tconst oldPerms = oldRole.permissions.toArray();\n\tconst newPerms = newRole.permissions.toArray();\n\tconst changes: PermissionDiff[] = [];\n\n\t// Find added permissions\n\tfor (const p of newPerms) {\n\t\tif (!oldPerms.includes(p)) {\n\t\t\tchanges.push({ permission: p, oldState: \"NEUTRAL\", newState: \"ALLOW\" });\n\t\t}\n\t}\n\n\t// Find removed permissions\n\tfor (const p of oldPerms) {\n\t\tif (!newPerms.includes(p)) {\n\t\t\tchanges.push({ permission: p, oldState: \"ALLOW\", newState: \"NEUTRAL\" });\n\t\t}\n\t}\n\n\treturn changes;\n}\n\n/**\n * Diff member roles to find added and removed roles.\n */\nexport function diffMemberRoles(\n\toldMember: GuildMember,\n\tnewMember: GuildMember,\n): { added: Role[]; removed: Role[] } {\n\tconst oldRoles = oldMember.roles.cache;\n\tconst newRoles = newMember.roles.cache;\n\n\treturn {\n\t\tadded: [...newRoles.filter((r) => !oldRoles.has(r.id)).values()],\n\t\tremoved: [...oldRoles.filter((r) => !newRoles.has(r.id)).values()],\n\t};\n}\n",
|
|
48
|
+
"/**\n * Discord message history / spidering — extracted from service.ts\n *\n * Contains fetchChannelHistory(), getSpiderState(), saveSpiderState(),\n * buildMemoryFromMessage(), ensureConnectionsForMessages().\n */\nimport {\n\tChannelType,\n\ttype CustomMetadata,\n\tcreateUniqueUuid,\n\ttype Media,\n\ttype Memory,\n\tMemoryType,\n\tstringToUuid,\n\ttype UUID,\n} from \"@elizaos/core\";\nimport type { Channel, GuildTextBasedChannel, Message } from \"discord.js\";\nimport type { ICompatRuntime, WorldCompat } from \"./compat\";\nimport {\n\tbuildDiscordEntityMetadata,\n\tbuildDiscordWorldMetadata,\n} from \"./identity\";\nimport type { MessageManager } from \"./messages\";\nimport type { DiscordService } from \"./service\";\nimport type {\n\tChannelHistoryOptions,\n\tChannelHistoryResult,\n\tChannelSpiderState,\n} from \"./types\";\n\n/**\n * Subset of DiscordService fields needed by history functions.\n */\nexport interface HistoryServiceInternals {\n\tclient: NonNullable<DiscordService[\"client\"]>;\n\truntime: ICompatRuntime;\n\tmessageManager: MessageManager | undefined;\n\n\tresolveDiscordEntityId(userId: string): UUID;\n\tgetChannelType(channel: Channel): Promise<ChannelType>;\n\tisGuildTextBasedChannel(\n\t\tchannel: Channel | null,\n\t): channel is GuildTextBasedChannel;\n}\n\n/**\n * Helper to delay execution.\n */\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Get spider state for a channel from the database.\n */\nexport async function getSpiderState(\n\tservice: HistoryServiceInternals,\n\tchannelId: string,\n): Promise<ChannelSpiderState | null> {\n\ttry {\n\t\tconst stateId = createUniqueUuid(\n\t\t\tservice.runtime,\n\t\t\t`discord-spider-state-${channelId}`,\n\t\t);\n\n\t\tconst stateMemory = await service.runtime.getMemoryById(stateId);\n\n\t\tconst stateMemoryContent = stateMemory?.content;\n\t\tif (stateMemoryContent?.text) {\n\t\t\tconst state = JSON.parse(stateMemory.content.text) as ChannelSpiderState;\n\t\t\tservice.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\tchannelId,\n\t\t\t\t\tstate,\n\t\t\t\t},\n\t\t\t\t\"Loaded spider state from database\",\n\t\t\t);\n\t\t\treturn state;\n\t\t}\n\t} catch (error) {\n\t\tservice.runtime.logger.warn(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\tchannelId,\n\t\t\t},\n\t\t\t\"Failed to load spider state from database\",\n\t\t);\n\t}\n\treturn null;\n}\n\n/**\n * Save spider state for a channel to the database.\n */\nexport async function saveSpiderState(\n\tservice: HistoryServiceInternals,\n\tstate: ChannelSpiderState,\n): Promise<void> {\n\ttry {\n\t\tconst stateId = createUniqueUuid(\n\t\t\tservice.runtime,\n\t\t\t`discord-spider-state-${state.channelId}`,\n\t\t);\n\t\tconst roomId = createUniqueUuid(service.runtime, state.channelId);\n\n\t\tservice.runtime.logger.debug(\n\t\t\t`[SpiderState] Saving channel=${state.channelId} stateId=${stateId}`,\n\t\t);\n\n\t\tlet existing: Memory | null = null;\n\t\ttry {\n\t\t\texisting = await service.runtime.getMemoryById(stateId);\n\t\t\tservice.runtime.logger.debug(\n\t\t\t\t`[SpiderState] getMemoryById: ${existing ? \"EXISTS\" : \"NOT_FOUND\"}`,\n\t\t\t);\n\t\t} catch (lookupError) {\n\t\t\tconst lookupErrorMessage =\n\t\t\t\tlookupError instanceof Error\n\t\t\t\t\t? lookupError.message\n\t\t\t\t\t: String(lookupError);\n\t\t\tservice.runtime.logger.debug(\n\t\t\t\t`[SpiderState] getMemoryById error: ${lookupErrorMessage}`,\n\t\t\t);\n\t\t}\n\n\t\tif (existing) {\n\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\"[SpiderState] Deleting existing state before insert\",\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tawait service.runtime.deleteMemory(stateId);\n\t\t\t\tservice.runtime.logger.debug(\"[SpiderState] Delete successful\");\n\t\t\t} catch (deleteError) {\n\t\t\t\tconst deleteErrorMessage =\n\t\t\t\t\tdeleteError instanceof Error\n\t\t\t\t\t\t? deleteError.message\n\t\t\t\t\t\t: String(deleteError);\n\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t`[SpiderState] Delete error: ${deleteErrorMessage}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tlet serverId: string | undefined;\n\t\tlet worldId: UUID;\n\t\tlet channelName = state.channelId;\n\n\t\ttry {\n\t\t\tif (service.client?.isReady?.()) {\n\t\t\t\tconst channel = await service.client.channels.fetch(state.channelId);\n\t\t\t\tif (channel && \"guild\" in channel && channel.guild) {\n\t\t\t\t\tserverId = channel.guild.id;\n\t\t\t\t\tchannelName =\n\t\t\t\t\t\t\"name\" in channel\n\t\t\t\t\t\t\t? (channel.name ?? state.channelId)\n\t\t\t\t\t\t\t: state.channelId;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// If we can't fetch the channel, use a default serverId\n\t\t}\n\n\t\tworldId = createUniqueUuid(service.runtime, serverId ?? state.channelId);\n\n\t\tconst entityId = service.runtime.agentId;\n\t\ttry {\n\t\t\tconst entity = await service.runtime.getEntityById(entityId);\n\t\t\tif (!entity) {\n\t\t\t\tawait service.runtime.createEntity({\n\t\t\t\t\tid: entityId,\n\t\t\t\t\tnames: [\"Spider\"],\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\tmetadata: { source: \"discord-spider\" },\n\t\t\t\t});\n\t\t\t\tservice.runtime.logger.debug(\"[SpiderState] Created entity for agent\");\n\t\t\t}\n\t\t} catch (entityError) {\n\t\t\tconst entityErrorMessage =\n\t\t\t\tentityError instanceof Error\n\t\t\t\t\t? entityError.message\n\t\t\t\t\t: String(entityError);\n\t\t\tif (!entityErrorMessage.includes(\"duplicate key\")) {\n\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t`[SpiderState] Entity ensure error: ${entityErrorMessage}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tawait service.runtime.ensureWorldExists({\n\t\t\t\tid: worldId,\n\t\t\t\tname: serverId\n\t\t\t\t\t? `Discord Server ${serverId}`\n\t\t\t\t\t: `Spider World ${state.channelId}`,\n\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\tmessageServerId: stringToUuid(serverId ?? state.channelId),\n\t\t\t});\n\t\t\tservice.runtime.logger.debug(`[SpiderState] World ensured: ${worldId}`);\n\t\t} catch (worldError) {\n\t\t\tconst worldErrorMessage =\n\t\t\t\tworldError instanceof Error ? worldError.message : String(worldError);\n\t\t\tservice.runtime.logger.debug(\n\t\t\t\t`[SpiderState] World ensure error: ${worldErrorMessage}`,\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tawait service.runtime.ensureRoomExists({\n\t\t\t\tid: roomId,\n\t\t\t\tname: channelName,\n\t\t\t\tsource: \"discord\",\n\t\t\t\ttype: ChannelType.GROUP,\n\t\t\t\tchannelId: state.channelId,\n\t\t\t\tmessageServerId: stringToUuid(serverId ?? state.channelId),\n\t\t\t\tworldId,\n\t\t\t});\n\t\t\tservice.runtime.logger.debug(`[SpiderState] Room ensured: ${roomId}`);\n\t\t} catch (roomError) {\n\t\t\tconst roomErrorMessage =\n\t\t\t\troomError instanceof Error ? roomError.message : String(roomError);\n\t\t\tservice.runtime.logger.debug(\n\t\t\t\t`[SpiderState] Room ensure error: ${roomErrorMessage}`,\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tawait service.runtime.ensureParticipantInRoom(entityId, roomId);\n\t\t\tservice.runtime.logger.debug(\"[SpiderState] Participant ensured in room\");\n\t\t} catch (participantError) {\n\t\t\ttry {\n\t\t\t\tawait service.runtime.addParticipant(entityId, roomId);\n\t\t\t\tservice.runtime.logger.debug(\"[SpiderState] Participant added to room\");\n\t\t\t} catch {\n\t\t\t\tconst participantErrorMessage = participantError?.message\n\t\t\t\t\t? participantError.message\n\t\t\t\t\t: String(participantError);\n\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t`[SpiderState] Participant ensure error: ${participantErrorMessage}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst stateMemory: Memory = {\n\t\t\tid: stateId,\n\t\t\tagentId: service.runtime.agentId,\n\t\t\tentityId,\n\t\t\troomId,\n\t\t\tcontent: {\n\t\t\t\ttext: JSON.stringify(state),\n\t\t\t\tsource: \"discord-spider\",\n\t\t\t},\n\t\t\tmetadata: {\n\t\t\t\ttype: MemoryType.CUSTOM,\n\t\t\t\tsource: \"discord-spider-state\",\n\t\t\t\tchannelId: state.channelId,\n\t\t\t\tfullyBackfilled: state.fullyBackfilled,\n\t\t\t} satisfies CustomMetadata,\n\t\t\tcreatedAt: Date.now(),\n\t\t};\n\n\t\tservice.runtime.logger.debug(\"[SpiderState] Inserting new state\");\n\t\tawait service.runtime.createMemory(stateMemory, \"custom\");\n\n\t\tservice.runtime.logger.debug(\n\t\t\t`[SpiderState] Save successful for channel ${state.channelId}`,\n\t\t);\n\t} catch (error) {\n\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\tconst errorCause =\n\t\t\terror &&\n\t\t\t(\n\t\t\t\terror as {\n\t\t\t\t\tcause?: { message?: string; code?: string; detail?: string };\n\t\t\t\t}\n\t\t\t).cause;\n\t\tconst causeMsg =\n\t\t\terrorCause?.message || (errorCause ? String(errorCause) : \"\");\n\t\tconst causeCode = errorCause?.code || \"\";\n\t\tconst causeDetail = errorCause?.detail || \"\";\n\n\t\tif (\n\t\t\terrorMsg.includes(\"duplicate key\") ||\n\t\t\terrorMsg.includes(\"unique constraint\") ||\n\t\t\tString(causeMsg).includes(\"duplicate key\") ||\n\t\t\tString(causeMsg).includes(\"unique constraint\")\n\t\t) {\n\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\"[SpiderState] Duplicate key - state already saved by another operation\",\n\t\t\t);\n\t\t} else {\n\t\t\tservice.runtime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\terror: errorMsg,\n\t\t\t\t\tcause: String(causeMsg),\n\t\t\t\t\tcauseCode,\n\t\t\t\t\tcauseDetail,\n\t\t\t\t\tchannelId: state.channelId,\n\t\t\t\t},\n\t\t\t\t\"Failed to save spider state to database\",\n\t\t\t);\n\t\t}\n\t}\n}\n\n/**\n * Builds a Memory object from a Discord Message.\n */\nexport async function buildMemoryFromMessage(\n\tservice: HistoryServiceInternals,\n\tmessage: Message,\n\toptions?: {\n\t\tprocessedContent?: string;\n\t\tprocessedAttachments?: Media[];\n\t\textraContent?: Record<string, unknown>;\n\t\textraMetadata?: Record<string, unknown>;\n\t},\n): Promise<Memory | null> {\n\tif (!message.author || !message.channel) {\n\t\treturn null;\n\t}\n\n\tconst entityId = service.resolveDiscordEntityId(message.author.id);\n\tconst roomId = createUniqueUuid(service.runtime, message.channel.id);\n\tconst channel = message.channel;\n\tconst channelType = await service.getChannelType(channel as Channel);\n\tconst channelGuild = \"guild\" in channel ? channel.guild : null;\n\tconst serverId = channelGuild?.id\n\t\t? channelGuild.id\n\t\t: (message.guild?.id ?? message.channel.id);\n\tconst worldId = serverId\n\t\t? createUniqueUuid(service.runtime, serverId)\n\t\t: service.runtime.agentId;\n\n\tlet textContent: string;\n\tlet attachments: Media[];\n\n\tconst optionsProcessedContent = options?.processedContent;\n\tconst optionsProcessedAttachments = options?.processedAttachments;\n\tif (\n\t\toptionsProcessedContent !== undefined ||\n\t\toptionsProcessedAttachments !== undefined\n\t) {\n\t\ttextContent = options.processedContent || \" \";\n\t\tattachments = options.processedAttachments || [];\n\t} else {\n\t\tconst processed = service.messageManager\n\t\t\t? await service.messageManager.processMessage(message)\n\t\t\t: { processedContent: message.content, attachments: [] };\n\n\t\tconst processedContent = processed?.processedContent;\n\t\ttextContent =\n\t\t\tprocessedContent && processedContent.trim().length > 0\n\t\t\t\t? processedContent\n\t\t\t\t: message.content || \" \";\n\t\tattachments = processed?.attachments ?? [];\n\t}\n\n\tconst entityName =\n\t\t(message.member &&\n\t\t\"displayName\" in message.member &&\n\t\ttypeof message.member.displayName === \"string\"\n\t\t\t? message.member.displayName\n\t\t\t: undefined) ??\n\t\t(\"globalName\" in message.author &&\n\t\ttypeof message.author.globalName === \"string\"\n\t\t\t? message.author.globalName\n\t\t\t: undefined) ??\n\t\tmessage.author.username;\n\tconst discordServerId = (() => {\n\t\tconst messageChannelGuild =\n\t\t\t\"guild\" in message.channel ? message.channel.guild : null;\n\t\treturn messageChannelGuild?.id || message.guild?.id || undefined;\n\t})();\n\tconst metadata = {\n\t\ttype: \"message\" as const,\n\t\tsource: \"discord\",\n\t\tprovider: \"discord\",\n\t\ttimestamp: message.createdTimestamp ?? Date.now(),\n\t\tentityName,\n\t\tentityUserName: message.author.username,\n\t\tentityAvatarUrl: message.author.displayAvatarURL(),\n\t\tfromBot: message.author.bot,\n\t\tfromId: message.author.id,\n\t\tsourceId: entityId,\n\t\tchatType: channelType,\n\t\tmessageIdFull: message.id,\n\t\tsender: {\n\t\t\tid: message.author.id,\n\t\t\tname: entityName,\n\t\t\tusername: message.author.username,\n\t\t},\n\t\tdiscord: {\n\t\t\tid: message.author.id,\n\t\t\tuserId: message.author.id,\n\t\t\tusername: message.author.username,\n\t\t\tuserName: message.author.username,\n\t\t\tname: entityName,\n\t\t\tmessageId: message.id,\n\t\t\tchannelId: message.channel.id,\n\t\t\tguildId: discordServerId,\n\t\t},\n\t\tdiscordMessageId: message.id,\n\t\tdiscordChannelId: message.channel.id,\n\t\tdiscordServerId,\n\t\ttags: [] as string[],\n\t\t...(options?.extraMetadata ? options.extraMetadata : {}),\n\t};\n\n\tconst memory: Memory = {\n\t\tid: createUniqueUuid(service.runtime, message.id),\n\t\tentityId,\n\t\tagentId: service.runtime.agentId,\n\t\troomId,\n\t\tcontent: {\n\t\t\ttext: textContent || \" \",\n\t\t\tattachments,\n\t\t\tsource: \"discord\",\n\t\t\tchannelType,\n\t\t\turl: message.url,\n\t\t\tinReplyTo: message.reference?.messageId\n\t\t\t\t? createUniqueUuid(service.runtime, message.reference.messageId)\n\t\t\t\t: undefined,\n\t\t\t...(options?.extraContent ? options.extraContent : {}),\n\t\t},\n\t\tmetadata: metadata as unknown as Memory[\"metadata\"],\n\t\tcreatedAt: message.createdTimestamp ?? Date.now(),\n\t\tworldId,\n\t};\n\n\treturn memory;\n}\n\n/**\n * Ensures entity connections exist for a batch of Discord messages using batch API.\n */\nexport async function ensureConnectionsForMessages(\n\tservice: HistoryServiceInternals,\n\tmessages: Message[],\n\tensuredEntityIds: Set<string> = new Set(),\n): Promise<void> {\n\tif (messages.length === 0) {\n\t\treturn;\n\t}\n\n\tconst uniqueAuthors = new Map<string, Message>();\n\tfor (const message of messages) {\n\t\tif (message.author && !ensuredEntityIds.has(message.author.id)) {\n\t\t\tuniqueAuthors.set(message.author.id, message);\n\t\t}\n\t}\n\n\tif (uniqueAuthors.size === 0) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tconst firstMessage = messages[0];\n\t\tconst channelType = await service.getChannelType(\n\t\t\tfirstMessage.channel as Channel,\n\t\t);\n\t\tconst firstMessageChannelGuild =\n\t\t\t\"guild\" in firstMessage.channel ? firstMessage.channel.guild : null;\n\t\tconst serverId = firstMessageChannelGuild?.id\n\t\t\t? firstMessageChannelGuild.id\n\t\t\t: (firstMessage.guild?.id ?? firstMessage.channel.id);\n\t\tconst worldId = serverId\n\t\t\t? createUniqueUuid(service.runtime, serverId)\n\t\t\t: service.runtime.agentId;\n\n\t\tconst entities = Array.from(uniqueAuthors.entries()).map(\n\t\t\t([authorId, message]) => {\n\t\t\t\tconst userName = message.author.username;\n\t\t\t\tconst name =\n\t\t\t\t\t(message.member &&\n\t\t\t\t\t\"displayName\" in message.member &&\n\t\t\t\t\ttypeof message.member.displayName === \"string\"\n\t\t\t\t\t\t? message.member.displayName\n\t\t\t\t\t\t: undefined) ??\n\t\t\t\t\t(\"globalName\" in message.author &&\n\t\t\t\t\ttypeof message.author.globalName === \"string\"\n\t\t\t\t\t\t? message.author.globalName\n\t\t\t\t\t\t: undefined) ??\n\t\t\t\t\tuserName;\n\t\t\t\treturn {\n\t\t\t\t\tid: service.resolveDiscordEntityId(authorId),\n\t\t\t\t\tnames: [userName, name].filter(\n\t\t\t\t\t\t(n): n is string => typeof n === \"string\" && n.length > 0,\n\t\t\t\t\t),\n\t\t\t\t\tmetadata: buildDiscordEntityMetadata(\n\t\t\t\t\t\tauthorId,\n\t\t\t\t\t\tuserName,\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\tmessage.author.displayAvatarURL(),\n\t\t\t\t\t),\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t};\n\t\t\t},\n\t\t);\n\n\t\tconst rooms = [\n\t\t\t{\n\t\t\t\tid: createUniqueUuid(service.runtime, firstMessage.channel.id),\n\t\t\t\tchannelId: firstMessage.channel.id,\n\t\t\t\ttype: channelType,\n\t\t\t\tsource: \"discord\",\n\t\t\t},\n\t\t];\n\n\t\tconst world: WorldCompat = {\n\t\t\tid: worldId,\n\t\t\tmessageServerId: stringToUuid(serverId),\n\t\t\tname: firstMessage.guild?.name ?? `DM-${firstMessage.channel.id}`,\n\t\t\tagentId: service.runtime.agentId,\n\t\t\tmetadata: buildDiscordWorldMetadata(\n\t\t\t\tservice.runtime,\n\t\t\t\tfirstMessageChannelGuild?.ownerId ??\n\t\t\t\t\tfirstMessage.guild?.ownerId ??\n\t\t\t\t\tundefined,\n\t\t\t),\n\t\t};\n\n\t\tawait service.runtime.ensureConnections(entities, rooms, \"discord\", world);\n\n\t\tfor (const authorId of uniqueAuthors.keys()) {\n\t\t\tensuredEntityIds.add(authorId);\n\t\t}\n\t} catch (error) {\n\t\tservice.runtime.logger.debug(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\tauthorCount: uniqueAuthors.size,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t},\n\t\t\t\"Failed to ensure batch connections for message authors during history fetch\",\n\t\t);\n\t}\n}\n\n/**\n * Fetches and persists message history from a Discord channel.\n */\nexport async function fetchChannelHistory(\n\tservice: HistoryServiceInternals,\n\tchannelId: string,\n\toptions: ChannelHistoryOptions = {},\n): Promise<ChannelHistoryResult> {\n\tif (!service.client?.isReady?.()) {\n\t\tservice.runtime.logger.warn(\n\t\t\t{ src: \"plugin:discord\", agentId: service.runtime.agentId, channelId },\n\t\t\t\"Discord client not ready for history fetch\",\n\t\t);\n\t\treturn {\n\t\t\tmessages: [],\n\t\t\tstats: { fetched: 0, stored: 0, pages: 0, fullyBackfilled: false },\n\t\t};\n\t}\n\n\tconst fetchedChannel = await service.client.channels.fetch(channelId);\n\tif (!service.isGuildTextBasedChannel(fetchedChannel)) {\n\t\tservice.runtime.logger.warn(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\tchannelId,\n\t\t\t\tchannelType: fetchedChannel?.type ?? null,\n\t\t\t},\n\t\t\t\"Channel is not a guild text-based channel\",\n\t\t);\n\t\treturn {\n\t\t\tmessages: [],\n\t\t\tstats: { fetched: 0, stored: 0, pages: 0, fullyBackfilled: false },\n\t\t};\n\t}\n\n\tconst channel = fetchedChannel as GuildTextBasedChannel;\n\tconst serverId =\n\t\t\"guild\" in channel && channel.guild\n\t\t\t? channel.guild.id\n\t\t\t: \"guildId\" in channel && channel.guildId\n\t\t\t\t? channel.guildId\n\t\t\t\t: channel.id;\n\tconst worldId = serverId\n\t\t? createUniqueUuid(service.runtime, serverId)\n\t\t: service.runtime.agentId;\n\n\tawait service.runtime.ensureWorldExists({\n\t\tid: worldId,\n\t\tagentId: service.runtime.agentId,\n\t\tmessageServerId: stringToUuid(serverId),\n\t\tname: (() => {\n\t\t\tconst channelGuild = \"guild\" in channel ? channel.guild : null;\n\t\t\treturn channelGuild?.name || \"Discord\";\n\t\t})(),\n\t});\n\n\tawait service.runtime.ensureRoomExists({\n\t\tid: createUniqueUuid(service.runtime, channel.id),\n\t\tagentId: service.runtime.agentId,\n\t\tname: (\"name\" in channel && channel.name) || channel.id,\n\t\tsource: \"discord\",\n\t\ttype: await service.getChannelType(channel as Channel),\n\t\tchannelId: channel.id,\n\t\tmessageServerId: stringToUuid(serverId),\n\t\tworldId,\n\t});\n\n\t// Load spider state\n\tconst spiderState = options.force\n\t\t? null\n\t\t: await getSpiderState(service, channelId);\n\tconst channelName = (\"name\" in channel && channel.name) || channelId;\n\n\tlet consecutiveNoNew = 0;\n\tlet totalStored = 0;\n\tlet totalFetched = 0;\n\tlet pagesProcessed = 0;\n\tconst allMessages: Memory[] = [];\n\tconst startTime = Date.now();\n\tconst ensuredEntityIds = new Set<string>();\n\n\tlet oldestMessageId: string | undefined =\n\t\tspiderState?.oldestMessageId ?? options.before;\n\tlet newestMessageId: string | undefined =\n\t\tspiderState?.newestMessageId ?? options.after;\n\tlet oldestMessageTimestamp: number | undefined =\n\t\tspiderState?.oldestMessageTimestamp;\n\tlet newestMessageTimestamp: number | undefined =\n\t\tspiderState?.newestMessageTimestamp;\n\tlet reachedEnd = false;\n\n\t// Phase 1: Catch up on new messages\n\tif (!options.force && spiderState && spiderState.newestMessageId) {\n\t\tconst lastDate = spiderState.newestMessageTimestamp\n\t\t\t? new Date(spiderState.newestMessageTimestamp).toISOString().split(\"T\")[0]\n\t\t\t: \"unknown\";\n\t\tservice.runtime.logger.info(\n\t\t\t`#${channelName}: Catching up on new messages since ${lastDate}`,\n\t\t);\n\n\t\tconst catchUpBatches: Message[][] = [];\n\t\tlet catchUpBefore: string | undefined;\n\t\tlet _catchUpPages = 0;\n\t\tlet reachedKnownHistory = false;\n\n\t\twhile (!reachedKnownHistory) {\n\t\t\t_catchUpPages++;\n\t\t\tconst fetchParams: { limit: number; before?: string } = { limit: 100 };\n\t\t\tif (catchUpBefore) {\n\t\t\t\tfetchParams.before = catchUpBefore;\n\t\t\t}\n\n\t\t\tconst batch = await channel.messages.fetch(fetchParams);\n\t\t\tif (batch.size === 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst messages = Array.from(\n\t\t\t\tbatch.values() as IterableIterator<Message>,\n\t\t\t).sort((a, b) => (a.createdTimestamp ?? 0) - (b.createdTimestamp ?? 0));\n\n\t\t\tconst knownNewestTimestamp = spiderState.newestMessageTimestamp ?? 0;\n\t\t\tconst knownNewestId = spiderState.newestMessageId;\n\t\t\tconst filteredMessages: Message[] = [];\n\t\t\tfor (const msg of messages) {\n\t\t\t\tconst msgTimestamp = msg.createdTimestamp ?? 0;\n\t\t\t\tif (msgTimestamp > knownNewestTimestamp) {\n\t\t\t\t\tfilteredMessages.push(msg);\n\t\t\t\t} else if (\n\t\t\t\t\tmsgTimestamp === knownNewestTimestamp &&\n\t\t\t\t\tmsg.id !== knownNewestId\n\t\t\t\t) {\n\t\t\t\t\tfilteredMessages.push(msg);\n\t\t\t\t} else {\n\t\t\t\t\treachedKnownHistory = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (filteredMessages.length > 0) {\n\t\t\t\tcatchUpBatches.push(filteredMessages);\n\t\t\t}\n\n\t\t\tif (batch.size < 100 || reachedKnownHistory) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst batchLast = batch.last();\n\t\t\tcatchUpBefore = batchLast?.id;\n\t\t\tawait delay(250);\n\t\t}\n\n\t\tcatchUpBatches.reverse();\n\n\t\tlet catchUpBatchIndex = 0;\n\t\tfor (let messages of catchUpBatches) {\n\t\t\tcatchUpBatchIndex++;\n\n\t\t\tif (options.limit) {\n\t\t\t\tconst remaining = options.limit - totalFetched;\n\t\t\t\tif (remaining <= 0) {\n\t\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\t\tchannelId,\n\t\t\t\t\t\t\tlimit: options.limit,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Reached fetch limit during catch-up\",\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (messages.length > remaining) {\n\t\t\t\t\tmessages = messages.slice(0, remaining);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttotalFetched += messages.length;\n\t\t\tpagesProcessed++;\n\n\t\t\tif (messages.length > 0) {\n\t\t\t\tconst lastMsg = messages[messages.length - 1];\n\t\t\t\tconst lastTimestamp = lastMsg.createdTimestamp ?? 0;\n\t\t\t\tif (!newestMessageTimestamp || lastTimestamp > newestMessageTimestamp) {\n\t\t\t\t\tnewestMessageId = lastMsg.id;\n\t\t\t\t\tnewestMessageTimestamp = lastTimestamp;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet catchUpNewCount = 0;\n\t\t\tlet catchUpExistingCount = 0;\n\t\t\tconst catchUpBatchMemories: Memory[] = [];\n\n\t\t\tconst allMemories: Memory[] = [];\n\t\t\tfor (const discordMessage of messages) {\n\t\t\t\tconst memory = await buildMemoryFromMessage(service, discordMessage);\n\t\t\t\tif (memory?.id) {\n\t\t\t\t\tallMemories.push(memory);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (allMemories.length > 0) {\n\t\t\t\tconst memoryIds = allMemories\n\t\t\t\t\t.map((m) => m.id)\n\t\t\t\t\t.filter((id): id is UUID => id !== undefined);\n\t\t\t\tconst existingMemories = await service.runtime.getMemoriesByIds(\n\t\t\t\t\tmemoryIds,\n\t\t\t\t\t\"messages\",\n\t\t\t\t);\n\t\t\t\tconst existingIdSet = new Set(existingMemories.map((m) => m.id));\n\n\t\t\t\tfor (const memory of allMemories) {\n\t\t\t\t\tif (memory.id && existingIdSet.has(memory.id)) {\n\t\t\t\t\t\tcatchUpExistingCount++;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcatchUpNewCount++;\n\t\t\t\t\t\tcatchUpBatchMemories.push(memory);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options.onBatch) {\n\t\t\t\tconst shouldContinue = await options.onBatch(catchUpBatchMemories, {\n\t\t\t\t\tpage: pagesProcessed,\n\t\t\t\t\ttotalFetched,\n\t\t\t\t\ttotalStored: totalStored + catchUpBatchMemories.length,\n\t\t\t\t});\n\n\t\t\t\ttotalStored += catchUpBatchMemories.length;\n\n\t\t\t\tif (shouldContinue === false) {\n\t\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\t\tchannelId,\n\t\t\t\t\t\t\tpage: pagesProcessed,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Batch handler requested early stop during catch-up\",\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tawait ensureConnectionsForMessages(service, messages, ensuredEntityIds);\n\n\t\t\t\tconst successfullyPersisted: Memory[] = [];\n\t\t\t\tfor (const memory of catchUpBatchMemories) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait service.runtime.createMemory(memory, \"messages\");\n\t\t\t\t\t\tsuccessfullyPersisted.push(memory);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tservice.runtime.logger.warn(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\t\t\tmemoryId: memory.id,\n\t\t\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"Failed to persist memory during catch-up\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tallMessages.push(...successfullyPersisted);\n\t\t\t\ttotalStored += successfullyPersisted.length;\n\t\t\t}\n\n\t\t\tconst catchUpHitMiss =\n\t\t\t\tcatchUpExistingCount > 0 && catchUpNewCount === 0\n\t\t\t\t\t? \"HIT\"\n\t\t\t\t\t: catchUpNewCount > 0\n\t\t\t\t\t\t? \"MISS\"\n\t\t\t\t\t\t: \"EMPTY\";\n\n\t\t\tawait saveSpiderState(service, {\n\t\t\t\tchannelId,\n\t\t\t\toldestMessageId,\n\t\t\t\tnewestMessageId,\n\t\t\t\toldestMessageTimestamp,\n\t\t\t\tnewestMessageTimestamp,\n\t\t\t\tlastSpideredAt: Date.now(),\n\t\t\t\tfullyBackfilled: spiderState.fullyBackfilled,\n\t\t\t});\n\n\t\t\tconst newestDate = newestMessageTimestamp\n\t\t\t\t? new Date(newestMessageTimestamp).toISOString().split(\"T\")[0]\n\t\t\t\t: \"?\";\n\t\t\tconst elapsedSec = ((Date.now() - startTime) / 1000).toFixed(1);\n\t\t\tservice.runtime.logger.debug(\n\t\t\t\t`#${channelName}: Catch-up batch ${catchUpBatchIndex}/${catchUpBatches.length} [${catchUpHitMiss}], ${messages.length} msgs fetched (${catchUpNewCount} new, ${catchUpExistingCount} existing), ${totalFetched} total fetched, ${totalStored} total stored, newest date ${newestDate} (${elapsedSec}s)`,\n\t\t\t);\n\t\t}\n\n\t\tif (catchUpBatches.length > 0) {\n\t\t\tservice.runtime.logger.info(\n\t\t\t\t`#${channelName}: Caught up ${catchUpBatches.length} batches of new messages`,\n\t\t\t);\n\t\t}\n\t}\n\n\t// Phase 2: Determine backfill direction\n\tlet before: string | undefined = options.before;\n\tlet after: string | undefined = options.after;\n\n\tif (!options.force && spiderState) {\n\t\tif (spiderState.fullyBackfilled) {\n\t\t\treachedEnd = true;\n\t\t} else {\n\t\t\tbefore = spiderState.oldestMessageId;\n\t\t\tconst oldestDate = spiderState.oldestMessageTimestamp\n\t\t\t\t? new Date(spiderState.oldestMessageTimestamp)\n\t\t\t\t\t\t.toISOString()\n\t\t\t\t\t\t.split(\"T\")[0]\n\t\t\t\t: \"unknown\";\n\t\t\tservice.runtime.logger.info(\n\t\t\t\t`#${channelName}: Resuming backfill from ${oldestDate}`,\n\t\t\t);\n\t\t}\n\t} else if (!spiderState) {\n\t\tservice.runtime.logger.info(\n\t\t\t`#${channelName}: Starting fresh history fetch`,\n\t\t);\n\t}\n\n\t// Phase 3: Backfill older messages\n\twhile (!reachedEnd) {\n\t\tif (options.limit && totalFetched >= options.limit) {\n\t\t\tservice.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\tchannelId,\n\t\t\t\t\tlimit: options.limit,\n\t\t\t\t},\n\t\t\t\t\"Reached fetch limit before backfill batch\",\n\t\t\t);\n\t\t\tbreak;\n\t\t}\n\n\t\tpagesProcessed += 1;\n\t\tconst remaining = options.limit ? options.limit - totalFetched : 100;\n\t\tconst fetchLimit = Math.min(100, remaining);\n\t\tconst fetchParams: Record<string, unknown> = { limit: fetchLimit };\n\n\t\tif (after) {\n\t\t\tfetchParams.after = after;\n\t\t} else if (before) {\n\t\t\tfetchParams.before = before;\n\t\t}\n\n\t\tconst batch = await channel.messages.fetch(fetchParams);\n\t\tif (batch.size === 0) {\n\t\t\treachedEnd = true;\n\t\t\tbreak;\n\t\t}\n\n\t\tconst messages = Array.from(batch.values()).sort(\n\t\t\t(a, b) => (a.createdTimestamp ?? 0) - (b.createdTimestamp ?? 0),\n\t\t);\n\t\ttotalFetched += messages.length;\n\n\t\tif (messages.length > 0) {\n\t\t\tconst firstMsg = messages[0];\n\t\t\tconst lastMsg = messages[messages.length - 1];\n\t\t\tconst firstTimestamp = firstMsg.createdTimestamp ?? 0;\n\t\t\tconst lastTimestamp = lastMsg.createdTimestamp ?? 0;\n\n\t\t\tif (!oldestMessageTimestamp || firstTimestamp < oldestMessageTimestamp) {\n\t\t\t\toldestMessageId = firstMsg.id;\n\t\t\t\toldestMessageTimestamp = firstTimestamp;\n\t\t\t}\n\n\t\t\tif (!newestMessageTimestamp || lastTimestamp > newestMessageTimestamp) {\n\t\t\t\tnewestMessageId = lastMsg.id;\n\t\t\t\tnewestMessageTimestamp = lastTimestamp;\n\t\t\t}\n\t\t}\n\n\t\tconst batchMemories: Memory[] = [];\n\t\tlet newCount = 0;\n\t\tlet existingCount = 0;\n\n\t\tconst allMemories: Memory[] = [];\n\t\tfor (const discordMessage of messages) {\n\t\t\tconst memory = await buildMemoryFromMessage(service, discordMessage);\n\t\t\tif (memory?.id) {\n\t\t\t\tallMemories.push(memory);\n\t\t\t}\n\t\t}\n\n\t\tif (allMemories.length > 0) {\n\t\t\tconst memoryIds = allMemories\n\t\t\t\t.map((m) => m.id)\n\t\t\t\t.filter((id): id is UUID => id !== undefined);\n\t\t\tconst existingMemories = await service.runtime.getMemoriesByIds(\n\t\t\t\tmemoryIds,\n\t\t\t\t\"messages\",\n\t\t\t);\n\t\t\tconst existingIdSet = new Set(existingMemories.map((m) => m.id));\n\n\t\t\tfor (const memory of allMemories) {\n\t\t\t\tif (memory.id && existingIdSet.has(memory.id)) {\n\t\t\t\t\texistingCount++;\n\t\t\t\t} else {\n\t\t\t\t\tnewCount++;\n\t\t\t\t\tbatchMemories.push(memory);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst hitMiss =\n\t\t\texistingCount > 0 && newCount === 0\n\t\t\t\t? \"HIT\"\n\t\t\t\t: newCount > 0\n\t\t\t\t\t? \"MISS\"\n\t\t\t\t\t: \"EMPTY\";\n\n\t\tif (options.onBatch) {\n\t\t\tconst shouldContinue = await options.onBatch(batchMemories, {\n\t\t\t\tpage: pagesProcessed,\n\t\t\t\ttotalFetched,\n\t\t\t\ttotalStored: totalStored + batchMemories.length,\n\t\t\t});\n\n\t\t\ttotalStored += batchMemories.length;\n\n\t\t\tif (shouldContinue === false) {\n\t\t\t\tservice.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\tchannelId,\n\t\t\t\t\t\tpage: pagesProcessed,\n\t\t\t\t\t},\n\t\t\t\t\t\"Batch handler requested early stop\",\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else {\n\t\t\tawait ensureConnectionsForMessages(service, messages, ensuredEntityIds);\n\n\t\t\tconst successfullyPersisted: Memory[] = [];\n\t\t\tfor (const memory of batchMemories) {\n\t\t\t\ttry {\n\t\t\t\t\tawait service.runtime.createMemory(memory, \"messages\");\n\t\t\t\t\tsuccessfullyPersisted.push(memory);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tservice.runtime.logger.warn(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\t\tmemoryId: memory.id,\n\t\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Failed to persist memory during backfill\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tallMessages.push(...successfullyPersisted);\n\t\t\ttotalStored += successfullyPersisted.length;\n\t\t}\n\t\tconsecutiveNoNew = batchMemories.length === 0 ? consecutiveNoNew + 1 : 0;\n\n\t\tconst incrementalState: ChannelSpiderState = {\n\t\t\tchannelId,\n\t\t\toldestMessageId,\n\t\t\tnewestMessageId,\n\t\t\toldestMessageTimestamp,\n\t\t\tnewestMessageTimestamp,\n\t\t\tlastSpideredAt: Date.now(),\n\t\t\tfullyBackfilled: false,\n\t\t};\n\t\tawait saveSpiderState(service, incrementalState);\n\n\t\tconst oldestDate = oldestMessageTimestamp\n\t\t\t? new Date(oldestMessageTimestamp).toISOString().split(\"T\")[0]\n\t\t\t: \"?\";\n\t\tconst newestDate = newestMessageTimestamp\n\t\t\t? new Date(newestMessageTimestamp).toISOString().split(\"T\")[0]\n\t\t\t: \"?\";\n\t\tconst elapsedSec = ((Date.now() - startTime) / 1000).toFixed(1);\n\t\tservice.runtime.logger.debug(\n\t\t\t`#${channelName}: Page ${pagesProcessed} [${hitMiss}], ${messages.length} msgs fetched (${newCount} new, ${existingCount} existing), ${batchMemories.length} stored, ${totalFetched} total fetched, ${totalStored} total stored, dates ${oldestDate} to ${newestDate} (${elapsedSec}s)`,\n\t\t);\n\n\t\tif (pagesProcessed === 1 || pagesProcessed % 10 === 0) {\n\t\t\tservice.runtime.logger.info(\n\t\t\t\t`#${channelName}: Page ${pagesProcessed}, ${totalFetched} msgs fetched, ${totalStored} stored, dates ${oldestDate} to ${newestDate} (${elapsedSec}s)`,\n\t\t\t);\n\t\t}\n\n\t\tservice.runtime.logger.debug(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\tchannelId,\n\t\t\t\tbatchSize: batch.size,\n\t\t\t\tstoredThisBatch: batchMemories.length,\n\t\t\t\ttotalStored,\n\t\t\t\ttotalFetched,\n\t\t\t\tpage: pagesProcessed,\n\t\t\t},\n\t\t\t\"Processed channel history batch\",\n\t\t);\n\n\t\tif (options.limit && totalFetched >= options.limit) {\n\t\t\tservice.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\tchannelId,\n\t\t\t\t\tlimit: options.limit,\n\t\t\t\t},\n\t\t\t\t\"Reached fetch limit\",\n\t\t\t);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (batch.size < 100) {\n\t\t\treachedEnd = true;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (consecutiveNoNew >= 3) {\n\t\t\tservice.runtime.logger.debug(\n\t\t\t\t{ src: \"plugin:discord\", agentId: service.runtime.agentId, channelId },\n\t\t\t\t\"Stopping backfill: 3 consecutive pages of existing messages (will resume from oldest on next run)\",\n\t\t\t);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (after) {\n\t\t\tconst lastMessage = messages[messages.length - 1];\n\t\t\tafter = lastMessage?.id;\n\t\t} else {\n\t\t\tconst firstMessage = messages[0];\n\t\t\tbefore = firstMessage?.id;\n\t\t}\n\n\t\tawait delay(250);\n\t}\n\n\t// Update spider state\n\tconst newState: ChannelSpiderState = {\n\t\tchannelId,\n\t\toldestMessageId,\n\t\tnewestMessageId,\n\t\toldestMessageTimestamp,\n\t\tnewestMessageTimestamp,\n\t\tlastSpideredAt: Date.now(),\n\t\tfullyBackfilled: spiderState?.fullyBackfilled || (reachedEnd && !after),\n\t};\n\tawait saveSpiderState(service, newState);\n\n\tconst elapsedSec = ((Date.now() - startTime) / 1000).toFixed(1);\n\tconst dateRange =\n\t\toldestMessageTimestamp && newestMessageTimestamp\n\t\t\t? `${new Date(oldestMessageTimestamp).toISOString().split(\"T\")[0]} to ${new Date(newestMessageTimestamp).toISOString().split(\"T\")[0]}`\n\t\t\t: \"no messages\";\n\tconst status = newState.fullyBackfilled ? \"✓ complete\" : \"↻ partial\";\n\tservice.runtime.logger.info(\n\t\t`#${channelName}: ${status} - ${totalFetched} msgs, ${pagesProcessed} pages, ${dateRange} (${elapsedSec}s)`,\n\t);\n\n\treturn {\n\t\tmessages: allMessages,\n\t\tstats: {\n\t\t\tfetched: totalFetched,\n\t\t\tstored: totalStored,\n\t\t\tpages: pagesProcessed,\n\t\t\tfullyBackfilled: newState.fullyBackfilled,\n\t\t},\n\t};\n}\n",
|
|
49
|
+
"/**\n * Discord reaction handling — extracted from service.ts\n *\n * Contains handleReaction(), handleReactionAdd(), handleReactionRemove().\n */\nimport {\n\ttype ChannelType,\n\tcreateUniqueUuid,\n\ttype EventPayload,\n\tEventType,\n\ttype HandlerCallback,\n\ttype Memory,\n\tstringToUuid,\n\ttype UUID,\n} from \"@elizaos/core\";\nimport type {\n\tChannel,\n\tMessageReaction,\n\tPartialMessageReaction,\n\tPartialUser,\n\tTextChannel,\n\tUser,\n} from \"discord.js\";\nimport { buildDiscordWorldMetadata } from \"./identity\";\nimport type { DiscordService } from \"./service\";\nimport { DiscordEventTypes, type DiscordReactionPayload } from \"./types\";\nimport { normalizeDiscordMessageText } from \"./utils\";\n\n/**\n * Subset of DiscordService fields needed by reaction handlers.\n */\nexport interface ReactionServiceInternals {\n\truntime: DiscordService[\"runtime\"];\n\tresolveDiscordEntityId(userId: string): UUID;\n\tgetChannelType(channel: Channel): Promise<ChannelType>;\n}\n\n/**\n * Generic handler for reaction events (add/remove).\n */\nexport async function handleReaction(\n\tservice: ReactionServiceInternals,\n\treaction: MessageReaction | PartialMessageReaction,\n\tuser: User | PartialUser,\n\ttype: \"add\" | \"remove\",\n): Promise<void> {\n\ttry {\n\t\tconst actionVerb = type === \"add\" ? \"added\" : \"removed\";\n\t\tconst actionText = type === \"add\" ? \"Added\" : \"Removed\";\n\t\tconst preposition = type === \"add\" ? \"to\" : \"from\";\n\n\t\tservice.runtime.logger.debug(\n\t\t\t{ src: \"plugin:discord\", agentId: service.runtime.agentId, type },\n\t\t\t`Reaction ${actionVerb}`,\n\t\t);\n\n\t\t// Early returns\n\t\tif (!reaction || !user) {\n\t\t\tservice.runtime.logger.warn(\"Invalid reaction or user\");\n\t\t\treturn;\n\t\t}\n\n\t\t// Get emoji info\n\t\tlet emoji = reaction.emoji.name;\n\t\tif (!emoji && reaction.emoji.id) {\n\t\t\temoji = `<:${reaction.emoji.name}:${reaction.emoji.id}>`;\n\t\t}\n\n\t\t// Fetch full message if partial\n\t\tif (reaction.partial) {\n\t\t\ttry {\n\t\t\t\tawait reaction.fetch();\n\t\t\t} catch (error) {\n\t\t\t\tservice.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t},\n\t\t\t\t\t\"Failed to fetch partial reaction\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Generate IDs with timestamp to ensure uniqueness\n\t\tconst timestamp = Date.now();\n\t\tconst roomId = createUniqueUuid(\n\t\t\tservice.runtime,\n\t\t\treaction.message.channel.id,\n\t\t);\n\t\tconst entityId = service.resolveDiscordEntityId(user.id);\n\t\tconst reactionUUID = createUniqueUuid(\n\t\t\tservice.runtime,\n\t\t\t`${reaction.message.id}-${user.id}-${emoji}-${timestamp}`,\n\t\t);\n\n\t\t// Validate IDs\n\t\tif (!entityId || !roomId) {\n\t\t\tservice.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\t\tentityId,\n\t\t\t\t\troomId,\n\t\t\t\t},\n\t\t\t\t\"Invalid user ID or room ID\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Process message content\n\t\tconst messageContent = reaction.message.content || \"\";\n\t\tconst truncatedContent =\n\t\t\tmessageContent.length > 50\n\t\t\t\t? `${messageContent.substring(0, 50)}...`\n\t\t\t\t: messageContent;\n\t\tconst reactionMessage = `*${actionText} <${emoji}> ${preposition}: \\\\\"${truncatedContent}\\\\\"*`;\n\n\t\t// Get user info from the reacting user (not the message author)\n\t\tconst reactionMessageAuthor = reaction.message.author;\n\t\tconst userName =\n\t\t\t(\"username\" in user && (user as User).username) ||\n\t\t\treactionMessageAuthor?.username ||\n\t\t\t\"unknown\";\n\t\tconst name =\n\t\t\t(\"globalName\" in user && typeof user.globalName === \"string\"\n\t\t\t\t? user.globalName\n\t\t\t\t: undefined) ||\n\t\t\t(reactionMessageAuthor &&\n\t\t\t\"displayName\" in reactionMessageAuthor &&\n\t\t\ttypeof reactionMessageAuthor.displayName === \"string\"\n\t\t\t\t? reactionMessageAuthor.displayName\n\t\t\t\t: undefined) ||\n\t\t\tuserName;\n\n\t\t// Get channel type once and reuse\n\t\tconst channelType = await service.getChannelType(\n\t\t\treaction.message.channel as Channel,\n\t\t);\n\n\t\tawait service.runtime.ensureConnection({\n\t\t\tentityId,\n\t\t\troomId,\n\t\t\troomName:\n\t\t\t\t\"name\" in reaction.message.channel &&\n\t\t\t\ttypeof reaction.message.channel.name === \"string\"\n\t\t\t\t\t? reaction.message.channel.name\n\t\t\t\t\t: name,\n\t\t\tuserName,\n\t\t\tworldId: createUniqueUuid(\n\t\t\t\tservice.runtime,\n\t\t\t\treaction.message.guild?.id ?? roomId,\n\t\t\t) as UUID,\n\t\t\tworldName: reaction.message.guild?.name || undefined,\n\t\t\tname,\n\t\t\tsource: \"discord\",\n\t\t\tchannelId: reaction.message.channel.id,\n\t\t\tmessageServerId: reaction.message.guild?.id\n\t\t\t\t? stringToUuid(reaction.message.guild.id)\n\t\t\t\t: undefined,\n\t\t\ttype: channelType,\n\t\t\tuserId: user.id as unknown as UUID,\n\t\t\tmetadata: buildDiscordWorldMetadata(\n\t\t\t\tservice.runtime,\n\t\t\t\treaction.message.guild?.ownerId,\n\t\t\t),\n\t\t});\n\n\t\tconst inReplyTo = createUniqueUuid(service.runtime, reaction.message.id);\n\n\t\tconst memory: Memory = {\n\t\t\tid: reactionUUID,\n\t\t\tentityId,\n\t\t\tagentId: service.runtime.agentId,\n\t\t\tcontent: {\n\t\t\t\ttext: reactionMessage,\n\t\t\t\tsource: \"discord\",\n\t\t\t\tinReplyTo,\n\t\t\t\tchannelType,\n\t\t\t},\n\t\t\tmetadata: {\n\t\t\t\tentityName: name,\n\t\t\t\tentityUserName: userName,\n\t\t\t\tfromId: user.id,\n\t\t\t\tdiscordReaction: {\n\t\t\t\t\taction: type,\n\t\t\t\t\temoji,\n\t\t\t\t\ttargetMessageId: inReplyTo,\n\t\t\t\t},\n\t\t\t},\n\t\t\troomId,\n\t\t\tcreatedAt: timestamp,\n\t\t};\n\n\t\tconst callback: HandlerCallback = async (content): Promise<Memory[]> => {\n\t\t\tif (!reaction.message.channel) {\n\t\t\t\tservice.runtime.logger.error(\n\t\t\t\t\t{ src: \"plugin:discord\", agentId: service.runtime.agentId },\n\t\t\t\t\t\"No channel found for reaction message\",\n\t\t\t\t);\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tconst responseText = normalizeDiscordMessageText(content.text);\n\t\t\tif (!responseText.trim()) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tawait (reaction.message.channel as TextChannel).send(responseText);\n\t\t\treturn [];\n\t\t};\n\n\t\tconst events =\n\t\t\ttype === \"add\"\n\t\t\t\t? [DiscordEventTypes.REACTION_RECEIVED, EventType.REACTION_RECEIVED]\n\t\t\t\t: [DiscordEventTypes.REACTION_REMOVED];\n\n\t\tconst reactionPayload: DiscordReactionPayload & EventPayload = {\n\t\t\truntime: service.runtime,\n\t\t\tmessage: memory,\n\t\t\toriginalReaction: reaction as MessageReaction,\n\t\t\tuser: user as User,\n\t\t\tsource: \"discord\",\n\t\t\tcallback,\n\t\t};\n\t\tservice.runtime.emitEvent(events, reactionPayload);\n\t} catch (error) {\n\t\tservice.runtime.logger.error(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\tagentId: service.runtime.agentId,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t},\n\t\t\t\"Error handling reaction\",\n\t\t);\n\t}\n}\n\n/**\n * Handles reaction addition.\n */\nexport async function handleReactionAdd(\n\tservice: ReactionServiceInternals,\n\treaction: MessageReaction | PartialMessageReaction,\n\tuser: User | PartialUser,\n): Promise<void> {\n\tawait handleReaction(service, reaction, user, \"add\");\n}\n\n/**\n * Handles reaction removal.\n */\nexport async function handleReactionRemove(\n\tservice: ReactionServiceInternals,\n\treaction: MessageReaction | PartialMessageReaction,\n\tuser: User | PartialUser,\n): Promise<void> {\n\tawait handleReaction(service, reaction, user, \"remove\");\n}\n",
|
|
50
|
+
"import {\n\ttype IAgentRuntime,\n\tlogger,\n\ttype Media,\n\tModelType,\n\tparseJSONObjectFromText,\n\ttype ReplyToMode,\n\ttrimTokens,\n} from \"@elizaos/core\";\nimport {\n\tActionRowBuilder,\n\ttype AttachmentBuilder,\n\tButtonBuilder,\n\tChannelType,\n\ttype Message as DiscordMessage,\n\ttype MessageActionRowComponentBuilder,\n\ttype MessageCreateOptions,\n\tPermissionsBitField,\n\tStringSelectMenuBuilder,\n\ttype TextChannel,\n\tThreadChannel,\n} from \"discord.js\";\nimport type {\n\tDiscordActionRow,\n\tDiscordComponentOptions,\n\tJsonValue,\n} from \"./types\";\n\nexport interface MessagingAPI {\n\thandleMessage?: (\n\t\tagentId: string,\n\t\tmessage: unknown,\n\t\toptions?: { onResponse?: unknown },\n\t) => Promise<unknown>;\n\tsendMessage?: (\n\t\tagentId: string,\n\t\tmessage: unknown,\n\t\toptions?: { onResponse?: unknown },\n\t) => Promise<unknown>;\n}\n\nexport interface MessageServiceAPI {\n\thandleMessage: (\n\t\truntime: IAgentRuntime,\n\t\tmessage: unknown,\n\t\tcallback: unknown,\n\t) => Promise<unknown>;\n}\n\ninterface RuntimeWithMessagingAPI extends IAgentRuntime {\n\telizaOS: MessagingAPI;\n}\n\nexport function hasMessagingAPI(\n\truntime: IAgentRuntime,\n): runtime is RuntimeWithMessagingAPI {\n\treturn (\n\t\t\"elizaOS\" in runtime &&\n\t\ttypeof (\n\t\t\truntime as {\n\t\t\t\telizaOS?: { handleMessage?: unknown; sendMessage?: unknown };\n\t\t\t}\n\t\t).elizaOS === \"object\" &&\n\t\truntime.elizaOS !== null &&\n\t\t(typeof (runtime.elizaOS as { handleMessage?: unknown }).handleMessage ===\n\t\t\t\"function\" ||\n\t\t\ttypeof (runtime.elizaOS as { sendMessage?: unknown }).sendMessage ===\n\t\t\t\t\"function\")\n\t);\n}\n\nexport function hasMessageService(runtime: IAgentRuntime): boolean {\n\treturn (\n\t\truntime.messageService !== null &&\n\t\ttypeof runtime.messageService?.handleMessage === \"function\"\n\t);\n}\n\nexport function getMessagingAPI(runtime: IAgentRuntime): MessagingAPI | null {\n\tif (hasMessagingAPI(runtime)) {\n\t\treturn runtime.elizaOS;\n\t}\n\treturn null;\n}\n\nexport function getMessageService(\n\truntime: IAgentRuntime,\n): MessageServiceAPI | null {\n\tif (hasMessageService(runtime)) {\n\t\treturn runtime.messageService;\n\t}\n\treturn null;\n}\n\nexport const MAX_MESSAGE_LENGTH = 1900;\n\nfunction collectStructuredText(value: unknown, seen: Set<object>): string[] {\n\tif (typeof value === \"string\") {\n\t\treturn value.trim() ? [value] : [];\n\t}\n\tif (\n\t\ttypeof value === \"number\" ||\n\t\ttypeof value === \"boolean\" ||\n\t\ttypeof value === \"bigint\"\n\t) {\n\t\treturn [String(value)];\n\t}\n\tif (!value || typeof value !== \"object\") {\n\t\treturn [];\n\t}\n\tif (seen.has(value)) {\n\t\treturn [];\n\t}\n\tseen.add(value);\n\n\tif (Array.isArray(value)) {\n\t\treturn value.flatMap((entry) => collectStructuredText(entry, seen));\n\t}\n\n\tconst record = value as Record<string, unknown>;\n\tfor (const key of [\"text\", \"responseText\", \"message\", \"body\"] as const) {\n\t\tconst normalized = collectStructuredText(record[key], seen);\n\t\tif (normalized.length > 0) {\n\t\t\treturn normalized;\n\t\t}\n\t}\n\n\tfor (const key of [\n\t\t\"content\",\n\t\t\"parts\",\n\t\t\"blocks\",\n\t\t\"items\",\n\t\t\"segments\",\n\t] as const) {\n\t\tconst normalized = collectStructuredText(record[key], seen);\n\t\tif (normalized.length > 0) {\n\t\t\treturn normalized;\n\t\t}\n\t}\n\n\tfor (const key of [\"title\", \"summary\"] as const) {\n\t\tconst normalized = collectStructuredText(record[key], seen);\n\t\tif (normalized.length > 0) {\n\t\t\treturn normalized;\n\t\t}\n\t}\n\n\treturn [];\n}\n\nexport function normalizeDiscordMessageText(value: unknown): string {\n\tconst fragments = collectStructuredText(value, new Set())\n\t\t.map((fragment) => fragment.trim())\n\t\t.filter((fragment) => fragment.length > 0);\n\tif (fragments.length === 0) {\n\t\treturn \"\";\n\t}\n\treturn fragments.join(\"\\n\\n\");\n}\n\nexport function cleanUrl(url: string): string {\n\tlet clean = url;\n\n\tclean = clean.replace(/\\\\([._\\-~])/g, \"$1\");\n\n\tif (clean.startsWith(\"](\")) {\n\t\tclean = clean.substring(2);\n\t} else {\n\t\tconst markdownLinkPattern = /\\]\\(/;\n\t\tconst markdownPatternIdx = clean.search(markdownLinkPattern);\n\t\tif (markdownPatternIdx > -1) {\n\t\t\tclean = clean.substring(0, markdownPatternIdx);\n\t\t}\n\t}\n\n\tlet prev = \"\";\n\twhile (prev !== clean) {\n\t\tprev = clean;\n\t\tclean = clean.replace(/[)\\]>.,;!*_]+$/, \"\");\n\t\tclean = clean.replace(\n\t\t\t/[()[]【】{}《》〈〉「」『』、。,.;:!?~~]+$/,\n\t\t\t\"\",\n\t\t);\n\t}\n\n\treturn clean;\n}\n\nexport function extractUrls(text: string, runtime?: IAgentRuntime): string[] {\n\tconst urlRegex = /(https?:\\/\\/[^\\s]+)/g;\n\tconst rawUrls = text.match(urlRegex) || [];\n\n\treturn rawUrls\n\t\t.map((url) => {\n\t\t\tconst original = url;\n\t\t\tconst clean = cleanUrl(url);\n\n\t\t\tif (runtime && original !== clean) {\n\t\t\t\truntime.logger.debug(`URL cleaned: \"${original}\" -> \"${clean}\"`);\n\t\t\t}\n\n\t\t\treturn clean;\n\t\t})\n\t\t.filter((url) => {\n\t\t\ttry {\n\t\t\t\tnew URL(url);\n\t\t\t\treturn true;\n\t\t\t} catch {\n\t\t\t\tif (runtime) {\n\t\t\t\t\truntime.logger.debug(`Invalid URL after cleanup, skipping: \"${url}\"`);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n}\n\nexport function getAttachmentFileName(media: Media): string {\n\tlet extension = \"\";\n\ttry {\n\t\tconst urlPath = new URL(media.url).pathname;\n\t\tconst urlExtension = urlPath.substring(urlPath.lastIndexOf(\".\"));\n\t\tif (urlExtension && urlExtension.length > 1 && urlExtension.length <= 5) {\n\t\t\textension = urlExtension;\n\t\t}\n\t} catch {\n\t\tconst lastDot = media.url.lastIndexOf(\".\");\n\t\tconst queryStart = media.url.indexOf(\"?\", lastDot);\n\t\tif (lastDot > 0 && (queryStart === -1 || queryStart > lastDot + 1)) {\n\t\t\tconst potentialExt = media.url.substring(\n\t\t\t\tlastDot,\n\t\t\t\tqueryStart > -1 ? queryStart : undefined,\n\t\t\t);\n\t\t\tif (potentialExt.length > 1 && potentialExt.length <= 5) {\n\t\t\t\textension = potentialExt;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!extension && media.contentType) {\n\t\tconst contentTypeMap: Record<string, string> = {\n\t\t\timage: \".png\",\n\t\t\tvideo: \".mp4\",\n\t\t\taudio: \".mp3\",\n\t\t\tdocument: \".txt\",\n\t\t\tlink: \".html\",\n\t\t};\n\t\textension = contentTypeMap[media.contentType] || \"\";\n\t}\n\n\tif (!extension) {\n\t\textension = \".txt\";\n\t}\n\n\tconst baseName = media.title || media.id || \"attachment\";\n\tconst hasExtension = /\\.\\w{1,5}$/i.test(baseName);\n\n\treturn hasExtension ? baseName : `${baseName}${extension}`;\n}\n\nexport async function generateSummary(\n\truntime: IAgentRuntime,\n\ttext: string,\n): Promise<{ title: string; description: string }> {\n\ttext = await trimTokens(text, 100000, runtime);\n\n\tif (!text) {\n\t\treturn {\n\t\t\ttitle: \"\",\n\t\t\tdescription: \"\",\n\t\t};\n\t}\n\n\tif (text.length < 1000) {\n\t\treturn {\n\t\t\ttitle: \"\",\n\t\t\tdescription: text,\n\t\t};\n\t}\n\n\truntime.logger.info(\n\t\t`[Summarization] Calling TEXT_SMALL for ${text.length} chars: \"${text.substring(0, 50).replace(/\\n/g, \" \")}...\"`,\n\t);\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(ModelType.TEXT_SMALL, {\n\t\tprompt,\n\t});\n\n\tconst parsedResponse = parseJSONObjectFromText(response) as {\n\t\ttitle?: string;\n\t\tsummary?: string;\n\t} | null;\n\n\tif (\n\t\tparsedResponse &&\n\t\ttypeof parsedResponse.title === \"string\" &&\n\t\ttypeof parsedResponse.summary === \"string\"\n\t) {\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 * Discord API error structure\n */\ninterface DiscordAPIError extends Error {\n\tcode?: number;\n}\n\n/**\n * Type guard for Discord API errors\n */\nfunction isDiscordAPIError(error: unknown): error is DiscordAPIError {\n\treturn error instanceof Error && \"code\" in error;\n}\n\nfunction isReplyReferenceFailure(error: unknown): boolean {\n\tif (!isDiscordAPIError(error)) {\n\t\treturn false;\n\t}\n\n\tconst errorMessage = error.message.toLowerCase();\n\treturn (\n\t\terror.code === 10008 ||\n\t\terrorMessage.includes(\"unknown message\") ||\n\t\terrorMessage.includes(\"message reference\") ||\n\t\terrorMessage.includes(\"message_reference\")\n\t);\n}\n\n/**\n * Discord.js component with toJSON method\n */\ninterface DiscordJsComponent {\n\ttoJSON(): JsonValue;\n}\n\n/**\n * Type guard for Discord.js components\n */\nfunction isDiscordJsComponent(\n\tcomponent: unknown,\n): component is DiscordJsComponent {\n\treturn (\n\t\tcomponent !== null &&\n\t\ttypeof component === \"object\" &&\n\t\t\"toJSON\" in component &&\n\t\ttypeof (component as DiscordJsComponent).toJSON === \"function\"\n\t);\n}\n\n/**\n * Type guard for arrays of Discord.js components (ActionRowBuilder)\n */\nfunction isDiscordJsComponentArray(\n\tcomponents: unknown[],\n): components is ActionRowBuilder<MessageActionRowComponentBuilder>[] {\n\treturn components.length > 0 && components.every(isDiscordJsComponent);\n}\n\n/**\n * Safe JSON stringify that handles BigInt values\n */\nfunction safeStringify(obj: unknown): string {\n\treturn JSON.stringify(obj, (_, value) =>\n\t\ttypeof value === \"bigint\" ? value.toString() : value,\n\t);\n}\n\n/**\n * Message send options for Discord\n */\ninterface MessageSendOptions {\n\tcontent: string;\n\treply?: {\n\t\tmessageReference: string;\n\t};\n\tfiles?: Array<\n\t\tAttachmentBuilder | { attachment: Buffer | string; name: string }\n\t>;\n\tcomponents?: ActionRowBuilder<MessageActionRowComponentBuilder>[];\n}\n\nexport async function sendMessageInChunks(\n\tchannel: TextChannel,\n\tcontent: string,\n\tinReplyTo: string,\n\tfiles: Array<\n\t\tAttachmentBuilder | { attachment: Buffer | string; name: string }\n\t>,\n\tcomponents?: DiscordActionRow[],\n\truntime?: IAgentRuntime,\n\treplyToMode: ReplyToMode = \"first\",\n): Promise<DiscordMessage[]> {\n\tconst sentMessages: DiscordMessage[] = [];\n\tlet lastSendError: unknown = null;\n\n\tlet messages: string[];\n\tif (\n\t\truntime &&\n\t\tcontent.length > MAX_MESSAGE_LENGTH &&\n\t\tneedsSmartSplit(content)\n\t) {\n\t\tmessages = await smartSplitMessage(runtime, content);\n\t} else {\n\t\tmessages = splitMessage(content);\n\t}\n\tif (\n\t\tmessages.length === 0 &&\n\t\t((files && files.length > 0) || (components && components.length > 0))\n\t) {\n\t\tmessages = [\"\"];\n\t}\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\t(i === messages.length - 1 && components && components.length > 0)\n\t\t\t) {\n\t\t\t\tconst options: MessageSendOptions = {\n\t\t\t\t\tcontent: message.trim(),\n\t\t\t\t};\n\n\t\t\t\tif (\n\t\t\t\t\tinReplyTo &&\n\t\t\t\t\t(replyToMode === \"all\" || (replyToMode === \"first\" && i === 0))\n\t\t\t\t) {\n\t\t\t\t\toptions.reply = {\n\t\t\t\t\t\tmessageReference: inReplyTo,\n\t\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\toptions.files = files;\n\t\t\t\t}\n\n\t\t\t\tif (i === messages.length - 1 && components && components.length > 0) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlogger.info(`Components received: ${safeStringify(components)}`);\n\n\t\t\t\t\t\tif (!Array.isArray(components)) {\n\t\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t\t\"Components is not an array, skipping component processing\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else if (isDiscordJsComponentArray(components)) {\n\t\t\t\t\t\t\toptions.components = components;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst discordComponents = (components as DiscordActionRow[])\n\t\t\t\t\t\t\t\t.map((row: DiscordActionRow) => {\n\t\t\t\t\t\t\t\t\tif (!row || typeof row !== \"object\" || row.type !== 1) {\n\t\t\t\t\t\t\t\t\t\tlogger.warn(\"Invalid component row structure, skipping\");\n\t\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tif (row.type === 1) {\n\t\t\t\t\t\t\t\t\t\tconst actionRow =\n\t\t\t\t\t\t\t\t\t\t\tnew ActionRowBuilder<MessageActionRowComponentBuilder>();\n\n\t\t\t\t\t\t\t\t\t\tif (!Array.isArray(row.components)) {\n\t\t\t\t\t\t\t\t\t\t\tlogger.warn(\"Row components is not an array, skipping\");\n\t\t\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tconst validComponents = row.components\n\t\t\t\t\t\t\t\t\t\t\t.map((comp: DiscordComponentOptions) => {\n\t\t\t\t\t\t\t\t\t\t\t\tif (!comp || typeof comp !== \"object\") {\n\t\t\t\t\t\t\t\t\t\t\t\t\tlogger.warn(\"Invalid component, skipping\");\n\t\t\t\t\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (comp.type === 2) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn new ButtonBuilder()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.setCustomId(comp.custom_id)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.setLabel(comp.label || \"\")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.setStyle(comp.style || 1);\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (comp.type === 3) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst selectMenu = new StringSelectMenuBuilder()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.setCustomId(comp.custom_id)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.setPlaceholder(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcomp.placeholder || \"Select an option\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (typeof comp.min_values === \"number\") {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tselectMenu.setMinValues(comp.min_values);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (typeof comp.max_values === \"number\") {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tselectMenu.setMaxValues(comp.max_values);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (Array.isArray(comp.options)) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tselectMenu.addOptions(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcomp.options.map((option) => ({\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlabel: option.label,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvalue: option.value,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: option.description,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn selectMenu;\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tlogger.error(`Error creating component: ${err}`);\n\t\t\t\t\t\t\t\t\t\t\t\t\treturn null;\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\treturn null;\n\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\t.filter(Boolean);\n\n\t\t\t\t\t\t\t\t\t\tif (validComponents.length > 0) {\n\t\t\t\t\t\t\t\t\t\t\tactionRow.addComponents(validComponents);\n\t\t\t\t\t\t\t\t\t\t\treturn actionRow;\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\treturn null;\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t.filter(Boolean);\n\n\t\t\t\t\t\t\tif (discordComponents.length > 0) {\n\t\t\t\t\t\t\t\toptions.components = discordComponents;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tlogger.error(`Error processing components: ${error}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tconst m = await channel.send(options as MessageCreateOptions);\n\t\t\t\t\tsentMessages.push(m);\n\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\tif (isReplyReferenceFailure(error) && options.reply) {\n\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t\"Message reference no longer valid (message may have been deleted). Sending without reply threading.\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst optionsWithoutReply = { ...options };\n\t\t\t\t\t\tdelete optionsWithoutReply.reply;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst m = await channel.send(\n\t\t\t\t\t\t\t\toptionsWithoutReply as MessageCreateOptions,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tsentMessages.push(m);\n\t\t\t\t\t\t} catch (retryError: unknown) {\n\t\t\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\t\t\tretryError instanceof Error\n\t\t\t\t\t\t\t\t\t? retryError.message\n\t\t\t\t\t\t\t\t\t: String(retryError);\n\t\t\t\t\t\t\tlastSendError = retryError;\n\t\t\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t\t\t`Error sending message after removing reply reference: ${errorMessage}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tthrow retryError;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlastSendError = error;\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tlastSendError = error;\n\t\tlogger.error(`Error sending message: ${error}`);\n\t}\n\n\tconst attemptedSend =\n\t\tcontent.trim().length > 0 ||\n\t\t(files && files.length > 0) ||\n\t\t(components && components.length > 0);\n\tif (attemptedSend && sentMessages.length === 0) {\n\t\tif (lastSendError instanceof Error) {\n\t\t\tthrow lastSendError;\n\t\t}\n\t\tthrow new Error(\n\t\t\t\"Discord message send completed without delivering any chunks\",\n\t\t);\n\t}\n\n\treturn sentMessages;\n}\n\nexport function needsSmartSplit(content: string): boolean {\n\tconst codeBlockCount = (content.match(/```/g) || []).length;\n\tif (codeBlockCount >= 2) {\n\t\treturn true;\n\t}\n\n\tif (/^#{1,3}\\s/m.test(content)) {\n\t\treturn true;\n\t}\n\n\tif (/^\\d+\\.\\s/m.test(content)) {\n\t\treturn true;\n\t}\n\n\tconst lines = content.split(\"\\n\");\n\tconst hasLongUnbreakableLines = lines.some(\n\t\t(line) => line.length > 500 && !line.includes(\". \") && !line.includes(\", \"),\n\t);\n\tif (hasLongUnbreakableLines) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nfunction parseJSONArrayFromText(text: string): JsonValue[] | null {\n\tconst jsonBlockPattern = /```json\\n([\\s\\S]*?)\\n```/;\n\tlet jsonData: JsonValue = null;\n\tconst jsonBlockMatch = text.match(jsonBlockPattern);\n\n\ttry {\n\t\tif (jsonBlockMatch) {\n\t\t\tjsonData = JSON.parse(jsonBlockMatch[1].trim()) as JsonValue;\n\t\t} else {\n\t\t\tjsonData = JSON.parse(text.trim()) as JsonValue;\n\t\t}\n\t} catch (_e) {\n\t\treturn null;\n\t}\n\n\tif (Array.isArray(jsonData)) {\n\t\treturn jsonData;\n\t}\n\n\treturn null;\n}\n\nexport async function smartSplitMessage(\n\truntime: IAgentRuntime,\n\tcontent: string,\n\tmaxLength: number = MAX_MESSAGE_LENGTH,\n): Promise<string[]> {\n\tif (content.length <= maxLength) {\n\t\treturn [content];\n\t}\n\n\tconst estimatedChunks = Math.ceil(content.length / (maxLength - 100));\n\n\ttry {\n\t\truntime.logger.debug(\n\t\t\t`Smart splitting ${content.length} chars into ~${estimatedChunks} chunks`,\n\t\t);\n\n\t\tconst prompt = `Split the following text into ${estimatedChunks} parts for Discord messages (max ${maxLength} chars each).\nKeep related content together (don't split code blocks, keep list items with their headers, etc.).\nReturn ONLY a JSON array of strings, no explanation.\n\nText to split:\n\"\"\"\n${content}\n\"\"\"\n\nReturn format: [\"chunk1\", \"chunk2\", ...]`;\n\n\t\tconst response = await runtime.useModel(ModelType.TEXT_SMALL, { prompt });\n\n\t\tconst parsed = parseJSONArrayFromText(response);\n\t\tif (Array.isArray(parsed)) {\n\t\t\tconst validChunks = parsed.filter(\n\t\t\t\t(chunk: unknown): chunk is string =>\n\t\t\t\t\ttypeof chunk === \"string\" &&\n\t\t\t\t\tchunk.trim().length > 0 &&\n\t\t\t\t\tchunk.length <= maxLength,\n\t\t\t);\n\n\t\t\tif (validChunks.length > 0) {\n\t\t\t\treturn validChunks;\n\t\t\t}\n\n\t\t\truntime.logger.debug(\n\t\t\t\t\"Smart split returned empty or invalid chunks, falling back to simple split\",\n\t\t\t);\n\t\t}\n\t} catch (error) {\n\t\truntime.logger.debug(\n\t\t\t`Smart split failed, falling back to simple split: ${error}`,\n\t\t);\n\t}\n\n\treturn splitMessage(content, maxLength);\n}\n\nexport function splitMessage(\n\tcontent: string,\n\tmaxLength: number = MAX_MESSAGE_LENGTH,\n): string[] {\n\tif (!content || content.length <= maxLength) {\n\t\treturn content ? [content] : [];\n\t}\n\n\tconst messages: string[] = [];\n\tlet currentMessage = \"\";\n\n\tconst rawLines = content.split(\"\\n\");\n\tconst lines = rawLines.flatMap((line) => {\n\t\tconst chunks: string[] = [];\n\t\twhile (line.length > maxLength) {\n\t\t\tlet splitIdx = maxLength;\n\t\t\tconst lastSpace = line.lastIndexOf(\" \", maxLength);\n\n\t\t\tif (lastSpace > maxLength * 0.7) {\n\t\t\t\tsplitIdx = lastSpace;\n\t\t\t} else if (lastSpace > maxLength * 0.3) {\n\t\t\t\tsplitIdx = lastSpace;\n\t\t\t}\n\n\t\t\tchunks.push(line.slice(0, splitIdx));\n\t\t\tline = line.slice(splitIdx).trimStart();\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 > maxLength) {\n\t\t\tif (currentMessage.trim().length > 0) {\n\t\t\t\tmessages.push(currentMessage.trim());\n\t\t\t}\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\tif (messages.length === 0 && content.length > 0) {\n\t\tmessages.push(\" \");\n\t}\n\n\treturn messages;\n}\n\nexport interface CanSendMessageResult {\n\tcanSend: boolean;\n\treason: string | null;\n\tmissingPermissions?: bigint[];\n}\n\ntype SendableChannel = TextChannel | ThreadChannel | { type: ChannelType };\n\nexport function canSendMessage(\n\tchannel: SendableChannel | null | undefined,\n): CanSendMessageResult {\n\tif (!channel) {\n\t\treturn {\n\t\t\tcanSend: false,\n\t\t\treason: \"No channel given\",\n\t\t};\n\t}\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\n\tif (!(\"guild\" in channel) || !channel.guild) {\n\t\treturn {\n\t\t\tcanSend: false,\n\t\t\treason: \"Not a guild channel\",\n\t\t};\n\t}\n\n\tconst guildChannel = channel as TextChannel | ThreadChannel;\n\tconst botMember = guildChannel.guild.members.cache.get(\n\t\tguildChannel.client.user.id,\n\t);\n\n\tif (!botMember) {\n\t\treturn {\n\t\t\tcanSend: false,\n\t\t\treason: \"Bot member not found in guild\",\n\t\t};\n\t}\n\n\tconst requiredPermissions: bigint[] = [\n\t\tPermissionsBitField.Flags.ViewChannel,\n\t\tPermissionsBitField.Flags.SendMessages,\n\t\tPermissionsBitField.Flags.ReadMessageHistory,\n\t];\n\n\tif (guildChannel instanceof ThreadChannel) {\n\t\trequiredPermissions.push(PermissionsBitField.Flags.SendMessagesInThreads);\n\t}\n\n\tconst permissions = guildChannel.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\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,\n\t\treason:\n\t\t\tmissingPermissions.length > 0\n\t\t\t\t? `Missing permissions: ${missingPermissions.map((p) => String(p)).join(\", \")}`\n\t\t\t\t: null,\n\t};\n}\n",
|
|
51
|
+
"import type { IAgentRuntime } from \"@elizaos/core\";\nimport { parseBooleanFromText } from \"@elizaos/core\";\nimport { z } from \"zod\";\nimport type { DiscordSettings } from \"./types\";\n\nfunction getEnvBoolean(name: string, fallback: boolean): boolean {\n\tconst value = process.env?.[name];\n\tif (!value) {\n\t\treturn fallback;\n\t}\n\treturn value.toLowerCase() === \"true\";\n}\n\nfunction getEnvArray(name: string, fallback: string[]): string[] {\n\tconst value = process.env?.[name];\n\tif (!value || value.trim() === \"\") {\n\t\treturn fallback;\n\t}\n\treturn value\n\t\t.split(\",\")\n\t\t.map((item) => item.trim())\n\t\t.filter((item) => item.length > 0);\n}\n\nexport const DISCORD_DEFAULTS = {\n\tSHOULD_IGNORE_BOT_MESSAGES: getEnvBoolean(\n\t\t\"DISCORD_SHOULD_IGNORE_BOT_MESSAGES\",\n\t\ttrue,\n\t),\n\tSHOULD_IGNORE_DIRECT_MESSAGES: getEnvBoolean(\n\t\t\"DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES\",\n\t\ttrue,\n\t),\n\tSHOULD_RESPOND_ONLY_TO_MENTIONS: getEnvBoolean(\n\t\t\"DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS\",\n\t\ttrue,\n\t),\n\tALLOWED_CHANNEL_IDS: getEnvArray(\"CHANNEL_IDS\", []),\n\tDM_POLICY: (process.env?.DISCORD_DM_POLICY || \"pairing\") as\n\t\t| \"open\"\n\t\t| \"allowlist\"\n\t\t| \"pairing\"\n\t\t| \"disabled\",\n\tALLOW_FROM: getEnvArray(\"DISCORD_ALLOW_FROM\", []),\n\tSYNC_PROFILE: getEnvBoolean(\"DISCORD_SYNC_PROFILE\", true),\n} as const;\n\nexport const discordEnvSchema = z.object({\n\tDISCORD_API_TOKEN: z.string().min(1, \"Discord API token is required\"),\n\tCHANNEL_IDS: z\n\t\t.string()\n\t\t.nullish()\n\t\t.transform((val) =>\n\t\t\tval\n\t\t\t\t? val\n\t\t\t\t\t\t.split(\",\")\n\t\t\t\t\t\t.map((s) => s.trim())\n\t\t\t\t\t\t.filter((s) => s.length > 0)\n\t\t\t\t: undefined,\n\t\t),\n\tDISCORD_SHOULD_IGNORE_BOT_MESSAGES: z\n\t\t.string()\n\t\t.nullish()\n\t\t.transform((val) => (val ? parseBooleanFromText(val) : undefined)),\n\tDISCORD_SHOULD_IGNORE_DIRECT_MESSAGES: z\n\t\t.string()\n\t\t.nullish()\n\t\t.transform((val) => (val ? parseBooleanFromText(val) : undefined)),\n\tDISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS: z\n\t\t.string()\n\t\t.nullish()\n\t\t.transform((val) => (val ? parseBooleanFromText(val) : undefined)),\n});\n\nexport type DiscordConfig = z.infer<typeof discordEnvSchema>;\n\nexport function getDiscordSettings(runtime: IAgentRuntime): DiscordSettings {\n\tconst characterSettings =\n\t\t(runtime.character.settings &&\n\t\t\t(runtime.character.settings.discord as DiscordSettings)) ||\n\t\t{};\n\n\tconst resolveSetting = <T>(\n\t\tenvKey: string,\n\t\tcharacterValue: T | undefined,\n\t\tdefaultValue: T,\n\t\ttransform?: (value: string) => T,\n\t): T => {\n\t\tconst runtimeValue = runtime.getSetting(envKey);\n\t\tif (runtimeValue !== undefined && runtimeValue !== null) {\n\t\t\tconst normalized =\n\t\t\t\ttypeof runtimeValue === \"string\" ? runtimeValue : String(runtimeValue);\n\t\t\treturn transform ? transform(normalized) : (runtimeValue as T);\n\t\t}\n\t\treturn characterValue ?? defaultValue;\n\t};\n\n\tconst resolvedAllowedChannelIds = resolveSetting<string[]>(\n\t\t\"CHANNEL_IDS\",\n\t\tcharacterSettings.allowedChannelIds,\n\t\tDISCORD_DEFAULTS.ALLOWED_CHANNEL_IDS,\n\t\t(value: string) =>\n\t\t\tvalue\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((s) => s.trim())\n\t\t\t\t.filter((s) => s.length > 0),\n\t);\n\n\treturn {\n\t\t...characterSettings,\n\t\tshouldIgnoreBotMessages: resolveSetting(\n\t\t\t\"DISCORD_SHOULD_IGNORE_BOT_MESSAGES\",\n\t\t\tcharacterSettings.shouldIgnoreBotMessages,\n\t\t\tDISCORD_DEFAULTS.SHOULD_IGNORE_BOT_MESSAGES,\n\t\t\tparseBooleanFromText,\n\t\t),\n\n\t\tshouldIgnoreDirectMessages: resolveSetting(\n\t\t\t\"DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES\",\n\t\t\tcharacterSettings.shouldIgnoreDirectMessages,\n\t\t\tDISCORD_DEFAULTS.SHOULD_IGNORE_DIRECT_MESSAGES,\n\t\t\tparseBooleanFromText,\n\t\t),\n\n\t\tshouldRespondOnlyToMentions: resolveSetting(\n\t\t\t\"DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS\",\n\t\t\tcharacterSettings.shouldRespondOnlyToMentions,\n\t\t\tDISCORD_DEFAULTS.SHOULD_RESPOND_ONLY_TO_MENTIONS,\n\t\t\tparseBooleanFromText,\n\t\t),\n\n\t\tallowedChannelIds:\n\t\t\tresolvedAllowedChannelIds.length > 0\n\t\t\t\t? resolvedAllowedChannelIds\n\t\t\t\t: undefined,\n\n\t\tdmPolicy: resolveSetting(\n\t\t\t\"DISCORD_DM_POLICY\",\n\t\t\tcharacterSettings.dmPolicy,\n\t\t\tDISCORD_DEFAULTS.DM_POLICY,\n\t\t\t(value: string) => {\n\t\t\t\tconst normalized = value.toLowerCase().trim();\n\t\t\t\tif ([\"open\", \"allowlist\", \"pairing\", \"disabled\"].includes(normalized)) {\n\t\t\t\t\treturn normalized as \"open\" | \"allowlist\" | \"pairing\" | \"disabled\";\n\t\t\t\t}\n\t\t\t\treturn DISCORD_DEFAULTS.DM_POLICY;\n\t\t\t},\n\t\t),\n\n\t\tallowFrom: resolveSetting<string[]>(\n\t\t\t\"DISCORD_ALLOW_FROM\",\n\t\t\tcharacterSettings.allowFrom,\n\t\t\tDISCORD_DEFAULTS.ALLOW_FROM,\n\t\t\t(value: string) =>\n\t\t\t\tvalue\n\t\t\t\t\t.split(\",\")\n\t\t\t\t\t.map((s) => s.trim())\n\t\t\t\t\t.filter((s) => s.length > 0),\n\t\t),\n\n\t\tsyncProfile: resolveSetting(\n\t\t\t\"DISCORD_SYNC_PROFILE\",\n\t\t\tcharacterSettings.syncProfile,\n\t\t\tDISCORD_DEFAULTS.SYNC_PROFILE,\n\t\t\tparseBooleanFromText,\n\t\t),\n\n\t\tprofileName: resolveSetting(\n\t\t\t\"DISCORD_PROFILE_NAME\",\n\t\t\tcharacterSettings.profileName,\n\t\t\tundefined,\n\t\t\t(value: string) => value.trim(),\n\t\t),\n\n\t\tprofileAvatar: resolveSetting(\n\t\t\t\"DISCORD_PROFILE_AVATAR\",\n\t\t\tcharacterSettings.profileAvatar,\n\t\t\tundefined,\n\t\t\t(value: string) => value.trim(),\n\t\t),\n\n\t\tautoReply: resolveSetting(\n\t\t\t\"DISCORD_AUTO_REPLY\",\n\t\t\tcharacterSettings.autoReply,\n\t\t\tfalse,\n\t\t\tparseBooleanFromText,\n\t\t),\n\t};\n}\n\nexport async function validateDiscordConfig(\n\truntime: IAgentRuntime,\n): Promise<DiscordConfig> {\n\ttry {\n\t\tconst config = {\n\t\t\tDISCORD_API_TOKEN: runtime.getSetting(\"DISCORD_API_TOKEN\"),\n\t\t\tCHANNEL_IDS: runtime.getSetting(\"CHANNEL_IDS\"),\n\t\t\tDISCORD_SHOULD_IGNORE_BOT_MESSAGES: runtime.getSetting(\n\t\t\t\t\"DISCORD_SHOULD_IGNORE_BOT_MESSAGES\",\n\t\t\t),\n\t\t\tDISCORD_SHOULD_IGNORE_DIRECT_MESSAGES: runtime.getSetting(\n\t\t\t\t\"DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES\",\n\t\t\t),\n\t\t\tDISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS: runtime.getSetting(\n\t\t\t\t\"DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS\",\n\t\t\t),\n\t\t};\n\n\t\treturn discordEnvSchema.parse(config);\n\t} catch (error) {\n\t\tif (error instanceof z.ZodError) {\n\t\t\tconst errorMessages = error.issues\n\t\t\t\t.map((err) => `${err.path.join(\".\")}: ${err.message}`)\n\t\t\t\t.join(\"\\n\");\n\t\t\tthrow new Error(\n\t\t\t\t`Discord configuration validation failed:\\n${errorMessages}`,\n\t\t\t);\n\t\t}\n\t\tthrow error;\n\t}\n}\n",
|
|
52
|
+
"import {\n\tChannelType,\n\ttype Content,\n\tcheckPairingAllowed,\n\tcreateUniqueUuid,\n\tEventType,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\tisInAllowlist,\n\tlifeOpsPassiveConnectorsEnabled,\n\ttype Media,\n\ttype Memory,\n\ttype Service,\n\tServiceType,\n\tstringToUuid,\n\ttype UUID,\n} from \"@elizaos/core\";\nimport {\n\tAttachmentBuilder,\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\";\n// See service.ts for detailed documentation on Discord ID handling.\n// Key point: Discord snowflake IDs (e.g., \"1253563208833433701\") are NOT valid UUIDs.\n// Use stringToUuid() to convert them, not asUUID() which would throw an error.\nimport type { ICompatRuntime } from \"./compat\";\nimport { createDraftStreamController } from \"./draft-stream\";\nimport { getDiscordSettings } from \"./environment\";\nimport { buildDiscordWorldMetadata } from \"./identity\";\nimport { formatInboundEnvelope } from \"./inbound-envelope\";\nimport { stripReasoningTags } from \"./reasoning-tags\";\nimport {\n\tcreateStatusReactionController,\n\ttype StatusReactionScope,\n\tshouldShowStatusReaction,\n} from \"./status-reactions\";\nimport type { DiscordSettings, IDiscordService } from \"./types\";\nimport { createTypingController } from \"./typing\";\nimport {\n\tcanSendMessage,\n\textractUrls,\n\tgetAttachmentFileName,\n\tgetMessageService,\n\tgetMessagingAPI,\n\tnormalizeDiscordMessageText,\n\tsendMessageInChunks,\n} from \"./utils\";\n\nfunction escapeRegex(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction textMentionsAnyName(\n\ttext: string | undefined,\n\tnames: Array<string | null | undefined>,\n): boolean {\n\tif (!text) {\n\t\treturn false;\n\t}\n\n\treturn names.some((name) => {\n\t\tconst candidate = name?.trim();\n\t\tif (!candidate) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst pattern = new RegExp(\n\t\t\t`(^|[^\\\\p{L}\\\\p{N}])${escapeRegex(candidate)}(?=$|[^\\\\p{L}\\\\p{N}])`,\n\t\t\t\"iu\",\n\t\t);\n\t\treturn pattern.test(text);\n\t});\n}\n\nfunction normalizeReplyToMode(\n\treplyToMode: DiscordSettings[\"replyToMode\"],\n): \"off\" | \"first\" | \"all\" {\n\tif (replyToMode === \"off\" || replyToMode === \"all\") {\n\t\treturn replyToMode;\n\t}\n\n\treturn \"first\";\n}\n\n/**\n * Class representing a Message Manager for handling Discord messages.\n */\n\nexport class MessageManager {\n\tprivate client: Client;\n\tprivate runtime: ICompatRuntime;\n\tprivate attachmentManager: AttachmentManager;\n\tprivate getChannelType: (channel: Channel) => Promise<ChannelType>;\n\tprivate discordSettings: DiscordSettings;\n\tprivate discordService: IDiscordService;\n\tprivate statusReactionScope: StatusReactionScope;\n\tprivate envelopeEnabled: boolean;\n\tprivate draftStreamingEnabled: boolean;\n\tprivate recentlyProcessedMessageIds = new Map<string, number>();\n\tprivate static readonly PROCESSED_MESSAGE_TTL_MS = 2 * 60 * 1000;\n\t/**\n\t * Constructor for a new instance of MessageManager.\n\t * @param {IDiscordService} discordService - The Discord service instance.\n\t * @param {ICompatRuntime} runtime - The agent runtime instance (with cross-core compat).\n\t * @throws {Error} If the Discord client is not initialized\n\t */\n\tconstructor(discordService: IDiscordService, runtime: ICompatRuntime) {\n\t\t// Guard against null client - fail fast with a clear error\n\t\tif (!discordService.client) {\n\t\t\tconst errorMsg =\n\t\t\t\t\"Discord client not initialized - cannot create MessageManager\";\n\t\t\truntime.logger.error(\n\t\t\t\t{ src: \"plugin:discord\", agentId: runtime.agentId },\n\t\t\t\terrorMsg,\n\t\t\t);\n\t\t\tthrow new Error(errorMsg);\n\t\t}\n\n\t\tthis.client = discordService.client;\n\t\tthis.runtime = runtime;\n\t\tthis.attachmentManager = new AttachmentManager(this.runtime);\n\t\tthis.getChannelType = discordService.getChannelType;\n\t\tthis.discordService = discordService;\n\t\t// Load Discord settings with proper priority (env vars > character settings > defaults)\n\t\tthis.discordSettings = getDiscordSettings(this.runtime);\n\t\tconst reactionScopeSetting = this.runtime.getSetting(\n\t\t\t\"DISCORD_STATUS_REACTIONS\",\n\t\t) as string | undefined;\n\t\tthis.statusReactionScope = (\n\t\t\t[\"all\", \"group-mentions\", \"none\"].includes(reactionScopeSetting ?? \"\")\n\t\t\t\t? reactionScopeSetting\n\t\t\t\t: \"group-mentions\"\n\t\t) as StatusReactionScope;\n\n\t\tconst envelopeSetting = this.runtime.getSetting(\n\t\t\t\"DISCORD_ENVELOPE_ENABLED\",\n\t\t) as string | undefined;\n\t\tthis.envelopeEnabled =\n\t\t\tenvelopeSetting !== \"false\" && envelopeSetting !== \"0\";\n\n\t\tconst draftStreamSetting = this.runtime.getSetting(\n\t\t\t\"DISCORD_DRAFT_STREAMING\",\n\t\t) as string | undefined;\n\t\tthis.draftStreamingEnabled =\n\t\t\tdraftStreamSetting === \"true\" || draftStreamSetting === \"1\";\n\t}\n\n\t/**\n\t * Check DM access based on the configured dmPolicy.\n\t *\n\t * @param message - The Discord DM message\n\t * @returns Access check result with allowed status and optional reply message\n\t */\n\tprivate async checkDmAccess(message: DiscordMessage): Promise<{\n\t\tallowed: boolean;\n\t\treplyMessage?: string;\n\t}> {\n\t\tconst policy = this.discordSettings.dmPolicy ?? \"pairing\";\n\t\tconst userId = message.author.id;\n\n\t\t// Disabled policy - block all DMs\n\t\tif (policy === \"disabled\") {\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tuserId,\n\t\t\t\t},\n\t\t\t\t\"DM blocked: policy is disabled\",\n\t\t\t);\n\t\t\treturn { allowed: false };\n\t\t}\n\n\t\t// Open policy - allow all DMs\n\t\tif (policy === \"open\") {\n\t\t\treturn { allowed: true };\n\t\t}\n\n\t\t// Allowlist policy - check static allowFrom list and dynamic pairing allowlist\n\t\tif (policy === \"allowlist\") {\n\t\t\t// Check static allowlist first\n\t\t\tif (this.discordSettings.allowFrom?.includes(userId)) {\n\t\t\t\treturn { allowed: true };\n\t\t\t}\n\n\t\t\t// Check dynamic pairing allowlist\n\t\t\tconst inDynamicAllowlist = await isInAllowlist(\n\t\t\t\tthis.runtime,\n\t\t\t\t\"discord\",\n\t\t\t\tuserId,\n\t\t\t);\n\t\t\tif (inDynamicAllowlist) {\n\t\t\t\treturn { allowed: true };\n\t\t\t}\n\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tuserId,\n\t\t\t\t},\n\t\t\t\t\"DM blocked: user not in allowlist\",\n\t\t\t);\n\t\t\treturn { allowed: false };\n\t\t}\n\n\t\t// Pairing policy - use PairingService\n\t\tif (policy === \"pairing\") {\n\t\t\t// Check static allowlist first (if configured, allow bypass of pairing)\n\t\t\tif (this.discordSettings.allowFrom?.includes(userId)) {\n\t\t\t\treturn { allowed: true };\n\t\t\t}\n\n\t\t\t// Use the PairingService for pairing workflow\n\t\t\tconst result = await checkPairingAllowed(this.runtime, {\n\t\t\t\tchannel: \"discord\",\n\t\t\t\tsenderId: userId,\n\t\t\t\tmetadata: {\n\t\t\t\t\tusername: message.author.username,\n\t\t\t\t\tdisplayName: message.author.displayName ?? message.author.username,\n\t\t\t\t\tdiscriminator: message.author.discriminator ?? \"\",\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tif (result.allowed) {\n\t\t\t\treturn { allowed: true };\n\t\t\t}\n\n\t\t\t// Not allowed - return pairing reply message only for new requests\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tuserId,\n\t\t\t\t\tpairingCode: result.pairingCode,\n\t\t\t\t\tnewRequest: result.newRequest,\n\t\t\t\t},\n\t\t\t\t\"DM blocked: pairing required\",\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tallowed: false,\n\t\t\t\t// Only send reply for new pairing requests (avoid spamming on every message)\n\t\t\t\treplyMessage: result.newRequest ? result.replyMessage : undefined,\n\t\t\t};\n\t\t}\n\n\t\t// Default: allow\n\t\treturn { allowed: true };\n\t}\n\n\tprivate async persistInboundMemory(memory: Memory): Promise<void> {\n\t\tif (!memory.id) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst existing = await this.runtime.getMemoryById(memory.id);\n\t\tif (existing) {\n\t\t\treturn;\n\t\t}\n\n\t\tawait this.runtime.createMemory(memory, \"messages\");\n\t}\n\n\tprivate markMessageAsProcessing(messageId: string): boolean {\n\t\tconst now = Date.now();\n\t\tfor (const [candidateId, processedAt] of this.recentlyProcessedMessageIds) {\n\t\t\tif (now - processedAt > MessageManager.PROCESSED_MESSAGE_TTL_MS) {\n\t\t\t\tthis.recentlyProcessedMessageIds.delete(candidateId);\n\t\t\t}\n\t\t}\n\n\t\tif (this.recentlyProcessedMessageIds.has(messageId)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.recentlyProcessedMessageIds.set(messageId, now);\n\t\treturn true;\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\t// this filtering is already done in setupEventListeners\n\t\t/*\n if (\n (this.discordSettings.allowedChannelIds && this.discordSettings.allowedChannelIds.length) &&\n !this.discordSettings.allowedChannelIds.some((id: string) => id === message.channel.id)\n ) {\n return;\n }\n */\n\n\t\tconst clientUser = this.client.user;\n\t\tif (\n\t\t\tmessage.interaction ||\n\t\t\t(clientUser && message.author.id === clientUser.id)\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.discordSettings.shouldIgnoreBotMessages && message.author?.bot) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.id && !this.markMessageAsProcessing(message.id)) {\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tmessageId: message.id,\n\t\t\t\t},\n\t\t\t\t\"Skipping duplicate Discord message\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// DM policy check - applies access control policies for direct messages\n\t\tif (message.channel.type === DiscordChannelType.DM) {\n\t\t\tconst userId = message.author.id;\n\t\t\tif (this.discordSettings.shouldIgnoreDirectMessages) {\n\t\t\t\tconst staticallyAllowed =\n\t\t\t\t\tthis.discordSettings.allowFrom?.includes(userId) === true;\n\t\t\t\tconst dynamicallyAllowed = await isInAllowlist(\n\t\t\t\t\tthis.runtime,\n\t\t\t\t\t\"discord\",\n\t\t\t\t\tuserId,\n\t\t\t\t);\n\t\t\t\tif (!staticallyAllowed && !dynamicallyAllowed) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst accessCheck = await this.checkDmAccess(message);\n\t\t\tif (!accessCheck.allowed) {\n\t\t\t\t// If a reply message was generated (new pairing request), send it\n\t\t\t\tif (accessCheck.replyMessage) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait message.author.send(accessCheck.replyMessage);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\t\tuserId: message.author.id,\n\t\t\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"Failed to send pairing reply\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tconst isBotMentioned = !!(\n\t\t\tclientUser?.id && message.mentions.users?.has(clientUser.id)\n\t\t);\n\t\tconst isReplyToBot =\n\t\t\t!!message.reference?.messageId &&\n\t\t\tmessage.mentions.repliedUser?.id === clientUser?.id;\n\t\tconst mentionedOtherUsers = message.mentions.users\n\t\t\t? Array.from(message.mentions.users.values()).some(\n\t\t\t\t\t(user) => user.id !== clientUser?.id && user.id !== message.author.id,\n\t\t\t\t)\n\t\t\t: false;\n\t\tconst isReplyToOtherUser =\n\t\t\t!!message.reference?.messageId &&\n\t\t\t!!message.mentions.repliedUser?.id &&\n\t\t\tmessage.mentions.repliedUser.id !== clientUser?.id &&\n\t\t\tmessage.mentions.repliedUser.id !== message.author.id;\n\t\tconst isInThread = message.channel.isThread();\n\t\tconst isDM = message.channel.type === DiscordChannelType.DM;\n\t\tconst strictModeEnabled =\n\t\t\tthis.discordSettings.shouldRespondOnlyToMentions === true;\n\t\tconst replyToMode = normalizeReplyToMode(this.discordSettings.replyToMode);\n\t\tconst outboundReplyToMessageId =\n\t\t\t!isDM && replyToMode !== \"off\" && (isBotMentioned || isReplyToBot)\n\t\t\t\t? message.id\n\t\t\t\t: undefined;\n\t\tconst strictModeShouldProcess = isDM || isBotMentioned || isReplyToBot;\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 =\n\t\t\tmessage.member?.displayName ??\n\t\t\tmessage.author.globalName ??\n\t\t\tmessage.author.displayName ??\n\t\t\tmessage.author.username;\n\t\tconst channelId = message.channel.id;\n\t\tconst roomId = createUniqueUuid(this.runtime, channelId);\n\t\tconst roomName =\n\t\t\tmessage.guild &&\n\t\t\t\"name\" in message.channel &&\n\t\t\ttypeof message.channel.name === \"string\"\n\t\t\t\t? message.channel.name\n\t\t\t\t: name || userName;\n\n\t\t// Determine channel type and server ID for ensureConnection\n\t\t// messageServerId is a Discord snowflake string, converted to UUID when needed\n\t\tlet type: ChannelType;\n\t\tlet messageServerId: 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\tif (type === null) {\n\t\t\t\t// usually a forum type post\n\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tchannelId: message.channel.id,\n\t\t\t\t\t},\n\t\t\t\t\t\"Null channel type\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tmessageServerId = guild.id;\n\t\t} else {\n\t\t\ttype = ChannelType.DM;\n\t\t\tmessageServerId = message.channel.id;\n\t\t}\n\n\t\ttry {\n\t\t\tlet { processedContent, attachments } =\n\t\t\t\tawait this.processMessage(message);\n\t\t\t// Audio attachments already processed in processMessage via attachmentManager\n\n\t\t\tif (this.envelopeEnabled && processedContent) {\n\t\t\t\ttry {\n\t\t\t\t\tconst envelope = await formatInboundEnvelope(\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t\tprocessedContent,\n\t\t\t\t\t);\n\t\t\t\t\tprocessedContent = envelope.formattedContent;\n\t\t\t\t} catch {\n\t\t\t\t\t// Envelope formatting is best-effort only.\n\t\t\t\t}\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\t// Users often mention a teammate and then ask the bot by name in the\n\t\t\t// same message. Only short-circuit these messages when the bot is not\n\t\t\t// also clearly addressed.\n\t\t\tconst explicitlyAddressesBotByName = textMentionsAnyName(\n\t\t\t\tprocessedContent,\n\t\t\t\t[\n\t\t\t\t\tthis.runtime.character.name,\n\t\t\t\t\tthis.runtime.character.username,\n\t\t\t\t\tclientUser?.globalName,\n\t\t\t\t\tclientUser?.username,\n\t\t\t\t],\n\t\t\t);\n\t\t\tconst ignoresOtherTarget =\n\t\t\t\t!isDM &&\n\t\t\t\t!isBotMentioned &&\n\t\t\t\t!isReplyToBot &&\n\t\t\t\t!explicitlyAddressesBotByName &&\n\t\t\t\t(mentionedOtherUsers || isReplyToOtherUser);\n\n\t\t\t// Use the service's buildMemoryFromMessage method with pre-processed content\n\t\t\tconst newMessage = await this.discordService.buildMemoryFromMessage(\n\t\t\t\tmessage,\n\t\t\t\t{\n\t\t\t\t\tprocessedContent,\n\t\t\t\t\tprocessedAttachments: attachments,\n\t\t\t\t\textraContent: {\n\t\t\t\t\t\tmentionContext: {\n\t\t\t\t\t\t\tisMention: isBotMentioned,\n\t\t\t\t\t\t\tisReply: isReplyToBot,\n\t\t\t\t\t\t\tisThread: isInThread,\n\t\t\t\t\t\t\tmentionType: isBotMentioned\n\t\t\t\t\t\t\t\t? \"platform_mention\"\n\t\t\t\t\t\t\t\t: isReplyToBot\n\t\t\t\t\t\t\t\t\t? \"reply\"\n\t\t\t\t\t\t\t\t\t: isInThread\n\t\t\t\t\t\t\t\t\t\t? \"thread\"\n\t\t\t\t\t\t\t\t\t\t: \"none\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\textraMetadata: {\n\t\t\t\t\t\t// Reply attribution for cross-agent filtering\n\t\t\t\t\t\t// WHY: When user replies to another bot's message, we need to know\n\t\t\t\t\t\t// so other agents can ignore it (only the replied-to agent should respond)\n\t\t\t\t\t\treplyToAuthor: message.mentions.repliedUser\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tid: message.mentions.repliedUser.id,\n\t\t\t\t\t\t\t\t\tdisplayName:\n\t\t\t\t\t\t\t\t\t\tmessage.mentions.repliedUser.globalName ??\n\t\t\t\t\t\t\t\t\t\tmessage.mentions.repliedUser.username,\n\t\t\t\t\t\t\t\t\tusername: message.mentions.repliedUser.username,\n\t\t\t\t\t\t\t\t\tisBot: message.mentions.repliedUser.bot,\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\treplyToMessageId: message.reference?.messageId\n\t\t\t\t\t\t\t? createUniqueUuid(this.runtime, message.reference.messageId)\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\treplyToExternalMessageId: message.reference?.messageId,\n\t\t\t\t\t\treplyToSenderId: message.mentions.repliedUser?.id,\n\t\t\t\t\t\treplyToSenderName:\n\t\t\t\t\t\t\tmessage.mentions.repliedUser?.globalName ??\n\t\t\t\t\t\t\tmessage.mentions.repliedUser?.username,\n\t\t\t\t\t\treplyToSenderUserName: message.mentions.repliedUser?.username,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (!newMessage) {\n\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tmessageId: message.id,\n\t\t\t\t\t},\n\t\t\t\t\t\"Failed to build memory from message\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait this.runtime.ensureConnection({\n\t\t\t\tentityId: newMessage.entityId,\n\t\t\t\troomId,\n\t\t\t\troomName,\n\t\t\t\tuserName,\n\t\t\t\tname,\n\t\t\t\tsource: \"discord\",\n\t\t\t\tchannelId: message.channel.id,\n\t\t\t\t// Convert Discord snowflake to UUID (see service.ts header for why stringToUuid not asUUID)\n\t\t\t\tmessageServerId: messageServerId\n\t\t\t\t\t? stringToUuid(messageServerId)\n\t\t\t\t\t: undefined,\n\t\t\t\ttype,\n\t\t\t\tworldId: createUniqueUuid(this.runtime, messageServerId ?? roomId),\n\t\t\t\tworldName: message.guild?.name,\n\t\t\t\t// Preserve the raw Discord user id in source metadata for role and allowlist checks.\n\t\t\t\tuserId: message.author.id as unknown as UUID,\n\t\t\t\tmetadata: buildDiscordWorldMetadata(\n\t\t\t\t\tthis.runtime,\n\t\t\t\t\tmessage.guild?.ownerId ?? undefined,\n\t\t\t\t),\n\t\t\t});\n\n\t\t\tif (\n\t\t\t\t!this.discordSettings.autoReply ||\n\t\t\t\tlifeOpsPassiveConnectorsEnabled(this.runtime)\n\t\t\t) {\n\t\t\t\tawait this.persistInboundMemory(newMessage);\n\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tchannelId: message.channel.id,\n\t\t\t\t\t},\n\t\t\t\t\t\"Auto-reply disabled; message ingested without response\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (ignoresOtherTarget) {\n\t\t\t\tawait this.persistInboundMemory(newMessage);\n\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tchannelId: message.channel.id,\n\t\t\t\t\t},\n\t\t\t\t\t\"Ignoring message that targets another mentioned user\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (strictModeEnabled && !strictModeShouldProcess) {\n\t\t\t\tawait this.persistInboundMemory(newMessage);\n\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tchannelId: message.channel.id,\n\t\t\t\t\t},\n\t\t\t\t\t\"Strict mode: ignoring message (no mention or reply)\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (strictModeEnabled) {\n\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tchannelId: message.channel.id,\n\t\t\t\t\t},\n\t\t\t\t\t\"Strict mode: processing message\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst canSendResult = canSendMessage(message.channel);\n\t\t\tif (!canSendResult.canSend) {\n\t\t\t\tawait this.persistInboundMemory(newMessage);\n\t\t\t\treturn this.runtime.logger.warn(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tchannelId: message.channel.id,\n\t\t\t\t\t\treason: canSendResult.reason,\n\t\t\t\t\t},\n\t\t\t\t\t\"Cannot send message to channel\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst messageId = newMessage.id;\n\t\t\tconst channel = message.channel as TextChannel;\n\t\t\tconst typingController = createTypingController(channel);\n\t\t\tconst clientUserId = this.client.user?.id;\n\t\t\tconst useReactions = shouldShowStatusReaction(\n\t\t\t\tthis.statusReactionScope,\n\t\t\t\tmessage,\n\t\t\t\tclientUserId,\n\t\t\t);\n\t\t\tconst statusReactions = useReactions\n\t\t\t\t? createStatusReactionController(message)\n\t\t\t\t: null;\n\t\t\tconst draftStream = this.draftStreamingEnabled\n\t\t\t\t? createDraftStreamController({\n\t\t\t\t\t\tlog: (entry) =>\n\t\t\t\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t\t\t\t{ src: \"plugin:discord\", agentId: this.runtime.agentId },\n\t\t\t\t\t\t\t\tentry,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\twarn: (entry) =>\n\t\t\t\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t\t\t\t{ src: \"plugin:discord\", agentId: this.runtime.agentId },\n\t\t\t\t\t\t\t\tentry,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t})\n\t\t\t\t: null;\n\t\t\tlet typingStarted = false;\n\t\t\tlet responseEmitted = false;\n\t\t\tlet generationTimedOut = false;\n\t\t\tconst generationTimeoutMs = Math.max(\n\t\t\t\t30_000,\n\t\t\t\tNumber.parseInt(\n\t\t\t\t\tString(\n\t\t\t\t\t\tthis.runtime.getSetting(\"DISCORD_GENERATION_TIMEOUT_MS\") ??\n\t\t\t\t\t\t\tthis.runtime.getSetting(\"MESSAGE_TIMEOUT_MS\") ??\n\t\t\t\t\t\t\t\"120000\",\n\t\t\t\t\t),\n\t\t\t\t\t10,\n\t\t\t\t) || 120_000,\n\t\t\t);\n\n\t\t\tconst finalizePendingDraft = async () => {\n\t\t\t\tif (draftStream?.isStarted() && !draftStream.isDone()) {\n\t\t\t\t\tawait draftStream.finalize(\"\");\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst abortPendingDraft = async () => {\n\t\t\t\tif (draftStream?.isStarted() && !draftStream.isDone()) {\n\t\t\t\t\tawait draftStream.abort(\n\t\t\t\t\t\t\"An error occurred while generating the response.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst sendFailureReply = async (text: string) => {\n\t\t\t\ttry {\n\t\t\t\t\tawait channel.send({\n\t\t\t\t\t\tcontent: text,\n\t\t\t\t\t\t...(outboundReplyToMessageId && replyToMode !== \"off\"\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\treply: {\n\t\t\t\t\t\t\t\t\t\tmessageReference: outboundReplyToMessageId,\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});\n\t\t\t\t\tresponseEmitted = true;\n\t\t\t\t} catch (sendError) {\n\t\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\tsendError instanceof Error\n\t\t\t\t\t\t\t\t\t? sendError.message\n\t\t\t\t\t\t\t\t\t: String(sendError),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Failed to send Discord failure reply\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tif (draftStream) {\n\t\t\t\tawait draftStream.start(channel, outboundReplyToMessageId, replyToMode);\n\t\t\t}\n\t\t\t// Typing indicator is deferred until the runtime actually invokes the\n\t\t\t// handler callback (see the `typingStarted` guard further down). This\n\t\t\t// avoids showing \"Eliza is typing…\" for messages the agent decides to\n\t\t\t// IGNORE/NONE, and lines up with the message-service preamble that\n\t\t\t// fires the callback the moment we commit to responding.\n\n\t\t\tstatusReactions?.setQueued();\n\t\t\tstatusReactions?.setThinking();\n\n\t\t\tconst callback: HandlerCallback = async (content: Content) => {\n\t\t\t\ttry {\n\t\t\t\t\tif (generationTimedOut) {\n\t\t\t\t\t\treturn [];\n\t\t\t\t\t}\n\t\t\t\t\t// target is set but not addressed to us handling\n\t\t\t\t\tif (\n\t\t\t\t\t\tcontent.target &&\n\t\t\t\t\t\ttypeof content.target === \"string\" &&\n\t\t\t\t\t\tcontent.target.toLowerCase() !== \"discord\"\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn [];\n\t\t\t\t\t}\n\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\n\t\t\t\t\tif (typeof content.text === \"string\" && content.text.length > 0) {\n\t\t\t\t\t\tcontent.text = stripReasoningTags(content.text);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst textContent = normalizeDiscordMessageText(content.text);\n\t\t\t\t\tconst hasText = textContent.trim().length > 0;\n\t\t\t\t\tconst attachmentCount = Array.isArray(content.attachments)\n\t\t\t\t\t\t? content.attachments.filter((media) => Boolean(media?.url)).length\n\t\t\t\t\t\t: 0;\n\n\t\t\t\t\tif (!hasText && attachmentCount === 0) {\n\t\t\t\t\t\treturn [];\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!typingStarted) {\n\t\t\t\t\t\ttypingStarted = true;\n\t\t\t\t\t\ttypingController.start();\n\t\t\t\t\t}\n\n\t\t\t\t\t// Dedup: error when the runtime emits identical text\n\t\t\t\t\t// twice in response to the same inbound message (e.g.\n\t\t\t\t\t// post-action continuation repeating action output).\n\t\t\t\t\tif (hasText && content.inReplyTo) {\n\t\t\t\t\t\tconst dedupKey = `${content.inReplyTo}::${textContent.replace(/\\s+/g, \" \").trim()}`;\n\t\t\t\t\t\tconst callbackDedup = message as DiscordMessage & {\n\t\t\t\t\t\t\t_elizaSentReplyKeys?: Set<string>;\n\t\t\t\t\t\t};\n\t\t\t\t\t\tcallbackDedup._elizaSentReplyKeys ??= new Set();\n\t\t\t\t\t\tif (callbackDedup._elizaSentReplyKeys.has(dedupKey)) {\n\t\t\t\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\t\t\tmessageId: message.id,\n\t\t\t\t\t\t\t\t\ttextPreview: textContent\n\t\t\t\t\t\t\t\t\t\t.replace(/\\s+/g, \" \")\n\t\t\t\t\t\t\t\t\t\t.trim()\n\t\t\t\t\t\t\t\t\t\t.slice(0, 200),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"Suppressing duplicate callback reply with identical text\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn [];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcallbackDedup._elizaSentReplyKeys.add(dedupKey);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst files: AttachmentBuilder[] = [];\n\t\t\t\t\tif (content.attachments && content.attachments.length > 0) {\n\t\t\t\t\t\tfor (const media of content.attachments) {\n\t\t\t\t\t\t\tif (media.url) {\n\t\t\t\t\t\t\t\tconst fileName = getAttachmentFileName(media);\n\t\t\t\t\t\t\t\tfiles.push(\n\t\t\t\t\t\t\t\t\tnew AttachmentBuilder(media.url, { name: fileName }),\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\n\t\t\t\t\tlet messages: DiscordMessage[] = [];\n\t\t\t\t\tif (draftStream?.isStarted() && !draftStream.isDone()) {\n\t\t\t\t\t\tif (hasText || files.length === 0) {\n\t\t\t\t\t\t\tmessages = await draftStream.finalize(textContent);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tawait finalizePendingDraft();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (files.length > 0) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst attachmentMessage = await channel.send({\n\t\t\t\t\t\t\t\t\tfiles,\n\t\t\t\t\t\t\t\t\t...(outboundReplyToMessageId &&\n\t\t\t\t\t\t\t\t\t(replyToMode === \"all\" || !hasText)\n\t\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\t\treply: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tmessageReference: outboundReplyToMessageId,\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: {}),\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tmessages.push(attachmentMessage);\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\"Failed to send Discord attachments after draft finalize\",\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} else if (content && content.channelType === \"DM\") {\n\t\t\t\t\t\tconst user = await this.client.users.fetch(message.author.id);\n\t\t\t\t\t\tif (!user) {\n\t\t\t\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\t\t\tentityId: message.author.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"User not found for DM\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn [];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst dmMessage = await user.send({\n\t\t\t\t\t\t\tcontent: textContent,\n\t\t\t\t\t\t\tfiles: files.length > 0 ? files : undefined,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tmessages = [dmMessage];\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (!message.id) {\n\t\t\t\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t\t\t\t{ src: \"plugin:discord\", agentId: this.runtime.agentId },\n\t\t\t\t\t\t\t\t\"Cannot send message: message.id is missing\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn [];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmessages = await sendMessageInChunks(\n\t\t\t\t\t\t\tchannel,\n\t\t\t\t\t\t\ttextContent,\n\t\t\t\t\t\t\toutboundReplyToMessageId ?? \"\",\n\t\t\t\t\t\t\tfiles,\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\tthis.runtime,\n\t\t\t\t\t\t\treplyToMode,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst attemptedSend = hasText || attachmentCount > 0;\n\t\t\t\t\tif (attemptedSend && messages.length === 0) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\"Discord response callback completed without sending any messages\",\n\t\t\t\t\t\t);\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\t\t\t\t\t\t// Only attach files to the memory for the message that actually carries them\n\t\t\t\t\t\tconst hasAttachments = m.attachments?.size > 0;\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\tsource: \"discord\",\n\t\t\t\t\t\t\t\ttext: m.content || textContent || \" \",\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\t// Only include attachments for the message chunk that actually has them\n\t\t\t\t\t\t\t\tattachments:\n\t\t\t\t\t\t\t\t\thasAttachments && content.attachments\n\t\t\t\t\t\t\t\t\t\t? content.attachments\n\t\t\t\t\t\t\t\t\t\t: undefined,\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.createMemory(m, \"messages\");\n\t\t\t\t\t}\n\n\t\t\t\t\tresponseEmitted = memories.length > 0;\n\t\t\t\t\ttypingController.stop();\n\t\t\t\t\tstatusReactions?.setDone();\n\n\t\t\t\t\treturn memories;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Error handling message callback\",\n\t\t\t\t\t);\n\t\t\t\t\ttypingController.stop();\n\t\t\t\t\tstatusReactions?.setError();\n\t\t\t\t\tawait abortPendingDraft();\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst messagingAPI = getMessagingAPI(this.runtime);\n\t\t\tconst messageService = getMessageService(this.runtime);\n\t\t\tlet generationTimeoutHandle: ReturnType<typeof setTimeout> | undefined;\n\t\t\ttry {\n\t\t\t\tconst generationPromise = (async () => {\n\t\t\t\t\tif (messageService) {\n\t\t\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t\t\t{ src: \"plugin:discord\", agentId: this.runtime.agentId },\n\t\t\t\t\t\t\t\"Using messageService API\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tawait messageService.handleMessage(\n\t\t\t\t\t\t\tthis.runtime,\n\t\t\t\t\t\t\tnewMessage,\n\t\t\t\t\t\t\tcallback,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else if (messagingAPI?.handleMessage) {\n\t\t\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t\t\t{ src: \"plugin:discord\", agentId: this.runtime.agentId },\n\t\t\t\t\t\t\t\"Using messaging API handleMessage\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tawait messagingAPI.handleMessage(this.runtime.agentId, newMessage, {\n\t\t\t\t\t\t\tonResponse: callback,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if (messagingAPI?.sendMessage) {\n\t\t\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t\t\t{ src: \"plugin:discord\", agentId: this.runtime.agentId },\n\t\t\t\t\t\t\t\"Using messaging API sendMessage\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tawait messagingAPI.sendMessage(this.runtime.agentId, newMessage, {\n\t\t\t\t\t\t\tonResponse: callback,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t\t\t{ src: \"plugin:discord\", agentId: this.runtime.agentId },\n\t\t\t\t\t\t\t\"Using event-based message handling\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tawait this.runtime.emitEvent([EventType.MESSAGE_RECEIVED], {\n\t\t\t\t\t\t\truntime: this.runtime,\n\t\t\t\t\t\t\tmessage: newMessage,\n\t\t\t\t\t\t\tcallback,\n\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t})();\n\n\t\t\t\tconst timeoutPromise = new Promise<never>((_, reject) => {\n\t\t\t\t\tgenerationTimeoutHandle = setTimeout(() => {\n\t\t\t\t\t\tgenerationTimedOut = true;\n\t\t\t\t\t\treject(\n\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t`Discord generation timeout after ${generationTimeoutMs}ms`,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}, generationTimeoutMs);\n\t\t\t\t});\n\n\t\t\t\tgenerationPromise.catch(() => {});\n\t\t\t\tawait Promise.race([generationPromise, timeoutPromise]);\n\t\t\t} catch (generationError) {\n\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tmessageId: message.id,\n\t\t\t\t\t\ttimeoutMs: generationTimeoutMs,\n\t\t\t\t\t\terror:\n\t\t\t\t\t\t\tgenerationError instanceof Error\n\t\t\t\t\t\t\t\t? generationError.message\n\t\t\t\t\t\t\t\t: String(generationError),\n\t\t\t\t\t},\n\t\t\t\t\t\"Discord generation failed or timed out\",\n\t\t\t\t);\n\t\t\t\ttypingController.stop();\n\t\t\t\tstatusReactions?.setError();\n\t\t\t\tawait abortPendingDraft();\n\n\t\t\t\tif (!responseEmitted) {\n\t\t\t\t\tawait sendFailureReply(\n\t\t\t\t\t\tgenerationTimedOut\n\t\t\t\t\t\t\t? \"I timed out while generating that reply. Please retry.\"\n\t\t\t\t\t\t\t: \"I hit a provider issue while generating the reply. Please retry.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t} finally {\n\t\t\t\tif (generationTimeoutHandle) {\n\t\t\t\t\tclearTimeout(generationTimeoutHandle);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!responseEmitted) {\n\t\t\t\ttypingController.stop();\n\t\t\t\tstatusReactions?.setDone();\n\t\t\t\tawait finalizePendingDraft();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error handling message\",\n\t\t\t);\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\tif (message.embeds?.length) {\n\t\t\tfor (const i in message.embeds) {\n\t\t\t\tconst embed = message.embeds[i];\n\t\t\t\t// type: rich\n\t\t\t\tprocessedContent += `\\nEmbed #${parseInt(i, 10) + 1}:\\n`;\n\t\t\t\tprocessedContent += ` Title:${embed.title ?? \"(none)\"}\\n`;\n\t\t\t\tprocessedContent += ` Description:${embed.description ?? \"(none)\"}\\n`;\n\t\t\t}\n\t\t}\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: RegExpExecArray | null = codeBlockRegex.exec(processedContent);\n\t\twhile (match !== null) {\n\t\t\tconst fullMatch = match[0];\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 =\n\t\t\t\t`code-${Date.now()}-${Math.floor(Math.random() * 1000)}`.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,\n\t\t\t\ttext: codeBlock,\n\t\t\t});\n\t\t\tprocessedContent = processedContent.replace(\n\t\t\t\tfullMatch,\n\t\t\t\t`Code Block (${attachmentId})`,\n\t\t\t);\n\t\t\tmatch = codeBlockRegex.exec(processedContent);\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\t// Extract and clean URLs from the message content\n\t\tconst urls = extractUrls(processedContent, this.runtime);\n\n\t\tfor (const url of urls) {\n\t\t\t// Use string literal type for getService, assume methods exist at runtime\n\t\t\tconst videoService = this.runtime.getService(ServiceType.VIDEO) as\n\t\t\t\t| ({\n\t\t\t\t\t\tisVideoUrl?: (url: string) => boolean;\n\t\t\t\t\t\tprocessVideo?: (\n\t\t\t\t\t\t\turl: string,\n\t\t\t\t\t\t\truntime: IAgentRuntime,\n\t\t\t\t\t\t) => Promise<{\n\t\t\t\t\t\t\ttitle: string;\n\t\t\t\t\t\t\tdescription: string;\n\t\t\t\t\t\t\ttext: string;\n\t\t\t\t\t\t}>;\n\t\t\t\t } & Service)\n\t\t\t\t| null;\n\t\t\tif (videoService?.isVideoUrl(url)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst videoInfo = await videoService.processVideo(url, this.runtime);\n\n\t\t\t\t\tattachments.push({\n\t\t\t\t\t\tid: `youtube-${Date.now()}`,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\ttitle: videoInfo.title,\n\t\t\t\t\t\tsource: \"YouTube\",\n\t\t\t\t\t\tdescription: videoInfo.description,\n\t\t\t\t\t\ttext: videoInfo.text,\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Handle video processing errors gracefully - the URL is still preserved in the message\n\t\t\t\t\tconst errorMsg =\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error);\n\t\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t\t`Failed to process video ${url}: ${errorMsg}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Use string literal type for getService, assume methods exist at runtime\n\t\t\t\tconst browserService = this.runtime.getService(ServiceType.BROWSER) as\n\t\t\t\t\t| ({\n\t\t\t\t\t\t\tgetPageContent?: (\n\t\t\t\t\t\t\t\turl: string,\n\t\t\t\t\t\t\t\truntime: IAgentRuntime,\n\t\t\t\t\t\t\t) => Promise<{ title?: string; description?: string }>;\n\t\t\t\t\t } & Service)\n\t\t\t\t\t| null;\n\t\t\t\tif (!browserService) {\n\t\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t\t{ src: \"plugin:discord\", agentId: this.runtime.agentId },\n\t\t\t\t\t\t\"Skipping URL enrichment because browser service is unavailable\",\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t\t`Fetching page content for cleaned URL: \"${url}\"`,\n\t\t\t\t\t);\n\t\t\t\t\tconst { title, description: summary } =\n\t\t\t\t\t\tawait browserService.getPageContent(url, this.runtime);\n\n\t\t\t\t\tattachments.push({\n\t\t\t\t\t\tid: `webpage-${Date.now()}`,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\ttitle: title || \"Web Page\",\n\t\t\t\t\t\tsource: \"Web\",\n\t\t\t\t\t\tdescription: summary,\n\t\t\t\t\t\ttext: summary,\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Silently handle browser errors (certificate issues, timeouts, dead sites, etc.)\n\t\t\t\t\t// The URL is still preserved in the message content, just without scraped metadata\n\t\t\t\t\tconst errorMsg =\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error);\n\t\t\t\t\tconst errorString = String(error);\n\n\t\t\t\t\t// Check for common expected failures that don't need logging\n\t\t\t\t\tconst isExpectedFailure =\n\t\t\t\t\t\terrorMsg.includes(\"ERR_CERT\") ||\n\t\t\t\t\t\terrorString.includes(\"ERR_CERT\") ||\n\t\t\t\t\t\terrorMsg.includes(\"Timeout\") ||\n\t\t\t\t\t\terrorString.includes(\"Timeout\") ||\n\t\t\t\t\t\terrorMsg.includes(\"ERR_NAME_NOT_RESOLVED\") ||\n\t\t\t\t\t\terrorString.includes(\"ERR_NAME_NOT_RESOLVED\") ||\n\t\t\t\t\t\terrorMsg.includes(\"ERR_HTTP_RESPONSE_CODE_FAILURE\") ||\n\t\t\t\t\t\terrorString.includes(\"ERR_HTTP_RESPONSE_CODE_FAILURE\");\n\n\t\t\t\t\tif (!isExpectedFailure) {\n\t\t\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t\t\t`Failed to fetch page content for ${url}: ${errorMsg}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\t// Expected failures are silently handled - no logging needed\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",
|
|
53
|
+
"import fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport {\n\ttype IAgentRuntime,\n\ttype Media,\n\tModelType,\n\ttype Service,\n\tServiceType,\n} from \"@elizaos/core\";\nimport { type Attachment, Collection } from \"discord.js\";\nimport ffmpeg from \"fluent-ffmpeg\";\nimport { generateSummary } from \"./utils\";\n\n/**\n * Class representing an Attachment Manager.\n */\nexport class AttachmentManager {\n\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\tprivate isImageDescriptionEnabled(): boolean {\n\t\tconst disabled = this.runtime.getSetting(\"DISABLE_IMAGE_DESCRIPTION\");\n\t\tif (\n\t\t\tdisabled === true ||\n\t\t\t(typeof disabled === \"string\" &&\n\t\t\t\t[\"1\", \"true\", \"yes\", \"on\"].includes(disabled.trim().toLowerCase()))\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn (\n\t\t\ttypeof this.runtime.getModel(ModelType.IMAGE_DESCRIPTION) === \"function\"\n\t\t);\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\tconst cached = this.attachmentCache.get(attachment.url);\n\t\tif (cached) {\n\t\t\treturn cached;\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 (attachment.contentType?.startsWith(\"video/\")) {\n\t\t\tmedia = await this.processVideoAttachment(attachment);\n\t\t} else {\n\t\t\tconst videoService = this.runtime.getService(ServiceType.VIDEO) as\n\t\t\t\t| ({ isVideoUrl?: (url: string) => boolean } & Service)\n\t\t\t\t| null;\n\t\t\tif (videoService?.isVideoUrl?.(attachment.url)) {\n\t\t\t\tmedia = await this.processVideoAttachment(attachment);\n\t\t\t} else {\n\t\t\t\tmedia = await this.processGenericAttachment(attachment);\n\t\t\t}\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\tlet audioFileName: string;\n\t\t\tlet audioMimeType: string;\n\n\t\t\tif (attachment.contentType?.startsWith(\"audio/\")) {\n\t\t\t\taudioBuffer = Buffer.from(audioVideoArrayBuffer);\n\t\t\t\taudioFileName = attachment.name || \"audio.mp3\";\n\t\t\t\taudioMimeType = attachment.contentType;\n\t\t\t} else if (attachment.contentType?.startsWith(\"video/mp4\")) {\n\t\t\t\taudioBuffer = await this.extractAudioFromMP4(audioVideoArrayBuffer);\n\t\t\t\taudioFileName = \"extracted_audio.mp3\";\n\t\t\t\taudioMimeType = \"audio/mpeg\";\n\t\t\t} else {\n\t\t\t\tthrow new Error(\"Unsupported audio/video format\");\n\t\t\t}\n\n\t\t\t// Convert Buffer to File object for transcription API\n\t\t\tconst audioBlob = new Blob([new Uint8Array(audioBuffer)], {\n\t\t\t\ttype: audioMimeType,\n\t\t\t});\n\t\t\tconst audioFile = new File([audioBlob], audioFileName, {\n\t\t\t\ttype: audioMimeType,\n\t\t\t});\n\n\t\t\t// Convert File to Buffer for transcription\n\t\t\tconst transcriptionBuffer = Buffer.from(await audioFile.arrayBuffer());\n\t\t\tconst transcription = await this.runtime.useModel(\n\t\t\t\tModelType.TRANSCRIPTION,\n\t\t\t\ttranscriptionBuffer,\n\t\t\t);\n\n\t\t\t// Assess transcription length before summarizing\n\t\t\tconst transcriptionLength = transcription?.length || 0;\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tattachmentId: attachment.id,\n\t\t\t\t\tcontentType: attachment.contentType,\n\t\t\t\t\ttranscriptionLength,\n\t\t\t\t},\n\t\t\t\t\"Assessing transcription length before summarization\",\n\t\t\t);\n\n\t\t\t// Only summarize if transcription is meaningful (not empty and long enough)\n\t\t\tlet title: string | undefined;\n\t\t\tlet description: string | undefined;\n\n\t\t\tif (!transcription || transcriptionLength === 0) {\n\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tattachmentId: attachment.id,\n\t\t\t\t\t},\n\t\t\t\t\t\"Transcription is empty, skipping summarization\",\n\t\t\t\t);\n\t\t\t\ttitle = undefined;\n\t\t\t\tdescription =\n\t\t\t\t\t\"User-uploaded audio/video attachment (no transcription available)\";\n\t\t\t} else if (transcriptionLength < 1000) {\n\t\t\t\t// Short transcriptions don't benefit from summarization\n\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tattachmentId: attachment.id,\n\t\t\t\t\t\ttranscriptionLength,\n\t\t\t\t\t},\n\t\t\t\t\t\"Transcription is short, skipping summarization\",\n\t\t\t\t);\n\t\t\t\ttitle = undefined;\n\t\t\t\tdescription = transcription;\n\t\t\t} else {\n\t\t\t\t// Transcription is long enough to benefit from summarization\n\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tattachmentId: attachment.id,\n\t\t\t\t\t\ttranscriptionLength,\n\t\t\t\t\t},\n\t\t\t\t\t\"Summarizing transcription\",\n\t\t\t\t);\n\t\t\t\tconst summary = await generateSummary(this.runtime, transcription);\n\t\t\t\ttitle = summary.title;\n\t\t\t\tdescription = summary.description;\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\tthis.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tattachmentId: attachment.id,\n\t\t\t\t\tcontentType: attachment.contentType,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error processing audio/video attachment\",\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: \"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 fluent-ffmpeg to extract the audio stream from the MP4 data\n\t\t// and convert it to MP3 format\n\t\tconst tmpDir = os.tmpdir();\n\t\tconst timestamp = Date.now();\n\t\tconst tempMP4File = path.join(tmpDir, `discord_video_${timestamp}.mp4`);\n\t\tconst tempAudioFile = path.join(tmpDir, `discord_audio_${timestamp}.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// Check if file has audio stream using ffprobe\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tffmpeg.ffprobe(tempMP4File, (err, metadata) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!metadata.streams || !Array.isArray(metadata.streams)) {\n\t\t\t\t\t\treject(\n\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\"File metadata does not contain valid streams information\",\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst hasAudio = metadata.streams.some(\n\t\t\t\t\t\t(stream) => stream.codec_type === \"audio\",\n\t\t\t\t\t);\n\t\t\t\t\tif (!hasAudio) {\n\t\t\t\t\t\treject(new Error(\"File does not contain any audio streams\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tresolve();\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\ttempMP4File,\n\t\t\t\t\ttempAudioFile,\n\t\t\t\t},\n\t\t\t\t\"Extracting audio from MP4\",\n\t\t\t);\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.noVideo() // Disable video output\n\t\t\t\t\t.audioCodec(\"libmp3lame\") // Set audio codec to MP3\n\t\t\t\t\t.toFormat(\"mp3\") // Explicitly set output format\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.output(tempAudioFile)\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\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\taudioDataSize: audioData.length,\n\t\t\t\t},\n\t\t\t\t\"Successfully extracted audio from MP4\",\n\t\t\t);\n\n\t\t\treturn audioData;\n\t\t} finally {\n\t\t\t// Clean up the temporary files\n\t\t\ttry {\n\t\t\t\tif (fs.existsSync(tempMP4File)) {\n\t\t\t\t\tfs.unlinkSync(tempMP4File);\n\t\t\t\t}\n\t\t\t\tif (fs.existsSync(tempAudioFile)) {\n\t\t\t\t\tfs.unlinkSync(tempAudioFile);\n\t\t\t\t}\n\t\t\t} catch (cleanupError) {\n\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\terror:\n\t\t\t\t\t\t\tcleanupError instanceof Error\n\t\t\t\t\t\t\t\t? cleanupError.message\n\t\t\t\t\t\t\t\t: String(cleanupError),\n\t\t\t\t\t},\n\t\t\t\t\t\"Failed to cleanup temp files\",\n\t\t\t\t);\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 pdfService = this.runtime.getService(ServiceType.PDF) as\n\t\t\t\t| ({ convertPdfToText: (buffer: Buffer) => Promise<string> } & Service)\n\t\t\t\t| null;\n\t\t\tif (!pdfService) {\n\t\t\t\tthrow new Error(\"PDF service not found\");\n\t\t\t}\n\t\t\tconst text = await pdfService.convertPdfToText(Buffer.from(pdfBuffer));\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tattachmentId: attachment.id,\n\t\t\t\t\ttextLength: text?.length,\n\t\t\t\t},\n\t\t\t\t\"Summarizing PDF content\",\n\t\t\t);\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,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tthis.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tattachmentId: attachment.id,\n\t\t\t\t\tcontentType: attachment.contentType,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error processing PDF attachment\",\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: \"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\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tattachmentId: attachment.id,\n\t\t\t\t\ttextLength: text?.length,\n\t\t\t\t},\n\t\t\t\t\"Summarizing plaintext content\",\n\t\t\t);\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,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tthis.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tattachmentId: attachment.id,\n\t\t\t\t\tcontentType: attachment.contentType,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error processing plaintext attachment\",\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: \"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\tif (!this.isImageDescriptionEnabled()) {\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tattachmentId: attachment.id,\n\t\t\t\t\tcontentType: attachment.contentType,\n\t\t\t\t},\n\t\t\t\t\"Skipping image attachment description because IMAGE_DESCRIPTION is not available\",\n\t\t\t);\n\t\t\treturn this.createFallbackImageMedia(attachment);\n\t\t}\n\n\t\ttry {\n\t\t\tconst { description, title } = await this.runtime.useModel(\n\t\t\t\tModelType.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\tthis.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tattachmentId: attachment.id,\n\t\t\t\t\tcontentType: attachment.contentType,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error processing image attachment\",\n\t\t\t);\n\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(ServiceType.VIDEO) as\n\t\t\t| ({\n\t\t\t\t\tisVideoUrl?: (url: string) => boolean;\n\t\t\t\t\tprocessVideo?: (\n\t\t\t\t\t\turl: string,\n\t\t\t\t\t\truntime: IAgentRuntime,\n\t\t\t\t\t) => Promise<{\n\t\t\t\t\t\ttitle: string;\n\t\t\t\t\t\tdescription: string;\n\t\t\t\t\t\ttext: string;\n\t\t\t\t\t}>;\n\t\t\t } & Service)\n\t\t\t| null;\n\n\t\tif (!videoService) {\n\t\t\treturn {\n\t\t\t\tid: attachment.id,\n\t\t\t\turl: attachment.url,\n\t\t\t\ttitle: \"Video Attachment (Service Unavailable)\",\n\t\t\t\tsource: \"Video\",\n\t\t\t\tdescription:\n\t\t\t\t\t\"Could not process video attachment because the required service is not available.\",\n\t\t\t\ttext: \"Video content not available\",\n\t\t\t};\n\t\t}\n\n\t\tif (\n\t\t\ttypeof videoService.isVideoUrl === \"function\" &&\n\t\t\tvideoService.isVideoUrl(attachment.url)\n\t\t) {\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",
|
|
54
|
+
"export type BreakPreference = \"paragraph\" | \"newline\" | \"sentence\";\n\nexport interface DraftChunkConfig {\n\tminChars: number;\n\tmaxChars: number;\n\tbreakPreference: BreakPreference;\n}\n\nexport const DEFAULT_DRAFT_CHUNK_CONFIG: DraftChunkConfig = {\n\tminChars: 80,\n\tmaxChars: 1900,\n\tbreakPreference: \"sentence\",\n};\n\nexport function findBreakPoint(\n\ttext: string,\n\tmaxLen: number,\n\tbreakPreference: BreakPreference = \"sentence\",\n): number {\n\tif (text.length <= maxLen) {\n\t\treturn text.length;\n\t}\n\n\tconst region = text.slice(0, maxLen);\n\tif (breakPreference === \"paragraph\" || breakPreference === \"newline\") {\n\t\tconst paragraphBreak = region.lastIndexOf(\"\\n\\n\");\n\t\tif (paragraphBreak > maxLen * 0.3) {\n\t\t\treturn paragraphBreak + 2;\n\t\t}\n\t}\n\n\tif (breakPreference !== \"sentence\") {\n\t\tconst newlineBreak = region.lastIndexOf(\"\\n\");\n\t\tif (newlineBreak > maxLen * 0.3) {\n\t\t\treturn newlineBreak + 1;\n\t\t}\n\t}\n\n\tconst sentenceMatch = region.match(/[.!?]\\s+(?=[A-Z])/g);\n\tif (sentenceMatch) {\n\t\tconst lastSentenceEnd = region.lastIndexOf(\n\t\t\tsentenceMatch[sentenceMatch.length - 1],\n\t\t);\n\t\tif (lastSentenceEnd > maxLen * 0.3) {\n\t\t\treturn lastSentenceEnd + sentenceMatch[sentenceMatch.length - 1].length;\n\t\t}\n\t}\n\n\tconst simpleSentenceBreak = region.lastIndexOf(\". \");\n\tif (simpleSentenceBreak > maxLen * 0.3) {\n\t\treturn simpleSentenceBreak + 2;\n\t}\n\n\tconst wordBreak = region.lastIndexOf(\" \");\n\tif (wordBreak > maxLen * 0.5) {\n\t\treturn wordBreak + 1;\n\t}\n\n\treturn maxLen;\n}\n",
|
|
55
|
+
"import type { Message as DiscordMessage, TextChannel } from \"discord.js\";\nimport {\n\tDEFAULT_DRAFT_CHUNK_CONFIG,\n\ttype DraftChunkConfig,\n\tfindBreakPoint,\n} from \"./draft-chunking\";\n\ntype DraftReplyToMode = \"off\" | \"first\" | \"all\";\n\nexport interface DraftStreamOptions {\n\tthrottleMs?: number;\n\tminInitialChars?: number;\n\tmaxChars?: number;\n\tchunkConfig?: Partial<DraftChunkConfig>;\n\tlog?: (msg: string) => void;\n\twarn?: (msg: string) => void;\n}\n\nexport interface DraftStreamController {\n\tstart: (\n\t\tchannel: TextChannel,\n\t\treplyToMessageId?: string,\n\t\treplyToMode?: DraftReplyToMode,\n\t) => Promise<DiscordMessage | null>;\n\tupdate: (text: string) => void;\n\tfinalize: (text: string) => Promise<DiscordMessage[]>;\n\tabort: (reason?: string) => Promise<void>;\n\tmessageId: () => string | undefined;\n\tisStarted: () => boolean;\n\tisDone: () => boolean;\n}\n\nconst DEFAULT_THROTTLE_MS = 1_200;\nconst DEFAULT_MIN_INITIAL_CHARS = 40;\nconst DISCORD_MAX_CHARS = 2_000;\n\nexport function createDraftStreamController(\n\toptions: DraftStreamOptions = {},\n): DraftStreamController {\n\tconst throttleMs = Math.max(250, options.throttleMs ?? DEFAULT_THROTTLE_MS);\n\tconst minInitialChars = options.minInitialChars ?? DEFAULT_MIN_INITIAL_CHARS;\n\tconst maxChars = Math.min(options.maxChars ?? 1900, DISCORD_MAX_CHARS);\n\tconst log = options.log ?? (() => {});\n\tconst warn = options.warn ?? (() => {});\n\n\tlet channel: TextChannel | null = null;\n\tlet draftReplyToMessageId: string | undefined;\n\tlet draftReplyToMode: DraftReplyToMode = \"first\";\n\tlet lastSentText = \"\";\n\tlet lastSentMessage: DiscordMessage | null = null;\n\tconst sentMessages: DiscordMessage[] = [];\n\tlet pendingText: string | null = null;\n\tlet throttleTimer: ReturnType<typeof setTimeout> | null = null;\n\tlet started = false;\n\tlet done = false;\n\n\tconst clearThrottle = () => {\n\t\tif (throttleTimer) {\n\t\t\tclearTimeout(throttleTimer);\n\t\t\tthrottleTimer = null;\n\t\t}\n\t};\n\n\tconst sendSnapshot = async (text: string): Promise<boolean> => {\n\t\tif (done || !channel) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst trimmed = text.trimEnd();\n\t\tif (!trimmed) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst displayText =\n\t\t\ttrimmed.length > maxChars\n\t\t\t\t? `${trimmed.slice(0, maxChars - 3)}...`\n\t\t\t\t: trimmed;\n\t\tif (displayText === lastSentText) {\n\t\t\treturn true;\n\t\t}\n\n\t\ttry {\n\t\t\tconst sent = await channel.send({\n\t\t\t\tcontent: displayText,\n\t\t\t\t...(draftReplyToMessageId && draftReplyToMode !== \"off\"\n\t\t\t\t\t? {\n\t\t\t\t\t\t\treply: { messageReference: draftReplyToMessageId },\n\t\t\t\t\t\t}\n\t\t\t\t\t: {}),\n\t\t\t});\n\t\t\tlastSentMessage = sent;\n\t\t\tsentMessages.push(sent);\n\t\t\tlastSentText = displayText;\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : String(error);\n\t\t\twarn(`draft-stream: send failed: ${errorMessage}`);\n\t\t\treturn false;\n\t\t}\n\t};\n\n\tconst flush = async (): Promise<void> => {\n\t\tclearThrottle();\n\t\tif (pendingText !== null) {\n\t\t\tconst text = pendingText;\n\t\t\tpendingText = null;\n\t\t\tawait sendSnapshot(text);\n\t\t}\n\t};\n\n\tconst scheduleUpdate = (text: string) => {\n\t\tpendingText = text;\n\t\tif (!throttleTimer) {\n\t\t\tthrottleTimer = setTimeout(async () => {\n\t\t\t\tthrottleTimer = null;\n\t\t\t\tawait flush();\n\t\t\t}, throttleMs);\n\t\t}\n\t};\n\n\tconst start = async (\n\t\tnextChannel: TextChannel,\n\t\treplyToMessageId?: string,\n\t\treplyToMode: DraftReplyToMode = \"first\",\n\t): Promise<DiscordMessage | null> => {\n\t\tif (started) {\n\t\t\twarn(\"draft-stream: start() called twice, ignoring\");\n\t\t\treturn lastSentMessage;\n\t\t}\n\t\tstarted = true;\n\t\tchannel = nextChannel;\n\t\tdraftReplyToMessageId = replyToMessageId;\n\t\tdraftReplyToMode = replyToMode;\n\n\t\tlog(`draft-stream: started (append-only, throttle=${throttleMs}ms)`);\n\t\treturn null;\n\t};\n\n\tconst update = (text: string) => {\n\t\tif (done || !started) {\n\t\t\treturn;\n\t\t}\n\t\tif (!lastSentMessage && text.length < minInitialChars) {\n\t\t\treturn;\n\t\t}\n\t\tscheduleUpdate(text);\n\t};\n\n\tconst finalize = async (text: string): Promise<DiscordMessage[]> => {\n\t\tif (done) {\n\t\t\treturn sentMessages;\n\t\t}\n\n\t\tclearThrottle();\n\t\tpendingText = null;\n\n\t\tif (!started) {\n\t\t\twarn(\"draft-stream: finalize called before start\");\n\t\t\tdone = true;\n\t\t\treturn [];\n\t\t}\n\n\t\tconst trimmed = text.trimEnd();\n\t\tif (!trimmed) {\n\t\t\tdone = true;\n\t\t\treturn [];\n\t\t}\n\n\t\tif (trimmed.length <= maxChars) {\n\t\t\tawait sendSnapshot(trimmed);\n\t\t\tdone = true;\n\t\t\tlog(\"draft-stream: finalized (single message)\");\n\t\t\treturn sentMessages;\n\t\t}\n\n\t\tconst chunkConfig = {\n\t\t\t...DEFAULT_DRAFT_CHUNK_CONFIG,\n\t\t\t...options.chunkConfig,\n\t\t};\n\t\tconst breakPoint = findBreakPoint(\n\t\t\ttrimmed,\n\t\t\tmaxChars,\n\t\t\tchunkConfig.breakPreference,\n\t\t);\n\t\tconst firstChunk = trimmed.slice(0, breakPoint).trimEnd();\n\t\tlet remaining = trimmed.slice(breakPoint).trimStart();\n\n\t\tawait sendSnapshot(firstChunk);\n\n\t\twhile (remaining.length > 0 && channel) {\n\t\t\tconst nextBreak = findBreakPoint(\n\t\t\t\tremaining,\n\t\t\t\tmaxChars,\n\t\t\t\tchunkConfig.breakPreference,\n\t\t\t);\n\t\t\tconst chunk = remaining.slice(0, nextBreak).trimEnd();\n\t\t\tremaining = remaining.slice(nextBreak).trimStart();\n\t\t\tif (!chunk) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst overflowMessage = await channel.send({\n\t\t\t\t\tcontent: chunk,\n\t\t\t\t\t...(draftReplyToMessageId && draftReplyToMode === \"all\"\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\treply: { messageReference: draftReplyToMessageId },\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {}),\n\t\t\t\t});\n\t\t\t\tlastSentMessage = overflowMessage;\n\t\t\t\tsentMessages.push(overflowMessage);\n\t\t\t} catch (error) {\n\t\t\t\twarn(\n\t\t\t\t\t`draft-stream: overflow send failed: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tdone = true;\n\t\tlog(\"draft-stream: finalized (multi-message)\");\n\t\treturn sentMessages;\n\t};\n\n\tconst abort = async (reason?: string): Promise<void> => {\n\t\tif (done) {\n\t\t\treturn;\n\t\t}\n\t\tdone = true;\n\t\tclearThrottle();\n\t\tpendingText = null;\n\n\t\tif (!channel) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst errorText = reason\n\t\t\t? `⚠️ ${reason}`\n\t\t\t: \"⚠️ Response generation was interrupted.\";\n\t\ttry {\n\t\t\tawait channel.send({ content: errorText });\n\t\t} catch (error) {\n\t\t\twarn(\n\t\t\t\t`draft-stream: abort send failed: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\n\t\tlog(\"draft-stream: aborted\");\n\t};\n\n\treturn {\n\t\tstart,\n\t\tupdate,\n\t\tfinalize,\n\t\tabort,\n\t\tmessageId: () => lastSentMessage?.id,\n\t\tisStarted: () => started,\n\t\tisDone: () => done,\n\t};\n}\n",
|
|
56
|
+
"import {\n\tChannelType as DiscordChannelType,\n\ttype Message as DiscordMessage,\n\ttype TextChannel,\n\ttype ThreadChannel,\n} from \"discord.js\";\n\nexport type ChatType = \"dm\" | \"channel\" | \"thread\" | \"forum\";\n\nexport interface EnvelopeResult {\n\tformattedContent: string;\n\tchatType: ChatType;\n}\n\nconst WEEKDAYS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\n\nfunction formatTimestamp(timestamp: number | Date): string {\n\tconst date = timestamp instanceof Date ? timestamp : new Date(timestamp);\n\tconst weekday = WEEKDAYS[date.getDay()];\n\tconst month = String(date.getMonth() + 1).padStart(2, \"0\");\n\tconst day = String(date.getDate()).padStart(2, \"0\");\n\tconst year = date.getFullYear();\n\tconst hours = String(date.getHours()).padStart(2, \"0\");\n\tconst minutes = String(date.getMinutes()).padStart(2, \"0\");\n\n\tlet timezone = \"UTC\";\n\ttry {\n\t\ttimezone =\n\t\t\tdate\n\t\t\t\t.toLocaleTimeString(\"en-US\", { timeZoneName: \"short\" })\n\t\t\t\t.split(\" \")\n\t\t\t\t.pop() ?? \"UTC\";\n\t} catch {\n\t\t// Fall back to UTC.\n\t}\n\n\treturn `${weekday} ${month}/${day}/${year} ${hours}:${minutes} ${timezone}`;\n}\n\nfunction detectChatType(message: DiscordMessage): ChatType {\n\tconst channelType = message.channel.type;\n\tif (\n\t\tchannelType === DiscordChannelType.DM ||\n\t\tchannelType === DiscordChannelType.GroupDM\n\t) {\n\t\treturn \"dm\";\n\t}\n\n\tif (\n\t\tchannelType === DiscordChannelType.PublicThread ||\n\t\tchannelType === DiscordChannelType.PrivateThread ||\n\t\tchannelType === DiscordChannelType.AnnouncementThread\n\t) {\n\t\tconst thread = message.channel as ThreadChannel;\n\t\tif (thread.parent?.type === DiscordChannelType.GuildForum) {\n\t\t\treturn \"forum\";\n\t\t}\n\t\treturn \"thread\";\n\t}\n\n\treturn \"channel\";\n}\n\nfunction getSenderName(message: DiscordMessage): string {\n\tif (message.member?.nickname) {\n\t\treturn message.member.nickname;\n\t}\n\tif (message.author.globalName) {\n\t\treturn message.author.globalName;\n\t}\n\treturn message.author.displayName ?? message.author.username;\n}\n\nfunction buildChannelLabel(\n\tmessage: DiscordMessage,\n\tchatType: ChatType,\n): string {\n\tif (chatType === \"dm\") {\n\t\treturn \"DM\";\n\t}\n\n\tconst guildName = message.guild?.name;\n\tlet channelPart: string;\n\tif (chatType === \"thread\" || chatType === \"forum\") {\n\t\tconst thread = message.channel as ThreadChannel;\n\t\tchannelPart = `#${thread.parent?.name ?? \"unknown\"} › ${thread.name ?? \"thread\"}`;\n\t} else {\n\t\tconst channel = message.channel as TextChannel;\n\t\tchannelPart = `#${channel.name ?? message.channel.id}`;\n\t}\n\n\treturn guildName ? `${channelPart} | ${guildName}` : channelPart;\n}\n\nexport async function formatInboundEnvelope(\n\tmessage: DiscordMessage,\n\trawContent: string,\n): Promise<EnvelopeResult> {\n\tconst chatType = detectChatType(message);\n\tconst channelLabel = buildChannelLabel(message, chatType);\n\tconst senderName = getSenderName(message);\n\tconst timestamp = formatTimestamp(message.createdTimestamp ?? Date.now());\n\n\tlet replyContext = \"\";\n\tif (message.reference?.messageId) {\n\t\ttry {\n\t\t\tconst refMessage = await message.fetchReference();\n\t\t\tconst refAuthor =\n\t\t\t\trefMessage.author?.displayName ??\n\t\t\t\trefMessage.author?.username ??\n\t\t\t\t\"unknown\";\n\t\t\tconst refContent = refMessage.content ?? \"\";\n\t\t\tconst truncated =\n\t\t\t\trefContent.length > 200 ? `${refContent.slice(0, 200)}...` : refContent;\n\t\t\treplyContext = truncated\n\t\t\t\t? ` replying to @${refAuthor}:\\n> ${truncated}\\n`\n\t\t\t\t: ` replying to @${refAuthor}:\\n`;\n\t\t} catch {\n\t\t\t// Reply context is best-effort only.\n\t\t}\n\t}\n\n\tconst header = `[Discord ${channelLabel}] @${senderName} (${timestamp})`;\n\treturn {\n\t\tformattedContent: replyContext\n\t\t\t? `${header}${replyContext}${rawContent}`\n\t\t\t: `${header}: ${rawContent}`,\n\t\tchatType,\n\t};\n}\n",
|
|
57
|
+
"const REASONING_TAGS = [\n\t\"thinking\",\n\t\"reasoning\",\n\t\"reflection\",\n\t\"scratchpad\",\n\t\"thought\",\n\t\"antthinking\",\n] as const;\n\nconst SELF_CLOSING_ARTIFACTS_RE =\n\t/<(?:STOP|END|end_turn|eot_id)\\s*\\/?>|<\\|(?:end|stop|im_end|eot_id)\\|>/gi;\nconst QUICK_TAG_RE =\n\t/<\\/?(?:thinking|reasoning|reflection|scratchpad|thought|antthinking|final|STOP|END|end_turn)\\b|<\\|(?:end|stop|im_end)/i;\nconst CODE_BLOCK_RE = /```[\\s\\S]*?```/g;\nconst PLACEHOLDER_PREFIX = \"\\x00CB\";\n\nexport function stripReasoningTags(text: string): string {\n\tif (!text || !QUICK_TAG_RE.test(text)) {\n\t\treturn text;\n\t}\n\n\tconst codeBlocks: string[] = [];\n\tlet processed = text.replace(CODE_BLOCK_RE, (match) => {\n\t\tconst index = codeBlocks.length;\n\t\tcodeBlocks.push(match);\n\t\treturn `${PLACEHOLDER_PREFIX}${index}${PLACEHOLDER_PREFIX}`;\n\t});\n\n\tprocessed = processed.replace(SELF_CLOSING_ARTIFACTS_RE, \"\");\n\n\tfor (const tag of REASONING_TAGS) {\n\t\tconst paired = new RegExp(`<${tag}\\\\b[^>]*>([\\\\s\\\\S]*?)<\\\\/${tag}>`, \"gi\");\n\t\tprocessed = processed.replace(paired, \"\");\n\n\t\tconst unclosed = new RegExp(`<${tag}\\\\b[^>]*>[\\\\s\\\\S]*$`, \"gi\");\n\t\tprocessed = processed.replace(unclosed, \"\");\n\t}\n\n\tprocessed = processed.replace(/<final\\b[^>]*>([\\s\\S]*?)<\\/final>/gi, \"$1\");\n\n\tfor (let index = 0; index < codeBlocks.length; index++) {\n\t\tprocessed = processed.replace(\n\t\t\t`${PLACEHOLDER_PREFIX}${index}${PLACEHOLDER_PREFIX}`,\n\t\t\tcodeBlocks[index],\n\t\t);\n\t}\n\n\treturn processed.replace(/\\n{3,}/g, \"\\n\\n\").trim();\n}\n",
|
|
58
|
+
"import type { Message as DiscordMessage } from \"discord.js\";\n\nexport type StatusReactionScope = \"all\" | \"group-mentions\" | \"none\";\n\nexport interface StatusReactionController {\n\tsetQueued: () => void;\n\tsetThinking: () => void;\n\tsetDone: () => void;\n\tsetError: () => void;\n}\n\nconst EMOJI_QUEUED = \"⏳\";\nconst EMOJI_THINKING = \"🤔\";\nconst EMOJI_DONE = \"✅\";\nconst EMOJI_ERROR = \"❌\";\n\nexport function shouldShowStatusReaction(\n\tscope: StatusReactionScope,\n\tmessage: DiscordMessage,\n\tbotId: string | undefined,\n): boolean {\n\tif (scope === \"none\") {\n\t\treturn false;\n\t}\n\tif (scope === \"all\") {\n\t\treturn true;\n\t}\n\n\tif (!message.guild) {\n\t\treturn true;\n\t}\n\n\tconst isMentioned = Boolean(botId && message.mentions.users?.has(botId));\n\tconst isReplyToBot = message.mentions.repliedUser?.id === botId;\n\treturn isMentioned || isReplyToBot;\n}\n\nexport function createStatusReactionController(\n\tmessage: DiscordMessage,\n): StatusReactionController {\n\tlet currentEmoji: string | null = null;\n\tlet finished = false;\n\tlet chain: Promise<void> = Promise.resolve();\n\tconst botId = message.client?.user?.id;\n\n\tconst transition = (emoji: string, terminal = false) => {\n\t\tif (finished) {\n\t\t\treturn;\n\t\t}\n\t\tchain = chain.then(async () => {\n\t\t\tif (finished && !terminal) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tif (currentEmoji && currentEmoji !== emoji && botId) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst reaction = message.reactions.resolve(currentEmoji);\n\t\t\t\t\t\tif (reaction) {\n\t\t\t\t\t\t\tawait reaction.users.remove(botId);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Ignore missing permissions or already-removed reactions.\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tawait message.react(emoji);\n\t\t\t\tcurrentEmoji = emoji;\n\t\t\t} catch {\n\t\t\t\t// Reaction failures are non-critical.\n\t\t\t} finally {\n\t\t\t\tif (terminal) {\n\t\t\t\t\tfinished = true;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t};\n\n\treturn {\n\t\tsetQueued: () => transition(EMOJI_QUEUED),\n\t\tsetThinking: () => transition(EMOJI_THINKING),\n\t\tsetDone: () => transition(EMOJI_DONE, true),\n\t\tsetError: () => transition(EMOJI_ERROR, true),\n\t};\n}\n",
|
|
59
|
+
"import type { TextChannel } from \"discord.js\";\n\nexport interface TypingController {\n\tstart: () => void;\n\tstop: () => void;\n}\n\nconst HEARTBEAT_MS = 9_000;\nconst DEFAULT_MAX_DURATION_MS = 20 * 60 * 1000;\n\nexport function createTypingController(\n\tchannel: TextChannel,\n\tmaxDurationMs: number = DEFAULT_MAX_DURATION_MS,\n): TypingController {\n\tlet interval: ReturnType<typeof setInterval> | null = null;\n\tlet ttlTimeout: ReturnType<typeof setTimeout> | null = null;\n\tlet started = false;\n\tlet stopped = false;\n\n\tconst sendTyping = () => {\n\t\tif (stopped || typeof channel.sendTyping !== \"function\") {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconst result = channel.sendTyping();\n\t\t\tif (result && typeof result.catch === \"function\") {\n\t\t\t\tresult.catch(() => {\n\t\t\t\t\t// Typing failures are non-critical.\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// Typing failures are non-critical.\n\t\t}\n\t};\n\n\tconst stop = () => {\n\t\tif (stopped) {\n\t\t\treturn;\n\t\t}\n\t\tstopped = true;\n\t\tif (interval) {\n\t\t\tclearInterval(interval);\n\t\t\tinterval = null;\n\t\t}\n\t\tif (ttlTimeout) {\n\t\t\tclearTimeout(ttlTimeout);\n\t\t\tttlTimeout = null;\n\t\t}\n\t};\n\n\tconst start = () => {\n\t\tif (started || stopped) {\n\t\t\treturn;\n\t\t}\n\t\tstarted = true;\n\t\tsendTyping();\n\t\tinterval = setInterval(sendTyping, HEARTBEAT_MS);\n\t\tttlTimeout = setTimeout(stop, maxDurationMs);\n\t};\n\n\treturn { start, stop };\n}\n",
|
|
60
|
+
"import { EventEmitter } from \"node:events\";\nimport { pipeline, Readable, type Transform } from \"node:stream\";\nimport {\n\ttype AudioPlayer,\n\ttype AudioReceiveStream,\n\tcreateAudioPlayer,\n\tcreateAudioResource,\n\tentersState,\n\tgetVoiceConnections,\n\tjoinVoiceChannel,\n\tNoSubscriberBehavior,\n\tStreamType,\n\ttype VoiceConnection,\n\tVoiceConnectionStatus,\n} from \"@discordjs/voice\";\nimport {\n\tChannelType,\n\ttype Content,\n\tcreateUniqueUuid,\n\tEventType,\n\ttype HandlerCallback,\n\tlogger,\n\ttype Memory,\n\tModelType,\n\tstringToUuid,\n\ttype UUID,\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\";\n// See service.ts for detailed documentation on Discord ID handling.\n// Key point: Discord snowflake IDs (e.g., \"1253563208833433701\") are NOT valid UUIDs.\n// Use stringToUuid() to convert them, not asUUID() which would throw an error.\nimport type { ICompatRuntime } from \"./compat\";\nimport type { DiscordService } from \"./service\";\nimport { getMessageService, normalizeDiscordMessageText } from \"./utils\";\n\n// These values are chosen for compatibility with picovoice components\nconst DECODE_FRAME_SIZE = 1024;\nconst DECODE_SAMPLE_RATE = 16000;\n\n/**\n * Creates an opus decoder with fallback handling for different opus libraries\n * @param options - Decoder options including channels, rate, and frameSize\n * @returns An opus decoder instance or null if creation fails\n */\nfunction createOpusDecoder(options: {\n\tchannels: number;\n\trate: number;\n\tframeSize: number;\n}): Transform {\n\ttry {\n\t\t// First try to create decoder with prism-media\n\t\treturn new prism.opus.Decoder(options);\n\t} catch (error) {\n\t\t// Standalone function - no runtime context available\n\t\tlogger.warn(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t},\n\t\t\t\"Failed to create opus decoder\",\n\t\t);\n\n\t\t// Log available opus libraries for debugging\n\t\ttry {\n\t\t\tconst { generateDependencyReport } = require(\"@discordjs/voice\");\n\t\t\tconst report = generateDependencyReport();\n\t\t\tlogger.debug(\n\t\t\t\t{ src: \"plugin:discord:service:voice\", report },\n\t\t\t\t\"Voice dependency report\",\n\t\t\t);\n\t\t} catch (reportError) {\n\t\t\tlogger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\terror:\n\t\t\t\t\t\treportError instanceof Error\n\t\t\t\t\t\t\t? reportError.message\n\t\t\t\t\t\t\t: String(reportError),\n\t\t\t\t},\n\t\t\t\t\"Could not generate dependency report\",\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n}\n\n/**\n * Generates a WAV file header based on the provided audio parameters.\n * @param {number} audioLength - The length of the audio data in bytes.\n * @param {number} sampleRate - The sample rate of the audio.\n * @param {number} [channelCount=1] - The number of channels (default is 1).\n * @param {number} [bitsPerSample=16] - The number of bits per sample (default is 16).\n * @returns {Buffer} The WAV file header as a Buffer object.\n */\nfunction getWavHeader(\n\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\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\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.debug(\n\t\t\t\t{ src: \"plugin:discord:service:voice\" },\n\t\t\t\t\"AudioMonitor ended\",\n\t\t\t);\n\t\t\tthis.ended = true;\n\t\t\tif (this.lastFlagged < 0) {\n\t\t\t\treturn;\n\t\t\t}\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) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlogger.debug({ src: \"plugin:discord:service:voice\" }, \"Speaking stopped\");\n\t\t\tif (this.lastFlagged < 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcallback(this.getBufferFromStart());\n\t\t});\n\t\tthis.readable.on(\"speakingStarted\", () => {\n\t\t\tif (this.ended) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tonStart();\n\t\t\tlogger.debug({ src: \"plugin:discord:service:voice\" }, \"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: ReturnType<typeof setTimeout> | 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 | null;\n\tprivate runtime: ICompatRuntime;\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 {ICompatRuntime} runtime - The runtime for the agent (with cross-core compat).\n\t */\n\tconstructor(service: DiscordService, runtime: ICompatRuntime) {\n\t\tsuper();\n\t\tthis.client = service.client;\n\t\tthis.runtime = runtime;\n\t\tthis.ready = false;\n\n\t\tif (this.client) {\n\t\t\tthis.client.on(\"voiceManagerReady\", () => {\n\t\t\t\tthis.setReady(true);\n\t\t\t});\n\t\t} else {\n\t\t\tthis.runtime.logger.error(\n\t\t\t\t{ src: \"plugin:discord:service:voice\", agentId: this.runtime.agentId },\n\t\t\t\t\"Discord client not available for voiceManagerReady event\",\n\t\t\t);\n\t\t\tthis.ready = false;\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\tdefault:\n\t\t\t\t// This function should only be called with GuildVoice or GuildStageVoice channels\n\t\t\t\t// If it receives another type, it's an unexpected error.\n\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tchannelId: channel.id,\n\t\t\t\t\t\tchannelType: channel.type,\n\t\t\t\t\t},\n\t\t\t\t\t\"Unexpected channel type\",\n\t\t\t\t);\n\t\t\t\tthrow new Error(`Unexpected channel type encountered: ${channel.type}`);\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\tthis.runtime.logger.debug(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\tready: this.ready,\n\t\t\t},\n\t\t\t\"VoiceManager ready status changed\",\n\t\t);\n\t}\n\n\t/**\n\t * Tears down active voice state so the Discord connector can unload cleanly.\n\t */\n\tstop() {\n\t\tif (this.transcriptionTimeout) {\n\t\t\tclearTimeout(this.transcriptionTimeout);\n\t\t\tthis.transcriptionTimeout = null;\n\t\t}\n\n\t\tfor (const memberId of [...this.activeMonitors.keys()]) {\n\t\t\tthis.stopMonitoringMember(memberId);\n\t\t}\n\n\t\tfor (const connection of new Set(this.connections.values())) {\n\t\t\ttry {\n\t\t\t\tconnection.destroy();\n\t\t\t} catch (error) {\n\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t},\n\t\t\t\t\t\"Failed to destroy Discord voice connection during shutdown\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tthis.connections.clear();\n\t\tthis.streams.clear();\n\t\tthis.userStates.clear();\n\t\tthis.processingVoice = false;\n\t\tthis.cleanupAudioPlayer(this.activeAudioPlayer);\n\t\tthis.removeAllListeners();\n\t\tthis.ready = false;\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) {\n\t\t\treturn;\n\t\t}\n\t\tconst clientUser = this.client?.user;\n\t\tif (clientUser && member.id === clientUser.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\tthis.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t},\n\t\t\t\t\t\"Error leaving voice channel\",\n\t\t\t\t);\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,\n\t\t\tselfDeaf: false,\n\t\t\tselfMute: false,\n\t\t\tgroup: this.client?.user?.id ?? \"default-group\",\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\tthis.runtime.logger.info(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tstatus: connection.state.status,\n\t\t\t\t},\n\t\t\t\t\"Voice connection established\",\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\tthis.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\toldState: oldState.status,\n\t\t\t\t\t\tnewState: newState.status,\n\t\t\t\t\t},\n\t\t\t\t\t\"Voice connection state changed\",\n\t\t\t\t);\n\n\t\t\t\tif (newState.status === VoiceConnectionStatus.Disconnected) {\n\t\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Handling disconnection\",\n\t\t\t\t\t);\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\tthis.runtime.logger.debug(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"Reconnecting to channel\",\n\t\t\t\t\t\t);\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\tthis.runtime.logger.debug(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\t\terror: e instanceof Error ? e.message : String(e),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"Disconnection confirmed - cleaning up\",\n\t\t\t\t\t\t);\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\tthis.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t},\n\t\t\t\t\t\"Voice connection error\",\n\t\t\t\t);\n\t\t\t\t// Don't immediately destroy - let the state change handler deal with it\n\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t},\n\t\t\t\t\t\"Will attempt to recover\",\n\t\t\t\t);\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\tconst meVoice = me?.voice;\n\t\t\tif (meVoice && me.permissions.has(\"DeafenMembers\")) {\n\t\t\t\ttry {\n\t\t\t\t\tawait meVoice.setDeaf(false);\n\t\t\t\t\tawait meVoice.setMute(false);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Failed to modify voice state\",\n\t\t\t\t\t);\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\tthis.runtime.logger.error(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\t\tentityId,\n\t\t\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"Failed to fetch user\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst userUser = user?.user;\n\t\t\t\tif (user && userUser && !userUser.bot) {\n\t\t\t\t\tthis.monitorMember(user as GuildMember, channel);\n\t\t\t\t\tconst entityStream = this.streams.get(entityId);\n\t\t\t\t\tif (entityStream) {\n\t\t\t\t\t\tentityStream.emit(\"speakingStarted\");\n\t\t\t\t\t}\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\tconst userUser = user?.user;\n\t\t\t\tif (user && userUser && !userUser.bot) {\n\t\t\t\t\tconst entityStream = this.streams.get(entityId);\n\t\t\t\t\tif (entityStream) {\n\t\t\t\t\t\tentityStream.emit(\"speakingStopped\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tchannelId: channel.id,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Failed to establish voice connection\",\n\t\t\t);\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 clientUser = this.client?.user;\n\t\tconst userId = clientUser?.id;\n\t\tif (!userId) {\n\t\t\tthis.runtime.logger.error(\n\t\t\t\t{ src: \"plugin:discord:service:voice\", agentId: this.runtime.agentId },\n\t\t\t\t\"Client user ID not available\",\n\t\t\t);\n\t\t\treturn undefined;\n\t\t}\n\t\tconst connections = getVoiceConnections(userId);\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 memberUser = member?.user;\n\t\tconst userName = memberUser?.username;\n\t\tconst name = memberUser?.displayName;\n\t\tconst memberGuild = member?.guild;\n\t\tconst memberGuildId = memberGuild?.id;\n\t\tconst connection = this.getVoiceConnection(memberGuildId);\n\n\t\tconst connectionReceiver = connection?.receiver;\n\t\tconst receiveStream = connectionReceiver?.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\tthis.runtime.logger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tentityId,\n\t\t\t\t},\n\t\t\t\t\"No receiveStream or empty stream\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tlet opusDecoder: ReturnType<typeof createOpusDecoder>;\n\t\ttry {\n\t\t\t// Try to create opus decoder with error handling for Node.js 23 compatibility\n\t\t\topusDecoder = createOpusDecoder({\n\t\t\t\tchannels: 1,\n\t\t\t\trate: DECODE_SAMPLE_RATE,\n\t\t\t\tframeSize: DECODE_FRAME_SIZE,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tentityId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Failed to create opus decoder\",\n\t\t\t);\n\t\t\t// For now, log the error and return early.\n\t\t\t// In production, you might want to implement a PCM fallback or other audio processing\n\t\t\treturn;\n\t\t}\n\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 playbook 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\n\t\tif (!opusDecoder) {\n\t\t\tthis.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t},\n\t\t\t\t\"Opus decoder not available\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tpipeline(\n\t\t\treceiveStream as AudioReceiveStream,\n\t\t\topusDecoder,\n\t\t\t(err: Error | null) => {\n\t\t\t\tif (err) {\n\t\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\tentityId,\n\t\t\t\t\t\t\terror: err.message,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Opus decoding pipeline error\",\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\tentityId,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Opus decoding pipeline finished\",\n\t\t\t\t\t);\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: Error) => {\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t},\n\t\t\t\t\"Opus decoding error\",\n\t\t\t);\n\t\t});\n\t\tconst errorHandler = (err: Error) => {\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t},\n\t\t\t\t\"Opus decoding error\",\n\t\t\t);\n\t\t};\n\t\tconst streamCloseHandler = () => {\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tmember: member?.displayName || undefined,\n\t\t\t\t},\n\t\t\t\t\"Voice stream closed\",\n\t\t\t);\n\t\t\tthis.streams.delete(entityId);\n\t\t\tthis.connections.delete(entityId);\n\t\t};\n\t\tconst closeHandler = () => {\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tmember: member?.displayName || undefined,\n\t\t\t\t},\n\t\t\t\t\"Opus decoder closed\",\n\t\t\t);\n\t\t\topusDecoder.removeListener(\"error\", errorHandler);\n\t\t\topusDecoder.removeListener(\"close\", closeHandler);\n\t\t\tif (receiveStream) {\n\t\t\t\treceiveStream.removeListener(\"close\", streamCloseHandler);\n\t\t\t}\n\t\t};\n\t\topusDecoder.on(\"error\", errorHandler);\n\t\topusDecoder.on(\"close\", closeHandler);\n\t\tif (receiveStream) {\n\t\t\treceiveStream.on(\"close\", streamCloseHandler);\n\t\t}\n\n\t\tif (this.client) {\n\t\t\tthis.client.emit(\n\t\t\t\t\"userStream\",\n\t\t\t\tentityId,\n\t\t\t\tname,\n\t\t\t\tuserName,\n\t\t\t\tchannel,\n\t\t\t\topusDecoder,\n\t\t\t);\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\tthis.runtime.logger.debug(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tchannelName: channel.name,\n\t\t\t},\n\t\t\t\"Left voice channel\",\n\t\t);\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\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tmemberId,\n\t\t\t\t},\n\t\t\t\t\"Stopped monitoring user\",\n\t\t\t);\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\tconst activeAudioPlayer = this.activeAudioPlayer;\n\t\tconst activeAudioPlayerState = activeAudioPlayer?.state;\n\t\tif (activeAudioPlayerState && activeAudioPlayerState.status === \"idle\") {\n\t\t\tthis.runtime.logger.debug(\n\t\t\t\t{ src: \"plugin:discord:service:voice\", agentId: this.runtime.agentId },\n\t\t\t\t\"Cleaning up idle audio player\",\n\t\t\t);\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\tif (state) {\n\t\t\t\tstate.buffers.length = 0;\n\t\t\t\tstate.totalLength = 0;\n\t\t\t}\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);\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\tentityId: UUID,\n\t\tname: string,\n\t\tuserName: string,\n\t\tchannel: BaseGuildVoiceChannel,\n\t\taudioStream: Readable,\n\t) {\n\t\tthis.runtime.logger.debug(\n\t\t\t{\n\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\tentityId,\n\t\t\t},\n\t\t\t\"Starting audio monitor\",\n\t\t);\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\tif (state?.buffers) {\n\t\t\t\t\tstate.buffers.push(buffer);\n\t\t\t\t\tstate.totalLength += buffer.length;\n\t\t\t\t}\n\t\t\t\tif (state) {\n\t\t\t\t\tstate.lastActive = Date.now();\n\t\t\t\t}\n\t\t\t\tthis.debouncedProcessTranscription(entityId, name, userName, channel);\n\t\t\t} catch (error) {\n\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tentityId,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t},\n\t\t\t\t\t\"Error processing buffer\",\n\t\t\t\t);\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\tthis.runtime.logger.error(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Received empty buffer\",\n\t\t\t\t\t);\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) {\n\t\t\treturn;\n\t\t}\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\tthis.runtime.logger.debug(\n\t\t\t\t{ src: \"plugin:discord:service:voice\", agentId: this.runtime.agentId },\n\t\t\t\t\"Starting transcription\",\n\t\t\t);\n\n\t\t\tconst transcriptionText = await this.runtime.useModel(\n\t\t\t\tModelType.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]\")) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\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\tthis.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tentityId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error transcribing audio\",\n\t\t\t);\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 uniqueEntityId = createUniqueUuid(this.runtime, entityId);\n\t\t\tconst type = await this.getChannelType(channel as Channel);\n\n\t\t\tawait this.runtime.ensureConnection({\n\t\t\t\tentityId: uniqueEntityId,\n\t\t\t\troomId,\n\t\t\t\troomName: channel.name,\n\t\t\t\tuserName,\n\t\t\t\tname,\n\t\t\t\tsource: \"discord\",\n\t\t\t\tchannelId,\n\t\t\t\t// Convert Discord snowflake to UUID (see service.ts header for why stringToUuid not asUUID)\n\t\t\t\tmessageServerId: stringToUuid(channel.guild.id),\n\t\t\t\ttype,\n\t\t\t\tworldId: createUniqueUuid(this.runtime, channel.guild.id) as UUID,\n\t\t\t\tworldName: channel.guild.name,\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: uniqueEntityId,\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,\n\t\t\t\t\tuserName,\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_actionName?: string,\n\t\t\t) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst responseText = normalizeDiscordMessageText(content.text);\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\ttext: responseText || undefined,\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\tconst responseMemoryContentText = responseMemory.content.text;\n\t\t\t\t\tif (responseMemoryContentText?.trim()) {\n\t\t\t\t\t\tawait this.runtime.createMemory(responseMemory, \"messages\");\n\n\t\t\t\t\t\tif (responseText) {\n\t\t\t\t\t\t\tconst responseStream = await this.runtime.useModel(\n\t\t\t\t\t\t\t\tModelType.TEXT_TO_SPEECH,\n\t\t\t\t\t\t\t\tresponseText,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (responseStream) {\n\t\t\t\t\t\t\t\t// Convert Buffer/ArrayBuffer to Readable stream\n\t\t\t\t\t\t\t\tconst buffer = Buffer.isBuffer(responseStream)\n\t\t\t\t\t\t\t\t\t? responseStream\n\t\t\t\t\t\t\t\t\t: Buffer.from(responseStream as ArrayBuffer);\n\t\t\t\t\t\t\t\tconst readable = Readable.from(buffer);\n\t\t\t\t\t\t\t\tawait this.playAudioStream(entityId, readable);\n\t\t\t\t\t\t\t}\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\tthis.runtime.logger.error(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Error in voice message callback\",\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\t// Voice messages follow the same default-off auto-reply policy as\n\t\t\t// text messages: ingestion happens via the memory created above,\n\t\t\t// but the agent only generates a spoken reply when DISCORD_AUTO_REPLY\n\t\t\t// is explicitly enabled.\n\t\t\tconst voiceAutoReplyRaw = this.runtime.getSetting(\"DISCORD_AUTO_REPLY\");\n\t\t\tconst voiceAutoReply =\n\t\t\t\tvoiceAutoReplyRaw === true || voiceAutoReplyRaw === \"true\";\n\n\t\t\tif (!voiceAutoReply) {\n\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t{ src: \"plugin:discord:voice\", agentId: this.runtime.agentId },\n\t\t\t\t\t\"Auto-reply disabled (DISCORD_AUTO_REPLY=false); voice message ingested without response\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Process voice message - try messageService first (newer core), fall back to events (older core)\n\t\t\tconst messageService = getMessageService(this.runtime);\n\t\t\tif (messageService) {\n\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t{ src: \"plugin:discord:voice\", agentId: this.runtime.agentId },\n\t\t\t\t\t\"Using messageService API for voice\",\n\t\t\t\t);\n\t\t\t\tawait messageService.handleMessage(this.runtime, memory, callback);\n\t\t\t} else {\n\t\t\t\tthis.runtime.logger.debug(\n\t\t\t\t\t{ src: \"plugin:discord:voice\", agentId: this.runtime.agentId },\n\t\t\t\t\t\"Using event-based handling for voice\",\n\t\t\t\t);\n\t\t\t\tawait this.runtime.emitEvent([EventType.VOICE_MESSAGE_RECEIVED], {\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\tsource: \"discord\",\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error processing voice message\",\n\t\t\t);\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\tthis.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error converting PCM to WAV\",\n\t\t\t);\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) =>\n\t\t\t\t\t\tchannel && 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\tthis.runtime.logger.debug(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\tchannelName: chosenChannel.name,\n\t\t\t\t\t},\n\t\t\t\t\t\"Joining channel\",\n\t\t\t\t);\n\t\t\t\tawait this.joinChannel(chosenChannel);\n\t\t\t} else {\n\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t},\n\t\t\t\t\t\"No suitable voice channel found to join\",\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error selecting or joining a voice channel\",\n\t\t\t);\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\tthis.runtime.logger.debug(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tentityId,\n\t\t\t\t},\n\t\t\t\t\"No connection for user\",\n\t\t\t);\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: Error) => {\n\t\t\tthis.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t},\n\t\t\t\t\"Audio player error\",\n\t\t\t);\n\t\t});\n\n\t\taudioPlayer.on(\n\t\t\t\"stateChange\",\n\t\t\t(_oldState: unknown, 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\tthis.runtime.logger.debug(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\tdurationMs: idleTime - audioStartTime,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Audio playback completed\",\n\t\t\t\t\t);\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 | null) {\n\t\tif (!audioPlayer) {\n\t\t\treturn;\n\t\t}\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 interaction - The interaction object representing the user's input.\n\t * @returns A promise that resolves once the join channel command is handled.\n\t */\n\tasync handleJoinChannelCommand(interaction: {\n\t\tdeferReply: () => Promise<void>;\n\t\toptions: {\n\t\t\tget: (name: string) => { value: string } | null;\n\t\t};\n\t\tguild: Guild | null;\n\t\teditReply: (message: string) => Promise<void>;\n\t}) {\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 interactionOptionsChannel = interaction.options.get(\"channel\");\n\t\t\tconst channelId = interactionOptionsChannel?.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\tthis.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error joining voice channel\",\n\t\t\t);\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((err: Error) => {\n\t\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\t\t\terror: err.message,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Failed to send error reply\",\n\t\t\t\t\t);\n\t\t\t\t});\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 interaction - The interaction object representing the command invocation.\n\t * @returns A promise that resolves once the leave channel command is handled.\n\t */\n\tasync handleLeaveChannelCommand(interaction: {\n\t\tguildId: string | null;\n\t\treply: (message: string) => Promise<void>;\n\t}) {\n\t\tif (!interaction.guildId) {\n\t\t\tawait interaction.reply(\"This command can only be used in a server.\");\n\t\t\treturn;\n\t\t}\n\t\tconst connection = this.getVoiceConnection(interaction.guildId);\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\tthis.runtime.logger.error(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord:service:voice\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Error leaving voice channel\",\n\t\t\t);\n\t\t\tawait interaction.reply(\"Failed to leave the voice channel.\");\n\t\t}\n\t}\n}\n",
|
|
61
|
+
"/**\n * Discord local IPC connector.\n *\n * Connects to the Discord desktop app via the local RPC socket, enabling\n * message ingestion and macOS UI-automation-based replies. Previously\n * lived in `@elizaos/agent` as `discord-local-plugin.ts`; moved here so\n * the Discord plugin package owns its own runtime service.\n */\n\nimport { execFile } from \"node:child_process\";\nimport crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport fsp from \"node:fs/promises\";\nimport net from \"node:net\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport {\n\tChannelType,\n\tContentType,\n\tcreateMessageMemory,\n\tcreateUniqueUuid,\n\ttype IAgentRuntime,\n\tlogger,\n\ttype Media,\n\ttype Memory,\n\ttype MemoryMetadata,\n\ttype Plugin,\n\tService,\n\tstringToUuid,\n\ttype UUID,\n} from \"@elizaos/core\";\n\nconst execFileAsync = promisify(execFile);\n\nexport const DISCORD_LOCAL_PLUGIN_NAME = \"@elizaos/plugin-discord-local\";\nexport const DISCORD_LOCAL_SERVICE_NAME = \"discord-local\";\nconst DISCORD_OAUTH_TOKEN_URL = \"https://discord.com/api/v10/oauth2/token\";\nconst DISCORD_LOCAL_DEFAULT_SCOPES = [\n\t\"rpc\",\n\t\"identify\",\n\t\"rpc.notifications.read\",\n] as const;\n\nconst IPC_OP_HANDSHAKE = 0;\nconst IPC_OP_FRAME = 1;\nconst IPC_OP_CLOSE = 2;\nconst IPC_OP_PING = 3;\nconst IPC_OP_PONG = 4;\n\ntype DiscordLocalConfig = {\n\tenabled: boolean;\n\tclientId: string;\n\tclientSecret: string;\n\tscopes: string[];\n\tmessageChannelIds: string[];\n\tsendDelayMs: number;\n};\n\ntype DiscordLocalSession = {\n\taccessToken: string;\n\trefreshToken?: string;\n\texpiresAt?: number;\n\tscopes: string[];\n};\n\ntype DiscordLocalUser = {\n\tid: string;\n\tusername: string;\n\tglobal_name?: string | null;\n\tavatar?: string | null;\n};\n\ntype DiscordLocalChannel = {\n\tid: string;\n\tguild_id?: string | null;\n\ttype?: number;\n\tname?: string | null;\n\trecipients?: DiscordLocalUser[];\n};\n\ntype DiscordLocalGuild = {\n\tid: string;\n\tname: string;\n};\n\ntype DiscordLocalAttachment = {\n\tid: string;\n\tfilename?: string;\n\turl?: string;\n\tcontent_type?: string;\n\tdescription?: string | null;\n};\n\ntype DiscordLocalMessage = {\n\tid: string;\n\tchannel_id?: string;\n\tguild_id?: string | null;\n\tcontent?: string;\n\tauthor?: DiscordLocalUser | null;\n\tattachments?: DiscordLocalAttachment[];\n\ttimestamp?: string;\n\treferenced_message?: DiscordLocalMessage | null;\n\tmessage_reference?: {\n\t\tmessage_id?: string;\n\t\tchannel_id?: string;\n\t\tguild_id?: string | null;\n\t} | null;\n};\n\ntype DiscordLocalNotification = {\n\tchannel_id?: string;\n\tmessage?: DiscordLocalMessage | null;\n};\n\ntype DiscordLocalRpcPayload = {\n\tcmd?: string;\n\tevt?: string;\n\tnonce?: string;\n\tdata?: unknown;\n};\n\ntype PendingRpcRequest = {\n\tresolve: (value: DiscordLocalRpcPayload) => void;\n\treject: (error: Error) => void;\n};\n\n// ── Inline state-dir resolution (avoids @elizaos/agent dependency) ──\n\nfunction resolveStateDir(): string {\n\tconst explicit = process.env.ELIZA_STATE_DIR?.trim() || undefined;\n\tif (explicit) {\n\t\tif (explicit.startsWith(\"~\")) {\n\t\t\treturn path.resolve(explicit.replace(/^~(?=$|[\\\\/])/, os.homedir()));\n\t\t}\n\t\treturn path.resolve(explicit);\n\t}\n\tconst namespace = process.env.ELIZA_NAMESPACE?.trim() || \"eliza\";\n\treturn path.join(os.homedir(), `.${namespace}`);\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────\n\nfunction parseListSetting(value: unknown): string[] {\n\tif (Array.isArray(value)) {\n\t\treturn value\n\t\t\t.map((entry) => (typeof entry === \"string\" ? entry.trim() : \"\"))\n\t\t\t.filter((entry) => entry.length > 0);\n\t}\n\n\tif (typeof value === \"string\") {\n\t\treturn value\n\t\t\t.split(\",\")\n\t\t\t.map((entry) => entry.trim())\n\t\t\t.filter((entry) => entry.length > 0);\n\t}\n\n\treturn [];\n}\n\nfunction getDiscordLocalConfig(\n\truntime: IAgentRuntime,\n): DiscordLocalConfig | null {\n\tconst clientId = runtime.getSetting(\"DISCORD_LOCAL_CLIENT_ID\");\n\tconst clientSecret = runtime.getSetting(\"DISCORD_LOCAL_CLIENT_SECRET\");\n\tconst enabledValue = runtime.getSetting(\"DISCORD_LOCAL_ENABLED\");\n\n\tconst enabled =\n\t\tenabledValue === undefined ||\n\t\tenabledValue === null ||\n\t\t(typeof enabledValue === \"string\" && enabledValue.trim() !== \"false\");\n\n\tif (\n\t\ttypeof clientId !== \"string\" ||\n\t\tclientId.trim().length === 0 ||\n\t\ttypeof clientSecret !== \"string\" ||\n\t\tclientSecret.trim().length === 0\n\t) {\n\t\treturn null;\n\t}\n\n\tconst rawSendDelayMs = runtime.getSetting(\"DISCORD_LOCAL_SEND_DELAY_MS\");\n\tconst parsedSendDelayMs =\n\t\ttypeof rawSendDelayMs === \"string\"\n\t\t\t? Number.parseInt(rawSendDelayMs, 10)\n\t\t\t: Number.NaN;\n\n\treturn {\n\t\tenabled,\n\t\tclientId: clientId.trim(),\n\t\tclientSecret: clientSecret.trim(),\n\t\tscopes: (() => {\n\t\t\tconst parsed = parseListSetting(\n\t\t\t\truntime.getSetting(\"DISCORD_LOCAL_SCOPES\"),\n\t\t\t);\n\t\t\treturn parsed.length > 0 ? parsed : [...DISCORD_LOCAL_DEFAULT_SCOPES];\n\t\t})(),\n\t\tmessageChannelIds: parseListSetting(\n\t\t\truntime.getSetting(\"DISCORD_LOCAL_MESSAGE_CHANNEL_IDS\"),\n\t\t),\n\t\tsendDelayMs:\n\t\t\tNumber.isFinite(parsedSendDelayMs) && parsedSendDelayMs >= 100\n\t\t\t\t? parsedSendDelayMs\n\t\t\t\t: 900,\n\t};\n}\n\nfunction resolveSessionPath(): string {\n\tconst dir = path.join(resolveStateDir(), \"discord-local\");\n\tfs.mkdirSync(dir, { recursive: true });\n\treturn path.join(dir, \"session.json\");\n}\n\nfunction buildDiscordAvatarUrl(\n\tuser: DiscordLocalUser | null | undefined,\n): string | undefined {\n\tif (!user?.avatar || !user.id) {\n\t\treturn undefined;\n\t}\n\treturn `https://cdn.discordapp.com/avatars/${encodeURIComponent(user.id)}/${encodeURIComponent(user.avatar)}.png?size=128`;\n}\n\nfunction roomTypeForChannel(channelType: number | undefined): ChannelType {\n\tif (channelType === 1) {\n\t\treturn ChannelType.DM;\n\t}\n\treturn ChannelType.GROUP;\n}\n\nfunction worldIdFor(runtime: IAgentRuntime, serverKey: string): UUID {\n\treturn stringToUuid(\n\t\t`discord-local-world:${runtime.agentId}:${serverKey}`,\n\t) as UUID;\n}\n\nfunction roomIdFor(runtime: IAgentRuntime, channelId: string): UUID {\n\treturn stringToUuid(\n\t\t`discord-local-room:${runtime.agentId}:${channelId}`,\n\t) as UUID;\n}\n\nfunction entityIdFor(userId: string): UUID {\n\treturn stringToUuid(`discord-local-user:${userId}`) as UUID;\n}\n\nfunction messageIdFor(\n\truntime: IAgentRuntime,\n\tchannelId: string,\n\tmessageId: string,\n): UUID {\n\treturn stringToUuid(\n\t\t`discord-local-message:${runtime.agentId}:${channelId}:${messageId}`,\n\t) as UUID;\n}\n\nfunction outboundMemoryIdFor(runtime: IAgentRuntime, roomId: UUID): UUID {\n\treturn createUniqueUuid(\n\t\truntime,\n\t\t`discord-local-outbound:${roomId}:${Date.now()}`,\n\t) as UUID;\n}\n\nfunction getRegisteredSendHandlers(\n\truntime: IAgentRuntime,\n): Map<string, unknown> | null {\n\tconst sendHandlers = (runtime as unknown as { sendHandlers?: unknown })\n\t\t.sendHandlers;\n\treturn sendHandlers instanceof Map ? sendHandlers : null;\n}\n\nfunction contentTypeForMime(\n\tmimeType: string | undefined,\n): ContentType | undefined {\n\tconst normalized = mimeType?.trim().toLowerCase();\n\tif (!normalized) {\n\t\treturn undefined;\n\t}\n\tif (normalized.startsWith(\"image/\")) {\n\t\treturn ContentType.IMAGE;\n\t}\n\tif (normalized.startsWith(\"video/\")) {\n\t\treturn ContentType.VIDEO;\n\t}\n\tif (normalized.startsWith(\"audio/\")) {\n\t\treturn ContentType.AUDIO;\n\t}\n\tif (normalized === \"text/uri-list\") {\n\t\treturn ContentType.LINK;\n\t}\n\treturn ContentType.DOCUMENT;\n}\n\nfunction describeRpcError(payload: DiscordLocalRpcPayload): string {\n\tconst data =\n\t\tpayload.data && typeof payload.data === \"object\"\n\t\t\t? (payload.data as Record<string, unknown>)\n\t\t\t: null;\n\tconst message =\n\t\ttypeof data?.message === \"string\"\n\t\t\t? data.message\n\t\t\t: typeof data?.error === \"string\"\n\t\t\t\t? data.error\n\t\t\t\t: payload.cmd\n\t\t\t\t\t? `Discord RPC command ${payload.cmd} failed`\n\t\t\t\t\t: \"Discord RPC request failed\";\n\tconst code =\n\t\ttypeof data?.code === \"number\" || typeof data?.code === \"string\"\n\t\t\t? ` (${String(data.code)})`\n\t\t\t: \"\";\n\treturn `${message}${code}`;\n}\n\nfunction toAppleScriptStringLiteral(value: string): string {\n\treturn `\"${value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n}\n\nfunction buildDiscordSendScript(text: string, delaySeconds: number): string {\n\tconst lines = text.split(/\\r?\\n/);\n\tconst scriptLines = [\n\t\t'tell application \"Discord\" to activate',\n\t\t`delay ${delaySeconds.toFixed(2)}`,\n\t\t'tell application \"System Events\"',\n\t];\n\n\tfor (const [index, line] of lines.entries()) {\n\t\tif (line.length > 0) {\n\t\t\tscriptLines.push(` keystroke ${toAppleScriptStringLiteral(line)}`);\n\t\t}\n\t\tif (index < lines.length - 1) {\n\t\t\tscriptLines.push(\" key code 36 using shift down\");\n\t\t}\n\t}\n\n\tscriptLines.push(\" key code 36\");\n\tscriptLines.push(\"end tell\");\n\n\treturn scriptLines.join(\"\\n\");\n}\n\nasync function openDiscordTarget(\n\tchannelId: string,\n\tguildId?: string | null,\n): Promise<void> {\n\tconst url =\n\t\tguildId && guildId.trim().length > 0\n\t\t\t? `discord://-/channels/${guildId}/${channelId}`\n\t\t\t: `discord://-/channels/@me/${channelId}`;\n\tawait execFileAsync(\"/usr/bin/open\", [url]);\n}\n\nfunction getIpcCandidateDirs(): string[] {\n\tconst candidates = [\n\t\tprocess.env.DISCORD_IPC_DIR,\n\t\tprocess.env.XDG_RUNTIME_DIR,\n\t\tprocess.env.TMPDIR,\n\t\tprocess.env.TMP,\n\t\tprocess.env.TEMP,\n\t\t\"/tmp\",\n\t\t\"/private/tmp\",\n\t].filter(\n\t\t(entry): entry is string =>\n\t\t\ttypeof entry === \"string\" && entry.trim().length > 0,\n\t);\n\n\treturn Array.from(new Set(candidates.map((entry) => entry.trim())));\n}\n\nfunction findDiscordIpcPath(): string | null {\n\tfor (const dir of getIpcCandidateDirs()) {\n\t\tfor (let index = 0; index < 10; index += 1) {\n\t\t\tconst candidate = path.join(dir, `discord-ipc-${index}`);\n\t\t\tif (fs.existsSync(candidate)) {\n\t\t\t\treturn candidate;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst macRoot = path.join(\n\t\tos.homedir(),\n\t\t\"Library\",\n\t\t\"Application Support\",\n\t\t\"discord\",\n\t);\n\tif (!fs.existsSync(macRoot)) {\n\t\treturn null;\n\t}\n\n\tconst stack = [macRoot];\n\twhile (stack.length > 0) {\n\t\tconst current = stack.pop();\n\t\tif (!current) continue;\n\n\t\tconst entries = fs.readdirSync(current, { withFileTypes: true });\n\t\tfor (const entry of entries) {\n\t\t\tconst candidatePath = path.join(current, entry.name);\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tstack.push(candidatePath);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (entry.isSocket() && /^discord-ipc-\\d+$/.test(entry.name)) {\n\t\t\t\treturn candidatePath;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\n// ── Service ─────────────────────────────────────────────────────────\n\nexport class DiscordLocalService extends Service {\n\tstatic serviceType = DISCORD_LOCAL_SERVICE_NAME;\n\tcapabilityDescription =\n\t\t\"The agent can read Discord notifications and channel messages from the local Discord desktop app and send replies through macOS UI automation.\";\n\n\tprivate readonly sessionPath = resolveSessionPath();\n\tprivate readonly pendingRequests = new Map<string, PendingRpcRequest>();\n\tprivate readonly channelCache = new Map<string, DiscordLocalChannel>();\n\tprivate readonly guildCache = new Map<string, DiscordLocalGuild>();\n\tprivate readonly subscribedChannelIds = new Set<string>();\n\tprivate connectorConfig: DiscordLocalConfig | null = null;\n\tprivate socket: net.Socket | null = null;\n\tprivate connectedIpcPath: string | null = null;\n\tprivate readBuffer = Buffer.alloc(0);\n\tprivate readyPromise: Promise<void> | null = null;\n\tprivate readyResolve: (() => void) | null = null;\n\tprivate readyReject: ((error: Error) => void) | null = null;\n\tprivate reconnectTimer: NodeJS.Timeout | null = null;\n\tprivate session: DiscordLocalSession | null = null;\n\tprivate currentUser: DiscordLocalUser | null = null;\n\tprivate connected = false;\n\tprivate authenticated = false;\n\tprivate lastError: string | null = null;\n\n\tconstructor(runtime?: IAgentRuntime) {\n\t\tsuper(runtime);\n\t\tif (!runtime) {\n\t\t\treturn;\n\t\t}\n\t\tthis.connectorConfig = getDiscordLocalConfig(runtime);\n\t}\n\n\tstatic async start(runtime: IAgentRuntime): Promise<DiscordLocalService> {\n\t\tconst service = new DiscordLocalService(runtime);\n\t\tawait service.startService();\n\t\treturn service;\n\t}\n\n\tstatic registerSendHandlers(\n\t\truntime: IAgentRuntime,\n\t\tservice: DiscordLocalService,\n\t): void {\n\t\tconst register = (source: string) => {\n\t\t\truntime.registerSendHandler(source, async (_runtime, target, content) => {\n\t\t\t\tconst text =\n\t\t\t\t\ttypeof content.text === \"string\" ? content.text.trim() : \"\";\n\t\t\t\tif (!text) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst room =\n\t\t\t\t\ttarget.roomId && typeof runtime.getRoom === \"function\"\n\t\t\t\t\t\t? await runtime.getRoom(target.roomId)\n\t\t\t\t\t\t: null;\n\t\t\t\tconst channelId = String(\n\t\t\t\t\ttarget.channelId ?? room?.channelId ?? \"\",\n\t\t\t\t).trim();\n\t\t\t\tif (!channelId) {\n\t\t\t\t\tthrow new Error(\"Discord local target is missing a channel ID\");\n\t\t\t\t}\n\n\t\t\t\tconst channel = await service.getChannel(channelId);\n\t\t\t\tconst guildId =\n\t\t\t\t\tchannel?.guild_id && channel.guild_id.trim().length > 0\n\t\t\t\t\t\t? channel.guild_id\n\t\t\t\t\t\t: null;\n\t\t\t\tawait service.sendUiMessage(channelId, guildId, text);\n\n\t\t\t\tif (!target.roomId) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst memory = createMessageMemory({\n\t\t\t\t\tid: outboundMemoryIdFor(runtime, target.roomId),\n\t\t\t\t\tagentId: runtime.agentId,\n\t\t\t\t\tentityId: runtime.agentId,\n\t\t\t\t\troomId: target.roomId,\n\t\t\t\t\tcontent: {\n\t\t\t\t\t\t...content,\n\t\t\t\t\t\ttext,\n\t\t\t\t\t\tsource: DISCORD_LOCAL_SERVICE_NAME,\n\t\t\t\t\t},\n\t\t\t\t}) as Memory;\n\t\t\t\tmemory.createdAt = Date.now();\n\t\t\t\tmemory.metadata = {\n\t\t\t\t\t...(memory.metadata ?? {}),\n\t\t\t\t\tdiscordChannelId: channelId,\n\t\t\t\t\t...(guildId ? { discordServerId: guildId } : {}),\n\t\t\t\t} as MemoryMetadata;\n\n\t\t\t\tawait runtime.createMemory(memory, \"messages\");\n\t\t\t});\n\t\t};\n\n\t\tregister(DISCORD_LOCAL_SERVICE_NAME);\n\t\tconst sendHandlers = getRegisteredSendHandlers(runtime);\n\t\tif (!(sendHandlers instanceof Map) || !sendHandlers.has(\"discord\")) {\n\t\t\tregister(\"discord\");\n\t\t}\n\t}\n\n\tasync stop(): Promise<void> {\n\t\tif (this.reconnectTimer) {\n\t\t\tclearTimeout(this.reconnectTimer);\n\t\t\tthis.reconnectTimer = null;\n\t\t}\n\t\tthis.connected = false;\n\t\tthis.authenticated = false;\n\t\tthis.connectedIpcPath = null;\n\t\tthis.rejectPendingRequests(new Error(\"Discord local service stopped\"));\n\t\tthis.socket?.destroy();\n\t\tthis.socket = null;\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.connected;\n\t}\n\n\tisAuthenticated(): boolean {\n\t\treturn this.authenticated;\n\t}\n\n\tgetStatus() {\n\t\treturn {\n\t\t\tavailable: Boolean(this.connectorConfig),\n\t\t\tconnected: this.connected,\n\t\t\tauthenticated: this.authenticated,\n\t\t\tcurrentUser: this.currentUser,\n\t\t\tsubscribedChannelIds: [...this.subscribedChannelIds],\n\t\t\tconfiguredChannelIds: this.connectorConfig?.messageChannelIds ?? [],\n\t\t\tscopes: this.session?.scopes ?? this.connectorConfig?.scopes ?? [],\n\t\t\tlastError: this.lastError,\n\t\t\tipcPath: this.connected ? this.connectedIpcPath : findDiscordIpcPath(),\n\t\t};\n\t}\n\n\tasync authorize(): Promise<ReturnType<DiscordLocalService[\"getStatus\"]>> {\n\t\tconst config = this.requireConfig();\n\t\tawait this.ensureRpcConnection();\n\t\tconst response = await this.sendRpcCommand(\"AUTHORIZE\", {\n\t\t\tclient_id: config.clientId,\n\t\t\tscopes: config.scopes,\n\t\t});\n\n\t\tconst code =\n\t\t\tresponse.data &&\n\t\t\ttypeof response.data === \"object\" &&\n\t\t\ttypeof (response.data as Record<string, unknown>).code === \"string\"\n\t\t\t\t? ((response.data as Record<string, unknown>).code as string)\n\t\t\t\t: \"\";\n\t\tif (!code) {\n\t\t\tthrow new Error(\"Discord AUTHORIZE did not return an authorization code\");\n\t\t}\n\n\t\tawait this.exchangeAuthorizationCode(code);\n\t\treturn this.getStatus();\n\t}\n\n\tasync disconnectSession(): Promise<void> {\n\t\tthis.session = null;\n\t\tthis.currentUser = null;\n\t\tthis.authenticated = false;\n\t\tthis.subscribedChannelIds.clear();\n\t\tawait fsp.rm(this.sessionPath, { force: true });\n\t\tawait this.stop();\n\t}\n\n\tasync listGuilds(): Promise<DiscordLocalGuild[]> {\n\t\tawait this.ensureAuthenticated();\n\t\tconst response = await this.sendRpcCommand(\"GET_GUILDS\");\n\t\tconst guilds = Array.isArray(response.data)\n\t\t\t? (response.data as DiscordLocalGuild[])\n\t\t\t: [];\n\t\tfor (const guild of guilds) {\n\t\t\tif (guild?.id) {\n\t\t\t\tthis.guildCache.set(guild.id, guild);\n\t\t\t}\n\t\t}\n\t\treturn guilds;\n\t}\n\n\tasync listChannels(guildId: string): Promise<DiscordLocalChannel[]> {\n\t\tawait this.ensureAuthenticated();\n\t\tconst response = await this.sendRpcCommand(\"GET_CHANNELS\", {\n\t\t\tguild_id: guildId,\n\t\t});\n\t\tconst channels = Array.isArray(response.data)\n\t\t\t? (response.data as DiscordLocalChannel[])\n\t\t\t: [];\n\t\tfor (const channel of channels) {\n\t\t\tif (channel?.id) {\n\t\t\t\tthis.channelCache.set(channel.id, channel);\n\t\t\t}\n\t\t}\n\t\treturn channels;\n\t}\n\n\tasync subscribeChannelMessages(channelIds: string[]): Promise<string[]> {\n\t\tconst config = this.requireConfig();\n\t\tawait this.ensureAuthenticated();\n\t\tconst normalized = [\n\t\t\t...new Set(channelIds.map((entry) => entry.trim()).filter(Boolean)),\n\t\t];\n\t\tconfig.messageChannelIds = normalized;\n\n\t\tfor (const channelId of [...this.subscribedChannelIds]) {\n\t\t\tif (normalized.includes(channelId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tawait this.sendRpcCommand(\n\t\t\t\t\"UNSUBSCRIBE\",\n\t\t\t\t{ channel_id: channelId },\n\t\t\t\t\"MESSAGE_CREATE\",\n\t\t\t);\n\t\t\tthis.subscribedChannelIds.delete(channelId);\n\t\t}\n\n\t\tfor (const channelId of normalized) {\n\t\t\tif (this.subscribedChannelIds.has(channelId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tawait this.sendRpcCommand(\n\t\t\t\t\"SUBSCRIBE\",\n\t\t\t\t{ channel_id: channelId },\n\t\t\t\t\"MESSAGE_CREATE\",\n\t\t\t);\n\t\t\tthis.subscribedChannelIds.add(channelId);\n\t\t}\n\n\t\treturn [...normalized];\n\t}\n\n\tasync getChannel(channelId: string): Promise<DiscordLocalChannel | null> {\n\t\tconst cached = this.channelCache.get(channelId);\n\t\tif (cached) {\n\t\t\treturn cached;\n\t\t}\n\t\tawait this.ensureAuthenticated();\n\t\tconst response = await this.sendRpcCommand(\"GET_CHANNEL\", {\n\t\t\tchannel_id: channelId,\n\t\t});\n\t\tconst channel =\n\t\t\tresponse.data && typeof response.data === \"object\"\n\t\t\t\t? (response.data as DiscordLocalChannel)\n\t\t\t\t: null;\n\t\tif (channel?.id) {\n\t\t\tthis.channelCache.set(channel.id, channel);\n\t\t}\n\t\treturn channel;\n\t}\n\n\tprivate requireConfig(): DiscordLocalConfig {\n\t\tif (!this.connectorConfig) {\n\t\t\tthrow new Error(\"Discord local connector is not configured\");\n\t\t}\n\t\tif (process.platform !== \"darwin\") {\n\t\t\tthrow new Error(\"Discord local connector currently supports macOS only\");\n\t\t}\n\t\treturn this.connectorConfig;\n\t}\n\n\tprivate async startService(): Promise<void> {\n\t\tif (!this.connectorConfig?.enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.session = await this.loadSession();\n\t\tif (!this.session) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tawait this.ensureAuthenticated();\n\t\t} catch (error) {\n\t\t\tthis.lastError = error instanceof Error ? error.message : String(error);\n\t\t\tlogger.warn(\n\t\t\t\t`[discord-local] Failed to restore Discord local session: ${this.lastError}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async ensureAuthenticated(): Promise<void> {\n\t\tthis.requireConfig();\n\t\tif (!this.session) {\n\t\t\tthrow new Error(\"Discord local connector is not authorized\");\n\t\t}\n\n\t\tawait this.ensureRpcConnection();\n\n\t\tconst expiresAt = this.session.expiresAt ?? 0;\n\t\tif (\n\t\t\tthis.session.refreshToken &&\n\t\t\texpiresAt > 0 &&\n\t\t\tDate.now() >= expiresAt - 60_000\n\t\t) {\n\t\t\tawait this.refreshAccessToken();\n\t\t}\n\n\t\tif (this.authenticated) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst response = await this.sendRpcCommand(\"AUTHENTICATE\", {\n\t\t\taccess_token: this.session.accessToken,\n\t\t});\n\t\tconst rawUser =\n\t\t\tresponse.data && typeof response.data === \"object\"\n\t\t\t\t? ((response.data as Record<string, unknown>).user as\n\t\t\t\t\t\t| DiscordLocalUser\n\t\t\t\t\t\t| undefined)\n\t\t\t\t: undefined;\n\t\tthis.currentUser = rawUser ?? null;\n\t\tthis.authenticated = true;\n\t\tawait this.subscribeNotifications();\n\t\tawait this.subscribeConfiguredChannels();\n\t}\n\n\tprivate async subscribeNotifications(): Promise<void> {\n\t\tif (!this.session?.scopes.includes(\"rpc.notifications.read\")) {\n\t\t\treturn;\n\t\t}\n\t\tawait this.sendRpcCommand(\"SUBSCRIBE\", {}, \"NOTIFICATION_CREATE\");\n\t}\n\n\tprivate async subscribeConfiguredChannels(): Promise<void> {\n\t\tconst channelIds = this.connectorConfig?.messageChannelIds ?? [];\n\t\tfor (const channelId of channelIds) {\n\t\t\tif (this.subscribedChannelIds.has(channelId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tawait this.sendRpcCommand(\n\t\t\t\t\"SUBSCRIBE\",\n\t\t\t\t{ channel_id: channelId },\n\t\t\t\t\"MESSAGE_CREATE\",\n\t\t\t);\n\t\t\tthis.subscribedChannelIds.add(channelId);\n\t\t}\n\t}\n\n\tprivate async exchangeAuthorizationCode(code: string): Promise<void> {\n\t\tconst config = this.requireConfig();\n\t\tconst body = new URLSearchParams({\n\t\t\tclient_id: config.clientId,\n\t\t\tclient_secret: config.clientSecret,\n\t\t\tgrant_type: \"authorization_code\",\n\t\t\tcode,\n\t\t});\n\n\t\tconst response = await fetch(DISCORD_OAUTH_TOKEN_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t},\n\t\t\tbody,\n\t\t});\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Discord OAuth token exchange failed with ${response.status}`,\n\t\t\t);\n\t\t}\n\t\tconst json = (await response.json()) as Record<string, unknown>;\n\t\tawait this.storeTokenResponse(json);\n\t\tthis.authenticated = false;\n\t\tawait this.ensureAuthenticated();\n\t}\n\n\tprivate async refreshAccessToken(): Promise<void> {\n\t\tconst config = this.requireConfig();\n\t\tif (!this.session?.refreshToken) {\n\t\t\tthrow new Error(\"Discord local session cannot be refreshed\");\n\t\t}\n\n\t\tconst body = new URLSearchParams({\n\t\t\tclient_id: config.clientId,\n\t\t\tclient_secret: config.clientSecret,\n\t\t\tgrant_type: \"refresh_token\",\n\t\t\trefresh_token: this.session.refreshToken,\n\t\t});\n\n\t\tconst response = await fetch(DISCORD_OAUTH_TOKEN_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t},\n\t\t\tbody,\n\t\t});\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Discord OAuth refresh failed with ${response.status}`);\n\t\t}\n\t\tconst json = (await response.json()) as Record<string, unknown>;\n\t\tawait this.storeTokenResponse(json);\n\t\tthis.authenticated = false;\n\t}\n\n\tprivate async storeTokenResponse(\n\t\tjson: Record<string, unknown>,\n\t): Promise<void> {\n\t\tconst accessToken =\n\t\t\ttypeof json.access_token === \"string\" ? json.access_token : \"\";\n\t\tif (!accessToken) {\n\t\t\tthrow new Error(\"Discord OAuth token response is missing access_token\");\n\t\t}\n\n\t\tconst refreshToken =\n\t\t\ttypeof json.refresh_token === \"string\" ? json.refresh_token : undefined;\n\t\tconst expiresIn =\n\t\t\ttypeof json.expires_in === \"number\"\n\t\t\t\t? json.expires_in\n\t\t\t\t: typeof json.expires_in === \"string\"\n\t\t\t\t\t? Number.parseInt(json.expires_in, 10)\n\t\t\t\t\t: 0;\n\t\tconst scopeString =\n\t\t\ttypeof json.scope === \"string\"\n\t\t\t\t? json.scope\n\t\t\t\t: (this.connectorConfig?.scopes.join(\" \") ?? \"\");\n\n\t\tthis.session = {\n\t\t\taccessToken,\n\t\t\trefreshToken,\n\t\t\texpiresAt:\n\t\t\t\tNumber.isFinite(expiresIn) && expiresIn > 0\n\t\t\t\t\t? Date.now() + expiresIn * 1000\n\t\t\t\t\t: undefined,\n\t\t\tscopes: scopeString\n\t\t\t\t.split(/\\s+/)\n\t\t\t\t.map((entry) => entry.trim())\n\t\t\t\t.filter(Boolean),\n\t\t};\n\t\tawait fsp.writeFile(\n\t\t\tthis.sessionPath,\n\t\t\tJSON.stringify(this.session, null, 2),\n\t\t\t\"utf8\",\n\t\t);\n\t}\n\n\tprivate async loadSession(): Promise<DiscordLocalSession | null> {\n\t\tif (!fs.existsSync(this.sessionPath)) {\n\t\t\treturn null;\n\t\t}\n\t\tconst raw = await fsp.readFile(this.sessionPath, \"utf8\");\n\t\tconst parsed = JSON.parse(raw) as Partial<DiscordLocalSession>;\n\t\tif (\n\t\t\ttypeof parsed.accessToken !== \"string\" ||\n\t\t\tparsed.accessToken.trim().length === 0\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\treturn {\n\t\t\taccessToken: parsed.accessToken,\n\t\t\trefreshToken:\n\t\t\t\ttypeof parsed.refreshToken === \"string\"\n\t\t\t\t\t? parsed.refreshToken\n\t\t\t\t\t: undefined,\n\t\t\texpiresAt:\n\t\t\t\ttypeof parsed.expiresAt === \"number\" ? parsed.expiresAt : undefined,\n\t\t\tscopes: Array.isArray(parsed.scopes)\n\t\t\t\t? parsed.scopes.filter(\n\t\t\t\t\t\t(entry): entry is string => typeof entry === \"string\",\n\t\t\t\t\t)\n\t\t\t\t: [...DISCORD_LOCAL_DEFAULT_SCOPES],\n\t\t};\n\t}\n\n\tprivate async ensureRpcConnection(): Promise<void> {\n\t\tconst config = this.requireConfig();\n\t\tif (this.connected && this.socket && !this.socket.destroyed) {\n\t\t\treturn;\n\t\t}\n\t\tif (this.readyPromise) {\n\t\t\treturn this.readyPromise;\n\t\t}\n\n\t\tconst ipcPath = findDiscordIpcPath();\n\t\tif (!ipcPath) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Discord IPC socket not found. Open the Discord desktop app first.\",\n\t\t\t);\n\t\t}\n\n\t\tthis.readyPromise = new Promise<void>((resolve, reject) => {\n\t\t\tthis.readyResolve = resolve;\n\t\t\tthis.readyReject = reject;\n\t\t});\n\n\t\tconst socket = net.createConnection(ipcPath);\n\t\tthis.socket = socket;\n\t\tthis.readBuffer = Buffer.alloc(0);\n\n\t\tsocket.on(\"connect\", () => {\n\t\t\tthis.connectedIpcPath = ipcPath;\n\t\t\tthis.writeFrame(IPC_OP_HANDSHAKE, {\n\t\t\t\tv: 1,\n\t\t\t\tclient_id: config.clientId,\n\t\t\t});\n\t\t});\n\n\t\tsocket.on(\"data\", (chunk: Buffer) => {\n\t\t\tthis.handleSocketData(chunk);\n\t\t});\n\n\t\tsocket.on(\"close\", () => {\n\t\t\tconst error = new Error(\"Discord local RPC connection closed\");\n\t\t\tthis.connected = false;\n\t\t\tthis.authenticated = false;\n\t\t\tthis.connectedIpcPath = null;\n\t\t\tthis.socket = null;\n\t\t\tthis.rejectPendingRequests(error);\n\t\t\tthis.readyReject?.(error);\n\t\t\tthis.readyReject = null;\n\t\t\tthis.readyResolve = null;\n\t\t\tthis.readyPromise = null;\n\t\t\tif (this.session?.accessToken) {\n\t\t\t\tthis.scheduleReconnect();\n\t\t\t}\n\t\t});\n\n\t\tsocket.on(\"error\", (error) => {\n\t\t\tthis.lastError = error.message;\n\t\t\tthis.connectedIpcPath = null;\n\t\t\tthis.readyReject?.(error);\n\t\t\tthis.readyReject = null;\n\t\t\tthis.readyResolve = null;\n\t\t\tthis.readyPromise = null;\n\t\t\tthis.rejectPendingRequests(error);\n\t\t});\n\n\t\tawait this.readyPromise;\n\t}\n\n\tprivate scheduleReconnect(): void {\n\t\tif (this.reconnectTimer) {\n\t\t\tclearTimeout(this.reconnectTimer);\n\t\t}\n\t\tthis.reconnectTimer = setTimeout(() => {\n\t\t\tthis.reconnectTimer = null;\n\t\t\tvoid this.ensureAuthenticated().catch((error) => {\n\t\t\t\tthis.lastError = error instanceof Error ? error.message : String(error);\n\t\t\t});\n\t\t}, 3_000);\n\t}\n\n\tprivate handleSocketData(chunk: Buffer): void {\n\t\tthis.readBuffer = Buffer.concat([this.readBuffer, chunk]);\n\t\twhile (this.readBuffer.length >= 8) {\n\t\t\tconst op = this.readBuffer.readInt32LE(0);\n\t\t\tconst length = this.readBuffer.readInt32LE(4);\n\t\t\tif (length < 0) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t\"[discord-local] Discarding malformed IPC frame with negative payload length\",\n\t\t\t\t);\n\t\t\t\tthis.readBuffer = Buffer.alloc(0);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (this.readBuffer.length < 8 + length) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst body = this.readBuffer.subarray(8, 8 + length);\n\t\t\tthis.readBuffer = this.readBuffer.subarray(8 + length);\n\t\t\tlet payload: DiscordLocalRpcPayload;\n\t\t\ttry {\n\t\t\t\tpayload = JSON.parse(body.toString(\"utf8\")) as DiscordLocalRpcPayload;\n\t\t\t} catch {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t\"[discord-local] Discarding malformed IPC frame with invalid JSON payload\",\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.handleRpcPayload(op, payload);\n\t\t}\n\t}\n\n\tprivate handleRpcPayload(op: number, payload: DiscordLocalRpcPayload): void {\n\t\tif (op === IPC_OP_PING) {\n\t\t\tthis.writeFrame(IPC_OP_PONG, payload);\n\t\t\treturn;\n\t\t}\n\n\t\tif (op === IPC_OP_CLOSE) {\n\t\t\tthis.lastError = \"Discord local RPC closed the connection\";\n\t\t\tthis.socket?.destroy();\n\t\t\treturn;\n\t\t}\n\n\t\tif (payload.nonce) {\n\t\t\tconst pending = this.pendingRequests.get(payload.nonce);\n\t\t\tif (pending) {\n\t\t\t\tthis.pendingRequests.delete(payload.nonce);\n\t\t\t\tif (payload.evt === \"ERROR\") {\n\t\t\t\t\tpending.reject(new Error(describeRpcError(payload)));\n\t\t\t\t} else {\n\t\t\t\t\tpending.resolve(payload);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (payload.evt === \"READY\") {\n\t\t\tthis.connected = true;\n\t\t\tthis.readyResolve?.();\n\t\t\tthis.readyResolve = null;\n\t\t\tthis.readyReject = null;\n\t\t\tthis.readyPromise = null;\n\t\t\treturn;\n\t\t}\n\n\t\tif (payload.evt === \"MESSAGE_CREATE\") {\n\t\t\tconst data = payload.data as Record<string, unknown> | undefined;\n\t\t\tconst channelId =\n\t\t\t\ttypeof data?.channel_id === \"string\" ? data.channel_id : undefined;\n\t\t\tconst message =\n\t\t\t\tdata && typeof data.message === \"object\"\n\t\t\t\t\t? (data.message as DiscordLocalMessage)\n\t\t\t\t\t: (payload.data as DiscordLocalMessage | undefined);\n\t\t\tif (channelId && message) {\n\t\t\t\tvoid this.ingestMessage(channelId, message);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (payload.evt === \"NOTIFICATION_CREATE\") {\n\t\t\tconst notification = payload.data as DiscordLocalNotification | undefined;\n\t\t\tconst channelId =\n\t\t\t\tnotification?.channel_id ??\n\t\t\t\t(typeof notification?.message?.channel_id === \"string\"\n\t\t\t\t\t? notification.message.channel_id\n\t\t\t\t\t: undefined);\n\t\t\tconst message = notification?.message ?? null;\n\t\t\tif (channelId && message) {\n\t\t\t\tvoid this.ingestMessage(channelId, message);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate writeFrame(op: number, payload: Record<string, unknown>): void {\n\t\tif (!this.socket) {\n\t\t\tthrow new Error(\"Discord local RPC socket is not connected\");\n\t\t}\n\t\tconst body = Buffer.from(JSON.stringify(payload), \"utf8\");\n\t\tconst header = Buffer.alloc(8);\n\t\theader.writeInt32LE(op, 0);\n\t\theader.writeInt32LE(body.length, 4);\n\t\tthis.socket.write(Buffer.concat([header, body]));\n\t}\n\n\tprivate async sendRpcCommand(\n\t\tcmd: string,\n\t\targs: Record<string, unknown> = {},\n\t\tevt?: string,\n\t): Promise<DiscordLocalRpcPayload> {\n\t\tawait this.ensureRpcConnection();\n\t\tconst nonce = crypto.randomUUID();\n\n\t\treturn await new Promise<DiscordLocalRpcPayload>((resolve, reject) => {\n\t\t\tconst timeout = setTimeout(() => {\n\t\t\t\tif (!this.pendingRequests.delete(nonce)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\treject(new Error(`Discord RPC command ${cmd} timed out`));\n\t\t\t}, 20_000);\n\t\t\tthis.pendingRequests.set(nonce, {\n\t\t\t\tresolve: (value) => {\n\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\tresolve(value);\n\t\t\t\t},\n\t\t\t\treject: (error) => {\n\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\treject(error);\n\t\t\t\t},\n\t\t\t});\n\t\t\ttry {\n\t\t\t\tthis.writeFrame(IPC_OP_FRAME, {\n\t\t\t\t\tcmd,\n\t\t\t\t\targs,\n\t\t\t\t\t...(evt ? { evt } : {}),\n\t\t\t\t\tnonce,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\tthis.pendingRequests.delete(nonce);\n\t\t\t\treject(error instanceof Error ? error : new Error(String(error)));\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate rejectPendingRequests(error: Error): void {\n\t\tfor (const pending of this.pendingRequests.values()) {\n\t\t\tpending.reject(error);\n\t\t}\n\t\tthis.pendingRequests.clear();\n\t}\n\n\tprivate async ingestMessage(\n\t\tchannelId: string,\n\t\tmessage: DiscordLocalMessage,\n\t): Promise<void> {\n\t\tif (!message.id) {\n\t\t\treturn;\n\t\t}\n\t\tif (message.author?.id && message.author.id === this.currentUser?.id) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst memoryId = messageIdFor(this.runtime, channelId, message.id);\n\t\tconst existing = await this.runtime.getMemoryById(memoryId);\n\t\tif (existing) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst channel = await this.getChannel(channelId);\n\t\tconst guildId = channel?.guild_id ?? message.guild_id ?? null;\n\t\tconst guild = guildId ? (this.guildCache.get(guildId) ?? null) : null;\n\t\tconst serverKey = guildId ?? `dm:${channelId}`;\n\t\tconst worldId = worldIdFor(this.runtime, serverKey);\n\t\tconst roomId = roomIdFor(this.runtime, channelId);\n\t\tconst entityId = entityIdFor(message.author?.id ?? channelId);\n\t\tconst roomType = roomTypeForChannel(channel?.type);\n\t\tconst roomName =\n\t\t\tchannel?.name?.trim() ||\n\t\t\tmessage.author?.global_name ||\n\t\t\tmessage.author?.username ||\n\t\t\t`Discord ${channelId}`;\n\n\t\t// `roomName` is accepted by the local `./eliza` source but not by\n\t\t// the npm alpha dist-tag of `@elizaos/core`. Cast around the\n\t\t// excess-property check so the call works under both resolutions;\n\t\t// the runtime itself reads `roomName` in both versions, the type\n\t\t// just lags in the published package.\n\t\ttype EnsureConnectionArg = Parameters<\n\t\t\ttypeof this.runtime.ensureConnection\n\t\t>[0] & { roomName?: string };\n\t\tawait this.runtime.ensureConnection({\n\t\t\tentityId,\n\t\t\troomId,\n\t\t\troomName,\n\t\t\tworldId,\n\t\t\tworldName: guild?.name ?? \"Discord Direct Messages\",\n\t\t\tuserName: message.author?.username ?? undefined,\n\t\t\tname:\n\t\t\t\tmessage.author?.global_name ?? message.author?.username ?? undefined,\n\t\t\tsource: DISCORD_LOCAL_SERVICE_NAME,\n\t\t\ttype: roomType,\n\t\t\tchannelId,\n\t\t\tmessageServerId: stringToUuid(\n\t\t\t\t`discord-local-server:${serverKey}`,\n\t\t\t) as UUID,\n\t\t\tmetadata: {\n\t\t\t\tdiscordChannelId: channelId,\n\t\t\t\t...(guildId ? { discordServerId: guildId } : {}),\n\t\t\t},\n\t\t} as EnsureConnectionArg);\n\n\t\tconst attachments: Media[] = (message.attachments ?? []).flatMap(\n\t\t\t(attachment) => {\n\t\t\t\tconst url = attachment.url?.trim();\n\t\t\t\tif (!url) {\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t\treturn [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: attachment.id,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\ttitle: attachment.filename,\n\t\t\t\t\t\tsource: DISCORD_LOCAL_SERVICE_NAME,\n\t\t\t\t\t\tdescription: attachment.description ?? undefined,\n\t\t\t\t\t\tcontentType: contentTypeForMime(attachment.content_type),\n\t\t\t\t\t},\n\t\t\t\t];\n\t\t\t},\n\t\t);\n\n\t\tconst replyReference =\n\t\t\tmessage.referenced_message?.id ??\n\t\t\tmessage.message_reference?.message_id ??\n\t\t\tundefined;\n\t\tconst replyChannelId =\n\t\t\tmessage.referenced_message?.channel_id ??\n\t\t\tmessage.message_reference?.channel_id ??\n\t\t\tchannelId;\n\t\tconst inReplyTo =\n\t\t\ttypeof replyReference === \"string\" && replyReference.length > 0\n\t\t\t\t? messageIdFor(this.runtime, replyChannelId, replyReference)\n\t\t\t\t: undefined;\n\n\t\tconst memory = createMessageMemory({\n\t\t\tid: memoryId,\n\t\t\tagentId: this.runtime.agentId,\n\t\t\tentityId,\n\t\t\troomId,\n\t\t\tcontent: {\n\t\t\t\ttext: message.content ?? \"\",\n\t\t\t\tsource: DISCORD_LOCAL_SERVICE_NAME,\n\t\t\t\t...(attachments.length > 0 ? { attachments } : {}),\n\t\t\t\t...(inReplyTo ? { inReplyTo } : {}),\n\t\t\t},\n\t\t}) as Memory;\n\t\tmemory.createdAt = message.timestamp\n\t\t\t? Date.parse(message.timestamp)\n\t\t\t: Date.now();\n\t\tmemory.metadata = {\n\t\t\t...(memory.metadata ?? {}),\n\t\t\tentityName:\n\t\t\t\tmessage.author?.global_name ?? message.author?.username ?? roomName,\n\t\t\tentityUserName: message.author?.username ?? undefined,\n\t\t\tentityAvatarUrl: buildDiscordAvatarUrl(message.author),\n\t\t\tfromId: message.author?.id ?? undefined,\n\t\t\tdiscordChannelId: channelId,\n\t\t\tdiscordMessageId: message.id,\n\t\t\t...(guildId ? { discordServerId: guildId } : {}),\n\t\t} as MemoryMetadata;\n\n\t\tawait this.runtime.createMemory(memory, \"messages\");\n\t}\n\n\tprivate async sendUiMessage(\n\t\tchannelId: string,\n\t\tguildId: string | null,\n\t\ttext: string,\n\t): Promise<void> {\n\t\tconst config = this.requireConfig();\n\t\tif (process.platform !== \"darwin\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"Discord local send automation currently supports macOS only\",\n\t\t\t);\n\t\t}\n\n\t\tawait openDiscordTarget(channelId, guildId);\n\t\tconst script = buildDiscordSendScript(text, config.sendDelayMs / 1000);\n\t\tawait execFileAsync(\"/usr/bin/osascript\", [\"-e\", script]);\n\t}\n}\n\nconst discordLocalPlugin: Plugin = {\n\tname: DISCORD_LOCAL_PLUGIN_NAME,\n\tdescription:\n\t\t\"Local Discord desktop integration for Eliza via Discord RPC and macOS UI automation\",\n\tservices: [DiscordLocalService],\n};\n\nexport default discordLocalPlugin;\n",
|
|
62
|
+
"/**\n * Discord local setup HTTP routes.\n *\n * Provides status, authorization, and subscription management for the\n * Discord local IPC connector:\n *\n * GET /api/discord-local/status connection + auth status\n * POST /api/discord-local/authorize start OAuth authorize flow\n * POST /api/discord-local/disconnect tear down session\n * GET /api/discord-local/guilds list guilds (requires auth)\n * GET /api/discord-local/channels list channels for a guild\n * POST /api/discord-local/subscriptions subscribe to channel messages\n *\n * These routes are registered with `rawPath: true` so they mount at their\n * legacy paths without the plugin-name prefix.\n */\n\nimport type {\n\tIAgentRuntime,\n\tRoute,\n\tRouteRequest,\n\tRouteResponse,\n} from \"@elizaos/core\";\nimport { DISCORD_LOCAL_SERVICE_NAME } from \"./discord-local-service\";\n\ninterface DiscordLocalServiceLike {\n\tgetStatus(): Record<string, unknown>;\n\tauthorize(): Promise<Record<string, unknown>>;\n\tdisconnectSession(): Promise<void>;\n\tlistGuilds(): Promise<Array<Record<string, unknown>>>;\n\tlistChannels(guildId: string): Promise<Array<Record<string, unknown>>>;\n\tsubscribeChannelMessages(channelIds: string[]): Promise<string[]>;\n}\n\n/**\n * Minimal interface for the connector-setup service exposed by the agent.\n * Plugins access it via `runtime.getService(\"connector-setup\")`.\n */\ninterface ConnectorSetupService {\n\tgetConfig(): Record<string, unknown>;\n\tpersistConfig(config: Record<string, unknown>): void;\n\tupdateConfig(updater: (config: Record<string, unknown>) => void): void;\n\tregisterEscalationChannel(channelName: string): boolean;\n\tsetOwnerContact(update: {\n\t\tsource: string;\n\t\tchannelId?: string;\n\t\tentityId?: string;\n\t\troomId?: string;\n\t}): boolean;\n}\n\ninterface ConnectorConfig {\n\tenabled?: boolean;\n\tmessageChannelIds?: string[];\n\t[key: string]: unknown;\n}\n\nfunction isConnectorSetupService(\n\tservice: unknown,\n): service is ConnectorSetupService {\n\tif (!service || typeof service !== \"object\") {\n\t\treturn false;\n\t}\n\n\tconst candidate = service as Record<string, unknown>;\n\treturn (\n\t\ttypeof candidate.getConfig === \"function\" &&\n\t\ttypeof candidate.persistConfig === \"function\" &&\n\t\ttypeof candidate.updateConfig === \"function\" &&\n\t\ttypeof candidate.registerEscalationChannel === \"function\" &&\n\t\ttypeof candidate.setOwnerContact === \"function\"\n\t);\n}\n\nfunction isDiscordLocalServiceLike(\n\tservice: unknown,\n): service is DiscordLocalServiceLike {\n\tif (!service || typeof service !== \"object\") {\n\t\treturn false;\n\t}\n\n\tconst candidate = service as Record<string, unknown>;\n\treturn (\n\t\ttypeof candidate.getStatus === \"function\" &&\n\t\ttypeof candidate.authorize === \"function\" &&\n\t\ttypeof candidate.disconnectSession === \"function\" &&\n\t\ttypeof candidate.listGuilds === \"function\" &&\n\t\ttypeof candidate.listChannels === \"function\" &&\n\t\ttypeof candidate.subscribeChannelMessages === \"function\"\n\t);\n}\n\nfunction getSetupService(runtime: IAgentRuntime): ConnectorSetupService | null {\n\tconst service = runtime.getService(\"connector-setup\");\n\treturn isConnectorSetupService(service) ? service : null;\n}\n\nfunction resolveService(\n\truntime: IAgentRuntime,\n): DiscordLocalServiceLike | null {\n\tconst raw = runtime.getService(DISCORD_LOCAL_SERVICE_NAME);\n\treturn isDiscordLocalServiceLike(raw) ? raw : null;\n}\n\nfunction getConnectorConfig(\n\tsetupService: ConnectorSetupService,\n): ConnectorConfig {\n\tconst config = setupService.getConfig();\n\tconst connectors =\n\t\t(config.connectors as Record<string, ConnectorConfig> | undefined) ??\n\t\t((config as Record<string, unknown>).channels as\n\t\t\t| Record<string, ConnectorConfig>\n\t\t\t| undefined) ??\n\t\t{};\n\n\tconst current = connectors.discordLocal;\n\tif (current && typeof current === \"object\" && !Array.isArray(current)) {\n\t\treturn current as ConnectorConfig;\n\t}\n\treturn {};\n}\n\n// ── GET /api/discord-local/status ──────────────────────────────────\nfunction getUnregisteredStatus() {\n\treturn {\n\t\tavailable: false,\n\t\tconnected: false,\n\t\tauthenticated: false,\n\t\tcurrentUser: null,\n\t\tsubscribedChannelIds: [],\n\t\tconfiguredChannelIds: [],\n\t\tscopes: [],\n\t\tlastError: \"discord-local service not registered\",\n\t\tipcPath: null,\n\t\treason: \"discord-local service not registered\",\n\t};\n}\n\nasync function handleStatus(\n\t_req: RouteRequest,\n\tres: RouteResponse,\n\truntime: IAgentRuntime,\n): Promise<void> {\n\tconst service = resolveService(runtime);\n\tres.status(200).json(service ? service.getStatus() : getUnregisteredStatus());\n}\n\n// ── POST /api/discord-local/authorize ──────────────────────────────\nasync function handleAuthorize(\n\t_req: RouteRequest,\n\tres: RouteResponse,\n\truntime: IAgentRuntime,\n): Promise<void> {\n\tconst service = resolveService(runtime);\n\tif (!service) {\n\t\tres.status(503).json({ error: \"discord-local service not registered\" });\n\t\treturn;\n\t}\n\ttry {\n\t\tres.status(200).json(await service.authorize());\n\t} catch (error) {\n\t\tres.status(500).json({\n\t\t\terror: `failed to authorize discord-local: ${error instanceof Error ? error.message : String(error)}`,\n\t\t});\n\t}\n}\n\n// ── POST /api/discord-local/disconnect ─────────────────────────────\nasync function handleDisconnect(\n\t_req: RouteRequest,\n\tres: RouteResponse,\n\truntime: IAgentRuntime,\n): Promise<void> {\n\tconst service = resolveService(runtime);\n\tif (!service) {\n\t\tres.status(503).json({ error: \"discord-local service not registered\" });\n\t\treturn;\n\t}\n\ttry {\n\t\tawait service.disconnectSession();\n\t\tres.status(200).json({ ok: true });\n\t} catch (error) {\n\t\tres.status(500).json({\n\t\t\terror: `failed to disconnect discord-local: ${error instanceof Error ? error.message : String(error)}`,\n\t\t});\n\t}\n}\n\n// ── GET /api/discord-local/guilds ──────────────────────────────────\nasync function handleGuilds(\n\t_req: RouteRequest,\n\tres: RouteResponse,\n\truntime: IAgentRuntime,\n): Promise<void> {\n\tconst service = resolveService(runtime);\n\tif (!service) {\n\t\tres.status(503).json({ error: \"discord-local service not registered\" });\n\t\treturn;\n\t}\n\ttry {\n\t\tconst guilds = await service.listGuilds();\n\t\tres.status(200).json({ guilds, count: guilds.length });\n\t} catch (error) {\n\t\tres.status(500).json({\n\t\t\terror: `failed to list discord-local guilds: ${error instanceof Error ? error.message : String(error)}`,\n\t\t});\n\t}\n}\n\n// ── GET /api/discord-local/channels ────────────────────────────────\nasync function handleChannels(\n\treq: RouteRequest,\n\tres: RouteResponse,\n\truntime: IAgentRuntime,\n): Promise<void> {\n\tconst service = resolveService(runtime);\n\tif (!service) {\n\t\tres.status(503).json({ error: \"discord-local service not registered\" });\n\t\treturn;\n\t}\n\n\tconst url = new URL(\n\t\t(req as unknown as { url?: string }).url ?? \"/api/discord-local/channels\",\n\t\t\"http://localhost\",\n\t);\n\tconst guildId = url.searchParams.get(\"guildId\")?.trim() ?? \"\";\n\tif (!guildId) {\n\t\tres.status(400).json({ error: \"guildId is required\" });\n\t\treturn;\n\t}\n\n\ttry {\n\t\tconst channels = await service.listChannels(guildId);\n\t\tres.status(200).json({ channels, count: channels.length });\n\t} catch (error) {\n\t\tres.status(500).json({\n\t\t\terror: `failed to list discord-local channels: ${error instanceof Error ? error.message : String(error)}`,\n\t\t});\n\t}\n}\n\n// ── POST /api/discord-local/subscriptions ──────────────────────────\nasync function handleSubscriptions(\n\treq: RouteRequest,\n\tres: RouteResponse,\n\truntime: IAgentRuntime,\n): Promise<void> {\n\tconst service = resolveService(runtime);\n\tif (!service) {\n\t\tres.status(503).json({ error: \"discord-local service not registered\" });\n\t\treturn;\n\t}\n\n\tconst body = (req.body as { channelIds?: string[] } | null) ?? null;\n\tif (!body) {\n\t\tres.status(400).json({ error: \"request body is required\" });\n\t\treturn;\n\t}\n\n\tconst channelIds = Array.isArray(body.channelIds)\n\t\t? Array.from(\n\t\t\t\tnew Set(\n\t\t\t\t\tbody.channelIds\n\t\t\t\t\t\t.map((entry) => (typeof entry === \"string\" ? entry.trim() : \"\"))\n\t\t\t\t\t\t.filter((entry) => entry.length > 0),\n\t\t\t\t),\n\t\t\t)\n\t\t: [];\n\n\ttry {\n\t\tconst subscribedChannelIds =\n\t\t\tawait service.subscribeChannelMessages(channelIds);\n\n\t\tconst setupService = getSetupService(runtime);\n\t\tif (setupService) {\n\t\t\tconst connectorConfig = getConnectorConfig(setupService);\n\t\t\tsetupService.updateConfig((config) => {\n\t\t\t\tif (!config.connectors) {\n\t\t\t\t\tconfig.connectors = {};\n\t\t\t\t}\n\t\t\t\t(config.connectors as Record<string, ConnectorConfig>).discordLocal = {\n\t\t\t\t\t...connectorConfig,\n\t\t\t\t\tenabled: connectorConfig.enabled !== false,\n\t\t\t\t\tmessageChannelIds: subscribedChannelIds,\n\t\t\t\t};\n\t\t\t});\n\n\t\t\t// Auto-populate owner contact so LifeOps can deliver reminders\n\t\t\tif (subscribedChannelIds.length > 0) {\n\t\t\t\tsetupService.setOwnerContact({\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\tchannelId: subscribedChannelIds[0],\n\t\t\t\t});\n\t\t\t\t// Add Discord to the escalation channel list so it is reachable\n\t\t\t\t// without the user explicitly configuring escalation.\n\t\t\t\tsetupService.registerEscalationChannel(\"discord\");\n\t\t\t}\n\t\t}\n\n\t\tres.status(200).json({ subscribedChannelIds });\n\t} catch (error) {\n\t\tres.status(500).json({\n\t\t\terror: `failed to update discord-local subscriptions: ${error instanceof Error ? error.message : String(error)}`,\n\t\t});\n\t}\n}\n\n/**\n * Plugin routes for Discord local setup.\n * Registered with `rawPath: true` to preserve legacy `/api/discord-local/*` paths.\n */\nexport const discordSetupRoutes: Route[] = [\n\t{\n\t\ttype: \"GET\",\n\t\tpath: \"/api/discord-local/status\",\n\t\thandler: handleStatus,\n\t\trawPath: true,\n\t},\n\t{\n\t\ttype: \"POST\",\n\t\tpath: \"/api/discord-local/authorize\",\n\t\thandler: handleAuthorize,\n\t\trawPath: true,\n\t},\n\t{\n\t\ttype: \"POST\",\n\t\tpath: \"/api/discord-local/disconnect\",\n\t\thandler: handleDisconnect,\n\t\trawPath: true,\n\t},\n\t{\n\t\ttype: \"GET\",\n\t\tpath: \"/api/discord-local/guilds\",\n\t\thandler: handleGuilds,\n\t\trawPath: true,\n\t},\n\t{\n\t\ttype: \"GET\",\n\t\tpath: \"/api/discord-local/channels\",\n\t\thandler: handleChannels,\n\t\trawPath: true,\n\t},\n\t{\n\t\ttype: \"POST\",\n\t\tpath: \"/api/discord-local/subscriptions\",\n\t\thandler: handleSubscriptions,\n\t\trawPath: true,\n\t},\n];\n",
|
|
63
|
+
"import type { Readable } from \"node:stream\";\nimport {\n\tAudioPlayerStatus,\n\tcreateAudioPlayer,\n\tcreateAudioResource,\n\tentersState,\n\tNoSubscriberBehavior,\n\ttype VoiceConnection,\n\tVoiceConnectionStatus,\n} from \"@discordjs/voice\";\nimport {\n\ttype IAgentRuntime,\n\tlogger,\n\tModelType,\n\ttype TestSuite,\n} from \"@elizaos/core\";\nimport {\n\tAttachmentBuilder,\n\tChannelType,\n\tEvents,\n\ttype Guild,\n\ttype Message,\n\ttype TextChannel,\n} from \"discord.js\";\nimport type { DiscordService } from \"./service\";\nimport { ServiceType } 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; // Use definite assignment assertion\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\tServiceType.DISCORD,\n\t\t\t) as DiscordService;\n\t\t\tif (!this.discordClient) {\n\t\t\t\tthrow new Error(\"Failed to get DiscordService from runtime.\");\n\t\t\t}\n\n\t\t\t// Wait for the bot to be ready before proceeding\n\t\t\tconst discordClient = this.discordClient.client;\n\t\t\tif (discordClient?.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\tif (!discordClient) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"Discord client instance is missing within the service.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tawait new Promise((resolve, reject) => {\n\t\t\t\t\tif (discordClient) {\n\t\t\t\t\t\tdiscordClient.once(Events.ClientReady, resolve);\n\t\t\t\t\t\tdiscordClient.once(Events.Error, reject);\n\t\t\t\t\t}\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\tif (!this.discordClient) {\n\t\t\tthrow new Error(\"Discord client not initialized.\");\n\t\t}\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?.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\tinterface MockJoinInteraction {\n\t\t\t\tisCommand: () => boolean;\n\t\t\t\tcommandName: string;\n\t\t\t\toptions: {\n\t\t\t\t\tget: (name: string) => { value: string } | null;\n\t\t\t\t};\n\t\t\t\tguild: Guild;\n\t\t\t\tdeferReply: () => Promise<void>;\n\t\t\t\teditReply: (message: string) => Promise<void>;\n\t\t\t}\n\t\t\tconst fakeJoinInteraction: MockJoinInteraction = {\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\tif (!this.discordClient.voiceManager) {\n\t\t\t\tthrow new Error(\"VoiceManager is not available on the Discord client.\");\n\t\t\t}\n\t\t\tawait this.discordClient.voiceManager.handleJoinChannelCommand(\n\t\t\t\tfakeJoinInteraction,\n\t\t\t);\n\n\t\t\tlogger.success(\"Join voice slash command test completed successfully.\");\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error in join voice 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\tif (!this.discordClient) {\n\t\t\tthrow new Error(\"Discord client not initialized.\");\n\t\t}\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?.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\tinterface LeaveChannelInteraction {\n\t\t\t\tguildId: string | null;\n\t\t\t\treply: (message: string) => Promise<void>;\n\t\t\t}\n\t\t\tconst fakeLeaveInteraction: LeaveChannelInteraction = {\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\tif (!this.discordClient.voiceManager) {\n\t\t\t\tthrow new Error(\"VoiceManager is not available on the Discord client.\");\n\t\t\t}\n\t\t\tawait this.discordClient.voiceManager.handleLeaveChannelCommand(\n\t\t\t\tfakeLeaveInteraction,\n\t\t\t);\n\n\t\t\tlogger.success(\"Leave voice slash command test completed successfully.\");\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error in leave voice 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\tif (!this.discordClient) {\n\t\t\tthrow new Error(\"Discord client not initialized.\");\n\t\t}\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\tif (!this.discordClient.voiceManager) {\n\t\t\t\tthrow new Error(\"VoiceManager is not available on the Discord client.\");\n\t\t\t}\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\n\t\t\tif (!this.discordClient.voiceManager) {\n\t\t\t\tthrow new Error(\"VoiceManager is not available on the Discord client.\");\n\t\t\t}\n\t\t\tconst connection =\n\t\t\t\tthis.discordClient.voiceManager.getVoiceConnection(guildId);\n\n\t\t\tif (!connection) {\n\t\t\t\tthrow new Error(`No voice connection found for guild: ${guildId}`);\n\t\t\t}\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\tModelType.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\tif (!this.discordClient) {\n\t\t\tthrow new Error(\"Discord client not initialized.\");\n\t\t}\n\t\ttry {\n\t\t\tconst channel = await this.getTestChannel(runtime);\n\t\t\tif (!channel?.isTextBased()) {\n\t\t\t\tthrow new Error(\"Cannot send message to a non-text channel.\");\n\t\t\t}\n\t\t\tconst attachment = new AttachmentBuilder(TEST_IMAGE_URL);\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[attachment],\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\tif (!this.discordClient) {\n\t\t\tthrow new Error(\"Discord client not initialized.\");\n\t\t}\n\t\ttry {\n\t\t\tconst channel = await this.getTestChannel(runtime);\n\n\t\t\t// Create a mock message that satisfies the Message interface requirements\n\t\t\tinterface MockMessage {\n\t\t\t\tcontent: string;\n\t\t\t\tauthor: {\n\t\t\t\t\tid: string;\n\t\t\t\t\tusername: string;\n\t\t\t\t\tbot: boolean;\n\t\t\t\t};\n\t\t\t\tchannel: typeof channel;\n\t\t\t\tid: string;\n\t\t\t\tcreatedTimestamp: number;\n\t\t\t\tmentions: {\n\t\t\t\t\thas: (id: string) => boolean;\n\t\t\t\t};\n\t\t\t\treference: { messageId?: string } | null;\n\t\t\t\tattachments: unknown[];\n\t\t\t\tinteraction?: unknown;\n\t\t\t}\n\t\t\tconst fakeMessage: MockMessage = {\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\tif (!this.discordClient.messageManager) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"MessageManager is not available on the Discord client.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Type assertion through unknown is needed because MockMessage is a minimal test stub\n\t\t\tawait this.discordClient.messageManager.handleMessage(\n\t\t\t\tfakeMessage as unknown as Message<boolean>,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Error in handling message test: ${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\tif (!this.discordClient) {\n\t\t\tthrow new Error(\"Discord client not initialized.\");\n\t\t}\n\t\tconst channelId = this.validateChannelId(runtime);\n\t\tconst discordClient = this.discordClient.client;\n\t\tconst channel =\n\t\t\tdiscordClient && (await discordClient.channels.fetch(channelId));\n\n\t\tif (!channel) {\n\t\t\tthrow new Error(\"no test channel found!\");\n\t\t}\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 {AttachmentBuilder[]} 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: AttachmentBuilder[],\n\t) {\n\t\ttry {\n\t\t\tif (!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\t// Pass empty string for _inReplyTo as it expects a string\n\t\t\tawait sendMessageInChunks(\n\t\t\t\tchannel as TextChannel,\n\t\t\t\tmessageContent,\n\t\t\t\t\"\",\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 responseStream - The response stream to play as audio.\n\t * @param connection - The VoiceConnection to use for playing the audio.\n\t * @returns A Promise that resolves when the TTS playback is finished.\n\t */\n\tasync playAudioStream(\n\t\tresponseStream: ReadableStream | NodeJS.ReadableStream | Readable,\n\t\tconnection: VoiceConnection,\n\t) {\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 as Readable);\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\tif (!discordClient.client) {\n\t\t\tthrow new Error(\"Discord client instance is missing within the service.\");\n\t\t}\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) => {\n\t\t\tconst membersMe = g.members.me;\n\t\t\treturn membersMe?.voice?.channelId;\n\t\t});\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\t// This check might be redundant if called after the initial test setup check, but safe to keep.\n\t\t\tthrow new Error(\"Discord client is not initialized.\");\n\t\t}\n\n\t\tif (!discordClient.voiceManager) {\n\t\t\tthrow new Error(\"VoiceManager is not available on the Discord client.\");\n\t\t}\n\n\t\tconst voiceManager = discordClient.voiceManager;\n\t\tif (!voiceManager?.isReady()) {\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tif (voiceManager) {\n\t\t\t\t\tvoiceManager.once(\"ready\", resolve);\n\t\t\t\t\tvoiceManager.once(\"error\", reject);\n\t\t\t\t}\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 as string; // Assert as string since we check for falsy above\n\t}\n}\n",
|
|
64
|
+
"import type { IAgentRuntime } from \"@elizaos/core\";\n\n/**\n * Default account identifier used when no specific account is configured\n */\nexport const DEFAULT_ACCOUNT_ID = \"default\";\n\n/**\n * Source of the Discord token\n */\nexport type DiscordTokenSource = \"env\" | \"config\" | \"character\" | \"none\";\n\n/**\n * Result of token resolution\n */\nexport interface DiscordTokenResolution {\n\ttoken: string;\n\tsource: DiscordTokenSource;\n}\n\n/**\n * Configuration for a single Discord account\n */\nexport interface DiscordAccountConfig {\n\t/** Optional display name for this account */\n\tname?: string;\n\t/** If false, do not start this Discord account */\n\tenabled?: boolean;\n\t/** Discord bot token for this account */\n\ttoken?: string;\n\t/** Allow bot-authored messages to trigger replies */\n\tallowBots?: boolean;\n\t/** Controls how guild channel messages are handled */\n\tgroupPolicy?: \"open\" | \"disabled\" | \"allowlist\";\n\t/** Outbound text chunk size (chars) */\n\ttextChunkLimit?: number;\n\t/** Max lines per message */\n\tmaxLinesPerMessage?: number;\n\t/** Max media size in MB */\n\tmediaMaxMb?: number;\n\t/** History limit for context */\n\thistoryLimit?: number;\n\t/** Max DM turns to keep as history context */\n\tdmHistoryLimit?: number;\n\t/** DM configuration */\n\tdm?: DiscordDmConfig;\n\t/** Per-guild configuration */\n\tguilds?: Record<string, DiscordGuildEntry>;\n\t/** Channel IDs to allow */\n\tchannelIds?: string[];\n\t/** Listen-only channel IDs */\n\tlistenChannelIds?: string[];\n\t/** Whether to ignore bot messages */\n\tshouldIgnoreBotMessages?: boolean;\n\t/** Whether to ignore direct messages */\n\tshouldIgnoreDirectMessages?: boolean;\n\t/** Whether to respond only to mentions */\n\tshouldRespondOnlyToMentions?: boolean;\n}\n\n/**\n * DM-specific configuration\n */\nexport interface DiscordDmConfig {\n\t/** If false, ignore all incoming Discord DMs */\n\tenabled?: boolean;\n\t/** Direct message access policy */\n\tpolicy?: \"open\" | \"disabled\" | \"allowlist\" | \"pairing\";\n\t/** Allowlist for DM senders (ids or names) */\n\tallowFrom?: Array<string | number>;\n\t/** If true, allow group DMs */\n\tgroupEnabled?: boolean;\n\t/** Optional allowlist for group DM channels */\n\tgroupChannels?: Array<string | number>;\n}\n\n/**\n * Channel-specific configuration within a guild\n */\nexport interface DiscordGuildChannelConfig {\n\t/** Whether this channel is allowed */\n\tallow?: boolean;\n\t/** Require bot mention to respond */\n\trequireMention?: boolean;\n\t/** Skills to load for this channel */\n\tskills?: string[];\n\t/** If false, disable the bot for this channel */\n\tenabled?: boolean;\n\t/** Allowlist for channel senders */\n\tusers?: Array<string | number>;\n\t/** System prompt snippet for this channel */\n\tsystemPrompt?: string;\n\t/** Auto-create threads for replies */\n\tautoThread?: boolean;\n}\n\n/**\n * Guild-level configuration\n */\nexport interface DiscordGuildEntry {\n\t/** Guild slug for name-based matching */\n\tslug?: string;\n\t/** Require bot mention to respond */\n\trequireMention?: boolean;\n\t/** Reaction notification mode */\n\treactionNotifications?: \"off\" | \"own\" | \"all\" | \"allowlist\";\n\t/** Allowlist for guild users */\n\tusers?: Array<string | number>;\n\t/** Per-channel configuration */\n\tchannels?: Record<string, DiscordGuildChannelConfig>;\n}\n\n/**\n * Multi-account Discord configuration structure\n */\nexport interface DiscordMultiAccountConfig {\n\t/** Default/base configuration applied to all accounts */\n\tenabled?: boolean;\n\ttoken?: string;\n\t/** Per-account configuration overrides */\n\taccounts?: Record<string, DiscordAccountConfig>;\n}\n\n/**\n * Resolved Discord account with all configuration merged\n */\nexport interface ResolvedDiscordAccount {\n\taccountId: string;\n\tenabled: boolean;\n\tname?: string;\n\ttoken: string;\n\ttokenSource: DiscordTokenSource;\n\tconfig: DiscordAccountConfig;\n}\n\n/**\n * Normalizes an account ID, returning the default if not provided\n */\nexport function normalizeAccountId(accountId?: string | null): string {\n\tif (!accountId || typeof accountId !== \"string\") {\n\t\treturn DEFAULT_ACCOUNT_ID;\n\t}\n\tconst trimmed = accountId.trim().toLowerCase();\n\treturn trimmed || DEFAULT_ACCOUNT_ID;\n}\n\n/**\n * Normalizes a Discord token by trimming and removing \"Bot \" prefix\n */\nexport function normalizeDiscordToken(raw?: string | null): string | undefined {\n\tconst trimmed = raw?.trim();\n\treturn trimmed ? trimmed.replace(/^Bot\\s+/i, \"\") : undefined;\n}\n\n/**\n * Gets the multi-account configuration from runtime settings\n */\nexport function getMultiAccountConfig(\n\truntime: IAgentRuntime,\n): DiscordMultiAccountConfig {\n\tconst characterDiscord = runtime.character?.settings?.discord as\n\t\t| DiscordMultiAccountConfig\n\t\t| undefined;\n\n\treturn {\n\t\tenabled: characterDiscord?.enabled,\n\t\ttoken: characterDiscord?.token,\n\t\taccounts: characterDiscord?.accounts,\n\t};\n}\n\n/**\n * Lists all configured account IDs\n */\nexport function listDiscordAccountIds(runtime: IAgentRuntime): string[] {\n\tconst config = getMultiAccountConfig(runtime);\n\tconst accounts = config.accounts;\n\n\tif (!accounts || typeof accounts !== \"object\") {\n\t\treturn [DEFAULT_ACCOUNT_ID];\n\t}\n\n\tconst ids = Object.keys(accounts).filter(Boolean);\n\tif (ids.length === 0) {\n\t\treturn [DEFAULT_ACCOUNT_ID];\n\t}\n\n\treturn ids.slice().sort((a, b) => a.localeCompare(b));\n}\n\n/**\n * Resolves the default account ID to use\n */\nexport function resolveDefaultDiscordAccountId(runtime: IAgentRuntime): string {\n\tconst ids = listDiscordAccountIds(runtime);\n\tif (ids.includes(DEFAULT_ACCOUNT_ID)) {\n\t\treturn DEFAULT_ACCOUNT_ID;\n\t}\n\treturn ids[0] ?? DEFAULT_ACCOUNT_ID;\n}\n\n/**\n * Gets the account-specific configuration\n */\nfunction getAccountConfig(\n\truntime: IAgentRuntime,\n\taccountId: string,\n): DiscordAccountConfig | undefined {\n\tconst config = getMultiAccountConfig(runtime);\n\tconst accounts = config.accounts;\n\n\tif (!accounts || typeof accounts !== \"object\") {\n\t\treturn undefined;\n\t}\n\n\treturn accounts[accountId];\n}\n\n/**\n * Removes undefined values from an object to prevent them from overwriting during spread\n */\nfunction filterDefined<T extends object>(obj: T): Partial<T> {\n\treturn Object.fromEntries(\n\t\tObject.entries(obj).filter(([, v]) => v !== undefined),\n\t) as Partial<T>;\n}\n\nfunction parseOptionalBooleanSetting(\n\truntime: IAgentRuntime,\n\tkey: string,\n): boolean | undefined {\n\tconst value = runtime.getSetting(key);\n\tif (value === undefined || value === null) {\n\t\treturn undefined;\n\t}\n\tif (typeof value === \"boolean\") {\n\t\treturn value;\n\t}\n\tconst normalized = String(value).trim().toLowerCase();\n\tif (!normalized) {\n\t\treturn undefined;\n\t}\n\tif (normalized === \"true\" || normalized === \"1\") {\n\t\treturn true;\n\t}\n\tif (normalized === \"false\" || normalized === \"0\") {\n\t\treturn false;\n\t}\n\treturn undefined;\n}\n\n/**\n * Merges base configuration with account-specific overrides\n */\nfunction mergeDiscordAccountConfig(\n\truntime: IAgentRuntime,\n\taccountId: string,\n): DiscordAccountConfig {\n\tconst multiConfig = getMultiAccountConfig(runtime);\n\tconst { accounts: _ignored, ...baseConfig } = multiConfig;\n\tconst accountConfig = getAccountConfig(runtime, accountId) ?? {};\n\n\t// Get environment/runtime settings for the base config\n\tconst envChannelIds = runtime.getSetting(\"CHANNEL_IDS\") as string | undefined;\n\tconst envListenChannelIds = runtime.getSetting(\n\t\t\"DISCORD_LISTEN_CHANNEL_IDS\",\n\t) as string | undefined;\n\n\tconst envConfig: DiscordAccountConfig = {\n\t\tshouldIgnoreBotMessages: parseOptionalBooleanSetting(\n\t\t\truntime,\n\t\t\t\"DISCORD_SHOULD_IGNORE_BOT_MESSAGES\",\n\t\t),\n\t\tshouldIgnoreDirectMessages: parseOptionalBooleanSetting(\n\t\t\truntime,\n\t\t\t\"DISCORD_SHOULD_IGNORE_DIRECT_MESSAGES\",\n\t\t),\n\t\tshouldRespondOnlyToMentions: parseOptionalBooleanSetting(\n\t\t\truntime,\n\t\t\t\"DISCORD_SHOULD_RESPOND_ONLY_TO_MENTIONS\",\n\t\t),\n\t\tchannelIds: envChannelIds\n\t\t\t? envChannelIds\n\t\t\t\t\t.split(\",\")\n\t\t\t\t\t.map((s) => s.trim())\n\t\t\t\t\t.filter(Boolean)\n\t\t\t: undefined,\n\t\tlistenChannelIds: envListenChannelIds\n\t\t\t? envListenChannelIds\n\t\t\t\t\t.split(\",\")\n\t\t\t\t\t.map((s) => s.trim())\n\t\t\t\t\t.filter(Boolean)\n\t\t\t: undefined,\n\t};\n\n\t// Merge order: env defaults < base config < account config\n\t// Filter undefined values to prevent them from overwriting defined values\n\treturn {\n\t\t...filterDefined(envConfig),\n\t\t...filterDefined(baseConfig),\n\t\t...filterDefined(accountConfig),\n\t};\n}\n\n/**\n * Resolves the Discord token for a specific account\n */\nexport function resolveDiscordToken(\n\truntime: IAgentRuntime,\n\topts: { accountId?: string | null } = {},\n): DiscordTokenResolution {\n\tconst accountId = normalizeAccountId(opts.accountId);\n\tconst multiConfig = getMultiAccountConfig(runtime);\n\n\t// Check account-specific token first\n\tconst accountConfig =\n\t\taccountId !== DEFAULT_ACCOUNT_ID\n\t\t\t? multiConfig.accounts?.[accountId]\n\t\t\t: multiConfig.accounts?.[DEFAULT_ACCOUNT_ID];\n\n\tconst accountToken = normalizeDiscordToken(accountConfig?.token);\n\tif (accountToken) {\n\t\treturn { token: accountToken, source: \"config\" };\n\t}\n\n\t// For default account, check base config token\n\tconst allowBase = accountId === DEFAULT_ACCOUNT_ID;\n\tconst baseToken = allowBase\n\t\t? normalizeDiscordToken(multiConfig.token)\n\t\t: undefined;\n\tif (baseToken) {\n\t\treturn { token: baseToken, source: \"character\" };\n\t}\n\n\t// For default account, check environment token\n\tconst envToken = allowBase\n\t\t? normalizeDiscordToken(runtime.getSetting(\"DISCORD_API_TOKEN\") as string)\n\t\t: undefined;\n\tif (envToken) {\n\t\treturn { token: envToken, source: \"env\" };\n\t}\n\n\treturn { token: \"\", source: \"none\" };\n}\n\n/**\n * Resolves a complete Discord account configuration\n */\nexport function resolveDiscordAccount(\n\truntime: IAgentRuntime,\n\taccountId?: string | null,\n): ResolvedDiscordAccount {\n\tconst normalizedAccountId = normalizeAccountId(accountId);\n\tconst multiConfig = getMultiAccountConfig(runtime);\n\n\tconst baseEnabled = multiConfig.enabled !== false;\n\tconst merged = mergeDiscordAccountConfig(runtime, normalizedAccountId);\n\tconst accountEnabled = merged.enabled !== false;\n\tconst enabled = baseEnabled && accountEnabled;\n\n\tconst tokenResolution = resolveDiscordToken(runtime, {\n\t\taccountId: normalizedAccountId,\n\t});\n\n\treturn {\n\t\taccountId: normalizedAccountId,\n\t\tenabled,\n\t\tname: merged.name?.trim() || undefined,\n\t\ttoken: tokenResolution.token,\n\t\ttokenSource: tokenResolution.source,\n\t\tconfig: merged,\n\t};\n}\n\n/**\n * Lists all enabled Discord accounts\n */\nexport function listEnabledDiscordAccounts(\n\truntime: IAgentRuntime,\n): ResolvedDiscordAccount[] {\n\treturn listDiscordAccountIds(runtime)\n\t\t.map((accountId) => resolveDiscordAccount(runtime, accountId))\n\t\t.filter((account) => account.enabled && account.token);\n}\n\n/**\n * Checks if multi-account mode is enabled\n */\nexport function isMultiAccountEnabled(runtime: IAgentRuntime): boolean {\n\tconst accounts = listEnabledDiscordAccounts(runtime);\n\treturn accounts.length > 1;\n}\n",
|
|
65
|
+
"import type { Guild, GuildMember, User } from \"discord.js\";\nimport type {\n\tDiscordAccountConfig,\n\tDiscordGuildChannelConfig,\n\tDiscordGuildEntry,\n} from \"./accounts\";\n\n/**\n * Normalized allowlist structure for Discord entities\n */\nexport interface DiscordAllowList {\n\tallowAll: boolean;\n\tids: Set<string>;\n\tnames: Set<string>;\n}\n\n/**\n * Match source type for allowlist matching\n */\nexport type AllowListMatchSource = \"wildcard\" | \"id\" | \"name\" | \"tag\";\n\n/**\n * Result of an allowlist match operation\n */\nexport interface DiscordAllowListMatch {\n\tallowed: boolean;\n\tmatchKey?: string;\n\tmatchSource?: AllowListMatchSource;\n}\n\n/**\n * Match source for channel configuration\n */\nexport type ChannelMatchSource = \"id\" | \"name\" | \"slug\" | \"wildcard\" | \"parent\";\n\n/**\n * Resolved channel configuration with match metadata\n */\nexport interface DiscordChannelConfigResolved {\n\tallowed: boolean;\n\trequireMention?: boolean;\n\tskills?: string[];\n\tenabled?: boolean;\n\tusers?: Array<string | number>;\n\tsystemPrompt?: string;\n\tautoThread?: boolean;\n\tmatchKey?: string;\n\tmatchSource?: ChannelMatchSource;\n}\n\n/**\n * Normalizes a Discord slug (channel name, guild name, etc.)\n * Converts to lowercase, removes special characters, replaces spaces with dashes\n */\nexport function normalizeDiscordSlug(value: string): string {\n\treturn value\n\t\t.trim()\n\t\t.toLowerCase()\n\t\t.replace(/^#/, \"\")\n\t\t.replace(/[^a-z0-9]+/g, \"-\")\n\t\t.replace(/^-+|-+$/g, \"\");\n}\n\n/**\n * Formats a Discord user tag for matching\n */\nexport function formatDiscordUserTag(user: User | GuildMember): string {\n\tif (\"user\" in user) {\n\t\treturn user.user.discriminator === \"0\"\n\t\t\t? user.user.username\n\t\t\t: `${user.user.username}#${user.user.discriminator}`;\n\t}\n\treturn user.discriminator === \"0\"\n\t\t? user.username\n\t\t: `${user.username}#${user.discriminator}`;\n}\n\n/**\n * Normalizes a raw allowlist array into a structured DiscordAllowList\n */\nexport function normalizeDiscordAllowList(\n\traw: Array<string | number> | undefined,\n\tprefixes: string[] = [\"discord:\", \"user:\", \"pk:\"],\n): DiscordAllowList | null {\n\tif (!raw || raw.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst ids = new Set<string>();\n\tconst names = new Set<string>();\n\tconst allowAll = raw.some((entry) => String(entry).trim() === \"*\");\n\n\tfor (const entry of raw) {\n\t\tconst text = String(entry).trim();\n\t\tif (!text || text === \"*\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst normalized = normalizeDiscordSlug(text);\n\n\t\t// Check for mention format <@!123456> or <@123456>\n\t\tconst maybeId = text.replace(/^<@!?/, \"\").replace(/>$/, \"\");\n\t\tif (/^\\d+$/.test(maybeId)) {\n\t\t\tids.add(maybeId);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Check for prefixed IDs\n\t\tconst prefix = prefixes.find((p) => text.startsWith(p));\n\t\tif (prefix) {\n\t\t\tconst candidate = text.slice(prefix.length);\n\t\t\tif (candidate) {\n\t\t\t\tids.add(candidate);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Otherwise treat as name/slug\n\t\tif (normalized) {\n\t\t\tnames.add(normalized);\n\t\t}\n\t}\n\n\treturn { allowAll, ids, names };\n}\n\n/**\n * Checks if a candidate matches the allowlist\n */\nexport function allowListMatches(\n\tlist: DiscordAllowList,\n\tcandidate: { id?: string; name?: string; tag?: string },\n): boolean {\n\tif (list.allowAll) {\n\t\treturn true;\n\t}\n\n\tif (candidate.id && list.ids.has(candidate.id)) {\n\t\treturn true;\n\t}\n\n\tconst slug = candidate.name ? normalizeDiscordSlug(candidate.name) : \"\";\n\tif (slug && list.names.has(slug)) {\n\t\treturn true;\n\t}\n\n\tif (candidate.tag && list.names.has(normalizeDiscordSlug(candidate.tag))) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n/**\n * Resolves an allowlist match with detailed match information\n */\nexport function resolveDiscordAllowListMatch(params: {\n\tallowList: DiscordAllowList;\n\tcandidate: { id?: string; name?: string; tag?: string };\n}): DiscordAllowListMatch {\n\tconst { allowList, candidate } = params;\n\n\tif (allowList.allowAll) {\n\t\treturn { allowed: true, matchKey: \"*\", matchSource: \"wildcard\" };\n\t}\n\n\tif (candidate.id && allowList.ids.has(candidate.id)) {\n\t\treturn { allowed: true, matchKey: candidate.id, matchSource: \"id\" };\n\t}\n\n\tconst nameSlug = candidate.name ? normalizeDiscordSlug(candidate.name) : \"\";\n\tif (nameSlug && allowList.names.has(nameSlug)) {\n\t\treturn { allowed: true, matchKey: nameSlug, matchSource: \"name\" };\n\t}\n\n\tconst tagSlug = candidate.tag ? normalizeDiscordSlug(candidate.tag) : \"\";\n\tif (tagSlug && allowList.names.has(tagSlug)) {\n\t\treturn { allowed: true, matchKey: tagSlug, matchSource: \"tag\" };\n\t}\n\n\treturn { allowed: false };\n}\n\n/**\n * Checks if a Discord user is allowed based on an allowlist\n */\nexport function resolveDiscordUserAllowed(params: {\n\tallowList?: Array<string | number>;\n\tuserId: string;\n\tuserName?: string;\n\tuserTag?: string;\n}): boolean {\n\tconst allowList = normalizeDiscordAllowList(params.allowList, [\n\t\t\"discord:\",\n\t\t\"user:\",\n\t\t\"pk:\",\n\t]);\n\tif (!allowList) {\n\t\treturn true;\n\t}\n\n\treturn allowListMatches(allowList, {\n\t\tid: params.userId,\n\t\tname: params.userName,\n\t\ttag: params.userTag,\n\t});\n}\n\n/**\n * Checks if a user is authorized for commands in DMs\n */\nexport function resolveDiscordCommandAuthorized(params: {\n\tisDirectMessage: boolean;\n\tallowFrom?: Array<string | number>;\n\tauthor: User;\n}): boolean {\n\tif (!params.isDirectMessage) {\n\t\treturn true;\n\t}\n\n\tconst allowList = normalizeDiscordAllowList(params.allowFrom, [\n\t\t\"discord:\",\n\t\t\"user:\",\n\t\t\"pk:\",\n\t]);\n\tif (!allowList) {\n\t\treturn true;\n\t}\n\n\treturn allowListMatches(allowList, {\n\t\tid: params.author.id,\n\t\tname: params.author.username,\n\t\ttag: formatDiscordUserTag(params.author),\n\t});\n}\n\n/**\n * Resolves guild entry configuration by ID or slug\n */\nexport function resolveDiscordGuildEntry(params: {\n\tguild?: Guild | null;\n\tguildEntries?: Record<string, DiscordGuildEntry>;\n}): (DiscordGuildEntry & { id?: string }) | null {\n\tconst { guild, guildEntries } = params;\n\tif (!guild || !guildEntries) {\n\t\treturn null;\n\t}\n\n\t// Check by ID first\n\tconst byId = guildEntries[guild.id];\n\tif (byId) {\n\t\treturn { ...byId, id: guild.id };\n\t}\n\n\t// Check by slug\n\tconst slug = normalizeDiscordSlug(guild.name ?? \"\");\n\tconst bySlug = guildEntries[slug];\n\tif (bySlug) {\n\t\treturn { ...bySlug, id: guild.id, slug: slug || bySlug.slug };\n\t}\n\n\t// Check for wildcard\n\tconst wildcard = guildEntries[\"*\"];\n\tif (wildcard) {\n\t\treturn { ...wildcard, id: guild.id, slug: slug || wildcard.slug };\n\t}\n\n\treturn null;\n}\n\n/**\n * Builds channel key candidates for matching\n */\nfunction buildChannelKeyCandidates(\n\tid: string,\n\tslug?: string,\n\tname?: string,\n\tallowNameMatch = true,\n): string[] {\n\tconst keys: string[] = [id];\n\n\tif (allowNameMatch) {\n\t\tif (slug) {\n\t\t\tkeys.push(slug);\n\t\t}\n\t\tif (name) {\n\t\t\tconst nameSlug = normalizeDiscordSlug(name);\n\t\t\tif (nameSlug && nameSlug !== slug) {\n\t\t\t\tkeys.push(nameSlug);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn keys;\n}\n\n/**\n * Resolves channel entry match from configuration\n */\nfunction resolveChannelEntryMatch(\n\tchannels: Record<string, DiscordGuildChannelConfig>,\n\tkeys: string[],\n\tparentKeys?: string[],\n): {\n\tentry: DiscordGuildChannelConfig;\n\tmatchKey: string;\n\tmatchSource: ChannelMatchSource;\n} | null {\n\t// Try direct keys first\n\tfor (const key of keys) {\n\t\tconst entry = channels[key];\n\t\tif (entry) {\n\t\t\tconst source: ChannelMatchSource = /^\\d+$/.test(key) ? \"id\" : \"name\";\n\t\t\treturn { entry, matchKey: key, matchSource: source };\n\t\t}\n\t}\n\n\t// Try parent keys if provided\n\tif (parentKeys) {\n\t\tfor (const parentKey of parentKeys) {\n\t\t\tconst entry = channels[parentKey];\n\t\t\tif (entry) {\n\t\t\t\treturn { entry, matchKey: parentKey, matchSource: \"parent\" };\n\t\t\t}\n\t\t}\n\t}\n\n\t// Try wildcard\n\tconst wildcard = channels[\"*\"];\n\tif (wildcard) {\n\t\treturn { entry: wildcard, matchKey: \"*\", matchSource: \"wildcard\" };\n\t}\n\n\treturn null;\n}\n\n/**\n * Resolves channel configuration from guild info\n */\nexport function resolveDiscordChannelConfig(params: {\n\tguildInfo?: (DiscordGuildEntry & { id?: string }) | null;\n\tchannelId: string;\n\tchannelName?: string;\n\tchannelSlug?: string;\n}): DiscordChannelConfigResolved | null {\n\tconst { guildInfo, channelId, channelName } = params;\n\tconst channelSlug =\n\t\tparams.channelSlug ??\n\t\t(channelName ? normalizeDiscordSlug(channelName) : \"\");\n\tconst channels = guildInfo?.channels;\n\n\tif (!channels) {\n\t\treturn null;\n\t}\n\n\tconst keys = buildChannelKeyCandidates(channelId, channelSlug, channelName);\n\tconst match = resolveChannelEntryMatch(channels, keys);\n\n\tif (!match) {\n\t\treturn { allowed: false };\n\t}\n\n\treturn {\n\t\tallowed: match.entry.allow !== false,\n\t\trequireMention: match.entry.requireMention,\n\t\tskills: match.entry.skills,\n\t\tenabled: match.entry.enabled,\n\t\tusers: match.entry.users,\n\t\tsystemPrompt: match.entry.systemPrompt,\n\t\tautoThread: match.entry.autoThread,\n\t\tmatchKey: match.matchKey,\n\t\tmatchSource: match.matchSource,\n\t};\n}\n\n/**\n * Resolves channel configuration with thread/parent fallback\n */\nexport function resolveDiscordChannelConfigWithFallback(params: {\n\tguildInfo?: (DiscordGuildEntry & { id?: string }) | null;\n\tchannelId: string;\n\tchannelName?: string;\n\tchannelSlug?: string;\n\tparentId?: string;\n\tparentName?: string;\n\tparentSlug?: string;\n\tisThread?: boolean;\n}): DiscordChannelConfigResolved | null {\n\tconst {\n\t\tguildInfo,\n\t\tchannelId,\n\t\tchannelName,\n\t\tparentId,\n\t\tparentName,\n\t\tparentSlug,\n\t\tisThread = false,\n\t} = params;\n\tconst channelSlug =\n\t\tparams.channelSlug ??\n\t\t(channelName ? normalizeDiscordSlug(channelName) : \"\");\n\tconst channels = guildInfo?.channels;\n\n\tif (!channels) {\n\t\treturn null;\n\t}\n\n\tconst resolvedParentSlug =\n\t\tparentSlug ?? (parentName ? normalizeDiscordSlug(parentName) : \"\");\n\n\t// For threads, don't match by name (thread names change)\n\tconst keys = buildChannelKeyCandidates(\n\t\tchannelId,\n\t\tchannelSlug,\n\t\tchannelName,\n\t\t!isThread,\n\t);\n\n\tconst parentKeys =\n\t\tparentId || parentName || parentSlug\n\t\t\t? buildChannelKeyCandidates(\n\t\t\t\t\tparentId ?? \"\",\n\t\t\t\t\tresolvedParentSlug,\n\t\t\t\t\tparentName,\n\t\t\t\t)\n\t\t\t: undefined;\n\n\tconst match = resolveChannelEntryMatch(channels, keys, parentKeys);\n\n\tif (!match) {\n\t\treturn { allowed: false };\n\t}\n\n\treturn {\n\t\tallowed: match.entry.allow !== false,\n\t\trequireMention: match.entry.requireMention,\n\t\tskills: match.entry.skills,\n\t\tenabled: match.entry.enabled,\n\t\tusers: match.entry.users,\n\t\tsystemPrompt: match.entry.systemPrompt,\n\t\tautoThread: match.entry.autoThread,\n\t\tmatchKey: match.matchKey,\n\t\tmatchSource: match.matchSource,\n\t};\n}\n\n/**\n * Determines if a mention is required for the bot to respond\n */\nexport function resolveDiscordShouldRequireMention(params: {\n\tisGuildMessage: boolean;\n\tisThread: boolean;\n\tbotId?: string | null;\n\tthreadOwnerId?: string | null;\n\tchannelConfig?: DiscordChannelConfigResolved | null;\n\tguildInfo?: (DiscordGuildEntry & { id?: string }) | null;\n\tisAutoThreadOwnedByBot?: boolean;\n}): boolean {\n\tif (!params.isGuildMessage) {\n\t\treturn false;\n\t}\n\n\t// Don't require mention in threads created by the bot (autoThread)\n\tconst isBotThread =\n\t\tparams.isAutoThreadOwnedByBot ?? isDiscordAutoThreadOwnedByBot(params);\n\tif (isBotThread) {\n\t\treturn false;\n\t}\n\n\treturn (\n\t\tparams.channelConfig?.requireMention ??\n\t\tparams.guildInfo?.requireMention ??\n\t\ttrue\n\t);\n}\n\n/**\n * Checks if a thread was created by the bot via autoThread feature\n */\nexport function isDiscordAutoThreadOwnedByBot(params: {\n\tisThread: boolean;\n\tchannelConfig?: DiscordChannelConfigResolved | null;\n\tbotId?: string | null;\n\tthreadOwnerId?: string | null;\n}): boolean {\n\tif (!params.isThread) {\n\t\treturn false;\n\t}\n\n\tif (!params.channelConfig?.autoThread) {\n\t\treturn false;\n\t}\n\n\tconst botId = params.botId?.trim();\n\tconst threadOwnerId = params.threadOwnerId?.trim();\n\n\treturn Boolean(botId && threadOwnerId && botId === threadOwnerId);\n}\n\n/**\n * Checks if a group (guild channel) is allowed by the policy\n */\nexport function isDiscordGroupAllowedByPolicy(params: {\n\tgroupPolicy: \"open\" | \"disabled\" | \"allowlist\";\n\tguildAllowlisted: boolean;\n\tchannelAllowlistConfigured: boolean;\n\tchannelAllowed: boolean;\n}): boolean {\n\tconst {\n\t\tgroupPolicy,\n\t\tguildAllowlisted,\n\t\tchannelAllowlistConfigured,\n\t\tchannelAllowed,\n\t} = params;\n\n\tif (groupPolicy === \"disabled\") {\n\t\treturn false;\n\t}\n\n\tif (groupPolicy === \"open\") {\n\t\treturn true;\n\t}\n\n\t// allowlist mode\n\tif (!guildAllowlisted) {\n\t\treturn false;\n\t}\n\n\tif (!channelAllowlistConfigured) {\n\t\treturn true;\n\t}\n\n\treturn channelAllowed;\n}\n\n/**\n * Resolves if a group DM is allowed\n */\nexport function resolveGroupDmAllow(params: {\n\tchannels?: Array<string | number>;\n\tchannelId: string;\n\tchannelName?: string;\n\tchannelSlug?: string;\n}): boolean {\n\tconst { channels, channelId, channelName } = params;\n\tconst channelSlug =\n\t\tparams.channelSlug ??\n\t\t(channelName ? normalizeDiscordSlug(channelName) : \"\");\n\n\tif (!channels || channels.length === 0) {\n\t\treturn true;\n\t}\n\n\tconst allowList = new Set(\n\t\tchannels.map((entry) => normalizeDiscordSlug(String(entry))),\n\t);\n\tconst candidates = [\n\t\tnormalizeDiscordSlug(channelId),\n\t\tchannelSlug,\n\t\tchannelName ? normalizeDiscordSlug(channelName) : \"\",\n\t].filter(Boolean);\n\n\treturn (\n\t\tallowList.has(\"*\") ||\n\t\tcandidates.some((candidate) => allowList.has(candidate))\n\t);\n}\n\n/**\n * Determines if a reaction notification should be emitted\n */\nexport function shouldEmitDiscordReactionNotification(params: {\n\tmode?: \"off\" | \"own\" | \"all\" | \"allowlist\";\n\tbotId?: string;\n\tmessageAuthorId?: string;\n\tuserId: string;\n\tuserName?: string;\n\tuserTag?: string;\n\tallowlist?: Array<string | number>;\n}): boolean {\n\tconst mode = params.mode ?? \"own\";\n\n\tif (mode === \"off\") {\n\t\treturn false;\n\t}\n\n\tif (mode === \"all\") {\n\t\treturn true;\n\t}\n\n\tif (mode === \"own\") {\n\t\treturn Boolean(params.botId && params.messageAuthorId === params.botId);\n\t}\n\n\tif (mode === \"allowlist\") {\n\t\tconst list = normalizeDiscordAllowList(params.allowlist, [\n\t\t\t\"discord:\",\n\t\t\t\"user:\",\n\t\t\t\"pk:\",\n\t\t]);\n\t\tif (!list) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn allowListMatches(list, {\n\t\t\tid: params.userId,\n\t\t\tname: params.userName,\n\t\t\ttag: params.userTag,\n\t\t});\n\t}\n\n\treturn false;\n}\n\n/**\n * Validates a message against all allowlist policies\n */\nexport function validateMessageAllowed(params: {\n\taccountConfig: DiscordAccountConfig;\n\tisDirectMessage: boolean;\n\tisGroupDm: boolean;\n\tguild?: Guild | null;\n\tchannelId: string;\n\tchannelName?: string;\n\tauthor: User;\n\tbotId?: string;\n}): {\n\tallowed: boolean;\n\treason?: string;\n\tchannelConfig?: DiscordChannelConfigResolved | null;\n\tguildInfo?: (DiscordGuildEntry & { id?: string }) | null;\n} {\n\tconst {\n\t\taccountConfig,\n\t\tisDirectMessage,\n\t\tisGroupDm,\n\t\tguild,\n\t\tchannelId,\n\t\tchannelName,\n\t\tauthor,\n\t} = params;\n\n\t// Handle DMs\n\tif (isDirectMessage && !isGroupDm) {\n\t\tconst dmConfig = accountConfig.dm;\n\n\t\tif (dmConfig?.enabled === false) {\n\t\t\treturn { allowed: false, reason: \"DMs disabled\" };\n\t\t}\n\n\t\tconst dmPolicy = dmConfig?.policy ?? \"open\";\n\t\tif (dmPolicy === \"disabled\") {\n\t\t\treturn { allowed: false, reason: \"DM policy disabled\" };\n\t\t}\n\n\t\tif (dmPolicy === \"allowlist\" && dmConfig?.allowFrom) {\n\t\t\tconst isAllowed = resolveDiscordUserAllowed({\n\t\t\t\tallowList: dmConfig.allowFrom,\n\t\t\t\tuserId: author.id,\n\t\t\t\tuserName: author.username,\n\t\t\t\tuserTag: formatDiscordUserTag(author),\n\t\t\t});\n\n\t\t\tif (!isAllowed) {\n\t\t\t\treturn { allowed: false, reason: \"User not in DM allowlist\" };\n\t\t\t}\n\t\t}\n\n\t\treturn { allowed: true };\n\t}\n\n\t// Handle group DMs\n\tif (isGroupDm) {\n\t\tconst dmConfig = accountConfig.dm;\n\n\t\tif (!dmConfig?.groupEnabled) {\n\t\t\treturn { allowed: false, reason: \"Group DMs disabled\" };\n\t\t}\n\n\t\tconst isAllowed = resolveGroupDmAllow({\n\t\t\tchannels: dmConfig.groupChannels,\n\t\t\tchannelId,\n\t\t\tchannelName,\n\t\t});\n\n\t\tif (!isAllowed) {\n\t\t\treturn { allowed: false, reason: \"Group DM channel not allowed\" };\n\t\t}\n\n\t\treturn { allowed: true };\n\t}\n\n\t// Handle guild messages\n\tconst groupPolicy = accountConfig.groupPolicy ?? \"open\";\n\tconst guildInfo = resolveDiscordGuildEntry({\n\t\tguild,\n\t\tguildEntries: accountConfig.guilds,\n\t});\n\n\tconst guildAllowlisted = guildInfo !== null;\n\tconst channelSlug = channelName ? normalizeDiscordSlug(channelName) : \"\";\n\n\tconst channelConfig = resolveDiscordChannelConfig({\n\t\tguildInfo,\n\t\tchannelId,\n\t\tchannelName,\n\t\tchannelSlug,\n\t});\n\n\tconst channelAllowlistConfigured = Boolean(guildInfo?.channels);\n\tconst channelAllowed = channelConfig?.allowed ?? false;\n\n\tconst isAllowed = isDiscordGroupAllowedByPolicy({\n\t\tgroupPolicy,\n\t\tguildAllowlisted,\n\t\tchannelAllowlistConfigured,\n\t\tchannelAllowed,\n\t});\n\n\tif (!isAllowed) {\n\t\treturn {\n\t\t\tallowed: false,\n\t\t\treason: \"Channel not allowed by policy\",\n\t\t\tchannelConfig,\n\t\t\tguildInfo,\n\t\t};\n\t}\n\n\t// Check user allowlist for channel\n\tif (channelConfig?.users) {\n\t\tconst userAllowed = resolveDiscordUserAllowed({\n\t\t\tallowList: channelConfig.users,\n\t\t\tuserId: author.id,\n\t\t\tuserName: author.username,\n\t\t\tuserTag: formatDiscordUserTag(author),\n\t\t});\n\n\t\tif (!userAllowed) {\n\t\t\treturn {\n\t\t\t\tallowed: false,\n\t\t\t\treason: \"User not in channel allowlist\",\n\t\t\t\tchannelConfig,\n\t\t\t\tguildInfo,\n\t\t\t};\n\t\t}\n\t}\n\n\t// Check user allowlist for guild\n\tif (guildInfo?.users) {\n\t\tconst userAllowed = resolveDiscordUserAllowed({\n\t\t\tallowList: guildInfo.users,\n\t\t\tuserId: author.id,\n\t\t\tuserName: author.username,\n\t\t\tuserTag: formatDiscordUserTag(author),\n\t\t});\n\n\t\tif (!userAllowed) {\n\t\t\treturn {\n\t\t\t\tallowed: false,\n\t\t\t\treason: \"User not in guild allowlist\",\n\t\t\t\tchannelConfig,\n\t\t\t\tguildInfo,\n\t\t\t};\n\t\t}\n\t}\n\n\treturn { allowed: true, channelConfig, guildInfo };\n}\n",
|
|
66
|
+
"import type { Guild, MessageReaction } from \"discord.js\";\n\n/**\n * Options for chunking Discord text\n */\nexport interface ChunkDiscordTextOpts {\n\t/** Max characters per Discord message. Default: 2000. */\n\tmaxChars?: number;\n\t/**\n\t * Soft max line count per message. Default: 17.\n\t * Discord clients can clip/collapse very tall messages in the UI.\n\t */\n\tmaxLines?: number;\n\t/** Chunking mode: \"length\" (default) or \"newline\" */\n\tchunkMode?: \"length\" | \"newline\";\n}\n\ninterface OpenFence {\n\tindent: string;\n\tmarkerChar: string;\n\tmarkerLen: number;\n\topenLine: string;\n}\n\nconst DEFAULT_MAX_CHARS = 2000;\nconst DEFAULT_MAX_LINES = 17;\nconst FENCE_RE = /^( {0,3})(`{3,}|~{3,})(.*)$/;\n\nfunction countLines(text: string): number {\n\tif (!text) {\n\t\treturn 0;\n\t}\n\treturn text.split(\"\\n\").length;\n}\n\nfunction parseFenceLine(line: string): OpenFence | null {\n\tconst match = line.match(FENCE_RE);\n\tif (!match) {\n\t\treturn null;\n\t}\n\tconst indent = match[1] ?? \"\";\n\tconst marker = match[2] ?? \"\";\n\treturn {\n\t\tindent,\n\t\tmarkerChar: marker[0] ?? \"`\",\n\t\tmarkerLen: marker.length,\n\t\topenLine: line,\n\t};\n}\n\nfunction closeFenceLine(openFence: OpenFence): string {\n\treturn `${openFence.indent}${openFence.markerChar.repeat(openFence.markerLen)}`;\n}\n\nfunction closeFenceIfNeeded(text: string, openFence: OpenFence | null): string {\n\tif (!openFence) {\n\t\treturn text;\n\t}\n\tconst closeLine = closeFenceLine(openFence);\n\tif (!text) {\n\t\treturn closeLine;\n\t}\n\tif (!text.endsWith(\"\\n\")) {\n\t\treturn `${text}\\n${closeLine}`;\n\t}\n\treturn `${text}${closeLine}`;\n}\n\nfunction splitLongLine(\n\tline: string,\n\tmaxChars: number,\n\topts: { preserveWhitespace: boolean },\n): string[] {\n\tconst limit = Math.max(1, Math.floor(maxChars));\n\tif (line.length <= limit) {\n\t\treturn [line];\n\t}\n\n\tconst out: string[] = [];\n\tlet remaining = line;\n\n\twhile (remaining.length > limit) {\n\t\tif (opts.preserveWhitespace) {\n\t\t\tout.push(remaining.slice(0, limit));\n\t\t\tremaining = remaining.slice(limit);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst window = remaining.slice(0, limit);\n\t\tlet breakIdx = -1;\n\t\tfor (let i = window.length - 1; i >= 0; i--) {\n\t\t\tif (/\\s/.test(window[i])) {\n\t\t\t\tbreakIdx = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (breakIdx <= 0) {\n\t\t\tbreakIdx = limit;\n\t\t}\n\n\t\tout.push(remaining.slice(0, breakIdx));\n\t\tremaining = remaining.slice(breakIdx);\n\t}\n\n\tif (remaining.length) {\n\t\tout.push(remaining);\n\t}\n\n\treturn out;\n}\n\nfunction isReasoningItalicsPayload(source: string): boolean {\n\treturn source.startsWith(\"Reasoning:\\n_\") && source.trimEnd().endsWith(\"_\");\n}\n\n/**\n * Keep italics intact for reasoning payloads wrapped with `_…_`.\n * When Discord chunking splits the message, we close italics at the end of\n * each chunk and reopen at the start of the next so every chunk renders\n * consistently.\n */\nfunction rebalanceReasoningItalics(source: string, chunks: string[]): string[] {\n\tif (chunks.length <= 1) {\n\t\treturn chunks;\n\t}\n\n\tif (!isReasoningItalicsPayload(source)) {\n\t\treturn chunks;\n\t}\n\n\tconst adjusted = [...chunks];\n\tfor (let i = 0; i < adjusted.length; i++) {\n\t\tconst isLast = i === adjusted.length - 1;\n\t\tconst current = adjusted[i];\n\n\t\t// Ensure current chunk closes italics so Discord renders it italicized\n\t\tconst needsClosing = !current.trimEnd().endsWith(\"_\");\n\t\tif (needsClosing) {\n\t\t\tadjusted[i] = `${current}_`;\n\t\t}\n\n\t\tif (isLast) {\n\t\t\tbreak;\n\t\t}\n\n\t\t// Re-open italics on the next chunk if needed\n\t\tconst next = adjusted[i + 1];\n\t\tconst leadingWhitespaceLen = next.length - next.trimStart().length;\n\t\tconst leadingWhitespace = next.slice(0, leadingWhitespaceLen);\n\t\tconst nextBody = next.slice(leadingWhitespaceLen);\n\t\tif (!nextBody.startsWith(\"_\")) {\n\t\t\tadjusted[i + 1] = `${leadingWhitespace}_${nextBody}`;\n\t\t}\n\t}\n\n\treturn adjusted;\n}\n\n/**\n * Chunks outbound Discord text by both character count and (soft) line count,\n * while keeping fenced code blocks balanced across chunks.\n */\nexport function chunkDiscordText(\n\ttext: string,\n\topts: ChunkDiscordTextOpts = {},\n): string[] {\n\tconst requestedMaxChars = Math.max(\n\t\t1,\n\t\tMath.floor(opts.maxChars ?? DEFAULT_MAX_CHARS),\n\t);\n\tconst maxLines = Math.max(1, Math.floor(opts.maxLines ?? DEFAULT_MAX_LINES));\n\n\tconst body = text ?? \"\";\n\tif (!body) {\n\t\treturn [];\n\t}\n\n\tconst maxChars = isReasoningItalicsPayload(body)\n\t\t? Math.max(1, requestedMaxChars - 2)\n\t\t: requestedMaxChars;\n\n\tconst alreadyOk = body.length <= maxChars && countLines(body) <= maxLines;\n\tif (alreadyOk) {\n\t\treturn [body];\n\t}\n\n\tconst lines = body.split(\"\\n\");\n\tconst chunks: string[] = [];\n\n\tlet current = \"\";\n\tlet currentLines = 0;\n\tlet openFence: OpenFence | null = null;\n\n\tconst flush = () => {\n\t\tif (!current) {\n\t\t\treturn;\n\t\t}\n\t\tconst payload = closeFenceIfNeeded(current, openFence);\n\t\tif (payload.trim().length) {\n\t\t\tchunks.push(payload);\n\t\t}\n\t\tcurrent = \"\";\n\t\tcurrentLines = 0;\n\t\tif (openFence) {\n\t\t\tcurrent = openFence.openLine;\n\t\t\tcurrentLines = 1;\n\t\t}\n\t};\n\n\tfor (const originalLine of lines) {\n\t\tconst fenceInfo = parseFenceLine(originalLine);\n\t\tconst wasInsideFence = openFence !== null;\n\t\tlet nextOpenFence: OpenFence | null = openFence;\n\n\t\tif (fenceInfo) {\n\t\t\tif (!openFence) {\n\t\t\t\tnextOpenFence = fenceInfo;\n\t\t\t} else if (\n\t\t\t\topenFence.markerChar === fenceInfo.markerChar &&\n\t\t\t\tfenceInfo.markerLen >= openFence.markerLen\n\t\t\t) {\n\t\t\t\tnextOpenFence = null;\n\t\t\t}\n\t\t}\n\n\t\tconst reserveChars = nextOpenFence\n\t\t\t? closeFenceLine(nextOpenFence).length + 1\n\t\t\t: 0;\n\t\tconst reserveLines = nextOpenFence ? 1 : 0;\n\t\tconst effectiveMaxChars = maxChars - reserveChars;\n\t\tconst effectiveMaxLines = maxLines - reserveLines;\n\t\tconst charLimit = effectiveMaxChars > 0 ? effectiveMaxChars : maxChars;\n\t\tconst lineLimit = effectiveMaxLines > 0 ? effectiveMaxLines : maxLines;\n\t\tconst prefixLen = current.length > 0 ? current.length + 1 : 0;\n\t\tconst segmentLimit = Math.max(1, charLimit - prefixLen);\n\t\tconst segments = splitLongLine(originalLine, segmentLimit, {\n\t\t\tpreserveWhitespace: wasInsideFence,\n\t\t});\n\n\t\tfor (let segIndex = 0; segIndex < segments.length; segIndex++) {\n\t\t\tconst segment = segments[segIndex];\n\t\t\tconst isLineContinuation = segIndex > 0;\n\t\t\tconst delimiter = isLineContinuation\n\t\t\t\t? \"\"\n\t\t\t\t: current.length > 0\n\t\t\t\t\t? \"\\n\"\n\t\t\t\t\t: \"\";\n\t\t\tconst addition = `${delimiter}${segment}`;\n\t\t\tconst nextLen = current.length + addition.length;\n\t\t\tconst nextLines = currentLines + (isLineContinuation ? 0 : 1);\n\n\t\t\tconst wouldExceedChars = nextLen > charLimit;\n\t\t\tconst wouldExceedLines = nextLines > lineLimit;\n\n\t\t\tif ((wouldExceedChars || wouldExceedLines) && current.length > 0) {\n\t\t\t\tflush();\n\t\t\t}\n\n\t\t\tif (current.length > 0) {\n\t\t\t\tcurrent += addition;\n\t\t\t\tif (!isLineContinuation) {\n\t\t\t\t\tcurrentLines += 1;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcurrent = segment;\n\t\t\t\tcurrentLines = 1;\n\t\t\t}\n\t\t}\n\n\t\topenFence = nextOpenFence;\n\t}\n\n\tif (current.length) {\n\t\tconst payload = closeFenceIfNeeded(current, openFence);\n\t\tif (payload.trim().length) {\n\t\t\tchunks.push(payload);\n\t\t}\n\t}\n\n\treturn rebalanceReasoningItalics(text, chunks);\n}\n\n/**\n * Chunks text by newlines first, then by character/line limits\n */\nfunction chunkMarkdownTextByNewline(text: string, maxChars: number): string[] {\n\tconst lines = text.split(\"\\n\");\n\tconst chunks: string[] = [];\n\tlet current = \"\";\n\n\tfor (const line of lines) {\n\t\tif (current.length + line.length + 1 > maxChars && current.length > 0) {\n\t\t\tchunks.push(current);\n\t\t\tcurrent = line;\n\t\t} else {\n\t\t\tcurrent = current.length > 0 ? `${current}\\n${line}` : line;\n\t\t}\n\t}\n\n\tif (current.length > 0) {\n\t\tchunks.push(current);\n\t}\n\n\treturn chunks;\n}\n\n/**\n * Chunks Discord text with configurable chunking mode\n */\nexport function chunkDiscordTextWithMode(\n\ttext: string,\n\topts: ChunkDiscordTextOpts = {},\n): string[] {\n\tconst chunkMode = opts.chunkMode ?? \"length\";\n\n\tif (chunkMode !== \"newline\") {\n\t\treturn chunkDiscordText(text, opts);\n\t}\n\n\tconst lineChunks = chunkMarkdownTextByNewline(\n\t\ttext,\n\t\tMath.max(1, Math.floor(opts.maxChars ?? DEFAULT_MAX_CHARS)),\n\t);\n\n\tconst chunks: string[] = [];\n\tfor (const line of lineChunks) {\n\t\tconst nested = chunkDiscordText(line, opts);\n\t\tif (!nested.length && line) {\n\t\t\tchunks.push(line);\n\t\t\tcontinue;\n\t\t}\n\t\tchunks.push(...nested);\n\t}\n\n\treturn chunks;\n}\n\n/**\n * Resolves the system location string for logging/display\n */\nexport function resolveDiscordSystemLocation(params: {\n\tisDirectMessage: boolean;\n\tisGroupDm: boolean;\n\tguild?: Guild | null;\n\tchannelName: string;\n}): string {\n\tconst { isDirectMessage, isGroupDm, guild, channelName } = params;\n\n\tif (isDirectMessage) {\n\t\treturn \"DM\";\n\t}\n\n\tif (isGroupDm) {\n\t\treturn `Group DM #${channelName}`;\n\t}\n\n\treturn guild?.name ? `${guild.name} #${channelName}` : `#${channelName}`;\n}\n\n/**\n * Formats a Discord reaction emoji for display\n */\nexport function formatDiscordReactionEmoji(emoji: {\n\tid?: string | null;\n\tname?: string | null;\n}): string {\n\tif (emoji.id && emoji.name) {\n\t\treturn `${emoji.name}:${emoji.id}`;\n\t}\n\treturn emoji.name ?? \"emoji\";\n}\n\n/**\n * Formats a Discord reaction emoji from a MessageReaction\n */\nexport function formatMessageReactionEmoji(reaction: MessageReaction): string {\n\tconst emoji = reaction.emoji;\n\tif (emoji.id && emoji.name) {\n\t\treturn `<:${emoji.name}:${emoji.id}>`;\n\t}\n\treturn emoji.name ?? \"emoji\";\n}\n\n/**\n * Formats a Discord user mention\n */\nexport function formatDiscordUserMention(userId: string): string {\n\treturn `<@${userId}>`;\n}\n\n/**\n * Formats a Discord channel mention\n */\nexport function formatDiscordChannelMention(channelId: string): string {\n\treturn `<#${channelId}>`;\n}\n\n/**\n * Formats a Discord role mention\n */\nexport function formatDiscordRoleMention(roleId: string): string {\n\treturn `<@&${roleId}>`;\n}\n\n/**\n * Extracts user ID from a mention string\n */\nexport function extractUserIdFromMention(mention: string): string | null {\n\tconst match = mention.match(/^<@!?(\\d+)>$/);\n\treturn match ? match[1] : null;\n}\n\n/**\n * Extracts channel ID from a mention string\n */\nexport function extractChannelIdFromMention(mention: string): string | null {\n\tconst match = mention.match(/^<#(\\d+)>$/);\n\treturn match ? match[1] : null;\n}\n\n/**\n * Extracts role ID from a mention string\n */\nexport function extractRoleIdFromMention(mention: string): string | null {\n\tconst match = mention.match(/^<@&(\\d+)>$/);\n\treturn match ? match[1] : null;\n}\n\n/**\n * Resolves a timestamp string to milliseconds\n */\nexport function resolveTimestampMs(\n\ttimestamp?: string | null,\n): number | undefined {\n\tif (!timestamp) {\n\t\treturn undefined;\n\t}\n\tconst parsed = Date.parse(timestamp);\n\treturn Number.isNaN(parsed) ? undefined : parsed;\n}\n\n/**\n * Formats a timestamp for Discord (Discord timestamp format)\n */\nexport function formatDiscordTimestamp(\n\ttimestamp: Date | number,\n\tformat: \"t\" | \"T\" | \"d\" | \"D\" | \"f\" | \"F\" | \"R\" = \"f\",\n): string {\n\tconst unix = Math.floor(\n\t\t(typeof timestamp === \"number\" ? timestamp : timestamp.getTime()) / 1000,\n\t);\n\treturn `<t:${unix}:${format}>`;\n}\n\n/**\n * Strips Discord formatting from text\n */\nexport function stripDiscordFormatting(text: string): string {\n\treturn text\n\t\t.replace(/\\*\\*(.+?)\\*\\*/g, \"$1\") // Bold\n\t\t.replace(/\\*(.+?)\\*/g, \"$1\") // Italic\n\t\t.replace(/__(.+?)__/g, \"$1\") // Underline\n\t\t.replace(/~~(.+?)~~/g, \"$1\") // Strikethrough\n\t\t.replace(/`{3}[\\s\\S]*?`{3}/g, \"\") // Code blocks\n\t\t.replace(/`(.+?)`/g, \"$1\") // Inline code\n\t\t.replace(/\\|\\|(.+?)\\|\\|/g, \"$1\") // Spoilers\n\t\t.replace(/<@!?\\d+>/g, \"\") // User mentions\n\t\t.replace(/<#\\d+>/g, \"\") // Channel mentions\n\t\t.replace(/<@&\\d+>/g, \"\") // Role mentions\n\t\t.replace(/<a?:\\w+:\\d+>/g, \"\") // Custom emojis\n\t\t.trim();\n}\n\n/**\n * Escapes special Discord markdown characters\n */\nexport function escapeDiscordMarkdown(text: string): string {\n\treturn text.replace(/([*_`~|\\\\])/g, \"\\\\$1\");\n}\n\n/**\n * Truncates text to a maximum length with an ellipsis\n */\nexport function truncateText(\n\ttext: string,\n\tmaxLength: number,\n\tellipsis = \"…\",\n): string {\n\tif (text.length <= maxLength) {\n\t\treturn text;\n\t}\n\treturn text.slice(0, maxLength - ellipsis.length) + ellipsis;\n}\n\n/**\n * Truncates text at a UTF-16 boundary safely\n */\nexport function truncateUtf16Safe(\n\ttext: string,\n\tmaxLength: number,\n\tellipsis = \"…\",\n): string {\n\tif (text.length <= maxLength) {\n\t\treturn text;\n\t}\n\n\tconst targetLength = maxLength - ellipsis.length;\n\tif (targetLength <= 0) {\n\t\treturn ellipsis.slice(0, maxLength);\n\t}\n\n\t// Check if we're in the middle of a surrogate pair\n\tlet truncateAt = targetLength;\n\tconst charAtTruncate = text.charCodeAt(truncateAt);\n\n\t// If we're at a low surrogate, back up one\n\tif (charAtTruncate >= 0xdc00 && charAtTruncate <= 0xdfff) {\n\t\ttruncateAt--;\n\t}\n\n\treturn text.slice(0, truncateAt) + ellipsis;\n}\n\n/**\n * Checks if a message mentions a specific user\n */\nexport function messageContainsMention(text: string, userId: string): boolean {\n\tconst mentionPattern = new RegExp(`<@!?${userId}>`);\n\treturn mentionPattern.test(text);\n}\n\n/**\n * Extracts all user mentions from a message\n */\nexport function extractAllUserMentions(text: string): string[] {\n\tconst matches = text.matchAll(/<@!?(\\d+)>/g);\n\treturn Array.from(matches, (m) => m[1]);\n}\n\n/**\n * Extracts all channel mentions from a message\n */\nexport function extractAllChannelMentions(text: string): string[] {\n\tconst matches = text.matchAll(/<#(\\d+)>/g);\n\treturn Array.from(matches, (m) => m[1]);\n}\n\n/**\n * Extracts all role mentions from a message\n */\nexport function extractAllRoleMentions(text: string): string[] {\n\tconst matches = text.matchAll(/<@&(\\d+)>/g);\n\treturn Array.from(matches, (m) => m[1]);\n}\n\n/**\n * Sanitizes a thread name for Discord (max 100 chars, no invalid chars)\n */\nexport function sanitizeThreadName(name: string): string {\n\tconst sanitized = name\n\t\t.replace(/[\\n\\r]/g, \" \")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n\n\treturn truncateUtf16Safe(sanitized, 100, \"\");\n}\n\n/**\n * Builds a message link URL\n */\nexport function buildMessageLink(\n\tguildId: string,\n\tchannelId: string,\n\tmessageId: string,\n): string {\n\treturn `https://discord.com/channels/${guildId}/${channelId}/${messageId}`;\n}\n\n/**\n * Builds a channel link URL\n */\nexport function buildChannelLink(guildId: string, channelId: string): string {\n\treturn `https://discord.com/channels/${guildId}/${channelId}`;\n}\n\n/**\n * Parses a Discord message link URL\n */\nexport function parseMessageLink(\n\turl: string,\n): { guildId: string; channelId: string; messageId: string } | null {\n\tconst match = url.match(\n\t\t/^https?:\\/\\/(?:www\\.)?discord\\.com\\/channels\\/(\\d+)\\/(\\d+)\\/(\\d+)$/,\n\t);\n\n\tif (!match) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tguildId: match[1],\n\t\tchannelId: match[2],\n\t\tmessageId: match[3],\n\t};\n}\n",
|
|
67
|
+
"import {\n\ttype APIApplicationCommandOption,\n\tApplicationCommandOptionType,\n\tButtonStyle,\n\ttype RESTPostAPIChatInputApplicationCommandsJSONBody,\n} from \"discord-api-types/v10\";\n\n/**\n * Command argument definition\n */\nexport interface CommandArgDefinition {\n\tname: string;\n\tdescription: string;\n\ttype: \"string\" | \"number\" | \"boolean\";\n\trequired?: boolean;\n\tchoices?: Array<{ label: string; value: string }>;\n}\n\n/**\n * Native command specification\n */\nexport interface NativeCommandSpec {\n\tname: string;\n\tdescription: string;\n\tacceptsArgs?: boolean;\n\targs?: CommandArgDefinition[];\n\tephemeralDefault?: boolean;\n}\n\n/**\n * Command argument values\n */\nexport type CommandArgValues = Record<string, string | number | boolean>;\n\n/**\n * Parsed command arguments\n */\nexport interface CommandArgs {\n\tvalues: CommandArgValues;\n\traw?: string;\n}\n\n/**\n * Result of building command options\n */\nexport interface BuiltCommandOption {\n\tname: string;\n\tdescription: string;\n\ttype: ApplicationCommandOptionType;\n\trequired?: boolean;\n\tchoices?: Array<{ name: string; value: string | number }>;\n}\n\n/**\n * Button specification for argument menus\n */\nexport interface CommandArgButton {\n\tlabel: string;\n\tcustomId: string;\n\tstyle: ButtonStyle;\n}\n\n/**\n * Row of buttons for argument menus\n */\nexport interface CommandArgButtonRow {\n\tbuttons: CommandArgButton[];\n}\n\n/**\n * Argument menu specification\n */\nexport interface CommandArgMenu {\n\tcontent: string;\n\trows: CommandArgButtonRow[];\n}\n\n/**\n * Key for command argument custom IDs\n */\nexport const COMMAND_ARG_CUSTOM_ID_KEY = \"cmdarg\";\n\n/**\n * Builds Discord command options from argument definitions\n */\nexport function buildDiscordCommandOptions(\n\targs?: CommandArgDefinition[],\n): BuiltCommandOption[] | undefined {\n\tif (!args || args.length === 0) {\n\t\treturn undefined;\n\t}\n\n\treturn args.map((arg) => {\n\t\tconst required = arg.required ?? false;\n\n\t\tif (arg.type === \"number\") {\n\t\t\treturn {\n\t\t\t\tname: arg.name,\n\t\t\t\tdescription: arg.description,\n\t\t\t\ttype: ApplicationCommandOptionType.Number,\n\t\t\t\trequired,\n\t\t\t};\n\t\t}\n\n\t\tif (arg.type === \"boolean\") {\n\t\t\treturn {\n\t\t\t\tname: arg.name,\n\t\t\t\tdescription: arg.description,\n\t\t\t\ttype: ApplicationCommandOptionType.Boolean,\n\t\t\t\trequired,\n\t\t\t};\n\t\t}\n\n\t\tconst choices =\n\t\t\targ.choices && arg.choices.length > 0 && arg.choices.length <= 25\n\t\t\t\t? arg.choices.map((choice) => ({\n\t\t\t\t\t\tname: choice.label,\n\t\t\t\t\t\tvalue: choice.value,\n\t\t\t\t\t}))\n\t\t\t\t: undefined;\n\n\t\treturn {\n\t\t\tname: arg.name,\n\t\t\tdescription: arg.description,\n\t\t\ttype: ApplicationCommandOptionType.String,\n\t\t\trequired,\n\t\t\tchoices,\n\t\t};\n\t});\n}\n\n/**\n * Builds a complete Discord slash command JSON body\n */\nexport function buildDiscordSlashCommand(\n\tspec: NativeCommandSpec,\n): RESTPostAPIChatInputApplicationCommandsJSONBody {\n\tconst options = buildDiscordCommandOptions(spec.args);\n\n\tconst commandOptions: APIApplicationCommandOption[] | undefined =\n\t\toptions?.map(\n\t\t\t(opt) =>\n\t\t\t\t({\n\t\t\t\t\tname: opt.name,\n\t\t\t\t\tdescription: opt.description,\n\t\t\t\t\ttype: opt.type,\n\t\t\t\t\trequired: opt.required,\n\t\t\t\t\tchoices: opt.choices,\n\t\t\t\t}) as APIApplicationCommandOption,\n\t\t) ??\n\t\t(spec.acceptsArgs\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"input\",\n\t\t\t\t\t\tdescription: \"Command input\",\n\t\t\t\t\t\ttype: ApplicationCommandOptionType.String,\n\t\t\t\t\t\trequired: false,\n\t\t\t\t\t} as APIApplicationCommandOption,\n\t\t\t\t]\n\t\t\t: undefined);\n\n\treturn {\n\t\tname: spec.name,\n\t\tdescription: spec.description,\n\t\toptions: commandOptions,\n\t};\n}\n\n/**\n * Encodes a command argument value for use in custom IDs\n */\nexport function encodeCommandArgValue(value: string): string {\n\treturn encodeURIComponent(value);\n}\n\n/**\n * Decodes a command argument value from a custom ID\n */\nexport function decodeCommandArgValue(value: string): string {\n\ttry {\n\t\treturn decodeURIComponent(value);\n\t} catch {\n\t\treturn value;\n\t}\n}\n\n/**\n * Builds a custom ID for a command argument button\n */\nexport function buildCommandArgCustomId(params: {\n\tcommand: string;\n\targ: string;\n\tvalue: string;\n\tuserId: string;\n}): string {\n\treturn [\n\t\t`${COMMAND_ARG_CUSTOM_ID_KEY}:command=${encodeCommandArgValue(params.command)}`,\n\t\t`arg=${encodeCommandArgValue(params.arg)}`,\n\t\t`value=${encodeCommandArgValue(params.value)}`,\n\t\t`user=${encodeCommandArgValue(params.userId)}`,\n\t].join(\";\");\n}\n\n/**\n * Parses command argument data from a custom ID\n */\nexport function parseCommandArgCustomId(\n\tcustomId: string,\n): { command: string; arg: string; value: string; userId: string } | null {\n\tif (!customId.startsWith(COMMAND_ARG_CUSTOM_ID_KEY)) {\n\t\treturn null;\n\t}\n\n\tconst parts = customId.split(\";\");\n\tconst data: Record<string, string> = {};\n\n\tfor (const part of parts) {\n\t\tconst [key, value] = part.split(\"=\");\n\t\tif (key && value) {\n\t\t\t// Handle the first part which has the prefix\n\t\t\tconst cleanKey = key.replace(`${COMMAND_ARG_CUSTOM_ID_KEY}:`, \"\");\n\t\t\tdata[cleanKey] = decodeCommandArgValue(value);\n\t\t}\n\t}\n\n\tif (!data.command || !data.arg || !data.value || !data.user) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tcommand: data.command,\n\t\targ: data.arg,\n\t\tvalue: data.value,\n\t\tuserId: data.user,\n\t};\n}\n\n/**\n * Chunks an array into smaller arrays of a specified size\n */\nfunction chunkArray<T>(items: T[], size: number): T[][] {\n\tif (size <= 0) {\n\t\treturn [items];\n\t}\n\tconst rows: T[][] = [];\n\tfor (let i = 0; i < items.length; i += size) {\n\t\trows.push(items.slice(i, i + size));\n\t}\n\treturn rows;\n}\n\n/**\n * Builds an argument menu with buttons for command selection\n */\nexport function buildCommandArgMenu(params: {\n\tcommandName: string;\n\targ: CommandArgDefinition;\n\tchoices: Array<{ value: string; label: string }>;\n\tuserId: string;\n\ttitle?: string;\n\tbuttonsPerRow?: number;\n}): CommandArgMenu {\n\tconst {\n\t\tcommandName,\n\t\targ,\n\t\tchoices,\n\t\tuserId,\n\t\ttitle,\n\t\tbuttonsPerRow = 4,\n\t} = params;\n\n\tconst rows = chunkArray(choices.slice(0, 20), buttonsPerRow).map(\n\t\t(rowChoices) => ({\n\t\t\tbuttons: rowChoices.map((choice) => ({\n\t\t\t\tlabel: choice.label.slice(0, 80),\n\t\t\t\tcustomId: buildCommandArgCustomId({\n\t\t\t\t\tcommand: commandName,\n\t\t\t\t\targ: arg.name,\n\t\t\t\t\tvalue: choice.value,\n\t\t\t\t\tuserId,\n\t\t\t\t}),\n\t\t\t\tstyle: ButtonStyle.Secondary,\n\t\t\t})),\n\t\t}),\n\t);\n\n\tconst content =\n\t\ttitle ?? `Choose ${arg.description || arg.name} for /${commandName}.`;\n\n\treturn { content, rows };\n}\n\n/**\n * Checks if an error is a Discord \"Unknown Interaction\" error\n */\nexport function isUnknownInteractionError(error: unknown): boolean {\n\tif (!error || typeof error !== \"object\") {\n\t\treturn false;\n\t}\n\n\tconst err = error as {\n\t\tcode?: number;\n\t\tstatus?: number;\n\t\tmessage?: string;\n\t\trawError?: { code?: number; message?: string };\n\t};\n\n\t// Discord error code 10062 = Unknown Interaction\n\tif (err.code === 10062 || err.rawError?.code === 10062) {\n\t\treturn true;\n\t}\n\n\tif (err.status === 404 && /Unknown interaction/i.test(err.message ?? \"\")) {\n\t\treturn true;\n\t}\n\n\tif (/Unknown interaction/i.test(err.rawError?.message ?? \"\")) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n/**\n * Safely executes a Discord interaction call, catching expired interaction errors\n */\nexport async function safeInteractionCall<T>(\n\tfn: () => Promise<T>,\n\tonExpired?: () => void,\n): Promise<T | null> {\n\ttry {\n\t\treturn await fn();\n\t} catch (error) {\n\t\tif (isUnknownInteractionError(error)) {\n\t\t\tonExpired?.();\n\t\t\treturn null;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Creates command arguments from a single value\n */\nexport function createCommandArgs(\n\targName: string,\n\tvalue: string | number | boolean,\n): CommandArgs {\n\treturn {\n\t\tvalues: { [argName]: value },\n\t};\n}\n\n/**\n * Serializes command arguments to a string\n */\nexport function serializeCommandArgs(args?: CommandArgs): string {\n\tif (!args?.values) {\n\t\treturn \"\";\n\t}\n\n\treturn Object.entries(args.values)\n\t\t.map(([key, value]) => `${key}=${String(value)}`)\n\t\t.join(\" \");\n}\n\n/**\n * Builds the full command text from arguments\n */\nexport function buildCommandText(\n\tcommandName: string,\n\targs?: CommandArgs,\n): string {\n\tconst argsText = serializeCommandArgs(args);\n\treturn argsText ? `/${commandName} ${argsText}` : `/${commandName}`;\n}\n"
|
|
68
|
+
],
|
|
69
|
+
"mappings": "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",
|
|
70
|
+
"debugId": "623F0C93FED6BAFB64756E2164756E21",
|
|
71
|
+
"names": []
|
|
72
|
+
}
|