Fixed TTS using StreamElements. Fixed several issues.

This commit is contained in:
Tom
2026-01-03 05:19:33 +00:00
parent fb04f4003f
commit aa89578297
16 changed files with 275 additions and 135 deletions

View File

@@ -229,6 +229,7 @@ namespace TwitchChatTTS.Chat.Commands.Limits
var temp = usage.Uses.Skip(sizeDiff); var temp = usage.Uses.Skip(sizeDiff);
var tempSize = usage.Uses.Length - sizeDiff; var tempSize = usage.Uses.Length - sizeDiff;
usage.Uses = temp.Union(new DateTime[Math.Max(0, Limit.Count - tempSize)]).ToArray(); usage.Uses = temp.Union(new DateTime[Math.Max(0, Limit.Count - tempSize)]).ToArray();
}
finally finally
{ {
_rwls.ExitWriteLock(); _rwls.ExitWriteLock();
@@ -256,7 +257,7 @@ namespace TwitchChatTTS.Chat.Commands.Limits
} }
finally finally
{ {
_rwls.ExitWriteLock(); _rwls.ExitUpgradeableReadLock();
} }
return true; return true;

View File

@@ -1,5 +1,3 @@
using System.Collections.Concurrent;
using HermesSocketLibrary.Requests.Messages; using HermesSocketLibrary.Requests.Messages;
using Serilog; using Serilog;
@@ -9,22 +7,35 @@ namespace TwitchChatTTS.Chat.Groups
{ {
private readonly IDictionary<string, Group> _groups; private readonly IDictionary<string, Group> _groups;
private readonly IDictionary<long, ICollection<string>> _chatters; private readonly IDictionary<long, ICollection<string>> _chatters;
private readonly ReaderWriterLockSlim _rwls;
private readonly ILogger _logger; private readonly ILogger _logger;
public ChatterGroupManager(ILogger logger) public ChatterGroupManager(ILogger logger)
{ {
_logger = logger; _logger = logger;
_groups = new ConcurrentDictionary<string, Group>(); _groups = new Dictionary<string, Group>();
_chatters = new ConcurrentDictionary<long, ICollection<string>>(); _chatters = new Dictionary<long, ICollection<string>>();
_rwls = new ReaderWriterLockSlim();
} }
public void Add(Group group) public void Add(Group group)
{
_rwls.EnterWriteLock();
try
{ {
_groups.Add(group.Id, group); _groups.Add(group.Id, group);
} }
finally
{
_rwls.ExitWriteLock();
}
}
public void Add(long chatterId, string groupId) public void Add(long chatterId, string groupId)
{
_rwls.EnterWriteLock();
try
{ {
if (_chatters.TryGetValue(chatterId, out var list)) if (_chatters.TryGetValue(chatterId, out var list))
{ {
@@ -34,8 +45,16 @@ namespace TwitchChatTTS.Chat.Groups
else else
_chatters.Add(chatterId, new List<string>() { groupId }); _chatters.Add(chatterId, new List<string>() { groupId });
} }
finally
{
_rwls.ExitWriteLock();
}
}
public void Add(long chatter, ICollection<string> groupIds) public void Add(long chatter, ICollection<string> groupIds)
{
_rwls.EnterWriteLock();
try
{ {
if (_chatters.TryGetValue(chatter, out var list)) if (_chatters.TryGetValue(chatter, out var list))
{ {
@@ -46,21 +65,45 @@ namespace TwitchChatTTS.Chat.Groups
else else
_chatters.Add(chatter, groupIds); _chatters.Add(chatter, groupIds);
} }
finally
{
_rwls.ExitWriteLock();
}
}
public void Clear() public void Clear()
{
_rwls.EnterWriteLock();
try
{ {
_groups.Clear(); _groups.Clear();
_chatters.Clear(); _chatters.Clear();
} }
finally
{
_rwls.ExitWriteLock();
}
}
public Group? Get(string groupId) public Group? Get(string groupId)
{
_rwls.EnterReadLock();
try
{ {
if (_groups.TryGetValue(groupId, out var group)) if (_groups.TryGetValue(groupId, out var group))
return group; return group;
return null; return null;
} }
finally
{
_rwls.ExitReadLock();
}
}
public IEnumerable<string> GetGroupNamesFor(long chatter) public IEnumerable<string> GetGroupNamesFor(long chatter)
{
_rwls.EnterReadLock();
try
{ {
if (_chatters.TryGetValue(chatter, out var groups)) if (_chatters.TryGetValue(chatter, out var groups))
return groups.Select(g => _groups.TryGetValue(g, out var group) ? group.Name : null) return groups.Select(g => _groups.TryGetValue(g, out var group) ? group.Name : null)
@@ -69,29 +112,61 @@ namespace TwitchChatTTS.Chat.Groups
return Array.Empty<string>(); return Array.Empty<string>();
} }
finally
{
_rwls.ExitReadLock();
}
}
public int GetPriorityFor(long chatter) public int GetPriorityFor(long chatter)
{
_rwls.EnterReadLock();
try
{ {
if (!_chatters.TryGetValue(chatter, out var groups)) if (!_chatters.TryGetValue(chatter, out var groups))
return 0; return 0;
return GetPriorityFor(groups); return GetPriorityFor(groups);
} }
finally
{
_rwls.ExitReadLock();
}
}
public int GetPriorityFor(IEnumerable<string> groupIds) public int GetPriorityFor(IEnumerable<string> groupIds)
{
_rwls.EnterReadLock();
try
{ {
var values = groupIds.Select(g => _groups.TryGetValue(g, out var group) ? group : null).Where(g => g != null); var values = groupIds.Select(g => _groups.TryGetValue(g, out var group) ? group : null).Where(g => g != null);
if (values.Any()) if (values.Any())
return values.Max(g => g!.Priority); return values.Max(g => g!.Priority);
return 0; return 0;
} }
finally
{
_rwls.ExitReadLock();
}
}
public void Modify(Group group) public void Modify(Group group)
{
_rwls.EnterWriteLock();
try
{ {
_groups[group.Id] = group; _groups[group.Id] = group;
} }
finally
{
_rwls.ExitWriteLock();
}
}
public bool Remove(string groupId) public bool Remove(string groupId)
{
_rwls.EnterWriteLock();
try
{ {
if (_groups.Remove(groupId)) if (_groups.Remove(groupId))
{ {
@@ -101,8 +176,16 @@ namespace TwitchChatTTS.Chat.Groups
} }
return false; return false;
} }
finally
{
_rwls.ExitReadLock();
}
}
public bool Remove(long chatterId, string groupId) public bool Remove(long chatterId, string groupId)
{
_rwls.EnterWriteLock();
try
{ {
if (_chatters.TryGetValue(chatterId, out var groups)) if (_chatters.TryGetValue(chatterId, out var groups))
{ {
@@ -113,5 +196,10 @@ namespace TwitchChatTTS.Chat.Groups
_logger.Debug($"Failed to remove chatter from group [chatter id: {chatterId}][group name: {_groups[groupId].Name}][group id: {groupId}]"); _logger.Debug($"Failed to remove chatter from group [chatter id: {chatterId}][group name: {_groups[groupId].Name}][group id: {groupId}]");
return false; return false;
} }
finally
{
_rwls.ExitReadLock();
}
}
} }
} }

View File

@@ -5,25 +5,38 @@ namespace TwitchChatTTS.Chat.Groups.Permissions
{ {
public class GroupPermissionManager : IGroupPermissionManager public class GroupPermissionManager : IGroupPermissionManager
{ {
private PermissionNode _root; private readonly PermissionNode _root;
private ILogger _logger; private readonly ILogger _logger;
private readonly ReaderWriterLockSlim _rwls;
public GroupPermissionManager(ILogger logger) public GroupPermissionManager(ILogger logger)
{ {
_logger = logger; _logger = logger;
_root = new PermissionNode(string.Empty, null, null); _root = new PermissionNode(string.Empty, null, null);
_rwls = new ReaderWriterLockSlim();
} }
public bool? CheckIfAllowed(string path) public bool? CheckIfAllowed(string path)
{
_rwls.EnterReadLock();
try
{ {
var res = Get(path)!.Allow; var res = Get(path)!.Allow;
_logger.Debug($"Permission Node GET {path} = {res?.ToString() ?? "null"}"); _logger.Debug($"Permission Node GET {path} = {res?.ToString() ?? "null"}");
return res; return res;
} }
finally
{
_rwls.ExitReadLock();
}
}
public bool? CheckIfDirectAllowed(string path) public bool? CheckIfDirectAllowed(string path)
{
_rwls.EnterReadLock();
try
{ {
var node = Get(path, nullIfMissing: true); var node = Get(path, nullIfMissing: true);
if (node == null) if (node == null)
@@ -33,6 +46,11 @@ namespace TwitchChatTTS.Chat.Groups.Permissions
_logger.Debug($"Permission Node GET {path} = {res?.ToString() ?? "null"} [direct]"); _logger.Debug($"Permission Node GET {path} = {res?.ToString() ?? "null"} [direct]");
return res; return res;
} }
finally
{
_rwls.ExitReadLock();
}
}
public bool? CheckIfAllowed(IEnumerable<string> groups, string path) public bool? CheckIfAllowed(IEnumerable<string> groups, string path)
{ {
@@ -63,16 +81,30 @@ namespace TwitchChatTTS.Chat.Groups.Permissions
} }
public void Clear() public void Clear()
{
_rwls.EnterWriteLock();
try
{ {
_root.Clear(); _root.Clear();
} }
finally
{
_rwls.ExitWriteLock();
}
}
public bool Remove(string path) public bool Remove(string path)
{
_rwls.EnterUpgradeableReadLock();
try
{ {
var node = Get(path); var node = Get(path);
if (node == null || node.Parent == null) if (node == null || node.Parent == null)
return false; return false;
_rwls.EnterWriteLock();
try
{
var parts = path.Split('.'); var parts = path.Split('.');
var last = parts.Last(); var last = parts.Last();
if (parts.Length > 1 && parts[parts.Length - 1] == node.Parent.Name || parts.Length == 1 && node.Parent.Name == null) if (parts.Length > 1 && parts[parts.Length - 1] == node.Parent.Name || parts.Length == 1 && node.Parent.Name == null)
@@ -83,13 +115,31 @@ namespace TwitchChatTTS.Chat.Groups.Permissions
} }
return false; return false;
} }
finally
{
_rwls.ExitWriteLock();
}
}
finally
{
_rwls.ExitUpgradeableReadLock();
}
}
public void Set(string path, bool? allow) public void Set(string path, bool? allow)
{
_rwls.EnterWriteLock();
try
{ {
var node = Get(path, true); var node = Get(path, true);
node!.Allow = allow; node!.Allow = allow;
_logger.Debug($"Permission Node ADD {path} = {allow?.ToString() ?? "null"}"); _logger.Debug($"Permission Node ADD {path} = {allow?.ToString() ?? "null"}");
} }
finally
{
_rwls.ExitWriteLock();
}
}
private PermissionNode? Get(string path, bool edit = false, bool nullIfMissing = false) private PermissionNode? Get(string path, bool edit = false, bool nullIfMissing = false)
{ {

View File

@@ -62,15 +62,15 @@ namespace TwitchChatTTS.Chat.Messaging
var emoteUsage = GetEmoteUsage(fragments); var emoteUsage = GetEmoteUsage(fragments);
var tasks = new List<Task>(); var tasks = new List<Task>();
if (_obs.Streaming && !_user.Slave) if ((!_obs.Connected || _obs.Streaming) && !_user.Slave)
{ {
if (emoteUsage.NewEmotes.Any()) if (emoteUsage.NewEmotes.Any())
tasks.Add(_hermes.SendEmoteDetails(emoteUsage.NewEmotes)); tasks.Add(_hermes.SendEmoteDetails(emoteUsage.NewEmotes));
if (emoteUsage.EmotesUsed.Any() && messageId != null && chatterId != null) if (emoteUsage.EmotesUsed.Any() && messageId != null && chatterId != null)
tasks.Add(_hermes.SendEmoteUsage(messageId, chatterId.Value, emoteUsage.EmotesUsed)); tasks.Add(_hermes.SendEmoteUsage(messageId, chatterId.Value, emoteUsage.EmotesUsed));
if (!string.IsNullOrEmpty(chatterLogin) && chatterId != null && !_user.Chatters.Contains(chatterId.Value)) if (chatterId.HasValue && !_user.Chatters.Contains(chatterId.Value))
{ {
tasks.Add(_hermes.SendChatterDetails(chatterId.Value, chatterLogin)); tasks.Add(_hermes.SendChatterDetails(chatterId.Value, chatterLogin!));
_user.Chatters.Add(chatterId.Value); _user.Chatters.Add(chatterId.Value);
} }
} }

View File

@@ -33,7 +33,6 @@ namespace TwitchChatTTS.Hermes.Socket.Handlers
if (message.AnotherClient) if (message.AnotherClient)
{ {
if (client.LoggedIn)
_logger.Warning($"Another client has connected to the same account via {(message.WebLogin ? "web login" : "application")}."); _logger.Warning($"Another client has connected to the same account via {(message.WebLogin ? "web login" : "application")}.");
return; return;
} }
@@ -51,6 +50,7 @@ namespace TwitchChatTTS.Hermes.Socket.Handlers
_user.TwitchUsername = message.UserName; _user.TwitchUsername = message.UserName;
_user.TwitchUserId = long.Parse(message.ProviderAccountId); _user.TwitchUserId = long.Parse(message.ProviderAccountId);
_user.OwnerId = message.OwnerId; _user.OwnerId = message.OwnerId;
_user.StreamElementsOverlayKey = message.StreamElementsOverlayKey;
_user.DefaultTTSVoice = message.DefaultTTSVoice; _user.DefaultTTSVoice = message.DefaultTTSVoice;
_user.VoicesAvailable = new ConcurrentDictionary<string, string>(message.TTSVoicesAvailable); _user.VoicesAvailable = new ConcurrentDictionary<string, string>(message.TTSVoicesAvailable);
_user.VoicesEnabled = new HashSet<string>(message.EnabledTTSVoices); _user.VoicesEnabled = new HashSet<string>(message.EnabledTTSVoices);
@@ -58,7 +58,7 @@ namespace TwitchChatTTS.Hermes.Socket.Handlers
_user.NightbotConnection = message.Connections.FirstOrDefault(c => c.Default && c.Type == "nightbot") ?? message.Connections.FirstOrDefault(c => c.Type == "nightbot"); _user.NightbotConnection = message.Connections.FirstOrDefault(c => c.Default && c.Type == "nightbot") ?? message.Connections.FirstOrDefault(c => c.Type == "nightbot");
if (_user.TwitchConnection != null) if (_user.TwitchConnection != null)
{ {
_logger.Information("Twitch connection: " + _user.TwitchConnection.Name + " / " + _user.TwitchConnection.AccessToken); _logger.Debug("Twitch connection: " + _user.TwitchConnection.Name + " / " + _user.TwitchConnection.AccessToken);
} }
var filters = message.WordFilters.Where(f => f.Search != null && f.Replace != null).ToList(); var filters = message.WordFilters.Where(f => f.Search != null && f.Replace != null).ToList();
@@ -101,8 +101,9 @@ namespace TwitchChatTTS.Hermes.Socket.Handlers
} }
_logger.Information("TTS is now ready."); _logger.Information("TTS is now ready.");
_bus.Send(this, "tts_connected", _user);
client.Ready = true; client.Ready = true;
_bus.Send(this, "tts_connected", _user);
} }
} }
} }

View File

@@ -82,35 +82,36 @@ namespace TwitchChatTTS.Hermes.Socket
public override async Task Connect() public override async Task Connect()
{ {
_rwls.EnterWriteLock(); _rwls.EnterReadLock();
try try
{ {
if (Connected) if (Connected)
return; return;
_logger.Debug($"Attempting to connect to {URL}");
await ConnectAsync(URL);
} }
finally finally
{ {
_rwls.ExitWriteLock(); _rwls.ExitReadLock();
} }
_logger.Debug($"Attempting to connect to {URL}");
await ConnectAsync(URL);
} }
private async Task Disconnect() private async Task Disconnect()
{ {
_rwls.EnterWriteLock(); _rwls.EnterReadLock();
try try
{ {
if (!Connected) if (!Connected)
return; return;
await DisconnectAsync(new SocketDisconnectionEventArgs("Normal disconnection", "Disconnection was executed"));
} }
finally finally
{ {
_rwls.ExitWriteLock(); _rwls.ExitReadLock();
} }
await DisconnectAsync(new SocketDisconnectionEventArgs("Normal disconnection", "Disconnection was executed"));
} }
public async Task CreateTTSVoice(string voiceName) public async Task CreateTTSVoice(string voiceName)
@@ -437,13 +438,13 @@ namespace TwitchChatTTS.Hermes.Socket
_logger.Warning("Tom to Speech websocket client is not connected. Not sending a message."); _logger.Warning("Tom to Speech websocket client is not connected. Not sending a message.");
return; return;
} }
await base.Send(opcode, message);
} }
finally finally
{ {
_rwls.ExitReadLock(); _rwls.ExitReadLock();
} }
await base.Send(opcode, message);
} }
} }
} }

