fix: don't use same websocket connection for multiple accounts (#6348)
This commit is contained in:
@@ -52,6 +52,7 @@
|
|||||||
- Bugfix: Fixed some minor typos. (#6196)
|
- Bugfix: Fixed some minor typos. (#6196)
|
||||||
- Bugfix: Fixed inconsistent spaces in messages when using fractional scaling. (#6231, #6254)
|
- Bugfix: Fixed inconsistent spaces in messages when using fractional scaling. (#6231, #6254)
|
||||||
- Bugfix: Fixed eventsub message delete notifications not being affected by "Show deletions of single messages". (#6233)
|
- Bugfix: Fixed eventsub message delete notifications not being affected by "Show deletions of single messages". (#6233)
|
||||||
|
- Bugfix: Fixed an issue where eventsub subscriptions might fail after changing user. (#6348)
|
||||||
- Bugfix: Don't add reply buttons to messages that are invalid reply targets. (#6119)
|
- Bugfix: Don't add reply buttons to messages that are invalid reply targets. (#6119)
|
||||||
- Bugfix: Fixed invalid commands from being forwarded to Helix, making it possible for information to leak (e.g. if you typed `/bann username ban reason` it would be seen by others in chat as `username ban reason`). (#6272, #6330)
|
- Bugfix: Fixed invalid commands from being forwarded to Helix, making it possible for information to leak (e.g. if you typed `/bann username ban reason` it would be seen by others in chat as `username ban reason`). (#6272, #6330)
|
||||||
- Bugfix: Emotes that failed to load their images now show as text. (#6355)
|
- Bugfix: Emotes that failed to load their images now show as text. (#6355)
|
||||||
|
|||||||
@@ -471,6 +471,8 @@ CommandController::CommandController(const Paths &paths)
|
|||||||
this->registerCommand("/debug-invalidate-buffers",
|
this->registerCommand("/debug-invalidate-buffers",
|
||||||
&commands::invalidateBuffers);
|
&commands::invalidateBuffers);
|
||||||
|
|
||||||
|
this->registerCommand("/debug-eventsub", &commands::eventsub);
|
||||||
|
|
||||||
this->registerCommand("/debug-test", &commands::debugTest);
|
this->registerCommand("/debug-test", &commands::debugTest);
|
||||||
|
|
||||||
this->registerCommand("/shield", &commands::shieldModeOn);
|
this->registerCommand("/shield", &commands::shieldModeOn);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "messages/Message.hpp"
|
#include "messages/Message.hpp"
|
||||||
#include "messages/MessageBuilder.hpp"
|
#include "messages/MessageBuilder.hpp"
|
||||||
#include "messages/MessageElement.hpp"
|
#include "messages/MessageElement.hpp"
|
||||||
|
#include "providers/twitch/eventsub/Controller.hpp"
|
||||||
#include "providers/twitch/TwitchChannel.hpp"
|
#include "providers/twitch/TwitchChannel.hpp"
|
||||||
#include "providers/twitch/TwitchIrcServer.hpp"
|
#include "providers/twitch/TwitchIrcServer.hpp"
|
||||||
#include "singletons/Theme.hpp"
|
#include "singletons/Theme.hpp"
|
||||||
@@ -158,6 +159,12 @@ QString invalidateBuffers(const CommandContext & /*ctx*/)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString eventsub(const CommandContext & /*ctx*/)
|
||||||
|
{
|
||||||
|
getApp()->getEventSub()->debug();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
QString debugTest(const CommandContext &ctx)
|
QString debugTest(const CommandContext &ctx)
|
||||||
{
|
{
|
||||||
if (!ctx.channel)
|
if (!ctx.channel)
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ QString incrementImageGeneration(const CommandContext &ctx);
|
|||||||
|
|
||||||
QString invalidateBuffers(const CommandContext &ctx);
|
QString invalidateBuffers(const CommandContext &ctx);
|
||||||
|
|
||||||
|
QString eventsub(const CommandContext &ctx);
|
||||||
|
|
||||||
QString debugTest(const CommandContext &ctx);
|
QString debugTest(const CommandContext &ctx);
|
||||||
|
|
||||||
} // namespace chatterino::commands
|
} // namespace chatterino::commands
|
||||||
|
|||||||
@@ -342,6 +342,9 @@ void TwitchAccountManager::load()
|
|||||||
|
|
||||||
this->currentUsername.connect([this](const QString &newUsername) {
|
this->currentUsername.connect([this](const QString &newUsername) {
|
||||||
auto user = this->findUserByUsername(newUsername);
|
auto user = this->findUserByUsername(newUsername);
|
||||||
|
|
||||||
|
this->currentUserAboutToChange.invoke(this->currentUser_, user);
|
||||||
|
|
||||||
if (user)
|
if (user)
|
||||||
{
|
{
|
||||||
qCDebug(chatterinoTwitch)
|
qCDebug(chatterinoTwitch)
|
||||||
|
|||||||
@@ -55,7 +55,15 @@ public:
|
|||||||
|
|
||||||
pajlada::Settings::Setting<QString> currentUsername{"/accounts/current",
|
pajlada::Settings::Setting<QString> currentUsername{"/accounts/current",
|
||||||
""};
|
""};
|
||||||
// pajlada::Signals::NoArgSignal currentUserChanged;
|
|
||||||
|
/// This signal fires after we've figured out what the new account is, but before
|
||||||
|
/// any updates to Helix have been made.
|
||||||
|
///
|
||||||
|
/// Useful for scenarios where you have to call Helix using the previous account.
|
||||||
|
pajlada::Signals::Signal<std::shared_ptr<TwitchAccount>,
|
||||||
|
std::shared_ptr<TwitchAccount>>
|
||||||
|
currentUserAboutToChange;
|
||||||
|
|
||||||
boost::signals2::signal<void()> currentUserChanged;
|
boost::signals2::signal<void()> currentUserChanged;
|
||||||
pajlada::Signals::NoArgSignal userListUpdated;
|
pajlada::Signals::NoArgSignal userListUpdated;
|
||||||
|
|
||||||
|
|||||||
@@ -125,6 +125,18 @@ TwitchChannel::TwitchChannel(const QString &name)
|
|||||||
{
|
{
|
||||||
qCDebug(chatterinoTwitch) << "[TwitchChannel" << name << "] Opened";
|
qCDebug(chatterinoTwitch) << "[TwitchChannel" << name << "] Opened";
|
||||||
|
|
||||||
|
this->signalHolder_.managedConnect(
|
||||||
|
getApp()->getAccounts()->twitch.currentUserAboutToChange,
|
||||||
|
[this](const auto & /*oldAccount*/, const auto & /*newAccount*/) {
|
||||||
|
this->eventSubChannelChatUserMessageHoldHandle.reset();
|
||||||
|
this->eventSubChannelChatUserMessageUpdateHandle.reset();
|
||||||
|
this->eventSubChannelModerateHandle.reset();
|
||||||
|
this->eventSubAutomodMessageHoldHandle.reset();
|
||||||
|
this->eventSubAutomodMessageUpdateHandle.reset();
|
||||||
|
this->eventSubSuspiciousUserMessageHandle.reset();
|
||||||
|
this->eventSubSuspiciousUserUpdateHandle.reset();
|
||||||
|
});
|
||||||
|
|
||||||
this->bSignals_.emplace_back(
|
this->bSignals_.emplace_back(
|
||||||
getApp()->getAccounts()->twitch.currentUserChanged.connect([this] {
|
getApp()->getAccounts()->twitch.currentUserChanged.connect([this] {
|
||||||
this->setMod(false);
|
this->setMod(false);
|
||||||
@@ -1547,12 +1559,15 @@ void TwitchChannel::refreshPubSub()
|
|||||||
|
|
||||||
auto currentAccount = getApp()->getAccounts()->twitch.getCurrent();
|
auto currentAccount = getApp()->getAccounts()->twitch.getCurrent();
|
||||||
|
|
||||||
|
const auto ¤tTwitchUserID = currentAccount->getUserId();
|
||||||
|
|
||||||
if (this->hasModRights())
|
if (this->hasModRights())
|
||||||
{
|
{
|
||||||
this->eventSubChannelModerateHandle =
|
this->eventSubChannelModerateHandle =
|
||||||
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
||||||
.subscriptionType = "channel.moderate",
|
.subscriptionType = "channel.moderate",
|
||||||
.subscriptionVersion = "2",
|
.subscriptionVersion = "2",
|
||||||
|
.ownerTwitchUserID = currentTwitchUserID,
|
||||||
.conditions =
|
.conditions =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@@ -1561,7 +1576,7 @@ void TwitchChannel::refreshPubSub()
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"moderator_user_id",
|
"moderator_user_id",
|
||||||
currentAccount->getUserId(),
|
currentTwitchUserID,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1569,6 +1584,7 @@ void TwitchChannel::refreshPubSub()
|
|||||||
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
||||||
.subscriptionType = "automod.message.hold",
|
.subscriptionType = "automod.message.hold",
|
||||||
.subscriptionVersion = "2",
|
.subscriptionVersion = "2",
|
||||||
|
.ownerTwitchUserID = currentTwitchUserID,
|
||||||
.conditions =
|
.conditions =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@@ -1577,7 +1593,7 @@ void TwitchChannel::refreshPubSub()
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"moderator_user_id",
|
"moderator_user_id",
|
||||||
currentAccount->getUserId(),
|
currentTwitchUserID,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1585,6 +1601,7 @@ void TwitchChannel::refreshPubSub()
|
|||||||
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
||||||
.subscriptionType = "automod.message.update",
|
.subscriptionType = "automod.message.update",
|
||||||
.subscriptionVersion = "2",
|
.subscriptionVersion = "2",
|
||||||
|
.ownerTwitchUserID = currentTwitchUserID,
|
||||||
.conditions =
|
.conditions =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@@ -1593,7 +1610,7 @@ void TwitchChannel::refreshPubSub()
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"moderator_user_id",
|
"moderator_user_id",
|
||||||
currentAccount->getUserId(),
|
currentTwitchUserID,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1601,6 +1618,7 @@ void TwitchChannel::refreshPubSub()
|
|||||||
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
||||||
.subscriptionType = "channel.suspicious_user.message",
|
.subscriptionType = "channel.suspicious_user.message",
|
||||||
.subscriptionVersion = "1",
|
.subscriptionVersion = "1",
|
||||||
|
.ownerTwitchUserID = currentTwitchUserID,
|
||||||
.conditions =
|
.conditions =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@@ -1609,7 +1627,7 @@ void TwitchChannel::refreshPubSub()
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"moderator_user_id",
|
"moderator_user_id",
|
||||||
currentAccount->getUserId(),
|
currentTwitchUserID,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1617,6 +1635,7 @@ void TwitchChannel::refreshPubSub()
|
|||||||
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
||||||
.subscriptionType = "channel.suspicious_user.update",
|
.subscriptionType = "channel.suspicious_user.update",
|
||||||
.subscriptionVersion = "1",
|
.subscriptionVersion = "1",
|
||||||
|
.ownerTwitchUserID = currentTwitchUserID,
|
||||||
.conditions =
|
.conditions =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@@ -1625,7 +1644,7 @@ void TwitchChannel::refreshPubSub()
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"moderator_user_id",
|
"moderator_user_id",
|
||||||
currentAccount->getUserId(),
|
currentTwitchUserID,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1645,6 +1664,7 @@ void TwitchChannel::refreshPubSub()
|
|||||||
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
||||||
.subscriptionType = "channel.chat.user_message_hold",
|
.subscriptionType = "channel.chat.user_message_hold",
|
||||||
.subscriptionVersion = "1",
|
.subscriptionVersion = "1",
|
||||||
|
.ownerTwitchUserID = currentTwitchUserID,
|
||||||
.conditions =
|
.conditions =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@@ -1653,7 +1673,7 @@ void TwitchChannel::refreshPubSub()
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"user_id",
|
"user_id",
|
||||||
currentAccount->getUserId(),
|
currentTwitchUserID,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1662,6 +1682,7 @@ void TwitchChannel::refreshPubSub()
|
|||||||
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
||||||
.subscriptionType = "channel.chat.user_message_update",
|
.subscriptionType = "channel.chat.user_message_update",
|
||||||
.subscriptionVersion = "1",
|
.subscriptionVersion = "1",
|
||||||
|
.ownerTwitchUserID = currentTwitchUserID,
|
||||||
.conditions =
|
.conditions =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@@ -1670,7 +1691,7 @@ void TwitchChannel::refreshPubSub()
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"user_id",
|
"user_id",
|
||||||
currentAccount->getUserId(),
|
currentTwitchUserID,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "providers/twitch/eventsub/Controller.hpp"
|
#include "providers/twitch/eventsub/Controller.hpp"
|
||||||
#include "providers/twitch/eventsub/MessageBuilder.hpp"
|
#include "providers/twitch/eventsub/MessageBuilder.hpp"
|
||||||
#include "providers/twitch/eventsub/MessageHandlers.hpp"
|
#include "providers/twitch/eventsub/MessageHandlers.hpp"
|
||||||
|
#include "providers/twitch/PubSubManager.hpp"
|
||||||
#include "providers/twitch/TwitchBadge.hpp"
|
#include "providers/twitch/TwitchBadge.hpp"
|
||||||
#include "providers/twitch/TwitchChannel.hpp"
|
#include "providers/twitch/TwitchChannel.hpp"
|
||||||
#include "providers/twitch/TwitchIrcServer.hpp"
|
#include "providers/twitch/TwitchIrcServer.hpp"
|
||||||
@@ -416,12 +417,42 @@ bool Connection::isSubscribedTo(const SubscriptionRequest &request) const
|
|||||||
|
|
||||||
void Connection::markRequestSubscribed(const SubscriptionRequest &request)
|
void Connection::markRequestSubscribed(const SubscriptionRequest &request)
|
||||||
{
|
{
|
||||||
|
assert((this->twitchUserID.isEmpty() ||
|
||||||
|
this->twitchUserID == request.ownerTwitchUserID) &&
|
||||||
|
"A subscription was made when another user's subscriptions were "
|
||||||
|
"still active");
|
||||||
|
|
||||||
|
this->twitchUserID = request.ownerTwitchUserID;
|
||||||
|
|
||||||
this->subscriptions.emplace(request);
|
this->subscriptions.emplace(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::markRequestUnsubscribed(const SubscriptionRequest &request)
|
void Connection::markRequestUnsubscribed(const SubscriptionRequest &request)
|
||||||
{
|
{
|
||||||
this->subscriptions.erase(request);
|
this->subscriptions.erase(request);
|
||||||
|
|
||||||
|
if (this->subscriptions.empty())
|
||||||
|
{
|
||||||
|
// TODO: Verify that it's fine for us to reuse a connection for another
|
||||||
|
// user after all old subscriptions are gone
|
||||||
|
this->twitchUserID.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Connection::canHandleSubscriptionFrom(
|
||||||
|
const QString &otherTwitchUserID) const
|
||||||
|
{
|
||||||
|
return this->twitchUserID.isEmpty() ||
|
||||||
|
this->twitchUserID == otherTwitchUserID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Connection::debug()
|
||||||
|
{
|
||||||
|
for (const auto &request : this->subscriptions)
|
||||||
|
{
|
||||||
|
qCInfo(LOG).noquote().nospace()
|
||||||
|
<< this->getSessionID() << " -> " << request;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace chatterino::eventsub
|
} // namespace chatterino::eventsub
|
||||||
|
|||||||
@@ -90,9 +90,17 @@ public:
|
|||||||
void markRequestSubscribed(const SubscriptionRequest &request);
|
void markRequestSubscribed(const SubscriptionRequest &request);
|
||||||
void markRequestUnsubscribed(const SubscriptionRequest &request);
|
void markRequestUnsubscribed(const SubscriptionRequest &request);
|
||||||
|
|
||||||
|
bool canHandleSubscriptionFrom(const QString &otherTwitchUserID) const;
|
||||||
|
|
||||||
|
void debug();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString sessionID;
|
QString sessionID;
|
||||||
|
|
||||||
|
/// The Twitch User ID that's attached to all subscriptions of this connection.
|
||||||
|
/// If no subscriptions are attached yet, this is an empty string, meaning it's open for any subscription.
|
||||||
|
QString twitchUserID;
|
||||||
|
|
||||||
std::unordered_set<SubscriptionRequest> subscriptions;
|
std::unordered_set<SubscriptionRequest> subscriptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "common/Version.hpp"
|
#include "common/Version.hpp"
|
||||||
#include "providers/twitch/api/Helix.hpp"
|
#include "providers/twitch/api/Helix.hpp"
|
||||||
#include "providers/twitch/eventsub/Connection.hpp"
|
#include "providers/twitch/eventsub/Connection.hpp"
|
||||||
|
#include "util/QMagicEnum.hpp"
|
||||||
#include "util/RenameThread.hpp"
|
#include "util/RenameThread.hpp"
|
||||||
|
|
||||||
#include <boost/asio/io_context.hpp>
|
#include <boost/asio/io_context.hpp>
|
||||||
@@ -169,7 +170,7 @@ void Controller::removeRef(const SubscriptionRequest &request)
|
|||||||
<< "Refcount fell to zero for" << request
|
<< "Refcount fell to zero for" << request
|
||||||
<< "but we had no subscription ID attached - a "
|
<< "but we had no subscription ID attached - a "
|
||||||
"successful subscription was never made. From state "
|
"successful subscription was never made. From state "
|
||||||
<< static_cast<uint8_t>(subscription.state);
|
<< qmagicenum::enumName(subscription.state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,6 +204,9 @@ SubscriptionHandle Controller::subscribe(const SubscriptionRequest &request)
|
|||||||
assert(!this->quitting &&
|
assert(!this->quitting &&
|
||||||
"Subscribe cannot be called while we are quitting");
|
"Subscribe cannot be called while we are quitting");
|
||||||
|
|
||||||
|
assert(!request.ownerTwitchUserID.isEmpty() &&
|
||||||
|
"Subscription requests must include a Twitch User ID");
|
||||||
|
|
||||||
bool needToSubscribe = false;
|
bool needToSubscribe = false;
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -251,7 +255,7 @@ SubscriptionHandle Controller::subscribe(const SubscriptionRequest &request)
|
|||||||
subscription.refCount++;
|
subscription.refCount++;
|
||||||
qCDebug(LOG) << "Added ref for" << request << subscription.refCount
|
qCDebug(LOG) << "Added ref for" << request << subscription.refCount
|
||||||
<< needToSubscribe
|
<< needToSubscribe
|
||||||
<< "state:" << static_cast<uint8_t>(subscription.state);
|
<< "state:" << qmagicenum::enumName(subscription.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto handle = std::make_unique<RawSubscriptionHandle>(request);
|
auto handle = std::make_unique<RawSubscriptionHandle>(request);
|
||||||
@@ -314,6 +318,59 @@ void Controller::reconnectConnection(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Controller::debug()
|
||||||
|
{
|
||||||
|
std::lock_guard g(this->subscriptionsMutex);
|
||||||
|
for (const auto &[request, subscription] : this->subscriptions)
|
||||||
|
{
|
||||||
|
QString sessionID;
|
||||||
|
auto connection = subscription.connection.lock();
|
||||||
|
if (connection)
|
||||||
|
{
|
||||||
|
auto *connection2 =
|
||||||
|
dynamic_cast<Connection *>(connection->getListener());
|
||||||
|
if (connection2)
|
||||||
|
{
|
||||||
|
sessionID = connection2->getSessionID();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sessionID = "BAD";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sessionID = "DEAD";
|
||||||
|
}
|
||||||
|
|
||||||
|
qCInfo(LOG).noquote().nospace()
|
||||||
|
<< request << " (" << qmagicenum::enumName(subscription.state)
|
||||||
|
<< ") -> " << sessionID;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::post(this->ioContext, [this] {
|
||||||
|
for (const auto &weakConnection : this->connections)
|
||||||
|
{
|
||||||
|
auto connection = weakConnection.lock();
|
||||||
|
if (connection)
|
||||||
|
{
|
||||||
|
auto *connection2 =
|
||||||
|
dynamic_cast<Connection *>(connection->getListener());
|
||||||
|
if (connection2)
|
||||||
|
{
|
||||||
|
// qCInfo(LOG)
|
||||||
|
// << "Connected to" << connection2->getSessionID();
|
||||||
|
connection2->debug();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qCInfo(LOG) << "Dead connection";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void Controller::subscribe(const SubscriptionRequest &request, bool isRetry)
|
void Controller::subscribe(const SubscriptionRequest &request, bool isRetry)
|
||||||
{
|
{
|
||||||
// 1. Flush dead connections (maybe this should not be done here)
|
// 1. Flush dead connections (maybe this should not be done here)
|
||||||
@@ -341,8 +398,8 @@ void Controller::subscribe(const SubscriptionRequest &request, bool isRetry)
|
|||||||
uint32_t openButNotReadyConnections = 0;
|
uint32_t openButNotReadyConnections = 0;
|
||||||
|
|
||||||
// 2. Check if any currently open connection can handle this subscription
|
// 2. Check if any currently open connection can handle this subscription
|
||||||
auto viableConnection =
|
auto viableConnection = this->getViableConnection(
|
||||||
this->getViableConnection(openButNotReadyConnections);
|
request.ownerTwitchUserID, openButNotReadyConnections);
|
||||||
|
|
||||||
if (viableConnection.has_value())
|
if (viableConnection.has_value())
|
||||||
{
|
{
|
||||||
@@ -436,7 +493,7 @@ void Controller::subscribe(const SubscriptionRequest &request, bool isRetry)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::shared_ptr<lib::Session>> Controller::getViableConnection(
|
std::optional<std::shared_ptr<lib::Session>> Controller::getViableConnection(
|
||||||
uint32_t &openButNotReadyConnections)
|
const QString &ownerTwitchUserID, uint32_t &openButNotReadyConnections)
|
||||||
{
|
{
|
||||||
for (const auto &weakConnection : this->connections)
|
for (const auto &weakConnection : this->connections)
|
||||||
{
|
{
|
||||||
@@ -461,6 +518,11 @@ std::optional<std::shared_ptr<lib::Session>> Controller::getViableConnection(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!listener->canHandleSubscriptionFrom(ownerTwitchUserID))
|
||||||
|
{
|
||||||
|
continue; // Connection is active with another Twitch User's subscriptions
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Check if this listener has room for another subscription
|
// TODO: Check if this listener has room for another subscription
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
@@ -536,9 +598,12 @@ void Controller::retrySubscription(const SubscriptionRequest &request)
|
|||||||
qCDebug(LOG) << "No one is interested in this subscription anymore, "
|
qCDebug(LOG) << "No one is interested in this subscription anymore, "
|
||||||
"stop trying"
|
"stop trying"
|
||||||
<< request << "from state"
|
<< request << "from state"
|
||||||
<< static_cast<uint8_t>(subscription.state);
|
<< qmagicenum::enumName(subscription.state);
|
||||||
qCDebug(LOG) << "Set state to unsubscribed" << request;
|
qCDebug(LOG) << "Set state to unsubscribed" << request;
|
||||||
subscription.state = Subscription::State::Unsubscribed;
|
subscription.state = Subscription::State::Unsubscribed;
|
||||||
|
|
||||||
|
this->subscriptions.erase(request);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,7 +721,7 @@ void Controller::markRequestUnsubscribed(const SubscriptionRequest &request)
|
|||||||
auto &subscription = this->subscriptions[request];
|
auto &subscription = this->subscriptions[request];
|
||||||
|
|
||||||
qCDebug(LOG) << "Request" << request << "marked as unsubscribed from state"
|
qCDebug(LOG) << "Request" << request << "marked as unsubscribed from state"
|
||||||
<< static_cast<uint8_t>(subscription.state);
|
<< qmagicenum::enumName(subscription.state);
|
||||||
|
|
||||||
assert(subscription.state == Subscription::State::Unsubscribing ||
|
assert(subscription.state == Subscription::State::Unsubscribing ||
|
||||||
subscription.state == Subscription::State::Retrying);
|
subscription.state == Subscription::State::Retrying);
|
||||||
@@ -679,6 +744,7 @@ void Controller::markRequestUnsubscribed(const SubscriptionRequest &request)
|
|||||||
if (subscription.refCount == 0)
|
if (subscription.refCount == 0)
|
||||||
{
|
{
|
||||||
// we could remove the subscription here
|
// we could remove the subscription here
|
||||||
|
this->subscriptions.erase(request);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ public:
|
|||||||
std::unique_ptr<lib::Listener> connection,
|
std::unique_ptr<lib::Listener> connection,
|
||||||
const std::optional<std::string> &reconnectURL,
|
const std::optional<std::string> &reconnectURL,
|
||||||
const std::unordered_set<SubscriptionRequest> &subs) = 0;
|
const std::unordered_set<SubscriptionRequest> &subs) = 0;
|
||||||
|
|
||||||
|
virtual void debug() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Controller : public IController
|
class Controller : public IController
|
||||||
@@ -73,6 +75,8 @@ public:
|
|||||||
const std::optional<std::string> &reconnectURL,
|
const std::optional<std::string> &reconnectURL,
|
||||||
const std::unordered_set<SubscriptionRequest> &subs) override;
|
const std::unordered_set<SubscriptionRequest> &subs) override;
|
||||||
|
|
||||||
|
void debug() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void subscribe(const SubscriptionRequest &request, bool isRetry);
|
void subscribe(const SubscriptionRequest &request, bool isRetry);
|
||||||
|
|
||||||
@@ -110,7 +114,8 @@ private:
|
|||||||
std::vector<std::weak_ptr<lib::Session>> connections;
|
std::vector<std::weak_ptr<lib::Session>> connections;
|
||||||
|
|
||||||
[[nodiscard]] std::optional<std::shared_ptr<lib::Session>>
|
[[nodiscard]] std::optional<std::shared_ptr<lib::Session>>
|
||||||
getViableConnection(uint32_t &openButNotReadyConnections);
|
getViableConnection(const QString &ownerTwitchUserID,
|
||||||
|
uint32_t &openButNotReadyConnections);
|
||||||
|
|
||||||
struct Subscription {
|
struct Subscription {
|
||||||
enum class State : uint8_t {
|
enum class State : uint8_t {
|
||||||
@@ -179,6 +184,10 @@ public:
|
|||||||
std::unique_ptr<lib::Listener> connection,
|
std::unique_ptr<lib::Listener> connection,
|
||||||
const std::optional<std::string> &reconnectURL,
|
const std::optional<std::string> &reconnectURL,
|
||||||
const std::unordered_set<SubscriptionRequest> &subs) override;
|
const std::unordered_set<SubscriptionRequest> &subs) override;
|
||||||
|
|
||||||
|
void debug() override
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino::eventsub
|
} // namespace chatterino::eventsub
|
||||||
|
|||||||
@@ -18,6 +18,12 @@ struct SubscriptionRequest {
|
|||||||
// maybe this should be part of the enum later
|
// maybe this should be part of the enum later
|
||||||
QString subscriptionVersion;
|
QString subscriptionVersion;
|
||||||
|
|
||||||
|
/// The ID of the Twitch User that wants to create this subscription
|
||||||
|
///
|
||||||
|
/// Since a single websocket connection cannot share connections between users,
|
||||||
|
/// we need to key them by the user id even if it's not used in the condition.
|
||||||
|
QString ownerTwitchUserID;
|
||||||
|
|
||||||
/// Optional list of conditions for the subscription
|
/// Optional list of conditions for the subscription
|
||||||
std::vector<std::pair<QString, QString>> conditions;
|
std::vector<std::pair<QString, QString>> conditions;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user