Skip to main content

cortexfs_channels/platform/
discord.rs

1use serde_json::json;
2
3use super::{ChannelCodec, OutboundRequest, object, participant, scalar, text};
4use crate::{
5    ChannelError, ChannelId, ConversationId, InboundMessage, MessageTarget, OutboundMessage,
6};
7
8/// Discord message/webhook codec. Gateway authentication and websocket choice remain host-owned.
9#[derive(Clone, Copy, Debug, Default)]
10pub struct DiscordCodec;
11
12impl ChannelCodec for DiscordCodec {
13    fn channel(&self) -> ChannelId {
14        ChannelId::from_static("discord")
15    }
16
17    fn decode(&self, payload: &str) -> Result<Option<InboundMessage>, ChannelError> {
18        let root = object(payload)?;
19        if root
20            .get("author")
21            .and_then(|author| author.get("bot"))
22            .and_then(serde_json::Value::as_bool)
23            == Some(true)
24        {
25            return Ok(None);
26        }
27        let id = scalar(root.get("id"), "id")?;
28        let conversation = ConversationId::new(scalar(root.get("channel_id"), "channel_id")?)?;
29        Ok(Some(InboundMessage {
30            id,
31            target: MessageTarget {
32                channel: self.channel(),
33                conversation,
34                thread: root
35                    .get("thread_id")
36                    .map(|value| scalar(Some(value), "thread_id"))
37                    .transpose()?,
38                reply_to: root
39                    .get("message_reference")
40                    .and_then(|reference| reference.get("message_id"))
41                    .map(|value| scalar(Some(value), "message_reference.message_id"))
42                    .transpose()?,
43            },
44            sender: participant(
45                root.get("author"),
46                scalar(
47                    root.get("author").and_then(|value| value.get("id")),
48                    "author.id",
49                )?,
50            ),
51            body: text(root.get("content"))?,
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                "discord media attachments".to_owned(),
62            ));
63        }
64        let mut fields = serde_json::Map::new();
65        fields.insert("content".to_owned(), json!(message.body.text));
66        fields.insert("allowed_mentions".to_owned(), json!({ "parse": [] }));
67        if let Some(thread) = message.target.thread.as_deref() {
68            fields.insert("thread_id".to_owned(), json!(thread));
69        }
70        Ok(OutboundRequest {
71            method: "POST".to_owned(),
72            path: "webhook".to_owned(),
73            content_type: "application/json".to_owned(),
74            body: serde_json::Value::Object(fields).to_string(),
75            headers: std::collections::BTreeMap::new(),
76        })
77    }
78}