- JavaScript 52.2%
- CSS 27.2%
- HTML 16.7%
- Shell 2%
- Dockerfile 1.9%
| .woodpecker | ||
| public | ||
| src | ||
| .gitignore | ||
| config.json | ||
| docker-compose.yml | ||
| Dockerfile | ||
| LICENSE | ||
| package.json | ||
| README.md | ||
| update-container.sh | ||
SkyEmbeds
A self-hostable Discord bot for creating rich embeds via a beautiful web editor.
By CrystalSky
Features
/createembed #channel— generates a private, one-time editor link/editembed <message link or ID>— opens the editor prefilled from an existing SkyEmbeds message- Live Preview — side-by-side on desktop, tab-switching on mobile
- Discord Markdown — bold, italic, underline, strikethrough, code, code blocks, blockquotes
- Channel Linking — insert
#channel-namementions via dropdown - Accent Color — hex picker + quick-color palette
- Large Image & Thumbnail URL support
- Author name + icon
- Custom Footer text (falls back to hoster info from
config.json) - Create sessions expire after 30 minutes, edit links can be reopened later
- Fully open source & self-hostable
Quick Start
Prerequisites
- Node.js 18+
- A Discord Application & Bot token (Discord Developer Portal)
Installation
git clone https://github.com/yourname/skyembeds.git
cd skyembeds
npm install
Configuration
Edit config.json:
{
"discord": {
"token": "YOUR_BOT_TOKEN",
"clientId": "YOUR_APPLICATION_CLIENT_ID"
},
"server": {
"port": 3000,
"baseUrl": "http://localhost:3000"
},
"hoster": {
"name": "Your Name / Org",
"email": "contact@example.com",
"website": "https://example.com",
"tosUrl": "https://example.com/tos",
"privacyUrl": "https://example.com/privacy"
},
"branding": {
"footerText": "Powered by SkyEmbeds"
}
}
baseUrlmust be the publicly accessible URL of your web server (e.g.https://embeds.yourdomain.com). Discord users will follow this link to open the editor.
Register Slash Commands
npm run deploy-commands
This registers /createembed and /editembed globally. Global commands can take up to 1 hour to propagate.
Start the Bot
npm start
Usage
- Invite the bot to your server with
bot+applications.commandsscopes andSend Messages,Embed Linkspermissions. - In any channel, run
/createembed #channelto create a new embed, or/editembed <message link or ID>to edit an existing one. Discord does not expose reply targets to slash commands, so the edit command needs a link or ID. - The bot replies with an ephemeral message containing your personal editor link.
- Open the link, design your embed, and click Send Embed or Save Changes.
- The embed appears in the chosen channel instantly, or the original message is updated.
- Edit links remain valid for reopening later, until the source message is deleted or the bot can no longer access it.
Project Structure
skyembeds/
├── config.json ← Configuration (token, hoster info, etc.)
├── package.json
├── src/
│ ├── index.js ← Bot + Express server
│ └── deploy-commands.js
└── public/ ← Web editor assets
├── editor.html ← Main editor page
├── expired.html ← Session-expired page
├── css/
│ └── editor.css
└── js/
├── markdown.js ← Discord Markdown renderer
└── editor.js ← Editor logic, preview, send
Docker
A pre-built image is published to the Forgejo registry on every push to main:
docker pull git.crystalsky.dev/crystalsky/skyembeds:latest
Run with a mounted config file:
docker run -d \
--name skyembeds \
-p 3000:3000 \
-v /path/to/your/config.json:/app/config.json:ro \
git.crystalsky.dev/crystalsky/skyembeds:latest
Docker Compose
services:
skyembeds:
image: git.crystalsky.dev/crystalsky/skyembeds:latest
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- ./config.json:/app/config.json:ro
CI/CD (Woodpecker CI)
The .woodpecker/ directory contains two workflow files:
| File | Triggers | Purpose |
|---|---|---|
lint.yaml |
push, pull_request | Installs deps, validates config.json, checks all required files exist |
docker.yaml |
push / manual on main |
Builds a multi-arch image (linux/amd64, linux/arm64) and pushes to the Forgejo registry |
Required Secrets
Set these in your Woodpecker project settings (Settings → Secrets):
| Secret | Value |
|---|---|
reg_url |
git.crystalsky.dev |
reg_owner |
crystalsky |
reg_token |
A Forgejo personal access token with package:write scope |
The project must be marked Trusted in Woodpecker (required for privileged: true used by the buildx plugin).
Self-Hosting with a Domain
For production, place a reverse proxy (nginx, Caddy) in front of the Express server:
Caddy example:
embeds.yourdomain.com {
reverse_proxy localhost:3000
}
Set baseUrl in config.json to https://embeds.yourdomain.com.
License
GPL v3 — see LICENSE