Migrate to type-safe model ID structs
This commit is contained in:
parent
e5ac5edc35
commit
b9cbd241de
@ -276,7 +276,7 @@ namespace PluralKit.Bot
|
|||||||
public LookupContext LookupContextFor(PKSystem target) =>
|
public LookupContext LookupContextFor(PKSystem target) =>
|
||||||
System?.Id == target.Id ? LookupContext.ByOwner : LookupContext.ByNonOwner;
|
System?.Id == target.Id ? LookupContext.ByOwner : LookupContext.ByNonOwner;
|
||||||
|
|
||||||
public LookupContext LookupContextFor(int systemId) =>
|
public LookupContext LookupContextFor(SystemId systemId) =>
|
||||||
System?.Id == systemId ? LookupContext.ByOwner : LookupContext.ByNonOwner;
|
System?.Id == systemId ? LookupContext.ByOwner : LookupContext.ByNonOwner;
|
||||||
|
|
||||||
public Context CheckSystemPrivacy(PKSystem target, PrivacyLevel level)
|
public Context CheckSystemPrivacy(PKSystem target, PrivacyLevel level)
|
||||||
|
@ -87,7 +87,7 @@ namespace PluralKit.Bot
|
|||||||
var fronters = ctx.MessageContext.LastSwitchMembers;
|
var fronters = ctx.MessageContext.LastSwitchMembers;
|
||||||
var relevantMember = ctx.MessageContext.AutoproxyMode switch
|
var relevantMember = ctx.MessageContext.AutoproxyMode switch
|
||||||
{
|
{
|
||||||
AutoproxyMode.Front => fronters.Count > 0 ? await _db.Execute(c => c.QueryMember(fronters[0])) : null,
|
AutoproxyMode.Front => fronters.Length > 0 ? await _db.Execute(c => c.QueryMember(fronters[0])) : null,
|
||||||
AutoproxyMode.Member => await _db.Execute(c => c.QueryMember(ctx.MessageContext.AutoproxyMember.Value)),
|
AutoproxyMode.Member => await _db.Execute(c => c.QueryMember(ctx.MessageContext.AutoproxyMember.Value)),
|
||||||
_ => null
|
_ => null
|
||||||
};
|
};
|
||||||
@ -97,7 +97,7 @@ namespace PluralKit.Bot
|
|||||||
break;
|
break;
|
||||||
case AutoproxyMode.Front:
|
case AutoproxyMode.Front:
|
||||||
{
|
{
|
||||||
if (fronters.Count == 0)
|
if (fronters.Length == 0)
|
||||||
eb.WithDescription("Autoproxy is currently set to **front mode** in this server, but there are currently no fronters registered. Use the `pk;switch` command to log a switch.");
|
eb.WithDescription("Autoproxy is currently set to **front mode** in this server, but there are currently no fronters registered. Use the `pk;switch` command to log a switch.");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -123,7 +123,7 @@ namespace PluralKit.Bot
|
|||||||
return eb.Build();
|
return eb.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task UpdateAutoproxy(Context ctx, AutoproxyMode autoproxyMode, int? autoproxyMember) =>
|
private Task UpdateAutoproxy(Context ctx, AutoproxyMode autoproxyMode, MemberId? autoproxyMember) =>
|
||||||
_db.Execute(c =>
|
_db.Execute(c =>
|
||||||
c.ExecuteAsync(
|
c.ExecuteAsync(
|
||||||
"update system_guild set autoproxy_mode = @autoproxyMode, autoproxy_member = @autoproxyMember where guild = @guild and system = @system",
|
"update system_guild set autoproxy_mode = @autoproxyMode, autoproxy_member = @autoproxyMember where guild = @guild and system = @system",
|
||||||
|
@ -48,7 +48,7 @@ namespace PluralKit.Bot
|
|||||||
AutoproxyMode.Member when ctx.AutoproxyMember != null =>
|
AutoproxyMode.Member when ctx.AutoproxyMember != null =>
|
||||||
members.FirstOrDefault(m => m.Id == ctx.AutoproxyMember),
|
members.FirstOrDefault(m => m.Id == ctx.AutoproxyMember),
|
||||||
|
|
||||||
AutoproxyMode.Front when ctx.LastSwitchMembers.Count > 0 =>
|
AutoproxyMode.Front when ctx.LastSwitchMembers.Length > 0 =>
|
||||||
members.FirstOrDefault(m => m.Id == ctx.LastSwitchMembers[0]),
|
members.FirstOrDefault(m => m.Id == ctx.LastSwitchMembers[0]),
|
||||||
|
|
||||||
AutoproxyMode.Latch when ctx.LastMessageMember != null && !IsLatchExpired(ctx.LastMessage) =>
|
AutoproxyMode.Latch when ctx.LastMessageMember != null && !IsLatchExpired(ctx.LastMessage) =>
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using App.Metrics;
|
using App.Metrics;
|
||||||
@ -36,13 +38,14 @@ namespace PluralKit.Core
|
|||||||
|
|
||||||
public static void InitStatic()
|
public static void InitStatic()
|
||||||
{
|
{
|
||||||
|
DefaultTypeMap.MatchNamesWithUnderscores = true;
|
||||||
|
|
||||||
// Dapper by default tries to pass ulongs to Npgsql, which rejects them since PostgreSQL technically
|
// Dapper by default tries to pass ulongs to Npgsql, which rejects them since PostgreSQL technically
|
||||||
// doesn't support unsigned types on its own.
|
// 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.
|
// Instead we add a custom mapper to encode them as signed integers instead, converting them back and forth.
|
||||||
SqlMapper.RemoveTypeMap(typeof(ulong));
|
SqlMapper.RemoveTypeMap(typeof(ulong));
|
||||||
SqlMapper.AddTypeHandler(new UlongEncodeAsLongHandler());
|
SqlMapper.AddTypeHandler(new UlongEncodeAsLongHandler());
|
||||||
SqlMapper.AddTypeHandler(new UlongArrayHandler());
|
SqlMapper.AddTypeHandler(new UlongArrayHandler());
|
||||||
DefaultTypeMap.MatchNamesWithUnderscores = true;
|
|
||||||
|
|
||||||
NpgsqlConnection.GlobalTypeMapper.UseNodaTime();
|
NpgsqlConnection.GlobalTypeMapper.UseNodaTime();
|
||||||
// With the thing we add above, Npgsql already handles NodaTime integration
|
// With the thing we add above, Npgsql already handles NodaTime integration
|
||||||
@ -51,6 +54,14 @@ namespace PluralKit.Core
|
|||||||
SqlMapper.AddTypeHandler(new PassthroughTypeHandler<Instant>());
|
SqlMapper.AddTypeHandler(new PassthroughTypeHandler<Instant>());
|
||||||
SqlMapper.AddTypeHandler(new PassthroughTypeHandler<LocalDate>());
|
SqlMapper.AddTypeHandler(new PassthroughTypeHandler<LocalDate>());
|
||||||
|
|
||||||
|
// Add ID types to Dapper
|
||||||
|
SqlMapper.AddTypeHandler(new NumericIdHandler<SystemId, int>(i => new SystemId(i)));
|
||||||
|
SqlMapper.AddTypeHandler(new NumericIdHandler<MemberId, int>(i => new MemberId(i)));
|
||||||
|
SqlMapper.AddTypeHandler(new NumericIdHandler<SwitchId, int>(i => new SwitchId(i)));
|
||||||
|
SqlMapper.AddTypeHandler(new NumericIdArrayHandler<SystemId, int>(i => new SystemId(i)));
|
||||||
|
SqlMapper.AddTypeHandler(new NumericIdArrayHandler<MemberId, int>(i => new MemberId(i)));
|
||||||
|
SqlMapper.AddTypeHandler(new NumericIdArrayHandler<SwitchId, int>(i => new SwitchId(i)));
|
||||||
|
|
||||||
// Register our custom types to Npgsql
|
// Register our custom types to Npgsql
|
||||||
// Without these it'll still *work* but break at the first launch + probably cause other small issues
|
// 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.MapComposite<ProxyTag>("proxy_tag");
|
||||||
@ -153,5 +164,37 @@ namespace PluralKit.Core
|
|||||||
|
|
||||||
public override ulong[] Parse(object value) => Array.ConvertAll((long[]) value, i => (ulong) i);
|
public override ulong[] Parse(object value) => Array.ConvertAll((long[]) value, i => (ulong) i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class NumericIdHandler<T, TInner>: SqlMapper.TypeHandler<T>
|
||||||
|
where T: INumericId<T, TInner>
|
||||||
|
where TInner: IEquatable<TInner>, IComparable<TInner>
|
||||||
|
{
|
||||||
|
private readonly Func<TInner, T> _factory;
|
||||||
|
|
||||||
|
public NumericIdHandler(Func<TInner, T> factory)
|
||||||
|
{
|
||||||
|
_factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetValue(IDbDataParameter parameter, T value) => parameter.Value = value.Value;
|
||||||
|
|
||||||
|
public override T Parse(object value) => _factory((TInner) value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NumericIdArrayHandler<T, TInner>: SqlMapper.TypeHandler<T[]>
|
||||||
|
where T: INumericId<T, TInner>
|
||||||
|
where TInner: IEquatable<TInner>, IComparable<TInner>
|
||||||
|
{
|
||||||
|
private readonly Func<TInner, T> _factory;
|
||||||
|
|
||||||
|
public NumericIdArrayHandler(Func<TInner, T> factory)
|
||||||
|
{
|
||||||
|
_factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetValue(IDbDataParameter parameter, T[] value) => parameter.Value = Array.ConvertAll(value, v => v.Value);
|
||||||
|
|
||||||
|
public override T[] Parse(object value) => Array.ConvertAll((TInner[]) value, v => _factory(v));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,18 +10,18 @@ namespace PluralKit.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class MessageContext
|
public class MessageContext
|
||||||
{
|
{
|
||||||
public int? SystemId { get; }
|
public SystemId? SystemId { get; }
|
||||||
public ulong? LogChannel { get; }
|
public ulong? LogChannel { get; }
|
||||||
public bool InBlacklist { get; }
|
public bool InBlacklist { get; }
|
||||||
public bool InLogBlacklist { get; }
|
public bool InLogBlacklist { get; }
|
||||||
public bool LogCleanupEnabled { get; }
|
public bool LogCleanupEnabled { get; }
|
||||||
public bool ProxyEnabled { get; }
|
public bool ProxyEnabled { get; }
|
||||||
public AutoproxyMode AutoproxyMode { get; }
|
public AutoproxyMode AutoproxyMode { get; }
|
||||||
public int? AutoproxyMember { get; }
|
public MemberId? AutoproxyMember { get; }
|
||||||
public ulong? LastMessage { get; }
|
public ulong? LastMessage { get; }
|
||||||
public int? LastMessageMember { get; }
|
public MemberId? LastMessageMember { get; }
|
||||||
public int LastSwitch { get; }
|
public SwitchId LastSwitch { get; }
|
||||||
public IReadOnlyList<int> LastSwitchMembers { get; } = new int[0];
|
public MemberId[] LastSwitchMembers { get; } = new MemberId[0];
|
||||||
public Instant LastSwitchTimestamp { get; }
|
public Instant LastSwitchTimestamp { get; }
|
||||||
public string? SystemTag { get; }
|
public string? SystemTag { get; }
|
||||||
public string? SystemAvatar { get; }
|
public string? SystemAvatar { get; }
|
||||||
|
@ -8,7 +8,7 @@ namespace PluralKit.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ProxyMember
|
public class ProxyMember
|
||||||
{
|
{
|
||||||
public int Id { get; }
|
public MemberId Id { get; }
|
||||||
public IReadOnlyCollection<ProxyTag> ProxyTags { get; } = new ProxyTag[0];
|
public IReadOnlyCollection<ProxyTag> ProxyTags { get; } = new ProxyTag[0];
|
||||||
public bool KeepProxy { get; }
|
public bool KeepProxy { get; }
|
||||||
|
|
||||||
|
@ -9,10 +9,10 @@ namespace PluralKit.Core
|
|||||||
{
|
{
|
||||||
public static class DatabaseViewsExt
|
public static class DatabaseViewsExt
|
||||||
{
|
{
|
||||||
public static Task<IEnumerable<SystemFronter>> QueryCurrentFronters(this IPKConnection conn, int system) =>
|
public static Task<IEnumerable<SystemFronter>> QueryCurrentFronters(this IPKConnection conn, SystemId system) =>
|
||||||
conn.QueryAsync<SystemFronter>("select * from system_fronters where system = @system", new {system});
|
conn.QueryAsync<SystemFronter>("select * from system_fronters where system = @system", new {system});
|
||||||
|
|
||||||
public static Task<IEnumerable<ListedMember>> QueryMemberList(this IPKConnection conn, int system, PrivacyLevel? privacyFilter = null, string? filter = null, bool includeDescriptionInNameFilter = false)
|
public static Task<IEnumerable<ListedMember>> QueryMemberList(this IPKConnection conn, SystemId system, PrivacyLevel? privacyFilter = null, string? filter = null, bool includeDescriptionInNameFilter = false)
|
||||||
{
|
{
|
||||||
StringBuilder query = new StringBuilder("select * from member_list where system = @system");
|
StringBuilder query = new StringBuilder("select * from member_list where system = @system");
|
||||||
|
|
||||||
|
@ -4,10 +4,10 @@ namespace PluralKit.Core
|
|||||||
{
|
{
|
||||||
public class SystemFronter
|
public class SystemFronter
|
||||||
{
|
{
|
||||||
public int SystemId { get; }
|
public SystemId SystemId { get; }
|
||||||
public int SwitchId { get; }
|
public SwitchId SwitchId { get; }
|
||||||
public Instant SwitchTimestamp { get; }
|
public Instant SwitchTimestamp { get; }
|
||||||
public int MemberId { get; }
|
public MemberId MemberId { get; }
|
||||||
public string MemberHid { get; }
|
public string MemberHid { get; }
|
||||||
public string MemberName { get; }
|
public string MemberName { get; }
|
||||||
}
|
}
|
||||||
|
11
PluralKit.Core/Models/INumericId.cs
Normal file
11
PluralKit.Core/Models/INumericId.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PluralKit.Core
|
||||||
|
{
|
||||||
|
public interface INumericId<T, out TInner>: IEquatable<T>, IComparable<T>
|
||||||
|
where T: INumericId<T, TInner>
|
||||||
|
where TInner: IEquatable<TInner>, IComparable<TInner>
|
||||||
|
{
|
||||||
|
public TInner Value { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@ namespace PluralKit.Core
|
|||||||
{
|
{
|
||||||
public class MemberGuildSettings
|
public class MemberGuildSettings
|
||||||
{
|
{
|
||||||
public int Member { get; }
|
public MemberId Member { get; }
|
||||||
public ulong Guild { get; }
|
public ulong Guild { get; }
|
||||||
public string? DisplayName { get; }
|
public string? DisplayName { get; }
|
||||||
public string? AvatarUrl { get; }
|
public string? AvatarUrl { get; }
|
||||||
|
24
PluralKit.Core/Models/MemberId.cs
Normal file
24
PluralKit.Core/Models/MemberId.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
namespace PluralKit.Core
|
||||||
|
{
|
||||||
|
public readonly struct MemberId: INumericId<MemberId, int>
|
||||||
|
{
|
||||||
|
public int Value { get; }
|
||||||
|
|
||||||
|
public MemberId(int value)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(MemberId other) => Value == other.Value;
|
||||||
|
|
||||||
|
public override bool Equals(object obj) => obj is MemberId other && Equals(other);
|
||||||
|
|
||||||
|
public override int GetHashCode() => Value;
|
||||||
|
|
||||||
|
public static bool operator ==(MemberId left, MemberId right) => left.Equals(right);
|
||||||
|
|
||||||
|
public static bool operator !=(MemberId left, MemberId right) => !left.Equals(right);
|
||||||
|
|
||||||
|
public int CompareTo(MemberId other) => Value.CompareTo(other.Value);
|
||||||
|
}
|
||||||
|
}
|
@ -7,22 +7,22 @@ namespace PluralKit.Core
|
|||||||
{
|
{
|
||||||
public static class ModelQueryExt
|
public static class ModelQueryExt
|
||||||
{
|
{
|
||||||
public static Task<PKSystem?> QuerySystem(this IPKConnection conn, int id) =>
|
public static Task<PKSystem?> QuerySystem(this IPKConnection conn, SystemId id) =>
|
||||||
conn.QueryFirstOrDefaultAsync<PKSystem?>("select * from systems where id = @id", new {id});
|
conn.QueryFirstOrDefaultAsync<PKSystem?>("select * from systems where id = @id", new {id});
|
||||||
|
|
||||||
public static Task<PKMember?> QueryMember(this IPKConnection conn, int id) =>
|
public static Task<PKMember?> QueryMember(this IPKConnection conn, MemberId id) =>
|
||||||
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 (id) do update set id = @guild returning *", new {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, SystemId system) =>
|
||||||
conn.QueryFirstAsync<SystemGuildSettings>(
|
conn.QueryFirstAsync<SystemGuildSettings>(
|
||||||
"insert into member_guild (guild, member) values (@guild, @member) on conflict (guild, member) do update set guild = @guild, member = @member returning *",
|
"insert into member_guild (guild, member) values (@guild, @member) on conflict (guild, member) do update set guild = @guild, member = @member returning *",
|
||||||
new {guild, system});
|
new {guild, system});
|
||||||
|
|
||||||
public static Task<MemberGuildSettings> QueryOrInsertMemberGuildConfig(
|
public static Task<MemberGuildSettings> QueryOrInsertMemberGuildConfig(
|
||||||
this IPKConnection conn, ulong guild, int member) =>
|
this IPKConnection conn, ulong guild, MemberId member) =>
|
||||||
conn.QueryFirstAsync<MemberGuildSettings>(
|
conn.QueryFirstAsync<MemberGuildSettings>(
|
||||||
"insert into member_guild (guild, member) values (@guild, @member) on conflict (guild, member) do update set guild = @guild, member = @member returning *",
|
"insert into member_guild (guild, member) values (@guild, @member) on conflict (guild, member) do update set guild = @guild, member = @member returning *",
|
||||||
new {guild, member});
|
new {guild, member});
|
||||||
|
@ -8,9 +8,9 @@ using NodaTime.Text;
|
|||||||
namespace PluralKit.Core {
|
namespace PluralKit.Core {
|
||||||
public class PKMember
|
public class PKMember
|
||||||
{
|
{
|
||||||
public int Id { get; }
|
public MemberId Id { get; }
|
||||||
public string Hid { get; set; }
|
public string Hid { get; set; }
|
||||||
public int System { get; set; }
|
public SystemId System { get; set; }
|
||||||
public string Color { get; set; }
|
public string Color { get; set; }
|
||||||
public string AvatarUrl { get; set; }
|
public string AvatarUrl { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
namespace PluralKit.Core {
|
namespace PluralKit.Core {
|
||||||
public class PKSwitch
|
public class PKSwitch
|
||||||
{
|
{
|
||||||
public int Id { get; }
|
public SwitchId Id { get; }
|
||||||
public int System { get; set; }
|
public SystemId System { get; set; }
|
||||||
public Instant Timestamp { get; }
|
public Instant Timestamp { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,7 +8,7 @@ namespace PluralKit.Core {
|
|||||||
public class PKSystem
|
public class PKSystem
|
||||||
{
|
{
|
||||||
// Additions here should be mirrored in SystemStore::Save
|
// Additions here should be mirrored in SystemStore::Save
|
||||||
[Key] public int Id { get; }
|
[Key] public SystemId Id { get; }
|
||||||
public string Hid { get; }
|
public string Hid { get; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
24
PluralKit.Core/Models/SwitchId.cs
Normal file
24
PluralKit.Core/Models/SwitchId.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
namespace PluralKit.Core
|
||||||
|
{
|
||||||
|
public readonly struct SwitchId: INumericId<SwitchId, int>
|
||||||
|
{
|
||||||
|
public int Value { get; }
|
||||||
|
|
||||||
|
public SwitchId(int value)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(SwitchId other) => Value == other.Value;
|
||||||
|
|
||||||
|
public override bool Equals(object obj) => obj is SwitchId other && Equals(other);
|
||||||
|
|
||||||
|
public override int GetHashCode() => Value;
|
||||||
|
|
||||||
|
public static bool operator ==(SwitchId left, SwitchId right) => left.Equals(right);
|
||||||
|
|
||||||
|
public static bool operator !=(SwitchId left, SwitchId right) => !left.Equals(right);
|
||||||
|
|
||||||
|
public int CompareTo(SwitchId other) => Value.CompareTo(other.Value);
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,10 @@
|
|||||||
{
|
{
|
||||||
public class SystemGuildSettings
|
public class SystemGuildSettings
|
||||||
{
|
{
|
||||||
public ulong Guild { get; }
|
public SystemId Guild { get; }
|
||||||
public bool ProxyEnabled { get; } = true;
|
public bool ProxyEnabled { get; } = true;
|
||||||
|
|
||||||
public AutoproxyMode AutoproxyMode { get; } = AutoproxyMode.Off;
|
public AutoproxyMode AutoproxyMode { get; } = AutoproxyMode.Off;
|
||||||
public int? AutoproxyMember { get; }
|
public MemberId? AutoproxyMember { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
24
PluralKit.Core/Models/SystemId.cs
Normal file
24
PluralKit.Core/Models/SystemId.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
namespace PluralKit.Core
|
||||||
|
{
|
||||||
|
public readonly struct SystemId: INumericId<SystemId, int>
|
||||||
|
{
|
||||||
|
public int Value { get; }
|
||||||
|
|
||||||
|
public SystemId(int value)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(SystemId other) => Value == other.Value;
|
||||||
|
|
||||||
|
public override bool Equals(object obj) => obj is SystemId other && Equals(other);
|
||||||
|
|
||||||
|
public override int GetHashCode() => Value;
|
||||||
|
|
||||||
|
public static bool operator ==(SystemId left, SystemId right) => left.Equals(right);
|
||||||
|
|
||||||
|
public static bool operator !=(SystemId left, SystemId right) => !left.Equals(right);
|
||||||
|
|
||||||
|
public int CompareTo(SystemId other) => Value.CompareTo(other.Value);
|
||||||
|
}
|
||||||
|
}
|
@ -45,7 +45,7 @@ namespace PluralKit.Core {
|
|||||||
|
|
||||||
public struct SwitchMembersListEntry
|
public struct SwitchMembersListEntry
|
||||||
{
|
{
|
||||||
public int Member;
|
public MemberId Member;
|
||||||
public Instant Timestamp;
|
public Instant Timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ namespace PluralKit.Core {
|
|||||||
/// Gets a system by its internal member ID.
|
/// Gets a system by its internal member ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The <see cref="PKMember"/> with the given internal ID, or null if no member was found.</returns>
|
/// <returns>The <see cref="PKMember"/> with the given internal ID, or null if no member was found.</returns>
|
||||||
Task<PKMember> GetMemberById(int memberId);
|
Task<PKMember> GetMemberById(MemberId memberId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a member by its user-facing human ID.
|
/// Gets a member by its user-facing human ID.
|
||||||
@ -195,7 +195,7 @@ namespace PluralKit.Core {
|
|||||||
/// <param name="triggerMessageId">The ID of the original trigger message containing the proxy tags.</param>
|
/// <param name="triggerMessageId">The ID of the original trigger message containing the proxy tags.</param>
|
||||||
/// <param name="proxiedMemberId">The member (and by extension system) that was proxied.</param>
|
/// <param name="proxiedMemberId">The member (and by extension system) that was proxied.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task AddMessage(IPKConnection conn, ulong senderAccount, ulong guildId, ulong channelId, ulong postedMessageId, ulong triggerMessageId, int proxiedMemberId);
|
Task AddMessage(IPKConnection conn, ulong senderAccount, ulong guildId, ulong channelId, ulong postedMessageId, ulong triggerMessageId, MemberId proxiedMemberId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes a message from the data store.
|
/// Deletes a message from the data store.
|
||||||
|
@ -125,7 +125,7 @@ namespace PluralKit.Core {
|
|||||||
return member;
|
return member;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PKMember> GetMemberById(int id) {
|
public async Task<PKMember> GetMemberById(MemberId id) {
|
||||||
using (var conn = await _conn.Obtain())
|
using (var conn = await _conn.Obtain())
|
||||||
return await conn.QuerySingleOrDefaultAsync<PKMember>("select * from members where id = @Id", new { Id = id });
|
return await conn.QuerySingleOrDefaultAsync<PKMember>("select * from members where id = @Id", new { Id = id });
|
||||||
}
|
}
|
||||||
@ -177,7 +177,7 @@ namespace PluralKit.Core {
|
|||||||
return await conn.ExecuteScalarAsync<ulong>("select count(id) from members");
|
return await conn.ExecuteScalarAsync<ulong>("select count(id) from members");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddMessage(IPKConnection conn, ulong senderId, ulong guildId, ulong channelId, ulong postedMessageId, ulong triggerMessageId, int proxiedMemberId) {
|
public async Task AddMessage(IPKConnection conn, ulong senderId, ulong guildId, ulong channelId, ulong postedMessageId, ulong triggerMessageId, MemberId proxiedMemberId) {
|
||||||
// "on conflict do nothing" in the (pretty rare) case of duplicate events coming in from Discord, which would lead to a DB error before
|
// "on conflict do nothing" in the (pretty rare) case of duplicate events coming in from Discord, which would lead to a DB error before
|
||||||
await conn.ExecuteAsync("insert into messages(mid, guild, channel, member, sender, original_mid) values(@MessageId, @GuildId, @ChannelId, @MemberId, @SenderId, @OriginalMid) on conflict do nothing", new {
|
await conn.ExecuteAsync("insert into messages(mid, guild, channel, member, sender, original_mid) values(@MessageId, @GuildId, @ChannelId, @MemberId, @SenderId, @OriginalMid) on conflict do nothing", new {
|
||||||
MessageId = postedMessageId,
|
MessageId = postedMessageId,
|
||||||
@ -334,7 +334,7 @@ namespace PluralKit.Core {
|
|||||||
// query DB for all members involved in any of the switches above and collect into a dictionary for future use
|
// query DB for all members involved in any of the switches above and collect into a dictionary for future use
|
||||||
// this makes sure the return list has the same instances of PKMember throughout, which is important for the dictionary
|
// this makes sure the return list has the same instances of PKMember throughout, which is important for the dictionary
|
||||||
// key used in GetPerMemberSwitchDuration below
|
// key used in GetPerMemberSwitchDuration below
|
||||||
Dictionary<int, PKMember> memberObjects;
|
Dictionary<MemberId, PKMember> memberObjects;
|
||||||
using (var conn = await _conn.Obtain())
|
using (var conn = await _conn.Obtain())
|
||||||
{
|
{
|
||||||
memberObjects = (
|
memberObjects = (
|
||||||
@ -351,7 +351,7 @@ namespace PluralKit.Core {
|
|||||||
select new SwitchListEntry
|
select new SwitchListEntry
|
||||||
{
|
{
|
||||||
TimespanStart = g.Key,
|
TimespanStart = g.Key,
|
||||||
Members = g.Where(x => x.Member != 0).Select(x => memberObjects[x.Member]).ToList()
|
Members = g.Where(x => x.Member != default(MemberId)).Select(x => memberObjects[x.Member]).ToList()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Loop through every switch that overlaps the range and add it to the output list
|
// Loop through every switch that overlaps the range and add it to the output list
|
||||||
|
@ -16,14 +16,14 @@ namespace PluralKit.Core
|
|||||||
{
|
{
|
||||||
public class BulkImporter: IAsyncDisposable
|
public class BulkImporter: IAsyncDisposable
|
||||||
{
|
{
|
||||||
private readonly int _systemId;
|
private readonly SystemId _systemId;
|
||||||
private readonly IPKConnection _conn;
|
private readonly IPKConnection _conn;
|
||||||
private readonly IPKTransaction _tx;
|
private readonly IPKTransaction _tx;
|
||||||
private readonly Dictionary<string, int> _knownMembers = new Dictionary<string, int>();
|
private readonly Dictionary<string, MemberId> _knownMembers = new Dictionary<string, MemberId>();
|
||||||
private readonly Dictionary<string, PKMember> _existingMembersByHid = new Dictionary<string, PKMember>();
|
private readonly Dictionary<string, PKMember> _existingMembersByHid = new Dictionary<string, PKMember>();
|
||||||
private readonly Dictionary<string, PKMember> _existingMembersByName = new Dictionary<string, PKMember>();
|
private readonly Dictionary<string, PKMember> _existingMembersByName = new Dictionary<string, PKMember>();
|
||||||
|
|
||||||
private BulkImporter(int systemId, IPKConnection conn, IPKTransaction tx)
|
private BulkImporter(SystemId systemId, IPKConnection conn, IPKTransaction tx)
|
||||||
{
|
{
|
||||||
_systemId = systemId;
|
_systemId = systemId;
|
||||||
_conn = conn;
|
_conn = conn;
|
||||||
@ -124,7 +124,7 @@ namespace PluralKit.Core
|
|||||||
// Fetch the existing switches in the database so we can avoid duplicates
|
// Fetch the existing switches in the database so we can avoid duplicates
|
||||||
var existingSwitches = (await _conn.QueryAsync<PKSwitch>("select * from switches where system = @System", new {System = _systemId})).ToList();
|
var existingSwitches = (await _conn.QueryAsync<PKSwitch>("select * from switches where system = @System", new {System = _systemId})).ToList();
|
||||||
var existingTimestamps = existingSwitches.Select(sw => sw.Timestamp).ToImmutableHashSet();
|
var existingTimestamps = existingSwitches.Select(sw => sw.Timestamp).ToImmutableHashSet();
|
||||||
var lastSwitchId = existingSwitches.Count != 0 ? existingSwitches.Select(sw => sw.Id).Max() : -1;
|
var lastSwitchId = existingSwitches.Count != 0 ? existingSwitches.Select(sw => sw.Id).Max() : (SwitchId?) null;
|
||||||
|
|
||||||
// Import switch definitions
|
// Import switch definitions
|
||||||
var importedSwitches = new Dictionary<Instant, SwitchInfo>();
|
var importedSwitches = new Dictionary<Instant, SwitchInfo>();
|
||||||
@ -152,7 +152,7 @@ namespace PluralKit.Core
|
|||||||
// IDs are sequential, so any ID in this system, with a switch ID > the last max, will be one we just added
|
// IDs are sequential, so any ID in this system, with a switch ID > the last max, will be one we just added
|
||||||
var justAddedSwitches = await _conn.QueryAsync<PKSwitch>(
|
var justAddedSwitches = await _conn.QueryAsync<PKSwitch>(
|
||||||
"select * from switches where system = @System and id > @LastSwitchId",
|
"select * from switches where system = @System and id > @LastSwitchId",
|
||||||
new {System = _systemId, LastSwitchId = lastSwitchId});
|
new {System = _systemId, LastSwitchId = lastSwitchId?.Value ?? -1});
|
||||||
|
|
||||||
// Lastly, import the switch members
|
// Lastly, import the switch members
|
||||||
await using (var importer = _conn.BeginBinaryImport("copy switch_members (switch, member) from stdin (format binary)"))
|
await using (var importer = _conn.BeginBinaryImport("copy switch_members (switch, member) from stdin (format binary)"))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user