Skip to content

Commit

Permalink
Replace env_logger with tracing and cleanup a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
rushsteve1 committed Dec 28, 2023
1 parent b5cd474 commit 2464d33
Show file tree
Hide file tree
Showing 15 changed files with 348 additions and 412 deletions.
299 changes: 154 additions & 145 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ edition = "2021"

[dependencies]
askama_gotham = "^0.14"
chrono = "^0.4"
chrono = "*"
chrono-tz = "*"
clokwerk = "^0.4"
env_logger = "^0.10"
gotham = "^0.7"
gotham_derive = "^0.7"
lazy_static = "^1.4"
log = "^0.4"
lazy_static = "*"
once_cell = "*"
regex = "*"
scraper = "^0.18"
serde = "*"
serde_derive = "*"
tracing = "*"
tracing-subscriber = "^0.3"

[dependencies.askama]
version = "^0.12.1"
version = "^0.12"
default_features = false
features = [ "with-gotham" ]

Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,12 @@

A Discord bot that does a whole bunch of things that no one needs. Like a Swiss Army Knife.

# TODO List

-[ ] Better error handling (eventually disable unwrap/expect)
-[ ] General cleanups
-[x] SQLx compile-time validations
-[ ] Replace Gotham with Axum
-[x] Replace env_logger with tracing
-[ ] Documentation
-[ ] Tests
2 changes: 2 additions & 0 deletions src/commands/bigmoji.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use chrono::NaiveDateTime;
use serenity::all::Interaction;
use tracing::instrument;

use super::get_cmd;
use crate::DB_POOL;
Expand All @@ -11,6 +12,7 @@ pub struct BigMoji {
pub inserted_at: NaiveDateTime,
}

