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