@elizaos/plugin-discord 2.0.0-beta.2 → 2.0.3-beta.2

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.
Files changed (116) hide show
  1. package/README.md +28 -68
  2. package/package.json +29 -10
  3. package/dist/account-client-pool.d.ts +0 -35
  4. package/dist/account-client-pool.d.ts.map +0 -1
  5. package/dist/accounts.d.ts +0 -164
  6. package/dist/accounts.d.ts.map +0 -1
  7. package/dist/actions/actionResultSemantics.d.ts +0 -9
  8. package/dist/actions/actionResultSemantics.d.ts.map +0 -1
  9. package/dist/actions/setup-credentials.d.ts +0 -25
  10. package/dist/actions/setup-credentials.d.ts.map +0 -1
  11. package/dist/addressing.d.ts +0 -15
  12. package/dist/addressing.d.ts.map +0 -1
  13. package/dist/allowlist.d.ts +0 -198
  14. package/dist/allowlist.d.ts.map +0 -1
  15. package/dist/attachments.d.ts +0 -93
  16. package/dist/attachments.d.ts.map +0 -1
  17. package/dist/auto-enable.d.ts +0 -4
  18. package/dist/auto-enable.d.ts.map +0 -1
  19. package/dist/banner.d.ts +0 -39
  20. package/dist/banner.d.ts.map +0 -1
  21. package/dist/compat.d.ts +0 -57
  22. package/dist/compat.d.ts.map +0 -1
  23. package/dist/config.d.ts +0 -156
  24. package/dist/config.d.ts.map +0 -1
  25. package/dist/connector-account-provider.d.ts +0 -26
  26. package/dist/connector-account-provider.d.ts.map +0 -1
  27. package/dist/constants.d.ts +0 -2
  28. package/dist/constants.d.ts.map +0 -1
  29. package/dist/debouncer.d.ts +0 -28
  30. package/dist/debouncer.d.ts.map +0 -1
  31. package/dist/discord-avatar-cache.d.ts +0 -10
  32. package/dist/discord-avatar-cache.d.ts.map +0 -1
  33. package/dist/discord-commands.d.ts +0 -16
  34. package/dist/discord-commands.d.ts.map +0 -1
  35. package/dist/discord-events.d.ts +0 -56
  36. package/dist/discord-events.d.ts.map +0 -1
  37. package/dist/discord-history.d.ts +0 -56
  38. package/dist/discord-history.d.ts.map +0 -1
  39. package/dist/discord-interactions.d.ts +0 -47
  40. package/dist/discord-interactions.d.ts.map +0 -1
  41. package/dist/discord-local-service.d.ts +0 -94
  42. package/dist/discord-local-service.d.ts.map +0 -1
  43. package/dist/discord-profiles.d.ts +0 -26
  44. package/dist/discord-profiles.d.ts.map +0 -1
  45. package/dist/discord-reactions.d.ts +0 -30
  46. package/dist/discord-reactions.d.ts.map +0 -1
  47. package/dist/draft-chunking.d.ts +0 -9
  48. package/dist/draft-chunking.d.ts.map +0 -1
  49. package/dist/draft-stream.d.ts +0 -23
  50. package/dist/draft-stream.d.ts.map +0 -1
  51. package/dist/environment.d.ts +0 -23
  52. package/dist/environment.d.ts.map +0 -1
  53. package/dist/generated/specs/spec-helpers.d.ts +0 -36
  54. package/dist/generated/specs/spec-helpers.d.ts.map +0 -1
  55. package/dist/generated/specs/specs.d.ts +0 -52
  56. package/dist/generated/specs/specs.d.ts.map +0 -1
  57. package/dist/identity.d.ts +0 -8
  58. package/dist/identity.d.ts.map +0 -1
  59. package/dist/inbound-envelope.d.ts +0 -8
  60. package/dist/inbound-envelope.d.ts.map +0 -1
  61. package/dist/index.browser.d.ts +0 -34
  62. package/dist/index.browser.d.ts.map +0 -1
  63. package/dist/index.d.ts +0 -22
  64. package/dist/index.d.ts.map +0 -1
  65. package/dist/index.js +0 -13784
  66. package/dist/index.js.map +0 -56
  67. package/dist/message-coalesce.d.ts +0 -31
  68. package/dist/message-coalesce.d.ts.map +0 -1
  69. package/dist/messages.d.ts +0 -66
  70. package/dist/messages.d.ts.map +0 -1
  71. package/dist/messaging.d.ts +0 -129
  72. package/dist/messaging.d.ts.map +0 -1
  73. package/dist/native-commands.d.ts +0 -142
  74. package/dist/native-commands.d.ts.map +0 -1
  75. package/dist/owner-pairing-service.d.ts +0 -73
  76. package/dist/owner-pairing-service.d.ts.map +0 -1
  77. package/dist/permissionEvents.d.ts +0 -42
  78. package/dist/permissionEvents.d.ts.map +0 -1
  79. package/dist/permissions.d.ts +0 -36
  80. package/dist/permissions.d.ts.map +0 -1
  81. package/dist/profileSync.d.ts +0 -8
  82. package/dist/profileSync.d.ts.map +0 -1
  83. package/dist/reasoning-tags.d.ts +0 -2
  84. package/dist/reasoning-tags.d.ts.map +0 -1
  85. package/dist/service.d.ts +0 -298
  86. package/dist/service.d.ts.map +0 -1
  87. package/dist/setup-routes.d.ts +0 -23
  88. package/dist/setup-routes.d.ts.map +0 -1
  89. package/dist/slash-commands.d.ts +0 -39
  90. package/dist/slash-commands.d.ts.map +0 -1
  91. package/dist/staleness.d.ts +0 -25
  92. package/dist/staleness.d.ts.map +0 -1
  93. package/dist/status-reactions.d.ts +0 -11
  94. package/dist/status-reactions.d.ts.map +0 -1
  95. package/dist/test-setup.d.ts +0 -1
  96. package/dist/test-setup.d.ts.map +0 -1
  97. package/dist/tests.d.ts +0 -123
  98. package/dist/tests.d.ts.map +0 -1
  99. package/dist/types.d.ts +0 -339
  100. package/dist/types.d.ts.map +0 -1
  101. package/dist/typing.d.ts +0 -7
  102. package/dist/typing.d.ts.map +0 -1
  103. package/dist/user-account-scraper/discord-browser-scraper.d.ts +0 -118
  104. package/dist/user-account-scraper/discord-browser-scraper.d.ts.map +0 -1
  105. package/dist/user-account-scraper/discord-desktop-cdp.d.ts +0 -33
  106. package/dist/user-account-scraper/discord-desktop-cdp.d.ts.map +0 -1
  107. package/dist/user-account-scraper/index.d.ts +0 -15
  108. package/dist/user-account-scraper/index.d.ts.map +0 -1
  109. package/dist/user-account-scraper/service.d.ts +0 -92
  110. package/dist/user-account-scraper/service.d.ts.map +0 -1
  111. package/dist/utils.d.ts +0 -51
  112. package/dist/utils.d.ts.map +0 -1
  113. package/dist/vitest.config.d.ts +0 -3
  114. package/dist/vitest.config.d.ts.map +0 -1
  115. package/dist/voice.d.ts +0 -235
  116. package/dist/voice.d.ts.map +0 -1
