2018-06-26 14:09:39 +02:00
|
|
|
#include "MessageBuilder.hpp"
|
2018-06-28 00:24:21 +02:00
|
|
|
|
2019-01-20 14:45:59 +01:00
|
|
|
#include "Application.hpp"
|
2022-10-01 17:36:22 +02:00
|
|
|
#include "common/IrcColors.hpp"
|
2018-06-28 00:24:21 +02:00
|
|
|
#include "common/LinkParser.hpp"
|
2020-04-13 12:31:06 +02:00
|
|
|
#include "controllers/accounts/AccountController.hpp"
|
2019-01-20 14:45:59 +01:00
|
|
|
#include "messages/Image.hpp"
|
2018-08-11 22:23:06 +02:00
|
|
|
#include "messages/Message.hpp"
|
2023-11-19 12:05:30 +01:00
|
|
|
#include "messages/MessageColor.hpp"
|
2018-08-11 22:23:06 +02:00
|
|
|
#include "messages/MessageElement.hpp"
|
2019-03-13 15:26:55 +01:00
|
|
|
#include "providers/LinkResolver.hpp"
|
2022-05-07 17:22:39 +02:00
|
|
|
#include "providers/twitch/PubSubActions.hpp"
|
2022-12-18 15:36:39 +01:00
|
|
|
#include "providers/twitch/TwitchAccount.hpp"
|
2018-06-28 19:46:45 +02:00
|
|
|
#include "singletons/Emotes.hpp"
|
|
|
|
#include "singletons/Resources.hpp"
|
2018-06-28 20:03:04 +02:00
|
|
|
#include "singletons/Theme.hpp"
|
2018-08-07 01:35:24 +02:00
|
|
|
#include "util/FormatTime.hpp"
|
2022-07-31 11:55:25 +02:00
|
|
|
#include "util/Qt.hpp"
|
2017-04-12 17:46:44 +02:00
|
|
|
|
2017-12-27 01:22:12 +01:00
|
|
|
#include <QDateTime>
|
|
|
|
|
2022-10-01 17:36:22 +02:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
QRegularExpression IRC_COLOR_PARSE_REGEX(
|
|
|
|
"(\u0003(\\d{1,2})?(,(\\d{1,2}))?|\u000f)",
|
|
|
|
QRegularExpression::UseUnicodePropertiesOption);
|
|
|
|
|
2022-11-13 12:07:41 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-10-01 17:36:22 +02:00
|
|
|
} // namespace
|
|
|
|
|
2017-04-14 17:52:22 +02:00
|
|
|
namespace chatterino {
|
2017-04-12 17:46:44 +02:00
|
|
|
|
2018-08-07 01:35:24 +02:00
|
|
|
MessagePtr makeSystemMessage(const QString &text)
|
|
|
|
{
|
|
|
|
return MessageBuilder(systemMessage, text).release();
|
|
|
|
}
|
|
|
|
|
2020-07-18 16:03:51 +02:00
|
|
|
MessagePtr makeSystemMessage(const QString &text, const QTime &time)
|
|
|
|
{
|
|
|
|
return MessageBuilder(systemMessage, text, time).release();
|
|
|
|
}
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
MessageBuilder::MessageBuilder()
|
2018-08-11 22:23:06 +02:00
|
|
|
: message_(std::make_shared<Message>())
|
2018-08-07 01:35:24 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-07-18 16:03:51 +02:00
|
|
|
MessageBuilder::MessageBuilder(SystemMessageTag, const QString &text,
|
|
|
|
const QTime &time)
|
2018-08-07 01:35:24 +02:00
|
|
|
: MessageBuilder()
|
2017-04-12 17:46:44 +02:00
|
|
|
{
|
2020-07-18 16:03:51 +02:00
|
|
|
this->emplace<TimestampElement>(time);
|
2019-05-01 12:07:35 +02:00
|
|
|
|
|
|
|
// check system message for links
|
|
|
|
// (e.g. needed for sub ticket message in sub only mode)
|
2022-07-31 11:55:25 +02:00
|
|
|
const QStringList textFragments =
|
|
|
|
text.split(QRegularExpression("\\s"), Qt::SkipEmptyParts);
|
2019-05-01 12:07:35 +02:00
|
|
|
for (const auto &word : textFragments)
|
|
|
|
{
|
2023-04-23 00:58:37 +02:00
|
|
|
LinkParser parser(word);
|
|
|
|
if (parser.result())
|
2019-05-01 12:07:35 +02:00
|
|
|
{
|
2023-04-23 00:58:37 +02:00
|
|
|
this->addLink(*parser.result());
|
2022-07-31 11:55:25 +02:00
|
|
|
continue;
|
2019-05-01 12:07:35 +02:00
|
|
|
}
|
2022-07-31 11:55:25 +02:00
|
|
|
|
|
|
|
this->emplace<TextElement>(word, MessageElementFlag::Text,
|
|
|
|
MessageColor::System);
|
2019-05-01 12:07:35 +02:00
|
|
|
}
|
2018-08-07 07:55:31 +02:00
|
|
|
this->message().flags.set(MessageFlag::System);
|
|
|
|
this->message().flags.set(MessageFlag::DoNotTriggerNotification);
|
2019-05-01 16:43:52 +02:00
|
|
|
this->message().messageText = text;
|
2018-08-07 01:35:24 +02:00
|
|
|
this->message().searchText = text;
|
2017-04-12 17:46:44 +02:00
|
|
|
}
|
|
|
|
|
2022-08-14 15:34:32 +02:00
|
|
|
MessageBuilder::MessageBuilder(TimeoutMessageTag, const QString &timeoutUser,
|
|
|
|
const QString &sourceUser,
|
2020-10-03 13:37:07 +02:00
|
|
|
const QString &systemMessageText, int times,
|
|
|
|
const QTime &time)
|
2020-07-18 16:03:51 +02:00
|
|
|
: MessageBuilder()
|
|
|
|
{
|
2022-08-14 15:34:32 +02:00
|
|
|
QString usernameText = systemMessageText.split(" ").at(0);
|
|
|
|
QString remainder = systemMessageText.mid(usernameText.length() + 1);
|
|
|
|
bool timeoutUserIsFirst =
|
|
|
|
usernameText == "You" || timeoutUser == usernameText;
|
|
|
|
QString messageText;
|
2020-07-18 16:03:51 +02:00
|
|
|
|
2020-10-03 13:37:07 +02:00
|
|
|
this->emplace<TimestampElement>(time);
|
2022-08-14 15:34:32 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(usernameText, messageText)
|
|
|
|
->setLink(
|
|
|
|
{Link::UserInfo, timeoutUserIsFirst ? timeoutUser : sourceUser});
|
|
|
|
|
|
|
|
if (!sourceUser.isEmpty())
|
|
|
|
{
|
|
|
|
// the second username in the message
|
|
|
|
const auto &targetUsername =
|
|
|
|
timeoutUserIsFirst ? sourceUser : timeoutUser;
|
|
|
|
int userPos = remainder.indexOf(targetUsername);
|
|
|
|
|
|
|
|
QString mid = remainder.mid(0, userPos - 1);
|
|
|
|
QString username = remainder.mid(userPos, targetUsername.length());
|
|
|
|
remainder = remainder.mid(userPos + targetUsername.length() + 1);
|
|
|
|
|
|
|
|
this->emplaceSystemTextAndUpdate(mid, messageText);
|
|
|
|
this->emplaceSystemTextAndUpdate(username, messageText)
|
|
|
|
->setLink({Link::UserInfo, username});
|
|
|
|
}
|
|
|
|
|
2020-07-18 16:03:51 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(
|
2022-08-14 15:34:32 +02:00
|
|
|
QString("%1 (%2 times)").arg(remainder.trimmed()).arg(times),
|
|
|
|
messageText);
|
2020-07-18 16:03:51 +02:00
|
|
|
|
2022-08-14 15:34:32 +02:00
|
|
|
this->message().messageText = messageText;
|
|
|
|
this->message().searchText = messageText;
|
2020-07-18 16:03:51 +02:00
|
|
|
}
|
|
|
|
|
2018-08-07 01:35:24 +02:00
|
|
|
MessageBuilder::MessageBuilder(TimeoutMessageTag, const QString &username,
|
|
|
|
const QString &durationInSeconds,
|
2021-04-25 15:44:12 +02:00
|
|
|
bool multipleTimes, const QTime &time)
|
2018-08-07 01:35:24 +02:00
|
|
|
: MessageBuilder()
|
2017-04-12 17:46:44 +02:00
|
|
|
{
|
2020-07-18 16:03:51 +02:00
|
|
|
QString fullText;
|
2018-08-07 01:35:24 +02:00
|
|
|
QString text;
|
|
|
|
|
2020-10-03 13:37:07 +02:00
|
|
|
this->emplace<TimestampElement>(time);
|
2020-07-18 16:03:51 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(username, fullText)
|
|
|
|
->setLink({Link::UserInfo, username});
|
|
|
|
|
2018-08-07 01:35:24 +02:00
|
|
|
if (!durationInSeconds.isEmpty())
|
|
|
|
{
|
2020-07-18 16:03:51 +02:00
|
|
|
text.append("has been timed out");
|
2018-08-07 01:35:24 +02:00
|
|
|
|
|
|
|
// TODO: Implement who timed the user out
|
|
|
|
|
|
|
|
text.append(" for ");
|
|
|
|
bool ok = true;
|
|
|
|
int timeoutSeconds = durationInSeconds.toInt(&ok);
|
|
|
|
if (ok)
|
|
|
|
{
|
|
|
|
text.append(formatTime(timeoutSeconds));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-07-18 16:03:51 +02:00
|
|
|
text.append("has been permanently banned");
|
2018-08-07 01:35:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
text.append(".");
|
|
|
|
|
|
|
|
if (multipleTimes)
|
|
|
|
{
|
|
|
|
text.append(" (multiple times)");
|
|
|
|
}
|
|
|
|
|
2018-08-07 07:55:31 +02:00
|
|
|
this->message().flags.set(MessageFlag::System);
|
|
|
|
this->message().flags.set(MessageFlag::Timeout);
|
|
|
|
this->message().flags.set(MessageFlag::DoNotTriggerNotification);
|
2018-08-07 01:35:24 +02:00
|
|
|
this->message().timeoutUser = username;
|
2020-07-18 16:03:51 +02:00
|
|
|
|
|
|
|
this->emplaceSystemTextAndUpdate(text, fullText);
|
|
|
|
this->message().messageText = fullText;
|
|
|
|
this->message().searchText = fullText;
|
2017-04-12 17:46:44 +02:00
|
|
|
}
|
|
|
|
|
2020-04-13 12:31:06 +02:00
|
|
|
// XXX: This does not belong in the MessageBuilder, this should be part of the TwitchMessageBuilder
|
2018-08-07 01:35:24 +02:00
|
|
|
MessageBuilder::MessageBuilder(const BanAction &action, uint32_t count)
|
2018-08-08 15:35:54 +02:00
|
|
|
: MessageBuilder()
|
2017-04-12 17:46:44 +02:00
|
|
|
{
|
2020-04-13 12:31:06 +02:00
|
|
|
auto current = getApp()->accounts->twitch.getCurrent();
|
|
|
|
|
2018-08-07 01:35:24 +02:00
|
|
|
this->emplace<TimestampElement>();
|
2018-08-07 07:55:31 +02:00
|
|
|
this->message().flags.set(MessageFlag::System);
|
|
|
|
this->message().flags.set(MessageFlag::Timeout);
|
2021-07-11 13:33:35 +02:00
|
|
|
this->message().timeoutUser = action.target.login;
|
2022-08-14 15:34:32 +02:00
|
|
|
this->message().loginName = action.source.login;
|
2018-08-07 01:35:24 +02:00
|
|
|
this->message().count = count;
|
|
|
|
|
|
|
|
QString text;
|
|
|
|
|
2020-04-13 12:31:06 +02:00
|
|
|
if (action.target.id == current->getUserId())
|
2018-08-07 01:35:24 +02:00
|
|
|
{
|
2022-08-14 15:34:32 +02:00
|
|
|
this->emplaceSystemTextAndUpdate("You", text)
|
|
|
|
->setLink({Link::UserInfo, current->getUserName()});
|
|
|
|
this->emplaceSystemTextAndUpdate("were", text);
|
2020-04-13 12:31:06 +02:00
|
|
|
if (action.isBan())
|
|
|
|
{
|
2020-07-18 16:03:51 +02:00
|
|
|
this->emplaceSystemTextAndUpdate("banned", text);
|
2020-04-13 12:31:06 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-07-18 16:03:51 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(
|
|
|
|
QString("timed out for %1").arg(formatTime(action.duration)),
|
|
|
|
text);
|
2020-04-13 12:31:06 +02:00
|
|
|
}
|
|
|
|
|
2021-07-11 13:33:35 +02:00
|
|
|
if (!action.source.login.isEmpty())
|
2020-04-13 12:31:06 +02:00
|
|
|
{
|
2020-07-18 16:03:51 +02:00
|
|
|
this->emplaceSystemTextAndUpdate("by", text);
|
|
|
|
this->emplaceSystemTextAndUpdate(
|
2021-07-11 13:33:35 +02:00
|
|
|
action.source.login + (action.reason.isEmpty() ? "." : ":"),
|
2020-07-18 16:03:51 +02:00
|
|
|
text)
|
2021-07-11 13:33:35 +02:00
|
|
|
->setLink({Link::UserInfo, action.source.login});
|
2020-04-13 12:31:06 +02:00
|
|
|
}
|
|
|
|
|
2020-07-18 16:03:51 +02:00
|
|
|
if (!action.reason.isEmpty())
|
2018-08-07 01:35:24 +02:00
|
|
|
{
|
2020-07-18 16:03:51 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(
|
|
|
|
QString("\"%1\".").arg(action.reason), text);
|
2018-08-07 01:35:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-13 12:31:06 +02:00
|
|
|
if (action.isBan())
|
2018-10-21 13:43:02 +02:00
|
|
|
{
|
2021-07-11 13:33:35 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(action.source.login, text)
|
|
|
|
->setLink({Link::UserInfo, action.source.login});
|
2020-07-18 16:03:51 +02:00
|
|
|
this->emplaceSystemTextAndUpdate("banned", text);
|
2020-04-13 12:31:06 +02:00
|
|
|
if (action.reason.isEmpty())
|
|
|
|
{
|
2021-07-11 13:33:35 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(action.target.login, text)
|
|
|
|
->setLink({Link::UserInfo, action.target.login});
|
2020-04-13 12:31:06 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-07-11 13:33:35 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(action.target.login + ":",
|
|
|
|
text)
|
|
|
|
->setLink({Link::UserInfo, action.target.login});
|
2020-07-18 16:03:51 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(
|
|
|
|
QString("\"%1\".").arg(action.reason), text);
|
2020-04-13 12:31:06 +02:00
|
|
|
}
|
2018-08-07 01:35:24 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-07-11 13:33:35 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(action.source.login, text)
|
|
|
|
->setLink({Link::UserInfo, action.source.login});
|
2020-07-18 16:03:51 +02:00
|
|
|
this->emplaceSystemTextAndUpdate("timed out", text);
|
2021-07-11 13:33:35 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(action.target.login, text)
|
|
|
|
->setLink({Link::UserInfo, action.target.login});
|
2020-04-13 12:31:06 +02:00
|
|
|
if (action.reason.isEmpty())
|
|
|
|
{
|
2020-07-18 16:03:51 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(
|
|
|
|
QString("for %1.").arg(formatTime(action.duration)), text);
|
2020-04-13 12:31:06 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-07-18 16:03:51 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(
|
|
|
|
QString("for %1: \"%2\".")
|
|
|
|
.arg(formatTime(action.duration))
|
|
|
|
.arg(action.reason),
|
|
|
|
text);
|
2020-04-13 12:31:06 +02:00
|
|
|
}
|
2018-08-07 01:35:24 +02:00
|
|
|
|
2020-04-13 12:31:06 +02:00
|
|
|
if (count > 1)
|
|
|
|
{
|
2020-07-18 16:03:51 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(
|
|
|
|
QString("(%1 times)").arg(count), text);
|
2020-04-13 12:31:06 +02:00
|
|
|
}
|
2018-08-07 01:35:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-01 16:43:52 +02:00
|
|
|
this->message().messageText = text;
|
2018-08-07 01:35:24 +02:00
|
|
|
this->message().searchText = text;
|
2017-04-12 17:46:44 +02:00
|
|
|
}
|
|
|
|
|
2018-08-07 01:35:24 +02:00
|
|
|
MessageBuilder::MessageBuilder(const UnbanAction &action)
|
2018-08-08 15:35:54 +02:00
|
|
|
: MessageBuilder()
|
2017-07-31 00:57:42 +02:00
|
|
|
{
|
2018-08-07 01:35:24 +02:00
|
|
|
this->emplace<TimestampElement>();
|
2018-08-07 07:55:31 +02:00
|
|
|
this->message().flags.set(MessageFlag::System);
|
|
|
|
this->message().flags.set(MessageFlag::Untimeout);
|
2018-08-07 01:35:24 +02:00
|
|
|
|
2021-07-11 13:33:35 +02:00
|
|
|
this->message().timeoutUser = action.target.login;
|
2018-08-07 01:35:24 +02:00
|
|
|
|
2020-07-18 16:03:51 +02:00
|
|
|
QString text;
|
|
|
|
|
2021-07-11 13:33:35 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(action.source.login, text)
|
|
|
|
->setLink({Link::UserInfo, action.source.login});
|
2020-07-18 16:03:51 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(
|
|
|
|
action.wasBan() ? "unbanned" : "untimedout", text);
|
2021-07-11 13:33:35 +02:00
|
|
|
this->emplaceSystemTextAndUpdate(action.target.login, text)
|
|
|
|
->setLink({Link::UserInfo, action.target.login});
|
2018-08-07 01:35:24 +02:00
|
|
|
|
2019-05-01 16:43:52 +02:00
|
|
|
this->message().messageText = text;
|
2018-08-07 01:35:24 +02:00
|
|
|
this->message().searchText = text;
|
|
|
|
}
|
|
|
|
|
2019-01-21 18:33:57 +01:00
|
|
|
MessageBuilder::MessageBuilder(const AutomodUserAction &action)
|
|
|
|
: MessageBuilder()
|
|
|
|
{
|
|
|
|
this->emplace<TimestampElement>();
|
|
|
|
this->message().flags.set(MessageFlag::System);
|
|
|
|
|
|
|
|
QString text;
|
2019-01-22 23:20:43 +01:00
|
|
|
switch (action.type)
|
2019-01-21 18:33:57 +01:00
|
|
|
{
|
2019-09-26 00:51:05 +02:00
|
|
|
case AutomodUserAction::AddPermitted: {
|
2022-04-07 14:15:41 +02:00
|
|
|
text = QString("%1 added \"%2\" as a permitted term on AutoMod.")
|
2021-07-11 13:33:35 +02:00
|
|
|
.arg(action.source.login, action.message);
|
2019-01-22 23:20:43 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-09-26 00:51:05 +02:00
|
|
|
case AutomodUserAction::AddBlocked: {
|
2022-04-07 14:15:41 +02:00
|
|
|
text = QString("%1 added \"%2\" as a blocked term on AutoMod.")
|
2021-07-11 13:33:35 +02:00
|
|
|
.arg(action.source.login, action.message);
|
2019-01-22 23:20:43 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-09-26 00:51:05 +02:00
|
|
|
case AutomodUserAction::RemovePermitted: {
|
2022-04-07 14:15:41 +02:00
|
|
|
text = QString("%1 removed \"%2\" as a permitted term on AutoMod.")
|
2021-07-11 13:33:35 +02:00
|
|
|
.arg(action.source.login, action.message);
|
2019-01-22 23:20:43 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-09-26 00:51:05 +02:00
|
|
|
case AutomodUserAction::RemoveBlocked: {
|
2022-04-07 14:15:41 +02:00
|
|
|
text = QString("%1 removed \"%2\" as a blocked term on AutoMod.")
|
2021-07-11 13:33:35 +02:00
|
|
|
.arg(action.source.login, action.message);
|
2019-01-22 23:20:43 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-09-26 00:51:05 +02:00
|
|
|
case AutomodUserAction::Properties: {
|
2019-01-22 23:20:43 +01:00
|
|
|
text = QString("%1 modified the AutoMod properties.")
|
2021-07-11 13:33:35 +02:00
|
|
|
.arg(action.source.login);
|
2019-01-22 23:20:43 +01:00
|
|
|
}
|
|
|
|
break;
|
2019-01-21 18:33:57 +01:00
|
|
|
}
|
2021-07-11 12:19:35 +02:00
|
|
|
this->message().messageText = text;
|
|
|
|
this->message().searchText = text;
|
2019-01-21 18:33:57 +01:00
|
|
|
|
|
|
|
this->emplace<TextElement>(text, MessageElementFlag::Text,
|
|
|
|
MessageColor::System);
|
|
|
|
}
|
|
|
|
|
2022-11-13 12:07:41 +01:00
|
|
|
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()
|
|
|
|
{
|
2023-01-21 15:06:55 +01:00
|
|
|
QString text;
|
|
|
|
if (actor.isEmpty())
|
|
|
|
{
|
|
|
|
text = "Renamed";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
text = "renamed";
|
|
|
|
}
|
|
|
|
text +=
|
|
|
|
QString(" %1 emote %2 to %3.").arg(platform, oldEmoteName, emoteName);
|
2022-11-13 12:07:41 +01:00
|
|
|
|
|
|
|
this->emplace<TimestampElement>();
|
2023-01-21 15:06:55 +01:00
|
|
|
if (!actor.isEmpty())
|
|
|
|
{
|
|
|
|
this->emplace<TextElement>(actor, MessageElementFlag::Username,
|
|
|
|
MessageColor::System)
|
|
|
|
->setLink({Link::UserInfo, actor});
|
|
|
|
}
|
2022-11-13 12:07:41 +01:00
|
|
|
this->emplace<TextElement>(text, MessageElementFlag::Text,
|
|
|
|
MessageColor::System);
|
|
|
|
|
2023-01-21 15:06:55 +01:00
|
|
|
QString finalText;
|
|
|
|
if (actor.isEmpty())
|
|
|
|
{
|
|
|
|
finalText = text;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
finalText = QString("%1 %2").arg(actor, text);
|
|
|
|
}
|
2022-11-13 12:07:41 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-11-19 12:05:30 +01:00
|
|
|
MessageBuilder::MessageBuilder(ImageUploaderResultTag /*unused*/,
|
|
|
|
const QString &imageLink,
|
|
|
|
const QString &deletionLink,
|
|
|
|
size_t imagesStillQueued, size_t secondsLeft)
|
|
|
|
: MessageBuilder()
|
|
|
|
{
|
|
|
|
this->message().flags.set(MessageFlag::System);
|
|
|
|
this->message().flags.set(MessageFlag::DoNotTriggerNotification);
|
|
|
|
|
|
|
|
this->emplace<TimestampElement>();
|
|
|
|
|
|
|
|
using MEF = MessageElementFlag;
|
|
|
|
auto addText = [this](QString text, MessageElementFlags mefs = MEF::Text,
|
|
|
|
MessageColor color =
|
|
|
|
MessageColor::System) -> TextElement * {
|
|
|
|
this->message().searchText += text;
|
|
|
|
this->message().messageText += text;
|
|
|
|
return this->emplace<TextElement>(text, mefs, color);
|
|
|
|
};
|
|
|
|
|
|
|
|
addText("Your image has been uploaded to");
|
|
|
|
|
|
|
|
// ASSUMPTION: the user gave this uploader configuration to the program
|
|
|
|
// therefore they trust that the host is not wrong/malicious. This doesn't obey getSettings()->lowercaseDomains.
|
|
|
|
// This also ensures that the LinkResolver doesn't get these links.
|
|
|
|
addText(imageLink, {MEF::OriginalLink, MEF::LowercaseLink},
|
|
|
|
MessageColor::Link)
|
|
|
|
->setLink({Link::Url, imageLink})
|
|
|
|
->setTrailingSpace(false);
|
|
|
|
|
|
|
|
if (!deletionLink.isEmpty())
|
|
|
|
{
|
|
|
|
addText("(Deletion link:");
|
|
|
|
addText(deletionLink, {MEF::OriginalLink, MEF::LowercaseLink},
|
|
|
|
MessageColor::Link)
|
|
|
|
->setLink({Link::Url, deletionLink})
|
|
|
|
->setTrailingSpace(false);
|
|
|
|
addText(")")->setTrailingSpace(false);
|
|
|
|
}
|
|
|
|
addText(".");
|
|
|
|
|
|
|
|
if (imagesStillQueued == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
addText(QString("%1 left. Please wait until all of them are uploaded. "
|
|
|
|
"About %2 seconds left.")
|
|
|
|
.arg(imagesStillQueued)
|
|
|
|
.arg(secondsLeft));
|
|
|
|
}
|
|
|
|
|
2018-08-07 01:35:24 +02:00
|
|
|
Message *MessageBuilder::operator->()
|
|
|
|
{
|
|
|
|
return this->message_.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
Message &MessageBuilder::message()
|
|
|
|
{
|
|
|
|
return *this->message_;
|
|
|
|
}
|
|
|
|
|
|
|
|
MessagePtr MessageBuilder::release()
|
|
|
|
{
|
2018-08-11 22:23:06 +02:00
|
|
|
std::shared_ptr<Message> ptr;
|
|
|
|
this->message_.swap(ptr);
|
|
|
|
return ptr;
|
2017-07-31 00:37:22 +02:00
|
|
|
}
|
|
|
|
|
2018-10-24 11:36:36 +02:00
|
|
|
std::weak_ptr<Message> MessageBuilder::weakOf()
|
|
|
|
{
|
|
|
|
return this->message_;
|
|
|
|
}
|
|
|
|
|
2018-08-07 01:35:24 +02:00
|
|
|
void MessageBuilder::append(std::unique_ptr<MessageElement> element)
|
2017-04-12 17:46:44 +02:00
|
|
|
{
|
2018-08-07 01:35:24 +02:00
|
|
|
this->message().elements.push_back(std::move(element));
|
2017-04-12 17:46:44 +02:00
|
|
|
}
|
|
|
|
|
2023-03-18 17:30:08 +01:00
|
|
|
bool MessageBuilder::isEmpty() const
|
|
|
|
{
|
|
|
|
return this->message_->elements.empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
MessageElement &MessageBuilder::back()
|
|
|
|
{
|
|
|
|
assert(!this->isEmpty());
|
|
|
|
return *this->message().elements.back();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<MessageElement> MessageBuilder::releaseBack()
|
|
|
|
{
|
|
|
|
assert(!this->isEmpty());
|
|
|
|
|
|
|
|
auto ptr = std::move(this->message().elements.back());
|
|
|
|
this->message().elements.pop_back();
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
2023-04-23 00:58:37 +02:00
|
|
|
void MessageBuilder::addLink(const ParsedLink &parsedLink)
|
2017-04-12 17:46:44 +02:00
|
|
|
{
|
2023-04-23 00:58:37 +02:00
|
|
|
QString lowercaseLinkString;
|
|
|
|
QString origLink = parsedLink.source;
|
|
|
|
QString matchedLink;
|
2017-08-12 12:09:26 +02:00
|
|
|
|
2023-04-23 00:58:37 +02:00
|
|
|
if (parsedLink.protocol.isNull())
|
2018-06-28 00:24:21 +02:00
|
|
|
{
|
2023-04-23 00:58:37 +02:00
|
|
|
matchedLink = QStringLiteral("http://") + parsedLink.source;
|
2017-08-12 12:09:26 +02:00
|
|
|
}
|
2023-04-23 00:58:37 +02:00
|
|
|
else
|
2018-07-11 13:50:05 +02:00
|
|
|
{
|
2023-04-23 00:58:37 +02:00
|
|
|
lowercaseLinkString += parsedLink.protocol;
|
|
|
|
matchedLink = parsedLink.source;
|
2017-07-26 12:01:23 +02:00
|
|
|
}
|
2017-09-24 18:43:24 +02:00
|
|
|
|
2023-04-23 00:58:37 +02:00
|
|
|
lowercaseLinkString += parsedLink.host.toString().toLower();
|
|
|
|
lowercaseLinkString += parsedLink.rest;
|
2019-03-13 15:26:55 +01:00
|
|
|
|
|
|
|
auto linkElement = Link(Link::Url, matchedLink);
|
|
|
|
|
|
|
|
auto textColor = MessageColor(MessageColor::Link);
|
|
|
|
auto linkMELowercase =
|
|
|
|
this->emplace<TextElement>(lowercaseLinkString,
|
|
|
|
MessageElementFlag::LowercaseLink, textColor)
|
|
|
|
->setLink(linkElement);
|
|
|
|
auto linkMEOriginal =
|
|
|
|
this->emplace<TextElement>(origLink, MessageElementFlag::OriginalLink,
|
|
|
|
textColor)
|
|
|
|
->setLink(linkElement);
|
|
|
|
|
2019-08-20 23:30:39 +02:00
|
|
|
LinkResolver::getLinkInfo(
|
|
|
|
matchedLink, nullptr,
|
|
|
|
[weakMessage = this->weakOf(), linkMELowercase, linkMEOriginal,
|
2020-05-10 12:11:10 +02:00
|
|
|
matchedLink](QString tooltipText, Link originalLink,
|
|
|
|
ImagePtr thumbnail) {
|
2019-08-20 23:30:39 +02:00
|
|
|
auto shared = weakMessage.lock();
|
|
|
|
if (!shared)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!tooltipText.isEmpty())
|
|
|
|
{
|
|
|
|
linkMELowercase->setTooltip(tooltipText);
|
|
|
|
linkMEOriginal->setTooltip(tooltipText);
|
|
|
|
}
|
|
|
|
if (originalLink.value != matchedLink &&
|
|
|
|
!originalLink.value.isEmpty())
|
|
|
|
{
|
|
|
|
linkMELowercase->setLink(originalLink)->updateLink();
|
|
|
|
linkMEOriginal->setLink(originalLink)->updateLink();
|
|
|
|
}
|
2020-05-10 12:11:10 +02:00
|
|
|
linkMELowercase->setThumbnail(thumbnail);
|
|
|
|
linkMELowercase->setThumbnailType(
|
|
|
|
MessageElement::ThumbnailType::Link_Thumbnail);
|
|
|
|
linkMEOriginal->setThumbnail(thumbnail);
|
|
|
|
linkMEOriginal->setThumbnailType(
|
|
|
|
MessageElement::ThumbnailType::Link_Thumbnail);
|
2019-08-20 23:30:39 +02:00
|
|
|
});
|
2019-03-13 15:26:55 +01:00
|
|
|
}
|
|
|
|
|
2022-10-01 17:36:22 +02:00
|
|
|
void MessageBuilder::addIrcMessageText(const QString &text)
|
|
|
|
{
|
|
|
|
this->message().messageText = text;
|
|
|
|
|
|
|
|
auto words = text.split(' ');
|
|
|
|
MessageColor defaultColorType = MessageColor::Text;
|
|
|
|
const auto &defaultColor = defaultColorType.getColor(*getApp()->themes);
|
|
|
|
QColor textColor = defaultColor;
|
|
|
|
int fg = -1;
|
|
|
|
int bg = -1;
|
|
|
|
|
|
|
|
for (const auto &word : words)
|
|
|
|
{
|
|
|
|
if (word.isEmpty())
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto string = QString(word);
|
|
|
|
|
|
|
|
// Actually just text
|
2023-04-23 00:58:37 +02:00
|
|
|
LinkParser parser(string);
|
|
|
|
if (parser.result())
|
2022-10-01 17:36:22 +02:00
|
|
|
{
|
2023-04-23 00:58:37 +02:00
|
|
|
this->addLink(*parser.result());
|
2022-10-01 17:36:22 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Does the word contain a color changer? If so, split on it.
|
|
|
|
// Add color indicators, then combine into the same word with the color being changed
|
|
|
|
|
|
|
|
auto i = IRC_COLOR_PARSE_REGEX.globalMatch(string);
|
|
|
|
|
|
|
|
if (!i.hasNext())
|
|
|
|
{
|
|
|
|
this->addIrcWord(string, textColor);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
int lastPos = 0;
|
|
|
|
|
|
|
|
while (i.hasNext())
|
|
|
|
{
|
|
|
|
auto match = i.next();
|
|
|
|
|
|
|
|
if (lastPos != match.capturedStart() && match.capturedStart() != 0)
|
|
|
|
{
|
|
|
|
if (fg >= 0 && fg <= 98)
|
|
|
|
{
|
|
|
|
textColor = IRC_COLORS[fg];
|
|
|
|
getApp()->themes->normalizeColor(textColor);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
textColor = defaultColor;
|
|
|
|
}
|
|
|
|
this->addIrcWord(
|
|
|
|
string.mid(lastPos, match.capturedStart() - lastPos),
|
|
|
|
textColor, false);
|
|
|
|
lastPos = match.capturedStart() + match.capturedLength();
|
|
|
|
}
|
|
|
|
if (!match.captured(1).isEmpty())
|
|
|
|
{
|
|
|
|
fg = -1;
|
|
|
|
bg = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!match.captured(2).isEmpty())
|
|
|
|
{
|
|
|
|
fg = match.captured(2).toInt(nullptr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fg = -1;
|
|
|
|
}
|
|
|
|
if (!match.captured(4).isEmpty())
|
|
|
|
{
|
|
|
|
bg = match.captured(4).toInt(nullptr);
|
|
|
|
}
|
|
|
|
else if (fg == -1)
|
|
|
|
{
|
|
|
|
bg = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
lastPos = match.capturedStart() + match.capturedLength();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fg >= 0 && fg <= 98)
|
|
|
|
{
|
|
|
|
textColor = IRC_COLORS[fg];
|
|
|
|
getApp()->themes->normalizeColor(textColor);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
textColor = defaultColor;
|
|
|
|
}
|
|
|
|
this->addIrcWord(string.mid(lastPos), textColor);
|
|
|
|
}
|
|
|
|
|
|
|
|
this->message().elements.back()->setTrailingSpace(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MessageBuilder::addTextOrEmoji(EmotePtr emote)
|
|
|
|
{
|
|
|
|
this->emplace<EmoteElement>(emote, MessageElementFlag::EmojiAll);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MessageBuilder::addTextOrEmoji(const QString &string_)
|
|
|
|
{
|
|
|
|
auto string = QString(string_);
|
|
|
|
|
|
|
|
// Actually just text
|
2023-04-23 00:58:37 +02:00
|
|
|
LinkParser linkParser(string);
|
|
|
|
if (linkParser.result())
|
|
|
|
{
|
|
|
|
this->addLink(*linkParser.result());
|
|
|
|
return;
|
|
|
|
}
|
2022-10-01 17:36:22 +02:00
|
|
|
|
|
|
|
auto &&textColor = this->textColor_;
|
2023-04-23 00:58:37 +02:00
|
|
|
if (string.startsWith('@'))
|
2022-10-01 17:36:22 +02:00
|
|
|
{
|
2023-04-23 00:58:37 +02:00
|
|
|
this->emplace<TextElement>(string, MessageElementFlag::BoldUsername,
|
|
|
|
textColor, FontStyle::ChatMediumBold);
|
|
|
|
this->emplace<TextElement>(string, MessageElementFlag::NonBoldUsername,
|
|
|
|
textColor);
|
2022-10-01 17:36:22 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-04-23 00:58:37 +02:00
|
|
|
this->emplace<TextElement>(string, MessageElementFlag::Text, textColor);
|
2022-10-01 17:36:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MessageBuilder::addIrcWord(const QString &text, const QColor &color,
|
|
|
|
bool addSpace)
|
|
|
|
{
|
|
|
|
this->textColor_ = color;
|
|
|
|
for (auto &variant : getApp()->emotes->emojis.parse(text))
|
|
|
|
{
|
|
|
|
boost::apply_visitor(
|
|
|
|
[&](auto &&arg) {
|
|
|
|
this->addTextOrEmoji(arg);
|
|
|
|
},
|
|
|
|
variant);
|
|
|
|
if (!addSpace)
|
|
|
|
{
|
|
|
|
this->message().elements.back()->setTrailingSpace(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-18 16:03:51 +02:00
|
|
|
TextElement *MessageBuilder::emplaceSystemTextAndUpdate(const QString &text,
|
|
|
|
QString &toUpdate)
|
|
|
|
{
|
|
|
|
toUpdate.append(text + " ");
|
|
|
|
return this->emplace<TextElement>(text, MessageElementFlag::Text,
|
|
|
|
MessageColor::System);
|
|
|
|
}
|
|
|
|
|
2017-06-11 20:53:43 +02:00
|
|
|
} // namespace chatterino
|