Squashed commit of the following:
commit ea07bbef0be589cc5412bff0a25735ac713128e3 Merge:0b36f4365cfcf114 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: e8e059f88bcc9c48Author: 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: e7f2f39763eaf3b9Author: 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
This commit is contained in:
+1
-1
Submodule lib/settings updated: bcbd29b793...7f0db95f24
+1
-1
Submodule lib/signals updated: a1b61606c1...e03c868ec9
@@ -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));
|
||||||
|
|||||||
Reference in New Issue
Block a user