LLM and agent context

A compact, accurate summary of Athena to paste into an AI coding assistant (Claude Code, Copilot, Cursor) working in an Athena project. Keep it in your...

A compact, accurate summary of Athena to paste into an AI coding assistant (Claude Code, Copilot, Cursor) working in an Athena project. Keep it in your project's agent instructions.

What Athena is

A TypeScript Discord library for Node 22.15+, forked from Eris, targeting discord-api-types/v10 (re-exported as Constants). Zero required runtime dependencies beyond discord-api-types and eventemitter3: WebSocket and compression use the runtime's built-ins, and the ws package is optional (proxy agents/custom headers only). Published as athena-prime, installed under the alias athena (npm install athena@npm:athena-prime), so imports stay from 'athena'. athena/webhooks is a subpath export receiving Discord Webhook Events over HTTP (verifyWebhookSignature, handleWebhookEventRequest, createWebhookEventsListener). Synchronous in-memory cache by default; optional pluggable and Redis-backed caches.

Discord's official API docs live at docs.discord.com, with an MCP server at https://docs.discord.com/mcp.

Top-level exports

import {
  Client, CommandClient, VERSION,
  // structures
  Base, BaseChannel, CategoryChannel, ForumChannel, MediaChannel, Guild, GuildChannel,
  GuildTextChannel, GuildVoiceChannel, GuildPreview, GuildAuditLogEntry, GuildIntegration,
  GuildTemplate, NewsChannel, NewsThreadChannel, PartialChannel, PrivateChannel, StageChannel,
  StageInstance, ThreadChannel, ThreadMember, UnavailableGuild, VoiceTextChannel, Member,
  Message, Role, User, VoiceState, Invite, PermissionOverwrite, Entitlement, SKU, Subscription,
  AutoModerationRule, GuildScheduledEvent,
  // interactions
  Interaction, CommandInteraction, ComponentInteraction, AutocompleteInteraction,
  ModalSubmitInteraction, PingInteraction, UnknownInteraction,
  // util
  Bitfield, Bucket, ChannelFlagBitfield, Collection, LRUCollection, NullCollection,
  RemoteBackedCollection, RedisCacheStore, GatewayIntentsBitfield, MessageFlagBitfield,
  MultipartData, PermissionBitfield, SequentialBucket, SKUFlagBitfield,
  // rest / gateway / errors
  Endpoints, RequestHandler, Shard, ShardManager,
  DiscordHTTPError, DiscordRESTError, CommandError, SearchIndexNotReadyError,
  // builders / commands
  CommandBuilder, ComponentBuilder, NewComponentBuilder, ModalBuilder, Command, Event, CooldownHandler, SlashCommand
} from 'athena';

Type-only exports include GatewayRateLimitedData, GatewayChannelInfoData / GatewayChannelInfoEntry, GatewayVoiceChannelStatusUpdateData, GatewayVoiceChannelStartTimeUpdateData, and GuildMessagesSearchQuery.

Construction

new Client(`Bot ${token}`, options?)        // token MUST start with "Bot "
new CommandClient({ token, options, commandContext?, commandMiddleware?, cooldownLogic?, deployGlobally? })

Default intents 3243773 (all non-privileged). Privileged: GuildPresences, GuildMembers, MessageContent.

Transport defaults: compress: true (boolean | 'zlib' | 'zstd') picks the best available stream via the built-in node

(zstd-stream on Node >= 22.15, zlib-stream otherwise; explicit 'zstd' warns and falls back to zlib when unsupported). gateway: { transport: 'auto' } runs on the runtime's built-in WebSocket unless options.ws is set; 'ws' selects the optional ws package (npm install ws), 'native' forces the built-in.

Key conventions

  • Snowflake IDs are always string.
  • update(data) mutates a structure from a new dispatch payload (internal).
  • Base-derived objects have id, createdAt, toJSON().
  • Type guards narrow unions: interaction.isCommand(), channel.isTextBased(), etc.
  • Cache reads are synchronous: client.users.get(id), guild.members.get(id). For possibly-uncached, use async client.fetchUser(id) / guild.fetchMember(id).

Commands

