Implement migrations, and switch from sqlx to rusqlite
This commit is contained in:
parent
7cdc3a2755
commit
fee7fe77f8
17 changed files with 629 additions and 1044 deletions
|
@ -1,10 +1,9 @@
|
|||
use poise::serenity_prelude::{CreateAttachment, CreateEmbed, CreateMessage};
|
||||
use sqlx::query;
|
||||
|
||||
use crate::{
|
||||
arcaea::chart::Side,
|
||||
arcaea::{chart::Side, play::Play},
|
||||
context::{Context, Error},
|
||||
get_user, play_from_db_record,
|
||||
get_user,
|
||||
recognition::fuzzy_song_name::guess_song_and_chart,
|
||||
};
|
||||
use std::io::Cursor;
|
||||
|
@ -23,7 +22,7 @@ use poise::CreateReply;
|
|||
|
||||
use crate::{
|
||||
arcaea::score::{Score, ScoringSystem},
|
||||
user::discord_it_to_discord_user,
|
||||
user::discord_id_to_discord_user,
|
||||
};
|
||||
|
||||
// {{{ Top command
|
||||
|
@ -55,16 +54,18 @@ async fn info(
|
|||
None => None,
|
||||
};
|
||||
|
||||
let play_count = query!(
|
||||
"
|
||||
let play_count: usize = ctx
|
||||
.data()
|
||||
.db
|
||||
.get()?
|
||||
.prepare_cached(
|
||||
"
|
||||
SELECT COUNT(*) as count
|
||||
FROM plays
|
||||
WHERE chart_id=?
|
||||
",
|
||||
chart.id
|
||||
)
|
||||
.fetch_one(&ctx.data().db)
|
||||
.await?;
|
||||
",
|
||||
)?
|
||||
.query_row([chart.id], |row| row.get(0))?;
|
||||
|
||||
let mut embed = CreateEmbed::default()
|
||||
.title(format!(
|
||||
|
@ -77,7 +78,7 @@ async fn info(
|
|||
format!("{:.1}", chart.chart_constant as f32 / 100.0),
|
||||
true,
|
||||
)
|
||||
.field("Total plays", format!("{}", play_count.count), true)
|
||||
.field("Total plays", format!("{play_count}"), true)
|
||||
.field("BPM", &song.bpm, true)
|
||||
.field("Side", Side::SIDE_STRINGS[song.side.to_index()], true)
|
||||
.field("Artist", &song.title, true);
|
||||
|
@ -117,42 +118,40 @@ async fn best(
|
|||
let user = get_user!(&ctx);
|
||||
|
||||
let (song, chart) = guess_song_and_chart(&ctx.data(), &name)?;
|
||||
let play = query!(
|
||||
"
|
||||
SELECT
|
||||
let play = ctx
|
||||
.data()
|
||||
.db
|
||||
.get()?
|
||||
.prepare_cached(
|
||||
"
|
||||
SELECT
|
||||
p.id, p.chart_id, p.user_id, p.created_at,
|
||||
p.max_recall, p.far_notes, s.score
|
||||
FROM plays p
|
||||
JOIN scores s ON s.play_id = p.id
|
||||
WHERE s.scoring_system='standard'
|
||||
AND p.user_id=?
|
||||
AND p.chart_id=?
|
||||
ORDER BY s.score DESC
|
||||
LIMIT 1
|
||||
",
|
||||
user.id,
|
||||
chart.id
|
||||
)
|
||||
.fetch_one(&ctx.data().db)
|
||||
.await
|
||||
.map_err(|_| {
|
||||
format!(
|
||||
"Could not find any scores for {} [{:?}]",
|
||||
song.title, chart.difficulty
|
||||
)
|
||||
})?;
|
||||
let play = play_from_db_record!(chart, play);
|
||||
FROM plays p
|
||||
JOIN scores s ON s.play_id = p.id
|
||||
WHERE s.scoring_system='standard'
|
||||
AND p.user_id=?
|
||||
AND p.chart_id=?
|
||||
ORDER BY s.score DESC
|
||||
LIMIT 1
|
||||
",
|
||||
)?
|
||||
.query_row((user.id, chart.id), |row| Play::from_sql(chart, row))
|
||||
.map_err(|_| {
|
||||
format!(
|
||||
"Could not find any scores for {} [{:?}]",
|
||||
song.title, chart.difficulty
|
||||
)
|
||||
})?;
|
||||
|
||||
let (embed, attachment) = play
|
||||
.to_embed(
|
||||
&ctx.data().db,
|
||||
&user,
|
||||
&song,
|
||||
&chart,
|
||||
0,
|
||||
Some(&discord_it_to_discord_user(&ctx, &user.discord_id).await?),
|
||||
)
|
||||
.await?;
|
||||
let (embed, attachment) = play.to_embed(
|
||||
ctx.data(),
|
||||
&user,
|
||||
song,
|
||||
chart,
|
||||
0,
|
||||
Some(&discord_id_to_discord_user(&ctx, &user.discord_id).await?),
|
||||
)?;
|
||||
|
||||
ctx.channel_id()
|
||||
.send_files(ctx.http(), attachment, CreateMessage::new().embed(embed))
|
||||
|
@ -177,8 +176,12 @@ async fn plot(
|
|||
let (song, chart) = guess_song_and_chart(&ctx.data(), &name)?;
|
||||
|
||||
// SAFETY: we limit the amount of plotted plays to 1000.
|
||||
let plays = query!(
|
||||
"
|
||||
let plays = ctx
|
||||
.data()
|
||||
.db
|
||||
.get()?
|
||||
.prepare_cached(
|
||||
"
|
||||
SELECT
|
||||
p.id, p.chart_id, p.user_id, p.created_at,
|
||||
p.max_recall, p.far_notes, s.score
|
||||
|
@ -190,11 +193,9 @@ async fn plot(
|
|||
ORDER BY s.score DESC
|
||||
LIMIT 1000
|
||||
",
|
||||
user.id,
|
||||
chart.id
|
||||
)
|
||||
.fetch_all(&ctx.data().db)
|
||||
.await?;
|
||||
)?
|
||||
.query_map((user.id, chart.id), |row| Play::from_sql(chart, row))?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
if plays.len() == 0 {
|
||||
ctx.reply(format!(
|
||||
|
@ -209,7 +210,7 @@ async fn plot(
|
|||
let max_time = plays.iter().map(|p| p.created_at).max().unwrap();
|
||||
let mut min_score = plays
|
||||
.iter()
|
||||
.map(|p| play_from_db_record!(chart, p).score(scoring_system))
|
||||
.map(|p| p.score(scoring_system))
|
||||
.min()
|
||||
.unwrap()
|
||||
.0 as i64;
|
||||
|
@ -266,7 +267,7 @@ async fn plot(
|
|||
.map(|play| {
|
||||
(
|
||||
play.created_at.and_utc().timestamp_millis(),
|
||||
play_from_db_record!(chart, play).score(scoring_system),
|
||||
play.score(scoring_system),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
use std::time::Instant;
|
||||
|
||||
use crate::arcaea::play::CreatePlay;
|
||||
use crate::arcaea::play::{CreatePlay, Play};
|
||||
use crate::arcaea::score::Score;
|
||||
use crate::context::{Context, Error};
|
||||
use crate::recognition::recognize::{ImageAnalyzer, ScoreKind};
|
||||
use crate::user::{discord_it_to_discord_user, User};
|
||||
use crate::{edit_reply, get_user, play_from_db_record, timed};
|
||||
use crate::user::{discord_id_to_discord_user, User};
|
||||
use crate::{edit_reply, get_user, timed};
|
||||
use image::DynamicImage;
|
||||
use poise::serenity_prelude::futures::future::join_all;
|
||||
use poise::serenity_prelude::CreateMessage;
|
||||
use poise::{serenity_prelude as serenity, CreateReply};
|
||||
use sqlx::query;
|
||||
|
||||
// {{{ Score
|
||||
/// Score management
|
||||
|
@ -121,15 +120,13 @@ pub async fn magic(
|
|||
.with_attachment(file)
|
||||
.with_fars(maybe_fars)
|
||||
.with_max_recall(max_recall)
|
||||
.save(&ctx.data(), &user, &chart)
|
||||
.await?;
|
||||
.save(&ctx.data(), &user, &chart)?;
|
||||
// }}}
|
||||
// }}}
|
||||
// {{{ Deliver embed
|
||||
|
||||
let (embed, attachment) = timed!("to embed", {
|
||||
play.to_embed(&ctx.data().db, &user, &song, &chart, i, None)
|
||||
.await?
|
||||
play.to_embed(ctx.data(), &user, &song, &chart, i, None)?
|
||||
});
|
||||
|
||||
embeds.push(embed);
|
||||
|
@ -183,11 +180,14 @@ pub async fn delete(
|
|||
let mut count = 0;
|
||||
|
||||
for id in ids {
|
||||
let res = query!("DELETE FROM plays WHERE id=? AND user_id=?", id, user.id)
|
||||
.execute(&ctx.data().db)
|
||||
.await?;
|
||||
let res = ctx
|
||||
.data()
|
||||
.db
|
||||
.get()?
|
||||
.prepare_cached("DELETE FROM plays WHERE id=? AND user_id=?")?
|
||||
.execute((id, user.id))?;
|
||||
|
||||
if res.rows_affected() == 0 {
|
||||
if res == 0 {
|
||||
ctx.reply(format!("No play with id {} found", id)).await?;
|
||||
} else {
|
||||
count += 1;
|
||||
|
@ -216,36 +216,38 @@ pub async fn show(
|
|||
|
||||
let mut embeds = Vec::with_capacity(ids.len());
|
||||
let mut attachments = Vec::with_capacity(ids.len());
|
||||
let conn = ctx.data().db.get()?;
|
||||
for (i, id) in ids.iter().enumerate() {
|
||||
let res = query!(
|
||||
"
|
||||
SELECT
|
||||
p.id, p.chart_id, p.user_id, p.created_at,
|
||||
p.max_recall, p.far_notes, s.score,
|
||||
u.discord_id
|
||||
FROM plays p
|
||||
JOIN scores s ON s.play_id = p.id
|
||||
JOIN users u ON p.user_id = u.id
|
||||
WHERE s.scoring_system='standard'
|
||||
AND p.id=?
|
||||
ORDER BY s.score DESC
|
||||
LIMIT 1
|
||||
",
|
||||
id
|
||||
)
|
||||
.fetch_one(&ctx.data().db)
|
||||
.await
|
||||
.map_err(|_| format!("Could not find play with id {}", id))?;
|
||||
let (song, chart, play, discord_id) = conn
|
||||
.prepare_cached(
|
||||
"
|
||||
SELECT
|
||||
p.id, p.chart_id, p.user_id, p.created_at,
|
||||
p.max_recall, p.far_notes, s.score,
|
||||
u.discord_id
|
||||
FROM plays p
|
||||
JOIN scores s ON s.play_id = p.id
|
||||
JOIN users u ON p.user_id = u.id
|
||||
WHERE s.scoring_system='standard'
|
||||
AND p.id=?
|
||||
ORDER BY s.score DESC
|
||||
LIMIT 1
|
||||
",
|
||||
)?
|
||||
.query_and_then([id], |row| -> Result<_, Error> {
|
||||
let (song, chart) = ctx.data().song_cache.lookup_chart(row.get("chart_id")?)?;
|
||||
let play = Play::from_sql(chart, row)?;
|
||||
let discord_id = row.get::<_, String>("discord_id")?;
|
||||
Ok((song, chart, play, discord_id))
|
||||
})?
|
||||
.next()
|
||||
.ok_or_else(|| format!("Could not find play with id {}", id))??;
|
||||
|
||||
let (song, chart) = ctx.data().song_cache.lookup_chart(res.chart_id as u32)?;
|
||||
let play = play_from_db_record!(chart, res);
|
||||
let author = discord_id_to_discord_user(&ctx, &discord_id).await?;
|
||||
let user = User::by_id(ctx.data(), play.user_id)?;
|
||||
|
||||
let author = discord_it_to_discord_user(&ctx, &res.discord_id).await?;
|
||||
let user = User::by_id(&ctx.data().db, play.user_id).await?;
|
||||
|
||||
let (embed, attachment) = play
|
||||
.to_embed(&ctx.data().db, &user, song, chart, i, Some(&author))
|
||||
.await?;
|
||||
let (embed, attachment) =
|
||||
play.to_embed(ctx.data(), &user, song, chart, i, Some(&author))?;
|
||||
|
||||
embeds.push(embed);
|
||||
attachments.extend(attachment);
|
||||
|
|
|
@ -5,7 +5,6 @@ use poise::{
|
|||
serenity_prelude::{CreateAttachment, CreateEmbed},
|
||||
CreateReply,
|
||||
};
|
||||
use sqlx::query;
|
||||
|
||||
use crate::{
|
||||
arcaea::{
|
||||
|
@ -26,7 +25,7 @@ use crate::{
|
|||
context::{Context, Error},
|
||||
get_user,
|
||||
logs::debug_image_log,
|
||||
reply_errors,
|
||||
reply_errors, timed,
|
||||
user::User,
|
||||
};
|
||||
|
||||
|
@ -53,20 +52,20 @@ async fn best_plays(
|
|||
let user_ctx = ctx.data();
|
||||
let plays = reply_errors!(
|
||||
ctx,
|
||||
get_best_plays(
|
||||
&user_ctx.db,
|
||||
&user_ctx.song_cache,
|
||||
user.id,
|
||||
scoring_system,
|
||||
if require_full {
|
||||
grid_size.0 * grid_size.1
|
||||
} else {
|
||||
grid_size.0 * (grid_size.1.max(1) - 1) + 1
|
||||
} as usize,
|
||||
(grid_size.0 * grid_size.1) as usize,
|
||||
None
|
||||
)
|
||||
.await?
|
||||
timed!("get_best_plays", {
|
||||
get_best_plays(
|
||||
user_ctx,
|
||||
user.id,
|
||||
scoring_system,
|
||||
if require_full {
|
||||
grid_size.0 * grid_size.1
|
||||
} else {
|
||||
grid_size.0 * (grid_size.1.max(1) - 1) + 1
|
||||
} as usize,
|
||||
(grid_size.0 * grid_size.1) as usize,
|
||||
None,
|
||||
)?
|
||||
})
|
||||
);
|
||||
|
||||
// {{{ Layout
|
||||
|
@ -463,48 +462,42 @@ pub async fn bany(
|
|||
#[poise::command(prefix_command, slash_command, user_cooldown = 1)]
|
||||
async fn meta(ctx: Context<'_>) -> Result<(), Error> {
|
||||
let user = get_user!(&ctx);
|
||||
let song_count = query!("SELECT count() as count FROM songs")
|
||||
.fetch_one(&ctx.data().db)
|
||||
.await?
|
||||
.count;
|
||||
let conn = ctx.data().db.get()?;
|
||||
let song_count: usize = conn
|
||||
.prepare_cached("SELECT count() as count FROM songs")?
|
||||
.query_row((), |row| row.get(0))?;
|
||||
|
||||
let chart_count = query!("SELECT count() as count FROM charts")
|
||||
.fetch_one(&ctx.data().db)
|
||||
.await?
|
||||
.count;
|
||||
let chart_count: usize = conn
|
||||
.prepare_cached("SELECT count() as count FROM charts")?
|
||||
.query_row((), |row| row.get(0))?;
|
||||
|
||||
let users_count = query!("SELECT count() as count FROM users")
|
||||
.fetch_one(&ctx.data().db)
|
||||
.await?
|
||||
.count;
|
||||
let users_count: usize = conn
|
||||
.prepare_cached("SELECT count() as count FROM users")?
|
||||
.query_row((), |row| row.get(0))?;
|
||||
|
||||
let pookie_count = query!(
|
||||
"
|
||||
SELECT count() as count
|
||||
FROM users
|
||||
WHERE is_pookie=1
|
||||
"
|
||||
)
|
||||
.fetch_one(&ctx.data().db)
|
||||
.await?
|
||||
.count;
|
||||
let pookie_count: usize = conn
|
||||
.prepare_cached(
|
||||
"
|
||||
SELECT count() as count
|
||||
FROM users
|
||||
WHERE is_pookie=1
|
||||
",
|
||||
)?
|
||||
.query_row((), |row| row.get(0))?;
|
||||
|
||||
let play_count = query!("SELECT count() as count FROM plays")
|
||||
.fetch_one(&ctx.data().db)
|
||||
.await?
|
||||
.count;
|
||||
let play_count: usize = conn
|
||||
.prepare_cached("SELECT count() as count FROM plays")?
|
||||
.query_row((), |row| row.get(0))?;
|
||||
|
||||
let your_play_count = query!(
|
||||
"
|
||||
let your_play_count: usize = conn
|
||||
.prepare_cached(
|
||||
"
|
||||
SELECT count() as count
|
||||
FROM plays
|
||||
WHERE user_id=?
|
||||
",
|
||||
user.id
|
||||
)
|
||||
.fetch_one(&ctx.data().db)
|
||||
.await?
|
||||
.count;
|
||||
",
|
||||
)?
|
||||
.query_row([user.id], |row| row.get(0))?;
|
||||
|
||||
let embed = CreateEmbed::default()
|
||||
.title("Bot statistics")
|
||||
|
|
|
@ -14,7 +14,7 @@ macro_rules! edit_reply {
|
|||
#[macro_export]
|
||||
macro_rules! get_user {
|
||||
($ctx:expr) => {{
|
||||
crate::reply_errors!($ctx, crate::user::User::from_context($ctx).await)
|
||||
crate::reply_errors!($ctx, crate::user::User::from_context($ctx))
|
||||
}};
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue