Skip to content

Commit

Permalink
Add option to check if users leave or join Twitch chat
Browse files Browse the repository at this point in the history
  • Loading branch information
WarmUpTill committed Oct 8, 2024
1 parent 62ce83a commit 0fd5f47
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 5 deletions.
8 changes: 8 additions & 0 deletions data/locale/en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,8 @@ AdvSceneSwitcher.condition.twitch.type.chat.properties.color="Color matches"
AdvSceneSwitcher.condition.twitch.type.chat.properties.displayName="Display name matches"
AdvSceneSwitcher.condition.twitch.type.chat.properties.loginName="User name matches"
AdvSceneSwitcher.condition.twitch.type.chat.properties.badge="User has badge"
AdvSceneSwitcher.condition.twitch.type.chat.userJoined="User joined the chat"
AdvSceneSwitcher.condition.twitch.type.chat.userLeft="User left the chat"
AdvSceneSwitcher.condition.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!"
AdvSceneSwitcher.condition.twitch.entry="Channel{{channel}}{{conditions}}{{pointsReward}}{{streamTitle}}{{regex}}{{category}}"
AdvSceneSwitcher.condition.twitch.entry.account="Check using account{{account}}"
Expand Down Expand Up @@ -1765,6 +1767,12 @@ AdvSceneSwitcher.tempVar.twitch.is_turbo.chatReceive.description="'true' if the
AdvSceneSwitcher.tempVar.twitch.is_vip.chatReceive="Is chatter a VIP"
AdvSceneSwitcher.tempVar.twitch.is_vip.chatReceive.description="'true' if the chatter is a VIP in the channel, 'false' otherwise."

AdvSceneSwitcher.tempVar.twitch.user_login.chatJoin="User login"
AdvSceneSwitcher.tempVar.twitch.user_login.chatJoin.description="The user login of the person who joined the chat room."

AdvSceneSwitcher.tempVar.twitch.user_login.chatLeave="User login"
AdvSceneSwitcher.tempVar.twitch.user_login.chatLeave.description="The user login of the person who left the chat room."

AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid="Raid creator user ID"
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid.description="The broadcaster ID that created the raid."
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_login.raid="Raid creator user login"
Expand Down
43 changes: 39 additions & 4 deletions plugins/twitch/chat-connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,6 @@ static void parseCommand(const std::string &rawCommandComponent,
return;
} else if (message.command.command == "PING" ||
message.command.command == "001" ||
message.command.command == "JOIN" ||
message.command.command == "PART" ||
message.command.command == "NOTICE" ||
message.command.command == "CLEARCHAT" ||
message.command.command == "HOSTTARGET" ||
Expand All @@ -191,6 +189,18 @@ static void parseCommand(const std::string &rawCommandComponent,
return;
}
message.command.parameters = commandParts[1];
} else if (message.command.command == "JOIN") {
if (commandParts.size() < 2) {
return;
}
message.properties.joinedChannel = true;
message.command.parameters = commandParts[1];
} else if (message.command.command == "PART") {
if (commandParts.size() < 2) {
return;
}
message.properties.leftChannel = true;
message.command.parameters = commandParts[1];
} else if (message.command.command == "GLOBALUSERSTATE" ||
message.command.command == "USERSTATE" ||
message.command.command == "ROOMSTATE" ||
Expand Down Expand Up @@ -504,12 +514,34 @@ void TwitchChatConnection::JoinChannel(const std::string &channelName)
Send("JOIN #" + channelName);
}

static bool nickMatchesTokenUser(const std::string &nick, TwitchToken &token)
{
auto tokenUserName = token.GetName();
std::transform(tokenUserName.begin(), tokenUserName.end(),
tokenUserName.begin(),
[](unsigned char c) { return std::tolower(c); });
return nick == tokenUserName;
}

void TwitchChatConnection::HandleJoin(const IRCMessage &message)
{
if (!nickMatchesTokenUser(message.source.nick, _token)) {
_messageDispatcher.DispatchMessage(message);
return;
}
_joinedChannelName = std::get<std::string>(message.command.parameters);
vblog(LOG_INFO, "Twitch chat join was successful!");
}

