Migrated block, unblock and get user block list methods to Helix (#2370)
Co-authored-by: pajlada <rasmus.karlsson@pajlada.com>
This commit is contained in:
@@ -7,7 +7,9 @@
|
||||
#include "common/NetworkRequest.hpp"
|
||||
#include "common/Outcome.hpp"
|
||||
#include "common/QLogging.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "providers/twitch/TwitchCommon.hpp"
|
||||
#include "providers/twitch/TwitchUser.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "singletons/Emotes.hpp"
|
||||
#include "util/RapidjsonHelpers.hpp"
|
||||
@@ -89,189 +91,60 @@ bool TwitchAccount::isAnon() const
|
||||
return this->isAnon_;
|
||||
}
|
||||
|
||||
void TwitchAccount::loadIgnores()
|
||||
void TwitchAccount::loadBlocks()
|
||||
{
|
||||
QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() +
|
||||
"/blocks");
|
||||
getHelix()->loadBlocks(
|
||||
getApp()->accounts->twitch.getCurrent()->userId_,
|
||||
[this](std::vector<HelixBlock> blocks) {
|
||||
std::lock_guard<std::mutex> lock(this->ignoresMutex_);
|
||||
this->ignores_.clear();
|
||||
|
||||
NetworkRequest(url)
|
||||
|
||||
.authorizeTwitchV5(this->getOAuthClient(), this->getOAuthToken())
|
||||
.onSuccess([=](auto result) -> Outcome {
|
||||
auto document = result.parseRapidJson();
|
||||
if (!document.IsObject())
|
||||
for (const HelixBlock &block : blocks)
|
||||
{
|
||||
return Failure;
|
||||
TwitchUser blockedUser;
|
||||
blockedUser.fromHelixBlock(block);
|
||||
this->ignores_.insert(blockedUser);
|
||||
}
|
||||
|
||||
auto blocksIt = document.FindMember("blocks");
|
||||
if (blocksIt == document.MemberEnd())
|
||||
{
|
||||
return Failure;
|
||||
}
|
||||
const auto &blocks = blocksIt->value;
|
||||
|
||||
if (!blocks.IsArray())
|
||||
{
|
||||
return Failure;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->ignoresMutex_);
|
||||
this->ignores_.clear();
|
||||
|
||||
for (const auto &block : blocks.GetArray())
|
||||
{
|
||||
if (!block.IsObject())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto userIt = block.FindMember("user");
|
||||
if (userIt == block.MemberEnd())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
TwitchUser ignoredUser;
|
||||
if (!rj::getSafe(userIt->value, ignoredUser))
|
||||
{
|
||||
qCWarning(chatterinoTwitch)
|
||||
<< "Error parsing twitch user JSON"
|
||||
<< rj::stringify(userIt->value).c_str();
|
||||
continue;
|
||||
}
|
||||
|
||||
this->ignores_.insert(ignoredUser);
|
||||
}
|
||||
}
|
||||
|
||||
return Success;
|
||||
})
|
||||
.execute();
|
||||
},
|
||||
[] {
|
||||
qDebug() << "Fetching blocks failed!";
|
||||
});
|
||||
}
|
||||
|
||||
void TwitchAccount::ignore(
|
||||
const QString &targetName,
|
||||
std::function<void(IgnoreResult, const QString &)> onFinished)
|
||||
void TwitchAccount::blockUser(QString userId, std::function<void()> onSuccess,
|
||||
std::function<void()> onFailure)
|
||||
{
|
||||
const auto onUserFetched = [this, targetName,
|
||||
onFinished](const auto &user) {
|
||||
this->ignoreByID(user.id, targetName, onFinished);
|
||||
};
|
||||
|
||||
const auto onUserFetchFailed = [] {};
|
||||
|
||||
getHelix()->getUserByName(targetName, onUserFetched, onUserFetchFailed);
|
||||
}
|
||||
|
||||
void TwitchAccount::ignoreByID(
|
||||
const QString &targetUserID, const QString &targetName,
|
||||
std::function<void(IgnoreResult, const QString &)> onFinished)
|
||||
{
|
||||
QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() +
|
||||
"/blocks/" + targetUserID);
|
||||
|
||||
NetworkRequest(url, NetworkRequestType::Put)
|
||||
|
||||
.authorizeTwitchV5(this->getOAuthClient(), this->getOAuthToken())
|
||||
.onError([=](NetworkResult result) {
|
||||
onFinished(IgnoreResult_Failed,
|
||||
QString("An unknown error occurred while trying to "
|
||||
"ignore user %1 (%2)")
|
||||
.arg(targetName)
|
||||
.arg(result.status()));
|
||||
})
|
||||
.onSuccess([=](auto result) -> Outcome {
|
||||
auto document = result.parseRapidJson();
|
||||
if (!document.IsObject())
|
||||
{
|
||||
onFinished(IgnoreResult_Failed,
|
||||
"Bad JSON data while ignoring user " + targetName);
|
||||
return Failure;
|
||||
}
|
||||
|
||||
auto userIt = document.FindMember("user");
|
||||
if (userIt == document.MemberEnd())
|
||||
{
|
||||
onFinished(IgnoreResult_Failed,
|
||||
"Bad JSON data while ignoring user (missing user) " +
|
||||
targetName);
|
||||
return Failure;
|
||||
}
|
||||
|
||||
TwitchUser ignoredUser;
|
||||
if (!rj::getSafe(userIt->value, ignoredUser))
|
||||
{
|
||||
onFinished(IgnoreResult_Failed,
|
||||
"Bad JSON data while ignoring user (invalid user) " +
|
||||
targetName);
|
||||
return Failure;
|
||||
}
|
||||
getHelix()->blockUser(
|
||||
userId,
|
||||
[this, userId, onSuccess] {
|
||||
TwitchUser blockedUser;
|
||||
blockedUser.id = userId;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->ignoresMutex_);
|
||||
|
||||
auto res = this->ignores_.insert(ignoredUser);
|
||||
if (!res.second)
|
||||
{
|
||||
const TwitchUser &existingUser = *(res.first);
|
||||
existingUser.update(ignoredUser);
|
||||
onFinished(IgnoreResult_AlreadyIgnored,
|
||||
"User " + targetName + " is already ignored");
|
||||
return Failure;
|
||||
}
|
||||
this->ignores_.insert(blockedUser);
|
||||
}
|
||||
onFinished(IgnoreResult_Success,
|
||||
"Successfully ignored user " + targetName);
|
||||
|
||||
return Success;
|
||||
})
|
||||
.execute();
|
||||
onSuccess();
|
||||
},
|
||||
onFailure);
|
||||
}
|
||||
|
||||
void TwitchAccount::unignore(
|
||||
const QString &targetName,
|
||||
std::function<void(UnignoreResult, const QString &message)> onFinished)
|
||||
void TwitchAccount::unblockUser(QString userId, std::function<void()> onSuccess,
|
||||
std::function<void()> onFailure)
|
||||
{
|
||||
const auto onUserFetched = [this, targetName,
|
||||
onFinished](const auto &user) {
|
||||
this->unignoreByID(user.id, targetName, onFinished);
|
||||
};
|
||||
|
||||
const auto onUserFetchFailed = [] {};
|
||||
|
||||
getHelix()->getUserByName(targetName, onUserFetched, onUserFetchFailed);
|
||||
}
|
||||
|
||||
void TwitchAccount::unignoreByID(
|
||||
const QString &targetUserID, const QString &targetName,
|
||||
std::function<void(UnignoreResult, const QString &message)> onFinished)
|
||||
{
|
||||
QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() +
|
||||
"/blocks/" + targetUserID);
|
||||
|
||||
NetworkRequest(url, NetworkRequestType::Delete)
|
||||
|
||||
.authorizeTwitchV5(this->getOAuthClient(), this->getOAuthToken())
|
||||
.onError([=](NetworkResult result) {
|
||||
onFinished(
|
||||
UnignoreResult_Failed,
|
||||
"An unknown error occurred while trying to unignore user " +
|
||||
targetName + " (" + QString::number(result.status()) + ")");
|
||||
})
|
||||
.onSuccess([=](auto result) -> Outcome {
|
||||
auto document = result.parseRapidJson();
|
||||
getHelix()->unblockUser(
|
||||
userId,
|
||||
[this, userId, onSuccess] {
|
||||
TwitchUser ignoredUser;
|
||||
ignoredUser.id = targetUserID;
|
||||
ignoredUser.id = userId;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->ignoresMutex_);
|
||||
|
||||
this->ignores_.erase(ignoredUser);
|
||||
}
|
||||
onFinished(UnignoreResult_Success,
|
||||
"Successfully unignored user " + targetName);
|
||||
|
||||
return Success;
|
||||
})
|
||||
.execute();
|
||||
onSuccess();
|
||||
},
|
||||
onFailure);
|
||||
}
|
||||
|
||||
void TwitchAccount::checkFollow(const QString targetUserID,
|
||||
@@ -291,7 +164,7 @@ void TwitchAccount::checkFollow(const QString targetUserID,
|
||||
[] {});
|
||||
}
|
||||
|
||||
std::set<TwitchUser> TwitchAccount::getIgnores() const
|
||||
std::set<TwitchUser> TwitchAccount::getBlocks() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->ignoresMutex_);
|
||||
|
||||
|
||||
@@ -17,17 +17,6 @@
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
enum IgnoreResult {
|
||||
IgnoreResult_Success,
|
||||
IgnoreResult_AlreadyIgnored,
|
||||
IgnoreResult_Failed,
|
||||
};
|
||||
|
||||
enum UnignoreResult {
|
||||
UnignoreResult_Success,
|
||||
UnignoreResult_Failed,
|
||||
};
|
||||
|
||||
enum FollowResult {
|
||||
FollowResult_Following,
|
||||
FollowResult_NotFollowing,
|
||||
@@ -83,23 +72,16 @@ public:
|
||||
|
||||
bool isAnon() const;
|
||||
|
||||
void loadIgnores();
|
||||
void ignore(const QString &targetName,
|
||||
std::function<void(IgnoreResult, const QString &)> onFinished);
|
||||
void ignoreByID(
|
||||
const QString &targetUserID, const QString &targetName,
|
||||
std::function<void(IgnoreResult, const QString &)> onFinished);
|
||||
void unignore(
|
||||
const QString &targetName,
|
||||
std::function<void(UnignoreResult, const QString &)> onFinished);
|
||||
void unignoreByID(
|
||||
const QString &targetUserID, const QString &targetName,
|
||||
std::function<void(UnignoreResult, const QString &message)> onFinished);
|
||||
void loadBlocks();
|
||||
void blockUser(QString userId, std::function<void()> onSuccess,
|
||||
std::function<void()> onFailure);
|
||||
void unblockUser(QString userId, std::function<void()> onSuccess,
|
||||
std::function<void()> onFailure);
|
||||
|
||||
void checkFollow(const QString targetUserID,
|
||||
std::function<void(FollowResult)> onFinished);
|
||||
|
||||
std::set<TwitchUser> getIgnores() const;
|
||||
std::set<TwitchUser> getBlocks() const;
|
||||
|
||||
void loadEmotes();
|
||||
AccessGuard<const TwitchAccountEmoteData> accessEmotes() const;
|
||||
|
||||
@@ -15,7 +15,7 @@ TwitchAccountManager::TwitchAccountManager()
|
||||
{
|
||||
this->currentUserChanged.connect([this] {
|
||||
auto currentUser = this->getCurrent();
|
||||
currentUser->loadIgnores();
|
||||
currentUser->loadBlocks();
|
||||
});
|
||||
|
||||
this->accounts.itemRemoved.connect([this](const auto &acc) {
|
||||
|
||||
@@ -138,18 +138,17 @@ bool TwitchMessageBuilder::isIgnored() const
|
||||
|
||||
auto app = getApp();
|
||||
|
||||
if (getSettings()->enableTwitchIgnoredUsers &&
|
||||
if (getSettings()->enableTwitchBlockedUsers &&
|
||||
this->tags.contains("user-id"))
|
||||
{
|
||||
auto sourceUserID = this->tags.value("user-id").toString();
|
||||
|
||||
for (const auto &user :
|
||||
app->accounts->twitch.getCurrent()->getIgnores())
|
||||
for (const auto &user : app->accounts->twitch.getCurrent()->getBlocks())
|
||||
{
|
||||
if (sourceUserID == user.id)
|
||||
{
|
||||
switch (static_cast<ShowIgnoredUsersMessages>(
|
||||
getSettings()->showIgnoredUsersMessages.getValue()))
|
||||
getSettings()->showBlockedUsersMessages.getValue()))
|
||||
{
|
||||
case ShowIgnoredUsersMessages::IfModerator:
|
||||
if (this->channel->isMod() ||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "util/RapidjsonHelpers.hpp"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
@@ -23,6 +24,13 @@ struct TwitchUser {
|
||||
this->displayName = other.displayName;
|
||||
}
|
||||
|
||||
void fromHelixBlock(const HelixBlock &ignore)
|
||||
{
|
||||
this->id = ignore.userId;
|
||||
this->name = ignore.userName;
|
||||
this->displayName = ignore.displayName;
|
||||
}
|
||||
|
||||
bool operator<(const TwitchUser &rhs) const
|
||||
{
|
||||
return this->id < rhs.id;
|
||||
|
||||
@@ -46,7 +46,7 @@ void Helix::fetchUsers(QStringList userIds, QStringList userLogins,
|
||||
|
||||
return Success;
|
||||
})
|
||||
.onError([failureCallback](auto result) {
|
||||
.onError([failureCallback](auto /*result*/) {
|
||||
// TODO: make better xd
|
||||
failureCallback();
|
||||
})
|
||||
@@ -125,7 +125,7 @@ void Helix::fetchUsersFollows(
|
||||
successCallback(HelixUsersFollowsResponse(root));
|
||||
return Success;
|
||||
})
|
||||
.onError([failureCallback](auto result) {
|
||||
.onError([failureCallback](auto /*result*/) {
|
||||
// TODO: make better xd
|
||||
failureCallback();
|
||||
})
|
||||
@@ -198,7 +198,7 @@ void Helix::fetchStreams(
|
||||
|
||||
return Success;
|
||||
})
|
||||
.onError([failureCallback](auto result) {
|
||||
.onError([failureCallback](auto /*result*/) {
|
||||
// TODO: make better xd
|
||||
failureCallback();
|
||||
})
|
||||
@@ -288,7 +288,7 @@ void Helix::fetchGames(QStringList gameIds, QStringList gameNames,
|
||||
|
||||
return Success;
|
||||
})
|
||||
.onError([failureCallback](auto result) {
|
||||
.onError([failureCallback](auto /*result*/) {
|
||||
// TODO: make better xd
|
||||
failureCallback();
|
||||
})
|
||||
@@ -326,11 +326,11 @@ void Helix::followUser(QString userId, QString targetId,
|
||||
|
||||
this->makeRequest("users/follows", urlQuery)
|
||||
.type(NetworkRequestType::Post)
|
||||
.onSuccess([successCallback](auto result) -> Outcome {
|
||||
.onSuccess([successCallback](auto /*result*/) -> Outcome {
|
||||
successCallback();
|
||||
return Success;
|
||||
})
|
||||
.onError([failureCallback](auto result) {
|
||||
.onError([failureCallback](auto /*result*/) {
|
||||
// TODO: make better xd
|
||||
failureCallback();
|
||||
})
|
||||
@@ -348,11 +348,11 @@ void Helix::unfollowUser(QString userId, QString targetId,
|
||||
|
||||
this->makeRequest("users/follows", urlQuery)
|
||||
.type(NetworkRequestType::Delete)
|
||||
.onSuccess([successCallback](auto result) -> Outcome {
|
||||
.onSuccess([successCallback](auto /*result*/) -> Outcome {
|
||||
successCallback();
|
||||
return Success;
|
||||
})
|
||||
.onError([failureCallback](auto result) {
|
||||
.onError([failureCallback](auto /*result*/) {
|
||||
// TODO: make better xd
|
||||
failureCallback();
|
||||
})
|
||||
@@ -385,7 +385,7 @@ void Helix::createClip(QString channelId,
|
||||
successCallback(clip);
|
||||
return Success;
|
||||
})
|
||||
.onError([failureCallback](NetworkResult result) {
|
||||
.onError([failureCallback](auto result) {
|
||||
switch (result.status())
|
||||
{
|
||||
case 503: {
|
||||
@@ -502,6 +502,82 @@ void Helix::createStreamMarker(
|
||||
.execute();
|
||||
};
|
||||
|
||||
void Helix::loadBlocks(QString userId,
|
||||
ResultCallback<std::vector<HelixBlock>> successCallback,
|
||||
HelixFailureCallback failureCallback)
|
||||
{
|
||||
QUrlQuery urlQuery;
|
||||
urlQuery.addQueryItem("broadcaster_id", userId);
|
||||
|
||||
this->makeRequest("users/blocks", urlQuery)
|
||||
.onSuccess([successCallback, failureCallback](auto result) -> Outcome {
|
||||
auto root = result.parseJson();
|
||||
auto data = root.value("data");
|
||||
|
||||
if (!data.isArray())
|
||||
{
|
||||
failureCallback();
|
||||
return Failure;
|
||||
}
|
||||
|
||||
std::vector<HelixBlock> ignores;
|
||||
|
||||
for (const auto &jsonStream : data.toArray())
|
||||
{
|
||||
ignores.emplace_back(jsonStream.toObject());
|
||||
}
|
||||
|
||||
successCallback(ignores);
|
||||
|
||||
return Success;
|
||||
})
|
||||
.onError([failureCallback](auto /*result*/) {
|
||||
// TODO: make better xd
|
||||
failureCallback();
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
void Helix::blockUser(QString targetUserId,
|
||||
std::function<void()> successCallback,
|
||||
HelixFailureCallback failureCallback)
|
||||
{
|
||||
QUrlQuery urlQuery;
|
||||
urlQuery.addQueryItem("target_user_id", targetUserId);
|
||||
|
||||
this->makeRequest("users/blocks", urlQuery)
|
||||
.type(NetworkRequestType::Put)
|
||||
.onSuccess([successCallback](auto /*result*/) -> Outcome {
|
||||
successCallback();
|
||||
return Success;
|
||||
})
|
||||
.onError([failureCallback](auto /*result*/) {
|
||||
// TODO: make better xd
|
||||
failureCallback();
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
void Helix::unblockUser(QString targetUserId,
|
||||
std::function<void()> successCallback,
|
||||
HelixFailureCallback failureCallback)
|
||||
{
|
||||
QUrlQuery urlQuery;
|
||||
urlQuery.addQueryItem("target_user_id", targetUserId);
|
||||
|
||||
this->makeRequest("users/blocks", urlQuery)
|
||||
.type(NetworkRequestType::Delete)
|
||||
.onSuccess([successCallback](auto /*result*/) -> Outcome {
|
||||
successCallback();
|
||||
return Success;
|
||||
})
|
||||
.onError([failureCallback](auto /*result*/) {
|
||||
// TODO: make better xd
|
||||
failureCallback();
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
NetworkRequest Helix::makeRequest(QString url, QUrlQuery urlQuery)
|
||||
{
|
||||
assert(!url.startsWith("/"));
|
||||
|
||||
@@ -180,6 +180,19 @@ struct HelixStreamMarker {
|
||||
}
|
||||
};
|
||||
|
||||
struct HelixBlock {
|
||||
QString userId;
|
||||
QString userName;
|
||||
QString displayName;
|
||||
|
||||
explicit HelixBlock(QJsonObject jsonObject)
|
||||
: userId(jsonObject.value("user_id").toString())
|
||||
, userName(jsonObject.value("user_login").toString())
|
||||
, displayName(jsonObject.value("display_name").toString())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
enum class HelixClipError {
|
||||
Unknown,
|
||||
ClipsDisabled,
|
||||
@@ -269,6 +282,20 @@ public:
|
||||
ResultCallback<HelixStreamMarker> successCallback,
|
||||
std::function<void(HelixStreamMarkerError)> failureCallback);
|
||||
|
||||
// https://dev.twitch.tv/docs/api/reference#get-user-block-list
|
||||
void loadBlocks(QString userId,
|
||||
ResultCallback<std::vector<HelixBlock>> successCallback,
|
||||
HelixFailureCallback failureCallback);
|
||||
|
||||
// https://dev.twitch.tv/docs/api/reference#block-user
|
||||
void blockUser(QString targetUserId, std::function<void()> successCallback,
|
||||
HelixFailureCallback failureCallback);
|
||||
|
||||
// https://dev.twitch.tv/docs/api/reference#unblock-user
|
||||
void unblockUser(QString targetUserId,
|
||||
std::function<void()> successCallback,
|
||||
HelixFailureCallback failureCallback);
|
||||
|
||||
void update(QString clientId, QString oauthToken);
|
||||
|
||||
static void initialize();
|
||||
|
||||
@@ -11,29 +11,6 @@ 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 Block List
|
||||
URL: https://dev.twitch.tv/docs/v5/reference/users#get-user-block-list
|
||||
|
||||
Migration path: **Unknown**
|
||||
|
||||
* We use this in `providers/twitch/TwitchAccount.cpp loadIgnores`
|
||||
|
||||
### Block User
|
||||
URL: https://dev.twitch.tv/docs/v5/reference/users#block-user
|
||||
Requires `user_blocks_edit` scope
|
||||
|
||||
Migration path: **Unknown**
|
||||
|
||||
* We use this in `providers/twitch/TwitchAccount.cpp ignoreByID`
|
||||
|
||||
### Unblock User
|
||||
URL: https://dev.twitch.tv/docs/v5/reference/users#unblock-user
|
||||
Requires `user_blocks_edit` scope
|
||||
|
||||
Migration path: **Unknown**
|
||||
|
||||
* We use this in `providers/twitch/TwitchAccount.cpp unignoreByID`
|
||||
|
||||
### Get User Emotes
|
||||
URL: https://dev.twitch.tv/docs/v5/reference/users#get-user-emotes
|
||||
Requires `user_subscriptions` scope
|
||||
@@ -63,7 +40,7 @@ URL: https://dev.twitch.tv/docs/api/reference#get-users
|
||||
* `UserInfoPopup` to get ID, viewCount, displayName, createdAt of username we clicked
|
||||
* `CommandController` to power any commands that need to get a user ID
|
||||
* `Toasts` to get the profile picture of a streamer who just went live
|
||||
* `TwitchAccount` ignore and unignore features to translate user name to user ID
|
||||
* `TwitchAccount` block and unblock features to translate user name to user ID
|
||||
|
||||
### Get Users Follows
|
||||
URL: https://dev.twitch.tv/docs/api/reference#get-users-follows
|
||||
@@ -121,6 +98,32 @@ Requires `user:edit:broadcast` scope
|
||||
Used in:
|
||||
* `controllers/commands/CommandController.cpp` in /marker command
|
||||
|
||||
### 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`
|
||||
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
|
||||
Requires `user:manage:blocked_users` scope
|
||||
|
||||
* 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
|
||||
Requires `user:manage:blocked_users` scope
|
||||
|
||||
* 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
|
||||
|
||||
## TMI
|
||||
The TMI api is undocumented.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user