Move ChatSettings commands to their own file (#4116)

* Move ChatSettings commands to their own file

* reformat error message strings

* move ChatCommands together in CommandController.cpp

* Allow CommandContext to be provided for builtin functions

using variants MEGADANK

* add missing include

* rename to ComandFunctionVariants

also include some move magic & const reffing
This commit is contained in:
pajlada 2022-11-06 13:07:54 +01:00 committed by GitHub
parent ac7baf4073
commit c6a162c7ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 526 additions and 469 deletions

View file

@ -59,10 +59,13 @@ set(SOURCE_FILES
controllers/accounts/AccountModel.cpp controllers/accounts/AccountModel.cpp
controllers/accounts/AccountModel.hpp controllers/accounts/AccountModel.hpp
controllers/commands/Command.cpp controllers/commands/builtin/twitch/ChatSettings.cpp
controllers/commands/Command.hpp controllers/commands/builtin/twitch/ChatSettings.hpp
controllers/commands/CommandContext.hpp
controllers/commands/CommandController.cpp controllers/commands/CommandController.cpp
controllers/commands/CommandController.hpp controllers/commands/CommandController.hpp
controllers/commands/Command.cpp
controllers/commands/Command.hpp
controllers/commands/CommandModel.cpp controllers/commands/CommandModel.cpp
controllers/commands/CommandModel.hpp controllers/commands/CommandModel.hpp

View file

@ -0,0 +1,20 @@
#pragma once
#include "common/Channel.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include <QStringList>
namespace chatterino {
struct CommandContext {
QStringList words;
// Can be null
ChannelPtr channel;
// Can be null if `channel` is null or if `channel` is not a Twitch channel
TwitchChannel *twitchChannel;
};
} // namespace chatterino

View file

@ -7,6 +7,7 @@
#include "controllers/accounts/AccountController.hpp" #include "controllers/accounts/AccountController.hpp"
#include "controllers/commands/Command.hpp" #include "controllers/commands/Command.hpp"
#include "controllers/commands/CommandModel.hpp" #include "controllers/commands/CommandModel.hpp"
#include "controllers/commands/builtin/twitch/ChatSettings.hpp"
#include "messages/Message.hpp" #include "messages/Message.hpp"
#include "messages/MessageBuilder.hpp" #include "messages/MessageBuilder.hpp"
#include "messages/MessageElement.hpp" #include "messages/MessageElement.hpp"
@ -2557,409 +2558,22 @@ void CommandController::initialize(Settings &, Paths &paths)
return ""; return "";
}); // unraid }); // unraid
const auto formatChatSettingsError = [](const HelixUpdateChatSettingsError this->registerCommand("/emoteonly", &commands::emoteOnly);
error, this->registerCommand("/emoteonlyoff", &commands::emoteOnlyOff);
const QString &message,
int durationUnitMultiplier = 1) {
static const QRegularExpression invalidRange("(\\d+) through (\\d+)");
QString errorMessage = QString("Failed to update - "); this->registerCommand("/subscribers", &commands::subscribers);
using Error = HelixUpdateChatSettingsError; this->registerCommand("/subscribersoff", &commands::subscribersOff);
switch (error)
{
case Error::UserMissingScope: {
// TODO(pajlada): Phrase MISSING_REQUIRED_SCOPE
errorMessage += "Missing required scope. "
"Re-login with your "
"account and try again.";
}
break;
case Error::UserNotAuthorized: this->registerCommand("/slow", &commands::slow);
case Error::Forbidden: { this->registerCommand("/slowoff", &commands::slowOff);
// TODO(pajlada): Phrase MISSING_PERMISSION
errorMessage += "You don't have permission to "
"perform that action.";
}
break;
case Error::Ratelimited: { this->registerCommand("/followers", &commands::followers);
errorMessage += "You are being ratelimited by Twitch. Try " this->registerCommand("/followersoff", &commands::followersOff);
"again in a few seconds.";
}
break;
case Error::OutOfRange: { this->registerCommand("/uniquechat", &commands::uniqueChat);
QRegularExpressionMatch matched = invalidRange.match(message); this->registerCommand("/r9kbeta", &commands::uniqueChat);
if (matched.hasMatch()) this->registerCommand("/uniquechatoff", &commands::uniqueChatOff);
{ this->registerCommand("/r9kbetaoff", &commands::uniqueChatOff);
auto from = matched.captured(1).toInt();
auto to = matched.captured(2).toInt();
errorMessage +=
QString("The duration is out of the valid range: "
"%1 through %2.")
.arg(from == 0 ? "0s"
: formatTime(from *
durationUnitMultiplier),
to == 0
? "0s"
: formatTime(to * durationUnitMultiplier));
}
else
{
errorMessage += message;
}
}
break;
case Error::Forwarded: {
errorMessage = message;
}
break;
case Error::Unknown:
default: {
errorMessage += "An unknown error has occurred.";
}
break;
}
return errorMessage;
};
this->registerCommand("/emoteonly", [formatChatSettingsError](
const QStringList & /* words */,
auto channel) {
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
channel->addMessage(makeSystemMessage(
"You must be logged in to update chat settings!"));
return "";
}
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel == nullptr)
{
channel->addMessage(makeSystemMessage(
"The /emoteonly command only works in Twitch channels"));
return "";
}
if (twitchChannel->accessRoomModes()->emoteOnly)
{
channel->addMessage(
makeSystemMessage("This room is already in emote-only mode."));
return "";
}
getHelix()->updateEmoteMode(
twitchChannel->roomId(), currentUser->getUserId(), true,
[](auto) {
//we'll get a message from irc
},
[channel, formatChatSettingsError](auto error, auto message) {
channel->addMessage(
makeSystemMessage(formatChatSettingsError(error, message)));
});
return "";
});
this->registerCommand(
"/emoteonlyoff", [formatChatSettingsError](
const QStringList & /* words */, auto channel) {
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
channel->addMessage(makeSystemMessage(
"You must be logged in to update chat settings!"));
return "";
}
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel == nullptr)
{
channel->addMessage(makeSystemMessage(
"The /emoteonlyoff command only works in Twitch channels"));
return "";
}
if (!twitchChannel->accessRoomModes()->emoteOnly)
{
channel->addMessage(
makeSystemMessage("This room is not in emote-only mode."));
return "";
}
getHelix()->updateEmoteMode(
twitchChannel->roomId(), currentUser->getUserId(), false,
[](auto) {
// we'll get a message from irc
},
[channel, formatChatSettingsError](auto error, auto message) {
channel->addMessage(makeSystemMessage(
formatChatSettingsError(error, message)));
});
return "";
});
this->registerCommand(
"/subscribers", [formatChatSettingsError](
const QStringList & /* words */, auto channel) {
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
channel->addMessage(makeSystemMessage(
"You must be logged in to update chat settings!"));
return "";
}
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel == nullptr)
{
channel->addMessage(makeSystemMessage(
"The /subscribers command only works in Twitch channels"));
return "";
}
if (twitchChannel->accessRoomModes()->submode)
{
channel->addMessage(makeSystemMessage(
"This room is already in subscribers-only mode."));
return "";
}
getHelix()->updateSubscriberMode(
twitchChannel->roomId(), currentUser->getUserId(), true,
[](auto) {
//we'll get a message from irc
},
[channel, formatChatSettingsError](auto error, auto message) {
channel->addMessage(makeSystemMessage(
formatChatSettingsError(error, message)));
});
return "";
});
this->registerCommand("/subscribersoff", [formatChatSettingsError](
const QStringList
& /* words */,
auto channel) {
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
channel->addMessage(makeSystemMessage(
"You must be logged in to update chat settings!"));
return "";
}
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel == nullptr)
{
channel->addMessage(makeSystemMessage(
"The /subscribersoff command only works in Twitch channels"));
return "";
}
if (!twitchChannel->accessRoomModes()->submode)
{
channel->addMessage(makeSystemMessage(
"This room is not in subscribers-only mode."));
return "";
}
getHelix()->updateSubscriberMode(
twitchChannel->roomId(), currentUser->getUserId(), false,
[](auto) {
// we'll get a message from irc
},
[channel, formatChatSettingsError](auto error, auto message) {
channel->addMessage(
makeSystemMessage(formatChatSettingsError(error, message)));
});
return "";
});
this->registerCommand("/slow", [formatChatSettingsError](
const QStringList &words, auto channel) {
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
channel->addMessage(makeSystemMessage(
"You must be logged in to update chat settings!"));
return "";
}
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel == nullptr)
{
channel->addMessage(makeSystemMessage(
"The /slow command only works in Twitch channels"));
return "";
}
int duration = 30;
if (words.length() >= 2)
{
bool ok = false;
duration = words.at(1).toInt(&ok);
if (!ok || duration <= 0)
{
channel->addMessage(makeSystemMessage(
"Usage: \"/slow [duration]\" - Enables slow mode (limit "
"how often users may send messages). Duration (optional, "
"default=30) must be a positive number of seconds. Use "
"\"slowoff\" to disable. "));
return "";
}
}
if (twitchChannel->accessRoomModes()->slowMode == duration)
{
channel->addMessage(makeSystemMessage(
QString("This room is already in %1-second slow mode.")
.arg(duration)));
return "";
}
getHelix()->updateSlowMode(
twitchChannel->roomId(), currentUser->getUserId(), duration,
[](auto) {
//we'll get a message from irc
},
[channel, formatChatSettingsError](auto error, auto message) {
channel->addMessage(
makeSystemMessage(formatChatSettingsError(error, message)));
});
return "";
});
this->registerCommand(
"/slowoff", [formatChatSettingsError](const QStringList & /* words */,
auto channel) {
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
channel->addMessage(makeSystemMessage(
"You must be logged in to update chat settings!"));
return "";
}
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel == nullptr)
{
channel->addMessage(makeSystemMessage(
"The /slowoff command only works in Twitch channels"));
return "";
}
if (twitchChannel->accessRoomModes()->slowMode <= 0)
{
channel->addMessage(
makeSystemMessage("This room is not in slow mode."));
return "";
}
getHelix()->updateSlowMode(
twitchChannel->roomId(), currentUser->getUserId(), boost::none,
[](auto) {
// we'll get a message from irc
},
[channel, formatChatSettingsError](auto error, auto message) {
channel->addMessage(makeSystemMessage(
formatChatSettingsError(error, message)));
});
return "";
});
this->registerCommand("/followers", [formatChatSettingsError](
const QStringList &words,
auto channel) {
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
channel->addMessage(makeSystemMessage(
"You must be logged in to update chat settings!"));
return "";
}
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel == nullptr)
{
channel->addMessage(makeSystemMessage(
"The /followers command only works in Twitch channels"));
return "";
}
int duration = 0;
if (words.length() >= 2)
{
auto parsed = parseDurationToSeconds(words.mid(1).join(' '), 60);
duration = (int)(parsed / 60);
// -1 / 60 == 0 => use parsed
if (parsed < 0)
{
channel->addMessage(makeSystemMessage(
"Usage: \"/followers [duration]\" - Enables followers-only"
" mode (only users who have followed for 'duration' may "
"chat). Examples: \"30m\", \"1 week\", \"5 days 12 "
"hours\". Must be less than 3 months. "));
return "";
}
}
if (twitchChannel->accessRoomModes()->followerOnly == duration)
{
channel->addMessage(makeSystemMessage(
QString("This room is already in %1 followers-only mode.")
.arg(formatTime(duration * 60))));
return "";
}
getHelix()->updateFollowerMode(
twitchChannel->roomId(), currentUser->getUserId(), duration,
[](auto) {
//we'll get a message from irc
},
[channel, formatChatSettingsError](auto error, auto message) {
channel->addMessage(makeSystemMessage(
formatChatSettingsError(error, message, 60)));
});
return "";
});
this->registerCommand("/followersoff", [formatChatSettingsError](
const QStringList & /* words */,
auto channel) {
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
channel->addMessage(makeSystemMessage(
"You must be logged in to update chat settings!"));
return "";
}
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel == nullptr)
{
channel->addMessage(makeSystemMessage(
"The /followersoff command only works in Twitch channels"));
return "";
}
if (twitchChannel->accessRoomModes()->followerOnly < 0)
{
channel->addMessage(
makeSystemMessage("This room is not in followers-only mode. "));
return "";
}
getHelix()->updateFollowerMode(
twitchChannel->roomId(), currentUser->getUserId(), boost::none,
[](auto) {
// we'll get a message from irc
},
[channel, formatChatSettingsError](auto error, auto message) {
channel->addMessage(
makeSystemMessage(formatChatSettingsError(error, message)));
});
return "";
});
auto formatBanTimeoutError = auto formatBanTimeoutError =
[](const char *operation, HelixBanUserError error, [](const char *operation, HelixBanUserError error,
@ -3337,67 +2951,6 @@ void CommandController::initialize(Settings &, Paths &paths)
return ""; return "";
}); });
auto uniqueChatLambda = [formatChatSettingsError](auto words, auto channel,
bool target) {
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
channel->addMessage(makeSystemMessage(
"You must be logged in to update chat settings!"));
return "";
}
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel == nullptr)
{
channel->addMessage(makeSystemMessage(
QString("The /%1 command only works in Twitch channels")
.arg(target ? "uniquechat" : "uniquechatoff")));
return "";
}
if (twitchChannel->accessRoomModes()->r9k == target)
{
channel->addMessage(makeSystemMessage(
target ? "This room is already in unique-chat mode."
: "This room is not in unique-chat mode."));
return "";
}
getHelix()->updateUniqueChatMode(
twitchChannel->roomId(), currentUser->getUserId(), target,
[](auto) {
// we'll get a message from irc
},
[channel, formatChatSettingsError](auto error, auto message) {
channel->addMessage(
makeSystemMessage(formatChatSettingsError(error, message)));
});
return "";
};
this->registerCommand(
"/uniquechatoff",
[uniqueChatLambda](const QStringList &words, auto channel) {
return uniqueChatLambda(words, channel, false);
});
this->registerCommand(
"/r9kbetaoff",
[uniqueChatLambda](const QStringList &words, auto channel) {
return uniqueChatLambda(words, channel, false);
});
this->registerCommand(
"/uniquechat",
[uniqueChatLambda](const QStringList &words, auto channel) {
return uniqueChatLambda(words, channel, true);
});
this->registerCommand(
"/r9kbeta", [uniqueChatLambda](const QStringList &words, auto channel) {
return uniqueChatLambda(words, channel, true);
});
this->registerCommand( this->registerCommand(
"/commercial", "/commercial",
[formatStartCommercialError](const QStringList &words, [formatStartCommercialError](const QStringList &words,
@ -3544,7 +3097,22 @@ QString CommandController::execCommand(const QString &textNoEmoji,
const auto it = this->commands_.find(commandName); const auto it = this->commands_.find(commandName);
if (it != this->commands_.end()) if (it != this->commands_.end())
{ {
return it.value()(words, channel); 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 "";
} }
} }
@ -3570,12 +3138,12 @@ QString CommandController::execCommand(const QString &textNoEmoji,
return text; return text;
} }
void CommandController::registerCommand(QString commandName, void CommandController::registerCommand(const QString &commandName,
CommandFunction commandFunction) CommandFunctionVariants commandFunction)
{ {
assert(!this->commands_.contains(commandName)); assert(this->commands_.count(commandName) == 0);
this->commands_[commandName] = commandFunction; this->commands_[commandName] = std::move(commandFunction);
this->defaultChatterinoCommandAutoCompletions_.append(commandName); this->defaultChatterinoCommandAutoCompletions_.append(commandName);
} }

View file

@ -4,6 +4,7 @@
#include "common/SignalVector.hpp" #include "common/SignalVector.hpp"
#include "common/Singleton.hpp" #include "common/Singleton.hpp"
#include "controllers/commands/Command.hpp" #include "controllers/commands/Command.hpp"
#include "controllers/commands/CommandContext.hpp"
#include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchChannel.hpp"
#include <QMap> #include <QMap>
@ -12,6 +13,7 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <unordered_map> #include <unordered_map>
#include <variant>
namespace chatterino { namespace chatterino {
@ -46,10 +48,16 @@ private:
using CommandFunction = using CommandFunction =
std::function<QString(QStringList /*words*/, ChannelPtr /*channel*/)>; std::function<QString(QStringList /*words*/, ChannelPtr /*channel*/)>;
void registerCommand(QString commandName, CommandFunction commandFunction); using CommandFunctionWithContext = std::function<QString(CommandContext)>;
using CommandFunctionVariants =
std::variant<CommandFunction, CommandFunctionWithContext>;
void registerCommand(const QString &commandName,
CommandFunctionVariants commandFunction);
// Chatterino commands // Chatterino commands
QMap<QString, CommandFunction> commands_; std::unordered_map<QString, CommandFunctionVariants> commands_;
// User-created commands // User-created commands
QMap<QString, Command> userCommands_; QMap<QString, Command> userCommands_;

View file

@ -0,0 +1,434 @@
#include "controllers/commands/builtin/twitch/ChatSettings.hpp"
#include "Application.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "messages/MessageBuilder.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/api/Helix.hpp"
#include "util/FormatTime.hpp"
#include "util/Helpers.hpp"
namespace {
using namespace chatterino;
QString formatError(const HelixUpdateChatSettingsError error,
const QString &message, int durationUnitMultiplier = 1)
{
static const QRegularExpression invalidRange("(\\d+) through (\\d+)");
QString errorMessage = QString("Failed to update - ");
using Error = HelixUpdateChatSettingsError;
switch (error)
{
case Error::UserMissingScope: {
// TODO(pajlada): Phrase MISSING_REQUIRED_SCOPE
errorMessage += "Missing required scope. "
"Re-login with your "
"account and try again.";
}
break;
case Error::UserNotAuthorized:
case Error::Forbidden: {
// TODO(pajlada): Phrase MISSING_PERMISSION
errorMessage += "You don't have permission to "
"perform that action.";
}
break;
case Error::Ratelimited: {
errorMessage += "You are being ratelimited by Twitch. Try "
"again in a few seconds.";
}
break;
case Error::OutOfRange: {
QRegularExpressionMatch matched = invalidRange.match(message);
if (matched.hasMatch())
{
auto from = matched.captured(1).toInt();
auto to = matched.captured(2).toInt();
errorMessage +=
QString("The duration is out of the valid range: "
"%1 through %2.")
.arg(from == 0
? "0s"
: formatTime(from * durationUnitMultiplier),
to == 0 ? "0s"
: formatTime(to * durationUnitMultiplier));
}
else
{
errorMessage += message;
}
}
break;
case Error::Forwarded: {
errorMessage = message;
}
break;
case Error::Unknown:
default: {
errorMessage += "An unknown error has occurred.";
}
break;
}
return errorMessage;
}
// Do nothing as we'll receive a message from IRC about updates
auto successCallback = [](auto result) {};
auto failureCallback = [](ChannelPtr channel, int durationUnitMultiplier = 1) {
return [channel, durationUnitMultiplier](const auto &error,
const QString &message) {
channel->addMessage(makeSystemMessage(
formatError(error, message, durationUnitMultiplier)));
};
};
const auto P_NOT_LOGGED_IN =
QStringLiteral("You must be logged in to update chat settings!");
} // namespace
namespace chatterino::commands {
QString emoteOnly(const CommandContext &ctx)
{
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
return "";
}
if (ctx.twitchChannel == nullptr)
{
ctx.channel->addMessage(makeSystemMessage(
"The /emoteonly command only works in Twitch channels"));
return "";
}
if (ctx.twitchChannel->accessRoomModes()->emoteOnly)
{
ctx.channel->addMessage(
makeSystemMessage("This room is already in emote-only mode."));
return "";
}
getHelix()->updateEmoteMode(ctx.twitchChannel->roomId(),
currentUser->getUserId(), true, successCallback,
failureCallback(ctx.channel));
return "";
}
QString emoteOnlyOff(const CommandContext &ctx)
{
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
return "";
}
if (ctx.twitchChannel == nullptr)
{
ctx.channel->addMessage(makeSystemMessage(
"The /emoteonlyoff command only works in Twitch channels"));
return "";
}
if (!ctx.twitchChannel->accessRoomModes()->emoteOnly)
{
ctx.channel->addMessage(
makeSystemMessage("This room is not in emote-only mode."));
return "";
}
getHelix()->updateEmoteMode(ctx.twitchChannel->roomId(),
currentUser->getUserId(), false,
successCallback, failureCallback(ctx.channel));
return "";
}
QString subscribers(const CommandContext &ctx)
{
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
return "";
}
if (ctx.twitchChannel == nullptr)
{
ctx.channel->addMessage(makeSystemMessage(
"The /subscribers command only works in Twitch channels"));
return "";
}
if (ctx.twitchChannel->accessRoomModes()->submode)
{
ctx.channel->addMessage(makeSystemMessage(
"This room is already in subscribers-only mode."));
return "";
}
getHelix()->updateSubscriberMode(
ctx.twitchChannel->roomId(), currentUser->getUserId(), true,
successCallback, failureCallback(ctx.channel));
return "";
}
QString subscribersOff(const CommandContext &ctx)
{
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
return "";
}
if (ctx.twitchChannel == nullptr)
{
ctx.channel->addMessage(makeSystemMessage(
"The /subscribersoff command only works in Twitch channels"));
return "";
}
if (!ctx.twitchChannel->accessRoomModes()->submode)
{
ctx.channel->addMessage(
makeSystemMessage("This room is not in subscribers-only mode."));
return "";
}
getHelix()->updateSubscriberMode(
ctx.twitchChannel->roomId(), currentUser->getUserId(), false,
successCallback, failureCallback(ctx.channel));
return "";
}
QString slow(const CommandContext &ctx)
{
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
return "";
}
if (ctx.twitchChannel == nullptr)
{
ctx.channel->addMessage(makeSystemMessage(
"The /slow command only works in Twitch channels"));
return "";
}
int duration = 30;
if (ctx.words.length() >= 2)
{
bool ok = false;
duration = ctx.words.at(1).toInt(&ok);
if (!ok || duration <= 0)
{
ctx.channel->addMessage(makeSystemMessage(
"Usage: \"/slow [duration]\" - Enables slow mode (limit how "
"often users may send messages). Duration (optional, "
"default=30) must be a positive number of seconds. Use "
"\"slowoff\" to disable."));
return "";
}
}
if (ctx.twitchChannel->accessRoomModes()->slowMode == duration)
{
ctx.channel->addMessage(makeSystemMessage(
QString("This room is already in %1-second slow mode.")
.arg(duration)));
return "";
}
getHelix()->updateSlowMode(ctx.twitchChannel->roomId(),
currentUser->getUserId(), duration,
successCallback, failureCallback(ctx.channel));
return "";
}
QString slowOff(const CommandContext &ctx)
{
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
return "";
}
if (ctx.twitchChannel == nullptr)
{
ctx.channel->addMessage(makeSystemMessage(
"The /slowoff command only works in Twitch channels"));
return "";
}
if (ctx.twitchChannel->accessRoomModes()->slowMode <= 0)
{
ctx.channel->addMessage(
makeSystemMessage("This room is not in slow mode."));
return "";
}
getHelix()->updateSlowMode(ctx.twitchChannel->roomId(),
currentUser->getUserId(), boost::none,
successCallback, failureCallback(ctx.channel));
return "";
}
QString followers(const CommandContext &ctx)
{
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
return "";
}
if (ctx.twitchChannel == nullptr)
{
ctx.channel->addMessage(makeSystemMessage(
"The /followers command only works in Twitch channels"));
return "";
}
int duration = 0;
if (ctx.words.length() >= 2)
{
auto parsed = parseDurationToSeconds(ctx.words.mid(1).join(' '), 60);
duration = (int)(parsed / 60);
// -1 / 60 == 0 => use parsed
if (parsed < 0)
{
ctx.channel->addMessage(makeSystemMessage(
"Usage: \"/followers [duration]\" - Enables followers-only "
"mode (only users who have followed for 'duration' may chat). "
"Examples: \"30m\", \"1 week\", \"5 days 12 hours\". Must be "
"less than 3 months."));
return "";
}
}
if (ctx.twitchChannel->accessRoomModes()->followerOnly == duration)
{
ctx.channel->addMessage(makeSystemMessage(
QString("This room is already in %1 followers-only mode.")
.arg(formatTime(duration * 60))));
return "";
}
getHelix()->updateFollowerMode(
ctx.twitchChannel->roomId(), currentUser->getUserId(), duration,
successCallback, failureCallback(ctx.channel, 60));
return "";
}
QString followersOff(const CommandContext &ctx)
{
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
return "";
}
if (ctx.twitchChannel == nullptr)
{
ctx.channel->addMessage(makeSystemMessage(
"The /followersoff command only works in Twitch channels"));
return "";
}
if (ctx.twitchChannel->accessRoomModes()->followerOnly < 0)
{
ctx.channel->addMessage(
makeSystemMessage("This room is not in followers-only mode. "));
return "";
}
getHelix()->updateFollowerMode(
ctx.twitchChannel->roomId(), currentUser->getUserId(), boost::none,
successCallback, failureCallback(ctx.channel));
return "";
}
QString uniqueChat(const CommandContext &ctx)
{
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
return "";
}
if (ctx.twitchChannel == nullptr)
{
ctx.channel->addMessage(makeSystemMessage(
"The /uniquechat command only works in Twitch channels"));
return "";
}
if (ctx.twitchChannel->accessRoomModes()->r9k)
{
ctx.channel->addMessage(
makeSystemMessage("This room is already in unique-chat mode."));
return "";
}
getHelix()->updateUniqueChatMode(
ctx.twitchChannel->roomId(), currentUser->getUserId(), true,
successCallback, failureCallback(ctx.channel));
return "";
}
QString uniqueChatOff(const CommandContext &ctx)
{
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
return "";
}
if (ctx.twitchChannel == nullptr)
{
ctx.channel->addMessage(makeSystemMessage(
"The /uniquechatoff command only works in Twitch channels"));
return "";
}
if (!ctx.twitchChannel->accessRoomModes()->r9k)
{
ctx.channel->addMessage(
makeSystemMessage("This room is not in unique-chat mode."));
return "";
}
getHelix()->updateUniqueChatMode(
ctx.twitchChannel->roomId(), currentUser->getUserId(), false,
successCallback, failureCallback(ctx.channel));
return "";
}
} // namespace chatterino::commands

View file

@ -0,0 +1,24 @@
#pragma once
#include "controllers/commands/CommandContext.hpp"
#include <QString>
namespace chatterino::commands {
QString emoteOnly(const CommandContext &ctx);
QString emoteOnlyOff(const CommandContext &ctx);
QString subscribers(const CommandContext &ctx);
QString subscribersOff(const CommandContext &ctx);
QString slow(const CommandContext &ctx);
QString slowOff(const CommandContext &ctx);
QString followers(const CommandContext &ctx);
QString followersOff(const CommandContext &ctx);
QString uniqueChat(const CommandContext &ctx);
QString uniqueChatOff(const CommandContext &ctx);
} // namespace chatterino::commands