Skip to main content

okbot_core/
message.rs

1//! Messages flowing through the pipeline.
2
3use jiff::Timestamp;
4use serde::{Deserialize, Serialize};
5
6use crate::id::{ConversationKey, MessageId, UserRef};
7
8/// Media attached to a message. Platforms that deliver media do so by URL.
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(tag = "kind", rename_all = "kebab-case")]
11#[non_exhaustive]
12pub enum Attachment {
13    /// An image.
14    Image {
15        /// Where to fetch it.
16        url: String,
17        /// Text sent with the image.
18        caption: Option<String>,
19    },
20    /// A voice or audio message.
21    Audio {
22        /// Where to fetch it.
23        url: String,
24    },
25    /// A sticker.
26    Sticker {
27        /// Platform sticker id or URL.
28        id: String,
29    },
30    /// Any other file.
31    File {
32        /// Where to fetch it.
33        url: String,
34        /// File name, when known.
35        name: Option<String>,
36    },
37}
38
39/// A message received from a platform, normalized by its channel adapter.
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41pub struct InboundEvent {
42    /// Platform event or message id, used to drop duplicate deliveries.
43    pub event_id: String,
44    /// The conversation it belongs to.
45    pub conversation: ConversationKey,
46    /// Who wrote it.
47    pub author: UserRef,
48    /// The text, with mentions of the bot removed and other mentions written as `@name`.
49    pub text: String,
50    /// Media sent with the message.
51    pub attachments: Vec<Attachment>,
52    /// The bot was mentioned (or replied to, on platforms that only deliver such group messages).
53    pub mentions_bot: bool,
54    /// A one-to-one conversation with the bot.
55    pub is_direct: bool,
56    /// The platform withheld the content (for example Zalo's `message.unsupported.received` for
57    /// protected users). The runtime answers with a fixed message and never calls an agent.
58    pub restricted: bool,
59    /// Platform message id, used to reply to this message where supported.
60    pub message_id: Option<MessageId>,
61    /// When the platform says it was sent (or when it was received, if unknown).
62    pub sent_at: Timestamp,
63    /// The author's language as the platform reports it (`en-US`, `ja`, `vi`), when it does:
64    /// Discord slash commands and button presses, Telegram messages. Plain Discord, Slack, Google
65    /// Chat and Zalo messages carry none.
66    #[serde(default, skip_serializing_if = "Option::is_none")]
67    pub locale: Option<String>,
68}
69
70/// Where a reply goes.
71#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
72pub struct ReplyTarget {
73    /// The conversation to post into.
74    pub conversation: ConversationKey,
75    /// The message being answered, on platforms that can quote or thread under it.
76    pub in_reply_to: Option<MessageId>,
77}
78
79impl ReplyTarget {
80    /// The target that answers `event`.
81    pub fn to_event(event: &InboundEvent) -> Self {
82        Self {
83            conversation: event.conversation.clone(),
84            in_reply_to: event.message_id.clone(),
85        }
86    }
87}
88
89/// A reply in canonical Markdown, before rendering for a platform.
90#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
91pub struct OutboundMessage {
92    /// CommonMark text.
93    pub markdown: String,
94    /// Media to send.
95    pub attachments: Vec<Attachment>,
96}
97
98impl OutboundMessage {
99    /// A text-only message.
100    pub fn text(markdown: impl Into<String>) -> Self {
101        Self {
102            markdown: markdown.into(),
103            attachments: Vec::new(),
104        }
105    }
106}
107
108/// One platform message, already in the platform's dialect and within its length limit.
109#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
110pub struct RenderedMessage {
111    /// Text in the channel's [`MarkdownDialect`](crate::MarkdownDialect).
112    pub text: String,
113}
114
115/// A message the bot sent, for later edits.
116#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
117pub struct SentRef {
118    /// Where it was sent.
119    pub target: ReplyTarget,
120    /// The platform's id for it.
121    pub message_id: MessageId,
122}