feat(eventsub): implement suspicious user message (#6007)
This commit is contained in:
+1
-1
@@ -30,7 +30,7 @@
|
|||||||
- Bugfix: Fixed the input font not immediately updating when zooming in/out. (#5960)
|
- Bugfix: Fixed the input font not immediately updating when zooming in/out. (#5960)
|
||||||
- Bugfix: Fixed color input thinking blue is also red. (#5982)
|
- Bugfix: Fixed color input thinking blue is also red. (#5982)
|
||||||
- Dev: Subscriptions to PubSub channel points redemption topics now use no auth token, making it continue to work during PubSub shutdown. (#5947)
|
- 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)
|
- 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)
|
||||||
- Dev: Remove unneeded platform specifier for toasts. (#5914)
|
- Dev: Remove unneeded platform specifier for toasts. (#5914)
|
||||||
- Dev: Highlight checks now use non-capturing groups for the boundaries. (#5784)
|
- Dev: Highlight checks now use non-capturing groups for the boundaries. (#5784)
|
||||||
- Dev: Removed unused PubSub whisper code. (#5898)
|
- Dev: Removed unused PubSub whisper code. (#5898)
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ class CommentCommands:
|
|||||||
|
|
||||||
inner_root: str = ""
|
inner_root: str = ""
|
||||||
|
|
||||||
|
default: Optional[str] = None
|
||||||
|
|
||||||
def __init__(self, parent: Optional["CommentCommands"] = None) -> None:
|
def __init__(self, parent: Optional["CommentCommands"] = None) -> None:
|
||||||
if parent is not None:
|
if parent is not None:
|
||||||
self.name_transform = parent.name_transform
|
self.name_transform = parent.name_transform
|
||||||
@@ -59,6 +61,8 @@ class CommentCommands:
|
|||||||
pass
|
pass
|
||||||
case "json_tag":
|
case "json_tag":
|
||||||
self.tag = value
|
self.tag = value
|
||||||
|
case "default":
|
||||||
|
self.default = value
|
||||||
case other:
|
case other:
|
||||||
log.warning(f"Unknown comment command found: {other} with value {value}")
|
log.warning(f"Unknown comment command found: {other} with value {value}")
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import List
|
from typing import List, Optional
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@@ -20,6 +20,7 @@ class Enum:
|
|||||||
self.comment_commands = CommentCommands()
|
self.comment_commands = CommentCommands()
|
||||||
self.inner_root: str = ""
|
self.inner_root: str = ""
|
||||||
self.namespace = namespace
|
self.namespace = namespace
|
||||||
|
self.default: Optional[str] = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def full_name(self) -> str:
|
def full_name(self) -> str:
|
||||||
@@ -48,3 +49,4 @@ class Enum:
|
|||||||
|
|
||||||
def apply_comment_commands(self, comment_commands: CommentCommands) -> None:
|
def apply_comment_commands(self, comment_commands: CommentCommands) -> None:
|
||||||
self.inner_root = comment_commands.inner_root
|
self.inner_root = comment_commands.inner_root
|
||||||
|
self.default = comment_commands.default
|
||||||
|
|||||||
@@ -15,5 +15,9 @@ boost::json::result_for<{{enum.full_name}}, boost::json::value>::type tag_invoke
|
|||||||
}
|
}
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
|
|
||||||
|
{%- if enum.default -%}
|
||||||
|
return {{enum.full_name}}::{{enum.default}};
|
||||||
|
{%- else -%}
|
||||||
EVENTSUB_BAIL_HERE(error::Kind::UnknownEnumValue);
|
EVENTSUB_BAIL_HERE(error::Kind::UnknownEnumValue);
|
||||||
|
{%- endif -%}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-15
@@ -2,35 +2,31 @@
|
|||||||
|
|
||||||
#include "twitch-eventsub-ws/payloads/structured-message.hpp"
|
#include "twitch-eventsub-ws/payloads/structured-message.hpp"
|
||||||
#include "twitch-eventsub-ws/payloads/subscription.hpp"
|
#include "twitch-eventsub-ws/payloads/subscription.hpp"
|
||||||
|
#include "twitch-eventsub-ws/payloads/suspicious-users.hpp"
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace chatterino::eventsub::lib::payload::channel_suspicious_user_message::
|
namespace chatterino::eventsub::lib::payload::channel_suspicious_user_message::
|
||||||
v1 {
|
v1 {
|
||||||
|
|
||||||
struct Event {
|
struct Event {
|
||||||
// Broadcaster of the channel the message was sent in
|
// Broadcaster of the channel the message was sent in
|
||||||
std::string broadcasterUserID;
|
String broadcasterUserID;
|
||||||
std::string broadcasterUserLogin;
|
String broadcasterUserLogin;
|
||||||
std::string broadcasterUserName;
|
String broadcasterUserName;
|
||||||
|
|
||||||
// User who sent the message
|
// User who sent the message
|
||||||
std::string userID;
|
String userID;
|
||||||
std::string userLogin;
|
String userLogin;
|
||||||
std::string userName;
|
String userName;
|
||||||
|
|
||||||
// "none", "active_monitoring", "restricted"
|
suspicious_users::Status lowTrustStatus;
|
||||||
std::string lowTrustStatus;
|
|
||||||
|
|
||||||
std::vector<std::string> sharedBanChannelIds;
|
std::vector<String> sharedBanChannelIds;
|
||||||
|
|
||||||
// "manual", "ban_evader_detector", or "shared_channel_ban"
|
std::vector<suspicious_users::Type> types;
|
||||||
std::vector<std::string> types;
|
|
||||||
|
|
||||||
// "unknown", "possible", "likely"
|
suspicious_users::BanEvasionEvaluation banEvasionEvaluation;
|
||||||
std::string banEvasionEvaluation;
|
|
||||||
// this event also has the ID in this message (hopefully we don't need it)
|
// this event also has the ID in this message (hopefully we don't need it)
|
||||||
chat::Message message;
|
chat::Message message;
|
||||||
};
|
};
|
||||||
|
|||||||
+12
-13
@@ -1,32 +1,31 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "twitch-eventsub-ws/payloads/subscription.hpp"
|
#include "twitch-eventsub-ws/payloads/subscription.hpp"
|
||||||
|
#include "twitch-eventsub-ws/payloads/suspicious-users.hpp"
|
||||||
|
#include "twitch-eventsub-ws/string.hpp"
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace chatterino::eventsub::lib::payload::channel_suspicious_user_update::
|
namespace chatterino::eventsub::lib::payload::channel_suspicious_user_update::
|
||||||
v1 {
|
v1 {
|
||||||
|
|
||||||
struct Event {
|
struct Event {
|
||||||
// Broadcaster of the channel
|
// Broadcaster of the channel
|
||||||
std::string broadcasterUserID;
|
String broadcasterUserID;
|
||||||
std::string broadcasterUserLogin;
|
String broadcasterUserLogin;
|
||||||
std::string broadcasterUserName;
|
String broadcasterUserName;
|
||||||
|
|
||||||
// Affected user
|
// Affected user
|
||||||
std::string userID;
|
String userID;
|
||||||
std::string userLogin;
|
String userLogin;
|
||||||
std::string userName;
|
String userName;
|
||||||
|
|
||||||
// Moderator who updated the user
|
// Moderator who updated the user
|
||||||
std::string moderatorUserID;
|
String moderatorUserID;
|
||||||
std::string moderatorUserLogin;
|
String moderatorUserLogin;
|
||||||
std::string moderatorUserName;
|
String moderatorUserName;
|
||||||
|
|
||||||
// "none", "active_monitoring", or "restricted"
|
suspicious_users::Status lowTrustStatus;
|
||||||
std::string lowTrustStatus;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Payload {
|
struct Payload {
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace chatterino::eventsub::lib::suspicious_users {
|
||||||
|
|
||||||
|
/// default=None
|
||||||
|
enum class Status : std::uint8_t {
|
||||||
|
None,
|
||||||
|
ActiveMonitoring,
|
||||||
|
Restricted,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// default=Unknown
|
||||||
|
enum class Type : std::uint8_t {
|
||||||
|
Unknown,
|
||||||
|
Manual,
|
||||||
|
BanEvaderDetector,
|
||||||
|
SharedChannelBan
|
||||||
|
};
|
||||||
|
|
||||||
|
/// default=Unknown
|
||||||
|
enum class BanEvasionEvaluation : std::uint8_t {
|
||||||
|
Unknown,
|
||||||
|
Possible,
|
||||||
|
Likely,
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "twitch-eventsub-ws/payloads/suspicious-users.inc"
|
||||||
|
|
||||||
|
} // namespace chatterino::eventsub::lib::suspicious_users
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
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<Type, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Type>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<BanEvasionEvaluation, boost::json::value>::type
|
||||||
|
tag_invoke(boost::json::try_value_to_tag<BanEvasionEvaluation>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
@@ -21,6 +21,7 @@ generate_json_impls(
|
|||||||
include/twitch-eventsub-ws/payloads/stream-online-v1.hpp
|
include/twitch-eventsub-ws/payloads/stream-online-v1.hpp
|
||||||
include/twitch-eventsub-ws/payloads/structured-message.hpp
|
include/twitch-eventsub-ws/payloads/structured-message.hpp
|
||||||
include/twitch-eventsub-ws/payloads/subscription.hpp
|
include/twitch-eventsub-ws/payloads/subscription.hpp
|
||||||
|
include/twitch-eventsub-ws/payloads/suspicious-users.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
|
|||||||
+23
-17
@@ -26,7 +26,7 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto broadcasterUserID =
|
auto broadcasterUserID =
|
||||||
boost::json::try_value_to<std::string>(*jvbroadcasterUserID);
|
boost::json::try_value_to<String>(*jvbroadcasterUserID);
|
||||||
|
|
||||||
if (broadcasterUserID.has_error())
|
if (broadcasterUserID.has_error())
|
||||||
{
|
{
|
||||||
@@ -41,7 +41,7 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto broadcasterUserLogin =
|
auto broadcasterUserLogin =
|
||||||
boost::json::try_value_to<std::string>(*jvbroadcasterUserLogin);
|
boost::json::try_value_to<String>(*jvbroadcasterUserLogin);
|
||||||
|
|
||||||
if (broadcasterUserLogin.has_error())
|
if (broadcasterUserLogin.has_error())
|
||||||
{
|
{
|
||||||
@@ -56,7 +56,7 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto broadcasterUserName =
|
auto broadcasterUserName =
|
||||||
boost::json::try_value_to<std::string>(*jvbroadcasterUserName);
|
boost::json::try_value_to<String>(*jvbroadcasterUserName);
|
||||||
|
|
||||||
if (broadcasterUserName.has_error())
|
if (broadcasterUserName.has_error())
|
||||||
{
|
{
|
||||||
@@ -69,7 +69,7 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto userID = boost::json::try_value_to<std::string>(*jvuserID);
|
auto userID = boost::json::try_value_to<String>(*jvuserID);
|
||||||
|
|
||||||
if (userID.has_error())
|
if (userID.has_error())
|
||||||
{
|
{
|
||||||
@@ -82,7 +82,7 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto userLogin = boost::json::try_value_to<std::string>(*jvuserLogin);
|
auto userLogin = boost::json::try_value_to<String>(*jvuserLogin);
|
||||||
|
|
||||||
if (userLogin.has_error())
|
if (userLogin.has_error())
|
||||||
{
|
{
|
||||||
@@ -95,13 +95,15 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto userName = boost::json::try_value_to<std::string>(*jvuserName);
|
auto userName = boost::json::try_value_to<String>(*jvuserName);
|
||||||
|
|
||||||
if (userName.has_error())
|
if (userName.has_error())
|
||||||
{
|
{
|
||||||
return userName.error();
|
return userName.error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static_assert(std::is_trivially_copyable_v<std::remove_reference_t<
|
||||||
|
decltype(std::declval<Event>().lowTrustStatus)>>);
|
||||||
const auto *jvlowTrustStatus = root.if_contains("low_trust_status");
|
const auto *jvlowTrustStatus = root.if_contains("low_trust_status");
|
||||||
if (jvlowTrustStatus == nullptr)
|
if (jvlowTrustStatus == nullptr)
|
||||||
{
|
{
|
||||||
@@ -109,21 +111,21 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto lowTrustStatus =
|
auto lowTrustStatus =
|
||||||
boost::json::try_value_to<std::string>(*jvlowTrustStatus);
|
boost::json::try_value_to<suspicious_users::Status>(*jvlowTrustStatus);
|
||||||
|
|
||||||
if (lowTrustStatus.has_error())
|
if (lowTrustStatus.has_error())
|
||||||
{
|
{
|
||||||
return lowTrustStatus.error();
|
return lowTrustStatus.error();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> vsharedBanChannelIds;
|
std::vector<chatterino::eventsub::lib::String> vsharedBanChannelIds;
|
||||||
const auto *jvsharedBanChannelIds =
|
const auto *jvsharedBanChannelIds =
|
||||||
root.if_contains("shared_ban_channel_ids");
|
root.if_contains("shared_ban_channel_ids");
|
||||||
if (jvsharedBanChannelIds != nullptr && !jvsharedBanChannelIds->is_null())
|
if (jvsharedBanChannelIds != nullptr && !jvsharedBanChannelIds->is_null())
|
||||||
{
|
{
|
||||||
auto sharedBanChannelIds =
|
auto sharedBanChannelIds = boost::json::try_value_to<
|
||||||
boost::json::try_value_to<std::vector<std::string>>(
|
std::vector<chatterino::eventsub::lib::String>>(
|
||||||
*jvsharedBanChannelIds);
|
*jvsharedBanChannelIds);
|
||||||
if (sharedBanChannelIds.has_error())
|
if (sharedBanChannelIds.has_error())
|
||||||
{
|
{
|
||||||
return sharedBanChannelIds.error();
|
return sharedBanChannelIds.error();
|
||||||
@@ -134,12 +136,13 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> vtypes;
|
std::vector<chatterino::eventsub::lib::suspicious_users::Type> vtypes;
|
||||||
const auto *jvtypes = root.if_contains("types");
|
const auto *jvtypes = root.if_contains("types");
|
||||||
if (jvtypes != nullptr && !jvtypes->is_null())
|
if (jvtypes != nullptr && !jvtypes->is_null())
|
||||||
{
|
{
|
||||||
auto types =
|
auto types = boost::json::try_value_to<
|
||||||
boost::json::try_value_to<std::vector<std::string>>(*jvtypes);
|
std::vector<chatterino::eventsub::lib::suspicious_users::Type>>(
|
||||||
|
*jvtypes);
|
||||||
if (types.has_error())
|
if (types.has_error())
|
||||||
{
|
{
|
||||||
return types.error();
|
return types.error();
|
||||||
@@ -149,6 +152,8 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
vtypes = std::move(types.value());
|
vtypes = std::move(types.value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
static_assert(std::is_trivially_copyable_v<std::remove_reference_t<
|
||||||
|
decltype(std::declval<Event>().banEvasionEvaluation)>>);
|
||||||
const auto *jvbanEvasionEvaluation =
|
const auto *jvbanEvasionEvaluation =
|
||||||
root.if_contains("ban_evasion_evaluation");
|
root.if_contains("ban_evasion_evaluation");
|
||||||
if (jvbanEvasionEvaluation == nullptr)
|
if (jvbanEvasionEvaluation == nullptr)
|
||||||
@@ -157,7 +162,8 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto banEvasionEvaluation =
|
auto banEvasionEvaluation =
|
||||||
boost::json::try_value_to<std::string>(*jvbanEvasionEvaluation);
|
boost::json::try_value_to<suspicious_users::BanEvasionEvaluation>(
|
||||||
|
*jvbanEvasionEvaluation);
|
||||||
|
|
||||||
if (banEvasionEvaluation.has_error())
|
if (banEvasionEvaluation.has_error())
|
||||||
{
|
{
|
||||||
@@ -184,10 +190,10 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
.userID = std::move(userID.value()),
|
.userID = std::move(userID.value()),
|
||||||
.userLogin = std::move(userLogin.value()),
|
.userLogin = std::move(userLogin.value()),
|
||||||
.userName = std::move(userName.value()),
|
.userName = std::move(userName.value()),
|
||||||
.lowTrustStatus = std::move(lowTrustStatus.value()),
|
.lowTrustStatus = lowTrustStatus.value(),
|
||||||
.sharedBanChannelIds = std::move(vsharedBanChannelIds),
|
.sharedBanChannelIds = std::move(vsharedBanChannelIds),
|
||||||
.types = std::move(vtypes),
|
.types = std::move(vtypes),
|
||||||
.banEvasionEvaluation = std::move(banEvasionEvaluation.value()),
|
.banEvasionEvaluation = banEvasionEvaluation.value(),
|
||||||
.message = std::move(message.value()),
|
.message = std::move(message.value()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-11
@@ -26,7 +26,7 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto broadcasterUserID =
|
auto broadcasterUserID =
|
||||||
boost::json::try_value_to<std::string>(*jvbroadcasterUserID);
|
boost::json::try_value_to<String>(*jvbroadcasterUserID);
|
||||||
|
|
||||||
if (broadcasterUserID.has_error())
|
if (broadcasterUserID.has_error())
|
||||||
{
|
{
|
||||||
@@ -41,7 +41,7 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto broadcasterUserLogin =
|
auto broadcasterUserLogin =
|
||||||
boost::json::try_value_to<std::string>(*jvbroadcasterUserLogin);
|
boost::json::try_value_to<String>(*jvbroadcasterUserLogin);
|
||||||
|
|
||||||
if (broadcasterUserLogin.has_error())
|
if (broadcasterUserLogin.has_error())
|
||||||
{
|
{
|
||||||
@@ -56,7 +56,7 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto broadcasterUserName =
|
auto broadcasterUserName =
|
||||||
boost::json::try_value_to<std::string>(*jvbroadcasterUserName);
|
boost::json::try_value_to<String>(*jvbroadcasterUserName);
|
||||||
|
|
||||||
if (broadcasterUserName.has_error())
|
if (broadcasterUserName.has_error())
|
||||||
{
|
{
|
||||||
@@ -69,7 +69,7 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto userID = boost::json::try_value_to<std::string>(*jvuserID);
|
auto userID = boost::json::try_value_to<String>(*jvuserID);
|
||||||
|
|
||||||
if (userID.has_error())
|
if (userID.has_error())
|
||||||
{
|
{
|
||||||
@@ -82,7 +82,7 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto userLogin = boost::json::try_value_to<std::string>(*jvuserLogin);
|
auto userLogin = boost::json::try_value_to<String>(*jvuserLogin);
|
||||||
|
|
||||||
if (userLogin.has_error())
|
if (userLogin.has_error())
|
||||||
{
|
{
|
||||||
@@ -95,7 +95,7 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto userName = boost::json::try_value_to<std::string>(*jvuserName);
|
auto userName = boost::json::try_value_to<String>(*jvuserName);
|
||||||
|
|
||||||
if (userName.has_error())
|
if (userName.has_error())
|
||||||
{
|
{
|
||||||
@@ -109,7 +109,7 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto moderatorUserID =
|
auto moderatorUserID =
|
||||||
boost::json::try_value_to<std::string>(*jvmoderatorUserID);
|
boost::json::try_value_to<String>(*jvmoderatorUserID);
|
||||||
|
|
||||||
if (moderatorUserID.has_error())
|
if (moderatorUserID.has_error())
|
||||||
{
|
{
|
||||||
@@ -123,7 +123,7 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto moderatorUserLogin =
|
auto moderatorUserLogin =
|
||||||
boost::json::try_value_to<std::string>(*jvmoderatorUserLogin);
|
boost::json::try_value_to<String>(*jvmoderatorUserLogin);
|
||||||
|
|
||||||
if (moderatorUserLogin.has_error())
|
if (moderatorUserLogin.has_error())
|
||||||
{
|
{
|
||||||
@@ -137,13 +137,15 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto moderatorUserName =
|
auto moderatorUserName =
|
||||||
boost::json::try_value_to<std::string>(*jvmoderatorUserName);
|
boost::json::try_value_to<String>(*jvmoderatorUserName);
|
||||||
|
|
||||||
if (moderatorUserName.has_error())
|
if (moderatorUserName.has_error())
|
||||||
{
|
{
|
||||||
return moderatorUserName.error();
|
return moderatorUserName.error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static_assert(std::is_trivially_copyable_v<std::remove_reference_t<
|
||||||
|
decltype(std::declval<Event>().lowTrustStatus)>>);
|
||||||
const auto *jvlowTrustStatus = root.if_contains("low_trust_status");
|
const auto *jvlowTrustStatus = root.if_contains("low_trust_status");
|
||||||
if (jvlowTrustStatus == nullptr)
|
if (jvlowTrustStatus == nullptr)
|
||||||
{
|
{
|
||||||
@@ -151,7 +153,7 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto lowTrustStatus =
|
auto lowTrustStatus =
|
||||||
boost::json::try_value_to<std::string>(*jvlowTrustStatus);
|
boost::json::try_value_to<suspicious_users::Status>(*jvlowTrustStatus);
|
||||||
|
|
||||||
if (lowTrustStatus.has_error())
|
if (lowTrustStatus.has_error())
|
||||||
{
|
{
|
||||||
@@ -168,7 +170,7 @@ boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|||||||
.moderatorUserID = std::move(moderatorUserID.value()),
|
.moderatorUserID = std::move(moderatorUserID.value()),
|
||||||
.moderatorUserLogin = std::move(moderatorUserLogin.value()),
|
.moderatorUserLogin = std::move(moderatorUserLogin.value()),
|
||||||
.moderatorUserName = std::move(moderatorUserName.value()),
|
.moderatorUserName = std::move(moderatorUserName.value()),
|
||||||
.lowTrustStatus = std::move(lowTrustStatus.value()),
|
.lowTrustStatus = lowTrustStatus.value(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
// 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/suspicious-users.hpp"
|
||||||
|
|
||||||
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
|
namespace chatterino::eventsub::lib::suspicious_users {
|
||||||
|
|
||||||
|
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 == "none"sv)
|
||||||
|
{
|
||||||
|
return Status::None;
|
||||||
|
}
|
||||||
|
if (eString == "active_monitoring"sv)
|
||||||
|
{
|
||||||
|
return Status::ActiveMonitoring;
|
||||||
|
}
|
||||||
|
if (eString == "restricted"sv)
|
||||||
|
{
|
||||||
|
return Status::Restricted;
|
||||||
|
}
|
||||||
|
return Status::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::json::result_for<Type, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Type> /* 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 == "unknown"sv)
|
||||||
|
{
|
||||||
|
return Type::Unknown;
|
||||||
|
}
|
||||||
|
if (eString == "manual"sv)
|
||||||
|
{
|
||||||
|
return Type::Manual;
|
||||||
|
}
|
||||||
|
if (eString == "ban_evader_detector"sv)
|
||||||
|
{
|
||||||
|
return Type::BanEvaderDetector;
|
||||||
|
}
|
||||||
|
if (eString == "shared_channel_ban"sv)
|
||||||
|
{
|
||||||
|
return Type::SharedChannelBan;
|
||||||
|
}
|
||||||
|
return Type::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::json::result_for<BanEvasionEvaluation, boost::json::value>::type
|
||||||
|
tag_invoke(boost::json::try_value_to_tag<BanEvasionEvaluation> /* 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 == "unknown"sv)
|
||||||
|
{
|
||||||
|
return BanEvasionEvaluation::Unknown;
|
||||||
|
}
|
||||||
|
if (eString == "possible"sv)
|
||||||
|
{
|
||||||
|
return BanEvasionEvaluation::Possible;
|
||||||
|
}
|
||||||
|
if (eString == "likely"sv)
|
||||||
|
{
|
||||||
|
return BanEvasionEvaluation::Likely;
|
||||||
|
}
|
||||||
|
return BanEvasionEvaluation::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino::eventsub::lib::suspicious_users
|
||||||
@@ -1522,10 +1522,27 @@ void TwitchChannel::refreshPubSub()
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
this->eventSubSuspiciousUserMessageHandle =
|
||||||
|
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
|
||||||
|
.subscriptionType = "channel.suspicious_user.message",
|
||||||
|
.subscriptionVersion = "1",
|
||||||
|
.conditions =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"broadcaster_user_id",
|
||||||
|
roomId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"moderator_user_id",
|
||||||
|
currentAccount->getUserId(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this->eventSubChannelModerateHandle.reset();
|
this->eventSubChannelModerateHandle.reset();
|
||||||
|
this->eventSubSuspiciousUserMessageHandle.reset();
|
||||||
}
|
}
|
||||||
getApp()->getTwitchPubSub()->listenToChannelPointRewards(roomId);
|
getApp()->getTwitchPubSub()->listenToChannelPointRewards(roomId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -510,6 +510,7 @@ private:
|
|||||||
|
|
||||||
eventsub::SubscriptionHandle eventSubChannelModerateHandle;
|
eventsub::SubscriptionHandle eventSubChannelModerateHandle;
|
||||||
eventsub::SubscriptionHandle eventSubAutomodMessageHoldHandle;
|
eventsub::SubscriptionHandle eventSubAutomodMessageHoldHandle;
|
||||||
|
eventsub::SubscriptionHandle eventSubSuspiciousUserMessageHandle;
|
||||||
|
|
||||||
friend class TwitchIrcServer;
|
friend class TwitchIrcServer;
|
||||||
friend class MessageBuilder;
|
friend class MessageBuilder;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "providers/twitch/TwitchChannel.hpp"
|
#include "providers/twitch/TwitchChannel.hpp"
|
||||||
#include "providers/twitch/TwitchIrcServer.hpp"
|
#include "providers/twitch/TwitchIrcServer.hpp"
|
||||||
#include "singletons/Settings.hpp"
|
#include "singletons/Settings.hpp"
|
||||||
|
#include "singletons/StreamerMode.hpp"
|
||||||
#include "singletons/WindowManager.hpp"
|
#include "singletons/WindowManager.hpp"
|
||||||
#include "util/Helpers.hpp"
|
#include "util/Helpers.hpp"
|
||||||
#include "util/PostToThread.hpp"
|
#include "util/PostToThread.hpp"
|
||||||
@@ -243,9 +244,40 @@ void Connection::onChannelSuspiciousUserMessage(
|
|||||||
const lib::messages::Metadata &metadata,
|
const lib::messages::Metadata &metadata,
|
||||||
const lib::payload::channel_suspicious_user_message::v1::Payload &payload)
|
const lib::payload::channel_suspicious_user_message::v1::Payload &payload)
|
||||||
{
|
{
|
||||||
(void)metadata;
|
// monitored chats are received over irc; in the future, we will use eventsub instead
|
||||||
qCDebug(LOG) << "On channel suspicious user message for"
|
if (payload.event.lowTrustStatus !=
|
||||||
<< payload.event.broadcasterUserLogin.c_str();
|
lib::suspicious_users::Status::Restricted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getSettings()->streamerModeHideModActions &&
|
||||||
|
getApp()->getStreamerMode()->isEnabled())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *channel = dynamic_cast<TwitchChannel *>(
|
||||||
|
getApp()
|
||||||
|
->getTwitch()
|
||||||
|
->getChannelOrEmpty(payload.event.broadcasterUserLogin.qt())
|
||||||
|
.get());
|
||||||
|
if (!channel || channel->isEmpty())
|
||||||
|
{
|
||||||
|
qCDebug(LOG)
|
||||||
|
<< "Suspicious message for broadcaster we're not interested in"
|
||||||
|
<< payload.event.broadcasterUserLogin.qt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto time = chronoToQDateTime(metadata.messageTimestamp);
|
||||||
|
auto header = makeSuspiciousUserMessageHeader(channel, time, payload.event);
|
||||||
|
auto body = makeSuspiciousUserMessageBody(channel, time, payload.event);
|
||||||
|
|
||||||
|
runInGuiThread([channel, header, body] {
|
||||||
|
channel->addMessage(header, MessageContext::Original);
|
||||||
|
channel->addMessage(body, MessageContext::Original);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::onChannelSuspiciousUserUpdate(
|
void Connection::onChannelSuspiciousUserUpdate(
|
||||||
@@ -254,7 +286,7 @@ void Connection::onChannelSuspiciousUserUpdate(
|
|||||||
{
|
{
|
||||||
(void)metadata;
|
(void)metadata;
|
||||||
qCDebug(LOG) << "On channel suspicious user update for"
|
qCDebug(LOG) << "On channel suspicious user update for"
|
||||||
<< payload.event.broadcasterUserLogin.c_str();
|
<< payload.event.broadcasterUserLogin.qt();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Connection::getSessionID() const
|
QString Connection::getSessionID() const
|
||||||
|
|||||||
@@ -637,6 +637,7 @@ MessagePtr makeAutomodHoldMessageBody(
|
|||||||
displayName + ':', event.userLogin.qt(), MessageColor::Text,
|
displayName + ':', event.userLogin.qt(), MessageColor::Text,
|
||||||
channel->getUserColor(event.userLogin.qt()));
|
channel->getUserColor(event.userLogin.qt()));
|
||||||
// sender's message caught by AutoMod
|
// sender's message caught by AutoMod
|
||||||
|
// XXX: add the structured message here
|
||||||
builder.emplace<TextElement>(event.message.text.qt(),
|
builder.emplace<TextElement>(event.message.text.qt(),
|
||||||
MessageElementFlag::Text, MessageColor::Text);
|
MessageElementFlag::Text, MessageColor::Text);
|
||||||
auto text = displayName % u": " % event.message.text.qt();
|
auto text = displayName % u": " % event.message.text.qt();
|
||||||
@@ -646,4 +647,113 @@ MessagePtr makeAutomodHoldMessageBody(
|
|||||||
return builder.release();
|
return builder.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MessagePtr makeSuspiciousUserMessageHeader(
|
||||||
|
TwitchChannel *channel, const QDateTime &time,
|
||||||
|
const lib::payload::channel_suspicious_user_message::v1::Event &event)
|
||||||
|
{
|
||||||
|
EventSubMessageBuilder builder(channel);
|
||||||
|
|
||||||
|
// Builder for low trust user message with explanation
|
||||||
|
builder->channelName = event.broadcasterUserLogin.qt();
|
||||||
|
builder->serverReceivedTime = time;
|
||||||
|
builder->flags.set(MessageFlag::LowTrustUsers);
|
||||||
|
|
||||||
|
// AutoMod shield badge
|
||||||
|
builder.emplace<BadgeElement>(makeAutoModBadge(),
|
||||||
|
MessageElementFlag::BadgeChannelAuthority);
|
||||||
|
|
||||||
|
// Suspicious user header message
|
||||||
|
QString prefix = u"Suspicious User:"_s;
|
||||||
|
builder.emplace<TextElement>(prefix, MessageElementFlag::Text,
|
||||||
|
MessageColor(QColor(0, 0, 255)),
|
||||||
|
FontStyle::ChatMediumBold);
|
||||||
|
|
||||||
|
QString headerMessage;
|
||||||
|
if (event.lowTrustStatus == lib::suspicious_users::Status::Restricted)
|
||||||
|
{
|
||||||
|
headerMessage = u"Restricted"_s;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
headerMessage = u"Monitored"_s;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto hasType = [&](lib::suspicious_users::Type type) {
|
||||||
|
return std::ranges::find(event.types, type) != event.types.end();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hasType(lib::suspicious_users::Type::BanEvaderDetector))
|
||||||
|
{
|
||||||
|
QString evader;
|
||||||
|
if (event.banEvasionEvaluation ==
|
||||||
|
lib::suspicious_users::BanEvasionEvaluation::Likely)
|
||||||
|
{
|
||||||
|
evader = u"likely"_s;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
evader = u"possible"_s;
|
||||||
|
}
|
||||||
|
|
||||||
|
headerMessage += u". Detected as " % evader % " ban evader";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasType(lib::suspicious_users::Type::SharedChannelBan))
|
||||||
|
{
|
||||||
|
headerMessage += u". Banned in " %
|
||||||
|
QString::number(event.sharedBanChannelIds.size()) %
|
||||||
|
u" shared channels";
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.emplace<TextElement>(headerMessage, MessageElementFlag::Text,
|
||||||
|
MessageColor::Text);
|
||||||
|
builder->messageText = prefix % u" " % headerMessage;
|
||||||
|
builder->searchText = prefix % u" " % headerMessage;
|
||||||
|
|
||||||
|
return builder.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
MessagePtr makeSuspiciousUserMessageBody(
|
||||||
|
TwitchChannel *channel, const QDateTime &time,
|
||||||
|
const lib::payload::channel_suspicious_user_message::v1::Event &event)
|
||||||
|
{
|
||||||
|
EventSubMessageBuilder builder(channel);
|
||||||
|
builder->channelName = event.broadcasterUserLogin.qt();
|
||||||
|
builder->serverReceivedTime = time;
|
||||||
|
if (event.lowTrustStatus == lib::suspicious_users::Status::Restricted)
|
||||||
|
{
|
||||||
|
builder->flags.set(MessageFlag::RestrictedMessage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder->flags.set(MessageFlag::MonitoredMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder
|
||||||
|
.emplace<TextElement>(u'#' + event.broadcasterUserLogin.qt(),
|
||||||
|
MessageElementFlag::ChannelName,
|
||||||
|
MessageColor::System)
|
||||||
|
->setLink({Link::JumpToChannel, event.broadcasterUserLogin.qt()});
|
||||||
|
builder.emplace<TimestampElement>(time.time());
|
||||||
|
builder.emplace<TwitchModerationElement>();
|
||||||
|
builder->loginName = event.userLogin.qt();
|
||||||
|
builder->flags.set(MessageFlag::PubSub, MessageFlag::LowTrustUsers);
|
||||||
|
|
||||||
|
// sender username
|
||||||
|
builder.emplace<MentionElement>(
|
||||||
|
event.userName.qt() + ":", event.userLogin.qt(), MessageColor::Text,
|
||||||
|
channel->getUserColor(event.userLogin.qt()));
|
||||||
|
|
||||||
|
// sender's message caught by AutoMod
|
||||||
|
// XXX: add the structured message here
|
||||||
|
builder.emplace<TextElement>(event.message.text.qt(),
|
||||||
|
MessageElementFlag::Text, MessageColor::Text);
|
||||||
|
|
||||||
|
auto text = event.userName.qt() % u": " % event.message.text.qt();
|
||||||
|
builder.message().messageText = text;
|
||||||
|
builder.message().searchText = text;
|
||||||
|
|
||||||
|
return builder.release();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace chatterino::eventsub
|
} // namespace chatterino::eventsub
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "providers/twitch/TwitchChannel.hpp"
|
#include "providers/twitch/TwitchChannel.hpp"
|
||||||
#include "twitch-eventsub-ws/payloads/automod-message-hold-v2.hpp"
|
#include "twitch-eventsub-ws/payloads/automod-message-hold-v2.hpp"
|
||||||
#include "twitch-eventsub-ws/payloads/channel-moderate-v2.hpp"
|
#include "twitch-eventsub-ws/payloads/channel-moderate-v2.hpp"
|
||||||
|
#include "twitch-eventsub-ws/payloads/channel-suspicious-user-message-v1.hpp"
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
||||||
@@ -156,4 +157,12 @@ MessagePtr makeAutomodHoldMessageBody(
|
|||||||
TwitchChannel *channel, const QDateTime &time,
|
TwitchChannel *channel, const QDateTime &time,
|
||||||
const lib::payload::automod_message_hold::v2::Event &event);
|
const lib::payload::automod_message_hold::v2::Event &event);
|
||||||
|
|
||||||
|
MessagePtr makeSuspiciousUserMessageHeader(
|
||||||
|
TwitchChannel *channel, const QDateTime &time,
|
||||||
|
const lib::payload::channel_suspicious_user_message::v1::Event &event);
|
||||||
|
|
||||||
|
MessagePtr makeSuspiciousUserMessageBody(
|
||||||
|
TwitchChannel *channel, const QDateTime &time,
|
||||||
|
const lib::payload::channel_suspicious_user_message::v1::Event &event);
|
||||||
|
|
||||||
} // namespace chatterino::eventsub
|
} // namespace chatterino::eventsub
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"input": {
|
||||||
|
"ban_evasion_evaluation": "unknown",
|
||||||
|
"broadcaster_user_id": "11148817",
|
||||||
|
"broadcaster_user_login": "pajlada",
|
||||||
|
"broadcaster_user_name": "pajlada",
|
||||||
|
"low_trust_status": "monitored",
|
||||||
|
"message": {
|
||||||
|
"fragments": [
|
||||||
|
{
|
||||||
|
"cheermote": null,
|
||||||
|
"emote": null,
|
||||||
|
"text": "FeelsDankMan",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"message_id": "4715b69c-a147-4ff8-bbb5-7c830544d809",
|
||||||
|
"text": "FeelsDankMan"
|
||||||
|
},
|
||||||
|
"shared_ban_channel_ids": null,
|
||||||
|
"types": [
|
||||||
|
"manually_added"
|
||||||
|
],
|
||||||
|
"user_id": "129546453",
|
||||||
|
"user_login": "nerixyz",
|
||||||
|
"user_name": "nerixyz"
|
||||||
|
},
|
||||||
|
"output": [
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"input": {
|
||||||
|
"ban_evasion_evaluation": "unknown",
|
||||||
|
"broadcaster_user_id": "11148817",
|
||||||
|
"broadcaster_user_login": "pajlada",
|
||||||
|
"broadcaster_user_name": "pajlada",
|
||||||
|
"low_trust_status": "none",
|
||||||
|
"message": {
|
||||||
|
"fragments": [
|
||||||
|
{
|
||||||
|
"cheermote": null,
|
||||||
|
"emote": null,
|
||||||
|
"text": "FeelsDankMan",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"message_id": "4715b69c-a147-4ff8-bbb5-7c830544d809",
|
||||||
|
"text": "FeelsDankMan"
|
||||||
|
},
|
||||||
|
"shared_ban_channel_ids": null,
|
||||||
|
"types": [
|
||||||
|
"manually_added"
|
||||||
|
],
|
||||||
|
"user_id": "129546453",
|
||||||
|
"user_login": "nerixyz",
|
||||||
|
"user_name": "nerixyz"
|
||||||
|
},
|
||||||
|
"output": [
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
{
|
||||||
|
"input": {
|
||||||
|
"ban_evasion_evaluation": "unknown",
|
||||||
|
"broadcaster_user_id": "11148817",
|
||||||
|
"broadcaster_user_login": "pajlada",
|
||||||
|
"broadcaster_user_name": "pajlada",
|
||||||
|
"low_trust_status": "restricted",
|
||||||
|
"message": {
|
||||||
|
"fragments": [
|
||||||
|
{
|
||||||
|
"cheermote": null,
|
||||||
|
"emote": {
|
||||||
|
"emote_set_id": "0",
|
||||||
|
"id": "25"
|
||||||
|
},
|
||||||
|
"text": "Kappa",
|
||||||
|
"type": "emote"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"message_id": "c7acaf8b-6694-4181-be68-5d79464b0001",
|
||||||
|
"text": "Kappa"
|
||||||
|
},
|
||||||
|
"shared_ban_channel_ids": null,
|
||||||
|
"types": [
|
||||||
|
"manually_added"
|
||||||
|
],
|
||||||
|
"user_id": "129546453",
|
||||||
|
"user_login": "nerixyz",
|
||||||
|
"user_name": "nerixyz"
|
||||||
|
},
|
||||||
|
"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": [
|
||||||
|
"Suspicious",
|
||||||
|
"User:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "Text",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"Restricted"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flags": "LowTrustUsers|EventSub",
|
||||||
|
"id": "",
|
||||||
|
"localizedName": "",
|
||||||
|
"loginName": "",
|
||||||
|
"messageText": "Suspicious User: Restricted",
|
||||||
|
"searchText": "Suspicious User: Restricted",
|
||||||
|
"serverReceivedTime": "2024-05-14T12:31:47Z",
|
||||||
|
"timeoutUser": "",
|
||||||
|
"userID": "",
|
||||||
|
"usernameColor": "#ff000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"badgeInfos": {
|
||||||
|
},
|
||||||
|
"badges": [
|
||||||
|
],
|
||||||
|
"channelName": "pajlada",
|
||||||
|
"count": 1,
|
||||||
|
"displayName": "",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "ChannelName",
|
||||||
|
"link": {
|
||||||
|
"type": "JumpToChannel",
|
||||||
|
"value": "pajlada"
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"#pajlada"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"element": {
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"12:31"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"format": "",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"time": "12:31:47",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TimestampElement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flags": "ModeratorTools",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TwitchModerationElement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "Text",
|
||||||
|
"fallbackColor": "Text",
|
||||||
|
"flags": "Text|Mention",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "MentionElement",
|
||||||
|
"userColor": "Text",
|
||||||
|
"userLoginName": "nerixyz",
|
||||||
|
"words": [
|
||||||
|
"nerixyz:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "Text",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"Kappa"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flags": "PubSub|LowTrustUsers|RestrictedMessage|EventSub",
|
||||||
|
"id": "",
|
||||||
|
"localizedName": "",
|
||||||
|
"loginName": "nerixyz",
|
||||||
|
"messageText": "nerixyz: Kappa",
|
||||||
|
"searchText": "nerixyz: Kappa",
|
||||||
|
"serverReceivedTime": "2024-05-14T12:31:47Z",
|
||||||
|
"timeoutUser": "",
|
||||||
|
"userID": "",
|
||||||
|
"usernameColor": "#ff000000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
{
|
||||||
|
"input": {
|
||||||
|
"ban_evasion_evaluation": "unknown",
|
||||||
|
"broadcaster_user_id": "11148817",
|
||||||
|
"broadcaster_user_login": "pajlada",
|
||||||
|
"broadcaster_user_name": "pajlada",
|
||||||
|
"low_trust_status": "restricted",
|
||||||
|
"message": {
|
||||||
|
"fragments": [
|
||||||
|
{
|
||||||
|
"cheermote": null,
|
||||||
|
"emote": null,
|
||||||
|
"text": "FeelsDankMan",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"message_id": "4715b69c-a147-4ff8-bbb5-7c830544d809",
|
||||||
|
"text": "FeelsDankMan"
|
||||||
|
},
|
||||||
|
"shared_ban_channel_ids": null,
|
||||||
|
"types": [
|
||||||
|
"manually_added"
|
||||||
|
],
|
||||||
|
"user_id": "129546453",
|
||||||
|
"user_login": "nerixyz",
|
||||||
|
"user_name": "nerixyz"
|
||||||
|
},
|
||||||
|
"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": [
|
||||||
|
"Suspicious",
|
||||||
|
"User:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "Text",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"Restricted"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flags": "LowTrustUsers|EventSub",
|
||||||
|
"id": "",
|
||||||
|
"localizedName": "",
|
||||||
|
"loginName": "",
|
||||||
|
"messageText": "Suspicious User: Restricted",
|
||||||
|
"searchText": "Suspicious User: Restricted",
|
||||||
|
"serverReceivedTime": "2024-05-14T12:31:47Z",
|
||||||
|
"timeoutUser": "",
|
||||||
|
"userID": "",
|
||||||
|
"usernameColor": "#ff000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"badgeInfos": {
|
||||||
|
},
|
||||||
|
"badges": [
|
||||||
|
],
|
||||||
|
"channelName": "pajlada",
|
||||||
|
"count": 1,
|
||||||
|
"displayName": "",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "ChannelName",
|
||||||
|
"link": {
|
||||||
|
"type": "JumpToChannel",
|
||||||
|
"value": "pajlada"
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"#pajlada"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"element": {
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"12:31"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"format": "",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"time": "12:31:47",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TimestampElement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flags": "ModeratorTools",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TwitchModerationElement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "Text",
|
||||||
|
"fallbackColor": "Text",
|
||||||
|
"flags": "Text|Mention",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "MentionElement",
|
||||||
|
"userColor": "Text",
|
||||||
|
"userLoginName": "nerixyz",
|
||||||
|
"words": [
|
||||||
|
"nerixyz:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "Text",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"FeelsDankMan"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flags": "PubSub|LowTrustUsers|RestrictedMessage|EventSub",
|
||||||
|
"id": "",
|
||||||
|
"localizedName": "",
|
||||||
|
"loginName": "nerixyz",
|
||||||
|
"messageText": "nerixyz: FeelsDankMan",
|
||||||
|
"searchText": "nerixyz: FeelsDankMan",
|
||||||
|
"serverReceivedTime": "2024-05-14T12:31:47Z",
|
||||||
|
"timeoutUser": "",
|
||||||
|
"userID": "",
|
||||||
|
"usernameColor": "#ff000000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -82,6 +82,25 @@ const std::map<QString, std::string_view, QCompareCaseInsensitive>
|
|||||||
"cost": 0
|
"cost": 0
|
||||||
})",
|
})",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"channel-suspicious-user-message",
|
||||||
|
R"({
|
||||||
|
"id": "a3122e32-6498-4847-8675-109b9b94f29c",
|
||||||
|
"status": "enabled",
|
||||||
|
"type": "channel.suspicious_user.message",
|
||||||
|
"version": "1",
|
||||||
|
"condition": {
|
||||||
|
"broadcaster_user_id": "489584266",
|
||||||
|
"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
|
class MockApplication : public mock::BaseApplication
|
||||||
|
|||||||
Reference in New Issue
Block a user