Add option to query reward information

This commit is contained in:
WarmUpTill
2025-03-08 19:38:59 +01:00
committed by WarmUpTill
parent 34e125f82d
commit d8807077da
6 changed files with 383 additions and 132 deletions

View File

@@ -1144,10 +1144,13 @@ AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Enable chat's emote-o
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Disable chat's emote-only mode"
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Send chat message"
AdvSceneSwitcher.action.twitch.type.user.getInfo="Get user information"
AdvSceneSwitcher.action.twitch.type.reward.getInfo="Get channel points reward information"
AdvSceneSwitcher.action.twitch.reward.toggleControl="Toggle reward name / variable selection control"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!"
AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="Using account{{account}}{{actions}}on{{channel}}"
AdvSceneSwitcher.action.twitch.entry.user.getInfo="Using account{{account}}{{actions}}for{{userInfoQueryType}}{{userLogin}}{{userId}}"
AdvSceneSwitcher.action.twitch.entry.reward.getInfo="Using account{{account}}{{actions}}for channel{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
AdvSceneSwitcher.action.twitch.title.title="Enter title"
AdvSceneSwitcher.action.twitch.marker.description="Describe marker"
AdvSceneSwitcher.action.twitch.clip.hasDelay="Add a slight delay before capturing the clip"
@@ -1631,22 +1634,37 @@ AdvSceneSwitcher.tempVar.twitch.title.reward="Reward title"
AdvSceneSwitcher.tempVar.twitch.cost.reward="Reward cost"
AdvSceneSwitcher.tempVar.twitch.prompt.reward="Reward description"
AdvSceneSwitcher.tempVar.twitch.is_user_input_required.reward="Reward requires user input"
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward="Redemptions skip request queue"
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward.description="Should redemptions be set to fulfilled status immediately when redeemed and skip the request queue instead of the normal unfulfilled status."
AdvSceneSwitcher.tempVar.twitch.max_per_stream.reward="Reward maximum per stream"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.reward="Reward maximum per user per stream"
AdvSceneSwitcher.tempVar.twitch.background_color.reward="Reward background color"
AdvSceneSwitcher.tempVar.twitch.image.reward="Reward image"
AdvSceneSwitcher.tempVar.twitch.image.reward="Reward image (JSON)"
AdvSceneSwitcher.tempVar.twitch.image.reward.description="Set of custom images of 1x, 2x and 4x sizes for the reward. Can be null if no images have been uploaded."
AdvSceneSwitcher.tempVar.twitch.default_image.reward="Reward default image"
AdvSceneSwitcher.tempVar.twitch.image.url_4x.reward="Reward image URL"
AdvSceneSwitcher.tempVar.twitch.image.url_4x.reward.description="The URL to the custom image for the reward. This field is null if the broadcaster didnt upload images."
AdvSceneSwitcher.tempVar.twitch.default_image.reward="Reward default image (JSON)"
AdvSceneSwitcher.tempVar.twitch.default_image.reward.description="Set of default images of 1x, 2x and 4x sizes for the reward."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward="Reward cooldown"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward.description="Whether a cooldown is enabled and what the cooldown is in seconds."
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward="Reward cooldown expiration"
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward.description="Timestamp of the cooldown expiration. null if the reward isnt on cooldown."
AdvSceneSwitcher.tempVar.twitch.default_image.url_4x.reward="Reward default image URL"
AdvSceneSwitcher.tempVar.twitch.default_image.url_4x.reward.description="The URL to the default image for the reward."
AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward="Redemption count"
AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward.description="The number of redemptions redeemed during the current live stream. Counts against the max_per_stream limit. null if the broadcasters stream isnt live or max_per_stream isnt enabled."
AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward.description="The number of redemptions redeemed during the current live stream. Counts against the max_per_stream limit. null if the broadcasters stream isnt live or max_per_stream isnt enabled."
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward="Redemptions skip request queue"
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward.description="Should redemptions be set to fulfilled status immediately when redeemed and skip the request queue instead of the normal unfulfilled status."
AdvSceneSwitcher.tempVar.twitch.max_per_stream.reward="Maximum redemptions per stream (JSON)"
AdvSceneSwitcher.tempVar.twitch.max_per_stream.is_enabled.reward="Maximum redemptions per stream enabled"
AdvSceneSwitcher.tempVar.twitch.max_per_stream.is_enabled.reward.description="A Boolean value that determines whether the reward applies a limit on the number of redemptions allowed per live stream. Is true if the reward applies a limit."
AdvSceneSwitcher.tempVar.twitch.max_per_stream.max_per_stream.reward="Maximum redemptions per stream"
AdvSceneSwitcher.tempVar.twitch.max_per_stream.max_per_stream.reward.description="The maximum number of redemptions allowed per live stream."
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.reward="Reward maximum per user per stream (JSON)"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.is_enabled.reward="Maximum redemptions per user per stream enabled"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.is_enabled.reward.description="A Boolean value that determines whether the reward applies a limit on the number of redemptions allowed per user per live stream. Is true if the reward applies a limit."
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.max_per_user_per_stream.reward="Maximum redemptions per user per stream"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.max_per_user_per_stream.reward.description="The settings used to determine whether to apply a maximum to the number of redemptions allowed per user per live stream."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward="Global cooldown (JSON)"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward.description="Whether a cooldown is enabled and what the cooldown is in seconds."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.is_enabled.reward="Global cooldown enabled"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.is_enabled.reward.description="A Boolean value that determines whether to apply a cooldown period. Is true if a cooldown period is enabled."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.global_cooldown_seconds.reward="Global cooldown"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.global_cooldown_seconds.reward.description="The cooldown period, in seconds."
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward="Reward cooldown expiration"
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward.description="Timestamp of the cooldown expiration. null if the reward isnt on cooldown."
AdvSceneSwitcher.tempVar.twitch.charity_name="Charity name"
AdvSceneSwitcher.tempVar.twitch.charity_description="Charity description"

