major-domo-legacy/cogs/draft.py
2025-06-07 23:53:32 -05:00

992 lines
45 KiB
Python

import copy
import math
import re
from typing import Optional
from api_calls.current import get_current
from api_calls.draft_data import DraftData, get_draft_data
from api_calls.draft_pick import DraftPick, get_one_draftpick, patch_draftpick
from api_calls.player import Player
from exceptions import ApiException, log_exception
from helpers import *
from db_calls import db_get, db_patch, put_player, db_post, get_player_by_name
from discord.ext import commands, tasks
from discord import TextChannel, app_commands
logger = logging.getLogger('discord_app')
class Draft(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.warnings = 0
self.pick_lock = True
self.guild: Optional[discord.Guild] = None
self.fa_team_id = 498
# self.sheets = pygsheets.authorize(service_file='storage/major-domo-service-creds.json')
self.draft_loop.start() # type: ignore
async def cog_command_error(self, ctx, error):
logger.error(msg=error, stack_info=True, exc_info=True)
await ctx.send(f'{error}\n\nRun !help <command_name> to see the command requirements')
async def slash_error(self, ctx, error):
logger.error(msg=error, stack_info=True, exc_info=True)
await ctx.send(f'{error[:1600]}')
@tasks.loop(seconds=10)
async def draft_loop(self):
logger.info(f'JUST ENTERED THE DRAFTD LOOP')
guild_id = os.environ.get('GUILD_ID')
if guild_id is None:
log_exception(AttributeError(f'Guild ID was not found'))
logger.info(f'GUILD ID: {guild_id}')
guild = self.bot.get_guild(int(guild_id))
if guild is None:
log_exception(LookupError(f'Guild not found with ID {guild_id}'))
self.guild = guild
logger.info(f'GUILD: {guild}')
draft_data = await get_draft_data()
now = datetime.datetime.now()
# logger.info('Entering draft loop')
deadline = draft_data.pick_deadline
# logger.info(f'Timer: {draft_data.timer} / Deadline: {deadline} / Warnings: {self.warnings}')
# logger.info(f'10 Hrs?: {deadline - datetime.timedelta(hours=10) <= now}')
# Timer is active and pick is due
if draft_data.timer and deadline:
current = await get_current()
draft_pick = await get_one_draftpick(season=current.season, overall=draft_data.currentpick)
if not draft_pick.owner:
log_exception(ValueError(f'Pick #{draft_pick.overall} has no owner associated'))
return
logger.info(f'getting team role')
team_role = get_team_role(None, draft_pick.owner.model_dump(), self.bot)
logger.info(f'team role: {team_role}')
if deadline <= now:
logger.info(f'deadline has past')
# Auto-draft
# team_list = await get_draft_list(draft_pick['owner'])
l_query = await db_get('draftlist', params=[
('season', current.season), ('team_id', draft_pick.owner.id)
])
if l_query and l_query.get('count', 0) > 0:
for x in l_query['picks']:
this_pick = await self.draft_player(current, draft_data, draft_pick, x['player'])
if this_pick['success']:
break
await self.advance_pick()
else:
logger.info(f'deadline has not yet past')
# # Slow Draft
# if (deadline - datetime.timedelta(hours=10) <= now) and self.warnings == 0:
# current = await get_current()
# draft_pick = await get_one_draftpick_byoverall(current.season, draft_data.currentpick)
# team_role = get_team_role(None, draft_pick['owner'], self.bot)
# await send_to_channel(
# self.bot,
# draft_data.ping_channel,
# f'{team_role.mention if team_role else draft_pick.owner.lname}\n'
# f'You are two hours in on pick #{draft_pick.overall}.'
# )
# self.warnings = 1
# elif (deadline - datetime.timedelta(hours=6) <= now) and self.warnings == 1:
# current = await get_current()
# draft_pick = await get_one_draftpick_byoverall(current.season, draft_data.currentpick)
# team_role = get_team_role(None, draft_pick['owner'], self.bot)
# await send_to_channel(
# self.bot,
# draft_data.ping_channel,
# f'{team_role.mention if team_role else draft_pick.owner.lname}\n'
# f'You are halfway into the 12-hour timer for pick #{draft_pick.overall}.'
# )
# self.warnings = 2
# elif (deadline - datetime.timedelta(hours=2) <= now) and self.warnings == 2:
# current = await get_current()
# draft_pick = await get_one_draftpick_byoverall(current.season, draft_data.currentpick)
# team_role = get_team_role(None, draft_pick['owner'], self.bot)
# await send_to_channel(
# self.bot,
# draft_data.ping_channel,
# f'{team_role.mention if team_role else draft_pick.owner.lname}\n'
# f'You have two hours remaining to make pick #{draft_pick.overall}.'
# )
# self.warnings = 3
# elif (deadline - datetime.timedelta(hours=1) <= now) and self.warnings == 3:
# current = await get_current()
# draft_pick = await get_one_draftpick_byoverall(current.season, draft_data.currentpick)
# team_role = get_team_role(None, draft_pick['owner'], self.bot)
# await send_to_channel(
# self.bot,
# draft_data.ping_channel,
# f'{team_role.mention if team_role else draft_pick.owner.lname}\n'
# f'You have one hour remaining to make pick #{draft_pick.overall}.'
# )
# self.warnings = 4
# elif (deadline - datetime.timedelta(minutes=5) <= now) and self.warnings == 4:
# current = await get_current()
# draft_pick = await get_one_draftpick_byoverall(current.season, draft_data.currentpick)
# team_role = get_team_role(None, draft_pick['owner'], self.bot)
# await send_to_channel(
# self.bot,
# draft_data.ping_channel,
# f'{team_role.mention if team_role else draft_pick.owner.lname}\n'
# f'You have five minutes remaining to make pick #{draft_pick.overall}. Is Neil Walker available?'
# )
# self.warnings = 5
# 2-minute draft
logger.info(f'pulling p_channel')
p_channel = discord.utils.get(self.guild.text_channels, id=int(draft_data.ping_channel)) # type: ignore
if not p_channel:
log_exception(LookupError(f'Ping channel with ID {draft_data.ping_channel} not found'))
logger.info(f'p_channel: {p_channel}')
if (deadline - datetime.timedelta(seconds=59) <= now) and self.warnings < 1:
await p_channel.send(
content=f'{team_role.mention if team_role else draft_pick.owner.lname}\nLess than a minute remaining to make pick #{draft_pick.overall}!'
)
self.warnings += 1
elif (deadline - datetime.timedelta(seconds=29) <= now) and self.warnings < 2:
await p_channel.send(
content=f'{team_role.mention if team_role else draft_pick.owner.lname}\nLess than 30 seconds remaining to make pick #{draft_pick.overall}!'
)
self.warnings += 1
@draft_loop.before_loop
async def before_weekly_check(self):
await self.bot.wait_until_ready()
async def update_timer(self, draft_data):
deadline = datetime.datetime.now() + datetime.timedelta(minutes=draft_data.pick_minutes)
# await patch_draftdata(pick_deadline=deadline)
await db_patch('draftdata', object_id=draft_data.id, params=[('pick_deadline', deadline)])
async def send_draft_ping(self, ping=True):
current = await get_current()
draft_data = await get_draft_data()
logger.info(f'current: {current}\nd_data: {draft_data}')
this_pick = await get_one_draftpick(season=current.season, overall=draft_data.currentpick)
if not this_pick.owner:
log_exception(ValueError(f'Pick #{this_pick.overall} has no owner associated'))
return
logger.info(f'pick: {this_pick}')
this_team = await db_get('teams', object_id=this_pick.owner.id)
logger.info(f'team: {this_team}')
if not this_team:
raise ApiException(f'Team for {this_pick.owner.abbrev} not found')
team_role = get_team_role(None, this_team, self.bot)
logger.info(f'role: {team_role}')
team_ping = None
if ping and team_role:
team_ping = team_role.mention
pick_num = this_pick.overall % 16
if pick_num == 0:
pick_num = 16
logger.info(f'current: {current}\ndata: {draft_data}\npick: {this_pick}\nteam: {this_team}\n'
f'role: {team_role}\nping: {team_ping}')
embed = get_team_embed(f'{this_team["lname"]} On The Clock', team=this_team)
embed.description = f'Round {this_pick.round} / Pick {pick_num} / Overall {this_pick.overall}'
# players = await get_players(current.season, team_abbrev=this_team['abbrev'], sort='wara-desc')
p_query = await db_get('players', params=[
('season', current.season), ('team_id', this_team['id']), ('sort', 'cost-desc')
])
if p_query and p_query.get('count') > 0:
count = 0
core_players_string = ''
for x in p_query['players']:
if count < 5:
core_players_string += f'{x["pos_1"]} {x["name"]} ({x["wara"]})\n'
else:
break
count += 1
embed.add_field(name='Core Players', value=core_players_string)
# roster = await get_team_roster(this_team, 'current')
r_query = await db_get(f'teams/{this_team["id"]}/roster/current')
if r_query and r_query.get('active'):
embed.add_field(name=f'{this_team["sname"]} sWAR', value=f'{r_query["active"]["WARa"]:.2f}')
# embed.add_field(
# name=f'{this_team["abbrev"]} Roster Page',
# value=f'https://sombaseball.ddns.net/teams?abbrev={this_team["abbrev"]}',
# inline=False
# )
embed.add_field(
name=f'Draft Sheet',
value=f'https://docs.google.com/spreadsheets/d/{DRAFT_KEY.get(current.season, f'help-i-dont-know-the-sheet-id-for-season-{current.season}')}',
inline=False
)
# Last 5 Loop
# all_picks = await get_draftpicks(
# current.season, overall_start=this_pick['overall'] - 15, overall_end=this_pick['overall'] - 1
# )
# last_five = dict(sorted(all_picks.items(), key=lambda item: item[1]["overall"], reverse=True))
l_query = await db_get('draftpicks', params=[
('season', current.season), ('sort', 'order-desc'), ('limit', 5), ('short_output', False),
('overall_end', this_pick.overall - 1), ('player_taken', True)
])
if not l_query or not l_query.get('picks'):
all_picks = []
else:
all_picks = l_query['picks']
last_string = ''
count = 0
for x in all_picks:
if x['player'] is not None:
last_string += f'Pick #{x["overall"]}: {x["player"]["name"]}\n'
count += 1
if count >= 5:
break
if len(last_string) == 0:
last_string = 'None, yet'
embed.add_field(name='Last 5', value=last_string)
# Next 5 Loop
# all_picks = await get_draftpicks(
# current.season, overall_start=this_pick['overall'] + 1, overall_end=this_pick['overall'] + 15
# )
# next_five = dict(sorted(all_picks.items(), key=lambda item: item[1]["overall"]))
n_query = await db_get('draftpicks', params=[
('season', current.season), ('sort', 'order-asc'), ('limit', 5), ('short_output', False),
('overall_start', this_pick.overall + 1)
])
if not n_query or not n_query.get('picks'):
all_picks = []
else:
all_picks = n_query['picks']
next_string = ''
for x in all_picks:
next_string += f'Pick #{x["overall"]}: {x["owner"]["sname"]}\n'
if len(next_string) == 0:
next_string = 'None, yet'
embed.add_field(name='Next 5', value=next_string)
# Pick Deadline
if draft_data.pick_deadline:
deadline = draft_data.pick_deadline
# deadline = deadline - datetime.timedelta(hours=6)
dead_string = deadline.strftime("%b %d @ %H:%M Central")
# dead_string = f'<t:{deadline.timestamp()}>'
else:
dead_string = 'None'
embed.add_field(name='Pick Deadline', value=dead_string, inline=False)
p_channel = discord.utils.get(self.guild.text_channels, id=int(draft_data.ping_channel)) # type: ignore
if not p_channel:
log_exception(LookupError(f'Ping channel with ID {draft_data.ping_channel} not found'))
await p_channel.send(content=team_ping, embed=embed)
# await send_to_channel(self.bot, draft_data.ping_channel, content=team_ping, embed=embed)
async def advance_pick(self):
self.pick_lock = True
current = await get_current()
draft_data = await get_draft_data()
starting_round = math.ceil(draft_data.currentpick / 16)
draft_pick = await get_one_draftpick(season=current.season, overall=draft_data.currentpick)
if not draft_pick.owner:
log_exception(ValueError(f'Pick #{draft_pick.overall} has no owner associated'))
return
p_channel = discord.utils.get(self.guild.text_channels, id=int(draft_data.ping_channel)) # type: ignore
if not p_channel:
log_exception(LookupError(f'Ping channel with ID {draft_data.ping_channel} not found'))
r_channel = discord.utils.get(self.guild.text_channels, id=int(draft_data.result_channel)) # type: ignore
if not r_channel:
log_exception(LookupError(f'Ping channel with ID {draft_data.ping_channel} not found'))
if draft_pick.player is None:
await p_channel.send(
content=f'It\'s time to to ***SKIP***. Can I get an F in chat?'
)
self.warnings = 0
# await patch_draftdata(currentpick=draft_data.currentpick + 1)
d_query = await db_patch('draftdata', object_id=draft_data.id, params=[
('currentpick', draft_data.currentpick + 1)
])
draft_data = DraftData(**d_query)
# Advance the current pick until a selection is possible
while True:
# draft_data = await get_draft_data()
# draft_pick = await get_one_draftpick_byoverall(current.season, draft_data.currentpick)
if draft_data.currentpick > 512:
await p_channel.send(
content='Looks like that is the end of the draft!'
)
await db_patch('draftdata', object_id=draft_data.id, params=[('timer', False)])
self.pick_lock = False
return
draft_pick = await get_one_draftpick(season=current.season, overall=draft_data.currentpick)
if not draft_pick.owner:
log_exception(ValueError(f'Pick #{draft_pick.overall} has no owner associated'))
return
# Check that selection has been made for current pick, advance if so
if draft_pick.player is not None:
# await patch_draftdata(currentpick=draft_data.currentpick + 1)
draft_data = await db_patch('draftdata', object_id=draft_data.id, params=[
('currentpick', draft_data.currentpick + 1)
])
else:
round_num = math.ceil(draft_data.currentpick / 16)
if round_num > starting_round:
await r_channel.send(
content=f'# Round {round_num}'
)
break
# If timer is true, set new deadline
# draft_data = await get_draft_data() # No longer needed
if draft_data.timer:
await self.update_timer(draft_data)
else:
deadline = datetime.datetime.now() + datetime.timedelta(days=690)
# await patch_draftdata(pick_deadline=deadline)
await db_patch('draftdata', object_id=draft_data.id, params=[('pick_deadline', deadline)])
# Post splash screen to ping_channel
await self.send_draft_ping()
self.pick_lock = False
async def send_pick_to_sheets(self, draft_pick: DraftPick, player, pick_num):
if not draft_pick.origowner or not draft_pick.owner:
log_exception(AttributeError(f'Draft Pick ID #{draft_pick.id} is missing ownership data'))
sheets = pygsheets.authorize(service_file='storage/major-domo-service-creds.json')
# this_pick = [[
# draft_pick['round'], pick_num, draft_pick['owner']['abbrev'], draft_pick['owner']['sname'],
# player['name'], player['wara'], draft_pick['overall']
# ]]
this_pick = [[
draft_pick.origowner.abbrev, draft_pick.owner.abbrev, player['name'],
player['wara']
]]
sheets_key = DRAFT_KEY.get(draft_pick.season)
if sheets_key:
logger.info(f'sending pick to sheets')
sheets.open_by_key(sheets_key).worksheet_by_title('Ordered List').update_values(
crange=f'D{draft_pick.overall + 1}',
values=this_pick
)
logger.info(f'pick has been sent to sheets')
else:
logger.error(f'Cannot find draft key for season {draft_pick.season}')
async def draft_player(self, current, draft_data: DraftData, draft_pick: DraftPick, player):
# Is this player available to be drafted?
if draft_pick.owner is None:
log_exception(ValueError(f'Draft pick S{draft_pick.season} / overall {draft_pick.overall} has no owner'))
return
if player['team']['id'] == draft_pick.owner.id:
return {'success': False, 'error': f'{player["name"]} is already on your team, dingus.'}
elif player['team']['abbrev'] != 'FA':
return {
'success': False,
'error': f'Hey, uh, {player["team"]["sname"]}, you think {draft_pick.owner.abbrev} can have '
f'{player["name"]} for free? No? Sorry, I tried.'
}
# team_roster = await get_team_roster(draft_pick['owner'], 'current')
team_roster = await db_get(f'teams/{draft_pick.owner.id}/roster/current')
if not team_roster or not team_roster.get('active'):
raise ApiException(f'Team roster not found for {draft_pick.owner.abbrev}')
# Does this team have the cap space to afford this player?
max_zeroes = 32 - len(team_roster['active']['players'])
max_counted = 26 - max_zeroes
total_swar = 0
count = 0
for x in team_roster['active']['players']:
count += 1
if count > max_counted:
break
total_swar += x['wara']
if total_swar > 32.00001:
return {
'success': False,
'error': f'Drafting {player["name"]} would put you at {total_swar:.2f} '
f'sWAR, friendo.'
}
logger.info(
f'{draft_pick.owner.lname} selects {player["name"]} with the #{draft_pick.overall} overall pick'
)
draft_pick.player = Player(**player)
await patch_draftpick(draft_pick) # TODO: uncomment for live draft
player['team']['id'] = draft_pick.owner.id
player['demotion_week'] = 1
# await patch_player(player['id'], team_id=draft_pick['owner']['id'], demotion_week=2)
await put_player(player) # TODO: uncomment for live draft
# await post_transactions([{
# 'week': -1,
# 'player_id': player['id'],
# 'oldteam_id': 201, # FA team ID
# 'newteam_id': draft_pick['owner']['id'],
# 'season': current.season,
# 'moveid': f'draft-overall-{draft_pick.overall}'
# }])
await db_post('transactions', payload={'count': 1, 'moves': [{
'week': 0,
'player_id': player['id'],
'oldteam_id': self.fa_team_id,
'newteam_id': draft_pick.owner.id,
'season': current.season,
'moveid': f'draft-overall-{draft_pick.overall}'
}]})
p_channel = discord.utils.get(self.guild.text_channels, id=int(draft_data.ping_channel)) # type: ignore
if not p_channel:
log_exception(LookupError(f'Ping channel with ID {draft_data.ping_channel} not found'))
await p_channel.send(
content=None,
embed=await get_player_embed(player, current)
)
if player['image2']:
embed = get_team_embed(f'{player["name"]}', player["team"], thumbnail=False)
embed.set_image(url=player['image2'])
await p_channel.send(
content=None,
embed=embed
)
pick_num = draft_pick.overall % 16
if pick_num == 0:
pick_num = 16
await self.send_pick_to_sheets(draft_pick, player, draft_pick.overall)
await p_channel.send(
content=f'Round {draft_pick.round} / Pick {pick_num}: {draft_pick.owner.abbrev} selects **{player["name"]}**'
)
return {'success': True}
async def draftdata_to_string(self, current, draft_data: DraftData):
draft_pick = await get_one_draftpick(season=current.season, overall=draft_data.currentpick)
if not draft_pick.owner:
log_exception(ValueError(f'Pick #{draft_pick.overall} has no owner associated'))
return
if draft_data.pick_deadline:
deadline = draft_data.pick_deadline
deadline = deadline - datetime.timedelta(hours=6)
dead_string = deadline.strftime("%b %d @ %H:%M Central")
else:
dead_string = 'None'
p_channel = discord.utils.get(self.guild.text_channels, id=int(draft_data.ping_channel))
if not p_channel:
log_exception(LookupError(f'Ping channel with ID {draft_data.ping_channel} not found'))
r_channel = discord.utils.get(self.guild.text_channels, id=int(draft_data.result_channel))
if not r_channel:
log_exception(LookupError(f'Result channel with ID {draft_data.result_channel} not found'))
draft_string = f'Current Pick: {draft_data.currentpick}\n' \
f'Pick Owner: {draft_pick.owner.sname if draft_pick else "N/A"}\n' \
f'Timer: {"Active" if draft_data.timer else "Inactive"} ' \
f'({draft_data.pick_minutes} min total)\n' \
f'Pick Deadline: {dead_string}\n' \
f'Ping Channel: {p_channel.mention}\n' \
f'Result Channel: {r_channel.mention}\n' \
f'Pick Lock: {self.pick_lock}'
logger.info(f'draft_string: {draft_string}')
return draft_string
@commands.command(name='select', aliases=['pick', 'draft', 'gib', 'gimme', 'deliveruntomewhatismine', 'itsmyplayerandineedhimnow'], help='Draft a player')
@commands.cooldown(1, 5, commands.BucketType.user)
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME)
async def draft_command(self, ctx, *, name):
cal_can_pick_for_all = True
if self.pick_lock:
await react_and_reply(ctx, '', 'Picks are locked right now')
return
else:
self.pick_lock = True
current = await get_current()
team = await get_team_by_owner(current.season, ctx.author.id)
if not team:
# if ctx.author.id == 403294362550796299:
# team = await get_team_by_abbrev('HAM', current.season)
# else:
await ctx.message.add_reaction('')
await ctx.send('I don\'t know youuuuuuuuu')
self.pick_lock = False
return
draft_data = await get_draft_data()
if draft_data is None:
logger.error(f'Cannot find draft_data')
await send_to_channel(self.bot, 'commissioners-office', f'Failed to GET draftdata')
self.pick_lock = False
raise LookupError('Cannot find draftdata, send help')
draft_pick = await get_one_draftpick(season=current.season, overall=draft_data.currentpick)
if not draft_pick.owner:
self.pick_lock = False
log_exception(ValueError(f'Pick #{draft_pick.overall} has no owner associated'))
alt_pick_flag = False
# Does the current pick belong to this team?
if draft_pick.owner.id != team['id']:
alt_pick_flag = True
# Does this team have any skipped picks?
# raw_picks = await get_draftpicks(
# current.season, owner_team=team, round_end=math.ceil(draft_pick['overall'] / 16), round_start=1
# )
# TODO: add get_draftpicks to api_calls.draft_picks
p_query = await db_get('draftpicks', params=[
('season', current.season), ('owner_team_id', team['id']), ('round_start', 1),
('round_end', math.ceil(draft_pick.overall / 16)), ('sort', 'overall-asc'), ('short_output', False)
])
if p_query['count'] == 0:
raise LookupError(f'Draft picks for {team["abbrev"]} not found')
new_pick = None
for x in p_query['picks']:
if x["player"] is None and x['overall'] < draft_pick.overall:
# new_pick = await get_one_draftpick_byoverall(current.season, team_picks[x]['overall'])
# p_query = await db_get('draftpicks', params=[
# ('season', current.season), ('overall', x['overall']), ('short_output', False)
# ])
# if p_query['count'] == 0:
# raise ValueError(f'No pick found for overall #{x["overall"]}')
new_pick = x
break
if new_pick is not None:
draft_pick = new_pick
else:
# mil_team = await get_team_by_abbrev(f'{team["abbrev"]}MiL', current.season)
# if mil_team == draft_pick['owner']:
# team = mil_team
if ctx.author.id == self.bot.owner_id and cal_can_pick_for_all:
alt_pick_flag = False
else:
await ctx.message.add_reaction('')
await ctx.send(f'You are not on the clock {ctx.author.mention}. **{draft_pick.owner.abbrev}** is up right now.')
self.pick_lock = False
return
player_cog = self.bot.get_cog('Players')
try:
player_name = await fuzzy_player_search(ctx, ctx.channel, self.bot, name, player_cog.player_list.keys())
except ValueError as e:
logger.error(e)
await ctx.send(f'{name} not found')
self.pick_lock = False
return
player = await get_player_by_name(current.season, player_name)
if player is None:
e_msg = f'Could not find {player_name} after matching the name'
logger.error(e_msg)
await ctx.send(e_msg)
self.pick_lock = False
return
the_pick = await self.draft_player(current, draft_data, draft_pick, player)
if the_pick is None:
log_exception(ValueError('Did not receive confirmation for this pick.'))
self.pick_lock = False
return
self.pick_lock = False
if the_pick['success']:
await ctx.message.add_reaction('')
if not alt_pick_flag:
await self.advance_pick()
else:
await react_and_reply(ctx, '', the_pick['error'])
@commands.command(name='list', aliases=['draftlist', 'mylist'], help='Set your draft list')
# @commands.has_any_role(SBA_PLAYERS_ROLE_NAME)
async def draft_list_command(self, ctx, *player_list):
current = await get_current()
team = await get_team_by_owner(current.season, ctx.author.id)
if not team:
await react_and_reply(ctx, '', 'I don\'t know youuuuuuuuu')
return
if not player_list:
# team_list = await get_draft_list(team)
l_query = await db_get('draftlist', params=[
('season', current.season), ('team_id', team['id'])
])
if not l_query:
raise ApiException('There was a database error trying to pull your list')
if l_query['count'] > 0:
list_string = 'Current list:\n'
for x in l_query['picks']:
list_string += f'{x["rank"]}. {x["player"]["name"]}\n'
await ctx.send(list_string)
else:
await ctx.send('I do not have a list for you.')
return
else:
player_list = ' '.join(player_list)
player_names = re.split(',', player_list)
draft_list = []
spelling = []
errors = []
rank = 0
for x in player_names:
rank += 1
player_cog = self.bot.get_cog('Players')
try:
player_name = await fuzzy_player_search(ctx, ctx.channel, self.bot, x, player_cog.player_list.keys())
player = await get_player_by_name(current.season, player_name)
except Exception as e:
logger.error(f'Could not draft {x} for {team["abbrev"]}: {e}')
errors.append(x)
rank -= 1
else:
if not player:
log_exception(LookupError(f'{player_name} not found'))
if player.get('team', {'abbrev': None}).get('abbrev') != 'FA':
errors.append(player['name'])
rank -= 1
else:
draft_list.append({
'season': current.season,
'team_id': team['id'],
'rank': rank,
'player_id': player['id']
})
if len(errors) > 0:
error_string = 'The following players have already been drafted: '
error_string += ', '.join(errors)
await react_and_reply(ctx, '😬', error_string)
if len(spelling) > 0:
error_string = 'Who the fuck is this: '
error_string += ', '.join(spelling)
await react_and_reply(ctx, '', error_string)
# await post_draft_list(draft_list)
await db_post('draftlist', payload={'count': len(draft_list), 'draft_list': draft_list})
await ctx.message.add_reaction('')
@commands.command(name='restart-loop', help='Mod: Restart the draft tick loop')
async def restart_loop_command(self, ctx):
if ctx.author.id not in [258104532423147520, 139926308644847616]:
await ctx.send(random_gif('you\'re not my dad'))
return
self.draft_loop.start()
await ctx.send(random_gif('we are so back'))
@commands.command(name='whomst', aliases=['draftstatus'], help='Get draft status')
async def draft_status_command(self, ctx):
current = await get_current()
draft_data = await get_draft_data()
logger.info(f'building draft string')
async with ctx.typing():
await ctx.send(await self.draftdata_to_string(current, draft_data))
async with ctx.typing():
if draft_data.ping_channel is not None:
await self.send_draft_ping(ping=False)
@app_commands.command(name='draft-mod', description='Mod commands for draft administration')
@app_commands.describe(
result_channel='Text Channel: where Major Domo posts draft results',
ping_channel='Text Channel: where Major Domo pings for draft picks',
current_overall='Integer: override the current pick number',
timer_master='Integer: number of minutes for all draft picks',
timer_this_pick='Integer: number of minutes for the current draft pick',
timer_active='Boolean: enable/disable the pick timer',
wipe_pick='Integer: overall pick number to delete; cannot be combined with other parameters',
pick_lock='Boolean: enable/disable the !select command'
)
async def draftmod_slash_command(
self, interaction: discord.Interaction, result_channel: Optional[TextChannel] = None,
ping_channel: Optional[TextChannel] = None, current_overall: Optional[int] = None, timer_master: Optional[int] = None, timer_this_pick: Optional[int] = None, timer_active: Optional[bool] = None, wipe_pick: Optional[int] = None, pick_lock: Optional[bool] = None):
if interaction.user.id not in [258104532423147520, 139926308644847616]:
await interaction.response.send_message(random_gif('you\'re not my dad'))
return
await interaction.response.defer()
current = await get_current()
draft_data = await get_draft_data()
pick_deadline = None
res_channel_id = None
ping_channel_id = None
if wipe_pick is not None:
this_pick = await get_one_draftpick(season=current.season, overall=wipe_pick)
# if not this_pick.owner:
# log_exception(ValueError(f'Pick #{this_pick.overall} has no owner associated'))
# return
if not this_pick.player:
await interaction.edit_original_response(content=f'I don\'t see a player taken {wipe_pick} overall.')
else:
fa_team = await get_team_by_abbrev('FA', current.season)
temp_player = this_pick.player.model_dump()
this_pick.player = None
await patch_draftpick(this_pick)
temp_player['team'] = fa_team
this_player = await put_player(temp_player)
# this_player = await get_player_by_name(current.season, this_pick['player']['id'])
# this_player = await db_get('players', object_id=this_pick['player']['id'])
await interaction.edit_original_response(
content='Don\'t forget to delete the transaction from the database.',
embed=await get_player_embed(this_player, current)
)
return
if pick_lock is not None:
if pick_lock:
self.pick_lock = True
else:
self.pick_lock = False
params = []
if timer_this_pick is not None:
params.append(('pick_deadline', datetime.datetime.now() + datetime.timedelta(minutes=timer_this_pick)))
if timer_active is not None:
params.append(('timer', timer_active))
if timer_active is True:
if timer_this_pick is not None:
timer = timer_this_pick
elif timer_master is not None:
timer = timer_master
params.append(('pick_minutes', timer_master))
else:
timer = draft_data.pick_minutes
deadline = datetime.datetime.now() + datetime.timedelta(minutes=timer)
else:
deadline = datetime.datetime.now() + datetime.timedelta(weeks=1)
params.append(('pick_deadline', deadline))
if result_channel is not None:
params.append(('result_channel', result_channel.id))
if ping_channel is not None:
params.append(('ping_channel', ping_channel.id))
if current_overall is not None and draft_data.timer and timer_active is None:
params.append(
('pick_deadline', datetime.datetime.now() + datetime.timedelta(minutes=draft_data.pick_minutes))
)
if current_overall is not None:
params.append(('currentpick', current_overall))
if timer_master is not None:
params.append(('pick_minutes', timer_master))
# await patch_draftdata(
# currentpick=current_overall,
# timer=timer_active,
# pick_deadline=pick_deadline,
# result_channel=res_channel_id,
# ping_channel=ping_channel_id,
# pick_minutes=timer_master
# )
await db_patch('draftdata', object_id=draft_data.id, params=params)
draft_data = await get_draft_data()
await interaction.edit_original_response(content=await self.draftdata_to_string(current, draft_data))
if timer_active is True or draft_data.timer:
self.warnings = 0
# draft_pick = await get_one_draftpick_byoverall(current.season, draft_data.currentpick)
p_query = await db_get('draftpicks', params=[
('season', current.season), ('overall', draft_data.currentpick), ('short_output', False)
])
if not p_query:
raise ApiException(f'No pick found for Overall #{draft_data.currentpick}')
if p_query['count'] == 0:
raise ValueError(f'No pick found for overall #{wipe_pick}')
draft_pick = p_query['picks'][0]
if draft_pick['player']:
await self.advance_pick()
else:
await self.send_draft_ping()
@commands.command(name='keepers', help='Set keepers', hidden=True)
async def keepers_command(self, ctx, team_abbrev, *, player_names):
if ctx.author.id not in [258104532423147520, 139926308644847616]:
return
current = await get_current()
player_list = []
this_team = await get_team_by_abbrev(team_abbrev, current.season)
if not this_team:
raise ApiException(f'{team_abbrev} not found')
fa_team = await get_team_by_abbrev('FA', current.season)
if not fa_team:
raise ApiException(f'FA team not found')
player_names = re.split(',', player_names)
keepers = []
keeper_count = 1
for x in player_names:
player_cog = self.bot.get_cog('Players')
player_name = await fuzzy_player_search(ctx, ctx.channel, self.bot, x, player_cog.player_list.keys())
player = await get_player_by_name(current.season, player_name)
if not player:
raise ApiException(f'{player_name} not found')
keepers.append(player["name"])
all_players = []
# active_roster = await get_players(season=current.season, team_abbrev=team_abbrev)
# il_players = await get_players(season=current.season, team_abbrev=f'{team_abbrev}IL')
# mil_players = await get_players(season=current.season, team_abbrev=f'{team_abbrev}MiL')
active_roster = await db_get('players', params=[('season', current.season), ('team_id', this_team['id'])])
if not active_roster:
raise ApiException(f'Active roster not found for {this_team["id"]}')
il_players = await db_get('players', params=[('season', current.season), ('team_id', this_team['id'] + 1)])
if not il_players:
raise ApiException(f'Active roster not found for {this_team["id"]}')
mil_players = await db_get('players', params=[('season', current.season), ('team_id', this_team['id'] + 2)])
if not mil_players:
raise ApiException(f'Active roster not found for {this_team["id"]}')
for guy in active_roster['players']:
all_players.append(guy)
for guy in il_players['players']:
all_players.append(guy)
for guy in mil_players['players']:
all_players.append(guy)
# all_picks = await get_draftpicks(season=current.season, owner_team=this_team, round_end=7)
if current.season % 2 == 1:
start_round = 1
end_round = 7
else:
start_round = 2
end_round = 8
p_query = await db_get(
'draftpicks',
params=[('season', current.season), ('owner_team_id', this_team['id']), ('pick_round_start', start_round), ('pick_round_end', end_round), ('sort', 'order-asc')]
)
if not p_query:
raise ApiException(f'No picks found for {this_team["abbrev"]} in rounds {start_round} to {end_round}')
# logger.info(f'\n{all_picks}\n')
# sorted_picks = sorted(all_picks.items(), key=lambda item: item[1]["overall"])
sorted_picks = p_query['picks']
# for p in all_picks:
# sorted_picks.append(all_picks[p])
# logger.info(f'{sorted_picks}')
all_moves = []
all_keepers = []
for x in all_players:
logger.info(f'Checking {x["name"]} for keeper status')
this_player = copy.deepcopy(x)
if x["name"] in keepers:
logger.info(f'{x["name"]} is a keeper')
draft_pick = sorted_picks[keeper_count - 1]
this_player['demotion_week'] = 1
draft_pick['player'] = this_player
logger.info(f'Setting {x["name"]} as {this_team["abbrev"]}\'s #{keeper_count} keeper with overall '
f'pick #{draft_pick.overall}')
await patch_draftpick(draft_pick)
await put_player(this_player)
# await post_transactions([{
# 'week': -1,
# 'player_id': x['id'],
# 'oldteam_id': 201, # FA team ID
# 'newteam_id': this_team['id'],
# 'season': current.season,
# 'moveid': f'keeper-{this_team["abbrev"]}-{keeper_count}'
# }])
all_moves.append({
'week': -1,
'player_id': x['id'],
'oldteam_id': fa_team['id'], # FA team ID
'newteam_id': this_team['id'],
'season': current.season,
'moveid': f'keeper-{this_team["abbrev"]}-{keeper_count}'
})
all_keepers.append({
'season': current.season,
'team_id': this_team['id'],
'player_id': this_player['id']
})
pick_num = draft_pick['overall'] % 16
if pick_num == 0:
pick_num = 16
try:
await self.send_pick_to_sheets(draft_pick, this_player, draft_pick['overall'])
except Exception as e:
logger.error(f'{e}')
keeper_count += 1
else:
this_player['team'] = {'id': fa_team['id']}
await put_player(this_player)
await db_post('transactions', payload={'count': len(all_moves), 'moves': all_moves})
await db_post('keepers', payload={'count': len(all_keepers), 'keepers': all_keepers})
await ctx.send(f'Posted {len(all_moves)} transactions with {len(all_keepers)} keepers for {this_team["lname"]}')
async def setup(bot):
await bot.add_cog(Draft(bot))