futu_backend/
system_profile.rs1use bytes::Bytes;
2use prost::Message;
3
4use futu_command_spec::{BackendExtensionOperation, SystemReadOperation};
5use futu_core::error::{FutuError, Result};
6
7use crate::command_runtime::{execute_backend_extension, execute_system_read};
8use crate::conn::BackendConn;
9use crate::proto_internal::{account_flag, auth_query_user_agree_api, sig};
10
11pub struct AppJumpSigBatch {
12 pub invalid_time_secs: u32,
13 pub tickets: Vec<Vec<u8>>,
14}
15
16pub async fn query_agree_api_license(backend: &BackendConn, user_id: u64) -> Result<bool> {
17 let request = auth_query_user_agree_api::AuthQueryUserAgreeApiReq { uid: Some(user_id) };
18 let frame = execute_system_read(
19 backend,
20 SystemReadOperation::AgreeApiLicense,
21 Bytes::from(request.encode_to_vec()),
22 )
23 .await?;
24 let response = auth_query_user_agree_api::AuthQueryUserAgreeApiRsp::decode(frame.body.as_ref())
25 .map_err(|error| FutuError::Codec(format!("CMD5243 decode failed: {error}")))?;
26 match (response.ret_code, response.agree_api) {
27 (Some(0), Some(agreed)) => Ok(agreed != 0),
28 (Some(0), None) | (None, _) => Err(FutuError::Codec(
29 "CMD5243 response missing ret_code or agree_api".to_string(),
30 )),
31 (Some(code), _) => Err(FutuError::ServerError {
32 ret_type: code,
33 msg: response
34 .ret_msg
35 .unwrap_or_else(|| "CMD5243 agreement query rejected".to_string()),
36 }),
37 }
38}
39
40pub async fn query_broker_disclaimer_flag(backend: &BackendConn, user_id: u64) -> Result<bool> {
41 const OPEN_API_DISCLAIMER_FLAG_ID: u32 = 46;
44 let request = account_flag::GetFlagReq {
45 uid: Some(user_id),
46 flag_id: Some(OPEN_API_DISCLAIMER_FLAG_ID),
47 };
48 let frame = execute_backend_extension(
49 backend,
50 BackendExtensionOperation::AccountFlag,
51 Bytes::from(request.encode_to_vec()),
52 )
53 .await?;
54 let response = account_flag::GetFlagRsp::decode(frame.body.as_ref())
55 .map_err(|error| FutuError::Codec(format!("CMD5281 decode failed: {error}")))?;
56 match response.ret_code {
57 Some(0) => response
58 .item
59 .and_then(|item| item.flag_value)
60 .map(|value| value != 0)
61 .ok_or_else(|| FutuError::Codec("CMD5281 response missing flag_value".to_string())),
62 Some(-40) => Ok(false),
63 Some(code) => Err(FutuError::ServerError {
64 ret_type: code,
65 msg: response
66 .ret_msg
67 .unwrap_or_else(|| "CMD5281 disclaimer flag query rejected".to_string()),
68 }),
69 None => Err(FutuError::Codec(
70 "CMD5281 response missing ret_code".to_string(),
71 )),
72 }
73}
74
75pub async fn fetch_app_jump_sig_batch(backend: &BackendConn) -> Result<AppJumpSigBatch> {
76 let request = sig::GetAppJumpSigRequest {
77 dummy: Some("1".to_string()),
80 };
81 let frame = execute_system_read(
82 backend,
83 SystemReadOperation::AppJumpSig,
84 Bytes::from(request.encode_to_vec()),
85 )
86 .await?;
87 let response = sig::GetAppJumpSigResponse::decode(frame.body.as_ref())
88 .map_err(|error| FutuError::Codec(format!("CMD1009 decode failed: {error}")))?;
89 match response.ret_code {
90 Some(0) => {
91 let invalid_time_secs = response.invalid_time.ok_or_else(|| {
92 FutuError::Codec("CMD1009 success missing invalid_time".to_string())
93 })?;
94 Ok(AppJumpSigBatch {
95 invalid_time_secs,
96 tickets: response.app_jump_str,
97 })
98 }
99 Some(code) => Err(FutuError::ServerError {
100 ret_type: code,
101 msg: response
102 .ret_msg
103 .unwrap_or_else(|| "CMD1009 ticket refill rejected".to_string()),
104 }),
105 None => Err(FutuError::Codec(
106 "CMD1009 response missing ret_code".to_string(),
107 )),
108 }
109}