Added random_gif() Moved back from exception-handler cog to local error handling Updated !keepers to be season agnostic Added new !sync param to update and clear local guild Added error checking to !player command
2385 lines
101 KiB
Python
2385 lines
101 KiB
Python
import re
|
|
from pandas import DataFrame
|
|
|
|
from helpers import *
|
|
from db_calls import *
|
|
from db_calls_scouting import *
|
|
from db_calls_gameday import *
|
|
|
|
import discord
|
|
from discord.ext import commands
|
|
|
|
logger = logging.getLogger('discord_app')
|
|
|
|
class Gameday(commands.Cog):
|
|
def __init__(self, bot):
|
|
self.bot = bot
|
|
|
|
async def cog_command_error(self, ctx, error):
|
|
await ctx.send(f'{error}')
|
|
|
|
@staticmethod
|
|
def lineup_check(this_game, home=True):
|
|
if home:
|
|
team_id = this_game['home_team_id']
|
|
team_name = 'home team'
|
|
else:
|
|
team_id = this_game['away_team_id']
|
|
team_name = 'away team'
|
|
|
|
full_lineup = get_lineup_members(this_game['id'], active=1, team_id=team_id)
|
|
roster_check = {
|
|
'C': 0,
|
|
'1B': 0,
|
|
'2B': 0,
|
|
'3B': 0,
|
|
'SS': 0,
|
|
'LF': 0,
|
|
'CF': 0,
|
|
'RF': 0,
|
|
'P': 0,
|
|
'DH': 0,
|
|
'batting_count': 0,
|
|
}
|
|
|
|
for x in full_lineup['members']:
|
|
if x['position'] in roster_check.keys():
|
|
roster_check[x["position"]] += 1
|
|
if 0 < x['batting_order'] < 10:
|
|
roster_check['batting_count'] += 1
|
|
|
|
errors = []
|
|
for key in roster_check:
|
|
if roster_check[key] > 1 and key != 'batting_count':
|
|
errors.append(f'{team_name} has {roster_check[key]}x {key}')
|
|
if roster_check[key] < 1 and key != 'DH':
|
|
errors.append(f'{team_name} has {roster_check[key]}x {key}')
|
|
if roster_check['batting_count'] != 9:
|
|
errors.append(f'{team_name} has {roster_check["batting_count"]} players in the batting order')
|
|
|
|
logger.info(f'{"home" if home else "away"} error count: {len(errors)}')
|
|
return {
|
|
'valid': True if len(errors) == 0 else False,
|
|
'errors': errors
|
|
}
|
|
|
|
async def game_state_ascii(self, this_game):
|
|
away_team = await get_one_team(this_game['away_team_id'])
|
|
home_team = await get_one_team(this_game['home_team_id'])
|
|
gs = get_game_state(this_game['id'])['states'][0]
|
|
occupied = '●'
|
|
unoccupied = '○'
|
|
|
|
logger.info(f'checking baserunners')
|
|
first_base = unoccupied if not gs['on_first'] else occupied
|
|
second_base = unoccupied if not gs['on_second'] else occupied
|
|
third_base = unoccupied if not gs['on_third'] else occupied
|
|
logger.info(f'checking inning and score')
|
|
if gs['final']:
|
|
outs = ''
|
|
inning = 'FINAL'
|
|
else:
|
|
outs = f'{gs["outs"]} Out{"s" if gs["outs"] != 1 else ""}'
|
|
half = '▲' if gs['top_half'] else '▼'
|
|
inning = f'{half} {gs["inning"]}'
|
|
|
|
logger.info(f'getting pitcher and batter members')
|
|
pbc_data = self.get_pbc(this_game)
|
|
batter_member = pbc_data['batter']
|
|
pitcher_member = pbc_data['pitcher']
|
|
logger.info(f'getting pitcher and batter players')
|
|
if not batter_member:
|
|
this_batter = {'name': 'BATTER NOT FOUND', 'image': LOGO}
|
|
else:
|
|
this_batter = await get_one_player(batter_member['player_id'])
|
|
if not pitcher_member:
|
|
this_pitcher = {'name': 'PITCHER NOT FOUND', 'image': LOGO}
|
|
else:
|
|
this_pitcher = await get_one_player(pitcher_member['player_id'])
|
|
# pitcher_name = this_pitcher['name'].split(' ', 1)[1]
|
|
# batter_name = this_batter['name'].split(' ', 1)[1]
|
|
|
|
logger.info(f'setting player names')
|
|
# pitcher_name = this_pitcher['name'] if len(this_pitcher['name']) <= 19 else f'{this_pitcher["name"][:16]}...'
|
|
# batter_name = this_batter['name'] if len(this_batter['name']) <= 19 else f'{this_batter["name"][:16]}...'
|
|
logger.info(f'setting player stats')
|
|
era = f'{6.9:.2f}'
|
|
avg = f'{.420:.3f}'
|
|
if avg[0] == '0':
|
|
avg = avg[1:]
|
|
|
|
logger.info(f'generating game string')
|
|
game_string = f'```\n' \
|
|
f'{away_team["abbrev"]: ^4}{gs["away_score"]: ^3} {second_base}' \
|
|
f'{inning: >12}\n' \
|
|
f'{home_team["abbrev"]: ^4}{gs["home_score"]: ^3} {third_base} {first_base}' \
|
|
f'{outs: >10}\n```'
|
|
return {'gs': game_string, 'batter': this_batter, 'pitcher': this_pitcher, 'away_team': away_team,
|
|
'home_team': home_team, 'batter_member': batter_member, 'pitcher_member': pitcher_member}
|
|
|
|
@staticmethod
|
|
def on_base_code(this_state):
|
|
if this_state['on_first'] and this_state['on_second'] and this_state['on_third']:
|
|
obc = 7
|
|
elif this_state['on_second'] and this_state['on_third']:
|
|
obc = 6
|
|
elif this_state['on_first'] and this_state['on_third']:
|
|
obc = 5
|
|
elif this_state['on_first'] and this_state['on_second']:
|
|
obc = 4
|
|
elif this_state['on_third']:
|
|
obc = 3
|
|
elif this_state['on_second']:
|
|
obc = 2
|
|
elif this_state['on_first']:
|
|
obc = 1
|
|
else:
|
|
obc = 0
|
|
|
|
return obc
|
|
|
|
@staticmethod
|
|
def increment_score(this_game, runs: int):
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
if this_state['top_half']:
|
|
new_state = patch_game_state(
|
|
this_state['id'],
|
|
away_score=this_state['away_score'] + runs
|
|
)
|
|
else:
|
|
new_state = patch_game_state(
|
|
this_state['id'],
|
|
home_score=this_state['home_score'] + runs
|
|
)
|
|
|
|
return new_state
|
|
|
|
@staticmethod
|
|
def increment_outs(this_game, outs: int):
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
if this_state['outs'] + outs < 3:
|
|
new_outs = this_state['outs'] + outs
|
|
new_state = patch_game_state(
|
|
this_state['id'],
|
|
outs=new_outs
|
|
)
|
|
elif this_state['top_half']:
|
|
new_state = patch_game_state(
|
|
this_state['id'],
|
|
outs=0,
|
|
top_half=False,
|
|
on_first_id=False,
|
|
on_second_id=False,
|
|
on_third_id=False
|
|
)
|
|
else:
|
|
new_state = patch_game_state(
|
|
this_state['id'],
|
|
outs=0,
|
|
top_half=True,
|
|
inning=this_state['inning'] + 1,
|
|
on_first_id=False,
|
|
on_second_id=False,
|
|
on_third_id=False
|
|
)
|
|
|
|
return new_state
|
|
|
|
def advance_runners(self, this_state, num_bases: int, error: int, force_only=False):
|
|
"""
|
|
Moves runners and gives them a run scored if necessary - does not update game state
|
|
|
|
:param error:
|
|
:param this_state:
|
|
:param num_bases:
|
|
:param force_only:
|
|
:return: int: runs scored on play
|
|
"""
|
|
rbi = 0
|
|
|
|
# logger.info(f'this_state:\n{this_state}\n')
|
|
if this_state['on_third']:
|
|
if not force_only or (force_only and this_state['on_second'] and this_state['on_first']):
|
|
logger.info(f'runner {this_state["on_third"]["id"]} scores')
|
|
on_third = get_one_atbat(
|
|
game_id=this_state['game']['id'], batter_id=this_state['on_third']['player_id']
|
|
)
|
|
patch_atbat(on_third['id'], run=1, error=error)
|
|
patch_game_state(this_state['id'], on_third_id=False)
|
|
rbi += 1
|
|
|
|
# logger.info('pre-on_second')
|
|
if this_state['on_second']:
|
|
if not force_only or (force_only and this_state['on_first']):
|
|
if num_bases > 1:
|
|
logger.info(f'runner {this_state["on_second"]["id"]} scores')
|
|
on_second = get_one_atbat(
|
|
game_id=this_state['game']['id'], batter_id=this_state['on_second']['player_id']
|
|
)
|
|
patch_atbat(on_second['id'], run=1, error=error)
|
|
patch_game_state(this_state['id'], on_second_id=False)
|
|
rbi += 1
|
|
else:
|
|
logger.info(f'runner {this_state["on_second"]["id"]} from second to third')
|
|
patch_game_state(this_state['id'], on_second_id=False, on_third_id=this_state['on_second']['id'])
|
|
|
|
# logger.info('pre-on_first')
|
|
if this_state['on_first']:
|
|
if num_bases > 2:
|
|
logger.info(f'runner {this_state["on_first"]["id"]} scores')
|
|
on_first = get_one_atbat(
|
|
game_id=this_state['game']['id'], batter_id=this_state['on_first']['player_id']
|
|
)
|
|
patch_atbat(on_first['id'], run=1, error=error)
|
|
patch_game_state(this_state['id'], on_first_id=False)
|
|
rbi += 1
|
|
elif num_bases == 2:
|
|
logger.info(f'runner {this_state["on_first"]["id"]} from first to third')
|
|
patch_game_state(this_state['id'], on_first_id=False, on_third_id=this_state['on_first']['id'])
|
|
else:
|
|
logger.info(f'runner {this_state["on_first"]["id"]} from first to second')
|
|
patch_game_state(this_state['id'], on_first_id=False, on_second_id=this_state['on_first']['id'])
|
|
|
|
return rbi
|
|
|
|
@staticmethod
|
|
def get_pbc(this_game):
|
|
"""
|
|
Return a dict including the pitcher, batter, and catcher
|
|
|
|
:param this_game:
|
|
:return:
|
|
"""
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
# Helpers for finding pitcher and batter
|
|
if this_state['top_half']:
|
|
bat_team_id = this_game['away_team_id']
|
|
pit_team_id = this_game['home_team_id']
|
|
batting_order = this_state['away_batter_up']
|
|
else:
|
|
bat_team_id = this_game['home_team_id']
|
|
pit_team_id = this_game['away_team_id']
|
|
batting_order = this_state['home_batter_up']
|
|
|
|
# Get pitcher and batter
|
|
logger.info('get pitcher and batter')
|
|
try:
|
|
this_batter = get_one_member(
|
|
game_id=this_game['id'], team_id=bat_team_id, batting_order=batting_order, active=1
|
|
)
|
|
except:
|
|
this_batter = None
|
|
|
|
try:
|
|
this_pitcher = get_one_member(
|
|
game_id=this_game['id'], team_id=pit_team_id, position='P', active=1
|
|
)
|
|
except:
|
|
this_pitcher = None
|
|
|
|
try:
|
|
this_catcher = get_one_member(
|
|
game_id=this_game['id'], team_id=pit_team_id, position='C', active=1
|
|
)
|
|
except:
|
|
this_catcher = None
|
|
|
|
return {'batter': this_batter, 'pitcher': this_pitcher, 'catcher': this_catcher}
|
|
|
|
@staticmethod
|
|
def get_defender(this_game, pos):
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
if this_state['top_half']:
|
|
this_member = get_one_member(
|
|
game_id=this_game['id'],
|
|
team_id=this_game['home_team_id'],
|
|
position=pos.upper(),
|
|
active=1
|
|
)
|
|
else:
|
|
this_member = get_one_member(
|
|
game_id=this_game['id'],
|
|
team_id=this_game['away_team_id'],
|
|
position=pos.upper(),
|
|
active=1
|
|
)
|
|
|
|
return this_member
|
|
|
|
@staticmethod
|
|
async def get_batter_statline(data):
|
|
batter_string = ''
|
|
bs = get_atbat_totals(data['batter_member']['id'])
|
|
if not bs['empty']:
|
|
batter_string += f' - {bs["hit"] if bs["hit"] else 0}-{bs["ab"] if bs["ab"] else 0}'
|
|
if bs["homerun"]:
|
|
batter_string += f', '
|
|
if bs["homerun"] > 1:
|
|
batter_string += f'{bs["homerun"]} '
|
|
batter_string += 'HR'
|
|
if bs["triple"]:
|
|
batter_string += f', '
|
|
if bs["triple"] > 1:
|
|
batter_string += f'{bs["triple"]} '
|
|
batter_string += '3B'
|
|
if bs["double"]:
|
|
batter_string += f', '
|
|
if bs["double"] > 1:
|
|
batter_string += f'{bs["double"]} '
|
|
batter_string += '2B'
|
|
if bs["bb"]:
|
|
batter_string += f', '
|
|
if bs["bb"] > 1:
|
|
batter_string += f'{bs["bb"]} '
|
|
batter_string += 'BB'
|
|
if bs["hbp"]:
|
|
batter_string += f', '
|
|
if bs["hbp"] > 1:
|
|
batter_string += f'{bs["hbp"]} '
|
|
batter_string += 'HBP'
|
|
if bs["sac"]:
|
|
batter_string += f', '
|
|
if bs["sac"] > 1:
|
|
batter_string += f'{bs["sac"]} '
|
|
batter_string += 'SAC'
|
|
if bs["so"]:
|
|
batter_string += f', '
|
|
if bs["so"] > 1:
|
|
batter_string += f'{bs["so"]} '
|
|
batter_string += 'K'
|
|
else:
|
|
b = await get_one_battingseason(data['batter_member']['player_id'])
|
|
batter_string = '1st PA'
|
|
if len(b) > 0:
|
|
if b['ab'] > 0:
|
|
singles = b['hit'] - b['hr'] - b['triple'] - b['double']
|
|
avg = b['hit'] / b['ab']
|
|
obp = (b['hit'] + b['bb'] + b['ibb'] + b['hbp']) / b['pa']
|
|
slg = ((b['hr'] * 4) + (b['triple'] * 3) + (b['double'] * 2) + singles) / b['ab']
|
|
ops = obp + slg
|
|
woba = ((b['bb'] * .69) + (b['hbp'] * .72) + (singles * .89) + (b['double'] * 1.27) +
|
|
(b['triple'] * 1.62) + (b['hr'] * 2.1)) / (b['pa'] - b['hbp'] - b['sac'])
|
|
ab = f'{b["ab"]:.0f}'
|
|
run = f'{b["run"]:.0f}'
|
|
hit = f'{b["hit"]:.0f}'
|
|
double = f'{b["double"]:.0f}'
|
|
top_stat = {'name': '2B', 'value': double, 'int': b['double']}
|
|
|
|
triple = f'{b["triple"]:.0f}'
|
|
if b["triple"] > top_stat['int']:
|
|
top_stat['name'] = '3B'
|
|
top_stat['value'] = triple
|
|
top_stat['int'] = b['triple']
|
|
|
|
hr = f'{b["hr"]:.0f}'
|
|
if b["hr"] > top_stat['int']:
|
|
top_stat['name'] = 'HR'
|
|
top_stat['value'] = hr
|
|
top_stat['int'] = b['hr']
|
|
|
|
rbi = f'{b["rbi"]:.0f}'
|
|
sb = f'{b["sb"]:.0f}'
|
|
if b["sb"] > top_stat['int']:
|
|
top_stat['name'] = 'SB'
|
|
top_stat['value'] = sb
|
|
top_stat['int'] = b['sb']
|
|
|
|
so = f'{b["so"]:.0f}'
|
|
|
|
batter_string = f' - {avg:.3f} / {obp:.3f} / {slg:.3f}, {top_stat["value"]} {top_stat["name"]}'
|
|
|
|
# batting_string = f'```\n' \
|
|
# f' AVG OBP SLG OPS\n' \
|
|
# f' {avg:.3f} {obp:.3f} {slg:.3f} {ops:.3f}\n``````\n' \
|
|
# f' AB R H 2B 3B HR RBI SB SO\n' \
|
|
# f'{ab: >3} {run: >2} {hit: ^3} {double: >2} {triple: >2} {hr: >2} {rbi: >3} ' \
|
|
# f'{sb: >2} {so: ^3}\n```'
|
|
|
|
logger.info(f'batter_string: {batter_string}')
|
|
return batter_string
|
|
|
|
@staticmethod
|
|
async def get_pitcher_statline(data):
|
|
pitcher_string = ''
|
|
bs = get_atbat_pitching_totals(data['pitcher_member']['id'])
|
|
if not bs['empty']:
|
|
pitcher_string += f'{bs["ip"]:.1f} IP'
|
|
if bs["run"]:
|
|
pitcher_string += f', {bs["run"]} R'
|
|
if bs["run"] != bs["erun"]:
|
|
pitcher_string += f' ({bs["erun"]} ER)'
|
|
if bs["so"]:
|
|
pitcher_string += f', {bs["so"]} K'
|
|
if bs["hit"]:
|
|
pitcher_string += f', {bs["hit"]} H'
|
|
if bs["bb"]:
|
|
pitcher_string += f', {bs["bb"]} BB'
|
|
if bs["hbp"]:
|
|
pitcher_string += f', {bs["hbp"]} HBP'
|
|
if bs["hr"]:
|
|
pitcher_string += f', {bs["hbp"]} HR'
|
|
else:
|
|
p = await get_one_pitchingseason(data['pitcher_member']['player_id'])
|
|
if len(p) > 0:
|
|
if p['ip'] > 0:
|
|
win = f'{p["win"]:.0f}'
|
|
loss = f'{p["loss"]:.0f}'
|
|
save = f'{p["sv"]:.0f}'
|
|
era = f'{(p["erun"] * 9) / p["ip"]:.2f}'
|
|
game = f'{p["game"]:.0f}'
|
|
gs = f'{p["gs"]:.0f}'
|
|
ip = f'{p["ip"]:.0f}'
|
|
if p["ip"] % 1 == 0:
|
|
ip += '.0'
|
|
elif str(p["ip"] % 1)[2] == '3':
|
|
ip += '.1'
|
|
else:
|
|
ip += '.2'
|
|
so = f'{p["so"]:.0f}'
|
|
whip = f'{(p["bb"] + p["hit"]) / p["ip"]:.2f}'
|
|
|
|
g_string = f'{gs} GS' if p['game'] == p['gs'] else f'{game} G'
|
|
pitcher_string = f' - {g_string}, ({win}-{loss})'
|
|
if p["sv"]:
|
|
pitcher_string += f', {save} SV'
|
|
pitcher_string += f', {era} ERA'
|
|
|
|
# pitching_string = f'```\n' \
|
|
# f' W L SV ERA IP SO WHIP\n' \
|
|
# f'{win: >2} {loss: >2} {save: >2} {era: >5} {ip: >5} ' \
|
|
# f'{so: >3} {whip: >4}\n```'
|
|
|
|
logger.info(f'pitcher_string: {pitcher_string}')
|
|
return pitcher_string
|
|
|
|
@staticmethod
|
|
def adv_specific_runner(this_game, from_base, num_bases):
|
|
"""
|
|
Moves a single runner and gives them a run scored if necessary - does not update game state
|
|
|
|
:param this_game:
|
|
:param from_base:
|
|
:param num_bases:
|
|
:return:
|
|
"""
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
rbi = 0
|
|
if from_base == 3 and this_state['on_third']:
|
|
logger.info(f'runner {this_state["on_third"]["id"]} scores')
|
|
this_runner = get_atbat(
|
|
game_id=this_state['game']['id'], batter_id=this_state['on_third']['player_id']
|
|
)['atbats']
|
|
on_third = this_runner[len(this_runner) - 1]
|
|
patch_atbat(on_third['id'], run=1)
|
|
patch_game_state(this_state['id'], on_third_id=False)
|
|
rbi += 1
|
|
|
|
elif from_base == 2 and this_state['on_second']:
|
|
if num_bases > 1:
|
|
logger.info(f'runner {this_state["on_second"]["id"]} scores')
|
|
this_runner = get_atbat(
|
|
game_id=this_state['game']['id'], batter_id=this_state['on_second']['player_id']
|
|
)['atbats']
|
|
on_second = this_runner[len(this_runner) - 1]
|
|
patch_atbat(on_second['id'], run=1)
|
|
patch_game_state(this_state['id'], on_second_id=False)
|
|
rbi += 1
|
|
else:
|
|
logger.info(f'runner {this_state["on_second"]["id"]} from second to third')
|
|
patch_game_state(this_state['id'], on_second_id=False, on_third_id=this_state['on_second']['id'])
|
|
|
|
elif from_base == 1 and this_state['on_first']:
|
|
if num_bases > 2:
|
|
logger.info(f'runner {this_state["on_first"]["id"]} scores')
|
|
this_runner = get_atbat(
|
|
game_id=this_state['game']['id'], batter_id=this_state['on_first']['player_id']
|
|
)['atbats']
|
|
on_first = this_runner[len(this_runner) - 1]
|
|
patch_atbat(on_first['id'], run=1)
|
|
patch_game_state(this_state['id'], on_first_id=False)
|
|
rbi += 1
|
|
elif num_bases == 2:
|
|
logger.info(f'runner {this_state["on_first"]["id"]} from first to third')
|
|
patch_game_state(this_state['id'], on_first_id=False, on_third_id=this_state['on_first']['id'])
|
|
else:
|
|
logger.info(f'runner {this_state["on_first"]["id"]} from first to second')
|
|
patch_game_state(this_state['id'], on_first_id=False, on_second_id=this_state['on_first']['id'])
|
|
|
|
return rbi
|
|
|
|
async def game_state_embed(self, this_game, full_length=True):
|
|
data = await self.game_state_ascii(this_game)
|
|
|
|
embed = discord.Embed(title=f'{data["away_team"]["sname"]} @ {data["home_team"]["sname"]}')
|
|
embed.add_field(name='Game State', value=data['gs'], inline=False)
|
|
|
|
pitcher_string = f'[{data["pitcher"]["name"]}]({get_player_url(data["pitcher"])}) ' \
|
|
f'{await self.get_pitcher_statline(data)}'
|
|
# embed.add_field(name='Pitcher', value=pitcher_string)
|
|
|
|
batter_string = f'[{data["batter"]["name"]}]({get_player_url(data["batter"])}) ' \
|
|
f'{await self.get_batter_statline(data)}'
|
|
# embed.add_field(name='Batter', value=batter_string)
|
|
embed.add_field(name='Matchup', value=f'Pitcher: {pitcher_string}\nvs\nBatter: {batter_string}', inline=False)
|
|
|
|
embed.set_thumbnail(url=f'{data["pitcher"]["image"]}')
|
|
embed.set_image(url=f'{data["batter"]["image"]}')
|
|
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
baserunner_string = ''
|
|
if this_state['on_first']:
|
|
runner = await get_one_player(this_state['on_first']['player_id'])
|
|
# embed.add_field(name='On First', value=f'[{runner["name"]}]({get_player_url(runner)})')
|
|
baserunner_string += f'On First: [{runner["name"]}]({get_player_url(runner)})\n'
|
|
if this_state['on_second']:
|
|
runner = await get_one_player(this_state['on_second']['player_id'])
|
|
# embed.add_field(name='On Second', value=f'[{runner["name"]}]({get_player_url(runner)})')
|
|
baserunner_string += f'On Second: [{runner["name"]}]({get_player_url(runner)})\n'
|
|
if this_state['on_third']:
|
|
runner = await get_one_player(this_state['on_third']['player_id'])
|
|
# embed.add_field(name='On Third', value=f'[{runner["name"]}]({get_player_url(runner)})')
|
|
baserunner_string += f'On Third: [{runner["name"]}]({get_player_url(runner)})\n'
|
|
if len(baserunner_string) > 0:
|
|
embed.add_field(name='Baserunners', value=baserunner_string, inline=False)
|
|
|
|
if not full_length:
|
|
return embed
|
|
|
|
async def lineup_string(team):
|
|
full_lineup = get_lineup_members(this_game['id'], active=1, team_id=team['id'])
|
|
sorted_lineup = sorted(full_lineup['members'], key=lambda item: item['batting_order'])
|
|
l_string = ''
|
|
for x in sorted_lineup:
|
|
player = await get_one_player(x["player_id"])
|
|
l_string += f'{x["batting_order"]}. [{player["name"]}]({get_player_url(player)}) - ' \
|
|
f'{x["position"]}\n'
|
|
if len(l_string) == 0:
|
|
l_string = 'None, yet\n\'!sl\' to set'
|
|
return l_string
|
|
|
|
embed.add_field(
|
|
name=f'{data["away_team"]["abbrev"]} Lineup',
|
|
value=await lineup_string(data["away_team"])
|
|
)
|
|
embed.add_field(
|
|
name=f'{data["home_team"]["abbrev"]} Lineup',
|
|
value=await lineup_string(data["home_team"])
|
|
)
|
|
|
|
return embed
|
|
|
|
def log_play_core(self, this_game, bases=0, outs=0, ab=1, hit=0, error=0, force_only=False):
|
|
"""
|
|
Logs an AtBat, taking the optional parameters into account; does not place batter runner
|
|
|
|
:param error:
|
|
:param this_game:
|
|
:param bases:
|
|
:param outs:
|
|
:param ab:
|
|
:param hit:
|
|
:param force_only:
|
|
:return: {'batter': LineupMember, 'ab': AtBat}
|
|
"""
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
data = self.get_pbc(this_game)
|
|
this_batter = data['batter']
|
|
this_pitcher = data['pitcher']
|
|
|
|
# Get OBC and patch GameState score if necessary
|
|
logger.info('Get OBC and patch GameState score if necessary')
|
|
obc = self.on_base_code(this_state)
|
|
|
|
rbi = 0
|
|
next_batters = {
|
|
'away': this_state['away_batter_up'] + 1 if this_state['away_batter_up'] < 9 else 1,
|
|
'home': this_state['home_batter_up'] + 1 if this_state['home_batter_up'] < 9 else 1,
|
|
}
|
|
new_half = this_state['top_half']
|
|
new_inning = this_state['inning']
|
|
if this_state['outs'] + outs < 3:
|
|
new_outs = this_state['outs'] + outs
|
|
|
|
# Advance runners
|
|
logger.info('Advance runners')
|
|
if obc > 0 and bases > 0:
|
|
rbi = self.advance_runners(this_state, bases, error=error, force_only=force_only)
|
|
if bases == 4:
|
|
rbi += 1
|
|
else:
|
|
# Ends half inning
|
|
new_outs = 0
|
|
new_half = not this_state['top_half']
|
|
if new_half:
|
|
new_inning += 1
|
|
patch_game_state(
|
|
this_state['id'],
|
|
on_first_id=False,
|
|
on_second_id=False,
|
|
on_third_id=False
|
|
)
|
|
|
|
if this_state['top_half']:
|
|
# Update score, batter up, and runner on first
|
|
logger.info(f'current batter: {this_state["away_batter_up"]} / next: {next_batters["away"]}')
|
|
patch_game_state(
|
|
this_state['id'],
|
|
away_score=this_state['away_score'] + rbi,
|
|
away_batter_up=next_batters['away'],
|
|
outs=new_outs,
|
|
top_half=new_half,
|
|
inning=new_inning
|
|
)
|
|
else:
|
|
patch_game_state(
|
|
this_state['id'],
|
|
home_score=this_state['home_score'] + rbi,
|
|
home_batter_up=next_batters['home'],
|
|
outs=new_outs,
|
|
top_half=new_half,
|
|
inning=new_inning
|
|
)
|
|
|
|
logger.info(f'post AB:\ngame_id: {this_game["id"]}\nbatter_id: {this_batter["player_id"]}\n'
|
|
f'pitcher_id: {this_pitcher["player_id"]}\nobc: {obc}\ninning: {this_state["inning"]}\n'
|
|
f'hit: {hit}\nrbi: {rbi}\nab: {ab}\nerror: {error}')
|
|
this_ab = post_atbat({
|
|
'game_id': this_game['id'],
|
|
'batter_id': this_batter['player_id'],
|
|
'pitcher_id': this_pitcher['player_id'],
|
|
'on_base_code': obc,
|
|
'inning': this_state['inning'],
|
|
'hit': hit,
|
|
'rbi': rbi if not error else 0,
|
|
'ab': ab,
|
|
'error': error
|
|
})
|
|
|
|
return {'batter': this_batter, 'ab': this_ab}
|
|
|
|
async def get_csv_batters(self, this_game, team_id):
|
|
bats = []
|
|
|
|
for x in get_lineup_members(this_game['id'], team_id=team_id)['members']:
|
|
this_player = await get_one_player(x['player_id'])
|
|
bs = get_atbat_totals(x['id'])
|
|
rs = get_running_totals(x['id'])
|
|
ch = get_chaos_totals(x['id'])
|
|
ds = get_defense_totals(x['id'])
|
|
|
|
if bs['empty'] and rs['empty'] and ch['empty'] and len(ds['defense']) == 0:
|
|
logger.info(f'No stats for {this_player["name"]} - moving on')
|
|
else:
|
|
dpos = None
|
|
if len(ds['defense']) == 1:
|
|
logger.info('one defense found')
|
|
if ds['defense'][0]['pos'] == x['position']:
|
|
logger.info('matches position')
|
|
dpos = ds['defense'][0]
|
|
else:
|
|
logger.info('doesn\'t match position')
|
|
line = ds['defense'][0]
|
|
bats.append([
|
|
this_player['name'], this_player['team']['abbrev'], x['position'], '', '', '',
|
|
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', line['xch'],
|
|
line['xch'],
|
|
line['xch'], line['xhit'], line['errors'], '', line['sba'], line['csc'], line['roba'],
|
|
line['robs'], line['raa'], line['rto']
|
|
])
|
|
else:
|
|
logger.info('many defenses found')
|
|
for line in ds['defense']:
|
|
if line['pos'] == line['pos']:
|
|
logger.info('this one matches pos')
|
|
dpos = line
|
|
else:
|
|
logger.info('this one does not matche pos')
|
|
bats.append([
|
|
this_player['name'], this_player['team']['abbrev'], x['position'], '', '', '',
|
|
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', line['xch'],
|
|
line['xch'],
|
|
line['xch'], line['xhit'], line['errors'], '', line['sba'], line['csc'], line['roba'],
|
|
line['robs'], line['raa'], line['rto']
|
|
])
|
|
if not dpos:
|
|
logger.info('no defense found; adding blanks')
|
|
dpos = {
|
|
'xch': '',
|
|
'xhit': '',
|
|
'errors': '',
|
|
'sba': '',
|
|
'csc': '',
|
|
'roba': '',
|
|
'robs': '',
|
|
'raa': '',
|
|
'rto': '',
|
|
}
|
|
logger.info('done with defense')
|
|
|
|
bats.append([
|
|
this_player['name'], this_player['team']['abbrev'], x['position'], bs['pa'], bs['ab'], bs['run'],
|
|
bs['hit'], bs['rbi'], bs['double'], bs['triple'], bs['homerun'], bs['bb'], bs['so'],
|
|
bs['hbp'], bs['sac'], bs['ibb'], bs['gidp'], rs['sb'], rs['cs'], bs['bphr'], bs['bpfo'], bs['bp1b'],
|
|
bs['bplo'], '', '', dpos['xch'], dpos['xhit'], dpos['errors'],
|
|
ch['pb'] if x['position'] == 'C' else '', dpos['sba'], dpos['csc'], '', '', dpos['raa'], dpos['rto']
|
|
])
|
|
return bats
|
|
|
|
async def get_csv_pitchers(self, this_game, team_id):
|
|
arms = []
|
|
|
|
for x in get_lineup_members(this_game['id'], team_id=team_id, position='P')['members']:
|
|
this_player = await get_one_player(x['player_id'])
|
|
bs = get_atbat_pitching_totals(x['id'])
|
|
cs = get_chaos_totals(x['id'])
|
|
|
|
if bs['empty'] and cs['empty']:
|
|
logger.info(f'No stats for {this_player["name"]} - moving on')
|
|
else:
|
|
arms.append([
|
|
this_player['name'], this_player['team']['abbrev'], bs['ip'], bs['hit'], bs['run'], bs['erun'],
|
|
bs['so'], bs['bb'], bs['hbp'], cs['wp'], cs['bk'], bs['hr']
|
|
])
|
|
return arms
|
|
|
|
async def get_game_results(self, this_game, combined=False):
|
|
away_bats = await self.get_csv_batters(this_game, this_game['away_team_id'])
|
|
away_arms = await self.get_csv_pitchers(this_game, this_game['away_team_id'])
|
|
home_bats = await self.get_csv_batters(this_game, this_game['home_team_id'])
|
|
home_arms = await self.get_csv_pitchers(this_game, this_game['home_team_id'])
|
|
|
|
if combined:
|
|
batters = [['player', 'team', 'pos', 'pa', 'ab', 'run', 'hit', 'rbi', '2b', '3b', 'hr', 'bb', 'so', 'hbp',
|
|
'sac', 'ibb', 'gidp', 'sb', 'cs', 'bphr', 'bpfo', 'bp1b', 'bplo', 'xba', 'xbt', 'xch', 'xhit', 'e',
|
|
'pb', 'sba', 'csc', 'roba', 'robs', 'raa', 'rto']]
|
|
for x in away_bats:
|
|
batters.append(x)
|
|
for x in home_bats:
|
|
batters.append(x)
|
|
|
|
pitchers = [['pitcher', 'team', 'ip', 'h', 'r', 'er', 'so', 'bb', 'hbp', 'wp', 'bk', 'hr', 'ir', 'irs',
|
|
'gs', 'win', 'loss', 'hold', 'save', 'bs']]
|
|
for x in away_arms:
|
|
pitchers.append(x)
|
|
for x in home_arms:
|
|
pitchers.append(x)
|
|
|
|
return {'batters': batters, 'pitchers': pitchers}
|
|
else:
|
|
return {'ab': away_bats, 'aa': away_arms, 'hb': home_bats, 'ha': home_arms}
|
|
|
|
async def get_game_sheet(self, home_team, away_team, results, player_name):
|
|
sheets = pygsheets.authorize(service_file='storage/major-domo-service-creds.json')
|
|
results_sheet = sheets.drive.copy_file(
|
|
'1e_o5Fg0-Q9sp09btesBpXA_4lUtbNkRngQ32_Ie9_iI',
|
|
f'{away_team["abbrev"]} @ {home_team["abbrev"]} for {player_name}',
|
|
'1G0XJvlUb2cIGjD3b2-XT89TSwAOZmzaz')
|
|
sheet_id = results_sheet['id']
|
|
|
|
results_tab = sheets.open_by_key(sheet_id).worksheet_by_title('Results')
|
|
results_tab.update_values(
|
|
crange='A3', values=results['ab']
|
|
)
|
|
results_tab.update_values(
|
|
crange='A33', values=results['hb']
|
|
)
|
|
results_tab.update_values(
|
|
crange='A22', values=results['aa']
|
|
)
|
|
results_tab.update_values(
|
|
crange='A52', values=results['ha']
|
|
)
|
|
|
|
return sheet_id
|
|
|
|
@commands.command(name='newgame', aliases=['ng'], help='Start a new game')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def new_game_command(self, ctx):
|
|
current = await db_get('current')
|
|
this_team = await get_team_by_owner(current['season'], ctx.author.id)
|
|
|
|
await ctx.send('**Note:** Make sure this is the channel where you will be playing commands. Once this is set, '
|
|
'I will only take gameplay commands here.')
|
|
|
|
async def get_team(which='home'):
|
|
prompt = f'Please enter the abbrev of the {which} team.'
|
|
this_q = Question(self.bot, ctx.channel, prompt, qtype='text', timeout=15)
|
|
resp = await this_q.ask([ctx.author])
|
|
|
|
if not resp:
|
|
await ctx.send('You keep thinking about it and hit me up later if you figure it out.')
|
|
return None
|
|
else:
|
|
other_team = await get_one_team(resp)
|
|
if not other_team:
|
|
await ctx.send(f'What\'s a **{resp}**? If you could go ahead and run this command again, that\'d '
|
|
f'be great.')
|
|
return None
|
|
else:
|
|
return other_team
|
|
|
|
if not this_team:
|
|
await ctx.send('Hmm...I can\'t find your team. Are you from around here?')
|
|
return
|
|
|
|
try:
|
|
this_matchup = await get_one_schedule(
|
|
season=current['season'],
|
|
team_abbrev1=this_team['abbrev'],
|
|
week=current['week']
|
|
)
|
|
except ValueError as e:
|
|
home_team = await get_team('home')
|
|
away_team = await get_team('away')
|
|
if not home_team or not away_team:
|
|
return
|
|
else:
|
|
away_team = this_matchup['awayteam']
|
|
home_team = this_matchup['hometeam']
|
|
|
|
prompt = f'Would you like to start {away_team["abbrev"]} @ {home_team["abbrev"]} now?'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 15)
|
|
resp = await this_q.ask([ctx.author])
|
|
|
|
if not resp:
|
|
home_team = await get_team('home')
|
|
away_team = await get_team('away')
|
|
if not home_team or not away_team:
|
|
return
|
|
|
|
this_game = post_game({
|
|
'away_team_id': away_team['id'],
|
|
'home_team_id': home_team['id'],
|
|
'channel_id': ctx.channel.id
|
|
})
|
|
await ctx.send(random_conf_gif())
|
|
await ctx.send('Go ahead and set lineups with the !setlineup command')
|
|
|
|
@commands.command(name='endgame', aliases=['eg'], help='End game in channel')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def end_game_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs_embed = await self.game_state_embed(this_game)
|
|
|
|
prompt = 'Is this the game you would like to end?'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 15, gs_embed)
|
|
resp = await this_q.ask([ctx.author])
|
|
end_proper = True
|
|
|
|
if not resp:
|
|
await ctx.send('Okay, we can leave it active for now.')
|
|
return
|
|
else:
|
|
away_team = await get_one_team(this_game['away_team_id'])
|
|
home_team = await get_one_team(this_game['home_team_id'])
|
|
|
|
try:
|
|
async with ctx.typing():
|
|
results = await self.get_game_results(this_game)
|
|
sheet_id = await self.get_game_sheet(home_team, away_team, results, ctx.author.display_name)
|
|
except pygsheets.InvalidArgumentValue as e:
|
|
prompt = 'There was an error creating the scorecard. Should I just nuke this game?'
|
|
this_q.prompt = prompt
|
|
resp = await this_q.ask([ctx.author])
|
|
|
|
if not resp:
|
|
await ctx.send('Okay, we can leave it active for now.')
|
|
return
|
|
else:
|
|
end_proper = False
|
|
|
|
if end_proper:
|
|
await ctx.send(f'Here is the results sheet for this game: https://docs.google.com/spreadsheets/d/'
|
|
f'{sheet_id}')
|
|
else:
|
|
await ctx.send(random_conf_gif())
|
|
patch_game(game_id=this_game['id'], active=False)
|
|
|
|
@commands.command(name='setlineup', aliases=['sub', 'sl'], help='!sl <formatted_subs>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def set_lineup_command(self, ctx, team_abbrev, *, lineup):
|
|
ERROR_MESSAGE = 'The format for a lineup is: <batting order # (10 for pitcher)>-<Player Name>-<position>\n' \
|
|
'Examples:\n2-Trea Turner-SS\n10-Rich Hill-P\n\n' \
|
|
'You may set multiple spots by adding a comma at the end of the position and, without ' \
|
|
'spaces, entering the next spot.\n' \
|
|
'Examples:\n1-Ronald Acuna Jr-RF,2-Trea Turner-SS,3-Robinson Cano-2B\n' \
|
|
'7-Travis Jankowski-LF,10-Anthony Bass-P,5-Erik Gonzalez-3B'
|
|
|
|
# current = await db_get('current')
|
|
this_team = await get_one_team(team_abbrev)
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
logger.info(f'this_game: {this_game}')
|
|
if this_team['id'] not in [this_game['away_team_id'], this_game['home_team_id']]:
|
|
away_team = await get_one_team(this_game['away_team_id'])
|
|
home_team = await get_one_team(this_game['home_team_id'])
|
|
await ctx.send(f'This channel is for {away_team["abbrev"]} @ {home_team["abbrev"]} - I can\'t set a lineup '
|
|
f'for **{team_abbrev.upper()}**.')
|
|
return
|
|
|
|
async with ctx.typing():
|
|
for member in re.split(',', lineup):
|
|
data = re.split('-', member)
|
|
this_player = await get_one_player(data[1])
|
|
|
|
# Check if player is already in game
|
|
try:
|
|
this_member = get_one_member(game_id=this_game['id'], player_id=this_player['id'], active=1)
|
|
except:
|
|
this_member = None
|
|
|
|
# If already in game, just change position
|
|
if this_member:
|
|
# Stop sub if changing batting order - unless pitcher is entering the lineup
|
|
if this_member['batting_order'] != int(data[0]) and this_member['batting_order'] != 10:
|
|
await ctx.send(f'{this_player["name"]} is already in the game batting '
|
|
f'#{this_member["batting_order"]} so they cannot move in the order.')
|
|
return
|
|
|
|
new_member = patch_lineup_member(
|
|
member_id=this_member['id'],
|
|
position=data[2].upper(),
|
|
batting_order=data[0]
|
|
)
|
|
|
|
# Else add new lineup member
|
|
else:
|
|
new_member = post_lineup_member({
|
|
'game_id': this_game['id'],
|
|
'team_id': this_team['id'],
|
|
'player_id': this_player['id'],
|
|
'position': data[2].upper(),
|
|
'batting_order': data[0]
|
|
})
|
|
|
|
members = get_lineup_members(this_game['id'], this_team['id'], batting_order=data[0], active=1)
|
|
if len(members['members']) > 1:
|
|
for x in range(len(members['members'])):
|
|
if members['members'][x]['id'] != new_member['id']:
|
|
old_member = patch_lineup_member(members['members'][x]['id'], active=False)
|
|
|
|
this_state = get_one_game_state(game_id=this_game['id'])
|
|
logger.info(f'old_member_id: {old_member["id"]} / on_first: {this_state["on_first"]} / '
|
|
f'on_second: {this_state["on_second"]} / on_third: {this_state["on_third"]}')
|
|
if old_member in [this_state['on_first'], this_state['on_second'], this_state['on_third']]:
|
|
if old_member == this_state['on_first']:
|
|
this_state = patch_game_state(
|
|
this_state['id'],
|
|
on_first_id=new_member['id']
|
|
)
|
|
elif old_member == this_state['on_second']:
|
|
this_state = patch_game_state(
|
|
this_state['id'],
|
|
on_second_id=new_member['id']
|
|
)
|
|
elif old_member == this_state['on_third']:
|
|
this_state = patch_game_state(
|
|
this_state['id'],
|
|
on_third_id=new_member['id']
|
|
)
|
|
|
|
sub_ab = post_atbat({
|
|
'game_id': this_game['id'],
|
|
'batter_id': new_member['player_id'],
|
|
'pitcher_id': self.get_pbc(this_game)['pitcher']['player_id'],
|
|
'on_base_code': 0,
|
|
'inning': this_state['inning'],
|
|
'pa': 0
|
|
})
|
|
|
|
# If player appears elsewhere in lineup, deactive old members
|
|
dupes = get_lineup_members(
|
|
this_game['id'], team_id=this_team['id'], player_id=this_player['id'], active=1
|
|
)
|
|
if len(dupes['members']) > 1:
|
|
for x in range(len(dupes['members']) - 1):
|
|
patch_lineup_member(dupes['members'][x]['id'], active=False)
|
|
|
|
home_check = self.lineup_check(this_game, True)
|
|
away_check = self.lineup_check(this_game, False)
|
|
|
|
check_string = f'Away lineup valid: {away_check["valid"]}\n'
|
|
if len(away_check['errors']) > 0:
|
|
error_string = "\n- ".join(away_check["errors"])
|
|
check_string += f'- {error_string}\n\n'
|
|
|
|
check_string += f'Home lineup valid: {home_check["valid"]}\n'
|
|
if len(home_check['errors']) > 0:
|
|
error_string = "\n- ".join(home_check["errors"])
|
|
check_string += f'- {error_string}\n\n'
|
|
|
|
if away_check['valid'] and home_check['valid']:
|
|
logger.info('Both lineups valid - sending gamestate')
|
|
content = None
|
|
embed = await self.game_state_embed(this_game, full_length=False)
|
|
else:
|
|
logger.info('Both lineups are not valid - sending check_string')
|
|
content = check_string
|
|
embed = None
|
|
|
|
await ctx.send(content=content, embed=embed)
|
|
|
|
@commands.command(name='gamestate', aliases=['gs'], help='Show game state')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def game_state_command(self, ctx):
|
|
# current = await db_get('current')
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game))
|
|
|
|
@commands.group(name='log', help='`!help log` for all commands')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_play(self, ctx):
|
|
if ctx.invoked_subcommand is None:
|
|
await ctx.send('No play details listed. Run `!help log` for available commands.')
|
|
|
|
@log_play.command(
|
|
name='single',
|
|
aliases=['si', 'si*', 'si**', 'siwh', '1b', '1b*', 'bp1b', 'bpsi', '1b**', '1bwh', 'single*', 'single**',
|
|
'singlewh'],
|
|
help='si, 1b, bp1b, bpsi, single, *, **, wh'
|
|
)
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_single_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
cmd = ctx.message.content.lower()
|
|
|
|
# Get baserunner advancement
|
|
if 'wh' in cmd or '**' in cmd:
|
|
bases = 2
|
|
else:
|
|
bases = 1
|
|
|
|
data = self.log_play_core(this_game, bases=bases, hit=1)
|
|
patch_game_state(
|
|
this_game['id'],
|
|
on_first_id=data['batter']['id']
|
|
)
|
|
|
|
if 'bp' in cmd:
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
bp1b=1
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(
|
|
name='double',
|
|
aliases=['do', 'do**', 'do***', 'dowh', '2b', '2b**', '2b***', '2bwh', 'double**', 'double***', 'doublewh'],
|
|
help='do, 2b, double, **, ***, wh'
|
|
)
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_double_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
# Get baserunner advancement
|
|
if 'wh' in ctx.message.content.lower() or '***' in ctx.message.content:
|
|
bases = 3
|
|
else:
|
|
bases = 2
|
|
|
|
data = self.log_play_core(this_game, bases=bases, hit=1)
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
double=1,
|
|
)
|
|
patch_game_state(
|
|
this_game['id'],
|
|
on_second_id=data['batter']['id']
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='triple', aliases=['tr', '3b'], help='tr, 3b')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_triple_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
# Get baserunner advancement
|
|
bases = 3
|
|
|
|
data = self.log_play_core(this_game, bases=bases, hit=1)
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
triple=1,
|
|
)
|
|
patch_game_state(
|
|
this_game['id'],
|
|
on_third_id=data['batter']['id']
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='homerun', aliases=['hr', 'bphr'], help='hr, bphr')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_homerun_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
# Get baserunner advancement
|
|
ballpark = 1 if 'bphr' in ctx.message.content.lower() else 0
|
|
data = self.log_play_core(this_game, bases=4, hit=1)
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
run=1,
|
|
rbi=data['ab']['rbi'],
|
|
homerun=1,
|
|
bphr=ballpark
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='sacrifice', aliases=['sacf', 'sacb'], help='sacf, sacb')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_sac_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
rbi = 0
|
|
sac = 0
|
|
bases = 0
|
|
ab = 0
|
|
if gs['outs'] < 2:
|
|
cmd = ctx.message.content.lower()
|
|
sac = 1
|
|
ab = 0
|
|
if 'sacf' in cmd:
|
|
rbi = self.adv_specific_runner(this_game, 3, 1)
|
|
elif 'sacb' in cmd:
|
|
bases = 1
|
|
|
|
data = self.log_play_core(this_game, bases=bases, outs=1, ab=ab)
|
|
patch_atbat(
|
|
atbat_id=data['ab']['id'],
|
|
rbi=rbi,
|
|
sac=sac
|
|
)
|
|
if rbi:
|
|
self.increment_score(this_game, rbi)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='walk', aliases=['bb', 'hbp', 'ibb'], help='bb, hbp, ibb')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_walk_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
data = self.log_play_core(this_game, bases=1, ab=0, force_only=True)
|
|
patch_game_state(
|
|
this_game['id'],
|
|
on_first_id=data['batter']['id']
|
|
)
|
|
|
|
cmd = ctx.message.content.lower()
|
|
if 'ibb' in cmd:
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
ibb=1,
|
|
)
|
|
elif 'walk' in cmd or 'bb' in cmd:
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
bb=1,
|
|
)
|
|
elif 'hbp' in cmd:
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
hbp=1,
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='sb_attempt', aliases=['sb', 'cs'], help='sb, cs')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_sb_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
if not gs['on_first'] and not gs['on_second'] and not gs['on_third']:
|
|
await ctx.send('I don\'t see any baserunners aboard - maybe this isn\'t a stolen base.')
|
|
return
|
|
|
|
runner_found = None
|
|
this_runner = None
|
|
count = 4
|
|
for runner in [gs['on_third'], gs['on_second'], gs['on_first']]:
|
|
count -= 1
|
|
if runner:
|
|
a_runner = await get_one_player(runner['player_id'])
|
|
prompt = f'Did {a_runner["name"]} attempt the steal?'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
resp = await this_q.ask([ctx.author])
|
|
if resp:
|
|
runner_found = count
|
|
this_runner = runner
|
|
break
|
|
|
|
if not runner_found:
|
|
await ctx.send('If nobody was forced out - it must not have been a fielder\'s choice.')
|
|
return
|
|
|
|
cmd = ctx.message.content.upper()
|
|
stolen_base = 1 if 'SB' in cmd else 0
|
|
battery = self.get_pbc(this_game)
|
|
|
|
post_running({
|
|
'game_id': this_game['id'],
|
|
'runner_id': this_runner['player_id'],
|
|
'stolen_base': stolen_base,
|
|
'caught_stealing': not stolen_base
|
|
})
|
|
|
|
post_defense({
|
|
'game_id': this_game['id'],
|
|
'player_id': battery['catcher']['player_id'],
|
|
'position': 'C',
|
|
'stolen_base_attempt': 1,
|
|
'caught_stealing': not stolen_base,
|
|
})
|
|
|
|
if stolen_base:
|
|
if runner_found == 1:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_second_id=runner['id'],
|
|
on_first_id=False
|
|
)
|
|
elif runner_found == 2:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_third_id=runner['id'],
|
|
on_second_id=False
|
|
)
|
|
else:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_third_id=False
|
|
)
|
|
self.increment_score(this_game, 1)
|
|
else:
|
|
if runner_found == 1:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_first_id=False
|
|
)
|
|
elif runner_found == 2:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_second_id=False
|
|
)
|
|
else:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_third_id=False
|
|
)
|
|
self.increment_outs(this_game, 1)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
# @log_play.command(name='extra_base', aliases=['xba', 'xbt'], help='xba, xbt')
|
|
# @commands.has_any_role(SBA_PLAYERS_ROLE_NAME)
|
|
# async def log_xba_command(self, ctx):
|
|
# this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
# gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
#
|
|
# if not gs['on_first'] and not gs['on_second'] and not gs['on_third']:
|
|
# await ctx.send('I don\'t see any baserunners aboard - maybe there wasn\'t an advance.')
|
|
# return
|
|
#
|
|
# runner_found = None
|
|
# runner_player = None
|
|
# this_runner = None
|
|
# base_taken = True
|
|
# count = 4
|
|
# for runner in [gs['on_third'], gs['on_second'], gs['on_first']]:
|
|
# count -= 1
|
|
# if runner:
|
|
# runner_player = await get_one_player(runner['player_id'])
|
|
# prompt = f'Did {runner_player["name"]} attempt the advance?'
|
|
# this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
# resp = await this_q.ask([ctx.author])
|
|
# if resp:
|
|
# runner_found = count
|
|
# this_runner = runner
|
|
# break
|
|
#
|
|
# if not runner_found:
|
|
# await ctx.send('If nobody made an attempt - it must not have been an xba.')
|
|
# return
|
|
#
|
|
# prompt = 'Was there a throw from an outfielder? (Was a d20 rolled?)'
|
|
# this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
# defender_found = None
|
|
# this_defender = None
|
|
# resp = await this_q.ask([ctx.author])
|
|
# if resp:
|
|
# count = -1
|
|
# for defender in [self.get_defender(this_game, 'CF'), self.get_defender(this_game, 'RF'),
|
|
# self.get_defender(this_game, 'LF')]:
|
|
# count += 1
|
|
# if defender:
|
|
# a_defender = await get_one_player(defender['player_id'])
|
|
# prompt = f'Did {a_defender["name"]} make the throw?'
|
|
# this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
# resp = await this_q.ask([ctx.author])
|
|
# if resp:
|
|
# defender_found = ['CF', 'RF', 'LF'][count]
|
|
# this_defender = defender
|
|
# break
|
|
#
|
|
# if not defender_found:
|
|
# await ctx.send('So I guess there wasn\'t an attempt.')
|
|
# return
|
|
#
|
|
# this_q.prompt = f'Did {runner_player["name"]} reach safely?'
|
|
# resp = await this_q.ask([ctx.author])
|
|
# if resp is not None:
|
|
# base_taken = resp
|
|
#
|
|
# logger.info(f'defender: {defender}')
|
|
# post_defense({
|
|
# 'game_id': this_game['id'],
|
|
# 'player_id': this_defender['player_id'],
|
|
# 'position': this_defender['position'],
|
|
# 'runner_adv_att': 1,
|
|
# 'runner_throw_out': not base_taken
|
|
# })
|
|
#
|
|
# post_running({
|
|
# 'game_id': this_game['id'],
|
|
# 'runner_id': this_runner['player_id'],
|
|
# 'extra_base_attempt': 1,
|
|
# 'extra_base_taken': base_taken
|
|
# })
|
|
#
|
|
# if base_taken:
|
|
# if runner_found == 1:
|
|
# self.adv_specific_runner(this_game, 1, 1)
|
|
# elif runner_found == 2:
|
|
# self.adv_specific_runner(this_game, 2, 1)
|
|
# else:
|
|
# self.adv_specific_runner(this_game, 3, 1)
|
|
# self.increment_score(this_game, 1)
|
|
# await ctx.send('**Take a note to log this RBI - it\'s one of the things '
|
|
# 'I can\'t do automatically, yet.**')
|
|
# else:
|
|
# if runner_found == 1:
|
|
# patch_game_state(
|
|
# state_id=gs['id'],
|
|
# on_first_id=False
|
|
# )
|
|
# elif runner_found == 2:
|
|
# patch_game_state(
|
|
# state_id=gs['id'],
|
|
# on_second_id=False
|
|
# )
|
|
# else:
|
|
# patch_game_state(
|
|
# state_id=gs['id'],
|
|
# on_third_id=False
|
|
# )
|
|
# self.increment_outs(this_game, 1)
|
|
#
|
|
# await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
# @log_play.command(name='robs', aliases=['roba'], help='RobA, RobS')
|
|
# @commands.has_any_role(SBA_PLAYERS_ROLE_NAME)
|
|
# async def log_rob_command(self, ctx):
|
|
# this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
#
|
|
# defender_found = None
|
|
# this_defender = None
|
|
# count = 0
|
|
# for defender in [self.get_defender(this_game, 'CF'), self.get_defender(this_game, 'LF'),
|
|
# self.get_defender(this_game, 'RF')]:
|
|
# count += 1
|
|
# if defender:
|
|
# a_defender = await get_one_player(defender['player_id'])
|
|
# prompt = f'Did {a_defender["name"]} attempt the rob?'
|
|
# this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
# resp = await this_q.ask([ctx.author])
|
|
# if resp:
|
|
# defender_found = True
|
|
# this_defender = defender
|
|
# break
|
|
#
|
|
# if not defender_found:
|
|
# await ctx.send('So I guess there wasn\'t an attempt.')
|
|
# return
|
|
#
|
|
# prompt = f'Did {a_defender["name"]} successfully rob it?'
|
|
# this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
# resp = await this_q.ask([ctx.author])
|
|
# if resp is not None:
|
|
# base_taken = resp
|
|
#
|
|
# post_defense({
|
|
# 'game_id': this_game['id'],
|
|
# 'player_id': this_defender['player_id'],
|
|
# 'position': this_defender['position'],
|
|
# 'rob_attempt': 1,
|
|
# 'rob_success': resp
|
|
# })
|
|
#
|
|
# await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(
|
|
name='out',
|
|
aliases=['flyout', 'groundout', 'popout', 'lineout', 'strikeout', 'fo', 'bpfo', 'go', 'po', 'lo', 'bplo',
|
|
'so', 'k'],
|
|
help='fo, bpfo, go, po, lo, bplo, so, k'
|
|
)
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME)
|
|
async def log_out_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
cmd = ctx.message.content.lower()
|
|
if 'bpfo' in cmd:
|
|
data = self.log_play_core(this_game, outs=1, ab=1)
|
|
sac_rbi = self.adv_specific_runner(this_game, 3, 1)
|
|
patch_atbat(
|
|
atbat_id=data['ab']['id'],
|
|
ab=1 - sac_rbi,
|
|
bpfo=1,
|
|
rbi=sac_rbi,
|
|
sac=sac_rbi
|
|
)
|
|
else:
|
|
data = self.log_play_core(this_game, outs=1)
|
|
if 'so' in cmd or 'k' in cmd or 'strikeout' in cmd:
|
|
patch_atbat(
|
|
atbat_id=data['ab']['id'],
|
|
so=1
|
|
)
|
|
elif 'bplo' in cmd:
|
|
patch_atbat(
|
|
atbat_id=data['ab']['id'],
|
|
bplo=1
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='xcheck', aliases=['x'], help='x <pos>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_xcheck_command(self, ctx, position):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
if position.upper() not in ['C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF', 'P']:
|
|
await ctx.send(f'Ope, **{position.upper()}** is not a valid position.')
|
|
return
|
|
|
|
defender = self.get_defender(this_game, position)
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
prompt = f'Please enter the hit/out result (\'1B(\\*\\*)\', \'2B(\\*\\*\\*)\', \'3B\', or \'out\')'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'text', 15)
|
|
resp = await this_q.ask([ctx.author])
|
|
if not resp:
|
|
await ctx.send('You think on it and get back to me.')
|
|
return
|
|
elif resp.upper() not in ['1B', '1BWH', '1B**', '2B', '2BWH', '2B***', '3B', 'OUT']:
|
|
await ctx.send('I can only take 1B, 2B, 3B, or OUT')
|
|
return
|
|
else:
|
|
if resp.upper() == 'OUT':
|
|
hit = 0
|
|
else:
|
|
hit_bases = int(resp[0])
|
|
if len(resp) > 2:
|
|
bases = int(resp[0]) + 1
|
|
else:
|
|
bases = hit_bases
|
|
hit = 1
|
|
data = self.log_play_core(this_game, bases=bases, hit=1)
|
|
if hit_bases == 1:
|
|
patch_game_state(
|
|
state_id=this_state['id'],
|
|
on_first_id=data['batter']['id']
|
|
)
|
|
elif hit_bases == 2:
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
double=1,
|
|
)
|
|
patch_game_state(
|
|
state_id=this_state['id'],
|
|
on_second_id=data['batter']['id']
|
|
)
|
|
elif hit_bases == 3:
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
triple=1,
|
|
)
|
|
patch_game_state(
|
|
state_id=this_state['id'],
|
|
on_third_id=data['batter']['id']
|
|
)
|
|
|
|
prompt = f'For an error, please enter the number of bases. Otherwise, enter 0.'
|
|
this_q.prompt = prompt
|
|
this_q.qtype = 'int'
|
|
resp = await this_q.ask([ctx.author])
|
|
if resp is None:
|
|
await ctx.send('You think on it and get back to me.')
|
|
return
|
|
elif 0 > resp > 3:
|
|
await ctx.send('I can only take 0 -> 3')
|
|
return
|
|
else:
|
|
if resp > 0:
|
|
error = 1
|
|
if hit == 0:
|
|
data = self.log_play_core(this_game, bases=resp, error=1)
|
|
logger.info(f'error: {resp}')
|
|
if resp == 1:
|
|
patch_game_state(
|
|
state_id=this_state['id'],
|
|
on_first_id=data['batter']['id']
|
|
)
|
|
elif resp == 2:
|
|
patch_game_state(
|
|
state_id=this_state['id'],
|
|
on_second_id=data['batter']['id']
|
|
)
|
|
elif resp == 3:
|
|
patch_game_state(
|
|
state_id=this_state['id'],
|
|
on_third_id=data['batter']['id']
|
|
)
|
|
else:
|
|
self.advance_runners(this_state=this_state, num_bases=resp, error=1)
|
|
else:
|
|
error = 0
|
|
|
|
post_defense({
|
|
'game_id': this_game['id'],
|
|
'player_id': defender['player_id'],
|
|
'x_check': 1,
|
|
'position': defender['position'],
|
|
'hit': hit,
|
|
'error': error
|
|
})
|
|
if hit == 0 and error == 0:
|
|
await ctx.send('**The defense check has been logged - please log the specific out now. (e.g. !log '
|
|
'fo, !log gora, !log sacf)**')
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='doubleplay', aliases=['gidp', 'gba'], help='gidp, gbA')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_gidp_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
if not gs['on_first'] and not gs['on_second'] and not gs['on_third']:
|
|
await ctx.send('I don\'t see any baserunners aboard - maybe this should just be a groundout.')
|
|
return
|
|
|
|
runner_found = None
|
|
count = 0
|
|
for runner in [gs['on_first'], gs['on_second'], gs['on_third']]:
|
|
count += 1
|
|
if runner:
|
|
this_runner = await get_one_player(runner['player_id'])
|
|
prompt = f'Was {this_runner["name"]} doubled off?'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
resp = await this_q.ask([ctx.author])
|
|
if resp:
|
|
runner_found = count
|
|
break
|
|
|
|
if not runner_found:
|
|
await ctx.send('If nobody was double off - it must not have been a GIDP.')
|
|
return
|
|
|
|
# If both outs of gidp are recorded, remove baserunner and record gidp
|
|
if gs['outs'] < 2:
|
|
gidp = 1
|
|
if runner_found == 1:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_first_id=False
|
|
)
|
|
elif runner_found == 2:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_second_id=False
|
|
)
|
|
else:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_third_id=False
|
|
)
|
|
else:
|
|
gidp = 0
|
|
|
|
data = self.log_play_core(this_game, outs=2)
|
|
patch_atbat(
|
|
atbat_id=data['ab']['id'],
|
|
gidp=gidp
|
|
)
|
|
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
rbi = 0
|
|
count = 1
|
|
for runner in [gs['on_second'], gs['on_third']]:
|
|
count += 1
|
|
if runner:
|
|
this_runner = await get_one_player(runner['player_id'])
|
|
prompt = f'Did {this_runner["name"]} advance?'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
resp = await this_q.ask([ctx.author])
|
|
if resp:
|
|
rbi += self.adv_specific_runner(this_game, count, 1)
|
|
|
|
if rbi:
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
rbi=rbi,
|
|
)
|
|
patch_game_state(
|
|
gs['id'],
|
|
home_score=gs['home_score'] + rbi
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='gora', aliases=['gbc'], help='gora, gbC')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_gora_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
data = self.log_play_core(this_game, outs=1, bases=1)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='fc', aliases=['gbb'], help='fc, gbB')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_fc_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
if not gs['on_first'] and not gs['on_second'] and not gs['on_third']:
|
|
await ctx.send('I don\'t see any baserunners aboard - maybe this should just be a groundout.')
|
|
return
|
|
|
|
runner_found = None
|
|
count = 0
|
|
for runner in [gs['on_first'], gs['on_second'], gs['on_third']]:
|
|
count += 1
|
|
if runner:
|
|
this_runner = await get_one_player(runner['player_id'])
|
|
prompt = f'Was {this_runner["name"]} the forced out runner?'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
resp = await this_q.ask([ctx.author])
|
|
if resp:
|
|
runner_found = count
|
|
break
|
|
|
|
if not runner_found:
|
|
await ctx.send('If nobody was forced out - it must not have been a fielder\'s choice.')
|
|
return
|
|
|
|
# If fewer than 2 outs, remove forced out runner first
|
|
if gs['outs'] < 2:
|
|
if runner_found == 1:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_first_id=False
|
|
)
|
|
elif runner_found == 2:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_second_id=False
|
|
)
|
|
else:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_third_id=False
|
|
)
|
|
|
|
data = self.log_play_core(this_game, outs=1)
|
|
patch_game_state(
|
|
gs['id'],
|
|
on_first_id=data['batter']['id']
|
|
)
|
|
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
rbi = 0
|
|
count = 1
|
|
for runner in [gs['on_second'], gs['on_third']]:
|
|
count += 1
|
|
if runner:
|
|
this_runner = await get_one_player(runner['player_id'])
|
|
prompt = f'Did {this_runner["name"]} advance?'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
resp = await this_q.ask([ctx.author])
|
|
if resp:
|
|
rbi += self.adv_specific_runner(this_game, count, 1)
|
|
|
|
if rbi:
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
rbi=rbi,
|
|
)
|
|
self.increment_score(this_game, rbi)
|
|
# patch_game_state(
|
|
# gs['id'],
|
|
# home_score=gs['home_score'] + rbi
|
|
# )
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='chaos', aliases=['wp', 'pb', 'pick', 'balk'], help='wp, pb, pick, balk')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_chaos_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
cmd = ctx.message.content.lower()
|
|
pick = 0
|
|
wp = 0
|
|
pb = 0
|
|
balk = 0
|
|
if 'pick' in cmd:
|
|
if not gs['on_first'] and not gs['on_second'] and not gs['on_third']:
|
|
await ctx.send('I don\'t see any baserunners aboard.')
|
|
return
|
|
|
|
runner_found = None
|
|
count = 0
|
|
for runner in [gs['on_first'], gs['on_second'], gs['on_third']]:
|
|
count += 1
|
|
if runner:
|
|
this_runner = await get_one_player(runner['player_id'])
|
|
prompt = f'Was {this_runner["name"]} picked off?'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
resp = await this_q.ask([ctx.author])
|
|
if resp:
|
|
runner_found = count
|
|
break
|
|
|
|
if not runner_found:
|
|
await ctx.send('If nobody was picked off - maybe try another play.')
|
|
return
|
|
elif runner_found == 1:
|
|
patch_game_state(
|
|
gs['id'],
|
|
on_first_id=False
|
|
)
|
|
elif runner_found == 2:
|
|
patch_game_state(
|
|
gs['id'],
|
|
on_second_id=False
|
|
)
|
|
elif runner_found == 3:
|
|
patch_game_state(
|
|
gs['id'],
|
|
on_third_id=False
|
|
)
|
|
|
|
pick = 1
|
|
self.increment_outs(this_game, 1)
|
|
else:
|
|
if 'wp' in cmd:
|
|
wp = 1
|
|
elif 'balk' in cmd:
|
|
balk = 1
|
|
else:
|
|
pb = 1
|
|
runs = self.advance_runners(gs, num_bases=1, error=True)
|
|
if runs:
|
|
self.increment_score(this_game, runs)
|
|
|
|
data = self.get_pbc(this_game)
|
|
|
|
post_chaos({
|
|
'game_id': this_game['id'],
|
|
'pitcher_id': data['pitcher']['player_id'],
|
|
'catcher_id': data['catcher']['player_id'],
|
|
'wild_pitch': wp,
|
|
'passed_ball': pb,
|
|
'pick_off': pick,
|
|
'balk': balk
|
|
})
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@commands.command(name='undo', help='Roll back last entered play', hidden=True)
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def roll_back_command(self, ctx):
|
|
await ctx.send('This command will do something soon, I promise.')
|
|
|
|
@commands.command(name='advance', aliases=['adv'], help='!adv <from_base> <num_bases>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def advance_runners_command(self, ctx, from_base: int, num_bases: int):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
if 0 > from_base > 3:
|
|
await ctx.send('from_base parameter must be 1, 2, or 3')
|
|
return
|
|
if 0 > num_bases > 3:
|
|
await ctx.send('num_bases parameter must be 1, 2, or 3')
|
|
return
|
|
|
|
self.adv_specific_runner(this_game, from_base, num_bases)
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@commands.command(name='place', help='!place <base_num> <player_to_place>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def place_runner_command(self, ctx, base: int, *, player_name):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
if 0 > base > 3:
|
|
await ctx.send('from_base parameter must be 1, 2, or 3')
|
|
return
|
|
|
|
player_cog = self.bot.get_cog('Players')
|
|
player_name = await fuzzy_player_search(ctx, ctx.channel, self.bot, player_name, player_cog.player_list.keys())
|
|
player = await get_one_player(player_name)
|
|
this_member = get_one_member(
|
|
game_id=this_game['id'],
|
|
player_id=player['id'],
|
|
active=1
|
|
)
|
|
|
|
if base == 1:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_first_id=this_member['id']
|
|
)
|
|
elif base == 2:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_second_id=this_member['id']
|
|
)
|
|
else:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_third_id=this_member['id']
|
|
)
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@commands.command(name='clear', help='!clear <base_to_clear>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def clear_runner_command(self, ctx, base: int):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
if 0 > base > 3:
|
|
await ctx.send('from_base parameter must be 1, 2, or 3')
|
|
return
|
|
|
|
if base == 1:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_first_id=False
|
|
)
|
|
elif base == 2:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_second_id=False
|
|
)
|
|
else:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_third_id=False
|
|
)
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@commands.command(name='outs', help='!outs <modifier>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def outs_command(self, ctx, modifier: int):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
if -2 <= modifier <= 3:
|
|
outs = modifier
|
|
else:
|
|
await ctx.send('I can only change the outs from -2 to +3')
|
|
return
|
|
if outs == 0:
|
|
await ctx.message.add_reaction('👍')
|
|
return
|
|
|
|
if outs > 0:
|
|
self.increment_outs(this_game, outs)
|
|
else:
|
|
if this_state['outs'] + outs < 0:
|
|
new_outs = 0
|
|
else:
|
|
new_outs = this_state['outs'] + outs
|
|
patch_game_state(
|
|
this_state['id'],
|
|
outs=0
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@commands.command(name='score', help='!score <home_or_away> <modifier>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def score_command(self, ctx, home_or_away: str, modifier: int):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
home_score = this_state['home_score']
|
|
away_score = this_state['away_score']
|
|
|
|
if home_or_away.lower() == 'home':
|
|
new_home_score = home_score + modifier
|
|
new_away_score = away_score
|
|
elif home_or_away.lower() == 'away':
|
|
new_away_score = away_score + modifier
|
|
new_home_score = home_score
|
|
else:
|
|
await ctx.send('I only take \'home\' or \'away\'. Try again.')
|
|
return
|
|
|
|
new_state = patch_game_state(
|
|
this_state['id'],
|
|
home_score=new_home_score,
|
|
away_score=new_away_score
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@commands.group(name='show', help='`!help show` for all options')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def show_data(self, ctx):
|
|
if ctx.invoked_subcommand is None:
|
|
await ctx.send('No play details listed. Run `!help show` for available commands.')
|
|
|
|
@show_data.command(name='pitcher', help='Show pitcher card')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def show_pitcher_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
logger.info(f'gs: {gs}')
|
|
|
|
if gs['top_half']:
|
|
this_member = get_one_member(
|
|
game_id=this_game['id'],
|
|
team_id=this_game['home_team_id'],
|
|
batting_order=10,
|
|
active=1
|
|
)
|
|
else:
|
|
this_member = get_one_member(
|
|
game_id=this_game['id'],
|
|
team_id=this_game['away_team_id'],
|
|
batting_order=10,
|
|
active=1
|
|
)
|
|
|
|
this_player = await get_one_player(this_member['player_id'])
|
|
await ctx.send(content=None, embed=await get_player_embed(this_player, await db_get('current')))
|
|
|
|
@show_data.command(name='batter', help='Show batter card')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def show_batter_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
logger.info(f'gs: {gs}')
|
|
|
|
if gs['top_half']:
|
|
this_member = get_one_member(
|
|
game_id=this_game['id'],
|
|
team_id=this_game['away_team_id'],
|
|
batting_order=gs['away_batter_up'],
|
|
active=1
|
|
)
|
|
else:
|
|
this_member = get_one_member(
|
|
game_id=this_game['id'],
|
|
team_id=this_game['home_team_id'],
|
|
batting_order=gs['home_batter_up'],
|
|
active=1
|
|
)
|
|
|
|
this_player = await get_one_player(this_member['player_id'])
|
|
await ctx.send(content=None, embed=await get_player_embed(this_player, await db_get('current')))
|
|
|
|
@show_data.command(
|
|
name='position',
|
|
aliases=['c', '1b', '2b', '3b', 'ss', 'lf', 'cf', 'rf'],
|
|
help='!show 1b, !show rf, etc'
|
|
)
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def show_defense_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
pos = ctx.message.content.split(' ')[1].upper()
|
|
logger.info(f'pos: {pos}')
|
|
|
|
this_player = await get_one_player(self.get_defender(this_game, pos)['player_id'])
|
|
await ctx.send(content=None, embed=await get_player_embed(this_player, await db_get('current')))
|
|
|
|
@show_data.command(name='runner', help='!show runner <base_num>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def show_runner_command(self, ctx, base_num: int):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
if base_num == 1:
|
|
runner_id = gs['on_first']['player_id']
|
|
elif base_num == 2:
|
|
runner_id = gs['on_second']['player_id']
|
|
elif base_num == 3:
|
|
runner_id = gs['on_third']['player_id']
|
|
else:
|
|
await ctx.send('I can only check for runners on base 1, 2, or 3. Try again maybe?')
|
|
return
|
|
|
|
this_player = await get_one_player(runner_id)
|
|
await ctx.send(content=None, embed=await get_player_embed(this_player, await db_get('current')))
|
|
|
|
@commands.command(name='stats', aliases=['tocsv'], help='Get current stats')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def export_csv_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
async with ctx.typing():
|
|
results = await self.get_game_results(this_game, combined=True)
|
|
|
|
csv = DataFrame(results['batters']).to_csv(header=False, index=False)
|
|
file_name = f'storage/csv/{ctx.author.display_name}{random.randint(1000000000,9999999999)}.csv'
|
|
file = open(file_name, 'w')
|
|
file.write(csv)
|
|
file.close()
|
|
|
|
await ctx.send(content='Here is your batter csv:', file=discord.File(file_name))
|
|
|
|
csv = DataFrame(results['pitchers']).to_csv(header=False, index=False)
|
|
file_name = f'storage/csv/{ctx.author.display_name}{random.randint(1000000000,9999999999)}.csv'
|
|
file = open(file_name, 'w')
|
|
file.write(csv)
|
|
file.close()
|
|
|
|
await ctx.send(content='Here is your pitcher csv:', file=discord.File(file_name))
|
|
|
|
@commands.command(name='tosheets', help='Get results Sheet')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def export_sheets_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
away_team = await get_one_team(this_game['away_team_id'])
|
|
home_team = await get_one_team(this_game['home_team_id'])
|
|
|
|
results = await self.get_game_results(this_game)
|
|
await ctx.send('You got it - just pulled stats and posting them to a spreadsheet for ya.')
|
|
|
|
async with ctx.typing():
|
|
sheet_id = await self.get_game_sheet(home_team, away_team, results, ctx.author.display_name)
|
|
|
|
await ctx.send(f'Here is the results sheet for this game: https://docs.google.com/spreadsheets/d/'
|
|
f'{sheet_id}')
|
|
|
|
@commands.command(name='pull', hidden=True)
|
|
@commands.is_owner()
|
|
async def pull_command(self, ctx, player_type):
|
|
if player_type.lower() not in ['pitchers', 'batters']:
|
|
await ctx.send('Can only import pitcher or batter')
|
|
return
|
|
|
|
# Get data from Sheets
|
|
async with ctx.typing():
|
|
sheets = pygsheets.authorize(service_file='storage/major-domo-service-creds.json')
|
|
if player_type == 'pitchers':
|
|
data_sheet = sheets.open_by_key('1tAKj1PXTJbVyEXzB8YPcGOdydS24N12qoT6n7bSSk_w').worksheet_by_title(
|
|
'2020 Pitchers')
|
|
raw_data = data_sheet.get_values('A3', 'AP547')
|
|
else:
|
|
data_sheet = sheets.open_by_key('1tAKj1PXTJbVyEXzB8YPcGOdydS24N12qoT6n7bSSk_w').worksheet_by_title(
|
|
'2020 Batters')
|
|
raw_data = data_sheet.get_values('A3', 'AZ509')
|
|
|
|
await ctx.send('Alrighty, got the import data. Now to sift through...')
|
|
|
|
all_data = []
|
|
async with ctx.typing():
|
|
if player_type == 'pitchers':
|
|
for line in raw_data:
|
|
this_player = await get_one_player(line[0])
|
|
this_line = {
|
|
'player_id': this_player['id'],
|
|
'name': line[0],
|
|
'hand': line[2],
|
|
'innings': line[3],
|
|
'so_vl': line[4],
|
|
'so_vr': line[5],
|
|
'bb_vl': line[6],
|
|
'bb_vr': line[7],
|
|
'hit_vl': line[8],
|
|
'hit_vr': line[9],
|
|
'ob_vl': line[10],
|
|
'ob_vr': line[11],
|
|
'tb_vl': line[12],
|
|
'tb_vr': line[13],
|
|
'tb123_vl': line[16],
|
|
'tb123_vr': line[17],
|
|
'hit123_vl': line[14],
|
|
'hit123_vr': line[15],
|
|
'hr_vl': line[18],
|
|
'hr_vr': line[19],
|
|
'bphr_vl': line[22],
|
|
'bphr_vr': line[23],
|
|
'bp1b_vl': line[24],
|
|
'bp1b_vr': line[25],
|
|
'dp_vl': line[26],
|
|
'dp_vr': line[27],
|
|
'hold': line[29],
|
|
'st_rating': line[30] if line[30] != '' else None,
|
|
're_rating': line[31] if line[31] != '' else None,
|
|
'cl_rating': line[32] if line[32] != '' else None,
|
|
'range': line[33],
|
|
'error': line[34],
|
|
'balk': line[35],
|
|
'wild_pitch': line[36],
|
|
'speed': line[39],
|
|
'bunt': line[40],
|
|
'injury': line[41],
|
|
}
|
|
all_data.append(this_line)
|
|
|
|
posts = post_pitchers(all_data)
|
|
else:
|
|
for line in raw_data:
|
|
this_player = await get_one_player(line[0])
|
|
this_line = {
|
|
'player_id': this_player['id'],
|
|
'name': line[0],
|
|
'hand': line[3],
|
|
'atbats': line[2],
|
|
'so_vl': line[4],
|
|
'so_vr': line[5],
|
|
'bb_vl': line[6],
|
|
'bb_vr': line[7],
|
|
'hit_vl': line[8],
|
|
'hit_vr': line[9],
|
|
'ob_vl': line[10],
|
|
'ob_vr': line[11],
|
|
'tb_vl': line[12],
|
|
'tb_vr': line[13],
|
|
'hr_vl': line[14],
|
|
'hr_vr': line[15],
|
|
'bphr_vl': line[18],
|
|
'bphr_vr': line[19],
|
|
'bp1b_vl': line[20],
|
|
'bp1b_vr': line[21],
|
|
'dp_vl': line[24],
|
|
'dp_vr': line[25],
|
|
'steal': line[27],
|
|
'speed': line[28],
|
|
'bunt': line[29],
|
|
'hit_and_run': line[30],
|
|
'injury': line[31],
|
|
'rating_c': line[32] if line[32] != '' else None,
|
|
'rating_1b': line[33] if line[33] != '' else None,
|
|
'rating_2b': line[34] if line[34] != '' else None,
|
|
'rating_3b': line[35] if line[35] != '' else None,
|
|
'rating_ss': line[36] if line[36] != '' else None,
|
|
'rating_lf': line[37] if line[37] != '' else None,
|
|
'rating_cf': line[38] if line[38] != '' else None,
|
|
'rating_rf': line[39] if line[39] != '' else None,
|
|
'error_c': line[40] if line[40] != '' else None,
|
|
'error_1b': line[41] if line[41] != '' else None,
|
|
'error_2b': line[42] if line[42] != '' else None,
|
|
'error_3b': line[43] if line[43] != '' else None,
|
|
'error_ss': line[44] if line[44] != '' else None,
|
|
'error_lf': line[45] if line[45] != '' else None,
|
|
'error_cf': line[46] if line[46] != '' else None,
|
|
'error_rf': line[47] if line[47] != '' else None,
|
|
'arm_c': line[49] if line[49] != '' else None,
|
|
'arm_of': line[48] if line[48] != '' else None,
|
|
'overthrow': line[50] if line[50] != '' else None,
|
|
'passed_ball': line[51] if line[51] != '' else None,
|
|
}
|
|
all_data.append(this_line)
|
|
posts = post_batters(all_data)
|
|
|
|
await ctx.send(f'Posted {posts} {player_type.lower()}!')
|
|
|
|
@commands.command(name='vs', help='!vs <Pitcher Name> vs <Batter Name>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def versus_command(self, ctx, *, args):
|
|
# Split names out
|
|
names = re.split(' vs ', args)
|
|
if len(names) == 1:
|
|
names = re.split(' vs. ', args)
|
|
if len(names) == 1:
|
|
await ctx.send('I could not find player names in that message. Make sure the format is '
|
|
'`!vs Gerrit Cole vs Salvador Perez')
|
|
return
|
|
|
|
# Perform fuzzy search to get player objects
|
|
player_cog = self.bot.get_cog('Players')
|
|
player_name = await fuzzy_player_search(ctx, ctx.channel, self.bot, names[0], player_cog.player_list.keys())
|
|
player1 = await get_one_player(player_name)
|
|
player_name = await fuzzy_player_search(ctx, ctx.channel, self.bot, names[1], player_cog.player_list.keys())
|
|
player2 = await get_one_player(player_name)
|
|
pitcher_player = None
|
|
batter_player = None
|
|
pitcher_pos = ['SP', 'RP', 'CP']
|
|
|
|
if player1['pos_1'] in pitcher_pos:
|
|
pitcher_player = player1
|
|
if player2['pos_1'] not in pitcher_pos:
|
|
batter_player = player2
|
|
else:
|
|
await ctx.send(f'I could not find batting info for {player2["name"]}.')
|
|
return
|
|
else:
|
|
pitcher_player = player2
|
|
if player1['pos_1'] not in pitcher_pos:
|
|
batter_player = player1
|
|
else:
|
|
await ctx.send(f'I could not find batting info for {player1["name"]}.')
|
|
return
|
|
|
|
# Get Pitcher and Batter objects
|
|
pitcher = get_one_pitcher(pitcher_player['id'])
|
|
batter = get_one_batter(batter_player['id'])
|
|
|
|
da_roll = random.randint(1, 1000)
|
|
embed = get_team_embed(f'{pitcher_player["name"]} vs {batter_player["name"]}', thumbnail=False)
|
|
|
|
# Calculate matchup OBP
|
|
batter_ob = batter['ob_vr'] if pitcher['hand'] == 'R' else batter['ob_vl']
|
|
if batter['hand'] == 'L':
|
|
pitcher_ob = pitcher['ob_vl']
|
|
elif batter['hand'] == 'R':
|
|
pitcher_ob = pitcher['ob_vr']
|
|
else:
|
|
pitcher_ob = pitcher['ob_vr'] if pitcher['hand'] == 'L' else pitcher['ob_vl']
|
|
obp = ((batter_ob + pitcher_ob) / 216)
|
|
|
|
# Calculate matchup AVG
|
|
batter_hit = batter['hit_vr'] if pitcher['hand'] == 'R' else batter['hit_vl']
|
|
batter_hr = batter['hr_vr'] if pitcher['hand'] == 'R' else batter['hr_vl']
|
|
batter_bb = batter['bb_vr'] if pitcher['hand'] == 'R' else batter['bb_vl']
|
|
batter_so = batter['so_vr'] if pitcher['hand'] == 'R' else batter['so_vl']
|
|
if batter['hand'] == 'L':
|
|
pitcher_hit = pitcher['hit_vl']
|
|
pitcher_hr = pitcher['hr_vl']
|
|
pitcher_bb = pitcher['bb_vl']
|
|
pitcher_so = pitcher['so_vl']
|
|
elif batter['hand'] == 'R':
|
|
pitcher_hit = pitcher['hit_vr']
|
|
pitcher_hr = pitcher['hr_vr']
|
|
pitcher_bb = pitcher['bb_vr']
|
|
pitcher_so = pitcher['so_vr']
|
|
else:
|
|
pitcher_hit = pitcher['hit_vr'] if pitcher['hand'] == 'L' else pitcher['hit_vl']
|
|
pitcher_hr = pitcher['hr_vr'] if pitcher['hand'] == 'L' else pitcher['hr_vl']
|
|
pitcher_bb = pitcher['bb_vr'] if pitcher['hand'] == 'L' else pitcher['bb_vl']
|
|
pitcher_so = pitcher['so_vr'] if pitcher['hand'] == 'L' else pitcher['so_vl']
|
|
avg = ((batter_hit + pitcher_hit) / (216 - batter_ob - pitcher_ob + batter_hit + pitcher_hit))
|
|
|
|
# Calculate matchup SLG
|
|
batter_tb = batter['tb_vr'] if pitcher['hand'] == 'R' else batter['tb_vl']
|
|
if batter['hand'] == 'L':
|
|
pitcher_tb = pitcher['tb_vl']
|
|
elif batter['hand'] == 'R':
|
|
pitcher_tb = pitcher['tb_vr']
|
|
else:
|
|
pitcher_tb = pitcher['tb_vr'] if pitcher['hand'] == 'L' else pitcher['tb_vl']
|
|
slg = ((batter_tb + pitcher_tb) / (216 - batter_ob - pitcher_ob + batter_hit + pitcher_hit))
|
|
|
|
embed.add_field(name='Average', value=f'{avg:.3f}')
|
|
embed.add_field(name='On Base', value=f'{obp:.3f}')
|
|
embed.add_field(name='Slugging', value=f'{slg:.3f}')
|
|
|
|
# Check for hit
|
|
if da_roll <= round(avg * 1000):
|
|
hit_roll = random.randint(1, round((batter_hit + pitcher_hit) * 1000))
|
|
if hit_roll <= round((batter_hr + pitcher_hr) * 1000):
|
|
result = 'Home Run!'
|
|
else:
|
|
tb123 = batter_tb + pitcher_tb - ((batter_hr + pitcher_hr) * 4)
|
|
si_chance = tb123 / (batter_hit + pitcher_hit)
|
|
if random.random() <= si_chance:
|
|
result = 'Single!'
|
|
else:
|
|
result = 'Double!'
|
|
|
|
# Check for on base
|
|
elif da_roll <= round(obp * 1000):
|
|
bb_chance = (batter_bb + pitcher_bb) / (batter_ob + pitcher_ob - batter_hit - pitcher_hit)
|
|
logger.info(f'bb_chance: {bb_chance}')
|
|
if random.random() <= bb_chance:
|
|
result = 'Walk!'
|
|
else:
|
|
result = 'HBP!'
|
|
|
|
# Check for out
|
|
else:
|
|
out_chances = 216 - round(216 * obp)
|
|
out_roll = random.randint(1, out_chances)
|
|
|
|
if out_roll <= batter_so + pitcher_so:
|
|
result = 'Strikeout!'
|
|
elif out_roll <= out_chances - ((out_chances - (batter_so + pitcher_so)) / 2):
|
|
result = 'Groundout!'
|
|
else:
|
|
result = 'Flyout!'
|
|
|
|
embed.add_field(name='Result', value=result)
|
|
|
|
logger.info(f'{pitcher_player["name"]} vs {batter_player["name"]}\n'
|
|
f'batter OB: {batter_ob} / pitcher OB: {pitcher_ob}\n'
|
|
f'batter BB: {batter_bb} / pitcher BB: {pitcher_bb}\n'
|
|
f'batter hit: {batter_hit} / pitcher hit: {pitcher_hit}\n'
|
|
f'batter hr: {batter_hr} / pitcher hr: {pitcher_hr}')
|
|
await ctx.send(content=None, embed=embed)
|
|
|
|
@commands.command(name='tutorial', help='Quick how-to guide')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def tutorial_command(self, ctx):
|
|
embed = get_team_embed(f'Discord Scorebot Tutorial')
|
|
embed.add_field(
|
|
name='Start & End Game',
|
|
value=f'The `!startgame` command will initiate a game in the channel it is run. Other commands will only '
|
|
f'be accepted in this channel. The `!endgame` command will ask for confirmation and then send game '
|
|
f'stats to a google sheet (just like running `!tosheets`) before closing out the game.'
|
|
)
|
|
embed.add_field(
|
|
name='Set Lineups',
|
|
value=f'The `!setlineups` command or `!sl` is used to set the initial lineups as well as make mid-game '
|
|
f'substitutions. The format is as follows:\n```!sl <team_abbrev> <lineup_order>-<player_name>-'
|
|
f'<position>```\nIf there are multiple subs (or this is the initial lineup), enter a comma after the '
|
|
f'position and, without any spaces, begin the next player. Below are two examples:\n```!sl WV 1-'
|
|
f'Ronald Acuna Jr-RF```and```!sl WV 2-Trea Turner-SS,4-Eloy Jimenez-RF,7-Cody Bellinger-CF,10-'
|
|
f'Anthony Bass-P```\nNote on pitchers: if the pitcher is not batting, they are entered with 10 for '
|
|
f'their lineup_order; if they enter the game to bat, they are then entered in their batting order.',
|
|
inline=False
|
|
)
|
|
embed.add_field(
|
|
name='Logging Plays',
|
|
value=f'Plays are logged with the `!log <play_details>` command. You can run `!help log` to see a full '
|
|
f'list. For help with a specific play log, run `!help log x` for help logging an x-chance.',
|
|
inline=False
|
|
)
|
|
embed.add_field(
|
|
name='Game State Modifications',
|
|
value=f'The score, the number of ours, and the placement of baserunners can all be modified manually in '
|
|
f'cases of either bad automation (please let Cal know) or a mistake was made.\nThe `!advance` '
|
|
f'command manually advanced baserunners; the `!clear` command removes a baserunner; the `!outs` '
|
|
f'command can add or subtract outs (never past 0 or 3); the `!place` command manually places '
|
|
f'baserunners; the `!score` command adjusts either the home or away team\'s score.',
|
|
inline=False
|
|
)
|
|
embed.add_field(
|
|
name='Results',
|
|
value=f'The `!tosheets` command will compile the game\'s stats and upload them into a google sheets page '
|
|
f'formatted for the !submit command. The `!stats` command will display the game\'s stats in a simple '
|
|
f'csv format and post them to discord. Both of these commands can be run at any time __before__ the '
|
|
f'`!endgame` command is run.',
|
|
inline=False
|
|
)
|
|
await ctx.send(content=None, embed=embed)
|
|
|
|
|
|
async def setup(bot):
|
|
await bot.add_cog(Gameday(bot))
|
|
|