2022-05-07 17:22:39 +02:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "providers/twitch/PubSubClientOptions.hpp"
|
|
|
|
#include "providers/twitch/PubSubWebsocket.hpp"
|
|
|
|
#include "util/ExponentialBackoff.hpp"
|
2022-12-18 15:36:39 +01:00
|
|
|
#include "util/QStringHash.hpp"
|
2022-05-07 17:22:39 +02:00
|
|
|
|
Sort and force grouping of includes (#4172)
This change enforces strict include grouping using IncludeCategories
In addition to adding this to the .clang-format file and applying it in the tests/src and src directories, I also did the following small changes:
In ChatterSet.hpp, I changed lrucache to a <>include
In Irc2.hpp, I change common/SignalVector.hpp to a "project-include"
In AttachedWindow.cpp, NativeMessaging.cpp, WindowsHelper.hpp, BaseWindow.cpp, and StreamerMode.cpp, I disabled clang-format for the windows-includes
In WindowDescriptors.hpp, I added the missing vector include. It was previously not needed because the include was handled by another file that was previously included first.
clang-format minimum version has been bumped, so Ubuntu version used in the check-formatting job has been bumped to 22.04 (which is the latest LTS)
2022-11-27 19:32:53 +01:00
|
|
|
#include <pajlada/signals/signal.hpp>
|
2022-05-07 17:22:39 +02:00
|
|
|
#include <QJsonObject>
|
|
|
|
#include <QString>
|
|
|
|
#include <websocketpp/client.hpp>
|
|
|
|
|
|
|
|
#include <atomic>
|
|
|
|
#include <chrono>
|
|
|
|
#include <map>
|
|
|
|
#include <memory>
|
2023-10-08 18:50:48 +02:00
|
|
|
#include <optional>
|
2022-05-07 17:22:39 +02:00
|
|
|
#include <thread>
|
|
|
|
#include <unordered_map>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
namespace chatterino {
|
|
|
|
|
2022-12-18 15:36:39 +01:00
|
|
|
class TwitchAccount;
|
2022-12-31 15:41:01 +01:00
|
|
|
class PubSubClient;
|
|
|
|
|
|
|
|
struct ClearChatAction;
|
|
|
|
struct DeleteAction;
|
|
|
|
struct ModeChangedAction;
|
|
|
|
struct ModerationStateAction;
|
|
|
|
struct BanAction;
|
|
|
|
struct UnbanAction;
|
|
|
|
struct PubSubAutoModQueueMessage;
|
|
|
|
struct AutomodAction;
|
|
|
|
struct AutomodUserAction;
|
|
|
|
struct AutomodInfoAction;
|
2023-12-31 11:44:55 +01:00
|
|
|
struct PubSubLowTrustUsersMessage;
|
2022-12-31 15:41:01 +01:00
|
|
|
struct PubSubWhisperMessage;
|
|
|
|
|
|
|
|
struct PubSubListenMessage;
|
|
|
|
struct PubSubMessage;
|
|
|
|
struct PubSubMessageMessage;
|
2022-12-18 15:36:39 +01:00
|
|
|
|
2024-01-06 13:18:37 +01:00
|
|
|
/**
|
|
|
|
* This handles the Twitch PubSub connection
|
|
|
|
*
|
|
|
|
* Known issues:
|
|
|
|
* - Upon closing a channel, we don't unsubscribe to its pubsub connections
|
|
|
|
* - Stop is never called, meaning we never do a clean shutdown
|
|
|
|
*/
|
2022-05-07 17:22:39 +02:00
|
|
|
class PubSub
|
|
|
|
{
|
|
|
|
using WebsocketMessagePtr =
|
|
|
|
websocketpp::config::asio_tls_client::message_type::ptr;
|
|
|
|
using WebsocketContextPtr =
|
|
|
|
websocketpp::lib::shared_ptr<boost::asio::ssl::context>;
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
using Signal =
|
|
|
|
pajlada::Signals::Signal<T>; // type-id is vector<T, Alloc<T>>
|
|
|
|
|
|
|
|
struct NonceInfo {
|
|
|
|
std::weak_ptr<PubSubClient> client;
|
|
|
|
QString messageType; // e.g. LISTEN or UNLISTEN
|
|
|
|
std::vector<QString> topics;
|
|
|
|
std::vector<QString>::size_type topicCount;
|
|
|
|
};
|
|
|
|
|
|
|
|
WebsocketClient websocketClient;
|
2024-01-06 13:18:37 +01:00
|
|
|
std::unique_ptr<std::thread> thread;
|
2022-05-07 17:22:39 +02:00
|
|
|
|
|
|
|
// Account credentials
|
2024-01-06 13:18:37 +01:00
|
|
|
// Set from setAccount
|
2022-05-07 17:22:39 +02:00
|
|
|
QString token_;
|
|
|
|
QString userID_;
|
|
|
|
|
|
|
|
public:
|
|
|
|
PubSub(const QString &host,
|
|
|
|
std::chrono::seconds pingInterval = std::chrono::seconds(15));
|
2024-01-06 13:18:37 +01:00
|
|
|
~PubSub();
|
2022-05-07 17:22:39 +02:00
|
|
|
|
2024-01-06 13:18:37 +01:00
|
|
|
PubSub(const PubSub &) = delete;
|
|
|
|
PubSub(PubSub &&) = delete;
|
|
|
|
PubSub &operator=(const PubSub &) = delete;
|
|
|
|
PubSub &operator=(PubSub &&) = delete;
|
2022-05-07 17:22:39 +02:00
|
|
|
|
2024-01-06 13:18:37 +01:00
|
|
|
void setAccount(std::shared_ptr<TwitchAccount> account);
|
2022-05-07 17:22:39 +02:00
|
|
|
|
|
|
|
void start();
|
|
|
|
void stop();
|
|
|
|
|
2024-01-06 13:18:37 +01:00
|
|
|
struct {
|
|
|
|
Signal<ClearChatAction> chatCleared;
|
|
|
|
Signal<DeleteAction> messageDeleted;
|
|
|
|
Signal<ModeChangedAction> modeChanged;
|
|
|
|
Signal<ModerationStateAction> moderationStateChanged;
|
|
|
|
|
|
|
|
Signal<BanAction> userBanned;
|
|
|
|
Signal<UnbanAction> userUnbanned;
|
|
|
|
|
|
|
|
Signal<PubSubLowTrustUsersMessage> suspiciousMessageReceived;
|
|
|
|
Signal<PubSubLowTrustUsersMessage> suspiciousTreatmentUpdated;
|
|
|
|
|
|
|
|
// Message caught by automod
|
|
|
|
// channelID
|
|
|
|
pajlada::Signals::Signal<PubSubAutoModQueueMessage, QString>
|
|
|
|
autoModMessageCaught;
|
|
|
|
|
|
|
|
// Message blocked by moderator
|
|
|
|
Signal<AutomodAction> autoModMessageBlocked;
|
|
|
|
|
|
|
|
Signal<AutomodUserAction> automodUserMessage;
|
|
|
|
Signal<AutomodInfoAction> automodInfoMessage;
|
|
|
|
} moderation;
|
2022-05-07 17:22:39 +02:00
|
|
|
|
|
|
|
struct {
|
2024-01-06 13:18:37 +01:00
|
|
|
// Parsing should be done in PubSubManager as well,
|
|
|
|
// but for now we just send the raw data
|
|
|
|
Signal<const PubSubWhisperMessage &> received;
|
|
|
|
Signal<const PubSubWhisperMessage &> sent;
|
|
|
|
} whisper;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
Signal<const QJsonObject &> redeemed;
|
|
|
|
} pointReward;
|
2022-05-07 17:22:39 +02:00
|
|
|
|
2023-12-31 11:44:55 +01:00
|
|
|
/**
|
|
|
|
* Listen to incoming whispers for the currently logged in user.
|
|
|
|
* This topic is relevant for everyone.
|
|
|
|
*
|
|
|
|
* PubSub topic: whispers.{currentUserID}
|
|
|
|
*/
|
2022-05-07 17:22:39 +02:00
|
|
|
bool listenToWhispers();
|
2024-01-06 13:18:37 +01:00
|
|
|
void unlistenWhispers();
|
2023-12-31 11:44:55 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Listen to moderation actions in the given channel.
|
|
|
|
* This topic is relevant for everyone.
|
|
|
|
* For moderators, this topic includes blocked/permitted terms updates,
|
|
|
|
* roomstate changes, general mod/vip updates, all bans/timeouts/deletions.
|
|
|
|
* For normal users, this topic includes moderation actions that are targetted at the local user:
|
|
|
|
* automod catching a user's sent message, a moderator approving or denying their caught messages,
|
|
|
|
* the user gaining/losing mod/vip, the user receiving a ban/timeout/deletion.
|
|
|
|
*
|
|
|
|
* PubSub topic: chat_moderator_actions.{currentUserID}.{channelID}
|
|
|
|
*/
|
2022-05-07 17:22:39 +02:00
|
|
|
void listenToChannelModerationActions(const QString &channelID);
|
2024-01-06 13:18:37 +01:00
|
|
|
void unlistenChannelModerationActions();
|
2023-12-31 11:44:55 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Listen to Automod events in the given channel.
|
|
|
|
* This topic is only relevant for moderators.
|
|
|
|
* This will send events about incoming messages that
|
|
|
|
* are caught by Automod.
|
|
|
|
*
|
|
|
|
* PubSub topic: automod-queue.{currentUserID}.{channelID}
|
|
|
|
*/
|
2022-05-07 17:22:39 +02:00
|
|
|
void listenToAutomod(const QString &channelID);
|
2024-01-06 13:18:37 +01:00
|
|
|
void unlistenAutomod();
|
2022-05-07 17:22:39 +02:00
|
|
|
|
2023-12-31 11:44:55 +01:00
|
|
|
/**
|
|
|
|
* Listen to Low Trust events in the given channel.
|
|
|
|
* This topic is only relevant for moderators.
|
|
|
|
* This will fire events about suspicious treatment updates
|
|
|
|
* and messages sent by restricted/monitored users.
|
|
|
|
*
|
|
|
|
* PubSub topic: low-trust-users.{currentUserID}.{channelID}
|
|
|
|
*/
|
|
|
|
void listenToLowTrustUsers(const QString &channelID);
|
2024-01-06 13:18:37 +01:00
|
|
|
void unlistenLowTrustUsers();
|
2023-12-31 11:44:55 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Listen to incoming channel point redemptions in the given channel.
|
|
|
|
* This topic is relevant for everyone.
|
|
|
|
*
|
|
|
|
* PubSub topic: community-points-channel-v1.{channelID}
|
|
|
|
*/
|
2022-05-07 17:22:39 +02:00
|
|
|
void listenToChannelPointRewards(const QString &channelID);
|
2024-01-06 13:18:37 +01:00
|
|
|
void unlistenChannelPointRewards();
|
2022-05-07 17:22:39 +02:00
|
|
|
|
|
|
|
struct {
|
|
|
|
std::atomic<uint32_t> connectionsClosed{0};
|
|
|
|
std::atomic<uint32_t> connectionsOpened{0};
|
|
|
|
std::atomic<uint32_t> connectionsFailed{0};
|
|
|
|
std::atomic<uint32_t> messagesReceived{0};
|
|
|
|
std::atomic<uint32_t> messagesFailedToParse{0};
|
|
|
|
std::atomic<uint32_t> failedListenResponses{0};
|
|
|
|
std::atomic<uint32_t> listenResponses{0};
|
|
|
|
std::atomic<uint32_t> unlistenResponses{0};
|
|
|
|
} diag;
|
|
|
|
|
2024-01-06 13:18:37 +01:00
|
|
|
private:
|
|
|
|
/**
|
|
|
|
* Unlistens to all topics matching the prefix in all clients
|
|
|
|
*/
|
|
|
|
void unlistenPrefix(const QString &prefix);
|
|
|
|
|
2022-05-07 17:22:39 +02:00
|
|
|
void listenToTopic(const QString &topic);
|
|
|
|
|
|
|
|
void listen(PubSubListenMessage msg);
|
|
|
|
bool tryListen(PubSubListenMessage msg);
|
|
|
|
|
|
|
|
bool isListeningToTopic(const QString &topic);
|
|
|
|
|
|
|
|
void addClient();
|
2024-01-06 13:18:37 +01:00
|
|
|
|
|
|
|
std::vector<QString> requests;
|
|
|
|
|
2022-05-07 17:22:39 +02:00
|
|
|
std::atomic<bool> addingClient{false};
|
|
|
|
ExponentialBackoff<5> connectBackoff{std::chrono::milliseconds(1000)};
|
|
|
|
|
|
|
|
std::map<WebsocketHandle, std::shared_ptr<PubSubClient>,
|
|
|
|
std::owner_less<WebsocketHandle>>
|
|
|
|
clients;
|
|
|
|
|
|
|
|
std::unordered_map<
|
|
|
|
QString, std::function<void(const QJsonObject &, const QString &)>>
|
|
|
|
moderationActionHandlers;
|
|
|
|
|
|
|
|
std::unordered_map<
|
|
|
|
QString, std::function<void(const QJsonObject &, const QString &)>>
|
|
|
|
channelTermsActionHandlers;
|
|
|
|
|
|
|
|
void onMessage(websocketpp::connection_hdl hdl, WebsocketMessagePtr msg);
|
|
|
|
void onConnectionOpen(websocketpp::connection_hdl hdl);
|
|
|
|
void onConnectionFail(websocketpp::connection_hdl hdl);
|
|
|
|
void onConnectionClose(websocketpp::connection_hdl hdl);
|
|
|
|
WebsocketContextPtr onTLSInit(websocketpp::connection_hdl hdl);
|
|
|
|
|
|
|
|
void handleResponse(const PubSubMessage &message);
|
|
|
|
void handleListenResponse(const NonceInfo &info, bool failed);
|
|
|
|
void handleUnlistenResponse(const NonceInfo &info, bool failed);
|
|
|
|
void handleMessageResponse(const PubSubMessageMessage &message);
|
|
|
|
|
|
|
|
// Register a nonce for a specific client
|
|
|
|
void registerNonce(QString nonce, NonceInfo nonceInfo);
|
|
|
|
|
|
|
|
// Find client associated with a nonce
|
2023-10-08 18:50:48 +02:00
|
|
|
std::optional<NonceInfo> findNonceInfo(QString nonce);
|
2022-05-07 17:22:39 +02:00
|
|
|
|
|
|
|
std::unordered_map<QString, NonceInfo> nonces_;
|
|
|
|
|
|
|
|
void runThread();
|
|
|
|
|
|
|
|
std::shared_ptr<boost::asio::io_service::work> work{nullptr};
|
|
|
|
|
|
|
|
const QString host_;
|
|
|
|
const PubSubClientOptions clientOptions_;
|
|
|
|
|
|
|
|
bool stopping_{false};
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace chatterino
|