From 0c74ab17ed41dcb2a176bfebf900347c802a8277 Mon Sep 17 00:00:00 2001 From: nerix Date: Sat, 4 Feb 2023 13:42:52 +0100 Subject: [PATCH] Refactor 7TV EventAPI (#4342) * refactor: remove file prefix * refactor: put eventapi into a namespace * refactor: support multiple conditions * fix: handle ack * docs: add changelog entry * fix: unsubscribe in destructor * fix: naming of parameter * fix: namespaces * chore: casing * fix: missing namespace --------- Co-authored-by: pajlada --- CHANGELOG.md | 1 + src/Application.cpp | 4 +- src/CMakeLists.txt | 16 +- src/providers/seventv/SeventvEmotes.cpp | 13 +- src/providers/seventv/SeventvEmotes.hpp | 15 +- src/providers/seventv/SeventvEventAPI.cpp | 277 +++++++++--------- src/providers/seventv/SeventvEventAPI.hpp | 36 ++- .../{SeventvEventAPIClient.cpp => Client.cpp} | 29 +- .../{SeventvEventAPIClient.hpp => Client.hpp} | 22 +- ...ventvEventAPIDispatch.cpp => Dispatch.cpp} | 39 ++- src/providers/seventv/eventapi/Dispatch.hpp | 70 +++++ src/providers/seventv/eventapi/Message.cpp | 11 + ...SeventvEventAPIMessage.hpp => Message.hpp} | 19 +- .../eventapi/SeventvEventAPIDispatch.hpp | 72 ----- .../eventapi/SeventvEventAPIMessage.cpp | 11 - .../eventapi/SeventvEventAPISubscription.cpp | 80 ----- .../eventapi/SeventvEventAPISubscription.hpp | 76 ----- .../seventv/eventapi/Subscription.cpp | 105 +++++++ .../seventv/eventapi/Subscription.hpp | 106 +++++++ src/providers/twitch/TwitchChannel.cpp | 31 +- src/providers/twitch/TwitchChannel.hpp | 23 +- src/providers/twitch/TwitchIrcServer.cpp | 2 +- tests/src/SeventvEventAPI.cpp | 15 +- 23 files changed, 580 insertions(+), 493 deletions(-) rename src/providers/seventv/eventapi/{SeventvEventAPIClient.cpp => Client.cpp} (64%) rename src/providers/seventv/eventapi/{SeventvEventAPIClient.hpp => Client.hpp} (55%) rename src/providers/seventv/eventapi/{SeventvEventAPIDispatch.cpp => Dispatch.cpp} (62%) create mode 100644 src/providers/seventv/eventapi/Dispatch.hpp create mode 100644 src/providers/seventv/eventapi/Message.cpp rename src/providers/seventv/eventapi/{SeventvEventAPIMessage.hpp => Message.hpp} (50%) delete mode 100644 src/providers/seventv/eventapi/SeventvEventAPIDispatch.hpp delete mode 100644 src/providers/seventv/eventapi/SeventvEventAPIMessage.cpp delete mode 100644 src/providers/seventv/eventapi/SeventvEventAPISubscription.cpp delete mode 100644 src/providers/seventv/eventapi/SeventvEventAPISubscription.hpp create mode 100644 src/providers/seventv/eventapi/Subscription.cpp create mode 100644 src/providers/seventv/eventapi/Subscription.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 3103bd206..8f34224a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ - Dev: Format YAML and JSON files with prettier. (#4304) - Dev: Added CMake Install Support on Windows. (#4300) - Dev: Changed conan generator to [`CMakeDeps`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmakedeps.html) and [`CMakeToolchain`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmaketoolchain.html). See PR for migration notes. (#4335) +- Dev: Refactored 7TV EventAPI implementation. (#4342) ## 2.4.0 diff --git a/src/Application.cpp b/src/Application.cpp index 7d02b3931..7804169ce 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -19,8 +19,8 @@ #include "providers/chatterino/ChatterinoBadges.hpp" #include "providers/ffz/FfzBadges.hpp" #include "providers/irc/Irc2.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp" -#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp" +#include "providers/seventv/eventapi/Dispatch.hpp" +#include "providers/seventv/eventapi/Subscription.hpp" #include "providers/seventv/SeventvBadges.hpp" #include "providers/seventv/SeventvEventAPI.hpp" #include "providers/twitch/ChannelPointReward.hpp" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2d2105dc1..b412c6895 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -250,14 +250,14 @@ set(SOURCE_FILES providers/seventv/SeventvEventAPI.cpp providers/seventv/SeventvEventAPI.hpp - providers/seventv/eventapi/SeventvEventAPIClient.cpp - providers/seventv/eventapi/SeventvEventAPIClient.hpp - providers/seventv/eventapi/SeventvEventAPIDispatch.cpp - providers/seventv/eventapi/SeventvEventAPIDispatch.hpp - providers/seventv/eventapi/SeventvEventAPIMessage.cpp - providers/seventv/eventapi/SeventvEventAPIMessage.hpp - providers/seventv/eventapi/SeventvEventAPISubscription.cpp - providers/seventv/eventapi/SeventvEventAPISubscription.hpp + providers/seventv/eventapi/Client.cpp + providers/seventv/eventapi/Client.hpp + providers/seventv/eventapi/Dispatch.cpp + providers/seventv/eventapi/Dispatch.hpp + providers/seventv/eventapi/Message.cpp + providers/seventv/eventapi/Message.hpp + providers/seventv/eventapi/Subscription.cpp + providers/seventv/eventapi/Subscription.hpp providers/twitch/ChannelPointReward.cpp providers/twitch/ChannelPointReward.hpp diff --git a/src/providers/seventv/SeventvEmotes.cpp b/src/providers/seventv/SeventvEmotes.cpp index 8f8ee18d6..2f7883abc 100644 --- a/src/providers/seventv/SeventvEmotes.cpp +++ b/src/providers/seventv/SeventvEmotes.cpp @@ -7,7 +7,7 @@ #include "messages/Image.hpp" #include "messages/ImageSet.hpp" #include "messages/MessageBuilder.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp" +#include "providers/seventv/eventapi/Dispatch.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "singletons/Settings.hpp" @@ -30,6 +30,7 @@ namespace { using namespace chatterino; +using namespace seventv::eventapi; // These declarations won't throw an exception. const QString CHANNEL_HAS_NO_EMOTES("This channel has no 7TV channel emotes."); @@ -224,7 +225,7 @@ EmoteMap parseEmotes(const QJsonArray &emoteSetEmotes, bool isGlobal) } EmotePtr createUpdatedEmote(const EmotePtr &oldEmote, - const SeventvEventAPIEmoteUpdateDispatch &dispatch) + const EmoteUpdateDispatch &dispatch) { bool toNonAliased = oldEmote->baseName.has_value() && dispatch.emoteName == oldEmote->baseName->string; @@ -245,6 +246,8 @@ EmotePtr createUpdatedEmote(const EmotePtr &oldEmote, namespace chatterino { +using namespace seventv::eventapi; + SeventvEmotes::SeventvEmotes() : global_(std::make_shared()) { @@ -401,7 +404,7 @@ void SeventvEmotes::loadChannelEmotes( boost::optional SeventvEmotes::addEmote( Atomic> &map, - const SeventvEventAPIEmoteAddDispatch &dispatch) + const EmoteAddDispatch &dispatch) { // Check for visibility first, so we don't copy the map. auto emoteData = dispatch.emoteJson["data"].toObject(); @@ -429,7 +432,7 @@ boost::optional SeventvEmotes::addEmote( boost::optional SeventvEmotes::updateEmote( Atomic> &map, - const SeventvEventAPIEmoteUpdateDispatch &dispatch) + const EmoteUpdateDispatch &dispatch) { auto oldMap = map.get(); auto oldEmote = oldMap->findEmote(dispatch.emoteName, dispatch.emoteID); @@ -451,7 +454,7 @@ boost::optional SeventvEmotes::updateEmote( boost::optional SeventvEmotes::removeEmote( Atomic> &map, - const SeventvEventAPIEmoteRemoveDispatch &dispatch) + const EmoteRemoveDispatch &dispatch) { // This copies the map. EmoteMap updatedMap = *map.get(); diff --git a/src/providers/seventv/SeventvEmotes.hpp b/src/providers/seventv/SeventvEmotes.hpp index 023e99555..f978337be 100644 --- a/src/providers/seventv/SeventvEmotes.hpp +++ b/src/providers/seventv/SeventvEmotes.hpp @@ -10,9 +10,12 @@ namespace chatterino { class Channel; -struct SeventvEventAPIEmoteAddDispatch; -struct SeventvEventAPIEmoteUpdateDispatch; -struct SeventvEventAPIEmoteRemoveDispatch; + +namespace seventv::eventapi { + struct EmoteAddDispatch; + struct EmoteUpdateDispatch; + struct EmoteRemoveDispatch; +} // namespace seventv::eventapi // https://github.com/SevenTV/API/blob/a84e884b5590dbb5d91a5c6b3548afabb228f385/data/model/emote-set.model.go#L29-L36 enum class SeventvActiveEmoteFlag : int64_t { @@ -86,7 +89,7 @@ public: */ static boost::optional addEmote( Atomic> &map, - const SeventvEventAPIEmoteAddDispatch &dispatch); + const seventv::eventapi::EmoteAddDispatch &dispatch); /** * Updates an emote in this `map`. @@ -97,7 +100,7 @@ public: */ static boost::optional updateEmote( Atomic> &map, - const SeventvEventAPIEmoteUpdateDispatch &dispatch); + const seventv::eventapi::EmoteUpdateDispatch &dispatch); /** * Removes an emote from this `map`. @@ -108,7 +111,7 @@ public: */ static boost::optional removeEmote( Atomic> &map, - const SeventvEventAPIEmoteRemoveDispatch &dispatch); + const seventv::eventapi::EmoteRemoveDispatch &dispatch); /** Fetches an emote-set by its id */ static void getEmoteSet( diff --git a/src/providers/seventv/SeventvEventAPI.cpp b/src/providers/seventv/SeventvEventAPI.cpp index c4be244dc..5cec6ed30 100644 --- a/src/providers/seventv/SeventvEventAPI.cpp +++ b/src/providers/seventv/SeventvEventAPI.cpp @@ -1,8 +1,8 @@ #include "providers/seventv/SeventvEventAPI.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIClient.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIMessage.hpp" +#include "providers/seventv/eventapi/Client.hpp" +#include "providers/seventv/eventapi/Dispatch.hpp" +#include "providers/seventv/eventapi/Message.hpp" #include @@ -10,6 +10,8 @@ namespace chatterino { +using namespace seventv::eventapi; + SeventvEventAPI::SeventvEventAPI( QString host, std::chrono::milliseconds defaultHeartbeatInterval) : BasicPubSubManager(std::move(host)) @@ -22,13 +24,14 @@ void SeventvEventAPI::subscribeUser(const QString &userID, { if (!userID.isEmpty() && this->subscribedUsers_.insert(userID).second) { - this->subscribe({userID, SeventvEventAPISubscriptionType::UpdateUser}); + this->subscribe( + {ObjectIDCondition{userID}, SubscriptionType::UpdateUser}); } if (!emoteSetID.isEmpty() && this->subscribedEmoteSets_.insert(emoteSetID).second) { this->subscribe( - {emoteSetID, SeventvEventAPISubscriptionType::UpdateEmoteSet}); + {ObjectIDCondition{emoteSetID}, SubscriptionType::UpdateEmoteSet}); } } @@ -37,7 +40,7 @@ void SeventvEventAPI::unsubscribeEmoteSet(const QString &id) if (this->subscribedEmoteSets_.erase(id) > 0) { this->unsubscribe( - {id, SeventvEventAPISubscriptionType::UpdateEmoteSet}); + {ObjectIDCondition{id}, SubscriptionType::UpdateEmoteSet}); } } @@ -45,27 +48,27 @@ void SeventvEventAPI::unsubscribeUser(const QString &id) { if (this->subscribedUsers_.erase(id) > 0) { - this->unsubscribe({id, SeventvEventAPISubscriptionType::UpdateUser}); + this->unsubscribe( + {ObjectIDCondition{id}, SubscriptionType::UpdateUser}); } } -std::shared_ptr> - SeventvEventAPI::createClient(liveupdates::WebsocketClient &client, - websocketpp::connection_hdl hdl) +std::shared_ptr> SeventvEventAPI::createClient( + liveupdates::WebsocketClient &client, websocketpp::connection_hdl hdl) { - auto shared = std::make_shared( - client, hdl, this->heartbeatInterval_); - return std::static_pointer_cast< - BasicPubSubClient>(std::move(shared)); + auto shared = + std::make_shared(client, hdl, this->heartbeatInterval_); + return std::static_pointer_cast>( + std::move(shared)); } void SeventvEventAPI::onMessage( websocketpp::connection_hdl hdl, - BasicPubSubManager::WebsocketMessagePtr msg) + BasicPubSubManager::WebsocketMessagePtr msg) { const auto &payload = QString::fromStdString(msg->get_payload()); - auto pMessage = parseSeventvEventAPIBaseMessage(payload); + auto pMessage = parseBaseMessage(payload); if (!pMessage) { @@ -76,11 +79,10 @@ void SeventvEventAPI::onMessage( auto message = *pMessage; switch (message.op) { - case SeventvEventAPIOpcode::Hello: { + case Opcode::Hello: { if (auto client = this->findClient(hdl)) { - if (auto *stvClient = - dynamic_cast(client.get())) + if (auto *stvClient = dynamic_cast(client.get())) { stvClient->setHeartbeatInterval( message.data["heartbeat_interval"].toInt()); @@ -88,19 +90,18 @@ void SeventvEventAPI::onMessage( } } break; - case SeventvEventAPIOpcode::Heartbeat: { + case Opcode::Heartbeat: { if (auto client = this->findClient(hdl)) { - if (auto *stvClient = - dynamic_cast(client.get())) + if (auto *stvClient = dynamic_cast(client.get())) { stvClient->handleHeartbeat(); } } } break; - case SeventvEventAPIOpcode::Dispatch: { - auto dispatch = message.toInner(); + case Opcode::Dispatch: { + auto dispatch = message.toInner(); if (!dispatch) { qCDebug(chatterinoSeventvEventAPI) @@ -110,133 +111,37 @@ void SeventvEventAPI::onMessage( this->handleDispatch(*dispatch); } break; - case SeventvEventAPIOpcode::Reconnect: { + case Opcode::Reconnect: { if (auto client = this->findClient(hdl)) { - if (auto *stvClient = - dynamic_cast(client.get())) + if (auto *stvClient = dynamic_cast(client.get())) { stvClient->close("Reconnecting"); } } } break; + case Opcode::Ack: { + // unhandled + } + break; default: { - qCDebug(chatterinoSeventvEventAPI) << "Unhandled op: " << payload; + qCDebug(chatterinoSeventvEventAPI) << "Unhandled op:" << payload; } break; } } -void SeventvEventAPI::handleDispatch(const SeventvEventAPIDispatch &dispatch) +void SeventvEventAPI::handleDispatch(const Dispatch &dispatch) { switch (dispatch.type) { - case SeventvEventAPISubscriptionType::UpdateEmoteSet: { - // dispatchBody: { - // pushed: Array<{ key, value }>, - // pulled: Array<{ key, old_value }>, - // updated: Array<{ key, value, old_value }>, - // } - for (const auto pushedRef : dispatch.body["pushed"].toArray()) - { - auto pushed = pushedRef.toObject(); - if (pushed["key"].toString() != "emotes") - { - continue; - } - - SeventvEventAPIEmoteAddDispatch added( - dispatch, pushed["value"].toObject()); - - if (added.validate()) - { - this->signals_.emoteAdded.invoke(added); - } - else - { - qCDebug(chatterinoSeventvEventAPI) - << "Invalid dispatch" << dispatch.body; - } - } - for (const auto updatedRef : dispatch.body["updated"].toArray()) - { - auto updated = updatedRef.toObject(); - if (updated["key"].toString() != "emotes") - { - continue; - } - - SeventvEventAPIEmoteUpdateDispatch update( - dispatch, updated["old_value"].toObject(), - updated["value"].toObject()); - - if (update.validate()) - { - this->signals_.emoteUpdated.invoke(update); - } - else - { - qCDebug(chatterinoSeventvEventAPI) - << "Invalid dispatch" << dispatch.body; - } - } - for (const auto pulledRef : dispatch.body["pulled"].toArray()) - { - auto pulled = pulledRef.toObject(); - if (pulled["key"].toString() != "emotes") - { - continue; - } - - SeventvEventAPIEmoteRemoveDispatch removed( - dispatch, pulled["old_value"].toObject()); - - if (removed.validate()) - { - this->signals_.emoteRemoved.invoke(removed); - } - else - { - qCDebug(chatterinoSeventvEventAPI) - << "Invalid dispatch" << dispatch.body; - } - } + case SubscriptionType::UpdateEmoteSet: { + this->onEmoteSetUpdate(dispatch); } break; - case SeventvEventAPISubscriptionType::UpdateUser: { - // dispatchBody: { - // updated: Array<{ key, value: Array<{key, value}> }> - // } - for (const auto updatedRef : dispatch.body["updated"].toArray()) - { - auto updated = updatedRef.toObject(); - if (updated["key"].toString() != "connections") - { - continue; - } - for (const auto valueRef : updated["value"].toArray()) - { - auto value = valueRef.toObject(); - if (value["key"].toString() != "emote_set") - { - continue; - } - - SeventvEventAPIUserConnectionUpdateDispatch update( - dispatch, value, (size_t)updated["index"].toInt()); - - if (update.validate()) - { - this->signals_.userUpdated.invoke(update); - } - else - { - qCDebug(chatterinoSeventvEventAPI) - << "Invalid dispatch" << dispatch.body; - } - } - } + case SubscriptionType::UpdateUser: { + this->onUserUpdate(dispatch); } break; default: { @@ -248,4 +153,112 @@ void SeventvEventAPI::handleDispatch(const SeventvEventAPIDispatch &dispatch) } } +void SeventvEventAPI::onEmoteSetUpdate(const Dispatch &dispatch) +{ + // dispatchBody: { + // pushed: Array<{ key, value }>, + // pulled: Array<{ key, old_value }>, + // updated: Array<{ key, value, old_value }>, + // } + for (const auto pushedRef : dispatch.body["pushed"].toArray()) + { + auto pushed = pushedRef.toObject(); + if (pushed["key"].toString() != "emotes") + { + continue; + } + + const EmoteAddDispatch added(dispatch, pushed["value"].toObject()); + + if (added.validate()) + { + this->signals_.emoteAdded.invoke(added); + } + else + { + qCDebug(chatterinoSeventvEventAPI) + << "Invalid dispatch" << dispatch.body; + } + } + for (const auto updatedRef : dispatch.body["updated"].toArray()) + { + auto updated = updatedRef.toObject(); + if (updated["key"].toString() != "emotes") + { + continue; + } + + const EmoteUpdateDispatch update(dispatch, + updated["old_value"].toObject(), + updated["value"].toObject()); + + if (update.validate()) + { + this->signals_.emoteUpdated.invoke(update); + } + else + { + qCDebug(chatterinoSeventvEventAPI) + << "Invalid dispatch" << dispatch.body; + } + } + for (const auto pulledRef : dispatch.body["pulled"].toArray()) + { + auto pulled = pulledRef.toObject(); + if (pulled["key"].toString() != "emotes") + { + continue; + } + + const EmoteRemoveDispatch removed(dispatch, + pulled["old_value"].toObject()); + + if (removed.validate()) + { + this->signals_.emoteRemoved.invoke(removed); + } + else + { + qCDebug(chatterinoSeventvEventAPI) + << "Invalid dispatch" << dispatch.body; + } + } +} + +void SeventvEventAPI::onUserUpdate(const Dispatch &dispatch) +{ + // dispatchBody: { + // updated: Array<{ key, value: Array<{key, value}> }> + // } + for (const auto updatedRef : dispatch.body["updated"].toArray()) + { + auto updated = updatedRef.toObject(); + if (updated["key"].toString() != "connections") + { + continue; + } + for (const auto valueRef : updated["value"].toArray()) + { + auto value = valueRef.toObject(); + if (value["key"].toString() != "emote_set") + { + continue; + } + + const UserConnectionUpdateDispatch update( + dispatch, value, (size_t)updated["index"].toInt()); + + if (update.validate()) + { + this->signals_.userUpdated.invoke(update); + } + else + { + qCDebug(chatterinoSeventvEventAPI) + << "Invalid dispatch" << dispatch.body; + } + } + } +} + } // namespace chatterino diff --git a/src/providers/seventv/SeventvEventAPI.hpp b/src/providers/seventv/SeventvEventAPI.hpp index 946e147cb..5672e59b8 100644 --- a/src/providers/seventv/SeventvEventAPI.hpp +++ b/src/providers/seventv/SeventvEventAPI.hpp @@ -8,14 +8,17 @@ namespace chatterino { -struct SeventvEventAPISubscription; -struct SeventvEventAPIDispatch; -struct SeventvEventAPIEmoteAddDispatch; -struct SeventvEventAPIEmoteUpdateDispatch; -struct SeventvEventAPIEmoteRemoveDispatch; -struct SeventvEventAPIUserConnectionUpdateDispatch; +namespace seventv::eventapi { + struct Subscription; + struct Dispatch; + struct EmoteAddDispatch; + struct EmoteUpdateDispatch; + struct EmoteRemoveDispatch; + struct UserConnectionUpdateDispatch; +} // namespace seventv::eventapi -class SeventvEventAPI : public BasicPubSubManager +class SeventvEventAPI + : public BasicPubSubManager { template using Signal = @@ -27,10 +30,10 @@ public: std::chrono::milliseconds(25000)); struct { - Signal emoteAdded; - Signal emoteUpdated; - Signal emoteRemoved; - Signal userUpdated; + Signal emoteAdded; + Signal emoteUpdated; + Signal emoteRemoved; + Signal userUpdated; } signals_; // NOLINT(readability-identifier-naming) /** @@ -48,18 +51,23 @@ public: void unsubscribeEmoteSet(const QString &id); protected: - std::shared_ptr> + std::shared_ptr> createClient(liveupdates::WebsocketClient &client, websocketpp::connection_hdl hdl) override; void onMessage( websocketpp::connection_hdl hdl, - BasicPubSubManager::WebsocketMessagePtr + BasicPubSubManager::WebsocketMessagePtr msg) override; private: - void handleDispatch(const SeventvEventAPIDispatch &dispatch); + void handleDispatch(const seventv::eventapi::Dispatch &dispatch); + void onEmoteSetUpdate(const seventv::eventapi::Dispatch &dispatch); + void onUserUpdate(const seventv::eventapi::Dispatch &dispatch); + + /** emote-set ids */ std::unordered_set subscribedEmoteSets_; + /** user ids */ std::unordered_set subscribedUsers_; std::chrono::milliseconds heartbeatInterval_; }; diff --git a/src/providers/seventv/eventapi/SeventvEventAPIClient.cpp b/src/providers/seventv/eventapi/Client.cpp similarity index 64% rename from src/providers/seventv/eventapi/SeventvEventAPIClient.cpp rename to src/providers/seventv/eventapi/Client.cpp index e1fcf2d02..f266478ce 100644 --- a/src/providers/seventv/eventapi/SeventvEventAPIClient.cpp +++ b/src/providers/seventv/eventapi/Client.cpp @@ -1,44 +1,42 @@ -#include "providers/seventv/eventapi/SeventvEventAPIClient.hpp" +#include "providers/seventv/eventapi/Client.hpp" -#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp" +#include "providers/seventv/eventapi/Subscription.hpp" #include "providers/twitch/PubSubHelpers.hpp" #include -namespace chatterino { +namespace chatterino::seventv::eventapi { -SeventvEventAPIClient::SeventvEventAPIClient( - liveupdates::WebsocketClient &websocketClient, - liveupdates::WebsocketHandle handle, - std::chrono::milliseconds heartbeatInterval) - : BasicPubSubClient(websocketClient, - std::move(handle)) +Client::Client(liveupdates::WebsocketClient &websocketClient, + liveupdates::WebsocketHandle handle, + std::chrono::milliseconds heartbeatInterval) + : BasicPubSubClient(websocketClient, std::move(handle)) , lastHeartbeat_(std::chrono::steady_clock::now()) , heartbeatInterval_(heartbeatInterval) { } -void SeventvEventAPIClient::onConnectionEstablished() +void Client::onConnectionEstablished() { this->lastHeartbeat_.store(std::chrono::steady_clock::now(), std::memory_order_release); this->checkHeartbeat(); } -void SeventvEventAPIClient::setHeartbeatInterval(int intervalMs) +void Client::setHeartbeatInterval(int intervalMs) { qCDebug(chatterinoSeventvEventAPI) << "Setting expected heartbeat interval to" << intervalMs << "ms"; this->heartbeatInterval_ = std::chrono::milliseconds(intervalMs); } -void SeventvEventAPIClient::handleHeartbeat() +void Client::handleHeartbeat() { this->lastHeartbeat_.store(std::chrono::steady_clock::now(), std::memory_order_release); } -void SeventvEventAPIClient::checkHeartbeat() +void Client::checkHeartbeat() { // Following the heartbeat docs, a connection is dead // after three missed heartbeats. @@ -54,8 +52,7 @@ void SeventvEventAPIClient::checkHeartbeat() return; } - auto self = std::dynamic_pointer_cast( - this->shared_from_this()); + auto self = std::dynamic_pointer_cast(this->shared_from_this()); runAfter(this->websocketClient_.get_io_service(), this->heartbeatInterval_, [self](auto) { @@ -67,4 +64,4 @@ void SeventvEventAPIClient::checkHeartbeat() }); } -} // namespace chatterino +} // namespace chatterino::seventv::eventapi diff --git a/src/providers/seventv/eventapi/SeventvEventAPIClient.hpp b/src/providers/seventv/eventapi/Client.hpp similarity index 55% rename from src/providers/seventv/eventapi/SeventvEventAPIClient.hpp rename to src/providers/seventv/eventapi/Client.hpp index f33256d42..11683edcf 100644 --- a/src/providers/seventv/eventapi/SeventvEventAPIClient.hpp +++ b/src/providers/seventv/eventapi/Client.hpp @@ -2,18 +2,22 @@ #include "providers/liveupdates/BasicPubSubClient.hpp" // this needs to be included for the specialization -// of std::hash for SeventvEventAPISubscription -#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp" +// of std::hash for Subscription +#include "providers/seventv/eventapi/Subscription.hpp" namespace chatterino { +class SeventvEventAPI; -class SeventvEventAPIClient - : public BasicPubSubClient +} // namespace chatterino + +namespace chatterino::seventv::eventapi { + +class Client : public BasicPubSubClient { public: - SeventvEventAPIClient(liveupdates::WebsocketClient &websocketClient, - liveupdates::WebsocketHandle handle, - std::chrono::milliseconds heartbeatInterval); + Client(liveupdates::WebsocketClient &websocketClient, + liveupdates::WebsocketHandle handle, + std::chrono::milliseconds heartbeatInterval); void setHeartbeatInterval(int intervalMs); void handleHeartbeat(); @@ -29,7 +33,7 @@ private: // This will be set once on the welcome message. std::chrono::milliseconds heartbeatInterval_; - friend class SeventvEventAPI; + friend SeventvEventAPI; }; -} // namespace chatterino +} // namespace chatterino::seventv::eventapi diff --git a/src/providers/seventv/eventapi/SeventvEventAPIDispatch.cpp b/src/providers/seventv/eventapi/Dispatch.cpp similarity index 62% rename from src/providers/seventv/eventapi/SeventvEventAPIDispatch.cpp rename to src/providers/seventv/eventapi/Dispatch.cpp index 52d4bde6f..bb4b4fa1d 100644 --- a/src/providers/seventv/eventapi/SeventvEventAPIDispatch.cpp +++ b/src/providers/seventv/eventapi/Dispatch.cpp @@ -1,21 +1,20 @@ -#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp" +#include "providers/seventv/eventapi/Dispatch.hpp" #include -namespace chatterino { +namespace chatterino::seventv::eventapi { -SeventvEventAPIDispatch::SeventvEventAPIDispatch(QJsonObject obj) - : type(magic_enum::enum_cast( +Dispatch::Dispatch(QJsonObject obj) + : type(magic_enum::enum_cast( obj["type"].toString().toStdString()) - .value_or(SeventvEventAPISubscriptionType::INVALID)) + .value_or(SubscriptionType::INVALID)) , body(obj["body"].toObject()) , id(this->body["id"].toString()) , actorName(this->body["actor"].toObject()["display_name"].toString()) { } -SeventvEventAPIEmoteAddDispatch::SeventvEventAPIEmoteAddDispatch( - const SeventvEventAPIDispatch &dispatch, QJsonObject emote) +EmoteAddDispatch::EmoteAddDispatch(const Dispatch &dispatch, QJsonObject emote) : emoteSetID(dispatch.id) , actorName(dispatch.actorName) , emoteJson(std::move(emote)) @@ -23,7 +22,7 @@ SeventvEventAPIEmoteAddDispatch::SeventvEventAPIEmoteAddDispatch( { } -bool SeventvEventAPIEmoteAddDispatch::validate() const +bool EmoteAddDispatch::validate() const { bool validValues = !this->emoteSetID.isEmpty() && !this->emoteJson.isEmpty(); @@ -43,8 +42,8 @@ bool SeventvEventAPIEmoteAddDispatch::validate() const emoteData.contains("owner"); } -SeventvEventAPIEmoteRemoveDispatch::SeventvEventAPIEmoteRemoveDispatch( - const SeventvEventAPIDispatch &dispatch, QJsonObject emote) +EmoteRemoveDispatch::EmoteRemoveDispatch(const Dispatch &dispatch, + QJsonObject emote) : emoteSetID(dispatch.id) , actorName(dispatch.actorName) , emoteName(emote["name"].toString()) @@ -52,15 +51,15 @@ SeventvEventAPIEmoteRemoveDispatch::SeventvEventAPIEmoteRemoveDispatch( { } -bool SeventvEventAPIEmoteRemoveDispatch::validate() const +bool EmoteRemoveDispatch::validate() const { return !this->emoteSetID.isEmpty() && !this->emoteName.isEmpty() && !this->emoteID.isEmpty(); } -SeventvEventAPIEmoteUpdateDispatch::SeventvEventAPIEmoteUpdateDispatch( - const SeventvEventAPIDispatch &dispatch, QJsonObject oldValue, - QJsonObject value) +EmoteUpdateDispatch::EmoteUpdateDispatch(const Dispatch &dispatch, + QJsonObject oldValue, + QJsonObject value) : emoteSetID(dispatch.id) , actorName(dispatch.actorName) , emoteID(value["id"].toString()) @@ -69,17 +68,15 @@ SeventvEventAPIEmoteUpdateDispatch::SeventvEventAPIEmoteUpdateDispatch( { } -bool SeventvEventAPIEmoteUpdateDispatch::validate() const +bool EmoteUpdateDispatch::validate() const { return !this->emoteSetID.isEmpty() && !this->emoteID.isEmpty() && !this->oldEmoteName.isEmpty() && !this->emoteName.isEmpty() && this->oldEmoteName != this->emoteName; } -SeventvEventAPIUserConnectionUpdateDispatch:: - SeventvEventAPIUserConnectionUpdateDispatch( - const SeventvEventAPIDispatch &dispatch, const QJsonObject &update, - size_t connectionIndex) +UserConnectionUpdateDispatch::UserConnectionUpdateDispatch( + const Dispatch &dispatch, const QJsonObject &update, size_t connectionIndex) : userID(dispatch.id) , actorName(dispatch.actorName) , oldEmoteSetID(update["old_value"].toObject()["id"].toString()) @@ -88,10 +85,10 @@ SeventvEventAPIUserConnectionUpdateDispatch:: { } -bool SeventvEventAPIUserConnectionUpdateDispatch::validate() const +bool UserConnectionUpdateDispatch::validate() const { return !this->userID.isEmpty() && !this->oldEmoteSetID.isEmpty() && !this->emoteSetID.isEmpty(); } -} // namespace chatterino +} // namespace chatterino::seventv::eventapi diff --git a/src/providers/seventv/eventapi/Dispatch.hpp b/src/providers/seventv/eventapi/Dispatch.hpp new file mode 100644 index 000000000..666f5c28a --- /dev/null +++ b/src/providers/seventv/eventapi/Dispatch.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include "providers/seventv/eventapi/Subscription.hpp" + +#include +#include + +namespace chatterino::seventv::eventapi { + +// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#message-payload +struct Dispatch { + SubscriptionType type; + QJsonObject body; + QString id; + // it's okay for this to be empty + QString actorName; + + Dispatch(QJsonObject obj); +}; + +struct EmoteAddDispatch { + QString emoteSetID; + QString actorName; + QJsonObject emoteJson; + QString emoteID; + + EmoteAddDispatch(const Dispatch &dispatch, QJsonObject emote); + + bool validate() const; +}; + +struct EmoteRemoveDispatch { + QString emoteSetID; + QString actorName; + QString emoteName; + QString emoteID; + + EmoteRemoveDispatch(const Dispatch &dispatch, QJsonObject emote); + + bool validate() const; +}; + +struct EmoteUpdateDispatch { + QString emoteSetID; + QString actorName; + QString emoteID; + QString oldEmoteName; + QString emoteName; + + EmoteUpdateDispatch(const Dispatch &dispatch, QJsonObject oldValue, + QJsonObject value); + + bool validate() const; +}; + +struct UserConnectionUpdateDispatch { + QString userID; + QString actorName; + QString oldEmoteSetID; + QString emoteSetID; + size_t connectionIndex; + + UserConnectionUpdateDispatch(const Dispatch &dispatch, + const QJsonObject &update, + size_t connectionIndex); + + bool validate() const; +}; + +} // namespace chatterino::seventv::eventapi diff --git a/src/providers/seventv/eventapi/Message.cpp b/src/providers/seventv/eventapi/Message.cpp new file mode 100644 index 000000000..a216a72b4 --- /dev/null +++ b/src/providers/seventv/eventapi/Message.cpp @@ -0,0 +1,11 @@ +#include "providers/seventv/eventapi/Message.hpp" + +namespace chatterino::seventv::eventapi { + +Message::Message(QJsonObject _json) + : data(_json["d"].toObject()) + , op(Opcode(_json["op"].toInt())) +{ +} + +} // namespace chatterino::seventv::eventapi diff --git a/src/providers/seventv/eventapi/SeventvEventAPIMessage.hpp b/src/providers/seventv/eventapi/Message.hpp similarity index 50% rename from src/providers/seventv/eventapi/SeventvEventAPIMessage.hpp rename to src/providers/seventv/eventapi/Message.hpp index f18d07f31..1b857f9ea 100644 --- a/src/providers/seventv/eventapi/SeventvEventAPIMessage.hpp +++ b/src/providers/seventv/eventapi/Message.hpp @@ -1,6 +1,6 @@ #pragma once -#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp" +#include "providers/seventv/eventapi/Subscription.hpp" #include #include @@ -8,27 +8,26 @@ #include #include -namespace chatterino { +namespace chatterino::seventv::eventapi { -struct SeventvEventAPIMessage { +struct Message { QJsonObject data; - SeventvEventAPIOpcode op; + Opcode op; - SeventvEventAPIMessage(QJsonObject _json); + Message(QJsonObject _json); template boost::optional toInner(); }; template -boost::optional SeventvEventAPIMessage::toInner() +boost::optional Message::toInner() { return InnerClass{this->data}; } -static boost::optional parseSeventvEventAPIBaseMessage( - const QString &blob) +static boost::optional parseBaseMessage(const QString &blob) { QJsonDocument jsonDoc(QJsonDocument::fromJson(blob.toUtf8())); @@ -37,7 +36,7 @@ static boost::optional parseSeventvEventAPIBaseMessage( return boost::none; } - return SeventvEventAPIMessage(jsonDoc.object()); + return Message(jsonDoc.object()); } -} // namespace chatterino +} // namespace chatterino::seventv::eventapi diff --git a/src/providers/seventv/eventapi/SeventvEventAPIDispatch.hpp b/src/providers/seventv/eventapi/SeventvEventAPIDispatch.hpp deleted file mode 100644 index a11b8471e..000000000 --- a/src/providers/seventv/eventapi/SeventvEventAPIDispatch.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp" - -#include -#include - -namespace chatterino { - -// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#message-payload -struct SeventvEventAPIDispatch { - SeventvEventAPISubscriptionType type; - QJsonObject body; - QString id; - // it's okay for this to be empty - QString actorName; - - SeventvEventAPIDispatch(QJsonObject obj); -}; - -struct SeventvEventAPIEmoteAddDispatch { - QString emoteSetID; - QString actorName; - QJsonObject emoteJson; - QString emoteID; - - SeventvEventAPIEmoteAddDispatch(const SeventvEventAPIDispatch &dispatch, - QJsonObject emote); - - bool validate() const; -}; - -struct SeventvEventAPIEmoteRemoveDispatch { - QString emoteSetID; - QString actorName; - QString emoteName; - QString emoteID; - - SeventvEventAPIEmoteRemoveDispatch(const SeventvEventAPIDispatch &dispatch, - QJsonObject emote); - - bool validate() const; -}; - -struct SeventvEventAPIEmoteUpdateDispatch { - QString emoteSetID; - QString actorName; - QString emoteID; - QString oldEmoteName; - QString emoteName; - - SeventvEventAPIEmoteUpdateDispatch(const SeventvEventAPIDispatch &dispatch, - QJsonObject oldValue, QJsonObject value); - - bool validate() const; -}; - -struct SeventvEventAPIUserConnectionUpdateDispatch { - QString userID; - QString actorName; - QString oldEmoteSetID; - QString emoteSetID; - size_t connectionIndex; - - SeventvEventAPIUserConnectionUpdateDispatch( - const SeventvEventAPIDispatch &dispatch, const QJsonObject &update, - size_t connectionIndex); - - bool validate() const; -}; - -} // namespace chatterino diff --git a/src/providers/seventv/eventapi/SeventvEventAPIMessage.cpp b/src/providers/seventv/eventapi/SeventvEventAPIMessage.cpp deleted file mode 100644 index 69b3b2b22..000000000 --- a/src/providers/seventv/eventapi/SeventvEventAPIMessage.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "providers/seventv/eventapi/SeventvEventAPIMessage.hpp" - -namespace chatterino { - -SeventvEventAPIMessage::SeventvEventAPIMessage(QJsonObject _json) - : data(_json["d"].toObject()) - , op(SeventvEventAPIOpcode(_json["op"].toInt())) -{ -} - -} // namespace chatterino diff --git a/src/providers/seventv/eventapi/SeventvEventAPISubscription.cpp b/src/providers/seventv/eventapi/SeventvEventAPISubscription.cpp deleted file mode 100644 index 6ae3b9363..000000000 --- a/src/providers/seventv/eventapi/SeventvEventAPISubscription.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp" - -#include -#include -#include - -#include - -namespace { - -using namespace chatterino; - -const char *typeToString(SeventvEventAPISubscriptionType type) -{ - switch (type) - { - case SeventvEventAPISubscriptionType::UpdateEmoteSet: - return "emote_set.update"; - case SeventvEventAPISubscriptionType::UpdateUser: - return "user.update"; - default: - return ""; - } -} - -QJsonObject createDataJson(const char *typeName, const QString &condition) -{ - QJsonObject data; - data["type"] = typeName; - { - QJsonObject conditionObj; - conditionObj["object_id"] = condition; - data["condition"] = conditionObj; - } - return data; -} - -} // namespace - -namespace chatterino { - -bool SeventvEventAPISubscription::operator==( - const SeventvEventAPISubscription &rhs) const -{ - return std::tie(this->condition, this->type) == - std::tie(rhs.condition, rhs.type); -} - -bool SeventvEventAPISubscription::operator!=( - const SeventvEventAPISubscription &rhs) const -{ - return !(rhs == *this); -} - -QByteArray SeventvEventAPISubscription::encodeSubscribe() const -{ - const auto *typeName = typeToString(this->type); - QJsonObject root; - root["op"] = (int)SeventvEventAPIOpcode::Subscribe; - root["d"] = createDataJson(typeName, this->condition); - return QJsonDocument(root).toJson(); -} - -QByteArray SeventvEventAPISubscription::encodeUnsubscribe() const -{ - const auto *typeName = typeToString(this->type); - QJsonObject root; - root["op"] = (int)SeventvEventAPIOpcode::Unsubscribe; - root["d"] = createDataJson(typeName, this->condition); - return QJsonDocument(root).toJson(); -} - -QDebug &operator<<(QDebug &dbg, const SeventvEventAPISubscription &subscription) -{ - dbg << "SeventvEventAPISubscription{ condition:" << subscription.condition - << "type:" << (int)subscription.type << '}'; - return dbg; -} - -} // namespace chatterino diff --git a/src/providers/seventv/eventapi/SeventvEventAPISubscription.hpp b/src/providers/seventv/eventapi/SeventvEventAPISubscription.hpp deleted file mode 100644 index 55c233da7..000000000 --- a/src/providers/seventv/eventapi/SeventvEventAPISubscription.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace chatterino { - -// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#subscription-types -enum class SeventvEventAPISubscriptionType { - UpdateEmoteSet, - UpdateUser, - - INVALID, -}; - -// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#opcodes -enum class SeventvEventAPIOpcode { - Dispatch = 0, - Hello = 1, - Heartbeat = 2, - Reconnect = 4, - Ack = 5, - Error = 6, - EndOfStream = 7, - Identify = 33, - Resume = 34, - Subscribe = 35, - Unsubscribe = 36, - Signal = 37, -}; - -struct SeventvEventAPISubscription { - bool operator==(const SeventvEventAPISubscription &rhs) const; - bool operator!=(const SeventvEventAPISubscription &rhs) const; - QString condition; - SeventvEventAPISubscriptionType type; - - QByteArray encodeSubscribe() const; - QByteArray encodeUnsubscribe() const; - - friend QDebug &operator<<(QDebug &dbg, - const SeventvEventAPISubscription &subscription); -}; - -} // namespace chatterino - -template <> -constexpr magic_enum::customize::customize_t magic_enum::customize::enum_name< - chatterino::SeventvEventAPISubscriptionType>( - chatterino::SeventvEventAPISubscriptionType value) noexcept -{ - switch (value) - { - case chatterino::SeventvEventAPISubscriptionType::UpdateEmoteSet: - return "emote_set.update"; - case chatterino::SeventvEventAPISubscriptionType::UpdateUser: - return "user.update"; - - default: - return default_tag; - } -} - -namespace std { - -template <> -struct hash { - size_t operator()(const chatterino::SeventvEventAPISubscription &sub) const - { - return (size_t)qHash(sub.condition, qHash((int)sub.type)); - } -}; - -} // namespace std diff --git a/src/providers/seventv/eventapi/Subscription.cpp b/src/providers/seventv/eventapi/Subscription.cpp new file mode 100644 index 000000000..1de1f667e --- /dev/null +++ b/src/providers/seventv/eventapi/Subscription.cpp @@ -0,0 +1,105 @@ +#include "providers/seventv/eventapi/Subscription.hpp" + +#include +#include +#include + +#include +#include + +namespace { + +using namespace chatterino::seventv::eventapi; + +const char *typeToString(SubscriptionType type) +{ + return magic_enum::enum_name(type).data(); +} + +QJsonObject createDataJson(const char *typeName, const Condition &condition) +{ + QJsonObject data; + data["type"] = typeName; + data["condition"] = std::visit( + [](const auto &c) { + return c.encode(); + }, + condition); + return data; +} + +} // namespace + +namespace chatterino::seventv::eventapi { + +bool Subscription::operator==(const Subscription &rhs) const +{ + return std::tie(this->condition, this->type) == + std::tie(rhs.condition, rhs.type); +} + +bool Subscription::operator!=(const Subscription &rhs) const +{ + return !(rhs == *this); +} + +QByteArray Subscription::encodeSubscribe() const +{ + const auto *typeName = typeToString(this->type); + QJsonObject root; + root["op"] = (int)Opcode::Subscribe; + root["d"] = createDataJson(typeName, this->condition); + return QJsonDocument(root).toJson(); +} + +QByteArray Subscription::encodeUnsubscribe() const +{ + const auto *typeName = typeToString(this->type); + QJsonObject root; + root["op"] = (int)Opcode::Unsubscribe; + root["d"] = createDataJson(typeName, this->condition); + return QJsonDocument(root).toJson(); +} + +QDebug &operator<<(QDebug &dbg, const Subscription &subscription) +{ + std::visit( + [&](const auto &cond) { + dbg << "Subscription{ condition:" << cond + << "type:" << magic_enum::enum_name(subscription.type).data() + << '}'; + }, + subscription.condition); + return dbg; +} + +ObjectIDCondition::ObjectIDCondition(QString objectID) + : objectID(std::move(objectID)) +{ +} + +QJsonObject ObjectIDCondition::encode() const +{ + QJsonObject obj; + obj["object_id"] = this->objectID; + + return obj; +} + +bool ObjectIDCondition::operator==(const ObjectIDCondition &rhs) const +{ + return this->objectID == rhs.objectID; +} + +bool ObjectIDCondition::operator!=(const ObjectIDCondition &rhs) const +{ + return !(*this == rhs); +} + +QDebug &operator<<(QDebug &dbg, const ObjectIDCondition &condition) +{ + dbg << "{ objectID:" << condition.objectID << "}"; + return dbg; +} + +} // namespace chatterino::seventv::eventapi diff --git a/src/providers/seventv/eventapi/Subscription.hpp b/src/providers/seventv/eventapi/Subscription.hpp new file mode 100644 index 000000000..53143fbd8 --- /dev/null +++ b/src/providers/seventv/eventapi/Subscription.hpp @@ -0,0 +1,106 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace chatterino::seventv::eventapi { + +// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#subscription-types +enum class SubscriptionType { + UpdateEmoteSet, + UpdateUser, + + INVALID, +}; + +// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#opcodes +enum class Opcode { + Dispatch = 0, + Hello = 1, + Heartbeat = 2, + Reconnect = 4, + Ack = 5, + Error = 6, + EndOfStream = 7, + Identify = 33, + Resume = 34, + Subscribe = 35, + Unsubscribe = 36, + Signal = 37, +}; + +struct ObjectIDCondition { + ObjectIDCondition(QString objectID); + + QString objectID; + + QJsonObject encode() const; + + friend QDebug &operator<<(QDebug &dbg, const ObjectIDCondition &condition); + bool operator==(const ObjectIDCondition &rhs) const; + bool operator!=(const ObjectIDCondition &rhs) const; +}; + +using Condition = std::variant; + +struct Subscription { + bool operator==(const Subscription &rhs) const; + bool operator!=(const Subscription &rhs) const; + Condition condition; + SubscriptionType type; + + QByteArray encodeSubscribe() const; + QByteArray encodeUnsubscribe() const; + + friend QDebug &operator<<(QDebug &dbg, const Subscription &subscription); +}; + +} // namespace chatterino::seventv::eventapi + +template <> +constexpr magic_enum::customize::customize_t magic_enum::customize::enum_name< + chatterino::seventv::eventapi::SubscriptionType>( + chatterino::seventv::eventapi::SubscriptionType value) noexcept +{ + using chatterino::seventv::eventapi::SubscriptionType; + switch (value) + { + case SubscriptionType::UpdateEmoteSet: + return "emote_set.update"; + case SubscriptionType::UpdateUser: + return "user.update"; + + default: + return default_tag; + } +} + +namespace std { + +template <> +struct hash { + size_t operator()( + const chatterino::seventv::eventapi::ObjectIDCondition &c) const + { + return (size_t)qHash(c.objectID); + } +}; + +template <> +struct hash { + size_t operator()( + const chatterino::seventv::eventapi::Subscription &sub) const + { + const size_t conditionHash = + std::hash{}( + sub.condition); + return (size_t)qHash(conditionHash, qHash((int)sub.type)); + } +}; + +} // namespace std diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 5e3892ed6..ec8405c1a 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -18,7 +18,7 @@ #include "providers/bttv/BttvLiveUpdates.hpp" #include "providers/bttv/liveupdates/BttvLiveUpdateMessages.hpp" #include "providers/RecentMessagesApi.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp" +#include "providers/seventv/eventapi/Dispatch.hpp" #include "providers/seventv/SeventvEmotes.hpp" #include "providers/seventv/SeventvEventAPI.hpp" #include "providers/twitch/api/Helix.hpp" @@ -121,16 +121,6 @@ TwitchChannel::TwitchChannel(const QString &name) this->loadRecentMessagesReconnect(); }); - this->destroyed.connect([this]() { - getApp()->twitch->dropSeventvChannel(this->seventvUserID_, - this->seventvEmoteSetID_); - - if (getApp()->twitch->bttvLiveUpdates) - { - getApp()->twitch->bttvLiveUpdates->partChannel(this->roomId()); - } - }); - this->messageRemovedFromStart.connect([this](MessagePtr &msg) { if (msg->replyThread) { @@ -169,6 +159,17 @@ TwitchChannel::TwitchChannel(const QString &name) #endif } +TwitchChannel::~TwitchChannel() +{ + getApp()->twitch->dropSeventvChannel(this->seventvUserID_, + this->seventvEmoteSetID_); + + if (getApp()->twitch->bttvLiveUpdates) + { + getApp()->twitch->bttvLiveUpdates->partChannel(this->roomId()); + } +} + void TwitchChannel::initialize() { this->fetchDisplayName(); @@ -691,7 +692,7 @@ void TwitchChannel::removeBttvEmote( } void TwitchChannel::addSeventvEmote( - const SeventvEventAPIEmoteAddDispatch &dispatch) + const seventv::eventapi::EmoteAddDispatch &dispatch) { if (!SeventvEmotes::addEmote(this->seventvEmotes_, dispatch)) { @@ -703,7 +704,7 @@ void TwitchChannel::addSeventvEmote( } void TwitchChannel::updateSeventvEmote( - const SeventvEventAPIEmoteUpdateDispatch &dispatch) + const seventv::eventapi::EmoteUpdateDispatch &dispatch) { if (!SeventvEmotes::updateEmote(this->seventvEmotes_, dispatch)) { @@ -717,7 +718,7 @@ void TwitchChannel::updateSeventvEmote( } void TwitchChannel::removeSeventvEmote( - const SeventvEventAPIEmoteRemoveDispatch &dispatch) + const seventv::eventapi::EmoteRemoveDispatch &dispatch) { auto removed = SeventvEmotes::removeEmote(this->seventvEmotes_, dispatch); if (!removed) @@ -730,7 +731,7 @@ void TwitchChannel::removeSeventvEmote( } void TwitchChannel::updateSeventvUser( - const SeventvEventAPIUserConnectionUpdateDispatch &dispatch) + const seventv::eventapi::UserConnectionUpdateDispatch &dispatch) { if (dispatch.connectionIndex != this->seventvUserTwitchConnectionIndex_) { diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index 7435f06d8..2074ad2c1 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -49,11 +49,15 @@ class FfzEmotes; class BttvEmotes; struct BttvLiveUpdateEmoteUpdateAddMessage; struct BttvLiveUpdateEmoteRemoveMessage; + class SeventvEmotes; -struct SeventvEventAPIEmoteAddDispatch; -struct SeventvEventAPIEmoteUpdateDispatch; -struct SeventvEventAPIEmoteRemoveDispatch; -struct SeventvEventAPIUserConnectionUpdateDispatch; +namespace seventv::eventapi { + struct EmoteAddDispatch; + struct EmoteUpdateDispatch; + struct EmoteRemoveDispatch; + struct UserConnectionUpdateDispatch; +} // namespace seventv::eventapi + struct ChannelPointReward; class MessageThread; struct CheerEmoteSet; @@ -98,6 +102,7 @@ public: }; explicit TwitchChannel(const QString &channelName); + ~TwitchChannel() override; void initialize(); @@ -149,14 +154,16 @@ public: void removeBttvEmote(const BttvLiveUpdateEmoteRemoveMessage &message); /** Adds a 7TV channel emote to this channel. */ - void addSeventvEmote(const SeventvEventAPIEmoteAddDispatch &dispatch); + void addSeventvEmote(const seventv::eventapi::EmoteAddDispatch &dispatch); /** Updates a 7TV channel emote's name in this channel */ - void updateSeventvEmote(const SeventvEventAPIEmoteUpdateDispatch &dispatch); + void updateSeventvEmote( + const seventv::eventapi::EmoteUpdateDispatch &dispatch); /** Removes a 7TV channel emote from this channel */ - void removeSeventvEmote(const SeventvEventAPIEmoteRemoveDispatch &dispatch); + void removeSeventvEmote( + const seventv::eventapi::EmoteRemoveDispatch &dispatch); /** Updates the current 7TV user. Currently, only the emote-set is updated. */ void updateSeventvUser( - const SeventvEventAPIUserConnectionUpdateDispatch &dispatch); + const seventv::eventapi::UserConnectionUpdateDispatch &dispatch); // Update the channel's 7TV information (the channel's 7TV user ID and emote set ID) void updateSeventvData(const QString &newUserID, diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index 70d197f69..c608f9e0b 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -7,7 +7,7 @@ #include "messages/Message.hpp" #include "messages/MessageBuilder.hpp" #include "providers/bttv/BttvLiveUpdates.hpp" -#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp" +#include "providers/seventv/eventapi/Subscription.hpp" #include "providers/seventv/SeventvEventAPI.hpp" #include "providers/twitch/api/Helix.hpp" #include "providers/twitch/ChannelPointReward.hpp" diff --git a/tests/src/SeventvEventAPI.cpp b/tests/src/SeventvEventAPI.cpp index bdde3207a..4c47e34c3 100644 --- a/tests/src/SeventvEventAPI.cpp +++ b/tests/src/SeventvEventAPI.cpp @@ -1,14 +1,15 @@ #include "providers/seventv/SeventvEventAPI.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIClient.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIMessage.hpp" +#include "providers/seventv/eventapi/Client.hpp" +#include "providers/seventv/eventapi/Dispatch.hpp" +#include "providers/seventv/eventapi/Message.hpp" #include #include #include using namespace chatterino; +using namespace chatterino::seventv::eventapi; using namespace std::chrono_literals; const QString EMOTE_SET_A = "60b39e943e203cc169dfc106"; @@ -21,10 +22,10 @@ TEST(SeventvEventAPI, AllEvents) auto *eventAPI = new SeventvEventAPI(host, std::chrono::milliseconds(1000)); eventAPI->start(); - boost::optional addDispatch; - boost::optional updateDispatch; - boost::optional removeDispatch; - boost::optional userDispatch; + boost::optional addDispatch; + boost::optional updateDispatch; + boost::optional removeDispatch; + boost::optional userDispatch; eventAPI->signals_.emoteAdded.connect([&](const auto &d) { addDispatch = d;