Added better error handling and help messages
parent
7799f0e2fe
commit
4a182090ff
|
@ -1,7 +1,7 @@
|
||||||
use super::schema::events;
|
use super::schema::events;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
|
|
||||||
#[derive(Queryable)]
|
#[derive(Queryable, Clone)]
|
||||||
pub struct Event {
|
pub struct Event {
|
||||||
/// Event ID
|
/// Event ID
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
|
@ -19,6 +19,19 @@ pub struct Event {
|
||||||
pub reminder_sent: i32,
|
pub reminder_sent: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Into<NewEvent> for Event {
|
||||||
|
fn into(self) -> NewEvent {
|
||||||
|
NewEvent {
|
||||||
|
event_name: self.event_name.clone(),
|
||||||
|
event_desc: self.event_desc.clone(),
|
||||||
|
event_time: self.event_time.clone(),
|
||||||
|
message_id: self.message_id.clone(),
|
||||||
|
thumbnail_link: self.message_id.clone(),
|
||||||
|
reminder_sent: self.reminder_sent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Insertable, Clone)]
|
#[derive(Insertable, Clone)]
|
||||||
#[table_name = "events"]
|
#[table_name = "events"]
|
||||||
pub struct NewEvent {
|
pub struct NewEvent {
|
||||||
|
|
109
src/main.rs
109
src/main.rs
|
@ -8,8 +8,8 @@ use chrono::{DateTime, Datelike, NaiveDateTime, TimeZone, Timelike, Utc};
|
||||||
use chrono_tz::Tz;
|
use chrono_tz::Tz;
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use serenity::client::Client;
|
use serenity::client::Client;
|
||||||
use serenity::framework::standard::macros::{command, group};
|
use serenity::framework::standard::macros::{command, group, help};
|
||||||
use serenity::framework::standard::Args;
|
use serenity::framework::standard::{help_commands, Args, CommandGroup, HelpOptions};
|
||||||
use serenity::framework::standard::{CommandError, CommandResult, StandardFramework};
|
use serenity::framework::standard::{CommandError, CommandResult, StandardFramework};
|
||||||
use serenity::http::Http;
|
use serenity::http::Http;
|
||||||
use serenity::model::channel::{Message, Reaction};
|
use serenity::model::channel::{Message, Reaction};
|
||||||
|
@ -31,14 +31,18 @@ mod hypebot_config;
|
||||||
use crate::database::models::NewEvent;
|
use crate::database::models::NewEvent;
|
||||||
use database::*;
|
use database::*;
|
||||||
use hypebot_config::HypeBotConfig;
|
use hypebot_config::HypeBotConfig;
|
||||||
|
use serenity::model::id::UserId;
|
||||||
use serenity::model::user::User;
|
use serenity::model::user::User;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
const INTERESTED_EMOJI: &str = "\u{2705}";
|
const INTERESTED_EMOJI: &str = "\u{2705}";
|
||||||
const UNINTERESTED_EMOJI: &str = "\u{274C}";
|
const UNINTERESTED_EMOJI: &str = "\u{274C}";
|
||||||
|
|
||||||
/// Event commands group
|
/// Event commands group
|
||||||
#[group]
|
#[group]
|
||||||
#[commands(create_event, confirm_event, cancel_event)]
|
#[only_in(guilds)]
|
||||||
|
#[description("Commands for Creating Events")]
|
||||||
|
#[commands(create, confirm, cancel)]
|
||||||
struct EventCommands;
|
struct EventCommands;
|
||||||
|
|
||||||
/// Struct for storing drafted events
|
/// Struct for storing drafted events
|
||||||
|
@ -83,6 +87,20 @@ impl EventHandler for Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[help]
|
||||||
|
#[command_not_found_text = "Could not find: `{}`."]
|
||||||
|
#[strikethrough_commands_tip_in_guild("HypeBot")]
|
||||||
|
fn bot_help(
|
||||||
|
context: &mut Context,
|
||||||
|
msg: &Message,
|
||||||
|
args: Args,
|
||||||
|
help_options: &'static HelpOptions,
|
||||||
|
groups: &[&'static CommandGroup],
|
||||||
|
owners: HashSet<UserId>,
|
||||||
|
) -> CommandResult {
|
||||||
|
help_commands::with_embeds(context, msg, args, help_options, groups, owners)
|
||||||
|
}
|
||||||
|
|
||||||
/// Thread to send reminders to users
|
/// Thread to send reminders to users
|
||||||
fn send_reminders(cache_and_http: &Arc<CacheAndHttp>, data: &Arc<RwLock<ShareMap>>) {
|
fn send_reminders(cache_and_http: &Arc<CacheAndHttp>, data: &Arc<RwLock<ShareMap>>) {
|
||||||
let sleep_duration = Duration::from_secs(60);
|
let sleep_duration = Duration::from_secs(60);
|
||||||
|
@ -184,9 +202,10 @@ fn send_event_msg(
|
||||||
e.title(event.event_name.clone())
|
e.title(event.event_name.clone())
|
||||||
.color(Colour::PURPLE)
|
.color(Colour::PURPLE)
|
||||||
.description(format!(
|
.description(format!(
|
||||||
"**{}**\n{}",
|
"**{}**\n{}\n\nReact with {} below to receive event reminders!",
|
||||||
native_time.format("%A, %B %d @ %I:%M %P %t %Z"),
|
native_time.format("%A, %B %d @ %I:%M %P %t %Z"),
|
||||||
event.event_desc
|
event.event_desc,
|
||||||
|
INTERESTED_EMOJI
|
||||||
))
|
))
|
||||||
.thumbnail(event.thumbnail_link.clone())
|
.thumbnail(event.thumbnail_link.clone())
|
||||||
.footer(|f| f.text("Local Event Time"))
|
.footer(|f| f.text("Local Event Time"))
|
||||||
|
@ -238,7 +257,7 @@ fn send_draft_event(ctx: &Context, channel: ChannelId) -> CommandResult {
|
||||||
|
|
||||||
channel.send_message(&ctx, |m| {
|
channel.send_message(&ctx, |m| {
|
||||||
m.content(format!(
|
m.content(format!(
|
||||||
"Draft message, use the `confirm_event` command to post it."
|
"Draft message, use the `confirm` command to post it."
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
send_event_msg(&ctx.http, config, channel.0, &draft_event.event, false)?;
|
send_event_msg(&ctx.http, config, channel.0, &draft_event.event, false)?;
|
||||||
|
@ -276,32 +295,28 @@ fn permission_check(ctx: &mut Context, msg: &Message, _command_name: &str) -> bo
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
/// Posts the pending event in the shared context
|
/// Posts a previewed event
|
||||||
fn confirm_event(ctx: &mut Context, msg: &Message, _args: Args) -> CommandResult {
|
///
|
||||||
|
/// **Note**
|
||||||
|
/// You can only post events you have created. Only one preview event can exist at a time.
|
||||||
|
fn confirm(ctx: &mut Context, msg: &Message, _args: Args) -> CommandResult {
|
||||||
let config = get_config(&ctx.data)?;
|
let config = get_config(&ctx.data)?;
|
||||||
let data = ctx.data.read();
|
let data = ctx.data.read();
|
||||||
|
|
||||||
// Get draft event
|
// Get draft event
|
||||||
if let Some(draft_event) = data.get::<DraftEvent>() {
|
if let Some(draft_event) = data.get::<DraftEvent>() {
|
||||||
let new_event = &draft_event.event;
|
let mut new_event = draft_event.event.clone();
|
||||||
// Check to to see if message author is the owner of the pending event
|
// Check to to see if message author is the owner of the pending event
|
||||||
if draft_event.creator_id == msg.author.id.0 {
|
if draft_event.creator_id == msg.author.id.0 {
|
||||||
// Send event message
|
// Send event message
|
||||||
let event_msg =
|
let event_msg =
|
||||||
send_event_msg(&ctx.http, &config, config.event_channel, new_event, true)?;
|
send_event_msg(&ctx.http, &config, config.event_channel, &new_event, true)?;
|
||||||
|
|
||||||
msg.reply(&ctx, "Event posted!")?;
|
msg.reply(&ctx, "Event posted!")?;
|
||||||
|
|
||||||
let new_event = NewEvent {
|
new_event.message_id = event_msg.id.0.to_string();
|
||||||
message_id: event_msg.id.0.to_string(),
|
|
||||||
event_time: new_event.event_time.clone(),
|
|
||||||
event_desc: new_event.event_desc.clone(),
|
|
||||||
event_name: new_event.event_name.clone(),
|
|
||||||
thumbnail_link: new_event.event_name.clone(),
|
|
||||||
reminder_sent: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
insert_event(config.db_url.clone(), &new_event).ok();
|
insert_event(config.db_url.clone(), &new_event)?;
|
||||||
} else {
|
} else {
|
||||||
msg.reply(&ctx, format!("You do not have a pending event!"))?;
|
msg.reply(&ctx, format!("You do not have a pending event!"))?;
|
||||||
}
|
}
|
||||||
|
@ -313,8 +328,16 @@ fn confirm_event(ctx: &mut Context, msg: &Message, _args: Args) -> CommandResult
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
/// Creates an event and announce it
|
/// Creates an event and previews the announcement
|
||||||
fn create_event(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
|
///
|
||||||
|
/// `~create "event name" "04:20pm 2069-04-20" "event description" \<http://optional.thumbnail.link>`
|
||||||
|
///
|
||||||
|
/// **Time format**
|
||||||
|
/// The time format is HH:MMam YYYY-MM-DD
|
||||||
|
///
|
||||||
|
/// **Thumbnail Link**
|
||||||
|
/// The thumbnail link is optional, if one is not provided, a default image is shown
|
||||||
|
fn create(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
// Get config
|
// Get config
|
||||||
let config = get_config(&ctx.data)?;
|
let config = get_config(&ctx.data)?;
|
||||||
let guild_id = msg
|
let guild_id = msg
|
||||||
|
@ -322,9 +345,27 @@ fn create_event(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResu
|
||||||
.ok_or(CommandError("Unable to get guild ID".to_string()))?;
|
.ok_or(CommandError("Unable to get guild ID".to_string()))?;
|
||||||
|
|
||||||
// Parse args
|
// Parse args
|
||||||
let event_name = args.single::<String>()?.replace("\"", "");
|
let event_name = match args.single::<String>() {
|
||||||
let date_string = args.single::<String>()?.replace("\"", "");
|
Ok(event_name) => event_name.replace("\"", ""),
|
||||||
let description = args.single::<String>()?.replace("\"", "");
|
Err(_) => {
|
||||||
|
msg.reply(&ctx, "No event name provided.".to_string())?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let date_string = match args.single::<String>() {
|
||||||
|
Ok(date_string) => date_string.replace("\"", ""),
|
||||||
|
Err(_) => {
|
||||||
|
msg.reply(&ctx, "No date provided.".to_string())?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let description = match args.single::<String>() {
|
||||||
|
Ok(desc) => desc.replace("\"", ""),
|
||||||
|
Err(_) => {
|
||||||
|
msg.reply(&ctx, "No description provided.".to_string())?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
let thumbnail_link = match args.single::<String>() {
|
let thumbnail_link = match args.single::<String>() {
|
||||||
Ok(link) => link.replace("<", "").replace(">", ""),
|
Ok(link) => link.replace("<", "").replace(">", ""),
|
||||||
Err(_) => config.default_thumbnail_link.clone(),
|
Err(_) => config.default_thumbnail_link.clone(),
|
||||||
|
@ -332,7 +373,16 @@ fn create_event(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResu
|
||||||
|
|
||||||
// Parse date
|
// Parse date
|
||||||
let tz: Tz = config.event_timezone;
|
let tz: Tz = config.event_timezone;
|
||||||
let input_date = NaiveDateTime::parse_from_str(date_string.as_str(), "%I:%M%p %Y-%m-%d")?;
|
let input_date = match NaiveDateTime::parse_from_str(date_string.as_str(), "%I:%M%P %Y-%m-%d") {
|
||||||
|
Ok(date) => date,
|
||||||
|
Err(_) => {
|
||||||
|
msg.reply(
|
||||||
|
&ctx,
|
||||||
|
"Invalid date format. Format is HH:MMam YYYY-MM-DD".to_string(),
|
||||||
|
)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let input_date = tz
|
let input_date = tz
|
||||||
.ymd(
|
.ymd(
|
||||||
|
@ -373,8 +423,10 @@ fn create_event(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResu
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
/// Cancels an event
|
/// Cancels an already scheduled event
|
||||||
fn cancel_event(ctx: &mut Context, _msg: &Message, mut args: Args) -> CommandResult {
|
///
|
||||||
|
/// `~create_event "event name"`
|
||||||
|
fn cancel(ctx: &mut Context, _msg: &Message, mut args: Args) -> CommandResult {
|
||||||
let config = get_config(&ctx.data)?;
|
let config = get_config(&ctx.data)?;
|
||||||
|
|
||||||
// Parse args
|
// Parse args
|
||||||
|
@ -447,7 +499,8 @@ fn main() -> clap::Result<()> {
|
||||||
.ignore_webhooks(true)
|
.ignore_webhooks(true)
|
||||||
})
|
})
|
||||||
.before(permission_check)
|
.before(permission_check)
|
||||||
.group(&EVENTCOMMANDS_GROUP),
|
.group(&EVENTCOMMANDS_GROUP)
|
||||||
|
.help(&BOT_HELP),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Copy config data to client data
|
// Copy config data to client data
|
||||||
|
|
Loading…
Reference in New Issue