use std::collections::HashMap;
use log::error;
use reqwest::Method;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use crate::core::{
api_req::ApiRequest,
api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
config::Config,
constants::AccessTokenType,
http::Transport,
req_option::RequestOption,
SDKResult,
};
pub struct MessageService {
pub config: Config,
}
impl MessageService {
pub async fn create(
&self,
create_message_request: CreateMessageRequest,
option: Option<RequestOption>,
) -> SDKResult<BaseResponse<Message>> {
let mut api_req = create_message_request.api_req;
api_req.http_method = Method::POST;
api_req.api_path = "/open-apis/im/v1/messages".to_string();
api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
let api_resp = Transport::request(api_req, &self.config, option).await?;
Ok(api_resp)
}
pub async fn list(
&self,
list_message_request: ListMessageRequest,
option: Option<RequestOption>,
) -> SDKResult<BaseResponse<ListMessageRespData>> {
let mut api_req = list_message_request.api_req;
api_req.http_method = Method::GET;
api_req.api_path = "/open-apis/im/v1/messages".to_string();
api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
let api_resp = Transport::request(api_req, &self.config, option).await?;
Ok(api_resp)
}
pub fn list_iter(
&self,
list_message_request: ListMessageRequest,
option: Option<RequestOption>,
) -> ListMessageIterator {
ListMessageIterator {
service: self,
req: list_message_request,
option,
has_more: true,
}
}
}
pub struct ListMessageIterator<'a> {
service: &'a MessageService,
req: ListMessageRequest,
option: Option<RequestOption>,
has_more: bool,
}
impl<'a> ListMessageIterator<'a> {
pub async fn next(&mut self) -> Option<Vec<Message>> {
if !self.has_more {
return None;
}
match self
.service
.list(self.req.clone(), self.option.clone())
.await
{
Ok(resp) => match resp.data {
Some(data) => {
self.has_more = data.has_more;
if data.has_more {
self.req
.api_req
.query_params
.insert("page_token".to_string(), data.page_token.unwrap());
Some(data.items)
} else if data.items.is_empty() {
None
} else {
Some(data.items)
}
}
None => None,
},
Err(e) => {
error!("Error: {:?}", e);
None
}
}
}
}
#[derive(Default)]
pub struct CreateMessageRequest {
api_req: ApiRequest,
}
impl CreateMessageRequest {
pub fn builder() -> CreateMessageRequestBuilder {
CreateMessageRequestBuilder::default()
}
}
#[derive(Default)]
pub struct CreateMessageRequestBuilder {
request: CreateMessageRequest,
}
impl CreateMessageRequestBuilder {
pub fn receive_id_type(mut self, receive_id_type: impl ToString) -> Self {
self.request
.api_req
.query_params
.insert("receive_id_type".to_string(), receive_id_type.to_string());
self
}
pub fn request_body(mut self, body: CreateMessageRequestBody) -> Self {
self.request.api_req.body = serde_json::to_vec(&body).unwrap();
self
}
pub fn build(self) -> CreateMessageRequest {
self.request
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct CreateMessageRequestBody {
receive_id: String,
msg_type: String,
content: String,
uuid: Option<String>,
}
impl CreateMessageRequestBody {
pub fn builder() -> CreateMessageRequestBodyBuilder {
CreateMessageRequestBodyBuilder::default()
}
}
#[derive(Default)]
pub struct CreateMessageRequestBodyBuilder {
request: CreateMessageRequestBody,
}
impl CreateMessageRequestBodyBuilder {
pub fn receive_id(mut self, receive_id: impl ToString) -> Self {
self.request.receive_id = receive_id.to_string();
self
}
pub fn msg_type(mut self, msg_type: impl ToString) -> Self {
self.request.msg_type = msg_type.to_string();
self
}
pub fn content(mut self, content: impl ToString) -> Self {
self.request.content = content.to_string();
self
}
pub fn uuid(mut self, uuid: impl ToString) -> Self {
self.request.uuid = Some(uuid.to_string());
self
}
pub fn build(self) -> CreateMessageRequestBody {
self.request
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CreateMessageResp {
pub data: Message,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Message {
pub message_id: String,
pub root_id: Option<String>,
pub parent_id: Option<String>,
pub thread_id: Option<String>,
pub msg_type: String,
pub create_time: String,
pub update_time: String,
pub deleted: bool,
pub updated: bool,
pub chat_id: String,
pub sender: Sender,
pub body: MessageBody,
pub mentions: Option<Vec<Mention>>,
}
impl ApiResponseTrait for Message {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Sender {
id: String,
id_type: String,
sender_type: String,
tenant_key: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct MessageBody {
pub content: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Mention {
pub key: String,
pub id: String,
pub id_type: String,
pub name: String,
pub tenant_key: String,
pub upper_message_id: String,
}
#[derive(Default, Clone)]
pub struct ListMessageRequest {
api_req: ApiRequest,
}
impl ListMessageRequest {
pub fn builder() -> ListMessageRequestBuilder {
ListMessageRequestBuilder::default()
}
}
#[derive(Default)]
pub struct ListMessageRequestBuilder {
request: ListMessageRequest,
}
impl ListMessageRequestBuilder {
pub fn container_id_type(mut self, container_id_type: impl ToString) -> Self {
self.request.api_req.query_params.insert(
"container_id_type".to_string(),
container_id_type.to_string(),
);
self
}
pub fn container_id(mut self, container_id: impl ToString) -> Self {
self.request
.api_req
.query_params
.insert("container_id".to_string(), container_id.to_string());
self
}
pub fn start_time(mut self, start_time: i64) -> Self {
self.request
.api_req
.query_params
.insert("start_time".to_string(), start_time.to_string());
self
}
pub fn end_time(mut self, end_time: i64) -> Self {
self.request
.api_req
.query_params
.insert("end_time".to_string(), end_time.to_string());
self
}
pub fn sort_type(mut self, sort_type: impl ToString) -> Self {
self.request
.api_req
.query_params
.insert("sort_type".to_string(), sort_type.to_string());
self
}
pub fn page_token(mut self, page_token: impl ToString) -> Self {
self.request
.api_req
.query_params
.insert("page_token".to_string(), page_token.to_string());
self
}
pub fn page_size(mut self, page_size: i32) -> Self {
self.request
.api_req
.query_params
.insert("page_size".to_string(), page_size.to_string());
self
}
pub fn build(self) -> ListMessageRequest {
self.request
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ListMessageRespData {
pub has_more: bool,
pub page_token: Option<String>,
pub items: Vec<Message>,
}
impl ApiResponseTrait for ListMessageRespData {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
pub trait SendMessageTrait {
fn msg_type(&self) -> String;
fn content(&self) -> String;
}
pub struct MessageText {
text: String,
}
impl MessageText {
pub fn new(text: &str) -> Self {
Self {
text: text.to_string(),
}
}
pub fn add_text(mut self, text: &str) -> Self {
self.text += text;
self
}
pub fn text_line(mut self, text: &str) -> Self {
self.text = self.text + text + "\n";
self
}
pub fn line(mut self) -> Self {
self.text += "\n";
self
}
pub fn at_user(mut self, user_id: &str) -> Self {
self.text = self.text + &format!("<at user_id=\"{}\"></at>", user_id);
self
}
pub fn at_all(mut self) -> Self {
self.text += "<at user_id=\"all\">name=\"全体成员\"</at>";
self
}
pub fn build(self) -> MessageText {
MessageText { text: self.text }
}
}
impl SendMessageTrait for MessageText {
fn msg_type(&self) -> String {
"text".to_string()
}
fn content(&self) -> String {
json!({"text": self.text}).to_string()
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct MessagePost {
#[serde(skip)]
default_language: String,
post: HashMap<String, MessagePostContent>,
}
impl SendMessageTrait for MessagePost {
fn msg_type(&self) -> String {
"post".to_string()
}
fn content(&self) -> String {
json!(self).to_string()
}
}
impl MessagePost {
pub fn new(lng: &str) -> Self {
let post = HashMap::new();
Self {
default_language: lng.to_string(),
post,
}
}
pub fn title(mut self, title: impl ToString) -> Self {
let post = self
.post
.entry(self.default_language.clone())
.or_insert(MessagePostContent {
title: title.to_string(),
content: vec![],
});
post.title = title.to_string();
self
}
pub fn append_content(mut self, contents: Vec<MessagePostNode>) -> Self {
let post = self
.post
.entry(self.default_language.clone())
.or_insert(MessagePostContent {
title: "".to_string(),
content: vec![],
});
post.content.push(contents);
self
}
}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct MessagePostContent {
pub title: String,
pub content: Vec<Vec<MessagePostNode>>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "tag")]
pub enum MessagePostNode {
#[serde(rename = "text")]
Text(TextNode),
#[serde(rename = "a")]
A(ANode),
#[serde(rename = "at")]
At(AtNode),
#[serde(rename = "img")]
Img(ImgNode),
#[serde(rename = "media")]
Media(MediaNode),
#[serde(rename = "emotion")]
Emotion(EmotionNode),
}
#[derive(Debug, Serialize, Deserialize)]
pub struct TextNode {
text: String,
#[serde(skip_serializing_if = "Option::is_none")]
un_escape: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<Vec<String>>,
}
impl TextNode {
pub fn new(text: &str) -> Self {
Self {
text: text.to_string(),
un_escape: None,
style: None,
}
}
pub fn un_escape(mut self, un_escape: bool) -> Self {
self.un_escape = Some(un_escape);
self
}
pub fn style(mut self, style: Vec<&str>) -> Self {
self.style = Some(style.iter().map(|s| s.to_string()).collect());
self
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ANode {
text: String,
href: String,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<Vec<String>>,
}
impl ANode {
pub fn new(text: &str, href: &str) -> Self {
Self {
text: text.to_string(),
href: href.to_string(),
style: None,
}
}
pub fn style(mut self, style: Vec<&str>) -> Self {
self.style = Some(style.iter().map(|s| s.to_string()).collect());
self
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AtNode {
user_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<Vec<String>>,
}
impl AtNode {
pub fn new(user_id: &str) -> Self {
Self {
user_id: user_id.to_string(),
style: None,
}
}
pub fn style(mut self, style: Vec<&str>) -> Self {
self.style = Some(style.iter().map(|s| s.to_string()).collect());
self
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ImgNode {
image_key: String,
}
impl ImgNode {
pub fn new(image_key: &str) -> Self {
Self {
image_key: image_key.to_string(),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct MediaNode {
file_key: String,
#[serde(skip_serializing_if = "Option::is_none")]
image_key: Option<String>,
}
impl MediaNode {
pub fn new(file_key: &str, image_key: Option<&str>) -> Self {
Self {
file_key: file_key.to_string(),
image_key: image_key.map(|s| s.to_string()),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct EmotionNode {
emoji_type: String,
}
impl EmotionNode {
pub fn new(emoji_type: &str) -> Self {
Self {
emoji_type: emoji_type.to_string(),
}
}
}
pub struct MessageImage {
pub image_key: String,
}
impl SendMessageTrait for MessageImage {
fn msg_type(&self) -> String {
"image".to_string()
}
fn content(&self) -> String {
json!({"image_key": self.image_key}).to_string()
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct MessageCardTemplate {
r#type: String,
data: CardTemplate,
}
impl SendMessageTrait for MessageCardTemplate {
fn msg_type(&self) -> String {
"interactive".to_string()
}
fn content(&self) -> String {
serde_json::to_string(self).unwrap()
}
}
impl MessageCardTemplate {
pub fn new(template_id: impl ToString, template_variable: Value) -> Self {
Self {
r#type: "template".to_string(),
data: CardTemplate {
template_id: template_id.to_string(),
template_variable,
},
}
}
}
#[derive(Debug, Serialize, Deserialize)]
struct CardTemplate {
template_id: String,
template_variable: Value,
}
#[cfg(test)]
mod test {
use serde_json::json;
use crate::service::im::v1::message::{
ANode, AtNode, EmotionNode, ImgNode, MediaNode, MessageText, SendMessageTrait, TextNode,
};
#[test]
fn test_message_text() {
let t1 = MessageText::new("").add_text(" test content").build();
assert_eq!(t1.text, " test content");
let t2 = MessageText::new("").text_line(" test content").build();
assert_eq!(t2.text, " test content\n");
let t3 = MessageText::new("")
.add_text(" test content")
.line()
.build();
assert_eq!(t3.text, " test content\n");
let t4 = MessageText::new("")
.add_text(" test content")
.at_user("user_id")
.build();
assert_eq!(t4.text, " test content<at user_id=\"user_id\"></at>");
let t5 = MessageText::new("").at_all().build();
assert_eq!(t5.text, "<at user_id=\"all\">name=\"全体成员\"</at>");
}
#[test]
fn test_message_post() {
use crate::service::im::v1::message::{MessagePost, MessagePostNode};
let post = MessagePost::new("zh_cn")
.title("title")
.append_content(vec![
MessagePostNode::Text(TextNode::new("text")),
MessagePostNode::A(ANode::new("text", "https://www.feishu.cn")),
MessagePostNode::At(AtNode::new("user_id")),
MessagePostNode::Img(ImgNode::new("image_key")),
MessagePostNode::Media(MediaNode::new("file_key", Some("image_key"))),
MessagePostNode::Emotion(EmotionNode::new("SMILE")),
]);
assert_eq!(post.msg_type(), "post");
assert_eq!(
json!(post),
json!({
"post": {
"zh_cn": {
"title":"title",
"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"}
]]
}}})
);
}
#[test]
fn test_message_image() {
use crate::service::im::v1::message::MessageImage;
let image = MessageImage {
image_key: "image_key".to_string(),
};
assert_eq!(image.msg_type(), "image");
assert_eq!(
image.content(),
json!({"image_key": "image_key"}).to_string()
);
}
}