Skip to content

Commit

Permalink
feat: delete messages from context menu (#5956)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nerixyz authored Feb 15, 2025
1 parent 9afd605 commit dd6f204
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 55 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- Minor: Added the ability to filter on messages by the author's user ID (example: `author.user_id == "22484632"`). (#5862)
- Minor: Improved error messaging of the `/clip` command. (#5879)
- Minor: Added Linux support for Live Notifications toasts. (#5881)
- Minor: Messages can now be deleted from the context menu in a channel. (#5956)
- Bugfix: Fixed a potential way to escape the Lua Plugin sandbox. (#5846)
- Bugfix: Fixed a crash relating to Lua HTTP. (#5800)
- Bugfix: Fixed a crash that could occur on Linux and macOS when clicking "Install" from the update prompt. (#5818)
Expand Down
2 changes: 1 addition & 1 deletion src/common/Channel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ void Channel::replaceMessage(size_t hint, const MessagePtr &message,
}
}

void Channel::deleteMessage(QString messageID)
void Channel::disableMessage(QString messageID)
{
auto msg = this->findMessage(messageID);
if (msg != nullptr)
Expand Down
2 changes: 1 addition & 1 deletion src/common/Channel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class Channel : public std::enable_shared_from_this<Channel>, public MessageSink
void replaceMessage(size_t index, const MessagePtr &replacement);
void replaceMessage(size_t hint, const MessagePtr &message,
const MessagePtr &replacement);
void deleteMessage(QString messageID);
void disableMessage(QString messageID);

/// Removes all messages from this channel and invokes #messagesCleared
void clearMessages();
Expand Down
53 changes: 1 addition & 52 deletions src/controllers/commands/builtin/twitch/DeleteMessages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,58 +32,7 @@ QString deleteMessages(TwitchChannel *twitchChannel, const QString &messageID)
return "";
}

getHelix()->deleteChatMessages(
twitchChannel->roomId(), user->getUserId(), messageID,
[]() {
// Success handling, we do nothing: IRC/pubsub-edge will dispatch the correct
// events to update state for us.
},
[twitchChannel, messageID](auto error, auto message) {
QString errorMessage = QString("Failed to delete chat messages - ");

switch (error)
{
case HelixDeleteChatMessagesError::UserMissingScope: {
errorMessage +=
"Missing required scope. Re-login with your "
"account and try again.";
}
break;

case HelixDeleteChatMessagesError::UserNotAuthorized: {
errorMessage +=
"you don't have permission to perform that action.";
}
break;

case HelixDeleteChatMessagesError::MessageUnavailable: {
// Override default message prefix to match with IRC message format
errorMessage =
QString("The message %1 does not exist, was deleted, "
"or is too old to be deleted.")
.arg(messageID);
}
break;

case HelixDeleteChatMessagesError::UserNotAuthenticated: {
errorMessage += "you need to re-authenticate.";
}
break;

case HelixDeleteChatMessagesError::Forwarded: {
errorMessage += message;
}
break;

case HelixDeleteChatMessagesError::Unknown:
default: {
errorMessage += "An unknown error has occurred.";
}
break;
}

twitchChannel->addSystemMessage(errorMessage);
});
twitchChannel->deleteMessagesAs(messageID, user.get());

return "";
}
Expand Down
65 changes: 65 additions & 0 deletions src/providers/twitch/TwitchChannel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1897,6 +1897,71 @@ void TwitchChannel::createClip()
});
}

void TwitchChannel::deleteMessagesAs(const QString &messageID,
TwitchAccount *moderator)
{
getHelix()->deleteChatMessages(
this->roomId(), moderator->getUserId(), messageID,
[]() {
// Success handling, we do nothing: IRC/pubsub will dispatch the correct
// events to update state for us.
},
[lifetime{this->weak_from_this()}, messageID](auto error,
const auto &message) {
auto self =
std::dynamic_pointer_cast<TwitchChannel>(lifetime.lock());
if (!self)
{
return;
}

QString errorMessage = QString("Failed to delete chat messages - ");

switch (error)
{
case HelixDeleteChatMessagesError::UserMissingScope: {
errorMessage +=
"Missing required scope. Re-login with your "
"account and try again.";
}
break;

case HelixDeleteChatMessagesError::UserNotAuthorized: {
errorMessage +=
"you don't have permission to perform that action.";
}
break;

case HelixDeleteChatMessagesError::MessageUnavailable: {
// Override default message prefix to match with IRC message format
errorMessage =
QString("The message %1 does not exist, was deleted, "
"or is too old to be deleted.")
.arg(messageID);
}
break;

case HelixDeleteChatMessagesError::UserNotAuthenticated: {
errorMessage += "you need to re-authenticate.";
}
break;

case HelixDeleteChatMessagesError::Forwarded: {
errorMessage += message;
}
break;

case HelixDeleteChatMessagesError::Unknown:
default: {
errorMessage += "An unknown error has occurred.";
}
break;
}

self->addSystemMessage(errorMessage);
});
}

std::optional<EmotePtr> TwitchChannel::twitchBadge(const QString &set,
const QString &version) const
{
Expand Down
7 changes: 7 additions & 0 deletions src/providers/twitch/TwitchChannel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ struct HelixGlobalBadges;
using HelixChannelBadges = HelixGlobalBadges;

class TwitchIrcServer;
class TwitchAccount;

const int MAX_QUEUED_REDEMPTIONS = 16;

Expand Down Expand Up @@ -164,6 +165,12 @@ class TwitchChannel final : public Channel, public ChannelChatters
QString getCurrentStreamID() const override;
void createClip();

/// Delete the message with the specified ID as a moderator.
///
/// If the ID is empty, all messages will be deleted, effectively clearing
/// the chat.
void deleteMessagesAs(const QString &messageID, TwitchAccount *moderator);

// Data
const QString &subscriptionUrl();
const QString &channelUrl();
Expand Down
2 changes: 1 addition & 1 deletion src/providers/twitch/TwitchIrcServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ void TwitchIrcServer::initialize()
{
// Gray out approve/deny button upon "ALLOWED" and "DENIED" statuses
// They are versions of automod_message_(denied|approved) but for mods.
chan->deleteMessage("automod_" + msg.messageID);
chan->disableMessage("automod_" + msg.messageID);
}
}
break;
Expand Down
12 changes: 12 additions & 0 deletions src/widgets/helper/ChannelView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2570,6 +2570,18 @@ void ChannelView::addMessageContextMenuItems(QMenu *menu,
}
}

auto *twitchChannel =
dynamic_cast<TwitchChannel *>(this->underlyingChannel_.get());
if (!layout->getMessage()->id.isEmpty() && twitchChannel &&
twitchChannel->hasModRights())
{
menu->addAction(
"&Delete message", [twitchChannel, id = layout->getMessage()->id] {
twitchChannel->deleteMessagesAs(
id, getApp()->getAccounts()->twitch.getCurrent().get());
});
}

bool isSearch = this->context_ == Context::Search;
bool isReplyOrUserCard = (this->context_ == Context::ReplyThread ||
this->context_ == Context::UserCard) &&
Expand Down

0 comments on commit dd6f204

Please sign in to comment.