global project update, migrate to poise
This commit is contained in:
parent
e437445861
commit
3cf5977bb6
1907
Cargo.lock
generated
1907
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
15
Cargo.toml
15
Cargo.toml
@ -1,13 +1,12 @@
|
||||
[package]
|
||||
name = "oscuro-discord-bot"
|
||||
name = "oscuro"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.66"
|
||||
shuttle-serenity = "0.20.0"
|
||||
shuttle-runtime = "0.20.0"
|
||||
serenity = { version = "0.11.5", default-features = false, features = ["client", "gateway", "rustls_backend", "model"] }
|
||||
shuttle-secrets = "0.20.0"
|
||||
tokio = "1.26.0"
|
||||
tracing = "0.1.37"
|
||||
oscuro-core = { version = "0.1.0", path = "crates/oscuro-core" }
|
||||
tokio = { version = "1.26.0", features = ["macros", "rt-multi-thread"] }
|
||||
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["crates/oscuro-core", "crates/oscuro-shuttle"]
|
||||
|
12
crates/oscuro-core/Cargo.toml
Normal file
12
crates/oscuro-core/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "oscuro-core"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
poise = "0.6.1"
|
||||
serde = { version = "1.0.198", features = ["derive"] }
|
||||
serde_json = "1.0.116"
|
||||
tokio = { version = "1.26.0", features = ["macros", "rt-multi-thread"] }
|
||||
toml = "0.8.12"
|
||||
tracing = "0.1.37"
|
21
crates/oscuro-core/src/commands.rs
Normal file
21
crates/oscuro-core/src/commands.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use poise::serenity_prelude as serenity;
|
||||
|
||||
use super::errors::BoxedError;
|
||||
use super::Context;
|
||||
|
||||
#[poise::command(prefix_command)]
|
||||
pub async fn register(ctx: Context<'_>) -> Result<(), BoxedError> {
|
||||
poise::builtins::register_application_commands_buttons(ctx).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[poise::command(slash_command, prefix_command)]
|
||||
pub async fn age(
|
||||
ctx: Context<'_>,
|
||||
#[description = "Ooph user"] user: Option<serenity::User>,
|
||||
) -> Result<(), BoxedError> {
|
||||
let u = user.as_ref().unwrap_or_else(|| ctx.author());
|
||||
let response = format!("{}'s account was created at {}", u.name, u.created_at());
|
||||
ctx.say(response).await?;
|
||||
Ok(())
|
||||
}
|
85
crates/oscuro-core/src/config.rs
Normal file
85
crates/oscuro-core/src/config.rs
Normal file
@ -0,0 +1,85 @@
|
||||
use std::fs;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
pub discord_token: String,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn data_dir() -> Result<std::path::PathBuf, ConfigError> {
|
||||
let cwd = std::env::current_dir()?;
|
||||
if cfg!(debug_assertions) {
|
||||
Ok(cwd.join("temp"))
|
||||
} else {
|
||||
Ok(cwd)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open(path: &std::path::Path) -> Result<Config, ConfigError> {
|
||||
fs::read_to_string(path)?.parse()
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> Result<String, ConfigError> {
|
||||
Ok(toml::to_string(self)?)
|
||||
}
|
||||
|
||||
pub fn write(&self, path: &std::path::Path) -> Result<(), ConfigError> {
|
||||
Ok(fs::write(path, self.to_string()?)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
discord_token: String::from("Bot ###"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Config {
|
||||
type Err = ConfigError;
|
||||
fn from_str(s: &str) -> Result<Self, ConfigError> {
|
||||
toml::from_str(s).map_err(|_| ConfigError::Parse)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ConfigError {
|
||||
Parse,
|
||||
StringParse,
|
||||
Serialize,
|
||||
IO,
|
||||
}
|
||||
|
||||
impl std::error::Error for ConfigError {}
|
||||
|
||||
impl std::fmt::Display for ConfigError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Parse => write!(f, "Failed to parse Config from string"),
|
||||
Self::StringParse => write!(f, "Failed to parse environment variable"),
|
||||
Self::Serialize => write!(f, "Failed to serialize Config to TOML"),
|
||||
Self::IO => write!(f, "Faild to write file"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<toml::ser::Error> for ConfigError {
|
||||
fn from(_: toml::ser::Error) -> Self {
|
||||
ConfigError::Serialize
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for ConfigError {
|
||||
fn from(_: std::io::Error) -> Self {
|
||||
ConfigError::IO
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::num::ParseIntError> for ConfigError {
|
||||
fn from(_: std::num::ParseIntError) -> Self {
|
||||
ConfigError::StringParse
|
||||
}
|
||||
}
|
1
crates/oscuro-core/src/errors.rs
Normal file
1
crates/oscuro-core/src/errors.rs
Normal file
@ -0,0 +1 @@
|
||||
pub type BoxedError = Box<dyn std::error::Error + Send + Sync>;
|
63
crates/oscuro-core/src/lib.rs
Normal file
63
crates/oscuro-core/src/lib.rs
Normal file
@ -0,0 +1,63 @@
|
||||
pub mod commands;
|
||||
pub mod config;
|
||||
pub mod errors;
|
||||
|
||||
use errors::BoxedError;
|
||||
use poise::serenity_prelude::{self as serenity, prelude::TypeMapKey, Client};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AppState {
|
||||
pub config: config::Config,
|
||||
}
|
||||
|
||||
impl TypeMapKey for AppState {
|
||||
type Value = AppState;
|
||||
}
|
||||
|
||||
type Context<'a> = poise::Context<'a, AppState, BoxedError>;
|
||||
|
||||
pub async fn client(state: AppState) -> Result<Client, BoxedError> {
|
||||
let intents = serenity::GatewayIntents::non_privileged();
|
||||
let state_copy = state.clone();
|
||||
let framework = poise::Framework::builder()
|
||||
.options(poise::FrameworkOptions {
|
||||
commands: vec![commands::register(), commands::age()],
|
||||
event_handler: |ctx, event, framework, data| {
|
||||
Box::pin(event_handler(ctx, event, framework, data))
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.setup(|ctx, _ready, framework| {
|
||||
Box::pin(async move {
|
||||
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
||||
Ok(state_copy)
|
||||
})
|
||||
})
|
||||
.build();
|
||||
|
||||
let client = serenity::ClientBuilder::new(state.clone().config.discord_token, intents)
|
||||
.framework(framework)
|
||||
.await?;
|
||||
|
||||
{
|
||||
let mut data = client.data.write().await;
|
||||
data.insert::<AppState>(state);
|
||||
}
|
||||
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
async fn event_handler(
|
||||
_ctx: &serenity::Context,
|
||||
event: &serenity::FullEvent,
|
||||
_framework: poise::FrameworkContext<'_, AppState, BoxedError>,
|
||||
_state: &AppState,
|
||||
) -> Result<(), BoxedError> {
|
||||
match event {
|
||||
serenity::FullEvent::Ready { data_about_bot, .. } => {
|
||||
println!("Logged in as {}", data_about_bot.user.name);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
9
crates/oscuro-shuttle/Cargo.toml
Normal file
9
crates/oscuro-shuttle/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "oscuro-shuttle"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
oscuro-core = { version = "0.1.0", path = "../oscuro-core" }
|
||||
shuttle-runtime = "0.43.0"
|
||||
shuttle-serenity = "0.43.0"
|
20
crates/oscuro-shuttle/src/main.rs
Normal file
20
crates/oscuro-shuttle/src/main.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use oscuro_core::{client, config::Config, AppState};
|
||||
|
||||
#[shuttle_runtime::main]
|
||||
async fn main(
|
||||
#[shuttle_runtime::Secrets] secrets: shuttle_runtime::SecretStore,
|
||||
) -> shuttle_serenity::ShuttleSerenity {
|
||||
let token = secrets
|
||||
.get("discord_token")
|
||||
.expect("Variable 'DISCORD_TOKEN' must be set");
|
||||
|
||||
let state = AppState {
|
||||
config: Config {
|
||||
discord_token: token,
|
||||
},
|
||||
};
|
||||
|
||||
let client = client(state).await.expect("Failed to create client");
|
||||
|
||||
Ok(client.into())
|
||||
}
|
55
src/main.rs
55
src/main.rs
@ -1,46 +1,19 @@
|
||||
use anyhow::anyhow;
|
||||
use serenity::async_trait;
|
||||
use serenity::model::channel::Message;
|
||||
use serenity::model::gateway::Ready;
|
||||
use serenity::prelude::*;
|
||||
use shuttle_secrets::SecretStore;
|
||||
use tracing::{error, info};
|
||||
use oscuro_core::{client, config::Config, AppState};
|
||||
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(e) = msg.channel_id.say(&ctx.http, "world!").await {
|
||||
error!("Error sending message: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn ready(&self, _: Context, ready: Ready) {
|
||||
info!("{} is connected!", ready.user.name);
|
||||
}
|
||||
}
|
||||
|
||||
#[shuttle_runtime::main]
|
||||
async fn serenity(
|
||||
#[shuttle_secrets::Secrets] secret_store: SecretStore,
|
||||
) -> shuttle_serenity::ShuttleSerenity {
|
||||
// Get the discord token set in `Secrets.toml`
|
||||
let token = if let Some(token) = secret_store.get("DISCORD_TOKEN") {
|
||||
token
|
||||
} else {
|
||||
return Err(anyhow!("'DISCORD_TOKEN' was not found").into());
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let token = env::var("DISCORD_TOKEN").expect("Variable 'DISCORD_TOKEN' must be set");
|
||||
let state = AppState {
|
||||
config: Config {
|
||||
discord_token: token,
|
||||
},
|
||||
};
|
||||
|
||||
// Set gateway intents, which decides what events the bot will be notified about
|
||||
let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT;
|
||||
|
||||
let client = Client::builder(&token, intents)
|
||||
.event_handler(Bot)
|
||||
client(state)
|
||||
.await
|
||||
.expect("Err creating client");
|
||||
|
||||
Ok(client.into())
|
||||
.expect("Failed to create client")
|
||||
.start()
|
||||
.await
|
||||
.expect("Failed to start client");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user