433 lines
12 KiB
C++
433 lines
12 KiB
C++
#include "controllers/commands/builtin/twitch/ChatSettings.hpp"
|
|
|
|
#include "Application.hpp"
|
|
#include "controllers/accounts/AccountController.hpp"
|
|
#include "controllers/commands/CommandContext.hpp"
|
|
#include "providers/twitch/api/Helix.hpp"
|
|
#include "providers/twitch/TwitchAccount.hpp"
|
|
#include "providers/twitch/TwitchChannel.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->addSystemMessage(
|
|
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 = getIApp()->getAccounts()->twitch.getCurrent();
|
|
if (currentUser->isAnon())
|
|
{
|
|
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
|
return "";
|
|
}
|
|
|
|
if (ctx.twitchChannel == nullptr)
|
|
{
|
|
ctx.channel->addSystemMessage(
|
|
"The /emoteonly command only works in Twitch channels.");
|
|
return "";
|
|
}
|
|
|
|
if (ctx.twitchChannel->accessRoomModes()->emoteOnly)
|
|
{
|
|
ctx.channel->addSystemMessage(
|
|
"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 = getIApp()->getAccounts()->twitch.getCurrent();
|
|
if (currentUser->isAnon())
|
|
{
|
|
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
|
return "";
|
|
}
|
|
if (ctx.twitchChannel == nullptr)
|
|
{
|
|
ctx.channel->addSystemMessage(
|
|
"The /emoteonlyoff command only works in Twitch channels.");
|
|
return "";
|
|
}
|
|
|
|
if (!ctx.twitchChannel->accessRoomModes()->emoteOnly)
|
|
{
|
|
ctx.channel->addSystemMessage("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 = getIApp()->getAccounts()->twitch.getCurrent();
|
|
if (currentUser->isAnon())
|
|
{
|
|
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
|
return "";
|
|
}
|
|
|
|
if (ctx.twitchChannel == nullptr)
|
|
{
|
|
ctx.channel->addSystemMessage(
|
|
"The /subscribers command only works in Twitch channels.");
|
|
return "";
|
|
}
|
|
|
|
if (ctx.twitchChannel->accessRoomModes()->submode)
|
|
{
|
|
ctx.channel->addSystemMessage(
|
|
"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 = getIApp()->getAccounts()->twitch.getCurrent();
|
|
if (currentUser->isAnon())
|
|
{
|
|
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
|
return "";
|
|
}
|
|
|
|
if (ctx.twitchChannel == nullptr)
|
|
{
|
|
ctx.channel->addSystemMessage(
|
|
"The /subscribersoff command only works in Twitch channels.");
|
|
return "";
|
|
}
|
|
|
|
if (!ctx.twitchChannel->accessRoomModes()->submode)
|
|
{
|
|
ctx.channel->addSystemMessage(
|
|
"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 = getIApp()->getAccounts()->twitch.getCurrent();
|
|
if (currentUser->isAnon())
|
|
{
|
|
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
|
return "";
|
|
}
|
|
|
|
if (ctx.twitchChannel == nullptr)
|
|
{
|
|
ctx.channel->addSystemMessage(
|
|
"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->addSystemMessage(
|
|
"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->addSystemMessage(
|
|
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 = getIApp()->getAccounts()->twitch.getCurrent();
|
|
if (currentUser->isAnon())
|
|
{
|
|
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
|
return "";
|
|
}
|
|
|
|
if (ctx.twitchChannel == nullptr)
|
|
{
|
|
ctx.channel->addSystemMessage(
|
|
"The /slowoff command only works in Twitch channels.");
|
|
return "";
|
|
}
|
|
|
|
if (ctx.twitchChannel->accessRoomModes()->slowMode <= 0)
|
|
{
|
|
ctx.channel->addSystemMessage("This room is not in slow mode.");
|
|
return "";
|
|
}
|
|
|
|
getHelix()->updateSlowMode(ctx.twitchChannel->roomId(),
|
|
currentUser->getUserId(), std::nullopt,
|
|
successCallback, failureCallback(ctx.channel));
|
|
|
|
return "";
|
|
}
|
|
|
|
QString followers(const CommandContext &ctx)
|
|
{
|
|
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
|
if (currentUser->isAnon())
|
|
{
|
|
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
|
return "";
|
|
}
|
|
|
|
if (ctx.twitchChannel == nullptr)
|
|
{
|
|
ctx.channel->addSystemMessage(
|
|
"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->addSystemMessage(
|
|
"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->addSystemMessage(
|
|
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 = getIApp()->getAccounts()->twitch.getCurrent();
|
|
if (currentUser->isAnon())
|
|
{
|
|
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
|
return "";
|
|
}
|
|
|
|
if (ctx.twitchChannel == nullptr)
|
|
{
|
|
ctx.channel->addSystemMessage(
|
|
"The /followersoff command only works in Twitch channels.");
|
|
return "";
|
|
}
|
|
|
|
if (ctx.twitchChannel->accessRoomModes()->followerOnly < 0)
|
|
{
|
|
ctx.channel->addSystemMessage(
|
|
"This room is not in followers-only mode. ");
|
|
return "";
|
|
}
|
|
|
|
getHelix()->updateFollowerMode(
|
|
ctx.twitchChannel->roomId(), currentUser->getUserId(), std::nullopt,
|
|
successCallback, failureCallback(ctx.channel));
|
|
|
|
return "";
|
|
}
|
|
|
|
QString uniqueChat(const CommandContext &ctx)
|
|
{
|
|
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
|
if (currentUser->isAnon())
|
|
{
|
|
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
|
return "";
|
|
}
|
|
|
|
if (ctx.twitchChannel == nullptr)
|
|
{
|
|
ctx.channel->addSystemMessage(
|
|
"The /uniquechat command only works in Twitch channels.");
|
|
return "";
|
|
}
|
|
|
|
if (ctx.twitchChannel->accessRoomModes()->r9k)
|
|
{
|
|
ctx.channel->addSystemMessage(
|
|
"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 = getIApp()->getAccounts()->twitch.getCurrent();
|
|
if (currentUser->isAnon())
|
|
{
|
|
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
|
return "";
|
|
}
|
|
|
|
if (ctx.twitchChannel == nullptr)
|
|
{
|
|
ctx.channel->addSystemMessage(
|
|
"The /uniquechatoff command only works in Twitch channels.");
|
|
return "";
|
|
}
|
|
|
|
if (!ctx.twitchChannel->accessRoomModes()->r9k)
|
|
{
|
|
ctx.channel->addSystemMessage("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
|