diff --git a/PluralKit.Bot/Bot.cs b/PluralKit.Bot/Bot.cs index da3ed512..cd20b114 100644 --- a/PluralKit.Bot/Bot.cs +++ b/PluralKit.Bot/Bot.cs @@ -111,6 +111,7 @@ namespace PluralKit.Bot .AddTransient() .AddTransient() .AddTransient() + .AddTransient() .AddTransient() .AddTransient() diff --git a/PluralKit.Bot/Commands/AutoproxyCommands.cs b/PluralKit.Bot/Commands/AutoproxyCommands.cs new file mode 100644 index 00000000..66416d30 --- /dev/null +++ b/PluralKit.Bot/Commands/AutoproxyCommands.cs @@ -0,0 +1,139 @@ +using System; +using System.Linq; +using System.Threading.Tasks; + +using Discord; + +using PluralKit.Bot.CommandSystem; + +namespace PluralKit.Bot.Commands +{ + public class AutoproxyCommands + { + private IDataStore _data; + + public AutoproxyCommands(IDataStore data) + { + _data = data; + } + + public async Task Autoproxy(Context ctx) + { + ctx.CheckSystem().CheckGuildContext(); + + if (ctx.Match("off", "stop", "cancel", "no")) + await AutoproxyOff(ctx); + else if (ctx.Match("latch", "last", "proxy", "stick", "sticky")) + await AutoproxyLatch(ctx); + else if (ctx.Match("front", "fronter", "switch")) + await AutoproxyFront(ctx); + else if (ctx.Match("member")) + throw new PKSyntaxError("Member-mode autoproxy must target a specific member. Use the `pk;autoproxy ` command, where `member` is the name or ID of a member in your system."); + else if (await ctx.MatchMember() is PKMember member) + await AutoproxyMember(ctx, member); + else if (!ctx.HasNext()) + await ctx.Reply(embed: await CreateAutoproxyStatusEmbed(ctx)); + else + throw new PKSyntaxError($"Invalid autoproxy mode `{ctx.PopArgument().EscapeMarkdown()}`."); + } + + private async Task AutoproxyOff(Context ctx) + { + var settings = await _data.GetSystemGuildSettings(ctx.System, ctx.Guild.Id); + if (settings.AutoproxyMode == AutoproxyMode.Off) + { + await ctx.Reply($"{Emojis.Note} Autoproxy is already off in this server."); + } + else + { + settings.AutoproxyMode = AutoproxyMode.Off; + settings.AutoproxyMember = null; + await _data.SetSystemGuildSettings(ctx.System, ctx.Guild.Id, settings); + await ctx.Reply($"{Emojis.Success} Autoproxy turned off in this server."); + } + } + + private async Task AutoproxyLatch(Context ctx) + { + var settings = await _data.GetSystemGuildSettings(ctx.System, ctx.Guild.Id); + if (settings.AutoproxyMode == AutoproxyMode.Latch) + { + await ctx.Reply($"{Emojis.Note} Autoproxy is already set to latch mode in this server. If you want to disable autoproxying, use `pk;autoproxy off`."); + } + else + { + settings.AutoproxyMode = AutoproxyMode.Latch; + settings.AutoproxyMember = null; + await _data.SetSystemGuildSettings(ctx.System, ctx.Guild.Id, settings); + await ctx.Reply($"{Emojis.Success} Autoproxy set to latch mode in this server. Messages will now be autoproxied using the *last-proxied member* in this server."); + } + } + + private async Task AutoproxyFront(Context ctx) + { + var settings = await _data.GetSystemGuildSettings(ctx.System, ctx.Guild.Id); + if (settings.AutoproxyMode == AutoproxyMode.Front) + { + await ctx.Reply($"{Emojis.Note} Autoproxy is already set to front mode in this server. If you want to disable autoproxying, use `pk;autoproxy off`."); + } + else + { + settings.AutoproxyMode = AutoproxyMode.Front; + settings.AutoproxyMember = null; + await _data.SetSystemGuildSettings(ctx.System, ctx.Guild.Id, settings); + await ctx.Reply($"{Emojis.Success} Autoproxy set to front mode in this server. Messages will now be autoproxied using the *current first fronter*, if any."); + } + } + + private async Task AutoproxyMember(Context ctx, PKMember member) + { + ctx.CheckOwnMember(member); + + var settings = await _data.GetSystemGuildSettings(ctx.System, ctx.Guild.Id); + settings.AutoproxyMode = AutoproxyMode.Member; + settings.AutoproxyMember = member.Id; + await _data.SetSystemGuildSettings(ctx.System, ctx.Guild.Id, settings); + + await ctx.Reply($"{Emojis.Success} Autoproxy set to **{member.Name}** in this server."); + } + + private async Task CreateAutoproxyStatusEmbed(Context ctx) + { + var settings = await _data.GetSystemGuildSettings(ctx.System, ctx.Guild.Id); + + var commandList = "**pk;autoproxy latch** - Autoproxies as last-proxied member\n**pk;autoproxy front** - Autoproxies as current (first) fronter\n**pk;autoproxy ** - Autoproxies as a specific member"; + var eb = new EmbedBuilder().WithTitle($"Current autoproxy status (for {ctx.Guild.Name.EscapeMarkdown()})"); + + switch (settings.AutoproxyMode) { + case AutoproxyMode.Off: eb.WithDescription($"Autoproxy is currently **off** in this server. To enable it, use one of the following commands:\n{commandList}"); + break; + case AutoproxyMode.Front: { + var lastSwitch = await _data.GetLatestSwitch(ctx.System); + if (lastSwitch == null) + eb.WithDescription("Autoproxy is currently set to **front mode** in this server, but you have no registered switches. Use the `pk;switch` command to log one."); + else + { + var firstMember = await _data.GetSwitchMembers(lastSwitch).FirstOrDefaultAsync(); + eb.WithDescription(firstMember == null + ? "Autoproxy is currently set to **front mode** in this server, but there are currently no fronters registered." + : $"Autoproxy is currently set to **front mode** in this server. The current (first) fronter is **{firstMember.Name.EscapeMarkdown()}** (`{firstMember.Hid}`). To disable, type `pk;autoproxy off`."); + } + + break; + } + // AutoproxyMember is never null if Mode is Member, this is just to make the compiler shut up + case AutoproxyMode.Member when settings.AutoproxyMember != null: { + var member = await _data.GetMemberById(settings.AutoproxyMember.Value); + eb.WithDescription($"Autoproxy is active for member **{member.Name}** (`{member.Hid}`) in this server. To disable, type `pk;autoproxy off`."); + break; + } + case AutoproxyMode.Latch: + eb.WithDescription($"Autoproxy is currently set to **latch mode**, meaning the *last-proxied member* will be autoproxied. To disable, type `pk;autoproxy off`."); + break; + default: throw new ArgumentOutOfRangeException(); + } + + return eb.Build(); + } + } +} \ No newline at end of file diff --git a/PluralKit.Bot/Commands/CommandTree.cs b/PluralKit.Bot/Commands/CommandTree.cs index d0489150..d004f3f4 100644 --- a/PluralKit.Bot/Commands/CommandTree.cs +++ b/PluralKit.Bot/Commands/CommandTree.cs @@ -23,6 +23,7 @@ namespace PluralKit.Bot.Commands public static Command SystemFrontHistory = new Command("system fronthistory", "system [system] fronthistory", "Shows a system's front history"); public static Command SystemFrontPercent = new Command("system frontpercent", "system [system] frontpercent [timespan]", "Shows a system's front breakdown"); public static Command SystemPrivacy = new Command("system privacy", "system privacy ", "Changes your system's privacy settings"); + public static Command Autoproxy = new Command("autoproxy", "autoproxy [off|front|latch|member]", "Sets your system's autoproxy mode for this server"); public static Command MemberInfo = new Command("member", "member ", "Looks up information about a member"); public static Command MemberNew = new Command("member new", "member new ", "Creates a new member"); public static Command MemberRename = new Command("member rename", "member rename ", "Renames a member"); @@ -87,6 +88,8 @@ namespace PluralKit.Bot.Commands return HandleMemberCommand(ctx); if (ctx.Match("switch", "sw")) return HandleSwitchCommand(ctx); + if (ctx.Match("ap", "autoproxy", "auto")) + return ctx.Execute(Autoproxy, m => m.Autoproxy(ctx)); if (ctx.Match("link")) return ctx.Execute(Link, m => m.LinkSystem(ctx)); if (ctx.Match("unlink")) diff --git a/PluralKit.Core/Migrations/3.sql b/PluralKit.Core/Migrations/3.sql index 2dc0660b..29cabda4 100644 --- a/PluralKit.Core/Migrations/3.sql +++ b/PluralKit.Core/Migrations/3.sql @@ -6,8 +6,10 @@ alter table system_guild add column autoproxy_mode int check (autoproxy_mode in (1, 2, 3, 4)) not null default 1; -- for member mode -alter table system_guild add column autoproxy_member nullable references members (id) on delete set null; +alter table system_guild add column autoproxy_member int references members (id) on delete set null; -- for latch mode -- not *really* nullable, null just means old (pre-schema-change) data. -alter table messages add column guild bigint nullable default null; \ No newline at end of file +alter table messages add column guild bigint default null; + +update info set schema_version = 3; \ No newline at end of file diff --git a/PluralKit.Core/SchemaService.cs b/PluralKit.Core/SchemaService.cs index abcb34de..3be9938c 100644 --- a/PluralKit.Core/SchemaService.cs +++ b/PluralKit.Core/SchemaService.cs @@ -10,7 +10,7 @@ using Serilog; namespace PluralKit { public class SchemaService { - private const int TargetSchemaVersion = 2; + private const int TargetSchemaVersion = 3; private DbConnectionFactory _conn; private ILogger _logger; diff --git a/PluralKit.Core/Stores.cs b/PluralKit.Core/Stores.cs index 64f70394..0801b4cc 100644 --- a/PluralKit.Core/Stores.cs +++ b/PluralKit.Core/Stores.cs @@ -77,9 +77,9 @@ namespace PluralKit { public class SystemGuildSettings { public bool ProxyEnabled { get; set; } = true; - - public AutoproxyMode AutoproxyMode { get; set; } - public int AutoproxyMember { get; set; } + + public AutoproxyMode AutoproxyMode { get; set; } = AutoproxyMode.Off; + public int? AutoproxyMember { get; set; } } public class MemberGuildSettings @@ -446,11 +446,13 @@ namespace PluralKit { public async Task SetSystemGuildSettings(PKSystem system, ulong guild, SystemGuildSettings settings) { using (var conn = await _conn.Obtain()) - await conn.ExecuteAsync("insert into system_guild (system, guild, proxy_enabled) values (@System, @Guild, @ProxyEnabled) on conflict (system, guild) do update set proxy_enabled = @ProxyEnabled", new + await conn.ExecuteAsync("insert into system_guild (system, guild, proxy_enabled, autoproxy_mode, autoproxy_member) values (@System, @Guild, @ProxyEnabled, @AutoproxyMode, @AutoproxyMember) on conflict (system, guild) do update set proxy_enabled = @ProxyEnabled, autoproxy_mode = @AutoproxyMode, autoproxy_member = @AutoproxyMember", new { System = system.Id, Guild = guild, - ProxyEnabled = settings.ProxyEnabled + settings.ProxyEnabled, + settings.AutoproxyMode, + settings.AutoproxyMember }); }