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 string Id { get; set; }
public required User User { get; set; } public required User User { get; set; }
public required ChatterStore Chatters { get; set; } public required ChatterStore Chatters { get; set; }
public required ConnectionStore Connections { get; set; }
public required GroupStore Groups { get; set; } public required GroupStore Groups { get; set; }
public required GroupPermissionStore GroupPermissions { get; set; } public required GroupPermissionStore GroupPermissions { get; set; }
public required PolicyStore Policies { get; set; } public required PolicyStore Policies { get; set; }
public required TTSFilterStore Filters { get; set; } public required TTSFilterStore Filters { get; set; }
public required ActionStore Actions { get; set; } public required ActionStore Actions { get; set; }
public required RedemptionStore Redemptions { 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); bool result = channel.Redemptions.Set(id.ToString(), redemption);
if (result) 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.Successful(redemption));
} }
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache.")); 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 HermesSocketServer.Models;
using ILogger = Serilog.ILogger; using ILogger = Serilog.ILogger;
@ -9,35 +7,18 @@ namespace HermesSocketServer.Requests
{ {
public string Name => "get_connections"; public string Name => "get_connections";
public string[] RequiredKeys => []; public string[] RequiredKeys => [];
private Database _database;
private ILogger _logger; private ILogger _logger;
public GetConnections(Database database, ILogger logger) public GetConnections(ILogger logger)
{ {
_database = database;
_logger = logger; _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 = channel.Connections.Get().Values;
_logger.Information($"Fetched all connections for channel [channel: {channel.Id}]");
var connections = new List<Connection>(); return Task.FromResult(RequestResult.Successful(connections, notifyClientsOnAccount: false));
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);
} }
} }
} }

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

View File

@ -36,16 +36,11 @@ namespace HermesSocketServer.Requests
}; };
bool result = channel.Redemptions.Modify(id, redemption); bool result = channel.Redemptions.Modify(id, redemption);
var r = channel.Redemptions.Get(id);
if (result) if (result)
{ {
_logger.Information($"Updated redemption to channel [id: {id}][redemption id: {redemptionId}][action: {actionName}][order: {order}][channel: {channel.Id}]"); _logger.Information($"Updated redemption on channel [id: {id}][redemption id: {redemptionId}][action: {actionName}][order: {order}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(r)); 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.")); 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); bool result = channel.Filters.Modify(id, filter);
if (result) 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.Successful(filter));
} }
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache.")); 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) public async Task<RequestResult> Grant(Channel channel, IDictionary<string, object> data)
{ {
if (long.TryParse(data["chatter"].ToString(), out long chatterId)) if (!long.TryParse(data["chatter"].ToString(), out long chatterId))
data["chatter"] = chatterId; return RequestResult.Failed("Chatter should be an integer, representing the Twitch User Id of the chatter.");
data["voice"] = data["voice"].ToString()!;
data["user"] = channel.Id; var voiceId = data["voice"].ToString()!;
var check = await _database.ExecuteScalar("SELECT state FROM \"TtsVoiceState\" WHERE \"userId\" = @user AND \"ttsVoiceId\" = @voice", data) ?? false; 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) if ((check is not bool state || !state) && chatterId != _configuration.Tts.OwnerId)
return RequestResult.Failed("Voice is either non-existent or disabled on this channel."); 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, UserId = channel.Id,
ChatterId = chatterId, ChatterId = chatterId,
VoiceId = data["voice"].ToString()! VoiceId = voiceId
}); };
var result = channel.Chatters.Modify(chatterId.ToString(), voice);
if (result) if (result)
{ {
_logger.Information($"Updated chatter's [chatter: {data["chatter"]}] selected tts voice [voice: {data["voice"]}] in channel [channel: {channel.Id}]"); _logger.Information($"Updated chatter's selected tts voice on channel [chatter id: {chatterId}][voice id: {voiceId}][channel: {channel.Id}]");
return RequestResult.Successful(null); return RequestResult.Successful(voice);
} }
return RequestResult.Failed("Soemthing went wrong when updating the cache."); 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 voiceName = data["voice"].ToString()!;
string voiceId = data["voiceid"].ToString()!; string voiceId = data["voiceid"].ToString()!;
var result = _voices.Set(voiceId, new TTSVoice() var voice = new TTSVoice()
{ {
Id = voiceId, Id = voiceId,
Name = voiceName Name = voiceName
}); };
var result = _voices.Modify(voiceId, voice);
if (result) if (result)
{ {
_logger.Information($"Updated voice's [voice id: {voiceId}] name [new name: {voiceName}]"); _logger.Information($"Updated voice's name on channel [voice id: {voiceId}][name: {voiceName}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(null)); return Task.FromResult(RequestResult.Successful(voice));
} }
return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache.")); return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache."));
} }

View File

