mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
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 <rasmus.karlsson@pajlada.com>
This commit is contained in:
parent
6516116244
commit
0c74ab17ed
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<EmoteMap>())
|
||||
{
|
||||
|
@ -401,7 +404,7 @@ void SeventvEmotes::loadChannelEmotes(
|
|||
|
||||
boost::optional<EmotePtr> SeventvEmotes::addEmote(
|
||||
Atomic<std::shared_ptr<const EmoteMap>> &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<EmotePtr> SeventvEmotes::addEmote(
|
|||
|
||||
boost::optional<EmotePtr> SeventvEmotes::updateEmote(
|
||||
Atomic<std::shared_ptr<const EmoteMap>> &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<EmotePtr> SeventvEmotes::updateEmote(
|
|||
|
||||
boost::optional<EmotePtr> SeventvEmotes::removeEmote(
|
||||
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
||||
const SeventvEventAPIEmoteRemoveDispatch &dispatch)
|
||||
const EmoteRemoveDispatch &dispatch)
|
||||
{
|
||||
// This copies the map.
|
||||
EmoteMap updatedMap = *map.get();
|
||||
|
|
|
@ -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<EmotePtr> addEmote(
|
||||
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
||||
const SeventvEventAPIEmoteAddDispatch &dispatch);
|
||||
const seventv::eventapi::EmoteAddDispatch &dispatch);
|
||||
|
||||
/**
|
||||
* Updates an emote in this `map`.
|
||||
|
@ -97,7 +100,7 @@ public:
|
|||
*/
|
||||
static boost::optional<EmotePtr> updateEmote(
|
||||
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
||||
const SeventvEventAPIEmoteUpdateDispatch &dispatch);
|
||||
const seventv::eventapi::EmoteUpdateDispatch &dispatch);
|
||||
|
||||
/**
|
||||
* Removes an emote from this `map`.
|
||||
|
@ -108,7 +111,7 @@ public:
|
|||
*/
|
||||
static boost::optional<EmotePtr> removeEmote(
|
||||
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
||||
const SeventvEventAPIEmoteRemoveDispatch &dispatch);
|
||||
const seventv::eventapi::EmoteRemoveDispatch &dispatch);
|
||||
|
||||
/** Fetches an emote-set by its id */
|
||||
static void getEmoteSet(
|
||||
|
|
|
@ -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 <QJsonArray>
|
||||
|
||||
|
@ -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<BasicPubSubClient<SeventvEventAPISubscription>>
|
||||
SeventvEventAPI::createClient(liveupdates::WebsocketClient &client,
|
||||
websocketpp::connection_hdl hdl)
|
||||
std::shared_ptr<BasicPubSubClient<Subscription>> SeventvEventAPI::createClient(
|
||||
liveupdates::WebsocketClient &client, websocketpp::connection_hdl hdl)
|
||||
{
|
||||
auto shared = std::make_shared<SeventvEventAPIClient>(
|
||||
client, hdl, this->heartbeatInterval_);
|
||||
return std::static_pointer_cast<
|
||||
BasicPubSubClient<SeventvEventAPISubscription>>(std::move(shared));
|
||||
auto shared =
|
||||
std::make_shared<Client>(client, hdl, this->heartbeatInterval_);
|
||||
return std::static_pointer_cast<BasicPubSubClient<Subscription>>(
|
||||
std::move(shared));
|
||||
}
|
||||
|
||||
void SeventvEventAPI::onMessage(
|
||||
websocketpp::connection_hdl hdl,
|
||||
BasicPubSubManager<SeventvEventAPISubscription>::WebsocketMessagePtr msg)
|
||||
BasicPubSubManager<Subscription>::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<SeventvEventAPIClient *>(client.get()))
|
||||
if (auto *stvClient = dynamic_cast<Client *>(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<SeventvEventAPIClient *>(client.get()))
|
||||
if (auto *stvClient = dynamic_cast<Client *>(client.get()))
|
||||
{
|
||||
stvClient->handleHeartbeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SeventvEventAPIOpcode::Dispatch: {
|
||||
auto dispatch = message.toInner<SeventvEventAPIDispatch>();
|
||||
case Opcode::Dispatch: {
|
||||
auto dispatch = message.toInner<Dispatch>();
|
||||
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<SeventvEventAPIClient *>(client.get()))
|
||||
if (auto *stvClient = dynamic_cast<Client *>(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
|
||||
|
|
|
@ -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<SeventvEventAPISubscription>
|
||||
class SeventvEventAPI
|
||||
: public BasicPubSubManager<seventv::eventapi::Subscription>
|
||||
{
|
||||
template <typename T>
|
||||
using Signal =
|
||||
|
@ -27,10 +30,10 @@ public:
|
|||
std::chrono::milliseconds(25000));
|
||||
|
||||
struct {
|
||||
Signal<SeventvEventAPIEmoteAddDispatch> emoteAdded;
|
||||
Signal<SeventvEventAPIEmoteUpdateDispatch> emoteUpdated;
|
||||
Signal<SeventvEventAPIEmoteRemoveDispatch> emoteRemoved;
|
||||
Signal<SeventvEventAPIUserConnectionUpdateDispatch> userUpdated;
|
||||
Signal<seventv::eventapi::EmoteAddDispatch> emoteAdded;
|
||||
Signal<seventv::eventapi::EmoteUpdateDispatch> emoteUpdated;
|
||||
Signal<seventv::eventapi::EmoteRemoveDispatch> emoteRemoved;
|
||||
Signal<seventv::eventapi::UserConnectionUpdateDispatch> userUpdated;
|
||||
} signals_; // NOLINT(readability-identifier-naming)
|
||||
|
||||
/**
|
||||
|
@ -48,18 +51,23 @@ public:
|
|||
void unsubscribeEmoteSet(const QString &id);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<BasicPubSubClient<SeventvEventAPISubscription>>
|
||||
std::shared_ptr<BasicPubSubClient<seventv::eventapi::Subscription>>
|
||||
createClient(liveupdates::WebsocketClient &client,
|
||||
websocketpp::connection_hdl hdl) override;
|
||||
void onMessage(
|
||||
websocketpp::connection_hdl hdl,
|
||||
BasicPubSubManager<SeventvEventAPISubscription>::WebsocketMessagePtr
|
||||
BasicPubSubManager<seventv::eventapi::Subscription>::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<QString> subscribedEmoteSets_;
|
||||
/** user ids */
|
||||
std::unordered_set<QString> subscribedUsers_;
|
||||
std::chrono::milliseconds heartbeatInterval_;
|
||||
};
|
||||
|
|
|
@ -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 <utility>
|
||||
|
||||
namespace chatterino {
|
||||
namespace chatterino::seventv::eventapi {
|
||||
|
||||
SeventvEventAPIClient::SeventvEventAPIClient(
|
||||
liveupdates::WebsocketClient &websocketClient,
|
||||
liveupdates::WebsocketHandle handle,
|
||||
std::chrono::milliseconds heartbeatInterval)
|
||||
: BasicPubSubClient<SeventvEventAPISubscription>(websocketClient,
|
||||
std::move(handle))
|
||||
Client::Client(liveupdates::WebsocketClient &websocketClient,
|
||||
liveupdates::WebsocketHandle handle,
|
||||
std::chrono::milliseconds heartbeatInterval)
|
||||
: BasicPubSubClient<Subscription>(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<SeventvEventAPIClient>(
|
||||
this->shared_from_this());
|
||||
auto self = std::dynamic_pointer_cast<Client>(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
|
|
@ -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<SeventvEventAPISubscription>
|
||||
} // namespace chatterino
|
||||
|
||||
namespace chatterino::seventv::eventapi {
|
||||
|
||||
class Client : public BasicPubSubClient<Subscription>
|
||||
{
|
||||
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
|
|
@ -1,21 +1,20 @@
|
|||
#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp"
|
||||
#include "providers/seventv/eventapi/Dispatch.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace chatterino {
|
||||
namespace chatterino::seventv::eventapi {
|
||||
|
||||
SeventvEventAPIDispatch::SeventvEventAPIDispatch(QJsonObject obj)
|
||||
: type(magic_enum::enum_cast<SeventvEventAPISubscriptionType>(
|
||||
Dispatch::Dispatch(QJsonObject obj)
|
||||
: type(magic_enum::enum_cast<SubscriptionType>(
|
||||
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
|
70
src/providers/seventv/eventapi/Dispatch.hpp
Normal file
70
src/providers/seventv/eventapi/Dispatch.hpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
#pragma once
|
||||
|
||||
#include "providers/seventv/eventapi/Subscription.hpp"
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
|
||||
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
|
11
src/providers/seventv/eventapi/Message.cpp
Normal file
11
src/providers/seventv/eventapi/Message.cpp
Normal file
|
@ -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
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp"
|
||||
#include "providers/seventv/eventapi/Subscription.hpp"
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <magic_enum.hpp>
|
||||
|
@ -8,27 +8,26 @@
|
|||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
|
||||
namespace chatterino {
|
||||
namespace chatterino::seventv::eventapi {
|
||||
|
||||
struct SeventvEventAPIMessage {
|
||||
struct Message {
|
||||
QJsonObject data;
|
||||
|
||||
SeventvEventAPIOpcode op;
|
||||
Opcode op;
|
||||
|
||||
SeventvEventAPIMessage(QJsonObject _json);
|
||||
Message(QJsonObject _json);
|
||||
|
||||
template <class InnerClass>
|
||||
boost::optional<InnerClass> toInner();
|
||||
};
|
||||
|
||||
template <class InnerClass>
|
||||
boost::optional<InnerClass> SeventvEventAPIMessage::toInner()
|
||||
boost::optional<InnerClass> Message::toInner()
|
||||
{
|
||||
return InnerClass{this->data};
|
||||
}
|
||||
|
||||
static boost::optional<SeventvEventAPIMessage> parseSeventvEventAPIBaseMessage(
|
||||
const QString &blob)
|
||||
static boost::optional<Message> parseBaseMessage(const QString &blob)
|
||||
{
|
||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(blob.toUtf8()));
|
||||
|
||||
|
@ -37,7 +36,7 @@ static boost::optional<SeventvEventAPIMessage> parseSeventvEventAPIBaseMessage(
|
|||
return boost::none;
|
||||
}
|
||||
|
||||
return SeventvEventAPIMessage(jsonDoc.object());
|
||||
return Message(jsonDoc.object());
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
} // namespace chatterino::seventv::eventapi
|
|
@ -1,72 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp"
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
|
||||
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
|
|
@ -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
|
|
@ -1,80 +0,0 @@
|
|||
#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <tuple>
|
||||
|
||||
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
|
|
@ -1,76 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
|
||||
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<chatterino::SeventvEventAPISubscription> {
|
||||
size_t operator()(const chatterino::SeventvEventAPISubscription &sub) const
|
||||
{
|
||||
return (size_t)qHash(sub.condition, qHash((int)sub.type));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
105
src/providers/seventv/eventapi/Subscription.cpp
Normal file
105
src/providers/seventv/eventapi/Subscription.cpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
#include "providers/seventv/eventapi/Subscription.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
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
|
106
src/providers/seventv/eventapi/Subscription.hpp
Normal file
106
src/providers/seventv/eventapi/Subscription.hpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
#pragma once
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
|
||||
#include <variant>
|
||||
|
||||
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<ObjectIDCondition>;
|
||||
|
||||
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<chatterino::seventv::eventapi::ObjectIDCondition> {
|
||||
size_t operator()(
|
||||
const chatterino::seventv::eventapi::ObjectIDCondition &c) const
|
||||
{
|
||||
return (size_t)qHash(c.objectID);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<chatterino::seventv::eventapi::Subscription> {
|
||||
size_t operator()(
|
||||
const chatterino::seventv::eventapi::Subscription &sub) const
|
||||
{
|
||||
const size_t conditionHash =
|
||||
std::hash<chatterino::seventv::eventapi::Condition>{}(
|
||||
sub.condition);
|
||||
return (size_t)qHash(conditionHash, qHash((int)sub.type));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
|
@ -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_)
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 <boost/optional.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
#include <QString>
|
||||
|
||||
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<SeventvEventAPIEmoteAddDispatch> addDispatch;
|
||||
boost::optional<SeventvEventAPIEmoteUpdateDispatch> updateDispatch;
|
||||
boost::optional<SeventvEventAPIEmoteRemoveDispatch> removeDispatch;
|
||||
boost::optional<SeventvEventAPIUserConnectionUpdateDispatch> userDispatch;
|
||||
boost::optional<EmoteAddDispatch> addDispatch;
|
||||
boost::optional<EmoteUpdateDispatch> updateDispatch;
|
||||
boost::optional<EmoteRemoveDispatch> removeDispatch;
|
||||
boost::optional<UserConnectionUpdateDispatch> userDispatch;
|
||||
|
||||
eventAPI->signals_.emoteAdded.connect([&](const auto &d) {
|
||||
addDispatch = d;
|
||||
|
|
Loading…
Reference in a new issue