From dd1bb5fb5cbf64424b827defb6cf9c94fdb07111 Mon Sep 17 00:00:00 2001 From: mokurin000 <1348292515a@gmail.com> Date: Wed, 30 Jul 2025 04:27:32 +0800 Subject: [PATCH] feat: introduce typed resp `request` --- sdgb-api/src/title/mod.rs | 20 ++++++++- sdgb-api/src/title/model/mod.rs | 3 ++ .../src/title/model/user_logout_api/mod.rs | 45 +++++++++++++++++++ sdgb-cli/src/commands.rs | 4 ++ sdgb-cli/src/main.rs | 22 ++++++--- 5 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 sdgb-api/src/title/model/user_logout_api/mod.rs diff --git a/sdgb-api/src/title/mod.rs b/sdgb-api/src/title/mod.rs index fe4c5b0..d965393 100644 --- a/sdgb-api/src/title/mod.rs +++ b/sdgb-api/src/title/mod.rs @@ -13,7 +13,7 @@ use nyquest::{ r#async::Request, header::{ACCEPT_ENCODING, CONTENT_ENCODING, EXPECT, USER_AGENT}, }; -use serde::Serialize; +use serde::{Deserialize, Serialize}; pub trait MaiVersion { const AES_KEY: &[u8; 32]; @@ -63,7 +63,7 @@ pub trait MaiVersionExt: MaiVersion { Ok(req) } - fn request( + fn request_raw( client: &AsyncClient, api: APIMethod, agent_extra: impl Display, @@ -79,6 +79,22 @@ pub trait MaiVersionExt: MaiVersion { Ok(decoded) } } + + fn request( + client: &AsyncClient, + api: APIMethod, + agent_extra: impl Display, + data: D, + ) -> impl Future> + where + D: Serialize, + R: for<'a> Deserialize<'a>, + { + async { + let raw_data = Self::request_raw(client, api, agent_extra, data).await?; + Ok(serde_json::from_slice(&raw_data)?) + } + } } pub struct Sdgb1_40; diff --git a/sdgb-api/src/title/model/mod.rs b/sdgb-api/src/title/model/mod.rs index ad6cdc0..ec6d833 100644 --- a/sdgb-api/src/title/model/mod.rs +++ b/sdgb-api/src/title/model/mod.rs @@ -5,3 +5,6 @@ pub struct Ping {} mod get_user_preview_api; pub use get_user_preview_api::{GetUserPreviewApi, GetUserPreviewApiResp}; + +mod user_logout_api; +pub use user_logout_api::{UserLogoutApi, UserLogoutApiResp}; diff --git a/sdgb-api/src/title/model/user_logout_api/mod.rs b/sdgb-api/src/title/model/user_logout_api/mod.rs new file mode 100644 index 0000000..a64985a --- /dev/null +++ b/sdgb-api/src/title/model/user_logout_api/mod.rs @@ -0,0 +1,45 @@ +use std::time::{SystemTime, UNIX_EPOCH}; + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct UserLogoutApi { + pub user_id: u32, + pub region_id: u64, + pub place_id: u64, + /// empty on SDGB + pub access_code: &'static str, + /// keychip without dash, 11 bytes + pub client_id: String, + /// Unix timestamp + pub date_time: u64, + #[serde(rename = "type")] + pub type_: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserLogoutApiResp { + pub result_code: i64, +} + +impl Default for UserLogoutApi { + fn default() -> Self { + let user_id = 0; + let date_time = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|t| t.as_secs()) + .unwrap_or_default(); + + Self { + user_id, + date_time, + region_id: 22, + place_id: 3490, + client_id: "A63E01E9564".into(), + type_: 1, + access_code: "", + } + } +} diff --git a/sdgb-cli/src/commands.rs b/sdgb-cli/src/commands.rs index 93dc30a..42cd138 100644 --- a/sdgb-cli/src/commands.rs +++ b/sdgb-cli/src/commands.rs @@ -15,6 +15,10 @@ pub enum Commands { #[arg(short, long)] user_id: u32, }, + Logout { + #[arg(short, long)] + user_id: u32, + }, /// Login with QRCode from wechat QRLogin { /// content of the qrcode, only the last 64 characters were used diff --git a/sdgb-cli/src/main.rs b/sdgb-cli/src/main.rs index ed69484..7ebe437 100644 --- a/sdgb-cli/src/main.rs +++ b/sdgb-cli/src/main.rs @@ -5,7 +5,7 @@ use sdgb_api::{ title::{ MaiVersionExt, Sdgb1_40, Sdgb1_50, methods::APIMethod, - model::{GetUserPreviewApi, GetUserPreviewApiResp, Ping}, + model::{GetUserPreviewApi, GetUserPreviewApiResp, Ping, UserLogoutApi, UserLogoutApiResp}, }, }; use spdlog::{error, info}; @@ -22,8 +22,21 @@ async fn main() -> Result<(), Box> { let client = ClientBuilder::default().build_async().await?; match cmd.command { + commands::Commands::Logout { user_id } => { + let logout: UserLogoutApiResp = Sdgb1_50::request( + &client, + APIMethod::UserLogoutApi, + user_id, + UserLogoutApi { + user_id, + ..Default::default() + }, + ) + .await?; + println!("{logout:?}"); + } commands::Commands::Preview { user_id } => { - let preview = Sdgb1_50::request( + let preview: GetUserPreviewApiResp = Sdgb1_50::request( &client, APIMethod::GetUserPreviewApi, user_id, @@ -31,11 +44,10 @@ async fn main() -> Result<(), Box> { ) .await?; - let preview: GetUserPreviewApiResp = serde_json::from_slice(&preview)?; println!("{preview}"); } commands::Commands::Ping => { - let decoded = Sdgb1_40::request( + let decoded = Sdgb1_40::request_raw( &client, APIMethod::Ping, "", @@ -43,7 +55,7 @@ async fn main() -> Result<(), Box> { ) .await?; info!("sdgb 1.40 resp: {:?}", String::from_utf8_lossy(&decoded)); - let decoded = Sdgb1_50::request(&client, APIMethod::Ping, "", Ping {}).await?; + let decoded = Sdgb1_50::request_raw(&client, APIMethod::Ping, "", Ping {}).await?; info!("sdgb 1.50 resp: {:?}", String::from_utf8_lossy(&decoded)); } commands::Commands::QRLogin { ref qrcode_content } => {