Skip to content

Releases: borhara/cordless

v1.5.1

Choose a tag to compare

@github-actions github-actions released this 02 Sep 12:11

Added

  • The package now ships py.typed, so type checkers (mypy, pyright) read cordless's annotations when you depend on it.
  • DiscordObject.to_dict() returns the raw Discord payload behind any wrapped response model (webhook, channel, message, and so on).

Changed

  • The entire codebase is now type-checked under pyright strict.

Fixed

  • An interaction that arrives without a request path is now ignored instead of being passed on to route matching.

Full Changelog: v1.5.0...v1.5.1

v1.5.0

Choose a tag to compare

@github-actions github-actions released this 31 Aug 21:51

Added

  • Full REST API client. A complete async client for Discord's bot-accessible REST API, exposed as flat bot.<verb>() methods and as object-method sugar (channel.send(), message.edit(), member.add_role(), role.delete(), …). Covers channels, messages and reactions, polls, members and roles, guilds (bans, prune, widget, welcome screen, onboarding, incident actions), threads, webhooks, invites, emojis, stickers, soundboard, scheduled events, stage instances, auto-moderation, guild templates, audit log, users, voice state, entitlements, SKUs, subscriptions, the application object, and application commands
  • Typed DiscordHTTPError hierarchy (BadRequest, Unauthorized, Forbidden, NotFound, ServerError) carrying .status, .body, and .headers; MissingTokenError when no bot token is configured
  • REST rate limiting keyed by Discord's X-RateLimit-Bucket id and shared across concurrent Lambda invocations via the existing DynamoDB table, with a warm-container HTTPS connection kept open between calls
  • idempotent= on REST calls to override the default per-method safe-to-retry guess
  • @bot.route(method, path) for raw HTTP handlers on the interaction Lambda, outside Discord's signature-verified flow: incoming service webhooks, OAuth callbacks, health checks. Supports {name} path params and a trailing {name+} greedy segment (delivered on event["pathParameters"]), coerces handler returns (string, dict/list as JSON, status int, (status, body) / (status, body, headers) tuple, or a full proxy dict), and works on either a Function URL or API Gateway
  • cordless deploy syncs @bot.route paths onto API Gateway alongside the slash commands; cordless dev serves them locally; cordless doctor reports whether they're in sync
  • Per-option name_localizations / description_localizations, plus localisation of auto-created subcommand groups, parent command names, and choices
  • choice() helper for building localised choice dicts
  • user_installable="only" for commands that drop the guild install and are usable only by users who installed them
  • default_member_permissions and nsfw on @bot.user_command and @bot.message_command
  • channel_types on channel options; group_description for a real description on an auto-created subcommand group
  • Label component for wrapping a select menu inside a modal; default_values on entity select menus
  • ctx.entitlements on the interaction context
  • bot.execute_slack_webhook / bot.execute_github_webhook; bot.fetch_webhook_message, bot.fetch_webhook_with_token, bot.edit_webhook_with_token
  • reason= (audit-log reason) on bot.add_role, bot.remove_role, bot.create_webhook, and bot.delete_webhook
  • Decoration-time validation of command and option shape (name pattern, 1–100 char descriptions, 25-option and 25-choice caps), with the offending command and option named in the error
  • Discord's Components v2 structural limits and per-message action-row limits are enforced at build time with a clear error
  • Guild, the DiscordHTTPError family, MissingTokenError, Label, and choice are exported from the top-level cordless package

Changed

  • REST and webhook calls that fail now raise DiscordHTTPError (a CordlessError subclass) instead of RuntimeError
  • pynacl is now a required runtime dependency and the pure-Python Ed25519 signature-verification fallback has been removed; cordless deploy fails rather than shipping a function that can't verify Discord's requests
  • bot.send_message and bot.edit_message return the resulting Message; send_message / edit_message / delete_message / add_role / remove_role now run through the shared async REST client
  • ratelimit.wait_if_needed can raise DiscordHTTPError(429) without sending when a confirmed, shared rate-limit block has more time left than the retry budget allows
  • bot.<verb>() methods return _rest.models objects (e.g. Webhook, Thread) that are not exported at the top level

