run dotnet format

This commit is contained in:
spiral 2021-08-27 11:03:47 -04:00
parent 05989242f9
commit ac2671452d
No known key found for this signature in database
GPG key ID: A6059F0CA0E1BD31
278 changed files with 1913 additions and 1808 deletions

View file

@ -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" };
}

View file

@ -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;
}

View file

@ -1,4 +1,4 @@
#nullable enable
#nullable enable
using NodaTime;

View file

@ -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; }

View file

@ -1,4 +1,4 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
namespace PluralKit.Core
{

View file

@ -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; }
}
}
}

View file

@ -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);
}
}
}
}

View file

@ -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() });
}
}
}

View file

@ -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() });
}
}

View file

@ -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 });
}
}

View file

@ -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 });
}
}
}

View file

@ -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;

View file

@ -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 });
}
}

View file

@ -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;

View file

@ -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 });
}
}
}

View file

@ -1,4 +1,4 @@
using Serilog;
using Serilog;
namespace PluralKit.Core
{

View file

@ -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);
}
}
}

View file

@ -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;
}
}
}
}

View file

@ -1,4 +1,4 @@
using System.Threading;
using System.Threading;
namespace PluralKit.Core
{

View file

@ -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(";");

View file

@ -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));

View file

@ -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;

View file

@ -1,6 +1,6 @@
namespace PluralKit.Core
namespace PluralKit.Core
{
public class ListedGroup : PKGroup
public class ListedGroup: PKGroup
{
public int MemberCount { get; }
}

View file

@ -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;
}
}

View file

@ -1,4 +1,4 @@
using NodaTime;
using NodaTime;
namespace PluralKit.Core
{

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Data;
using System.Data.Common;
using System.Threading;

View file

@ -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();

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Data;
using System.Threading;
using System.Threading.Tasks;

View file

@ -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}!");
}
}

View file

@ -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);
}

View file

@ -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}!");
}
}

View file

@ -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())
{

View file

@ -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)
{

View file

@ -1,4 +1,4 @@
namespace PluralKit.Core
namespace PluralKit.Core
{
public class GuildConfig
{

View file

@ -1,4 +1,4 @@
#nullable enable
#nullable enable
namespace PluralKit.Core
{
public class MemberGuildSettings

View file

@ -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; }
}

View file

@ -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) =>

View file

@ -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());
}

View file

@ -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);

View file

@ -1,4 +1,4 @@
using NodaTime;
using NodaTime;
namespace PluralKit.Core
{

View file

@ -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}";
}

View file

@ -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;
}
}
}
}

View file

@ -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);
}
}

View file

@ -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; }

View file

@ -1,4 +1,4 @@
namespace PluralKit.Core
namespace PluralKit.Core
{
public class GuildPatch: PatchObject
{

View file

@ -1,4 +1,4 @@
#nullable enable
#nullable enable
namespace PluralKit.Core
{
public class MemberGuildPatch: PatchObject

View file

@ -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;

View file

@ -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;

View file

@ -1,4 +1,4 @@
#nullable enable
#nullable enable
namespace PluralKit.Core
{
public class SystemGuildPatch: PatchObject

View file

@ -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");

View file

@ -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;
}

View file

@ -1,4 +1,4 @@
namespace PluralKit.Core
namespace PluralKit.Core
{
public enum LookupContext
{

View file

@ -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;
}

View file

@ -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)

View file

@ -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;
}
}
}
}

View file

@ -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
}
}
}
}
}

View file

@ -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; }
}
}

View file

@ -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;

View file

@ -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());
}
}

View file

@ -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
{

View file

@ -1,4 +1,4 @@
using App.Metrics;
using App.Metrics;
using Autofac;

View file

@ -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);
}
}

View file

@ -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) { }
}
}

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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)
{

View file

@ -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";

View file

@ -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

View file

@ -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)

View file

@ -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;

View file

@ -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)

View file

@ -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;
}
}
}
}

View file

@ -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();
}
}