#[instrument]
pub async fn add(interaction: &Interaction) -> String {
let cmd = get_cmd(interaction);

Expand Down
5 changes: 4 additions & 1 deletion src/commands/definition.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use log::warn;
use serenity::all::{
Command, CommandOptionType, Context, CreateCommand, CreateCommandOption, GuildId,
};
use tracing::{instrument, warn};

#[instrument]
pub async fn _clear_definitions(ctx: &Context) {
warn!("Clearing slash command definitions");
let commands = Command::get_global_commands(&ctx.http).await.unwrap();
Expand All @@ -14,6 +15,7 @@ pub async fn _clear_definitions(ctx: &Context) {
}
}

#[instrument]
pub async fn _clear_definitions_for_guild(ctx: &Context, guild_id: GuildId) {
warn!("Clearing slash command definitions for guild {}", guild_id);
let commands = ctx.http.get_guild_commands(guild_id).await.unwrap();
Expand All @@ -28,6 +30,7 @@ pub async fn _clear_definitions_for_guild(ctx: &Context, guild_id: GuildId) {

/// Builds the definition of the slash command "interactions" and sends it to
/// Discord where it can will be displayed
#[instrument]
pub async fn interactions_definition(ctx: Context) -> Vec<Command> {
let quote_cmd = CreateCommand::new("quote")
.description("Manage peoples' quotes")
Expand Down
2 changes: 2 additions & 0 deletions src/commands/drunk.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use chrono::NaiveDateTime;
use serenity::all::{Interaction, Mentionable};
use tracing::instrument;

use super::get_cmd;
use crate::DB_POOL;
Expand All @@ -17,6 +18,7 @@ pub struct Drunk {
pub updated_at: NaiveDateTime,
}

#[instrument]
pub async fn update(interaction: &Interaction) -> String {
let cmd = get_cmd(interaction);

Expand Down
6 changes: 5 additions & 1 deletion src/commands/handler.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use log::{error, warn, info};
use serenity::all::{
CommandDataOption, Context, CreateInteractionResponse, CreateInteractionResponseMessage,
EventHandler, Interaction, Message, Reaction, ReactionType, Ready,
};
use serenity::async_trait;
use tracing::{error, info, instrument, warn};

use super::definition::interactions_definition;
use crate::commands::bigmoji::BigMoji;
Expand All @@ -12,17 +12,20 @@ use crate::{DB_POOL, DOMAIN, PREFIX};
const DOWN: &str = "⬇️";
const DOWNVOTE_LIMIT: u8 = 5;

#[derive(Debug)]
pub struct Handler;

#[async_trait]
impl EventHandler for Handler {
#[instrument]
async fn ready(&self, ctx: Context, _ready: Ready) {
info!("SwissArmyBot is ready!");

// Upserts the existing commands
let _commands = interactions_definition(ctx).await;
}

#[instrument]
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
if let Interaction::Command(ref inter) = interaction {
let content = match inter.data.name.as_str() {
Expand All @@ -48,6 +51,7 @@ impl EventHandler for Handler {
}
}

#[instrument]
async fn message(&self, ctx: Context, message: Message) {
// Don't bother with bot messages (including our own)
if message.author.bot {
Expand Down
4 changes: 4 additions & 0 deletions src/commands/quotes.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use chrono::NaiveDateTime;
use serenity::model::application::Interaction;
use serenity::model::prelude::*;
use tracing::instrument;

use super::get_cmd;
use crate::{DB_POOL, DOMAIN, HTTP, PREFIX};
Expand All @@ -16,6 +17,7 @@ pub struct Quote {
pub inserted_at: NaiveDateTime,
}

#[instrument]
pub async fn add(interaction: &Interaction) -> String {
let cmd = get_cmd(interaction);

Expand Down Expand Up @@ -62,6 +64,7 @@ pub async fn remove(interaction: &Interaction) -> String {
}
}

#[instrument]
pub async fn get(interaction: &Interaction) -> String {
let cmd = get_cmd(interaction);
let id = cmd.value.as_i64().unwrap();
Expand All @@ -84,6 +87,7 @@ pub async fn get(interaction: &Interaction) -> String {
}
}

#[instrument]
pub async fn list(interaction: &Interaction) -> String {
let cmd = get_cmd(interaction);

Expand Down
119 changes: 119 additions & 0 deletions src/helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use scraper::{Html, Selector};
use serenity::{
all::{ChannelId, Mentionable, Message},
builder::CreateMessage,
};
use tracing::instrument;

use crate::{
commands::{BigMoji, Drunk, Quote},
DB_POOL, HTTP,
};

const GOOD_STONKS: &str = "📈";
const BAD_STONKS: &str = "📉";
const STONKS_URL: &str = "https://finance.yahoo.com";
const STONKS_SEL: &str = "#marketsummary-itm-2 > h3:nth-child(1) > div:nth-child(4) > fin-streamer:nth-child(1) > span:nth-child(1)";

#[instrument]
pub async fn get_bigmoji() -> Vec<BigMoji> {
sqlx::query_as!(BigMoji, "SELECT * FROM bigmoji;")
.fetch_all(&*DB_POOL)
.await
.expect("Error getting bigmoji")
}

#[instrument]
pub async fn get_quotes(
from_date: String,
to_date: String,
user_id: i64,
) -> (Vec<Quote>, i64, String, String) {
let quotes = if user_id > 0 {
sqlx::query_as!(
Quote,
"SELECT * FROM quotes WHERE user_id = ? AND inserted_at BETWEEN ? AND ?;",
user_id,
from_date,
to_date
)
.fetch_all(&*DB_POOL)
.await
.expect("Error getting quotes")
} else {
sqlx::query_as!(
Quote,
"SELECT * FROM quotes WHERE inserted_at BETWEEN ? AND ?;",
from_date,
to_date
)
.fetch_all(&*DB_POOL)
.await
.expect("Error getting quotes")
};

(quotes, user_id, from_date, to_date)
}

#[instrument]
pub async fn get_drunks() -> Vec<Drunk> {
sqlx::query_as!(Drunk, "SELECT * FROM drunk;")
.fetch_all(&*DB_POOL)
.await
.expect("Error getting drunks")
}

#[instrument]
pub async fn get_random_quote() -> Quote {
sqlx::query_as!(Quote, "SELECT * FROM quotes ORDER BY RANDOM() LIMIT 1;")
.fetch_one(&*DB_POOL)
.await
.expect("Error getting quote")
}

#[instrument]
pub async fn post_random_to_channel(
chan: ChannelId,
body: String,
) -> Result<Message, serenity::Error> {
let quote = get_random_quote().await;
let user_id = serenity::model::id::UserId::new(quote.user_id as u64);
let author_id = serenity::model::id::UserId::new(quote.author_id as u64);

let txt = format!(
"{}\n#{} by {}, added by {} on <t:{}:f>\n\n>>> {}",
body,
quote.id,
user_id.mention(),
author_id.mention(),
quote.inserted_at.timestamp(),
quote.text
);

chan.send_message(HTTP.get().unwrap(), CreateMessage::new().content(txt))
.await
}

#[instrument]
pub async fn post_stonks_to_channel(chan: ChannelId) -> Result<Message, serenity::Error> {
let txt = {
let body = reqwest::get(STONKS_URL)
.await
.expect("Failed to get Yahoo Finance")
.text()
.await
.expect("Could not get Stonks body");
let document = Html::parse_document(&body);
let selector = Selector::parse(STONKS_SEL).expect("Failed to parse selector");
let el = document.select(&selector).next().unwrap();
let c = el.text().next().unwrap().chars().next().unwrap();
match c {
'+' => GOOD_STONKS,
'-' => BAD_STONKS,
_ => unreachable!(),
}
};

chan.send_message(HTTP.get().unwrap(), CreateMessage::new().content(txt))
.await
}
11 changes: 7 additions & 4 deletions src/jobs.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
use crate::web::{post_random_to_channel, post_stonks_to_channel};
use clokwerk::Interval;
use clokwerk::{AsyncScheduler, Job, TimeUnits};
use log::warn;
use tracing::{instrument, trace_span, warn};

use crate::helpers::{post_random_to_channel, post_stonks_to_channel};
use crate::{QOTD_CHANNELS, STONKS_CHANNELS};

#[instrument]
pub fn setup_jobs() -> AsyncScheduler<chrono_tz::Tz> {
let mut scheduler = AsyncScheduler::with_tz(chrono_tz::America::New_York);

// Quote of the Day schedule
scheduler.every(1.day()).at("5:00 am").run(|| async {
warn!("Running QOTD Job");
let span = trace_span!("QOTD job");
let _enter = span.enter();
for chan in QOTD_CHANNELS.iter() {
post_random_to_channel(*chan, "**Quote of the Day**".to_string())
.await
Expand All @@ -23,7 +25,8 @@ pub fn setup_jobs() -> AsyncScheduler<chrono_tz::Tz> {
.every(Interval::Weekday)
.at("5:00 pm")
.run(|| async {
warn!("Running Stonks Job");
let span = trace_span!("Stonks job");
let _enter = span.enter();
for chan in STONKS_CHANNELS.iter() {
post_stonks_to_channel(*chan)
.await
Expand Down
37 changes: 19 additions & 18 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use std::env;
use std::sync::Arc;

use log::{debug, error, info, warn};
use once_cell::sync::OnceCell;
use serenity::all::ApplicationId;
use serenity::http::Http;
use serenity::model::id::ChannelId;
use serenity::prelude::*;
use sqlx::migrate::MigrateDatabase;
use tracing::{debug, error, info, instrument};

mod commands;
mod helpers;
mod jobs;
mod web;

Expand All @@ -20,6 +21,7 @@ use jobs::setup_jobs;
pub const VERSION: &str = std::env!("CARGO_PKG_VERSION");
pub const GIT_VERSION: Option<&'static str> = std::option_env!("GIT_VERSION");

// TODO clean up this disaster
lazy_static::lazy_static! {
// Get configuration from environment variables
// These make working with SAB in a docker container much easier
Expand Down Expand Up @@ -66,8 +68,12 @@ lazy_static::lazy_static! {
pub static HTTP: OnceCell<Arc<Http>> = OnceCell::new();

#[tokio::main]
#[instrument]
async fn main() {
env_logger::init();
// Setup tracing
let subscriber = tracing_subscriber::FmtSubscriber::new();
tracing::subscriber::set_global_default(subscriber).expect("Subscriber failed to set");

info!("Starting up SwissArmyBot {}...", VERSION);

// Check the database path properly, creating the database if needed
Expand Down Expand Up @@ -123,22 +129,17 @@ async fn main() {
// We're running both Serenity and Gotham in Tokio workers, and neither of
// them should ever exit, so we wait for them and print an error if they do.
debug!("Starting event loop...");
loop {
tokio::select!(
e = client_fut => {
error!("Serenity exited with {:?}", e.unwrap_err());
break;
}
e = gotham_fut => {
error!("Gotham exited with {:?}", e.unwrap_err());
break;
}
e = job_fut => {
error!("Clokwerk exited with {:?}", e.unwrap_err());
break;
}
)
}
tokio::select!(
e = client_fut => {
error!("Serenity exited with {:?}", e.unwrap_err());
}
e = gotham_fut => {
error!("Gotham exited with {:?}", e.unwrap_err());
}
e = job_fut => {
error!("Clokwerk exited with {:?}", e.unwrap_err());
}
);

// If it gets to this point then it has exited abnormally
error!("SwissArmyBot has exited, whoops!");
Expand Down
Empty file added src/models.rs
Empty file.
Loading

0 comments on commit 2464d33

Please sign in to comment.