298 lines
8.9 KiB
Rust
298 lines
8.9 KiB
Rust
use crate::config::GlobalData;
|
|
use crate::discord::Context;
|
|
use crate::error::Error;
|
|
use crate::image_manipulation::create_motivation_image;
|
|
use crate::inventory::{InventoryError, ItemData, ItemType, Operation, Target};
|
|
use crate::user::{User, UserError};
|
|
use poise::ChoiceParameter;
|
|
use poise::serenity_prelude::UserId;
|
|
use poise::serenity_prelude::all::parse_user_mention;
|
|
use poise::serenity_prelude::utils::MessageBuilder;
|
|
use rand::{Rng, rng};
|
|
use std::collections::hash_map::DefaultHasher;
|
|
use std::hash::Hasher;
|
|
use std::sync::Arc;
|
|
use tokio::io::AsyncWriteExt;
|
|
|
|
/// Shop at a real virtual shop in the Metaverse!
|
|
#[poise::command(prefix_command, category = "Shop")]
|
|
pub async fn shop(ctx: Context<'_>) -> Result<(), Error> {
|
|
let bot_user = User::get_user(&ctx.data().db, ctx.cache().current_user().id)?;
|
|
|
|
if bot_user.inventory.inventory.is_empty() {
|
|
ctx.reply("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(bot_user.inventory.list_items(true));
|
|
|
|
ctx.reply(inv_msg.build()).await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Look at your inventory, this button is forced bound to any key other than "B"
|
|
#[poise::command(prefix_command, category = "Shop")]
|
|
pub async fn inventory(ctx: Context<'_>) -> Result<(), Error> {
|
|
let mut inv_msg = MessageBuilder::new();
|
|
|
|
let user = User::get_user(&ctx.data().db, ctx.author().id)?;
|
|
|
|
if user.inventory.inventory.is_empty() {
|
|
ctx.reply("Sorry your inventory is empty.").await?;
|
|
} else {
|
|
inv_msg.push_bold_line("Your inventory: ");
|
|
|
|
inv_msg.push_safe(user.inventory.list_items(false));
|
|
|
|
ctx.reply(inv_msg.build()).await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Buy an item from the shop with your hard earned cash
|
|
#[poise::command(prefix_command, category = "Shop")]
|
|
pub async fn buy(
|
|
ctx: Context<'_>,
|
|
#[description = "Item to buy"]
|
|
#[rest]
|
|
item: ItemType,
|
|
) -> Result<(), Error> {
|
|
let res = User::item_transaction(
|
|
&ctx.data().db,
|
|
ctx.cache().current_user().id,
|
|
ctx.author().id,
|
|
item,
|
|
Operation::Buy,
|
|
);
|
|
|
|
if let Err(err) = res {
|
|
if let Error::UserError(err) = err {
|
|
let resp = match err {
|
|
UserError::NotEnoughFunds => "Sorry Link, but I can't give store credit. Come back when you are mhhmm richer.".to_string(),
|
|
UserError::InvalidTarget => "I don't know who you are or how you got here.".to_string(),
|
|
UserError::InventoryError(err) => format!("Something went wrong with that item: {err}")
|
|
};
|
|
|
|
ctx.reply(resp).await?;
|
|
} else {
|
|
return Err(err);
|
|
}
|
|
} else {
|
|
ctx.reply(format!("Congrats, you now own a '{item}'"))
|
|
.await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Use an item and hope it doesn't use you
|
|
#[poise::command(prefix_command, category = "Shop", guild_only, aliases("use"))]
|
|
pub async fn use_item(
|
|
ctx: Context<'_>,
|
|
#[description = "item to use"]
|
|
#[rest]
|
|
item: String,
|
|
) -> Result<(), Error> {
|
|
if item.is_empty() {
|
|
ctx.reply("You need to select one item to use.").await?;
|
|
return Ok(());
|
|
}
|
|
|
|
let msg_content = item.to_lowercase();
|
|
let item = match msg_content.parse::<ItemType>() {
|
|
Ok(i) => i,
|
|
Err(_) => {
|
|
ctx.reply("I don't know what the heck that is tbh.").await?;
|
|
return Ok(());
|
|
}
|
|
};
|
|
|
|
let target_users: Vec<UserId> = match item.target() {
|
|
Target::Myself => {
|
|
vec![ctx.author().id]
|
|
}
|
|
Target::User => {
|
|
let item_name = format!("{} ", item.name().to_ascii_lowercase());
|
|
let split = msg_content.split(&item_name);
|
|
let target = parse_user_mention(split.last().unwrap()).unwrap();
|
|
let target_member = match ctx.guild_id().unwrap().member(ctx, target).await {
|
|
Ok(member) => member,
|
|
Err(_) => {
|
|
return Err(Error::CommandError(
|
|
"I have no clue who that is tbh".to_string(),
|
|
));
|
|
}
|
|
};
|
|
|
|
vec![target_member.user.id]
|
|
}
|
|
Target::Everyone => ctx.guild().unwrap().members.keys().copied().collect(),
|
|
};
|
|
|
|
let item_data = match User::try_use_item(&ctx.data().db, ctx.author().id, item, false) {
|
|
Ok(i) => i,
|
|
Err(err) => {
|
|
if let Error::UserError(UserError::InventoryError(InventoryError::NotEnoughItems)) = err
|
|
{
|
|
ctx.reply("Looks like you don't have enough of that item to use it")
|
|
.await?;
|
|
}
|
|
return Ok(());
|
|
}
|
|
};
|
|
|
|
for (ndx, target_user) in target_users.iter().enumerate() {
|
|
let outcome =
|
|
User::use_item_on_user(ctx, *target_user, ctx.guild_id().unwrap(), item, &item_data)
|
|
.await?;
|
|
|
|
if let Some(outcome) = outcome {
|
|
if ndx + 1 == target_users.len() {
|
|
ctx.reply(outcome).await?;
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Sell an item for profit
|
|
#[poise::command(prefix_command, category = "Shop", aliases("sell"))]
|
|
pub async fn sell_item(
|
|
ctx: Context<'_>,
|
|
#[description = "Item to sell"]
|
|
#[rest]
|
|
item: ItemType,
|
|
) -> Result<(), Error> {
|
|
let bot_user = ctx.cache().current_user().id;
|
|
|
|
match User::item_transaction(
|
|
&ctx.data().db,
|
|
ctx.author().id,
|
|
bot_user,
|
|
item,
|
|
Operation::Sell,
|
|
) {
|
|
Ok(_) => {
|
|
ctx.reply("I guess I could buy that...").await?;
|
|
}
|
|
Err(err) => {
|
|
if let Error::UserError(err) = err {
|
|
match err {
|
|
UserError::NotEnoughFunds => {
|
|
ctx.reply("What do I look like a charity service?").await?;
|
|
}
|
|
UserError::InvalidTarget => {
|
|
ctx.reply("If you got here, that means I'm not real...????")
|
|
.await?;
|
|
}
|
|
UserError::InventoryError(InventoryError::NotEnoughItems) => {
|
|
ctx.reply("You don't have enough of that item to sell it lmao")
|
|
.await?;
|
|
}
|
|
_ => {}
|
|
}
|
|
} else {
|
|
return Err(err);
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Get help on what an item does
|
|
#[poise::command(prefix_command, category = "Shop")]
|
|
pub async fn item_help(
|
|
ctx: Context<'_>,
|
|
#[description = "Item to get help on"]
|
|
#[rest]
|
|
item: ItemType,
|
|
) -> Result<(), Error> {
|
|
ctx.reply(format!("**{}**: {}", item, item.description()))
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn restock_shop(
|
|
ctx: &poise::serenity_prelude::Context,
|
|
global_data: &Arc<GlobalData>,
|
|
) -> Result<(), Error> {
|
|
let mut bot_user = User::get_user(&global_data.db, ctx.cache.current_user().id)?;
|
|
|
|
bot_user.inventory.inventory.clear();
|
|
|
|
bot_user
|
|
.inventory
|
|
.give_item(ItemType::TheConceptOfLove, rng().random_range(0..=5), None);
|
|
bot_user
|
|
.inventory
|
|
.give_item(ItemType::GoodFortune, rng().random_range(0..10), None);
|
|
bot_user
|
|
.inventory
|
|
.give_item(ItemType::CancelInsurance, rng().random_range(1..=3), None);
|
|
bot_user
|
|
.inventory
|
|
.give_item(ItemType::LicenseToBeHorny, rng().random_range(0..=25), None);
|
|
bot_user
|
|
.inventory
|
|
.give_item(ItemType::KillGun, rng().random_range(1..=3), None);
|
|
bot_user
|
|
.inventory
|
|
.give_item(ItemType::CancelRay, rng().random_range(1..=3), None);
|
|
bot_user
|
|
.inventory
|
|
.give_item(ItemType::Helmet, rng().random_range(1..=3), None);
|
|
|
|
bot_user
|
|
.inventory
|
|
.give_item(ItemType::EMP, rng().random_range(0..=1), 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_motivation_image(&global_data.db, &global_data.picox, "gold", None).await?;
|
|
let mut hasher = DefaultHasher::new();
|
|
hasher.write(&nft);
|
|
let nft_hash = hasher.finish();
|
|
|
|
let path = global_data.cfg.nft_path.join(format!("{nft_hash}.jpg"));
|
|
|
|
if path.exists() {
|
|
continue;
|
|
}
|
|
|
|
let mut file = tokio::fs::File::create(path.clone()).await?;
|
|
file.write_all(&nft).await?;
|
|
|
|
bot_user.inventory.give_item(
|
|
ItemType::Nft,
|
|
1,
|
|
Some(ItemData::Nft(path.to_str().unwrap().to_string())),
|
|
);
|
|
break;
|
|
}
|
|
|
|
global_data.db.insert::<User>(bot_user)?;
|
|
|
|
Ok(())
|
|
}
|