feat: introduce typed resp request

This commit is contained in:
mokurin000
2025-07-30 04:27:32 +08:00
parent 5ea8ebbf64
commit dd1bb5fb5c
5 changed files with 87 additions and 7 deletions

View File

@@ -13,7 +13,7 @@ use nyquest::{
r#async::Request, r#async::Request,
header::{ACCEPT_ENCODING, CONTENT_ENCODING, EXPECT, USER_AGENT}, header::{ACCEPT_ENCODING, CONTENT_ENCODING, EXPECT, USER_AGENT},
}; };
use serde::Serialize; use serde::{Deserialize, Serialize};
pub trait MaiVersion { pub trait MaiVersion {
const AES_KEY: &[u8; 32]; const AES_KEY: &[u8; 32];
@@ -63,7 +63,7 @@ pub trait MaiVersionExt: MaiVersion {
Ok(req) Ok(req)
} }
fn request<D>( fn request_raw<D>(
client: &AsyncClient, client: &AsyncClient,
api: APIMethod, api: APIMethod,
agent_extra: impl Display, agent_extra: impl Display,
@@ -79,6 +79,22 @@ pub trait MaiVersionExt: MaiVersion {
Ok(decoded) Ok(decoded)
} }
} }
fn request<D, R>(
client: &AsyncClient,
api: APIMethod,
agent_extra: impl Display,
data: D,
) -> impl Future<Output = Result<R, ApiError>>
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; pub struct Sdgb1_40;

View File

@@ -5,3 +5,6 @@ pub struct Ping {}
mod get_user_preview_api; mod get_user_preview_api;
pub use get_user_preview_api::{GetUserPreviewApi, GetUserPreviewApiResp}; pub use get_user_preview_api::{GetUserPreviewApi, GetUserPreviewApiResp};
mod user_logout_api;
pub use user_logout_api::{UserLogoutApi, UserLogoutApiResp};

View File

@@ -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: "",
}
}
}

View File

@@ -15,6 +15,10 @@ pub enum Commands {
#[arg(short, long)] #[arg(short, long)]
user_id: u32, user_id: u32,
}, },
Logout {
#[arg(short, long)]
user_id: u32,
},
/// Login with QRCode from wechat /// Login with QRCode from wechat
QRLogin { QRLogin {
/// content of the qrcode, only the last 64 characters were used /// content of the qrcode, only the last 64 characters were used

View File

@@ -5,7 +5,7 @@ use sdgb_api::{
title::{ title::{
MaiVersionExt, Sdgb1_40, Sdgb1_50, MaiVersionExt, Sdgb1_40, Sdgb1_50,
methods::APIMethod, methods::APIMethod,
model::{GetUserPreviewApi, GetUserPreviewApiResp, Ping}, model::{GetUserPreviewApi, GetUserPreviewApiResp, Ping, UserLogoutApi, UserLogoutApiResp},
}, },
}; };
use spdlog::{error, info}; use spdlog::{error, info};
@@ -22,8 +22,21 @@ async fn main() -> Result<(), Box<dyn snafu::Error>> {
let client = ClientBuilder::default().build_async().await?; let client = ClientBuilder::default().build_async().await?;
match cmd.command { 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 } => { commands::Commands::Preview { user_id } => {
let preview = Sdgb1_50::request( let preview: GetUserPreviewApiResp = Sdgb1_50::request(
&client, &client,
APIMethod::GetUserPreviewApi, APIMethod::GetUserPreviewApi,
user_id, user_id,
@@ -31,11 +44,10 @@ async fn main() -> Result<(), Box<dyn snafu::Error>> {
) )
.await?; .await?;
let preview: GetUserPreviewApiResp = serde_json::from_slice(&preview)?;
println!("{preview}"); println!("{preview}");
} }
commands::Commands::Ping => { commands::Commands::Ping => {
let decoded = Sdgb1_40::request( let decoded = Sdgb1_40::request_raw(
&client, &client,
APIMethod::Ping, APIMethod::Ping,
"", "",
@@ -43,7 +55,7 @@ async fn main() -> Result<(), Box<dyn snafu::Error>> {
) )
.await?; .await?;
info!("sdgb 1.40 resp: {:?}", String::from_utf8_lossy(&decoded)); 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)); info!("sdgb 1.50 resp: {:?}", String::from_utf8_lossy(&decoded));
} }
commands::Commands::QRLogin { ref qrcode_content } => { commands::Commands::QRLogin { ref qrcode_content } => {