2018-07-24 22:47:57 +02:00
import dateparser
import humanize
2018-09-07 17:34:38 +02:00
from datetime import datetime , timezone
from typing import List
2018-07-24 22:47:57 +02:00
2018-08-02 00:36:50 +02:00
import pluralkit . utils
2018-09-07 17:34:38 +02:00
from pluralkit . bot import help
2018-07-24 22:47:57 +02:00
from pluralkit . bot . commands import *
logger = logging . getLogger ( " pluralkit.commands " )
2018-09-07 17:34:38 +02:00
async def switch_member ( ctx : CommandContext ) :
system = await ctx . ensure_system ( )
if not ctx . has_next ( ) :
return CommandError ( " You must pass at least one member name or ID to register a switch to. " ,
help = help . switch_register )
2018-07-24 22:47:57 +02:00
members : List [ Member ] = [ ]
2018-09-07 17:34:38 +02:00
for member_name in ctx . remaining ( ) . split ( " " ) :
2018-07-24 22:47:57 +02:00
# Find the member
2018-09-07 17:34:38 +02:00
member = await utils . get_member_fuzzy ( ctx . conn , system . id , member_name )
2018-07-24 22:47:57 +02:00
if not member :
2018-09-07 17:34:38 +02:00
return CommandError ( " Couldn ' t find member \" {} \" . " . format ( member_name ) )
2018-07-24 22:47:57 +02:00
members . append ( member )
# Compare requested switch IDs and existing fronter IDs to check for existing switches
# Lists, because order matters, it makes sense to just swap fronters
member_ids = [ member . id for member in members ]
2018-09-07 17:34:38 +02:00
fronter_ids = ( await pluralkit . utils . get_fronter_ids ( ctx . conn , system . id ) ) [ 0 ]
2018-07-24 22:47:57 +02:00
if member_ids == fronter_ids :
if len ( members ) == 1 :
2018-09-07 17:34:38 +02:00
return CommandError ( " {} is already fronting. " . format ( members [ 0 ] . name ) )
return CommandError ( " Members {} are already fronting. " . format ( " , " . join ( [ m . name for m in members ] ) ) )
2018-07-24 22:47:57 +02:00
# Also make sure there aren't any duplicates
if len ( set ( member_ids ) ) != len ( member_ids ) :
2018-09-07 17:34:38 +02:00
return CommandError ( " Duplicate members in member list. " )
2018-07-24 22:47:57 +02:00
# Log the switch
async with ctx . conn . transaction ( ) :
2018-09-07 17:34:38 +02:00
switch_id = await db . add_switch ( ctx . conn , system_id = system . id )
2018-07-24 22:47:57 +02:00
for member in members :
await db . add_switch_member ( ctx . conn , switch_id = switch_id , member_id = member . id )
if len ( members ) == 1 :
2018-09-07 17:34:38 +02:00
return CommandSuccess ( " Switch registered. Current fronter is now {} . " . format ( members [ 0 ] . name ) )
2018-07-24 22:47:57 +02:00
else :
2018-09-07 17:34:38 +02:00
return CommandSuccess (
" 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
# Get current fronters
2018-09-07 17:34:38 +02:00
fronters , _ = await pluralkit . utils . get_fronter_ids ( ctx . conn , system_id = system . id )
2018-07-24 22:47:57 +02:00
if not fronters :
2018-09-07 17:34:38 +02:00
return 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-09-07 17:34:38 +02:00
await db . add_switch ( ctx . conn , system_id = system . id )
return CommandSuccess ( " Switch-out registered. " )
2018-07-24 22:47:57 +02:00
2018-09-07 17:34:38 +02:00
async def switch_move ( ctx : CommandContext ) :
system = await ctx . ensure_system ( )
if not ctx . has_next ( ) :
return CommandError ( " You must pass a time to move the switch to. " , help = help . switch_move )
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-07-24 22:47:57 +02:00
" TO_TIMEZONE " : " UTC " ,
" RETURN_AS_TIMEZONE_AWARE " : False
} )
if not new_time :
2018-09-07 17:34:38 +02:00
return CommandError ( " ' {} ' can ' t be parsed as a valid time. " . format ( ctx . remaining ( ) ) , help = help . switch_move )
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 ( ) :
return CommandError ( " Can ' t move switch to a time in the future. " , help = help . switch_move )
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-09-07 17:34:38 +02:00
last_two_switches = await pluralkit . utils . get_front_history ( ctx . conn , system . id , count = 2 )
2018-07-24 22:47:57 +02:00
if len ( last_two_switches ) == 0 :
2018-09-07 17:34:38 +02:00
return CommandError ( " There are no registered switches for this system. " )
2018-07-24 22:47:57 +02:00
last_timestamp , last_fronters = last_two_switches [ 0 ]
if len ( last_two_switches ) > 1 :
second_last_timestamp , _ = last_two_switches [ 1 ]
if new_time < second_last_timestamp :
2018-09-07 17:34:38 +02:00
time_str = humanize . naturaltime ( pluralkit . utils . fix_time ( second_last_timestamp ) )
return CommandError (
" Can ' t move switch to before last switch time ( {} ), as it would cause conflicts. " . format ( time_str ) )
2018-07-24 22:47:57 +02:00
# Display the confirmation message w/ humanized times
members = " , " . join ( [ member . name for member in last_fronters ] ) or " nobody "
last_absolute = last_timestamp . isoformat ( sep = " " , timespec = " seconds " )
2018-09-07 17:34:38 +02:00
last_relative = humanize . naturaltime ( pluralkit . utils . fix_time ( last_timestamp ) )
2018-07-24 22:47:57 +02:00
new_absolute = new_time . isoformat ( sep = " " , timespec = " seconds " )
2018-09-07 17:34:38 +02:00
new_relative = humanize . naturaltime ( pluralkit . utils . fix_time ( new_time ) )
2018-09-08 13:48:18 +02:00
# Confirm with user
switch_confirm_message = " This will move the latest switch ( {} ) from {} ( {} ) to {} ( {} ). Is this OK? " . format ( members , last_absolute , last_relative , new_absolute , new_relative )
if not await ctx . confirm_react ( ctx . message . author , switch_confirm_message ) :
2018-09-07 17:34:38 +02:00
return CommandError ( " Switch move cancelled. " )
2018-07-24 22:47:57 +02:00
# DB requires the actual switch ID which our utility method above doesn't return, do this manually
2018-09-07 17:34:38 +02:00
switch_id = ( await db . front_history ( ctx . conn , system . id , count = 1 ) ) [ 0 ] [ " id " ]
2018-07-24 22:47:57 +02:00
# Change the switch in the DB
2018-09-07 17:34:38 +02:00
await db . move_last_switch ( ctx . conn , system . id , switch_id , new_time )
return CommandSuccess ( " Switch moved. " )
2018-09-08 13:48:18 +02:00