@SlashCommand(new CommandBuilder('ping', 'Replies pong'))
class PingCommand extends Command {
  async handleCommand(context, interaction: CommandInteraction) {
    await interaction.createMessage({ content: 'Pong!' });
  }
}
client.registerCommand(new PingCommand());
client.on('ready', () => client.deployCommands());

Read options with getString / getRequiredInteger / getUser / getChannel / etc. Route modals and components by custom_id prefix matching the command name.

Components

ComponentBuilder = V1 (.toJSON(), max 5 rows). NewComponentBuilder = V2 (.toMessageData(), up to 40 components; cannot carry content/embeds/poll). Buttons: addNormalButton, addURLButton, addPremiumButton. Selects: addStringSelect, addChannelSelect, addRoleSelect, addUserSelect, addMentionableSelect.

Errors

DiscordRESTError (Discord JSON error, has numeric code; HTTP status on status), DiscordHTTPError (non-JSON/transport), SearchIndexNotReadyError (searchGuildMessages while Discord indexes; retryAfter, documentsIndexed), CommandError enum emitted via the commandError event (NotDeveloperGuild, DeniedUser, DeniedGuild, MissingPermission, OnCooldown, MiddlewareError, UncaughtError).

Caching at scale

  • Retention knobs on options.cache (all default to historical behaviour): guildEmojis / guildStickers: false skip retaining raw emoji/sticker arrays per guild; transientMembers: false stops caching members/users seen only via reactions, typing, messages, and interactions (resolve later reads with fetchMember); userSweepIntervalMs > 0 periodically evicts users referenced by nothing (also callable manually via client.sweepUsers()).
  • Per-cache overrides via options.cache (factories for users, members, messages, ...). LRUCollection(Class, max), NullCollection(Class).
  • Cross-cluster dedup: RemoteBackedCollection + RedisCacheStore (over an injected Redis client) implementing RemoteCacheStore (getEntity/getEntities/getAllEntities/setEntity/removeEntity), resolved via client.fetchUser / guild.fetchMember.
  • One-flag preset: largeBotOptimizations: true (or env ATHENA_LARGE_BOT=1) = rest.fullErrorStacks: false + cache.guildEmojis/guildStickers: false + cache.userSweepIntervalMs: 3_600_000. Explicit options win; never sets transientMembers.
  • Member and Message allocate container fields (roles, clientStatus, activities; mentions, embeds, reactions, ...) lazily on first access - automatic, identity-stable, safe to mutate in place.

Performance options (REST)

rest.fullErrorStacks: false skips the per-request caller-stack capture (errors still carry method, route, code, status, message). rest.bucketSweepInterval (default 300000 ms, 0 disables) sweeps idle ratelimit buckets so they don't accumulate.

New in 2.10 / 3.0 (June 2026)

  • REST helpers: getChannelPins (paginated pins: { items: [{ pinnedAt, message }], hasMore }), bulkBanGuildMembers, searchGuildMessages(guildID, GuildMessagesSearchQuery) (throws SearchIndexNotReadyError while indexing), setVoiceChannelStatus, editGuildIncidentActions, getGuildRoleMemberCounts.
  • Events: rateLimited (gateway RATE_LIMITED, member chunk requests), voiceChannelStatusUpdate, voiceChannelStartTimeUpdate, channelInfo (opcode 43 response via client.requestChannelInfo).
  • interaction.createMessage(...) and interaction.editParent(...) resolve with the created/updated Message (no getOriginalMessage round trip). interaction.launchActivity() for Activities apps.

When generating code, prefer

  • interaction.getRequiredString('name') over digging into interaction.data.options.
  • MessageFlags.Ephemeral over the literal 64.
  • PermissionFlagsBits.X over hex values.
  • CommandBuilder over hand-written command JSON.
  • await-ing all REST calls.
  • Imports from 'athena'; from 'discord-api-types/v10' only for types Athena does not re-export.

Common pitfalls

  • Missing the Bot token prefix.
  • getAllUsers: true without the GuildMembers intent (throws on construct).
  • Responding to an interaction after 3 seconds (code 10062) or twice (40060).
  • Not handling the error event.
  • Mixing V1 and V2 components on one message.
  • Installing plain athena from the registry (frozen legacy 2.8.0) instead of the athena@npm:athena-prime alias.