diff --git a/resources/resources_autogenerated.qrc b/resources/resources_autogenerated.qrc
index 9ec2b2a0a..fcf0b5dac 100644
--- a/resources/resources_autogenerated.qrc
+++ b/resources/resources_autogenerated.qrc
@@ -1,77 +1,77 @@
-
- chatterino2.icns
- contributors.txt
- emoji.json
- emojidata.txt
- error.png
- icon.ico
- icon.png
- pajaDank.png
- tlds.txt
- avatars/fourtf.png
- avatars/pajlada.png
- buttons/addSplit.png
- buttons/addSplitDark.png
- buttons/ban.png
- buttons/banRed.png
- buttons/emote.svg
- buttons/emoteDark.svg
- buttons/menuDark.png
- buttons/menuLight.png
- buttons/mod.png
- buttons/modModeDisabled.png
- buttons/modModeDisabled2.png
- buttons/modModeEnabled.png
- buttons/modModeEnabled2.png
- buttons/timeout.png
- buttons/unban.png
- buttons/unmod.png
- buttons/update.png
- buttons/updateError.png
- examples/moving.gif
- examples/splitting.gif
- licenses/boost_boost.txt
- licenses/emoji-data-source.txt
- licenses/fmt_bsd2.txt
- licenses/libcommuni_BSD3.txt
- licenses/openssl.txt
- licenses/pajlada_settings.txt
- licenses/pajlada_signals.txt
- licenses/qt_lgpl-3.0.txt
- licenses/rapidjson.txt
- licenses/websocketpp.txt
- qss/settings.qss
- settings/about.svg
- settings/aboutlogo.png
- settings/accounts.svg
- settings/behave.svg
- settings/commands.svg
- settings/emote.svg
- settings/notifications.svg
- settings/theme.svg
- sounds/ping2.wav
- split/down.png
- split/left.png
- split/move.png
- split/right.png
- split/up.png
- twitch/admin.png
- twitch/broadcaster.png
- twitch/cheer1.png
- twitch/globalmod.png
- twitch/moderator.png
- twitch/prime.png
- twitch/staff.png
- twitch/subscriber.png
- twitch/turbo.png
- twitch/verified.png
- settings/ignore.svg
- settings/keybinds.svg
- settings/moderation.svg
- settings/notification2.svg
- settings/browser.svg
- settings/externaltools.svg
- settings/advanced.svg
-
-
+ chatterino2.icns
+ contributors.txt
+ emoji.json
+ emojidata.txt
+ error.png
+ icon.ico
+ icon.png
+ pajaDank.png
+ tlds.txt
+ avatars/fourtf.png
+ avatars/pajlada.png
+ buttons/addSplit.png
+ buttons/addSplitDark.png
+ buttons/ban.png
+ buttons/banRed.png
+ buttons/emote.svg
+ buttons/emoteDark.svg
+ buttons/menuDark.png
+ buttons/menuLight.png
+ buttons/mod.png
+ buttons/modModeDisabled.png
+ buttons/modModeDisabled2.png
+ buttons/modModeEnabled.png
+ buttons/modModeEnabled2.png
+ buttons/timeout.png
+ buttons/unban.png
+ buttons/unmod.png
+ buttons/update.png
+ buttons/updateError.png
+ examples/moving.gif
+ examples/splitting.gif
+ licenses/boost_boost.txt
+ licenses/emoji-data-source.txt
+ licenses/fmt_bsd2.txt
+ licenses/libcommuni_BSD3.txt
+ licenses/openssl.txt
+ licenses/pajlada_settings.txt
+ licenses/pajlada_signals.txt
+ licenses/qt_lgpl-3.0.txt
+ licenses/rapidjson.txt
+ licenses/websocketpp.txt
+ qss/settings.qss
+ settings/about.svg
+ settings/aboutlogo.png
+ settings/accounts.svg
+ settings/advanced.svg
+ settings/behave.svg
+ settings/browser.svg
+ settings/commands.svg
+ settings/emote.svg
+ settings/externaltools.svg
+ settings/ignore.svg
+ settings/keybinds.svg
+ settings/moderation.svg
+ settings/notification2.svg
+ settings/notifications.svg
+ settings/theme.svg
+ sounds/ping2.wav
+ split/down.png
+ split/left.png
+ split/move.png
+ split/right.png
+ split/up.png
+ twitch/admin.png
+ twitch/automod.png
+ twitch/broadcaster.png
+ twitch/cheer1.png
+ twitch/globalmod.png
+ twitch/moderator.png
+ twitch/prime.png
+ twitch/staff.png
+ twitch/subscriber.png
+ twitch/turbo.png
+ twitch/verified.png
+
+
\ No newline at end of file
diff --git a/resources/twitch/automod.png b/resources/twitch/automod.png
new file mode 100644
index 000000000..01174644f
Binary files /dev/null and b/resources/twitch/automod.png differ
diff --git a/src/Application.cpp b/src/Application.cpp
index 107f0b05f..2870c6ed6 100644
--- a/src/Application.cpp
+++ b/src/Application.cpp
@@ -234,6 +234,38 @@ void Application::initPubsub()
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();
auto RequestModerationActions = [=]() {
diff --git a/src/autogenerated/ResourcesAutogen.cpp b/src/autogenerated/ResourcesAutogen.cpp
index ee25daf1f..34cf039ef 100644
--- a/src/autogenerated/ResourcesAutogen.cpp
+++ b/src/autogenerated/ResourcesAutogen.cpp
@@ -32,6 +32,7 @@ Resources2::Resources2()
this->split.right = QPixmap(":/split/right.png");
this->split.up = QPixmap(":/split/up.png");
this->twitch.admin = QPixmap(":/twitch/admin.png");
+ this->twitch.automod = QPixmap(":/twitch/automod.png");
this->twitch.broadcaster = QPixmap(":/twitch/broadcaster.png");
this->twitch.cheer1 = QPixmap(":/twitch/cheer1.png");
this->twitch.globalmod = QPixmap(":/twitch/globalmod.png");
diff --git a/src/autogenerated/ResourcesAutogen.hpp b/src/autogenerated/ResourcesAutogen.hpp
index 075c801eb..fbbb2fbf3 100644
--- a/src/autogenerated/ResourcesAutogen.hpp
+++ b/src/autogenerated/ResourcesAutogen.hpp
@@ -3,8 +3,7 @@
namespace chatterino {
-class Resources2 : public Singleton
-{
+class Resources2 : public Singleton {
public:
Resources2();
@@ -45,6 +44,7 @@ public:
} split;
struct {
QPixmap admin;
+ QPixmap automod;
QPixmap broadcaster;
QPixmap cheer1;
QPixmap globalmod;
diff --git a/src/common/NetworkRequest.cpp b/src/common/NetworkRequest.cpp
index 524693c17..b4192e8e6 100644
--- a/src/common/NetworkRequest.cpp
+++ b/src/common/NetworkRequest.cpp
@@ -147,6 +147,12 @@ void NetworkRequest::execute()
}
break;
+ case NetworkRequestType::Post:
+ {
+ this->doRequest();
+ }
+ break;
+
default:
{
log("[Execute] Unhandled request type");
@@ -217,6 +223,10 @@ void NetworkRequest::doRequest()
return NetworkManager::accessManager.deleteResource(
data->request_);
+ case NetworkRequestType::Post:
+ return NetworkManager::accessManager.post(data->request_,
+ data->payload_);
+
default:
return nullptr;
}
diff --git a/src/messages/Link.hpp b/src/messages/Link.hpp
index 38000982f..52a52444b 100644
--- a/src/messages/Link.hpp
+++ b/src/messages/Link.hpp
@@ -17,6 +17,8 @@ public:
InsertText,
ShowMessage,
UserAction,
+ AutoModAllow,
+ AutoModDeny,
};
Link();
diff --git a/src/messages/Message.hpp b/src/messages/Message.hpp
index cc716dd2f..572615a04 100644
--- a/src/messages/Message.hpp
+++ b/src/messages/Message.hpp
@@ -28,6 +28,7 @@ enum class MessageFlag : uint16_t {
PubSub = (1 << 11),
Subscription = (1 << 12),
Notification = (1 << 13),
+ AutoMod = (1 << 14),
};
using MessageFlags = FlagsEnum;
diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp
index 7ea91fcfa..9a4b1a00d 100644
--- a/src/messages/MessageBuilder.cpp
+++ b/src/messages/MessageBuilder.cpp
@@ -1,6 +1,8 @@
#include "MessageBuilder.hpp"
+#include "Application.hpp"
#include "common/LinkParser.hpp"
+#include "messages/Image.hpp"
#include "messages/Message.hpp"
#include "messages/MessageElement.hpp"
#include "providers/twitch/PubsubActions.hpp"
@@ -11,6 +13,7 @@
#include "util/IrcHelpers.hpp"
#include
+#include
namespace chatterino {
@@ -19,6 +22,61 @@ MessagePtr makeSystemMessage(const QString &text)
return MessageBuilder(systemMessage, text).release();
}
+std::pair makeAutomodMessage(
+ const AutomodAction &action)
+{
+ auto builder = MessageBuilder();
+
+ builder.emplace();
+ builder.message().flags.set(MessageFlag::PubSub);
+
+ builder
+ .emplace(
+ Image::fromPixmap(getApp()->resources->twitch.automod),
+ MessageElementFlag::BadgeChannelAuthority)
+ ->setTooltip("AutoMod");
+ builder.emplace(
+ "AutoMod:", MessageElementFlag::NonBoldUsername,
+ MessageColor(QColor("blue")));
+ builder.emplace(
+ ("Held a message for reason: " + action.reason +
+ ". Allow will post it in chat. "),
+ MessageElementFlag::Text, MessageColor::Text);
+ builder
+ .emplace("Allow", MessageElementFlag::Text,
+ MessageColor(QColor("green")),
+ FontStyle::ChatMediumBold)
+ ->setLink({Link::AutoModAllow, action.msgID});
+ builder
+ .emplace(" Deny", MessageElementFlag::Text,
+ MessageColor(QColor("red")),
+ FontStyle::ChatMediumBold)
+ ->setLink({Link::AutoModDeny, action.msgID});
+ // builder.emplace(action.msgID,
+ // MessageElementFlag::Text,
+ // MessageColor::Text);
+ builder.message().flags.set(MessageFlag::AutoMod);
+
+ auto message1 = builder.release();
+
+ builder = MessageBuilder();
+ builder.emplace();
+ builder.message().flags.set(MessageFlag::PubSub);
+
+ builder
+ .emplace(action.target.name + ":",
+ MessageElementFlag::NonBoldUsername,
+ MessageColor(QColor("red")))
+ ->setLink({Link::UserInfo, action.target.name});
+ builder.emplace(action.message, MessageElementFlag::Text,
+ MessageColor::Text);
+ builder.message().flags.set(MessageFlag::AutoMod);
+
+ auto message2 = builder.release();
+
+ return std::make_pair(message1, message2);
+}
+
MessageBuilder::MessageBuilder()
: message_(std::make_shared())
{
@@ -179,6 +237,47 @@ MessageBuilder::MessageBuilder(const UnbanAction &action)
this->message().searchText = text;
}
+MessageBuilder::MessageBuilder(const AutomodUserAction &action)
+ : MessageBuilder()
+{
+ this->emplace();
+ 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(text, MessageElementFlag::Text,
+ MessageColor::System);
+}
+
Message *MessageBuilder::operator->()
{
return this->message_.get();
diff --git a/src/messages/MessageBuilder.hpp b/src/messages/MessageBuilder.hpp
index b09b82ed2..8fe7915e2 100644
--- a/src/messages/MessageBuilder.hpp
+++ b/src/messages/MessageBuilder.hpp
@@ -4,10 +4,13 @@
#include
#include
+#include
namespace chatterino {
struct BanAction;
struct UnbanAction;
+struct AutomodAction;
+struct AutomodUserAction;
struct Message;
using MessagePtr = std::shared_ptr;
@@ -19,6 +22,8 @@ const SystemMessageTag systemMessage{};
const TimeoutMessageTag timeoutMessage{};
MessagePtr makeSystemMessage(const QString &text);
+std::pair makeAutomodMessage(
+ const AutomodAction &action);
struct MessageParseArgs {
bool disablePingSounds = false;
@@ -29,6 +34,7 @@ struct MessageParseArgs {
};
class MessageBuilder
+
{
public:
MessageBuilder();
@@ -39,6 +45,7 @@ public:
bool multipleTimes);
MessageBuilder(const BanAction &action, uint32_t count = 1);
MessageBuilder(const UnbanAction &action);
+ MessageBuilder(const AutomodUserAction &action);
Message *operator->();
Message &message();
@@ -63,5 +70,4 @@ public:
private:
std::shared_ptr message_;
};
-
} // namespace chatterino
diff --git a/src/messages/layouts/MessageLayout.cpp b/src/messages/layouts/MessageLayout.cpp
index cf1be4a72..06af7c790 100644
--- a/src/messages/layouts/MessageLayout.cpp
+++ b/src/messages/layouts/MessageLayout.cpp
@@ -268,6 +268,10 @@ void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/,
{
backgroundColor = app->themes->messages.backgrounds.alternate;
}
+ else if (this->message_->flags.has(MessageFlag::AutoMod))
+ {
+ backgroundColor = QColor("#404040");
+ }
painter.fillRect(buffer->rect(), backgroundColor);
diff --git a/src/providers/twitch/PubsubActions.hpp b/src/providers/twitch/PubsubActions.hpp
index 8b2bfecfb..6377b6d4e 100644
--- a/src/providers/twitch/PubsubActions.hpp
+++ b/src/providers/twitch/PubsubActions.hpp
@@ -105,4 +105,26 @@ struct ModerationStateAction : PubSubAction {
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
diff --git a/src/providers/twitch/PubsubClient.cpp b/src/providers/twitch/PubsubClient.cpp
index 6bc866700..f5fb54255 100644
--- a/src/providers/twitch/PubsubClient.cpp
+++ b/src/providers/twitch/PubsubClient.cpp
@@ -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.clear_access_channels(
websocketpp::log::alevel::frame_payload);
@@ -539,7 +729,7 @@ PubSub::PubSub()
// Add an initial client
this->addClient();
-}
+} // namespace chatterino
void PubSub::addClient()
{
diff --git a/src/providers/twitch/PubsubClient.hpp b/src/providers/twitch/PubsubClient.hpp
index f3cc10f15..9e2def087 100644
--- a/src/providers/twitch/PubsubClient.hpp
+++ b/src/providers/twitch/PubsubClient.hpp
@@ -112,6 +112,9 @@ public:
Signal userBanned;
Signal userUnbanned;
+
+ Signal automodMessage;
+ Signal automodUserMessage;
} moderation;
struct {
diff --git a/src/providers/twitch/PubsubHelpers.cpp b/src/providers/twitch/PubsubHelpers.cpp
index cda4b8788..b30c47c0f 100644
--- a/src/providers/twitch/PubsubHelpers.cpp
+++ b/src/providers/twitch/PubsubHelpers.cpp
@@ -23,6 +23,18 @@ const rapidjson::Value &getArgs(const rapidjson::Value &data)
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)
{
return rj::getSafe(data, "created_by", user.name) &&
diff --git a/src/providers/twitch/PubsubHelpers.hpp b/src/providers/twitch/PubsubHelpers.hpp
index 7ea540ad8..ad8c52130 100644
--- a/src/providers/twitch/PubsubHelpers.hpp
+++ b/src/providers/twitch/PubsubHelpers.hpp
@@ -12,6 +12,7 @@ class TwitchAccount;
struct ActionUser;
const rapidjson::Value &getArgs(const rapidjson::Value &data);
+const rapidjson::Value &getMsgID(const rapidjson::Value &data);
bool getCreatedByUser(const rapidjson::Value &data, ActionUser &user);
diff --git a/src/providers/twitch/TwitchAccount.cpp b/src/providers/twitch/TwitchAccount.cpp
index f6337d0c6..1d798f8ce 100644
--- a/src/providers/twitch/TwitchAccount.cpp
+++ b/src/providers/twitch/TwitchAccount.cpp
@@ -413,6 +413,51 @@ AccessGuard
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)
{
auto emoteData = this->emotes_.access();
diff --git a/src/providers/twitch/TwitchAccount.hpp b/src/providers/twitch/TwitchAccount.hpp
index 1ea718b73..e5db1749d 100644
--- a/src/providers/twitch/TwitchAccount.hpp
+++ b/src/providers/twitch/TwitchAccount.hpp
@@ -108,6 +108,10 @@ public:
void loadEmotes();
AccessGuard accessEmotes() const;
+ // Automod actions
+ void autoModAllow(const QString msgID);
+ void autoModDeny(const QString msgID);
+
private:
void parseEmotes(const rapidjson::Document &document);
void loadEmoteSetData(std::shared_ptr emoteSet);
diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp
index 91bfed93b..682a976d7 100644
--- a/src/widgets/helper/ChannelView.cpp
+++ b/src/widgets/helper/ChannelView.cpp
@@ -2,6 +2,7 @@
#include "Application.hpp"
#include "common/Common.hpp"
+#include "controllers/accounts/AccountController.hpp"
#include "debug/Benchmark.hpp"
#include "debug/Log.hpp"
#include "messages/Emote.hpp"
@@ -1671,6 +1672,17 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link,
}
break;
+ case Link::AutoModAllow:
+ {
+ getApp()->accounts->twitch.getCurrent()->autoModAllow(link.value);
+ }
+ break;
+
+ case Link::AutoModDeny:
+ {
+ getApp()->accounts->twitch.getCurrent()->autoModDeny(link.value);
+ }
+
default:;
}
}