SceneSwitcher/plugins/twitch/macro-action-twitch.cpp

871 lines
26 KiB
C++

#include "macro-action-twitch.hpp"
#include "twitch-helpers.hpp"
#include <log-helper.hpp>
#include <layout-helpers.hpp>
namespace advss {
const std::string MacroActionTwitch::id = "twitch";
bool MacroActionTwitch::_registered = MacroActionFactory::Register(
MacroActionTwitch::id,
{MacroActionTwitch::Create, MacroActionTwitchEdit::Create,
"AdvSceneSwitcher.action.twitch"});
void MacroActionTwitch::ResolveVariablesToFixedValues()
{
_streamTitle.ResolveVariables();
_markerDescription.ResolveVariables();
_duration.ResolveVariables();
_announcementMessage.ResolveVariables();
_channel.ResolveVariables();
_chatMessage.ResolveVariables();
}
std::shared_ptr<MacroAction> MacroActionTwitch::Create(Macro *m)
{
return std::make_shared<MacroActionTwitch>(m);
}
std::shared_ptr<MacroAction> MacroActionTwitch::Copy() const
{
return std::make_shared<MacroActionTwitch>(*this);
}
std::string MacroActionTwitch::GetShortDesc() const
{
return GetWeakTwitchTokenName(_token);
}
const static std::map<MacroActionTwitch::Action, std::string> actionTypes = {
{MacroActionTwitch::Action::CHANNEL_INFO_TITLE_SET,
"AdvSceneSwitcher.action.twitch.type.channel.info.title.set"},
{MacroActionTwitch::Action::CHANNEL_INFO_CATEGORY_SET,
"AdvSceneSwitcher.action.twitch.type.channel.info.category.set"},
{MacroActionTwitch::Action::RAID_START,
"AdvSceneSwitcher.action.twitch.type.raid.start"},
{MacroActionTwitch::Action::COMMERCIAL_START,
"AdvSceneSwitcher.action.twitch.type.commercial.start"},
{MacroActionTwitch::Action::MARKER_CREATE,
"AdvSceneSwitcher.action.twitch.type.marker.create"},
{MacroActionTwitch::Action::CLIP_CREATE,
"AdvSceneSwitcher.action.twitch.type.clip.create"},
{MacroActionTwitch::Action::CHAT_ANNOUNCEMENT_SEND,
"AdvSceneSwitcher.action.twitch.type.chat.announcement.send"},
{MacroActionTwitch::Action::CHAT_EMOTE_ONLY_ENABLE,
"AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable"},
{MacroActionTwitch::Action::CHAT_EMOTE_ONLY_DISABLE,
"AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable"},
{MacroActionTwitch::Action::SEND_CHAT_MESSAGE,
"AdvSceneSwitcher.action.twitch.type.sendChatMessage"},
};
const static std::map<MacroActionTwitch::AnnouncementColor, std::string>
announcementColors = {
{MacroActionTwitch::AnnouncementColor::PRIMARY,
"AdvSceneSwitcher.action.twitch.announcement.primary"},
{MacroActionTwitch::AnnouncementColor::BLUE,
"AdvSceneSwitcher.action.twitch.announcement.blue"},
{MacroActionTwitch::AnnouncementColor::GREEN,
"AdvSceneSwitcher.action.twitch.announcement.green"},
{MacroActionTwitch::AnnouncementColor::ORANGE,
"AdvSceneSwitcher.action.twitch.announcement.orange"},
{MacroActionTwitch::AnnouncementColor::PURPLE,
"AdvSceneSwitcher.action.twitch.announcement.purple"},
};
const static std::map<MacroActionTwitch::AnnouncementColor, std::string>
announcementColorsTwitch = {
{MacroActionTwitch::AnnouncementColor::PRIMARY, "primary"},
{MacroActionTwitch::AnnouncementColor::BLUE, "blue"},
{MacroActionTwitch::AnnouncementColor::GREEN, "green"},
{MacroActionTwitch::AnnouncementColor::ORANGE, "orange"},
{MacroActionTwitch::AnnouncementColor::PURPLE, "purple"},
};
const static std::map<MacroActionTwitch::RedemptionStatus, std::string>
redemptionStatusesTwitch = {
{MacroActionTwitch::RedemptionStatus::CANCELED, "CANCELED"},
{MacroActionTwitch::RedemptionStatus::FULFILLED, "FULFILLED"},
};
void MacroActionTwitch::SetStreamTitle(
const std::shared_ptr<TwitchToken> &token) const
{
if (std::string(_streamTitle).empty()) {
return;
}
OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "title", _streamTitle.c_str());
auto result = SendPatchRequest(*token, "https://api.twitch.tv",
"/helix/channels",
{{"broadcaster_id", token->GetUserID()}},
data.Get());
if (result.status != 204) {
blog(LOG_INFO, "Failed to set stream title! (%d)",
result.status);
}
}
void MacroActionTwitch::SetStreamCategory(
const std::shared_ptr<TwitchToken> &token) const
{
if (_category.id == -1) {
return;
}
OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "game_id",
std::to_string(_category.id).c_str());
auto result = SendPatchRequest(*token, "https://api.twitch.tv",
"/helix/channels",
{{"broadcaster_id", token->GetUserID()}},
data.Get());
if (result.status != 204) {
blog(LOG_INFO, "Failed to set stream category! (%d)",
result.status);
}
}
void MacroActionTwitch::CreateStreamMarker(
const std::shared_ptr<TwitchToken> &token) const
{
OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "user_id", token->GetUserID().c_str());
if (!std::string(_markerDescription).empty()) {
obs_data_set_string(data, "description",
_markerDescription.c_str());
}
auto result = SendPostRequest(*token, "https://api.twitch.tv",
"/helix/streams/markers", {}, data.Get());
if (result.status != 200) {
blog(LOG_INFO, "Failed to create marker! (%d)", result.status);
}
}
void MacroActionTwitch::CreateStreamClip(
const std::shared_ptr<TwitchToken> &token) const
{
auto hasDelay = _clipHasDelay ? "true" : "false";
auto result = SendPostRequest(*token, "https://api.twitch.tv",
"/helix/clips",
{{"broadcaster_id", token->GetUserID()},
{"has_delay", hasDelay}});
if (result.status != 202) {
blog(LOG_INFO, "Failed to create clip! (%d)", result.status);
}
}
void MacroActionTwitch::StartCommercial(
const std::shared_ptr<TwitchToken> &token) const
{
OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "broadcaster_id", token->GetUserID().c_str());
obs_data_set_int(data, "length", _duration.Seconds());
auto result = SendPostRequest(*token, "https://api.twitch.tv",
"/helix/channels/commercial", {},
data.Get());
if (result.status == 200) {
OBSDataArrayAutoRelease replyArray =
obs_data_get_array(result.data, "data");
OBSDataAutoRelease replyData =
obs_data_array_item(replyArray, 0);
vblog(LOG_INFO,
"Commercial started! (%d)\n"
"length: %lld\n"
"message: %s\n"
"retry_after: %lld\n",
result.status, obs_data_get_int(replyData, "length"),
obs_data_get_string(replyData, "message"),
obs_data_get_int(replyData, "retry_after"));
} else {
blog(LOG_INFO,
"Failed to start commercial! (%d)\n"
"error: %s\n"
"message: %s\n",
result.status, obs_data_get_string(result.data, "error"),
obs_data_get_string(result.data, "message"));
}
}
void MacroActionTwitch::SendChatAnnouncement(
const std::shared_ptr<TwitchToken> &token) const
{
OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "message", _announcementMessage.c_str());
obs_data_set_string(
data, "color",
announcementColorsTwitch.at(_announcementColor).c_str());
auto userId = token->GetUserID();
auto result = SendPostRequest(
*token, "https://api.twitch.tv", "/helix/chat/announcements",
{{"broadcaster_id", userId}, {"moderator_id", userId}},
data.Get());
if (result.status != 204) {
blog(LOG_INFO, "Failed to send chat announcement! (%d)",
result.status);
}
}
void MacroActionTwitch::SetChatEmoteOnlyMode(
const std::shared_ptr<TwitchToken> &token, bool enable) const
{
OBSDataAutoRelease data = obs_data_create();
obs_data_set_bool(data, "emote_mode", enable);
auto userId = token->GetUserID();
auto result = SendPatchRequest(
*token, "https://api.twitch.tv", "/helix/chat/settings",
{{"broadcaster_id", userId}, {"moderator_id", userId}},
data.Get());
if (result.status != 200) {
blog(LOG_INFO, "Failed to %s chat's emote-only mode! (%d)",
enable ? "enable" : "disable", result.status);
}
}
void MacroActionTwitch::StartRaid(const std::shared_ptr<TwitchToken> &token)
{
OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "from_broadcaster_id",
token->GetUserID().c_str());
obs_data_set_string(data, "to_broadcaster_id",
_channel.GetUserID(*token).c_str());
auto result = SendPostRequest(*token, "https://api.twitch.tv",
"/helix/raids", {}, data.Get());
if (result.status != 200) {
blog(LOG_INFO, "Failed to start raid! (%d)\n", result.status);
}
}
void MacroActionTwitch::SendChatMessage(
const std::shared_ptr<TwitchToken> &token)
{
if (!_chatConnection) {
_chatConnection = TwitchChatConnection::GetChatConnection(
*token, _channel);
return;
}
_chatConnection->SendChatMessage(_chatMessage);
}
bool MacroActionTwitch::PerformAction()
{
auto token = _token.lock();
if (!token) {
return true;
}
switch (_action) {
case Action::CHANNEL_INFO_TITLE_SET:
SetStreamTitle(token);
break;
case Action::CHANNEL_INFO_CATEGORY_SET:
SetStreamCategory(token);
break;
case Action::RAID_START:
StartRaid(token);
break;
case Action::COMMERCIAL_START:
StartCommercial(token);
break;
case Action::MARKER_CREATE:
CreateStreamMarker(token);
break;
case Action::CLIP_CREATE:
CreateStreamClip(token);
break;
case Action::CHAT_ANNOUNCEMENT_SEND:
SendChatAnnouncement(token);
break;
case Action::CHAT_EMOTE_ONLY_ENABLE:
SetChatEmoteOnlyMode(token, true);
break;
case Action::CHAT_EMOTE_ONLY_DISABLE:
SetChatEmoteOnlyMode(token, false);
break;
case MacroActionTwitch::Action::SEND_CHAT_MESSAGE:
SendChatMessage(token);
break;
default:
break;
}
return true;
}
void MacroActionTwitch::LogAction() const
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
ablog(LOG_INFO, "performed action \"%s\" with token for \"%s\"",
it->second.c_str(),
GetWeakTwitchTokenName(_token).c_str());
} else {
blog(LOG_WARNING, "ignored unknown twitch action %d",
static_cast<int>(_action));
}
}
bool MacroActionTwitch::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
obs_data_set_string(obj, "token",
GetWeakTwitchTokenName(_token).c_str());
_streamTitle.Save(obj, "streamTitle");
_category.Save(obj);
_markerDescription.Save(obj, "markerDescription");
obs_data_set_bool(obj, "clipHasDelay", _clipHasDelay);
_duration.Save(obj);
_announcementMessage.Save(obj, "announcementMessage");
obs_data_set_int(obj, "announcementColor",
static_cast<int>(_announcementColor));
_channel.Save(obj);
_chatMessage.Save(obj, "chatMessage");
return true;
}
bool MacroActionTwitch::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_token = GetWeakTwitchTokenByName(obs_data_get_string(obj, "token"));
_streamTitle.Load(obj, "streamTitle");
_category.Load(obj);
_markerDescription.Load(obj, "markerDescription");
_clipHasDelay = obs_data_get_bool(obj, "clipHasDelay");
_duration.Load(obj);
_announcementMessage.Load(obj, "announcementMessage");
_announcementColor = static_cast<AnnouncementColor>(
obs_data_get_int(obj, "announcementColor"));
_channel.Load(obj);
_chatMessage.Load(obj, "chatMessage");
SetAction(static_cast<Action>(obs_data_get_int(obj, "action")));
return true;
}
void MacroActionTwitch::SetAction(Action action)
{
_action = action;
ResetChatConnection();
}
bool MacroActionTwitch::ActionIsSupportedByToken()
{
static const std::unordered_map<Action, TokenOption> requiredOption = {
{Action::CHANNEL_INFO_TITLE_SET, {"channel:manage:broadcast"}},
{Action::CHANNEL_INFO_CATEGORY_SET,
{"channel:manage:broadcast"}},
{Action::CHANNEL_INFO_LANGUAGE_SET,
{"channel:manage:broadcast"}},
{Action::CHANNEL_INFO_DELAY_SET, {"channel:manage:broadcast"}},
{Action::CHANNEL_INFO_BRANDED_CONTENT_ENABLE,
{"channel:manage:broadcast"}},
{Action::CHANNEL_INFO_BRANDED_CONTENT_DISABLE,
{"channel:manage:broadcast"}},
{Action::RAID_START, {"channel:manage:raids"}},
{Action::RAID_END, {"channel:manage:raids"}},
{Action::SHOUTOUT_SEND, {"moderator:manage:shoutouts"}},
{Action::POLL_END, {"channel:manage:polls"}},
{Action::PREDICTION_END, {"channel:manage:predictions"}},
{Action::SHIELD_MODE_START, {"moderator:manage:shield_mode"}},
{Action::SHIELD_MODE_END, {"moderator:manage:shield_mode"}},
{Action::POINTS_REWARD_ENABLE, {"channel:manage:redemptions"}},
{Action::POINTS_REWARD_DISABLE, {"channel:manage:redemptions"}},
{Action::POINTS_REWARD_PAUSE, {"channel:manage:redemptions"}},
{Action::POINTS_REWARD_UNPAUSE, {"channel:manage:redemptions"}},
{Action::POINTS_REWARD_TITLE_SET,
{"channel:manage:redemptions"}},
{Action::POINTS_REWARD_PROMPT_SET,
{"channel:manage:redemptions"}},
{Action::POINTS_REWARD_COST_SET,
{"channel:manage:redemptions"}},
{Action::POINTS_REWARD_USER_INPUT_REQUIRE,
{"channel:manage:redemptions"}},
{Action::POINTS_REWARD_USER_INPUT_UNREQUIRE,
{"channel:manage:redemptions"}},
{Action::POINTS_REWARD_COOLDOWN_ENABLE,
{"channel:manage:redemptions"}},
{Action::POINTS_REWARD_COOLDOWN_DISABLE,
{"channel:manage:redemptions"}},
{Action::POINTS_REWARD_QUEUE_SKIP_ENABLE,
{"channel:manage:redemptions"}},
{Action::POINTS_REWARD_QUEUE_SKIP_DISABLE,
{"channel:manage:redemptions"}},
{Action::POINTS_REWARD_MAX_PER_STREAM_ENABLE,
{"channel:manage:redemptions"}},
{Action::POINTS_REWARD_MAX_PER_STREAM_DISABLE,
{"channel:manage:redemptions"}},
{Action::POINTS_REWARD_MAX_PER_USER_ENABLE,
{"channel:manage:redemptions"}},
{Action::POINTS_REWARD_MAX_PER_USER_DISABLE,
{"channel:manage:redemptions"}},
{Action::POINTS_REWARD_DELETE, {"channel:manage:redemptions"}},
{Action::POINTS_REWARD_REDEMPTION_CANCEL,
{"channel:manage:redemptions"}},
{Action::POINTS_REWARD_REDEMPTION_FULFILL,
{"channel:manage:redemptions"}},
{Action::USER_BAN, {"moderator:manage:banned_users"}},
{Action::USER_UNBAN, {"moderator:manage:banned_users"}},
{Action::USER_BLOCK, {"user:manage:blocked_users"}},
{Action::USER_UNBLOCK, {"user:manage:blocked_users"}},
{Action::USER_MODERATOR_ADD, {"channel:manage:moderators"}},
{Action::USER_MODERATOR_DELETE, {"channel:manage:moderators"}},
{Action::USER_VIP_ADD, {"channel:manage:vips"}},
{Action::USER_VIP_DELETE, {"channel:manage:vips"}},
{Action::COMMERCIAL_START, {"channel:edit:commercial"}},
{Action::COMMERCIAL_SNOOZE, {"channel:manage:ads"}},
{Action::MARKER_CREATE, {"channel:manage:broadcast"}},
{Action::CLIP_CREATE, {"clips:edit"}},
{Action::CHAT_ANNOUNCEMENT_SEND,
{"moderator:manage:announcements"}},
{Action::CHAT_EMOTE_ONLY_ENABLE,
{"moderator:manage:chat_settings"}},
{Action::CHAT_EMOTE_ONLY_DISABLE,
{"moderator:manage:chat_settings"}},
{Action::CHAT_FOLLOWER_ONLY_ENABLE,
{"moderator:manage:chat_settings"}},
{Action::CHAT_FOLLOWER_ONLY_DISABLE,
{"moderator:manage:chat_settings"}},
{Action::CHAT_SUBSCRIBER_ONLY_ENABLE,
{"moderator:manage:chat_settings"}},
{Action::CHAT_SUBSCRIBER_ONLY_DISABLE,
{"moderator:manage:chat_settings"}},
{Action::CHAT_SLOW_MODE_ENABLE,
{"moderator:manage:chat_settings"}},
{Action::CHAT_SLOW_MODE_DISABLE,
{"moderator:manage:chat_settings"}},
{Action::CHAT_NON_MODERATOR_DELAY_ENABLE,
{"moderator:manage:chat_settings"}},
{Action::CHAT_NON_MODERATOR_DELAY_DISABLE,
{"moderator:manage:chat_settings"}},
{Action::CHAT_UNIQUE_MODE_ENABLE,
{"moderator:manage:chat_settings"}},
{Action::CHAT_UNIQUE_MODE_DISABLE,
{"moderator:manage:chat_settings"}},
{Action::WHISPER_SEND, {"user:manage:whispers"}},
{Action::SEND_CHAT_MESSAGE, {"chat:edit"}},
};
auto token = _token.lock();
if (!token) {
return false;
}
auto option = requiredOption.find(_action);
assert(option != requiredOption.end());
if (option == requiredOption.end()) {
return false;
}
return token->OptionIsEnabled(option->second);
}
void MacroActionTwitch::ResetChatConnection()
{
_chatConnection.reset();
if (_action == Action::SEND_CHAT_MESSAGE) {
auto token = _token.lock();
if (!token) {
return;
}
_chatConnection = TwitchChatConnection::GetChatConnection(
*token, _channel);
}
}
static inline void populateActionSelection(QComboBox *list)
{
for (const auto &[action, name] : actionTypes) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(action));
}
}
static inline void populateAnnouncementColorSelection(QComboBox *list)
{
for (const auto &[_, name] : announcementColors) {
list->addItem(obs_module_text(name.c_str()));
}
}
MacroActionTwitchEdit::MacroActionTwitchEdit(
QWidget *parent, std::shared_ptr<MacroActionTwitch> entryData)
: QWidget(parent),
_layout(new QHBoxLayout()),
_actions(new FilterComboBox()),
_tokens(new TwitchConnectionSelection()),
_tokenWarning(new QLabel()),
_streamTitle(new VariableLineEdit(this)),
_category(new TwitchCategoryWidget(this)),
_markerDescription(new VariableLineEdit(this)),
_clipHasDelay(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.action.twitch.clip.hasDelay"))),
_duration(new DurationSelection(this, false, 0)),
_announcementMessage(new VariableTextEdit(this)),
_announcementColor(new QComboBox(this)),
_channel(new TwitchChannelSelection(this)),
_chatMessage(new VariableTextEdit(this))
{
SetWidgetProperties();
SetWidgetSignalConnections();
_entryData = entryData;
SetWidgetLayout();
auto mainLayout = new QVBoxLayout();
mainLayout->addLayout(_layout);
mainLayout->addWidget(_announcementMessage);
mainLayout->addWidget(_chatMessage);
mainLayout->addWidget(_tokenWarning);
setLayout(mainLayout);
_tokenCheckTimer.start(1000);
UpdateEntryData();
_loading = false;
}
void MacroActionTwitchEdit::ActionChanged(int idx)
{
if (_loading || !_entryData) {
return;
}
if (idx == -1) { // Reset to previous selection
const QSignalBlocker b(_actions);
_actions->setCurrentIndex(_actions->findData(
static_cast<int>(_entryData->GetAction())));
return;
}
auto lock = LockContext();
_entryData->SetAction(static_cast<MacroActionTwitch::Action>(
_actions->itemData(idx).toInt()));
SetWidgetLayout();
SetWidgetVisibility();
}
void MacroActionTwitchEdit::TwitchTokenChanged(const QString &token)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_token = GetWeakTwitchTokenByQString(token);
_category->SetToken(_entryData->_token);
_channel->SetToken(_entryData->_token);
_entryData->ResetChatConnection();
SetWidgetVisibility();
emit(HeaderInfoChanged(token));
}
void MacroActionTwitchEdit::SetTokenWarning(bool visible, const QString &text)
{
_tokenWarning->setText(text);
_tokenWarning->setVisible(visible);
adjustSize();
updateGeometry();
}
void MacroActionTwitchEdit::CheckToken()
{
if (!_entryData) {
return;
}
if (_entryData->_token.expired()) {
SetTokenWarning(
true,
obs_module_text(
"AdvSceneSwitcher.twitchToken.noSelection"));
return;
}
if (!TokenIsValid(_entryData->_token)) {
SetTokenWarning(
true, obs_module_text(
"AdvSceneSwitcher.twitchToken.notValid"));
return;
}
if (!_entryData->ActionIsSupportedByToken()) {
SetTokenWarning(
true,
obs_module_text(
"AdvSceneSwitcher.twitchToken.permissionsInsufficient"));
return;
}
SetTokenWarning(false);
}
void MacroActionTwitchEdit::StreamTitleChanged()
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_streamTitle = _streamTitle->text().toStdString();
}
void MacroActionTwitchEdit::CategoreyChanged(const TwitchCategory &category)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_category = category;
}
void MacroActionTwitchEdit::MarkerDescriptionChanged()
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_markerDescription =
_markerDescription->text().toStdString();
}
void MacroActionTwitchEdit::ClipHasDelayChanged(int state)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_clipHasDelay = state;
}
void MacroActionTwitchEdit::DurationChanged(const Duration &duration)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_duration = duration;
}
void MacroActionTwitchEdit::AnnouncementMessageChanged()
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_announcementMessage =
_announcementMessage->toPlainText().toStdString();
adjustSize();
updateGeometry();
}
void MacroActionTwitchEdit::AnnouncementColorChanged(int index)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_announcementColor =
static_cast<MacroActionTwitch::AnnouncementColor>(index);
}
void MacroActionTwitchEdit::SetWidgetProperties()
{
_streamTitle->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred);
_streamTitle->setMaxLength(140);
_markerDescription->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred);
_markerDescription->setMaxLength(140);
_announcementMessage->setMaxLength(500);
auto durationSpinBox = _duration->SpinBox();
durationSpinBox->setMaximum(180);
durationSpinBox->setSuffix("s");
populateActionSelection(_actions);
populateAnnouncementColorSelection(_announcementColor);
}
void MacroActionTwitchEdit::SetWidgetSignalConnections()
{
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_tokens, SIGNAL(SelectionChanged(const QString &)),
this, SLOT(TwitchTokenChanged(const QString &)));
QWidget::connect(&_tokenCheckTimer, SIGNAL(timeout()), this,
SLOT(CheckToken()));
QWidget::connect(_streamTitle, SIGNAL(editingFinished()), this,
SLOT(StreamTitleChanged()));
QWidget::connect(_category,
SIGNAL(CategoreyChanged(const TwitchCategory &)), this,
SLOT(CategoreyChanged(const TwitchCategory &)));
QWidget::connect(_markerDescription, SIGNAL(editingFinished()), this,
SLOT(MarkerDescriptionChanged()));
QObject::connect(_clipHasDelay, SIGNAL(stateChanged(int)), this,
SLOT(ClipHasDelayChanged(int)));
QObject::connect(_duration, SIGNAL(DurationChanged(const Duration &)),
this, SLOT(DurationChanged(const Duration &)));
QWidget::connect(_announcementMessage, SIGNAL(textChanged()), this,
SLOT(AnnouncementMessageChanged()));
QWidget::connect(_announcementColor, SIGNAL(currentIndexChanged(int)),
this, SLOT(AnnouncementColorChanged(int)));
QWidget::connect(_channel,
SIGNAL(ChannelChanged(const TwitchChannel &)), this,
SLOT(ChannelChanged(const TwitchChannel &)));
QWidget::connect(_chatMessage, SIGNAL(textChanged()), this,
SLOT(ChatMessageChanged()));
}
void MacroActionTwitchEdit::SetWidgetVisibility()
{
_streamTitle->setVisible(
_entryData->GetAction() ==
MacroActionTwitch::Action::CHANNEL_INFO_TITLE_SET);
_category->setVisible(
_entryData->GetAction() ==
MacroActionTwitch::Action::CHANNEL_INFO_CATEGORY_SET);
_channel->setVisible(
_entryData->GetAction() ==
MacroActionTwitch::Action::RAID_START ||
_entryData->GetAction() ==
MacroActionTwitch::Action::RAID_END ||
_entryData->GetAction() ==
MacroActionTwitch::Action::SEND_CHAT_MESSAGE);
_duration->setVisible(_entryData->GetAction() ==
MacroActionTwitch::Action::COMMERCIAL_START);
_markerDescription->setVisible(
_entryData->GetAction() ==
MacroActionTwitch::Action::MARKER_CREATE);
_clipHasDelay->setVisible(_entryData->GetAction() ==
MacroActionTwitch::Action::CLIP_CREATE);
_announcementMessage->setVisible(
_entryData->GetAction() ==
MacroActionTwitch::Action::CHAT_ANNOUNCEMENT_SEND);
_announcementColor->setVisible(
_entryData->GetAction() ==
MacroActionTwitch::Action::CHAT_ANNOUNCEMENT_SEND);
_chatMessage->setVisible(_entryData->GetAction() ==
MacroActionTwitch::Action::SEND_CHAT_MESSAGE);
if (_entryData->GetAction() ==
MacroActionTwitch::Action::CHANNEL_INFO_TITLE_SET ||
_entryData->GetAction() ==
MacroActionTwitch::Action::MARKER_CREATE) {
RemoveStretchIfPresent(_layout);
} else {
AddStretchIfNecessary(_layout);
}
CheckToken();
adjustSize();
updateGeometry();
}
void MacroActionTwitchEdit::ChannelChanged(const TwitchChannel &channel)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_channel = channel;
_entryData->ResetChatConnection();
}
void MacroActionTwitchEdit::ChatMessageChanged()
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_chatMessage = _chatMessage->toPlainText().toStdString();
adjustSize();
updateGeometry();
}
void MacroActionTwitchEdit::SetWidgetLayout()
{
const std::vector<QWidget *> widgets{
_tokens, _actions, _streamTitle,
_category, _markerDescription, _clipHasDelay,
_duration, _announcementColor, _channel};
for (auto widget : widgets) {
_layout->removeWidget(widget);
}
ClearLayout(_layout);
auto layoutText =
_entryData->GetAction() ==
MacroActionTwitch::Action::SEND_CHAT_MESSAGE
? obs_module_text(
"AdvSceneSwitcher.action.twitch.entry.chat")
: obs_module_text(
"AdvSceneSwitcher.action.twitch.entry.default");
PlaceWidgets(layoutText, _layout,
{{"{{account}}", _tokens},
{"{{actions}}", _actions},
{"{{streamTitle}}", _streamTitle},
{"{{category}}", _category},
{"{{markerDescription}}", _markerDescription},
{"{{clipHasDelay}}", _clipHasDelay},
{"{{duration}}", _duration},
{"{{announcementColor}}", _announcementColor},
{"{{channel}}", _channel}});
_layout->setContentsMargins(0, 0, 0, 0);
}
void MacroActionTwitchEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(
_actions->findData(static_cast<int>(_entryData->GetAction())));
_tokens->SetToken(_entryData->_token);
_streamTitle->setText(_entryData->_streamTitle);
_category->SetToken(_entryData->_token);
_category->SetCategory(_entryData->_category);
_markerDescription->setText(_entryData->_markerDescription);
_clipHasDelay->setChecked(_entryData->_clipHasDelay);
_duration->SetDuration(_entryData->_duration);
_announcementMessage->setPlainText(_entryData->_announcementMessage);
_announcementColor->setCurrentIndex(
static_cast<int>(_entryData->_announcementColor));
_channel->SetToken(_entryData->_token);
_channel->SetChannel(_entryData->_channel);
_chatMessage->setPlainText(_entryData->_chatMessage);
SetWidgetVisibility();
}
} // namespace advss