View File

@@ -1072,7 +1072,7 @@ AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Habilitar modo apenas
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Desabilitar modo apenas emotes no chat"
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Enviar mensagem de chat"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Não é possível selecionar categoria sem selecionar uma conta Twitch primeiro!"
AdvSceneSwitcher.action.twitch.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
AdvSceneSwitcher.action.twitch.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="Usando conta{{account}}{{actions}}em{{channel}}"
AdvSceneSwitcher.action.twitch.title.title="Digite o título"
AdvSceneSwitcher.action.twitch.marker.description="Descreva o marcador"

View File

@@ -3,6 +3,7 @@
#include <log-helper.hpp>
#include <layout-helpers.hpp>
#include <ui-helpers.hpp>
namespace advss {
@@ -23,6 +24,16 @@ void MacroActionTwitch::ResolveVariablesToFixedValues()
_chatMessage.ResolveVariables();
_userLogin.ResolveVariables();
_userId.ResolveVariables();
_useVariableForRewardSelection = false;
auto token = _token.lock();
if (token) {
ResolveVariableSelectionToRewardId(token);
_pointsReward.title = _lastResolvedRewardTitle;
_pointsReward.id = _lastResolvedRewardId;
} else {
_pointsReward.id = "";
_pointsReward.id = "";
}
}
std::shared_ptr<MacroAction> MacroActionTwitch::Create(Macro *m)
@@ -63,6 +74,8 @@ const static std::map<MacroActionTwitch::Action, std::string> actionTypes = {
"AdvSceneSwitcher.action.twitch.type.chat.sendMessage"},
{MacroActionTwitch::Action::USER_GET_INFO,
"AdvSceneSwitcher.action.twitch.type.user.getInfo"},
{MacroActionTwitch::Action::POINTS_REWARD_GET_INFO,
"AdvSceneSwitcher.action.twitch.type.reward.getInfo"},
};
const static std::map<MacroActionTwitch::AnnouncementColor, std::string>
@@ -282,7 +295,7 @@ void MacroActionTwitch::GetUserInfo(const std::shared_ptr<TwitchToken> &token)
break;
}
auto result = SendGetRequest(*token, "https://api.twitch.tv",
"/helix/users", params);
"/helix/users", params, true);
if (result.status != 200) {
blog(LOG_INFO, "Failed get user info! (%d)\n", result.status);
@@ -297,80 +310,196 @@ void MacroActionTwitch::GetUserInfo(const std::shared_ptr<TwitchToken> &token)
}
OBSDataAutoRelease data = obs_data_array_item(array, 0);
SetJsonTempVars(data, [this](const char *id, const char *value) {
SetTempVarValue(id, value);
});
}
SetTempVarValue("id", obs_data_get_string(data, "id"));
SetTempVarValue("login", obs_data_get_string(data, "login"));
SetTempVarValue("display_name",
obs_data_get_string(data, "display_name"));
SetTempVarValue("type", obs_data_get_string(data, "type"));
SetTempVarValue("broadcaster_type",
obs_data_get_string(data, "broadcaster_type"));
SetTempVarValue("description",
obs_data_get_string(data, "description"));
SetTempVarValue("profile_image_url",
obs_data_get_string(data, "profile_image_url"));
SetTempVarValue("offline_image_url",
obs_data_get_string(data, "offline_image_url"));
SetTempVarValue("created_at", obs_data_get_string(data, "created_at"));
bool MacroActionTwitch::ResolveVariableSelectionToRewardId(
const std::shared_ptr<TwitchToken> &token)
{
if (!token) {
return false;
}
auto variable = _rewardVariable.lock();
if (!variable) {
return false;
}
const auto rewardTitle = variable->Value();
if (rewardTitle.empty()) {
return false;
}
if (_lastResolvedRewardTitle == rewardTitle) {
return true;
}
const auto rewards = GetPointsRewardsForChannel(token, _channel);
if (!rewards) {
return false;
}
for (const auto &reward : *rewards) {
if (reward.title == rewardTitle) {
_lastResolvedRewardId = reward.id;
_lastResolvedRewardTitle = reward.title;
return true;
}
}
return false;
}
void MacroActionTwitch::GetRewardInfo(const std::shared_ptr<TwitchToken> &token)
{
if (_useVariableForRewardSelection &&
!ResolveVariableSelectionToRewardId(token)) {
if (VerboseLoggingEnabled()) {
auto variable = _rewardVariable.lock();
if (!variable) {
blog(LOG_WARNING,
"failed to resolve variable reward name to reward id (invalid selection)");
return;
}
blog(LOG_WARNING,
"failed to resolve variable reward name '%s' to reward id",
variable->Value().c_str());
return;
}
}
httplib::Params params = {
{"broadcaster_id", token->GetUserID()},
{"id", _useVariableForRewardSelection ? _lastResolvedRewardId
: _pointsReward.id},
};
auto result = SendGetRequest(*token, "https://api.twitch.tv",
"/helix/channel_points/custom_rewards",
params, true);
if (result.status != 200) {
blog(LOG_INFO, "Failed get reward info! (%d)\n", result.status);
return;
}
OBSDataArrayAutoRelease array = obs_data_get_array(result.data, "data");
size_t count = obs_data_array_count(array);
if (count == 0) {
blog(LOG_WARNING, "%s did not return any data!", __func__);
return;
}
OBSDataAutoRelease data = obs_data_array_item(array, 0);
SetJsonTempVars(data, [this](const char *id, const char *value) {
SetTempVarValue(id, value);
});
OBSDataAutoRelease image = obs_data_get_obj(data, "image");
SetTempVarValue("image.url_4x", obs_data_get_string(image, "url_4x"));
OBSDataAutoRelease default_image =
obs_data_get_obj(data, "default_image");
SetTempVarValue("default_image.url_4x",
obs_data_get_string(default_image, "url_4x"));
OBSDataAutoRelease max_per_stream_setting =
obs_data_get_obj(data, "max_per_stream_setting");
SetTempVarValue("max_per_stream.is_enabled",
obs_data_get_bool(max_per_stream_setting,
"is_enabled"));
SetTempVarValue("max_per_stream.max_per_stream",
std::to_string(obs_data_get_int(max_per_stream_setting,
"max_per_stream")));
OBSDataAutoRelease max_per_user_per_stream_setting =
obs_data_get_obj(data, "max_per_user_per_stream_setting");
SetTempVarValue("max_per_user_per_stream.is_enabled",
obs_data_get_bool(max_per_user_per_stream_setting,
"is_enabled"));
SetTempVarValue(
"max_per_user_per_stream.max_per_user_per_stream",
std::to_string(obs_data_get_int(max_per_user_per_stream_setting,
"max_per_user_per_stream")));
OBSDataAutoRelease global_cooldown_setting =
obs_data_get_obj(data, "global_cooldown_setting");
SetTempVarValue("global_cooldown.is_enabled",
obs_data_get_bool(global_cooldown_setting,
"is_enabled"));
SetTempVarValue(
"max_per_user_per_stream.global_cooldown_seconds",
std::to_string(obs_data_get_int(max_per_user_per_stream_setting,
"global_cooldown_seconds")));
}
static std::string tryTranslate(const std::string &testString)
{
auto translated = obs_module_text(testString.c_str());
if (testString != translated) {
return translated;
}
return "";
}
void MacroActionTwitch::SetupTempVars()
{
MacroAction::SetupTempVars();
if (_action != Action::USER_GET_INFO) {
return;
}
AddTempvar(
"id",
obs_module_text(
"AdvSceneSwitcher.tempVar.twitch.id.user.getInfo"),
obs_module_text(
"AdvSceneSwitcher.tempVar.twitch.id.user.getInfo.description"));
AddTempvar(
"login",
obs_module_text(
"AdvSceneSwitcher.tempVar.twitch.login.user.getInfo"),
obs_module_text(
"AdvSceneSwitcher.tempVar.twitch.login.user.getInfo.description"));
AddTempvar(
"display_name",
obs_module_text(
"AdvSceneSwitcher.tempVar.twitch.display_name.user.getInfo"),
obs_module_text(
"AdvSceneSwitcher.tempVar.twitch.display_name.user.getInfo.description"));
AddTempvar(
"type",
obs_module_text(
"AdvSceneSwitcher.tempVar.twitch.type.user.getInfo"),
obs_module_text(
"AdvSceneSwitcher.tempVar.twitch.type.user.getInfo.description"));
AddTempvar(
"broadcaster_type",
obs_module_text(
"AdvSceneSwitcher.tempVar.twitch.broadcaster_type.user.getInfo"),
obs_module_text(
"AdvSceneSwitcher.tempVar.twitch.broadcaster_type.user.getInfo.description"));
AddTempvar(
"description",
obs_module_text(
"AdvSceneSwitcher.tempVar.twitch.description.user.getInfo"),
obs_module_text(
"AdvSceneSwitcher.tempVar.twitch.description.user.getInfo.description"));
AddTempvar(
"profile_image_url",
obs_module_text(
"AdvSceneSwitcher.tempVar.twitch.profile_image_url.user.getInfo"));
AddTempvar(
"offline_image_url",
obs_module_text(
"AdvSceneSwitcher.tempVar.twitch.offline_image_url.user.getInfo"));
AddTempvar(
"created_at",
obs_module_text(
"AdvSceneSwitcher.tempVar.twitch.created_at.user.getInfo"),
obs_module_text(
"AdvSceneSwitcher.tempVar.twitch.created_at.user.getInfo.description"));
auto setupTempVarHelper = [&](const std::string &id,
const std::string &extra = "") {
std::string name = tryTranslate(
"AdvSceneSwitcher.tempVar.twitch." + id + extra);
std::string description =
tryTranslate("AdvSceneSwitcher.tempVar.twitch." + id +
extra + ".description");
AddTempvar(id, name.empty() ? id : name, description);
};
switch (_action) {
case Action::POINTS_REWARD_GET_INFO:
setupTempVarHelper("title", ".reward");
setupTempVarHelper("prompt", ".reward");
setupTempVarHelper("cost", ".reward");
setupTempVarHelper("background_color", ".reward");
setupTempVarHelper("is_enabled", ".reward");
setupTempVarHelper("is_user_input_required", ".reward");
setupTempVarHelper("is_paused", ".reward");
setupTempVarHelper("is_in_stock", ".reward");
setupTempVarHelper("should_redemptions_skip_request_queue",
".reward");
setupTempVarHelper("redemptions_redeemed_current_stream",
".reward");
setupTempVarHelper("cooldown_expires_at", ".reward");
setupTempVarHelper("max_per_stream.is_enabled", ".reward");
setupTempVarHelper("max_per_stream.max_per_stream", ".reward");
setupTempVarHelper("max_per_user_per_stream.is_enabled",
".reward");
setupTempVarHelper(
"max_per_user_per_stream.max_per_user_per_stream",
".reward");
setupTempVarHelper("global_cooldown.is_enabled", ".reward");
setupTempVarHelper("global_cooldown.global_cooldown_seconds",
".reward");
setupTempVarHelper("image.url_4x", ".reward");
setupTempVarHelper("default_image.url_4x", ".reward");
break;
case Action::USER_GET_INFO:
setupTempVarHelper("id", ".user.getInfo");
setupTempVarHelper("login", ".user.getInfo");
setupTempVarHelper("display_name", ".user.getInfo");
setupTempVarHelper("type", ".user.getInfo");
setupTempVarHelper("broadcaster_type", ".user.getInfo");
setupTempVarHelper("description", ".user.getInfo");
setupTempVarHelper("profile_image_url", ".user.getInfo");
setupTempVarHelper("offline_image_url", ".user.getInfo");
setupTempVarHelper("created_at", ".user.getInfo");
break;
default:
break;
}
}
bool MacroActionTwitch::PerformAction()
@@ -414,6 +543,9 @@ bool MacroActionTwitch::PerformAction()
case MacroActionTwitch::Action::USER_GET_INFO:
GetUserInfo(token);
break;
case MacroActionTwitch::Action::POINTS_REWARD_GET_INFO:
GetRewardInfo(token);
break;
default:
break;
}
@@ -455,6 +587,11 @@ bool MacroActionTwitch::Save(obs_data_t *obj) const
static_cast<int>(_userInfoQueryType));
_userLogin.Save(obj, "userLogin");
_userId.Save(obj, "userId");
_pointsReward.Save(obj);
obs_data_set_string(obj, "rewardVariable",
GetWeakVariableName(_rewardVariable).c_str());
obs_data_set_bool(obj, "useVariableForRewardSelection",
_useVariableForRewardSelection);
return true;
}
@@ -478,6 +615,12 @@ bool MacroActionTwitch::Load(obs_data_t *obj)
obs_data_get_int(obj, "userInfoQueryType"));
_userLogin.Load(obj, "userLogin");
_userId.Load(obj, "userId");
_pointsReward.Load(obj);
_rewardVariable = GetWeakVariableByName(
obs_data_get_string(obj, "rewardVariable"));
_useVariableForRewardSelection =
obs_data_get_bool(obj, "useVariableForRewardSelection");
SetAction(static_cast<Action>(obs_data_get_int(obj, "action")));
return true;
@@ -602,7 +745,9 @@ bool MacroActionTwitch::ActionIsSupportedByToken()
{Action::WHISPER_SEND, {{"user:manage:whispers"}}},
{Action::SEND_CHAT_MESSAGE, {{"chat:edit"}}},
{Action::USER_GET_INFO, {}},
};
{Action::POINTS_REWARD_GET_INFO,
{{"channel:read:redemptions"},
{"channel:manage:redemptions"}}}};
auto token = _token.lock();
if (!token) {
@@ -679,7 +824,10 @@ MacroActionTwitchEdit::MacroActionTwitchEdit(
_chatMessage(new VariableTextEdit(this)),
_userInfoQueryType(new QComboBox(this)),
_userLogin(new VariableLineEdit(this)),
_userId(new VariableSpinBox(this))
_userId(new VariableSpinBox(this)),
_pointsReward(new TwitchPointsRewardWidget(this)),
_rewardVariable(new VariableSelection(this)),
_toggleRewardSelection(new QPushButton())
{
SetWidgetProperties();
SetWidgetSignalConnections();
@@ -726,6 +874,7 @@ void MacroActionTwitchEdit::TwitchTokenChanged(const QString &token)
_entryData->_token = GetWeakTwitchTokenByQString(token);
_category->SetToken(_entryData->_token);
_channel->SetToken(_entryData->_token);
_pointsReward->SetToken(_entryData->_token);
_entryData->ResetChatConnection();
SetWidgetVisibility();
@@ -835,6 +984,15 @@ void MacroActionTwitchEdit::SetWidgetProperties()
populateUserQueryInfoTypeSelection(_userInfoQueryType);
_userId->setMaximum(999999999);
_toggleRewardSelection->setCheckable(true);
_toggleRewardSelection->setMaximumWidth(11);
SetButtonIcon(_toggleRewardSelection,
GetThemeTypeName() == "Light"
? ":/res/images/dots-vert.svg"
: "theme:Dark/dots-vert.svg");
_toggleRewardSelection->setToolTip(obs_module_text(
"AdvSceneSwitcher.action.twitch.reward.toggleControl"));
}
void MacroActionTwitchEdit::SetWidgetSignalConnections()
@@ -873,6 +1031,15 @@ void MacroActionTwitchEdit::SetWidgetSignalConnections()
_userId,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(UserIdChanged(const NumberVariable<int> &)));
QWidget::connect(
_pointsReward,
SIGNAL(PointsRewardChanged(const TwitchPointsReward &)), this,
SLOT(PointsRewardChanged(const TwitchPointsReward &)));
QWidget::connect(_rewardVariable,
SIGNAL(SelectionChanged(const QString &)), this,
SLOT(RewardVariableChanged(const QString &)));
QWidget::connect(_toggleRewardSelection, SIGNAL(toggled(bool)), this,
SLOT(ToggleRewardSelection(bool)));
}
void MacroActionTwitchEdit::SetWidgetVisibility()
@@ -889,7 +1056,9 @@ void MacroActionTwitchEdit::SetWidgetVisibility()
_entryData->GetAction() ==
MacroActionTwitch::Action::RAID_END ||
_entryData->GetAction() ==
MacroActionTwitch::Action::SEND_CHAT_MESSAGE);
MacroActionTwitch::Action::SEND_CHAT_MESSAGE ||
_entryData->GetAction() ==
MacroActionTwitch::Action::POINTS_REWARD_GET_INFO);
_duration->setVisible(_entryData->GetAction() ==
MacroActionTwitch::Action::COMMERCIAL_START);
_markerDescription->setVisible(
@@ -917,6 +1086,17 @@ void MacroActionTwitchEdit::SetWidgetVisibility()
MacroActionTwitch::Action::USER_GET_INFO &&
_entryData->_userInfoQueryType ==
MacroActionTwitch::UserInfoQueryType::ID);
_pointsReward->setVisible(
_entryData->GetAction() ==
MacroActionTwitch::Action::POINTS_REWARD_GET_INFO &&
!_entryData->_useVariableForRewardSelection);
_rewardVariable->setVisible(
_entryData->GetAction() ==
MacroActionTwitch::Action::POINTS_REWARD_GET_INFO &&
_entryData->_useVariableForRewardSelection);
_toggleRewardSelection->setVisible(
_entryData->GetAction() ==
MacroActionTwitch::Action::POINTS_REWARD_GET_INFO);
if (_entryData->GetAction() ==
MacroActionTwitch::Action::CHANNEL_INFO_TITLE_SET ||
@@ -937,6 +1117,7 @@ void MacroActionTwitchEdit::ChannelChanged(const TwitchChannel &channel)
{
GUARD_LOADING_AND_LOCK();
_entryData->_channel = channel;
_pointsReward->SetChannel(channel);
_entryData->ResetChatConnection();
}
@@ -964,12 +1145,32 @@ void MacroActionTwitchEdit::UserLoginChanged()
_entryData->_userLogin = _userLogin->text().toStdString();
}
void MacroActionTwitchEdit::RewardVariableChanged(const QString &text)
{
GUARD_LOADING_AND_LOCK()
_entryData->_rewardVariable = GetWeakVariableByQString(text);
}
void MacroActionTwitchEdit::ToggleRewardSelection(bool)
{
GUARD_LOADING_AND_LOCK();
_entryData->_useVariableForRewardSelection =
!_entryData->_useVariableForRewardSelection;
SetWidgetVisibility();
}
void MacroActionTwitchEdit::UserIdChanged(const NumberVariable<int> &value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_userId = value;
}
void MacroActionTwitchEdit::PointsRewardChanged(const TwitchPointsReward &reward)
{
GUARD_LOADING_AND_LOCK();
_entryData->_pointsReward = reward;
}
void MacroActionTwitchEdit::SetWidgetLayout()
{
const std::vector<QWidget *> widgets{_tokens,
@@ -983,7 +1184,10 @@ void MacroActionTwitchEdit::SetWidgetLayout()
_channel,
_userInfoQueryType,
_userLogin,
_userId};
_userId,
_pointsReward,
_rewardVariable,
_toggleRewardSelection};
for (auto widget : widgets) {
_layout->removeWidget(widget);
}
@@ -999,6 +1203,10 @@ void MacroActionTwitchEdit::SetWidgetLayout()
layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.entry.user.getInfo");
break;
case MacroActionTwitch::Action::POINTS_REWARD_GET_INFO:
layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.entry.reward.getInfo");
break;
default:
layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.entry.default");
@@ -1017,7 +1225,10 @@ void MacroActionTwitchEdit::SetWidgetLayout()
{"{{channel}}", _channel},
{"{{userInfoQueryType}}", _userInfoQueryType},
{"{{userLogin}}", _userLogin},
{"{{userId}}", _userId}});
{"{{userId}}", _userId},
{"{{pointsReward}}", _pointsReward},
{"{{rewardVariable}}", _rewardVariable},
{"{{toggleRewardSelection}}", _toggleRewardSelection}});
_layout->setContentsMargins(0, 0, 0, 0);
}
@@ -1047,6 +1258,10 @@ void MacroActionTwitchEdit::UpdateEntryData()
static_cast<int>(_entryData->_userInfoQueryType)));
_userLogin->setText(_entryData->_userLogin);
_userId->SetValue(_entryData->_userId);
_pointsReward->SetToken(_entryData->_token);
_pointsReward->SetChannel(_entryData->_channel);
_pointsReward->SetPointsReward(_entryData->_pointsReward);
_rewardVariable->SetVariable(_entryData->_rewardVariable);
SetWidgetVisibility();
}

