diff --git a/resources/buttons/trashCan.png b/resources/buttons/trashCan.png new file mode 100644 index 00000000..fd1bce3a Binary files /dev/null and b/resources/buttons/trashCan.png differ diff --git a/resources/buttons/trashcan.svg b/resources/buttons/trashcan.svg new file mode 100644 index 00000000..e225ee4e --- /dev/null +++ b/resources/buttons/trashcan.svg @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + Trashcan top + + + + + + + diff --git a/resources/resources_autogenerated.qrc b/resources/resources_autogenerated.qrc index 8aed98b9..96204e34 100644 --- a/resources/resources_autogenerated.qrc +++ b/resources/resources_autogenerated.qrc @@ -24,6 +24,7 @@ buttons/modModeEnabled.png buttons/modModeEnabled2.png buttons/timeout.png + buttons/trashCan.png buttons/unban.png buttons/unmod.png buttons/update.png diff --git a/src/Application.cpp b/src/Application.cpp index 2870c6ed..d92e6e89 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -264,6 +264,7 @@ void Application::initPubsub() auto msg = MessageBuilder(action).release(); postToThread([chan, msg] { chan->addMessage(msg); }); + chan->deleteMessage(msg->id); }); this->twitch.pubsub->start(); diff --git a/src/autogenerated/ResourcesAutogen.cpp b/src/autogenerated/ResourcesAutogen.cpp index 34cf039e..59f5fce7 100644 --- a/src/autogenerated/ResourcesAutogen.cpp +++ b/src/autogenerated/ResourcesAutogen.cpp @@ -18,6 +18,7 @@ Resources2::Resources2() this->buttons.modModeEnabled = QPixmap(":/buttons/modModeEnabled.png"); this->buttons.modModeEnabled2 = QPixmap(":/buttons/modModeEnabled2.png"); this->buttons.timeout = QPixmap(":/buttons/timeout.png"); + this->buttons.trashCan = QPixmap(":/buttons/trashCan.png"); this->buttons.unban = QPixmap(":/buttons/unban.png"); this->buttons.unmod = QPixmap(":/buttons/unmod.png"); this->buttons.update = QPixmap(":/buttons/update.png"); diff --git a/src/autogenerated/ResourcesAutogen.hpp b/src/autogenerated/ResourcesAutogen.hpp index fbbb2fbf..1c8a0bce 100644 --- a/src/autogenerated/ResourcesAutogen.hpp +++ b/src/autogenerated/ResourcesAutogen.hpp @@ -24,6 +24,7 @@ public: QPixmap modModeEnabled; QPixmap modModeEnabled2; QPixmap timeout; + QPixmap trashCan; QPixmap unban; QPixmap unmod; QPixmap update; diff --git a/src/common/Channel.cpp b/src/common/Channel.cpp index 905bd1b6..caa43275 100644 --- a/src/common/Channel.cpp +++ b/src/common/Channel.cpp @@ -212,6 +212,25 @@ void Channel::replaceMessage(MessagePtr message, MessagePtr replacement) } } +void Channel::deleteMessage(QString messageID) +{ + LimitedQueueSnapshot snapshot = this->getMessageSnapshot(); + int snapshotLength = snapshot.size(); + + int end = std::max(0, snapshotLength - 200); + + for (int i = snapshotLength - 1; i >= end; --i) + { + auto &s = snapshot[i]; + + if (s->id == messageID) + { + s->flags.set(MessageFlag::Disabled); + break; + } + } +} + void Channel::addRecentChatter(const MessagePtr &message) { } diff --git a/src/common/Channel.hpp b/src/common/Channel.hpp index c45d9c97..9abc668e 100644 --- a/src/common/Channel.hpp +++ b/src/common/Channel.hpp @@ -62,6 +62,7 @@ public: void addOrReplaceTimeout(MessagePtr message); void disableAllMessages(); void replaceMessage(MessagePtr message, MessagePtr replacement); + void deleteMessage(QString messageID); QStringList modList; diff --git a/src/controllers/moderationactions/ModerationAction.cpp b/src/controllers/moderationactions/ModerationAction.cpp index 0b6418a5..199ea552 100644 --- a/src/controllers/moderationactions/ModerationAction.cpp +++ b/src/controllers/moderationactions/ModerationAction.cpp @@ -73,6 +73,10 @@ ModerationAction::ModerationAction(const QString &action) { this->image_ = Image::fromPixmap(getApp()->resources->buttons.ban); } + else if (action.startsWith("/delete")) + { + this->image_ = Image::fromPixmap(getApp()->resources->buttons.trashCan); + } else { QString xD = action; diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 7e89c6b4..2fda025a 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -266,6 +266,41 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message) } } +void IrcMessageHandler::handleClearMessageMessage(Communi::IrcMessage *message) +{ + // check parameter count + if (message->parameters().length() < 1) + { + return; + } + + QString chanName; + if (!trimChannelName(message->parameter(0), chanName)) + { + return; + } + + auto app = getApp(); + + // get channel + auto chan = app->twitch.server->getChannelOrEmpty(chanName); + + if (chan->isEmpty()) + { + log("[IrcMessageHandler:handleClearMessageMessage] Twitch channel {} " + "not " + "found", + chanName); + return; + } + + auto tags = message->tags(); + + QString targetID = tags.value("target-msg-id").toString(); + + chan->deleteMessage(targetID); +} + void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message) { auto app = getApp(); @@ -516,7 +551,17 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message) return; } - channel->addMessage(msg); + QString tags = message->tags().value("msg-id", "").toString(); + if (tags == "bad_delete_message_error" || tags == "usage_delete") + { + channel->addMessage(makeSystemMessage( + "Usage: \"/delete \" - can't take more " + "than one argument")); + } + else + { + channel->addMessage(msg); + } } } diff --git a/src/providers/twitch/IrcMessageHandler.hpp b/src/providers/twitch/IrcMessageHandler.hpp index 0474f097..861ee734 100644 --- a/src/providers/twitch/IrcMessageHandler.hpp +++ b/src/providers/twitch/IrcMessageHandler.hpp @@ -27,6 +27,7 @@ public: void handleRoomStateMessage(Communi::IrcMessage *message); void handleClearChatMessage(Communi::IrcMessage *message); + void handleClearMessageMessage(Communi::IrcMessage *message); void handleUserStateMessage(Communi::IrcMessage *message); void handleWhisperMessage(Communi::IrcMessage *message); diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 204cf6c0..b935a283 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -580,7 +580,7 @@ void TwitchMessageBuilder::parseMessageID() if (iterator != this->tags.end()) { - this->messageID = iterator.value().toString(); + this->message().id = iterator.value().toString(); } } @@ -607,7 +607,7 @@ void TwitchMessageBuilder::parseRoomID() void TwitchMessageBuilder::appendChannelName() { QString channelName("#" + this->channel->getName()); - Link link(Link::Url, this->channel->getName() + "\n" + this->messageID); + Link link(Link::Url, this->channel->getName() + "\n" + this->message().id); this->emplace(channelName, MessageElementFlag::ChannelName, MessageColor::System) // diff --git a/src/providers/twitch/TwitchMessageBuilder.hpp b/src/providers/twitch/TwitchMessageBuilder.hpp index 5aa72b20..da7b3865 100644 --- a/src/providers/twitch/TwitchMessageBuilder.hpp +++ b/src/providers/twitch/TwitchMessageBuilder.hpp @@ -41,7 +41,6 @@ public: MessageParseArgs args; const QVariantMap tags; - QString messageID; QString userName; bool isIgnored() const; @@ -55,8 +54,10 @@ private: void appendUsername(); void parseHighlights(bool isPastMsg); - void appendTwitchEmote(const QString &emote, - std::vector> &vec, std::vector &correctPositions); + void appendTwitchEmote( + const QString &emote, + std::vector> &vec, + std::vector &correctPositions); Outcome tryAppendEmote(const EmoteName &name); void addWords( diff --git a/src/providers/twitch/TwitchServer.cpp b/src/providers/twitch/TwitchServer.cpp index 349e4035..eaf90026 100644 --- a/src/providers/twitch/TwitchServer.cpp +++ b/src/providers/twitch/TwitchServer.cpp @@ -156,6 +156,10 @@ void TwitchServer::messageReceived(Communi::IrcMessage *message) { handler.handleClearChatMessage(message); } + else if (command == "CLEARMSG") + { + handler.handleClearMessageMessage(message); + } else if (command == "USERSTATE") { handler.handleUserStateMessage(message); diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 9578c352..a1fdf645 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -1667,7 +1667,9 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link, case Link::UserAction: { QString value = link.value; - value.replace("{user}", layout->getMessage()->loginName).replace("{channel}", this->channel_->getName()); + value.replace("{user}", layout->getMessage()->loginName) + .replace("{channel}", this->channel_->getName()) + .replace("{msg-id}", layout->getMessage()->id); this->channel_->sendMessage(value); } break; diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 3349d2e9..b44e7327 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -161,7 +161,8 @@ ModerationPage::ModerationPage() // clang-format off auto label = modMode.emplace( "Moderation mode is enabled by clicking in a channel that you moderate.

" - "Moderation buttons can be bound to chat commands such as \"/ban {user}\", \"/timeout {user} 1000\", \"/w someusername !report {user} was bad in channel {channel}\" or any other custom text commands.
"); + "Moderation buttons can be bound to chat commands such as \"/ban {user}\", \"/timeout {user} 1000\", \"/w someusername !report {user} was bad in channel {channel}\" or any other custom text commands.
" + "For deleting messages use /delete {msg-id}."); label->setWordWrap(true); label->setStyleSheet("color: #bbb"); // clang-format on