chore: refactor TwitchIrcServer (#5421)

This commit is contained in:
pajlada 2024-06-01 14:56:40 +02:00 committed by GitHub
parent 2a46ee708e
commit b6dc5d9e03
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 373 additions and 163 deletions

View file

@ -19,6 +19,7 @@
- Dev: Add doxygen build target. (#5377) - Dev: Add doxygen build target. (#5377)
- Dev: Make printing of strings in tests easier. (#5379) - Dev: Make printing of strings in tests easier. (#5379)
- Dev: Refactor and document `Scrollbar`. (#5334, #5393) - Dev: Refactor and document `Scrollbar`. (#5334, #5393)
- Dev: Refactor `TwitchIrcServer`, making it abstracted. (#5421)
- Dev: Reduced the amount of scale events. (#5404, #5406) - Dev: Reduced the amount of scale events. (#5404, #5406)
- Dev: Removed unused timegate settings. (#5361) - Dev: Removed unused timegate settings. (#5361)
- Dev: All Lua globals now show in the `c2` global in the LuaLS metadata. (#5385) - Dev: All Lua globals now show in the `c2` global in the LuaLS metadata. (#5385)

View file

@ -118,6 +118,13 @@ public:
return nullptr; return nullptr;
} }
IAbstractIrcServer *getTwitchAbstract() override
{
assert(false && "EmptyApplication::getTwitchAbstract was called "
"without being initialized");
return nullptr;
}
PubSub *getTwitchPubSub() override PubSub *getTwitchPubSub() override
{ {
assert(false && "getTwitchPubSub was called without being initialized"); assert(false && "getTwitchPubSub was called without being initialized");

View file

@ -2,8 +2,13 @@
#include "mocks/Channel.hpp" #include "mocks/Channel.hpp"
#include "providers/bttv/BttvEmotes.hpp" #include "providers/bttv/BttvEmotes.hpp"
#include "providers/bttv/BttvLiveUpdates.hpp"
#include "providers/ffz/FfzEmotes.hpp" #include "providers/ffz/FfzEmotes.hpp"
#include "providers/seventv/eventapi/Client.hpp"
#include "providers/seventv/eventapi/Dispatch.hpp"
#include "providers/seventv/eventapi/Message.hpp"
#include "providers/seventv/SeventvEmotes.hpp" #include "providers/seventv/SeventvEmotes.hpp"
#include "providers/seventv/SeventvEventAPI.hpp"
#include "providers/twitch/TwitchIrcServer.hpp" #include "providers/twitch/TwitchIrcServer.hpp"
namespace chatterino::mock { namespace chatterino::mock {
@ -16,22 +21,91 @@ public:
std::shared_ptr<Channel>(new MockChannel("testaccount_420"))) std::shared_ptr<Channel>(new MockChannel("testaccount_420")))
, watchingChannel(this->watchingChannelInner, , watchingChannel(this->watchingChannelInner,
Channel::Type::TwitchWatching) Channel::Type::TwitchWatching)
, whispersChannel(std::shared_ptr<Channel>(new MockChannel("whispers")))
, mentionsChannel(std::shared_ptr<Channel>(new MockChannel("forsen3")))
, liveChannel(std::shared_ptr<Channel>(new MockChannel("forsen")))
, automodChannel(std::shared_ptr<Channel>(new MockChannel("forsen2")))
{ {
} }
void forEachChannelAndSpecialChannels(
std::function<void(ChannelPtr)> func) override
{
//
}
std::shared_ptr<Channel> getChannelOrEmptyByID(
const QString &channelID) override
{
return {};
}
void dropSeventvChannel(const QString &userID,
const QString &emoteSetID) override
{
//
}
std::unique_ptr<BttvLiveUpdates> &getBTTVLiveUpdates() override
{
return this->bttvLiveUpdates;
}
std::unique_ptr<SeventvEventAPI> &getSeventvEventAPI() override
{
return this->seventvEventAPI;
}
const IndirectChannel &getWatchingChannel() const override const IndirectChannel &getWatchingChannel() const override
{ {
return this->watchingChannel; return this->watchingChannel;
} }
void setWatchingChannel(ChannelPtr newWatchingChannel) override
{
this->watchingChannel.reset(newWatchingChannel);
}
QString getLastUserThatWhisperedMe() const override QString getLastUserThatWhisperedMe() const override
{ {
return this->lastUserThatWhisperedMe; return this->lastUserThatWhisperedMe;
} }
void setLastUserThatWhisperedMe(const QString &user) override
{
this->lastUserThatWhisperedMe = user;
}
ChannelPtr getWhispersChannel() const override
{
return this->whispersChannel;
}
ChannelPtr getMentionsChannel() const override
{
return this->mentionsChannel;
}
ChannelPtr getLiveChannel() const override
{
return this->liveChannel;
}
ChannelPtr getAutomodChannel() const override
{
return this->automodChannel;
}
ChannelPtr watchingChannelInner; ChannelPtr watchingChannelInner;
IndirectChannel watchingChannel; IndirectChannel watchingChannel;
ChannelPtr whispersChannel;
ChannelPtr mentionsChannel;
ChannelPtr liveChannel;
ChannelPtr automodChannel;
QString lastUserThatWhisperedMe{"forsen"}; QString lastUserThatWhisperedMe{"forsen"};
std::unique_ptr<BttvLiveUpdates> bttvLiveUpdates;
std::unique_ptr<SeventvEventAPI> seventvEventAPI;
}; };
} // namespace chatterino::mock } // namespace chatterino::mock

View file

@ -13,6 +13,7 @@
#include "controllers/sound/ISoundController.hpp" #include "controllers/sound/ISoundController.hpp"
#include "providers/bttv/BttvEmotes.hpp" #include "providers/bttv/BttvEmotes.hpp"
#include "providers/ffz/FfzEmotes.hpp" #include "providers/ffz/FfzEmotes.hpp"
#include "providers/irc/AbstractIrcServer.hpp"
#include "providers/links/LinkResolver.hpp" #include "providers/links/LinkResolver.hpp"
#include "providers/seventv/SeventvAPI.hpp" #include "providers/seventv/SeventvAPI.hpp"
#include "providers/seventv/SeventvEmotes.hpp" #include "providers/seventv/SeventvEmotes.hpp"
@ -131,7 +132,7 @@ Application::Application(Settings &_settings, const Paths &paths,
, commands(&this->emplace<CommandController>()) , commands(&this->emplace<CommandController>())
, notifications(&this->emplace<NotificationController>()) , notifications(&this->emplace<NotificationController>())
, highlights(&this->emplace<HighlightController>()) , highlights(&this->emplace<HighlightController>())
, twitch(&this->emplace<TwitchIrcServer>()) , twitch(new TwitchIrcServer)
, ffzBadges(&this->emplace<FfzBadges>()) , ffzBadges(&this->emplace<FfzBadges>())
, seventvBadges(&this->emplace<SeventvBadges>()) , seventvBadges(&this->emplace<SeventvBadges>())
, userData(&this->emplace(new UserDataController(paths))) , userData(&this->emplace(new UserDataController(paths)))
@ -170,6 +171,7 @@ void Application::fakeDtor()
this->bttvEmotes.reset(); this->bttvEmotes.reset();
this->ffzEmotes.reset(); this->ffzEmotes.reset();
this->seventvEmotes.reset(); this->seventvEmotes.reset();
// this->twitch.reset();
this->fonts.reset(); this->fonts.reset();
} }
@ -483,7 +485,14 @@ ITwitchIrcServer *Application::getTwitch()
{ {
assertInGuiThread(); assertInGuiThread();
return this->twitch; return this->twitch.get();
}
IAbstractIrcServer *Application::getTwitchAbstract()
{
assertInGuiThread();
return this->twitch.get();
} }
PubSub *Application::getTwitchPubSub() PubSub *Application::getTwitchPubSub()
@ -865,17 +874,25 @@ void Application::initPubSub()
chan->addMessage(p.first); chan->addMessage(p.first);
chan->addMessage(p.second); chan->addMessage(p.second);
getApp()->twitch->automodChannel->addMessage( getIApp()
p.first); ->getTwitch()
getApp()->twitch->automodChannel->addMessage( ->getAutomodChannel()
p.second); ->addMessage(p.first);
getIApp()
->getTwitch()
->getAutomodChannel()
->addMessage(p.second);
if (getSettings()->showAutomodInMentions) if (getSettings()->showAutomodInMentions)
{ {
getApp()->twitch->mentionsChannel->addMessage( getIApp()
p.first); ->getTwitch()
getApp()->twitch->mentionsChannel->addMessage( ->getMentionsChannel()
p.second); ->addMessage(p.first);
getIApp()
->getTwitch()
->getMentionsChannel()
->addMessage(p.second);
} }
}); });
} }
@ -984,7 +1001,9 @@ void Application::initPubSub()
void Application::initBttvLiveUpdates() void Application::initBttvLiveUpdates()
{ {
if (!this->twitch->bttvLiveUpdates) auto &bttvLiveUpdates = this->twitch->getBTTVLiveUpdates();
if (!bttvLiveUpdates)
{ {
qCDebug(chatterinoBttv) qCDebug(chatterinoBttv)
<< "Skipping initialization of Live Updates as it's disabled"; << "Skipping initialization of Live Updates as it's disabled";
@ -993,8 +1012,8 @@ void Application::initBttvLiveUpdates()
// We can safely ignore these signal connections since the twitch object will always // We can safely ignore these signal connections since the twitch object will always
// be destroyed before the Application // be destroyed before the Application
std::ignore = this->twitch->bttvLiveUpdates->signals_.emoteAdded.connect( std::ignore =
[&](const auto &data) { bttvLiveUpdates->signals_.emoteAdded.connect([&](const auto &data) {
auto chan = this->twitch->getChannelOrEmptyByID(data.channelID); auto chan = this->twitch->getChannelOrEmptyByID(data.channelID);
postToThread([chan, data] { postToThread([chan, data] {
@ -1004,8 +1023,8 @@ void Application::initBttvLiveUpdates()
} }
}); });
}); });
std::ignore = this->twitch->bttvLiveUpdates->signals_.emoteUpdated.connect( std::ignore =
[&](const auto &data) { bttvLiveUpdates->signals_.emoteUpdated.connect([&](const auto &data) {
auto chan = this->twitch->getChannelOrEmptyByID(data.channelID); auto chan = this->twitch->getChannelOrEmptyByID(data.channelID);
postToThread([chan, data] { postToThread([chan, data] {
@ -1015,8 +1034,8 @@ void Application::initBttvLiveUpdates()
} }
}); });
}); });
std::ignore = this->twitch->bttvLiveUpdates->signals_.emoteRemoved.connect( std::ignore =
[&](const auto &data) { bttvLiveUpdates->signals_.emoteRemoved.connect([&](const auto &data) {
auto chan = this->twitch->getChannelOrEmptyByID(data.channelID); auto chan = this->twitch->getChannelOrEmptyByID(data.channelID);
postToThread([chan, data] { postToThread([chan, data] {
@ -1026,12 +1045,14 @@ void Application::initBttvLiveUpdates()
} }
}); });
}); });
this->twitch->bttvLiveUpdates->start(); bttvLiveUpdates->start();
} }
void Application::initSeventvEventAPI() void Application::initSeventvEventAPI()
{ {
if (!this->twitch->seventvEventAPI) auto &seventvEventAPI = this->twitch->getSeventvEventAPI();
if (!seventvEventAPI)
{ {
qCDebug(chatterinoSeventvEventAPI) qCDebug(chatterinoSeventvEventAPI)
<< "Skipping initialization as the EventAPI is disabled"; << "Skipping initialization as the EventAPI is disabled";
@ -1040,8 +1061,8 @@ void Application::initSeventvEventAPI()
// We can safely ignore these signal connections since the twitch object will always // We can safely ignore these signal connections since the twitch object will always
// be destroyed before the Application // be destroyed before the Application
std::ignore = this->twitch->seventvEventAPI->signals_.emoteAdded.connect( std::ignore =
[&](const auto &data) { seventvEventAPI->signals_.emoteAdded.connect([&](const auto &data) {
postToThread([this, data] { postToThread([this, data] {
this->twitch->forEachSeventvEmoteSet( this->twitch->forEachSeventvEmoteSet(
data.emoteSetID, [data](TwitchChannel &chan) { data.emoteSetID, [data](TwitchChannel &chan) {
@ -1049,8 +1070,8 @@ void Application::initSeventvEventAPI()
}); });
}); });
}); });
std::ignore = this->twitch->seventvEventAPI->signals_.emoteUpdated.connect( std::ignore =
[&](const auto &data) { seventvEventAPI->signals_.emoteUpdated.connect([&](const auto &data) {
postToThread([this, data] { postToThread([this, data] {
this->twitch->forEachSeventvEmoteSet( this->twitch->forEachSeventvEmoteSet(
data.emoteSetID, [data](TwitchChannel &chan) { data.emoteSetID, [data](TwitchChannel &chan) {
@ -1058,8 +1079,8 @@ void Application::initSeventvEventAPI()
}); });
}); });
}); });
std::ignore = this->twitch->seventvEventAPI->signals_.emoteRemoved.connect( std::ignore =
[&](const auto &data) { seventvEventAPI->signals_.emoteRemoved.connect([&](const auto &data) {
postToThread([this, data] { postToThread([this, data] {
this->twitch->forEachSeventvEmoteSet( this->twitch->forEachSeventvEmoteSet(
data.emoteSetID, [data](TwitchChannel &chan) { data.emoteSetID, [data](TwitchChannel &chan) {
@ -1067,15 +1088,15 @@ void Application::initSeventvEventAPI()
}); });
}); });
}); });
std::ignore = this->twitch->seventvEventAPI->signals_.userUpdated.connect( std::ignore =
[&](const auto &data) { seventvEventAPI->signals_.userUpdated.connect([&](const auto &data) {
this->twitch->forEachSeventvUser(data.userID, this->twitch->forEachSeventvUser(data.userID,
[data](TwitchChannel &chan) { [data](TwitchChannel &chan) {
chan.updateSeventvUser(data); chan.updateSeventvUser(data);
}); });
}); });
this->twitch->seventvEventAPI->start(); seventvEventAPI->start();
} }
Application *getApp() Application *getApp()

View file

@ -54,6 +54,7 @@ class FfzEmotes;
class SeventvEmotes; class SeventvEmotes;
class ILinkResolver; class ILinkResolver;
class IStreamerMode; class IStreamerMode;
class IAbstractIrcServer;
class IApplication class IApplication
{ {
@ -77,6 +78,7 @@ public:
virtual HighlightController *getHighlights() = 0; virtual HighlightController *getHighlights() = 0;
virtual NotificationController *getNotifications() = 0; virtual NotificationController *getNotifications() = 0;
virtual ITwitchIrcServer *getTwitch() = 0; virtual ITwitchIrcServer *getTwitch() = 0;
virtual IAbstractIrcServer *getTwitchAbstract() = 0;
virtual PubSub *getTwitchPubSub() = 0; virtual PubSub *getTwitchPubSub() = 0;
virtual Logging *getChatLogger() = 0; virtual Logging *getChatLogger() = 0;
virtual IChatterinoBadges *getChatterinoBadges() = 0; virtual IChatterinoBadges *getChatterinoBadges() = 0;
@ -147,11 +149,7 @@ private:
CommandController *const commands{}; CommandController *const commands{};
NotificationController *const notifications{}; NotificationController *const notifications{};
HighlightController *const highlights{}; HighlightController *const highlights{};
std::unique_ptr<TwitchIrcServer> twitch;
public:
TwitchIrcServer *const twitch{};
private:
FfzBadges *const ffzBadges{}; FfzBadges *const ffzBadges{};
SeventvBadges *const seventvBadges{}; SeventvBadges *const seventvBadges{};
UserDataController *const userData{}; UserDataController *const userData{};
@ -191,6 +189,7 @@ public:
NotificationController *getNotifications() override; NotificationController *getNotifications() override;
HighlightController *getHighlights() override; HighlightController *getHighlights() override;
ITwitchIrcServer *getTwitch() override; ITwitchIrcServer *getTwitch() override;
IAbstractIrcServer *getTwitchAbstract() override;
PubSub *getTwitchPubSub() override; PubSub *getTwitchPubSub() override;
Logging *getChatLogger() override; Logging *getChatLogger() override;
FfzBadges *getFfzBadges() override; FfzBadges *getFfzBadges() override;

View file

@ -390,9 +390,9 @@ QString popup(const CommandContext &ctx)
} }
// Open channel passed as argument in a popup // Open channel passed as argument in a popup
auto *app = getApp(); auto targetChannel =
auto targetChannel = app->twitch->getOrAddChannel(target); getIApp()->getTwitchAbstract()->getOrAddChannel(target);
app->getWindows()->openInPopup(targetChannel); getIApp()->getWindows()->openInPopup(targetChannel);
return ""; return "";
} }
@ -533,7 +533,8 @@ QString sendRawMessage(const CommandContext &ctx)
if (ctx.channel->isTwitchChannel()) if (ctx.channel->isTwitchChannel())
{ {
getApp()->twitch->sendRawMessage(ctx.words.mid(1).join(" ")); getIApp()->getTwitchAbstract()->sendRawMessage(
ctx.words.mid(1).join(" "));
} }
else else
{ {
@ -566,7 +567,7 @@ QString injectFakeMessage(const CommandContext &ctx)
} }
auto ircText = ctx.words.mid(1).join(" "); auto ircText = ctx.words.mid(1).join(" ");
getApp()->twitch->addFakeMessage(ircText); getIApp()->getTwitchAbstract()->addFakeMessage(ircText);
return ""; return "";
} }
@ -667,7 +668,7 @@ QString openUsercard(const CommandContext &ctx)
stripChannelName(channelName); stripChannelName(channelName);
ChannelPtr channelTemp = ChannelPtr channelTemp =
getApp()->twitch->getChannelOrEmpty(channelName); getIApp()->getTwitchAbstract()->getChannelOrEmpty(channelName);
if (channelTemp->isEmpty()) if (channelTemp->isEmpty())
{ {

View file

@ -92,7 +92,7 @@ QString formatWhisperError(HelixWhisperError error, const QString &message)
bool appendWhisperMessageWordsLocally(const QStringList &words) bool appendWhisperMessageWordsLocally(const QStringList &words)
{ {
auto *app = getApp(); auto *app = getIApp();
MessageBuilder b; MessageBuilder b;
@ -177,7 +177,7 @@ bool appendWhisperMessageWordsLocally(const QStringList &words)
b->flags.set(MessageFlag::Whisper); b->flags.set(MessageFlag::Whisper);
auto messagexD = b.release(); auto messagexD = b.release();
app->twitch->whispersChannel->addMessage(messagexD); getIApp()->getTwitch()->getWhispersChannel()->addMessage(messagexD);
auto overrideFlags = std::optional<MessageFlags>(messagexD->flags); auto overrideFlags = std::optional<MessageFlags>(messagexD->flags);
overrideFlags->set(MessageFlag::DoNotLog); overrideFlags->set(MessageFlag::DoNotLog);
@ -186,7 +186,7 @@ bool appendWhisperMessageWordsLocally(const QStringList &words)
!(getSettings()->streamerModeSuppressInlineWhispers && !(getSettings()->streamerModeSuppressInlineWhispers &&
getIApp()->getStreamerMode()->isEnabled())) getIApp()->getStreamerMode()->isEnabled()))
{ {
app->twitch->forEachChannel( app->getTwitchAbstract()->forEachChannel(
[&messagexD, overrideFlags](ChannelPtr _channel) { [&messagexD, overrideFlags](ChannelPtr _channel) {
_channel->addMessage(messagexD, overrideFlags); _channel->addMessage(messagexD, overrideFlags);
}); });

View file

@ -124,7 +124,7 @@ void NotificationController::fetchFakeChannels()
for (std::vector<int>::size_type i = 0; for (std::vector<int>::size_type i = 0;
i < channelMap[Platform::Twitch].raw().size(); i++) i < channelMap[Platform::Twitch].raw().size(); i++)
{ {
auto chan = getApp()->twitch->getChannelOrEmpty( auto chan = getIApp()->getTwitchAbstract()->getChannelOrEmpty(
channelMap[Platform::Twitch].raw()[i]); channelMap[Platform::Twitch].raw()[i]);
if (chan->isEmpty()) if (chan->isEmpty())
{ {
@ -202,7 +202,7 @@ void NotificationController::checkStream(bool live, QString channelName)
} }
MessageBuilder builder; MessageBuilder builder;
TwitchMessageBuilder::liveMessage(channelName, &builder); TwitchMessageBuilder::liveMessage(channelName, &builder);
getApp()->twitch->liveChannel->addMessage(builder.release()); getIApp()->getTwitch()->getLiveChannel()->addMessage(builder.release());
// Indicate that we have pushed notifications for this stream // Indicate that we have pushed notifications for this stream
fakeTwitchChannels.push_back(channelName); fakeTwitchChannels.push_back(channelName);
@ -217,7 +217,7 @@ void NotificationController::removeFakeChannel(const QString channelName)
fakeTwitchChannels.erase(it); fakeTwitchChannels.erase(it);
// "delete" old 'CHANNEL is live' message // "delete" old 'CHANNEL is live' message
LimitedQueueSnapshot<MessagePtr> snapshot = LimitedQueueSnapshot<MessagePtr> snapshot =
getApp()->twitch->liveChannel->getMessageSnapshot(); getIApp()->getTwitch()->getLiveChannel()->getMessageSnapshot();
int snapshotLength = snapshot.size(); int snapshotLength = snapshot.size();
// MSVC hates this code if the parens are not there // MSVC hates this code if the parens are not there

View file

@ -300,7 +300,7 @@ int ChannelRef::get_by_name(lua_State *L)
lua_pushnil(L); lua_pushnil(L);
return 1; return 1;
} }
auto chn = getApp()->twitch->getChannelOrEmpty(name); auto chn = getIApp()->getTwitchAbstract()->getChannelOrEmpty(name);
lua::push(L, chn); lua::push(L, chn);
return 1; return 1;
} }
@ -324,7 +324,7 @@ int ChannelRef::get_by_twitch_id(lua_State *L)
lua_pushnil(L); lua_pushnil(L);
return 1; return 1;
} }
auto chn = getApp()->twitch->getChannelOrEmptyByID(id); auto chn = getIApp()->getTwitch()->getChannelOrEmptyByID(id);
lua::push(L, chn); lua::push(L, chn);
return 1; return 1;

