mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Implement simple ignored phrase system
This commit is contained in:
parent
3a48f5db96
commit
15b432a4cf
12 changed files with 266 additions and 31 deletions
|
@ -191,6 +191,8 @@ SOURCES += \
|
|||
src/controllers/commands/commandcontroller.cpp \
|
||||
src/controllers/highlights/highlightcontroller.cpp \
|
||||
src/controllers/highlights/highlightmodel.cpp \
|
||||
src/controllers/ignores/ignorecontroller.cpp \
|
||||
src/controllers/ignores/ignoremodel.cpp \
|
||||
src/widgets/helper/editablemodelview.cpp \
|
||||
src/controllers/accounts/accountcontroller.cpp \
|
||||
src/controllers/accounts/accountmodel.cpp \
|
||||
|
@ -333,6 +335,9 @@ HEADERS += \
|
|||
src/controllers/highlights/highlightcontroller.hpp \
|
||||
src/controllers/highlights/highlightphrase.hpp \
|
||||
src/controllers/highlights/highlightmodel.hpp \
|
||||
src/controllers/ignores/ignorecontroller.hpp \
|
||||
src/controllers/ignores/ignorephrase.hpp \
|
||||
src/controllers/ignores/ignoremodel.hpp \
|
||||
src/widgets/helper/editablemodelview.hpp \
|
||||
src/controllers/accounts/accountcontroller.hpp \
|
||||
src/controllers/accounts/accountmodel.hpp \
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "controllers/commands/commandcontroller.hpp"
|
||||
#include "controllers/highlights/highlightcontroller.hpp"
|
||||
#include "controllers/ignores/ignorecontroller.hpp"
|
||||
#include "providers/twitch/pubsub.hpp"
|
||||
#include "providers/twitch/twitchserver.hpp"
|
||||
#include "singletons/accountmanager.hpp"
|
||||
|
@ -66,6 +67,7 @@ void Application::construct()
|
|||
this->logging = new singletons::LoggingManager;
|
||||
this->commands = new controllers::commands::CommandController;
|
||||
this->highlights = new controllers::highlights::HighlightController;
|
||||
this->ignores = new controllers::ignores::IgnoreController;
|
||||
this->accounts = new singletons::AccountManager;
|
||||
this->emotes = new singletons::EmoteManager;
|
||||
this->settings = new singletons::SettingManager;
|
||||
|
@ -100,6 +102,7 @@ void Application::initialize()
|
|||
this->resources->initialize();
|
||||
|
||||
this->highlights->initialize();
|
||||
this->ignores->initialize();
|
||||
|
||||
this->emotes->initialize();
|
||||
|
||||
|
|
|
@ -23,7 +23,10 @@ class CommandController;
|
|||
namespace highlights {
|
||||
class HighlightController;
|
||||
}
|
||||
namespace ignores {
|
||||
class IgnoreController;
|
||||
}
|
||||
} // namespace controllers
|
||||
|
||||
namespace singletons {
|
||||
|
||||
|
@ -63,6 +66,7 @@ public:
|
|||
singletons::LoggingManager *logging = nullptr;
|
||||
controllers::commands::CommandController *commands = nullptr;
|
||||
controllers::highlights::HighlightController *highlights = nullptr;
|
||||
controllers::ignores::IgnoreController *ignores = nullptr;
|
||||
singletons::AccountManager *accounts = nullptr;
|
||||
singletons::EmoteManager *emotes = nullptr;
|
||||
singletons::NativeMessagingManager *nativeMessaging = nullptr;
|
||||
|
|
36
src/controllers/ignores/ignorecontroller.cpp
Normal file
36
src/controllers/ignores/ignorecontroller.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "controllers/ignores/ignorecontroller.hpp"
|
||||
|
||||
#include "application.hpp"
|
||||
#include "controllers/ignores/ignoremodel.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace chatterino {
|
||||
namespace controllers {
|
||||
namespace ignores {
|
||||
|
||||
void IgnoreController::initialize()
|
||||
{
|
||||
assert(!this->initialized);
|
||||
this->initialized = true;
|
||||
|
||||
for (const IgnorePhrase &phrase : this->ignoresSetting.getValue()) {
|
||||
this->phrases.appendItem(phrase);
|
||||
}
|
||||
|
||||
this->phrases.delayedItemsChanged.connect([this] { //
|
||||
this->ignoresSetting.setValue(this->phrases.getVector());
|
||||
});
|
||||
}
|
||||
|
||||
IgnoreModel *IgnoreController::createModel(QObject *parent)
|
||||
{
|
||||
IgnoreModel *model = new IgnoreModel(parent);
|
||||
model->init(&this->phrases);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
} // namespace ignores
|
||||
} // namespace controllers
|
||||
} // namespace chatterino
|
31
src/controllers/ignores/ignorecontroller.hpp
Normal file
31
src/controllers/ignores/ignorecontroller.hpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "controllers/ignores/ignorephrase.hpp"
|
||||
#include "singletons/settingsmanager.hpp"
|
||||
#include "util/signalvector2.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace controllers {
|
||||
namespace ignores {
|
||||
|
||||
class IgnoreModel;
|
||||
|
||||
class IgnoreController
|
||||
{
|
||||
public:
|
||||
void initialize();
|
||||
|
||||
util::UnsortedSignalVector<IgnorePhrase> phrases;
|
||||
|
||||
IgnoreModel *createModel(QObject *parent);
|
||||
|
||||
private:
|
||||
bool initialized = false;
|
||||
|
||||
singletons::ChatterinoSetting<std::vector<ignores::IgnorePhrase>> ignoresSetting = {
|
||||
"/ignore/phrases"};
|
||||
};
|
||||
|
||||
} // namespace ignores
|
||||
} // namespace controllers
|
||||
} // namespace chatterino
|
35
src/controllers/ignores/ignoremodel.cpp
Normal file
35
src/controllers/ignores/ignoremodel.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include "ignoremodel.hpp"
|
||||
|
||||
#include "application.hpp"
|
||||
#include "singletons/settingsmanager.hpp"
|
||||
#include "util/standarditemhelper.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace controllers {
|
||||
namespace ignores {
|
||||
|
||||
// commandmodel
|
||||
IgnoreModel::IgnoreModel(QObject *parent)
|
||||
: util::SignalVectorModel<IgnorePhrase>(2, parent)
|
||||
{
|
||||
}
|
||||
|
||||
// turn a vector item into a model row
|
||||
IgnorePhrase IgnoreModel::getItemFromRow(std::vector<QStandardItem *> &row)
|
||||
{
|
||||
// key, regex
|
||||
|
||||
return IgnorePhrase{row[0]->data(Qt::DisplayRole).toString(),
|
||||
row[1]->data(Qt::CheckStateRole).toBool()};
|
||||
}
|
||||
|
||||
// turns a row in the model into a vector item
|
||||
void IgnoreModel::getRowFromItem(const IgnorePhrase &item, std::vector<QStandardItem *> &row)
|
||||
{
|
||||
util::setStringItem(row[0], item.getPattern());
|
||||
util::setBoolItem(row[1], item.isRegex());
|
||||
}
|
||||
|
||||
} // namespace ignores
|
||||
} // namespace controllers
|
||||
} // namespace chatterino
|
31
src/controllers/ignores/ignoremodel.hpp
Normal file
31
src/controllers/ignores/ignoremodel.hpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "controllers/ignores/ignorephrase.hpp"
|
||||
#include "util/signalvectormodel.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace controllers {
|
||||
namespace ignores {
|
||||
|
||||
class IgnoreController;
|
||||
|
||||
class IgnoreModel : public util::SignalVectorModel<IgnorePhrase>
|
||||
{
|
||||
explicit IgnoreModel(QObject *parent);
|
||||
|
||||
protected:
|
||||
// turn a vector item into a model row
|
||||
virtual IgnorePhrase getItemFromRow(std::vector<QStandardItem *> &row) override;
|
||||
|
||||
// turns a row in the model into a vector item
|
||||
virtual void getRowFromItem(const IgnorePhrase &item,
|
||||
std::vector<QStandardItem *> &row) override;
|
||||
|
||||
friend class IgnoreController;
|
||||
};
|
||||
|
||||
} // namespace ignores
|
||||
} // namespace controllers
|
||||
} // namespace chatterino
|
95
src/controllers/ignores/ignorephrase.hpp
Normal file
95
src/controllers/ignores/ignorephrase.hpp
Normal file
|
@ -0,0 +1,95 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/rapidjson-helpers.hpp"
|
||||
#include "util/serialize-custom.hpp"
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
#include <pajlada/settings/serialize.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace chatterino {
|
||||
namespace controllers {
|
||||
namespace ignores {
|
||||
|
||||
class IgnorePhrase
|
||||
{
|
||||
QString pattern;
|
||||
bool _isRegex;
|
||||
QRegularExpression regex;
|
||||
|
||||
public:
|
||||
bool operator==(const IgnorePhrase &other) const
|
||||
{
|
||||
return std::tie(this->pattern, this->_isRegex) == std::tie(other.pattern, other._isRegex);
|
||||
}
|
||||
|
||||
IgnorePhrase(const QString &_pattern, bool isRegex)
|
||||
: pattern(_pattern)
|
||||
, _isRegex(isRegex)
|
||||
, regex(_isRegex ? _pattern : "\\b" + QRegularExpression::escape(_pattern) + "\\b",
|
||||
QRegularExpression::CaseInsensitiveOption)
|
||||
{
|
||||
}
|
||||
|
||||
const QString &getPattern() const
|
||||
{
|
||||
return this->pattern;
|
||||
}
|
||||
bool isRegex() const
|
||||
{
|
||||
return this->_isRegex;
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return !this->pattern.isEmpty() && this->regex.isValid();
|
||||
}
|
||||
|
||||
bool isMatch(const QString &subject) const
|
||||
{
|
||||
return this->isValid() && this->regex.match(subject).hasMatch();
|
||||
}
|
||||
};
|
||||
} // namespace ignores
|
||||
} // namespace controllers
|
||||
} // namespace chatterino
|
||||
|
||||
namespace pajlada {
|
||||
namespace Settings {
|
||||
|
||||
template <>
|
||||
struct Serialize<chatterino::controllers::ignores::IgnorePhrase> {
|
||||
static rapidjson::Value get(const chatterino::controllers::ignores::IgnorePhrase &value,
|
||||
rapidjson::Document::AllocatorType &a)
|
||||
{
|
||||
rapidjson::Value ret(rapidjson::kObjectType);
|
||||
|
||||
AddMember(ret, "pattern", value.getPattern(), a);
|
||||
AddMember(ret, "regex", value.isRegex(), a);
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Deserialize<chatterino::controllers::ignores::IgnorePhrase> {
|
||||
static chatterino::controllers::ignores::IgnorePhrase get(const rapidjson::Value &value)
|
||||
{
|
||||
if (!value.IsObject()) {
|
||||
return chatterino::controllers::ignores::IgnorePhrase(QString(), false);
|
||||
}
|
||||
|
||||
QString _pattern;
|
||||
bool _isRegex = false;
|
||||
|
||||
chatterino::rj::getSafe(value, "pattern", _pattern);
|
||||
chatterino::rj::getSafe(value, "regex", _isRegex);
|
||||
|
||||
return chatterino::controllers::ignores::IgnorePhrase(_pattern, _isRegex);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Settings
|
||||
} // namespace pajlada
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "application.hpp"
|
||||
#include "controllers/highlights/highlightcontroller.hpp"
|
||||
#include "controllers/ignores/ignorecontroller.hpp"
|
||||
#include "debug/log.hpp"
|
||||
#include "providers/twitch/twitchchannel.hpp"
|
||||
#include "singletons/accountmanager.hpp"
|
||||
|
@ -54,10 +55,12 @@ TwitchMessageBuilder::TwitchMessageBuilder(Channel *_channel,
|
|||
bool TwitchMessageBuilder::isIgnored() const
|
||||
{
|
||||
auto app = getApp();
|
||||
std::shared_ptr<std::vector<QString>> ignoredKeywords = app->settings->getIgnoredKeywords();
|
||||
|
||||
for (const QString &keyword : *ignoredKeywords) {
|
||||
if (this->originalMessage.contains(keyword, Qt::CaseInsensitive)) {
|
||||
// TODO(pajlada): Do we need to check if the phrase is valid first?
|
||||
for (const auto &phrase : app->ignores->phrases.getVector()) {
|
||||
if (phrase.isMatch(this->originalMessage)) {
|
||||
debug::Log("Blocking message because it contains ignored phrase {}",
|
||||
phrase.getPattern());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ void _actuallyRegisterSetting(std::weak_ptr<pajlada::Settings::ISettingData> set
|
|||
}
|
||||
|
||||
SettingManager::SettingManager()
|
||||
: _ignoredKeywords(new std::vector<QString>)
|
||||
{
|
||||
qDebug() << "init SettingManager";
|
||||
|
||||
|
@ -37,7 +36,6 @@ SettingManager::SettingManager()
|
|||
void SettingManager::initialize()
|
||||
{
|
||||
this->moderationActions.connect([this](auto, auto) { this->updateModerationActions(); });
|
||||
this->ignoredKeywords.connect([this](auto, auto) { this->updateIgnoredKeywords(); });
|
||||
|
||||
this->timestampFormat.connect([](auto, auto) {
|
||||
auto app = getApp();
|
||||
|
@ -151,11 +149,6 @@ std::vector<ModerationAction> SettingManager::getModerationActions() const
|
|||
return this->_moderationActions;
|
||||
}
|
||||
|
||||
const std::shared_ptr<std::vector<QString>> SettingManager::getIgnoredKeywords() const
|
||||
{
|
||||
return this->_ignoredKeywords;
|
||||
}
|
||||
|
||||
void SettingManager::updateModerationActions()
|
||||
{
|
||||
auto app = getApp();
|
||||
|
@ -224,21 +217,5 @@ void SettingManager::updateModerationActions()
|
|||
}
|
||||
}
|
||||
|
||||
void SettingManager::updateIgnoredKeywords()
|
||||
{
|
||||
static QRegularExpression newLineRegex("(\r\n?|\n)+");
|
||||
|
||||
auto items = new std::vector<QString>();
|
||||
|
||||
for (const QString &line : this->ignoredKeywords.getValue().split(newLineRegex)) {
|
||||
QString line2 = line.trimmed();
|
||||
|
||||
if (!line2.isEmpty()) {
|
||||
items->push_back(line2);
|
||||
}
|
||||
}
|
||||
|
||||
this->_ignoredKeywords = std::shared_ptr<std::vector<QString>>(items);
|
||||
}
|
||||
} // namespace singletons
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -86,7 +86,6 @@ public:
|
|||
|
||||
/// Ingored Users
|
||||
BoolSetting enableTwitchIgnoredUsers = {"/ignore/enableTwitchIgnoredUsers", true};
|
||||
QStringSetting ignoredKeywords = {"/ignore/ignoredKeywords", ""};
|
||||
|
||||
/// Moderation
|
||||
QStringSetting moderationActions = {"/moderation/actions", "/ban {user}\n/timeout {user} 300"};
|
||||
|
@ -123,16 +122,13 @@ public:
|
|||
void recallSnapshot();
|
||||
|
||||
std::vector<ModerationAction> getModerationActions() const;
|
||||
const std::shared_ptr<std::vector<QString>> getIgnoredKeywords() const;
|
||||
pajlada::Signals::NoArgSignal wordFlagsChanged;
|
||||
|
||||
private:
|
||||
std::vector<ModerationAction> _moderationActions;
|
||||
std::unique_ptr<rapidjson::Document> snapshot;
|
||||
std::shared_ptr<std::vector<QString>> _ignoredKeywords;
|
||||
|
||||
void updateModerationActions();
|
||||
void updateIgnoredKeywords();
|
||||
|
||||
messages::MessageElement::Flags wordFlags = messages::MessageElement::Default;
|
||||
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
#include "ignoreuserspage.hpp"
|
||||
|
||||
#include "application.hpp"
|
||||
#include "controllers/ignores/ignorecontroller.hpp"
|
||||
#include "controllers/ignores/ignoremodel.hpp"
|
||||
#include "singletons/accountmanager.hpp"
|
||||
#include "singletons/settingsmanager.hpp"
|
||||
#include "util/layoutcreator.hpp"
|
||||
#include "widgets/helper/editablemodelview.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QListView>
|
||||
#include <QPushButton>
|
||||
#include <QTableView>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
// clang-format off
|
||||
|
@ -60,7 +64,22 @@ IgnoreUsersPage::IgnoreUsersPage()
|
|||
// messages
|
||||
auto messages = tabs.appendTab(new QVBoxLayout, "Messages");
|
||||
{
|
||||
messages.emplace<QLabel>("wip");
|
||||
helper::EditableModelView *view =
|
||||
*messages.emplace<helper::EditableModelView>(app->ignores->createModel(nullptr));
|
||||
view->setTitles({"Pattern", "Regex"});
|
||||
view->getTableView()->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
|
||||
view->getTableView()->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
|
||||
// fourtf: make class extrend BaseWidget and add this to dpiChanged
|
||||
QTimer::singleShot(1, [view] {
|
||||
view->getTableView()->resizeColumnsToContents();
|
||||
view->getTableView()->setColumnWidth(0, 200);
|
||||
});
|
||||
|
||||
view->addButtonPressed.connect([] {
|
||||
getApp()->ignores->phrases.appendItem(
|
||||
controllers::ignores::IgnorePhrase{"my phrase", false});
|
||||
});
|
||||
}
|
||||
|
||||
auto label = layout.emplace<QLabel>(INFO);
|
||||
|
|
Loading…
Reference in a new issue