Added stores for connections. Added requests for groups, group chatters, group permissions & connections. Using TTS Voice State store.

This commit is contained in:
Tom
2025-01-17 04:32:31 +00:00
parent 422cd91db2
commit 6d955f245a
29 changed files with 759 additions and 67 deletions

View File

@ -7,11 +7,13 @@ namespace HermesSocketServer.Models
public required string Id { get; set; }
public required User User { get; set; }
public required ChatterStore Chatters { get; set; }
public required ConnectionStore Connections { get; set; }
public required GroupStore Groups { get; set; }
public required GroupPermissionStore GroupPermissions { get; set; }
public required PolicyStore Policies { get; set; }
public required TTSFilterStore Filters { get; set; }
public required ActionStore Actions { get; set; }
public required RedemptionStore Redemptions { get; set; }
public required VoiceStateStore VoiceStates { get; set; }
}
}

View File

@ -0,0 +1,50 @@
using HermesSocketLibrary.Socket.Data;
using HermesSocketServer.Models;
using ILogger = Serilog.ILogger;
namespace HermesSocketServer.Requests
{
public class CreateConnection : IRequest
{
public string Name => "create_connection";
public string[] RequiredKeys => ["name", "type", "clientId", "accessToken", "grantType", "scope", "expiration"];
private ILogger _logger;
public CreateConnection(ILogger logger)
{
_logger = logger;
}
public Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
string name = data["name"].ToString()!;
string type = data["type"].ToString()!;
string clientId = data["clientId"].ToString()!;
string accessToken = data["accessToken"].ToString()!;
string grantType = data["grantType"].ToString()!;
string scope = data["scope"].ToString()!;
if (!DateTime.TryParse(data["expiration"].ToString()!, out var expiresAt))
return Task.FromResult(RequestResult.Failed("Expiration needs to be a date time string."));
var connection = new Connection()
{
UserId = channel.Id,
Name = name,
Type = type,
ClientId = clientId,
AccessToken = accessToken,
GrantType = grantType,
Scope = scope,
ExpiresAt = expiresAt,
};
bool result = channel.Connections.Set(name, connection);
if (result)
{
_logger.Information($"Added connection to channel [name: {name}][type: {type}][scope: {scope}][expiration: {expiresAt}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(connection));
}
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));
}
}
}

51
Requests/CreateGroup.cs Normal file
View File

@ -0,0 +1,51 @@
using HermesSocketLibrary.db;
using HermesSocketLibrary.Requests.Messages;
using HermesSocketServer.Models;
using HermesSocketServer.Store;
using HermesSocketServer.Store.Internal;
using ILogger = Serilog.ILogger;
namespace HermesSocketServer.Requests
{
public class CreateGroup : IRequest
{
public string Name => "create_group";
public string[] RequiredKeys => ["name", "priority"];
private readonly DatabaseTable _table;
private readonly Database _database;
private ILogger _logger;
public CreateGroup([FromKeyedServices("ChatterGroup")] DatabaseTable table, Database database, ILogger logger)
{
_table = table;
_database = database;
_logger = logger;
}
public Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
var id = Guid.NewGuid();
string name = data["name"].ToString()!;
if (!int.TryParse(data["priority"].ToString()!, out var priority))
return Task.FromResult(RequestResult.Failed("Priority needs to be an integer."));
var group = new Group()
{
Id = id.ToString(),
UserId = channel.Id,
Name = name,
Priority = priority,
};
bool result = channel.Groups.Set(id.ToString(), group);
if (result)
{
var store = new ChatterGroupStore(channel.Id, group.Id, _table, _database, _logger);
channel.Groups.Chatters.Add(group.Id, store);
_logger.Information($"Added group to channel [group id: {id}][name: {name}][priority: {priority}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(group));
}
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));
}
}
}

View File

@ -0,0 +1,46 @@
using HermesSocketLibrary.Requests.Messages;
using HermesSocketServer.Models;
using ILogger = Serilog.ILogger;
namespace HermesSocketServer.Requests
{
public class CreateGroupChatter : IRequest
{
public string Name => "create_group_chatter";
public string[] RequiredKeys => ["group", "chatter", "label"];
private ILogger _logger;
public CreateGroupChatter(ILogger logger)
{
_logger = logger;
}
public Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
var id = Guid.NewGuid();
string groupId = data["group"].ToString()!;
if (!int.TryParse(data["chatter"].ToString()!, out var chatterId))
return Task.FromResult(RequestResult.Failed("Priority needs to be an integer."));
string chatterLabel = data["label"].ToString()!;
if (!channel.Groups.Chatters.TryGetValue(groupId, out var chatters))
return Task.FromResult(RequestResult.Failed($"The group does not exist."));
var groupChatter = new GroupChatter()
{
UserId = channel.Id,
GroupId = groupId,
ChatterId = chatterId,
ChatterLabel = chatterLabel,
};
bool result = chatters.Set(chatterId.ToString(), groupChatter);
if (result)
{
_logger.Information($"Added group chatter to channel [group id: {id}][group id: {groupId}][chatter id: {chatterId}][chatter label: {chatterLabel}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(groupChatter));
}
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));
}
}
}

