mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-04 13:06:50 +00:00
run dotnet format
This commit is contained in:
parent
05989242f9
commit
ac2671452d
278 changed files with 1913 additions and 1808 deletions
|
|
@ -12,15 +12,15 @@ namespace PluralKit.Core
|
|||
public static GaugeOptions MessageCount => new GaugeOptions { Name = "Messages", MeasurementUnit = Unit.Items };
|
||||
public static GaugeOptions SwitchCount => new GaugeOptions { Name = "Switches", MeasurementUnit = Unit.Items };
|
||||
public static GaugeOptions GroupCount => new GaugeOptions { Name = "Groups", MeasurementUnit = Unit.Items };
|
||||
|
||||
|
||||
public static GaugeOptions ProcessPhysicalMemory => new GaugeOptions { Name = "Process Physical Memory", MeasurementUnit = Unit.Bytes, Context = "Process" };
|
||||
public static GaugeOptions ProcessVirtualMemory => new GaugeOptions { Name = "Process Virtual Memory", MeasurementUnit = Unit.Bytes, Context = "Process" };
|
||||
public static GaugeOptions ProcessPrivateMemory => new GaugeOptions { Name = "Process Private Memory", MeasurementUnit = Unit.Bytes, Context = "Process" };
|
||||
public static GaugeOptions ProcessThreads => new GaugeOptions { Name = "Process Thread Count", MeasurementUnit = Unit.Threads, Context = "Process" };
|
||||
public static GaugeOptions ProcessHandles => new GaugeOptions { Name = "Process Handle Count", MeasurementUnit = Unit.Items, Context = "Process" };
|
||||
public static GaugeOptions CpuUsage => new GaugeOptions { Name = "CPU Usage", MeasurementUnit = Unit.Percent, Context = "Process" };
|
||||
|
||||
public static MeterOptions DatabaseRequests => new MeterOptions() { Name = "Database Requests", MeasurementUnit = Unit.Requests, Context = "Database", RateUnit = TimeUnit.Seconds};
|
||||
|
||||
public static MeterOptions DatabaseRequests => new MeterOptions() { Name = "Database Requests", MeasurementUnit = Unit.Requests, Context = "Database", RateUnit = TimeUnit.Seconds };
|
||||
public static TimerOptions DatabaseQuery => new TimerOptions() { Name = "Database Query", MeasurementUnit = Unit.Requests, DurationUnit = TimeUnit.Seconds, RateUnit = TimeUnit.Seconds, Context = "Database" };
|
||||
public static GaugeOptions DatabaseConnections => new GaugeOptions() { Name = "Database Connections", MeasurementUnit = Unit.Connections, Context = "Database" };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
|
|
@ -18,7 +18,7 @@ namespace PluralKit.Core
|
|||
{
|
||||
internal class Database: IDatabase
|
||||
{
|
||||
|
||||
|
||||
private readonly CoreConfig _config;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IMetrics _metrics;
|
||||
|
|
@ -34,20 +34,22 @@ namespace PluralKit.Core
|
|||
_metrics = metrics;
|
||||
_migrator = migrator;
|
||||
_logger = logger.ForContext<Database>();
|
||||
|
||||
|
||||
_connectionString = new NpgsqlConnectionStringBuilder(_config.Database)
|
||||
{
|
||||
Pooling = true, Enlist = false, NoResetOnClose = true,
|
||||
|
||||
Pooling = true,
|
||||
Enlist = false,
|
||||
NoResetOnClose = true,
|
||||
|
||||
// Lower timeout than default (15s -> 2s), should ideally fail-fast instead of hanging
|
||||
Timeout = 2
|
||||
}.ConnectionString;
|
||||
}
|
||||
|
||||
|
||||
public static void InitStatic()
|
||||
{
|
||||
DefaultTypeMap.MatchNamesWithUnderscores = true;
|
||||
|
||||
|
||||
// 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.
|
||||
|
|
@ -61,7 +63,7 @@ namespace PluralKit.Core
|
|||
// 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 ID types to Dapper
|
||||
SqlMapper.AddTypeHandler(new NumericIdHandler<SystemId, int>(i => new SystemId(i)));
|
||||
SqlMapper.AddTypeHandler(new NumericIdHandler<MemberId, int>(i => new MemberId(i)));
|
||||
|
|
@ -71,25 +73,25 @@ namespace PluralKit.Core
|
|||
SqlMapper.AddTypeHandler(new NumericIdArrayHandler<MemberId, int>(i => new MemberId(i)));
|
||||
SqlMapper.AddTypeHandler(new NumericIdArrayHandler<SwitchId, int>(i => new SwitchId(i)));
|
||||
SqlMapper.AddTypeHandler(new NumericIdArrayHandler<GroupId, int>(i => new GroupId(i)));
|
||||
|
||||
|
||||
// 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()
|
||||
{
|
||||
// Mark the request (for a handle, I guess) in the metrics
|
||||
_metrics.Measure.Meter.Mark(CoreMetrics.DatabaseRequests);
|
||||
|
||||
|
||||
// Create a connection and open it
|
||||
// We wrap it in PKConnection for tracing purposes
|
||||
var conn = new PKConnection(new NpgsqlConnection(_connectionString), _countHolder, _logger, _metrics);
|
||||
await conn.OpenAsync();
|
||||
return conn;
|
||||
}
|
||||
|
||||
|
||||
public async Task ApplyMigrations()
|
||||
{
|
||||
using var conn = await Obtain();
|
||||
|
|
@ -99,28 +101,28 @@ namespace PluralKit.Core
|
|||
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;
|
||||
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;
|
||||
(ulong)(long)value;
|
||||
|
||||
public override void SetValue(IDbDataParameter parameter, ulong value) => parameter.Value = (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 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);
|
||||
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>
|
||||
where T : INumericId<T, TInner>
|
||||
where TInner : IEquatable<TInner>, IComparable<TInner>
|
||||
{
|
||||
private readonly Func<TInner, T> _factory;
|
||||
|
||||
|
|
@ -131,12 +133,12 @@ namespace PluralKit.Core
|
|||
|
||||
public override void SetValue(IDbDataParameter parameter, T value) => parameter.Value = value.Value;
|
||||
|
||||
public override T Parse(object value) => _factory((TInner) 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>
|
||||
where T : INumericId<T, TInner>
|
||||
where TInner : IEquatable<TInner>, IComparable<TInner>
|
||||
{
|
||||
private readonly Func<TInner, T> _factory;
|
||||
|
||||
|
|
@ -147,10 +149,10 @@ namespace PluralKit.Core
|
|||
|
||||
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));
|
||||
public override T[] Parse(object value) => Array.ConvertAll((TInner[])value, v => _factory(v));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class DatabaseExt
|
||||
{
|
||||
public static async Task Execute(this IDatabase db, Func<IPKConnection, Task> func)
|
||||
|
|
@ -164,11 +166,11 @@ namespace PluralKit.Core
|
|||
await using var conn = await db.Obtain();
|
||||
return await func(conn);
|
||||
}
|
||||
|
||||
|
||||
public static async IAsyncEnumerable<T> Execute<T>(this IDatabase db, Func<IPKConnection, IAsyncEnumerable<T>> func)
|
||||
{
|
||||
await using var conn = await db.Obtain();
|
||||
|
||||
|
||||
await foreach (var val in func(conn))
|
||||
yield return val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
|
||||
using NodaTime;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PluralKit.Core
|
||||
|
|
@ -11,11 +11,11 @@ namespace PluralKit.Core
|
|||
public MemberId Id { get; }
|
||||
public IReadOnlyCollection<ProxyTag> ProxyTags { get; } = new ProxyTag[0];
|
||||
public bool KeepProxy { get; }
|
||||
|
||||
|
||||
public string? ServerName { get; }
|
||||
public string? DisplayName { get; }
|
||||
public string Name { get; } = "";
|
||||
|
||||
|
||||
public string? ServerAvatar { get; }
|
||||
public string? Avatar { get; }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,23 +5,23 @@ using Dapper;
|
|||
namespace PluralKit.Core
|
||||
{
|
||||
public partial class ModelRepository
|
||||
{
|
||||
public Task SaveCommandMessage(IPKConnection conn, ulong messageId, ulong authorId) =>
|
||||
conn.QueryAsync("insert into command_messages (message_id, author_id) values (@Message, @Author)",
|
||||
new {Message = messageId, Author = authorId });
|
||||
{
|
||||
public Task SaveCommandMessage(IPKConnection conn, ulong messageId, ulong authorId) =>
|
||||
conn.QueryAsync("insert into command_messages (message_id, author_id) values (@Message, @Author)",
|
||||
new { Message = messageId, Author = authorId });
|
||||
|
||||
public Task<CommandMessage> GetCommandMessage(IPKConnection conn, ulong messageId) =>
|
||||
conn.QuerySingleOrDefaultAsync<CommandMessage>("select * from command_messages where message_id = @Message",
|
||||
new {Message = messageId});
|
||||
public Task<CommandMessage> GetCommandMessage(IPKConnection conn, ulong messageId) =>
|
||||
conn.QuerySingleOrDefaultAsync<CommandMessage>("select * from command_messages where message_id = @Message",
|
||||
new { Message = messageId });
|
||||
|
||||
public Task<int> DeleteCommandMessagesBefore(IPKConnection conn, ulong messageIdThreshold) =>
|
||||
conn.ExecuteAsync("delete from command_messages where message_id < @Threshold",
|
||||
new {Threshold = messageIdThreshold});
|
||||
new { Threshold = messageIdThreshold });
|
||||
}
|
||||
|
||||
public class CommandMessage
|
||||
{
|
||||
public class CommandMessage
|
||||
{
|
||||
public ulong AuthorId { get; set; }
|
||||
public ulong MessageId { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
|
@ -10,16 +10,16 @@ namespace PluralKit.Core
|
|||
{
|
||||
public Task<MessageContext> GetMessageContext(IPKConnection conn, ulong account, ulong guild, ulong channel)
|
||||
{
|
||||
return conn.QueryFirstAsync<MessageContext>("message_context",
|
||||
new { account_id = account, guild_id = guild, channel_id = channel },
|
||||
return conn.QueryFirstAsync<MessageContext>("message_context",
|
||||
new { account_id = account, guild_id = guild, channel_id = channel },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Task<IEnumerable<ProxyMember>> GetProxyMembers(IPKConnection conn, ulong account, ulong guild)
|
||||
{
|
||||
return conn.QueryAsync<ProxyMember>("proxy_members",
|
||||
new { account_id = account, guild_id = guild },
|
||||
return conn.QueryAsync<ProxyMember>("proxy_members",
|
||||
new { account_id = account, guild_id = guild },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
|
|
@ -12,14 +12,14 @@ namespace PluralKit.Core
|
|||
public partial class ModelRepository
|
||||
{
|
||||
public Task<PKGroup?> GetGroupByName(IPKConnection conn, SystemId system, string name) =>
|
||||
conn.QueryFirstOrDefaultAsync<PKGroup?>("select * from groups where system = @System and lower(Name) = lower(@Name)", new {System = system, Name = name});
|
||||
|
||||
conn.QueryFirstOrDefaultAsync<PKGroup?>("select * from groups where system = @System and lower(Name) = lower(@Name)", new { System = system, Name = name });
|
||||
|
||||
public Task<PKGroup?> GetGroupByDisplayName(IPKConnection conn, SystemId system, string display_name) =>
|
||||
conn.QueryFirstOrDefaultAsync<PKGroup?>("select * from groups where system = @System and lower(display_name) = lower(@Name)", new {System = system, Name = display_name});
|
||||
|
||||
conn.QueryFirstOrDefaultAsync<PKGroup?>("select * from groups where system = @System and lower(display_name) = lower(@Name)", new { System = system, Name = display_name });
|
||||
|
||||
public Task<PKGroup?> GetGroupByHid(IPKConnection conn, string hid) =>
|
||||
conn.QueryFirstOrDefaultAsync<PKGroup?>("select * from groups where hid = @hid", new {hid = hid.ToLowerInvariant()});
|
||||
|
||||
conn.QueryFirstOrDefaultAsync<PKGroup?>("select * from groups where hid = @hid", new { hid = hid.ToLowerInvariant() });
|
||||
|
||||
public Task<int> GetGroupMemberCount(IPKConnection conn, GroupId id, PrivacyLevel? privacyFilter = null)
|
||||
{
|
||||
var query = new StringBuilder("select count(*) from group_members");
|
||||
|
|
@ -28,14 +28,14 @@ namespace PluralKit.Core
|
|||
query.Append(" where group_members.group_id = @Id");
|
||||
if (privacyFilter != null)
|
||||
query.Append(" and members.member_visibility = @PrivacyFilter");
|
||||
return conn.QuerySingleOrDefaultAsync<int>(query.ToString(), new {Id = id, PrivacyFilter = privacyFilter});
|
||||
return conn.QuerySingleOrDefaultAsync<int>(query.ToString(), new { Id = id, PrivacyFilter = privacyFilter });
|
||||
}
|
||||
|
||||
|
||||
public async Task<PKGroup> CreateGroup(IPKConnection conn, SystemId system, string name, IDbTransaction? transaction = null)
|
||||
{
|
||||
var group = await conn.QueryFirstAsync<PKGroup>(
|
||||
"insert into groups (hid, system, name) values (find_free_group_hid(), @System, @Name) returning *",
|
||||
new {System = system, Name = name}, transaction);
|
||||
new { System = system, Name = name }, transaction);
|
||||
_logger.Information("Created group {GroupId} in system {SystemId}: {GroupName}", group.Id, system, name);
|
||||
return group;
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ namespace PluralKit.Core
|
|||
public Task DeleteGroup(IPKConnection conn, GroupId group)
|
||||
{
|
||||
_logger.Information("Deleted {GroupId}", group);
|
||||
return conn.ExecuteAsync("delete from groups where id = @Id", new {Id = @group});
|
||||
return conn.ExecuteAsync("delete from groups where id = @Id", new { Id = @group });
|
||||
}
|
||||
|
||||
public async Task AddMembersToGroup(IPKConnection conn, GroupId group,
|
||||
|
|
@ -76,7 +76,7 @@ namespace PluralKit.Core
|
|||
{
|
||||
_logger.Information("Removed members from {GroupId}: {MemberIds}", group, members);
|
||||
return conn.ExecuteAsync("delete from group_members where group_id = @Group and member_id = any(@Members)",
|
||||
new {Group = @group, Members = members.ToArray()});
|
||||
new { Group = @group, Members = members.ToArray() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,12 +7,12 @@ using Dapper;
|
|||
namespace PluralKit.Core
|
||||
{
|
||||
public partial class ModelRepository
|
||||
{
|
||||
{
|
||||
public IAsyncEnumerable<PKGroup> GetMemberGroups(IPKConnection conn, MemberId id) =>
|
||||
conn.QueryStreamAsync<PKGroup>(
|
||||
"select groups.* from group_members inner join groups on group_members.group_id = groups.id where group_members.member_id = @Id",
|
||||
new {Id = id});
|
||||
|
||||
new { Id = id });
|
||||
|
||||
|
||||
public async Task AddGroupsToMember(IPKConnection conn, MemberId member, IReadOnlyCollection<GroupId> groups)
|
||||
{
|
||||
|
|
@ -33,7 +33,7 @@ namespace PluralKit.Core
|
|||
{
|
||||
_logger.Information("Removed groups from {MemberId}: {GroupIds}", member, groups);
|
||||
return conn.ExecuteAsync("delete from group_members where member_id = @Member and group_id = any(@Groups)",
|
||||
new {Member = @member, Groups = groups.ToArray() });
|
||||
new { Member = @member, Groups = groups.ToArray() });
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dapper;
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ namespace PluralKit.Core
|
|||
.Build();
|
||||
return conn.ExecuteAsync(query, pms);
|
||||
}
|
||||
|
||||
|
||||
public Task UpsertSystemGuild(IPKConnection conn, SystemId system, ulong guild,
|
||||
SystemGuildPatch patch)
|
||||
{
|
||||
|
|
@ -36,18 +36,18 @@ namespace PluralKit.Core
|
|||
.Build();
|
||||
return conn.ExecuteAsync(query, pms);
|
||||
}
|
||||
|
||||
|
||||
public Task<GuildConfig> GetGuild(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 Task<SystemGuildSettings> GetSystemGuild(IPKConnection conn, ulong guild, SystemId system) =>
|
||||
conn.QueryFirstAsync<SystemGuildSettings>(
|
||||
"insert into system_guild (guild, system) values (@guild, @system) on conflict (guild, system) do update set guild = @guild, system = @system returning *",
|
||||
new {guild, system});
|
||||
"insert into system_guild (guild, system) values (@guild, @system) on conflict (guild, system) do update set guild = @guild, system = @system returning *",
|
||||
new { guild, system });
|
||||
|
||||
public Task<MemberGuildSettings> GetMemberGuild(IPKConnection conn, ulong guild, MemberId member) =>
|
||||
conn.QueryFirstAsync<MemberGuildSettings>(
|
||||
"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 });
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
using System.Data;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
|
@ -9,22 +9,22 @@ namespace PluralKit.Core
|
|||
public partial class ModelRepository
|
||||
{
|
||||
public Task<PKMember?> GetMember(IPKConnection conn, MemberId id) =>
|
||||
conn.QueryFirstOrDefaultAsync<PKMember?>("select * from members where id = @id", new {id});
|
||||
|
||||
public Task<PKMember?> GetMemberByHid(IPKConnection conn, string hid) =>
|
||||
conn.QueryFirstOrDefaultAsync<PKMember?>("select * from members where id = @id", new { id });
|
||||
|
||||
public Task<PKMember?> GetMemberByHid(IPKConnection conn, string hid) =>
|
||||
conn.QuerySingleOrDefaultAsync<PKMember?>("select * from members where hid = @Hid", new { Hid = hid.ToLower() });
|
||||
|
||||
public Task<PKMember?> GetMemberByName(IPKConnection conn, SystemId system, string name) =>
|
||||
public Task<PKMember?> GetMemberByName(IPKConnection conn, SystemId system, string name) =>
|
||||
conn.QueryFirstOrDefaultAsync<PKMember?>("select * from members where lower(name) = lower(@Name) and system = @SystemID", new { Name = name, SystemID = system });
|
||||
|
||||
public Task<PKMember?> GetMemberByDisplayName(IPKConnection conn, SystemId system, string name) =>
|
||||
public Task<PKMember?> GetMemberByDisplayName(IPKConnection conn, SystemId system, string name) =>
|
||||
conn.QueryFirstOrDefaultAsync<PKMember?>("select * from members where lower(display_name) = lower(@Name) and system = @SystemID", new { Name = name, SystemID = system });
|
||||
|
||||
public async Task<PKMember> CreateMember(IPKConnection conn, SystemId id, string memberName, IDbTransaction? transaction = null)
|
||||
{
|
||||
var member = await conn.QueryFirstAsync<PKMember>(
|
||||
"insert into members (hid, system, name) values (find_free_member_hid(), @SystemId, @Name) returning *",
|
||||
new {SystemId = id, Name = memberName}, transaction);
|
||||
new { SystemId = id, Name = memberName }, transaction);
|
||||
_logger.Information("Created {MemberId} in {SystemId}: {MemberName}",
|
||||
member.Id, id, memberName);
|
||||
return member;
|
||||
|
|
@ -42,7 +42,7 @@ namespace PluralKit.Core
|
|||
public Task DeleteMember(IPKConnection conn, MemberId id)
|
||||
{
|
||||
_logger.Information("Deleted {MemberId}", id);
|
||||
return conn.ExecuteAsync("delete from members where id = @Id", new {Id = id});
|
||||
return conn.ExecuteAsync("delete from members where id = @Id", new { Id = id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
|
@ -8,26 +8,27 @@ namespace PluralKit.Core
|
|||
{
|
||||
public partial class ModelRepository
|
||||
{
|
||||
public async Task AddMessage(IPKConnection conn, PKMessage msg) {
|
||||
public async Task AddMessage(IPKConnection conn, PKMessage msg)
|
||||
{
|
||||
// "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(@Mid, @Guild, @Channel, @Member, @Sender, @OriginalMid) on conflict do nothing", msg);
|
||||
_logger.Debug("Stored message {@StoredMessage} in channel {Channel}", msg, msg.Channel);
|
||||
}
|
||||
|
||||
|
||||
public async Task<FullMessage?> GetMessage(IPKConnection conn, ulong id)
|
||||
{
|
||||
FullMessage Mapper(PKMessage msg, PKMember member, PKSystem system) =>
|
||||
new FullMessage {Message = msg, System = system, Member = member};
|
||||
new FullMessage { Message = msg, System = system, Member = member };
|
||||
|
||||
var result = await conn.QueryAsync<PKMessage, PKMember, PKSystem, FullMessage>(
|
||||
"select messages.*, members.*, systems.* from messages, members, systems where (mid = @Id or original_mid = @Id) and messages.member = members.id and systems.id = members.system",
|
||||
Mapper, new {Id = id});
|
||||
Mapper, new { Id = id });
|
||||
return result.FirstOrDefault();
|
||||
}
|
||||
|
||||
public async Task DeleteMessage(IPKConnection conn, ulong id)
|
||||
{
|
||||
var rowCount = await conn.ExecuteAsync("delete from messages where mid = @Id", new {Id = id});
|
||||
var rowCount = await conn.ExecuteAsync("delete from messages where mid = @Id", new { Id = id });
|
||||
if (rowCount > 0)
|
||||
_logger.Information("Deleted message {MessageId} from database", id);
|
||||
}
|
||||
|
|
@ -37,7 +38,7 @@ namespace PluralKit.Core
|
|||
// Npgsql doesn't support ulongs in general - we hacked around it for plain ulongs but tbh not worth it for collections of ulong
|
||||
// Hence we map them to single longs, which *are* supported (this is ok since they're Technically (tm) stored as signed longs in the db anyway)
|
||||
var rowCount = await conn.ExecuteAsync("delete from messages where mid = any(@Ids)",
|
||||
new {Ids = ids.Select(id => (long) id).ToArray()});
|
||||
new { Ids = ids.Select(id => (long)id).ToArray() });
|
||||
if (rowCount > 0)
|
||||
_logger.Information("Bulk deleted messages ({FoundCount} found) from database: {MessageIds}", rowCount,
|
||||
ids);
|
||||
|
|
@ -65,7 +66,7 @@ namespace PluralKit.Core
|
|||
public ulong Sender { get; set; }
|
||||
public ulong? OriginalMid { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class FullMessage
|
||||
{
|
||||
public PKMessage Message;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dapper;
|
||||
|
|
@ -15,16 +15,16 @@ namespace PluralKit.Core
|
|||
public Task SetShardStatus(IPKConnection conn, int shard, PKShardInfo.ShardStatus status) =>
|
||||
conn.ExecuteAsync(
|
||||
"insert into shards (id, status) values (@Id, @Status) on conflict (id) do update set status = @Status",
|
||||
new {Id = shard, Status = status});
|
||||
new { Id = shard, Status = status });
|
||||
|
||||
public Task RegisterShardHeartbeat(IPKConnection conn, int shard, Duration ping) =>
|
||||
conn.ExecuteAsync(
|
||||
"insert into shards (id, last_heartbeat, ping) values (@Id, now(), @Ping) on conflict (id) do update set last_heartbeat = now(), ping = @Ping",
|
||||
new {Id = shard, Ping = ping.TotalSeconds});
|
||||
new { Id = shard, Ping = ping.TotalSeconds });
|
||||
|
||||
public Task RegisterShardConnection(IPKConnection conn, int shard) =>
|
||||
conn.ExecuteAsync(
|
||||
"insert into shards (id, last_connection) values (@Id, now()) on conflict (id) do update set last_connection = now()",
|
||||
new {Id = shard});
|
||||
new { Id = shard });
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ namespace PluralKit.Core
|
|||
|
||||
// First, we insert the switch itself
|
||||
var sw = await conn.QuerySingleAsync<PKSwitch>("insert into switches(system) values (@System) returning *",
|
||||
new {System = system});
|
||||
new { System = system });
|
||||
|
||||
// Then we insert each member in the switch in the switch_members table
|
||||
await using (var w = conn.BeginBinaryImport("copy switch_members (switch, member) from stdin (format binary)"))
|
||||
|
|
@ -43,20 +43,20 @@ namespace PluralKit.Core
|
|||
public async Task MoveSwitch(IPKConnection conn, SwitchId id, Instant time)
|
||||
{
|
||||
await conn.ExecuteAsync("update switches set timestamp = @Time where id = @Id",
|
||||
new {Time = time, Id = id});
|
||||
new { Time = time, Id = id });
|
||||
|
||||
_logger.Information("Updated {SwitchId} timestamp: {SwitchTimestamp}", id, time);
|
||||
}
|
||||
|
||||
public async Task DeleteSwitch(IPKConnection conn, SwitchId id)
|
||||
{
|
||||
await conn.ExecuteAsync("delete from switches where id = @Id", new {Id = id});
|
||||
await conn.ExecuteAsync("delete from switches where id = @Id", new { Id = id });
|
||||
_logger.Information("Deleted {Switch}", id);
|
||||
}
|
||||
|
||||
public async Task DeleteAllSwitches(IPKConnection conn, SystemId system)
|
||||
{
|
||||
await conn.ExecuteAsync("delete from switches where system = @Id", new {Id = system});
|
||||
await conn.ExecuteAsync("delete from switches where system = @Id", new { Id = system });
|
||||
_logger.Information("Deleted all switches in {SystemId}", system);
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ namespace PluralKit.Core
|
|||
// TODO: refactor the PKSwitch data structure to somehow include a hydrated member list
|
||||
return conn.QueryStreamAsync<PKSwitch>(
|
||||
"select * from switches where system = @System order by timestamp desc",
|
||||
new {System = system});
|
||||
new { System = system });
|
||||
}
|
||||
|
||||
public async Task<int> GetSwitchCount(IPKConnection conn, SystemId system)
|
||||
|
|
@ -86,7 +86,7 @@ namespace PluralKit.Core
|
|||
FROM switches
|
||||
WHERE switches.system = @System
|
||||
AND switches.timestamp < @Start",
|
||||
new {System = system, Start = start});
|
||||
new { System = system, Start = start });
|
||||
|
||||
// Then collect the time and members of all switches that overlap the range
|
||||
var switchMembersEntries = conn.QueryStreamAsync<SwitchMembersListEntry>(
|
||||
|
|
@ -101,7 +101,7 @@ namespace PluralKit.Core
|
|||
)
|
||||
AND switches.timestamp < @End
|
||||
ORDER BY switches.timestamp DESC",
|
||||
new {System = system, Start = start, End = end, LastSwitch = lastSwitch});
|
||||
new { System = system, Start = start, End = end, LastSwitch = lastSwitch });
|
||||
|
||||
// Yield each value here
|
||||
await foreach (var entry in switchMembersEntries)
|
||||
|
|
@ -114,7 +114,7 @@ namespace PluralKit.Core
|
|||
{
|
||||
return conn.QueryStreamAsync<PKMember>(
|
||||
"select * from switch_members, members where switch_members.member = members.id and switch_members.switch = @Switch order by switch_members.id",
|
||||
new {Switch = sw});
|
||||
new { Switch = sw });
|
||||
}
|
||||
|
||||
public async Task<PKSwitch> GetLatestSwitch(IPKConnection conn, SystemId system) =>
|
||||
|
|
@ -136,13 +136,13 @@ namespace PluralKit.Core
|
|||
// key used in GetPerMemberSwitchDuration below
|
||||
var membersList = await conn.QueryAsync<PKMember>(
|
||||
"select * from members where id = any(@Switches)", // lol postgres specific `= any()` syntax
|
||||
new {Switches = switchMembers.Select(m => m.Member.Value).Distinct().ToList()});
|
||||
new { Switches = switchMembers.Select(m => m.Member.Value).Distinct().ToList() });
|
||||
var memberObjects = membersList.ToDictionary(m => m.Id);
|
||||
|
||||
// check if a group ID is provided. if so, query DB for all members of said group, otherwise use membersList
|
||||
var groupMembersList = group != null ? await conn.QueryAsync<PKMember>(
|
||||
"select * from members inner join group_members on members.id = group_members.member_id where group_id = @id",
|
||||
new {id = group}) : membersList;
|
||||
new { id = group }) : membersList;
|
||||
var groupMemberObjects = groupMembersList.ToDictionary(m => m.Id);
|
||||
|
||||
// Initialize entries - still need to loop to determine the TimespanEnd below
|
||||
|
|
@ -154,7 +154,7 @@ namespace PluralKit.Core
|
|||
select new SwitchListEntry
|
||||
{
|
||||
TimespanStart = g.Key,
|
||||
Members = g.Where(x => x.Member != default(MemberId) && groupMemberObjects.Any(m => x.Member == m.Key) ).Select(x => memberObjects[x.Member])
|
||||
Members = g.Where(x => x.Member != default(MemberId) && groupMemberObjects.Any(m => x.Member == m.Key)).Select(x => memberObjects[x.Member])
|
||||
.ToList()
|
||||
};
|
||||
|
||||
|
|
@ -171,7 +171,9 @@ namespace PluralKit.Core
|
|||
|
||||
outList.Add(new SwitchListEntry
|
||||
{
|
||||
Members = e.Members, TimespanStart = switchStartClamped, TimespanEnd = endTime
|
||||
Members = e.Members,
|
||||
TimespanStart = switchStartClamped,
|
||||
TimespanEnd = endTime
|
||||
});
|
||||
|
||||
// next switch's end is this switch's start (we're working backward in time)
|
||||
|
|
@ -219,7 +221,7 @@ namespace PluralKit.Core
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct SwitchListEntry
|
||||
{
|
||||
public ICollection<PKMember> Members;
|
||||
|
|
@ -234,7 +236,7 @@ namespace PluralKit.Core
|
|||
public Instant RangeStart;
|
||||
public Instant RangeEnd;
|
||||
}
|
||||
|
||||
|
||||
public struct SwitchMembersListEntry
|
||||
{
|
||||
public MemberId Member;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -10,36 +10,36 @@ namespace PluralKit.Core
|
|||
public partial class ModelRepository
|
||||
{
|
||||
public Task<PKSystem?> GetSystem(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 Task<PKSystem?> GetSystemByAccount(IPKConnection conn, ulong accountId) =>
|
||||
conn.QuerySingleOrDefaultAsync<PKSystem?>(
|
||||
"select systems.* from systems, accounts where accounts.system = systems.id and accounts.uid = @Id",
|
||||
new {Id = accountId});
|
||||
new { Id = accountId });
|
||||
|
||||
public Task<PKSystem?> GetSystemByHid(IPKConnection conn, string hid) =>
|
||||
conn.QuerySingleOrDefaultAsync<PKSystem?>("select * from systems where systems.hid = @Hid",
|
||||
new {Hid = hid.ToLower()});
|
||||
new { Hid = hid.ToLower() });
|
||||
|
||||
public Task<IEnumerable<ulong>> GetSystemAccounts(IPKConnection conn, SystemId system) =>
|
||||
conn.QueryAsync<ulong>("select uid from accounts where system = @Id", new {Id = system});
|
||||
conn.QueryAsync<ulong>("select uid from accounts where system = @Id", new { Id = system });
|
||||
|
||||
public IAsyncEnumerable<PKMember> GetSystemMembers(IPKConnection conn, SystemId system) =>
|
||||
conn.QueryStreamAsync<PKMember>("select * from members where system = @SystemID", new {SystemID = system});
|
||||
conn.QueryStreamAsync<PKMember>("select * from members where system = @SystemID", new { SystemID = system });
|
||||
|
||||
public Task<int> GetSystemMemberCount(IPKConnection conn, SystemId id, PrivacyLevel? privacyFilter = null)
|
||||
{
|
||||
var query = new StringBuilder("select count(*) from members where system = @Id");
|
||||
if (privacyFilter != null)
|
||||
query.Append($" and member_visibility = {(int) privacyFilter.Value}");
|
||||
return conn.QuerySingleAsync<int>(query.ToString(), new {Id = id});
|
||||
query.Append($" and member_visibility = {(int)privacyFilter.Value}");
|
||||
return conn.QuerySingleAsync<int>(query.ToString(), new { Id = id });
|
||||
}
|
||||
|
||||
public async Task<PKSystem> CreateSystem(IPKConnection conn, string? systemName = null, IPKTransaction? tx = null)
|
||||
{
|
||||
var system = await conn.QuerySingleAsync<PKSystem>(
|
||||
"insert into systems (hid, name) values (find_free_system_hid(), @Name) returning *",
|
||||
new {Name = systemName},
|
||||
new { Name = systemName },
|
||||
transaction: tx);
|
||||
_logger.Information("Created {SystemId}", system.Id);
|
||||
return system;
|
||||
|
|
@ -59,21 +59,21 @@ namespace PluralKit.Core
|
|||
// We have "on conflict do nothing" since linking an account when it's already linked to the same system is idempotent
|
||||
// This is used in import/export, although the pk;link command checks for this case beforehand
|
||||
await conn.ExecuteAsync("insert into accounts (uid, system) values (@Id, @SystemId) on conflict do nothing",
|
||||
new {Id = accountId, SystemId = system});
|
||||
new { Id = accountId, SystemId = system });
|
||||
_logger.Information("Linked account {UserId} to {SystemId}", accountId, system);
|
||||
}
|
||||
|
||||
public async Task RemoveAccount(IPKConnection conn, SystemId system, ulong accountId)
|
||||
{
|
||||
await conn.ExecuteAsync("delete from accounts where uid = @Id and system = @SystemId",
|
||||
new {Id = accountId, SystemId = system});
|
||||
new { Id = accountId, SystemId = system });
|
||||
_logger.Information("Unlinked account {UserId} from {SystemId}", accountId, system);
|
||||
}
|
||||
|
||||
public Task DeleteSystem(IPKConnection conn, SystemId id)
|
||||
{
|
||||
_logger.Information("Deleted {SystemId}", id);
|
||||
return conn.ExecuteAsync("delete from systems where id = @Id", new {Id = id});
|
||||
return conn.ExecuteAsync("delete from systems where id = @Id", new { Id = id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using Serilog;
|
||||
using Serilog;
|
||||
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
|
||||
using Dapper;
|
||||
|
||||
namespace PluralKit.Core {
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public static class ConnectionUtils
|
||||
{
|
||||
public static async IAsyncEnumerable<T> QueryStreamAsync<T>(this IPKConnection conn, string sql, object param)
|
||||
{
|
||||
await using var reader = (DbDataReader) await conn.ExecuteReaderAsync(sql, param);
|
||||
await using var reader = (DbDataReader)await conn.ExecuteReaderAsync(sql, param);
|
||||
var parser = reader.GetRowParser<T>();
|
||||
|
||||
|
||||
while (await reader.ReadAsync())
|
||||
yield return parser(reader);
|
||||
yield return parser(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ namespace PluralKit.Core
|
|||
|
||||
// If the above creates new enum/composite types, we must tell Npgsql to reload the internal type caches
|
||||
// This will propagate to every other connection as well, since it marks the global type mapper collection dirty.
|
||||
((PKConnection) conn).ReloadTypes();
|
||||
((PKConnection)conn).ReloadTypes();
|
||||
}
|
||||
|
||||
private async Task<int> GetCurrentDatabaseVersion(IPKConnection conn)
|
||||
|
|
@ -82,4 +82,4 @@ namespace PluralKit.Core
|
|||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Threading;
|
||||
using System.Threading;
|
||||
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
|
|
@ -23,32 +23,32 @@ namespace PluralKit.Core
|
|||
_conflictField = conflictField;
|
||||
_condition = condition;
|
||||
}
|
||||
|
||||
public static QueryBuilder Insert(string table) => new QueryBuilder(QueryType.Insert, table, null, null);
|
||||
|
||||
public static QueryBuilder Insert(string table) => new QueryBuilder(QueryType.Insert, table, null, null);
|
||||
public static QueryBuilder Update(string table, string condition) => new QueryBuilder(QueryType.Update, table, null, condition);
|
||||
public static QueryBuilder Upsert(string table, string conflictField) => new QueryBuilder(QueryType.Upsert, table, conflictField, null);
|
||||
|
||||
public QueryBuilder Constant(string fieldName, string paramName)
|
||||
{
|
||||
if (_firstInsert) _firstInsert = false;
|
||||
else
|
||||
else
|
||||
{
|
||||
_insertFragment.Append(", ");
|
||||
_valuesFragment.Append(", ");
|
||||
}
|
||||
|
||||
|
||||
_insertFragment.Append(fieldName);
|
||||
_valuesFragment.Append(paramName);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public QueryBuilder Variable(string fieldName, string paramName)
|
||||
{
|
||||
Constant(fieldName, paramName);
|
||||
|
||||
|
||||
if (_firstUpdate) _firstUpdate = false;
|
||||
else _updateFragment.Append(", ");
|
||||
|
||||
|
||||
_updateFragment.Append(fieldName);
|
||||
_updateFragment.Append(" = ");
|
||||
_updateFragment.Append(paramName);
|
||||
|
|
@ -59,7 +59,7 @@ namespace PluralKit.Core
|
|||
{
|
||||
if (_firstInsert)
|
||||
throw new ArgumentException("No fields have been added to the query.");
|
||||
|
||||
|
||||
StringBuilder query = new StringBuilder(Type switch
|
||||
{
|
||||
QueryType.Insert => $"insert into {Table} ({_insertFragment}) values ({_valuesFragment})",
|
||||
|
|
@ -70,7 +70,7 @@ namespace PluralKit.Core
|
|||
|
||||
if (Type == QueryType.Update && _condition != null)
|
||||
query.Append($" where {_condition}");
|
||||
|
||||
|
||||
if (!string.IsNullOrEmpty(suffix))
|
||||
query.Append($" {suffix}");
|
||||
query.Append(";");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Text;
|
||||
using System.Text;
|
||||
|
||||
using Dapper;
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ namespace PluralKit.Core
|
|||
{
|
||||
_qb = qb;
|
||||
}
|
||||
|
||||
|
||||
public static UpdateQueryBuilder Insert(string table) => new UpdateQueryBuilder(QueryBuilder.Insert(table));
|
||||
public static UpdateQueryBuilder Update(string table, string condition) => new UpdateQueryBuilder(QueryBuilder.Update(table, condition));
|
||||
public static UpdateQueryBuilder Upsert(string table, string conflictField) => new UpdateQueryBuilder(QueryBuilder.Upsert(table, conflictField));
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -10,11 +10,11 @@ namespace PluralKit.Core
|
|||
public static class DatabaseViewsExt
|
||||
{
|
||||
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<ListedGroup>> QueryGroupList(this IPKConnection conn, SystemId system) =>
|
||||
conn.QueryAsync<ListedGroup>("select * from group_list where system = @System", new {System = system});
|
||||
|
||||
conn.QueryAsync<ListedGroup>("select * from group_list where system = @System", new { System = system });
|
||||
|
||||
public static Task<IEnumerable<ListedMember>> QueryMemberList(this IPKConnection conn, SystemId system, MemberListQueryOptions opts)
|
||||
{
|
||||
StringBuilder query;
|
||||
|
|
@ -24,11 +24,11 @@ namespace PluralKit.Core
|
|||
query = new StringBuilder("select member_list.* from group_members inner join member_list on member_list.id = group_members.member_id where group_id = @groupFilter");
|
||||
|
||||
if (opts.PrivacyFilter != null)
|
||||
query.Append($" and member_visibility = {(int) opts.PrivacyFilter}");
|
||||
query.Append($" and member_visibility = {(int)opts.PrivacyFilter}");
|
||||
|
||||
if (opts.Search != null)
|
||||
{
|
||||
static string Filter(string column) => $"(position(lower(@filter) in lower(coalesce({column}, ''))) > 0)";
|
||||
static string Filter(string column) => $"(position(lower(@filter) in lower(coalesce({column}, ''))) > 0)";
|
||||
|
||||
query.Append($" and ({Filter("name")} or {Filter("display_name")}");
|
||||
if (opts.SearchDescription)
|
||||
|
|
@ -41,10 +41,10 @@ namespace PluralKit.Core
|
|||
}
|
||||
query.Append(")");
|
||||
}
|
||||
|
||||
return conn.QueryAsync<ListedMember>(query.ToString(), new {system, filter = opts.Search, groupFilter = opts.GroupFilter});
|
||||
|
||||
return conn.QueryAsync<ListedMember>(query.ToString(), new { system, filter = opts.Search, groupFilter = opts.GroupFilter });
|
||||
}
|
||||
|
||||
|
||||
public struct MemberListQueryOptions
|
||||
{
|
||||
public PrivacyLevel? PrivacyFilter;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
namespace PluralKit.Core
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public class ListedGroup : PKGroup
|
||||
public class ListedGroup: PKGroup
|
||||
{
|
||||
public int MemberCount { get; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
using NodaTime;
|
||||
|
||||
namespace PluralKit.Core
|
||||
|
|
@ -12,6 +12,6 @@ namespace PluralKit.Core
|
|||
public AnnualDate? AnnualBirthday =>
|
||||
Birthday != null
|
||||
? new AnnualDate(Birthday.Value.Month, Birthday.Value.Day)
|
||||
: (AnnualDate?) null;
|
||||
: (AnnualDate?)null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using NodaTime;
|
||||
using NodaTime;
|
||||
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Threading;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -10,18 +10,18 @@ namespace PluralKit.Core
|
|||
public interface IPKConnection: IDbConnection, IAsyncDisposable
|
||||
{
|
||||
public Guid ConnectionId { get; }
|
||||
|
||||
|
||||
public Task OpenAsync(CancellationToken cancellationToken = default);
|
||||
public Task CloseAsync();
|
||||
|
||||
public Task ChangeDatabaseAsync(string databaseName, CancellationToken ct = default);
|
||||
|
||||
|
||||
public ValueTask<IPKTransaction> BeginTransactionAsync(CancellationToken ct = default) => BeginTransactionAsync(IsolationLevel.Unspecified, ct);
|
||||
public ValueTask<IPKTransaction> BeginTransactionAsync(IsolationLevel level, CancellationToken ct = default);
|
||||
|
||||
|
||||
public NpgsqlBinaryImporter BeginBinaryImport(string copyFromCommand);
|
||||
public NpgsqlBinaryExporter BeginBinaryExport(string copyToCommand);
|
||||
|
||||
|
||||
[Obsolete] new void Open();
|
||||
[Obsolete] new void Close();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
|
|
@ -19,11 +19,11 @@ namespace PluralKit.Core
|
|||
internal class PKCommand: DbCommand, IPKCommand
|
||||
{
|
||||
private NpgsqlCommand Inner { get; }
|
||||
|
||||
|
||||
private readonly PKConnection _ourConnection;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IMetrics _metrics;
|
||||
|
||||
|
||||
public PKCommand(NpgsqlCommand inner, PKConnection ourConnection, ILogger logger, IMetrics metrics)
|
||||
{
|
||||
Inner = inner;
|
||||
|
|
@ -111,16 +111,16 @@ namespace PluralKit.Core
|
|||
{
|
||||
var end = SystemClock.Instance.GetCurrentInstant();
|
||||
var elapsed = end - start;
|
||||
|
||||
|
||||
_logger.Verbose("Executed query {Query} in {ElapsedTime} on connection {ConnectionId}", CommandText, elapsed, _ourConnection.ConnectionId);
|
||||
|
||||
|
||||
// One "BCL compatible tick" is 100 nanoseconds
|
||||
var micros = elapsed.BclCompatibleTicks / 10;
|
||||
_metrics.Provider.Timer.Instance(CoreMetrics.DatabaseQuery, new MetricTags("query", CommandText))
|
||||
.Record(micros, TimeUnit.Microseconds, CommandText);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static Exception SyncError(string caller) => throw new Exception($"Executed synchronous IDbCommand function {caller}!");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
|
|
@ -37,28 +37,28 @@ namespace PluralKit.Core
|
|||
_logger = logger.ForContext<PKConnection>();
|
||||
_metrics = metrics;
|
||||
}
|
||||
|
||||
|
||||
public override Task OpenAsync(CancellationToken ct)
|
||||
{
|
||||
if (_hasOpened) return Inner.OpenAsync(ct);
|
||||
_countHolder.Increment();
|
||||
_hasOpened = true;
|
||||
_openTime = SystemClock.Instance.GetCurrentInstant();
|
||||
_logger.Verbose("Opened database connection {ConnectionId}, new connection count {ConnectionCount}", ConnectionId, _countHolder.ConnectionCount);
|
||||
_logger.Verbose("Opened database connection {ConnectionId}, new connection count {ConnectionCount}", ConnectionId, _countHolder.ConnectionCount);
|
||||
return Inner.OpenAsync(ct);
|
||||
}
|
||||
|
||||
public override Task CloseAsync() => Inner.CloseAsync();
|
||||
|
||||
protected override DbCommand CreateDbCommand() => new PKCommand(Inner.CreateCommand(), this, _logger, _metrics);
|
||||
|
||||
|
||||
public void ReloadTypes() => Inner.ReloadTypes();
|
||||
|
||||
public new async ValueTask<IPKTransaction> BeginTransactionAsync(IsolationLevel level, CancellationToken ct = default) => new PKTransaction(await Inner.BeginTransactionAsync(level, ct));
|
||||
|
||||
public NpgsqlBinaryImporter BeginBinaryImport(string copyFromCommand) => Inner.BeginBinaryImport(copyFromCommand);
|
||||
public NpgsqlBinaryExporter BeginBinaryExport(string copyToCommand) => Inner.BeginBinaryExport(copyToCommand);
|
||||
|
||||
|
||||
public override void ChangeDatabase(string databaseName) => Inner.ChangeDatabase(databaseName);
|
||||
public override Task ChangeDatabaseAsync(string databaseName, CancellationToken ct = default) => Inner.ChangeDatabaseAsync(databaseName, ct);
|
||||
protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) => throw SyncError(nameof(BeginDbTransaction));
|
||||
|
|
@ -85,12 +85,12 @@ namespace PluralKit.Core
|
|||
public override ConnectionState State => Inner.State;
|
||||
public override string DataSource => Inner.DataSource;
|
||||
public override string ServerVersion => Inner.ServerVersion;
|
||||
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
{
|
||||
Inner.Dispose();
|
||||
if (_hasClosed) return;
|
||||
|
||||
|
||||
LogClose();
|
||||
}
|
||||
|
||||
|
|
@ -100,12 +100,12 @@ namespace PluralKit.Core
|
|||
LogClose();
|
||||
return Inner.DisposeAsync();
|
||||
}
|
||||
|
||||
|
||||
private void LogClose()
|
||||
{
|
||||
_countHolder.Decrement();
|
||||
_hasClosed = true;
|
||||
|
||||
|
||||
var duration = SystemClock.Instance.GetCurrentInstant() - _openTime;
|
||||
_logger.Verbose("Closed database connection {ConnectionId} (open for {ConnectionDuration}), new connection count {ConnectionCount}", ConnectionId, duration, _countHolder.ConnectionCount);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Threading;
|
||||
|
|
@ -11,7 +11,7 @@ namespace PluralKit.Core
|
|||
internal class PKTransaction: DbTransaction, IPKTransaction
|
||||
{
|
||||
public NpgsqlTransaction Inner { get; }
|
||||
|
||||
|
||||
public PKTransaction(NpgsqlTransaction inner)
|
||||
{
|
||||
Inner = inner;
|
||||
|
|
@ -25,7 +25,7 @@ namespace PluralKit.Core
|
|||
|
||||
protected override DbConnection DbConnection => Inner.Connection;
|
||||
public override IsolationLevel IsolationLevel => Inner.IsolationLevel;
|
||||
|
||||
|
||||
private static Exception SyncError(string caller) => throw new Exception($"Executed synchronous IDbTransaction function {caller}!");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
|
|
@ -12,7 +12,7 @@ namespace PluralKit.Core
|
|||
{
|
||||
result = null;
|
||||
if (!(value is PatchObject po)) return false;
|
||||
|
||||
|
||||
var propList = new List<LogEventProperty>();
|
||||
foreach (var props in po.GetType().GetProperties())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using Serilog.Formatting.Elasticsearch;
|
||||
|
|
@ -30,7 +30,7 @@ namespace PluralKit.Core
|
|||
public Elasticsearch(bool omitEnclosingObject = false, string closingDelimiter = null,
|
||||
bool renderMessage = true, IFormatProvider formatProvider = null,
|
||||
ISerializer serializer = null, bool inlineFields = false,
|
||||
bool renderMessageTemplate = true, bool formatStackTraceAsArray = false): base(
|
||||
bool renderMessageTemplate = true, bool formatStackTraceAsArray = false) : base(
|
||||
omitEnclosingObject, closingDelimiter, renderMessage, formatProvider, serializer, inlineFields,
|
||||
renderMessageTemplate, formatStackTraceAsArray)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
namespace PluralKit.Core
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public class GuildConfig
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public class MemberGuildSettings
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
using System;
|
||||
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>
|
||||
where T : INumericId<T, TInner>
|
||||
where TInner : IEquatable<TInner>, IComparable<TInner>
|
||||
{
|
||||
public TInner Value { get; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
|
@ -26,7 +26,7 @@ namespace PluralKit.Core
|
|||
public static Partial<T> Present(T obj) => new Partial<T>(true, obj);
|
||||
public static Partial<T> Absent = new Partial<T>(false, default!);
|
||||
|
||||
public IEnumerable<T> ToArray() => IsPresent ? new[] {Value} : new T[0];
|
||||
public IEnumerable<T> ToArray() => IsPresent ? new[] { Value } : new T[0];
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => ToArray().GetEnumerator();
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ namespace PluralKit.Core
|
|||
return typeof(Partial<>)
|
||||
.MakeGenericType(innerType)
|
||||
.GetMethod(nameof(Partial<object>.Present))!
|
||||
.Invoke(null, new[] {innerValue});
|
||||
.Invoke(null, new[] { innerValue });
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) =>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using NodaTime;
|
||||
using NodaTime;
|
||||
|
||||
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ namespace PluralKit.Core
|
|||
public static bool operator !=(GroupId left, GroupId right) => !left.Equals(right);
|
||||
|
||||
public int CompareTo(GroupId other) => Value.CompareTo(other.Value);
|
||||
|
||||
|
||||
public override string ToString() => $"Group #{Value}";
|
||||
}
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ namespace PluralKit.Core
|
|||
public PrivacyLevel IconPrivacy { get; private set; }
|
||||
public PrivacyLevel ListPrivacy { get; private set; }
|
||||
public PrivacyLevel Visibility { get; private set; }
|
||||
|
||||
|
||||
public Instant Created { get; private set; }
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ namespace PluralKit.Core
|
|||
{
|
||||
public static string? DescriptionFor(this PKGroup group, LookupContext ctx) =>
|
||||
group.DescriptionPrivacy.Get(ctx, group.Description);
|
||||
|
||||
|
||||
public static string? IconFor(this PKGroup group, LookupContext ctx) =>
|
||||
group.IconPrivacy.Get(ctx, group.Icon?.TryGetCleanCdnUrl());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
|
@ -7,7 +7,8 @@ using Newtonsoft.Json.Linq;
|
|||
using NodaTime;
|
||||
using NodaTime.Text;
|
||||
|
||||
namespace PluralKit.Core {
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public readonly struct MemberId: INumericId<MemberId, int>
|
||||
{
|
||||
public int Value { get; }
|
||||
|
|
@ -28,7 +29,7 @@ namespace PluralKit.Core {
|
|||
public static bool operator !=(MemberId left, MemberId right) => !left.Equals(right);
|
||||
|
||||
public int CompareTo(MemberId other) => Value.CompareTo(other.Value);
|
||||
|
||||
|
||||
public override string ToString() => $"Member #{Value}";
|
||||
}
|
||||
|
||||
|
|
@ -61,10 +62,11 @@ namespace PluralKit.Core {
|
|||
public PrivacyLevel PronounPrivacy { get; private set; }
|
||||
public PrivacyLevel MetadataPrivacy { get; private set; }
|
||||
// public PrivacyLevel ColorPrivacy { get; private set; }
|
||||
|
||||
|
||||
/// Returns a formatted string representing the member's birthday, taking into account that a year of "0001" or "0004" is hidden
|
||||
/// Before Feb 10 2020, the sentinel year was 0001, now it is 0004.
|
||||
[JsonIgnore] public string BirthdayString
|
||||
[JsonIgnore]
|
||||
public string BirthdayString
|
||||
{
|
||||
get
|
||||
{
|
||||
|
|
@ -97,7 +99,7 @@ namespace PluralKit.Core {
|
|||
member.PronounPrivacy.Get(ctx, member.Pronouns);
|
||||
|
||||
public static Instant? CreatedFor(this PKMember member, LookupContext ctx) =>
|
||||
member.MetadataPrivacy.Get(ctx, (Instant?) member.Created);
|
||||
member.MetadataPrivacy.Get(ctx, (Instant?)member.Created);
|
||||
|
||||
public static int MessageCountFor(this PKMember member, LookupContext ctx) =>
|
||||
member.MetadataPrivacy.Get(ctx, member.MessageCount);
|
||||
|
|
@ -105,7 +107,7 @@ namespace PluralKit.Core {
|
|||
public static JObject ToJson(this PKMember member, LookupContext ctx, bool needsLegacyProxyTags = false)
|
||||
{
|
||||
var includePrivacy = ctx == LookupContext.ByOwner;
|
||||
|
||||
|
||||
var o = new JObject();
|
||||
o.Add("id", member.Hid);
|
||||
o.Add("name", member.NameFor(ctx));
|
||||
|
|
@ -117,14 +119,14 @@ namespace PluralKit.Core {
|
|||
o.Add("avatar_url", member.AvatarFor(ctx).TryGetCleanCdnUrl());
|
||||
o.Add("banner", member.DescriptionPrivacy.Get(ctx, member.BannerImage).TryGetCleanCdnUrl());
|
||||
o.Add("description", member.DescriptionFor(ctx));
|
||||
|
||||
|
||||
var tagArray = new JArray();
|
||||
foreach (var tag in member.ProxyTags)
|
||||
tagArray.Add(new JObject {{"prefix", tag.Prefix}, {"suffix", tag.Suffix}});
|
||||
foreach (var tag in member.ProxyTags)
|
||||
tagArray.Add(new JObject { { "prefix", tag.Prefix }, { "suffix", tag.Suffix } });
|
||||
o.Add("proxy_tags", tagArray);
|
||||
|
||||
o.Add("keep_proxy", member.KeepProxy);
|
||||
|
||||
|
||||
o.Add("privacy", includePrivacy ? (member.MemberVisibility.LevelName()) : null);
|
||||
|
||||
o.Add("visibility", includePrivacy ? (member.MemberVisibility.LevelName()) : null);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using NodaTime;
|
||||
using NodaTime;
|
||||
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
using NodaTime;
|
||||
using NodaTime;
|
||||
|
||||
|
||||
|
||||
namespace PluralKit.Core {
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
|
||||
public readonly struct SwitchId: INumericId<SwitchId, int>
|
||||
{
|
||||
|
|
@ -24,7 +25,7 @@ namespace PluralKit.Core {
|
|||
public static bool operator !=(SwitchId left, SwitchId right) => !left.Equals(right);
|
||||
|
||||
public int CompareTo(SwitchId other) => Value.CompareTo(other.Value);
|
||||
|
||||
|
||||
public override string ToString() => $"Switch #{Value}";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
using Dapper.Contrib.Extensions;
|
||||
using Dapper.Contrib.Extensions;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using NodaTime;
|
||||
|
||||
namespace PluralKit.Core {
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
|
||||
public readonly struct SystemId: INumericId<SystemId, int>
|
||||
{
|
||||
|
|
@ -46,14 +47,14 @@ namespace PluralKit.Core {
|
|||
public string UiTz { get; set; }
|
||||
public bool PingsEnabled { get; }
|
||||
public int? LatchTimeout { get; }
|
||||
public PrivacyLevel DescriptionPrivacy { get; }
|
||||
public PrivacyLevel MemberListPrivacy { get;}
|
||||
public PrivacyLevel DescriptionPrivacy { get; }
|
||||
public PrivacyLevel MemberListPrivacy { get; }
|
||||
public PrivacyLevel FrontPrivacy { get; }
|
||||
public PrivacyLevel FrontHistoryPrivacy { get; }
|
||||
public PrivacyLevel GroupListPrivacy { get; }
|
||||
public int? MemberLimitOverride { get; }
|
||||
public int? GroupLimitOverride { get; }
|
||||
|
||||
|
||||
[JsonIgnore] public DateTimeZone Zone => DateTimeZoneProviders.Tzdb.GetZoneOrNull(UiTz);
|
||||
}
|
||||
|
||||
|
|
@ -82,4 +83,4 @@ namespace PluralKit.Core {
|
|||
return o;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
namespace PluralKit.Core
|
||||
{
|
||||
public class AccountPatch: PatchObject
|
||||
{
|
||||
public Partial<bool> AllowAutoproxy { get; set; }
|
||||
public class AccountPatch: PatchObject
|
||||
{
|
||||
public Partial<bool> AllowAutoproxy { get; set; }
|
||||
|
||||
public override UpdateQueryBuilder Apply(UpdateQueryBuilder b) => b
|
||||
.With("allow_autoproxy", AllowAutoproxy);
|
||||
}
|
||||
}
|
||||
public override UpdateQueryBuilder Apply(UpdateQueryBuilder b) => b
|
||||
.With("allow_autoproxy", AllowAutoproxy);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace PluralKit.Core
|
||||
|
|
@ -12,7 +12,7 @@ namespace PluralKit.Core
|
|||
public Partial<string?> Icon { get; set; }
|
||||
public Partial<string?> BannerImage { get; set; }
|
||||
public Partial<string?> Color { get; set; }
|
||||
|
||||
|
||||
public Partial<PrivacyLevel> DescriptionPrivacy { get; set; }
|
||||
public Partial<PrivacyLevel> IconPrivacy { get; set; }
|
||||
public Partial<PrivacyLevel> ListPrivacy { get; set; }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
namespace PluralKit.Core
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public class GuildPatch: PatchObject
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public class MemberGuildPatch: PatchObject
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
|
|
@ -83,9 +83,9 @@ namespace PluralKit.Core
|
|||
{
|
||||
var patch = new MemberPatch();
|
||||
|
||||
if (o.ContainsKey("name") && o["name"].Type == JTokenType.Null)
|
||||
if (o.ContainsKey("name") && o["name"].Type == JTokenType.Null)
|
||||
throw new ValidationError("Member name can not be set to null.");
|
||||
|
||||
|
||||
if (o.ContainsKey("name")) patch.Name = o.Value<string>("name");
|
||||
if (o.ContainsKey("color")) patch.Color = o.Value<string>("color").NullIfEmpty()?.ToLower();
|
||||
if (o.ContainsKey("display_name")) patch.DisplayName = o.Value<string>("display_name").NullIfEmpty();
|
||||
|
|
@ -107,17 +107,17 @@ namespace PluralKit.Core
|
|||
|
||||
// legacy: used in old export files and APIv1
|
||||
if (o.ContainsKey("prefix") || o.ContainsKey("suffix") && !o.ContainsKey("proxy_tags"))
|
||||
patch.ProxyTags = new[] {new ProxyTag(o.Value<string>("prefix"), o.Value<string>("suffix"))};
|
||||
patch.ProxyTags = new[] { new ProxyTag(o.Value<string>("prefix"), o.Value<string>("suffix")) };
|
||||
else if (o.ContainsKey("proxy_tags"))
|
||||
patch.ProxyTags = o.Value<JArray>("proxy_tags")
|
||||
.OfType<JObject>().Select(o => new ProxyTag(o.Value<string>("prefix"), o.Value<string>("suffix")))
|
||||
.Where(p => p.Valid)
|
||||
.ToArray();
|
||||
|
||||
if(o.ContainsKey("privacy")) //TODO: Deprecate this completely in api v2
|
||||
|
||||
if (o.ContainsKey("privacy")) //TODO: Deprecate this completely in api v2
|
||||
{
|
||||
var plevel = o.ParsePrivacy("privacy");
|
||||
|
||||
|
||||
patch.Visibility = plevel;
|
||||
patch.NamePrivacy = plevel;
|
||||
patch.AvatarPrivacy = plevel;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace PluralKit.Core
|
||||
|
|
@ -7,7 +7,7 @@ namespace PluralKit.Core
|
|||
{
|
||||
public abstract UpdateQueryBuilder Apply(UpdateQueryBuilder b);
|
||||
|
||||
public void AssertIsValid() {}
|
||||
public void AssertIsValid() { }
|
||||
|
||||
protected bool AssertValid(string input, string name, int maxLength, Func<string, bool>? validate = null)
|
||||
{
|
||||
|
|
@ -28,7 +28,7 @@ namespace PluralKit.Core
|
|||
|
||||
public class ValidationError: Exception
|
||||
{
|
||||
public ValidationError(string message): base(message) { }
|
||||
public ValidationError(string message) : base(message) { }
|
||||
}
|
||||
|
||||
public class FieldTooLongError: ValidationError
|
||||
|
|
@ -37,7 +37,7 @@ namespace PluralKit.Core
|
|||
public int MaxLength;
|
||||
public int ActualLength;
|
||||
|
||||
public FieldTooLongError(string name, int maxLength, int actualLength):
|
||||
public FieldTooLongError(string name, int maxLength, int actualLength) :
|
||||
base($"{name} too long ({actualLength} > {maxLength})")
|
||||
{
|
||||
Name = name;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public class SystemGuildPatch: PatchObject
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#nullable enable
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ namespace PluralKit.Core
|
|||
// legacy: APIv1 uses "tz" instead of "timezone"
|
||||
// todo: remove in APIv2
|
||||
if (o.ContainsKey("tz")) patch.UiTz = o.Value<string>("tz") ?? "UTC";
|
||||
|
||||
|
||||
if (o.ContainsKey("description_privacy")) patch.DescriptionPrivacy = o.ParsePrivacy("description_privacy");
|
||||
if (o.ContainsKey("member_list_privacy")) patch.MemberListPrivacy = o.ParsePrivacy("member_list_privacy");
|
||||
if (o.ContainsKey("front_privacy")) patch.FrontPrivacy = o.ParsePrivacy("front_privacy");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
|
|
@ -9,7 +9,7 @@ namespace PluralKit.Core
|
|||
List,
|
||||
Visibility
|
||||
}
|
||||
|
||||
|
||||
public static class GroupPrivacyUtils
|
||||
{
|
||||
public static GroupPatch WithPrivacy(this GroupPatch group, GroupPrivacySubject subject, PrivacyLevel level)
|
||||
|
|
@ -23,14 +23,14 @@ namespace PluralKit.Core
|
|||
GroupPrivacySubject.Visibility => group.Visibility = level,
|
||||
_ => throw new ArgumentOutOfRangeException($"Unknown privacy subject {subject}")
|
||||
};
|
||||
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
public static GroupPatch WithAllPrivacy(this GroupPatch member, PrivacyLevel level)
|
||||
{
|
||||
foreach (var subject in Enum.GetValues(typeof(GroupPrivacySubject)))
|
||||
member.WithPrivacy((GroupPrivacySubject) subject, level);
|
||||
member.WithPrivacy((GroupPrivacySubject)subject, level);
|
||||
return member;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
namespace PluralKit.Core
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public enum LookupContext
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
|
|
@ -29,14 +29,14 @@ namespace PluralKit.Core
|
|||
MemberPrivacySubject.Visibility => member.Visibility = level,
|
||||
_ => throw new ArgumentOutOfRangeException($"Unknown privacy subject {subject}")
|
||||
};
|
||||
|
||||
|
||||
return member;
|
||||
}
|
||||
|
||||
public static MemberPatch WithAllPrivacy(this MemberPatch member, PrivacyLevel level)
|
||||
{
|
||||
foreach (var subject in Enum.GetValues(typeof(MemberPrivacySubject)))
|
||||
member.WithPrivacy((MemberPrivacySubject) subject, level);
|
||||
member.WithPrivacy((MemberPrivacySubject)subject, level);
|
||||
return member;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
|
|
@ -9,18 +9,18 @@ namespace PluralKit.Core
|
|||
Public = 1,
|
||||
Private = 2
|
||||
}
|
||||
|
||||
|
||||
public static class PrivacyLevelExt
|
||||
{
|
||||
public static bool CanAccess(this PrivacyLevel level, LookupContext ctx) =>
|
||||
level == PrivacyLevel.Public || ctx == LookupContext.ByOwner;
|
||||
|
||||
public static string LevelName(this PrivacyLevel level) =>
|
||||
public static string LevelName(this PrivacyLevel level) =>
|
||||
level == PrivacyLevel.Public ? "public" : "private";
|
||||
|
||||
public static T Get<T>(this PrivacyLevel level, LookupContext ctx, T input, T fallback = default) =>
|
||||
level.CanAccess(ctx) ? input : fallback;
|
||||
|
||||
|
||||
public static string Explanation(this PrivacyLevel level) =>
|
||||
level switch
|
||||
{
|
||||
|
|
@ -40,7 +40,7 @@ namespace PluralKit.Core
|
|||
output = input;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static string ToJsonString(this PrivacyLevel level) => level.LevelName();
|
||||
|
||||
public static PrivacyLevel ParsePrivacy(this JObject o, string propertyName)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
|
|
@ -25,17 +25,17 @@ namespace PluralKit.Core
|
|||
SystemPrivacySubject.GroupList => system.GroupListPrivacy = level,
|
||||
_ => throw new ArgumentOutOfRangeException($"Unknown privacy subject {subject}")
|
||||
};
|
||||
|
||||
|
||||
return system;
|
||||
}
|
||||
|
||||
public static SystemPatch WithAllPrivacy(this SystemPatch system, PrivacyLevel level)
|
||||
{
|
||||
foreach (var subject in Enum.GetValues(typeof(SystemPrivacySubject)))
|
||||
WithPrivacy(system, (SystemPrivacySubject) subject, level);
|
||||
WithPrivacy(system, (SystemPrivacySubject)subject, level);
|
||||
return system;
|
||||
}
|
||||
|
||||
|
||||
public static bool TryParseSystemPrivacy(string input, out SystemPrivacySubject subject)
|
||||
{
|
||||
switch (input.ToLowerInvariant())
|
||||
|
|
@ -45,7 +45,7 @@ namespace PluralKit.Core
|
|||
case "text":
|
||||
case "info":
|
||||
subject = SystemPrivacySubject.Description;
|
||||
break;
|
||||
break;
|
||||
case "members":
|
||||
case "memberlist":
|
||||
case "list":
|
||||
|
|
@ -72,6 +72,6 @@ namespace PluralKit.Core
|
|||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
|
|
@ -14,7 +14,8 @@ namespace PluralKit.Core
|
|||
[JsonProperty("prefix")] public string Prefix { get; set; }
|
||||
[JsonProperty("suffix")] public string Suffix { get; set; }
|
||||
|
||||
[JsonIgnore] public bool Valid =>
|
||||
[JsonIgnore]
|
||||
public bool Valid =>
|
||||
Prefix != null || Suffix != null
|
||||
&& ProxyString.Length <= Limits.MaxProxyTagLength;
|
||||
|
||||
|
|
@ -35,4 +36,4 @@ namespace PluralKit.Core
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace PluralKit.Core
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public enum AutoproxyMode
|
||||
{
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
Latch = 3,
|
||||
Member = 4
|
||||
}
|
||||
|
||||
|
||||
public class SystemGuildSettings
|
||||
{
|
||||
public ulong Guild { get; }
|
||||
|
|
@ -16,8 +16,8 @@
|
|||
|
||||
public AutoproxyMode AutoproxyMode { get; } = AutoproxyMode.Off;
|
||||
public MemberId? AutoproxyMember { get; }
|
||||
|
||||
|
||||
public string? Tag { get; }
|
||||
public bool TagEnabled { get; }
|
||||
public bool TagEnabled { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
using Autofac;
|
||||
using Autofac;
|
||||
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public class ConfigModule<T>: Module where T: new()
|
||||
public class ConfigModule<T>: Module where T : new()
|
||||
{
|
||||
private readonly string _submodule;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Autofac;
|
||||
using Autofac;
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -13,7 +13,7 @@ namespace PluralKit.Core
|
|||
builder.RegisterType<DatabaseMigrator>().SingleInstance();
|
||||
builder.RegisterType<Database>().As<IDatabase>().SingleInstance();
|
||||
builder.RegisterType<ModelRepository>().AsSelf().SingleInstance();
|
||||
|
||||
|
||||
builder.Populate(new ServiceCollection().AddMemoryCache());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
using Autofac;
|
||||
|
|
@ -43,7 +43,7 @@ namespace PluralKit.Core
|
|||
{
|
||||
var consoleTemplate = "[{Timestamp:HH:mm:ss.fff}] {Level:u3} {Message:lj}{NewLine}{Exception}";
|
||||
var outputTemplate = "[{Timestamp:yyyy-MM-dd HH:mm:ss.ffffff}] {Level:u3} {Message:lj}{NewLine}{Exception}";
|
||||
|
||||
|
||||
var logCfg = new LoggerConfiguration()
|
||||
.Enrich.FromLogContext()
|
||||
.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb)
|
||||
|
|
@ -52,7 +52,7 @@ namespace PluralKit.Core
|
|||
|
||||
// Don't want App.Metrics/D#+ spam
|
||||
.MinimumLevel.Override("App.Metrics", LogEventLevel.Information)
|
||||
|
||||
|
||||
// Actual formatting for these is handled in ScalarFormatting
|
||||
.Destructure.AsScalar<SystemId>()
|
||||
.Destructure.AsScalar<MemberId>()
|
||||
|
|
@ -60,7 +60,7 @@ namespace PluralKit.Core
|
|||
.Destructure.AsScalar<SwitchId>()
|
||||
.Destructure.ByTransforming<ProxyTag>(t => new { t.Prefix, t.Suffix })
|
||||
.Destructure.With<PatchObjectDestructuring>()
|
||||
|
||||
|
||||
.WriteTo.Async(a =>
|
||||
{
|
||||
// Both the same output, except one is raw compact JSON and one is plain text.
|
||||
|
|
@ -109,7 +109,7 @@ namespace PluralKit.Core
|
|||
return Log.Logger = logCfg.CreateLogger();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Serilog why is this necessary for such a simple thing >.>
|
||||
public class UTCTimestampFormatProvider: IFormatProvider
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using App.Metrics;
|
||||
using App.Metrics;
|
||||
|
||||
using Autofac;
|
||||
|
||||
|
|
|
|||
|
|
@ -17,14 +17,14 @@ namespace PluralKit.Core
|
|||
private readonly IDatabase _db;
|
||||
private readonly ModelRepository _repo;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
|
||||
public DataFileService(IDatabase db, ModelRepository repo, ILogger logger)
|
||||
{
|
||||
_db = db;
|
||||
_repo = repo;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
public async Task<JObject> ExportSystem(PKSystem system)
|
||||
{
|
||||
await using var conn = await _db.Obtain();
|
||||
|
|
@ -40,8 +40,8 @@ namespace PluralKit.Core
|
|||
o.Add("timezone", system.UiTz);
|
||||
o.Add("created", system.Created.FormatExport());
|
||||
o.Add("accounts", new JArray((await _repo.GetSystemAccounts(conn, system.Id)).ToList()));
|
||||
o.Add("members", new JArray((await _repo.GetSystemMembers(conn, system.Id).ToListAsync()).Select(m => m.ToJson(LookupContext.ByOwner))));
|
||||
|
||||
o.Add("members", new JArray((await _repo.GetSystemMembers(conn, system.Id).ToListAsync()).Select(m => m.ToJson(LookupContext.ByOwner))));
|
||||
|
||||
var switches = new JArray();
|
||||
var switchList = await _repo.GetPeriodFronters(conn, system.Id, null,
|
||||
Instant.FromDateTimeUtc(DateTime.MinValue.ToUniversalTime()), SystemClock.Instance.GetCurrentInstant());
|
||||
|
|
@ -53,15 +53,15 @@ namespace PluralKit.Core
|
|||
switches.Add(s);
|
||||
}
|
||||
o.Add("switches", switches);
|
||||
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
public async Task<ImportResultNew> ImportSystem(ulong userId, PKSystem? system, JObject importFile, Func<string, Task> confirmFunc)
|
||||
{
|
||||
await using var conn = await _db.Obtain();
|
||||
await using var tx = await conn.BeginTransactionAsync();
|
||||
|
||||
|
||||
return await BulkImporter.PerformImport(conn, tx, _repo, _logger, userId, system, importFile, confirmFunc);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ using Serilog;
|
|||
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public partial class BulkImporter : IAsyncDisposable
|
||||
public partial class BulkImporter: IAsyncDisposable
|
||||
{
|
||||
private ILogger _logger { get; init; }
|
||||
private ModelRepository _repo { get; init; }
|
||||
private ModelRepository _repo { get; init; }
|
||||
|
||||
private PKSystem _system { get; set; }
|
||||
private IPKConnection _conn { get; init; }
|
||||
|
|
@ -41,16 +41,17 @@ namespace PluralKit.Core
|
|||
_confirmFunc = confirmFunc,
|
||||
};
|
||||
|
||||
if (system == null) {
|
||||
if (system == null)
|
||||
{
|
||||
system = await repo.CreateSystem(conn, null, tx);
|
||||
await repo.AddAccount(conn, system.Id, userId);
|
||||
importer._result.CreatedSystem = system.Hid;
|
||||
importer._system = system;
|
||||
}
|
||||
|
||||
|
||||
// Fetch all members in the system and log their names and hids
|
||||
var members = await conn.QueryAsync<PKMember>("select id, hid, name from members where system = @System",
|
||||
new {System = system.Id});
|
||||
new { System = system.Id });
|
||||
foreach (var m in members)
|
||||
{
|
||||
importer._existingMemberHids[m.Hid] = m.Id;
|
||||
|
|
@ -105,11 +106,12 @@ namespace PluralKit.Core
|
|||
{
|
||||
await _tx.RollbackAsync();
|
||||
}
|
||||
catch (InvalidOperationException) {}
|
||||
catch (InvalidOperationException) { }
|
||||
}
|
||||
|
||||
private class ImportException : Exception {
|
||||
public ImportException(string Message) : base(Message) {}
|
||||
private class ImportException: Exception
|
||||
{
|
||||
public ImportException(string Message) : base(Message) { }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,9 +33,10 @@ namespace PluralKit.Core
|
|||
|
||||
var members = importFile.Value<JArray>("members");
|
||||
var switches = importFile.Value<JArray>("switches");
|
||||
|
||||
var newMembers = members.Count(m => {
|
||||
var (found, _) = TryGetExistingMember(m.Value<string>("id"), m.Value<string>("name"));
|
||||
|
||||
var newMembers = members.Count(m =>
|
||||
{
|
||||
var (found, _) = TryGetExistingMember(m.Value<string>("id"), m.Value<string>("name"));
|
||||
return found == null;
|
||||
});
|
||||
await AssertLimitNotReached(newMembers);
|
||||
|
|
@ -43,7 +44,7 @@ namespace PluralKit.Core
|
|||
foreach (JObject member in members)
|
||||
await ImportMember(member);
|
||||
|
||||
if (switches.Any(sw => sw.Value<JArray>("members").Any(m => !_knownIdentifiers.ContainsKey((string) m))))
|
||||
if (switches.Any(sw => sw.Value<JArray>("members").Any(m => !_knownIdentifiers.ContainsKey((string)m))))
|
||||
throw new ImportException("One or more switches include members that haven't been imported.");
|
||||
|
||||
await ImportSwitches(switches);
|
||||
|
|
@ -99,9 +100,9 @@ namespace PluralKit.Core
|
|||
|
||||
private async Task ImportSwitches(JArray switches)
|
||||
{
|
||||
var existingSwitches = (await _conn.QueryAsync<PKSwitch>("select * from switches where system = @System", new {System = _system.Id})).ToList();
|
||||
var existingSwitches = (await _conn.QueryAsync<PKSwitch>("select * from switches where system = @System", new { System = _system.Id })).ToList();
|
||||
var existingTimestamps = existingSwitches.Select(sw => sw.Timestamp).ToImmutableHashSet();
|
||||
var lastSwitchId = existingSwitches.Count != 0 ? existingSwitches.Select(sw => sw.Id).Max() : (SwitchId?) null;
|
||||
var lastSwitchId = existingSwitches.Count != 0 ? existingSwitches.Select(sw => sw.Id).Max() : (SwitchId?)null;
|
||||
|
||||
if (switches.Count > 10000)
|
||||
throw new ImportException($"Too many switches present in import file.");
|
||||
|
|
@ -115,10 +116,10 @@ namespace PluralKit.Core
|
|||
var timestampString = sw.Value<string>("timestamp");
|
||||
var timestamp = DateTimeFormats.TimestampExportFormat.Parse(timestampString);
|
||||
if (!timestamp.Success) throw new ImportException($"Switch timestamp {timestampString} is not an valid timestamp.");
|
||||
|
||||
|
||||
// Don't import duplicate switches
|
||||
if (existingTimestamps.Contains(timestamp.Value)) continue;
|
||||
|
||||
|
||||
// Otherwise, write to importer
|
||||
await importer.StartRowAsync();
|
||||
await importer.WriteAsync(_system.Id.Value, NpgsqlDbType.Integer);
|
||||
|
|
@ -127,7 +128,7 @@ namespace PluralKit.Core
|
|||
var members = sw.Value<JArray>("members");
|
||||
if (members.Count > Limits.MaxSwitchMemberCount)
|
||||
throw new ImportException($"Switch with timestamp {timestampString} contains too many members ({members.Count} > 100).");
|
||||
|
||||
|
||||
// Note that we've imported a switch with this timestamp
|
||||
importedSwitches[timestamp.Value] = sw.Value<JArray>("members");
|
||||
}
|
||||
|
|
@ -135,13 +136,13 @@ namespace PluralKit.Core
|
|||
// Commit the import
|
||||
await importer.CompleteAsync();
|
||||
}
|
||||
|
||||
|
||||
// Now, fetch all the switches we just added (so, now we get their IDs too)
|
||||
// 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>(
|
||||
"select * from switches where system = @System and id > @LastSwitchId",
|
||||
new {System = _system.Id, LastSwitchId = lastSwitchId?.Value ?? -1});
|
||||
|
||||
new { System = _system.Id, LastSwitchId = lastSwitchId?.Value ?? -1 });
|
||||
|
||||
// Lastly, import the switch members
|
||||
await using (var importer = _conn.BeginBinaryImport("copy switch_members (switch, member) from stdin (format binary)"))
|
||||
{
|
||||
|
|
@ -149,13 +150,13 @@ namespace PluralKit.Core
|
|||
{
|
||||
if (!importedSwitches.TryGetValue(justAddedSwitch.Timestamp, out var switchMembers))
|
||||
throw new Exception($"Found 'just-added' switch (by ID) with timestamp {justAddedSwitch.Timestamp}, but this did not correspond to a timestamp we just added a switch entry of! :/");
|
||||
|
||||
|
||||
// We still assume timestamps are unique and non-duplicate, so:
|
||||
foreach (var memberIdentifier in switchMembers)
|
||||
{
|
||||
if (!_knownIdentifiers.TryGetValue((string) memberIdentifier, out var memberId))
|
||||
if (!_knownIdentifiers.TryGetValue((string)memberIdentifier, out var memberId))
|
||||
throw new Exception($"Attempted to import switch with member identifier {memberIdentifier} but could not find an entry in the id map for this! :/");
|
||||
|
||||
|
||||
await importer.StartRowAsync();
|
||||
await importer.WriteAsync(justAddedSwitch.Id.Value, NpgsqlDbType.Integer);
|
||||
await importer.WriteAsync(memberId.Value, NpgsqlDbType.Integer);
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ namespace PluralKit.Core
|
|||
|
||||
var hasGroup = tupper.ContainsKey("group_id") && tupper["group_id"].Type != JTokenType.Null;
|
||||
var multipleTags = false;
|
||||
|
||||
|
||||
var name = tupper.Value<string>("name");
|
||||
var patch = new MemberPatch();
|
||||
|
||||
|
|
@ -61,8 +61,8 @@ namespace PluralKit.Core
|
|||
if (brackets.Count % 2 != 0)
|
||||
throw new ImportException($"Field 'brackets' in tupper {name} is invalid.");
|
||||
var tags = new List<ProxyTag>();
|
||||
for (var i = 0; i < brackets.Count / 2; i++)
|
||||
tags.Add(new ProxyTag((string) brackets[i * 2], (string) brackets[i * 2 + 1]));
|
||||
for (var i = 0; i < brackets.Count / 2; i++)
|
||||
tags.Add(new ProxyTag((string)brackets[i * 2], (string)brackets[i * 2 + 1]));
|
||||
patch.ProxyTags = tags.ToArray();
|
||||
}
|
||||
// todo: && if is new member
|
||||
|
|
@ -97,7 +97,7 @@ namespace PluralKit.Core
|
|||
}
|
||||
else
|
||||
_result.Modified++;
|
||||
|
||||
|
||||
_logger.Debug("Importing member with identifier {FileId} to system {System} (is creating new member? {IsCreatingNewMember})",
|
||||
name, _system.Id, isNewMember);
|
||||
|
||||
|
|
@ -113,9 +113,9 @@ namespace PluralKit.Core
|
|||
{
|
||||
throw new ImportException($"Field {e.Message} in tupper {name} is invalid.");
|
||||
}
|
||||
|
||||
|
||||
await _repo.UpdateMember(_conn, memberId, patch, _tx);
|
||||
|
||||
|
||||
return (lastSetTag, multipleTags, hasGroup);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
using NodaTime;
|
||||
using NodaTime;
|
||||
using NodaTime.Text;
|
||||
|
||||
namespace PluralKit.Core {
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public static class DateTimeFormats
|
||||
{
|
||||
public static IPattern<Instant> TimestampExportFormat = InstantPattern.ExtendedIso;
|
||||
public static IPattern<LocalDate> DateExportFormat = LocalDatePattern.CreateWithInvariantCulture("yyyy-MM-dd");
|
||||
|
||||
|
||||
// We create a composite pattern that only shows the two most significant things
|
||||
// eg. if we have something with nonzero day component, we show <x>d <x>h, but if it's
|
||||
// a smaller duration we may only bother with showing <x>h <x>m or <x>m <x>s
|
||||
|
|
@ -17,7 +18,7 @@ namespace PluralKit.Core {
|
|||
{DurationPattern.CreateWithInvariantCulture("H'h' m'm'"), d => d.Hours > 0},
|
||||
{DurationPattern.CreateWithInvariantCulture("D'd' h'h'"), d => d.Days > 0}
|
||||
}.Build();
|
||||
|
||||
|
||||
public static IPattern<LocalDateTime> LocalDateTimeFormat = LocalDateTimePattern.CreateWithInvariantCulture("yyyy-MM-dd HH:mm:ss");
|
||||
public static IPattern<ZonedDateTime> ZonedDateTimeFormat = ZonedDateTimePattern.CreateWithInvariantCulture("yyyy-MM-dd HH:mm:ss x", DateTimeZoneProviders.Tzdb);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using NodaTime;
|
||||
|
|
@ -11,7 +11,7 @@ namespace PluralKit.Core
|
|||
public static Duration? ParsePeriod(string str)
|
||||
{
|
||||
Duration d = Duration.Zero;
|
||||
|
||||
|
||||
foreach (Match match in Regex.Matches(str, "(\\d{1,6})(\\w)"))
|
||||
{
|
||||
var amount = int.Parse(match.Groups[1].Value);
|
||||
|
|
@ -34,7 +34,7 @@ namespace PluralKit.Core
|
|||
// NodaTime can't parse constructs like "1st" and "2nd" so we quietly replace those away
|
||||
// Gotta make sure to do the regex otherwise we'll catch things like the "st" in "August" too
|
||||
str = Regex.Replace(str, "(\\d+)(st|nd|rd|th)", "$1");
|
||||
|
||||
|
||||
var patterns = new[]
|
||||
{
|
||||
"MMM d yyyy", // Jan 1 2019
|
||||
|
|
@ -72,12 +72,12 @@ namespace PluralKit.Core
|
|||
public static ZonedDateTime? ParseDateTime(string str, bool nudgeToPast = false, DateTimeZone zone = null)
|
||||
{
|
||||
if (zone == null) zone = DateTimeZone.Utc;
|
||||
|
||||
|
||||
// Find the current timestamp in the given zone, find the (naive) midnight timestamp, then put that into the same zone (and make it naive again)
|
||||
// Should yield a <current *local @ zone* date> 12:00:00 AM.
|
||||
var now = SystemClock.Instance.GetCurrentInstant().InZone(zone).LocalDateTime;
|
||||
var midnight = now.Date.AtMidnight();
|
||||
|
||||
|
||||
// First we try to parse the string as a relative time using the period parser
|
||||
var relResult = ParsePeriod(str);
|
||||
if (relResult != null)
|
||||
|
|
@ -119,7 +119,7 @@ namespace PluralKit.Core
|
|||
"MM dd", // 01 01
|
||||
"MM/dd" // 01-01
|
||||
};
|
||||
|
||||
|
||||
// First, we try all the timestamps that only have a time
|
||||
foreach (var timePattern in timePatterns)
|
||||
{
|
||||
|
|
@ -130,17 +130,17 @@ namespace PluralKit.Core
|
|||
// If we have a successful match and we need a time in the past, we try to shove a future-time a date before
|
||||
// Example: "4:30 pm" at 3:30 pm likely refers to 4:30 pm the previous day
|
||||
var val = result.Value;
|
||||
|
||||
|
||||
// If we need to nudge, we just subtract a day. This only occurs when we're parsing specifically *just time*, so
|
||||
// we know we won't nudge it by more than a day since we use today's midnight timestamp as a date template.
|
||||
|
||||
|
||||
// Since this is a naive datetime, this ensures we're actually moving by one calendar day even if
|
||||
// DST changes occur, since they'll be resolved later wrt. the right side of the boundary
|
||||
if (val > now && nudgeToPast) val = val.PlusDays(-1);
|
||||
return val.InZoneLeniently(zone);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Then we try specific date+time combinations, both date first and time first, with and without commas
|
||||
foreach (var timePattern in timePatterns)
|
||||
{
|
||||
|
|
@ -158,7 +158,7 @@ namespace PluralKit.Core
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Finally, just date patterns, still using midnight as the template
|
||||
foreach (var datePattern in datePatterns)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
namespace PluralKit.Core {
|
||||
public static class Emojis {
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public static class Emojis
|
||||
{
|
||||
public static readonly string Warn = "\u26A0";
|
||||
public static readonly string Success = "\u2705";
|
||||
public static readonly string Error = "\u274C";
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ namespace PluralKit.Core
|
|||
return default;
|
||||
}
|
||||
|
||||
var entry = new HandlerEntry {Predicate = predicate, Handler = Handler};
|
||||
var entry = new HandlerEntry { Predicate = predicate, Handler = Handler };
|
||||
_handlers[Interlocked.Increment(ref _seq)] = entry;
|
||||
|
||||
// Wait for either the event task or the timeout task
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.IO;
|
||||
using System.IO;
|
||||
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
|
|
@ -7,20 +7,21 @@ using Newtonsoft.Json;
|
|||
using NodaTime;
|
||||
using NodaTime.Serialization.JsonNet;
|
||||
|
||||
namespace PluralKit.Core {
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public static class InitUtils
|
||||
{
|
||||
public static void InitStatic()
|
||||
{
|
||||
Database.InitStatic();
|
||||
}
|
||||
|
||||
|
||||
public static IConfigurationBuilder BuildConfiguration(string[] args) => new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("pluralkit.conf", true)
|
||||
.AddEnvironmentVariables()
|
||||
.AddCommandLine(args);
|
||||
|
||||
|
||||
public static JsonSerializerSettings BuildSerializerSettings() => new JsonSerializerSettings().BuildSerializerSettings();
|
||||
|
||||
public static JsonSerializerSettings BuildSerializerSettings(this JsonSerializerSettings settings)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
namespace PluralKit.Core {
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public static class Limits
|
||||
{
|
||||
public static readonly int MaxProxyNameLength = 80;
|
||||
|
|
@ -6,7 +7,7 @@ namespace PluralKit.Core {
|
|||
public static readonly int MaxSystemNameLength = 100;
|
||||
public static readonly int MaxSystemTagLength = MaxProxyNameLength - 1;
|
||||
public static readonly int MaxMemberCount = 1000;
|
||||
public static int MaxMembersWarnThreshold (int memberLimit) => memberLimit - 50;
|
||||
public static int MaxMembersWarnThreshold(int memberLimit) => memberLimit - 50;
|
||||
public static readonly int MaxGroupCount = 250;
|
||||
public static readonly int MaxDescriptionLength = 1000;
|
||||
public static readonly int MaxProxyTagLength = 100;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ namespace PluralKit.Core
|
|||
try
|
||||
{
|
||||
uri = new Uri(input);
|
||||
if (!uri.IsAbsoluteUri || (uri.Scheme != "http" && uri.Scheme != "https"))
|
||||
if (!uri.IsAbsoluteUri || (uri.Scheme != "http" && uri.Scheme != "https"))
|
||||
return false;
|
||||
}
|
||||
catch (UriFormatException)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
|
@ -20,7 +20,7 @@ namespace PluralKit.Core
|
|||
if (str != null) return str.Length > length;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static string ExtractCountryFlag(string flag)
|
||||
{
|
||||
if (flag.Length != 4) return null;
|
||||
|
|
@ -30,14 +30,14 @@ namespace PluralKit.Core
|
|||
var cp2 = char.ConvertToUtf32(flag, 2);
|
||||
if (cp1 < 0x1F1E6 || cp1 > 0x1F1FF) return null;
|
||||
if (cp2 < 0x1F1E6 || cp2 > 0x1F1FF) return null;
|
||||
return $"{(char) (cp1 - 0x1F1E6 + 'A')}{(char) (cp2 - 0x1F1E6 + 'A')}";
|
||||
return $"{(char)(cp1 - 0x1F1E6 + 'A')}{(char)(cp2 - 0x1F1E6 + 'A')}";
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static string NullIfEmpty(this string input)
|
||||
{
|
||||
if (input == null) return null;
|
||||
|
|
@ -70,7 +70,7 @@ namespace PluralKit.Core
|
|||
foreach (var s in input)
|
||||
{
|
||||
var limit = characterLimitByPage.Invoke(output.Count);
|
||||
|
||||
|
||||
// Would adding this string put us over the limit?
|
||||
// (note: don't roll over if the buffer's already empty; this means an individual section is above the character limit. todo: truncate, then?)
|
||||
if (buf.Length > 0 && buf.Length + s.Length > limit)
|
||||
|
|
@ -82,12 +82,12 @@ namespace PluralKit.Core
|
|||
|
||||
buf.Append(s);
|
||||
}
|
||||
|
||||
|
||||
// We most likely have something left over, so add that in too
|
||||
if (buf.Length > 0)
|
||||
output.Add(buf.ToString());
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,24 +2,35 @@ using System;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PluralKit.Core {
|
||||
public static class TaskUtils {
|
||||
public static async Task CatchException(this Task task, Action<Exception> handler) {
|
||||
try {
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public static class TaskUtils
|
||||
{
|
||||
public static async Task CatchException(this Task task, Action<Exception> handler)
|
||||
{
|
||||
try
|
||||
{
|
||||
await task;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
handler(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan? timeout) {
|
||||
|
||||
public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan? timeout)
|
||||
{
|
||||
// https://stackoverflow.com/a/22078975
|
||||
using (var timeoutCancellationTokenSource = new CancellationTokenSource()) {
|
||||
using (var timeoutCancellationTokenSource = new CancellationTokenSource())
|
||||
{
|
||||
var completedTask = await Task.WhenAny(task, Task.Delay(timeout ?? TimeSpan.FromMilliseconds(-1), timeoutCancellationTokenSource.Token));
|
||||
if (completedTask == task) {
|
||||
if (completedTask == task)
|
||||
{
|
||||
timeoutCancellationTokenSource.Cancel();
|
||||
return await task; // Very important in order to propagate exceptions
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TimeoutException();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue