Open ObserverHerb opened 8 months ago
Can I query for the channel's list of redemptions and populate a list in real-time?
This is suspended since I dropped Twitch affiliate.
diff --git a/bot.cpp b/bot.cpp
index 2df34aa..b99ca3b 100644
--- a/bot.cpp
+++ b/bot.cpp
@@ -46,11 +46,12 @@ const char *TWITCH_API_OPERATION_EMOTE_ONLY="emote only";
const char *TWITCH_API_OPERATION_STREAM_TITLE="stream title";
const char *TWITCH_API_OPERATION_STREAM_CATEGORY="stream category";
const char *TWITCH_API_OPERATION_LOAD_BADGES="badges";
-const char *TWITCH_API_OPERATION_SHOUTOUT="shoutout";
+const char *TWITCH_API_OPERATION_FETCH_REDEMPTIONS="redemption list";
const char *TWITCH_API_ERROR_TEMPLATE_INCOMPLETE="Response from requesting %1 was incomplete";
const char *TWITCH_API_ERROR_TEMPLATE_UNKNOWN="Something went wrong obtaining %1";
const char *TWITCH_API_ERROR_TEMPLATE_JSON_PARSE="Error parsing %1 JSON: %2";
const char *TWITCH_API_ERROR_AUTH="Auth token or client ID missing or invalid";
+const char *TWITCH_API_ERROR_SERVER="Twitch choked";
const char16_t *CHAT_BADGE_BROADCASTER=u"broadcaster";
const char16_t *CHAT_BADGE_MODERATOR=u"moderator";
const char16_t *CHAT_TAG_DISPLAY_NAME=u"display-name";
@@ -136,6 +137,11 @@ Bot::Bot(Music::Player &musicPlayer,Security &security,QObject *parent) : QObjec
connect(&vibeKeeper,&Music::Player::Print,this,&Bot::Print);
}
+void Bot::Initialize()
+{
+ FetchRedemptionList();
+}
+
void Bot::DeclareCommand(const Command &&command,NativeCommandFlag flag)
{
commands.insert({{command.Name(),command}});
@@ -606,6 +612,61 @@ void Bot::RestoreMusic()
vibeKeeper.DuckVolume(false);
}
+void Bot::FetchRedemptionList()
+{
+ Network::Request::Send({Twitch::Endpoint(Twitch::ENDPOINT_REDEMPTION_LIST)},Network::Method::GET,[this](QNetworkReply *reply) {
+ switch (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt())
+ {
+ case 400:
+ emit Print(u"Missing information or too many IDs specified"_s,TWITCH_API_OPERATION_FETCH_REDEMPTIONS);
+ return;
+ case 401:
+ emit Print(TWITCH_API_ERROR_AUTH,TWITCH_API_OPERATION_FETCH_REDEMPTIONS);
+ return;
+ case 403:
+ emit Print(u"The broadcaster does not qualify for redemptions"_s,TWITCH_API_OPERATION_FETCH_REDEMPTIONS);
+ return;
+ case 404:
+ emit Print(u"Specified redemptions were not found"_s,TWITCH_API_OPERATION_FETCH_REDEMPTIONS);
+ return;
+ case 500:
+ emit Print(TWITCH_API_ERROR_SERVER,TWITCH_API_OPERATION_FETCH_REDEMPTIONS);
+ return;
+ }
+
+ if (reply->error())
+ {
+ emit Print(u"Failed to obtain list of redemptions for unknown reason"_s,TWITCH_API_OPERATION_FETCH_REDEMPTIONS);
+ return;
+ }
+
+ const JSON::ParseResult parsedJSON=JSON::Parse(reply->readAll());
+ if (!parsedJSON)
+ {
+ emit Print(QString(TWITCH_API_ERROR_TEMPLATE_JSON_PARSE).arg(TWITCH_API_OPERATION_FETCH_REDEMPTIONS,parsedJSON.error));
+ return;
+ }
+
+ const QJsonObject object=parsedJSON().object();
+ auto jsonFieldData=object.find(JSON::Keys::DATA);
+ if (jsonFieldData == object.end()) return;
+ QStringList redemptionNames;
+ for (const QJsonValue &redemptions : jsonFieldData->toArray())
+ {
+ const QJsonObject objectRedemption=redemptions.toObject();
+ auto jsonFieldRedemptionName=objectRedemption.find("title");
+ if (jsonFieldRedemptionName == objectRedemption.end()) continue;
+ redemptionNames.append(jsonFieldRedemptionName->toString());
+ }
+ emit RedemptionList(redemptionNames);
+ },{
+ {QUERY_PARAMETER_BROADCASTER_ID,security.AdministratorID()}
+ },{
+ {NETWORK_HEADER_AUTHORIZATION,security.Bearer(security.OAuthToken())},
+ {NETWORK_HEADER_CLIENT_ID,security.ClientID()}
+ });
+}
+
void Bot::DispatchArrival(const QString &login)
{
if (auto viewer=viewers.find(login); viewer != viewers.end())
@@ -1103,6 +1164,8 @@ void Bot::DispatchShoutout(Command command)
void Bot::DispatchShoutout(const QString &streamer)
{
+ static const char *TWITCH_API_OPERATION_SHOUTOUT="shoutout";
+
Viewer::Remote *profile=new Viewer::Remote(security,streamer);
connect(profile,&Viewer::Remote::Recognized,profile,[this](const Viewer::Local &profile) {
// native Twitch shoutout
diff --git a/bot.h b/bot.h
index d9c4d79..a0b68ed 100644
--- a/bot.h
+++ b/bot.h
@@ -133,6 +133,7 @@ protected:
void AdjustVibeVolume(Command command);
void StreamTitle(const QString &title);
void StreamCategory(const QString &category);
+ void FetchRedemptionList();
signals:
void Print(const QString &message,const QString operation=QString(),const QString subsystem=QString("bot core"));
void ChatMessage(std::shared_ptr<Chat::Message> message);
@@ -160,7 +161,9 @@ signals:
void AnnounceTextWall(const QString &message,const QString &audioPath);
void AnnounceDeniedCommand(const QString &videoPath);
void Welcomed(const QString &user);
+ void RedemptionList(const QStringList &redemptions);
public slots:
+ void Initialize();
void ParseChatMessage(const QString &prefix,const QString &source,const QStringList ¶meters,const QString &message);
void DispatchCommandViaSubsystem(JSON::SignalPayload *response,const QString &name,const QString &login);
void Ping();
diff --git a/main.cpp b/main.cpp
index 89ecd6e..4234385 100644
--- a/main.cpp
+++ b/main.cpp
@@ -250,6 +250,9 @@ int main(int argc,char *argv[])
celeste.connect(&celeste,&Bot::Pulse,&pulsar,QOverload<const QString&,const QString&>::of(&Pulsar::Pulse));
celeste.connect(&celeste,&Bot::Welcomed,&metrics,&UI::Metrics::Dialog::Acknowledged);
celeste.connect(&celeste,&Bot::Panic,&window,&Window::ShowPanicText);
+ celeste.connect(&celeste,&Bot::RedemptionList,&celeste,[](const QStringList &redemptionNames) {
+ UI::Commands::Entry::redemptionNames=redemptionNames;
+ });
celeste.connect(&celeste,&Bot::Panic,&celeste,[&celeste]() {
celeste.disconnect();
});
@@ -300,6 +303,7 @@ int main(int argc,char *argv[])
});
channel->connect(channel,&Channel::Denied,&security,&Security::AuthorizeUser);
security.connect(&security,&Security::Initialized,channel,&Channel::Connect);
+ security.connect(&security,&Security::Initialized,&celeste,&Bot::Initialize);
application.connect(&application,&QApplication::aboutToQuit,[&log,&socket,channel]() {
socket.connect(&socket,&IRCSocket::disconnected,&log,&Log::Archive);
channel->disconnect(); // stops attempting to reconnect by removing all connections to signals
diff --git a/security.cpp b/security.cpp
index 92b65d4..73d2f67 100644
--- a/security.cpp
+++ b/security.cpp
@@ -231,7 +231,7 @@ void Security::ObtainAdministratorProfile()
Viewer::Remote *profile=new Viewer::Remote(*this,settingAdministrator);
connect(profile,&Viewer::Remote::Recognized,profile,[this](Viewer::Local profile) {
administratorID=profile.ID();
- emit Initialize();
+ Initialize();
});
connect(profile,&Viewer::Remote::Unrecognized,this,[this]() {
AuthorizeUser();
diff --git a/security.h b/security.h
index f96024d..71fbd5f 100644
--- a/security.h
+++ b/security.h
@@ -53,6 +53,7 @@ private:
bool tokensInitialized;
QTimer tokenValidationTimer;
bool authorizing;
+ void Initialize();
signals:
void TokenRequestFailed();
void Listening();
@@ -66,5 +67,4 @@ private slots:
void RewireConnected();
void RewireError(QMqttClient::ClientError error);
void RewireMessage(QMqttMessage messasge);
- void Initialize();
};
diff --git a/twitch.h b/twitch.h
index 2692d15..8b65856 100644
--- a/twitch.h
+++ b/twitch.h
@@ -15,6 +15,7 @@ namespace Twitch
inline const char *ENDPOINT_BADGES="chat/badges/global";
inline const char *ENDPOINT_SHOUTOUTS="chat/shoutouts";
inline const char *ENDPOINT_USERS="users";
+ inline const char *ENDPOINT_REDEMPTION_LIST="channel_points/custom_rewards";
inline const char *ENDPOINT_EVENTSUB="eventsub/subscriptions";
inline const char *ENDPOINT_EVENTSUB_SUBSCRIPTIONS="eventsub/subscriptions";
diff --git a/widgets.cpp b/widgets.cpp
index 1a45f06..8785328 100644
--- a/widgets.cpp
+++ b/widgets.cpp
@@ -310,6 +310,8 @@ namespace UI
QDialog::hideEvent(event);
}
+ QStringList Entry::redemptionNames;
+
Entry::Entry(Feedback::Error &errorReport,QWidget *parent) : QWidget(parent),
layout(this),
details(this),
@@ -325,6 +327,7 @@ namespace UI
random(u"Choose Random Media"_s,std::bind_front(&Entry::SetUpRandomCheckBox,this),&details),
duplicates(u"Allow Duplicates"_s,std::bind_front(&Entry::SetUpDuplicatesCheckBox,this),&details),
protect(u"Protect"_s,std::bind_front(&Entry::SetUpProtectCheckBox,this),&details),
+ redemption(u"Redemption"_s,std::bind_front(&Entry::SetUpRedemptionList,this),&details),
message(u"Message"_s,std::bind_front(&Entry::SetUpMessageTextEdit,this),&details),
errorReport(errorReport)
{
@@ -531,6 +534,7 @@ namespace UI
protect.Hide();
duplicates.Hide();
random.Hide();
+ redemption.Hide();
message.Hide();
browse.Hide();
aliases.Hide();
@@ -546,6 +550,7 @@ namespace UI
protect.Show();
duplicates.Show();
random.Show();
+ redemption.Show();
message.Show();
browse.Show();
aliases.Show();
@@ -629,6 +634,14 @@ namespace UI
detailsLayout.addWidget(widget,3,3,1,1);
}
+ void Entry::SetUpRedemptionList(QComboBox *widget)
+ {
+ widget->setEditable(true);
+ widget->lineEdit()->setPlaceholderText("Redemption");
+ widget->addItems(redemptionNames);
+ detailsLayout.addWidget(widget,4,0,1,4);
+ }
+
void Entry::SetUpMessageTextEdit(QTextEdit *widget)
{
widget->setPlaceholderText(u"Message to display in announcement"_s);
@@ -637,7 +650,7 @@ namespace UI
connect(widget,&QTextEdit::textChanged,this,&Entry::ValidateMessage);
connect(widget,&QTextEdit::textChanged,this,&Entry::UpdateMessage);
widget->viewport()->installEventFilter(this);
- detailsLayout.addWidget(widget,4,0,1,4);
+ detailsLayout.addWidget(widget,5,0,1,4);
}
void Entry::SetUpBrowseButton(QPushButton *widget)
diff --git a/widgets.h b/widgets.h
index e1e3f7a..c26967c 100644
--- a/widgets.h
+++ b/widgets.h
@@ -343,6 +343,7 @@ namespace UI
QString Message() const;
bool Protected() const;
void ToggleFold();
+ static QStringList redemptionNames;
protected:
QGridLayout layout;
QFrame details;
@@ -358,6 +359,7 @@ namespace UI
EphemeralWidget<QCheckBox> random;
EphemeralWidget<QCheckBox> duplicates;
EphemeralWidget<QCheckBox> protect;
+ EphemeralWidget<QComboBox> redemption;
EphemeralWidget<QTextEdit> message;
Feedback::Error &errorReport;
void UpdateName();
@@ -380,6 +382,7 @@ namespace UI
void SetUpProtectCheckBox(QCheckBox *widget);
void SetUpRandomCheckBox(QCheckBox *widget);
void SetUpDuplicatesCheckBox(QCheckBox *widget);
+ void SetUpRedemptionList(QComboBox *widget);
void SetUpMessageTextEdit(QTextEdit *widget);
void SetUpBrowseButton(QPushButton *widget);
void SetUpAliasesButton(QPushButton *widget);
It appears I already had a POC for this.
Add a text field that allows entering the redemption name for commands that are triggered by redemptions.