mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
Respect follower emotes context, making them only available in their owner channels (#2951)
Co-authored-by: pajlada <rasmus.karlsson@pajlada.com>
This commit is contained in:
parent
2844c8e7e0
commit
d5add46730
|
@ -9,6 +9,7 @@
|
||||||
- Bugfix: Fixed large timeout durations in moderation buttons overlapping with usernames or other buttons. (#2865, #2921)
|
- Bugfix: Fixed large timeout durations in moderation buttons overlapping with usernames or other buttons. (#2865, #2921)
|
||||||
- Bugfix: Middle mouse click no longer scrolls in not fully populated usercards and splits. (#2933)
|
- Bugfix: Middle mouse click no longer scrolls in not fully populated usercards and splits. (#2933)
|
||||||
- Bugfix: Fix bad behavior of the HTML color picker edit when user input is being entered. (#2942)
|
- Bugfix: Fix bad behavior of the HTML color picker edit when user input is being entered. (#2942)
|
||||||
|
- Bugfix: Made follower emotes suggested (in emote popup menu, tab completion, emote input menu) only in their origin channel, not globally. (#2951)
|
||||||
- Bugfix: Fixed founder badge not being respected by `author.subbed` filter. (#2971)
|
- Bugfix: Fixed founder badge not being respected by `author.subbed` filter. (#2971)
|
||||||
- Bugfix: Usercards on IRC will now only show user's messages. (#1780, #2979)
|
- Bugfix: Usercards on IRC will now only show user's messages. (#1780, #2979)
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "providers/twitch/TwitchIrcServer.hpp"
|
#include "providers/twitch/TwitchIrcServer.hpp"
|
||||||
#include "singletons/Emotes.hpp"
|
#include "singletons/Emotes.hpp"
|
||||||
#include "singletons/Settings.hpp"
|
#include "singletons/Settings.hpp"
|
||||||
|
#include "util/QStringHash.hpp"
|
||||||
|
|
||||||
#include <QtAlgorithms>
|
#include <QtAlgorithms>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
@ -96,14 +97,24 @@ void CompletionModel::refresh(const QString &prefix, bool isFirstWord)
|
||||||
|
|
||||||
if (auto channel = dynamic_cast<TwitchChannel *>(&this->channel_))
|
if (auto channel = dynamic_cast<TwitchChannel *>(&this->channel_))
|
||||||
{
|
{
|
||||||
// account emotes
|
|
||||||
if (auto account = getApp()->accounts->twitch.getCurrent())
|
if (auto account = getApp()->accounts->twitch.getCurrent())
|
||||||
{
|
{
|
||||||
for (const auto &emote : account->accessEmotes()->allEmoteNames)
|
// Twitch Emotes available globally
|
||||||
|
for (const auto &emote : account->accessEmotes()->emotes)
|
||||||
{
|
{
|
||||||
// XXX: No way to discern between a twitch global emote and sub
|
addString(emote.first.string, TaggedString::TwitchGlobalEmote);
|
||||||
// emote right now
|
}
|
||||||
addString(emote.string, TaggedString::Type::TwitchGlobalEmote);
|
|
||||||
|
// Twitch Emotes available locally
|
||||||
|
auto localEmoteData = account->accessLocalEmotes();
|
||||||
|
if (localEmoteData->find(channel->roomId()) !=
|
||||||
|
localEmoteData->end())
|
||||||
|
{
|
||||||
|
for (const auto &emote : localEmoteData->at(channel->roomId()))
|
||||||
|
{
|
||||||
|
addString(emote.first.string,
|
||||||
|
TaggedString::Type::TwitchLocalEmote);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ class CompletionModel : public QAbstractListModel
|
||||||
BTTVGlobalEmote,
|
BTTVGlobalEmote,
|
||||||
BTTVChannelEmote,
|
BTTVChannelEmote,
|
||||||
TwitchGlobalEmote,
|
TwitchGlobalEmote,
|
||||||
|
TwitchLocalEmote,
|
||||||
TwitchSubscriberEmote,
|
TwitchSubscriberEmote,
|
||||||
Emoji,
|
Emoji,
|
||||||
EmoteEnd,
|
EmoteEnd,
|
||||||
|
|
|
@ -40,7 +40,7 @@ void IvrApi::getBulkEmoteSets(QString emoteSetList,
|
||||||
QUrlQuery urlQuery;
|
QUrlQuery urlQuery;
|
||||||
urlQuery.addQueryItem("set_id", emoteSetList);
|
urlQuery.addQueryItem("set_id", emoteSetList);
|
||||||
|
|
||||||
this->makeRequest("twitch/emoteset", urlQuery)
|
this->makeRequest("v2/twitch/emotes/sets", urlQuery)
|
||||||
.onSuccess([successCallback, failureCallback](auto result) -> Outcome {
|
.onSuccess([successCallback, failureCallback](auto result) -> Outcome {
|
||||||
auto root = result.parseJsonArray();
|
auto root = result.parseJsonArray();
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "common/NetworkRequest.hpp"
|
#include "common/NetworkRequest.hpp"
|
||||||
#include "messages/Link.hpp"
|
#include "messages/Link.hpp"
|
||||||
|
#include "providers/twitch/TwitchEmotes.hpp"
|
||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ struct IvrEmoteSet {
|
||||||
const QString setId;
|
const QString setId;
|
||||||
const QString displayName;
|
const QString displayName;
|
||||||
const QString login;
|
const QString login;
|
||||||
const QString id;
|
const QString channelId;
|
||||||
const QString tier;
|
const QString tier;
|
||||||
const QJsonArray emotes;
|
const QJsonArray emotes;
|
||||||
|
|
||||||
|
@ -43,9 +44,9 @@ struct IvrEmoteSet {
|
||||||
: setId(root.value("setID").toString())
|
: setId(root.value("setID").toString())
|
||||||
, displayName(root.value("channelName").toString())
|
, displayName(root.value("channelName").toString())
|
||||||
, login(root.value("channelLogin").toString())
|
, login(root.value("channelLogin").toString())
|
||||||
, id(root.value("channelID").toString())
|
, channelId(root.value("channelID").toString())
|
||||||
, tier(root.value("tier").toString())
|
, tier(root.value("tier").toString())
|
||||||
, emotes(root.value("emotes").toArray())
|
, emotes(root.value("emoteList").toArray())
|
||||||
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -56,12 +57,18 @@ struct IvrEmote {
|
||||||
const QString id;
|
const QString id;
|
||||||
const QString setId;
|
const QString setId;
|
||||||
const QString url;
|
const QString url;
|
||||||
|
const QString emoteType;
|
||||||
|
const QString imageType;
|
||||||
|
|
||||||
IvrEmote(QJsonObject root)
|
explicit IvrEmote(QJsonObject root)
|
||||||
: code(root.value("token").toString())
|
: code(root.value("code").toString())
|
||||||
, id(root.value("id").toString())
|
, id(root.value("id").toString())
|
||||||
, setId(root.value("setID").toString())
|
, setId(root.value("setID").toString())
|
||||||
, url(root.value("url_3x").toString())
|
, url(QString(TWITCH_EMOTE_TEMPLATE)
|
||||||
|
.replace("{id}", this->id)
|
||||||
|
.replace("{scale}", "3.0"))
|
||||||
|
, emoteType(root.value("type").toString())
|
||||||
|
, imageType(root.value("assetType").toString())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "providers/twitch/api/Helix.hpp"
|
#include "providers/twitch/api/Helix.hpp"
|
||||||
#include "providers/twitch/api/Kraken.hpp"
|
#include "providers/twitch/api/Kraken.hpp"
|
||||||
#include "singletons/Emotes.hpp"
|
#include "singletons/Emotes.hpp"
|
||||||
|
#include "util/QStringHash.hpp"
|
||||||
#include "util/RapidjsonHelpers.hpp"
|
#include "util/RapidjsonHelpers.hpp"
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
@ -216,7 +217,7 @@ void TwitchAccount::loadEmotes()
|
||||||
// Clearing emote data
|
// Clearing emote data
|
||||||
auto emoteData = this->emotes_.access();
|
auto emoteData = this->emotes_.access();
|
||||||
emoteData->emoteSets.clear();
|
emoteData->emoteSets.clear();
|
||||||
emoteData->allEmoteNames.clear();
|
emoteData->emotes.clear();
|
||||||
|
|
||||||
for (auto emoteSetIt = data.emoteSets.begin();
|
for (auto emoteSetIt = data.emoteSets.begin();
|
||||||
emoteSetIt != data.emoteSets.end(); ++emoteSetIt)
|
emoteSetIt != data.emoteSets.end(); ++emoteSetIt)
|
||||||
|
@ -245,12 +246,15 @@ void TwitchAccount::loadEmotes()
|
||||||
EmoteName{TwitchEmotes::cleanUpEmoteCode(code)};
|
EmoteName{TwitchEmotes::cleanUpEmoteCode(code)};
|
||||||
emoteSet->emotes.emplace_back(
|
emoteSet->emotes.emplace_back(
|
||||||
TwitchEmote{id, cleanCode});
|
TwitchEmote{id, cleanCode});
|
||||||
emoteData->allEmoteNames.push_back(cleanCode);
|
|
||||||
|
|
||||||
|
if (!emoteSet->local)
|
||||||
|
{
|
||||||
auto emote =
|
auto emote =
|
||||||
getApp()->emotes->twitch.getOrCreateEmote(id, code);
|
getApp()->emotes->twitch.getOrCreateEmote(id,
|
||||||
|
code);
|
||||||
emoteData->emotes.emplace(code, emote);
|
emoteData->emotes.emplace(code, emote);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::sort(emoteSet->emotes.begin(), emoteSet->emotes.end(),
|
std::sort(emoteSet->emotes.begin(), emoteSet->emotes.end(),
|
||||||
[](const TwitchEmote &l, const TwitchEmote &r) {
|
[](const TwitchEmote &l, const TwitchEmote &r) {
|
||||||
|
@ -345,6 +349,7 @@ void TwitchAccount::loadUserstateEmotes()
|
||||||
batch.join(","),
|
batch.join(","),
|
||||||
[this](QJsonArray emoteSetArray) {
|
[this](QJsonArray emoteSetArray) {
|
||||||
auto emoteData = this->emotes_.access();
|
auto emoteData = this->emotes_.access();
|
||||||
|
auto localEmoteData = this->localEmotes_.access();
|
||||||
for (auto emoteSet : emoteSetArray)
|
for (auto emoteSet : emoteSetArray)
|
||||||
{
|
{
|
||||||
auto newUserEmoteSet = std::make_shared<EmoteSet>();
|
auto newUserEmoteSet = std::make_shared<EmoteSet>();
|
||||||
|
@ -360,9 +365,9 @@ void TwitchAccount::loadUserstateEmotes()
|
||||||
newUserEmoteSet->text = name;
|
newUserEmoteSet->text = name;
|
||||||
newUserEmoteSet->channelName = ivrEmoteSet.login;
|
newUserEmoteSet->channelName = ivrEmoteSet.login;
|
||||||
|
|
||||||
for (const auto &emote : ivrEmoteSet.emotes)
|
for (const auto &emoteObj : ivrEmoteSet.emotes)
|
||||||
{
|
{
|
||||||
IvrEmote ivrEmote(emote.toObject());
|
IvrEmote ivrEmote(emoteObj.toObject());
|
||||||
|
|
||||||
auto id = EmoteId{ivrEmote.id};
|
auto id = EmoteId{ivrEmote.id};
|
||||||
auto code = EmoteName{ivrEmote.code};
|
auto code = EmoteName{ivrEmote.code};
|
||||||
|
@ -371,11 +376,29 @@ void TwitchAccount::loadUserstateEmotes()
|
||||||
newUserEmoteSet->emotes.push_back(
|
newUserEmoteSet->emotes.push_back(
|
||||||
TwitchEmote{id, cleanCode});
|
TwitchEmote{id, cleanCode});
|
||||||
|
|
||||||
emoteData->allEmoteNames.push_back(cleanCode);
|
auto emote =
|
||||||
|
|
||||||
auto twitchEmote =
|
|
||||||
getApp()->emotes->twitch.getOrCreateEmote(id, code);
|
getApp()->emotes->twitch.getOrCreateEmote(id, code);
|
||||||
emoteData->emotes.emplace(code, twitchEmote);
|
|
||||||
|
// Follower emotes can be only used in their origin channel
|
||||||
|
if (ivrEmote.emoteType == "FOLLOWER")
|
||||||
|
{
|
||||||
|
newUserEmoteSet->local = true;
|
||||||
|
|
||||||
|
// EmoteMap for target channel wasn't initialized yet, doing it now
|
||||||
|
if (localEmoteData->find(ivrEmoteSet.channelId) ==
|
||||||
|
localEmoteData->end())
|
||||||
|
{
|
||||||
|
localEmoteData->emplace(ivrEmoteSet.channelId,
|
||||||
|
EmoteMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
localEmoteData->at(ivrEmoteSet.channelId)
|
||||||
|
.emplace(code, emote);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emoteData->emotes.emplace(code, emote);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
std::sort(newUserEmoteSet->emotes.begin(),
|
std::sort(newUserEmoteSet->emotes.begin(),
|
||||||
newUserEmoteSet->emotes.end(),
|
newUserEmoteSet->emotes.end(),
|
||||||
|
@ -397,6 +420,12 @@ SharedAccessGuard<const TwitchAccount::TwitchAccountEmoteData>
|
||||||
return this->emotes_.accessConst();
|
return this->emotes_.accessConst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SharedAccessGuard<const std::unordered_map<QString, EmoteMap>>
|
||||||
|
TwitchAccount::accessLocalEmotes() const
|
||||||
|
{
|
||||||
|
return this->localEmotes_.accessConst();
|
||||||
|
}
|
||||||
|
|
||||||
// AutoModActions
|
// AutoModActions
|
||||||
void TwitchAccount::autoModAllow(const QString msgID, ChannelPtr channel)
|
void TwitchAccount::autoModAllow(const QString msgID, ChannelPtr channel)
|
||||||
{
|
{
|
||||||
|
@ -510,6 +539,12 @@ void TwitchAccount::loadEmoteSetData(std::shared_ptr<EmoteSet> emoteSet)
|
||||||
getHelix()->getEmoteSetData(
|
getHelix()->getEmoteSetData(
|
||||||
emoteSet->key,
|
emoteSet->key,
|
||||||
[emoteSet](HelixEmoteSetData emoteSetData) {
|
[emoteSet](HelixEmoteSetData emoteSetData) {
|
||||||
|
// Follower emotes can be only used in their origin channel
|
||||||
|
if (emoteSetData.emoteType == "follower")
|
||||||
|
{
|
||||||
|
emoteSet->local = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (emoteSetData.ownerId.isEmpty() ||
|
if (emoteSetData.ownerId.isEmpty() ||
|
||||||
emoteSetData.setId != emoteSet->key)
|
emoteSetData.setId != emoteSet->key)
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "controllers/accounts/Account.hpp"
|
#include "controllers/accounts/Account.hpp"
|
||||||
#include "messages/Emote.hpp"
|
#include "messages/Emote.hpp"
|
||||||
#include "providers/twitch/TwitchUser.hpp"
|
#include "providers/twitch/TwitchUser.hpp"
|
||||||
|
#include "util/QStringHash.hpp"
|
||||||
|
|
||||||
#include <rapidjson/document.h>
|
#include <rapidjson/document.h>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
@ -62,6 +63,7 @@ public:
|
||||||
QString key;
|
QString key;
|
||||||
QString channelName;
|
QString channelName;
|
||||||
QString text;
|
QString text;
|
||||||
|
bool local{false};
|
||||||
std::vector<TwitchEmote> emotes;
|
std::vector<TwitchEmote> emotes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -70,8 +72,8 @@ public:
|
||||||
struct TwitchAccountEmoteData {
|
struct TwitchAccountEmoteData {
|
||||||
std::vector<std::shared_ptr<EmoteSet>> emoteSets;
|
std::vector<std::shared_ptr<EmoteSet>> emoteSets;
|
||||||
|
|
||||||
std::vector<EmoteName> allEmoteNames;
|
// this EmoteMap should contain all emotes available globally
|
||||||
|
// excluding locally available emotes, such as follower ones
|
||||||
EmoteMap emotes;
|
EmoteMap emotes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -118,6 +120,8 @@ public:
|
||||||
// Returns true if the newly inserted emote sets differ from the ones previously saved
|
// Returns true if the newly inserted emote sets differ from the ones previously saved
|
||||||
[[nodiscard]] bool setUserstateEmoteSets(QStringList newEmoteSets);
|
[[nodiscard]] bool setUserstateEmoteSets(QStringList newEmoteSets);
|
||||||
SharedAccessGuard<const TwitchAccountEmoteData> accessEmotes() const;
|
SharedAccessGuard<const TwitchAccountEmoteData> accessEmotes() const;
|
||||||
|
SharedAccessGuard<const std::unordered_map<QString, EmoteMap>>
|
||||||
|
accessLocalEmotes() const;
|
||||||
|
|
||||||
// Automod actions
|
// Automod actions
|
||||||
void autoModAllow(const QString msgID, ChannelPtr channel);
|
void autoModAllow(const QString msgID, ChannelPtr channel);
|
||||||
|
@ -140,6 +144,7 @@ private:
|
||||||
|
|
||||||
// std::map<UserId, TwitchAccountEmoteData> emotes;
|
// std::map<UserId, TwitchAccountEmoteData> emotes;
|
||||||
UniqueAccess<TwitchAccountEmoteData> emotes_;
|
UniqueAccess<TwitchAccountEmoteData> emotes_;
|
||||||
|
UniqueAccess<std::unordered_map<QString, EmoteMap>> localEmotes_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "common/Common.hpp"
|
#include "common/Common.hpp"
|
||||||
#include "common/Env.hpp"
|
#include "common/Env.hpp"
|
||||||
#include "common/NetworkRequest.hpp"
|
#include "common/NetworkRequest.hpp"
|
||||||
|
#include "common/QLogging.hpp"
|
||||||
#include "controllers/accounts/AccountController.hpp"
|
#include "controllers/accounts/AccountController.hpp"
|
||||||
#include "controllers/notifications/NotificationController.hpp"
|
#include "controllers/notifications/NotificationController.hpp"
|
||||||
#include "messages/Message.hpp"
|
#include "messages/Message.hpp"
|
||||||
|
@ -21,6 +22,7 @@
|
||||||
#include "singletons/WindowManager.hpp"
|
#include "singletons/WindowManager.hpp"
|
||||||
#include "util/FormatTime.hpp"
|
#include "util/FormatTime.hpp"
|
||||||
#include "util/PostToThread.hpp"
|
#include "util/PostToThread.hpp"
|
||||||
|
#include "util/QStringHash.hpp"
|
||||||
#include "widgets/Window.hpp"
|
#include "widgets/Window.hpp"
|
||||||
|
|
||||||
#include <rapidjson/document.h>
|
#include <rapidjson/document.h>
|
||||||
|
@ -30,7 +32,6 @@
|
||||||
#include <QJsonValue>
|
#include <QJsonValue>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include "common/QLogging.hpp"
|
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "providers/twitch/ChannelPointReward.hpp"
|
#include "providers/twitch/ChannelPointReward.hpp"
|
||||||
#include "providers/twitch/TwitchEmotes.hpp"
|
#include "providers/twitch/TwitchEmotes.hpp"
|
||||||
#include "providers/twitch/api/Helix.hpp"
|
#include "providers/twitch/api/Helix.hpp"
|
||||||
|
#include "util/QStringHash.hpp"
|
||||||
|
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
|
|
|
@ -793,6 +793,43 @@ void Helix::getEmoteSetData(QString emoteSetId,
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Helix::getChannelEmotes(
|
||||||
|
QString broadcasterId,
|
||||||
|
ResultCallback<std::vector<HelixChannelEmote>> successCallback,
|
||||||
|
HelixFailureCallback failureCallback)
|
||||||
|
{
|
||||||
|
QUrlQuery urlQuery;
|
||||||
|
urlQuery.addQueryItem("broadcaster_id", broadcasterId);
|
||||||
|
|
||||||
|
this->makeRequest("chat/emotes", urlQuery)
|
||||||
|
.onSuccess([successCallback,
|
||||||
|
failureCallback](NetworkResult result) -> Outcome {
|
||||||
|
QJsonObject root = result.parseJson();
|
||||||
|
auto data = root.value("data");
|
||||||
|
|
||||||
|
if (!data.isArray())
|
||||||
|
{
|
||||||
|
failureCallback();
|
||||||
|
return Failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<HelixChannelEmote> channelEmotes;
|
||||||
|
|
||||||
|
for (const auto &jsonStream : data.toArray())
|
||||||
|
{
|
||||||
|
channelEmotes.emplace_back(jsonStream.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
successCallback(channelEmotes);
|
||||||
|
return Success;
|
||||||
|
})
|
||||||
|
.onError([failureCallback](auto result) {
|
||||||
|
// TODO: make better xd
|
||||||
|
failureCallback();
|
||||||
|
})
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
NetworkRequest Helix::makeRequest(QString url, QUrlQuery urlQuery)
|
NetworkRequest Helix::makeRequest(QString url, QUrlQuery urlQuery)
|
||||||
{
|
{
|
||||||
assert(!url.startsWith("/"));
|
assert(!url.startsWith("/"));
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "common/Aliases.hpp"
|
#include "common/Aliases.hpp"
|
||||||
#include "common/NetworkRequest.hpp"
|
#include "common/NetworkRequest.hpp"
|
||||||
|
#include "providers/twitch/TwitchEmotes.hpp"
|
||||||
|
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
@ -267,10 +268,31 @@ struct HelixCheermoteSet {
|
||||||
struct HelixEmoteSetData {
|
struct HelixEmoteSetData {
|
||||||
QString setId;
|
QString setId;
|
||||||
QString ownerId;
|
QString ownerId;
|
||||||
|
QString emoteType;
|
||||||
|
|
||||||
explicit HelixEmoteSetData(QJsonObject jsonObject)
|
explicit HelixEmoteSetData(QJsonObject jsonObject)
|
||||||
: setId(jsonObject.value("emote_set_id").toString())
|
: setId(jsonObject.value("emote_set_id").toString())
|
||||||
, ownerId(jsonObject.value("owner_id").toString())
|
, ownerId(jsonObject.value("owner_id").toString())
|
||||||
|
, emoteType(jsonObject.value("emote_type").toString())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HelixChannelEmote {
|
||||||
|
const QString emoteId;
|
||||||
|
const QString name;
|
||||||
|
const QString type;
|
||||||
|
const QString setId;
|
||||||
|
const QString url;
|
||||||
|
|
||||||
|
explicit HelixChannelEmote(QJsonObject jsonObject)
|
||||||
|
: emoteId(jsonObject.value("id").toString())
|
||||||
|
, name(jsonObject.value("name").toString())
|
||||||
|
, type(jsonObject.value("emote_type").toString())
|
||||||
|
, setId(jsonObject.value("emote_set_id").toString())
|
||||||
|
, url(QString(TWITCH_EMOTE_TEMPLATE)
|
||||||
|
.replace("{id}", this->emoteId)
|
||||||
|
.replace("{scale}", "3.0"))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -414,6 +436,12 @@ public:
|
||||||
ResultCallback<HelixEmoteSetData> successCallback,
|
ResultCallback<HelixEmoteSetData> successCallback,
|
||||||
HelixFailureCallback failureCallback);
|
HelixFailureCallback failureCallback);
|
||||||
|
|
||||||
|
// https://dev.twitch.tv/docs/api/reference#get-channel-emotes
|
||||||
|
void getChannelEmotes(
|
||||||
|
QString broadcasterId,
|
||||||
|
ResultCallback<std::vector<HelixChannelEmote>> successCallback,
|
||||||
|
HelixFailureCallback failureCallback);
|
||||||
|
|
||||||
void update(QString clientId, QString oauthToken);
|
void update(QString clientId, QString oauthToken);
|
||||||
|
|
||||||
static void initialize();
|
static void initialize();
|
||||||
|
|
|
@ -165,6 +165,13 @@ URL: https://dev.twitch.tv/docs/api/reference#get-emote-sets
|
||||||
Used in:
|
Used in:
|
||||||
- `providers/twitch/TwitchAccount.cpp` to set emoteset owner data upon loading subscriber emotes from Kraken
|
- `providers/twitch/TwitchAccount.cpp` to set emoteset owner data upon loading subscriber emotes from Kraken
|
||||||
|
|
||||||
|
### Get Channel Emotes
|
||||||
|
|
||||||
|
URL: https://dev.twitch.tv/docs/api/reference#get-channel-emotes
|
||||||
|
|
||||||
|
- We implement this in `providers/twitch/api/Helix.cpp getChannelEmotes`
|
||||||
|
Not used anywhere at the moment.
|
||||||
|
|
||||||
## TMI
|
## TMI
|
||||||
|
|
||||||
The TMI api is undocumented.
|
The TMI api is undocumented.
|
||||||
|
|
|
@ -69,6 +69,12 @@ namespace {
|
||||||
|
|
||||||
for (const auto &set : sets)
|
for (const auto &set : sets)
|
||||||
{
|
{
|
||||||
|
// Some emotes (e.g. follower ones) are only available in their origin channel
|
||||||
|
if (set->local && currentChannelName != set->channelName)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// TITLE
|
// TITLE
|
||||||
auto channelName = set->channelName;
|
auto channelName = set->channelName;
|
||||||
auto text = set->text.isEmpty() ? "Twitch" : set->text;
|
auto text = set->text.isEmpty() ? "Twitch" : set->text;
|
||||||
|
|
|
@ -80,8 +80,20 @@ void InputCompletionPopup::updateEmotes(const QString &text, ChannelPtr channel)
|
||||||
{
|
{
|
||||||
if (auto user = getApp()->accounts->twitch.getCurrent())
|
if (auto user = getApp()->accounts->twitch.getCurrent())
|
||||||
{
|
{
|
||||||
auto twitch = user->accessEmotes();
|
// Twitch Emotes available globally
|
||||||
addEmotes(emotes, twitch->emotes, text, "Twitch Emote");
|
auto emoteData = user->accessEmotes();
|
||||||
|
addEmotes(emotes, emoteData->emotes, text, "Twitch Emote");
|
||||||
|
|
||||||
|
// Twitch Emotes available locally
|
||||||
|
auto localEmoteData = user->accessLocalEmotes();
|
||||||
|
if (localEmoteData->find(tc->roomId()) != localEmoteData->end())
|
||||||
|
{
|
||||||
|
if (auto localEmotes = &localEmoteData->at(tc->roomId()))
|
||||||
|
{
|
||||||
|
addEmotes(emotes, *localEmotes, text,
|
||||||
|
"Local Twitch Emotes");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tc)
|
if (tc)
|
||||||
|
|
Loading…
Reference in a new issue