Fixed 7tv & Twitch reconnection. Added adbreak, follow, subscription handlers for Twitch. Added multi-chat support. Added support to unsubscribe from Twitch event subs.
This commit is contained in:
57
Twitch/Socket/Handlers/ChannelAdBreakHandler.cs
Normal file
57
Twitch/Socket/Handlers/ChannelAdBreakHandler.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using Serilog;
|
||||
using TwitchChatTTS.Twitch.Redemptions;
|
||||
using TwitchChatTTS.Twitch.Socket.Messages;
|
||||
|
||||
namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
{
|
||||
public class ChannelAdBreakHandler : ITwitchSocketHandler
|
||||
{
|
||||
public string Name => "channel.ad_break.begin";
|
||||
|
||||
private readonly IRedemptionManager _redemptionManager;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public ChannelAdBreakHandler(IRedemptionManager redemptionManager, ILogger logger)
|
||||
{
|
||||
_redemptionManager = redemptionManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Execute(TwitchWebsocketClient sender, object data)
|
||||
{
|
||||
if (data is not ChannelAdBreakMessage message)
|
||||
return;
|
||||
|
||||
bool isAutomatic = message.IsAutomatic == "true";
|
||||
if (isAutomatic)
|
||||
_logger.Information($"Ad break has begun [duration: {message.DurationSeconds} seconds][automatic: {isAutomatic}]");
|
||||
else
|
||||
_logger.Information($"Ad break has begun [duration: {message.DurationSeconds} seconds][requester: {message.RequesterUserLogin}][requester id: {message.RequesterUserId}]");
|
||||
|
||||
try
|
||||
{
|
||||
var actions = _redemptionManager.Get("adbreak");
|
||||
if (!actions.Any())
|
||||
{
|
||||
_logger.Debug($"No redemable actions for ad break was found");
|
||||
return;
|
||||
}
|
||||
_logger.Debug($"Found {actions.Count} actions for this Twitch ad break");
|
||||
|
||||
foreach (var action in actions)
|
||||
try
|
||||
{
|
||||
await _redemptionManager.Execute(action, message.RequesterUserLogin, long.Parse(message.RequesterUserId));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, $"Failed to execute redeeemable action [action: {action.Name}][action type: {action.Type}][redeem: ad break]");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, $"Failed to fetch the redeemable actions for ad break");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Task Execute(TwitchWebsocketClient sender, object? data)
|
||||
public Task Execute(TwitchWebsocketClient sender, object data)
|
||||
{
|
||||
if (data is not ChannelBanMessage message)
|
||||
return Task.CompletedTask;
|
||||
|
@ -18,7 +18,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Task Execute(TwitchWebsocketClient sender, object? data)
|
||||
public Task Execute(TwitchWebsocketClient sender, object data)
|
||||
{
|
||||
if (data is not ChannelChatClearMessage message)
|
||||
return Task.CompletedTask;
|
||||
|
@ -18,14 +18,16 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Task Execute(TwitchWebsocketClient sender, object? data)
|
||||
public Task Execute(TwitchWebsocketClient sender, object data)
|
||||
{
|
||||
if (data is not ChannelChatClearUserMessage message)
|
||||
return Task.CompletedTask;
|
||||
|
||||
|
||||
long broadcasterId = long.Parse(message.BroadcasterUserId);
|
||||
long chatterId = long.Parse(message.TargetUserId);
|
||||
_player.RemoveAll(chatterId);
|
||||
if (_player.Playing?.ChatterId == chatterId) {
|
||||
_player.RemoveAll(broadcasterId, chatterId);
|
||||
if (_player.Playing != null && _player.Playing.RoomId == broadcasterId && _player.Playing.ChatterId == chatterId)
|
||||
{
|
||||
_playback.RemoveMixerInput(_player.Playing.Audio!);
|
||||
_player.Playing = null;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Task Execute(TwitchWebsocketClient sender, object? data)
|
||||
public Task Execute(TwitchWebsocketClient sender, object data)
|
||||
{
|
||||
if (data is not ChannelChatDeleteMessage message)
|
||||
return Task.CompletedTask;
|
||||
|
@ -19,7 +19,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
|
||||
private readonly User _user;
|
||||
private readonly TTSPlayer _player;
|
||||
private readonly CommandManager _commands;
|
||||
private readonly ICommandManager _commands;
|
||||
private readonly IGroupPermissionManager _permissionManager;
|
||||
private readonly IChatterGroupManager _chatterGroupManager;
|
||||
private readonly IEmoteDatabase _emotes;
|
||||
@ -34,7 +34,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
public ChannelChatMessageHandler(
|
||||
User user,
|
||||
TTSPlayer player,
|
||||
CommandManager commands,
|
||||
ICommandManager commands,
|
||||
IGroupPermissionManager permissionManager,
|
||||
IChatterGroupManager chatterGroupManager,
|
||||
IEmoteDatabase emotes,
|
||||
@ -59,15 +59,10 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Execute(TwitchWebsocketClient sender, object? data)
|
||||
public async Task Execute(TwitchWebsocketClient sender, object data)
|
||||
{
|
||||
if (sender == null)
|
||||
return;
|
||||
if (data == null)
|
||||
{
|
||||
_logger.Warning("Twitch websocket message data is null.");
|
||||
return;
|
||||
}
|
||||
if (data is not ChannelChatMessage message)
|
||||
return;
|
||||
|
||||
@ -231,6 +226,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
|
||||
var parts = _sfxRegex.Split(message);
|
||||
var chatterId = long.Parse(e.ChatterUserId);
|
||||
var broadcasterId = long.Parse(e.BroadcasterUserId);
|
||||
var badgesString = string.Join(", ", e.Badges.Select(b => b.SetId + '|' + b.Id + '=' + b.Info));
|
||||
|
||||
if (parts.Length == 1)
|
||||
@ -241,6 +237,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
Voice = voice,
|
||||
Message = message,
|
||||
Timestamp = DateTime.UtcNow,
|
||||
RoomId = broadcasterId,
|
||||
ChatterId = chatterId,
|
||||
MessageId = e.MessageId,
|
||||
Badges = e.Badges,
|
||||
@ -271,6 +268,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
Voice = voice,
|
||||
Message = parts[i * 2],
|
||||
Timestamp = DateTime.UtcNow,
|
||||
RoomId = broadcasterId,
|
||||
ChatterId = chatterId,
|
||||
MessageId = e.MessageId,
|
||||
Badges = e.Badges,
|
||||
@ -284,6 +282,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
Voice = voice,
|
||||
File = $"sfx/{sfxName}.mp3",
|
||||
Timestamp = DateTime.UtcNow,
|
||||
RoomId = broadcasterId,
|
||||
ChatterId = chatterId,
|
||||
MessageId = e.MessageId,
|
||||
Badges = e.Badges,
|
||||
@ -299,6 +298,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
Voice = voice,
|
||||
Message = parts.Last(),
|
||||
Timestamp = DateTime.UtcNow,
|
||||
RoomId = broadcasterId,
|
||||
ChatterId = chatterId,
|
||||
MessageId = e.MessageId,
|
||||
Badges = e.Badges,
|
||||
|
@ -8,11 +8,11 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
{
|
||||
public string Name => "channel.channel_points_custom_reward_redemption.add";
|
||||
|
||||
private readonly RedemptionManager _redemptionManager;
|
||||
private readonly IRedemptionManager _redemptionManager;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public ChannelCustomRedemptionHandler(
|
||||
RedemptionManager redemptionManager,
|
||||
IRedemptionManager redemptionManager,
|
||||
ILogger logger
|
||||
)
|
||||
{
|
||||
@ -20,7 +20,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Execute(TwitchWebsocketClient sender, object? data)
|
||||
public async Task Execute(TwitchWebsocketClient sender, object data)
|
||||
{
|
||||
if (data is not ChannelCustomRedemptionMessage message)
|
||||
return;
|
||||
|
52
Twitch/Socket/Handlers/ChannelFollowHandler.cs
Normal file
52
Twitch/Socket/Handlers/ChannelFollowHandler.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using Serilog;
|
||||
using TwitchChatTTS.Twitch.Redemptions;
|
||||
using TwitchChatTTS.Twitch.Socket.Messages;
|
||||
|
||||
namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
{
|
||||
public class ChannelFollowHandler : ITwitchSocketHandler
|
||||
{
|
||||
public string Name => "channel.follow";
|
||||
|
||||
private readonly IRedemptionManager _redemptionManager;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public ChannelFollowHandler(IRedemptionManager redemptionManager, ILogger logger)
|
||||
{
|
||||
_redemptionManager = redemptionManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Execute(TwitchWebsocketClient sender, object data)
|
||||
{
|
||||
if (data is not ChannelFollowMessage message)
|
||||
return;
|
||||
|
||||
_logger.Information($"User followed [chatter: {message.UserLogin}][chatter id: {message.UserId}]");
|
||||
try
|
||||
{
|
||||
var actions = _redemptionManager.Get("follow");
|
||||
if (!actions.Any())
|
||||
{
|
||||
_logger.Debug($"No redemable actions for follow was found");
|
||||
return;
|
||||
}
|
||||
_logger.Debug($"Found {actions.Count} actions for this Twitch follow");
|
||||
|
||||
foreach (var action in actions)
|
||||
try
|
||||
{
|
||||
await _redemptionManager.Execute(action, message.UserName, long.Parse(message.UserId));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, $"Failed to execute redeeemable action [action: {action.Name}][action type: {action.Type}][redeem: follow]");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, $"Failed to fetch the redeemable actions for follow");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
52
Twitch/Socket/Handlers/ChannelResubscriptionHandler.cs
Normal file
52
Twitch/Socket/Handlers/ChannelResubscriptionHandler.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using Serilog;
|
||||
using TwitchChatTTS.Twitch.Redemptions;
|
||||
using TwitchChatTTS.Twitch.Socket.Messages;
|
||||
|
||||
namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
{
|
||||
public class ChannelResubscriptionHandler : ITwitchSocketHandler
|
||||
{
|
||||
public string Name => "channel.subscription.message";
|
||||
|
||||
private readonly IRedemptionManager _redemptionManager;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public ChannelResubscriptionHandler(IRedemptionManager redemptionManager, ILogger logger)
|
||||
{
|
||||
_redemptionManager = redemptionManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Execute(TwitchWebsocketClient sender, object data)
|
||||
{
|
||||
if (data is not ChannelResubscriptionMessage message)
|
||||
return;
|
||||
|
||||
_logger.Debug("Resubscription occured.");
|
||||
try
|
||||
{
|
||||
var actions = _redemptionManager.Get("subscription");
|
||||
if (!actions.Any())
|
||||
{
|
||||
_logger.Debug($"No redemable actions for this subscription was found [message: {message.Message.Text}]");
|
||||
return;
|
||||
}
|
||||
_logger.Debug($"Found {actions.Count} actions for this Twitch subscription [message: {message.Message.Text}]");
|
||||
|
||||
foreach (var action in actions)
|
||||
try
|
||||
{
|
||||
await _redemptionManager.Execute(action, message.UserName, long.Parse(message.UserId));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, $"Failed to execute redeeemable action [action: {action.Name}][action type: {action.Type}][redeem: subscription][message: {message.Message.Text}]");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, $"Failed to fetch the redeemable actions for subscription [message: {message.Message.Text}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
52
Twitch/Socket/Handlers/ChannelSubscriptionGiftHandler.cs
Normal file
52
Twitch/Socket/Handlers/ChannelSubscriptionGiftHandler.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using Serilog;
|
||||
using TwitchChatTTS.Twitch.Redemptions;
|
||||
using TwitchChatTTS.Twitch.Socket.Messages;
|
||||
|
||||
namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
{
|
||||
public class ChannelSubscriptionGiftHandler : ITwitchSocketHandler
|
||||
{
|
||||
public string Name => "channel.subscription.gift";
|
||||
|
||||
private readonly IRedemptionManager _redemptionManager;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public ChannelSubscriptionGiftHandler(IRedemptionManager redemptionManager, ILogger logger)
|
||||
{
|
||||
_redemptionManager = redemptionManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Execute(TwitchWebsocketClient sender, object data)
|
||||
{
|
||||
if (data is not ChannelSubscriptionGiftMessage message)
|
||||
return;
|
||||
|
||||
_logger.Debug("Gifted subscription occured.");
|
||||
try
|
||||
{
|
||||
var actions = _redemptionManager.Get("subscription.gift");
|
||||
if (!actions.Any())
|
||||
{
|
||||
_logger.Debug($"No redemable actions for this gifted subscription was found");
|
||||
return;
|
||||
}
|
||||
_logger.Debug($"Found {actions.Count} actions for this Twitch gifted subscription [gifted: {message.UserLogin}][gifted id: {message.UserId}][Anonymous: {message.IsAnonymous}][cumulative: {message.CumulativeTotal ?? -1}]");
|
||||
|
||||
foreach (var action in actions)
|
||||
try
|
||||
{
|
||||
await _redemptionManager.Execute(action, message.UserName, long.Parse(message.UserId));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, $"Failed to execute redeeemable action [action: {action.Name}][action type: {action.Type}][redeem: gifted subscription]");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, $"Failed to fetch the redeemable actions for gifted subscription");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +1,54 @@
|
||||
using Serilog;
|
||||
using TwitchChatTTS.Twitch.Redemptions;
|
||||
using TwitchChatTTS.Twitch.Socket.Messages;
|
||||
|
||||
namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
{
|
||||
public class ChannelSubscriptionHandler : ITwitchSocketHandler
|
||||
{
|
||||
public string Name => "channel.subscription.message";
|
||||
public string Name => "channel.subscription";
|
||||
|
||||
private readonly TTSPlayer _player;
|
||||
private readonly IRedemptionManager _redemptionManager;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public ChannelSubscriptionHandler(TTSPlayer player, ILogger logger) {
|
||||
_player = player;
|
||||
public ChannelSubscriptionHandler(IRedemptionManager redemptionManager, ILogger logger)
|
||||
{
|
||||
_redemptionManager = redemptionManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Execute(TwitchWebsocketClient sender, object? data)
|
||||
public async Task Execute(TwitchWebsocketClient sender, object data)
|
||||
{
|
||||
if (sender == null)
|
||||
return;
|
||||
if (data == null)
|
||||
{
|
||||
_logger.Warning("Twitch websocket message data is null.");
|
||||
return;
|
||||
}
|
||||
if (data is not ChannelSubscriptionMessage message)
|
||||
return;
|
||||
if (message.IsGifted)
|
||||
return;
|
||||
|
||||
_logger.Debug("Subscription occured.");
|
||||
try
|
||||
{
|
||||
var actions = _redemptionManager.Get("subscription");
|
||||
if (!actions.Any())
|
||||
{
|
||||
_logger.Debug($"No redemable actions for this subscription was found [subscriber: {message.UserLogin}][subscriber id: {message.UserId}]");
|
||||
return;
|
||||
}
|
||||
_logger.Debug($"Found {actions.Count} actions for this Twitch subscription [subscriber: {message.UserLogin}][subscriber id: {message.UserId}]");
|
||||
|
||||
foreach (var action in actions)
|
||||
try
|
||||
{
|
||||
await _redemptionManager.Execute(action, message.UserName, long.Parse(message.UserId));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, $"Failed to execute redeeemable action [action: {action.Name}][action type: {action.Type}][redeem: subscription][subscriber: {message.UserLogin}][subscriber id: {message.UserId}]");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, $"Failed to fetch the redeemable actions for subscription [subscriber: {message.UserLogin}][subscriber id: {message.UserId}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,6 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
public interface ITwitchSocketHandler
|
||||
{
|
||||
string Name { get; }
|
||||
Task Execute(TwitchWebsocketClient sender, object? data);
|
||||
Task Execute(TwitchWebsocketClient sender, object data);
|
||||
}
|
||||
}
|
@ -23,30 +23,30 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
_handlers = handlers.ToDictionary(h => h.Name, h => h);
|
||||
_logger = logger;
|
||||
|
||||
_options = new JsonSerializerOptions() {
|
||||
_options = new JsonSerializerOptions()
|
||||
{
|
||||
PropertyNameCaseInsensitive = false,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
|
||||
};
|
||||
|
||||
_messageTypes = new Dictionary<string, Type>();
|
||||
_messageTypes.Add("channel.adbreak.begin", typeof(ChannelAdBreakMessage));
|
||||
_messageTypes.Add("channel.ban", typeof(ChannelBanMessage));
|
||||
_messageTypes.Add("channel.chat.message", typeof(ChannelChatMessage));
|
||||
_messageTypes.Add("channel.chat.clear_user_messages", typeof(ChannelChatClearUserMessage));
|
||||
_messageTypes.Add("channel.chat.clear", typeof(ChannelChatClearMessage));
|
||||
_messageTypes.Add("channel.chat.message_delete", typeof(ChannelChatDeleteMessage));
|
||||
_messageTypes.Add("channel.channel_points_custom_reward_redemption.add", typeof(ChannelCustomRedemptionMessage));
|
||||
_messageTypes.Add("channel.follow", typeof(ChannelFollowMessage));
|
||||
_messageTypes.Add("channel.resubscription", typeof(ChannelResubscriptionMessage));
|
||||
_messageTypes.Add("channel.subscription.message", typeof(ChannelSubscriptionMessage));
|
||||
_messageTypes.Add("channel.subscription.gift", typeof(ChannelSubscriptionGiftMessage));
|
||||
}
|
||||
|
||||
public async Task Execute(TwitchWebsocketClient sender, object? data)
|
||||
public async Task Execute(TwitchWebsocketClient sender, object data)
|
||||
{
|
||||
if (sender == null)
|
||||
return;
|
||||
if (data == null)
|
||||
{
|
||||
_logger.Warning("Twitch websocket message data is null.");
|
||||
return;
|
||||
}
|
||||
if (data is not NotificationMessage message)
|
||||
return;
|
||||
|
||||
|
12
Twitch/Socket/Handlers/SessionKeepAliveHandler.cs
Normal file
12
Twitch/Socket/Handlers/SessionKeepAliveHandler.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
{
|
||||
public class SessionKeepAliveHandler : ITwitchSocketHandler
|
||||
{
|
||||
public string Name => "session_keepalive";
|
||||
|
||||
public Task Execute(TwitchWebsocketClient sender, object data)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,40 +8,45 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
{
|
||||
public string Name => "session_reconnect";
|
||||
|
||||
private readonly TwitchApiClient _api;
|
||||
private readonly ITwitchConnectionManager _manager;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public SessionReconnectHandler(TwitchApiClient api, ILogger logger)
|
||||
public SessionReconnectHandler(ITwitchConnectionManager manager, ILogger logger)
|
||||
{
|
||||
_api = api;
|
||||
_manager = manager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Execute(TwitchWebsocketClient sender, object? data)
|
||||
public async Task Execute(TwitchWebsocketClient sender, object data)
|
||||
{
|
||||
if (sender == null)
|
||||
return;
|
||||
if (data == null)
|
||||
{
|
||||
_logger.Warning("Twitch websocket message data is null.");
|
||||
return;
|
||||
}
|
||||
if (data is not SessionWelcomeMessage message)
|
||||
return;
|
||||
if (_api == null)
|
||||
return;
|
||||
|
||||
if (string.IsNullOrEmpty(message.Session.Id))
|
||||
{
|
||||
_logger.Warning($"No session info provided by Twitch [status: {message.Session.Status}]");
|
||||
_logger.Warning($"No session id provided by Twitch [status: {message.Session.Status}]");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Be able to handle multiple websocket connections.
|
||||
sender.URL = message.Session.ReconnectUrl;
|
||||
await Task.Delay(TimeSpan.FromSeconds(29));
|
||||
await sender.DisconnectAsync(new SocketDisconnectionEventArgs("Close", "Twitch asking to reconnect."));
|
||||
await sender.Connect();
|
||||
if (message.Session.ReconnectUrl == null)
|
||||
{
|
||||
_logger.Warning($"No reconnection info provided by Twitch [status: {message.Session.Status}]");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.ReceivedReconnecting = true;
|
||||
|
||||
var backup = _manager.GetBackupClient();
|
||||
var identified = _manager.GetWorkingClient();
|
||||
if (identified != null && backup != identified)
|
||||
{
|
||||
await identified.DisconnectAsync(new SocketDisconnectionEventArgs("Closed", "Reconnection from another client."));
|
||||
}
|
||||
|
||||
backup.URL = message.Session.ReconnectUrl;
|
||||
await backup.Connect();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
using CommonSocketLibrary.Abstract;
|
||||
using Serilog;
|
||||
using TwitchChatTTS.Twitch.Socket.Messages;
|
||||
|
||||
@ -8,26 +7,23 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
{
|
||||
public string Name => "session_welcome";
|
||||
|
||||
private readonly HermesApiClient _hermes;
|
||||
private readonly TwitchApiClient _api;
|
||||
private readonly User _user;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public SessionWelcomeHandler(TwitchApiClient api, User user, ILogger logger)
|
||||
public SessionWelcomeHandler(HermesApiClient hermes, TwitchApiClient api, User user, ILogger logger)
|
||||
{
|
||||
_hermes = hermes;
|
||||
_api = api;
|
||||
_user = user;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Execute(TwitchWebsocketClient sender, object? data)
|
||||
public async Task Execute(TwitchWebsocketClient sender, object data)
|
||||
{
|
||||
if (sender == null)
|
||||
return;
|
||||
if (data == null)
|
||||
{
|
||||
_logger.Warning("Twitch websocket message data is null.");
|
||||
return;
|
||||
}
|
||||
if (data is not SessionWelcomeMessage message)
|
||||
return;
|
||||
if (_api == null)
|
||||
@ -39,6 +35,11 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
return;
|
||||
}
|
||||
|
||||
await _hermes.AuthorizeTwitch();
|
||||
var token = await _hermes.FetchTwitchBotToken();
|
||||
_api.Initialize(token);
|
||||
|
||||
string broadcasterId = _user.TwitchUserId.ToString();
|
||||
string[] subscriptionsv1 = [
|
||||
"channel.chat.message",
|
||||
"channel.chat.message_delete",
|
||||
@ -53,17 +54,36 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
string[] subscriptionsv2 = [
|
||||
"channel.follow",
|
||||
];
|
||||
string broadcasterId = _user.TwitchUserId.ToString();
|
||||
|
||||
string? pagination = null;
|
||||
int size = 0;
|
||||
do
|
||||
{
|
||||
var subscriptionsData = await _api.GetSubscriptions(status: "enabled", broadcasterId: broadcasterId, after: pagination);
|
||||
var subscriptionNames = subscriptionsData?.Data == null ? [] : subscriptionsData.Data.Select(s => s.Type).ToArray();
|
||||
|
||||
if (subscriptionNames.Length == 0)
|
||||
break;
|
||||
|
||||
foreach (var d in subscriptionsData!.Data!)
|
||||
sender.AddSubscription(broadcasterId, d.Type, d.Id);
|
||||
|
||||
subscriptionsv1 = subscriptionsv1.Except(subscriptionNames).ToArray();
|
||||
subscriptionsv2 = subscriptionsv2.Except(subscriptionNames).ToArray();
|
||||
|
||||
pagination = subscriptionsData?.Pagination?.Cursor;
|
||||
size = subscriptionNames.Length;
|
||||
} while (size >= 100 && pagination != null && subscriptionsv1.Length + subscriptionsv2.Length > 0);
|
||||
|
||||
foreach (var subscription in subscriptionsv1)
|
||||
await Subscribe(subscription, message.Session.Id, broadcasterId, "1");
|
||||
await Subscribe(sender, subscription, message.Session.Id, broadcasterId, "1");
|
||||
foreach (var subscription in subscriptionsv2)
|
||||
await Subscribe(subscription, message.Session.Id, broadcasterId, "2");
|
||||
|
||||
sender.SessionId = message.Session.Id;
|
||||
sender.Identified = sender.SessionId != null;
|
||||
await Subscribe(sender, subscription, message.Session.Id, broadcasterId, "2");
|
||||
|
||||
sender.Identify(message.Session.Id);
|
||||
}
|
||||
|
||||
private async Task Subscribe(string subscriptionName, string sessionId, string broadcasterId, string version)
|
||||
private async Task Subscribe(TwitchWebsocketClient sender, string subscriptionName, string sessionId, string broadcasterId, string version)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -83,6 +103,10 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
|
||||
_logger.Error($"Failed to create an event subscription [subscription type: {subscriptionName}][reason: data is empty]");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var d in response.Data)
|
||||
sender.AddSubscription(broadcasterId, d.Type, d.Id);
|
||||
|
||||
_logger.Information($"Sucessfully added subscription to Twitch websockets [subscription type: {subscriptionName}]");
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
Reference in New Issue
Block a user