-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #457 from twitch-rs/feat/small-eventsub
feat(eventsub): add remaining `channel.*` topics
- Loading branch information
Showing
31 changed files
with
3,575 additions
and
21 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#![doc(alias = "channel.chat_settings")] | ||
//! A broadcaster's chat settings are updated | ||
use super::{EventSubscription, EventType}; | ||
use crate::types; | ||
use serde_derive::{Deserialize, Serialize}; | ||
|
||
pub mod update; | ||
|
||
#[doc(inline)] | ||
pub use update::{ChannelChatSettingsUpdateV1, ChannelChatSettingsUpdateV1Payload}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
#![doc(alias = "channel.chat_settings.update")] | ||
//! a broadcaster’s chat settings are updated. | ||
use super::*; | ||
/// [`channel.chat_settings.update`](https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types/#channelchat_settingsupdate): a broadcaster’s chat settings are updated. | ||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | ||
#[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] | ||
#[cfg_attr(feature = "deny_unknown_fields", serde(deny_unknown_fields))] | ||
#[non_exhaustive] | ||
pub struct ChannelChatSettingsUpdateV1 { | ||
/// User ID of the channel to receive chat settings update events for. | ||
#[cfg_attr(feature = "typed-builder", builder(setter(into)))] | ||
pub broadcaster_user_id: types::UserId, | ||
/// The user ID to read chat as. | ||
#[cfg_attr(feature = "typed-builder", builder(setter(into)))] | ||
pub user_id: types::UserId, | ||
} | ||
|
||
impl ChannelChatSettingsUpdateV1 { | ||
/// Get notifications for updates on chat settings in this channel as a user | ||
pub fn new( | ||
broadcaster_user_id: impl Into<types::UserId>, | ||
user_id: impl Into<types::UserId>, | ||
) -> Self { | ||
Self { | ||
broadcaster_user_id: broadcaster_user_id.into(), | ||
user_id: user_id.into(), | ||
} | ||
} | ||
} | ||
|
||
impl EventSubscription for ChannelChatSettingsUpdateV1 { | ||
type Payload = ChannelChatSettingsUpdateV1Payload; | ||
|
||
const EVENT_TYPE: EventType = EventType::ChannelChatSettingsUpdate; | ||
#[cfg(feature = "twitch_oauth2")] | ||
const SCOPE: twitch_oauth2::Validator = | ||
twitch_oauth2::validator![twitch_oauth2::Scope::UserReadChat]; | ||
const VERSION: &'static str = "1"; | ||
} | ||
|
||
/// [`channel.chat_settings.update`](ChannelChatSettingsUpdateV1) response payload. | ||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | ||
#[cfg_attr(feature = "deny_unknown_fields", serde(deny_unknown_fields))] | ||
#[non_exhaustive] | ||
pub struct ChannelChatSettingsUpdateV1Payload { | ||
/// The ID of the broadcaster specified in the request. | ||
pub broadcaster_user_id: types::UserId, | ||
/// The login of the broadcaster specified in the request. | ||
pub broadcaster_user_login: types::UserName, | ||
/// The user name of the broadcaster specified in the request. | ||
pub broadcaster_user_name: types::DisplayName, | ||
|
||
/// A Boolean value that determines whether chat messages must contain only emotes. | ||
/// | ||
/// True if only messages that are 100% emotes are allowed; otherwise false. | ||
pub emote_mode: bool, | ||
/// A Boolean value that determines whether the broadcaster restricts the chat room to followers only, based on how long they’ve followed. | ||
/// | ||
/// True if the broadcaster restricts the chat room to followers only; otherwise false. | ||
/// | ||
/// See [follower_mode_duration_minutes][Self::follower_mode_duration_minutes] for how long the followers must have followed the broadcaster to participate in the chat room. | ||
pub follower_mode: bool, | ||
/// The length of time, in minutes, that the followers must have followed the broadcaster to participate in the chat room. See [follower_mode][Self::follower_mode]. | ||
/// | ||
/// [None] if [follower_mode][Self::follower_mode] is false. | ||
pub follower_mode_duration_minutes: Option<usize>, | ||
/// A Boolean value that determines whether the broadcaster limits how often users in the chat room are allowed to send messages. | ||
/// | ||
/// Is true, if the broadcaster applies a delay; otherwise, false. | ||
/// | ||
/// See [slow_mode_wait_time_seconds][Self::slow_mode_wait_time_seconds] for the delay. | ||
pub slow_mode: bool, | ||
/// The amount of time, in seconds, that users need to wait between sending messages. See [slow_mode][Self::slow_mode]. | ||
/// | ||
/// [None] if [slow_mode][Self::slow_mode] is false. | ||
pub slow_mode_wait_time_seconds: Option<usize>, | ||
/// A Boolean value that determines whether only users that subscribe to the broadcaster’s channel can talk in the chat room. | ||
/// | ||
/// True if the broadcaster restricts the chat room to subscribers only; otherwise false. | ||
pub subscriber_mode: bool, | ||
/// A Boolean value that determines whether the broadcaster requires users to post only unique messages in the chat room. | ||
/// | ||
/// True if the broadcaster requires unique messages only; otherwise false. | ||
pub unique_chat_mode: bool, | ||
} | ||
|
||
#[cfg(test)] | ||
#[test] | ||
fn parse_payload() { | ||
use crate::eventsub::{Event, Message}; | ||
|
||
let payload = r##" | ||
{ | ||
"subscription": { | ||
"id": "f1c2a387-161a-49f9-a165-0f21d7a4e1c4", | ||
"type": "channel.chat_settings.update", | ||
"version": "1", | ||
"status": "enabled", | ||
"cost": 0, | ||
"condition": { | ||
"broadcaster_user_id": "1337", | ||
"user_id": "9001" | ||
}, | ||
"transport": { | ||
"method": "webhook", | ||
"callback": "https://example.com/webhooks/callback" | ||
}, | ||
"created_at": "2023-04-11T10:11:12.123Z" | ||
}, | ||
"event": { | ||
"broadcaster_user_id": "1337", | ||
"broadcaster_user_login": "cool_user", | ||
"broadcaster_user_name": "Cool_User", | ||
"emote_mode": true, | ||
"follower_mode": false, | ||
"follower_mode_duration_minutes": null, | ||
"slow_mode": true, | ||
"slow_mode_wait_time_seconds": 10, | ||
"subscriber_mode": false, | ||
"unique_chat_mode": false | ||
} | ||
} | ||
"##; | ||
|
||
let val = Event::parse(payload).unwrap(); | ||
crate::tests::roundtrip(&val); | ||
|
||
let Event::ChannelChatSettingsUpdateV1(val) = val else { | ||
panic!("invalid event type"); | ||
}; | ||
let Message::Notification(notif) = val.message else { | ||
panic!("invalid settings type"); | ||
}; | ||
|
||
assert_eq!(notif.broadcaster_user_id.as_str(), "1337"); | ||
assert!(notif.emote_mode); | ||
assert!(!notif.follower_mode); | ||
assert!(notif.follower_mode_duration_minutes.is_none()); | ||
assert!(notif.slow_mode); | ||
assert_eq!(notif.slow_mode_wait_time_seconds, Some(10)); | ||
assert!(!notif.subscriber_mode); | ||
assert!(!notif.unique_chat_mode); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
#![doc(alias = "channel.guest_star_guest")] | ||
//! Events regarding guests of guest star sessions | ||
use super::{EventSubscription, EventType}; | ||
use crate::types; | ||
use serde_derive::{Deserialize, Serialize}; | ||
|
||
pub mod update; | ||
|
||
#[doc(inline)] | ||
pub use update::{ChannelGuestStarGuestUpdateBeta, ChannelGuestStarGuestUpdateBetaPayload}; | ||
|
||
/// The current state of a user in a guest star session | ||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | ||
#[non_exhaustive] | ||
#[serde(rename_all = "snake_case")] | ||
pub enum GuestState { | ||
/// The guest has transitioned to the invite queue. | ||
/// | ||
/// This can take place when the guest was previously assigned a slot, but have been removed from the call and are sent back to the invite queue. | ||
Invited, | ||
/// The guest has accepted the invite and is currently in the process of setting up to join the session. | ||
Accepted, | ||
/// The guest has signaled they are ready and can be assigned a slot. | ||
Ready, | ||
/// The guest has been assigned a slot in the session, but is not currently seen live in the broadcasting software. | ||
Backstage, | ||
/// The guest is now live in the host's broadcasting software. | ||
Live, | ||
/// The guest was removed from the call or queue. | ||
Removed, | ||
/// An unknown state, contains the raw string provided by Twitch. | ||
#[serde(untagged)] | ||
Unknown(String), | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
#![doc(alias = "channel.guest_star_guest.update")] | ||
//! the host preferences for Guest Star have been updated. | ||
use super::*; | ||
/// [`channel.guest_star_guest.update`](https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types/#channelguest_star_guestupdate): the host preferences for Guest Star have been updated. | ||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | ||
#[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] | ||
#[cfg_attr(feature = "deny_unknown_fields", serde(deny_unknown_fields))] | ||
#[non_exhaustive] | ||
pub struct ChannelGuestStarGuestUpdateBeta { | ||
/// The broadcaster user ID for the channel you want to receive Guest Star guest update notifications for. | ||
#[cfg_attr(feature = "typed-builder", builder(setter(into)))] | ||
pub broadcaster_user_id: types::UserId, | ||
/// The user ID of the moderator or broadcaster of the specified channel. | ||
#[cfg_attr(feature = "typed-builder", builder(setter(into)))] | ||
pub moderator_user_id: types::UserId, | ||
} | ||
|
||
impl ChannelGuestStarGuestUpdateBeta { | ||
/// Get notifications for guest star sessions in this channel as a moderator | ||
pub fn new( | ||
broadcaster_user_id: impl Into<types::UserId>, | ||
moderator_user_id: impl Into<types::UserId>, | ||
) -> Self { | ||
Self { | ||
broadcaster_user_id: broadcaster_user_id.into(), | ||
moderator_user_id: moderator_user_id.into(), | ||
} | ||
} | ||
} | ||
|
||
impl EventSubscription for ChannelGuestStarGuestUpdateBeta { | ||
type Payload = ChannelGuestStarGuestUpdateBetaPayload; | ||
|
||
const EVENT_TYPE: EventType = EventType::ChannelGuestStarGuestUpdate; | ||
#[cfg(feature = "twitch_oauth2")] | ||
const SCOPE: twitch_oauth2::Validator = twitch_oauth2::validator![any( | ||
twitch_oauth2::Scope::ChannelReadGuestStar, | ||
twitch_oauth2::Scope::ChannelManageGuestStar, | ||
twitch_oauth2::Scope::ModeratorReadGuestStar, | ||
twitch_oauth2::Scope::ModeratorManageGuestStar, | ||
)]; | ||
const VERSION: &'static str = "beta"; | ||
} | ||
|
||
/// [`channel.guest_star_guest.update`](ChannelGuestStarGuestUpdateBeta) response payload. | ||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | ||
#[cfg_attr(feature = "deny_unknown_fields", serde(deny_unknown_fields))] | ||
#[non_exhaustive] | ||
pub struct ChannelGuestStarGuestUpdateBetaPayload { | ||
/// The non-host broadcaster user ID. | ||
pub broadcaster_user_id: types::UserId, | ||
/// The non-host broadcaster display name. | ||
pub broadcaster_user_login: types::UserName, | ||
/// The non-host broadcaster login. | ||
pub broadcaster_user_name: types::DisplayName, | ||
|
||
/// ID representing the unique session that was started. | ||
pub session_id: types::GuestStarSessionId, | ||
|
||
/// The user ID of the moderator who updated the guest’s state (could be the host). | ||
/// | ||
/// [None] if the update was performed by the guest. | ||
pub moderator_user_id: Option<types::UserId>, | ||
/// The moderator display name. | ||
/// | ||
/// [None] if the update was performed by the guest. | ||
pub moderator_user_login: Option<types::UserName>, | ||
/// The moderator login. | ||
/// | ||
/// [None] if the update was performed by the guest. | ||
pub moderator_user_name: Option<types::DisplayName>, | ||
|
||
/// The user ID of the guest who transitioned states in the session. | ||
/// | ||
/// [None] if the slot is now empty. | ||
pub guest_user_id: Option<types::UserId>, | ||
/// The guest display name. | ||
/// | ||
/// [None] if the slot is now empty. | ||
pub guest_user_login: Option<types::UserName>, | ||
/// The guest login. | ||
/// | ||
/// [None] if the slot is now empty. | ||
pub guest_user_name: Option<types::DisplayName>, | ||
|
||
/// The ID of the slot assignment the guest is assigned to. | ||
/// | ||
/// [None] if the guest is in the [Invited][GuestState::Invited], [Removed][GuestState::Removed], [Ready][GuestState::Ready], or [Accepted][GuestState::Accepted] state. | ||
pub slot_id: Option<types::GuestStarSlotId>, | ||
/// The current state of the user after the update has taken place. | ||
/// | ||
/// [None] if the slot is now empty. | ||
pub state: Option<GuestState>, | ||
|
||
/// User ID of the host channel. | ||
pub host_user_id: types::UserId, | ||
/// The host display name. | ||
pub host_user_login: types::UserName, | ||
/// The host login. | ||
pub host_user_name: types::DisplayName, | ||
|
||
/// Flag that signals whether the host is allowing the slot’s video to be seen by participants within the session. | ||
/// | ||
/// [None] if the guest is not slotted. | ||
pub host_video_enabled: Option<bool>, | ||
/// Flag that signals whether the host is allowing the slot’s audio to be heard by participants within the session. | ||
/// | ||
/// [None] if the guest is not slotted. | ||
pub host_audio_enabled: Option<bool>, | ||
/// Value between 0-100 that represents the slot’s audio level as heard by participants within the session. | ||
/// | ||
/// [None] if the guest is not slotted. | ||
pub host_volume: Option<u8>, | ||
} | ||
|
||
#[cfg(test)] | ||
#[test] | ||
fn parse_payload() { | ||
use crate::eventsub::{Event, Message}; | ||
|
||
let payload = r##" | ||
{ | ||
"subscription": { | ||
"id": "f1c2a387-161a-49f9-a165-0f21d7a4e1c4", | ||
"type": "channel.guest_star_guest.update", | ||
"version": "beta", | ||
"status": "enabled", | ||
"cost": 0, | ||
"condition": { | ||
"broadcaster_user_id": "1337", | ||
"moderator_user_id": "1312" | ||
}, | ||
"transport": { | ||
"method": "webhook", | ||
"callback": "https://example.com/webhooks/callback" | ||
}, | ||
"created_at": "2023-04-11T10:11:32.123Z" | ||
}, | ||
"event": { | ||
"broadcaster_user_id": "1337", | ||
"broadcaster_user_name": "Cool_User", | ||
"broadcaster_user_login": "cool_user", | ||
"session_id": "2KFRQbFtpmfyD3IevNRnCzOPRJI", | ||
"moderator_user_id": "1312", | ||
"moderator_user_name": "Cool_Mod", | ||
"moderator_user_login": "cool_mod", | ||
"guest_user_id": "1234", | ||
"guest_user_name": "Cool_Guest", | ||
"guest_user_login": "cool_guest", | ||
"slot_id": "1", | ||
"state": "live", | ||
"host_user_id": "4242", | ||
"host_user_name": "A_host", | ||
"host_user_login": "a_host", | ||
"host_video_enabled": true, | ||
"host_audio_enabled": true, | ||
"host_volume": 100 | ||
} | ||
} | ||
"##; | ||
|
||
let val = Event::parse(payload).unwrap(); | ||
crate::tests::roundtrip(&val); | ||
|
||
let Event::ChannelGuestStarGuestUpdateBeta(val) = val else { | ||
panic!("invalid event type"); | ||
}; | ||
let Message::Notification(notif) = val.message else { | ||
panic!("invalid guest type"); | ||
}; | ||
|
||
assert_eq!(notif.broadcaster_user_id.as_str(), "1337"); | ||
assert_eq!(notif.moderator_user_id.unwrap().as_str(), "1312"); | ||
assert_eq!(notif.session_id.as_str(), "2KFRQbFtpmfyD3IevNRnCzOPRJI"); | ||
assert_eq!(notif.guest_user_id.unwrap().as_str(), "1234"); | ||
assert_eq!(notif.host_user_id.as_str(), "4242"); | ||
assert_eq!(notif.slot_id.unwrap().as_str(), "1"); | ||
assert_eq!(notif.state, Some(GuestState::Live)); | ||
assert_eq!(notif.host_video_enabled, Some(true)); | ||
assert_eq!(notif.host_audio_enabled, Some(true)); | ||
assert_eq!(notif.host_volume, Some(100)); | ||
} |
Oops, something went wrong.