View File

@@ -1,4 +1,3 @@
using System.Text.Json;
using Serilog; using Serilog;
using TwitchChatTTS.Chat.Groups; using TwitchChatTTS.Chat.Groups;
@@ -8,13 +7,11 @@ namespace TwitchChatTTS.Hermes.Socket.Requests
{ {
public string Name => "delete_group"; public string Name => "delete_group";
private readonly IChatterGroupManager _groups; private readonly IChatterGroupManager _groups;
private readonly JsonSerializerOptions _options;
private readonly ILogger _logger; private readonly ILogger _logger;
public DeleteGroupAck(IChatterGroupManager groups, JsonSerializerOptions options, ILogger logger) public DeleteGroupAck(IChatterGroupManager groups, ILogger logger)
{ {
_groups = groups; _groups = groups;
_options = options;
_logger = logger; _logger = logger;
} }

View File

@@ -35,7 +35,13 @@ public class SevenApiClient
{ {
_logger.Debug($"Fetching 7tv information using Twitch Id [twitch id: {twitchId}]"); _logger.Debug($"Fetching 7tv information using Twitch Id [twitch id: {twitchId}]");
var details = await _web.GetJson<UserDetails>($"{API_URL}/users/twitch/" + twitchId); var details = await _web.GetJson<UserDetails>($"{API_URL}/users/twitch/" + twitchId);
return details?.EmoteSet; _logger.Information($"Fetched 7tv emotes [count: {details?.EmoteSet.EmoteCount ?? -1}]");
if (details?.EmoteSet == null)
{
_logger.Warning("Could not find 7tv emotes linked to your Twitch account.");
return null;
}
return details.EmoteSet;
} }
catch (JsonException e) catch (JsonException e)
{ {

10
TTS.cs
View File

@@ -22,8 +22,8 @@ namespace TwitchChatTTS
public class TTS : IHostedService public class TTS : IHostedService
{ {
public const int MAJOR_VERSION = 4; public const int MAJOR_VERSION = 4;
public const int MINOR_VERSION = 8; public const int MINOR_VERSION = 9;
public const int PATCH_VERSION = 2; public const int PATCH_VERSION = 3;
private readonly User _user; private readonly User _user;
private readonly HermesApiClient _hermesApiClient; private readonly HermesApiClient _hermesApiClient;
@@ -84,7 +84,7 @@ namespace TwitchChatTTS
if (string.IsNullOrWhiteSpace(_configuration.Hermes?.Token)) if (string.IsNullOrWhiteSpace(_configuration.Hermes?.Token))
{ {
_logger.Error("Tom to Speech API token not set in the yml file."); _logger.Error("Tom to Speech API token not set in the configuration file.");
return; return;
} }
@@ -96,7 +96,7 @@ namespace TwitchChatTTS
_logger.Error("Failed to fetch latest TTS version. Something went wrong."); _logger.Error("Failed to fetch latest TTS version. Something went wrong.");
return; return;
} }
if (hermesVersion.MajorVersion > TTS.MAJOR_VERSION || hermesVersion.MajorVersion == TTS.MAJOR_VERSION && (hermesVersion.MinorVersion > TTS.MINOR_VERSION || hermesVersion.MinorVersion == TTS.MINOR_VERSION && (hermesVersion.PatchVersion == null || hermesVersion.PatchVersion > TTS.PATCH_VERSION))) if (hermesVersion.MajorVersion > MAJOR_VERSION || hermesVersion.MajorVersion == MAJOR_VERSION && (hermesVersion.MinorVersion > MINOR_VERSION || hermesVersion.MinorVersion == MINOR_VERSION && (hermesVersion.PatchVersion == null || hermesVersion.PatchVersion > PATCH_VERSION)))
{ {
_logger.Information($"A new update for TTS is avaiable! Version {hermesVersion.MajorVersion}.{hermesVersion.MinorVersion}.{hermesVersion.PatchVersion} is available at {hermesVersion.Download}"); _logger.Information($"A new update for TTS is avaiable! Version {hermesVersion.MajorVersion}.{hermesVersion.MinorVersion}.{hermesVersion.PatchVersion} is available at {hermesVersion.Download}");
var changes = hermesVersion.Changelog.Split("\n"); var changes = hermesVersion.Changelog.Split("\n");
@@ -111,9 +111,9 @@ namespace TwitchChatTTS
} }
var disposables = new List<IDisposable>(); var disposables = new List<IDisposable>();
var connected = _bus.GetTopic("tts_connected");
// 7tv // 7tv
var connected = _bus.GetTopic("tts_connected");
disposables.Add(connected.FirstAsync().Subscribe(async (data) => disposables.Add(connected.FirstAsync().Subscribe(async (data) =>
{ {
if (data.Value is not User user) if (data.Value is not User user)

View File

@@ -11,19 +11,23 @@ namespace TwitchChatTTS
{ {
public class TTSListening : IHostedService public class TTSListening : IHostedService
{ {
private const string TTS_API_URL = "https://api.streamelements.com/kappa/v2/speech";
private readonly AudioPlaybackEngine _playback; private readonly AudioPlaybackEngine _playback;
private readonly TTSPlayer _player; private readonly TTSPlayer _player;
private readonly TTSConsumer _consumer; private readonly TTSConsumer _consumer;
private readonly IDisposable _subscription; private readonly IDisposable _subscription;
private readonly User _user;
private readonly ILogger _logger; private readonly ILogger _logger;
public TTSListening(AudioPlaybackEngine playback, TTSPlayer player, TTSPublisher publisher, TTSConsumer consumer, ILogger logger) public TTSListening(AudioPlaybackEngine playback, TTSPlayer player, TTSPublisher publisher, TTSConsumer consumer, User user, ILogger logger)
{ {
_playback = playback; _playback = playback;
_player = player; _player = player;
_consumer = consumer; _consumer = consumer;
_subscription = publisher.Subscribe(consumer); _subscription = publisher.Subscribe(consumer);
_user = user;
_logger = logger; _logger = logger;
} }
@@ -115,7 +119,7 @@ namespace TwitchChatTTS
try try
{ {
string url = $"https://api.streamelements.com/kappa/v2/speech?voice={message.Voice}&text={HttpUtility.UrlEncode(message.Message.Trim())}"; string url = $"{TTS_API_URL}?key={_user.StreamElementsOverlayKey}&voice={message.Voice}&text={HttpUtility.UrlEncode(message.Message.Trim())}";
var nws = new NetworkWavSound(url); var nws = new NetworkWavSound(url);
var provider = new CachedWavProvider(nws); var provider = new CachedWavProvider(nws);
var data = _playback.ConvertSound(provider); var data = _playback.ConvertSound(provider);

View File

@@ -109,9 +109,6 @@ namespace TwitchChatTTS.Twitch.Redemptions
} }
private void Add(string twitchRedemptionId, string redemptionId) private void Add(string twitchRedemptionId, string redemptionId)
{
_rwls.EnterWriteLock();
try
{ {
if (!_redeems.TryGetValue(twitchRedemptionId, out var redeems)) if (!_redeems.TryGetValue(twitchRedemptionId, out var redeems))
_redeems.Add(twitchRedemptionId, redeems = new List<string>()); _redeems.Add(twitchRedemptionId, redeems = new List<string>());
@@ -136,11 +133,6 @@ namespace TwitchChatTTS.Twitch.Redemptions
} }
if (!added) if (!added)
redeems.Add(redemptionId); redeems.Add(redemptionId);
}
finally
{
_rwls.ExitWriteLock();
}
_logger.Debug($"Added redemption action [redemption id: {redemptionId}][twitch redemption id: {twitchRedemptionId}]"); _logger.Debug($"Added redemption action [redemption id: {redemptionId}][twitch redemption id: {twitchRedemptionId}]");
} }

View File

@@ -120,8 +120,7 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
private int GetTotalBits(TwitchChatFragment[] fragments) private int GetTotalBits(TwitchChatFragment[] fragments)
{ {
return fragments.Where(f => f.Type == "cheermote" && f.Cheermote != null) return fragments.Where(f => f.Type == "cheermote" && f.Cheermote != null)
.Select(f => f.Cheermote!.Bits) .Sum(f => f.Cheermote!.Bits);
.Sum();
} }
private string GetPermissionPath(string? customRewardId, int bits) private string GetPermissionPath(string? customRewardId, int bits)

View File

@@ -142,14 +142,14 @@ namespace TwitchChatTTS.Twitch.Socket
_logger.Warning($"Twitch client has been identified, but isn't main or backup [client: {client.UID}][main: {_identified.UID}][backup: {_backup?.UID}]"); _logger.Warning($"Twitch client has been identified, but isn't main or backup [client: {client.UID}][main: {_identified.UID}][backup: {_backup?.UID}]");
clientDisconnect = true; clientDisconnect = true;
} }
if (clientDisconnect)
client.DisconnectAsync(new SocketDisconnectionEventArgs("Closed", "No need for a tertiary client.")).Wait();
} }
finally finally
{ {
_mutex.ReleaseMutex(); _mutex.ReleaseMutex();
} }
if (clientDisconnect)
await client.DisconnectAsync(new SocketDisconnectionEventArgs("Closed", "No need for a tertiary client."));
} }
} }
} }

View File

@@ -183,7 +183,7 @@ namespace TwitchChatTTS.Twitch.Socket
while (current < total) while (current < total)
{ {
var size = Encoding.UTF8.GetBytes(content.Substring(current), array); var size = Encoding.UTF8.GetBytes(content.Substring(current), array);
await _socket!.SendAsync(array, WebSocketMessageType.Text, current + size >= total, _cts!.Token); await _socket!.SendAsync(array, WebSocketMessageType.Text, current + size >= total, CancellationToken.None);
current += size; current += size;
} }
_logger.Debug("Twitch TX #" + type + ": " + content); _logger.Debug("Twitch TX #" + type + ": " + content);

View File

@@ -15,6 +15,7 @@ namespace TwitchChatTTS
public required string TwitchUsername { get; set; } public required string TwitchUsername { get; set; }
public required string SevenEmoteSetId { get; set; } public required string SevenEmoteSetId { get; set; }
public long? OwnerId { get; set; } public long? OwnerId { get; set; }
public required string StreamElementsOverlayKey { get; set; }
public Connection? TwitchConnection { get; set; } public Connection? TwitchConnection { get; set; }
public Connection? NightbotConnection { get; set; } public Connection? NightbotConnection { get; set; }

View File

@@ -178,7 +178,7 @@ namespace TwitchChatTTS.Veadotube
while (current < total) while (current < total)
{ {
var size = Encoding.UTF8.GetBytes(content.Substring(current), array); var size = Encoding.UTF8.GetBytes(content.Substring(current), array);
await _socket.SendAsync(array, WebSocketMessageType.Text, current + size >= total, _cts!.Token); await _socket.SendAsync(array, WebSocketMessageType.Text, current + size >= total, CancellationToken.None);
current += size; current += size;
} }
_logger.Debug($"Veado TX [message type: {typeof(T).Name}]: " + content); _logger.Debug($"Veado TX [message type: {typeof(T).Name}]: " + content);