-
Notifications
You must be signed in to change notification settings - Fork 259
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- implement Service for serenity::Client to deploy discord bots with shuttle - add hello-world and postgres examples
- Loading branch information
Showing
9 changed files
with
188 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[package] | ||
name = "hello-world" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
serenity = { version = "0.11.5", default-features = false, features = ["client", "gateway", "rustls_backend", "model"] } | ||
shuttle-service = { version = "0.4.1", features = ["bot-serenity"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
name = "hello-world-serenity-bot" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
use serenity::async_trait; | ||
use serenity::model::channel::Message; | ||
use serenity::model::gateway::Ready; | ||
use serenity::prelude::*; | ||
use std::env; | ||
|
||
struct Bot; | ||
|
||
#[async_trait] | ||
impl EventHandler for Bot { | ||
async fn message(&self, ctx: Context, msg: Message) { | ||
if msg.content == "!hello" { | ||
if let Err(why) = msg.channel_id.say(&ctx.http, "world!").await { | ||
println!("Error sending message: {:?}", why); | ||
} | ||
} | ||
} | ||
|
||
async fn ready(&self, _: Context, ready: Ready) { | ||
println!("{} is connected!", ready.user.name); | ||
} | ||
} | ||
|
||
#[shuttle_service::main] | ||
async fn serenity() -> shuttle_service::ShuttleSerenity { | ||
// Configure the client with your Discord bot token in the environment. | ||
let token = std::env::var("DISCORD_TOKEN").expect("Expected a token in the environment"); | ||
|
||
// Set gateway intents, which decides what events the bot will be notified about | ||
let intents = GatewayIntents::GUILD_MESSAGES | ||
| GatewayIntents::DIRECT_MESSAGES | ||
| GatewayIntents::MESSAGE_CONTENT; | ||
|
||
let client = Client::builder(&token, intents) | ||
.event_handler(Bot) | ||
.await | ||
.expect("Err creating client"); | ||
|
||
Ok(client) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[package] | ||
name = "serenity-postgres" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
serde = "1.0" | ||
serenity = { version = "0.11.5", default-features = false, features = ["client", "gateway", "rustls_backend", "model"] } | ||
shuttle-service = { version = "0.4.1", features = ["sqlx-postgres", "bot-serenity"] } | ||
sqlx = { version = "0.5", features = ["runtime-tokio-native-tls", "postgres"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
name = "postgres-serenity-bot" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
DROP TABLE IF EXISTS todos; | ||
|
||
CREATE TABLE todos ( | ||
id serial PRIMARY KEY, | ||
user_id BIGINT NULL, | ||
note TEXT NOT NULL | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
use serenity::async_trait; | ||
use serenity::model::prelude::*; | ||
use serenity::prelude::*; | ||
use shuttle_service::error::CustomError; | ||
use sqlx::{Executor, FromRow, PgPool}; | ||
use std::env; | ||
|
||
struct Bot { | ||
database: PgPool, | ||
} | ||
|
||
#[derive(FromRow)] | ||
struct Todo { | ||
pub id: i32, | ||
pub note: String, | ||
} | ||
|
||
#[async_trait] | ||
impl EventHandler for Bot { | ||
async fn message(&self, ctx: Context, msg: Message) { | ||
// The user_id of the user sending a command | ||
let user_id = msg.author.id.0 as i64; | ||
|
||
// Add a new todo using `~todo add <note>` and persist it in postgres | ||
if let Some(note) = msg.content.strip_prefix("~todo add") { | ||
let note = note.trim(); | ||
sqlx::query("INSERT INTO todos (note, user_id) VALUES ($1, $2)") | ||
.bind(note) | ||
.bind(user_id) | ||
.execute(&self.database) | ||
.await | ||
.unwrap(); | ||
|
||
let response = format!("Added `{}` to your todo list", note); | ||
msg.channel_id.say(&ctx, response).await.unwrap(); | ||
|
||
// Remove a todo by calling `~todo remove <index>` with the index of the todo you want to remove | ||
// from the `~todo list` output | ||
} else if let Some(todo_index) = msg.content.strip_prefix("~todo remove") { | ||
let todo_index = todo_index.trim().parse::<i64>().unwrap() - 1; | ||
|
||
let todo: Todo = sqlx::query_as( | ||
"SELECT id, note FROM todos WHERE user_id = $1 ORDER BY id LIMIT 1 OFFSET $2", | ||
) | ||
.bind(user_id) | ||
.bind(todo_index) | ||
.fetch_one(&self.database) | ||
.await | ||
.unwrap(); | ||
|
||
sqlx::query("DELETE FROM todos WHERE id = $1") | ||
.bind(todo.id) | ||
.execute(&self.database) | ||
.await | ||
.unwrap(); | ||
|
||
let response = format!("Completed `{}`!", todo.note); | ||
msg.channel_id.say(&ctx, response).await.unwrap(); | ||
|
||
// List the calling users todos using ´~todo list` | ||
} else if msg.content.trim() == "~todo list" { | ||
let todos: Vec<Todo> = | ||
sqlx::query_as("SELECT note, id FROM todos WHERE user_id = $1 ORDER BY id") | ||
.bind(user_id) | ||
.fetch_all(&self.database) | ||
.await | ||
.unwrap(); | ||
|
||
let mut response = format!("You have {} pending todos:\n", todos.len()); | ||
for (i, todo) in todos.iter().enumerate() { | ||
response += &format!("{}. {}\n", i + 1, todo.note); | ||
} | ||
|
||
msg.channel_id.say(&ctx, response).await.unwrap(); | ||
} | ||
} | ||
|
||
async fn ready(&self, _: Context, ready: Ready) { | ||
println!("{} is connected!", ready.user.name); | ||
} | ||
} | ||
|
||
#[shuttle_service::main] | ||
async fn serenity(#[shared::Postgres] pool: PgPool) -> shuttle_service::ShuttleSerenity { | ||
// Configure the client with your Discord bot token in the environment. | ||
let token = std::env::var("DISCORD_TOKEN").expect("Expected a token in the environment"); | ||
|
||
// Run the schema migration | ||
pool.execute(include_str!("../schema.sql")) | ||
.await | ||
.map_err(CustomError::new)?; | ||
|
||
// Set gateway intents, which decides what events the bot will be notified about | ||
let intents = GatewayIntents::GUILD_MESSAGES | ||
| GatewayIntents::DIRECT_MESSAGES | ||
| GatewayIntents::MESSAGE_CONTENT; | ||
|
||
let bot = Bot { database: pool }; | ||
let client = Client::builder(&token, intents) | ||
.event_handler(bot) | ||
.await | ||
.expect("Err creating client"); | ||
|
||
Ok(client) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters