mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-19 16:58:55 +00:00
osu: make commands aware of user's preferred mode (#54)
* Add preferred_mode to sql database * Update username and preferred mode * Make commands aware of preferred mode * Fetch user extras to display information * Show user information on forcesave
This commit is contained in:
parent
c5354e30ad
commit
7d490774e0
37 changed files with 410 additions and 170 deletions
44
.sqlx/query-25dcc59341e6375ee6a55aa014aecc54be42e1e8787ae5a06a61bb8ba8e9c366.json
generated
Normal file
44
.sqlx/query-25dcc59341e6375ee6a55aa014aecc54be42e1e8787ae5a06a61bb8ba8e9c366.json
generated
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "SELECT\n user_id as \"user_id: i64\",\n username,\n id as \"id: i64\",\n preferred_mode as \"preferred_mode: u8\",\n failures as \"failures: u8\"\n FROM osu_users WHERE user_id = ?",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "user_id: i64",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "username",
|
||||||
|
"ordinal": 1,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id: i64",
|
||||||
|
"ordinal": 2,
|
||||||
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "preferred_mode: u8",
|
||||||
|
"ordinal": 3,
|
||||||
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "failures: u8",
|
||||||
|
"ordinal": 4,
|
||||||
|
"type_info": "Int64"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 1
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "25dcc59341e6375ee6a55aa014aecc54be42e1e8787ae5a06a61bb8ba8e9c366"
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"db_name": "SQLite",
|
"db_name": "SQLite",
|
||||||
"query": "SELECT\n user_id as \"user_id: i64\",\n username,\n id as \"id: i64\",\n failures as \"failures: u8\"\n FROM osu_users",
|
"query": "SELECT\n user_id as \"user_id: i64\",\n username,\n id as \"id: i64\",\n preferred_mode as \"preferred_mode: u8\",\n failures as \"failures: u8\"\n FROM osu_users",
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
|
@ -19,9 +19,14 @@
|
||||||
"type_info": "Int64"
|
"type_info": "Int64"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "failures: u8",
|
"name": "preferred_mode: u8",
|
||||||
"ordinal": 3,
|
"ordinal": 3,
|
||||||
"type_info": "Int64"
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "failures: u8",
|
||||||
|
"ordinal": 4,
|
||||||
|
"type_info": "Int64"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
|
@ -31,8 +36,9 @@
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
false
|
false
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"hash": "246e26a34c042872a77f53a84d62da31db069cced20e3b0f96a40c3c7dd99783"
|
"hash": "54f54f669244fbdf1ad68664290d8f32f0bda74ceee62d10c84ac03b710c828c"
|
||||||
}
|
}
|
12
.sqlx/query-b9c63ef764711088cfbb58ce2ed1f46e3521357ec1d7c062bef762d3e4267378.json
generated
Normal file
12
.sqlx/query-b9c63ef764711088cfbb58ce2ed1f46e3521357ec1d7c062bef762d3e4267378.json
generated
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "INSERT\n INTO osu_users(user_id, username, id, preferred_mode, failures)\n VALUES(?, ?, ?, ?, ?)\n ON CONFLICT (user_id) WHERE id = ? DO UPDATE\n SET\n username = excluded.username,\n preferred_mode = excluded.preferred_mode,\n failures = excluded.failures\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 6
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "b9c63ef764711088cfbb58ce2ed1f46e3521357ec1d7c062bef762d3e4267378"
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"db_name": "SQLite",
|
"db_name": "SQLite",
|
||||||
"query": "SELECT\n user_id as \"user_id: i64\",\n username,\n id as \"id: i64\",\n failures as \"failures: u8\"\n FROM osu_users WHERE id = ?",
|
"query": "SELECT\n user_id as \"user_id: i64\",\n username,\n id as \"id: i64\",\n preferred_mode as \"preferred_mode: u8\",\n failures as \"failures: u8\"\n FROM osu_users WHERE id = ?",
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
|
@ -19,9 +19,14 @@
|
||||||
"type_info": "Int64"
|
"type_info": "Int64"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "failures: u8",
|
"name": "preferred_mode: u8",
|
||||||
"ordinal": 3,
|
"ordinal": 3,
|
||||||
"type_info": "Int64"
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "failures: u8",
|
||||||
|
"ordinal": 4,
|
||||||
|
"type_info": "Int64"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
|
@ -31,8 +36,9 @@
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
false
|
false
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"hash": "9b7788f4d7144fe00f4bc9004c88dc8562ff3d7a931fc3f1dc039cc55fe3195a"
|
"hash": "e313b2e91d0da80a94e9a1030f94cf30b025c9ac0ac4d5bcc12656459c5e083a"
|
||||||
}
|
}
|
44
youmubot-db-sql/.sqlx/query-25dcc59341e6375ee6a55aa014aecc54be42e1e8787ae5a06a61bb8ba8e9c366.json
generated
Normal file
44
youmubot-db-sql/.sqlx/query-25dcc59341e6375ee6a55aa014aecc54be42e1e8787ae5a06a61bb8ba8e9c366.json
generated
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "SELECT\n user_id as \"user_id: i64\",\n username,\n id as \"id: i64\",\n preferred_mode as \"preferred_mode: u8\",\n failures as \"failures: u8\"\n FROM osu_users WHERE user_id = ?",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "user_id: i64",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "username",
|
||||||
|
"ordinal": 1,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id: i64",
|
||||||
|
"ordinal": 2,
|
||||||
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "preferred_mode: u8",
|
||||||
|
"ordinal": 3,
|
||||||
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "failures: u8",
|
||||||
|
"ordinal": 4,
|
||||||
|
"type_info": "Int64"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 1
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "25dcc59341e6375ee6a55aa014aecc54be42e1e8787ae5a06a61bb8ba8e9c366"
|
||||||
|
}
|
44
youmubot-db-sql/.sqlx/query-54f54f669244fbdf1ad68664290d8f32f0bda74ceee62d10c84ac03b710c828c.json
generated
Normal file
44
youmubot-db-sql/.sqlx/query-54f54f669244fbdf1ad68664290d8f32f0bda74ceee62d10c84ac03b710c828c.json
generated
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "SELECT\n user_id as \"user_id: i64\",\n username,\n id as \"id: i64\",\n preferred_mode as \"preferred_mode: u8\",\n failures as \"failures: u8\"\n FROM osu_users",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "user_id: i64",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "username",
|
||||||
|
"ordinal": 1,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id: i64",
|
||||||
|
"ordinal": 2,
|
||||||
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "preferred_mode: u8",
|
||||||
|
"ordinal": 3,
|
||||||
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "failures: u8",
|
||||||
|
"ordinal": 4,
|
||||||
|
"type_info": "Int64"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 0
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "54f54f669244fbdf1ad68664290d8f32f0bda74ceee62d10c84ac03b710c828c"
|
||||||
|
}
|
|
@ -1,12 +0,0 @@
|
||||||
{
|
|
||||||
"db_name": "SQLite",
|
|
||||||
"query": "INSERT\n INTO osu_users(user_id, username, id, failures)\n VALUES(?, ?, ?, ?)\n ON CONFLICT (user_id) WHERE id = ? DO UPDATE\n SET\n username = excluded.username,\n failures = excluded.failures\n ",
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"parameters": {
|
|
||||||
"Right": 5
|
|
||||||
},
|
|
||||||
"nullable": []
|
|
||||||
},
|
|
||||||
"hash": "a5d8dccaaf80b2673c5c0e689c01a90861788ca84221baaaf19cd159ed3062c9"
|
|
||||||
}
|
|
12
youmubot-db-sql/.sqlx/query-b9c63ef764711088cfbb58ce2ed1f46e3521357ec1d7c062bef762d3e4267378.json
generated
Normal file
12
youmubot-db-sql/.sqlx/query-b9c63ef764711088cfbb58ce2ed1f46e3521357ec1d7c062bef762d3e4267378.json
generated
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "INSERT\n INTO osu_users(user_id, username, id, preferred_mode, failures)\n VALUES(?, ?, ?, ?, ?)\n ON CONFLICT (user_id) WHERE id = ? DO UPDATE\n SET\n username = excluded.username,\n preferred_mode = excluded.preferred_mode,\n failures = excluded.failures\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 6
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "b9c63ef764711088cfbb58ce2ed1f46e3521357ec1d7c062bef762d3e4267378"
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"db_name": "SQLite",
|
"db_name": "SQLite",
|
||||||
"query": "SELECT\n user_id as \"user_id: i64\",\n username,\n id as \"id: i64\",\n failures as \"failures: u8\"\n FROM osu_users WHERE user_id = ?",
|
"query": "SELECT\n user_id as \"user_id: i64\",\n username,\n id as \"id: i64\",\n preferred_mode as \"preferred_mode: u8\",\n failures as \"failures: u8\"\n FROM osu_users WHERE id = ?",
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
|
@ -19,9 +19,14 @@
|
||||||
"type_info": "Int64"
|
"type_info": "Int64"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "failures: u8",
|
"name": "preferred_mode: u8",
|
||||||
"ordinal": 3,
|
"ordinal": 3,
|
||||||
"type_info": "Int64"
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "failures: u8",
|
||||||
|
"ordinal": 4,
|
||||||
|
"type_info": "Int64"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
|
@ -31,8 +36,9 @@
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
false
|
false
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"hash": "ae7f57eb92e0bec8439e682ab3ca10732991ffe803d05b09e908ecb4a74c0566"
|
"hash": "e313b2e91d0da80a94e9a1030f94cf30b025c9ac0ac4d5bcc12656459c5e083a"
|
||||||
}
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
-- Add migration script here
|
||||||
|
|
||||||
|
ALTER TABLE osu_users
|
||||||
|
ADD COLUMN preferred_mode INT NOT NULL DEFAULT 0 CHECK (preferred_mode >= 0 AND preferred_mode < 4);
|
|
@ -9,6 +9,7 @@ pub struct OsuUser {
|
||||||
pub username: Option<String>, // should always be there
|
pub username: Option<String>, // should always be there
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
pub modes: Map<u8, OsuUserMode>,
|
pub modes: Map<u8, OsuUserMode>,
|
||||||
|
pub preferred_mode: u8,
|
||||||
/// Number of consecutive update failures
|
/// Number of consecutive update failures
|
||||||
pub failures: u8,
|
pub failures: u8,
|
||||||
}
|
}
|
||||||
|
@ -97,6 +98,7 @@ mod raw {
|
||||||
pub user_id: i64,
|
pub user_id: i64,
|
||||||
pub username: Option<String>, // should always be there
|
pub username: Option<String>, // should always be there
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
|
pub preferred_mode: u8,
|
||||||
pub failures: u8,
|
pub failures: u8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,6 +110,7 @@ impl OsuUser {
|
||||||
username: r.username,
|
username: r.username,
|
||||||
id: r.id,
|
id: r.id,
|
||||||
modes,
|
modes,
|
||||||
|
preferred_mode: r.preferred_mode,
|
||||||
failures: r.failures,
|
failures: r.failures,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,6 +122,7 @@ impl OsuUser {
|
||||||
user_id as "user_id: i64",
|
user_id as "user_id: i64",
|
||||||
username,
|
username,
|
||||||
id as "id: i64",
|
id as "id: i64",
|
||||||
|
preferred_mode as "preferred_mode: u8",
|
||||||
failures as "failures: u8"
|
failures as "failures: u8"
|
||||||
FROM osu_users WHERE user_id = ?"#,
|
FROM osu_users WHERE user_id = ?"#,
|
||||||
user_id
|
user_id
|
||||||
|
@ -141,6 +145,7 @@ impl OsuUser {
|
||||||
user_id as "user_id: i64",
|
user_id as "user_id: i64",
|
||||||
username,
|
username,
|
||||||
id as "id: i64",
|
id as "id: i64",
|
||||||
|
preferred_mode as "preferred_mode: u8",
|
||||||
failures as "failures: u8"
|
failures as "failures: u8"
|
||||||
FROM osu_users WHERE id = ?"#,
|
FROM osu_users WHERE id = ?"#,
|
||||||
osu_id
|
osu_id
|
||||||
|
@ -164,6 +169,7 @@ impl OsuUser {
|
||||||
user_id as "user_id: i64",
|
user_id as "user_id: i64",
|
||||||
username,
|
username,
|
||||||
id as "id: i64",
|
id as "id: i64",
|
||||||
|
preferred_mode as "preferred_mode: u8",
|
||||||
failures as "failures: u8"
|
failures as "failures: u8"
|
||||||
FROM osu_users"#,
|
FROM osu_users"#,
|
||||||
)
|
)
|
||||||
|
@ -200,16 +206,18 @@ impl OsuUser {
|
||||||
|
|
||||||
query!(
|
query!(
|
||||||
r#"INSERT
|
r#"INSERT
|
||||||
INTO osu_users(user_id, username, id, failures)
|
INTO osu_users(user_id, username, id, preferred_mode, failures)
|
||||||
VALUES(?, ?, ?, ?)
|
VALUES(?, ?, ?, ?, ?)
|
||||||
ON CONFLICT (user_id) WHERE id = ? DO UPDATE
|
ON CONFLICT (user_id) WHERE id = ? DO UPDATE
|
||||||
SET
|
SET
|
||||||
username = excluded.username,
|
username = excluded.username,
|
||||||
|
preferred_mode = excluded.preferred_mode,
|
||||||
failures = excluded.failures
|
failures = excluded.failures
|
||||||
"#,
|
"#,
|
||||||
self.user_id,
|
self.user_id,
|
||||||
self.username,
|
self.username,
|
||||||
self.id,
|
self.id,
|
||||||
|
self.preferred_mode,
|
||||||
self.failures,
|
self.failures,
|
||||||
self.user_id,
|
self.user_id,
|
||||||
)
|
)
|
||||||
|
|
|
@ -122,6 +122,10 @@ impl Announcer {
|
||||||
.unwrap_or(0),
|
.unwrap_or(0),
|
||||||
last_update: now,
|
last_update: now,
|
||||||
};
|
};
|
||||||
|
if u.username != user.username {
|
||||||
|
user.username = u.username.clone().into();
|
||||||
|
}
|
||||||
|
user.preferred_mode = u.preferred_mode;
|
||||||
let last = user.modes.insert(mode, stats);
|
let last = user.modes.insert(mode, stats);
|
||||||
|
|
||||||
// broadcast
|
// broadcast
|
||||||
|
|
|
@ -113,6 +113,7 @@ pub struct OsuUser {
|
||||||
pub username: Cow<'static, str>,
|
pub username: Cow<'static, str>,
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
pub modes: Map<Mode, OsuUserMode>,
|
pub modes: Map<Mode, OsuUserMode>,
|
||||||
|
pub preferred_mode: Mode,
|
||||||
/// More than 5 failures => gone
|
/// More than 5 failures => gone
|
||||||
pub failures: u8,
|
pub failures: u8,
|
||||||
}
|
}
|
||||||
|
@ -136,6 +137,7 @@ impl From<OsuUser> for model::OsuUser {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, v)| (k as u8, v.into()))
|
.map(|(k, v)| (k as u8, v.into()))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
preferred_mode: u.preferred_mode as u8,
|
||||||
failures: u.failures,
|
failures: u.failures,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,11 +154,21 @@ impl From<model::OsuUser> for OsuUser {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, v)| (k.into(), v.into()))
|
.map(|(k, v)| (k.into(), v.into()))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
preferred_mode: u.preferred_mode.into(),
|
||||||
failures: u.failures,
|
failures: u.failures,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<OsuUser> for crate::models::UserHeader {
|
||||||
|
fn from(value: OsuUser) -> Self {
|
||||||
|
Self {
|
||||||
|
id: value.id as u64,
|
||||||
|
username: value.username.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<OsuUserMode> for model::OsuUserMode {
|
impl From<OsuUserMode> for model::OsuUserMode {
|
||||||
fn from(m: OsuUserMode) -> Self {
|
fn from(m: OsuUserMode) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use super::BeatmapWithMode;
|
use super::{BeatmapWithMode, UserExtras};
|
||||||
use crate::{
|
use crate::{
|
||||||
discord::oppai_cache::{Accuracy, BeatmapContent, BeatmapInfo, BeatmapInfoWithPP},
|
discord::oppai_cache::{Accuracy, BeatmapContent, BeatmapInfoWithPP},
|
||||||
models::{Beatmap, Difficulty, Mode, Mods, Rank, Score, User},
|
models::{Beatmap, Difficulty, Mode, Mods, Rank, Score, User},
|
||||||
UserHeader,
|
UserHeader,
|
||||||
};
|
};
|
||||||
|
@ -479,13 +479,13 @@ impl<'a> ScoreEmbedBuilder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn user_embed(
|
pub(crate) fn user_embed(u: User, ex: UserExtras) -> CreateEmbed {
|
||||||
u: User,
|
|
||||||
map_length: f64,
|
|
||||||
map_age: i64,
|
|
||||||
best: Option<(Score, BeatmapWithMode, BeatmapInfo)>,
|
|
||||||
) -> CreateEmbed {
|
|
||||||
let mut stats = Vec::<(&'static str, String, bool)>::new();
|
let mut stats = Vec::<(&'static str, String, bool)>::new();
|
||||||
|
let UserExtras {
|
||||||
|
map_length,
|
||||||
|
map_age,
|
||||||
|
best_score: best,
|
||||||
|
} = ex;
|
||||||
if map_length > 0.0 {
|
if map_length > 0.0 {
|
||||||
stats.push((
|
stats.push((
|
||||||
"Weighted Map Length",
|
"Weighted Map Length",
|
||||||
|
|
|
@ -8,7 +8,7 @@ use serenity::all::{
|
||||||
};
|
};
|
||||||
use youmubot_prelude::*;
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
use crate::Mods;
|
use crate::{Mods, UserHeader};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
display::ScoreListStyle,
|
display::ScoreListStyle,
|
||||||
|
@ -70,8 +70,9 @@ pub fn handle_check_button<'a>(
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let header = UserHeader::from(user.clone());
|
||||||
|
|
||||||
let scores = super::do_check(&env, &bm, Mods::NOMOD, &crate::UserID::ID(user.id)).await?;
|
let scores = super::do_check(&env, &bm, Mods::NOMOD, &header).await?;
|
||||||
if scores.is_empty() {
|
if scores.is_empty() {
|
||||||
comp.create_followup(
|
comp.create_followup(
|
||||||
&ctx,
|
&ctx,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::{borrow::Borrow, collections::HashMap as Map, str::FromStr, sync::Arc};
|
use std::{borrow::Borrow, collections::HashMap as Map, str::FromStr, sync::Arc};
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use future::try_join;
|
|
||||||
use futures_util::join;
|
use futures_util::join;
|
||||||
use interaction::{beatmap_components, score_components};
|
use interaction::{beatmap_components, score_components};
|
||||||
use rand::seq::IteratorRandom;
|
use rand::seq::IteratorRandom;
|
||||||
|
@ -33,7 +32,7 @@ use crate::{
|
||||||
models::{Beatmap, Mode, Mods, Score, User},
|
models::{Beatmap, Mode, Mods, Score, User},
|
||||||
mods::UnparsedMods,
|
mods::UnparsedMods,
|
||||||
request::{BeatmapRequestKind, UserID},
|
request::{BeatmapRequestKind, UserID},
|
||||||
OsuClient as OsuHttpClient,
|
OsuClient as OsuHttpClient, UserHeader,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod announcer;
|
mod announcer;
|
||||||
|
@ -141,6 +140,7 @@ pub async fn setup(
|
||||||
#[prefix = "osu"]
|
#[prefix = "osu"]
|
||||||
#[description = "osu! related commands."]
|
#[description = "osu! related commands."]
|
||||||
#[commands(
|
#[commands(
|
||||||
|
user,
|
||||||
std,
|
std,
|
||||||
taiko,
|
taiko,
|
||||||
catch,
|
catch,
|
||||||
|
@ -156,9 +156,19 @@ pub async fn setup(
|
||||||
show_leaderboard,
|
show_leaderboard,
|
||||||
clean_cache
|
clean_cache
|
||||||
)]
|
)]
|
||||||
#[default_command(std)]
|
#[default_command(user)]
|
||||||
|
|
||||||
struct Osu;
|
struct Osu;
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
#[description = "Receive information about an user in their preferred mode."]
|
||||||
|
#[usage = "[username or user_id = your saved username]"]
|
||||||
|
#[max_args(1)]
|
||||||
|
pub async fn user(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
|
let env = ctx.data.read().await.get::<OsuEnv>().unwrap().clone();
|
||||||
|
get_user(ctx, &env, msg, args, None).await
|
||||||
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[aliases("osu", "osu!")]
|
#[aliases("osu", "osu!")]
|
||||||
#[description = "Receive information about an user in osu!std mode."]
|
#[description = "Receive information about an user in osu!std mode."]
|
||||||
|
@ -235,7 +245,13 @@ pub async fn save(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
async fn find_score(client: &OsuHttpClient, u: &User) -> Result<Option<(Score, Mode)>> {
|
async fn find_score(client: &OsuHttpClient, u: &User) -> Result<Option<(Score, Mode)>> {
|
||||||
for mode in &[Mode::Std, Mode::Taiko, Mode::Catch, Mode::Mania] {
|
for mode in &[
|
||||||
|
u.preferred_mode,
|
||||||
|
Mode::Std,
|
||||||
|
Mode::Taiko,
|
||||||
|
Mode::Catch,
|
||||||
|
Mode::Mania,
|
||||||
|
] {
|
||||||
let scores = client
|
let scores = client
|
||||||
.user_best(UserID::ID(u.id), |f| f.mode(*mode))
|
.user_best(UserID::ID(u.id), |f| f.mode(*mode))
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -269,10 +285,11 @@ pub async fn save(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
|
||||||
let reply = msg.reply(
|
let reply = msg.reply(
|
||||||
&ctx,
|
&ctx,
|
||||||
format!(
|
format!(
|
||||||
"To set your osu username, please make your most recent play \
|
"To set your osu username to **{}**, please make your most recent play \
|
||||||
be the following map: `/b/{}` in **{}** mode! \
|
be the following map: `/b/{}` in **{}** mode! \
|
||||||
It does **not** have to be a pass, and **NF** can be used! \
|
It does **not** have to be a pass, and **NF** can be used! \
|
||||||
React to this message with 👌 within 5 minutes when you're done!",
|
React to this message with 👌 within 5 minutes when you're done!",
|
||||||
|
u.username,
|
||||||
score.beatmap_id,
|
score.beatmap_id,
|
||||||
mode.as_str_new_site()
|
mode.as_str_new_site()
|
||||||
),
|
),
|
||||||
|
@ -306,7 +323,7 @@ pub async fn save(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
|
||||||
.message_id(reply.id)
|
.message_id(reply.id)
|
||||||
.author_id(msg.author.id)
|
.author_id(msg.author.id)
|
||||||
.filter(move |r| r.emoji == emoji)
|
.filter(move |r| r.emoji == emoji)
|
||||||
.timeout(std::time::Duration::from_secs(300))
|
.timeout(std::time::Duration::from_secs(300) + beatmap.difficulty.total_length)
|
||||||
.next()
|
.next()
|
||||||
.await;
|
.await;
|
||||||
if let Some(ur) = user_reaction {
|
if let Some(ur) = user_reaction {
|
||||||
|
@ -319,20 +336,41 @@ pub async fn save(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if !completed {
|
if !completed {
|
||||||
|
reply
|
||||||
|
.edit(
|
||||||
|
&ctx,
|
||||||
|
EditMessage::new()
|
||||||
|
.content(format!(
|
||||||
|
"Setting username to **{}** failed due to timeout. Please try again!",
|
||||||
|
u.username
|
||||||
|
))
|
||||||
|
.embeds(vec![])
|
||||||
|
.components(vec![]),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
reaction.delete(&ctx).await?;
|
reaction.delete(&ctx).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let username = u.username.clone();
|
let username = u.username.clone();
|
||||||
add_user(msg.author.id, u, &env).await?;
|
add_user(msg.author.id, &u, &env).await?;
|
||||||
msg.reply(
|
let ex = UserExtras::from_user(&env, &u, mode).await?;
|
||||||
&ctx,
|
msg.channel_id
|
||||||
MessageBuilder::new()
|
.send_message(
|
||||||
.push("user has been set to ")
|
&ctx,
|
||||||
.push_mono_safe(username)
|
CreateMessage::new()
|
||||||
.build(),
|
.reference_message(msg)
|
||||||
)
|
.content(
|
||||||
.await?;
|
MessageBuilder::new()
|
||||||
|
.push("Youmu is now tracking user ")
|
||||||
|
.push(msg.author.mention().to_string())
|
||||||
|
.push(" with osu! account ")
|
||||||
|
.push_bold_safe(username)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.add_embed(user_embed(u, ex)),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,15 +393,24 @@ pub async fn forcesave(ctx: &Context, msg: &Message, mut args: Args) -> CommandR
|
||||||
.await?;
|
.await?;
|
||||||
match user {
|
match user {
|
||||||
Some(u) => {
|
Some(u) => {
|
||||||
add_user(target, u, &env).await?;
|
add_user(target, &u, &env).await?;
|
||||||
msg.reply(
|
let ex = UserExtras::from_user(&env, &u, u.preferred_mode).await?;
|
||||||
&ctx,
|
msg.channel_id
|
||||||
MessageBuilder::new()
|
.send_message(
|
||||||
.push("user has been set to ")
|
&ctx,
|
||||||
.push_mono_safe(username)
|
CreateMessage::new()
|
||||||
.build(),
|
.reference_message(msg)
|
||||||
)
|
.content(
|
||||||
.await?;
|
MessageBuilder::new()
|
||||||
|
.push("Youmu is now tracking user ")
|
||||||
|
.push(target.mention().to_string())
|
||||||
|
.push(" with osu! account ")
|
||||||
|
.push_bold_safe(username)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.embed(user_embed(u, ex)),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
msg.reply(&ctx, "user not found...").await?;
|
msg.reply(&ctx, "user not found...").await?;
|
||||||
|
@ -372,49 +419,34 @@ pub async fn forcesave(ctx: &Context, msg: &Message, mut args: Args) -> CommandR
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_user(target: serenity::model::id::UserId, user: User, env: &OsuEnv) -> Result<()> {
|
async fn add_user(target: serenity::model::id::UserId, user: &User, env: &OsuEnv) -> Result<()> {
|
||||||
let modes = [Mode::Std, Mode::Taiko, Mode::Catch, Mode::Mania]
|
let modes = [Mode::Std, Mode::Taiko, Mode::Catch, Mode::Mania]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mode| async move {
|
.map(|mode| {
|
||||||
let pp = async {
|
let mode = mode.clone();
|
||||||
env.client
|
async move {
|
||||||
.user(&UserID::ID(user.id), |f| f.mode(mode))
|
let pp = async {
|
||||||
.await
|
env.client
|
||||||
.pls_ok()
|
.user(&UserID::ID(user.id), |f| f.mode(mode))
|
||||||
.unwrap_or(None)
|
|
||||||
.and_then(|u| u.pp)
|
|
||||||
};
|
|
||||||
let map_length_age = async {
|
|
||||||
let scores = env
|
|
||||||
.client
|
|
||||||
.user_best(UserID::ID(user.id), |f| f.mode(mode).limit(100))
|
|
||||||
.await
|
|
||||||
.pls_ok()
|
|
||||||
.unwrap_or_else(std::vec::Vec::new);
|
|
||||||
|
|
||||||
(
|
|
||||||
calculate_weighted_map_length(&scores, &env.beatmaps, mode)
|
|
||||||
.await
|
.await
|
||||||
.pls_ok(),
|
.pls_ok()
|
||||||
calculate_weighted_map_age(&scores, &env.beatmaps, mode)
|
.unwrap_or(None)
|
||||||
.await
|
.and_then(|u| u.pp)
|
||||||
.pls_ok(),
|
};
|
||||||
)
|
let map_length_age = UserExtras::from_user(env, user, mode);
|
||||||
};
|
let (pp, ex) = join!(pp, map_length_age);
|
||||||
let (pp, (map_length, map_age)) = join!(pp, map_length_age);
|
pp.zip(ex.ok()).map(|(pp, ex)| {
|
||||||
pp.zip(map_length)
|
|
||||||
.zip(map_age)
|
|
||||||
.map(|((pp, map_length), map_age)| {
|
|
||||||
(
|
(
|
||||||
mode,
|
mode,
|
||||||
OsuUserMode {
|
OsuUserMode {
|
||||||
pp,
|
pp,
|
||||||
map_length,
|
map_length: ex.map_length,
|
||||||
map_age,
|
map_age: ex.map_age,
|
||||||
last_update: Utc::now(),
|
last_update: Utc::now(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect::<stream::FuturesOrdered<_>>()
|
.collect::<stream::FuturesOrdered<_>>()
|
||||||
.filter_map(future::ready)
|
.filter_map(future::ready)
|
||||||
|
@ -423,7 +455,8 @@ async fn add_user(target: serenity::model::id::UserId, user: User, env: &OsuEnv)
|
||||||
|
|
||||||
let u = OsuUser {
|
let u = OsuUser {
|
||||||
user_id: target,
|
user_id: target,
|
||||||
username: user.username.into(),
|
username: user.username.clone().into(),
|
||||||
|
preferred_mode: user.preferred_mode,
|
||||||
id: user.id,
|
id: user.id,
|
||||||
failures: 0,
|
failures: 0,
|
||||||
modes,
|
modes,
|
||||||
|
@ -432,6 +465,47 @@ async fn add_user(target: serenity::model::id::UserId, user: User, env: &OsuEnv)
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stores extra information to create an user embed.
|
||||||
|
pub(crate) struct UserExtras {
|
||||||
|
pub map_length: f64,
|
||||||
|
pub map_age: i64,
|
||||||
|
pub best_score: Option<(Score, BeatmapWithMode, BeatmapInfo)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserExtras {
|
||||||
|
// Collect UserExtras from the given user.
|
||||||
|
pub async fn from_user(env: &OsuEnv, user: &User, mode: Mode) -> Result<Self> {
|
||||||
|
let scores = env
|
||||||
|
.client
|
||||||
|
.user_best(UserID::ID(user.id), |f| f.mode(mode).limit(100))
|
||||||
|
.await
|
||||||
|
.pls_ok()
|
||||||
|
.unwrap_or_else(std::vec::Vec::new);
|
||||||
|
|
||||||
|
let (length, age) = join!(
|
||||||
|
calculate_weighted_map_length(&scores, &env.beatmaps, mode),
|
||||||
|
calculate_weighted_map_age(&scores, &env.beatmaps, mode)
|
||||||
|
);
|
||||||
|
let best = if let Some(s) = scores.into_iter().next() {
|
||||||
|
let beatmap = env.beatmaps.get_beatmap(s.beatmap_id, mode).await?;
|
||||||
|
let info = env
|
||||||
|
.oppai
|
||||||
|
.get_beatmap(s.beatmap_id)
|
||||||
|
.await?
|
||||||
|
.get_info_with(mode, &s.mods)?;
|
||||||
|
Some((s, BeatmapWithMode(beatmap, mode), info))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
map_length: length.unwrap_or(0.0),
|
||||||
|
map_age: age.unwrap_or(0),
|
||||||
|
best_score: best,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct ModeArg(Mode);
|
struct ModeArg(Mode);
|
||||||
|
|
||||||
|
@ -448,24 +522,6 @@ impl FromStr for ModeArg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn to_user_id_query(
|
|
||||||
s: Option<UsernameArg>,
|
|
||||||
env: &OsuEnv,
|
|
||||||
author: serenity::all::UserId,
|
|
||||||
) -> Result<UserID, Error> {
|
|
||||||
let id = match s {
|
|
||||||
Some(UsernameArg::Raw(s)) => return Ok(UserID::from_string(s)),
|
|
||||||
Some(UsernameArg::Tagged(r)) => r,
|
|
||||||
None => author,
|
|
||||||
};
|
|
||||||
|
|
||||||
env.saved_users
|
|
||||||
.by_user_id(id)
|
|
||||||
.await?
|
|
||||||
.map(|u| UserID::Username(u.username.to_string()))
|
|
||||||
.ok_or_else(|| Error::msg("No saved account found"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
enum Nth {
|
enum Nth {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -496,7 +552,7 @@ struct ListingArgs {
|
||||||
pub nth: Nth,
|
pub nth: Nth,
|
||||||
pub style: ScoreListStyle,
|
pub style: ScoreListStyle,
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
pub user: UserID,
|
pub user: UserHeader,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ListingArgs {
|
impl ListingArgs {
|
||||||
|
@ -508,13 +564,10 @@ impl ListingArgs {
|
||||||
) -> Result<ListingArgs> {
|
) -> Result<ListingArgs> {
|
||||||
let nth = args.single::<Nth>().unwrap_or(Nth::All);
|
let nth = args.single::<Nth>().unwrap_or(Nth::All);
|
||||||
let style = args.single::<ScoreListStyle>().unwrap_or(default_style);
|
let style = args.single::<ScoreListStyle>().unwrap_or(default_style);
|
||||||
let mode = args.single::<ModeArg>().unwrap_or(ModeArg(Mode::Std)).0;
|
let mode_override = args.single::<ModeArg>().map(|v| v.0).ok();
|
||||||
let user = to_user_id_query(
|
let (mode, user) =
|
||||||
args.quoted().trimmed().single::<UsernameArg>().ok(),
|
user_header_from_args(args.single::<UsernameArg>().ok(), env, msg).await?;
|
||||||
&env,
|
let mode = mode_override.unwrap_or(mode);
|
||||||
msg.author.id,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
nth,
|
nth,
|
||||||
style,
|
style,
|
||||||
|
@ -524,6 +577,35 @@ impl ListingArgs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn user_header_from_args(
|
||||||
|
arg: Option<UsernameArg>,
|
||||||
|
env: &OsuEnv,
|
||||||
|
msg: &Message,
|
||||||
|
) -> Result<(Mode, UserHeader)> {
|
||||||
|
let (mode, user) = match arg {
|
||||||
|
Some(UsernameArg::Raw(r)) => {
|
||||||
|
let user = env
|
||||||
|
.client
|
||||||
|
.user(&UserID::Username(r), |f| f)
|
||||||
|
.await?
|
||||||
|
.ok_or(Error::msg("User not found"))?;
|
||||||
|
(user.preferred_mode, user.into())
|
||||||
|
}
|
||||||
|
Some(UsernameArg::Tagged(t)) => {
|
||||||
|
let user = env.saved_users.by_user_id(t).await?.ok_or_else(|| {
|
||||||
|
Error::msg(format!("{} does not have a saved account!", t.mention()))
|
||||||
|
})?;
|
||||||
|
(user.preferred_mode, user.into())
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let user = env.saved_users.by_user_id(msg.author.id).await?
|
||||||
|
.ok_or(Error::msg("You do not have a saved account! Use `osu save` command to save your osu! account."))?;
|
||||||
|
(user.preferred_mode, user.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok((mode, user))
|
||||||
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[aliases("rs", "rc", "r")]
|
#[aliases("rs", "rc", "r")]
|
||||||
#[description = "Gets an user's recent play"]
|
#[description = "Gets an user's recent play"]
|
||||||
|
@ -542,11 +624,6 @@ pub async fn recent(ctx: &Context, msg: &Message, mut args: Args) -> CommandResu
|
||||||
} = ListingArgs::parse(&env, msg, &mut args, ScoreListStyle::Table).await?;
|
} = ListingArgs::parse(&env, msg, &mut args, ScoreListStyle::Table).await?;
|
||||||
|
|
||||||
let osu_client = &env.client;
|
let osu_client = &env.client;
|
||||||
|
|
||||||
let user = osu_client
|
|
||||||
.user(&user, |f| f.mode(mode))
|
|
||||||
.await?
|
|
||||||
.ok_or_else(|| Error::msg("User not found"))?;
|
|
||||||
let plays = osu_client
|
let plays = osu_client
|
||||||
.user_recent(UserID::ID(user.id), |f| f.mode(mode).limit(50))
|
.user_recent(UserID::ID(user.id), |f| f.mode(mode).limit(50))
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -581,7 +658,7 @@ pub async fn recent(ctx: &Context, msg: &Message, mut args: Args) -> CommandResu
|
||||||
CreateMessage::new()
|
CreateMessage::new()
|
||||||
.content("Here is the play that you requested".to_string())
|
.content("Here is the play that you requested".to_string())
|
||||||
.embed(
|
.embed(
|
||||||
score_embed(play, &beatmap_mode, &content, &user)
|
score_embed(play, &beatmap_mode, &content, user)
|
||||||
.footer(format!("Attempt #{}", attempts))
|
.footer(format!("Attempt #{}", attempts))
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
|
@ -616,10 +693,6 @@ pub async fn pins(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
|
||||||
|
|
||||||
let osu_client = &env.client;
|
let osu_client = &env.client;
|
||||||
|
|
||||||
let user = osu_client
|
|
||||||
.user(&user, |f| f.mode(mode))
|
|
||||||
.await?
|
|
||||||
.ok_or_else(|| Error::msg("User not found"))?;
|
|
||||||
let plays = osu_client
|
let plays = osu_client
|
||||||
.user_pins(UserID::ID(user.id), |f| f.mode(mode).limit(50))
|
.user_pins(UserID::ID(user.id), |f| f.mode(mode).limit(50))
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -648,7 +721,7 @@ pub async fn pins(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
|
||||||
&ctx,
|
&ctx,
|
||||||
CreateMessage::new()
|
CreateMessage::new()
|
||||||
.content("Here is the play that you requested".to_string())
|
.content("Here is the play that you requested".to_string())
|
||||||
.embed(score_embed(play, &beatmap_mode, &content, &user).build())
|
.embed(score_embed(play, &beatmap_mode, &content, user).build())
|
||||||
.components(vec![score_components(msg.guild_id)])
|
.components(vec![score_components(msg.guild_id)])
|
||||||
.reference_message(msg),
|
.reference_message(msg),
|
||||||
)
|
)
|
||||||
|
@ -803,7 +876,7 @@ pub async fn check(ctx: &Context, msg: &Message, mut args: Args) -> CommandResul
|
||||||
.single::<ScoreListStyle>()
|
.single::<ScoreListStyle>()
|
||||||
.unwrap_or(ScoreListStyle::Grid);
|
.unwrap_or(ScoreListStyle::Grid);
|
||||||
let username_arg = args.single::<UsernameArg>().ok();
|
let username_arg = args.single::<UsernameArg>().ok();
|
||||||
let user = to_user_id_query(username_arg, &env, msg.author.id).await?;
|
let (_, user) = user_header_from_args(username_arg, &env, msg).await?;
|
||||||
|
|
||||||
let scores = do_check(&env, &bm, &mods, &user).await?;
|
let scores = do_check(&env, &bm, &mods, &user).await?;
|
||||||
|
|
||||||
|
@ -816,7 +889,7 @@ pub async fn check(ctx: &Context, msg: &Message, mut args: Args) -> CommandResul
|
||||||
&ctx,
|
&ctx,
|
||||||
format!(
|
format!(
|
||||||
"Here are the scores by `{}` on `{}`!",
|
"Here are the scores by `{}` on `{}`!",
|
||||||
&user,
|
&user.username,
|
||||||
bm.short_link(&mods)
|
bm.short_link(&mods)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -832,16 +905,12 @@ pub(crate) async fn do_check(
|
||||||
env: &OsuEnv,
|
env: &OsuEnv,
|
||||||
bm: &BeatmapWithMode,
|
bm: &BeatmapWithMode,
|
||||||
mods: &Mods,
|
mods: &Mods,
|
||||||
user: &UserID,
|
user: &UserHeader,
|
||||||
) -> Result<Vec<Score>> {
|
) -> Result<Vec<Score>> {
|
||||||
let BeatmapWithMode(b, m) = bm;
|
let BeatmapWithMode(b, m) = bm;
|
||||||
|
|
||||||
let osu_client = &env.client;
|
let osu_client = &env.client;
|
||||||
|
|
||||||
let user = osu_client
|
|
||||||
.user(user, |f| f)
|
|
||||||
.await?
|
|
||||||
.ok_or_else(|| Error::msg("User not found"))?;
|
|
||||||
let mut scores = osu_client
|
let mut scores = osu_client
|
||||||
.scores(b.beatmap_id, |f| f.user(UserID::ID(user.id)).mode(*m))
|
.scores(b.beatmap_id, |f| f.user(UserID::ID(user.id)).mode(*m))
|
||||||
.await?
|
.await?
|
||||||
|
@ -871,10 +940,6 @@ pub async fn top(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
|
||||||
user,
|
user,
|
||||||
} = ListingArgs::parse(&env, msg, &mut args, ScoreListStyle::default()).await?;
|
} = ListingArgs::parse(&env, msg, &mut args, ScoreListStyle::default()).await?;
|
||||||
let osu_client = &env.client;
|
let osu_client = &env.client;
|
||||||
let user = osu_client
|
|
||||||
.user(&user, |f| f.mode(mode))
|
|
||||||
.await?
|
|
||||||
.ok_or_else(|| Error::msg("User not found"))?;
|
|
||||||
|
|
||||||
let plays = osu_client
|
let plays = osu_client
|
||||||
.user_best(UserID::ID(user.id), |f| f.mode(mode).limit(100))
|
.user_best(UserID::ID(user.id), |f| f.mode(mode).limit(100))
|
||||||
|
@ -898,7 +963,7 @@ pub async fn top(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
|
||||||
msg.author
|
msg.author
|
||||||
))
|
))
|
||||||
.embed(
|
.embed(
|
||||||
score_embed(&play, &beatmap, &content, &user)
|
score_embed(&play, &beatmap, &content, user)
|
||||||
.top_record(nth + 1)
|
.top_record(nth + 1)
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
|
@ -945,33 +1010,18 @@ async fn get_user(
|
||||||
env: &OsuEnv,
|
env: &OsuEnv,
|
||||||
msg: &Message,
|
msg: &Message,
|
||||||
mut args: Args,
|
mut args: Args,
|
||||||
mode: Mode,
|
mode_override: impl Into<Option<Mode>>,
|
||||||
) -> CommandResult {
|
) -> CommandResult {
|
||||||
let user = to_user_id_query(args.single::<UsernameArg>().ok(), env, msg.author.id).await?;
|
let (mode, user) = user_header_from_args(args.single::<UsernameArg>().ok(), env, msg).await?;
|
||||||
let osu_client = &env.client;
|
let mode = mode_override.into().unwrap_or(mode);
|
||||||
let meta_cache = &env.beatmaps;
|
let user = env
|
||||||
let user = osu_client.user(&user, |f| f.mode(mode)).await?;
|
.client
|
||||||
|
.user(&UserID::ID(user.id), |f| f.mode(mode))
|
||||||
|
.await?;
|
||||||
|
|
||||||
match user {
|
match user {
|
||||||
Some(u) => {
|
Some(u) => {
|
||||||
let bests = osu_client
|
let ex = UserExtras::from_user(env, &u, mode).await?;
|
||||||
.user_best(UserID::ID(u.id), |f| f.limit(100).mode(mode))
|
|
||||||
.await?;
|
|
||||||
let map_length = calculate_weighted_map_length(&bests, meta_cache, mode);
|
|
||||||
let map_age = calculate_weighted_map_age(&bests, meta_cache, mode);
|
|
||||||
let (map_length, map_age) = try_join(map_length, map_age).await?;
|
|
||||||
let best = match bests.into_iter().next() {
|
|
||||||
Some(m) => {
|
|
||||||
let beatmap = meta_cache.get_beatmap(m.beatmap_id, mode).await?;
|
|
||||||
let info = env
|
|
||||||
.oppai
|
|
||||||
.get_beatmap(m.beatmap_id)
|
|
||||||
.await?
|
|
||||||
.get_info_with(mode, &m.mods)?;
|
|
||||||
Some((m, BeatmapWithMode(beatmap, mode), info))
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.send_message(
|
.send_message(
|
||||||
&ctx,
|
&ctx,
|
||||||
|
@ -980,7 +1030,7 @@ async fn get_user(
|
||||||
"{}: here is the user that you requested",
|
"{}: here is the user that you requested",
|
||||||
msg.author
|
msg.author
|
||||||
))
|
))
|
||||||
.embed(user_embed(u, map_length, map_age, best)),
|
.embed(user_embed(u, ex)),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -488,7 +488,6 @@ impl UserEvent {
|
||||||
pub struct UserHeader {
|
pub struct UserHeader {
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub country: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -497,6 +496,7 @@ pub struct User {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub joined: DateTime<Utc>,
|
pub joined: DateTime<Utc>,
|
||||||
pub country: String,
|
pub country: String,
|
||||||
|
pub preferred_mode: Mode,
|
||||||
// History
|
// History
|
||||||
pub count_300: u64,
|
pub count_300: u64,
|
||||||
pub count_100: u64,
|
pub count_100: u64,
|
||||||
|
@ -544,7 +544,6 @@ impl<'a> From<&'a User> for UserHeader {
|
||||||
Self {
|
Self {
|
||||||
id: u.id,
|
id: u.id,
|
||||||
username: u.username.clone(),
|
username: u.username.clone(),
|
||||||
country: u.country.clone(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -554,7 +553,6 @@ impl From<User> for UserHeader {
|
||||||
Self {
|
Self {
|
||||||
id: u.id,
|
id: u.id,
|
||||||
username: u.username,
|
username: u.username,
|
||||||
country: u.country,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,7 @@ impl User {
|
||||||
username: user.username.into_string(),
|
username: user.username.into_string(),
|
||||||
joined: time_to_utc(user.join_date),
|
joined: time_to_utc(user.join_date),
|
||||||
country: user.country_code.to_string(),
|
country: user.country_code.to_string(),
|
||||||
|
preferred_mode: user.mode.into(),
|
||||||
count_300: 0, // why do we even want this
|
count_300: 0, // why do we even want this
|
||||||
count_100: 0, // why do we even want this
|
count_100: 0, // why do we even want this
|
||||||
count_50: 0, // why do we even want this
|
count_50: 0, // why do we even want this
|
||||||
|
|
|
@ -132,8 +132,8 @@ pub mod builders {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mode(&mut self, mode: Mode) -> &mut Self {
|
pub fn mode(&mut self, mode: impl Into<Option<Mode>>) -> &mut Self {
|
||||||
self.mode = Some(mode);
|
self.mode = mode.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,8 +185,8 @@ pub mod builders {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mode(&mut self, mode: Mode) -> &mut Self {
|
pub fn mode(&mut self, mode: impl Into<Option<Mode>>) -> &mut Self {
|
||||||
self.mode = Some(mode);
|
self.mode = mode.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue