1use std::collections::HashMap;
2
3use log::error;
4use reqwest::Method;
5use serde::{Deserialize, Serialize};
6use serde_json::{json, Value};
7
8use crate::core::{
9 api_req::ApiRequest,
10 api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
11 config::Config,
12 constants::AccessTokenType,
13 http::Transport,
14 req_option::RequestOption,
15 standard_response::StandardResponse,
16 SDKResult,
17};
18
19pub struct MessageService {
58 pub config: Config,
60}
61
62impl MessageService {
63 pub async fn create(
70 &self,
71 create_message_request: CreateMessageRequest,
72 option: Option<RequestOption>,
73 ) -> SDKResult<Message> {
74 let mut api_req = create_message_request.api_req;
75 api_req.http_method = Method::POST;
76 api_req.api_path = "/open-apis/im/v1/messages".to_string();
77 api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
78
79 let api_resp: BaseResponse<Message> =
80 Transport::request(api_req, &self.config, option).await?;
81
82 api_resp.into_result()
83 }
84
85 pub async fn list(
91 &self,
92 list_message_request: ListMessageRequest,
93 option: Option<RequestOption>,
94 ) -> SDKResult<ListMessageRespData> {
95 let mut api_req = list_message_request.api_req;
96 api_req.http_method = Method::GET;
97 api_req.api_path = "/open-apis/im/v1/messages".to_string();
98 api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
99
100 let api_resp: BaseResponse<ListMessageRespData> =
101 Transport::request(api_req, &self.config, option).await?;
102
103 api_resp.into_result()
104 }
105
106 pub fn list_iter(
107 &self,
108 list_message_request: ListMessageRequest,
109 option: Option<RequestOption>,
110 ) -> ListMessageIterator<'_> {
111 ListMessageIterator {
112 service: self,
113 req: list_message_request,
114 option,
115 has_more: true,
116 }
117 }
118}
119
120pub struct ListMessageIterator<'a> {
121 service: &'a MessageService,
122 req: ListMessageRequest,
123 option: Option<RequestOption>,
124 has_more: bool,
125}
126
127impl ListMessageIterator<'_> {
128 pub async fn next(&mut self) -> Option<Vec<Message>> {
129 if !self.has_more {
130 return None;
131 }
132 match self
133 .service
134 .list(self.req.clone(), self.option.clone())
135 .await
136 {
137 Ok(data) => {
138 self.has_more = data.has_more;
139 if data.has_more {
140 self.req
141 .api_req
142 .query_params
143 .insert("page_token".to_string(), data.page_token.unwrap());
144 Some(data.items)
145 } else if data.items.is_empty() {
146 None
147 } else {
148 Some(data.items)
149 }
150 }
151 Err(e) => {
152 error!("Error: {e:?}");
153 None
154 }
155 }
156 }
157}
158
159#[derive(Default, Clone)]
180pub struct CreateMessageRequest {
181 api_req: ApiRequest,
182}
183
184impl CreateMessageRequest {
185 pub fn builder() -> CreateMessageRequestBuilder {
187 CreateMessageRequestBuilder::default()
188 }
189}
190
191#[derive(Default)]
195pub struct CreateMessageRequestBuilder {
196 request: CreateMessageRequest,
197}
198
199impl CreateMessageRequestBuilder {
200 pub fn receive_id_type(mut self, receive_id_type: impl ToString) -> Self {
210 self.request
211 .api_req
212 .query_params
213 .insert("receive_id_type".to_string(), receive_id_type.to_string());
214 self
215 }
216
217 pub fn request_body(mut self, body: CreateMessageRequestBody) -> Self {
222 self.request.api_req.body = serde_json::to_vec(&body).unwrap();
223 self
224 }
225
226 pub fn build(self) -> CreateMessageRequest {
228 self.request
229 }
230}
231
232crate::impl_executable_builder_owned!(
234 CreateMessageRequestBuilder,
235 MessageService,
236 CreateMessageRequest,
237 Message,
238 create
239);
240
241#[derive(Debug, Default, Clone, Serialize, Deserialize)]
243pub struct CreateMessageRequestBody {
244 receive_id: String,
249 msg_type: String,
254 content: String,
263 uuid: Option<String>,
272}
273
274impl CreateMessageRequestBody {
275 pub fn builder() -> CreateMessageRequestBodyBuilder {
276 CreateMessageRequestBodyBuilder::default()
277 }
278}
279
280#[derive(Default)]
281pub struct CreateMessageRequestBodyBuilder {
282 request: CreateMessageRequestBody,
283}
284
285impl CreateMessageRequestBodyBuilder {
286 pub fn receive_id(mut self, receive_id: impl ToString) -> Self {
291 self.request.receive_id = receive_id.to_string();
292 self
293 }
294
295 pub fn msg_type(mut self, msg_type: impl ToString) -> Self {
300 self.request.msg_type = msg_type.to_string();
301 self
302 }
303
304 pub fn content(mut self, content: impl ToString) -> Self {
313 self.request.content = content.to_string();
314 self
315 }
316
317 pub fn uuid(mut self, uuid: impl ToString) -> Self {
326 self.request.uuid = Some(uuid.to_string());
327 self
328 }
329
330 pub fn build(self) -> CreateMessageRequestBody {
331 self.request
332 }
333}
334
335#[derive(Debug, Serialize, Deserialize)]
336pub struct CreateMessageResp {
337 pub data: Message,
338}
339
340#[derive(Debug, Serialize, Deserialize)]
362pub struct Message {
363 pub message_id: String,
365 pub root_id: Option<String>,
367 pub parent_id: Option<String>,
369 pub thread_id: Option<String>,
371 pub msg_type: String,
374 pub create_time: String,
376 pub update_time: String,
378 pub deleted: bool,
380 pub updated: bool,
382 pub chat_id: String,
384 pub sender: Sender,
386 pub body: MessageBody,
388 pub mentions: Option<Vec<Mention>>,
390}
391
392impl ApiResponseTrait for Message {
393 fn data_format() -> ResponseFormat {
394 ResponseFormat::Data
395 }
396}
397
398#[derive(Debug, Serialize, Deserialize)]
400pub struct Sender {
401 id: String,
403 id_type: String,
409 sender_type: String,
417 tenant_key: String,
420}
421
422#[derive(Debug, Serialize, Deserialize)]
424pub struct MessageBody {
425 pub content: String,
430}
431
432#[derive(Debug, Serialize, Deserialize)]
434pub struct Mention {
435 pub key: String,
437 pub id: String,
439 pub id_type: String,
441 pub name: String,
443 pub tenant_key: String,
446 pub upper_message_id: String,
448}
449
450#[derive(Default, Clone)]
451pub struct ListMessageRequest {
452 api_req: ApiRequest,
453}
454
455impl ListMessageRequest {
456 pub fn builder() -> ListMessageRequestBuilder {
457 ListMessageRequestBuilder::default()
458 }
459}
460
461#[derive(Default)]
462pub struct ListMessageRequestBuilder {
463 request: ListMessageRequest,
464}
465
466impl ListMessageRequestBuilder {
467 pub fn container_id_type(mut self, container_id_type: impl ToString) -> Self {
471 self.request.api_req.query_params.insert(
472 "container_id_type".to_string(),
473 container_id_type.to_string(),
474 );
475 self
476 }
477
478 pub fn container_id(mut self, container_id: impl ToString) -> Self {
482 self.request
483 .api_req
484 .query_params
485 .insert("container_id".to_string(), container_id.to_string());
486 self
487 }
488
489 pub fn start_time(mut self, start_time: i64) -> Self {
493 self.request
494 .api_req
495 .query_params
496 .insert("start_time".to_string(), start_time.to_string());
497 self
498 }
499
500 pub fn end_time(mut self, end_time: i64) -> Self {
504 self.request
505 .api_req
506 .query_params
507 .insert("end_time".to_string(), end_time.to_string());
508 self
509 }
510
511 pub fn sort_type(mut self, sort_type: impl ToString) -> Self {
515 self.request
516 .api_req
517 .query_params
518 .insert("sort_type".to_string(), sort_type.to_string());
519 self
520 }
521
522 pub fn page_token(mut self, page_token: impl ToString) -> Self {
525 self.request
526 .api_req
527 .query_params
528 .insert("page_token".to_string(), page_token.to_string());
529 self
530 }
531
532 pub fn page_size(mut self, page_size: i32) -> Self {
536 self.request
537 .api_req
538 .query_params
539 .insert("page_size".to_string(), page_size.to_string());
540 self
541 }
542
543 pub fn build(self) -> ListMessageRequest {
544 self.request
545 }
546}
547
548crate::impl_executable_builder_owned!(
549 ListMessageRequestBuilder,
550 MessageService,
551 ListMessageRequest,
552 ListMessageRespData,
553 list
554);
555
556#[derive(Debug, Serialize, Deserialize)]
557pub struct ListMessageRespData {
558 pub has_more: bool,
560 pub page_token: Option<String>,
562 pub items: Vec<Message>,
563}
564
565impl ApiResponseTrait for ListMessageRespData {
566 fn data_format() -> ResponseFormat {
567 ResponseFormat::Data
568 }
569}
570
571pub trait SendMessageTrait {
593 fn msg_type(&self) -> String;
597
598 fn content(&self) -> String;
602}
603
604pub struct MessageText {
633 text: String,
634}
635
636impl MessageText {
637 pub fn new(text: &str) -> Self {
642 Self {
643 text: text.to_string(),
644 }
645 }
646
647 pub fn add_text(mut self, text: &str) -> Self {
652 self.text += text;
653 self
654 }
655
656 pub fn text_line(mut self, text: &str) -> Self {
661 self.text = self.text + text + "\n";
662 self
663 }
664
665 pub fn line(mut self) -> Self {
667 self.text += "\n";
668 self
669 }
670
671 pub fn at_user(mut self, user_id: &str) -> Self {
676 self.text = self.text + &format!("<at user_id=\"{user_id}\"></at>");
677 self
678 }
679
680 pub fn at_all(mut self) -> Self {
682 self.text += "<at user_id=\"all\">name=\"全体成员\"</at>";
683 self
684 }
685
686 pub fn build(self) -> MessageText {
688 MessageText { text: self.text }
689 }
690}
691
692impl SendMessageTrait for MessageText {
693 fn msg_type(&self) -> String {
694 "text".to_string()
695 }
696
697 fn content(&self) -> String {
698 json!({"text": self.text}).to_string()
699 }
700}
701
702#[derive(Debug, Serialize, Deserialize)]
704pub struct MessagePost {
705 #[serde(skip)]
707 default_language: String,
708 post: HashMap<String, MessagePostContent>,
709}
710
711impl SendMessageTrait for MessagePost {
712 fn msg_type(&self) -> String {
713 "post".to_string()
714 }
715
716 fn content(&self) -> String {
717 json!(self).to_string()
718 }
719}
720
721impl MessagePost {
722 pub fn new(lng: &str) -> Self {
723 let post = HashMap::new();
724 Self {
725 default_language: lng.to_string(),
726 post,
727 }
728 }
729
730 pub fn title(mut self, title: impl ToString) -> Self {
731 let post = self
732 .post
733 .entry(self.default_language.clone())
734 .or_insert(MessagePostContent {
735 title: title.to_string(),
736 content: vec![],
737 });
738 post.title = title.to_string();
739 self
740 }
741
742 pub fn append_content(mut self, contents: Vec<MessagePostNode>) -> Self {
744 let post = self
745 .post
746 .entry(self.default_language.clone())
747 .or_insert(MessagePostContent {
748 title: "".to_string(),
749 content: vec![],
750 });
751 post.content.push(contents);
752 self
753 }
754}
755
756#[derive(Debug, Serialize, Deserialize, Default)]
757pub struct MessagePostContent {
758 pub title: String,
760 pub content: Vec<Vec<MessagePostNode>>,
762}
763
764#[derive(Debug, Serialize, Deserialize)]
766#[serde(tag = "tag")]
767pub enum MessagePostNode {
768 #[serde(rename = "text")]
770 Text(TextNode),
771 #[serde(rename = "a")]
772 A(ANode),
773 #[serde(rename = "at")]
774 At(AtNode),
775 #[serde(rename = "img")]
776 Img(ImgNode),
777 #[serde(rename = "media")]
778 Media(MediaNode),
779 #[serde(rename = "emotion")]
780 Emotion(EmotionNode),
781}
782
783#[derive(Debug, Serialize, Deserialize)]
785pub struct TextNode {
786 text: String,
787 #[serde(skip_serializing_if = "Option::is_none")]
789 un_escape: Option<bool>,
790 #[serde(skip_serializing_if = "Option::is_none")]
793 style: Option<Vec<String>>,
794}
795
796impl TextNode {
797 pub fn new(text: &str) -> Self {
798 Self {
799 text: text.to_string(),
800 un_escape: None,
801 style: None,
802 }
803 }
804
805 pub fn un_escape(mut self, un_escape: bool) -> Self {
806 self.un_escape = Some(un_escape);
807 self
808 }
809
810 pub fn style(mut self, style: Vec<&str>) -> Self {
811 self.style = Some(style.iter().map(|s| s.to_string()).collect());
812 self
813 }
814}
815
816#[derive(Debug, Serialize, Deserialize)]
818pub struct ANode {
819 text: String,
821 href: String,
823 #[serde(skip_serializing_if = "Option::is_none")]
826 style: Option<Vec<String>>,
827}
828
829impl ANode {
830 pub fn new(text: &str, href: &str) -> Self {
831 Self {
832 text: text.to_string(),
833 href: href.to_string(),
834 style: None,
835 }
836 }
837
838 pub fn style(mut self, style: Vec<&str>) -> Self {
839 self.style = Some(style.iter().map(|s| s.to_string()).collect());
840 self
841 }
842}
843
844#[derive(Debug, Serialize, Deserialize)]
845pub struct AtNode {
846 user_id: String,
849 #[serde(skip_serializing_if = "Option::is_none")]
852 style: Option<Vec<String>>,
853}
854
855impl AtNode {
856 pub fn new(user_id: &str) -> Self {
857 Self {
858 user_id: user_id.to_string(),
859 style: None,
860 }
861 }
862
863 pub fn style(mut self, style: Vec<&str>) -> Self {
864 self.style = Some(style.iter().map(|s| s.to_string()).collect());
865 self
866 }
867}
868
869#[derive(Debug, Serialize, Deserialize)]
870pub struct ImgNode {
871 image_key: String,
873}
874
875impl ImgNode {
876 pub fn new(image_key: &str) -> Self {
877 Self {
878 image_key: image_key.to_string(),
879 }
880 }
881}
882
883#[derive(Debug, Serialize, Deserialize)]
884pub struct MediaNode {
885 file_key: String,
887 #[serde(skip_serializing_if = "Option::is_none")]
889 image_key: Option<String>,
890}
891
892impl MediaNode {
893 pub fn new(file_key: &str, image_key: Option<&str>) -> Self {
894 Self {
895 file_key: file_key.to_string(),
896 image_key: image_key.map(|s| s.to_string()),
897 }
898 }
899}
900
901#[derive(Debug, Serialize, Deserialize)]
903pub struct EmotionNode {
904 emoji_type: String,
906}
907
908impl EmotionNode {
909 pub fn new(emoji_type: &str) -> Self {
910 Self {
911 emoji_type: emoji_type.to_string(),
912 }
913 }
914}
915
916pub struct MessageImage {
918 pub image_key: String,
919}
920
921impl SendMessageTrait for MessageImage {
922 fn msg_type(&self) -> String {
923 "image".to_string()
924 }
925
926 fn content(&self) -> String {
927 json!({"image_key": self.image_key}).to_string()
928 }
929}
930
931#[derive(Debug, Serialize, Deserialize)]
933pub struct MessageCardTemplate {
934 r#type: String,
936 data: CardTemplate,
938}
939
940impl SendMessageTrait for MessageCardTemplate {
941 fn msg_type(&self) -> String {
942 "interactive".to_string()
943 }
944
945 fn content(&self) -> String {
946 serde_json::to_string(self).unwrap()
947 }
948}
949
950impl MessageCardTemplate {
951 pub fn new(template_id: impl ToString, template_variable: Value) -> Self {
952 Self {
953 r#type: "template".to_string(),
954 data: CardTemplate {
955 template_id: template_id.to_string(),
956 template_variable,
957 },
958 }
959 }
960}
961
962#[derive(Debug, Serialize, Deserialize)]
964struct CardTemplate {
965 template_id: String,
967 template_variable: Value,
969}
970
971#[cfg(test)]
972mod test {
973 use serde_json::json;
974
975 use crate::service::im::v1::message::{
976 ANode, AtNode, EmotionNode, ImgNode, MediaNode, MessageText, SendMessageTrait, TextNode,
977 };
978
979 #[test]
980 fn test_message_text() {
981 let t1 = MessageText::new("").add_text(" test content").build();
982 assert_eq!(t1.text, " test content");
983 let t2 = MessageText::new("").text_line(" test content").build();
984 assert_eq!(t2.text, " test content\n");
985 let t3 = MessageText::new("")
986 .add_text(" test content")
987 .line()
988 .build();
989 assert_eq!(t3.text, " test content\n");
990 let t4 = MessageText::new("")
991 .add_text(" test content")
992 .at_user("user_id")
993 .build();
994 assert_eq!(t4.text, " test content<at user_id=\"user_id\"></at>");
995 let t5 = MessageText::new("").at_all().build();
996 assert_eq!(t5.text, "<at user_id=\"all\">name=\"全体成员\"</at>");
997 }
998
999 #[test]
1000 fn test_message_post() {
1001 use crate::service::im::v1::message::{MessagePost, MessagePostNode};
1002 let post = MessagePost::new("zh_cn")
1003 .title("title")
1004 .append_content(vec![
1005 MessagePostNode::Text(TextNode::new("text")),
1006 MessagePostNode::A(ANode::new("text", "https://www.feishu.cn")),
1007 MessagePostNode::At(AtNode::new("user_id")),
1008 MessagePostNode::Img(ImgNode::new("image_key")),
1009 MessagePostNode::Media(MediaNode::new("file_key", Some("image_key"))),
1010 MessagePostNode::Emotion(EmotionNode::new("SMILE")),
1011 ]);
1012 assert_eq!(post.msg_type(), "post");
1013 assert_eq!(
1014 json!(post),
1015 json!({
1016 "post": {
1017 "zh_cn": {
1018 "title":"title",
1019 "content": [[{"tag":"text","text":"text"},{"tag":"a","text":"text","href":"https://www.feishu.cn"},{"tag":"at","user_id":"user_id"},{"tag":"img","image_key":"image_key"},{"tag":"media","file_key":"file_key","image_key":"image_key"},{"tag":"emotion","emoji_type":"SMILE"}
1020 ]]
1021 }}})
1022 );
1023 }
1024
1025 #[test]
1026 fn test_message_image() {
1027 use crate::service::im::v1::message::MessageImage;
1028 let image = MessageImage {
1029 image_key: "image_key".to_string(),
1030 };
1031 assert_eq!(image.msg_type(), "image");
1032 assert_eq!(
1033 image.content(),
1034 json!({"image_key": "image_key"}).to_string()
1035 );
1036 }
1037}