chore: migrate /vips command to Helix call (#4053)

* feat(helix): create response model for VIP listing

* feat(helix): stub out channel/vips request + handler

* feat(helix): parse VIPs list from data and pass to callback

* feat(helix): handle errors when getting VIP list then pass to callback

* feat(command): add barebones handler for helix-based /vips

* feat(command): provide better /vips output when user is not broadcaster

* chore(format): bulk reformat with clang-format

* chore(changelog): add entry for /vips Helix migration

* fix(helix): use correct method when calling VIP list endpoint

* fix(helix): use correct VIP list endpoint

* chore(tidy): please clang-tidy by marking parameter as unused

* feat(command): display unsorted VIP list returned from Helix API

* feat(settings): clone raid timegate settings for /vips

* feat(command): check /vips timegate setting before execution

* feat(command): handle 0 VIPs from Helix response

* feat(command): sort users alphabetically from Helix VIPs response

* fix(command): highlight users in Helix /vips output to match IRC

* fix(command): replace dynamic /vips error message with hardcoded string

* chore(comment): remove TODO comment that was DONE

* chore(format): bulk reformat using clang-format

* fix(command): send 0 VIP message after creation

* chore: apply suggestions from Felanbird

* fix(helix): change mention of user ban to VIPs in VIP list error message

* feat(helix): distinguish non-broadcaster auth error when getting VIPs

* chore(command): move handling of non-broadcaster /vips usage to API response

* chore(format): re-indent multiline string to get away from 80 char limit

* reformat

* fix tests

Co-authored-by: pajlada <rasmus.karlsson@pajlada.com>
This commit is contained in:
James Upjohn
2022-10-10 23:56:55 +13:00
committed by GitHub
parent e604a36777
commit ceecc7ef91
7 changed files with 290 additions and 0 deletions
@@ -12,6 +12,7 @@
#include "messages/MessageElement.hpp"
#include "providers/twitch/TwitchCommon.hpp"
#include "providers/twitch/TwitchIrcServer.hpp"
#include "providers/twitch/TwitchMessageBuilder.hpp"
#include "providers/twitch/api/Helix.hpp"
#include "singletons/Emotes.hpp"
#include "singletons/Paths.hpp"
@@ -2936,6 +2937,137 @@ void CommandController::initialize(Settings &, Paths &paths)
return runWhisperCommand(words, channel);
});
}
auto formatVIPListError = [](HelixListVIPsError error,
const QString &message) -> QString {
using Error = HelixListVIPsError;
QString errorMessage = QString("Failed to list VIPs - ");
switch (error)
{
case Error::Forwarded: {
errorMessage += message;
}
break;
case Error::Ratelimited: {
errorMessage += "You are being ratelimited by Twitch. Try "
"again in a few seconds.";
}
break;
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: {
// TODO(pajlada): Phrase MISSING_PERMISSION
errorMessage += "You don't have permission to "
"perform that action.";
}
break;
case Error::UserNotBroadcaster: {
errorMessage +=
"Due to Twitch restrictions, "
"this command can only be used by the broadcaster. "
"To see the list of VIPs you must use the Twitch website.";
}
break;
case Error::Unknown: {
errorMessage += "An unknown error has occurred.";
}
break;
}
return errorMessage;
};
this->registerCommand(
"/vips",
[formatVIPListError](const QStringList &words,
auto channel) -> QString {
switch (getSettings()->helixTimegateVIPs.getValue())
{
case HelixTimegateOverride::Timegate: {
if (areIRCCommandsStillAvailable())
{
return useIRCCommand(words);
}
// fall through to Helix logic
}
break;
case HelixTimegateOverride::AlwaysUseIRC: {
return useIRCCommand(words);
}
break;
case HelixTimegateOverride::AlwaysUseHelix: {
// do nothing and fall through to Helix logic
}
break;
}
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel == nullptr)
{
channel->addMessage(makeSystemMessage(
"The /vips command only works in Twitch channels"));
return "";
}
auto currentUser = getApp()->accounts->twitch.getCurrent();
if (currentUser->isAnon())
{
channel->addMessage(makeSystemMessage(
"Due to Twitch restrictions, " //
"this command can only be used by the broadcaster. "
"To see the list of VIPs you must use the "
"Twitch website."));
return "";
}
getHelix()->getChannelVIPs(
twitchChannel->roomId(),
[channel, twitchChannel](const std::vector<HelixVip> &vipList) {
if (vipList.empty())
{
channel->addMessage(makeSystemMessage(
"This channel does not have any VIPs."));
return;
}
auto messagePrefix =
QString("The VIPs of this channel are");
auto entries = QStringList();
for (const auto &vip : vipList)
{
entries.append(vip.userName);
}
entries.sort(Qt::CaseInsensitive);
MessageBuilder builder;
TwitchMessageBuilder::listOfUsersSystemMessage(
messagePrefix, entries, twitchChannel, &builder);
channel->addMessage(builder.release());
},
[channel, formatVIPListError](auto error, auto message) {
auto errorMessage = formatVIPListError(error, message);
channel->addMessage(makeSystemMessage(errorMessage));
});
return "";
});
}
void CommandController::save()