diff --git a/src/database/mod.rs b/src/database/mod.rs index 66bdc4c..746e8f9 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -3,14 +3,13 @@ pub mod schema; use diesel::prelude::*; use diesel::result::Error; -use diesel::update; use models::{Event, NewEvent}; use std::vec::Vec; /// Establish a connection to the database pub fn establish_connection(database_url: String) -> MysqlConnection { MysqlConnection::establish(&database_url) - .expect(&format!("Error connecting to {}", database_url)) + .unwrap_or_else(|_| panic!("Error connecting to {}", database_url)) } /// Insert an event into the database pub fn insert_event(database_url: String, new_event: &NewEvent) -> Result { @@ -65,14 +64,3 @@ pub fn get_all_events(database_url: String) -> Result, Error> { events.order(event_time).load(&connection) } - -/// Set the reminder state of an event -pub fn set_reminder(database_url: String, event_id: i32, state: i32) -> Result { - use schema::events::dsl::{events, id, reminder_sent}; - let connection = establish_connection(database_url); - - let target = events.filter(id.eq(event_id)); - update(target) - .set(reminder_sent.eq(state)) - .execute(&connection) -} diff --git a/src/database/models.rs b/src/database/models.rs index 04814fb..6230913 100644 --- a/src/database/models.rs +++ b/src/database/models.rs @@ -30,7 +30,7 @@ impl Into for Event { event_desc: self.event_desc.clone(), organizer: self.organizer.clone(), event_loc: self.event_loc.clone(), - event_time: self.event_time.clone(), + event_time: self.event_time, message_id: self.message_id.clone(), thumbnail_link: self.message_id.clone(), reminder_sent: self.reminder_sent, diff --git a/src/discord/events.rs b/src/discord/events.rs index 41650f2..8e84b51 100644 --- a/src/discord/events.rs +++ b/src/discord/events.rs @@ -8,7 +8,7 @@ use chrono::offset::TimeZone; use chrono::{Datelike, NaiveDateTime, Timelike, Utc}; use chrono_tz::Tz; use serenity::framework::standard::{macros::command, Args, CommandResult}; -use serenity::model::prelude::{Mentionable, Message, User}; +use serenity::model::prelude::{Mentionable, Message}; use serenity::prelude::Context; use serenity::utils::{content_safe, ContentSafeOptions}; use url::Url; @@ -28,7 +28,8 @@ async fn confirm(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { // Check to to see if message author is the owner of the pending event if draft_event.creator_id == msg.author.id.0 { // Send event message - let event_msg = send_event_msg(&ctx, &config, config.event_channel, &new_event, true).await?; + let event_msg = + send_event_msg(&ctx, &config, config.event_channel, &new_event, true).await?; msg.reply(&ctx, "Event posted!").await?; @@ -38,7 +39,8 @@ async fn confirm(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { schedule_event(&ctx, &event).await; } else { - msg.reply(&ctx, format!("You do not have a pending event!")).await?; + msg.reply(&ctx, "You do not have a pending event!".to_string()) + .await?; } Ok(()) @@ -65,7 +67,8 @@ async fn create(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { let event_name = match args.find::() { Ok(event_name) => event_name.replace("\"", ""), Err(_) => { - msg.reply(&ctx, "No event name provided.".to_string()).await?; + msg.reply(&ctx, "No event name provided.".to_string()) + .await?; return Ok(()); } }; @@ -79,7 +82,8 @@ async fn create(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { let description = match args.find::() { Ok(desc) => desc.replace("\"", ""), Err(_) => { - msg.reply(&ctx, "No description provided.".to_string()).await?; + msg.reply(&ctx, "No description provided.".to_string()) + .await?; return Ok(()); } }; @@ -110,7 +114,8 @@ async fn create(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { msg.reply( &ctx, "Invalid date format. Format is HH:MMam YYYY-MM-DD".to_string(), - ).await?; + ) + .await?; return Ok(()); } }; @@ -130,7 +135,8 @@ async fn create(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { let event_time = input_date.with_timezone(&Utc).naive_utc(); if Utc::now().naive_utc() > event_time { - msg.reply(&ctx, "The scheduled time has already passed!").await?; + msg.reply(&ctx, "The scheduled time has already passed!") + .await?; return Ok(()); } @@ -155,7 +161,8 @@ async fn create(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { thumbnail_link, event_time, msg.author.id.0, - ).await?; + ) + .await?; send_draft_event(&ctx, msg.channel_id).await?; Ok(()) @@ -173,17 +180,19 @@ async fn cancel(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { let event = get_event_by_name(config.db_url.clone(), event_name)?; let message_id = event.message_id.parse::()?; - let message = ctx.http.get_message(config.event_channel, message_id).await?; - - let reaction_users = message - .reaction_users(&ctx.http, INTERESTED_EMOJI, None, None) - .await - .unwrap_or(Vec::::new()); - + let message = ctx + .http + .get_message(config.event_channel, message_id) + .await?; let cancel_msg = format!("**{}** has been canceled!", event.event_name.clone()); - for user in reaction_users { - send_dm_message(ctx, user, &cancel_msg).await; + if let Ok(reaction_users) = message + .reaction_users(&ctx.http, INTERESTED_EMOJI, None, None) + .await + { + for user in reaction_users { + send_dm_message(ctx, user, &cancel_msg).await; + } } remove_event(config.db_url.clone(), event.id)?; diff --git a/src/discord/mod.rs b/src/discord/mod.rs index 6c53cdb..1adb3ac 100644 --- a/src/discord/mod.rs +++ b/src/discord/mod.rs @@ -3,18 +3,17 @@ use crate::database::{get_event_by_msg_id, remove_event}; use crate::hypebot_config::HypeBotConfig; use crate::{INTERESTED_EMOJI, UNINTERESTED_EMOJI}; use chrono::{DateTime, NaiveDateTime, Utc}; +use serenity::framework::standard::macros::hook; use serenity::framework::standard::{CommandError, CommandResult}; use serenity::http::Http; use serenity::model::prelude::{ChannelId, Message, Reaction, User}; -use serenity::prelude::{TypeMapKey, TypeMap}; use serenity::prelude::{Context, RwLock}; +use serenity::prelude::{TypeMap, TypeMapKey}; use serenity::utils::Colour; use serenity::Result; -use serenity::framework::standard::macros::hook; use std::collections::HashMap; use std::sync::Arc; use strfmt::strfmt; -use diesel::result::Error; pub mod events; @@ -45,7 +44,7 @@ pub async fn send_message_to_reaction_users(ctx: &Context, reaction: &Reaction, } }; - let event_utc_time = DateTime::::from_utc(event.event_time.clone(), Utc); + let event_utc_time = DateTime::::from_utc(event.event_time, Utc); let current_utc_time = chrono::offset::Utc::now(); let msg; @@ -66,14 +65,17 @@ pub async fn send_message_to_reaction_users(ctx: &Context, reaction: &Reaction, } /// Send a DM message to a user -pub async fn send_dm_message(ctx: &Context, user: User, message: &String) { +pub async fn send_dm_message(ctx: &Context, user: User, message: &str) { if let Ok(dm_channel) = user.create_dm_channel(ctx).await { - dm_channel.send_message(ctx, |m| m.content(message)).await.ok(); + dm_channel + .send_message(ctx, |m| m.content(message)) + .await + .ok(); } } /// Create a countdown link for the event -pub fn get_countdown_link(event_name: &String, utc: &DateTime) -> String { +pub fn get_countdown_link(event_name: &str, utc: &DateTime) -> String { let msg = event_name.replace(" ", "+"); let time = utc.format("%G%m%dT%H%M").to_string(); @@ -93,7 +95,7 @@ pub async fn send_event_msg( ) -> Result { let channel = ctx.http.get_channel(channel_id).await?; - let utc_time = DateTime::::from_utc(event.event_time.clone(), Utc); + let utc_time = DateTime::::from_utc(event.event_time, Utc); let native_time = utc_time.with_timezone(&config.event_timezone); @@ -135,6 +137,7 @@ pub async fn send_event_msg( } /// Updates the draft event stored in the context data +#[allow(clippy::too_many_arguments)] pub async fn update_draft_event( ctx: &Context, event_name: String, @@ -148,7 +151,7 @@ pub async fn update_draft_event( let mut data = ctx.data.write().await; let mut draft_event = data .get_mut::() - .ok_or(CommandError::from("Unable get draft event!".to_string()))?; + .ok_or_else(|| CommandError::from("Unable get draft event!".to_string()))?; draft_event.event.event_name = event_name; draft_event.event.event_desc = event_desc; @@ -166,16 +169,16 @@ pub async fn send_draft_event(ctx: &Context, channel: ChannelId) -> CommandResul let data = ctx.data.read().await; let config = data .get::() - .ok_or(CommandError::from("Config not found!".to_string()))?; + .ok_or_else(|| CommandError::from("Config not found!".to_string()))?; let draft_event = data .get::() - .ok_or(CommandError::from("Draft event not found!".to_string()))?; + .ok_or_else(|| CommandError::from("Draft event not found!".to_string()))?; - channel.send_message(&ctx, |m| { - m.content(format!( - "Draft message, use the `confirm` command to post it." - )) - }).await?; + channel + .send_message(&ctx, |m| { + m.content("Draft message, use the `confirm` command to post it.".to_string()) + }) + .await?; send_event_msg(ctx, config, channel.0, &draft_event.event, false).await?; Ok(()) } @@ -187,7 +190,7 @@ pub async fn get_config( let data_read = data.read().await; let config = data_read .get::() - .ok_or(CommandError::from("Unable to get config".to_string()))?; + .ok_or_else(|| CommandError::from("Unable to get config".to_string()))?; Ok(config.clone()) } @@ -199,7 +202,7 @@ pub async fn get_draft_event( let data_read = data.read().await; let draft_event = data_read .get::() - .ok_or(CommandError::from("Unable to queued event".to_string()))?; + .ok_or_else(|| CommandError::from("Unable to queued event".to_string()))?; Ok(draft_event.clone()) } @@ -244,7 +247,7 @@ pub async fn schedule_event(ctx: &Context, event: &Event) { let config = get_config(&ctx.data).await.expect("Unable to get config"); if let Some(reminders) = config.reminders { - let event_time: DateTime = DateTime::::from_utc(event.event_time.clone(), Utc); + let event_time: DateTime = DateTime::::from_utc(event.event_time, Utc); for reminder in reminders { let reminder_time = @@ -256,9 +259,8 @@ pub async fn schedule_event(ctx: &Context, event: &Event) { let event = event.clone(); tokio::task::spawn(async move { - send_reminders_task(&ctx, reminder_time, &event, &reminder_msg).await; - } - ); + send_reminders_task(&ctx, reminder_time, &event, &reminder_msg).await; + }); } } } @@ -269,38 +271,44 @@ pub async fn send_reminders_task( ctx: &Context, datetime: DateTime, event: &Event, - reminder_msg: &String, + reminder_msg: &str, ) { let duration = datetime - chrono::offset::Utc::now(); - tokio::time::sleep(tokio::time::Duration::from_millis(duration.num_milliseconds() as u64)).await; + tokio::time::sleep(tokio::time::Duration::from_millis( + duration.num_milliseconds() as u64, + )) + .await; let config = get_config(&ctx.data).await.expect("Unable to get config"); let event_channel_id = config.event_channel; if let Ok(message_id) = event.message_id.parse::() { // Get message id if let Ok(message) = ctx.http.get_message(event_channel_id, message_id).await { - let reaction_users = message + if let Ok(reaction_users) = message .reaction_users(ctx, INTERESTED_EMOJI, None, None) .await - .unwrap_or(Vec::::new()); + { + // Build reminder message + let msg: String = reminder_msg.replace("{EVENT_NAME}", event.event_name.as_str()); - // Build reminder message - let msg: String = reminder_msg.replace("{EVENT_NAME}", event.event_name.as_str()); - - // Send reminder to each reacted user - for user in reaction_users { - send_dm_message(ctx, user, &msg).await; + // Send reminder to each reacted user + for user in reaction_users { + send_dm_message(ctx, user, &msg).await; + } } } } } /// Delete event +#[allow(dead_code)] pub async fn delete_event(http: &Arc, data: &Arc>, event: &Event) { let config = get_config(&data).await.expect("Unable to get config"); remove_event(config.db_url.clone(), event.id).ok(); if let Ok(message_id) = event.message_id.parse::() { - http.delete_message(config.event_channel, message_id).await.ok(); + http.delete_message(config.event_channel, message_id) + .await + .ok(); } } diff --git a/src/hypebot_config.rs b/src/hypebot_config.rs index e50825b..9d067d3 100644 --- a/src/hypebot_config.rs +++ b/src/hypebot_config.rs @@ -48,9 +48,9 @@ where { let string = deserializer.deserialize_struct("Value", &["into_str"], ConfigValueVisitor)?; - let tz: Tz = string.parse().ok().ok_or(D::Error::custom( - "Unable to parse datetime, should be in format \"Country/City\"", - ))?; + let tz: Tz = string.parse().ok().ok_or_else(|| { + D::Error::custom("Unable to parse datetime, should be in format \"Country/City\"") + })?; Ok(tz) } diff --git a/src/main.rs b/src/main.rs index 13786b0..3d5fb9f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,35 +11,35 @@ use std::collections::HashSet; use std::path::Path; use std::process::exit; -use chrono::{DateTime, Utc}; +use chrono::Utc; use clap::{App, Arg}; +use log::LevelFilter; use log4rs::append::console::ConsoleAppender; -use log4rs::append::rolling_file::policy::compound::CompoundPolicy; use log4rs::append::rolling_file::policy::compound::roll::fixed_window::FixedWindowRoller; use log4rs::append::rolling_file::policy::compound::trigger::size::SizeTrigger; +use log4rs::append::rolling_file::policy::compound::CompoundPolicy; use log4rs::append::rolling_file::RollingFileAppender; use log4rs::config::{Appender, Config, Root}; use log4rs::encode::pattern::PatternEncoder; use log4rs::filter::threshold::ThresholdFilter; use log4rs::init_config; -use log::LevelFilter; -use serenity::client::Client; use serenity::async_trait; -use serenity::framework::standard::{Args, CommandGroup, help_commands, HelpOptions}; -use serenity::framework::standard::{CommandResult, StandardFramework}; +use serenity::client::Client; use serenity::framework::standard::macros::{group, help}; +use serenity::framework::standard::{help_commands, Args, CommandGroup, HelpOptions}; +use serenity::framework::standard::{CommandResult, StandardFramework}; use serenity::model::channel::{Message, Reaction}; use serenity::model::id::UserId; use serenity::model::prelude::Ready; use serenity::prelude::{Context, EventHandler}; -use database::*; use database::models::NewEvent; -use discord::{ - delete_event, DraftEvent, get_config, log_error, permission_check, - schedule_event, send_message_to_reaction_users, -}; +use database::*; use discord::events::{CANCEL_COMMAND, CONFIRM_COMMAND, CREATE_COMMAND}; +use discord::{ + get_config, log_error, permission_check, schedule_event, send_message_to_reaction_users, + DraftEvent, +}; use hypebot_config::HypeBotConfig; mod database; @@ -79,7 +79,8 @@ impl EventHandler for Handler { &ctx, &reaction, "Hello, you are now receiving reminders for **{event}**", - ).await; + ) + .await; } } @@ -99,7 +100,8 @@ impl EventHandler for Handler { &ctx, &reaction, "Hello, you are no longer receiving reminders for **{event}**", - ).await; + ) + .await; } } @@ -110,9 +112,6 @@ impl EventHandler for Handler { // Schedule current events let config = get_config(&ctx.data).await.expect("Unable to get config"); for event in get_all_events(config.db_url.clone()).unwrap() { - let event_time: DateTime = - DateTime::::from_utc(event.event_time.clone(), Utc); - if event.reminder_sent == 0 { schedule_event(&ctx, &event).await; } @@ -230,11 +229,12 @@ async fn main() -> HypeBotResult<()> { embedded_migrations::run(&connection)?; // New client - let mut client = - Client::builder(cfg.discord_key.clone()).event_handler(Handler).framework( + let mut client = Client::builder(cfg.discord_key.clone()) + .event_handler(Handler) + .framework( StandardFramework::new() .configure(|c| { - c.prefix(cfg.prefix.as_str().clone()) + c.prefix(cfg.prefix.as_str()) .allow_dm(false) .ignore_bots(true) .ignore_webhooks(true) @@ -243,7 +243,8 @@ async fn main() -> HypeBotResult<()> { .after(log_error) .group(&EVENTCOMMANDS_GROUP) .help(&BOT_HELP), - ).await?; + ) + .await?; // Copy config data to client data and setup scheduler { @@ -264,7 +265,6 @@ async fn main() -> HypeBotResult<()> { }); } - // Start bot info!("Starting HypeBot!"); if let Err(why) = client.start().await {