Updated serenity version and added new commands
+ Added task.rs to handle periodic tasks
This commit is contained in:
parent
8ec84ff946
commit
67ff69aca0
2093
Cargo.lock
generated
2093
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
34
Cargo.toml
34
Cargo.toml
@ -1,34 +1,36 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fren"
|
name = "fren"
|
||||||
version = "0.1.0"
|
version = "0.5.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
config = "0.13.2"
|
config = "0.13.4"
|
||||||
structopt = "0.3.26"
|
structopt = "0.3.26"
|
||||||
reqwest = "0.11.12"
|
reqwest = { version = "0.11.23", features = ["json"] }
|
||||||
serde = "1.0.147"
|
serde = "1.0.195"
|
||||||
toml = "0.7.3"
|
toml = "0.8.8"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
tera = "1.17.1"
|
tera = "1.19.1"
|
||||||
ndm = "0.9.9"
|
ndm = "0.9.10"
|
||||||
regex = "1.7.0"
|
regex = "1.10.2"
|
||||||
magick_rust = "0.18.0"
|
magick_rust = "0.19.1"
|
||||||
songbird = "0.3.2"
|
songbird = "0.4.0"
|
||||||
json = "0.12.4"
|
json = "0.12.4"
|
||||||
axum = "0.6.3"
|
axum = "0.6.3"
|
||||||
sha3 = "0.10.6"
|
axum-macros = "0.4.1"
|
||||||
base64 = "0.21.0"
|
sha3 = "0.10.8"
|
||||||
|
base64 = "0.21.7"
|
||||||
j_db = { version = "0.1.0", registry = "jojo-dev" }
|
j_db = { version = "0.1.0", registry = "jojo-dev" }
|
||||||
chrono = { version = "0.4.24", features = ["serde"] }
|
chrono = { version = "0.4.31", features = ["serde"] }
|
||||||
chrono-tz = "0.8.2"
|
chrono-tz = "0.8.5"
|
||||||
|
log = "0.4.20"
|
||||||
|
|
||||||
[dependencies.serenity]
|
[dependencies.serenity]
|
||||||
version = "0.11.5"
|
version = "0.12.0"
|
||||||
features = ["framework", "standard_framework", "rustls_backend"]
|
features = ["framework", "standard_framework", "rustls_backend"]
|
||||||
|
|
||||||
[dependencies.tokio]
|
[dependencies.tokio]
|
||||||
version = "1.0"
|
version = "1.35.1"
|
||||||
features = ["macros", "rt-multi-thread"]
|
features = ["macros", "rt-multi-thread"]
|
||||||
|
|||||||
@ -34,6 +34,7 @@ pub struct BotConfig {
|
|||||||
pub api_addr: SocketAddr,
|
pub api_addr: SocketAddr,
|
||||||
pub announcement_channel: ChannelId,
|
pub announcement_channel: ChannelId,
|
||||||
pub toys: Vec<String>,
|
pub toys: Vec<String>,
|
||||||
|
pub effect_role_duration: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BotConfig {
|
impl BotConfig {
|
||||||
|
|||||||
@ -1,20 +1,31 @@
|
|||||||
use crate::config::BotConfig;
|
use crate::config::BotConfig;
|
||||||
|
use crate::inventory::ItemType;
|
||||||
use crate::models::api_key::Apikey;
|
use crate::models::api_key::Apikey;
|
||||||
use crate::models::lil_fren::{
|
use crate::models::lil_fren::{
|
||||||
draw_dancing, draw_frankenstein, draw_gone, draw_magic, draw_mining, draw_resonance_cascade,
|
draw_dancing, draw_frankenstein, draw_gone, draw_magic, draw_mining, draw_resonance_cascade,
|
||||||
draw_sick, draw_sleep, draw_standing, draw_tax_fraud, AliveState, LilFren,
|
draw_sick, draw_sleep, draw_standing, draw_tax_fraud, AliveState, LilFren,
|
||||||
};
|
};
|
||||||
|
use crate::user::User;
|
||||||
use crate::{command, group, GlobalData};
|
use crate::{command, group, GlobalData};
|
||||||
use json::JsonValue;
|
use json::JsonValue;
|
||||||
|
use serenity::all::{CreateAttachment, CreateMessage};
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::framework::standard::{Args, CommandResult};
|
use serenity::framework::standard::{Args, CommandResult};
|
||||||
use serenity::model::channel::{AttachmentType, Message};
|
use serenity::model::channel::Message;
|
||||||
use serenity::model::misc::EmojiIdentifier;
|
use serenity::model::misc::EmojiIdentifier;
|
||||||
use serenity::model::prelude::UserId;
|
use serenity::model::prelude::UserId;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
#[group]
|
#[group]
|
||||||
#[commands(reload, dump_db, load_db, add_key, debug_buddy, draw_buddy_states)]
|
#[commands(
|
||||||
|
reload,
|
||||||
|
dump_db,
|
||||||
|
load_db,
|
||||||
|
add_key,
|
||||||
|
debug_buddy,
|
||||||
|
draw_buddy_states,
|
||||||
|
op_give
|
||||||
|
)]
|
||||||
pub struct ADMIN;
|
pub struct ADMIN;
|
||||||
|
|
||||||
pub fn is_admin(user_id: &UserId, cfg: &BotConfig) -> bool {
|
pub fn is_admin(user_id: &UserId, cfg: &BotConfig) -> bool {
|
||||||
@ -53,13 +64,15 @@ async fn dump_db(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
|||||||
.id
|
.id
|
||||||
.create_dm_channel(&ctx.http)
|
.create_dm_channel(&ctx.http)
|
||||||
.await?
|
.await?
|
||||||
.send_message(&ctx.http, |m| {
|
.send_message(
|
||||||
m.content("The current DB state")
|
&ctx.http,
|
||||||
.add_file(AttachmentType::Bytes {
|
CreateMessage::new()
|
||||||
data: Cow::from(output.as_bytes()),
|
.content("The current DB state")
|
||||||
filename: "db.json".to_string(),
|
.add_file(CreateAttachment::bytes(
|
||||||
})
|
Cow::from(output.as_bytes()),
|
||||||
})
|
"db.json".to_string(),
|
||||||
|
)),
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -186,3 +199,36 @@ async fn draw_buddy_states(ctx: &Context, msg: &Message, args: Args) -> CommandR
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
#[description("Hey can I get op? I'm from planet minecraft")]
|
||||||
|
async fn op_give(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
|
let mut data = ctx.data.write().await;
|
||||||
|
let global_data = data.get_mut::<GlobalData>().unwrap();
|
||||||
|
|
||||||
|
if args.is_empty() {
|
||||||
|
msg.reply(&ctx.http, "You need to select an item to give")
|
||||||
|
.await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg_content = args.rest().to_lowercase();
|
||||||
|
let item = match msg_content.parse::<ItemType>() {
|
||||||
|
Ok(i) => i,
|
||||||
|
Err(_) => {
|
||||||
|
msg.reply(&ctx.http, "I don't know what the heck that is tbh.")
|
||||||
|
.await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
User::give_item(&global_data.db, msg.author.id, item, 1, None)?;
|
||||||
|
|
||||||
|
msg.reply(
|
||||||
|
&ctx,
|
||||||
|
format!("[Console] Op has given {} 1 {}", msg.author.name, item),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@ -44,13 +44,13 @@ pub async fn add_birthday(ctx: &Context, msg: &Message, args: Args) -> CommandRe
|
|||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let global_data = data.get::<GlobalData>().unwrap();
|
let global_data = data.get::<GlobalData>().unwrap();
|
||||||
|
|
||||||
BirthdayEntry::add_birthday(&global_data.db, msg.author.id.0, date)?;
|
BirthdayEntry::add_birthday(&global_data.db, msg.author.id.get(), date)?;
|
||||||
|
|
||||||
msg.reply(
|
msg.reply(
|
||||||
&ctx.http,
|
&ctx.http,
|
||||||
format!(
|
format!(
|
||||||
"Thank you subject #{}, I am now {}% closer to making a full AI replica of you.",
|
"Thank you subject #{}, I am now {}% closer to making a full AI replica of you.",
|
||||||
msg.author.id.0,
|
msg.author.id.get(),
|
||||||
thread_rng().gen_range(0.0..100.0)
|
thread_rng().gen_range(0.0..100.0)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -78,14 +78,16 @@ pub async fn list_birthdays(ctx: &Context, msg: &Message) -> CommandResult {
|
|||||||
|
|
||||||
msg_builder.push_bold_line("All the birthdays I know:");
|
msg_builder.push_bold_line("All the birthdays I know:");
|
||||||
for birthday in birthdays {
|
for birthday in birthdays {
|
||||||
let user = msg
|
let guild = msg.guild(&ctx.cache).unwrap();
|
||||||
.guild(&ctx.cache)
|
|
||||||
.unwrap()
|
let member = guild
|
||||||
.member(&ctx.http, UserId::from(birthday.discord_id))
|
.members
|
||||||
.await?;
|
.get(&UserId::from(birthday.discord_id))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
msg_builder.push_line(format!(
|
msg_builder.push_line(format!(
|
||||||
"* {} {}",
|
"* {} {}",
|
||||||
user.display_name(),
|
member.display_name(),
|
||||||
birthday.birthday.format("%m-%d-%Y")
|
birthday.birthday.format("%m-%d-%Y")
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
use crate::{command, group};
|
use crate::{command, group};
|
||||||
|
use serenity::builder::EditRole;
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::framework::standard::{Args, CommandResult};
|
use serenity::framework::standard::{Args, CommandResult};
|
||||||
use serenity::model::channel::Message;
|
use serenity::model::channel::Message;
|
||||||
use serenity::utils::Colour;
|
use serenity::model::Colour;
|
||||||
|
|
||||||
#[group]
|
#[group]
|
||||||
#[commands(set_color, remove_color)]
|
#[commands(set_color, remove_color)]
|
||||||
@ -16,13 +17,15 @@ pub struct Color;
|
|||||||
#[min_args(1)]
|
#[min_args(1)]
|
||||||
#[max_args(3)]
|
#[max_args(3)]
|
||||||
#[only_in(guilds)]
|
#[only_in(guilds)]
|
||||||
async fn set_color(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
async fn set_color(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
let color = if args.len() == 3 {
|
let color = if args.len() == 3 {
|
||||||
Colour::from_rgb(
|
let r = args.parse::<u8>()?;
|
||||||
args.parse::<u8>()?,
|
args.advance();
|
||||||
args.parse::<u8>()?,
|
let g = args.parse::<u8>()?;
|
||||||
args.parse::<u8>()?,
|
args.advance();
|
||||||
)
|
let b = args.parse::<u8>()?;
|
||||||
|
args.advance();
|
||||||
|
Colour::from_rgb(r, g, b)
|
||||||
} else {
|
} else {
|
||||||
let color_str = args.rest();
|
let color_str = args.rest();
|
||||||
|
|
||||||
@ -36,42 +39,38 @@ async fn set_color(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let guild = msg.guild(&ctx.cache).unwrap();
|
let role = if let Some(role) = msg.member.as_ref().unwrap().roles.iter().find(|r| {
|
||||||
|
ctx.cache
|
||||||
let (_, member) = guild
|
.role(msg.guild_id.unwrap(), **r)
|
||||||
.members
|
.unwrap()
|
||||||
.iter()
|
.name
|
||||||
.find(|(id, _)| id == &&msg.author.id)
|
.contains("COwOlor")
|
||||||
.unwrap();
|
}) {
|
||||||
|
*role
|
||||||
let role = if let Some(role) = member
|
|
||||||
.roles(&ctx.cache)
|
|
||||||
.unwrap_or_default()
|
|
||||||
.iter()
|
|
||||||
.find(|r| r.name.contains("COwOlor"))
|
|
||||||
{
|
|
||||||
role.clone()
|
|
||||||
} else {
|
} else {
|
||||||
guild
|
msg.guild_id
|
||||||
.create_role(&ctx.http, |r| {
|
.unwrap()
|
||||||
r.name(&format!("{} COwOlor", member.user.name))
|
.create_role(
|
||||||
})
|
&ctx.http,
|
||||||
|
EditRole::new().name(&format!("{} COwOlor", msg.author.name)),
|
||||||
|
)
|
||||||
.await?
|
.await?
|
||||||
|
.id
|
||||||
};
|
};
|
||||||
|
|
||||||
guild
|
msg.guild_id
|
||||||
.edit_role(&ctx.http, role.id, |r| {
|
.unwrap()
|
||||||
r.position(255).hoist(false).colour(color.0 as u64)
|
.edit_role(&ctx.http, role, EditRole::new().colour(color))
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let len = guild.roles.len() - 1;
|
msg.guild_id
|
||||||
guild
|
.unwrap()
|
||||||
.edit_role_position(&ctx.http, role.id, len as u64)
|
.member(&ctx.http, msg.author.id)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.add_role(&ctx.http, role)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
member.clone().add_role(&ctx.http, role).await?;
|
|
||||||
|
|
||||||
msg.reply(&ctx.http, "Color set!").await?;
|
msg.reply(&ctx.http, "Color set!").await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -82,21 +81,14 @@ async fn set_color(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
#[only_in(guilds)]
|
#[only_in(guilds)]
|
||||||
#[description("Remove your name color.")]
|
#[description("Remove your name color.")]
|
||||||
async fn remove_color(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
async fn remove_color(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
||||||
let guild = msg.guild(&ctx.cache).unwrap();
|
if let Some(role) = msg.member.as_ref().unwrap().roles.iter().find(|r| {
|
||||||
|
ctx.cache
|
||||||
let (_, member) = guild
|
.role(msg.guild_id.unwrap(), **r)
|
||||||
.members
|
.unwrap()
|
||||||
.iter()
|
.name
|
||||||
.find(|(id, _)| id == &&msg.author.id)
|
.contains("COwOlor")
|
||||||
.unwrap();
|
}) {
|
||||||
|
msg.guild_id.unwrap().delete_role(&ctx.http, role).await?;
|
||||||
if let Some(role) = member
|
|
||||||
.roles(&ctx.cache)
|
|
||||||
.unwrap_or_default()
|
|
||||||
.iter()
|
|
||||||
.find(|r| r.name.contains("COwOlor"))
|
|
||||||
{
|
|
||||||
role.clone().delete(&ctx.http).await?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.reply(&ctx.http, "Color removed!").await?;
|
msg.reply(&ctx.http, "Color removed!").await?;
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use crate::user::{User, UserError};
|
|||||||
use crate::{command, group};
|
use crate::{command, group};
|
||||||
use rand::seq::IteratorRandom;
|
use rand::seq::IteratorRandom;
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
|
use serenity::builder::EditMessage;
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::framework::standard::{Args, CommandResult};
|
use serenity::framework::standard::{Args, CommandResult};
|
||||||
use serenity::model::id::{EmojiId, UserId};
|
use serenity::model::id::{EmojiId, UserId};
|
||||||
@ -77,7 +78,7 @@ impl Eq for Racer {}
|
|||||||
|
|
||||||
impl Racer {
|
impl Racer {
|
||||||
pub fn new(emoji: Emoji) -> Self {
|
pub fn new(emoji: Emoji) -> Self {
|
||||||
let genetic_stat = (emoji.id.0 & 0xff) as f32 / 255.0;
|
let genetic_stat = (emoji.id.get() & 0xff) as f32 / 255.0;
|
||||||
let random_stat = thread_rng().gen_range(0.0..0.5);
|
let random_stat = thread_rng().gen_range(0.0..0.5);
|
||||||
let speed = BASE_SPEED + (0.50 * genetic_stat) + (0.50 * random_stat);
|
let speed = BASE_SPEED + (0.50 * genetic_stat) + (0.50 * random_stat);
|
||||||
|
|
||||||
@ -169,19 +170,17 @@ async fn race(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let guild = msg.guild(&ctx.cache).unwrap();
|
let guild = msg.guild_id.unwrap();
|
||||||
let channels = guild.channels(&ctx.http).await?;
|
let channels = guild.channels(&ctx.http).await?;
|
||||||
let race_channel = channels.get(&msg.channel_id).unwrap();
|
let race_channel = channels.get(&msg.channel_id).unwrap();
|
||||||
|
|
||||||
let racers: Vec<_> = guild
|
let emojis = guild.emojis(&ctx.http).await.unwrap();
|
||||||
.emojis
|
|
||||||
|
let racers: Vec<_> = emojis
|
||||||
.iter()
|
.iter()
|
||||||
.choose_multiple(&mut thread_rng(), NUMBER_OF_RACERS);
|
.choose_multiple(&mut thread_rng(), NUMBER_OF_RACERS);
|
||||||
|
|
||||||
let mut racers: Vec<Racer> = racers
|
let mut racers: Vec<Racer> = racers.iter().map(|e| Racer::new((*e).clone())).collect();
|
||||||
.iter()
|
|
||||||
.map(|(_, e)| Racer::new((*e).clone()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut racers_msg = MessageBuilder::new();
|
let mut racers_msg = MessageBuilder::new();
|
||||||
racers_msg.push_bold_line("Welcome to the Emoji Races, the racers today are:");
|
racers_msg.push_bold_line("Welcome to the Emoji Races, the racers today are:");
|
||||||
@ -193,18 +192,28 @@ async fn race(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
|||||||
|
|
||||||
let mut bets: Vec<Bet> = Vec::new();
|
let mut bets: Vec<Bet> = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
let msg = recv.recv().await.unwrap();
|
let race_msg = recv.recv().await.unwrap();
|
||||||
|
|
||||||
match msg {
|
match race_msg {
|
||||||
RaceMessage::StartRace => {
|
RaceMessage::StartRace => {
|
||||||
race_channel.say(&ctx, "And they're off!").await?;
|
race_channel.say(&ctx, "And they're off!").await?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
RaceMessage::Bet(bet) => {
|
RaceMessage::Bet(bet) => {
|
||||||
|
let emoji = match msg.guild_id.unwrap().emoji(&ctx.http, bet.emoji).await {
|
||||||
|
Ok(emoji) => emoji,
|
||||||
|
Err(_) => {
|
||||||
|
race_channel
|
||||||
|
.say(&ctx, "That's a weird looking racer, pick a normal one!")
|
||||||
|
.await?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if let Err(e) = add_bet(ctx, bet.clone(), &mut bets, &racers).await {
|
if let Err(e) = add_bet(ctx, bet.clone(), &mut bets, &racers).await {
|
||||||
let msg = match e {
|
let msg = match e {
|
||||||
RaceError::RacerNotFound => {
|
RaceError::RacerNotFound => {
|
||||||
format!("{} is not in this race!", bet.emoji.mention())
|
format!("{} is not in this race!", emoji)
|
||||||
}
|
}
|
||||||
RaceError::BetFundError(_) => {
|
RaceError::BetFundError(_) => {
|
||||||
"Only rich people can bet in this race, sorry".to_string()
|
"Only rich people can bet in this race, sorry".to_string()
|
||||||
@ -225,7 +234,7 @@ async fn race(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
|||||||
"{} has put {} on {}",
|
"{} has put {} on {}",
|
||||||
bet.author.mention(),
|
bet.author.mention(),
|
||||||
bet.amount,
|
bet.amount,
|
||||||
bet.emoji.mention()
|
emoji,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@ -243,7 +252,7 @@ async fn race(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
race_msg
|
race_msg
|
||||||
.edit(&ctx.http, |m| m.content(draw_race(&racers)))
|
.edit(&ctx.http, EditMessage::new().content(draw_race(&racers)))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
for racer in &racers {
|
for racer in &racers {
|
||||||
|
|||||||
@ -28,12 +28,7 @@ async fn balance(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
&ctx.http,
|
&ctx.http,
|
||||||
format!(
|
format!(
|
||||||
"{}'s current balance is {} fren coins!",
|
"{}'s current balance is {} fren coins!",
|
||||||
msg.guild(&ctx.cache)
|
user.mention(),
|
||||||
.unwrap()
|
|
||||||
.member(&ctx.http, user)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.mention(),
|
|
||||||
wallet.coin_count
|
wallet.coin_count
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -80,16 +75,11 @@ async fn gift(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let target_user = msg
|
|
||||||
.guild(&ctx.cache)
|
|
||||||
.unwrap()
|
|
||||||
.member(&ctx.http, target)
|
|
||||||
.await?;
|
|
||||||
msg.reply(
|
msg.reply(
|
||||||
&ctx.http,
|
&ctx.http,
|
||||||
format!(
|
format!(
|
||||||
"You have gifted {} {} fren coins!",
|
"You have gifted {} {} fren coins!",
|
||||||
target_user.mention(),
|
target.mention(),
|
||||||
amount
|
amount
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use rand::prelude::SliceRandom;
|
|||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serenity::builder::EditMessage;
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::constants::MESSAGE_CODE_LIMIT;
|
use serenity::constants::MESSAGE_CODE_LIMIT;
|
||||||
use serenity::framework::standard::{Args, CommandResult};
|
use serenity::framework::standard::{Args, CommandResult};
|
||||||
@ -107,13 +108,14 @@ pub async fn random(ctx: &Context, msg: &Message, random_name: &str) -> CommandR
|
|||||||
};
|
};
|
||||||
|
|
||||||
let guild_member = msg
|
let guild_member = msg
|
||||||
.guild(&ctx.cache)
|
.guild_id
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.member(&ctx.http, msg.author.id)
|
.member(&ctx.http, msg.author.id)
|
||||||
.await?;
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let reply = render_random(
|
let reply = render_random(
|
||||||
&guild_member.display_name(),
|
guild_member.display_name(),
|
||||||
global_data,
|
global_data,
|
||||||
response_template_str,
|
response_template_str,
|
||||||
)?;
|
)?;
|
||||||
@ -248,13 +250,14 @@ async fn bad_apple(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
|||||||
|
|
||||||
if (idx % 10) == 0 {
|
if (idx % 10) == 0 {
|
||||||
bad_apple_msg
|
bad_apple_msg
|
||||||
.edit(&ctx.http, |m| {
|
.edit(
|
||||||
m.content(
|
&ctx.http,
|
||||||
|
EditMessage::new().content(
|
||||||
MessageBuilder::default()
|
MessageBuilder::default()
|
||||||
.push_codeblock_safe(frame, None)
|
.push_codeblock_safe(frame, None)
|
||||||
.build(),
|
.build(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,26 +15,24 @@ pub mod voices;
|
|||||||
use crate::api::web_server;
|
use crate::api::web_server;
|
||||||
use crate::discord::fren_coin::give_coin;
|
use crate::discord::fren_coin::give_coin;
|
||||||
use crate::discord::joke::random;
|
use crate::discord::joke::random;
|
||||||
use crate::discord::shop::restock_shop;
|
|
||||||
use crate::models::birthday::BirthdayEntry;
|
|
||||||
use crate::models::insult_compliment::{RandomResponseTemplate, ResponseType};
|
|
||||||
use crate::models::lil_fren::lil_fren_task;
|
use crate::models::lil_fren::lil_fren_task;
|
||||||
|
use crate::models::task::Task;
|
||||||
use crate::{help, hook, GlobalData};
|
use crate::{help, hook, GlobalData};
|
||||||
use chrono::{Days, TimeZone, Timelike, Utc};
|
|
||||||
use rand::prelude::IteratorRandom;
|
use rand::prelude::IteratorRandom;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
|
use serenity::all::{ActivityData, Http, RoleId};
|
||||||
use serenity::async_trait;
|
use serenity::async_trait;
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::framework::standard::{
|
use serenity::framework::standard::{
|
||||||
help_commands, Args, CommandGroup, CommandResult, HelpOptions,
|
help_commands, Args, CommandGroup, CommandResult, HelpOptions,
|
||||||
};
|
};
|
||||||
use serenity::model::channel::{Message, ReactionType};
|
use serenity::model::channel::{Message, ReactionType};
|
||||||
use serenity::model::gateway::Activity;
|
|
||||||
use serenity::model::id::UserId;
|
use serenity::model::id::UserId;
|
||||||
use serenity::model::prelude::{GuildId, OnlineStatus, Ready};
|
use serenity::model::prelude::{GuildId, OnlineStatus, Ready};
|
||||||
use serenity::prelude::{EventHandler, Mentionable};
|
use serenity::prelude::EventHandler;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use tokio::time::sleep;
|
||||||
|
|
||||||
pub struct Handler;
|
pub struct Handler;
|
||||||
|
|
||||||
@ -52,72 +50,10 @@ impl EventHandler for Handler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut next_check = chrono_tz::America::Chicago
|
Task::create_reoccurring_tasks(&ctx).await.unwrap();
|
||||||
.from_utc_datetime(&Utc::now().naive_utc())
|
|
||||||
.checked_sub_days(Days::new(1))
|
|
||||||
.unwrap();
|
|
||||||
loop {
|
loop {
|
||||||
{
|
Task::run_tasks(&ctx).await.unwrap();
|
||||||
println!("Restocking shop...");
|
sleep(Duration::from_secs(5)).await;
|
||||||
restock_shop(&ctx).await.unwrap();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let now =
|
|
||||||
chrono_tz::America::Chicago.from_utc_datetime(&Utc::now().naive_utc());
|
|
||||||
if now >= next_check {
|
|
||||||
let data = ctx.data.read().await;
|
|
||||||
let global_data = data.get::<GlobalData>().unwrap();
|
|
||||||
|
|
||||||
let todays_birthdays =
|
|
||||||
BirthdayEntry::todays_birthdays(&global_data.db, now.date_naive())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
for birth in todays_birthdays {
|
|
||||||
if let Ok(user) = global_data
|
|
||||||
.cfg
|
|
||||||
.guild_id
|
|
||||||
.member(&ctx.http, birth.discord_id)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
global_data
|
|
||||||
.cfg
|
|
||||||
.announcement_channel
|
|
||||||
.say(&ctx.http, format!("Happy birthday {}!", user.mention()))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let compliment = RandomResponseTemplate::get_random_response(
|
|
||||||
&global_data.db,
|
|
||||||
ResponseType::Compliment,
|
|
||||||
user.display_name().as_str(),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
global_data
|
|
||||||
.cfg
|
|
||||||
.announcement_channel
|
|
||||||
.say(&ctx.http, compliment)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
next_check = next_check
|
|
||||||
.with_hour(7)
|
|
||||||
.unwrap()
|
|
||||||
.checked_add_days(Days::new(1))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tokio::time::sleep(Duration::from_secs(60 * 60)).await;
|
|
||||||
{
|
|
||||||
{
|
|
||||||
println!("Reloading config...");
|
|
||||||
let mut data = ctx.data.write().await;
|
|
||||||
let global_data = data.get_mut::<GlobalData>().unwrap();
|
|
||||||
global_data.reload().await.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -151,7 +87,12 @@ impl EventHandler for Handler {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if recv_coin {
|
if recv_coin {
|
||||||
let emojis = new_message.guild(&ctx.cache).unwrap().emojis;
|
let emojis = &new_message
|
||||||
|
.guild_id
|
||||||
|
.unwrap()
|
||||||
|
.emojis(&ctx.http)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let emoji = {
|
let emoji = {
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
@ -163,9 +104,9 @@ impl EventHandler for Handler {
|
|||||||
.react(
|
.react(
|
||||||
&ctx.http,
|
&ctx.http,
|
||||||
ReactionType::Custom {
|
ReactionType::Custom {
|
||||||
animated: emoji.1.animated,
|
animated: emoji.animated,
|
||||||
id: emoji.1.id,
|
id: emoji.id,
|
||||||
name: Some(emoji.1.name.clone()),
|
name: Some(emoji.name.clone()),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@ -187,10 +128,15 @@ impl EventHandler for Handler {
|
|||||||
println!("Connected as {}", ready.user.name);
|
println!("Connected as {}", ready.user.name);
|
||||||
|
|
||||||
ctx.set_presence(
|
ctx.set_presence(
|
||||||
Some(Activity::listening("to your deepest secrets")),
|
Some(
|
||||||
OnlineStatus::Online,
|
ActivityData::streaming(
|
||||||
|
"Security Cameras inside your Home",
|
||||||
|
"https://www.youtube.com/watch?v=6n3pFFPSlW4",
|
||||||
)
|
)
|
||||||
.await;
|
.unwrap(),
|
||||||
|
),
|
||||||
|
OnlineStatus::Online,
|
||||||
|
);
|
||||||
|
|
||||||
tokio::spawn(async move { web_server(ctx).await });
|
tokio::spawn(async move { web_server(ctx).await });
|
||||||
}
|
}
|
||||||
@ -268,3 +214,14 @@ pub async fn my_help(
|
|||||||
let _ = help_commands::with_embeds(context, msg, args, help_options, groups, owners).await;
|
let _ = help_commands::with_embeds(context, msg, args, help_options, groups, owners).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_role(http: &Http, guild_id: GuildId, role_name: &str) -> Option<RoleId> {
|
||||||
|
guild_id
|
||||||
|
.roles(http)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.find(|(_, role)| role.name == role_name)
|
||||||
|
.map(|(role_id, _)| role_id)
|
||||||
|
.copied()
|
||||||
|
}
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
use crate::models::motivation::{Motivation, MotivationConfig};
|
use crate::models::motivation::{Motivation, MotivationConfig};
|
||||||
use crate::{command, group, GlobalData};
|
use crate::{command, group, GlobalData};
|
||||||
use magick_rust::{DrawingWand, MagickWand, PixelWand};
|
use magick_rust::{DrawingWand, MagickWand, PixelWand};
|
||||||
|
use serenity::builder::{CreateAttachment, CreateMessage};
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::framework::standard::{Args, CommandError, CommandResult};
|
use serenity::framework::standard::{Args, CommandError, CommandResult};
|
||||||
use serenity::model::channel::{AttachmentType, Message};
|
use serenity::model::channel::Message;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
#[group]
|
#[group]
|
||||||
@ -80,13 +81,15 @@ async fn motivation(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
let image = create_motivation_image(motivation).await?;
|
let image = create_motivation_image(motivation).await?;
|
||||||
|
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.send_message(&ctx.http, |m| {
|
.send_message(
|
||||||
m.content("Today's motivation")
|
&ctx.http,
|
||||||
.add_file(AttachmentType::Bytes {
|
CreateMessage::new()
|
||||||
data: Cow::from(image),
|
.content("Today's motivation")
|
||||||
filename: "motivate.png".to_string(),
|
.add_file(CreateAttachment::bytes(
|
||||||
})
|
Cow::from(image),
|
||||||
})
|
"motivate.png".to_string(),
|
||||||
|
)),
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -1,21 +1,28 @@
|
|||||||
|
use crate::discord::get_role;
|
||||||
use crate::discord::motivate::create_motivation_image;
|
use crate::discord::motivate::create_motivation_image;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::inventory::{InventoryError, ItemData, ItemType, Operation};
|
use crate::inventory::{InventoryError, ItemData, ItemType, Operation};
|
||||||
use crate::models::motivation::MotivationConfig;
|
use crate::models::motivation::MotivationConfig;
|
||||||
|
use crate::models::task::{Task, TaskType};
|
||||||
use crate::user::{User, UserError};
|
use crate::user::{User, UserError};
|
||||||
use crate::{command, group, GlobalData};
|
use crate::{command, group, GlobalData};
|
||||||
use rand::prelude::SliceRandom;
|
use rand::prelude::SliceRandom;
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
|
use serenity::all::{
|
||||||
|
parse_user_mention, CreateAttachment, CreateMessage, EditRole, GuildId, Member,
|
||||||
|
};
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::framework::standard::{Args, CommandError, CommandResult};
|
use serenity::framework::standard::{Args, CommandError, CommandResult};
|
||||||
use serenity::model::channel::{AttachmentType, Message};
|
use serenity::model::channel::Message;
|
||||||
|
use serenity::model::Colour;
|
||||||
|
use serenity::prelude::Mentionable;
|
||||||
use serenity::utils::MessageBuilder;
|
use serenity::utils::MessageBuilder;
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use std::hash::Hasher;
|
use std::hash::Hasher;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
|
|
||||||
#[group]
|
#[group]
|
||||||
#[commands(shop, buy, inventory, use_item, sell_item)]
|
#[commands(shop, buy, inventory, use_item, sell_item, item_help)]
|
||||||
pub struct Shop;
|
pub struct Shop;
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
@ -24,7 +31,7 @@ async fn shop(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
|||||||
let mut data = ctx.data.write().await;
|
let mut data = ctx.data.write().await;
|
||||||
let global_data = data.get_mut::<GlobalData>().unwrap();
|
let global_data = data.get_mut::<GlobalData>().unwrap();
|
||||||
|
|
||||||
let bot_user = User::get_user(&global_data.db, ctx.cache.current_user_id())?;
|
let bot_user = User::get_user(&global_data.db, ctx.cache.current_user().id)?;
|
||||||
|
|
||||||
if bot_user.inventory.inventory.is_empty() {
|
if bot_user.inventory.inventory.is_empty() {
|
||||||
msg.reply(&ctx.http, "Sorry shop is closed until we get more wares.")
|
msg.reply(&ctx.http, "Sorry shop is closed until we get more wares.")
|
||||||
@ -91,7 +98,7 @@ async fn buy(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
|
|
||||||
let res = User::item_transaction(
|
let res = User::item_transaction(
|
||||||
&global_data.db,
|
&global_data.db,
|
||||||
ctx.cache.current_user_id(),
|
ctx.cache.current_user().id,
|
||||||
msg.author.id,
|
msg.author.id,
|
||||||
item,
|
item,
|
||||||
Operation::Buy,
|
Operation::Buy,
|
||||||
@ -117,6 +124,58 @@ async fn buy(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn blockable_item(
|
||||||
|
ctx: &Context,
|
||||||
|
global_data: &GlobalData,
|
||||||
|
guild_id: GuildId,
|
||||||
|
msg_content: &str,
|
||||||
|
role_name: &str,
|
||||||
|
weapon_name: &str,
|
||||||
|
block_item: ItemType,
|
||||||
|
) -> CommandResult<(bool, Member)> {
|
||||||
|
let split = msg_content.split(weapon_name);
|
||||||
|
let target = parse_user_mention(split.last().unwrap()).unwrap();
|
||||||
|
let target_member = match guild_id.member(&ctx.http, target).await {
|
||||||
|
Ok(member) => member,
|
||||||
|
Err(_) => return Err(CommandError::from("I have no clue who that is tbh")),
|
||||||
|
};
|
||||||
|
|
||||||
|
if target_member.user.id == ctx.cache.current_user().id {
|
||||||
|
return Err(CommandError::from("You can not harm me in a way that matters."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if User::try_use_item(&global_data.db, target, block_item, true).is_ok() {
|
||||||
|
Ok((false, target_member))
|
||||||
|
} else {
|
||||||
|
let role = if let Some(role) = get_role(&ctx.http, guild_id, role_name).await {
|
||||||
|
role
|
||||||
|
} else {
|
||||||
|
guild_id
|
||||||
|
.create_role(
|
||||||
|
&ctx.http,
|
||||||
|
EditRole::new()
|
||||||
|
.name(role_name)
|
||||||
|
.colour(Colour::from_rgb(1, 1, 1)),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.id
|
||||||
|
};
|
||||||
|
|
||||||
|
target_member.add_role(&ctx.http, role).await?;
|
||||||
|
|
||||||
|
Task::add_task(
|
||||||
|
&global_data.db,
|
||||||
|
TaskType::RemoveRole {
|
||||||
|
role_id: role.get(),
|
||||||
|
user_id: target_member.user.id.get(),
|
||||||
|
},
|
||||||
|
chrono::Utc::now() + chrono::Duration::seconds(global_data.cfg.effect_role_duration),
|
||||||
|
)?;
|
||||||
|
Ok((true, target_member))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[only_in(guilds)]
|
#[only_in(guilds)]
|
||||||
#[aliases("use")]
|
#[aliases("use")]
|
||||||
@ -131,7 +190,8 @@ async fn use_item(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let item = match args.rest().parse::<ItemType>() {
|
let msg_content = args.rest().to_lowercase();
|
||||||
|
let item = match msg_content.parse::<ItemType>() {
|
||||||
Ok(i) => i,
|
Ok(i) => i,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
msg.reply(&ctx.http, "I don't know what the heck that is tbh.")
|
msg.reply(&ctx.http, "I don't know what the heck that is tbh.")
|
||||||
@ -140,7 +200,7 @@ async fn use_item(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let item_data = match User::try_use_item(&global_data.db, msg.author.id, item) {
|
let item_data = match User::try_use_item(&global_data.db, msg.author.id, item, false) {
|
||||||
Ok(i) => i,
|
Ok(i) => i,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if let Error::UserError(UserError::InventoryError(InventoryError::NotEnoughItems)) = err
|
if let Error::UserError(UserError::InventoryError(InventoryError::NotEnoughItems)) = err
|
||||||
@ -185,7 +245,7 @@ async fn use_item(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
];
|
];
|
||||||
let fortune = good_fortunes.choose(&mut thread_rng()).unwrap();
|
let fortune = good_fortunes.choose(&mut thread_rng()).unwrap();
|
||||||
|
|
||||||
msg.reply(&ctx.http, fortune).await?;
|
msg.reply(&ctx.http, fortune.to_string()).await?;
|
||||||
}
|
}
|
||||||
ItemType::Nft => {
|
ItemType::Nft => {
|
||||||
if let Some(ItemData::Nft(path)) = item_data {
|
if let Some(ItemData::Nft(path)) = item_data {
|
||||||
@ -198,13 +258,16 @@ async fn use_item(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.send_message(&ctx.http, |m| {
|
.send_message(
|
||||||
m.content("Your NFT my good friend:")
|
&ctx.http,
|
||||||
.add_file(AttachmentType::File {
|
CreateMessage::new()
|
||||||
file: &file,
|
.content("Your NFT my good friend:")
|
||||||
filename: "nft.png".to_string(),
|
.add_file(
|
||||||
})
|
CreateAttachment::file(&file, "nft.png".to_string())
|
||||||
})
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
),
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
msg.reply(
|
msg.reply(
|
||||||
@ -217,6 +280,46 @@ async fn use_item(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
ItemType::LicenseToBeHorny => {
|
ItemType::LicenseToBeHorny => {
|
||||||
msg.reply(&ctx.http, "https://media.discordapp.net/attachments/840015650286075945/1127022083919069184/Img_2022_10_21_05_08_12.jpg").await?;
|
msg.reply(&ctx.http, "https://media.discordapp.net/attachments/840015650286075945/1127022083919069184/Img_2022_10_21_05_08_12.jpg").await?;
|
||||||
}
|
}
|
||||||
|
ItemType::KillGun => {
|
||||||
|
let (outcome, target) = blockable_item(
|
||||||
|
ctx,
|
||||||
|
global_data,
|
||||||
|
msg.guild_id.unwrap(),
|
||||||
|
&msg_content,
|
||||||
|
"Dead",
|
||||||
|
"kill gun ",
|
||||||
|
ItemType::Helmet,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if outcome {
|
||||||
|
msg.reply(&ctx.http, format!("You draw your trusty kill gun and shoot one kill bullet. It hits it mark between {}'s eyes, killing them instantly. They are now dead.", target.mention())).await?;
|
||||||
|
} else {
|
||||||
|
msg.reply(&ctx.http, format!("The kill bullet shoots at kill velocity toward {}! They smirk, and simply pull out their Helmet and put it on, the bullet bounces off and falls to the floor. The crowd gasps (like they do in my animes). \"No death today pal\"", target.mention())).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ItemType::CancelRay => {
|
||||||
|
let (outcome, target) = blockable_item(
|
||||||
|
ctx,
|
||||||
|
global_data,
|
||||||
|
msg.guild_id.unwrap(),
|
||||||
|
&msg_content,
|
||||||
|
"Cancelled",
|
||||||
|
"cancel ray ",
|
||||||
|
ItemType::CancelInsurance,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if outcome {
|
||||||
|
msg.reply(&ctx.http, format!("You shoot the cancel ray at {}. As the ray impacts them, you can here their phone buzz. They have been cancelled, you hear the liberal media in the distance.", target.mention())).await?;
|
||||||
|
} else {
|
||||||
|
msg.reply(&ctx.http, format!("The ray nearly hits {}, but they are surrounded in a a shimmering blue energy shield. \"The liberal media won't strike this time!\"", target.mention())).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ItemType::Helmet => {
|
||||||
|
msg.reply(&ctx.http, "You're trusty helmet regards you helmetly")
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -239,10 +342,12 @@ async fn sell_item(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let bot_user = ctx.cache.current_user().id;
|
||||||
|
|
||||||
match User::item_transaction(
|
match User::item_transaction(
|
||||||
&global_data.db,
|
&global_data.db,
|
||||||
msg.author.id,
|
msg.author.id,
|
||||||
ctx.cache.current_user_id(),
|
bot_user,
|
||||||
item,
|
item,
|
||||||
Operation::Sell,
|
Operation::Sell,
|
||||||
) {
|
) {
|
||||||
@ -278,17 +383,33 @@ async fn sell_item(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn restock_shop(ctx: &Context) -> Result<(), CommandError> {
|
#[command]
|
||||||
let mut data = ctx.data.write().await;
|
#[only_in(guilds)]
|
||||||
let global_data = data.get_mut::<GlobalData>().unwrap();
|
#[description("Get help with an item")]
|
||||||
|
async fn item_help(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
|
let item = match args.rest().parse::<ItemType>() {
|
||||||
|
Ok(i) => i,
|
||||||
|
Err(_) => {
|
||||||
|
msg.reply(&ctx.http, "I don't know what the heck that is tbh.")
|
||||||
|
.await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let mut bot_user = User::get_user(&global_data.db, ctx.cache.current_user_id())?;
|
msg.reply(&ctx.http, format!("**{}**: {}", item, item.description()))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn restock_shop(ctx: &Context, global_data: &GlobalData) -> Result<(), CommandError> {
|
||||||
|
let mut bot_user = User::get_user(&global_data.db, ctx.cache.current_user().id)?;
|
||||||
|
|
||||||
bot_user.inventory.inventory.clear();
|
bot_user.inventory.inventory.clear();
|
||||||
|
|
||||||
bot_user.inventory.give_item(
|
bot_user.inventory.give_item(
|
||||||
ItemType::TheConceptOfLove,
|
ItemType::TheConceptOfLove,
|
||||||
thread_rng().gen_range(0..5),
|
thread_rng().gen_range(0..=5),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
bot_user
|
bot_user
|
||||||
@ -296,14 +417,23 @@ pub async fn restock_shop(ctx: &Context) -> Result<(), CommandError> {
|
|||||||
.give_item(ItemType::GoodFortune, thread_rng().gen_range(0..10), None);
|
.give_item(ItemType::GoodFortune, thread_rng().gen_range(0..10), None);
|
||||||
bot_user.inventory.give_item(
|
bot_user.inventory.give_item(
|
||||||
ItemType::CancelInsurance,
|
ItemType::CancelInsurance,
|
||||||
thread_rng().gen_range(0..10),
|
thread_rng().gen_range(1..=3),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
bot_user.inventory.give_item(
|
bot_user.inventory.give_item(
|
||||||
ItemType::LicenseToBeHorny,
|
ItemType::LicenseToBeHorny,
|
||||||
thread_rng().gen_range(0..25),
|
thread_rng().gen_range(0..=25),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
bot_user
|
||||||
|
.inventory
|
||||||
|
.give_item(ItemType::KillGun, thread_rng().gen_range(1..=3), None);
|
||||||
|
bot_user
|
||||||
|
.inventory
|
||||||
|
.give_item(ItemType::CancelRay, thread_rng().gen_range(1..=3), None);
|
||||||
|
bot_user
|
||||||
|
.inventory
|
||||||
|
.give_item(ItemType::Helmet, thread_rng().gen_range(1..=3), None);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut dir = tokio::fs::read_dir(&global_data.cfg.nft_path).await?;
|
let mut dir = tokio::fs::read_dir(&global_data.cfg.nft_path).await?;
|
||||||
|
|||||||
@ -119,15 +119,7 @@ async fn story(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
|
|
||||||
let mut story_globals: HashMap<String, String> = HashMap::new();
|
let mut story_globals: HashMap<String, String> = HashMap::new();
|
||||||
|
|
||||||
let msg_channel = msg.channel(&ctx.http).await?;
|
let msg_channel = msg.channel(&ctx.http).await?.id();
|
||||||
let guild = msg.guild(&ctx.cache).unwrap();
|
|
||||||
|
|
||||||
let msg_channel = guild
|
|
||||||
.channels(&ctx.http)
|
|
||||||
.await?
|
|
||||||
.get(&msg_channel.id())
|
|
||||||
.unwrap()
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
for global in get_all_blanks(&story_contents) {
|
for global in get_all_blanks(&story_contents) {
|
||||||
msg_channel
|
msg_channel
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
use crate::{command, group, GlobalData};
|
use crate::{command, group, GlobalData};
|
||||||
|
use serenity::all::CreateAttachment;
|
||||||
|
use serenity::builder::CreateMessage;
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::framework::standard::{Args, CommandResult};
|
use serenity::framework::standard::{Args, CommandResult};
|
||||||
use serenity::model::channel::{AttachmentType, Message};
|
use serenity::model::channel::Message;
|
||||||
use serenity::model::id::UserId;
|
use serenity::model::id::UserId;
|
||||||
use serenity::model::prelude::GuildId;
|
use serenity::model::prelude::GuildId;
|
||||||
use serenity::utils::MessageBuilder;
|
use serenity::utils::MessageBuilder;
|
||||||
@ -157,13 +159,17 @@ pub async fn speak(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let guild = guild_id.to_guild_cached(&ctx.cache).unwrap();
|
let channel_id = {
|
||||||
|
let guild = ctx.cache.guild(guild_id).unwrap();
|
||||||
|
|
||||||
let channel_id = guild
|
let channel_id = guild
|
||||||
.voice_states
|
.voice_states
|
||||||
.get(&user_id)
|
.get(&user_id)
|
||||||
.and_then(|voice_state| voice_state.channel_id);
|
.and_then(|voice_state| voice_state.channel_id);
|
||||||
|
|
||||||
|
channel_id
|
||||||
|
};
|
||||||
|
|
||||||
let connect_to = match channel_id {
|
let connect_to = match channel_id {
|
||||||
Some(channel) => channel,
|
Some(channel) => channel,
|
||||||
None => {
|
None => {
|
||||||
@ -176,31 +182,30 @@ pub async fn speak(
|
|||||||
.expect("Songbird not initialized")
|
.expect("Songbird not initialized")
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
let (handler_lock, success_reader) = manager.join(guild_id, connect_to).await;
|
let handler_lock = manager.join(guild_id, connect_to).await;
|
||||||
|
|
||||||
|
if let Ok(handler_lock) = handler_lock {
|
||||||
let mut handler = handler_lock.lock().await;
|
let mut handler = handler_lock.lock().await;
|
||||||
if success_reader.is_ok() {
|
|
||||||
for word in sentence {
|
for word in sentence {
|
||||||
let word_path = dict.get(&word).unwrap();
|
let word_path = dict.get(&word).cloned().unwrap();
|
||||||
|
|
||||||
let audio_src = Compressed::new(
|
let src = input::File::new(word_path);
|
||||||
input::ffmpeg(word_path.to_str().unwrap())
|
|
||||||
|
let audio_src = Compressed::new(src.into(), Bitrate::BitsPerSecond(128_000))
|
||||||
.await
|
.await
|
||||||
.expect("Bad audio link."),
|
|
||||||
Bitrate::BitsPerSecond(128_000),
|
|
||||||
)
|
|
||||||
.expect("Bad params on message load");
|
.expect("Bad params on message load");
|
||||||
|
|
||||||
let _ = audio_src.raw.spawn_loader();
|
let _ = audio_src.raw.spawn_loader();
|
||||||
|
|
||||||
let duration = audio_src.metadata.duration.unwrap();
|
let voice = handler.play_input(audio_src.into());
|
||||||
let voice = handler.play_source(audio_src.into());
|
|
||||||
voice.set_volume(0.5).unwrap();
|
voice.set_volume(0.5).unwrap();
|
||||||
|
|
||||||
tokio::time::sleep(duration).await;
|
//tokio::time::sleep(duration).await;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.leave().await.unwrap();
|
handler.leave().await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,8 +213,7 @@ pub async fn speak(
|
|||||||
#[only_in(guilds)]
|
#[only_in(guilds)]
|
||||||
#[min_args(1)]
|
#[min_args(1)]
|
||||||
async fn say(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
async fn say(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
let guild = msg.guild(&ctx.cache).unwrap();
|
let guild_id = msg.guild_id.unwrap();
|
||||||
let guild_id = guild.id;
|
|
||||||
|
|
||||||
let voice = args.parse::<String>()?;
|
let voice = args.parse::<String>()?;
|
||||||
args.advance();
|
args.advance();
|
||||||
@ -262,12 +266,11 @@ async fn list_words(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
let file_data = Cow::from(file_data);
|
let file_data = Cow::from(file_data);
|
||||||
|
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.send_message(&ctx.http, |m| {
|
.send_message(
|
||||||
m.add_file(AttachmentType::Bytes {
|
&ctx.http,
|
||||||
data: file_data,
|
CreateMessage::new()
|
||||||
filename: "words.txt".to_string(),
|
.add_file(CreateAttachment::bytes(file_data, "words.txt".to_string())),
|
||||||
})
|
)
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -33,6 +33,26 @@ pub enum ItemType {
|
|||||||
GoodFortune,
|
GoodFortune,
|
||||||
Nft,
|
Nft,
|
||||||
LicenseToBeHorny,
|
LicenseToBeHorny,
|
||||||
|
KillGun,
|
||||||
|
CancelRay,
|
||||||
|
Helmet,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ItemType {
|
||||||
|
pub fn description(&self) -> String {
|
||||||
|
match self {
|
||||||
|
ItemType::CancelInsurance => "Automatically used to prevent being canceled".to_string(),
|
||||||
|
ItemType::TheConceptOfLove => "Understand!".to_string(),
|
||||||
|
ItemType::GoodFortune => {
|
||||||
|
"Used to give a good fortune. `!use good fortune am I not cringe?`".to_string()
|
||||||
|
}
|
||||||
|
ItemType::Nft => "A Non Fungible Token on the REAL FREN blockchain network".to_string(),
|
||||||
|
ItemType::LicenseToBeHorny => "Allows one horny time".to_string(),
|
||||||
|
ItemType::KillGun => "Used to kill people. `!use kill gun @Austin`".to_string(),
|
||||||
|
ItemType::CancelRay => "Used to cancel people. `!use cancel ray @Austin`".to_string(),
|
||||||
|
ItemType::Helmet => "Automatically used to block being killed".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, Eq, PartialEq, Copy)]
|
#[derive(Debug, Clone, Hash, Eq, PartialEq, Copy)]
|
||||||
@ -59,6 +79,12 @@ impl FromStr for ItemType {
|
|||||||
Ok(ItemType::Nft)
|
Ok(ItemType::Nft)
|
||||||
} else if item.starts_with("licensetobehorny") {
|
} else if item.starts_with("licensetobehorny") {
|
||||||
Ok(ItemType::LicenseToBeHorny)
|
Ok(ItemType::LicenseToBeHorny)
|
||||||
|
} else if item.starts_with("killgun") {
|
||||||
|
Ok(ItemType::KillGun)
|
||||||
|
} else if item.starts_with("helmet") {
|
||||||
|
Ok(ItemType::Helmet)
|
||||||
|
} else if item.starts_with("cancelray") {
|
||||||
|
Ok(ItemType::CancelRay)
|
||||||
} else {
|
} else {
|
||||||
Err(InventoryError::UnkownItem)
|
Err(InventoryError::UnkownItem)
|
||||||
}
|
}
|
||||||
@ -73,6 +99,9 @@ impl Display for ItemType {
|
|||||||
ItemType::GoodFortune => "Good Fortune".to_string(),
|
ItemType::GoodFortune => "Good Fortune".to_string(),
|
||||||
ItemType::Nft => "NFT".to_string(),
|
ItemType::Nft => "NFT".to_string(),
|
||||||
ItemType::LicenseToBeHorny => "License to be Horny".to_string(),
|
ItemType::LicenseToBeHorny => "License to be Horny".to_string(),
|
||||||
|
ItemType::KillGun => "Kill Gun".to_string(),
|
||||||
|
ItemType::Helmet => "Helmet".to_string(),
|
||||||
|
ItemType::CancelRay => "Cancel Ray".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(f, "{}", name)
|
write!(f, "{}", name)
|
||||||
@ -88,11 +117,14 @@ pub struct InventorySlot {
|
|||||||
impl InventorySlot {
|
impl InventorySlot {
|
||||||
pub fn value(&self) -> i64 {
|
pub fn value(&self) -> i64 {
|
||||||
match self.item_type {
|
match self.item_type {
|
||||||
ItemType::CancelInsurance => 50,
|
ItemType::CancelInsurance => 500,
|
||||||
ItemType::TheConceptOfLove => 300,
|
ItemType::TheConceptOfLove => 300,
|
||||||
ItemType::GoodFortune => 75,
|
ItemType::GoodFortune => 75,
|
||||||
ItemType::Nft => 100,
|
ItemType::Nft => 100,
|
||||||
ItemType::LicenseToBeHorny => 100,
|
ItemType::LicenseToBeHorny => 100,
|
||||||
|
ItemType::KillGun => 1000,
|
||||||
|
ItemType::Helmet => 500,
|
||||||
|
ItemType::CancelRay => 1000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +151,8 @@ impl InventorySlot {
|
|||||||
pub fn use_cost(&self) -> i64 {
|
pub fn use_cost(&self) -> i64 {
|
||||||
match self.item_type {
|
match self.item_type {
|
||||||
ItemType::Nft => 0,
|
ItemType::Nft => 0,
|
||||||
|
ItemType::Helmet => 0,
|
||||||
|
ItemType::CancelInsurance => 0,
|
||||||
_ => 1,
|
_ => 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,16 +239,24 @@ impl InventoryManager {
|
|||||||
msg_builder.build()
|
msg_builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_use_item(&mut self, item: ItemType) -> Result<Option<ItemData>, InventoryError> {
|
pub fn try_use_item(
|
||||||
|
&mut self,
|
||||||
|
item: ItemType,
|
||||||
|
force: bool,
|
||||||
|
) -> Result<Option<ItemData>, InventoryError> {
|
||||||
let item_slot = self
|
let item_slot = self
|
||||||
.get_item_mut(item)
|
.get_item_mut(item)
|
||||||
.ok_or(InventoryError::NotEnoughItems)?;
|
.ok_or(InventoryError::NotEnoughItems)?;
|
||||||
|
|
||||||
if item_slot.quantity < item_slot.use_cost() {
|
if item_slot.quantity < item_slot.use_cost() || item_slot.quantity == 0 {
|
||||||
return Err(InventoryError::NotEnoughItems);
|
return Err(InventoryError::NotEnoughItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if force {
|
||||||
|
item_slot.quantity -= 1;
|
||||||
|
} else {
|
||||||
item_slot.quantity -= item_slot.use_cost();
|
item_slot.quantity -= item_slot.use_cost();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(item_slot.item_data.clone())
|
Ok(item_slot.item_data.clone())
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/main.rs
13
src/main.rs
@ -12,8 +12,9 @@ use crate::config::{Args, BotConfig, Channel, GlobalData};
|
|||||||
use crate::discord::emoji_race::RaceMessage;
|
use crate::discord::emoji_race::RaceMessage;
|
||||||
use crate::discord::unrecognised_command_hook;
|
use crate::discord::unrecognised_command_hook;
|
||||||
use magick_rust::magick_wand_genesis;
|
use magick_rust::magick_wand_genesis;
|
||||||
|
use serenity::all::standard::BucketBuilder;
|
||||||
use serenity::framework::standard::macros::{command, group, help, hook};
|
use serenity::framework::standard::macros::{command, group, help, hook};
|
||||||
use serenity::framework::standard::StandardFramework;
|
use serenity::framework::standard::{Configuration, StandardFramework};
|
||||||
use serenity::prelude::*;
|
use serenity::prelude::*;
|
||||||
use songbird::SerenityInit;
|
use songbird::SerenityInit;
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
@ -48,7 +49,6 @@ async fn main() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let framework = StandardFramework::new()
|
let framework = StandardFramework::new()
|
||||||
.configure(|c| c.with_whitespace(true).prefix("!").ignore_bots(true))
|
|
||||||
.group(&discord::color::COLOR_GROUP)
|
.group(&discord::color::COLOR_GROUP)
|
||||||
.group(&discord::album::ALBUMCMD_GROUP)
|
.group(&discord::album::ALBUMCMD_GROUP)
|
||||||
.group(&discord::celeryman::CELERYMAN_GROUP)
|
.group(&discord::celeryman::CELERYMAN_GROUP)
|
||||||
@ -63,11 +63,18 @@ async fn main() {
|
|||||||
.group(&discord::birthday::BIRTHDAY_GROUP)
|
.group(&discord::birthday::BIRTHDAY_GROUP)
|
||||||
.group(&discord::little_fren::BUDDY_GROUP)
|
.group(&discord::little_fren::BUDDY_GROUP)
|
||||||
.unrecognised_command(unrecognised_command_hook)
|
.unrecognised_command(unrecognised_command_hook)
|
||||||
.bucket("bad_apple", |b| b.delay(60 * 10))
|
.bucket("bad_apple", BucketBuilder::default().delay(60 * 10))
|
||||||
.await
|
.await
|
||||||
.help(&discord::MY_HELP)
|
.help(&discord::MY_HELP)
|
||||||
.after(discord::after);
|
.after(discord::after);
|
||||||
|
|
||||||
|
framework.configure(
|
||||||
|
Configuration::new()
|
||||||
|
.with_whitespace(true)
|
||||||
|
.prefix("!")
|
||||||
|
.ignore_bots(true),
|
||||||
|
);
|
||||||
|
|
||||||
let story_channel = Channel::<String>::new();
|
let story_channel = Channel::<String>::new();
|
||||||
let race_channel = Channel::<RaceMessage>::new();
|
let race_channel = Channel::<RaceMessage>::new();
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,10 @@ impl Migration for Migration2RemoveImgur {
|
|||||||
|
|
||||||
let mot_tree = db.db.open_tree(MotivationConfig::tree())?;
|
let mot_tree = db.db.open_tree(MotivationConfig::tree())?;
|
||||||
|
|
||||||
|
if mot_tree.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let (id, config) = mot_tree.iter().next().unwrap()?;
|
let (id, config) = mot_tree.iter().next().unwrap()?;
|
||||||
|
|
||||||
let mut config = json::parse(std::str::from_utf8(&config).unwrap()).unwrap();
|
let mut config = json::parse(std::str::from_utf8(&config).unwrap()).unwrap();
|
||||||
|
|||||||
@ -4,3 +4,4 @@ pub mod insult_compliment;
|
|||||||
pub mod lil_fren;
|
pub mod lil_fren;
|
||||||
pub mod motivation;
|
pub mod motivation;
|
||||||
pub mod random;
|
pub mod random;
|
||||||
|
pub mod task;
|
||||||
|
|||||||
200
src/models/task.rs
Normal file
200
src/models/task.rs
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
use crate::config::GlobalData;
|
||||||
|
use crate::discord::shop::restock_shop;
|
||||||
|
use crate::error::Error;
|
||||||
|
use crate::models::birthday::BirthdayEntry;
|
||||||
|
use crate::models::insult_compliment::{RandomResponseTemplate, ResponseType};
|
||||||
|
use chrono::{Days, Duration, TimeZone, Timelike, Utc};
|
||||||
|
use j_db::database::Database;
|
||||||
|
use j_db::model::JdbModel;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serenity::all::Mentionable;
|
||||||
|
use serenity::prelude::Context;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq)]
|
||||||
|
pub enum TaskType {
|
||||||
|
RemoveRole { user_id: u64, role_id: u64 },
|
||||||
|
CheckBirthdays,
|
||||||
|
HandleReload,
|
||||||
|
RestockShop,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TaskType {
|
||||||
|
pub fn exclusive(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
TaskType::CheckBirthdays | TaskType::HandleReload | TaskType::RestockShop
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub struct Task {
|
||||||
|
id: Option<u64>,
|
||||||
|
task_type: TaskType,
|
||||||
|
time: chrono::DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JdbModel for Task {
|
||||||
|
fn id(&self) -> Option<u64> {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_id(&mut self, id: u64) {
|
||||||
|
self.id = Some(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tree() -> String {
|
||||||
|
"tasks".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_unique(&self, _other: &Self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Task {
|
||||||
|
pub fn add_task(
|
||||||
|
db: &Database,
|
||||||
|
task_type: TaskType,
|
||||||
|
time: chrono::DateTime<chrono::Utc>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
if task_type.exclusive() {
|
||||||
|
let old_tasks: Vec<Task> = db
|
||||||
|
.filter(|_, task: &Task| task.task_type == task_type)?
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for task in old_tasks {
|
||||||
|
db.remove::<Task>(task.id().unwrap())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db.insert::<Task>(Task {
|
||||||
|
id: None,
|
||||||
|
task_type,
|
||||||
|
time,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
println!("Adding {:?} task to run at {}", task_type, time);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_reoccurring_tasks(ctx: &Context) -> Result<(), Error> {
|
||||||
|
let mut data = ctx.data.write().await;
|
||||||
|
let global_data = data.get_mut::<GlobalData>().unwrap();
|
||||||
|
|
||||||
|
Task::add_task(&global_data.db, TaskType::CheckBirthdays, Utc::now())?;
|
||||||
|
Task::add_task(&global_data.db, TaskType::RestockShop, Utc::now())?;
|
||||||
|
Task::add_task(
|
||||||
|
&global_data.db,
|
||||||
|
TaskType::HandleReload,
|
||||||
|
Utc::now() + Duration::hours(1),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run_tasks(ctx: &Context) -> Result<(), Error> {
|
||||||
|
let mut data = ctx.data.write().await;
|
||||||
|
let global_data = data.get_mut::<GlobalData>().unwrap();
|
||||||
|
|
||||||
|
let active_tasks: Vec<Task> = global_data
|
||||||
|
.db
|
||||||
|
.filter(|_, task: &Task| task.time < chrono::Utc::now())?
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for task in active_tasks {
|
||||||
|
match task.task_type {
|
||||||
|
TaskType::RemoveRole { user_id, role_id } => {
|
||||||
|
let user = global_data
|
||||||
|
.cfg
|
||||||
|
.guild_id
|
||||||
|
.member(&ctx.http, user_id)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("Removing role {} from {}", role_id, user.display_name());
|
||||||
|
user.remove_role(&ctx.http, role_id).await.unwrap();
|
||||||
|
}
|
||||||
|
TaskType::CheckBirthdays => {
|
||||||
|
println!("Checking Birthdays");
|
||||||
|
let todays_birthdays = BirthdayEntry::todays_birthdays(
|
||||||
|
&global_data.db,
|
||||||
|
chrono::Utc::now().date_naive(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
for birth in todays_birthdays {
|
||||||
|
if let Ok(user) = global_data
|
||||||
|
.cfg
|
||||||
|
.guild_id
|
||||||
|
.member(&ctx.http, birth.discord_id)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
global_data
|
||||||
|
.cfg
|
||||||
|
.announcement_channel
|
||||||
|
.say(&ctx.http, format!("Happy birthday {}!", user.mention()))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let compliment = RandomResponseTemplate::get_random_response(
|
||||||
|
&global_data.db,
|
||||||
|
ResponseType::Compliment,
|
||||||
|
user.display_name(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
global_data
|
||||||
|
.cfg
|
||||||
|
.announcement_channel
|
||||||
|
.say(&ctx.http, compliment)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let next_check = chrono_tz::America::Chicago
|
||||||
|
.from_utc_datetime(&Utc::now().naive_utc())
|
||||||
|
.with_hour(8)
|
||||||
|
.unwrap()
|
||||||
|
.with_minute(0)
|
||||||
|
.unwrap()
|
||||||
|
.with_second(0)
|
||||||
|
.unwrap()
|
||||||
|
.checked_add_days(Days::new(1))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Task::add_task(
|
||||||
|
&global_data.db,
|
||||||
|
TaskType::CheckBirthdays,
|
||||||
|
next_check.with_timezone(&Utc),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
TaskType::HandleReload => {
|
||||||
|
println!("Reloading config...");
|
||||||
|
global_data.reload().await.unwrap();
|
||||||
|
|
||||||
|
Task::add_task(
|
||||||
|
&global_data.db,
|
||||||
|
TaskType::HandleReload,
|
||||||
|
Utc::now() + Duration::hours(1),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
TaskType::RestockShop => {
|
||||||
|
println!("Restocking Shop...");
|
||||||
|
restock_shop(ctx, global_data).await.unwrap();
|
||||||
|
Task::add_task(
|
||||||
|
&global_data.db,
|
||||||
|
TaskType::RestockShop,
|
||||||
|
Utc::now() + Duration::hours(1),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = global_data.db.remove::<Task>(task.id().unwrap()).is_ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -136,12 +136,13 @@ impl User {
|
|||||||
db: &Database,
|
db: &Database,
|
||||||
discord_id: UserId,
|
discord_id: UserId,
|
||||||
item: ItemType,
|
item: ItemType,
|
||||||
|
force: bool,
|
||||||
) -> Result<Option<ItemData>, Error> {
|
) -> Result<Option<ItemData>, Error> {
|
||||||
let mut user = Self::get_user(db, discord_id)?;
|
let mut user = Self::get_user(db, discord_id)?;
|
||||||
|
|
||||||
let item = user
|
let item = user
|
||||||
.inventory
|
.inventory
|
||||||
.try_use_item(item)
|
.try_use_item(item, force)
|
||||||
.map_err(UserError::InventoryError)?;
|
.map_err(UserError::InventoryError)?;
|
||||||
|
|
||||||
db.insert::<User>(user)?;
|
db.insert::<User>(user)?;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user