Fixed

  • Autocomplete interactions no longer fall through to the shared error handler, which could return a message-type response Discord rejects
  • parse_webhook_url no longer echoes the passed-in URL (which contains a live token) in its error message
  • Multipart uploads escape filenames and field values in Content-Disposition headers
  • The rate limiter never retries a 429 before Discord's own retry_after, never sleeps past the retry deadline, keys shared state by major resource, tolerates an explicit null retry_after, and warns once instead of silently when the shared DynamoDB table is unreachable
  • Non-idempotent requests (POST/PATCH) are no longer resent after a dropped connection or network error
  • The persistent connection lock is held until the response body is read, fixing a race on the shared connection
  • DiscordObject instances are hashable again
  • Thread and ThreadMember keep fields cordless doesn't explicitly declare instead of dropping them
  • bot.execute_slack_webhook no longer crashes on the Slack endpoint's plain-text wait response
  • Forum thread creation no longer forces rate_limit_per_user=0; start_thread_from_forum(files=...) nests the attachments descriptor under message so the files actually attach
  • fetch_thread_members supports after / limit pagination
  • An interaction POST delivered on a non-root path is routed through the normal interaction handler
  • API Gateway route sync no longer drops routes when the bot fails to load, and handles greedy route keys correctly
  • A @bot.route handler that returns a value cordless can't turn into a response now gets a clean 500 instead of an unhandled 502
  • Webhook 429 retries no longer stack sleeps past a total time budget
  • Editing or deleting a Nitro-pack sticker or default soundboard sound raises a clear error
  • A token-authenticated webhook's avatar can be cleared by passing None

Full Changelog: v1.4.7...v1.5.0

v1.4.7

Choose a tag to compare

@github-actions github-actions released this 15 Aug 18:41

Added

  • Guild model with ctx.guild, exposing icon, banner, and splash URLs from the interaction's partial guild object
  • Command and subcommand name/description localisation support
  • cordless doctor: a new diagnostic command for checking your deploy setup, with streamed section-by-section output
  • Testing helpers: invoke() and builders for slash/context menu commands, buttons, selects, modals, and autocomplete, plus support for dispatching deferred handlers in worker mode

Fixed

  • CloudWatch log groups now default to 30 day retention instead of never expiring, configurable via log_retention in [deploy]
  • Subcommands no longer leak name_localizations onto the parent command
  • invoke() now correctly decodes file-attachment responses
  • Testing helpers default placeholder usernames to test-user

Full Changelog: v1.4.6...v1.4.7

v1.4.6

Choose a tag to compare

@github-actions github-actions released this 26 Jul 18:41

