Combine Ban/Timeout pubsub actions

Fully implement PubSub ban/unban messages
Move the "message combining" to Channel::addMessage

Disable "irc" ban/timeout handling for now. In the future, we might want
to use this if you're not a moderator in a channel.
This commit is contained in:
Rasmus Karlsson 2018-04-27 18:35:31 +02:00
parent 42182ed504
commit 32b6417a55
8 changed files with 163 additions and 37 deletions

View file

@ -65,7 +65,7 @@ Application::Application()
action.source.name); action.source.name);
}); });
pubsub.sig.moderation.userTimedOut.connect([&](const auto &action) { pubsub.sig.moderation.userBanned.connect([&](const auto &action) {
auto &server = providers::twitch::TwitchServer::getInstance(); auto &server = providers::twitch::TwitchServer::getInstance();
auto chan = server.getChannelOrEmptyByID(action.roomID); auto chan = server.getChannelOrEmptyByID(action.roomID);
@ -73,31 +73,22 @@ Application::Application()
return; return;
} }
auto msg = messages::Message::createSystemMessage( auto msg = messages::Message::createTimeoutMessage(action);
QString("User %1(%2) was timed out by %3 for %4 seconds with reason: '%5'")
.arg(action.target.name)
.arg(action.target.id)
.arg(action.source.name)
.arg(action.duration)
.arg(action.reason));
util::postToThread([chan, msg] { chan->addMessage(msg); }); util::postToThread([chan, msg] { chan->addMessage(msg); });
debug::Log("User {}({}) was timed out by {} for {} seconds with reason: '{}'",
action.target.name, action.target.id, action.source.name, action.duration,
action.reason);
});
pubsub.sig.moderation.userBanned.connect([&](const auto &action) {
debug::Log("User {}({}) was banned by {} with reason: '{}'", action.target.name,
action.target.id, action.source.name, action.reason);
}); });
pubsub.sig.moderation.userUnbanned.connect([&](const auto &action) { pubsub.sig.moderation.userUnbanned.connect([&](const auto &action) {
debug::Log( auto &server = providers::twitch::TwitchServer::getInstance();
"User {}({}) was unbanned by {}. User was previously {}", action.target.name, auto chan = server.getChannelOrEmptyByID(action.roomID);
action.target.id, action.source.name,
action.previousState == singletons::UnbanAction::Banned ? "banned" : "timed out"); if (chan->isEmpty()) {
return;
}
auto msg = messages::Message::createUntimeoutMessage(action);
util::postToThread([chan, msg] { chan->addMessage(msg); });
}); });
auto &accountManager = singletons::AccountManager::getInstance(); auto &accountManager = singletons::AccountManager::getInstance();

View file

