Skip to content

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.

PluginFactoryMain purposeRequirements
Activity RotatoractivityRotatorPluginRotating Discord presenceActivities list
AIpopiiAiPluginAI context and /askProvider API key
Audit LogauditLogPluginDurable audit trail and backupsPersistence
AutoModautoModPluginMessage, raid, and join protectionRelevant gateway intents
Auto RoleautoRolePluginSelf-service role panelsPersistence
CanvascanvasPluginImage and now-playing rendering@napi-rs/canvas
CAPTCHAcaptchaPluginWeb verification gateSite and secret keys
Command AnalyticscommandAnalyticPluginCommand usage countersPersistence
Command LoggercommandLoggerPluginStructured command logsNone
DeskdeskPluginTickets and transcriptsPersistence, UI recommended
EconomyeconomyPluginBalances, daily claims, transfersPersistence
Error HandlererrorHandlerPluginFriendly command errorsNone
GamesgamesPluginCoinflip, slots, RPS, blackjackEconomy
GDPRgdprPluginUser export and deletionPersistence
GiveawaygiveawayPluginTimed giveaways and rerollsPersistence
Last.fmlastFmPluginMusic profiles and chartsAPI credentials, persistence
LevelslevelsPluginXP, ranks, leaderboardPersistence
ModerationmoderationPluginCases and moderation actionsPersistence
MongoosemongoosePluginMongoDB persistence providermongoose
NotifynotifyPluginTwitch and YouTube notificationsProvider credentials as needed
PaymentspayPluginPatreon and Ko-fi webhooksExperimental
Permission GuardpermissionGuardPluginCentral command permission checksNone
PollpollPluginTimed interactive pollsPersistence
Reaction RolesreactionRolesPluginReaction-to-role mappingsPersistence
RemindersremindersPluginPersonal and repeating remindersPersistence
RSSrssPluginFeed deliveryPersistence, outbound HTTPS
SnapshotssnapshotsPluginActivity snapshots and recovery metadataPersistence
SQLitesqlitePluginLocal SQLite persistence providerWritable durable disk
StarboardstarboardPluginMultiple reaction boardsPersistence
SuggestionssuggestionsPluginSuggestions and votingPersistence
TelemetrytelemetryPluginError capture callbackNone
UIuiPluginPrompts, forms, pagination, sessionsPersistence optional
VoicevoicePluginMusic playback and queueVoice peers, FFmpeg, extractor
WebwebPluginOAuth dashboard and operationsEncryption key, OAuth, SQLite
WebhookwebhookPluginVerified inbound webhook serverListening port
WelcomewelcomePluginWelcome messages and cardsCanvas optional

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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 })

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

Run these checks after changing plugins:

Terminal window
bunx popii doctor
bunx popii sync
bunx popii test

doctor 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.