rust-discord-bot
rust-discord-bot copied to clipboard
A pure-Rust serverless discord chatbot hosted on Cloudflare Workers.
About
A pure-Rust serverless discord chatbot hosted on Cloudflare Workers. With a free account you have up to 100k requests per day. For storing state you can use the bundled workers-rs crate to access KV or Durable objects.
This template is designed for compiling Rust to WebAssembly and publishing the resulting worker to Cloudflare's edge infrastructure.
Setup
- Signup for a Cloudflare account, in the dashboard setup a subdomain (i.e
<mydomain>.workers.dev) - Setup a worker project named
bot(i.ebot.<mydomain>.workers.dev) or pick your own name and update wrangler.toml - Install wrangler CLI with
cargo install wranglerand authenticate with cloudflare viawrangler config - Create a new discord app at https://discord.com/developers/applications and copy your token/application_id/public_key
- Pass those secrets to your bot with
wrangler secret put DISCORD_TOKEN,wrangler secret put DISCORD_PUBLIC_KEY,wrangler secret put DISCORD_APPLICATION_ID - Add bot permissions and grab your Oauth url to invite the bot to your server
- Publish the demo app with
wrangler publish. The template bot contains a single hello command with a dummy autocomplete argument. - Put your bot domain
https://bot.<mydomain>.workers.devin theINTERACTIONS ENDPOINT URLin your discord app page from step 4 - After initial deployment and each time you add a new command on your bot you need to register it with the discord api. To do that simply
curl -X POST https://bot.<mydomain>.workers.dev/register
You should now be able to run the /hello command on discord
Adding new commands
To add a new command simply implement the Command trait. For example to add a ping command
- create a file src/commands/ping.rs
use crate::interaction::{
InteractionApplicationCommandCallbackData, ApplicationCommandOption, ApplicationCommandOptionChoice, ApplicationCommandOptionType
};
use crate::error::InteractionError;
use crate::command::{Command, CommandInput};
use async_trait::async_trait;
pub(crate) struct Ping {}
#[async_trait(?Send)]
impl Command for Ping {
async fn respond(&self, _input: &CommandInput) -> Result<InteractionApplicationCommandCallbackData, InteractionError> {
Ok(InteractionApplicationCommandCallbackData {
content: Some("Pong".to_string()),
choices: None,
embeds: None
})
}
fn name(&self) -> String{
"ping".into()
}
fn description(&self) -> String {
"Send a ping".into()
}
fn options(&self) -> Option<Vec<ApplicationCommandOption>> {
// add any arguments/choices here, more info at https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure
None
}
async fn autocomplete(&self, _input: &CommandInput) -> Result<Option<InteractionApplicationCommandCallbackData>, InteractionError> {
None
}
}
- add your new module in src/commands/mod.rs
- Register your command in
init_commandsin src/command.rs
pub(crate) fn init_commands() -> Vec<Box<dyn Command + Sync>> {
let mut v : Vec<Box<dyn Command + Sync>> = Vec::new();
v.push(Box::new(commands::hello::Hello {}));
// Add this line
v.push(Box::new(commands::ping::Ping {}));
v
}
- publish your package with
wrangler publish - register your new command with discord with
curl -X POST http://bot.<mydomain>.workers.dev/register
You can store and access state using the input context object passed to the respond and autocomplete methods, for example:
let my_val = input.kv_get("my_namespace", "my_key").await?; // the namespace must be first registered on cloudflare dashboard
input.kv_put("my_namespace", "foo", "bar").await?;
Local Dev
With wrangler, you can build, test, and deploy your Worker with the following commands:
# compiles your project to WebAssembly and will warn of any issues
wrangler build
# run your Worker in an ideal development workflow (with a local server, file watcher & more)
wrangler dev
# deploy your Worker globally to the Cloudflare network (update your wrangler.toml file for configuration)
wrangler publish
you can use ngrok to tunnel traffic into your local machine, more info here
Actions Deployments
You can create an a action to automatically deploy your worker & register your commands on each push to main.
Create a Cloudflare API token and use the Edit Cloudflare Workers template.
Then, create a repository secret with your API Token under CF_API_TOKEN and add the following inside .github/workflows/deploy.yml:
name: Deploy to Cloudflare Workers
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
- uses: actions/checkout@v3
- name: Deploy to Cloudflare Workers
env:
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
run: npm i -g wrangler && npx wrangler publish
- name: Curl the worker
run: curl -X POST https://<your_worker_domain>/register
WebAssembly
workers-rs (the Rust SDK for Cloudflare Workers used in this template) is meant to be executed as
compiled WebAssembly, and as such so must all the code you write and depend upon. All crates and
modules used in Rust-based Workers projects have to compile to the wasm32-unknown-unknown triple.
Read more about this on the workers-rs project README.
Credits
based on stateless-discord-bot