View File

@ -0,0 +1,43 @@
using HermesSocketLibrary.Requests.Messages;
using HermesSocketServer.Models;
using ILogger = Serilog.ILogger;
namespace HermesSocketServer.Requests
{
public class CreateGroupPermission : IRequest
{
public string Name => "create_group_permission";
public string[] RequiredKeys => ["group", "path", "allow"];
private ILogger _logger;
public CreateGroupPermission(ILogger logger)
{
_logger = logger;
}
public Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
var id = Guid.NewGuid();
string groupId = data["group"].ToString()!;
string path = data["path"].ToString()!;
bool? allow = bool.TryParse(data["allow"].ToString()!, out bool a) ? a : null;
var permission = new GroupPermission()
{
Id = id.ToString(),
UserId = channel.Id,
GroupId = groupId,
Path = path,
Allow = allow,
};
bool result = channel.GroupPermissions.Set(id.ToString(), permission);
if (result)
{
_logger.Information($"Added group permission to channel [permission id: {id}][group id: {groupId}][path: {path}][allow: {allow}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(permission));
}
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));
}
}
}

View File

@ -38,7 +38,7 @@ namespace HermesSocketServer.Requests
bool result = channel.Redemptions.Set(id.ToString(), redemption);
if (result)
{
_logger.Information($"Added redemption to channel [id: {id}][redemption id: {redemptionId}][action: {actionName}][order: {order}][channel: {channel.Id}]");
_logger.Information($"Added redemption to channel [redemption id: {id}][twitch redemption id: {redemptionId}][action: {actionName}][order: {order}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(redemption));
}
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));

View File

@ -0,0 +1,32 @@
using HermesSocketServer.Models;
using ILogger = Serilog.ILogger;
namespace HermesSocketServer.Requests
{
public class DeleteConnection : IRequest
{
public string Name => "delete_connection";
public string[] RequiredKeys => ["id"];
private ILogger _logger;
public DeleteConnection(ILogger logger)
{
_logger = logger;
}
public Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
var connectionId = data["id"].ToString()!;
var result = channel.Connections.Remove(connectionId);
if (result)
{
_logger.Information($"Deleted a connection by id [connection id: {connectionId}]");
return Task.FromResult(RequestResult.Successful(null));
}
_logger.Warning($"Connection Id does not exist [connection id: {connectionId}]");
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));
}
}
}

67
Requests/DeleteGroup.cs Normal file
View File

@ -0,0 +1,67 @@
using HermesSocketServer.Models;
using ILogger = Serilog.ILogger;
namespace HermesSocketServer.Requests
{
public class DeleteGroup : IRequest
{
public string Name => "delete_group";
public string[] RequiredKeys => ["id"];
private ILogger _logger;
public DeleteGroup(ILogger logger)
{
_logger = logger;
}
public async Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
var groupId = data["id"].ToString()!;
var result = channel.Groups.Remove(groupId);
if (result)
{
var permissions = channel.GroupPermissions.Get().Values
.Where(p => p.GroupId == groupId);
Task? chattersSave = null;
if (channel.Groups.Chatters.TryGetValue(groupId, out var chatters))
{
var filteredChatters = chatters.Get().Values.Where(c => c.GroupId == groupId).ToArray();
if (filteredChatters.Any())
{
foreach (var chatter in filteredChatters)
{
var res = chatters.Remove(chatter.ChatterId.ToString());
if (!res)
_logger.Warning($"Failed to delete group chatter by id [group chatter id: {chatter.ChatterId}]");
}
chattersSave = chatters.Save();
}
}
foreach (var permission in permissions)
{
var res = channel.GroupPermissions.Remove(permission.Id);
if (!res)
_logger.Warning($"Failed to delete group permission by id [group chatter id: {permission.Id}]");
}
if (chattersSave != null)
await Task.WhenAll(chattersSave, channel.GroupPermissions.Save());
else
await channel.GroupPermissions.Save();
if (!channel.Groups.Chatters.Remove(groupId))
_logger.Warning($"Failed to delete group chatters from inner store [group id: {groupId}]");
_logger.Information($"Deleted a group by id [group id: {groupId}]");
return RequestResult.Successful(null);
}
_logger.Warning($"Group Id does not exist [group id: {groupId}]");
return RequestResult.Failed("Something went wrong when updating the cache.");
}
}
}

View File

@ -0,0 +1,36 @@
using HermesSocketServer.Models;
using ILogger = Serilog.ILogger;
namespace HermesSocketServer.Requests
{
public class DeleteGroupChatter : IRequest
{
public string Name => "delete_group_chatter";
public string[] RequiredKeys => ["id", "group"];
private ILogger _logger;
public DeleteGroupChatter(ILogger logger)
{
_logger = logger;
}
public Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
var chatterId = data["id"].ToString()!;
var groupId = data["group"].ToString()!;
if (!channel.Groups.Chatters.TryGetValue(groupId, out var chatters))
return Task.FromResult(RequestResult.Failed($"The group does not exist."));
var result = chatters.Remove(chatterId);
if (result)
{
_logger.Information($"Deleted a group chatter by id [group id: {chatterId}]");
return Task.FromResult(RequestResult.Successful(null));
}
_logger.Warning($"Group Chatter Id does not exist [group id: {chatterId}]");
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));
}
}
}

View File

@ -0,0 +1,32 @@
using HermesSocketServer.Models;
using ILogger = Serilog.ILogger;
namespace HermesSocketServer.Requests
{
public class DeleteGroupPermission : IRequest
{
public string Name => "delete_group_permission";
public string[] RequiredKeys => ["id"];
private ILogger _logger;
public DeleteGroupPermission(ILogger logger)
{
_logger = logger;
}
public Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
var id = data["id"].ToString()!;
var result = channel.GroupPermissions.Remove(id);
if (result)
{
_logger.Information($"Deleted a group permission by id [group permission id: {id}]");
return Task.FromResult(RequestResult.Successful(null));
}
_logger.Warning($"Group Permission Id does not exist [group permission id: {id}]");
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));
}
}
}

View File

@ -1,5 +1,3 @@
using HermesSocketLibrary.db;
using HermesSocketLibrary.Socket.Data;
using HermesSocketServer.Models;
using ILogger = Serilog.ILogger;
@ -9,35 +7,18 @@ namespace HermesSocketServer.Requests
{
public string Name => "get_connections";
public string[] RequiredKeys => [];
private Database _database;
private ILogger _logger;
public GetConnections(Database database, ILogger logger)
public GetConnections(ILogger logger)
{
_database = database;
_logger = logger;
}
public async Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
public Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
var temp = new Dictionary<string, object>() { { "user", channel.Id } };
var connections = new List<Connection>();
string sql = "select \"name\", \"type\", \"clientId\", \"accessToken\", \"grantType\", \"scope\", \"expiresAt\", \"default\" from \"Connection\" where \"userId\" = @user";
await _database.Execute(sql, temp, sql =>
connections.Add(new Connection()
{
Name = sql.GetString(0),
Type = sql.GetString(1),
ClientId = sql.GetString(2),
AccessToken = sql.GetString(3),
GrantType = sql.GetString(4),
Scope = sql.GetString(5),
ExpiresAt = sql.GetDateTime(6),
Default = sql.GetBoolean(7)
})
);
return RequestResult.Successful(connections, notifyClientsOnAccount: false);
var connections = channel.Connections.Get().Values;
_logger.Information($"Fetched all connections for channel [channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(connections, notifyClientsOnAccount: false));
}
}
}

View File

@ -0,0 +1,24 @@
using HermesSocketServer.Models;
using ILogger = Serilog.ILogger;
namespace HermesSocketServer.Requests
{
public class GetGroupPermissions : IRequest
{
public string Name => "get_group_permissions";
public string[] RequiredKeys => [];
private readonly ILogger _logger;
public GetGroupPermissions(ILogger logger)
{
_logger = logger;
}
public Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
var permissions = channel.GroupPermissions.Get().Values;
_logger.Information($"Fetched all group permissions for channel [channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(permissions, notifyClientsOnAccount: false));
}
}
}

37
Requests/GetGroups.cs Normal file
View File

