2024-03-12 14:05:27 -04:00
using System.Text.Json ;
using TwitchChatTTS.Helpers ;
2024-06-16 20:19:31 -04:00
using Serilog ;
2024-03-12 14:05:27 -04:00
using TwitchChatTTS ;
using TwitchLib.Api.Core.Exceptions ;
using TwitchLib.Client.Events ;
using TwitchLib.Client.Models ;
using TwitchLib.Communication.Events ;
using static TwitchChatTTS . Configuration ;
2024-03-15 08:27:35 -04:00
using Microsoft.Extensions.DependencyInjection ;
using CommonSocketLibrary.Abstract ;
using CommonSocketLibrary.Common ;
using TwitchLib.PubSub.Interfaces ;
using TwitchLib.Client.Interfaces ;
using TwitchChatTTS.OBS.Socket ;
2024-03-12 14:05:27 -04:00
2024-06-16 20:19:31 -04:00
public class TwitchApiClient
{
2024-03-15 08:27:35 -04:00
private readonly Configuration _configuration ;
2024-06-16 20:19:31 -04:00
private readonly ILogger _logger ;
private TwitchBotAuth _token ;
2024-03-15 08:27:35 -04:00
private readonly ITwitchClient _client ;
private readonly ITwitchPubSub _publisher ;
2024-06-16 20:19:31 -04:00
private readonly WebClientWrap _web ;
2024-03-15 08:27:35 -04:00
private readonly IServiceProvider _serviceProvider ;
2024-06-16 20:19:31 -04:00
private bool _initialized ;
private string _broadcasterId ;
2024-03-15 08:27:35 -04:00
public TwitchApiClient (
Configuration configuration ,
2024-06-16 20:19:31 -04:00
TwitchBotAuth token ,
2024-03-15 08:27:35 -04:00
ITwitchClient twitchClient ,
ITwitchPubSub twitchPublisher ,
2024-06-16 20:19:31 -04:00
IServiceProvider serviceProvider ,
ILogger logger
)
{
2024-03-15 08:27:35 -04:00
_configuration = configuration ;
_token = token ;
_client = twitchClient ;
_publisher = twitchPublisher ;
_serviceProvider = serviceProvider ;
2024-06-16 20:19:31 -04:00
_logger = logger ;
_initialized = false ;
2024-03-12 14:05:27 -04:00
2024-06-16 20:19:31 -04:00
_web = new WebClientWrap ( new JsonSerializerOptions ( )
{
2024-03-12 14:05:27 -04:00
PropertyNameCaseInsensitive = false ,
PropertyNamingPolicy = JsonNamingPolicy . SnakeCaseLower
} ) ;
2024-03-15 08:27:35 -04:00
if ( ! string . IsNullOrWhiteSpace ( _configuration . Hermes ? . Token ) )
2024-06-16 20:19:31 -04:00
_web . AddHeader ( "x-api-key" , _configuration . Hermes . Token . Trim ( ) ) ;
2024-03-12 14:05:27 -04:00
}
2024-06-16 20:19:31 -04:00
public async Task < bool > Authorize ( string broadcasterId )
{
try
{
var authorize = await _web . GetJson < TwitchBotAuth > ( "https://hermes.goblincaves.com/api/account/reauthorize" ) ;
if ( authorize ! = null & & broadcasterId = = authorize . BroadcasterId )
{
2024-03-15 08:27:35 -04:00
_token . AccessToken = authorize . AccessToken ;
_token . RefreshToken = authorize . RefreshToken ;
2024-06-16 20:19:31 -04:00
_token . UserId = authorize . UserId ;
_token . BroadcasterId = authorize . BroadcasterId ;
_logger . Information ( "Updated Twitch API tokens." ) ;
}
else if ( authorize ! = null )
{
_logger . Error ( "Twitch API Authorization failed: " + authorize . AccessToken + " | " + authorize . RefreshToken + " | " + authorize . UserId + " | " + authorize . BroadcasterId ) ;
return false ;
2024-03-12 14:05:27 -04:00
}
2024-06-16 20:19:31 -04:00
_broadcasterId = broadcasterId ;
return true ;
}
catch ( HttpResponseException e )
{
2024-03-15 08:27:35 -04:00
if ( string . IsNullOrWhiteSpace ( _configuration . Hermes ? . Token ) )
2024-06-16 20:19:31 -04:00
_logger . Error ( "No Hermes API key found. Enter it into the configuration file." ) ;
2024-03-12 14:05:27 -04:00
else
2024-06-16 20:19:31 -04:00
_logger . Error ( "Invalid Hermes API key. Double check the token. HTTP Error Code: " + e . HttpResponse . StatusCode ) ;
}
catch ( JsonException )
{
}
catch ( Exception e )
{
_logger . Error ( e , "Failed to authorize to Twitch API." ) ;
2024-03-12 14:05:27 -04:00
}
2024-06-16 20:19:31 -04:00
return false ;
2024-03-12 14:05:27 -04:00
}
2024-06-16 20:19:31 -04:00
public async Task Connect ( )
{
2024-03-15 08:27:35 -04:00
_client . Connect ( ) ;
await _publisher . ConnectAsync ( ) ;
2024-03-12 14:05:27 -04:00
}
2024-06-16 20:19:31 -04:00
public void InitializeClient ( string username , IEnumerable < string > channels )
{
2024-03-15 08:27:35 -04:00
ConnectionCredentials credentials = new ConnectionCredentials ( username , _token ? . AccessToken ) ;
_client . Initialize ( credentials , channels . Distinct ( ) . ToList ( ) ) ;
2024-03-12 14:05:27 -04:00
2024-06-16 20:19:31 -04:00
if ( _initialized )
{
_logger . Debug ( "Twitch API client has already been initialized." ) ;
2024-03-12 14:05:27 -04:00
return ;
}
2024-06-16 20:19:31 -04:00
_initialized = true ;
2024-03-12 14:05:27 -04:00
2024-06-16 20:19:31 -04:00
_client . OnJoinedChannel + = async Task ( object? s , OnJoinedChannelArgs e ) = >
{
_logger . Information ( "Joined channel: " + e . Channel ) ;
2024-03-12 14:05:27 -04:00
} ;
2024-06-16 20:19:31 -04:00
_client . OnConnected + = async Task ( object? s , OnConnectedArgs e ) = >
{
_logger . Information ( "-----------------------------------------------------------" ) ;
2024-03-12 14:05:27 -04:00
} ;
2024-06-16 20:19:31 -04:00
_client . OnIncorrectLogin + = async Task ( object? s , OnIncorrectLoginArgs e ) = >
{
_logger . Error ( e . Exception , "Incorrect Login on Twitch API client." ) ;
2024-03-12 14:05:27 -04:00
2024-06-16 20:19:31 -04:00
_logger . Information ( "Attempting to re-authorize." ) ;
await Authorize ( _broadcasterId ) ;
2024-03-12 14:05:27 -04:00
} ;
2024-06-16 20:19:31 -04:00
_client . OnConnectionError + = async Task ( object? s , OnConnectionErrorArgs e ) = >
{
_logger . Error ( "Connection Error: " + e . Error . Message + " (" + e . Error . GetType ( ) . Name + ")" ) ;
2024-03-15 08:27:35 -04:00
2024-06-16 20:19:31 -04:00
_logger . Information ( "Attempting to re-authorize." ) ;
await Authorize ( _broadcasterId ) ;
2024-03-12 14:05:27 -04:00
} ;
2024-06-16 20:19:31 -04:00
_client . OnError + = async Task ( object? s , OnErrorEventArgs e ) = >
{
_logger . Error ( e . Exception , "Twitch API client error." ) ;
2024-03-12 14:05:27 -04:00
} ;
}
2024-06-16 20:19:31 -04:00
public void InitializePublisher ( )
{
_publisher . OnPubSubServiceConnected + = async ( s , e ) = >
{
2024-03-15 08:27:35 -04:00
_publisher . ListenToChannelPoints ( _token . BroadcasterId ) ;
_publisher . ListenToFollows ( _token . BroadcasterId ) ;
2024-03-12 14:05:27 -04:00
2024-03-15 08:27:35 -04:00
await _publisher . SendTopicsAsync ( _token . AccessToken ) ;
2024-06-16 20:19:31 -04:00
_logger . Information ( "Twitch PubSub has been connected." ) ;
2024-03-12 14:05:27 -04:00
} ;
2024-06-16 20:19:31 -04:00
_publisher . OnFollow + = ( s , e ) = >
{
2024-03-15 08:27:35 -04:00
var client = _serviceProvider . GetRequiredKeyedService < SocketClient < WebSocketMessage > > ( "obs" ) as OBSSocketClient ;
if ( _configuration . Twitch ? . TtsWhenOffline ! = true & & client ? . Live = = false )
return ;
2024-06-16 20:19:31 -04:00
_logger . Information ( "Follow: " + e . DisplayName ) ;
2024-03-12 14:05:27 -04:00
} ;
2024-06-16 20:19:31 -04:00
_publisher . OnChannelPointsRewardRedeemed + = ( s , e ) = >
{
2024-03-15 08:27:35 -04:00
var client = _serviceProvider . GetRequiredKeyedService < SocketClient < WebSocketMessage > > ( "obs" ) as OBSSocketClient ;
if ( _configuration . Twitch ? . TtsWhenOffline ! = true & & client ? . Live = = false )
return ;
2024-06-16 20:19:31 -04:00
_logger . Information ( $"Channel Point Reward Redeemed [redeem: {e.RewardRedeemed.Redemption.Reward.Title}][id: {e.RewardRedeemed.Redemption.Id}]" ) ;
2024-03-12 14:05:27 -04:00
2024-06-16 20:19:31 -04:00
if ( _configuration . Twitch ? . Redeems = = null )
2024-03-12 14:05:27 -04:00
return ;
var redeemName = e . RewardRedeemed . Redemption . Reward . Title . ToLower ( ) . Trim ( ) . Replace ( " " , "-" ) ;
2024-03-15 08:27:35 -04:00
if ( ! _configuration . Twitch . Redeems . TryGetValue ( redeemName , out RedeemConfiguration ? redeem ) )
2024-03-12 14:05:27 -04:00
return ;
2024-03-15 08:27:35 -04:00
if ( redeem = = null )
2024-03-12 14:05:27 -04:00
return ;
2024-06-16 20:19:31 -04:00
2024-03-12 14:05:27 -04:00
// Write or append to file if needed.
var outputFile = string . IsNullOrWhiteSpace ( redeem . OutputFilePath ) ? null : redeem . OutputFilePath . Trim ( ) ;
2024-06-16 20:19:31 -04:00
if ( outputFile = = null )
{
_logger . Debug ( $"No output file was provided for redeem [redeem: {e.RewardRedeemed.Redemption.Reward.Title}][id: {e.RewardRedeemed.Redemption.Id}]" ) ;
}
else
{
2024-03-12 14:05:27 -04:00
var outputContent = string . IsNullOrWhiteSpace ( redeem . OutputContent ) ? null : redeem . OutputContent . Trim ( ) . Replace ( "%USER%" , e . RewardRedeemed . Redemption . User . DisplayName ) . Replace ( "\\n" , "\n" ) ;
2024-06-16 20:19:31 -04:00
if ( outputContent = = null )
{
_logger . Warning ( $"No output content was provided for redeem [redeem: {e.RewardRedeemed.Redemption.Reward.Title}][id: {e.RewardRedeemed.Redemption.Id}]" ) ;
}
else
{
if ( redeem . OutputAppend = = true )
{
2024-03-12 14:05:27 -04:00
File . AppendAllText ( outputFile , outputContent + "\n" ) ;
2024-06-16 20:19:31 -04:00
}
else
{
2024-03-12 14:05:27 -04:00
File . WriteAllText ( outputFile , outputContent ) ;
}
}
}
// Play audio file if needed.
var audioFile = string . IsNullOrWhiteSpace ( redeem . AudioFilePath ) ? null : redeem . AudioFilePath . Trim ( ) ;
2024-06-16 20:19:31 -04:00
if ( audioFile = = null )
{
_logger . Debug ( $"No audio file was provided for redeem [redeem: {e.RewardRedeemed.Redemption.Reward.Title}][id: {e.RewardRedeemed.Redemption.Id}]" ) ;
2024-03-12 14:05:27 -04:00
}
2024-06-16 20:19:31 -04:00
else if ( ! File . Exists ( audioFile ) )
{
_logger . Warning ( $"Cannot find audio file [location: {audioFile}] for redeem [redeem: {e.RewardRedeemed.Redemption.Reward.Title}][id: {e.RewardRedeemed.Redemption.Id}]" ) ;
2024-03-12 14:05:27 -04:00
}
2024-06-16 20:19:31 -04:00
else
{
AudioPlaybackEngine . Instance . PlaySound ( audioFile ) ;
2024-03-12 14:05:27 -04:00
}
2024-06-16 20:19:31 -04:00
} ;
2024-03-12 14:05:27 -04:00
}
2024-06-16 20:19:31 -04:00
public void AddOnNewMessageReceived ( AsyncEventHandler < OnMessageReceivedArgs > handler )
{
2024-03-15 08:27:35 -04:00
_client . OnMessageReceived + = handler ;
2024-03-12 14:05:27 -04:00
}
}