Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add missing endpoints #394

Merged
merged 13 commits into from
Feb 25, 2024
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
toolchain: ${{ matrix.rust || env.MSRV }}
- uses: Swatinem/rust-cache@v2
- name: Test twitch_api
run: cargo test --locked --all-targets --features "${{ env.CI_TWITCH_API_FEATURES }}" ${{matrix.rust == 'nightly' && '--workspace'}}
run: cargo test --locked --all-targets --features "${{ env.CI_TWITCH_API_FEATURES }}" ${{matrix.rust == 'nightly' && '--workspace' || ''}}
rustfmt:
name: Rustfmt
runs-on: ubuntu-latest
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@
- Added `helix::Response::new` and `helix::Response::with_data` to make it possible to create your own responses.
- Added `is_branded_content` and `content_classification_labels` to `Get Channel Information` and `Modify Channel information`
- Added `channel.update` v2 EventSub event
- Added `is_featured` to Get Clips
- Added beta `Get Ad Schedule` and `Snooze Next Ad` endpoint
- Added beta `channel.ad_break.begin` eventsub event

### Fixed

Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ trace_unknown_fields = ["dep:serde_ignored", "tracing"]

serde_json = ["dep:serde_json", "dep:serde_path_to_error"]
helix = [
"twitch_types/color",
"twitch_types/color",
"twitch_types/emote",
"twitch_types/goal",
Expand Down Expand Up @@ -126,6 +125,8 @@ eventsub = [
"twitch_types/points",
"twitch_types/stream",
"twitch_types/timestamp",
"twitch_types/sub",
"twitch_types/color",
]

hmac = ["dep:crypto_hmac", "dep:sha2"]
Expand Down
101 changes: 101 additions & 0 deletions src/eventsub/channel/ad_break/begin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#![doc(alias = "channel.ad_break.begin")]
//! a user runs a midroll commercial break, either manually or automatically via ads manager.

use super::*;
/// [`channel.ad_break.begin`](https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types#channelad_breakbegin): a user runs a midroll commercial break, either manually or automatically via ads manager.
#[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 ChannelAdBreakBeginV1 {
/// The ID of the broadcaster that you want to get Channel Ad Break begin notifications for.
#[cfg_attr(feature = "typed-builder", builder(setter(into)))]
pub broadcaster_user_id: types::UserId,
}

impl ChannelAdBreakBeginV1 {
/// The ID of the broadcaster that you want to get Channel Ad Break begin notifications for.
pub fn broadcaster_user_id(broadcaster_user_id: impl Into<types::UserId>) -> Self {
Self {
broadcaster_user_id: broadcaster_user_id.into(),
}
}
}

impl EventSubscription for ChannelAdBreakBeginV1 {
type Payload = ChannelAdBreakBeginV1Payload;

const EVENT_TYPE: EventType = EventType::ChannelAdBreakBegin;
#[cfg(feature = "twitch_oauth2")]
const SCOPE: twitch_oauth2::Validator = twitch_oauth2::validator![any(
twitch_oauth2::Scope::ChannelReadAds,
twitch_oauth2::Scope::ChannelManageAds
)];
const VERSION: &'static str = "1";
}

/// [`channel.ad_break.begin`](ChannelAdBreakBeginV1) response payload.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "deny_unknown_fields", serde(deny_unknown_fields))]
#[non_exhaustive]
pub struct ChannelAdBreakBeginV1Payload {
/// Length in seconds of the mid-roll ad break requested
pub duration_seconds: i32,
#[serde(alias = "timestamp")]
/// The UTC timestamp of when the ad break began, in RFC3339 format. Note that there is potential delay between this event, when the streamer requested the ad break, and when the viewers will see ads.
pub started_at: types::Timestamp,
/// Indicates if the ad was automatically scheduled via Ads Manager
pub is_automatic: bool,
/// The broadcaster’s user ID for the channel the ad was run on.
pub broadcaster_user_id: types::UserId,
/// The broadcaster’s user login for the channel the ad was run on.
pub broadcaster_user_login: types::UserName,
/// The broadcaster’s user display name for the channel the ad was run on.
pub broadcaster_user_name: types::DisplayName,
/// The ID of the user that requested the ad. For automatic ads, this will be the ID of the broadcaster.
pub requester_user_id: types::UserId,
/// The login of the user that requested the ad.
pub requester_user_login: types::UserName,
/// The display name of the user that requested the ad.
pub requester_user_name: types::DisplayName,
}