@ -1,4 +1,5 @@
using HermesSocketLibrary.db; using HermesSocketLibrary.db;
using HermesSocketLibrary.Requests.Messages;
using HermesSocketServer.Models; using HermesSocketServer.Models;
using ILogger = Serilog.ILogger; using ILogger = Serilog.ILogger;
@ -17,18 +18,25 @@ namespace HermesSocketServer.Requests
_logger = logger; _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()!; var id = data["voice"].ToString()!;
data["state"] = data["state"].ToString() == "True"; var state = data["state"].ToString() == "True";
data["user"] = channel.Id;
string sql = "INSERT INTO \"TtsVoiceState\" (\"userId\", \"ttsVoiceId\", state) VALUES (@user, @voice, @state) ON CONFLICT (\"userId\", \"ttsVoiceId\") DO UPDATE SET state = @state"; var voiceState = new TTSVoiceState()
var result = await _database.Execute(sql, data); {
_logger.Information($"Updated voice's [voice id: {data["voice"]}] state [new state: {data["state"]}][channel: {data["user"]}]"); Id = id,
if (result > 0) UserId = channel.Id,
return RequestResult.Successful(null); Enabled = state,
return RequestResult.Failed("Something went wrong when updating the database."); };
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 HermesSocketLibrary.db;
using HermesSocketServer.Models; using HermesSocketServer.Models;
using HermesSocketServer.Store; using HermesSocketServer.Store;
using HermesSocketServer.Validators;
namespace HermesSocketServer.Services namespace HermesSocketServer.Services
{ {
@ -38,29 +39,35 @@ namespace HermesSocketServer.Services
var actionTable = _configuration.Database.Tables["Action"]; var actionTable = _configuration.Database.Tables["Action"];
var chatterTable = _configuration.Database.Tables["Chatter"]; var chatterTable = _configuration.Database.Tables["Chatter"];
var connectionTable = _configuration.Database.Tables["Connection"];
//var chatterGroupTable = _configuration.Database.Tables["ChatterGroup"]; //var chatterGroupTable = _configuration.Database.Tables["ChatterGroup"];
var groupTable = _configuration.Database.Tables["Group"]; var groupTable = _configuration.Database.Tables["Group"];
var groupPermissionTable = _configuration.Database.Tables["GroupPermission"]; var groupPermissionTable = _configuration.Database.Tables["GroupPermission"];
var policyTable = _configuration.Database.Tables["Policy"]; var policyTable = _configuration.Database.Tables["Policy"];
var redemptionTable = _configuration.Database.Tables["Redemption"]; var redemptionTable = _configuration.Database.Tables["Redemption"];
var ttsFilterTable = _configuration.Database.Tables["TtsFilter"]; var ttsFilterTable = _configuration.Database.Tables["TtsFilter"];
var ttsVoiceStateTable = _configuration.Database.Tables["VoiceState"];
var chatters = new ChatterStore(userId, chatterTable, _database, _logger); 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 groups = new GroupStore(userId, groupTable, _database, _configuration, _logger);
var groupPermissions = new GroupPermissionStore(userId, groupPermissionTable, _database, _logger); var groupPermissions = new GroupPermissionStore(userId, groupPermissionTable, _database, _logger);
var policies = new PolicyStore(userId, policyTable, _database, _logger); var policies = new PolicyStore(userId, policyTable, _database, _logger);
var filters = new TTSFilterStore(userId, ttsFilterTable, _database, _logger); var filters = new TTSFilterStore(userId, ttsFilterTable, _database, _logger);
var actions = new ActionStore(userId, actionTable, _database, _logger); var actions = new ActionStore(userId, actionTable, _database, _logger);
var redemptions = new RedemptionStore(userId, redemptionTable, _database, _logger); var redemptions = new RedemptionStore(userId, redemptionTable, _database, _logger);
var voiceStates = new VoiceStateStore(userId, new VoiceIdValidator(), ttsVoiceStateTable, _database, _logger);
Task.WaitAll([ Task.WaitAll([
chatters.Load(), chatters.Load(),
connections.Load(),
groups.Load(), groups.Load(),
groupPermissions.Load(), groupPermissions.Load(),
policies.Load(), policies.Load(),
filters.Load(), filters.Load(),
actions.Load(), actions.Load(),
redemptions.Load(), redemptions.Load(),
voiceStates.Save(),
]); ]);
var channel = new Channel() var channel = new Channel()
@ -68,12 +75,14 @@ namespace HermesSocketServer.Services
Id = userId, Id = userId,
User = user, User = user,
Chatters = chatters, Chatters = chatters,
Connections = connections,
Groups = groups, Groups = groups,
GroupPermissions = groupPermissions, GroupPermissions = groupPermissions,
Policies = policies, Policies = policies,
Filters = filters, Filters = filters,
Actions = actions, Actions = actions,
Redemptions = redemptions, Redemptions = redemptions,
VoiceStates = voiceStates,
}; };
_channels.Add(userId, channel); _channels.Add(userId, channel);
@ -93,12 +102,17 @@ namespace HermesSocketServer.Services
if (!_channels.TryGetValue(userId, out var channel)) if (!_channels.TryGetValue(userId, out var channel))
return; return;
_logger.Debug($"Saving channel data to database [channel id: {channel.Id}][channel name: {channel.User.Name}]");
await Task.WhenAll([ await Task.WhenAll([
channel.Chatters.Save(), channel.Chatters.Save(),
channel.Connections.Save(),
channel.Groups.Save(),
channel.GroupPermissions.Save(),
channel.Policies.Save(), channel.Policies.Save(),
channel.Filters.Save(), channel.Filters.Save(),
channel.Actions.Save(), channel.Actions.Save(),
channel.Redemptions.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}]"); _logger.Debug($"Saving channel data to database [channel id: {channel.Id}][channel name: {channel.User.Name}]");
await Task.WhenAll([ await Task.WhenAll([
channel.Chatters.Save(), channel.Chatters.Save(),
channel.Connections.Save(),
channel.Groups.Save(),
channel.GroupPermissions.Save(),
channel.Policies.Save(), channel.Policies.Save(),
channel.Filters.Save(), channel.Filters.Save(),
channel.Actions.Save(), channel.Actions.Save(),
channel.Redemptions.Save(), channel.Redemptions.Save(),
channel.VoiceStates.Save(),
]); ]);
} }
} }