void TwitchChatConnection::HandlePart(const IRCMessage &message)
{
if (!nickMatchesTokenUser(message.source.nick, _token)) {
_messageDispatcher.DispatchMessage(message);
return;
}
vblog(LOG_INFO, "Left Twitch chat!");
}

void TwitchChatConnection::HandleNewMessage(const IRCMessage &message)
{
_messageDispatcher.DispatchMessage(message);
Expand Down Expand Up @@ -571,7 +603,8 @@ void TwitchChatConnection::OnMessage(
{
static constexpr std::string_view authOKCommand = "001";
static constexpr std::string_view pingCommand = "PING";
static constexpr std::string_view joinOKCommand = "JOIN";
static constexpr std::string_view joinCommand = "JOIN";
static constexpr std::string_view partCommand = "PART";
static constexpr std::string_view noticeCommand = "NOTICE";
static constexpr std::string_view reconnectCommand = "RECONNECT";
static constexpr std::string_view newMessageCommand = "PRIVMSG";
Expand All @@ -596,8 +629,10 @@ void TwitchChatConnection::OnMessage(
} else if (message.command.command == pingCommand) {
Send("PONG " +
std::get<std::string>(message.command.parameters));
} else if (message.command.command == joinOKCommand) {
} else if (message.command.command == joinCommand) {
HandleJoin(message);
} else if (message.command.command == partCommand) {
HandlePart(message);
} else if (message.command.command == newMessageCommand) {
HandleNewMessage(message);
} else if (message.command.command == whisperCommand) {
Expand Down
3 changes: 3 additions & 0 deletions plugins/twitch/chat-connection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ struct IRCMessage {
std::string userId;
std::string userType;
bool isVIP = false;
bool joinedChannel = false;
bool leftChannel = false;
} properties;

struct {
Expand Down Expand Up @@ -95,6 +97,7 @@ class TwitchChatConnection : public QObject {
void Authenticate();
void JoinChannel(const std::string &);
void HandleJoin(const IRCMessage &);
void HandlePart(const IRCMessage &);
void HandleNewMessage(const IRCMessage &);
void HandleWhisper(const IRCMessage &);
void HandleNotice(const IRCMessage &) const;
Expand Down
71 changes: 70 additions & 1 deletion plugins/twitch/macro-condition-twitch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ const static std::map<MacroConditionTwitch::Condition, std::string> conditionTyp
"AdvSceneSwitcher.condition.twitch.type.polling.channel.category"},
{MacroConditionTwitch::Condition::CHAT_MESSAGE_RECEIVED,
"AdvSceneSwitcher.condition.twitch.type.chat.message"},
{MacroConditionTwitch::Condition::CHAT_USER_JOINED,
"AdvSceneSwitcher.condition.twitch.type.chat.userJoined"},
{MacroConditionTwitch::Condition::CHAT_USER_LEFT,
"AdvSceneSwitcher.condition.twitch.type.chat.userLeft"},
};

const static std::map<MacroConditionTwitch::Condition, std::string> eventIdentifiers = {
Expand Down Expand Up @@ -385,6 +389,12 @@ bool MacroConditionTwitch::CheckChatMessages(TwitchToken &token)
continue;
}

// Join and leave message don't have any message data
if (message->properties.leftChannel ||
message->properties.joinedChannel) {
continue;
}

if (!_chatMessagePattern.Matches(*message)) {
continue;
}
Expand Down Expand Up @@ -443,6 +453,41 @@ bool MacroConditionTwitch::CheckChatMessages(TwitchToken &token)
return false;
}

bool MacroConditionTwitch::CheckChatUserJoinOrLeave(TwitchToken &token)
{
if (!_chatConnection) {
_chatConnection = TwitchChatConnection::GetChatConnection(
token, _channel);
if (!_chatConnection) {
return false;
}
_chatBuffer = _chatConnection->RegisterForMessages();
return false;
}

while (!_chatBuffer->Empty()) {
auto message = _chatBuffer->ConsumeMessage();
if (!message) {
continue;
}

if ((_condition == Condition::CHAT_USER_JOINED &&
!message->properties.joinedChannel) ||
(_condition == Condition::CHAT_USER_LEFT &&
!message->properties.leftChannel)) {
continue;
}

SetTempVarValue("user_login", message->source.nick);

if (_clearBufferOnMatch) {
_eventBuffer->Clear();
}
return true;
}
return false;
}

void MacroConditionTwitch::SetTempVarValues(const ChannelLiveInfo &info)
{
SetTempVarValue("broadcaster_user_id", info.user_id);
Expand Down Expand Up @@ -625,6 +670,14 @@ bool MacroConditionTwitch::CheckCondition()
}
return CheckChatMessages(*token);
}
case Condition::CHAT_USER_JOINED:
case Condition::CHAT_USER_LEFT: {
auto token = _token.lock();
if (!token) {
return false;
}
return CheckChatUserJoinOrLeave(*token);
}
default:
break;
}
Expand Down Expand Up @@ -773,6 +826,10 @@ bool MacroConditionTwitch::ConditionIsSupportedByToken()
{Condition::CATEGORY_POLLING, {}},
{Condition::CHAT_MESSAGE_RECEIVED,
{{"chat:read"}, {"chat:edit"}}},
{Condition::CHAT_USER_JOINED,
{{"chat:read"}, {"chat:edit"}}},
{Condition::CHAT_USER_LEFT,
{{"chat:read"}, {"chat:edit"}}},
};