#[cfg(test)]
#[test]
fn parse_payload() {
// FIXME: twitch docs has trailing commas
// FIXME: it uses string for the integer and bool, https://github.com/twitchdev/issues/issues/857#issuecomment-1793796590
let payload = r##"
{
"subscription": {
"id": "f1c2a387-161a-49f9-a165-0f21d7a4e1c4",
"type": "channel.ad_break.begin",
"version": "1",
"status": "enabled",
"cost": 0,
"condition": {
"broadcaster_user_id": "1337"
},
"transport": {
"method": "webhook",
"callback": "https://example.com/webhooks/callback"
},
"created_at": "2019-11-16T10:11:12.634234626Z"
},
"event": {
"duration_seconds": 60,
"started_at": "2019-11-16T10:11:12.634234626Z",
"is_automatic": false,
"broadcaster_user_id": "1337",
"broadcaster_user_login": "cool_user",
"broadcaster_user_name": "Cool_User",
"requester_user_id": "1337",
"requester_user_login": "cool_user",
"requester_user_name": "Cool_User"
}
}
"##;

let val = dbg!(crate::eventsub::Event::parse(payload).unwrap());
crate::tests::roundtrip(&val)
}
10 changes: 10 additions & 0 deletions src/eventsub/channel/ad_break/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![doc(alias = "channel.ad_break")]
//! Ad break on channel has begun
use super::{EventSubscription, EventType};
use crate::types;
use serde_derive::{Deserialize, Serialize};

pub mod begin;

#[doc(inline)]
pub use begin::{ChannelAdBreakBeginV1, ChannelAdBreakBeginV1Payload};
86 changes: 86 additions & 0 deletions src/eventsub/channel/chat/clear.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#![doc(alias = "channel.chat.clear")]
//! A moderator or bot clears all messages from the chat room.

use super::*;
/// [`channel.chat.clear`](https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types#channelchatclear): a moderator or bot clears all messages from the chat room.
#[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 ChannelChatClearV1 {
/// User ID of the channel to receive chat clear user messages 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 ChannelChatClearV1 {
/// Get chat clear on broadcasters channel reading chat as a specific 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 ChannelChatClearV1 {
type Payload = ChannelChatClearV1Payload;

const EVENT_TYPE: EventType = EventType::ChannelChatClear;
#[cfg(feature = "twitch_oauth2")]
const SCOPE: twitch_oauth2::Validator =
twitch_oauth2::validator![twitch_oauth2::Scope::UserReadChat];
const VERSION: &'static str = "1";
}

/// [`channel.chat.clear`](ChannelChatClearV1) response payload.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "deny_unknown_fields", serde(deny_unknown_fields))]
#[non_exhaustive]
pub struct ChannelChatClearV1Payload {
/// The broadcaster user ID.
pub broadcaster_user_id: types::UserId,
/// The broadcaster display name.
pub broadcaster_user_name: types::DisplayName,
/// The broadcaster login.
pub broadcaster_user_login: types::UserName,
}

#[cfg(test)]
#[test]
fn parse_payload() {
let payload = r##"
{
"subscription": {
"id": "f1c2a387-161a-49f9-a165-0f21d7a4e1c4",
"type": "channel.chat.clear",
"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_name": "Cool_User",
"broadcaster_user_login": "cool_user"
}
}
"##;

let val = dbg!(crate::eventsub::Event::parse(payload).unwrap());
crate::tests::roundtrip(&val)
}
97 changes: 97 additions & 0 deletions src/eventsub/channel/chat/clear_user_messages.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#![doc(alias = "channel.chat.clear_user_messages")]
//! A moderator or bot clears all messages for a specific user.

use super::*;
/// [`channel.chat.clear_user_messages`](https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types#channelchatclear_user_messages): a moderator or bot clears all messages for a specific user.
#[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 ChannelChatClearUserMessagesV1 {
/// User ID of the channel to receive chat clear user messages 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 ChannelChatClearUserMessagesV1 {
/// Get chat clear user messages on broadcasters channel reading chat as a specific 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 ChannelChatClearUserMessagesV1 {
type Payload = ChannelChatClearUserMessagesV1Payload;

const EVENT_TYPE: EventType = EventType::ChannelChatClearUserMessages;
#[cfg(feature = "twitch_oauth2")]
const SCOPE: twitch_oauth2::Validator =
twitch_oauth2::validator![twitch_oauth2::Scope::UserReadChat];
const VERSION: &'static str = "1";
}

/// [`channel.chat.clear_user_messages`](ChannelChatClearUserMessagesV1) response payload.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "deny_unknown_fields", serde(deny_unknown_fields))]
#[non_exhaustive]
pub struct ChannelChatClearUserMessagesV1Payload {
/// The broadcaster user ID.
pub broadcaster_user_id: types::UserId,
/// The broadcaster display name.
pub broadcaster_user_name: types::DisplayName,
/// The broadcaster login.
pub broadcaster_user_login: types::UserName,
/// The ID of the user that was banned or put in a timeout.
///
/// All of their messages are deleted.
pub target_user_id: types::UserId,
/// The user name of the user that was banned or put in a timeout.
pub target_user_name: types::DisplayName,
/// The user login of the user that was banned or put in a timeout.
pub target_user_login: types::UserName,
}

#[cfg(test)]
#[test]
fn parse_payload() {
let payload = r##"
{
"subscription": {
"id": "f1c2a387-161a-49f9-a165-0f21d7a4e1c4",
"type": "channel.chat.clear_user_messages",
"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_name": "Cool_User",
"broadcaster_user_login": "cool_user",
"target_user_id": "7734",
"target_user_name": "Uncool_viewer",
"target_user_login": "uncool_viewer"
}
}
"##;

let val = dbg!(crate::eventsub::Event::parse(payload).unwrap());
crate::tests::roundtrip(&val)
}
Loading
Loading