Added

  • Permissions bitfield object for reading and building Discord permission bits. Read named bits off an incoming member or role (ctx.member.permissions.manage_guild), or construct one to send (Permissions(manage_guild=True, kick_members=True)).
  • Member.permissions and Role.permissions, wrapping Discord's raw bitfield string in a Permissions object.
  • ctx.resolved_users, ctx.resolved_members, ctx.resolved_roles, and ctx.resolved_channels: dicts of id to typed User/Member/Role/Channel, resolving UserSelect, RoleSelect, ChannelSelect, and MentionableSelect picks instead of leaving callers to dig through raw resolved payloads.
  • Role model, with .mention (<@&id>) and .permissions.
  • Channel.mention (<#id>).
  • User.avatar_url and User.banner_url, and Role.icon_url: full Discord CDN URLs built from the raw asset hashes, including the default-avatar fallback for users without a custom avatar.

Changed

  • Outgoing message content is now validated against Discord's 2000-character limit before sending, raising MessageTooLongError instead of letting the request fail invisibly on Discord's end after we've already returned 200 to API Gateway.
  • cordless dev's tunnel output no longer blocks startup waiting for the cloudflared tunnel to answer; it now prints the public URL immediately (highlighted) with a note that it may take a few seconds to start working.
  • cordless deploy now prints a hint to paste the returned URL into the app's Interactions Endpoint URL setting.

Fixed

  • default_member_permissions is now coerced to an int before being stringified when registering commands, so passing a Permissions object works correctly.

Full Changelog: v1.4.5...v1.4.6

v1.4.5

Choose a tag to compare

@github-actions github-actions released this 20 Jul 14:03

Added

  • User-installable commands: pass user_installable=True to @bot.command, @bot.user_command, or @bot.message_command to let users install the command as a personal app and run it in any DM, not just servers the bot is in
  • cordless dev request logs are now timestamped and colour-coded by status, with a --verbose/-v flag to print the full interaction payload under each line

Changed

  • Full docstring coverage across the public API, feeding cordless.dev's generated reference

Full Changelog: v1.4.4...v1.4.5

v1.4.4

Choose a tag to compare

@github-actions github-actions released this 19 Jul 15:20

Changed

  • The deploy extra is folded into cordless's base dependencies: uv add cordless is now enough, no separate extra needed
  • Docstring coverage extended across the public API, feeding cordless.dev's generated reference

Fixed

  • CLI and deploy-only source are no longer bundled into the Lambda layer or function zip

Full Changelog: v1.4.3...v1.4.4

v1.4.3

Choose a tag to compare

@github-actions github-actions released this 19 Jul 12:59

Added

  • Discord payloads (user, member, message, channel, attachment) are now wrapped in typed objects instead of raw dicts
  • cordless now declares Python 3.14 support
  • Rate-limit waits are now logged when they actually happen

Changed

  • cordless deploy now falls back to the process environment for Discord credentials if not otherwise configured

Fixed

  • cordless dev now waits for the cloudflared tunnel to actually route before printing its URL
  • The keep-warm deploy step is now correctly labelled as off when not opted into

Full Changelog: v1.4.2...v1.4.3

v1.4.2

Choose a tag to compare

@github-actions github-actions released this 19 Jul 01:57

Added

  • cordless deploy now runs a describe-only post-deploy health check, and prints the function's runtime and signature-verification method after every deploy

Full Changelog: v1.4.1...v1.4.2

v1.4.1

Choose a tag to compare

@github-actions github-actions released this 19 Jul 01:25

Added

  • Opt-in keep-warm cron that periodically invokes the main function to avoid cold starts
  • cordless init now requires an explicit endpoint choice

Changed

  • New Lambda functions default to the arm64 architecture (existing deployments keep their current architecture on redeploy)
  • New projects default to the us-east-1 region, closest to Discord's own API
  • HTTP connections are now reused across webhook, app, and deferred-followup requests instead of opening a new one per call, and the DynamoDB table handle is cached per table name

Fixed

  • Missing Function URL invoke permission is now healed automatically on existing deployments
  • Duplicate file names in an upload zip no longer get written twice
  • A pynacl fetch failure is now surfaced clearly after the deploy spinner instead of being lost mid-spin, and pynacl is now bundled correctly for bundle_cordless deploys too

Full Changelog: v1.4.0...v1.4.1

v1.4.0

Choose a tag to compare

@github-actions github-actions released this 13 Jul 15:17

Added

  • Optional cross-invocation rate-limit coordination via DynamoDB (ratelimit = true in [deploy]), tracking Discord's rate-limit headers locally and falling back to a shared table when needed

Changed

  • Outbound requests now retry a 429 on a time budget instead of a fixed 3-attempt cap, with jitter added to backoff waits

Fixed

  • Deferred followups (post_followup, delete_original) and webhook calls (execute/edit/delete) now retry on 429
  • Rate-limit state is now published proactively when remaining requests get low, not only after an actual 429

Full Changelog: v1.3.0...v1.4.0