Skip to content
GitHub

What's new in 4.1

MeoCord 4.1 · since 4.1.0

What 4.1 adds to a 4.0 bot, area by area, and where the few changes a working bot may notice are listed.

4.1 adds to 4.0, and most 4.0 bots and their tests build and run without edits. A few 4.0 patterns now stop the bot, fail to compile or change a test's result, and a few fixes change what a bot does at runtime; Upgrading from 4.0 to 4.1 lists each, with what to check. 4.1 needs discord.js 14.27 or a later 14.x and dotenv 18.0.5 or a later 18.x: npm install discord.js@^14.27.0 dotenv@^18.0.5. Every release's notes are in the changelog.

Answering Discord

  • respond(interaction) is one place to answer an interaction. It acknowledges, sends, follows up and reports errors in whatever form the interaction's state allows. See Responses.
  • @Defer() acknowledges an interaction first, so slow stages and handlers never miss Discord's three seconds, and locks a message's controls while the handler works. See @Defer.
  • Errors after a reply or a deferral are answered too, where 4.0 left them unanswered. See the upgrade note.
  • An unanswered handler is named in a warning in development, once, so "The application did not respond" has a cause to look for. See Responses.
  • Presenters decide how MeoCord's own answers look: the loading view, error answers and the built-in !help. See Presenters.
  • Themes name the colours, emojis and button styles those answers use by what they mean. Theme is deprecated, and its colours changed; see the upgrade note and Theming. A theme can differ per server and per user with themeFor, which takes functions or a ThemeResolver class that injects the app's services; see Per server and per user.
  • Localisation translates commands and replies from typed catalogs, and MeoCord's own texts too, with keys, params and plurals checked when the code compiles. See Localisation.

Handling a call

Every handler runs through one pipeline, in a fixed order. See How a call runs.

  • Guards can be global, in @MeoCord({ guards }), read typed facts about the handler through ExecutionContext, and throw GuardDeniedError to tell the user why. See Guards.
  • Stage params: a guard, interceptor, filter or pipe can declare the params it takes, and each { provide, params } given for it is checked against them when the code compiles. See Settings for one use.
  • MeoCordOptions names @MeoCord's options, so a base shared by two app classes keeps the checks @MeoCord makes. See The app's options.
  • Interceptors run around a handler, for timing, logging, caching or mapping errors. See Interceptors.
  • Exception filters decide what the user is told when a call throws, and UserError tells the user about their own mistake: privately after an interaction, and in a reply that doesn't ping after a message. See Exception filters and UserError.
  • Validation and pipes check a handler's input against any Standard Schema and turn it into what the handler wants. See Validation and pipes.
  • @Cooldown limits how often a handler runs, per user, channel, server or everyone, and optionally apart for each value of the call with by. Counts can live in the shard manager, Redis or a database of your own, and cooldownStoreFailure decides whether a call is refused or allowed while the store is down. See Cooldowns, When the store fails and Cooldown stores.
  • Observers are told as each call starts and once it settles, with how it ended and how long it took, for metrics, audit logs and tracing. See Observers.
  • Custom decorators: createMetadata makes a typed fact about a handler that guards read through ExecutionContext, and applyDecorators combines decorators into one. See Custom decorators.
  • Class stages on a controller cover the handlers its subclasses declare, and class guards cover its autocomplete handlers. See the upgrade notes and the other.

Beyond slash commands

  • Components route by customId pattern, with typed params such as {count:int}, and route() builds the ids a pattern matches. A select menu's choices arrive in its params. See Buttons, selects and modals.
  • Context menus type the interaction a handler receives from its builder. See Context menus.
  • Message commands: @MessageHandler('roll {sides:int}') matches a message word by word after a prefix or a mention, with typed params, flags, aliases, usage replies and a built-in !help. Keywords match in any case, and only the most specific pattern runs; see the upgrade note. A message command can tell its author about an error or a cooldown in a direct message, with messages.dmOnError and messages.dmOnCooldown. See Message commands and Message params.
  • Reactions route by emoji name or id, and reach a handler from a bot only with { bots: true }; see the upgrade note and Reactions.
  • Gateway events with @On and @Once, through the same pipeline. See Gateway events.
  • Lifecycle hooks: onReady and onShutdown, in dependency order, and app.stop() shuts the bot down from code. See Lifecycle hooks.
  • Providers supply values, classes and async factories under a token, injected with @Inject(token). See Providers.
  • Handler discovery: HandlerRegistry lists every handler, for a help command. See Handler discovery.

Building and shipping

  • Command registration is configurable: globally or to servers, to a development server under --dev, at startup or only with meocord register. A builder that throws is named in the registration error; see the upgrade note and Registering commands.
  • Sharding, in one process or a process per shard, with ShardContext.call to reach every shard, its results typed as the JSON they arrive as. See Sharding.
  • Self-contained builds pack native addons into dist, and run on Bun as on Node.js. optionalExternals covers packages a dependency tries to load and runs without. See Self-contained builds.
  • logLevel in meocord.config.ts, or MEOCORD_LOG_LEVEL for one run, sets which lines the logger prints. See Logging.
  • Mistakes MeoCord refuses as the bot loads, such as an invalid pattern or two handlers for one command, are reported as one message that names the class, and the method where there is one, with the source file in a built app, and the bot exits 1. Among them are two handlers of one command, two component handlers with the same pattern, a method-only decorator such as @Defer on a class, and a class whose constructor injects but has no decorator; see the upgrade notes and the other.
  • start --dev rebuilds and restarts on changes to the source, meocord.config.ts and tsconfig.json, and restarts on a change to a development .env file, one bot at a time. See The CLI.
  • Import cycles are a lint warning in meocord/eslint. See ESLint.

Testing

  • invoke and dispatch run a handler through everything the bot runs around it, and getResponse reports what it sent. See Invoke and dispatch.
  • inspectHandler lists what a handler ends up with, in the order it runs. See How a call runs.
  • MeoCordTestingModule.fromApp(App) builds a module from the whole app, wired as the bot wires it. See Testing the whole app.
  • Lifecycle hooks and gateway events run in a test with module.init() and module.emit(). See Testing.
  • Mocks read the data Discord always sends as Discord sends it, including locales and install contexts, and take values for their properties; their option getters throw discord.js's own errors where discord.js would. See Mocks and Options and fields.
  • testCooldownStore checks a cooldown store you write against the built-in one's behaviour. See Checking a store.