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:
+13
-16
@@ -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
|
||||
+13
-9
@@ -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
|
||||
+18
-21
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
+9
-10
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user