View file

@ -17,7 +17,24 @@ class Channel;
using ChannelPtr = std::shared_ptr<Channel>; using ChannelPtr = std::shared_ptr<Channel>;
class RatelimitBucket; class RatelimitBucket;
class AbstractIrcServer : public QObject class IAbstractIrcServer
{
public:
virtual void connect() = 0;
virtual void sendRawMessage(const QString &rawMessage) = 0;
virtual ChannelPtr getOrAddChannel(const QString &dirtyChannelName) = 0;
virtual ChannelPtr getChannelOrEmpty(const QString &dirtyChannelName) = 0;
virtual void addFakeMessage(const QString &data) = 0;
virtual void addGlobalSystemMessage(const QString &messageText) = 0;
virtual void forEachChannel(std::function<void(ChannelPtr)> func) = 0;
};
class AbstractIrcServer : public IAbstractIrcServer, public QObject
{ {
public: public:
enum ConnectionType { Read = 1, Write = 2, Both = 3 }; enum ConnectionType { Read = 1, Write = 2, Both = 3 };
@ -33,27 +50,27 @@ public:
void initializeIrc(); void initializeIrc();
// connection // connection
void connect(); void connect() final;
void disconnect(); void disconnect();
void sendMessage(const QString &channelName, const QString &message); void sendMessage(const QString &channelName, const QString &message);
virtual void sendRawMessage(const QString &rawMessage); void sendRawMessage(const QString &rawMessage) override;
// channels // channels
ChannelPtr getOrAddChannel(const QString &dirtyChannelName); ChannelPtr getOrAddChannel(const QString &dirtyChannelName) final;
ChannelPtr getChannelOrEmpty(const QString &dirtyChannelName); ChannelPtr getChannelOrEmpty(const QString &dirtyChannelName) final;
std::vector<std::weak_ptr<Channel>> getChannels(); std::vector<std::weak_ptr<Channel>> getChannels();
// signals // signals
pajlada::Signals::NoArgSignal connected; pajlada::Signals::NoArgSignal connected;
pajlada::Signals::NoArgSignal disconnected; pajlada::Signals::NoArgSignal disconnected;
void addFakeMessage(const QString &data); void addFakeMessage(const QString &data) final;
void addGlobalSystemMessage(const QString &messageText); void addGlobalSystemMessage(const QString &messageText) final;
// iteration // iteration
void forEachChannel(std::function<void(ChannelPtr)> func); void forEachChannel(std::function<void(ChannelPtr)> func) final;
protected: protected:
AbstractIrcServer(); AbstractIrcServer();

View file

@ -244,7 +244,7 @@ void IrcServer::privateMessageReceived(Communi::IrcPrivateMessage *message)
if (highlighted && showInMentions) if (highlighted && showInMentions)
{ {
getApp()->twitch->mentionsChannel->addMessage(msg); getIApp()->getTwitch()->getMentionsChannel()->addMessage(msg);
} }
} }
else else

