Merge branch 'master' into apa-bits
This commit is contained in:
@@ -21,13 +21,25 @@ namespace {
|
||||
return {urlTemplate.replace("{{id}}", id.string)
|
||||
.replace("{{image}}", emoteScale)};
|
||||
}
|
||||
|
||||
Url getEmoteLinkV3(const EmoteId &id, const QString &emoteScale)
|
||||
{
|
||||
static const QString urlTemplate(
|
||||
"https://cdn.betterttv.net/emote/%1/%2");
|
||||
|
||||
return {urlTemplate.arg(id.string, emoteScale)};
|
||||
}
|
||||
EmotePtr cachedOrMake(Emote &&emote, const EmoteId &id)
|
||||
{
|
||||
static std::unordered_map<EmoteId, std::weak_ptr<const Emote>> cache;
|
||||
static std::mutex mutex;
|
||||
|
||||
return cachedOrMakeEmotePtr(std::move(emote), cache, mutex, id);
|
||||
}
|
||||
std::pair<Outcome, EmoteMap> parseGlobalEmotes(
|
||||
const QJsonObject &jsonRoot, const EmoteMap ¤tEmotes)
|
||||
const QJsonArray &jsonEmotes, const EmoteMap ¤tEmotes)
|
||||
{
|
||||
auto emotes = EmoteMap();
|
||||
auto jsonEmotes = jsonRoot.value("emotes").toArray();
|
||||
auto urlTemplate =
|
||||
qS("https:") + jsonRoot.value("urlTemplate").toString();
|
||||
|
||||
for (auto jsonEmote : jsonEmotes)
|
||||
{
|
||||
@@ -37,10 +49,9 @@ namespace {
|
||||
|
||||
auto emote = Emote(
|
||||
{name,
|
||||
ImageSet{
|
||||
Image::fromUrl(getEmoteLink(urlTemplate, id, "1x"), 1),
|
||||
Image::fromUrl(getEmoteLink(urlTemplate, id, "2x"), 0.5),
|
||||
Image::fromUrl(getEmoteLink(urlTemplate, id, "3x"), 0.25)},
|
||||
ImageSet{Image::fromUrl(getEmoteLinkV3(id, "1x"), 1),
|
||||
Image::fromUrl(getEmoteLinkV3(id, "2x"), 0.5),
|
||||
Image::fromUrl(getEmoteLinkV3(id, "3x"), 0.25)},
|
||||
Tooltip{name.string + "<br />Global BetterTTV Emote"},
|
||||
Url{"https://manage.betterttv.net/emotes/" + id.string}});
|
||||
|
||||
@@ -50,38 +61,36 @@ namespace {
|
||||
|
||||
return {Success, std::move(emotes)};
|
||||
}
|
||||
EmotePtr cachedOrMake(Emote &&emote, const EmoteId &id)
|
||||
{
|
||||
static std::unordered_map<EmoteId, std::weak_ptr<const Emote>> cache;
|
||||
static std::mutex mutex;
|
||||
|
||||
return cachedOrMakeEmotePtr(std::move(emote), cache, mutex, id);
|
||||
}
|
||||
std::pair<Outcome, EmoteMap> parseChannelEmotes(const QJsonObject &jsonRoot)
|
||||
{
|
||||
auto emotes = EmoteMap();
|
||||
auto jsonEmotes = jsonRoot.value("emotes").toArray();
|
||||
auto urlTemplate = "https:" + jsonRoot.value("urlTemplate").toString();
|
||||
|
||||
for (auto jsonEmote_ : jsonEmotes)
|
||||
{
|
||||
auto jsonEmote = jsonEmote_.toObject();
|
||||
auto innerParse = [&jsonRoot, &emotes](const char *key) {
|
||||
auto jsonEmotes = jsonRoot.value(key).toArray();
|
||||
for (auto jsonEmote_ : jsonEmotes)
|
||||
{
|
||||
auto jsonEmote = jsonEmote_.toObject();
|
||||
|
||||
auto id = EmoteId{jsonEmote.value("id").toString()};
|
||||
auto name = EmoteName{jsonEmote.value("code").toString()};
|
||||
// emoteObject.value("imageType").toString();
|
||||
auto id = EmoteId{jsonEmote.value("id").toString()};
|
||||
auto name = EmoteName{jsonEmote.value("code").toString()};
|
||||
// emoteObject.value("imageType").toString();
|
||||
|
||||
auto emote = Emote(
|
||||
{name,
|
||||
ImageSet{
|
||||
Image::fromUrl(getEmoteLink(urlTemplate, id, "1x"), 1),
|
||||
Image::fromUrl(getEmoteLink(urlTemplate, id, "2x"), 0.5),
|
||||
Image::fromUrl(getEmoteLink(urlTemplate, id, "3x"), 0.25)},
|
||||
Tooltip{name.string + "<br />Channel BetterTTV Emote"},
|
||||
Url{"https://manage.betterttv.net/emotes/" + id.string}});
|
||||
auto emote = Emote(
|
||||
{name,
|
||||
ImageSet{
|
||||
Image::fromUrl(getEmoteLinkV3(id, "1x"), 1),
|
||||
Image::fromUrl(getEmoteLinkV3(id, "2x"), 0.5),
|
||||
Image::fromUrl(getEmoteLinkV3(id, "3x"), 0.25),
|
||||
},
|
||||
Tooltip{name.string + "<br />Channel BetterTTV Emote"},
|
||||
Url{"https://manage.betterttv.net/emotes/" + id.string}});
|
||||
|
||||
emotes[name] = cachedOrMake(std::move(emote), id);
|
||||
}
|
||||
emotes[name] = cachedOrMake(std::move(emote), id);
|
||||
}
|
||||
};
|
||||
|
||||
innerParse("channelEmotes");
|
||||
innerParse("sharedEmotes");
|
||||
|
||||
return {Success, std::move(emotes)};
|
||||
}
|
||||
@@ -116,7 +125,7 @@ void BttvEmotes::loadEmotes()
|
||||
.timeout(30000)
|
||||
.onSuccess([this](auto result) -> Outcome {
|
||||
auto emotes = this->global_.get();
|
||||
auto pair = parseGlobalEmotes(result.parseJson(), *emotes);
|
||||
auto pair = parseGlobalEmotes(result.parseJsonArray(), *emotes);
|
||||
if (pair.first)
|
||||
this->global_.set(
|
||||
std::make_shared<EmoteMap>(std::move(pair.second)));
|
||||
@@ -125,10 +134,10 @@ void BttvEmotes::loadEmotes()
|
||||
.execute();
|
||||
}
|
||||
|
||||
void BttvEmotes::loadChannel(const QString &channelName,
|
||||
void BttvEmotes::loadChannel(const QString &channelId,
|
||||
std::function<void(EmoteMap &&)> callback)
|
||||
{
|
||||
NetworkRequest(QString(bttvChannelEmoteApiUrl) + channelName)
|
||||
NetworkRequest(QString(bttvChannelEmoteApiUrl) + channelId)
|
||||
.timeout(3000)
|
||||
.onSuccess([callback = std::move(callback)](auto result) -> Outcome {
|
||||
auto pair = parseChannelEmotes(result.parseJson());
|
||||
|
||||
@@ -14,9 +14,9 @@ class EmoteMap;
|
||||
class BttvEmotes final
|
||||
{
|
||||
static constexpr const char *globalEmoteApiUrl =
|
||||
"https://api.betterttv.net/2/emotes";
|
||||
"https://api.betterttv.net/3/cached/emotes/global";
|
||||
static constexpr const char *bttvChannelEmoteApiUrl =
|
||||
"https://api.betterttv.net/2/channels/";
|
||||
"https://api.betterttv.net/3/cached/users/twitch/";
|
||||
|
||||
public:
|
||||
BttvEmotes();
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
std::shared_ptr<const EmoteMap> emotes() const;
|
||||
boost::optional<EmotePtr> emote(const EmoteName &name) const;
|
||||
void loadEmotes();
|
||||
static void loadChannel(const QString &channelName,
|
||||
static void loadChannel(const QString &channelId,
|
||||
std::function<void(EmoteMap &&)> callback);
|
||||
|
||||
private:
|
||||
|
||||
@@ -79,7 +79,38 @@ namespace {
|
||||
|
||||
return {Success, std::move(emotes)};
|
||||
}
|
||||
std::pair<Outcome, EmoteMap> parseChannelEmotes(const QJsonObject &jsonRoot)
|
||||
|
||||
boost::optional<EmotePtr> parseModBadge(const QJsonObject &jsonRoot)
|
||||
{
|
||||
boost::optional<EmotePtr> modBadge;
|
||||
|
||||
auto room = jsonRoot.value("room").toObject();
|
||||
auto modUrls = room.value("mod_urls").toObject();
|
||||
if (!modUrls.isEmpty())
|
||||
{
|
||||
auto modBadge1x = getEmoteLink(modUrls, "1");
|
||||
auto modBadge2x = getEmoteLink(modUrls, "2");
|
||||
auto modBadge3x = getEmoteLink(modUrls, "4");
|
||||
|
||||
auto modBadgeImageSet = ImageSet{
|
||||
Image::fromUrl(modBadge1x, 1),
|
||||
modBadge2x.string.isEmpty() ? Image::getEmpty()
|
||||
: Image::fromUrl(modBadge2x, 0.5),
|
||||
modBadge3x.string.isEmpty() ? Image::getEmpty()
|
||||
: Image::fromUrl(modBadge3x, 0.25),
|
||||
};
|
||||
|
||||
modBadge = std::make_shared<Emote>(Emote{
|
||||
{""},
|
||||
modBadgeImageSet,
|
||||
Tooltip{"Twitch Channel Moderator"},
|
||||
modBadge1x,
|
||||
});
|
||||
}
|
||||
return modBadge;
|
||||
}
|
||||
|
||||
EmoteMap parseChannelEmotes(const QJsonObject &jsonRoot)
|
||||
{
|
||||
auto jsonSets = jsonRoot.value("sets").toObject();
|
||||
auto emotes = EmoteMap();
|
||||
@@ -110,7 +141,7 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
return {Success, std::move(emotes)};
|
||||
return emotes;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -138,7 +169,7 @@ void FfzEmotes::loadEmotes()
|
||||
QString url("https://api.frankerfacez.com/v1/set/global");
|
||||
|
||||
NetworkRequest(url)
|
||||
|
||||
|
||||
.timeout(30000)
|
||||
.onSuccess([this](auto result) -> Outcome {
|
||||
auto emotes = this->emotes();
|
||||
@@ -151,21 +182,28 @@ void FfzEmotes::loadEmotes()
|
||||
.execute();
|
||||
}
|
||||
|
||||
void FfzEmotes::loadChannel(const QString &channelName,
|
||||
std::function<void(EmoteMap &&)> callback)
|
||||
void FfzEmotes::loadChannel(
|
||||
const QString &channelId, std::function<void(EmoteMap &&)> emoteCallback,
|
||||
std::function<void(boost::optional<EmotePtr>)> modBadgeCallback)
|
||||
{
|
||||
log("[FFZEmotes] Reload FFZ Channel Emotes for channel {}\n", channelName);
|
||||
log("[FFZEmotes] Reload FFZ Channel Emotes for channel {}\n", channelId);
|
||||
|
||||
NetworkRequest("https://api.frankerfacez.com/v1/room/id/" + channelId)
|
||||
|
||||
NetworkRequest("https://api.frankerfacez.com/v1/room/" + channelName)
|
||||
|
||||
.timeout(20000)
|
||||
.onSuccess([callback = std::move(callback)](auto result) -> Outcome {
|
||||
auto pair = parseChannelEmotes(result.parseJson());
|
||||
if (pair.first)
|
||||
callback(std::move(pair.second));
|
||||
return pair.first;
|
||||
.onSuccess([emoteCallback = std::move(emoteCallback),
|
||||
modBadgeCallback =
|
||||
std::move(modBadgeCallback)](auto result) -> Outcome {
|
||||
auto json = result.parseJson();
|
||||
auto emoteMap = parseChannelEmotes(json);
|
||||
auto modBadge = parseModBadge(json);
|
||||
|
||||
emoteCallback(std::move(emoteMap));
|
||||
modBadgeCallback(std::move(modBadge));
|
||||
|
||||
return Success;
|
||||
})
|
||||
.onError([channelName](int result) {
|
||||
.onError([channelId](int result) {
|
||||
if (result == 203)
|
||||
{
|
||||
// User does not have any FFZ emotes
|
||||
@@ -176,12 +214,12 @@ void FfzEmotes::loadChannel(const QString &channelName,
|
||||
{
|
||||
// TODO: Auto retry in case of a timeout, with a delay
|
||||
log("Fetching FFZ emotes for channel {} failed due to timeout",
|
||||
channelName);
|
||||
channelId);
|
||||
return true;
|
||||
}
|
||||
|
||||
log("Error fetching FFZ emotes for channel {}, error {}",
|
||||
channelName, result);
|
||||
log("Error fetching FFZ emotes for channel {}, error {}", channelId,
|
||||
result);
|
||||
|
||||
return true;
|
||||
})
|
||||
|
||||
@@ -22,8 +22,10 @@ public:
|
||||
std::shared_ptr<const EmoteMap> emotes() const;
|
||||
boost::optional<EmotePtr> emote(const EmoteName &name) const;
|
||||
void loadEmotes();
|
||||
static void loadChannel(const QString &channelName,
|
||||
std::function<void(EmoteMap &&)> callback);
|
||||
static void loadChannel(
|
||||
const QString &channelId,
|
||||
std::function<void(EmoteMap &&)> emoteCallback,
|
||||
std::function<void(boost::optional<EmotePtr>)> modBadgeCallback);
|
||||
|
||||
private:
|
||||
Atomic<std::shared_ptr<const EmoteMap>> global_;
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
#include "FfzModBadge.hpp"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QImageReader>
|
||||
#include <QJsonObject>
|
||||
#include <QPainter>
|
||||
#include <QString>
|
||||
|
||||
#include "common/NetworkRequest.hpp"
|
||||
#include "common/Outcome.hpp"
|
||||
#include "messages/Emote.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
FfzModBadge::FfzModBadge(const QString &channelName)
|
||||
: channelName_(channelName)
|
||||
{
|
||||
}
|
||||
|
||||
void FfzModBadge::loadCustomModBadge()
|
||||
{
|
||||
static QString partialUrl("https://cdn.frankerfacez.com/room-badge/mod/");
|
||||
|
||||
QString url = partialUrl + channelName_ + "/1";
|
||||
NetworkRequest(url)
|
||||
|
||||
.onSuccess([this, url](auto result) -> Outcome {
|
||||
auto data = result.getData();
|
||||
|
||||
QBuffer buffer(const_cast<QByteArray *>(&data));
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
QImageReader reader(&buffer);
|
||||
if (reader.imageCount() == 0)
|
||||
return Failure;
|
||||
|
||||
QPixmap badgeOverlay = QPixmap::fromImageReader(&reader);
|
||||
QPixmap badgePixmap(18, 18);
|
||||
|
||||
// the default mod badge green color
|
||||
badgePixmap.fill(QColor("#34AE0A"));
|
||||
QPainter painter(&badgePixmap);
|
||||
QRectF rect(0, 0, 18, 18);
|
||||
painter.drawPixmap(rect, badgeOverlay, rect);
|
||||
|
||||
auto emote = Emote{{""},
|
||||
ImageSet{Image::fromPixmap(badgePixmap)},
|
||||
Tooltip{"Twitch Channel Moderator"},
|
||||
Url{url}};
|
||||
|
||||
this->badge_ = std::make_shared<Emote>(emote);
|
||||
// getBadge.execute();
|
||||
|
||||
return Success;
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
EmotePtr FfzModBadge::badge() const
|
||||
{
|
||||
return this->badge_;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
struct Emote;
|
||||
using EmotePtr = std::shared_ptr<const Emote>;
|
||||
|
||||
class FfzModBadge
|
||||
{
|
||||
public:
|
||||
FfzModBadge(const QString &channelName);
|
||||
|
||||
void loadCustomModBadge();
|
||||
|
||||
EmotePtr badge() const;
|
||||
|
||||
private:
|
||||
const QString channelName_;
|
||||
EmotePtr badge_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -30,15 +30,18 @@ AbstractIrcServer::AbstractIrcServer()
|
||||
this->readConnection_.reset(new IrcConnection);
|
||||
this->readConnection_->moveToThread(QCoreApplication::instance()->thread());
|
||||
|
||||
QObject::connect(this->readConnection_.get(),
|
||||
&Communi::IrcConnection::messageReceived,
|
||||
[this](auto msg) { this->messageReceived(msg); });
|
||||
QObject::connect(
|
||||
this->readConnection_.get(), &Communi::IrcConnection::messageReceived,
|
||||
[this](auto msg) { this->readConnectionMessageReceived(msg); });
|
||||
QObject::connect(this->readConnection_.get(),
|
||||
&Communi::IrcConnection::privateMessageReceived,
|
||||
[this](auto msg) { this->privateMessageReceived(msg); });
|
||||
QObject::connect(
|
||||
this->readConnection_.get(), &Communi::IrcConnection::connected,
|
||||
[this] { this->onConnected(this->readConnection_.get()); });
|
||||
[this] { this->onReadConnected(this->readConnection_.get()); });
|
||||
QObject::connect(
|
||||
this->writeConnection_.get(), &Communi::IrcConnection::connected,
|
||||
[this] { this->onWriteConnected(this->writeConnection_.get()); });
|
||||
QObject::connect(this->readConnection_.get(),
|
||||
&Communi::IrcConnection::disconnected,
|
||||
[this] { this->onDisconnected(); });
|
||||
@@ -227,7 +230,7 @@ std::shared_ptr<Channel> AbstractIrcServer::getChannelOrEmpty(
|
||||
return Channel::getEmpty();
|
||||
}
|
||||
|
||||
void AbstractIrcServer::onConnected(IrcConnection *connection)
|
||||
void AbstractIrcServer::onReadConnected(IrcConnection *connection)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->channelMutex);
|
||||
|
||||
@@ -262,6 +265,10 @@ void AbstractIrcServer::onConnected(IrcConnection *connection)
|
||||
this->falloffCounter_ = 1;
|
||||
}
|
||||
|
||||
void AbstractIrcServer::onWriteConnected(IrcConnection *connection)
|
||||
{
|
||||
}
|
||||
|
||||
void AbstractIrcServer::onDisconnected()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->channelMutex);
|
||||
@@ -310,7 +317,7 @@ void AbstractIrcServer::addFakeMessage(const QString &data)
|
||||
}
|
||||
else
|
||||
{
|
||||
this->messageReceived(fakeMessage);
|
||||
this->readConnectionMessageReceived(fakeMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,7 +326,8 @@ void AbstractIrcServer::privateMessageReceived(
|
||||
{
|
||||
}
|
||||
|
||||
void AbstractIrcServer::messageReceived(Communi::IrcMessage *message)
|
||||
void AbstractIrcServer::readConnectionMessageReceived(
|
||||
Communi::IrcMessage *message)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -49,10 +49,11 @@ protected:
|
||||
const QString &channelName) = 0;
|
||||
|
||||
virtual void privateMessageReceived(Communi::IrcPrivateMessage *message);
|
||||
virtual void messageReceived(Communi::IrcMessage *message);
|
||||
virtual void readConnectionMessageReceived(Communi::IrcMessage *message);
|
||||
virtual void writeConnectionMessageReceived(Communi::IrcMessage *message);
|
||||
|
||||
virtual void onConnected(IrcConnection *connection);
|
||||
virtual void onReadConnected(IrcConnection *connection);
|
||||
virtual void onWriteConnected(IrcConnection *connection);
|
||||
virtual void onDisconnected();
|
||||
virtual void onSocketError();
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ ChatroomChannel::ChatroomChannel(const QString &channelName,
|
||||
}
|
||||
}
|
||||
|
||||
void ChatroomChannel::refreshChannelEmotes()
|
||||
void ChatroomChannel::refreshBTTVChannelEmotes()
|
||||
{
|
||||
if (this->chatroomOwnerId.isEmpty())
|
||||
{
|
||||
@@ -36,17 +36,28 @@ void ChatroomChannel::refreshChannelEmotes()
|
||||
this->bttvEmotes_.set(
|
||||
std::make_shared<EmoteMap>(std::move(emoteMap)));
|
||||
});
|
||||
FfzEmotes::loadChannel(username, [this, weak](auto &&emoteMap) {
|
||||
if (auto shared = weak.lock())
|
||||
this->ffzEmotes_.set(
|
||||
std::make_shared<EmoteMap>(std::move(emoteMap)));
|
||||
});
|
||||
if (auto shared = weak.lock())
|
||||
{
|
||||
this->chatroomOwnerName = username;
|
||||
}
|
||||
});
|
||||
}
|
||||
void ChatroomChannel::refreshFFZChannelEmotes()
|
||||
{
|
||||
if (this->chatroomOwnerId.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
FfzEmotes::loadChannel(
|
||||
this->chatroomOwnerId,
|
||||
[this](auto &&emoteMap) {
|
||||
this->ffzEmotes_.set(
|
||||
std::make_shared<EmoteMap>(std::move(emoteMap)));
|
||||
},
|
||||
[this](auto &&modBadge) {
|
||||
this->ffzCustomModBadge_.set(std::move(modBadge));
|
||||
});
|
||||
}
|
||||
|
||||
const QString &ChatroomChannel::getDisplayName() const
|
||||
{
|
||||
|
||||
@@ -13,7 +13,8 @@ protected:
|
||||
explicit ChatroomChannel(const QString &channelName,
|
||||
TwitchBadges &globalTwitchBadges,
|
||||
BttvEmotes &globalBttv, FfzEmotes &globalFfz);
|
||||
virtual void refreshChannelEmotes() override;
|
||||
virtual void refreshBTTVChannelEmotes() override;
|
||||
virtual void refreshFFZChannelEmotes() override;
|
||||
virtual const QString &getDisplayName() const override;
|
||||
|
||||
QString chatroomOwnerId;
|
||||
|
||||
@@ -582,49 +582,6 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message)
|
||||
}
|
||||
}
|
||||
|
||||
void IrcMessageHandler::handleWriteConnectionNoticeMessage(
|
||||
Communi::IrcNoticeMessage *message)
|
||||
{
|
||||
static std::unordered_set<std::string> readConnectionOnlyIDs{
|
||||
"host_on",
|
||||
"host_off",
|
||||
"host_target_went_offline",
|
||||
"emote_only_on",
|
||||
"emote_only_off",
|
||||
"slow_on",
|
||||
"slow_off",
|
||||
"subs_on",
|
||||
"subs_off",
|
||||
"r9k_on",
|
||||
"r9k_off",
|
||||
|
||||
// Display for user who times someone out. This implies you're a
|
||||
// moderator, at which point you will be connected to PubSub and receive
|
||||
// a better message from there
|
||||
"timeout_success",
|
||||
"ban_success",
|
||||
|
||||
// Channel suspended notices
|
||||
"msg_channel_suspended",
|
||||
};
|
||||
|
||||
QVariant v = message->tag("msg-id");
|
||||
if (v.isValid())
|
||||
{
|
||||
std::string msgID = v.toString().toStdString();
|
||||
|
||||
if (readConnectionOnlyIDs.find(msgID) != readConnectionOnlyIDs.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
log("Showing notice message from write connection with message id '{}'",
|
||||
msgID);
|
||||
}
|
||||
|
||||
this->handleNoticeMessage(message);
|
||||
}
|
||||
|
||||
void IrcMessageHandler::handleJoinMessage(Communi::IrcMessage *message)
|
||||
{
|
||||
auto app = getApp();
|
||||
|
||||
@@ -46,8 +46,6 @@ public:
|
||||
Communi::IrcNoticeMessage *message);
|
||||
void handleNoticeMessage(Communi::IrcNoticeMessage *message);
|
||||
|
||||
void handleWriteConnectionNoticeMessage(Communi::IrcNoticeMessage *message);
|
||||
|
||||
void handleJoinMessage(Communi::IrcMessage *message);
|
||||
void handlePartMessage(Communi::IrcMessage *message);
|
||||
|
||||
|
||||
@@ -699,13 +699,13 @@ PubSub::PubSub()
|
||||
};
|
||||
|
||||
this->moderationActionHandlers["denied_automod_message"] =
|
||||
[this](const auto &data, const auto &roomID) {
|
||||
[](const auto &data, const auto &roomID) {
|
||||
// This message got denied by a moderator
|
||||
// qDebug() << QString::fromStdString(rj::stringify(data));
|
||||
};
|
||||
|
||||
this->moderationActionHandlers["approved_automod_message"] =
|
||||
[this](const auto &data, const auto &roomID) {
|
||||
[](const auto &data, const auto &roomID) {
|
||||
// This message got approved by a moderator
|
||||
// qDebug() << QString::fromStdString(rj::stringify(data));
|
||||
};
|
||||
|
||||
@@ -87,7 +87,6 @@ TwitchChannel::TwitchChannel(const QString &name,
|
||||
, globalFfz_(ffz)
|
||||
, bttvEmotes_(std::make_shared<EmoteMap>())
|
||||
, ffzEmotes_(std::make_shared<EmoteMap>())
|
||||
, ffzCustomModBadge_(name)
|
||||
, mod_(false)
|
||||
{
|
||||
log("[TwitchChannel:{}] Opened", name);
|
||||
@@ -113,6 +112,8 @@ TwitchChannel::TwitchChannel(const QString &name,
|
||||
this->refreshLiveStatus();
|
||||
this->refreshBadges();
|
||||
this->refreshCheerEmotes();
|
||||
this->refreshFFZChannelEmotes();
|
||||
this->refreshBTTVChannelEmotes();
|
||||
});
|
||||
|
||||
// timers
|
||||
@@ -135,9 +136,7 @@ TwitchChannel::TwitchChannel(const QString &name,
|
||||
void TwitchChannel::initialize()
|
||||
{
|
||||
this->refreshChatters();
|
||||
this->refreshChannelEmotes();
|
||||
this->refreshBadges();
|
||||
this->ffzCustomModBadge_.loadCustomModBadge();
|
||||
}
|
||||
|
||||
bool TwitchChannel::isEmpty() const
|
||||
@@ -150,19 +149,30 @@ bool TwitchChannel::canSendMessage() const
|
||||
return !this->isEmpty();
|
||||
}
|
||||
|
||||
void TwitchChannel::refreshChannelEmotes()
|
||||
void TwitchChannel::refreshBTTVChannelEmotes()
|
||||
{
|
||||
BttvEmotes::loadChannel(
|
||||
this->getName(), [this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
||||
this->roomId(), [this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
||||
if (auto shared = weak.lock())
|
||||
this->bttvEmotes_.set(
|
||||
std::make_shared<EmoteMap>(std::move(emoteMap)));
|
||||
});
|
||||
}
|
||||
|
||||
void TwitchChannel::refreshFFZChannelEmotes()
|
||||
{
|
||||
FfzEmotes::loadChannel(
|
||||
this->getName(), [this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
||||
this->roomId(),
|
||||
[this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
||||
if (auto shared = weak.lock())
|
||||
this->ffzEmotes_.set(
|
||||
std::make_shared<EmoteMap>(std::move(emoteMap)));
|
||||
},
|
||||
[this, weak = weakOf<Channel>(this)](auto &&modBadge) {
|
||||
if (auto shared = weak.lock())
|
||||
{
|
||||
this->ffzCustomModBadge_.set(std::move(modBadge));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -818,10 +828,7 @@ boost::optional<EmotePtr> TwitchChannel::twitchBadge(
|
||||
|
||||
boost::optional<EmotePtr> TwitchChannel::ffzCustomModBadge() const
|
||||
{
|
||||
if (auto badge = this->ffzCustomModBadge_.badge())
|
||||
return badge;
|
||||
|
||||
return boost::none;
|
||||
return this->ffzCustomModBadge_.get();
|
||||
}
|
||||
|
||||
boost::optional<CheerEmote> TwitchChannel::cheerEmote(const QString &string)
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "common/Outcome.hpp"
|
||||
#include "common/UniqueAccess.hpp"
|
||||
#include "common/UsernameSet.hpp"
|
||||
#include "providers/ffz/FfzModBadge.hpp"
|
||||
#include "providers/twitch/TwitchEmotes.hpp"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
@@ -85,7 +84,8 @@ public:
|
||||
std::shared_ptr<const EmoteMap> bttvEmotes() const;
|
||||
std::shared_ptr<const EmoteMap> ffzEmotes() const;
|
||||
|
||||
virtual void refreshChannelEmotes();
|
||||
virtual void refreshBTTVChannelEmotes();
|
||||
virtual void refreshFFZChannelEmotes();
|
||||
|
||||
// Badges
|
||||
boost::optional<EmotePtr> ffzCustomModBadge() const;
|
||||
@@ -150,13 +150,13 @@ protected:
|
||||
FfzEmotes &globalFfz_;
|
||||
Atomic<std::shared_ptr<const EmoteMap>> bttvEmotes_;
|
||||
Atomic<std::shared_ptr<const EmoteMap>> ffzEmotes_;
|
||||
Atomic<boost::optional<EmotePtr>> ffzCustomModBadge_;
|
||||
|
||||
private:
|
||||
// Badges
|
||||
UniqueAccess<std::map<QString, std::map<QString, EmotePtr>>>
|
||||
badgeSets_; // "subscribers": { "0": ... "3": ... "6": ...
|
||||
UniqueAccess<std::vector<CheerEmoteSet>> cheerEmoteSets_;
|
||||
FfzModBadge ffzCustomModBadge_;
|
||||
|
||||
bool mod_ = false;
|
||||
bool vip_ = false;
|
||||
|
||||
@@ -952,7 +952,7 @@ void TwitchMessageBuilder::parseHighlights()
|
||||
{
|
||||
HighlightPhrase selfHighlight(
|
||||
currentUsername, getSettings()->enableSelfHighlightTaskbar,
|
||||
getSettings()->enableSelfHighlightSound, false);
|
||||
getSettings()->enableSelfHighlightSound, false, false);
|
||||
activeHighlights.emplace_back(std::move(selfHighlight));
|
||||
}
|
||||
|
||||
@@ -1171,7 +1171,7 @@ void TwitchMessageBuilder::appendTwitchBadges()
|
||||
{
|
||||
if (auto customModBadge = this->twitchChannel->ffzCustomModBadge())
|
||||
{
|
||||
this->emplace<BadgeElement>(
|
||||
this->emplace<ModBadgeElement>(
|
||||
customModBadge.get(),
|
||||
MessageElementFlag::BadgeChannelAuthority)
|
||||
->setTooltip((*customModBadge)->tooltip.string);
|
||||
|
||||
@@ -128,9 +128,8 @@ void TwitchServer::privateMessageReceived(Communi::IrcPrivateMessage *message)
|
||||
IrcMessageHandler::getInstance().handlePrivMessage(message, *this);
|
||||
}
|
||||
|
||||
void TwitchServer::messageReceived(Communi::IrcMessage *message)
|
||||
void TwitchServer::readConnectionMessageReceived(Communi::IrcMessage *message)
|
||||
{
|
||||
// this->readConnection
|
||||
if (message->type() == Communi::IrcMessage::Type::Private)
|
||||
{
|
||||
// We already have a handler for private messages
|
||||
@@ -141,39 +140,11 @@ void TwitchServer::messageReceived(Communi::IrcMessage *message)
|
||||
|
||||
auto &handler = IrcMessageHandler::getInstance();
|
||||
|
||||
if (command == "ROOMSTATE")
|
||||
{
|
||||
handler.handleRoomStateMessage(message);
|
||||
}
|
||||
else if (command == "CLEARCHAT")
|
||||
{
|
||||
handler.handleClearChatMessage(message);
|
||||
}
|
||||
else if (command == "CLEARMSG")
|
||||
{
|
||||
handler.handleClearMessageMessage(message);
|
||||
}
|
||||
else if (command == "USERSTATE")
|
||||
{
|
||||
handler.handleUserStateMessage(message);
|
||||
}
|
||||
else if (command == "WHISPER")
|
||||
{
|
||||
handler.handleWhisperMessage(message);
|
||||
}
|
||||
else if (command == "USERNOTICE")
|
||||
{
|
||||
handler.handleUserNoticeMessage(message, *this);
|
||||
}
|
||||
else if (command == "MODE")
|
||||
// Below commands enabled through the twitch.tv/membership CAP REQ
|
||||
if (command == "MODE")
|
||||
{
|
||||
handler.handleModeMessage(message);
|
||||
}
|
||||
else if (command == "NOTICE")
|
||||
{
|
||||
handler.handleNoticeMessage(
|
||||
static_cast<Communi::IrcNoticeMessage *>(message));
|
||||
}
|
||||
else if (command == "JOIN")
|
||||
{
|
||||
handler.handleJoinMessage(message);
|
||||
@@ -186,24 +157,58 @@ void TwitchServer::messageReceived(Communi::IrcMessage *message)
|
||||
|
||||
void TwitchServer::writeConnectionMessageReceived(Communi::IrcMessage *message)
|
||||
{
|
||||
switch (message->type())
|
||||
{
|
||||
case Communi::IrcMessage::Type::Notice:
|
||||
{
|
||||
IrcMessageHandler::getInstance().handleWriteConnectionNoticeMessage(
|
||||
static_cast<Communi::IrcNoticeMessage *>(message));
|
||||
}
|
||||
break;
|
||||
const QString &command = message->command();
|
||||
|
||||
default:;
|
||||
auto &handler = IrcMessageHandler::getInstance();
|
||||
|
||||
// Below commands enabled through the twitch.tv/commands CAP REQ
|
||||
if (command == "USERSTATE")
|
||||
{
|
||||
handler.handleUserStateMessage(message);
|
||||
}
|
||||
else if (command == "WHISPER")
|
||||
{
|
||||
handler.handleWhisperMessage(message);
|
||||
}
|
||||
else if (command == "USERNOTICE")
|
||||
{
|
||||
handler.handleUserNoticeMessage(message, *this);
|
||||
}
|
||||
else if (command == "ROOMSTATE")
|
||||
{
|
||||
handler.handleRoomStateMessage(message);
|
||||
}
|
||||
else if (command == "CLEARCHAT")
|
||||
{
|
||||
handler.handleClearChatMessage(message);
|
||||
}
|
||||
else if (command == "CLEARMSG")
|
||||
{
|
||||
handler.handleClearMessageMessage(message);
|
||||
}
|
||||
else if (command == "NOTICE")
|
||||
{
|
||||
handler.handleNoticeMessage(
|
||||
static_cast<Communi::IrcNoticeMessage *>(message));
|
||||
}
|
||||
}
|
||||
|
||||
void TwitchServer::onConnected(IrcConnection *connection)
|
||||
void TwitchServer::onReadConnected(IrcConnection *connection)
|
||||
{
|
||||
// connection in thise case is the read connection
|
||||
connection->sendRaw(
|
||||
"CAP REQ :twitch.tv/tags twitch.tv/commands twitch.tv/membership");
|
||||
AbstractIrcServer::onReadConnected(connection);
|
||||
|
||||
// twitch.tv/tags enables IRCv3 tags on messages. See https://dev.twitch.tv/docs/irc/tags/
|
||||
// twitch.tv/membership enables the JOIN/PART/MODE/NAMES commands. See https://dev.twitch.tv/docs/irc/membership/
|
||||
connection->sendRaw("CAP REQ :twitch.tv/tags twitch.tv/membership");
|
||||
}
|
||||
|
||||
void TwitchServer::onWriteConnected(IrcConnection *connection)
|
||||
{
|
||||
AbstractIrcServer::onWriteConnected(connection);
|
||||
|
||||
// twitch.tv/tags enables IRCv3 tags on messages. See https://dev.twitch.tv/docs/irc/tags/
|
||||
// twitch.tv/commands enables a bunch of miscellaneous command capabilities. See https://dev.twitch.tv/docs/irc/commands/
|
||||
connection->sendRaw("CAP REQ :twitch.tv/tags twitch.tv/commands");
|
||||
}
|
||||
|
||||
std::shared_ptr<Channel> TwitchServer::getCustomChannel(
|
||||
|
||||
@@ -51,11 +51,13 @@ protected:
|
||||
|
||||
virtual void privateMessageReceived(
|
||||
Communi::IrcPrivateMessage *message) override;
|
||||
virtual void messageReceived(Communi::IrcMessage *message) override;
|
||||
virtual void readConnectionMessageReceived(
|
||||
Communi::IrcMessage *message) override;
|
||||
virtual void writeConnectionMessageReceived(
|
||||
Communi::IrcMessage *message) override;
|
||||
|
||||
virtual void onConnected(IrcConnection *connection) override;
|
||||
virtual void onReadConnected(IrcConnection *connection) override;
|
||||
virtual void onWriteConnected(IrcConnection *connection) override;
|
||||
|
||||
virtual std::shared_ptr<Channel> getCustomChannel(
|
||||
const QString &channelname) override;
|
||||
|
||||
Reference in New Issue
Block a user