Skip to main content

twitch_irc/message/commands/
clearchat.rs

1use crate::message::commands::IRCMessageParseExt;
2use crate::message::{IRCMessage, ServerMessageParseError};
3use chrono::{DateTime, Utc};
4use std::convert::TryFrom;
5use std::str::FromStr;
6use std::time::Duration;
7
8#[cfg(feature = "with-serde")]
9use {serde::Deserialize, serde::Serialize};
10
11/// Timeout, Permaban or when a chat is entirely cleared.
12///
13/// This represents the `CLEARCHAT` IRC command.
14#[derive(Debug, Clone, PartialEq, Eq)]
15#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
16pub struct ClearChatMessage {
17    /// Login name of the channel that this message was sent to
18    pub channel_login: String,
19    /// ID of the channel that this message was sent to
20    pub channel_id: String,
21    /// The action that this `CLEARCHAT` message encodes - one of Timeout, Permaban, and the
22    /// chat being cleared. See `ClearChatAction` for details
23    pub action: ClearChatAction,
24    /// The time when the Twitch IRC server created this message
25    pub server_timestamp: DateTime<Utc>,
26
27    /// The message that this `ClearChatMessage` was parsed from.
28    pub source: IRCMessage,
29}
30
31/// One of the three types of meaning a `CLEARCHAT` message can have.
32#[derive(Debug, Clone, PartialEq, Eq)]
33#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
34pub enum ClearChatAction {
35    /// A moderator cleared the entire chat.
36    ChatCleared,
37    /// A user was permanently banned.
38    UserBanned {
39        /// Login name of the user that was banned
40        user_login: String,
41        /// ID of the user that was banned
42        user_id: String,
43    },
44    /// A user was temporarily banned (timed out).
45    UserTimedOut {
46        /// Login name of the user that was banned
47        user_login: String,
48        /// ID of the user that was banned
49        user_id: String,
50        /// Duration that the user was timed out for.
51        timeout_length: Duration,
52    },
53}
54
55impl TryFrom<IRCMessage> for ClearChatMessage {
56    type Error = ServerMessageParseError;
57
58    fn try_from(source: IRCMessage) -> Result<ClearChatMessage, ServerMessageParseError> {
59        if source.command != "CLEARCHAT" {
60            return Err(ServerMessageParseError::MismatchedCommand(Box::new(source)));
61        }
62
63        // timeout example:
64        // @ban-duration=1;room-id=11148817;target-user-id=148973258;tmi-sent-ts=1594553828245 :tmi.twitch.tv CLEARCHAT #pajlada :fabzeef
65        // ban example:
66        // @room-id=11148817;target-user-id=70948394;tmi-sent-ts=1594561360331 :tmi.twitch.tv CLEARCHAT #pajlada :weeb123
67        // chat clear example:
68        // @room-id=40286300;tmi-sent-ts=1594561392337 :tmi.twitch.tv CLEARCHAT #randers
69
70        let action = match source.params.get(1) {
71            Some(user_login) => {
72                // ban or timeout
73                let user_id = source.try_get_nonempty_tag_value("target-user-id")?;
74
75                let ban_duration = source.try_get_optional_nonempty_tag_value("ban-duration")?;
76                match ban_duration {
77                    Some(ban_duration) => {
78                        let ban_duration = u64::from_str(ban_duration).map_err(|_| {
79                            ServerMessageParseError::MalformedTagValue(
80                                Box::new(source.clone()),
81                                "ban-duration",
82                                ban_duration.to_owned(),
83                            )
84                        })?;
85
86                        ClearChatAction::UserTimedOut {
87                            user_login: user_login.to_owned(),
88                            user_id: user_id.to_owned(),
89                            timeout_length: Duration::from_secs(ban_duration),
90                        }
91                    }
92                    None => ClearChatAction::UserBanned {
93                        user_login: user_login.to_owned(),
94                        user_id: user_id.to_owned(),
95                    },
96                }
97            }
98            None => ClearChatAction::ChatCleared,
99        };
100
101        Ok(ClearChatMessage {
102            channel_login: source.try_get_channel_login()?.to_owned(),
103            channel_id: source.try_get_nonempty_tag_value("room-id")?.to_owned(),
104            action,
105            server_timestamp: source.try_get_timestamp("tmi-sent-ts")?,
106            source,
107        })
108    }
109}
110
111impl From<ClearChatMessage> for IRCMessage {
112    fn from(msg: ClearChatMessage) -> IRCMessage {
113        msg.source
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use crate::message::commands::clearchat::ClearChatAction;
120    use crate::message::{ClearChatMessage, IRCMessage};
121    use chrono::{TimeZone, Utc};
122    use std::convert::TryFrom;
123    use std::time::Duration;
124
125    #[test]
126    pub fn test_timeout() {
127        let src = "@ban-duration=1;room-id=11148817;target-user-id=148973258;tmi-sent-ts=1594553828245 :tmi.twitch.tv CLEARCHAT #pajlada :fabzeef";
128        let irc_message = IRCMessage::parse(src).unwrap();
129        let msg = ClearChatMessage::try_from(irc_message.clone()).unwrap();
130
131        assert_eq!(
132            msg,
133            ClearChatMessage {
134                channel_login: "pajlada".to_owned(),
135                channel_id: "11148817".to_owned(),
136                action: ClearChatAction::UserTimedOut {
137                    user_login: "fabzeef".to_owned(),
138                    user_id: "148973258".to_owned(),
139                    timeout_length: Duration::from_secs(1)
140                },
141                server_timestamp: Utc.timestamp_millis_opt(1_594_553_828_245).unwrap(),
142                source: irc_message
143            }
144        );
145    }
146
147    #[test]
148    pub fn test_permaban() {
149        let src = "@room-id=11148817;target-user-id=70948394;tmi-sent-ts=1594561360331 :tmi.twitch.tv CLEARCHAT #pajlada :weeb123";
150        let irc_message = IRCMessage::parse(src).unwrap();
151        let msg = ClearChatMessage::try_from(irc_message.clone()).unwrap();
152
153        assert_eq!(
154            msg,
155            ClearChatMessage {
156                channel_login: "pajlada".to_owned(),
157                channel_id: "11148817".to_owned(),
158                action: ClearChatAction::UserBanned {
159                    user_login: "weeb123".to_owned(),
160                    user_id: "70948394".to_owned(),
161                },
162                server_timestamp: Utc.timestamp_millis_opt(1_594_561_360_331).unwrap(),
163                source: irc_message
164            }
165        );
166    }
167
168    #[test]
169    pub fn test_chat_clear() {
170        let src = "@room-id=40286300;tmi-sent-ts=1594561392337 :tmi.twitch.tv CLEARCHAT #randers";
171        let irc_message = IRCMessage::parse(src).unwrap();
172        let msg = ClearChatMessage::try_from(irc_message.clone()).unwrap();
173
174        assert_eq!(
175            msg,
176            ClearChatMessage {
177                channel_login: "randers".to_owned(),
178                channel_id: "40286300".to_owned(),
179                action: ClearChatAction::ChatCleared,
180                server_timestamp: Utc.timestamp_millis_opt(1_594_561_392_337).unwrap(),
181                source: irc_message
182            }
183        );
184    }
185}