diff --git a/CHANGELOG.md b/CHANGELOG.md index 8506f238..c10ae413 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ - Bugfix: Fixed the reply button showing for inline whispers and announcements. (#5863) - Bugfix: Fixed suspicious user treatment update messages not being searchable. (#5865) - Bugfix: Ensure miniaudio backend exits even if it doesn't exit cleanly. (#5896) -- Dev: Add initial experimental EventSub support. (#5837, #5895, #5897, #5904, #5910, #5903, #5915, #5916) +- Dev: Add initial experimental EventSub support. (#5837, #5895, #5897, #5904, #5910, #5903, #5915, #5916, #5930) - Dev: Highlight checks now use non-capturing groups for the boundaries. (#5784) - Dev: Removed unused PubSub whisper code. (#5898) - Dev: Updated Conan dependencies. (#5776) diff --git a/lib/twitch-eventsub-ws/ast/lib/generate.py b/lib/twitch-eventsub-ws/ast/lib/generate.py index ed6647f1..276c8e1e 100644 --- a/lib/twitch-eventsub-ws/ast/lib/generate.py +++ b/lib/twitch-eventsub-ws/ast/lib/generate.py @@ -17,6 +17,7 @@ def _generate_implementation(header_path: str, items: list[Struct | Enum]) -> st doc = f"""// WARNING: This file is automatically generated. Any changes will be lost. #include "{header_path}" #include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep +#include "twitch-eventsub-ws/detail/variant.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/detail/errors.hpp" #include diff --git a/lib/twitch-eventsub-ws/ast/lib/member.py b/lib/twitch-eventsub-ws/ast/lib/member.py index f32fcc2e..52f9b5ed 100644 --- a/lib/twitch-eventsub-ws/ast/lib/member.py +++ b/lib/twitch-eventsub-ws/ast/lib/member.py @@ -1,6 +1,7 @@ from __future__ import annotations from typing import Optional +from dataclasses import dataclass import logging @@ -54,6 +55,19 @@ def _is_trivially_copyable(type: clang.cindex.Type) -> bool: return _is_chrono_like_type(type) +def _has_no_fields(type: clang.cindex.Type) -> bool: + for _ in type.get_fields(): + return False + return True + + +@dataclass +class VariantType: + name: str + trivial: bool + empty: bool + + class Member: def __init__( self, @@ -68,6 +82,8 @@ class Member: self.type_name = type_name self.tag: Optional[str] = None self.trivial = trivial + self.variant_types: list[VariantType] | None = None + self.variant_fallback: str | None = None self.dont_fail_on_deserialization: bool = False @@ -124,7 +140,8 @@ class Member: overwrite_member_type = MemberType.OPTIONAL_VECTOR case other: log.warning(f"Vector cannot be added on top of other member type: {other}") - + case "variant": + overwrite_member_type = MemberType.VARIANT case other: log.warning(f"Unhandled template type: {other}") @@ -144,8 +161,28 @@ class Member: member.apply_comment_commands(comment_commands) + if member.member_type == MemberType.VARIANT: + member.apply_variant(node.type, namespace) + return member + def apply_variant(self, type: clang.cindex.Type, namespace: tuple[str, ...]): + self.variant_types = [] + for idx in range(type.get_num_template_arguments()): + inner = type.get_template_argument_type(idx) + name = get_type_name(inner, namespace) + if name == "std::string" or name == "String": + assert not self.variant_fallback + self.variant_fallback = name + continue + self.variant_types.append( + VariantType( + name=name, + trivial=_is_trivially_copyable(inner), + empty=_has_no_fields(inner), + ) + ) + def __eq__(self, other: object) -> bool: if not isinstance(other, self.__class__): return False diff --git a/lib/twitch-eventsub-ws/ast/lib/membertype.py b/lib/twitch-eventsub-ws/ast/lib/membertype.py index cf376063..a5db355b 100644 --- a/lib/twitch-eventsub-ws/ast/lib/membertype.py +++ b/lib/twitch-eventsub-ws/ast/lib/membertype.py @@ -6,3 +6,4 @@ class MemberType(Enum): VECTOR = 2 OPTIONAL = 3 OPTIONAL_VECTOR = 4 + VARIANT = 5 diff --git a/lib/twitch-eventsub-ws/ast/lib/templates/field-variant.tmpl b/lib/twitch-eventsub-ws/ast/lib/templates/field-variant.tmpl new file mode 100644 index 00000000..74a12138 --- /dev/null +++ b/lib/twitch-eventsub-ws/ast/lib/templates/field-variant.tmpl @@ -0,0 +1,49 @@ +{% if not field.tag -%} +static_assert(false, "{{field.name}} doesn't have a json_tag"); +{%- endif %} +const auto *jv{{field.name}}Tag = root.if_contains("{{field.tag}}"); +if (jv{{field.name}}Tag == nullptr) +{ + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); +} + +auto {{field.name}}TagRes = boost::json::try_value_to(*jv{{field.name}}Tag); +if ({{field.name}}TagRes.has_error()) +{ + return {{field.name}}TagRes.error(); +} +std::string_view {{field.name}}Tag = *{{field.name}}TagRes; +decltype(std::declval<{{struct.full_name}}>().{{field.name}}) {{field.name}}; +{%- for type in field.variant_types %} + +{%- if not loop.first %}else{% endif %} if ({{field.name}}Tag == {{type.name}}::TAG) +{ +{%- if type.empty -%} + {{field.name}}.emplace<{{type.name}}>(); +{%- else -%} + const auto *{{field.name}}Val = root.if_contains(detail::fieldFor<{{type.name}}>()); + if (!{{field.name}}Val) + { + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); + } + auto {{field.name}}{{type.name}} = boost::json::try_value_to<{{type.name}}>(*{{field.name}}Val); + if ({{field.name}}{{type.name}}.has_error()) + { + return {{field.name}}{{type.name}}.error(); + } + {% if type.trivial -%} + {{field.name}}.emplace<{{type.name}}>({{field.name}}{{type.name}}.value()); + {%- else -%} + {{field.name}}.emplace<{{type.name}}>(std::move({{field.name}}{{type.name}}.value())); + {%- endif %} +{%- endif -%} +} +{%- endfor -%} +else +{ +{%- if field.variant_fallback -%} + {{field.name}}.emplace<{{field.variant_fallback}}>({{field.name}}Tag); +{%- else -%} + EVENTSUB_BAIL_HERE(error::Kind::UnknownVariant); +{%- endif -%} +} diff --git a/lib/twitch-eventsub-ws/ast/lib/templates/initializer-variant.tmpl b/lib/twitch-eventsub-ws/ast/lib/templates/initializer-variant.tmpl new file mode 100644 index 00000000..8964263a --- /dev/null +++ b/lib/twitch-eventsub-ws/ast/lib/templates/initializer-variant.tmpl @@ -0,0 +1 @@ +.{{field.name}} = std::move({{field.name}}), diff --git a/lib/twitch-eventsub-ws/ast/lib/templates/struct-implementation.tmpl b/lib/twitch-eventsub-ws/ast/lib/templates/struct-implementation.tmpl index f428bf73..c32931c2 100644 --- a/lib/twitch-eventsub-ws/ast/lib/templates/struct-implementation.tmpl +++ b/lib/twitch-eventsub-ws/ast/lib/templates/struct-implementation.tmpl @@ -1,5 +1,5 @@ boost::json::result_for<{{struct.full_name}}, boost::json::value>::type tag_invoke( - boost::json::try_value_to_tag<{{struct.full_name}}> /* tag */, const boost::json::value &jvRoot) + boost::json::try_value_to_tag<{{struct.full_name}}> /* tag */, const boost::json::value &{%- if struct.members|length -%}jvRoot{%- else -%}/* jvRoot */{%- endif -%}) { {% if struct.inner_root %} if (!jvRoot.is_object()) @@ -38,6 +38,8 @@ boost::json::result_for<{{struct.full_name}}, boost::json::value>::type tag_invo {% include 'field-optional.tmpl' indent content %} {%- elif field.member_type == MemberType.OPTIONAL_VECTOR -%} {% include 'field-optional-vector.tmpl' indent content %} + {%- elif field.member_type == MemberType.VARIANT -%} + {% include 'field-variant.tmpl' indent content %} {%- endif -%} {% endfor %} @@ -51,6 +53,8 @@ boost::json::result_for<{{struct.full_name}}, boost::json::value>::type tag_invo {% include 'initializer-optional.tmpl' indent content %} {%- elif field.member_type == MemberType.OPTIONAL_VECTOR -%} {% include 'initializer-optional-vector.tmpl' indent content %} + {%- elif field.member_type == MemberType.VARIANT -%} + {% include 'initializer-variant.tmpl' indent content %} {%- endif -%} {% endfor %} }; diff --git a/lib/twitch-eventsub-ws/ast/lib/walker.py b/lib/twitch-eventsub-ws/ast/lib/walker.py index 5d316fbe..05195b55 100644 --- a/lib/twitch-eventsub-ws/ast/lib/walker.py +++ b/lib/twitch-eventsub-ws/ast/lib/walker.py @@ -68,6 +68,8 @@ class Walker: if type is None: # Skip nodes without a type return False + if node.storage_class == clang.cindex.StorageClass.STATIC: + return False # log.debug(f"{struct}: {type.spelling} {node.spelling} ({type.kind})") if struct: diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/detail/variant.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/detail/variant.hpp new file mode 100644 index 00000000..58b730d9 --- /dev/null +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/detail/variant.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace chatterino::eventsub::lib::detail { + +template +consteval std::string_view fieldFor() +{ + return T::TAG; +} + +template +consteval std::string_view fieldFor() + requires requires { T::FIELD; } +{ + return T::FIELD; +} + +} // namespace chatterino::eventsub::lib::detail diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/errors.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/errors.hpp index 56942cf8..662292e7 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/errors.hpp +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/errors.hpp @@ -13,7 +13,8 @@ enum class Kind : int { ExpectedString, UnknownEnumValue, InnerRootMissing, - NoMessageHandler + NoMessageHandler, + UnknownVariant, }; class ApplicationErrorCategory final : public boost::system::error_category diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-notification-v1.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-notification-v1.hpp index 7ab29a79..0a3cf9a4 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-notification-v1.hpp +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-notification-v1.hpp @@ -44,12 +44,16 @@ struct MessageFragment { }; struct Subcription { + static constexpr std::string_view TAG = "sub"; + std::string subTier; bool isPrime; int durationMonths; }; struct Resubscription { + static constexpr std::string_view TAG = "resub"; + int cumulativeMonths; int durationMonths; std::optional streakMonths; @@ -63,6 +67,8 @@ struct Resubscription { }; struct GiftSubscription { + static constexpr std::string_view TAG = "sub_gift"; + int durationMonths; std::optional cumulativeTotal; std::optional streakMonths; @@ -74,6 +80,8 @@ struct GiftSubscription { }; struct CommunityGiftSubscription { + static constexpr std::string_view TAG = "community_sub_gift"; + std::string id; int total; std::string subTier; @@ -81,6 +89,8 @@ struct CommunityGiftSubscription { }; struct GiftPaidUpgrade { + static constexpr std::string_view TAG = "gift_paid_upgrade"; + bool gifterIsAnonymous; std::optional gifterUserID; std::optional gifterUserName; @@ -88,10 +98,14 @@ struct GiftPaidUpgrade { }; struct PrimePaidUpgrade { + static constexpr std::string_view TAG = "prime_paid_upgrade"; + std::string subTier; }; struct Raid { + static constexpr std::string_view TAG = "raid"; + std::string userID; std::string userName; std::string userLogin; @@ -100,9 +114,12 @@ struct Raid { }; struct Unraid { + static constexpr std::string_view TAG = "unraid"; }; struct PayItForward { + static constexpr std::string_view TAG = "pay_it_forward"; + bool gifterIsAnonymous; std::optional gifterUserID; std::optional gifterUserName; @@ -110,21 +127,29 @@ struct PayItForward { }; struct Announcement { + static constexpr std::string_view TAG = "announcement"; + std::string color; }; struct CharityDonationAmount { + static constexpr std::string_view TAG = "charity_donation_amount"; + int value; int decimalPlaces; std::string currency; }; struct CharityDonation { + static constexpr std::string_view TAG = "charity_donation"; + std::string charityName; CharityDonationAmount amount; }; struct BitsBadgeTier { + static constexpr std::string_view TAG = "bits_badge_tier"; + int tier; }; @@ -146,19 +171,22 @@ struct Event { std::string systemMessage; std::string messageID; Message message; - std::string noticeType; - std::optional sub; - std::optional resub; - std::optional subGift; - std::optional communitySubGift; - std::optional giftPaidUpgrade; - std::optional primePaidUpgrade; - std::optional raid; - std::optional unraid; - std::optional payItForward; - std::optional announcement; - std::optional charityDonation; - std::optional bitsBadgeTier; + /// json_tag=notice_type + std::variant + inner; }; struct Payload { diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.hpp index a587f579..2f138ef1 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.hpp +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.hpp @@ -22,9 +22,15 @@ namespace chatterino::eventsub::lib::payload::channel_moderate::v2 { */ struct Followers { + static constexpr std::string_view TAG = "followers"; + int followDurationMinutes; }; +struct FollowersOff { + static constexpr std::string_view TAG = "followersoff"; +}; + /* slow mode set to 30s {"subscription":{"id":"86d99e53-2837-40cf-bc6e-c6e00698919c","status":"enabled","type":"channel.moderate","version":"2","condition":{"broadcaster_user_id":"11148817","moderator_user_id":"117166826"},"transport":{"method":"websocket","session_id":"AgoQZgfnyKxXQ32hpzCWF4aCGBIGY2VsbC1j"},"created_at":"2025-02-01T12:02:16.005321321Z","cost":0},"event":{"broadcaster_user_id":"11148817","broadcaster_user_login":"pajlada","broadcaster_user_name":"pajlada","source_broadcaster_user_id":null,"source_broadcaster_user_login":null,"source_broadcaster_user_name":null,"moderator_user_id":"11148817","moderator_user_login":"pajlada","moderator_user_name":"pajlada","action":"slow","followers":null,"slow":{"wait_time_seconds":30},"vip":null,"unvip":null,"mod":null,"unmod":null,"ban":null,"unban":null,"timeout":null,"untimeout":null,"raid":null,"unraid":null,"delete":null,"automod_terms":null,"unban_request":null,"warn":null,"shared_chat_ban":null,"shared_chat_unban":null,"shared_chat_timeout":null,"shared_chat_untimeout":null,"shared_chat_delete":null}} */ @@ -34,9 +40,15 @@ struct Followers { */ struct Slow { + static constexpr std::string_view TAG = "slow"; + int waitTimeSeconds; }; +struct SlowOff { + static constexpr std::string_view TAG = "slowoff"; +}; + /* User is VIP'ed { "subscription": { @@ -96,6 +108,8 @@ struct Slow { */ struct Vip { + static constexpr std::string_view TAG = "vip"; + String userID; String userLogin; String userName; @@ -106,6 +120,8 @@ struct Vip { */ struct Unvip { + static constexpr std::string_view TAG = "unvip"; + String userID; String userLogin; String userName; @@ -116,6 +132,8 @@ struct Unvip { */ struct Mod { + static constexpr std::string_view TAG = "mod"; + std::string userID; std::string userLogin; std::string userName; @@ -126,6 +144,8 @@ struct Mod { */ struct Unmod { + static constexpr std::string_view TAG = "unmod"; + std::string userID; std::string userLogin; std::string userName; @@ -140,22 +160,34 @@ struct Unmod { */ struct Ban { + static constexpr std::string_view TAG = "ban"; + static constexpr std::string_view FIELD = "ban"; + std::string userID; std::string userLogin; std::string userName; // TODO: Verify that we handle null here std::string reason; }; +struct SharedChatBan : public Ban { + static constexpr std::string_view TAG = "shared_chat_ban"; +}; /* user is unbanned {"subscription":{"id":"86d99e53-2837-40cf-bc6e-c6e00698919c","status":"enabled","type":"channel.moderate","version":"2","condition":{"broadcaster_user_id":"11148817","moderator_user_id":"117166826"},"transport":{"method":"websocket","session_id":"AgoQZgfnyKxXQ32hpzCWF4aCGBIGY2VsbC1j"},"created_at":"2025-02-01T12:02:16.005321321Z","cost":0},"event":{"broadcaster_user_id":"11148817","broadcaster_user_login":"pajlada","broadcaster_user_name":"pajlada","source_broadcaster_user_id":null,"source_broadcaster_user_login":null,"source_broadcaster_user_name":null,"moderator_user_id":"11148817","moderator_user_login":"pajlada","moderator_user_name":"pajlada","action":"unban","followers":null,"slow":null,"vip":null,"unvip":null,"mod":null,"unmod":null,"ban":null,"unban":{"user_id":"70948394","user_login":"weeb123","user_name":"WEEB123"},"timeout":null,"untimeout":null,"raid":null,"unraid":null,"delete":null,"automod_terms":null,"unban_request":null,"warn":null,"shared_chat_ban":null,"shared_chat_unban":null,"shared_chat_timeout":null,"shared_chat_untimeout":null,"shared_chat_delete":null}} */ struct Unban { + static constexpr std::string_view TAG = "unban"; + static constexpr std::string_view FIELD = "unban"; + std::string userID; std::string userLogin; std::string userName; }; +struct SharedChatUnban : public Ban { + static constexpr std::string_view TAG = "shared_chat_unban"; +}; /* user is timed out without reason {"subscription":{"id":"86d99e53-2837-40cf-bc6e-c6e00698919c","status":"enabled","type":"channel.moderate","version":"2","condition":{"broadcaster_user_id":"11148817","moderator_user_id":"117166826"},"transport":{"method":"websocket","session_id":"AgoQZgfnyKxXQ32hpzCWF4aCGBIGY2VsbC1j"},"created_at":"2025-02-01T12:02:16.005321321Z","cost":0},"event":{"broadcaster_user_id":"11148817","broadcaster_user_login":"pajlada","broadcaster_user_name":"pajlada","source_broadcaster_user_id":null,"source_broadcaster_user_login":null,"source_broadcaster_user_name":null,"moderator_user_id":"11148817","moderator_user_login":"pajlada","moderator_user_name":"pajlada","action":"timeout","followers":null,"slow":null,"vip":null,"unvip":null,"mod":null,"unmod":null,"ban":null,"unban":null,"timeout":{"user_id":"70948394","user_login":"weeb123","user_name":"WEEB123","reason":"","expires_at":"2025-02-01T12:11:02.684499409Z"},"untimeout":null,"raid":null,"unraid":null,"delete":null,"automod_terms":null,"unban_request":null,"warn":null,"shared_chat_ban":null,"shared_chat_unban":null,"shared_chat_timeout":null,"shared_chat_untimeout":null,"shared_chat_delete":null}} @@ -166,6 +198,9 @@ struct Unban { */ struct Timeout { + static constexpr std::string_view TAG = "timeout"; + static constexpr std::string_view FIELD = "timeout"; + std::string userID; std::string userLogin; std::string userName; @@ -174,22 +209,33 @@ struct Timeout { // TODO: This should be a timestamp? std::string expiresAt; }; +struct SharedChatTimeout : public Ban { + static constexpr std::string_view TAG = "shared_chat_timeout"; +}; /* user is untimeouted {"subscription":{"id":"86d99e53-2837-40cf-bc6e-c6e00698919c","status":"enabled","type":"channel.moderate","version":"2","condition":{"broadcaster_user_id":"11148817","moderator_user_id":"117166826"},"transport":{"method":"websocket","session_id":"AgoQZgfnyKxXQ32hpzCWF4aCGBIGY2VsbC1j"},"created_at":"2025-02-01T12:02:16.005321321Z","cost":0},"event":{"broadcaster_user_id":"11148817","broadcaster_user_login":"pajlada","broadcaster_user_name":"pajlada","source_broadcaster_user_id":null,"source_broadcaster_user_login":null,"source_broadcaster_user_name":null,"moderator_user_id":"11148817","moderator_user_login":"pajlada","moderator_user_name":"pajlada","action":"untimeout","followers":null,"slow":null,"vip":null,"unvip":null,"mod":null,"unmod":null,"ban":null,"unban":null,"timeout":null,"untimeout":{"user_id":"70948394","user_login":"weeb123","user_name":"WEEB123"},"raid":null,"unraid":null,"delete":null,"automod_terms":null,"unban_request":null,"warn":null,"shared_chat_ban":null,"shared_chat_unban":null,"shared_chat_timeout":null,"shared_chat_untimeout":null,"shared_chat_delete":null}} */ struct Untimeout { + static constexpr std::string_view TAG = "untimeout"; + static constexpr std::string_view FIELD = "untimeout"; + std::string userID; std::string userLogin; std::string userName; }; +struct SharedChatUntimeout : public Ban { + static constexpr std::string_view TAG = "shared_chat_untimeout"; +}; /* channel is raided (from bajlada to pajlada) {"subscription":{"id":"e7b45c7a-9b4d-4101-8d7d-92e8945c26fa","status":"enabled","type":"channel.moderate","version":"2","condition":{"broadcaster_user_id":"159849156","moderator_user_id":"159849156"},"transport":{"method":"websocket","session_id":"AgoQEPRIfB3SQTCSkJM2zznvNxIGY2VsbC1j"},"created_at":"2025-02-01T12:12:45.685831769Z","cost":0},"event":{"broadcaster_user_id":"159849156","broadcaster_user_login":"bajlada","broadcaster_user_name":"BajLada","source_broadcaster_user_id":null,"source_broadcaster_user_login":null,"source_broadcaster_user_name":null,"moderator_user_id":"159849156","moderator_user_login":"bajlada","moderator_user_name":"BajLada","action":"raid","followers":null,"slow":null,"vip":null,"unvip":null,"mod":null,"unmod":null,"ban":null,"unban":null,"timeout":null,"untimeout":null,"raid":{"user_id":"11148817","user_login":"pajlada","user_name":"pajlada","viewer_count":0},"unraid":null,"delete":null,"automod_terms":null,"unban_request":null,"warn":null,"shared_chat_ban":null,"shared_chat_unban":null,"shared_chat_timeout":null,"shared_chat_untimeout":null,"shared_chat_delete":null}} */ struct Raid { + static constexpr std::string_view TAG = "raid"; + std::string userID; std::string userLogin; std::string userName; @@ -202,6 +248,8 @@ struct Raid { */ struct Unraid { + static constexpr std::string_view TAG = "unraid"; + std::string userID; std::string userLogin; std::string userName; @@ -212,12 +260,18 @@ struct Unraid { */ struct Delete { + static constexpr std::string_view TAG = "delete"; + static constexpr std::string_view FIELD = "delete"; + std::string userID; std::string userLogin; std::string userName; std::string messageID; std::string messageBody; }; +struct SharedChatDelete : public Ban { + static constexpr std::string_view TAG = "shared_chat_delete"; +}; /* automodded message approved {"subscription":{"id":"0ccc8f11-7e77-40cf-84a9-25ab934c30fb","status":"enabled","type":"channel.moderate","version":"2","condition":{"broadcaster_user_id":"117166826","moderator_user_id":"117166826"},"transport":{"method":"websocket","session_id":"AgoQfS3RYz3MSqOophais4HEjxIGY2VsbC1j"},"created_at":"2025-02-01T12:15:13.092673205Z","cost":0},"event":{"broadcaster_user_id":"117166826","broadcaster_user_login":"testaccount_420","broadcaster_user_name":"테스트계정420","source_broadcaster_user_id":null,"source_broadcaster_user_login":null,"source_broadcaster_user_name":null,"moderator_user_id":"117166826","moderator_user_login":"testaccount_420","moderator_user_name":"테스트계정420","action":"add_permitted_term","followers":null,"slow":null,"vip":null,"unvip":null,"mod":null,"unmod":null,"ban":null,"unban":null,"timeout":null,"untimeout":null,"raid":null,"unraid":null,"delete":null,"automod_terms":{"action":"add","list":"permitted","terms":["cock cock cock penis sex cock"],"from_automod":true},"unban_request":null,"warn":null,"shared_chat_ban":null,"shared_chat_unban":null,"shared_chat_timeout":null,"shared_chat_untimeout":null,"shared_chat_delete":null}} @@ -230,6 +284,8 @@ struct Delete { */ struct AutomodTerms { + static constexpr std::string_view FIELD = "automod_terms"; + // either add or remove std::string action; // either blocked or permitted @@ -239,6 +295,19 @@ struct AutomodTerms { bool fromAutomod; }; +struct AddBlockedTerm : public AutomodTerms { + static constexpr std::string_view TAG = "add_blocked_term"; +}; +struct AddPermittedTerm : public AutomodTerms { + static constexpr std::string_view TAG = "add_permitted_term"; +}; +struct RemoveBlockedTerm : public AutomodTerms { + static constexpr std::string_view TAG = "remove_blocked_term"; +}; +struct RemovePermittedTerm : public AutomodTerms { + static constexpr std::string_view TAG = "remove_permitted_term"; +}; + /* unban request approved {"subscription":{"id":"4284c08c-402a-43a8-8537-1e75f38f562c","status":"enabled","type":"channel.moderate","version":"2","condition":{"broadcaster_user_id":"11148817","moderator_user_id":"117166826"},"transport":{"method":"websocket","session_id":"AgoQfS3RYz3MSqOophais4HEjxIGY2VsbC1j"},"created_at":"2025-02-01T12:15:13.083098352Z","cost":0},"event":{"broadcaster_user_id":"11148817","broadcaster_user_login":"pajlada","broadcaster_user_name":"pajlada","source_broadcaster_user_id":null,"source_broadcaster_user_login":null,"source_broadcaster_user_name":null,"moderator_user_id":"11148817","moderator_user_login":"pajlada","moderator_user_name":"pajlada","action":"approve_unban_request","followers":null,"slow":null,"vip":null,"unvip":null,"mod":null,"unmod":null,"ban":null,"unban":null,"timeout":null,"untimeout":null,"raid":null,"unraid":null,"delete":null,"automod_terms":null,"unban_request":{"is_approved":true,"user_id":"159849156","user_login":"bajlada","user_name":"BajLada","moderator_message":"you have been granted mercy"},"warn":null,"shared_chat_ban":null,"shared_chat_unban":null,"shared_chat_timeout":null,"shared_chat_untimeout":null,"shared_chat_delete":null}} */ @@ -248,6 +317,8 @@ struct AutomodTerms { */ struct UnbanRequest { + static constexpr std::string_view FIELD = "unban_request"; + bool isApproved; std::string userID; @@ -257,6 +328,13 @@ struct UnbanRequest { std::string moderatorMessage; }; +struct ApproveUnbanRequest : public UnbanRequest { + static constexpr std::string_view TAG = "approve_unban_request"; +}; +struct DenyUnbanRequest : public UnbanRequest { + static constexpr std::string_view TAG = "deny_unban_request"; +}; + /* freetext warn from chatterino {"subscription":{"id":"4284c08c-402a-43a8-8537-1e75f38f562c","status":"enabled","type":"channel.moderate","version":"2","condition":{"broadcaster_user_id":"11148817","moderator_user_id":"117166826"},"transport":{"method":"websocket","session_id":"AgoQfS3RYz3MSqOophais4HEjxIGY2VsbC1j"},"created_at":"2025-02-01T12:15:13.083098352Z","cost":0},"event":{"broadcaster_user_id":"11148817","broadcaster_user_login":"pajlada","broadcaster_user_name":"pajlada","source_broadcaster_user_id":null,"source_broadcaster_user_login":null,"source_broadcaster_user_name":null,"moderator_user_id":"11148817","moderator_user_login":"pajlada","moderator_user_name":"pajlada","action":"warn","followers":null,"slow":null,"vip":null,"unvip":null,"mod":null,"unmod":null,"ban":null,"unban":null,"timeout":null,"untimeout":null,"raid":null,"unraid":null,"delete":null,"automod_terms":null,"unban_request":null,"warn":{"user_id":"159849156","user_login":"bajlada","user_name":"BajLada","reason":"this is a test warning from chatterino","chat_rules_cited":null},"shared_chat_ban":null,"shared_chat_unban":null,"shared_chat_timeout":null,"shared_chat_untimeout":null,"shared_chat_delete":null}} */ @@ -266,6 +344,8 @@ struct UnbanRequest { */ struct Warn { + static constexpr std::string_view TAG = "warn"; + std::string userID; std::string userLogin; std::string userName; @@ -275,44 +355,29 @@ struct Warn { std::vector chatRulesCited; }; -enum class Action : uint8_t { - Ban, - Timeout, - Unban, - Untimeout, - Clear, - Emoteonly, - Emoteonlyoff, - Followers, - Followersoff, - Uniquechat, - Uniquechatoff, - Slow, - Slowoff, - Subscribers, - Subscribersoff, - Unraid, - /// json_rename=delete - DeleteMessage, - /// clangd currently "inherits" all future comments to all future enum constants - /// so after using something like json_rename we need to ensure it doesn't spread - Unvip, - Vip, - Raid, - AddBlockedTerm, - AddPermittedTerm, - RemoveBlockedTerm, - RemovePermittedTerm, - Mod, - Unmod, - ApproveUnbanRequest, - DenyUnbanRequest, - Warn, - SharedChatBan, - SharedChatTimeout, - SharedChatUnban, - SharedChatUntimeout, - SharedChatDelete, +struct Clear { + static constexpr std::string_view TAG = "clear"; +}; + +struct EmoteOnly { + static constexpr std::string_view TAG = "emoteonly"; +}; +struct EmoteOnlyOff { + static constexpr std::string_view TAG = "emoteonlyoff"; +}; + +struct Uniquechat { + static constexpr std::string_view TAG = "uniquechat"; +}; +struct UniquechatOff { + static constexpr std::string_view TAG = "uniquechatoff"; +}; + +struct Subscribers { + static constexpr std::string_view TAG = "subscribers"; +}; +struct SubscribersOff { + static constexpr std::string_view TAG = "subscribersoff"; }; struct Event { @@ -337,31 +402,43 @@ struct Event { /// User Name (e.g. 테스트계정420) of the user who took the action String moderatorUserName; - // TODO: enum? - /// The action that took place (e.g. "warn" or "ban") - Action action; - - std::optional followers; - std::optional slow; - std::optional vip; - std::optional unvip; - std::optional unmod; - std::optional ban; - std::optional unban; - std::optional timeout; - std::optional untimeout; - std::optional raid; - std::optional unraid; - /// json_rename=delete - std::optional deleteMessage; - std::optional automodTerms; - std::optional unbanRequest; - std::optional warn; - std::optional sharedChatBan; - std::optional sharedChatUnban; - std::optional sharedChatTimeout; - std::optional sharedChatUntimeout; - std::optional sharedChatDelete; + /// json_tag=action + std::variant + action; }; struct Payload { diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.inc b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.inc index f10b0d30..edd42e67 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.inc +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.inc @@ -1,12 +1,16 @@ -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot); +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot); +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot); @@ -22,15 +26,31 @@ boost::json::result_for::type tag_invoke( boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot); +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot); +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot); +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot); +boost::json::result_for::type + tag_invoke(boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot); @@ -40,17 +60,71 @@ boost::json::result_for::type tag_invoke( boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot); +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot); +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type + tag_invoke(boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot); +boost::json::result_for::type + tag_invoke(boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot); +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot); diff --git a/lib/twitch-eventsub-ws/src/errors.cpp b/lib/twitch-eventsub-ws/src/errors.cpp index 3fa19986..c30310b3 100644 --- a/lib/twitch-eventsub-ws/src/errors.cpp +++ b/lib/twitch-eventsub-ws/src/errors.cpp @@ -25,6 +25,8 @@ std::string ApplicationErrorCategory::message(int ev) const return "Missing inner root"s; case Kind::NoMessageHandler: return "No message handler found"s; + case Kind::UnknownVariant: + return "Unknown variant value"s; } assert(false); diff --git a/lib/twitch-eventsub-ws/src/generated/messages/metadata.cpp b/lib/twitch-eventsub-ws/src/generated/messages/metadata.cpp index fa1f0401..815e72b3 100644 --- a/lib/twitch-eventsub-ws/src/generated/messages/metadata.cpp +++ b/lib/twitch-eventsub-ws/src/generated/messages/metadata.cpp @@ -1,6 +1,7 @@ // WARNING: This file is automatically generated. Any changes will be lost. #include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/detail/errors.hpp" +#include "twitch-eventsub-ws/detail/variant.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/messages/metadata.hpp" #include diff --git a/lib/twitch-eventsub-ws/src/generated/payloads/channel-ban-v1.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/channel-ban-v1.cpp index deccc2d9..9e8edb9a 100644 --- a/lib/twitch-eventsub-ws/src/generated/payloads/channel-ban-v1.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/channel-ban-v1.cpp @@ -1,6 +1,7 @@ // WARNING: This file is automatically generated. Any changes will be lost. #include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/detail/errors.hpp" +#include "twitch-eventsub-ws/detail/variant.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/payloads/channel-ban-v1.hpp" #include diff --git a/lib/twitch-eventsub-ws/src/generated/payloads/channel-chat-message-v1.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/channel-chat-message-v1.cpp index 725906c6..4cf3eb2a 100644 --- a/lib/twitch-eventsub-ws/src/generated/payloads/channel-chat-message-v1.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/channel-chat-message-v1.cpp @@ -1,6 +1,7 @@ // WARNING: This file is automatically generated. Any changes will be lost. #include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/detail/errors.hpp" +#include "twitch-eventsub-ws/detail/variant.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/payloads/channel-chat-message-v1.hpp" #include diff --git a/lib/twitch-eventsub-ws/src/generated/payloads/channel-chat-notification-v1.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/channel-chat-notification-v1.cpp index f0f4c6f0..4391d4de 100644 --- a/lib/twitch-eventsub-ws/src/generated/payloads/channel-chat-notification-v1.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/channel-chat-notification-v1.cpp @@ -1,6 +1,7 @@ // WARNING: This file is automatically generated. Any changes will be lost. #include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/detail/errors.hpp" +#include "twitch-eventsub-ws/detail/variant.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/payloads/channel-chat-notification-v1.hpp" #include @@ -979,7 +980,7 @@ boost::json::result_for::type tag_invoke( boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag /* tag */, - const boost::json::value &jvRoot) + const boost::json::value & /* jvRoot */) { return Unraid{}; } @@ -1439,186 +1440,204 @@ boost::json::result_for::type tag_invoke( return message.error(); } - const auto *jvnoticeType = root.if_contains("notice_type"); - if (jvnoticeType == nullptr) + const auto *jvinnerTag = root.if_contains("notice_type"); + if (jvinnerTag == nullptr) { EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - auto noticeType = boost::json::try_value_to(*jvnoticeType); - - if (noticeType.has_error()) + auto innerTagRes = + boost::json::try_value_to(*jvinnerTag); + if (innerTagRes.has_error()) { - return noticeType.error(); + return innerTagRes.error(); } - - std::optional sub = std::nullopt; - const auto *jvsub = root.if_contains("sub"); - if (jvsub != nullptr && !jvsub->is_null()) + std::string_view innerTag = *innerTagRes; + decltype(std::declval().inner) inner; + if (innerTag == Subcription::TAG) { - auto tsub = boost::json::try_value_to(*jvsub); - - if (tsub.has_error()) + const auto *innerVal = + root.if_contains(detail::fieldFor()); + if (!innerVal) { - return tsub.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - sub = std::move(tsub.value()); + auto innerSubcription = + boost::json::try_value_to(*innerVal); + if (innerSubcription.has_error()) + { + return innerSubcription.error(); + } + inner.emplace(std::move(innerSubcription.value())); } - - std::optional resub = std::nullopt; - const auto *jvresub = root.if_contains("resub"); - if (jvresub != nullptr && !jvresub->is_null()) + else if (innerTag == Resubscription::TAG) { - auto tresub = boost::json::try_value_to(*jvresub); - - if (tresub.has_error()) + const auto *innerVal = + root.if_contains(detail::fieldFor()); + if (!innerVal) { - return tresub.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - resub = std::move(tresub.value()); + auto innerResubscription = + boost::json::try_value_to(*innerVal); + if (innerResubscription.has_error()) + { + return innerResubscription.error(); + } + inner.emplace(std::move(innerResubscription.value())); } - - std::optional subGift = std::nullopt; - const auto *jvsubGift = root.if_contains("sub_gift"); - if (jvsubGift != nullptr && !jvsubGift->is_null()) + else if (innerTag == GiftSubscription::TAG) { - auto tsubGift = boost::json::try_value_to(*jvsubGift); - - if (tsubGift.has_error()) + const auto *innerVal = + root.if_contains(detail::fieldFor()); + if (!innerVal) { - return tsubGift.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - subGift = std::move(tsubGift.value()); + auto innerGiftSubscription = + boost::json::try_value_to(*innerVal); + if (innerGiftSubscription.has_error()) + { + return innerGiftSubscription.error(); + } + inner.emplace( + std::move(innerGiftSubscription.value())); } - - std::optional communitySubGift = std::nullopt; - const auto *jvcommunitySubGift = root.if_contains("community_sub_gift"); - if (jvcommunitySubGift != nullptr && !jvcommunitySubGift->is_null()) + else if (innerTag == CommunityGiftSubscription::TAG) { - auto tcommunitySubGift = - boost::json::try_value_to( - *jvcommunitySubGift); - - if (tcommunitySubGift.has_error()) + const auto *innerVal = + root.if_contains(detail::fieldFor()); + if (!innerVal) { - return tcommunitySubGift.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - communitySubGift = std::move(tcommunitySubGift.value()); + auto innerCommunityGiftSubscription = + boost::json::try_value_to(*innerVal); + if (innerCommunityGiftSubscription.has_error()) + { + return innerCommunityGiftSubscription.error(); + } + inner.emplace( + std::move(innerCommunityGiftSubscription.value())); } - - std::optional giftPaidUpgrade = std::nullopt; - const auto *jvgiftPaidUpgrade = root.if_contains("gift_paid_upgrade"); - if (jvgiftPaidUpgrade != nullptr && !jvgiftPaidUpgrade->is_null()) + else if (innerTag == GiftPaidUpgrade::TAG) { - auto tgiftPaidUpgrade = - boost::json::try_value_to(*jvgiftPaidUpgrade); - - if (tgiftPaidUpgrade.has_error()) + const auto *innerVal = + root.if_contains(detail::fieldFor()); + if (!innerVal) { - return tgiftPaidUpgrade.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - giftPaidUpgrade = std::move(tgiftPaidUpgrade.value()); + auto innerGiftPaidUpgrade = + boost::json::try_value_to(*innerVal); + if (innerGiftPaidUpgrade.has_error()) + { + return innerGiftPaidUpgrade.error(); + } + inner.emplace(std::move(innerGiftPaidUpgrade.value())); } - - std::optional primePaidUpgrade = std::nullopt; - const auto *jvprimePaidUpgrade = root.if_contains("prime_paid_upgrade"); - if (jvprimePaidUpgrade != nullptr && !jvprimePaidUpgrade->is_null()) + else if (innerTag == PrimePaidUpgrade::TAG) { - auto tprimePaidUpgrade = - boost::json::try_value_to(*jvprimePaidUpgrade); - - if (tprimePaidUpgrade.has_error()) + const auto *innerVal = + root.if_contains(detail::fieldFor()); + if (!innerVal) { - return tprimePaidUpgrade.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - primePaidUpgrade = std::move(tprimePaidUpgrade.value()); + auto innerPrimePaidUpgrade = + boost::json::try_value_to(*innerVal); + if (innerPrimePaidUpgrade.has_error()) + { + return innerPrimePaidUpgrade.error(); + } + inner.emplace( + std::move(innerPrimePaidUpgrade.value())); } - - std::optional raid = std::nullopt; - const auto *jvraid = root.if_contains("raid"); - if (jvraid != nullptr && !jvraid->is_null()) + else if (innerTag == Raid::TAG) { - auto traid = boost::json::try_value_to(*jvraid); - - if (traid.has_error()) + const auto *innerVal = root.if_contains(detail::fieldFor()); + if (!innerVal) { - return traid.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - raid = std::move(traid.value()); + auto innerRaid = boost::json::try_value_to(*innerVal); + if (innerRaid.has_error()) + { + return innerRaid.error(); + } + inner.emplace(std::move(innerRaid.value())); } - - static_assert( - std::is_trivially_copyable_v< - std::remove_reference_t().unraid)>>); - std::optional unraid = std::nullopt; - const auto *jvunraid = root.if_contains("unraid"); - if (jvunraid != nullptr && !jvunraid->is_null()) + else if (innerTag == Unraid::TAG) { - auto tunraid = boost::json::try_value_to(*jvunraid); - - if (tunraid.has_error()) - { - return tunraid.error(); - } - unraid = tunraid.value(); + inner.emplace(); } - - std::optional payItForward = std::nullopt; - const auto *jvpayItForward = root.if_contains("pay_it_forward"); - if (jvpayItForward != nullptr && !jvpayItForward->is_null()) + else if (innerTag == PayItForward::TAG) { - auto tpayItForward = - boost::json::try_value_to(*jvpayItForward); - - if (tpayItForward.has_error()) + const auto *innerVal = + root.if_contains(detail::fieldFor()); + if (!innerVal) { - return tpayItForward.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - payItForward = std::move(tpayItForward.value()); + auto innerPayItForward = + boost::json::try_value_to(*innerVal); + if (innerPayItForward.has_error()) + { + return innerPayItForward.error(); + } + inner.emplace(std::move(innerPayItForward.value())); } - - std::optional announcement = std::nullopt; - const auto *jvannouncement = root.if_contains("announcement"); - if (jvannouncement != nullptr && !jvannouncement->is_null()) + else if (innerTag == Announcement::TAG) { - auto tannouncement = - boost::json::try_value_to(*jvannouncement); - - if (tannouncement.has_error()) + const auto *innerVal = + root.if_contains(detail::fieldFor()); + if (!innerVal) { - return tannouncement.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - announcement = std::move(tannouncement.value()); + auto innerAnnouncement = + boost::json::try_value_to(*innerVal); + if (innerAnnouncement.has_error()) + { + return innerAnnouncement.error(); + } + inner.emplace(std::move(innerAnnouncement.value())); } - - std::optional charityDonation = std::nullopt; - const auto *jvcharityDonation = root.if_contains("charity_donation"); - if (jvcharityDonation != nullptr && !jvcharityDonation->is_null()) + else if (innerTag == CharityDonation::TAG) { - auto tcharityDonation = - boost::json::try_value_to(*jvcharityDonation); - - if (tcharityDonation.has_error()) + const auto *innerVal = + root.if_contains(detail::fieldFor()); + if (!innerVal) { - return tcharityDonation.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - charityDonation = std::move(tcharityDonation.value()); + auto innerCharityDonation = + boost::json::try_value_to(*innerVal); + if (innerCharityDonation.has_error()) + { + return innerCharityDonation.error(); + } + inner.emplace(std::move(innerCharityDonation.value())); } - - static_assert(std::is_trivially_copyable_v().bitsBadgeTier)>>); - std::optional bitsBadgeTier = std::nullopt; - const auto *jvbitsBadgeTier = root.if_contains("bits_badge_tier"); - if (jvbitsBadgeTier != nullptr && !jvbitsBadgeTier->is_null()) + else if (innerTag == BitsBadgeTier::TAG) { - auto tbitsBadgeTier = - boost::json::try_value_to(*jvbitsBadgeTier); - - if (tbitsBadgeTier.has_error()) + const auto *innerVal = + root.if_contains(detail::fieldFor()); + if (!innerVal) { - return tbitsBadgeTier.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - bitsBadgeTier = tbitsBadgeTier.value(); + auto innerBitsBadgeTier = + boost::json::try_value_to(*innerVal); + if (innerBitsBadgeTier.has_error()) + { + return innerBitsBadgeTier.error(); + } + inner.emplace(innerBitsBadgeTier.value()); + } + else + { + inner.emplace(innerTag); } return Event{ @@ -1634,19 +1653,7 @@ boost::json::result_for::type tag_invoke( .systemMessage = std::move(systemMessage.value()), .messageID = std::move(messageID.value()), .message = std::move(message.value()), - .noticeType = std::move(noticeType.value()), - .sub = std::move(sub), - .resub = std::move(resub), - .subGift = std::move(subGift), - .communitySubGift = std::move(communitySubGift), - .giftPaidUpgrade = std::move(giftPaidUpgrade), - .primePaidUpgrade = std::move(primePaidUpgrade), - .raid = std::move(raid), - .unraid = unraid, - .payItForward = std::move(payItForward), - .announcement = std::move(announcement), - .charityDonation = std::move(charityDonation), - .bitsBadgeTier = bitsBadgeTier, + .inner = std::move(inner), }; } diff --git a/lib/twitch-eventsub-ws/src/generated/payloads/channel-moderate-v2.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/channel-moderate-v2.cpp index 6b9d05cf..daedf459 100644 --- a/lib/twitch-eventsub-ws/src/generated/payloads/channel-moderate-v2.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/channel-moderate-v2.cpp @@ -1,163 +1,13 @@ // WARNING: This file is automatically generated. Any changes will be lost. #include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/detail/errors.hpp" +#include "twitch-eventsub-ws/detail/variant.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/payloads/channel-moderate-v2.hpp" #include namespace chatterino::eventsub::lib::payload::channel_moderate::v2 { -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag /* tag */, - const boost::json::value &jvRoot) -{ - if (!jvRoot.is_string()) - { - EVENTSUB_BAIL_HERE(error::Kind::ExpectedString); - } - std::string_view eString(jvRoot.get_string()); - - using namespace std::string_view_literals; - if (eString == "ban"sv) - { - return Action::Ban; - } - if (eString == "timeout"sv) - { - return Action::Timeout; - } - if (eString == "unban"sv) - { - return Action::Unban; - } - if (eString == "untimeout"sv) - { - return Action::Untimeout; - } - if (eString == "clear"sv) - { - return Action::Clear; - } - if (eString == "emoteonly"sv) - { - return Action::Emoteonly; - } - if (eString == "emoteonlyoff"sv) - { - return Action::Emoteonlyoff; - } - if (eString == "followers"sv) - { - return Action::Followers; - } - if (eString == "followersoff"sv) - { - return Action::Followersoff; - } - if (eString == "uniquechat"sv) - { - return Action::Uniquechat; - } - if (eString == "uniquechatoff"sv) - { - return Action::Uniquechatoff; - } - if (eString == "slow"sv) - { - return Action::Slow; - } - if (eString == "slowoff"sv) - { - return Action::Slowoff; - } - if (eString == "subscribers"sv) - { - return Action::Subscribers; - } - if (eString == "subscribersoff"sv) - { - return Action::Subscribersoff; - } - if (eString == "unraid"sv) - { - return Action::Unraid; - } - if (eString == "delete"sv) - { - return Action::DeleteMessage; - } - if (eString == "unvip"sv) - { - return Action::Unvip; - } - if (eString == "vip"sv) - { - return Action::Vip; - } - if (eString == "raid"sv) - { - return Action::Raid; - } - if (eString == "add_blocked_term"sv) - { - return Action::AddBlockedTerm; - } - if (eString == "add_permitted_term"sv) - { - return Action::AddPermittedTerm; - } - if (eString == "remove_blocked_term"sv) - { - return Action::RemoveBlockedTerm; - } - if (eString == "remove_permitted_term"sv) - { - return Action::RemovePermittedTerm; - } - if (eString == "mod"sv) - { - return Action::Mod; - } - if (eString == "unmod"sv) - { - return Action::Unmod; - } - if (eString == "approve_unban_request"sv) - { - return Action::ApproveUnbanRequest; - } - if (eString == "deny_unban_request"sv) - { - return Action::DenyUnbanRequest; - } - if (eString == "warn"sv) - { - return Action::Warn; - } - if (eString == "shared_chat_ban"sv) - { - return Action::SharedChatBan; - } - if (eString == "shared_chat_timeout"sv) - { - return Action::SharedChatTimeout; - } - if (eString == "shared_chat_unban"sv) - { - return Action::SharedChatUnban; - } - if (eString == "shared_chat_untimeout"sv) - { - return Action::SharedChatUntimeout; - } - if (eString == "shared_chat_delete"sv) - { - return Action::SharedChatDelete; - } - - EVENTSUB_BAIL_HERE(error::Kind::UnknownEnumValue); -} - boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag /* tag */, const boost::json::value &jvRoot) @@ -191,6 +41,13 @@ boost::json::result_for::type tag_invoke( }; } +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return FollowersOff{}; +} + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag /* tag */, const boost::json::value &jvRoot) @@ -221,6 +78,13 @@ boost::json::result_for::type tag_invoke( }; } +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return SlowOff{}; +} + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag /* tag */, const boost::json::value &jvRoot) @@ -515,6 +379,13 @@ boost::json::result_for::type tag_invoke( }; } +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return SharedChatBan{}; +} + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag /* tag */, const boost::json::value &jvRoot) @@ -571,6 +442,13 @@ boost::json::result_for::type tag_invoke( }; } +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return SharedChatUnban{}; +} + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag /* tag */, const boost::json::value &jvRoot) @@ -655,6 +533,13 @@ boost::json::result_for::type tag_invoke( }; } +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return SharedChatTimeout{}; +} + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag /* tag */, const boost::json::value &jvRoot) @@ -711,6 +596,13 @@ boost::json::result_for::type tag_invoke( }; } +boost::json::result_for::type + tag_invoke(boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return SharedChatUntimeout{}; +} + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag /* tag */, const boost::json::value &jvRoot) @@ -923,6 +815,13 @@ boost::json::result_for::type tag_invoke( }; } +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return SharedChatDelete{}; +} + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag /* tag */, const boost::json::value &jvRoot) @@ -993,6 +892,34 @@ boost::json::result_for::type tag_invoke( }; } +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return AddBlockedTerm{}; +} + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return AddPermittedTerm{}; +} + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return RemoveBlockedTerm{}; +} + +boost::json::result_for::type + tag_invoke(boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return RemovePermittedTerm{}; +} + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag /* tag */, const boost::json::value &jvRoot) @@ -1080,6 +1007,20 @@ boost::json::result_for::type tag_invoke( }; } +boost::json::result_for::type + tag_invoke(boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return ApproveUnbanRequest{}; +} + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return DenyUnbanRequest{}; +} + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag /* tag */, const boost::json::value &jvRoot) @@ -1163,6 +1104,55 @@ boost::json::result_for::type tag_invoke( }; } +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return Clear{}; +} + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return EmoteOnly{}; +} + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return EmoteOnlyOff{}; +} + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return Uniquechat{}; +} + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return UniquechatOff{}; +} + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return Subscribers{}; +} + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag /* tag */, + const boost::json::value & /* jvRoot */) +{ + return SubscribersOff{}; +} + boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag /* tag */, const boost::json::value &jvRoot) @@ -1311,293 +1301,299 @@ boost::json::result_for::type tag_invoke( return moderatorUserName.error(); } - static_assert( - std::is_trivially_copyable_v< - std::remove_reference_t().action)>>); - const auto *jvaction = root.if_contains("action"); - if (jvaction == nullptr) + const auto *jvactionTag = root.if_contains("action"); + if (jvactionTag == nullptr) { EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - auto action = boost::json::try_value_to(*jvaction); - - if (action.has_error()) + auto actionTagRes = + boost::json::try_value_to(*jvactionTag); + if (actionTagRes.has_error()) { - return action.error(); + return actionTagRes.error(); } - - static_assert(std::is_trivially_copyable_v().followers)>>); - std::optional followers = std::nullopt; - const auto *jvfollowers = root.if_contains("followers"); - if (jvfollowers != nullptr && !jvfollowers->is_null()) + std::string_view actionTag = *actionTagRes; + decltype(std::declval().action) action; + if (actionTag == Ban::TAG) { - auto tfollowers = boost::json::try_value_to(*jvfollowers); - - if (tfollowers.has_error()) + const auto *actionVal = root.if_contains(detail::fieldFor()); + if (!actionVal) { - return tfollowers.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - followers = tfollowers.value(); + auto actionBan = boost::json::try_value_to(*actionVal); + if (actionBan.has_error()) + { + return actionBan.error(); + } + action.emplace(std::move(actionBan.value())); } - - static_assert( - std::is_trivially_copyable_v< - std::remove_reference_t().slow)>>); - std::optional slow = std::nullopt; - const auto *jvslow = root.if_contains("slow"); - if (jvslow != nullptr && !jvslow->is_null()) + else if (actionTag == Timeout::TAG) { - auto tslow = boost::json::try_value_to(*jvslow); - - if (tslow.has_error()) + const auto *actionVal = root.if_contains(detail::fieldFor()); + if (!actionVal) { - return tslow.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - slow = tslow.value(); + auto actionTimeout = boost::json::try_value_to(*actionVal); + if (actionTimeout.has_error()) + { + return actionTimeout.error(); + } + action.emplace(std::move(actionTimeout.value())); } - - std::optional vip = std::nullopt; - const auto *jvvip = root.if_contains("vip"); - if (jvvip != nullptr && !jvvip->is_null()) + else if (actionTag == Unban::TAG) { - auto tvip = boost::json::try_value_to(*jvvip); - - if (tvip.has_error()) + const auto *actionVal = root.if_contains(detail::fieldFor()); + if (!actionVal) { - return tvip.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - vip = std::move(tvip.value()); + auto actionUnban = boost::json::try_value_to(*actionVal); + if (actionUnban.has_error()) + { + return actionUnban.error(); + } + action.emplace(std::move(actionUnban.value())); } - - std::optional unvip = std::nullopt; - const auto *jvunvip = root.if_contains("unvip"); - if (jvunvip != nullptr && !jvunvip->is_null()) + else if (actionTag == Untimeout::TAG) { - auto tunvip = boost::json::try_value_to(*jvunvip); - - if (tunvip.has_error()) + const auto *actionVal = root.if_contains(detail::fieldFor()); + if (!actionVal) { - return tunvip.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - unvip = std::move(tunvip.value()); + auto actionUntimeout = boost::json::try_value_to(*actionVal); + if (actionUntimeout.has_error()) + { + return actionUntimeout.error(); + } + action.emplace(std::move(actionUntimeout.value())); } - - std::optional unmod = std::nullopt; - const auto *jvunmod = root.if_contains("unmod"); - if (jvunmod != nullptr && !jvunmod->is_null()) + else if (actionTag == Clear::TAG) { - auto tunmod = boost::json::try_value_to(*jvunmod); - - if (tunmod.has_error()) - { - return tunmod.error(); - } - unmod = std::move(tunmod.value()); + action.emplace(); } - - std::optional ban = std::nullopt; - const auto *jvban = root.if_contains("ban"); - if (jvban != nullptr && !jvban->is_null()) + else if (actionTag == EmoteOnly::TAG) { - auto tban = boost::json::try_value_to(*jvban); - - if (tban.has_error()) - { - return tban.error(); - } - ban = std::move(tban.value()); + action.emplace(); } - - std::optional unban = std::nullopt; - const auto *jvunban = root.if_contains("unban"); - if (jvunban != nullptr && !jvunban->is_null()) + else if (actionTag == EmoteOnlyOff::TAG) { - auto tunban = boost::json::try_value_to(*jvunban); - - if (tunban.has_error()) - { - return tunban.error(); - } - unban = std::move(tunban.value()); + action.emplace(); } - - std::optional timeout = std::nullopt; - const auto *jvtimeout = root.if_contains("timeout"); - if (jvtimeout != nullptr && !jvtimeout->is_null()) + else if (actionTag == Followers::TAG) { - auto ttimeout = boost::json::try_value_to(*jvtimeout); - - if (ttimeout.has_error()) + const auto *actionVal = root.if_contains(detail::fieldFor()); + if (!actionVal) { - return ttimeout.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - timeout = std::move(ttimeout.value()); + auto actionFollowers = boost::json::try_value_to(*actionVal); + if (actionFollowers.has_error()) + { + return actionFollowers.error(); + } + action.emplace(actionFollowers.value()); } - - std::optional untimeout = std::nullopt; - const auto *jvuntimeout = root.if_contains("untimeout"); - if (jvuntimeout != nullptr && !jvuntimeout->is_null()) + else if (actionTag == FollowersOff::TAG) { - auto tuntimeout = boost::json::try_value_to(*jvuntimeout); - - if (tuntimeout.has_error()) - { - return tuntimeout.error(); - } - untimeout = std::move(tuntimeout.value()); + action.emplace(); } - - std::optional raid = std::nullopt; - const auto *jvraid = root.if_contains("raid"); - if (jvraid != nullptr && !jvraid->is_null()) + else if (actionTag == Uniquechat::TAG) { - auto traid = boost::json::try_value_to(*jvraid); - - if (traid.has_error()) - { - return traid.error(); - } - raid = std::move(traid.value()); + action.emplace(); } - - std::optional unraid = std::nullopt; - const auto *jvunraid = root.if_contains("unraid"); - if (jvunraid != nullptr && !jvunraid->is_null()) + else if (actionTag == UniquechatOff::TAG) { - auto tunraid = boost::json::try_value_to(*jvunraid); - - if (tunraid.has_error()) - { - return tunraid.error(); - } - unraid = std::move(tunraid.value()); + action.emplace(); } - - std::optional deleteMessage = std::nullopt; - const auto *jvdeleteMessage = root.if_contains("delete"); - if (jvdeleteMessage != nullptr && !jvdeleteMessage->is_null()) + else if (actionTag == Slow::TAG) { - auto tdeleteMessage = - boost::json::try_value_to(*jvdeleteMessage); - - if (tdeleteMessage.has_error()) + const auto *actionVal = root.if_contains(detail::fieldFor()); + if (!actionVal) { - return tdeleteMessage.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - deleteMessage = std::move(tdeleteMessage.value()); + auto actionSlow = boost::json::try_value_to(*actionVal); + if (actionSlow.has_error()) + { + return actionSlow.error(); + } + action.emplace(actionSlow.value()); } - - std::optional automodTerms = std::nullopt; - const auto *jvautomodTerms = root.if_contains("automod_terms"); - if (jvautomodTerms != nullptr && !jvautomodTerms->is_null()) + else if (actionTag == SlowOff::TAG) { - auto tautomodTerms = - boost::json::try_value_to(*jvautomodTerms); - - if (tautomodTerms.has_error()) - { - return tautomodTerms.error(); - } - automodTerms = std::move(tautomodTerms.value()); + action.emplace(); } - - std::optional unbanRequest = std::nullopt; - const auto *jvunbanRequest = root.if_contains("unban_request"); - if (jvunbanRequest != nullptr && !jvunbanRequest->is_null()) + else if (actionTag == Subscribers::TAG) { - auto tunbanRequest = - boost::json::try_value_to(*jvunbanRequest); - - if (tunbanRequest.has_error()) - { - return tunbanRequest.error(); - } - unbanRequest = std::move(tunbanRequest.value()); + action.emplace(); } - - std::optional warn = std::nullopt; - const auto *jvwarn = root.if_contains("warn"); - if (jvwarn != nullptr && !jvwarn->is_null()) + else if (actionTag == SubscribersOff::TAG) { - auto twarn = boost::json::try_value_to(*jvwarn); - - if (twarn.has_error()) - { - return twarn.error(); - } - warn = std::move(twarn.value()); + action.emplace(); } - - std::optional sharedChatBan = std::nullopt; - const auto *jvsharedChatBan = root.if_contains("shared_chat_ban"); - if (jvsharedChatBan != nullptr && !jvsharedChatBan->is_null()) + else if (actionTag == Unraid::TAG) { - auto tsharedChatBan = boost::json::try_value_to(*jvsharedChatBan); - - if (tsharedChatBan.has_error()) + const auto *actionVal = root.if_contains(detail::fieldFor()); + if (!actionVal) { - return tsharedChatBan.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - sharedChatBan = std::move(tsharedChatBan.value()); + auto actionUnraid = boost::json::try_value_to(*actionVal); + if (actionUnraid.has_error()) + { + return actionUnraid.error(); + } + action.emplace(std::move(actionUnraid.value())); } - - std::optional sharedChatUnban = std::nullopt; - const auto *jvsharedChatUnban = root.if_contains("shared_chat_unban"); - if (jvsharedChatUnban != nullptr && !jvsharedChatUnban->is_null()) + else if (actionTag == Delete::TAG) { - auto tsharedChatUnban = - boost::json::try_value_to(*jvsharedChatUnban); - - if (tsharedChatUnban.has_error()) + const auto *actionVal = root.if_contains(detail::fieldFor()); + if (!actionVal) { - return tsharedChatUnban.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - sharedChatUnban = std::move(tsharedChatUnban.value()); + auto actionDelete = boost::json::try_value_to(*actionVal); + if (actionDelete.has_error()) + { + return actionDelete.error(); + } + action.emplace(std::move(actionDelete.value())); } - - std::optional sharedChatTimeout = std::nullopt; - const auto *jvsharedChatTimeout = root.if_contains("shared_chat_timeout"); - if (jvsharedChatTimeout != nullptr && !jvsharedChatTimeout->is_null()) + else if (actionTag == Unvip::TAG) { - auto tsharedChatTimeout = - boost::json::try_value_to(*jvsharedChatTimeout); - - if (tsharedChatTimeout.has_error()) + const auto *actionVal = root.if_contains(detail::fieldFor()); + if (!actionVal) { - return tsharedChatTimeout.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - sharedChatTimeout = std::move(tsharedChatTimeout.value()); + auto actionUnvip = boost::json::try_value_to(*actionVal); + if (actionUnvip.has_error()) + { + return actionUnvip.error(); + } + action.emplace(std::move(actionUnvip.value())); } - - std::optional sharedChatUntimeout = std::nullopt; - const auto *jvsharedChatUntimeout = - root.if_contains("shared_chat_untimeout"); - if (jvsharedChatUntimeout != nullptr && !jvsharedChatUntimeout->is_null()) + else if (actionTag == Vip::TAG) { - auto tsharedChatUntimeout = - boost::json::try_value_to(*jvsharedChatUntimeout); - - if (tsharedChatUntimeout.has_error()) + const auto *actionVal = root.if_contains(detail::fieldFor()); + if (!actionVal) { - return tsharedChatUntimeout.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - sharedChatUntimeout = std::move(tsharedChatUntimeout.value()); + auto actionVip = boost::json::try_value_to(*actionVal); + if (actionVip.has_error()) + { + return actionVip.error(); + } + action.emplace(std::move(actionVip.value())); } - - std::optional sharedChatDelete = std::nullopt; - const auto *jvsharedChatDelete = root.if_contains("shared_chat_delete"); - if (jvsharedChatDelete != nullptr && !jvsharedChatDelete->is_null()) + else if (actionTag == Raid::TAG) { - auto tsharedChatDelete = - boost::json::try_value_to(*jvsharedChatDelete); - - if (tsharedChatDelete.has_error()) + const auto *actionVal = root.if_contains(detail::fieldFor()); + if (!actionVal) { - return tsharedChatDelete.error(); + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - sharedChatDelete = std::move(tsharedChatDelete.value()); + auto actionRaid = boost::json::try_value_to(*actionVal); + if (actionRaid.has_error()) + { + return actionRaid.error(); + } + action.emplace(std::move(actionRaid.value())); + } + else if (actionTag == AddBlockedTerm::TAG) + { + action.emplace(); + } + else if (actionTag == AddPermittedTerm::TAG) + { + action.emplace(); + } + else if (actionTag == RemoveBlockedTerm::TAG) + { + action.emplace(); + } + else if (actionTag == RemovePermittedTerm::TAG) + { + action.emplace(); + } + else if (actionTag == Mod::TAG) + { + const auto *actionVal = root.if_contains(detail::fieldFor()); + if (!actionVal) + { + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); + } + auto actionMod = boost::json::try_value_to(*actionVal); + if (actionMod.has_error()) + { + return actionMod.error(); + } + action.emplace(std::move(actionMod.value())); + } + else if (actionTag == Unmod::TAG) + { + const auto *actionVal = root.if_contains(detail::fieldFor()); + if (!actionVal) + { + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); + } + auto actionUnmod = boost::json::try_value_to(*actionVal); + if (actionUnmod.has_error()) + { + return actionUnmod.error(); + } + action.emplace(std::move(actionUnmod.value())); + } + else if (actionTag == ApproveUnbanRequest::TAG) + { + action.emplace(); + } + else if (actionTag == DenyUnbanRequest::TAG) + { + action.emplace(); + } + else if (actionTag == Warn::TAG) + { + const auto *actionVal = root.if_contains(detail::fieldFor()); + if (!actionVal) + { + EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); + } + auto actionWarn = boost::json::try_value_to(*actionVal); + if (actionWarn.has_error()) + { + return actionWarn.error(); + } + action.emplace(std::move(actionWarn.value())); + } + else if (actionTag == SharedChatBan::TAG) + { + action.emplace(); + } + else if (actionTag == SharedChatTimeout::TAG) + { + action.emplace(); + } + else if (actionTag == SharedChatUnban::TAG) + { + action.emplace(); + } + else if (actionTag == SharedChatUntimeout::TAG) + { + action.emplace(); + } + else if (actionTag == SharedChatDelete::TAG) + { + action.emplace(); + } + else + { + action.emplace(actionTag); } return Event{ @@ -1610,27 +1606,7 @@ boost::json::result_for::type tag_invoke( .moderatorUserID = std::move(moderatorUserID.value()), .moderatorUserLogin = std::move(moderatorUserLogin.value()), .moderatorUserName = std::move(moderatorUserName.value()), - .action = action.value(), - .followers = followers, - .slow = slow, - .vip = std::move(vip), - .unvip = std::move(unvip), - .unmod = std::move(unmod), - .ban = std::move(ban), - .unban = std::move(unban), - .timeout = std::move(timeout), - .untimeout = std::move(untimeout), - .raid = std::move(raid), - .unraid = std::move(unraid), - .deleteMessage = std::move(deleteMessage), - .automodTerms = std::move(automodTerms), - .unbanRequest = std::move(unbanRequest), - .warn = std::move(warn), - .sharedChatBan = std::move(sharedChatBan), - .sharedChatUnban = std::move(sharedChatUnban), - .sharedChatTimeout = std::move(sharedChatTimeout), - .sharedChatUntimeout = std::move(sharedChatUntimeout), - .sharedChatDelete = std::move(sharedChatDelete), + .action = std::move(action), }; } diff --git a/lib/twitch-eventsub-ws/src/generated/payloads/channel-update-v1.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/channel-update-v1.cpp index c76a1623..0b699ffe 100644 --- a/lib/twitch-eventsub-ws/src/generated/payloads/channel-update-v1.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/channel-update-v1.cpp @@ -1,6 +1,7 @@ // WARNING: This file is automatically generated. Any changes will be lost. #include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/detail/errors.hpp" +#include "twitch-eventsub-ws/detail/variant.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/payloads/channel-update-v1.hpp" #include diff --git a/lib/twitch-eventsub-ws/src/generated/payloads/session-welcome.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/session-welcome.cpp index 74ad7d49..4347b6e4 100644 --- a/lib/twitch-eventsub-ws/src/generated/payloads/session-welcome.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/session-welcome.cpp @@ -1,6 +1,7 @@ // WARNING: This file is automatically generated. Any changes will be lost. #include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/detail/errors.hpp" +#include "twitch-eventsub-ws/detail/variant.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/payloads/session-welcome.hpp" #include diff --git a/lib/twitch-eventsub-ws/src/generated/payloads/stream-offline-v1.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/stream-offline-v1.cpp index ad5171d6..c3e095e0 100644 --- a/lib/twitch-eventsub-ws/src/generated/payloads/stream-offline-v1.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/stream-offline-v1.cpp @@ -1,6 +1,7 @@ // WARNING: This file is automatically generated. Any changes will be lost. #include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/detail/errors.hpp" +#include "twitch-eventsub-ws/detail/variant.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/payloads/stream-offline-v1.hpp" #include diff --git a/lib/twitch-eventsub-ws/src/generated/payloads/stream-online-v1.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/stream-online-v1.cpp index f8553b95..6be3b350 100644 --- a/lib/twitch-eventsub-ws/src/generated/payloads/stream-online-v1.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/stream-online-v1.cpp @@ -1,6 +1,7 @@ // WARNING: This file is automatically generated. Any changes will be lost. #include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/detail/errors.hpp" +#include "twitch-eventsub-ws/detail/variant.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/payloads/stream-online-v1.hpp" #include diff --git a/lib/twitch-eventsub-ws/src/generated/payloads/subscription.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/subscription.cpp index b36854ef..293c0b72 100644 --- a/lib/twitch-eventsub-ws/src/generated/payloads/subscription.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/subscription.cpp @@ -1,6 +1,7 @@ // WARNING: This file is automatically generated. Any changes will be lost. #include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/detail/errors.hpp" +#include "twitch-eventsub-ws/detail/variant.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/payloads/subscription.hpp" #include diff --git a/src/providers/twitch/eventsub/Connection.cpp b/src/providers/twitch/eventsub/Connection.cpp index d3d2f003..04e27a60 100644 --- a/src/providers/twitch/eventsub/Connection.cpp +++ b/src/providers/twitch/eventsub/Connection.cpp @@ -142,8 +142,6 @@ void Connection::onChannelModerate( { (void)metadata; - using lib::payload::channel_moderate::v2::Action; - auto channelPtr = getApp()->getTwitch()->getChannelOrEmpty( payload.event.broadcasterUserLogin.qt()); if (channelPtr->isEmpty()) @@ -165,74 +163,29 @@ void Connection::onChannelModerate( const auto now = QDateTime::currentDateTime(); - switch (payload.event.action) - { - case Action::Vip: { - const auto &oAction = payload.event.vip; - - if (!oAction.has_value()) + std::visit( + [&](auto &&oAction) { + using Action = std::remove_cvref_t; + if constexpr (std::is_same_v< + Action, lib::payload::channel_moderate::v2::Vip>) { - qCWarning(LOG) << "VIP action type had no VIP action body"; - return; + auto msg = makeVipMessage(channel, now, payload.event, oAction); + runInGuiThread([channel, msg] { + channel->addMessage(msg, MessageContext::Original); + }); } - auto msg = - makeVipMessage(channel, now, payload.event, oAction.value()); - runInGuiThread([channel, msg] { - channel->addMessage(msg, MessageContext::Original); - }); - } - break; - - case Action::Unvip: { - const auto &oAction = payload.event.unvip; - - if (!oAction.has_value()) + else if constexpr (std::is_same_v< + Action, + lib::payload::channel_moderate::v2::Unvip>) { - qCWarning(LOG) << "UnVIP action type had no UnVIP action body"; - return; + auto msg = + makeUnvipMessage(channel, now, payload.event, oAction); + runInGuiThread([channel, msg] { + channel->addMessage(msg, MessageContext::Original); + }); } - auto msg = - makeUnvipMessage(channel, now, payload.event, oAction.value()); - runInGuiThread([channel, msg] { - channel->addMessage(msg, MessageContext::Original); - }); - } - break; - - case Action::Ban: - case Action::Timeout: - case Action::Unban: - case Action::Untimeout: - case Action::Clear: - case Action::Emoteonly: - case Action::Emoteonlyoff: - case Action::Followers: - case Action::Followersoff: - case Action::Uniquechat: - case Action::Uniquechatoff: - case Action::Slow: - case Action::Slowoff: - case Action::Subscribers: - case Action::Subscribersoff: - case Action::Unraid: - case Action::DeleteMessage: - case Action::Raid: - case Action::AddBlockedTerm: - case Action::AddPermittedTerm: - case Action::RemoveBlockedTerm: - case Action::RemovePermittedTerm: - case Action::Mod: - case Action::Unmod: - case Action::ApproveUnbanRequest: - case Action::DenyUnbanRequest: - case Action::Warn: - case Action::SharedChatBan: - case Action::SharedChatTimeout: - case Action::SharedChatUnban: - case Action::SharedChatUntimeout: - case Action::SharedChatDelete: - break; - } + }, + payload.event.action); } QString Connection::getSessionID() const