feat: Live Emote Updates for 7TV (#4090)

Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
nerix
2022-11-13 12:07:41 +01:00
committed by GitHub
parent 8fa89b4073
commit 39f7d8ac4c
35 changed files with 1833 additions and 54 deletions
+19
View File
@@ -46,4 +46,23 @@ EmotePtr cachedOrMakeEmotePtr(
}
}
EmoteMap::const_iterator EmoteMap::findEmote(const QString &emoteNameHint,
const QString &emoteID) const
{
auto it = this->end();
if (!emoteNameHint.isEmpty())
{
it = this->find(EmoteName{emoteNameHint});
}
if (it == this->end() || it->second->id.string != emoteID)
{
it = std::find_if(this->begin(), this->end(),
[emoteID](const auto entry) {
return entry.second->id.string == emoteID;
});
}
return it;
}
} // namespace chatterino
+23
View File
@@ -1,8 +1,10 @@
#pragma once
#include "common/Atomic.hpp"
#include "messages/Image.hpp"
#include "messages/ImageSet.hpp"
#include <boost/optional.hpp>
#include <functional>
#include <memory>
#include <unordered_map>
@@ -15,6 +17,13 @@ struct Emote {
Tooltip tooltip;
Url homePage;
bool zeroWidth;
EmoteId id;
EmoteAuthor author;
/**
* If this emote is aliased, this contains
* the original (base) name of the emote.
*/
boost::optional<EmoteName> baseName;
// FOURTF: no solution yet, to be refactored later
const QString &getCopyString() const
@@ -30,6 +39,20 @@ using EmotePtr = std::shared_ptr<const Emote>;
class EmoteMap : public std::unordered_map<EmoteName, EmotePtr>
{
public:
/**
* Finds an emote by it's id with a hint to it's name.
*
* 1. Searches by name for the emote, checking if the ids match (fast-path).
* 2. Searches through the map for an emote with the `emoteID` (slow-path).
*
* @param emoteNameHint A hint to the name of the searched emote,
* may be empty.
* @param emoteID The emote id to search for.
* @return An iterator to the found emote (possibly this->end()).
*/
EmoteMap::const_iterator findEmote(const QString &emoteNameHint,
const QString &emoteID) const;
};
using EmoteIdMap = std::unordered_map<EmoteId, EmotePtr>;
using WeakEmoteMap = std::unordered_map<EmoteName, std::weak_ptr<const Emote>>;
+3
View File
@@ -45,6 +45,9 @@ enum class MessageFlag : int64_t {
ElevatedMessage = (1LL << 25),
ParticipatedThread = (1LL << 26),
CheerMessage = (1LL << 27),
LiveUpdatesAdd = (1LL << 28),
LiveUpdatesRemove = (1LL << 29),
LiveUpdatesUpdate = (1LL << 30),
};
using MessageFlags = FlagsEnum<MessageFlag>;
+166
View File
@@ -23,6 +23,45 @@ QRegularExpression IRC_COLOR_PARSE_REGEX(
"(\u0003(\\d{1,2})?(,(\\d{1,2}))?|\u000f)",
QRegularExpression::UseUnicodePropertiesOption);
QString formatUpdatedEmoteList(const QString &platform,
const std::vector<QString> &emoteNames,
bool isAdd, bool isFirstWord)
{
QString text = "";
if (isAdd)
{
text += isFirstWord ? "Added" : "added";
}
else
{
text += isFirstWord ? "Removed" : "removed";
}
if (emoteNames.size() == 1)
{
text += QString(" %1 emote ").arg(platform);
}
else
{
text += QString(" %1 %2 emotes ").arg(emoteNames.size()).arg(platform);
}
auto i = 0;
for (const auto &emoteName : emoteNames)
{
i++;
if (i > 1)
{
text += i == emoteNames.size() ? " and " : ", ";
}
text += emoteName;
}
text += ".";
return text;
}
} // namespace
namespace chatterino {
@@ -473,6 +512,133 @@ MessageBuilder::MessageBuilder(const AutomodUserAction &action)
MessageColor::System);
}
MessageBuilder::MessageBuilder(LiveUpdatesAddEmoteMessageTag /*unused*/,
const QString &platform, const QString &actor,
const std::vector<QString> &emoteNames)
: MessageBuilder()
{
auto text =
formatUpdatedEmoteList(platform, emoteNames, true, actor.isEmpty());
this->emplace<TimestampElement>();
if (!actor.isEmpty())
{
this->emplace<TextElement>(actor, MessageElementFlag::Username,
MessageColor::System)
->setLink({Link::UserInfo, actor});
}
this->emplace<TextElement>(text, MessageElementFlag::Text,
MessageColor::System);
QString finalText;
if (actor.isEmpty())
{
finalText = text;
}
else
{
finalText = QString("%1 %2").arg(actor, text);
}
this->message().loginName = actor;
this->message().messageText = finalText;
this->message().searchText = finalText;
this->message().flags.set(MessageFlag::System);
this->message().flags.set(MessageFlag::LiveUpdatesAdd);
this->message().flags.set(MessageFlag::DoNotTriggerNotification);
}
MessageBuilder::MessageBuilder(LiveUpdatesRemoveEmoteMessageTag /*unused*/,
const QString &platform, const QString &actor,
const std::vector<QString> &emoteNames)
: MessageBuilder()
{
auto text =
formatUpdatedEmoteList(platform, emoteNames, false, actor.isEmpty());
this->emplace<TimestampElement>();
if (!actor.isEmpty())
{
this->emplace<TextElement>(actor, MessageElementFlag::Username,
MessageColor::System)
->setLink({Link::UserInfo, actor});
}
this->emplace<TextElement>(text, MessageElementFlag::Text,
MessageColor::System);
QString finalText;
if (actor.isEmpty())
{
finalText = text;
}
else
{
finalText = QString("%1 %2").arg(actor, text);
}
this->message().loginName = actor;
this->message().messageText = finalText;
this->message().searchText = finalText;
this->message().flags.set(MessageFlag::System);
this->message().flags.set(MessageFlag::LiveUpdatesRemove);
this->message().flags.set(MessageFlag::DoNotTriggerNotification);
}
MessageBuilder::MessageBuilder(LiveUpdatesUpdateEmoteMessageTag /*unused*/,
const QString &platform, const QString &actor,
const QString &emoteName,
const QString &oldEmoteName)
: MessageBuilder()
{
auto text = QString("renamed %1 emote %2 to %3.")
.arg(platform, oldEmoteName, emoteName);
this->emplace<TimestampElement>();
this->emplace<TextElement>(actor, MessageElementFlag::Username,
MessageColor::System)
->setLink({Link::UserInfo, actor});
this->emplace<TextElement>(text, MessageElementFlag::Text,
MessageColor::System);
auto finalText = QString("%1 %2").arg(actor, text);
this->message().loginName = actor;
this->message().messageText = finalText;
this->message().searchText = finalText;
this->message().flags.set(MessageFlag::System);
this->message().flags.set(MessageFlag::LiveUpdatesUpdate);
this->message().flags.set(MessageFlag::DoNotTriggerNotification);
}
MessageBuilder::MessageBuilder(LiveUpdatesUpdateEmoteSetMessageTag /*unused*/,
const QString &platform, const QString &actor,
const QString &emoteSetName)
: MessageBuilder()
{
auto text = QString("switched the active %1 Emote Set to \"%2\".")
.arg(platform, emoteSetName);
this->emplace<TimestampElement>();
this->emplace<TextElement>(actor, MessageElementFlag::Username,
MessageColor::System)
->setLink({Link::UserInfo, actor});
this->emplace<TextElement>(text, MessageElementFlag::Text,
MessageColor::System);
auto finalText = QString("%1 %2").arg(actor, text);
this->message().loginName = actor;
this->message().messageText = finalText;
this->message().searchText = finalText;
this->message().flags.set(MessageFlag::System);
this->message().flags.set(MessageFlag::LiveUpdatesUpdate);
this->message().flags.set(MessageFlag::DoNotTriggerNotification);
}
Message *MessageBuilder::operator->()
{
return this->message_.get();
+25
View File
@@ -19,8 +19,20 @@ struct SystemMessageTag {
};
struct TimeoutMessageTag {
};
struct LiveUpdatesUpdateEmoteMessageTag {
};
struct LiveUpdatesRemoveEmoteMessageTag {
};
struct LiveUpdatesAddEmoteMessageTag {
};
struct LiveUpdatesUpdateEmoteSetMessageTag {
};
const SystemMessageTag systemMessage{};
const TimeoutMessageTag timeoutMessage{};
const LiveUpdatesUpdateEmoteMessageTag liveUpdatesUpdateEmoteMessage{};
const LiveUpdatesRemoveEmoteMessageTag liveUpdatesRemoveEmoteMessage{};
const LiveUpdatesAddEmoteMessageTag liveUpdatesAddEmoteMessage{};
const LiveUpdatesUpdateEmoteSetMessageTag liveUpdatesUpdateEmoteSetMessage{};
MessagePtr makeSystemMessage(const QString &text);
MessagePtr makeSystemMessage(const QString &text, const QTime &time);
@@ -53,6 +65,19 @@ public:
MessageBuilder(const BanAction &action, uint32_t count = 1);
MessageBuilder(const UnbanAction &action);
MessageBuilder(const AutomodUserAction &action);
MessageBuilder(LiveUpdatesAddEmoteMessageTag, const QString &platform,
const QString &actor,
const std::vector<QString> &emoteNames);
MessageBuilder(LiveUpdatesRemoveEmoteMessageTag, const QString &platform,
const QString &actor,
const std::vector<QString> &emoteNames);
MessageBuilder(LiveUpdatesUpdateEmoteMessageTag, const QString &platform,
const QString &actor, const QString &emoteName,
const QString &oldEmoteName);
MessageBuilder(LiveUpdatesUpdateEmoteSetMessageTag, const QString &platform,
const QString &actor, const QString &emoteSetName);
virtual ~MessageBuilder() = default;
Message *operator->();