2018-09-09 19:53:32 +02:00
from datetime import datetime
2018-09-07 17:34:38 +02:00
from typing import List
2018-07-24 22:47:57 +02:00
2018-12-10 20:09:35 +01:00
import dateparser
2018-12-18 20:13:45 +01:00
import pytz
2018-12-10 20:09:35 +01:00
2018-07-24 22:47:57 +02:00
from pluralkit . bot . commands import *
2018-09-09 19:53:32 +02:00
from pluralkit . member import Member
2018-12-05 11:44:10 +01:00
from pluralkit . utils import display_relative
2018-07-24 22:47:57 +02:00
2018-12-05 11:44:10 +01:00
async def switch_root ( ctx : CommandContext ) :
if not ctx . has_next ( ) :
2018-12-10 20:09:35 +01:00
raise CommandError ( " You must use a subcommand. For a list of subcommands, type `pk;help member`. " )
2018-12-05 11:44:10 +01:00
if ctx . match ( " out " ) :
await switch_out ( ctx )
elif ctx . match ( " move " ) :
await switch_move ( ctx )
elif ctx . match ( " delete " ) or ctx . match ( " remove " ) or ctx . match ( " erase " ) or ctx . match ( " cancel " ) :
await switch_delete ( ctx )
else :
await switch_member ( ctx )
2018-07-24 22:47:57 +02:00
2018-09-07 17:34:38 +02:00
async def switch_member ( ctx : CommandContext ) :
system = await ctx . ensure_system ( )
if not ctx . has_next ( ) :
2018-12-05 11:44:10 +01:00
raise CommandError ( " You must pass at least one member name or ID to register a switch to. " )
2018-07-24 22:47:57 +02:00
members : List [ Member ] = [ ]
2018-12-05 11:44:10 +01:00
while ctx . has_next ( ) :
members . append ( await ctx . pop_member ( ) )
2018-07-24 22:47:57 +02:00
# Log the switch
2018-12-05 11:44:10 +01:00
await system . add_switch ( ctx . conn , members )
2018-07-24 22:47:57 +02:00
if len ( members ) == 1 :
2018-11-15 21:05:13 +01:00
await ctx . reply_ok ( " Switch registered. Current fronter is now {} . " . format ( members [ 0 ] . name ) )
2018-07-24 22:47:57 +02:00
else :
2018-11-15 21:05:13 +01:00
await ctx . reply_ok (
2018-09-07 17:34:38 +02:00
" Switch registered. Current fronters are now {} . " . format ( " , " . join ( [ m . name for m in members ] ) ) )
async def switch_out ( ctx : CommandContext ) :
system = await ctx . ensure_system ( )
2018-07-24 22:47:57 +02:00
2018-12-05 11:44:10 +01:00
switch = await system . get_latest_switch ( ctx . conn )
if switch and not switch . members :
2018-11-15 21:05:13 +01:00
raise CommandError ( " There ' s already no one in front. " )
2018-07-24 22:47:57 +02:00
# Log it, and don't log any members
2018-12-05 11:44:10 +01:00
await system . add_switch ( ctx . conn , [ ] )
2018-11-15 21:05:13 +01:00
await ctx . reply_ok ( " Switch-out registered. " )
2018-09-07 17:34:38 +02:00
2018-07-24 22:47:57 +02:00
2018-12-05 11:44:10 +01:00
async def switch_delete ( ctx : CommandContext ) :
system = await ctx . ensure_system ( )
last_two_switches = await system . get_switches ( ctx . conn , 2 )
if not last_two_switches :
raise CommandError ( " You do not have a logged switch to delete. " )
last_switch = last_two_switches [ 0 ]
next_last_switch = last_two_switches [ 1 ] if len ( last_two_switches ) > 1 else None
last_switch_members = " , " . join ( [ member . name for member in await last_switch . fetch_members ( ctx . conn ) ] )
last_switch_time = display_relative ( last_switch . timestamp )
if next_last_switch :
next_last_switch_members = " , " . join ( [ member . name for member in await next_last_switch . fetch_members ( ctx . conn ) ] )
next_last_switch_time = display_relative ( next_last_switch . timestamp )
msg = await ctx . reply_warn ( " This will delete the latest switch ( {} , {} ago). The next latest switch is {} ( {} ago). Is this okay? " . format ( last_switch_members , last_switch_time , next_last_switch_members , next_last_switch_time ) )
else :
msg = await ctx . reply_warn ( " This will delete the latest switch ( {} , {} ago). You have no other switches logged. Is this okay? " . format ( last_switch_members , last_switch_time ) )
if not await ctx . confirm_react ( ctx . message . author , msg ) :
raise CommandError ( " Switch deletion cancelled. " )
await last_switch . delete ( ctx . conn )
if next_last_switch :
# lol block scope amirite
# but yeah this is fine
await ctx . reply_ok ( " Switch deleted. Next latest switch is now {} ( {} ago). " . format ( next_last_switch_members , next_last_switch_time ) )
else :
await ctx . reply_ok ( " Switch deleted. You now have no logged switches. " )
2018-09-07 17:34:38 +02:00
async def switch_move ( ctx : CommandContext ) :
system = await ctx . ensure_system ( )
if not ctx . has_next ( ) :
2018-12-05 11:44:10 +01:00
raise CommandError ( " You must pass a time to move the switch to. " )
2018-07-24 22:47:57 +02:00
# Parse the time to move to
2018-09-07 17:34:38 +02:00
new_time = dateparser . parse ( ctx . remaining ( ) , languages = [ " en " ] , settings = {
2018-12-18 20:13:45 +01:00
# Tell it to default to the system's given time zone
# If no time zone was given *explicitly in the string* it'll return as naive
" TIMEZONE " : system . ui_tz
2018-07-24 22:47:57 +02:00
} )
2018-12-19 02:09:47 +01:00
if not new_time :
raise CommandError ( " ' {} ' can ' t be parsed as a valid time. " . format ( ctx . remaining ( ) ) )
2018-12-18 20:13:45 +01:00
tz = pytz . timezone ( system . ui_tz )
# So we default to putting the system's time zone in the tzinfo
if not new_time . tzinfo :
new_time = tz . localize ( new_time )
# Now that we have a system-time datetime, convert this to UTC and make it naive since that's what we deal with
new_time = pytz . utc . normalize ( new_time ) . replace ( tzinfo = None )
2018-07-24 22:47:57 +02:00
# Make sure the time isn't in the future
2018-09-07 17:34:38 +02:00
if new_time > datetime . utcnow ( ) :
2018-12-05 11:44:10 +01:00
raise CommandError ( " Can ' t move switch to a time in the future. " )
2018-07-24 22:47:57 +02:00
# Make sure it all runs in a big transaction for atomicity
async with ctx . conn . transaction ( ) :
# Get the last two switches to make sure the switch to move isn't before the second-last switch
2018-12-18 18:30:10 +01:00
last_two_switches = await system . get_switches ( ctx . conn , 2 )
2018-07-24 22:47:57 +02:00
if len ( last_two_switches ) == 0 :
2018-11-15 21:05:13 +01:00
raise CommandError ( " There are no registered switches for this system. " )
2018-07-24 22:47:57 +02:00
2018-12-18 18:30:10 +01:00
last_switch = last_two_switches [ 0 ]
2018-07-24 22:47:57 +02:00
if len ( last_two_switches ) > 1 :
2018-12-18 18:30:10 +01:00
second_last_switch = last_two_switches [ 1 ]
2018-07-24 22:47:57 +02:00
2018-12-18 18:30:10 +01:00
if new_time < second_last_switch . timestamp :
time_str = display_relative ( second_last_switch . timestamp )
2018-11-15 21:05:13 +01:00
raise CommandError (
2018-12-05 11:44:10 +01:00
" Can ' t move switch to before last switch time ( {} ago), as it would cause conflicts. " . format ( time_str ) )
2018-09-07 17:34:38 +02:00
2018-07-24 22:47:57 +02:00
# Display the confirmation message w/ humanized times
2018-12-18 18:30:10 +01:00
last_fronters = await last_switch . fetch_members ( ctx . conn )
2018-07-24 22:47:57 +02:00
members = " , " . join ( [ member . name for member in last_fronters ] ) or " nobody "
2018-12-18 19:38:53 +01:00
last_absolute = ctx . format_time ( last_switch . timestamp )
2018-12-18 18:30:10 +01:00
last_relative = display_relative ( last_switch . timestamp )
2018-12-18 19:38:53 +01:00
new_absolute = ctx . format_time ( new_time )
2018-12-05 11:44:10 +01:00
new_relative = display_relative ( new_time )
2018-09-08 13:48:18 +02:00
# Confirm with user
2018-12-05 11:44:10 +01:00
switch_confirm_message = await ctx . reply (
2018-12-18 19:38:53 +01:00
" This will move the latest switch ( {} ) from {} ( {} ago) to {} ( {} ago). Is this OK? " . format ( members ,
2018-12-05 11:44:10 +01:00
last_absolute ,
last_relative ,
new_absolute ,
new_relative ) )
2018-09-08 13:48:18 +02:00
if not await ctx . confirm_react ( ctx . message . author , switch_confirm_message ) :
2018-11-15 21:05:13 +01:00
raise CommandError ( " Switch move cancelled. " )
2018-07-24 22:47:57 +02:00
2018-12-18 18:30:10 +01:00
# Actually move the switch
await last_switch . move ( ctx . conn , new_time )
2018-11-15 21:05:13 +01:00
await ctx . reply_ok ( " Switch moved. " )