View File

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

View File

@ -94,15 +94,23 @@ s.AddSingleton<IStore<string, TTSVoice>, VoiceStore>();
s.AddSingleton<IStore<string, User>, UserStore>(); s.AddSingleton<IStore<string, User>, UserStore>();
// Request handlers // 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, CreatePolicy>();
s.AddSingleton<IRequest, CreateRedeemableAction>(); s.AddSingleton<IRequest, CreateRedeemableAction>();
s.AddSingleton<IRequest, CreateRedemption>(); s.AddSingleton<IRequest, CreateRedemption>();
s.AddSingleton<IRequest, CreateTTSFilter>(); s.AddSingleton<IRequest, CreateTTSFilter>();
s.AddSingleton<IRequest, CreateTTSUser>(); s.AddSingleton<IRequest, CreateTTSUser>();
s.AddSingleton<IRequest, CreateTTSVoice>(); 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, DeleteRedeemableAction>();
s.AddSingleton<IRequest, DeleteRedemption>(); s.AddSingleton<IRequest, DeleteRedemption>();
s.AddSingleton<IRequest, DeletePolicy>();
s.AddSingleton<IRequest, DeleteTTSFilter>(); s.AddSingleton<IRequest, DeleteTTSFilter>();
s.AddSingleton<IRequest, DeleteTTSVoice>(); s.AddSingleton<IRequest, DeleteTTSVoice>();
s.AddSingleton<IRequest, GetChatterIds>(); s.AddSingleton<IRequest, GetChatterIds>();
@ -117,7 +125,11 @@ s.AddSingleton<IRequest, GetPolicies>();
s.AddSingleton<IRequest, GetTTSUsers>(); s.AddSingleton<IRequest, GetTTSUsers>();
s.AddSingleton<IRequest, GetTTSVoices>(); s.AddSingleton<IRequest, GetTTSVoices>();
s.AddSingleton<IRequest, GetTTSWordFilters>(); s.AddSingleton<IRequest, GetTTSWordFilters>();
s.AddSingleton<IRequest, UpdateConnection>();
s.AddSingleton<IRequest, UpdateDefaultTTSVoice>(); s.AddSingleton<IRequest, UpdateDefaultTTSVoice>();
s.AddSingleton<IRequest, UpdateGroup>();
s.AddSingleton<IRequest, UpdateGroupChatter>();
s.AddSingleton<IRequest, UpdateGroupPermission>();
s.AddSingleton<IRequest, UpdatePolicy>(); s.AddSingleton<IRequest, UpdatePolicy>();
s.AddSingleton<IRequest, UpdateRedeemableAction>(); s.AddSingleton<IRequest, UpdateRedeemableAction>();
s.AddSingleton<IRequest, UpdateRedemption>(); s.AddSingleton<IRequest, UpdateRedemption>();
@ -149,9 +161,7 @@ var wsOptions = new WebSocketOptions()
{ {
KeepAliveInterval = TimeSpan.FromSeconds(30) 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); app.UseWebSockets(wsOptions);
var options = app.Services.GetRequiredService<JsonSerializerOptions>(); 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> 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 IDictionary<string, ChatterGroupStore> _chatters;
private readonly string _userId; private readonly string _userId;