Merge pull request #925 from apa420/apa-automod-implementation
Automod implementation
This commit is contained in:
@@ -1,77 +1,77 @@
|
|||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/">
|
<qresource prefix="/"> <file>chatterino2.icns</file>
|
||||||
<file>chatterino2.icns</file>
|
<file>contributors.txt</file>
|
||||||
<file>contributors.txt</file>
|
<file>emoji.json</file>
|
||||||
<file>emoji.json</file>
|
<file>emojidata.txt</file>
|
||||||
<file>emojidata.txt</file>
|
<file>error.png</file>
|
||||||
<file>error.png</file>
|
<file>icon.ico</file>
|
||||||
<file>icon.ico</file>
|
<file>icon.png</file>
|
||||||
<file>icon.png</file>
|
<file>pajaDank.png</file>
|
||||||
<file>pajaDank.png</file>
|
<file>tlds.txt</file>
|
||||||
<file>tlds.txt</file>
|
<file>avatars/fourtf.png</file>
|
||||||
<file>avatars/fourtf.png</file>
|
<file>avatars/pajlada.png</file>
|
||||||
<file>avatars/pajlada.png</file>
|
<file>buttons/addSplit.png</file>
|
||||||
<file>buttons/addSplit.png</file>
|
<file>buttons/addSplitDark.png</file>
|
||||||
<file>buttons/addSplitDark.png</file>
|
<file>buttons/ban.png</file>
|
||||||
<file>buttons/ban.png</file>
|
<file>buttons/banRed.png</file>
|
||||||
<file>buttons/banRed.png</file>
|
<file>buttons/emote.svg</file>
|
||||||
<file>buttons/emote.svg</file>
|
<file>buttons/emoteDark.svg</file>
|
||||||
<file>buttons/emoteDark.svg</file>
|
<file>buttons/menuDark.png</file>
|
||||||
<file>buttons/menuDark.png</file>
|
<file>buttons/menuLight.png</file>
|
||||||
<file>buttons/menuLight.png</file>
|
<file>buttons/mod.png</file>
|
||||||
<file>buttons/mod.png</file>
|
<file>buttons/modModeDisabled.png</file>
|
||||||
<file>buttons/modModeDisabled.png</file>
|
<file>buttons/modModeDisabled2.png</file>
|
||||||
<file>buttons/modModeDisabled2.png</file>
|
<file>buttons/modModeEnabled.png</file>
|
||||||
<file>buttons/modModeEnabled.png</file>
|
<file>buttons/modModeEnabled2.png</file>
|
||||||
<file>buttons/modModeEnabled2.png</file>
|
<file>buttons/timeout.png</file>
|
||||||
<file>buttons/timeout.png</file>
|
<file>buttons/unban.png</file>
|
||||||
<file>buttons/unban.png</file>
|
<file>buttons/unmod.png</file>
|
||||||
<file>buttons/unmod.png</file>
|
<file>buttons/update.png</file>
|
||||||
<file>buttons/update.png</file>
|
<file>buttons/updateError.png</file>
|
||||||
<file>buttons/updateError.png</file>
|
<file>examples/moving.gif</file>
|
||||||
<file>examples/moving.gif</file>
|
<file>examples/splitting.gif</file>
|
||||||
<file>examples/splitting.gif</file>
|
<file>licenses/boost_boost.txt</file>
|
||||||
<file>licenses/boost_boost.txt</file>
|
<file>licenses/emoji-data-source.txt</file>
|
||||||
<file>licenses/emoji-data-source.txt</file>
|
<file>licenses/fmt_bsd2.txt</file>
|
||||||
<file>licenses/fmt_bsd2.txt</file>
|
<file>licenses/libcommuni_BSD3.txt</file>
|
||||||
<file>licenses/libcommuni_BSD3.txt</file>
|
<file>licenses/openssl.txt</file>
|
||||||
<file>licenses/openssl.txt</file>
|
<file>licenses/pajlada_settings.txt</file>
|
||||||
<file>licenses/pajlada_settings.txt</file>
|
<file>licenses/pajlada_signals.txt</file>
|
||||||
<file>licenses/pajlada_signals.txt</file>
|
<file>licenses/qt_lgpl-3.0.txt</file>
|
||||||
<file>licenses/qt_lgpl-3.0.txt</file>
|
<file>licenses/rapidjson.txt</file>
|
||||||
<file>licenses/rapidjson.txt</file>
|
<file>licenses/websocketpp.txt</file>
|
||||||
<file>licenses/websocketpp.txt</file>
|
<file>qss/settings.qss</file>
|
||||||
<file>qss/settings.qss</file>
|
<file>settings/about.svg</file>
|
||||||
<file>settings/about.svg</file>
|
<file>settings/aboutlogo.png</file>
|
||||||
<file>settings/aboutlogo.png</file>
|
<file>settings/accounts.svg</file>
|
||||||
<file>settings/accounts.svg</file>
|
<file>settings/advanced.svg</file>
|
||||||
<file>settings/behave.svg</file>
|
<file>settings/behave.svg</file>
|
||||||
<file>settings/commands.svg</file>
|
<file>settings/browser.svg</file>
|
||||||
<file>settings/emote.svg</file>
|
<file>settings/commands.svg</file>
|
||||||
<file>settings/notifications.svg</file>
|
<file>settings/emote.svg</file>
|
||||||
<file>settings/theme.svg</file>
|
<file>settings/externaltools.svg</file>
|
||||||
<file>sounds/ping2.wav</file>
|
<file>settings/ignore.svg</file>
|
||||||
<file>split/down.png</file>
|
<file>settings/keybinds.svg</file>
|
||||||
<file>split/left.png</file>
|
<file>settings/moderation.svg</file>
|
||||||
<file>split/move.png</file>
|
<file>settings/notification2.svg</file>
|
||||||
<file>split/right.png</file>
|
<file>settings/notifications.svg</file>
|
||||||
<file>split/up.png</file>
|
<file>settings/theme.svg</file>
|
||||||
<file>twitch/admin.png</file>
|
<file>sounds/ping2.wav</file>
|
||||||
<file>twitch/broadcaster.png</file>
|
<file>split/down.png</file>
|
||||||
<file>twitch/cheer1.png</file>
|
<file>split/left.png</file>
|
||||||
<file>twitch/globalmod.png</file>
|
<file>split/move.png</file>
|
||||||
<file>twitch/moderator.png</file>
|
<file>split/right.png</file>
|
||||||
<file>twitch/prime.png</file>
|
<file>split/up.png</file>
|
||||||
<file>twitch/staff.png</file>
|
<file>twitch/admin.png</file>
|
||||||
<file>twitch/subscriber.png</file>
|
<file>twitch/automod.png</file>
|
||||||
<file>twitch/turbo.png</file>
|
<file>twitch/broadcaster.png</file>
|
||||||
<file>twitch/verified.png</file>
|
<file>twitch/cheer1.png</file>
|
||||||
<file>settings/ignore.svg</file>
|
<file>twitch/globalmod.png</file>
|
||||||
<file>settings/keybinds.svg</file>
|
<file>twitch/moderator.png</file>
|
||||||
<file>settings/moderation.svg</file>
|
<file>twitch/prime.png</file>
|
||||||
<file>settings/notification2.svg</file>
|
<file>twitch/staff.png</file>
|
||||||
<file>settings/browser.svg</file>
|
<file>twitch/subscriber.png</file>
|
||||||
<file>settings/externaltools.svg</file>
|
<file>twitch/turbo.png</file>
|
||||||
<file>settings/advanced.svg</file>
|
<file>twitch/verified.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 305 B |
@@ -234,6 +234,38 @@ void Application::initPubsub()
|
|||||||
postToThread([chan, msg] { chan->addMessage(msg); });
|
postToThread([chan, msg] { chan->addMessage(msg); });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this->twitch.pubsub->signals_.moderation.automodMessage.connect(
|
||||||
|
[&](const auto &action) {
|
||||||
|
auto chan =
|
||||||
|
this->twitch.server->getChannelOrEmptyByID(action.roomID);
|
||||||
|
|
||||||
|
if (chan->isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
postToThread([chan, action] {
|
||||||
|
auto p = makeAutomodMessage(action);
|
||||||
|
chan->addMessage(p.first);
|
||||||
|
chan->addMessage(p.second);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this->twitch.pubsub->signals_.moderation.automodUserMessage.connect(
|
||||||
|
[&](const auto &action) {
|
||||||
|
auto chan =
|
||||||
|
this->twitch.server->getChannelOrEmptyByID(action.roomID);
|
||||||
|
|
||||||
|
if (chan->isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto msg = MessageBuilder(action).release();
|
||||||
|
|
||||||
|
postToThread([chan, msg] { chan->addMessage(msg); });
|
||||||
|
});
|
||||||
|
|
||||||
this->twitch.pubsub->start();
|
this->twitch.pubsub->start();
|
||||||
|
|
||||||
auto RequestModerationActions = [=]() {
|
auto RequestModerationActions = [=]() {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ Resources2::Resources2()
|
|||||||
this->split.right = QPixmap(":/split/right.png");
|
this->split.right = QPixmap(":/split/right.png");
|
||||||
this->split.up = QPixmap(":/split/up.png");
|
this->split.up = QPixmap(":/split/up.png");
|
||||||
this->twitch.admin = QPixmap(":/twitch/admin.png");
|
this->twitch.admin = QPixmap(":/twitch/admin.png");
|
||||||
|
this->twitch.automod = QPixmap(":/twitch/automod.png");
|
||||||
this->twitch.broadcaster = QPixmap(":/twitch/broadcaster.png");
|
this->twitch.broadcaster = QPixmap(":/twitch/broadcaster.png");
|
||||||
this->twitch.cheer1 = QPixmap(":/twitch/cheer1.png");
|
this->twitch.cheer1 = QPixmap(":/twitch/cheer1.png");
|
||||||
this->twitch.globalmod = QPixmap(":/twitch/globalmod.png");
|
this->twitch.globalmod = QPixmap(":/twitch/globalmod.png");
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
class Resources2 : public Singleton
|
class Resources2 : public Singleton {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
Resources2();
|
Resources2();
|
||||||
|
|
||||||
@@ -45,6 +44,7 @@ public:
|
|||||||
} split;
|
} split;
|
||||||
struct {
|
struct {
|
||||||
QPixmap admin;
|
QPixmap admin;
|
||||||
|
QPixmap automod;
|
||||||
QPixmap broadcaster;
|
QPixmap broadcaster;
|
||||||
QPixmap cheer1;
|
QPixmap cheer1;
|
||||||
QPixmap globalmod;
|
QPixmap globalmod;
|
||||||
|
|||||||
@@ -147,6 +147,12 @@ void NetworkRequest::execute()
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NetworkRequestType::Post:
|
||||||
|
{
|
||||||
|
this->doRequest();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
log("[Execute] Unhandled request type");
|
log("[Execute] Unhandled request type");
|
||||||
@@ -217,6 +223,10 @@ void NetworkRequest::doRequest()
|
|||||||
return NetworkManager::accessManager.deleteResource(
|
return NetworkManager::accessManager.deleteResource(
|
||||||
data->request_);
|
data->request_);
|
||||||
|
|
||||||
|
case NetworkRequestType::Post:
|
||||||
|
return NetworkManager::accessManager.post(data->request_,
|
||||||
|
data->payload_);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ public:
|
|||||||
InsertText,
|
InsertText,
|
||||||
ShowMessage,
|
ShowMessage,
|
||||||
UserAction,
|
UserAction,
|
||||||
|
AutoModAllow,
|
||||||
|
AutoModDeny,
|
||||||
};
|
};
|
||||||
|
|
||||||
Link();
|
Link();
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ enum class MessageFlag : uint16_t {
|
|||||||
PubSub = (1 << 11),
|
PubSub = (1 << 11),
|
||||||
Subscription = (1 << 12),
|
Subscription = (1 << 12),
|
||||||
Notification = (1 << 13),
|
Notification = (1 << 13),
|
||||||
|
AutoMod = (1 << 14),
|
||||||
};
|
};
|
||||||
using MessageFlags = FlagsEnum<MessageFlag>;
|
using MessageFlags = FlagsEnum<MessageFlag>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "MessageBuilder.hpp"
|
#include "MessageBuilder.hpp"
|
||||||
|
|
||||||
|
#include "Application.hpp"
|
||||||
#include "common/LinkParser.hpp"
|
#include "common/LinkParser.hpp"
|
||||||
|
#include "messages/Image.hpp"
|
||||||
#include "messages/Message.hpp"
|
#include "messages/Message.hpp"
|
||||||
#include "messages/MessageElement.hpp"
|
#include "messages/MessageElement.hpp"
|
||||||
#include "providers/twitch/PubsubActions.hpp"
|
#include "providers/twitch/PubsubActions.hpp"
|
||||||
@@ -11,6 +13,7 @@
|
|||||||
#include "util/IrcHelpers.hpp"
|
#include "util/IrcHelpers.hpp"
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QImageReader>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
@@ -19,6 +22,61 @@ MessagePtr makeSystemMessage(const QString &text)
|
|||||||
return MessageBuilder(systemMessage, text).release();
|
return MessageBuilder(systemMessage, text).release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<MessagePtr, MessagePtr> makeAutomodMessage(
|
||||||
|
const AutomodAction &action)
|
||||||
|
{
|
||||||
|
auto builder = MessageBuilder();
|
||||||
|
|
||||||
|
builder.emplace<TimestampElement>();
|
||||||
|
builder.message().flags.set(MessageFlag::PubSub);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.emplace<ImageElement>(
|
||||||
|
Image::fromPixmap(getApp()->resources->twitch.automod),
|
||||||
|
MessageElementFlag::BadgeChannelAuthority)
|
||||||
|
->setTooltip("AutoMod");
|
||||||
|
builder.emplace<TextElement>(
|
||||||
|
"AutoMod:", MessageElementFlag::NonBoldUsername,
|
||||||
|
MessageColor(QColor("blue")));
|
||||||
|
builder.emplace<TextElement>(
|
||||||
|
("Held a message for reason: " + action.reason +
|
||||||
|
". Allow will post it in chat. "),
|
||||||
|
MessageElementFlag::Text, MessageColor::Text);
|
||||||
|
builder
|
||||||
|
.emplace<TextElement>("Allow", MessageElementFlag::Text,
|
||||||
|
MessageColor(QColor("green")),
|
||||||
|
FontStyle::ChatMediumBold)
|
||||||
|
->setLink({Link::AutoModAllow, action.msgID});
|
||||||
|
builder
|
||||||
|
.emplace<TextElement>(" Deny", MessageElementFlag::Text,
|
||||||
|
MessageColor(QColor("red")),
|
||||||
|
FontStyle::ChatMediumBold)
|
||||||
|
->setLink({Link::AutoModDeny, action.msgID});
|
||||||
|
// builder.emplace<TextElement>(action.msgID,
|
||||||
|
// MessageElementFlag::Text,
|
||||||
|
// MessageColor::Text);
|
||||||
|
builder.message().flags.set(MessageFlag::AutoMod);
|
||||||
|
|
||||||
|
auto message1 = builder.release();
|
||||||
|
|
||||||
|
builder = MessageBuilder();
|
||||||
|
builder.emplace<TimestampElement>();
|
||||||
|
builder.message().flags.set(MessageFlag::PubSub);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.emplace<TextElement>(action.target.name + ":",
|
||||||
|
MessageElementFlag::NonBoldUsername,
|
||||||
|
MessageColor(QColor("red")))
|
||||||
|
->setLink({Link::UserInfo, action.target.name});
|
||||||
|
builder.emplace<TextElement>(action.message, MessageElementFlag::Text,
|
||||||
|
MessageColor::Text);
|
||||||
|
builder.message().flags.set(MessageFlag::AutoMod);
|
||||||
|
|
||||||
|
auto message2 = builder.release();
|
||||||
|
|
||||||
|
return std::make_pair(message1, message2);
|
||||||
|
}
|
||||||
|
|
||||||
MessageBuilder::MessageBuilder()
|
MessageBuilder::MessageBuilder()
|
||||||
: message_(std::make_shared<Message>())
|
: message_(std::make_shared<Message>())
|
||||||
{
|
{
|
||||||
@@ -179,6 +237,47 @@ MessageBuilder::MessageBuilder(const UnbanAction &action)
|
|||||||
this->message().searchText = text;
|
this->message().searchText = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MessageBuilder::MessageBuilder(const AutomodUserAction &action)
|
||||||
|
: MessageBuilder()
|
||||||
|
{
|
||||||
|
this->emplace<TimestampElement>();
|
||||||
|
this->message().flags.set(MessageFlag::System);
|
||||||
|
|
||||||
|
QString text;
|
||||||
|
if (action.type == 1)
|
||||||
|
{
|
||||||
|
text = QString("%1 added %2 as a permitted term on AutoMod.")
|
||||||
|
.arg(action.source.name)
|
||||||
|
.arg(action.message);
|
||||||
|
}
|
||||||
|
else if (action.type == 2)
|
||||||
|
{
|
||||||
|
text = QString("%1 added %2 as a blocked term on AutoMod.")
|
||||||
|
.arg(action.source.name)
|
||||||
|
.arg(action.message);
|
||||||
|
}
|
||||||
|
else if (action.type == 3)
|
||||||
|
{
|
||||||
|
text = QString("%1 removed %2 as a permitted term term on AutoMod.")
|
||||||
|
.arg(action.source.name)
|
||||||
|
.arg(action.message);
|
||||||
|
}
|
||||||
|
else if (action.type == 4)
|
||||||
|
{
|
||||||
|
text = QString("%1 removed %2 as a blocked term on AutoMod.")
|
||||||
|
.arg(action.source.name)
|
||||||
|
.arg(action.message);
|
||||||
|
}
|
||||||
|
else if (action.type == 5)
|
||||||
|
{
|
||||||
|
text = QString("%1 modified the AutoMod properties.")
|
||||||
|
.arg(action.source.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->emplace<TextElement>(text, MessageElementFlag::Text,
|
||||||
|
MessageColor::System);
|
||||||
|
}
|
||||||
|
|
||||||
Message *MessageBuilder::operator->()
|
Message *MessageBuilder::operator->()
|
||||||
{
|
{
|
||||||
return this->message_.get();
|
return this->message_.get();
|
||||||
|
|||||||
@@ -4,10 +4,13 @@
|
|||||||
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
struct BanAction;
|
struct BanAction;
|
||||||
struct UnbanAction;
|
struct UnbanAction;
|
||||||
|
struct AutomodAction;
|
||||||
|
struct AutomodUserAction;
|
||||||
struct Message;
|
struct Message;
|
||||||
using MessagePtr = std::shared_ptr<const Message>;
|
using MessagePtr = std::shared_ptr<const Message>;
|
||||||
|
|
||||||
@@ -19,6 +22,8 @@ const SystemMessageTag systemMessage{};
|
|||||||
const TimeoutMessageTag timeoutMessage{};
|
const TimeoutMessageTag timeoutMessage{};
|
||||||
|
|
||||||
MessagePtr makeSystemMessage(const QString &text);
|
MessagePtr makeSystemMessage(const QString &text);
|
||||||
|
std::pair<MessagePtr, MessagePtr> makeAutomodMessage(
|
||||||
|
const AutomodAction &action);
|
||||||
|
|
||||||
struct MessageParseArgs {
|
struct MessageParseArgs {
|
||||||
bool disablePingSounds = false;
|
bool disablePingSounds = false;
|
||||||
@@ -29,6 +34,7 @@ struct MessageParseArgs {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class MessageBuilder
|
class MessageBuilder
|
||||||
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MessageBuilder();
|
MessageBuilder();
|
||||||
@@ -39,6 +45,7 @@ public:
|
|||||||
bool multipleTimes);
|
bool multipleTimes);
|
||||||
MessageBuilder(const BanAction &action, uint32_t count = 1);
|
MessageBuilder(const BanAction &action, uint32_t count = 1);
|
||||||
MessageBuilder(const UnbanAction &action);
|
MessageBuilder(const UnbanAction &action);
|
||||||
|
MessageBuilder(const AutomodUserAction &action);
|
||||||
|
|
||||||
Message *operator->();
|
Message *operator->();
|
||||||
Message &message();
|
Message &message();
|
||||||
@@ -63,5 +70,4 @@ public:
|
|||||||
private:
|
private:
|
||||||
std::shared_ptr<Message> message_;
|
std::shared_ptr<Message> message_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|||||||
@@ -268,6 +268,10 @@ void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/,
|
|||||||
{
|
{
|
||||||
backgroundColor = app->themes->messages.backgrounds.alternate;
|
backgroundColor = app->themes->messages.backgrounds.alternate;
|
||||||
}
|
}
|
||||||
|
else if (this->message_->flags.has(MessageFlag::AutoMod))
|
||||||
|
{
|
||||||
|
backgroundColor = QColor("#404040");
|
||||||
|
}
|
||||||
|
|
||||||
painter.fillRect(buffer->rect(), backgroundColor);
|
painter.fillRect(buffer->rect(), backgroundColor);
|
||||||
|
|
||||||
|
|||||||
@@ -105,4 +105,26 @@ struct ModerationStateAction : PubSubAction {
|
|||||||
bool modded;
|
bool modded;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AutomodAction : PubSubAction {
|
||||||
|
using PubSubAction::PubSubAction;
|
||||||
|
|
||||||
|
ActionUser target;
|
||||||
|
|
||||||
|
QString message;
|
||||||
|
|
||||||
|
QString reason;
|
||||||
|
|
||||||
|
QString msgID;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AutomodUserAction : PubSubAction {
|
||||||
|
using PubSubAction::PubSubAction;
|
||||||
|
|
||||||
|
ActionUser target;
|
||||||
|
|
||||||
|
QString message;
|
||||||
|
|
||||||
|
qint8 type;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|||||||
@@ -520,6 +520,196 @@ PubSub::PubSub()
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this->moderationActionHandlers["automod_rejected"] =
|
||||||
|
[this](const auto &data, const auto &roomID) {
|
||||||
|
// Display the automod message and prompt the allow/deny
|
||||||
|
AutomodAction action(data, roomID);
|
||||||
|
|
||||||
|
getCreatedByUser(data, action.source);
|
||||||
|
getTargetUser(data, action.target);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto &args = getArgs(data);
|
||||||
|
const auto &msgID = getMsgID(data);
|
||||||
|
|
||||||
|
if (args.Size() < 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rj::getSafe(args[0], action.target.name))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Size() >= 2)
|
||||||
|
{
|
||||||
|
if (!rj::getSafe(args[1], action.message))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Size() >= 3)
|
||||||
|
{
|
||||||
|
if (!rj::getSafe(args[2], action.reason))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rj::getSafe(msgID, action.msgID))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->signals_.moderation.automodMessage.invoke(action);
|
||||||
|
}
|
||||||
|
catch (const std::runtime_error &ex)
|
||||||
|
{
|
||||||
|
log("Error parsing moderation action: {}", ex.what());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this->moderationActionHandlers["add_permitted_term"] =
|
||||||
|
[this](const auto &data, const auto &roomID) {
|
||||||
|
// This term got a pass through automod
|
||||||
|
AutomodUserAction action(data, roomID);
|
||||||
|
getCreatedByUser(data, action.source);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto &args = getArgs(data);
|
||||||
|
action.type = 1;
|
||||||
|
|
||||||
|
if (args.Size() < 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rj::getSafe(args[0], action.message))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->signals_.moderation.automodUserMessage.invoke(action);
|
||||||
|
}
|
||||||
|
catch (const std::runtime_error &ex)
|
||||||
|
{
|
||||||
|
log("Error parsing moderation action: {}", ex.what());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this->moderationActionHandlers["add_blocked_term"] =
|
||||||
|
[this](const auto &data, const auto &roomID) {
|
||||||
|
// A term has been added
|
||||||
|
AutomodUserAction action(data, roomID);
|
||||||
|
getCreatedByUser(data, action.source);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto &args = getArgs(data);
|
||||||
|
action.type = 2;
|
||||||
|
|
||||||
|
if (args.Size() < 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rj::getSafe(args[0], action.message))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->signals_.moderation.automodUserMessage.invoke(action);
|
||||||
|
}
|
||||||
|
catch (const std::runtime_error &ex)
|
||||||
|
{
|
||||||
|
log("Error parsing moderation action: {}", ex.what());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this->moderationActionHandlers["delete_permitted_term"] =
|
||||||
|
[this](const auto &data, const auto &roomID) {
|
||||||
|
// This term got deleted
|
||||||
|
AutomodUserAction action(data, roomID);
|
||||||
|
getCreatedByUser(data, action.source);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto &args = getArgs(data);
|
||||||
|
action.type = 3;
|
||||||
|
|
||||||
|
if (args.Size() < 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rj::getSafe(args[0], action.message))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->signals_.moderation.automodUserMessage.invoke(action);
|
||||||
|
}
|
||||||
|
catch (const std::runtime_error &ex)
|
||||||
|
{
|
||||||
|
log("Error parsing moderation action: {}", ex.what());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this->moderationActionHandlers["delete_blocked_term"] =
|
||||||
|
[this](const auto &data, const auto &roomID) {
|
||||||
|
// This term got deleted
|
||||||
|
AutomodUserAction action(data, roomID);
|
||||||
|
|
||||||
|
getCreatedByUser(data, action.source);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto &args = getArgs(data);
|
||||||
|
action.type = 4;
|
||||||
|
|
||||||
|
if (args.Size() < 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rj::getSafe(args[0], action.message))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->signals_.moderation.automodUserMessage.invoke(action);
|
||||||
|
}
|
||||||
|
catch (const std::runtime_error &ex)
|
||||||
|
{
|
||||||
|
log("Error parsing moderation action: {}", ex.what());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this->moderationActionHandlers["modified_automod_properties"] =
|
||||||
|
[this](const auto &data, const auto &roomID) {
|
||||||
|
// The automod settings got modified
|
||||||
|
AutomodUserAction action(data, roomID);
|
||||||
|
getCreatedByUser(data, action.source);
|
||||||
|
action.type = 5;
|
||||||
|
this->signals_.moderation.automodUserMessage.invoke(action);
|
||||||
|
};
|
||||||
|
|
||||||
|
this->moderationActionHandlers["denied_automod_message"] =
|
||||||
|
[this](const auto &data, const auto &roomID) {
|
||||||
|
// This message got denied by a moderator
|
||||||
|
// qDebug() << QString::fromStdString(rj::stringify(data));
|
||||||
|
};
|
||||||
|
|
||||||
|
this->moderationActionHandlers["approved_automod_message"] =
|
||||||
|
[this](const auto &data, const auto &roomID) {
|
||||||
|
// This message got approved by a moderator
|
||||||
|
// qDebug() << QString::fromStdString(rj::stringify(data));
|
||||||
|
};
|
||||||
|
|
||||||
this->websocketClient.set_access_channels(websocketpp::log::alevel::all);
|
this->websocketClient.set_access_channels(websocketpp::log::alevel::all);
|
||||||
this->websocketClient.clear_access_channels(
|
this->websocketClient.clear_access_channels(
|
||||||
websocketpp::log::alevel::frame_payload);
|
websocketpp::log::alevel::frame_payload);
|
||||||
@@ -539,7 +729,7 @@ PubSub::PubSub()
|
|||||||
|
|
||||||
// Add an initial client
|
// Add an initial client
|
||||||
this->addClient();
|
this->addClient();
|
||||||
}
|
} // namespace chatterino
|
||||||
|
|
||||||
void PubSub::addClient()
|
void PubSub::addClient()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -112,6 +112,9 @@ public:
|
|||||||
|
|
||||||
Signal<BanAction> userBanned;
|
Signal<BanAction> userBanned;
|
||||||
Signal<UnbanAction> userUnbanned;
|
Signal<UnbanAction> userUnbanned;
|
||||||
|
|
||||||
|
Signal<AutomodAction> automodMessage;
|
||||||
|
Signal<AutomodUserAction> automodUserMessage;
|
||||||
} moderation;
|
} moderation;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|||||||
@@ -23,6 +23,18 @@ const rapidjson::Value &getArgs(const rapidjson::Value &data)
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rapidjson::Value &getMsgID(const rapidjson::Value &data)
|
||||||
|
{
|
||||||
|
if (!data.HasMember("msg_id"))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Missing member msg_id");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &msgID = data["msg_id"];
|
||||||
|
|
||||||
|
return msgID;
|
||||||
|
}
|
||||||
|
|
||||||
bool getCreatedByUser(const rapidjson::Value &data, ActionUser &user)
|
bool getCreatedByUser(const rapidjson::Value &data, ActionUser &user)
|
||||||
{
|
{
|
||||||
return rj::getSafe(data, "created_by", user.name) &&
|
return rj::getSafe(data, "created_by", user.name) &&
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class TwitchAccount;
|
|||||||
struct ActionUser;
|
struct ActionUser;
|
||||||
|
|
||||||
const rapidjson::Value &getArgs(const rapidjson::Value &data);
|
const rapidjson::Value &getArgs(const rapidjson::Value &data);
|
||||||
|
const rapidjson::Value &getMsgID(const rapidjson::Value &data);
|
||||||
|
|
||||||
bool getCreatedByUser(const rapidjson::Value &data, ActionUser &user);
|
bool getCreatedByUser(const rapidjson::Value &data, ActionUser &user);
|
||||||
|
|
||||||
|
|||||||
@@ -413,6 +413,51 @@ AccessGuard<const TwitchAccount::TwitchAccountEmoteData>
|
|||||||
return this->emotes_.accessConst();
|
return this->emotes_.accessConst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AutoModActions
|
||||||
|
void TwitchAccount::autoModAllow(const QString msgID)
|
||||||
|
{
|
||||||
|
QString url("https://api.twitch.tv/kraken/chat/twitchbot/approve");
|
||||||
|
|
||||||
|
NetworkRequest req(url, NetworkRequestType::Post);
|
||||||
|
req.setRawHeader("Content-Type", "application/json");
|
||||||
|
|
||||||
|
auto qba = (QString("{\"msg_id\":\"") + msgID + "\"}").toUtf8();
|
||||||
|
qDebug() << qba;
|
||||||
|
|
||||||
|
req.setRawHeader("Content-Length", QByteArray::number(qba.size()));
|
||||||
|
req.setPayload(qba);
|
||||||
|
req.setCaller(QThread::currentThread());
|
||||||
|
req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken());
|
||||||
|
|
||||||
|
req.onError([=](int errorCode) {
|
||||||
|
log("[TwitchAccounts::autoModAllow] Error {}", errorCode);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
req.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TwitchAccount::autoModDeny(const QString msgID)
|
||||||
|
{
|
||||||
|
QString url("https://api.twitch.tv/kraken/chat/twitchbot/deny");
|
||||||
|
|
||||||
|
NetworkRequest req(url, NetworkRequestType::Post);
|
||||||
|
req.setRawHeader("Content-Type", "application/json");
|
||||||
|
auto qba = (QString("{\"msg_id\":\"") + msgID + "\"}").toUtf8();
|
||||||
|
qDebug() << qba;
|
||||||
|
|
||||||
|
req.setRawHeader("Content-Length", QByteArray::number(qba.size()));
|
||||||
|
req.setPayload(qba);
|
||||||
|
req.setCaller(QThread::currentThread());
|
||||||
|
req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken());
|
||||||
|
|
||||||
|
req.onError([=](int errorCode) {
|
||||||
|
log("[TwitchAccounts::autoModDeny] Error {}", errorCode);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
req.execute();
|
||||||
|
}
|
||||||
|
|
||||||
void TwitchAccount::parseEmotes(const rapidjson::Document &root)
|
void TwitchAccount::parseEmotes(const rapidjson::Document &root)
|
||||||
{
|
{
|
||||||
auto emoteData = this->emotes_.access();
|
auto emoteData = this->emotes_.access();
|
||||||
|
|||||||
@@ -108,6 +108,10 @@ public:
|
|||||||
void loadEmotes();
|
void loadEmotes();
|
||||||
AccessGuard<const TwitchAccountEmoteData> accessEmotes() const;
|
AccessGuard<const TwitchAccountEmoteData> accessEmotes() const;
|
||||||
|
|
||||||
|
// Automod actions
|
||||||
|
void autoModAllow(const QString msgID);
|
||||||
|
void autoModDeny(const QString msgID);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void parseEmotes(const rapidjson::Document &document);
|
void parseEmotes(const rapidjson::Document &document);
|
||||||
void loadEmoteSetData(std::shared_ptr<EmoteSet> emoteSet);
|
void loadEmoteSetData(std::shared_ptr<EmoteSet> emoteSet);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "Application.hpp"
|
#include "Application.hpp"
|
||||||
#include "common/Common.hpp"
|
#include "common/Common.hpp"
|
||||||
|
#include "controllers/accounts/AccountController.hpp"
|
||||||
#include "debug/Benchmark.hpp"
|
#include "debug/Benchmark.hpp"
|
||||||
#include "debug/Log.hpp"
|
#include "debug/Log.hpp"
|
||||||
#include "messages/Emote.hpp"
|
#include "messages/Emote.hpp"
|
||||||
@@ -1671,6 +1672,17 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Link::AutoModAllow:
|
||||||
|
{
|
||||||
|
getApp()->accounts->twitch.getCurrent()->autoModAllow(link.value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Link::AutoModDeny:
|
||||||
|
{
|
||||||
|
getApp()->accounts->twitch.getCurrent()->autoModDeny(link.value);
|
||||||
|
}
|
||||||
|
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user