1
Fork 0

Revampt jacket loading system

This commit is contained in:
prescientmoon 2024-09-09 18:06:07 +02:00
parent cba88c5def
commit ac4145ee40
Signed by: prescientmoon
SSH key fingerprint: SHA256:WFp/cO76nbarETAoQcQXuV+0h7XJsEsOCI0UsyPIy6U
24 changed files with 750 additions and 261 deletions

View file

@ -1,3 +1,4 @@
use anyhow::anyhow;
use poise::serenity_prelude::{CreateAttachment, CreateEmbed, CreateMessage};
use crate::{
@ -25,6 +26,8 @@ use crate::{
user::discord_id_to_discord_user,
};
use super::discord::MessageContext;
// {{{ Top command
/// Chart-related stats
#[poise::command(
@ -38,15 +41,9 @@ pub async fn chart(_ctx: Context<'_>) -> Result<(), Error> {
}
// }}}
// {{{ Info
/// Show a chart given it's name
#[poise::command(prefix_command, slash_command, user_cooldown = 1)]
async fn info(
ctx: Context<'_>,
#[rest]
#[description = "Name of chart to show (difficulty at the end)"]
name: String,
) -> Result<(), Error> {
let (song, chart) = guess_song_and_chart(&ctx.data(), &name)?;
// {{{ Implementation
async fn info_impl(ctx: &mut impl MessageContext, name: &str) -> Result<(), Error> {
let (song, chart) = guess_song_and_chart(&ctx.data(), name)?;
let attachement_name = "chart.png";
let icon_attachement = match chart.cached_jacket.as_ref() {
@ -95,17 +92,62 @@ async fn info(
embed = embed.thumbnail(format!("attachment://{}", &attachement_name));
}
ctx.channel_id()
.send_files(
ctx.http(),
icon_attachement,
CreateMessage::new().embed(embed),
)
ctx.send_files(icon_attachement, CreateMessage::new().embed(embed))
.await?;
Ok(())
}
// }}}
// {{{ Tests
#[cfg(test)]
mod info_tests {
use crate::{commands::discord::mock::MockContext, with_test_ctx};
use super::*;
#[tokio::test]
async fn no_suffix() -> Result<(), Error> {
with_test_ctx!("test/commands/chart/info/no_suffix", async |ctx| {
info_impl(ctx, "Pentiment").await?;
Ok(())
})
}
#[tokio::test]
async fn specify_difficulty() -> Result<(), Error> {
with_test_ctx!("test/commands/chart/info/specify_difficulty", async |ctx| {
info_impl(ctx, "Hellohell [ETR]").await?;
Ok(())
})
}
#[tokio::test]
async fn last_byd() -> Result<(), Error> {
with_test_ctx!(
"test/commands/chart/info/last_byd",
async |ctx: &mut MockContext| {
info_impl(ctx, "Last | Moment [BYD]").await?;
info_impl(ctx, "Last | Eternity [BYD]").await?;
Ok(())
}
)
}
}
// }}}
/// Show a chart given it's name
#[poise::command(prefix_command, slash_command, user_cooldown = 1)]
async fn info(
mut ctx: Context<'_>,
#[rest]
#[description = "Name of chart to show (difficulty at the end)"]
name: String,
) -> Result<(), Error> {
info_impl(&mut ctx, &name).await?;
Ok(())
}
// }}}
// {{{ Best score
/// Show the best score on a given chart
#[poise::command(prefix_command, slash_command, user_cooldown = 1)]
@ -138,9 +180,10 @@ async fn best(
)?
.query_row((user.id, chart.id), |row| Play::from_sql(chart, row))
.map_err(|_| {
format!(
anyhow!(
"Could not find any scores for {} [{:?}]",
song.title, chart.difficulty
song.title,
chart.difficulty
)
})?;

View file

@ -53,9 +53,7 @@ pub trait MessageContext {
}
// }}}
// {{{ Poise implementation
impl<'a, 'b> MessageContext
for poise::Context<'a, UserContext, Box<dyn std::error::Error + Send + Sync + 'b>>
{
impl<'a> MessageContext for poise::Context<'a, UserContext, Error> {
type Attachment = poise::serenity_prelude::Attachment;
fn data(&self) -> &UserContext {

View file

@ -4,6 +4,7 @@ use crate::context::{Context, Error};
use crate::recognition::recognize::{ImageAnalyzer, ScoreKind};
use crate::user::{discord_id_to_discord_user, User};
use crate::{get_user, timed};
use anyhow::anyhow;
use image::DynamicImage;
use poise::serenity_prelude as serenity;
use poise::serenity_prelude::CreateMessage;
@ -90,9 +91,10 @@ async fn magic_impl<C: MessageContext>(
analyzer
.read_score(ctx.data(), Some(chart.note_count), &grayscale_image, kind)
.map_err(|err| {
format!(
anyhow!(
"Could not read score for chart {} [{:?}]: {err}",
song.title, chart.difficulty
song.title,
chart.difficulty
)
})?
});
@ -137,52 +139,24 @@ async fn magic_impl<C: MessageContext>(
// {{{ Tests
#[cfg(test)]
mod magic_tests {
use std::{path::PathBuf, process::Command, str::FromStr};
use r2d2_sqlite::SqliteConnectionManager;
use std::path::PathBuf;
use crate::{
commands::discord::mock::MockContext,
context::{connect_db, get_shared_context},
};
use crate::with_test_ctx;
use super::*;
macro_rules! with_ctx {
($test_path:expr, $f:expr) => {{
let mut data = (*get_shared_context().await).clone();
let dir = tempfile::tempdir()?;
let path = dir.path().join("db.sqlite");
println!("path {path:?}");
data.db = connect_db(SqliteConnectionManager::file(path));
Command::new("scripts/import-charts.py")
.env("SHIMMERING_DATA_DIR", dir.path().to_str().unwrap())
.output()
.unwrap();
let mut ctx = MockContext::new(data);
User::create_from_context(&ctx)?;
let res: Result<(), Error> = $f(&mut ctx).await;
res?;
ctx.write_to(&PathBuf::from_str($test_path)?)?;
Ok(())
}};
}
#[tokio::test]
async fn no_pics() -> Result<(), Error> {
with_ctx!("test/commands/score/magic/no_pics", async |ctx| {
with_test_ctx!("test/commands/score/magic/no_pics", async |ctx| {
magic_impl(ctx, vec![]).await?;
Ok(())
})
}
#[tokio::test]
async fn basic_pic() -> Result<(), Error> {
with_ctx!("test/commands/score/magic/single_pic", async |ctx| {
async fn simple_pic() -> Result<(), Error> {
with_test_ctx!("test/commands/score/magic/single_pic", async |ctx| {
magic_impl(
ctx,
vec![PathBuf::from_str("test/screenshots/alter_ego.jpg")?],
@ -194,7 +168,7 @@ mod magic_tests {
#[tokio::test]
async fn weird_kerning() -> Result<(), Error> {
with_ctx!("test/commands/score/magic/weird_kerning", async |ctx| {
with_test_ctx!("test/commands/score/magic/weird_kerning", async |ctx| {
magic_impl(
ctx,
vec![
@ -298,7 +272,7 @@ pub async fn show(
Ok((song, chart, play, discord_id))
})?
.next()
.ok_or_else(|| format!("Could not find play with id {}", id))??;
.ok_or_else(|| anyhow!("Could not find play with id {}", id))??;
let author = discord_id_to_discord_user(&ctx, &discord_id).await?;
let user = User::by_id(ctx.data(), play.user_id)?;

View file

@ -1,5 +1,6 @@
use std::io::Cursor;
use anyhow::anyhow;
use image::{DynamicImage, ImageBuffer};
use poise::{
serenity_prelude::{CreateAttachment, CreateEmbed},
@ -194,9 +195,10 @@ async fn best_plays(
// }}}
// {{{ Display jacket
let jacket = chart.cached_jacket.as_ref().ok_or_else(|| {
format!(
anyhow!(
"Cannot find jacket for chart {} [{:?}]",
song.title, chart.difficulty
song.title,
chart.difficulty
)
})?;
@ -289,7 +291,7 @@ async fn best_plays(
// {{{ Display status text
with_font(&EXO_FONT, |faces| {
let status = play.short_status(scoring_system, chart).ok_or_else(|| {
format!(
anyhow!(
"Could not get status for score {}",
play.score(scoring_system)
)