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.
Themeis deprecated, and its colours changed; see the upgrade note and Theming. A theme can differ per server and per user withthemeFor, which takes functions or aThemeResolverclass 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 throughExecutionContext, and throwGuardDeniedErrorto 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. MeoCordOptionsnames@MeoCord's options, so a base shared by two app classes keeps the checks@MeoCordmakes. 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
UserErrortells 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.
@Cooldownlimits how often a handler runs, per user, channel, server or everyone, and optionally apart for each value of the call withby. Counts can live in the shard manager, Redis or a database of your own, andcooldownStoreFailuredecides 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:
createMetadatamakes a typed fact about a handler that guards read throughExecutionContext, andapplyDecoratorscombines 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
customIdpattern, with typed params such as{count:int}, androute()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, withmessages.dmOnErrorandmessages.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
@Onand@Once, through the same pipeline. See Gateway events. - Lifecycle hooks:
onReadyandonShutdown, in dependency order, andapp.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:
HandlerRegistrylists 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 withmeocord 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.callto 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.optionalExternalscovers packages a dependency tries to load and runs without. See Self-contained builds. logLevelinmeocord.config.ts, orMEOCORD_LOG_LEVELfor 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
@Deferon a class, and a class whose constructor injects but has no decorator; see the upgrade notes and the other. start --devrebuilds and restarts on changes to the source,meocord.config.tsandtsconfig.json, and restarts on a change to a development.envfile, one bot at a time. See The CLI.- Import cycles are a lint warning in
meocord/eslint. See ESLint.
Testing
invokeanddispatchrun a handler through everything the bot runs around it, andgetResponsereports what it sent. See Invoke and dispatch.inspectHandlerlists 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()andmodule.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.
testCooldownStorechecks a cooldown store you write against the built-in one's behaviour. See Checking a store.