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]
|
[package]
|
||||||
name = "oscuro-discord-bot"
|
name = "oscuro"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.66"
|
oscuro-core = { version = "0.1.0", path = "crates/oscuro-core" }
|
||||||
shuttle-serenity = "0.20.0"
|
tokio = { version = "1.26.0", features = ["macros", "rt-multi-thread"] }
|
||||||
shuttle-runtime = "0.20.0"
|
|
||||||
serenity = { version = "0.11.5", default-features = false, features = ["client", "gateway", "rustls_backend", "model"] }
|
[workspace]
|
||||||
shuttle-secrets = "0.20.0"
|
resolver = "2"
|
||||||
tokio = "1.26.0"
|
members = ["crates/oscuro-core", "crates/oscuro-shuttle"]
|
||||||
tracing = "0.1.37"
|
|
||||||
|
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 oscuro_core::{client, config::Config, AppState};
|
||||||
use serenity::async_trait;
|
use std::env;
|
||||||
use serenity::model::channel::Message;
|
|
||||||
use serenity::model::gateway::Ready;
|
|
||||||
use serenity::prelude::*;
|
|
||||||
use shuttle_secrets::SecretStore;
|
|
||||||
use tracing::{error, info};
|
|
||||||
|
|
||||||
struct Bot;
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
#[async_trait]
|
let token = env::var("DISCORD_TOKEN").expect("Variable 'DISCORD_TOKEN' must be set");
|
||||||
impl EventHandler for Bot {
|
let state = AppState {
|
||||||
async fn message(&self, ctx: Context, msg: Message) {
|
config: Config {
|
||||||
if msg.content == "!hello" {
|
discord_token: token,
|
||||||
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());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set gateway intents, which decides what events the bot will be notified about
|
client(state)
|
||||||
let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT;
|
|
||||||
|
|
||||||
let client = Client::builder(&token, intents)
|
|
||||||
.event_handler(Bot)
|
|
||||||
.await
|
.await
|
||||||
.expect("Err creating client");
|
.expect("Failed to create client")
|
||||||
|
.start()
|
||||||
Ok(client.into())
|
.await
|
||||||
|
.expect("Failed to start client");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user