2020-08-10 21:12:26 +00:00
|
|
|
use regex_command_attr::command;
|
|
|
|
|
|
|
|
use serenity::{
|
|
|
|
client::Context,
|
|
|
|
model::{
|
2020-08-10 23:26:39 +00:00
|
|
|
id::{
|
|
|
|
UserId, GuildId, ChannelId,
|
|
|
|
},
|
2020-08-10 21:12:26 +00:00
|
|
|
channel::{
|
|
|
|
Message,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
framework::standard::CommandResult,
|
|
|
|
};
|
|
|
|
|
2020-08-17 23:18:33 +00:00
|
|
|
use std::fmt;
|
|
|
|
|
2020-08-17 13:16:52 +00:00
|
|
|
use crate::SQLPool;
|
|
|
|
use sqlx::MySqlPool;
|
2020-08-17 23:18:33 +00:00
|
|
|
use std::convert::TryFrom;
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct TodoNotFound;
|
2020-08-17 13:16:52 +00:00
|
|
|
|
2020-08-17 23:18:33 +00:00
|
|
|
impl std::error::Error for TodoNotFound {}
|
|
|
|
impl fmt::Display for TodoNotFound {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "Todo not found")
|
|
|
|
}
|
|
|
|
}
|
2020-08-17 13:16:52 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct Todo {
|
|
|
|
id: u32,
|
|
|
|
user_id: Option<u32>,
|
|
|
|
guild_id: Option<u32>,
|
|
|
|
channel_id: Option<u32>,
|
|
|
|
value: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TodoTarget {
|
|
|
|
user: UserId,
|
|
|
|
guild: Option<GuildId>,
|
|
|
|
channel: Option<ChannelId>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TodoTarget {
|
2020-08-17 23:18:33 +00:00
|
|
|
pub fn name(&self) -> String {
|
|
|
|
if self.channel.is_some() {
|
|
|
|
"Channel"
|
|
|
|
}
|
|
|
|
else if self.guild.is_some() {
|
|
|
|
"Guild"
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
"User"
|
|
|
|
}.to_string()
|
|
|
|
}
|
|
|
|
|
2020-08-17 13:16:52 +00:00
|
|
|
pub async fn view(&self, pool: MySqlPool) -> Result<Vec<Todo>, Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
Ok(if let Some(cid) = self.channel {
|
|
|
|
sqlx::query_as!(Todo,
|
|
|
|
"
|
|
|
|
SELECT * FROM todos WHERE channel_id = (SELECT id FROM channels WHERE channel = ?)
|
|
|
|
", cid.as_u64())
|
|
|
|
.fetch_all(&pool)
|
|
|
|
.await?
|
|
|
|
}
|
|
|
|
else if let Some(gid) = self.guild {
|
|
|
|
sqlx::query_as!(Todo,
|
|
|
|
"
|
|
|
|
SELECT * FROM todos WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND channel_id IS NULL
|
|
|
|
", gid.as_u64())
|
|
|
|
.fetch_all(&pool)
|
|
|
|
.await?
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sqlx::query_as!(Todo,
|
|
|
|
"
|
2020-08-17 23:18:33 +00:00
|
|
|
SELECT * FROM todos WHERE user_id = (SELECT id FROM users WHERE user = ?) AND guild_id IS NULL
|
2020-08-17 13:16:52 +00:00
|
|
|
", self.user.as_u64())
|
|
|
|
.fetch_all(&pool)
|
|
|
|
.await?
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn add(&self, value: String, pool: MySqlPool) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
if let (Some(cid), Some(gid)) = (self.channel, self.guild) {
|
|
|
|
sqlx::query!(
|
|
|
|
"
|
|
|
|
INSERT INTO todos (user_id, guild_id, channel_id, value) VALUES (
|
|
|
|
(SELECT id FROM users WHERE user = ?),
|
|
|
|
(SELECT id FROM guilds WHERE guild = ?),
|
|
|
|
(SELECT id FROM channels WHERE channel = ?),
|
|
|
|
?
|
|
|
|
)
|
|
|
|
", self.user.as_u64(), gid.as_u64(), cid.as_u64(), value)
|
|
|
|
.execute(&pool)
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
else if let Some(gid) = self.guild {
|
|
|
|
sqlx::query!(
|
|
|
|
"
|
|
|
|
INSERT INTO todos (user_id, guild_id, value) VALUES (
|
|
|
|
(SELECT id FROM users WHERE user = ?),
|
|
|
|
(SELECT id FROM guilds WHERE guild = ?),
|
|
|
|
?
|
|
|
|
)
|
|
|
|
", self.user.as_u64(), gid.as_u64(), value)
|
|
|
|
.execute(&pool)
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sqlx::query!(
|
|
|
|
"
|
|
|
|
INSERT INTO todos (user_id, value) VALUES (
|
|
|
|
(SELECT id FROM users WHERE user = ?),
|
|
|
|
?
|
|
|
|
)
|
|
|
|
", self.user.as_u64(), value)
|
|
|
|
.execute(&pool)
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-08-17 23:18:33 +00:00
|
|
|
pub async fn remove(&self, num: usize, pool: MySqlPool) -> Result<(), Box<dyn std::error::Error + Sync + Send>> {
|
|
|
|
let todos = self.view(pool.clone()).await?;
|
2020-08-17 13:16:52 +00:00
|
|
|
|
2020-08-17 23:18:33 +00:00
|
|
|
if let Some(removal_todo) = todos.get(num) {
|
|
|
|
sqlx::query!(
|
|
|
|
"
|
|
|
|
DELETE FROM todos WHERE id = ?
|
|
|
|
", removal_todo.id)
|
|
|
|
.execute(&pool)
|
|
|
|
.await?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Err(Box::try_from(TodoNotFound).unwrap())
|
|
|
|
}
|
2020-08-17 13:16:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn clear(&self, pool: MySqlPool) -> Result<(), Box<dyn std::error::Error + Sync + Send>> {
|
|
|
|
if let Some(cid) = self.channel {
|
|
|
|
sqlx::query!(
|
|
|
|
"
|
|
|
|
DELETE FROM todos WHERE channel_id = (SELECT id FROM channels WHERE channel = ?)
|
|
|
|
", cid.as_u64())
|
|
|
|
.execute(&pool)
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
else if let Some(gid) = self.guild {
|
|
|
|
sqlx::query!(
|
|
|
|
"
|
|
|
|
DELETE FROM todos WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND channel_id IS NULL
|
|
|
|
", gid.as_u64())
|
|
|
|
.execute(&pool)
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sqlx::query!(
|
|
|
|
"
|
2020-08-17 23:18:33 +00:00
|
|
|
DELETE FROM todos WHERE user_id = (SELECT id FROM users WHERE user = ?) AND guild_id IS NULL
|
2020-08-17 13:16:52 +00:00
|
|
|
", self.user.as_u64())
|
|
|
|
.execute(&pool)
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-08-10 21:12:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
enum SubCommand {
|
|
|
|
View,
|
|
|
|
Add,
|
|
|
|
Remove,
|
|
|
|
Clear,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[command]
|
|
|
|
async fn todo_parse(ctx: &Context, msg: &Message, args: String) -> CommandResult {
|
2020-08-10 23:26:39 +00:00
|
|
|
|
|
|
|
let mut split = args.split(" ");
|
|
|
|
|
|
|
|
if let Some(target) = split.next() {
|
2020-08-17 13:16:52 +00:00
|
|
|
let target_opt = match target {
|
2020-08-10 23:26:39 +00:00
|
|
|
"user" =>
|
2020-08-17 13:16:52 +00:00
|
|
|
Some(TodoTarget {user: msg.author.id, guild: None, channel: None}),
|
2020-08-10 23:26:39 +00:00
|
|
|
|
|
|
|
"channel" =>
|
2020-08-17 13:16:52 +00:00
|
|
|
if let Some(gid) = msg.guild_id {
|
|
|
|
Some(TodoTarget {user: msg.author.id, guild: Some(gid), channel: Some(msg.channel_id)})
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
None
|
|
|
|
},
|
2020-08-10 23:26:39 +00:00
|
|
|
|
|
|
|
"server" | "guild" => {
|
|
|
|
if let Some(gid) = msg.guild_id {
|
2020-08-17 13:16:52 +00:00
|
|
|
Some(TodoTarget {user: msg.author.id, guild: Some(gid), channel: None})
|
2020-08-10 23:26:39 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_ => {
|
|
|
|
None
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(target) = target_opt {
|
|
|
|
|
|
|
|
let subcommand_opt = match split.next() {
|
|
|
|
|
|
|
|
Some("add") => Some(SubCommand::Add),
|
|
|
|
|
|
|
|
Some("remove") => Some(SubCommand::Remove),
|
|
|
|
|
|
|
|
Some("clear") => Some(SubCommand::Clear),
|
|
|
|
|
|
|
|
None => Some(SubCommand::View),
|
|
|
|
|
|
|
|
Some(_unrecognised) => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(subcommand) = subcommand_opt {
|
2020-08-17 23:18:33 +00:00
|
|
|
todo(ctx, msg, target, subcommand, split.collect::<Vec<&str>>().join(" ")).await;
|
2020-08-10 23:26:39 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
let _ = msg.channel_id.say(&ctx, "Todo help").await;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let _ = msg.channel_id.say(&ctx, "Todo help").await;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let _ = msg.channel_id.say(&ctx, "Todo help").await;
|
|
|
|
}
|
|
|
|
|
2020-08-10 21:12:26 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-08-17 23:18:33 +00:00
|
|
|
async fn todo(ctx: &Context, msg: &Message, target: TodoTarget, subcommand: SubCommand, extra: String) {
|
2020-08-17 13:16:52 +00:00
|
|
|
let pool = ctx.data.read().await
|
|
|
|
.get::<SQLPool>().cloned().expect("Could not get SQLPool from data");
|
2020-08-10 21:12:26 +00:00
|
|
|
|
2020-08-17 13:16:52 +00:00
|
|
|
match subcommand {
|
|
|
|
SubCommand::View => {
|
2020-08-17 23:18:33 +00:00
|
|
|
let todo_items = target.view(pool).await.unwrap();
|
|
|
|
let mut todo_groups = vec!["".to_string()];
|
|
|
|
let mut char_count: u16 = 0;
|
|
|
|
|
|
|
|
todo_items.iter().enumerate().for_each(|(count, todo)| {
|
|
|
|
let display = format!("{}: {}\n", count + 1, todo.value);
|
|
|
|
|
|
|
|
if char_count + display.len() as u16 > 2000 {
|
|
|
|
char_count = display.len() as u16;
|
|
|
|
|
|
|
|
todo_groups.push(display);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
char_count += display.len() as u16;
|
|
|
|
|
|
|
|
let last_group = todo_groups.pop().unwrap();
|
|
|
|
|
|
|
|
todo_groups.push(format!("{}{}", last_group, display));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
for group in todo_groups {
|
|
|
|
let _ = msg.channel_id.send_message(&ctx, |m| m
|
|
|
|
.embed(|e| e
|
|
|
|
.title(format!("{} Todo", target.name()))
|
|
|
|
.description(group)
|
|
|
|
)
|
|
|
|
).await;
|
|
|
|
}
|
2020-08-17 13:16:52 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
SubCommand::Add => {
|
2020-08-17 23:18:33 +00:00
|
|
|
target.add(extra, pool).await.unwrap();
|
2020-08-17 13:16:52 +00:00
|
|
|
|
2020-08-17 23:18:33 +00:00
|
|
|
let _ = msg.channel_id.say(&ctx, "Todo added").await;
|
2020-08-17 13:16:52 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
SubCommand::Remove => {
|
2020-08-17 23:18:33 +00:00
|
|
|
let _ = if let Ok(num) = extra.parse::<usize>() {
|
|
|
|
if target.remove(num - 1, pool).await.is_ok() {
|
|
|
|
msg.channel_id.say(&ctx, "Todo removed")
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
msg.channel_id.say(&ctx, "Todo not removed")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
msg.channel_id.say(&ctx, "Todo not removed")
|
|
|
|
}.await;
|
2020-08-17 13:16:52 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
SubCommand::Clear => {
|
2020-08-17 23:18:33 +00:00
|
|
|
target.clear(pool).await.unwrap();
|
2020-08-17 13:16:52 +00:00
|
|
|
},
|
|
|
|
}
|
2020-08-10 21:12:26 +00:00
|
|
|
}
|