@ -0,0 +1,37 @@
using HermesSocketLibrary.Requests.Messages;
using HermesSocketServer.Models;
using ILogger = Serilog.ILogger;
namespace HermesSocketServer.Requests
{
public class GetGroups : IRequest
{
public string Name => "get_groups";
public string[] RequiredKeys => [];
private readonly ILogger _logger;
public GetGroups(ILogger logger)
{
_logger = logger;
}
public Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
var groups = channel.Groups.Get().Values;
var chatters = channel.Groups.Chatters;
var all = groups.Select(g => new GroupDetails()
{
Group = g,
Chatters = chatters[g.Id].Get().Values,
});
_logger.Information($"Fetched all groups for channel [channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(all, notifyClientsOnAccount: false));
}
private class GroupDetails
{
public required Group Group;
public required IEnumerable<GroupChatter> Chatters;
}
}
}

View File

@ -0,0 +1,50 @@
using HermesSocketLibrary.Socket.Data;
using HermesSocketServer.Models;
using ILogger = Serilog.ILogger;
namespace HermesSocketServer.Requests
{
public class UpdateConnection : IRequest
{
public string Name => "update_connection";
public string[] RequiredKeys => ["name", "type", "clientId", "accessToken", "grantType", "scope", "expiration"];
private ILogger _logger;
public UpdateConnection(ILogger logger)
{
_logger = logger;
}
public Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
string name = data["name"].ToString()!;
string type = data["type"].ToString()!;
string clientId = data["clientId"].ToString()!;
string accessToken = data["accessToken"].ToString()!;
string grantType = data["grantType"].ToString()!;
string scope = data["scope"].ToString()!;
if (!DateTime.TryParse(data["expiration"].ToString()!, out var expiresAt))
return Task.FromResult(RequestResult.Failed("Expiration needs to be a date time string."));
var connection = new Connection()
{
UserId = channel.Id,
Name = name,
Type = type,
ClientId = clientId,
AccessToken = accessToken,
GrantType = grantType,
Scope = scope,
ExpiresAt = expiresAt,
};
bool result = channel.Connections.Modify(name, connection);
if (result)
{
_logger.Information($"Added connection to channel [name: {name}][type: {type}][scope: {scope}][expiration: {expiresAt}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(connection));
}
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));
}
}
}

42
Requests/UpdateGroup.cs Normal file
View File

@ -0,0 +1,42 @@
using HermesSocketLibrary.Requests.Messages;
using HermesSocketServer.Models;
using ILogger = Serilog.ILogger;
namespace HermesSocketServer.Requests
{
public class UpdateGroup : IRequest
{
public string Name => "update_group";
public string[] RequiredKeys => ["id", "name", "priority"];
private ILogger _logger;
public UpdateGroup(ILogger logger)
{
_logger = logger;
}
public Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
var id = data["id"].ToString()!;
string name = data["name"].ToString()!;
if (!int.TryParse(data["priority"].ToString()!, out var priority))
return Task.FromResult(RequestResult.Failed("Priority needs to be an integer."));
var group = new Group()
{
Id = id,
UserId = channel.Id,
Name = name,
Priority = priority,
};
bool result = channel.Groups.Modify(id, group);
if (result)
{
_logger.Information($"Updated group on channel [group id: {id}][name: {name}][priority: {priority}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(group));
}
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));
}
}
}

View File

@ -0,0 +1,46 @@
using HermesSocketLibrary.Requests.Messages;
using HermesSocketServer.Models;
using ILogger = Serilog.ILogger;
namespace HermesSocketServer.Requests
{
public class UpdateGroupChatter : IRequest
{
public string Name => "update_group_chatter";
public string[] RequiredKeys => ["group", "chatter", "label"];
private ILogger _logger;
public UpdateGroupChatter(ILogger logger)
{
_logger = logger;
}
public Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
var id = Guid.NewGuid();
string groupId = data["group"].ToString()!;
if (!int.TryParse(data["chatter"].ToString()!, out var chatterId))
return Task.FromResult(RequestResult.Failed("Priority needs to be an integer."));
string chatterLabel = data["label"].ToString()!;
var groupChatter = new GroupChatter()
{
UserId = channel.Id,
GroupId = groupId,
ChatterId = chatterId,
ChatterLabel = chatterLabel,
};
if (!channel.Groups.Chatters.TryGetValue(groupId, out var chatters))
return Task.FromResult(RequestResult.Failed($"The group does not exist."));
bool result = chatters.Modify(chatterId.ToString(), groupChatter);
if (result)
{
_logger.Information($"Updated group chatter on channel [group id: {id}][group id: {groupId}][chatter id: {chatterId}][chatter label: {chatterLabel}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(groupChatter));
}
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));
}
}
}

View File

