Built-in plugins
All built-ins are exported from popii-framework/plugins. Add factories to plugins in popii.config.ts; order normally does not matter because Popii resolves declared dependencies before setup.
import { defineConfig } from "popii-framework/config";import { sqlitePlugin, economyPlugin, levelsPlugin } from "popii-framework/plugins";
export default defineConfig({ token: process.env.DISCORD_TOKEN!, plugins: [sqlitePlugin(), economyPlugin(), levelsPlugin()],});Plugin options are validated immediately with Zod. Unknown keys, missing credentials, dependency conflicts, command collisions, and absent optional packages fail with actionable errors. A failed setup is rolled back and cleanup runs in reverse order.
At a glance
Section titled “At a glance”| Plugin | Factory | Main purpose | Requirements |
|---|---|---|---|
| Activity Rotator | activityRotatorPlugin | Rotating Discord presence | Activities list |
| AI | popiiAiPlugin | AI context and /ask | Provider API key |
| Audit Log | auditLogPlugin | Durable audit trail and backups | Persistence |
| AutoMod | autoModPlugin | Message, raid, and join protection | Relevant gateway intents |
| Auto Role | autoRolePlugin | Self-service role panels | Persistence |
| Canvas | canvasPlugin | Image and now-playing rendering | @napi-rs/canvas |
| CAPTCHA | captchaPlugin | Web verification gate | Site and secret keys |
| Command Analytics | commandAnalyticPlugin | Command usage counters | Persistence |
| Command Logger | commandLoggerPlugin | Structured command logs | None |
| Desk | deskPlugin | Tickets and transcripts | Persistence, UI recommended |
| Economy | economyPlugin | Balances, daily claims, transfers | Persistence |
| Error Handler | errorHandlerPlugin | Friendly command errors | None |
| Games | gamesPlugin | Coinflip, slots, RPS, blackjack | Economy |
| GDPR | gdprPlugin | User export and deletion | Persistence |
| Giveaway | giveawayPlugin | Timed giveaways and rerolls | Persistence |
| Last.fm | lastFmPlugin | Music profiles and charts | API credentials, persistence |
| Levels | levelsPlugin | XP, ranks, leaderboard | Persistence |
| Moderation | moderationPlugin | Cases and moderation actions | Persistence |
| Mongoose | mongoosePlugin | MongoDB persistence provider | mongoose |
| Notify | notifyPlugin | Twitch and YouTube notifications | Provider credentials as needed |
| Payments | payPlugin | Patreon and Ko-fi webhooks | Experimental |
| Permission Guard | permissionGuardPlugin | Central command permission checks | None |
| Poll | pollPlugin | Timed interactive polls | Persistence |
| Reaction Roles | reactionRolesPlugin | Reaction-to-role mappings | Persistence |
| Reminders | remindersPlugin | Personal and repeating reminders | Persistence |
| RSS | rssPlugin | Feed delivery | Persistence, outbound HTTPS |
| Snapshots | snapshotsPlugin | Activity snapshots and recovery metadata | Persistence |
| SQLite | sqlitePlugin | Local SQLite persistence provider | Writable durable disk |
| Starboard | starboardPlugin | Multiple reaction boards | Persistence |
| Suggestions | suggestionsPlugin | Suggestions and voting | Persistence |
| Telemetry | telemetryPlugin | Error capture callback | None |
| UI | uiPlugin | Prompts, forms, pagination, sessions | Persistence optional |
| Voice | voicePlugin | Music playback and queue | Voice peers, FFmpeg, extractor |
| Web | webPlugin | OAuth dashboard and operations | Encryption key, OAuth, SQLite |
| Webhook | webhookPlugin | Verified inbound webhook server | Listening port |
| Welcome | welcomePlugin | Welcome messages and cards | Canvas optional |
Activity Rotator
Section titled “Activity Rotator”activityRotatorPlugin({ activities, intervalMs? }) rotates the bot user’s Discord activity after readiness. Each activity has a Discord ActivityType and a static name or (client) => string. intervalMs defaults to 60 seconds. The timer is stopped during cleanup.
import { ActivityType } from "discord.js";import { activityRotatorPlugin } from "popii-framework/plugins";
activityRotatorPlugin({ intervalMs: 60_000, activities: [{ name: client => `${client.discord.guilds.cache.size} servers`, type: ActivityType.Watching }],});popiiAiPlugin({ provider, apiKey, model?, systemPrompt?, locale? }) supports openai, gemini, anthropic, and deepseek. It adds pop.ai.generate() and pop.ai.chat() and registers /ask. Store the API key in an environment variable; it must never be committed or placed in dashboard settings. Provider failures become command errors rather than exposing credentials.
Audit Log
Section titled “Audit Log”auditLogPlugin() provides the framework audit adapter used by privileged dashboard and guild operations. It records actor, guild, action, target, safe before/after values, request or operation IDs, and timestamps. It supports filtered Cakemix history, redacted exports, and configuration backup/restore events. OAuth data, cookies, raw headers, connection strings, and paths are excluded. Persistence required.
AutoMod
Section titled “AutoMod”autoModPlugin({ maxAiChecksPerMinute?, maxAiChecksPerUserPerMinute? }) installs configurable invite, word, mention, caps, link, account-age, anti-raid, and optional AI-assisted checks. Guild settings select actions and log channels. AI checks default to bounded guild and user limits; enforcement also respects Discord role hierarchy and bot permissions. Enable the Message Content and member-related gateway intents for the rules you use.
Auto Role
Section titled “Auto Role”autoRolePlugin() registers /autorole panel and the associated button workflow. Managers configure available roles through guild settings/dashboard. Popii refuses unsafe managed roles and roles above the bot. Persistence required.
Canvas
Section titled “Canvas”canvasPlugin({ fontPath?, fontFamily? }) adds pop.canvas.create(width, height) and pop.canvas.nowPlayingCard(track, options?). CanvasBuilder exposes chainable drawing helpers and produces Discord attachments. Install the peer dependency with bun add @napi-rs/canvas. A supplied font path must point to a trusted local font; otherwise Popii tries platform fonts and falls back safely.
CAPTCHA
Section titled “CAPTCHA”captchaPlugin({ siteKey, secretKey }) registers /verify, pop.captcha.sendVerificationPanel(), and guild settings for the verified role and messages. The secret key stays server-side. Verification callbacks must use the configured public web origin and must not trust a role or guild supplied only by the browser.
Command Analytics
Section titled “Command Analytics”commandAnalyticPlugin() counts command use by command, guild, and time period for safe dashboard metrics. It does not record command secrets or full message content. Persistence required. Use commandLoggerPlugin when you need execution logs rather than aggregates.
Command Logger
Section titled “Command Logger”commandLoggerPlugin({ logExecution?, logCompletion?, logErrors? }) adds middleware that logs who invoked a command, its completion time, and failures through the command logger. All three switches default to true. The plugin does not log command option values; avoid adding raw interaction objects in custom logger transports.
deskPlugin({ categoryId?, supportRoleId?, panelTitle?, panelMessage?, transcriptChannelId? }) registers /ticket panel, button-driven ticket creation, closing forms, participant handling, and transcripts. Cakemix exposes authorized transcript lists and inert, escaped transcript content. uiPlugin() enables the richer close form. Persistence required. Ensure the bot can manage channels and that its role is above the support role.
Economy
Section titled “Economy”economyPlugin({ currencyName?, dailyAmount? }) registers /daily, /balance, /leaderboard, and /transfer. Balances are guild-scoped; transfers are atomic and reject invalid or insufficient amounts. Cakemix provides balances, history, leaderboards, and audited manager adjustments. Persistence required.
Error Handler
Section titled “Error Handler”errorHandlerPlugin({ incidentChannelId? }) maps known command failures to friendly Cakemix responses. When incidentChannelId is set, unexpected errors are also posted to that Discord channel for owner incident tracking. Treat that channel as privileged because incident embeds can contain stack traces; user-facing replies remain generic.
gamesPlugin({ minBet?, maxBet?, cooldownMs?, slotSymbols? }) registers /coinflip, /slots, /rps, and /blackjack. Bets use the Economy balance atomically, enforce configured bounds and cooldowns, and cannot make a balance negative. minBet may not exceed maxBet. Requires economyPlugin() and a persistence provider.
gdprPlugin() registers /privacy export and /privacy delete. Export gathers supported plugin-owned user records; deletion requires confirmation and removes or anonymizes records according to each plugin’s policy. It does not export bot secrets, other users’ data, or raw database files. Persistence required.
Giveaway
Section titled “Giveaway”giveawayPlugin() registers /giveaway start, /giveaway cancel, and /giveaway list, with button entry and timed completion. Cakemix adds entrant inspection and idempotent, audited rerolls. Duration, winner count, guild ownership, eligibility, and current state are validated server-side. Persistence required.
Last.fm
Section titled “Last.fm”lastFmPlugin({ apiKey, apiSecret, scrobble?, nowPlaying? }) registers /lastfm link, unlink, profile, recent, nowplaying, toptracks, and topartists. Links and privacy choices are user-owned. API credentials remain server-side; profiles are not public unless the user enables that behavior. Persistence required.
Levels
Section titled “Levels”levelsPlugin({ xpPerMessage?, cooldownMs?, onLevelUp? }) awards guild-scoped message XP and registers /rank and /leaderboard. xpPerMessage accepts a positive fixed value or [minimum, maximum]. Cooldowns prevent message spam farming. Cakemix includes progress, history, reward configuration, and audited adjustments. Requires Message Content only if your surrounding command/message behavior needs it. Persistence required.
Moderation
Section titled “Moderation”moderationPlugin({ defaultReason? }) registers /warn, /mute, /unmute, /kick, /ban, /unban, /note, /case, and /cases, plus configured moderation logging. Cakemix supports member search, case detail, reason edits, voiding, and safe actions. Every action checks actor and bot role hierarchy immediately before mutation and records its Discord and audit outcomes. Persistence required.
Mongoose
Section titled “Mongoose”mongoosePlugin({ uri, ...connectOptions }) connects MongoDB and exposes the framework persistence capability to storage-neutral plugins. Install mongoose separately. Do not include credentials in logs or dashboard fields. Use TLS and a least-privilege database user in production. It conflicts with sqlitePlugin().
mongoosePlugin({ uri: process.env.MONGODB_URI!, serverSelectionTimeoutMS: 10_000 })Notify
Section titled “Notify”notifyPlugin({ twitch?, youtube? }) polls enabled Twitch and YouTube sources and delivers configured guild notifications. Twitch accepts clientId, clientSecret, and an optional polling interval; YouTube accepts an optional polling interval. Provider credentials stay server-side, polling is deduplicated, and unavailable providers do not reveal upstream bodies.
Payments
Section titled “Payments”payPlugin({ patreon?, kofi? }) is experimental. It receives verified Patreon or Ko-fi webhook events and invokes configured callbacks. Patreon uses webhookSecret; Ko-fi uses verificationToken; both allow a port/path and lifecycle callbacks. Put it behind TLS and a reverse proxy, keep verification secrets out of source control, and make callbacks idempotent. Do not use browser-supplied payment status as authorization.
Permission Guard
Section titled “Permission Guard”permissionGuardPlugin() centrally enforces command permissions, guildOnly, owner, blacklist, and related runtime declarations before command execution. It complements Discord’s command permission hints; it does not replace dashboard authorization or Discord role-hierarchy checks.
pollPlugin() registers /poll create and /poll end, creates button-voted timed polls, and stores votes and results. Cakemix provides active/ended lists, detail, totals, and configured privacy behavior. Poll ownership and guild scope are checked before ending. Persistence required.
Reaction Roles
Section titled “Reaction Roles”reactionRolesPlugin() registers /reactionrole add, remove, and list. Mappings are guild-scoped and validate the target message, emoji, bot access, and assignable role. Enable the Guild Message Reactions intent. Persistence required.
Reminders
Section titled “Reminders”remindersPlugin() registers /remind plus /reminders list and cancel. It supports one-time, daily, weekly, and monthly schedules and optional delivery channels. Stored schedules survive restarts; delivery failures are summarized safely. Persistence required.
rssPlugin() manages guild feed delivery, while Cakemix provides create, edit, pause, resume, delete, test, and preview workflows. Feed requests default to HTTPS, revalidate redirects, reject loopback/private/link-local/multicast/metadata addresses, and enforce timeout and body-size limits. Raw upstream bodies and headers are never returned. Persistence required.
Snapshots
Section titled “Snapshots”snapshotsPlugin() registers /activity for 7-, 14-, or 30-day activity summaries and stores safe aggregate snapshots. Cakemix configuration snapshots support diff preview and controlled restoration with a pre-restore backup, schema validation, confirmation, transactional application, and audit record. It does not expose raw database files. Persistence required.
SQLite
Section titled “SQLite”sqlitePlugin({ filename? }) opens the Bun SQLite persistence provider and prepares plugin tables as plugins initialize. filename defaults to popii.db; production must use durable writable storage and regular external backups. SQLite is appropriate for a single Popii process. It conflicts with mongoosePlugin() and is separate from the Web plugin’s private session database.
Starboard
Section titled “Starboard”starboardPlugin({ defaultThreshold?, defaultEmoji? }) registers /starboard add|list|edit|remove and /starlb. It supports multiple boards per guild, custom emoji/thresholds, message mirroring, and leaderboards. Bot-authored and invalid target messages are guarded against loops. Enable Guild Message Reactions. Persistence required.
Suggestions
Section titled “Suggestions”suggestionsPlugin() registers /suggest and button voting, with staff status transitions and notes. Cakemix separates personal submissions from manager moderation. User-supplied text is escaped and guild-scoped. Persistence required.
Telemetry
Section titled “Telemetry”telemetryPlugin({ onCapture? }) wraps commands and snaps to capture failures with a small safe context and recent-error summary. The callback receives the Error and command/user/guild identifiers. Redact before sending to any external service; never attach pop, interactions, environment objects, or configuration wholesale.
uiPlugin({ successColor?, errorColor?, infoColor?, useSnapsForPagination? }) adds pop.success, error, info, paginate, prompt, confirm, form, awaitMessage, wizard, and pop.session. Colors are 24-bit integers. Sessions use the active persistence provider when present and otherwise an in-memory store that does not survive restarts. All collectors are scoped to the initiating user and cleaned up.
voicePlugin(options?) registers /play, /skip, /stop, /pause, /resume, /queue, and /nowplaying. Options cover debug logging, IPv4/IPv6 preference, proxy rotation, SponsorBlock, crossfade, and YouTube PO-token configuration. Install the voice peers and provide FFmpeg plus a supported extractor. Current Discord voice dependencies negotiate modern voice encryption, while Popii owns queue state, requester attribution, lyrics, and Cakemix controls.
Never expose proxy credentials, extractor arguments, PO tokens, visitor data, temporary paths, or media cookies in dashboard output or logs.
webPlugin(options) starts the Hono Cakemix portal. Required options are publicUrl and Discord OAuth clientId/clientSecret; POPII_WEB_ENCRYPTION_KEY must be a base64-encoded 32-byte key. Optional groups configure database, sessions, proxy trust, branding, and audit/task/diagnostic features. Web sessions and security metadata always live in the Web plugin’s private SQLite database.
See Dashboard setup, OAuth and security, and dashboard contributions for the complete configuration and extension model.
Webhook
Section titled “Webhook”webhookPlugin({ port, endpoints }) starts one Bun HTTP listener. Each endpoint declares a path, method, handler, optional rate limit, and HMAC verification. Built-in sha256, sha1, and md5 presets select conventional signature headers; verify can implement a provider-specific check. Signature comparison is timing-safe. Bind it behind TLS, set explicit limits, and validate payloads again inside handle.
Welcome
Section titled “Welcome”welcomePlugin() sends configurable join/leave messages and can render welcome cards when Canvas is available. Guild settings select channels, content, tokens, and presentation. The plugin escapes user-controlled values and degrades to a normal message when image rendering is unavailable. Enable the Guild Members intent.
Dashboard contribution API
Section titled “Dashboard contribution API”defineDashboardContribution() and the dashboard types are extension contracts, not a separately enabled runtime plugin. Contributions declare member, guild-manager, or owner pages and Zod-validated actions; they cannot register arbitrary Hono routes or JSX. The Web plugin retains authentication, capability checks, CSRF, rate limits, audit, layouts, and error isolation. See Dashboard contributions.
Removed legacy plugins
Section titled “Removed legacy plugins”The following IDs remain in the manifest registry only so tooling can give clear migration guidance. They have no executable implementation:
popii-plugin-manager: browser/runtime package installation is removed; use the CLI and restart.popii-reload: runtime code/plugin reload is removed; use the supervised development process.popii-sandbox: arbitrary code execution is removed with no compatibility mode.
Validate your setup
Section titled “Validate your setup”Run these checks after changing plugins:
bunx popii doctorbunx popii syncbunx popii testdoctor checks peer dependencies and configuration. sync catches command collisions before deployment. For a locally linked checkout, use the generated project’s package scripts instead of relying on a globally installed popii binary.