feat: add /endpoll and /cancelpoll commands for broadcasters (#6605)

Reviewed-by: pajlada <rasmus.karlsson@pajlada.com>
This commit is contained in:
iProdigy
2025-11-29 14:12:21 -06:00
committed by GitHub
parent e06021293c
commit b9446990a7
7 changed files with 352 additions and 1 deletions
@@ -484,6 +484,9 @@ CommandController::CommandController(const Paths &paths)
this->registerCommand("/shoutout", &commands::sendShoutout);
this->registerCommand("/poll", &commands::createPoll);
this->registerCommand("/cancelpoll", &commands::cancelPoll);
this->registerCommand("/endpoll", &commands::endPoll);
this->registerCommand("/prediction", &commands::createPrediction);
this->registerCommand("/c2-set-logging-rules", &commands::setLoggingRules);
@@ -67,4 +67,164 @@ QString createPoll(const CommandContext &ctx)
return "";
}
QString endPoll(const CommandContext &ctx)
{
if (ctx.twitchChannel == nullptr)
{
const auto err = QStringLiteral(
"The /endpoll command only works in Twitch channels");
if (ctx.channel != nullptr)
{
ctx.channel->addSystemMessage(err);
}
else
{
qCWarning(chatterinoCommands) << "Invalid command context:" << err;
}
return "";
}
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
if (currentUser->isAnon())
{
ctx.channel->addSystemMessage("You must be logged in to end a poll!");
return "";
}
const auto roomId = ctx.twitchChannel->roomId();
getHelix()->getPolls(
roomId, {}, 1, {},
[channel = ctx.channel, roomId](const auto &result) {
if (result.polls.empty())
{
channel->addSystemMessage("Failed to find any polls");
return;
}
auto poll = result.polls.front();
if (poll.status != "ACTIVE")
{
channel->addSystemMessage("Could not find an active poll");
return;
}
getHelix()->endPoll(
roomId, poll.id, false,
[channel](const HelixPoll &data) {
// find most popular choice
HelixPollChoice winner = data.choices.front();
int totalVotes = 0;
int winnerCount = 0;
for (const auto &choice : data.choices)
{
totalVotes += choice.votes;
if (choice.votes > winner.votes)
{
winner = choice;
winnerCount = 1;
}
else if (choice.votes == winner.votes)
{
winnerCount++;
}
}
if (totalVotes == 0)
{
channel->addSystemMessage(
QString("Poll ended with zero votes: '%1'")
.arg(data.title));
return;
}
if (winnerCount > 1)
{
channel->addSystemMessage(
QString("Poll ended in a draw: '%1'")
.arg(data.title));
return;
}
const double percent =
100.0 * winner.votes / std::max(totalVotes, 1);
channel->addSystemMessage(
QString(
"Ended poll: '%1' - '%2' won with %3 votes (%4%)")
.arg(data.title, winner.title,
QString::number(winner.votes),
QString::number(percent, 'f', 1)));
},
[channel](const auto &error) {
channel->addSystemMessage("Failed to end the poll - " +
error);
});
},
[channel = ctx.channel](const auto &error) {
channel->addSystemMessage("Failed to query polls - " + error);
});
return "";
}
QString cancelPoll(const CommandContext &ctx)
{
if (ctx.twitchChannel == nullptr)
{
const auto err = QStringLiteral(
"The /cancelpoll command only works in Twitch channels");
if (ctx.channel != nullptr)
{
ctx.channel->addSystemMessage(err);
}
else
{
qCWarning(chatterinoCommands) << "Invalid command context:" << err;
}
return "";
}
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
if (currentUser->isAnon())
{
ctx.channel->addSystemMessage(
"You must be logged in to cancel a poll!");
return "";
}
const auto roomId = ctx.twitchChannel->roomId();
getHelix()->getPolls(
roomId, {}, 1, {},
[channel = ctx.channel, roomId](const auto &result) {
if (result.polls.empty())
{
channel->addSystemMessage("Failed to find any polls");
return;
}
auto poll = result.polls.front();
if (poll.status != "ACTIVE")
{
channel->addSystemMessage("Could not find an active poll");
return;
}
getHelix()->endPoll(
roomId, poll.id, true,
[channel](const HelixPoll &data) {
channel->addSystemMessage(
QString("Canceled poll: '%1'").arg(data.title));
},
[channel](const auto &error) {
channel->addSystemMessage("Failed to cancel the poll - " +
error);
});
},
[channel = ctx.channel](const auto &error) {
channel->addSystemMessage("Failed to query polls - " + error);
});
return "";
}
} // namespace chatterino::commands
@@ -13,4 +13,10 @@ namespace chatterino::commands {
/// /poll
QString createPoll(const CommandContext &ctx);
/// /endpoll
QString endPoll(const CommandContext &ctx);
/// /cancelpoll
QString cancelPoll(const CommandContext &ctx);
} // namespace chatterino::commands