Clean starting point for building an external (npm-installable) plugin for Advanced Discord Bot (ADB).
See adb-plugin-reminders (sibling repo) for a complete, working example built from this template.
- Copy this folder / use as a GitHub template repo, rename it to
adb-plugin-<your-name>. - Find-and-replace
adb-plugin-templatewith your real package name inplugin.jsonandpackage.json. - Naming rule: the package name (and the folder name, if run as a local plugin) must start with
adb-plugin-— that's the exact stringPluginManagerscansnode_modules/for. - Implement your feature in
index.js/commands//models/. - Update this README.
When the bot runs with plugin isolation enabled (the default), an npm-installed
plugin runs in a sandboxed worker thread — it cannot touch process.env,
require('fs'), the raw Discord client, or Mongo directly. It reaches those
only through capability-gated RPC on ctx. Keep your plugin isolation-safe:
- Never use
ctx.client— it isnullin a worker. Usectx.discord.*. - Never
require("discord.js")/require("mongoose")at runtime. Model schemas are the exception: pass a plain schema object toctx.defineModel, and Core compiles it. - Event payloads are serialized plain objects, not discord.js instances.
Read ids defensively:
eventPayload.guildId || eventPayload.guild?.id. - Declare everything in
capabilities. The broker denies any RPC whose capability you didn't declare. Missing a capability = silent failure at runtime (the call throws "Missing capability: ...").
Every plugin's entry file (default index.js) must export:
async function load(ctx) { /* ... */ }
module.exports = { load };PluginManager calls load(ctx) once at startup (or on hot-reload). Errors thrown here disable just this plugin — they don't crash the bot.
| Member | What it is |
|---|---|
ctx.client |
null in isolated mode — do not use. Use ctx.discord. |
ctx.discord |
Isolation-safe Discord surface: sendToChannel(channelId, {content, embeds, files}), sendDM(userId, payload), getGuild(guildId), getMember(guildId, userId), fetchChannel(channelId) |
ctx.db |
Capability-gated DB: getPluginConfig(guildId, name), updatePluginConfig(...), plus profile/server methods under storage:read-profiles/write-profiles |
ctx.registerCommand(command) |
Register a { data, execute } slash command |
ctx.registerEvent(eventName, handler) |
Listen to a Discord event (handler gets a serialized payload in isolated mode) |
ctx.defineModel(modelName, schema) |
Compile a Mongo model namespaced as plugin_<your-plugin-name>_<modelName> |
ctx.hooks.on(hookName, handler) / ctx.hooks.emitHook(hookName, payload) |
Inter-plugin hook bus (needs hooks:subscribe / hooks:emit) |
ctx.scheduler.schedule(name, cron, fn) / ctx.scheduler.cancel(name) |
Recurring jobs (needs scheduler:cron) |
ctx.config.env |
Empty in isolated mode — secrets never leave Core |
ctx.logger |
.info() / .warn() / .error(), namespaced to your plugin |
ctx.overrideCommand() is not available in isolated mode — use ctx.registerCommand().
| Field | Required | Notes |
|---|---|---|
name |
yes | must start with adb-plugin- |
version |
yes | semver |
description / author |
yes | |
main |
no | defaults to index.js |
displayName |
no | shown in marketplace UI |
requiresRestart |
no | true disables hot-reload eligibility |
isolation |
no | true (default) run in a worker |
manifestVersion |
v2 | set to 2 |
process |
v2 | { model: "pooled"|"persistent"|"oneshot", maxExecutionMs, memoryMb, persistentReason } |
capabilities |
yes for isolated | what the broker lets you do — { storage: [...], discord: [...], hooks: [...], scheduler: [...] } |
permissions |
v2 | mirror of capabilities + network.outbound host allowlist, filesystem, childProcess, nativeAddons |
configSchema |
no | JSON Schema → per-guild settings UI, read via ctx.db.getPluginConfig(guildId, name) |
discordPermissions |
no | Discord permission flags for the bot invite link |
npm install
npm testtest/local-harness.js loads your plugin against test/mock-ctx.js — a fake in-memory ctx — and exercises registered commands directly. Extend both files as you add features. This catches logic bugs fast; it does not replace a real smoke test (see below).
- Have a working local checkout of Advanced Discord Bot.
- Symlink or copy your plugin folder into its
plugins/directory:or, to test the actualln -s $(pwd) /path/to/Advanced-Discord-Bot/plugins/adb-plugin-yournamenode_modules/adb-plugin-*discovery path a real npm install would use:npm link cd /path/to/Advanced-Discord-Bot && npm link adb-plugin-yourname
- Start the bot, confirm your plugin's load-log line appears.
- If you added slash commands, run
npm run deployin the bot repo — command logic hot-reloads, but Discord command registration needs an explicit deploy. - Exercise the feature for real in a Discord server.
npm login
npm publishAnyone installs it with npm install adb-plugin-yourname into their bot's root — ADB's PluginManager auto-discovers any node_modules/adb-plugin-* folder containing a plugin.json.
See REGISTRY-SETUP.md in the main ADB repo — fork the registry repo, add an entry to plugins.json with your npmPackage name, open a PR.
This project is licensed under the GNU Affero General Public License v3.0. See the LICENSE file for details.
This repository follows the policies of the main ADB project.
- Contribution Guidelines: CONTRIBUTING.md
- Code of Conduct: CODE_OF_CONDUCT.md
- Security Policy: SECURITY.md