feature/xp-leaderboard #11

Merged
xyon merged 2 commits from feature/xp-leaderboard into main 2024-10-19 15:23:57 +00:00
5 changed files with 74 additions and 11 deletions

1
Cargo.lock generated
View File

@ -180,6 +180,7 @@ dependencies = [
"regex 1.11.0",
"rust-i18n",
"tokio",
"url",
]
[[package]]

View File

@ -22,6 +22,7 @@ rand = { version = "0.8.5", features = [ "small_rng" ] }
regex = "1.9.5"
rust-i18n = "3.1.2"
tokio = { version = "1.16.1", features = ["sync", "macros", "rt-multi-thread"] }
url = "2.5.2"
[package.metadata.i18n]
# The available locales for your application, default: ["en"].

View File

@ -2,7 +2,8 @@ use poise::serenity_prelude as serenity;
use manifold::error::{ManifoldError, ManifoldResult};
use manifold::{ManifoldContext, ManifoldData};
use poise::serenity_prelude::Mentionable;
use poise::serenity_prelude::{CreateEmbed, Mentionable};
use url::Url;
use crate::badgey::models::custom_response::{CustomResponse, CustomResponseInserter};
@ -52,18 +53,32 @@ async fn undelete_custom_response(ctx: ManifoldContext<'_>, id: i32) -> Manifold
#[poise::command(slash_command, prefix_command, required_permissions = "MODERATE_MEMBERS")]
async fn list_custom_responses_for_user(ctx: ManifoldContext<'_>, target: serenity::User) -> ManifoldResult<()> {
let db = &ctx.data().database;
let userinfo = &ctx.data().user_info.lock().await;
let mut answer: String = "".to_string();
let reply_handle = ctx.reply("Retrieving custom responses, please stand by...".to_string()).await?;
let mut pages = Vec::<CreateEmbed>::new();
let responses = CustomResponse::find_by_user(db, &target)?;
let total = responses.len();
CustomResponse::find_by_user(db, &target)?.iter().for_each(|f| {
answer.push_str(format!("{}{}", "\n", f.to_string()).as_str());
responses.iter().enumerate().for_each(|(i, f)| {
pages.push(CreateEmbed::default()
.title(format!("Custom Response {item} of {total} for user {target}", item=(i + 1), total=&total, target=target.name))
.description(format!("Added by {added_by} on <t:{added_on}:F> (<t:{added_on}:R>)", added_by=userinfo.get(&(f.added_by as u64)).unwrap().username, added_on=f.added_on))
.field("ID", format!("{id}", id=f.id), true)
.field("Deleted", format!("{deleted}", deleted=(if f.deleted.unwrap_or(false) {"yes"} else {"no"})), true)
.field("Trigger", format!("{trigger}", trigger=f.trigger), true)
.field("Response", format!("{response}", response=f.response), false)
.image(format!("{response}", response=Url::parse(&*f.response).unwrap_or("https://example.com".parse().unwrap())))
.to_owned()
)
});
if answer.len() == 0 {
answer.push_str("\nNone found");
if pages.len() == 0 {
ctx.reply(format!("No custom responses found for {target}", target=target.mention())).await?;
return Ok(())
}
ctx.reply(t!("commands.custom_response.list_user", user = &target.mention(), response_list = answer)).await?;
manifold::helpers::paginate(ctx, reply_handle, pages, 0).await?;
Ok(())
}

View File

@ -1,7 +1,7 @@
use built::chrono;
use manifold::error::{ManifoldError, ManifoldResult};
use manifold::{ManifoldContext, ManifoldData};
use poise::serenity_prelude::{Mentionable, RoleId};
use poise::serenity_prelude::{CreateEmbed, Mentionable, RoleId};
use crate::badgey::models::xp::{Rank, Track, Xp};
#[poise::command(prefix_command, slash_command, user_cooldown = 86400)]
@ -119,6 +119,46 @@ async fn rank(ctx: ManifoldContext<'_>) -> ManifoldResult<()> {
Ok(())
}
pub fn commands() -> [poise::Command<ManifoldData, ManifoldError>; 3] {
[switch_rank_track(), freeze_rank(), rank()]
#[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 mut pages = Vec::<CreateEmbed>::new();
let leaderboard = Xp::get_leaderboard(&ctx.data().database)?;
let total = leaderboard.len();
let pages_needed = (total / 10) + 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 mut fields = Vec::new();
fields.push(("Rank", "".to_string(), true));
fields.push(("User", "".to_string(), true));
fields.push(("XP", "".to_string(), true));
leaderboard.iter().skip(i*10).enumerate().for_each(|(i, f)| {
fields.push(("", format!("{rank}", rank=(i+1)), true));
fields.push(("", format!("<@{id}>", id=f.user_id), true));
fields.push(("", format!("{xp}", xp=f.xp_value), true));
});
page.fields(fields);
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()]
}

View File

@ -27,7 +27,7 @@ pub struct Xp {
pub rank_track: i64,
pub rank_track_last_changed: Option<i64>,
pub freeze_rank: Option<i64>,
pub freeze_rank_last_changed: Option<i64>
pub freeze_rank_last_changed: Option<i64>,
}
#[derive(Queryable, Selectable, Identifiable, Debug, Clone)]
@ -119,6 +119,12 @@ impl Xp {
Ok(())
}
pub fn get_leaderboard(conn: &Db) -> ManifoldResult<Vec<Self>> {
Ok(xp_table::dsl::xp
.order_by(xp::xp_value.desc())
.load::<Self>(&mut conn.get()?)?)
}
pub async fn award(ctx: &Context, fctx: &FrameworkContext<'_, ManifoldData, ManifoldError>, msg: &Message, db: &Db) -> ManifoldResult<()>{
if fctx.user_data().await.user_info.lock().await.get_mut(&msg.author.id.as_u64()).is_none() {
debug!("Tried to add XP to a user we don't know about, aborting.");