Skip to content
GitHub

4.1.0-beta.6 changelog

Published

Entries marked Breaking change a working bot; the migration guide says what to do about them. Every release of the line is in the changelog.

Minor Changes

  • #228 8e1b936 Thanks @l7aromeo! - @MeoCord({ messages: { mention: 'only' } }) starts every message command in a server with a mention of the bot and nothing else, neither a prefix nor the message as plain text, while a direct message starts as usual, after the prefix or as it is; @MessageHandler(pattern, { mention: 'only' }) does the same for one command beside the app's prefix. Discord sends a message's text without the privileged MessageContent intent when the message mentions the bot, and in direct messages, so such commands, and commands with scope: 'dm', no longer need it: MeoCord's startup warning about MessageContent names only a @MessageHandler() listener and commands a prefix or plain text starts in a server, and says what still arrives without it. A mention-only bot can run without the intent, and without applying for it once verified. In a test, resolveRoute(App, { content, dm: true }) reads a message as a direct message.

  • #252 2d0575f Thanks @l7aromeo! - MeoCord's own texts for users go through the app's translator: a message command's usage and each thing wrong with it, the built-in !help and the labels of HandlerRegistry.messageHelp, cooldown and cooldown store refusals, "Command not found!", the generic error, and the default presenter's "Working on it…" and "Oops!". Add a meocord group to any catalog given to @MeoCord({ i18n }), all of it or part, such as meocord: { usage: { heading: 'Cara pakai: {usage}' } }; a text a locale leaves out stays in English, line by line. Answers to an interaction are in the user's language, replies to a message in the server's preferred language, or the default locale's in a DM. MeoCord's English stands as the English catalog, so an English-speaking user or server gets it even under a default locale in another language, unless the app's own en-US or en-GB catalog words the text. The keys and their English are in the new MeoCordMessages type from meocord/interface, and a key MeoCord lacks, or a {param} its English text lacks, fails to compile. Without i18n, every text is the English one it is today.

    • translateError(error, t, target) from meocord/common returns the text the fallback answers an error with, in the language of an interaction, a message or a locale, for an exception filter that answers MeoCord's errors its own way.
    • A message param type takes labelKey, a message key of the app's catalog, for a label in each server's language. @MeoCord refuses one without i18n, or one the default catalog has no message for.
    • expectCompleteCatalog(t, { meocord: true }) requires every locale that is not English to translate each of MeoCord's texts. Without the option it reports only a meocord key MeoCord lacks.

    See Localisation.

  • #251 b4a9e58 Thanks @l7aromeo! - Message commands have a built-in help command, off unless asked for: @MeoCord({ messages: { prefix: '!', help: true } }) answers !help with the commands the caller can use where they asked, one line each with its handler's description, and !help <command> with one command's usage, params, aliases and where it works. help: { command, aliases } names other words. The list leaves out a handler with a guard on its method or controller, since it runs no guards, and one whose new hidden: true option asks to be left out; named, either is shown. !help for words with no handler of their own lists their subcommands, and an unknown name or nothing to list gets a line saying so. It answers only after a prefix or mention, and an app's own help handler always runs instead, with a warning at startup. With replyEmoji the reply begins with the theme's emojis.info.

    The reply follows the app's translations, meocord.help.* in its catalog with @MeoCord({ i18n }), in the server's language, and is English otherwise; see MeoCord's own texts. A presenter's new optional messageHelp(help, message) method writes it instead, from MessageHelp, what the built-in found. HandlerRegistry.messageHelp(message, query?) gives the same model to a help command of the app's own, with help on or off, and MessageHandlerEntry.hidden says whether a handler asked to be left out. hidden also leaves a subcommand out of the usage listing a message naming only a parent gets.

  • #246 6c46b07 Thanks @l7aromeo! - A message that names only a command's leading words, such as !config when config set … and config get … exist, or an unknown subcommand, such as !config reset, gets the usage of each subcommand in reply, under a Usage: heading, one line per handler by its own pattern, where it got no reply. A handler whose pattern matches the message still runs, so a config or config {key} handler takes it as before. A subcommand with a guard, on its method or its controller, inherited ones included, is left out of the listing on purpose, since the listing runs no guards and must not name what a caller may be refused; it still answers its own usage when named, and a parent with nothing left to list gets no reply. App-wide guards do not filter the listing, as they do not filter a usage reply. The reply is a MessageUsageError, answered through the app's global filters and then the fallback, like any usage reply.

  • #231 8395b05 Thanks @l7aromeo! - A button's, select menu's or modal's customId pattern can type a param, {name:type}, with int, number, bool or words to choose from such as {order:asc|desc}, read by the parsers message commands use. The handler receives the value, such as a number for @Command('counter/{count:int}', CommandType.BUTTON), and its params are checked against the pattern when the code compiles; route(pattern).build() takes values of those types. A segment that is not a value of its type matches no route. Such a pattern used to be read as literal text, so it silently never matched; a type a customId cannot hold, such as {target:member}, now stops the bot where it is declared. Beside a text param in the same place, a typed one is tried first, and of two types the narrower (words to choose from, then bool, int, number), whatever order they are declared in; and patterns whose typed segments take no value in common are different routes. For a route with a typed param, resolveRoute adds values, the params as the handler receives them, beside params, which stays their text; every other result is as it was. From meocord/common, RouteValues<Pattern> now gives each typed param the value of its type, such as number for {count:int}, and an untyped param takes any RouteValue, a string, number or bigint, as before; RouteParams<Pattern> names each param without its type.

  • #239 a5868f4 Thanks @l7aromeo! - A context menu handler can declare the kind of interaction its builder registers: UserContextMenuCommandInteraction for a builder that calls setType(ApplicationCommandType.User), or MessageContextMenuCommandInteraction for Message. It had to take the union of both, since declaring one failed to compile with "Unable to resolve signature of method decorator". The union still works. A builder's kind is a value TypeScript cannot read, so the bot checks it as it starts: a handler that declares the other kind stops it, naming both.

    meocord g co context-menu <name> generates a user context menu command with its handler typed to match, and --message generates a message one. The context menu controller in a new project is typed the same way.

