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' 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 asyncclient.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: falseskip retaining raw emoji/sticker arrays per guild;transientMembers: falsestops caching members/users seen only via reactions, typing, messages, and interactions (resolve later reads withfetchMember);userSweepIntervalMs > 0periodically evicts users referenced by nothing (also callable manually viaclient.sweepUsers()). - Per-cache overrides via
options.cache(factories forusers,members,messages, ...).LRUCollection(Class, max),NullCollection(Class). - Cross-cluster dedup:
RemoteBackedCollection+RedisCacheStore(over an injected Redis client) implementingRemoteCacheStore(getEntity/getEntities/getAllEntities/setEntity/removeEntity), resolved viaclient.fetchUser/guild.fetchMember. - One-flag preset:
largeBotOptimizations: true(or envATHENA_LARGE_BOT=1) =rest.fullErrorStacks: false+cache.guildEmojis/guildStickers: false+cache.userSweepIntervalMs: 3_600_000. Explicit options win; never setstransientMembers. MemberandMessageallocate 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)(throwsSearchIndexNotReadyErrorwhile indexing),setVoiceChannelStatus,editGuildIncidentActions,getGuildRoleMemberCounts. - Events:
rateLimited(gateway RATE_LIMITED, member chunk requests),voiceChannelStatusUpdate,voiceChannelStartTimeUpdate,channelInfo(opcode 43 response viaclient.requestChannelInfo). interaction.createMessage(...)andinteraction.editParent(...)resolve with the created/updatedMessage(nogetOriginalMessageround trip).interaction.launchActivity()for Activities apps.
When generating code, prefer
interaction.getRequiredString('name')over digging intointeraction.data.options.MessageFlags.Ephemeralover the literal64.PermissionFlagsBits.Xover hex values.CommandBuilderover 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
Bottoken prefix. getAllUsers: truewithout theGuildMembersintent (throws on construct).- Responding to an interaction after 3 seconds (code 10062) or twice (40060).
- Not handling the
errorevent. - Mixing V1 and V2 components on one message.
- Installing plain
athenafrom the registry (frozen legacy 2.8.0) instead of theathena@npm:athena-primealias.