All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[0.3.0] - 2026-09-19
Installation
def deps do
[
{:eda, "~> 0.3.0"}
]
endAdded
- EDA.API.Message.pins/2 — one page of a channel's pins, with
pinned_attimestamps andhas_more - EDA.API.Channel.set_voice_status/3 / EDA.Channel.set_voice_status/3 — set or clear a voice channel's status, plus the
VOICE_CHANNEL_STATUS_UPDATEevent and the:statusfield onEDA.Channel - EDA.Event.RateLimited — gateway rate limit notifications (
opcode,retry_after,guild_id,nonce), previously swallowed byEDA.Event.Raw :reason(audit log) onEDA.API.Message.pin/3andunpin/3, and onEDA.Message.pin/2andunpin/2
Changed
- Pins now use
/channels/{id}/messages/pins; the deprecated/channels/{id}/pinsroutes are gone.EDA.API.Message.pinned/2keeps returning{:ok, [message]}but paginates past the old 50-pin ceiling - EDA.Gateway.MemberChunker throttles all-members OP 8 requests to one per guild per 30 seconds, queueing them instead of letting Discord drop them. Prefix searches and
user_idslookups are exempt. Configurable withconfig :eda, member_chunk_cooldown_ms: 30_000
Fixed
- Cache admission policies are now consulted once, with the real entity. On the REST fallback path a custom
fn/3or module policy was asked about the whole result withnilfor both key and value, and then asked again per entity by the cache module. A policy that pattern matched on a map raisedFunctionClauseError, and per-role decisions were impossible.EDA.Cache.fetch_role/2and the otherfetch_*fallbacks now delegate straight to the cache modules, which apply the policy with the real key and value EDA.Voice.Dave.Nativespecs now match what the NIF actually returns. The Rust side returnsResult<T, Atom>, which Rustler encodes as{:ok, T}, sonew_session/3yields{:ok, reference()}andcreate_key_package/1,process_proposals/4,encrypt_opus/2anddecrypt_audio/3are double wrapped as{:ok, {:ok, binary()}}. The getters (ready?/1,get_epoch/1,status/1,can_passthrough?/2,protocol_version/1) are wrapped too. Runtime behaviour is unchanged —EDA.Voice.Dave.Manageralready normalised both shapes — but the published specs were misleading for anyone calling the NIF directlyEDA.Modal.modal/7declared every input asmap()whilemodal/3..6default the trailing inputs tonil, so each defaulted arity broke its own contract- Removed unreachable clauses from private helpers in
EDA.CacheandEDA.Command.Option
Documentation
- Corrected the Gateway and Cache configuration examples in the README: the key is
gateway_encoding:(notencoding:) and:etfis the default;compress:does not exist since zlib-stream is unconditional; shard overrides use a top-levelshards:(:auto, an integer, or{range, total}) rather thanshard_count:under:gateway; and:cacheoptions are per entity, with noevict_interval EDA.Gateway.Intentsno longer claims:nonprivilegedis the default —EDA.intents/0falls back to[:guilds]
[0.2.0] - 2026-04-04
Installation
def deps do
[
{:eda, "~> 0.2.0"}
]
endAdded
- EDA.Collector — Discord.js-style
awaitpatterns for messages, reactions, components with filters, timeouts, and max count (#4) - EDA.AutoDelete — Timer-based auto-deletion using BEAM timer wheel.
delete_after:option onMessage.create,Interaction.respond,Interaction.followup - EDA.Color — 30 named colors with individual functions, crypto-random generation (
Color.random()), hex parsing,:randomsupport inEmbed.color/2 - EDA.Mention — User, channel, role, emoji, and timestamp formatting helpers
- EDA.OAuth2 — Bot invite URL generator with permission atoms and scopes
- EDA.API.SKU / Entitlement / Subscription — Monetization API endpoints (beta)
- Webhook —
get_message/3,edit_message/4,delete_message/3,wait: trueoption onexecute/3 - Thread —
remove_member/2,get_member/2,list_members/1 - Message —
forward/3(message forwarding),reply/2(auto message_reference) - Command/Option —
localize/3for multi-language name/description translations - Component —
disable_all/1to recursively disable all buttons/selects - Embed —
error/1andsuccess/1pre-styled presets - Interaction —
selected_values/1,component_type/1,delete_source/1,defer_and_edit/3 - Member —
move_voice/3helper - Guild —
icon_url/1CDN helper - EDA.latency/1 — Gateway heartbeat latency shortcut
- EDA.await_message/2, await_reaction/2, await_component/2 — Convenience wrappers
Fixed
- Ban.create —
reason:now sent asX-Audit-Log-Reasonheader instead of JSON body - Modal.get_values — handles atom keys from parsed interaction structs
- Component.section — raises if
:accessoryis missing (Discord requires it) - parse_error — includes Discord's
errorsfield for validation details (50035) - Cache.me() — returns
%EDA.User{}struct instead of raw map - File.from_binary — accepts byte lists for NIF
Vec<u8>compatibility - User helpers —
mention/1,avatar_url/1,display_name/1,bot?/1accept raw maps from cache - ReadyTracker test — stabilized flaky test with synchronous state reset
- Interaction.respond —
delete_after:schedules deletion via interaction token - Interaction.delete_source — works on ephemeral messages (uses type 6 DEFERRED_UPDATE_MESSAGE)
[0.1.3] - 2026-03-07
Installation
def deps do
[
{:eda, "~> 0.1.3"}
]
endAdded
- Voice — FFmpeg volume passthrough for voice playback (
play/3withvolume:option) (#2 by @christomitov)
Fixed
- DAVE — Fail closed on media encryption errors — stops playback cleanly instead of sending undecryptable raw Opus when DAVE encryption fails (#3 by @christomitov)
Acknowledgments
Thanks to @christomitov for both contributions in this release.
[0.1.2] - 2026-03-05
Installation
def deps do
[
{:eda, "~> 0.1.2"}
]
endRequires Rust toolchain for the DAVE NIF (auto-compiled via Rustler on first mix compile).
Fixed
- DAVE — Track
connected_clientsvia OP 11/12/13 (CLIENTS_CONNECT/CLIENTS_DISCONNECT) and pass user IDs toprocess_proposals— MLS group now forms correctly in multi-user channels (#2) - DAVE — OP 24
epoch=1sole member reset — reset MLS group and send new key package so the group reforms when someone rejoins - DAVE — OP 13
client_disconnecthandler per DAVE spec (singleuser_idformat), in addition to batch OP 12
Added
- DAVE —
DirtyCpuscheduling on 6 crypto NIFs (encrypt_opus,decrypt_audio,process_proposals,process_commit,process_welcome,create_key_package) — no longer blocks the BEAM scheduler (#6) - DAVE —
can_passthrough?/2NIF + fallback indecrypt_frame— audio passes through during epoch transitions instead of being dropped (#4) - DAVE — New NIFs:
reinit/4,status/1,protocol_version/1,max_protocol_version/0for fulldaveyAPI coverage (#5)
Acknowledgments
Built on the solid DAVE foundation from #1 by @christomitov. The improvements in this release build on top of that work — connected_clients tracking, DirtyCpu scheduling, passthrough fallback, and additional NIF bindings are all new code layered on the original PR's binary frame routing, key package flow, and error handling.
[0.1.1] - 2026-03-05
Fixed
- DAVE — Binary frame routing for OP 25/27/29/30, proper welcome/commit handling, improved error normalization (#1 by @christomitov)
- Voice — Better reconnection handling, ETS playback progress table lifecycle, encrypt error differentiation (
:not_readyvs:encryption_failed)
[0.1.0] - 2026-02-18
Added
- Gateway — WebSocket connection with automatic reconnection, heartbeat, resume, zlib compression, and ETF/JSON encoding
- Sharding — Shard manager with automatic shard count, member chunking, and per-shard ready tracking
- REST API — Resource-based API modules (Guild, Channel, Message, Member, Role, Emoji, Sticker, Webhook, Invite, Interaction, etc.) with full rate limiting
- Cache — ETS-backed cache for guilds, channels, users, members, roles, presences, and voice states with configurable eviction policies
- Voice — Voice connection with Opus audio sending/receiving, OGG file playback, AES-256-GCM and XChaCha20-Poly1305 encryption
- DAVE — Discord Audio/Video E2EE (experimental) via Rust NIF for MLS-based key ratcheting
- Events — Typed event structs for all Discord gateway events with
EDA.Consumercallback pattern - Entities — Struct-based models for all Discord objects (Guild, Channel, Message, Member, Role, User, etc.) with
Accessbehaviour - Interactions — Slash commands, components, modals, and autocomplete support
- Permissions — Bitfield-based permission calculations with channel overwrite resolution
- Telemetry — Built-in telemetry events for gateway, HTTP, and cache operations