From 34ea303607c165acabe3738f758f690564c3a058 Mon Sep 17 00:00:00 2001 From: pajlada Date: Sat, 25 Jun 2022 14:06:16 +0200 Subject: [PATCH] Move most Command variables into the `CommandController` shared variables (#3824) --- CHANGELOG.md | 1 + .../commands/CommandController.cpp | 290 +++++++++++++----- .../commands/CommandController.hpp | 8 +- src/widgets/helper/ChannelView.cpp | 29 +- 4 files changed, 223 insertions(+), 105 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff37c9a56..d6d20fd95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ - Dev: Overhaul highlight system by moving all checks into a Controller allowing for easier tests. (#3399, #3801) - Dev: Use Game Name returned by Get Streams instead of querying it from the Get Games API. (#3662) - Dev: Batch checking live status for all channels after startup. (#3757, #3762, #3767) +- Dev: Move most command context into the command controller. (#3824) ## 2.3.5 diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 1dd90b445..662cb1504 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -177,80 +177,213 @@ bool appendWhisperMessageStringLocally(const QString &textNoEmoji) return false; } -const std::function - noOpPlaceholder = [](const auto &altText, const auto &channel) { +using VariableReplacer = std::function; + +const VariableReplacer NO_OP_PLACEHOLDER = + [](const auto &altText, const auto &channel, const auto *message) { return altText; }; -const std::map> - COMMAND_VARS{ - { - "channel.name", - [](const auto &altText, const auto &channel) { - (void)(altText); //unused - return channel->getName(); - }, +const std::unordered_map COMMAND_VARS{ + { + "channel.name", + [](const auto &altText, const auto &channel, const auto *message) { + (void)(altText); //unused + (void)(message); //unused + return channel->getName(); }, - { - "channel.id", - [](const auto &altText, const auto &channel) { - auto *tc = dynamic_cast(channel.get()); - if (tc == nullptr) - { - return altText; - } + }, + { + "channel.id", + [](const auto &altText, const auto &channel, const auto *message) { + (void)(message); //unused + auto *tc = dynamic_cast(channel.get()); + if (tc == nullptr) + { + return altText; + } - return tc->roomId(); - }, + return tc->roomId(); }, - { - "stream.game", - [](const auto &altText, const auto &channel) { - auto *tc = dynamic_cast(channel.get()); - if (tc == nullptr) - { - return altText; - } - const auto &status = tc->accessStreamStatus(); - return status->live ? status->game : altText; - }, + }, + { + // NOTE: The use of {channel} is deprecated and support for it will drop at some point + // Users should be encouraged to use {channel.name} instead. + "channel", + [](const auto &altText, const auto &channel, const auto *message) { + (void)(altText); //unused + (void)(message); //unused + return channel->getName(); }, - { - "stream.title", - [](const auto &altText, const auto &channel) { - auto *tc = dynamic_cast(channel.get()); - if (tc == nullptr) - { - return altText; - } - const auto &status = tc->accessStreamStatus(); - return status->live ? status->title : altText; - }, + }, + { + "stream.game", + [](const auto &altText, const auto &channel, const auto *message) { + (void)(message); //unused + auto *tc = dynamic_cast(channel.get()); + if (tc == nullptr) + { + return altText; + } + const auto &status = tc->accessStreamStatus(); + return status->live ? status->game : altText; }, - { - "my.id", - [](const auto &altText, const auto &channel) { - (void)(channel); //unused - auto uid = getApp()->accounts->twitch.getCurrent()->getUserId(); - return uid.isEmpty() ? altText : uid; - }, + }, + { + "stream.title", + [](const auto &altText, const auto &channel, const auto *message) { + (void)(message); //unused + auto *tc = dynamic_cast(channel.get()); + if (tc == nullptr) + { + return altText; + } + const auto &status = tc->accessStreamStatus(); + return status->live ? status->title : altText; }, - { - "my.name", - [](const auto &altText, const auto &channel) { - (void)(channel); //unused - auto name = - getApp()->accounts->twitch.getCurrent()->getUserName(); - return name.isEmpty() ? altText : name; - }, + }, + { + "my.id", + [](const auto &altText, const auto &channel, const auto *message) { + (void)(channel); //unused + (void)(message); //unused + auto uid = getApp()->accounts->twitch.getCurrent()->getUserId(); + return uid.isEmpty() ? altText : uid; }, - // variables used in mod buttons and the like, these make no sense in normal commands, so they are left empty - {"input.text", noOpPlaceholder}, - {"msg.id", noOpPlaceholder}, - {"user.name", noOpPlaceholder}, - {"msg.text", noOpPlaceholder}, - }; + }, + { + "my.name", + [](const auto &altText, const auto &channel, const auto *message) { + (void)(channel); //unused + (void)(message); //unused + auto name = getApp()->accounts->twitch.getCurrent()->getUserName(); + return name.isEmpty() ? altText : name; + }, + }, + { + "user.name", + [](const auto &altText, const auto &channel, const auto *message) { + (void)(channel); //unused + if (message == nullptr) + { + return altText; + } + + const auto &v = message->loginName; + + if (v.isEmpty()) + { + return altText; + } + + return v; + }, + }, + { + // NOTE: The use of {user} is deprecated and support for it will drop at some point + // Users should be encouraged to use {user.name} instead. + "user", + [](const auto &altText, const auto &channel, const auto *message) { + (void)(channel); //unused + if (message == nullptr) + { + return altText; + } + + const auto &v = message->loginName; + + if (v.isEmpty()) + { + return altText; + } + + return v; + }, + }, + { + "msg.id", + [](const auto &altText, const auto &channel, const auto *message) { + (void)(channel); //unused + if (message == nullptr) + { + return altText; + } + + const auto &v = message->id; + + if (v.isEmpty()) + { + return altText; + } + + return v; + }, + }, + { + // NOTE: The use of {msg-id} is deprecated and support for it will drop at some point + // Users should be encouraged to use {msg.id} instead. + "msg-id", + [](const auto &altText, const auto &channel, const auto *message) { + (void)(channel); //unused + if (message == nullptr) + { + return altText; + } + + const auto &v = message->id; + + if (v.isEmpty()) + { + return altText; + } + + return v; + }, + }, + { + "msg.text", + [](const auto &altText, const auto &channel, const auto *message) { + (void)(channel); //unused + if (message == nullptr) + { + return altText; + } + + const auto &v = message->messageText; + + if (v.isEmpty()) + { + return altText; + } + + return v; + }, + }, + { + // NOTE: The use of {message} is deprecated and support for it will drop at some point + // Users should be encouraged to use {msg.text} instead. + "message", + [](const auto &altText, const auto &channel, const auto *message) { + (void)(channel); //unused + if (message == nullptr) + { + return altText; + } + + const auto &v = message->messageText; + + if (v.isEmpty()) + { + return altText; + } + + return v; + }, + }, + // variables used in mod buttons and the like, these make no sense in normal commands, so they are left empty + {"input.text", NO_OP_PLACEHOLDER}, +}; } // namespace @@ -1120,10 +1253,10 @@ void CommandController::registerCommand(QString commandName, this->defaultChatterinoCommandAutoCompletions_.append(commandName); } -QString CommandController::execCustomCommand(const QStringList &words, - const Command &command, - bool dryRun, ChannelPtr channel, - std::map context) +QString CommandController::execCustomCommand( + const QStringList &words, const Command &command, bool dryRun, + ChannelPtr channel, const Message *message, + std::unordered_map context) { QString result; @@ -1167,20 +1300,21 @@ QString CommandController::execCustomCommand(const QStringList &words, if (var != context.end()) { + // Found variable in `context` result += var->second.isEmpty() ? altText : var->second; + continue; } - else + + auto it = COMMAND_VARS.find(varName); + if (it != COMMAND_VARS.end()) { - auto it = COMMAND_VARS.find(varName); - if (it != COMMAND_VARS.end()) - { - result += it->second(altText, channel); - } - else - { - result += "{" + match.captured(3) + "}"; - } + // Found variable in `COMMAND_VARS` + result += it->second(altText, channel, message); + continue; } + + // Fall back to replacing it with the actual matched string + result += "{" + match.captured(3) + "}"; continue; } diff --git a/src/controllers/commands/CommandController.hpp b/src/controllers/commands/CommandController.hpp index b27610109..f36ea3be6 100644 --- a/src/controllers/commands/CommandController.hpp +++ b/src/controllers/commands/CommandController.hpp @@ -11,6 +11,7 @@ #include #include +#include namespace chatterino { @@ -34,9 +35,10 @@ public: CommandModel *createModel(QObject *parent); - QString execCustomCommand(const QStringList &words, const Command &command, - bool dryRun, ChannelPtr channel, - std::map context = {}); + QString execCustomCommand( + const QStringList &words, const Command &command, bool dryRun, + ChannelPtr channel, const Message *message = nullptr, + std::unordered_map context = {}); private: void load(Paths &paths); diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 29e85d268..8fdc3ba92 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -2125,20 +2125,12 @@ void ChannelView::addCommandExecutionContextMenuItems( { userText = split->getInput().getInputText(); } + + // Execute command through right-clicking a message -> Execute command QString value = getApp()->commands->execCustomCommand( - inputText.split(' '), cmd, true, channel, + inputText.split(' '), cmd, true, channel, layout->getMessage(), { - {"user.name", layout->getMessage()->loginName}, - {"msg.id", layout->getMessage()->id}, - {"msg.text", layout->getMessage()->messageText}, {"input.text", userText}, - - // old placeholders - {"user", layout->getMessage()->loginName}, - {"msg-id", layout->getMessage()->id}, - {"message", layout->getMessage()->messageText}, - - {"channel", this->channel()->getName()}, }); value = getApp()->commands->execCommand(value, channel, false); @@ -2283,21 +2275,10 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link, } } + // Execute command clicking a moderator button value = getApp()->commands->execCustomCommand( QStringList(), Command{"(modaction)", value}, true, channel, - { - {"user.name", layout->getMessage()->loginName}, - {"msg.id", layout->getMessage()->id}, - {"msg.text", layout->getMessage()->messageText}, - - // old placeholders - {"user", layout->getMessage()->loginName}, - {"msg-id", layout->getMessage()->id}, - {"message", layout->getMessage()->messageText}, - - // new version of this is inside execCustomCommand - {"channel", this->channel()->getName()}, - }); + layout->getMessage()); value = getApp()->commands->execCommand(value, channel, false);