Added shop commands
+ Ton of refactoring + clippy + fmt
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use crate::config::{Channel, GlobalData};
|
||||
use crate::wallet::WalletError;
|
||||
use crate::user::UserError;
|
||||
use crate::{command, group};
|
||||
use rand::seq::IteratorRandom;
|
||||
use rand::{thread_rng, Rng};
|
||||
@@ -25,7 +25,7 @@ pub struct EmojiRace;
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum RaceError {
|
||||
RacerNotFound,
|
||||
BetFundError(WalletError),
|
||||
BetFundError(UserError),
|
||||
UserHasBet,
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ impl Display for RaceError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WalletError> for RaceError {
|
||||
fn from(value: WalletError) -> Self {
|
||||
impl From<UserError> for RaceError {
|
||||
fn from(value: UserError) -> Self {
|
||||
Self::BetFundError(value)
|
||||
}
|
||||
}
|
||||
@@ -135,7 +135,7 @@ async fn add_bet(
|
||||
|
||||
global
|
||||
.cfg
|
||||
.wallet_manager
|
||||
.user_manager
|
||||
.try_take_funds(bet.author, bet.amount)?;
|
||||
|
||||
if !racers.iter().any(|r| r.emoji.id == bet.emoji) {
|
||||
@@ -298,9 +298,8 @@ async fn race(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
||||
|
||||
global_data
|
||||
.cfg
|
||||
.wallet_manager
|
||||
.get_user_wallet(winner)
|
||||
.coin_count += payout;
|
||||
.user_manager
|
||||
.give_funds(winner, payout as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::config::GlobalData;
|
||||
use crate::error::Error;
|
||||
use crate::wallet::WalletError;
|
||||
use crate::user::UserError;
|
||||
use crate::{command, group};
|
||||
use rand::{thread_rng, Rng};
|
||||
use serenity::client::Context;
|
||||
@@ -23,7 +23,7 @@ async fn balance(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
|
||||
let user = args.parse::<UserId>().unwrap_or(msg.author.id);
|
||||
|
||||
let wallet = global_data.cfg.wallet_manager.get_user_wallet(user);
|
||||
let wallet = global_data.cfg.user_manager.get_user(user);
|
||||
|
||||
msg.reply(
|
||||
&ctx.http,
|
||||
@@ -74,10 +74,10 @@ async fn gift(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||
|
||||
if let Err(e) = global_data
|
||||
.cfg
|
||||
.wallet_manager
|
||||
.user_manager
|
||||
.transfer_funds(msg.author.id, target, amount)
|
||||
{
|
||||
if let WalletError::NotEnoughFunds = e {
|
||||
if let UserError::NotEnoughFunds = e {
|
||||
msg.reply(
|
||||
&ctx.http,
|
||||
"Sorry pal, I can't give credit. Come back when you're a bit mmmm richer.",
|
||||
@@ -108,7 +108,7 @@ pub async fn give_coin(
|
||||
ctx: &Context,
|
||||
user: UserId,
|
||||
percent: f64,
|
||||
numer_of_coins: i64,
|
||||
number_of_coins: i64,
|
||||
) -> Result<bool, Error> {
|
||||
let should_get_coin = {
|
||||
let mut thread_rng = thread_rng();
|
||||
@@ -120,10 +120,10 @@ pub async fn give_coin(
|
||||
let mut data = ctx.data.write().await;
|
||||
let global_data = data.get_mut::<GlobalData>().unwrap();
|
||||
|
||||
{
|
||||
let wallet = global_data.cfg.wallet_manager.get_user_wallet(user);
|
||||
wallet.coin_count += numer_of_coins;
|
||||
}
|
||||
global_data
|
||||
.cfg
|
||||
.user_manager
|
||||
.give_funds(user, number_of_coins as u32);
|
||||
|
||||
global_data
|
||||
.cfg
|
||||
|
||||
+15
-7
@@ -6,11 +6,13 @@ pub mod emoji_race;
|
||||
pub mod fren_coin;
|
||||
pub mod joke;
|
||||
pub mod motivate;
|
||||
pub mod shop;
|
||||
pub mod story;
|
||||
pub mod voices;
|
||||
|
||||
use crate::discord::fren_coin::give_coin;
|
||||
use crate::discord::joke::random;
|
||||
use crate::discord::shop::restock_shop;
|
||||
use crate::{help, hook, GlobalData};
|
||||
use rand::prelude::IteratorRandom;
|
||||
use rand::thread_rng;
|
||||
@@ -29,22 +31,28 @@ use std::time::Duration;
|
||||
pub struct Handler;
|
||||
|
||||
static ERROR_MSG: &str =
|
||||
"OOPSIE WOOPSIE!! Uwu We made a fucky wucky!! A wittle fucko boingo! The admins at our
|
||||
headquarters are working VEWY HAWD to fix this!";
|
||||
"OOPSIE WOOPSIE!! Uwu We made a fucky wucky!! A wittle fucko boingo! The admins at our headquarters are working VEWY HAWD to fix this!";
|
||||
|
||||
#[async_trait]
|
||||
impl EventHandler for Handler {
|
||||
async fn cache_ready(&self, ctx: Context, _guilds: Vec<GuildId>) {
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
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();
|
||||
{
|
||||
println!("Reloading config...");
|
||||
let mut data = ctx.data.write().await;
|
||||
let global_data = data.get_mut::<GlobalData>().unwrap();
|
||||
|
||||
global_data.reload().await.unwrap();
|
||||
global_data.reload().await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
println!("Restocking shop...");
|
||||
restock_shop(&ctx).await.unwrap();
|
||||
}
|
||||
tokio::time::sleep(Duration::from_secs(60 * 60)).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+47
-41
@@ -4,7 +4,7 @@ use magick_rust::{DrawingWand, MagickWand, PixelWand};
|
||||
use rand::prelude::IteratorRandom;
|
||||
use rand::thread_rng;
|
||||
use serenity::client::Context;
|
||||
use serenity::framework::standard::{Args, CommandResult};
|
||||
use serenity::framework::standard::{Args, CommandError, CommandResult};
|
||||
use serenity::model::channel::{AttachmentType, Message};
|
||||
use std::borrow::Cow;
|
||||
|
||||
@@ -12,13 +12,10 @@ use std::borrow::Cow;
|
||||
#[commands(motivation)]
|
||||
pub struct Motivate;
|
||||
|
||||
#[command]
|
||||
#[only_in(guilds)]
|
||||
#[description("Let's give you motivation")]
|
||||
async fn motivation(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
||||
let data = ctx.data.read().await;
|
||||
let global_data = data.get::<GlobalData>().unwrap();
|
||||
|
||||
pub async fn create_image(
|
||||
global_data: &GlobalData,
|
||||
border_color: &str,
|
||||
) -> Result<Vec<u8>, CommandError> {
|
||||
let album = &global_data
|
||||
.cfg
|
||||
.motivation
|
||||
@@ -49,45 +46,54 @@ async fn motivation(ctx: &Context, msg: &Message, _args: Args) -> CommandResult
|
||||
.iter()
|
||||
.choose(&mut thread_rng())
|
||||
.unwrap();
|
||||
|
||||
let motivation = format!("{} {}", action, goal);
|
||||
|
||||
let image = {
|
||||
let mut wand = MagickWand::new();
|
||||
let mut border_wand = PixelWand::new();
|
||||
wand.read_image_blob(motivation_image_blob)?;
|
||||
let mut wand = MagickWand::new();
|
||||
let mut border_wand = PixelWand::new();
|
||||
wand.read_image_blob(motivation_image_blob)?;
|
||||
|
||||
border_wand.set_color("white")?;
|
||||
let width = wand.get_image_width();
|
||||
let border = width / 100;
|
||||
wand.border_image(
|
||||
&border_wand,
|
||||
border,
|
||||
border,
|
||||
magick_rust::bindings::CompositeOperator_OverCompositeOp,
|
||||
)?;
|
||||
border_wand.set_color(border_color)?;
|
||||
let width = wand.get_image_width();
|
||||
let border = width / 100;
|
||||
wand.border_image(
|
||||
&border_wand,
|
||||
border,
|
||||
border,
|
||||
magick_rust::bindings::CompositeOperator_OverCompositeOp,
|
||||
)?;
|
||||
|
||||
border_wand.set_color("black")?;
|
||||
let width = wand.get_image_width();
|
||||
let border = width * 20 / 100;
|
||||
wand.border_image(
|
||||
&border_wand,
|
||||
border,
|
||||
border,
|
||||
magick_rust::bindings::CompositeOperator_OverCompositeOp,
|
||||
)?;
|
||||
border_wand.set_color("black")?;
|
||||
let width = wand.get_image_width();
|
||||
let border = width * 20 / 100;
|
||||
wand.border_image(
|
||||
&border_wand,
|
||||
border,
|
||||
border,
|
||||
magick_rust::bindings::CompositeOperator_OverCompositeOp,
|
||||
)?;
|
||||
|
||||
let text_pos_x = wand.get_image_width() as f64 / 2.0;
|
||||
let text_pos_y = wand.get_image_height() as f64 - (wand.get_image_height() as f64 * 0.05);
|
||||
let text_pos_x = wand.get_image_width() as f64 / 2.0;
|
||||
let text_pos_y = wand.get_image_height() as f64 - (wand.get_image_height() as f64 * 0.05);
|
||||
|
||||
let mut text_wand = DrawingWand::new();
|
||||
let mut text_color_wand = PixelWand::new();
|
||||
text_color_wand.set_color("white")?;
|
||||
text_wand.set_fill_color(&text_color_wand);
|
||||
text_wand.set_font_size(0.07 * (wand.get_image_width() as f64));
|
||||
text_wand.set_text_alignment(magick_rust::bindings::AlignType_CenterAlign);
|
||||
wand.annotate_image(&text_wand, text_pos_x, text_pos_y, 0.0, &motivation)?;
|
||||
wand.write_image_blob("png")?
|
||||
};
|
||||
let mut text_wand = DrawingWand::new();
|
||||
let mut text_color_wand = PixelWand::new();
|
||||
text_color_wand.set_color("white")?;
|
||||
text_wand.set_fill_color(&text_color_wand);
|
||||
text_wand.set_font_size(0.07 * (wand.get_image_width() as f64));
|
||||
text_wand.set_text_alignment(magick_rust::bindings::AlignType_CenterAlign);
|
||||
wand.annotate_image(&text_wand, text_pos_x, text_pos_y, 0.0, &motivation)?;
|
||||
Ok(wand.write_image_blob("png")?)
|
||||
}
|
||||
|
||||
#[command]
|
||||
#[only_in(guilds)]
|
||||
#[description("Let's give you motivation")]
|
||||
async fn motivation(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
||||
let data = ctx.data.read().await;
|
||||
let global_data = data.get::<GlobalData>().unwrap();
|
||||
|
||||
let image = create_image(global_data, "white").await?;
|
||||
|
||||
msg.channel_id
|
||||
.send_message(&ctx.http, |m| {
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
use crate::discord::motivate::create_image;
|
||||
use crate::inventory::{InventoryError, ItemData, ItemType};
|
||||
use crate::user::UserError;
|
||||
use crate::{command, group, GlobalData};
|
||||
use rand::prelude::SliceRandom;
|
||||
use rand::{thread_rng, Rng};
|
||||
use serenity::client::Context;
|
||||
use serenity::framework::standard::{Args, CommandError, CommandResult};
|
||||
use serenity::model::channel::{AttachmentType, Message};
|
||||
use serenity::utils::MessageBuilder;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::Hasher;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
#[group]
|
||||
#[commands(shop, buy, inventory, use_item)]
|
||||
pub struct Shop;
|
||||
|
||||
#[command]
|
||||
#[description("Fren has wares if you have coin")]
|
||||
async fn shop(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
||||
let mut data = ctx.data.write().await;
|
||||
let global_data = data.get_mut::<GlobalData>().unwrap();
|
||||
|
||||
if global_data.cfg.bot_inventory.inventory.is_empty() {
|
||||
msg.reply(&ctx.http, "Sorry shop is closed until we get more wares.")
|
||||
.await?;
|
||||
} else {
|
||||
let mut inv_msg = MessageBuilder::new();
|
||||
|
||||
inv_msg.push_bold_line("Fren has wares if you have coin:");
|
||||
|
||||
inv_msg.push_safe(global_data.cfg.bot_inventory.list_items(true));
|
||||
|
||||
msg.reply(&ctx.http, inv_msg.build()).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command]
|
||||
#[description("Open your inventory, is specifically can not be rebound to the B key")]
|
||||
async fn inventory(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
||||
let mut data = ctx.data.write().await;
|
||||
let global_data = data.get_mut::<GlobalData>().unwrap();
|
||||
|
||||
let mut inv_msg = MessageBuilder::new();
|
||||
|
||||
let user = global_data.cfg.user_manager.get_user(msg.author.id);
|
||||
|
||||
if user.inventory.inventory.is_empty() {
|
||||
msg.reply(&ctx, "Sorry your inventory is empty.").await?;
|
||||
} else {
|
||||
inv_msg.push_bold_line("Your inventory: ");
|
||||
|
||||
inv_msg.push_safe(user.inventory.list_items(false));
|
||||
|
||||
msg.reply(&ctx.http, inv_msg.build()).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command]
|
||||
#[description("Buying something?")]
|
||||
async fn buy(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 gonna buy something or do you just like to stare?",
|
||||
)
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
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(());
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(item_slot) = global_data.cfg.bot_inventory.get_item(item) {
|
||||
if let Err(err) = global_data
|
||||
.cfg
|
||||
.user_manager
|
||||
.try_take_funds(msg.author.id, item_slot.value() as u32)
|
||||
{
|
||||
msg.reply(
|
||||
&ctx.http,
|
||||
format!(
|
||||
"I see why you don't get to the cloud district very often: {}",
|
||||
err
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
} else {
|
||||
msg.reply(&ctx.http, "I don't have that in stock you goob")
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let item_data = match global_data.cfg.bot_inventory.try_take_item(item, 1) {
|
||||
Ok(i) => i,
|
||||
Err(err) => {
|
||||
msg.reply(&ctx.http, format!("I can't just sell you that: {}", err))
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
global_data
|
||||
.cfg
|
||||
.user_manager
|
||||
.give_item(msg.author.id, item, 1, item_data);
|
||||
|
||||
msg.reply(&ctx, format!("Congrats, you now own a '{}'", item))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command]
|
||||
#[only_in(guilds)]
|
||||
#[aliases("use")]
|
||||
#[description("Buying something?")]
|
||||
async fn use_item(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 one item to use.")
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
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 item_data = match global_data
|
||||
.cfg
|
||||
.user_manager
|
||||
.try_use_item(msg.author.id, item)
|
||||
{
|
||||
Ok(i) => i,
|
||||
Err(err) => {
|
||||
if let UserError::InventoryError(InventoryError::NotEnoughItems) = err {
|
||||
msg.reply(
|
||||
&ctx.http,
|
||||
"Looks like you don't have enough of that item to use it",
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
match item {
|
||||
ItemType::CancelInsurance => {
|
||||
msg.reply(&ctx.http, "You are immune to the next (1) cancelings.")
|
||||
.await?;
|
||||
}
|
||||
ItemType::TheConceptOfLove => {
|
||||
msg.reply(&ctx.http, "I have DMed you the concept of love.")
|
||||
.await?;
|
||||
|
||||
msg.author
|
||||
.id
|
||||
.create_dm_channel(&ctx.http)
|
||||
.await?
|
||||
.say(
|
||||
&ctx.http,
|
||||
"DO NOT SHARE\nhttps://www.youtube.com/watch?v=HNy_retSME0",
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ItemType::GoodFortune => {
|
||||
let good_fortunes = [
|
||||
"Yes.",
|
||||
"OF COURSE.",
|
||||
"Carolyn, I'm sorry. So yes :)",
|
||||
"That sounds great!",
|
||||
"Yes, I am happy for you!",
|
||||
"||YES||",
|
||||
];
|
||||
let fortune = good_fortunes.choose(&mut thread_rng()).unwrap();
|
||||
|
||||
msg.reply(&ctx.http, fortune).await?;
|
||||
}
|
||||
ItemType::Nft => {
|
||||
if let Some(ItemData::Nft(path)) = item_data {
|
||||
let file: tokio::fs::File = match tokio::fs::File::open(path).await {
|
||||
Ok(f) => f,
|
||||
Err(_) => {
|
||||
msg.reply(&ctx.http, "Sorry this was a pump and dump")
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
msg.channel_id
|
||||
.send_message(&ctx.http, |m| {
|
||||
m.content("Your NFT my good friend:")
|
||||
.add_file(AttachmentType::File {
|
||||
file: &file,
|
||||
filename: "nft.png".to_string(),
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
msg.reply(
|
||||
&ctx.http,
|
||||
"Fren NFTs were never my creation, I merely promoted the brand",
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn restock_shop(ctx: &Context) -> Result<(), CommandError> {
|
||||
let mut data = ctx.data.write().await;
|
||||
let global_data = data.get_mut::<GlobalData>().unwrap();
|
||||
|
||||
global_data.cfg.bot_inventory.inventory.clear();
|
||||
|
||||
global_data.cfg.bot_inventory.give_item(
|
||||
ItemType::TheConceptOfLove,
|
||||
thread_rng().gen_range(0..5),
|
||||
None,
|
||||
);
|
||||
global_data.cfg.bot_inventory.give_item(
|
||||
ItemType::GoodFortune,
|
||||
thread_rng().gen_range(0..10),
|
||||
None,
|
||||
);
|
||||
global_data.cfg.bot_inventory.give_item(
|
||||
ItemType::CancelInsurance,
|
||||
thread_rng().gen_range(0..10),
|
||||
None,
|
||||
);
|
||||
|
||||
loop {
|
||||
let mut dir = tokio::fs::read_dir(&global_data.cfg.nft_path).await?;
|
||||
|
||||
let mut count = 0;
|
||||
while let Ok(Some(_)) = dir.next_entry().await {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
if count > 64 {
|
||||
tokio::fs::remove_dir_all(&global_data.cfg.nft_path).await?;
|
||||
tokio::fs::create_dir(&global_data.cfg.nft_path).await?
|
||||
}
|
||||
|
||||
let nft = create_image(global_data, "gold").await.unwrap();
|
||||
let mut hasher = DefaultHasher::new();
|
||||
hasher.write(&nft);
|
||||
let nft_hash = hasher.finish();
|
||||
|
||||
let path = global_data.cfg.nft_path.join(format!("{}.png", nft_hash));
|
||||
|
||||
if path.exists() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut file = tokio::fs::File::create(path.clone()).await?;
|
||||
file.write_all(&nft).await?;
|
||||
|
||||
global_data.cfg.bot_inventory.give_item(
|
||||
ItemType::Nft,
|
||||
1,
|
||||
Some(ItemData::Nft(path.to_str().unwrap().to_string())),
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
global_data.cfg.save(&global_data.args.cfg_path).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user