larkrs_client/bot/
chat.rs1#![allow(dead_code)]
2
3use crate::LarkApiResponse;
4use crate::auth::FeishuTokenManager;
5use anyhow::{Result, anyhow};
6use reqwest::Client;
7use serde_json::Value;
8use thiserror::Error;
9
10use super::{ChatInfoItem, ChatListResponse, SendMessageRequest};
11
12#[derive(Error, Debug)]
13pub enum ChatApiError {
14 #[error("Network error: {0}")]
15 NetworkError(#[from] reqwest::Error),
16
17 #[error("JSON serialization error: {0}")]
18 SerdeError(#[from] serde_json::Error),
19
20 #[error("API error: {message} (code: {code})")]
21 ApiError { code: i32, message: String },
22}
23
24pub struct ChatClient {
25 token_manager: FeishuTokenManager,
26}
27
28impl ChatClient {
29 pub fn new() -> Self {
30 Self {
31 token_manager: FeishuTokenManager::new(),
32 }
33 }
34
35 pub async fn send_message(&self, request: SendMessageRequest) -> Result<Value> {
39 let token = self.token_manager.get_token().await?;
40
41 let url = "https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=chat_id";
42
43 let resp = Client::new()
44 .post(url)
45 .header("Authorization", format!("Bearer {}", token))
46 .header("Content-Type", "application/json")
47 .json(&request)
48 .send()
49 .await
50 .map_err(|e| anyhow!(e).context("Failed to send request for sending message"))?
51 .json::<LarkApiResponse<Value>>()
52 .await
53 .map_err(|e| anyhow!(e).context("Failed to parse send message response"))?;
54
55 match resp.is_success() {
56 true => Ok(resp.data),
57 false => Err(anyhow!(ChatApiError::ApiError {
58 code: resp.code,
59 message: resp.msg.clone(),
60 })
61 .context(format!("API returned error code: {}", resp.code))),
62 }
63 }
64
65 pub async fn send_text_message(&self, chat_id: &str, text: &str) -> Result<Value> {
66 let request = SendMessageRequest::text(chat_id, text);
67 self.send_message(request).await
68 }
69
70 pub async fn send_markdown_message(
74 &self,
75 chat_id: &str,
76 title: &str,
77 content: &str,
78 ) -> Result<Value> {
79 let elements = vec![vec![super::MarkdownElement {
81 tag: "md".to_string(),
82 text: content.to_string(),
83 style: None,
84 }]];
85
86 let request = SendMessageRequest::markdown(chat_id, title, elements);
87 self.send_message(request).await
88 }
89
90 pub async fn get_chat_group_list(&self) -> Result<Vec<ChatInfoItem>> {
94 let token = self.token_manager.get_token().await?;
95
96 let resp = Client::new()
98 .get("https://open.feishu.cn/open-apis/im/v1/chats")
99 .query(&[("page_size", "20"), ("sort_type", "ByCreateTimeAsc")])
100 .header("Authorization", format!("Bearer {}", token))
101 .header("Content-Type", "application/json; charset=utf-8")
102 .send()
103 .await
104 .map_err(|e| anyhow!(e).context("Failed to send request for getting chat list"))?
105 .json::<LarkApiResponse<ChatListResponse>>()
106 .await
107 .map_err(|e| anyhow!(e).context("Failed to parse chat list response"))?;
108
109 match resp.is_success() {
110 true => Ok(resp.data.into()),
111 false => Err(anyhow!(ChatApiError::ApiError {
112 code: resp.code,
113 message: resp.msg.clone(),
114 })
115 .context(format!("API returned error code: {}", resp.code))),
116 }
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[tokio::test]
125 async fn test_get_chat_group_list() {
126 dotenvy::dotenv().ok();
127
128 let client = ChatClient::new();
129 let result = client.get_chat_group_list().await;
130
131 println!("Result: {:?}", result);
132 }
133
134 #[tokio::test]
135 async fn test_send_text_message() {
136 dotenvy::dotenv().ok();
137
138 let client = ChatClient::new();
139 let chat_id = std::env::var("CHAT_ID").unwrap();
140 let result = client
141 .send_text_message(&chat_id, "Test message from Rust API")
142 .await;
143
144 println!("Send message result: {:?}", result);
145 }
146
147 #[tokio::test]
148 async fn test_send_markdown_message() {
149 dotenvy::dotenv().ok();
150
151 let client = ChatClient::new();
152 let chat_id = std::env::var("CHAT_ID").unwrap();
153
154 let markdown_content = "# 股票市场实时数据\n\n**今日热门股票列表**\n\n- **阿里巴巴 (BABA)**: ¥78.45 📈 +2.3%\n- **腾讯控股 (0700.HK)**: ¥321.80 📉 -1.5%\n- **美团 (3690.HK)**: ¥125.60 📈 +3.7%\n- **京东 (JD)**: ¥142.30 📈 +0.8%\n- **百度 (BIDU)**: ¥112.75 📉 -2.1%\n- **小米集团 (1810.HK)**: ¥12.86 📈 +4.2%\n- **拼多多 (PDD)**: ¥89.35 📈 +5.6%\n\n> 数据更新时间: 2025-04-01 19:30:00";
155
156 let result = client
157 .send_markdown_message(&chat_id, "我是一个标题", markdown_content)
158 .await;
159
160 println!("Send markdown message result: {:?}", result);
161 }
162}