feat: abstract method to perform action

This commit is contained in:
mokurin000
2025-07-30 03:20:21 +08:00
parent 6ee009715d
commit 1a281e0cc7
7 changed files with 59 additions and 15 deletions

1
Cargo.lock generated
View File

@@ -1099,6 +1099,7 @@ name = "sdgb-api"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"aes", "aes",
"block-padding",
"cbc", "cbc",
"chrono", "chrono",
"digest", "digest",

View File

@@ -26,3 +26,4 @@ flate2 = "1.1.2"
cbc = "0.1.2" cbc = "0.1.2"
aes = "0.8.4" aes = "0.8.4"
md5 = "0.8.0" md5 = "0.8.0"
block-padding = "0.3.3"

View File

@@ -3,6 +3,7 @@ use std::io::{Read, Write as _};
use aes::cipher::{ use aes::cipher::{
BlockDecryptMut, BlockEncryptMut, BlockSizeUser, KeyIvInit, block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, BlockSizeUser, KeyIvInit, block_padding::Pkcs7,
}; };
use digest::generic_array::GenericArray; use digest::generic_array::GenericArray;
use flate2::write::ZlibEncoder; use flate2::write::ZlibEncoder;
use flate2::{Compression, read::ZlibDecoder}; use flate2::{Compression, read::ZlibDecoder};
@@ -12,6 +13,20 @@ use crate::title::{MaiVersion, MaiVersionExt, Sdgb1_40, Sdgb1_50, error::ApiErro
impl MaiVersionExt for Sdgb1_40 { impl MaiVersionExt for Sdgb1_40 {
fn decode(mut data: impl AsMut<[u8]>) -> Result<Vec<u8>, ApiError> { fn decode(mut data: impl AsMut<[u8]>) -> Result<Vec<u8>, ApiError> {
let mut decompressed = decompress(data.as_mut()); let mut decompressed = decompress(data.as_mut());
if decompressed.is_empty() {
return Err(ApiError::EmptyResponse);
}
let orig_len = decompressed.len();
let remain = 16 - decompressed.len() % 16;
if
// weird but nessacary for Rust Pkcs7
remain != 16 {
decompressed.resize(remain + orig_len, remain as _);
}
let unpad_size = decrypt(&mut decompressed, Self::AES_KEY, Self::AES_IV)?.len(); let unpad_size = decrypt(&mut decompressed, Self::AES_KEY, Self::AES_IV)?.len();
decompressed.truncate(unpad_size); decompressed.truncate(unpad_size);
Ok(decompressed) Ok(decompressed)
@@ -26,6 +41,10 @@ impl MaiVersionExt for Sdgb1_40 {
impl MaiVersionExt for Sdgb1_50 { impl MaiVersionExt for Sdgb1_50 {
fn decode(mut data: impl AsMut<[u8]>) -> Result<Vec<u8>, ApiError> { fn decode(mut data: impl AsMut<[u8]>) -> Result<Vec<u8>, ApiError> {
if data.as_mut().is_empty() {
return Err(ApiError::EmptyResponse);
}
let decrypted = decrypt(&mut data, Self::AES_KEY, Self::AES_IV)?; let decrypted = decrypt(&mut data, Self::AES_KEY, Self::AES_IV)?;
let decompressed = decompress(decrypted); let decompressed = decompress(decrypted);
Ok(decompressed) Ok(decompressed)

View File

@@ -3,28 +3,34 @@ use snafu::Snafu;
#[derive(Debug, Snafu)] #[derive(Debug, Snafu)]
pub enum ApiError { pub enum ApiError {
#[snafu(display("pad data: {error}"))] #[snafu(display("api returned nothing!"))]
PadError { error: PadError }, EmptyResponse,
#[snafu(display("unpad data: {error}"))] #[snafu(display("encrypt data: {error}"))]
UnpadError { error: UnpadError }, Pad { error: PadError },
#[snafu(display("decrypt data: {error}"))]
Unpad { error: UnpadError },
#[snafu(display("io error: {source}"))] #[snafu(display("io error: {source}"))]
#[snafu(context(false))] #[snafu(context(false))]
IOError { source: std::io::Error }, IO { source: std::io::Error },
#[snafu(display("json error: {source}"))] #[snafu(display("json error: {source}"))]
#[snafu(context(false))] #[snafu(context(false))]
JSONError { source: serde_json::Error }, JSON { source: serde_json::Error },
#[snafu(display("request error: {source}"))]
#[snafu(context(false))]
Request { source: nyquest::Error },
} }
impl From<UnpadError> for ApiError { impl From<UnpadError> for ApiError {
fn from(error: UnpadError) -> Self { fn from(error: UnpadError) -> Self {
Self::UnpadError { error } Self::Unpad { error }
} }
} }
impl From<PadError> for ApiError { impl From<PadError> for ApiError {
fn from(error: PadError) -> Self { fn from(error: PadError) -> Self {
Self::PadError { error } Self::Pad { error }
} }
} }

View File

@@ -9,7 +9,7 @@ pub mod model;
mod error; mod error;
pub use error::ApiError; pub use error::ApiError;
use nyquest::{ use nyquest::{
Body, AsyncClient, Body,
r#async::Request, r#async::Request,
header::{ACCEPT_ENCODING, CONTENT_ENCODING, EXPECT, USER_AGENT}, header::{ACCEPT_ENCODING, CONTENT_ENCODING, EXPECT, USER_AGENT},
}; };
@@ -62,6 +62,23 @@ pub trait MaiVersionExt: MaiVersion {
Ok(req) Ok(req)
} }
fn request<D>(
client: &AsyncClient,
api: APIMethod,
agent_extra: impl Display,
data: D,
) -> impl Future<Output = Result<Vec<u8>, ApiError>>
where
D: Serialize,
{
async {
let req = Self::api_request(api, agent_extra, data)?;
let data = client.request(req).await?.bytes().await?;
let decoded = Self::decode(data)?;
Ok(decoded)
}
}
} }
pub struct Sdgb1_40; pub struct Sdgb1_40;

View File

@@ -1,4 +1,4 @@
use serde::Serialize; use serde::Serialize;
#[derive(Serialize)] #[derive(Serialize)]
pub struct Ping; pub struct Ping {}

View File

@@ -2,7 +2,7 @@ use nyquest_preset::nyquest::ClientBuilder;
use palc::Parser; use palc::Parser;
use sdgb_api::{ use sdgb_api::{
all_net::QRCode, all_net::QRCode,
title::{MaiVersionExt, Sdgb1_50, methods::APIMethod, model::Ping}, title::{MaiVersionExt, Sdgb1_40, Sdgb1_50, methods::APIMethod, model::Ping},
}; };
use spdlog::{error, info}; use spdlog::{error, info};
@@ -19,10 +19,10 @@ async fn main() -> Result<(), Box<dyn snafu::Error>> {
match cmd.command { match cmd.command {
commands::Commands::Ping => { commands::Commands::Ping => {
let req = Sdgb1_50::api_request(APIMethod::Ping, "", Ping)?; let decoded = Sdgb1_40::request(&client, APIMethod::Ping, "1", Ping {}).await?;
let data = client.request(req).await?.bytes().await?; info!("sdgb 1.40 resp: {:?}", String::from_utf8_lossy(&decoded));
let decoded = Sdgb1_50::decode(data)?; let decoded = Sdgb1_50::request(&client, APIMethod::Ping, "", Ping {}).await?;
info!("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 } => {
let qrcode = QRCode { qrcode_content }; let qrcode = QRCode { qrcode_content };