Various small fixes, setting guild config now works

This commit is contained in:
Ske 2020-06-13 19:15:50 +02:00
parent 2038f023a0
commit c875c8af9f
9 changed files with 56 additions and 70 deletions

View File

@ -86,7 +86,7 @@ namespace PluralKit.API
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{ {
Schemas.Initialize(); Database.InitStatic();
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {

View File

@ -67,7 +67,7 @@ namespace PluralKit.Bot
else else
blacklist.UnionWith(affectedChannels.Select(c => c.Id)); blacklist.UnionWith(affectedChannels.Select(c => c.Id));
await conn.ExecuteAsync("update servers set log_blacklist = @LogBlacklist where id = @Id", await conn.ExecuteAsync("update servers set log_blacklist = @LogBlacklist where id = @Id",
new {ctx.Guild.Id, LogBlacklist = blacklist}); new {ctx.Guild.Id, LogBlacklist = blacklist.ToArray()});
} }
await ctx.Reply( await ctx.Reply(
@ -99,7 +99,7 @@ namespace PluralKit.Bot
else else
blacklist.UnionWith(affectedChannels.Select(c => c.Id)); blacklist.UnionWith(affectedChannels.Select(c => c.Id));
await conn.ExecuteAsync("update servers set blacklist = @Blacklist where id = @Id", await conn.ExecuteAsync("update servers set blacklist = @Blacklist where id = @Id",
new {ctx.Guild.Id, Blacklist = blacklist}); new {ctx.Guild.Id, Blacklist = blacklist.ToArray()});
} }
await ctx.Reply($"{Emojis.Success} Channels {(onBlacklist ? "added to" : "removed from")} the proxy blacklist."); await ctx.Reply($"{Emojis.Success} Channels {(onBlacklist ? "added to" : "removed from")} the proxy blacklist.");

View File

@ -21,7 +21,7 @@ namespace PluralKit.Bot
{ {
// Load configuration and run global init stuff // Load configuration and run global init stuff
var config = InitUtils.BuildConfiguration(args).Build(); var config = InitUtils.BuildConfiguration(args).Build();
InitUtils.Init(); Database.InitStatic();
// Set up DI container and modules // Set up DI container and modules
var services = BuildContainer(config); var services = BuildContainer(config);

View File

@ -1,8 +1,13 @@
using System; using System;
using System.Data;
using System.Threading.Tasks; using System.Threading.Tasks;
using App.Metrics; using App.Metrics;
using Dapper;
using NodaTime;
using Npgsql; using Npgsql;
using Serilog; using Serilog;
@ -25,6 +30,29 @@ namespace PluralKit.Core
_logger = logger; _logger = logger;
} }
public static void InitStatic()
{
// Dapper by default tries to pass ulongs to Npgsql, which rejects them since PostgreSQL technically
// doesn't support unsigned types on its own.
// Instead we add a custom mapper to encode them as signed integers instead, converting them back and forth.
SqlMapper.RemoveTypeMap(typeof(ulong));
SqlMapper.AddTypeHandler(new UlongEncodeAsLongHandler());
SqlMapper.AddTypeHandler(new UlongArrayHandler());
DefaultTypeMap.MatchNamesWithUnderscores = true;
NpgsqlConnection.GlobalTypeMapper.UseNodaTime();
// With the thing we add above, Npgsql already handles NodaTime integration
// This makes Dapper confused since it thinks it has to convert it anyway and doesn't understand the types
// So we add a custom type handler that literally just passes the type through to Npgsql
SqlMapper.AddTypeHandler(new PassthroughTypeHandler<Instant>());
SqlMapper.AddTypeHandler(new PassthroughTypeHandler<LocalDate>());
// Register our custom types to Npgsql
// Without these it'll still *work* but break at the first launch + probably cause other small issues
NpgsqlConnection.GlobalTypeMapper.MapComposite<ProxyTag>("proxy_tag");
NpgsqlConnection.GlobalTypeMapper.MapEnum<PrivacyLevel>("privacy_level");
}
public async Task<IPKConnection> Obtain() public async Task<IPKConnection> Obtain()
{ {
// Mark the request (for a handle, I guess) in the metrics // Mark the request (for a handle, I guess) in the metrics
@ -48,5 +76,27 @@ namespace PluralKit.Core
await using var conn = await Obtain(); await using var conn = await Obtain();
return await func(conn); return await func(conn);
} }
private class PassthroughTypeHandler<T>: SqlMapper.TypeHandler<T>
{
public override void SetValue(IDbDataParameter parameter, T value) => parameter.Value = value;
public override T Parse(object value) => (T) value;
}
private class UlongEncodeAsLongHandler: SqlMapper.TypeHandler<ulong>
{
public override ulong Parse(object value) =>
// Cast to long to unbox, then to ulong (???)
(ulong) (long) value;
public override void SetValue(IDbDataParameter parameter, ulong value) => parameter.Value = (long) value;
}
private class UlongArrayHandler: SqlMapper.TypeHandler<ulong[]>
{
public override void SetValue(IDbDataParameter parameter, ulong[] value) => parameter.Value = Array.ConvertAll(value, i => (long) i);
public override ulong[] Parse(object value) => Array.ConvertAll((long[]) value, i => (ulong) i);
}
} }
} }

