mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Makes it possible to hide one man spam (#1496)
This commit is contained in:
parent
497ce2d2f2
commit
1fd64be7f5
9 changed files with 164 additions and 1 deletions
|
@ -3,6 +3,7 @@
|
||||||
#include "Application.hpp"
|
#include "Application.hpp"
|
||||||
#include "messages/Message.hpp"
|
#include "messages/Message.hpp"
|
||||||
#include "messages/MessageBuilder.hpp"
|
#include "messages/MessageBuilder.hpp"
|
||||||
|
#include "providers/twitch/IrcMessageHandler.hpp"
|
||||||
#include "singletons/Emotes.hpp"
|
#include "singletons/Emotes.hpp"
|
||||||
#include "singletons/Logging.hpp"
|
#include "singletons/Logging.hpp"
|
||||||
#include "singletons/Settings.hpp"
|
#include "singletons/Settings.hpp"
|
||||||
|
|
|
@ -33,6 +33,7 @@ enum class MessageFlag : uint32_t {
|
||||||
Whisper = (1 << 16),
|
Whisper = (1 << 16),
|
||||||
HighlightedWhisper = (1 << 17),
|
HighlightedWhisper = (1 << 17),
|
||||||
Debug = (1 << 18),
|
Debug = (1 << 18),
|
||||||
|
Similar = (1 << 19),
|
||||||
};
|
};
|
||||||
using MessageFlags = FlagsEnum<MessageFlag>;
|
using MessageFlags = FlagsEnum<MessageFlag>;
|
||||||
|
|
||||||
|
|
|
@ -141,6 +141,12 @@ void MessageLayout::actuallyLayout(int width, MessageElementFlags flags)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getSettings()->hideSimilar &&
|
||||||
|
this->message_->flags.has(MessageFlag::Similar))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
element->addToContainer(*this->container_, flags);
|
element->addToContainer(*this->container_, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,97 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
|
static float relativeSimilarity(const QString &str1, const QString &str2)
|
||||||
|
{
|
||||||
|
// Longest Common Substring Problem
|
||||||
|
std::vector<std::vector<int>> tree(str1.size(),
|
||||||
|
std::vector<int>(str2.size(), 0));
|
||||||
|
int z = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < str1.size(); ++i)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < str2.size(); ++j)
|
||||||
|
{
|
||||||
|
if (str1[i] == str2[j])
|
||||||
|
{
|
||||||
|
if (i == 0 || j == 0)
|
||||||
|
{
|
||||||
|
tree[i][j] = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tree[i][j] = tree[i - 1][j - 1] + 1;
|
||||||
|
}
|
||||||
|
if (tree[i][j] > z)
|
||||||
|
{
|
||||||
|
z = tree[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tree[i][j] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return z == 0 ? 0.f : float(z) / std::max(str1.size(), str2.size());
|
||||||
|
};
|
||||||
|
|
||||||
|
float IrcMessageHandler::similarity(
|
||||||
|
MessagePtr msg, const LimitedQueueSnapshot<MessagePtr> &messages)
|
||||||
|
{
|
||||||
|
float similarityPercent = 0.0f;
|
||||||
|
int bySameUser = 0;
|
||||||
|
for (int i = 1; bySameUser < getSettings()->hideSimilarMaxMessagesToCheck;
|
||||||
|
++i)
|
||||||
|
{
|
||||||
|
if (messages.size() < i)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const auto &prevMsg = messages[messages.size() - i];
|
||||||
|
if (prevMsg->parseTime.secsTo(QTime::currentTime()) >=
|
||||||
|
getSettings()->hideSimilarMaxDelay)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (msg->loginName != prevMsg->loginName)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++bySameUser;
|
||||||
|
similarityPercent = std::max(
|
||||||
|
similarityPercent,
|
||||||
|
relativeSimilarity(msg->messageText, prevMsg->messageText));
|
||||||
|
}
|
||||||
|
return similarityPercent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IrcMessageHandler::setSimilarityFlags(MessagePtr msg, ChannelPtr chan)
|
||||||
|
{
|
||||||
|
if (getSettings()->similarityEnabled)
|
||||||
|
{
|
||||||
|
bool isMyself = msg->loginName ==
|
||||||
|
getApp()->accounts->twitch.getCurrent()->getUserName();
|
||||||
|
bool hideMyself = getSettings()->hideSimilarMyself;
|
||||||
|
|
||||||
|
if (isMyself && !hideMyself)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IrcMessageHandler::similarity(msg, chan->getMessageSnapshot()) >
|
||||||
|
getSettings()->similarityPercentage)
|
||||||
|
{
|
||||||
|
msg->flags.set(MessageFlag::Similar, true);
|
||||||
|
if (getSettings()->colorSimilarDisabled)
|
||||||
|
{
|
||||||
|
msg->flags.set(MessageFlag::Disabled, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static QMap<QString, QString> parseBadges(QString badgesString)
|
static QMap<QString, QString> parseBadges(QString badgesString)
|
||||||
{
|
{
|
||||||
QMap<QString, QString> badges;
|
QMap<QString, QString> badges;
|
||||||
|
@ -133,7 +224,16 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message,
|
||||||
}
|
}
|
||||||
|
|
||||||
auto msg = builder.build();
|
auto msg = builder.build();
|
||||||
builder.triggerHighlights();
|
|
||||||
|
IrcMessageHandler::setSimilarityFlags(msg, chan);
|
||||||
|
|
||||||
|
if (!msg->flags.has(MessageFlag::Similar) ||
|
||||||
|
(!getSettings()->hideSimilar &&
|
||||||
|
getSettings()->shownSimilarTriggerHighlights))
|
||||||
|
{
|
||||||
|
builder.triggerHighlights();
|
||||||
|
}
|
||||||
|
|
||||||
auto highlighted = msg->flags.has(MessageFlag::Highlighted);
|
auto highlighted = msg->flags.has(MessageFlag::Highlighted);
|
||||||
|
|
||||||
if (!isSub)
|
if (!isSub)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <IrcMessage>
|
#include <IrcMessage>
|
||||||
|
#include "common/Channel.hpp"
|
||||||
#include "messages/Message.hpp"
|
#include "messages/Message.hpp"
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
@ -49,6 +50,10 @@ public:
|
||||||
void handleJoinMessage(Communi::IrcMessage *message);
|
void handleJoinMessage(Communi::IrcMessage *message);
|
||||||
void handlePartMessage(Communi::IrcMessage *message);
|
void handlePartMessage(Communi::IrcMessage *message);
|
||||||
|
|
||||||
|
static float similarity(MessagePtr msg,
|
||||||
|
const LimitedQueueSnapshot<MessagePtr> &messages);
|
||||||
|
static void setSimilarityFlags(MessagePtr message, ChannelPtr channel);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addMessage(Communi::IrcMessage *message, const QString &target,
|
void addMessage(Communi::IrcMessage *message, const QString &target,
|
||||||
const QString &content, TwitchIrcServer &server,
|
const QString &content, TwitchIrcServer &server,
|
||||||
|
|
|
@ -249,6 +249,20 @@ public:
|
||||||
IntSetting lastSelectChannelTab = {"/ui/lastSelectChannelTab", 0};
|
IntSetting lastSelectChannelTab = {"/ui/lastSelectChannelTab", 0};
|
||||||
IntSetting lastSelectIrcConn = {"/ui/lastSelectIrcConn", 0};
|
IntSetting lastSelectIrcConn = {"/ui/lastSelectIrcConn", 0};
|
||||||
|
|
||||||
|
// Similarity
|
||||||
|
BoolSetting similarityEnabled = {"/similarity/similarityEnabled", false};
|
||||||
|
BoolSetting colorSimilarDisabled = {"/similarity/colorSimilarDisabled",
|
||||||
|
true};
|
||||||
|
BoolSetting hideSimilar = {"/similarity/hideSimilar", false};
|
||||||
|
BoolSetting hideSimilarMyself = {"/similarity/hideSimilarMyself", false};
|
||||||
|
BoolSetting shownSimilarTriggerHighlights = {
|
||||||
|
"/similarity/shownSimilarTriggerHighlights", false};
|
||||||
|
FloatSetting similarityPercentage = {"/similarity/similarityPercentage",
|
||||||
|
0.9f};
|
||||||
|
IntSetting hideSimilarMaxDelay = {"/similarity/hideSimilarMaxDelay", 5};
|
||||||
|
IntSetting hideSimilarMaxMessagesToCheck = {
|
||||||
|
"/similarity/hideSimilarMaxMessagesToCheck", 3};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateModerationActions();
|
void updateModerationActions();
|
||||||
};
|
};
|
||||||
|
|
|
@ -351,6 +351,11 @@ void Window::addShortcuts()
|
||||||
getApp()->twitch.server->getOrAddChannel(si.channelName));
|
getApp()->twitch.server->getOrAddChannel(si.channelName));
|
||||||
splitContainer->appendSplit(split);
|
splitContainer->appendSplit(split);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
createWindowShortcut(this, "CTRL+H", [this] {
|
||||||
|
getSettings()->hideSimilar.setValue(!getSettings()->hideSimilar);
|
||||||
|
getApp()->windows->forceLayoutChannelViews();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::addMenuBar()
|
void Window::addMenuBar()
|
||||||
|
|
|
@ -547,6 +547,33 @@ void GeneralPage::initLayout(SettingsLayout &layout)
|
||||||
QDesktopServices::openUrl(getPaths()->rootAppDataDirectory);
|
QDesktopServices::openUrl(getPaths()->rootAppDataDirectory);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
layout.addTitle("Similarity");
|
||||||
|
layout.addDescription(
|
||||||
|
"Hides or grays out similar messages from the same user");
|
||||||
|
layout.addCheckbox("Similarity enabled", s.similarityEnabled);
|
||||||
|
layout.addCheckbox("Gray out similar messages", s.colorSimilarDisabled);
|
||||||
|
layout.addCheckbox("Hide similar messages (Ctrl + H)", s.hideSimilar);
|
||||||
|
layout.addCheckbox("Hide or gray out my own similar messages",
|
||||||
|
s.hideSimilarMyself);
|
||||||
|
layout.addCheckbox("Shown similar messages trigger highlights",
|
||||||
|
s.shownSimilarTriggerHighlights);
|
||||||
|
layout.addDropdown<float>(
|
||||||
|
"Similarity percentage", {"0.5", "0.75", "0.9"}, s.similarityPercentage,
|
||||||
|
[](auto val) { return QString::number(val); },
|
||||||
|
[](auto args) { return fuzzyToFloat(args.value, 0.9f); });
|
||||||
|
s.hideSimilar.connect(
|
||||||
|
[]() { getApp()->windows->forceLayoutChannelViews(); }, false);
|
||||||
|
layout.addDropdown<int>(
|
||||||
|
"Similar messages max delay in seconds",
|
||||||
|
{"5", "10", "15", "30", "60", "120"}, s.hideSimilarMaxDelay,
|
||||||
|
[](auto val) { return QString::number(val); },
|
||||||
|
[](auto args) { return fuzzyToInt(args.value, 5); });
|
||||||
|
layout.addDropdown<int>(
|
||||||
|
"Similar messages max previous messages to check",
|
||||||
|
{"1", "2", "3", "4", "5"}, s.hideSimilarMaxMessagesToCheck,
|
||||||
|
[](auto val) { return QString::number(val); },
|
||||||
|
[](auto args) { return fuzzyToInt(args.value, 3); });
|
||||||
|
|
||||||
// invisible element for width
|
// invisible element for width
|
||||||
auto inv = new BaseWidget(this);
|
auto inv = new BaseWidget(this);
|
||||||
inv->setScaleIndependantWidth(500);
|
inv->setScaleIndependantWidth(500);
|
||||||
|
|
|
@ -26,6 +26,10 @@ KeyboardSettingsPage::KeyboardSettingsPage()
|
||||||
form->addRow(new QLabel("Ctrl + Shift + T"), new QLabel("Create new tab"));
|
form->addRow(new QLabel("Ctrl + Shift + T"), new QLabel("Create new tab"));
|
||||||
form->addRow(new QLabel("Ctrl + Shift + W"),
|
form->addRow(new QLabel("Ctrl + Shift + W"),
|
||||||
new QLabel("Close current tab"));
|
new QLabel("Close current tab"));
|
||||||
|
form->addRow(
|
||||||
|
new QLabel("Ctrl + H"),
|
||||||
|
new QLabel(
|
||||||
|
"Hide/Show similar messages (Enable in General under Similarity)"));
|
||||||
|
|
||||||
form->addItem(new QSpacerItem(16, 16));
|
form->addItem(new QSpacerItem(16, 16));
|
||||||
form->addRow(new QLabel("Ctrl + 1/2/3/..."),
|
form->addRow(new QLabel("Ctrl + 1/2/3/..."),
|
||||||
|
|
Loading…
Reference in a new issue