Skip to content

Commit

Permalink
feat: improve create clip error messaging (Chatterino#5879)
Browse files Browse the repository at this point in the history
  • Loading branch information
pajlada authored Feb 1, 2025
1 parent 810d3cd commit f629eec
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 24 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Minor: Remove incognito browser support for `iexplore`, because internet explorer is EOL. (#5810)
- Minor: When (re-)connecting, visible channels are now joined first. (#5850)
- Minor: Added the ability to filter on messages by the author's user ID (example: `author.user_id == "22484632"`). (#5862)
- Minor: Improved error messaging of the `/clip` command. (#5879)
- Bugfix: Fixed a potential way to escape the Lua Plugin sandbox. (#5846)
- Bugfix: Fixed a crash relating to Lua HTTP. (#5800)
- Bugfix: Fixed a crash that could occur on Linux and macOS when clicking "Install" from the update prompt. (#5818)
Expand Down
2 changes: 1 addition & 1 deletion mocks/include/mocks/Helix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class Helix : public IHelix

MOCK_METHOD(void, createClip,
(QString channelId, ResultCallback<HelixClip> successCallback,
std::function<void(HelixClipError)> failureCallback,
std::function<void(HelixClipError, QString)> failureCallback,
std::function<void()> finallyCallback),
(override));

Expand Down
45 changes: 39 additions & 6 deletions src/providers/twitch/TwitchChannel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,21 @@ namespace {
#endif
constexpr int CLIP_CREATION_COOLDOWN = 5000;
const QString CLIPS_LINK("https://clips.twitch.tv/%1");
const QString CLIPS_FAILURE_CLIPS_UNAVAILABLE_TEXT(
"Failed to create a clip - clips are temporarily unavailable: %1");
const QString CLIPS_FAILURE_CLIPS_DISABLED_TEXT(
"Failed to create a clip - the streamer has clips disabled entirely or "
"requires a certain subscriber or follower status to create clips.");
"Failed to create a clip - the streamer has clips disabled in their "
"channel.");
const QString CLIPS_FAILURE_CLIPS_RESTRICTED_TEXT(
"Failed to create a clip - the streamer has restricted clip creation "
"to subscribers, or followers of an unknown duration.");
const QString CLIPS_FAILURE_CLIPS_RESTRICTED_CATEGORY_TEXT(
"Failed to create a clip - the streamer has disabled clips while in "
"this category.");
const QString CLIPS_FAILURE_NOT_AUTHENTICATED_TEXT(
"Failed to create a clip - you need to re-authenticate.");
const QString CLIPS_FAILURE_UNKNOWN_ERROR_TEXT(
"Failed to create a clip - an unknown error occurred.");
"Failed to create a clip: %1");
const QString LOGIN_PROMPT_TEXT("Click here to add your account again.");
const Link ACCOUNTS_LINK(Link::OpenAccountsPage, QString());

Expand Down Expand Up @@ -1771,7 +1779,7 @@ void TwitchChannel::createClip()
this->addMessage(builder.release(), MessageContext::Original);
},
// failureCallback
[this](auto error) {
[this](auto error, auto errorMessage) {
MessageBuilder builder;
QString text;
builder.message().flags.set(MessageFlag::System);
Expand All @@ -1780,6 +1788,15 @@ void TwitchChannel::createClip()

switch (error)
{
case HelixClipError::ClipsUnavailable: {
builder.emplace<TextElement>(
CLIPS_FAILURE_CLIPS_UNAVAILABLE_TEXT.arg(errorMessage),
MessageElementFlag::Text, MessageColor::System);
text =
CLIPS_FAILURE_CLIPS_UNAVAILABLE_TEXT.arg(errorMessage);
}
break;

case HelixClipError::ClipsDisabled: {
builder.emplace<TextElement>(
CLIPS_FAILURE_CLIPS_DISABLED_TEXT,
Expand All @@ -1788,6 +1805,22 @@ void TwitchChannel::createClip()
}
break;

case HelixClipError::ClipsRestricted: {
builder.emplace<TextElement>(
CLIPS_FAILURE_CLIPS_RESTRICTED_TEXT,
MessageElementFlag::Text, MessageColor::System);
text = CLIPS_FAILURE_CLIPS_RESTRICTED_TEXT;
}
break;

case HelixClipError::ClipsRestrictedCategory: {
builder.emplace<TextElement>(
CLIPS_FAILURE_CLIPS_RESTRICTED_CATEGORY_TEXT,
MessageElementFlag::Text, MessageColor::System);
text = CLIPS_FAILURE_CLIPS_RESTRICTED_CATEGORY_TEXT;
}
break;

case HelixClipError::UserNotAuthenticated: {
builder.emplace<TextElement>(
CLIPS_FAILURE_NOT_AUTHENTICATED_TEXT,
Expand All @@ -1807,9 +1840,9 @@ void TwitchChannel::createClip()
case HelixClipError::Unknown:
default: {
builder.emplace<TextElement>(
CLIPS_FAILURE_UNKNOWN_ERROR_TEXT,
CLIPS_FAILURE_UNKNOWN_ERROR_TEXT.arg(errorMessage),
MessageElementFlag::Text, MessageColor::System);
text = CLIPS_FAILURE_UNKNOWN_ERROR_TEXT;
text = CLIPS_FAILURE_UNKNOWN_ERROR_TEXT.arg(errorMessage);
}
break;
}
Expand Down
46 changes: 37 additions & 9 deletions src/providers/twitch/api/Helix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,10 +351,10 @@ void Helix::getGameById(QString gameId,
failureCallback);
}

void Helix::createClip(QString channelId,
ResultCallback<HelixClip> successCallback,
std::function<void(HelixClipError)> failureCallback,
std::function<void()> finallyCallback)
void Helix::createClip(
QString channelId, ResultCallback<HelixClip> successCallback,
std::function<void(HelixClipError, QString)> failureCallback,
std::function<void()> finallyCallback)
{
QUrlQuery urlQuery;
urlQuery.addQueryItem("broadcaster_id", channelId);
Expand All @@ -367,7 +367,7 @@ void Helix::createClip(QString channelId,

if (!data.isArray())
{
failureCallback(HelixClipError::Unknown);
failureCallback(HelixClipError::Unknown, "No clip was created");
return;
}

Expand All @@ -376,25 +376,53 @@ void Helix::createClip(QString channelId,
successCallback(clip);
})
.onError([failureCallback](auto result) {
auto obj = result.parseJson();
auto message = obj.value("message").toString();
switch (result.status().value_or(0))
{
case 503: {
// Channel has disabled clip-creation, or channel has made cliops only creatable by followers and the user is not a follower (or subscriber)
failureCallback(HelixClipError::ClipsDisabled);
// We should not necessarily handle this, so the error messaging will use `message` if it exists
failureCallback(HelixClipError::ClipsUnavailable, message);
}
break;

case 401: {
// User does not have the required scope to be able to create clips, user must reauthenticate
failureCallback(HelixClipError::UserNotAuthenticated);
failureCallback(HelixClipError::UserNotAuthenticated,
message);
}
break;

case 403: {
if (message.contains("restricted for this channel"))
{
failureCallback(HelixClipError::ClipsDisabled, message);
}
else if (message.contains("User does not have permissions"))
{
failureCallback(HelixClipError::ClipsRestricted,
message);
}
else if (message.contains("restricted for this category"))
{
failureCallback(HelixClipError::ClipsRestrictedCategory,
message);
}
else
{
qCDebug(chatterinoTwitch)
<< "Failed to create a clip: "
<< result.formatError() << result.getData();
failureCallback(HelixClipError::Unknown, message);
}
}
break;

default: {
qCDebug(chatterinoTwitch)
<< "Failed to create a clip: " << result.formatError()
<< result.getData();
failureCallback(HelixClipError::Unknown);
failureCallback(HelixClipError::Unknown, message);
}
break;
}
Expand Down
19 changes: 11 additions & 8 deletions src/providers/twitch/api/Helix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,10 @@ enum class HelixAnnouncementColor {

enum class HelixClipError {
Unknown,
ClipsUnavailable,
ClipsDisabled,
ClipsRestricted,
ClipsRestrictedCategory,
UserNotAuthenticated,
};

Expand Down Expand Up @@ -864,10 +867,10 @@ class IHelix
HelixFailureCallback failureCallback) = 0;

// https://dev.twitch.tv/docs/api/reference#create-clip
virtual void createClip(QString channelId,
ResultCallback<HelixClip> successCallback,
std::function<void(HelixClipError)> failureCallback,
std::function<void()> finallyCallback) = 0;
virtual void createClip(
QString channelId, ResultCallback<HelixClip> successCallback,
std::function<void(HelixClipError, QString)> failureCallback,
std::function<void()> finallyCallback) = 0;

// https://dev.twitch.tv/docs/api/reference#get-channel-information
virtual void fetchChannels(
Expand Down Expand Up @@ -1207,10 +1210,10 @@ class Helix final : public IHelix
HelixFailureCallback failureCallback) final;

// https://dev.twitch.tv/docs/api/reference#create-clip
void createClip(QString channelId,
ResultCallback<HelixClip> successCallback,
std::function<void(HelixClipError)> failureCallback,
std::function<void()> finallyCallback) final;
void createClip(
QString channelId, ResultCallback<HelixClip> successCallback,
std::function<void(HelixClipError, QString)> failureCallback,
std::function<void()> finallyCallback) final;

// https://dev.twitch.tv/docs/api/reference#get-channel-information
void fetchChannels(
Expand Down

0 comments on commit f629eec

Please sign in to comment.