diff --git a/src-tauri/src/bili_client.rs b/src-tauri/src/bili_client.rs index f06febf..c8c232f 100644 --- a/src-tauri/src/bili_client.rs +++ b/src-tauri/src/bili_client.rs @@ -12,7 +12,7 @@ use tauri::{ AppHandle, }; -use crate::types::{qrcode_data::QrcodeData, qrcode_status::QrcodeStatus}; +use crate::types::{qrcode_data::QrcodeData, qrcode_status::QrcodeStatus, user_info::UserInfo}; const USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"; const REFERRER: &str = "https://www.bilibili.com/"; @@ -97,6 +97,41 @@ impl BiliClient { } Ok(qrcode_status) } + + pub async fn get_user_info(&self, sessdata: &str) -> anyhow::Result { + // 发送获取用户信息的请求 + let request = self + .api_client + .read() + .get("https://api.bilibili.com/x/web-interface/nav") + .header("cookie", format!("SESSDATA={sessdata}")); + let http_resp = request.send().await?; + // 检查http响应状态码 + let status = http_resp.status(); + let body = http_resp.text().await?; + if status != StatusCode::OK { + return Err(anyhow!("预料之外的状态码({status}): {body}")); + } + // 尝试将body解析为BiliResp + let bili_resp: BiliResp = + serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?; + // 检查BiliResp的code字段 + if bili_resp.code == -101 { + return Err(anyhow!("cookie错误或已过期,请重新登录: {bili_resp:?}")); + } else if bili_resp.code != 0 { + return Err(anyhow!("预料之外的code: {bili_resp:?}")); + } + // 检查BiliResp的data是否存在 + let Some(data) = bili_resp.data else { + return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}")); + }; + // 尝试将data解析为UserInfo + let data_str = data.to_string(); + let user_info: UserInfo = serde_json::from_str(&data_str) + .context(format!("将data解析为UserInfo失败: {data_str}"))?; + + Ok(user_info) + } } fn create_api_client(_app: &AppHandle) -> ClientWithMiddleware { diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index ae7a5b7..7b6b900 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -6,7 +6,7 @@ use crate::{ errors::{CommandError, CommandResult}, extensions::AppHandleExt, logger, - types::{qrcode_data::QrcodeData, qrcode_status::QrcodeStatus}, + types::{qrcode_data::QrcodeData, qrcode_status::QrcodeStatus, user_info::UserInfo}, }; #[tauri::command] @@ -80,3 +80,14 @@ pub async fn get_qrcode_status(app: AppHandle, qrcode_key: String) -> CommandRes .map_err(|err| CommandError::from("获取二维码状态", err))?; Ok(qrcode_status) } + +#[tauri::command(async)] +#[specta::specta] +pub async fn get_user_info(app: AppHandle, sessdata: String) -> CommandResult { + let bili_client = app.get_bili_client(); + let user_info = bili_client + .get_user_info(&sessdata) + .await + .map_err(|err| CommandError::from("获取用户信息失败", err))?; + Ok(user_info) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 1dc21b8..ebf2013 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -29,6 +29,7 @@ pub fn run() { save_config, generate_qrcode, get_qrcode_status, + get_user_info, ]) .events(tauri_specta::collect_events![LogEvent]); diff --git a/src-tauri/src/types/mod.rs b/src-tauri/src/types/mod.rs index 336b80a..d05d719 100644 --- a/src-tauri/src/types/mod.rs +++ b/src-tauri/src/types/mod.rs @@ -1,3 +1,4 @@ pub mod log_level; pub mod qrcode_data; pub mod qrcode_status; +pub mod user_info; diff --git a/src-tauri/src/types/user_info.rs b/src-tauri/src/types/user_info.rs new file mode 100644 index 0000000..ec2cdcd --- /dev/null +++ b/src-tauri/src/types/user_info.rs @@ -0,0 +1,146 @@ +use serde::{Deserialize, Serialize}; +use specta::Type; + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)] +pub struct UserInfo { + #[serde(rename = "isLogin")] + pub is_login: bool, + pub email_verified: i64, + pub face: String, + pub face_nft: i64, + pub face_nft_type: i64, + pub level_info: LevelInfoInUserInfo, + pub mid: i64, + pub mobile_verified: i64, + pub money: f64, + pub moral: i64, + pub official: Official, + #[serde(rename = "officialVerify")] + pub official_verify: OfficialVerify, + pub pendant: PendantInUserInfo, + pub scores: i64, + pub uname: String, + #[serde(rename = "vipDueDate")] + pub vip_due_date: i64, + #[serde(rename = "vipStatus")] + pub vip_status: i64, + #[serde(rename = "vipType")] + pub vip_type: i64, + pub vip_pay_type: i64, + pub vip_theme_type: i64, + pub vip_label: VipLabel, + pub vip_avatar_subscript: i64, + pub vip_nickname_color: String, + pub vip: VipInUserInfo, + pub wallet: Option, + pub has_shop: bool, + pub shop_url: String, + pub answer_status: i64, + pub is_senior_member: i64, + pub wbi_img: WbiImg, + pub is_jury: bool, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)] +#[allow(clippy::struct_field_names)] +pub struct LevelInfoInUserInfo { + pub current_level: i64, + pub current_min: i64, + pub current_exp: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)] +pub struct Official { + pub role: i64, + pub title: String, + pub desc: String, + #[serde(rename = "type")] + pub type_field: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)] +pub struct OfficialVerify { + #[serde(rename = "type")] + pub type_field: i64, + pub desc: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)] +pub struct PendantInUserInfo { + pub pid: i64, + pub name: String, + pub image: String, + pub expire: i64, + pub image_enhance: String, + pub image_enhance_frame: String, + pub n_pid: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)] +pub struct VipLabel { + pub path: String, + pub text: String, + pub label_theme: String, + pub text_color: String, + pub bg_style: i64, + pub bg_color: String, + pub border_color: String, + pub use_img_label: bool, + pub img_label_uri_hans: String, + pub img_label_uri_hant: String, + pub img_label_uri_hans_static: String, + pub img_label_uri_hant_static: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)] +#[allow(clippy::struct_field_names)] +pub struct VipInUserInfo { + #[serde(rename = "type")] + pub type_field: i64, + pub status: i64, + pub due_date: i64, + pub vip_pay_type: i64, + pub theme_type: i64, + pub label: LabelInUserInfo, + pub avatar_subscript: i64, + pub nickname_color: String, + pub role: i64, + pub avatar_subscript_url: String, + pub tv_vip_status: i64, + pub tv_vip_pay_type: i64, + pub tv_due_date: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)] +#[allow(clippy::struct_field_names)] +pub struct LabelInUserInfo { + pub path: String, + pub text: String, + pub label_theme: String, + pub text_color: String, + pub bg_style: i64, + pub bg_color: String, + pub border_color: String, + pub use_img_label: bool, + pub img_label_uri_hans: String, + pub img_label_uri_hant: String, + pub img_label_uri_hans_static: String, + pub img_label_uri_hant_static: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)] +pub struct IconResource {} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)] +pub struct Wallet { + pub mid: i64, + pub bcoin_balance: f64, + pub coupon_balance: f64, + pub coupon_due_time: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)] +pub struct WbiImg { + pub img_url: String, + pub sub_url: String, +} diff --git a/src/bindings.ts b/src/bindings.ts index c3af336..d6cafe2 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -34,6 +34,14 @@ async getQrcodeStatus(qrcodeKey: string) : Promise> { + try { + return { status: "ok", data: await TAURI_INVOKE("get_user_info", { sessdata }) }; +} catch (e) { + if(e instanceof Error) throw e; + else return { status: "error", error: e as any }; +} } } @@ -55,10 +63,20 @@ logEvent: "log-event" export type CommandError = { err_title: string; err_message: string } export type Config = { downloadDir: string; enableFileLogger: boolean } export type JsonValue = null | boolean | number | string | JsonValue[] | { [key in string]: JsonValue } +export type LabelInUserInfo = { path: string; text: string; label_theme: string; text_color: string; bg_style: number; bg_color: string; border_color: string; use_img_label: boolean; img_label_uri_hans: string; img_label_uri_hant: string; img_label_uri_hans_static: string; img_label_uri_hant_static: string } +export type LevelInfoInUserInfo = { current_level: number; current_min: number; current_exp: number } export type LogEvent = { timestamp: string; level: LogLevel; fields: { [key in string]: JsonValue }; target: string; filename: string; line_number: number } export type LogLevel = "TRACE" | "DEBUG" | "INFO" | "WARN" | "ERROR" +export type Official = { role: number; title: string; desc: string; type: number } +export type OfficialVerify = { type: number; desc: string } +export type PendantInUserInfo = { pid: number; name: string; image: string; expire: number; image_enhance: string; image_enhance_frame: string; n_pid: number } export type QrcodeData = { url: string; qrcode_key: string } export type QrcodeStatus = { url: string; refresh_token: string; timestamp: number; code: number; message: string } +export type UserInfo = { isLogin: boolean; email_verified: number; face: string; face_nft: number; face_nft_type: number; level_info: LevelInfoInUserInfo; mid: number; mobile_verified: number; money: number; moral: number; official: Official; officialVerify: OfficialVerify; pendant: PendantInUserInfo; scores: number; uname: string; vipDueDate: number; vipStatus: number; vipType: number; vip_pay_type: number; vip_theme_type: number; vip_label: VipLabel; vip_avatar_subscript: number; vip_nickname_color: string; vip: VipInUserInfo; wallet: Wallet | null; has_shop: boolean; shop_url: string; answer_status: number; is_senior_member: number; wbi_img: WbiImg; is_jury: boolean } +export type VipInUserInfo = { type: number; status: number; due_date: number; vip_pay_type: number; theme_type: number; label: LabelInUserInfo; avatar_subscript: number; nickname_color: string; role: number; avatar_subscript_url: string; tv_vip_status: number; tv_vip_pay_type: number; tv_due_date: number } +export type VipLabel = { path: string; text: string; label_theme: string; text_color: string; bg_style: number; bg_color: string; border_color: string; use_img_label: boolean; img_label_uri_hans: string; img_label_uri_hant: string; img_label_uri_hans_static: string; img_label_uri_hant_static: string } +export type Wallet = { mid: number; bcoin_balance: number; coupon_balance: number; coupon_due_time: number } +export type WbiImg = { img_url: string; sub_url: string } /** tauri-specta globals **/