View File

@ -25,13 +25,6 @@ namespace PluralKit.Core
_logger = logger.ForContext<Schemas>(); _logger = logger.ForContext<Schemas>();
} }
public static void Initialize()
{
// Without these it'll still *work* but break at the first launch + probably cause other small issues
NpgsqlConnection.GlobalTypeMapper.MapComposite<ProxyTag>("proxy_tag");
NpgsqlConnection.GlobalTypeMapper.MapEnum<PrivacyLevel>("privacy_level");
}
public async Task InitializeDatabase() public async Task InitializeDatabase()
{ {
// Run everything in a transaction // Run everything in a transaction

View File

@ -2,7 +2,7 @@
{ {
public class GuildConfig public class GuildConfig
{ {
public int Id { get; } public ulong Id { get; }
public ulong? LogChannel { get; } public ulong? LogChannel { get; }
public ulong[] LogBlacklist { get; } public ulong[] LogBlacklist { get; }
public ulong[] Blacklist { get; } public ulong[] Blacklist { get; }

View File

@ -12,7 +12,7 @@ namespace PluralKit.Core
conn.QueryFirstOrDefaultAsync<PKMember?>("select * from members where id = @id", new {id}); conn.QueryFirstOrDefaultAsync<PKMember?>("select * from members where id = @id", new {id});
public static Task<GuildConfig> QueryOrInsertGuildConfig(this IPKConnection conn, ulong guild) => public static Task<GuildConfig> QueryOrInsertGuildConfig(this IPKConnection conn, ulong guild) =>
conn.QueryFirstAsync<GuildConfig>("insert into servers (id) values (@Guild) on conflict do nothing returning *", new {Guild = guild}); conn.QueryFirstAsync<GuildConfig>("insert into servers (id) values (@guild) on conflict (id) do update set id = @guild returning *", new {guild});
public static Task<SystemGuildSettings> QueryOrInsertSystemGuildConfig(this IPKConnection conn, ulong guild, int system) => public static Task<SystemGuildSettings> QueryOrInsertSystemGuildConfig(this IPKConnection conn, ulong guild, int system) =>
conn.QueryFirstAsync<SystemGuildSettings>( conn.QueryFirstAsync<SystemGuildSettings>(

View File

@ -20,31 +20,4 @@ namespace PluralKit.Core
Interlocked.Decrement(ref _connectionCount); Interlocked.Decrement(ref _connectionCount);
} }
} }
public class PassthroughTypeHandler<T>: SqlMapper.TypeHandler<T>
{
public override void SetValue(IDbDataParameter parameter, T value)
{
parameter.Value = value;
}
public override T Parse(object value)
{
return (T) value;
}
}
public class UlongEncodeAsLongHandler: SqlMapper.TypeHandler<ulong>
{
public override ulong Parse(object value)
{
// Cast to long to unbox, then to ulong (???)
return (ulong) (long) value;
}
public override void SetValue(IDbDataParameter parameter, ulong value)
{
parameter.Value = (long) value;
}
}
} }

View File

@ -1,7 +1,5 @@
using System.IO; using System.IO;
using Dapper;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -9,8 +7,6 @@ using Newtonsoft.Json;
using NodaTime; using NodaTime;
using NodaTime.Serialization.JsonNet; using NodaTime.Serialization.JsonNet;
using Npgsql;
namespace PluralKit.Core { namespace PluralKit.Core {
public static class InitUtils public static class InitUtils
{ {
@ -20,32 +16,6 @@ namespace PluralKit.Core {
.AddEnvironmentVariables() .AddEnvironmentVariables()
.AddCommandLine(args); .AddCommandLine(args);
public static void Init()
{
InitDatabase();
}
private static void InitDatabase()
{
// Dapper by default tries to pass ulongs to Npgsql, which rejects them since PostgreSQL technically
// doesn't support unsigned types on its own.
// Instead we add a custom mapper to encode them as signed integers instead, converting them back and forth.
SqlMapper.RemoveTypeMap(typeof(ulong));
SqlMapper.AddTypeHandler<ulong>(new UlongEncodeAsLongHandler());
Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
// Also, use NodaTime. it's good.
NpgsqlConnection.GlobalTypeMapper.UseNodaTime();
// With the thing we add above, Npgsql already handles NodaTime integration
// This makes Dapper confused since it thinks it has to convert it anyway and doesn't understand the types
// So we add a custom type handler that literally just passes the type through to Npgsql
SqlMapper.AddTypeHandler(new PassthroughTypeHandler<Instant>());
SqlMapper.AddTypeHandler(new PassthroughTypeHandler<LocalDate>());
// Add global type mapper for ProxyTag compound type in Postgres
NpgsqlConnection.GlobalTypeMapper.MapComposite<ProxyTag>("proxy_tag");
}
public static JsonSerializerSettings BuildSerializerSettings() => new JsonSerializerSettings().BuildSerializerSettings(); public static JsonSerializerSettings BuildSerializerSettings() => new JsonSerializerSettings().BuildSerializerSettings();
public static JsonSerializerSettings BuildSerializerSettings(this JsonSerializerSettings settings) public static JsonSerializerSettings BuildSerializerSettings(this JsonSerializerSettings settings)