diff --git a/CHANGELOG.md b/CHANGELOG.md index ec0728ae9..d190c35b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,7 +70,7 @@ - Minor: Migrated /commercial to Helix API. (#4094) - Minor: Added stream titles to windows live toast notifications. (#1297) - Minor: Make menus and placeholders display appropriate custom key combos. (#4045) -- Minor: Migrated /chatters to Helix API. (#4088, #4097) +- Minor: Migrated /chatters to Helix API. (#4088, #4097, #4114) - Minor: Migrated /mods to Helix API. (#4103) - Minor: Add settings tooltips. (#3437) - Bugfix: Connection to Twitch PubSub now recovers more reliably. (#3643, #3716) diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 3ea04cd68..05ab067ca 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -878,60 +878,109 @@ void CommandController::initialize(Settings &, Paths &paths) return ""; }); - this->registerCommand("/chatters", [](const auto &words, auto channel) { - auto formatError = [](HelixGetChattersError error, QString message) { - using Error = HelixGetChattersError; + auto formatChattersError = [](HelixGetChattersError error, + QString message) { + using Error = HelixGetChattersError; - QString errorMessage = QString("Failed to get chatter count: "); + QString errorMessage = QString("Failed to get chatter count: "); - switch (error) - { - case Error::Forwarded: { - errorMessage += message; - } - break; - - case Error::UserMissingScope: { - errorMessage += "Missing required scope. " - "Re-login with your " - "account and try again."; - } - break; - - case Error::UserNotAuthorized: { - errorMessage += "You must have moderator permissions to " - "use this command."; - } - break; - - case Error::Unknown: { - errorMessage += "An unknown error has occurred."; - } - break; + switch (error) + { + case Error::Forwarded: { + errorMessage += message; } - return errorMessage; - }; + break; - auto twitchChannel = dynamic_cast(channel.get()); + case Error::UserMissingScope: { + errorMessage += "Missing required scope. " + "Re-login with your " + "account and try again."; + } + break; + + case Error::UserNotAuthorized: { + errorMessage += "You must have moderator permissions to " + "use this command."; + } + break; + + case Error::Unknown: { + errorMessage += "An unknown error has occurred."; + } + break; + } + return errorMessage; + }; + + this->registerCommand( + "/chatters", [formatChattersError](const auto &words, auto channel) { + auto *twitchChannel = dynamic_cast(channel.get()); + + if (twitchChannel == nullptr) + { + channel->addMessage(makeSystemMessage( + "The /chatters command only works in Twitch Channels")); + return ""; + } + + // Refresh chatter list via helix api for mods + getHelix()->getChatters( + twitchChannel->roomId(), + getApp()->accounts->twitch.getCurrent()->getUserId(), 1, + [channel](auto result) { + channel->addMessage(makeSystemMessage( + QString("Chatter count: %1") + .arg(localizeNumbers(result.total)))); + }, + [channel, formatChattersError](auto error, auto message) { + auto errorMessage = formatChattersError(error, message); + channel->addMessage(makeSystemMessage(errorMessage)); + }); + + return ""; + }); + + this->registerCommand("/test-chatters", [formatChattersError]( + const auto & /*words*/, + auto channel) { + auto *twitchChannel = dynamic_cast(channel.get()); if (twitchChannel == nullptr) { channel->addMessage(makeSystemMessage( - "The /chatters command only works in Twitch Channels")); + "The /test-chatters command only works in Twitch Channels")); return ""; } - // Refresh chatter list via helix api for mods getHelix()->getChatters( twitchChannel->roomId(), - getApp()->accounts->twitch.getCurrent()->getUserId(), 1, - [channel](auto result) { - channel->addMessage( - makeSystemMessage(QString("Chatter count: %1") - .arg(localizeNumbers(result.total)))); + getApp()->accounts->twitch.getCurrent()->getUserId(), 5000, + [channel, twitchChannel](auto result) { + QStringList entries; + for (const auto &username : result.chatters) + { + entries << username; + } + + QString prefix = "Chatters "; + + if (result.total > 5000) + { + prefix += QString("(5000/%1):").arg(result.total); + } + else + { + prefix += QString("(%1):").arg(result.total); + } + + MessageBuilder builder; + TwitchMessageBuilder::listOfUsersSystemMessage( + prefix, entries, twitchChannel, &builder); + + channel->addMessage(builder.release()); }, - [channel, formatError](auto error, auto message) { - auto errorMessage = formatError(error, message); + [channel, formatChattersError](auto error, auto message) { + auto errorMessage = formatChattersError(error, message); channel->addMessage(makeSystemMessage(errorMessage)); }); diff --git a/src/providers/twitch/api/Helix.cpp b/src/providers/twitch/api/Helix.cpp index a35f5d007..1c6f717f3 100644 --- a/src/providers/twitch/api/Helix.cpp +++ b/src/providers/twitch/api/Helix.cpp @@ -12,6 +12,8 @@ using namespace chatterino; static constexpr auto NUM_MODERATORS_TO_FETCH_PER_REQUEST = 100; +static constexpr auto NUM_CHATTERS_TO_FETCH = 1000; + } // namespace namespace chatterino { @@ -1790,6 +1792,37 @@ void Helix::updateChatSettings( .execute(); } +void Helix::onFetchChattersSuccess( + std::shared_ptr finalChatters, QString broadcasterID, + QString moderatorID, int maxChattersToFetch, + ResultCallback successCallback, + FailureCallback failureCallback, + HelixChatters chatters) +{ + qCDebug(chatterinoTwitch) + << "Fetched" << chatters.chatters.size() << "chatters"; + + finalChatters->chatters.merge(chatters.chatters); + finalChatters->total = chatters.total; + + if (chatters.cursor.isEmpty() || + finalChatters->chatters.size() >= maxChattersToFetch) + { + // Done paginating + successCallback(*finalChatters); + return; + } + + this->fetchChatters( + broadcasterID, moderatorID, NUM_CHATTERS_TO_FETCH, chatters.cursor, + [=](auto chatters) { + this->onFetchChattersSuccess( + finalChatters, broadcasterID, moderatorID, maxChattersToFetch, + successCallback, failureCallback, chatters); + }, + failureCallback); +} + // https://dev.twitch.tv/docs/api/reference#get-chatters void Helix::fetchChatters( QString broadcasterID, QString moderatorID, int first, QString after, @@ -2191,36 +2224,17 @@ void Helix::getChatters( ResultCallback successCallback, FailureCallback failureCallback) { - static const auto NUM_CHATTERS_TO_FETCH = 1000; - auto finalChatters = std::make_shared(); - auto fetchSuccess = [this, broadcasterID, moderatorID, maxChattersToFetch, - finalChatters, successCallback, - failureCallback](auto fs) { - return [=](auto chatters) { - qCDebug(chatterinoTwitch) - << "Fetched" << chatters.chatters.size() << "chatters"; - finalChatters->chatters.merge(chatters.chatters); - finalChatters->total = chatters.total; - - if (chatters.cursor.isEmpty() || - finalChatters->chatters.size() >= maxChattersToFetch) - { - // Done paginating - successCallback(*finalChatters); - return; - } - - this->fetchChatters(broadcasterID, moderatorID, - NUM_CHATTERS_TO_FETCH, chatters.cursor, fs, - failureCallback); - }; - }; - // Initiate the recursive calls - this->fetchChatters(broadcasterID, moderatorID, NUM_CHATTERS_TO_FETCH, "", - fetchSuccess(fetchSuccess), failureCallback); + this->fetchChatters( + broadcasterID, moderatorID, NUM_CHATTERS_TO_FETCH, "", + [=](auto chatters) { + this->onFetchChattersSuccess( + finalChatters, broadcasterID, moderatorID, maxChattersToFetch, + successCallback, failureCallback, chatters); + }, + failureCallback); } // https://dev.twitch.tv/docs/api/reference#get-moderators diff --git a/src/providers/twitch/api/Helix.hpp b/src/providers/twitch/api/Helix.hpp index f056ccd6a..4ee3fee08 100644 --- a/src/providers/twitch/api/Helix.hpp +++ b/src/providers/twitch/api/Helix.hpp @@ -1206,6 +1206,14 @@ protected: FailureCallback failureCallback) final; + // Recursive boy + void onFetchChattersSuccess( + std::shared_ptr finalChatters, QString broadcasterID, + QString moderatorID, int maxChattersToFetch, + ResultCallback successCallback, + FailureCallback failureCallback, + HelixChatters chatters); + // Get chatters list - This method is what actually runs the API request // https://dev.twitch.tv/docs/api/reference#get-chatters void fetchChatters(