View File

@@ -54,6 +54,9 @@ public:
// Reward add
POINTS_REWARD_ADD = 600, // TODO
// Get reward information
POINTS_REWARD_GET_INFO = 650,
// Reward update
POINTS_REWARD_ENABLE = 710,
POINTS_REWARD_DISABLE = 711,
@@ -177,6 +180,9 @@ public:
UserInfoQueryType _userInfoQueryType = UserInfoQueryType::LOGIN;
StringVariable _userLogin = "user login";
IntVariable _userId = 0;
TwitchPointsReward _pointsReward;
std::weak_ptr<Variable> _rewardVariable;
bool _useVariableForRewardSelection = false;
private:
void SetStreamTitle(const std::shared_ptr<TwitchToken> &) const;
@@ -190,12 +196,19 @@ private:
void StartRaid(const std::shared_ptr<TwitchToken> &);
void SendChatMessage(const std::shared_ptr<TwitchToken> &);
void GetUserInfo(const std::shared_ptr<TwitchToken> &);
void GetRewardInfo(const std::shared_ptr<TwitchToken> &);
bool ResolveVariableSelectionToRewardId(
const std::shared_ptr<TwitchToken> &);
void SetupTempVars();
Action _action = Action::CHANNEL_INFO_TITLE_SET;
std::shared_ptr<TwitchChatConnection> _chatConnection;
std::string _lastResolvedRewardTitle;
std::string _lastResolvedRewardId;
static bool _registered;
static const std::string id;
};
@@ -232,6 +245,9 @@ private slots:
void UserInfoQueryTypeChanged(int);
void UserLoginChanged();
void UserIdChanged(const NumberVariable<int> &);
void PointsRewardChanged(const TwitchPointsReward &);
void RewardVariableChanged(const QString &);
void ToggleRewardSelection(bool);
signals:
void HeaderInfoChanged(const QString &);
@@ -263,6 +279,9 @@ private:
QComboBox *_userInfoQueryType;
VariableLineEdit *_userLogin;
VariableSpinBox *_userId;
TwitchPointsRewardWidget *_pointsReward;
VariableSelection *_rewardVariable;
QPushButton *_toggleRewardSelection;
bool _loading = true;
};

