Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: load Twitch emotes from Helix #5239

Merged
merged 22 commits into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
- Bugfix: Fixed splits staying paused after unfocusing Chatterino in certain configurations. (#5504)
- Bugfix: Links with invalid characters in the domain are no longer detected. (#5509)
- Bugfix: Fixed janky selection for messages with RTL segments (selection is still wrong, but consistently wrong). (#5525)
- Bugfix: Fixed event emotes not showing up in autocomplete and popups. (#5239)
- Bugfix: Fixed tab visibility being controllable in the emote popup. (#5530)
- Bugfix: Fixed account switch not being saved if no other settings were changed. (#5558)
- Dev: Update Windows build from Qt 6.5.0 to Qt 6.7.1. (#5420)
Expand Down
7 changes: 7 additions & 0 deletions mocks/include/mocks/EmptyApplication.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,13 @@ class EmptyApplication : public IApplication
return nullptr;
}

ITwitchUsers *getTwitchUsers() override
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
pajlada marked this conversation as resolved.
Show resolved Hide resolved
pajlada marked this conversation as resolved.
Show resolved Hide resolved
{
assert(false && "EmptyApplication::getTwitchUsers was called without "
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
pajlada marked this conversation as resolved.
Show resolved Hide resolved
pajlada marked this conversation as resolved.
Show resolved Hide resolved
"being initialized");
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
pajlada marked this conversation as resolved.
Show resolved Hide resolved
pajlada marked this conversation as resolved.
Show resolved Hide resolved
return nullptr;
}

QTemporaryDir settingsDir;
Paths paths_;
Args args_;
Expand Down
17 changes: 17 additions & 0 deletions mocks/include/mocks/Helix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,23 @@ class Helix : public IHelix
(FailureCallback<HelixSendMessageError, QString> failureCallback)),
(override));

// get user emotes
MOCK_METHOD(
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
pajlada marked this conversation as resolved.
Show resolved Hide resolved
pajlada marked this conversation as resolved.
Show resolved Hide resolved
void, getUserEmotes,
(QString userID, QString broadcasterID,
(ResultCallback<std::vector<HelixChannelEmote>, HelixPaginationState>
successCallback),
FailureCallback<QString> failureCallback, CancellationToken &&token),
(override));

// get followed channel
MOCK_METHOD(
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
pajlada marked this conversation as resolved.
Show resolved Hide resolved
pajlada marked this conversation as resolved.
Show resolved Hide resolved
void, getFollowedChannel,
(QString userID, QString broadcasterID,
ResultCallback<std::optional<HelixFollowedChannel>> successCallback,
FailureCallback<QString> failureCallback),
(override));

MOCK_METHOD(void, update, (QString clientId, QString oauthToken),
(override));

Expand Down
7 changes: 7 additions & 0 deletions src/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "providers/twitch/PubSubMessages.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/TwitchIrcServer.hpp"
#include "providers/twitch/TwitchUsers.hpp"
#include "singletons/CrashHandler.hpp"
#include "singletons/Emotes.hpp"
#include "singletons/Fonts.hpp"
Expand Down Expand Up @@ -179,6 +180,7 @@ Application::Application(Settings &_settings, const Paths &paths,
, logging(new Logging(_settings))
, linkResolver(new LinkResolver)
, streamerMode(new StreamerMode)
, twitchUsers(new TwitchUsers)
#ifdef CHATTERINO_HAVE_PLUGINS
, plugins(new PluginController(paths))
#endif
Expand Down Expand Up @@ -571,6 +573,11 @@ IStreamerMode *Application::getStreamerMode()
return this->streamerMode.get();
}

ITwitchUsers *Application::getTwitchUsers()
{
return this->twitchUsers.get();
}

BttvEmotes *Application::getBttvEmotes()
{
assertInGuiThread();
Expand Down
4 changes: 4 additions & 0 deletions src/Application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class SeventvEmotes;
class SeventvEventAPI;
class ILinkResolver;
class IStreamerMode;
class ITwitchUsers;

class IApplication
{
Expand Down Expand Up @@ -106,6 +107,7 @@ class IApplication
virtual SeventvEventAPI *getSeventvEventAPI() = 0;
virtual ILinkResolver *getLinkResolver() = 0;
virtual IStreamerMode *getStreamerMode() = 0;
virtual ITwitchUsers *getTwitchUsers() = 0;
};

class Application : public IApplication
Expand Down Expand Up @@ -169,6 +171,7 @@ class Application : public IApplication
const std::unique_ptr<Logging> logging;
std::unique_ptr<ILinkResolver> linkResolver;
std::unique_ptr<IStreamerMode> streamerMode;
std::unique_ptr<ITwitchUsers> twitchUsers;
#ifdef CHATTERINO_HAVE_PLUGINS
std::unique_ptr<PluginController> plugins;
#endif
Expand Down Expand Up @@ -220,6 +223,7 @@ class Application : public IApplication

ILinkResolver *getLinkResolver() override;
IStreamerMode *getStreamerMode() override;
ITwitchUsers *getTwitchUsers() override;

private:
void initPubSub();
Expand Down
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,8 @@ set(SOURCE_FILES
providers/twitch/TwitchIrcServer.hpp
providers/twitch/TwitchUser.cpp
providers/twitch/TwitchUser.hpp
providers/twitch/TwitchUsers.cpp
providers/twitch/TwitchUsers.hpp

providers/twitch/pubsubmessages/AutoMod.cpp
providers/twitch/pubsubmessages/AutoMod.hpp
Expand Down
57 changes: 34 additions & 23 deletions src/common/Aliases.hpp
Original file line number Diff line number Diff line change
@@ -1,38 +1,49 @@
#pragma once

#include <boost/container_hash/hash_fwd.hpp>
#include <QHash>
#include <QString>

#include <functional>

#define QStringAlias(name) \
namespace chatterino { \
struct name { \
QString string; \
bool operator==(const name &other) const \
{ \
return this->string == other.string; \
} \
bool operator!=(const name &other) const \
{ \
return this->string != other.string; \
} \
}; \
} /* namespace chatterino */ \
namespace std { \
template <> \
struct hash<chatterino::name> { \
size_t operator()(const chatterino::name &s) const \
{ \
return qHash(s.string); \
} \
}; \
} /* namespace std */
#define QStringAlias(name) \
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
pajlada marked this conversation as resolved.
Show resolved Hide resolved
namespace chatterino { \
struct name { \
QString string; \
bool operator==(const name &other) const \
{ \
return this->string == other.string; \
} \
bool operator!=(const name &other) const \
{ \
return this->string != other.string; \
} \
}; \
} /* namespace chatterino */ \
namespace std { \
template <> \
struct hash<chatterino::name> { \
size_t operator()(const chatterino::name &s) const \
{ \
return qHash(s.string); \
} \
}; \
} /* namespace std */ \
namespace boost { \
template <> \
struct hash<chatterino::name> { \
std::size_t operator()(chatterino::name const &s) const \
{ \
return qHash(s.string); \
} \
}; \
} /* namespace boost */

QStringAlias(UserName);
QStringAlias(UserId);
QStringAlias(Url);
QStringAlias(Tooltip);
QStringAlias(EmoteId);
QStringAlias(EmoteSetId);
QStringAlias(EmoteName);
QStringAlias(EmoteAuthor);
4 changes: 2 additions & 2 deletions src/controllers/commands/builtin/twitch/SendWhisper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ bool appendWhisperMessageWordsLocally(const QStringList &words)
for (int i = 2; i < words.length(); i++)
{
{ // Twitch emote
auto it = accemotes.emotes.find({words[i]});
if (it != accemotes.emotes.end())
auto it = accemotes->find({words[i]});
if (it != accemotes->end())
{
b.emplace<EmoteElement>(it->second,
MessageElementFlag::TwitchEmote);
Expand Down
24 changes: 8 additions & 16 deletions src/controllers/completion/sources/EmoteSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,26 +95,18 @@ void EmoteSource::initializeFromChannel(const Channel *channel)
// returns true also for special Twitch channels (/live, /mentions, /whispers, etc.)
if (channel->isTwitchChannel())
{
if (auto user = app->getAccounts()->twitch.getCurrent())
if (tc)
{
// Twitch Emotes available globally
auto emoteData = user->accessEmotes();
addEmotes(emotes, emoteData->emotes, "Twitch Emote");

// Twitch Emotes available locally
auto localEmoteData = user->accessLocalEmotes();
if ((tc != nullptr) &&
localEmoteData->find(tc->roomId()) != localEmoteData->end())
if (auto twitch = tc->localTwitchEmotes())
{
if (const auto *localEmotes = &localEmoteData->at(tc->roomId()))
{
addEmotes(emotes, *localEmotes, "Local Twitch Emotes");
}
addEmotes(emotes, *twitch, "Local Twitch Emotes");
}

if (auto user = getApp()->getAccounts()->twitch.getCurrent())
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
{
addEmotes(emotes, **user->accessEmotes(), "Twitch Emote");
}
}

if (tc)
{
// TODO extract "Channel {BetterTTV,7TV,FrankerFaceZ}" text into a #define.
if (auto bttv = tc->bttvEmotes())
{
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/ignores/IgnorePhrase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ bool IgnorePhrase::containsEmote() const
for (const auto &acc : accvec)
{
const auto &accemotes = *acc->accessEmotes();
for (const auto &emote : accemotes.emotes)
for (const auto &emote : *accemotes)
{
if (this->replace_.contains(emote.first.string,
Qt::CaseSensitive))
Expand Down
22 changes: 0 additions & 22 deletions src/providers/IvrApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,6 @@ void IvrApi::getSubage(QString userName, QString channelName,
.execute();
}

void IvrApi::getBulkEmoteSets(QString emoteSetList,
ResultCallback<QJsonArray> successCallback,
IvrFailureCallback failureCallback)
{
QUrlQuery urlQuery;
urlQuery.addQueryItem("set_id", emoteSetList);

this->makeRequest("twitch/emotes/sets", urlQuery)
.onSuccess([successCallback, failureCallback](auto result) {
auto root = result.parseJsonArray();

successCallback(root);
})
.onError([failureCallback](auto result) {
qCWarning(chatterinoIvr)
<< "Failed IVR API Call!" << result.formatError()
<< QString(result.getData());
failureCallback();
})
.execute();
}

NetworkRequest IvrApi::makeRequest(QString url, QUrlQuery urlQuery)
{
assert(!url.startsWith("/"));
Expand Down
45 changes: 0 additions & 45 deletions src/providers/IvrApi.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#pragma once

#include "common/network/NetworkRequest.hpp"
#include "providers/twitch/TwitchEmotes.hpp"

#include <QJsonArray>
#include <QJsonObject>
Expand Down Expand Up @@ -32,45 +31,6 @@ struct IvrSubage {
}
};

struct IvrEmoteSet {
const QString setId;
const QString displayName;
const QString login;
const QString channelId;
const QString tier;
const QJsonArray emotes;

IvrEmoteSet(const QJsonObject &root)
: setId(root.value("setID").toString())
, displayName(root.value("channelName").toString())
, login(root.value("channelLogin").toString())
, channelId(root.value("channelID").toString())
, tier(root.value("tier").toString())
, emotes(root.value("emoteList").toArray())

{
}
};

struct IvrEmote {
const QString code;
const QString id;
const QString setId;
const QString url;
const QString emoteType;
const QString imageType;

explicit IvrEmote(const QJsonObject &root)
: code(root.value("code").toString())
, id(root.value("id").toString())
, setId(root.value("setID").toString())
, url(TWITCH_EMOTE_TEMPLATE.arg(this->id, u"3.0"))
, emoteType(root.value("type").toString())
, imageType(root.value("assetType").toString())
{
}
};

class IvrApi final
{
public:
Expand All @@ -79,11 +39,6 @@ class IvrApi final
ResultCallback<IvrSubage> resultCallback,
IvrFailureCallback failureCallback);

// https://api.ivr.fi/v2/docs/static/index.html#/Twitch/get_twitch_emotes_sets
void getBulkEmoteSets(QString emoteSetList,
ResultCallback<QJsonArray> successCallback,
IvrFailureCallback failureCallback);

static void initialize();

IvrApi() = default;
Expand Down
29 changes: 0 additions & 29 deletions src/providers/twitch/IrcMessageHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -870,17 +870,6 @@ void IrcMessageHandler::handleClearMessageMessage(Communi::IrcMessage *message)

void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message)
{
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();

// set received emote-sets, used in TwitchAccount::loadUserstateEmotes
bool emoteSetsChanged = currentUser->setUserstateEmoteSets(
message->tag("emote-sets").toString().split(","));

if (emoteSetsChanged)
{
currentUser->loadUserstateEmotes();
}

QString channelName;
if (!trimChannelName(message->parameter(0), channelName))
{
Expand Down Expand Up @@ -918,24 +907,6 @@ void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message)
}
}

// This will emit only once and right after user logs in to IRC - reset emote data and reload emotes
void IrcMessageHandler::handleGlobalUserStateMessage(
Communi::IrcMessage *message)
{
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();

// set received emote-sets, this time used to initially load emotes
// NOTE: this should always return true unless we reconnect
auto emoteSetsChanged = currentUser->setUserstateEmoteSets(
message->tag("emote-sets").toString().split(","));

// We should always attempt to reload emotes even on reconnections where
// emoteSetsChanged, since we want to trigger emote reloads when
// "currentUserChanged" signal is emitted
qCDebug(chatterinoTwitch) << emoteSetsChanged << message->toData();
currentUser->loadEmotes();
}

void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *ircMessage)
{
MessageParseArgs args;
Expand Down
1 change: 0 additions & 1 deletion src/providers/twitch/IrcMessageHandler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ class IrcMessageHandler
void handleClearChatMessage(Communi::IrcMessage *message);
void handleClearMessageMessage(Communi::IrcMessage *message);
void handleUserStateMessage(Communi::IrcMessage *message);
void handleGlobalUserStateMessage(Communi::IrcMessage *message);
void handleWhisperMessage(Communi::IrcMessage *ircMessage);

void handleUserNoticeMessage(Communi::IrcMessage *message,
Expand Down
Loading
Loading