mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Migrated cheermotes to Helix API (#2440)
Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
parent
7c4c797dbc
commit
519855d852
9 changed files with 159 additions and 401 deletions
|
@ -15,6 +15,7 @@
|
|||
- Bugfix: Fix Ctrl + Backspace not closing colon emote picker. (#2780)
|
||||
- Bugfix: Approving/denying AutoMod messages works again. (#2779)
|
||||
- Dev: Migrated AutoMod approve/deny endpoints to Helix. (#2779)
|
||||
- Dev: Migrated Get Cheermotes endpoint to Helix. (#2440)
|
||||
|
||||
## 2.3.1
|
||||
|
||||
|
|
|
@ -220,7 +220,6 @@ SOURCES += \
|
|||
src/providers/twitch/TwitchHelpers.cpp \
|
||||
src/providers/twitch/TwitchIrcServer.cpp \
|
||||
src/providers/twitch/TwitchMessageBuilder.cpp \
|
||||
src/providers/twitch/TwitchParseCheerEmotes.cpp \
|
||||
src/providers/twitch/TwitchUser.cpp \
|
||||
src/RunGui.cpp \
|
||||
src/singletons/Badges.cpp \
|
||||
|
@ -459,7 +458,6 @@ HEADERS += \
|
|||
src/providers/twitch/TwitchHelpers.hpp \
|
||||
src/providers/twitch/TwitchIrcServer.hpp \
|
||||
src/providers/twitch/TwitchMessageBuilder.hpp \
|
||||
src/providers/twitch/TwitchParseCheerEmotes.hpp \
|
||||
src/providers/twitch/TwitchUser.hpp \
|
||||
src/RunGui.hpp \
|
||||
src/singletons/Badges.hpp \
|
||||
|
|
|
@ -217,8 +217,6 @@ set(SOURCE_FILES main.cpp
|
|||
providers/twitch/TwitchIrcServer.hpp
|
||||
providers/twitch/TwitchMessageBuilder.cpp
|
||||
providers/twitch/TwitchMessageBuilder.hpp
|
||||
providers/twitch/TwitchParseCheerEmotes.cpp
|
||||
providers/twitch/TwitchParseCheerEmotes.hpp
|
||||
providers/twitch/TwitchUser.cpp
|
||||
providers/twitch/TwitchUser.hpp
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include "providers/twitch/PubsubClient.hpp"
|
||||
#include "providers/twitch/TwitchCommon.hpp"
|
||||
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
||||
#include "providers/twitch/TwitchParseCheerEmotes.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/api/Kraken.hpp"
|
||||
#include "singletons/Emotes.hpp"
|
||||
|
@ -910,28 +909,26 @@ void TwitchChannel::refreshBadges()
|
|||
|
||||
void TwitchChannel::refreshCheerEmotes()
|
||||
{
|
||||
QString url("https://api.twitch.tv/kraken/bits/actions?channel_id=" +
|
||||
this->roomId());
|
||||
NetworkRequest::twitchRequest(url)
|
||||
.onSuccess([this,
|
||||
weak = weakOf<Channel>(this)](auto result) -> Outcome {
|
||||
getHelix()->getCheermotes(
|
||||
this->roomId(),
|
||||
[this, weak = weakOf<Channel>(this)](
|
||||
const std::vector<HelixCheermoteSet> &cheermoteSets) -> Outcome {
|
||||
auto shared = weak.lock();
|
||||
if (!shared)
|
||||
{
|
||||
return Failure;
|
||||
}
|
||||
|
||||
auto cheerEmoteSets = ParseCheermoteSets(result.parseRapidJson());
|
||||
std::vector<CheerEmoteSet> emoteSets;
|
||||
|
||||
for (auto &set : cheerEmoteSets)
|
||||
for (const auto &set : cheermoteSets)
|
||||
{
|
||||
auto cheerEmoteSet = CheerEmoteSet();
|
||||
cheerEmoteSet.regex = QRegularExpression(
|
||||
"^" + set.prefix + "([1-9][0-9]*)$",
|
||||
QRegularExpression::CaseInsensitiveOption);
|
||||
|
||||
for (auto &tier : set.tiers)
|
||||
for (const auto &tier : set.tiers)
|
||||
{
|
||||
CheerEmote cheerEmote;
|
||||
|
||||
|
@ -949,36 +946,42 @@ void TwitchChannel::refreshCheerEmotes()
|
|||
cheerEmote.animatedEmote = std::make_shared<Emote>(
|
||||
Emote{EmoteName{"cheer emote"},
|
||||
ImageSet{
|
||||
tier.images["dark"]["animated"]["1"],
|
||||
tier.images["dark"]["animated"]["2"],
|
||||
tier.images["dark"]["animated"]["4"],
|
||||
tier.darkAnimated.imageURL1x,
|
||||
tier.darkAnimated.imageURL2x,
|
||||
tier.darkAnimated.imageURL4x,
|
||||
},
|
||||
Tooltip{emoteTooltip}, Url{}});
|
||||
cheerEmote.staticEmote = std::make_shared<Emote>(
|
||||
Emote{EmoteName{"cheer emote"},
|
||||
ImageSet{
|
||||
tier.images["dark"]["static"]["1"],
|
||||
tier.images["dark"]["static"]["2"],
|
||||
tier.images["dark"]["static"]["4"],
|
||||
tier.darkStatic.imageURL1x,
|
||||
tier.darkStatic.imageURL2x,
|
||||
tier.darkStatic.imageURL4x,
|
||||
},
|
||||
Tooltip{emoteTooltip}, Url{}});
|
||||
|
||||
cheerEmoteSet.cheerEmotes.emplace_back(cheerEmote);
|
||||
cheerEmoteSet.cheerEmotes.emplace_back(
|
||||
std::move(cheerEmote));
|
||||
}
|
||||
|
||||
// Sort cheermotes by cost
|
||||
std::sort(cheerEmoteSet.cheerEmotes.begin(),
|
||||
cheerEmoteSet.cheerEmotes.end(),
|
||||
[](const auto &lhs, const auto &rhs) {
|
||||
return lhs.minBits > rhs.minBits;
|
||||
});
|
||||
|
||||
emoteSets.emplace_back(cheerEmoteSet);
|
||||
emoteSets.emplace_back(std::move(cheerEmoteSet));
|
||||
}
|
||||
|
||||
*this->cheerEmoteSets_.access() = std::move(emoteSets);
|
||||
|
||||
return Success;
|
||||
})
|
||||
.execute();
|
||||
},
|
||||
[] {
|
||||
// Failure
|
||||
return Failure;
|
||||
});
|
||||
}
|
||||
|
||||
void TwitchChannel::createClip()
|
||||
|
|
|
@ -1,321 +0,0 @@
|
|||
#include "TwitchParseCheerEmotes.hpp"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <QString>
|
||||
#include <vector>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename Type>
|
||||
inline bool ReadValue(const rapidjson::Value &object, const char *key,
|
||||
Type &out)
|
||||
{
|
||||
if (!object.HasMember(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &value = object[key];
|
||||
|
||||
if (!value.Is<Type>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
out = value.Get<Type>();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool ReadValue<QString>(const rapidjson::Value &object,
|
||||
const char *key, QString &out)
|
||||
{
|
||||
if (!object.HasMember(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &value = object[key];
|
||||
|
||||
if (!value.IsString())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
out = value.GetString();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool ReadValue<std::vector<QString>>(const rapidjson::Value &object,
|
||||
const char *key,
|
||||
std::vector<QString> &out)
|
||||
{
|
||||
if (!object.HasMember(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &value = object[key];
|
||||
|
||||
if (!value.IsArray())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const rapidjson::Value &innerValue : value.GetArray())
|
||||
{
|
||||
if (!innerValue.IsString())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
out.emplace_back(innerValue.GetString());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse a single cheermote set (or "action") from the twitch api
|
||||
inline bool ParseSingleCheermoteSet(JSONCheermoteSet &set,
|
||||
const rapidjson::Value &action)
|
||||
{
|
||||
if (!action.IsObject())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReadValue(action, "prefix", set.prefix))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReadValue(action, "scales", set.scales))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReadValue(action, "backgrounds", set.backgrounds))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReadValue(action, "states", set.states))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReadValue(action, "type", set.type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReadValue(action, "updated_at", set.updatedAt))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReadValue(action, "priority", set.priority))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Tiers
|
||||
if (!action.HasMember("tiers"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &tiersValue = action["tiers"];
|
||||
|
||||
if (!tiersValue.IsArray())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const rapidjson::Value &tierValue : tiersValue.GetArray())
|
||||
{
|
||||
JSONCheermoteSet::CheermoteTier tier;
|
||||
|
||||
if (!tierValue.IsObject())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReadValue(tierValue, "min_bits", tier.minBits))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReadValue(tierValue, "id", tier.id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReadValue(tierValue, "color", tier.color))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Images
|
||||
if (!tierValue.HasMember("images"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &imagesValue = tierValue["images"];
|
||||
|
||||
if (!imagesValue.IsObject())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read images object
|
||||
for (const auto &imageBackgroundValue : imagesValue.GetObject())
|
||||
{
|
||||
QString background = imageBackgroundValue.name.GetString();
|
||||
bool backgroundExists = false;
|
||||
for (const auto &bg : set.backgrounds)
|
||||
{
|
||||
if (background == bg)
|
||||
{
|
||||
backgroundExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!backgroundExists)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const rapidjson::Value &imageBackgroundStates =
|
||||
imageBackgroundValue.value;
|
||||
if (!imageBackgroundStates.IsObject())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read each key which represents a background
|
||||
for (const auto &imageBackgroundState :
|
||||
imageBackgroundStates.GetObject())
|
||||
{
|
||||
QString state = imageBackgroundState.name.GetString();
|
||||
bool stateExists = false;
|
||||
for (const auto &_state : set.states)
|
||||
{
|
||||
if (state == _state)
|
||||
{
|
||||
stateExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!stateExists)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const rapidjson::Value &imageScalesValue =
|
||||
imageBackgroundState.value;
|
||||
if (!imageScalesValue.IsObject())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read each key which represents a scale
|
||||
for (const auto &imageScaleValue :
|
||||
imageScalesValue.GetObject())
|
||||
{
|
||||
QString scale = imageScaleValue.name.GetString();
|
||||
bool scaleExists = false;
|
||||
for (const auto &_scale : set.scales)
|
||||
{
|
||||
if (scale == _scale)
|
||||
{
|
||||
scaleExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!scaleExists)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const rapidjson::Value &imageScaleURLValue =
|
||||
imageScaleValue.value;
|
||||
if (!imageScaleURLValue.IsString())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QString url = imageScaleURLValue.GetString();
|
||||
|
||||
bool ok = false;
|
||||
qreal scaleNumber = scale.toFloat(&ok);
|
||||
if (!ok)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
qreal chatterinoScale = 1 / scaleNumber;
|
||||
|
||||
auto image = Image::fromUrl({url}, chatterinoScale);
|
||||
|
||||
// TODO(pajlada): Fill in name and tooltip
|
||||
tier.images[background][state][scale] = image;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set.tiers.emplace_back(tier);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Look through the results of
|
||||
// https://api.twitch.tv/kraken/bits/actions?channel_id=11148817 for cheermote
|
||||
// sets or "Actions" as they are called in the API
|
||||
std::vector<JSONCheermoteSet> ParseCheermoteSets(const rapidjson::Document &d)
|
||||
{
|
||||
std::vector<JSONCheermoteSet> sets;
|
||||
|
||||
if (!d.IsObject())
|
||||
{
|
||||
return sets;
|
||||
}
|
||||
|
||||
if (!d.HasMember("actions"))
|
||||
{
|
||||
return sets;
|
||||
}
|
||||
|
||||
const auto &actionsValue = d["actions"];
|
||||
|
||||
if (!actionsValue.IsArray())
|
||||
{
|
||||
return sets;
|
||||
}
|
||||
|
||||
for (const auto &action : actionsValue.GetArray())
|
||||
{
|
||||
JSONCheermoteSet set;
|
||||
bool res = ParseSingleCheermoteSet(set, action);
|
||||
|
||||
if (res)
|
||||
{
|
||||
sets.emplace_back(set);
|
||||
}
|
||||
}
|
||||
|
||||
return sets;
|
||||
}
|
||||
} // namespace chatterino
|
|
@ -1,37 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <QString>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "messages/Image.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
struct JSONCheermoteSet {
|
||||
QString prefix;
|
||||
std::vector<QString> scales;
|
||||
|
||||
std::vector<QString> backgrounds;
|
||||
std::vector<QString> states;
|
||||
|
||||
QString type;
|
||||
QString updatedAt;
|
||||
int priority;
|
||||
|
||||
struct CheermoteTier {
|
||||
int minBits;
|
||||
QString id;
|
||||
QString color;
|
||||
|
||||
// Background State Scale
|
||||
std::map<QString, std::map<QString, std::map<QString, ImagePtr>>>
|
||||
images;
|
||||
};
|
||||
|
||||
std::vector<CheermoteTier> tiers;
|
||||
};
|
||||
|
||||
std::vector<JSONCheermoteSet> ParseCheermoteSets(const rapidjson::Document &d);
|
||||
|
||||
} // namespace chatterino
|
|
@ -722,6 +722,45 @@ void Helix::manageAutoModMessages(
|
|||
.execute();
|
||||
}
|
||||
|
||||
void Helix::getCheermotes(
|
||||
QString broadcasterId,
|
||||
ResultCallback<std::vector<HelixCheermoteSet>> successCallback,
|
||||
HelixFailureCallback failureCallback)
|
||||
{
|
||||
QUrlQuery urlQuery;
|
||||
|
||||
urlQuery.addQueryItem("broadcaster_id", broadcasterId);
|
||||
|
||||
this->makeRequest("bits/cheermotes", urlQuery)
|
||||
.onSuccess([successCallback, failureCallback](auto result) -> Outcome {
|
||||
auto root = result.parseJson();
|
||||
auto data = root.value("data");
|
||||
|
||||
if (!data.isArray())
|
||||
{
|
||||
failureCallback();
|
||||
return Failure;
|
||||
}
|
||||
|
||||
std::vector<HelixCheermoteSet> cheermoteSets;
|
||||
|
||||
for (const auto &jsonStream : data.toArray())
|
||||
{
|
||||
cheermoteSets.emplace_back(jsonStream.toObject());
|
||||
}
|
||||
|
||||
successCallback(cheermoteSets);
|
||||
return Success;
|
||||
})
|
||||
.onError([broadcasterId, failureCallback](NetworkResult result) {
|
||||
qCDebug(chatterinoTwitch)
|
||||
<< "Failed to get cheermotes(broadcaster_id=" << broadcasterId
|
||||
<< "): " << result.status() << result.getData();
|
||||
failureCallback();
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
NetworkRequest Helix::makeRequest(QString url, QUrlQuery urlQuery)
|
||||
{
|
||||
assert(!url.startsWith("/"));
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/Aliases.hpp"
|
||||
#include "common/NetworkRequest.hpp"
|
||||
|
||||
#include <QJsonArray>
|
||||
|
@ -193,6 +194,76 @@ struct HelixBlock {
|
|||
}
|
||||
};
|
||||
|
||||
struct HelixCheermoteImage {
|
||||
Url imageURL1x;
|
||||
Url imageURL2x;
|
||||
Url imageURL4x;
|
||||
|
||||
explicit HelixCheermoteImage(QJsonObject jsonObject)
|
||||
: imageURL1x(Url{jsonObject.value("1").toString()})
|
||||
, imageURL2x(Url{jsonObject.value("2").toString()})
|
||||
, imageURL4x(Url{jsonObject.value("4").toString()})
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct HelixCheermoteTier {
|
||||
QString id;
|
||||
QString color;
|
||||
int minBits;
|
||||
HelixCheermoteImage darkAnimated;
|
||||
HelixCheermoteImage darkStatic;
|
||||
HelixCheermoteImage lightAnimated;
|
||||
HelixCheermoteImage lightStatic;
|
||||
|
||||
explicit HelixCheermoteTier(QJsonObject jsonObject)
|
||||
: id(jsonObject.value("id").toString())
|
||||
, color(jsonObject.value("color").toString())
|
||||
, minBits(jsonObject.value("min_bits").toInt())
|
||||
, darkAnimated(jsonObject.value("images")
|
||||
.toObject()
|
||||
.value("dark")
|
||||
.toObject()
|
||||
.value("animated")
|
||||
.toObject())
|
||||
, darkStatic(jsonObject.value("images")
|
||||
.toObject()
|
||||
.value("dark")
|
||||
.toObject()
|
||||
.value("static")
|
||||
.toObject())
|
||||
, lightAnimated(jsonObject.value("images")
|
||||
.toObject()
|
||||
.value("light")
|
||||
.toObject()
|
||||
.value("animated")
|
||||
.toObject())
|
||||
, lightStatic(jsonObject.value("images")
|
||||
.toObject()
|
||||
.value("light")
|
||||
.toObject()
|
||||
.value("static")
|
||||
.toObject())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct HelixCheermoteSet {
|
||||
QString prefix;
|
||||
QString type;
|
||||
std::vector<HelixCheermoteTier> tiers;
|
||||
|
||||
explicit HelixCheermoteSet(QJsonObject jsonObject)
|
||||
: prefix(jsonObject.value("prefix").toString())
|
||||
, type(jsonObject.value("type").toString())
|
||||
{
|
||||
for (const auto &tier : jsonObject.value("tiers").toArray())
|
||||
{
|
||||
this->tiers.emplace_back(tier.toObject());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
enum class HelixClipError {
|
||||
Unknown,
|
||||
ClipsDisabled,
|
||||
|
@ -321,6 +392,12 @@ public:
|
|||
std::function<void()> successCallback,
|
||||
std::function<void(HelixAutoModMessageError)> failureCallback);
|
||||
|
||||
// https://dev.twitch.tv/docs/api/reference/#get-cheermotes
|
||||
void getCheermotes(
|
||||
QString broadcasterId,
|
||||
ResultCallback<std::vector<HelixCheermoteSet>> successCallback,
|
||||
HelixFailureCallback failureCallback);
|
||||
|
||||
void update(QString clientId, QString oauthToken);
|
||||
|
||||
static void initialize();
|
||||
|
|
|
@ -6,14 +6,6 @@ this folder describes what sort of API requests we do, what permissions are requ
|
|||
|
||||
We use few Kraken endpoints in Chatterino2.
|
||||
|
||||
### Get Cheermotes
|
||||
|
||||
URL: https://dev.twitch.tv/docs/v5/reference/bits#get-cheermotes
|
||||
|
||||
Migration path: **Not checked**
|
||||
|
||||
- We implement this API in `providers/twitch/TwitchChannel.cpp` to resolve a chats available cheer emotes. This helps us parse incoming messages like `pajaCheer1000`
|
||||
|
||||
### Get User Emotes
|
||||
|
||||
URL: https://dev.twitch.tv/docs/v5/reference/users#get-user-emotes
|
||||
|
@ -88,16 +80,16 @@ Requires `clips:edit` scope
|
|||
|
||||
URL: https://dev.twitch.tv/docs/api/reference#get-channel-information
|
||||
|
||||
- We implement this in `providers/twitch/api/Helix.cpp getChannel`
|
||||
- We implement this in `providers/twitch/api/Helix.cpp getChannel`
|
||||
Used in:
|
||||
- `TwitchChannel` to refresh stream title
|
||||
|
||||
### Update Channel
|
||||
|
||||
URL: https://dev.twitch.tv/docs/api/reference#modify-channel-information
|
||||
URL: https://dev.twitch.tv/docs/api/reference#modify-channel-information
|
||||
Requires `channel:manage:broadcast` scope
|
||||
|
||||
- We implement this in `providers/twitch/api/Helix.cpp updateChannel`
|
||||
- We implement this in `providers/twitch/api/Helix.cpp updateChannel`
|
||||
Used in:
|
||||
- `/setgame` to update the game in the current channel
|
||||
- `/settitle` to update the title in the current channel
|
||||
|
@ -113,29 +105,29 @@ Requires `user:edit:broadcast` scope
|
|||
|
||||
### Get User Block List
|
||||
|
||||
URL: https://dev.twitch.tv/docs/api/reference#get-user-block-list
|
||||
URL: https://dev.twitch.tv/docs/api/reference#get-user-block-list
|
||||
Requires `user:read:blocked_users` scope
|
||||
|
||||
- We implement this in `providers/twitch/api/Helix.cpp loadBlocks`
|
||||
- We implement this in `providers/twitch/api/Helix.cpp loadBlocks`
|
||||
Used in:
|
||||
- `providers/twitch/TwitchAccount.cpp loadBlocks` to load list of blocked (blocked) users by current user
|
||||
|
||||
### Block User
|
||||
|
||||
URL: https://dev.twitch.tv/docs/api/reference#block-user
|
||||
URL: https://dev.twitch.tv/docs/api/reference#block-user
|
||||
Requires `user:manage:blocked_users` scope
|
||||
|
||||
- We implement this in `providers/twitch/api/Helix.cpp blockUser`
|
||||
- We implement this in `providers/twitch/api/Helix.cpp blockUser`
|
||||
Used in:
|
||||
- `widgets/dialogs/UserInfoPopup.cpp` to block a user via checkbox in the usercard
|
||||
- `controllers/commands/CommandController.cpp` to block a user via "/block" command
|
||||
|
||||
### Unblock User
|
||||
|
||||
URL: https://dev.twitch.tv/docs/api/reference#unblock-user
|
||||
URL: https://dev.twitch.tv/docs/api/reference#unblock-user
|
||||
Requires `user:manage:blocked_users` scope
|
||||
|
||||
- We implement this in `providers/twitch/api/Helix.cpp unblockUser`
|
||||
- We implement this in `providers/twitch/api/Helix.cpp unblockUser`
|
||||
Used in:
|
||||
- `widgets/dialogs/UserInfoPopup.cpp` to unblock a user via checkbox in the usercard
|
||||
- `controllers/commands/CommandController.cpp` to unblock a user via "/unblock" command
|
||||
|
@ -144,19 +136,27 @@ Requires `user:manage:blocked_users` scope
|
|||
|
||||
URL: https://dev.twitch.tv/docs/api/reference#search-categories
|
||||
|
||||
- We implement this in `providers/twitch/api/Helix.cpp searchGames`
|
||||
- We implement this in `providers/twitch/api/Helix.cpp searchGames`
|
||||
Used in:
|
||||
- `controllers/commands/CommandController.cpp` in `/setgame` command to fuzzy search for game titles
|
||||
|
||||
### Manage Held AutoMod Messages
|
||||
|
||||
URL: https://dev.twitch.tv/docs/api/reference#manage-held-automod-messages
|
||||
URL: https://dev.twitch.tv/docs/api/reference#manage-held-automod-messages
|
||||
Requires `moderator:manage:automod` scope
|
||||
|
||||
- We implement this in `providers/twitch/api/Helix.cpp manageAutoModMessages`
|
||||
- We implement this in `providers/twitch/api/Helix.cpp manageAutoModMessages`
|
||||
Used in:
|
||||
- `providers/twitch/TwitchAccount.cpp` to approve/deny held AutoMod messages
|
||||
|
||||
### Get Cheermotes
|
||||
|
||||
URL: https://dev.twitch.tv/docs/api/reference/#get-cheermotes
|
||||
|
||||
- We implement this in `providers/twitch/api/Helix.cpp getCheermotes`
|
||||
Used in:
|
||||
- `providers/twitch/TwitchChannel.cpp` to resolve a chats available cheer emotes. This helps us parse incoming messages like `pajaCheer1000`
|
||||
|
||||
## TMI
|
||||
|
||||
The TMI api is undocumented.
|
||||
|
|
Loading…
Reference in a new issue