badgey/src/badgey/commands/ranks.rs

174 lines
6.5 KiB
Rust

use built::chrono;
use manifold::error::{ManifoldError, ManifoldResult};
use manifold::{ManifoldContext, ManifoldData};
use poise::serenity_prelude::{CreateEmbed, Mentionable, RoleId};
use to_markdown_table::MarkdownTable;
use crate::badgey::models::xp::{Leaderboard, Rank, Track, Xp};
#[poise::command(prefix_command, slash_command, user_cooldown = 86400)]
async fn switch_rank_track(ctx: ManifoldContext<'_>, #[description = "Track to switch to: officer or enlisted"] track: String) -> ManifoldResult<()> {
let db = &ctx.data().database;
let user_id_i64 = ctx.author().id.as_u64().clone() as i64;
let mut xp = match Xp::get(db, &user_id_i64) {
Ok(x) => x,
Err(_) => Xp::new(&user_id_i64)
};
let parsed_track = Track::get_track_by_name(db, &track)?;
if xp.rank_track != parsed_track.track_id {
let current_level_rank = Rank::get_rank_for_level(db, &xp.user_current_level, &xp.rank_track)?;
let new_level_rank = Rank::get_rank_for_level(db, &xp.user_current_level, &parsed_track.track_id)?;
if let Some(mut member) = ctx.author_member().await {
member.to_mut().remove_role(ctx, current_level_rank.role_id as u64).await?;
member.to_mut().add_role(ctx, new_level_rank.role_id as u64).await?;
}
xp.rank_track = parsed_track.track_id;
ctx.reply(t!("commands.ranks.track_switch", new_track = parsed_track.track_name, new_rank = new_level_rank.rank_name)).await?;
xp.rank_track_last_changed = Some(chrono::Utc::now().timestamp());
xp.insert(db)?;
} else {
ctx.reply(t!("commands.ranks.track_switch.failure", rank_track = parsed_track.track_name)).await?;
}
Ok(())
}
#[poise::command(prefix_command, slash_command, user_cooldown = 86400)]
async fn freeze_rank(ctx: ManifoldContext<'_>, #[rest] #[description = "Rank to lock yourself at. Omit to unfreeze your progression."] rank: Option<String>) -> ManifoldResult<()> {
let db = &ctx.data().database;
let user_id_i64 = ctx.author().id.as_u64().clone() as i64;
let mut xp = match Xp::get(db, &user_id_i64) {
Ok(x) => x,
Err(_) => Xp::new(&user_id_i64)
};
if let Some(r) = rank {
let frozen_rank = match Rank::get_rank_by_name(db, &r, &xp.rank_track) {
Ok(r) => r,
Err(e) => {
error!("Rank lookup error: {}", e);
ctx.reply(t!("commands.ranks.rank_freeze.not_found")).await?;
Err(e)?
}
};
if let Some(fr) = xp.freeze_rank {
// Remove any old frozen rank
if let Some(mut member) = ctx.author_member().await {
member.to_mut().remove_role(ctx, fr as u64).await?;
}
}
xp.freeze_rank = Some(frozen_rank.role_id.clone() as i64);
if let Some(mut member) = ctx.author_member().await {
member.to_mut().add_role(ctx, frozen_rank.role_id as u64).await?;
ctx.reply(t!("commands.ranks.rank_freeze.success", rank = frozen_rank.rank_name)).await?;
}
} else {
if let Some(fr) = xp.freeze_rank {
// Remove any old frozen rank
if let Some(mut member) = ctx.author_member().await {
member.to_mut().remove_role(ctx, fr as u64).await?;
}
xp.freeze_rank = None;
ctx.reply(t!("commands.ranks.rank_unfreeze.success")).await?;
} else {
ctx.reply(t!("commands.ranks.rank_unfreeze.failure")).await?;
}
}
xp.freeze_rank_last_changed = Some(chrono::Utc::now().timestamp());
xp.insert(db)?;
Ok(())
}
#[poise::command(prefix_command, slash_command)]
async fn rank(ctx: ManifoldContext<'_>) -> ManifoldResult<()> {
let db = &ctx.data().database;
let user_id_i64 = ctx.author().id.as_u64().clone() as i64;
let xp = match Xp::get(db, &user_id_i64) {
Ok(x) => x,
Err(_) => Xp::new(&user_id_i64)
};
let current_rank = Rank::get_rank_for_level(db, &xp.user_current_level, &xp.rank_track).unwrap_or(Rank::new());
let next_level_xp = xp.get_xp_to_next_level(&db);
let next_rank_xp = xp.get_xp_to_next_rank(&db).unwrap_or(0);
let mut response = t!("commands.ranks.rank.current_rank", current_rank = RoleId::from(current_rank.role_id as u64).mention(), next_level_xp = next_level_xp);
if next_rank_xp == 0 {
response = t!("commands.ranks.rank.max_rank", reponse = response);
} else if next_rank_xp == next_level_xp {
response = t!("commands.ranks.rank.and_rank", response = response);
} else {
response = t!("commands.ranks.rank.and_rank_different_level", response = response, next_rank_xp = next_rank_xp);
}
ctx.reply(response).await?;
Ok(())
}
#[poise::command(prefix_command, slash_command)]
async fn leaderboard(ctx: ManifoldContext<'_>) -> ManifoldResult<()> {
let reply_handle = ctx.reply("Retrieving leaderboard, please stand by...".to_string()).await?;
let entries_per_page = 20;
let mut pages = Vec::<CreateEmbed>::new();
let leaderboard = Leaderboard::get_leaderboard(&ctx.data().database)?;
let userinfo = ctx.data().user_info.lock().await;
let total = leaderboard.rows.len();
let pages_needed = (total / entries_per_page) + 1;
for i in 0..pages_needed {
let mut page = CreateEmbed::default()
.title(format!("XP Leaderboard Page {page} of {total_pages}", page=(i + 1), total_pages=&pages_needed)).to_owned();
let offset = i*entries_per_page;
let mut leaderboard_rows = Vec::new();
leaderboard.rows.iter().skip(offset).enumerate().for_each(|(j, f)| {
// cap at per-page limit
if j >= entries_per_page {
return;
}
let mut row = f.to_owned();
row.rank = (j + 1 + offset) as i32;
row.user_name = userinfo.get(&(row.uid as u64)).unwrap().username.clone();
leaderboard_rows.push(row);
});
let leaderboard_table = MarkdownTable::new(Some(vec!["Rank".to_string(), "User".to_string(), "XP".to_string()]), leaderboard_rows)?;
page.description(format!("```{table}```", table=leaderboard_table));
pages.push(page);
}
if pages.len() == 0 {
ctx.reply("No leaderboard entries yet!".to_string()).await?;
return Ok(())
}
manifold::helpers::paginate(ctx, reply_handle, pages, 0).await?;
Ok(())
}
pub fn commands() -> [poise::Command<ManifoldData, ManifoldError>; 4] {
[switch_rank_track(), freeze_rank(), rank(), leaderboard()]
}