2022-08-27 10:44:43 +00:00
using NodaTime ;
2020-12-24 13:52:44 +00:00
using Myriad.Builders ;
using Myriad.Types ;
2020-02-12 14:16:19 +00:00
using PluralKit.Core ;
2020-01-23 20:20:22 +00:00
2021-11-27 02:10:56 +00:00
namespace PluralKit.Bot ;
public class Autoproxy
2020-01-23 20:20:22 +00:00
{
2022-08-27 10:44:43 +00:00
private readonly IClock _clock ;
public Autoproxy ( IClock clock )
{
_clock = clock ;
}
2021-11-27 02:10:56 +00:00
public async Task SetAutoproxyMode ( Context ctx )
{
// no need to check account here, it's already done at CommandTree
ctx . CheckGuildContext ( ) ;
2022-03-22 03:43:33 +00:00
// for now, just for guild
// this also creates settings if there are none present
var settings = await ctx . Repository . GetAutoproxySettings ( ctx . System . Id , ctx . Guild . Id , null ) ;
2021-11-27 02:10:56 +00:00
if ( ctx . Match ( "off" , "stop" , "cancel" , "no" , "disable" , "remove" ) )
2022-03-22 03:43:33 +00:00
await AutoproxyOff ( ctx , settings ) ;
2022-05-08 06:30:46 +00:00
else if ( ctx . Match ( "latch" , "last" , "proxy" , "stick" , "sticky" , "l" ) )
2022-03-22 03:43:33 +00:00
await AutoproxyLatch ( ctx , settings ) ;
2022-05-08 06:30:46 +00:00
else if ( ctx . Match ( "front" , "fronter" , "switch" , "f" ) )
2022-03-22 03:43:33 +00:00
await AutoproxyFront ( ctx , settings ) ;
2021-11-27 02:10:56 +00:00
else if ( ctx . Match ( "member" ) )
throw new PKSyntaxError ( "Member-mode autoproxy must target a specific member. Use the `pk;autoproxy <member>` 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 ( ) )
2022-03-22 03:43:33 +00:00
await ctx . Reply ( embed : await CreateAutoproxyStatusEmbed ( ctx , settings ) ) ;
2021-11-27 02:10:56 +00:00
else
throw new PKSyntaxError ( $"Invalid autoproxy mode {ctx.PopArgument().AsCode()}." ) ;
}
2022-03-22 03:43:33 +00:00
private async Task AutoproxyOff ( Context ctx , AutoproxySettings settings )
2021-11-27 02:10:56 +00:00
{
2022-03-22 03:43:33 +00:00
if ( settings . AutoproxyMode = = AutoproxyMode . Off )
2020-01-23 20:20:22 +00:00
{
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Note} Autoproxy is already off in this server." ) ;
2020-01-23 20:20:22 +00:00
}
2021-11-27 02:10:56 +00:00
else
2020-01-23 20:20:22 +00:00
{
2021-11-27 02:10:56 +00:00
await UpdateAutoproxy ( ctx , AutoproxyMode . Off , null ) ;
await ctx . Reply ( $"{Emojis.Success} Autoproxy turned off in this server." ) ;
2020-01-23 20:20:22 +00:00
}
2021-11-27 02:10:56 +00:00
}
2020-01-23 20:20:22 +00:00
2022-03-22 03:43:33 +00:00
private async Task AutoproxyLatch ( Context ctx , AutoproxySettings settings )
2021-11-27 02:10:56 +00:00
{
2022-03-22 03:43:33 +00:00
if ( settings . AutoproxyMode = = AutoproxyMode . Latch )
2020-01-23 20:20:22 +00:00
{
2021-11-27 02:10:56 +00:00
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`." ) ;
2020-01-23 20:20:22 +00:00
}
2021-11-27 02:10:56 +00:00
else
2020-01-23 20:20:22 +00:00
{
2021-11-27 02:10:56 +00:00
await UpdateAutoproxy ( ctx , AutoproxyMode . Latch , null ) ;
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." ) ;
2020-01-23 20:20:22 +00:00
}
2021-11-27 02:10:56 +00:00
}
2020-01-23 20:20:22 +00:00
2022-03-22 03:43:33 +00:00
private async Task AutoproxyFront ( Context ctx , AutoproxySettings settings )
2021-11-27 02:10:56 +00:00
{
2022-03-22 03:43:33 +00:00
if ( settings . AutoproxyMode = = AutoproxyMode . Front )
2020-01-23 20:20:22 +00:00
{
2021-11-27 02:10:56 +00:00
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`." ) ;
2020-01-23 20:20:22 +00:00
}
2021-11-27 02:10:56 +00:00
else
2020-01-23 20:20:22 +00:00
{
2021-11-27 02:10:56 +00:00
await UpdateAutoproxy ( ctx , AutoproxyMode . Front , null ) ;
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." ) ;
2020-01-23 20:20:22 +00:00
}
2021-11-27 02:10:56 +00:00
}
2020-01-23 20:20:22 +00:00
2021-11-27 02:10:56 +00:00
private async Task AutoproxyMember ( Context ctx , PKMember member )
{
ctx . CheckOwnMember ( member ) ;
2021-08-27 15:03:47 +00:00
2022-03-22 03:43:33 +00:00
// todo: why does this not throw an error if the member is already set
2021-11-27 02:10:56 +00:00
await UpdateAutoproxy ( ctx , AutoproxyMode . Member , member . Id ) ;
await ctx . Reply ( $"{Emojis.Success} Autoproxy set to **{member.NameFor(ctx)}** in this server." ) ;
}
2020-01-23 20:20:22 +00:00
2022-03-22 03:43:33 +00:00
private async Task < Embed > CreateAutoproxyStatusEmbed ( Context ctx , AutoproxySettings settings )
2021-11-27 02:10:56 +00:00
{
var commandList = "**pk;autoproxy latch** - Autoproxies as last-proxied member"
+ "\n**pk;autoproxy front** - Autoproxies as current (first) fronter"
+ "\n**pk;autoproxy <member>** - Autoproxies as a specific member" ;
var eb = new EmbedBuilder ( )
. Title ( $"Current autoproxy status (for {ctx.Guild.Name.EscapeMarkdown()})" ) ;
2022-06-15 23:28:34 +00:00
var sw = await ctx . Repository . GetLatestSwitch ( ctx . System . Id ) ;
2022-06-27 09:42:23 +00:00
var fronters = sw = = null ? new ( ) : await ctx . Database . Execute ( c = > ctx . Repository . GetSwitchMembers ( c , sw . Id ) ) . ToListAsync ( ) ;
2022-08-27 10:44:43 +00:00
var latchTimeout = ctx . Config . LatchTimeout . HasValue ? Duration . FromSeconds ( ctx . Config . LatchTimeout . Value ) : ProxyMatcher . DefaultLatchExpiryTime ;
2022-06-15 23:28:34 +00:00
2022-03-22 03:43:33 +00:00
var relevantMember = settings . AutoproxyMode switch
2021-11-27 02:10:56 +00:00
{
2022-06-15 23:28:34 +00:00
AutoproxyMode . Front = > fronters . Count > 0 ? fronters [ 0 ] : null ,
2022-03-22 03:43:33 +00:00
AutoproxyMode . Member when settings . AutoproxyMember . HasValue = > await ctx . Repository . GetMember ( settings . AutoproxyMember . Value ) ,
2022-08-27 10:44:43 +00:00
AutoproxyMode . Latch when settings . AutoproxyMember . HasValue & & ctx . Config . LatchTimeout = = 0 = > await ctx . Repository . GetMember ( settings . AutoproxyMember . Value ) ,
2022-11-22 14:13:33 +00:00
AutoproxyMode . Latch when settings . AutoproxyMember . HasValue = >
2022-08-27 10:44:43 +00:00
_clock . GetCurrentInstant ( ) - settings . LastLatchTimestamp > latchTimeout
? null
: await ctx . Repository . GetMember ( settings . AutoproxyMember . Value ) ,
2021-11-27 02:10:56 +00:00
_ = > null
} ;
2022-03-22 03:43:33 +00:00
switch ( settings . AutoproxyMode )
2021-11-27 02:10:56 +00:00
{
case AutoproxyMode . Off :
eb . Description ( $"Autoproxy is currently **off** in this server. To enable it, use one of the following commands:\n{commandList}" ) ;
break ;
case AutoproxyMode . Front :
{
2022-06-15 23:28:34 +00:00
if ( fronters . Count = = 0 )
2020-01-23 20:20:22 +00:00
{
2021-11-27 02:10:56 +00:00
eb . Description ( "Autoproxy is currently set to **front mode** in this server, but there are currently no fronters registered. Use the `pk;switch` command to log a switch." ) ;
2020-01-23 20:20:22 +00:00
}
2021-11-27 02:10:56 +00:00
else
2021-08-27 15:03:47 +00:00
{
2021-11-27 02:10:56 +00:00
if ( relevantMember = = null )
throw new ArgumentException ( "Attempted to print member autoproxy status, but the linked member ID wasn't found in the database. Should be handled appropriately." ) ;
eb . Description ( $"Autoproxy is currently set to **front mode** in this server. The current (first) fronter is **{relevantMember.NameFor(ctx).EscapeMarkdown()}** (`{relevantMember.Hid}`). To disable, type `pk;autoproxy off`." ) ;
2021-08-27 15:03:47 +00:00
}
2021-11-27 02:10:56 +00:00
2020-01-23 20:20:22 +00:00
break ;
2021-11-27 02:10:56 +00:00
}
2022-03-30 09:06:47 +00:00
case AutoproxyMode . Member :
2021-11-27 02:10:56 +00:00
{
2022-03-30 09:06:47 +00:00
if ( relevantMember = = null )
// just pretend autoproxy is off if the member was deleted
// ideally we would set it to off in the database though...
eb . Description ( $"Autoproxy is currently **off** in this server. To enable it, use one of the following commands:\n{commandList}" ) ;
else
eb . Description ( $"Autoproxy is active for member **{relevantMember.NameFor(ctx)}** (`{relevantMember.Hid}`) in this server. To disable, type `pk;autoproxy off`." ) ;
2021-11-27 02:10:56 +00:00
break ;
}
case AutoproxyMode . Latch :
2022-08-27 10:44:43 +00:00
if ( relevantMember = = null )
eb . Description ( "Autoproxy is currently set to **latch mode**, meaning the *last-proxied member* will be autoproxied. **No member is currently latched.** To disable, type `pk;autoproxy off`." ) ;
else
eb . Description ( $"Autoproxy is currently set to **latch mode**, meaning the *last-proxied member* will be autoproxied. The currently latched member is **{relevantMember.NameFor(ctx)}** (`{relevantMember.Hid}`). To disable, type `pk;autoproxy off`." ) ;
2021-11-27 02:10:56 +00:00
break ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
default : throw new ArgumentOutOfRangeException ( ) ;
}
2020-01-23 20:20:22 +00:00
2022-06-15 23:28:34 +00:00
var allowAutoproxy = await ctx . Repository . GetAutoproxyEnabled ( ctx . Author . Id ) ;
if ( ! allowAutoproxy )
2021-11-27 02:10:56 +00:00
eb . Field ( new Embed . Field ( "\u200b" , $"{Emojis.Note} Autoproxy is currently **disabled** for your account (<@{ctx.Author.Id}>). To enable it, use `pk;autoproxy account enable`." ) ) ;
2020-11-20 23:34:08 +00:00
2021-11-27 02:10:56 +00:00
return eb . Build ( ) ;
}
private async Task UpdateAutoproxy ( Context ctx , AutoproxyMode autoproxyMode , MemberId ? autoproxyMember )
{
2022-03-22 03:43:33 +00:00
var patch = new AutoproxyPatch { AutoproxyMode = autoproxyMode , AutoproxyMember = autoproxyMember } ;
await ctx . Repository . UpdateAutoproxy ( ctx . System . Id , ctx . Guild . Id , null , patch ) ;
2020-01-23 20:20:22 +00:00
}
}