Added more information to logs when receiving subscriptions. Added raid message spam prevention. Added bit message detection - requires tts.chat.bits.read permission for TTS."
This commit is contained in:
parent
d6b66b3446
commit
e4a11382ef
@ -77,7 +77,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
|||||||
return; // new MessageResult(MessageStatus.NotReady, -1, -1);
|
return; // new MessageResult(MessageStatus.NotReady, -1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var msg = message.Message.Text;
|
var msg = string.Join(string.Empty, message.Message.Fragments.Where(f => f.Type != "cheermote").Select(f => f.Text)).Trim();
|
||||||
var chatterId = long.Parse(message.ChatterUserId);
|
var chatterId = long.Parse(message.ChatterUserId);
|
||||||
var tasks = new List<Task>();
|
var tasks = new List<Task>();
|
||||||
|
|
||||||
@ -98,12 +98,23 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_user.AllowedChatters != null && !_user.AllowedChatters.Contains(chatterId))
|
||||||
|
{
|
||||||
|
_logger.Information("Potential chat message from raider ignored due to potential raid message spam.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (message.Reply != null)
|
if (message.Reply != null)
|
||||||
msg = msg.Substring(message.Reply.ParentUserLogin.Length + 2);
|
msg = msg.Substring(message.Reply.ParentUserLogin.Length + 2);
|
||||||
|
|
||||||
|
var bits = message.Message.Fragments.Where(f => f.Type == "cheermote" && f.Cheermote != null)
|
||||||
|
.Select(f => f.Cheermote!.Bits)
|
||||||
|
.Sum();
|
||||||
var permissionPath = "tts.chat.messages.read";
|
var permissionPath = "tts.chat.messages.read";
|
||||||
if (!string.IsNullOrWhiteSpace(message.ChannelPointsCustomRewardId))
|
if (!string.IsNullOrWhiteSpace(message.ChannelPointsCustomRewardId))
|
||||||
permissionPath = "tts.chat.redemptions.read";
|
permissionPath = "tts.chat.redemptions.read";
|
||||||
|
else if (bits > 0)
|
||||||
|
permissionPath = "tts.chat.bits.read";
|
||||||
|
|
||||||
var permission = chatterId == _user.OwnerId ? true : _permissionManager.CheckIfAllowed(groups, permissionPath);
|
var permission = chatterId == _user.OwnerId ? true : _permissionManager.CheckIfAllowed(groups, permissionPath);
|
||||||
if (permission != true)
|
if (permission != true)
|
||||||
|
59
Twitch/Socket/Handlers/ChannelRaidHandler.cs
Normal file
59
Twitch/Socket/Handlers/ChannelRaidHandler.cs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
using Serilog;
|
||||||
|
using TwitchChatTTS.Twitch.Socket.Messages;
|
||||||
|
|
||||||
|
namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||||
|
{
|
||||||
|
public class ChannelRaidHandler : ITwitchSocketHandler
|
||||||
|
{
|
||||||
|
public string Name => "channel.raid";
|
||||||
|
|
||||||
|
private readonly TwitchApiClient _api;
|
||||||
|
private readonly User _user;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly object _lock;
|
||||||
|
|
||||||
|
public ChannelRaidHandler(TwitchApiClient api, User user, ILogger logger)
|
||||||
|
{
|
||||||
|
_api = api;
|
||||||
|
_user = user;
|
||||||
|
_logger = logger;
|
||||||
|
_lock = new object();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Execute(TwitchWebsocketClient sender, object data)
|
||||||
|
{
|
||||||
|
if (data is not ChannelRaidMessage message)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var chatters = await _api.GetChatters(message.ToBroadcasterUserId, message.ToBroadcasterUserLogin);
|
||||||
|
if (chatters?.Data == null)
|
||||||
|
{
|
||||||
|
_logger.Error("Could not fetch the list of chatters in chat.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var date = DateTime.Now;
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_user.RaidStart = date;
|
||||||
|
if (_user.AllowedChatters == null)
|
||||||
|
{
|
||||||
|
var chatterIds = chatters.Data.Select(c => long.Parse(c.UserId));
|
||||||
|
_user.AllowedChatters = new HashSet<long>(chatterIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(30));
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_user.RaidStart == date)
|
||||||
|
{
|
||||||
|
_logger.Information("Raid message spam prevention ended.");
|
||||||
|
_user.RaidStart = null;
|
||||||
|
_user.AllowedChatters = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
|||||||
if (data is not ChannelResubscriptionMessage message)
|
if (data is not ChannelResubscriptionMessage message)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_logger.Debug("Resubscription occured.");
|
_logger.Debug($"Resubscription occured [chatter: {message.UserLogin}][chatter id: {message.UserId}][Tier: {message.Tier}][Streak: {message.StreakMonths}][Cumulative: {message.CumulativeMonths}][Duration: {message.DurationMonths}]");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var actions = _redemptionManager.Get("subscription");
|
var actions = _redemptionManager.Get("subscription");
|
||||||
@ -36,7 +36,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
|||||||
foreach (var action in actions)
|
foreach (var action in actions)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _redemptionManager.Execute(action, message.UserName, long.Parse(message.UserId));
|
await _redemptionManager.Execute(action, message.UserName!, long.Parse(message.UserId!));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -22,7 +22,11 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
|||||||
if (data is not ChannelSubscriptionGiftMessage message)
|
if (data is not ChannelSubscriptionGiftMessage message)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_logger.Debug("Gifted subscription occured.");
|
if (message.IsAnonymous)
|
||||||
|
_logger.Debug($"Gifted subscription occured [chatter: Anonymous][Tier: {message.Tier}][Count: {message.Total}]");
|
||||||
|
else
|
||||||
|
_logger.Debug($"Gifted subscription occured [chatter: {message.UserLogin}][chatter id: {message.UserId}][Tier: {message.Tier}][Count: {message.Total}][Cumulative Count: {message.CumulativeTotal}]");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var actions = _redemptionManager.Get("subscription.gift");
|
var actions = _redemptionManager.Get("subscription.gift");
|
||||||
@ -36,7 +40,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
|||||||
foreach (var action in actions)
|
foreach (var action in actions)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _redemptionManager.Execute(action, message.UserName, long.Parse(message.UserId));
|
await _redemptionManager.Execute(action, message.UserName ?? "Anonymous", message.UserId == null ? 0 : long.Parse(message.UserId));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -24,7 +24,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
|||||||
if (message.IsGifted)
|
if (message.IsGifted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_logger.Debug("Subscription occured.");
|
_logger.Debug($"Subscription occured [chatter: {message.UserLogin}][chatter id: {message.UserId}][Tier: {message.Tier}]");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var actions = _redemptionManager.Get("subscription");
|
var actions = _redemptionManager.Get("subscription");
|
||||||
@ -38,7 +38,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
|||||||
foreach (var action in actions)
|
foreach (var action in actions)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _redemptionManager.Execute(action, message.UserName, long.Parse(message.UserId));
|
await _redemptionManager.Execute(action, message.UserName!, long.Parse(message.UserId!));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -22,12 +22,8 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
|||||||
|
|
||||||
public async Task Execute(TwitchWebsocketClient sender, object data)
|
public async Task Execute(TwitchWebsocketClient sender, object data)
|
||||||
{
|
{
|
||||||
if (sender == null)
|
|
||||||
return;
|
|
||||||
if (data is not SessionWelcomeMessage message)
|
if (data is not SessionWelcomeMessage message)
|
||||||
return;
|
return;
|
||||||
if (_api == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(message.Session.Id))
|
if (string.IsNullOrEmpty(message.Session.Id))
|
||||||
{
|
{
|
||||||
@ -43,15 +39,15 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
|||||||
string[] subscriptionsv1 = [
|
string[] subscriptionsv1 = [
|
||||||
"channel.chat.message",
|
"channel.chat.message",
|
||||||
"channel.chat.message_delete",
|
"channel.chat.message_delete",
|
||||||
"channel.chat.notification",
|
|
||||||
"channel.chat.clear",
|
"channel.chat.clear",
|
||||||
"channel.chat.clear_user_messages",
|
"channel.chat.clear_user_messages",
|
||||||
"channel.ad_break.begin",
|
|
||||||
"channel.subscribe",
|
"channel.subscribe",
|
||||||
"channel.subscription.gift",
|
"channel.subscription.gift",
|
||||||
"channel.subscription.message",
|
"channel.subscription.message",
|
||||||
|
"channel.ad_break.begin",
|
||||||
"channel.ban",
|
"channel.ban",
|
||||||
"channel.channel_points_custom_reward_redemption.add"
|
"channel.channel_points_custom_reward_redemption.add",
|
||||||
|
"channel.raid"
|
||||||
];
|
];
|
||||||
string[] subscriptionsv2 = [
|
string[] subscriptionsv2 = [
|
||||||
"channel.follow",
|
"channel.follow",
|
||||||
@ -92,7 +88,6 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
|||||||
var response = await _api.CreateEventSubscription(subscriptionName, version, sessionId, broadcasterId);
|
var response = await _api.CreateEventSubscription(subscriptionName, version, sessionId, broadcasterId);
|
||||||
if (response == null)
|
if (response == null)
|
||||||
{
|
{
|
||||||
_logger.Error($"Failed to create an event subscription [subscription type: {subscriptionName}][reason: response is null]");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (response.Data == null)
|
if (response.Data == null)
|
||||||
|
13
Twitch/Socket/Messages/ChannelRaidMessage.cs
Normal file
13
Twitch/Socket/Messages/ChannelRaidMessage.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace TwitchChatTTS.Twitch.Socket.Messages
|
||||||
|
{
|
||||||
|
public class ChannelRaidMessage
|
||||||
|
{
|
||||||
|
public string FromBroadcasterUserId { get; set; }
|
||||||
|
public string FromBroadcasterUserLogin { get; set; }
|
||||||
|
public string FromBroadcasterUserName { get; set; }
|
||||||
|
public string ToBroadcasterUserId { get; set; }
|
||||||
|
public string ToBroadcasterUserLogin { get; set; }
|
||||||
|
public string ToBroadcasterUserName { get; set; }
|
||||||
|
public int Viewers { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -2,9 +2,9 @@ namespace TwitchChatTTS.Twitch.Socket.Messages
|
|||||||
{
|
{
|
||||||
public class ChannelSubscriptionData
|
public class ChannelSubscriptionData
|
||||||
{
|
{
|
||||||
public string UserId { get; set; }
|
public string? UserId { get; set; }
|
||||||
public string UserLogin { get; set; }
|
public string? UserLogin { get; set; }
|
||||||
public string UserName { get; set; }
|
public string? UserName { get; set; }
|
||||||
public string BroadcasterUserId { get; set; }
|
public string BroadcasterUserId { get; set; }
|
||||||
public string BroadcasterUserLogin { get; set; }
|
public string BroadcasterUserLogin { get; set; }
|
||||||
public string BroadcasterUserName { get; set; }
|
public string BroadcasterUserName { get; set; }
|
||||||
|
9
Twitch/Socket/Messages/ChatterMessage.cs
Normal file
9
Twitch/Socket/Messages/ChatterMessage.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace TwitchChatTTS.Twitch.Socket.Messages
|
||||||
|
{
|
||||||
|
public class ChatterMessage
|
||||||
|
{
|
||||||
|
public string UserId { get; set; }
|
||||||
|
public string UserLogin { get; set; }
|
||||||
|
public string UserName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -51,6 +51,19 @@ public class TwitchApiClient
|
|||||||
await _web.Delete($"{base_url}/eventsub/subscriptions?id=" + subscriptionId);
|
await _web.Delete($"{base_url}/eventsub/subscriptions?id=" + subscriptionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<EventResponse<ChatterMessage>?> GetChatters(string broadcasterId, string? moderatorId = null)
|
||||||
|
{
|
||||||
|
moderatorId ??= broadcasterId;
|
||||||
|
var response = await _web.Get($"https://api.twitch.tv/helix/chat/chatters?broadcaster_id={broadcasterId}&moderator_id={moderatorId}");
|
||||||
|
if (response.StatusCode == HttpStatusCode.Accepted)
|
||||||
|
{
|
||||||
|
_logger.Debug($"Twitch API call [type: get chatters][response: {await response.Content.ReadAsStringAsync()}]");
|
||||||
|
return await response.Content.ReadFromJsonAsync(typeof(EventResponse<ChatterMessage>)) as EventResponse<ChatterMessage>;
|
||||||
|
}
|
||||||
|
_logger.Error($"Twitch API call failed [type: get chatters][response: {await response.Content.ReadAsStringAsync()}]");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<EventResponse<NotificationInfo>?> GetSubscriptions(string? status = null, string? broadcasterId = null, string? after = null)
|
public async Task<EventResponse<NotificationInfo>?> GetSubscriptions(string? status = null, string? broadcasterId = null, string? after = null)
|
||||||
{
|
{
|
||||||
List<string> queryParams = new List<string>();
|
List<string> queryParams = new List<string>();
|
||||||
|
2
User.cs
2
User.cs
@ -22,6 +22,8 @@ namespace TwitchChatTTS
|
|||||||
// voice names
|
// voice names
|
||||||
public HashSet<string> VoicesEnabled { get => _voicesEnabled; set { _voicesEnabled = value; VoiceNameRegex = GenerateEnabledVoicesRegex(); } }
|
public HashSet<string> VoicesEnabled { get => _voicesEnabled; set { _voicesEnabled = value; VoiceNameRegex = GenerateEnabledVoicesRegex(); } }
|
||||||
|
|
||||||
|
public DateTime? RaidStart { get; set; }
|
||||||
|
public HashSet<long>? AllowedChatters { get; set; }
|
||||||
public HashSet<long> Chatters { get; set; }
|
public HashSet<long> Chatters { get; set; }
|
||||||
public TTSWordFilter[] RegexFilters { get; set; }
|
public TTSWordFilter[] RegexFilters { get; set; }
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
|
Loading…
Reference in New Issue
Block a user