Released: 19 November 2022
BREAKING CHANGES
- Knub now uses discord.js v14
- Knub now requires TypeScript 4.9 or higher
- Renamed several helper functions:
typedGuildCommand()
➔guildPluginMessageCommand()
typedGlobalCommand()
➔globalPluginMessageCommand()
typedGuildEventListener()
➔guildPluginEventListener()
typedGlobalEventListener()
➔globalPluginEventListener()
typedGuildPlugin()
➔guildPlugin()
typedGlobalPlugin()
➔globalPlugin()
- Plugin properties
configPreprocessor()
andconfigValidator()
have been replaced byconfigParser()
- Message command blueprints now require a
type: "message"
property- This is set automatically by the
guildPluginMessageCommand()
/globalPluginMessageCommand()
helper functions
- This is set automatically by the
- Types
BasePluginConfig
andPartialPluginOptions
have been removed
New features
- Slash commands
Other
- Knub has a new documentation website: https://knub.zeppelin.gg
- BREAKING CHANGE:
Knub.getPluginPerformanceStats()
has been removed and replaced with a new profiler - Knub now has a built-in profiler, accessible from
Knub.profiler
- By default, the profiler tracks plugin load times, event listeners, and command handlers
- You can add track custom items by using the
Profiler.addDataPoint()
function - You can retrieve accumulated performance stats with
Profiler.getData()
- Changed guild loading to be sequential as a test
- Increase guild load queue timeout
- Add plugin load time performance stats
- Fix bug where errors during guild loading did not result in the partial guild being unloaded
PluginBlueprint.dependencies
can now also return a Promise
- BREAKING CHANGE:
PluginBlueprint.dependencies
is now a function that returns an array of dependencies- This should better accomodate circular dependencies
- New overrides:
thread
— Match on thread IDis_thread
— Match on whether a given message is in a thread
channel
andcategory
overrides now support messages within threads- A thread message's
channel
is the thread's parent channel and itscategory
is the parent channel's category
- A thread message's
- BREAKING CHANGE: Knub now uses discord.js v13 and requires Node v16.6+
- Lots of discord.js fixes from @Dark
- BREAKING CHANGE: As an experiment, this release of Knub uses discord.js instead of Eris
- This means many events have different names and objects behave differently. This is not a drop-in replacement for beta.37.
- The specific d.js version used is a PR with support for message components. This will be switched to a pinned master branch release once the PR is merged.
- Version:
monbrey/discord.js#9c42f571093b2565df28b756fdca4ac59cad0fe3
- Version:
- Many
resolveX
helpers have been removed in favor of d.js'sresolve()
functions on Manager objects waitForReaction
helper has been removed in favor of using buttons. More helpers for buttons to follow.
- BREAKING CHANGE: Knub now supports and expects Eris ^0.15.1
- BREAKING CHANGE: Rename
onLoad
toafterLoad
andonUnload
toafterUnload
for clarity - BREAKING CHANGE: Rename
onBeforeLoad
tobeforeLoad
andonBeforeUnload
tobeforeUnload
for consistency with the change above - BREAKING CHANGE:
beforeLoad
has new guarantees and limitations:- Other plugins haven't yet interacted with this plugin
- Other plugins can't interact with this plugin during this function
- For that reason,
PluginData.hasPlugin()
andPluginData.getPlugin()
are unavailable
- BREAKING CHANGE:
afterUnload
has new guarantees and limitations:- Other plugins can't interact with this plugin anymore
PluginData.hasPlugin()
andPluginData.getPlugin()
are unavailable
- BREAKING CHANGE: Remove
guildPluginLoaded
andguildPluginUnloaded
events- Guild loads are now considered a single opaque event. Listen to the
guildLoaded
event instead and then find the plugin via Map inknub.getLoadedGuild().loadedPlugins
.
- Guild loads are now considered a single opaque event. Listen to the
- Both
GuildPluginData
andGlobalPluginData
now contain aloaded
boolean- The value is set to
true
afterbeforeLoad()
but beforeafterLoad()
- The value is set to
false
afterbeforeUnload()
but beforeafterUnload()
- Any persistent loops and checks should check this value and interrupt themselves if it's changed to
false
- The value is set to
- In plugins that queue operations or set timeouts/intervals, you should include logic in
beforeUnload
that either interrupts or waits for these operations to finish - Guild loads and unloads are now properly queued, hopefully resulting in fewer race conditions
- BREAKING CHANGE: Rework how custom override criteria are defined and typed.
-
In addition to
customOverrideCriteria
, PluginTypes now also supportcustomOverrideMatchParams
. This adds types for theextra
property inMatchParams
. -
Instead of a
customOverrideCriteriaMatcher
function, custom override criteria are now defined as acustomOverrideCriteriaFunctions
object. This makes it statically analyzable and allows proper type checks. -
Before:
{ // ...other plugin properties... customOverrideCriteriaMatcher: (pluginData, criteria, matchParams) => { if (criteria.targetUserId && matchParams.extra.targetUserId !== criteria.targetUserId) return false; if (criteria.targetChannelId && matchParams.extra.targetChannelId !== criteria.targetChannelId) return false; return true; } }
After:
{ // ...other plugin properties... customOverrideCriteriaFunctions: { // `matchParams.extra` has types from the `PluginType.customOverrideMatchParams` // `value` has its type from the matching property in `PluginType.customOverrideCriteria` targetUserId: (pluginData, matchParams, value) => matchParams.extra.targetUserId === value, targetChannelId: (pluginData, matchParams, value) => matchParams.extra.targetChannelId === value } }
-
- BREAKING CHANGE: Override criteria can now be resolved asynchronously.
- This means that all calls to
PluginData.config.get*
(PluginConfigManager.get*
) now return promises, except plainPluginData.config.get()
which does no override matching. - Custom override criteria functions can now also return
Promise<boolean>
in addition toboolean
- This means that all calls to
- Fix bug in
CooldownManager
that causedgetCooldownRemaining()
to always return 0 (#7) - Remove
GuildMessage
helper type. UseMessage<GuildTextableChannel>
instead.
- BREAKING CHANGE: Remove all other plugin, event, and command helper function signatures except
(signature)
and the type-helper no-argument signature- In other words, the following signatures have been removed:
guildPlugin(name, blueprint)
/globalPlugin(name, blueprint)
guildEventListener(event, listener)
/globalEventListener(event, listener)
guildEventListener(event, options, listener)
/globalEventListener(event, options, listener)
guildCommand(trigger, run)
/globalCommand(trigger, run)
guildCommand(trigger, signature, run)
/globalCommand(trigger, signature, run)
guildCommand(trigger, signature, options, run)
/globalCommand(trigger, signature, options, run)
- The following signatures remain:
guildPlugin(blueprint)
/globalPlugin(blueprint)
guildPlugin<TPluginType>()(blueprint)
/globalPlugin<TPluginType>()(blueprint)
guildEventListener(blueprint)
/globalEventListener(blueprint)
guildEventListener<TPluginType>()(blueprint)
/globalEventListener<TPluginType>()(blueprint)
guildCommand(blueprint)
/globalCommand(blueprint)
guildCommand<TPluginType>()(blueprint)
/globalCommand<TPluginType>()(blueprint)
- This change was done to narrow these helper functions' role to purely a type helper, and make the relationship between the blueprint objects and these helper functions clearer
- In other words, the following signatures have been removed:
- BREAKING CHANGE: Rename plugin, event, and command helper functions to clarify their new, narrower role
guildPlugin()
➔typedGuildPlugin()
globalPlugin()
➔typedGlobalPlugin()
guildEventListener()
➔typedGuildEventListener()
globalEventListener()
➔typedGlobalEventListener()
guildCommand()
➔typedGuildCommand()
globalCommand()
➔typedGlobalCommand()
- BREAKING CHANGE: Rename
baseTypeHelpers
tobaseCommandParameterTypeHelpers
to clarify their use
- Fix permission level 0 not matching in config level overrides
- Add
chunkLength
parameter tosplitMessageIntoChunks()
helper function
- Fix
args
type inference for command blueprints where multiple signatures had non-overlapping properties
- New
onBeforeUnload
hook for plugins. This is called before the context's plugins are unloaded.
- BREAKING CHANGE: Functions for loading/unloading/reloading individual plugins are now private to the Knub class
- This means that you can only load, unload, or reload an entire context (guild or all global plugins) at once
- This was done to give plugins guarantees about load order and when their dependencies are available and in what state
- New
onAfterLoad
hook for plugins. This is called after all of the context's plugins have loaded. - Fix
onUnload
hook not being called for global plugins
- Allow null for
PluginOverrideCriteria
properties
- The type hint for guild command messages now properly asserts that the message is from a guild, and thus always has a member and its channel is always a textable guild channel.
- Knub is now compiled with TypeScript strict mode
- This has resulted in some typing tweaks/changes
- Update peer dependency to Eris ^0.14.0
- Update
messageReactionAdd
andmessageReactionRemove
event arguments
- Fixed global plugins attempting to load before the bot user was available
- Improve start-up handling to be able to load guilds earlier
- Guilds are now automatically loaded when the bot initially joins the guilds
- BREAKING CHANGE: Fix
guildCommand()
andglobalCommand()
requiring a full PluginData type instead of just a PluginType type. The functions now only require a PluginType, which makes them consistent with other helpers in Knub. - Small other type fixes
- BREAKING CHANGE: Better type-safety between guild plugins and global plugins
plugin()
=>guildPlugin()
,globalPlugin()
eventListener()
=>guildEventListener()
,globalEventListener()
command()
=>guildCommand()
,globalCommand()
PluginEventManager
=>GuildPluginEventManager
,GlobalPluginEventManager
Knub#loadPlugin()
=>Knub#loadGuildPlugin()
,Knub#loadGlobalPlugin()
Knub#unloadPlugin()
=>Knub#unloadGuildPlugin()
,Knub#unloadGlobalPlugin()
Knub#reloadPlugin()
=>Knub#reloadGuildPlugin()
,Knub#reloadGlobalPlugin()
PluginData
=>GuildPluginData
,GlobalPluginData
PluginData.guildConfig
=>GuildPluginData.fullConfig
PluginData.globalConfig
=>GlobalPluginData.fullConfig
- Event handling performance improvements by centralizing guild/global event filtering/passing in an
EventRelay
object- Event arguments are now only converted to Knub's object representation once
- Event guild is now also only checked once
- Guild events are now only passed to the
GuildPluginEventManager
objects of the plugins of the matching guild - Global events are now only passed to the
GlobalPluginEventManager
objects of global plugins- This should especially improve performance with presence events, which are often global (e.g.
userUpdate
)
- This should especially improve performance with presence events, which are often global (e.g.
- Update
knub-command-manager
tov8.1.2
, fixing error messages on invalid option values
- Fix the "ready" event listener adding multiple instances of guildAvailable listeners, causing some servers to load multiple times (more pronounced with an async canLoadGuild() function).
- Add some additional checks to make sure servers don't load twice
- If plugin loading fails, unload the entire guild and re-throw the error
- Disable implicit guild restriction on events for global plugins
- BREAKING CHANGE: Removed exclusion modifiers from override values in favor of logical operator criteria.
- For example, instead of this:
...use:
overrides: - channel: "!1234" config: ...
overrides: - not: channel: "!1234" config: ...
- For example, instead of this:
- Fix crash when
typingStart
channel isundefined
- Fix
PluginConfigManager.getMatchingConfig()
breaking if Member doesn't have guild property
- Fix plugin public interface functions getting the calling Plugin's
PluginData
, not their own
- Fix command argument type inference for rest parameters not being an array
- Fix type inference for public interfaces of plugin blueprints created with the
plugin()
helper when usingpluginData.getPlugin()
- BREAKING CHANGE: Removed
PluginClass
. UsePluginBlueprint
(viaplugin()
helper) instead. - BREAKING CHANGE: Removed
logger
.- The logFn option still exists and is used internally, but Knub does not export a generic log function anymore; that is left to the application.
- BREAKING CHANGE:
CommandBlueprint
now always requirespermission
to be set. Set tonull
to make a public command. - BREAKING CHANGE: Plugins loaded only as dependencies no longer register their commands or events.
These plugins have a new property
loadedAsDependency
set totrue
inpluginData
. - Transitive dependencies are now also loaded
- Fix error in getCommandSignature() when command has no signatures
- Trim down the published package size slightly by leaving out test files
- Removed
exports
frompackage.json
until microsoft/TypeScript#33079 is fixed
- Update to
knub-command-manager
v8.1.1 for improved type helper type hints
- BREAKING CHANGE: Combined the
args
andmeta
parameters for event listener functions. Theargs
object is now atmeta.args
.- E.g.
eventListener("messageCreate", ({ args }) => { ... })
- E.g.
- New
plugin()
signatures:plugin(blueprint)
plugin<TPluginType>()(blueprint)
- New
command()
signatures:command(blueprint)
command<TPluginType>()(blueprint)
- New
eventListener()
signatures:eventListener(blueprint)
eventListener<TPluginType>()(blueprint)
CommandBlueprint
now also accepts arrays of triggers- Export
Plugin
andLoadedPlugin
- Add
string
,bool
, andswitchOption
fromknub-command-manager
tobaseTypeHelpers
- Update TypeScript compilation target to
ES2020
for Node.js 14
- BREAKING CHANGE: Combined the
args
andmeta
parameters for command functions. Theargs
object is now atmeta.args
.- E.g.
command("foo", ({ args }) => { ... })
- E.g.
- BREAKING CHANGE: Updated
plugin()
helper signature for clarity and better type inference:plugin(name, blueprint)
plugin<TPluginType>()(name, blueprint)
- Updated
command()
helper signature for clarity and better type inference:command(trigger, run)
command(trigger, signature, run)
command(trigger, signature, options, run)
command<TPluginType>()(trigger, run)
command<TPluginType>()(trigger, signature, run)
command<TPluginType>()(trigger, signature, options, run)
- Updated
eventListener()
helper signature for clarity and better type inference:eventListener(event, listener)
eventListener(event, options, listener)
eventListener<TPluginType>()(event, listener)
eventListener<TPluginType>()(event, options, listener)
EventListenerBlueprint
now extendsOnOpts
rather than having a separateopts
property forOnOpts
- Add
PluginData#state
andBasePluginType#state
to allow plugins to keep plugin-instance-specific state and easily pass it around
- Export
PluginData
interface - Add plugin helper function:
helpers.getMemberLevel()
- Use block comments for
PluginClass
andPluginBlueprint
properties so they're retained in compiled files
- BREAKING CHANGE: Default overrides for a plugin now come before overrides defined in the config
when evaluating overrides
- This behavior feels more intuitive than the previous one, since you'd expect to be "extending" the default overrides, not prepending your own overrides to them
- Added support for config preprocessors and validators
PluginBlueprint.configPreprocessor
,PluginBlueprint.configValidator
PluginClass.configPreprocessor
(static),PluginClass.configValidator
(static)
- Updated peer dependency of Eris to 0.13.3
- Updated target Node.js version to 14
- Updated several dependencies and made required changes to the code on Knub's end
- Updated peer dependency of Eris to 0.13.2
- Updated to
knub-command-manager
v8- Signature strings (for parameters) are no longer parsed implicitly, and it is recommended
to specify them as an object using the new parameter helpers instead. If you'd prefer to use
a string instead, you can parse it with the exported
parseSignature()
helper function. - Options are no longer specified as command options, but as part of the command's signature
- Signature strings (for parameters) are no longer parsed implicitly, and it is recommended
to specify them as an object using the new parameter helpers instead. If you'd prefer to use
a string instead, you can parse it with the exported
- Replaced
asCommand()
helper from beta.0 withcommand()
with better type inference - Replaced
asEvent()
helper from beta.0 withevent()
with better type inference - Replaced
asPlugin()
helper from beta.0 withplugin()
for consistency with the above - Interfaces
PluginOptions
,PartialPluginOptions
, andBaseConfig
now always require specifying the type forTPluginType
- Knub now uses Eris 0.13
- The
Plugin
class is now calledPluginClass
- Deprecated the
GlobalPlugin
class. UsePluginClass
instead. PluginClass
constructor now takes aPluginData
object as its only argument (see below for more details)- Most plugin functionality has been moved to separate "manager" objects:
PluginCommandManager
,PluginEventManager
, etc. These can be accessed through thePluginData
object (see below). - New: PluginBlueprint
- An alternative, static way of defining plugins without using classes
- Makes it easier to split large plugins into smaller chunks
- New: EventListenerBlueprint
- A static way of defining event listeners
- Works with both PluginClass and PluginBlueprint
- New: CommandBlueprint
- A static way of defining a command
- Works with both PluginClass and PluginBlueprint
- New: PluginData object
- A plugin instance specific object with several utilities, such as event and command managers. Most plugin functionality has been moved to PluginData. This object is passed to each plugin and is also available in event filters, command filters, event listeners, and command handlers.
- Changes to
KnubArgs
(the object passed to Knub constructor):- Renamed
plugins
toguildPlugins
options.getEnabledPlugins
is now calledoptions.getEnabledGuildPlugins
- Signature:
(ctx, pluginMap) => Awaitable<string[]>
- Signature:
- Renamed
- Deprecated PluginClass fields:
PluginClass.getDefaultOptions()
— Now a static propertyPluginClass.defaultOptions
PluginClass.getConfig()
— Usethis.config.get()
insteadPluginClass.getMatchingConfig()
— Usethis.config.getMatchingConfig()
insteadPluginClass.getConfigForMsg()
— Usethis.config.getForMsg()
insteadPluginClass.getConfigForChannel()
— Usethis.config.getForChannel()
insteadPluginClass.getConfigForUser()
— Usethis.config.getForUser()
insteadPluginClass.getConfigForMember()
— Usethis.config.getForMember()
insteadPluginClass.hasPermission()
— UsepluginUtils.hasPermission()
insteadPluginClass.addCommand()
— Usethis.commands.add()
insteadPluginClass.removeCommand()
— Usethis.commands.remove()
insteadPluginClass.on()
— Usethis.events.on()
insteadPluginClass.off()
— Usethis.events.off()
insteadPluginClass.clearEventHandlers()
— Usethis.events.clearAllListeners()
insteadPluginClass.matchCustomOverrideCriteria()
— Now a static propertyPluginClass.customOverrideMatcher
with an updated signaturePluginClass.runtimePluginName
— Always the same asPluginClass.pluginName
nowPluginClass.bot
— now calledPluginClass.client
PluginClass.knub
— usethis.pluginData.getKnubInstance()
instead
- New static plugin fields:
commands
— array of command blueprints to register when Knub loads the pluginevents
— array of event listener blueprints to register when Knub loads the plugindefaultOptions
— default options (config + overrides) for the plugincustomOverrideMatcher
— Matcher function for custom overrides, if used
- Functions and interfaces that previously took
TConfig
andTCustomOverrideCriteria
now take aTPluginType
type instead. This is an interface that includes equivalents forTConfig
andTCustomOverrideCriteria
and is used throughout Knub to pass plugin types. - Command
config.extra
no longer containsrequiredPermission
,cooldown
, etc. directly. Instead,config.extra
now contains the original blueprint used to create the command (config.extra.blueprint
), which in turn contains the values that were previously inconfig.extra
. There should rarely be a need to touch these values directly. command()
andevent()
decorators no longer save their data in class metadata, but in the static commands/events arrays instead- Renamed
Knub.getGuildData()
toKnub.getLoadedGuild()
- Fix override conditions resolving to
true
even when unknown conditions were present. Such overrides will now always resolve tofalse
.
- BREAKING CHANGE: Knub's constructor now takes an optional
TGuildConfig
type argument that specifies the type of guild configuration (must extendIGuildConfig
). Defaults toIGuildConfig
. This type argument is also used inIGuildData
andIOptions
, where it is required. These two types were not exported fromindex.ts
, but were still accessible if importing fromKnub.ts
directly, so I'm marking this as a breaking change. - Knub's constructor now takes an optional
TGlobalConfig
type argument that specifies the type of the global configuration (must extendIGlobalConfig
). Defaults toIGlobalConfig
. - BREAKING CHANGE:
IGuildConfig
andIGlobalConfig
no longer allow arbitrary extra properties. You should specify your own types in Knub's constructor instead (where you can also add back support for arbitrary properties if you want). IGuildData
andIOptions
are now exported fromindex.ts
- BREAKING CHANGE: Errors in plugins are now only re-thrown as
PluginError
in production mode (i.e. NODE_ENV=production). Before, this happened regardless of NODE_ENV.- This change was made because catching and then re-throwing the error caused issues with debuggers. Specifically, the call stack when pausing on the re-thrown error would not match that of the original error, presumably because Promise.catch() is handled separately.
- BREAKING CHANGE: Update
knub-command-manager
to v7.0.0- This update adds support for escaping characters in commands with a backslash. This means that backslashes are effectively ignored in argument parsing unless also escaped.
- Add
Plugin.runCommand()
- This function is used internally to run matching commands from messages and can also be used to manually trigger a specific command
- New helper functions:
splitIntoCleanChunks()
- Splits a string into chunks of the specified length, preferring to split at newlinessplitMessageIntoChunks()
- Building on the above, splits a message's content into smaller chunks if the content is longer than Discord's message limit (2000). Retains leading and trailing line breaks, open code blocks, etc.createChunkedMessage()
- Building on the above, sends a chunked message to the specified channel
getInviteLink()
helper now includeshttps://
at the start- Fix role id detection from role mentions in the
role
parameter type andresolveRole
utility function - Add the proper type for the
command
parameter inTCommandHandler
- More
getCommandSignature()
clean-up
- Fix messy triggers in
getCommandSignature()
when using regular, non-regex triggers
- Update
knub-command-manager
to v6.0.0. See full changelog here. The main backwards compatibility breaking change here is that both--option
and-option
are now valid, as well as both-o
and--o
. - Update Eris peer dependency to v0.11.0. See full changelog here.
- Export
TypeConversionError
from knub-command-manager so that bots built on Knub can useinstanceof
against it even if they have a different version of knub-command-manager required locally (and thus not deduped in node dependencies).
getCommandSignature()
: fix prefix showing as a full regex toString() instead of just the pattern- Wrap command usage info in an inline code block when encountering an error when matching a command
- BREAKING CHANGE:
configUtils.getMatchingPluginOptions
is nowconfigUtils.getMatchingPluginConfig
, returning only the config part of the passed options instead of the whole options object - BREAKING CHANGE: Overrides no longer support the
type
option ("all"/"any"). All criteria are now required to match for the override to apply, except when using the newall
andany
criteria, which are described in the next bullet point. - Add support for chaining override criteria with new
all
andany
special criteria. The value for either of these should be an array of further sets of criteria to evaluate.all
only evaluates totrue
if every set of criteria within it also evaluate totrue
any
evaluates totrue
if any set of criteria within it evaluates totrue
- An empty array as the value for either of these evaluates to
false
- Add
not
special override criterion. Its value should be a set of criteria to evaluate. If the set of criteria matches,not
evaluates tofalse
and vice versa. - Add support for custom override criteria
Plugin
now has a second generic type that defines the type of an optionalextra
key in overridesPlugin.matchCustomOverrideCriteria
can be defined by plugins to resolve these custom override criteria
- Empty override criteria now always evaluate to
false
, i.e. don't match- I.e. an override with just the
config
property or none at all - Plugins can naturally choose to treat their custom criteria however they want, e.g. evaluate to
true
by default
- I.e. an override with just the
- Fix decorator command pre-filters (e.g. permission checks) being run on all loaded servers, not just the current one
- Fix error when compiling the project on a case-sensitive file system
- Update
knub-command-manager
to v5.2.0, restoring support for async type conversion functions
- Fix regression where command arguments were no longer being passed to the command handler as the argument value, but the full argument object instead. Arguments are now passed as values again, as intended.
- BREAKING CHANGE: Interfaces/types from
knub-command-manager
are no longer exported from Knub. To use them in your project, addknub-command-manager
as a dependency to the project instead. - Update
knub-command-manager
to v5, returning support for command signature overloads (though without doing it the hacky way - adding a new command for each overload/signature - this time) - Update the signature of
getCommandSignature()
to no longer require passing the prefix/trigger. Trigger and signature can now be overwritten with 2 new optional arguments.
- Export extra types:
ICommandContext
ICommandExtraData
IKnubPluginCommandDefinition
IKnubPluginCommandConfig
IKnubPluginCommandManager
- Update
knub-command-manager
to4.4.0
. This allows accessing the original sources of command triggers viacommand.originalTriggers
.
This release contains lots of BREAKING CHANGES. You have been warned!
- Commands are now managed through
knub-command-manager
.- Accessing
this.commands
in plugins is no longer supported (usethis.addCommand()
,this.removeCommand()
, andthis.getRegisteredCommands()
instead) - Command config types have changed somewhat: several values are now under
config.extra
instead - Custom argument type functions now get a context object as the second parameter
- Potentially other changes. TypeScript should warn about most of them as long as they are type-related.
- Accessing
- Remove modifier support from
configUtils.mergeConfig
(e.g. "+arrayProp" or "-arrayProp")- Modifiers made static analysis harder and were generally not very well documented. Most of their functionality can be replaced with a different plugin config structure instead (e.g. named groups).
- Remove "target" param from
configUtils.mergeConfig
(now always returns a new object) - Replace usages of lodash.at with a custom function, remove all lodash dependencies
- Instead of "=overrides" to replace overrides, add a new replaceDefaultOverrides property to plugin config
- PermissionDecorator, CooldownDecorator, and LockDecorator now modify the commands/events decorator metadata directly
instead of being applied in
Plugin.runLoad()
- This improves static access to plugin commands/event handlers
- Decorators can still be used in any order
- Add exported
pluginUtils
object with the following functions:getPluginDecoratorCommands()
- returns command decorator metadata of the plugin staticallygetPluginDecoratorEventListeners()
- returns event listener decorator metadata of the plugin statically
- Fix bug where a guild was not properly unloaded if it had 0 plugins loaded
- BREAKING CHANGE: Deprecate
description
property fromICommandConfig
in favor of new, arbitrary typeinfo
property - BREAKING CHANGE: Deprecate
name
anddescription
properties fromPlugin
in favor of new, arbitrary typepluginInfo
property- Note that the
pluginName
property for the plugin's internal name still exists and is still required
- Note that the
- BREAKING CHANGE: change capitalization on certain command parameter types (which are no longer case-insensitive since v20.0.0):
userid
→userId
channelid
→channelId
- Add new command parameter types:
textChannel
voiceChannel
- As with the regular
channel
type, these types are only usable in guilds
- Fix bug where command pre-filters were never run if the command didn't also have regular filters
- Commands can now have
preFilters
in addition to regular filters. These are run right after permission checks, before any command argument types or command errors are handled. This makes them ideal for custom permission checks.
- Command filter functions are now also passed the plugin instance as a third parameter (plays nicer with typings than just binding in this case)
- Command filter functions now have their context (
this
) bound to the plugin instance
- Fix bug where mergeConfig could cause unintended deep modifications to the source objects
- Abort
loadGuild()
if the bot is not in the guild. This could happen with reloads prior to the fix.
- Export
configUtils
- Update Eris peer dependency to
^0.10.1
- Security patches to some dependencies
- Add support for category overrides. The override property is called
category
and, like channel overrides, matches if any of the specified categories matches.
- Add getInviteLink helper function (#1)
- Fix not being able to use
null
for the restrict param of the event decorator
- Allow partial members in
Plugin.getMemberLevel
- BREAKING CHANGE: Argument type names are no longer case-insensitive
- Add new helper functions:
disableLinkPreviews()
disableCodeBlocks()
deactivateMentions()
- When showing command argument type conversion errors, the original error messages thrown from the conversion function are now used. Also applies to errors thrown from custom type functions.
- Make error messages for default command argument types more descriptive
- Export
ICommandDefinition
andIMatchedCommand
fromCommandManager
inindex.ts
- Fix error when adding reactions in
waitForReaction()
if the message is deleted before the reactions are all added
- The
waitForReaction()
helper function no longer waits for all reactions to be added before accepting reaction events
- There is a new exported function,
getCommandSignature
, that can be used to get a human-readable description of a command/its signature
- You can now specify aliases for commands
- Command parameter overloads now also work when using a CommandManager directly, not just with the command decorator
- Channel arguments are now only resolved to the current guild's channels (as it was intended)
Plugin.getMatchingConfig
now supports the same match params asPlugin.hasPermission
- that is, you can pass a Message or Member and have the user/channel/level/roles be inferred from those
- BREAKING CHANGE: Plugin options no longer have a permissions property. Instead, permissions are now part of the plugin's config,
and permission checks via
hasPermission()
/permission
decorator are wrappers around getting a matching config and checking that the value at the permission's path istrue
.- This change was made to simplify complex permissions and to unify the two systems that were, more or less, identical
- Add
sendErrorMessage()
andsendSuccessMessage()
to Knub and Plugin. These can be used to send unified (formatting-wise) error messages across plugins. The actual functions to send the message can be specified in Knub options withsendErrorMessageFn
andsendSuccessMessageFn
. - Command errors have been improved, and they now also send the command's usage/signature on error
- Add support for argument overloads in command options (could be done with multiple commands with the same name previously)
- Add support for custom command argument types. They can either be specified globally in Knub options, or per-plugin with the customArgumentTypes property.
- Old locks are now garbage collected after they haven't been acquired in 120 seconds
- Fix erroneous error message in plugin loading
- Fix type error when specifying global plugins to load
- Fix missing
ts-essentials
dependency
- Make Plugin TConfig and TPermissions types less strict (no longer requires an index signature)
- Add the same generics to GlobalPlugin as Plugin
- Fix overrides with modifiers being marked as invalid when using config/permission types
- Fix overrides requiring full plugin config/permissions when using config/permission types
- Tweak plugin option types to work more reliably
- Remove support for "runtime options" in IKnubArgs
- If you need to override a plugin's default options, extend that plugin instead!
- Remove support for overriding a plugin's name in IKnubArgs
- See above
- Changes to config value usage in plugins:
- Remove the following configValue functions:
configValue()
configValueForMemberIdAndChannelId()
configValueForMsg()
configValueForChannel()
configValueForUser()
configValueForMember()
- Add the following new getConfig functions:
getConfig()
getMatchingConfig()
getConfigForMemberIdAndChannelId()
getConfigForMsg()
getConfigForChannel()
getConfigForUser()
getConfigForMember()
- Add optional generic types to Plugin that define the type of the plugin's config and permissions
- These changes were made to allow proper static analysis of config and permission values in plugin code
- Remove the following configValue functions:
- Update to Typescript 3.3(.3333)
- Add
Knub.getLoadedGuilds()
- Add support for cooldowns. Cooldowns can be set on commands via the
cooldown
andcooldownPermission
command config values, or with the newcooldown(time, permission)
decorator. If the permission is set, users with this permission are exempt from the cooldown. Cooldowns can also be managed manually in plugins viathis.cooldowns
, or by creating your ownCooldownManager
object.
- Fix type error that prevented the build from.. building
- Remove blocking functionality
- Add locks. Locks can be acquired in plugins via
this.locks.acquire(string|string[])
or with the newlock
decorator, and then unlocked vialock.unlock()
(done automatically with the decorator). When using locks, the promise returned byacquire()
will wait for the old matching locks to unlock before resolving. This replicates the old blocking functionality, but is opt-in and more flexible by allowing you to be as specific as you want with your locks.- There is, by default, a 10 second timeout after which locks will automatically unlock, similar to the timeout with
blocking before. This can be changed by calling
setLockTimeout
on aLockManager
instance, by passing in the desired lock timeout as the first parameter when creating aLockManager
, or by giving the lock timeout as the second parameter toacquire()
. Lock timeouts are always specified in milliseconds. - When using the decorator, the lock object is also passed to the event/command handler:
- For events, as an extra argument at the end
- For commands, as part of the
command
object that's passed as the last argument
- There is, by default, a 10 second timeout after which locks will automatically unlock, similar to the timeout with
blocking before. This can be changed by calling
- Add performance debug functionality
- Errors thrown in a Plugin's onLoad() no longer crash the bot, but show a warning in the console (and the plugin won't be marked as loaded)
- Changed the following properties/methods of
Plugin
fromprivate
toprotected
:guildConfig
(property)pluginOptions
(property)mergedPluginOptions
(property)getMergedOptions()
(method)clearMergedOptions()
(method)runCommandsInMessage()
(method)
- The installed package should no longer contain certain unneeded files
- Fix invalid command parsing when using multiple options
- Add
bool
/boolean
type for command options (and arguments)- A command option used as a "switch" (i.e. just
--option
) with the bool type have its value converted totrue
- Otherwise, all values are truthy except "false" and "0"
- A command option used as a "switch" (i.e. just
- Fix error when matching commands without any options
- Rename command definition "options" property to "config"
- Commands now support options (
--option=value
), defined inconfig.options
- For commands in plugins, option values will be placed in the same args object (second argument to command handler) as the matched command arguments
- Errors in plugin event handlers and commands are now thrown as a
PluginError
- Fix error when resolving user id from an unknown message in
Plugin.hasPermission
- Add safeguard for unknown channel when converting typingStart event to a guild
- Remove typingStop from utils.eventToGuild (wasn't being used anymore)
- Add
Plugin.hasPermission()
This is also used internally to check for command/event permissions, so it should now be easy to replicate that functionality in custom message/event handlers and similar use cases. - Allow overriding all known surrounding/nested config/permission values by specifying a config/permission value with
the key
*
. Useful for e.g. setting all permissions at once in an override.
- Plugins and global plugins are now listed in an array instead of an object in the Knub constructor.
- Plugins are now expected to specify their own static
pluginName
property. This can be overridden by the user, however, so if your plugin needs to access its own name at runtime for whatever reason, the newruntimePluginName
property should be used. - Only the first matched command that passes all checks is run for any message. This allows e.g. having both a wildcard and hardcoded command parameters simultaneously by putting the hardcoded parameter in the command name itself and declaring it before the wildcard command.
- Added the following utility functions to resolve Eris/Discord objects from strings that contain IDs, mentions, etc:
- utils.resolveUser
- utils.resolveMember
- utils.resolveChannel
- utils.resolveRole
- Fixed channel mention matching. Match snowflakes more strictly.
- Commands no longer match when extra arguments are present. Rest and catch-all parameters can still be used as normal.
- Fix
"messageDeleteBulk"
event being swallowed when restricted to"guild"
- Fix falsy ignoreSelf and restrict params being ignored in
event
decorator
- Add
Plugin.configValueForMemberIdAndChannelId()
- Fix type definition for LoggerFn (
msg, level
->level, msg
)
- Add
nonBlocking()
decorator (accessible through the same decorator object ascommand
andevent
) that makes the event listener/command handler non-blocking (see 12.0.0 below)
- Event handlers in plugins are now run sequentially, waiting for any promises returned to resolve before running the next handler
- If you don't need this behaviour, simply don't return a promise from your event/command handler
- This also affects commands, though note that commands were already run sequentially before this update - this change just makes it so commands across different plugins also run sequentially.
- Increase startup timeout warning delay from 10sec to 30sec
- I.e. the "This is taking unusually long. Check the token?" warning
- Add
Plugin.hasPlugin()
andPlugin.getPlugin()
for easier and more standardized interoperability between plugins
- Replace default YAML config files with JSON
- Removes dependency on js-yaml
- Replace default winston logger with a custom function
- Removes dependency on winston
- Allow specifying a custom logging function
logFn
in Knub constructor argumentuserArgs.options
- Update to TypeScript 3.1
- Knub now requires Node.js 10 or higher
- Add
waitForReply
helper
- Default plugin overrides are now included after user overrides. This is, in most cases, more intuitive, because it allows e.g. default mod overrides to apply even if the user specifies their own overrides.
- Allow specifying "=overrides" in plugin config to replace default overrides entirely
- Fix infinite loop when reloading all global plugins
- Fix error when reloading global plugins
- Allow arbitrary props in guild and global config
- Add
getGlobalConfig
function to the Knub instance
- Fix
getEnabledPlugins
being called with a weirdthis
- Fix
userId
andchannelId
parameter types
- Allow applying multiple decorators for commands or events for one function
- Move argument type conversion before command error handling. This should help with parameter-overloaded commands.
- Add
userId
andchannelId
command parameter types
- Fix
commandUtils.convertToType
accepting invalid number values for number params
- Fix
waitForReaction
ignoring all reactions to bot messages
- Add
restrictToUserId
param towaitForReaction
helper
- Fix slow reaction adding in waitForReaction
- Fix "missing argument" errors being shown for commands that you don't have a permission to run
- Ignore errors when removing reactions from the
waitForReaction()
message
- Fix
waitForReaction()
reacting to its own reactions (heh)
- Fix
waitForReaction()
failing to add the reactions
- Add timeout parameter to
waitForReaction()
(default 15sec)
- Null/undefined values for command parameters are no longer converted to the specified type. This fixes e.g. a missing string argument getting the value "null" (as a string).
- Fixed error when using optional catchAll arguments
- Global config is now properly loaded from
global.yml
by default
- Fix
mergeConfig()
error when merging a null value
- Fix
Plugin.getMergedConfig()
throwing an error if default plugin options don't contain aconfig
orpermissions
key
- Change default
getEnabledPlugins()
function so all plugins, except those that are explicitly disabled, are loaded by default
- Change default
getEnabledPlugins()
function so it respects the plugin'senabled
value (only if explicitly disabled)
- Overrides from default plugin options and actual plugin options are now concatted instead of overwritten
- Exported various interfaces to help with typings
- Fix default value being required for
Plugin.configValue()
- Switch from Discord.js to Eris
- New config format
- Pass the plugin object as the third argument in the
guildPluginLoaded
andguildPluginUnloaded
events
- Lock Discord.js version to 11.3.2
- Allow passing runtime config to Plugins and GlobalPlugins by passing an array of
[Plugin, <config>]
inplugins
orglobalPlugins
. This runtime config is available to plugins inthis.runtimeConfig
. If no config is passed (i.e. by using the regular way of setting plugins),this.runtimeConfig
is set tonull
.
- Fix
Knub.getGuildData()
- Add
Knub.getGuildData()
- Knub now extends
EventEmitter
and emits several events:loadingFinished
guildLoaded
guildUnloaded
guildPluginLoaded
guildPluginUnloaded
globalPluginLoaded
globalPluginUnloaded
- Add
Knub.getPlugins()
andKnub.getGlobalPlugins()
- Knub now takes an arguments object as the first and only argument
- Changed default prefix from ! to mentioning the bot
- Added global plugins
- Loaded once when the bot is started. Can be reloaded with
Knub.reloadAllGlobalPlugins()
- exported as
GlobalPlugin
- Registered just like regular plugins, but using the
globalPlugins
property instead - Non-guild-specific permissions
- Loaded once when the bot is started. Can be reloaded with
- Guild/channel/user/message checks for events should now be more reliable
- Renamed
Plugin.parent
toPlugin.knub