2020-04-24 19:50:28 +00:00
using System ;
2020-01-23 20:20:22 +00:00
using System.Threading.Tasks ;
2020-12-08 11:57:17 +00:00
using Humanizer ;
2020-12-24 13:52:44 +00:00
using Myriad.Builders ;
using Myriad.Types ;
2020-12-08 11:57:17 +00:00
using NodaTime ;
2020-02-12 14:16:19 +00:00
using PluralKit.Core ;
2020-01-23 20:20:22 +00:00
2020-02-12 14:16:19 +00:00
namespace PluralKit.Bot
2020-01-23 20:20:22 +00:00
{
2020-02-01 12:03:02 +00:00
public class Autoproxy
2020-01-23 20:20:22 +00:00
{
2020-06-13 17:36:43 +00:00
private readonly IDatabase _db ;
2020-08-29 11:46:27 +00:00
private readonly ModelRepository _repo ;
2020-01-23 20:20:22 +00:00
2020-08-29 11:46:27 +00:00
public Autoproxy ( IDatabase db , ModelRepository repo )
2020-01-23 20:20:22 +00:00
{
2020-06-13 14:03:57 +00:00
_db = db ;
2020-08-29 11:46:27 +00:00
_repo = repo ;
2020-01-23 20:20:22 +00:00
}
2020-11-21 01:48:33 +00:00
public async Task SetAutoproxyMode ( Context ctx )
2020-01-23 20:20:22 +00:00
{
2020-11-21 01:48:33 +00:00
// no need to check account here, it's already done at CommandTree
2020-11-20 23:34:08 +00:00
ctx . CheckGuildContext ( ) ;
2021-08-27 15:03:47 +00:00
2020-07-09 13:11:04 +00:00
if ( ctx . Match ( "off" , "stop" , "cancel" , "no" , "disable" , "remove" ) )
2020-01-23 20:20:22 +00:00
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 <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 ( ) )
await ctx . Reply ( embed : await CreateAutoproxyStatusEmbed ( ctx ) ) ;
else
2020-08-25 20:44:52 +00:00
throw new PKSyntaxError ( $"Invalid autoproxy mode {ctx.PopArgument().AsCode()}." ) ;
2020-01-23 20:20:22 +00:00
}
private async Task AutoproxyOff ( Context ctx )
{
2020-06-13 14:03:57 +00:00
if ( ctx . MessageContext . AutoproxyMode = = AutoproxyMode . Off )
2020-01-23 20:20:22 +00:00
await ctx . Reply ( $"{Emojis.Note} Autoproxy is already off in this server." ) ;
else
{
2020-06-13 14:03:57 +00:00
await UpdateAutoproxy ( ctx , AutoproxyMode . Off , null ) ;
2020-01-23 20:20:22 +00:00
await ctx . Reply ( $"{Emojis.Success} Autoproxy turned off in this server." ) ;
}
}
private async Task AutoproxyLatch ( Context ctx )
{
2020-06-13 14:03:57 +00:00
if ( ctx . MessageContext . AutoproxyMode = = AutoproxyMode . Latch )
2020-01-23 20:20:22 +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`." ) ;
else
{
2020-06-13 14:03:57 +00:00
await UpdateAutoproxy ( ctx , AutoproxyMode . Latch , null ) ;
2020-01-23 20:20:22 +00:00
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 )
{
2020-06-13 14:03:57 +00:00
if ( ctx . MessageContext . AutoproxyMode = = AutoproxyMode . Front )
2020-01-23 20:20:22 +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`." ) ;
else
{
2020-06-13 14:03:57 +00:00
await UpdateAutoproxy ( ctx , AutoproxyMode . Front , null ) ;
2020-01-23 20:20:22 +00:00
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 ) ;
2020-06-13 14:03:57 +00:00
await UpdateAutoproxy ( ctx , AutoproxyMode . Member , member . Id ) ;
2020-06-18 15:08:36 +00:00
await ctx . Reply ( $"{Emojis.Success} Autoproxy set to **{member.NameFor(ctx)}** in this server." ) ;
2020-01-23 20:20:22 +00:00
}
2020-12-24 13:52:44 +00:00
private async Task < Embed > CreateAutoproxyStatusEmbed ( Context ctx )
2020-01-23 20:20:22 +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" ;
2020-12-24 13:52:44 +00:00
var eb = new EmbedBuilder ( )
2021-01-31 15:16:52 +00:00
. Title ( $"Current autoproxy status (for {ctx.Guild.Name.EscapeMarkdown()})" ) ;
2021-08-27 15:03:47 +00:00
2020-06-13 14:03:57 +00:00
var fronters = ctx . MessageContext . LastSwitchMembers ;
var relevantMember = ctx . MessageContext . AutoproxyMode switch
{
2021-09-30 01:51:38 +00:00
AutoproxyMode . Front = > fronters . Length > 0 ? await _repo . GetMember ( fronters [ 0 ] ) : null ,
AutoproxyMode . Member = > await _repo . GetMember ( ctx . MessageContext . AutoproxyMember . Value ) ,
2020-06-13 14:03:57 +00:00
_ = > null
} ;
2020-01-23 20:20:22 +00:00
2021-08-27 15:03:47 +00:00
switch ( ctx . MessageContext . AutoproxyMode )
{
2020-12-24 13:52:44 +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}" ) ;
2020-01-23 20:20:22 +00:00
break ;
2020-06-13 14:03:57 +00:00
case AutoproxyMode . Front :
2020-01-23 20:20:22 +00:00
{
2021-08-27 15:03:47 +00:00
if ( fronters . Length = = 0 )
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." ) ;
else
{
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`." ) ;
}
break ;
2020-01-23 20:20:22 +00:00
}
// AutoproxyMember is never null if Mode is Member, this is just to make the compiler shut up
2021-08-27 15:03:47 +00:00
case AutoproxyMode . Member when relevantMember ! = null :
{
eb . Description ( $"Autoproxy is active for member **{relevantMember.NameFor(ctx)}** (`{relevantMember.Hid}`) in this server. To disable, type `pk;autoproxy off`." ) ;
break ;
}
2020-01-23 20:20:22 +00:00
case AutoproxyMode . Latch :
2020-12-24 13:52:44 +00:00
eb . Description ( "Autoproxy is currently set to **latch mode**, meaning the *last-proxied member* will be autoproxied. To disable, type `pk;autoproxy off`." ) ;
2020-01-23 20:20:22 +00:00
break ;
2021-08-27 15:03:47 +00:00
2020-01-23 20:20:22 +00:00
default : throw new ArgumentOutOfRangeException ( ) ;
}
2021-08-27 15:03:47 +00:00
if ( ! ctx . MessageContext . AllowAutoproxy )
2021-01-31 15:16:52 +00:00
eb . Field ( new ( "\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
2020-01-23 20:20:22 +00:00
return eb . Build ( ) ;
}
2020-06-29 13:20:28 +00:00
2020-11-21 01:48:33 +00:00
public async Task AutoproxyTimeout ( Context ctx )
2020-11-21 00:44:15 +00:00
{
if ( ! ctx . HasNext ( ) )
{
2020-12-08 11:57:17 +00:00
var timeout = ctx . System . LatchTimeout . HasValue
2021-08-27 15:03:47 +00:00
? Duration . FromSeconds ( ctx . System . LatchTimeout . Value )
: ( Duration ? ) null ;
2020-12-08 11:57:17 +00:00
if ( timeout = = null )
2021-04-13 13:55:46 +00:00
await ctx . Reply ( $"You do not have a custom autoproxy timeout duration set. The default latch timeout duration is {ProxyMatcher.DefaultLatchExpiryTime.ToTimeSpan().Humanize(4)}." ) ;
2020-12-08 11:57:17 +00:00
else if ( timeout = = Duration . Zero )
await ctx . Reply ( "Latch timeout is currently **disabled** for your system. Latch mode autoproxy will never time out." ) ;
2020-11-21 00:44:15 +00:00
else
2021-04-13 13:55:46 +00:00
await ctx . Reply ( $"The current latch timeout duration for your system is {timeout.Value.ToTimeSpan().Humanize(4)}." ) ;
2020-11-21 00:44:15 +00:00
return ;
}
2021-04-13 13:55:46 +00:00
Duration ? newTimeout ;
Duration overflow = Duration . Zero ;
if ( ctx . Match ( "off" , "stop" , "cancel" , "no" , "disable" , "remove" ) ) newTimeout = Duration . Zero ;
else if ( ctx . Match ( "reset" , "default" ) ) newTimeout = null ;
else
2020-12-24 21:27:03 +00:00
{
2021-04-13 13:55:46 +00:00
var timeoutStr = ctx . RemainderOrNull ( ) ;
var timeoutPeriod = DateUtils . ParsePeriod ( timeoutStr ) ;
if ( timeoutPeriod = = null ) throw new PKError ( $"Could not parse '{timeoutStr}' as a valid duration. Try using a syntax such as \" 3 h5m \ " (i.e. 3 hours and 5 minutes)." ) ;
2021-05-03 08:34:53 +00:00
if ( timeoutPeriod . Value . TotalHours > 100000 )
2021-04-13 13:55:46 +00:00
{
// sanity check to prevent seconds overflow if someone types in 999999999
overflow = timeoutPeriod . Value ;
newTimeout = Duration . Zero ;
}
else newTimeout = timeoutPeriod ;
2020-12-24 21:27:03 +00:00
}
2020-12-24 18:41:46 +00:00
2021-09-30 01:51:38 +00:00
await _repo . UpdateSystem ( ctx . System . Id , new ( ) { LatchTimeout = ( int? ) newTimeout ? . TotalSeconds } ) ;
2021-08-27 15:03:47 +00:00
2021-04-13 13:55:46 +00:00
if ( newTimeout = = null )
await ctx . Reply ( $"{Emojis.Success} Latch timeout reset to default ({ProxyMatcher.DefaultLatchExpiryTime.ToTimeSpan().Humanize(4)})." ) ;
else if ( newTimeout = = Duration . Zero & & overflow ! = Duration . Zero )
await ctx . Reply ( $"{Emojis.Success} Latch timeout disabled. Latch mode autoproxy will never time out. ({overflow.ToTimeSpan().Humanize(4)} is too long)" ) ;
else if ( newTimeout = = Duration . Zero )
2020-12-08 11:57:17 +00:00
await ctx . Reply ( $"{Emojis.Success} Latch timeout disabled. Latch mode autoproxy will never time out." ) ;
2020-11-21 00:44:15 +00:00
else
2021-04-13 13:55:46 +00:00
await ctx . Reply ( $"{Emojis.Success} Latch timeout set to {newTimeout.Value!.ToTimeSpan().Humanize(4)}." ) ;
2020-11-21 00:44:15 +00:00
}
2020-11-21 01:48:33 +00:00
public async Task AutoproxyAccount ( Context ctx )
2020-11-20 23:34:08 +00:00
{
2020-11-21 01:26:34 +00:00
// todo: this might be useful elsewhere, consider moving it to ctx.MatchToggle
2020-11-20 23:34:08 +00:00
if ( ctx . Match ( "enable" , "on" ) )
await AutoproxyEnableDisable ( ctx , true ) ;
else if ( ctx . Match ( "disable" , "off" ) )
await AutoproxyEnableDisable ( ctx , false ) ;
else if ( ctx . HasNext ( ) )
throw new PKSyntaxError ( "You must pass either \"on\" or \"off\"." ) ;
else
{
var statusString = ctx . MessageContext . AllowAutoproxy ? "enabled" : "disabled" ;
2021-01-31 15:16:52 +00:00
await ctx . Reply ( $"Autoproxy is currently **{statusString}** for account <@{ctx.Author.Id}>." ) ;
2020-11-20 23:34:08 +00:00
}
}
private async Task AutoproxyEnableDisable ( Context ctx , bool allow )
{
var statusString = allow ? "enabled" : "disabled" ;
if ( ctx . MessageContext . AllowAutoproxy = = allow )
{
2021-01-31 15:16:52 +00:00
await ctx . Reply ( $"{Emojis.Note} Autoproxy is already {statusString} for account <@{ctx.Author.Id}>." ) ;
2020-11-20 23:34:08 +00:00
return ;
}
var patch = new AccountPatch { AllowAutoproxy = allow } ;
2021-09-30 01:51:38 +00:00
await _repo . UpdateAccount ( ctx . Author . Id , patch ) ;
2021-01-31 15:16:52 +00:00
await ctx . Reply ( $"{Emojis.Success} Autoproxy {statusString} for account <@{ctx.Author.Id}>." ) ;
2020-11-20 23:34:08 +00:00
}
2021-09-30 01:51:38 +00:00
private async Task UpdateAutoproxy ( Context ctx , AutoproxyMode autoproxyMode , MemberId ? autoproxyMember )
2020-06-29 13:20:28 +00:00
{
2021-09-30 01:51:38 +00:00
await _repo . GetSystemGuild ( ctx . Guild . Id , ctx . System . Id ) ;
2021-08-27 15:03:47 +00:00
var patch = new SystemGuildPatch { AutoproxyMode = autoproxyMode , AutoproxyMember = autoproxyMember } ;
2021-09-30 01:51:38 +00:00
await _repo . UpdateSystemGuild ( ctx . System . Id , ctx . Guild . Id , patch ) ;
2020-06-29 13:20:28 +00:00
}
2020-01-23 20:20:22 +00:00
}
}