mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Squashed commit of the following:
commitea07bbef0b
Merge:0b36f436
5cfcf114
Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 20:05:14 2018 +0200 Merge branch 'blacklist' into blacklistnew commit5cfcf114b6
Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 20:00:16 2018 +0200 rename second pattern to replacement commitf08cc4cf88
Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 19:52:30 2018 +0200 delete out commented code commit1acb1278aa
Author: hemirt <hemirt@email.cz> Date: Sun Sep 23 19:52:03 2018 +0200 fix replacement with emotes issues commit646268ab18
Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 01:06:36 2018 +0200 fix build commitad711b4c15
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 commite8e059f847
Author: hemirt <hemirt@email.cz> Date: Sun Aug 19 00:25:58 2018 +0200 add replaced emotes into twitchEmotes commita63454f00d
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 commite7f2f39737
Author: hemirt <hemirt@email.cz> Date: Sat Aug 11 21:54:01 2018 +0200 emotedata commitf00d3da537
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 commit00c9fa080a
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 commit4385fcd13f
Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 21:09:14 2018 +0200 remove commented code commit1834342f74
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 commitd3b6e294ed
Author: hemirt <hemirt@email.cz> Date: Sun Jul 8 16:21:33 2018 +0200 ignore phrases
This commit is contained in:
parent
86024ade24
commit
3184234c19
8 changed files with 414 additions and 211 deletions
|
@ -1 +1 @@
|
||||||
Subproject commit bcbd29b793a2895ddf1772dca0105f51a0380798
|
Subproject commit 7f0db95f245fb726e756ecde15a800c0928b054b
|
|
@ -1 +1 @@
|
||||||
Subproject commit a1b61606c144c34a0f0f9173768cb85dd58e7f29
|
Subproject commit e03c868ec922027a0e672b64388808beb1297816
|
|
@ -8,7 +8,7 @@ namespace chatterino {
|
||||||
|
|
||||||
// commandmodel
|
// commandmodel
|
||||||
IgnoreModel::IgnoreModel(QObject *parent)
|
IgnoreModel::IgnoreModel(QObject *parent)
|
||||||
: SignalVectorModel<IgnorePhrase>(2, parent)
|
: SignalVectorModel<IgnorePhrase>(5, parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,8 +18,10 @@ IgnorePhrase IgnoreModel::getItemFromRow(std::vector<QStandardItem *> &row,
|
||||||
{
|
{
|
||||||
// key, regex
|
// key, regex
|
||||||
|
|
||||||
return IgnorePhrase{row[0]->data(Qt::DisplayRole).toString(),
|
return IgnorePhrase{
|
||||||
row[1]->data(Qt::CheckStateRole).toBool()};
|
row[0]->data(Qt::DisplayRole).toString(), row[1]->data(Qt::CheckStateRole).toBool(),
|
||||||
|
row[3]->data(Qt::CheckStateRole).toBool(), row[4]->data(Qt::DisplayRole).toString(),
|
||||||
|
row[2]->data(Qt::CheckStateRole).toBool()};
|
||||||
}
|
}
|
||||||
|
|
||||||
// turns a row in the model into a vector item
|
// turns a row in the model into a vector item
|
||||||
|
@ -28,6 +30,9 @@ void IgnoreModel::getRowFromItem(const IgnorePhrase &item,
|
||||||
{
|
{
|
||||||
setStringItem(row[0], item.getPattern());
|
setStringItem(row[0], item.getPattern());
|
||||||
setBoolItem(row[1], item.isRegex());
|
setBoolItem(row[1], item.isRegex());
|
||||||
|
setBoolItem(row[2], item.isCaseSensitive());
|
||||||
|
setBoolItem(row[3], item.isBlock());
|
||||||
|
setStringItem(row[4], item.getReplace());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Application.hpp"
|
||||||
|
#include "controllers/accounts/AccountController.hpp"
|
||||||
|
#include "singletons/Settings.hpp"
|
||||||
|
|
||||||
#include "util/RapidJsonSerializeQString.hpp"
|
#include "util/RapidJsonSerializeQString.hpp"
|
||||||
#include "util/RapidjsonHelpers.hpp"
|
#include "util/RapidjsonHelpers.hpp"
|
||||||
|
|
||||||
|
@ -16,80 +20,154 @@ class IgnorePhrase
|
||||||
public:
|
public:
|
||||||
bool operator==(const IgnorePhrase &other) const
|
bool operator==(const IgnorePhrase &other) const
|
||||||
{
|
{
|
||||||
return std::tie(this->pattern_, this->isRegex_) ==
|
return std::tie(this->pattern_, this->isRegex_, this->isBlock_, this->replace_,
|
||||||
std::tie(other.pattern_, other.isRegex_);
|
this->isCaseSensitive_) == std::tie(other.pattern_, other.isRegex_,
|
||||||
|
other.isBlock_, other.replace_,
|
||||||
|
other.isCaseSensitive_);
|
||||||
}
|
}
|
||||||
|
|
||||||
IgnorePhrase(const QString &pattern, bool isRegex)
|
IgnorePhrase(const QString &pattern, bool isRegex, bool isBlock, const QString &replace,
|
||||||
|
bool isCaseSensitive)
|
||||||
: pattern_(pattern)
|
: pattern_(pattern)
|
||||||
, isRegex_(isRegex)
|
, isRegex_(isRegex)
|
||||||
, regex_(isRegex_ ? pattern
|
, regex_(pattern)
|
||||||
: "\\b" + QRegularExpression::escape(pattern) + "\\b",
|
, isBlock_(isBlock)
|
||||||
QRegularExpression::CaseInsensitiveOption |
|
, replace_(replace)
|
||||||
QRegularExpression::UseUnicodePropertiesOption)
|
, isCaseSensitive_(isCaseSensitive)
|
||||||
{
|
{
|
||||||
|
if (this->isCaseSensitive_) {
|
||||||
|
regex_.setPatternOptions(QRegularExpression::UseUnicodePropertiesOption);
|
||||||
|
} else {
|
||||||
|
regex_.setPatternOptions(QRegularExpression::CaseInsensitiveOption |
|
||||||
|
QRegularExpression::UseUnicodePropertiesOption);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString &getPattern() const
|
const QString &getPattern() const
|
||||||
{
|
{
|
||||||
return this->pattern_;
|
return this->pattern_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isRegex() const
|
bool isRegex() const
|
||||||
{
|
{
|
||||||
return this->isRegex_;
|
return this->isRegex_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isValid() const
|
bool isRegexValid() const
|
||||||
{
|
{
|
||||||
return !this->pattern_.isEmpty() && this->regex_.isValid();
|
return this->regex_.isValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isMatch(const QString &subject) const
|
bool isMatch(const QString &subject) const
|
||||||
{
|
{
|
||||||
return this->isValid() && this->regex_.match(subject).hasMatch();
|
return !this->pattern_.isEmpty() &&
|
||||||
|
(this->isRegex() ? (this->regex_.isValid() && this->regex_.match(subject).hasMatch())
|
||||||
|
: subject.contains(this->pattern_, this->caseSensitivity()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const QRegularExpression &getRegex() const
|
||||||
|
{
|
||||||
|
return this->regex_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isBlock() const
|
||||||
|
{
|
||||||
|
return this->isBlock_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &getReplace() const
|
||||||
|
{
|
||||||
|
return this->replace_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isCaseSensitive() const
|
||||||
|
{
|
||||||
|
return this->isCaseSensitive_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::CaseSensitivity caseSensitivity() const
|
||||||
|
{
|
||||||
|
return this->isCaseSensitive_ ? Qt::CaseSensitive : Qt::CaseInsensitive;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::unordered_map<EmoteName, EmotePtr> &getEmotes() const
|
||||||
|
{
|
||||||
|
return this->emotes_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool containsEmote() const
|
||||||
|
{
|
||||||
|
if (!this->emotesChecked_) {
|
||||||
|
const auto &accvec = getApp()->accounts->twitch.accounts.getVector();
|
||||||
|
for (const auto &acc : accvec) {
|
||||||
|
const auto &accemotes = *acc->accessEmotes();
|
||||||
|
for (const auto &emote : accemotes.emotes) {
|
||||||
|
if (this->replace_.contains(emote.first.string, Qt::CaseSensitive)) {
|
||||||
|
this->emotes_.emplace(emote.first, emote.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->emotesChecked_ = true;
|
||||||
|
}
|
||||||
|
return !this->emotes_.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString pattern_;
|
QString pattern_;
|
||||||
bool isRegex_;
|
bool isRegex_;
|
||||||
QRegularExpression regex_;
|
QRegularExpression regex_;
|
||||||
|
bool isBlock_;
|
||||||
|
QString replace_;
|
||||||
|
bool isCaseSensitive_;
|
||||||
|
mutable std::unordered_map<EmoteName, EmotePtr> emotes_;
|
||||||
|
mutable bool emotesChecked_{false};
|
||||||
};
|
};
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
||||||
namespace pajlada {
|
namespace pajlada {
|
||||||
namespace Settings {
|
namespace Settings {
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct Serialize<chatterino::IgnorePhrase> {
|
struct Serialize<chatterino::IgnorePhrase> {
|
||||||
static rapidjson::Value get(const chatterino::IgnorePhrase &value,
|
static rapidjson::Value get(const chatterino::IgnorePhrase &value,
|
||||||
rapidjson::Document::AllocatorType &a)
|
rapidjson::Document::AllocatorType &a)
|
||||||
{
|
{
|
||||||
rapidjson::Value ret(rapidjson::kObjectType);
|
rapidjson::Value ret(rapidjson::kObjectType);
|
||||||
|
|
||||||
AddMember(ret, "pattern", value.getPattern(), a);
|
AddMember(ret, "pattern", value.getPattern(), a);
|
||||||
AddMember(ret, "regex", value.isRegex(), a);
|
AddMember(ret, "regex", value.isRegex(), a);
|
||||||
|
AddMember(ret, "isBlock", value.isBlock(), a);
|
||||||
|
AddMember(ret, "replaceWith", value.getReplace(), a);
|
||||||
|
AddMember(ret, "caseSensitive", value.isCaseSensitive(), a);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Deserialize<chatterino::IgnorePhrase> {
|
||||||
|
static chatterino::IgnorePhrase get(const rapidjson::Value &value)
|
||||||
|
{
|
||||||
|
if (!value.IsObject()) {
|
||||||
|
return chatterino::IgnorePhrase(
|
||||||
|
QString(), false, false,
|
||||||
|
::chatterino::getSettings()->ignoredPhraseReplace.getValue(), true);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
QString _pattern;
|
||||||
struct Deserialize<chatterino::IgnorePhrase> {
|
bool _isRegex = false;
|
||||||
static chatterino::IgnorePhrase get(const rapidjson::Value &value)
|
bool _isBlock = false;
|
||||||
{
|
QString _replace;
|
||||||
if (!value.IsObject()) {
|
bool _caseSens = true;
|
||||||
return chatterino::IgnorePhrase(QString(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString _pattern;
|
chatterino::rj::getSafe(value, "pattern", _pattern);
|
||||||
bool _isRegex = false;
|
chatterino::rj::getSafe(value, "regex", _isRegex);
|
||||||
|
chatterino::rj::getSafe(value, "isBlock", _isBlock);
|
||||||
chatterino::rj::getSafe(value, "pattern", _pattern);
|
chatterino::rj::getSafe(value, "replaceWith", _replace);
|
||||||
chatterino::rj::getSafe(value, "regex", _isRegex);
|
chatterino::rj::getSafe(value, "caseSensitive", _caseSens);
|
||||||
|
|
||||||
return chatterino::IgnorePhrase(_pattern, _isRegex);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
return chatterino::IgnorePhrase(_pattern, _isRegex, _isBlock, _replace, _caseSens);
|
||||||
|
}
|
||||||
|
};
|
||||||
} // namespace Settings
|
} // namespace Settings
|
||||||
} // namespace pajlada
|
} // namespace pajlada
|
||||||
|
|
|
@ -21,13 +21,14 @@
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QMediaPlayer>
|
#include <QMediaPlayer>
|
||||||
|
#include <QStringRef>
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
TwitchMessageBuilder::TwitchMessageBuilder(
|
TwitchMessageBuilder::TwitchMessageBuilder(Channel *_channel,
|
||||||
Channel *_channel, const Communi::IrcPrivateMessage *_ircMessage,
|
const Communi::IrcPrivateMessage *_ircMessage,
|
||||||
const MessageParseArgs &_args)
|
const MessageParseArgs &_args)
|
||||||
: channel(_channel)
|
: channel(_channel)
|
||||||
, twitchChannel(dynamic_cast<TwitchChannel *>(_channel))
|
, twitchChannel(dynamic_cast<TwitchChannel *>(_channel))
|
||||||
, ircMessage(_ircMessage)
|
, ircMessage(_ircMessage)
|
||||||
|
@ -39,9 +40,10 @@ TwitchMessageBuilder::TwitchMessageBuilder(
|
||||||
this->usernameColor_ = getApp()->themes->messages.textColors.system;
|
this->usernameColor_ = getApp()->themes->messages.textColors.system;
|
||||||
}
|
}
|
||||||
|
|
||||||
TwitchMessageBuilder::TwitchMessageBuilder(
|
TwitchMessageBuilder::TwitchMessageBuilder(Channel *_channel,
|
||||||
Channel *_channel, const Communi::IrcMessage *_ircMessage,
|
const Communi::IrcMessage *_ircMessage,
|
||||||
const MessageParseArgs &_args, QString content, bool isAction)
|
const MessageParseArgs &_args, QString content,
|
||||||
|
bool isAction)
|
||||||
: channel(_channel)
|
: channel(_channel)
|
||||||
, twitchChannel(dynamic_cast<TwitchChannel *>(_channel))
|
, twitchChannel(dynamic_cast<TwitchChannel *>(_channel))
|
||||||
, ircMessage(_ircMessage)
|
, ircMessage(_ircMessage)
|
||||||
|
@ -59,22 +61,18 @@ bool TwitchMessageBuilder::isIgnored() const
|
||||||
|
|
||||||
// TODO(pajlada): Do we need to check if the phrase is valid first?
|
// TODO(pajlada): Do we need to check if the phrase is valid first?
|
||||||
for (const auto &phrase : app->ignores->phrases.getVector()) {
|
for (const auto &phrase : app->ignores->phrases.getVector()) {
|
||||||
if (phrase.isMatch(this->originalMessage_)) {
|
if (phrase.isBlock() && phrase.isMatch(this->originalMessage_)) {
|
||||||
log("Blocking message because it contains ignored phrase {}",
|
log("Blocking message because it contains ignored phrase {}", phrase.getPattern());
|
||||||
phrase.getPattern());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getSettings()->enableTwitchIgnoredUsers &&
|
if (getSettings()->enableTwitchIgnoredUsers && this->tags.contains("user-id")) {
|
||||||
this->tags.contains("user-id")) {
|
|
||||||
auto sourceUserID = this->tags.value("user-id").toString();
|
auto sourceUserID = this->tags.value("user-id").toString();
|
||||||
|
|
||||||
for (const auto &user :
|
for (const auto &user : app->accounts->twitch.getCurrent()->getIgnores()) {
|
||||||
app->accounts->twitch.getCurrent()->getIgnores()) {
|
|
||||||
if (sourceUserID == user.id) {
|
if (sourceUserID == user.id) {
|
||||||
log("Blocking message because it's from blocked user {}",
|
log("Blocking message because it's from blocked user {}", user.name);
|
||||||
user.name);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,7 +147,7 @@ MessagePtr TwitchMessageBuilder::build()
|
||||||
}
|
}
|
||||||
|
|
||||||
// twitch emotes
|
// twitch emotes
|
||||||
std::vector<std::pair<int, EmotePtr>> twitchEmotes;
|
std::vector<std::tuple<int, EmotePtr, EmoteName>> twitchEmotes;
|
||||||
|
|
||||||
iterator = this->tags.find("emotes");
|
iterator = this->tags.find("emotes");
|
||||||
if (iterator != this->tags.end()) {
|
if (iterator != this->tags.end()) {
|
||||||
|
@ -158,11 +156,176 @@ MessagePtr TwitchMessageBuilder::build()
|
||||||
for (QString emote : emoteString) {
|
for (QString emote : emoteString) {
|
||||||
this->appendTwitchEmote(emote, twitchEmotes);
|
this->appendTwitchEmote(emote, twitchEmotes);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(
|
|
||||||
twitchEmotes.begin(), twitchEmotes.end(),
|
|
||||||
[](const auto &a, const auto &b) { return a.first < b.first; });
|
|
||||||
}
|
}
|
||||||
|
auto app = getApp();
|
||||||
|
const auto &phrases = app->ignores->phrases.getVector();
|
||||||
|
auto removeEmotesInRange =
|
||||||
|
[](int pos, int len,
|
||||||
|
std::vector<std::tuple<int, EmotePtr, EmoteName>> &twitchEmotes) mutable {
|
||||||
|
auto it = std::partition(
|
||||||
|
twitchEmotes.begin(), twitchEmotes.end(), [pos, len](const auto &item) {
|
||||||
|
return !((std::get<0>(item) >= pos) && std::get<0>(item) < (pos + len));
|
||||||
|
});
|
||||||
|
for (auto copy = it; copy != twitchEmotes.end(); ++copy) {
|
||||||
|
if (std::get<1>(*copy) == nullptr) {
|
||||||
|
log("remem nullptr {}", std::get<2>(*copy).string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<std::tuple<int, EmotePtr, EmoteName>> 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 = std::get<0>(item);
|
||||||
|
if (index >= pos) {
|
||||||
|
index += 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) {
|
||||||
|
log("emote null {}", emote.first.string);
|
||||||
|
}
|
||||||
|
twitchEmotes.push_back(std::tuple<int, EmotePtr, EmoteName>{
|
||||||
|
startIndex + pos, emote.second, emote.first});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos += word.length() + 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto &phrase : phrases) {
|
||||||
|
if (phrase.isBlock()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (phrase.isRegex()) {
|
||||||
|
const auto ®ex = 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 (std::get<1>(tup) == nullptr) {
|
||||||
|
log("v nullptr {}", std::get<2>(tup).string);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int index = 0;
|
||||||
|
QString emote = " " + std::get<2>(tup).string + " ";
|
||||||
|
while ((index = midExtendedRef.indexOf(emote, index)) != -1) {
|
||||||
|
std::get<0>(tup) = from + index + 1;
|
||||||
|
index += emote.size() - 1;
|
||||||
|
twitchEmotes.push_back(tup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addReplEmotes(phrase, midExtendedRef, pos1);
|
||||||
|
|
||||||
|
from += midsize;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const auto &pattern = phrase.getPattern();
|
||||||
|
if (pattern.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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 (std::get<1>(tup) == nullptr) {
|
||||||
|
log("v nullptr {}", std::get<2>(tup).string);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int index = 0;
|
||||||
|
QString emote = " " + std::get<2>(tup).string + " ";
|
||||||
|
while ((index = midExtendedRef.indexOf(emote, index)) != -1) {
|
||||||
|
std::get<0>(tup) = from + index + 1;
|
||||||
|
index += emote.size() - 1;
|
||||||
|
twitchEmotes.push_back(tup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addReplEmotes(phrase, midExtendedRef, pos1);
|
||||||
|
|
||||||
|
from += replacesize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(twitchEmotes.begin(), twitchEmotes.end(),
|
||||||
|
[](const auto &a, const auto &b) { return std::get<0>(a) < std::get<0>(b); });
|
||||||
|
twitchEmotes.erase(std::unique(twitchEmotes.begin(), twitchEmotes.end(),
|
||||||
|
[](const auto &first, const auto &second) {
|
||||||
|
return std::get<0>(first) == std::get<0>(second);
|
||||||
|
}),
|
||||||
|
twitchEmotes.end());
|
||||||
|
auto currentTwitchEmote = twitchEmotes.begin();
|
||||||
|
|
||||||
// words
|
// words
|
||||||
QStringList splits = this->originalMessage_.split(' ');
|
QStringList splits = this->originalMessage_.split(' ');
|
||||||
|
@ -175,19 +338,22 @@ MessagePtr TwitchMessageBuilder::build()
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchMessageBuilder::addWords(
|
void TwitchMessageBuilder::addWords(
|
||||||
const QStringList &words,
|
const QStringList &words, const std::vector<std::tuple<int, EmotePtr, EmoteName>> &twitchEmotes)
|
||||||
const std::vector<std::pair<int, EmotePtr>> &twitchEmotes)
|
|
||||||
{
|
{
|
||||||
auto i = int();
|
auto i = int();
|
||||||
auto currentTwitchEmote = twitchEmotes.begin();
|
auto currentTwitchEmote = twitchEmotes.begin();
|
||||||
|
|
||||||
for (const auto &word : words) {
|
for (const auto &word : words) {
|
||||||
// check if it's a twitch emote twitch emote
|
// check if it's a twitch emote twitch emote
|
||||||
if (currentTwitchEmote != twitchEmotes.end() &&
|
while (currentTwitchEmote != twitchEmotes.end() && std::get<0>(*currentTwitchEmote) < i) {
|
||||||
currentTwitchEmote->first == i) {
|
++currentTwitchEmote;
|
||||||
auto emoteImage = currentTwitchEmote->second;
|
}
|
||||||
this->emplace<EmoteElement>(emoteImage,
|
if (currentTwitchEmote != twitchEmotes.end() && std::get<0>(*currentTwitchEmote) == i) {
|
||||||
MessageElementFlag::TwitchEmote);
|
auto emoteImage = std::get<1>(*currentTwitchEmote);
|
||||||
|
if (emoteImage == nullptr) {
|
||||||
|
log("emoteImage nullptr {}", std::get<2>(*currentTwitchEmote).string);
|
||||||
|
}
|
||||||
|
this->emplace<EmoteElement>(emoteImage, MessageElementFlag::TwitchEmote);
|
||||||
|
|
||||||
i += word.length() + 1;
|
i += word.length() + 1;
|
||||||
currentTwitchEmote++;
|
currentTwitchEmote++;
|
||||||
|
@ -197,8 +363,7 @@ void TwitchMessageBuilder::addWords(
|
||||||
|
|
||||||
// split words
|
// split words
|
||||||
for (auto &variant : getApp()->emotes->emojis.parse(word)) {
|
for (auto &variant : getApp()->emotes->emojis.parse(word)) {
|
||||||
boost::apply_visitor([&](auto &&arg) { this->addTextOrEmoji(arg); },
|
boost::apply_visitor([&](auto &&arg) { this->addTextOrEmoji(arg); }, variant);
|
||||||
variant);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j = 0; j < word.size(); j++) {
|
for (int j = 0; j < word.size(); j++) {
|
||||||
|
@ -240,18 +405,16 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
|
||||||
// Actually just text
|
// Actually just text
|
||||||
auto linkString = this->matchLink(string);
|
auto linkString = this->matchLink(string);
|
||||||
auto link = Link();
|
auto link = Link();
|
||||||
auto textColor = this->action_ ? MessageColor(this->usernameColor_)
|
auto textColor =
|
||||||
: MessageColor(MessageColor::Text);
|
this->action_ ? MessageColor(this->usernameColor_) : MessageColor(MessageColor::Text);
|
||||||
|
|
||||||
if (linkString.isEmpty()) {
|
if (linkString.isEmpty()) {
|
||||||
if (string.startsWith('@')) {
|
if (string.startsWith('@')) {
|
||||||
this->emplace<TextElement>(string, MessageElementFlag::BoldUsername,
|
this->emplace<TextElement>(string, MessageElementFlag::BoldUsername, textColor,
|
||||||
textColor, FontStyle::ChatMediumBold);
|
FontStyle::ChatMediumBold);
|
||||||
this->emplace<TextElement>(
|
this->emplace<TextElement>(string, MessageElementFlag::NonBoldUsername, textColor);
|
||||||
string, MessageElementFlag::NonBoldUsername, textColor);
|
|
||||||
} else {
|
} else {
|
||||||
this->emplace<TextElement>(string, MessageElementFlag::Text,
|
this->emplace<TextElement>(string, MessageElementFlag::Text, textColor);
|
||||||
textColor);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
static QRegularExpression domainRegex(
|
static QRegularExpression domainRegex(
|
||||||
|
@ -262,8 +425,7 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
|
||||||
auto match = domainRegex.match(string);
|
auto match = domainRegex.match(string);
|
||||||
if (match.isValid()) {
|
if (match.isValid()) {
|
||||||
lowercaseLinkString = string.mid(0, match.capturedStart(1)) +
|
lowercaseLinkString = string.mid(0, match.capturedStart(1)) +
|
||||||
match.captured(1).toLower() +
|
match.captured(1).toLower() + string.mid(match.capturedEnd(1));
|
||||||
string.mid(match.capturedEnd(1));
|
|
||||||
} else {
|
} else {
|
||||||
lowercaseLinkString = string;
|
lowercaseLinkString = string;
|
||||||
}
|
}
|
||||||
|
@ -271,28 +433,24 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
|
||||||
|
|
||||||
textColor = MessageColor(MessageColor::Link);
|
textColor = MessageColor(MessageColor::Link);
|
||||||
auto linkMELowercase =
|
auto linkMELowercase =
|
||||||
this->emplace<TextElement>(lowercaseLinkString,
|
this->emplace<TextElement>(lowercaseLinkString, MessageElementFlag::LowercaseLink,
|
||||||
MessageElementFlag::LowercaseLink,
|
|
||||||
textColor)
|
textColor)
|
||||||
->setLink(link);
|
->setLink(link);
|
||||||
auto linkMEOriginal =
|
auto linkMEOriginal =
|
||||||
this->emplace<TextElement>(string, MessageElementFlag::OriginalLink,
|
this->emplace<TextElement>(string, MessageElementFlag::OriginalLink, textColor)
|
||||||
textColor)
|
|
||||||
->setLink(link);
|
->setLink(link);
|
||||||
|
|
||||||
LinkResolver::getLinkInfo(
|
LinkResolver::getLinkInfo(linkString, [linkMELowercase, linkMEOriginal, linkString](
|
||||||
linkString, [linkMELowercase, linkMEOriginal, linkString]
|
QString tooltipText, Link originalLink) {
|
||||||
(QString tooltipText, Link originalLink) {
|
if (!tooltipText.isEmpty()) {
|
||||||
if (!tooltipText.isEmpty()) {
|
linkMELowercase->setTooltip(tooltipText);
|
||||||
linkMELowercase->setTooltip(tooltipText);
|
linkMEOriginal->setTooltip(tooltipText);
|
||||||
linkMEOriginal->setTooltip(tooltipText);
|
}
|
||||||
}
|
if (originalLink.value != linkString && !originalLink.value.isEmpty()) {
|
||||||
if (originalLink.value != linkString &&
|
linkMELowercase->setLink(originalLink)->updateLink();
|
||||||
!originalLink.value.isEmpty()) {
|
linkMEOriginal->setLink(originalLink)->updateLink();
|
||||||
linkMELowercase->setLink(originalLink)->updateLink();
|
}
|
||||||
linkMEOriginal->setLink(originalLink)->updateLink();
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (!linkString.isEmpty()) {
|
// if (!linkString.isEmpty()) {
|
||||||
|
@ -400,11 +558,9 @@ void TwitchMessageBuilder::appendUsername()
|
||||||
|
|
||||||
auto iterator = this->tags.find("display-name");
|
auto iterator = this->tags.find("display-name");
|
||||||
if (iterator != this->tags.end()) {
|
if (iterator != this->tags.end()) {
|
||||||
QString displayName =
|
QString displayName = parseTagString(iterator.value().toString()).trimmed();
|
||||||
parseTagString(iterator.value().toString()).trimmed();
|
|
||||||
|
|
||||||
if (QString::compare(displayName, this->userName,
|
if (QString::compare(displayName, this->userName, Qt::CaseInsensitive) == 0) {
|
||||||
Qt::CaseInsensitive) == 0) {
|
|
||||||
username = displayName;
|
username = displayName;
|
||||||
|
|
||||||
this->message().displayName = displayName;
|
this->message().displayName = displayName;
|
||||||
|
@ -422,8 +578,7 @@ void TwitchMessageBuilder::appendUsername()
|
||||||
QString usernameText;
|
QString usernameText;
|
||||||
|
|
||||||
pajlada::Settings::Setting<int> usernameDisplayMode(
|
pajlada::Settings::Setting<int> usernameDisplayMode(
|
||||||
"/appearance/messages/usernameDisplayMode",
|
"/appearance/messages/usernameDisplayMode", UsernameDisplayMode::UsernameAndLocalizedName);
|
||||||
UsernameDisplayMode::UsernameAndLocalizedName);
|
|
||||||
|
|
||||||
switch (usernameDisplayMode.getValue()) {
|
switch (usernameDisplayMode.getValue()) {
|
||||||
case UsernameDisplayMode::Username: {
|
case UsernameDisplayMode::Username: {
|
||||||
|
@ -454,8 +609,7 @@ void TwitchMessageBuilder::appendUsername()
|
||||||
// IrcManager::getInstance().getUser().getUserName();
|
// IrcManager::getInstance().getUser().getUserName();
|
||||||
} else if (this->args.isReceivedWhisper) {
|
} else if (this->args.isReceivedWhisper) {
|
||||||
// Sender username
|
// Sender username
|
||||||
this->emplace<TextElement>(usernameText, MessageElementFlag::Username,
|
this->emplace<TextElement>(usernameText, MessageElementFlag::Username, this->usernameColor_,
|
||||||
this->usernameColor_,
|
|
||||||
FontStyle::ChatMediumBold)
|
FontStyle::ChatMediumBold)
|
||||||
->setLink({Link::UserWhisper, this->message().displayName});
|
->setLink({Link::UserWhisper, this->message().displayName});
|
||||||
|
|
||||||
|
@ -463,8 +617,7 @@ void TwitchMessageBuilder::appendUsername()
|
||||||
|
|
||||||
// Separator
|
// Separator
|
||||||
this->emplace<TextElement>("->", MessageElementFlag::Username,
|
this->emplace<TextElement>("->", MessageElementFlag::Username,
|
||||||
app->themes->messages.textColors.system,
|
app->themes->messages.textColors.system, FontStyle::ChatMedium);
|
||||||
FontStyle::ChatMedium);
|
|
||||||
|
|
||||||
QColor selfColor = currentUser->color();
|
QColor selfColor = currentUser->color();
|
||||||
if (!selfColor.isValid()) {
|
if (!selfColor.isValid()) {
|
||||||
|
@ -472,16 +625,14 @@ void TwitchMessageBuilder::appendUsername()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Your own username
|
// Your own username
|
||||||
this->emplace<TextElement>(currentUser->getUserName() + ":",
|
this->emplace<TextElement>(currentUser->getUserName() + ":", MessageElementFlag::Username,
|
||||||
MessageElementFlag::Username, selfColor,
|
selfColor, FontStyle::ChatMediumBold);
|
||||||
FontStyle::ChatMediumBold);
|
|
||||||
} else {
|
} else {
|
||||||
if (!this->action_) {
|
if (!this->action_) {
|
||||||
usernameText += ":";
|
usernameText += ":";
|
||||||
}
|
}
|
||||||
|
|
||||||
this->emplace<TextElement>(usernameText, MessageElementFlag::Username,
|
this->emplace<TextElement>(usernameText, MessageElementFlag::Username, this->usernameColor_,
|
||||||
this->usernameColor_,
|
|
||||||
FontStyle::ChatMediumBold)
|
FontStyle::ChatMediumBold)
|
||||||
->setLink({Link::UserInfo, this->message().displayName});
|
->setLink({Link::UserInfo, this->message().displayName});
|
||||||
}
|
}
|
||||||
|
@ -507,8 +658,7 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
|
||||||
// update the media player url if necessary
|
// update the media player url if necessary
|
||||||
QUrl highlightSoundUrl;
|
QUrl highlightSoundUrl;
|
||||||
if (getSettings()->customHighlightSound) {
|
if (getSettings()->customHighlightSound) {
|
||||||
highlightSoundUrl =
|
highlightSoundUrl = QUrl::fromLocalFile(getSettings()->pathHighlightSound.getValue());
|
||||||
QUrl::fromLocalFile(getSettings()->pathHighlightSound.getValue());
|
|
||||||
} else {
|
} else {
|
||||||
highlightSoundUrl = QUrl("qrc:/sounds/ping2.wav");
|
highlightSoundUrl = QUrl("qrc:/sounds/ping2.wav");
|
||||||
}
|
}
|
||||||
|
@ -521,15 +671,12 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
|
||||||
|
|
||||||
// TODO: This vector should only be rebuilt upon highlights being changed
|
// TODO: This vector should only be rebuilt upon highlights being changed
|
||||||
// fourtf: should be implemented in the HighlightsController
|
// fourtf: should be implemented in the HighlightsController
|
||||||
std::vector<HighlightPhrase> activeHighlights =
|
std::vector<HighlightPhrase> activeHighlights = app->highlights->phrases.getVector();
|
||||||
app->highlights->phrases.getVector();
|
std::vector<HighlightPhrase> userHighlights = app->highlights->highlightedUsers.getVector();
|
||||||
std::vector<HighlightPhrase> userHighlights =
|
|
||||||
app->highlights->highlightedUsers.getVector();
|
|
||||||
|
|
||||||
if (getSettings()->enableSelfHighlight && currentUsername.size() > 0) {
|
if (getSettings()->enableSelfHighlight && currentUsername.size() > 0) {
|
||||||
HighlightPhrase selfHighlight(
|
HighlightPhrase selfHighlight(currentUsername, getSettings()->enableSelfHighlightTaskbar,
|
||||||
currentUsername, getSettings()->enableSelfHighlightTaskbar,
|
getSettings()->enableSelfHighlightSound, false);
|
||||||
getSettings()->enableSelfHighlightSound, false);
|
|
||||||
activeHighlights.emplace_back(std::move(selfHighlight));
|
activeHighlights.emplace_back(std::move(selfHighlight));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -564,8 +711,7 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
|
||||||
}
|
}
|
||||||
for (const HighlightPhrase &userHighlight : userHighlights) {
|
for (const HighlightPhrase &userHighlight : userHighlights) {
|
||||||
if (userHighlight.isMatch(this->ircMessage->nick())) {
|
if (userHighlight.isMatch(this->ircMessage->nick())) {
|
||||||
log("Highlight because user {} sent a message",
|
log("Highlight because user {} sent a message", this->ircMessage->nick());
|
||||||
this->ircMessage->nick());
|
|
||||||
doHighlight = true;
|
doHighlight = true;
|
||||||
|
|
||||||
if (userHighlight.getAlert()) {
|
if (userHighlight.getAlert()) {
|
||||||
|
@ -583,8 +729,7 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this->args.isReceivedWhisper &&
|
if (this->args.isReceivedWhisper && getSettings()->enableWhisperHighlight) {
|
||||||
getSettings()->enableWhisperHighlight) {
|
|
||||||
if (getSettings()->enableWhisperHighlightTaskbar) {
|
if (getSettings()->enableWhisperHighlightTaskbar) {
|
||||||
doAlert = true;
|
doAlert = true;
|
||||||
}
|
}
|
||||||
|
@ -596,22 +741,19 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
|
||||||
this->message().flags.set(MessageFlag::Highlighted, doHighlight);
|
this->message().flags.set(MessageFlag::Highlighted, doHighlight);
|
||||||
|
|
||||||
if (!isPastMsg) {
|
if (!isPastMsg) {
|
||||||
if (playSound &&
|
if (playSound && (!hasFocus || getSettings()->highlightAlwaysPlaySound)) {
|
||||||
(!hasFocus || getSettings()->highlightAlwaysPlaySound)) {
|
|
||||||
player->play();
|
player->play();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doAlert) {
|
if (doAlert) {
|
||||||
QApplication::alert(getApp()->windows->getMainWindow().window(),
|
QApplication::alert(getApp()->windows->getMainWindow().window(), 2500);
|
||||||
2500);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchMessageBuilder::appendTwitchEmote(
|
void TwitchMessageBuilder::appendTwitchEmote(const QString &emote,
|
||||||
const QString &emote,
|
std::vector<std::tuple<int, EmotePtr, EmoteName>> &vec)
|
||||||
std::vector<std::pair<int, EmotePtr>> &vec)
|
|
||||||
{
|
{
|
||||||
auto app = getApp();
|
auto app = getApp();
|
||||||
if (!emote.contains(':')) {
|
if (!emote.contains(':')) {
|
||||||
|
@ -638,16 +780,17 @@ void TwitchMessageBuilder::appendTwitchEmote(
|
||||||
auto start = coords.at(0).toInt();
|
auto start = coords.at(0).toInt();
|
||||||
auto end = coords.at(1).toInt();
|
auto end = coords.at(1).toInt();
|
||||||
|
|
||||||
if (start >= end || start < 0 ||
|
if (start >= end || start < 0 || end > this->originalMessage_.length()) {
|
||||||
end > this->originalMessage_.length()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto name =
|
auto name = EmoteName{this->originalMessage_.mid(start, end - start + 1)};
|
||||||
EmoteName{this->originalMessage_.mid(start, end - start + 1)};
|
auto tup = std::tuple<int, EmotePtr, EmoteName>{
|
||||||
|
start, app->emotes->twitch.getOrCreateEmote(id, name), name};
|
||||||
vec.push_back(std::make_pair(
|
if (std::get<1>(tup) == nullptr) {
|
||||||
start, app->emotes->twitch.getOrCreateEmote(id, name)));
|
log("nullptr {}", std::get<2>(tup).string);
|
||||||
|
}
|
||||||
|
vec.push_back(std::move(tup));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -686,7 +829,8 @@ void TwitchMessageBuilder::appendTwitchBadges()
|
||||||
auto app = getApp();
|
auto app = getApp();
|
||||||
|
|
||||||
auto iterator = this->tags.find("badges");
|
auto iterator = this->tags.find("badges");
|
||||||
if (iterator == this->tags.end()) return;
|
if (iterator == this->tags.end())
|
||||||
|
return;
|
||||||
|
|
||||||
for (QString badge : iterator.value().toString().split(',')) {
|
for (QString badge : iterator.value().toString().split(',')) {
|
||||||
if (badge.startsWith("bits/")) {
|
if (badge.startsWith("bits/")) {
|
||||||
|
@ -696,10 +840,8 @@ void TwitchMessageBuilder::appendTwitchBadges()
|
||||||
// Try to fetch channel-specific bit badge
|
// Try to fetch channel-specific bit badge
|
||||||
try {
|
try {
|
||||||
if (twitchChannel)
|
if (twitchChannel)
|
||||||
if (const auto &badge = this->twitchChannel->twitchBadge(
|
if (const auto &badge = this->twitchChannel->twitchBadge("bits", cheerAmount)) {
|
||||||
"bits", cheerAmount)) {
|
this->emplace<EmoteElement>(badge.get(), MessageElementFlag::BadgeVanity)
|
||||||
this->emplace<EmoteElement>(
|
|
||||||
badge.get(), MessageElementFlag::BadgeVanity)
|
|
||||||
->setTooltip(tooltip);
|
->setTooltip(tooltip);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -708,55 +850,45 @@ void TwitchMessageBuilder::appendTwitchBadges()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use default bit badge
|
// Use default bit badge
|
||||||
if (auto badge = this->twitchChannel->globalTwitchBadges().badge(
|
if (auto badge = this->twitchChannel->globalTwitchBadges().badge("bits", cheerAmount)) {
|
||||||
"bits", cheerAmount)) {
|
this->emplace<EmoteElement>(badge.get(), MessageElementFlag::BadgeVanity)
|
||||||
this->emplace<EmoteElement>(badge.get(),
|
|
||||||
MessageElementFlag::BadgeVanity)
|
|
||||||
->setTooltip(tooltip);
|
->setTooltip(tooltip);
|
||||||
}
|
}
|
||||||
} else if (badge == "staff/1") {
|
} else if (badge == "staff/1") {
|
||||||
this->emplace<ImageElement>(
|
this->emplace<ImageElement>(Image::fromPixmap(app->resources->twitch.staff),
|
||||||
Image::fromPixmap(app->resources->twitch.staff),
|
MessageElementFlag::BadgeGlobalAuthority)
|
||||||
MessageElementFlag::BadgeGlobalAuthority)
|
|
||||||
->setTooltip("Twitch Staff");
|
->setTooltip("Twitch Staff");
|
||||||
} else if (badge == "admin/1") {
|
} else if (badge == "admin/1") {
|
||||||
this->emplace<ImageElement>(
|
this->emplace<ImageElement>(Image::fromPixmap(app->resources->twitch.admin),
|
||||||
Image::fromPixmap(app->resources->twitch.admin),
|
MessageElementFlag::BadgeGlobalAuthority)
|
||||||
MessageElementFlag::BadgeGlobalAuthority)
|
|
||||||
->setTooltip("Twitch Admin");
|
->setTooltip("Twitch Admin");
|
||||||
} else if (badge == "global_mod/1") {
|
} else if (badge == "global_mod/1") {
|
||||||
this->emplace<ImageElement>(
|
this->emplace<ImageElement>(Image::fromPixmap(app->resources->twitch.globalmod),
|
||||||
Image::fromPixmap(app->resources->twitch.globalmod),
|
MessageElementFlag::BadgeGlobalAuthority)
|
||||||
MessageElementFlag::BadgeGlobalAuthority)
|
|
||||||
->setTooltip("Twitch Global Moderator");
|
->setTooltip("Twitch Global Moderator");
|
||||||
} else if (badge == "moderator/1") {
|
} else if (badge == "moderator/1") {
|
||||||
// TODO: Implement custom FFZ moderator badge
|
// TODO: Implement custom FFZ moderator badge
|
||||||
this->emplace<ImageElement>(
|
this->emplace<ImageElement>(Image::fromPixmap(app->resources->twitch.moderator),
|
||||||
Image::fromPixmap(app->resources->twitch.moderator),
|
MessageElementFlag::BadgeChannelAuthority)
|
||||||
MessageElementFlag::BadgeChannelAuthority)
|
|
||||||
->setTooltip("Twitch Channel Moderator");
|
->setTooltip("Twitch Channel Moderator");
|
||||||
} else if (badge == "turbo/1") {
|
} else if (badge == "turbo/1") {
|
||||||
this->emplace<ImageElement>(
|
this->emplace<ImageElement>(Image::fromPixmap(app->resources->twitch.turbo),
|
||||||
Image::fromPixmap(app->resources->twitch.turbo),
|
MessageElementFlag::BadgeGlobalAuthority)
|
||||||
MessageElementFlag::BadgeGlobalAuthority)
|
|
||||||
->setTooltip("Twitch Turbo Subscriber");
|
->setTooltip("Twitch Turbo Subscriber");
|
||||||
} else if (badge == "broadcaster/1") {
|
} else if (badge == "broadcaster/1") {
|
||||||
this->emplace<ImageElement>(
|
this->emplace<ImageElement>(Image::fromPixmap(app->resources->twitch.broadcaster),
|
||||||
Image::fromPixmap(app->resources->twitch.broadcaster),
|
MessageElementFlag::BadgeChannelAuthority)
|
||||||
MessageElementFlag::BadgeChannelAuthority)
|
|
||||||
->setTooltip("Twitch Broadcaster");
|
->setTooltip("Twitch Broadcaster");
|
||||||
} else if (badge == "premium/1") {
|
} else if (badge == "premium/1") {
|
||||||
this->emplace<ImageElement>(
|
this->emplace<ImageElement>(Image::fromPixmap(app->resources->twitch.prime),
|
||||||
Image::fromPixmap(app->resources->twitch.prime),
|
MessageElementFlag::BadgeVanity)
|
||||||
MessageElementFlag::BadgeVanity)
|
|
||||||
->setTooltip("Twitch Prime Subscriber");
|
->setTooltip("Twitch Prime Subscriber");
|
||||||
} else if (badge.startsWith("partner/")) {
|
} else if (badge.startsWith("partner/")) {
|
||||||
int index = badge.midRef(8).toInt();
|
int index = badge.midRef(8).toInt();
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case 1: {
|
case 1: {
|
||||||
this->emplace<ImageElement>(
|
this->emplace<ImageElement>(
|
||||||
Image::fromPixmap(app->resources->twitch.verified,
|
Image::fromPixmap(app->resources->twitch.verified, 0.25),
|
||||||
0.25),
|
|
||||||
MessageElementFlag::BadgeVanity)
|
MessageElementFlag::BadgeVanity)
|
||||||
->setTooltip("Twitch Verified");
|
->setTooltip("Twitch Verified");
|
||||||
} break;
|
} break;
|
||||||
|
@ -767,27 +899,23 @@ void TwitchMessageBuilder::appendTwitchBadges()
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
} else if (badge.startsWith("subscriber/")) {
|
} else if (badge.startsWith("subscriber/")) {
|
||||||
if (auto badgeEmote = this->twitchChannel->twitchBadge(
|
if (auto badgeEmote = this->twitchChannel->twitchBadge("subscriber", badge.mid(11))) {
|
||||||
"subscriber", badge.mid(11))) {
|
this->emplace<EmoteElement>(badgeEmote.get(), MessageElementFlag::BadgeSubscription)
|
||||||
this->emplace<EmoteElement>(
|
|
||||||
badgeEmote.get(), MessageElementFlag::BadgeSubscription)
|
|
||||||
->setTooltip((*badgeEmote)->tooltip.string);
|
->setTooltip((*badgeEmote)->tooltip.string);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// use default subscriber badge if custom one not found
|
// use default subscriber badge if custom one not found
|
||||||
this->emplace<ImageElement>(
|
this->emplace<ImageElement>(Image::fromPixmap(app->resources->twitch.subscriber, 0.25),
|
||||||
Image::fromPixmap(app->resources->twitch.subscriber, 0.25),
|
MessageElementFlag::BadgeSubscription)
|
||||||
MessageElementFlag::BadgeSubscription)
|
|
||||||
->setTooltip("Twitch Subscriber");
|
->setTooltip("Twitch Subscriber");
|
||||||
} else {
|
} else {
|
||||||
auto splits = badge.split('/');
|
auto splits = badge.split('/');
|
||||||
if (splits.size() != 2) continue;
|
if (splits.size() != 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (auto badgeEmote =
|
if (auto badgeEmote = this->twitchChannel->twitchBadge(splits[0], splits[1])) {
|
||||||
this->twitchChannel->twitchBadge(splits[0], splits[1])) {
|
this->emplace<EmoteElement>(badgeEmote.get(), MessageElementFlag::BadgeVanity)
|
||||||
this->emplace<EmoteElement>(badgeEmote.get(),
|
|
||||||
MessageElementFlag::BadgeVanity)
|
|
||||||
->setTooltip((*badgeEmote)->tooltip.string);
|
->setTooltip((*badgeEmote)->tooltip.string);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -797,11 +925,9 @@ void TwitchMessageBuilder::appendTwitchBadges()
|
||||||
|
|
||||||
void TwitchMessageBuilder::appendChatterinoBadges()
|
void TwitchMessageBuilder::appendChatterinoBadges()
|
||||||
{
|
{
|
||||||
auto chatterinoBadgePtr =
|
auto chatterinoBadgePtr = getApp()->chatterinoBadges->getBadge({this->userName});
|
||||||
getApp()->chatterinoBadges->getBadge({this->userName});
|
|
||||||
if (chatterinoBadgePtr) {
|
if (chatterinoBadgePtr) {
|
||||||
this->emplace<EmoteElement>(*chatterinoBadgePtr,
|
this->emplace<EmoteElement>(*chatterinoBadgePtr, MessageElementFlag::BadgeChatterino);
|
||||||
MessageElementFlag::BadgeChatterino);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,13 +27,10 @@ public:
|
||||||
|
|
||||||
TwitchMessageBuilder() = delete;
|
TwitchMessageBuilder() = delete;
|
||||||
|
|
||||||
explicit TwitchMessageBuilder(Channel *_channel,
|
explicit TwitchMessageBuilder(Channel *_channel, const Communi::IrcPrivateMessage *_ircMessage,
|
||||||
const Communi::IrcPrivateMessage *_ircMessage,
|
|
||||||
const MessageParseArgs &_args);
|
const MessageParseArgs &_args);
|
||||||
explicit TwitchMessageBuilder(Channel *_channel,
|
explicit TwitchMessageBuilder(Channel *_channel, const Communi::IrcMessage *_ircMessage,
|
||||||
const Communi::IrcMessage *_ircMessage,
|
const MessageParseArgs &_args, QString content, bool isAction);
|
||||||
const MessageParseArgs &_args,
|
|
||||||
QString content, bool isAction);
|
|
||||||
|
|
||||||
Channel *channel;
|
Channel *channel;
|
||||||
TwitchChannel *twitchChannel;
|
TwitchChannel *twitchChannel;
|
||||||
|
@ -56,11 +53,11 @@ private:
|
||||||
void parseHighlights(bool isPastMsg);
|
void parseHighlights(bool isPastMsg);
|
||||||
|
|
||||||
void appendTwitchEmote(const QString &emote,
|
void appendTwitchEmote(const QString &emote,
|
||||||
std::vector<std::pair<int, EmotePtr>> &vec);
|
std::vector<std::tuple<int, EmotePtr, EmoteName>> &vec);
|
||||||
Outcome tryAppendEmote(const EmoteName &name);
|
Outcome tryAppendEmote(const EmoteName &name);
|
||||||
|
|
||||||
void addWords(const QStringList &words,
|
void addWords(const QStringList &words,
|
||||||
const std::vector<std::pair<int, EmotePtr>> &twitchEmotes);
|
const std::vector<std::tuple<int, EmotePtr, EmoteName>> &twitchEmotes);
|
||||||
void addTextOrEmoji(EmotePtr emote);
|
void addTextOrEmoji(EmotePtr emote);
|
||||||
void addTextOrEmoji(const QString &value);
|
void addTextOrEmoji(const QString &value);
|
||||||
|
|
||||||
|
@ -72,7 +69,7 @@ private:
|
||||||
bool hasBits_ = false;
|
bool hasBits_ = false;
|
||||||
|
|
||||||
QColor usernameColor_;
|
QColor usernameColor_;
|
||||||
const QString originalMessage_;
|
QString originalMessage_;
|
||||||
bool senderIsBroadcaster{};
|
bool senderIsBroadcaster{};
|
||||||
|
|
||||||
const bool action_ = false;
|
const bool action_ = false;
|
||||||
|
|
|
@ -116,6 +116,9 @@ public:
|
||||||
BoolSetting enableUnshortLinks = {"/links/unshortLinks", false};
|
BoolSetting enableUnshortLinks = {"/links/unshortLinks", false};
|
||||||
BoolSetting enableLowercaseLink = {"/links/linkLowercase", true};
|
BoolSetting enableLowercaseLink = {"/links/linkLowercase", true};
|
||||||
|
|
||||||
|
/// Ignored phrases
|
||||||
|
QStringSetting ignoredPhraseReplace = {"/ignore/ignoredPhraseReplace", "***"};
|
||||||
|
|
||||||
/// Ingored Users
|
/// Ingored Users
|
||||||
BoolSetting enableTwitchIgnoredUsers = {"/ignore/enableTwitchIgnoredUsers",
|
BoolSetting enableTwitchIgnoredUsers = {"/ignore/enableTwitchIgnoredUsers",
|
||||||
true};
|
true};
|
||||||
|
|
|
@ -25,8 +25,7 @@
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
static void addPhrasesTab(LayoutCreator<QVBoxLayout> box);
|
static void addPhrasesTab(LayoutCreator<QVBoxLayout> box);
|
||||||
static void addUsersTab(IgnoresPage &page, LayoutCreator<QVBoxLayout> box,
|
static void addUsersTab(IgnoresPage &page, LayoutCreator<QVBoxLayout> box, QStringListModel &model);
|
||||||
QStringListModel &model);
|
|
||||||
|
|
||||||
IgnoresPage::IgnoresPage()
|
IgnoresPage::IgnoresPage()
|
||||||
: SettingsPage("Ignores", "")
|
: SettingsPage("Ignores", "")
|
||||||
|
@ -36,8 +35,7 @@ IgnoresPage::IgnoresPage()
|
||||||
auto tabs = layout.emplace<QTabWidget>();
|
auto tabs = layout.emplace<QTabWidget>();
|
||||||
|
|
||||||
addPhrasesTab(tabs.appendTab(new QVBoxLayout, "Phrases"));
|
addPhrasesTab(tabs.appendTab(new QVBoxLayout, "Phrases"));
|
||||||
addUsersTab(*this, tabs.appendTab(new QVBoxLayout, "Users"),
|
addUsersTab(*this, tabs.appendTab(new QVBoxLayout, "Users"), this->userListModel_);
|
||||||
this->userListModel_);
|
|
||||||
|
|
||||||
auto label = layout.emplace<QLabel>(INFO);
|
auto label = layout.emplace<QLabel>(INFO);
|
||||||
label->setWordWrap(true);
|
label->setWordWrap(true);
|
||||||
|
@ -47,14 +45,10 @@ IgnoresPage::IgnoresPage()
|
||||||
void addPhrasesTab(LayoutCreator<QVBoxLayout> layout)
|
void addPhrasesTab(LayoutCreator<QVBoxLayout> layout)
|
||||||
{
|
{
|
||||||
EditableModelView *view =
|
EditableModelView *view =
|
||||||
layout
|
layout.emplace<EditableModelView>(getApp()->ignores->createModel(nullptr)).getElement();
|
||||||
.emplace<EditableModelView>(getApp()->ignores->createModel(nullptr))
|
view->setTitles({"Pattern", "Regex", "Case Sensitive", "Block", "Replacement"});
|
||||||
.getElement();
|
view->getTableView()->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
|
||||||
view->setTitles({"Pattern", "Regex"});
|
view->getTableView()->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||||
view->getTableView()->horizontalHeader()->setSectionResizeMode(
|
|
||||||
QHeaderView::Fixed);
|
|
||||||
view->getTableView()->horizontalHeader()->setSectionResizeMode(
|
|
||||||
0, QHeaderView::Stretch);
|
|
||||||
|
|
||||||
QTimer::singleShot(1, [view] {
|
QTimer::singleShot(1, [view] {
|
||||||
view->getTableView()->resizeColumnsToContents();
|
view->getTableView()->resizeColumnsToContents();
|
||||||
|
@ -62,12 +56,12 @@ void addPhrasesTab(LayoutCreator<QVBoxLayout> layout)
|
||||||
});
|
});
|
||||||
|
|
||||||
view->addButtonPressed.connect([] {
|
view->addButtonPressed.connect([] {
|
||||||
getApp()->ignores->phrases.appendItem(IgnorePhrase{"my phrase", false});
|
getApp()->ignores->phrases.appendItem(IgnorePhrase{
|
||||||
|
"my phrase", false, false, getSettings()->ignoredPhraseReplace.getValue(), true});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void addUsersTab(IgnoresPage &page, LayoutCreator<QVBoxLayout> users,
|
void addUsersTab(IgnoresPage &page, LayoutCreator<QVBoxLayout> users, QStringListModel &userModel)
|
||||||
QStringListModel &userModel)
|
|
||||||
{
|
{
|
||||||
users.append(page.createCheckBox("Enable twitch ignored users",
|
users.append(page.createCheckBox("Enable twitch ignored users",
|
||||||
getSettings()->enableTwitchIgnoredUsers));
|
getSettings()->enableTwitchIgnoredUsers));
|
||||||
|
|
Loading…
Reference in a new issue