package/dist/index.js.map DELETED
@@ -1,56 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../index.ts", "../permissions.ts", "../banner.ts", "../accounts.ts", "../connector-account-provider.ts", "../owner-pairing-service.ts", "../slash-commands.ts", "../actions/setup-credentials.ts", "../constants.ts", "../actions/actionResultSemantics.ts", "../service.ts", "../account-client-pool.ts", "../discord-commands.ts", "../discord-interactions.ts", "../identity.ts", "../profileSync.ts", "../types.ts", "../discord-events.ts", "../addressing.ts", "../debouncer.ts", "../message-coalesce.ts", "../permissionEvents.ts", "../staleness.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", "../user-account-scraper/service.ts", "../user-account-scraper/discord-browser-scraper.ts", "../user-account-scraper/discord-desktop-cdp.ts", "../allowlist.ts", "../discord-avatar-cache.ts", "../discord-profiles.ts", "../messaging.ts", "../native-commands.ts"],
4
- "sourcesContent": [
5
- "import {\n\tgetConnectorAccountManager,\n\ttype IAgentRuntime,\n\tlogger,\n\ttype Plugin,\n} from \"@elizaos/core\";\nimport { printBanner } from \"./banner\";\nimport { createDiscordConnectorAccountProvider } from \"./connector-account-provider\";\nimport { DiscordOwnerPairingServiceImpl } from \"./owner-pairing-service\";\nimport { getPermissionValues } from \"./permissions\";\nimport { DiscordService } from \"./service\";\nimport { discordSetupRoutes } from \"./setup-routes\";\nimport { DiscordTestSuite } from \"./tests\";\nimport { DiscordUserAccountScraperImpl } from \"./user-account-scraper/service\";\n\nconst discordPlugin: Plugin = {\n\tname: \"discord\",\n\tdescription:\n\t\t\"Discord service plugin for integration with Discord servers and channels\",\n\tservices: [\n\t\tDiscordService,\n\t\tDiscordOwnerPairingServiceImpl,\n\t\tDiscordUserAccountScraperImpl,\n\t],\n\troutes: discordSetupRoutes,\n\tactions: [],\n\tproviders: [],\n\ttests: [new DiscordTestSuite()],\n\tautoEnable: {\n\t\tconnectorKeys: [\"discord\"],\n\t},\n\tinit: async (_config: Record<string, string>, runtime: IAgentRuntime) => {\n\t\ttry {\n\t\t\tconst manager = getConnectorAccountManager(runtime);\n\t\t\tmanager.registerProvider(createDiscordConnectorAccountProvider(runtime));\n\t\t} catch (err) {\n\t\t\tlogger.warn(\n\t\t\t\t{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\terr: err instanceof Error ? err.message : String(err),\n\t\t\t\t},\n\t\t\t\t\"Failed to register Discord provider with ConnectorAccountManager\",\n\t\t\t);\n\t\t}\n\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\";\nexport {\n\tbuildDiscordAvatarCacheFileName,\n\tcacheDiscordAvatarUrl,\n\tgetDiscordAvatarCacheDir,\n\tgetDiscordAvatarCachePath,\n\tgetDiscordAvatarPublicPath,\n\tisDiscordAvatarUrl,\n} from \"./discord-avatar-cache\";\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\";\nexport {\n\tcacheDiscordAvatarForRuntime,\n\tisCanonicalDiscordSource,\n\tresolveDiscordMessageAuthorProfile,\n\tresolveDiscordRoomProfile,\n\tresolveDiscordUserProfile,\n\tresolveStoredDiscordEntityProfile,\n} from \"./discord-profiles\";\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\";\nexport * from \"./user-account-scraper\";\n",
6
- "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",
7
- "/**\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",
8
- "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",
9
- "/**\n * Discord ConnectorAccountManager provider.\n *\n * Adapts the existing multi-account scaffolding in `accounts.ts` to the\n * `ConnectorAccountProvider` contract from\n * `@elizaos/core/connectors/account-manager`.\n *\n * Source of truth for accounts is character settings (`character.settings.discord`)\n * plus the legacy env-only DISCORD_API_TOKEN. The manager observes those via\n * `listAccounts`. `createAccount`/`patchAccount`/`deleteAccount` here are\n * adapters that surface a `ConnectorAccount` shape; persistent storage is\n * delegated to the manager's `ConnectorAccountStorage`.\n *\n * OAuth: Discord uses bot installation (out-of-band) plus an in-app pairing\n * flow handled by `owner-pairing-service.ts`. `startOAuth` returns a Discord\n * application install URL; `completeOAuth` is a no-op marker because the\n * pairing happens via the `/eliza-pair` slash command, not a redirect.\n */\n\nimport type {\n\tConnectorAccount,\n\tConnectorAccountManager,\n\tConnectorAccountPatch,\n\tConnectorAccountProvider,\n\tConnectorOAuthCallbackRequest,\n\tConnectorOAuthCallbackResult,\n\tConnectorOAuthStartRequest,\n\tConnectorOAuthStartResult,\n\tIAgentRuntime,\n} from \"@elizaos/core\";\nimport {\n\tDEFAULT_ACCOUNT_ID,\n\tlistEnabledDiscordAccounts,\n\tnormalizeAccountId,\n\ttype ResolvedDiscordAccount,\n\tresolveDiscordAccount,\n} from \"./accounts\";\n\nexport const DISCORD_PROVIDER_ID = \"discord\";\n\nfunction purposeForAccount(_account: ResolvedDiscordAccount): string[] {\n\treturn [\"messaging\"];\n}\n\nfunction accessGateForAccount(account: ResolvedDiscordAccount): string {\n\tconst dmPolicy = account.config?.dm?.policy;\n\tif (dmPolicy === \"pairing\") {\n\t\treturn \"pairing\";\n\t}\n\tif (dmPolicy === \"disabled\") {\n\t\treturn \"disabled\";\n\t}\n\treturn \"open\";\n}\n\nfunction roleForAccount(account: ResolvedDiscordAccount): \"OWNER\" | \"AGENT\" {\n\t// Owner-paired accounts surface as OWNER, otherwise treat the bot token\n\t// as the agent's own connector identity.\n\tconst dmPolicy = account.config?.dm?.policy;\n\tif (dmPolicy === \"pairing\") {\n\t\treturn \"OWNER\";\n\t}\n\treturn \"AGENT\";\n}\n\nfunction toConnectorAccount(account: ResolvedDiscordAccount): ConnectorAccount {\n\tconst now = Date.now();\n\treturn {\n\t\tid: normalizeAccountId(account.accountId),\n\t\tprovider: DISCORD_PROVIDER_ID,\n\t\tlabel: account.name ?? account.accountId,\n\t\trole: roleForAccount(account),\n\t\tpurpose: purposeForAccount(account),\n\t\taccessGate: accessGateForAccount(account),\n\t\tstatus: account.enabled && account.token ? \"connected\" : \"disabled\",\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t\tmetadata: {\n\t\t\ttokenSource: account.tokenSource,\n\t\t\tdmPolicy: account.config?.dm?.policy ?? \"open\",\n\t\t},\n\t};\n}\n\n/**\n * Builds the Discord provider for the ConnectorAccountManager. The provider's\n * lifecycle is owned by the manager; the plugin only registers it.\n */\nexport function createDiscordConnectorAccountProvider(\n\truntime: IAgentRuntime,\n): ConnectorAccountProvider {\n\treturn {\n\t\tprovider: DISCORD_PROVIDER_ID,\n\t\tlabel: \"Discord\",\n\t\tlistAccounts: async (\n\t\t\t_manager: ConnectorAccountManager,\n\t\t): Promise<ConnectorAccount[]> => {\n\t\t\tconst enabled = listEnabledDiscordAccounts(runtime);\n\t\t\tif (enabled.length > 0) {\n\t\t\t\treturn enabled.map(toConnectorAccount);\n\t\t\t}\n\t\t\t// Fall back to the default account so single-account env-only\n\t\t\t// deployments still surface in the manager. Status reflects whether\n\t\t\t// a token is actually configured.\n\t\t\tconst fallback = resolveDiscordAccount(runtime, DEFAULT_ACCOUNT_ID);\n\t\t\treturn [toConnectorAccount(fallback)];\n\t\t},\n\t\tcreateAccount: async (\n\t\t\tinput: ConnectorAccountPatch,\n\t\t\t_manager: ConnectorAccountManager,\n\t\t) => {\n\t\t\t// Persistence of new accounts is owned by the manager's storage; this\n\t\t\t// adapter just normalizes the patch into a Discord-shaped account.\n\t\t\treturn {\n\t\t\t\t...input,\n\t\t\t\tprovider: DISCORD_PROVIDER_ID,\n\t\t\t\trole: input.role ?? \"AGENT\",\n\t\t\t\tpurpose: input.purpose ?? [\"messaging\"],\n\t\t\t\taccessGate: input.accessGate ?? \"open\",\n\t\t\t\tstatus: input.status ?? \"pending\",\n\t\t\t};\n\t\t},\n\t\tpatchAccount: async (\n\t\t\t_accountId: string,\n\t\t\tpatch: ConnectorAccountPatch,\n\t\t\t_manager: ConnectorAccountManager,\n\t\t) => {\n\t\t\treturn { ...patch, provider: DISCORD_PROVIDER_ID };\n\t\t},\n\t\tdeleteAccount: async (\n\t\t\t_accountId: string,\n\t\t\t_manager: ConnectorAccountManager,\n\t\t) => {\n\t\t\t// No-op at the provider layer — runtime credentials live in character\n\t\t\t// settings; deletion of those is out of band.\n\t\t},\n\t\tstartOAuth: async (\n\t\t\trequest: ConnectorOAuthStartRequest,\n\t\t\t_manager: ConnectorAccountManager,\n\t\t): Promise<ConnectorOAuthStartResult> => {\n\t\t\tconst applicationId = runtime.getSetting(\"DISCORD_APPLICATION_ID\") as\n\t\t\t\t| string\n\t\t\t\t| undefined;\n\t\t\tif (!applicationId) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"DISCORD_APPLICATION_ID is not configured — cannot build install URL\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst scopes = (request.scopes ?? [\"bot\", \"applications.commands\"]).join(\n\t\t\t\t\"+\",\n\t\t\t);\n\t\t\t// Default permissions bitflag: send messages, view channel, read message history (1024+2048+65536)\n\t\t\tconst permissions = \"68608\";\n\t\t\tconst authUrl = `https://discord.com/api/oauth2/authorize?client_id=${encodeURIComponent(\n\t\t\t\tapplicationId,\n\t\t\t)}&scope=${scopes}&permissions=${permissions}`;\n\t\t\treturn {\n\t\t\t\tauthUrl,\n\t\t\t\tmetadata: {\n\t\t\t\t\tmode: \"bot_install\",\n\t\t\t\t\tnote: \"Discord uses bot installation + /eliza-pair slash command instead of a code exchange.\",\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\tcompleteOAuth: async (\n\t\t\trequest: ConnectorOAuthCallbackRequest,\n\t\t\t_manager: ConnectorAccountManager,\n\t\t): Promise<ConnectorOAuthCallbackResult> => {\n\t\t\t// Discord pairing is completed via the in-app slash command; the\n\t\t\t// callback simply marks the flow as completed and forwards any\n\t\t\t// account hints already attached to the flow.\n\t\t\treturn {\n\t\t\t\tflow: { status: \"completed\" },\n\t\t\t\taccount: {\n\t\t\t\t\tprovider: DISCORD_PROVIDER_ID,\n\t\t\t\t\tstatus: \"connected\",\n\t\t\t\t\taccessGate: \"pairing\",\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\tcompletedVia: \"owner_pairing_slash_command\",\n\t\t\t\t\t\tstate: request.flow.state,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n",
10
- "/**\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",
11
- "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-5-mini\",\n\t\"gpt-5.5\",\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\"openai/gpt-oss-120b\",\n\t\"eliza-1-9b\",\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 = (interaction.client as { runtime?: IAgentRuntime }).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\";\n\t\t\tconst { hasRoleAccess } = (await import(accessModuleId)) 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",
12
- "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 MAX_DISCORD_SETUP_TEXT_CHARS = 2_000;\nconst DISCORD_SETUP_ACTION_TIMEOUT_MS = 30_000;\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 readRoomChannelId(room: unknown): string | undefined {\n\tif (!room || typeof room !== \"object\" || !(\"channelId\" in room)) {\n\t\treturn undefined;\n\t}\n\tconst channelId = room.channelId;\n\treturn typeof channelId === \"string\" ? channelId : undefined;\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\t// @duplicate-component-audit-allow: credential probe validates the key; response content is ignored.\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 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 truncateActionText(text: string, maxChars: number): string {\n\treturn text.length > maxChars ? `${text.slice(0, maxChars - 3)}...` : text;\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: \"DISCORD_SETUP_CREDENTIALS\",\n\tsimiles: [\n\t\t\"DISCORD_SETUP\",\n\t\t\"DISCORD_PAIR\",\n\t\t\"DISCORD_CONNECT\",\n\t\t\"DISCORD_ADD_CREDENTIALS\",\n\t\t\"DISCORD_CONFIGURE_SERVICE\",\n\t\t\"DISCORD_CONNECT_SERVICE\",\n\t\t\"DISCORD_ADD_API_KEY\",\n\t\t\"DISCORD_SETUP_SERVICE\",\n\t],\n\tdescription:\n\t\t\"Start Discord credential setup or account pairing. Guides the user through setting up API credentials for supported third-party services, validates them when possible, and stores them securely.\",\n\tdescriptionCompressed: \"Set up Discord credentials.\",\n\tcontexts: [\"messaging\", \"connectors\", \"settings\"],\n\tcontextGate: { anyOf: [\"messaging\", \"connectors\", \"settings\"] },\n\troleGate: { minRole: \"USER\" },\n\tparameters: [\n\t\t{\n\t\t\tname: \"service\",\n\t\t\tdescription: \"Third-party service to configure from Discord.\",\n\t\t\trequired: false,\n\t\t\tschema: { type: \"string\" },\n\t\t},\n\t\t{\n\t\t\tname: \"credentials\",\n\t\t\tdescription: \"Credential values supplied by the user, when present.\",\n\t\t\trequired: false,\n\t\t\tschema: { type: \"object\" },\n\t\t},\n\t],\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 userId = message.entityId as string;\n\t\treturn activeSessions.has(userId) || Boolean(userId);\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 timeoutMs = DISCORD_SETUP_ACTION_TIMEOUT_MS;\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 = readRoomChannelId(room) || (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(\n\t\t\t\t\t\t\ttruncateActionText(\n\t\t\t\t\t\t\t\tbuildServiceListMessage(),\n\t\t\t\t\t\t\t\tMAX_DISCORD_SETUP_TEXT_CHARS,\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\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(\n\t\t\t\t\t\ttruncateActionText(\n\t\t\t\t\t\t\tbuildServiceListMessage(),\n\t\t\t\t\t\t\tMAX_DISCORD_SETUP_TEXT_CHARS,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlogger.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(), timeoutMs },\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(), timeoutMs },\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(), timeoutMs },\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(), timeoutMs },\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(), timeoutMs },\n\t\t\t};\n\t\t}\n\n\t\tif (callback) {\n\t\t\tawait callback({\n\t\t\t\ttext: truncateActionText(\n\t\t\t\t\tbuildServiceListMessage(),\n\t\t\t\t\tMAX_DISCORD_SETUP_TEXT_CHARS,\n\t\t\t\t),\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(), timeoutMs },\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: \"DISCORD_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: \"DISCORD_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: \"DISCORD_SETUP_CREDENTIALS\",\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t] as ActionExample[][],\n};\n\nexport default setupCredentials;\n",
13
- "export const DISCORD_SERVICE_NAME = \"discord\";\n",
14
- "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",
15
- "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\ttype MessageConnectorChatContext,\n\ttype MessageConnectorQueryContext,\n\ttype MessageConnectorTarget,\n\ttype MessageConnectorUserContext,\n\ttype Room,\n\tService,\n\tsetConnectorAdminWhitelist,\n\tstringToUuid,\n\ttype TargetInfo,\n\ttype UUID,\n\ttype World,\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 GuildTextBasedChannel,\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 {\n\tDiscordAccountClientPool,\n\ttype DiscordAccountClientState,\n} from \"./account-client-pool\";\nimport {\n\tDEFAULT_ACCOUNT_ID,\n\tlistEnabledDiscordAccounts,\n\tnormalizeAccountId,\n\ttype ResolvedDiscordAccount,\n\tresolveDefaultDiscordAccountId,\n} from \"./accounts\";\nimport 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 {\n\ttype DiscordServiceInternals,\n\tsetupDiscordEventListeners,\n} from \"./discord-events\";\nimport {\n\tbuildMemoryFromMessage as buildMemoryFromMessageExtracted,\n\tfetchChannelHistory as fetchChannelHistoryExtracted,\n\ttype HistoryServiceInternals,\n} from \"./discord-history\";\nimport {\n\thandleInteractionCreate as handleInteractionCreateExtracted,\n\ttype InteractionServiceInternals,\n\tonReady as onReadyExtracted,\n} from \"./discord-interactions\";\nimport {\n\thandleReactionAdd as handleReactionAddExtracted,\n\thandleReactionRemove as handleReactionRemoveExtracted,\n\ttype ReactionServiceInternals,\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\tBuildMemoryFromMessageOptions,\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}$/;\ntype MessageConnectorRegistration = Parameters<\n\tIAgentRuntime[\"registerMessageConnector\"]\n>[0];\n\ntype DiscordSettingsForEvents = DiscordSettings & {\n\tshouldIgnoreBotMessages: boolean;\n};\n\ntype DiscordAccountServiceFacade = IDiscordService &\n\tDiscordServiceInternals &\n\tHistoryServiceInternals &\n\tInteractionServiceInternals &\n\tReactionServiceInternals & {\n\t\tclient: DiscordJsClient;\n\t\tdiscordSettings: DiscordSettingsForEvents;\n\t\tcommandRegistrationQueue: Promise<void>;\n\t\taddAllowedChannel(channelId: string): boolean;\n\t\tremoveAllowedChannel(channelId: string): boolean;\n\t\tgetAllowedChannels(): string[];\n\t};\n\nfunction isGuildTextBasedChannel(\n\tchannel: Channel | null,\n): channel is GuildTextBasedChannel {\n\tif (!channel) return false;\n\tconst candidate = channel as Channel & {\n\t\tisTextBased?: () => boolean;\n\t\tguild?: unknown;\n\t};\n\treturn candidate.isTextBased?.() === true && Boolean(candidate.guild);\n}\n\ntype ConnectorFetchMessagesParams = {\n\ttarget?: TargetInfo;\n\taccountId?: string;\n\tlimit?: number;\n\tbefore?: string;\n\tafter?: string;\n\tcursor?: string;\n\tchannelId?: string;\n\troomId?: UUID;\n\tthreadId?: string;\n};\n\ntype ConnectorSearchMessagesParams = ConnectorFetchMessagesParams & {\n\tquery?: string;\n\tauthor?: string;\n};\n\ntype ConnectorMessageMutationParams = {\n\ttarget?: TargetInfo;\n\taccountId?: string;\n\tchannelId?: string;\n\troomId?: UUID;\n\tthreadId?: string;\n\tmessageId?: string;\n\temoji?: string;\n\tremove?: boolean;\n\tpin?: boolean;\n\ttext?: string;\n\tcontent?: Content;\n};\n\ntype ConnectorChannelMutationParams = {\n\ttarget?: TargetInfo;\n\taccountId?: string;\n\tchannelId?: string;\n\troomId?: UUID;\n\talias?: string;\n};\n\ntype ConnectorUserLookupParams = {\n\taccountId?: string;\n\tuserId?: string;\n\tusername?: string;\n\thandle?: string;\n\tquery?: string;\n};\n\ntype ExtendedMessageConnectorRegistration = MessageConnectorRegistration & {\n\tlistServers?: (context: MessageConnectorQueryContext) => Promise<World[]>;\n\tfetchMessages?: (\n\t\tcontext: MessageConnectorQueryContext,\n\t\tparams: ConnectorFetchMessagesParams,\n\t) => Promise<Memory[]>;\n\tsearchMessages?: (\n\t\tcontext: MessageConnectorQueryContext,\n\t\tparams: ConnectorSearchMessagesParams,\n\t) => Promise<Memory[]>;\n\treactHandler?: (\n\t\truntime: IAgentRuntime,\n\t\tparams: ConnectorMessageMutationParams,\n\t) => Promise<void>;\n\teditHandler?: (\n\t\truntime: IAgentRuntime,\n\t\tparams: ConnectorMessageMutationParams,\n\t) => Promise<Memory>;\n\tdeleteHandler?: (\n\t\truntime: IAgentRuntime,\n\t\tparams: ConnectorMessageMutationParams,\n\t) => Promise<void>;\n\tpinHandler?: (\n\t\truntime: IAgentRuntime,\n\t\tparams: ConnectorMessageMutationParams,\n\t) => Promise<void>;\n\tjoinHandler?: (\n\t\truntime: IAgentRuntime,\n\t\tparams: ConnectorChannelMutationParams,\n\t) => Promise<Room | null>;\n\tleaveHandler?: (\n\t\truntime: IAgentRuntime,\n\t\tparams: ConnectorChannelMutationParams,\n\t) => Promise<void>;\n\tgetUser?: (\n\t\truntime: IAgentRuntime,\n\t\tparams: ConnectorUserLookupParams,\n\t) => Promise<unknown>;\n};\n\nconst DISCORD_CONNECTOR_CONTEXTS = [\"social\", \"connectors\"];\nconst DISCORD_CONNECTOR_CAPABILITIES = [\n\t\"send_message\",\n\t\"read_messages\",\n\t\"search_messages\",\n\t\"resolve_targets\",\n\t\"list_rooms\",\n\t\"list_servers\",\n\t\"chat_context\",\n\t\"user_context\",\n\t\"react_message\",\n\t\"edit_message\",\n\t\"delete_message\",\n\t\"pin_message\",\n\t\"join_channel\",\n\t\"leave_channel\",\n\t\"get_user\",\n];\n\nfunction normalizeDiscordConnectorQuery(value: string): string {\n\treturn value\n\t\t.trim()\n\t\t.replace(/^<#(\\d+)>$/, \"$1\")\n\t\t.replace(/^<@!?(\\d+)>$/, \"$1\")\n\t\t.replace(/^#/, \"\")\n\t\t.replace(/^@/, \"\")\n\t\t.toLowerCase();\n}\n\nfunction scoreDiscordConnectorMatch(\n\tquery: string,\n\tid: string,\n\tlabels: Array<string | null | undefined>,\n): number {\n\tif (!query) {\n\t\treturn 0.45;\n\t}\n\tif (id === query) {\n\t\treturn 1;\n\t}\n\n\tlet bestScore = 0;\n\tfor (const label of labels) {\n\t\tconst normalized = label?.trim().toLowerCase();\n\t\tif (!normalized) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (normalized === query) {\n\t\t\tbestScore = Math.max(bestScore, 0.95);\n\t\t} else if (normalized.startsWith(query)) {\n\t\t\tbestScore = Math.max(bestScore, 0.85);\n\t\t} else if (normalized.includes(query)) {\n\t\t\tbestScore = Math.max(bestScore, 0.7);\n\t\t}\n\t}\n\treturn bestScore;\n}\n\nfunction isDiscordTextTarget(channel: unknown): boolean {\n\tconst maybeChannel = channel as {\n\t\tisTextBased?: () => boolean;\n\t\tisVoiceBased?: () => boolean;\n\t};\n\treturn Boolean(\n\t\tmaybeChannel?.isTextBased?.() && !maybeChannel?.isVoiceBased?.(),\n\t);\n}\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\nfunction stringArraySetting(value: unknown): string[] | undefined {\n\tif (Array.isArray(value)) {\n\t\tconst values = value\n\t\t\t.map((item) => String(item).trim())\n\t\t\t.filter((item) => item.length > 0);\n\t\treturn values.length > 0 ? values : undefined;\n\t}\n\tif (typeof value === \"string\" && value.trim()) {\n\t\tconst values = value\n\t\t\t.split(\",\")\n\t\t\t.map((item) => item.trim())\n\t\t\t.filter((item) => item.length > 0);\n\t\treturn values.length > 0 ? values : undefined;\n\t}\n\treturn undefined;\n}\n\nfunction accountIdFromRecord(value: unknown): string | undefined {\n\tif (!value || typeof value !== \"object\") {\n\t\treturn undefined;\n\t}\n\tconst accountId = (value as { accountId?: unknown }).accountId;\n\treturn typeof accountId === \"string\" && accountId.trim()\n\t\t? accountId.trim()\n\t\t: undefined;\n}\n\ntype DiscordAccountSettingsConfig = ResolvedDiscordAccount[\"config\"] &\n\tPartial<DiscordSettings> & {\n\t\tallowedChannelIds?: string[];\n\t\tchannelIds?: string[];\n\t\tlistenChannelIds?: string[];\n\t\tdm?: {\n\t\t\tpolicy?: DiscordSettings[\"dmPolicy\"];\n\t\t\tallowFrom?: Array<string | number>;\n\t\t};\n\t};\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\t/**\n\t * Connector account ID this service instance speaks for. Single-account\n\t * env-only deployments use DEFAULT_ACCOUNT_ID. When the multi-account\n\t * pool is wired in, each pool slot owns one client and one accountId.\n\t */\n\tpublic accountId: string = DEFAULT_ACCOUNT_ID;\n\tprivate defaultAccountId = DEFAULT_ACCOUNT_ID;\n\tprivate readonly accountPool = new DiscordAccountClientPool();\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\taccountId?: string | null,\n\t): Promise<void> {\n\t\tconst state = this.requireAccountState(accountId);\n\t\tawait state.clientReadyPromise;\n\n\t\tconst client = state.client;\n\t\tconst clientApplication = client?.application;\n\t\tif (!clientApplication) {\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\taccountId: state.accountId,\n\t\t\t\t},\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{\n\t\t\t\t\tsrc: \"plugin:discord\",\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\taccountId: state.accountId,\n\t\t\t\t},\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{\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\taccountId: state.accountId,\n\t\t\t\t\t},\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 = 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\taccountId: state.accountId,\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 = 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\taccountId: state.accountId,\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\taccountId: state.accountId,\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\taccountId: state.accountId,\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\taccountId: state.accountId,\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\tprivate resolveDiscordSettingsForAccount(\n\t\taccount: ResolvedDiscordAccount,\n\t): DiscordSettings {\n\t\tconst base = getDiscordSettings(this.runtime);\n\t\tconst config = account.config as DiscordAccountSettingsConfig;\n\t\tconst dmAllowFrom = config.dm?.allowFrom\n\t\t\t?.map((value) => String(value).trim())\n\t\t\t.filter((value) => value.length > 0);\n\n\t\treturn {\n\t\t\t...base,\n\t\t\tallowedChannelIds:\n\t\t\t\tstringArraySetting(config.allowedChannelIds) ??\n\t\t\t\tstringArraySetting(config.channelIds) ??\n\t\t\t\tbase.allowedChannelIds,\n\t\t\tshouldIgnoreBotMessages:\n\t\t\t\tconfig.shouldIgnoreBotMessages ?? base.shouldIgnoreBotMessages,\n\t\t\tshouldIgnoreDirectMessages:\n\t\t\t\tconfig.shouldIgnoreDirectMessages ?? base.shouldIgnoreDirectMessages,\n\t\t\tshouldRespondOnlyToMentions:\n\t\t\t\tconfig.shouldRespondOnlyToMentions ?? base.shouldRespondOnlyToMentions,\n\t\t\treplyToMode: config.replyToMode ?? base.replyToMode,\n\t\t\tdmPolicy: config.dm?.policy ?? base.dmPolicy,\n\t\t\tallowFrom:\n\t\t\t\tdmAllowFrom && dmAllowFrom.length > 0 ? dmAllowFrom : base.allowFrom,\n\t\t\tsyncProfile: config.syncProfile ?? base.syncProfile,\n\t\t\tprofileName: config.profileName ?? base.profileName,\n\t\t\tprofileAvatar: config.profileAvatar ?? base.profileAvatar,\n\t\t\tautoReply: config.autoReply ?? base.autoReply,\n\t\t};\n\t}\n\n\tprivate resolveListenChannelIdsForAccount(\n\t\taccount: ResolvedDiscordAccount,\n\t): string[] | undefined {\n\t\treturn (\n\t\t\tstringArraySetting(\n\t\t\t\t(account.config as DiscordAccountSettingsConfig).listenChannelIds,\n\t\t\t) ??\n\t\t\tstringArraySetting(this.runtime.getSetting(\"DISCORD_LISTEN_CHANNEL_IDS\"))\n\t\t);\n\t}\n\n\tprivate createDiscordJsClient(): DiscordJsClient {\n\t\treturn new DiscordJsClient({\n\t\t\tintents: [\n\t\t\t\tGatewayIntentBits.Guilds,\n\t\t\t\tGatewayIntentBits.GuildMembers,\n\t\t\t\tGatewayIntentBits.GuildPresences,\n\t\t\t\tGatewayIntentBits.DirectMessages,\n\t\t\t\tGatewayIntentBits.GuildVoiceStates,\n\t\t\t\tGatewayIntentBits.MessageContent,\n\t\t\t\tGatewayIntentBits.GuildMessages,\n\t\t\t\tGatewayIntentBits.DirectMessageTyping,\n\t\t\t\tGatewayIntentBits.GuildMessageTyping,\n\t\t\t\tGatewayIntentBits.GuildMessageReactions,\n\t\t\t],\n\t\t\tpartials: [\n\t\t\t\tPartials.Channel,\n\t\t\t\tPartials.Message,\n\t\t\t\tPartials.User,\n\t\t\t\tPartials.Reaction,\n\t\t\t],\n\t\t});\n\t}\n\n\tprivate syncLegacyDefaultAliases(\n\t\tstate: DiscordAccountClientState | null,\n\t): void {\n\t\tthis.accountId = state?.accountId ?? this.defaultAccountId;\n\t\tthis.client = state?.client ?? null;\n\t\tthis.discordSettings = state?.settings ?? getDiscordSettings(this.runtime);\n\t\tthis.messageManager = state?.messageManager;\n\t\tthis.voiceManager = state?.voiceManager;\n\t\tthis.messageDebouncer = state?.messageDebouncer;\n\t\tthis.channelDebouncer = state?.channelDebouncer;\n\t\tthis.allowedChannelIds = state?.allowedChannelIds;\n\t\tthis.dynamicChannelIds = state?.dynamicChannelIds ?? new Set();\n\t\tthis.clientReadyPromise = state?.clientReadyPromise ?? null;\n\t}\n\n\tprivate getAccountState(\n\t\taccountId?: string | null,\n\t): DiscordAccountClientState | null {\n\t\tconst requested = accountId\n\t\t\t? normalizeAccountId(accountId)\n\t\t\t: this.defaultAccountId;\n\t\treturn this.accountPool?.get?.(requested) ?? null;\n\t}\n\n\tprivate getDefaultAccountState(): DiscordAccountClientState | null {\n\t\treturn this.accountPool?.getDefault?.() ?? null;\n\t}\n\n\tprivate requireAccountState(\n\t\taccountId?: string | null,\n\t): DiscordAccountClientState {\n\t\tconst normalized = accountId\n\t\t\t? normalizeAccountId(accountId)\n\t\t\t: this.defaultAccountId;\n\t\tconst state = this.getAccountState(normalized);\n\t\tif (!state) {\n\t\t\tthrow new Error(`Discord account is not configured: ${normalized}`);\n\t\t}\n\t\treturn state;\n\t}\n\n\tprivate resolveAccountIdFromTarget(\n\t\ttarget?: TargetInfo | null,\n\t\tfallback?: unknown,\n\t): string {\n\t\treturn normalizeAccountId(\n\t\t\taccountIdFromRecord(target) ??\n\t\t\t\taccountIdFromRecord(fallback) ??\n\t\t\t\tthis.defaultAccountId,\n\t\t);\n\t}\n\n\tpublic getDefaultAccountId(): string {\n\t\treturn this.defaultAccountId ?? DEFAULT_ACCOUNT_ID;\n\t}\n\n\tpublic getAccountIds(): string[] {\n\t\treturn this.accountPool?.listAccountIds?.() ?? [];\n\t}\n\n\tpublic getClient(accountId?: string | null): DiscordJsClient | null {\n\t\tconst state = this.getAccountState(accountId);\n\t\tif (state?.client) {\n\t\t\treturn state.client;\n\t\t}\n\t\tconst requested = accountId\n\t\t\t? normalizeAccountId(accountId)\n\t\t\t: (this.defaultAccountId ?? DEFAULT_ACCOUNT_ID);\n\t\tconst defaultAccountId = this.defaultAccountId ?? DEFAULT_ACCOUNT_ID;\n\t\treturn requested === defaultAccountId ? (this.client ?? null) : null;\n\t}\n\n\tpublic getAccountLabel(accountId?: string | null): string {\n\t\tconst state = this.getAccountState(accountId);\n\t\treturn state?.account.name ?? state?.accountId ?? this.defaultAccountId;\n\t}\n\n\tprivate createAccountServiceFacade(\n\t\tstate?: DiscordAccountClientState | null,\n\t): DiscordAccountServiceFacade {\n\t\tconst parent = this;\n\t\tconst accountId = () => state?.accountId ?? parent.accountId;\n\t\tconst accountClient = (): DiscordJsClient => {\n\t\t\tconst client = state?.client ?? parent.client;\n\t\t\tif (!client) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Discord client is not available for account ${accountId()}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn client;\n\t\t};\n\t\tconst accountSettings = (): DiscordSettingsForEvents => {\n\t\t\tconst settings = state?.settings ?? parent.discordSettings;\n\t\t\treturn {\n\t\t\t\t...settings,\n\t\t\t\tshouldIgnoreBotMessages: settings.shouldIgnoreBotMessages ?? false,\n\t\t\t};\n\t\t};\n\t\tconst facade: DiscordAccountServiceFacade = {\n\t\t\tget accountId() {\n\t\t\t\treturn accountId();\n\t\t\t},\n\t\t\tget client() {\n\t\t\t\treturn accountClient();\n\t\t\t},\n\t\t\tset client(value: DiscordJsClient | null) {\n\t\t\t\tif (state) {\n\t\t\t\t\tstate.client = value;\n\t\t\t\t}\n\t\t\t\tif (!state || state.accountId === parent.defaultAccountId) {\n\t\t\t\t\tparent.client = value;\n\t\t\t\t}\n\t\t\t},\n\t\t\tget runtime() {\n\t\t\t\treturn parent.runtime;\n\t\t\t},\n\t\t\tget character() {\n\t\t\t\treturn parent.character;\n\t\t\t},\n\t\t\tget discordSettings() {\n\t\t\t\treturn accountSettings();\n\t\t\t},\n\t\t\tset discordSettings(value: DiscordSettingsForEvents) {\n\t\t\t\tif (state) {\n\t\t\t\t\tstate.settings = value;\n\t\t\t\t}\n\t\t\t\tif (!state || state.accountId === parent.defaultAccountId) {\n\t\t\t\t\tparent.discordSettings = value;\n\t\t\t\t}\n\t\t\t},\n\t\t\tget messageManager() {\n\t\t\t\treturn state?.messageManager ?? parent.messageManager;\n\t\t\t},\n\t\t\tset messageManager(value: MessageManager | undefined) {\n\t\t\t\tif (state) {\n\t\t\t\t\tstate.messageManager = value;\n\t\t\t\t}\n\t\t\t\tif (!state || state.accountId === parent.defaultAccountId) {\n\t\t\t\t\tparent.messageManager = value;\n\t\t\t\t}\n\t\t\t},\n\t\t\tget voiceManager() {\n\t\t\t\treturn state?.voiceManager ?? parent.voiceManager;\n\t\t\t},\n\t\t\tset voiceManager(value: VoiceManager | undefined) {\n\t\t\t\tif (state) {\n\t\t\t\t\tstate.voiceManager = value;\n\t\t\t\t}\n\t\t\t\tif (!state || state.accountId === parent.defaultAccountId) {\n\t\t\t\t\tparent.voiceManager = value;\n\t\t\t\t}\n\t\t\t},\n\t\t\tget messageDebouncer() {\n\t\t\t\treturn state?.messageDebouncer ?? parent.messageDebouncer;\n\t\t\t},\n\t\t\tset messageDebouncer(value: MessageDebouncer | undefined) {\n\t\t\t\tif (state) {\n\t\t\t\t\tstate.messageDebouncer = value;\n\t\t\t\t}\n\t\t\t\tif (!state || state.accountId === parent.defaultAccountId) {\n\t\t\t\t\tparent.messageDebouncer = value;\n\t\t\t\t}\n\t\t\t},\n\t\t\tget channelDebouncer() {\n\t\t\t\treturn state?.channelDebouncer ?? parent.channelDebouncer;\n\t\t\t},\n\t\t\tset channelDebouncer(value: ChannelDebouncer | undefined) {\n\t\t\t\tif (state) {\n\t\t\t\t\tstate.channelDebouncer = value;\n\t\t\t\t}\n\t\t\t\tif (!state || state.accountId === parent.defaultAccountId) {\n\t\t\t\t\tparent.channelDebouncer = value;\n\t\t\t\t}\n\t\t\t},\n\t\t\tget allowedChannelIds() {\n\t\t\t\treturn state?.allowedChannelIds ?? parent.allowedChannelIds;\n\t\t\t},\n\t\t\tset allowedChannelIds(value: string[] | undefined) {\n\t\t\t\tif (state) {\n\t\t\t\t\tstate.allowedChannelIds = value;\n\t\t\t\t}\n\t\t\t\tif (!state || state.accountId === parent.defaultAccountId) {\n\t\t\t\t\tparent.allowedChannelIds = value;\n\t\t\t\t}\n\t\t\t},\n\t\t\tget listenChannelIds() {\n\t\t\t\treturn state?.listenChannelIds;\n\t\t\t},\n\t\t\tget allowAllSlashCommands() {\n\t\t\t\treturn parent.allowAllSlashCommands;\n\t\t\t},\n\t\t\tget slashCommands() {\n\t\t\t\treturn parent.slashCommands;\n\t\t\t},\n\t\t\tset slashCommands(value: DiscordSlashCommand[]) {\n\t\t\t\tparent.slashCommands = value;\n\t\t\t},\n\t\t\tget commandRegistrationQueue() {\n\t\t\t\treturn parent.commandRegistrationQueue;\n\t\t\t},\n\t\t\tset commandRegistrationQueue(value: Promise<void>) {\n\t\t\t\tparent.commandRegistrationQueue = value;\n\t\t\t},\n\t\t\tget userSelections() {\n\t\t\t\treturn parent.userSelections;\n\t\t\t},\n\t\t\tget timeouts() {\n\t\t\t\treturn parent.timeouts;\n\t\t\t},\n\t\t\tisChannelAllowed: (channelId: string) =>\n\t\t\t\tparent.isChannelAllowed(channelId, state?.accountId),\n\t\t\taddAllowedChannel: (channelId: string) =>\n\t\t\t\tparent.addAllowedChannel(channelId, state?.accountId),\n\t\t\tremoveAllowedChannel: (channelId: string) =>\n\t\t\t\tparent.removeAllowedChannel(channelId, state?.accountId),\n\t\t\tgetAllowedChannels: () => parent.getAllowedChannels(state?.accountId),\n\t\t\tresolveDiscordEntityId: (userId: string) =>\n\t\t\t\tparent.resolveDiscordEntityId(userId),\n\t\t\tgetChannelType: (channel: Channel) => parent.getChannelType(channel),\n\t\t\tisGuildTextBasedChannel,\n\t\t\tbuildMemoryFromMessage: (\n\t\t\t\tmessage: Message,\n\t\t\t\toptions?: BuildMemoryFromMessageOptions,\n\t\t\t) =>\n\t\t\t\tparent.buildMemoryFromMessage(message, {\n\t\t\t\t\t...options,\n\t\t\t\t\taccountId: state?.accountId ?? parent.accountId,\n\t\t\t\t}),\n\t\t\thandleInteractionCreate: (interaction: Interaction) =>\n\t\t\t\tparent.handleInteractionCreateForAccount(accountId(), interaction),\n\t\t\thandleGuildCreate: (guild: Guild) =>\n\t\t\t\tparent.handleGuildCreateForAccount(accountId(), guild),\n\t\t\thandleGuildMemberAdd: (member: GuildMember) =>\n\t\t\t\tparent.handleGuildMemberAddForAccount(accountId(), member),\n\t\t\thandleReactionAdd: (\n\t\t\t\treaction: MessageReaction | PartialMessageReaction,\n\t\t\t\tuser: User | PartialUser,\n\t\t\t) => parent.handleReactionAddForAccount(accountId(), reaction, user),\n\t\t\thandleReactionRemove: (\n\t\t\t\treaction: MessageReaction | PartialMessageReaction,\n\t\t\t\tuser: User | PartialUser,\n\t\t\t) => parent.handleReactionRemoveForAccount(accountId(), reaction, user),\n\t\t\trefreshOwnerDiscordUserIds: (client: unknown) => {\n\t\t\t\tif (!(client instanceof DiscordJsClient)) {\n\t\t\t\t\tthrow new Error(\"Discord client is not available for owner refresh\");\n\t\t\t\t}\n\t\t\t\treturn parent.refreshOwnerDiscordUserIds(client);\n\t\t\t},\n\t\t\tregisterSlashCommands: (commands: DiscordSlashCommand[]) =>\n\t\t\t\tparent.registerSlashCommands(commands, state?.accountId),\n\t\t\tclientReadyPromise:\n\t\t\t\tstate?.clientReadyPromise ?? parent.clientReadyPromise,\n\t\t\taccountToken: state?.account.token,\n\t\t};\n\t\treturn facade;\n\t}\n\n\tprivate initializeAccount(account: ResolvedDiscordAccount): void {\n\t\tconst accountId = normalizeAccountId(account.accountId);\n\t\tconst settings = this.resolveDiscordSettingsForAccount(account);\n\t\tconst state: DiscordAccountClientState = {\n\t\t\taccountId,\n\t\t\taccount: { ...account, accountId },\n\t\t\tclient: this.createDiscordJsClient(),\n\t\t\tsettings,\n\t\t\tallowedChannelIds: settings.allowedChannelIds,\n\t\t\tlistenChannelIds: this.resolveListenChannelIdsForAccount(account),\n\t\t\tdynamicChannelIds: new Set(),\n\t\t\tclientReadyPromise: null,\n\t\t\tloginFailed: false,\n\t\t};\n\n\t\tthis.accountPool.set(state);\n\t\tconst facade = this.createAccountServiceFacade(state);\n\t\tstate.voiceManager = new VoiceManager(facade, this.runtime);\n\t\tstate.messageManager = new MessageManager(facade, this.runtime);\n\n\t\tconst client = state.client;\n\t\tstate.clientReadyPromise = new Promise((resolve, reject) => {\n\t\t\tclient.once(Events.ClientReady, async (readyClient) => {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.onReadyForAccount(state.accountId, readyClient);\n\t\t\t\t\tresolve();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t\t`Error in Discord onReady for account ${state.accountId}: ${\n\t\t\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t\t\t}`,\n\t\t\t\t\t);\n\t\t\t\t\treject(error);\n\t\t\t\t}\n\t\t\t});\n\t\t\tclient.once(Events.Error, (error) => {\n\t\t\t\tthis.runtime.logger.error(\n\t\t\t\t\t`Discord client error for account ${state.accountId}: ${\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\tstate.loginFailed = true;\n\t\t\t\treject(error);\n\t\t\t});\n\t\t\tclient.login(account.token).catch((error) => {\n\t\t\t\tthis.runtime.logger.warn(\n\t\t\t\t\t`Failed to login to Discord account ${state.accountId}: ${\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t\t} — check the configured Discord bot token`,\n\t\t\t\t);\n\t\t\t\tstate.loginFailed = true;\n\t\t\t\tstate.client?.destroy().catch(() => {});\n\t\t\t\tstate.client = null;\n\t\t\t\tif (state.accountId === this.defaultAccountId) {\n\t\t\t\t\tthis.syncLegacyDefaultAliases(state);\n\t\t\t\t}\n\t\t\t\treject(error);\n\t\t\t});\n\t\t});\n\n\t\tstate.clientReadyPromise.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\taccountId: state.accountId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t\t\"Discord client ready promise rejected (already logged above)\",\n\t\t\t);\n\t\t\tstate.loginFailed = true;\n\t\t\tif (state.accountId === this.defaultAccountId) {\n\t\t\t\tthis._loginFailed = true;\n\t\t\t}\n\t\t});\n\n\t\tthis.setupEventListenersForAccount(state);\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\tthis.defaultAccountId = normalizeAccountId(\n\t\t\tresolveDefaultDiscordAccountId(this.runtime),\n\t\t);\n\t\tthis.accountPool.setDefaultAccountId(this.defaultAccountId);\n\t\tthis.accountId = this.defaultAccountId;\n\n\t\tconst accounts = listEnabledDiscordAccounts(this.runtime);\n\t\tif (accounts.length === 0) {\n\t\t\tthis.runtime.logger.warn(\"Discord API Token not provided\");\n\t\t\tthis.syncLegacyDefaultAliases(null);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tfor (const account of accounts) {\n\t\t\t\tthis.initializeAccount(account);\n\t\t\t}\n\n\t\t\tconst defaultState = this.getDefaultAccountState();\n\t\t\tif (defaultState) {\n\t\t\t\tthis.defaultAccountId = defaultState.accountId;\n\t\t\t\tthis.accountPool.setDefaultAccountId(defaultState.accountId);\n\t\t\t}\n\t\t\tthis.syncLegacyDefaultAliases(defaultState);\n\t\t\tthis.runtime.logger.info(\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\tdefaultAccountId: this.defaultAccountId,\n\t\t\t\t\taccountIds: this.getAccountIds(),\n\t\t\t\t},\n\t\t\t\t\"Initialized Discord account client pool\",\n\t\t\t);\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.syncLegacyDefaultAliases(null);\n\t\t}\n\t}\n\n\tpublic isHealthy(): boolean {\n\t\tconst state = this.getDefaultAccountState();\n\t\tif (this._loginFailed || !state?.client || state.loginFailed) {\n\t\t\treturn false;\n\t\t}\n\t\treturn state.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\t// Resolve the connector account this outbound message must use.\n\t\t// Priority: explicit target.accountId > this service instance's default.\n\t\t// `Content.metadata` is intentionally NOT consulted because it may be\n\t\t// user-supplied per the MessageMetadata contract — actions thread the\n\t\t// trusted inbound `Memory.metadata.accountId` into `target.accountId`.\n\t\tconst accountId = this.resolveAccountIdFromTarget(target);\n\t\tconst state = this.getAccountState(accountId);\n\t\tconst client = state?.client ?? null;\n\t\tif (!client?.isReady()) {\n\t\t\truntime.logger.error(\"Client not ready\");\n\t\t\tthrow new Error(`Discord client is not ready for account ${accountId}.`);\n\t\t}\n\n\t\t// Reference content to avoid an unused-parameter lint hit; outbound\n\t\t// resolution only consults `target.accountId` for trust reasons.\n\t\tvoid content;\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, accountId);\n\t\t\tif (\n\t\t\t\tstate?.allowedChannelIds &&\n\t\t\t\t!this.isChannelAllowed(targetChannel.id, accountId) &&\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\tmetadata: {\n\t\t\t\t\t\t\taccountId,\n\t\t\t\t\t\t},\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\taccountId,\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\tprivate buildConnectorChannelTarget(\n\t\tchannel: Channel,\n\t\tscore = 0.5,\n\t\taccountId = this.defaultAccountId,\n\t): MessageConnectorTarget | null {\n\t\tif (!isDiscordTextTarget(channel)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst channelRecord = channel as Channel & {\n\t\t\tguild?: Guild;\n\t\t\tname?: string;\n\t\t\tparentId?: string | null;\n\t\t\tisThread?: () => boolean;\n\t\t\turl?: string;\n\t\t};\n\t\tconst parentId =\n\t\t\ttypeof channelRecord.parentId === \"string\"\n\t\t\t\t? channelRecord.parentId\n\t\t\t\t: undefined;\n\t\tconst isThread = Boolean(channelRecord.isThread?.());\n\t\tconst state = this.getAccountState(accountId);\n\t\tif (\n\t\t\tstate?.allowedChannelIds &&\n\t\t\t!this.isChannelAllowed(channel.id, accountId) &&\n\t\t\t!(parentId && this.isChannelAllowed(parentId, accountId))\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst guild = channelRecord.guild;\n\t\tconst roomId = createUniqueUuid(this.runtime, channel.id) as UUID;\n\t\tconst label =\n\t\t\ttypeof channelRecord.name === \"string\" && channelRecord.name.length > 0\n\t\t\t\t? `${isThread ? \"Thread\" : \"#\"}${channelRecord.name}`\n\t\t\t\t: channel.id;\n\n\t\treturn {\n\t\t\ttarget: {\n\t\t\t\tsource: \"discord\",\n\t\t\t\taccountId,\n\t\t\t\troomId,\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tserverId: guild?.id,\n\t\t\t\tthreadId: isThread ? channel.id : undefined,\n\t\t\t} as TargetInfo,\n\t\t\tlabel,\n\t\t\tkind: isThread ? \"thread\" : \"channel\",\n\t\t\tdescription: guild?.name ? `${label} in ${guild.name}` : label,\n\t\t\tscore,\n\t\t\tcontexts: [\"social\", \"connectors\"],\n\t\t\tmetadata: {\n\t\t\t\taccountId,\n\t\t\t\tdiscordChannelId: channel.id,\n\t\t\t\tdiscordGuildId: guild?.id,\n\t\t\t\tdiscordGuildName: guild?.name,\n\t\t\t\tdiscordParentChannelId: parentId,\n\t\t\t\tchannelName: channelRecord.name,\n\t\t\t\tisThread,\n\t\t\t\turl: channelRecord.url,\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate buildConnectorUserTarget(\n\t\tuser: User,\n\t\tguild?: Guild | null,\n\t\tdisplayName?: string,\n\t\tscore = 0.5,\n\t\taccountId = this.defaultAccountId,\n\t): MessageConnectorTarget | null {\n\t\tif (!user || user.bot) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst label = displayName || user.globalName || user.username || user.id;\n\t\treturn {\n\t\t\ttarget: {\n\t\t\t\tsource: \"discord\",\n\t\t\t\taccountId,\n\t\t\t\tentityId: user.id as UUID,\n\t\t\t\tserverId: guild?.id,\n\t\t\t} as TargetInfo,\n\t\t\tlabel: `@${label}`,\n\t\t\tkind: \"user\",\n\t\t\tdescription: guild?.name\n\t\t\t\t? `Discord user in ${guild.name}`\n\t\t\t\t: \"Discord user\",\n\t\t\tscore,\n\t\t\tcontexts: [\"social\", \"connectors\"],\n\t\t\tmetadata: {\n\t\t\t\taccountId,\n\t\t\t\tdiscordUserId: user.id,\n\t\t\t\tdiscordUsername: user.username,\n\t\t\t\tdiscordGlobalName: user.globalName,\n\t\t\t\tdiscordGuildId: guild?.id,\n\t\t\t\tdiscordGuildName: guild?.name,\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate dedupeConnectorTargets(\n\t\ttargets: MessageConnectorTarget[],\n\t): MessageConnectorTarget[] {\n\t\tconst byKey = new Map<string, MessageConnectorTarget>();\n\t\tfor (const target of targets) {\n\t\t\tconst key = [\n\t\t\t\ttarget.kind ?? \"target\",\n\t\t\t\ttarget.target.channelId ?? \"\",\n\t\t\t\ttarget.target.entityId ?? \"\",\n\t\t\t\ttarget.target.threadId ?? \"\",\n\t\t\t].join(\":\");\n\t\t\tconst existing = byKey.get(key);\n\t\t\tif (!existing || (target.score ?? 0) > (existing.score ?? 0)) {\n\t\t\t\tbyKey.set(key, target);\n\t\t\t}\n\t\t}\n\t\treturn Array.from(byKey.values()).sort(\n\t\t\t(a, b) => (b.score ?? 0) - (a.score ?? 0),\n\t\t);\n\t}\n\n\tpublic async resolveConnectorTargets(\n\t\tquery: string,\n\t\tcontext: MessageConnectorQueryContext,\n\t): Promise<MessageConnectorTarget[]> {\n\t\tconst accountId = this.resolveAccountIdFromTarget(context.target, context);\n\t\tconst client = this.getClient(accountId);\n\t\tif (!client) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst normalizedQuery = normalizeDiscordConnectorQuery(query);\n\t\tconst results: MessageConnectorTarget[] = [];\n\t\tconst guilds = Array.from(client.guilds.cache.values());\n\n\t\tfor (const guild of guilds) {\n\t\t\tconst cachedChannels = Array.from(guild.channels.cache.values());\n\t\t\tfor (const channel of cachedChannels) {\n\t\t\t\tif (!channel || !isDiscordTextTarget(channel)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst channelRecord = channel as Channel & { name?: string };\n\t\t\t\tconst score = scoreDiscordConnectorMatch(normalizedQuery, channel.id, [\n\t\t\t\t\tchannelRecord.name,\n\t\t\t\t]);\n\t\t\t\tif (score <= 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst target = this.buildConnectorChannelTarget(\n\t\t\t\t\tchannel,\n\t\t\t\t\tscore,\n\t\t\t\t\taccountId,\n\t\t\t\t);\n\t\t\t\tif (target) {\n\t\t\t\t\tresults.push(target);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (normalizedQuery.length >= 2) {\n\t\t\t\ttry {\n\t\t\t\t\tconst members = await guild.members.fetch({\n\t\t\t\t\t\tquery: normalizedQuery,\n\t\t\t\t\t\tlimit: 10,\n\t\t\t\t\t});\n\t\t\t\t\tfor (const member of members.values()) {\n\t\t\t\t\t\tconst score = scoreDiscordConnectorMatch(\n\t\t\t\t\t\t\tnormalizedQuery,\n\t\t\t\t\t\t\tmember.id,\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\tmember.displayName,\n\t\t\t\t\t\t\t\tmember.user.username,\n\t\t\t\t\t\t\t\tmember.user.globalName,\n\t\t\t\t\t\t\t\tmember.user.tag,\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst target = this.buildConnectorUserTarget(\n\t\t\t\t\t\t\tmember.user,\n\t\t\t\t\t\t\tguild,\n\t\t\t\t\t\t\tmember.displayName,\n\t\t\t\t\t\t\tscore || 0.65,\n\t\t\t\t\t\t\taccountId,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (target) {\n\t\t\t\t\t\t\tresults.push(target);\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.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\tguildId: guild.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\"Discord connector member query failed\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const member of guild.members.cache.values()) {\n\t\t\t\tconst score = scoreDiscordConnectorMatch(normalizedQuery, member.id, [\n\t\t\t\t\tmember.displayName,\n\t\t\t\t\tmember.user.username,\n\t\t\t\t\tmember.user.globalName,\n\t\t\t\t\tmember.user.tag,\n\t\t\t\t]);\n\t\t\t\tif (score <= 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst target = this.buildConnectorUserTarget(\n\t\t\t\t\tmember.user,\n\t\t\t\t\tguild,\n\t\t\t\t\tmember.displayName,\n\t\t\t\t\tscore,\n\t\t\t\t\taccountId,\n\t\t\t\t);\n\t\t\t\tif (target) {\n\t\t\t\t\tresults.push(target);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (DISCORD_SNOWFLAKE_PATTERN.test(normalizedQuery)) {\n\t\t\ttry {\n\t\t\t\tconst channel = await client.channels.fetch(normalizedQuery);\n\t\t\t\tif (channel) {\n\t\t\t\t\tconst target = this.buildConnectorChannelTarget(\n\t\t\t\t\t\tchannel,\n\t\t\t\t\t\t1,\n\t\t\t\t\t\taccountId,\n\t\t\t\t\t);\n\t\t\t\t\tif (target) {\n\t\t\t\t\t\tresults.push(target);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Snowflake may be a user id; try user lookup below.\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst user = await client.users.fetch(normalizedQuery);\n\t\t\t\tconst target = this.buildConnectorUserTarget(\n\t\t\t\t\tuser,\n\t\t\t\t\tnull,\n\t\t\t\t\tundefined,\n\t\t\t\t\t1,\n\t\t\t\t\taccountId,\n\t\t\t\t);\n\t\t\t\tif (target) {\n\t\t\t\t\tresults.push(target);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// No exact user match.\n\t\t\t}\n\t\t}\n\n\t\tif (context.target?.channelId) {\n\t\t\ttry {\n\t\t\t\tconst channel = await client.channels.fetch(context.target.channelId);\n\t\t\t\tif (channel) {\n\t\t\t\t\tconst target = this.buildConnectorChannelTarget(\n\t\t\t\t\t\tchannel,\n\t\t\t\t\t\t0.6,\n\t\t\t\t\t\taccountId,\n\t\t\t\t\t);\n\t\t\t\t\tif (target) {\n\t\t\t\t\t\tresults.push(target);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Ignore stale current-channel hints.\n\t\t\t}\n\t\t}\n\n\t\treturn this.dedupeConnectorTargets(results).slice(0, 25);\n\t}\n\n\tpublic async listConnectorRooms(\n\t\tcontext: MessageConnectorQueryContext,\n\t): Promise<MessageConnectorTarget[]> {\n\t\tconst accountId = this.resolveAccountIdFromTarget(context.target, context);\n\t\tconst client = this.getClient(accountId);\n\t\tif (!client) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst targets: MessageConnectorTarget[] = [];\n\t\tfor (const guild of client.guilds.cache.values()) {\n\t\t\tfor (const channel of guild.channels.cache.values()) {\n\t\t\t\tconst target = this.buildConnectorChannelTarget(\n\t\t\t\t\tchannel as Channel,\n\t\t\t\t\t0.5,\n\t\t\t\t\taccountId,\n\t\t\t\t);\n\t\t\t\tif (target) {\n\t\t\t\t\ttargets.push(target);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn this.dedupeConnectorTargets(targets).slice(0, 50);\n\t}\n\n\tpublic async listRecentConnectorTargets(\n\t\tcontext: MessageConnectorQueryContext,\n\t): Promise<MessageConnectorTarget[]> {\n\t\tconst accountId = this.resolveAccountIdFromTarget(context.target, context);\n\t\tconst client = this.getClient(accountId);\n\t\tconst targets: MessageConnectorTarget[] = [];\n\t\tconst currentRoom =\n\t\t\tcontext.roomId && typeof context.runtime.getRoom === \"function\"\n\t\t\t\t? await context.runtime.getRoom(context.roomId)\n\t\t\t\t: null;\n\t\tconst currentChannelId =\n\t\t\tcontext.target?.channelId ??\n\t\t\t(currentRoom?.source === \"discord\" ? currentRoom.channelId : undefined);\n\n\t\tif (currentChannelId && client) {\n\t\t\ttry {\n\t\t\t\tconst channel = await client.channels.fetch(currentChannelId);\n\t\t\t\tif (channel) {\n\t\t\t\t\tconst target = this.buildConnectorChannelTarget(\n\t\t\t\t\t\tchannel,\n\t\t\t\t\t\t0.95,\n\t\t\t\t\t\taccountId,\n\t\t\t\t\t);\n\t\t\t\t\tif (target) {\n\t\t\t\t\t\ttargets.push(target);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Ignore stale current-channel hints.\n\t\t\t}\n\t\t}\n\n\t\ttargets.push(...(await this.listConnectorRooms(context)));\n\t\treturn this.dedupeConnectorTargets(targets).slice(0, 25);\n\t}\n\n\tpublic async getConnectorChatContext(\n\t\ttarget: TargetInfo,\n\t\tcontext: MessageConnectorQueryContext,\n\t): Promise<MessageConnectorChatContext | null> {\n\t\tconst accountId = this.resolveAccountIdFromTarget(target, context);\n\t\tconst client = this.getClient(accountId);\n\t\tif (!client) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst room =\n\t\t\ttarget.roomId && typeof context.runtime.getRoom === \"function\"\n\t\t\t\t? await context.runtime.getRoom(target.roomId)\n\t\t\t\t: null;\n\t\tconst channelId = target.channelId ?? room?.channelId;\n\t\tif (!channelId) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst channel = await client.channels.fetch(channelId);\n\t\tif (!channel || !isDiscordTextTarget(channel)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst channelRecord = channel as Channel & {\n\t\t\tname?: string;\n\t\t\ttopic?: string | null;\n\t\t\tguild?: Guild;\n\t\t\tmessages?: {\n\t\t\t\tfetch: (options: {\n\t\t\t\t\tlimit: number;\n\t\t\t\t}) => Promise<Collection<string, Message>>;\n\t\t\t};\n\t\t};\n\t\tconst recentMessages: MessageConnectorChatContext[\"recentMessages\"] = [];\n\t\tif (channelRecord.messages?.fetch) {\n\t\t\ttry {\n\t\t\t\tconst fetched = await channelRecord.messages.fetch({ limit: 10 });\n\t\t\t\tfor (const message of Array.from(fetched.values()).reverse()) {\n\t\t\t\t\tif (!message.content?.trim()) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\trecentMessages.push({\n\t\t\t\t\t\tentityId: this.resolveDiscordEntityId(message.author.id),\n\t\t\t\t\t\tname:\n\t\t\t\t\t\t\tmessage.member?.displayName ||\n\t\t\t\t\t\t\tmessage.author.globalName ||\n\t\t\t\t\t\t\tmessage.author.username,\n\t\t\t\t\t\ttext: message.content,\n\t\t\t\t\t\ttimestamp: message.createdTimestamp,\n\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\taccountId,\n\t\t\t\t\t\t\tdiscordMessageId: message.id,\n\t\t\t\t\t\t\tdiscordUserId: message.author.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\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,\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 connector chat context history fetch failed\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst label =\n\t\t\ttypeof channelRecord.name === \"string\" && channelRecord.name.length > 0\n\t\t\t\t? `#${channelRecord.name}`\n\t\t\t\t: channelId;\n\t\treturn {\n\t\t\ttarget: {\n\t\t\t\tsource: \"discord\",\n\t\t\t\taccountId,\n\t\t\t\troomId: target.roomId ?? room?.id,\n\t\t\t\tchannelId,\n\t\t\t\tserverId: target.serverId ?? channelRecord.guild?.id,\n\t\t\t\tthreadId: target.threadId,\n\t\t\t} as TargetInfo,\n\t\t\tlabel,\n\t\t\tsummary:\n\t\t\t\tchannelRecord.topic ||\n\t\t\t\t(channelRecord.guild?.name\n\t\t\t\t\t? `Discord channel in ${channelRecord.guild.name}`\n\t\t\t\t\t: undefined),\n\t\t\trecentMessages,\n\t\t\tmetadata: {\n\t\t\t\taccountId,\n\t\t\t\tdiscordChannelId: channelId,\n\t\t\t\tdiscordGuildId: channelRecord.guild?.id,\n\t\t\t\tdiscordGuildName: channelRecord.guild?.name,\n\t\t\t},\n\t\t};\n\t}\n\n\tpublic async getConnectorUserContext(\n\t\tentityId: UUID | string,\n\t\tcontext: MessageConnectorQueryContext,\n\t): Promise<MessageConnectorUserContext | null> {\n\t\tconst accountId = this.resolveAccountIdFromTarget(context.target, context);\n\t\tconst client = this.getClient(accountId);\n\t\tif (!client) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst discordUserId = await this.resolveDiscordTargetUserId(\n\t\t\tString(entityId),\n\t\t);\n\t\tif (!discordUserId) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst user = await client.users.fetch(discordUserId);\n\t\tif (!user) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn {\n\t\t\tentityId,\n\t\t\tlabel: user.globalName || user.username || user.id,\n\t\t\taliases: [user.username, user.globalName, user.tag].filter(\n\t\t\t\t(value): value is string => Boolean(value),\n\t\t\t),\n\t\t\thandles: { discord: user.id },\n\t\t\tmetadata: {\n\t\t\t\taccountId,\n\t\t\t\tdiscordUserId: user.id,\n\t\t\t\tdiscordUsername: user.username,\n\t\t\t\tdiscordGlobalName: user.globalName,\n\t\t\t\trequestRoomId: context.roomId,\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate async resolveConnectorTextChannel(\n\t\ttarget?: TargetInfo | null,\n\t\tfallback?: ConnectorFetchMessagesParams | ConnectorChannelMutationParams,\n\t): Promise<\n\t\tChannel & {\n\t\t\tid: string;\n\t\t\tname?: string;\n\t\t\tguild?: Guild;\n\t\t\tmessages: TextChannel[\"messages\"];\n\t\t\tpermissionsFor?: TextChannel[\"permissionsFor\"];\n\t\t}\n\t> {\n\t\tconst accountId = this.resolveAccountIdFromTarget(target, fallback);\n\t\tconst client = this.getClient(accountId);\n\t\tif (!client) {\n\t\t\tthrow new Error(\"Discord client is not initialized.\");\n\t\t}\n\n\t\tlet channelId =\n\t\t\ttarget?.channelId ??\n\t\t\t(fallback && \"channelId\" in fallback ? fallback.channelId : undefined);\n\t\tconst roomId =\n\t\t\ttarget?.roomId ??\n\t\t\t(fallback && \"roomId\" in fallback ? fallback.roomId : undefined);\n\n\t\tif (roomId && !channelId) {\n\t\t\tconst room = await this.runtime.getRoom(roomId);\n\t\t\tchannelId = room?.channelId;\n\t\t}\n\n\t\tif (!channelId && fallback && \"alias\" in fallback && fallback.alias) {\n\t\t\tconst normalizedAlias = normalizeDiscordConnectorQuery(fallback.alias);\n\t\t\tfor (const guild of client.guilds.cache.values()) {\n\t\t\t\tconst found = guild.channels.cache.find((channel) => {\n\t\t\t\t\tif (!channel || !isDiscordTextTarget(channel)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tconst channelRecord = channel as Channel & { name?: string };\n\t\t\t\t\treturn (\n\t\t\t\t\t\tchannel.id === normalizedAlias ||\n\t\t\t\t\t\tchannelRecord.name?.toLowerCase() === normalizedAlias\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t\tif (found) {\n\t\t\t\t\tchannelId = found.id;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!channelId) {\n\t\t\tthrow new Error(\"Discord connector operation requires a channel target.\");\n\t\t}\n\n\t\tconst channel = await client.channels.fetch(channelId);\n\t\tif (!channel || !isDiscordTextTarget(channel) || !(\"messages\" in channel)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Discord channel ${channelId} is not a text message channel.`,\n\t\t\t);\n\t\t}\n\t\treturn channel as Channel & {\n\t\t\tid: string;\n\t\t\tname?: string;\n\t\t\tguild?: Guild;\n\t\t\tmessages: TextChannel[\"messages\"];\n\t\t\tpermissionsFor?: TextChannel[\"permissionsFor\"];\n\t\t};\n\t}\n\n\tprivate async fetchConnectorDiscordMessage(\n\t\tparams: ConnectorMessageMutationParams,\n\t): Promise<Message> {\n\t\tconst messageId = params.messageId;\n\t\tif (!messageId) {\n\t\t\tthrow new Error(\"Discord message operation requires messageId.\");\n\t\t}\n\t\tconst channel = await this.resolveConnectorTextChannel(\n\t\t\tparams.target,\n\t\t\tparams,\n\t\t);\n\t\treturn (await channel.messages.fetch(messageId)) as Message;\n\t}\n\n\tpublic async listConnectorServers(\n\t\tcontext: MessageConnectorQueryContext,\n\t): Promise<World[]> {\n\t\tconst accountId = this.resolveAccountIdFromTarget(context.target, context);\n\t\tconst client = this.getClient(accountId);\n\t\tif (!client) {\n\t\t\treturn [];\n\t\t}\n\t\treturn Array.from(client.guilds.cache.values()).map((guild) => ({\n\t\t\tid: createUniqueUuid(this.runtime, guild.id),\n\t\t\tagentId: this.runtime.agentId,\n\t\t\tname: guild.name,\n\t\t\tmessageServerId: stringToUuid(guild.id),\n\t\t\tmetadata: {\n\t\t\t\tsource: \"discord\",\n\t\t\t\taccountId,\n\t\t\t\tdiscordGuildId: guild.id,\n\t\t\t\tmemberCount: guild.memberCount,\n\t\t\t},\n\t\t}));\n\t}\n\n\tpublic async fetchConnectorMessages(\n\t\t_context: MessageConnectorQueryContext,\n\t\tparams: ConnectorFetchMessagesParams,\n\t): Promise<Memory[]> {\n\t\tconst accountId = this.resolveAccountIdFromTarget(params.target, params);\n\t\tconst channel = await this.resolveConnectorTextChannel(\n\t\t\tparams.target,\n\t\t\tparams,\n\t\t);\n\t\tconst limit = Number.isFinite(params.limit)\n\t\t\t? Math.max(1, Math.min(Number(params.limit), 100))\n\t\t\t: 25;\n\t\tconst fetchParams: { limit: number; before?: string; after?: string } = {\n\t\t\tlimit,\n\t\t};\n\t\tif (params.before ?? params.cursor) {\n\t\t\tfetchParams.before = params.before ?? params.cursor;\n\t\t}\n\t\tif (params.after) {\n\t\t\tfetchParams.after = params.after;\n\t\t}\n\n\t\tconst fetched = await channel.messages.fetch(fetchParams);\n\t\tconst memories: Memory[] = [];\n\t\tfor (const discordMessage of fetched.values()) {\n\t\t\tconst memory = await this.buildMemoryFromMessage(\n\t\t\t\tdiscordMessage as Message,\n\t\t\t\t{ accountId },\n\t\t\t);\n\t\t\tif (memory) {\n\t\t\t\tmemories.push(memory);\n\t\t\t}\n\t\t}\n\t\treturn memories.sort(\n\t\t\t(left, right) =>\n\t\t\t\tNumber(right.createdAt ?? 0) - Number(left.createdAt ?? 0),\n\t\t);\n\t}\n\n\tpublic async searchConnectorMessages(\n\t\tcontext: MessageConnectorQueryContext,\n\t\tparams: ConnectorSearchMessagesParams,\n\t): Promise<Memory[]> {\n\t\tconst query = params.query?.trim().toLowerCase();\n\t\tif (!query) {\n\t\t\treturn [];\n\t\t}\n\t\tconst author = params.author?.trim().toLowerCase();\n\t\tconst memories = await this.fetchConnectorMessages(context, {\n\t\t\t...params,\n\t\t\tlimit: Math.max(params.limit ?? 100, 100),\n\t\t});\n\t\treturn memories\n\t\t\t.filter((memory) => {\n\t\t\t\tconst text = String(memory.content?.text ?? \"\").toLowerCase();\n\t\t\t\tconst name = String(memory.content?.name ?? \"\").toLowerCase();\n\t\t\t\tconst metadata = memory.metadata as Record<string, unknown> | undefined;\n\t\t\t\tconst sender = metadata?.sender as Record<string, unknown> | undefined;\n\t\t\t\tconst username = String(sender?.username ?? \"\").toLowerCase();\n\t\t\t\tconst matchesQuery = text.includes(query) || name.includes(query);\n\t\t\t\tconst matchesAuthor =\n\t\t\t\t\t!author || name.includes(author) || username.includes(author);\n\t\t\t\treturn matchesQuery && matchesAuthor;\n\t\t\t})\n\t\t\t.slice(0, params.limit ?? 25);\n\t}\n\n\tpublic async reactConnectorMessage(\n\t\t_runtime: IAgentRuntime,\n\t\tparams: ConnectorMessageMutationParams,\n\t): Promise<void> {\n\t\tconst accountId = this.resolveAccountIdFromTarget(params.target, params);\n\t\tconst state = this.requireAccountState(accountId);\n\t\tconst emoji = params.emoji?.trim();\n\t\tif (!emoji) {\n\t\t\tthrow new Error(\"Discord reaction requires emoji.\");\n\t\t}\n\t\tconst targetMessage = await this.fetchConnectorDiscordMessage(params);\n\t\tif (params.remove) {\n\t\t\tconst clientUserId = state.client?.user?.id;\n\t\t\tconst reaction = targetMessage.reactions.cache.find(\n\t\t\t\t(candidate) =>\n\t\t\t\t\tcandidate.emoji.name === emoji ||\n\t\t\t\t\tcandidate.emoji.toString() === emoji,\n\t\t\t);\n\t\t\tif (reaction && clientUserId) {\n\t\t\t\tawait reaction.users.remove(clientUserId);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tawait targetMessage.react(emoji);\n\t}\n\n\tpublic async editConnectorMessage(\n\t\t_runtime: IAgentRuntime,\n\t\tparams: ConnectorMessageMutationParams,\n\t): Promise<Memory> {\n\t\tconst accountId = this.resolveAccountIdFromTarget(params.target, params);\n\t\tconst state = this.requireAccountState(accountId);\n\t\tconst text = params.content?.text ?? params.text;\n\t\tif (!text?.trim()) {\n\t\t\tthrow new Error(\"Discord edit requires non-empty text.\");\n\t\t}\n\t\tconst targetMessage = await this.fetchConnectorDiscordMessage(params);\n\t\tif (targetMessage.author.id !== state.client?.user?.id) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Discord connector can only edit the bot's own messages.\",\n\t\t\t);\n\t\t}\n\t\tconst edited = await targetMessage.edit(text);\n\t\tconst memory = await this.buildMemoryFromMessage(edited as Message, {\n\t\t\taccountId,\n\t\t});\n\t\tif (!memory) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Discord edit succeeded but could not build updated memory.\",\n\t\t\t);\n\t\t}\n\t\treturn memory;\n\t}\n\n\tpublic async deleteConnectorMessage(\n\t\t_runtime: IAgentRuntime,\n\t\tparams: ConnectorMessageMutationParams,\n\t): Promise<void> {\n\t\tconst targetMessage = await this.fetchConnectorDiscordMessage(params);\n\t\tawait targetMessage.delete();\n\t}\n\n\tpublic async pinConnectorMessage(\n\t\t_runtime: IAgentRuntime,\n\t\tparams: ConnectorMessageMutationParams,\n\t): Promise<void> {\n\t\tconst targetMessage = await this.fetchConnectorDiscordMessage(params);\n\t\tif (params.pin === false) {\n\t\t\tawait targetMessage.unpin();\n\t\t\treturn;\n\t\t}\n\t\tawait targetMessage.pin();\n\t}\n\n\tpublic async joinConnectorChannel(\n\t\t_runtime: IAgentRuntime,\n\t\tparams: ConnectorChannelMutationParams,\n\t): Promise<Room | null> {\n\t\tconst accountId = this.resolveAccountIdFromTarget(params.target, params);\n\t\tconst channel = await this.resolveConnectorTextChannel(\n\t\t\tparams.target,\n\t\t\tparams,\n\t\t);\n\t\tthis.addAllowedChannel(channel.id, accountId);\n\n\t\tconst guild = \"guild\" in channel ? channel.guild : null;\n\t\tconst roomId = createUniqueUuid(this.runtime, channel.id);\n\t\tconst worldId = createUniqueUuid(this.runtime, guild?.id ?? channel.id);\n\t\tconst room: Room = {\n\t\t\tid: roomId,\n\t\t\tagentId: this.runtime.agentId,\n\t\t\tname: channel.name ?? channel.id,\n\t\t\tsource: \"discord\",\n\t\t\ttype: await this.getChannelType(channel as Channel),\n\t\t\tchannelId: channel.id,\n\t\t\tworldId,\n\t\t\tserverId: guild?.id,\n\t\t\tmetadata: {\n\t\t\t\taccountId,\n\t\t\t\tdiscordChannelId: channel.id,\n\t\t\t\tdiscordGuildId: guild?.id,\n\t\t\t\tdiscordGuildName: guild?.name,\n\t\t\t},\n\t\t};\n\n\t\tconst runtimeWithEnsure = this.runtime as typeof this.runtime & {\n\t\t\tensureRoomExists?: (room: Room) => Promise<void>;\n\t\t\tcreateRoom?: (room: Room) => Promise<UUID | undefined>;\n\t\t};\n\t\tif (typeof runtimeWithEnsure.ensureRoomExists === \"function\") {\n\t\t\tawait runtimeWithEnsure.ensureRoomExists(room);\n\t\t} else if (typeof runtimeWithEnsure.createRoom === \"function\") {\n\t\t\tconst existing = await this.runtime.getRoom(roomId);\n\t\t\tif (!existing) {\n\t\t\t\tawait runtimeWithEnsure.createRoom(room);\n\t\t\t}\n\t\t}\n\n\t\treturn (await this.runtime.getRoom(roomId)) ?? room;\n\t}\n\n\tpublic async leaveConnectorChannel(\n\t\t_runtime: IAgentRuntime,\n\t\tparams: ConnectorChannelMutationParams,\n\t): Promise<void> {\n\t\tconst accountId = this.resolveAccountIdFromTarget(params.target, params);\n\t\tconst channel = await this.resolveConnectorTextChannel(\n\t\t\tparams.target,\n\t\t\tparams,\n\t\t);\n\t\tthis.removeAllowedChannel(channel.id, accountId);\n\t}\n\n\tpublic async getConnectorUser(\n\t\t_runtime: IAgentRuntime,\n\t\tparams: ConnectorUserLookupParams,\n\t): Promise<unknown> {\n\t\tconst accountId = normalizeAccountId(\n\t\t\tparams.accountId ?? this.defaultAccountId,\n\t\t);\n\t\tconst client = this.getClient(accountId);\n\t\tif (!client) {\n\t\t\treturn null;\n\t\t}\n\t\tconst lookup =\n\t\t\tparams.userId ?? params.handle ?? params.username ?? params.query;\n\t\tif (!lookup) {\n\t\t\treturn null;\n\t\t}\n\n\t\tlet user: User | null = null;\n\t\tif (DISCORD_SNOWFLAKE_PATTERN.test(lookup)) {\n\t\t\tuser = await client.users.fetch(lookup).catch(() => null);\n\t\t}\n\t\tif (!user) {\n\t\t\tconst normalized = normalizeDiscordConnectorQuery(lookup);\n\t\t\tfor (const guild of client.guilds.cache.values()) {\n\t\t\t\tconst cached = guild.members.cache.find((member) =>\n\t\t\t\t\t[\n\t\t\t\t\t\tmember.id,\n\t\t\t\t\t\tmember.displayName,\n\t\t\t\t\t\tmember.user.username,\n\t\t\t\t\t\tmember.user.globalName,\n\t\t\t\t\t\tmember.user.tag,\n\t\t\t\t\t]\n\t\t\t\t\t\t.filter((value): value is string => Boolean(value))\n\t\t\t\t\t\t.some((value) =>\n\t\t\t\t\t\t\tnormalizeDiscordConnectorQuery(value).includes(normalized),\n\t\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tif (cached) {\n\t\t\t\t\tuser = cached.user;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!user) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn {\n\t\t\tid: this.resolveDiscordEntityId(user.id),\n\t\t\tagentId: this.runtime.agentId,\n\t\t\tnames: [user.globalName, user.username, user.tag].filter(\n\t\t\t\t(value): value is string => Boolean(value),\n\t\t\t),\n\t\t\tmetadata: {\n\t\t\t\tsource: \"discord\",\n\t\t\t\taccountId,\n\t\t\t\tdiscord: {\n\t\t\t\t\taccountId,\n\t\t\t\t\tid: user.id,\n\t\t\t\t\tuserId: user.id,\n\t\t\t\t\tusername: user.username,\n\t\t\t\t\tglobalName: user.globalName,\n\t\t\t\t\ttag: user.tag,\n\t\t\t\t},\n\t\t\t},\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 setupEventListenersForAccount(state: DiscordAccountClientState) {\n\t\tif (!state.client) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { messageDebouncer, channelDebouncer } = setupDiscordEventListeners(\n\t\t\tthis.createAccountServiceFacade(state),\n\t\t);\n\n\t\tstate.messageDebouncer = messageDebouncer;\n\t\tstate.channelDebouncer = channelDebouncer;\n\t\tif (state.accountId === this.defaultAccountId) {\n\t\t\tthis.messageDebouncer = messageDebouncer;\n\t\t\tthis.channelDebouncer = channelDebouncer;\n\t\t}\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 onReadyForAccount(accountId: string, readyClient: any) {\n\t\tconst state = this.requireAccountState(accountId);\n\t\treturn onReadyExtracted(\n\t\t\tthis.createAccountServiceFacade(state),\n\t\t\treadyClient,\n\t\t);\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\tif (typeof runtime.registerMessageConnector === \"function\") {\n\t\t\t\tconst accountIds =\n\t\t\t\t\ttypeof serviceInstance.getAccountIds === \"function\"\n\t\t\t\t\t\t? serviceInstance.getAccountIds()\n\t\t\t\t\t\t: [];\n\t\t\t\tconst defaultAccountId =\n\t\t\t\t\ttypeof serviceInstance.getDefaultAccountId === \"function\"\n\t\t\t\t\t\t? serviceInstance.getDefaultAccountId()\n\t\t\t\t\t\t: DEFAULT_ACCOUNT_ID;\n\t\t\t\tconst registerConnector = (\n\t\t\t\t\taccountId: string | undefined,\n\t\t\t\t\tlegacy = false,\n\t\t\t\t) => {\n\t\t\t\t\tconst scopedTarget = (target: TargetInfo): TargetInfo =>\n\t\t\t\t\t\t({\n\t\t\t\t\t\t\t...target,\n\t\t\t\t\t\t\taccountId: accountIdFromRecord(target) ?? accountId,\n\t\t\t\t\t\t}) as TargetInfo;\n\t\t\t\t\tconst scopedContext = (\n\t\t\t\t\t\tcontext: MessageConnectorQueryContext,\n\t\t\t\t\t): MessageConnectorQueryContext =>\n\t\t\t\t\t\t({\n\t\t\t\t\t\t\t...context,\n\t\t\t\t\t\t\taccountId: accountIdFromRecord(context) ?? accountId,\n\t\t\t\t\t\t\ttarget: context.target ? scopedTarget(context.target) : undefined,\n\t\t\t\t\t\t}) as MessageConnectorQueryContext;\n\t\t\t\t\tconst scopedFetchParams = <\n\t\t\t\t\t\tT extends\n\t\t\t\t\t\t\t| ConnectorFetchMessagesParams\n\t\t\t\t\t\t\t| ConnectorSearchMessagesParams\n\t\t\t\t\t\t\t| ConnectorMessageMutationParams\n\t\t\t\t\t\t\t| ConnectorChannelMutationParams\n\t\t\t\t\t\t\t| ConnectorUserLookupParams,\n\t\t\t\t\t>(\n\t\t\t\t\t\tparams: T,\n\t\t\t\t\t): T => ({\n\t\t\t\t\t\t...params,\n\t\t\t\t\t\taccountId: params.accountId ?? accountId,\n\t\t\t\t\t\t...(\"target\" in params && params.target\n\t\t\t\t\t\t\t? { target: scopedTarget(params.target) }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t});\n\t\t\t\t\tconst label = accountId\n\t\t\t\t\t\t? `Discord (${serviceInstance.getAccountLabel(accountId)})`\n\t\t\t\t\t\t: \"Discord\";\n\t\t\t\t\tconst registration: ExtendedMessageConnectorRegistration = {\n\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t...(accountId ? { accountId } : {}),\n\t\t\t\t\t\t...(accountId\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\taccount: {\n\t\t\t\t\t\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\t\t\t\t\t\taccountId,\n\t\t\t\t\t\t\t\t\t\tlabel: serviceInstance.getAccountLabel(accountId),\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\tlabel,\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Discord connector for sending, reading, searching, reacting to, editing, deleting, pinning, joining, and leaving messages/channels.\",\n\t\t\t\t\t\tcapabilities: [...DISCORD_CONNECTOR_CAPABILITIES],\n\t\t\t\t\t\tsupportedTargetKinds: [\"channel\", \"thread\", \"user\"],\n\t\t\t\t\t\tcontexts: [...DISCORD_CONNECTOR_CONTEXTS],\n\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\tservice: DISCORD_SERVICE_NAME,\n\t\t\t\t\t\t\tsupportsAttachments: true,\n\t\t\t\t\t\t\tmaxMessageLength: MAX_MESSAGE_LENGTH,\n\t\t\t\t\t\t\tdefaultAccountId,\n\t\t\t\t\t\t\t...(accountId ? { accountId } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tresolveTargets: (query, context) =>\n\t\t\t\t\t\t\tserviceInstance.resolveConnectorTargets(\n\t\t\t\t\t\t\t\tquery,\n\t\t\t\t\t\t\t\tscopedContext(context),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\tlistRecentTargets: (context) =>\n\t\t\t\t\t\t\tserviceInstance.listRecentConnectorTargets(\n\t\t\t\t\t\t\t\tscopedContext(context),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\tlistRooms: (context) =>\n\t\t\t\t\t\t\tserviceInstance.listConnectorRooms(scopedContext(context)),\n\t\t\t\t\t\tlistServers: (context) =>\n\t\t\t\t\t\t\tserviceInstance.listConnectorServers(scopedContext(context)),\n\t\t\t\t\t\tfetchMessages: (context, params) =>\n\t\t\t\t\t\t\tserviceInstance.fetchConnectorMessages(\n\t\t\t\t\t\t\t\tscopedContext(context),\n\t\t\t\t\t\t\t\tscopedFetchParams(params),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\tsearchMessages: (context, params) =>\n\t\t\t\t\t\t\tserviceInstance.searchConnectorMessages(\n\t\t\t\t\t\t\t\tscopedContext(context),\n\t\t\t\t\t\t\t\tscopedFetchParams(params),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\treactHandler: (runtime, params) =>\n\t\t\t\t\t\t\tserviceInstance.reactConnectorMessage(\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tscopedFetchParams(params),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\teditHandler: (runtime, params) =>\n\t\t\t\t\t\t\tserviceInstance.editConnectorMessage(\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tscopedFetchParams(params),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\tdeleteHandler: (runtime, params) =>\n\t\t\t\t\t\t\tserviceInstance.deleteConnectorMessage(\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tscopedFetchParams(params),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\tpinHandler: (runtime, params) =>\n\t\t\t\t\t\t\tserviceInstance.pinConnectorMessage(\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tscopedFetchParams(params),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\tjoinHandler: (runtime, params) =>\n\t\t\t\t\t\t\tserviceInstance.joinConnectorChannel(\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tscopedFetchParams(params),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\tleaveHandler: (runtime, params) =>\n\t\t\t\t\t\t\tserviceInstance.leaveConnectorChannel(\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tscopedFetchParams(params),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\tgetUser: (runtime, params) =>\n\t\t\t\t\t\t\tserviceInstance.getConnectorUser(\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tscopedFetchParams(params),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\tgetChatContext: (target, context) =>\n\t\t\t\t\t\t\tserviceInstance.getConnectorChatContext(\n\t\t\t\t\t\t\t\tscopedTarget(target),\n\t\t\t\t\t\t\t\tscopedContext(context),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\tgetUserContext: (entityId, context) =>\n\t\t\t\t\t\t\tserviceInstance.getConnectorUserContext(\n\t\t\t\t\t\t\t\tentityId,\n\t\t\t\t\t\t\t\tscopedContext(context),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\tsendHandler: (runtime, target, content) =>\n\t\t\t\t\t\t\tserviceInstance.handleSendMessage(\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tscopedTarget(target),\n\t\t\t\t\t\t\t\tcontent,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t};\n\t\t\t\t\truntime.registerMessageConnector(registration);\n\t\t\t\t\truntime.logger.info(\n\t\t\t\t\t\taccountId && !legacy\n\t\t\t\t\t\t\t? `Registered Discord message connector for account ${accountId}`\n\t\t\t\t\t\t\t: \"Registered Discord message connector\",\n\t\t\t\t\t);\n\t\t\t\t};\n\n\t\t\t\tregisterConnector(undefined, true);\n\t\t\t\tfor (const accountId of accountIds) {\n\t\t\t\t\tregisterConnector(accountId);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst sendHandler =\n\t\t\t\t\tserviceInstance.handleSendMessage.bind(serviceInstance);\n\t\t\t\truntime.registerSendHandler(\"discord\", sendHandler);\n\t\t\t\truntime.logger.info(\"Registered send handler\");\n\t\t\t}\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\taccountId?: string | null,\n\t): Promise<Array<{ id: string; username: string; displayName: string }>> {\n\t\tconst state = this.getAccountState(accountId);\n\t\tconst client = state?.client ?? null;\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\taccountId: state?.accountId ?? this.defaultAccountId,\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 = client\n\t\t\t\t? ((await 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 = 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(\n\t\tchannelId: string,\n\t\taccountId?: string | null,\n\t): Promise<string | null> {\n\t\ttry {\n\t\t\tconst client = this.getClient(accountId);\n\t\t\tconst channel = client ? await client.channels.fetch(channelId) : 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(\n\t\tchannelId: string,\n\t\taccountId?: string | null,\n\t): boolean {\n\t\tconst state = this.getAccountState(accountId);\n\t\tconst allowedChannelIds =\n\t\t\tstate?.allowedChannelIds ??\n\t\t\t(accountId ? undefined : this.allowedChannelIds);\n\t\tconst dynamicChannelIds =\n\t\t\tstate?.dynamicChannelIds ??\n\t\t\t(accountId ? new Set<string>() : this.dynamicChannelIds);\n\t\tif (!allowedChannelIds) {\n\t\t\treturn true;\n\t\t}\n\t\treturn (\n\t\t\tallowedChannelIds.includes(channelId) || 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(\n\t\tchannelId: string,\n\t\taccountId?: string | null,\n\t): boolean {\n\t\tconst state = this.getAccountState(accountId);\n\t\tconst client = state?.client ?? this.client;\n\t\tif (!client?.channels.cache.has(channelId)) {\n\t\t\treturn false;\n\t\t}\n\t\t(state?.dynamicChannelIds ?? this.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(\n\t\tchannelId: string,\n\t\taccountId?: string | null,\n\t): boolean {\n\t\tconst state = this.getAccountState(accountId);\n\t\tconst allowedChannelIds =\n\t\t\tstate?.allowedChannelIds ?? this.allowedChannelIds;\n\t\tconst dynamicChannelIds =\n\t\t\tstate?.dynamicChannelIds ?? this.dynamicChannelIds;\n\t\tif (allowedChannelIds?.includes(channelId)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn dynamicChannelIds.delete(channelId);\n\t}\n\n\t/**\n\t * Gets the list of all allowed channels (env + dynamic).\n\t */\n\tpublic getAllowedChannels(accountId?: string | null): string[] {\n\t\tconst state = this.getAccountState(accountId);\n\t\tconst envChannels =\n\t\t\tstate?.allowedChannelIds ?? this.allowedChannelIds ?? [];\n\t\tconst dynamicChannels = Array.from(\n\t\t\tstate?.dynamicChannelIds ?? this.dynamicChannelIds,\n\t\t);\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\tconst state = this.getAccountState(options.accountId);\n\t\treturn fetchChannelHistoryExtracted(\n\t\t\tthis.createAccountServiceFacade(state),\n\t\t\tchannelId,\n\t\t\toptions,\n\t\t);\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\taccountId?: string;\n\t\t},\n\t): Promise<Memory | null> {\n\t\t// Always stamp the connector accountId on inbound memory. Explicit\n\t\t// per-call overrides win for legacy callers that already supply one.\n\t\tconst merged = {\n\t\t\t...options,\n\t\t\taccountId: options?.accountId ?? this.accountId,\n\t\t};\n\t\treturn buildMemoryFromMessageExtracted(\n\t\t\tthis.createAccountServiceFacade(this.getAccountState(merged.accountId)),\n\t\t\tmessage,\n\t\t\tmerged,\n\t\t);\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 this.handleReactionAddForAccount(\n\t\t\tthis.defaultAccountId,\n\t\t\treaction,\n\t\t\tuser,\n\t\t);\n\t}\n\n\tprivate async handleReactionAddForAccount(\n\t\taccountId: string,\n\t\treaction: MessageReaction | PartialMessageReaction,\n\t\tuser: User | PartialUser,\n\t): Promise<void> {\n\t\tconst state = this.requireAccountState(accountId);\n\t\tawait handleReactionAddExtracted(\n\t\t\tthis.createAccountServiceFacade(state),\n\t\t\treaction,\n\t\t\tuser,\n\t\t);\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 this.handleReactionRemoveForAccount(\n\t\t\tthis.defaultAccountId,\n\t\t\treaction,\n\t\t\tuser,\n\t\t);\n\t}\n\n\tprivate async handleReactionRemoveForAccount(\n\t\taccountId: string,\n\t\treaction: MessageReaction | PartialMessageReaction,\n\t\tuser: User | PartialUser,\n\t): Promise<void> {\n\t\tconst state = this.requireAccountState(accountId);\n\t\tawait handleReactionRemoveExtracted(\n\t\t\tthis.createAccountServiceFacade(state),\n\t\t\treaction,\n\t\t\tuser,\n\t\t);\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 this.handleGuildCreateForAccount(this.defaultAccountId, guild);\n\t}\n\n\tprivate async handleGuildCreateForAccount(\n\t\taccountId: string,\n\t\tguild: Guild,\n\t): Promise<void> {\n\t\tawait handleGuildCreateExtracted(\n\t\t\tthis.createAccountServiceFacade(this.getAccountState(accountId)),\n\t\t\tguild,\n\t\t);\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 this.handleInteractionCreateForAccount(\n\t\t\tthis.defaultAccountId,\n\t\t\tinteraction,\n\t\t);\n\t}\n\n\tprivate async handleInteractionCreateForAccount(\n\t\taccountId: string,\n\t\tinteraction: Interaction,\n\t): Promise<void> {\n\t\tconst state = this.requireAccountState(accountId);\n\t\tawait handleInteractionCreateExtracted(\n\t\t\tthis.createAccountServiceFacade(state),\n\t\t\tinteraction,\n\t\t);\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\tawait this.handleGuildMemberAddForAccount(this.defaultAccountId, member);\n\t}\n\n\tprivate async handleGuildMemberAddForAccount(\n\t\taccountId: string,\n\t\tmember: GuildMember,\n\t): 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\taccountId,\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\tconst states = this.accountPool.list();\n\t\tfor (const state of states) {\n\t\t\tstate.messageDebouncer?.destroy();\n\t\t\tstate.channelDebouncer?.destroy();\n\t\t\tstate.messageDebouncer = undefined;\n\t\t\tstate.channelDebouncer = undefined;\n\t\t}\n\t\tthis.messageDebouncer = undefined;\n\t\tthis.channelDebouncer = undefined;\n\n\t\tthis.userSelections.clear();\n\n\t\tfor (const state of states) {\n\t\t\ttry {\n\t\t\t\tstate.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\tfor (const state of states) {\n\t\t\tconst client = state.client;\n\t\t\tif (!client) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait client.destroy();\n\t\t\t\tthis.runtime.logger.info(\n\t\t\t\t\t`Discord client destroyed for account ${state.accountId}`,\n\t\t\t\t);\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\tstate.client = null;\n\t\t\t}\n\t\t}\n\n\t\tthis.accountPool.clear();\n\t\tthis.clientReadyPromise = null;\n\t\tthis.messageManager = undefined;\n\t\tthis.voiceManager = undefined;\n\t\tthis.client = null;\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",
16
- "import type { Client as DiscordJsClient } from \"discord.js\";\nimport {\n\tDEFAULT_ACCOUNT_ID,\n\tnormalizeAccountId,\n\ttype ResolvedDiscordAccount,\n} from \"./accounts\";\nimport type { ChannelDebouncer, MessageDebouncer } from \"./debouncer\";\nimport type { MessageManager } from \"./messages\";\nimport type { DiscordSettings } from \"./types\";\nimport type { VoiceManager } from \"./voice\";\n\nexport interface DiscordAccountClientState {\n\taccountId: string;\n\taccount: ResolvedDiscordAccount;\n\tclient: DiscordJsClient | null;\n\tsettings: DiscordSettings;\n\tallowedChannelIds?: string[];\n\tlistenChannelIds?: string[];\n\tdynamicChannelIds: Set<string>;\n\tclientReadyPromise: Promise<void> | null;\n\tloginFailed: boolean;\n\tmessageManager?: MessageManager;\n\tvoiceManager?: VoiceManager;\n\tmessageDebouncer?: MessageDebouncer;\n\tchannelDebouncer?: ChannelDebouncer;\n}\n\nexport class DiscordAccountClientPool {\n\tprivate defaultAccountId: string;\n\tprivate readonly clients = new Map<string, DiscordAccountClientState>();\n\n\tconstructor(defaultAccountId = DEFAULT_ACCOUNT_ID) {\n\t\tthis.defaultAccountId = normalizeAccountId(defaultAccountId);\n\t}\n\n\tsetDefaultAccountId(accountId: string): void {\n\t\tthis.defaultAccountId = normalizeAccountId(accountId);\n\t}\n\n\tgetDefaultAccountId(): string {\n\t\treturn this.defaultAccountId;\n\t}\n\n\tset(state: DiscordAccountClientState): void {\n\t\tconst accountId = normalizeAccountId(state.accountId);\n\t\tstate.accountId = accountId;\n\t\tthis.clients.set(accountId, state);\n\t}\n\n\tget(accountId?: string | null): DiscordAccountClientState | null {\n\t\tconst normalized = normalizeAccountId(accountId ?? this.defaultAccountId);\n\t\treturn this.clients.get(normalized) ?? null;\n\t}\n\n\tgetDefault(): DiscordAccountClientState | null {\n\t\treturn this.get(this.defaultAccountId) ?? this.list()[0] ?? null;\n\t}\n\n\tlist(): DiscordAccountClientState[] {\n\t\treturn Array.from(this.clients.values());\n\t}\n\n\tlistAccountIds(): string[] {\n\t\treturn this.list().map((state) => state.accountId);\n\t}\n\n\tclear(): void {\n\t\tthis.clients.clear();\n\t}\n}\n",
17
- "/**\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",
18
- "/**\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 Client as DiscordClient,\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\taccountId?: string;\n\taccountToken?: string;\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: DiscordClient): 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 accountId = service.accountId ?? \"default\";\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 UUID,\n\t\tmetadata: {\n\t\t\t...buildDiscordWorldMetadata(service.runtime, interaction.guild?.ownerId),\n\t\t\taccountId,\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\taccountId,\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\taccountId,\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\taccountId?: string;\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\taccountId,\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 accountId = service.accountId ?? \"default\";\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\taccountId,\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: DiscordClient<true>,\n): Promise<void> {\n\tconst accountId = service.accountId ?? \"default\";\n\tservice.runtime.logger.success(\n\t\t`Discord client ready for account ${accountId}`,\n\t);\n\tconst discordApiToken =\n\t\tservice.accountToken ?? 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\taccountId,\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\taccountId,\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\taccountId,\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",
19
- "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",
20
- "import { createHash } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { IAgentRuntime } from \"@elizaos/core\";\nimport { resolveStateDir, resolveUserPath } 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 resolveProfileSyncStatePath(\n\tenv: NodeJS.ProcessEnv = process.env,\n): string {\n\treturn path.join(resolveStateDir(env), \"cache\", PROFILE_SYNC_STATE_FILE);\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",
21
- "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\taccountId?: string;\n}\n\nexport interface DiscordMessageSentPayload extends MessagePayload {\n\toriginalMessages: Message[];\n\taccountId?: string;\n}\n\nexport interface DiscordReactionPayload extends MessagePayload {\n\toriginalReaction: MessageReaction;\n\tuser: User;\n\taccountId?: string;\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\taccountId?: string;\n}\n\nexport interface DiscordNotInChannelsPayload {\n\truntime: IAgentRuntime;\n\tmessage: Message;\n\tsource: string;\n\taccountId?: 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\taccountId?: string;\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?: Record<string, unknown>;\n\taccountId?: string;\n}\n\nexport interface IDiscordService {\n\taccountId?: string;\n\tclient: DiscordJsClient | null;\n\tcharacter: Character;\n\tdiscordSettings?: DiscordSettings;\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\taccountId?: string;\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",
22
- "/**\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 { isDiscordUserAddressed } from \"./addressing\";\nimport {\n\ttype ChannelDebouncer,\n\tcreateChannelDebouncer,\n\tcreateMessageDebouncer,\n\ttype MessageDebouncer,\n} from \"./debouncer\";\nimport {\n\tgetDiscordMessageCoalesceConfig,\n\tmakeCoalescedDiscordMessage,\n} from \"./message-coalesce\";\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 { recordDiscordChannelMessageSeen } from \"./staleness\";\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 DiscordServiceInternals`.\n */\nexport interface DiscordServiceInternals {\n\taccountId?: string;\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\tlistenChannelIds?: string[];\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 = service.listenChannelIds\n\t\t? service.listenChannelIds\n\t\t: Array.isArray(listenCidsRaw)\n\t\t\t? listenCidsRaw\n\t\t\t: listenCidsRaw &&\n\t\t\t\t\ttypeof listenCidsRaw === \"string\" &&\n\t\t\t\t\tlistenCidsRaw.trim()\n\t\t\t\t? listenCidsRaw\n\t\t\t\t\t\t.trim()\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: [];\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 accountId = service.accountId ?? \"default\";\n\tconst { listenCids, debounceMs, channelDebounceMs, responseCooldownMs } =\n\t\tparseEventListenerConfig(service);\n\tconst messageCoalesce = getDiscordMessageCoalesceConfig((key) =>\n\t\tservice.runtime.getSetting(key),\n\t);\n\tconst effectiveDebounceMs = messageCoalesce.enabled\n\t\t? messageCoalesce.windowMs\n\t\t: debounceMs;\n\tconst effectiveChannelDebounceMs = messageCoalesce.enabled\n\t\t? messageCoalesce.windowMs\n\t\t: channelDebounceMs;\n\n\t// ── Message debouncer ──────────────────────────────────────────────\n\tconst messageDebouncer = createMessageDebouncer(\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 anchor = messages[0];\n\t\t\tif (messageCoalesce.enabled) {\n\t\t\t\tconst combined = makeCoalescedDiscordMessage(\n\t\t\t\t\tmessages,\n\t\t\t\t\tanchor,\n\t\t\t\t\tmessageCoalesce,\n\t\t\t\t);\n\t\t\t\tif (messages.length > 1) {\n\t\t\t\t\tservice.runtime.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: service.runtime.agentId,\n\t\t\t\t\t\t\tchannelId: messages[0]?.channel?.id,\n\t\t\t\t\t\t\tmessageIds: messages.map((message) => message.id),\n\t\t\t\t\t\t\tcount: messages.length,\n\t\t\t\t\t\t\tpath: \"messageDebouncer\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Coalesced inbound Discord messages\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tvoid service.messageManager.handleMessage(combined as Message);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (messages.length === 1) {\n\t\t\t\tvoid service.messageManager.handleMessage(anchor);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst combinedText = messages\n\t\t\t\t.map((message) => message.content)\n\t\t\t\t.join(\"\\n\");\n\t\t\tconst combined = Object.create(anchor, {\n\t\t\t\tcontent: { value: combinedText, writable: true, enumerable: true },\n\t\t\t});\n\t\t\tvoid service.messageManager.handleMessage(combined as Message);\n\t\t},\n\t\teffectiveDebounceMs,\n\t\t{\n\t\t\tmaxBatch: messageCoalesce.enabled ? messageCoalesce.maxBatch : undefined,\n\t\t},\n\t);\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\tlet anchor: Message | undefined;\n\t\t\tconst botId = service.client?.user?.id;\n\t\t\tif (botId) {\n\t\t\t\tanchor = messages.find((message) =>\n\t\t\t\t\tisDiscordUserAddressed({\n\t\t\t\t\t\ttext: message.content,\n\t\t\t\t\t\tuserId: botId,\n\t\t\t\t\t\thasMessageReference: Boolean(message.reference?.messageId),\n\t\t\t\t\t\trepliedUserId: message.mentions?.repliedUser?.id,\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 (messageCoalesce.enabled) {\n\t\t\t\tconst combined = makeCoalescedDiscordMessage(\n\t\t\t\t\tmessages,\n\t\t\t\t\tanchor,\n\t\t\t\t\tmessageCoalesce,\n\t\t\t\t);\n\t\t\t\tif (messages.length > 1) {\n\t\t\t\t\tservice.runtime.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: service.runtime.agentId,\n\t\t\t\t\t\t\tchannelId: messages[0]?.channel?.id,\n\t\t\t\t\t\t\tmessageIds: messages.map((message) => message.id),\n\t\t\t\t\t\t\tcount: messages.length,\n\t\t\t\t\t\t\tpath: \"channelDebouncer\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Coalesced inbound Discord messages\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tvoid service.messageManager.handleMessage(combined as Message);\n\t\t\t} else if (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\t__discordAddressingContent: {\n\t\t\t\t\t\tvalue: anchor.content,\n\t\t\t\t\t\twritable: false,\n\t\t\t\t\t\tenumerable: false,\n\t\t\t\t\t\tconfigurable: true,\n\t\t\t\t\t},\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: effectiveChannelDebounceMs,\n\t\t\tresponseCooldownMs,\n\t\t\tgetBotUserId: () => service.client?.user?.id,\n\t\t\tcoalesceEnabled: messageCoalesce.enabled,\n\t\t\tmaxBatch: messageCoalesce.maxBatch,\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 (service.messageManager) {\n\t\t\trecordDiscordChannelMessageSeen(\n\t\t\t\tservice.messageManager,\n\t\t\t\tmessage.channel.id,\n\t\t\t\tmessage.id,\n\t\t\t);\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\taccountId,\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\taccountId,\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",
23
- "/**\n * Discord-native addressing helper.\n *\n * A bot can be mentioned as the subject of a message addressed to someone else.\n * Connector routing should only treat the bot as directly addressed when the\n * platform facts say so: the message replies to the bot, or the first user\n * mention in the raw Discord content is the bot.\n */\n\nfunction readUserMentionAt(text: string, start: number): string | null {\n\tif (text[start] !== \"<\" || text[start + 1] !== \"@\") return null;\n\tlet index = start + 2;\n\tif (text[index] === \"!\") index += 1;\n\tconst idStart = index;\n\twhile (index < text.length) {\n\t\tconst code = text.charCodeAt(index);\n\t\tif (code < 48 || code > 57) break;\n\t\tindex += 1;\n\t}\n\tif (index === idStart || text[index] !== \">\") return null;\n\treturn text.slice(idStart, index);\n}\n\nfunction firstUserMentionId(text: string): string | null {\n\tfor (let index = 0; index < text.length; index += 1) {\n\t\tif (text[index] !== \"<\") continue;\n\t\tconst mentionId = readUserMentionAt(text, index);\n\t\tif (mentionId) return mentionId;\n\t}\n\treturn null;\n}\n\nexport function isDiscordUserAddressed({\n\ttext,\n\tuserId,\n\thasMessageReference = false,\n\trepliedUserId,\n}: {\n\ttext?: string | null;\n\tuserId?: string | null;\n\thasMessageReference?: boolean;\n\trepliedUserId?: string | null;\n}): boolean {\n\tif (userId && hasMessageReference && repliedUserId === userId) {\n\t\treturn true;\n\t}\n\treturn Boolean(userId && firstUserMentionId(text ?? \"\") === userId);\n}\n",
24
- "import type { Message as DiscordMessage } from \"discord.js\";\nimport { isDiscordUserAddressed } from \"./addressing\";\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 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\tcoalesceEnabled?: boolean;\n\tmaxBatch?: number;\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 coalesceEnabled = options.coalesceEnabled === true;\n\tconst maxBatch = Math.max(1, options.maxBatch ?? Number.POSITIVE_INFINITY);\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\treturn isDiscordUserAddressed({\n\t\t\ttext: message.content,\n\t\t\tuserId: botId,\n\t\t\thasMessageReference: Boolean(message.reference?.messageId),\n\t\t\trepliedUserId: message.mentions?.repliedUser?.id,\n\t\t});\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\tconst targeted = isBotTargeted(message);\n\t\tif (targeted && !coalesceEnabled) {\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) && !targeted) {\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\tif (coalesceEnabled && existing.messages.length >= maxBatch) {\n\t\t\t\tflush(channelId);\n\t\t\t\treturn;\n\t\t\t}\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\toptions: { maxBatch?: number } = {},\n): MessageDebouncer {\n\tconst pending = new Map<string, PendingEntry>();\n\tconst maxBatch = Math.max(1, options.maxBatch ?? Number.POSITIVE_INFINITY);\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\tif (existing.messages.length >= maxBatch) {\n\t\t\t\tflush(key);\n\t\t\t\treturn;\n\t\t\t}\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",
25
- "import type { Message as DiscordMessage } from \"discord.js\";\n\nexport interface DiscordMessageCoalesceConfig {\n\tenabled: boolean;\n\twindowMs: number;\n\tmaxBatch: number;\n}\n\nexport interface CoalescedDiscordMessageMeta {\n\tid?: string;\n\tchannelId?: string;\n\tauthorId?: string;\n\tusername?: string;\n\tdisplayName?: string;\n\tcreatedTimestamp?: number;\n\tcontentPreview: string;\n}\n\nexport type DiscordMessageWithCoalescedMetadata = DiscordMessage & {\n\t__discordCoalescedMessages?: CoalescedDiscordMessageMeta[];\n\t__discordCoalescedMessageIds?: string[];\n\t__discordAddressingContent?: string;\n};\n\nexport type DiscordMessageMetadata = Record<string, unknown> & {\n\tcoalescedDiscordMessageIds?: string[];\n\tcoalescedDiscordMessages?: CoalescedDiscordMessageMeta[];\n\tcoalescedDiscordMessageCount?: number;\n};\n\nconst DEFAULT_WINDOW_MS = 8_000;\nconst DEFAULT_MAX_BATCH = 5;\n\nfunction parseBoolean(value: unknown, fallback: boolean): boolean {\n\tif (value === undefined || value === null) {\n\t\treturn fallback;\n\t}\n\treturn String(value).trim().toLowerCase() === \"true\";\n}\n\nfunction parsePositiveInteger(value: unknown, fallback: number): number {\n\tconst parsed = Number.parseInt(String(value ?? \"\"), 10);\n\treturn Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;\n}\n\nexport function getDiscordMessageCoalesceConfig(\n\tgetSetting: (key: string) => unknown,\n): DiscordMessageCoalesceConfig {\n\tconst enabled = parseBoolean(\n\t\tgetSetting(\"DISCORD_MESSAGE_COALESCE_ENABLED\"),\n\t\tfalse,\n\t);\n\n\treturn {\n\t\tenabled,\n\t\twindowMs: enabled\n\t\t\t? parsePositiveInteger(\n\t\t\t\t\tgetSetting(\"DISCORD_MESSAGE_COALESCE_WINDOW_MS\"),\n\t\t\t\t\tDEFAULT_WINDOW_MS,\n\t\t\t\t)\n\t\t\t: DEFAULT_WINDOW_MS,\n\t\tmaxBatch: parsePositiveInteger(\n\t\t\tgetSetting(\"DISCORD_MESSAGE_COALESCE_MAX_BATCH\"),\n\t\t\tDEFAULT_MAX_BATCH,\n\t\t),\n\t};\n}\n\nexport function getDiscordMessageMeta(\n\tmessage: DiscordMessage,\n): CoalescedDiscordMessageMeta {\n\treturn {\n\t\tid: message.id,\n\t\tchannelId: message.channel?.id,\n\t\tauthorId: message.author?.id,\n\t\tusername: message.author?.username,\n\t\tdisplayName:\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\tcreatedTimestamp: message.createdTimestamp,\n\t\tcontentPreview: String(message.content || \"\").slice(0, 300),\n\t};\n}\n\nexport function formatCoalescedDiscordMessages(\n\tmessages: DiscordMessage[],\n): string {\n\treturn messages\n\t\t.map((message, index) => {\n\t\t\tconst meta = getDiscordMessageMeta(message);\n\t\t\tconst label =\n\t\t\t\tmeta.displayName || meta.username || meta.authorId || \"unknown\";\n\t\t\tconst ordinal = index + 1;\n\t\t\treturn `[Discord message ${ordinal}/${messages.length} id=${meta.id || \"unknown\"} author=${label} author_id=${meta.authorId || \"unknown\"} at=${meta.createdTimestamp || \"unknown\"}]\\n${message.content || \"\"}\\n[/Discord message ${ordinal}/${messages.length} id=${meta.id || \"unknown\"}]`;\n\t\t})\n\t\t.join(\"\\n\\n\");\n}\n\nexport function makeCoalescedDiscordMessage(\n\tmessages: DiscordMessage[],\n\tanchor?: DiscordMessage,\n\tconfig: Pick<DiscordMessageCoalesceConfig, \"enabled\" | \"maxBatch\"> = {\n\t\tenabled: false,\n\t\tmaxBatch: DEFAULT_MAX_BATCH,\n\t},\n): DiscordMessage {\n\tif (!config.enabled || messages.length <= 1) {\n\t\treturn anchor ?? messages[0];\n\t}\n\n\tconst capped = messages.slice(0, config.maxBatch);\n\tconst base = anchor ?? capped[capped.length - 1] ?? messages[0];\n\tconst meta = capped.map(getDiscordMessageMeta);\n\treturn Object.create(base, {\n\t\tcontent: {\n\t\t\tvalue: formatCoalescedDiscordMessages(capped),\n\t\t\twritable: true,\n\t\t\tenumerable: true,\n\t\t\tconfigurable: true,\n\t\t},\n\t\t__discordCoalescedMessages: {\n\t\t\tvalue: meta,\n\t\t\twritable: false,\n\t\t\tenumerable: false,\n\t\t\tconfigurable: true,\n\t\t},\n\t\t__discordCoalescedMessageIds: {\n\t\t\tvalue: meta.map((entry) => entry.id).filter(Boolean),\n\t\t\twritable: false,\n\t\t\tenumerable: false,\n\t\t\tconfigurable: true,\n\t\t},\n\t\t__discordAddressingContent: {\n\t\t\tvalue: base.content,\n\t\t\twritable: false,\n\t\t\tenumerable: false,\n\t\t\tconfigurable: true,\n\t\t},\n\t});\n}\n\nexport function appendCoalescedDiscordMetadata(\n\tmessage: DiscordMessage,\n\textraMetadata: Record<string, unknown> = {},\n): DiscordMessageMetadata {\n\tconst coalesced = message as DiscordMessageWithCoalescedMetadata;\n\tconst ids = coalesced.__discordCoalescedMessageIds;\n\tconst messages = coalesced.__discordCoalescedMessages;\n\tif (!Array.isArray(ids) || ids.length <= 1) {\n\t\treturn extraMetadata;\n\t}\n\n\treturn {\n\t\t...extraMetadata,\n\t\tcoalescedDiscordMessageIds: ids,\n\t\tcoalescedDiscordMessages: Array.isArray(messages) ? messages : undefined,\n\t\tcoalescedDiscordMessageCount: ids.length,\n\t};\n}\n",
26
- "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",
27
- "import type { Content } from \"@elizaos/core\";\nimport type { Message as DiscordMessage } from \"discord.js\";\n\nexport type DiscordStalenessBehavior = \"tag\" | \"skip\" | \"ignore\";\n\nexport interface DiscordStalenessConfig {\n\tenabled: boolean;\n\tbehavior: DiscordStalenessBehavior;\n\tthreshold: number;\n}\n\nconst DEFAULT_THRESHOLD = 2;\nconst channelSequences = new WeakMap<object, Map<string, number>>();\nconst lastMessageIds = new WeakMap<object, Map<string, string | undefined>>();\n\nfunction parseBoolean(value: unknown, fallback: boolean): boolean {\n\tif (value === undefined || value === null) {\n\t\treturn fallback;\n\t}\n\treturn String(value).trim().toLowerCase() === \"true\";\n}\n\nfunction parseNonNegativeInteger(value: unknown, fallback: number): number {\n\tconst parsed = Number.parseInt(String(value ?? \"\"), 10);\n\treturn Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;\n}\n\nfunction parseBehavior(value: unknown): DiscordStalenessBehavior {\n\tconst normalized = String(value ?? \"tag\")\n\t\t.trim()\n\t\t.toLowerCase();\n\treturn normalized === \"skip\" ||\n\t\tnormalized === \"ignore\" ||\n\t\tnormalized === \"tag\"\n\t\t? normalized\n\t\t: \"tag\";\n}\n\nfunction ensureSequenceMap(owner: object): Map<string, number> {\n\tlet map = channelSequences.get(owner);\n\tif (!map) {\n\t\tmap = new Map<string, number>();\n\t\tchannelSequences.set(owner, map);\n\t}\n\treturn map;\n}\n\nexport function getDiscordStalenessConfig(\n\tgetSetting: (key: string) => unknown,\n): DiscordStalenessConfig {\n\treturn {\n\t\tenabled: parseBoolean(getSetting(\"DISCORD_STALENESS_ENABLED\"), false),\n\t\tbehavior: parseBehavior(getSetting(\"DISCORD_STALENESS_BEHAVIOR\")),\n\t\tthreshold: parseNonNegativeInteger(\n\t\t\tgetSetting(\"DISCORD_STALENESS_THRESHOLD\"),\n\t\t\tDEFAULT_THRESHOLD,\n\t\t),\n\t};\n}\n\nexport function recordDiscordChannelMessageSeen(\n\towner: object | undefined,\n\tchannelId: string | undefined,\n\tmessageId?: string,\n): number {\n\tif (!owner || !channelId) {\n\t\treturn 0;\n\t}\n\tconst sequences = ensureSequenceMap(owner);\n\tconst next = (sequences.get(channelId) ?? 0) + 1;\n\tsequences.set(channelId, next);\n\n\tlet lastIds = lastMessageIds.get(owner);\n\tif (!lastIds) {\n\t\tlastIds = new Map<string, string | undefined>();\n\t\tlastMessageIds.set(owner, lastIds);\n\t}\n\tlastIds.set(channelId, messageId);\n\n\treturn next;\n}\n\nexport function getDiscordChannelMessageSequence(\n\towner: object | undefined,\n\tchannelId: string | undefined,\n): number {\n\tif (!owner || !channelId) {\n\t\treturn 0;\n\t}\n\treturn ensureSequenceMap(owner).get(channelId) ?? 0;\n}\n\nexport interface DiscordStalenessDecision {\n\tshouldSend: boolean;\n\tstale: boolean;\n\tmessagesSinceTurnStart: number;\n\tbehavior: DiscordStalenessBehavior;\n}\n\nexport function applyDiscordStalenessGuard(options: {\n\tconfig: DiscordStalenessConfig;\n\towner: object | undefined;\n\tmessage: DiscordMessage;\n\tstartSequence: number;\n\tcontent: Content;\n}): DiscordStalenessDecision {\n\tconst { config, owner, message, startSequence, content } = options;\n\tif (!config.enabled || config.behavior === \"ignore\") {\n\t\treturn {\n\t\t\tshouldSend: true,\n\t\t\tstale: false,\n\t\t\tmessagesSinceTurnStart: 0,\n\t\t\tbehavior: config.behavior,\n\t\t};\n\t}\n\n\tconst currentSequence = getDiscordChannelMessageSequence(\n\t\towner,\n\t\tmessage.channel?.id,\n\t);\n\tconst messagesSinceTurnStart = Math.max(0, currentSequence - startSequence);\n\tconst stale = messagesSinceTurnStart > config.threshold;\n\tif (!stale) {\n\t\treturn {\n\t\t\tshouldSend: true,\n\t\t\tstale: false,\n\t\t\tmessagesSinceTurnStart,\n\t\t\tbehavior: config.behavior,\n\t\t};\n\t}\n\n\tif (config.behavior === \"skip\") {\n\t\treturn {\n\t\t\tshouldSend: false,\n\t\t\tstale: true,\n\t\t\tmessagesSinceTurnStart,\n\t\t\tbehavior: config.behavior,\n\t\t};\n\t}\n\n\tif (\n\t\tconfig.behavior === \"tag\" &&\n\t\ttypeof content.text === \"string\" &&\n\t\tcontent.text.trim().length > 0 &&\n\t\t!/^(\\s*\\(catching up:\\))/i.test(content.text)\n\t) {\n\t\tcontent.text = `(catching up:) ${content.text}`;\n\t}\n\n\treturn {\n\t\tshouldSend: true,\n\t\tstale: true,\n\t\tmessagesSinceTurnStart,\n\t\tbehavior: config.behavior,\n\t};\n}\n",
28
- "/**\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\nfunction formatJsonScalar(value: unknown): string {\n\tif (value == null) return \"\";\n\tif (typeof value === \"string\") return value.replace(/\\s+/g, \" \").trim();\n\tif (typeof value === \"number\" || typeof value === \"boolean\") {\n\t\treturn String(value);\n\t}\n\tif (Array.isArray(value)) {\n\t\treturn value.map((item) => formatJsonScalar(item)).join(\", \");\n\t}\n\tif (typeof value === \"object\") {\n\t\treturn Object.entries(value as Record<string, unknown>)\n\t\t\t.map(([key, entry]) => `${key}:${formatJsonScalar(entry)}`)\n\t\t\t.join(\", \");\n\t}\n\treturn String(value);\n}\n\nfunction formatSpiderStateJson(state: ChannelSpiderState): string {\n\treturn [\n\t\t\"state:\",\n\t\t...Object.entries(state).map(\n\t\t\t([key, value]) => ` ${key}: ${formatJsonScalar(value)}`,\n\t\t),\n\t].join(\"\\n\");\n}\n\n/**\n * Subset of DiscordService fields needed by history functions.\n */\nexport interface HistoryServiceInternals {\n\taccountId?: string;\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\tconst accountId = service.accountId ?? \"default\";\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\tmetadata: {\n\t\t\t\t\taccountId,\n\t\t\t\t},\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\tmetadata: {\n\t\t\t\t\taccountId,\n\t\t\t\t},\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: formatSpiderStateJson(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\taccountId,\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\t/**\n\t\t * Connector account ID this message arrived on. Stamped into\n\t\t * `Memory.metadata.accountId` so downstream actions (and policy\n\t\t * evaluation) can route outbound replies through the same account.\n\t\t */\n\t\taccountId?: string;\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 accountId = options?.accountId ?? service.accountId ?? \"default\";\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\t// Top-level accountId per MessageMetadata contract. Inbound connector\n\t\t// stamps this so outbound resolution can route replies back through the\n\t\t// same connector account.\n\t\taccountId,\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\taccountId,\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 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\tconst accountId = service.accountId ?? \"default\";\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\tmetadata: {\n\t\t\t\t\taccountId,\n\t\t\t\t},\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: {\n\t\t\t\t...buildDiscordWorldMetadata(\n\t\t\t\t\tservice.runtime,\n\t\t\t\t\tfirstMessageChannelGuild?.ownerId ??\n\t\t\t\t\t\tfirstMessage.guild?.ownerId ??\n\t\t\t\t\t\tundefined,\n\t\t\t\t),\n\t\t\t\taccountId,\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\tconst accountId = service.accountId ?? \"default\";\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\tmetadata: {\n\t\t\taccountId,\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\tmetadata: {\n\t\t\taccountId,\n\t\t},\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",
29
- "/**\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\taccountId?: string;\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 accountId = service.accountId ?? \"default\";\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 UUID,\n\t\t\tmetadata: {\n\t\t\t\t...buildDiscordWorldMetadata(\n\t\t\t\t\tservice.runtime,\n\t\t\t\t\treaction.message.guild?.ownerId,\n\t\t\t\t),\n\t\t\t\taccountId,\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\taccountId,\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\taccountId,\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",
30
- "import {\n\ttype IAgentRuntime,\n\tlogger,\n\ttype Media,\n\tModelType,\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 stripJsonFence(text: string): string {\n\tconst trimmed = text.trim();\n\tconst fenceMatch = trimmed.match(/^```(?:json)?\\s*([\\s\\S]*?)\\s*```$/i);\n\treturn fenceMatch?.[1]?.trim() ?? trimmed;\n}\n\nexport function parseJsonObjectFromText(\n\ttext: string,\n): Record<string, unknown> | null {\n\ttry {\n\t\tconst parsed = JSON.parse(stripJsonFence(text));\n\t\tif (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n\t\t\treturn parsed as Record<string, unknown>;\n\t\t}\n\t} catch (_error) {\n\t\treturn null;\n\t}\n\treturn null;\n}\n\nexport function getActionParameters(options: unknown): Record<string, unknown> {\n\tconst optionsRecord =\n\t\toptions && typeof options === \"object\"\n\t\t\t? (options as Record<string, unknown>)\n\t\t\t: {};\n\tconst parameters = optionsRecord.parameters;\n\tif (\n\t\tparameters &&\n\t\ttypeof parameters === \"object\" &&\n\t\t!Array.isArray(parameters)\n\t) {\n\t\treturn parameters as Record<string, unknown>;\n\t}\n\treturn optionsRecord;\n}\n\nexport function parseJsonArrayFromText(text: string): JsonValue[] | null {\n\ttry {\n\t\tconst parsed = JSON.parse(stripJsonFence(text));\n\t\tif (Array.isArray(parsed)) {\n\t\t\treturn parsed.filter(\n\t\t\t\t(chunk): chunk is JsonValue =>\n\t\t\t\t\ttypeof chunk === \"string\" ||\n\t\t\t\t\ttypeof chunk === \"number\" ||\n\t\t\t\t\ttypeof chunk === \"boolean\" ||\n\t\t\t\t\tchunk === null ||\n\t\t\t\t\t(Array.isArray(chunk) &&\n\t\t\t\t\t\tchunk.every(\n\t\t\t\t\t\t\t(item) =>\n\t\t\t\t\t\t\t\ttypeof item === \"string\" ||\n\t\t\t\t\t\t\t\ttypeof item === \"number\" ||\n\t\t\t\t\t\t\t\ttypeof item === \"boolean\" ||\n\t\t\t\t\t\t\t\titem === null,\n\t\t\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t} catch (_error) {\n\t\treturn null;\n\t}\n\treturn null;\n}\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 JSON only, no markdown:\n {\n \"title\": \"Generated Title\",\n \"summary\": \"Generated summary and/or description of the text\"\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\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 JSON only, no markdown or explanation.\n\nText to split:\n\"\"\"\n${content}\n\"\"\"\n\nReturn format:\n[\"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",
31
- "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",
32
- "import { createHash } from \"node:crypto\";\nimport {\n\tChannelType,\n\ttype Content,\n\tContentType,\n\tcheckPairingAllowed,\n\tcreateUniqueUuid,\n\ttype EventPayload,\n\tEventType,\n\ttype FetchedDocumentUrl as FetchedKnowledgeUrl,\n\tfetchDocumentFromUrl,\n\ttype HandlerCallback,\n\ttype IAgentRuntime,\n\tisInAllowlist,\n\tlifeOpsPassiveConnectorsEnabled,\n\ttype Media,\n\ttype Memory,\n\tMemoryType,\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 { isDiscordUserAddressed } from \"./addressing\";\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 {\n\tappendCoalescedDiscordMetadata,\n\ttype DiscordMessageWithCoalescedMetadata,\n} from \"./message-coalesce\";\nimport { stripReasoningTags } from \"./reasoning-tags\";\nimport {\n\tapplyDiscordStalenessGuard,\n\ttype DiscordStalenessConfig,\n\tgetDiscordChannelMessageSequence,\n\tgetDiscordStalenessConfig,\n} from \"./staleness\";\nimport {\n\tcreateStatusReactionController,\n\ttype StatusReactionScope,\n\tshouldShowStatusReaction,\n} from \"./status-reactions\";\nimport {\n\tDiscordEventTypes,\n\ttype DiscordSettings,\n\ttype IDiscordService,\n\ttype JsonObject,\n\ttype JsonValue,\n} 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\nexport function resolveGenerationTimeoutMs(\n\ttimeoutSetting: unknown,\n\tfallbackSetting: unknown,\n): number | null {\n\tconst parsed = Number.parseInt(\n\t\tString(timeoutSetting ?? fallbackSetting ?? \"120000\"),\n\t\t10,\n\t);\n\tif (!Number.isFinite(parsed)) {\n\t\treturn 120_000;\n\t}\n\treturn parsed > 0 ? Math.max(30_000, parsed) : null;\n}\n\nfunction isJsonValue(value: unknown): value is JsonValue {\n\tif (\n\t\tvalue === null ||\n\t\ttypeof value === \"string\" ||\n\t\ttypeof value === \"number\" ||\n\t\ttypeof value === \"boolean\"\n\t) {\n\t\treturn true;\n\t}\n\tif (Array.isArray(value)) {\n\t\treturn value.every(isJsonValue);\n\t}\n\tif (typeof value === \"object\" && value !== null) {\n\t\treturn Object.values(value).every(isJsonValue);\n\t}\n\treturn false;\n}\n\nfunction compactJsonObject(record: Record<string, unknown>): JsonObject {\n\tconst json: JsonObject = {};\n\tfor (const [key, value] of Object.entries(record)) {\n\t\tif (value === undefined) continue;\n\t\tif (isJsonValue(value)) {\n\t\t\tjson[key] = value;\n\t\t}\n\t}\n\treturn json;\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\nfunction getAddressingContent(message: DiscordMessage): string {\n\treturn (\n\t\t(message as DiscordMessageWithCoalescedMetadata)\n\t\t\t.__discordAddressingContent ?? message.content\n\t);\n}\n\nfunction fetchedUrlToAttachment(\n\turl: string,\n\tfetched: FetchedKnowledgeUrl,\n): Media {\n\tconst hasReadableText = fetched.contentType !== \"binary\";\n\treturn {\n\t\tid: webpageAttachmentId(url),\n\t\turl,\n\t\ttitle: fetched.filename || \"Web Page\",\n\t\tsource: fetched.contentType === \"transcript\" ? \"YouTube\" : \"Web\",\n\t\ttext: hasReadableText ? fetched.content : \"\",\n\t\tcontentType: ContentType.LINK,\n\t};\n}\n\nfunction webpageAttachmentId(url: string): string {\n\treturn `webpage-${createHash(\"sha256\").update(url).digest(\"hex\").slice(0, 24)}`;\n}\n\nconst ACTIVE_TASK_AGENT_STATUSES = new Set([\n\t\"active\",\n\t\"blocked\",\n\t\"tool_running\",\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 stringField(\n\trecord: Record<string, unknown> | null,\n\tfield: string,\n): string | undefined {\n\tconst value = record?.[field];\n\treturn typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nexport function hasActiveTaskAgentWorkForMessage(\n\truntime: Pick<IAgentRuntime, \"getService\">,\n\tmessageId: string,\n): boolean {\n\ttry {\n\t\tconst coordinator = asRecord(runtime.getService(\"SWARM_COORDINATOR\"));\n\t\tconst tasks = coordinator?.tasks;\n\t\tif (!(tasks instanceof Map)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (const taskValue of tasks.values()) {\n\t\t\tconst task = asRecord(taskValue);\n\t\t\tconst status = stringField(task, \"status\");\n\t\t\tif (!status || !ACTIVE_TASK_AGENT_STATUSES.has(status)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst metadata = asRecord(task?.originMetadata);\n\t\t\tconst originMessageId = stringField(metadata, \"messageId\");\n\t\t\tif (originMessageId === messageId) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t} catch {\n\t\treturn false;\n\t}\n\n\treturn false;\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 accountId: string;\n\tprivate statusReactionScope: StatusReactionScope;\n\tprivate envelopeEnabled: boolean;\n\tprivate draftStreamingEnabled: boolean;\n\tprivate stalenessConfig: DiscordStalenessConfig;\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\tthis.accountId = discordService.accountId ?? \"default\";\n\t\t// Load Discord settings with proper priority (env vars > character settings > defaults)\n\t\tthis.discordSettings =\n\t\t\tdiscordService.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\tthis.stalenessConfig = getDiscordStalenessConfig((key) =>\n\t\t\tthis.runtime.getSetting(key),\n\t\t);\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 isBotPlatformMentioned = !!(\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 isBotAddressed = isDiscordUserAddressed({\n\t\t\ttext: getAddressingContent(message),\n\t\t\tuserId: clientUser?.id,\n\t\t\thasMessageReference: Boolean(message.reference?.messageId),\n\t\t\trepliedUserId: message.mentions.repliedUser?.id,\n\t\t});\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\" && isBotAddressed ? message.id : undefined;\n\t\tconst strictModeShouldProcess = isDM || isBotAddressed;\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 ignoresOtherTarget =\n\t\t\t\t!isDM && !isBotAddressed && (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: isBotPlatformMentioned && isBotAddressed,\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:\n\t\t\t\t\t\t\t\tisBotPlatformMentioned && isBotAddressed\n\t\t\t\t\t\t\t\t\t? \"platform_mention\"\n\t\t\t\t\t\t\t\t\t: isReplyToBot\n\t\t\t\t\t\t\t\t\t\t? \"reply\"\n\t\t\t\t\t\t\t\t\t\t: isInThread\n\t\t\t\t\t\t\t\t\t\t\t? \"thread\"\n\t\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: compactJsonObject(\n\t\t\t\t\t\tappendCoalescedDiscordMetadata(message, {\n\t\t\t\t\t\t\t// Reply attribution for cross-agent filtering\n\t\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\t// so other agents can ignore it (only the replied-to agent should respond)\n\t\t\t\t\t\t\t...(message.mentions.repliedUser\n\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\treplyToAuthor: {\n\t\t\t\t\t\t\t\t\t\t\tid: message.mentions.repliedUser.id,\n\t\t\t\t\t\t\t\t\t\t\tdisplayName:\n\t\t\t\t\t\t\t\t\t\t\t\tmessage.mentions.repliedUser.globalName ??\n\t\t\t\t\t\t\t\t\t\t\t\tmessage.mentions.repliedUser.username,\n\t\t\t\t\t\t\t\t\t\t\tusername: message.mentions.repliedUser.username,\n\t\t\t\t\t\t\t\t\t\t\tisBot: message.mentions.repliedUser.bot,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\treplyToSenderId: message.mentions.repliedUser.id,\n\t\t\t\t\t\t\t\t\t\treplyToSenderName:\n\t\t\t\t\t\t\t\t\t\t\tmessage.mentions.repliedUser.globalName ??\n\t\t\t\t\t\t\t\t\t\t\tmessage.mentions.repliedUser.username,\n\t\t\t\t\t\t\t\t\t\treplyToSenderUserName:\n\t\t\t\t\t\t\t\t\t\t\tmessage.mentions.repliedUser.username,\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...(message.reference?.messageId\n\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\treplyToMessageId: createUniqueUuid(\n\t\t\t\t\t\t\t\t\t\t\tthis.runtime,\n\t\t\t\t\t\t\t\t\t\t\tmessage.reference.messageId,\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\treplyToExternalMessageId: message.reference.messageId,\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}),\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 UUID,\n\t\t\t\tmetadata: {\n\t\t\t\t\t...buildDiscordWorldMetadata(\n\t\t\t\t\t\tthis.runtime,\n\t\t\t\t\t\tmessage.guild?.ownerId ?? undefined,\n\t\t\t\t\t),\n\t\t\t\t\taccountId: this.accountId,\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 stalenessStartSequence = getDiscordChannelMessageSequence(\n\t\t\t\tthis,\n\t\t\t\tmessage.channel.id,\n\t\t\t);\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 = resolveGenerationTimeoutMs(\n\t\t\t\tthis.runtime.getSetting(\"DISCORD_GENERATION_TIMEOUT_MS\") ??\n\t\t\t\t\tprocess.env.DISCORD_GENERATION_TIMEOUT_MS,\n\t\t\t\tthis.runtime.getSetting(\"MESSAGE_TIMEOUT_MS\") ??\n\t\t\t\t\tprocess.env.MESSAGE_TIMEOUT_MS,\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\tconst stalenessDecision = applyDiscordStalenessGuard({\n\t\t\t\t\t\tconfig: this.stalenessConfig,\n\t\t\t\t\t\towner: this,\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t\tstartSequence: stalenessStartSequence,\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t});\n\t\t\t\t\tif (stalenessDecision.stale) {\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\tchannelId: message.channel.id,\n\t\t\t\t\t\t\t\tmessageId: message.id,\n\t\t\t\t\t\t\t\tmessagesSinceTurnStart:\n\t\t\t\t\t\t\t\t\tstalenessDecision.messagesSinceTurnStart,\n\t\t\t\t\t\t\t\tthreshold: this.stalenessConfig.threshold,\n\t\t\t\t\t\t\t\tbehavior: stalenessDecision.behavior,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"Discord response completed after newer channel messages arrived\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (!stalenessDecision.shouldSend) {\n\t\t\t\t\t\ttypingController.stop();\n\t\t\t\t\t\tstatusReactions?.setDone();\n\t\t\t\t\t\tawait finalizePendingDraft();\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// planner follow-up 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\tmetadata: {\n\t\t\t\t\t\t\t\ttype: MemoryType.MESSAGE,\n\t\t\t\t\t\t\t\taccountId: this.accountId,\n\t\t\t\t\t\t\t},\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\tconst payload: EventPayload & {\n\t\t\t\t\t\t\tmessage: Memory;\n\t\t\t\t\t\t\tcallback: HandlerCallback;\n\t\t\t\t\t\t\taccountId: string;\n\t\t\t\t\t\t} = {\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\taccountId: this.accountId,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tawait this.runtime.emitEvent(\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\tDiscordEventTypes.MESSAGE_RECEIVED,\n\t\t\t\t\t\t\t\tEventType.MESSAGE_RECEIVED,\n\t\t\t\t\t\t\t] as string[],\n\t\t\t\t\t\t\tpayload,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t})();\n\n\t\t\t\tgenerationPromise.catch(() => {});\n\t\t\t\tif (generationTimeoutMs === null) {\n\t\t\t\t\tawait generationPromise;\n\t\t\t\t} else {\n\t\t\t\t\tconst timeoutPromise = new Promise<never>((_, reject) => {\n\t\t\t\t\t\tgenerationTimeoutHandle = setTimeout(() => {\n\t\t\t\t\t\t\tgenerationTimedOut = true;\n\t\t\t\t\t\t\treject(\n\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t`Discord generation timeout after ${generationTimeoutMs}ms`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}, generationTimeoutMs);\n\t\t\t\t\t});\n\n\t\t\t\t\tawait Promise.race([generationPromise, timeoutPromise]);\n\t\t\t\t}\n\t\t\t} catch (generationError) {\n\t\t\t\tconst activeTaskAgentWork =\n\t\t\t\t\tgenerationTimedOut &&\n\t\t\t\t\thasActiveTaskAgentWorkForMessage(this.runtime, messageId);\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\tactiveTaskAgentWork,\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\tif (activeTaskAgentWork) {\n\t\t\t\t\tstatusReactions?.setDone();\n\t\t\t\t\tawait abortPendingDraft();\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\tmessageId: message.id,\n\t\t\t\t\t\t\tmemoryId: messageId,\n\t\t\t\t\t\t\troomId,\n\t\t\t\t\t\t\ttimeoutMs: generationTimeoutMs,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Suppressing Discord timeout reply while task-agent work is still active\",\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\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\tconst 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.push(\n\t\t\t\t...(await this.attachmentManager.processAttachments(\n\t\t\t\t\tmessage.attachments,\n\t\t\t\t)),\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\ttry {\n\t\t\t\t\tconst fetched = await fetchDocumentFromUrl(url);\n\t\t\t\t\tattachments.push(fetchedUrlToAttachment(url, fetched));\n\t\t\t\t\tcontinue;\n\t\t\t\t} catch (error) {\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.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\turl,\n\t\t\t\t\t\t\terror: errorMsg,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Direct URL enrichment failed; trying browser service fallback\",\n\t\t\t\t\t);\n\t\t\t\t}\n\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: webpageAttachmentId(url),\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\tcontentType: ContentType.LINK,\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",
33
- "import fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport {\n\tContentType,\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\nconst TEXT_ATTACHMENT_EXTENSIONS = new Set([\n\t\".cjs\",\n\t\".conf\",\n\t\".csv\",\n\t\".env\",\n\t\".ini\",\n\t\".js\",\n\t\".json\",\n\t\".jsonl\",\n\t\".jsx\",\n\t\".log\",\n\t\".md\",\n\t\".mdx\",\n\t\".mjs\",\n\t\".sql\",\n\t\".toml\",\n\t\".ts\",\n\t\".tsx\",\n\t\".txt\",\n\t\".xml\",\n\t\".yaml\",\n\t\".yml\",\n]);\n\nconst TEXT_ATTACHMENT_MIME_TYPES = new Set([\n\t\"application/javascript\",\n\t\"application/json\",\n\t\"application/ld+json\",\n\t\"application/typescript\",\n\t\"application/x-javascript\",\n\t\"application/x-ndjson\",\n\t\"application/x-yaml\",\n\t\"application/xml\",\n\t\"application/yaml\",\n]);\n\nfunction normalizedMimeType(contentType: string | null | undefined): string {\n\treturn (contentType ?? \"\").split(\";\")[0]?.trim().toLowerCase() ?? \"\";\n}\n\nfunction attachmentExtension(attachment: Attachment): string {\n\treturn path.extname(attachment.name ?? \"\").toLowerCase();\n}\n\nfunction isReadableTextAttachment(attachment: Attachment): boolean {\n\tconst mimeType = normalizedMimeType(attachment.contentType);\n\tif (\n\t\tmimeType.startsWith(\"text/\") ||\n\t\tTEXT_ATTACHMENT_MIME_TYPES.has(mimeType)\n\t) {\n\t\treturn true;\n\t}\n\n\tif (\n\t\t!mimeType ||\n\t\tmimeType === \"application/octet-stream\" ||\n\t\tmimeType === \"binary/octet-stream\"\n\t) {\n\t\treturn TEXT_ATTACHMENT_EXTENSIONS.has(attachmentExtension(attachment));\n\t}\n\n\treturn false;\n}\n\n/**\n * Class representing an Attachment Manager.\n */\nexport class AttachmentManager {\n\tprivate attachmentCache: Map<string, Media> = new Map();\n\tprivate runtime: IAgentRuntime;\n\n\t/**\n\t * Constructor for creating a new instance of the class.\n\t *\n\t * @param {IAgentRuntime} runtime The runtime object to be injected into the instance.\n\t */\n\tconstructor(runtime: IAgentRuntime) {\n\t\tthis.runtime = runtime;\n\t}\n\n\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\tconst mimeType = normalizedMimeType(attachment.contentType);\n\t\tif (mimeType === \"application/pdf\") {\n\t\t\tmedia = await this.processPdfAttachment(attachment);\n\t\t} else if (isReadableTextAttachment(attachment)) {\n\t\t\tmedia = await this.processPlaintextAttachment(attachment);\n\t\t} else if (mimeType.startsWith(\"audio/\") || mimeType === \"video/mp4\") {\n\t\t\tmedia = await this.processAudioVideoAttachment(attachment);\n\t\t} else if (mimeType.startsWith(\"image/\")) {\n\t\t\tmedia = await this.processImageAttachment(attachment);\n\t\t} else if (mimeType.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\tcontentType: attachment.contentType?.startsWith(\"audio/\")\n\t\t\t\t\t? ContentType.AUDIO\n\t\t\t\t\t: ContentType.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 || \"\",\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\tcontentType: attachment.contentType?.startsWith(\"audio/\")\n\t\t\t\t\t? ContentType.AUDIO\n\t\t\t\t\t: ContentType.VIDEO,\n\t\t\t\tdescription: \"An audio/video attachment (transcription failed)\",\n\t\t\t\ttext: \"\",\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\tcontentType: ContentType.DOCUMENT,\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\tcontentType: ContentType.DOCUMENT,\n\t\t\t\tdescription: \"A PDF document that could not be converted to text\",\n\t\t\t\ttext: \"\",\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\tcontentType: ContentType.DOCUMENT,\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\tcontentType: ContentType.DOCUMENT,\n\t\t\t\tdescription: \"A plaintext document that could not be retrieved\",\n\t\t\t\ttext: \"\",\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\tcontentType: ContentType.IMAGE,\n\t\t\t\tdescription: description || \"An image attachment\",\n\t\t\t\ttext: description || \"\",\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\tcontentType: ContentType.IMAGE,\n\t\t\tdescription: \"An image attachment (recognition failed)\",\n\t\t\ttext: \"\",\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\tcontentType: ContentType.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: \"\",\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\tcontentType: ContentType.VIDEO,\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\tcontentType: ContentType.VIDEO,\n\t\t\tdescription: \"A video attachment\",\n\t\t\ttext: \"\",\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: \"\",\n\t\t};\n\t}\n}\n",
34
- "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",
35
- "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",
36
- "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\t// Put the reply quote AFTER the user's actual message so Stage 1\n\t\t\t// classification weights the user's current intent first. The previous\n\t\t\t// order (\"replying to @x:\\n> <quote>\\n<userText>\") biased the\n\t\t\t// classifier toward the quoted topic, which broke routing for turns\n\t\t\t// where the user replied to a long bot message and asked for something\n\t\t\t// unrelated (e.g. an app build after a tech-debt status update).\n\t\t\t// Use typographic curly quotes as outer delimiters so embedded straight\n\t\t\t// `\"` characters in the quoted content don't visually break the wrapper\n\t\t\t// or confuse an LLM classifier reading the result.\n\t\t\treplyContext = truncated\n\t\t\t\t? `\\n(in reply to @${refAuthor}: “${truncated}”)`\n\t\t\t\t: `\\n(in reply to @${refAuthor})`;\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: `${header}: ${rawContent}${replyContext}`,\n\t\tchatType,\n\t};\n}\n",
37
- "const REASONING_TAGS = [\n\t\"thinking\",\n\t\"reasoning\",\n\t\"reflection\",\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|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",
38
- "import type { Message as DiscordMessage } from \"discord.js\";\nimport { isDiscordUserAddressed } from \"./addressing\";\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\treturn isDiscordUserAddressed({\n\t\ttext: message.content,\n\t\tuserId: botId,\n\t\thasMessageReference: Boolean(message.reference?.messageId),\n\t\trepliedUserId: message.mentions.repliedUser?.id,\n\t});\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",
39
- "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",
40
- "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\ttype EventPayload,\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 { IDiscordService } from \"./types\";\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 accountId: string;\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 {IDiscordService} service - The Discord service to use.\n\t * @param {ICompatRuntime} runtime - The runtime for the agent (with cross-core compat).\n\t */\n\tconstructor(\n\t\tservice: Pick<IDiscordService, \"accountId\" | \"client\">,\n\t\truntime: ICompatRuntime,\n\t) {\n\t\tsuper();\n\t\tthis.client = service.client;\n\t\tthis.runtime = runtime;\n\t\tthis.accountId = service.accountId ?? \"default\";\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\tmetadata: {\n\t\t\t\t\taccountId: this.accountId,\n\t\t\t\t},\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\tmetadata: {\n\t\t\t\t\taccountId: this.accountId,\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\tmetadata: {\n\t\t\t\t\t\t\taccountId: this.accountId,\n\t\t\t\t\t\t},\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\tconst payload: EventPayload & {\n\t\t\t\t\tmessage: Memory;\n\t\t\t\t\tcallback: HandlerCallback;\n\t\t\t\t\taccountId: string;\n\t\t\t\t} = {\n\t\t\t\t\truntime: this.runtime,\n\t\t\t\t\tmessage: memory,\n\t\t\t\t\tcallback,\n\t\t\t\t\tsource: \"discord\",\n\t\t\t\t\taccountId: this.accountId,\n\t\t\t\t};\n\t\t\t\tawait this.runtime.emitEvent(\n\t\t\t\t\t[EventType.VOICE_MESSAGE_RECEIVED],\n\t\t\t\t\tpayload,\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",
41
- "/**\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\tresolveStateDir,\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\n// TODO(plugin-discord multi-account handoff): thread accountId through the\n// browser/CDP auth partition before enabling multiple Discord local bridge\n// accounts. This stream only adds bot-token account routing.\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// ── 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 { sendHandlers?: unknown }).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 beta 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",
42
- "/**\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 { 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",
43
- "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\tMessage,\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// Minimal fixture for the fields MessageManager reads in this smoke test.\n\t\t\tinterface MockMessage {\n\t\t\t\t_cacheType: boolean;\n\t\t\t\t_patch: () => void;\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 fakeMessageFields = {\n\t\t\t\t_cacheType: true,\n\t\t\t\t_patch: () => undefined,\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} satisfies MockMessage;\n\t\t\tconst fakeMessage = Object.assign(\n\t\t\t\tObject.create(Message.prototype),\n\t\t\t\tfakeMessageFields,\n\t\t\t) as Message<boolean>;\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\tawait this.discordClient.messageManager.handleMessage(fakeMessage);\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",
44
- "/**\n * DiscordUserAccountScraperService\n *\n * Wraps the per-account Discord browser scraper as an Eliza Service so\n * downstream plugins (e.g. app-lifeops) can resolve it via\n * `runtime.getService('discord_user_account_scraper')` instead of importing\n * the raw module.\n */\n\nimport { type IAgentRuntime, Service } from \"@elizaos/core\";\nimport { DEFAULT_ACCOUNT_ID, normalizeAccountId } from \"../accounts\";\nimport {\n captureDiscordDeliveryStatus,\n closeDiscordTab,\n type DiscordMessageSearchResult,\n type DiscordTabProbe,\n discordBrowserWorkspaceAvailable,\n discordUserAccountPartitionFor,\n ensureDiscordTab,\n navigateDiscordTabToHome,\n probeDiscordTab,\n searchDiscordMessages,\n} from \"./discord-browser-scraper\";\nimport {\n type DiscordDesktopCdpStatus,\n getDiscordDesktopCdpStatus,\n relaunchDiscordDesktopForCdp,\n sendDiscordViaDesktopCdp,\n} from \"./discord-desktop-cdp\";\n\nexport const DISCORD_USER_ACCOUNT_SCRAPER_SERVICE_TYPE =\n \"discord_user_account_scraper\";\n\n/**\n * Public surface of the scraper. Each call carries a `accountId` so the\n * underlying browser partition is correctly scoped.\n */\nexport interface DiscordUserAccountScraper {\n isWorkspaceAvailable(env?: NodeJS.ProcessEnv): boolean;\n partitionFor(accountId?: string): string;\n ensureTab(args: {\n accountId?: string;\n existingTabId?: string | null;\n show?: boolean;\n env?: NodeJS.ProcessEnv;\n }): Promise<{ tabId: string; url: string }>;\n navigateTabHome(tabId: string, env?: NodeJS.ProcessEnv): Promise<void>;\n closeTab(tabId: string, env?: NodeJS.ProcessEnv): Promise<void>;\n probeTab(tabId: string, env?: NodeJS.ProcessEnv): Promise<DiscordTabProbe>;\n searchMessages(args: {\n tabId: string;\n query: string;\n channelId?: string;\n env?: NodeJS.ProcessEnv;\n }): Promise<DiscordMessageSearchResult[]>;\n captureDelivery(args: {\n tabId: string;\n env?: NodeJS.ProcessEnv;\n }): Promise<DiscordMessageSearchResult[]>;\n desktopCdpStatus(): Promise<DiscordDesktopCdpStatus>;\n relaunchDesktopForCdp(): Promise<DiscordDesktopCdpStatus>;\n sendViaDesktopCdp(args: {\n channelId: string;\n text: string;\n }): Promise<{ ok: boolean; error?: string }>;\n}\n\nclass DiscordUserAccountScraperImpl\n extends Service\n implements DiscordUserAccountScraper\n{\n static serviceType = DISCORD_USER_ACCOUNT_SCRAPER_SERVICE_TYPE;\n capabilityDescription =\n \"Drives a per-account Discord web app inside the Eliza browser workspace for DM probing, search, and delivery status capture.\";\n\n static async start(runtime: IAgentRuntime): Promise<Service> {\n return new DiscordUserAccountScraperImpl(runtime);\n }\n\n async stop(): Promise<void> {}\n\n isWorkspaceAvailable(env: NodeJS.ProcessEnv = process.env): boolean {\n return discordBrowserWorkspaceAvailable(env);\n }\n\n partitionFor(accountId?: string): string {\n return discordUserAccountPartitionFor(\n normalizeAccountId(accountId ?? DEFAULT_ACCOUNT_ID),\n );\n }\n\n ensureTab(args: {\n accountId?: string;\n existingTabId?: string | null;\n show?: boolean;\n env?: NodeJS.ProcessEnv;\n }): Promise<{ tabId: string; url: string }> {\n return ensureDiscordTab(args);\n }\n\n navigateTabHome(\n tabId: string,\n env: NodeJS.ProcessEnv = process.env,\n ): Promise<void> {\n return navigateDiscordTabToHome(tabId, env);\n }\n\n closeTab(tabId: string, env: NodeJS.ProcessEnv = process.env): Promise<void> {\n return closeDiscordTab(tabId, env);\n }\n\n probeTab(\n tabId: string,\n env: NodeJS.ProcessEnv = process.env,\n ): Promise<DiscordTabProbe> {\n return probeDiscordTab(tabId, env);\n }\n\n searchMessages(args: {\n tabId: string;\n query: string;\n channelId?: string;\n env?: NodeJS.ProcessEnv;\n }): Promise<DiscordMessageSearchResult[]> {\n return searchDiscordMessages(args);\n }\n\n captureDelivery(args: {\n tabId: string;\n env?: NodeJS.ProcessEnv;\n }): Promise<DiscordMessageSearchResult[]> {\n return captureDiscordDeliveryStatus(args);\n }\n\n desktopCdpStatus(): Promise<DiscordDesktopCdpStatus> {\n return getDiscordDesktopCdpStatus();\n }\n\n relaunchDesktopForCdp(): Promise<DiscordDesktopCdpStatus> {\n return relaunchDiscordDesktopForCdp();\n }\n\n sendViaDesktopCdp(args: {\n channelId: string;\n text: string;\n }): Promise<{ ok: boolean; error?: string }> {\n return sendDiscordViaDesktopCdp(args);\n }\n}\n\nexport { DiscordUserAccountScraperImpl };\n",
45
- "// Discord user-account scraper. Drives a logged-in Discord web app inside the\n// Eliza browser workspace, scoped to a per-account partition. The bot-token\n// path (discord.js) is for agent-owned messaging; this scraper exists for\n// owner-side inspection of a real user's Discord (DM previews, search,\n// delivery status) when a bot can't reach that scope.\nimport {\n closeBrowserWorkspaceTab,\n evaluateBrowserWorkspaceTab,\n isBrowserWorkspaceBridgeConfigured,\n listBrowserWorkspaceTabs,\n navigateBrowserWorkspaceTab,\n openBrowserWorkspaceTab,\n resolveBrowserWorkspaceConnectorPartition,\n showBrowserWorkspaceTab,\n} from \"@elizaos/plugin-browser\";\nimport type { BrowserBridgePageContext } from \"@elizaos/plugin-browser\";\nimport { DEFAULT_ACCOUNT_ID, normalizeAccountId } from \"../accounts\";\n\nexport const DISCORD_PROVIDER_ID = \"discord\";\n\nexport const DISCORD_APP_URL = \"https://discord.com/channels/@me\";\nconst DISCORD_APP_TITLE = \"Discord\";\nconst DISCORD_DM_PREVIEW_LIMIT = 5;\n\nfunction isDiscordHost(url: string): boolean {\n try {\n const u = new URL(url);\n return u.hostname === \"discord.com\" || u.hostname.endsWith(\".discord.com\");\n } catch {\n return false;\n }\n}\n\nexport interface DiscordTabIdentity {\n id: string | null;\n username: string | null;\n discriminator: string | null;\n}\n\nexport interface DiscordVisibleDmPreview {\n channelId: string | null;\n href: string | null;\n label: string;\n selected: boolean;\n unread: boolean;\n snippet: string | null;\n}\n\nexport interface DiscordDmInboxProbe {\n visible: boolean;\n count: number;\n selectedChannelId: string | null;\n previews: DiscordVisibleDmPreview[];\n}\n\nexport interface DiscordTabProbe {\n loggedIn: boolean;\n url: string | null;\n identity: DiscordTabIdentity;\n rawSnippet: string | null;\n dmInbox: DiscordDmInboxProbe;\n}\n\nfunction normalizeDiscordText(value: string | null | undefined): string | null {\n if (typeof value !== \"string\") return null;\n const normalized = value.replace(/\\s+/g, \" \").trim();\n return normalized.length > 0 ? normalized : null;\n}\n\nfunction isDiscordUrl(value: string | null | undefined): boolean {\n if (!value) return false;\n try {\n const url = new URL(value, \"https://discord.com\");\n return /(^|\\.)discord\\.com$/i.test(url.hostname);\n } catch {\n return false;\n }\n}\n\nfunction selectedDiscordDmChannelId(\n value: string | null | undefined,\n): string | null {\n const normalized = normalizeDiscordText(value);\n if (!normalized) return null;\n const match = normalized.match(/\\/channels\\/@me\\/([^/?#]+)/);\n return match?.[1] ?? null;\n}\n\nfunction isDiscordLoginPage(args: {\n url: string | null;\n title?: string | null;\n mainText?: string | null;\n formFields?: string[];\n}): boolean {\n const url = normalizeDiscordText(args.url);\n const title = normalizeDiscordText(args.title);\n const mainText = normalizeDiscordText(args.mainText);\n const formFields = (args.formFields ?? [])\n .map((field) => normalizeDiscordText(field))\n .filter((field): field is string => field !== null)\n .join(\" \");\n\n if (url?.includes(\"/login\") || url?.includes(\"/register\")) {\n return true;\n }\n\n const combined = [title, mainText, formFields]\n .filter((value): value is string => value !== null)\n .join(\" \");\n return /\\b(log ?in|sign ?in|register)\\b/i.test(combined) &&\n /\\bdiscord\\b/i.test(combined)\n ? true\n : /\\b(email|password)\\b/i.test(formFields);\n}\n\nfunction discordAnchorTextParts(anchor: Element): string[] {\n const values = new Set<string>();\n\n const push = (value: string | null) => {\n if (!value) return;\n if (/^\\d+$/.test(value)) return;\n values.add(value);\n };\n\n for (const node of anchor.querySelectorAll(\"span, div\")) {\n push(normalizeDiscordText(node.textContent));\n }\n\n push(normalizeDiscordText(anchor.getAttribute(\"aria-label\")));\n if (values.size === 0) {\n push(normalizeDiscordText(anchor.textContent));\n }\n\n return [...values];\n}\n\nfunction discordAnchorLabel(anchor: Element): string | null {\n const ariaLabel = normalizeDiscordText(anchor.getAttribute(\"aria-label\"));\n if (ariaLabel) {\n return (\n ariaLabel\n .split(\",\")\n .map((part) => normalizeDiscordText(part))\n .find((part) => part !== null && !/\\bunread\\b/i.test(part)) ?? ariaLabel\n );\n }\n\n const parts = discordAnchorTextParts(anchor);\n return (\n parts.find(\n (part) =>\n !/\\bunread\\b/i.test(part) &&\n !/^(active now|voice connected|mutual friends?)$/i.test(part),\n ) ?? null\n );\n}\n\nfunction discordAnchorSnippet(\n anchor: Element,\n label: string | null,\n): string | null {\n const parts = discordAnchorTextParts(anchor);\n return (\n parts.find(\n (part) =>\n part !== label &&\n !/\\bunread\\b/i.test(part) &&\n !/^(active now|voice connected|mutual friends?)$/i.test(part),\n ) ?? null\n );\n}\n\nfunction extractDiscordDmPreviews(\n document: Document,\n selectedChannelId: string | null,\n): DiscordVisibleDmPreview[] {\n const previews: DiscordVisibleDmPreview[] = [];\n const seen = new Set<string>();\n\n for (const anchor of document.querySelectorAll('a[href^=\"/channels/@me/\"]')) {\n const href = normalizeDiscordText(anchor.getAttribute(\"href\"));\n if (!href || href === \"/channels/@me\") continue;\n\n const channelId = selectedDiscordDmChannelId(href);\n const dedupeKey = channelId ?? href;\n if (seen.has(dedupeKey)) continue;\n seen.add(dedupeKey);\n\n const label = discordAnchorLabel(anchor) ?? channelId ?? \"Direct message\";\n const unreadSignal = [\n normalizeDiscordText(anchor.getAttribute(\"aria-label\")),\n normalizeDiscordText(anchor.textContent),\n ]\n .filter((value): value is string => value !== null)\n .join(\" \");\n\n previews.push({\n channelId,\n href,\n label,\n selected:\n (channelId !== null && channelId === selectedChannelId) ||\n anchor.getAttribute(\"aria-current\") === \"page\" ||\n anchor.getAttribute(\"aria-selected\") === \"true\",\n unread:\n /\\bunread\\b|\\bnew messages?\\b/i.test(unreadSignal) ||\n Boolean(\n anchor.querySelector('[aria-label*=\"unread\" i], [class*=\"unread\"]'),\n ),\n snippet: discordAnchorSnippet(anchor, label),\n });\n }\n\n return previews;\n}\n\nexport function emptyDiscordDmInboxProbe(): DiscordDmInboxProbe {\n return {\n visible: false,\n count: 0,\n selectedChannelId: null,\n previews: [],\n };\n}\n\nfunction emptyDiscordTabProbe(url: string | null = null): DiscordTabProbe {\n return {\n loggedIn: false,\n url,\n identity: {\n id: null,\n username: null,\n discriminator: null,\n },\n rawSnippet: null,\n dmInbox: emptyDiscordDmInboxProbe(),\n };\n}\n\nexport function probeDiscordCapturedPage(\n page:\n | Pick<\n BrowserBridgePageContext,\n \"url\" | \"title\" | \"mainText\" | \"links\" | \"forms\"\n >\n | {\n url: string | null;\n title?: string | null;\n mainText?: string | null;\n links?: Array<{ text: string; href: string }>;\n forms?: Array<{ action: string | null; fields: string[] }>;\n },\n): DiscordTabProbe {\n const safeUrl = normalizeDiscordText(page.url);\n if (!safeUrl || !isDiscordUrl(safeUrl)) {\n return emptyDiscordTabProbe(safeUrl ?? null);\n }\n\n const forms: Array<{ action: string | null; fields: string[] }> =\n page.forms ?? [];\n const formFields = forms.flatMap((form) => form.fields);\n if (\n isDiscordLoginPage({\n url: safeUrl,\n title: page.title ?? null,\n mainText: page.mainText ?? null,\n formFields,\n })\n ) {\n return {\n ...emptyDiscordTabProbe(safeUrl),\n rawSnippet:\n normalizeDiscordText(page.mainText ?? null)?.slice(0, 160) ?? null,\n };\n }\n\n const selectedChannelId = selectedDiscordDmChannelId(safeUrl);\n const previews: DiscordVisibleDmPreview[] = [];\n const seen = new Set<string>();\n for (const candidate of page.links ?? []) {\n if (!isDiscordUrl(candidate.href)) continue;\n const href = normalizeDiscordText(candidate.href);\n if (!href?.includes(\"/channels/@me/\")) continue;\n const channelId = selectedDiscordDmChannelId(href);\n const dedupeKey = channelId ?? href;\n if (seen.has(dedupeKey)) continue;\n seen.add(dedupeKey);\n\n previews.push({\n channelId,\n href,\n label:\n normalizeDiscordText(candidate.text) ?? channelId ?? \"Direct message\",\n selected: channelId !== null && channelId === selectedChannelId,\n unread: false,\n snippet: null,\n });\n }\n\n return {\n loggedIn: true,\n url: safeUrl,\n identity: {\n id: null,\n username: null,\n discriminator: null,\n },\n rawSnippet:\n normalizeDiscordText(page.mainText ?? null)?.slice(0, 160) ?? null,\n dmInbox: {\n visible: previews.length > 0 || safeUrl.includes(\"/channels/@me\"),\n count: previews.length,\n selectedChannelId,\n previews: previews.slice(0, DISCORD_DM_PREVIEW_LIMIT),\n },\n };\n}\n\nexport function probeDiscordDocumentState(\n document: Document,\n url: string | null,\n): DiscordTabProbe {\n try {\n const safeUrl = normalizeDiscordText(url);\n const atLogin =\n (safeUrl?.includes(\"/login\") ?? false) ||\n (safeUrl?.includes(\"/register\") ?? false) ||\n !!document.querySelector('input[name=\"email\"], input[type=\"email\"]');\n if (atLogin) {\n return emptyDiscordTabProbe(safeUrl ?? null);\n }\n\n const guildsNav = document.querySelector('[data-list-id=\"guildsnav\"]');\n const sidebar =\n guildsNav ||\n document.querySelector('nav[aria-label*=\"Servers\" i]') ||\n document.querySelector('[class*=\"guilds-\"]') ||\n document.querySelector('a[href^=\"/channels/@me/\"]');\n if (!sidebar) {\n return emptyDiscordTabProbe(safeUrl ?? null);\n }\n\n const panel =\n document.querySelector('section[aria-label*=\"User area\" i]') ||\n document.querySelector('[class*=\"panelTitleContainer\"]') ||\n document.querySelector('[class*=\"nameTag\"]')?.parentElement ||\n null;\n const nameEl =\n panel?.querySelector('[class*=\"nameTag\"] [class*=\"name-\"]') ||\n panel?.querySelector('[class*=\"name-\"]') ||\n document.querySelector('[class*=\"nameTag\"] [class*=\"name-\"]') ||\n document.querySelector('[class*=\"nameTag\"]');\n const tagEl =\n panel?.querySelector('[class*=\"nameTag\"] [class*=\"discrim\"]') ||\n document.querySelector('[class*=\"nameTag\"] [class*=\"discrim\"]');\n const username = normalizeDiscordText(nameEl?.textContent ?? null);\n const discriminator =\n normalizeDiscordText(tagEl?.textContent ?? null)?.replace(/^#/, \"\") ??\n null;\n const snippet =\n normalizeDiscordText(panel?.textContent ?? null)?.slice(0, 160) ?? null;\n const selectedChannelId = selectedDiscordDmChannelId(safeUrl ?? null);\n const previews = extractDiscordDmPreviews(document, selectedChannelId);\n\n return {\n loggedIn: true,\n url: safeUrl ?? null,\n identity: {\n id: null,\n username,\n discriminator,\n },\n rawSnippet: snippet,\n dmInbox: {\n visible: previews.length > 0 || selectedChannelId !== null,\n count: previews.length,\n selectedChannelId,\n previews: previews.slice(0, DISCORD_DM_PREVIEW_LIMIT),\n },\n };\n } catch (error) {\n return {\n ...emptyDiscordTabProbe(null),\n rawSnippet: String(error),\n };\n }\n}\n\nexport function buildDiscordProbeScript(): string {\n return `(() => {\n const DISCORD_DM_PREVIEW_LIMIT = ${DISCORD_DM_PREVIEW_LIMIT};\n const normalizeDiscordText = ${normalizeDiscordText.toString()};\n const selectedDiscordDmChannelId = ${selectedDiscordDmChannelId.toString()};\n const discordAnchorTextParts = ${discordAnchorTextParts.toString()};\n const discordAnchorLabel = ${discordAnchorLabel.toString()};\n const discordAnchorSnippet = ${discordAnchorSnippet.toString()};\n const extractDiscordDmPreviews = ${extractDiscordDmPreviews.toString()};\n const emptyDiscordDmInboxProbe = ${emptyDiscordDmInboxProbe.toString()};\n const emptyDiscordTabProbe = ${emptyDiscordTabProbe.toString()};\n const probeDiscordDocumentState = ${probeDiscordDocumentState.toString()};\n return probeDiscordDocumentState(document, window.location.href || null);\n })();`;\n}\n\nexport function discordBrowserWorkspaceAvailable(\n env: NodeJS.ProcessEnv = process.env,\n): boolean {\n return isBrowserWorkspaceBridgeConfigured(env);\n}\n\n/**\n * Resolve the browser-workspace partition for a Discord user-account scrape\n * session. Each user account gets its own partition so cookies are not shared\n * across accounts. Single-account env-only deployments use DEFAULT_ACCOUNT_ID.\n */\nexport function discordUserAccountPartitionFor(accountId?: string): string {\n return resolveBrowserWorkspaceConnectorPartition(\n DISCORD_PROVIDER_ID,\n normalizeAccountId(accountId ?? DEFAULT_ACCOUNT_ID),\n );\n}\n\nasync function findTabByIdOrPartition(\n tabId: string | null,\n partition: string,\n env: NodeJS.ProcessEnv,\n): Promise<{ id: string; url: string } | null> {\n const tabs = await listBrowserWorkspaceTabs(env);\n if (tabId) {\n const hit = tabs.find((tab) => tab.id === tabId);\n if (hit) return { id: hit.id, url: hit.url };\n }\n const byPartition = tabs.find(\n (tab) =>\n tab.partition === partition &&\n typeof tab.url === \"string\" &&\n isDiscordHost(tab.url),\n );\n if (byPartition) return { id: byPartition.id, url: byPartition.url };\n return null;\n}\n\nexport async function ensureDiscordTab(args: {\n /**\n * Connector account ID. The browser partition is keyed off this so cookies\n * stay isolated across multiple Discord user accounts. Defaults to\n * DEFAULT_ACCOUNT_ID when omitted.\n */\n accountId?: string;\n existingTabId?: string | null;\n show?: boolean;\n env?: NodeJS.ProcessEnv;\n}): Promise<{ tabId: string; url: string }> {\n const env = args.env ?? process.env;\n if (!discordBrowserWorkspaceAvailable(env)) {\n throw new Error(\n \"Discord connector requires the Eliza Desktop Browser workspace.\",\n );\n }\n\n const partition = discordUserAccountPartitionFor(args.accountId);\n const existing = await findTabByIdOrPartition(\n args.existingTabId ?? null,\n partition,\n env,\n );\n\n if (existing) {\n if (args.show) {\n await showBrowserWorkspaceTab(existing.id, env);\n }\n return { tabId: existing.id, url: existing.url };\n }\n\n const tab = await openBrowserWorkspaceTab(\n {\n url: DISCORD_APP_URL,\n partition,\n kind: \"internal\",\n title: DISCORD_APP_TITLE,\n show: args.show ?? true,\n },\n env,\n );\n return { tabId: tab.id, url: tab.url };\n}\n\nexport async function navigateDiscordTabToHome(\n tabId: string,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<void> {\n await navigateBrowserWorkspaceTab({ id: tabId, url: DISCORD_APP_URL }, env);\n}\n\nexport async function closeDiscordTab(\n tabId: string,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<void> {\n await closeBrowserWorkspaceTab(tabId, env);\n}\n\n/**\n * Evaluate a probe inside the Discord tab to determine login state and\n * extract the current user. Returns `loggedIn: false` when the tab is on\n * the login screen, when the tab has not finished loading the app shell,\n * or when the selectors fail — never throws.\n */\nexport async function probeDiscordTab(\n tabId: string,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<DiscordTabProbe> {\n const result = (await evaluateBrowserWorkspaceTab(\n { id: tabId, script: buildDiscordProbeScript() },\n env,\n )) as DiscordTabProbe | null | undefined;\n\n if (!result || typeof result !== \"object\") {\n return emptyDiscordTabProbe(null);\n }\n return {\n ...emptyDiscordTabProbe(null),\n ...result,\n identity: {\n ...emptyDiscordTabProbe(null).identity,\n ...(result.identity ?? {}),\n },\n dmInbox: result.dmInbox ?? emptyDiscordDmInboxProbe(),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Message search via browser-DOM eval\n// ---------------------------------------------------------------------------\n\nexport interface DiscordMessageSearchResult {\n id: string | null;\n content: string;\n authorName: string | null;\n guildId: string | null;\n channelId: string | null;\n timestamp: string | null;\n /** Delivery status indicator derived from DOM state (partial). */\n deliveryStatus: \"sent\" | \"sending\" | \"failed\" | \"unknown\";\n}\n\nfunction normalizeDiscordDeliveryStatus(\n value: unknown,\n): DiscordMessageSearchResult[\"deliveryStatus\"] | null {\n return value === \"sent\" ||\n value === \"sending\" ||\n value === \"failed\" ||\n value === \"unknown\"\n ? value\n : null;\n}\n\nfunction normalizeDiscordResultText(value: unknown): string | null {\n return typeof value === \"string\" ? normalizeDiscordText(value) : null;\n}\n\nfunction normalizeDiscordMessageSearchResults(\n value: unknown,\n operation: string,\n): DiscordMessageSearchResult[] {\n if (!Array.isArray(value)) {\n throw new Error(`Discord ${operation} returned an invalid result.`);\n }\n\n return value.map((item, index) => {\n if (!item || typeof item !== \"object\") {\n throw new Error(`Discord ${operation} result ${index} is invalid.`);\n }\n const record = item as Record<string, unknown>;\n if (typeof record.content !== \"string\") {\n throw new Error(\n `Discord ${operation} result ${index} is missing content.`,\n );\n }\n const deliveryStatus = normalizeDiscordDeliveryStatus(\n record.deliveryStatus,\n );\n if (!deliveryStatus) {\n throw new Error(\n `Discord ${operation} result ${index} has invalid delivery status.`,\n );\n }\n return {\n id: normalizeDiscordResultText(record.id),\n content: record.content,\n authorName: normalizeDiscordResultText(record.authorName),\n guildId: normalizeDiscordResultText(record.guildId),\n channelId: normalizeDiscordResultText(record.channelId),\n timestamp: normalizeDiscordResultText(record.timestamp),\n deliveryStatus,\n };\n });\n}\n\nfunction buildDiscordSearchResultsScript(): string {\n return `(() => {\n const results = [];\n const routeMatch = window.location.href.match(/\\\\/channels\\\\/([^/?#]+)\\\\/([^/?#]+)/);\n const guildId = routeMatch && routeMatch[1] !== \"@me\" ? routeMatch[1] : null;\n const channelId = routeMatch ? routeMatch[2] : null;\n const containers = Array.from(\n document.querySelectorAll(\n '[class*=\"searchResultMessage\"], [class*=\"search-result-message\"]'\n )\n );\n for (const container of containers) {\n const contentEl =\n container.querySelector('[id^=\"message-content-\"]') ||\n container.querySelector('[class*=\"messageContent\"]');\n const content = contentEl ? (contentEl.textContent || \"\").trim() : \"\";\n\n const authorEl =\n container.querySelector('[class*=\"username-\"]') ||\n container.querySelector('[class*=\"author-\"]');\n const authorName = authorEl ? (authorEl.textContent || \"\").trim() : null;\n\n const msgEl = container.closest('[data-list-item-id]') ||\n container.querySelector('[id^=\"chat-messages-\"]');\n const rawId = msgEl\n ? (msgEl.getAttribute('data-list-item-id') || msgEl.id || \"\")\n : \"\";\n const idMatch = rawId.match(/\\\\d{17,19}/);\n const id = idMatch ? idMatch[0] : null;\n\n const timestampEl = container.querySelector('time[datetime]');\n const timestamp = timestampEl\n ? timestampEl.getAttribute('datetime')\n : null;\n\n results.push({ id, content, authorName, guildId, channelId, timestamp, deliveryStatus: \"unknown\" });\n }\n return results;\n })();`;\n}\n\n/**\n * Trigger Discord's native in-app search using the keyboard shortcut, wait\n * for results to render, then scrape them via DOM eval.\n *\n * The search uses Discord's own full-text index — no client-side filtering.\n * Results are scoped to the currently open DM or channel when a `channelId`\n * scope is provided (by navigating to that channel first), or global when\n * no scope is given.\n *\n * Because this relies on the browser workspace eval bridge, it requires a\n * live Discord tab (`tabId`).\n */\nexport async function searchDiscordMessages(args: {\n tabId: string;\n query: string;\n channelId?: string;\n env?: NodeJS.ProcessEnv;\n}): Promise<DiscordMessageSearchResult[]> {\n const env = args.env ?? process.env;\n const query = args.query.trim();\n if (query.length === 0) {\n throw new Error(\"Discord search query must not be empty.\");\n }\n\n if (args.channelId) {\n const channelUrl = `https://discord.com/channels/@me/${args.channelId}`;\n await navigateBrowserWorkspaceTab({ id: args.tabId, url: channelUrl }, env);\n // Allow the navigation to settle before searching.\n await new Promise((resolve) => setTimeout(resolve, 800));\n }\n\n // Build a script that uses the Discord search bar (Ctrl+K / Ctrl+F) via\n // the internal store — the Discord web app exposes a Flux-like dispatcher\n // that can be reached via `webpackChunkdiscord_app`.\n const searchScript = `(() => {\n const query = ${JSON.stringify(query)};\n try {\n // Walk webpack chunk to find the search action dispatcher.\n const chunks = window.webpackChunkdiscord_app;\n if (!chunks) return { injected: false };\n let SearchActions;\n for (const [, factories] of chunks) {\n for (const factory of Object.values(factories)) {\n try {\n const mod = {};\n factory({}, mod, { c: {}, d: (m, e) => { Object.assign(m, e); }, n: (m) => () => m });\n if (mod.searchMessages) { SearchActions = mod; break; }\n } catch { /* non-factory */ }\n }\n if (SearchActions) break;\n }\n if (!SearchActions?.searchMessages) return { injected: false };\n SearchActions.searchMessages({ query });\n return { injected: true };\n } catch(e) {\n return { injected: false, error: String(e) };\n }\n })();`;\n\n const injected = await evaluateBrowserWorkspaceTab(\n { id: args.tabId, script: searchScript },\n env,\n );\n if (\n !injected ||\n typeof injected !== \"object\" ||\n (injected as { injected?: unknown }).injected !== true\n ) {\n const error =\n typeof (injected as { error?: unknown } | null)?.error === \"string\"\n ? ` ${(injected as { error: string }).error}`\n : \"\";\n throw new Error(`Discord search injection failed.${error}`);\n }\n\n // Wait for Discord's search results panel to render.\n await new Promise((resolve) => setTimeout(resolve, 1200));\n\n const raw = (await evaluateBrowserWorkspaceTab(\n { id: args.tabId, script: buildDiscordSearchResultsScript() },\n env,\n )) as DiscordMessageSearchResult[] | null | undefined;\n\n return normalizeDiscordMessageSearchResults(raw, \"search\");\n}\n\n// ---------------------------------------------------------------------------\n// Delivery status capture via DOM eval\n// ---------------------------------------------------------------------------\n\n/**\n * Read the delivery status of recently sent messages visible in the currently\n * open Discord channel. Discord renders delivery indicators in the DOM:\n * - No indicator: delivered (server acknowledged).\n * - `[class*=\"sending\"]`: sending in progress.\n * - `[class*=\"failed\"]` / `[aria-label*=\"failed\" i]`: send failed.\n *\n * This is inherently partial — only messages currently rendered in the\n * viewport can be inspected.\n */\nexport async function captureDiscordDeliveryStatus(args: {\n tabId: string;\n env?: NodeJS.ProcessEnv;\n}): Promise<DiscordMessageSearchResult[]> {\n const env = args.env ?? process.env;\n const script = `(() => {\n const results = [];\n const routeMatch = window.location.href.match(/\\\\/channels\\\\/([^/?#]+)\\\\/([^/?#]+)/);\n const guildId = routeMatch && routeMatch[1] !== \"@me\" ? routeMatch[1] : null;\n const channelId = routeMatch ? routeMatch[2] : null;\n const messages = Array.from(\n document.querySelectorAll('[class*=\"message-\"] [id^=\"message-content-\"], [id^=\"chat-messages-\"] li')\n );\n for (const el of messages) {\n const contentEl = el.querySelector\n ? el.querySelector('[id^=\"message-content-\"]') || el\n : el;\n const content = (contentEl.textContent || \"\").trim();\n\n const rawId = (el.getAttribute('data-list-item-id') || el.id || \"\");\n const idMatch = rawId.match(/\\\\d{17,19}/);\n const id = idMatch ? idMatch[0] : null;\n\n const timestampEl = el.querySelector('time[datetime]');\n const timestamp = timestampEl ? timestampEl.getAttribute('datetime') : null;\n\n const isSending = !!el.querySelector('[class*=\"sending\"]');\n const isFailed = !!el.querySelector('[class*=\"failed\"]') ||\n !!el.querySelector('[aria-label*=\"failed\" i]');\n const deliveryStatus = isFailed ? 'failed' : isSending ? 'sending' : 'sent';\n\n results.push({ id, content, authorName: null, guildId, channelId, timestamp, deliveryStatus });\n }\n return results;\n })();`;\n\n const raw = (await evaluateBrowserWorkspaceTab(\n { id: args.tabId, script },\n env,\n )) as DiscordMessageSearchResult[] | null | undefined;\n\n return normalizeDiscordMessageSearchResults(raw, \"delivery status capture\");\n}\n",
46
- "// Discord Desktop CDP fallback. Used when the Eliza browser-workspace tab\n// path isn't available but a local Discord Desktop is running with the\n// Chrome DevTools Protocol port exposed (e.g. relaunched by Eliza). Sends\n// and probes go through the same scraper primitives but over CDP rather\n// than the workspace bridge.\nimport { execFile } from \"node:child_process\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport {\n buildDiscordProbeScript,\n type DiscordDmInboxProbe,\n type DiscordTabProbe,\n type DiscordVisibleDmPreview,\n} from \"./discord-browser-scraper\";\n\nconst DEFAULT_DISCORD_DESKTOP_CDP_PORT = 9224;\nconst DISCORD_DESKTOP_CDP_HOST = \"127.0.0.1\";\nconst DISCORD_DESKTOP_QUIT_TIMEOUT_MS = 10_000;\nconst DISCORD_DESKTOP_READY_TIMEOUT_MS = 20_000;\nconst DISCORD_DESKTOP_POLL_INTERVAL_MS = 500;\nconst DISCORD_DESKTOP_FETCH_TIMEOUT_MS = 900;\nconst DISCORD_DESKTOP_EVALUATE_TIMEOUT_MS = 2_500;\n\ninterface CommandResult {\n stdout: string;\n stderr: string;\n}\n\ninterface CdpVersionResponse {\n Browser?: string;\n webSocketDebuggerUrl?: string;\n}\n\ninterface CdpTarget {\n id: string;\n type: string;\n title: string;\n url: string;\n webSocketDebuggerUrl: string | null;\n}\n\ninterface CdpRpcResponse {\n id?: number;\n error?: {\n message?: string;\n };\n result?: {\n result?: {\n value?: unknown;\n description?: string;\n };\n };\n}\n\nexport interface DiscordDesktopCdpStatus {\n supported: boolean;\n platform: NodeJS.Platform;\n port: number;\n appRunning: boolean;\n cdpAvailable: boolean;\n browserVersion: string | null;\n targetUrl: string | null;\n targetTitle: string | null;\n webSocketDebuggerUrl: string | null;\n probe: DiscordTabProbe | null;\n lastError: string | null;\n}\n\nfunction configuredDiscordDesktopCdpPort(\n env: NodeJS.ProcessEnv = process.env,\n): number {\n const raw = env.ELIZA_DISCORD_DESKTOP_CDP_PORT?.trim();\n if (!raw) {\n return DEFAULT_DISCORD_DESKTOP_CDP_PORT;\n }\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed > 0 && parsed < 65_536\n ? parsed\n : DEFAULT_DISCORD_DESKTOP_CDP_PORT;\n}\n\nfunction discordDesktopCdpDisabled(\n env: NodeJS.ProcessEnv = process.env,\n): boolean {\n return (\n env.ELIZA_DISABLE_DISCORD_DESKTOP_CDP === \"1\" ||\n env.ELIZA_DISABLE_DISCORD_DESKTOP_CDP === \"1\"\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\";\n}\n\nfunction execFileAsync(\n file: string,\n args: string[],\n timeoutMs: number,\n): Promise<CommandResult> {\n return new Promise((resolve, reject) => {\n execFile(file, args, { timeout: timeoutMs }, (error, stdout, stderr) => {\n if (error) {\n reject(error);\n return;\n }\n resolve({\n stdout: String(stdout),\n stderr: String(stderr),\n });\n });\n });\n}\n\nasync function discordAppRunning(): Promise<boolean> {\n try {\n await execFileAsync(\"/usr/bin/pgrep\", [\"-x\", \"Discord\"], 1_000);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function fetchJson<T>(url: string, timeoutMs: number): Promise<T> {\n const response = await fetch(url, {\n signal: AbortSignal.timeout(timeoutMs),\n });\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n return (await response.json()) as T;\n}\n\nfunction normalizeCdpTarget(value: unknown): CdpTarget | null {\n if (!isRecord(value)) {\n return null;\n }\n const id = typeof value.id === \"string\" ? value.id : \"\";\n const type = typeof value.type === \"string\" ? value.type : \"\";\n const title = typeof value.title === \"string\" ? value.title : \"\";\n const url = typeof value.url === \"string\" ? value.url : \"\";\n const webSocketDebuggerUrl =\n typeof value.webSocketDebuggerUrl === \"string\"\n ? value.webSocketDebuggerUrl\n : null;\n if (!id || !type) {\n return null;\n }\n return { id, type, title, url, webSocketDebuggerUrl };\n}\n\nfunction isDiscordHost(url: string): boolean {\n try {\n const hostname = new URL(url).hostname;\n return hostname === \"discord.com\" || hostname.endsWith(\".discord.com\");\n } catch {\n return false;\n }\n}\n\nfunction pickDiscordTarget(targets: CdpTarget[]): CdpTarget | null {\n const pageTargets = targets.filter(\n (target) => target.type === \"page\" && target.webSocketDebuggerUrl,\n );\n return (\n pageTargets.find(\n (target) => isDiscordHost(target.url) || /discord/i.test(target.title),\n ) ??\n pageTargets[0] ??\n null\n );\n}\n\nfunction normalizeString(value: unknown): string | null {\n return typeof value === \"string\" && value.trim().length > 0 ? value : null;\n}\n\nfunction normalizeDmPreview(value: unknown): DiscordVisibleDmPreview | null {\n if (!isRecord(value)) {\n return null;\n }\n const label = normalizeString(value.label);\n if (!label) {\n return null;\n }\n return {\n channelId: normalizeString(value.channelId),\n href: normalizeString(value.href),\n label,\n selected: value.selected === true,\n unread: value.unread === true,\n snippet: normalizeString(value.snippet),\n };\n}\n\nfunction normalizeDmInbox(value: unknown): DiscordDmInboxProbe {\n const record = isRecord(value) ? value : {};\n const previews = Array.isArray(record.previews)\n ? record.previews\n .map((preview) => normalizeDmPreview(preview))\n .filter(\n (preview): preview is DiscordVisibleDmPreview => preview !== null,\n )\n : [];\n return {\n visible: record.visible === true,\n count: typeof record.count === \"number\" ? record.count : previews.length,\n selectedChannelId: normalizeString(record.selectedChannelId),\n previews,\n };\n}\n\nfunction normalizeDiscordProbe(value: unknown): DiscordTabProbe | null {\n if (!isRecord(value)) {\n return null;\n }\n const identity = isRecord(value.identity) ? value.identity : {};\n return {\n loggedIn: value.loggedIn === true,\n url: normalizeString(value.url),\n identity: {\n id: normalizeString(identity.id),\n username: normalizeString(identity.username),\n discriminator: normalizeString(identity.discriminator),\n },\n rawSnippet: normalizeString(value.rawSnippet),\n dmInbox: normalizeDmInbox(value.dmInbox),\n };\n}\n\nasync function evaluateDiscordProbe(\n webSocketDebuggerUrl: string,\n): Promise<DiscordTabProbe> {\n const WebSocketConstructor = globalThis.WebSocket;\n if (!WebSocketConstructor) {\n throw new Error(\"WebSocket is not available in this runtime\");\n }\n\n return new Promise((resolve, reject) => {\n const requestId = 1;\n const socket = new WebSocketConstructor(webSocketDebuggerUrl);\n const timeout = setTimeout(() => {\n socket.close();\n reject(new Error(\"Timed out while probing Discord desktop\"));\n }, DISCORD_DESKTOP_EVALUATE_TIMEOUT_MS);\n\n const cleanup = () => {\n clearTimeout(timeout);\n socket.close();\n };\n const failProbe = (error: Error) => {\n cleanup();\n reject(error);\n };\n const resolveProbe = (probe: DiscordTabProbe) => {\n cleanup();\n resolve(probe);\n };\n\n socket.addEventListener(\"open\", () => {\n socket.send(\n JSON.stringify({\n id: requestId,\n method: \"Runtime.evaluate\",\n params: {\n expression: buildDiscordProbeScript(),\n awaitPromise: true,\n returnByValue: true,\n },\n }),\n );\n });\n\n socket.addEventListener(\"message\", (event: MessageEvent) => {\n let payload: CdpRpcResponse;\n try {\n payload = JSON.parse(String(event.data)) as CdpRpcResponse;\n } catch {\n return;\n }\n if (payload.id !== requestId) {\n return;\n }\n if (payload.error) {\n failProbe(\n new Error(payload.error.message ?? \"Discord desktop probe failed\"),\n );\n return;\n }\n const probe = normalizeDiscordProbe(payload.result?.result?.value);\n if (!probe) {\n failProbe(\n new Error(\n payload.result?.result?.description ??\n \"Discord desktop returned an invalid probe\",\n ),\n );\n return;\n }\n resolveProbe(probe);\n });\n\n socket.addEventListener(\"error\", () => {\n failProbe(new Error(\"Discord desktop CDP websocket failed\"));\n });\n });\n}\n\nexport async function getDiscordDesktopCdpStatus(\n env: NodeJS.ProcessEnv = process.env,\n): Promise<DiscordDesktopCdpStatus> {\n const platform = process.platform;\n const port = configuredDiscordDesktopCdpPort(env);\n if (discordDesktopCdpDisabled(env)) {\n return {\n supported: platform === \"darwin\",\n platform,\n port,\n appRunning: false,\n cdpAvailable: false,\n browserVersion: null,\n targetUrl: null,\n targetTitle: null,\n webSocketDebuggerUrl: null,\n probe: null,\n lastError: \"Discord Desktop CDP disabled by environment.\",\n };\n }\n const appRunning = platform === \"darwin\" ? await discordAppRunning() : false;\n if (platform !== \"darwin\") {\n return {\n supported: false,\n platform,\n port,\n appRunning,\n cdpAvailable: false,\n browserVersion: null,\n targetUrl: null,\n targetTitle: null,\n webSocketDebuggerUrl: null,\n probe: null,\n lastError: \"Discord Desktop control is currently supported on macOS.\",\n };\n }\n\n const baseUrl = `http://${DISCORD_DESKTOP_CDP_HOST}:${port}`;\n try {\n const [version, rawTargets] = await Promise.all([\n fetchJson<CdpVersionResponse>(\n `${baseUrl}/json/version`,\n DISCORD_DESKTOP_FETCH_TIMEOUT_MS,\n ),\n fetchJson<unknown[]>(\n `${baseUrl}/json/list`,\n DISCORD_DESKTOP_FETCH_TIMEOUT_MS,\n ).catch(() => []),\n ]);\n const targets = rawTargets\n .map((target) => normalizeCdpTarget(target))\n .filter((target): target is CdpTarget => target !== null);\n const target = pickDiscordTarget(targets);\n let probe: DiscordTabProbe | null = null;\n let lastError: string | null = null;\n if (target?.webSocketDebuggerUrl) {\n try {\n probe = await evaluateDiscordProbe(target.webSocketDebuggerUrl);\n } catch (error) {\n lastError = error instanceof Error ? error.message : String(error);\n }\n }\n\n return {\n supported: true,\n platform,\n port,\n appRunning,\n cdpAvailable: true,\n browserVersion: version.Browser ?? null,\n targetUrl: target?.url ?? null,\n targetTitle: target?.title ?? null,\n webSocketDebuggerUrl:\n target?.webSocketDebuggerUrl ?? version.webSocketDebuggerUrl ?? null,\n probe,\n lastError,\n };\n } catch (error) {\n return {\n supported: true,\n platform,\n port,\n appRunning,\n cdpAvailable: false,\n browserVersion: null,\n targetUrl: null,\n targetTitle: null,\n webSocketDebuggerUrl: null,\n probe: null,\n lastError: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nasync function waitForDiscordToQuit(): Promise<void> {\n const deadline = Date.now() + DISCORD_DESKTOP_QUIT_TIMEOUT_MS;\n while (await discordAppRunning()) {\n if (Date.now() >= deadline) {\n throw new Error(\"Discord did not quit before the relaunch timeout.\");\n }\n await delay(250);\n }\n}\n\nasync function waitForDiscordCdpReady(\n env: NodeJS.ProcessEnv,\n): Promise<DiscordDesktopCdpStatus> {\n const deadline = Date.now() + DISCORD_DESKTOP_READY_TIMEOUT_MS;\n let latest = await getDiscordDesktopCdpStatus(env);\n while (!latest.cdpAvailable) {\n if (Date.now() >= deadline) {\n throw new Error(\n latest.lastError ??\n \"Discord did not expose a desktop control endpoint before the timeout.\",\n );\n }\n await delay(DISCORD_DESKTOP_POLL_INTERVAL_MS);\n latest = await getDiscordDesktopCdpStatus(env);\n }\n return latest;\n}\n\n/**\n * Send a message to a Discord channel by driving the user's Discord Desktop\n * app through CDP. The local-execution path uses the user's own Discord\n * client, so the send appears as if the user typed it. This avoids the bot\n * REST path returning \"Missing Access\" on channels the bot is not a member\n * of (which is most DMs), and matches the same trust model as reads.\n */\nexport async function sendDiscordViaDesktopCdp(args: {\n channelId: string;\n text: string;\n env?: NodeJS.ProcessEnv;\n}): Promise<{\n ok: boolean;\n navigatedTo: string | null;\n error: string | null;\n}> {\n const status = await getDiscordDesktopCdpStatus(args.env);\n if (!status.cdpAvailable || !status.webSocketDebuggerUrl) {\n return {\n ok: false,\n navigatedTo: null,\n error:\n status.lastError ??\n \"Discord Desktop CDP is not available; cannot send.\",\n };\n }\n\n const channelUrl = `https://discord.com/channels/@me/${args.channelId}`;\n try {\n await runDiscordCdpSendScript({\n webSocketDebuggerUrl: status.webSocketDebuggerUrl,\n channelUrl,\n text: args.text,\n });\n return { ok: true, navigatedTo: channelUrl, error: null };\n } catch (error) {\n return {\n ok: false,\n navigatedTo: channelUrl,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\ninterface CdpRpcEnvelope {\n id: number;\n method: string;\n params?: unknown;\n}\n\nasync function runDiscordCdpSendScript(args: {\n webSocketDebuggerUrl: string;\n channelUrl: string;\n text: string;\n}): Promise<void> {\n const WebSocketConstructor = globalThis.WebSocket;\n if (!WebSocketConstructor) {\n throw new Error(\"WebSocket is not available in this runtime\");\n }\n\n await new Promise<void>((resolve, reject) => {\n const socket = new WebSocketConstructor(args.webSocketDebuggerUrl);\n let nextId = 1;\n const pending = new Map<\n number,\n { resolve: (value: unknown) => void; reject: (error: Error) => void }\n >();\n\n const overall = setTimeout(() => {\n cleanup();\n reject(new Error(\"Discord CDP send timed out\"));\n }, 25_000);\n\n const cleanup = () => {\n clearTimeout(overall);\n for (const { reject: rej } of pending.values()) {\n rej(new Error(\"Discord CDP socket closed\"));\n }\n pending.clear();\n try {\n socket.close();\n } catch {\n // ignore\n }\n };\n\n const rpc = (method: string, params: unknown = {}): Promise<unknown> =>\n new Promise<unknown>((resolveRpc, rejectRpc) => {\n const id = nextId++;\n pending.set(id, {\n resolve: resolveRpc,\n reject: rejectRpc,\n });\n const envelope: CdpRpcEnvelope = { id, method, params };\n socket.send(JSON.stringify(envelope));\n });\n\n socket.addEventListener(\"message\", (event: MessageEvent) => {\n let payload: {\n id?: number;\n error?: { message?: string };\n result?: unknown;\n };\n try {\n payload = JSON.parse(String(event.data)) as typeof payload;\n } catch {\n return;\n }\n if (typeof payload.id !== \"number\") return;\n const callback = pending.get(payload.id);\n if (!callback) return;\n pending.delete(payload.id);\n if (payload.error) {\n callback.reject(\n new Error(payload.error.message ?? `CDP error id=${payload.id}`),\n );\n return;\n }\n callback.resolve(payload.result);\n });\n\n socket.addEventListener(\"error\", () => {\n cleanup();\n reject(new Error(\"Discord CDP websocket failed\"));\n });\n\n socket.addEventListener(\"close\", () => {\n cleanup();\n });\n\n const sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n\n socket.addEventListener(\"open\", async () => {\n try {\n // Ensure the page is on the target channel. Setting location.href\n // works inside Discord's renderer where Page.navigate isn't always\n // available without enabling Page domain.\n const navigateExpr = `(() => {\n const target = ${JSON.stringify(args.channelUrl)};\n if (location.href !== target) {\n location.href = target;\n return \"navigated\";\n }\n return \"already\";\n })()`;\n await rpc(\"Runtime.evaluate\", {\n expression: navigateExpr,\n returnByValue: true,\n });\n // Give Discord's SPA router time to settle on the channel.\n await sleep(900);\n\n // Wait until the message editor exists and is focusable. Discord's\n // editor uses Slate.js with a slate-editor div + role=\"textbox\".\n const focusResult = (await rpc(\"Runtime.evaluate\", {\n expression: `\n (async () => {\n const start = Date.now();\n while (Date.now() - start < 6000) {\n const el = document.querySelector(\n 'div[role=\"textbox\"][data-slate-editor=\"true\"]'\n );\n if (el) {\n el.scrollIntoView({ block: \"end\" });\n el.focus();\n if (document.activeElement === el) {\n return { ok: true, focused: true };\n }\n // Click as fallback to force focus.\n const r = el.getBoundingClientRect();\n const ev = new MouseEvent(\"mousedown\", {\n bubbles: true,\n clientX: r.x + r.width / 2,\n clientY: r.y + r.height / 2,\n });\n el.dispatchEvent(ev);\n el.focus();\n if (document.activeElement === el) {\n return { ok: true, focused: true };\n }\n }\n await new Promise((r) => setTimeout(r, 150));\n }\n return { ok: false, focused: false };\n })()\n `,\n awaitPromise: true,\n returnByValue: true,\n })) as { result?: { result?: { value?: { ok?: boolean } } } };\n\n const focusValue = (\n focusResult?.result as { value?: { ok?: boolean } } | undefined\n )?.value;\n if (!focusValue?.ok) {\n throw new Error(\n \"Could not focus the Discord message editor for the channel.\",\n );\n }\n\n // Insert text via CDP — bypasses Slate's keystroke rules but the\n // editor still emits proper input events on Input.insertText.\n await rpc(\"Input.insertText\", { text: args.text });\n await sleep(250);\n\n // Press Enter to send (no Shift modifier so it sends, not newline).\n await rpc(\"Input.dispatchKeyEvent\", {\n type: \"keyDown\",\n key: \"Enter\",\n code: \"Enter\",\n windowsVirtualKeyCode: 13,\n nativeVirtualKeyCode: 13,\n });\n await rpc(\"Input.dispatchKeyEvent\", {\n type: \"keyUp\",\n key: \"Enter\",\n code: \"Enter\",\n windowsVirtualKeyCode: 13,\n nativeVirtualKeyCode: 13,\n });\n\n // Brief settle for the local echo so callers can observe delivery.\n await sleep(700);\n cleanup();\n resolve();\n } catch (error) {\n cleanup();\n reject(error instanceof Error ? error : new Error(String(error)));\n }\n });\n });\n}\n\nexport async function relaunchDiscordDesktopForCdp(\n env: NodeJS.ProcessEnv = process.env,\n): Promise<DiscordDesktopCdpStatus> {\n const current = await getDiscordDesktopCdpStatus(env);\n if (!current.supported) {\n throw new Error(\n current.lastError ?? \"Discord Desktop control unavailable.\",\n );\n }\n if (current.cdpAvailable) {\n return current;\n }\n\n if (current.appRunning) {\n await execFileAsync(\n \"/usr/bin/osascript\",\n [\"-e\", 'quit app \"Discord\"'],\n 5_000,\n );\n await waitForDiscordToQuit();\n }\n\n const port = configuredDiscordDesktopCdpPort(env);\n await execFileAsync(\n \"/usr/bin/open\",\n [\n \"-a\",\n \"Discord\",\n \"--args\",\n `--remote-debugging-port=${port}`,\n `--remote-debugging-address=${DISCORD_DESKTOP_CDP_HOST}`,\n \"--remote-allow-origins=*\",\n ],\n 5_000,\n );\n\n return waitForDiscordCdpReady(env);\n}\n",
47
- "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",
48
- "import { createHash } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { resolveStateDir } from \"@elizaos/core\";\n\nconst DISCORD_AVATAR_ROUTE_PREFIX = \"/api/avatar/discord\";\nconst MAX_DISCORD_AVATAR_BYTES = 2 * 1024 * 1024;\nconst ALLOWED_DISCORD_AVATAR_HOSTS = new Set([\n\t\"cdn.discordapp.com\",\n\t\"media.discordapp.net\",\n\t\"images-ext-1.discordapp.net\",\n\t\"images-ext-2.discordapp.net\",\n]);\nconst inflightDiscordAvatarDownloads = new Map<\n\tstring,\n\tPromise<string | undefined>\n>();\n\nfunction normalizeExtension(extension: string | null | undefined): string {\n\tconst normalized = (extension ?? \"\").trim().toLowerCase().replace(/^\\./, \"\");\n\tswitch (normalized) {\n\t\tcase \"jpg\":\n\t\tcase \"jpeg\":\n\t\t\treturn \"jpg\";\n\t\tcase \"png\":\n\t\tcase \"webp\":\n\t\tcase \"gif\":\n\t\t\treturn normalized;\n\t\tdefault:\n\t\t\treturn \"png\";\n\t}\n}\n\nfunction extensionFromContentType(contentType: string | null): string {\n\tswitch (contentType?.split(\";\")[0]?.trim().toLowerCase()) {\n\t\tcase \"image/jpeg\":\n\t\t\treturn \"jpg\";\n\t\tcase \"image/gif\":\n\t\t\treturn \"gif\";\n\t\tcase \"image/webp\":\n\t\t\treturn \"webp\";\n\t\tdefault:\n\t\t\treturn \"png\";\n\t}\n}\n\nfunction sanitizeFileSegment(value: string | undefined): string {\n\tconst normalized = (value ?? \"discord\")\n\t\t.trim()\n\t\t.replace(/[^a-zA-Z0-9_-]+/g, \"-\")\n\t\t.replace(/-+/g, \"-\")\n\t\t.replace(/^-|-$/g, \"\");\n\treturn normalized || \"discord\";\n}\n\nexport function isDiscordAvatarUrl(url: string): boolean {\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\treturn (\n\t\t\tparsed.protocol === \"https:\" &&\n\t\t\tALLOWED_DISCORD_AVATAR_HOSTS.has(parsed.hostname.toLowerCase())\n\t\t);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport function getDiscordAvatarCacheDir(): string {\n\treturn path.join(resolveStateDir(), \"cache\", \"discord-avatars\");\n}\n\nexport function getDiscordAvatarPublicPath(fileName: string): string {\n\treturn `${DISCORD_AVATAR_ROUTE_PREFIX}/${encodeURIComponent(fileName)}`;\n}\n\nexport function getDiscordAvatarCachePath(fileName: string): string {\n\treturn path.join(getDiscordAvatarCacheDir(), fileName);\n}\n\nexport function buildDiscordAvatarCacheFileName(\n\turl: string,\n\tuserId?: string,\n): string {\n\tconst parsed = new URL(url);\n\tconst pathnameExtension = path.extname(parsed.pathname);\n\tconst extension = normalizeExtension(pathnameExtension);\n\tconst baseId = sanitizeFileSegment(\n\t\tuserId ??\n\t\t\tparsed.pathname.split(\"/\").filter(Boolean).at(-2) ??\n\t\t\tparsed.pathname.split(\"/\").filter(Boolean).at(-1),\n\t);\n\tconst hash = createHash(\"sha256\").update(url).digest(\"hex\").slice(0, 24);\n\treturn `${baseId}-${hash}.${extension}`;\n}\n\nexport async function cacheDiscordAvatarUrl(\n\turl: string | undefined,\n\toptions: {\n\t\tfetchImpl?: typeof fetch;\n\t\tuserId?: string;\n\t} = {},\n): Promise<string | undefined> {\n\tif (!url || !isDiscordAvatarUrl(url)) {\n\t\treturn url;\n\t}\n\n\tconst fetchImpl = options.fetchImpl ?? globalThis.fetch;\n\tif (typeof fetchImpl !== \"function\") {\n\t\treturn url;\n\t}\n\n\tconst requestedFileName = buildDiscordAvatarCacheFileName(\n\t\turl,\n\t\toptions.userId,\n\t);\n\tconst requestedFilePath = getDiscordAvatarCachePath(requestedFileName);\n\ttry {\n\t\tconst stat = await fs.stat(requestedFilePath);\n\t\tif (stat.isFile()) {\n\t\t\treturn getDiscordAvatarPublicPath(requestedFileName);\n\t\t}\n\t} catch {}\n\n\tconst existing = inflightDiscordAvatarDownloads.get(requestedFileName);\n\tif (existing) {\n\t\treturn existing;\n\t}\n\n\tconst downloadPromise = (async () => {\n\t\tawait fs.mkdir(getDiscordAvatarCacheDir(), { recursive: true });\n\n\t\tconst response = await fetchImpl(url, {\n\t\t\theaders: { Accept: \"image/*\" },\n\t\t});\n\t\tif (!response.ok) {\n\t\t\treturn url;\n\t\t}\n\n\t\tconst contentType = response.headers.get(\"content-type\");\n\t\tif (!contentType?.toLowerCase().startsWith(\"image/\")) {\n\t\t\treturn url;\n\t\t}\n\n\t\tconst bytes = Buffer.from(await response.arrayBuffer());\n\t\tif (bytes.length === 0 || bytes.length > MAX_DISCORD_AVATAR_BYTES) {\n\t\t\treturn url;\n\t\t}\n\n\t\tconst preferredExtension = extensionFromContentType(contentType);\n\t\tconst finalFileName = requestedFileName.endsWith(`.${preferredExtension}`)\n\t\t\t? requestedFileName\n\t\t\t: requestedFileName.replace(/\\.[^.]+$/, `.${preferredExtension}`);\n\t\tconst finalFilePath = getDiscordAvatarCachePath(finalFileName);\n\n\t\ttry {\n\t\t\tconst stat = await fs.stat(finalFilePath);\n\t\t\tif (stat.isFile()) {\n\t\t\t\treturn getDiscordAvatarPublicPath(finalFileName);\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst tempFilePath = `${finalFilePath}.${process.pid}.${Date.now()}.tmp`;\n\t\tawait fs.writeFile(tempFilePath, bytes, { mode: 0o600 });\n\t\ttry {\n\t\t\tawait fs.rename(tempFilePath, finalFilePath);\n\t\t} catch (error) {\n\t\t\tawait fs.unlink(tempFilePath).catch(() => {});\n\t\t\tconst code =\n\t\t\t\ttypeof error === \"object\" &&\n\t\t\t\terror !== null &&\n\t\t\t\t\"code\" in error &&\n\t\t\t\ttypeof (error as { code?: unknown }).code === \"string\"\n\t\t\t\t\t? (error as { code: string }).code\n\t\t\t\t\t: \"\";\n\t\t\tif (code !== \"EEXIST\") {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\treturn getDiscordAvatarPublicPath(finalFileName);\n\t})().finally(() => {\n\t\tinflightDiscordAvatarDownloads.delete(requestedFileName);\n\t});\n\n\tinflightDiscordAvatarDownloads.set(requestedFileName, downloadPromise);\n\treturn downloadPromise;\n}\n",
49
- "import {\n\ttype AgentRuntime,\n\tnormalizeConnectorSource,\n\ttype Room,\n\ttype UUID,\n} from \"@elizaos/core\";\nimport { cacheDiscordAvatarUrl } from \"./discord-avatar-cache\";\n\ntype DiscordUserProfile = {\n\tavatarUrl?: string;\n\tdisplayName?: string;\n\tusername?: string;\n};\n\ntype DiscordMessageAuthorProfile = DiscordUserProfile & {\n\trawUserId?: string;\n};\n\ntype StoredDiscordEntityProfile = {\n\tavatarUrl?: string;\n\tdisplayName?: string;\n\trawUserId?: string;\n\tusername?: string;\n};\n\nconst DISCORD_PROFILE_CACHE_TTL_MS = 5 * 60_000;\n\nconst discordUserProfileCache = new Map<\n\tstring,\n\t{ expiresAt: number; value: DiscordUserProfile | null }\n>();\n\nconst discordMessageAuthorProfileCache = new Map<\n\tstring,\n\t{ expiresAt: number; value: DiscordMessageAuthorProfile | null }\n>();\n\ntype DiscordClientLike = {\n\tchannels?: {\n\t\tcache?: { get?: (id: string) => unknown };\n\t\tfetch?: (id: string) => Promise<unknown>;\n\t};\n\tusers?: {\n\t\tfetch?: (id: string) => Promise<unknown>;\n\t};\n};\n\nfunction readCachedValue<T>(\n\tcache: Map<string, { expiresAt: number; value: T }>,\n\tkey: string,\n): T | undefined {\n\tconst entry = cache.get(key);\n\tif (!entry) return undefined;\n\tif (entry.expiresAt <= Date.now()) {\n\t\tcache.delete(key);\n\t\treturn undefined;\n\t}\n\treturn entry.value;\n}\n\nfunction getDiscordClient(runtime: AgentRuntime): DiscordClientLike | null {\n\tconst runtimeWithServices = runtime as AgentRuntime & {\n\t\tgetService?: (name: string) => unknown;\n\t};\n\tconst service = runtimeWithServices.getService?.(\"discord\") as\n\t\t| { client?: DiscordClientLike | null }\n\t\t| undefined;\n\treturn service?.client ?? null;\n}\n\nfunction firstCollectionValue(collection: unknown): unknown {\n\tif (!collection || typeof collection !== \"object\") {\n\t\treturn null;\n\t}\n\tconst record = collection as {\n\t\tfirst?: () => unknown;\n\t\tvalues?: () => IterableIterator<unknown>;\n\t};\n\tif (typeof record.first === \"function\") {\n\t\treturn record.first();\n\t}\n\tif (typeof record.values === \"function\") {\n\t\treturn record.values().next().value ?? null;\n\t}\n\treturn null;\n}\n\nfunction readDiscordDisplayName(user: unknown): string | undefined {\n\tif (!user || typeof user !== \"object\") return undefined;\n\tconst record = user as Record<string, unknown>;\n\tconst globalName = record.globalName;\n\tif (typeof globalName === \"string\" && globalName.trim()) {\n\t\treturn globalName.trim();\n\t}\n\tconst displayName = record.displayName;\n\tif (typeof displayName === \"string\" && displayName.trim()) {\n\t\treturn displayName.trim();\n\t}\n\tconst username = record.username;\n\tif (typeof username === \"string\" && username.trim()) {\n\t\treturn username.trim();\n\t}\n\treturn undefined;\n}\n\nfunction readDiscordAvatarUrl(user: unknown): string | undefined {\n\tif (!user || typeof user !== \"object\") return undefined;\n\tconst record = user as {\n\t\tdisplayAvatarURL?: () => string;\n\t\tavatarURL?: () => string | null;\n\t};\n\tif (typeof record.displayAvatarURL === \"function\") {\n\t\tconst url = record.displayAvatarURL();\n\t\tif (typeof url === \"string\" && url.trim()) return url;\n\t}\n\tif (typeof record.avatarURL === \"function\") {\n\t\tconst url = record.avatarURL();\n\t\tif (typeof url === \"string\" && url.trim()) return url;\n\t}\n\treturn undefined;\n}\n\nfunction readLooseStringValue(\n\trecord: Record<string, unknown> | undefined,\n\tkeys: string[],\n): string | null {\n\tif (!record) return null;\n\n\tfor (const key of keys) {\n\t\tconst value = record[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\n\treturn null;\n}\n\nfunction readStoredDiscordEntityProfile(\n\tentity: unknown,\n): StoredDiscordEntityProfile | null {\n\tif (!entity || typeof entity !== \"object\") {\n\t\treturn null;\n\t}\n\n\tconst metadata = (entity as { metadata?: unknown }).metadata;\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\tconst fallback =\n\t\trecord.default && typeof record.default === \"object\"\n\t\t\t? (record.default as Record<string, unknown>)\n\t\t\t: null;\n\n\tconst displayName =\n\t\treadLooseStringValue(record, [\"displayName\", \"name\"]) ??\n\t\treadLooseStringValue(discord ?? undefined, [\n\t\t\t\"displayName\",\n\t\t\t\"globalName\",\n\t\t\t\"name\",\n\t\t]) ??\n\t\treadLooseStringValue(fallback ?? undefined, [\"name\"]);\n\tconst username =\n\t\treadLooseStringValue(record, [\"username\"]) ??\n\t\treadLooseStringValue(discord ?? undefined, [\"username\", \"userName\"]) ??\n\t\treadLooseStringValue(fallback ?? undefined, [\"username\"]);\n\tconst avatarUrl =\n\t\treadLooseStringValue(record, [\"avatarUrl\"]) ??\n\t\treadLooseStringValue(discord ?? undefined, [\"avatarUrl\"]) ??\n\t\treadLooseStringValue(fallback ?? undefined, [\"avatarUrl\"]);\n\tconst rawUserId =\n\t\treadLooseStringValue(discord ?? undefined, [\"userId\", \"id\"]) ??\n\t\treadLooseStringValue(record, [\"originalId\"]);\n\n\tif (!displayName && !username && !avatarUrl && !rawUserId) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\t...(avatarUrl ? { avatarUrl } : {}),\n\t\t...(displayName ? { displayName } : {}),\n\t\t...(rawUserId ? { rawUserId } : {}),\n\t\t...(username ? { username } : {}),\n\t};\n}\n\nexport function isCanonicalDiscordSource(\n\tsource: string | null | undefined,\n): boolean {\n\treturn normalizeConnectorSource(source) === \"discord\";\n}\n\nexport async function cacheDiscordAvatarForRuntime(\n\truntime: AgentRuntime,\n\tavatarUrl: string | undefined,\n\tuserId?: string,\n): Promise<string | undefined> {\n\treturn cacheDiscordAvatarUrl(avatarUrl, {\n\t\tfetchImpl: runtime.fetch ?? globalThis.fetch,\n\t\tuserId,\n\t});\n}\n\nexport async function resolveStoredDiscordEntityProfile(\n\truntime: AgentRuntime,\n\tentityId: string | undefined,\n): Promise<StoredDiscordEntityProfile | null> {\n\tif (!entityId) return null;\n\n\tconst runtimeWithEntityLookup = runtime as AgentRuntime & {\n\t\tgetEntityById?: (id: UUID) => Promise<unknown>;\n\t};\n\tif (typeof runtimeWithEntityLookup.getEntityById !== \"function\") {\n\t\treturn null;\n\t}\n\n\ttry {\n\t\tconst entity = await runtimeWithEntityLookup.getEntityById(\n\t\t\tentityId as UUID,\n\t\t);\n\t\treturn readStoredDiscordEntityProfile(entity);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport async function resolveDiscordMessageAuthorProfile(\n\truntime: AgentRuntime,\n\tchannelId: string,\n\tmessageId: string,\n): Promise<DiscordMessageAuthorProfile | null> {\n\tconst cacheKey = `${channelId}:${messageId}`;\n\tconst cached = readCachedValue(discordMessageAuthorProfileCache, cacheKey);\n\tif (cached !== undefined) return cached;\n\n\tconst client = getDiscordClient(runtime);\n\tconst cachedChannel = client?.channels?.cache?.get?.(channelId);\n\tconst fetchChannel = client?.channels?.fetch;\n\tconst channel =\n\t\tcachedChannel ??\n\t\t(typeof fetchChannel === \"function\"\n\t\t\t? await fetchChannel(channelId).catch(() => null)\n\t\t\t: null);\n\n\tconst fetchMessage =\n\t\tchannel &&\n\t\ttypeof channel === \"object\" &&\n\t\ttypeof (channel as { messages?: { fetch?: unknown } }).messages?.fetch ===\n\t\t\t\"function\"\n\t\t\t? (channel as { messages: { fetch: (id: string) => Promise<unknown> } })\n\t\t\t\t\t.messages.fetch\n\t\t\t: null;\n\tif (!fetchMessage) {\n\t\tdiscordMessageAuthorProfileCache.set(cacheKey, {\n\t\t\texpiresAt: Date.now() + DISCORD_PROFILE_CACHE_TTL_MS,\n\t\t\tvalue: null,\n\t\t});\n\t\treturn null;\n\t}\n\n\ttry {\n\t\tconst message = await fetchMessage(messageId);\n\t\tconst author =\n\t\t\tmessage && typeof message === \"object\"\n\t\t\t\t? ((message as { author?: unknown }).author ?? null)\n\t\t\t\t: null;\n\t\tconst member =\n\t\t\tmessage && typeof message === \"object\"\n\t\t\t\t? ((message as { member?: unknown }).member ?? null)\n\t\t\t\t: null;\n\t\tconst rawUserId =\n\t\t\tauthor &&\n\t\t\ttypeof author === \"object\" &&\n\t\t\ttypeof (author as { id?: unknown }).id === \"string\"\n\t\t\t\t? (author as { id: string }).id\n\t\t\t\t: undefined;\n\t\tconst profile: DiscordMessageAuthorProfile = {\n\t\t\tdisplayName: readDiscordDisplayName(member ?? author),\n\t\t\tusername:\n\t\t\t\tauthor &&\n\t\t\t\ttypeof author === \"object\" &&\n\t\t\t\ttypeof (author as { username?: unknown }).username === \"string\"\n\t\t\t\t\t? (author as { username: string }).username\n\t\t\t\t\t: undefined,\n\t\t\tavatarUrl: readDiscordAvatarUrl(author),\n\t\t\t...(rawUserId ? { rawUserId } : {}),\n\t\t};\n\t\tdiscordMessageAuthorProfileCache.set(cacheKey, {\n\t\t\texpiresAt: Date.now() + DISCORD_PROFILE_CACHE_TTL_MS,\n\t\t\tvalue: profile,\n\t\t});\n\t\treturn profile;\n\t} catch {\n\t\tdiscordMessageAuthorProfileCache.set(cacheKey, {\n\t\t\texpiresAt: Date.now() + DISCORD_PROFILE_CACHE_TTL_MS,\n\t\t\tvalue: null,\n\t\t});\n\t\treturn null;\n\t}\n}\n\nexport async function resolveDiscordUserProfile(\n\truntime: AgentRuntime,\n\tuserId: string,\n): Promise<DiscordUserProfile | null> {\n\tconst cached = readCachedValue(discordUserProfileCache, userId);\n\tif (cached !== undefined) return cached;\n\n\tconst client = getDiscordClient(runtime);\n\tconst fetchUser = client?.users?.fetch;\n\tif (typeof fetchUser !== \"function\") return null;\n\n\ttry {\n\t\tconst user = await fetchUser(userId);\n\t\tconst profile: DiscordUserProfile = {\n\t\t\tdisplayName: readDiscordDisplayName(user),\n\t\t\tusername:\n\t\t\t\tuser &&\n\t\t\t\ttypeof user === \"object\" &&\n\t\t\t\ttypeof (user as { username?: unknown }).username === \"string\"\n\t\t\t\t\t? (user as { username: string }).username\n\t\t\t\t\t: undefined,\n\t\t\tavatarUrl: readDiscordAvatarUrl(user),\n\t\t};\n\t\tdiscordUserProfileCache.set(userId, {\n\t\t\texpiresAt: Date.now() + DISCORD_PROFILE_CACHE_TTL_MS,\n\t\t\tvalue: profile,\n\t\t});\n\t\treturn profile;\n\t} catch {\n\t\tdiscordUserProfileCache.set(userId, {\n\t\t\texpiresAt: Date.now() + DISCORD_PROFILE_CACHE_TTL_MS,\n\t\t\tvalue: null,\n\t\t});\n\t\treturn null;\n\t}\n}\n\nexport async function resolveDiscordRoomProfile(\n\truntime: AgentRuntime,\n\troom: Room | undefined,\n\tchannelIdHint?: string,\n): Promise<{ avatarUrl?: string; title: string | null } | null> {\n\tconst channelId =\n\t\ttypeof channelIdHint === \"string\" && channelIdHint.trim()\n\t\t\t? channelIdHint.trim()\n\t\t\t: (() => {\n\t\t\t\t\tconst raw = room?.channelId;\n\t\t\t\t\treturn typeof raw === \"string\" && raw.trim() ? raw.trim() : \"\";\n\t\t\t\t})();\n\tif (!channelId) return null;\n\n\tconst client = getDiscordClient(runtime);\n\tconst cachedChannel = client?.channels?.cache?.get?.(channelId);\n\tconst fetchChannel = client?.channels?.fetch;\n\tconst channel =\n\t\tcachedChannel ??\n\t\t(typeof fetchChannel === \"function\"\n\t\t\t? await fetchChannel(channelId).catch(() => null)\n\t\t\t: null);\n\n\tlet title: string | null = null;\n\tlet avatarUrl: string | undefined;\n\tif (channel && typeof channel === \"object\") {\n\t\tconst namedChannel = channel as { name?: unknown };\n\t\tif (typeof namedChannel.name === \"string\" && namedChannel.name.trim()) {\n\t\t\ttitle = namedChannel.name.trim();\n\t\t} else {\n\t\t\tconst record = channel as {\n\t\t\t\trecipient?: unknown;\n\t\t\t\trecipients?: unknown;\n\t\t\t};\n\t\t\tconst recipient =\n\t\t\t\trecord.recipient ?? firstCollectionValue(record.recipients);\n\t\t\ttitle = readDiscordDisplayName(recipient) ?? null;\n\t\t\tavatarUrl = readDiscordAvatarUrl(recipient);\n\t\t}\n\t}\n\n\treturn {\n\t\ttitle,\n\t\t...(typeof avatarUrl === \"string\" && avatarUrl.length > 0\n\t\t\t? { avatarUrl }\n\t\t\t: {}),\n\t};\n}\n",
50
- "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",
51
- "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"
52
- ],
53
- "mappings": "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",
54
- "debugId": "24CC8FFF789E686664756E2164756E21",
55
- "names": []
56
- }