mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
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:
parent
46f1347e4b
commit
7d9f4c2b0c
14 changed files with 406 additions and 349 deletions
|
@ -6,6 +6,7 @@
|
||||||
- Major: Added "Channel Filters". See https://wiki.chatterino.com/Filters/ for how they work or how to configure them. (#1748, #2083, #2090, #2200, #2225)
|
- Major: Added "Channel Filters". See https://wiki.chatterino.com/Filters/ for how they work or how to configure them. (#1748, #2083, #2090, #2200, #2225)
|
||||||
- Major: Added Streamer Mode configuration (under `Settings -> General`), where you can select which features of Chatterino should behave differently when you are in Streamer Mode. (#2001, #2316, #2342, #2376)
|
- Major: Added Streamer Mode configuration (under `Settings -> General`), where you can select which features of Chatterino should behave differently when you are in Streamer Mode. (#2001, #2316, #2342, #2376)
|
||||||
- Major: Color mentions to match the mentioned users. You can disable this by unchecking "Color @usernames" under `Settings -> General -> Advanced (misc.)`. (#1963, #2284)
|
- Major: Color mentions to match the mentioned users. You can disable this by unchecking "Color @usernames" under `Settings -> General -> Advanced (misc.)`. (#1963, #2284)
|
||||||
|
- Major: Commands `/ignore` and `/unignore` have been renamed to `/block` and `/unblock` in order to keep consistency with Twitch's terms. (#2370)
|
||||||
- Minor: Added `/marker` command - similar to webchat, it creates a stream marker. (#2360)
|
- Minor: Added `/marker` command - similar to webchat, it creates a stream marker. (#2360)
|
||||||
- Minor: Added `/chatters` command showing chatter count. (#2344)
|
- Minor: Added `/chatters` command showing chatter count. (#2344)
|
||||||
- Minor: Added a button to the split context menu to open the moderation view for a channel when the account selected has moderator permissions. (#2321)
|
- Minor: Added a button to the split context menu to open the moderation view for a channel when the account selected has moderator permissions. (#2321)
|
||||||
|
@ -80,6 +81,7 @@
|
||||||
- Dev: Migrated `Kraken::getUser` to Helix (#2260)
|
- Dev: Migrated `Kraken::getUser` to Helix (#2260)
|
||||||
- Dev: Migrated `TwitchAccount::(un)followUser` from Kraken to Helix and moved it to `Helix::(un)followUser`. (#2306)
|
- Dev: Migrated `TwitchAccount::(un)followUser` from Kraken to Helix and moved it to `Helix::(un)followUser`. (#2306)
|
||||||
- Dev: Migrated `Kraken::getChannel` to Helix. (#2381)
|
- Dev: Migrated `Kraken::getChannel` to Helix. (#2381)
|
||||||
|
- Dev: Migrated `TwitchAccount::(un)ignoreUser` to Helix and made `TwitchAccount::loadIgnores` use Helix call. (#2370)
|
||||||
- Dev: Build in CI with multiple Qt versions (#2349)
|
- Dev: Build in CI with multiple Qt versions (#2349)
|
||||||
- Dev: Updated minimum required macOS version to 10.14 (#2386)
|
- Dev: Updated minimum required macOS version to 10.14 (#2386)
|
||||||
- Dev: Removed unused `humanize` library (#2422)
|
- Dev: Removed unused `humanize` library (#2422)
|
||||||
|
|
|
@ -230,7 +230,127 @@ void CommandController::initialize(Settings &, Paths &paths)
|
||||||
this->items_.append(command);
|
this->items_.append(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->registerCommand("/debug-args", [](const auto &words, auto channel) {
|
/// Deprecated commands
|
||||||
|
|
||||||
|
auto blockLambda = [](const auto &words, auto channel) {
|
||||||
|
if (words.size() < 2)
|
||||||
|
{
|
||||||
|
channel->addMessage(makeSystemMessage("Usage: /block [user]"));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto currentUser = getApp()->accounts->twitch.getCurrent();
|
||||||
|
|
||||||
|
if (currentUser->isAnon())
|
||||||
|
{
|
||||||
|
channel->addMessage(
|
||||||
|
makeSystemMessage("You must be logged in to block someone!"));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto target = words.at(1);
|
||||||
|
|
||||||
|
getHelix()->getUserByName(
|
||||||
|
target,
|
||||||
|
[currentUser, channel, target](const HelixUser &targetUser) {
|
||||||
|
getApp()->accounts->twitch.getCurrent()->blockUser(
|
||||||
|
targetUser.id,
|
||||||
|
[channel, target, targetUser] {
|
||||||
|
channel->addMessage(makeSystemMessage(
|
||||||
|
QString("You successfully blocked user %1")
|
||||||
|
.arg(target)));
|
||||||
|
},
|
||||||
|
[channel, target] {
|
||||||
|
channel->addMessage(makeSystemMessage(
|
||||||
|
QString("User %1 couldn't be blocked, an unknown "
|
||||||
|
"error occurred!")
|
||||||
|
.arg(target)));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[channel, target] {
|
||||||
|
channel->addMessage(
|
||||||
|
makeSystemMessage(QString("User %1 couldn't be blocked, no "
|
||||||
|
"user with that name found!")
|
||||||
|
.arg(target)));
|
||||||
|
});
|
||||||
|
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
auto unblockLambda = [](const auto &words, auto channel) {
|
||||||
|
if (words.size() < 2)
|
||||||
|
{
|
||||||
|
channel->addMessage(makeSystemMessage("Usage: /unblock [user]"));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto currentUser = getApp()->accounts->twitch.getCurrent();
|
||||||
|
|
||||||
|
if (currentUser->isAnon())
|
||||||
|
{
|
||||||
|
channel->addMessage(
|
||||||
|
makeSystemMessage("You must be logged in to unblock someone!"));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto target = words.at(1);
|
||||||
|
|
||||||
|
getHelix()->getUserByName(
|
||||||
|
target,
|
||||||
|
[currentUser, channel, target](const auto &targetUser) {
|
||||||
|
getApp()->accounts->twitch.getCurrent()->unblockUser(
|
||||||
|
targetUser.id,
|
||||||
|
[channel, target, targetUser] {
|
||||||
|
channel->addMessage(makeSystemMessage(
|
||||||
|
QString("You successfully unblocked user %1")
|
||||||
|
.arg(target)));
|
||||||
|
},
|
||||||
|
[channel, target] {
|
||||||
|
channel->addMessage(makeSystemMessage(
|
||||||
|
QString("User %1 couldn't be unblocked, an unknown "
|
||||||
|
"error occurred!")
|
||||||
|
.arg(target)));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[channel, target] {
|
||||||
|
channel->addMessage(
|
||||||
|
makeSystemMessage(QString("User %1 couldn't be unblocked, "
|
||||||
|
"no user with that name found!")
|
||||||
|
.arg(target)));
|
||||||
|
});
|
||||||
|
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
this->registerCommand("/logs", [](const auto & /*words*/, auto channel) {
|
||||||
|
channel->addMessage(makeSystemMessage(
|
||||||
|
"Online logs functionality has been removed. If you're a "
|
||||||
|
"moderator, you can use the /user command"));
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
|
||||||
|
this->registerCommand(
|
||||||
|
"/ignore", [blockLambda](const auto &words, auto channel) {
|
||||||
|
channel->addMessage(makeSystemMessage(
|
||||||
|
"Ignore command has been renamed to /block, please use it from "
|
||||||
|
"now on as /ignore is going to be removed soon."));
|
||||||
|
blockLambda(words, channel);
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
|
||||||
|
this->registerCommand(
|
||||||
|
"/unignore", [unblockLambda](const auto &words, auto channel) {
|
||||||
|
channel->addMessage(makeSystemMessage(
|
||||||
|
"Unignore command has been renamed to /unblock, please use it "
|
||||||
|
"from now on as /unignore is going to be removed soon."));
|
||||||
|
unblockLambda(words, channel);
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Supported commands
|
||||||
|
|
||||||
|
this->registerCommand(
|
||||||
|
"/debug-args", [](const auto & /*words*/, auto channel) {
|
||||||
QString msg = QApplication::instance()->arguments().join(' ');
|
QString msg = QApplication::instance()->arguments().join(' ');
|
||||||
|
|
||||||
channel->addMessage(makeSystemMessage(msg));
|
channel->addMessage(makeSystemMessage(msg));
|
||||||
|
@ -238,7 +358,7 @@ void CommandController::initialize(Settings &, Paths &paths)
|
||||||
return "";
|
return "";
|
||||||
});
|
});
|
||||||
|
|
||||||
this->registerCommand("/uptime", [](const auto &words, auto channel) {
|
this->registerCommand("/uptime", [](const auto & /*words*/, auto channel) {
|
||||||
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
|
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
|
||||||
if (twitchChannel == nullptr)
|
if (twitchChannel == nullptr)
|
||||||
{
|
{
|
||||||
|
@ -257,57 +377,9 @@ void CommandController::initialize(Settings &, Paths &paths)
|
||||||
return "";
|
return "";
|
||||||
});
|
});
|
||||||
|
|
||||||
this->registerCommand("/ignore", [](const auto &words, auto channel) {
|
this->registerCommand("/block", blockLambda);
|
||||||
if (words.size() < 2)
|
|
||||||
{
|
|
||||||
channel->addMessage(makeSystemMessage("Usage: /ignore [user]"));
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
auto app = getApp();
|
|
||||||
|
|
||||||
auto user = app->accounts->twitch.getCurrent();
|
this->registerCommand("/unblock", unblockLambda);
|
||||||
auto target = words.at(1);
|
|
||||||
|
|
||||||
if (user->isAnon())
|
|
||||||
{
|
|
||||||
channel->addMessage(
|
|
||||||
makeSystemMessage("You must be logged in to ignore someone"));
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
user->ignore(target,
|
|
||||||
[channel](auto resultCode, const QString &message) {
|
|
||||||
channel->addMessage(makeSystemMessage(message));
|
|
||||||
});
|
|
||||||
|
|
||||||
return "";
|
|
||||||
});
|
|
||||||
|
|
||||||
this->registerCommand("/unignore", [](const auto &words, auto channel) {
|
|
||||||
if (words.size() < 2)
|
|
||||||
{
|
|
||||||
channel->addMessage(makeSystemMessage("Usage: /unignore [user]"));
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
auto app = getApp();
|
|
||||||
|
|
||||||
auto user = app->accounts->twitch.getCurrent();
|
|
||||||
auto target = words.at(1);
|
|
||||||
|
|
||||||
if (user->isAnon())
|
|
||||||
{
|
|
||||||
channel->addMessage(
|
|
||||||
makeSystemMessage("You must be logged in to ignore someone"));
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
user->unignore(target,
|
|
||||||
[channel](auto resultCode, const QString &message) {
|
|
||||||
channel->addMessage(makeSystemMessage(message));
|
|
||||||
});
|
|
||||||
|
|
||||||
return "";
|
|
||||||
});
|
|
||||||
|
|
||||||
this->registerCommand("/follow", [](const auto &words, auto channel) {
|
this->registerCommand("/follow", [](const auto &words, auto channel) {
|
||||||
if (words.size() < 2)
|
if (words.size() < 2)
|
||||||
|
@ -321,7 +393,7 @@ void CommandController::initialize(Settings &, Paths &paths)
|
||||||
if (currentUser->isAnon())
|
if (currentUser->isAnon())
|
||||||
{
|
{
|
||||||
channel->addMessage(
|
channel->addMessage(
|
||||||
makeSystemMessage("You must be logged in to follow someone"));
|
makeSystemMessage("You must be logged in to follow someone!"));
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,8 +436,8 @@ void CommandController::initialize(Settings &, Paths &paths)
|
||||||
|
|
||||||
if (currentUser->isAnon())
|
if (currentUser->isAnon())
|
||||||
{
|
{
|
||||||
channel->addMessage(
|
channel->addMessage(makeSystemMessage(
|
||||||
makeSystemMessage("You must be logged in to follow someone"));
|
"You must be logged in to unfollow someone!"));
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,13 +465,6 @@ void CommandController::initialize(Settings &, Paths &paths)
|
||||||
return "";
|
return "";
|
||||||
});
|
});
|
||||||
|
|
||||||
this->registerCommand("/logs", [](const auto & /*words*/, auto channel) {
|
|
||||||
channel->addMessage(makeSystemMessage(
|
|
||||||
"Online logs functionality has been removed. If you're a "
|
|
||||||
"moderator, you can use the /user command"));
|
|
||||||
return "";
|
|
||||||
});
|
|
||||||
|
|
||||||
this->registerCommand("/user", [](const auto &words, auto channel) {
|
this->registerCommand("/user", [](const auto &words, auto channel) {
|
||||||
if (words.size() < 2)
|
if (words.size() < 2)
|
||||||
{
|
{
|
||||||
|
@ -455,7 +520,7 @@ void CommandController::initialize(Settings &, Paths &paths)
|
||||||
return "";
|
return "";
|
||||||
});
|
});
|
||||||
|
|
||||||
this->registerCommand("/clip", [](const auto &words, auto channel) {
|
this->registerCommand("/clip", [](const auto & /*words*/, auto channel) {
|
||||||
if (!channel->isTwitchChannel())
|
if (!channel->isTwitchChannel())
|
||||||
{
|
{
|
||||||
return "";
|
return "";
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
#include "common/NetworkRequest.hpp"
|
#include "common/NetworkRequest.hpp"
|
||||||
#include "common/Outcome.hpp"
|
#include "common/Outcome.hpp"
|
||||||
#include "common/QLogging.hpp"
|
#include "common/QLogging.hpp"
|
||||||
|
#include "controllers/accounts/AccountController.hpp"
|
||||||
#include "providers/twitch/TwitchCommon.hpp"
|
#include "providers/twitch/TwitchCommon.hpp"
|
||||||
|
#include "providers/twitch/TwitchUser.hpp"
|
||||||
#include "providers/twitch/api/Helix.hpp"
|
#include "providers/twitch/api/Helix.hpp"
|
||||||
#include "singletons/Emotes.hpp"
|
#include "singletons/Emotes.hpp"
|
||||||
#include "util/RapidjsonHelpers.hpp"
|
#include "util/RapidjsonHelpers.hpp"
|
||||||
|
@ -89,189 +91,60 @@ bool TwitchAccount::isAnon() const
|
||||||
return this->isAnon_;
|
return this->isAnon_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchAccount::loadIgnores()
|
void TwitchAccount::loadBlocks()
|
||||||
{
|
|
||||||
QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() +
|
|
||||||
"/blocks");
|
|
||||||
|
|
||||||
NetworkRequest(url)
|
|
||||||
|
|
||||||
.authorizeTwitchV5(this->getOAuthClient(), this->getOAuthToken())
|
|
||||||
.onSuccess([=](auto result) -> Outcome {
|
|
||||||
auto document = result.parseRapidJson();
|
|
||||||
if (!document.IsObject())
|
|
||||||
{
|
|
||||||
return Failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto blocksIt = document.FindMember("blocks");
|
|
||||||
if (blocksIt == document.MemberEnd())
|
|
||||||
{
|
|
||||||
return Failure;
|
|
||||||
}
|
|
||||||
const auto &blocks = blocksIt->value;
|
|
||||||
|
|
||||||
if (!blocks.IsArray())
|
|
||||||
{
|
|
||||||
return Failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
|
getHelix()->loadBlocks(
|
||||||
|
getApp()->accounts->twitch.getCurrent()->userId_,
|
||||||
|
[this](std::vector<HelixBlock> blocks) {
|
||||||
std::lock_guard<std::mutex> lock(this->ignoresMutex_);
|
std::lock_guard<std::mutex> lock(this->ignoresMutex_);
|
||||||
this->ignores_.clear();
|
this->ignores_.clear();
|
||||||
|
|
||||||
for (const auto &block : blocks.GetArray())
|
for (const HelixBlock &block : blocks)
|
||||||
{
|
{
|
||||||
if (!block.IsObject())
|
TwitchUser blockedUser;
|
||||||
|
blockedUser.fromHelixBlock(block);
|
||||||
|
this->ignores_.insert(blockedUser);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[] {
|
||||||
|
qDebug() << "Fetching blocks failed!";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TwitchAccount::blockUser(QString userId, std::function<void()> onSuccess,
|
||||||
|
std::function<void()> onFailure)
|
||||||
{
|
{
|
||||||
continue;
|
getHelix()->blockUser(
|
||||||
}
|
userId,
|
||||||
auto userIt = block.FindMember("user");
|
[this, userId, onSuccess] {
|
||||||
if (userIt == block.MemberEnd())
|
TwitchUser blockedUser;
|
||||||
{
|
blockedUser.id = userId;
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TwitchAccount::ignore(
|
|
||||||
const QString &targetName,
|
|
||||||
std::function<void(IgnoreResult, const QString &)> onFinished)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(this->ignoresMutex_);
|
std::lock_guard<std::mutex> lock(this->ignoresMutex_);
|
||||||
|
|
||||||
auto res = this->ignores_.insert(ignoredUser);
|
this->ignores_.insert(blockedUser);
|
||||||
if (!res.second)
|
}
|
||||||
|
onSuccess();
|
||||||
|
},
|
||||||
|
onFailure);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TwitchAccount::unblockUser(QString userId, std::function<void()> onSuccess,
|
||||||
|
std::function<void()> onFailure)
|
||||||
{
|
{
|
||||||
const TwitchUser &existingUser = *(res.first);
|
getHelix()->unblockUser(
|
||||||
existingUser.update(ignoredUser);
|
userId,
|
||||||
onFinished(IgnoreResult_AlreadyIgnored,
|
[this, userId, onSuccess] {
|
||||||
"User " + targetName + " is already ignored");
|
|
||||||
return Failure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onFinished(IgnoreResult_Success,
|
|
||||||
"Successfully ignored user " + targetName);
|
|
||||||
|
|
||||||
return Success;
|
|
||||||
})
|
|
||||||
.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TwitchAccount::unignore(
|
|
||||||
const QString &targetName,
|
|
||||||
std::function<void(UnignoreResult, const QString &message)> onFinished)
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
TwitchUser ignoredUser;
|
TwitchUser ignoredUser;
|
||||||
ignoredUser.id = targetUserID;
|
ignoredUser.id = userId;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(this->ignoresMutex_);
|
std::lock_guard<std::mutex> lock(this->ignoresMutex_);
|
||||||
|
|
||||||
this->ignores_.erase(ignoredUser);
|
this->ignores_.erase(ignoredUser);
|
||||||
}
|
}
|
||||||
onFinished(UnignoreResult_Success,
|
onSuccess();
|
||||||
"Successfully unignored user " + targetName);
|
},
|
||||||
|
onFailure);
|
||||||
return Success;
|
|
||||||
})
|
|
||||||
.execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchAccount::checkFollow(const QString targetUserID,
|
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_);
|
std::lock_guard<std::mutex> lock(this->ignoresMutex_);
|
||||||
|
|
||||||
|
|
|
@ -17,17 +17,6 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
enum IgnoreResult {
|
|
||||||
IgnoreResult_Success,
|
|
||||||
IgnoreResult_AlreadyIgnored,
|
|
||||||
IgnoreResult_Failed,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum UnignoreResult {
|
|
||||||
UnignoreResult_Success,
|
|
||||||
UnignoreResult_Failed,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum FollowResult {
|
enum FollowResult {
|
||||||
FollowResult_Following,
|
FollowResult_Following,
|
||||||
FollowResult_NotFollowing,
|
FollowResult_NotFollowing,
|
||||||
|
@ -83,23 +72,16 @@ public:
|
||||||
|
|
||||||
bool isAnon() const;
|
bool isAnon() const;
|
||||||
|
|
||||||
void loadIgnores();
|
void loadBlocks();
|
||||||
void ignore(const QString &targetName,
|
void blockUser(QString userId, std::function<void()> onSuccess,
|
||||||
std::function<void(IgnoreResult, const QString &)> onFinished);
|
std::function<void()> onFailure);
|
||||||
void ignoreByID(
|
void unblockUser(QString userId, std::function<void()> onSuccess,
|
||||||
const QString &targetUserID, const QString &targetName,
|
std::function<void()> onFailure);
|
||||||
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 checkFollow(const QString targetUserID,
|
void checkFollow(const QString targetUserID,
|
||||||
std::function<void(FollowResult)> onFinished);
|
std::function<void(FollowResult)> onFinished);
|
||||||
|
|
||||||
std::set<TwitchUser> getIgnores() const;
|
std::set<TwitchUser> getBlocks() const;
|
||||||
|
|
||||||
void loadEmotes();
|
void loadEmotes();
|
||||||
AccessGuard<const TwitchAccountEmoteData> accessEmotes() const;
|
AccessGuard<const TwitchAccountEmoteData> accessEmotes() const;
|
||||||
|
|
|
@ -15,7 +15,7 @@ TwitchAccountManager::TwitchAccountManager()
|
||||||
{
|
{
|
||||||
this->currentUserChanged.connect([this] {
|
this->currentUserChanged.connect([this] {
|
||||||
auto currentUser = this->getCurrent();
|
auto currentUser = this->getCurrent();
|
||||||
currentUser->loadIgnores();
|
currentUser->loadBlocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
this->accounts.itemRemoved.connect([this](const auto &acc) {
|
this->accounts.itemRemoved.connect([this](const auto &acc) {
|
||||||
|
|
|
@ -138,18 +138,17 @@ bool TwitchMessageBuilder::isIgnored() const
|
||||||
|
|
||||||
auto app = getApp();
|
auto app = getApp();
|
||||||
|
|
||||||
if (getSettings()->enableTwitchIgnoredUsers &&
|
if (getSettings()->enableTwitchBlockedUsers &&
|
||||||
this->tags.contains("user-id"))
|
this->tags.contains("user-id"))
|
||||||
{
|
{
|
||||||
auto sourceUserID = this->tags.value("user-id").toString();
|
auto sourceUserID = this->tags.value("user-id").toString();
|
||||||
|
|
||||||
for (const auto &user :
|
for (const auto &user : app->accounts->twitch.getCurrent()->getBlocks())
|
||||||
app->accounts->twitch.getCurrent()->getIgnores())
|
|
||||||
{
|
{
|
||||||
if (sourceUserID == user.id)
|
if (sourceUserID == user.id)
|
||||||
{
|
{
|
||||||
switch (static_cast<ShowIgnoredUsersMessages>(
|
switch (static_cast<ShowIgnoredUsersMessages>(
|
||||||
getSettings()->showIgnoredUsersMessages.getValue()))
|
getSettings()->showBlockedUsersMessages.getValue()))
|
||||||
{
|
{
|
||||||
case ShowIgnoredUsersMessages::IfModerator:
|
case ShowIgnoredUsersMessages::IfModerator:
|
||||||
if (this->channel->isMod() ||
|
if (this->channel->isMod() ||
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "providers/twitch/api/Helix.hpp"
|
||||||
#include "util/RapidjsonHelpers.hpp"
|
#include "util/RapidjsonHelpers.hpp"
|
||||||
|
|
||||||
#include <rapidjson/document.h>
|
#include <rapidjson/document.h>
|
||||||
|
@ -23,6 +24,13 @@ struct TwitchUser {
|
||||||
this->displayName = other.displayName;
|
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
|
bool operator<(const TwitchUser &rhs) const
|
||||||
{
|
{
|
||||||
return this->id < rhs.id;
|
return this->id < rhs.id;
|
||||||
|
|
|
@ -46,7 +46,7 @@ void Helix::fetchUsers(QStringList userIds, QStringList userLogins,
|
||||||
|
|
||||||
return Success;
|
return Success;
|
||||||
})
|
})
|
||||||
.onError([failureCallback](auto result) {
|
.onError([failureCallback](auto /*result*/) {
|
||||||
// TODO: make better xd
|
// TODO: make better xd
|
||||||
failureCallback();
|
failureCallback();
|
||||||
})
|
})
|
||||||
|
@ -125,7 +125,7 @@ void Helix::fetchUsersFollows(
|
||||||
successCallback(HelixUsersFollowsResponse(root));
|
successCallback(HelixUsersFollowsResponse(root));
|
||||||
return Success;
|
return Success;
|
||||||
})
|
})
|
||||||
.onError([failureCallback](auto result) {
|
.onError([failureCallback](auto /*result*/) {
|
||||||
// TODO: make better xd
|
// TODO: make better xd
|
||||||
failureCallback();
|
failureCallback();
|
||||||
})
|
})
|
||||||
|
@ -198,7 +198,7 @@ void Helix::fetchStreams(
|
||||||
|
|
||||||
return Success;
|
return Success;
|
||||||
})
|
})
|
||||||
.onError([failureCallback](auto result) {
|
.onError([failureCallback](auto /*result*/) {
|
||||||
// TODO: make better xd
|
// TODO: make better xd
|
||||||
failureCallback();
|
failureCallback();
|
||||||
})
|
})
|
||||||
|
@ -288,7 +288,7 @@ void Helix::fetchGames(QStringList gameIds, QStringList gameNames,
|
||||||
|
|
||||||
return Success;
|
return Success;
|
||||||
})
|
})
|
||||||
.onError([failureCallback](auto result) {
|
.onError([failureCallback](auto /*result*/) {
|
||||||
// TODO: make better xd
|
// TODO: make better xd
|
||||||
failureCallback();
|
failureCallback();
|
||||||
})
|
})
|
||||||
|
@ -326,11 +326,11 @@ void Helix::followUser(QString userId, QString targetId,
|
||||||
|
|
||||||
this->makeRequest("users/follows", urlQuery)
|
this->makeRequest("users/follows", urlQuery)
|
||||||
.type(NetworkRequestType::Post)
|
.type(NetworkRequestType::Post)
|
||||||
.onSuccess([successCallback](auto result) -> Outcome {
|
.onSuccess([successCallback](auto /*result*/) -> Outcome {
|
||||||
successCallback();
|
successCallback();
|
||||||
return Success;
|
return Success;
|
||||||
})
|
})
|
||||||
.onError([failureCallback](auto result) {
|
.onError([failureCallback](auto /*result*/) {
|
||||||
// TODO: make better xd
|
// TODO: make better xd
|
||||||
failureCallback();
|
failureCallback();
|
||||||
})
|
})
|
||||||
|
@ -348,11 +348,11 @@ void Helix::unfollowUser(QString userId, QString targetId,
|
||||||
|
|
||||||
this->makeRequest("users/follows", urlQuery)
|
this->makeRequest("users/follows", urlQuery)
|
||||||
.type(NetworkRequestType::Delete)
|
.type(NetworkRequestType::Delete)
|
||||||
.onSuccess([successCallback](auto result) -> Outcome {
|
.onSuccess([successCallback](auto /*result*/) -> Outcome {
|
||||||
successCallback();
|
successCallback();
|
||||||
return Success;
|
return Success;
|
||||||
})
|
})
|
||||||
.onError([failureCallback](auto result) {
|
.onError([failureCallback](auto /*result*/) {
|
||||||
// TODO: make better xd
|
// TODO: make better xd
|
||||||
failureCallback();
|
failureCallback();
|
||||||
})
|
})
|
||||||
|
@ -385,7 +385,7 @@ void Helix::createClip(QString channelId,
|
||||||
successCallback(clip);
|
successCallback(clip);
|
||||||
return Success;
|
return Success;
|
||||||
})
|
})
|
||||||
.onError([failureCallback](NetworkResult result) {
|
.onError([failureCallback](auto result) {
|
||||||
switch (result.status())
|
switch (result.status())
|
||||||
{
|
{
|
||||||
case 503: {
|
case 503: {
|
||||||
|
@ -502,6 +502,82 @@ void Helix::createStreamMarker(
|
||||||
.execute();
|
.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)
|
NetworkRequest Helix::makeRequest(QString url, QUrlQuery urlQuery)
|
||||||
{
|
{
|
||||||
assert(!url.startsWith("/"));
|
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 {
|
enum class HelixClipError {
|
||||||
Unknown,
|
Unknown,
|
||||||
ClipsDisabled,
|
ClipsDisabled,
|
||||||
|
@ -269,6 +282,20 @@ public:
|
||||||
ResultCallback<HelixStreamMarker> successCallback,
|
ResultCallback<HelixStreamMarker> successCallback,
|
||||||
std::function<void(HelixStreamMarkerError)> failureCallback);
|
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);
|
void update(QString clientId, QString oauthToken);
|
||||||
|
|
||||||
static void initialize();
|
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`
|
* 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
|
### Get User Emotes
|
||||||
URL: https://dev.twitch.tv/docs/v5/reference/users#get-user-emotes
|
URL: https://dev.twitch.tv/docs/v5/reference/users#get-user-emotes
|
||||||
Requires `user_subscriptions` scope
|
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
|
* `UserInfoPopup` to get ID, viewCount, displayName, createdAt of username we clicked
|
||||||
* `CommandController` to power any commands that need to get a user ID
|
* `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
|
* `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
|
### Get Users Follows
|
||||||
URL: https://dev.twitch.tv/docs/api/reference#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:
|
Used in:
|
||||||
* `controllers/commands/CommandController.cpp` in /marker command
|
* `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
|
## TMI
|
||||||
The TMI api is undocumented.
|
The TMI api is undocumented.
|
||||||
|
|
||||||
|
|
|
@ -200,10 +200,10 @@ public:
|
||||||
QStringSetting ignoredPhraseReplace = {"/ignore/ignoredPhraseReplace",
|
QStringSetting ignoredPhraseReplace = {"/ignore/ignoredPhraseReplace",
|
||||||
"***"};
|
"***"};
|
||||||
|
|
||||||
/// Ingored Users
|
/// Blocked Users
|
||||||
BoolSetting enableTwitchIgnoredUsers = {"/ignore/enableTwitchIgnoredUsers",
|
BoolSetting enableTwitchBlockedUsers = {"/ignore/enableTwitchBlockedUsers",
|
||||||
true};
|
true};
|
||||||
IntSetting showIgnoredUsersMessages = {"/ignore/showIgnoredUsers", 0};
|
IntSetting showBlockedUsersMessages = {"/ignore/showBlockedUsers", 0};
|
||||||
|
|
||||||
/// Moderation
|
/// Moderation
|
||||||
QStringSetting timeoutAction = {"/moderation/timeoutAction", "Disable"};
|
QStringSetting timeoutAction = {"/moderation/timeoutAction", "Disable"};
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "controllers/highlights/HighlightBlacklistUser.hpp"
|
#include "controllers/highlights/HighlightBlacklistUser.hpp"
|
||||||
#include "messages/Message.hpp"
|
#include "messages/Message.hpp"
|
||||||
#include "providers/IvrApi.hpp"
|
#include "providers/IvrApi.hpp"
|
||||||
|
#include "providers/irc/IrcMessageBuilder.hpp"
|
||||||
#include "providers/twitch/TwitchChannel.hpp"
|
#include "providers/twitch/TwitchChannel.hpp"
|
||||||
#include "providers/twitch/api/Helix.hpp"
|
#include "providers/twitch/api/Helix.hpp"
|
||||||
#include "providers/twitch/api/Kraken.hpp"
|
#include "providers/twitch/api/Kraken.hpp"
|
||||||
|
@ -188,7 +189,7 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, QWidget *parent)
|
||||||
user->addStretch(1);
|
user->addStretch(1);
|
||||||
|
|
||||||
user.emplace<QCheckBox>("Follow").assign(&this->ui_.follow);
|
user.emplace<QCheckBox>("Follow").assign(&this->ui_.follow);
|
||||||
user.emplace<QCheckBox>("Ignore").assign(&this->ui_.ignore);
|
user.emplace<QCheckBox>("Block").assign(&this->ui_.block);
|
||||||
user.emplace<QCheckBox>("Ignore highlights")
|
user.emplace<QCheckBox>("Ignore highlights")
|
||||||
.assign(&this->ui_.ignoreHighlights);
|
.assign(&this->ui_.ignoreHighlights);
|
||||||
auto usercard = user.emplace<EffectLabel2>(this);
|
auto usercard = user.emplace<EffectLabel2>(this);
|
||||||
|
@ -414,51 +415,72 @@ void UserInfoPopup::installEvents()
|
||||||
|
|
||||||
std::shared_ptr<bool> ignoreNext = std::make_shared<bool>(false);
|
std::shared_ptr<bool> ignoreNext = std::make_shared<bool>(false);
|
||||||
|
|
||||||
// ignore
|
// block
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
this->ui_.ignore, &QCheckBox::stateChanged,
|
this->ui_.block, &QCheckBox::stateChanged,
|
||||||
[this, ignoreNext, hack](int) mutable {
|
[this](int newState) mutable {
|
||||||
if (*ignoreNext)
|
auto currentUser = getApp()->accounts->twitch.getCurrent();
|
||||||
|
|
||||||
|
const auto reenableBlockCheckbox = [this] {
|
||||||
|
this->ui_.block->setEnabled(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!this->ui_.block->isEnabled())
|
||||||
{
|
{
|
||||||
*ignoreNext = false;
|
reenableBlockCheckbox();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->ui_.ignore->setEnabled(false);
|
switch (newState)
|
||||||
|
{
|
||||||
|
case Qt::CheckState::Unchecked: {
|
||||||
|
this->ui_.block->setEnabled(false);
|
||||||
|
|
||||||
auto currentUser = getApp()->accounts->twitch.getCurrent();
|
getApp()->accounts->twitch.getCurrent()->unblockUser(
|
||||||
if (this->ui_.ignore->isChecked())
|
this->userId_,
|
||||||
{
|
[this, reenableBlockCheckbox, currentUser] {
|
||||||
currentUser->ignoreByID(
|
this->channel_->addMessage(makeSystemMessage(
|
||||||
this->userId_, this->userName_,
|
QString("You successfully unblocked user %1")
|
||||||
[=](auto result, const auto &message) mutable {
|
.arg(this->userName_)));
|
||||||
if (hack.lock())
|
reenableBlockCheckbox();
|
||||||
{
|
},
|
||||||
if (result == IgnoreResult_Failed)
|
[this, reenableBlockCheckbox] {
|
||||||
{
|
this->channel_->addMessage(
|
||||||
*ignoreNext = true;
|
makeSystemMessage(QString(
|
||||||
this->ui_.ignore->setChecked(false);
|
"User %1 couldn't be unblocked, an unknown "
|
||||||
}
|
"error occurred!")));
|
||||||
this->ui_.ignore->setEnabled(true);
|
reenableBlockCheckbox();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
break;
|
||||||
{
|
|
||||||
currentUser->unignoreByID(
|
case Qt::CheckState::PartiallyChecked: {
|
||||||
this->userId_, this->userName_,
|
// We deliberately ignore this state
|
||||||
[=](auto result, const auto &message) mutable {
|
|
||||||
if (hack.lock())
|
|
||||||
{
|
|
||||||
if (result == UnignoreResult_Failed)
|
|
||||||
{
|
|
||||||
*ignoreNext = true;
|
|
||||||
this->ui_.ignore->setChecked(true);
|
|
||||||
}
|
|
||||||
this->ui_.ignore->setEnabled(true);
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Qt::CheckState::Checked: {
|
||||||
|
this->ui_.block->setEnabled(false);
|
||||||
|
|
||||||
|
getApp()->accounts->twitch.getCurrent()->blockUser(
|
||||||
|
this->userId_,
|
||||||
|
[this, reenableBlockCheckbox, currentUser] {
|
||||||
|
this->channel_->addMessage(makeSystemMessage(
|
||||||
|
QString("You successfully blocked user %1")
|
||||||
|
.arg(this->userName_)));
|
||||||
|
reenableBlockCheckbox();
|
||||||
|
},
|
||||||
|
[this, reenableBlockCheckbox] {
|
||||||
|
this->channel_->addMessage(makeSystemMessage(
|
||||||
|
QString(
|
||||||
|
"User %1 couldn't be blocked, an unknown "
|
||||||
|
"error occurred!")
|
||||||
|
.arg(this->userName_)));
|
||||||
|
reenableBlockCheckbox();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ignore highlights
|
// ignore highlights
|
||||||
|
@ -634,9 +656,9 @@ void UserInfoPopup::updateUserData()
|
||||||
|
|
||||||
// get ignore state
|
// get ignore state
|
||||||
bool isIgnoring = false;
|
bool isIgnoring = false;
|
||||||
for (const auto &ignoredUser : currentUser->getIgnores())
|
for (const auto &blockedUser : currentUser->getBlocks())
|
||||||
{
|
{
|
||||||
if (user.id == ignoredUser.id)
|
if (user.id == blockedUser.id)
|
||||||
{
|
{
|
||||||
isIgnoring = true;
|
isIgnoring = true;
|
||||||
break;
|
break;
|
||||||
|
@ -663,8 +685,8 @@ void UserInfoPopup::updateUserData()
|
||||||
{
|
{
|
||||||
this->ui_.ignoreHighlights->setEnabled(true);
|
this->ui_.ignoreHighlights->setEnabled(true);
|
||||||
}
|
}
|
||||||
this->ui_.ignore->setEnabled(true);
|
this->ui_.block->setChecked(isIgnoring);
|
||||||
this->ui_.ignore->setChecked(isIgnoring);
|
this->ui_.block->setEnabled(true);
|
||||||
this->ui_.ignoreHighlights->setChecked(isIgnoringHighlights);
|
this->ui_.ignoreHighlights->setChecked(isIgnoringHighlights);
|
||||||
|
|
||||||
// get followage and subage
|
// get followage and subage
|
||||||
|
@ -711,7 +733,7 @@ void UserInfoPopup::updateUserData()
|
||||||
onUserFetchFailed);
|
onUserFetchFailed);
|
||||||
|
|
||||||
this->ui_.follow->setEnabled(false);
|
this->ui_.follow->setEnabled(false);
|
||||||
this->ui_.ignore->setEnabled(false);
|
this->ui_.block->setEnabled(false);
|
||||||
this->ui_.ignoreHighlights->setEnabled(false);
|
this->ui_.ignoreHighlights->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ private:
|
||||||
Label *subageLabel = nullptr;
|
Label *subageLabel = nullptr;
|
||||||
|
|
||||||
QCheckBox *follow = nullptr;
|
QCheckBox *follow = nullptr;
|
||||||
QCheckBox *ignore = nullptr;
|
QCheckBox *block = nullptr;
|
||||||
QCheckBox *ignoreHighlights = nullptr;
|
QCheckBox *ignoreHighlights = nullptr;
|
||||||
|
|
||||||
Label *noMessagesLabel = nullptr;
|
Label *noMessagesLabel = nullptr;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#define INFO "/ignore <user> in chat ignores a user.\n/unignore <user> in chat unignores a user.\nYou can also click on a user to open the usercard."
|
#define INFO "/block <user> in chat blocks a user.\n/block <user> in chat unblocks a user.\nYou can also click on a user to open the usercard."
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
@ -73,18 +73,18 @@ void addUsersTab(IgnoresPage &page, LayoutCreator<QVBoxLayout> users,
|
||||||
{
|
{
|
||||||
auto label = users.emplace<QLabel>(INFO);
|
auto label = users.emplace<QLabel>(INFO);
|
||||||
label->setWordWrap(true);
|
label->setWordWrap(true);
|
||||||
users.append(page.createCheckBox("Enable twitch ignored users",
|
users.append(page.createCheckBox("Enable twitch blocked users",
|
||||||
getSettings()->enableTwitchIgnoredUsers));
|
getSettings()->enableTwitchBlockedUsers));
|
||||||
|
|
||||||
auto anyways = users.emplace<QHBoxLayout>().withoutMargin();
|
auto anyways = users.emplace<QHBoxLayout>().withoutMargin();
|
||||||
{
|
{
|
||||||
anyways.emplace<QLabel>("Show messages from ignored users anyways:");
|
anyways.emplace<QLabel>("Show messages from blocked users:");
|
||||||
|
|
||||||
auto combo = anyways.emplace<QComboBox>().getElement();
|
auto combo = anyways.emplace<QComboBox>().getElement();
|
||||||
combo->addItems(
|
combo->addItems(
|
||||||
{"Never", "If you are Moderator", "If you are Broadcaster"});
|
{"Never", "If you are Moderator", "If you are Broadcaster"});
|
||||||
|
|
||||||
auto &setting = getSettings()->showIgnoredUsersMessages;
|
auto &setting = getSettings()->showBlockedUsersMessages;
|
||||||
|
|
||||||
setting.connect([combo](const int value) {
|
setting.connect([combo](const int value) {
|
||||||
combo->setCurrentIndex(value);
|
combo->setCurrentIndex(value);
|
||||||
|
@ -102,12 +102,12 @@ void addUsersTab(IgnoresPage &page, LayoutCreator<QVBoxLayout> users,
|
||||||
|
|
||||||
/*auto addremove = users.emplace<QHBoxLayout>().withoutMargin();
|
/*auto addremove = users.emplace<QHBoxLayout>().withoutMargin();
|
||||||
{
|
{
|
||||||
auto add = addremove.emplace<QPushButton>("Ignore user");
|
auto add = addremove.emplace<QPushButton>("Block user");
|
||||||
auto remove = addremove.emplace<QPushButton>("Unignore User");
|
auto remove = addremove.emplace<QPushButton>("Unblock User");
|
||||||
addremove->addStretch(1);
|
addremove->addStretch(1);
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
users.emplace<QLabel>("List of ignored users:");
|
users.emplace<QLabel>("List of blocked users:");
|
||||||
users.emplace<QListView>()->setModel(&userModel);
|
users.emplace<QListView>()->setModel(&userModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,9 +123,9 @@ void IgnoresPage::onShow()
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList users;
|
QStringList users;
|
||||||
for (const auto &ignoredUser : user->getIgnores())
|
for (const auto &blockedUser : user->getBlocks())
|
||||||
{
|
{
|
||||||
users << ignoredUser.name;
|
users << blockedUser.name;
|
||||||
}
|
}
|
||||||
users.sort(Qt::CaseInsensitive);
|
users.sort(Qt::CaseInsensitive);
|
||||||
this->userListModel_.setStringList(users);
|
this->userListModel_.setStringList(users);
|
||||||
|
|
Loading…
Reference in a new issue