summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorseth <[email protected]>2023-11-30 22:18:51 -0500
committerseth <[email protected]>2023-12-01 07:12:49 -0500
commit76c0f94e6d7aa108424b34826eb7d8514b026287 (patch)
tree7315bd6dfe52c158041bed64ba39781718a69335
parentdb52e639b85d79bed870020aec7a045851ca5ee3 (diff)
feat: use eyre, better logging, & refactor
small commits be damned
-rw-r--r--Cargo.lock113
-rw-r--r--Cargo.toml7
-rw-r--r--src/api/guzzle.rs30
-rw-r--r--src/api/mod.rs6
-rw-r--r--src/api/shiggy.rs30
-rw-r--r--src/colors.rs2
-rw-r--r--src/commands/ask.rs23
-rw-r--r--src/commands/bing.rs6
-rw-r--r--src/commands/convert.rs29
-rw-r--r--src/commands/copypasta.rs10
-rw-r--r--src/commands/mod.rs8
-rw-r--r--src/commands/random.rs42
-rw-r--r--src/commands/teawiespam.rs8
-rw-r--r--src/commands/version.rs6
-rw-r--r--src/handlers/error.rs42
-rw-r--r--src/handlers/event/message.rs (renamed from src/handler/message.rs)36
-rw-r--r--src/handlers/event/mod.rs (renamed from src/handler/mod.rs)16
-rw-r--r--src/handlers/event/pinboard.rs (renamed from src/handler/pinboard.rs)0
-rw-r--r--src/handlers/event/reactboard.rs (renamed from src/handler/reactboard.rs)38
-rw-r--r--src/handlers/mod.rs5
-rw-r--r--src/main.rs73
-rw-r--r--src/utils.rs60
22 files changed, 355 insertions, 235 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 861ee8f..303384f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -222,6 +222,33 @@ dependencies = [
]
[[package]]
+name = "color-eyre"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204"
+dependencies = [
+ "backtrace",
+ "color-spantrace",
+ "eyre",
+ "indenter",
+ "once_cell",
+ "owo-colors",
+ "tracing-error",
+]
+
+[[package]]
+name = "color-spantrace"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2"
+dependencies = [
+ "once_cell",
+ "owo-colors",
+ "tracing-core",
+ "tracing-error",
+]
+
+[[package]]
name = "core-foundation"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -384,6 +411,16 @@ dependencies = [
]
[[package]]
+name = "eyre"
+version = "0.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80f656be11ddf91bd709454d15d5bd896fbaf4cc3314e69349e4d1569f5b46cd"
+dependencies = [
+ "indenter",
+ "once_cell",
+]
+
+[[package]]
name = "flate2"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -706,6 +743,12 @@ dependencies = [
]
[[package]]
+name = "indenter"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
+
+[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -748,6 +791,12 @@ dependencies = [
]
[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
name = "libc"
version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -861,6 +910,12 @@ dependencies = [
]
[[package]]
+name = "owo-colors"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
+
+[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1378,6 +1433,24 @@ dependencies = [
]
[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "siphasher"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1490,6 +1563,7 @@ name = "teawiebot"
version = "1.0.0"
dependencies = [
"bottomify",
+ "color-eyre",
"dotenvy",
"env_logger",
"include_dir",
@@ -1542,6 +1616,16 @@ dependencies = [
]
[[package]]
+name = "thread_local"
+version = "1.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
name = "time"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1597,6 +1681,7 @@ dependencies = [
"mio",
"num_cpus",
"pin-project-lite",
+ "signal-hook-registry",
"socket2 0.5.5",
"tokio-macros",
"windows-sys",
@@ -1684,6 +1769,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
"once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-error"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"
+dependencies = [
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
+dependencies = [
+ "sharded-slab",
+ "thread_local",
+ "tracing-core",
]
[[package]]
@@ -1792,6 +1899,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 69ce46c..9969c79 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,6 +10,7 @@ readme = "README.md"
[dependencies]
bottomify = "1.2.0"
+color-eyre = "0.6.2"
dotenvy = "0.15.7"
env_logger = "0.10.0"
include_dir = "0.7.3"
@@ -22,5 +23,9 @@ reqwest = { version = "0.11.22", default-features = false, features = [
"json",
] }
serde = "1.0.193"
-tokio = { version = "1.33.0", features = ["macros", "rt-multi-thread"] }
+tokio = { version = "1.33.0", features = [
+ "macros",
+ "rt-multi-thread",
+ "signal",
+] }
url = { version = "2.5.0", features = ["serde"] }
diff --git a/src/api/guzzle.rs b/src/api/guzzle.rs
index 17d2c0c..83c159e 100644
--- a/src/api/guzzle.rs
+++ b/src/api/guzzle.rs
@@ -1,6 +1,6 @@
use crate::api::REQWEST_CLIENT;
-use crate::Error;
+use color_eyre::eyre::{eyre, Result};
use log::*;
use reqwest::StatusCode;
use serde::{Deserialize, Serialize};
@@ -11,32 +11,20 @@ struct GuzzleResponse {
}
const GUZZLE: &str = "https://api.mydadleft.me";
+const RANDOM_TEAWIE: &str = "/random_teawie";
-pub async fn get_random_teawie() -> Result<String, Error> {
- let endpoint = "/get_random_teawie";
-
+pub async fn get_random_teawie() -> Result<String> {
let req = REQWEST_CLIENT
- .get(format!("{GUZZLE}{endpoint}"))
- .build()
- .unwrap();
+ .get(format!("{GUZZLE}{RANDOM_TEAWIE}"))
+ .build()?;
info!("making request to {}", req.url());
- let resp = REQWEST_CLIENT.execute(req).await.unwrap();
+ let resp = REQWEST_CLIENT.execute(req).await?;
let status = resp.status();
if let StatusCode::OK = status {
- match resp.json::<GuzzleResponse>().await {
- Ok(data) => Ok(data.url),
- Err(why) => {
- if let Some(url) = why.url() {
- error!("error parsing json from {}! {}", url, why)
- } else {
- error!("couldn't even get the url! {}", why);
- }
-
- Err(Box::new(why))
- }
- }
+ let data = resp.json::<GuzzleResponse>().await?;
+ Ok(data.url)
} else {
error!(
"couldn't fetch random teawie from {}! {}",
@@ -44,6 +32,6 @@ pub async fn get_random_teawie() -> Result<String, Error> {
status
);
- Err(status.to_string().into())
+ Err(eyre!("failed to get random teawie with {status}"))
}
}
diff --git a/src/api/mod.rs b/src/api/mod.rs
index a1e0e97..2ce664e 100644
--- a/src/api/mod.rs
+++ b/src/api/mod.rs
@@ -3,11 +3,13 @@ use once_cell::sync::Lazy;
pub mod guzzle;
pub mod shiggy;
-pub const USER_AGENT: &str = "teawieBot/0.1.0";
+pub const USER_AGENT: &str = "teawieBot/";
pub static REQWEST_CLIENT: Lazy<reqwest::Client> = Lazy::new(|| {
+ let version = option_env!("CARGO_PKG_VERSION").unwrap_or("development");
+
reqwest::Client::builder()
- .user_agent(USER_AGENT)
+ .user_agent(format!("{USER_AGENT}/{version}"))
.build()
.unwrap_or_default()
});
diff --git a/src/api/shiggy.rs b/src/api/shiggy.rs
index 8dbadef..7a582ee 100644
--- a/src/api/shiggy.rs
+++ b/src/api/shiggy.rs
@@ -1,42 +1,30 @@
use crate::api::REQWEST_CLIENT;
-use crate::Error;
+use color_eyre::eyre::{eyre, Result};
use log::*;
use reqwest::StatusCode;
use serde::Deserialize;
const SHIGGY: &str = "https://safebooru.donmai.us";
+const RANDOM_SHIGGY: &str = "/posts/random.json?tags=kemomimi-chan_(naga_u)+naga_u&only=file_url";
#[derive(Deserialize)]
struct SafebooruResponse {
file_url: String,
}
-pub async fn get_random_shiggy() -> Result<String, Error> {
- let endpoint = "/posts/random.json?tags=kemomimi-chan_(naga_u)+naga_u&only=file_url";
-
+pub async fn get_random_shiggy() -> Result<String> {
let req = REQWEST_CLIENT
- .get(format!("{SHIGGY}{endpoint}"))
- .build()
- .unwrap();
+ .get(format!("{SHIGGY}{RANDOM_SHIGGY}"))
+ .build()?;
info!("making request to {}", req.url());
- let resp = REQWEST_CLIENT.execute(req).await.unwrap();
+ let resp = REQWEST_CLIENT.execute(req).await?;
let status = resp.status();
if let StatusCode::OK = status {
- match resp.json::<SafebooruResponse>().await {
- Ok(data) => Ok(data.file_url),
- Err(why) => {
- if let Some(url) = why.url() {
- error!("failed to make a request to {}! {}", url, why)
- } else {
- error!("couldn't even figure out the url! {}", why)
- };
-
- Err(Box::new(why))
- }
- }
+ let data = resp.json::<SafebooruResponse>().await?;
+ Ok(data.file_url)
} else {
error!(
"couldn't fetch random teawie from {}! {}",
@@ -44,6 +32,6 @@ pub async fn get_random_shiggy() -> Result<String, Error> {
status
);
- Err(status.to_string().into())
+ Err(eyre!("failed to get random teawie with {status}"))
}
}
diff --git a/src/colors.rs b/src/colors.rs
index ebd231e..5291933 100644
--- a/src/colors.rs
+++ b/src/colors.rs
@@ -2,12 +2,14 @@ use poise::serenity_prelude::Colour;
pub enum Colors {
Blue,
+ Orange,
}
impl From<Colors> for Colour {
fn from(val: Colors) -> Self {
match val {
Colors::Blue => Colour::from((136, 199, 253)),
+ Colors::Orange => Colour::from((255, 179, 74)),
}
}
}
diff --git a/src/commands/ask.rs b/src/commands/ask.rs
index 7cc82a1..3589484 100644
--- a/src/commands/ask.rs
+++ b/src/commands/ask.rs
@@ -1,6 +1,6 @@
-use crate::consts;
-use crate::utils;
-use crate::{Context, Error};
+use crate::{consts, utils, Context};
+
+use color_eyre::eyre::{Context as _, Result};
/// ask teawie a question!
#[poise::command(prefix_command, slash_command)]
@@ -9,15 +9,10 @@ pub async fn ask(
#[description = "the question you want to ask teawie"]
#[rename = "question"]
_question: String,
-) -> Result<(), Error> {
- match utils::random_choice(consts::RESPONSES) {
- Ok(resp) => {
- ctx.say(resp).await?;
- Ok(())
- }
- Err(why) => {
- ctx.say("idk").await?;
- Err(why)
- }
- }
+) -> Result<()> {
+ let resp = utils::random_choice(consts::RESPONSES)
+ .wrap_err("couldn't choose from random responses!")?;
+
+ ctx.say(resp).await?;
+ Ok(())
}
diff --git a/src/commands/bing.rs b/src/commands/bing.rs
index ed91bb3..b80ebca 100644
--- a/src/commands/bing.rs
+++ b/src/commands/bing.rs
@@ -1,8 +1,10 @@
-use crate::{Context, Error};
+use crate::Context;
+
+use color_eyre::eyre::Result;
/// make sure the wie is alive
#[poise::command(prefix_command)]
-pub async fn bing(ctx: Context<'_>) -> Result<(), Error> {
+pub async fn bing(ctx: Context<'_>) -> Result<()> {
ctx.say("bong!").await?;
Ok(())
}
diff --git a/src/commands/convert.rs b/src/commands/convert.rs
index 1f39ae4..cbbf8dc 100644
--- a/src/commands/convert.rs
+++ b/src/commands/convert.rs
@@ -1,11 +1,13 @@
-use crate::{Context, Error};
+use crate::Context;
+
use bottomify::bottom;
+use color_eyre::eyre::Result;
#[poise::command(
slash_command,
subcommands("to_fahrenheit", "to_celsius", "to_bottom", "from_bottom")
)]
-pub async fn convert(_ctx: Context<'_>) -> Result<(), Error> {
+pub async fn convert(_ctx: Context<'_>) -> Result<()> {
Ok(())
}
@@ -14,7 +16,7 @@ pub async fn convert(_ctx: Context<'_>) -> Result<(), Error> {
pub async fn to_celsius(
ctx: Context<'_>,
#[description = "what teawie will convert"] degrees_fahrenheit: f32,
-) -> Result<(), Error> {
+) -> Result<()> {
let temp = (degrees_fahrenheit - 32.0) * (5.0 / 9.0);
ctx.say(temp.to_string()).await?;
Ok(())
@@ -25,7 +27,7 @@ pub async fn to_celsius(
pub async fn to_fahrenheit(
ctx: Context<'_>,
#[description = "what teawie will convert"] degrees_celsius: f32,
-) -> Result<(), Error> {
+) -> Result<()> {
let temp = (degrees_celsius * (9.0 / 5.0)) + 32.0;
ctx.say(temp.to_string()).await?;
Ok(())
@@ -36,7 +38,7 @@ pub async fn to_fahrenheit(
pub async fn to_bottom(
ctx: Context<'_>,
#[description = "what teawie will translate into bottom"] message: String,
-) -> Result<(), Error> {
+) -> Result<()> {
let encoded = bottom::encode_string(&message);
ctx.say(encoded).await?;
Ok(())
@@ -47,17 +49,8 @@ pub async fn to_bottom(
pub async fn from_bottom(
ctx: Context<'_>,
#[description = "what teawie will translate from bottom"] message: String,
-) -> Result<(), Error> {
- let d = bottom::decode_string(&message);
- match d {
- Ok(decoded) => {
- ctx.say(decoded).await?;
- Ok(())
- }
- Err(why) => {
- ctx.say("couldn't decode that for you, i'm sowwy!! :((".to_string())
- .await?;
- Err(Box::new(why))
- }
- }
+) -> Result<()> {
+ let decoded = bottom::decode_string(&message)?;
+ ctx.say(decoded).await?;
+ Ok(())
}
diff --git a/src/commands/copypasta.rs b/src/commands/copypasta.rs
index 14a6673..313cd13 100644
--- a/src/commands/copypasta.rs
+++ b/src/commands/copypasta.rs
@@ -1,7 +1,8 @@
-use crate::{utils, Context, Error};
+use crate::{utils, Context};
use std::collections::HashMap;
+use color_eyre::eyre::{eyre, Result};
use include_dir::{include_dir, Dir};
use log::*;
@@ -58,8 +59,11 @@ fn get_copypasta(name: Copypastas) -> String {
pub async fn copypasta(
ctx: Context<'_>,
#[description = "the copypasta you want to send"] copypasta: Copypastas,
-) -> Result<(), Error> {
- let gid = ctx.guild_id().unwrap_or_default();
+) -> Result<()> {
+ let gid = ctx
+ .guild_id()
+ .ok_or_else(|| eyre!("couldnt get guild from message!"))?;
+
if !utils::is_guild_allowed(gid) {
info!("not running copypasta command in {gid}");
return Ok(());
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index b6130ab..5edf0b7 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -6,10 +6,12 @@ pub mod random;
pub mod teawiespam;
pub mod version;
-use crate::{Data, Error};
+use crate::Data;
+
+use color_eyre::eyre::Report;
use poise::Command;
-pub fn to_global_commands() -> Vec<Command<Data, Error>> {
+pub fn to_global_commands() -> Vec<Command<Data, Report>> {
vec![
ask::ask(),
bing::bing(),
@@ -21,6 +23,6 @@ pub fn to_global_commands() -> Vec<Command<Data, Error>> {
]
}
-pub fn to_guild_commands() -> Vec<Command<Data, Error>> {
+pub fn to_guild_commands() -> Vec<Command<Data, Report>> {
vec![copypasta::copypasta(), teawiespam::teawiespam()]
}
diff --git a/src/commands/random.rs b/src/commands/random.rs
index bc34928..9595d09 100644
--- a/src/commands/random.rs
+++ b/src/commands/random.rs
@@ -1,44 +1,30 @@
-use crate::{api, consts, utils, Context, Error};
+use crate::{api, consts, utils, Context};
+
+use color_eyre::eyre::Result;
#[poise::command(slash_command, subcommands("lore", "teawie", "shiggy"))]
-pub async fn random(_ctx: Context<'_>) -> Result<(), Error> {
+pub async fn random(_ctx: Context<'_>) -> Result<()> {
Ok(())
}
/// get a random piece of teawie lore!
#[poise::command(prefix_command, slash_command)]
-pub async fn lore(ctx: Context<'_>) -> Result<(), Error> {
- match utils::random_choice(consts::LORE) {
- Ok(resp) => {
- ctx.say(resp).await?;
- Ok(())
- }
- Err(why) => {
- ctx.say("i can't think of any right now :(").await?;
- Err(why)
- }
- }
+pub async fn lore(ctx: Context<'_>) -> Result<()> {
+ let resp = utils::random_choice(consts::LORE)?;
+ ctx.say(resp).await?;
+ Ok(())
}
/// get a random teawie
#[poise::command(prefix_command, slash_command)]
-pub async fn teawie(ctx: Context<'_>) -> Result<(), Error> {
- if let Ok(url) = api::guzzle::get_random_teawie().await {
- utils::send_url_as_embed(ctx, url).await
- } else {
- ctx.say("i'm too lazy to send a selfie right now :(")
- .await?;
- Ok(())
- }
+pub async fn teawie(ctx: Context<'_>) -> Result<()> {
+ let url = api::guzzle::get_random_teawie().await?;
+ utils::send_url_as_embed(ctx, url).await
}
/// get a random shiggy
#[poise::command(prefix_command, slash_command)]
-pub async fn shiggy(ctx: Context<'_>) -> Result<(), Error> {
- if let Ok(url) = api::shiggy::get_random_shiggy().await {
- utils::send_url_as_embed(ctx, url).await
- } else {
- ctx.say("i couldn't get a shiggy right now :(").await?;
- Ok(())
- }
+pub async fn shiggy(ctx: Context<'_>) -> Result<()> {
+ let url = api::shiggy::get_random_shiggy().await?;
+ utils::send_url_as_embed(ctx, url).await
}
diff --git a/src/commands/teawiespam.rs b/src/commands/teawiespam.rs
index 4964e90..da01af9 100644
--- a/src/commands/teawiespam.rs
+++ b/src/commands/teawiespam.rs
@@ -1,13 +1,15 @@
use crate::utils;
-use crate::{Context, Error};
+use crate::Context;
+
+use color_eyre::eyre::Result;
use log::*;
/// teawie will spam you.
#[poise::command(slash_command, prefix_command)]
-pub async fn teawiespam(ctx: Context<'_>) -> Result<(), Error> {
+pub async fn teawiespam(ctx: Context<'_>) -> Result<()> {
let gid = ctx.guild_id().unwrap_or_default();
if !utils::is_guild_allowed(gid) {
- info!("not running copypasta command in {gid}");
+ info!("not running teawiespam command in {gid}");
return Ok(());
}
diff --git a/src/commands/version.rs b/src/commands/version.rs
index e87ab4d..c5e97f9 100644
--- a/src/commands/version.rs
+++ b/src/commands/version.rs
@@ -1,9 +1,11 @@
use crate::colors::Colors;
-use crate::{Context, Error};
+use crate::Context;
+
+use color_eyre::eyre::Result;
/// get version info
#[poise::command(slash_command)]
-pub async fn version(ctx: Context<'_>) -> Result<(), Error> {
+pub async fn version(ctx: Context<'_>) -> Result<()> {
let sha = option_env!("GIT_SHA").unwrap_or("main");
let revision_url = format!(
diff --git a/src/handlers/error.rs b/src/handlers/error.rs
new file mode 100644
index 0000000..b4e1361
--- /dev/null
+++ b/src/handlers/error.rs
@@ -0,0 +1,42 @@
+use crate::colors::Colors;
+use crate::Data;
+
+use color_eyre::eyre::Report;
+use log::*;
+use poise::serenity_prelude::Timestamp;
+use poise::FrameworkError;
+
+pub async fn handle(error: poise::FrameworkError<'_, Data, Report>) {
+ match error {
+ FrameworkError::Setup { error, .. } => error!("error setting up client! {error:#?}"),
+
+ FrameworkError::Command { error, ctx } => {
+ error!("error in command {}:\n{error:?}", ctx.command().name);
+ ctx.send(|c| {
+ c.embed(|e| {
+ e.title("Something went wrong!")
+ .description("oopsie")
+ .timestamp(Timestamp::now())
+ .color(Colors::Orange)
+ })
+ })
+ .await
+ .ok();
+ }
+
+ FrameworkError::EventHandler {
+ error,
+ ctx: _,
+ event: _,
+ framework: _,
+ } => {
+ error!("error while handling event:\n{error:#?}");
+ }
+
+ error => {
+ if let Err(e) = poise::builtins::on_error(error).await {
+ error!("error while handling an error: {}", e);
+ }
+ }
+ }
+}
diff --git a/src/handler/message.rs b/src/handlers/event/message.rs
index 37a49bf..a84ec59 100644
--- a/src/handler/message.rs
+++ b/src/handlers/event/message.rs
@@ -1,9 +1,23 @@
-use crate::{consts, utils, Data, Error};
+use crate::{consts, utils, Data};
+
+use color_eyre::eyre::{Report, Result};
use log::*;
-use poise::serenity_prelude as serenity;
-use poise::{Event, FrameworkContext};
+use poise::serenity_prelude::{Context, Message};
+use poise::FrameworkContext;
+
+pub async fn handle(
+ ctx: &Context,
+ framework: FrameworkContext<'_, Data, Report>,
+ msg: &Message,
+) -> Result<()> {
+ if should_echo(framework, msg) {
+ msg.reply(ctx, &msg.content).await?;
+ }
+
+ Ok(())
+}
-fn should_echo(framework: FrameworkContext<'_, Data, Error>, msg: &serenity::Message) -> bool {
+fn should_echo(framework: FrameworkContext<'_, Data, Report>, msg: &Message) -> bool {
let gid = msg.guild_id.unwrap_or_default();
if msg.author.id == framework.bot_id || !utils::is_guild_allowed(gid) {
info!("not running copypasta command in {gid}");
@@ -19,17 +33,3 @@ fn should_echo(framework: FrameworkContext<'_, Data, Error>, msg: &serenity::Mes
.to_ascii_lowercase()
.contains("twitter's recommendation algorithm")
}
-
-pub async fn handle(
- ctx: &serenity::Context,
- _event: &Event<'_>,
- framework: FrameworkContext<'_, Data, Error>,
- _data: &Data,
- msg: &serenity::Message,
-) -> Result<(), Error> {
- if should_echo(framework, msg) {
- msg.reply(ctx, &msg.content).await?;
- }
-
- Ok(())
-}
diff --git a/src/handler/mod.rs b/src/handlers/event/mod.rs
index 3489b4a..09be62b 100644
--- a/src/handler/mod.rs
+++ b/src/handlers/event/mod.rs
@@ -1,25 +1,25 @@
-use crate::{Data, Error};
+use crate::Data;
+
+use color_eyre::eyre::{Report, Result};
use poise::serenity_prelude as serenity;
-use poise::Event;
+use poise::{Event, FrameworkContext};
mod message;
-pub mod pinboard;
+mod pinboard;
mod reactboard;
pub async fn handle(
ctx: &serenity::Context,
event: &Event<'_>,
- framework: poise::FrameworkContext<'_, Data, Error>,
+ framework: FrameworkContext<'_, Data, Report>,
data: &Data,
-) -> Result<(), Error> {
+) -> Result<()> {
match event {
Event::Ready { data_about_bot } => {
log::info!("logged in as {}", data_about_bot.user.name)
}
- Event::Message { new_message } => {
- message::handle(ctx, event, framework, data, new_message).await?
- }
+ Event::Message { new_message } => message::handle(ctx, framework, new_message).await?,
Event::ChannelPinsUpdate { pin } => {
if let Some(settings) = &data.settings {
diff --git a/src/handler/pinboard.rs b/src/handlers/event/pinboard.rs
index 0c87a5b..0c87a5b 100644
--- a/src/handler/pinboard.rs
+++ b/src/handlers/event/pinboard.rs
diff --git a/src/handler/reactboard.rs b/src/handlers/event/reactboard.rs
index 36f8361..3972931 100644
--- a/src/handler/reactboard.rs
+++ b/src/handlers/event/reactboard.rs
@@ -1,31 +1,29 @@
-use crate::Error;
use crate::{settings::Settings, utils};
+
+use color_eyre::eyre::{eyre, Context as _, Result};
use log::*;
use poise::serenity_prelude::{Context, Message, MessageReaction, Reaction};
-pub async fn handle(ctx: &Context, reaction: &Reaction, settings: &Settings) -> Result<(), Error> {
- let msg = match reaction.message(&ctx.http).await {
- Ok(msg) => msg,
- Err(why) => {
- warn!("couldn't get message of reaction! {}", why);
- return Err(Box::new(why));
- }
- };
+pub async fn handle(ctx: &Context, reaction: &Reaction, settings: &Settings) -> Result<()> {
+ let msg = reaction
+ .message(&ctx.http)
+ .await
+ .wrap_err("couldn't get reaction from message!")?;
- if let Some(matched) = msg
+ let matched = msg
.clone()
.reactions
.into_iter()
.find(|r| r.reaction_type == reaction.emoji)
- {
- send_to_reactboard(ctx, &matched, &msg, settings).await?;
- } else {
- warn!(
- "couldn't find any matching reactions for {} in {}",
- reaction.emoji.as_data(),
- msg.id
- )
- }
+ .ok_or_else(|| {
+ eyre!(
+ "couldn't find any matching reactions for {} in message {}!",
+ reaction.emoji.as_data(),
+ msg.id
+ )
+ })?;
+
+ send_to_reactboard(ctx, &matched, &msg, settings).await?;
Ok(())
}
@@ -35,7 +33,7 @@ async fn send_to_reactboard(
reaction: &MessageReaction,
msg: &Message,
settings: &Settings,
-) -> Result<(), Error> {
+) -> Result<()> {
if !settings.can_use_reaction(reaction) {
info!("reaction {} can't be used!", reaction.reaction_type);
return Ok(());
diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs
new file mode 100644
index 0000000..2ae0539
--- /dev/null
+++ b/src/handlers/mod.rs
@@ -0,0 +1,5 @@
+mod error;
+mod event;
+
+pub use error::handle as handle_error;
+pub use event::handle as handle_event;
diff --git a/src/main.rs b/src/main.rs
index a93e102..98ebe5f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,19 +1,21 @@
-use std::{error, time};
+use std::time::Duration;
+use color_eyre::eyre::{eyre, Context as _, Report, Result};
use log::*;
-use poise::serenity_prelude as serentiy;
+use poise::{
+ serenity_prelude as serenity, EditTracker, Framework, FrameworkOptions, PrefixFrameworkOptions,
+};
use settings::Settings;
mod api;
mod colors;
mod commands;
mod consts;
-mod handler;
+mod handlers;
mod settings;
mod utils;
-type Error = Box<dyn error::Error + Send + Sync>;
-type Context<'a> = poise::Context<'a, Data, Error>;
+type Context<'a> = poise::Context<'a, Data, Report>;
#[derive(Clone)]
pub struct Data {
@@ -34,54 +36,43 @@ impl Default for Data {
}
}
-async fn on_error(error: poise::FrameworkError<'_, Data, Error>) {
- match error {
- poise::FrameworkError::Setup { error, .. } => panic!("failed to start bot: {error:?}"),
- poise::FrameworkError::Command { error, ctx } => {
- error!("error in command {}: {:?}", ctx.command().name, error);
- }
- error => {
- if let Err(e) = poise::builtins::on_error(error).await {
- error!("error while handling an error: {}", e);
- }
- }
- }
-}
-
#[tokio::main]
-async fn main() {
+async fn main() -> Result<()> {
+ color_eyre::install()?;
env_logger::init();
dotenvy::dotenv().ok();
- let options = poise::FrameworkOptions {
+ let token =
+ std::env::var("TOKEN").wrap_err_with(|| eyre!("Couldn't find token in environment!"))?;
+
+ let intents =
+ serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::MESSAGE_CONTENT;
+
+ let options = FrameworkOptions {
commands: commands::to_global_commands(),
+ on_error: |error| Box::pin(handlers::handle_error(error)),
+ command_check: Some(|ctx| {
+ Box::pin(async move { Ok(ctx.author().id != ctx.framework().bot_id) })
+ }),
event_handler: |ctx, event, framework, data| {
- Box::pin(handler::handle(ctx, event, framework, data))
+ Box::pin(handlers::handle_event(ctx, event, framework, data))
},
- prefix_options: poise::PrefixFrameworkOptions {
+ prefix_options: PrefixFrameworkOptions {
prefix: Some("!".into()),
- edit_tracker: Some(poise::EditTracker::for_timespan(time::Duration::from_secs(
- 3600,
- ))),
+ edit_tracker: Some(EditTracker::for_timespan(Duration::from_secs(3600))),
..Default::default()
},
- on_error: |error| Box::pin(on_error(error)),
- command_check: Some(|ctx| {
- Box::pin(async move { Ok(ctx.author().id != ctx.framework().bot_id) })
- }),
..Default::default()
};
- let framework = poise::Framework::builder()
+ let framework = Framework::builder()
+ .token(token)
+ .intents(intents)
.options(options)
- .token(std::env::var("TOKEN").expect("couldn't find token in environment."))
- .intents(
- serentiy::GatewayIntents::non_privileged() | serentiy::GatewayIntents::MESSAGE_CONTENT,
- )
.setup(|ctx, _ready, framework| {
Box::pin(async move {
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
- info!("registered global commands!");
+ info!("Registered global commands!");
poise::builtins::register_in_guild(
ctx,
@@ -89,11 +80,17 @@ async fn main() {
consts::TEAWIE_GUILD,
)
.await?;
- info!("registered guild commands!");
+ info!("Registered guild commands to {}", consts::TEAWIE_GUILD);
Ok(Data::new())
})
});
- framework.run().await.unwrap()
+ tokio::select! {
+ result = framework.run() => { result.map_err(Report::from) },
+ _ = tokio::signal::ctrl_c() => {
+ info!("Interrupted! Exiting...");
+ std::process::exit(130);
+ }
+ }
}
diff --git a/src/utils.rs b/src/utils.rs
index af079ff..9a1d09c 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,6 +1,6 @@
-use crate::{colors, consts, Context, Error};
+use crate::{colors, consts, Context};
-use log::*;
+use color_eyre::eyre::{eyre, Result};
use once_cell::sync::Lazy;
use poise::serenity_prelude as serenity;
use rand::seq::SliceRandom;
@@ -21,13 +21,13 @@ pub fn parse_snowflakes_from_env<T, F: Fn(u64) -> T>(key: &str, f: F) -> Option<
/*
* chooses a random element from an array
*/
-pub fn random_choice<const N: usize>(arr: [&str; N]) -> Result<String, Error> {
+pub fn random_choice<const N: usize>(arr: [&str; N]) -> Result<String> {
let mut rng = rand::thread_rng();
- if let Some(resp) = arr.choose(&mut rng) {
- Ok((*resp).to_string())
- } else {
- Err(Into::into("couldn't choose from arr!"))
- }
+ let resp = arr
+ .choose(&mut rng)
+ .ok_or_else(|| eyre!("couldn't choose from array!"))?;
+
+ Ok((*resp).to_string())
}
// waiting for `round_char_boundary` to stabilize
@@ -54,31 +54,25 @@ pub fn is_guild_allowed(gid: GuildId) -> bool {
ALLOWED_GUILDS.contains(&gid)
}
-pub async fn send_url_as_embed(ctx: Context<'_>, url: String) -> Result<(), Error> {
- match Url::parse(&url) {
- Ok(parsed) => {
- let title = parsed
- .path_segments()
- .unwrap()
- .last()
- .unwrap_or("image")
- .replace("%20", " ");
-
- ctx.send(|c| {
- c.embed(|e| {
- e.title(title)
- .image(&url)
- .url(url)
- .color(colors::Colors::Blue)
- })
- })
- .await?;
- }
- Err(why) => {
- error!("failed to parse url {}! {}", url, why);
- ctx.say("i can't get that for you right now :(").await?;
- }
- }
+pub async fn send_url_as_embed(ctx: Context<'_>, url: String) -> Result<()> {
+ let parsed = Url::parse(&url)?;
+
+ let title = parsed
+ .path_segments()
+ .unwrap()
+ .last()
+ .unwrap_or("image")
+ .replace("%20", " ");
+
+ ctx.send(|c| {
+ c.embed(|e| {
+ e.title(title)
+ .image(&url)
+ .url(url)
+ .color(colors::Colors::Blue)
+ })
+ })
+ .await?;
Ok(())
}