2019-06-13 14:53:04 +00:00
using System.Collections.Generic ;
using System.Linq ;
using System.Threading.Tasks ;
using Discord ;
2019-10-05 05:41:00 +00:00
2019-06-13 14:53:04 +00:00
using NodaTime ;
using NodaTime.TimeZones ;
2019-10-05 05:41:00 +00:00
using PluralKit.Bot.CommandSystem ;
2019-06-13 14:53:04 +00:00
namespace PluralKit.Bot.Commands
{
2019-10-05 05:41:00 +00:00
public class SwitchCommands
2019-06-13 14:53:04 +00:00
{
2019-10-26 17:45:30 +00:00
private IDataStore _data ;
2019-10-05 05:41:00 +00:00
2019-10-26 17:45:30 +00:00
public SwitchCommands ( IDataStore data )
2019-10-05 05:41:00 +00:00
{
2019-10-26 17:45:30 +00:00
_data = data ;
2019-10-05 05:41:00 +00:00
}
2019-06-13 14:53:04 +00:00
2019-10-05 05:41:00 +00:00
public async Task Switch ( Context ctx )
{
ctx . CheckSystem ( ) ;
var members = new List < PKMember > ( ) ;
2019-06-13 14:53:04 +00:00
2019-10-05 05:41:00 +00:00
// Loop through all the given arguments
while ( ctx . HasNext ( ) )
{
// and attempt to match a member
var member = await ctx . MatchMember ( ) ;
if ( member = = null )
// if we can't, big error. Every member name must be valid.
throw new PKError ( ctx . CreateMemberNotFoundError ( ctx . PopArgument ( ) ) ) ;
ctx . CheckOwnMember ( member ) ; // Ensure they're in our own system
members . Add ( member ) ; // Then add to the final output list
}
2019-06-13 14:53:04 +00:00
2019-10-05 05:41:00 +00:00
// Finally, do the actual switch
await DoSwitchCommand ( ctx , members ) ;
}
public async Task SwitchOut ( Context ctx )
2019-06-13 14:53:04 +00:00
{
2019-10-05 05:41:00 +00:00
ctx . CheckSystem ( ) ;
2019-07-09 22:25:47 +00:00
2019-10-05 05:41:00 +00:00
// Switch with no members = switch-out
await DoSwitchCommand ( ctx , new PKMember [ ] { } ) ;
}
private async Task DoSwitchCommand ( Context ctx , ICollection < PKMember > members )
{
2019-06-13 14:53:04 +00:00
// Make sure there are no dupes in the list
// We do this by checking if removing duplicate member IDs results in a list of different length
if ( members . Select ( m = > m . Id ) . Distinct ( ) . Count ( ) ! = members . Count ) throw Errors . DuplicateSwitchMembers ;
2019-07-09 22:25:47 +00:00
2019-06-13 14:53:04 +00:00
// Find the last switch and its members if applicable
2019-10-26 17:45:30 +00:00
var lastSwitch = await _data . GetLatestSwitch ( ctx . System ) ;
2019-06-13 14:53:04 +00:00
if ( lastSwitch ! = null )
{
2019-10-26 17:45:30 +00:00
var lastSwitchMembers = await _data . GetSwitchMembers ( lastSwitch ) ;
2019-06-13 14:53:04 +00:00
// Make sure the requested switch isn't identical to the last one
if ( lastSwitchMembers . Select ( m = > m . Id ) . SequenceEqual ( members . Select ( m = > m . Id ) ) )
throw Errors . SameSwitch ( members ) ;
}
2019-10-26 17:45:30 +00:00
await _data . AddSwitch ( ctx . System , members ) ;
2019-06-13 14:53:04 +00:00
if ( members . Count = = 0 )
2019-10-05 05:41:00 +00:00
await ctx . Reply ( $"{Emojis.Success} Switch-out registered." ) ;
2019-06-13 14:53:04 +00:00
else
2019-10-18 11:14:36 +00:00
await ctx . Reply ( $"{Emojis.Success} Switch registered. Current fronter is now {string.Join(" , ", members.Select(m => m.Name)).SanitizeMentions()}." ) ;
2019-06-13 14:53:04 +00:00
}
2019-10-05 05:41:00 +00:00
public async Task SwitchMove ( Context ctx )
2019-06-13 14:53:04 +00:00
{
2019-10-05 05:41:00 +00:00
ctx . CheckSystem ( ) ;
var timeToMove = ctx . RemainderOrNull ( ) ? ? throw new PKSyntaxError ( "Must pass a date or time to move the switch to." ) ;
var tz = TzdbDateTimeZoneSource . Default . ForId ( ctx . System . UiTz ? ? "UTC" ) ;
2019-06-13 14:53:04 +00:00
2019-10-05 05:41:00 +00:00
var result = PluralKit . Utils . ParseDateTime ( timeToMove , true , tz ) ;
if ( result = = null ) throw Errors . InvalidDateTime ( timeToMove ) ;
2019-06-13 14:53:04 +00:00
var time = result . Value ;
if ( time . ToInstant ( ) > SystemClock . Instance . GetCurrentInstant ( ) ) throw Errors . SwitchTimeInFuture ;
// Fetch the last two switches for the system to do bounds checking on
2019-10-26 17:45:30 +00:00
var lastTwoSwitches = ( await _data . GetSwitches ( ctx . System , 2 ) ) . ToArray ( ) ;
2019-06-13 14:53:04 +00:00
// If we don't have a switch to move, don't bother
if ( lastTwoSwitches . Length = = 0 ) throw Errors . NoRegisteredSwitches ;
// If there's a switch *behind* the one we move, we check to make srue we're not moving the time further back than that
if ( lastTwoSwitches . Length = = 2 )
{
if ( lastTwoSwitches [ 1 ] . Timestamp > time . ToInstant ( ) )
throw Errors . SwitchMoveBeforeSecondLast ( lastTwoSwitches [ 1 ] . Timestamp . InZone ( tz ) ) ;
}
// Now we can actually do the move, yay!
// But, we do a prompt to confirm.
2019-10-26 17:45:30 +00:00
var lastSwitchMembers = await _data . GetSwitchMembers ( lastTwoSwitches [ 0 ] ) ;
2019-06-13 15:05:50 +00:00
var lastSwitchMemberStr = string . Join ( ", " , lastSwitchMembers . Select ( m = > m . Name ) ) ;
2019-10-05 05:41:00 +00:00
var lastSwitchTimeStr = Formats . ZonedDateTimeFormat . Format ( lastTwoSwitches [ 0 ] . Timestamp . InZone ( ctx . System . Zone ) ) ;
2019-06-15 10:33:24 +00:00
var lastSwitchDeltaStr = Formats . DurationFormat . Format ( SystemClock . Instance . GetCurrentInstant ( ) - lastTwoSwitches [ 0 ] . Timestamp ) ;
var newSwitchTimeStr = Formats . ZonedDateTimeFormat . Format ( time ) ;
var newSwitchDeltaStr = Formats . DurationFormat . Format ( SystemClock . Instance . GetCurrentInstant ( ) - time . ToInstant ( ) ) ;
2019-06-13 14:53:04 +00:00
// yeet
2019-10-18 11:14:36 +00:00
var msg = await ctx . Reply ( $"{Emojis.Warn} This will move the latest switch ({lastSwitchMemberStr.SanitizeMentions()}) from {lastSwitchTimeStr} ({lastSwitchDeltaStr} ago) to {newSwitchTimeStr} ({newSwitchDeltaStr} ago). Is this OK?" ) ;
2019-10-05 05:41:00 +00:00
if ( ! await ctx . PromptYesNo ( msg ) ) throw Errors . SwitchMoveCancelled ;
2019-06-13 14:53:04 +00:00
// aaaand *now* we do the move
2019-10-26 17:45:30 +00:00
await _data . MoveSwitch ( lastTwoSwitches [ 0 ] , time . ToInstant ( ) ) ;
2019-10-05 05:41:00 +00:00
await ctx . Reply ( $"{Emojis.Success} Switch moved." ) ;
2019-06-13 14:53:04 +00:00
}
2019-10-05 05:41:00 +00:00
public async Task SwitchDelete ( Context ctx )
2019-06-13 15:05:50 +00:00
{
2019-10-05 05:41:00 +00:00
ctx . CheckSystem ( ) ;
2019-06-13 15:05:50 +00:00
// Fetch the last two switches for the system to do bounds checking on
2019-10-26 17:45:30 +00:00
var lastTwoSwitches = ( await _data . GetSwitches ( ctx . System , 2 ) ) . ToArray ( ) ;
2019-06-13 15:05:50 +00:00
if ( lastTwoSwitches . Length = = 0 ) throw Errors . NoRegisteredSwitches ;
2019-10-26 17:45:30 +00:00
var lastSwitchMembers = await _data . GetSwitchMembers ( lastTwoSwitches [ 0 ] ) ;
2019-06-13 15:05:50 +00:00
var lastSwitchMemberStr = string . Join ( ", " , lastSwitchMembers . Select ( m = > m . Name ) ) ;
2019-06-15 10:33:24 +00:00
var lastSwitchDeltaStr = Formats . DurationFormat . Format ( SystemClock . Instance . GetCurrentInstant ( ) - lastTwoSwitches [ 0 ] . Timestamp ) ;
2019-06-13 15:05:50 +00:00
IUserMessage msg ;
if ( lastTwoSwitches . Length = = 1 )
{
2019-10-05 05:41:00 +00:00
msg = await ctx . Reply (
2019-10-18 11:14:36 +00:00
$"{Emojis.Warn} This will delete the latest switch ({lastSwitchMemberStr.SanitizeMentions()}, {lastSwitchDeltaStr} ago). You have no other switches logged. Is this okay?" ) ;
2019-06-13 15:05:50 +00:00
}
else
{
2019-10-26 17:45:30 +00:00
var secondSwitchMembers = await _data . GetSwitchMembers ( lastTwoSwitches [ 1 ] ) ;
2019-06-13 15:05:50 +00:00
var secondSwitchMemberStr = string . Join ( ", " , secondSwitchMembers . Select ( m = > m . Name ) ) ;
2019-06-15 10:33:24 +00:00
var secondSwitchDeltaStr = Formats . DurationFormat . Format ( SystemClock . Instance . GetCurrentInstant ( ) - lastTwoSwitches [ 1 ] . Timestamp ) ;
2019-10-05 05:41:00 +00:00
msg = await ctx . Reply (
2019-10-18 11:14:36 +00:00
$"{Emojis.Warn} This will delete the latest switch ({lastSwitchMemberStr.SanitizeMentions()}, {lastSwitchDeltaStr} ago). The next latest switch is {secondSwitchMemberStr.SanitizeMentions()} ({secondSwitchDeltaStr} ago). Is this okay?" ) ;
2019-06-13 15:05:50 +00:00
}
2019-10-05 05:41:00 +00:00
if ( ! await ctx . PromptYesNo ( msg ) ) throw Errors . SwitchDeleteCancelled ;
2019-10-26 17:45:30 +00:00
await _data . DeleteSwitch ( lastTwoSwitches [ 0 ] ) ;
2019-06-13 15:05:50 +00:00
2019-10-05 05:41:00 +00:00
await ctx . Reply ( $"{Emojis.Success} Switch deleted." ) ;
2019-06-13 15:05:50 +00:00
}
2019-06-13 14:53:04 +00:00
}
}