1150 lines
52 KiB
Python
1150 lines
52 KiB
Python
import datetime
|
|
import math
|
|
|
|
import pygsheets
|
|
|
|
from db_calls import db_get, get_player_headshot, get_team_by_abbrev, get_team_by_owner
|
|
# from db_calls import *
|
|
|
|
import asyncio
|
|
import logging
|
|
import os
|
|
import random
|
|
import json
|
|
|
|
import discord
|
|
import requests
|
|
|
|
from discord.ext import commands
|
|
from difflib import get_close_matches
|
|
|
|
|
|
SBA_SEASON = 8
|
|
PD_SEASON = 4
|
|
SBA_COLOR = 'a6ce39'
|
|
|
|
SBA_ROSTER_KEY = '1bt7LLJe6h7axkhDVlxJ4f319l8QmFB0zQH-pjM0c8a8'
|
|
SBA_STATS_KEY = '1fnqx2uxC7DT5aTnx4EkXh83crwrL0W6eJefoC1d4KH4'
|
|
SBA_STANDINGS_KEY = '1cXZcPY08RvqV_GeLvZ7PY5-0CyM-AijpJxsaFisZjBc'
|
|
SBA_ROSTER_URL = 'https://docs.google.com/spreadsheets/d/10fKx1vQ7tEjKx0OD5tnFADdjsUeFbqxMIshz2d5i-Is/edit'
|
|
SBA_BASE_URL = 'https://sba.manticorum.com'
|
|
SBA_SEASON4_DRAFT_KEY = '1lztxahGRypykfWGKd2duDGFH0omclLqFLSt-vwqdSu8'
|
|
SBA_SEASON5_DRAFT_KEY = '1euuKeWqQEUmE9OiF9wihO5LMERWP3Zwg_KsG2w-Kx54'
|
|
SBA_SEASON6_DRAFT_KEY = '13_xWG1wQy7G4UJvohD8JIUBE-7yuWT9lVta1rkAlHQE'
|
|
SBA_SEASON7_DRAFT_KEY = '1BgySsUlQf9K21_uOjQOY7O0GrRfF6zt1BBaEFlvBokY'
|
|
SBA_SEASON8_DRAFT_KEY = '1FG4cAs8OeTdrreRqu8D-APxibjB3RiEzn34KTTBLLDk'
|
|
SBA_STANDINGS_URL = f'{SBA_BASE_URL}/standings'
|
|
SBA_SCHEDULE_URL = f'{SBA_BASE_URL}/schedule'
|
|
SBA_IMAGE_URL = f'{SBA_BASE_URL}/static/images'
|
|
SBA_PLAYERS_ROLE_NAME = f'Season {SBA_SEASON} Players'
|
|
PD_PLAYERS_ROLE_NAME = f'Paper Dynasty Players'
|
|
ALL_PLAYERS = [SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME]
|
|
|
|
LOGO = 'https://sombaseball.ddns.net/static/images/sba-logo.png'
|
|
INFIELD_X_CHART = {
|
|
'si1': {
|
|
'rp': 'Runner on first: Line drive hits the runner! Runner on first is out. Batter goes to first with single '
|
|
'and all other runners hold.\nNo runner on first: batter singles, runners advance 1 base.',
|
|
'e1': 'Single and Error, batter to second, runners advance 2 bases.',
|
|
'e2': 'Single and Error, batter to third, all runners score.',
|
|
'no': 'Single, runners advance 1 base.'
|
|
},
|
|
'spd': {
|
|
'rp': 'No effect; proceed with speed check',
|
|
'e1': 'Single and Error, batter to second, runners advance 2 bases.',
|
|
'e2': 'Single and Error, batter to third, all runners score.',
|
|
'no': 'Speed check, safe range equals batter\'s running rating, SI* result if safe, gb C if out'
|
|
},
|
|
'po': {
|
|
'rp': 'The batters hits a popup. None of the fielders take charge on the play and the ball drops in the '
|
|
'infield for a single! All runners advance 1 base.',
|
|
'e1': 'The catcher drops a popup for an error. All runners advance 1 base.',
|
|
'e2': 'The catcher grabs a squib in front of the plate and throws it into right field. The batter goes to '
|
|
'second and all runners score.',
|
|
'no': 'The batter pops out to the catcher.'
|
|
},
|
|
'wp': {
|
|
'rp': 'Automatic wild pitch. Catcher has trouble finding it and all base runners advance 2 bases.',
|
|
'no': 'Automatic wild pitch, all runners advance 1 base and batter rolls AB again.'
|
|
},
|
|
'x': {
|
|
'rp': 'Runner(s) on base: pitcher trips during his delivery and the ball sails for automatic wild pitch, '
|
|
'runners advance 1 base and batter rolls AB again.',
|
|
'no': 'Wild pitch check (credited as a PB). If a passed ball occurs, batter rerolls AB. '
|
|
'If no passed ball occurs, the batter fouls out to the catcher.'
|
|
},
|
|
'fo': {
|
|
'rp': 'Batter swings and misses, but is awarded first base on a catcher interference call! Baserunners advance '
|
|
'only if forced.',
|
|
'e1': 'The catcher drops a foul popup for an error. Batter rolls AB again.',
|
|
'e2': 'The catcher drops a foul popup for an error. Batter rolls AB again.',
|
|
'no': 'Runner(s) on base: make a passed ball check. If no passed ball, batter pops out to the catcher. If a '
|
|
'passed ball occurs, batter roll his AB again.\nNo runners: batter pops out to the catcher'
|
|
},
|
|
'g1': {
|
|
'rp': 'Runner on first: runner on first breaks up the double play, but umpires call runner interference and '
|
|
'the batter is out on GIDP.\nNo runners: Batter grounds out.',
|
|
'e1': 'Error, batter to first, runners advance 1 base.',
|
|
'e2': 'Error, batter to second, runners advance 2 bases.',
|
|
'no': 'Consult Groundball Chart: `!gbA`'
|
|
},
|
|
'g2': {
|
|
'rp': 'Batter lines the ball off the pitcher to the fielder who makes the play to first for the out! Runners '
|
|
'advance only if forced.',
|
|
'e1': 'Error, batter to first, runners advance 1 base.',
|
|
'e2': 'Error, batter to second, runners advance 2 bases.',
|
|
'no': 'Consult Groundball Chart: `!gbB`'
|
|
},
|
|
'g3': {
|
|
'rp': 'Batter lines the ball off the mound and deflects to the fielder who makes the play to first for the '
|
|
'out! Runners advance 1 base.',
|
|
'e1': 'Error, batter to first, runners advance 1 base.',
|
|
'e2': 'Error, batter to second, runners advance 2 bases.',
|
|
'no': 'Consult Groundball Chart: `!gbC`'
|
|
},
|
|
}
|
|
OUTFIELD_X_CHART = {
|
|
'si2': {
|
|
'rp': 'Batter singles, baserunners advance 2 bases. As the batter rounds first, the fielder throws behind him '
|
|
'and catches him off the bag for an out!',
|
|
'e1': 'Single and error, batter to second, runners advance 2 bases.',
|
|
'e2': 'Single and error, batter to third, all runners score.',
|
|
'e3': 'Single and error, batter to third, all runners score',
|
|
'no': 'Single, all runners advance 2 bases.'
|
|
},
|
|
'do2': {
|
|
'rp': 'Batter doubles, runners advance 2 bases. The outfielder throws the ball to the shortstop who executes a '
|
|
'hidden ball trick! Runner on second is called out!',
|
|
'e1': 'Double and error, batter to third, all runners score.',
|
|
'e2': 'Double and error, batter to third, and all runners score.',
|
|
'e3': 'Double and error, batter and all runners score. Little league home run!',
|
|
'no': 'Double, all runners advance 2 bases.'
|
|
},
|
|
'do3': {
|
|
'rp': 'Runner(s) on base: batter doubles and runners advance three bases as the outfielders collide!\n'
|
|
'No runners: Batter doubles, but the play is appealed. The umps rule the batter missed first base so is '
|
|
'out on the appeal!',
|
|
'e1': 'Double and error, batter to third, all runners score.',
|
|
'e2': 'Double and error, batter and all runners score. Little league home run!',
|
|
'e3': 'Double and error, batter and all runners score. Little league home run!',
|
|
'no': 'Double, all runners score.'
|
|
},
|
|
'tr3': {
|
|
'rp': 'Batter hits a ball into the gap and the outfielders collide trying to make the play! The ball rolls to '
|
|
'the wall and the batter trots home with an inside-the-park home run!',
|
|
'e1': 'Triple and error, batter and all runners score. Little league home run!',
|
|
'e2': 'Triple and error, batter and all runners score. Little league home run!',
|
|
'e3': 'Triple and error, batter and all runners score. Little league home run!',
|
|
'no': 'Triple, all runners score.'
|
|
},
|
|
'f1': {
|
|
'rp': 'The outfielder races back and makes a diving catch and collides with the wall! In the time he takes to '
|
|
'recuperate, all baserunners tag-up and advance 2 bases.',
|
|
'e1': '1 base error, runners advance 1 base.',
|
|
'e2': '2 base error, runners advance 2 bases.',
|
|
'e3': '3 base error, batter to third, all runners score.',
|
|
'no': 'Flyball A'
|
|
},
|
|
'f2': {
|
|
'rp': 'The outfielder catches the flyball for an out. If there is a runner on third, he tags-up and scores. '
|
|
'The play is appealed and the umps rule that the runner left early and is out on the appeal!',
|
|
'e1': '1 base error, runners advance 1 base.',
|
|
'e2': '2 base error, runners advance 2 bases.',
|
|
'e3': '3 base error, batter to third, all runners score.',
|
|
'no': 'Flyball B'
|
|
},
|
|
'f3': {
|
|
'rp': 'The outfielder makes a running catch in the gap! The lead runner lost track of the ball and was '
|
|
'advancing - he cannot return in time and is doubled off by the outfielder.',
|
|
'e1': '1 base error, runners advance 1 base.',
|
|
'e2': '2 base error, runners advance 2 bases.',
|
|
'e3': '3 base error, batter to third, all runners score.',
|
|
'no': 'Flyball C'
|
|
}
|
|
}
|
|
|
|
|
|
class Question:
|
|
def __init__(self, bot, channel, prompt, qtype, timeout, embed=None):
|
|
"""
|
|
Version 0.4
|
|
|
|
:param bot, discord bot object
|
|
:param channel, discord Channel object
|
|
:param prompt, string, prompt message to post
|
|
:param qtype, string, 'yesno', 'int', 'float', 'text', or 'url'
|
|
:param timeout, float, time to wait for a response
|
|
"""
|
|
if not prompt and not embed:
|
|
raise TypeError('prompt and embed may not both be None')
|
|
|
|
self.bot = bot
|
|
self.channel = channel
|
|
self.prompt = prompt
|
|
self.qtype = qtype
|
|
self.timeout = timeout
|
|
self.embed = embed
|
|
|
|
async def ask(self, responders: list):
|
|
"""
|
|
Params: responder, list of discord User objects
|
|
Returns: True/False if confirm question; full response otherwise
|
|
"""
|
|
yes = [
|
|
'yes', 'y', 'ye', 'yee', 'yerp', 'yep', 'yeet', 'yip', 'yup', 'yarp', 'si', 'fine', 'sure', 'k', 'ok',
|
|
'okay'
|
|
]
|
|
no = ['no', 'n', 'nope', 'nah', 'nyet', 'nein']
|
|
|
|
if type(responders) is not list:
|
|
raise TypeError('Responders must be a list of Members')
|
|
|
|
def yesno(mes):
|
|
return mes.channel == self.channel and mes.author in responders and mes.content.lower() in yes + no
|
|
|
|
def text(mes):
|
|
return mes.channel == self.channel and mes.author in responders
|
|
|
|
await self.channel.send(content=self.prompt, embed=self.embed)
|
|
|
|
try:
|
|
resp = await self.bot.wait_for(
|
|
'message',
|
|
timeout=self.timeout,
|
|
check=yesno if self.qtype == 'yesno' else text
|
|
)
|
|
except asyncio.TimeoutError:
|
|
return None
|
|
except Exception as e:
|
|
await self.channel.send(f'Yuck, do you know what this means?\n\n{e}')
|
|
return None
|
|
|
|
if self.qtype == 'yesno':
|
|
if resp.content.lower() in yes:
|
|
return True
|
|
else:
|
|
return False
|
|
elif self.qtype == 'int':
|
|
return int(resp.content)
|
|
elif self.qtype == 'float':
|
|
return float(resp.content)
|
|
elif self.qtype == 'url':
|
|
if resp.content[:7] == 'http://' or resp.content[:8] == 'https://':
|
|
return resp.content
|
|
else:
|
|
return False
|
|
else:
|
|
return resp.content
|
|
|
|
|
|
# Define a simple View that gives us a confirmation menu
|
|
class Confirm(discord.ui.View):
|
|
def __init__(self, responders: list, timeout: float = 300.0):
|
|
super().__init__(timeout=timeout)
|
|
if not isinstance(responders, list):
|
|
raise TypeError('responders must be a list')
|
|
self.value = None
|
|
self.responders = responders
|
|
|
|
# When the confirm button is pressed, set the inner value to `True` and
|
|
# stop the View from listening to more input.
|
|
# We also send the user an ephemeral message that we're confirming their choice.
|
|
@discord.ui.button(label='Confirm', style=discord.ButtonStyle.green)
|
|
async def confirm(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
if interaction.user not in self.responders:
|
|
return
|
|
|
|
# await interaction.response.send_message('Confirmed', ephemeral=True)
|
|
self.value = True
|
|
self.clear_items()
|
|
self.stop()
|
|
|
|
# This one is similar to the confirmation button except sets the inner value to `False`
|
|
@discord.ui.button(label='Cancel', style=discord.ButtonStyle.grey)
|
|
async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
if interaction.user not in self.responders:
|
|
return
|
|
|
|
# await interaction.response.send_message('Cancelled', ephemeral=True)
|
|
self.value = False
|
|
self.clear_items()
|
|
self.stop()
|
|
|
|
|
|
class ButtonOptions(discord.ui.View):
|
|
def __init__(self, responders: list, timeout: float = 300.0, labels=None):
|
|
super().__init__(timeout=timeout)
|
|
if not isinstance(responders, list):
|
|
raise TypeError('responders must be a list')
|
|
self.value = None
|
|
self.responders = responders
|
|
self.options = labels
|
|
for count, x in enumerate(labels):
|
|
if count == 0:
|
|
self.option1.label = x
|
|
if x is None or x.lower() == 'na' or x == 'N/A':
|
|
self.remove_item(self.option1)
|
|
if count == 1:
|
|
self.option2.label = x
|
|
if x is None or x.lower() == 'na' or x == 'N/A':
|
|
self.remove_item(self.option2)
|
|
if count == 2:
|
|
self.option3.label = x
|
|
if x is None or x.lower() == 'na' or x == 'N/A':
|
|
self.remove_item(self.option3)
|
|
if count == 3:
|
|
self.option4.label = x
|
|
if x is None or x.lower() == 'na' or x == 'N/A':
|
|
self.remove_item(self.option4)
|
|
if count == 4:
|
|
self.option5.label = x
|
|
if x is None or x.lower() == 'na' or x == 'N/A':
|
|
self.remove_item(self.option5)
|
|
|
|
@discord.ui.button(label='Option 1', style=discord.ButtonStyle.primary)
|
|
async def option1(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
if interaction.user not in self.responders:
|
|
return
|
|
|
|
self.value = self.options[0]
|
|
self.clear_items()
|
|
self.stop()
|
|
|
|
@discord.ui.button(label='Option 2', style=discord.ButtonStyle.primary)
|
|
async def option2(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
if interaction.user not in self.responders:
|
|
return
|
|
|
|
self.value = self.options[1]
|
|
self.clear_items()
|
|
self.stop()
|
|
|
|
@discord.ui.button(label='Option 3', style=discord.ButtonStyle.primary)
|
|
async def option3(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
if interaction.user not in self.responders:
|
|
return
|
|
|
|
self.value = self.options[2]
|
|
self.clear_items()
|
|
self.stop()
|
|
|
|
@discord.ui.button(label='Option 4', style=discord.ButtonStyle.primary)
|
|
async def option4(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
if interaction.user not in self.responders:
|
|
return
|
|
|
|
self.value = self.options[3]
|
|
self.clear_items()
|
|
self.stop()
|
|
|
|
@discord.ui.button(label='Option 5', style=discord.ButtonStyle.primary)
|
|
async def option5(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
if interaction.user not in self.responders:
|
|
return
|
|
|
|
self.value = self.options[4]
|
|
self.clear_items()
|
|
self.stop()
|
|
|
|
|
|
class Pagination(discord.ui.View):
|
|
def __init__(self, responders: list, timeout: float = 300.0):
|
|
super().__init__(timeout=timeout)
|
|
if not isinstance(responders, list):
|
|
raise TypeError('responders must be a list')
|
|
|
|
self.value = None
|
|
self.responders = responders
|
|
|
|
@discord.ui.button(label='⏮️', style=discord.ButtonStyle.blurple)
|
|
async def left_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
if interaction.user not in self.responders:
|
|
logging.info(f'{interaction.user} is not in {self.responders}')
|
|
return
|
|
|
|
self.value = 'left'
|
|
await interaction.response.defer()
|
|
self.stop()
|
|
|
|
@discord.ui.button(label='❌️', style=discord.ButtonStyle.secondary)
|
|
async def cancel_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
if interaction.user not in self.responders:
|
|
logging.info(f'{interaction.user} is not in {self.responders}')
|
|
return
|
|
|
|
self.value = 'cancel'
|
|
await interaction.response.defer()
|
|
self.stop()
|
|
|
|
@discord.ui.button(label='⏭️', style=discord.ButtonStyle.blurple)
|
|
async def right_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
if interaction.user not in self.responders:
|
|
logging.info(f'{interaction.user} is not in {self.responders}')
|
|
return
|
|
|
|
self.value = 'right'
|
|
await interaction.response.defer()
|
|
self.stop()
|
|
|
|
|
|
def get_xchart_data(code: str):
|
|
result = ''
|
|
if code in INFIELD_X_CHART.keys():
|
|
for key in INFIELD_X_CHART[code]:
|
|
result += f'**{key.upper()}**:\n{INFIELD_X_CHART[code][key]}\n\n'
|
|
return result
|
|
elif code in OUTFIELD_X_CHART.keys():
|
|
for key in OUTFIELD_X_CHART[code]:
|
|
result += f'**{key.upper()}**:\n{OUTFIELD_X_CHART[code][key]}\n\n'
|
|
return result
|
|
|
|
return None
|
|
|
|
|
|
def get_groundball_embeds(this_command):
|
|
url_base = 'https://sombaseball.ddns.net/static/images/season04/ground-ball-chart'
|
|
|
|
if this_command in ['g1', 'g2', 'g3']:
|
|
embed = discord.Embed(title=f'Groundball {this_command.upper()} Chart')
|
|
embed.set_image(url=f'{url_base}-{this_command}.png')
|
|
|
|
return [embed]
|
|
else:
|
|
embed1 = discord.Embed(title='Groundball Chart 1/2')
|
|
embed1.set_image(url=f'{url_base}01.png')
|
|
embed2 = discord.Embed(title='Groundball Chart 2/2')
|
|
embed2.set_image(url=f'{url_base}02.png')
|
|
|
|
return [embed1, embed2]
|
|
|
|
|
|
def old_rand_conf_gif():
|
|
conf_gifs = [
|
|
'https://tenor.com/view/boom-annakendrick-pitchperfect-pitchperfect2-micdrop-gif-5143507',
|
|
'https://tenor.com/view/boom-annakendrick-pitchperfect-pitchperfect2-micdrop-gif-5143507',
|
|
'https://tenor.com/view/boom-annakendrick-pitchperfect-pitchperfect2-micdrop-gif-5143507',
|
|
'https://tenor.com/view/explosion-boom-iron-man-gif-14282225',
|
|
'https://tenor.com/view/betty-white-dab-consider-it-done-gif-11972415',
|
|
'https://tenor.com/view/done-and-done-spongebob-finished-just-did-it-gif-10843280',
|
|
'https://tenor.com/view/thumbs-up-okay-ok-well-done-gif-13840394',
|
|
'https://tenor.com/view/tinkerbell-peter-pan-all-done-gif-15003723',
|
|
'https://tenor.com/view/done-and-done-ron-swanson-gotchu-gif-10843254',
|
|
'https://tenor.com/view/sponge-bob-thumbs-up-ok-smile-gif-12038157',
|
|
'https://tenor.com/view/thumbs-up-cool-okay-bye-gif-8633196',
|
|
'https://media.giphy.com/media/zCME2Cd20Czvy/giphy.gif',
|
|
'https://media.giphy.com/media/xT0xeJpckCr0qGAFna/giphy.gif',
|
|
'https://media0.giphy.com/media/l0MYw3oeYCUJhj5FC/200.gif',
|
|
'https://media2.giphy.com/media/52FcaTVc9Y1rk7q1NQ/200.gif',
|
|
'https://i0.wp.com/media1.giphy.com/media/iwvuPyfi7z14I/giphy.gif',
|
|
'https://media1.tenor.com/images/859a2d3b201fbacec13904242976b9e0/tenor.gif',
|
|
'https://media.giphy.com/media/3o6ZsZbUukGiYMf2a4/giphy.gif',
|
|
'https://media.giphy.com/media/l0Iyl55kTeh71nTXy/giphy.gif',
|
|
'https://media.giphy.com/media/3o7qDEq2bMbcbPRQ2c/giphy.gif',
|
|
'https://media.giphy.com/media/3orieTbyzN9QNCDANa/giphy.gif',
|
|
'https://media.giphy.com/media/l3V0JGUuz4xght7Py/giphy.gif',
|
|
'https://media.giphy.com/media/o1H5mqZKB0RCE/giphy.gif',
|
|
'https://media.giphy.com/media/Rhf0uSWt1P2TFqVMZK/giphy.gif',
|
|
'https://media.giphy.com/media/UIMAFTRUcf6V71BGsd/giphy.gif',
|
|
'https://media.giphy.com/media/xUOwFYnEb6rv4Oxx6M/giphy.gif',
|
|
'https://media.giphy.com/media/6bdiLSKOwgR6ekaTSS/giphy.gif',
|
|
'https://tenor.com/bc1OJ.gif',
|
|
'https://tenor.com/1EmF.gif',
|
|
'https://tenor.com/ZYCh.gif',
|
|
'https://tenor.com/patd.gif',
|
|
'https://tenor.com/u6mU.gif',
|
|
'https://tenor.com/x2sa.gif',
|
|
'https://tenor.com/bAVeS.gif',
|
|
'https://tenor.com/bxOcj.gif',
|
|
'https://tenor.com/ETJ7.gif',
|
|
'https://tenor.com/bpH3g.gif',
|
|
'https://tenor.com/biF9q.gif',
|
|
'https://tenor.com/OySS.gif',
|
|
'https://tenor.com/bvVFv.gif',
|
|
'https://tenor.com/bFeqA.gif'
|
|
]
|
|
return conf_gifs[random.randint(0, len(conf_gifs) - 1)]
|
|
|
|
|
|
def random_conf_gif():
|
|
req_url = 'https://api.giphy.com/v1/gifs/translate?s=all done&api_key=H86xibttEuUcslgmMM6uu74IgLEZ7UOD'
|
|
|
|
resp = requests.get(req_url, timeout=3)
|
|
if resp.status_code == 200:
|
|
data = resp.json()
|
|
if 'trump' in data['data']['title']:
|
|
return old_rand_conf_gif()
|
|
else:
|
|
return data['data']['url']
|
|
else:
|
|
logging.warning(resp.text)
|
|
raise ValueError(f'DB: {resp.text}')
|
|
|
|
|
|
def random_salute_gif():
|
|
salute_gifs = [
|
|
'https://media.giphy.com/media/fSAyceY3BCgtiQGnJs/giphy.gif',
|
|
'https://media.giphy.com/media/bsWDUSFUmJCOk/giphy.gif',
|
|
'https://media.giphy.com/media/hStvd5LiWCFzYNyxR4/giphy.gif',
|
|
'https://media.giphy.com/media/RhSR5xXDsXJ7jbnrRW/giphy.gif',
|
|
'https://media.giphy.com/media/lNQvrlPdbmZUU2wlh9/giphy.gif',
|
|
'https://gfycat.com/skeletaldependableandeancat',
|
|
'https://i.gifer.com/5EJk.gif',
|
|
'https://tenor.com/baJUV.gif',
|
|
'https://tenor.com/bdnQH.gif',
|
|
'https://tenor.com/bikQU.gif',
|
|
'https://i.pinimg.com/originals/04/36/bf/0436bfc9861b4b57ffffda82d3adad6e.gif',
|
|
'https://media.giphy.com/media/6RtOG4Q7v34kw/giphy.gif',
|
|
'https://keyassets-p2.timeincuk.net/wp/prod/wp-content/uploads/sites/42/2017/04/anigif_'
|
|
'enhanced-946-1433453114-7.gif',
|
|
'https://keyassets-p2.timeincuk.net/wp/prod/wp-content/uploads/sites/42/2017/04/100c5d677cc28ea3f15'
|
|
'4c70d641f655b_meme-crying-gif-crying-gif-meme_620-340.gif',
|
|
'https://media.giphy.com/media/fnKd6rCHaZoGdzLjjA/giphy.gif',
|
|
'https://media.giphy.com/media/47D5jmVc4f7ylygXYD/giphy.gif',
|
|
'https://media.giphy.com/media/I4wGMXoi2kMDe/giphy.gif',
|
|
]
|
|
return salute_gifs[random.randint(0, len(salute_gifs) - 1)]
|
|
|
|
|
|
def random_conf_word():
|
|
conf_words = [
|
|
'dope',
|
|
'cool',
|
|
'got it',
|
|
'noice',
|
|
'ok',
|
|
'lit',
|
|
]
|
|
return conf_words[random.randint(0, len(conf_words) - 1)]
|
|
|
|
|
|
def random_codename():
|
|
all_names = [
|
|
'Shong', 'DerekSux', 'JoeSux', 'CalSux', 'Friend', 'Andrea', 'Ent', 'Lindved', 'Camp', 'Idyll', 'Elaphus',
|
|
'Turki', 'Shrimp', 'Primary', 'Anglica', 'Shail', 'Blanket', 'Baffled', 'Deer', 'Thisted', 'Brisk', 'Shy',
|
|
'Table', 'Jorts', 'Renati', 'Gisky', 'Prig', 'Bathtub', 'Gallery', 'Mavas', 'Chird', 'Oxyura', 'Mydal', 'Brown',
|
|
'Vasen', 'Worthy', 'Bivver', 'Cirlus', 'Self', 'Len', 'Sharp', 'Dart', 'Crepis', 'Ferina', 'Curl', 'Lancome',
|
|
'Stuff', 'Glove', 'Consist', 'Smig', 'Egg', 'Pleat', 'Picture', 'Spin', 'Ridgty', 'Ickled', 'Abashed', 'Haul',
|
|
'Cordage', 'Chivery', 'Stointy', 'Baa', 'Here', 'Ulmi', 'Tour', 'Tribe', 'Crunch', 'Used', 'Pigface', 'Audit',
|
|
'Written', 'Once', 'Fickle', 'Drugged', 'Swarm', 'Blimber', 'Torso', 'Retusa', 'Hockey', 'Pusty', 'Sallow',
|
|
'Next', 'Mansion', 'Glass', 'Screen', 'Josiah', 'Bonkey', 'Stuff', 'Sane', 'Blooded', 'Gnat', 'Liparis',
|
|
'Ocean', 'Sway', 'Roband', 'Still', 'Ribba', 'Biryani', 'Halibut', 'Flyn', 'Until', 'Depend', 'Intel',
|
|
'Affinis', 'Chef', 'Trounce', 'Crawl', 'Grab', 'Eggs', 'Malfroy', 'Sitta', 'Cretin', 'May', 'Smithii',
|
|
'Saffron', 'Crummy', 'Powered', 'Rail', 'Trait', 'Koiled', 'Bronze', 'Quickly', 'Vikis', 'Trift', 'Jubilar',
|
|
'Deft', 'Juncus', 'Sodding', 'Distant', 'Poecile', 'Pipe', 'Sell', 'Inops', 'Peusi', 'Sparrow', 'Yams',
|
|
'Kidneys', 'Artery', 'Vuffin', 'Boink', 'Bos', 'Notable', 'Alba', 'Spurge', 'Ruby', 'Cilia', 'Pellow', 'Nox',
|
|
'Woozy', 'Semvik', 'Tyda', 'Season', 'Lychnis', 'Ibestad', 'Bagge', 'Marked', 'Browdie', 'Fisher', 'Tilly',
|
|
'Troll', 'Gypsy', 'Thisted', 'Flirt', 'Stop', 'Radiate', 'Poop', 'Plenty', 'Jeff', 'Magpie', 'Roof', 'Ent',
|
|
'Dumbo', 'Pride', 'Weights', 'Winted', 'Dolden', 'Meotica', 'Yikes', 'Teeny', 'Fizz', 'Eide', 'Foetida',
|
|
'Crash', 'Mann', 'Salong', 'Cetti', 'Balloon', 'Petite', 'Find', 'Sputter', 'Patula', 'Upstage', 'Aurora',
|
|
'Dadson', 'Drate', 'Heidal', 'Robin', 'Auditor', 'Ithil', 'Warmen', 'Pat', 'Muppet', '007', 'Advantage',
|
|
'Alert', 'Backhander', 'Badass', 'Blade', 'Blaze', 'Blockade', 'Blockbuster', 'Boxer', 'Brimstone', 'Broadway',
|
|
'Buccaneer', 'Champion', 'Cliffhanger', 'Coachman', 'Comet', 'Commander', 'Courier', 'Cowboy', 'Crawler',
|
|
'Crossroads', 'DeepSpace', 'Desperado', 'Double-Decker', 'Echelon', 'Edge', 'Encore', 'EnRoute', 'Escape',
|
|
'Eureka', 'Evangelist', 'Excursion', 'Explorer', 'Fantastic', 'Firefight', 'Foray', 'Forge', 'Freeway',
|
|
'Frontier', 'FunMachine', 'Galaxy', 'GameOver', 'Genesis', 'Hacker', 'Hawkeye', 'Haybailer', 'Haystack',
|
|
'Hexagon', 'Hitman', 'Hustler', 'Iceberg', 'Impossible', 'Impulse', 'Invader', 'Inventor', 'IronWolf',
|
|
'Jackrabbit', 'Juniper', 'Keyhole', 'Lancelot', 'Liftoff', 'MadHatter', 'Magnum', 'Majestic', 'Merlin',
|
|
'Multiplier', 'Netiquette', 'Nomad', 'Octagon', 'Offense', 'OliveBranch', 'OlympicTorch', 'Omega', 'Onyx',
|
|
'Orbit', 'OuterSpace', 'Outlaw', 'Patron', 'Patriot', 'Pegasus', 'Pentagon', 'Pilgrim', 'Pinball', 'Pinnacle',
|
|
'Pipeline', 'Pirate', 'Portal', 'Predator', 'Prism', 'RagingBull', 'Ragtime', 'Reunion', 'Ricochet',
|
|
'Roadrunner', 'Rockstar', 'RobinHood', 'Rover', 'Runabout', 'Sapphire', 'Scrappy', 'Seige', 'Shadow',
|
|
'Shakedown', 'Shockwave', 'Shooter', 'Showdown', 'SixPack', 'SlamDunk', 'Slasher', 'Sledgehammer', 'Spirit',
|
|
'Spotlight', 'Starlight', 'Steamroller', 'Stride', 'Sunrise', 'Superhuman', 'Supernova', 'SuperBowl', 'Sunset',
|
|
'Sweetheart', 'TopHand', 'Touchdown', 'Tour', 'Trailblazer', 'Transit', 'Trekker', 'Trio', 'TriplePlay',
|
|
'TripleThreat', 'Universe', 'Unstoppable', 'Utopia', 'Vicinity', 'Vector', 'Vigilance', 'Vigilante', 'Vista',
|
|
'Visage', 'Vis-à-vis', 'VIP', 'Volcano', 'Volley', 'Whizzler', 'Wingman', 'Badger', 'BlackCat', 'Bobcat',
|
|
'Caracal', 'Cheetah', 'Cougar', 'Jaguar', 'Leopard', 'Lion', 'Lynx', 'MountainLion', 'Ocelot', 'Panther',
|
|
'Puma', 'Siamese', 'Serval', 'Tiger', 'Wolverine', 'Abispa', 'Andrena', 'BlackWidow', 'Cataglyphis',
|
|
'Centipede', 'Cephalotes', 'Formica', 'Hornet', 'Jellyfish', 'Scorpion', 'Tarantula', 'Yellowjacket', 'Wasp',
|
|
'Apollo', 'Ares', 'Artemis', 'Athena', 'Hercules', 'Hermes', 'Iris', 'Medusa', 'Nemesis', 'Neptune', 'Perseus',
|
|
'Poseidon', 'Triton', 'Zeus', 'Aquarius', 'Aries', 'Cancer', 'Capricorn', 'Gemini', 'Libra', 'Leo', 'Pisces',
|
|
'Sagittarius', 'Scorpio', 'Taurus', 'Virgo', 'Andromeda', 'Aquila', 'Cassiopeia', 'Cepheus', 'Cygnus',
|
|
'Delphinus', 'Drako', 'Lyra', 'Orion', 'Perseus', 'Serpens', 'Triangulum', 'Anaconda', 'Boa', 'Cobra',
|
|
'Copperhead', 'Cottonmouth', 'Garter', 'Kingsnake', 'Mamba', 'Python', 'Rattler', 'Sidewinder', 'Taipan',
|
|
'Viper', 'Alligator', 'Barracuda', 'Crocodile', 'Gator', 'GreatWhite', 'Hammerhead', 'Jaws', 'Lionfish',
|
|
'Mako', 'Moray', 'Orca', 'Piranha', 'Shark', 'Stingray', 'Axe', 'BattleAxe', 'Bayonet', 'Blade', 'Crossbowe',
|
|
'Dagger', 'Excalibur', 'Halberd', 'Hatchet', 'Machete', 'Saber', 'Samurai', 'Scimitar', 'Scythe', 'Stiletto',
|
|
'Spear', 'Sword', 'Aurora', 'Avalanche', 'Blizzard', 'Cyclone', 'Dewdrop', 'Downpour', 'Duststorm', 'Fogbank',
|
|
'Freeze', 'Frost', 'GullyWasher', 'Gust', 'Hurricane', 'IceStorm', 'JetStream', 'Lightning', 'Mist', 'Monsoon',
|
|
'Rainbow', 'Raindrop', 'SandStorm', 'Seabreeze', 'Snowflake', 'Stratosphere', 'Storm', 'Sunrise', 'Sunset',
|
|
'Tornado', 'Thunder', 'Thunderbolt', 'Thunderstorm', 'TropicalStorm', 'Twister', 'Typhoon', 'Updraft', 'Vortex',
|
|
'Waterspout', 'Whirlwind', 'WindChill', 'Archimedes', 'Aristotle', 'Confucius', 'Copernicus', 'Curie',
|
|
'daVinci', 'Darwin', 'Descartes', 'Edison', 'Einstein', 'Epicurus', 'Freud', 'Galileo', 'Hawking',
|
|
'Machiavelli', 'Marx', 'Newton', 'Pascal', 'Pasteur', 'Plato', 'Sagan', 'Socrates', 'Tesla', 'Voltaire',
|
|
'Baccarat', 'Backgammon', 'Blackjack', 'Chess', 'Jenga', 'Jeopardy', 'Keno', 'Monopoly', 'Pictionary', 'Poker',
|
|
'Scrabble', 'TrivialPursuit', 'Twister', 'Roulette', 'Stratego', 'Yahtzee', 'Aquaman', 'Batman', 'BlackPanther',
|
|
'BlackWidow', 'CaptainAmerica', 'Catwoman', 'Daredevil', 'Dr.Strange', 'Flash', 'GreenArrow', 'GreenLantern',
|
|
'Hulk', 'IronMan', 'Phantom', 'Thor', 'SilverSurfer', 'SpiderMan', 'Supergirl', 'Superman', 'WonderWoman',
|
|
'Wolverine', 'Hypersonic', 'Lightspeed', 'Mach1,2,3,4,etc', 'Supersonic', 'WarpSpeed', 'Amiatina', 'Andalusian',
|
|
'Appaloosa', 'Clydesdale', 'Colt', 'Falabella', 'Knabstrupper', 'Lipizzan', 'Lucitano', 'Maverick', 'Mustang',
|
|
'Palomino', 'Pony', 'QuarterHorse', 'Stallion', 'Thoroughbred', 'Zebra', 'Antigua', 'Aruba', 'Azores', 'Baja',
|
|
'Bali', 'Barbados', 'Bermuda', 'BoraBora', 'Borneo', 'Capri', 'Cayman', 'Corfu', 'Cozumel', 'Curacao', 'Fiji',
|
|
'Galapagos', 'Hawaii', 'Ibiza', 'Jamaica', 'Kauai', 'Lanai', 'Majorca', 'Maldives', 'Maui', 'Mykonos',
|
|
'Nantucket', 'Oahu', 'Tahiti', 'Tortuga', 'Roatan', 'Santorini', 'Seychelles', 'St.Johns', 'St.Lucia',
|
|
'Albatross', 'BaldEagle', 'Blackhawk', 'BlueJay', 'Chukar', 'Condor', 'Crane', 'Dove', 'Eagle', 'Falcon',
|
|
'Goose(GoldenGoose)', 'Grouse', 'Hawk', 'Heron', 'Hornbill', 'Hummingbird', 'Lark', 'Mallard', 'Oriole',
|
|
'Osprey', 'Owl', 'Parrot', 'Penguin', 'Peregrine', 'Pelican', 'Pheasant', 'Quail', 'Raptor', 'Raven', 'Robin',
|
|
'Sandpiper', 'Seagull', 'Sparrow', 'Stork', 'Thunderbird', 'Toucan', 'Vulture', 'Waterfowl', 'Woodpecker',
|
|
'Wren', 'C-3PO', 'Chewbacca', 'Dagobah', 'DarthVader', 'DeathStar', 'Devaron', 'Droid', 'Endor', 'Ewok', 'Hoth',
|
|
'Jakku', 'Jedi', 'Leia', 'Lightsaber', 'Lothal', 'Naboo', 'Padawan', 'R2-D2', 'Scarif', 'Sith', 'Skywalker',
|
|
'Stormtrooper', 'Tatooine', 'Wookie', 'Yoda', 'Zanbar', 'Canoe', 'Catamaran', 'Cruiser', 'Cutter', 'Ferry',
|
|
'Galleon', 'Gondola', 'Hovercraft', 'Hydrofoil', 'Jetski', 'Kayak', 'Longboat', 'Motorboat', 'Outrigger',
|
|
'PirateShip', 'Riverboat', 'Sailboat', 'Skipjack', 'Schooner', 'Skiff', 'Sloop', 'Steamboat', 'Tanker',
|
|
'Trimaran', 'Trawler', 'Tugboat', 'U-boat', 'Yacht', 'Yawl', 'Lancer', 'Volunteer', 'Searchlight', 'Passkey',
|
|
'Deacon', 'Rawhide', 'Timberwolf', 'Eagle', 'Tumbler', 'Renegade', 'Mogul'
|
|
]
|
|
|
|
this_name = all_names[random.randint(0, len(all_names) - 1)]
|
|
return this_name
|
|
|
|
|
|
def random_no_phrase():
|
|
phrases = [
|
|
'uhh...no',
|
|
'lol no',
|
|
'nope',
|
|
'not again',
|
|
'huh uh',
|
|
'nah'
|
|
]
|
|
this_phrase = phrases[random.randint(0, len(phrases) - 1)]
|
|
return this_phrase
|
|
|
|
|
|
def random_soccer():
|
|
all_phrases = [
|
|
'Ugh, this again',
|
|
'Bro I can\'t with this',
|
|
'Fucking soccer again?',
|
|
'Gtfo with this soccer stuff',
|
|
'I\'m just gonna start deleting stuff',
|
|
'No\nNo\n\nNooooo\n\n\nFucking no more soccer\n\n\n\n\n\nNooooooooooooooooooo',
|
|
'Omg with this soccer shit again',
|
|
'Do you ever want to win an sba game again?'
|
|
]
|
|
this_phrase = all_phrases[random.randint(0, len(all_phrases) - 1)]
|
|
return this_phrase
|
|
|
|
|
|
async def get_emoji(ctx, name, return_empty=True):
|
|
try:
|
|
emoji = await commands.converter.EmojiConverter().convert(ctx, name)
|
|
except:
|
|
if return_empty:
|
|
emoji = ''
|
|
else:
|
|
return name
|
|
return emoji
|
|
|
|
|
|
async def team_emoji(ctx, team):
|
|
try:
|
|
emoji = await get_emoji(ctx, f'{team["sname"].lower().replace(" ","")}')
|
|
except:
|
|
emoji = ''
|
|
return emoji
|
|
|
|
|
|
async def fuzzy_player_search(ctx, channel, bot, name, master_list, author = None):
|
|
"""
|
|
Takes a name to search and returns the name of the best match
|
|
|
|
:param ctx: discord context
|
|
:param channel: discord channel
|
|
:param bot: discord.py bot object
|
|
:param name: string
|
|
:param master_list: list of names
|
|
:return:
|
|
"""
|
|
logging.warning(f'fuzzy player search - len(master_list): {len(master_list)}')
|
|
if name.lower() in master_list:
|
|
return name.lower()
|
|
|
|
if author is None:
|
|
author = ctx.author
|
|
|
|
great_matches = get_close_matches(name, master_list, cutoff=0.8)
|
|
if len(great_matches) == 1:
|
|
return great_matches[0]
|
|
elif len(great_matches) > 0:
|
|
matches = great_matches
|
|
else:
|
|
matches = get_close_matches(name, master_list, n=6)
|
|
if len(matches) == 1:
|
|
return matches[0]
|
|
|
|
if not matches:
|
|
raise ValueError(f'{name.title()} was not found')
|
|
|
|
embed = discord.Embed(
|
|
title="Did You Mean...",
|
|
description='Enter the number of the card you would like to see.',
|
|
color=0x7FC600
|
|
)
|
|
count = 1
|
|
for x in matches:
|
|
embed.add_field(name=f'{count}', value=x, inline=False)
|
|
count += 1
|
|
embed.set_footer(text='These are the closest matches. Spell better if they\'re not who you want.')
|
|
this_q = Question(bot, channel, None, 'int', 45, embed=embed)
|
|
resp = await this_q.ask([author])
|
|
|
|
if not resp:
|
|
return None
|
|
if resp < count:
|
|
return matches[resp - 1]
|
|
else:
|
|
raise ValueError(f'{resp} is not a valid response.')
|
|
|
|
|
|
def get_player_positions(player):
|
|
"""
|
|
:param player: Player instance
|
|
:return list: positions (ex: ['1B', '3B'] or ['SP', 'RP', 'CP'])
|
|
"""
|
|
positions = []
|
|
if player['pos_1']:
|
|
positions.append(player['pos_1'])
|
|
if player['pos_2']:
|
|
positions.append(player['pos_2'])
|
|
if player['pos_3']:
|
|
positions.append(player['pos_3'])
|
|
if player['pos_4']:
|
|
positions.append(player['pos_4'])
|
|
if player['pos_5']:
|
|
positions.append(player['pos_5'])
|
|
if player['pos_6']:
|
|
positions.append(player['pos_6'])
|
|
if player['pos_7']:
|
|
positions.append(player['pos_7'])
|
|
if player['pos_8']:
|
|
positions.append(player['pos_8'])
|
|
|
|
return positions
|
|
|
|
|
|
def get_team_embed(title, team=None, thumbnail: bool = True):
|
|
if team:
|
|
embed = discord.Embed(
|
|
title=title,
|
|
color=int(team["color"], 16) if team["color"] and int(team["color"], 16) <= 16777215
|
|
else int(SBA_COLOR, 16)
|
|
)
|
|
if thumbnail:
|
|
if 'thumbnail' in team:
|
|
embed.set_thumbnail(url=team["thumbnail"] if team["thumbnail"] else LOGO)
|
|
elif 'logo' in team:
|
|
embed.set_thumbnail(url=team["logo"] if team["logo"] else LOGO)
|
|
embed.set_footer(text=f'SBa Season {team["season"]}', icon_url=LOGO)
|
|
else:
|
|
embed = discord.Embed(
|
|
title=title,
|
|
color=int(SBA_COLOR, 16)
|
|
)
|
|
if thumbnail:
|
|
embed.set_thumbnail(url=LOGO)
|
|
embed.set_footer(text=f'SBa Season {SBA_SEASON}', icon_url=LOGO)
|
|
return embed
|
|
|
|
|
|
async def get_or_create_role(ctx, role_name, mentionable=True):
|
|
this_role = discord.utils.get(ctx.guild.roles, name=role_name)
|
|
logging.info(f'this_role: {this_role} / role_name: {role_name} (POST SEARCH)')
|
|
|
|
if not this_role:
|
|
this_role = await ctx.guild.create_role(name=role_name, mentionable=mentionable)
|
|
logging.info(f'this_role: {this_role} / role_name: {role_name} (PRE RETURN)')
|
|
|
|
return this_role
|
|
|
|
|
|
def get_role(ctx, role_name, bot=None):
|
|
role = None
|
|
|
|
if not ctx:
|
|
if bot:
|
|
guild = bot.get_guild(int(os.environ.get('GUILD_ID')))
|
|
if not guild:
|
|
logging.error('Cannot send to channel - bot not logged in')
|
|
return
|
|
role = discord.utils.get(guild.roles, name=role_name)
|
|
else:
|
|
role = discord.utils.get(ctx.guild.roles, name=role_name)
|
|
|
|
logging.info(f'this_role: {role} / role_name: {role_name} (PRE RETURN)')
|
|
if role:
|
|
return role
|
|
else:
|
|
return None
|
|
|
|
|
|
def get_team_role(ctx, team, bot=None):
|
|
return get_role(ctx, team['lname'], bot)
|
|
|
|
|
|
async def send_to_channel(bot, channel_name, content=None, embed=None):
|
|
guild = bot.get_guild(int(os.environ.get('GUILD_ID')))
|
|
if not guild:
|
|
logging.error('Cannot send to channel - bot not logged in')
|
|
return
|
|
|
|
this_channel = discord.utils.get(guild.text_channels, name=channel_name)
|
|
|
|
if not this_channel:
|
|
this_channel = discord.utils.get(guild.text_channels, id=channel_name)
|
|
if not this_channel:
|
|
raise NameError(f'**{channel_name}** channel not found')
|
|
|
|
await this_channel.send(content=content, embed=embed)
|
|
|
|
|
|
async def get_player_embed(player, current, ctx=None, season=None):
|
|
if season is None:
|
|
season = current['season']
|
|
player_name = player['name']
|
|
if player['il_return']:
|
|
if player['team']['abbrev'][-2:].lower() == 'il':
|
|
player_name = f'🏥 {player_name}'
|
|
else:
|
|
player_name = f'{await get_emoji(ctx, "WeenieHut", False)}{player_name}'
|
|
embed = get_team_embed(f'{player_name}', player["team"])
|
|
embed.set_footer(text=f'SBa Season {current["season"]}', icon_url=LOGO)
|
|
embed.add_field(name='Player ID', value=f'{player["id"]}')
|
|
embed.set_image(url=player['image'])
|
|
# embed.description = f'Player ID {player["id"]}'
|
|
|
|
if player['headshot']:
|
|
embed.set_thumbnail(url=player['headshot'])
|
|
elif player['vanity_card']:
|
|
embed.set_thumbnail(url=player['vanity_card'])
|
|
else:
|
|
player_photo = await get_player_headshot(player['name'])
|
|
if player_photo:
|
|
embed.set_thumbnail(url=player_photo)
|
|
|
|
t_query = await db_get('transactions', params=[
|
|
('season', current['season']), ('week_start', current['week']), ('week_end', current['week'] + 1),
|
|
('player_id', player['id'])
|
|
])
|
|
d_query = await db_get('draftpicks', params=[
|
|
('season', current['season']), ('player_id', player['id'])
|
|
])
|
|
|
|
positions = get_player_positions(player)
|
|
if len(positions) > 0:
|
|
embed.add_field(name=f'Position{"s" if len(positions) > 1 else ""}', value=",".join(positions))
|
|
embed.add_field(name='sWAR', value=player['wara'])
|
|
player_pages = f'[SBa]({get_player_url(player)}) / ' \
|
|
f'[BBRef]({get_player_url(player, "bbref")})'
|
|
embed.add_field(name='Player Page', value=player_pages)
|
|
# if player['last_game']:
|
|
# embed.add_field(name='Last G', value=player['last_game'])
|
|
# if player['last_game2']:
|
|
# embed.add_field(name='Last G-2', value=player['last_game2'])
|
|
if player['injury_rating'] is not None:
|
|
inj_string = f'{player["injury_rating"]}'
|
|
if player['pos_1'] in ['SP', 'RP']:
|
|
inj_code = player['injury_rating'][:1]
|
|
inj_string += f'(6-{13 - int(inj_code)})'
|
|
embed.add_field(name='Injury', value=inj_string)
|
|
if player['pitcher_injury'] is not None:
|
|
if player['pitcher_injury']:
|
|
embed.add_field(name='P Injury', value=f'{player["pitcher_injury"]} (6-{13 - player["pitcher_injury"]})')
|
|
else:
|
|
embed.add_field(name='P Injury', value=f'{player["pitcher_injury"]} (---)')
|
|
if d_query['count'] > 0:
|
|
pick = d_query['picks'][0]
|
|
num = pick["overall"] % 16
|
|
if num == 0:
|
|
num = 16
|
|
embed.add_field(name='Draft Pick', value=f'{pick["round"]}.{num} ({pick["owner"]["abbrev"]})')
|
|
else:
|
|
embed.add_field(name='Draft Pick', value=f'None')
|
|
embed.add_field(name='Current Team', value=player['team']['sname'])
|
|
if player['team']['abbrev'][-3:].lower() == 'mil':
|
|
major_team = await get_team_by_abbrev(player['team']['abbrev'][:-3], season=player['season'])
|
|
embed.add_field(name='SBa Affiliate', value=major_team['sname'])
|
|
if player['demotion_week'] is not None:
|
|
if player['demotion_week'] > current['week']:
|
|
embed.add_field(name='Dem Week', value=player["demotion_week"])
|
|
if player['il_return']:
|
|
embed.add_field(name='IL Return', value=player['il_return'])
|
|
for x in t_query['transactions']:
|
|
if x['week'] == current['week']:
|
|
embed.add_field(name='Last Week', value=f'{x["oldteam"]["sname"]}')
|
|
if x['week'] == current['week'] + 1:
|
|
embed.add_field(name='Next Week', value=f'To {x["newteam"]["sname"]}')
|
|
|
|
if player['season'] < 8:
|
|
b, p = None, None
|
|
batting_string = None
|
|
pitching_string = None
|
|
batter_priority = True
|
|
|
|
b_query = await db_get('battingstats/totals', params=[('season', season), ('player_id', player['id'])])
|
|
if b_query['count'] > 0:
|
|
b = b_query['stats'][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}'
|
|
triple = f'{b["triple"]:.0f}'
|
|
hr = f'{b["hr"]:.0f}'
|
|
rbi = f'{b["rbi"]:.0f}'
|
|
sb = f'{b["sb"]:.0f}'
|
|
cs = f'{b["cs"]:.0f}'
|
|
so = f'{b["so"]:.0f}'
|
|
|
|
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```'
|
|
|
|
p_query = await db_get('pitchingstats/totals', params=[('season', season), ('player_id', player['id'])])
|
|
if p_query['count'] > 0:
|
|
p = p_query['stats'][0]
|
|
|
|
if p['ip'] > 0:
|
|
if b is not None and p['ip'] > b['pa']:
|
|
batter_priority = False
|
|
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}'
|
|
pitching_string = f'```\n' \
|
|
f' W L SV ERA IP SO WHIP\n' \
|
|
f'{win: >2} {loss: >2} {save: >2} {era: >5} {ip: >5}\n``````\n' \
|
|
f'{so: >3} {whip: >4}\n```'
|
|
|
|
if batting_string and batter_priority:
|
|
embed.add_field(name='Batting Stats', value=batting_string, inline=False)
|
|
if pitching_string:
|
|
embed.add_field(name='Pitching Stats', value=pitching_string, inline=False)
|
|
if batting_string and not batter_priority:
|
|
embed.add_field(name='Batting Stats', value=batting_string, inline=False)
|
|
else:
|
|
b_query = await db_get('plays/batting', params=[('player_id', player['id']), ('min_wpa', 0)])
|
|
p_query = await db_get('plays/pitching', params=[('player_id', player['id'])])
|
|
batter_priority = True
|
|
b, p, batting_string, pitching_string = None, None, None, None
|
|
|
|
if b_query['count'] > 0:
|
|
b = b_query['stats'][0]
|
|
batting_string = f'```\n' \
|
|
f' AVG OBP SLG\n' \
|
|
f' {b["avg"]:.3f} {b["obp"]:.3f} {b["slg"]:.3f}\n``````\n' \
|
|
f' OPS wOBA WPA\n' \
|
|
f' {b["ops"]:.3f} {b["woba"]:.3f} {b["wpa"]:.3f}\n``````\n' \
|
|
f' PA H RBI 2B 3B HR SB\n' \
|
|
f'{b["pa"]: >3} {b["hit"]: ^3} {b["rbi"]: ^3} {b["double"]: >2} {b["triple"]: >2} ' \
|
|
f'{b["hr"]: >2} {b["sb"]: >2}```\n'
|
|
if p_query['count'] > 0:
|
|
p = p_query['stats'][0]
|
|
if b is None or p["tbf"] > b["pa"]:
|
|
batter_priority = False
|
|
|
|
ip_whole = math.floor(p['outs'] / 3)
|
|
ip_denom = p['outs'] % 3
|
|
ips = ip_whole + (ip_denom * 0.1)
|
|
|
|
kpbb = f'{p["k/bb"]:.1f}'
|
|
era = f'{p["era"]:.2f}'
|
|
whip = f'{p["whip"]:.2f}'
|
|
pitching_string = f'```\n' \
|
|
f' W-L SV ERA WHIP\n' \
|
|
f'{p["win"]: >2}-{p["loss"]: <2} {p["save"]: >2} {era: >5} {whip: >4}\n``````\n' \
|
|
f' IP SO K/BB WPA\n' \
|
|
f'{ips: >5} {p["so"]: ^3} {kpbb: ^4} {p["wpa"]:.3f}\n```'
|
|
|
|
if batting_string and batter_priority:
|
|
embed.add_field(name='Batting Stats', value=batting_string, inline=False)
|
|
if pitching_string:
|
|
embed.add_field(name='Pitching Stats', value=pitching_string, inline=False)
|
|
if batting_string and not batter_priority:
|
|
embed.add_field(name='Batting Stats', value=batting_string, inline=False)
|
|
|
|
return embed
|
|
|
|
|
|
def get_player_url(player, which="sba"):
|
|
stub_name = player["name"].replace(" ", "%20")
|
|
if which == 'bbref':
|
|
if player['bbref_id'] is not None:
|
|
br_id = player['bbref_id']
|
|
return f'https://www.baseball-reference.com/players/{br_id[0]}/{br_id}.shtml'
|
|
else:
|
|
return f'https://www.baseball-reference.com/search/search.fcgi?search={stub_name}'
|
|
else:
|
|
return f'{SBA_BASE_URL}/players/{player["season"]}/{stub_name}'
|
|
|
|
|
|
def get_channel(ctx, name):
|
|
channel = discord.utils.get(
|
|
ctx.guild.text_channels,
|
|
name=name
|
|
)
|
|
if channel:
|
|
return channel
|
|
|
|
return None
|
|
|
|
|
|
async def create_channel(
|
|
ctx, channel_name: str, category_name: str, everyone_send: bool = False, everyone_read: bool = True,
|
|
read_send_members: list = None, read_send_roles: list = None, read_only_roles: list = None,
|
|
read_only_pokemon: bool = True):
|
|
this_category = discord.utils.get(ctx.guild.categories, name=category_name)
|
|
if not this_category:
|
|
raise ValueError(f'I couldn\'t find a category named **{category_name}**')
|
|
|
|
overwrites = {
|
|
ctx.guild.me: discord.PermissionOverwrite(read_messages=True, send_messages=True),
|
|
ctx.guild.default_role: discord.PermissionOverwrite(read_messages=everyone_read, send_messages=everyone_send)
|
|
}
|
|
if read_send_members:
|
|
for member in read_send_members:
|
|
overwrites[member] = discord.PermissionOverwrite(read_messages=True, send_messages=True)
|
|
if read_send_roles:
|
|
for role in read_send_roles:
|
|
overwrites[role] = discord.PermissionOverwrite(read_messages=True, send_messages=True)
|
|
if read_only_roles:
|
|
for role in read_only_roles:
|
|
overwrites[role] = discord.PermissionOverwrite(read_messages=True, send_messages=False)
|
|
if read_only_pokemon:
|
|
poke_role = get_role(ctx, 'Pokétwo')
|
|
overwrites[poke_role] = discord.PermissionOverwrite(read_messages=True, send_messages=False)
|
|
|
|
this_channel = await ctx.guild.create_text_channel(
|
|
channel_name,
|
|
overwrites=overwrites,
|
|
category=this_category
|
|
)
|
|
|
|
logging.info(f'Creating channel ({channel_name}) in ({category_name})')
|
|
|
|
return this_channel
|
|
|
|
|
|
async def get_major_team(team):
|
|
if team['abbrev'][-3:] == 'MiL':
|
|
return await get_team_by_abbrev(team['abbrev'][:-3], team['season'])
|
|
elif team['abbrev'][-2:] == 'IL':
|
|
return await get_team_by_abbrev(team['abbrev'][:-2], team['season'])
|
|
else:
|
|
return team
|
|
|
|
|
|
async def react_and_reply(ctx, reaction, message):
|
|
await ctx.message.add_reaction(reaction)
|
|
await ctx.send(message)
|
|
|
|
|
|
async def toggle_draft_sheet() -> None:
|
|
sheets = pygsheets.authorize(service_file='storage/major-domo-service-creds.json')
|
|
this_sheet = sheets.open_by_key(SBA_SEASON6_DRAFT_KEY)
|
|
my_cards = this_sheet.worksheet_by_title('BUTTON')
|
|
logging.info(f'Toggling the draft button...')
|
|
|
|
my_cards.update_value(
|
|
'B3', 'FALSE'
|
|
)
|
|
await asyncio.sleep(1)
|
|
my_cards.update_value(
|
|
'B3', 'TRUE'
|
|
)
|
|
|
|
|
|
async def log_injury(current: dict, inj_dict: dict) -> None:
|
|
sheets = pygsheets.authorize(service_file='storage/major-domo-service-creds.json')
|
|
this_sheet = sheets.open_by_key(SBA_ROSTER_KEY)
|
|
injury_sheet = this_sheet.worksheet_by_title('Injury Log')
|
|
|
|
logging.info(f'updating values')
|
|
|
|
try:
|
|
injury_sheet.update_values(
|
|
crange=f'A{current["injury_count"]+2}',
|
|
values=[[
|
|
datetime.datetime.now().strftime('%Y-%m-%d, %H:%M:%S'), inj_dict['player']['name'], inj_dict['type'],
|
|
inj_dict['player']['team']['abbrev'], current['week'], inj_dict['current_game'],
|
|
inj_dict['injury_length']
|
|
]]
|
|
)
|
|
except Exception as e:
|
|
logging.error(f'log_injury sheets: {e}')
|
|
raise Exception(e)
|
|
|
|
|
|
def get_pos_abbrev(pos_name):
|
|
if pos_name == 'Catcher':
|
|
return 'C'
|
|
elif pos_name == 'First Base':
|
|
return '1B'
|
|
elif pos_name == 'Second Base':
|
|
return '2B'
|
|
elif pos_name == 'Third Base':
|
|
return '3B'
|
|
elif pos_name == 'Shortstop':
|
|
return 'SS'
|
|
elif pos_name == 'Left Field':
|
|
return 'LF'
|
|
elif pos_name == 'Center Field':
|
|
return 'CF'
|
|
elif pos_name == 'Right Field':
|
|
return 'RF'
|
|
elif pos_name == 'Pitcher':
|
|
return 'P'
|
|
elif pos_name == 'Designated Hitter':
|
|
return 'DH'
|
|
elif pos_name == 'Pinch Hitter':
|
|
return 'PH'
|
|
else:
|
|
raise KeyError(f'{pos_name} is not a recognized position name')
|
|
|
|
|
|
def random_gif(search_term: str):
|
|
req_url = f'https://api.giphy.com/v1/gifs/translate?s={search_term}&api_key=H86xibttEuUcslgmMM6uu74IgLEZ7UOD'
|
|
|
|
resp = requests.get(req_url, timeout=3)
|
|
if resp.status_code == 200:
|
|
data = resp.json()
|
|
if 'trump' in data['data']['title']:
|
|
return random_conf_gif()
|
|
else:
|
|
return data['data']['url']
|
|
else:
|
|
logging.warning(resp.text)
|
|
raise ValueError(f'DB: {resp.text}')
|
|
|
|
|
|
def new_rand_conf_gif():
|
|
return random_gif('all done')
|
|
|
|
|
|
def random_from_list(data_list: list):
|
|
item = data_list[random.randint(0, len(data_list) - 1)]
|
|
logging.info(f'random_from_list: {item}')
|
|
return item
|