Skip to main content

futu_backend/
user_profile.rs

1use bytes::Bytes;
2use futu_command_spec::SystemReadOperation;
3use prost::Message;
4
5use crate::conn::BackendConn;
6use futu_core::error::{FutuError, Result};
7
8pub use futu_command_spec::CMD_SYSTEM_QUERY_USER_PROFILE as CMD_QUERY_USER_PROFILE;
9const QUERY_PROFILE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct BackendUserProfile {
13    pub user_id: u64,
14    pub nick_name: String,
15    pub avatar_url: String,
16}
17
18pub fn build_query_user_profile_request(user_id: u64) -> Vec<u8> {
19    use crate::proto_internal::ft_cmd_sns_profile as pb;
20
21    let req = pb::GetUserProfilesReq {
22        uid: Some(user_id),
23        filter: Some(pb::UserProfile {
24            uid: Some(user_id),
25            nick: Some("1".to_string()),
26            icon: Some("1".to_string()),
27        }),
28        avatar_size: Some(120),
29    };
30    req.encode_to_vec()
31}
32
33pub fn decode_query_user_profile_response(body: &[u8]) -> Result<BackendUserProfile> {
34    let rsp: crate::proto_internal::ft_cmd_sns_profile::GetUserProfilesRsp =
35        Message::decode(body).map_err(FutuError::Proto)?;
36
37    let errcode = rsp.errcode.unwrap_or(0);
38    if errcode != 0 {
39        return Err(FutuError::ServerError {
40            ret_type: errcode,
41            msg: rsp
42                .errmsg
43                .unwrap_or_else(|| format!("CMD7506 QueryUserProfile failed: {errcode}")),
44        });
45    }
46
47    let user_data = rsp.user_data.ok_or_else(|| FutuError::ServerError {
48        ret_type: -1,
49        msg: "CMD7506 QueryUserProfile missing user_data".to_string(),
50    })?;
51
52    Ok(BackendUserProfile {
53        user_id: user_data.uid.unwrap_or(0),
54        nick_name: user_data.nick.unwrap_or_default(),
55        avatar_url: user_data.icon.unwrap_or_default(),
56    })
57}
58
59pub async fn query_user_profile(backend: &BackendConn, user_id: u64) -> Result<BackendUserProfile> {
60    let body = build_query_user_profile_request(user_id);
61    let frame = tokio::time::timeout(
62        QUERY_PROFILE_TIMEOUT,
63        crate::command_runtime::execute_system_read(
64            backend,
65            SystemReadOperation::UserProfile,
66            Bytes::from(body),
67        ),
68    )
69    .await
70    .map_err(|elapsed| {
71        tracing::warn!(
72            user_id_fp = %futu_core::log_redact::uid_log_fingerprint(user_id),
73            error = %elapsed,
74            "query user profile timed out"
75        );
76        FutuError::Timeout
77    })??;
78
79    decode_query_user_profile_response(frame.body.as_ref())
80}