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}/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] LONG_FOOTER_TEXT = f'SBa Season {SBA_SEASON} - I hope you are having a great day unless your last name ' \ f'starts with "castag".' LOGO = f'{SBA_BASE_URL}/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