View file

@ -13,6 +13,7 @@
#include "messages/MessageColor.hpp" #include "messages/MessageColor.hpp"
#include "messages/MessageElement.hpp" #include "messages/MessageElement.hpp"
#include "messages/MessageThread.hpp" #include "messages/MessageThread.hpp"
#include "providers/irc/AbstractIrcServer.hpp"
#include "providers/twitch/ChannelPointReward.hpp" #include "providers/twitch/ChannelPointReward.hpp"
#include "providers/twitch/TwitchAccount.hpp" #include "providers/twitch/TwitchAccount.hpp"
#include "providers/twitch/TwitchAccountManager.hpp" #include "providers/twitch/TwitchAccountManager.hpp"
@ -169,7 +170,7 @@ void updateReplyParticipatedStatus(const QVariantMap &tags,
} }
ChannelPtr channelOrEmptyByTarget(const QString &target, ChannelPtr channelOrEmptyByTarget(const QString &target,
TwitchIrcServer &server) IAbstractIrcServer &server)
{ {
QString channelName; QString channelName;
if (!trimChannelName(target, channelName)) if (!trimChannelName(target, channelName))
@ -677,9 +678,10 @@ std::vector<MessagePtr> IrcMessageHandler::parseMessageWithReply(
} }
void IrcMessageHandler::handlePrivMessage(Communi::IrcPrivateMessage *message, void IrcMessageHandler::handlePrivMessage(Communi::IrcPrivateMessage *message,
TwitchIrcServer &server) ITwitchIrcServer &twitchServer,
IAbstractIrcServer &abstractIrcServer)
{ {
auto chan = channelOrEmptyByTarget(message->target(), server); auto chan = channelOrEmptyByTarget(message->target(), abstractIrcServer);
if (chan->isEmpty()) if (chan->isEmpty())
{ {
return; return;
@ -710,8 +712,8 @@ void IrcMessageHandler::handlePrivMessage(Communi::IrcPrivateMessage *message,
// https://mm2pl.github.io/emoji_rfc.pdf for more details // https://mm2pl.github.io/emoji_rfc.pdf for more details
this->addMessage( this->addMessage(
message, chan, message, chan,
message->content().replace(COMBINED_FIXER, ZERO_WIDTH_JOINER), server, message->content().replace(COMBINED_FIXER, ZERO_WIDTH_JOINER),
false, message->isAction()); twitchServer, false, message->isAction());
if (message->tags().contains(u"pinned-chat-paid-amount"_s)) if (message->tags().contains(u"pinned-chat-paid-amount"_s))
{ {
@ -733,7 +735,7 @@ void IrcMessageHandler::handleRoomStateMessage(Communi::IrcMessage *message)
{ {
return; return;
} }
auto chan = getApp()->twitch->getChannelOrEmpty(chanName); auto chan = getIApp()->getTwitchAbstract()->getChannelOrEmpty(chanName);
auto *twitchChannel = dynamic_cast<TwitchChannel *>(chan.get()); auto *twitchChannel = dynamic_cast<TwitchChannel *>(chan.get());
if (!twitchChannel) if (!twitchChannel)
@ -795,7 +797,7 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
} }
// get channel // get channel
auto chan = getApp()->twitch->getChannelOrEmpty(chanName); auto chan = getIApp()->getTwitchAbstract()->getChannelOrEmpty(chanName);
if (chan->isEmpty()) if (chan->isEmpty())
{ {
@ -839,7 +841,7 @@ void IrcMessageHandler::handleClearMessageMessage(Communi::IrcMessage *message)
} }
// get channel // get channel
auto chan = getApp()->twitch->getChannelOrEmpty(chanName); auto chan = getIApp()->getTwitchAbstract()->getChannelOrEmpty(chanName);
if (chan->isEmpty()) if (chan->isEmpty())
{ {
@ -888,7 +890,7 @@ void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message)
return; return;
} }
auto c = getApp()->twitch->getChannelOrEmpty(channelName); auto c = getIApp()->getTwitchAbstract()->getChannelOrEmpty(channelName);
if (c->isEmpty()) if (c->isEmpty())
{ {
return; return;
@ -943,7 +945,7 @@ void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *ircMessage)
args.isReceivedWhisper = true; args.isReceivedWhisper = true;
auto *c = getApp()->twitch->whispersChannel.get(); auto *c = getIApp()->getTwitch()->getWhispersChannel().get();
TwitchMessageBuilder builder( TwitchMessageBuilder builder(
c, ircMessage, args, c, ircMessage, args,
@ -959,11 +961,11 @@ void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *ircMessage)
MessagePtr message = builder.build(); MessagePtr message = builder.build();
builder.triggerHighlights(); builder.triggerHighlights();
getApp()->twitch->lastUserThatWhisperedMe.set(builder.userName); getIApp()->getTwitch()->setLastUserThatWhisperedMe(builder.userName);
if (message->flags.has(MessageFlag::ShowInMentions)) if (message->flags.has(MessageFlag::ShowInMentions))
{ {
getApp()->twitch->mentionsChannel->addMessage(message); getIApp()->getTwitch()->getMentionsChannel()->addMessage(message);
} }
c->addMessage(message); c->addMessage(message);
@ -976,15 +978,16 @@ void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *ircMessage)
!(getSettings()->streamerModeSuppressInlineWhispers && !(getSettings()->streamerModeSuppressInlineWhispers &&
getIApp()->getStreamerMode()->isEnabled())) getIApp()->getStreamerMode()->isEnabled()))
{ {
getApp()->twitch->forEachChannel( getIApp()->getTwitchAbstract()->forEachChannel(
[&message, overrideFlags](ChannelPtr channel) { [&message, overrideFlags](ChannelPtr channel) {
channel->addMessage(message, overrideFlags); channel->addMessage(message, overrideFlags);
}); });
} }
} }
void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message, void IrcMessageHandler::handleUserNoticeMessage(
TwitchIrcServer &server) Communi::IrcMessage *message, ITwitchIrcServer &twitchServer,
IAbstractIrcServer &abstractIrcServer)
{ {
auto tags = message->tags(); auto tags = message->tags();
auto parameters = message->parameters(); auto parameters = message->parameters();
@ -997,7 +1000,7 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message,
content = parameters[1]; content = parameters[1];
} }
auto chn = server.getChannelOrEmpty(target); auto chn = abstractIrcServer.getChannelOrEmpty(target);
if (isIgnoredMessage({ if (isIgnoredMessage({
.message = content, .message = content,
.twitchUserID = tags.value("user-id").toString(), .twitchUserID = tags.value("user-id").toString(),
@ -1013,7 +1016,7 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message,
// Messages are not required, so they might be empty // Messages are not required, so they might be empty
if (!content.isEmpty()) if (!content.isEmpty())
{ {
this->addMessage(message, chn, content, server, true, false); this->addMessage(message, chn, content, twitchServer, true, false);
} }
} }
@ -1090,7 +1093,7 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message,
return; return;
} }
auto chan = server.getChannelOrEmpty(channelName); auto chan = abstractIrcServer.getChannelOrEmpty(channelName);
if (!chan->isEmpty()) if (!chan->isEmpty())
{ {
@ -1111,7 +1114,7 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message)
{ {
// Notice wasn't targeted at a single channel, send to all twitch // Notice wasn't targeted at a single channel, send to all twitch
// channels // channels
getApp()->twitch->forEachChannelAndSpecialChannels( getIApp()->getTwitch()->forEachChannelAndSpecialChannels(
[msg](const auto &c) { [msg](const auto &c) {
c->addMessage(msg); c->addMessage(msg);
}); });
@ -1119,7 +1122,8 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message)
return; return;
} }
auto channel = getApp()->twitch->getChannelOrEmpty(channelName); auto channel =
getIApp()->getTwitchAbstract()->getChannelOrEmpty(channelName);
if (channel->isEmpty()) if (channel->isEmpty())
{ {
@ -1202,8 +1206,8 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message)
void IrcMessageHandler::handleJoinMessage(Communi::IrcMessage *message) void IrcMessageHandler::handleJoinMessage(Communi::IrcMessage *message)
{ {
auto channel = auto channel = getIApp()->getTwitchAbstract()->getChannelOrEmpty(
getApp()->twitch->getChannelOrEmpty(message->parameter(0).remove(0, 1)); message->parameter(0).remove(0, 1));
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()); auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (!twitchChannel) if (!twitchChannel)
@ -1225,8 +1229,8 @@ void IrcMessageHandler::handleJoinMessage(Communi::IrcMessage *message)
void IrcMessageHandler::handlePartMessage(Communi::IrcMessage *message) void IrcMessageHandler::handlePartMessage(Communi::IrcMessage *message)
{ {
auto channel = auto channel = getIApp()->getTwitchAbstract()->getChannelOrEmpty(
getApp()->twitch->getChannelOrEmpty(message->parameter(0).remove(0, 1)); message->parameter(0).remove(0, 1));
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()); auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (!twitchChannel) if (!twitchChannel)
@ -1311,7 +1315,7 @@ void IrcMessageHandler::setSimilarityFlags(const MessagePtr &message,
void IrcMessageHandler::addMessage(Communi::IrcMessage *message, void IrcMessageHandler::addMessage(Communi::IrcMessage *message,
const ChannelPtr &chan, const ChannelPtr &chan,
const QString &originalContent, const QString &originalContent,
TwitchIrcServer &server, bool isSub, ITwitchIrcServer &server, bool isSub,
bool isAction) bool isAction)
{ {
if (chan->isEmpty()) if (chan->isEmpty())
@ -1446,7 +1450,7 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *message,
if (highlighted && showInMentions) if (highlighted && showInMentions)
{ {
server.mentionsChannel->addMessage(msg); server.getMentionsChannel()->addMessage(msg);
} }
chan->addMessage(msg); chan->addMessage(msg);

View file

@ -9,7 +9,8 @@
namespace chatterino { namespace chatterino {
class TwitchIrcServer; class IAbstractIrcServer;
class ITwitchIrcServer;
class Channel; class Channel;
using ChannelPtr = std::shared_ptr<Channel>; using ChannelPtr = std::shared_ptr<Channel>;
struct Message; struct Message;
@ -38,7 +39,8 @@ public:
std::vector<MessagePtr> &otherLoaded); std::vector<MessagePtr> &otherLoaded);
void handlePrivMessage(Communi::IrcPrivateMessage *message, void handlePrivMessage(Communi::IrcPrivateMessage *message,
TwitchIrcServer &server); ITwitchIrcServer &twitchServer,
IAbstractIrcServer &abstractIrcServer);
void handleRoomStateMessage(Communi::IrcMessage *message); void handleRoomStateMessage(Communi::IrcMessage *message);
void handleClearChatMessage(Communi::IrcMessage *message); void handleClearChatMessage(Communi::IrcMessage *message);
@ -48,7 +50,8 @@ public:
void handleWhisperMessage(Communi::IrcMessage *ircMessage); void handleWhisperMessage(Communi::IrcMessage *ircMessage);
void handleUserNoticeMessage(Communi::IrcMessage *message, void handleUserNoticeMessage(Communi::IrcMessage *message,
TwitchIrcServer &server); ITwitchIrcServer &twitchServer,
IAbstractIrcServer &abstractIrcServer);
void handleNoticeMessage(Communi::IrcNoticeMessage *message); void handleNoticeMessage(Communi::IrcNoticeMessage *message);
@ -56,7 +59,7 @@ public:
void handlePartMessage(Communi::IrcMessage *message); void handlePartMessage(Communi::IrcMessage *message);
void addMessage(Communi::IrcMessage *message, const ChannelPtr &chan, void addMessage(Communi::IrcMessage *message, const ChannelPtr &chan,
const QString &originalContent, TwitchIrcServer &server, const QString &originalContent, ITwitchIrcServer &server,
bool isSub, bool isAction); bool isSub, bool isAction);
private: private:

View file

@ -176,7 +176,8 @@ TwitchChannel::TwitchChannel(const QString &name)
TwitchMessageBuilder::liveMessage(this->getDisplayName(), TwitchMessageBuilder::liveMessage(this->getDisplayName(),
&builder2); &builder2);
builder2.message().id = this->roomId(); builder2.message().id = this->roomId();
getApp()->twitch->liveChannel->addMessage(builder2.release()); getIApp()->getTwitch()->getLiveChannel()->addMessage(
builder2.release());
// Notify on all channels with a ping sound // Notify on all channels with a ping sound
if (getSettings()->notificationOnAnyChannel && if (getSettings()->notificationOnAnyChannel &&
@ -198,7 +199,7 @@ TwitchChannel::TwitchChannel(const QString &name)
// "delete" old 'CHANNEL is live' message // "delete" old 'CHANNEL is live' message
LimitedQueueSnapshot<MessagePtr> snapshot = LimitedQueueSnapshot<MessagePtr> snapshot =
getApp()->twitch->liveChannel->getMessageSnapshot(); getIApp()->getTwitch()->getLiveChannel()->getMessageSnapshot();
int snapshotLength = snapshot.size(); int snapshotLength = snapshot.size();
// MSVC hates this code if the parens are not there // MSVC hates this code if the parens are not there
@ -237,17 +238,18 @@ TwitchChannel::~TwitchChannel()
return; return;
} }
getApp()->twitch->dropSeventvChannel(this->seventvUserID_, getIApp()->getTwitch()->dropSeventvChannel(this->seventvUserID_,
this->seventvEmoteSetID_); this->seventvEmoteSetID_);
if (getApp()->twitch->bttvLiveUpdates) if (getIApp()->getTwitch()->getBTTVLiveUpdates())
{ {
getApp()->twitch->bttvLiveUpdates->partChannel(this->roomId()); getIApp()->getTwitch()->getBTTVLiveUpdates()->partChannel(
this->roomId());
} }
if (getApp()->twitch->seventvEventAPI) if (getIApp()->getTwitch()->getSeventvEventAPI())
{ {
getApp()->twitch->seventvEventAPI->unsubscribeTwitchChannel( getIApp()->getTwitch()->getSeventvEventAPI()->unsubscribeTwitchChannel(
this->roomId()); this->roomId());
} }
} }
@ -425,7 +427,7 @@ void TwitchChannel::addChannelPointReward(const ChannelPointReward &reward)
<< "] Channel point reward added:" << reward.id << "," << "] Channel point reward added:" << reward.id << ","
<< reward.title << "," << reward.isUserInputRequired; << reward.title << "," << reward.isUserInputRequired;
auto *server = getApp()->twitch; auto *server = getIApp()->getTwitch();
auto it = std::remove_if( auto it = std::remove_if(
this->waitingRedemptions_.begin(), this->waitingRedemptions_.end(), this->waitingRedemptions_.begin(), this->waitingRedemptions_.end(),
[&](const QueuedRedemption &msg) { [&](const QueuedRedemption &msg) {
@ -776,7 +778,7 @@ bool TwitchChannel::canReconnect() const
void TwitchChannel::reconnect() void TwitchChannel::reconnect()
{ {
getApp()->twitch->connect(); getIApp()->getTwitchAbstract()->connect();
} }
QString TwitchChannel::roomId() const QString TwitchChannel::roomId() const
@ -891,7 +893,7 @@ const QString &TwitchChannel::seventvEmoteSetID() const
void TwitchChannel::joinBttvChannel() const void TwitchChannel::joinBttvChannel() const
{ {
if (getApp()->twitch->bttvLiveUpdates) if (getIApp()->getTwitch()->getBTTVLiveUpdates())
{ {
const auto currentAccount = const auto currentAccount =
getIApp()->getAccounts()->twitch.getCurrent(); getIApp()->getAccounts()->twitch.getCurrent();
@ -900,8 +902,8 @@ void TwitchChannel::joinBttvChannel() const
{ {
userName = currentAccount->getUserName(); userName = currentAccount->getUserName();
} }
getApp()->twitch->bttvLiveUpdates->joinChannel(this->roomId(), getIApp()->getTwitch()->getBTTVLiveUpdates()->joinChannel(
userName); this->roomId(), userName);
} }
} }
@ -1048,14 +1050,14 @@ void TwitchChannel::updateSeventvData(const QString &newUserID,
this->seventvUserID_ = newUserID; this->seventvUserID_ = newUserID;
this->seventvEmoteSetID_ = newEmoteSetID; this->seventvEmoteSetID_ = newEmoteSetID;
runInGuiThread([this, oldUserID, oldEmoteSetID]() { runInGuiThread([this, oldUserID, oldEmoteSetID]() {
if (getApp()->twitch->seventvEventAPI) if (getIApp()->getTwitch()->getSeventvEventAPI())
{ {
getApp()->twitch->seventvEventAPI->subscribeUser( getIApp()->getTwitch()->getSeventvEventAPI()->subscribeUser(
this->seventvUserID_, this->seventvEmoteSetID_); this->seventvUserID_, this->seventvEmoteSetID_);
if (oldUserID || oldEmoteSetID) if (oldUserID || oldEmoteSetID)
{ {
getApp()->twitch->dropSeventvChannel( getIApp()->getTwitch()->dropSeventvChannel(
oldUserID.value_or(QString()), oldUserID.value_or(QString()),
oldEmoteSetID.value_or(QString())); oldEmoteSetID.value_or(QString()));
} }
@ -1251,7 +1253,8 @@ void TwitchChannel::loadRecentMessages()
tc->addRecentChatter(msg->displayName); tc->addRecentChatter(msg->displayName);
} }
getApp()->twitch->mentionsChannel->fillInMissingMessages(msgs); getIApp()->getTwitch()->getMentionsChannel()->fillInMissingMessages(
msgs);
}, },
[weak]() { [weak]() {
auto shared = weak.lock(); auto shared = weak.lock();
@ -1841,9 +1844,9 @@ void TwitchChannel::updateSevenTVActivity()
void TwitchChannel::listenSevenTVCosmetics() const void TwitchChannel::listenSevenTVCosmetics() const
{ {
if (getApp()->twitch->seventvEventAPI) if (getIApp()->getTwitch()->getSeventvEventAPI())
{ {
getApp()->twitch->seventvEventAPI->subscribeTwitchChannel( getIApp()->getTwitch()->getSeventvEventAPI()->subscribeTwitchChannel(
this->roomId()); this->roomId());
} }
} }

View file

@ -263,7 +263,7 @@ std::shared_ptr<Channel> TwitchIrcServer::createChannel(
void TwitchIrcServer::privateMessageReceived( void TwitchIrcServer::privateMessageReceived(
Communi::IrcPrivateMessage *message) Communi::IrcPrivateMessage *message)
{ {
IrcMessageHandler::instance().handlePrivMessage(message, *this); IrcMessageHandler::instance().handlePrivMessage(message, *this, *this);
} }
void TwitchIrcServer::readConnectionMessageReceived( void TwitchIrcServer::readConnectionMessageReceived(
@ -310,7 +310,7 @@ void TwitchIrcServer::readConnectionMessageReceived(
} }
else if (command == "USERNOTICE") else if (command == "USERNOTICE")
{ {
handler.handleUserNoticeMessage(message, *this); handler.handleUserNoticeMessage(message, *this, *this);
} }
else if (command == "NOTICE") else if (command == "NOTICE")
{ {
@ -645,16 +645,56 @@ void TwitchIrcServer::onReplySendRequested(
sent = true; sent = true;
} }
std::unique_ptr<BttvLiveUpdates> &TwitchIrcServer::getBTTVLiveUpdates()
{
return this->bttvLiveUpdates;
}
std::unique_ptr<SeventvEventAPI> &TwitchIrcServer::getSeventvEventAPI()
{
return this->seventvEventAPI;
}
const IndirectChannel &TwitchIrcServer::getWatchingChannel() const const IndirectChannel &TwitchIrcServer::getWatchingChannel() const
{ {
return this->watchingChannel; return this->watchingChannel;
} }
void TwitchIrcServer::setWatchingChannel(ChannelPtr newWatchingChannel)
{
this->watchingChannel.reset(newWatchingChannel);
}
ChannelPtr TwitchIrcServer::getWhispersChannel() const
{
return this->whispersChannel;
}
ChannelPtr TwitchIrcServer::getMentionsChannel() const
{
return this->mentionsChannel;
}
ChannelPtr TwitchIrcServer::getLiveChannel() const
{
return this->liveChannel;
}
ChannelPtr TwitchIrcServer::getAutomodChannel() const
{
return this->automodChannel;
}
QString TwitchIrcServer::getLastUserThatWhisperedMe() const QString TwitchIrcServer::getLastUserThatWhisperedMe() const
{ {
return this->lastUserThatWhisperedMe.get(); return this->lastUserThatWhisperedMe.get();
} }
void TwitchIrcServer::setLastUserThatWhisperedMe(const QString &user)
{
this->lastUserThatWhisperedMe.set(user);
}
void TwitchIrcServer::reloadBTTVGlobalEmotes() void TwitchIrcServer::reloadBTTVGlobalEmotes()
{ {
getIApp()->getBttvEmotes()->loadEmotes(); getIApp()->getBttvEmotes()->loadEmotes();

View file

@ -27,9 +27,27 @@ class ITwitchIrcServer
public: public:
virtual ~ITwitchIrcServer() = default; virtual ~ITwitchIrcServer() = default;
virtual void forEachChannelAndSpecialChannels(
std::function<void(ChannelPtr)> func) = 0;
virtual std::shared_ptr<Channel> getChannelOrEmptyByID(
const QString &channelID) = 0;
virtual void dropSeventvChannel(const QString &userID,
const QString &emoteSetID) = 0;
virtual std::unique_ptr<BttvLiveUpdates> &getBTTVLiveUpdates() = 0;
virtual std::unique_ptr<SeventvEventAPI> &getSeventvEventAPI() = 0;
virtual const IndirectChannel &getWatchingChannel() const = 0; virtual const IndirectChannel &getWatchingChannel() const = 0;
virtual void setWatchingChannel(ChannelPtr newWatchingChannel) = 0;
virtual ChannelPtr getWhispersChannel() const = 0;
virtual ChannelPtr getMentionsChannel() const = 0;
virtual ChannelPtr getLiveChannel() const = 0;
virtual ChannelPtr getAutomodChannel() const = 0;
virtual QString getLastUserThatWhisperedMe() const = 0; virtual QString getLastUserThatWhisperedMe() const = 0;
virtual void setLastUserThatWhisperedMe(const QString &user) = 0;
// Update this interface with TwitchIrcServer methods as needed // Update this interface with TwitchIrcServer methods as needed
}; };
@ -44,9 +62,11 @@ public:
void initialize(Settings &settings, const Paths &paths) override; void initialize(Settings &settings, const Paths &paths) override;
void forEachChannelAndSpecialChannels(std::function<void(ChannelPtr)> func); void forEachChannelAndSpecialChannels(
std::function<void(ChannelPtr)> func) override;
std::shared_ptr<Channel> getChannelOrEmptyByID(const QString &channelID); std::shared_ptr<Channel> getChannelOrEmptyByID(
const QString &channelID) override;
void reloadBTTVGlobalEmotes(); void reloadBTTVGlobalEmotes();
void reloadAllBTTVChannelEmotes(); void reloadAllBTTVChannelEmotes();
@ -68,8 +88,10 @@ public:
* It's currently not possible to share emote sets among users, * It's currently not possible to share emote sets among users,
* but it's a commonly requested feature. * but it's a commonly requested feature.
*/ */
void dropSeventvChannel(const QString &userID, const QString &emoteSetID); void dropSeventvChannel(const QString &userID,
const QString &emoteSetID) override;
private:
Atomic<QString> lastUserThatWhisperedMe; Atomic<QString> lastUserThatWhisperedMe;
const ChannelPtr whispersChannel; const ChannelPtr whispersChannel;
@ -81,9 +103,19 @@ public:
std::unique_ptr<BttvLiveUpdates> bttvLiveUpdates; std::unique_ptr<BttvLiveUpdates> bttvLiveUpdates;
std::unique_ptr<SeventvEventAPI> seventvEventAPI; std::unique_ptr<SeventvEventAPI> seventvEventAPI;
public:
std::unique_ptr<BttvLiveUpdates> &getBTTVLiveUpdates() override;
std::unique_ptr<SeventvEventAPI> &getSeventvEventAPI() override;
const IndirectChannel &getWatchingChannel() const override; const IndirectChannel &getWatchingChannel() const override;
void setWatchingChannel(ChannelPtr newWatchingChannel) override;
ChannelPtr getWhispersChannel() const override;
ChannelPtr getMentionsChannel() const override;
ChannelPtr getLiveChannel() const override;
ChannelPtr getAutomodChannel() const override;
QString getLastUserThatWhisperedMe() const override; QString getLastUserThatWhisperedMe() const override;
void setLastUserThatWhisperedMe(const QString &user) override;
protected: protected:
void initializeConnection(IrcConnection *connection, void initializeConnection(IrcConnection *connection,

View file

@ -236,14 +236,13 @@ void NativeMessagingServer::ReceiverThread::handleSelect(
} }
postToThread([=] { postToThread([=] {
auto *app = getApp();
if (!name.isEmpty()) if (!name.isEmpty())
{ {
auto channel = app->twitch->getOrAddChannel(name); auto channel =
if (app->twitch->watchingChannel.get() != channel) getIApp()->getTwitchAbstract()->getOrAddChannel(name);
if (getIApp()->getTwitch()->getWatchingChannel().get() != channel)
{ {
app->twitch->watchingChannel.reset(channel); getIApp()->getTwitch()->setWatchingChannel(channel);
} }
} }
@ -253,7 +252,8 @@ void NativeMessagingServer::ReceiverThread::handleSelect(
auto *window = AttachedWindow::getForeground(args); auto *window = AttachedWindow::getForeground(args);
if (!name.isEmpty()) if (!name.isEmpty())
{ {
window->setChannel(app->twitch->getOrAddChannel(name)); window->setChannel(
getIApp()->getTwitchAbstract()->getOrAddChannel(name));
} }
#endif #endif
} }
@ -294,8 +294,6 @@ void NativeMessagingServer::syncChannels(const QJsonArray &twitchChannels)
{ {
assertInGuiThread(); assertInGuiThread();
auto *app = getApp();
std::vector<ChannelPtr> updated; std::vector<ChannelPtr> updated;
updated.reserve(twitchChannels.size()); updated.reserve(twitchChannels.size());
for (const auto &value : twitchChannels) for (const auto &value : twitchChannels)
@ -306,7 +304,8 @@ void NativeMessagingServer::syncChannels(const QJsonArray &twitchChannels)
continue; continue;
} }
// the deduping is done on the extension side // the deduping is done on the extension side
updated.emplace_back(app->twitch->getOrAddChannel(name)); updated.emplace_back(
getIApp()->getTwitchAbstract()->getOrAddChannel(name));
} }
// This will destroy channels that aren't used anymore. // This will destroy channels that aren't used anymore.

View file

@ -74,7 +74,7 @@ bool isBroadcasterSoftwareActive()
shouldShowTimeoutWarning = false; shouldShowTimeoutWarning = false;
postToThread([] { postToThread([] {
getApp()->twitch->addGlobalSystemMessage( getIApp()->getTwitchAbstract()->addGlobalSystemMessage(
"Streamer Mode is set to Automatic, but pgrep timed " "Streamer Mode is set to Automatic, but pgrep timed "
"out. This can happen if your system lagged at the " "out. This can happen if your system lagged at the "
"wrong moment. If Streamer Mode continues to not work, " "wrong moment. If Streamer Mode continues to not work, "
@ -94,7 +94,7 @@ bool isBroadcasterSoftwareActive()
shouldShowWarning = false; shouldShowWarning = false;
postToThread([] { postToThread([] {
getApp()->twitch->addGlobalSystemMessage( getIApp()->getTwitchAbstract()->addGlobalSystemMessage(
"Streamer Mode is set to Automatic, but pgrep is " "Streamer Mode is set to Automatic, but pgrep is "
"missing. " "missing. "
"Install it to fix the issue or set Streamer Mode to " "Install it to fix the issue or set Streamer Mode to "

View file

@ -688,27 +688,28 @@ IndirectChannel WindowManager::decodeChannel(const SplitDescriptor &descriptor)
if (descriptor.type_ == "twitch") if (descriptor.type_ == "twitch")
{ {
return app->twitch->getOrAddChannel(descriptor.channelName_); return getIApp()->getTwitchAbstract()->getOrAddChannel(
descriptor.channelName_);
} }
else if (descriptor.type_ == "mentions") else if (descriptor.type_ == "mentions")
{ {
return app->twitch->mentionsChannel; return getIApp()->getTwitch()->getMentionsChannel();
} }
else if (descriptor.type_ == "watching") else if (descriptor.type_ == "watching")
{ {
return app->twitch->watchingChannel; return getIApp()->getTwitch()->getWatchingChannel();
} }
else if (descriptor.type_ == "whispers") else if (descriptor.type_ == "whispers")
{ {
return app->twitch->whispersChannel; return getIApp()->getTwitch()->getWhispersChannel();
} }
else if (descriptor.type_ == "live") else if (descriptor.type_ == "live")
{ {
return app->twitch->liveChannel; return getIApp()->getTwitch()->getLiveChannel();
} }
else if (descriptor.type_ == "automod") else if (descriptor.type_ == "automod")
{ {
return app->twitch->automodChannel; return getIApp()->getTwitch()->getAutomodChannel();
} }
else if (descriptor.type_ == "irc") else if (descriptor.type_ == "irc")
{ {
@ -717,7 +718,8 @@ IndirectChannel WindowManager::decodeChannel(const SplitDescriptor &descriptor)
} }
else if (descriptor.type_ == "misc") else if (descriptor.type_ == "misc")
{ {
return app->twitch->getChannelOrEmpty(descriptor.channelName_); return getIApp()->getTwitchAbstract()->getChannelOrEmpty(
descriptor.channelName_);
} }
return Channel::getEmpty(); return Channel::getEmpty();

View file

@ -54,7 +54,8 @@ bool FramelessEmbedWindow::nativeEvent(const QByteArray &eventType,
auto channelName = root.value("channel-name").toString(); auto channelName = root.value("channel-name").toString();
this->split_->setChannel( this->split_->setChannel(
getApp()->twitch->getOrAddChannel(channelName)); getIApp()->getTwitchAbstract()->getOrAddChannel(
channelName));
} }
} }
} }

View file

@ -254,7 +254,7 @@ void Window::addDebugStuff(HotkeyController::HotkeyMap &actions)
const auto &messages = getSampleMiscMessages(); const auto &messages = getSampleMiscMessages();
static int index = 0; static int index = 0;
const auto &msg = messages[index++ % messages.size()]; const auto &msg = messages[index++ % messages.size()];
getApp()->twitch->addFakeMessage(msg); getIApp()->getTwitchAbstract()->addFakeMessage(msg);
return ""; return "";
}); });
@ -262,7 +262,7 @@ void Window::addDebugStuff(HotkeyController::HotkeyMap &actions)
const auto &messages = getSampleCheerMessages(); const auto &messages = getSampleCheerMessages();
static int index = 0; static int index = 0;
const auto &msg = messages[index++ % messages.size()]; const auto &msg = messages[index++ % messages.size()];
getApp()->twitch->addFakeMessage(msg); getIApp()->getTwitchAbstract()->addFakeMessage(msg);
return ""; return "";
}); });
@ -270,7 +270,7 @@ void Window::addDebugStuff(HotkeyController::HotkeyMap &actions)
const auto &messages = getSampleLinkMessages(); const auto &messages = getSampleLinkMessages();
static int index = 0; static int index = 0;
const auto &msg = messages[index++ % messages.size()]; const auto &msg = messages[index++ % messages.size()];
getApp()->twitch->addFakeMessage(msg); getIApp()->getTwitchAbstract()->addFakeMessage(msg);
return ""; return "";
}); });
@ -286,7 +286,8 @@ void Window::addDebugStuff(HotkeyController::HotkeyMap &actions)
oMessage->toInner<PubSubMessageMessage>() oMessage->toInner<PubSubMessageMessage>()
->toInner<PubSubCommunityPointsChannelV1Message>(); ->toInner<PubSubCommunityPointsChannelV1Message>();
app->twitch->addFakeMessage(getSampleChannelRewardIRCMessage()); getIApp()->getTwitchAbstract()->addFakeMessage(
getSampleChannelRewardIRCMessage());
getIApp()->getTwitchPubSub()->pointReward.redeemed.invoke( getIApp()->getTwitchPubSub()->pointReward.redeemed.invoke(
oInnerMessage->data.value("redemption").toObject()); oInnerMessage->data.value("redemption").toObject());
alt = !alt; alt = !alt;
@ -309,7 +310,7 @@ void Window::addDebugStuff(HotkeyController::HotkeyMap &actions)
const auto &messages = getSampleEmoteTestMessages(); const auto &messages = getSampleEmoteTestMessages();
static int index = 0; static int index = 0;
const auto &msg = messages[index++ % messages.size()]; const auto &msg = messages[index++ % messages.size()];
getApp()->twitch->addFakeMessage(msg); getIApp()->getTwitchAbstract()->addFakeMessage(msg);
return ""; return "";
}); });
@ -317,7 +318,7 @@ void Window::addDebugStuff(HotkeyController::HotkeyMap &actions)
const auto &messages = getSampleSubMessages(); const auto &messages = getSampleSubMessages();
static int index = 0; static int index = 0;
const auto &msg = messages[index++ % messages.size()]; const auto &msg = messages[index++ % messages.size()];
getApp()->twitch->addFakeMessage(msg); getIApp()->getTwitchAbstract()->addFakeMessage(msg);
return ""; return "";
}); });
#endif #endif
@ -480,8 +481,8 @@ void Window::addShortcuts()
splitContainer = this->notebook_->getOrAddSelectedPage(); splitContainer = this->notebook_->getOrAddSelectedPage();
} }
Split *split = new Split(splitContainer); Split *split = new Split(splitContainer);
split->setChannel( split->setChannel(getIApp()->getTwitchAbstract()->getOrAddChannel(
getApp()->twitch->getOrAddChannel(si.channelName)); si.channelName));
split->setFilters(si.filters); split->setFilters(si.filters);
splitContainer->insertSplit(split); splitContainer->insertSplit(split);
splitContainer->setSelected(split); splitContainer->setSelected(split);

View file

@ -375,35 +375,33 @@ IndirectChannel SelectChannelDialog::getSelectedChannel() const
return this->selectedChannel_; return this->selectedChannel_;
} }
auto *app = getApp();
switch (this->ui_.notebook->getSelectedIndex()) switch (this->ui_.notebook->getSelectedIndex())
{ {
case TAB_TWITCH: { case TAB_TWITCH: {
if (this->ui_.twitch.channel->isChecked()) if (this->ui_.twitch.channel->isChecked())
{ {
return app->twitch->getOrAddChannel( return getIApp()->getTwitchAbstract()->getOrAddChannel(
this->ui_.twitch.channelName->text().trimmed()); this->ui_.twitch.channelName->text().trimmed());
} }
else if (this->ui_.twitch.watching->isChecked()) else if (this->ui_.twitch.watching->isChecked())
{ {
return app->twitch->watchingChannel; return getIApp()->getTwitch()->getWatchingChannel();
} }
else if (this->ui_.twitch.mentions->isChecked()) else if (this->ui_.twitch.mentions->isChecked())
{ {
return app->twitch->mentionsChannel; return getIApp()->getTwitch()->getMentionsChannel();
} }
else if (this->ui_.twitch.whispers->isChecked()) else if (this->ui_.twitch.whispers->isChecked())
{ {
return app->twitch->whispersChannel; return getIApp()->getTwitch()->getWhispersChannel();
} }
else if (this->ui_.twitch.live->isChecked()) else if (this->ui_.twitch.live->isChecked())
{ {
return app->twitch->liveChannel; return getIApp()->getTwitch()->getLiveChannel();
} }
else if (this->ui_.twitch.automod->isChecked()) else if (this->ui_.twitch.automod->isChecked())
{ {
return app->twitch->automodChannel; return getIApp()->getTwitch()->getAutomodChannel();
} }
} }
break; break;

View file

@ -298,21 +298,23 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, Split *split)
menu->addAction( menu->addAction(
"Open channel in a new popup window", this, "Open channel in a new popup window", this,
[loginName] { [loginName] {
auto *app = getApp(); auto *app = getIApp();
auto &window = app->getWindows()->createWindow( auto &window = app->getWindows()->createWindow(
WindowType::Popup, true); WindowType::Popup, true);
auto *split = window.getNotebook() auto *split = window.getNotebook()
.getOrAddSelectedPage() .getOrAddSelectedPage()
->appendNewSplit(false); ->appendNewSplit(false);
split->setChannel(app->twitch->getOrAddChannel( split->setChannel(
loginName.toLower())); app->getTwitchAbstract()->getOrAddChannel(
loginName.toLower()));
}); });
menu->addAction( menu->addAction(
"Open channel in a new tab", this, [loginName] { "Open channel in a new tab", this, [loginName] {
ChannelPtr channel = ChannelPtr channel =
getApp()->twitch->getOrAddChannel( getIApp()
loginName); ->getTwitchAbstract()
->getOrAddChannel(loginName);
auto &nb = getApp() auto &nb = getApp()
->getWindows() ->getWindows()
->getMainWindow() ->getMainWindow()

View file

@ -21,7 +21,8 @@ NewPopupItem::NewPopupItem(const QString &channelName)
void NewPopupItem::action() void NewPopupItem::action()
{ {
auto channel = getApp()->twitch->getOrAddChannel(this->channelName_); auto channel =
getIApp()->getTwitchAbstract()->getOrAddChannel(this->channelName_);
getIApp()->getWindows()->openInPopup(channel); getIApp()->getWindows()->openInPopup(channel);
} }

View file

@ -26,7 +26,8 @@ void NewTabItem::action()
SplitContainer *container = nb.addPage(true); SplitContainer *container = nb.addPage(true);
Split *split = new Split(container); Split *split = new Split(container);
split->setChannel(getApp()->twitch->getOrAddChannel(this->channelName_)); split->setChannel(
getIApp()->getTwitchAbstract()->getOrAddChannel(this->channelName_));
container->insertSplit(split); container->insertSplit(split);
} }

View file

@ -1383,17 +1383,20 @@ MessageElementFlags ChannelView::getFlags() const
{ {
flags.set(MessageElementFlag::ModeratorTools); flags.set(MessageElementFlag::ModeratorTools);
} }
if (this->underlyingChannel_ == app->twitch->mentionsChannel || if (this->underlyingChannel_ ==
this->underlyingChannel_ == app->twitch->liveChannel || getIApp()->getTwitch()->getMentionsChannel() ||
this->underlyingChannel_ == app->twitch->automodChannel) this->underlyingChannel_ ==
getIApp()->getTwitch()->getLiveChannel() ||
this->underlyingChannel_ ==
getIApp()->getTwitch()->getAutomodChannel())
{ {
flags.set(MessageElementFlag::ChannelName); flags.set(MessageElementFlag::ChannelName);
flags.unset(MessageElementFlag::ChannelPointReward); flags.unset(MessageElementFlag::ChannelPointReward);
} }
} }
if (this->sourceChannel_ == app->twitch->mentionsChannel || if (this->sourceChannel_ == getIApp()->getTwitch()->getMentionsChannel() ||
this->sourceChannel_ == app->twitch->automodChannel) this->sourceChannel_ == getIApp()->getTwitch()->getAutomodChannel())
{ {
flags.set(MessageElementFlag::ChannelName); flags.set(MessageElementFlag::ChannelName);
} }
@ -1546,8 +1549,8 @@ void ChannelView::drawMessages(QPainter &painter, const QRect &area)
.canvasWidth = this->width(), .canvasWidth = this->width(),
.isWindowFocused = this->window() == QApplication::activeWindow(), .isWindowFocused = this->window() == QApplication::activeWindow(),
.isMentions = .isMentions = this->underlyingChannel_ ==
this->underlyingChannel_ == getApp()->twitch->mentionsChannel, getIApp()->getTwitch()->getMentionsChannel(),
.y = int(-(messagesSnapshot[start]->getHeight() * .y = int(-(messagesSnapshot[start]->getHeight() *
(fmod(this->scrollBar_->getRelativeCurrentValue(), 1)))), (fmod(this->scrollBar_->getRelativeCurrentValue(), 1)))),
@ -2707,8 +2710,8 @@ void ChannelView::showUserInfoPopup(const QString &userName,
auto *userPopup = auto *userPopup =
new UserInfoPopup(getSettings()->autoCloseUserPopup, this->split_); new UserInfoPopup(getSettings()->autoCloseUserPopup, this->split_);
auto contextChannel = auto contextChannel = getIApp()->getTwitchAbstract()->getChannelOrEmpty(
getApp()->twitch->getChannelOrEmpty(alternativePopoutChannel); alternativePopoutChannel);
auto openingChannel = this->hasSourceChannel() ? this->sourceChannel_ auto openingChannel = this->hasSourceChannel() ? this->sourceChannel_
: this->underlyingChannel_; : this->underlyingChannel_;
userPopup->setData(userName, contextChannel, openingChannel); userPopup->setData(userName, contextChannel, openingChannel);

View file

@ -570,7 +570,7 @@ void GeneralPage::initLayout(GeneralPageView &layout)
// as an official description from 7TV devs is best // as an official description from 7TV devs is best
s.showUnlistedSevenTVEmotes.connect( s.showUnlistedSevenTVEmotes.connect(
[]() { []() {
getApp()->twitch->forEachChannelAndSpecialChannels( getIApp()->getTwitch()->forEachChannelAndSpecialChannels(
[](const auto &c) { [](const auto &c) {
if (c->isTwitchChannel()) if (c->isTwitchChannel())
{ {

View file

@ -272,7 +272,7 @@ Split::Split(QWidget *parent)
std::ignore = this->view_->openChannelIn.connect( std::ignore = this->view_->openChannelIn.connect(
[this](QString twitchChannel, FromTwitchLinkOpenChannelIn openIn) { [this](QString twitchChannel, FromTwitchLinkOpenChannelIn openIn) {
ChannelPtr channel = ChannelPtr channel =
getApp()->twitch->getOrAddChannel(twitchChannel); getIApp()->getTwitchAbstract()->getOrAddChannel(twitchChannel);
switch (openIn) switch (openIn)
{ {
case FromTwitchLinkOpenChannelIn::Split: case FromTwitchLinkOpenChannelIn::Split: