tgbot/types/reaction/
mod.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{
4    api::{Method, Payload},
5    types::{Chat, ChatId, Integer, User},
6};
7
8#[cfg(test)]
9mod tests;
10
11/// Represents a reaction added to a message along with the number of times it was added.
12#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
13pub struct ReactionCount {
14    /// Type of the reaction.
15    #[serde(rename = "type")]
16    pub reaction_type: ReactionType,
17    /// Number of times the reaction was added.
18    pub total_count: Integer,
19}
20
21impl ReactionCount {
22    /// Creates a new `ReactionCount`.
23    ///
24    /// # Arguments
25    ///
26    /// * `reaction_type` - Type of the reaction.
27    /// * `total_count` - Number of times the reaction was added.
28    pub fn new(reaction_type: ReactionType, total_count: Integer) -> Self {
29        Self {
30            reaction_type,
31            total_count,
32        }
33    }
34}
35
36/// Represents reaction changes on a message with anonymous reactions.
37#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
38pub struct MessageReactionCountUpdated {
39    /// The chat containing the message.
40    pub chat: Chat,
41    /// Date of the change in Unix time.
42    pub date: Integer,
43    /// Unique message identifier inside the chat.
44    pub message_id: Integer,
45    /// List of reactions that are present on the message.
46    pub reactions: Vec<ReactionCount>,
47}
48
49impl MessageReactionCountUpdated {
50    /// Creates a new `MessageReactionCountUpdated`.
51    ///
52    /// # Arguments
53    ///
54    /// * `chat` - The chat containing the message.
55    /// * `date` - Date of the change in Unix time.
56    /// * `message_id` - Unique message identifier inside the chat.
57    /// * `reactions` - List of reactions that are present on the message.
58    pub fn new<A, B>(chat: A, date: Integer, message_id: Integer, reactions: B) -> Self
59    where
60        A: Into<Chat>,
61        B: IntoIterator<Item = ReactionCount>,
62    {
63        Self {
64            chat: chat.into(),
65            date,
66            message_id,
67            reactions: reactions.into_iter().collect(),
68        }
69    }
70}
71
72/// Represents a reaction type.
73#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
74#[serde(from = "RawReactionType", into = "RawReactionType")]
75pub enum ReactionType {
76    /// A reaction based on a custom emoji.
77    CustomEmoji(String),
78    /// A reaction based on a predefined emoji.
79    Emoji(String),
80    /// The reaction is paid.
81    Paid,
82}
83
84impl ReactionType {
85    /// Creates a new `ReactionType` based on a custom emoji.
86    ///
87    /// # Arguments
88    ///
89    /// `value` - A custom emoji.
90    pub fn custom_emoji<T>(value: T) -> Self
91    where
92        T: Into<String>,
93    {
94        Self::CustomEmoji(value.into())
95    }
96
97    /// Creates a new `ReactionType` based on a predefined emoji.
98    ///
99    /// # Arguments
100    ///
101    /// `value` - A predefined emoji.
102    pub fn emoji<T>(value: T) -> Self
103    where
104        T: Into<String>,
105    {
106        Self::Emoji(value.into())
107    }
108}
109
110#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
111#[serde(rename_all = "snake_case", tag = "type")]
112enum RawReactionType {
113    CustomEmoji { custom_emoji: String },
114    Emoji { emoji: String },
115    Paid,
116}
117
118impl From<ReactionType> for RawReactionType {
119    fn from(value: ReactionType) -> Self {
120        match value {
121            ReactionType::CustomEmoji(custom_emoji) => Self::CustomEmoji { custom_emoji },
122            ReactionType::Emoji(emoji) => Self::Emoji { emoji },
123            ReactionType::Paid => Self::Paid,
124        }
125    }
126}
127
128impl From<RawReactionType> for ReactionType {
129    fn from(value: RawReactionType) -> Self {
130        match value {
131            RawReactionType::CustomEmoji { custom_emoji } => Self::CustomEmoji(custom_emoji),
132            RawReactionType::Emoji { emoji } => Self::Emoji(emoji),
133            RawReactionType::Paid => Self::Paid,
134        }
135    }
136}
137
138/// Represents a change of a reaction on a message performed by a user.
139#[serde_with::skip_serializing_none]
140#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
141pub struct MessageReactionUpdated {
142    /// The chat containing the message the user reacted to.
143    pub chat: Chat,
144    /// Date of the change in Unix time.
145    pub date: Integer,
146    /// Unique identifier of the message inside the chat.
147    pub message_id: Integer,
148    /// New list of reaction types that have been set by the user.
149    pub new_reaction: Vec<ReactionType>,
150    /// Previous list of reaction types that were set by the user.
151    pub old_reaction: Vec<ReactionType>,
152    /// The chat on behalf of which the reaction was changed, if the user is anonymous.
153    pub actor_chat: Option<Chat>,
154    /// The user that changed the reaction, if the user isn't anonymous.
155    pub user: Option<User>,
156}
157
158impl MessageReactionUpdated {
159    /// Creates a new `MessageReactionUpdated`.
160    ///
161    ///  # Arguments
162    ///
163    /// * `chat` - The chat containing the message the user reacted to.
164    /// * `date` - Date of the change in Unix time.
165    /// * `message_id` - Unique identifier of the message inside the chat.
166    /// * `new_reaction` - New list of reaction types that have been set by the user.
167    /// * `old_reaction` - Previous list of reaction types that were set by the user.
168    pub fn new<A, B, C>(chat: A, date: Integer, message_id: Integer, new_reaction: B, old_reaction: C) -> Self
169    where
170        A: Into<Chat>,
171        B: IntoIterator<Item = ReactionType>,
172        C: IntoIterator<Item = ReactionType>,
173    {
174        Self {
175            chat: chat.into(),
176            date,
177            message_id,
178            new_reaction: new_reaction.into_iter().collect(),
179            old_reaction: old_reaction.into_iter().collect(),
180            actor_chat: None,
181            user: None,
182        }
183    }
184
185    /// Sets a new actor chat.
186    ///
187    /// # Arguments
188    ///
189    /// * `value` - The chat on behalf of which the reaction was changed, if the user is anonymous.
190    pub fn with_actor_chat<T>(mut self, value: T) -> Self
191    where
192        T: Into<Chat>,
193    {
194        self.actor_chat = Some(value.into());
195        self
196    }
197
198    /// Sets a new user.
199    ///
200    /// # Arguments
201    ///
202    /// * `value` - The user that changed the reaction, if the user isn't anonymous.
203    pub fn with_user(mut self, value: User) -> Self {
204        self.user = Some(value);
205        self
206    }
207}
208
209/// Changes the chosen reactions on a message.
210///
211/// Service messages of some types can't be reacted to.
212/// Automatically forwarded messages from a channel to its discussion group have
213/// the same available reactions as messages in the channel.
214/// Bots can't use paid reactions.
215#[serde_with::skip_serializing_none]
216#[derive(Clone, Debug, Serialize)]
217pub struct SetMessageReaction {
218    chat_id: ChatId,
219    is_big: bool,
220    message_id: Integer,
221    reaction: Option<Vec<ReactionType>>,
222}
223
224impl SetMessageReaction {
225    /// Creates a new `SetMessageReaction`.
226    ///
227    /// # Arguments
228    ///
229    /// * `chat_id` - Unique identifier of the target chat.
230    /// * `message_id` - Identifier of the target message.
231    pub fn new<T>(chat_id: T, message_id: Integer) -> Self
232    where
233        T: Into<ChatId>,
234    {
235        Self {
236            chat_id: chat_id.into(),
237            message_id,
238            reaction: None,
239            is_big: false,
240        }
241    }
242
243    /// Sets a new value for the `is_big` flag.
244    ///
245    /// # Arguments
246    ///
247    /// * `value` - Whether to set the reaction with a big animation.
248    pub fn with_is_big(mut self, value: bool) -> Self {
249        self.is_big = value;
250        self
251    }
252
253    /// Sets a new list of reaction types.
254    ///
255    /// # Arguments
256    ///
257    /// * `value` - New list of reaction types to set on the message.
258    ///
259    /// Currently, as non-premium users, bots can set up to one reaction per message.
260    /// A custom emoji reaction can be used if it is either already present on
261    /// the message or explicitly allowed by chat administrators.
262    pub fn with_reaction<T>(mut self, value: T) -> Self
263    where
264        T: IntoIterator<Item = ReactionType>,
265    {
266        self.reaction = Some(value.into_iter().collect());
267        self
268    }
269}
270
271impl Method for SetMessageReaction {
272    type Response = bool;
273
274    fn into_payload(self) -> Payload {
275        Payload::json("setMessageReaction", self)
276    }
277}