Move most Command variables into the CommandController shared variables (#3824)

This commit is contained in:
pajlada 2022-06-25 14:06:16 +02:00 committed by GitHub
parent 6a58ce1273
commit 34ea303607
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 223 additions and 105 deletions

View file

@ -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

View file

@ -177,80 +177,213 @@ bool appendWhisperMessageStringLocally(const QString &textNoEmoji)
return false;
}
const std::function<QString(const QString &, const ChannelPtr &)>
noOpPlaceholder = [](const auto &altText, const auto &channel) {
using VariableReplacer = std::function<QString(
const QString &, const ChannelPtr &, const Message *)>;
const VariableReplacer NO_OP_PLACEHOLDER =
[](const auto &altText, const auto &channel, const auto *message) {
return altText;
};
const std::map<QString,
std::function<QString(const QString &, const ChannelPtr &)>>
COMMAND_VARS{
{
"channel.name",
[](const auto &altText, const auto &channel) {
(void)(altText); //unused
return channel->getName();
},
const std::unordered_map<QString, VariableReplacer> 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<TwitchChannel *>(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<TwitchChannel *>(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<TwitchChannel *>(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<TwitchChannel *>(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<TwitchChannel *>(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<TwitchChannel *>(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<QString, QString> context)
QString CommandController::execCustomCommand(
const QStringList &words, const Command &command, bool dryRun,
ChannelPtr channel, const Message *message,
std::unordered_map<QString, QString> 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;
}

View file

@ -11,6 +11,7 @@
#include <memory>
#include <mutex>
#include <unordered_map>
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<QString, QString> context = {});
QString execCustomCommand(
const QStringList &words, const Command &command, bool dryRun,
ChannelPtr channel, const Message *message = nullptr,
std::unordered_map<QString, QString> context = {});
private:
void load(Paths &paths);

View file

@ -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);