twitch_api/pubsub/
raid.rs

1#![doc(alias = "raids")]
2//! PubSub messages for raids
3use crate::{pubsub, types};
4use serde_derive::{Deserialize, Serialize};
5
6/// A user raids the channel
7#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
8#[serde(into = "String", try_from = "String")]
9pub struct Raid {
10    /// The channel_id to watch. Can be fetched with the [Get Users](crate::helix::users::get_users) endpoint
11    pub channel_id: u32,
12}
13
14impl_de_ser!(Raid, "raid", channel_id);
15
16impl pubsub::Topic for Raid {
17    #[cfg(feature = "twitch_oauth2")]
18    const SCOPE: twitch_oauth2::Validator = twitch_oauth2::validator![];
19
20    fn into_topic(self) -> pubsub::Topics { super::Topics::Raid(self) }
21}
22
23/// Raid go
24#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
25#[cfg_attr(feature = "deny_unknown_fields", serde(deny_unknown_fields))]
26#[non_exhaustive]
27pub struct RaidGoV2 {
28    /// ID of user would be raided
29    pub creator_id: types::UserId,
30    /// Raid will be force started in this many seconds
31    pub force_raid_now_seconds: i64,
32    /// ID of raid
33    pub id: String,
34    /// ID of broadcaster doing raid
35    pub source_id: types::UserId,
36    /// Display name of targeted broadcaster/user
37    pub target_display_name: types::DisplayName,
38    /// ID of targeted broadcaster/user
39    pub target_id: types::UserId,
40    /// Username of targeted broadcaster/user
41    pub target_login: types::UserName,
42    /// Profile picture of targeted broadcaster/user
43    pub target_profile_image: String,
44    /// Jitter amount
45    pub transition_jitter_seconds: i64,
46    /// Amount of viewers that will join raid
47    pub viewer_count: i64,
48}
49
50/// Raid update
51#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
52#[cfg_attr(feature = "deny_unknown_fields", serde(deny_unknown_fields))]
53#[non_exhaustive]
54pub struct RaidUpdateV2 {
55    /// ID of user would be raided
56    pub creator_id: types::UserId,
57    /// Raid will be force started in this many seconds
58    pub force_raid_now_seconds: i64,
59    /// ID of raid
60    pub id: String,
61    /// ID of broadcaster doing raid
62    pub source_id: types::UserId,
63    /// Display name of targeted broadcaster/user
64    pub target_display_name: types::DisplayName,
65    /// ID of targeted broadcaster/user
66    pub target_id: types::UserId,
67    /// Username of targeted broadcaster/user
68    pub target_login: types::UserName,
69    /// Profile picture of targeted broadcaster/user
70    pub target_profile_image: String,
71    /// Jitter amount
72    pub transition_jitter_seconds: i64,
73    /// Amount of viewers that will join raid
74    pub viewer_count: i64,
75}
76
77/// Raid canceled
78#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
79#[cfg_attr(feature = "deny_unknown_fields", serde(deny_unknown_fields))]
80#[non_exhaustive]
81pub struct RaidCancelV2 {
82    /// ID of user would be raided
83    pub creator_id: types::UserId,
84    /// Raid would have be force started in this many seconds
85    pub force_raid_now_seconds: i64,
86    /// ID of raid
87    pub id: String,
88    /// ID of broadcaster doing raid
89    pub source_id: types::UserId,
90    /// Display name of targeted broadcaster/user
91    pub target_display_name: types::DisplayName,
92    /// ID of targeted broadcaster/user
93    pub target_id: types::UserId,
94    /// Username of targeted broadcaster/user
95    pub target_login: types::UserName,
96    /// Profile picture of targeted broadcaster/user
97    pub target_profile_image: String,
98    /// Jitter amount
99    pub transition_jitter_seconds: i64,
100    /// Amount of viewers that would join raid
101    pub viewer_count: i64,
102}
103
104/// Reply from [Raid]
105#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
106#[serde(tag = "type", content = "raid")]
107#[cfg_attr(feature = "deny_unknown_fields", serde(deny_unknown_fields))]
108#[non_exhaustive]
109pub enum RaidReply {
110    /// Raid go
111    #[serde(rename = "raid_go_v2")]
112    RaidGoV2(RaidGoV2),
113    /// Raid update
114    #[serde(rename = "raid_update_v2")]
115    RaidUpdateV2(RaidUpdateV2),
116    /// Raid canceled
117    #[serde(rename = "raid_cancel_v2")]
118    RaidCancelV2(RaidCancelV2),
119}
120
121#[cfg(test)]
122mod tests {
123    use super::super::{Response, TopicData};
124    use super::*;
125
126    #[test]
127    fn raid() {
128        let message = r##"
129{
130    "type": "raid_go_v2",
131    "raid": {
132        "id": "7fbea6d0-1337-4c61-8c92-b7510e639010",
133        "creator_id": "27620241",
134        "source_id": "27620241",
135        "target_id": "1234",
136        "target_login": "tmi",
137        "target_display_name": "TMI",
138        "target_profile_image": "https://static-cdn.jtvnw.net/jtv_user_pictures/tmi-profile_image-deadbeef1234-70x70.jpeg",
139        "transition_jitter_seconds": 5,
140        "force_raid_now_seconds": 90,
141        "viewer_count": 5238
142    }
143}
144"##;
145
146        let source = format!(
147            r#"{{"type": "MESSAGE", "data": {{ "topic": "raid.27620241", "message": {message:?} }}}}"#
148        );
149        let actual = dbg!(Response::parse(&source).unwrap());
150        assert!(matches!(
151            actual,
152            Response::Message {
153                data: TopicData::Raid { .. },
154            }
155        ));
156    }
157
158    #[test]
159    fn raid_update() {
160        let message = r##"
161{
162    "type": "raid_update_v2",
163    "raid": {
164        "id": "7fbea6d0-4087-48e7-a395-c0ecd15ef551",
165        "creator_id": "123455",
166        "source_id": "123455",
167        "target_id": "123",
168        "target_login": "tmi",
169        "target_display_name": "tmi",
170        "target_profile_image": "https://static-cdn.jtvnw.net/jtv_user_pictures/deadbeef-profile_image-70x70.png",
171        "transition_jitter_seconds": 0,
172        "force_raid_now_seconds": 90,
173        "viewer_count": 143
174    }
175}
176"##;
177
178        let source = format!(
179            r#"{{"type": "MESSAGE", "data": {{ "topic": "raid.27620241", "message": {message:?} }}}}"#
180        );
181        let actual = dbg!(Response::parse(&source).unwrap());
182        assert!(matches!(
183            actual,
184            Response::Message {
185                data: TopicData::Raid { .. },
186            }
187        ));
188    }
189
190    #[test]
191    fn raid_cancel() {
192        let message = r##"
193{
194    "type": "raid_cancel_v2",
195    "raid": {
196        "id": "7fbea6d0-5b4b-4b8f-a79d-c9e94bf88bbf",
197        "creator_id": "123455",
198        "source_id": "123455",
199        "target_id": "123",
200        "target_login": "tmi",
201        "target_display_name": "TMI",
202        "target_profile_image": "https://static-cdn.jtvnw.net/jtv_user_pictures/deadbeef-profile_image-70x70.png",
203        "transition_jitter_seconds": 4,
204        "force_raid_now_seconds": 90,
205        "viewer_count": 4463
206    }
207}
208"##;
209
210        let source = format!(
211            r#"{{"type": "MESSAGE", "data": {{ "topic": "raid.27620241", "message": {message:?} }}}}"#
212        );
213        let actual = dbg!(Response::parse(&source).unwrap());
214        assert!(matches!(
215            actual,
216            Response::Message {
217                data: TopicData::Raid { .. },
218            }
219        ));
220    }
221
222    #[test]
223    fn check_deser() {
224        use std::convert::TryInto as _;
225        let s = "raid.1234";
226        assert_eq!(Raid { channel_id: 1234 }, s.to_string().try_into().unwrap());
227    }
228
229    #[test]
230    fn check_ser() {
231        let s = "raid.1234";
232        let right: String = Raid { channel_id: 1234 }.into();
233        assert_eq!(s.to_string(), right);
234    }
235}