feat(eventsub): implement channel.chat.user_message_(hold/update) (#6008)
This commit is contained in:
+1
-1
@@ -31,7 +31,7 @@
|
||||
- Bugfix: Fixed color input thinking blue is also red. (#5982)
|
||||
- Bugfix: Fixed an issue where commands would sometimes reset if Chatterino was improperly shut down. (#6011)
|
||||
- Dev: Subscriptions to PubSub channel points redemption topics now use no auth token, making it continue to work during PubSub shutdown. (#5947)
|
||||
- Dev: Add initial experimental EventSub support. (#5837, #5895, #5897, #5904, #5910, #5903, #5915, #5916, #5930, #5935, #5932, #5943, #5952, #5953, #5968, #5973, #5974, #5980, #5981, #5985, #5990, #5992, #5993, #5996, #5995, #6000, #6001, #6002, #6003, #6005, #6007, #6010)
|
||||
- Dev: Add initial experimental EventSub support. (#5837, #5895, #5897, #5904, #5910, #5903, #5915, #5916, #5930, #5935, #5932, #5943, #5952, #5953, #5968, #5973, #5974, #5980, #5981, #5985, #5990, #5992, #5993, #5996, #5995, #6000, #6001, #6002, #6003, #6005, #6007, #6010, #6008)
|
||||
- Dev: Remove unneeded platform specifier for toasts. (#5914)
|
||||
- Dev: Highlight checks now use non-capturing groups for the boundaries. (#5784)
|
||||
- Dev: Removed unused PubSub whisper code. (#5898)
|
||||
|
||||
@@ -145,6 +145,24 @@ public:
|
||||
benchmark::DoNotOptimize(&metadata);
|
||||
benchmark::DoNotOptimize(&payload);
|
||||
}
|
||||
|
||||
void onChannelChatUserMessageHold(
|
||||
const messages::Metadata &metadata,
|
||||
const payload::channel_chat_user_message_hold::v1::Payload &payload)
|
||||
override
|
||||
{
|
||||
benchmark::DoNotOptimize(&metadata);
|
||||
benchmark::DoNotOptimize(&payload);
|
||||
}
|
||||
|
||||
void onChannelChatUserMessageUpdate(
|
||||
const messages::Metadata &metadata,
|
||||
const payload::channel_chat_user_message_update::v1::Payload &payload)
|
||||
override
|
||||
{
|
||||
benchmark::DoNotOptimize(&metadata);
|
||||
benchmark::DoNotOptimize(&payload);
|
||||
}
|
||||
// NOLINTEND(cppcoreguidelines-pro-type-const-cast)
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "twitch-eventsub-ws/payloads/channel-ban-v1.hpp"
|
||||
#include "twitch-eventsub-ws/payloads/channel-chat-message-v1.hpp"
|
||||
#include "twitch-eventsub-ws/payloads/channel-chat-notification-v1.hpp"
|
||||
#include "twitch-eventsub-ws/payloads/channel-chat-user-message-hold-v1.hpp"
|
||||
#include "twitch-eventsub-ws/payloads/channel-chat-user-message-update-v1.hpp"
|
||||
#include "twitch-eventsub-ws/payloads/channel-moderate-v2.hpp"
|
||||
#include "twitch-eventsub-ws/payloads/channel-suspicious-user-message-v1.hpp"
|
||||
#include "twitch-eventsub-ws/payloads/channel-suspicious-user-update-v1.hpp"
|
||||
@@ -74,6 +76,16 @@ public:
|
||||
const messages::Metadata &metadata,
|
||||
const payload::channel_suspicious_user_update::v1::Payload
|
||||
&payload) = 0;
|
||||
|
||||
virtual void onChannelChatUserMessageHold(
|
||||
const messages::Metadata &metadata,
|
||||
const payload::channel_chat_user_message_hold::v1::Payload
|
||||
&payload) = 0;
|
||||
|
||||
virtual void onChannelChatUserMessageUpdate(
|
||||
const messages::Metadata &metadata,
|
||||
const payload::channel_chat_user_message_update::v1::Payload
|
||||
&payload) = 0;
|
||||
// Add your new subscription types above this line
|
||||
};
|
||||
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "twitch-eventsub-ws/payloads/structured-message.hpp"
|
||||
#include "twitch-eventsub-ws/payloads/subscription.hpp"
|
||||
#include "twitch-eventsub-ws/string.hpp"
|
||||
|
||||
#include <boost/json.hpp>
|
||||
|
||||
namespace chatterino::eventsub::lib::payload::channel_chat_user_message_hold::
|
||||
v1 {
|
||||
|
||||
// {"subscription":{"id":"94ab19f1-dd41-41e7-a5bc-f8f45bd4342a","status":"enabled","type":"channel.chat.user_message_hold","version":"1","condition":{"broadcaster_user_id":"117166826","user_id":"159849156"},"transport":{"method":"websocket","session_id":"AgoQP515xn0yRxGvyU29dY9grBIGY2VsbC1j"},"created_at":"2025-03-01T10:40:14.527982426Z","cost":0},"event":{"broadcaster_user_id":"117166826","broadcaster_user_login":"testaccount_420","broadcaster_user_name":"테스트계정420","user_id":"159849156","user_login":"bajlada","user_name":"BajLada","message_id":"e39e3c58-3d25-49fd-8c94-e776ef57a7f8","message":{"text":"penis penis penis","fragments":[{"type":"text","text":"penis penis penis","cheermote":null,"emote":null}]}}}
|
||||
|
||||
struct Event {
|
||||
// Broadcaster of the channel the message was sent in
|
||||
String broadcasterUserID;
|
||||
String broadcasterUserLogin;
|
||||
String broadcasterUserName;
|
||||
|
||||
// User who sent the message
|
||||
String userID;
|
||||
String userLogin;
|
||||
String userName;
|
||||
|
||||
String messageID;
|
||||
chat::Message message;
|
||||
};
|
||||
|
||||
struct Payload {
|
||||
subscription::Subscription subscription;
|
||||
|
||||
Event event;
|
||||
};
|
||||
|
||||
#include "twitch-eventsub-ws/payloads/channel-chat-user-message-hold-v1.inc"
|
||||
|
||||
} // namespace chatterino::eventsub::lib::payload::channel_chat_user_message_hold::v1
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
||||
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot);
|
||||
|
||||
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
||||
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include "twitch-eventsub-ws/payloads/structured-message.hpp"
|
||||
#include "twitch-eventsub-ws/payloads/subscription.hpp"
|
||||
#include "twitch-eventsub-ws/string.hpp"
|
||||
|
||||
#include <boost/json.hpp>
|
||||
|
||||
namespace chatterino::eventsub::lib::payload::channel_chat_user_message_update::
|
||||
v1 {
|
||||
|
||||
// message approved:
|
||||
// {"subscription":{"id":"3a9fd3ec-f5b5-49d7-8624-2195b3ebfda9","status":"enabled","type":"channel.chat.user_message_update","version":"1","condition":{"broadcaster_user_id":"117166826","user_id":"159849156"},"transport":{"method":"websocket","session_id":"AgoQBFnyoXrLQxiCmkHTvJ8VNBIGY2VsbC1j"},"created_at":"2025-03-01T11:29:49.532375428Z","cost":0},"event":{"broadcaster_user_id":"117166826","broadcaster_user_login":"testaccount_420","broadcaster_user_name":"테스트계정420","user_id":"159849156","user_login":"bajlada","user_name":"BajLada","status":"approved","message_id":"5686ec52-e02c-4042-ac21-1dfd8cab0f9f","message":{"text":"penis ass penis","fragments":[{"type":"text","text":"penis ass penis","cheermote":null,"emote":null}]}}}
|
||||
|
||||
// message denied:
|
||||
// {"subscription":{"id":"3a9fd3ec-f5b5-49d7-8624-2195b3ebfda9","status":"enabled","type":"channel.chat.user_message_update","version":"1","condition":{"broadcaster_user_id":"117166826","user_id":"159849156"},"transport":{"method":"websocket","session_id":"AgoQBFnyoXrLQxiCmkHTvJ8VNBIGY2VsbC1j"},"created_at":"2025-03-01T11:29:49.532375428Z","cost":0},"event":{"broadcaster_user_id":"117166826","broadcaster_user_login":"testaccount_420","broadcaster_user_name":"테스트계정420","user_id":"159849156","user_login":"bajlada","user_name":"BajLada","status":"denied","message_id":"fc90dc3b-0634-41c3-8365-f40719f076ab","message":{"text":"penis ass penis","fragments":[{"type":"text","text":"penis ","cheermote":null,"emote":null},{"type":"text","text":"ass penis","cheermote":null,"emote":null}]}}}
|
||||
|
||||
enum class Status : std::uint8_t {
|
||||
Approved,
|
||||
Denied,
|
||||
Invalid,
|
||||
};
|
||||
|
||||
struct Event {
|
||||
// Broadcaster of the channel the message was sent in
|
||||
String broadcasterUserID;
|
||||
String broadcasterUserLogin;
|
||||
String broadcasterUserName;
|
||||
|
||||
// User who sent the message
|
||||
String userID;
|
||||
String userLogin;
|
||||
String userName;
|
||||
|
||||
Status status;
|
||||
|
||||
String messageID;
|
||||
chat::Message message;
|
||||
};
|
||||
|
||||
struct Payload {
|
||||
subscription::Subscription subscription;
|
||||
|
||||
Event event;
|
||||
};
|
||||
|
||||
#include "twitch-eventsub-ws/payloads/channel-chat-user-message-update-v1.inc"
|
||||
|
||||
} // namespace chatterino::eventsub::lib::payload::channel_chat_user_message_update::v1
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
boost::json::result_for<Status, boost::json::value>::type tag_invoke(
|
||||
boost::json::try_value_to_tag<Status>, const boost::json::value &jvRoot);
|
||||
|
||||
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
||||
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot);
|
||||
|
||||
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
||||
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
||||
@@ -12,6 +12,8 @@ generate_json_impls(
|
||||
include/twitch-eventsub-ws/payloads/channel-ban-v1.hpp
|
||||
include/twitch-eventsub-ws/payloads/channel-chat-message-v1.hpp
|
||||
include/twitch-eventsub-ws/payloads/channel-chat-notification-v1.hpp
|
||||
include/twitch-eventsub-ws/payloads/channel-chat-user-message-hold-v1.hpp
|
||||
include/twitch-eventsub-ws/payloads/channel-chat-user-message-update-v1.hpp
|
||||
include/twitch-eventsub-ws/payloads/channel-moderate-v2.hpp
|
||||
include/twitch-eventsub-ws/payloads/channel-suspicious-user-message-v1.hpp
|
||||
include/twitch-eventsub-ws/payloads/channel-suspicious-user-update-v1.hpp
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
// WARNING: This file is automatically generated. Any changes will be lost.
|
||||
#include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep
|
||||
#include "twitch-eventsub-ws/detail/errors.hpp"
|
||||
#include "twitch-eventsub-ws/detail/variant.hpp" // IWYU pragma: keep
|
||||
#include "twitch-eventsub-ws/payloads/channel-chat-user-message-hold-v1.hpp"
|
||||
|
||||
#include <boost/json.hpp>
|
||||
|
||||
namespace chatterino::eventsub::lib::payload::channel_chat_user_message_hold::
|
||||
v1 {
|
||||
|
||||
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
||||
boost::json::try_value_to_tag<Event> /* tag */,
|
||||
const boost::json::value &jvRoot)
|
||||
{
|
||||
if (!jvRoot.is_object())
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::ExpectedObject);
|
||||
}
|
||||
const auto &root = jvRoot.get_object();
|
||||
|
||||
const auto *jvbroadcasterUserID = root.if_contains("broadcaster_user_id");
|
||||
if (jvbroadcasterUserID == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto broadcasterUserID =
|
||||
boost::json::try_value_to<String>(*jvbroadcasterUserID);
|
||||
|
||||
if (broadcasterUserID.has_error())
|
||||
{
|
||||
return broadcasterUserID.error();
|
||||
}
|
||||
|
||||
const auto *jvbroadcasterUserLogin =
|
||||
root.if_contains("broadcaster_user_login");
|
||||
if (jvbroadcasterUserLogin == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto broadcasterUserLogin =
|
||||
boost::json::try_value_to<String>(*jvbroadcasterUserLogin);
|
||||
|
||||
if (broadcasterUserLogin.has_error())
|
||||
{
|
||||
return broadcasterUserLogin.error();
|
||||
}
|
||||
|
||||
const auto *jvbroadcasterUserName =
|
||||
root.if_contains("broadcaster_user_name");
|
||||
if (jvbroadcasterUserName == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto broadcasterUserName =
|
||||
boost::json::try_value_to<String>(*jvbroadcasterUserName);
|
||||
|
||||
if (broadcasterUserName.has_error())
|
||||
{
|
||||
return broadcasterUserName.error();
|
||||
}
|
||||
|
||||
const auto *jvuserID = root.if_contains("user_id");
|
||||
if (jvuserID == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto userID = boost::json::try_value_to<String>(*jvuserID);
|
||||
|
||||
if (userID.has_error())
|
||||
{
|
||||
return userID.error();
|
||||
}
|
||||
|
||||
const auto *jvuserLogin = root.if_contains("user_login");
|
||||
if (jvuserLogin == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto userLogin = boost::json::try_value_to<String>(*jvuserLogin);
|
||||
|
||||
if (userLogin.has_error())
|
||||
{
|
||||
return userLogin.error();
|
||||
}
|
||||
|
||||
const auto *jvuserName = root.if_contains("user_name");
|
||||
if (jvuserName == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto userName = boost::json::try_value_to<String>(*jvuserName);
|
||||
|
||||
if (userName.has_error())
|
||||
{
|
||||
return userName.error();
|
||||
}
|
||||
|
||||
const auto *jvmessageID = root.if_contains("message_id");
|
||||
if (jvmessageID == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto messageID = boost::json::try_value_to<String>(*jvmessageID);
|
||||
|
||||
if (messageID.has_error())
|
||||
{
|
||||
return messageID.error();
|
||||
}
|
||||
|
||||
const auto *jvmessage = root.if_contains("message");
|
||||
if (jvmessage == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto message = boost::json::try_value_to<chat::Message>(*jvmessage);
|
||||
|
||||
if (message.has_error())
|
||||
{
|
||||
return message.error();
|
||||
}
|
||||
|
||||
return Event{
|
||||
.broadcasterUserID = std::move(broadcasterUserID.value()),
|
||||
.broadcasterUserLogin = std::move(broadcasterUserLogin.value()),
|
||||
.broadcasterUserName = std::move(broadcasterUserName.value()),
|
||||
.userID = std::move(userID.value()),
|
||||
.userLogin = std::move(userLogin.value()),
|
||||
.userName = std::move(userName.value()),
|
||||
.messageID = std::move(messageID.value()),
|
||||
.message = std::move(message.value()),
|
||||
};
|
||||
}
|
||||
|
||||
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
||||
boost::json::try_value_to_tag<Payload> /* tag */,
|
||||
const boost::json::value &jvRoot)
|
||||
{
|
||||
if (!jvRoot.is_object())
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::ExpectedObject);
|
||||
}
|
||||
const auto &root = jvRoot.get_object();
|
||||
|
||||
const auto *jvsubscription = root.if_contains("subscription");
|
||||
if (jvsubscription == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto subscription =
|
||||
boost::json::try_value_to<subscription::Subscription>(*jvsubscription);
|
||||
|
||||
if (subscription.has_error())
|
||||
{
|
||||
return subscription.error();
|
||||
}
|
||||
|
||||
const auto *jvevent = root.if_contains("event");
|
||||
if (jvevent == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto event = boost::json::try_value_to<Event>(*jvevent);
|
||||
|
||||
if (event.has_error())
|
||||
{
|
||||
return event.error();
|
||||
}
|
||||
|
||||
return Payload{
|
||||
.subscription = std::move(subscription.value()),
|
||||
.event = std::move(event.value()),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace chatterino::eventsub::lib::payload::channel_chat_user_message_hold::v1
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
// WARNING: This file is automatically generated. Any changes will be lost.
|
||||
#include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep
|
||||
#include "twitch-eventsub-ws/detail/errors.hpp"
|
||||
#include "twitch-eventsub-ws/detail/variant.hpp" // IWYU pragma: keep
|
||||
#include "twitch-eventsub-ws/payloads/channel-chat-user-message-update-v1.hpp"
|
||||
|
||||
#include <boost/json.hpp>
|
||||
|
||||
namespace chatterino::eventsub::lib::payload::channel_chat_user_message_update::
|
||||
v1 {
|
||||
|
||||
boost::json::result_for<Status, boost::json::value>::type tag_invoke(
|
||||
boost::json::try_value_to_tag<Status> /* tag */,
|
||||
const boost::json::value &jvRoot)
|
||||
{
|
||||
if (!jvRoot.is_string())
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::ExpectedString);
|
||||
}
|
||||
std::string_view eString(jvRoot.get_string());
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
if (eString == "approved"sv)
|
||||
{
|
||||
return Status::Approved;
|
||||
}
|
||||
if (eString == "denied"sv)
|
||||
{
|
||||
return Status::Denied;
|
||||
}
|
||||
if (eString == "invalid"sv)
|
||||
{
|
||||
return Status::Invalid;
|
||||
}
|
||||
EVENTSUB_BAIL_HERE(error::Kind::UnknownEnumValue);
|
||||
}
|
||||
|
||||
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
||||
boost::json::try_value_to_tag<Event> /* tag */,
|
||||
const boost::json::value &jvRoot)
|
||||
{
|
||||
if (!jvRoot.is_object())
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::ExpectedObject);
|
||||
}
|
||||
const auto &root = jvRoot.get_object();
|
||||
|
||||
const auto *jvbroadcasterUserID = root.if_contains("broadcaster_user_id");
|
||||
if (jvbroadcasterUserID == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto broadcasterUserID =
|
||||
boost::json::try_value_to<String>(*jvbroadcasterUserID);
|
||||
|
||||
if (broadcasterUserID.has_error())
|
||||
{
|
||||
return broadcasterUserID.error();
|
||||
}
|
||||
|
||||
const auto *jvbroadcasterUserLogin =
|
||||
root.if_contains("broadcaster_user_login");
|
||||
if (jvbroadcasterUserLogin == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto broadcasterUserLogin =
|
||||
boost::json::try_value_to<String>(*jvbroadcasterUserLogin);
|
||||
|
||||
if (broadcasterUserLogin.has_error())
|
||||
{
|
||||
return broadcasterUserLogin.error();
|
||||
}
|
||||
|
||||
const auto *jvbroadcasterUserName =
|
||||
root.if_contains("broadcaster_user_name");
|
||||
if (jvbroadcasterUserName == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto broadcasterUserName =
|
||||
boost::json::try_value_to<String>(*jvbroadcasterUserName);
|
||||
|
||||
if (broadcasterUserName.has_error())
|
||||
{
|
||||
return broadcasterUserName.error();
|
||||
}
|
||||
|
||||
const auto *jvuserID = root.if_contains("user_id");
|
||||
if (jvuserID == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto userID = boost::json::try_value_to<String>(*jvuserID);
|
||||
|
||||
if (userID.has_error())
|
||||
{
|
||||
return userID.error();
|
||||
}
|
||||
|
||||
const auto *jvuserLogin = root.if_contains("user_login");
|
||||
if (jvuserLogin == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto userLogin = boost::json::try_value_to<String>(*jvuserLogin);
|
||||
|
||||
if (userLogin.has_error())
|
||||
{
|
||||
return userLogin.error();
|
||||
}
|
||||
|
||||
const auto *jvuserName = root.if_contains("user_name");
|
||||
if (jvuserName == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto userName = boost::json::try_value_to<String>(*jvuserName);
|
||||
|
||||
if (userName.has_error())
|
||||
{
|
||||
return userName.error();
|
||||
}
|
||||
|
||||
static_assert(
|
||||
std::is_trivially_copyable_v<
|
||||
std::remove_reference_t<decltype(std::declval<Event>().status)>>);
|
||||
const auto *jvstatus = root.if_contains("status");
|
||||
if (jvstatus == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto status = boost::json::try_value_to<Status>(*jvstatus);
|
||||
|
||||
if (status.has_error())
|
||||
{
|
||||
return status.error();
|
||||
}
|
||||
|
||||
const auto *jvmessageID = root.if_contains("message_id");
|
||||
if (jvmessageID == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto messageID = boost::json::try_value_to<String>(*jvmessageID);
|
||||
|
||||
if (messageID.has_error())
|
||||
{
|
||||
return messageID.error();
|
||||
}
|
||||
|
||||
const auto *jvmessage = root.if_contains("message");
|
||||
if (jvmessage == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto message = boost::json::try_value_to<chat::Message>(*jvmessage);
|
||||
|
||||
if (message.has_error())
|
||||
{
|
||||
return message.error();
|
||||
}
|
||||
|
||||
return Event{
|
||||
.broadcasterUserID = std::move(broadcasterUserID.value()),
|
||||
.broadcasterUserLogin = std::move(broadcasterUserLogin.value()),
|
||||
.broadcasterUserName = std::move(broadcasterUserName.value()),
|
||||
.userID = std::move(userID.value()),
|
||||
.userLogin = std::move(userLogin.value()),
|
||||
.userName = std::move(userName.value()),
|
||||
.status = status.value(),
|
||||
.messageID = std::move(messageID.value()),
|
||||
.message = std::move(message.value()),
|
||||
};
|
||||
}
|
||||
|
||||
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
||||
boost::json::try_value_to_tag<Payload> /* tag */,
|
||||
const boost::json::value &jvRoot)
|
||||
{
|
||||
if (!jvRoot.is_object())
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::ExpectedObject);
|
||||
}
|
||||
const auto &root = jvRoot.get_object();
|
||||
|
||||
const auto *jvsubscription = root.if_contains("subscription");
|
||||
if (jvsubscription == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto subscription =
|
||||
boost::json::try_value_to<subscription::Subscription>(*jvsubscription);
|
||||
|
||||
if (subscription.has_error())
|
||||
{
|
||||
return subscription.error();
|
||||
}
|
||||
|
||||
const auto *jvevent = root.if_contains("event");
|
||||
if (jvevent == nullptr)
|
||||
{
|
||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||
}
|
||||
|
||||
auto event = boost::json::try_value_to<Event>(*jvevent);
|
||||
|
||||
if (event.has_error())
|
||||
{
|
||||
return event.error();
|
||||
}
|
||||
|
||||
return Payload{
|
||||
.subscription = std::move(subscription.value()),
|
||||
.event = std::move(event.value()),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace chatterino::eventsub::lib::payload::channel_chat_user_message_update::v1
|
||||
@@ -212,6 +212,32 @@ namespace {
|
||||
return boost::system::error_code{};
|
||||
},
|
||||
},
|
||||
{
|
||||
{"channel.chat.user_message_hold", "1"},
|
||||
[](const auto &metadata, const auto &jv, auto &listener) {
|
||||
auto oPayload = parsePayload<
|
||||
payload::channel_chat_user_message_hold::v1::Payload>(jv);
|
||||
if (!oPayload)
|
||||
{
|
||||
return oPayload.error();
|
||||
}
|
||||
listener->onChannelChatUserMessageHold(metadata, *oPayload);
|
||||
return boost::system::error_code{};
|
||||
},
|
||||
},
|
||||
{
|
||||
{"channel.chat.user_message_update", "1"},
|
||||
[](const auto &metadata, const auto &jv, auto &listener) {
|
||||
auto oPayload = parsePayload<
|
||||
payload::channel_chat_user_message_update::v1::Payload>(jv);
|
||||
if (!oPayload)
|
||||
{
|
||||
return oPayload.error();
|
||||
}
|
||||
listener->onChannelChatUserMessageUpdate(metadata, *oPayload);
|
||||
return boost::system::error_code{};
|
||||
},
|
||||
},
|
||||
// Add your new subscription types above this line
|
||||
};
|
||||
|
||||
|
||||
@@ -129,6 +129,18 @@ class NoOpListener : public chatterino::eventsub::lib::Listener
|
||||
override
|
||||
{
|
||||
}
|
||||
void onChannelChatUserMessageHold(
|
||||
const messages::Metadata &metadata,
|
||||
const payload::channel_chat_user_message_hold::v1::Payload &payload)
|
||||
override
|
||||
{
|
||||
}
|
||||
void onChannelChatUserMessageUpdate(
|
||||
const messages::Metadata &metadata,
|
||||
const payload::channel_chat_user_message_update::v1::Payload &payload)
|
||||
override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -1554,6 +1554,9 @@ void TwitchChannel::refreshPubSub()
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
this->eventSubChannelChatUserMessageHoldHandle.reset();
|
||||
this->eventSubChannelChatUserMessageUpdateHandle.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1561,7 +1564,42 @@ void TwitchChannel::refreshPubSub()
|
||||
this->eventSubAutomodMessageHoldHandle.reset();
|
||||
this->eventSubAutomodMessageUpdateHandle.reset();
|
||||
this->eventSubSuspiciousUserMessageHandle.reset();
|
||||
|
||||
this->eventSubChannelChatUserMessageHoldHandle =
|
||||
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
||||
.subscriptionType = "channel.chat.user_message_hold",
|
||||
.subscriptionVersion = "1",
|
||||
.conditions =
|
||||
{
|
||||
{
|
||||
"broadcaster_user_id",
|
||||
roomId,
|
||||
},
|
||||
{
|
||||
"user_id",
|
||||
currentAccount->getUserId(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
this->eventSubChannelChatUserMessageUpdateHandle =
|
||||
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
||||
.subscriptionType = "channel.chat.user_message_update",
|
||||
.subscriptionVersion = "1",
|
||||
.conditions =
|
||||
{
|
||||
{
|
||||
"broadcaster_user_id",
|
||||
roomId,
|
||||
},
|
||||
{
|
||||
"user_id",
|
||||
currentAccount->getUserId(),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getApp()->getTwitchPubSub()->listenToChannelPointRewards(roomId);
|
||||
}
|
||||
|
||||
|
||||
@@ -512,6 +512,8 @@ private:
|
||||
eventsub::SubscriptionHandle eventSubAutomodMessageHoldHandle;
|
||||
eventsub::SubscriptionHandle eventSubAutomodMessageUpdateHandle;
|
||||
eventsub::SubscriptionHandle eventSubSuspiciousUserMessageHandle;
|
||||
eventsub::SubscriptionHandle eventSubChannelChatUserMessageHoldHandle;
|
||||
eventsub::SubscriptionHandle eventSubChannelChatUserMessageUpdateHandle;
|
||||
|
||||
friend class TwitchIrcServer;
|
||||
friend class MessageBuilder;
|
||||
|
||||
@@ -305,6 +305,57 @@ void Connection::onChannelSuspiciousUserUpdate(
|
||||
<< payload.event.broadcasterUserLogin.qt();
|
||||
}
|
||||
|
||||
void Connection::onChannelChatUserMessageHold(
|
||||
const lib::messages::Metadata &metadata,
|
||||
const lib::payload::channel_chat_user_message_hold::v1::Payload &payload)
|
||||
{
|
||||
auto *channel = dynamic_cast<TwitchChannel *>(
|
||||
getApp()
|
||||
->getTwitch()
|
||||
->getChannelOrEmpty(payload.event.broadcasterUserLogin.qt())
|
||||
.get());
|
||||
if (!channel || channel->isEmpty())
|
||||
{
|
||||
qCDebug(LOG) << "Channel Chat User Message Hold for broadcaster we're "
|
||||
"not interested in"
|
||||
<< payload.event.broadcasterUserLogin.qt();
|
||||
return;
|
||||
}
|
||||
|
||||
auto time = chronoToQDateTime(metadata.messageTimestamp);
|
||||
auto message = makeUserMessageHeldMessage(channel, time, payload.event);
|
||||
|
||||
runInGuiThread([channel, message] {
|
||||
channel->addMessage(message, MessageContext::Original);
|
||||
});
|
||||
}
|
||||
|
||||
void Connection::onChannelChatUserMessageUpdate(
|
||||
const lib::messages::Metadata &metadata,
|
||||
const lib::payload::channel_chat_user_message_update::v1::Payload &payload)
|
||||
{
|
||||
auto *channel = dynamic_cast<TwitchChannel *>(
|
||||
getApp()
|
||||
->getTwitch()
|
||||
->getChannelOrEmpty(payload.event.broadcasterUserLogin.qt())
|
||||
.get());
|
||||
if (!channel || channel->isEmpty())
|
||||
{
|
||||
qCDebug(LOG)
|
||||
<< "Channel Chat User Message Update for broadcaster we're "
|
||||
"not interested in"
|
||||
<< payload.event.broadcasterUserLogin.qt();
|
||||
return;
|
||||
}
|
||||
|
||||
auto time = chronoToQDateTime(metadata.messageTimestamp);
|
||||
auto message = makeUserMessageUpdateMessage(channel, time, payload.event);
|
||||
|
||||
runInGuiThread([channel, message] {
|
||||
channel->addMessage(message, MessageContext::Original);
|
||||
});
|
||||
}
|
||||
|
||||
QString Connection::getSessionID() const
|
||||
{
|
||||
return this->sessionID;
|
||||
|
||||
@@ -71,6 +71,16 @@ public:
|
||||
const lib::payload::channel_suspicious_user_update::v1::Payload
|
||||
&payload) override;
|
||||
|
||||
void onChannelChatUserMessageHold(
|
||||
const lib::messages::Metadata &metadata,
|
||||
const lib::payload::channel_chat_user_message_hold::v1::Payload
|
||||
&payload) override;
|
||||
|
||||
void onChannelChatUserMessageUpdate(
|
||||
const lib::messages::Metadata &metadata,
|
||||
const lib::payload::channel_chat_user_message_update::v1::Payload
|
||||
&payload) override;
|
||||
|
||||
QString getSessionID() const;
|
||||
|
||||
bool isSubscribedTo(const SubscriptionRequest &request) const;
|
||||
|
||||
@@ -756,4 +756,86 @@ MessagePtr makeSuspiciousUserMessageBody(
|
||||
return builder.release();
|
||||
}
|
||||
|
||||
MessagePtr makeUserMessageHeldMessage(
|
||||
TwitchChannel *channel, const QDateTime &time,
|
||||
const lib::payload::channel_chat_user_message_hold::v1::Event &event)
|
||||
{
|
||||
QString text("AutoMod: Hey! Your message is being checked by mods and has "
|
||||
"not been sent.");
|
||||
EventSubMessageBuilder builder(channel);
|
||||
builder->serverReceivedTime = time;
|
||||
builder->id = u"automod_" % event.messageID.qt();
|
||||
builder->loginName = u"automod"_s;
|
||||
builder->channelName = event.broadcasterUserLogin.qt();
|
||||
builder->flags.set(MessageFlag::PubSub, MessageFlag::Timeout,
|
||||
MessageFlag::AutoMod);
|
||||
|
||||
// AutoMod shield badge
|
||||
builder.emplace<BadgeElement>(makeAutoModBadge(),
|
||||
MessageElementFlag::BadgeChannelAuthority);
|
||||
// AutoMod "username"
|
||||
builder.emplace<TextElement>("AutoMod:", MessageElementFlag::Text,
|
||||
QColor(0, 0, 255), FontStyle::ChatMediumBold);
|
||||
builder.emplace<TextElement>(
|
||||
"Hey! Your message is being checked by mods and has not been sent.",
|
||||
MessageElementFlag::Text, MessageColor::Text);
|
||||
|
||||
builder->messageText = text;
|
||||
builder->searchText = text;
|
||||
|
||||
return builder.release();
|
||||
}
|
||||
|
||||
MessagePtr makeUserMessageUpdateMessage(
|
||||
TwitchChannel *channel, const QDateTime &time,
|
||||
const lib::payload::channel_chat_user_message_update::v1::Event &event)
|
||||
{
|
||||
using lib::payload::channel_chat_user_message_update::v1::Status;
|
||||
|
||||
QString text("AutoMod: ");
|
||||
EventSubMessageBuilder builder(channel);
|
||||
builder->serverReceivedTime = time;
|
||||
builder->id = u"automod_" % event.messageID.qt();
|
||||
builder->loginName = u"automod"_s;
|
||||
builder->channelName = event.broadcasterUserLogin.qt();
|
||||
builder->flags.set(MessageFlag::PubSub, MessageFlag::Timeout,
|
||||
MessageFlag::AutoMod);
|
||||
|
||||
// AutoMod shield badge
|
||||
builder.emplace<BadgeElement>(makeAutoModBadge(),
|
||||
MessageElementFlag::BadgeChannelAuthority);
|
||||
// AutoMod "username"
|
||||
builder.emplace<TextElement>("AutoMod:", MessageElementFlag::Text,
|
||||
QColor(0, 0, 255), FontStyle::ChatMediumBold);
|
||||
|
||||
switch (event.status)
|
||||
{
|
||||
case Status::Approved:
|
||||
text += "Mods have accepted your message.";
|
||||
builder.emplace<TextElement>("Mods have accepted your message.",
|
||||
MessageElementFlag::Text,
|
||||
MessageColor::Text);
|
||||
break;
|
||||
|
||||
case Status::Denied:
|
||||
text += "Mods have denied your message.";
|
||||
builder.emplace<TextElement>("Mods have denied your message.",
|
||||
MessageElementFlag::Text,
|
||||
MessageColor::Text);
|
||||
break;
|
||||
|
||||
case Status::Invalid:
|
||||
text += "Your message was lost in the void.";
|
||||
builder.emplace<TextElement>("Your message was lost in the void.",
|
||||
MessageElementFlag::Text,
|
||||
MessageColor::Text);
|
||||
break;
|
||||
}
|
||||
|
||||
builder->messageText = text;
|
||||
builder->searchText = text;
|
||||
|
||||
return builder.release();
|
||||
}
|
||||
|
||||
} // namespace chatterino::eventsub
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
#include "twitch-eventsub-ws/payloads/automod-message-hold-v2.hpp"
|
||||
#include "twitch-eventsub-ws/payloads/channel-chat-user-message-hold-v1.hpp"
|
||||
#include "twitch-eventsub-ws/payloads/channel-chat-user-message-update-v1.hpp"
|
||||
#include "twitch-eventsub-ws/payloads/channel-moderate-v2.hpp"
|
||||
#include "twitch-eventsub-ws/payloads/channel-suspicious-user-message-v1.hpp"
|
||||
|
||||
@@ -165,4 +167,12 @@ MessagePtr makeSuspiciousUserMessageBody(
|
||||
TwitchChannel *channel, const QDateTime &time,
|
||||
const lib::payload::channel_suspicious_user_message::v1::Event &event);
|
||||
|
||||
MessagePtr makeUserMessageHeldMessage(
|
||||
TwitchChannel *channel, const QDateTime &time,
|
||||
const lib::payload::channel_chat_user_message_hold::v1::Event &event);
|
||||
|
||||
MessagePtr makeUserMessageUpdateMessage(
|
||||
TwitchChannel *channel, const QDateTime &time,
|
||||
const lib::payload::channel_chat_user_message_update::v1::Event &event);
|
||||
|
||||
} // namespace chatterino::eventsub
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"input": {
|
||||
"broadcaster_user_id": "11148817",
|
||||
"broadcaster_user_login": "pajlada",
|
||||
"broadcaster_user_name": "pajlada",
|
||||
"message": {
|
||||
"fragments": [
|
||||
{
|
||||
"cheermote": null,
|
||||
"emote": null,
|
||||
"text": "penis penis penis",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"text": "penis penis penis"
|
||||
},
|
||||
"message_id": "e39e3c58-3d25-49fd-8c94-e776ef57a7f8",
|
||||
"user_id": "159849156",
|
||||
"user_login": "bajlada",
|
||||
"user_name": "BajLada"
|
||||
},
|
||||
"output": [
|
||||
{
|
||||
"badgeInfos": {
|
||||
},
|
||||
"badges": [
|
||||
],
|
||||
"channelName": "pajlada",
|
||||
"count": 1,
|
||||
"displayName": "",
|
||||
"elements": [
|
||||
{
|
||||
"emote": {
|
||||
"homePage": "https://dashboard.twitch.tv/settings/moderation/automod",
|
||||
"images": {
|
||||
"1x": ""
|
||||
},
|
||||
"name": "",
|
||||
"tooltip": "AutoMod"
|
||||
},
|
||||
"flags": "BadgeChannelAuthority",
|
||||
"link": {
|
||||
"type": "None",
|
||||
"value": ""
|
||||
},
|
||||
"tooltip": "AutoMod",
|
||||
"trailingSpace": true,
|
||||
"type": "BadgeElement"
|
||||
},
|
||||
{
|
||||
"color": "#ff0000ff",
|
||||
"flags": "Text",
|
||||
"link": {
|
||||
"type": "None",
|
||||
"value": ""
|
||||
},
|
||||
"style": "ChatMediumBold",
|
||||
"tooltip": "",
|
||||
"trailingSpace": true,
|
||||
"type": "TextElement",
|
||||
"words": [
|
||||
"AutoMod:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"color": "Text",
|
||||
"flags": "Text",
|
||||
"link": {
|
||||
"type": "None",
|
||||
"value": ""
|
||||
},
|
||||
"style": "ChatMedium",
|
||||
"tooltip": "",
|
||||
"trailingSpace": true,
|
||||
"type": "TextElement",
|
||||
"words": [
|
||||
"Hey!",
|
||||
"Your",
|
||||
"message",
|
||||
"is",
|
||||
"being",
|
||||
"checked",
|
||||
"by",
|
||||
"mods",
|
||||
"and",
|
||||
"has",
|
||||
"not",
|
||||
"been",
|
||||
"sent."
|
||||
]
|
||||
}
|
||||
],
|
||||
"flags": "Timeout|PubSub|AutoMod|EventSub",
|
||||
"id": "automod_e39e3c58-3d25-49fd-8c94-e776ef57a7f8",
|
||||
"localizedName": "",
|
||||
"loginName": "automod",
|
||||
"messageText": "AutoMod: Hey! Your message is being checked by mods and has not been sent.",
|
||||
"searchText": "AutoMod: Hey! Your message is being checked by mods and has not been sent.",
|
||||
"serverReceivedTime": "2024-05-14T12:31:47Z",
|
||||
"timeoutUser": "",
|
||||
"userID": "",
|
||||
"usernameColor": "#ff000000"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"input": {
|
||||
"broadcaster_user_id": "11148817",
|
||||
"broadcaster_user_login": "pajlada",
|
||||
"broadcaster_user_name": "pajlada",
|
||||
"message": {
|
||||
"fragments": [
|
||||
{
|
||||
"cheermote": null,
|
||||
"emote": null,
|
||||
"text": "penis ass penis",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"text": "penis ass penis"
|
||||
},
|
||||
"message_id": "5686ec52-e02c-4042-ac21-1dfd8cab0f9f",
|
||||
"status": "approved",
|
||||
"user_id": "159849156",
|
||||
"user_login": "bajlada",
|
||||
"user_name": "BajLada"
|
||||
},
|
||||
"output": [
|
||||
{
|
||||
"badgeInfos": {
|
||||
},
|
||||
"badges": [
|
||||
],
|
||||
"channelName": "pajlada",
|
||||
"count": 1,
|
||||
"displayName": "",
|
||||
"elements": [
|
||||
{
|
||||
"emote": {
|
||||
"homePage": "https://dashboard.twitch.tv/settings/moderation/automod",
|
||||
"images": {
|
||||
"1x": ""
|
||||
},
|
||||
"name": "",
|
||||
"tooltip": "AutoMod"
|
||||
},
|
||||
"flags": "BadgeChannelAuthority",
|
||||
"link": {
|
||||
"type": "None",
|
||||
"value": ""
|
||||
},
|
||||
"tooltip": "AutoMod",
|
||||
"trailingSpace": true,
|
||||
"type": "BadgeElement"
|
||||
},
|
||||
{
|
||||
"color": "#ff0000ff",
|
||||
"flags": "Text",
|
||||
"link": {
|
||||
"type": "None",
|
||||
"value": ""
|
||||
},
|
||||
"style": "ChatMediumBold",
|
||||
"tooltip": "",
|
||||
"trailingSpace": true,
|
||||
"type": "TextElement",
|
||||
"words": [
|
||||
"AutoMod:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"color": "Text",
|
||||
"flags": "Text",
|
||||
"link": {
|
||||
"type": "None",
|
||||
"value": ""
|
||||
},
|
||||
"style": "ChatMedium",
|
||||
"tooltip": "",
|
||||
"trailingSpace": true,
|
||||
"type": "TextElement",
|
||||
"words": [
|
||||
"Mods",
|
||||
"have",
|
||||
"accepted",
|
||||
"your",
|
||||
"message."
|
||||
]
|
||||
}
|
||||
],
|
||||
"flags": "Timeout|PubSub|AutoMod|EventSub",
|
||||
"id": "automod_5686ec52-e02c-4042-ac21-1dfd8cab0f9f",
|
||||
"localizedName": "",
|
||||
"loginName": "automod",
|
||||
"messageText": "AutoMod: Mods have accepted your message.",
|
||||
"searchText": "AutoMod: Mods have accepted your message.",
|
||||
"serverReceivedTime": "2024-05-14T12:31:47Z",
|
||||
"timeoutUser": "",
|
||||
"userID": "",
|
||||
"usernameColor": "#ff000000"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"input": {
|
||||
"broadcaster_user_id": "11148817",
|
||||
"broadcaster_user_login": "pajlada",
|
||||
"broadcaster_user_name": "pajlada",
|
||||
"message": {
|
||||
"fragments": [
|
||||
{
|
||||
"cheermote": null,
|
||||
"emote": null,
|
||||
"text": "penis ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"cheermote": null,
|
||||
"emote": null,
|
||||
"text": "ass penis",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"text": "penis ass penis"
|
||||
},
|
||||
"message_id": "fc90dc3b-0634-41c3-8365-f40719f076ab",
|
||||
"status": "denied",
|
||||
"user_id": "159849156",
|
||||
"user_login": "bajlada",
|
||||
"user_name": "BajLada"
|
||||
},
|
||||
"output": [
|
||||
{
|
||||
"badgeInfos": {
|
||||
},
|
||||
"badges": [
|
||||
],
|
||||
"channelName": "pajlada",
|
||||
"count": 1,
|
||||
"displayName": "",
|
||||
"elements": [
|
||||
{
|
||||
"emote": {
|
||||
"homePage": "https://dashboard.twitch.tv/settings/moderation/automod",
|
||||
"images": {
|
||||
"1x": ""
|
||||
},
|
||||
"name": "",
|
||||
"tooltip": "AutoMod"
|
||||
},
|
||||
"flags": "BadgeChannelAuthority",
|
||||
"link": {
|
||||
"type": "None",
|
||||
"value": ""
|
||||
},
|
||||
"tooltip": "AutoMod",
|
||||
"trailingSpace": true,
|
||||
"type": "BadgeElement"
|
||||
},
|
||||
{
|
||||
"color": "#ff0000ff",
|
||||
"flags": "Text",
|
||||
"link": {
|
||||
"type": "None",
|
||||
"value": ""
|
||||
},
|
||||
"style": "ChatMediumBold",
|
||||
"tooltip": "",
|
||||
"trailingSpace": true,
|
||||
"type": "TextElement",
|
||||
"words": [
|
||||
"AutoMod:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"color": "Text",
|
||||
"flags": "Text",
|
||||
"link": {
|
||||
"type": "None",
|
||||
"value": ""
|
||||
},
|
||||
"style": "ChatMedium",
|
||||
"tooltip": "",
|
||||
"trailingSpace": true,
|
||||
"type": "TextElement",
|
||||
"words": [
|
||||
"Mods",
|
||||
"have",
|
||||
"denied",
|
||||
"your",
|
||||
"message."
|
||||
]
|
||||
}
|
||||
],
|
||||
"flags": "Timeout|PubSub|AutoMod|EventSub",
|
||||
"id": "automod_fc90dc3b-0634-41c3-8365-f40719f076ab",
|
||||
"localizedName": "",
|
||||
"loginName": "automod",
|
||||
"messageText": "AutoMod: Mods have denied your message.",
|
||||
"searchText": "AutoMod: Mods have denied your message.",
|
||||
"serverReceivedTime": "2024-05-14T12:31:47Z",
|
||||
"timeoutUser": "",
|
||||
"userID": "",
|
||||
"usernameColor": "#ff000000"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -120,6 +120,44 @@ const std::map<QString, std::string_view, QCompareCaseInsensitive>
|
||||
"cost": 0
|
||||
})",
|
||||
},
|
||||
{
|
||||
"channel-chat-user-message-hold",
|
||||
R"({
|
||||
"id": "a3122e32-6498-4847-8675-109b9b94f29c",
|
||||
"status": "enabled",
|
||||
"type": "channel.chat.user_message_hold",
|
||||
"version": "1",
|
||||
"condition": {
|
||||
"broadcaster_user_id": "11148817",
|
||||
"moderator_user_id": "489584266"
|
||||
},
|
||||
"transport": {
|
||||
"method":"websocket",
|
||||
"session_id":"AgoQ59RRLw0mS6S000QtK8f54BIGY2VsbC1j"
|
||||
},
|
||||
"created_at": "2025-02-28T15:55:37.85489173Z",
|
||||
"cost": 0
|
||||
})",
|
||||
},
|
||||
{
|
||||
"channel-chat-user-message-update",
|
||||
R"({
|
||||
"id": "a3122e32-6498-4847-8675-109b9b94f29c",
|
||||
"status": "enabled",
|
||||
"type": "channel.chat.user_message_update",
|
||||
"version": "1",
|
||||
"condition": {
|
||||
"broadcaster_user_id": "11148817",
|
||||
"moderator_user_id": "489584266"
|
||||
},
|
||||
"transport": {
|
||||
"method":"websocket",
|
||||
"session_id":"AgoQ59RRLw0mS6S000QtK8f54BIGY2VsbC1j"
|
||||
},
|
||||
"created_at": "2025-02-28T15:55:37.85489173Z",
|
||||
"cost": 0
|
||||
})",
|
||||
},
|
||||
};
|
||||
|
||||
class MockApplication : public mock::BaseApplication
|
||||
|
||||
Reference in New Issue
Block a user