Compare commits
5 Commits
3429c8f8dc
...
259cbc9935
| Author | SHA1 | Date | |
|---|---|---|---|
| 259cbc9935 | |||
| 00cea32cfa | |||
| 125f71e779 | |||
| a9542b08fb | |||
| 467c3cf0b0 |
@@ -1,3 +1,4 @@
|
||||
using HermesSocketServer.Messages;
|
||||
using HermesSocketServer.Models;
|
||||
using ILogger = Serilog.ILogger;
|
||||
|
||||
@@ -22,7 +23,7 @@ namespace HermesSocketServer.Requests
|
||||
int count = int.Parse(data["count"].ToString()!);
|
||||
int span = int.Parse(data["span"].ToString()!);
|
||||
|
||||
var policy = new PolicyMessage()
|
||||
var policy = new Policy()
|
||||
{
|
||||
Id = id,
|
||||
UserId = channel.Id,
|
||||
|
||||
@@ -20,6 +20,10 @@ namespace HermesSocketServer.Requests
|
||||
var id = Guid.NewGuid();
|
||||
string search = data["search"].ToString()!;
|
||||
string replace = data["replace"].ToString()!;
|
||||
int flag = 0;
|
||||
if (data.TryGetValue("flag", out var flagObject)) {
|
||||
int.TryParse(flagObject.ToString(), out flag);
|
||||
}
|
||||
|
||||
var filter = new TTSWordFilter()
|
||||
{
|
||||
@@ -27,6 +31,7 @@ namespace HermesSocketServer.Requests
|
||||
UserId = channel.Id,
|
||||
Search = search,
|
||||
Replace = replace,
|
||||
Flag = flag,
|
||||
};
|
||||
|
||||
bool result = channel.Filters.Set(id.ToString(), filter);
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace HermesSocketServer.Requests
|
||||
Path = r.GetString(2),
|
||||
Allow = r.GetBoolean(3)
|
||||
}));
|
||||
_logger.Information($"Fetched all redemptions for channel [channel: {channel.Id}]");
|
||||
_logger.Information($"Fetched all permissions for channel [channel: {channel.Id}]");
|
||||
|
||||
var info = new GroupInfo()
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using HermesSocketServer.Messages;
|
||||
using HermesSocketServer.Models;
|
||||
using ILogger = Serilog.ILogger;
|
||||
|
||||
@@ -22,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 PolicyMessage()
|
||||
bool result = channel.Policies.Set(id.ToString(), new Policy()
|
||||
{
|
||||
Id = id,
|
||||
UserId = channel.Id,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using HermesSocketLibrary.Requests.Messages;
|
||||
using HermesSocketServer.Models;
|
||||
using ILogger = Serilog.ILogger;
|
||||
|
||||
@@ -7,7 +6,7 @@ namespace HermesSocketServer.Requests
|
||||
public class UpdateRedemption : IRequest
|
||||
{
|
||||
public string Name => "update_redemption";
|
||||
public string[] RequiredKeys => ["id", "redemption", "action", "order"];
|
||||
public string[] RequiredKeys => ["id", "redemption", "action", "order", "state"];
|
||||
private ILogger _logger;
|
||||
|
||||
public UpdateRedemption(ILogger logger)
|
||||
@@ -26,17 +25,7 @@ namespace HermesSocketServer.Requests
|
||||
return Task.FromResult(RequestResult.Failed("Order must be an integer."));
|
||||
bool state = data["state"].ToString()?.ToLower() == "true";
|
||||
|
||||
var redemption = new Redemption()
|
||||
{
|
||||
Id = id,
|
||||
UserId = channel.Id,
|
||||
RedemptionId = redemptionId,
|
||||
ActionName = actionName,
|
||||
Order = order,
|
||||
State = true,
|
||||
};
|
||||
|
||||
bool result = channel.Redemptions.Modify(id.ToString(), r =>
|
||||
bool result = channel.Redemptions.Modify(id, r =>
|
||||
{
|
||||
if (r.UserId != channel.Id)
|
||||
return;
|
||||
@@ -50,7 +39,7 @@ namespace HermesSocketServer.Requests
|
||||
var r = channel.Redemptions.Get(id);
|
||||
if (result)
|
||||
{
|
||||
_logger.Information($"Added redemption to channel [id: {id}][redemption id: {redemptionId}][action: {actionName}][order: {order}][channel: {channel.Id}]");
|
||||
_logger.Information($"Updated redemption to channel [id: {id}][redemption id: {redemptionId}][action: {actionName}][order: {order}][channel: {channel.Id}]");
|
||||
return Task.FromResult(RequestResult.Successful(r));
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@ namespace HermesSocketServer.Requests
|
||||
var id = data["id"].ToString()!;
|
||||
string search = data["search"].ToString()!;
|
||||
string replace = data["replace"].ToString()!;
|
||||
int flag = -1;
|
||||
if (data.TryGetValue("flag", out var flagObject)) {
|
||||
int.TryParse(flagObject.ToString(), out flag);
|
||||
}
|
||||
|
||||
var filter = new TTSWordFilter()
|
||||
{
|
||||
@@ -28,9 +32,15 @@ namespace HermesSocketServer.Requests
|
||||
UserId = channel.Id,
|
||||
Search = search,
|
||||
Replace = replace,
|
||||
Flag = flag,
|
||||
};
|
||||
|
||||
bool result = channel.Filters.Set(id, filter);
|
||||
bool result = channel.Filters.Modify(id, f => {
|
||||
f.Search = search;
|
||||
f.Replace = replace;
|
||||
if (flag >= 0)
|
||||
f.Flag = flag;
|
||||
});
|
||||
|
||||
if (result)
|
||||
{
|
||||
|
||||
@@ -26,10 +26,7 @@ namespace HermesSocketServer.Socket.Handlers
|
||||
if (message is not EmoteDetailsMessage data || sender.Id == null)
|
||||
return;
|
||||
|
||||
if (data.Emotes == null)
|
||||
return;
|
||||
|
||||
if (!data.Emotes.Any())
|
||||
if (data.Emotes == null || !data.Emotes.Any())
|
||||
return;
|
||||
|
||||
lock (_lock)
|
||||
@@ -38,7 +35,7 @@ namespace HermesSocketServer.Socket.Handlers
|
||||
{
|
||||
if (_emotes.Contains(entry.Key))
|
||||
{
|
||||
_emotes.Remove(entry.Key);
|
||||
data.Emotes.Remove(entry.Key);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -46,6 +43,9 @@ namespace HermesSocketServer.Socket.Handlers
|
||||
}
|
||||
}
|
||||
|
||||
if (!data.Emotes.Any())
|
||||
return;
|
||||
|
||||
int rows = 0;
|
||||
string sql = "INSERT INTO \"Emote\" (id, name) VALUES (@idd, @name)";
|
||||
using (var connection = await _database.DataSource.OpenConnectionAsync())
|
||||
|
||||
@@ -73,14 +73,27 @@ namespace HermesSocketServer.Socket.Handlers
|
||||
}
|
||||
if (string.IsNullOrEmpty(channel.User.DefaultVoice))
|
||||
_logger.Warning($"No default voice was set for an user [user id: {userId}][api key: {data.ApiKey}]");
|
||||
|
||||
sql = "select \"providerAccountId\" from \"Account\" where \"userId\" = @user and provider = @provider";
|
||||
var result2 = await _database.ExecuteScalar(sql, new Dictionary<string, object>() { { "user", userId }, { "provider", "twitch" } });
|
||||
var providerId = result2?.ToString();
|
||||
if (providerId == null) {
|
||||
_logger.Warning($"Could not find the Provider Account Id [user id: {userId}][provider: twitch]");
|
||||
return;
|
||||
}
|
||||
|
||||
var ack = new LoginAckMessage()
|
||||
{
|
||||
UserId = userId,
|
||||
ProviderAccountId = providerId,
|
||||
SessionId = sender.UID,
|
||||
UserName = channel.User.Name,
|
||||
OwnerId = _configuration.Tts.OwnerId,
|
||||
Admin = sender.Admin,
|
||||
WebLogin = data.WebLogin,
|
||||
WordFilters = channel.Filters.Get().Values,
|
||||
DefaultTTSVoice = channel.User.DefaultVoice ?? _configuration.Tts.DefaultTtsVoice,
|
||||
TTSVoicesAvailable = _voices.Get().ToDictionary(v => v.Key, v => v.Value.Name),
|
||||
};
|
||||
|
||||
var userIdDict = new Dictionary<string, object>() { { "user", userId } };
|
||||
@@ -100,25 +113,12 @@ namespace HermesSocketServer.Socket.Handlers
|
||||
})
|
||||
);
|
||||
|
||||
ack.TTSVoicesAvailable = _voices.Get().ToDictionary(v => v.Key, v => v.Value.Name);
|
||||
|
||||
ack.EnabledTTSVoices = new List<string>();
|
||||
string sql5 = $"SELECT v.name FROM \"TtsVoiceState\" s "
|
||||
+ "INNER JOIN \"TtsVoice\" v ON s.\"ttsVoiceId\" = v.id "
|
||||
+ "WHERE \"userId\" = @user AND state = true";
|
||||
await _database.Execute(sql5, userIdDict, (r) => ack.EnabledTTSVoices.Add(r.GetString(0)));
|
||||
|
||||
ack.WordFilters = new List<TTSWordFilter>();
|
||||
string sql6 = $"SELECT id, search, replace FROM \"TtsWordFilter\" WHERE \"userId\" = @user";
|
||||
await _database.Execute(sql6, userIdDict, (r) => ack.WordFilters.Add(new TTSWordFilter()
|
||||
{
|
||||
Id = r.GetString(0),
|
||||
Search = r.GetString(1),
|
||||
Replace = r.GetString(2)
|
||||
}));
|
||||
|
||||
ack.DefaultTTSVoice = channel.User.DefaultVoice ?? _configuration.Tts.DefaultTtsVoice;
|
||||
|
||||
await sender.Send(2, ack);
|
||||
|
||||
string version = data.MajorVersion == null ? "unknown" : $"{data.MajorVersion}.{data.MinorVersion}";
|
||||
|
||||
@@ -5,7 +5,7 @@ using HermesSocketServer.Store.Internal;
|
||||
|
||||
namespace HermesSocketServer.Store
|
||||
{
|
||||
public class ActionStore : AutoSavedStore<string, RedeemableAction>
|
||||
public class ActionStore : ComplexAutoSavedStore<string, RedeemableAction>
|
||||
{
|
||||
private readonly string _userId;
|
||||
private readonly Database _database;
|
||||
@@ -45,9 +45,5 @@ namespace HermesSocketServer.Store
|
||||
protected override void OnInitialModify(string key, RedeemableAction value)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnInitialRemove(string key)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using HermesSocketServer.Store.Internal;
|
||||
|
||||
namespace HermesSocketServer.Store
|
||||
{
|
||||
public class ChatterStore : AutoSavedStore<string, ChatterVoice>
|
||||
public class ChatterStore : ComplexAutoSavedStore<string, ChatterVoice>
|
||||
{
|
||||
private readonly string _userId;
|
||||
private readonly Database _database;
|
||||
@@ -43,9 +43,5 @@ namespace HermesSocketServer.Store
|
||||
protected override void OnInitialModify(string key, ChatterVoice value)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnInitialRemove(string key)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,12 @@ namespace HermesSocketServer.Store.Internal
|
||||
|
||||
|
||||
public AutoSavedStore(DatabaseTable table, Database database, Serilog.ILogger logger)
|
||||
: base(logger)
|
||||
: base()
|
||||
{
|
||||
_generator = new GroupSaveSqlGenerator<V>(table.PropertyMapping, logger);
|
||||
if (table.TypeMapping == null)
|
||||
_generator = new GroupSaveSqlGenerator<V>(table.PropertyMapping, logger);
|
||||
else
|
||||
_generator = new GroupSaveSqlGenerator<V>(table.PropertyMapping, table.TypeMapping, logger);
|
||||
_table = table;
|
||||
_database = database;
|
||||
_logger = logger;
|
||||
@@ -25,21 +28,22 @@ namespace HermesSocketServer.Store.Internal
|
||||
public override async Task Save()
|
||||
{
|
||||
var allColumns = _table.KeyColumns.Union(_table.DataColumns).ToArray();
|
||||
var typeMapping = _table.TypeMapping ?? new Dictionary<string, string>();
|
||||
|
||||
await GenerateQuery(_added,
|
||||
(size) => _generator.GeneratePreparedInsertSql(_table.TableName, size, allColumns),
|
||||
async (query, _, values) => await _generator.DoPreparedStatement(_database, query, values, allColumns));
|
||||
|
||||
await GenerateQuery(_modified,
|
||||
(size) => _generator.GeneratePreparedUpdateSql(_table.TableName, size, _table.KeyColumns, _table.DataColumns),
|
||||
await GenerateQuery(_added,
|
||||
(size) => _generator.GeneratePreparedInsertSql(_table.TableName, size, allColumns, typeMapping),
|
||||
async (query, _, values) => await _generator.DoPreparedStatement(_database, query, values, allColumns));
|
||||
|
||||
await GenerateQuery(_deleted,
|
||||
(size) => _generator.GeneratePreparedDeleteSql(_table.TableName, size, _table.KeyColumns),
|
||||
await GenerateQuery(_modified,
|
||||
(size) => _generator.GeneratePreparedUpdateSql(_table.TableName, size, _table.KeyColumns, _table.DataColumns, typeMapping),
|
||||
async (query, _, values) => await _generator.DoPreparedStatement(_database, query, values, allColumns));
|
||||
|
||||
await GenerateQuery(_deleted,
|
||||
(size) => _generator.GeneratePreparedDeleteSql(_table.TableName, size, _table.KeyColumns, typeMapping),
|
||||
async (query, keys, _) => await _generator.DoPreparedStatementRaw(_database, query, keys, _table.KeyColumns));
|
||||
}
|
||||
|
||||
private async Task GenerateQuery(IList<K> keys, Func<int, string> generate, Func<string, IEnumerable<K>, IEnumerable<V>, Task> execute)
|
||||
private async Task GenerateQuery(IList<K> keys, Func<int, string> generate, Func<string, IEnumerable<K>, IEnumerable<V>, Task<int>> execute)
|
||||
{
|
||||
ImmutableList<K>? list = null;
|
||||
lock (_lock)
|
||||
@@ -52,10 +56,17 @@ namespace HermesSocketServer.Store.Internal
|
||||
}
|
||||
|
||||
var query = generate(list.Count);
|
||||
_logger.Debug($"{_table.TableName} - Adding {list.Count} rows to database: {query}");
|
||||
|
||||
var values = list.Select(id => _store[id]).Where(v => v != null);
|
||||
await execute(query, list, values);
|
||||
int rowsAffected = await execute(query, list, values);
|
||||
|
||||
if (rowsAffected != list.Count)
|
||||
{
|
||||
_logger.Error($"Rows affected in database ({rowsAffected}) and the number of items that should be modified ({list.Count}) do not match: {query}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Information($"{rowsAffected} rows were affected by this query: {query}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
106
Store/Internal/ComplexAutoSavedStore.cs
Normal file
106
Store/Internal/ComplexAutoSavedStore.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using HermesSocketLibrary.db;
|
||||
|
||||
namespace HermesSocketServer.Store.Internal
|
||||
{
|
||||
public abstract class ComplexAutoSavedStore<K, V> : GroupSaveStore<K, V> where K : class where V : class
|
||||
{
|
||||
private readonly IList<V> _removedValues;
|
||||
private readonly GroupSaveSqlGenerator<V> _generator;
|
||||
private readonly DatabaseTable _table;
|
||||
private readonly Database _database;
|
||||
private readonly Serilog.ILogger _logger;
|
||||
|
||||
|
||||
public ComplexAutoSavedStore(DatabaseTable table, Database database, Serilog.ILogger logger)
|
||||
: base()
|
||||
{
|
||||
if (table.KeyColumns.Length <= 1)
|
||||
throw new InvalidOperationException($"Use AutoSavedStore instead due to a single key column [table name: {table.TableName}]");
|
||||
|
||||
_removedValues = new List<V>();
|
||||
if (table.TypeMapping == null)
|
||||
_generator = new GroupSaveSqlGenerator<V>(table.PropertyMapping, logger);
|
||||
else
|
||||
_generator = new GroupSaveSqlGenerator<V>(table.PropertyMapping, table.TypeMapping, logger);
|
||||
_table = table;
|
||||
_database = database;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override async Task Save()
|
||||
{
|
||||
var allColumns = _table.KeyColumns.Union(_table.DataColumns).ToArray();
|
||||
var typeMapping = _table.TypeMapping ?? new Dictionary<string, string>();
|
||||
|
||||
await GenerateQuery(_added,
|
||||
(size) => _generator.GeneratePreparedInsertSql(_table.TableName, size, allColumns, typeMapping),
|
||||
async (query, _, values) => await _generator.DoPreparedStatement(_database, query, values, allColumns));
|
||||
|
||||
await GenerateQuery(_modified,
|
||||
(size) => _generator.GeneratePreparedUpdateSql(_table.TableName, size, _table.KeyColumns, _table.DataColumns, typeMapping),
|
||||
async (query, _, values) => await _generator.DoPreparedStatement(_database, query, values, allColumns));
|
||||
|
||||
await GenerateDeleteQuery(_deleted, _removedValues,
|
||||
(size) => _generator.GeneratePreparedDeleteSql(_table.TableName, size, _table.KeyColumns, typeMapping),
|
||||
async (query, list) => await _generator.DoPreparedStatement(_database, query, list, _table.KeyColumns));
|
||||
}
|
||||
|
||||
private async Task GenerateQuery(IList<K> keys, Func<int, string> generate, Func<string, IEnumerable<K>, IEnumerable<V>, Task<int>> execute)
|
||||
{
|
||||
ImmutableList<K>? list = null;
|
||||
lock (_lock)
|
||||
{
|
||||
if (!keys.Any())
|
||||
return;
|
||||
|
||||
list = keys.ToImmutableList();
|
||||
keys.Clear();
|
||||
}
|
||||
|
||||
var query = generate(list.Count);
|
||||
var values = list.Select(id => _store[id]).Where(v => v != null);
|
||||
int rowsAffected = await execute(query, list, values);
|
||||
|
||||
if (rowsAffected != list.Count)
|
||||
{
|
||||
_logger.Error($"Rows affected in database ({rowsAffected}) and the number of items that should be modified ({list.Count}) do not match: {query}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Information($"{rowsAffected} rows were affected by this query: {query}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GenerateDeleteQuery(IList<K> keys, IList<V> values, Func<int, string> generate, Func<string, IEnumerable<V>, Task<int>> execute)
|
||||
{
|
||||
ImmutableList<V>? list = null;
|
||||
lock (_lock)
|
||||
{
|
||||
if (!keys.Any() || !values.Any()) {
|
||||
return;
|
||||
}
|
||||
|
||||
list = values.ToImmutableList();
|
||||
values.Clear();
|
||||
keys.Clear();
|
||||
}
|
||||
|
||||
var query = generate(list.Count);
|
||||
int rowsAffected = await execute(query, list);
|
||||
|
||||
if (rowsAffected != list.Count)
|
||||
{
|
||||
_logger.Error($"Rows affected in database ({rowsAffected}) and the number of items that should be modified ({list.Count}) do not match: {query}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Information($"{rowsAffected} rows were affected by this query: {query}");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPostRemove(K key, V value) => _removedValues.Add(value);
|
||||
}
|
||||
}
|
||||
@@ -2,22 +2,21 @@ using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using HermesSocketLibrary.db;
|
||||
using NpgsqlTypes;
|
||||
|
||||
namespace HermesSocketServer.Store.Internal
|
||||
{
|
||||
public class GroupSaveSqlGenerator<T>
|
||||
{
|
||||
private readonly IDictionary<string, PropertyInfo?> _columnPropertyRelations;
|
||||
private readonly IDictionary<string, NpgsqlDbType> _columnTypes;
|
||||
private readonly IDictionary<string, string> _columnTypes;
|
||||
private readonly Serilog.ILogger _logger;
|
||||
|
||||
public GroupSaveSqlGenerator(IDictionary<string, string> columnsToProperties, Serilog.ILogger logger)
|
||||
: this(columnsToProperties, new Dictionary<string, NpgsqlDbType>(), logger)
|
||||
: this(columnsToProperties, new Dictionary<string, string>(), logger)
|
||||
{
|
||||
}
|
||||
|
||||
public GroupSaveSqlGenerator(IDictionary<string, string> columnsToProperties, IDictionary<string, NpgsqlDbType> columnTypes, Serilog.ILogger logger)
|
||||
public GroupSaveSqlGenerator(IDictionary<string, string> columnsToProperties, IDictionary<string, string> columnTypes, Serilog.ILogger logger)
|
||||
{
|
||||
_columnPropertyRelations = columnsToProperties.ToDictionary(p => p.Key, p => typeof(T).GetProperty(p.Value));
|
||||
_columnTypes = columnTypes;
|
||||
@@ -29,11 +28,11 @@ namespace HermesSocketServer.Store.Internal
|
||||
throw new ArgumentException("Some properties do not exist on the values given: " + string.Join(", ", nullProperties));
|
||||
}
|
||||
|
||||
public async Task DoPreparedStatement<V>(Database database, string sql, IEnumerable<V> values, string[] columns)
|
||||
public async Task<int> DoPreparedStatement<V>(Database database, string sql, IEnumerable<V> values, string[] columns)
|
||||
{
|
||||
try
|
||||
{
|
||||
await database.Execute(sql, (c) =>
|
||||
return await database.Execute(sql, (c) =>
|
||||
{
|
||||
var valueCounter = 0;
|
||||
foreach (var value in values)
|
||||
@@ -43,12 +42,10 @@ namespace HermesSocketServer.Store.Internal
|
||||
var propValue = _columnPropertyRelations[column]!.GetValue(value);
|
||||
if (_columnTypes.Any() && _columnTypes.TryGetValue(column, out var type))
|
||||
{
|
||||
if (type == NpgsqlDbType.Jsonb)
|
||||
if (type == "jsonb")
|
||||
propValue = JsonSerializer.Serialize(propValue);
|
||||
c.Parameters.AddWithValue(column.ToLower() + valueCounter, type, propValue ?? DBNull.Value);
|
||||
}
|
||||
else
|
||||
c.Parameters.AddWithValue(column.ToLower() + valueCounter, propValue ?? DBNull.Value);
|
||||
c.Parameters.AddWithValue(column.ToLower() + valueCounter, propValue ?? DBNull.Value);
|
||||
}
|
||||
valueCounter++;
|
||||
}
|
||||
@@ -57,14 +54,15 @@ namespace HermesSocketServer.Store.Internal
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to execute a prepared statement: " + sql);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DoPreparedStatementRaw<V>(Database database, string sql, IEnumerable<V> values, string[] columns)
|
||||
public async Task<int> DoPreparedStatementRaw<V>(Database database, string sql, IEnumerable<V> values, string[] columns)
|
||||
{
|
||||
try
|
||||
{
|
||||
await database.Execute(sql, (c) =>
|
||||
return await database.Execute(sql, (c) =>
|
||||
{
|
||||
var valueCounter = 0;
|
||||
foreach (var value in values)
|
||||
@@ -81,44 +79,11 @@ namespace HermesSocketServer.Store.Internal
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to execute a prepared statement: " + sql);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public string GenerateInsertSql(string table, IEnumerable<T> values, IEnumerable<string> columns)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(table))
|
||||
throw new ArgumentException("Value is either null or whitespace-filled.", nameof(table));
|
||||
if (values == null)
|
||||
throw new ArgumentNullException(nameof(values));
|
||||
if (!values.Any())
|
||||
throw new ArgumentException("Empty list given.", nameof(values));
|
||||
if (columns == null)
|
||||
throw new ArgumentNullException(nameof(columns));
|
||||
if (!columns.Any())
|
||||
throw new ArgumentException("Empty list given.", nameof(columns));
|
||||
|
||||
var ctp = columns.ToDictionary(c => c, c => _columnPropertyRelations[c]);
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"INSERT INTO \"{table}\" (\"{string.Join("\", \"", columns)}\") VALUES ");
|
||||
foreach (var value in values)
|
||||
{
|
||||
sb.Append("(");
|
||||
foreach (var column in columns)
|
||||
{
|
||||
var propValue = _columnPropertyRelations[column]!.GetValue(value);
|
||||
var propType = _columnPropertyRelations[column]!.PropertyType;
|
||||
WriteValue(sb, propValue ?? DBNull.Value, propType);
|
||||
sb.Append(",");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1)
|
||||
.Append("),");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1)
|
||||
.Append(';');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string GeneratePreparedInsertSql(string table, int rows, IEnumerable<string> columns)
|
||||
public string GeneratePreparedInsertSql(string table, int rows, IEnumerable<string> columns, IDictionary<string, string> typeMapping)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(table))
|
||||
throw new ArgumentException("Value is either null or whitespace-filled.", nameof(table));
|
||||
@@ -138,8 +103,14 @@ namespace HermesSocketServer.Store.Internal
|
||||
{
|
||||
sb.Append('@')
|
||||
.Append(column)
|
||||
.Append(row)
|
||||
.Append(", ");
|
||||
.Append(row);
|
||||
|
||||
if (typeMapping.TryGetValue(column, out var type))
|
||||
sb.Append("::\"")
|
||||
.Append(type)
|
||||
.Append("\"");
|
||||
|
||||
sb.Append(", ");
|
||||
}
|
||||
sb.Remove(sb.Length - 2, 2)
|
||||
.Append("),");
|
||||
@@ -149,49 +120,7 @@ namespace HermesSocketServer.Store.Internal
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string GenerateUpdateSql(string table, IEnumerable<T> values, IEnumerable<string> keyColumns, IEnumerable<string> updateColumns)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(table))
|
||||
throw new ArgumentException("Value is either null or whitespace-filled.", nameof(table));
|
||||
if (values == null)
|
||||
throw new ArgumentNullException(nameof(values));
|
||||
if (!values.Any())
|
||||
throw new ArgumentException("Empty list given.", nameof(values));
|
||||
if (keyColumns == null)
|
||||
throw new ArgumentNullException(nameof(keyColumns));
|
||||
if (!keyColumns.Any())
|
||||
throw new ArgumentException("Empty list given.", nameof(keyColumns));
|
||||
if (updateColumns == null)
|
||||
throw new ArgumentNullException(nameof(updateColumns));
|
||||
if (!updateColumns.Any())
|
||||
throw new ArgumentException("Empty list given.", nameof(updateColumns));
|
||||
|
||||
var columns = keyColumns.Union(updateColumns);
|
||||
var ctp = columns.ToDictionary(c => c, c => _columnPropertyRelations[c]);
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"UPDATE \"{table}\" as t SET {string.Join(", ", updateColumns.Select(c => "\"" + c + "\" = c.\"" + c + "\""))} FROM (VALUES ");
|
||||
foreach (var value in values)
|
||||
{
|
||||
sb.Append("(");
|
||||
foreach (var column in columns)
|
||||
{
|
||||
var propValue = _columnPropertyRelations[column]!.GetValue(value);
|
||||
var propType = _columnPropertyRelations[column]!.PropertyType;
|
||||
WriteValue(sb, propValue, propType);
|
||||
sb.Append(",");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1)
|
||||
.Append("),");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1)
|
||||
.Append($") AS c(\"{string.Join("\", \"", columns)}\") WHERE ")
|
||||
.Append(string.Join(" AND ", keyColumns.Select(c => "t.\"" + c + "\" = c.\"" + c + "\"")))
|
||||
.Append(";");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string GeneratePreparedUpdateSql(string table, int rows, IEnumerable<string> keyColumns, IEnumerable<string> updateColumns)
|
||||
public string GeneratePreparedUpdateSql(string table, int rows, IEnumerable<string> keyColumns, IEnumerable<string> updateColumns, IDictionary<string, string> typeMapping)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(table))
|
||||
throw new ArgumentException("Value is either null or whitespace-filled.", nameof(table));
|
||||
@@ -215,8 +144,14 @@ namespace HermesSocketServer.Store.Internal
|
||||
{
|
||||
sb.Append('@')
|
||||
.Append(column)
|
||||
.Append(row)
|
||||
.Append(", ");
|
||||
.Append(row);
|
||||
|
||||
if (typeMapping.TryGetValue(column, out var type))
|
||||
sb.Append("::\"")
|
||||
.Append(type)
|
||||
.Append("\"");
|
||||
|
||||
sb.Append(", ");
|
||||
}
|
||||
sb.Remove(sb.Length - 2, 2)
|
||||
.Append("),");
|
||||
@@ -229,41 +164,7 @@ namespace HermesSocketServer.Store.Internal
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string GenerateDeleteSql(string table, IEnumerable<string> keys, IEnumerable<string> keyColumns)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(table))
|
||||
throw new ArgumentException("Value is either null or whitespace-filled.", nameof(table));
|
||||
if (keys == null)
|
||||
throw new ArgumentNullException(nameof(keys));
|
||||
if (!keys.Any())
|
||||
throw new ArgumentException("Empty list given.", nameof(keys));
|
||||
if (keyColumns == null)
|
||||
throw new ArgumentNullException(nameof(keyColumns));
|
||||
if (!keyColumns.Any())
|
||||
throw new ArgumentException("Empty list given.", nameof(keyColumns));
|
||||
|
||||
var ctp = keyColumns.ToDictionary(c => c, c => _columnPropertyRelations[c]);
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"DELETE FROM \"{table}\" WHERE (\"{string.Join("\", \"", keyColumns)}\") IN (");
|
||||
foreach (var k in keys)
|
||||
{
|
||||
sb.Append("(");
|
||||
foreach (var column in keyColumns)
|
||||
{
|
||||
var propType = _columnPropertyRelations[column]!.PropertyType;
|
||||
WriteValue(sb, k, propType);
|
||||
sb.Append(",");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1)
|
||||
.Append("),");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1)
|
||||
.Append(");");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string GeneratePreparedDeleteSql(string table, int rows, IEnumerable<string> keyColumns)
|
||||
public string GeneratePreparedDeleteSql(string table, int rows, IEnumerable<string> keyColumns, IDictionary<string, string> typeMapping)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(table))
|
||||
throw new ArgumentException("Value is either null or whitespace-filled.", nameof(table));
|
||||
@@ -282,8 +183,14 @@ namespace HermesSocketServer.Store.Internal
|
||||
{
|
||||
sb.Append('@')
|
||||
.Append(column)
|
||||
.Append(row)
|
||||
.Append(", ");
|
||||
.Append(row);
|
||||
|
||||
if (typeMapping.TryGetValue(column, out var type))
|
||||
sb.Append("::\"")
|
||||
.Append(type)
|
||||
.Append("\"");
|
||||
|
||||
sb.Append(", ");
|
||||
}
|
||||
sb.Remove(sb.Length - 2, 2)
|
||||
.Append("),");
|
||||
@@ -293,26 +200,5 @@ namespace HermesSocketServer.Store.Internal
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private void WriteValue(StringBuilder sb, object? value, Type type)
|
||||
{
|
||||
if (type == typeof(string))
|
||||
sb.Append("'")
|
||||
.Append(value)
|
||||
.Append("'");
|
||||
else if (type == typeof(Guid))
|
||||
sb.Append("uuid('")
|
||||
.Append(value?.ToString())
|
||||
.Append("')");
|
||||
else if (type == typeof(TimeSpan))
|
||||
{
|
||||
if (value == null)
|
||||
sb.Append("0");
|
||||
else
|
||||
sb.Append(((TimeSpan)value).TotalMilliseconds);
|
||||
}
|
||||
else
|
||||
sb.Append(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ namespace HermesSocketServer.Store.Internal
|
||||
{
|
||||
public abstract class GroupSaveStore<K, V> : IStore<K, V> where K : class where V : class
|
||||
{
|
||||
private readonly Serilog.ILogger _logger;
|
||||
protected readonly IDictionary<K, V> _store;
|
||||
protected readonly IList<K> _added;
|
||||
protected readonly IList<K> _modified;
|
||||
@@ -14,9 +13,8 @@ namespace HermesSocketServer.Store.Internal
|
||||
protected readonly object _lock;
|
||||
|
||||
|
||||
public GroupSaveStore(Serilog.ILogger logger)
|
||||
public GroupSaveStore()
|
||||
{
|
||||
_logger = logger;
|
||||
_store = new Dictionary<K, V>();
|
||||
_added = new List<K>();
|
||||
_modified = new List<K>();
|
||||
@@ -27,7 +25,7 @@ namespace HermesSocketServer.Store.Internal
|
||||
public abstract Task Load();
|
||||
protected abstract void OnInitialAdd(K key, V value);
|
||||
protected abstract void OnInitialModify(K key, V value);
|
||||
protected abstract void OnInitialRemove(K key);
|
||||
protected abstract void OnPostRemove(K key, V value);
|
||||
public abstract Task Save();
|
||||
|
||||
public V? Get(K key)
|
||||
@@ -52,7 +50,7 @@ namespace HermesSocketServer.Store.Internal
|
||||
{
|
||||
if (key == null)
|
||||
return false;
|
||||
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_store.TryGetValue(key, out V? value))
|
||||
@@ -65,7 +63,6 @@ namespace HermesSocketServer.Store.Internal
|
||||
if (!_added.Contains(key) && !_modified.Contains(key))
|
||||
{
|
||||
_modified.Add(key);
|
||||
_logger.Information($"added key to _modified {key}");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -80,21 +77,21 @@ namespace HermesSocketServer.Store.Internal
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
OnInitialRemove(key);
|
||||
if (_store.Remove(key))
|
||||
if (_store.TryGetValue(key, out var value))
|
||||
{
|
||||
_logger.Information($"removed key from _deleted {key}");
|
||||
if (!_added.Remove(key))
|
||||
if (_store.Remove(key))
|
||||
{
|
||||
_modified.Remove(key);
|
||||
_logger.Information($"removed key from _added & _modified {key}");
|
||||
if (!_deleted.Contains(key))
|
||||
OnPostRemove(key, value);
|
||||
if (!_added.Remove(key))
|
||||
{
|
||||
_deleted.Add(key);
|
||||
_logger.Information($"added key to _deleted {key}");
|
||||
_modified.Remove(key);
|
||||
if (!_deleted.Contains(key))
|
||||
{
|
||||
_deleted.Add(key);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -116,7 +113,6 @@ namespace HermesSocketServer.Store.Internal
|
||||
if (!_added.Contains(key) && !_modified.Contains(key))
|
||||
{
|
||||
_modified.Add(key);
|
||||
_logger.Information($"added key to _modified {key}");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -128,7 +124,6 @@ namespace HermesSocketServer.Store.Internal
|
||||
if (!_deleted.Remove(key) && !_added.Contains(key))
|
||||
{
|
||||
_added.Add(key);
|
||||
_logger.Information($"added key to _added {key}");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using HermesSocketLibrary.db;
|
||||
using HermesSocketServer.Models;
|
||||
using HermesSocketServer.Messages;
|
||||
using HermesSocketServer.Store.Internal;
|
||||
|
||||
namespace HermesSocketServer.Store
|
||||
{
|
||||
public class PolicyStore : AutoSavedStore<string, PolicyMessage>
|
||||
public class PolicyStore : AutoSavedStore<string, Policy>
|
||||
{
|
||||
private readonly string _userId;
|
||||
private readonly Database _database;
|
||||
@@ -26,7 +26,7 @@ namespace HermesSocketServer.Store
|
||||
await _database.Execute(sql, data, (reader) =>
|
||||
{
|
||||
var id = reader.GetGuid(0);
|
||||
_store.Add(id.ToString(), new PolicyMessage()
|
||||
_store.Add(id.ToString(), new Policy()
|
||||
{
|
||||
Id = id,
|
||||
UserId = _userId,
|
||||
@@ -39,15 +39,15 @@ namespace HermesSocketServer.Store
|
||||
_logger.Information($"Loaded {_store.Count} policies from database.");
|
||||
}
|
||||
|
||||
protected override void OnInitialAdd(string key, PolicyMessage value)
|
||||
protected override void OnInitialAdd(string key, Policy value)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnInitialModify(string key, PolicyMessage value)
|
||||
protected override void OnInitialModify(string key, Policy value)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnInitialRemove(string key)
|
||||
protected override void OnPostRemove(string key, Policy value)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace HermesSocketServer.Store
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnInitialRemove(string key)
|
||||
protected override void OnPostRemove(string key, Redemption value)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,15 +22,17 @@ namespace HermesSocketServer.Store
|
||||
public override async Task Load()
|
||||
{
|
||||
var data = new Dictionary<string, object>() { { "user", _userId } };
|
||||
string sql = $"SELECT id, search, replace FROM \"TtsWordFilter\" WHERE \"userId\" = @user";
|
||||
string sql = $"SELECT id, search, replace, flag FROM \"TtsWordFilter\" WHERE \"userId\" = @user";
|
||||
await _database.Execute(sql, data, (reader) =>
|
||||
{
|
||||
var id = reader.GetString(0);
|
||||
_store.Add(id.ToString(), new TTSWordFilter()
|
||||
{
|
||||
Id = id,
|
||||
UserId = _userId,
|
||||
Search = reader.GetString(1),
|
||||
Replace = reader.GetString(2)
|
||||
Replace = reader.GetString(2),
|
||||
Flag = reader.GetInt32(3),
|
||||
});
|
||||
});
|
||||
_logger.Information($"Loaded {_store.Count} TTS chatter voices from database.");
|
||||
@@ -44,7 +46,7 @@ namespace HermesSocketServer.Store
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnInitialRemove(string key)
|
||||
protected override void OnPostRemove(string key, TTSWordFilter value)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace HermesSocketServer.Store
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnInitialRemove(string key)
|
||||
protected override void OnPostRemove(string key, User value)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ using HermesSocketServer.Validators;
|
||||
|
||||
namespace HermesSocketServer.Store
|
||||
{
|
||||
public class VoiceStateStore : AutoSavedStore<string, TTSVoiceState>
|
||||
public class VoiceStateStore : ComplexAutoSavedStore<string, TTSVoiceState>
|
||||
{
|
||||
private readonly string _userId;
|
||||
private readonly VoiceIdValidator _idValidator;
|
||||
@@ -48,7 +48,7 @@ namespace HermesSocketServer.Store
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnInitialRemove(string key)
|
||||
protected override void OnPostRemove(string key, TTSVoiceState value)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace HermesSocketServer.Store
|
||||
_nameValidator.Check(value.Name);
|
||||
}
|
||||
|
||||
protected override void OnInitialRemove(string key)
|
||||
protected override void OnPostRemove(string key, TTSVoice value)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user