mirror-chatterino2/src/providers/twitch/TwitchMessageBuilder.cpp

1392 lines
43 KiB
C++
Raw Normal View History

2018-06-26 14:09:39 +02:00
#include "providers/twitch/TwitchMessageBuilder.hpp"
2018-06-26 14:09:39 +02:00
#include "Application.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/ignores/IgnoreController.hpp"
2020-02-28 19:04:25 +01:00
#include "controllers/ignores/IgnorePhrase.hpp"
#include "messages/Message.hpp"
#include "providers/chatterino/ChatterinoBadges.hpp"
#include "providers/ffz/FfzBadges.hpp"
2018-08-14 17:45:17 +02:00
#include "providers/twitch/TwitchBadges.hpp"
2018-06-26 14:09:39 +02:00
#include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/TwitchCommon.hpp"
#include "providers/twitch/TwitchIrcServer.hpp"
2018-06-28 19:46:45 +02:00
#include "singletons/Emotes.hpp"
#include "singletons/Resources.hpp"
#include "singletons/Settings.hpp"
2018-06-28 20:03:04 +02:00
#include "singletons/Theme.hpp"
2018-06-26 14:09:39 +02:00
#include "singletons/WindowManager.hpp"
#include "util/IrcHelpers.hpp"
#include "widgets/Window.hpp"
2017-04-12 17:46:44 +02:00
#include <QApplication>
#include <QColor>
#include <QDebug>
#include <QMediaPlayer>
Squashed commit of the following: commit ea07bbef0be589cc5412bff0a25735ac713128e3 Merge: 0b36f436 5cfcf114 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 20:05:14 2018 +0200 Merge branch 'blacklist' into blacklistnew commit 5cfcf114b65ea7c0fca9654393ac2faa78610098 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 20:00:16 2018 +0200 rename second pattern to replacement commit f08cc4cf88c49140a282d3d29af5ad8e7179bb7c Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 19:52:30 2018 +0200 delete out commented code commit 1acb1278aa0109359e0e349ae240d10b34de9d34 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 19:52:03 2018 +0200 fix replacement with emotes issues commit 646268ab1883a955291f152029fa37f4416e681e Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 01:06:36 2018 +0200 fix build commit ad711b4c15ef0660c554af5ceea82397769a2313 Merge: e8e059f8 8bcc9c48 Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 00:52:38 2018 +0200 Merge branch 'master' of https://github.com/fourtf/chatterino2 into blacklist commit e8e059f8473271128086c5230cf1c40b235af380 Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 00:25:58 2018 +0200 add replaced emotes into twitchEmotes commit a63454f00de479cee1ab1eca18a8b4ab93e37d52 Merge: e7f2f397 63eaf3b9 Author: hemirt <hemirt@email.cz> Date: Sat Aug 11 22:38:16 2018 +0200 Merge branch 'master' of https://github.com/fourtf/chatterino2 into blacklist commit e7f2f397378d0582d989ff8fcbe83bcec41449a1 Author: hemirt <hemirt@email.cz> Date: Sat Aug 11 21:54:01 2018 +0200 emotedata commit f00d3da537ec14aebd9cbb84d63f7b16c196f199 Author: hemirt <hemirt@email.cz> Date: Sat Jul 28 19:53:55 2018 +0200 rename variables to fit better, emotes in capture groups from regex work commit 00c9fa080aeb8a4a187743d708ba139cbed5a849 Author: hemirt <hemirt@email.cz> Date: Mon Jul 9 19:53:53 2018 +0200 add case sensitivity checkbox and fix validity issues due to isValid that checked regex commit 4385fcd13fe6e011b91a3f4a29fd440d019fc499 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 21:09:14 2018 +0200 remove commented code commit 1834342f74c4fbff38b81fa2c2fcfd6c55adc0d5 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 21:03:13 2018 +0200 IgnorePhrase replacement also removes twitch emotes info about the matched and changed parts and shifts positions of other emotes from emote infos to the corresponding new position commit d3b6e294ed38fa8587c367c5da6f257641c28b86 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 16:21:33 2018 +0200 ignore phrases
2018-09-23 20:21:50 +02:00
#include <QStringRef>
2018-08-02 14:23:27 +02:00
#include <boost/variant.hpp>
#include "common/QLogging.hpp"
namespace {
const QString regexHelpString("(\\w+)[.,!?;:]*?$");
2020-07-18 16:03:51 +02:00
// matches a mention with punctuation at the end, like "@username," or "@username!!!" where capture group would return "username"
const QRegularExpression mentionRegex("^@" + regexHelpString);
// if findAllUsernames setting is enabled, matches strings like in the examples above, but without @ symbol at the beginning
const QRegularExpression allUsernamesMentionRegex("^" + regexHelpString);
2020-07-18 16:03:51 +02:00
const QSet<QString> zeroWidthEmotes{
"SoSnowy", "IceCold", "SantaHat", "TopHat",
"ReinDeer", "CandyCane", "cvMask", "cvHazmat",
};
} // namespace
2017-04-14 17:52:22 +02:00
namespace chatterino {
2019-12-01 13:32:41 +01:00
namespace {
QColor getRandomColor(const QVariant &userId)
{
bool ok = true;
int colorSeed = userId.toInt(&ok);
if (!ok)
{
// We were unable to convert the user ID to an integer, this means Twitch has decided to start using non-integer user IDs
// Just randomize the users color
colorSeed = std::rand();
}
const auto colorIndex = colorSeed % TWITCH_USERNAME_COLORS.size();
return TWITCH_USERNAME_COLORS[colorIndex];
}
2019-12-01 13:32:41 +01:00
QStringList parseTagList(const QVariantMap &tags, const QString &key)
{
auto iterator = tags.find(key);
if (iterator == tags.end())
return QStringList{};
return iterator.value().toString().split(
',', QString::SplitBehavior::SkipEmptyParts);
}
std::map<QString, QString> parseBadgeInfos(const QVariantMap &tags)
{
std::map<QString, QString> badgeInfos;
for (QString badgeInfo : parseTagList(tags, "badge-info"))
{
QStringList parts = badgeInfo.split('/');
if (parts.size() != 2)
{
continue;
}
badgeInfos.emplace(parts[0], parts[1]);
}
return badgeInfos;
}
std::vector<Badge> parseBadges(const QVariantMap &tags)
{
std::vector<Badge> badges;
for (QString badge : parseTagList(tags, "badges"))
{
QStringList parts = badge.split('/');
if (parts.size() != 2)
{
continue;
}
badges.emplace_back(parts[0], parts[1]);
}
return badges;
}
} // namespace
2018-09-30 13:37:39 +02:00
TwitchMessageBuilder::TwitchMessageBuilder(
Channel *_channel, const Communi::IrcPrivateMessage *_ircMessage,
const MessageParseArgs &_args)
: SharedMessageBuilder(_channel, _ircMessage, _args)
2017-12-31 00:50:07 +01:00
, twitchChannel(dynamic_cast<TwitchChannel *>(_channel))
{
this->usernameColor_ = getApp()->themes->messages.textColors.system;
}
2018-09-30 13:37:39 +02:00
TwitchMessageBuilder::TwitchMessageBuilder(
Channel *_channel, const Communi::IrcMessage *_ircMessage,
const MessageParseArgs &_args, QString content, bool isAction)
: SharedMessageBuilder(_channel, _ircMessage, _args, content, isAction)
, twitchChannel(dynamic_cast<TwitchChannel *>(_channel))
2017-04-12 17:46:44 +02:00
{
this->usernameColor_ = getApp()->themes->messages.textColors.system;
2017-04-12 17:46:44 +02:00
}
2018-01-23 21:33:49 +01:00
bool TwitchMessageBuilder::isIgnored() const
2017-04-12 17:46:44 +02:00
{
if (SharedMessageBuilder::isIgnored())
2018-10-21 13:43:02 +02:00
{
return true;
2018-01-23 21:33:49 +01:00
}
auto app = getApp();
if (getSettings()->enableTwitchBlockedUsers &&
2018-10-21 13:43:02 +02:00
this->tags.contains("user-id"))
{
auto sourceUserID = this->tags.value("user-id").toString();
2021-05-01 13:38:58 +02:00
auto blocks =
app->accounts->twitch.getCurrent()->accessBlockedUserIds();
if (auto it = blocks->find(sourceUserID); it != blocks->end())
2018-10-21 13:43:02 +02:00
{
2021-05-01 13:38:58 +02:00
switch (static_cast<ShowIgnoredUsersMessages>(
getSettings()->showBlockedUsersMessages.getValue()))
2018-10-21 13:43:02 +02:00
{
2021-05-01 13:38:58 +02:00
case ShowIgnoredUsersMessages::IfModerator:
if (this->channel->isMod() ||
this->channel->isBroadcaster())
return false;
break;
case ShowIgnoredUsersMessages::IfBroadcaster:
if (this->channel->isBroadcaster())
return false;
break;
case ShowIgnoredUsersMessages::Never:
break;
}
2021-05-01 13:38:58 +02:00
return true;
}
}
2018-01-23 21:33:49 +01:00
return false;
2017-04-12 17:46:44 +02:00
}
void TwitchMessageBuilder::triggerHighlights()
{
if (this->historicalMessage_)
{
// Do nothing. Highlights should not be triggered on historical messages.
return;
}
SharedMessageBuilder::triggerHighlights();
}
2018-01-23 23:28:06 +01:00
MessagePtr TwitchMessageBuilder::build()
2017-04-12 17:46:44 +02:00
{
// PARSE
2019-08-23 16:52:04 +02:00
this->userId_ = this->ircMessage->tag("user-id").toString();
this->parse();
2018-10-21 13:43:02 +02:00
if (this->userName == this->channel->getName())
{
this->senderIsBroadcaster = true;
}
this->message().channelName = this->channel->getName();
2018-01-23 23:28:06 +01:00
this->parseMessageID();
2018-01-23 23:28:06 +01:00
this->parseRoomID();
// If it is a reward it has to be appended first
if (this->args.channelPointRewardId != "")
{
const auto &reward = this->twitchChannel->channelPointReward(
this->args.channelPointRewardId);
if (reward)
{
this->appendChannelPointRewardMessage(reward.get(), this);
}
}
2018-01-23 23:28:06 +01:00
this->appendChannelName();
if (this->tags.contains("rm-deleted"))
{
this->message().flags.set(MessageFlag::Disabled);
}
this->historicalMessage_ = this->tags.contains("historical");
if (this->tags.contains("msg-id") &&
this->tags["msg-id"].toString().split(';').contains(
"highlighted-message"))
{
this->message().flags.set(MessageFlag::RedeemedHighlight);
}
2018-01-23 23:28:06 +01:00
// timestamp
this->emplace<TimestampElement>(
calculateMessageTimestamp(this->ircMessage));
2017-04-12 17:46:44 +02:00
bool addModerationElement = true;
2018-10-21 13:43:02 +02:00
if (this->senderIsBroadcaster)
{
addModerationElement = false;
2018-10-21 13:43:02 +02:00
}
else
{
bool hasUserType = this->tags.contains("user-type");
2018-10-21 13:43:02 +02:00
if (hasUserType)
{
QString userType = this->tags.value("user-type").toString();
2018-10-21 13:43:02 +02:00
if (userType == "mod")
{
if (!args.isStaffOrBroadcaster)
{
addModerationElement = false;
}
}
}
}
2018-10-21 13:43:02 +02:00
if (addModerationElement)
{
this->emplace<TwitchModerationElement>();
}
2017-04-12 17:46:44 +02:00
2018-01-23 23:28:06 +01:00
this->appendTwitchBadges();
2017-04-12 17:46:44 +02:00
2018-01-23 23:28:06 +01:00
this->appendChatterinoBadges();
this->appendFfzBadges();
2017-04-12 17:46:44 +02:00
this->appendUsername();
2017-04-12 17:46:44 +02:00
2018-08-02 14:23:27 +02:00
// QString bits;
2017-07-02 18:12:11 +02:00
auto iterator = this->tags.find("bits");
2018-10-21 13:43:02 +02:00
if (iterator != this->tags.end())
{
2018-08-02 14:23:27 +02:00
this->hasBits_ = true;
this->bitsLeft = iterator.value().toInt();
this->bits = iterator.value().toString();
2017-04-12 17:46:44 +02:00
}
// twitch emotes
std::vector<TwitchEmoteOccurence> twitchEmotes;
2017-04-12 17:46:44 +02:00
2017-07-02 18:12:11 +02:00
iterator = this->tags.find("emotes");
2018-10-21 13:43:02 +02:00
if (iterator != this->tags.end())
{
QStringList emoteString = iterator.value().toString().split('/');
std::vector<int> correctPositions;
2018-11-17 11:09:31 +01:00
for (int i = 0; i < this->originalMessage_.size(); ++i)
{
if (!this->originalMessage_.at(i).isLowSurrogate())
{
correctPositions.push_back(i);
}
}
2018-10-21 13:43:02 +02:00
for (QString emote : emoteString)
{
this->appendTwitchEmote(emote, twitchEmotes, correctPositions);
2017-04-12 17:46:44 +02:00
}
Squashed commit of the following: commit ea07bbef0be589cc5412bff0a25735ac713128e3 Merge: 0b36f436 5cfcf114 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 20:05:14 2018 +0200 Merge branch 'blacklist' into blacklistnew commit 5cfcf114b65ea7c0fca9654393ac2faa78610098 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 20:00:16 2018 +0200 rename second pattern to replacement commit f08cc4cf88c49140a282d3d29af5ad8e7179bb7c Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 19:52:30 2018 +0200 delete out commented code commit 1acb1278aa0109359e0e349ae240d10b34de9d34 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 19:52:03 2018 +0200 fix replacement with emotes issues commit 646268ab1883a955291f152029fa37f4416e681e Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 01:06:36 2018 +0200 fix build commit ad711b4c15ef0660c554af5ceea82397769a2313 Merge: e8e059f8 8bcc9c48 Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 00:52:38 2018 +0200 Merge branch 'master' of https://github.com/fourtf/chatterino2 into blacklist commit e8e059f8473271128086c5230cf1c40b235af380 Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 00:25:58 2018 +0200 add replaced emotes into twitchEmotes commit a63454f00de479cee1ab1eca18a8b4ab93e37d52 Merge: e7f2f397 63eaf3b9 Author: hemirt <hemirt@email.cz> Date: Sat Aug 11 22:38:16 2018 +0200 Merge branch 'master' of https://github.com/fourtf/chatterino2 into blacklist commit e7f2f397378d0582d989ff8fcbe83bcec41449a1 Author: hemirt <hemirt@email.cz> Date: Sat Aug 11 21:54:01 2018 +0200 emotedata commit f00d3da537ec14aebd9cbb84d63f7b16c196f199 Author: hemirt <hemirt@email.cz> Date: Sat Jul 28 19:53:55 2018 +0200 rename variables to fit better, emotes in capture groups from regex work commit 00c9fa080aeb8a4a187743d708ba139cbed5a849 Author: hemirt <hemirt@email.cz> Date: Mon Jul 9 19:53:53 2018 +0200 add case sensitivity checkbox and fix validity issues due to isValid that checked regex commit 4385fcd13fe6e011b91a3f4a29fd440d019fc499 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 21:09:14 2018 +0200 remove commented code commit 1834342f74c4fbff38b81fa2c2fcfd6c55adc0d5 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 21:03:13 2018 +0200 IgnorePhrase replacement also removes twitch emotes info about the matched and changed parts and shifts positions of other emotes from emote infos to the corresponding new position commit d3b6e294ed38fa8587c367c5da6f257641c28b86 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 16:21:33 2018 +0200 ignore phrases
2018-09-23 20:21:50 +02:00
}
// This runs through all ignored phrases and runs its replacements on this->originalMessage_
this->runIgnoreReplaces(twitchEmotes);
2017-04-12 17:46:44 +02:00
Squashed commit of the following: commit ea07bbef0be589cc5412bff0a25735ac713128e3 Merge: 0b36f436 5cfcf114 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 20:05:14 2018 +0200 Merge branch 'blacklist' into blacklistnew commit 5cfcf114b65ea7c0fca9654393ac2faa78610098 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 20:00:16 2018 +0200 rename second pattern to replacement commit f08cc4cf88c49140a282d3d29af5ad8e7179bb7c Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 19:52:30 2018 +0200 delete out commented code commit 1acb1278aa0109359e0e349ae240d10b34de9d34 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 19:52:03 2018 +0200 fix replacement with emotes issues commit 646268ab1883a955291f152029fa37f4416e681e Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 01:06:36 2018 +0200 fix build commit ad711b4c15ef0660c554af5ceea82397769a2313 Merge: e8e059f8 8bcc9c48 Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 00:52:38 2018 +0200 Merge branch 'master' of https://github.com/fourtf/chatterino2 into blacklist commit e8e059f8473271128086c5230cf1c40b235af380 Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 00:25:58 2018 +0200 add replaced emotes into twitchEmotes commit a63454f00de479cee1ab1eca18a8b4ab93e37d52 Merge: e7f2f397 63eaf3b9 Author: hemirt <hemirt@email.cz> Date: Sat Aug 11 22:38:16 2018 +0200 Merge branch 'master' of https://github.com/fourtf/chatterino2 into blacklist commit e7f2f397378d0582d989ff8fcbe83bcec41449a1 Author: hemirt <hemirt@email.cz> Date: Sat Aug 11 21:54:01 2018 +0200 emotedata commit f00d3da537ec14aebd9cbb84d63f7b16c196f199 Author: hemirt <hemirt@email.cz> Date: Sat Jul 28 19:53:55 2018 +0200 rename variables to fit better, emotes in capture groups from regex work commit 00c9fa080aeb8a4a187743d708ba139cbed5a849 Author: hemirt <hemirt@email.cz> Date: Mon Jul 9 19:53:53 2018 +0200 add case sensitivity checkbox and fix validity issues due to isValid that checked regex commit 4385fcd13fe6e011b91a3f4a29fd440d019fc499 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 21:09:14 2018 +0200 remove commented code commit 1834342f74c4fbff38b81fa2c2fcfd6c55adc0d5 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 21:03:13 2018 +0200 IgnorePhrase replacement also removes twitch emotes info about the matched and changed parts and shifts positions of other emotes from emote infos to the corresponding new position commit d3b6e294ed38fa8587c367c5da6f257641c28b86 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 16:21:33 2018 +0200 ignore phrases
2018-09-23 20:21:50 +02:00
std::sort(twitchEmotes.begin(), twitchEmotes.end(),
2018-09-30 13:37:39 +02:00
[](const auto &a, const auto &b) {
return a.start < b.start;
2018-09-30 13:37:39 +02:00
});
Squashed commit of the following: commit ea07bbef0be589cc5412bff0a25735ac713128e3 Merge: 0b36f436 5cfcf114 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 20:05:14 2018 +0200 Merge branch 'blacklist' into blacklistnew commit 5cfcf114b65ea7c0fca9654393ac2faa78610098 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 20:00:16 2018 +0200 rename second pattern to replacement commit f08cc4cf88c49140a282d3d29af5ad8e7179bb7c Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 19:52:30 2018 +0200 delete out commented code commit 1acb1278aa0109359e0e349ae240d10b34de9d34 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 19:52:03 2018 +0200 fix replacement with emotes issues commit 646268ab1883a955291f152029fa37f4416e681e Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 01:06:36 2018 +0200 fix build commit ad711b4c15ef0660c554af5ceea82397769a2313 Merge: e8e059f8 8bcc9c48 Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 00:52:38 2018 +0200 Merge branch 'master' of https://github.com/fourtf/chatterino2 into blacklist commit e8e059f8473271128086c5230cf1c40b235af380 Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 00:25:58 2018 +0200 add replaced emotes into twitchEmotes commit a63454f00de479cee1ab1eca18a8b4ab93e37d52 Merge: e7f2f397 63eaf3b9 Author: hemirt <hemirt@email.cz> Date: Sat Aug 11 22:38:16 2018 +0200 Merge branch 'master' of https://github.com/fourtf/chatterino2 into blacklist commit e7f2f397378d0582d989ff8fcbe83bcec41449a1 Author: hemirt <hemirt@email.cz> Date: Sat Aug 11 21:54:01 2018 +0200 emotedata commit f00d3da537ec14aebd9cbb84d63f7b16c196f199 Author: hemirt <hemirt@email.cz> Date: Sat Jul 28 19:53:55 2018 +0200 rename variables to fit better, emotes in capture groups from regex work commit 00c9fa080aeb8a4a187743d708ba139cbed5a849 Author: hemirt <hemirt@email.cz> Date: Mon Jul 9 19:53:53 2018 +0200 add case sensitivity checkbox and fix validity issues due to isValid that checked regex commit 4385fcd13fe6e011b91a3f4a29fd440d019fc499 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 21:09:14 2018 +0200 remove commented code commit 1834342f74c4fbff38b81fa2c2fcfd6c55adc0d5 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 21:03:13 2018 +0200 IgnorePhrase replacement also removes twitch emotes info about the matched and changed parts and shifts positions of other emotes from emote infos to the corresponding new position commit d3b6e294ed38fa8587c367c5da6f257641c28b86 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 16:21:33 2018 +0200 ignore phrases
2018-09-23 20:21:50 +02:00
twitchEmotes.erase(std::unique(twitchEmotes.begin(), twitchEmotes.end(),
[](const auto &first, const auto &second) {
return first.start == second.start;
Squashed commit of the following: commit ea07bbef0be589cc5412bff0a25735ac713128e3 Merge: 0b36f436 5cfcf114 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 20:05:14 2018 +0200 Merge branch 'blacklist' into blacklistnew commit 5cfcf114b65ea7c0fca9654393ac2faa78610098 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 20:00:16 2018 +0200 rename second pattern to replacement commit f08cc4cf88c49140a282d3d29af5ad8e7179bb7c Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 19:52:30 2018 +0200 delete out commented code commit 1acb1278aa0109359e0e349ae240d10b34de9d34 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 19:52:03 2018 +0200 fix replacement with emotes issues commit 646268ab1883a955291f152029fa37f4416e681e Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 01:06:36 2018 +0200 fix build commit ad711b4c15ef0660c554af5ceea82397769a2313 Merge: e8e059f8 8bcc9c48 Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 00:52:38 2018 +0200 Merge branch 'master' of https://github.com/fourtf/chatterino2 into blacklist commit e8e059f8473271128086c5230cf1c40b235af380 Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 00:25:58 2018 +0200 add replaced emotes into twitchEmotes commit a63454f00de479cee1ab1eca18a8b4ab93e37d52 Merge: e7f2f397 63eaf3b9 Author: hemirt <hemirt@email.cz> Date: Sat Aug 11 22:38:16 2018 +0200 Merge branch 'master' of https://github.com/fourtf/chatterino2 into blacklist commit e7f2f397378d0582d989ff8fcbe83bcec41449a1 Author: hemirt <hemirt@email.cz> Date: Sat Aug 11 21:54:01 2018 +0200 emotedata commit f00d3da537ec14aebd9cbb84d63f7b16c196f199 Author: hemirt <hemirt@email.cz> Date: Sat Jul 28 19:53:55 2018 +0200 rename variables to fit better, emotes in capture groups from regex work commit 00c9fa080aeb8a4a187743d708ba139cbed5a849 Author: hemirt <hemirt@email.cz> Date: Mon Jul 9 19:53:53 2018 +0200 add case sensitivity checkbox and fix validity issues due to isValid that checked regex commit 4385fcd13fe6e011b91a3f4a29fd440d019fc499 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 21:09:14 2018 +0200 remove commented code commit 1834342f74c4fbff38b81fa2c2fcfd6c55adc0d5 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 21:03:13 2018 +0200 IgnorePhrase replacement also removes twitch emotes info about the matched and changed parts and shifts positions of other emotes from emote infos to the corresponding new position commit d3b6e294ed38fa8587c367c5da6f257641c28b86 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 16:21:33 2018 +0200 ignore phrases
2018-09-23 20:21:50 +02:00
}),
twitchEmotes.end());
2017-04-12 17:46:44 +02:00
// words
2018-07-06 19:23:47 +02:00
QStringList splits = this->originalMessage_.split(' ');
2017-04-12 17:46:44 +02:00
2018-08-02 14:23:27 +02:00
this->addWords(splits, twitchEmotes);
2017-04-12 17:46:44 +02:00
this->message().messageText = this->originalMessage_;
2019-05-30 17:23:01 +02:00
this->message().searchText = this->message().localizedName + " " +
this->userName + ": " + this->originalMessage_;
2018-08-02 14:23:27 +02:00
// highlights
this->parseHighlights();
// highlighting incoming whispers if requested per setting
if (this->args.isReceivedWhisper && getSettings()->highlightInlineWhispers)
{
this->message().flags.set(MessageFlag::HighlightedWhisper, true);
this->message().highlightColor =
ColorProvider::instance().color(ColorType::Whisper);
}
2018-08-07 01:35:24 +02:00
return this->release();
2018-08-02 14:23:27 +02:00
}
2017-08-12 12:07:53 +02:00
bool doesWordContainATwitchEmote(
int cursor, const QString &word,
const std::vector<TwitchEmoteOccurence> &twitchEmotes,
std::vector<TwitchEmoteOccurence>::const_iterator &currentTwitchEmoteIt)
{
if (currentTwitchEmoteIt == twitchEmotes.end())
{
// No emote to add!
return false;
}
const auto &currentTwitchEmote = *currentTwitchEmoteIt;
auto wordEnd = cursor + word.length();
// Check if this emote fits within the word boundaries
if (currentTwitchEmote.start < cursor || currentTwitchEmote.end > wordEnd)
{
// this emote does not fit xd
return false;
}
return true;
}
2018-08-06 21:17:03 +02:00
void TwitchMessageBuilder::addWords(
2018-09-30 13:37:39 +02:00
const QStringList &words,
const std::vector<TwitchEmoteOccurence> &twitchEmotes)
2018-08-02 14:23:27 +02:00
{
// cursor currently indicates what character index we're currently operating in the full list of words
int cursor = 0;
auto currentTwitchEmoteIt = twitchEmotes.begin();
2018-08-02 14:23:27 +02:00
for (auto word : words)
2018-10-21 13:43:02 +02:00
{
if (word.isEmpty())
{
cursor++;
continue;
}
while (doesWordContainATwitchEmote(cursor, word, twitchEmotes,
currentTwitchEmoteIt))
2018-10-21 13:43:02 +02:00
{
auto wordEnd = cursor + word.length();
const auto &currentTwitchEmote = *currentTwitchEmoteIt;
if (currentTwitchEmote.start == cursor)
2018-10-21 13:43:02 +02:00
{
// This emote exists right at the start of the word!
this->emplace<EmoteElement>(currentTwitchEmote.ptr,
MessageElementFlag::TwitchEmote);
auto len = currentTwitchEmote.name.string.length();
cursor += len;
word = word.mid(len);
2017-04-12 17:46:44 +02:00
++currentTwitchEmoteIt;
if (word.isEmpty())
{
// space
cursor += 1;
break;
}
else
{
this->message().elements.back()->setTrailingSpace(false);
}
2017-04-12 17:46:44 +02:00
continue;
}
// Emote is not at the start
// 1. Add text before the emote
QString preText = word.left(currentTwitchEmote.start - cursor);
for (auto &variant : getApp()->emotes->emojis.parse(preText))
{
boost::apply_visitor(
[&](auto &&arg) {
this->addTextOrEmoji(arg);
},
variant);
}
cursor += preText.size();
word = word.mid(preText.size());
}
if (word.isEmpty())
{
continue;
2017-04-12 17:46:44 +02:00
}
// split words
2018-10-21 13:43:02 +02:00
for (auto &variant : getApp()->emotes->emojis.parse(word))
{
boost::apply_visitor(
[&](auto &&arg) {
this->addTextOrEmoji(arg);
},
variant);
2017-04-12 17:46:44 +02:00
}
cursor += word.size() + 1;
2017-04-12 17:46:44 +02:00
}
2018-08-02 14:23:27 +02:00
}
2017-04-12 17:46:44 +02:00
2018-08-02 14:23:27 +02:00
void TwitchMessageBuilder::addTextOrEmoji(EmotePtr emote)
{
return SharedMessageBuilder::addTextOrEmoji(emote);
2018-08-02 14:23:27 +02:00
}
2017-04-12 17:46:44 +02:00
2018-08-02 14:23:27 +02:00
void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
{
auto string = QString(string_);
2018-07-04 12:22:01 +02:00
2018-10-21 13:43:02 +02:00
if (this->hasBits_ && this->tryParseCheermote(string))
{
2018-08-02 14:23:27 +02:00
// This string was parsed as a cheermote
return;
}
2017-08-12 12:09:26 +02:00
2018-08-02 14:23:27 +02:00
// TODO: Implement ignored emotes
// Format of ignored emotes:
// Emote name: "forsenPuke" - if string in ignoredEmotes
// Will match emote regardless of source (i.e. bttv, ffz)
// Emote source + name: "bttv:nyanPls"
2018-10-21 13:43:02 +02:00
if (this->tryAppendEmote({string}))
{
2018-08-02 14:23:27 +02:00
// Successfully appended an emote
return;
}
2017-08-05 18:44:14 +02:00
2018-08-02 14:23:27 +02:00
// Actually just text
auto linkString = this->matchLink(string);
2018-09-30 13:37:39 +02:00
auto textColor = this->action_ ? MessageColor(this->usernameColor_)
: MessageColor(MessageColor::Text);
2018-08-02 14:23:27 +02:00
2020-07-18 16:03:51 +02:00
if (!linkString.isEmpty())
{
this->addLink(string, linkString);
return;
}
if (string.startsWith('@'))
2018-10-21 13:43:02 +02:00
{
2020-07-18 16:03:51 +02:00
auto match = mentionRegex.match(string);
// Only treat as @mention if valid username
if (match.hasMatch())
2018-10-21 13:43:02 +02:00
{
2020-07-18 16:03:51 +02:00
QString username = match.captured(1);
auto originalTextColor = textColor;
if (this->twitchChannel != nullptr && getSettings()->colorUsernames)
{
if (auto userColor =
this->twitchChannel->getUserColor(username);
userColor.isValid())
{
textColor = userColor;
}
}
auto prefixedUsername = '@' + username;
this->emplace<TextElement>(prefixedUsername,
MessageElementFlag::BoldUsername,
2020-07-18 16:03:51 +02:00
textColor, FontStyle::ChatMediumBold)
->setLink({Link::UserInfo, username})
->setTrailingSpace(false);
this->emplace<TextElement>(prefixedUsername,
MessageElementFlag::NonBoldUsername,
textColor)
->setLink({Link::UserInfo, username})
->setTrailingSpace(false);
this->emplace<TextElement>(string.remove(prefixedUsername),
MessageElementFlag::Text,
originalTextColor);
2020-07-18 16:03:51 +02:00
return;
2017-04-12 17:46:44 +02:00
}
2018-10-21 13:43:02 +02:00
}
2020-07-18 16:03:51 +02:00
if (this->twitchChannel != nullptr && getSettings()->findAllUsernames)
2018-10-21 13:43:02 +02:00
{
auto match = allUsernamesMentionRegex.match(string);
QString username = match.captured(1);
if (match.hasMatch() &&
this->twitchChannel->accessChatters()->contains(username))
2020-07-18 16:03:51 +02:00
{
auto originalTextColor = textColor;
if (getSettings()->colorUsernames)
{
if (auto userColor =
this->twitchChannel->getUserColor(username);
userColor.isValid())
{
textColor = userColor;
}
}
this->emplace<TextElement>(username,
MessageElementFlag::BoldUsername,
2020-07-18 16:03:51 +02:00
textColor, FontStyle::ChatMediumBold)
->setLink({Link::UserInfo, username})
->setTrailingSpace(false);
2020-07-18 16:03:51 +02:00
this->emplace<TextElement>(
username, MessageElementFlag::NonBoldUsername, textColor)
->setLink({Link::UserInfo, username})
->setTrailingSpace(false);
this->emplace<TextElement>(string.remove(username),
MessageElementFlag::Text,
originalTextColor);
2020-07-18 16:03:51 +02:00
return;
}
2017-04-12 17:46:44 +02:00
}
2020-07-18 16:03:51 +02:00
this->emplace<TextElement>(string, MessageElementFlag::Text, textColor);
2017-07-02 18:12:11 +02:00
}
void TwitchMessageBuilder::parseMessageID()
{
auto iterator = this->tags.find("id");
2018-10-21 13:43:02 +02:00
if (iterator != this->tags.end())
{
this->message().id = iterator.value().toString();
2017-07-02 18:12:11 +02:00
}
2017-04-12 17:46:44 +02:00
}
2017-07-02 18:12:11 +02:00
void TwitchMessageBuilder::parseRoomID()
{
2018-10-21 13:43:02 +02:00
if (this->twitchChannel == nullptr)
{
2017-12-31 00:50:07 +01:00
return;
}
2017-07-02 18:12:11 +02:00
auto iterator = this->tags.find("room-id");
2018-10-21 13:43:02 +02:00
if (iterator != std::end(this->tags))
{
2018-07-06 19:23:47 +02:00
this->roomID_ = iterator.value().toString();
2017-07-02 18:12:11 +02:00
2018-10-21 13:43:02 +02:00
if (this->twitchChannel->roomId().isEmpty())
{
2018-07-15 20:28:54 +02:00
this->twitchChannel->setRoomId(this->roomID_);
2017-07-02 18:12:11 +02:00
}
}
}
2019-08-11 22:18:01 +02:00
void TwitchMessageBuilder::parseUsernameColor()
2017-07-02 18:12:11 +02:00
{
const auto iterator = this->tags.find("color");
2019-08-11 22:18:01 +02:00
if (iterator != this->tags.end())
{
2019-08-11 22:18:01 +02:00
if (const auto color = iterator.value().toString(); !color.isEmpty())
{
this->usernameColor_ = QColor(color);
this->message().usernameColor = this->usernameColor_;
2019-08-11 22:18:01 +02:00
return;
}
}
2019-08-11 22:18:01 +02:00
if (getSettings()->colorizeNicknames && this->tags.contains("user-id"))
2018-10-21 13:43:02 +02:00
{
this->usernameColor_ = getRandomColor(this->tags.value("user-id"));
this->message().usernameColor = this->usernameColor_;
2017-07-02 18:12:11 +02:00
}
2019-08-11 22:18:01 +02:00
}
void TwitchMessageBuilder::parseUsername()
{
SharedMessageBuilder::parseUsername();
2017-07-02 18:12:11 +02:00
2018-10-21 13:43:02 +02:00
if (this->userName.isEmpty() || this->args.trimSubscriberUsername)
{
2017-07-02 18:12:11 +02:00
this->userName = this->tags.value(QLatin1String("login")).toString();
}
2017-12-17 17:48:46 +01:00
2018-06-04 12:23:23 +02:00
// display name
// auto displayNameVariant = this->tags.value("display-name");
// if (displayNameVariant.isValid()) {
2018-08-06 21:17:03 +02:00
// this->userName = displayNameVariant.toString() + " (" +
// this->userName + ")";
2018-06-04 12:23:23 +02:00
// }
2018-08-07 01:35:24 +02:00
this->message().loginName = this->userName;
if (this->twitchChannel != nullptr)
{
this->twitchChannel->setUserColor(this->userName, this->usernameColor_);
}
// Update current user color if this is our message
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (this->ircMessage->nick() == currentUser->getUserName())
{
currentUser->setColor(this->usernameColor_);
}
}
2017-07-02 18:12:11 +02:00
void TwitchMessageBuilder::appendUsername()
{
auto app = getApp();
QString username = this->userName;
2018-08-07 01:35:24 +02:00
this->message().loginName = username;
QString localizedName;
2017-07-02 18:12:11 +02:00
auto iterator = this->tags.find("display-name");
2018-10-21 13:43:02 +02:00
if (iterator != this->tags.end())
{
2018-09-30 13:37:39 +02:00
QString displayName =
parseTagString(iterator.value().toString()).trimmed();
2018-09-30 13:37:39 +02:00
if (QString::compare(displayName, this->userName,
2018-10-21 13:43:02 +02:00
Qt::CaseInsensitive) == 0)
{
username = displayName;
2018-08-07 01:35:24 +02:00
this->message().displayName = displayName;
2018-10-21 13:43:02 +02:00
}
else
{
localizedName = displayName;
2018-08-07 01:35:24 +02:00
this->message().displayName = username;
this->message().localizedName = displayName;
}
2017-07-02 18:12:11 +02:00
}
bool hasLocalizedName = !localizedName.isEmpty();
// The full string that will be rendered in the chat widget
QString usernameText;
pajlada::Settings::Setting<int> usernameDisplayMode(
2018-09-30 13:37:39 +02:00
"/appearance/messages/usernameDisplayMode",
UsernameDisplayMode::UsernameAndLocalizedName);
2018-10-21 13:43:02 +02:00
switch (usernameDisplayMode.getValue())
{
2019-09-26 00:51:05 +02:00
case UsernameDisplayMode::Username: {
usernameText = username;
2018-10-21 13:43:02 +02:00
}
break;
2019-09-26 00:51:05 +02:00
case UsernameDisplayMode::LocalizedName: {
2018-10-21 13:43:02 +02:00
if (hasLocalizedName)
{
usernameText = localizedName;
2018-10-21 13:43:02 +02:00
}
else
{
usernameText = username;
}
2018-10-21 13:43:02 +02:00
}
break;
default:
2019-09-26 00:51:05 +02:00
case UsernameDisplayMode::UsernameAndLocalizedName: {
2018-10-21 13:43:02 +02:00
if (hasLocalizedName)
{
usernameText = username + "(" + localizedName + ")";
2018-10-21 13:43:02 +02:00
}
else
{
usernameText = username;
}
2018-10-21 13:43:02 +02:00
}
break;
}
2017-07-02 18:12:11 +02:00
2018-10-21 13:43:02 +02:00
if (this->args.isSentWhisper)
{
2017-07-02 18:12:11 +02:00
// TODO(pajlada): Re-implement
2018-08-06 21:17:03 +02:00
// userDisplayString +=
// IrcManager::instance().getUser().getUserName();
2018-10-21 13:43:02 +02:00
}
else if (this->args.isReceivedWhisper)
{
// Sender username
2018-09-30 13:37:39 +02:00
this->emplace<TextElement>(usernameText, MessageElementFlag::Username,
this->usernameColor_,
2018-05-23 04:22:17 +02:00
FontStyle::ChatMediumBold)
->setLink({Link::UserWhisper, this->message().displayName});
2018-05-26 20:26:25 +02:00
auto currentUser = app->accounts->twitch.getCurrent();
// Separator
2018-08-16 00:16:33 +02:00
this->emplace<TextElement>("->", MessageElementFlag::Username,
2018-09-30 13:37:39 +02:00
app->themes->messages.textColors.system,
FontStyle::ChatMedium);
QColor selfColor = currentUser->color();
2018-10-21 13:43:02 +02:00
if (!selfColor.isValid())
{
selfColor = app->themes->messages.textColors.system;
}
2017-07-02 18:12:11 +02:00
// Your own username
2018-09-30 13:37:39 +02:00
this->emplace<TextElement>(currentUser->getUserName() + ":",
MessageElementFlag::Username, selfColor,
FontStyle::ChatMediumBold);
2018-10-21 13:43:02 +02:00
}
else
{
if (!this->action_)
{
usernameText += ":";
}
2017-07-02 18:12:11 +02:00
2018-09-30 13:37:39 +02:00
this->emplace<TextElement>(usernameText, MessageElementFlag::Username,
this->usernameColor_,
2018-05-23 04:22:17 +02:00
FontStyle::ChatMediumBold)
->setLink({Link::UserInfo, this->message().displayName});
2017-07-02 18:12:11 +02:00
}
}
void TwitchMessageBuilder::runIgnoreReplaces(
std::vector<TwitchEmoteOccurence> &twitchEmotes)
{
auto phrases = getCSettings().ignoredMessages.readOnly();
auto removeEmotesInRange = [](int pos, int len,
auto &twitchEmotes) mutable {
auto it = std::partition(
twitchEmotes.begin(), twitchEmotes.end(),
[pos, len](const auto &item) {
return !((item.start >= pos) && item.start < (pos + len));
});
for (auto copy = it; copy != twitchEmotes.end(); ++copy)
{
if ((*copy).ptr == nullptr)
{
qCDebug(chatterinoTwitch)
<< "remem nullptr" << (*copy).name.string;
}
}
std::vector<TwitchEmoteOccurence> v(it, twitchEmotes.end());
twitchEmotes.erase(it, twitchEmotes.end());
return v;
};
auto shiftIndicesAfter = [&twitchEmotes](int pos, int by) mutable {
for (auto &item : twitchEmotes)
{
auto &index = item.start;
if (index >= pos)
{
index += by;
item.end += by;
}
}
};
auto addReplEmotes = [&twitchEmotes](const IgnorePhrase &phrase,
const QStringRef &midrepl,
int startIndex) mutable {
if (!phrase.containsEmote())
{
return;
}
QVector<QStringRef> words = midrepl.split(' ');
int pos = 0;
for (const auto &word : words)
{
for (const auto &emote : phrase.getEmotes())
{
if (word == emote.first.string)
{
if (emote.second == nullptr)
{
qCDebug(chatterinoTwitch)
<< "emote null" << emote.first.string;
}
twitchEmotes.push_back(TwitchEmoteOccurence{
startIndex + pos,
startIndex + pos + emote.first.string.length(),
emote.second,
emote.first,
});
}
}
pos += word.length() + 1;
}
};
2020-02-23 17:10:49 +01:00
for (const auto &phrase : *phrases)
{
if (phrase.isBlock())
{
continue;
}
const auto &pattern = phrase.getPattern();
if (pattern.isEmpty())
{
continue;
}
if (phrase.isRegex())
{
const auto &regex = phrase.getRegex();
if (!regex.isValid())
{
continue;
}
QRegularExpressionMatch match;
int from = 0;
while ((from = this->originalMessage_.indexOf(regex, from,
&match)) != -1)
{
int len = match.capturedLength();
auto vret = removeEmotesInRange(from, len, twitchEmotes);
auto mid = this->originalMessage_.mid(from, len);
mid.replace(regex, phrase.getReplace());
int midsize = mid.size();
this->originalMessage_.replace(from, len, mid);
int pos1 = from;
while (pos1 > 0)
{
if (this->originalMessage_[pos1 - 1] == ' ')
{
break;
}
--pos1;
}
int pos2 = from + midsize;
while (pos2 < this->originalMessage_.length())
{
if (this->originalMessage_[pos2] == ' ')
{
break;
}
++pos2;
}
shiftIndicesAfter(from + len, midsize - len);
auto midExtendedRef =
this->originalMessage_.midRef(pos1, pos2 - pos1);
for (auto &tup : vret)
{
if (tup.ptr == nullptr)
{
qCDebug(chatterinoTwitch)
<< "v nullptr" << tup.name.string;
continue;
}
QRegularExpression emoteregex(
"\\b" + tup.name.string + "\\b",
QRegularExpression::UseUnicodePropertiesOption);
auto _match = emoteregex.match(midExtendedRef);
if (_match.hasMatch())
{
int last = _match.lastCapturedIndex();
for (int i = 0; i <= last; ++i)
{
tup.start = from + _match.capturedStart();
twitchEmotes.push_back(std::move(tup));
}
}
}
addReplEmotes(phrase, midExtendedRef, pos1);
from += midsize;
}
}
else
{
int from = 0;
while ((from = this->originalMessage_.indexOf(
pattern, from, phrase.caseSensitivity())) != -1)
{
int len = pattern.size();
auto vret = removeEmotesInRange(from, len, twitchEmotes);
auto replace = phrase.getReplace();
int replacesize = replace.size();
this->originalMessage_.replace(from, len, replace);
int pos1 = from;
while (pos1 > 0)
{
if (this->originalMessage_[pos1 - 1] == ' ')
{
break;
}
--pos1;
}
int pos2 = from + replacesize;
while (pos2 < this->originalMessage_.length())
{
if (this->originalMessage_[pos2] == ' ')
{
break;
}
++pos2;
}
shiftIndicesAfter(from + len, replacesize - len);
auto midExtendedRef =
this->originalMessage_.midRef(pos1, pos2 - pos1);
for (auto &tup : vret)
{
if (tup.ptr == nullptr)
{
qCDebug(chatterinoTwitch)
<< "v nullptr" << tup.name.string;
continue;
}
QRegularExpression emoteregex(
"\\b" + tup.name.string + "\\b",
QRegularExpression::UseUnicodePropertiesOption);
auto match = emoteregex.match(midExtendedRef);
if (match.hasMatch())
{
int last = match.lastCapturedIndex();
for (int i = 0; i <= last; ++i)
{
tup.start = from + match.capturedStart();
twitchEmotes.push_back(std::move(tup));
}
}
}
addReplEmotes(phrase, midExtendedRef, pos1);
from += replacesize;
}
}
}
}
2018-09-30 13:37:39 +02:00
void TwitchMessageBuilder::appendTwitchEmote(
const QString &emote, std::vector<TwitchEmoteOccurence> &vec,
2018-11-17 11:09:31 +01:00
std::vector<int> &correctPositions)
{
auto app = getApp();
2018-10-21 13:43:02 +02:00
if (!emote.contains(':'))
{
return;
}
2018-08-02 14:23:27 +02:00
auto parameters = emote.split(':');
2018-10-21 13:43:02 +02:00
if (parameters.length() < 2)
{
return;
}
2018-08-02 14:23:27 +02:00
auto id = EmoteId{parameters.at(0)};
2018-08-02 14:23:27 +02:00
auto occurences = parameters.at(1).split(',');
2018-10-21 13:43:02 +02:00
for (QString occurence : occurences)
{
2018-08-02 14:23:27 +02:00
auto coords = occurence.split('-');
2018-10-21 13:43:02 +02:00
if (coords.length() < 2)
{
return;
}
2019-07-27 12:48:55 +02:00
auto start = correctPositions[coords.at(0).toUInt()];
auto end = correctPositions[coords.at(1).toUInt()];
2018-10-21 13:43:02 +02:00
if (start >= end || start < 0 || end > this->originalMessage_.length())
{
return;
}
2018-09-30 13:37:39 +02:00
auto name =
EmoteName{this->originalMessage_.mid(start, end - start + 1)};
TwitchEmoteOccurence emoteOccurence{
start, end, app->emotes->twitch.getOrCreateEmote(id, name), name};
if (emoteOccurence.ptr == nullptr)
2018-10-21 13:43:02 +02:00
{
qCDebug(chatterinoTwitch)
<< "nullptr" << emoteOccurence.name.string;
Squashed commit of the following: commit ea07bbef0be589cc5412bff0a25735ac713128e3 Merge: 0b36f436 5cfcf114 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 20:05:14 2018 +0200 Merge branch 'blacklist' into blacklistnew commit 5cfcf114b65ea7c0fca9654393ac2faa78610098 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 20:00:16 2018 +0200 rename second pattern to replacement commit f08cc4cf88c49140a282d3d29af5ad8e7179bb7c Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 19:52:30 2018 +0200 delete out commented code commit 1acb1278aa0109359e0e349ae240d10b34de9d34 Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 19:52:03 2018 +0200 fix replacement with emotes issues commit 646268ab1883a955291f152029fa37f4416e681e Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 01:06:36 2018 +0200 fix build commit ad711b4c15ef0660c554af5ceea82397769a2313 Merge: e8e059f8 8bcc9c48 Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 00:52:38 2018 +0200 Merge branch 'master' of https://github.com/fourtf/chatterino2 into blacklist commit e8e059f8473271128086c5230cf1c40b235af380 Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 00:25:58 2018 +0200 add replaced emotes into twitchEmotes commit a63454f00de479cee1ab1eca18a8b4ab93e37d52 Merge: e7f2f397 63eaf3b9 Author: hemirt <hemirt@email.cz> Date: Sat Aug 11 22:38:16 2018 +0200 Merge branch 'master' of https://github.com/fourtf/chatterino2 into blacklist commit e7f2f397378d0582d989ff8fcbe83bcec41449a1 Author: hemirt <hemirt@email.cz> Date: Sat Aug 11 21:54:01 2018 +0200 emotedata commit f00d3da537ec14aebd9cbb84d63f7b16c196f199 Author: hemirt <hemirt@email.cz> Date: Sat Jul 28 19:53:55 2018 +0200 rename variables to fit better, emotes in capture groups from regex work commit 00c9fa080aeb8a4a187743d708ba139cbed5a849 Author: hemirt <hemirt@email.cz> Date: Mon Jul 9 19:53:53 2018 +0200 add case sensitivity checkbox and fix validity issues due to isValid that checked regex commit 4385fcd13fe6e011b91a3f4a29fd440d019fc499 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 21:09:14 2018 +0200 remove commented code commit 1834342f74c4fbff38b81fa2c2fcfd6c55adc0d5 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 21:03:13 2018 +0200 IgnorePhrase replacement also removes twitch emotes info about the matched and changed parts and shifts positions of other emotes from emote infos to the corresponding new position commit d3b6e294ed38fa8587c367c5da6f257641c28b86 Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 16:21:33 2018 +0200 ignore phrases
2018-09-23 20:21:50 +02:00
}
vec.push_back(std::move(emoteOccurence));
}
}
2018-08-02 14:23:27 +02:00
Outcome TwitchMessageBuilder::tryAppendEmote(const EmoteName &name)
2017-07-23 09:53:50 +02:00
{
2019-06-22 14:16:21 +02:00
auto *app = getApp();
const auto &globalBttvEmotes = app->twitch.server->getBttvEmotes();
const auto &globalFfzEmotes = app->twitch.server->getFfzEmotes();
2018-08-07 07:55:31 +02:00
auto flags = MessageElementFlags();
2018-08-02 14:23:27 +02:00
auto emote = boost::optional<EmotePtr>{};
2019-06-22 14:16:21 +02:00
// Emote order:
// - FrankerFaceZ Channel
// - BetterTTV Channel
// - FrankerFaceZ Global
// - BetterTTV Global
if (this->twitchChannel && (emote = this->twitchChannel->ffzEmote(name)))
2018-10-21 13:43:02 +02:00
{
flags = MessageElementFlag::FfzEmote;
2018-10-21 13:43:02 +02:00
}
2019-06-22 14:16:21 +02:00
else if (this->twitchChannel &&
(emote = this->twitchChannel->bttvEmote(name)))
2018-10-21 13:43:02 +02:00
{
2018-08-07 07:55:31 +02:00
flags = MessageElementFlag::BttvEmote;
2018-10-21 13:43:02 +02:00
}
2019-06-22 14:16:21 +02:00
else if ((emote = globalFfzEmotes.emote(name)))
2018-10-21 13:43:02 +02:00
{
2018-08-07 07:55:31 +02:00
flags = MessageElementFlag::FfzEmote;
2018-10-21 13:43:02 +02:00
}
2019-06-22 14:16:21 +02:00
else if ((emote = globalBttvEmotes.emote(name)))
2018-10-21 13:43:02 +02:00
{
flags = MessageElementFlag::BttvEmote;
if (zeroWidthEmotes.contains(name.string))
{
flags.set(MessageElementFlag::ZeroWidthEmote);
}
2017-07-23 09:53:50 +02:00
}
2018-10-21 13:43:02 +02:00
if (emote)
{
2018-08-02 14:23:27 +02:00
this->emplace<EmoteElement>(emote.get(), flags);
return Success;
}
return Failure;
2017-07-23 09:53:50 +02:00
}
2019-12-01 13:32:41 +01:00
boost::optional<EmotePtr> TwitchMessageBuilder::getTwitchBadge(
const Badge &badge)
{
if (auto channelBadge =
this->twitchChannel->twitchBadge(badge.key_, badge.value_))
{
return channelBadge;
}
if (auto globalBadge =
TwitchBadges::instance()->badge(badge.key_, badge.value_))
2019-12-01 13:32:41 +01:00
{
return globalBadge;
}
return boost::none;
}
2018-01-23 23:28:06 +01:00
void TwitchMessageBuilder::appendTwitchBadges()
{
2018-10-21 13:43:02 +02:00
if (this->twitchChannel == nullptr)
{
return;
}
2019-12-01 13:32:41 +01:00
auto badgeInfos = parseBadgeInfos(this->tags);
auto badges = parseBadges(this->tags);
2017-07-02 18:12:11 +02:00
2019-12-01 13:32:41 +01:00
for (const auto &badge : badges)
2018-10-21 13:43:02 +02:00
{
2019-12-01 13:32:41 +01:00
auto badgeEmote = this->getTwitchBadge(badge);
if (!badgeEmote)
2018-10-21 13:43:02 +02:00
{
2019-12-01 13:32:41 +01:00
continue;
2018-10-21 13:43:02 +02:00
}
2019-12-01 13:32:41 +01:00
auto tooltip = (*badgeEmote)->tooltip.string;
if (badge.key_ == "bits")
2018-10-21 13:43:02 +02:00
{
2019-12-01 13:32:41 +01:00
const auto &cheerAmount = badge.value_;
tooltip = QString("Twitch cheer %0").arg(cheerAmount);
2018-10-21 13:43:02 +02:00
}
else if (badge.key_ == "moderator" &&
getSettings()->useCustomFfzModeratorBadges)
2018-10-21 13:43:02 +02:00
{
2018-10-25 21:53:03 +02:00
if (auto customModBadge = this->twitchChannel->ffzCustomModBadge())
{
this->emplace<ModBadgeElement>(
2018-10-25 21:53:03 +02:00
customModBadge.get(),
MessageElementFlag::BadgeChannelAuthority)
->setTooltip((*customModBadge)->tooltip.string);
2019-12-01 13:32:41 +01:00
// early out, since we have to add a custom badge element here
2018-10-25 21:53:03 +02:00
continue;
}
}
else if (badge.key_ == "vip" && getSettings()->useCustomFfzVipBadges)
{
if (auto customVipBadge = this->twitchChannel->ffzCustomVipBadge())
{
this->emplace<VipBadgeElement>(
customVipBadge.get(),
MessageElementFlag::BadgeChannelAuthority)
->setTooltip((*customVipBadge)->tooltip.string);
// early out, since we have to add a custom badge element here
continue;
}
}
2019-12-01 13:32:41 +01:00
else if (badge.flag_ == MessageElementFlag::BadgeSubscription)
2018-10-21 13:43:02 +02:00
{
2019-12-01 13:32:41 +01:00
auto badgeInfoIt = badgeInfos.find(badge.key_);
if (badgeInfoIt != badgeInfos.end())
2018-10-21 13:43:02 +02:00
{
// badge.value_ is 4 chars long if user is subbed on higher tier
// (tier + amount of months with leading zero if less than 100)
// e.g. 3054 - tier 3 4,5-year sub. 2108 - tier 2 9-year sub
const auto &subTier =
badge.value_.length() > 3 ? badge.value_.front() : '1';
2019-12-01 13:32:41 +01:00
const auto &subMonths = badgeInfoIt->second;
tooltip +=
QString(" (%1%2 months)")
.arg(subTier != '1' ? QString("Tier %1, ").arg(subTier)
: "")
.arg(subMonths);
}
2018-10-21 13:43:02 +02:00
}
2018-08-14 17:45:17 +02:00
2019-12-01 13:32:41 +01:00
this->emplace<BadgeElement>(badgeEmote.get(), badge.flag_)
->setTooltip(tooltip);
}
this->message().badges = badges;
this->message().badgeInfos = badgeInfos;
2019-12-01 13:32:41 +01:00
}
2018-01-23 23:28:06 +01:00
void TwitchMessageBuilder::appendChatterinoBadges()
2017-08-12 13:20:52 +02:00
{
2019-08-23 16:52:04 +02:00
if (auto badge = getApp()->chatterinoBadges->getBadge({this->userId_}))
2018-10-21 13:43:02 +02:00
{
2019-08-23 16:52:04 +02:00
this->emplace<BadgeElement>(*badge,
2018-09-30 13:37:39 +02:00
MessageElementFlag::BadgeChatterino);
}
2017-08-12 13:20:52 +02:00
}
void TwitchMessageBuilder::appendFfzBadges()
{
if (auto badge = getApp()->ffzBadges->getBadge({this->userId_}))
{
if (auto color = getApp()->ffzBadges->getBadgeColor({this->userId_}))
{
this->emplace<FfzBadgeElement>(*badge, MessageElementFlag::BadgeFfz,
color.get());
}
}
}
Outcome TwitchMessageBuilder::tryParseCheermote(const QString &string)
{
if (this->bitsLeft == 0)
{
return Failure;
}
auto cheerOpt = this->twitchChannel->cheerEmote(string);
if (!cheerOpt)
{
return Failure;
}
2019-09-08 18:01:38 +02:00
auto &cheerEmote = *cheerOpt;
auto match = cheerEmote.regex.match(string);
if (!match.hasMatch())
{
return Failure;
}
int cheerValue = match.captured(1).toInt();
2019-12-21 10:36:46 +01:00
if (getSettings()->stackBits)
{
if (this->bitsStacked)
{
return Success;
}
if (cheerEmote.staticEmote)
{
this->emplace<EmoteElement>(cheerEmote.staticEmote,
MessageElementFlag::BitsStatic);
}
if (cheerEmote.animatedEmote)
{
this->emplace<EmoteElement>(cheerEmote.animatedEmote,
MessageElementFlag::BitsAnimated);
}
if (cheerEmote.color != QColor())
{
this->emplace<TextElement>(QString::number(this->bitsLeft),
MessageElementFlag::BitsAmount,
cheerEmote.color);
}
this->bitsStacked = true;
return Success;
}
if (this->bitsLeft >= cheerValue)
{
this->bitsLeft -= cheerValue;
}
else
{
QString newString = string;
newString.chop(QString::number(cheerValue).length());
newString += QString::number(cheerValue - this->bitsLeft);
return tryParseCheermote(newString);
}
2019-09-08 18:01:38 +02:00
if (cheerEmote.staticEmote)
{
2019-09-08 18:01:38 +02:00
this->emplace<EmoteElement>(cheerEmote.staticEmote,
MessageElementFlag::BitsStatic);
}
2019-09-08 18:01:38 +02:00
if (cheerEmote.animatedEmote)
{
2019-09-08 18:01:38 +02:00
this->emplace<EmoteElement>(cheerEmote.animatedEmote,
MessageElementFlag::BitsAnimated);
}
2019-09-08 18:01:38 +02:00
if (cheerEmote.color != QColor())
{
this->emplace<TextElement>(match.captured(1),
MessageElementFlag::BitsAmount,
2019-09-08 18:01:38 +02:00
cheerEmote.color);
}
return Success;
}
2019-12-01 13:32:41 +01:00
void TwitchMessageBuilder::appendChannelPointRewardMessage(
const ChannelPointReward &reward, MessageBuilder *builder)
{
builder->emplace<TimestampElement>();
QString redeemed = "Redeemed";
if (!reward.isUserInputRequired)
{
builder->emplace<TextElement>(
reward.user.login, MessageElementFlag::ChannelPointReward,
MessageColor::Text, FontStyle::ChatMediumBold);
redeemed = "redeemed";
}
builder->emplace<TextElement>(redeemed,
MessageElementFlag::ChannelPointReward);
builder->emplace<TextElement>(
reward.title, MessageElementFlag::ChannelPointReward,
MessageColor::Text, FontStyle::ChatMediumBold);
builder->emplace<ScalingImageElement>(
reward.image, MessageElementFlag::ChannelPointRewardImage);
builder->emplace<TextElement>(
QString::number(reward.cost), MessageElementFlag::ChannelPointReward,
MessageColor::Text, FontStyle::ChatMediumBold);
if (reward.isUserInputRequired)
{
builder->emplace<LinebreakElement>(
MessageElementFlag::ChannelPointReward);
}
builder->message().flags.set(MessageFlag::RedeemedChannelPointReward);
}
void TwitchMessageBuilder::liveMessage(const QString &channelName,
MessageBuilder *builder)
{
builder->emplace<TimestampElement>();
builder
->emplace<TextElement>(channelName, MessageElementFlag::Username,
MessageColor::Text, FontStyle::ChatMediumBold)
->setLink({Link::UserInfo, channelName});
builder->emplace<TextElement>("is live!", MessageElementFlag::Text,
MessageColor::Text);
}
void TwitchMessageBuilder::liveSystemMessage(const QString &channelName,
MessageBuilder *builder)
{
builder->emplace<TimestampElement>();
builder->message().flags.set(MessageFlag::System);
builder->message().flags.set(MessageFlag::DoNotTriggerNotification);
builder
->emplace<TextElement>(channelName, MessageElementFlag::Username,
MessageColor::System, FontStyle::ChatMediumBold)
->setLink({Link::UserInfo, channelName});
builder->emplace<TextElement>("is live!", MessageElementFlag::Text,
MessageColor::System);
}
void TwitchMessageBuilder::offlineSystemMessage(const QString &channelName,
MessageBuilder *builder)
{
builder->emplace<TimestampElement>();
builder->message().flags.set(MessageFlag::System);
builder->message().flags.set(MessageFlag::DoNotTriggerNotification);
builder
->emplace<TextElement>(channelName, MessageElementFlag::Username,
MessageColor::System, FontStyle::ChatMediumBold)
->setLink({Link::UserInfo, channelName});
builder->emplace<TextElement>("is now offline.", MessageElementFlag::Text,
MessageColor::System);
}
void TwitchMessageBuilder::hostingSystemMessage(const QString &channelName,
MessageBuilder *builder)
{
builder->emplace<TimestampElement>();
builder->message().flags.set(MessageFlag::System);
builder->message().flags.set(MessageFlag::DoNotTriggerNotification);
builder->emplace<TextElement>("Now hosting", MessageElementFlag::Text,
MessageColor::System);
builder
->emplace<TextElement>(channelName + ".", MessageElementFlag::Username,
MessageColor::System, FontStyle::ChatMediumBold)
->setLink({Link::UserInfo, channelName});
}
void TwitchMessageBuilder::deletionMessage(const MessagePtr originalMessage,
MessageBuilder *builder)
{
builder->emplace<TimestampElement>();
builder->message().flags.set(MessageFlag::System);
builder->message().flags.set(MessageFlag::DoNotTriggerNotification);
builder->message().flags.set(MessageFlag::Timeout);
// TODO(mm2pl): If or when jumping to a single message gets implemented a link,
// add a link to the originalMessage
builder->emplace<TextElement>("A message from", MessageElementFlag::Text,
MessageColor::System);
builder
->emplace<TextElement>(originalMessage->displayName,
MessageElementFlag::Username,
MessageColor::System, FontStyle::ChatMediumBold)
->setLink({Link::UserInfo, originalMessage->loginName});
builder->emplace<TextElement>("was deleted:", MessageElementFlag::Text,
MessageColor::System);
if (originalMessage->messageText.length() > 50)
{
builder->emplace<TextElement>(
originalMessage->messageText.left(50) + "...",
MessageElementFlag::Text, MessageColor::Text);
}
else
{
builder->emplace<TextElement>(originalMessage->messageText,
MessageElementFlag::Text,
MessageColor::Text);
}
}
} // namespace chatterino