Plugins are how Popii extends the bot with additional functionality. They plug into the client lifecycle (setup, ready, reload, cleanup) and can augment pop context, register routes, hook into events, and more.
Add plugins to the plugins array in popiiClient():
import { popiiClient, sqlitePlugin, webPlugin, voicePlugin, errorHandlerPlugin } from "popii";
const client = popiiClient({
token: process.env.DISCORD_TOKEN!,
plugins: [
sqlitePlugin(),
voicePlugin(),
webPlugin({ port: 3000, dashboard: true }),
errorHandlerPlugin(),
]
});
Plugins run in the order listed. Higher-priority plugins (set via the priority field on the plugin object) run first regardless of order.
| Plugin | Import | Purpose |
|---|---|---|
sqlitePlugin |
from "popii" |
Injects a Bun SQLite db into every pop |
mongoosePlugin |
from "popii" |
Connects Mongoose to MongoDB |
voicePlugin |
from "popii" |
Voice playback with queue, SponsorBlock, now-playing cards |
webPlugin |
from "popii" |
HTTP server, health check, metrics, web dashboard |
deskPlugin |
from "popii" |
Support ticket system |
popiiAiPlugin |
from "popii" |
AI assistant (OpenAI / Gemini / Anthropic) |
economyPlugin |
from "popii" |
Virtual currency, XP, inventory |
uiPlugin |
from "popii" |
Pagination, forms, interactive prompts |
payPlugin |
from "popii" |
Payment processing |
autoModPlugin |
from "popii" |
Automated moderation |
giveawayPlugin |
from "popii" |
Giveaway system |
canvasPlugin |
from "popii" |
Image generation |
lastFmPlugin |
from "popii" |
Last.fm scrobbling |
captchaPlugin |
from "popii" |
Verification captchas |
activityRotatorPlugin |
from "popii" |
Rotating bot status |
telemetryPlugin |
from "popii" |
Performance monitoring |
commandLoggerPlugin |
from "popii" |
Execution logging |
commandAnalyticPlugin |
from "popii" |
Usage analytics |
permissionGuardPlugin |
from "popii" |
Permission enforcement |
errorHandlerPlugin |
from "popii" |
Graceful error display |
reloadPlugin |
from "popii" |
Adds /reload slash command |
Install plugins from the marketplace with the CLI:
bunx popii add economy # installs popii-plugin-economy
bunx popii search music # search the registry
Or enable auto-discovery to load all installed popii-plugin-* packages automatically:
const client = popiiClient({
token: process.env.DISCORD_TOKEN!,
autoDiscover: true,
pluginConfig: {
"popii-plugin-economy": { currencySymbol: "🪙" }
}
});
A plugin is an object with a name and lifecycle hooks. The setup hook receives the PopiiClient instance once, at startup.
import type { PopiiPlugin } from "popii";
export function myPlugin(options: { greeting: string } = { greeting: "Hello" }): PopiiPlugin {
return {
name: "my-plugin",
setup(client) {
// Runs once at startup. Register listeners, patch the client, etc.
client.onBroadcast("my-plugin:ping", (data) => {
console.log("Received ping:", data);
});
},
ready(client) {
// Runs after the bot is connected to Discord.
console.log(`${options.greeting} from my-plugin!`);
},
cleanup(client) {
// Runs on shutdown. Clean up intervals, close connections, etc.
}
};
}
| Hook | When it runs |
|---|---|
setup(client) |
Before Discord connects — register listeners, create DB tables |
ready(client) |
After Discord connects — safe to use client.discord.guilds.cache |
reload(client) |
After a hot-reload — re-wrap commands with middleware, etc. |
cleanup(client) |
On graceful shutdown — clear intervals, close sockets |
onCommandExecute(pop, cmd) |
Before every command runs — useful for analytics |
onCommandError(err, pop) |
When a command throws — useful for error reporting |
onContextCreate(pop) |
When a pop context is created — inject data into context |
Expose settings that appear automatically in the web dashboard's server settings form:
export function myPlugin(): PopiiPlugin {
return {
name: "my-plugin",
settingsSchema: [
{
id: "welcomeChannel",
label: "Welcome Channel",
type: "channel",
description: "Where to post welcome messages",
category: "My Plugin"
},
{
id: "enableGreeting",
label: "Enable greeting messages",
type: "boolean",
default: true,
category: "My Plugin"
}
],
setup(client) { /* ... */ }
};
}
export function myPlugin(): PopiiPlugin {
return {
name: "my-plugin",
setup(client) {
(client as any)._pluginRoutes?.set("/my-plugin", async (req: Request, session: any, url: URL) => {
return new Response(JSON.stringify({ ok: true }), {
headers: { "Content-Type": "application/json" }
});
});
}
};
}
popii-plugin-<name>"popii" field in package.json with plugin metadata:{
"name": "popii-plugin-my-feature",
"popii": {
"displayName": "My Feature",
"description": "Adds my feature to Popii bots",
"category": "features",
"export": "myFeaturePlugin"
}
}