auto token = _token.lock();
Expand Down Expand Up @@ -967,6 +1024,8 @@ void MacroConditionTwitch::SetupTempVars()
};

if (_condition != Condition::CHAT_MESSAGE_RECEIVED &&
_condition != Condition::CHAT_USER_JOINED &&
_condition != Condition::CHAT_USER_LEFT &&
_condition != Condition::RAID_INBOUND_EVENT &&
_condition != Condition::RAID_OUTBOUND_EVENT) {
setupTempVarHelper("broadcaster_user_id");
Expand Down Expand Up @@ -1303,6 +1362,12 @@ void MacroConditionTwitch::SetupTempVars()
setupTempVarHelper("is_turbo", ".chatReceive");
setupTempVarHelper("is_vip", ".chatReceive");
break;
case Condition::CHAT_USER_JOINED:
setupTempVarHelper("user_login", ".chatJoin");
break;
case Condition::CHAT_USER_LEFT:
setupTempVarHelper("user_login", ".chatLeave");
break;
default:
break;
}
Expand Down Expand Up @@ -1546,7 +1611,11 @@ void MacroConditionTwitchEdit::SetWidgetVisibility()
_clearBufferOnMatch->setVisible(
_entryData->IsUsingEventSubCondition() ||
_entryData->GetCondition() ==
MacroConditionTwitch::Condition::CHAT_MESSAGE_RECEIVED);
MacroConditionTwitch::Condition::CHAT_MESSAGE_RECEIVED ||
_entryData->GetCondition() ==
MacroConditionTwitch::Condition::CHAT_USER_JOINED ||
_entryData->GetCondition() ==
MacroConditionTwitch::Condition::CHAT_USER_LEFT);

if (condition == MacroConditionTwitch::Condition::TITLE_POLLING) {
RemoveStretchIfPresent(_layout);
Expand Down
3 changes: 3 additions & 0 deletions plugins/twitch/macro-condition-twitch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ class MacroConditionTwitch : public MacroCondition {

// Chat
CHAT_MESSAGE_RECEIVED = 500000,
CHAT_USER_JOINED = 500100,
CHAT_USER_LEFT = 500200,

// Polling
LIVE_POLLING = 1000000,
Expand Down Expand Up @@ -110,6 +112,7 @@ class MacroConditionTwitch : public MacroCondition {
bool CheckChannelGenericEvents();
bool CheckChannelLiveEvents();
bool CheckChatMessages(TwitchToken &token);
bool CheckChatUserJoinOrLeave(TwitchToken &token);

void RegisterEventSubscription();
void ResetSubscription();
Expand Down

0 comments on commit 0fd5f47

Please sign in to comment.