feat: improve create clip error messaging (#5879)
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
- Minor: Remove incognito browser support for `iexplore`, because internet explorer is EOL. (#5810)
|
- 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: 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: 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 potential way to escape the Lua Plugin sandbox. (#5846)
|
||||||
- Bugfix: Fixed a crash relating to Lua HTTP. (#5800)
|
- 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)
|
- Bugfix: Fixed a crash that could occur on Linux and macOS when clicking "Install" from the update prompt. (#5818)
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public:
|
|||||||
|
|
||||||
MOCK_METHOD(void, createClip,
|
MOCK_METHOD(void, createClip,
|
||||||
(QString channelId, ResultCallback<HelixClip> successCallback,
|
(QString channelId, ResultCallback<HelixClip> successCallback,
|
||||||
std::function<void(HelixClipError)> failureCallback,
|
std::function<void(HelixClipError, QString)> failureCallback,
|
||||||
std::function<void()> finallyCallback),
|
std::function<void()> finallyCallback),
|
||||||
(override));
|
(override));
|
||||||
|
|
||||||
|
|||||||
@@ -67,13 +67,21 @@ namespace {
|
|||||||
#endif
|
#endif
|
||||||
constexpr int CLIP_CREATION_COOLDOWN = 5000;
|
constexpr int CLIP_CREATION_COOLDOWN = 5000;
|
||||||
const QString CLIPS_LINK("https://clips.twitch.tv/%1");
|
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(
|
const QString CLIPS_FAILURE_CLIPS_DISABLED_TEXT(
|
||||||
"Failed to create a clip - the streamer has clips disabled entirely or "
|
"Failed to create a clip - the streamer has clips disabled in their "
|
||||||
"requires a certain subscriber or follower status to create clips.");
|
"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(
|
const QString CLIPS_FAILURE_NOT_AUTHENTICATED_TEXT(
|
||||||
"Failed to create a clip - you need to re-authenticate.");
|
"Failed to create a clip - you need to re-authenticate.");
|
||||||
const QString CLIPS_FAILURE_UNKNOWN_ERROR_TEXT(
|
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 QString LOGIN_PROMPT_TEXT("Click here to add your account again.");
|
||||||
const Link ACCOUNTS_LINK(Link::OpenAccountsPage, QString());
|
const Link ACCOUNTS_LINK(Link::OpenAccountsPage, QString());
|
||||||
|
|
||||||
@@ -1771,7 +1779,7 @@ void TwitchChannel::createClip()
|
|||||||
this->addMessage(builder.release(), MessageContext::Original);
|
this->addMessage(builder.release(), MessageContext::Original);
|
||||||
},
|
},
|
||||||
// failureCallback
|
// failureCallback
|
||||||
[this](auto error) {
|
[this](auto error, auto errorMessage) {
|
||||||
MessageBuilder builder;
|
MessageBuilder builder;
|
||||||
QString text;
|
QString text;
|
||||||
builder.message().flags.set(MessageFlag::System);
|
builder.message().flags.set(MessageFlag::System);
|
||||||
@@ -1780,6 +1788,15 @@ void TwitchChannel::createClip()
|
|||||||
|
|
||||||
switch (error)
|
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: {
|
case HelixClipError::ClipsDisabled: {
|
||||||
builder.emplace<TextElement>(
|
builder.emplace<TextElement>(
|
||||||
CLIPS_FAILURE_CLIPS_DISABLED_TEXT,
|
CLIPS_FAILURE_CLIPS_DISABLED_TEXT,
|
||||||
@@ -1788,6 +1805,22 @@ void TwitchChannel::createClip()
|
|||||||
}
|
}
|
||||||
break;
|
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: {
|
case HelixClipError::UserNotAuthenticated: {
|
||||||
builder.emplace<TextElement>(
|
builder.emplace<TextElement>(
|
||||||
CLIPS_FAILURE_NOT_AUTHENTICATED_TEXT,
|
CLIPS_FAILURE_NOT_AUTHENTICATED_TEXT,
|
||||||
@@ -1807,9 +1840,9 @@ void TwitchChannel::createClip()
|
|||||||
case HelixClipError::Unknown:
|
case HelixClipError::Unknown:
|
||||||
default: {
|
default: {
|
||||||
builder.emplace<TextElement>(
|
builder.emplace<TextElement>(
|
||||||
CLIPS_FAILURE_UNKNOWN_ERROR_TEXT,
|
CLIPS_FAILURE_UNKNOWN_ERROR_TEXT.arg(errorMessage),
|
||||||
MessageElementFlag::Text, MessageColor::System);
|
MessageElementFlag::Text, MessageColor::System);
|
||||||
text = CLIPS_FAILURE_UNKNOWN_ERROR_TEXT;
|
text = CLIPS_FAILURE_UNKNOWN_ERROR_TEXT.arg(errorMessage);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -351,10 +351,10 @@ void Helix::getGameById(QString gameId,
|
|||||||
failureCallback);
|
failureCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Helix::createClip(QString channelId,
|
void Helix::createClip(
|
||||||
ResultCallback<HelixClip> successCallback,
|
QString channelId, ResultCallback<HelixClip> successCallback,
|
||||||
std::function<void(HelixClipError)> failureCallback,
|
std::function<void(HelixClipError, QString)> failureCallback,
|
||||||
std::function<void()> finallyCallback)
|
std::function<void()> finallyCallback)
|
||||||
{
|
{
|
||||||
QUrlQuery urlQuery;
|
QUrlQuery urlQuery;
|
||||||
urlQuery.addQueryItem("broadcaster_id", channelId);
|
urlQuery.addQueryItem("broadcaster_id", channelId);
|
||||||
@@ -367,7 +367,7 @@ void Helix::createClip(QString channelId,
|
|||||||
|
|
||||||
if (!data.isArray())
|
if (!data.isArray())
|
||||||
{
|
{
|
||||||
failureCallback(HelixClipError::Unknown);
|
failureCallback(HelixClipError::Unknown, "No clip was created");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,17 +376,45 @@ void Helix::createClip(QString channelId,
|
|||||||
successCallback(clip);
|
successCallback(clip);
|
||||||
})
|
})
|
||||||
.onError([failureCallback](auto result) {
|
.onError([failureCallback](auto result) {
|
||||||
|
auto obj = result.parseJson();
|
||||||
|
auto message = obj.value("message").toString();
|
||||||
switch (result.status().value_or(0))
|
switch (result.status().value_or(0))
|
||||||
{
|
{
|
||||||
case 503: {
|
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)
|
// We should not necessarily handle this, so the error messaging will use `message` if it exists
|
||||||
failureCallback(HelixClipError::ClipsDisabled);
|
failureCallback(HelixClipError::ClipsUnavailable, message);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 401: {
|
case 401: {
|
||||||
// User does not have the required scope to be able to create clips, user must reauthenticate
|
// 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;
|
break;
|
||||||
|
|
||||||
@@ -394,7 +422,7 @@ void Helix::createClip(QString channelId,
|
|||||||
qCDebug(chatterinoTwitch)
|
qCDebug(chatterinoTwitch)
|
||||||
<< "Failed to create a clip: " << result.formatError()
|
<< "Failed to create a clip: " << result.formatError()
|
||||||
<< result.getData();
|
<< result.getData();
|
||||||
failureCallback(HelixClipError::Unknown);
|
failureCallback(HelixClipError::Unknown, message);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -474,7 +474,10 @@ enum class HelixAnnouncementColor {
|
|||||||
|
|
||||||
enum class HelixClipError {
|
enum class HelixClipError {
|
||||||
Unknown,
|
Unknown,
|
||||||
|
ClipsUnavailable,
|
||||||
ClipsDisabled,
|
ClipsDisabled,
|
||||||
|
ClipsRestricted,
|
||||||
|
ClipsRestrictedCategory,
|
||||||
UserNotAuthenticated,
|
UserNotAuthenticated,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -864,10 +867,10 @@ public:
|
|||||||
HelixFailureCallback failureCallback) = 0;
|
HelixFailureCallback failureCallback) = 0;
|
||||||
|
|
||||||
// https://dev.twitch.tv/docs/api/reference#create-clip
|
// https://dev.twitch.tv/docs/api/reference#create-clip
|
||||||
virtual void createClip(QString channelId,
|
virtual void createClip(
|
||||||
ResultCallback<HelixClip> successCallback,
|
QString channelId, ResultCallback<HelixClip> successCallback,
|
||||||
std::function<void(HelixClipError)> failureCallback,
|
std::function<void(HelixClipError, QString)> failureCallback,
|
||||||
std::function<void()> finallyCallback) = 0;
|
std::function<void()> finallyCallback) = 0;
|
||||||
|
|
||||||
// https://dev.twitch.tv/docs/api/reference#get-channel-information
|
// https://dev.twitch.tv/docs/api/reference#get-channel-information
|
||||||
virtual void fetchChannels(
|
virtual void fetchChannels(
|
||||||
@@ -1207,10 +1210,10 @@ public:
|
|||||||
HelixFailureCallback failureCallback) final;
|
HelixFailureCallback failureCallback) final;
|
||||||
|
|
||||||
// https://dev.twitch.tv/docs/api/reference#create-clip
|
// https://dev.twitch.tv/docs/api/reference#create-clip
|
||||||
void createClip(QString channelId,
|
void createClip(
|
||||||
ResultCallback<HelixClip> successCallback,
|
QString channelId, ResultCallback<HelixClip> successCallback,
|
||||||
std::function<void(HelixClipError)> failureCallback,
|
std::function<void(HelixClipError, QString)> failureCallback,
|
||||||
std::function<void()> finallyCallback) final;
|
std::function<void()> finallyCallback) final;
|
||||||
|
|
||||||
// https://dev.twitch.tv/docs/api/reference#get-channel-information
|
// https://dev.twitch.tv/docs/api/reference#get-channel-information
|
||||||
void fetchChannels(
|
void fetchChannels(
|
||||||
|
|||||||
Reference in New Issue
Block a user