@ -57,15 +57,57 @@ void Channel::addMessage(MessagePtr message)
{ {
MessagePtr deleted; MessagePtr deleted;
const QString &username = message->loginName; bool isTimeout = (message->flags & Message::Timeout) != 0;
if (!isTimeout) {
const QString &username = message->loginName;
if (!username.isEmpty()) { if (!username.isEmpty()) {
// TODO: Add recent chatters display name. This should maybe be a setting // TODO: Add recent chatters display name. This should maybe be a setting
this->addRecentChatter(message); this->addRecentChatter(message);
} }
}
singletons::LoggingManager::getInstance().addMessage(this->name, message); singletons::LoggingManager::getInstance().addMessage(this->name, message);
if (isTimeout) {
LimitedQueueSnapshot<MessagePtr> snapshot = this->getMessageSnapshot();
bool addMessage = true;
int snapshotLength = snapshot.getLength();
int end = std::max(0, snapshotLength - 20);
for (int i = snapshotLength - 1; i >= end; --i) {
auto &s = snapshot[i];
if (s->flags.HasFlag(Message::Untimeout) && s->timeoutUser == message->timeoutUser) {
break;
}
if (s->flags.HasFlag(Message::Timeout) && s->timeoutUser == message->timeoutUser) {
assert(message->banAction != nullptr);
MessagePtr replacement(
Message::createTimeoutMessage(*(message->banAction), s->count + 1));
this->replaceMessage(s, replacement);
addMessage = false;
}
}
// disable the messages from the user
for (int i = 0; i < snapshotLength; i++) {
auto &s = snapshot[i];
if ((s->flags & (Message::Timeout | Message::Untimeout)) == 0 &&
s->loginName == message->timeoutUser) {
s->flags.EnableFlag(Message::Disabled);
}
}
// XXX: Might need the following line
// WindowManager::getInstance().repaintVisibleChatWidgets(this);
if (!addMessage) {
return;
}
}
if (this->messages.pushBack(message, deleted)) { if (this->messages.pushBack(message, deleted)) {
this->messageRemovedFromStart.invoke(deleted); this->messageRemovedFromStart.invoke(deleted);
} }

View file

@ -1,5 +1,6 @@
#include "messages/message.hpp" #include "messages/message.hpp"
#include "messageelement.hpp" #include "messageelement.hpp"
#include "singletons/helper/pubsubactions.hpp"
#include "util/irchelpers.hpp" #include "util/irchelpers.hpp"
using SBHighlight = chatterino::widgets::ScrollbarHighlight; using SBHighlight = chatterino::widgets::ScrollbarHighlight;
@ -78,5 +79,85 @@ MessagePtr Message::createTimeoutMessage(const QString &username, const QString
return message; return message;
} }
MessagePtr Message::createTimeoutMessage(const singletons::BanAction &action, uint32_t count)
{
MessagePtr msg(new Message);
msg->addElement(new TimestampElement(QTime::currentTime()));
msg->flags.EnableFlag(MessageFlags::System);
msg->flags.EnableFlag(MessageFlags::Timeout);
msg->timeoutUser = action.target.name;
msg->count = count;
msg->banAction.reset(new singletons::BanAction(action));
QString text;
if (action.isBan()) {
if (action.reason.isEmpty()) {
text = QString("%1 banned %2.") //
.arg(action.source.name)
.arg(action.target.name);
} else {
text = QString("%1 banned %2: \"%3\".") //
.arg(action.source.name)
.arg(action.target.name)
.arg(action.reason);
}
} else {
if (action.reason.isEmpty()) {
text = QString("%1 timed out %2 for %3 seconds.") //
.arg(action.source.name)
.arg(action.target.name)
.arg(action.duration);
} else {
text = QString("%1 timed out %2 for %3 seconds: \"%4\".") //
.arg(action.source.name)
.arg(action.target.name)
.arg(action.duration)
.arg(action.reason);
}
if (count > 1) {
text.append(QString(" (%1 times)").arg(count));
}
}
msg->addElement(new messages::TextElement(text, messages::MessageElement::Text,
messages::MessageColor::System));
msg->searchText = text;
return msg;
}
MessagePtr Message::createUntimeoutMessage(const singletons::UnbanAction &action)
{
MessagePtr msg(new Message);
msg->addElement(new TimestampElement(QTime::currentTime()));
msg->flags.EnableFlag(MessageFlags::System);
msg->flags.EnableFlag(MessageFlags::Untimeout);
msg->timeoutUser = action.target.name;
QString text;
if (action.wasBan()) {
text = QString("%1 unbanned %2.") //
.arg(action.source.name)
.arg(action.target.name);
} else {
text = QString("%1 untimedout %2.") //
.arg(action.source.name)
.arg(action.target.name);
}
msg->addElement(new messages::TextElement(text, messages::MessageElement::Text,
messages::MessageColor::System));
msg->searchText = text;
return msg;
}
} // namespace messages } // namespace messages
} // namespace chatterino } // namespace chatterino

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "messages/messageelement.hpp" #include "messages/messageelement.hpp"
#include "singletons/helper/pubsubactions.hpp"
#include "util/flagsenum.hpp" #include "util/flagsenum.hpp"
#include "widgets/helper/scrollbarhighlight.hpp" #include "widgets/helper/scrollbarhighlight.hpp"
@ -37,6 +38,7 @@ struct Message {
DisableCompactEmotes = (1 << 6), DisableCompactEmotes = (1 << 6),
Collapsed = (1 << 7), Collapsed = (1 << 7),
DisconnectedMessage = (1 << 8), DisconnectedMessage = (1 << 8),
Untimeout = (1 << 9),
}; };
util::FlagsEnum<MessageFlags> flags; util::FlagsEnum<MessageFlags> flags;
@ -48,6 +50,9 @@ struct Message {
QString localizedName; QString localizedName;
QString timeoutUser; QString timeoutUser;
std::unique_ptr<singletons::BanAction> banAction;
uint32_t count = 1;
// Messages should not be added after the message is done initializing. // Messages should not be added after the message is done initializing.
void addElement(MessageElement *element); void addElement(MessageElement *element);
const std::vector<std::unique_ptr<MessageElement>> &getElements() const; const std::vector<std::unique_ptr<MessageElement>> &getElements() const;
@ -64,6 +69,10 @@ public:
static std::shared_ptr<Message> createTimeoutMessage(const QString &username, static std::shared_ptr<Message> createTimeoutMessage(const QString &username,
const QString &durationInSeconds, const QString &durationInSeconds,
const QString &reason, bool multipleTimes); const QString &reason, bool multipleTimes);
static std::shared_ptr<Message> createTimeoutMessage(const singletons::BanAction &action,
uint32_t count = 1);
static std::shared_ptr<Message> createUntimeoutMessage(const singletons::UnbanAction &action);
}; };
using MessagePtr = std::shared_ptr<Message>; using MessagePtr = std::shared_ptr<Message>;

