2022-12-31 15:41:01 +01:00
|
|
|
#include "controllers/commands/CommandController.hpp"
|
2019-09-08 22:27:57 +02:00
|
|
|
|
|
|
|
#include "Application.hpp"
|
2023-11-08 18:57:09 +01:00
|
|
|
#include "common/Channel.hpp"
|
2019-09-08 22:27:57 +02:00
|
|
|
#include "controllers/accounts/AccountController.hpp"
|
2023-05-17 23:32:50 +02:00
|
|
|
#include "controllers/commands/builtin/chatterino/Debugging.hpp"
|
2023-11-08 18:57:09 +01:00
|
|
|
#include "controllers/commands/builtin/Misc.hpp"
|
|
|
|
#include "controllers/commands/builtin/twitch/AddModerator.hpp"
|
|
|
|
#include "controllers/commands/builtin/twitch/AddVIP.hpp"
|
|
|
|
#include "controllers/commands/builtin/twitch/Announce.hpp"
|
2023-11-04 17:26:58 +01:00
|
|
|
#include "controllers/commands/builtin/twitch/Ban.hpp"
|
2023-11-08 18:57:09 +01:00
|
|
|
#include "controllers/commands/builtin/twitch/Block.hpp"
|
Sort and force grouping of includes (#4172)
This change enforces strict include grouping using IncludeCategories
In addition to adding this to the .clang-format file and applying it in the tests/src and src directories, I also did the following small changes:
In ChatterSet.hpp, I changed lrucache to a <>include
In Irc2.hpp, I change common/SignalVector.hpp to a "project-include"
In AttachedWindow.cpp, NativeMessaging.cpp, WindowsHelper.hpp, BaseWindow.cpp, and StreamerMode.cpp, I disabled clang-format for the windows-includes
In WindowDescriptors.hpp, I added the missing vector include. It was previously not needed because the include was handled by another file that was previously included first.
clang-format minimum version has been bumped, so Ubuntu version used in the check-formatting job has been bumped to 22.04 (which is the latest LTS)
2022-11-27 19:32:53 +01:00
|
|
|
#include "controllers/commands/builtin/twitch/ChatSettings.hpp"
|
2023-11-08 18:57:09 +01:00
|
|
|
#include "controllers/commands/builtin/twitch/Chatters.hpp"
|
|
|
|
#include "controllers/commands/builtin/twitch/DeleteMessages.hpp"
|
|
|
|
#include "controllers/commands/builtin/twitch/GetModerators.hpp"
|
|
|
|
#include "controllers/commands/builtin/twitch/GetVIPs.hpp"
|
|
|
|
#include "controllers/commands/builtin/twitch/Raid.hpp"
|
|
|
|
#include "controllers/commands/builtin/twitch/RemoveModerator.hpp"
|
|
|
|
#include "controllers/commands/builtin/twitch/RemoveVIP.hpp"
|
|
|
|
#include "controllers/commands/builtin/twitch/SendReply.hpp"
|
|
|
|
#include "controllers/commands/builtin/twitch/SendWhisper.hpp"
|
2023-05-07 12:52:05 +02:00
|
|
|
#include "controllers/commands/builtin/twitch/ShieldMode.hpp"
|
2023-05-20 18:32:06 +02:00
|
|
|
#include "controllers/commands/builtin/twitch/Shoutout.hpp"
|
2023-11-08 18:57:09 +01:00
|
|
|
#include "controllers/commands/builtin/twitch/StartCommercial.hpp"
|
|
|
|
#include "controllers/commands/builtin/twitch/Unban.hpp"
|
|
|
|
#include "controllers/commands/builtin/twitch/UpdateChannel.hpp"
|
|
|
|
#include "controllers/commands/builtin/twitch/UpdateColor.hpp"
|
2024-06-22 12:36:29 +02:00
|
|
|
#include "controllers/commands/builtin/twitch/Warn.hpp"
|
2019-09-08 22:27:57 +02:00
|
|
|
#include "controllers/commands/Command.hpp"
|
2022-12-31 15:41:01 +01:00
|
|
|
#include "controllers/commands/CommandContext.hpp"
|
2019-09-08 22:27:57 +02:00
|
|
|
#include "controllers/commands/CommandModel.hpp"
|
2023-04-02 15:31:53 +02:00
|
|
|
#include "controllers/plugins/PluginController.hpp"
|
2019-09-08 22:27:57 +02:00
|
|
|
#include "messages/Message.hpp"
|
|
|
|
#include "messages/MessageBuilder.hpp"
|
2022-12-18 15:36:39 +01:00
|
|
|
#include "providers/twitch/TwitchAccount.hpp"
|
2022-12-31 15:41:01 +01:00
|
|
|
#include "providers/twitch/TwitchChannel.hpp"
|
2021-12-26 14:21:52 +01:00
|
|
|
#include "providers/twitch/TwitchCommon.hpp"
|
2019-09-08 22:27:57 +02:00
|
|
|
#include "singletons/Emotes.hpp"
|
|
|
|
#include "singletons/Paths.hpp"
|
|
|
|
#include "util/CombinePath.hpp"
|
2023-11-08 18:57:09 +01:00
|
|
|
#include "util/QStringHash.hpp"
|
2019-09-08 22:27:57 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
#include <QString>
|
2019-09-08 22:27:57 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
#include <unordered_map>
|
2019-09-08 22:27:57 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
namespace {
|
2019-09-08 22:27:57 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
using namespace chatterino;
|
2021-09-11 14:35:26 +02:00
|
|
|
|
2022-06-25 14:06:16 +02:00
|
|
|
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) {
|
2022-05-29 14:23:29 +02:00
|
|
|
return altText;
|
|
|
|
};
|
|
|
|
|
2022-06-25 14:06:16 +02:00
|
|
|
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();
|
2021-09-11 14:35:26 +02:00
|
|
|
},
|
2022-06-25 14:06:16 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"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;
|
|
|
|
}
|
2021-09-11 14:35:26 +02:00
|
|
|
|
2022-06-25 14:06:16 +02:00
|
|
|
return tc->roomId();
|
2021-09-11 14:35:26 +02:00
|
|
|
},
|
2022-06-25 14:06:16 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
// 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();
|
2021-09-11 14:35:26 +02:00
|
|
|
},
|
2022-06-25 14:06:16 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"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;
|
2021-09-11 14:35:26 +02:00
|
|
|
},
|
2022-06-25 14:06:16 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"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;
|
2021-09-11 14:35:26 +02:00
|
|
|
},
|
2022-06-25 14:06:16 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"my.id",
|
|
|
|
[](const auto &altText, const auto &channel, const auto *message) {
|
|
|
|
(void)(channel); //unused
|
|
|
|
(void)(message); //unused
|
2024-01-19 17:59:55 +01:00
|
|
|
auto uid =
|
|
|
|
getIApp()->getAccounts()->twitch.getCurrent()->getUserId();
|
2022-06-25 14:06:16 +02:00
|
|
|
return uid.isEmpty() ? altText : uid;
|
2021-09-11 14:35:26 +02:00
|
|
|
},
|
2022-06-25 14:06:16 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"my.name",
|
|
|
|
[](const auto &altText, const auto &channel, const auto *message) {
|
|
|
|
(void)(channel); //unused
|
|
|
|
(void)(message); //unused
|
2024-01-19 17:59:55 +01:00
|
|
|
auto name =
|
|
|
|
getIApp()->getAccounts()->twitch.getCurrent()->getUserName();
|
2022-06-25 14:06:16 +02:00
|
|
|
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},
|
|
|
|
};
|
2021-09-11 14:35:26 +02:00
|
|
|
|
2019-09-08 22:27:57 +02:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace chatterino {
|
|
|
|
|
2024-01-16 21:56:43 +01:00
|
|
|
void CommandController::initialize(Settings &, const Paths &paths)
|
2019-09-08 22:27:57 +02:00
|
|
|
{
|
|
|
|
// Update commands map when the vector of commands has been updated
|
|
|
|
auto addFirstMatchToMap = [this](auto args) {
|
2020-09-26 10:01:00 +02:00
|
|
|
this->userCommands_.remove(args.item.name);
|
2019-09-08 22:27:57 +02:00
|
|
|
|
2021-12-26 14:21:52 +01:00
|
|
|
for (const Command &cmd : this->items)
|
2019-09-08 22:27:57 +02:00
|
|
|
{
|
|
|
|
if (cmd.name == args.item.name)
|
|
|
|
{
|
2020-09-26 10:01:00 +02:00
|
|
|
this->userCommands_[cmd.name] = cmd;
|
2019-09-08 22:27:57 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int maxSpaces = 0;
|
|
|
|
|
2021-12-26 14:21:52 +01:00
|
|
|
for (const Command &cmd : this->items)
|
2019-09-08 22:27:57 +02:00
|
|
|
{
|
|
|
|
auto localMaxSpaces = cmd.name.count(' ');
|
|
|
|
if (localMaxSpaces > maxSpaces)
|
|
|
|
{
|
|
|
|
maxSpaces = localMaxSpaces;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this->maxSpaces_ = maxSpaces;
|
|
|
|
};
|
2023-09-16 13:52:51 +02:00
|
|
|
// We can safely ignore these signal connections since items will be destroyed
|
|
|
|
// before CommandController
|
|
|
|
std::ignore = this->items.itemInserted.connect(addFirstMatchToMap);
|
|
|
|
std::ignore = this->items.itemRemoved.connect(addFirstMatchToMap);
|
2019-09-08 22:27:57 +02:00
|
|
|
|
|
|
|
// Initialize setting manager for commands.json
|
|
|
|
auto path = combinePath(paths.settingsDirectory, "commands.json");
|
|
|
|
this->sm_ = std::make_shared<pajlada::Settings::SettingManager>();
|
2021-12-11 12:56:19 +01:00
|
|
|
this->sm_->setPath(qPrintable(path));
|
2021-08-15 14:33:31 +02:00
|
|
|
this->sm_->setBackupEnabled(true);
|
|
|
|
this->sm_->setBackupSlots(9);
|
2019-09-08 22:27:57 +02:00
|
|
|
|
|
|
|
// Delayed initialization of the setting storing all commands
|
|
|
|
this->commandsSetting_.reset(
|
|
|
|
new pajlada::Settings::Setting<std::vector<Command>>("/commands",
|
|
|
|
this->sm_));
|
|
|
|
|
|
|
|
// Update the setting when the vector of commands has been updated (most
|
|
|
|
// likely from the settings dialog)
|
2023-09-16 13:52:51 +02:00
|
|
|
std::ignore = this->items.delayedItemsChanged.connect([this] {
|
2021-12-26 14:21:52 +01:00
|
|
|
this->commandsSetting_->setValue(this->items.raw());
|
2019-09-08 22:27:57 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
// Load commands from commands.json
|
|
|
|
this->sm_->load();
|
|
|
|
|
|
|
|
// Add loaded commands to our vector of commands (which will update the map
|
|
|
|
// of commands)
|
|
|
|
for (const auto &command : this->commandsSetting_->getValue())
|
|
|
|
{
|
2021-12-26 14:21:52 +01:00
|
|
|
this->items.append(command);
|
2019-09-08 22:27:57 +02:00
|
|
|
}
|
|
|
|
|
2021-02-14 14:01:13 +01:00
|
|
|
/// Deprecated commands
|
2019-09-08 22:27:57 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/ignore", &commands::ignoreUser);
|
2019-09-08 22:27:57 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/unignore", &commands::unignoreUser);
|
2020-09-26 10:01:00 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/follow", &commands::follow);
|
2019-09-08 22:27:57 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/unfollow", &commands::unfollow);
|
2021-08-04 22:41:27 +02:00
|
|
|
|
2021-02-14 14:01:13 +01:00
|
|
|
/// Supported commands
|
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/debug-args", &commands::listArgs);
|
2022-03-05 11:54:09 +01:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/debug-env", &commands::listEnvironmentVariables);
|
2022-03-05 11:54:09 +01:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/uptime", &commands::uptime);
|
2022-03-05 11:54:09 +01:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/block", &commands::blockUser);
|
2022-03-05 11:54:09 +01:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/unblock", &commands::unblockUser);
|
2022-03-05 11:54:09 +01:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/user", &commands::user);
|
2022-03-05 11:54:09 +01:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/usercard", &commands::openUsercard);
|
2022-03-05 11:54:09 +01:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/requests", &commands::requests);
|
2021-03-14 18:25:45 +01:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/lowtrust", &commands::lowtrust);
|
2021-03-14 18:25:45 +01:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/chatters", &commands::chatters);
|
2022-07-10 15:08:20 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/test-chatters", &commands::testChatters);
|
2021-03-14 18:25:45 +01:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/mods", &commands::getModerators);
|
2022-05-23 00:42:52 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/clip", &commands::clip);
|
2021-06-27 13:40:44 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/marker", &commands::marker);
|
2021-06-27 13:40:44 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/streamlink", &commands::streamlink);
|
2021-06-27 13:40:44 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/popout", &commands::popout);
|
2022-05-23 00:42:52 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/popup", &commands::popup);
|
2022-05-23 00:42:52 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/clearmessages", &commands::clearmessages);
|
2022-07-31 12:45:25 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/settitle", &commands::setTitle);
|
2022-07-31 12:45:25 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/setgame", &commands::setGame);
|
2022-07-31 12:45:25 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/openurl", &commands::openURL);
|
2022-07-31 12:45:25 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/raw", &commands::sendRawMessage);
|
2022-07-31 12:45:25 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/reply", &commands::sendReply);
|
2022-07-31 12:45:25 +02:00
|
|
|
|
2022-01-02 16:43:51 +01:00
|
|
|
#ifndef NDEBUG
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/fakemsg", &commands::injectFakeMessage);
|
2024-03-09 11:22:23 +01:00
|
|
|
this->registerCommand("/debug-update-to-no-stream",
|
|
|
|
&commands::injectStreamUpdateNoStream);
|
2022-01-02 16:43:51 +01:00
|
|
|
#endif
|
2022-05-23 00:42:52 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/copy", &commands::copyToClipboard);
|
2022-10-01 16:00:45 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/color", &commands::updateUserColor);
|
2022-10-01 17:10:06 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/clear", &commands::deleteAllMessages);
|
2022-10-01 17:10:06 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/delete", &commands::deleteOneMessage);
|
2022-10-02 15:27:55 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/mod", &commands::addModerator);
|
2022-10-02 15:27:55 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/unmod", &commands::removeModerator);
|
2022-10-02 15:27:55 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/announce", &commands::sendAnnouncement);
|
2024-03-14 20:36:58 +01:00
|
|
|
this->registerCommand("/announceblue", &commands::sendAnnouncementBlue);
|
|
|
|
this->registerCommand("/announcegreen", &commands::sendAnnouncementGreen);
|
|
|
|
this->registerCommand("/announceorange", &commands::sendAnnouncementOrange);
|
|
|
|
this->registerCommand("/announcepurple", &commands::sendAnnouncementPurple);
|
2022-10-08 14:10:38 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/vip", &commands::addVIP);
|
2022-10-08 14:10:38 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/unvip", &commands::removeVIP);
|
2022-10-08 14:10:38 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/unban", &commands::unbanUser);
|
|
|
|
this->registerCommand("/untimeout", &commands::unbanUser);
|
2022-10-08 14:10:38 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/raid", &commands::startRaid);
|
2022-10-08 14:10:38 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/unraid", &commands::cancelRaid);
|
2022-10-08 14:10:38 +02:00
|
|
|
|
2022-11-06 13:07:54 +01:00
|
|
|
this->registerCommand("/emoteonly", &commands::emoteOnly);
|
|
|
|
this->registerCommand("/emoteonlyoff", &commands::emoteOnlyOff);
|
2022-10-03 19:42:02 +02:00
|
|
|
|
2022-11-06 13:07:54 +01:00
|
|
|
this->registerCommand("/subscribers", &commands::subscribers);
|
|
|
|
this->registerCommand("/subscribersoff", &commands::subscribersOff);
|
2022-10-03 19:42:02 +02:00
|
|
|
|
2022-11-06 13:07:54 +01:00
|
|
|
this->registerCommand("/slow", &commands::slow);
|
|
|
|
this->registerCommand("/slowoff", &commands::slowOff);
|
2022-10-03 19:42:02 +02:00
|
|
|
|
2022-11-06 13:07:54 +01:00
|
|
|
this->registerCommand("/followers", &commands::followers);
|
|
|
|
this->registerCommand("/followersoff", &commands::followersOff);
|
2022-10-03 19:42:02 +02:00
|
|
|
|
2022-11-06 13:07:54 +01:00
|
|
|
this->registerCommand("/uniquechat", &commands::uniqueChat);
|
|
|
|
this->registerCommand("/r9kbeta", &commands::uniqueChat);
|
|
|
|
this->registerCommand("/uniquechatoff", &commands::uniqueChatOff);
|
|
|
|
this->registerCommand("/r9kbetaoff", &commands::uniqueChatOff);
|
2022-10-06 23:52:25 +02:00
|
|
|
|
2023-11-04 17:26:58 +01:00
|
|
|
this->registerCommand("/timeout", &commands::sendTimeout);
|
2022-10-06 23:52:25 +02:00
|
|
|
|
2023-11-04 17:26:58 +01:00
|
|
|
this->registerCommand("/ban", &commands::sendBan);
|
|
|
|
this->registerCommand("/banid", &commands::sendBanById);
|
2023-02-26 21:03:14 +01:00
|
|
|
|
2024-06-22 12:36:29 +02:00
|
|
|
this->registerCommand("/warn", &commands::sendWarn);
|
|
|
|
|
2022-10-08 13:11:55 +02:00
|
|
|
for (const auto &cmd : TWITCH_WHISPER_COMMANDS)
|
|
|
|
{
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand(cmd, &commands::sendWhisper);
|
2022-10-08 13:11:55 +02:00
|
|
|
}
|
2022-10-10 12:56:55 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/vips", &commands::getVIPs);
|
2022-11-05 10:43:31 +01:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/commercial", &commands::startCommercial);
|
2022-11-05 10:43:31 +01:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/unstable-set-user-color",
|
|
|
|
&commands::unstableSetUserClientSideColor);
|
2022-11-05 10:43:31 +01:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/debug-force-image-gc",
|
|
|
|
&commands::forceImageGarbageCollection);
|
2023-05-07 12:52:05 +02:00
|
|
|
|
2023-11-08 18:57:09 +01:00
|
|
|
this->registerCommand("/debug-force-image-unload",
|
|
|
|
&commands::forceImageUnload);
|
2023-05-27 14:18:08 +02:00
|
|
|
|
2023-05-07 12:52:05 +02:00
|
|
|
this->registerCommand("/shield", &commands::shieldModeOn);
|
|
|
|
this->registerCommand("/shieldoff", &commands::shieldModeOff);
|
2023-05-17 23:32:50 +02:00
|
|
|
|
2023-05-20 18:32:06 +02:00
|
|
|
this->registerCommand("/shoutout", &commands::sendShoutout);
|
|
|
|
|
2023-05-17 23:32:50 +02:00
|
|
|
this->registerCommand("/c2-set-logging-rules", &commands::setLoggingRules);
|
2023-07-23 14:13:21 +02:00
|
|
|
this->registerCommand("/c2-theme-autoreload", &commands::toggleThemeReload);
|
2020-09-26 10:01:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CommandController::save()
|
|
|
|
{
|
|
|
|
this->sm_->save();
|
|
|
|
}
|
|
|
|
|
|
|
|
CommandModel *CommandController::createModel(QObject *parent)
|
|
|
|
{
|
|
|
|
CommandModel *model = new CommandModel(parent);
|
2021-12-26 14:21:52 +01:00
|
|
|
model->initialize(&this->items);
|
2020-09-26 10:01:00 +02:00
|
|
|
|
|
|
|
return model;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString CommandController::execCommand(const QString &textNoEmoji,
|
|
|
|
ChannelPtr channel, bool dryRun)
|
|
|
|
{
|
2024-01-20 11:49:32 +01:00
|
|
|
QString text =
|
|
|
|
getIApp()->getEmotes()->getEmojis()->replaceShortCodes(textNoEmoji);
|
2022-05-14 12:11:39 +02:00
|
|
|
QStringList words = text.split(' ', Qt::SkipEmptyParts);
|
2020-09-26 10:01:00 +02:00
|
|
|
|
|
|
|
if (words.length() == 0)
|
|
|
|
{
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString commandName = words[0];
|
|
|
|
|
2021-04-17 12:33:18 +02:00
|
|
|
{
|
|
|
|
// check if user command exists
|
|
|
|
const auto it = this->userCommands_.find(commandName);
|
|
|
|
if (it != this->userCommands_.end())
|
|
|
|
{
|
2024-01-20 11:49:32 +01:00
|
|
|
text = getIApp()->getEmotes()->getEmojis()->replaceShortCodes(
|
2021-09-11 14:35:26 +02:00
|
|
|
this->execCustomCommand(words, it.value(), dryRun, channel));
|
2021-04-17 12:33:18 +02:00
|
|
|
|
2022-05-14 12:11:39 +02:00
|
|
|
words = text.split(' ', Qt::SkipEmptyParts);
|
2021-04-17 12:33:18 +02:00
|
|
|
|
|
|
|
if (words.length() == 0)
|
|
|
|
{
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
commandName = words[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-17 20:22:47 +01:00
|
|
|
if (!dryRun)
|
2019-09-08 22:27:57 +02:00
|
|
|
{
|
2020-09-26 10:01:00 +02:00
|
|
|
// check if command exists
|
|
|
|
const auto it = this->commands_.find(commandName);
|
|
|
|
if (it != this->commands_.end())
|
|
|
|
{
|
2022-11-06 13:07:54 +01:00
|
|
|
if (auto *command = std::get_if<CommandFunction>(&it->second))
|
|
|
|
{
|
|
|
|
return (*command)(words, channel);
|
|
|
|
}
|
|
|
|
if (auto *command =
|
|
|
|
std::get_if<CommandFunctionWithContext>(&it->second))
|
|
|
|
{
|
|
|
|
CommandContext ctx{
|
|
|
|
words,
|
|
|
|
channel,
|
|
|
|
dynamic_cast<TwitchChannel *>(channel.get()),
|
|
|
|
};
|
|
|
|
return (*command)(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
2020-09-26 10:01:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-19 20:19:18 +01:00
|
|
|
// We have checks to ensure words cannot be empty, so this can never wrap around
|
|
|
|
auto maxSpaces = std::min(this->maxSpaces_, (qsizetype)words.length() - 1);
|
2019-09-08 22:27:57 +02:00
|
|
|
for (int i = 0; i < maxSpaces; ++i)
|
|
|
|
{
|
|
|
|
commandName += ' ' + words[i + 1];
|
|
|
|
|
2020-09-26 10:01:00 +02:00
|
|
|
const auto it = this->userCommands_.find(commandName);
|
|
|
|
if (it != this->userCommands_.end())
|
2019-09-08 22:27:57 +02:00
|
|
|
{
|
2021-09-11 14:35:26 +02:00
|
|
|
return this->execCustomCommand(words, it.value(), dryRun, channel);
|
2019-09-08 22:27:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-15 12:50:03 +01:00
|
|
|
if (!dryRun && channel->getType() == Channel::Type::TwitchWhispers)
|
|
|
|
{
|
|
|
|
channel->addMessage(
|
|
|
|
makeSystemMessage("Use /w <username> <message> to whisper"));
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2019-09-08 22:27:57 +02:00
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
2023-04-02 15:31:53 +02:00
|
|
|
#ifdef CHATTERINO_HAVE_PLUGINS
|
|
|
|
bool CommandController::registerPluginCommand(const QString &commandName)
|
|
|
|
{
|
|
|
|
if (this->commands_.contains(commandName))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->commands_[commandName] = [commandName](const CommandContext &ctx) {
|
2024-01-19 17:59:55 +01:00
|
|
|
return getIApp()->getPlugins()->tryExecPluginCommand(commandName, ctx);
|
2023-04-02 15:31:53 +02:00
|
|
|
};
|
|
|
|
this->pluginCommands_.append(commandName);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CommandController::unregisterPluginCommand(const QString &commandName)
|
|
|
|
{
|
|
|
|
if (!this->pluginCommands_.contains(commandName))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
this->pluginCommands_.removeAll(commandName);
|
|
|
|
return this->commands_.erase(commandName) != 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-11-06 13:07:54 +01:00
|
|
|
void CommandController::registerCommand(const QString &commandName,
|
|
|
|
CommandFunctionVariants commandFunction)
|
2020-09-26 10:01:00 +02:00
|
|
|
{
|
2022-11-06 13:07:54 +01:00
|
|
|
assert(this->commands_.count(commandName) == 0);
|
2020-09-26 10:01:00 +02:00
|
|
|
|
2022-11-06 13:07:54 +01:00
|
|
|
this->commands_[commandName] = std::move(commandFunction);
|
2020-09-26 10:01:00 +02:00
|
|
|
|
2021-12-26 14:21:52 +01:00
|
|
|
this->defaultChatterinoCommandAutoCompletions_.append(commandName);
|
2020-09-26 10:01:00 +02:00
|
|
|
}
|
|
|
|
|
2022-06-25 14:06:16 +02:00
|
|
|
QString CommandController::execCustomCommand(
|
2022-10-08 13:11:55 +02:00
|
|
|
const QStringList &words, const Command &command, bool /* dryRun */,
|
2022-06-25 14:06:16 +02:00
|
|
|
ChannelPtr channel, const Message *message,
|
|
|
|
std::unordered_map<QString, QString> context)
|
2019-09-08 22:27:57 +02:00
|
|
|
{
|
|
|
|
QString result;
|
|
|
|
|
2021-09-11 14:35:26 +02:00
|
|
|
static QRegularExpression parseCommand(
|
|
|
|
R"((^|[^{])({{)*{(\d+\+?|([a-zA-Z.-]+)(?:;(.+?))?)})");
|
2019-09-08 22:27:57 +02:00
|
|
|
|
|
|
|
int lastCaptureEnd = 0;
|
|
|
|
|
|
|
|
auto globalMatch = parseCommand.globalMatch(command.func);
|
|
|
|
int matchOffset = 0;
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
QRegularExpressionMatch match =
|
|
|
|
parseCommand.match(command.func, matchOffset);
|
|
|
|
|
|
|
|
if (!match.hasMatch())
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
result += command.func.mid(lastCaptureEnd,
|
|
|
|
match.capturedStart() - lastCaptureEnd + 1);
|
|
|
|
|
|
|
|
lastCaptureEnd = match.capturedEnd();
|
|
|
|
matchOffset = lastCaptureEnd - 1;
|
|
|
|
|
|
|
|
QString wordIndexMatch = match.captured(3);
|
|
|
|
|
|
|
|
bool plus = wordIndexMatch.at(wordIndexMatch.size() - 1) == '+';
|
|
|
|
wordIndexMatch = wordIndexMatch.replace("+", "");
|
|
|
|
|
|
|
|
bool ok;
|
|
|
|
int wordIndex = wordIndexMatch.replace("=", "").toInt(&ok);
|
|
|
|
if (!ok || wordIndex == 0)
|
|
|
|
{
|
2021-09-11 14:35:26 +02:00
|
|
|
auto varName = match.captured(4);
|
|
|
|
auto altText = match.captured(5); // alt text or empty string
|
|
|
|
|
2022-05-29 14:23:29 +02:00
|
|
|
auto var = context.find(varName);
|
2021-09-11 14:35:26 +02:00
|
|
|
|
2022-05-29 14:23:29 +02:00
|
|
|
if (var != context.end())
|
2021-09-11 14:35:26 +02:00
|
|
|
{
|
2022-06-25 14:06:16 +02:00
|
|
|
// Found variable in `context`
|
2022-05-29 14:23:29 +02:00
|
|
|
result += var->second.isEmpty() ? altText : var->second;
|
2022-06-25 14:06:16 +02:00
|
|
|
continue;
|
2021-09-11 14:35:26 +02:00
|
|
|
}
|
2022-06-25 14:06:16 +02:00
|
|
|
|
|
|
|
auto it = COMMAND_VARS.find(varName);
|
|
|
|
if (it != COMMAND_VARS.end())
|
2021-09-11 14:35:26 +02:00
|
|
|
{
|
2022-06-25 14:06:16 +02:00
|
|
|
// Found variable in `COMMAND_VARS`
|
|
|
|
result += it->second(altText, channel, message);
|
|
|
|
continue;
|
2021-09-11 14:35:26 +02:00
|
|
|
}
|
2022-06-25 14:06:16 +02:00
|
|
|
|
|
|
|
// Fall back to replacing it with the actual matched string
|
|
|
|
result += "{" + match.captured(3) + "}";
|
2019-09-08 22:27:57 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (words.length() <= wordIndex)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (plus)
|
|
|
|
{
|
|
|
|
bool first = true;
|
|
|
|
for (int i = wordIndex; i < words.length(); i++)
|
|
|
|
{
|
|
|
|
if (!first)
|
|
|
|
{
|
|
|
|
result += " ";
|
|
|
|
}
|
|
|
|
result += words[i];
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result += words[wordIndex];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result += command.func.mid(lastCaptureEnd);
|
|
|
|
|
|
|
|
if (result.size() > 0 && result.at(0) == '{')
|
|
|
|
{
|
|
|
|
result = result.mid(1);
|
|
|
|
}
|
|
|
|
|
2022-10-08 13:11:55 +02:00
|
|
|
return result.replace("{{", "{");
|
2019-09-08 22:27:57 +02:00
|
|
|
}
|
|
|
|
|
2021-12-26 14:21:52 +01:00
|
|
|
QStringList CommandController::getDefaultChatterinoCommandList()
|
2019-09-08 22:27:57 +02:00
|
|
|
{
|
2021-12-26 14:21:52 +01:00
|
|
|
return this->defaultChatterinoCommandAutoCompletions_;
|
2019-09-08 22:27:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace chatterino
|