WASM-based Discord bot framework. Plugins run as isolated WebAssembly components and can be hot-reloaded while the bot is running.
- WASM plugin runtime with per-plugin CPU/memory limits and permissions
- Discord gateway event dispatch + outbound commands
- REST helpers: HTTP, attachments, components, interactions, modals
- Voice support via Songbird (join, play URL, stop/pause/skip/volume)
- Inter-plugin event bus
- Hot reload and watchdog (auto-unload failing plugins)
- Plugin manifests with dependencies and semver resolution
- FJALL-backed key-value storage
- Discord rate limiting and retry
- Rust 1.85+ (2024 edition)
- Windows, Linux, or macOS
- (Optional)
libopus-dev/opusfor voice features
git clone <repo-url>
cd ynsrvcs-bot
cp .env.example .env
# Edit .env and set DISCORD_TOKEN='Bot YOUR_TOKEN'
cargo run --releaseThe bot loads all .wasm files from the plugins/ directory on startup.
DISCORD_TOKEN=Bot YOUR_BOT_TOKEN_HERE
DATA_DIR=./data
PLUGIN_DIR=./plugins
RUST_LOG=infoplugins/hello.toml:
[plugin]
name = "hello"
version = "0.1.0"
abi_version = 1
[permissions]
http = truehttp-requestsend-channel-message-with-attachmentssend-channel-message-with-componentsreply-to-interaction,edit-interaction-message,show-modalapplication-id,update-presence,request-guild-membersjoin-voice-channel,leave-voice-channel,play-audio-url,stop-audio,pause-audio,resume-audio,skip-audio,set-volumebus-publish,bus-subscribekv-get,kv-set,fs-read,fs-write,get-env,log
See example-plugin/src/lib.rs for a slash command implementation. Build with:
cd example-plugin
cargo build --target wasm32-wasip2 --releaseCopy target/wasm32-wasip2/release/ping_plugin.wasm into plugins/.
[plugin]
name = "music"
version = "0.2.0"
abi_version = 1
[dependencies]
queue = { version = ">=0.1.0" }
[permissions]
http = true
kv = true
bus = true
[limits]
max_memory_bytes = 67108864abi_version— blocks old plugins when the host WIT changesdependencies— required/optional plugin deps with semverpermissions— enabled host functionslimits— WASM resource caps
- Join a channel:
join-voice-channel(guild-id, channel-id, self-mute, self-deaf) - Play audio:
play-audio-url(guild-id, "https://example.com/audio.mp3")
KV is stored by fjall under ./data/plugin-kv (or DATA_DIR).
Replace a .wasm in plugins/ while the bot is running. The bot loads the new version, shuts down the old one if successful, or keeps the old one if the new version fails.
- Each plugin has its own
workspace/ - Plugins communicate only via the event bus
- 5 consecutive failures/traps/timeouts trigger automatic unload
- Restrict
.envpermissions - Mount
DATA_DIRto persistent storage - Use
RUST_LOG=infoorwarn - Consider separate voice workers for very high voice concurrency
This project is licensed under the MIT License.