refactor: don't DI IDatabase and ModelRepository into bot command classes

This commit is contained in:
spiral 2022-01-22 03:05:01 -05:00
parent 89c44a3482
commit df6a6fcf12
No known key found for this signature in database
GPG Key ID: A6059F0CA0E1BD31
20 changed files with 194 additions and 284 deletions

View File

@ -7,12 +7,10 @@ namespace PluralKit.Bot;
public class Admin public class Admin
{ {
private readonly BotConfig _botConfig; private readonly BotConfig _botConfig;
private readonly ModelRepository _repo;
public Admin(BotConfig botConfig, ModelRepository repo) public Admin(BotConfig botConfig)
{ {
_botConfig = botConfig; _botConfig = botConfig;
_repo = repo;
} }
public async Task UpdateSystemId(Context ctx) public async Task UpdateSystemId(Context ctx)
@ -27,14 +25,14 @@ public class Admin
if (!Regex.IsMatch(newHid, "^[a-z]{5}$")) if (!Regex.IsMatch(newHid, "^[a-z]{5}$"))
throw new PKError($"Invalid new system ID `{newHid}`."); throw new PKError($"Invalid new system ID `{newHid}`.");
var existingSystem = await _repo.GetSystemByHid(newHid); var existingSystem = await ctx.Repository.GetSystemByHid(newHid);
if (existingSystem != null) if (existingSystem != null)
throw new PKError($"Another system already exists with ID `{newHid}`."); throw new PKError($"Another system already exists with ID `{newHid}`.");
if (!await ctx.PromptYesNo($"Change system ID of `{target.Hid}` to `{newHid}`?", "Change")) if (!await ctx.PromptYesNo($"Change system ID of `{target.Hid}` to `{newHid}`?", "Change"))
throw new PKError("ID change cancelled."); throw new PKError("ID change cancelled.");
await _repo.UpdateSystem(target.Id, new SystemPatch { Hid = newHid }); await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { Hid = newHid });
await ctx.Reply($"{Emojis.Success} System ID updated (`{target.Hid}` -> `{newHid}`)."); await ctx.Reply($"{Emojis.Success} System ID updated (`{target.Hid}` -> `{newHid}`).");
} }
@ -50,7 +48,7 @@ public class Admin
if (!Regex.IsMatch(newHid, "^[a-z]{5}$")) if (!Regex.IsMatch(newHid, "^[a-z]{5}$"))
throw new PKError($"Invalid new member ID `{newHid}`."); throw new PKError($"Invalid new member ID `{newHid}`.");
var existingMember = await _repo.GetMemberByHid(newHid); var existingMember = await ctx.Repository.GetMemberByHid(newHid);
if (existingMember != null) if (existingMember != null)
throw new PKError($"Another member already exists with ID `{newHid}`."); throw new PKError($"Another member already exists with ID `{newHid}`.");
@ -60,7 +58,7 @@ public class Admin
)) ))
throw new PKError("ID change cancelled."); throw new PKError("ID change cancelled.");
await _repo.UpdateMember(target.Id, new MemberPatch { Hid = newHid }); await ctx.Repository.UpdateMember(target.Id, new MemberPatch { Hid = newHid });
await ctx.Reply($"{Emojis.Success} Member ID updated (`{target.Hid}` -> `{newHid}`)."); await ctx.Reply($"{Emojis.Success} Member ID updated (`{target.Hid}` -> `{newHid}`).");
} }
@ -76,7 +74,7 @@ public class Admin
if (!Regex.IsMatch(newHid, "^[a-z]{5}$")) if (!Regex.IsMatch(newHid, "^[a-z]{5}$"))
throw new PKError($"Invalid new group ID `{newHid}`."); throw new PKError($"Invalid new group ID `{newHid}`.");
var existingGroup = await _repo.GetGroupByHid(newHid); var existingGroup = await ctx.Repository.GetGroupByHid(newHid);
if (existingGroup != null) if (existingGroup != null)
throw new PKError($"Another group already exists with ID `{newHid}`."); throw new PKError($"Another group already exists with ID `{newHid}`.");
@ -85,7 +83,7 @@ public class Admin
)) ))
throw new PKError("ID change cancelled."); throw new PKError("ID change cancelled.");
await _repo.UpdateGroup(target.Id, new GroupPatch { Hid = newHid }); await ctx.Repository.UpdateGroup(target.Id, new GroupPatch { Hid = newHid });
await ctx.Reply($"{Emojis.Success} Group ID updated (`{target.Hid}` -> `{newHid}`)."); await ctx.Reply($"{Emojis.Success} Group ID updated (`{target.Hid}` -> `{newHid}`).");
} }
@ -97,7 +95,7 @@ public class Admin
if (target == null) if (target == null)
throw new PKError("Unknown system."); throw new PKError("Unknown system.");
var config = await _repo.GetSystemConfig(target.Id); var config = await ctx.Repository.GetSystemConfig(target.Id);
var currentLimit = config.MemberLimitOverride ?? Limits.MaxMemberCount; var currentLimit = config.MemberLimitOverride ?? Limits.MaxMemberCount;
if (!ctx.HasNext()) if (!ctx.HasNext())
@ -113,7 +111,7 @@ public class Admin
if (!await ctx.PromptYesNo($"Update member limit from **{currentLimit}** to **{newLimit}**?", "Update")) if (!await ctx.PromptYesNo($"Update member limit from **{currentLimit}** to **{newLimit}**?", "Update"))
throw new PKError("Member limit change cancelled."); throw new PKError("Member limit change cancelled.");
await _repo.UpdateSystemConfig(target.Id, new SystemConfigPatch { MemberLimitOverride = newLimit }); await ctx.Repository.UpdateSystemConfig(target.Id, new SystemConfigPatch { MemberLimitOverride = newLimit });
await ctx.Reply($"{Emojis.Success} Member limit updated."); await ctx.Reply($"{Emojis.Success} Member limit updated.");
} }
@ -125,7 +123,7 @@ public class Admin
if (target == null) if (target == null)
throw new PKError("Unknown system."); throw new PKError("Unknown system.");
var config = await _repo.GetSystemConfig(target.Id); var config = await ctx.Repository.GetSystemConfig(target.Id);
var currentLimit = config.GroupLimitOverride ?? Limits.MaxGroupCount; var currentLimit = config.GroupLimitOverride ?? Limits.MaxGroupCount;
if (!ctx.HasNext()) if (!ctx.HasNext())
@ -141,7 +139,7 @@ public class Admin
if (!await ctx.PromptYesNo($"Update group limit from **{currentLimit}** to **{newLimit}**?", "Update")) if (!await ctx.PromptYesNo($"Update group limit from **{currentLimit}** to **{newLimit}**?", "Update"))
throw new PKError("Group limit change cancelled."); throw new PKError("Group limit change cancelled.");
await _repo.UpdateSystemConfig(target.Id, new SystemConfigPatch { GroupLimitOverride = newLimit }); await ctx.Repository.UpdateSystemConfig(target.Id, new SystemConfigPatch { GroupLimitOverride = newLimit });
await ctx.Reply($"{Emojis.Success} Group limit updated."); await ctx.Reply($"{Emojis.Success} Group limit updated.");
} }
} }

View File