@ -0,0 +1,43 @@
using HermesSocketLibrary.Requests.Messages;
using HermesSocketServer.Models;
using ILogger = Serilog.ILogger;
namespace HermesSocketServer.Requests
{
public class UpdateGroupPermission : IRequest
{
public string Name => "update_group_permission";
public string[] RequiredKeys => ["id", "group", "path", "allow"];
private ILogger _logger;
public UpdateGroupPermission(ILogger logger)
{
_logger = logger;
}
public Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
var id = data["id"].ToString()!;
string groupId = data["group"].ToString()!;
string path = data["path"].ToString()!;
bool? allow = bool.TryParse(data["allow"].ToString()!, out bool a) ? a : null;
var permission = new GroupPermission()
{
Id = id.ToString(),
UserId = channel.Id,
GroupId = groupId,
Path = path,
Allow = allow,
};
bool result = channel.GroupPermissions.Modify(id.ToString(), permission);
if (result)
{
_logger.Information($"Updated group permission on channel [permission id: {id}][group id: {groupId}][path: {path}][allow: {allow}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(permission));
}
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));
}
}
}

View File

@ -23,7 +23,7 @@ namespace HermesSocketServer.Requests
int count = int.Parse(data["count"].ToString()!);
int span = int.Parse(data["span"].ToString()!);
bool result = channel.Policies.Set(id.ToString(), new Policy()
var policy = new Policy()
{
Id = id,
UserId = channel.Id,
@ -31,12 +31,12 @@ namespace HermesSocketServer.Requests
Path = path,
Usage = count,
Span = span,
});
};
bool result = channel.Policies.Modify(id.ToString(), policy);
if (result)
{
var policy = channel.Policies.Get(id.ToString());
_logger.Information($"Updated policy to channel [policy id: {id}][group id: {groupId}][path: {path}][count: {count}][span: {span}][channel: {channel.Id}]");
_logger.Information($"Updated policy on channel [policy id: {id}][group id: {groupId}][path: {path}][count: {count}][span: {span}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(policy));
}
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));

View File

@ -44,7 +44,7 @@ namespace HermesSocketServer.Requests
bool result = channel.Actions.Modify(name, action);
if (result)
{
_logger.Information($"Added redeemable action to channel [name: {name}][type: {type}][channel: {channel.Id}]");
_logger.Information($"Updated redeemable action on channel [name: {name}][type: {type}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(action));
}
if (channel.Actions.Get(name) == null)

View File

@ -36,16 +36,11 @@ namespace HermesSocketServer.Requests
};
bool result = channel.Redemptions.Modify(id, redemption);
var r = channel.Redemptions.Get(id);
if (result)
{
_logger.Information($"Updated redemption to channel [id: {id}][redemption id: {redemptionId}][action: {actionName}][order: {order}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(r));
_logger.Information($"Updated redemption on channel [id: {id}][redemption id: {redemptionId}][action: {actionName}][order: {order}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(redemption));
}
if (r == null || r.UserId != channel.Id)
return Task.FromResult(RequestResult.Failed("Redemption does not exist."));
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));
}
}

View File

@ -36,10 +36,9 @@ namespace HermesSocketServer.Requests
};
bool result = channel.Filters.Modify(id, filter);
if (result)
{
_logger.Information($"Updated filter to channel [filter id: {id}][search: {search}][replace: {replace}][channel: {channel.Id}]");
_logger.Information($"Updated filter on channel [filter id: {id}][search: {search}][replace: {replace}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(filter));
}
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));

View File

@ -23,25 +23,27 @@ namespace HermesSocketServer.Requests
public async Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
if (long.TryParse(data["chatter"].ToString(), out long chatterId))
data["chatter"] = chatterId;
data["voice"] = data["voice"].ToString()!;
data["user"] = channel.Id;
if (!long.TryParse(data["chatter"].ToString(), out long chatterId))
return RequestResult.Failed("Chatter should be an integer, representing the Twitch User Id of the chatter.");
var voiceId = data["voice"].ToString()!;
var check = await _database.ExecuteScalar("SELECT state FROM \"TtsVoiceState\" WHERE \"userId\" = @user AND \"ttsVoiceId\" = @voice", data) ?? false;
if ((check is not bool state || !state) && chatterId != _configuration.Tts.OwnerId)
return RequestResult.Failed("Voice is either non-existent or disabled on this channel.");
var result = channel.Chatters.Set(chatterId.ToString(), new ChatterVoice()
var voice = new ChatterVoice()
{
UserId = channel.Id,
ChatterId = chatterId,
VoiceId = data["voice"].ToString()!
});
VoiceId = voiceId
};
var result = channel.Chatters.Modify(chatterId.ToString(), voice);
if (result)
{
_logger.Information($"Updated chatter's [chatter: {data["chatter"]}] selected tts voice [voice: {data["voice"]}] in channel [channel: {channel.Id}]");
return RequestResult.Successful(null);
_logger.Information($"Updated chatter's selected tts voice on channel [chatter id: {chatterId}][voice id: {voiceId}][channel: {channel.Id}]");
return RequestResult.Successful(voice);
}
return RequestResult.Failed("Soemthing went wrong when updating the cache.");
}

