2024-08-06 15:29:29 -04:00
using CommonSocketLibrary.Abstract ;
using Microsoft.Extensions.DependencyInjection ;
2024-06-16 20:19:31 -04:00
using Serilog ;
2024-07-16 00:48:55 -04:00
using TwitchChatTTS.Hermes.Socket ;
2024-08-06 15:29:29 -04:00
using TwitchChatTTS.Twitch.Socket ;
2024-08-04 19:46:10 -04:00
using TwitchChatTTS.Twitch.Socket.Messages ;
2024-07-19 12:56:41 -04:00
using static TwitchChatTTS . Chat . Commands . TTSCommands ;
2024-06-16 20:19:31 -04:00
namespace TwitchChatTTS.Chat.Commands
{
2024-07-19 12:56:41 -04:00
public class TTSCommand : IChatCommand
2024-06-16 20:19:31 -04:00
{
2024-08-06 15:29:29 -04:00
private readonly TwitchWebsocketClient _twitch ;
2024-06-24 18:11:36 -04:00
private readonly User _user ;
2024-08-06 15:29:29 -04:00
private readonly TwitchApiClient _client ;
2024-06-24 18:11:36 -04:00
private readonly ILogger _logger ;
2024-06-16 20:19:31 -04:00
2024-07-19 12:56:41 -04:00
2024-08-06 15:29:29 -04:00
public TTSCommand (
[FromKeyedServices("twitch")] SocketClient < TwitchWebsocketMessage > twitch ,
User user ,
TwitchApiClient client ,
ILogger logger )
2024-06-16 20:19:31 -04:00
{
2024-08-06 15:29:29 -04:00
_twitch = ( twitch as TwitchWebsocketClient ) ! ;
2024-06-24 18:11:36 -04:00
_user = user ;
2024-08-06 15:29:29 -04:00
_client = client ;
2024-06-16 20:19:31 -04:00
_logger = logger ;
2024-07-19 12:56:41 -04:00
}
public string Name = > "tts" ;
public void Build ( ICommandBuilder builder )
{
builder . CreateCommandTree ( Name , b = >
{
b . CreateStaticInputParameter ( "add" , b = >
{
2024-10-22 03:49:09 -04:00
b . CreateUnvalidatedParameter ( "voiceName" )
2024-07-19 12:56:41 -04:00
. CreateCommand ( new AddTTSVoiceCommand ( _user , _logger ) ) ;
} )
2024-08-04 19:46:10 -04:00
. AddAlias ( "insert" , "add" )
2024-07-19 12:56:41 -04:00
. CreateStaticInputParameter ( "delete" , b = >
{
2024-10-22 03:49:09 -04:00
b . CreateVoiceNameParameter ( "voiceName" , false )
2024-07-19 12:56:41 -04:00
. CreateCommand ( new DeleteTTSVoiceCommand ( _user , _logger ) ) ;
} )
2024-08-04 19:46:10 -04:00
. AddAlias ( "del" , "delete" )
. AddAlias ( "remove" , "delete" )
2024-07-19 12:56:41 -04:00
. CreateStaticInputParameter ( "enable" , b = >
{
b . CreateVoiceNameParameter ( "voiceName" , false )
. CreateCommand ( new SetTTSVoiceStateCommand ( true , _user , _logger ) ) ;
} )
2024-08-04 19:46:10 -04:00
. AddAlias ( "on" , "enable" )
. AddAlias ( "enabled" , "enable" )
. AddAlias ( "true" , "enable" )
2024-07-19 12:56:41 -04:00
. CreateStaticInputParameter ( "disable" , b = >
{
b . CreateVoiceNameParameter ( "voiceName" , true )
. CreateCommand ( new SetTTSVoiceStateCommand ( false , _user , _logger ) ) ;
} )
2024-08-04 19:46:10 -04:00
. AddAlias ( "off" , "disable" )
. AddAlias ( "disabled" , "disable" )
2024-08-06 15:29:29 -04:00
. AddAlias ( "false" , "disable" )
. CreateStaticInputParameter ( "join" , b = >
{
b . CreateMentionParameter ( "mention" , true )
. AddPermission ( "tts.commands.tts.join" )
. CreateCommand ( new JoinRoomCommand ( _twitch , _client , _user , _logger ) ) ;
} )
. CreateStaticInputParameter ( "leave" , b = >
{
b . CreateMentionParameter ( "mention" , true )
. AddPermission ( "tts.commands.tts.leave" )
. CreateCommand ( new LeaveRoomCommand ( _twitch , _client , _user , _logger ) ) ;
} ) ;
2024-07-19 12:56:41 -04:00
} ) ;
}
private sealed class AddTTSVoiceCommand : IChatPartialCommand
{
private readonly User _user ;
private readonly ILogger _logger ;
public bool AcceptCustomPermission { get = > false ; }
public AddTTSVoiceCommand ( User user , ILogger logger )
{
_user = user ;
_logger = logger ;
}
2024-08-04 19:46:10 -04:00
public async Task Execute ( IDictionary < string , string > values , ChannelChatMessage message , HermesSocketClient client )
2024-07-19 12:56:41 -04:00
{
if ( _user = = null | | _user . VoicesAvailable = = null )
return ;
var voiceName = values [ "voiceName" ] ;
var voiceNameLower = voiceName . ToLower ( ) ;
var exists = _user . VoicesAvailable . Any ( v = > v . Value . ToLower ( ) = = voiceNameLower ) ;
if ( exists )
{
2024-08-04 19:46:10 -04:00
_logger . Warning ( $"Voice already exists [voice: {voiceName}][id: {message.ChatterUserId}]" ) ;
2024-07-19 12:56:41 -04:00
return ;
}
await client . CreateTTSVoice ( voiceName ) ;
2024-08-04 19:46:10 -04:00
_logger . Information ( $"Added a new TTS voice [voice: {voiceName}][creator: {message.ChatterUserLogin}][creator id: {message.ChatterUserId}]" ) ;
2024-07-19 12:56:41 -04:00
}
2024-06-16 20:19:31 -04:00
}
2024-07-19 12:56:41 -04:00
private sealed class DeleteTTSVoiceCommand : IChatPartialCommand
2024-06-16 20:19:31 -04:00
{
2024-07-19 12:56:41 -04:00
private readonly User _user ;
private ILogger _logger ;
public bool AcceptCustomPermission { get = > false ; }
public DeleteTTSVoiceCommand ( User user , ILogger logger )
{
_user = user ;
_logger = logger ;
}
2024-08-04 19:46:10 -04:00
public async Task Execute ( IDictionary < string , string > values , ChannelChatMessage message , HermesSocketClient client )
2024-07-19 12:56:41 -04:00
{
if ( _user = = null | | _user . VoicesAvailable = = null )
{
2024-08-04 19:46:10 -04:00
_logger . Warning ( $"Voices available are not loaded [chatter: {message.ChatterUserLogin}][chatter id: {message.ChatterUserId}]" ) ;
2024-07-19 12:56:41 -04:00
return ;
}
var voiceName = values [ "voiceName" ] ;
var voiceNameLower = voiceName . ToLower ( ) ;
var exists = _user . VoicesAvailable . Any ( v = > v . Value . ToLower ( ) = = voiceNameLower ) ;
if ( ! exists )
{
2024-08-04 19:46:10 -04:00
_logger . Warning ( $"Voice does not exist [voice: {voiceName}][chatter: {message.ChatterUserLogin}][chatter id: {message.ChatterUserId}]" ) ;
return ;
}
var voiceId = _user . VoicesAvailable . FirstOrDefault ( v = > v . Value . ToLower ( ) = = voiceNameLower ) . Key ;
2024-08-06 15:29:29 -04:00
if ( voiceId = = null )
{
2024-08-04 19:46:10 -04:00
_logger . Warning ( $"Could not find the identifier for the tts voice [voice name: {voiceName}]" ) ;
2024-07-19 12:56:41 -04:00
return ;
}
await client . DeleteTTSVoice ( voiceId ) ;
2024-08-04 19:46:10 -04:00
_logger . Information ( $"Deleted a TTS voice [voice: {voiceName}][chatter: {message.ChatterUserLogin}][chatter id: {message.ChatterUserId}]" ) ;
2024-07-19 12:56:41 -04:00
}
2024-06-16 20:19:31 -04:00
}
2024-07-19 12:56:41 -04:00
private sealed class SetTTSVoiceStateCommand : IChatPartialCommand
2024-06-16 20:19:31 -04:00
{
2024-07-19 12:56:41 -04:00
private bool _state ;
private readonly User _user ;
private ILogger _logger ;
public bool AcceptCustomPermission { get = > true ; }
public SetTTSVoiceStateCommand ( bool state , User user , ILogger logger )
{
_state = state ;
_user = user ;
_logger = logger ;
}
2024-08-04 19:46:10 -04:00
public async Task Execute ( IDictionary < string , string > values , ChannelChatMessage message , HermesSocketClient client )
2024-07-19 12:56:41 -04:00
{
if ( _user = = null | | _user . VoicesAvailable = = null )
return ;
2024-06-16 20:19:31 -04:00
2024-07-19 12:56:41 -04:00
var voiceName = values [ "voiceName" ] ;
var voiceNameLower = voiceName . ToLower ( ) ;
var voiceId = _user . VoicesAvailable . FirstOrDefault ( v = > v . Value . ToLower ( ) = = voiceNameLower ) . Key ;
2024-06-16 20:19:31 -04:00
2024-07-19 12:56:41 -04:00
await client . UpdateTTSVoiceState ( voiceId , _state ) ;
2024-08-04 19:46:10 -04:00
_logger . Information ( $"Changed state for TTS voice [voice: {voiceName}][state: {_state}][invoker: {message.ChatterUserLogin}][id: {message.ChatterUserId}]" ) ;
2024-07-19 12:56:41 -04:00
}
2024-06-16 20:19:31 -04:00
}
2024-08-06 15:29:29 -04:00
private sealed class JoinRoomCommand : IChatPartialCommand
{
private readonly TwitchWebsocketClient _twitch ;
private readonly TwitchApiClient _client ;
private readonly User _user ;
private ILogger _logger ;
public bool AcceptCustomPermission { get = > true ; }
public JoinRoomCommand (
[FromKeyedServices("twitch")] SocketClient < TwitchWebsocketMessage > twitch ,
TwitchApiClient client ,
User user ,
ILogger logger
)
{
_twitch = ( twitch as TwitchWebsocketClient ) ! ;
_client = client ;
_user = user ;
_logger = logger ;
}
public async Task Execute ( IDictionary < string , string > values , ChannelChatMessage message , HermesSocketClient client )
{
var mention = values [ "mention" ] . ToLower ( ) ;
var fragment = message . Message . Fragments . FirstOrDefault ( f = > f . Mention ! = null & & f . Text . ToLower ( ) = = mention ) ;
if ( fragment = = null )
{
_logger . Warning ( "Cannot find the channel to join chat with." ) ;
return ;
}
2024-08-07 19:21:56 -04:00
string targetUserId = fragment . Mention ! . UserId ;
2024-08-06 18:50:45 -04:00
if ( targetUserId = = _user . TwitchUserId . ToString ( ) )
{
_logger . Warning ( "Cannot join yourself." ) ;
return ;
}
2024-08-07 19:21:56 -04:00
string targetUserLogin = fragment . Mention ! . UserLogin ;
2024-08-06 18:50:45 -04:00
string [ ] subscriptions = [ "channel.chat.message" , "channel.chat.message_delete" , "channel.chat.clear_user_messages" ] ;
foreach ( var subscription in subscriptions )
2024-08-07 19:21:56 -04:00
await Subscribe ( subscription , targetUserId , targetUserLogin , async ( ) = > await _client . CreateEventSubscription ( subscription , "1" , _twitch . SessionId , _user . TwitchUserId . ToString ( ) , targetUserId ) ) ;
await Subscribe ( "channel.raid" , targetUserId , targetUserLogin , async ( ) = > await _client . CreateChannelRaidEventSubscription ( "1" , _twitch . SessionId , targetUserId ) ) ;
_logger . Information ( $"Joined chat room [channel: {fragment.Mention.UserLogin}][channel: {targetUserLogin}][channel id: {targetUserId}][invoker: {message.ChatterUserLogin}][id: {message.ChatterUserId}]" ) ;
}
private async Task Subscribe ( string subscription , string targetId , string targetName , Func < Task < EventResponse < NotificationInfo > ? > > subscribe )
{
_logger . Debug ( $"Attempting to subscribe to Twitch events [subscription: {subscription}][target channel: {targetName}][target channel id: {targetId}]" ) ;
var data = await subscribe . Invoke ( ) ;
var info = data ? . Data ? . FirstOrDefault ( ) ;
if ( info = = null )
2024-08-06 18:50:45 -04:00
{
2024-08-07 19:21:56 -04:00
_logger . Warning ( "Could not find the subscription id." ) ;
return ;
2024-08-06 18:50:45 -04:00
}
2024-08-07 19:21:56 -04:00
_twitch . AddSubscription ( targetId , subscription , info . Id ) ;
2024-08-06 15:29:29 -04:00
}
}
private sealed class LeaveRoomCommand : IChatPartialCommand
{
private readonly TwitchWebsocketClient _twitch ;
private readonly TwitchApiClient _client ;
private readonly User _user ;
private ILogger _logger ;
public bool AcceptCustomPermission { get = > true ; }
public LeaveRoomCommand (
[FromKeyedServices("twitch")] SocketClient < TwitchWebsocketMessage > twitch ,
TwitchApiClient client ,
User user ,
ILogger logger
)
{
_twitch = ( twitch as TwitchWebsocketClient ) ! ;
_client = client ;
_user = user ;
_logger = logger ;
}
public async Task Execute ( IDictionary < string , string > values , ChannelChatMessage message , HermesSocketClient client )
{
var mention = values [ "mention" ] . ToLower ( ) ;
var fragment = message . Message . Fragments . FirstOrDefault ( f = > f . Mention ! = null & & f . Text . ToLower ( ) = = mention ) ;
if ( fragment ? . Mention = = null )
{
_logger . Warning ( "Cannot find the channel to leave chat from." ) ;
return ;
}
2024-08-06 18:50:45 -04:00
string targetUserId = fragment . Mention ! . UserId ! ;
if ( targetUserId = = _user . TwitchUserId . ToString ( ) )
2024-08-06 15:29:29 -04:00
{
2024-08-06 18:50:45 -04:00
_logger . Warning ( "Cannot join yourself." ) ;
2024-08-06 15:29:29 -04:00
return ;
}
2024-08-07 19:21:56 -04:00
string [ ] subscriptions = [ "channel.chat.message" , "channel.chat.message_delete" , "channel.chat.clear_user_messages" , "channel.raid" ] ;
2024-08-06 18:50:45 -04:00
foreach ( var subscription in subscriptions )
2024-08-06 15:29:29 -04:00
{
2024-08-06 18:50:45 -04:00
var subscriptionId = _twitch . GetSubscriptionId ( targetUserId , subscription ) ;
if ( subscriptionId = = null )
{
_logger . Warning ( $"Cannot find the subscription for that channel [subscription: {subscription}]" ) ;
continue ;
}
try
{
await _client . DeleteEventSubscription ( subscriptionId ) ;
_twitch . RemoveSubscription ( targetUserId , subscription ) ;
_logger . Information ( $"Left chat room [channel: {fragment.Mention.UserLogin}][channel id: {targetUserId}][invoker: {message.ChatterUserLogin}][id: {message.ChatterUserId}]" ) ;
}
catch ( Exception ex )
{
_logger . Error ( ex , $"Failed to delete the subscription from Twitch [subscription: {subscription}][subscription id: {subscriptionId}]" ) ;
}
2024-08-06 15:29:29 -04:00
}
}
}
2024-06-16 20:19:31 -04:00
}
}