@ -16,13 +16,11 @@ public class Api
private readonly BotConfig _botConfig; private readonly BotConfig _botConfig;
private readonly DispatchService _dispatch; private readonly DispatchService _dispatch;
private readonly ModelRepository _repo;
private readonly PrivateChannelService _dmCache; private readonly PrivateChannelService _dmCache;
public Api(BotConfig botConfig, ModelRepository repo, DispatchService dispatch, PrivateChannelService dmCache) public Api(BotConfig botConfig, DispatchService dispatch, PrivateChannelService dmCache)
{ {
_botConfig = botConfig; _botConfig = botConfig;
_repo = repo;
_dispatch = dispatch; _dispatch = dispatch;
_dmCache = dmCache; _dmCache = dmCache;
} }
@ -32,7 +30,7 @@ public class Api
ctx.CheckSystem(); ctx.CheckSystem();
// Get or make a token // Get or make a token
var token = ctx.System.Token ?? await MakeAndSetNewToken(ctx.System); var token = ctx.System.Token ?? await MakeAndSetNewToken(ctx, ctx.System);
try try
{ {
@ -65,9 +63,9 @@ public class Api
} }
} }
private async Task<string> MakeAndSetNewToken(PKSystem system) private async Task<string> MakeAndSetNewToken(Context ctx, PKSystem system)
{ {
system = await _repo.UpdateSystem(system.Id, new SystemPatch { Token = StringUtils.GenerateToken() }); system = await ctx.Repository.UpdateSystem(system.Id, new SystemPatch { Token = StringUtils.GenerateToken() });
return system.Token; return system.Token;
} }
@ -95,7 +93,7 @@ public class Api
// Make the new token after sending the first DM; this ensures if we can't DM, we also don't end up // Make the new token after sending the first DM; this ensures if we can't DM, we also don't end up
// breaking their existing token as a side effect :) // breaking their existing token as a side effect :)
var token = await MakeAndSetNewToken(ctx.System); var token = await MakeAndSetNewToken(ctx, ctx.System);
await ctx.Rest.CreateMessage(dm, new MessageRequest { Content = token }); await ctx.Rest.CreateMessage(dm, new MessageRequest { Content = token });
if (_botConfig.IsBetaBot) if (_botConfig.IsBetaBot)
@ -132,7 +130,7 @@ public class Api
if (await ctx.MatchClear("your system's webhook URL")) if (await ctx.MatchClear("your system's webhook URL"))
{ {
await _repo.UpdateSystem(ctx.System.Id, new SystemPatch { WebhookUrl = null, WebhookToken = null }); await ctx.Repository.UpdateSystem(ctx.System.Id, new SystemPatch { WebhookUrl = null, WebhookToken = null });
await ctx.Reply($"{Emojis.Success} System webhook URL removed."); await ctx.Reply($"{Emojis.Success} System webhook URL removed.");
return; return;
@ -156,7 +154,7 @@ public class Api
var newToken = StringUtils.GenerateToken(); var newToken = StringUtils.GenerateToken();
await _repo.UpdateSystem(ctx.System.Id, new SystemPatch { WebhookUrl = newUrl, WebhookToken = newToken }); await ctx.Repository.UpdateSystem(ctx.System.Id, new SystemPatch { WebhookUrl = newUrl, WebhookToken = newToken });
await ctx.Reply($"{Emojis.Success} Successfully the new webhook URL for your system." await ctx.Reply($"{Emojis.Success} Successfully the new webhook URL for your system."
+ $"\n\n{Emojis.Warn} The following token is used to authenticate requests from PluralKit to you." + $"\n\n{Emojis.Warn} The following token is used to authenticate requests from PluralKit to you."

View File

@ -11,15 +11,6 @@ namespace PluralKit.Bot;
public class Autoproxy public class Autoproxy
{ {
private readonly IDatabase _db;
private readonly ModelRepository _repo;
public Autoproxy(IDatabase db, ModelRepository repo)
{
_db = db;
_repo = repo;
}
public async Task SetAutoproxyMode(Context ctx) public async Task SetAutoproxyMode(Context ctx)
{ {
// no need to check account here, it's already done at CommandTree // no need to check account here, it's already done at CommandTree
@ -99,8 +90,8 @@ public class Autoproxy
var fronters = ctx.MessageContext.LastSwitchMembers; var fronters = ctx.MessageContext.LastSwitchMembers;
var relevantMember = ctx.MessageContext.AutoproxyMode switch var relevantMember = ctx.MessageContext.AutoproxyMode switch
{ {
AutoproxyMode.Front => fronters.Length > 0 ? await _repo.GetMember(fronters[0]) : null, AutoproxyMode.Front => fronters.Length > 0 ? await ctx.Repository.GetMember(fronters[0]) : null,
AutoproxyMode.Member => await _repo.GetMember(ctx.MessageContext.AutoproxyMember.Value), AutoproxyMode.Member => await ctx.Repository.GetMember(ctx.MessageContext.AutoproxyMember.Value),
_ => null _ => null
}; };
@ -145,9 +136,9 @@ public class Autoproxy
private async Task UpdateAutoproxy(Context ctx, AutoproxyMode autoproxyMode, MemberId? autoproxyMember) private async Task UpdateAutoproxy(Context ctx, AutoproxyMode autoproxyMode, MemberId? autoproxyMember)
{ {
await _repo.GetSystemGuild(ctx.Guild.Id, ctx.System.Id); await ctx.Repository.GetSystemGuild(ctx.Guild.Id, ctx.System.Id);
var patch = new SystemGuildPatch { AutoproxyMode = autoproxyMode, AutoproxyMember = autoproxyMember }; var patch = new SystemGuildPatch { AutoproxyMode = autoproxyMode, AutoproxyMember = autoproxyMember };
await _repo.UpdateSystemGuild(ctx.System.Id, ctx.Guild.Id, patch); await ctx.Repository.UpdateSystemGuild(ctx.System.Id, ctx.Guild.Id, patch);
} }
} }

View File

@ -15,10 +15,8 @@ public class Checks
{ {
private readonly BotConfig _botConfig; private readonly BotConfig _botConfig;
private readonly IDiscordCache _cache; private readonly IDiscordCache _cache;
private readonly IDatabase _db;
private readonly ProxyMatcher _matcher; private readonly ProxyMatcher _matcher;
private readonly ProxyService _proxy; private readonly ProxyService _proxy;
private readonly ModelRepository _repo;
private readonly DiscordApiClient _rest; private readonly DiscordApiClient _rest;
private readonly PermissionSet[] requiredPermissions = private readonly PermissionSet[] requiredPermissions =
@ -28,13 +26,10 @@ public class Checks
PermissionSet.ManageWebhooks, PermissionSet.ReadMessageHistory PermissionSet.ManageWebhooks, PermissionSet.ReadMessageHistory
}; };
public Checks(DiscordApiClient rest, IDiscordCache cache, IDatabase db, ModelRepository repo, public Checks(DiscordApiClient rest, IDiscordCache cache, BotConfig botConfig, ProxyService proxy, ProxyMatcher matcher)
BotConfig botConfig, ProxyService proxy, ProxyMatcher matcher)
{ {
_rest = rest; _rest = rest;
_cache = cache; _cache = cache;
_db = db;
_repo = repo;
_botConfig = botConfig; _botConfig = botConfig;
_proxy = proxy; _proxy = proxy;
_matcher = matcher; _matcher = matcher;
@ -216,7 +211,7 @@ public class Checks
if (messageId == null || channelId == null) if (messageId == null || channelId == null)
throw new PKError(failedToGetMessage); throw new PKError(failedToGetMessage);
var proxiedMsg = await _db.Execute(conn => _repo.GetMessage(conn, messageId.Value)); var proxiedMsg = await ctx.Database.Execute(conn => ctx.Repository.GetMessage(conn, messageId.Value));
if (proxiedMsg != null) if (proxiedMsg != null)
{ {
await ctx.Reply($"{Emojis.Success} This message was proxied successfully."); await ctx.Reply($"{Emojis.Success} This message was proxied successfully.");
@ -250,8 +245,8 @@ public class Checks
throw new PKError("Unable to get the channel associated with this message."); throw new PKError("Unable to get the channel associated with this message.");
// using channel.GuildId here since _rest.GetMessage() doesn't return the GuildId // using channel.GuildId here since _rest.GetMessage() doesn't return the GuildId
var context = await _repo.GetMessageContext(msg.Author.Id, channel.GuildId.Value, msg.ChannelId); var context = await ctx.Repository.GetMessageContext(msg.Author.Id, channel.GuildId.Value, msg.ChannelId);
var members = (await _repo.GetProxyMembers(msg.Author.Id, channel.GuildId.Value)).ToList(); var members = (await ctx.Repository.GetProxyMembers(msg.Author.Id, channel.GuildId.Value)).ToList();
// Run everything through the checks, catch the ProxyCheckFailedException, and reply with the error message. // Run everything through the checks, catch the ProxyCheckFailedException, and reply with the error message.
try try

View File

@ -11,13 +11,6 @@ using PluralKit.Core;
namespace PluralKit.Bot; namespace PluralKit.Bot;
public class Config public class Config
{ {
private readonly ModelRepository _repo;
public Config(ModelRepository repo)
{
_repo = repo;
}
private record PaginatedConfigItem(string Key, string Description, string? CurrentValue, string DefaultValue); private record PaginatedConfigItem(string Key, string Description, string? CurrentValue, string DefaultValue);
public async Task ShowConfig(Context ctx) public async Task ShowConfig(Context ctx)
@ -141,7 +134,7 @@ public class Config
return; return;
} }
var patch = new AccountPatch { AllowAutoproxy = allow }; var patch = new AccountPatch { AllowAutoproxy = allow };
await _repo.UpdateAccount(ctx.Author.Id, patch); await ctx.Repository.UpdateAccount(ctx.Author.Id, patch);
await ctx.Reply($"{Emojis.Success} Autoproxy {statusString} for account <@{ctx.Author.Id}>."); await ctx.Reply($"{Emojis.Success} Autoproxy {statusString} for account <@{ctx.Author.Id}>.");
} }
@ -181,7 +174,7 @@ public class Config
else newTimeout = timeoutPeriod; else newTimeout = timeoutPeriod;
} }
await _repo.UpdateSystemConfig(ctx.System.Id, new() { LatchTimeout = (int?)newTimeout?.TotalSeconds }); await ctx.Repository.UpdateSystemConfig(ctx.System.Id, new() { LatchTimeout = (int?)newTimeout?.TotalSeconds });
if (newTimeout == null) if (newTimeout == null)
await ctx.Reply($"{Emojis.Success} Latch timeout reset to default ({ProxyMatcher.DefaultLatchExpiryTime.ToTimeSpan().Humanize(4)})."); await ctx.Reply($"{Emojis.Success} Latch timeout reset to default ({ProxyMatcher.DefaultLatchExpiryTime.ToTimeSpan().Humanize(4)}).");
@ -199,7 +192,7 @@ public class Config
if (await ctx.MatchClear()) if (await ctx.MatchClear())
{ {
await _repo.UpdateSystemConfig(ctx.System.Id, new() { UiTz = "UTC" }); await ctx.Repository.UpdateSystemConfig(ctx.System.Id, new() { UiTz = "UTC" });
await ctx.Reply($"{Emojis.Success} System time zone cleared (set to UTC)."); await ctx.Reply($"{Emojis.Success} System time zone cleared (set to UTC).");
return; return;
@ -220,7 +213,7 @@ public class Config
var msg = $"This will change the system time zone to **{zone.Id}**. The current time is **{currentTime.FormatZoned()}**. Is this correct?"; var msg = $"This will change the system time zone to **{zone.Id}**. The current time is **{currentTime.FormatZoned()}**. Is this correct?";
if (!await ctx.PromptYesNo(msg, "Change Timezone")) throw Errors.TimezoneChangeCancelled; if (!await ctx.PromptYesNo(msg, "Change Timezone")) throw Errors.TimezoneChangeCancelled;
await _repo.UpdateSystemConfig(ctx.System.Id, new() { UiTz = zone.Id }); await ctx.Repository.UpdateSystemConfig(ctx.System.Id, new() { UiTz = zone.Id });
await ctx.Reply($"System time zone changed to **{zone.Id}**."); await ctx.Reply($"System time zone changed to **{zone.Id}**.");
} }
@ -308,7 +301,7 @@ public class Config
await ctx.Reply(Response(true, ctx.Config.PingsEnabled)); await ctx.Reply(Response(true, ctx.Config.PingsEnabled));
else else
{ {
await _repo.UpdateSystemConfig(ctx.System.Id, new() { PingsEnabled = value }); await ctx.Repository.UpdateSystemConfig(ctx.System.Id, new() { PingsEnabled = value });
await ctx.Reply($"Reaction pings have now been {EnabledDisabled(value)}."); await ctx.Reply($"Reaction pings have now been {EnabledDisabled(value)}.");
} }
} }
@ -324,13 +317,13 @@ public class Config
{ {
if (ctx.MatchToggle()) if (ctx.MatchToggle())
{ {
await _repo.UpdateSystemConfig(ctx.System.Id, new() { MemberDefaultPrivate = true }); await ctx.Repository.UpdateSystemConfig(ctx.System.Id, new() { MemberDefaultPrivate = true });
await ctx.Reply("Newly created members will now have their privacy settings set to private."); await ctx.Reply("Newly created members will now have their privacy settings set to private.");
} }
else else
{ {
await _repo.UpdateSystemConfig(ctx.System.Id, new() { MemberDefaultPrivate = false }); await ctx.Repository.UpdateSystemConfig(ctx.System.Id, new() { MemberDefaultPrivate = false });
await ctx.Reply("Newly created members will now have their privacy settings set to public."); await ctx.Reply("Newly created members will now have their privacy settings set to public.");
} }
@ -348,13 +341,13 @@ public class Config
{ {
if (ctx.MatchToggle()) if (ctx.MatchToggle())
{ {
await _repo.UpdateSystemConfig(ctx.System.Id, new() { GroupDefaultPrivate = true }); await ctx.Repository.UpdateSystemConfig(ctx.System.Id, new() { GroupDefaultPrivate = true });
await ctx.Reply("Newly created groups will now have their privacy settings set to private."); await ctx.Reply("Newly created groups will now have their privacy settings set to private.");
} }
else else
{ {
await _repo.UpdateSystemConfig(ctx.System.Id, new() { GroupDefaultPrivate = false }); await ctx.Repository.UpdateSystemConfig(ctx.System.Id, new() { GroupDefaultPrivate = false });
await ctx.Reply("Newly created groups will now have their privacy settings set to public."); await ctx.Reply("Newly created groups will now have their privacy settings set to public.");
} }
@ -372,13 +365,13 @@ public class Config
if (ctx.MatchToggle()) if (ctx.MatchToggle())
{ {
await _repo.UpdateSystemConfig(ctx.System.Id, new() { ShowPrivateInfo = true }); await ctx.Repository.UpdateSystemConfig(ctx.System.Id, new() { ShowPrivateInfo = true });
await ctx.Reply("Private information will now be **shown** when looking up your own info. Use the `-public` flag to hide it."); await ctx.Reply("Private information will now be **shown** when looking up your own info. Use the `-public` flag to hide it.");
} }
else else
{ {
await _repo.UpdateSystemConfig(ctx.System.Id, new() { ShowPrivateInfo = false }); await ctx.Repository.UpdateSystemConfig(ctx.System.Id, new() { ShowPrivateInfo = false });
await ctx.Reply("Private information will now be **hidden** when looking up your own info. Use the `-private` flag to show it."); await ctx.Reply("Private information will now be **hidden** when looking up your own info. Use the `-private` flag to show it.");
} }

View File

@ -8,15 +8,6 @@ namespace PluralKit.Bot;
public class GroupMember public class GroupMember
{ {
private readonly IDatabase _db;
private readonly ModelRepository _repo;
public GroupMember(IDatabase db, ModelRepository repo)
{
_db = db;
_repo = repo;
}
public async Task AddRemoveGroups(Context ctx, PKMember target, Groups.AddRemoveOperation op) public async Task AddRemoveGroups(Context ctx, PKMember target, Groups.AddRemoveOperation op)
{ {
ctx.CheckSystem().CheckOwnMember(target); ctx.CheckSystem().CheckOwnMember(target);
@ -26,7 +17,7 @@ public class GroupMember
.Distinct() .Distinct()
.ToList(); .ToList();
var existingGroups = (await _repo.GetMemberGroups(target.Id).ToListAsync()) var existingGroups = (await ctx.Repository.GetMemberGroups(target.Id).ToListAsync())
.Select(g => g.Id) .Select(g => g.Id)
.Distinct() .Distinct()
.ToList(); .ToList();
@ -39,7 +30,7 @@ public class GroupMember
.Where(group => !existingGroups.Contains(group)) .Where(group => !existingGroups.Contains(group))
.ToList(); .ToList();
await _repo.AddGroupsToMember(target.Id, toAction); await ctx.Repository.AddGroupsToMember(target.Id, toAction);
} }
else if (op == Groups.AddRemoveOperation.Remove) else if (op == Groups.AddRemoveOperation.Remove)
{ {
@ -47,7 +38,7 @@ public class GroupMember
.Where(group => existingGroups.Contains(group)) .Where(group => existingGroups.Contains(group))
.ToList(); .ToList();
await _repo.RemoveGroupsFromMember(target.Id, toAction); await ctx.Repository.RemoveGroupsFromMember(target.Id, toAction);
} }
else else
{ {
@ -62,7 +53,7 @@ public class GroupMember
{ {
var pctx = ctx.DirectLookupContextFor(target.System); var pctx = ctx.DirectLookupContextFor(target.System);
var groups = await _repo.GetMemberGroups(target.Id) var groups = await ctx.Repository.GetMemberGroups(target.Id)
.Where(g => g.Visibility.CanAccess(pctx)) .Where(g => g.Visibility.CanAccess(pctx))
.OrderBy(g => (g.DisplayName ?? g.Name), StringComparer.InvariantCultureIgnoreCase) .OrderBy(g => (g.DisplayName ?? g.Name), StringComparer.InvariantCultureIgnoreCase)
.ToListAsync(); .ToListAsync();
@ -96,7 +87,7 @@ public class GroupMember
.Distinct() .Distinct()
.ToList(); .ToList();
var existingMembersInGroup = (await _db.Execute(conn => conn.QueryMemberList(target.System, var existingMembersInGroup = (await ctx.Database.Execute(conn => conn.QueryMemberList(target.System,
new DatabaseViewsExt.ListQueryOptions { GroupFilter = target.Id }))) new DatabaseViewsExt.ListQueryOptions { GroupFilter = target.Id })))
.Select(m => m.Id.Value) .Select(m => m.Id.Value)
.Distinct() .Distinct()
@ -109,14 +100,14 @@ public class GroupMember
toAction = members toAction = members
.Where(m => !existingMembersInGroup.Contains(m.Value)) .Where(m => !existingMembersInGroup.Contains(m.Value))
.ToList(); .ToList();
await _repo.AddMembersToGroup(target.Id, toAction); await ctx.Repository.AddMembersToGroup(target.Id, toAction);
} }
else if (op == Groups.AddRemoveOperation.Remove) else if (op == Groups.AddRemoveOperation.Remove)
{ {
toAction = members toAction = members
.Where(m => existingMembersInGroup.Contains(m.Value)) .Where(m => existingMembersInGroup.Contains(m.Value))
.ToList(); .ToList();
await _repo.RemoveMembersFromGroup(target.Id, toAction); await ctx.Repository.RemoveMembersFromGroup(target.Id, toAction);
} }
else else
{ {
@ -154,6 +145,6 @@ public class GroupMember
var system = ctx.System; var system = ctx.System;
if (system?.Id == target.System) if (system?.Id == target.System)
return system; return system;
return await _repo.GetSystem(target.System)!; return await ctx.Repository.GetSystem(target.System)!;
} }
} }

View File

@ -23,16 +23,12 @@ public class Groups
} }
private readonly HttpClient _client; private readonly HttpClient _client;
private readonly IDatabase _db;
private readonly DispatchService _dispatch; private readonly DispatchService _dispatch;
private readonly EmbedService _embeds; private readonly EmbedService _embeds;
private readonly ModelRepository _repo;
public Groups(IDatabase db, ModelRepository repo, EmbedService embeds, HttpClient client, public Groups(EmbedService embeds, HttpClient client,
DispatchService dispatch) DispatchService dispatch)
{ {
_db = db;
_repo = repo;
_embeds = embeds; _embeds = embeds;
_client = client; _client = client;
_dispatch = dispatch; _dispatch = dispatch;
@ -48,14 +44,14 @@ public class Groups
throw new PKError($"Group name too long ({groupName.Length}/{Limits.MaxGroupNameLength} characters)."); throw new PKError($"Group name too long ({groupName.Length}/{Limits.MaxGroupNameLength} characters).");
// Check group cap // Check group cap
var existingGroupCount = await _repo.GetSystemGroupCount(ctx.System.Id); var existingGroupCount = await ctx.Repository.GetSystemGroupCount(ctx.System.Id);
var groupLimit = ctx.Config.GroupLimitOverride ?? Limits.MaxGroupCount; var groupLimit = ctx.Config.GroupLimitOverride ?? Limits.MaxGroupCount;
if (existingGroupCount >= groupLimit) if (existingGroupCount >= groupLimit)
throw new PKError( throw new PKError(
$"System has reached the maximum number of groups ({groupLimit}). Please delete unused groups first in order to create new ones."); $"System has reached the maximum number of groups ({groupLimit}). Please delete unused groups first in order to create new ones.");
// Warn if there's already a group by this name // Warn if there's already a group by this name
var existingGroup = await _repo.GetGroupByName(ctx.System.Id, groupName); var existingGroup = await ctx.Repository.GetGroupByName(ctx.System.Id, groupName);
if (existingGroup != null) if (existingGroup != null)
{ {
var msg = var msg =
@ -64,8 +60,10 @@ public class Groups
throw new PKError("Group creation cancelled."); throw new PKError("Group creation cancelled.");
} }
using var conn = await _db.Obtain(); // todo: this is supposed to be a transaction, but it's not used in any useful way
var newGroup = await _repo.CreateGroup(ctx.System.Id, groupName); // consider removing it?
using var conn = await ctx.Database.Obtain();
var newGroup = await ctx.Repository.CreateGroup(ctx.System.Id, groupName);
var dispatchData = new JObject(); var dispatchData = new JObject();
dispatchData.Add("name", groupName); dispatchData.Add("name", groupName);
@ -73,7 +71,7 @@ public class Groups
if (ctx.Config.GroupDefaultPrivate) if (ctx.Config.GroupDefaultPrivate)
{ {
var patch = new GroupPatch().WithAllPrivacy(PrivacyLevel.Private); var patch = new GroupPatch().WithAllPrivacy(PrivacyLevel.Private);
await _repo.UpdateGroup(newGroup.Id, patch, conn); await ctx.Repository.UpdateGroup(newGroup.Id, patch, conn);
dispatchData.Merge(patch.ToJson()); dispatchData.Merge(patch.ToJson());
} }
@ -111,7 +109,7 @@ public class Groups
$"New group name too long ({newName.Length}/{Limits.MaxMemberNameLength} characters)."); $"New group name too long ({newName.Length}/{Limits.MaxMemberNameLength} characters).");
// Warn if there's already a group by this name // Warn if there's already a group by this name
var existingGroup = await _repo.GetGroupByName(ctx.System.Id, newName); var existingGroup = await ctx.Repository.GetGroupByName(ctx.System.Id, newName);
if (existingGroup != null && existingGroup.Id != target.Id) if (existingGroup != null && existingGroup.Id != target.Id)
{ {
var msg = var msg =
@ -120,7 +118,7 @@ public class Groups
throw new PKError("Group rename cancelled."); throw new PKError("Group rename cancelled.");
} }
await _repo.UpdateGroup(target.Id, new GroupPatch { Name = newName }); await ctx.Repository.UpdateGroup(target.Id, new GroupPatch { Name = newName });
await ctx.Reply($"{Emojis.Success} Group name changed from **{target.Name}** to **{newName}**."); await ctx.Reply($"{Emojis.Success} Group name changed from **{target.Name}** to **{newName}**.");
} }
@ -172,7 +170,7 @@ public class Groups
if (await ctx.MatchClear("this group's display name")) if (await ctx.MatchClear("this group's display name"))
{ {
var patch = new GroupPatch { DisplayName = Partial<string>.Null() }; var patch = new GroupPatch { DisplayName = Partial<string>.Null() };
await _repo.UpdateGroup(target.Id, patch); await ctx.Repository.UpdateGroup(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Group display name cleared."); await ctx.Reply($"{Emojis.Success} Group display name cleared.");
if (target.NamePrivacy == PrivacyLevel.Private) if (target.NamePrivacy == PrivacyLevel.Private)
@ -183,7 +181,7 @@ public class Groups
var newDisplayName = ctx.RemainderOrNull(false).NormalizeLineEndSpacing(); var newDisplayName = ctx.RemainderOrNull(false).NormalizeLineEndSpacing();
var patch = new GroupPatch { DisplayName = Partial<string>.Present(newDisplayName) }; var patch = new GroupPatch { DisplayName = Partial<string>.Present(newDisplayName) };
await _repo.UpdateGroup(target.Id, patch); await ctx.Repository.UpdateGroup(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Group display name changed."); await ctx.Reply($"{Emojis.Success} Group display name changed.");
} }
@ -229,7 +227,7 @@ public class Groups
if (await ctx.MatchClear("this group's description")) if (await ctx.MatchClear("this group's description"))
{ {
var patch = new GroupPatch { Description = Partial<string>.Null() }; var patch = new GroupPatch { Description = Partial<string>.Null() };
await _repo.UpdateGroup(target.Id, patch); await ctx.Repository.UpdateGroup(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Group description cleared."); await ctx.Reply($"{Emojis.Success} Group description cleared.");
} }
else else
@ -239,7 +237,7 @@ public class Groups
throw Errors.StringTooLongError("Description", description.Length, Limits.MaxDescriptionLength); throw Errors.StringTooLongError("Description", description.Length, Limits.MaxDescriptionLength);
var patch = new GroupPatch { Description = Partial<string>.Present(description) }; var patch = new GroupPatch { Description = Partial<string>.Present(description) };
await _repo.UpdateGroup(target.Id, patch); await ctx.Repository.UpdateGroup(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Group description changed."); await ctx.Reply($"{Emojis.Success} Group description changed.");
} }
@ -251,7 +249,7 @@ public class Groups
{ {
ctx.CheckOwnGroup(target); ctx.CheckOwnGroup(target);
await _repo.UpdateGroup(target.Id, new GroupPatch { Icon = null }); await ctx.Repository.UpdateGroup(target.Id, new GroupPatch { Icon = null });
await ctx.Reply($"{Emojis.Success} Group icon cleared."); await ctx.Reply($"{Emojis.Success} Group icon cleared.");
} }
@ -261,7 +259,7 @@ public class Groups
await AvatarUtils.VerifyAvatarOrThrow(_client, img.Url); await AvatarUtils.VerifyAvatarOrThrow(_client, img.Url);
await _repo.UpdateGroup(target.Id, new GroupPatch { Icon = img.Url }); await ctx.Repository.UpdateGroup(target.Id, new GroupPatch { Icon = img.Url });
var msg = img.Source switch var msg = img.Source switch
{ {
@ -316,7 +314,7 @@ public class Groups
{ {
ctx.CheckOwnGroup(target); ctx.CheckOwnGroup(target);
await _repo.UpdateGroup(target.Id, new GroupPatch { BannerImage = null }); await ctx.Repository.UpdateGroup(target.Id, new GroupPatch { BannerImage = null });
await ctx.Reply($"{Emojis.Success} Group banner image cleared."); await ctx.Reply($"{Emojis.Success} Group banner image cleared.");
} }
@ -326,7 +324,7 @@ public class Groups
await AvatarUtils.VerifyAvatarOrThrow(_client, img.Url, true); await AvatarUtils.VerifyAvatarOrThrow(_client, img.Url, true);
await _repo.UpdateGroup(target.Id, new GroupPatch { BannerImage = img.Url }); await ctx.Repository.UpdateGroup(target.Id, new GroupPatch { BannerImage = img.Url });
var msg = img.Source switch var msg = img.Source switch
{ {
@ -382,7 +380,7 @@ public class Groups
ctx.CheckOwnGroup(target); ctx.CheckOwnGroup(target);
var patch = new GroupPatch { Color = Partial<string>.Null() }; var patch = new GroupPatch { Color = Partial<string>.Null() };
await _repo.UpdateGroup(target.Id, patch); await ctx.Repository.UpdateGroup(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Group color cleared."); await ctx.Reply($"{Emojis.Success} Group color cleared.");
} }
@ -413,7 +411,7 @@ public class Groups
if (!Regex.IsMatch(color, "^[0-9a-fA-F]{6}$")) throw Errors.InvalidColorError(color); if (!Regex.IsMatch(color, "^[0-9a-fA-F]{6}$")) throw Errors.InvalidColorError(color);
var patch = new GroupPatch { Color = Partial<string>.Present(color.ToLowerInvariant()) }; var patch = new GroupPatch { Color = Partial<string>.Present(color.ToLowerInvariant()) };
await _repo.UpdateGroup(target.Id, patch); await ctx.Repository.UpdateGroup(target.Id, patch);
await ctx.Reply(embed: new EmbedBuilder() await ctx.Reply(embed: new EmbedBuilder()
.Title($"{Emojis.Success} Group color changed.") .Title($"{Emojis.Success} Group color changed.")
@ -490,7 +488,7 @@ public class Groups
async Task SetAll(PrivacyLevel level) async Task SetAll(PrivacyLevel level)
{ {
await _repo.UpdateGroup(target.Id, new GroupPatch().WithAllPrivacy(level)); await ctx.Repository.UpdateGroup(target.Id, new GroupPatch().WithAllPrivacy(level));
if (level == PrivacyLevel.Private) if (level == PrivacyLevel.Private)
await ctx.Reply( await ctx.Reply(
@ -502,7 +500,7 @@ public class Groups
async Task SetLevel(GroupPrivacySubject subject, PrivacyLevel level) async Task SetLevel(GroupPrivacySubject subject, PrivacyLevel level)
{ {
await _repo.UpdateGroup(target.Id, new GroupPatch().WithPrivacy(subject, level)); await ctx.Repository.UpdateGroup(target.Id, new GroupPatch().WithPrivacy(subject, level));
var subjectName = subject switch var subjectName = subject switch
{ {
@ -570,7 +568,7 @@ public class Groups
throw new PKError( throw new PKError(
$"Group deletion cancelled. Note that you must reply with your group ID (`{target.Hid}`) *verbatim*."); $"Group deletion cancelled. Note that you must reply with your group ID (`{target.Hid}`) *verbatim*.");
await _repo.DeleteGroup(target.Id); await ctx.Repository.DeleteGroup(target.Id);
await ctx.Reply($"{Emojis.Success} Group deleted."); await ctx.Reply($"{Emojis.Success} Group deleted.");
} }
@ -580,6 +578,6 @@ public class Groups
var system = ctx.System; var system = ctx.System;
if (system?.Id == target.System) if (system?.Id == target.System)
return system; return system;
return await _repo.GetSystem(target.System)!; return await ctx.Repository.GetSystem(target.System)!;
} }
} }

View File

@ -14,17 +14,13 @@ namespace PluralKit.Bot;
public class Member public class Member
{ {
private readonly HttpClient _client; private readonly HttpClient _client;
private readonly IDatabase _db;
private readonly DispatchService _dispatch; private readonly DispatchService _dispatch;
private readonly EmbedService _embeds; private readonly EmbedService _embeds;
private readonly ModelRepository _repo;
public Member(EmbedService embeds, IDatabase db, ModelRepository repo, HttpClient client, public Member(EmbedService embeds, HttpClient client,
DispatchService dispatch) DispatchService dispatch)
{ {
_embeds = embeds; _embeds = embeds;
_db = db;
_repo = repo;
_client = client; _client = client;
_dispatch = dispatch; _dispatch = dispatch;
} }
@ -39,23 +35,23 @@ public class Member
throw Errors.StringTooLongError("Member name", memberName.Length, Limits.MaxMemberNameLength); throw Errors.StringTooLongError("Member name", memberName.Length, Limits.MaxMemberNameLength);
// Warn if there's already a member by this name // Warn if there's already a member by this name
var existingMember = await _repo.GetMemberByName(ctx.System.Id, memberName); var existingMember = await ctx.Repository.GetMemberByName(ctx.System.Id, memberName);
if (existingMember != null) if (existingMember != null)
{ {
var msg = $"{Emojis.Warn} You already have a member in your system with the name \"{existingMember.NameFor(ctx)}\" (with ID `{existingMember.Hid}`). Do you want to create another member with the same name?"; var msg = $"{Emojis.Warn} You already have a member in your system with the name \"{existingMember.NameFor(ctx)}\" (with ID `{existingMember.Hid}`). Do you want to create another member with the same name?";
if (!await ctx.PromptYesNo(msg, "Create")) throw new PKError("Member creation cancelled."); if (!await ctx.PromptYesNo(msg, "Create")) throw new PKError("Member creation cancelled.");
} }
await using var conn = await _db.Obtain(); await using var conn = await ctx.Database.Obtain();
// Enforce per-system member limit // Enforce per-system member limit
var memberCount = await _repo.GetSystemMemberCount(ctx.System.Id); var memberCount = await ctx.Repository.GetSystemMemberCount(ctx.System.Id);
var memberLimit = ctx.Config.MemberLimitOverride ?? Limits.MaxMemberCount; var memberLimit = ctx.Config.MemberLimitOverride ?? Limits.MaxMemberCount;
if (memberCount >= memberLimit) if (memberCount >= memberLimit)
throw Errors.MemberLimitReachedError(memberLimit); throw Errors.MemberLimitReachedError(memberLimit);
// Create the member // Create the member
var member = await _repo.CreateMember(ctx.System.Id, memberName, conn); var member = await ctx.Repository.CreateMember(ctx.System.Id, memberName, conn);
memberCount++; memberCount++;
JObject dispatchData = new JObject(); JObject dispatchData = new JObject();
@ -64,7 +60,7 @@ public class Member
if (ctx.Config.MemberDefaultPrivate) if (ctx.Config.MemberDefaultPrivate)
{ {
var patch = new MemberPatch().WithAllPrivacy(PrivacyLevel.Private); var patch = new MemberPatch().WithAllPrivacy(PrivacyLevel.Private);
await _repo.UpdateMember(member.Id, patch, conn); await ctx.Repository.UpdateMember(member.Id, patch, conn);
dispatchData.Merge(patch.ToJson()); dispatchData.Merge(patch.ToJson());
} }
@ -75,8 +71,7 @@ public class Member
try try
{ {
await AvatarUtils.VerifyAvatarOrThrow(_client, avatarArg.Url); await AvatarUtils.VerifyAvatarOrThrow(_client, avatarArg.Url);
await _db.Execute(conn => await ctx.Repository.UpdateMember(member.Id, new MemberPatch { AvatarUrl = avatarArg.Url }, conn);
_repo.UpdateMember(member.Id, new MemberPatch { AvatarUrl = avatarArg.Url }, conn));
dispatchData.Add("avatar_url", avatarArg.Url); dispatchData.Add("avatar_url", avatarArg.Url);
} }
@ -95,7 +90,7 @@ public class Member
await ctx.Reply( await ctx.Reply(
$"{Emojis.Success} Member \"{memberName}\" (`{member.Hid}`) registered! Check out the getting started page for how to get a member up and running: https://pluralkit.me/start#create-a-member"); $"{Emojis.Success} Member \"{memberName}\" (`{member.Hid}`) registered! Check out the getting started page for how to get a member up and running: https://pluralkit.me/start#create-a-member");
// todo: move this to ModelRepository // todo: move this to ModelRepository
if (await _db.Execute(conn => conn.QuerySingleAsync<bool>("select has_private_members(@System)", if (await ctx.Database.Execute(conn => conn.QuerySingleAsync<bool>("select has_private_members(@System)",
new { System = ctx.System.Id })) && !ctx.Config.MemberDefaultPrivate) //if has private members new { System = ctx.System.Id })) && !ctx.Config.MemberDefaultPrivate) //if has private members
await ctx.Reply( await ctx.Reply(
$"{Emojis.Warn} This member is currently **public**. To change this, use `pk;member {member.Hid} private`."); $"{Emojis.Warn} This member is currently **public**. To change this, use `pk;member {member.Hid} private`.");
@ -118,7 +113,7 @@ public class Member
public async Task ViewMember(Context ctx, PKMember target) public async Task ViewMember(Context ctx, PKMember target)
{ {
var system = await _repo.GetSystem(target.System); var system = await ctx.Repository.GetSystem(target.System);
await ctx.Reply( await ctx.Reply(
embed: await _embeds.CreateMemberEmbed(system, target, ctx.Guild, ctx.LookupContextFor(system.Id), ctx.Zone)); embed: await _embeds.CreateMemberEmbed(system, target, ctx.Guild, ctx.LookupContextFor(system.Id), ctx.Zone));
} }

View File

@ -9,11 +9,9 @@ namespace PluralKit.Bot;
public class MemberAvatar public class MemberAvatar
{ {
private readonly HttpClient _client; private readonly HttpClient _client;
private readonly ModelRepository _repo;
public MemberAvatar(ModelRepository repo, HttpClient client) public MemberAvatar(HttpClient client)
{ {
_repo = repo;
_client = client; _client = client;
} }
@ -76,14 +74,14 @@ public class MemberAvatar
public async Task ServerAvatar(Context ctx, PKMember target) public async Task ServerAvatar(Context ctx, PKMember target)
{ {
ctx.CheckGuildContext(); ctx.CheckGuildContext();
var guildData = await _repo.GetMemberGuild(ctx.Guild.Id, target.Id); var guildData = await ctx.Repository.GetMemberGuild(ctx.Guild.Id, target.Id);
await AvatarCommandTree(AvatarLocation.Server, ctx, target, guildData); await AvatarCommandTree(AvatarLocation.Server, ctx, target, guildData);
} }
public async Task Avatar(Context ctx, PKMember target) public async Task Avatar(Context ctx, PKMember target)
{ {
var guildData = ctx.Guild != null var guildData = ctx.Guild != null
? await _repo.GetMemberGuild(ctx.Guild.Id, target.Id) ? await ctx.Repository.GetMemberGuild(ctx.Guild.Id, target.Id)
: null; : null;
await AvatarCommandTree(AvatarLocation.Member, ctx, target, guildData); await AvatarCommandTree(AvatarLocation.Member, ctx, target, guildData);
@ -159,9 +157,9 @@ public class MemberAvatar
switch (location) switch (location)
{ {
case AvatarLocation.Server: case AvatarLocation.Server:
return _repo.UpdateMemberGuild(target.Id, ctx.Guild.Id, new MemberGuildPatch { AvatarUrl = url }); return ctx.Repository.UpdateMemberGuild(target.Id, ctx.Guild.Id, new MemberGuildPatch { AvatarUrl = url });
case AvatarLocation.Member: case AvatarLocation.Member:
return _repo.UpdateMember(target.Id, new MemberPatch { AvatarUrl = url }); return ctx.Repository.UpdateMember(target.Id, new MemberPatch { AvatarUrl = url });
default: default:
throw new ArgumentOutOfRangeException($"Unknown avatar location {location}"); throw new ArgumentOutOfRangeException($"Unknown avatar location {location}");
} }

View File

@ -13,11 +13,9 @@ namespace PluralKit.Bot;
public class MemberEdit public class MemberEdit
{ {
private readonly HttpClient _client; private readonly HttpClient _client;
private readonly ModelRepository _repo;
public MemberEdit(ModelRepository repo, HttpClient client) public MemberEdit(HttpClient client)
{ {
_repo = repo;
_client = client; _client = client;
} }
@ -32,7 +30,7 @@ public class MemberEdit
throw Errors.StringTooLongError("Member name", newName.Length, Limits.MaxMemberNameLength); throw Errors.StringTooLongError("Member name", newName.Length, Limits.MaxMemberNameLength);
// Warn if there's already a member by this name // Warn if there's already a member by this name
var existingMember = await _repo.GetMemberByName(ctx.System.Id, newName); var existingMember = await ctx.Repository.GetMemberByName(ctx.System.Id, newName);
if (existingMember != null && existingMember.Id != target.Id) if (existingMember != null && existingMember.Id != target.Id)
{ {
var msg = var msg =
@ -42,7 +40,7 @@ public class MemberEdit
// Rename the member // Rename the member
var patch = new MemberPatch { Name = Partial<string>.Present(newName) }; var patch = new MemberPatch { Name = Partial<string>.Present(newName) };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Member renamed."); await ctx.Reply($"{Emojis.Success} Member renamed.");
if (newName.Contains(" ")) if (newName.Contains(" "))
@ -54,7 +52,7 @@ public class MemberEdit
if (ctx.Guild != null) if (ctx.Guild != null)
{ {
var memberGuildConfig = await _repo.GetMemberGuild(ctx.Guild.Id, target.Id); var memberGuildConfig = await ctx.Repository.GetMemberGuild(ctx.Guild.Id, target.Id);
if (memberGuildConfig.DisplayName != null) if (memberGuildConfig.DisplayName != null)
await ctx.Reply( await ctx.Reply(
$"{Emojis.Note} Note that this member has a server name set ({memberGuildConfig.DisplayName}) in this server ({ctx.Guild.Name}), and will be proxied using that name here."); $"{Emojis.Note} Note that this member has a server name set ({memberGuildConfig.DisplayName}) in this server ({ctx.Guild.Name}), and will be proxied using that name here.");
@ -101,7 +99,7 @@ public class MemberEdit
if (await ctx.MatchClear("this member's description")) if (await ctx.MatchClear("this member's description"))
{ {
var patch = new MemberPatch { Description = Partial<string>.Null() }; var patch = new MemberPatch { Description = Partial<string>.Null() };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Member description cleared."); await ctx.Reply($"{Emojis.Success} Member description cleared.");
} }
else else
@ -111,7 +109,7 @@ public class MemberEdit
throw Errors.StringTooLongError("Description", description.Length, Limits.MaxDescriptionLength); throw Errors.StringTooLongError("Description", description.Length, Limits.MaxDescriptionLength);
var patch = new MemberPatch { Description = Partial<string>.Present(description) }; var patch = new MemberPatch { Description = Partial<string>.Present(description) };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Member description changed."); await ctx.Reply($"{Emojis.Success} Member description changed.");
} }
@ -152,7 +150,7 @@ public class MemberEdit
if (await ctx.MatchClear("this member's pronouns")) if (await ctx.MatchClear("this member's pronouns"))
{ {
var patch = new MemberPatch { Pronouns = Partial<string>.Null() }; var patch = new MemberPatch { Pronouns = Partial<string>.Null() };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Member pronouns cleared."); await ctx.Reply($"{Emojis.Success} Member pronouns cleared.");
} }
else else
@ -162,7 +160,7 @@ public class MemberEdit
throw Errors.StringTooLongError("Pronouns", pronouns.Length, Limits.MaxPronounsLength); throw Errors.StringTooLongError("Pronouns", pronouns.Length, Limits.MaxPronounsLength);
var patch = new MemberPatch { Pronouns = Partial<string>.Present(pronouns) }; var patch = new MemberPatch { Pronouns = Partial<string>.Present(pronouns) };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Member pronouns changed."); await ctx.Reply($"{Emojis.Success} Member pronouns changed.");
} }
@ -174,7 +172,7 @@ public class MemberEdit
async Task ClearBannerImage() async Task ClearBannerImage()
{ {
await _repo.UpdateMember(target.Id, new MemberPatch { BannerImage = null }); await ctx.Repository.UpdateMember(target.Id, new MemberPatch { BannerImage = null });
await ctx.Reply($"{Emojis.Success} Member banner image cleared."); await ctx.Reply($"{Emojis.Success} Member banner image cleared.");
} }
@ -182,7 +180,7 @@ public class MemberEdit
{ {
await AvatarUtils.VerifyAvatarOrThrow(_client, img.Url, true); await AvatarUtils.VerifyAvatarOrThrow(_client, img.Url, true);
await _repo.UpdateMember(target.Id, new MemberPatch { BannerImage = img.Url }); await ctx.Repository.UpdateMember(target.Id, new MemberPatch { BannerImage = img.Url });
var msg = img.Source switch var msg = img.Source switch
{ {
@ -233,7 +231,7 @@ public class MemberEdit
ctx.CheckOwnMember(target); ctx.CheckOwnMember(target);
var patch = new MemberPatch { Color = Partial<string>.Null() }; var patch = new MemberPatch { Color = Partial<string>.Null() };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Member color cleared."); await ctx.Reply($"{Emojis.Success} Member color cleared.");
} }
@ -267,7 +265,7 @@ public class MemberEdit
if (!Regex.IsMatch(color, "^[0-9a-fA-F]{6}$")) throw Errors.InvalidColorError(color); if (!Regex.IsMatch(color, "^[0-9a-fA-F]{6}$")) throw Errors.InvalidColorError(color);
var patch = new MemberPatch { Color = Partial<string>.Present(color.ToLowerInvariant()) }; var patch = new MemberPatch { Color = Partial<string>.Present(color.ToLowerInvariant()) };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
await ctx.Reply(embed: new EmbedBuilder() await ctx.Reply(embed: new EmbedBuilder()
.Title($"{Emojis.Success} Member color changed.") .Title($"{Emojis.Success} Member color changed.")
@ -284,7 +282,7 @@ public class MemberEdit
ctx.CheckOwnMember(target); ctx.CheckOwnMember(target);
var patch = new MemberPatch { Birthday = Partial<LocalDate?>.Null() }; var patch = new MemberPatch { Birthday = Partial<LocalDate?>.Null() };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Member birthdate cleared."); await ctx.Reply($"{Emojis.Success} Member birthdate cleared.");
} }
@ -318,7 +316,7 @@ public class MemberEdit
if (birthday == null) throw Errors.BirthdayParseError(birthdayStr); if (birthday == null) throw Errors.BirthdayParseError(birthdayStr);
var patch = new MemberPatch { Birthday = Partial<LocalDate?>.Present(birthday) }; var patch = new MemberPatch { Birthday = Partial<LocalDate?>.Present(birthday) };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Member birthdate changed."); await ctx.Reply($"{Emojis.Success} Member birthdate changed.");
} }
@ -330,7 +328,7 @@ public class MemberEdit
MemberGuildSettings memberGuildConfig = null; MemberGuildSettings memberGuildConfig = null;
if (ctx.Guild != null) if (ctx.Guild != null)
memberGuildConfig = await _repo.GetMemberGuild(ctx.Guild.Id, target.Id); memberGuildConfig = await ctx.Repository.GetMemberGuild(ctx.Guild.Id, target.Id);
var eb = new EmbedBuilder() var eb = new EmbedBuilder()
.Title("Member names") .Title("Member names")
@ -370,7 +368,7 @@ public class MemberEdit
var successStr = text; var successStr = text;
if (ctx.Guild != null) if (ctx.Guild != null)
{ {
var memberGuildConfig = await _repo.GetMemberGuild(ctx.Guild.Id, target.Id); var memberGuildConfig = await ctx.Repository.GetMemberGuild(ctx.Guild.Id, target.Id);
if (memberGuildConfig.DisplayName != null) if (memberGuildConfig.DisplayName != null)
successStr += successStr +=
$" However, this member has a server name set in this server ({ctx.Guild.Name}), and will be proxied using that name, \"{memberGuildConfig.DisplayName}\", here."; $" However, this member has a server name set in this server ({ctx.Guild.Name}), and will be proxied using that name, \"{memberGuildConfig.DisplayName}\", here.";
@ -412,7 +410,7 @@ public class MemberEdit
if (await ctx.MatchClear("this member's display name")) if (await ctx.MatchClear("this member's display name"))
{ {
var patch = new MemberPatch { DisplayName = Partial<string>.Null() }; var patch = new MemberPatch { DisplayName = Partial<string>.Null() };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
await PrintSuccess( await PrintSuccess(
$"{Emojis.Success} Member display name cleared. This member will now be proxied using their member name \"{target.Name}\"."); $"{Emojis.Success} Member display name cleared. This member will now be proxied using their member name \"{target.Name}\".");
@ -425,7 +423,7 @@ public class MemberEdit
var newDisplayName = ctx.RemainderOrNull(false).NormalizeLineEndSpacing(); var newDisplayName = ctx.RemainderOrNull(false).NormalizeLineEndSpacing();
var patch = new MemberPatch { DisplayName = Partial<string>.Present(newDisplayName) }; var patch = new MemberPatch { DisplayName = Partial<string>.Present(newDisplayName) };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
await PrintSuccess( await PrintSuccess(
$"{Emojis.Success} Member display name changed. This member will now be proxied using the name \"{newDisplayName}\"."); $"{Emojis.Success} Member display name changed. This member will now be proxied using the name \"{newDisplayName}\".");
@ -442,7 +440,7 @@ public class MemberEdit
$" To set one, type `pk;member {target.Reference()} servername <server name>`."; $" To set one, type `pk;member {target.Reference()} servername <server name>`.";
// No perms check, display name isn't covered by member privacy // No perms check, display name isn't covered by member privacy
var memberGuildConfig = await _repo.GetMemberGuild(ctx.Guild.Id, target.Id); var memberGuildConfig = await ctx.Repository.GetMemberGuild(ctx.Guild.Id, target.Id);
if (ctx.MatchRaw()) if (ctx.MatchRaw())
{ {
@ -467,7 +465,7 @@ public class MemberEdit
if (await ctx.MatchClear("this member's server name")) if (await ctx.MatchClear("this member's server name"))
{ {
await _repo.UpdateMemberGuild(target.Id, ctx.Guild.Id, new MemberGuildPatch { DisplayName = null }); await ctx.Repository.UpdateMemberGuild(target.Id, ctx.Guild.Id, new MemberGuildPatch { DisplayName = null });
if (target.DisplayName != null) if (target.DisplayName != null)
await ctx.Reply( await ctx.Reply(
@ -480,7 +478,7 @@ public class MemberEdit
{ {
var newServerName = ctx.RemainderOrNull(false).NormalizeLineEndSpacing(); var newServerName = ctx.RemainderOrNull(false).NormalizeLineEndSpacing();
await _repo.UpdateMemberGuild(target.Id, ctx.Guild.Id, await ctx.Repository.UpdateMemberGuild(target.Id, ctx.Guild.Id,
new MemberGuildPatch { DisplayName = newServerName }); new MemberGuildPatch { DisplayName = newServerName });
await ctx.Reply( await ctx.Reply(
@ -519,7 +517,7 @@ public class MemberEdit
; ;
var patch = new MemberPatch { KeepProxy = Partial<bool>.Present(newValue) }; var patch = new MemberPatch { KeepProxy = Partial<bool>.Present(newValue) };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
if (newValue) if (newValue)
await ctx.Reply( await ctx.Reply(
@ -548,7 +546,7 @@ public class MemberEdit
var newValue = ctx.MatchToggle(); var newValue = ctx.MatchToggle();
var patch = new MemberPatch { AllowAutoproxy = Partial<bool>.Present(newValue) }; var patch = new MemberPatch { AllowAutoproxy = Partial<bool>.Present(newValue) };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
if (newValue) if (newValue)
await ctx.Reply($"{Emojis.Success} Latch / front autoproxy have been **enabled** for this member."); await ctx.Reply($"{Emojis.Success} Latch / front autoproxy have been **enabled** for this member.");
@ -583,11 +581,11 @@ public class MemberEdit
// Get guild settings (mostly for warnings and such) // Get guild settings (mostly for warnings and such)
MemberGuildSettings guildSettings = null; MemberGuildSettings guildSettings = null;
if (ctx.Guild != null) if (ctx.Guild != null)
guildSettings = await _repo.GetMemberGuild(ctx.Guild.Id, target.Id); guildSettings = await ctx.Repository.GetMemberGuild(ctx.Guild.Id, target.Id);
async Task SetAll(PrivacyLevel level) async Task SetAll(PrivacyLevel level)
{ {
await _repo.UpdateMember(target.Id, new MemberPatch().WithAllPrivacy(level)); await ctx.Repository.UpdateMember(target.Id, new MemberPatch().WithAllPrivacy(level));
if (level == PrivacyLevel.Private) if (level == PrivacyLevel.Private)
await ctx.Reply( await ctx.Reply(
@ -599,7 +597,7 @@ public class MemberEdit
async Task SetLevel(MemberPrivacySubject subject, PrivacyLevel level) async Task SetLevel(MemberPrivacySubject subject, PrivacyLevel level)
{ {
await _repo.UpdateMember(target.Id, new MemberPatch().WithPrivacy(subject, level)); await ctx.Repository.UpdateMember(target.Id, new MemberPatch().WithPrivacy(subject, level));
var subjectName = subject switch var subjectName = subject switch
{ {
@ -677,7 +675,7 @@ public class MemberEdit
$"{Emojis.Warn} Are you sure you want to delete \"{target.NameFor(ctx)}\"? If so, reply to this message with the member's ID (`{target.Hid}`). __***This cannot be undone!***__"); $"{Emojis.Warn} Are you sure you want to delete \"{target.NameFor(ctx)}\"? If so, reply to this message with the member's ID (`{target.Hid}`). __***This cannot be undone!***__");
if (!await ctx.ConfirmWithReply(target.Hid)) throw Errors.MemberDeleteCancelled; if (!await ctx.ConfirmWithReply(target.Hid)) throw Errors.MemberDeleteCancelled;
await _repo.DeleteMember(target.Id); await ctx.Repository.DeleteMember(target.Id);
await ctx.Reply($"{Emojis.Success} Member deleted."); await ctx.Reply($"{Emojis.Success} Member deleted.");
} }

View File

@ -6,15 +6,6 @@ namespace PluralKit.Bot;
public class MemberProxy public class MemberProxy
{ {
private readonly IDatabase _db;
private readonly ModelRepository _repo;
public MemberProxy(IDatabase db, ModelRepository repo)
{
_db = db;
_repo = repo;
}
public async Task Proxy(Context ctx, PKMember target) public async Task Proxy(Context ctx, PKMember target)
{ {
ctx.CheckSystem().CheckOwnMember(target); ctx.CheckSystem().CheckOwnMember(target);
@ -32,7 +23,7 @@ public class MemberProxy
async Task<bool> WarnOnConflict(ProxyTag newTag) async Task<bool> WarnOnConflict(ProxyTag newTag)
{ {
var query = "select * from (select *, (unnest(proxy_tags)).prefix as prefix, (unnest(proxy_tags)).suffix as suffix from members where system = @System) as _ where prefix is not distinct from @Prefix and suffix is not distinct from @Suffix and id != @Existing"; var query = "select * from (select *, (unnest(proxy_tags)).prefix as prefix, (unnest(proxy_tags)).suffix as suffix from members where system = @System) as _ where prefix is not distinct from @Prefix and suffix is not distinct from @Suffix and id != @Existing";
var conflicts = (await _db.Execute(conn => conn.QueryAsync<PKMember>(query, var conflicts = (await ctx.Database.Execute(conn => conn.QueryAsync<PKMember>(query,
new { newTag.Prefix, newTag.Suffix, Existing = target.Id, system = target.System }))).ToList(); new { newTag.Prefix, newTag.Suffix, Existing = target.Id, system = target.System }))).ToList();
if (conflicts.Count <= 0) return true; if (conflicts.Count <= 0) return true;
@ -54,7 +45,7 @@ public class MemberProxy
} }
var patch = new MemberPatch { ProxyTags = Partial<ProxyTag[]>.Present(new ProxyTag[0]) }; var patch = new MemberPatch { ProxyTags = Partial<ProxyTag[]>.Present(new ProxyTag[0]) };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Proxy tags cleared."); await ctx.Reply($"{Emojis.Success} Proxy tags cleared.");
} }
@ -86,7 +77,7 @@ public class MemberProxy
var newTags = target.ProxyTags.ToList(); var newTags = target.ProxyTags.ToList();
newTags.Add(tagToAdd); newTags.Add(tagToAdd);
var patch = new MemberPatch { ProxyTags = Partial<ProxyTag[]>.Present(newTags.ToArray()) }; var patch = new MemberPatch { ProxyTags = Partial<ProxyTag[]>.Present(newTags.ToArray()) };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Added proxy tags {tagToAdd.ProxyString.AsCode()}."); await ctx.Reply($"{Emojis.Success} Added proxy tags {tagToAdd.ProxyString.AsCode()}.");
} }
@ -104,7 +95,7 @@ public class MemberProxy
var newTags = target.ProxyTags.ToList(); var newTags = target.ProxyTags.ToList();
newTags.Remove(tagToRemove); newTags.Remove(tagToRemove);
var patch = new MemberPatch { ProxyTags = Partial<ProxyTag[]>.Present(newTags.ToArray()) }; var patch = new MemberPatch { ProxyTags = Partial<ProxyTag[]>.Present(newTags.ToArray()) };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Removed proxy tags {tagToRemove.ProxyString.AsCode()}."); await ctx.Reply($"{Emojis.Success} Removed proxy tags {tagToRemove.ProxyString.AsCode()}.");
} }
@ -128,7 +119,7 @@ public class MemberProxy
var newTags = new[] { requestedTag }; var newTags = new[] { requestedTag };
var patch = new MemberPatch { ProxyTags = Partial<ProxyTag[]>.Present(newTags) }; var patch = new MemberPatch { ProxyTags = Partial<ProxyTag[]>.Present(newTags) };
await _repo.UpdateMember(target.Id, patch); await ctx.Repository.UpdateMember(target.Id, patch);
await ctx.Reply($"{Emojis.Success} Member proxy tags set to {requestedTag.ProxyString.AsCode()}."); await ctx.Reply($"{Emojis.Success} Member proxy tags set to {requestedTag.ProxyString.AsCode()}.");
} }

View File

@ -23,19 +23,15 @@ public class ProxiedMessage
private readonly IDiscordCache _cache; private readonly IDiscordCache _cache;
private readonly IClock _clock; private readonly IClock _clock;
private readonly IDatabase _db;
private readonly EmbedService _embeds; private readonly EmbedService _embeds;
private readonly LogChannelService _logChannel; private readonly LogChannelService _logChannel;
private readonly ModelRepository _repo;
private readonly DiscordApiClient _rest; private readonly DiscordApiClient _rest;
private readonly WebhookExecutorService _webhookExecutor; private readonly WebhookExecutorService _webhookExecutor;
public ProxiedMessage(IDatabase db, ModelRepository repo, EmbedService embeds, IClock clock, public ProxiedMessage(EmbedService embeds, IClock clock,
DiscordApiClient rest, DiscordApiClient rest,
WebhookExecutorService webhookExecutor, LogChannelService logChannel, IDiscordCache cache) WebhookExecutorService webhookExecutor, LogChannelService logChannel, IDiscordCache cache)
{ {
_db = db;
_repo = repo;
_embeds = embeds; _embeds = embeds;
_clock = clock; _clock = clock;
_rest = rest; _rest = rest;
@ -85,13 +81,14 @@ public class ProxiedMessage
private async Task<FullMessage> GetMessageToEdit(Context ctx) private async Task<FullMessage> GetMessageToEdit(Context ctx)
{ {
await using var conn = await _db.Obtain(); // todo: is it correct to get a connection here?
await using var conn = await ctx.Database.Obtain();
FullMessage? msg = null; FullMessage? msg = null;
var (referencedMessage, _) = ctx.MatchMessage(false); var (referencedMessage, _) = ctx.MatchMessage(false);
if (referencedMessage != null) if (referencedMessage != null)
{ {
msg = await _repo.GetMessage(conn, referencedMessage.Value); msg = await ctx.Repository.GetMessage(conn, referencedMessage.Value);
if (msg == null) if (msg == null)
throw new PKError("This is not a message proxied by PluralKit."); throw new PKError("This is not a message proxied by PluralKit.");
} }
@ -105,7 +102,7 @@ public class ProxiedMessage
if (recent == null) if (recent == null)
throw new PKSyntaxError("Could not find a recent message to edit."); throw new PKSyntaxError("Could not find a recent message to edit.");
msg = await _repo.GetMessage(conn, recent.Mid); msg = await ctx.Repository.GetMessage(conn, recent.Mid);
if (msg == null) if (msg == null)
throw new PKSyntaxError("Could not find a recent message to edit."); throw new PKSyntaxError("Could not find a recent message to edit.");
} }
@ -130,7 +127,7 @@ public class ProxiedMessage
private async Task<PKMessage?> FindRecentMessage(Context ctx) private async Task<PKMessage?> FindRecentMessage(Context ctx)
{ {
var lastMessage = await _repo.GetLastMessage(ctx.Guild.Id, ctx.Channel.Id, ctx.Author.Id); var lastMessage = await ctx.Repository.GetLastMessage(ctx.Guild.Id, ctx.Channel.Id, ctx.Author.Id);
if (lastMessage == null) if (lastMessage == null)
return null; return null;
@ -153,7 +150,7 @@ public class ProxiedMessage
var isDelete = ctx.Match("delete") || ctx.MatchFlag("delete"); var isDelete = ctx.Match("delete") || ctx.MatchFlag("delete");
var message = await _db.Execute(c => _repo.GetMessage(c, messageId.Value)); var message = await ctx.Database.Execute(c => ctx.Repository.GetMessage(c, messageId.Value));
if (message == null) if (message == null)
{ {
if (isDelete) if (isDelete)
@ -245,7 +242,7 @@ public class ProxiedMessage
private async Task DeleteCommandMessage(Context ctx, ulong messageId) private async Task DeleteCommandMessage(Context ctx, ulong messageId)
{ {
var message = await _repo.GetCommandMessage(messageId); var message = await ctx.Repository.GetCommandMessage(messageId);
if (message == null) if (message == null)
throw Errors.MessageNotFound(messageId); throw Errors.MessageNotFound(messageId);

View File

@ -4,17 +4,13 @@ namespace PluralKit.Bot;
public class Random public class Random
{ {
private readonly IDatabase _db;
private readonly EmbedService _embeds; private readonly EmbedService _embeds;
private readonly ModelRepository _repo;
private readonly global::System.Random randGen = new(); private readonly global::System.Random randGen = new();
public Random(EmbedService embeds, IDatabase db, ModelRepository repo) public Random(EmbedService embeds)
{ {
_embeds = embeds; _embeds = embeds;
_db = db;
_repo = repo;
} }
// todo: get postgresql to return one random member/group instead of querying all members/groups // todo: get postgresql to return one random member/group instead of querying all members/groups
@ -23,7 +19,7 @@ public class Random
{ {
ctx.CheckSystem(); ctx.CheckSystem();
var members = await _repo.GetSystemMembers(ctx.System.Id).ToListAsync(); var members = await ctx.Repository.GetSystemMembers(ctx.System.Id).ToListAsync();
if (!ctx.MatchFlag("all", "a")) if (!ctx.MatchFlag("all", "a"))
members = members.Where(m => m.MemberVisibility == PrivacyLevel.Public).ToList(); members = members.Where(m => m.MemberVisibility == PrivacyLevel.Public).ToList();
@ -41,7 +37,7 @@ public class Random
{ {
ctx.CheckSystem(); ctx.CheckSystem();
var groups = await _repo.GetSystemGroups(ctx.System.Id).ToListAsync(); var groups = await ctx.Repository.GetSystemGroups(ctx.System.Id).ToListAsync();
if (!ctx.MatchFlag("all", "a")) if (!ctx.MatchFlag("all", "a"))
groups = groups.Where(g => g.Visibility == PrivacyLevel.Public).ToList(); groups = groups.Where(g => g.Visibility == PrivacyLevel.Public).ToList();
@ -60,8 +56,7 @@ public class Random
var opts = ctx.ParseListOptions(ctx.DirectLookupContextFor(group.System)); var opts = ctx.ParseListOptions(ctx.DirectLookupContextFor(group.System));
opts.GroupFilter = group.Id; opts.GroupFilter = group.Id;
await using var conn = await _db.Obtain(); var members = await ctx.Database.Execute(conn => conn.QueryMemberList(ctx.System.Id, opts.ToQueryOptions()));
var members = await conn.QueryMemberList(ctx.System.Id, opts.ToQueryOptions());
if (members == null || !members.Any()) if (members == null || !members.Any())
throw new PKError( throw new PKError(

View File

@ -12,24 +12,20 @@ namespace PluralKit.Bot;
public class ServerConfig public class ServerConfig
{ {
private readonly IDiscordCache _cache; private readonly IDiscordCache _cache;
private readonly LoggerCleanService _cleanService;
private readonly ModelRepository _repo;
public ServerConfig(LoggerCleanService cleanService, ModelRepository repo, IDiscordCache cache) public ServerConfig(IDiscordCache cache)
{ {
_cleanService = cleanService;
_repo = repo;
_cache = cache; _cache = cache;
} }
public async Task SetLogChannel(Context ctx) public async Task SetLogChannel(Context ctx)
{ {
await ctx.CheckGuildContext().CheckAuthorPermission(PermissionSet.ManageGuild, "Manage Server"); await ctx.CheckGuildContext().CheckAuthorPermission(PermissionSet.ManageGuild, "Manage Server");
var settings = await _repo.GetGuild(ctx.Guild.Id); var settings = await ctx.Repository.GetGuild(ctx.Guild.Id);
if (await ctx.MatchClear("the server log channel")) if (await ctx.MatchClear("the server log channel"))
{ {
await _repo.UpdateGuild(ctx.Guild.Id, new GuildPatch { LogChannel = null }); await ctx.Repository.UpdateGuild(ctx.Guild.Id, new GuildPatch { LogChannel = null });
await ctx.Reply($"{Emojis.Success} Proxy logging channel cleared."); await ctx.Reply($"{Emojis.Success} Proxy logging channel cleared.");
return; return;
} }
@ -59,7 +55,7 @@ public class ServerConfig
if (!perms.HasFlag(PermissionSet.EmbedLinks)) if (!perms.HasFlag(PermissionSet.EmbedLinks))
throw new PKError("PluralKit is missing **Embed Links** permissions in the new log channel."); throw new PKError("PluralKit is missing **Embed Links** permissions in the new log channel.");
await _repo.UpdateGuild(ctx.Guild.Id, new GuildPatch { LogChannel = channel.Id }); await ctx.Repository.UpdateGuild(ctx.Guild.Id, new GuildPatch { LogChannel = channel.Id });
await ctx.Reply($"{Emojis.Success} Proxy logging channel set to <#{channel.Id}>."); await ctx.Reply($"{Emojis.Success} Proxy logging channel set to <#{channel.Id}>.");
} }
@ -82,7 +78,7 @@ public class ServerConfig
} }
ulong? logChannel = null; ulong? logChannel = null;
var config = await _repo.GetGuild(ctx.Guild.Id); var config = await ctx.Repository.GetGuild(ctx.Guild.Id);
logChannel = config.LogChannel; logChannel = config.LogChannel;
var blacklist = config.LogBlacklist.ToHashSet(); var blacklist = config.LogBlacklist.ToHashSet();
@ -91,7 +87,7 @@ public class ServerConfig
else else
blacklist.UnionWith(affectedChannels.Select(c => c.Id)); blacklist.UnionWith(affectedChannels.Select(c => c.Id));
await _repo.UpdateGuild(ctx.Guild.Id, new GuildPatch { LogBlacklist = blacklist.ToArray() }); await ctx.Repository.UpdateGuild(ctx.Guild.Id, new GuildPatch { LogBlacklist = blacklist.ToArray() });
await ctx.Reply( await ctx.Reply(
$"{Emojis.Success} Message logging for the given channels {(enable ? "enabled" : "disabled")}." + $"{Emojis.Success} Message logging for the given channels {(enable ? "enabled" : "disabled")}." +
@ -104,7 +100,7 @@ public class ServerConfig
{ {
await ctx.CheckGuildContext().CheckAuthorPermission(PermissionSet.ManageGuild, "Manage Server"); await ctx.CheckGuildContext().CheckAuthorPermission(PermissionSet.ManageGuild, "Manage Server");
var blacklist = await _repo.GetGuild(ctx.Guild.Id); var blacklist = await ctx.Repository.GetGuild(ctx.Guild.Id);
// Resolve all channels from the cache and order by position // Resolve all channels from the cache and order by position
var channels = (await Task.WhenAll(blacklist.Blacklist var channels = (await Task.WhenAll(blacklist.Blacklist
@ -168,7 +164,7 @@ public class ServerConfig
affectedChannels.Add(channel); affectedChannels.Add(channel);
} }
var guild = await _repo.GetGuild(ctx.Guild.Id); var guild = await ctx.Repository.GetGuild(ctx.Guild.Id);
var blacklist = guild.Blacklist.ToHashSet(); var blacklist = guild.Blacklist.ToHashSet();
if (shouldAdd) if (shouldAdd)
@ -176,7 +172,7 @@ public class ServerConfig
else else
blacklist.ExceptWith(affectedChannels.Select(c => c.Id)); blacklist.ExceptWith(affectedChannels.Select(c => c.Id));
await _repo.UpdateGuild(ctx.Guild.Id, new GuildPatch { Blacklist = blacklist.ToArray() }); await ctx.Repository.UpdateGuild(ctx.Guild.Id, new GuildPatch { Blacklist = blacklist.ToArray() });
await ctx.Reply( await ctx.Reply(
$"{Emojis.Success} Channels {(shouldAdd ? "added to" : "removed from")} the proxy blacklist."); $"{Emojis.Success} Channels {(shouldAdd ? "added to" : "removed from")} the proxy blacklist.");
@ -186,9 +182,9 @@ public class ServerConfig
{ {
await ctx.CheckGuildContext().CheckAuthorPermission(PermissionSet.ManageGuild, "Manage Server"); await ctx.CheckGuildContext().CheckAuthorPermission(PermissionSet.ManageGuild, "Manage Server");
var botList = string.Join(", ", _cleanService.Bots.Select(b => b.Name).OrderBy(x => x.ToLowerInvariant())); var botList = string.Join(", ", LoggerCleanService.Bots.Select(b => b.Name).OrderBy(x => x.ToLowerInvariant()));
var guild = await _repo.GetGuild(ctx.Guild.Id); var guild = await ctx.Repository.GetGuild(ctx.Guild.Id);
bool newValue; bool newValue;
if (ctx.Match("enable", "on", "yes")) if (ctx.Match("enable", "on", "yes"))
@ -205,7 +201,7 @@ public class ServerConfig
.Title("Log cleanup settings") .Title("Log cleanup settings")
.Field(new Embed.Field("Supported bots", botList)); .Field(new Embed.Field("Supported bots", botList));
var guildCfg = await _repo.GetGuild(ctx.Guild.Id); var guildCfg = await ctx.Repository.GetGuild(ctx.Guild.Id);
if (guildCfg.LogCleanupEnabled) if (guildCfg.LogCleanupEnabled)
eb.Description( eb.Description(
"Log cleanup is currently **on** for this server. To disable it, type `pk;logclean off`."); "Log cleanup is currently **on** for this server. To disable it, type `pk;logclean off`.");
@ -216,7 +212,7 @@ public class ServerConfig
return; return;
} }
await _repo.UpdateGuild(ctx.Guild.Id, new GuildPatch { LogCleanupEnabled = newValue }); await ctx.Repository.UpdateGuild(ctx.Guild.Id, new GuildPatch { LogCleanupEnabled = newValue });
if (newValue) if (newValue)
await ctx.Reply( await ctx.Reply(

View File

@ -7,15 +7,6 @@ namespace PluralKit.Bot;
public class Switch public class Switch
{ {
private readonly IDatabase _db;
private readonly ModelRepository _repo;
public Switch(IDatabase db, ModelRepository repo)
{
_db = db;
_repo = repo;
}
public async Task SwitchDo(Context ctx) public async Task SwitchDo(Context ctx)
{ {
ctx.CheckSystem(); ctx.CheckSystem();
@ -42,18 +33,18 @@ public class Switch
$"Switch contains too many members ({members.Count} > {Limits.MaxSwitchMemberCount} members)."); $"Switch contains too many members ({members.Count} > {Limits.MaxSwitchMemberCount} members).");
// Find the last switch and its members if applicable // Find the last switch and its members if applicable
await using var conn = await _db.Obtain(); await using var conn = await ctx.Database.Obtain();
var lastSwitch = await _repo.GetLatestSwitch(ctx.System.Id); var lastSwitch = await ctx.Repository.GetLatestSwitch(ctx.System.Id);
if (lastSwitch != null) if (lastSwitch != null)
{ {
var lastSwitchMembers = _repo.GetSwitchMembers(conn, lastSwitch.Id); var lastSwitchMembers = ctx.Repository.GetSwitchMembers(conn, lastSwitch.Id);
// Make sure the requested switch isn't identical to the last one // Make sure the requested switch isn't identical to the last one
if (await lastSwitchMembers.Select(m => m.Id) if (await lastSwitchMembers.Select(m => m.Id)
.SequenceEqualAsync(members.Select(m => m.Id).ToAsyncEnumerable())) .SequenceEqualAsync(members.Select(m => m.Id).ToAsyncEnumerable()))
throw Errors.SameSwitch(members, ctx.LookupContextFor(ctx.System.Id)); throw Errors.SameSwitch(members, ctx.LookupContextFor(ctx.System.Id));
} }
await _repo.AddSwitch(conn, ctx.System.Id, members.Select(m => m.Id).ToList()); await ctx.Repository.AddSwitch(conn, ctx.System.Id, members.Select(m => m.Id).ToList());
if (members.Count == 0) if (members.Count == 0)
await ctx.Reply($"{Emojis.Success} Switch-out registered."); await ctx.Reply($"{Emojis.Success} Switch-out registered.");
@ -78,7 +69,7 @@ public class Switch
if (time.ToInstant() > SystemClock.Instance.GetCurrentInstant()) throw Errors.SwitchTimeInFuture; if (time.ToInstant() > SystemClock.Instance.GetCurrentInstant()) throw Errors.SwitchTimeInFuture;
// Fetch the last two switches for the system to do bounds checking on // Fetch the last two switches for the system to do bounds checking on
var lastTwoSwitches = await _repo.GetSwitches(ctx.System.Id).Take(2).ToListAsync(); var lastTwoSwitches = await ctx.Repository.GetSwitches(ctx.System.Id).Take(2).ToListAsync();
// If we don't have a switch to move, don't bother // If we don't have a switch to move, don't bother
if (lastTwoSwitches.Count == 0) throw Errors.NoRegisteredSwitches; if (lastTwoSwitches.Count == 0) throw Errors.NoRegisteredSwitches;
@ -90,7 +81,7 @@ public class Switch
// Now we can actually do the move, yay! // Now we can actually do the move, yay!
// But, we do a prompt to confirm. // But, we do a prompt to confirm.
var lastSwitchMembers = _db.Execute(conn => _repo.GetSwitchMembers(conn, lastTwoSwitches[0].Id)); var lastSwitchMembers = ctx.Database.Execute(conn => ctx.Repository.GetSwitchMembers(conn, lastTwoSwitches[0].Id));
var lastSwitchMemberStr = var lastSwitchMemberStr =
string.Join(", ", await lastSwitchMembers.Select(m => m.NameFor(ctx)).ToListAsync()); string.Join(", ", await lastSwitchMembers.Select(m => m.NameFor(ctx)).ToListAsync());
var lastSwitchTime = lastTwoSwitches[0].Timestamp.ToUnixTimeSeconds(); // .FormatZoned(ctx.System) var lastSwitchTime = lastTwoSwitches[0].Timestamp.ToUnixTimeSeconds(); // .FormatZoned(ctx.System)
@ -105,7 +96,7 @@ public class Switch
if (!await ctx.PromptYesNo(msg, "Move Switch")) throw Errors.SwitchMoveCancelled; if (!await ctx.PromptYesNo(msg, "Move Switch")) throw Errors.SwitchMoveCancelled;
// aaaand *now* we do the move // aaaand *now* we do the move
await _repo.MoveSwitch(lastTwoSwitches[0].Id, time.ToInstant()); await ctx.Repository.MoveSwitch(lastTwoSwitches[0].Id, time.ToInstant());
await ctx.Reply($"{Emojis.Success} Switch moved to <t:{newSwitchTime}> ({newSwitchDeltaStr} ago)."); await ctx.Reply($"{Emojis.Success} Switch moved to <t:{newSwitchTime}> ({newSwitchDeltaStr} ago).");
} }
@ -130,11 +121,11 @@ public class Switch
if (members.Select(m => m.Id).Distinct().Count() != members.Count) throw Errors.DuplicateSwitchMembers; if (members.Select(m => m.Id).Distinct().Count() != members.Count) throw Errors.DuplicateSwitchMembers;
// Find the switch to edit // Find the switch to edit
await using var conn = await _db.Obtain(); await using var conn = await ctx.Database.Obtain();
var lastSwitch = await _repo.GetLatestSwitch(ctx.System.Id); var lastSwitch = await ctx.Repository.GetLatestSwitch(ctx.System.Id);
// Make sure there's at least one switch // Make sure there's at least one switch
if (lastSwitch == null) throw Errors.NoRegisteredSwitches; if (lastSwitch == null) throw Errors.NoRegisteredSwitches;
var lastSwitchMembers = _repo.GetSwitchMembers(conn, lastSwitch.Id); var lastSwitchMembers = ctx.Repository.GetSwitchMembers(conn, lastSwitch.Id);
// Make sure switch isn't being edited to have the members it already does // Make sure switch isn't being edited to have the members it already does
if (await lastSwitchMembers.Select(m => m.Id) if (await lastSwitchMembers.Select(m => m.Id)
.SequenceEqualAsync(members.Select(m => m.Id).ToAsyncEnumerable())) .SequenceEqualAsync(members.Select(m => m.Id).ToAsyncEnumerable()))
@ -154,7 +145,7 @@ public class Switch
if (!await ctx.PromptYesNo(msg, "Edit")) throw Errors.SwitchEditCancelled; if (!await ctx.PromptYesNo(msg, "Edit")) throw Errors.SwitchEditCancelled;
// Actually edit the switch // Actually edit the switch
await _repo.EditSwitch(conn, lastSwitch.Id, members.Select(m => m.Id).ToList()); await ctx.Repository.EditSwitch(conn, lastSwitch.Id, members.Select(m => m.Id).ToList());
// Tell the user the edit suceeded // Tell the user the edit suceeded
if (members.Count == 0) if (members.Count == 0)
@ -174,16 +165,16 @@ public class Switch
$"{Emojis.Warn} This will delete *all registered switches* in your system. Are you sure you want to proceed?"; $"{Emojis.Warn} This will delete *all registered switches* in your system. Are you sure you want to proceed?";
if (!await ctx.PromptYesNo(purgeMsg, "Clear Switches")) if (!await ctx.PromptYesNo(purgeMsg, "Clear Switches"))
throw Errors.GenericCancelled(); throw Errors.GenericCancelled();
await _repo.DeleteAllSwitches(ctx.System.Id); await ctx.Repository.DeleteAllSwitches(ctx.System.Id);
await ctx.Reply($"{Emojis.Success} Cleared system switches!"); await ctx.Reply($"{Emojis.Success} Cleared system switches!");
return; return;
} }
// Fetch the last two switches for the system to do bounds checking on // Fetch the last two switches for the system to do bounds checking on
var lastTwoSwitches = await _repo.GetSwitches(ctx.System.Id).Take(2).ToListAsync(); var lastTwoSwitches = await ctx.Repository.GetSwitches(ctx.System.Id).Take(2).ToListAsync();
if (lastTwoSwitches.Count == 0) throw Errors.NoRegisteredSwitches; if (lastTwoSwitches.Count == 0) throw Errors.NoRegisteredSwitches;
var lastSwitchMembers = _db.Execute(conn => _repo.GetSwitchMembers(conn, lastTwoSwitches[0].Id)); var lastSwitchMembers = ctx.Database.Execute(conn => ctx.Repository.GetSwitchMembers(conn, lastTwoSwitches[0].Id));
var lastSwitchMemberStr = var lastSwitchMemberStr =
string.Join(", ", await lastSwitchMembers.Select(m => m.NameFor(ctx)).ToListAsync()); string.Join(", ", await lastSwitchMembers.Select(m => m.NameFor(ctx)).ToListAsync());
var lastSwitchDeltaStr = var lastSwitchDeltaStr =
@ -196,7 +187,7 @@ public class Switch
} }
else else
{ {
var secondSwitchMembers = _db.Execute(conn => _repo.GetSwitchMembers(conn, lastTwoSwitches[1].Id)); var secondSwitchMembers = ctx.Database.Execute(conn => ctx.Repository.GetSwitchMembers(conn, lastTwoSwitches[1].Id));
var secondSwitchMemberStr = var secondSwitchMemberStr =
string.Join(", ", await secondSwitchMembers.Select(m => m.NameFor(ctx)).ToListAsync()); string.Join(", ", await secondSwitchMembers.Select(m => m.NameFor(ctx)).ToListAsync());
var secondSwitchDeltaStr = (SystemClock.Instance.GetCurrentInstant() - lastTwoSwitches[1].Timestamp) var secondSwitchDeltaStr = (SystemClock.Instance.GetCurrentInstant() - lastTwoSwitches[1].Timestamp)
@ -205,7 +196,7 @@ public class Switch
} }
if (!await ctx.PromptYesNo(msg, "Delete Switch")) throw Errors.SwitchDeleteCancelled; if (!await ctx.PromptYesNo(msg, "Delete Switch")) throw Errors.SwitchDeleteCancelled;
await _repo.DeleteSwitch(lastTwoSwitches[0].Id); await ctx.Repository.DeleteSwitch(lastTwoSwitches[0].Id);
await ctx.Reply($"{Emojis.Success} Switch deleted."); await ctx.Reply($"{Emojis.Success} Switch deleted.");
} }

View File

@ -5,12 +5,10 @@ namespace PluralKit.Bot;
public class System public class System
{ {
private readonly EmbedService _embeds; private readonly EmbedService _embeds;
private readonly ModelRepository _repo;
public System(EmbedService embeds, ModelRepository repo) public System(EmbedService embeds, ModelRepository repo)
{ {
_embeds = embeds; _embeds = embeds;
_repo = repo;
} }
public async Task Query(Context ctx, PKSystem system) public async Task Query(Context ctx, PKSystem system)
@ -28,8 +26,8 @@ public class System
if (systemName != null && systemName.Length > Limits.MaxSystemNameLength) if (systemName != null && systemName.Length > Limits.MaxSystemNameLength)
throw Errors.StringTooLongError("System name", systemName.Length, Limits.MaxSystemNameLength); throw Errors.StringTooLongError("System name", systemName.Length, Limits.MaxSystemNameLength);
var system = await _repo.CreateSystem(systemName); var system = await ctx.Repository.CreateSystem(systemName);
await _repo.AddAccount(system.Id, ctx.Author.Id); await ctx.Repository.AddAccount(system.Id, ctx.Author.Id);
// TODO: better message, perhaps embed like in groups? // TODO: better message, perhaps embed like in groups?
await ctx.Reply( await ctx.Reply(

View File

@ -14,11 +14,9 @@ namespace PluralKit.Bot;
public class SystemEdit public class SystemEdit
{ {
private readonly HttpClient _client; private readonly HttpClient _client;
private readonly ModelRepository _repo;
public SystemEdit(ModelRepository repo, HttpClient client) public SystemEdit(HttpClient client)
{ {
_repo = repo;
_client = client; _client = client;
} }
@ -54,7 +52,7 @@ public class SystemEdit
if (await ctx.MatchClear("your system's name")) if (await ctx.MatchClear("your system's name"))
{ {
await _repo.UpdateSystem(target.Id, new SystemPatch { Name = null }); await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { Name = null });
await ctx.Reply($"{Emojis.Success} System name cleared."); await ctx.Reply($"{Emojis.Success} System name cleared.");
} }
@ -65,7 +63,7 @@ public class SystemEdit
if (newSystemName.Length > Limits.MaxSystemNameLength) if (newSystemName.Length > Limits.MaxSystemNameLength)
throw Errors.StringTooLongError("System name", newSystemName.Length, Limits.MaxSystemNameLength); throw Errors.StringTooLongError("System name", newSystemName.Length, Limits.MaxSystemNameLength);
await _repo.UpdateSystem(target.Id, new SystemPatch { Name = newSystemName }); await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { Name = newSystemName });
await ctx.Reply($"{Emojis.Success} System name changed."); await ctx.Reply($"{Emojis.Success} System name changed.");
} }
@ -109,7 +107,7 @@ public class SystemEdit
if (await ctx.MatchClear("your system's description")) if (await ctx.MatchClear("your system's description"))
{ {
await _repo.UpdateSystem(target.Id, new SystemPatch { Description = null }); await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { Description = null });
await ctx.Reply($"{Emojis.Success} System description cleared."); await ctx.Reply($"{Emojis.Success} System description cleared.");
} }
@ -119,7 +117,7 @@ public class SystemEdit
if (newDescription.Length > Limits.MaxDescriptionLength) if (newDescription.Length > Limits.MaxDescriptionLength)
throw Errors.StringTooLongError("Description", newDescription.Length, Limits.MaxDescriptionLength); throw Errors.StringTooLongError("Description", newDescription.Length, Limits.MaxDescriptionLength);
await _repo.UpdateSystem(target.Id, new SystemPatch { Description = newDescription }); await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { Description = newDescription });
await ctx.Reply($"{Emojis.Success} System description changed."); await ctx.Reply($"{Emojis.Success} System description changed.");
} }
@ -149,7 +147,7 @@ public class SystemEdit
if (await ctx.MatchClear()) if (await ctx.MatchClear())
{ {
await _repo.UpdateSystem(target.Id, new SystemPatch { Color = Partial<string>.Null() }); await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { Color = Partial<string>.Null() });
await ctx.Reply($"{Emojis.Success} System color cleared."); await ctx.Reply($"{Emojis.Success} System color cleared.");
} }
@ -160,7 +158,7 @@ public class SystemEdit
if (color.StartsWith("#")) color = color.Substring(1); if (color.StartsWith("#")) color = color.Substring(1);
if (!Regex.IsMatch(color, "^[0-9a-fA-F]{6}$")) throw Errors.InvalidColorError(color); if (!Regex.IsMatch(color, "^[0-9a-fA-F]{6}$")) throw Errors.InvalidColorError(color);
await _repo.UpdateSystem(target.Id, await ctx.Repository.UpdateSystem(target.Id,
new SystemPatch { Color = Partial<string>.Present(color.ToLowerInvariant()) }); new SystemPatch { Color = Partial<string>.Present(color.ToLowerInvariant()) });
await ctx.Reply(embed: new EmbedBuilder() await ctx.Reply(embed: new EmbedBuilder()
@ -202,7 +200,7 @@ public class SystemEdit
if (await ctx.MatchClear("your system's tag")) if (await ctx.MatchClear("your system's tag"))
{ {
await _repo.UpdateSystem(target.Id, new SystemPatch { Tag = null }); await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { Tag = null });
await ctx.Reply($"{Emojis.Success} System tag cleared."); await ctx.Reply($"{Emojis.Success} System tag cleared.");
} }
@ -213,7 +211,7 @@ public class SystemEdit
if (newTag.Length > Limits.MaxSystemTagLength) if (newTag.Length > Limits.MaxSystemTagLength)
throw Errors.StringTooLongError("System tag", newTag.Length, Limits.MaxSystemTagLength); throw Errors.StringTooLongError("System tag", newTag.Length, Limits.MaxSystemTagLength);
await _repo.UpdateSystem(target.Id, new SystemPatch { Tag = newTag }); await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { Tag = newTag });
await ctx.Reply( await ctx.Reply(
$"{Emojis.Success} System tag changed. Member names will now end with {newTag.AsCode()} when proxied."); $"{Emojis.Success} System tag changed. Member names will now end with {newTag.AsCode()} when proxied.");
@ -227,7 +225,7 @@ public class SystemEdit
var setDisabledWarning = var setDisabledWarning =
$"{Emojis.Warn} Your system tag is currently **disabled** in this server. No tag will be applied when proxying.\nTo re-enable the system tag in the current server, type `pk;s servertag -enable`."; $"{Emojis.Warn} Your system tag is currently **disabled** in this server. No tag will be applied when proxying.\nTo re-enable the system tag in the current server, type `pk;s servertag -enable`.";
var settings = await _repo.GetSystemGuild(ctx.Guild.Id, target.Id); var settings = await ctx.Repository.GetSystemGuild(ctx.Guild.Id, target.Id);
async Task Show(bool raw = false) async Task Show(bool raw = false)
{ {
@ -264,7 +262,7 @@ public class SystemEdit
if (newTag != null && newTag.Length > Limits.MaxSystemTagLength) if (newTag != null && newTag.Length > Limits.MaxSystemTagLength)
throw Errors.StringTooLongError("System server tag", newTag.Length, Limits.MaxSystemTagLength); throw Errors.StringTooLongError("System server tag", newTag.Length, Limits.MaxSystemTagLength);
await _repo.UpdateSystemGuild(target.Id, ctx.Guild.Id, new SystemGuildPatch { Tag = newTag }); await ctx.Repository.UpdateSystemGuild(target.Id, ctx.Guild.Id, new SystemGuildPatch { Tag = newTag });
await ctx.Reply( await ctx.Reply(
$"{Emojis.Success} System server tag changed. Member names will now end with {newTag.AsCode()} when proxied in the current server '{ctx.Guild.Name}'."); $"{Emojis.Success} System server tag changed. Member names will now end with {newTag.AsCode()} when proxied in the current server '{ctx.Guild.Name}'.");
@ -275,7 +273,7 @@ public class SystemEdit
async Task Clear() async Task Clear()
{ {
await _repo.UpdateSystemGuild(target.Id, ctx.Guild.Id, new SystemGuildPatch { Tag = null }); await ctx.Repository.UpdateSystemGuild(target.Id, ctx.Guild.Id, new SystemGuildPatch { Tag = null });
await ctx.Reply( await ctx.Reply(
$"{Emojis.Success} System server tag cleared. Member names will now end with the global system tag, if there is one set."); $"{Emojis.Success} System server tag cleared. Member names will now end with the global system tag, if there is one set.");
@ -286,7 +284,7 @@ public class SystemEdit
async Task EnableDisable(bool newValue) async Task EnableDisable(bool newValue)
{ {
await _repo.UpdateSystemGuild(target.Id, ctx.Guild.Id, await ctx.Repository.UpdateSystemGuild(target.Id, ctx.Guild.Id,
new SystemGuildPatch { TagEnabled = newValue }); new SystemGuildPatch { TagEnabled = newValue });
await ctx.Reply(PrintEnableDisableResult(newValue, newValue != ctx.MessageContext.TagEnabled)); await ctx.Reply(PrintEnableDisableResult(newValue, newValue != ctx.MessageContext.TagEnabled));
@ -347,7 +345,7 @@ public class SystemEdit
{ {
ctx.CheckOwnSystem(target); ctx.CheckOwnSystem(target);
await _repo.UpdateSystem(target.Id, new SystemPatch { AvatarUrl = null }); await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { AvatarUrl = null });
await ctx.Reply($"{Emojis.Success} System icon cleared."); await ctx.Reply($"{Emojis.Success} System icon cleared.");
} }
@ -357,7 +355,7 @@ public class SystemEdit
await AvatarUtils.VerifyAvatarOrThrow(_client, img.Url); await AvatarUtils.VerifyAvatarOrThrow(_client, img.Url);
await _repo.UpdateSystem(target.Id, new SystemPatch { AvatarUrl = img.Url }); await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { AvatarUrl = img.Url });
var msg = img.Source switch var msg = img.Source switch
{ {
@ -439,7 +437,7 @@ public class SystemEdit
if (await ctx.MatchClear("your system's banner image")) if (await ctx.MatchClear("your system's banner image"))
{ {
await _repo.UpdateSystem(target.Id, new SystemPatch { BannerImage = null }); await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { BannerImage = null });
await ctx.Reply($"{Emojis.Success} System banner image cleared."); await ctx.Reply($"{Emojis.Success} System banner image cleared.");
} }
@ -447,7 +445,7 @@ public class SystemEdit
{ {
await AvatarUtils.VerifyAvatarOrThrow(_client, img.Url, true); await AvatarUtils.VerifyAvatarOrThrow(_client, img.Url, true);
await _repo.UpdateSystem(target.Id, new SystemPatch { BannerImage = img.Url }); await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { BannerImage = img.Url });
var msg = img.Source switch var msg = img.Source switch
{ {
@ -477,7 +475,7 @@ public class SystemEdit
throw new PKError( throw new PKError(
$"System deletion cancelled. Note that you must reply with your system ID (`{target.Hid}`) *verbatim*."); $"System deletion cancelled. Note that you must reply with your system ID (`{target.Hid}`) *verbatim*.");
await _repo.DeleteSystem(target.Id); await ctx.Repository.DeleteSystem(target.Id);
await ctx.Reply($"{Emojis.Success} System deleted."); await ctx.Reply($"{Emojis.Success} System deleted.");
} }
@ -489,7 +487,7 @@ public class SystemEdit
var guild = await ctx.MatchGuild() ?? ctx.Guild ?? var guild = await ctx.MatchGuild() ?? ctx.Guild ??
throw new PKError("You must run this command in a server or pass a server ID."); throw new PKError("You must run this command in a server or pass a server ID.");
var gs = await _repo.GetSystemGuild(guild.Id, ctx.System.Id); var gs = await ctx.Repository.GetSystemGuild(guild.Id, ctx.System.Id);
string serverText; string serverText;
if (guild.Id == ctx.Guild?.Id) if (guild.Id == ctx.Guild?.Id)
@ -510,7 +508,7 @@ public class SystemEdit
var newValue = ctx.MatchToggle(); var newValue = ctx.MatchToggle();
await _repo.UpdateSystemGuild(ctx.System.Id, guild.Id, new SystemGuildPatch { ProxyEnabled = newValue }); await ctx.Repository.UpdateSystemGuild(ctx.System.Id, guild.Id, new SystemGuildPatch { ProxyEnabled = newValue });
if (newValue) if (newValue)
await ctx.Reply($"Message proxying in {serverText} is now **enabled** for your system."); await ctx.Reply($"Message proxying in {serverText} is now **enabled** for your system.");
@ -538,7 +536,7 @@ public class SystemEdit
async Task SetLevel(SystemPrivacySubject subject, PrivacyLevel level) async Task SetLevel(SystemPrivacySubject subject, PrivacyLevel level)
{ {
await _repo.UpdateSystem(target.Id, new SystemPatch().WithPrivacy(subject, level)); await ctx.Repository.UpdateSystem(target.Id, new SystemPatch().WithPrivacy(subject, level));
var levelExplanation = level switch var levelExplanation = level switch
{ {
@ -564,7 +562,7 @@ public class SystemEdit
async Task SetAll(PrivacyLevel level) async Task SetAll(PrivacyLevel level)
{ {
await _repo.UpdateSystem(target.Id, new SystemPatch().WithAllPrivacy(level)); await ctx.Repository.UpdateSystem(target.Id, new SystemPatch().WithAllPrivacy(level));
var msg = level switch var msg = level switch
{ {

View File

@ -8,15 +8,11 @@ namespace PluralKit.Bot;
public class SystemFront public class SystemFront
{ {
private readonly IDatabase _db;
private readonly EmbedService _embeds; private readonly EmbedService _embeds;
private readonly ModelRepository _repo;
public SystemFront(EmbedService embeds, IDatabase db, ModelRepository repo) public SystemFront(EmbedService embeds)
{ {
_embeds = embeds; _embeds = embeds;
_db = db;
_repo = repo;
} }
public async Task SystemFronter(Context ctx, PKSystem system) public async Task SystemFronter(Context ctx, PKSystem system)
@ -24,7 +20,7 @@ public class SystemFront
if (system == null) throw Errors.NoSystemError; if (system == null) throw Errors.NoSystemError;
ctx.CheckSystemPrivacy(system.Id, system.FrontPrivacy); ctx.CheckSystemPrivacy(system.Id, system.FrontPrivacy);
var sw = await _repo.GetLatestSwitch(system.Id); var sw = await ctx.Repository.GetLatestSwitch(system.Id);
if (sw == null) throw Errors.NoRegisteredSwitches; if (sw == null) throw Errors.NoRegisteredSwitches;
await ctx.Reply(embed: await _embeds.CreateFronterEmbed(sw, ctx.Zone, ctx.LookupContextFor(system.Id))); await ctx.Reply(embed: await _embeds.CreateFronterEmbed(sw, ctx.Zone, ctx.LookupContextFor(system.Id)));
@ -35,10 +31,10 @@ public class SystemFront
if (system == null) throw Errors.NoSystemError; if (system == null) throw Errors.NoSystemError;
ctx.CheckSystemPrivacy(system.Id, system.FrontHistoryPrivacy); ctx.CheckSystemPrivacy(system.Id, system.FrontHistoryPrivacy);
var totalSwitches = await _repo.GetSwitchCount(system.Id); var totalSwitches = await ctx.Repository.GetSwitchCount(system.Id);
if (totalSwitches == 0) throw Errors.NoRegisteredSwitches; if (totalSwitches == 0) throw Errors.NoRegisteredSwitches;
var sws = _repo.GetSwitches(system.Id) var sws = ctx.Repository.GetSwitches(system.Id)
.Scan(new FrontHistoryEntry(null, null), .Scan(new FrontHistoryEntry(null, null),
(lastEntry, newSwitch) => new FrontHistoryEntry(lastEntry.ThisSwitch?.Timestamp, newSwitch)); (lastEntry, newSwitch) => new FrontHistoryEntry(lastEntry.ThisSwitch?.Timestamp, newSwitch));
@ -63,7 +59,7 @@ public class SystemFront
// Fetch member list and format // Fetch member list and format
var members = await _db.Execute(c => _repo.GetSwitchMembers(c, sw.Id)).ToListAsync(); var members = await ctx.Database.Execute(c => ctx.Repository.GetSwitchMembers(c, sw.Id)).ToListAsync();
var membersStr = members.Any() var membersStr = members.Any()
? string.Join(", ", members.Select(m => m.NameFor(ctx))) ? string.Join(", ", members.Select(m => m.NameFor(ctx)))
: "no fronter"; : "no fronter";
@ -102,7 +98,7 @@ public class SystemFront
ctx.CheckSystemPrivacy(system.Id, system.FrontHistoryPrivacy); ctx.CheckSystemPrivacy(system.Id, system.FrontHistoryPrivacy);
var totalSwitches = await _repo.GetSwitchCount(system.Id); var totalSwitches = await ctx.Repository.GetSwitchCount(system.Id);
if (totalSwitches == 0) throw Errors.NoRegisteredSwitches; if (totalSwitches == 0) throw Errors.NoRegisteredSwitches;
var ignoreNoFronters = ctx.MatchFlag("fo", "fronters-only"); var ignoreNoFronters = ctx.MatchFlag("fo", "fronters-only");
@ -130,7 +126,7 @@ public class SystemFront
else else
title.Append($"`{system.Hid}`"); title.Append($"`{system.Hid}`");
var frontpercent = await _db.Execute(c => _repo.GetFrontBreakdown(c, system.Id, group?.Id, rangeStart.Value.ToInstant(), now)); var frontpercent = await ctx.Database.Execute(c => ctx.Repository.GetFrontBreakdown(c, system.Id, group?.Id, rangeStart.Value.ToInstant(), now));
await ctx.Reply(embed: await _embeds.CreateFrontPercentEmbed(frontpercent, system, group, ctx.Zone, await ctx.Reply(embed: await _embeds.CreateFrontPercentEmbed(frontpercent, system, group, ctx.Zone,
ctx.LookupContextFor(system.Id), title.ToString(), ignoreNoFronters, showFlat)); ctx.LookupContextFor(system.Id), title.ToString(), ignoreNoFronters, showFlat));
} }
@ -140,7 +136,7 @@ public class SystemFront
var system = ctx.System; var system = ctx.System;
if (system?.Id == target.System) if (system?.Id == target.System)
return system; return system;
return await _repo.GetSystem(target.System)!; return await ctx.Repository.GetSystem(target.System)!;
} }
private struct FrontHistoryEntry private struct FrontHistoryEntry

View File

@ -6,30 +6,23 @@ namespace PluralKit.Bot;
public class SystemLink public class SystemLink
{ {
private readonly ModelRepository _repo;
public SystemLink(ModelRepository repo)
{
_repo = repo;
}
public async Task LinkSystem(Context ctx) public async Task LinkSystem(Context ctx)
{ {
ctx.CheckSystem(); ctx.CheckSystem();
var account = await ctx.MatchUser() ?? var account = await ctx.MatchUser() ??
throw new PKSyntaxError("You must pass an account to link with (either ID or @mention)."); throw new PKSyntaxError("You must pass an account to link with (either ID or @mention).");
var accountIds = await _repo.GetSystemAccounts(ctx.System.Id); var accountIds = await ctx.Repository.GetSystemAccounts(ctx.System.Id);
if (accountIds.Contains(account.Id)) if (accountIds.Contains(account.Id))
throw Errors.AccountAlreadyLinked; throw Errors.AccountAlreadyLinked;
var existingAccount = await _repo.GetSystemByAccount(account.Id); var existingAccount = await ctx.Repository.GetSystemByAccount(account.Id);
if (existingAccount != null) if (existingAccount != null)
throw Errors.AccountInOtherSystem(existingAccount); throw Errors.AccountInOtherSystem(existingAccount);
var msg = $"{account.Mention()}, please confirm the link."; var msg = $"{account.Mention()}, please confirm the link.";
if (!await ctx.PromptYesNo(msg, "Confirm", account, false)) throw Errors.MemberLinkCancelled; if (!await ctx.PromptYesNo(msg, "Confirm", account, false)) throw Errors.MemberLinkCancelled;
await _repo.AddAccount(ctx.System.Id, account.Id); await ctx.Repository.AddAccount(ctx.System.Id, account.Id);
await ctx.Reply($"{Emojis.Success} Account linked to system."); await ctx.Reply($"{Emojis.Success} Account linked to system.");
} }
@ -41,14 +34,14 @@ public class SystemLink
if (!ctx.MatchUserRaw(out id)) if (!ctx.MatchUserRaw(out id))
throw new PKSyntaxError("You must pass an account to link with (either ID or @mention)."); throw new PKSyntaxError("You must pass an account to link with (either ID or @mention).");
var accountIds = (await _repo.GetSystemAccounts(ctx.System.Id)).ToList(); var accountIds = (await ctx.Repository.GetSystemAccounts(ctx.System.Id)).ToList();
if (!accountIds.Contains(id)) throw Errors.AccountNotLinked; if (!accountIds.Contains(id)) throw Errors.AccountNotLinked;
if (accountIds.Count == 1) throw Errors.UnlinkingLastAccount; if (accountIds.Count == 1) throw Errors.UnlinkingLastAccount;
var msg = $"Are you sure you want to unlink <@{id}> from your system?"; var msg = $"Are you sure you want to unlink <@{id}> from your system?";
if (!await ctx.PromptYesNo(msg, "Unlink")) throw Errors.MemberUnlinkCancelled; if (!await ctx.PromptYesNo(msg, "Unlink")) throw Errors.MemberUnlinkCancelled;
await _repo.RemoveAccount(ctx.System.Id, id); await ctx.Repository.RemoveAccount(ctx.System.Id, id);
await ctx.Reply($"{Emojis.Success} Account unlinked."); await ctx.Reply($"{Emojis.Success} Account unlinked.");
} }
} }

View File

@ -80,7 +80,7 @@ public class LoggerCleanService
_logger = logger.ForContext<LoggerCleanService>(); _logger = logger.ForContext<LoggerCleanService>();
} }
public ICollection<LoggerBot> Bots => _bots.Values; public static ICollection<LoggerBot> Bots => _bots.Values;
public async ValueTask HandleLoggerBotCleanup(Message msg) public async ValueTask HandleLoggerBotCleanup(Message msg)
{ {