diff --git a/PluralKit.Bot/Commands/Autoproxy.cs b/PluralKit.Bot/Commands/Autoproxy.cs index 10e6fae4..645ab602 100644 --- a/PluralKit.Bot/Commands/Autoproxy.cs +++ b/PluralKit.Bot/Commands/Autoproxy.cs @@ -122,11 +122,11 @@ namespace PluralKit.Bot return eb.Build(); } - - private Task UpdateAutoproxy(Context ctx, AutoproxyMode autoproxyMode, MemberId? autoproxyMember) => - _db.Execute(c => - c.ExecuteAsync( - "insert into system_guild (system, guild, autoproxy_mode, autoproxy_member) values (@system, @guild, @autoproxyMode, @autoproxyMember) on conflict (system, guild) do update set autoproxy_mode = @autoproxyMode, autoproxy_member = @autoproxyMember", - new {autoproxyMode, autoproxyMember, guild = ctx.Guild.Id, system = ctx.System.Id})); + + private Task UpdateAutoproxy(Context ctx, AutoproxyMode autoproxyMode, MemberId? autoproxyMember) + { + var patch = new SystemGuildPatch {AutoproxyMode = autoproxyMode, AutoproxyMember = autoproxyMember}; + return _db.Execute(conn => conn.UpsertSystemGuild(ctx.System.Id, ctx.Guild.Id, patch)); + } } } \ No newline at end of file diff --git a/PluralKit.Bot/Commands/MemberAvatar.cs b/PluralKit.Bot/Commands/MemberAvatar.cs index 9226cccb..c55a8b97 100644 --- a/PluralKit.Bot/Commands/MemberAvatar.cs +++ b/PluralKit.Bot/Commands/MemberAvatar.cs @@ -139,19 +139,20 @@ namespace PluralKit.Bot else throw new Exception("Unexpected condition when parsing avatar command"); } - private Task UpdateAvatar(AvatarLocation location, Context ctx, PKMember target, string? avatar) => - location switch + private Task UpdateAvatar(AvatarLocation location, Context ctx, PKMember target, string? avatar) + { + switch (location) { - AvatarLocation.Server => _db.Execute(c => - c.ExecuteAsync( - "insert into member_guild(member, guild, avatar_url) values (@Member, @Guild, @Avatar) on conflict (member, guild) do update set avatar_url = @Avatar", - new {Avatar = avatar, Guild = ctx.Guild.Id, Member = target.Id})), - AvatarLocation.Member => _db.Execute(c => - c.ExecuteAsync( - "update members set avatar_url = @Avatar where id = @Member", - new {Avatar = avatar, Member = target.Id})), - _ => throw new ArgumentOutOfRangeException($"Unknown avatar location {location}") - }; + case AvatarLocation.Server: + var serverPatch = new MemberGuildPatch { AvatarUrl = avatar }; + return _db.Execute(c => c.UpsertMemberGuild(target.Id, ctx.Guild.Id, serverPatch)); + case AvatarLocation.Member: + var memberPatch = new MemberPatch { AvatarUrl = avatar }; + return _db.Execute(c => c.UpdateMember(target.Id, memberPatch)); + default: + throw new ArgumentOutOfRangeException($"Unknown avatar location {location}"); + } + } private enum AvatarLocation { diff --git a/PluralKit.Bot/Commands/MemberEdit.cs b/PluralKit.Bot/Commands/MemberEdit.cs index 6706c4d4..8038bdb9 100644 --- a/PluralKit.Bot/Commands/MemberEdit.cs +++ b/PluralKit.Bot/Commands/MemberEdit.cs @@ -319,9 +319,8 @@ namespace PluralKit.Bot { CheckEditMemberPermission(ctx, target); - await _db.Execute(c => - c.ExecuteAsync("update member_guild set display_name = null where member = @member and guild = @guild", - new {member = target.Id, guild = ctx.Guild.Id})); + var patch = new MemberGuildPatch {DisplayName = null}; + await _db.Execute(conn => conn.UpsertMemberGuild(target.Id, ctx.Guild.Id, patch)); if (target.DisplayName != null) await ctx.Reply($"{Emojis.Success} Member server name cleared. This member will now be proxied using their global display name \"{target.DisplayName}\" in this server ({ctx.Guild.Name})."); @@ -341,10 +340,9 @@ namespace PluralKit.Bot CheckEditMemberPermission(ctx, target); var newServerName = ctx.RemainderOrNull(); - - await _db.Execute(c => - c.ExecuteAsync("insert into member_guild(member, guild, display_name) values (@member, @guild, @newServerName) on conflict (member, guild) do update set display_name = @newServerName", - new {member = target.Id, guild = ctx.Guild.Id, newServerName})); + + var patch = new MemberGuildPatch {DisplayName = newServerName}; + await _db.Execute(conn => conn.UpsertMemberGuild(target.Id, ctx.Guild.Id, patch)); await ctx.Reply($"{Emojis.Success} Member server name changed. This member will now be proxied using the name \"{newServerName}\" in this server ({ctx.Guild.Name})."); } diff --git a/PluralKit.Bot/Commands/ServerConfig.cs b/PluralKit.Bot/Commands/ServerConfig.cs index de089244..9dd29d22 100644 --- a/PluralKit.Bot/Commands/ServerConfig.cs +++ b/PluralKit.Bot/Commands/ServerConfig.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Dapper; - using DSharpPlus; using DSharpPlus.Entities; @@ -30,10 +28,8 @@ namespace PluralKit.Bot channel = await ctx.MatchChannel() ?? throw new PKSyntaxError("You must pass a #channel to set."); if (channel != null && channel.GuildId != ctx.Guild.Id) throw new PKError("That channel is not in this server!"); - await _db.Execute(c => c.ExecuteAsync(QueryBuilder.Upsert("servers", "id") - .Constant("id", "@Id") - .Variable("log_channel", "@LogChannel") - .Build(), new {Id = ctx.Guild.Id, LogChannel = channel?.Id})); + var patch = new GuildPatch {LogChannel = channel?.Id}; + await _db.Execute(conn => conn.UpsertGuild(ctx.Guild.Id, patch)); if (channel != null) await ctx.Reply($"{Emojis.Success} Proxy logging channel set to #{channel.Name}."); @@ -66,8 +62,9 @@ namespace PluralKit.Bot blacklist.ExceptWith(affectedChannels.Select(c => c.Id)); else blacklist.UnionWith(affectedChannels.Select(c => c.Id)); - await conn.ExecuteAsync("update servers set log_blacklist = @LogBlacklist where id = @Id", - new {ctx.Guild.Id, LogBlacklist = blacklist.ToArray()}); + + var patch = new GuildPatch {LogBlacklist = blacklist.ToArray()}; + await conn.UpsertGuild(ctx.Guild.Id, patch); } await ctx.Reply( @@ -98,8 +95,9 @@ namespace PluralKit.Bot blacklist.UnionWith(affectedChannels.Select(c => c.Id)); else blacklist.ExceptWith(affectedChannels.Select(c => c.Id)); - await conn.ExecuteAsync("update servers set blacklist = @Blacklist where id = @Id", - new {ctx.Guild.Id, Blacklist = blacklist.ToArray()}); + + var patch = new GuildPatch {Blacklist = blacklist.ToArray()}; + await conn.UpsertGuild(ctx.Guild.Id, patch); } await ctx.Reply($"{Emojis.Success} Channels {(shouldAdd ? "added to" : "removed from")} the proxy blacklist."); @@ -131,9 +129,9 @@ namespace PluralKit.Bot return; } - await _db.Execute(c => c.ExecuteAsync("update servers set log_cleanup_enabled = @Value where id = @Id", - new {ctx.Guild.Id, Value = newValue})); - + var patch = new GuildPatch {LogCleanupEnabled = newValue}; + await _db.Execute(conn => conn.UpsertGuild(ctx.Guild.Id, patch)); + if (newValue) await ctx.Reply($"{Emojis.Success} Log cleanup has been **enabled** for this server. Messages deleted by PluralKit will now be cleaned up from logging channels managed by the following bots:\n- **{botList}**\n\n{Emojis.Note} Make sure PluralKit has the **Manage Messages** permission in the channels in question.\n{Emojis.Note} Also, make sure to blacklist the logging channel itself from the bots in question to prevent conflicts."); else diff --git a/PluralKit.Bot/Commands/SystemEdit.cs b/PluralKit.Bot/Commands/SystemEdit.cs index 6dd31270..ebb34e9c 100644 --- a/PluralKit.Bot/Commands/SystemEdit.cs +++ b/PluralKit.Bot/Commands/SystemEdit.cs @@ -214,9 +214,8 @@ namespace PluralKit.Bot return; } - await _db.Execute(c => - c.ExecuteAsync("update system_guild set proxy_enabled = @newValue where system = @system and guild = @guild", - new {newValue, system = ctx.System.Id, guild = ctx.Guild.Id})); + var patch = new SystemGuildPatch {ProxyEnabled = newValue}; + await _db.Execute(conn => conn.UpsertSystemGuild(ctx.System.Id, ctx.Guild.Id, patch)); if (newValue) await ctx.Reply($"Message proxying in this server ({ctx.Guild.Name.EscapeMarkdown()}) is now **enabled** for your system."); diff --git a/PluralKit.Core/Models/Patch/GuildPatch.cs b/PluralKit.Core/Models/Patch/GuildPatch.cs new file mode 100644 index 00000000..2e9849e2 --- /dev/null +++ b/PluralKit.Core/Models/Patch/GuildPatch.cs @@ -0,0 +1,16 @@ +namespace PluralKit.Core +{ + public class GuildPatch: PatchObject + { + public Partial LogChannel { get; set; } + public Partial LogBlacklist { get; set; } + public Partial Blacklist { get; set; } + public Partial LogCleanupEnabled { get; set; } + + public override UpdateQueryBuilder Apply(UpdateQueryBuilder b) => b + .With("log_channel", LogChannel) + .With("log_blacklist", LogBlacklist) + .With("blacklist", Blacklist) + .With("log_cleanup_enabled", LogCleanupEnabled); + } +} \ No newline at end of file diff --git a/PluralKit.Core/Models/Patch/MemberGuildPatch.cs b/PluralKit.Core/Models/Patch/MemberGuildPatch.cs new file mode 100644 index 00000000..39b7dabd --- /dev/null +++ b/PluralKit.Core/Models/Patch/MemberGuildPatch.cs @@ -0,0 +1,13 @@ +#nullable enable +namespace PluralKit.Core +{ + public class MemberGuildPatch: PatchObject + { + public Partial DisplayName { get; set; } + public Partial AvatarUrl { get; set; } + + public override UpdateQueryBuilder Apply(UpdateQueryBuilder b) => b + .With("display_name", DisplayName) + .With("avatar_url", AvatarUrl); + } +} \ No newline at end of file diff --git a/PluralKit.Core/Models/Patch/ModelPatchExt.cs b/PluralKit.Core/Models/Patch/ModelPatchExt.cs index a87632d5..05b0a471 100644 --- a/PluralKit.Core/Models/Patch/ModelPatchExt.cs +++ b/PluralKit.Core/Models/Patch/ModelPatchExt.cs @@ -8,7 +8,7 @@ namespace PluralKit.Core { public static Task UpdateSystem(this IPKConnection conn, SystemId id, SystemPatch patch) { - var (query, pms) = patch.Apply(new UpdateQueryBuilder("systems", "id = @id")) + var (query, pms) = patch.Apply(UpdateQueryBuilder.Update("systems", "id = @id")) .WithConstant("id", id) .Build("returning *"); return conn.QueryFirstAsync(query, pms); @@ -24,7 +24,7 @@ namespace PluralKit.Core public static Task UpdateMember(this IPKConnection conn, MemberId id, MemberPatch patch) { - var (query, pms) = patch.Apply(new UpdateQueryBuilder("members", "id = @id")) + var (query, pms) = patch.Apply(UpdateQueryBuilder.Update("members", "id = @id")) .WithConstant("id", id) .Build("returning *"); return conn.QueryFirstAsync(query, pms); @@ -32,5 +32,33 @@ namespace PluralKit.Core public static Task DeleteMember(this IPKConnection conn, MemberId id) => conn.ExecuteAsync("delete from members where id = @Id", new {Id = id}); + + public static Task UpsertSystemGuild(this IPKConnection conn, SystemId system, ulong guild, + SystemGuildPatch patch) + { + var (query, pms) = patch.Apply(UpdateQueryBuilder.Upsert("system_guild", "system, guild")) + .WithConstant("system", system) + .WithConstant("guild", guild) + .Build(); + return conn.ExecuteAsync(query, pms); + } + + public static Task UpsertMemberGuild(this IPKConnection conn, MemberId member, ulong guild, + MemberGuildPatch patch) + { + var (query, pms) = patch.Apply(UpdateQueryBuilder.Upsert("member_guild", "member, guild")) + .WithConstant("member", member) + .WithConstant("guild", guild) + .Build(); + return conn.ExecuteAsync(query, pms); + } + + public static Task UpsertGuild(this IPKConnection conn, ulong guild, GuildPatch patch) + { + var (query, pms) = patch.Apply(UpdateQueryBuilder.Upsert("servers", "id")) + .WithConstant("id", guild) + .Build(); + return conn.ExecuteAsync(query, pms); + } } } \ No newline at end of file diff --git a/PluralKit.Core/Models/Patch/SystemGuildPatch.cs b/PluralKit.Core/Models/Patch/SystemGuildPatch.cs new file mode 100644 index 00000000..51b68e2a --- /dev/null +++ b/PluralKit.Core/Models/Patch/SystemGuildPatch.cs @@ -0,0 +1,15 @@ +#nullable enable +namespace PluralKit.Core +{ + public class SystemGuildPatch: PatchObject + { + public Partial ProxyEnabled { get; set; } + public Partial AutoproxyMode { get; set; } + public Partial AutoproxyMember { get; set; } + + public override UpdateQueryBuilder Apply(UpdateQueryBuilder b) => b + .With("proxy_enabled", ProxyEnabled) + .With("autoproxy_mode", AutoproxyMode) + .With("autoproxy_member", AutoproxyMember); + } +} \ No newline at end of file diff --git a/PluralKit.Core/Utils/QueryBuilder.cs b/PluralKit.Core/Utils/QueryBuilder.cs index 5494ae24..14c4502e 100644 --- a/PluralKit.Core/Utils/QueryBuilder.cs +++ b/PluralKit.Core/Utils/QueryBuilder.cs @@ -50,7 +50,7 @@ namespace PluralKit.Core else _updateFragment.Append(", "); _updateFragment.Append(fieldName); - _updateFragment.Append("="); + _updateFragment.Append(" = "); _updateFragment.Append(paramName); return this; } @@ -71,7 +71,7 @@ namespace PluralKit.Core if (Type == QueryType.Update && _condition != null) query.Append($" where {_condition}"); - if (suffix != null) + if (!string.IsNullOrEmpty(suffix)) query.Append($" {suffix}"); query.Append(";"); diff --git a/PluralKit.Core/Utils/UpdateQueryBuilder.cs b/PluralKit.Core/Utils/UpdateQueryBuilder.cs index 549e2e32..805a4712 100644 --- a/PluralKit.Core/Utils/UpdateQueryBuilder.cs +++ b/PluralKit.Core/Utils/UpdateQueryBuilder.cs @@ -6,34 +6,29 @@ namespace PluralKit.Core { public class UpdateQueryBuilder { - private readonly string _table; - private readonly string _condition; + private readonly QueryBuilder _qb; private readonly DynamicParameters _params = new DynamicParameters(); - private bool _hasFields = false; - private readonly StringBuilder _setClause = new StringBuilder(); - - public UpdateQueryBuilder(string table, string condition) + private UpdateQueryBuilder(QueryBuilder qb) { - _table = table; - _condition = condition; + _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)); public UpdateQueryBuilder WithConstant(string name, T value) { _params.Add(name, value); + _qb.Constant(name, $"@{name}"); return this; } public UpdateQueryBuilder With(string columnName, T value) { _params.Add(columnName, value); - - if (_hasFields) - _setClause.Append(", "); - else _hasFields = true; - - _setClause.Append($"{columnName} = @{columnName}"); + _qb.Variable(columnName, $"@{columnName}"); return this; } @@ -42,10 +37,9 @@ namespace PluralKit.Core return partialValue.IsPresent ? With(columnName, partialValue.Value) : this; } - public (string Query, DynamicParameters Parameters) Build(string append = "") + public (string Query, DynamicParameters Parameters) Build(string suffix = "") { - var query = $"update {_table} set {_setClause} where {_condition} {append}"; - return (query, _params); + return (_qb.Build(suffix), _params); } } } \ No newline at end of file