more commands. fixed an issue with text only commands
This commit is contained in:
parent
471948bed3
commit
9b5333dc87
@ -53,13 +53,9 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let name = &name[..];
|
||||
|
||||
match name {
|
||||
"arg" => options
|
||||
.cmd_args
|
||||
.push(propagate_err!(attributes::parse(values))),
|
||||
"arg" => options.cmd_args.push(propagate_err!(attributes::parse(values))),
|
||||
"example" => {
|
||||
options
|
||||
.examples
|
||||
.push(propagate_err!(attributes::parse(values)));
|
||||
options.examples.push(propagate_err!(attributes::parse(values)));
|
||||
}
|
||||
"description" => {
|
||||
let line: String = propagate_err!(attributes::parse(values));
|
||||
@ -105,20 +101,14 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let arg_idents = cmd_args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
n.with_suffix(arg.name.replace(" ", "_").replace("-", "_").as_str())
|
||||
.with_suffix(ARG)
|
||||
n.with_suffix(arg.name.replace(" ", "_").replace("-", "_").as_str()).with_suffix(ARG)
|
||||
})
|
||||
.collect::<Vec<Ident>>();
|
||||
|
||||
let mut tokens = cmd_args
|
||||
.iter_mut()
|
||||
.map(|arg| {
|
||||
let Arg {
|
||||
name,
|
||||
description,
|
||||
kind,
|
||||
required,
|
||||
} = arg;
|
||||
let Arg { name, description, kind, required } = arg;
|
||||
|
||||
let an = n.with_suffix(name.as_str()).with_suffix(ARG);
|
||||
|
||||
@ -141,7 +131,7 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let variant = if args.len() == 2 {
|
||||
quote!(crate::framework::CommandFnType::Multi)
|
||||
} else {
|
||||
let string: Type = parse_quote!(std::string::String);
|
||||
let string: Type = parse_quote!(String);
|
||||
|
||||
let final_arg = args.get(2).unwrap();
|
||||
|
||||
|
@ -20,41 +20,28 @@ fn parse_argument(arg: FnArg) -> Result<Argument> {
|
||||
let name = id.ident;
|
||||
let mutable = id.mutability;
|
||||
|
||||
Ok(Argument {
|
||||
mutable,
|
||||
name,
|
||||
kind: *kind,
|
||||
})
|
||||
Ok(Argument { mutable, name, kind: *kind })
|
||||
}
|
||||
Pat::Wild(wild) => {
|
||||
let token = wild.underscore_token;
|
||||
|
||||
let name = Ident::new("_", token.spans[0]);
|
||||
|
||||
Ok(Argument {
|
||||
mutable: None,
|
||||
name,
|
||||
kind: *kind,
|
||||
})
|
||||
Ok(Argument { mutable: None, name, kind: *kind })
|
||||
}
|
||||
_ => Err(Error::new(
|
||||
pat.span(),
|
||||
format_args!("unsupported pattern: {:?}", pat),
|
||||
)),
|
||||
_ => Err(Error::new(pat.span(), format_args!("unsupported pattern: {:?}", pat))),
|
||||
}
|
||||
}
|
||||
FnArg::Receiver(_) => Err(Error::new(
|
||||
arg.span(),
|
||||
format_args!("`self` arguments are prohibited: {:?}", arg),
|
||||
)),
|
||||
FnArg::Receiver(_) => {
|
||||
Err(Error::new(arg.span(), format_args!("`self` arguments are prohibited: {:?}", arg)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Test if the attribute is cooked.
|
||||
fn is_cooked(attr: &Attribute) -> bool {
|
||||
const COOKED_ATTRIBUTE_NAMES: &[&str] = &[
|
||||
"cfg", "cfg_attr", "derive", "inline", "allow", "warn", "deny", "forbid",
|
||||
];
|
||||
const COOKED_ATTRIBUTE_NAMES: &[&str] =
|
||||
&["cfg", "cfg_attr", "derive", "inline", "allow", "warn", "deny", "forbid"];
|
||||
|
||||
COOKED_ATTRIBUTE_NAMES.iter().any(|n| attr.path.is_ident(n))
|
||||
}
|
||||
@ -115,32 +102,15 @@ impl Parse for CommandFun {
|
||||
braced!(bcont in input);
|
||||
let body = bcont.call(Block::parse_within)?;
|
||||
|
||||
let args = args
|
||||
.into_iter()
|
||||
.map(parse_argument)
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let args = args.into_iter().map(parse_argument).collect::<Result<Vec<_>>>()?;
|
||||
|
||||
Ok(Self {
|
||||
attributes,
|
||||
cooked,
|
||||
visibility,
|
||||
name,
|
||||
args,
|
||||
body,
|
||||
})
|
||||
Ok(Self { attributes, cooked, visibility, name, args, body })
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for CommandFun {
|
||||
fn to_tokens(&self, stream: &mut TokenStream2) {
|
||||
let Self {
|
||||
attributes: _,
|
||||
cooked,
|
||||
visibility,
|
||||
name,
|
||||
args,
|
||||
body,
|
||||
} = self;
|
||||
let Self { attributes: _, cooked, visibility, name, args, body } = self;
|
||||
|
||||
stream.extend(quote! {
|
||||
#(#cooked)*
|
||||
@ -211,6 +181,7 @@ pub(crate) enum ApplicationCommandOptionType {
|
||||
Channel,
|
||||
Role,
|
||||
Mentionable,
|
||||
Number,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
@ -226,6 +197,7 @@ impl ApplicationCommandOptionType {
|
||||
"Channel" => Self::Channel,
|
||||
"Role" => Self::Role,
|
||||
"Mentionable" => Self::Mentionable,
|
||||
"Number" => Self::Number,
|
||||
_ => Self::Unknown,
|
||||
}
|
||||
}
|
||||
@ -246,6 +218,7 @@ impl ToTokens for ApplicationCommandOptionType {
|
||||
ApplicationCommandOptionType::Channel => quote!(Channel),
|
||||
ApplicationCommandOptionType::Role => quote!(Role),
|
||||
ApplicationCommandOptionType::Mentionable => quote!(Mentionable),
|
||||
ApplicationCommandOptionType::Number => quote!(Number),
|
||||
ApplicationCommandOptionType::Unknown => quote!(Unknown),
|
||||
};
|
||||
|
||||
@ -289,9 +262,6 @@ pub(crate) struct Options {
|
||||
impl Options {
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
group: "Other".to_string(),
|
||||
..Default::default()
|
||||
}
|
||||
Self { group: "Other".to_string(), ..Default::default() }
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,3 @@
|
||||
imports_granularity = "Crate"
|
||||
group_imports = "StdExternalCrate"
|
||||
use_small_heuristics = "Max"
|
||||
|
@ -40,15 +40,15 @@ async fn info(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync)) {
|
||||
.description(format!(
|
||||
"Default prefix: `{default_prefix}`
|
||||
Reset prefix: `@{user} prefix {default_prefix}`
|
||||
Help: `{prefix}help`
|
||||
|
||||
**Welcome to Reminder Bot!**
|
||||
Help: `{prefix}help`**Welcome \
|
||||
to Reminder Bot!**
|
||||
Developer: <@203532103185465344>
|
||||
Icon: <@253202252821430272>
|
||||
Find me on https://discord.jellywx.com and on https://github.com/JellyWX :)
|
||||
Find me on https://discord.jellywx.com \
|
||||
and on https://github.com/JellyWX :)
|
||||
|
||||
Invite the bot: https://invite.reminder-bot.com/
|
||||
Use our dashboard: https://reminder-bot.com/",
|
||||
Invite the bot: https://invite.reminder-bot.com/Use our dashboard: \
|
||||
https://reminder-bot.com/",
|
||||
default_prefix = *DEFAULT_PREFIX,
|
||||
user = current_user.name,
|
||||
prefix = prefix
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub mod info_cmds;
|
||||
//pub mod moderation_cmds;
|
||||
//pub mod reminder_cmds;
|
||||
pub mod moderation_cmds;
|
||||
pub mod reminder_cmds;
|
||||
//pub mod todo_cmds;
|
||||
|
@ -2,134 +2,126 @@ use std::{collections::HashMap, iter};
|
||||
|
||||
use chrono::offset::Utc;
|
||||
use chrono_tz::{Tz, TZ_VARIANTS};
|
||||
use inflector::Inflector;
|
||||
use levenshtein::levenshtein;
|
||||
use regex::Regex;
|
||||
use regex_command_attr::command;
|
||||
use serenity::{
|
||||
builder::CreateActionRow,
|
||||
client::Context,
|
||||
framework::Framework,
|
||||
model::{
|
||||
channel::Message,
|
||||
guild::ActionRole::Create,
|
||||
id::{ChannelId, MessageId, RoleId},
|
||||
interactions::message_component::ButtonStyle,
|
||||
misc::Mentionable,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
command_help,
|
||||
consts::{REGEX_ALIAS, REGEX_CHANNEL, REGEX_COMMANDS, REGEX_ROLE, THEME_COLOR},
|
||||
framework::SendIterator,
|
||||
get_ctx_data,
|
||||
consts::{REGEX_ALIAS, REGEX_COMMANDS, THEME_COLOR},
|
||||
framework::{CommandInvoke, CreateGenericResponse, PermissionLevel},
|
||||
models::{channel_data::ChannelData, guild_data::GuildData, user_data::UserData, CtxData},
|
||||
FrameworkCtx, PopularTimezones,
|
||||
PopularTimezones, RegexFramework, SQLPool,
|
||||
};
|
||||
|
||||
#[command]
|
||||
#[command("blacklist")]
|
||||
#[description("Block channels from using bot commands")]
|
||||
#[arg(
|
||||
name = "channel",
|
||||
description = "The channel to blacklist",
|
||||
kind = "Channel",
|
||||
required = false
|
||||
)]
|
||||
#[supports_dm(false)]
|
||||
#[permission_level(Restricted)]
|
||||
#[required_permissions(Restricted)]
|
||||
#[can_blacklist(false)]
|
||||
async fn blacklist(ctx: &Context, msg: &Message, args: String) {
|
||||
let (pool, lm) = get_ctx_data(&ctx).await;
|
||||
async fn blacklist(
|
||||
ctx: &Context,
|
||||
invoke: &(dyn CommandInvoke + Send + Sync),
|
||||
args: HashMap<String, String>,
|
||||
) {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
|
||||
let language = UserData::language_of(&msg.author, &pool).await;
|
||||
let channel = match args.get("channel") {
|
||||
Some(channel_id) => ChannelId(channel_id.parse::<u64>().unwrap()),
|
||||
|
||||
let capture_opt = REGEX_CHANNEL
|
||||
.captures(&args)
|
||||
.map(|cap| cap.get(1))
|
||||
.flatten();
|
||||
None => invoke.channel_id(),
|
||||
}
|
||||
.to_channel_cached(&ctx)
|
||||
.unwrap();
|
||||
|
||||
let (channel, local) = match capture_opt {
|
||||
Some(capture) => (
|
||||
ChannelId(capture.as_str().parse::<u64>().unwrap()).to_channel_cached(&ctx),
|
||||
false,
|
||||
),
|
||||
|
||||
None => (msg.channel(&ctx).await.ok(), true),
|
||||
};
|
||||
|
||||
let mut channel_data = ChannelData::from_channel(channel.unwrap(), &pool)
|
||||
.await
|
||||
.unwrap();
|
||||
let mut channel_data = ChannelData::from_channel(&channel, &pool).await.unwrap();
|
||||
|
||||
channel_data.blacklisted = !channel_data.blacklisted;
|
||||
channel_data.commit_changes(&pool).await;
|
||||
|
||||
if channel_data.blacklisted {
|
||||
if local {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "blacklist/added"))
|
||||
.await;
|
||||
} else {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "blacklist/added_from"))
|
||||
.await;
|
||||
}
|
||||
} else if local {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "blacklist/removed"))
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content(format!("{} has been blacklisted", channel.mention())),
|
||||
)
|
||||
.await;
|
||||
} else {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "blacklist/removed_from"))
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content(format!("{} has been removed from the blacklist", channel.mention())),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
#[command]
|
||||
async fn timezone(ctx: &Context, msg: &Message, args: String) {
|
||||
let (pool, lm) = get_ctx_data(&ctx).await;
|
||||
#[command("timezone")]
|
||||
#[description("Select your timezone")]
|
||||
#[arg(
|
||||
name = "timezone",
|
||||
description = "Timezone to use from this list: https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
async fn timezone(
|
||||
ctx: &Context,
|
||||
invoke: &(dyn CommandInvoke + Send + Sync),
|
||||
args: HashMap<String, String>,
|
||||
) {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
let mut user_data = ctx.user_data(invoke.author_id()).await.unwrap();
|
||||
|
||||
let mut user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap();
|
||||
let footer_text = format!("Current timezone: {}", user_data.timezone);
|
||||
|
||||
let footer_text = lm.get(&user_data.language, "timezone/footer").replacen(
|
||||
"{timezone}",
|
||||
&user_data.timezone,
|
||||
1,
|
||||
);
|
||||
|
||||
if !args.is_empty() {
|
||||
match args.parse::<Tz>() {
|
||||
Ok(_) => {
|
||||
user_data.timezone = args;
|
||||
if let Some(timezone) = args.get("timezone") {
|
||||
match timezone.parse::<Tz>() {
|
||||
Ok(tz) => {
|
||||
user_data.timezone = timezone.clone();
|
||||
user_data.commit_changes(&pool).await;
|
||||
|
||||
let now = Utc::now().with_timezone(&user_data.timezone());
|
||||
let now = Utc::now().with_timezone(&tz);
|
||||
|
||||
let content = lm
|
||||
.get(&user_data.language, "timezone/set_p")
|
||||
.replacen("{timezone}", &user_data.timezone, 1)
|
||||
.replacen("{time}", &now.format("%H:%M").to_string(), 1);
|
||||
|
||||
let _ =
|
||||
msg.channel_id
|
||||
.send_message(&ctx, |m| {
|
||||
m.embed(|e| {
|
||||
e.title(lm.get(&user_data.language, "timezone/set_p_title"))
|
||||
.description(content)
|
||||
.color(*THEME_COLOR)
|
||||
.footer(|f| {
|
||||
f.text(
|
||||
lm.get(&user_data.language, "timezone/footer")
|
||||
.replacen("{timezone}", &user_data.timezone, 1),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
.await;
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().embed(|e| {
|
||||
e.title("Timezone Set")
|
||||
.description(format!(
|
||||
"Timezone has been set to **{}**. Your current time should be `{}`",
|
||||
timezone,
|
||||
now.format("%H:%M").to_string()
|
||||
))
|
||||
.color(*THEME_COLOR)
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
Err(_) => {
|
||||
let filtered_tz = TZ_VARIANTS
|
||||
.iter()
|
||||
.filter(|tz| {
|
||||
args.contains(&tz.to_string())
|
||||
|| tz.to_string().contains(&args)
|
||||
|| levenshtein(&tz.to_string(), &args) < 4
|
||||
timezone.contains(&tz.to_string())
|
||||
|| tz.to_string().contains(timezone)
|
||||
|| levenshtein(&tz.to_string(), timezone) < 4
|
||||
})
|
||||
.take(25)
|
||||
.map(|t| t.to_owned())
|
||||
@ -146,371 +138,164 @@ async fn timezone(ctx: &Context, msg: &Message, args: String) {
|
||||
)
|
||||
});
|
||||
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.send_message(&ctx, |m| {
|
||||
m.embed(|e| {
|
||||
e.title(lm.get(&user_data.language, "timezone/no_timezone_title"))
|
||||
.description(lm.get(&user_data.language, "timezone/no_timezone"))
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().embed(|e| {
|
||||
e.title("Timezone Not Recognized")
|
||||
.description("Possibly you meant one of the following timezones, otherwise click [here](https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee):")
|
||||
.color(*THEME_COLOR)
|
||||
.fields(fields)
|
||||
.footer(|f| f.text(footer_text))
|
||||
.url("https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee")
|
||||
}).components(|c| {
|
||||
for row in filtered_tz.as_slice().chunks(5) {
|
||||
let mut action_row = CreateActionRow::default();
|
||||
for timezone in row {
|
||||
action_row.create_button(|b| {
|
||||
b.style(ButtonStyle::Secondary)
|
||||
.label(timezone.to_string())
|
||||
.custom_id(format!("timezone:{}", timezone.to_string()))
|
||||
});
|
||||
}
|
||||
|
||||
c.add_action_row(action_row);
|
||||
}
|
||||
|
||||
c
|
||||
})
|
||||
})
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let content = lm
|
||||
.get(&user_data.language, "timezone/no_argument")
|
||||
.replace("{prefix}", &ctx.prefix(msg.guild_id).await);
|
||||
|
||||
let popular_timezones = ctx
|
||||
.data
|
||||
.read()
|
||||
.await
|
||||
.get::<PopularTimezones>()
|
||||
.cloned()
|
||||
.unwrap();
|
||||
let popular_timezones = ctx.data.read().await.get::<PopularTimezones>().cloned().unwrap();
|
||||
|
||||
let popular_timezones_iter = popular_timezones.iter().map(|t| {
|
||||
(
|
||||
t.to_string(),
|
||||
format!(
|
||||
"🕗 `{}`",
|
||||
Utc::now().with_timezone(t).format("%H:%M").to_string()
|
||||
),
|
||||
format!("🕗 `{}`", Utc::now().with_timezone(t).format("%H:%M").to_string()),
|
||||
true,
|
||||
)
|
||||
});
|
||||
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.send_message(&ctx, |m| {
|
||||
m.embed(|e| {
|
||||
e.title(lm.get(&user_data.language, "timezone/no_argument_title"))
|
||||
.description(content)
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().embed(|e| {
|
||||
e.title("Timezone Usage")
|
||||
.description(
|
||||
"**Usage:**
|
||||
`/timezone Name`
|
||||
|
||||
**Example:**
|
||||
`/timezone Europe/London`
|
||||
|
||||
You may want to use one of the popular timezones below, otherwise click [here](https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee):",
|
||||
)
|
||||
.color(*THEME_COLOR)
|
||||
.fields(popular_timezones_iter)
|
||||
.footer(|f| f.text(footer_text))
|
||||
.url("https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee")
|
||||
})
|
||||
.components(|c| {
|
||||
for row in popular_timezones.as_slice().chunks(5) {
|
||||
let mut action_row = CreateActionRow::default();
|
||||
for timezone in row {
|
||||
action_row.create_button(|b| {
|
||||
b.style(ButtonStyle::Secondary)
|
||||
.label(timezone.to_string())
|
||||
.custom_id(format!("timezone:{}", timezone.to_string()))
|
||||
});
|
||||
}
|
||||
|
||||
c.add_action_row(action_row);
|
||||
}
|
||||
|
||||
c
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
#[command("lang")]
|
||||
async fn language(ctx: &Context, msg: &Message, args: String) {
|
||||
let (pool, lm) = get_ctx_data(&ctx).await;
|
||||
|
||||
let mut user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap();
|
||||
|
||||
if !args.is_empty() {
|
||||
match lm.get_language(&args) {
|
||||
Some(lang) => {
|
||||
user_data.language = lang.to_string();
|
||||
|
||||
user_data.commit_changes(&pool).await;
|
||||
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.send_message(&ctx, |m| {
|
||||
m.embed(|e| {
|
||||
e.title(lm.get(&user_data.language, "lang/set_p_title"))
|
||||
.color(*THEME_COLOR)
|
||||
.description(lm.get(&user_data.language, "lang/set_p"))
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
None => {
|
||||
let language_codes = lm.all_languages().map(|(k, v)| {
|
||||
(
|
||||
format!("{} {}", lm.get(k, "flag"), v.to_title_case()),
|
||||
format!("`$lang {}`", k.to_uppercase()),
|
||||
true,
|
||||
)
|
||||
});
|
||||
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.send_message(&ctx, |m| {
|
||||
m.embed(|e| {
|
||||
e.title(lm.get(&user_data.language, "lang/invalid_title"))
|
||||
.color(*THEME_COLOR)
|
||||
.description(lm.get(&user_data.language, "lang/invalid"))
|
||||
.fields(language_codes)
|
||||
})
|
||||
.components(|c| {
|
||||
for row in lm
|
||||
.all_languages()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect::<Vec<(String, String)>>()
|
||||
.as_slice()
|
||||
.chunks(5)
|
||||
{
|
||||
let mut action_row = CreateActionRow::default();
|
||||
for (code, name) in row {
|
||||
action_row.create_button(|b| {
|
||||
b.style(ButtonStyle::Primary)
|
||||
.label(name.to_title_case())
|
||||
.custom_id(format!("lang:{}", code.to_uppercase()))
|
||||
});
|
||||
}
|
||||
|
||||
c.add_action_row(action_row);
|
||||
}
|
||||
|
||||
c
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let language_codes = lm.all_languages().map(|(k, v)| {
|
||||
(
|
||||
format!("{} {}", lm.get(k, "flag"), v.to_title_case()),
|
||||
format!("`$lang {}`", k.to_uppercase()),
|
||||
true,
|
||||
}),
|
||||
)
|
||||
});
|
||||
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.send_message(&ctx, |m| {
|
||||
m.embed(|e| {
|
||||
e.title(lm.get(&user_data.language, "lang/select_title"))
|
||||
.color(*THEME_COLOR)
|
||||
.description(lm.get(&user_data.language, "lang/select"))
|
||||
.fields(language_codes)
|
||||
})
|
||||
.components(|c| {
|
||||
for row in lm
|
||||
.all_languages()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect::<Vec<(String, String)>>()
|
||||
.as_slice()
|
||||
.chunks(5)
|
||||
{
|
||||
let mut action_row = CreateActionRow::default();
|
||||
for (code, name) in row {
|
||||
action_row.create_button(|b| {
|
||||
b.style(ButtonStyle::Primary)
|
||||
.label(name.to_title_case())
|
||||
.custom_id(format!("lang:{}", code.to_uppercase()))
|
||||
});
|
||||
}
|
||||
|
||||
c.add_action_row(action_row);
|
||||
}
|
||||
|
||||
c
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
#[command]
|
||||
#[command("prefix")]
|
||||
#[description("Configure a prefix for text-based commands (deprecated)")]
|
||||
#[supports_dm(false)]
|
||||
#[permission_level(Restricted)]
|
||||
async fn prefix(ctx: &Context, msg: &Message, args: String) {
|
||||
let (pool, lm) = get_ctx_data(&ctx).await;
|
||||
#[required_permissions(Restricted)]
|
||||
async fn prefix(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: String) {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
|
||||
let guild_data = ctx.guild_data(msg.guild_id.unwrap()).await.unwrap();
|
||||
let language = UserData::language_of(&msg.author, &pool).await;
|
||||
let guild_data = ctx.guild_data(invoke.guild_id().unwrap()).await.unwrap();
|
||||
|
||||
if args.len() > 5 {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "prefix/too_long"))
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().content("Please select a prefix under 5 characters"),
|
||||
)
|
||||
.await;
|
||||
} else if args.is_empty() {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "prefix/no_argument"))
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content("Please use this command as `@reminder-bot prefix <prefix>`"),
|
||||
)
|
||||
.await;
|
||||
} else {
|
||||
guild_data.write().await.prefix = args;
|
||||
|
||||
guild_data.read().await.commit_changes(&pool).await;
|
||||
|
||||
let content = lm.get(&language, "prefix/success").replacen(
|
||||
"{prefix}",
|
||||
&guild_data.read().await.prefix,
|
||||
1,
|
||||
);
|
||||
|
||||
let _ = msg.channel_id.say(&ctx, content).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[command]
|
||||
#[supports_dm(false)]
|
||||
#[permission_level(Restricted)]
|
||||
async fn restrict(ctx: &Context, msg: &Message, args: String) {
|
||||
let (pool, lm) = get_ctx_data(&ctx).await;
|
||||
|
||||
let language = UserData::language_of(&msg.author, &pool).await;
|
||||
let guild_data = GuildData::from_guild(msg.guild(&ctx).unwrap(), &pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let role_tag_match = REGEX_ROLE.find(&args);
|
||||
|
||||
if let Some(role_tag) = role_tag_match {
|
||||
let commands = REGEX_COMMANDS
|
||||
.find_iter(&args.to_lowercase())
|
||||
.map(|c| c.as_str().to_string())
|
||||
.collect::<Vec<String>>();
|
||||
let role_id = RoleId(
|
||||
role_tag.as_str()[3..role_tag.as_str().len() - 1]
|
||||
.parse::<u64>()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let role_opt = role_id.to_role_cached(&ctx);
|
||||
|
||||
if let Some(role) = role_opt {
|
||||
let _ = sqlx::query!(
|
||||
"
|
||||
DELETE FROM command_restrictions WHERE role_id = (SELECT id FROM roles WHERE role = ?)
|
||||
",
|
||||
role.id.as_u64()
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content(format!("Prefix changed to {}", guild_data.read().await.prefix)),
|
||||
)
|
||||
.execute(&pool)
|
||||
.await;
|
||||
|
||||
if commands.is_empty() {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "restrict/disabled"))
|
||||
.await;
|
||||
} else {
|
||||
let _ = sqlx::query!(
|
||||
"
|
||||
INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, ?, ?)
|
||||
",
|
||||
role.id.as_u64(),
|
||||
role.name,
|
||||
guild_data.id
|
||||
)
|
||||
.execute(&pool)
|
||||
.await;
|
||||
|
||||
for command in commands {
|
||||
let res = sqlx::query!(
|
||||
"
|
||||
INSERT INTO command_restrictions (role_id, command) VALUES ((SELECT id FROM roles WHERE role = ?), ?)
|
||||
", role.id.as_u64(), command)
|
||||
.execute(&pool)
|
||||
.await;
|
||||
|
||||
if res.is_err() {
|
||||
println!("{:?}", res);
|
||||
|
||||
let content = lm.get(&language, "restrict/failure").replacen(
|
||||
"{command}",
|
||||
&command,
|
||||
1,
|
||||
);
|
||||
|
||||
let _ = msg.channel_id.say(&ctx, content).await;
|
||||
}
|
||||
}
|
||||
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "restrict/enabled"))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
} else if args.is_empty() {
|
||||
let guild_id = msg.guild_id.unwrap().as_u64().to_owned();
|
||||
|
||||
let rows = sqlx::query!(
|
||||
"
|
||||
SELECT
|
||||
roles.role, command_restrictions.command
|
||||
FROM
|
||||
command_restrictions
|
||||
INNER JOIN
|
||||
roles
|
||||
ON
|
||||
roles.id = command_restrictions.role_id
|
||||
WHERE
|
||||
roles.guild_id = (SELECT id FROM guilds WHERE guild = ?)
|
||||
",
|
||||
guild_id
|
||||
)
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut commands_roles: HashMap<&str, Vec<String>> = HashMap::new();
|
||||
|
||||
rows.iter().for_each(|row| {
|
||||
if let Some(vec) = commands_roles.get_mut(&row.command.as_str()) {
|
||||
vec.push(format!("<@&{}>", row.role));
|
||||
} else {
|
||||
commands_roles.insert(&row.command, vec![format!("<@&{}>", row.role)]);
|
||||
}
|
||||
});
|
||||
|
||||
let fields = commands_roles
|
||||
.iter()
|
||||
.map(|(key, value)| (key.to_title_case(), value.join("\n"), true));
|
||||
|
||||
let title = lm.get(&language, "restrict/title");
|
||||
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.send_message(&ctx, |m| {
|
||||
m.embed(|e| e.title(title).fields(fields).color(*THEME_COLOR))
|
||||
})
|
||||
.await;
|
||||
} else {
|
||||
let prefix = ctx.prefix(msg.guild_id).await;
|
||||
|
||||
command_help(ctx, msg, lm, &prefix, &language, "restrict").await;
|
||||
}
|
||||
}
|
||||
|
||||
#[command("restrict")]
|
||||
#[description("Configure which roles can use commands on the bot")]
|
||||
#[arg(
|
||||
name = "role",
|
||||
description = "The role to configure command permissions for",
|
||||
kind = "Role",
|
||||
required = true
|
||||
)]
|
||||
#[supports_dm(false)]
|
||||
#[required_permissions(Restricted)]
|
||||
async fn restrict(
|
||||
ctx: &Context,
|
||||
invoke: &(dyn CommandInvoke + Send + Sync),
|
||||
args: HashMap<String, String>,
|
||||
) {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
let framework = ctx.data.read().await.get::<RegexFramework>().cloned().unwrap();
|
||||
|
||||
let role = RoleId(args.get("role").unwrap().parse::<u64>().unwrap());
|
||||
|
||||
let restricted_commands =
|
||||
sqlx::query!("SELECT command FROM command_restrictions WHERE role_id = ?", role.0)
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|row| row.command.clone())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let restrictable_commands = framework
|
||||
.commands
|
||||
.iter()
|
||||
.filter(|c| c.required_permissions == PermissionLevel::Managed)
|
||||
.map(|c| c.names[0].to_string())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let len = restrictable_commands.len();
|
||||
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content(format!("Select the commands to allow to {} from below:", role.mention()))
|
||||
.components(|c| {
|
||||
c.create_action_row(|row| {
|
||||
row.create_select_menu(|select| {
|
||||
select
|
||||
.custom_id("test_id")
|
||||
.options(|options| {
|
||||
for command in restrictable_commands {
|
||||
options.create_option(|opt| {
|
||||
opt.label(&command).value(&command).default_selection(
|
||||
restricted_commands.contains(&command),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
options
|
||||
})
|
||||
.min_values(0)
|
||||
.max_values(len as u64)
|
||||
})
|
||||
})
|
||||
}),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/*
|
||||
#[command("alias")]
|
||||
#[supports_dm(false)]
|
||||
#[permission_level(Managed)]
|
||||
@ -638,3 +423,4 @@ SELECT command FROM command_aliases WHERE guild_id = (SELECT id FROM guilds WHER
|
||||
command_help(ctx, msg, lm, &prefix, &language, "alias").await;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
default::Default,
|
||||
string::ToString,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
@ -13,13 +14,12 @@ use serenity::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
check_subscription_on_message, command_help,
|
||||
check_subscription_on_message,
|
||||
consts::{
|
||||
REGEX_CHANNEL_USER, REGEX_NATURAL_COMMAND_1, REGEX_NATURAL_COMMAND_2, REGEX_REMIND_COMMAND,
|
||||
THEME_COLOR,
|
||||
},
|
||||
framework::SendIterator,
|
||||
get_ctx_data,
|
||||
framework::{CommandInvoke, CreateGenericResponse},
|
||||
models::{
|
||||
channel_data::ChannelData,
|
||||
guild_data::GuildData,
|
||||
@ -34,44 +34,35 @@ use crate::{
|
||||
CtxData,
|
||||
},
|
||||
time_parser::{natural_parser, TimeParser},
|
||||
SQLPool,
|
||||
};
|
||||
|
||||
#[command]
|
||||
#[command("pause")]
|
||||
#[description("Pause all reminders on the current channel until a certain time or indefinitely")]
|
||||
#[arg(
|
||||
name = "until",
|
||||
description = "When to pause until (hint: try 'next Wednesday', or '10 minutes')",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[supports_dm(false)]
|
||||
#[permission_level(Restricted)]
|
||||
async fn pause(ctx: &Context, msg: &Message, args: String) {
|
||||
let (pool, lm) = get_ctx_data(&ctx).await;
|
||||
#[required_permissions(Restricted)]
|
||||
async fn pause(
|
||||
ctx: &Context,
|
||||
invoke: &(dyn CommandInvoke + Send + Sync),
|
||||
args: HashMap<String, String>,
|
||||
) {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
|
||||
let language = UserData::language_of(&msg.author, &pool).await;
|
||||
let timezone = UserData::timezone_of(&msg.author, &pool).await;
|
||||
let timezone = UserData::timezone_of(&invoke.author_id(), &pool).await;
|
||||
|
||||
let mut channel = ChannelData::from_channel(msg.channel(&ctx).await.unwrap(), &pool)
|
||||
.await
|
||||
.unwrap();
|
||||
let mut channel = ctx.channel_data(invoke.channel_id()).await.unwrap();
|
||||
|
||||
if args.is_empty() {
|
||||
channel.paused = !channel.paused;
|
||||
channel.paused_until = None;
|
||||
match args.get("until") {
|
||||
Some(until) => {
|
||||
let parsed = natural_parser(until, &timezone.to_string()).await;
|
||||
|
||||
channel.commit_changes(&pool).await;
|
||||
|
||||
if channel.paused {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "pause/paused_indefinite"))
|
||||
.await;
|
||||
} else {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "pause/unpaused"))
|
||||
.await;
|
||||
}
|
||||
} else {
|
||||
let parser = TimeParser::new(&args, timezone);
|
||||
let pause_until = parser.timestamp();
|
||||
|
||||
match pause_until {
|
||||
Ok(timestamp) => {
|
||||
if let Some(timestamp) = parsed {
|
||||
let dt = NaiveDateTime::from_timestamp(timestamp, 0);
|
||||
|
||||
channel.paused = true;
|
||||
@ -79,23 +70,53 @@ async fn pause(ctx: &Context, msg: &Message, args: String) {
|
||||
|
||||
channel.commit_changes(&pool).await;
|
||||
|
||||
let content = lm
|
||||
.get(&language, "pause/paused_until")
|
||||
.replace("{}", &format!("<t:{}:D>", timestamp));
|
||||
|
||||
let _ = msg.channel_id.say(&ctx, content).await;
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().content(format!(
|
||||
"Reminders in this channel have been silenced until **<t:{}:D>**",
|
||||
timestamp
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
} else {
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content("Time could not be processed. Please write the time as clearly as possible"),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
channel.paused = !channel.paused;
|
||||
channel.paused_until = None;
|
||||
|
||||
Err(_) => {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "pause/invalid_time"))
|
||||
channel.commit_changes(&pool).await;
|
||||
|
||||
if channel.paused {
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content("Reminders in this channel have been silenced indefinitely"),
|
||||
)
|
||||
.await;
|
||||
} else {
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content("Reminders in this channel have been unsilenced"),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[command]
|
||||
#[permission_level(Restricted)]
|
||||
async fn offset(ctx: &Context, msg: &Message, args: String) {
|
||||
@ -150,10 +171,8 @@ UPDATE reminders SET `utc_time` = `utc_time` + ? WHERE reminders.channel_id = ?
|
||||
|
||||
let _ = msg.channel_id.say(&ctx, response).await;
|
||||
} else {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&user_data.language, "offset/invalid_time"))
|
||||
.await;
|
||||
let _ =
|
||||
msg.channel_id.say(&ctx, lm.get(&user_data.language, "offset/invalid_time")).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -166,9 +185,8 @@ async fn nudge(ctx: &Context, msg: &Message, args: String) {
|
||||
let language = UserData::language_of(&msg.author, &pool).await;
|
||||
let timezone = UserData::timezone_of(&msg.author, &pool).await;
|
||||
|
||||
let mut channel = ChannelData::from_channel(msg.channel(&ctx).await.unwrap(), &pool)
|
||||
.await
|
||||
.unwrap();
|
||||
let mut channel =
|
||||
ChannelData::from_channel(msg.channel(&ctx).await.unwrap(), &pool).await.unwrap();
|
||||
|
||||
if args.is_empty() {
|
||||
let content = lm
|
||||
@ -183,10 +201,7 @@ async fn nudge(ctx: &Context, msg: &Message, args: String) {
|
||||
match nudge_time {
|
||||
Ok(displacement) => {
|
||||
if displacement < i16::MIN as i64 || displacement > i16::MAX as i64 {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "nudge/invalid_time"))
|
||||
.await;
|
||||
let _ = msg.channel_id.say(&ctx, lm.get(&language, "nudge/invalid_time")).await;
|
||||
} else {
|
||||
channel.nudge = displacement as i16;
|
||||
|
||||
@ -203,10 +218,7 @@ async fn nudge(ctx: &Context, msg: &Message, args: String) {
|
||||
}
|
||||
|
||||
Err(_) => {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "nudge/invalid_time"))
|
||||
.await;
|
||||
let _ = msg.channel_id.say(&ctx, lm.get(&language, "nudge/invalid_time")).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -236,14 +248,9 @@ async fn look(ctx: &Context, msg: &Message, args: String) {
|
||||
let reminders = Reminder::from_channel(ctx, channel_id, &flags).await;
|
||||
|
||||
if reminders.is_empty() {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, "No reminders on specified channel")
|
||||
.await;
|
||||
let _ = msg.channel_id.say(&ctx, "No reminders on specified channel").await;
|
||||
} else {
|
||||
let display = reminders
|
||||
.iter()
|
||||
.map(|reminder| reminder.display(&flags, &timezone));
|
||||
let display = reminders.iter().map(|reminder| reminder.display(&flags, &timezone));
|
||||
|
||||
let _ = msg.channel_id.say_lines(&ctx, display).await;
|
||||
}
|
||||
@ -256,10 +263,7 @@ async fn delete(ctx: &Context, msg: &Message, _args: String) {
|
||||
|
||||
let user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap();
|
||||
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&user_data.language, "del/listing"))
|
||||
.await;
|
||||
let _ = msg.channel_id.say(&ctx, lm.get(&user_data.language, "del/listing")).await;
|
||||
|
||||
let mut reminder_ids: Vec<u32> = vec![];
|
||||
|
||||
@ -278,23 +282,13 @@ async fn delete(ctx: &Context, msg: &Message, _args: String) {
|
||||
});
|
||||
|
||||
let _ = msg.channel_id.say_lines(&ctx, enumerated_reminders).await;
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&user_data.language, "del/listed"))
|
||||
.await;
|
||||
let _ = msg.channel_id.say(&ctx, lm.get(&user_data.language, "del/listed")).await;
|
||||
|
||||
let reply = msg
|
||||
.channel_id
|
||||
.await_reply(&ctx)
|
||||
.author_id(msg.author.id)
|
||||
.channel_id(msg.channel_id)
|
||||
.await;
|
||||
let reply =
|
||||
msg.channel_id.await_reply(&ctx).author_id(msg.author.id).channel_id(msg.channel_id).await;
|
||||
|
||||
if let Some(content) = reply.map(|m| m.content.replace(",", " ")) {
|
||||
let parts = content
|
||||
.split(' ')
|
||||
.filter(|i| !i.is_empty())
|
||||
.collect::<Vec<&str>>();
|
||||
let parts = content.split(' ').filter(|i| !i.is_empty()).collect::<Vec<&str>>();
|
||||
|
||||
let valid_parts = parts
|
||||
.iter()
|
||||
@ -352,9 +346,7 @@ INSERT INTO events (event_name, bulk_count, guild_id, user_id) VALUES ('delete',
|
||||
|
||||
let _ = msg.channel_id.say(&ctx, content).await;
|
||||
} else {
|
||||
let content = lm
|
||||
.get(&user_data.language, "del/count")
|
||||
.replacen("{}", "0", 1);
|
||||
let content = lm.get(&user_data.language, "del/count").replacen("{}", "0", 1);
|
||||
|
||||
let _ = msg.channel_id.say(&ctx, content).await;
|
||||
}
|
||||
@ -365,10 +357,7 @@ INSERT INTO events (event_name, bulk_count, guild_id, user_id) VALUES ('delete',
|
||||
#[permission_level(Managed)]
|
||||
async fn timer(ctx: &Context, msg: &Message, args: String) {
|
||||
fn time_difference(start_time: NaiveDateTime) -> String {
|
||||
let unix_time = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs() as i64;
|
||||
let unix_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64;
|
||||
let now = NaiveDateTime::from_timestamp(unix_time, 0);
|
||||
|
||||
let delta = (now - start_time).num_seconds();
|
||||
@ -415,10 +404,7 @@ async fn timer(ctx: &Context, msg: &Message, args: String) {
|
||||
let count = Timer::count_from_owner(owner, &pool).await;
|
||||
|
||||
if count >= 25 {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "timer/limit"))
|
||||
.await;
|
||||
let _ = msg.channel_id.say(&ctx, lm.get(&language, "timer/limit")).await;
|
||||
} else {
|
||||
let name = args_iter
|
||||
.next()
|
||||
@ -428,10 +414,7 @@ async fn timer(ctx: &Context, msg: &Message, args: String) {
|
||||
if name.len() <= 32 {
|
||||
Timer::create(&name, owner, &pool).await;
|
||||
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "timer/success"))
|
||||
.await;
|
||||
let _ = msg.channel_id.say(&ctx, lm.get(&language, "timer/success")).await;
|
||||
} else {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
@ -469,21 +452,12 @@ DELETE FROM timers WHERE owner = ? AND name = ?
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "timer/deleted"))
|
||||
.await;
|
||||
let _ = msg.channel_id.say(&ctx, lm.get(&language, "timer/deleted")).await;
|
||||
} else {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "timer/not_found"))
|
||||
.await;
|
||||
let _ = msg.channel_id.say(&ctx, lm.get(&language, "timer/not_found")).await;
|
||||
}
|
||||
} else {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(&ctx, lm.get(&language, "timer/help"))
|
||||
.await;
|
||||
let _ = msg.channel_id.say(&ctx, lm.get(&language, "timer/help")).await;
|
||||
}
|
||||
}
|
||||
|
||||
@ -547,9 +521,8 @@ async fn remind_command(ctx: &Context, msg: &Message, args: String, command: Rem
|
||||
|
||||
let time_parser = TimeParser::new(captures.name("time").unwrap().as_str(), timezone);
|
||||
|
||||
let expires_parser = captures
|
||||
.name("expires")
|
||||
.map(|mat| TimeParser::new(mat.as_str(), timezone));
|
||||
let expires_parser =
|
||||
captures.name("expires").map(|mat| TimeParser::new(mat.as_str(), timezone));
|
||||
|
||||
let interval_parser = captures
|
||||
.name("interval")
|
||||
@ -854,3 +827,4 @@ async fn natural(ctx: &Context, msg: &Message, args: String) {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
187
src/framework.rs
187
src/framework.rs