Patch Changes

  • #229 551c0eb Thanks @l7aromeo! - A user context menu command and a message context menu command with the same name, which Discord allows, each reach their own @Command handler. The first handler declared under the name took both, so a message command could run the user command's handler. TestingModule.invoke refuses the other kind's interaction the same way, naming both kinds.

  • #243 68638ab Thanks @l7aromeo! - meocord start --dev exits 1 when the bot cannot log in, as meocord start --prod does, instead of watching on with the bot offline: a missing or refused token, refused intents, or Discord being unreachable is not something a code change fixes. It says so in one line, after the reason the bot gave. After any other exit, such as an error at startup or a crash once online, it keeps watching and says The application exited with code N; waiting for changes., then starts the bot again on the next rebuild. With sharding, the shard manager ends the session the same way.

  • #249 4af6d70 Thanks @l7aromeo! - The package's homepage is https://meocord.dev, the documentation site, where npm links it, and a new application's README links MeoCord there.

  • #240 b462777 Thanks @l7aromeo! - module.invoke() checks an interaction's customId against every handler of the testing module, ranked as dispatch ranks them, as it already did for a message. A customId dispatch gives to another handler, such as card/summary beside card/{id}, rejects naming the handler that runs, where it ran the named handler anyway. A handler declared under two patterns gets the params of the one dispatch picks, typed values included. A testing module whose controllers would stop the bot, such as two whose patterns match the same customIds, now makes invoke throw the same startup error with a customId, as dispatch already did; give each such controller its own module.

  • #244 eef71c7 Thanks @l7aromeo! - The editor documentation of the application, the config file and the remaining public helpers says what each is for, with examples that compile, and links the guide: MeoCordFactory, MeoCordApplication, @MeoCord, HandlerRegistry and its entry types, ShardContext, MeoCordConfig, ShardingConfig, CommandRegistrationConfig, RsbuildConfig, the providers and createToken/factoryProvider, Logger, OnReady/OnShutdown, CommandType, DeepPartial/DeepReadonly and the meocord/eslint config. MetadataKey is marked internal: it holds MeoCord's own reflect keys and is not part of the documented API.

  • #226 5c14933 Thanks @l7aromeo! - The JSDoc of every decorator in meocord/decorator, and of CooldownOptions, DeferOptions and CommandBuilderOptions, is rewritten for the hover in your editor: a one-line summary, when to use it and what to use instead, how it works, where it runs in a call, and an example that compiles against the published types. The options of @Guard, @Interceptor, @Observer, @Validate, @Cooldown and @Defer are documented on each option, with its default.

  • #236 01d92f6 Thanks @l7aromeo! - The guide links in the JSDoc of the decorators and the pipeline types point to the 4.1 documentation, the version you installed, and to its pages as they are named: slash commands, buttons, selects and modals, and how a call runs.

  • #241 51282fe Thanks @l7aromeo! - The editor documentation of MessageCommandOptions, MessageHandlerOptions, route, Route, RouteParams, RouteValue and RouteValues says what each is for, with an example that compiles, and links the guide.

  • #234 4c9c1dc Thanks @l7aromeo! - The editor hover for @MessageHandler, @ReactionHandler, ReactionHandlerAction, CommandNotFoundError, MessageUsageError and the message param types (ParamsOf, ParamRefsOf, EntityRef, MessageParamType, MessageParamTypes, CheckedParams, MessageScope, MessagePrefix, ReactionHandlerOptions, ReactionHandlerSettings, MessageUsageIssue, MessageParams) now says in a sentence what each is, when to use it and what to use instead, with an example that compiles against the published types.

  • #230 1004d7b Thanks @l7aromeo! - The JSDoc of the types and helpers a guard, interceptor, filter, pipe or observer works with is rewritten for the hover in your editor: ExecutionContext, GuardDeniedError, ValidationError, createMetadata, SetMetadata, applyDecorators, StageParams, Piped, the stage interfaces, the observer types, the Standard Schema types and the command builder types. Each has a one-line summary and when to use it, and each example compiles against the published types.

  • #232 fcd3457 Thanks @l7aromeo! - The JSDoc of respond, the theme API (useTheme, bindTheme, UseTheme, ThemeCache, Theme and the theme types), the errors a user is shown (UserError, CooldownError, CooldownStoreError), the presenter types, the translator and the cooldown stores now follows the JSDoc standard, for the hover in your editor and the API reference. Every example among them compiles against the published types: those that read an interaction, a database or an app they did not declare are now complete, and MemoryCooldownStore, Theme, cooldownMessage and cooldownStoreMessage gain one.

  • #235 ee38bcb Thanks @l7aromeo! - The JSDoc of meocord/testing's mocks follows the standard in CONTRIBUTING.md: createMockInteraction, createMock, createMockUser, createMockClient, createMockGuild, createMockChannel, createMockMessage, createChatInputOptions, createModalFields, createDiscordError, createMockTheme, withTheme, createMockFn, isMockFunction, clearAllMocks and resetAllMocks, with the types they take and return. Each has a one-line summary, when to use it, and an example that compiles against the published package, and declares everything it uses. MockInstance's methods describe themselves on hover.

  • #233 5f42ad2 Thanks @l7aromeo! - The JSDoc of meocord/testing's module and inspection helpers follows the standard in CONTRIBUTING.md: MeoCordTestingModule, TestingModuleBuilder, TestingModule and their options and results, getResponse, inspectHandler, createExecutionContext, resolveRoute, findRouteConflicts, expectCompleteCatalog and testCooldownStore. Each has a one-line summary, when to use it, and an example that compiles against the published package, and every option documents itself, so the hover in your editor and the API reference say the same thing.

  • #247 b31f281 Thanks @l7aromeo! - bun run lint in an application prints nothing when the code is clean. meocord/eslint gives its import resolver the application's three tsconfigs, so each file resolves aliases through the tsconfig that includes it, and the resolver printed Multiple projects found, consider using a single tsconfig with references… on every run. It now sets the resolver's noWarnOnMultipleProjects, since several projects are the intended setup. Existing applications get it by updating meocord; nothing in them changes.

  • #238 dba5def Thanks @l7aromeo! - A message command's usage reply reads right for every param type: a word of the wrong type is now "lots" is not a valid whole number, so an app's own type labelled with a noun such as emoji reads is not a valid emoji, where it read is not a emoji. A bool param is named a yes or no answer. A role deleted while a command's guards ran is named <@&id> is not a role in this server, and a value of an app's own type whose EntityRef resolves to nothing is not a valid <label>, where both read is not a value of its type. A MessageParamType's label is the bare noun, such as hex colour, as its example now shows. A test that matches the old wording, such as is not a whole number, needs the new one.

  • #250 873aeab Thanks @l7aromeo! - MessageParamType's documentation link in your editor opens the message params page, which covers typed params and your own param types, rather than the message commands page.

  • #225 c862b1f Thanks @l7aromeo! - Importing meocord/common no longer computes the Redis script hash until a RedisCooldownStore uses it. Each script's SHA1 is worked out the first time a store given evalsha runs it, then kept, so an app that never uses Redis hashes nothing, and one without evalsha never needs the hash.

  • #237 69ec4e8 Thanks @l7aromeo! - getResponse(interaction).sent counts only calls Discord accepted. A reply, update, edit or follow-up that Discord refused, such as a reply rejected with 10062 once the three seconds passed, used to count as sent, so a test of what the member sees after an expired interaction could pass while the member saw nothing. The refused call stays in calls, in the order it was made, and carries what it rejected with as error, the new optional field of ResponseCall: every call respond() makes, deferrals and modals included, is marked this way.