1#[cfg(feature = "im")]
2use base64::{prelude::BASE64_STANDARD, Engine};
3#[cfg(feature = "im")]
4use hmac::{Hmac, Mac};
5#[cfg(feature = "im")]
6use serde_json::{json, Value};
7#[cfg(feature = "im")]
8use sha2::Sha256;
9
10#[cfg(feature = "im")]
11use crate::core::{
12 api_resp::{BaseResponse, RawResponse},
13 http::Transport,
14 SDKResult,
15};
16
17#[cfg(feature = "im")]
18use crate::service::im::v1::message::{MessageCardTemplate, SendMessageTrait};
19
20#[allow(dead_code)]
24pub struct CustomBot<'a> {
25 webhook_url: &'a str,
27 secret: Option<&'a str>,
29 client: reqwest::Client,
30}
31
32impl<'a> CustomBot<'a> {
33 pub fn new(webhook_url: &'a str, secret: Option<&'a str>) -> Self {
34 CustomBot {
35 webhook_url,
36 secret,
37 client: reqwest::Client::new(),
38 }
39 }
40}
41
42impl CustomBot<'_> {
43 #[cfg(feature = "im")]
44 pub async fn send_message<T>(&self, message: T) -> SDKResult<BaseResponse<RawResponse>>
45 where
46 T: SendMessageTrait,
47 {
48 let mut json = json!({
49 "msg_type": message.msg_type(),
50 "content": message.content()
51 });
52 self.check_sign(&mut json);
53 Transport::do_send(
54 self.client.post(self.webhook_url),
55 json.to_string().into(),
56 false,
57 )
58 .await
59 }
60
61 #[cfg(feature = "im")]
63 pub async fn send_card(
64 &self,
65 message: MessageCardTemplate,
66 ) -> SDKResult<BaseResponse<RawResponse>> {
67 let mut json = json!({
68 "msg_type": message.msg_type(),
69 "card": message.content()
70 });
71
72 self.check_sign(&mut json);
73
74 Transport::do_send(
75 self.client.post(self.webhook_url),
76 json.to_string().into_bytes(),
77 false,
78 )
79 .await
80 }
81
82 #[cfg(feature = "im")]
84 fn check_sign(&self, json: &mut Value) {
85 if let Some(secret) = self.secret.as_ref() {
86 let now = chrono::Local::now().timestamp();
87 json["timestamp"] = serde_json::to_value(now).unwrap();
88 let sign = CustomBot::sign(now, secret);
89 json["sign"] = serde_json::to_value(sign).unwrap();
90 }
91 }
92
93 #[cfg(feature = "im")]
95 fn sign(timestamp: i64, secret: &str) -> String {
96 let string_to_sign = format!("{timestamp}\n{secret}");
97 let hmac: Hmac<Sha256> = Hmac::new_from_slice(string_to_sign.as_bytes()).unwrap();
98 let hmac_code = hmac.finalize().into_bytes();
99 BASE64_STANDARD.encode(hmac_code)
100 }
101}