View File

@ -23,15 +23,17 @@ namespace HermesSocketServer.Requests
string voiceName = data["voice"].ToString()!;
string voiceId = data["voiceid"].ToString()!;
var result = _voices.Set(voiceId, new TTSVoice()
var voice = new TTSVoice()
{
Id = voiceId,
Name = voiceName
});
};
var result = _voices.Modify(voiceId, voice);
if (result)
{
_logger.Information($"Updated voice's [voice id: {voiceId}] name [new name: {voiceName}]");
return Task.FromResult(RequestResult.Successful(null));
_logger.Information($"Updated voice's name on channel [voice id: {voiceId}][name: {voiceName}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(voice));
}
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));
}

View File

@ -1,4 +1,5 @@
using HermesSocketLibrary.db;
using HermesSocketLibrary.Requests.Messages;
using HermesSocketServer.Models;
using ILogger = Serilog.ILogger;
@ -17,18 +18,25 @@ namespace HermesSocketServer.Requests
_logger = logger;
}
public async Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
public Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{
data["voice"] = data["voice"].ToString()!;
data["state"] = data["state"].ToString() == "True";
data["user"] = channel.Id;
var id = data["voice"].ToString()!;
var state = data["state"].ToString() == "True";
string sql = "INSERT INTO \"TtsVoiceState\" (\"userId\", \"ttsVoiceId\", state) VALUES (@user, @voice, @state) ON CONFLICT (\"userId\", \"ttsVoiceId\") DO UPDATE SET state = @state";
var result = await _database.Execute(sql, data);
_logger.Information($"Updated voice's [voice id: {data["voice"]}] state [new state: {data["state"]}][channel: {data["user"]}]");
if (result > 0)
return RequestResult.Successful(null);
return RequestResult.Failed("Something went wrong when updating the database.");
var voiceState = new TTSVoiceState()
{
Id = id,
UserId = channel.Id,
Enabled = state,
};
var result = channel.VoiceStates.Set(id, voiceState);
if (result)
{
_logger.Information($"Updated voice state on channel [voice id: {id}][state: {state}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(voiceState));
}
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the database."));
}
}
}

View File

@ -2,6 +2,7 @@ using System.Collections.Concurrent;
using HermesSocketLibrary.db;
using HermesSocketServer.Models;
using HermesSocketServer.Store;
using HermesSocketServer.Validators;
namespace HermesSocketServer.Services
{
@ -38,29 +39,35 @@ namespace HermesSocketServer.Services
var actionTable = _configuration.Database.Tables["Action"];
var chatterTable = _configuration.Database.Tables["Chatter"];
var connectionTable = _configuration.Database.Tables["Connection"];
//var chatterGroupTable = _configuration.Database.Tables["ChatterGroup"];
var groupTable = _configuration.Database.Tables["Group"];
var groupPermissionTable = _configuration.Database.Tables["GroupPermission"];
var policyTable = _configuration.Database.Tables["Policy"];
var redemptionTable = _configuration.Database.Tables["Redemption"];
var ttsFilterTable = _configuration.Database.Tables["TtsFilter"];
var ttsVoiceStateTable = _configuration.Database.Tables["VoiceState"];
var chatters = new ChatterStore(userId, chatterTable, _database, _logger);
var connections = new ConnectionStore(userId, connectionTable, _database, _logger);
var groups = new GroupStore(userId, groupTable, _database, _configuration, _logger);
var groupPermissions = new GroupPermissionStore(userId, groupPermissionTable, _database, _logger);
var policies = new PolicyStore(userId, policyTable, _database, _logger);
var filters = new TTSFilterStore(userId, ttsFilterTable, _database, _logger);
var actions = new ActionStore(userId, actionTable, _database, _logger);
var redemptions = new RedemptionStore(userId, redemptionTable, _database, _logger);
var voiceStates = new VoiceStateStore(userId, new VoiceIdValidator(), ttsVoiceStateTable, _database, _logger);
Task.WaitAll([
chatters.Load(),
connections.Load(),
groups.Load(),
groupPermissions.Load(),
policies.Load(),
filters.Load(),
actions.Load(),
redemptions.Load(),
voiceStates.Save(),
]);
var channel = new Channel()
@ -68,12 +75,14 @@ namespace HermesSocketServer.Services
Id = userId,
User = user,
Chatters = chatters,
Connections = connections,
Groups = groups,
GroupPermissions = groupPermissions,
Policies = policies,
Filters = filters,
Actions = actions,
Redemptions = redemptions,
VoiceStates = voiceStates,
};
_channels.Add(userId, channel);
@ -93,12 +102,17 @@ namespace HermesSocketServer.Services
if (!_channels.TryGetValue(userId, out var channel))
return;
_logger.Debug($"Saving channel data to database [channel id: {channel.Id}][channel name: {channel.User.Name}]");
await Task.WhenAll([
channel.Chatters.Save(),
channel.Connections.Save(),
channel.Groups.Save(),
channel.GroupPermissions.Save(),
channel.Policies.Save(),
channel.Filters.Save(),
channel.Actions.Save(),
channel.Redemptions.Save(),
channel.VoiceStates.Save(),
]);
}
@ -109,10 +123,14 @@ namespace HermesSocketServer.Services
_logger.Debug($"Saving channel data to database [channel id: {channel.Id}][channel name: {channel.User.Name}]");
await Task.WhenAll([
channel.Chatters.Save(),
channel.Connections.Save(),
channel.Groups.Save(),
channel.GroupPermissions.Save(),
channel.Policies.Save(),
channel.Filters.Save(),
channel.Actions.Save(),
channel.Redemptions.Save(),
channel.VoiceStates.Save(),
]);
}
}

