Skip to main content

twitch_irc/message/commands/
whisper.rs

1use crate::message::commands::IRCMessageParseExt;
2use crate::message::twitch::{Badge, Emote, RGBColor, TwitchUserBasics};
3use crate::message::{IRCMessage, ServerMessageParseError};
4use std::convert::TryFrom;
5
6#[cfg(feature = "with-serde")]
7use {serde::Deserialize, serde::Serialize};
8/// A incoming whisper message (a private user-to-user message).
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
11pub struct WhisperMessage {
12    /// The login name of the receiving user (the logged in user).
13    pub recipient_login: String,
14    /// User details of the user that sent us this whisper (the sending user).
15    pub sender: TwitchUserBasics,
16    /// The text content of the message.
17    pub message_text: String,
18    /// Name color of the sending user.
19    pub name_color: Option<RGBColor>,
20    /// List of badges (that the sending user has) that should be displayed alongside the message.
21    pub badges: Vec<Badge>,
22    /// A list of emotes in this message. Each emote replaces a part of the `message_text`.
23    /// These emotes are sorted in the order that they appear in the message.
24    pub emotes: Vec<Emote>,
25
26    /// The message that this `WhisperMessage` was parsed from.
27    pub source: IRCMessage,
28}
29
30impl TryFrom<IRCMessage> for WhisperMessage {
31    type Error = ServerMessageParseError;
32
33    fn try_from(source: IRCMessage) -> Result<WhisperMessage, ServerMessageParseError> {
34        if source.command != "WHISPER" {
35            return Err(ServerMessageParseError::MismatchedCommand(Box::new(source)));
36        }
37
38        // example:
39        // @badges=;color=#19E6E6;display-name=randers;emotes=25:22-26;message-id=1;thread-id=40286300_553170741;turbo=0;user-id=40286300;user-type= :randers!randers@randers.tmi.twitch.tv WHISPER randers811 :hello, this is a test Kappa
40
41        let message_text = source.try_get_param(1)?.to_owned();
42        let emotes = source.try_get_emotes("emotes", &message_text)?;
43
44        Ok(WhisperMessage {
45            recipient_login: source.try_get_param(0)?.to_owned(),
46            sender: TwitchUserBasics {
47                id: source.try_get_nonempty_tag_value("user-id")?.to_owned(),
48                login: source.try_get_prefix_nickname()?.to_owned(),
49                name: source
50                    .try_get_nonempty_tag_value("display-name")?
51                    .to_owned(),
52            },
53            message_text,
54            name_color: source.try_get_color("color")?,
55            badges: source.try_get_badges("badges")?,
56            emotes,
57            source,
58        })
59    }
60}
61
62impl From<WhisperMessage> for IRCMessage {
63    fn from(msg: WhisperMessage) -> IRCMessage {
64        msg.source
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use crate::message::twitch::{Emote, RGBColor, TwitchUserBasics};
71    use crate::message::{IRCMessage, WhisperMessage};
72    use std::convert::TryFrom;
73    use std::ops::Range;
74
75    #[test]
76    pub fn test_basic() {
77        let src = "@badges=;color=#19E6E6;display-name=randers;emotes=25:22-26;message-id=1;thread-id=40286300_553170741;turbo=0;user-id=40286300;user-type= :randers!randers@randers.tmi.twitch.tv WHISPER randers811 :hello, this is a test Kappa";
78        let irc_message = IRCMessage::parse(src).unwrap();
79        let msg = WhisperMessage::try_from(irc_message.clone()).unwrap();
80
81        assert_eq!(
82            msg,
83            WhisperMessage {
84                recipient_login: "randers811".to_owned(),
85                sender: TwitchUserBasics {
86                    id: "40286300".to_owned(),
87                    login: "randers".to_owned(),
88                    name: "randers".to_owned()
89                },
90                message_text: "hello, this is a test Kappa".to_owned(),
91                name_color: Some(RGBColor {
92                    r: 0x19,
93                    g: 0xE6,
94                    b: 0xE6
95                }),
96                badges: vec![],
97                emotes: vec![Emote {
98                    id: "25".to_owned(),
99                    char_range: Range { start: 22, end: 27 },
100                    code: "Kappa".to_owned()
101                }],
102                source: irc_message
103            },
104        );
105    }
106
107    // note, I have tested and there is no support for \u0001ACTION <message>\u0001 style actions
108    // via whispers. (the control character gets filtered.) - so there is no special case to test
109}