#include "points-reward-selection.hpp" #include "twitch-helpers.hpp" #include #include #include namespace advss { void TwitchPointsReward::Load(obs_data_t *obj) { OBSDataAutoRelease data = obs_data_get_obj(obj, "pointsReward"); id = obs_data_get_string(data, "id"); title = obs_data_get_string(data, "title"); } void TwitchPointsReward::Save(obs_data_t *obj) const { OBSDataAutoRelease data = obs_data_create(); obs_data_set_string(data, "id", id.c_str()); obs_data_set_string(data, "title", title.c_str()); obs_data_set_obj(obj, "pointsReward", data); } TwitchPointsRewardSelection::TwitchPointsRewardSelection(QWidget *parent, bool allowAny) : FilterComboBox( parent, obs_module_text( "AdvSceneSwitcher.twitch.selection.points.reward.placeholder")), _allowAny(allowAny) { setDuplicatesEnabled(true); setSizeAdjustPolicy(QComboBox::AdjustToContents); QWidget::connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(SelectionChanged(int))); } void TwitchPointsRewardSelection::SetPointsReward( const TwitchPointsReward &pointsReward) { int index = findData(QString::fromStdString(pointsReward.id)); if (index != -1) { setCurrentIndex(index); return; } setCurrentIndex(-1); } void TwitchPointsRewardSelection::SetChannel(const TwitchChannel &channel) { _channel = channel; PopulateSelection(); } void TwitchPointsRewardSelection::SetToken( const std::weak_ptr &token) { _token = token; if (token.expired()) { DisplayErrorMessage(obs_module_text( "AdvSceneSwitcher.twitch.selection.points.reward.tooltip.noAccount")); return; } PopulateSelection(); } void TwitchPointsRewardSelection::PopulateSelection() { auto token = _token.lock(); if (!token || !token->AnyOptionIsEnabled(_supportedTokenOptions)) { DisplayErrorMessage(obs_module_text( "AdvSceneSwitcher.twitch.selection.points.reward.tooltip.noPermission")); return; } if (!_channel || (*_channel).GetName().empty()) { DisplayErrorMessage(obs_module_text( "AdvSceneSwitcher.twitch.selection.points.reward.tooltip.noChannel")); return; } auto currentSelection = currentText(); const QSignalBlocker b(this); clear(); auto pointsRewards = GetPointsRewardsForChannel(token, *_channel); if (!pointsRewards) { DisplayErrorMessage(obs_module_text( "AdvSceneSwitcher.twitch.selection.points.reward.tooltip.error")); return; } HideErrorMessage(); AddPredefinedItems(); for (const auto &pointsReward : *pointsRewards) { addItem(QString::fromStdString(pointsReward.title), QString::fromStdString(pointsReward.id)); } setCurrentText(currentSelection); } void TwitchPointsRewardSelection::AddPredefinedItems() { if (_allowAny) { addItem(obs_module_text( "AdvSceneSwitcher.twitch.selection.points.reward.option.any"), "-"); } } void TwitchPointsRewardSelection::DisplayErrorMessage(const char *errorMessage) { setDisabled(true); setToolTip(errorMessage); } void TwitchPointsRewardSelection::HideErrorMessage() { setDisabled(false); setToolTip(""); } void TwitchPointsRewardSelection::SelectionChanged(int index) { TwitchPointsReward pointsReward{ itemData(index).toString().toStdString(), currentText().toStdString()}; emit PointsRewardChanged(pointsReward); } TwitchPointsRewardWidget::TwitchPointsRewardWidget(QWidget *parent, bool allowAny) : QWidget(parent), _selection(new TwitchPointsRewardSelection(this, allowAny)), _refreshButton(new QPushButton(this)) { _refreshButton->setMaximumWidth(22); SetButtonIcon(_refreshButton, GetThemeTypeName() == "Light" ? ":res/images/refresh.svg" : "theme:Dark/refresh.svg"); _refreshButton->setToolTip(obs_module_text( "AdvSceneSwitcher.twitch.selection.points.reward.refresh")); auto layout = new QHBoxLayout(); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(_selection); layout->addWidget(_refreshButton); setLayout(layout); QWidget::connect( _selection, SIGNAL(PointsRewardChanged(const TwitchPointsReward &)), this, SIGNAL(PointsRewardChanged(const TwitchPointsReward &))); QWidget::connect(_refreshButton, SIGNAL(clicked()), _selection, SLOT(PopulateSelection())); } void TwitchPointsRewardWidget::SetPointsReward( const TwitchPointsReward &pointsReward) { _selection->SetPointsReward(pointsReward); } void TwitchPointsRewardWidget::SetChannel(const TwitchChannel &channel) { _selection->SetChannel(channel); } void TwitchPointsRewardWidget::SetToken(const std::weak_ptr &token) { _selection->SetToken(token); } static std::vector parseApiResponse(obs_data_t *response) { std::vector pointRewards; OBSDataArrayAutoRelease jsonArray = obs_data_get_array(response, "data"); size_t count = obs_data_array_count(jsonArray); for (size_t i = 0; i < count; ++i) { OBSDataAutoRelease jsonObj = obs_data_array_item(jsonArray, i); std::string id = obs_data_get_string(jsonObj, "id"); std::string title = obs_data_get_string(jsonObj, "title"); pointRewards.push_back({id, title}); } return pointRewards; } std::optional> GetPointsRewardsForChannel(const std::shared_ptr &token, const TwitchChannel &channel) { httplib::Params params = { {"broadcaster_id", channel.GetUserID(*token)}}; auto response = SendGetRequest(*token, "https://api.twitch.tv", "/helix/channel_points/custom_rewards", params, true); if (response.status != 200) { blog(LOG_WARNING, "Failed to fetch points rewards for user %s and channel %s! (%d)", token->GetName().c_str(), channel.GetName().c_str(), response.status); return {}; } return parseApiResponse(response.data); } } // namespace advss