Compare commits
2 Commits
0932c1c38e
...
b35183249b
Author | SHA1 | Date | |
---|---|---|---|
b35183249b | |||
48dd6858a1 |
@ -1,3 +1,4 @@
|
||||
using System.Text.Json;
|
||||
using CommonSocketLibrary.Abstract;
|
||||
using CommonSocketLibrary.Common;
|
||||
using HermesSocketLibrary.Socket.Data;
|
||||
@ -35,7 +36,7 @@ namespace TwitchChatTTS.Hermes.Socket.Handlers
|
||||
|
||||
_logger.Debug($"Received a Hermes request message [type: {message.Request.Type}][data: {string.Join(',', message.Request.Data?.Select(entry => entry.Key + '=' + entry.Value) ?? Array.Empty<string>())}]");
|
||||
var json = message.Data?.ToString();
|
||||
if (message.Request.Type == null || json == null)
|
||||
if (message.Request.Type == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -15,8 +15,6 @@ namespace TwitchChatTTS.Hermes.Socket.Requests
|
||||
|
||||
public void Fulfill(string type, string requestId, string data, IDictionary<string, object>? requestData)
|
||||
{
|
||||
if (data == null)
|
||||
return;
|
||||
if (!_acknowledgements.TryGetValue(type, out var ack))
|
||||
{
|
||||
_logger.Warning($"Found unknown request type when acknowledging [type: {type}]");
|
||||
|
@ -35,6 +35,7 @@ using TwitchChatTTS.Chat.Observers;
|
||||
using TwitchChatTTS.Chat.Commands.Limits;
|
||||
using TwitchChatTTS.Hermes.Socket.Requests;
|
||||
using TwitchChatTTS.Bus;
|
||||
using TwitchChatTTS.Veadotube;
|
||||
|
||||
// dotnet publish -r linux-x64 -p:PublishSingleFile=true --self-contained true
|
||||
// dotnet publish -r win-x64 -p:PublishSingleFile=true --self-contained true
|
||||
@ -147,6 +148,10 @@ s.AddKeyedSingleton<IWebSocketHandler, EndOfStreamHandler>("7tv");
|
||||
s.AddKeyedSingleton<MessageTypeManager<IWebSocketHandler>, SevenMessageTypeManager>("7tv");
|
||||
s.AddKeyedSingleton<SocketClient<WebSocketMessage>, SevenSocketClient>("7tv");
|
||||
|
||||
// Veadotube
|
||||
s.AddKeyedSingleton<MessageTypeManager<IWebSocketHandler>, VeadoMessageTypeManager>("veadotube");
|
||||
s.AddKeyedSingleton<SocketClient<object>, VeadoSocketClient>("veadotube");
|
||||
|
||||
// Nightbot
|
||||
s.AddSingleton<NightbotApiClient>();
|
||||
|
||||
|
13
TTS.cs
13
TTS.cs
@ -14,6 +14,7 @@ using TwitchChatTTS.Twitch.Socket;
|
||||
using TwitchChatTTS.Chat.Commands;
|
||||
using System.Text;
|
||||
using TwitchChatTTS.Chat.Speech;
|
||||
using TwitchChatTTS.Veadotube;
|
||||
|
||||
namespace TwitchChatTTS
|
||||
{
|
||||
@ -29,6 +30,7 @@ namespace TwitchChatTTS
|
||||
private readonly OBSSocketClient _obs;
|
||||
private readonly SevenSocketClient _seven;
|
||||
private readonly TwitchWebsocketClient _twitch;
|
||||
private readonly VeadoSocketClient _veado;
|
||||
private readonly ICommandFactory _commandFactory;
|
||||
private readonly ICommandManager _commandManager;
|
||||
private readonly IEmoteDatabase _emotes;
|
||||
@ -45,6 +47,7 @@ namespace TwitchChatTTS
|
||||
[FromKeyedServices("obs")] SocketClient<WebSocketMessage> obs,
|
||||
[FromKeyedServices("7tv")] SocketClient<WebSocketMessage> seven,
|
||||
[FromKeyedServices("twitch")] SocketClient<TwitchWebsocketMessage> twitch,
|
||||
[FromKeyedServices("veadotube")] SocketClient<object> veado,
|
||||
ICommandFactory commandFactory,
|
||||
ICommandManager commandManager,
|
||||
IEmoteDatabase emotes,
|
||||
@ -61,6 +64,7 @@ namespace TwitchChatTTS
|
||||
_obs = (obs as OBSSocketClient)!;
|
||||
_seven = (seven as SevenSocketClient)!;
|
||||
_twitch = (twitch as TwitchWebsocketClient)!;
|
||||
_veado = (veado as VeadoSocketClient)!;
|
||||
_commandFactory = commandFactory;
|
||||
_commandManager = commandManager;
|
||||
_emotes = emotes;
|
||||
@ -132,6 +136,15 @@ namespace TwitchChatTTS
|
||||
}
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
_veado.Initialize();
|
||||
await _veado.Connect();
|
||||
}
|
||||
catch (Exception e) {
|
||||
_logger.Warning(e, "Failed to connect to Veado websocket server.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _twitch.Connect();
|
||||
|
@ -10,6 +10,7 @@ using TwitchChatTTS.Bus.Data;
|
||||
using TwitchChatTTS.Hermes.Socket;
|
||||
using TwitchChatTTS.OBS.Socket;
|
||||
using TwitchChatTTS.OBS.Socket.Data;
|
||||
using TwitchChatTTS.Veadotube;
|
||||
|
||||
namespace TwitchChatTTS.Twitch.Redemptions
|
||||
{
|
||||
@ -20,6 +21,7 @@ namespace TwitchChatTTS.Twitch.Redemptions
|
||||
private readonly User _user;
|
||||
private readonly OBSSocketClient _obs;
|
||||
private readonly HermesSocketClient _hermes;
|
||||
private readonly VeadoSocketClient _veado;
|
||||
private readonly NightbotApiClient _nightbot;
|
||||
private readonly AudioPlaybackEngine _playback;
|
||||
private readonly ILogger _logger;
|
||||
@ -32,6 +34,7 @@ namespace TwitchChatTTS.Twitch.Redemptions
|
||||
User user,
|
||||
[FromKeyedServices("obs")] SocketClient<WebSocketMessage> obs,
|
||||
[FromKeyedServices("hermes")] SocketClient<WebSocketMessage> hermes,
|
||||
[FromKeyedServices("veadotube")] SocketClient<object> veado,
|
||||
NightbotApiClient nightbot,
|
||||
AudioPlaybackEngine playback,
|
||||
ILogger logger)
|
||||
@ -41,6 +44,7 @@ namespace TwitchChatTTS.Twitch.Redemptions
|
||||
_user = user;
|
||||
_obs = (obs as OBSSocketClient)!;
|
||||
_hermes = (hermes as HermesSocketClient)!;
|
||||
_veado = (veado as VeadoSocketClient)!;
|
||||
_nightbot = nightbot;
|
||||
_playback = playback;
|
||||
_logger = logger;
|
||||
@ -220,6 +224,15 @@ namespace TwitchChatTTS.Twitch.Redemptions
|
||||
case "NIGHTBOT_CLEAR_QUEUE":
|
||||
await _nightbot.ClearQueue();
|
||||
break;
|
||||
case "VEADOTUBE_SET_STATE":
|
||||
await _veado.SetCurrentState(action.Data["state"]);
|
||||
break;
|
||||
case "VEADOTUBE_PUSH_STATE":
|
||||
await _veado.PushState(action.Data["state"]);
|
||||
break;
|
||||
case "VEADOTUBE_POP_STATE":
|
||||
await _veado.PopState(action.Data["state"]);
|
||||
break;
|
||||
default:
|
||||
_logger.Warning($"Unknown redeemable action has occured. Update needed? [type: {action.Type}][chatter: {senderDisplayName}][chatter id: {senderId}]");
|
||||
break;
|
||||
|
@ -12,6 +12,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageReference Include="NAudio" Version="2.2.1" />
|
||||
<PackageReference Include="NAudio.Extras" Version="2.2.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Serilog" Version="4.0.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2-dev-00338" />
|
||||
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
|
||||
|
14
Veadotube/VeadoInstanceInfo.cs
Normal file
14
Veadotube/VeadoInstanceInfo.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace TwitchChatTTS.Veadotube
|
||||
{
|
||||
public class VeadoInstanceInfo
|
||||
{
|
||||
[JsonPropertyName("time")]
|
||||
public long Time { get; set; }
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
[JsonPropertyName("server")]
|
||||
public string Server { get; set; }
|
||||
}
|
||||
}
|
38
Veadotube/VeadoMessage.cs
Normal file
38
Veadotube/VeadoMessage.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace TwitchChatTTS.Veadotube
|
||||
{
|
||||
public class VeadoPayloadMessage
|
||||
{
|
||||
public string Event { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Id { get; set; }
|
||||
public object Payload { get; set; }
|
||||
}
|
||||
|
||||
public class VeadoEventMessage
|
||||
{
|
||||
[JsonPropertyName("event")]
|
||||
public string Event { get; set; }
|
||||
}
|
||||
|
||||
public class VeadoNodeState {
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
public class VeadoNodeStateListMessage : VeadoEventMessage {
|
||||
public IEnumerable<VeadoNodeState> Entries { get; set; }
|
||||
}
|
||||
|
||||
public class VeadoNodeStateMessage : VeadoEventMessage {
|
||||
public string State { get; set; }
|
||||
}
|
||||
|
||||
public class VeadoNodeThumbMessage {
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
public string Png { get; set; }
|
||||
}
|
||||
}
|
17
Veadotube/VeadoMessageTypeManager.cs
Normal file
17
Veadotube/VeadoMessageTypeManager.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using CommonSocketLibrary.Common;
|
||||
using CommonSocketLibrary.Socket.Manager;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Serilog;
|
||||
|
||||
namespace TwitchChatTTS.Veadotube
|
||||
{
|
||||
public class VeadoMessageTypeManager : WebSocketMessageTypeManager
|
||||
{
|
||||
public VeadoMessageTypeManager(
|
||||
[FromKeyedServices("veadotube")] IEnumerable<IWebSocketHandler> handlers,
|
||||
ILogger logger
|
||||
) : base(handlers, logger)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
222
Veadotube/VeadoSocketClient.cs
Normal file
222
Veadotube/VeadoSocketClient.cs
Normal file
@ -0,0 +1,222 @@
|
||||
using CommonSocketLibrary.Common;
|
||||
using CommonSocketLibrary.Abstract;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Serilog;
|
||||
using System.Text.Json;
|
||||
using CommonSocketLibrary.Backoff;
|
||||
using System.Text;
|
||||
using System.Net.WebSockets;
|
||||
|
||||
namespace TwitchChatTTS.Veadotube
|
||||
{
|
||||
public class VeadoSocketClient : SocketClient<object>
|
||||
{
|
||||
private VeadoInstanceInfo? Instance;
|
||||
|
||||
public bool Connected { get; set; }
|
||||
public bool Identified { get; set; }
|
||||
public bool Streaming { get; set; }
|
||||
|
||||
|
||||
public VeadoSocketClient(
|
||||
[FromKeyedServices("veadotube")] IEnumerable<IWebSocketHandler> handlers,
|
||||
[FromKeyedServices("veadotube")] MessageTypeManager<IWebSocketHandler> typeManager,
|
||||
ILogger logger
|
||||
) : base(logger, new JsonSerializerOptions()
|
||||
{
|
||||
PropertyNameCaseInsensitive = false,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task<T> Deserialize<T>(Stream stream)
|
||||
{
|
||||
using StreamReader reader = new StreamReader(stream);
|
||||
string content = await reader.ReadToEndAsync();
|
||||
int index = content.IndexOf(':');
|
||||
string json = content.Substring(index + 1).Replace("\0", string.Empty);
|
||||
T? value = JsonSerializer.Deserialize<T>(json, _options);
|
||||
return value!;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_logger.Information($"Initializing Veadotube websocket client.");
|
||||
OnConnected += (sender, e) =>
|
||||
{
|
||||
Connected = true;
|
||||
_logger.Information("Veadotube websocket client connected.");
|
||||
};
|
||||
|
||||
OnDisconnected += async (sender, e) =>
|
||||
{
|
||||
_logger.Information($"Veadotube websocket client disconnected [status: {e.Status}][reason: {e.Reason}] " + (Identified ? "Will be attempting to reconnect every 30 seconds." : "Will not be attempting to reconnect."));
|
||||
|
||||
Connected = false;
|
||||
Identified = false;
|
||||
Streaming = false;
|
||||
|
||||
await Reconnect(new ExponentialBackoff(5000, 300000));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public override async Task Connect()
|
||||
{
|
||||
if (!UpdateURL() || string.IsNullOrEmpty(Instance?.Server) || string.IsNullOrEmpty(Instance.Name))
|
||||
{
|
||||
_logger.Warning("Lacking connection info for Veadotube websockets. Not connecting to Veadotube.");
|
||||
return;
|
||||
}
|
||||
|
||||
string url = $"ws://{Instance.Server}?n={Instance.Name}";
|
||||
_logger.Debug($"Veadotube websocket client attempting to connect to {url}");
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectAsync(url);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_logger.Warning("Connecting to Veadotube failed. Skipping Veadotube websockets.");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task FetchStates()
|
||||
{
|
||||
await Send(new VeadoPayloadMessage()
|
||||
{
|
||||
Event = "payload",
|
||||
Type = "stateEvents",
|
||||
Id = "mini",
|
||||
Payload = new VeadoEventMessage()
|
||||
{
|
||||
Event = "list",
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task SetCurrentState(string stateId)
|
||||
{
|
||||
await Send(new VeadoPayloadMessage()
|
||||
{
|
||||
Event = "payload",
|
||||
Type = "stateEvents",
|
||||
Id = "mini",
|
||||
Payload = new VeadoNodeStateMessage()
|
||||
{
|
||||
Event = "set",
|
||||
State = stateId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task PushState(string stateId)
|
||||
{
|
||||
await Send(new VeadoPayloadMessage()
|
||||
{
|
||||
Event = "payload",
|
||||
Type = "stateEvents",
|
||||
Id = "mini",
|
||||
Payload = new VeadoNodeStateMessage()
|
||||
{
|
||||
Event = "push",
|
||||
State = stateId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task PopState(string stateId)
|
||||
{
|
||||
await Send(new VeadoPayloadMessage()
|
||||
{
|
||||
Event = "payload",
|
||||
Type = "stateEvents",
|
||||
Id = "mini",
|
||||
Payload = new VeadoNodeStateMessage()
|
||||
{
|
||||
Event = "pop",
|
||||
State = stateId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task Send<T>(T data)
|
||||
{
|
||||
if (_socket == null || data == null)
|
||||
return;
|
||||
if (!Connected)
|
||||
{
|
||||
_logger.Debug("Not sending Veadotube message due to no connection.");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var content = "nodes:" + JsonSerializer.Serialize(data, _options);
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(content);
|
||||
var array = new ArraySegment<byte>(bytes);
|
||||
var total = bytes.Length;
|
||||
var current = 0;
|
||||
|
||||
while (current < total)
|
||||
{
|
||||
var size = Encoding.UTF8.GetBytes(content.Substring(current), array);
|
||||
await _socket.SendAsync(array, WebSocketMessageType.Text, current + size >= total, _cts!.Token);
|
||||
current += size;
|
||||
}
|
||||
_logger.Debug($"Veado TX [message type: {typeof(T).Name}]: " + content);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (_socket.State.ToString().Contains("Close") || _socket.State == WebSocketState.Aborted)
|
||||
{
|
||||
await DisconnectAsync(new SocketDisconnectionEventArgs(_socket.CloseStatus.ToString()!, _socket.CloseStatusDescription ?? string.Empty));
|
||||
_logger.Warning($"Socket state on closing = {_socket.State} | {_socket.CloseStatus?.ToString()} | {_socket.CloseStatusDescription}");
|
||||
}
|
||||
_logger.Error(e, $"Failed to send a websocket message to Veado [message type: {typeof(T).Name}]");
|
||||
}
|
||||
}
|
||||
|
||||
private bool UpdateURL()
|
||||
{
|
||||
string path = Environment.ExpandEnvironmentVariables("%userprofile%/.veadotube/instances");
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
var directory = Directory.CreateDirectory(path);
|
||||
var files = directory.GetFiles()
|
||||
.Where(f => f.Name.StartsWith("mini-"))
|
||||
.OrderByDescending(f => f.CreationTime);
|
||||
if (files.Any())
|
||||
{
|
||||
_logger.Debug("Veadotube's instance file exists: " + files.First().FullName);
|
||||
var data = File.ReadAllText(files.First().FullName);
|
||||
var instance = JsonSerializer.Deserialize<VeadoInstanceInfo>(data);
|
||||
|
||||
if (instance != null)
|
||||
{
|
||||
Instance = instance;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to find Veadotube instance information.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override Task OnResponseReceived(object? content)
|
||||
{
|
||||
var contentAsString = JsonSerializer.Serialize(content);
|
||||
_logger.Debug("VEADO RX: " + contentAsString);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user