Added gogurt reserve
This commit is contained in:
parent
3ec319b93d
commit
1367e427ed
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1093,7 +1093,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fren"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
dependencies = [
|
||||
"axum 0.8.1",
|
||||
"base64 0.22.1",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "fren"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
edition = "2024"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -11,6 +11,7 @@ mod little_fren;
|
||||
mod movie;
|
||||
mod role;
|
||||
pub(crate) mod shop;
|
||||
mod stonks;
|
||||
mod transit;
|
||||
pub(crate) mod voices;
|
||||
|
||||
@ -379,6 +380,10 @@ pub async fn run_bot(global_data: GlobalData) {
|
||||
shop::land_mine(),
|
||||
shop::phrase_canceler(),
|
||||
shop::nuke(),
|
||||
stonks::contribute_to_gogurt_reserve(),
|
||||
stonks::sell_from_gogurt_reserve(),
|
||||
stonks::gogurt_reserve_stats(),
|
||||
stonks::gogurt_reserve_contribution(),
|
||||
transit::cta_bets(),
|
||||
voices::list_voices(),
|
||||
voices::list_words(),
|
||||
|
||||
106
src/discord/stonks.rs
Normal file
106
src/discord/stonks.rs
Normal file
@ -0,0 +1,106 @@
|
||||
use crate::discord::Context;
|
||||
use crate::error::Error;
|
||||
use crate::models::gogurt_reserves::GogurtReserves;
|
||||
use poise::serenity_prelude::{Mentionable, MessageBuilder};
|
||||
|
||||
/// Contribute to the Gogurt Reserve
|
||||
#[poise::command(prefix_command, category = "Stonks", aliases("cgr"))]
|
||||
pub async fn contribute_to_gogurt_reserve(
|
||||
ctx: Context<'_>,
|
||||
#[description = "amount of FCs to spend"] contribute_amount: u64,
|
||||
) -> Result<(), Error> {
|
||||
let gogurt_bought =
|
||||
GogurtReserves::add_contribution(&ctx.data().db, ctx.author().id, contribute_amount)?;
|
||||
|
||||
ctx.reply(format!("You have purchased {gogurt_bought}lbs of Gogurt"))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sell gogurt from the Gogurt Reserve
|
||||
#[poise::command(prefix_command, category = "Stonks", aliases("sgr"))]
|
||||
pub async fn sell_from_gogurt_reserve(
|
||||
ctx: Context<'_>,
|
||||
#[description = "number of pounds of gogurt to sell"] gogurt_to_sell: f64,
|
||||
) -> Result<(), Error> {
|
||||
let fc_coin_made =
|
||||
GogurtReserves::take_contribution(&ctx.data().db, ctx.author().id, gogurt_to_sell)?;
|
||||
|
||||
ctx.reply(format!(
|
||||
"You have sold {gogurt_to_sell:.3}lbs of Gogurt for a profit of {fc_coin_made} FC"
|
||||
))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get stats on the reserve
|
||||
#[poise::command(prefix_command, category = "Stonks", aliases("grs"))]
|
||||
pub async fn gogurt_reserve_stats(ctx: Context<'_>) -> Result<(), Error> {
|
||||
let gogurt_reserves = GogurtReserves::get_gogurt_reserve(&ctx.data().db)?;
|
||||
let contributors = gogurt_reserves.get_contributors();
|
||||
let total_amount = gogurt_reserves.get_total_amount();
|
||||
let total_worth = gogurt_reserves.get_total_worth();
|
||||
let going_rate = gogurt_reserves.gogurt_rate_per_pound;
|
||||
|
||||
let mut msg_builder = MessageBuilder::new();
|
||||
|
||||
msg_builder.push_line("# Fren Gogurt Reserves LLC Report");
|
||||
msg_builder
|
||||
.push_italic_line("Where your portable yogurt dreams are our portable yogurt solutions!");
|
||||
msg_builder.push_line("");
|
||||
msg_builder.push_line(format!(
|
||||
"The reserve currently contains **{total_amount:.3} lb** of Gogurt brand Gogurt"
|
||||
));
|
||||
msg_builder.push_line(format!(
|
||||
"The worth of this Gogurt total is **{total_worth} FC **"
|
||||
));
|
||||
msg_builder.push_line("");
|
||||
msg_builder.push_line(format!(
|
||||
"The going rate of Gogurt on the free market is **{going_rate:.3} FC/lb**"
|
||||
));
|
||||
msg_builder.push_line("");
|
||||
msg_builder.push_line("## Top Contributors");
|
||||
|
||||
let top_contributors_cnt = 5.clamp(0, contributors.len());
|
||||
|
||||
for (ndx, (contributor, amount)) in contributors[0..top_contributors_cnt].iter().enumerate() {
|
||||
let mention = contributor.mention();
|
||||
let worth = gogurt_reserves.convert_pounds_to_fc(*amount);
|
||||
let ndx = ndx + 1;
|
||||
msg_builder.push_line(format!(
|
||||
"{ndx}. {mention} with {amount:.3} lb worth {worth} FC"
|
||||
));
|
||||
}
|
||||
|
||||
ctx.reply(msg_builder.build()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get info on your reserve contribution
|
||||
#[poise::command(prefix_command, category = "Stonks", aliases("grc"))]
|
||||
pub async fn gogurt_reserve_contribution(
|
||||
ctx: Context<'_>,
|
||||
#[description = "Optional user to get info on"] user: Option<poise::serenity_prelude::User>,
|
||||
) -> Result<(), Error> {
|
||||
let gogurt_reserves = GogurtReserves::get_gogurt_reserve(&ctx.data().db)?;
|
||||
let user_id = user.map(|u| u.id).unwrap_or(ctx.author().id);
|
||||
let contribution = gogurt_reserves.reserve_contributors.get(&user_id);
|
||||
|
||||
let mention = user_id.mention();
|
||||
if let Some(contribution) = contribution {
|
||||
let worth = gogurt_reserves.convert_pounds_to_fc(*contribution);
|
||||
let rate = gogurt_reserves.gogurt_rate_per_pound;
|
||||
|
||||
ctx.reply(format!("{mention}'s contribution is **{contribution:.3} lb** of Gogurt Brand Gogurt worth **{worth} FC** at the current rate of **{rate:.3} FC/lb**")).await?;
|
||||
} else {
|
||||
ctx.reply(format!(
|
||||
"{mention} has not contributed to our fine reserve yet!"
|
||||
))
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -26,6 +26,7 @@ pub enum Error {
|
||||
NoImageFound,
|
||||
PipelineArgumentError(ModifyImageArgError),
|
||||
NoRandomFound,
|
||||
NotEnoughGogurt,
|
||||
}
|
||||
|
||||
impl StdError for Error {}
|
||||
@ -116,6 +117,10 @@ impl Display for Error {
|
||||
Error::NoImageFound => write!(f, "Image not found"),
|
||||
Error::PipelineArgumentError(err) => write!(f, "{err}"),
|
||||
Error::NoRandomFound => write!(f, "No random found"),
|
||||
Error::NotEnoughGogurt => write!(
|
||||
f,
|
||||
"Wow, you really don't have enough gogurt. What are you 12?? Just got buy some poor."
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
153
src/models/gogurt_reserves.rs
Normal file
153
src/models/gogurt_reserves.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use crate::error::Error;
|
||||
use crate::user::User;
|
||||
use chrono::Utc;
|
||||
use j_db::database::Database;
|
||||
use log::info;
|
||||
use poise::serenity_prelude::UserId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::f32::consts::PI;
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct GogurtReserves {
|
||||
id: Option<u64>,
|
||||
pub reserve_contributors: HashMap<UserId, f64>,
|
||||
pub gogurt_rate_per_pound: f64,
|
||||
}
|
||||
|
||||
impl j_db::model::JdbModel for GogurtReserves {
|
||||
fn id(&self) -> Option<u64> {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn set_id(&mut self, id: u64) {
|
||||
self.id = Some(id)
|
||||
}
|
||||
|
||||
fn tree() -> String {
|
||||
"GogurtReserves".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl GogurtReserves {
|
||||
pub fn get_gogurt_reserve(db: &Database) -> Result<Self, Error> {
|
||||
Ok(db
|
||||
.filter(|_, _: &Self| true)?
|
||||
.next()
|
||||
.unwrap_or(Self::default()))
|
||||
}
|
||||
|
||||
pub fn add_contribution(db: &Database, user: UserId, fc_amount: u64) -> Result<f64, Error> {
|
||||
let mut gogurt_reserve = Self::get_gogurt_reserve(db)?;
|
||||
let gogurt_amount = gogurt_reserve.convert_fc_to_pounds(fc_amount);
|
||||
|
||||
let contribution = gogurt_reserve
|
||||
.reserve_contributors
|
||||
.entry(user)
|
||||
.or_insert_with(|| 0.0);
|
||||
|
||||
User::try_take_funds(db, user, fc_amount as u32)?;
|
||||
*contribution += gogurt_amount;
|
||||
|
||||
db.insert(gogurt_reserve)?;
|
||||
|
||||
Ok(gogurt_amount)
|
||||
}
|
||||
|
||||
pub fn take_contribution(
|
||||
db: &Database,
|
||||
user: UserId,
|
||||
pounds_of_gogurt: f64,
|
||||
) -> Result<u64, Error> {
|
||||
let mut gogurt_reserve = Self::get_gogurt_reserve(db)?;
|
||||
let contribution = gogurt_reserve
|
||||
.reserve_contributors
|
||||
.entry(user)
|
||||
.or_insert_with(|| 0.0);
|
||||
|
||||
if pounds_of_gogurt.is_sign_negative() {
|
||||
return Err(Error::CommandError(
|
||||
"You can't take negative gogurt, stop trying".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if pounds_of_gogurt > *contribution {
|
||||
Err(Error::NotEnoughGogurt)
|
||||
} else {
|
||||
*contribution -= pounds_of_gogurt;
|
||||
let fc_profit = gogurt_reserve.convert_pounds_to_fc(pounds_of_gogurt);
|
||||
|
||||
db.insert(gogurt_reserve)?;
|
||||
|
||||
User::give_funds(db, user, fc_profit as u32)?;
|
||||
|
||||
Ok(fc_profit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_contributors(&self) -> Vec<(UserId, f64)> {
|
||||
let mut contributors: Vec<(UserId, f64)> =
|
||||
self.reserve_contributors.clone().into_iter().collect();
|
||||
|
||||
contributors.sort_by(|(_, a), (_, b)| b.total_cmp(a));
|
||||
|
||||
contributors
|
||||
}
|
||||
|
||||
pub fn get_total_amount(&self) -> f64 {
|
||||
self.reserve_contributors.values().sum()
|
||||
}
|
||||
|
||||
pub fn get_total_worth(&self) -> u64 {
|
||||
(self.get_total_amount() * self.gogurt_rate_per_pound) as u64
|
||||
}
|
||||
|
||||
pub fn convert_pounds_to_fc(&self, pounds: f64) -> u64 {
|
||||
(pounds * self.gogurt_rate_per_pound) as u64
|
||||
}
|
||||
|
||||
pub fn convert_fc_to_pounds(&self, fc: u64) -> f64 {
|
||||
fc as f64 / self.gogurt_rate_per_pound
|
||||
}
|
||||
|
||||
pub fn market_function(time: i64) -> f64 {
|
||||
let x = time as f64;
|
||||
(x.sin() + (PI as f64 * x).cos() + (PI as f64 * x).sin() + x.cos()
|
||||
- (x * 4.0).sin()
|
||||
- (x * 3.0).cos())
|
||||
.clamp(0.0, 6.0)
|
||||
}
|
||||
|
||||
pub fn calculate_new_rate(time: i64) -> f64 {
|
||||
Self::market_function(time)
|
||||
}
|
||||
|
||||
pub fn update_rate(db: &Database) -> Result<(), Error> {
|
||||
let mut reserves = Self::get_gogurt_reserve(db)?;
|
||||
|
||||
let time = Utc::now().timestamp();
|
||||
|
||||
let new_rate = Self::calculate_new_rate(time);
|
||||
|
||||
info!("Updating rate to '{new_rate}'");
|
||||
reserves.gogurt_rate_per_pound = new_rate;
|
||||
|
||||
db.insert(reserves)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::models::gogurt_reserves::GogurtReserves;
|
||||
|
||||
#[test]
|
||||
fn test_avg_market_function() {
|
||||
let sum: f64 = (0..100000).map(GogurtReserves::market_function).sum();
|
||||
let avg = sum / 100000.0;
|
||||
|
||||
println!("Average = {avg}");
|
||||
assert!(avg < 1.0 && avg > 0.0)
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
pub mod api_key;
|
||||
pub mod birthday;
|
||||
pub mod gogurt_reserves;
|
||||
pub mod insult_compliment;
|
||||
pub mod lil_fren;
|
||||
pub mod managed_roles;
|
||||
|
||||
@ -2,6 +2,7 @@ use crate::config::GlobalData;
|
||||
use crate::discord::shop::restock_shop;
|
||||
use crate::error::Error;
|
||||
use crate::models::birthday::BirthdayEntry;
|
||||
use crate::models::gogurt_reserves::GogurtReserves;
|
||||
use crate::models::insult_compliment::{RandomResponseTemplate, ResponseType};
|
||||
use crate::models::lil_fren::lil_fren_task;
|
||||
use chrono::{Days, Duration, TimeDelta, TimeZone, Timelike, Utc};
|
||||
@ -20,6 +21,7 @@ pub enum TaskType {
|
||||
HandleReload,
|
||||
RestockShop,
|
||||
UpdateLilBuddy,
|
||||
UpdateGogurtRate,
|
||||
}
|
||||
|
||||
impl TaskType {
|
||||
@ -30,6 +32,7 @@ impl TaskType {
|
||||
| TaskType::HandleReload
|
||||
| TaskType::RestockShop
|
||||
| TaskType::UpdateLilBuddy
|
||||
| TaskType::UpdateGogurtRate
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -90,6 +93,7 @@ impl Task {
|
||||
Task::add_task(&data.db, TaskType::CheckBirthdays, Utc::now())?;
|
||||
Task::add_task(&data.db, TaskType::RestockShop, Utc::now())?;
|
||||
Task::add_task(&data.db, TaskType::UpdateLilBuddy, Utc::now())?;
|
||||
Task::add_task(&data.db, TaskType::UpdateGogurtRate, Utc::now())?;
|
||||
Task::add_task(
|
||||
&data.db,
|
||||
TaskType::HandleReload,
|
||||
@ -195,6 +199,15 @@ impl Task {
|
||||
Utc::now() + Duration::minutes(5),
|
||||
)?;
|
||||
}
|
||||
TaskType::UpdateGogurtRate => {
|
||||
GogurtReserves::update_rate(&data.db)?;
|
||||
|
||||
Task::add_task(
|
||||
&data.db,
|
||||
TaskType::UpdateGogurtRate,
|
||||
Utc::now() + Duration::minutes(20),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
let _ = data.db.remove::<Task>(task.id().unwrap()).is_ok();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user