futu_backend/
user_profile.rs1use 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}