View File

@@ -88,7 +88,7 @@ void TwitchPointsRewardSelection::PopulateSelection()
const QSignalBlocker b(this);
clear();
auto pointsRewards = GetPointsRewards(token, *_channel);
auto pointsRewards = GetPointsRewardsForChannel(token, *_channel);
if (!pointsRewards) {
DisplayErrorMessage(obs_module_text(
"AdvSceneSwitcher.twitch.selection.points.reward.tooltip.error"));
@@ -127,47 +127,6 @@ void TwitchPointsRewardSelection::HideErrorMessage()
setToolTip("");
}
std::optional<std::vector<TwitchPointsReward>>
TwitchPointsRewardSelection::GetPointsRewards(
const std::shared_ptr<TwitchToken> &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 ParseResponse(response.data);
}
std::vector<TwitchPointsReward>
TwitchPointsRewardSelection::ParseResponse(obs_data_t *response)
{
std::vector<TwitchPointsReward> 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;
}
void TwitchPointsRewardSelection::SelectionChanged(int index)
{
TwitchPointsReward pointsReward{
@@ -218,4 +177,44 @@ void TwitchPointsRewardWidget::SetToken(const std::weak_ptr<TwitchToken> &token)
_selection->SetToken(token);
}
static std::vector<TwitchPointsReward> parseApiResponse(obs_data_t *response)
{
std::vector<TwitchPointsReward> 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<std::vector<TwitchPointsReward>>
GetPointsRewardsForChannel(const std::shared_ptr<TwitchToken> &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

View File

@@ -35,10 +35,6 @@ protected:
private:
void AddPredefinedItems();
std::optional<std::vector<TwitchPointsReward>>
GetPointsRewards(const std::shared_ptr<TwitchToken> &token,
const TwitchChannel &channel);
std::vector<TwitchPointsReward> ParseResponse(obs_data_t *response);
bool _allowAny;
const std::vector<TokenOption> _supportedTokenOptions = {
@@ -71,4 +67,8 @@ protected:
QPushButton *_refreshButton;
};
std::optional<std::vector<TwitchPointsReward>>
GetPointsRewardsForChannel(const std::shared_ptr<TwitchToken> &token,
const TwitchChannel &channel);
} // namespace advss