From 7fee3f7e375a099b5207c215389343f8f1d95bf2 Mon Sep 17 00:00:00 2001 From: nerix Date: Sat, 22 Feb 2025 20:55:20 +0100 Subject: [PATCH] chore(eventsub): add operator== and indicator for shared chat (#5974) --- CHANGELOG.md | 2 +- .../payloads/channel-moderate-v2.hpp | 8 +-- .../include/twitch-eventsub-ws/string.hpp | 18 +++++++ lib/twitch-eventsub-ws/src/CMakeLists.txt | 1 + .../payloads/channel-moderate-v2.cpp | 16 +++--- .../src/payloads/channel-moderate-v2.cpp | 11 ++++ lib/twitch-eventsub-ws/tests/src/string.cpp | 52 +++++++++++++++++++ 7 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 lib/twitch-eventsub-ws/src/payloads/channel-moderate-v2.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index c264e761..a083d631 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,7 @@ - Bugfix: Fixed channel point redemptions with messages not showing up if PubSub is disconnected. (#5948) - Bugfix: Fixed the input font not immediately updating when zooming in/out. (#5960) - Dev: Subscriptions to PubSub channel points redemption topics now use no auth token, making it continue to work during PubSub shutdown. (#5947) -- Dev: Add initial experimental EventSub support. (#5837, #5895, #5897, #5904, #5910, #5903, #5915, #5916, #5930, #5935, #5932, #5943, #5952, #5953, #5968, #5973) +- Dev: Add initial experimental EventSub support. (#5837, #5895, #5897, #5904, #5910, #5903, #5915, #5916, #5930, #5935, #5932, #5943, #5952, #5953, #5968, #5973, #5974) - Dev: Remove unneeded platform specifier for toasts. (#5914) - Dev: Highlight checks now use non-capturing groups for the boundaries. (#5784) - Dev: Removed unused PubSub whisper code. (#5898) 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 98d0b47d..c64df994 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 @@ -389,11 +389,11 @@ struct Event { String broadcasterUserName; /// For Shared Chat events, the user ID (e.g. 117166826) of the user who's channel the event took place in - std::optional sourceBroadcasterUserID; + std::optional sourceBroadcasterUserID; /// For Shared Chat events, the user Login (e.g. testaccount_420) of the user who's channel the event took place in - std::optional sourceBroadcasterUserLogin; + std::optional sourceBroadcasterUserLogin; /// For Shared Chat events, the user Name (e.g. 테스트계정420) of the user who's channel the event took place in - std::optional sourceBroadcasterUserName; + std::optional sourceBroadcasterUserName; /// User ID (e.g. 117166826) of the user who took the action String moderatorUserID; @@ -439,6 +439,8 @@ struct Event { SharedChatDelete, // std::string> action; + + bool isFromSharedChat() const noexcept; }; struct Payload { diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/string.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/string.hpp index 7fe37ee3..1c5171df 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/string.hpp +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/string.hpp @@ -152,6 +152,24 @@ struct String { return (this->flags & (ALLOC_BIT | QT_BIT)) == 0; } + // note: because we're using C++ 20, the reversed operator + // (e.g. QAnyStringView == String) is automatically "synthesized". + bool operator==(const QAnyStringView &other) const noexcept + { + return this->view() == other; + } + + bool operator==(const String &other) const noexcept + { + return this->view() == other.view(); + } + + template // weak overload + bool operator==(const std::string_view &other) const noexcept + { + return this->view() == other; + } + private: static constexpr size_t QT_BIT = 1ULL << (sizeof(size_t) * 8 - 1); static constexpr size_t ALLOC_BIT = 1ULL << (sizeof(size_t) * 8 - 2); diff --git a/lib/twitch-eventsub-ws/src/CMakeLists.txt b/lib/twitch-eventsub-ws/src/CMakeLists.txt index 79b9d918..d5b65cfa 100644 --- a/lib/twitch-eventsub-ws/src/CMakeLists.txt +++ b/lib/twitch-eventsub-ws/src/CMakeLists.txt @@ -28,6 +28,7 @@ set(SOURCE_FILES # Subscription types (only additional functions) payloads/channel-ban-v1.cpp + payloads/channel-moderate-v2.cpp # Add your new subscription type source file above this line ${eventsub_generated_sources} 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 f3ecffc0..a60e3477 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 @@ -1215,14 +1215,16 @@ boost::json::result_for::type tag_invoke( return broadcasterUserName.error(); } - std::optional sourceBroadcasterUserID = std::nullopt; + std::optional sourceBroadcasterUserID = + std::nullopt; const auto *jvsourceBroadcasterUserID = root.if_contains("source_broadcaster_user_id"); if (jvsourceBroadcasterUserID != nullptr && !jvsourceBroadcasterUserID->is_null()) { auto tsourceBroadcasterUserID = - boost::json::try_value_to(*jvsourceBroadcasterUserID); + boost::json::try_value_to( + *jvsourceBroadcasterUserID); if (tsourceBroadcasterUserID.has_error()) { @@ -1231,14 +1233,15 @@ boost::json::result_for::type tag_invoke( sourceBroadcasterUserID = std::move(tsourceBroadcasterUserID.value()); } - std::optional sourceBroadcasterUserLogin = std::nullopt; + std::optional + sourceBroadcasterUserLogin = std::nullopt; const auto *jvsourceBroadcasterUserLogin = root.if_contains("source_broadcaster_user_login"); if (jvsourceBroadcasterUserLogin != nullptr && !jvsourceBroadcasterUserLogin->is_null()) { auto tsourceBroadcasterUserLogin = - boost::json::try_value_to( + boost::json::try_value_to( *jvsourceBroadcasterUserLogin); if (tsourceBroadcasterUserLogin.has_error()) @@ -1249,14 +1252,15 @@ boost::json::result_for::type tag_invoke( std::move(tsourceBroadcasterUserLogin.value()); } - std::optional sourceBroadcasterUserName = std::nullopt; + std::optional sourceBroadcasterUserName = + std::nullopt; const auto *jvsourceBroadcasterUserName = root.if_contains("source_broadcaster_user_name"); if (jvsourceBroadcasterUserName != nullptr && !jvsourceBroadcasterUserName->is_null()) { auto tsourceBroadcasterUserName = - boost::json::try_value_to( + boost::json::try_value_to( *jvsourceBroadcasterUserName); if (tsourceBroadcasterUserName.has_error()) diff --git a/lib/twitch-eventsub-ws/src/payloads/channel-moderate-v2.cpp b/lib/twitch-eventsub-ws/src/payloads/channel-moderate-v2.cpp new file mode 100644 index 00000000..7860106e --- /dev/null +++ b/lib/twitch-eventsub-ws/src/payloads/channel-moderate-v2.cpp @@ -0,0 +1,11 @@ +#include "twitch-eventsub-ws/payloads/channel-moderate-v2.hpp" + +namespace chatterino::eventsub::lib::payload::channel_moderate::v2 { + +bool Event::isFromSharedChat() const noexcept +{ + return this->sourceBroadcasterUserID && this->sourceBroadcasterUserLogin && + this->sourceBroadcasterUserName && + *this->sourceBroadcasterUserID != this->broadcasterUserID; +} +} // namespace chatterino::eventsub::lib::payload::channel_moderate::v2 diff --git a/lib/twitch-eventsub-ws/tests/src/string.cpp b/lib/twitch-eventsub-ws/tests/src/string.cpp index fa913103..b1f23c49 100644 --- a/lib/twitch-eventsub-ws/tests/src/string.cpp +++ b/lib/twitch-eventsub-ws/tests/src/string.cpp @@ -515,3 +515,55 @@ TEST(String, QtLifetime) } ASSERT_TRUE(qt.isDetached()); } + +TEST(String, Equals) +{ + String empty; + + // This is using ASSERT_TRUE instead of ASSERT_EQ to be able to view/go to + // the chosen operator== in editors (with clangd for example). + ASSERT_TRUE(empty == empty); + ASSERT_TRUE(empty == ""); + ASSERT_TRUE(empty == u""); + ASSERT_TRUE(empty == std::string_view{}); + ASSERT_TRUE(empty == QAnyStringView()); + ASSERT_TRUE(empty == String()); + ASSERT_TRUE("" == empty); + ASSERT_TRUE(u"" == empty); + + String longestSso(LONGEST_SSO); + String longestSsoQt(LONGEST_SSO); + ASSERT_EQ(longestSsoQt.qt(), LONGEST_SSO); + + ASSERT_TRUE(longestSso == longestSso); + ASSERT_TRUE(longestSsoQt == longestSsoQt); + ASSERT_TRUE(longestSso == longestSsoQt); + ASSERT_TRUE(longestSso == LONGEST_SSO); + ASSERT_TRUE(longestSsoQt == LONGEST_SSO); + ASSERT_TRUE(longestSso == String(LONGEST_SSO)); + ASSERT_TRUE(longestSsoQt == String(LONGEST_SSO)); + + String tooLong(TOO_LONG); + String tooLongQt(TOO_LONG); + ASSERT_EQ(tooLongQt.qt(), TOO_LONG); + + ASSERT_TRUE(tooLong == tooLong); + ASSERT_TRUE(tooLongQt == tooLongQt); + ASSERT_TRUE(tooLong == tooLongQt); + ASSERT_TRUE(tooLong == TOO_LONG); + ASSERT_TRUE(tooLongQt == TOO_LONG); + ASSERT_TRUE(tooLong == String(TOO_LONG)); + ASSERT_TRUE(tooLongQt == String(TOO_LONG)); + + String reallyLong(REALLY_LONG); + String reallyLongQt(REALLY_LONG); + ASSERT_EQ(reallyLongQt.qt(), REALLY_LONG); + + ASSERT_TRUE(reallyLong == reallyLong); + ASSERT_TRUE(reallyLongQt == reallyLongQt); + ASSERT_TRUE(reallyLong == reallyLongQt); + ASSERT_TRUE(reallyLong == REALLY_LONG); + ASSERT_TRUE(reallyLongQt == REALLY_LONG); + ASSERT_TRUE(reallyLong == String(REALLY_LONG)); + ASSERT_TRUE(reallyLongQt == String(REALLY_LONG)); +}