View file

@ -56,6 +56,7 @@ void IrcMessageHandler::handleRoomStateMessage(Communi::IrcMessage *message)
void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message) void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
{ {
return;
// check parameter count // check parameter count
if (message->parameters().length() < 1) { if (message->parameters().length() < 1) {
return; return;

View file

@ -46,21 +46,19 @@ struct ModeChangedAction : PubSubAction {
} args; } args;
}; };
struct TimeoutAction : PubSubAction {
using PubSubAction::PubSubAction;
ActionUser target;
QString reason;
uint32_t duration;
};
struct BanAction : PubSubAction { struct BanAction : PubSubAction {
using PubSubAction::PubSubAction; using PubSubAction::PubSubAction;
ActionUser target; ActionUser target;
QString reason; QString reason;
uint32_t duration = 0;
bool isBan() const
{
return this->duration == 0;
}
}; };
struct UnbanAction : PubSubAction { struct UnbanAction : PubSubAction {
@ -72,6 +70,11 @@ struct UnbanAction : PubSubAction {
Banned, Banned,
TimedOut, TimedOut,
} previousState; } previousState;
bool wasBan() const
{
return this->previousState == Banned;
}
}; };
struct ClearChatAction : PubSubAction { struct ClearChatAction : PubSubAction {

View file

@ -300,7 +300,7 @@ PubSubManager::PubSubManager()
}; };
this->moderationActionHandlers["timeout"] = [this](const auto &data, const auto &roomID) { this->moderationActionHandlers["timeout"] = [this](const auto &data, const auto &roomID) {
TimeoutAction action(data, roomID); BanAction action(data, roomID);
getCreatedByUser(data, action.source); getCreatedByUser(data, action.source);
getTargetUser(data, action.target); getTargetUser(data, action.target);
@ -329,7 +329,7 @@ PubSubManager::PubSubManager()
} }
} }
this->sig.moderation.userTimedOut.invoke(action); this->sig.moderation.userBanned.invoke(action);
} catch (const std::runtime_error &ex) { } catch (const std::runtime_error &ex) {
debug::Log("Error parsing moderation action: {}", ex.what()); debug::Log("Error parsing moderation action: {}", ex.what());
} }

View file

@ -102,7 +102,6 @@ public:
Signal<ModeChangedAction> modeChanged; Signal<ModeChangedAction> modeChanged;
Signal<ModerationStateAction> moderationStateChanged; Signal<ModerationStateAction> moderationStateChanged;
Signal<TimeoutAction> userTimedOut;
Signal<BanAction> userBanned; Signal<BanAction> userBanned;
Signal<UnbanAction> userUnbanned; Signal<UnbanAction> userUnbanned;
} moderation; } moderation;