1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use crate::message::commands::IRCMessageParseExt;
use crate::message::twitch::{Badge, RGBColor};
use crate::message::{IRCMessage, ServerMessageParseError};
use std::collections::HashSet;
use std::convert::TryFrom;

/// Sent once directly after successful login, containing properties for the logged in user.
///
/// This message is not sent if you log into chat as an anonymous user.
#[derive(Debug, Clone, PartialEq)]
pub struct GlobalUserStateMessage {
    /// ID of the logged in user
    pub user_id: String,
    /// Name (also called display name) of the logged in user
    pub user_name: String,
    /// Metadata related to the chat badges in the `badges` tag.
    ///
    /// Currently this is used only for `subscriber`, to indicate the exact number of months
    /// the user has been a subscriber. This number is finer grained than the version number in
    /// badges. For example, a user who has been a subscriber for 45 months would have a
    /// `badge_info` value of 45 but might have a `badges` `version` number for only 3 years.
    ///
    /// However note that subscriber badges are not sent on `GLOBALUSERSTATE` messages,
    /// so you can realistically expect this to be empty unless Twitch adds a new feature.
    pub badge_info: Vec<Badge>,
    /// List of badges the logged in user has in all channels.
    pub badges: Vec<Badge>,
    /// List of emote set IDs the logged in user has available. This always contains at least 0.
    pub emote_sets: HashSet<u64>,
    /// What name color the logged in user has chosen. The same color is used in all channels.
    pub name_color: Option<RGBColor>,

    /// The message that this `GlobalUserStateMessage` was parsed from.
    pub source: IRCMessage,
}

impl TryFrom<IRCMessage> for GlobalUserStateMessage {
    type Error = ServerMessageParseError;

    fn try_from(source: IRCMessage) -> Result<GlobalUserStateMessage, ServerMessageParseError> {
        if source.command != "GLOBALUSERSTATE" {
            return Err(ServerMessageParseError::MismatchedCommand(source));
        }

        // example:
        // @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

        Ok(GlobalUserStateMessage {
            user_id: source.try_get_nonempty_tag_value("user-id")?.to_owned(),
            user_name: source
                .try_get_nonempty_tag_value("display-name")?
                .to_owned(),
            badge_info: source.try_get_badges("badge-info")?,
            badges: source.try_get_badges("badges")?,
            emote_sets: source.try_get_emote_sets("emote-sets")?,
            name_color: source.try_get_color("color")?,
            source,
        })
    }
}

impl From<GlobalUserStateMessage> for IRCMessage {
    fn from(msg: GlobalUserStateMessage) -> IRCMessage {
        msg.source
    }
}

#[cfg(test)]
mod tests {
    use crate::message::twitch::{Badge, RGBColor};
    use crate::message::{GlobalUserStateMessage, IRCMessage};
    use std::collections::HashSet;
    use std::convert::TryFrom;
    use std::iter::FromIterator;

    #[test]
    pub fn test_basic() {
        let src = "@badge-info=;badges=;color=#19E6E6;display-name=randers;emote-sets=0,42,237;user-id=40286300;user-type= :tmi.twitch.tv GLOBALUSERSTATE";
        let irc_message = IRCMessage::parse(src).unwrap();
        let msg = GlobalUserStateMessage::try_from(irc_message.clone()).unwrap();

        assert_eq!(
            msg,
            GlobalUserStateMessage {
                user_id: "40286300".to_owned(),
                user_name: "randers".to_owned(),
                badge_info: vec![],
                badges: vec![],
                emote_sets: HashSet::from_iter(vec![0, 42, 237].into_iter()),
                name_color: Some(RGBColor {
                    r: 0x19,
                    g: 0xE6,
                    b: 0xE6
                }),
                source: irc_message
            }
        )
    }

    #[test]
    pub fn test_badges_no_color() {
        // according to twitch, emote-sets always has 0 in them. I don't trust them though,
        // so this tests that the "empty" case works too.
        let src = "@badge-info=;badges=premium/1;color=;display-name=randers;emote-sets=;user-id=40286300;user-type= :tmi.twitch.tv GLOBALUSERSTATE";
        let irc_message = IRCMessage::parse(src).unwrap();
        let msg = GlobalUserStateMessage::try_from(irc_message.clone()).unwrap();

        assert_eq!(
            msg,
            GlobalUserStateMessage {
                user_id: "40286300".to_owned(),
                user_name: "randers".to_owned(),
                badge_info: vec![],
                badges: vec![Badge {
                    name: "premium".to_owned(),
                    version: "1".to_owned()
                }],
                emote_sets: HashSet::new(),
                name_color: None,
                source: irc_message
            }
        )
    }

    #[test]
    pub fn test_plain_new_user() {
        // this is what a freshly registered user gets when logging in
        let src = "@badge-info=;badges=;color=;display-name=randers811;emote-sets=0;user-id=553170741;user-type= :tmi.twitch.tv GLOBALUSERSTATE";
        let irc_message = IRCMessage::parse(src).unwrap();
        let msg = GlobalUserStateMessage::try_from(irc_message.clone()).unwrap();

        assert_eq!(
            msg,
            GlobalUserStateMessage {
                user_id: "553170741".to_owned(),
                user_name: "randers811".to_owned(),
                badge_info: vec![],
                badges: vec![],
                emote_sets: HashSet::from_iter(vec![0]),
                name_color: None,
                source: irc_message
            }
        )
    }
}