Slashy is the first free and open-source Discord bot that allows you to create custom slash commands. It's been around since 2022 and you can track it's progress through the publicly available code.
Slashy's main feature is creating slash commands without being tech savy, or if you just want an easier way to add commands to your server. It supports full command editing through a modal, and the changes are reflected instantly.
Yes, I know, Slashy is a super creative name. I'm not the best at naming things, but I'm sure you'll love it anyway!
Couple of cool features:
- command adding, editing, deleting and listing (for admins/managers)
- tracking the usages of each command (for admins/managers, or the bot host via PostHog)
- ephemeral replies (only the person that runs the command gets to see its reply)
- role-gated commands (multi-select roles per command; empty = everyone)
- full internationalization (all Discord locales are supported, can't vouch on how correct they are)
- admin endpoints (limiting the number of commands per-guild, and banning guilds)
The publicly-hosted version of Slashy can be added with this link, or the button in the header. You can also (pretty easily) host it yourself if you'd like.
Have fun.
slashy-demo.mp4
-
/slashy addopens a modal for adding commands. -
/slashy listshows server commands (only for you). -
/slashy delete <name>lets you delete commands. Has autocomplete. -
/slashy edit <name>edit a command. Has autocomplete too. -
Created commands become real slash commands per guild; replies honor the stored visibility flag.
-
Optional role gate: select allowed roles in the modal; if none are chosen, everyone can run it.
Command replies can have placeholders, check out the full list in PLACEHOLDERS.md.
- Cloudflare Worker entrypoint
src/worker.ts, Bun toolchain, Wrangler deploy. - Discord interactions only; Ed25519 verified in
utils/verify.ts. - D1 binding
DBfor commands and guild metadata. - REST client (
discord/rest-client.ts) for registering commands on the fly.
worker.fetchrejects invalid signatures, logs minimal debug, builds context.routerhandles ping, autocomplete, modal submit,/slashysubcommands, or dynamic commands./slashyrequests runensureBaseCommandto keep the root command in sync.- Modal submit sanitizes name, enforces Manage Server, guild limits, saves to D1, registers/patches the guild command, replies ephemeral.
- Dynamic handler fetches the command row, increments
uses, applies placeholders, replies public or ephemeral per row. IfPOSTHOG_KEYis set, PostHog captures command lifecycle events (create, delete, list, run).
You might've noticed that this repository has 2 weird branches: legacy and legacy-legacy.
Slashy's been worked on for a while. legacy-legacy was v1 written in Python, and legacy was running in Node.js.
The current Slashy version (and hopefully final) runs in Cloudflare Workers off of interactions.
- D1 tables:
commands(reply, description, ephemeral, uses),guilds(max_commands, banned). Guilds default to 50 commands; raise the default withMAX_COMMANDS.
bun run scripts/register-base.tsregisters the base/slashycommand globally or forGUILD_ID=....bun run scripts/reset-commands.tsresets global/slashyand rebuilds commands for the listed guild IDs.
- Auth:
Authorization: Bearer <SLASHY_SECRET>or?secret=...orx-slashy-secret. - Secret: set
SLASHY_SECRETor let the worker log a generated one on first boot. GET /admin/register-baseregisters/slashyglobally.GET /admin/reset-commands?guildId=123,456resets global/slashyand recreates guild commands (excluding/slashy) for listed guilds.POST /admin/guild-limitsets a guild's max commands; body{ "guildId": "123", "limit": 50 }(query params also work).POST /admin/guild-bansets banned flag; body{ "guildId": "123", "banned": true }(query params also work).
DISCORD_TOKEN,DISCORD_APP_ID,DISCORD_PUBLIC_KEYsecrets in Wrangler.DBD1 binding name must matchwrangler.toml.SLASHY_SECRETfor admin endpoints (optional; auto-generated if absent).- Optional:
POSTHOG_KEYto enable analytics;POSTHOG_HOSTdefaults tohttps://eu.i.posthog.com. - Optional:
MAX_COMMANDSto change the default per-guild cap (default 50). - Optional:
GUILD_IDwhen running register/reset scripts for scoped testing. - Copy
.dev.vars.exampleto.dev.vars(or set secrets in dashboard) so the deploy button prompts for them.
bun installwrangler d1 migrations apply slashywrangler secret put DISCORD_TOKEN,DISCORD_APP_ID,DISCORD_PUBLIC_KEYbun dev -- --remote(orbunx wrangler dev src/worker.ts --remoteto hit real Discord)
commands:id,guild_id,name(unique per guild),reply,description(<=100),ephemeral(0/1),allowed_roles(JSON array of role IDs),uses, timestamps.guilds:id,banned,max_commands(default 50, override withMAX_COMMANDS),joined_at.
- Lint/format:
bun run check(Biome). - Deploy:
bun run deploy.
- Bun + Wrangler installed, Cloudflare D1 database bound as
DB(seewrangler.toml). - Discord app with bot token and public key.
- Secrets:
wrangler secret put DISCORD_TOKEN,wrangler secret put DISCORD_APP_ID,wrangler secret put DISCORD_PUBLIC_KEY. - DB schema:
wrangler d1 migrations apply slashy. - Admin secret: set
SLASHY_SECRETor read the generated one from worker logs on first boot. - Register
/slashy:curl -H "Authorization: Bearer $SLASHY_SECRET" https://your-worker/admin/register-base. - Dev:
bun dev -- --remote(orbunx wrangler dev src/worker.ts --remote). - Deploy:
bun run deploy.
GPL-3.0.
