cortexfs_channels/platform/
feishu.rs1use serde_json::{Value, json};
2
3use super::{ChannelCodec, OutboundRequest, object, participant, scalar, string};
4use crate::{
5 ChannelError, ChannelId, ConversationId, InboundMessage, MessageBody, MessageTarget,
6 OutboundMessage,
7};
8
9#[derive(Clone, Copy, Debug, Default)]
11pub struct FeishuCodec;
12
13impl ChannelCodec for FeishuCodec {
14 fn channel(&self) -> ChannelId {
15 ChannelId::from_static("feishu")
16 }
17
18 fn decode(&self, payload: &str) -> Result<Option<InboundMessage>, ChannelError> {
19 let root = object(payload)?;
20 let event = root.get("event").unwrap_or(&root);
21 let message = event
22 .get("message")
23 .ok_or_else(|| ChannelError::Protocol("feishu message is missing".to_owned()))?;
24 let body = message
25 .get("content")
26 .and_then(Value::as_str)
27 .ok_or_else(|| {
28 ChannelError::Protocol("feishu message.content is missing".to_owned())
29 })?;
30 let body: Value = serde_json::from_str(body)
31 .map_err(|error| ChannelError::Protocol(format!("invalid feishu content: {error}")))?;
32 let sender = event.get("sender").and_then(|value| value.get("sender_id"));
33 let sender_id = sender
34 .and_then(|value| value.get("open_id").or_else(|| value.get("user_id")))
35 .map(|value| scalar(Some(value), "sender.sender_id"))
36 .transpose()?
37 .unwrap_or_else(|| "unknown".to_owned());
38 let conversation = ConversationId::new(string(message.get("chat_id"), "message.chat_id")?)?;
39 Ok(Some(InboundMessage {
40 id: string(message.get("message_id"), "message.message_id")?,
41 target: MessageTarget {
42 channel: self.channel(),
43 conversation,
44 thread: message
45 .get("root_id")
46 .map(|value| string(Some(value), "message.root_id"))
47 .transpose()?,
48 reply_to: None,
49 },
50 sender: participant(None, sender_id),
51 body: MessageBody::text(string(body.get("text"), "content.text")?)?,
52 timestamp_ms: None,
53 metadata: std::collections::BTreeMap::new(),
54 }))
55 }
56
57 fn encode(&self, message: &OutboundMessage) -> Result<OutboundRequest, ChannelError> {
58 message.body.validate()?;
59 if !message.body.attachments.is_empty() {
60 return Err(ChannelError::Unsupported(
61 "feishu media attachments".to_owned(),
62 ));
63 }
64 let body = json!({
65 "receive_id": message.target.conversation.as_str(),
66 "msg_type": "text",
67 "content": json!({ "text": message.body.text }).to_string(),
68 });
69 Ok(OutboundRequest {
70 method: "POST".to_owned(),
71 path: "im/v1/messages".to_owned(),
72 content_type: "application/json".to_owned(),
73 body: body.to_string(),
74 headers: std::collections::BTreeMap::new(),
75 })
76 }
77}