Skip to content

Commit

Permalink
feat(eventsub): add automod.message.update (beta for v2)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nerixyz committed Nov 18, 2024
1 parent 3f8d361 commit e8fa35f
Show file tree
Hide file tree
Showing 5 changed files with 301 additions and 3 deletions.
3 changes: 3 additions & 0 deletions src/eventsub/automod/message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pub use hold::{AutomodMessageHoldBeta, AutomodMessageHoldBetaPayload};
#[doc(inline)]
pub use hold::{AutomodMessageHoldV1, AutomodMessageHoldV1Payload};
#[doc(inline)]
#[cfg(feature = "beta")]
pub use update::{AutomodMessageUpdateBeta, AutomodMessageUpdateBetaPayload};
#[doc(inline)]
pub use update::{AutomodMessageUpdateV1, AutomodMessageUpdateV1Payload};

/// A message's Automod status
Expand Down
289 changes: 288 additions & 1 deletion src/eventsub/automod/message/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ pub struct AutomodMessageUpdateV1Payload {

#[cfg(test)]
#[test]
fn parse_payload() {
fn parse_payload_v1() {
use crate::eventsub::{Event, Message};

let payload = r##"
Expand Down Expand Up @@ -148,3 +148,290 @@ fn parse_payload() {
assert_eq!(notif.status, AutomodMessageStatus::Approved);
assert_eq!(notif.message.fragments.len(), 1);
}

/// [`automod.message.update`](dev.twitch.tv/docs/eventsub/eventsub-subscription-types/#automodmessageupdate-v2): a message in the automod queue had its status changed.
#[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]
#[cfg(feature = "beta")]
pub struct AutomodMessageUpdateBeta {
/// User ID of the broadcaster (channel).
#[cfg_attr(feature = "typed-builder", builder(setter(into)))]
pub broadcaster_user_id: types::UserId,
/// User ID of the moderator.
#[cfg_attr(feature = "typed-builder", builder(setter(into)))]
pub moderator_user_id: types::UserId,
}

#[cfg(feature = "beta")]
impl AutomodMessageUpdateBeta {
/// Get automod update notifications for 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(),
}
}

Check warning on line 178 in src/eventsub/automod/message/update.rs

View check run for this annotation

Codecov / codecov/patch

src/eventsub/automod/message/update.rs#L170-L178

Added lines #L170 - L178 were not covered by tests
}

#[cfg(feature = "beta")]
impl EventSubscription for AutomodMessageUpdateBeta {
type Payload = AutomodMessageUpdateBetaPayload;

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

/// [`automod.message.hold`](AutomodMessageUpdateBeta) response payload.
// XXX: this struct can't be deny-unknown-fields because of the flattened reason
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
#[cfg(feature = "beta")]
pub struct AutomodMessageUpdateBetaPayload {
/// 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,

/// The message sender’s user ID.
pub user_id: types::UserId,
/// The message sender’s login name.
pub user_login: types::UserName,
/// The message sender’s display name.
pub user_name: types::DisplayName,

/// The ID of the moderator.
pub moderator_user_id: types::UserId,
/// The login of the moderator.
pub moderator_user_login: types::UserName,
/// The moderator’s user name.
pub moderator_user_name: types::DisplayName,

/// The ID of the message that was flagged by automod.
pub message_id: types::MsgId,
/// The body of the message.
pub message: AutomodMessage,
/// The message’s status.
pub status: AutomodMessageStatus,
/// The timestamp of when automod saved the message.
pub held_at: types::Timestamp,
/// The reason why a message was held
#[serde(flatten)]
pub reason: AutomodHeldReason,
}

#[cfg(all(test, feature = "beta"))]
#[test]
fn parse_payload_v2_automod() {
use crate::eventsub::{Event, Message};

let payload = r##"
{
"subscription": {
"id": "5d64b907-001e-4cf1-9227-37871c7ce1b0",
"status": "enabled",
"type": "automod.message.update",
"version": "beta",
"condition": {
"broadcaster_user_id": "129546453",
"moderator_user_id": "129546453"
},
"transport": {
"method": "websocket",
"session_id": "AgoQSrkrRHHrQsS-i4xbndeC0hIGY2VsbC1j"
},
"created_at": "2024-11-18T19:25:05.666970955Z",
"cost": 0
},
"event": {
"broadcaster_user_id": "129546453",
"broadcaster_user_login": "nerixyz",
"broadcaster_user_name": "nerixyz",
"user_id": "489584266",
"user_login": "uint128",
"user_name": "uint128",
"moderator_user_id": "129546453",
"moderator_user_login": "nerixyz",
"moderator_user_name": "nerixyz",
"message_id": "2a867e45-a4d3-4e7e-a5cc-a9a00ee98bf7",
"message": {
"text": "Kappa ass",
"fragments": [
{
"type": "emote",
"text": "Kappa",
"cheermote": null,
"emote": {
"id": "25",
"emote_set_id": "0"
}
},
{
"type": "text",
"text": " ",
"cheermote": null,
"emote": null
},
{
"type": "text",
"text": "ass",
"cheermote": null,
"emote": null
}
]
},
"reason": "automod",
"automod": {
"category": "swearing",
"level": 4,
"boundaries": [
{
"start_pos": 6,
"end_pos": 8
}
]
},
"blocked_term": null,
"status": "denied",
"held_at": "2024-11-18T19:26:37.707305502Z"
}
}
"##;

let val = Event::parse(payload).unwrap();
crate::tests::roundtrip(&val);

let Event::AutomodMessageUpdateBeta(val) = val else {
panic!("invalid event type");

Check warning on line 314 in src/eventsub/automod/message/update.rs

View check run for this annotation

Codecov / codecov/patch

src/eventsub/automod/message/update.rs#L314

Added line #L314 was not covered by tests
};
let Message::Notification(notif) = val.message else {
panic!("invalid message type");

Check warning on line 317 in src/eventsub/automod/message/update.rs

View check run for this annotation

Codecov / codecov/patch

src/eventsub/automod/message/update.rs#L317

Added line #L317 was not covered by tests
};

assert_eq!(notif.broadcaster_user_id.as_str(), "129546453");
assert_eq!(notif.message.fragments.len(), 3);
assert_eq!(notif.status, AutomodMessageStatus::Denied);

let AutomodHeldReason::Automod { automod } = &notif.reason else {
panic!("invalid held reason");

Check warning on line 325 in src/eventsub/automod/message/update.rs

View check run for this annotation

Codecov / codecov/patch

src/eventsub/automod/message/update.rs#L325

Added line #L325 was not covered by tests
};
assert_eq!(
automod.category,
AutomodCategory::Unknown("swearing".to_string())
);
assert_eq!(automod.level, 4);
assert_eq!(
automod.boundaries,
&[AutomodMessageBoundary {
start_pos: 6,
end_pos: 8
}]
);
}

#[cfg(all(test, feature = "beta"))]
#[test]
fn parse_payload_v2_blocked_term() {
use crate::eventsub::{Event, Message};

let payload = r##"
{
"subscription": {
"id": "5d64b907-001e-4cf1-9227-37871c7ce1b0",
"status": "enabled",
"type": "automod.message.update",
"version": "beta",
"condition": {
"broadcaster_user_id": "129546453",
"moderator_user_id": "129546453"
},
"transport": {
"method": "websocket",
"session_id": "AgoQSrkrRHHrQsS-i4xbndeC0hIGY2VsbC1j"
},
"created_at": "2024-11-18T19:25:05.666970955Z",
"cost": 0
},
"event": {
"broadcaster_user_id": "129546453",
"broadcaster_user_login": "nerixyz",
"broadcaster_user_name": "nerixyz",
"user_id": "489584266",
"user_login": "uint128",
"user_name": "uint128",
"moderator_user_id": "129546453",
"moderator_user_login": "nerixyz",
"moderator_user_name": "nerixyz",
"message_id": "8c2b43ed-88a0-4b3e-8c02-266c323e1d95",
"message": {
"text": "foo",
"fragments": [
{
"type": "text",
"text": "foo",
"cheermote": null,
"emote": null
}
]
},
"reason": "blocked_term",
"automod": null,
"blocked_term": {
"terms_found": [
{
"term_id": "e4d4f1ba-99bf-4b19-9875-cd4eda98ead9",
"owner_broadcaster_user_id": "129546453",
"owner_broadcaster_user_login": "nerixyz",
"owner_broadcaster_user_name": "nerixyz",
"boundary": {
"start_pos": 0,
"end_pos": 2
}
}
]
},
"status": "approved",
"held_at": "2024-11-18T19:25:52.991756968Z"
}
}
"##;

let val = Event::parse(payload).unwrap();
crate::tests::roundtrip(&val);

let Event::AutomodMessageUpdateBeta(val) = val else {
panic!("invalid event type");

Check warning on line 412 in src/eventsub/automod/message/update.rs

View check run for this annotation

Codecov / codecov/patch

src/eventsub/automod/message/update.rs#L412

Added line #L412 was not covered by tests
};
let Message::Notification(notif) = val.message else {
panic!("invalid message type");

Check warning on line 415 in src/eventsub/automod/message/update.rs

View check run for this annotation

Codecov / codecov/patch

src/eventsub/automod/message/update.rs#L415

Added line #L415 was not covered by tests
};

assert_eq!(notif.broadcaster_user_id.as_str(), "129546453");
assert_eq!(notif.message.fragments.len(), 1);
assert_eq!(notif.status, AutomodMessageStatus::Approved);

let AutomodHeldReason::BlockedTerm { blocked_term } = &notif.reason else {
panic!("invalid held reason");

Check warning on line 423 in src/eventsub/automod/message/update.rs

View check run for this annotation

Codecov / codecov/patch

src/eventsub/automod/message/update.rs#L423

Added line #L423 was not covered by tests
};
assert_eq!(blocked_term.terms_found.len(), 1);
assert_eq!(
blocked_term.terms_found[0].term_id.as_str(),
"e4d4f1ba-99bf-4b19-9875-cd4eda98ead9"
);
assert_eq!(
blocked_term.terms_found[0].boundary,
AutomodMessageBoundary {
start_pos: 0,
end_pos: 2
}
);
}
3 changes: 3 additions & 0 deletions src/eventsub/automod/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pub use message::{AutomodMessageHoldBeta, AutomodMessageHoldBetaPayload};
#[doc(inline)]
pub use message::{AutomodMessageHoldV1, AutomodMessageHoldV1Payload};
#[doc(inline)]
#[cfg(feature = "beta")]
pub use message::{AutomodMessageUpdateBeta, AutomodMessageUpdateBetaPayload};
#[doc(inline)]
pub use message::{AutomodMessageUpdateV1, AutomodMessageUpdateV1Payload};

#[doc(inline)]
Expand Down
5 changes: 5 additions & 0 deletions src/eventsub/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ macro_rules! fill_events {
#[cfg(feature = "beta")]
automod::AutomodMessageHoldBeta;
automod::AutomodMessageUpdateV1;
#[cfg(feature = "beta")]
automod::AutomodMessageUpdateBeta;
automod::AutomodSettingsUpdateV1;
automod::AutomodTermsUpdateV1;
channel::ChannelAdBreakBeginV1;
Expand Down Expand Up @@ -286,6 +288,9 @@ pub enum Event {
AutomodMessageHoldBeta(Payload<automod::AutomodMessageHoldBeta>),
/// Automod Message Update V1 Event
AutomodMessageUpdateV1(Payload<automod::AutomodMessageUpdateV1>),
/// Automod Message Update Beta Event
#[cfg(feature = "beta")]
AutomodMessageUpdateBeta(Payload<automod::AutomodMessageUpdateBeta>),
/// Automod Settings Update V1 Event
AutomodSettingsUpdateV1(Payload<automod::AutomodSettingsUpdateV1>),
/// Automod Terms Update V1 Event
Expand Down
4 changes: 2 additions & 2 deletions src/eventsub/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,14 @@
//!
//! <!-- generate with "cargo xtask overview" (with a nightly toolchain) -->
//! <!-- BEGIN-OVERVIEW -->
//! <details><summary style="cursor: pointer"><code style="color: var(--link-color)">automod.*</code> 🟡 5/6</summary>
//! <details><summary style="cursor: pointer"><code style="color: var(--link-color)">automod.*</code> 🟢 6/6</summary>
//!
//! | Name | Subscription<br>Payload |
//! |---|:---|
//! | [`automod.message.hold`](https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types#automodmessagehold) | [AutomodMessageHoldV1](automod::AutomodMessageHoldV1)<br>[AutomodMessageHoldV1Payload](automod::AutomodMessageHoldV1Payload) |
//! | [`automod.message.hold`](https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types#automodmessagehold-v2) | [AutomodMessageHoldBeta](automod::AutomodMessageHoldBeta)<br>[AutomodMessageHoldBetaPayload](automod::AutomodMessageHoldBetaPayload) |
//! | [`automod.message.update`](https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types#automodmessageupdate) | [AutomodMessageUpdateV1](automod::AutomodMessageUpdateV1)<br>[AutomodMessageUpdateV1Payload](automod::AutomodMessageUpdateV1Payload) |
//! | [`automod.message.update`](https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types#automodmessageupdate-v2) | -<br>- |
//! | [`automod.message.update`](https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types#automodmessageupdate-v2) | [AutomodMessageUpdateBeta](automod::AutomodMessageUpdateBeta)<br>[AutomodMessageUpdateBetaPayload](automod::AutomodMessageUpdateBetaPayload) |
//! | [`automod.settings.update`](https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types#automodsettingsupdate) | [AutomodSettingsUpdateV1](automod::AutomodSettingsUpdateV1)<br>[AutomodSettingsUpdateV1Payload](automod::AutomodSettingsUpdateV1Payload) |
//! | [`automod.terms.update`](https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types#automodtermsupdate) | [AutomodTermsUpdateV1](automod::AutomodTermsUpdateV1)<br>[AutomodTermsUpdateV1Payload](automod::AutomodTermsUpdateV1Payload) |
//!
Expand Down

0 comments on commit e8fa35f

Please sign in to comment.