Skip to main content

twitch_irc/message/commands/
globaluserstate.rs

1use crate::message::commands::IRCMessageParseExt;
2use crate::message::twitch::{Badge, RGBColor};
3use crate::message::{IRCMessage, ServerMessageParseError};
4use std::collections::HashSet;
5use std::convert::TryFrom;
6
7#[cfg(feature = "with-serde")]
8use {serde::Deserialize, serde::Serialize};
9
10/// Sent once directly after successful login, containing properties for the logged in user.
11///
12/// This message is not sent if you log into chat as an anonymous user.
13#[derive(Debug, Clone, PartialEq, Eq)]
14#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
15pub struct GlobalUserStateMessage {
16    /// ID of the logged in user
17    pub user_id: String,
18    /// Name (also called display name) of the logged in user
19    pub user_name: String,
20    /// Metadata related to the chat badges in the `badges` tag.
21    ///
22    /// Currently this is used only for `subscriber`, to indicate the exact number of months
23    /// the user has been a subscriber. This number is finer grained than the version number in
24    /// badges. For example, a user who has been a subscriber for 45 months would have a
25    /// `badge_info` value of 45 but might have a `badges` `version` number for only 3 years.
26    ///
27    /// However note that subscriber badges are not sent on `GLOBALUSERSTATE` messages,
28    /// so you can realistically expect this to be empty unless Twitch adds a new feature.
29    pub badge_info: Vec<Badge>,
30    /// List of badges the logged in user has in all channels.
31    pub badges: Vec<Badge>,
32    /// List of emote set IDs the logged in user has available. This always contains at least one entry ("0").
33    pub emote_sets: HashSet<String>,
34    /// What name color the logged in user has chosen. The same color is used in all channels.
35    pub name_color: Option<RGBColor>,
36
37    /// The message that this `GlobalUserStateMessage` was parsed from.
38    pub source: IRCMessage,
39}
40
41impl TryFrom<IRCMessage> for GlobalUserStateMessage {
42    type Error = ServerMessageParseError;
43
44    fn try_from(source: IRCMessage) -> Result<GlobalUserStateMessage, ServerMessageParseError> {
45        if source.command != "GLOBALUSERSTATE" {
46            return Err(ServerMessageParseError::MismatchedCommand(Box::new(source)));
47        }
48
49        // example:
50        // @badge-info=;badges=;color=#19E6E6;display-name=randers;emote-sets=0,42,237,4236,15961,19194,771823,1511293,1641460,1641461,1641462,300206295,300374282,300432482,300548756,472873131,477339272,488737509,537206155,625908879;user-id=40286300;user-type= :tmi.twitch.tv GLOBALUSERSTATE
51
52        Ok(GlobalUserStateMessage {
53            user_id: source.try_get_nonempty_tag_value("user-id")?.to_owned(),
54            user_name: source
55                .try_get_nonempty_tag_value("display-name")?
56                .to_owned(),
57            badge_info: source.try_get_badges("badge-info")?,
58            badges: source.try_get_badges("badges")?,
59            emote_sets: source.try_get_emote_sets("emote-sets")?,
60            name_color: source.try_get_color("color")?,
61            source,
62        })
63    }
64}
65
66impl From<GlobalUserStateMessage> for IRCMessage {
67    fn from(msg: GlobalUserStateMessage) -> IRCMessage {
68        msg.source
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use crate::message::twitch::{Badge, RGBColor};
75    use crate::message::{GlobalUserStateMessage, IRCMessage};
76    use std::collections::HashSet;
77    use std::convert::TryFrom;
78    use std::iter::FromIterator;
79
80    #[test]
81    pub fn test_basic() {
82        let src = "@badge-info=;badges=;color=#19E6E6;display-name=randers;emote-sets=0,42,237;user-id=40286300;user-type= :tmi.twitch.tv GLOBALUSERSTATE";
83        let irc_message = IRCMessage::parse(src).unwrap();
84        let msg = GlobalUserStateMessage::try_from(irc_message.clone()).unwrap();
85
86        assert_eq!(
87            msg,
88            GlobalUserStateMessage {
89                user_id: "40286300".to_owned(),
90                user_name: "randers".to_owned(),
91                badge_info: vec![],
92                badges: vec![],
93                emote_sets: vec!["0", "42", "237"]
94                    .into_iter()
95                    .map(|s| s.to_owned())
96                    .collect(),
97                name_color: Some(RGBColor {
98                    r: 0x19,
99                    g: 0xE6,
100                    b: 0xE6
101                }),
102                source: irc_message
103            }
104        );
105    }
106
107    #[test]
108    pub fn test_badges_no_color() {
109        // according to twitch, emote-sets always has 0 in them. I don't trust them though,
110        // so this tests that the "empty" case works too.
111        let src = "@badge-info=;badges=premium/1;color=;display-name=randers;emote-sets=;user-id=40286300;user-type= :tmi.twitch.tv GLOBALUSERSTATE";
112        let irc_message = IRCMessage::parse(src).unwrap();
113        let msg = GlobalUserStateMessage::try_from(irc_message.clone()).unwrap();
114
115        assert_eq!(
116            msg,
117            GlobalUserStateMessage {
118                user_id: "40286300".to_owned(),
119                user_name: "randers".to_owned(),
120                badge_info: vec![],
121                badges: vec![Badge {
122                    name: "premium".to_owned(),
123                    version: "1".to_owned()
124                }],
125                emote_sets: HashSet::new(),
126                name_color: None,
127                source: irc_message
128            }
129        );
130    }
131
132    #[test]
133    pub fn test_plain_new_user() {
134        // this is what a freshly registered user gets when logging in
135        let src = "@badge-info=;badges=;color=;display-name=randers811;emote-sets=0;user-id=553170741;user-type= :tmi.twitch.tv GLOBALUSERSTATE";
136        let irc_message = IRCMessage::parse(src).unwrap();
137        let msg = GlobalUserStateMessage::try_from(irc_message.clone()).unwrap();
138
139        assert_eq!(
140            msg,
141            GlobalUserStateMessage {
142                user_id: "553170741".to_owned(),
143                user_name: "randers811".to_owned(),
144                badge_info: vec![],
145                badges: vec![],
146                emote_sets: HashSet::from_iter(vec!["0".to_owned()]),
147                name_color: None,
148                source: irc_message
149            }
150        );
151    }
152}