View File

@ -102,6 +102,7 @@ namespace HermesSocketServer.Socket.Handlers
await _database.Execute(sql3, userIdDict, sql =>
ack.Connections.Add(new Connection()
{
UserId = channel.Id,
Name = sql.GetString(0),
Type = sql.GetString(1),
ClientId = sql.GetString(2),

View File

@ -94,15 +94,23 @@ s.AddSingleton<IStore<string, TTSVoice>, VoiceStore>();
s.AddSingleton<IStore<string, User>, UserStore>();
// Request handlers
s.AddSingleton<IRequest, CreateConnection>();
s.AddSingleton<IRequest, CreateGroup>();
s.AddSingleton<IRequest, CreateGroupChatter>();
s.AddSingleton<IRequest, CreateGroupPermission>();
s.AddSingleton<IRequest, CreatePolicy>();
s.AddSingleton<IRequest, CreateRedeemableAction>();
s.AddSingleton<IRequest, CreateRedemption>();
s.AddSingleton<IRequest, CreateTTSFilter>();
s.AddSingleton<IRequest, CreateTTSUser>();
s.AddSingleton<IRequest, CreateTTSVoice>();
s.AddSingleton<IRequest, DeleteConnection>();
s.AddSingleton<IRequest, DeleteGroup>();
s.AddSingleton<IRequest, DeleteGroupChatter>();
s.AddSingleton<IRequest, DeleteGroupPermission>();
s.AddSingleton<IRequest, DeletePolicy>();
s.AddSingleton<IRequest, DeleteRedeemableAction>();
s.AddSingleton<IRequest, DeleteRedemption>();
s.AddSingleton<IRequest, DeletePolicy>();
s.AddSingleton<IRequest, DeleteTTSFilter>();
s.AddSingleton<IRequest, DeleteTTSVoice>();
s.AddSingleton<IRequest, GetChatterIds>();
@ -117,7 +125,11 @@ s.AddSingleton<IRequest, GetPolicies>();
s.AddSingleton<IRequest, GetTTSUsers>();
s.AddSingleton<IRequest, GetTTSVoices>();
s.AddSingleton<IRequest, GetTTSWordFilters>();
s.AddSingleton<IRequest, UpdateConnection>();
s.AddSingleton<IRequest, UpdateDefaultTTSVoice>();
s.AddSingleton<IRequest, UpdateGroup>();
s.AddSingleton<IRequest, UpdateGroupChatter>();
s.AddSingleton<IRequest, UpdateGroupPermission>();
s.AddSingleton<IRequest, UpdatePolicy>();
s.AddSingleton<IRequest, UpdateRedeemableAction>();
s.AddSingleton<IRequest, UpdateRedemption>();
@ -149,9 +161,7 @@ var wsOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(30)
};
// wsOptions.AllowedOrigins.Add("wss://tomtospeech.com");
//wsOptions.AllowedOrigins.Add("ws.tomtospeech.com");
//wsOptions.AllowedOrigins.Add("hermes-ws.goblincaves.com");
app.UseWebSockets(wsOptions);
var options = app.Services.GetRequiredService<JsonSerializerOptions>();

75
Store/ConnectionStore.cs Normal file
View File

@ -0,0 +1,75 @@
using HermesSocketLibrary.db;
using HermesSocketLibrary.Socket.Data;
using HermesSocketServer.Store.Internal;
namespace HermesSocketServer.Store
{
public class ConnectionStore : ComplexAutoSavedStore<string, Connection>
{
private readonly string _userId;
private readonly Database _database;
private readonly Serilog.ILogger _logger;
public ConnectionStore(string userId, DatabaseTable table, Database database, Serilog.ILogger logger)
: base(table, database, logger)
{
_userId = userId;
_database = database;
_logger = logger;
}
public override async Task Load()
{
var data = new Dictionary<string, object>() { { "user", _userId } };
string sql = $"SELECT name, \"type\", \"clientId\", \"accessToken\", \"grantType\", \"scope\", \"expiresAt\", \"default\" FROM \"Connection\" WHERE \"userId\" = @user";
await _database.Execute(sql, data, (reader) =>
{
var name = reader.GetString(0);
_store.Add(name, new Connection()
{
Name = name,
UserId = _userId,
Type = reader.GetString(1),
ClientId = reader.GetString(2),
AccessToken = reader.GetString(3),
GrantType = reader.GetString(4),
Scope = reader.GetString(5),
ExpiresAt = reader.GetDateTime(6),
Default = reader.GetBoolean(7),
});
});
_logger.Information($"Loaded {_store.Count} groups from database.");
}
protected override void OnInitialAdd(string key, Connection value)
{
ArgumentException.ThrowIfNullOrWhiteSpace(key, nameof(key));
ArgumentNullException.ThrowIfNull(value, nameof(value));
ArgumentException.ThrowIfNullOrWhiteSpace(value.UserId, nameof(value.UserId));
ArgumentException.ThrowIfNullOrWhiteSpace(value.Name, nameof(value.Name));
ArgumentException.ThrowIfNullOrWhiteSpace(value.Type, nameof(value.Type));
ArgumentException.ThrowIfNullOrWhiteSpace(value.ClientId, nameof(value.ClientId));
ArgumentException.ThrowIfNullOrWhiteSpace(value.AccessToken, nameof(value.AccessToken));
ArgumentException.ThrowIfNullOrWhiteSpace(value.GrantType, nameof(value.GrantType));
ArgumentException.ThrowIfNullOrWhiteSpace(value.Scope, nameof(value.Scope));
ArgumentNullException.ThrowIfNull(value.ExpiresAt, nameof(value.ExpiresAt));
ArgumentNullException.ThrowIfNull(value.Default, nameof(value.Default));
}
protected override void OnInitialModify(string key, Connection oldValue, Connection newValue)
{
ArgumentNullException.ThrowIfNull(newValue, nameof(newValue));
ArgumentException.ThrowIfNullOrWhiteSpace(newValue.UserId, nameof(newValue.UserId));
ArgumentException.ThrowIfNullOrWhiteSpace(newValue.Name, nameof(newValue.Name));
ArgumentException.ThrowIfNullOrWhiteSpace(newValue.Type, nameof(newValue.Type));
ArgumentException.ThrowIfNullOrWhiteSpace(newValue.ClientId, nameof(newValue.ClientId));
ArgumentException.ThrowIfNullOrWhiteSpace(newValue.AccessToken, nameof(newValue.AccessToken));
ArgumentException.ThrowIfNullOrWhiteSpace(newValue.GrantType, nameof(newValue.GrantType));
ArgumentException.ThrowIfNullOrWhiteSpace(newValue.Scope, nameof(newValue.Scope));
ArgumentNullException.ThrowIfNull(newValue.ExpiresAt, nameof(newValue.ExpiresAt));
ArgumentNullException.ThrowIfNull(newValue.Default, nameof(newValue.Default));
ArgumentOutOfRangeException.ThrowIfNotEqual(oldValue.UserId, newValue.UserId, nameof(oldValue.UserId));
ArgumentOutOfRangeException.ThrowIfNotEqual(oldValue.Name, newValue.Name, nameof(oldValue.Name));
}
}
}

View File

@ -7,7 +7,7 @@ namespace HermesSocketServer.Store
{
public class GroupStore : AutoSavedStore<string, Group>
{
private static readonly string[] AUTO_GENERATED_GROUP_NAMES = ["everyone", "subscribers", "vip", "moderators", "broadcaster"];
public static readonly string[] AUTO_GENERATED_GROUP_NAMES = ["everyone", "subscribers", "vip", "moderators", "broadcaster"];
private IDictionary<string, ChatterGroupStore> _chatters;
private readonly string _userId;