diff --git a/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/AugmentedMessage.java b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/AugmentedMessage.java new file mode 100644 index 000000000..ca193c5c8 --- /dev/null +++ b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/AugmentedMessage.java @@ -0,0 +1,22 @@ +package com.github.twitch4j.eventsub.domain; + +import com.github.twitch4j.eventsub.domain.chat.Message; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Setter; +import lombok.ToString; + +@Data +@Setter(AccessLevel.PRIVATE) +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class AugmentedMessage extends Message { + + /** + * The UUID that identifies the message. + */ + private String messageId; + +} diff --git a/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/EvasionEvaluation.java b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/EvasionEvaluation.java new file mode 100644 index 000000000..69c737c5d --- /dev/null +++ b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/EvasionEvaluation.java @@ -0,0 +1,17 @@ +package com.github.twitch4j.eventsub.domain; + +import com.fasterxml.jackson.annotation.JsonAlias; +import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; + +public enum EvasionEvaluation { + + @JsonAlias("likely") // what docs claim + LIKELY_EVADER, + + @JsonAlias("possible") // what docs claim + POSSIBLE_EVADER, + + @JsonEnumDefaultValue + UNKNOWN + +} diff --git a/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/SuspiciousStatus.java b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/SuspiciousStatus.java new file mode 100644 index 000000000..8ee80b1ff --- /dev/null +++ b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/SuspiciousStatus.java @@ -0,0 +1,10 @@ +package com.github.twitch4j.eventsub.domain; + +import com.fasterxml.jackson.annotation.JsonAlias; + +public enum SuspiciousStatus { + @JsonAlias("none") // what docs falsely claim + NO_TREATMENT, + ACTIVE_MONITORING, + RESTRICTED +} diff --git a/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/SuspiciousType.java b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/SuspiciousType.java new file mode 100644 index 000000000..338a33fb6 --- /dev/null +++ b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/SuspiciousType.java @@ -0,0 +1,20 @@ +package com.github.twitch4j.eventsub.domain; + +import com.fasterxml.jackson.annotation.JsonAlias; +import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; + +public enum SuspiciousType { + + @JsonAlias("manual") // what docs falsely claim + MANUALLY_ADDED, + + @JsonAlias({"ban_evader_detector", "ban_evader"}) // what docs claim (yes, both) + DETECTED_BAN_EVADER, + + @JsonAlias("shared_channel_ban") // what docs claim + BANNED_IN_SHARED_CHANNEL, + + @JsonEnumDefaultValue + UNKNOWN + +} diff --git a/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/chat/Emote.java b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/chat/Emote.java index a70884a59..5a5de09ec 100644 --- a/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/chat/Emote.java +++ b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/chat/Emote.java @@ -3,8 +3,9 @@ import lombok.AccessLevel; import lombok.Data; import lombok.Setter; +import org.jetbrains.annotations.Nullable; -import java.util.Set; +import java.util.EnumSet; @Data @Setter(AccessLevel.PRIVATE) @@ -23,6 +24,7 @@ public class Emote { /** * The ID of the broadcaster who owns the emote. */ + @Nullable private String ownerId; /** @@ -31,7 +33,8 @@ public class Emote { * For example, if the emote is available only as a static PNG, the array contains only static. * But if the emote is available as a static PNG and an animated GIF, the array contains static and animated. */ - private Set format; + @Nullable + private EnumSet format; public enum Format { /** diff --git a/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/chat/Fragment.java b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/chat/Fragment.java index 8d3c09c98..59fc1685c 100644 --- a/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/chat/Fragment.java +++ b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/domain/chat/Fragment.java @@ -1,5 +1,6 @@ package com.github.twitch4j.eventsub.domain.chat; +import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; import lombok.AccessLevel; import lombok.Data; @@ -28,6 +29,7 @@ public class Fragment { * Optional: Metadata pertaining to the cheermote. */ @Nullable + @JsonAlias("Cheermote") private Cheermote cheermote; /** diff --git a/eventsub-common/src/main/java/com/github/twitch4j/eventsub/events/SuspiciousUserMessageEvent.java b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/events/SuspiciousUserMessageEvent.java new file mode 100644 index 000000000..856776d2e --- /dev/null +++ b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/events/SuspiciousUserMessageEvent.java @@ -0,0 +1,51 @@ +package com.github.twitch4j.eventsub.events; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.twitch4j.eventsub.domain.AugmentedMessage; +import com.github.twitch4j.eventsub.domain.EvasionEvaluation; +import com.github.twitch4j.eventsub.domain.SuspiciousStatus; +import com.github.twitch4j.eventsub.domain.SuspiciousType; +import lombok.AccessLevel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Setter; +import lombok.ToString; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.EnumSet; + +@Data +@Setter(AccessLevel.PRIVATE) +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class SuspiciousUserMessageEvent extends EventSubUserChannelEvent { + + /** + * The status set for the suspicious user. + */ + @JsonProperty("low_trust_status") + private SuspiciousStatus status; + + /** + * Channel IDs where the suspicious user is also banned. + */ + @Nullable + private Collection sharedBanChannelIds; + + /** + * User types (if any) that apply to the suspicious user. + */ + private EnumSet types; + + /** + * A ban evasion likelihood value (if any) that as been applied to the user automatically by Twitch. + */ + private EvasionEvaluation banEvasionEvaluation; + + /** + * The structured chat message. + */ + private AugmentedMessage message; + +} diff --git a/eventsub-common/src/main/java/com/github/twitch4j/eventsub/events/SuspiciousUserUpdateEvent.java b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/events/SuspiciousUserUpdateEvent.java new file mode 100644 index 000000000..2d2318d3a --- /dev/null +++ b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/events/SuspiciousUserUpdateEvent.java @@ -0,0 +1,23 @@ +package com.github.twitch4j.eventsub.events; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.twitch4j.eventsub.domain.SuspiciousStatus; +import lombok.AccessLevel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Setter; +import lombok.ToString; + +@Data +@Setter(AccessLevel.PRIVATE) +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class SuspiciousUserUpdateEvent extends EventSubModerationEvent { + + /** + * The status set for the suspicious user. + */ + @JsonProperty("low_trust_status") + private SuspiciousStatus status; + +} diff --git a/eventsub-common/src/main/java/com/github/twitch4j/eventsub/subscriptions/SubscriptionTypes.java b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/subscriptions/SubscriptionTypes.java index 85b18c087..9f924e09d 100644 --- a/eventsub-common/src/main/java/com/github/twitch4j/eventsub/subscriptions/SubscriptionTypes.java +++ b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/subscriptions/SubscriptionTypes.java @@ -47,6 +47,8 @@ public class SubscriptionTypes { public final ChannelSubscriptionEndType CHANNEL_SUBSCRIPTION_END; public final ChannelSubscriptionGiftType CHANNEL_SUBSCRIPTION_GIFT; public final ChannelSubscriptionMessageType CHANNEL_SUBSCRIPTION_MESSAGE; + public final SuspiciousUserMessageType CHANNEL_SUSPICIOUS_USER_MESSAGE; + public final SuspiciousUserUpdateType CHANNEL_SUSPICIOUS_USER_UPDATE; public final ChannelUnbanType CHANNEL_UNBAN; public final @Deprecated ChannelUpdateType CHANNEL_UPDATE; public final ChannelUpdateV2Type CHANNEL_UPDATE_V2; @@ -115,6 +117,8 @@ public class SubscriptionTypes { CHANNEL_SUBSCRIPTION_END = new ChannelSubscriptionEndType(), CHANNEL_SUBSCRIPTION_GIFT = new ChannelSubscriptionGiftType(), CHANNEL_SUBSCRIPTION_MESSAGE = new ChannelSubscriptionMessageType(), + CHANNEL_SUSPICIOUS_USER_MESSAGE = new SuspiciousUserMessageType(), + CHANNEL_SUSPICIOUS_USER_UPDATE = new SuspiciousUserUpdateType(), CHANNEL_UNBAN = new ChannelUnbanType(), CHANNEL_UPDATE = new ChannelUpdateType(), CHANNEL_UPDATE_V2 = new ChannelUpdateV2Type(), diff --git a/eventsub-common/src/main/java/com/github/twitch4j/eventsub/subscriptions/SuspiciousUserMessageType.java b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/subscriptions/SuspiciousUserMessageType.java new file mode 100644 index 000000000..409d9f25b --- /dev/null +++ b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/subscriptions/SuspiciousUserMessageType.java @@ -0,0 +1,33 @@ +package com.github.twitch4j.eventsub.subscriptions; + +import com.github.twitch4j.eventsub.condition.ModeratorEventSubCondition; +import com.github.twitch4j.eventsub.events.SuspiciousUserMessageEvent; + +/** + * Fires when a chat message has been sent from a suspicious user. + *

+ * Requires authorization from a moderator of the channel with the moderator:read:suspicious_users scope. + * + * @see com.github.twitch4j.auth.domain.TwitchScopes#HELIX_SUSPICIOUS_USERS_READ + */ +public class SuspiciousUserMessageType implements SubscriptionType, SuspiciousUserMessageEvent> { + @Override + public String getName() { + return "channel.suspicious_user.message"; + } + + @Override + public String getVersion() { + return "1"; + } + + @Override + public ModeratorEventSubCondition.ModeratorEventSubConditionBuilder getConditionBuilder() { + return ModeratorEventSubCondition.builder(); + } + + @Override + public Class getEventClass() { + return SuspiciousUserMessageEvent.class; + } +} diff --git a/eventsub-common/src/main/java/com/github/twitch4j/eventsub/subscriptions/SuspiciousUserUpdateType.java b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/subscriptions/SuspiciousUserUpdateType.java new file mode 100644 index 000000000..b6b36430a --- /dev/null +++ b/eventsub-common/src/main/java/com/github/twitch4j/eventsub/subscriptions/SuspiciousUserUpdateType.java @@ -0,0 +1,33 @@ +package com.github.twitch4j.eventsub.subscriptions; + +import com.github.twitch4j.eventsub.condition.ModeratorEventSubCondition; +import com.github.twitch4j.eventsub.events.SuspiciousUserUpdateEvent; + +/** + * Fires when a suspicious user's treatment has been updated. + *

+ * Requires authorization from a moderator of the channel with the moderator:read:suspicious_users scope. + * + * @see com.github.twitch4j.auth.domain.TwitchScopes#HELIX_SUSPICIOUS_USERS_READ + */ +public class SuspiciousUserUpdateType implements SubscriptionType, SuspiciousUserUpdateEvent> { + @Override + public String getName() { + return "channel.suspicious_user.update"; + } + + @Override + public String getVersion() { + return "1"; + } + + @Override + public ModeratorEventSubCondition.ModeratorEventSubConditionBuilder getConditionBuilder() { + return ModeratorEventSubCondition.builder(); + } + + @Override + public Class getEventClass() { + return SuspiciousUserUpdateEvent.class; + } +} diff --git a/eventsub-common/src/test/java/com/github/twitch4j/eventsub/events/SuspiciousUserMessageEventTest.java b/eventsub-common/src/test/java/com/github/twitch4j/eventsub/events/SuspiciousUserMessageEventTest.java new file mode 100644 index 000000000..14719b88c --- /dev/null +++ b/eventsub-common/src/test/java/com/github/twitch4j/eventsub/events/SuspiciousUserMessageEventTest.java @@ -0,0 +1,38 @@ +package com.github.twitch4j.eventsub.events; + +import com.github.twitch4j.common.util.TypeConvert; +import com.github.twitch4j.eventsub.domain.EvasionEvaluation; +import com.github.twitch4j.eventsub.domain.SuspiciousStatus; +import com.github.twitch4j.eventsub.domain.SuspiciousType; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import java.util.EnumSet; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Tag("unittest") +class SuspiciousUserMessageEventTest { + + @Test + void deserializeManualMonitor() { + SuspiciousUserMessageEvent event = TypeConvert.jsonToObject( + "{\"broadcaster_user_id\":\"53888434\",\"broadcaster_user_name\":\"OGprodigy\",\"broadcaster_user_login\":\"ogprodigy\",\"user_id\":\"100135110\",\"user_name\":\"StreamElements\",\"user_login\":\"streamelements\",\"low_trust_status\":\"active_monitoring\",\"shared_ban_channel_ids\":null,\"types\":[\"manually_added\"],\"ban_evasion_evaluation\":\"unknown\",\"message\":{\"message_id\":\"0a173734-bd97-4e15-9b00-0fb1d198d881\",\"text\":\"@OGprodigy You can find a list of all Commands here https://StreamElements.com/ogprodigy/commands\",\"fragments\":[]}}", + SuspiciousUserMessageEvent.class + ); + assertEquals("53888434", event.getBroadcasterUserId()); + assertEquals("100135110", event.getUserId()); + assertEquals(SuspiciousStatus.ACTIVE_MONITORING, event.getStatus()); + assertNull(event.getSharedBanChannelIds()); + assertEquals(EnumSet.of(SuspiciousType.MANUALLY_ADDED), event.getTypes()); + assertEquals(EvasionEvaluation.UNKNOWN, event.getBanEvasionEvaluation()); + assertNotNull(event.getMessage()); + assertEquals("0a173734-bd97-4e15-9b00-0fb1d198d881", event.getMessage().getMessageId()); + assertEquals("@OGprodigy You can find a list of all Commands here https://StreamElements.com/ogprodigy/commands", event.getMessage().getText()); + assertTrue(event.getMessage().getFragments().isEmpty()); + } + +}