import asyncio import datetime import logging import math import os import random import traceback import discord import pygsheets import requests from discord.ext import commands from api_calls import * from bs4 import BeautifulSoup from difflib import get_close_matches from dataclasses import dataclass from typing import Optional, Literal from in_game.gameplay_models import Team SBA_SEASON = 11 PD_SEASON = 9 ranked_cardsets = [20, 21, 22, 17, 18, 19] LIVE_CARDSET_ID = 20 LIVE_PROMO_CARDSET_ID = 21 MAX_CARDSET_ID = 30 CARDSETS = { 'Ranked': { 'primary': ranked_cardsets, 'human': ranked_cardsets }, 'Minor League': { 'primary': [20, 8], # 1998, Mario 'secondary': [6], # 2013 'human': [x for x in range(1, MAX_CARDSET_ID)] }, 'Major League': { 'primary': [20, 21, 17, 18, 12, 6, 7, 8], # 1998, 1998 Promos, 2024, 24 Promos, 2008, 2013, 2012, Mario 'secondary': [5, 3], # 2019, 2022 'human': ranked_cardsets }, 'Hall of Fame': { 'primary': [x for x in range(1, MAX_CARDSET_ID)], 'secondary': [], 'human': ranked_cardsets }, 'Flashback': { 'primary': [5, 1, 3, 9, 8], # 2019, 2021, 2022, 2023, Mario 'secondary': [13, 5], # 2018, 2019 'human': [5, 1, 3, 9, 8] # 2019, 2021, 2022, 2023 }, 'gauntlet-3': { 'primary': [13], # 2018 'secondary': [5, 11, 9], # 2019, 2016, 2023 'human': [x for x in range(1, MAX_CARDSET_ID)] }, 'gauntlet-4': { 'primary': [3, 6, 16], # 2022, 2013, Backyard Baseball 'secondary': [4, 9], # 2022 Promos, 2023 'human': [3, 4, 6, 9, 15, 16] }, 'gauntlet-5': { 'primary': [17, 8], # 2024, Mario 'secondary': [13], # 2018 'human': [x for x in range(1, MAX_CARDSET_ID)] }, 'gauntlet-6': { 'primary': [20, 8], # 1998, Mario 'secondary': [12], # 2008 'human': [x for x in range(1, MAX_CARDSET_ID)] }, 'gauntlet-7': { 'primary': [5, 23], # 2019, Brilliant Stars 'secondary': [1], # 2021 'human': [x for x in range(1, MAX_CARDSET_ID)] } } SBA_COLOR = 'a6ce39' PD_PLAYERS = 'Paper Dynasty Players' SBA_PLAYERS_ROLE_NAME = f'Season {SBA_SEASON} Players' PD_PLAYERS_ROLE_NAME = f'Paper Dynasty Players' PD_CARD_URL = 'https://sombaseball.ddns.net/cards/pd' PKMN_REF_URL = 'https://pkmncards.com/card/' RATINGS_BATTER_FORMULA = '=IMPORTRANGE("1zDmlOw94gTzOAjqOpNdDZsg0O6rxNWkL4-XT6-iL2IE","guide_Batters!A1:CD")' RATINGS_PITCHER_FORMULA = '=IMPORTRANGE("1zDmlOw94gTzOAjqOpNdDZsg0O6rxNWkL4-XT6-iL2IE","guide_Pitchers!A1:BQ")' RATINGS_SHEET_KEY = '1zDmlOw94gTzOAjqOpNdDZsg0O6rxNWkL4-XT6-iL2IE' ALL_MLB_TEAMS = { 'Arizona Diamondbacks': ['ARI', 'Diamondbacks'], 'Atlanta Braves': ['ATL', 'MLN', 'Braves'], 'Baltimore Orioles': ['BAL', 'Orioles'], 'Boston Red Sox': ['BOS', 'Red Sox'], 'Chicago Cubs': ['CHC', 'Cubs'], 'Chicago White Sox': ['CHW', 'White Sox'], 'Cincinnati Reds': ['CIN', 'Reds'], 'Cleveland Guardians': ['CLE', 'Guardians'], 'Colorado Rockies': ['COL', 'Rockies'], 'Detroit Tigers': ['DET', 'Tigers'], 'Houston Astros': ['HOU', 'Astros'], 'Kansas City Royals': ['KCR', 'Royals'], 'Los Angeles Angels': ['LAA', 'CAL', 'Angels'], 'Los Angeles Dodgers': ['LAD', 'Dodgers'], 'Miami Marlins': ['MIA', 'Marlins'], 'Milwaukee Brewers': ['MIL', 'MKE', 'Brewers'], 'Minnesota Twins': ['MIN', 'Twins'], 'New York Mets': ['NYM', 'Mets'], 'New York Yankees': ['NYY', 'Yankees'], 'Oakland Athletics': ['OAK', 'Athletics'], 'Philadelphia Phillies': ['PHI', 'Phillies'], 'Pittsburgh Pirates': ['PIT', 'Pirates'], 'San Diego Padres': ['SDP', 'Padres'], 'Seattle Mariners': ['SEA', 'Mariners'], 'San Francisco Giants': ['SFG', 'Giants'], 'St Louis Cardinals': ['STL', 'Cardinals'], 'Tampa Bay Rays': ['TBR', 'Rays'], 'Texas Rangers': ['TEX', 'Senators', 'Rangers'], 'Toronto Blue Jays': ['TOR', 'Jays'], 'Washington Nationals': ['WSN', 'WAS', 'Nationals'], } IMAGES = { 'logo': 'https://sombaseball.ddns.net/static/images/sba-logo.png', 'mvp-hype': f'{PD_CARD_URL}/mvp.png', 'pack-sta': f'{PD_CARD_URL}/pack-standard.png', 'pack-pre': f'{PD_CARD_URL}/pack-premium.png', 'pack-mar': f'{PD_CARD_URL}/sluggers/mario-gauntlet.png', 'pack-pkmnbs': f'https://i.postimg.cc/635M4X52/pokemon-brilliantstars.jpg', 'mvp': { 'Arizona Diamondbacks': f'{PD_CARD_URL}/mvp/arizona-diamondbacks.gif', 'Atlanta Braves': f'{PD_CARD_URL}/mvp/atlanta-braves.gif', 'Baltimore Orioles': f'{PD_CARD_URL}/mvp/baltimore-orioles.gif', 'Boston Red Sox': f'{PD_CARD_URL}/mvp/boston-red-sox.gif', 'Chicago Cubs': f'{PD_CARD_URL}/mvp/chicago-cubs.gif', 'Chicago White Sox': f'{PD_CARD_URL}/mvp/chicago-white-sox.gif', 'Cincinnati Reds': f'{PD_CARD_URL}/mvp/cincinnati-reds.gif', 'Cleveland Indians': f'{PD_CARD_URL}/mvp/cleveland-guardians.gif', 'Cleveland Guardians': f'{PD_CARD_URL}/mvp/cleveland-guardians.gif', 'Colorado Rockies': f'{PD_CARD_URL}/mvp/colorado-rockies.gif', 'Detroit Tigers': f'{PD_CARD_URL}/mvp/detroit-tigers.gif', 'Houston Astros': f'{PD_CARD_URL}/mvp/houston-astros.gif', 'Kansas City Royals': f'{PD_CARD_URL}/mvp/kansas-city-royals.gif', 'Los Angeles Angels': f'{PD_CARD_URL}/mvp/los-angeles-angels.gif', 'Los Angeles Dodgers': f'{PD_CARD_URL}/mvp/los-angeles-dodgers.gif', 'Miami Marlins': f'{PD_CARD_URL}/mvp/miami-marlins.gif', 'Milwaukee Brewers': f'{PD_CARD_URL}/mvp/milwaukee-brewers.gif', 'Minnesota Twins': f'{PD_CARD_URL}/mvp/minnesota-twins.gif', 'New York Mets': f'{PD_CARD_URL}/mvp/new-york-mets.gif', 'New York Yankees': f'{PD_CARD_URL}/mvp/new-york-yankees.gif', 'Oakland Athletics': f'{PD_CARD_URL}/mvp/oakland-athletics.gif', 'Philadelphia Phillies': f'{PD_CARD_URL}/mvp/philadelphia-phillies.gif', 'Pittsburgh Pirates': f'{PD_CARD_URL}/mvp/pittsburgh-pirates.gif', 'San Diego Padres': f'{PD_CARD_URL}/mvp/san-diego-padres.gif', 'Seattle Mariners': f'{PD_CARD_URL}/mvp/seattle-mariners.gif', 'San Francisco Giants': f'{PD_CARD_URL}/mvp/san-francisco-giants.gif', 'St Louis Cardinals': f'{PD_CARD_URL}/mvp/st-louis-cardinals.gif', 'St. Louis Cardinals': f'{PD_CARD_URL}/mvp/st-louis-cardinals.gif', 'Tampa Bay Rays': f'{PD_CARD_URL}/mvp/tampa-bay-rays.gif', 'Texas Rangers': f'{PD_CARD_URL}/mvp/texas-rangers.gif', 'Toronto Blue Jays': f'{PD_CARD_URL}/mvp/toronto-blue-jays.gif', 'Washington Nationals': f'{PD_CARD_URL}/mvp/washington-nationals.gif', 'Junior All Stars': f'{PD_CARD_URL}/mvp.png', 'Mario Super Sluggers': f'{PD_CARD_URL}/mvp.png', 'Pokemon League': 'https://i.postimg.cc/ydzYB7BR/masterball.jpg' }, 'gauntlets': f'{PD_CARD_URL}/gauntlets.png' } INFIELD_X_CHART = { 'si1': { 'rp': 'No runner on first: Batter is safe at first and no one covers second. Batter to second, runners only ' 'advance 1 base.\nRunner 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.' }, '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 SI1! 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.' }, 'fo': { 'rp': 'Batter swings and misses, but is awarded first base on a catcher interference call! One base error, ' '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, <2 outs: runner on first breaks up the double play, gbB\n' 'Else: gbA', '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': 'Runner(s) on base: fielder makes bad throw for lead runner but batter is out at first for a gbC\n' 'No runners: gbB', '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': 'Runner(s) on base: fielder checks the runner before throwing to first and allows a SI*\n' 'No runners: gbC', 'e1': 'Error, batter to first, runners advance 1 base.', 'e2': 'Error, batter to second, runners advance 2 bases.', 'no': 'Consult Groundball Chart: `!gbC`' }, 'spd': { 'rp': 'Catcher throws to first and hits the batter-runner in the back, SI1', 'e1': 'Error, batter to first, runners advance 1 base.', 'e2': 'Error, batter to second, runners advance 2 bases.', 'no': 'Speed check, Batter\'s safe range = Running; if safe, SI*; if out, 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 and runners advance three bases, but batter-runner is caught between second and third! ' 'He is tagged out in the rundown.', 'e1': 'Double and error, batter to third, all runners score.', 'e2': 'Double and error, batter to third, 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': 'Batter doubles and runners advance three bases, but batter-runner is caught between second and third! ' 'He is tagged out in the rundown.', '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' } } RARITY = { 'HoF': 8, 'MVP': 5, 'All-Star': 3, 'Starter': 2, 'Reserve': 1, 'Replacement': 0 } SELECT_CARDSET_OPTIONS = [ discord.SelectOption(label='2025 Live', value='24'), discord.SelectOption(label='2025 Promos', value='25'), discord.SelectOption(label='1998 Season', value='20'), discord.SelectOption(label='1998 Promos', value='21'), discord.SelectOption(label='2024 Season', value='17'), discord.SelectOption(label='2024 Promos', value='18'), discord.SelectOption(label='2023 Season', value='9'), discord.SelectOption(label='2023 Promos', value='10'), discord.SelectOption(label='2022 Season', value='3'), discord.SelectOption(label='2022 Promos', value='4'), discord.SelectOption(label='2021 Season', value='1'), discord.SelectOption(label='2019 Season', value='5'), discord.SelectOption(label='2018 Season', value='13'), discord.SelectOption(label='2018 Promos', value='14'), discord.SelectOption(label='2016 Season', value='11'), discord.SelectOption(label='2013 Season', value='6'), discord.SelectOption(label='2012 Season', value='7') ] ACTIVE_EVENT_LITERAL = Literal['1998 Season', 'Brilliant Stars'] DEFENSE_LITERAL = Literal['Pitcher', 'Catcher', 'First Base', 'Second Base', 'Third Base', 'Shortstop', 'Left Field', 'Center Field', 'Right Field'] COLORS = { 'sba': int('a6ce39', 16), 'yellow': int('FFEA00', 16), 'red': int('C70039', 16), 'white': int('FFFFFF', 16) } INSULTS = [ 'Ugh, who even are you?', 'Ugh, who even are you? Go away.', 'Ugh, who even are you? Leave me alone.', 'I will call the fucking cops!', 'I will call the fucking cops! Go away.', 'I will call the fucking cops! Leave me alone', 'Please don\'t talk to me', 'Don\'t talk to me.', 'Eww, don\'t talk to me.', 'Get away from me.', 'Get away from me, creep.', 'Get away from me, loser.', 'Get away from me, pedobear.', 'Why are you even here?', 'Why are you even here? Get lost.', 'Why are you even here? Scram.', 'Why are you even here? No one knows who you are.', 'HEY, DON\'T TOUCH ME!', 'Hey, don\'t touch me!' ] 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'] 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 class Confirm(discord.ui.View): def __init__(self, responders: list, timeout: float = 300.0, label_type: Literal['yes', 'confirm'] = None): super().__init__(timeout=timeout) if not isinstance(responders, list): raise TypeError('responders must be a list') self.value = None self.responders = responders if label_type == 'yes': self.confirm.label = 'Yes' self.cancel.label = 'No' # 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 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 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: logger.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: logger.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: logger.info(f'{interaction.user} is not in {self.responders}') return self.value = 'right' await interaction.response.defer() self.stop() class SelectChoicePackTeam(discord.ui.Select): def __init__(self, which: Literal['AL', 'NL'], team, cardset_id: Optional[int] = None): self.which = which self.owner_team = team self.cardset_id = cardset_id if which == 'AL': options = [ discord.SelectOption(label='Baltimore Orioles'), discord.SelectOption(label='Boston Red Sox'), discord.SelectOption(label='Chicago White Sox'), discord.SelectOption(label='Cleveland Guardians'), discord.SelectOption(label='Detroit Tigers'), discord.SelectOption(label='Houston Astros'), discord.SelectOption(label='Kansas City Royals'), discord.SelectOption(label='Los Angeles Angels'), discord.SelectOption(label='Minnesota Twins'), discord.SelectOption(label='New York Yankees'), discord.SelectOption(label='Oakland Athletics'), discord.SelectOption(label='Seattle Mariners'), discord.SelectOption(label='Tampa Bay Rays'), discord.SelectOption(label='Texas Rangers'), discord.SelectOption(label='Toronto Blue Jays') ] else: options = [ discord.SelectOption(label='Arizona Diamondbacks'), discord.SelectOption(label='Atlanta Braves'), discord.SelectOption(label='Chicago Cubs'), discord.SelectOption(label='Cincinnati Reds'), discord.SelectOption(label='Colorado Rockies'), discord.SelectOption(label='Los Angeles Dodgers'), discord.SelectOption(label='Miami Marlins'), discord.SelectOption(label='Milwaukee Brewers'), discord.SelectOption(label='New York Mets'), discord.SelectOption(label='Philadelphia Phillies'), discord.SelectOption(label='Pittsburgh Pirates'), discord.SelectOption(label='San Diego Padres'), discord.SelectOption(label='San Francisco Giants'), discord.SelectOption(label='St. Louis Cardinals'), discord.SelectOption(label='Washington Nationals') ] super().__init__(placeholder=f'Select an {which} team', options=options) async def callback(self, interaction: discord.Interaction): team_id = None if self.which == 'AL': if self.values[0] == 'Baltimore Orioles': team_id = 3 elif self.values[0] == 'Boston Red Sox': team_id = 4 elif self.values[0] == 'Chicago White Sox': team_id = 6 elif self.values[0] == 'Cleveland Guardians': team_id = 8 elif self.values[0] == 'Detroit Tigers': team_id = 10 elif self.values[0] == 'Houston Astros': team_id = 11 elif self.values[0] == 'Kansas City Royals': team_id = 12 elif self.values[0] == 'Los Angeles Angels': team_id = 13 elif self.values[0] == 'Minnesota Twins': team_id = 17 elif self.values[0] == 'New York Yankees': team_id = 19 elif self.values[0] == 'Oakland Athletics': team_id = 20 elif self.values[0] == 'Seattle Mariners': team_id = 24 elif self.values[0] == 'Tampa Bay Rays': team_id = 27 elif self.values[0] == 'Texas Rangers': team_id = 28 elif self.values[0] == 'Toronto Blue Jays': team_id = 29 else: if self.values[0] == 'Arizona Diamondbacks': team_id = 1 elif self.values[0] == 'Atlanta Braves': team_id = 2 elif self.values[0] == 'Chicago Cubs': team_id = 5 elif self.values[0] == 'Cincinnati Reds': team_id = 7 elif self.values[0] == 'Colorado Rockies': team_id = 9 elif self.values[0] == 'Los Angeles Dodgers': team_id = 14 elif self.values[0] == 'Miami Marlins': team_id = 15 elif self.values[0] == 'Milwaukee Brewers': team_id = 16 elif self.values[0] == 'New York Mets': team_id = 18 elif self.values[0] == 'Philadelphia Phillies': team_id = 21 elif self.values[0] == 'Pittsburgh Pirates': team_id = 22 elif self.values[0] == 'San Diego Padres': team_id = 23 elif self.values[0] == 'San Francisco Giants': team_id = 25 elif self.values[0] == 'St. Louis Cardinals': team_id = 26 elif self.values[0] == 'Washington Nationals': team_id = 30 await interaction.response.edit_message(content=f'You selected the **{self.values[0]}**', view=None) # Get the selected packs params = [ ('pack_type_id', 8), ('team_id', self.owner_team['id']), ('opened', False), ('limit', 1), ('exact_match', True) ] if self.cardset_id is not None: params.append(('pack_cardset_id', self.cardset_id)) p_query = await db_get('packs', params=params) if p_query['count'] == 0: logger.error(f'open-packs - no packs found with params: {params}') raise ValueError(f'Unable to open packs') this_pack = await db_patch('packs', object_id=p_query['packs'][0]['id'], params=[('pack_team_id', team_id)]) await open_choice_pack(this_pack, self.owner_team, interaction, self.cardset_id) class SelectOpenPack(discord.ui.Select): def __init__(self, options: list, team: dict): self.owner_team = team super().__init__(placeholder='Select a Pack Type', options=options) async def callback(self, interaction: discord.Interaction): logger.info(f'SelectPackChoice - selection: {self.values[0]}') pack_vals = self.values[0].split('-') logger.info(f'pack_vals: {pack_vals}') # Get the selected packs params = [('team_id', self.owner_team['id']), ('opened', False), ('limit', 5), ('exact_match', True)] open_type = 'standard' if 'Standard' in pack_vals: open_type = 'standard' params.append(('pack_type_id', 1)) elif 'Premium' in pack_vals: open_type = 'standard' params.append(('pack_type_id', 3)) elif 'Daily' in pack_vals: params.append(('pack_type_id', 4)) elif 'Promo Choice' in pack_vals: open_type = 'choice' params.append(('pack_type_id', 9)) elif 'MVP' in pack_vals: open_type = 'choice' params.append(('pack_type_id', 5)) elif 'All Star' in pack_vals: open_type = 'choice' params.append(('pack_type_id', 6)) elif 'Mario' in pack_vals: open_type = 'choice' params.append(('pack_type_id', 7)) elif 'Team Choice' in pack_vals: open_type = 'choice' params.append(('pack_type_id', 8)) else: raise KeyError(f'Cannot identify pack details: {pack_vals}') # If team isn't already set on team choice pack, make team pack selection now await interaction.response.edit_message(view=None) cardset_id = None if 'Team Choice' in pack_vals and 'Cardset' in pack_vals: # cardset_id = pack_vals[2] cardset_index = pack_vals.index('Cardset') cardset_id = pack_vals[cardset_index + 1] params.append(('pack_cardset_id', cardset_id)) if 'Team' not in pack_vals: view = SelectView( [SelectChoicePackTeam('AL', self.owner_team, cardset_id), SelectChoicePackTeam('NL', self.owner_team, cardset_id)], timeout=30 ) await interaction.channel.send( content=None, view=view ) return params.append(('pack_team_id', pack_vals[pack_vals.index('Team') + 1])) else: if 'Team' in pack_vals: params.append(('pack_team_id', pack_vals[pack_vals.index('Team') + 1])) if 'Cardset' in pack_vals: cardset_id = pack_vals[pack_vals.index('Cardset') + 1] params.append(('pack_cardset_id', cardset_id)) p_query = await db_get('packs', params=params) if p_query['count'] == 0: logger.error(f'open-packs - no packs found with params: {params}') raise ValueError(f'Unable to open packs') # Open the packs if open_type == 'standard': await open_st_pr_packs(p_query['packs'], self.owner_team, interaction) elif open_type == 'choice': await open_choice_pack(p_query['packs'][0], self.owner_team, interaction, cardset_id) class SelectPaperdexCardset(discord.ui.Select): def __init__(self): options = [ discord.SelectOption(label='2025 Live'), discord.SelectOption(label='1998 Season'), discord.SelectOption(label='2024 Season'), discord.SelectOption(label='2023 Season'), discord.SelectOption(label='2022 Season'), discord.SelectOption(label='2022 Promos'), discord.SelectOption(label='2021 Season'), discord.SelectOption(label='2019 Season'), discord.SelectOption(label='2018 Season'), discord.SelectOption(label='2016 Season'), discord.SelectOption(label='2013 Season'), discord.SelectOption(label='2012 Season'), discord.SelectOption(label='2008 Season'), discord.SelectOption(label='Mario Super Sluggers') ] super().__init__(placeholder='Select a Cardset', options=options) async def callback(self, interaction: discord.Interaction): logger.info(f'SelectPaperdexCardset - selection: {self.values[0]}') cardset_id = None if self.values[0] == '2022 Season': cardset_id = 3 elif self.values[0] == '2022 Promos': cardset_id = 4 elif self.values[0] == '2021 Season': cardset_id = 1 elif self.values[0] == '2019 Season': cardset_id = 5 elif self.values[0] == '2013 Season': cardset_id = 6 elif self.values[0] == '2012 Season': cardset_id = 7 elif self.values[0] == 'Mario Super Sluggers': cardset_id = 8 elif self.values[0] == '2023 Season': cardset_id = 9 elif self.values[0] == '2016 Season': cardset_id = 11 elif self.values[0] == '2008 Season': cardset_id = 12 elif self.values[0] == '2018 Season': cardset_id = 13 elif self.values[0] == '2024 Season': cardset_id = 17 elif self.values[0] == '2024 Promos': cardset_id = 18 elif self.values[0] == '1998 Season': cardset_id = 20 elif self.values[0] == '2025 Live': cardset_id = 24 c_query = await db_get('cardsets', object_id=cardset_id, none_okay=False) await interaction.response.edit_message(content=f'Okay, sifting through your cards...', view=None) cardset_embeds = await paperdex_cardset_embed( team=await get_team_by_owner(interaction.user.id), this_cardset=c_query ) await embed_pagination(cardset_embeds, interaction.channel, interaction.user) class SelectPaperdexTeam(discord.ui.Select): def __init__(self, which: Literal['AL', 'NL']): self.which = which if which == 'AL': options = [ discord.SelectOption(label='Baltimore Orioles'), discord.SelectOption(label='Boston Red Sox'), discord.SelectOption(label='Chicago White Sox'), discord.SelectOption(label='Cleveland Guardians'), discord.SelectOption(label='Detroit Tigers'), discord.SelectOption(label='Houston Astros'), discord.SelectOption(label='Kansas City Royals'), discord.SelectOption(label='Los Angeles Angels'), discord.SelectOption(label='Minnesota Twins'), discord.SelectOption(label='New York Yankees'), discord.SelectOption(label='Oakland Athletics'), discord.SelectOption(label='Seattle Mariners'), discord.SelectOption(label='Tampa Bay Rays'), discord.SelectOption(label='Texas Rangers'), discord.SelectOption(label='Toronto Blue Jays') ] else: options = [ discord.SelectOption(label='Arizona Diamondbacks'), discord.SelectOption(label='Atlanta Braves'), discord.SelectOption(label='Chicago Cubs'), discord.SelectOption(label='Cincinnati Reds'), discord.SelectOption(label='Colorado Rockies'), discord.SelectOption(label='Los Angeles Dodgers'), discord.SelectOption(label='Miami Marlins'), discord.SelectOption(label='Milwaukee Brewers'), discord.SelectOption(label='New York Mets'), discord.SelectOption(label='Philadelphia Phillies'), discord.SelectOption(label='Pittsburgh Pirates'), discord.SelectOption(label='San Diego Padres'), discord.SelectOption(label='San Francisco Giants'), discord.SelectOption(label='St. Louis Cardinals'), discord.SelectOption(label='Washington Nationals') ] super().__init__(placeholder=f'Select an {which} team', options=options) async def callback(self, interaction: discord.Interaction): team_id = None if self.which == 'AL': if self.values[0] == 'Baltimore Orioles': team_id = 3 elif self.values[0] == 'Boston Red Sox': team_id = 4 elif self.values[0] == 'Chicago White Sox': team_id = 6 elif self.values[0] == 'Cleveland Guardians': team_id = 8 elif self.values[0] == 'Detroit Tigers': team_id = 10 elif self.values[0] == 'Houston Astros': team_id = 11 elif self.values[0] == 'Kansas City Royals': team_id = 12 elif self.values[0] == 'Los Angeles Angels': team_id = 13 elif self.values[0] == 'Minnesota Twins': team_id = 17 elif self.values[0] == 'New York Yankees': team_id = 19 elif self.values[0] == 'Oakland Athletics': team_id = 20 elif self.values[0] == 'Seattle Mariners': team_id = 24 elif self.values[0] == 'Tampa Bay Rays': team_id = 27 elif self.values[0] == 'Texas Rangers': team_id = 28 elif self.values[0] == 'Toronto Blue Jays': team_id = 29 else: if self.values[0] == 'Arizona Diamondbacks': team_id = 1 elif self.values[0] == 'Atlanta Braves': team_id = 2 elif self.values[0] == 'Chicago Cubs': team_id = 5 elif self.values[0] == 'Cincinnati Reds': team_id = 7 elif self.values[0] == 'Colorado Rockies': team_id = 9 elif self.values[0] == 'Los Angeles Dodgers': team_id = 14 elif self.values[0] == 'Miami Marlins': team_id = 15 elif self.values[0] == 'Milwaukee Brewers': team_id = 16 elif self.values[0] == 'New York Mets': team_id = 18 elif self.values[0] == 'Philadelphia Phillies': team_id = 21 elif self.values[0] == 'Pittsburgh Pirates': team_id = 22 elif self.values[0] == 'San Diego Padres': team_id = 23 elif self.values[0] == 'San Francisco Giants': team_id = 25 elif self.values[0] == 'St. Louis Cardinals': team_id = 26 elif self.values[0] == 'Washington Nationals': team_id = 30 t_query = await db_get('teams', object_id=team_id, none_okay=False) await interaction.response.edit_message(content=f'Okay, sifting through your cards...', view=None) team_embeds = await paperdex_team_embed(team=await get_team_by_owner(interaction.user.id), mlb_team=t_query) await embed_pagination(team_embeds, interaction.channel, interaction.user) class SelectBuyPacksCardset(discord.ui.Select): def __init__(self, team: dict, quantity: int, pack_type_id: int, pack_embed: discord.Embed, cost: int): options = [ discord.SelectOption(label='2025 Live'), discord.SelectOption(label='1998 Season'), discord.SelectOption(label='Pokemon - Brilliant Stars'), discord.SelectOption(label='2024 Season'), discord.SelectOption(label='2023 Season'), discord.SelectOption(label='2022 Season'), discord.SelectOption(label='2021 Season'), discord.SelectOption(label='2019 Season'), discord.SelectOption(label='2018 Season'), discord.SelectOption(label='2016 Season'), discord.SelectOption(label='2013 Season'), discord.SelectOption(label='2012 Season'), discord.SelectOption(label='2008 Season') ] self.team = team self.quantity = quantity self.pack_type_id = pack_type_id self.pack_embed = pack_embed self.cost = cost super().__init__(placeholder='Select a Cardset', options=options) async def callback(self, interaction: discord.Interaction): logger.info(f'SelectBuyPacksCardset - selection: {self.values[0]}') cardset_id = None if self.values[0] == '2022 Season': cardset_id = 3 elif self.values[0] == '2021 Season': cardset_id = 1 elif self.values[0] == '2019 Season': cardset_id = 5 elif self.values[0] == '2013 Season': cardset_id = 6 elif self.values[0] == '2012 Season': cardset_id = 7 elif self.values[0] == '2023 Season': cardset_id = 9 elif self.values[0] == '2016 Season': cardset_id = 11 elif self.values[0] == '2008 Season': cardset_id = 12 elif self.values[0] == '2018 Season': cardset_id = 13 elif self.values[0] == '2024 Season': cardset_id = 17 elif self.values[0] == '1998 Season': cardset_id = 20 elif self.values[0] == '2025 Live': cardset_id = 24 elif self.values[0] == 'Pokemon - Brilliant Stars': cardset_id = 23 self.pack_embed.set_image(url=IMAGES['pack-pkmnbs']) self.pack_embed.description = f'{self.pack_embed.description} - {self.values[0]}' view = Confirm(responders=[interaction.user], timeout=30) await interaction.response.edit_message( content=None, embed=self.pack_embed, view=None ) question = await interaction.channel.send( content=f'Your Wallet: {self.team["wallet"]}₼\n' f'Pack{"s" if self.quantity > 1 else ""} Price: {self.cost}₼\n' f'After Purchase: {self.team["wallet"] - self.cost}₼\n\n' f'Would you like to make this purchase?', view=view ) await view.wait() if not view.value: await question.edit( content='Saving that money. Smart.', view=None ) return p_model = { 'team_id': self.team['id'], 'pack_type_id': self.pack_type_id, 'pack_cardset_id': cardset_id } await db_post('packs', payload={'packs': [p_model for x in range(self.quantity)]}) await db_post(f'teams/{self.team["id"]}/money/-{self.cost}') await question.edit( content=f'{"They are" if self.quantity > 1 else "It is"} all yours! Go rip \'em with `/open-packs`', view=None ) class SelectBuyPacksTeam(discord.ui.Select): def __init__( self, which: Literal['AL', 'NL'], team: dict, quantity: int, pack_type_id: int, pack_embed: discord.Embed, cost: int): self.which = which self.team = team self.quantity = quantity self.pack_type_id = pack_type_id self.pack_embed = pack_embed self.cost = cost if which == 'AL': options = [ discord.SelectOption(label='Baltimore Orioles'), discord.SelectOption(label='Boston Red Sox'), discord.SelectOption(label='Chicago White Sox'), discord.SelectOption(label='Cleveland Guardians'), discord.SelectOption(label='Detroit Tigers'), discord.SelectOption(label='Houston Astros'), discord.SelectOption(label='Kansas City Royals'), discord.SelectOption(label='Los Angeles Angels'), discord.SelectOption(label='Minnesota Twins'), discord.SelectOption(label='New York Yankees'), discord.SelectOption(label='Oakland Athletics'), discord.SelectOption(label='Seattle Mariners'), discord.SelectOption(label='Tampa Bay Rays'), discord.SelectOption(label='Texas Rangers'), discord.SelectOption(label='Toronto Blue Jays') ] else: options = [ discord.SelectOption(label='Arizona Diamondbacks'), discord.SelectOption(label='Atlanta Braves'), discord.SelectOption(label='Chicago Cubs'), discord.SelectOption(label='Cincinnati Reds'), discord.SelectOption(label='Colorado Rockies'), discord.SelectOption(label='Los Angeles Dodgers'), discord.SelectOption(label='Miami Marlins'), discord.SelectOption(label='Milwaukee Brewers'), discord.SelectOption(label='New York Mets'), discord.SelectOption(label='Philadelphia Phillies'), discord.SelectOption(label='Pittsburgh Pirates'), discord.SelectOption(label='San Diego Padres'), discord.SelectOption(label='San Francisco Giants'), discord.SelectOption(label='St. Louis Cardinals'), discord.SelectOption(label='Washington Nationals') ] super().__init__(placeholder=f'Select an {which} team', options=options) async def callback(self, interaction: discord.Interaction): team_id = None if self.which == 'AL': if self.values[0] == 'Baltimore Orioles': team_id = 3 elif self.values[0] == 'Boston Red Sox': team_id = 4 elif self.values[0] == 'Chicago White Sox': team_id = 6 elif self.values[0] == 'Cleveland Guardians': team_id = 8 elif self.values[0] == 'Detroit Tigers': team_id = 10 elif self.values[0] == 'Houston Astros': team_id = 11 elif self.values[0] == 'Kansas City Royals': team_id = 12 elif self.values[0] == 'Los Angeles Angels': team_id = 13 elif self.values[0] == 'Minnesota Twins': team_id = 17 elif self.values[0] == 'New York Yankees': team_id = 19 elif self.values[0] == 'Oakland Athletics': team_id = 20 elif self.values[0] == 'Seattle Mariners': team_id = 24 elif self.values[0] == 'Tampa Bay Rays': team_id = 27 elif self.values[0] == 'Texas Rangers': team_id = 28 elif self.values[0] == 'Toronto Blue Jays': team_id = 29 else: if self.values[0] == 'Arizona Diamondbacks': team_id = 1 elif self.values[0] == 'Atlanta Braves': team_id = 2 elif self.values[0] == 'Chicago Cubs': team_id = 5 elif self.values[0] == 'Cincinnati Reds': team_id = 7 elif self.values[0] == 'Colorado Rockies': team_id = 9 elif self.values[0] == 'Los Angeles Dodgers': team_id = 14 elif self.values[0] == 'Miami Marlins': team_id = 15 elif self.values[0] == 'Milwaukee Brewers': team_id = 16 elif self.values[0] == 'New York Mets': team_id = 18 elif self.values[0] == 'Philadelphia Phillies': team_id = 21 elif self.values[0] == 'Pittsburgh Pirates': team_id = 22 elif self.values[0] == 'San Diego Padres': team_id = 23 elif self.values[0] == 'San Francisco Giants': team_id = 25 elif self.values[0] == 'St. Louis Cardinals': team_id = 26 elif self.values[0] == 'Washington Nationals': team_id = 30 self.pack_embed.description = f'{self.pack_embed.description} - {self.values[0]}' view = Confirm(responders=[interaction.user], timeout=30) await interaction.response.edit_message( content=None, embed=self.pack_embed, view=None ) question = await interaction.channel.send( content=f'Your Wallet: {self.team["wallet"]}₼\n' f'Pack{"s" if self.quantity > 1 else ""} Price: {self.cost}₼\n' f'After Purchase: {self.team["wallet"] - self.cost}₼\n\n' f'Would you like to make this purchase?', view=view ) await view.wait() if not view.value: await question.edit( content='Saving that money. Smart.', view=None ) return p_model = { 'team_id': self.team['id'], 'pack_type_id': self.pack_type_id, 'pack_team_id': team_id } await db_post('packs', payload={'packs': [p_model for x in range(self.quantity)]}) await db_post(f'teams/{self.team["id"]}/money/-{self.cost}') await question.edit( content=f'{"They are" if self.quantity > 1 else "It is"} all yours! Go rip \'em with `/open-packs`', view=None ) class SelectUpdatePlayerTeam(discord.ui.Select): def __init__(self, which: Literal['AL', 'NL'], player: dict, reporting_team: dict, bot): self.bot = bot self.which = which self.player = player self.reporting_team = reporting_team if which == 'AL': options = [ discord.SelectOption(label='Baltimore Orioles'), discord.SelectOption(label='Boston Red Sox'), discord.SelectOption(label='Chicago White Sox'), discord.SelectOption(label='Cleveland Guardians'), discord.SelectOption(label='Detroit Tigers'), discord.SelectOption(label='Houston Astros'), discord.SelectOption(label='Kansas City Royals'), discord.SelectOption(label='Los Angeles Angels'), discord.SelectOption(label='Minnesota Twins'), discord.SelectOption(label='New York Yankees'), discord.SelectOption(label='Oakland Athletics'), discord.SelectOption(label='Seattle Mariners'), discord.SelectOption(label='Tampa Bay Rays'), discord.SelectOption(label='Texas Rangers'), discord.SelectOption(label='Toronto Blue Jays') ] else: options = [ discord.SelectOption(label='Arizona Diamondbacks'), discord.SelectOption(label='Atlanta Braves'), discord.SelectOption(label='Chicago Cubs'), discord.SelectOption(label='Cincinnati Reds'), discord.SelectOption(label='Colorado Rockies'), discord.SelectOption(label='Los Angeles Dodgers'), discord.SelectOption(label='Miami Marlins'), discord.SelectOption(label='Milwaukee Brewers'), discord.SelectOption(label='New York Mets'), discord.SelectOption(label='Philadelphia Phillies'), discord.SelectOption(label='Pittsburgh Pirates'), discord.SelectOption(label='San Diego Padres'), discord.SelectOption(label='San Francisco Giants'), discord.SelectOption(label='St. Louis Cardinals'), discord.SelectOption(label='Washington Nationals') ] super().__init__(placeholder=f'Select an {which} team', options=options) async def callback(self, interaction: discord.Interaction): if self.values[0] == self.player['franchise'] or self.values[0] == self.player['mlbclub']: await interaction.response.send_message( content=f'Thank you for the help, but it looks like somebody beat you to it! ' f'**{player_desc(self.player)}** is already assigned to the **{self.player["mlbclub"]}**.' ) return view = Confirm(responders=[interaction.user], timeout=15) await interaction.response.edit_message( content=f'Should I update **{player_desc(self.player)}**\'s team to the **{self.values[0]}**?', view=None ) question = await interaction.channel.send( content=None, view=view ) await view.wait() if not view.value: await question.edit( content='That didnt\'t sound right to me, either. Let\'s not touch that.', view=None ) return else: await question.delete() await db_patch('players', object_id=self.player['player_id'], params=[ ('mlbclub', self.values[0]), ('franchise', self.values[0]) ]) await db_post(f'teams/{self.reporting_team["id"]}/money/25') await send_to_channel( self.bot, 'pd-news-ticker', content=f'{interaction.user.name} just updated **{player_desc(self.player)}**\'s team to the ' f'**{self.values[0]}**' ) await interaction.channel.send(f'All done!') class SelectView(discord.ui.View): def __init__(self, select_objects: list[discord.ui.Select], timeout: float = 300.0): super().__init__(timeout=timeout) for x in select_objects: self.add_item(x) class Dropdown(discord.ui.Select): def __init__(self, option_list: list, placeholder: str = 'Make your selection', min_values: int = 1, max_values: int = 1, callback=None): # Set the options that will be presented inside the dropdown # options = [ # discord.SelectOption(label='Red', description='Your favourite colour is red', emoji='🟥'), # discord.SelectOption(label='Green', description='Your favourite colour is green', emoji='🟩'), # discord.SelectOption(label='Blue', description='Your favourite colour is blue', emoji='🟦'), # ] # The placeholder is what will be shown when no option is chosen # The min and max values indicate we can only pick one of the three options # The options parameter defines the dropdown options. We defined this above # If a default option is set on any SelectOption, the View will not process if only the default is # selected by the user self.custom_callback = callback super().__init__( placeholder=placeholder, min_values=min_values, max_values=max_values, options=option_list ) async def callback(self, interaction: discord.Interaction): # Use the interaction object to send a response message containing # the user's favourite colour or choice. The self object refers to the # Select object, and the values attribute gets a list of the user's # selected options. We only want the first one. # await interaction.response.send_message(f'Your favourite colour is {self.values[0]}') logger.info(f'Dropdown callback: {self.custom_callback}') await self.custom_callback(interaction, self.values) class DropdownView(discord.ui.View): def __init__(self, dropdown_objects: list[Dropdown], timeout: float = 300.0): super().__init__(timeout=timeout) # self.add_item(Dropdown()) for x in dropdown_objects: self.add_item(x) def random_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://i0.wp.com/media1.giphy.com/media/iwvuPyfi7z14I/giphy.gif', 'https://media1.tenor.com/images/859a2d3b201fbacec13904242976b9e0/tenor.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_no_gif(): no_gifs = [ 'https://tenor.com/view/youre-not-my-dad-dean-jensen-ackles-supernatural-you-arent-my-dad-gif-19503399', 'https://tenor.com/view/youre-not-my-dad-kid-gif-8300190', 'https://tenor.com/view/youre-not-my-supervisor-youre-not-my-boss-gif-12971403', 'https://tenor.com/view/dont-tell-me-what-to-do-gif-4951202' ] return no_gifs[random.randint(0, len(no_gifs) - 1)] 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', ] async def send_to_bothole(ctx, content, embed): await discord.utils.get(ctx.guild.text_channels, name='pd-bot-hole') \ .send(content=content, embed=embed) async def send_to_news(ctx, content, embed): await discord.utils.get(ctx.guild.text_channels, name='pd-news-ticker') \ .send(content=content, embed=embed) async def typing_pause(ctx, seconds=1): async with ctx.typing(): await asyncio.sleep(seconds) async def pause_then_type(ctx, message): async with ctx.typing(): await asyncio.sleep(len(message) / 100) await ctx.send(message) async def check_if_pdhole(ctx): if ctx.message.channel.name != 'pd-bot-hole': await ctx.send('Slide on down to my bot-hole for running commands.') await ctx.message.add_reaction('❌') return False return True def get_roster_sheet_legacy(team): return f'https://docs.google.com/spreadsheets/d/{team.gsheet}/edit' def get_special_embed(special): embed = discord.Embed(title=f'{special.name} - Special #{special.get_id()}', color=discord.Color.random(), description=f'{special.short_desc}') embed.add_field(name='Description', value=f'{special.long_desc}', inline=False) # embed.add_field(name='To Redeem', value=f'Run .redeem {special.get_id()}', inline=False) if special.thumbnail.lower() != 'none': embed.set_thumbnail(url=f'{special.thumbnail}') if special.url.lower() != 'none': embed.set_image(url=f'{special.url}') return embed def get_random_embed(title, thumb=None): embed = discord.Embed(title=title, color=discord.Color.random()) if thumb: embed.set_thumbnail(url=thumb) return embed async def get_player_photo(player): search_term = player['bbref_id'] if player['bbref_id'] else player['p_name'] req_url = f'https://www.thesportsdb.com/api/v1/json/1/searchplayers.php?p={search_term}' try: resp = requests.get(req_url, timeout=.5) except Exception as e: return None if resp.status_code == 200 and resp.json()['player']: if resp.json()['player'][0]['strSport'] == 'Baseball': await db_patch('players', object_id=player['player_id'], params=[('headshot', resp.json()['player'][0]['strThumb'])]) return resp.json()['player'][0]['strThumb'] return None async def get_player_headshot(player): search_term = player['bbref_id'] if player['bbref_id'] else player['p_name'] req_url = f'https://www.baseball-reference.com/search/search.fcgi?search={search_term}' try: resp = requests.get(req_url, timeout=2).text soup = BeautifulSoup(resp, 'html.parser') for item in soup.find_all('img'): if 'headshot' in item['src']: await db_patch('players', object_id=player['player_id'], params=[('headshot', item['src'])]) return item['src'] except: pass return await get_player_photo(player) def fuzzy_search(name, master_list): if name.lower() in master_list: return name.lower() 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') return matches[0] async def fuzzy_player_search(ctx, channel, bot, name, master_list): """ 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: """ matches = fuzzy_search(name, master_list) 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([ctx.author]) if not resp: return None if resp < count: return matches[resp - 1] else: raise ValueError(f'{resp} is not a valid response.') async def create_channel_old( ctx, channel_name: str, category_name: str, everyone_send=False, everyone_read=True, allowed_members=None, allowed_roles=None): 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 allowed_members: if isinstance(allowed_members, list): for member in allowed_members: overwrites[member] = discord.PermissionOverwrite(read_messages=True, send_messages=True) if allowed_roles: if isinstance(allowed_roles, list): for role in allowed_roles: overwrites[role] = discord.PermissionOverwrite(read_messages=True, send_messages=True) this_channel = await ctx.guild.create_text_channel( channel_name, overwrites=overwrites, category=this_category ) logger.info(f'Creating channel ({channel_name}) in ({category_name})') return this_channel async def react_and_reply(ctx, reaction, message): await ctx.message.add_reaction(reaction) await ctx.send(message) 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: logger.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') return await this_channel.send(content=content, embed=embed) async def get_or_create_role(ctx, role_name, mentionable=True): this_role = discord.utils.get(ctx.guild.roles, name=role_name) # logger.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) # logger.info(f'this_role: {this_role} / role_name: {role_name} (PRE RETURN)') return this_role """ NEW FOR SEASON 4 """ 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"] else int(SBA_COLOR, 16) ) embed.set_footer(text=f'Paper Dynasty Season {team["season"]}', icon_url=IMAGES['logo']) if thumbnail: embed.set_thumbnail(url=team["logo"] if team["logo"] else IMAGES['logo']) else: embed = discord.Embed( title=title, color=int(SBA_COLOR, 16) ) embed.set_footer(text=f'Paper Dynasty Season {PD_SEASON}', icon_url=IMAGES['logo']) if thumbnail: embed.set_thumbnail(url=IMAGES['logo']) return embed async def get_team_by_owner(owner_id: int): team = await db_get('teams', params=[('gm_id', owner_id)]) if not team['count']: return None return team['teams'][0] async def team_role(ctx, team: Team): return await get_or_create_role(ctx, f'{team.abbrev} - {team.lname}') def get_cal_user(ctx): return ctx.guild.get_member(258104532423147520) 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 def get_all_pos(player): all_pos = [] for x in range(1, 8): if player[f'pos_{x}']: all_pos.append(player[f'pos_{x}']) return all_pos async def create_channel( ctx, channel_name: str, category_name: str, everyone_send=False, everyone_read=True, read_send_members: list = None, read_send_roles: list = None, read_only_roles: list = None): 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) this_channel = await ctx.guild.create_text_channel( channel_name, overwrites=overwrites, category=this_category ) logger.info(f'Creating channel ({channel_name}) in ({category_name})') return this_channel async def share_channel(channel, user, read_only=False): await channel.set_permissions(user, read_messages=True, send_messages=not read_only) async def get_card_embeds(card, include_stats=False) -> list: embed = discord.Embed( title=f'{card["player"]["p_name"]}', color=int(card['player']['rarity']['color'], 16) ) # embed.description = card['team']['lname'] embed.description = f'{card["player"]["cardset"]["name"]} / {card["player"]["mlbclub"]}' embed.set_author(name=card['team']['lname'], url=IMAGES['logo'], icon_url=card['team']['logo']) embed.set_footer(text=f'Paper Dynasty Season {card["team"]["season"]}', icon_url=IMAGES['logo']) if include_stats: b_query = await db_get( 'plays/batting', params=[('player_id', card['player']['player_id']), ('season', PD_SEASON)]) p_query = await db_get( 'plays/pitching', params=[('player_id', card['player']['player_id']), ('season', PD_SEASON)]) embed.add_field(name='Player ID', value=f'{card["player"]["player_id"]}') embed.add_field(name='Rarity', value=f'{card["player"]["rarity"]["name"]}') embed.add_field(name='Cost', value=f'{card["player"]["cost"]}₼') pos_string = ", ".join(get_all_pos(card['player'])) embed.add_field(name='Positions', value=pos_string) # all_dex = card['player']['paperdex'] all_dex = await db_get('paperdex', params=[("player_id", card["player"]["player_id"]), ('flat', True)]) count = all_dex['count'] if card['team']['lname'] != 'Paper Dynasty': bool_list = [True for elem in all_dex['paperdex'] if elem['team'] == card['team']['id']] if any(bool_list): if count == 1: coll_string = f'Only you' else: coll_string = f'You and {count - 1} other{"s" if count - 1 != 1 else ""}' elif count: coll_string = f'{count} other team{"s" if count != 1 else ""}' else: coll_string = f'0 teams' embed.add_field(name='Collected By', value=coll_string) else: embed.add_field(name='Collected By', value=f'{count} team{"s" if count != 1 else ""}') # TODO: check for dupes with the included paperdex data # if card['team']['lname'] != 'Paper Dynasty': # team_dex = await db_get('cards', params=[("player_id", card["player"]["player_id"]), ('team_id', card['team']['id'])]) # count = 1 if not team_dex['count'] else team_dex['count'] # embed.add_field(name='# Dupes', value=f'{count - 1} dupe{"s" if count - 1 != 1 else ""}') # embed.add_field(name='Team', value=f'{card["player"]["mlbclub"]}') if card['player']['franchise'] != 'Pokemon': player_pages = f'[BBRef]({get_player_url(card["player"], "bbref")})' else: player_pages = f'[Pkmn]({PKMN_REF_URL}{card["player"]["bbref_id"]})' embed.add_field(name='Player Page', value=f'{player_pages}') embed.set_image(url=card["player"]["image"]) headshot = card['player']['headshot'] if card['player']['headshot'] else await get_player_headshot(card['player']) if headshot: embed.set_thumbnail(url=headshot) else: embed.set_thumbnail(url=IMAGES['logo']) if card['player']['franchise'] == 'Pokemon': if card['player']['fangr_id'] is not None: try: evo_mon = await db_get('players', object_id=card['player']['fangr_id'], none_okay=True) if evo_mon is not None: embed.add_field( name='Evolves Into', value=f'{evo_mon["p_name"]}' ) except Exception as e: logging.error('could not pull evolution: {e}', exc_info=True, stack_info=True) if '420420' not in card['player']['strat_code']: try: evo_mon = await db_get('players', object_id=card['player']['strat_code'], none_okay=True) if evo_mon is not None: embed.add_field( name='Evolves From', value=f'{evo_mon["p_name"]}' ) except Exception as e: logging.error('could not pull evolution: {e}', exc_info=True, stack_info=True) if include_stats: if b_query['count'] > 0: b = b_query['stats'][0] re24 = f'{b["re24"]:.2f}' batting_string = f'```\n' \ f' AVG OBP SLG\n' \ f' {b["avg"]:.3f} {b["obp"]:.3f} {b["slg"]:.3f}\n``````\n' \ f' OPS wOBA RE24\n' \ f' {b["ops"]:.3f} {b["woba"]:.3f} {re24: ^5}\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' embed.add_field(name='Batting Stats', value=batting_string, inline=False) if p_query['count'] > 0: p = p_query['stats'][0] 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}' re24 = f'{p["re24"]:.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 RE24\n' \ f'{ips: >5} {p["so"]: ^3} {kpbb: ^4} {re24: ^5}\n```' embed.add_field(name='Pitching Stats', value=pitching_string, inline=False) if not card['player']['image2']: return [embed] card_two = discord.Embed(color=int(card['player']['rarity']['color'], 16)) card_two.set_footer(text=f'Paper Dynasty Season {card["team"]["season"]}', icon_url=IMAGES['logo']) card_two.set_image(url=card['player']['image2']) return [embed, card_two] def image_embed(image_url: str, title: str = None, color: str = None, desc: str = None, author_name: str = None, author_icon: str = None): embed_color = int(SBA_COLOR, 16) if color is not None: embed_color = int(color, 16) embed = discord.Embed(color=embed_color) if title is not None: embed.title = title if desc is not None: embed.description = desc if author_name is not None: icon = author_icon if author_icon is not None else IMAGES['logo'] embed.set_author(name=author_name, icon_url=icon) embed.set_footer(text=f'Paper Dynasty Season {PD_SEASON}', icon_url=IMAGES['logo']) embed.set_image(url=image_url) return embed def is_shiny(card): if card['player']['rarity']['value'] >= 5: return True return False async def display_cards( cards: list, team: dict, channel, user, bot=None, pack_cover: str = None, cust_message: str = None, add_roster: bool = True, pack_name: str = None) -> bool: cards.sort(key=lambda x: x['player']['rarity']['value']) card_embeds = [await get_card_embeds(x) for x in cards] page_num = 0 if pack_cover is None else -1 seen_shiny = False view = Pagination([user], timeout=10) l_emoji = await get_emoji(channel.guild, 'arrow_left') r_emoji = await get_emoji(channel.guild, 'arrow_right') view.left_button.disabled = True view.left_button.label = f'{l_emoji}Prev: -/{len(card_embeds)}' view.cancel_button.label = f'Close Pack' view.right_button.label = f'Next: {page_num + 2}/{len(card_embeds)}{r_emoji}' if len(cards) == 1: view.right_button.disabled = True if pack_cover: msg = await channel.send( content=None, embed=image_embed(pack_cover, title=f'{team["lname"]}', desc=pack_name), view=view ) else: msg = await channel.send(content=None, embeds=card_embeds[page_num], view=view) if cust_message: follow_up = await channel.send(cust_message) else: follow_up = await channel.send(f'{user.mention} you\'ve got {len(cards)} cards here') while True: await view.wait() if view.value: if view.value == 'cancel': await msg.edit(view=None) if add_roster: await follow_up.edit(content=f'Refresh your cards here: {get_roster_sheet(team)}') return True if view.value == 'left': page_num -= 1 if page_num > 0 else 0 if view.value == 'right': page_num += 1 if page_num <= len(card_embeds) else len(card_embeds) else: if page_num == len(card_embeds) - 1: await msg.edit(view=None) if add_roster: await follow_up.edit(content=f'Refresh your cards here: {get_roster_sheet(team)}') return True else: page_num += 1 view.value = None if is_shiny(cards[page_num]) and not seen_shiny: seen_shiny = True view = Pagination([user], timeout=300) view.cancel_button.style = discord.ButtonStyle.success view.cancel_button.label = 'Flip!' view.left_button.label = '-' view.right_button.label = '-' view.left_button.disabled = True view.right_button.disabled = True await msg.edit( embed=image_embed( IMAGES['mvp'][cards[page_num]["player"]["franchise"]], color='56f1fa', author_name=team['lname'], author_icon=team['logo'] ), view=view) tmp_msg = await channel.send(content=f'<@&1163537676885033010> we\'ve got an MVP!') await follow_up.edit(content=f'<@&1163537676885033010> we\'ve got an MVP!') await tmp_msg.delete() await view.wait() view = Pagination([user], timeout=10) view.right_button.label = f'Next: {page_num + 2}/{len(card_embeds)}{r_emoji}' view.cancel_button.label = f'Close Pack' view.left_button.label = f'{l_emoji}Prev: {page_num}/{len(card_embeds)}' if page_num == 0: view.left_button.label = f'{l_emoji}Prev: -/{len(card_embeds)}' view.left_button.disabled = True elif page_num == len(card_embeds) - 1: view.timeout = 600.0 view.right_button.label = f'Next: -/{len(card_embeds)}{r_emoji}' view.right_button.disabled = True await msg.edit(content=None, embeds=card_embeds[page_num], view=view) async def embed_pagination( all_embeds: list, channel, user: discord.Member, custom_message: str = None, timeout: int = 10, start_page: int = 0): if start_page > len(all_embeds) - 1 or start_page < 0: page_num = 0 else: page_num = start_page view = Pagination([user], timeout=timeout) l_emoji = '' r_emoji = '' view.right_button.label = f'Next: {page_num + 2}/{len(all_embeds)}{r_emoji}' view.cancel_button.label = f'Cancel' view.left_button.label = f'{l_emoji}Prev: {page_num}/{len(all_embeds)}' if page_num == 0: view.left_button.label = f'{l_emoji}Prev: -/{len(all_embeds)}' view.left_button.disabled = True elif page_num == len(all_embeds) - 1: view.right_button.label = f'Next: -/{len(all_embeds)}{r_emoji}' view.right_button.disabled = True msg = await channel.send(content=custom_message, embed=all_embeds[page_num], view=view) while True: await view.wait() if view.value: if view.value == 'cancel': await msg.edit(view=None) return True if view.value == 'left': page_num -= 1 if page_num > 0 else 0 if view.value == 'right': page_num += 1 if page_num <= len(all_embeds) else len(all_embeds) else: if page_num == len(all_embeds) - 1: await msg.edit(view=None) return True else: page_num += 1 view.value = None view = Pagination([user], timeout=timeout) view.right_button.label = f'Next: {page_num + 2}/{len(all_embeds)}{r_emoji}' view.cancel_button.label = f'Cancel' view.left_button.label = f'{l_emoji}Prev: {page_num}/{len(all_embeds)}' if page_num == 0: view.left_button.label = f'{l_emoji}Prev: -/{len(all_embeds)}' view.left_button.disabled = True elif page_num == len(all_embeds) - 1: view.timeout = 600.0 view.right_button.label = f'Next: -/{len(all_embeds)}{r_emoji}' view.right_button.disabled = True await msg.edit(content=None, embed=all_embeds[page_num], view=view) def get_roster_sheet(team, allow_embed: bool = False): return f'{"" if allow_embed else "<"}' \ f'https://docs.google.com/spreadsheets/d/{team["gsheet"]}/edit' \ f'{"" if allow_embed else ">"}' def get_player_url(player, which="sba"): if which == 'bbref': return f'https://www.baseball-reference.com/search/search.fcgi?search={player["bbref_id"]}' else: stub_name = player["p_name"].replace(" ", "%20") return f'https://sombaseball.ddns.net/players?name={stub_name}' async def bad_channel(ctx): bad_channels = ['paper-dynasty-chat', 'pd-news-ticker'] if ctx.message.channel.name in bad_channels: await ctx.message.add_reaction('❌') bot_hole = discord.utils.get( ctx.guild.text_channels, name=f'pd-bot-hole' ) await ctx.send(f'Slide on down to the {bot_hole.mention} ;)') return True else: return False def get_channel(ctx, name) -> Optional[discord.TextChannel]: channel = discord.utils.get( ctx.guild.text_channels, name=name ) if channel: return channel return None async def get_test_pack(ctx, team): pull_notifs = [] this_pack = await db_post('packs/one', payload={ 'team_id': team['id'], 'pack_type_id': 1, 'open_time': int(datetime.datetime.timestamp(datetime.datetime.now())*1000) }) ft_query = await db_get('players/random', params=[('max_rarity', 1), ('limit', 3)]) four_query = await db_get('players/random', params=[('min_rarity', 1), ('max_rarity', 3), ('limit', 1)]) five_query = await db_get('players/random', params=[('min_rarity', 5), ('max_rarity', 5), ('limit', 1)]) first_three = ft_query['players'] fourth = four_query['players'] fifth = five_query['players'] all_cards = [*first_three, *fourth, *fifth] success = await db_post('cards', timeout=10, payload={'cards': [{ 'player_id': x['player_id'], 'team_id': team['id'], 'pack_id': this_pack['id']} for x in all_cards] }) if not success: await ctx.send(f'I was not able to create these cards {get_emoji(ctx, "slight_frown")}') return for x in all_cards: if x['rarity']['value'] >= 3: pull_notifs.append(x) for pull in pull_notifs: await db_post('notifs', payload={ 'created': int(datetime.datetime.timestamp(datetime.datetime.now())*1000), 'title': 'Rare Pull', 'field_name': f'{player_desc(pull)} ({pull["rarity"]["name"]})', 'message': f'Pulled by {team["abbrev"]}', 'about': f'Player-{pull["player_id"]}' }) return [{'player': x, 'team': team} for x in all_cards] async def roll_for_cards(all_packs: list, extra_val=None) -> list: """ Pack odds are calculated based on the pack type Parameters ---------- extra_val all_packs Returns ------- """ all_players = [] team = all_packs[0]['team'] pack_ids = [] for pack in all_packs: counts = { 'Rep': { 'count': 0, 'rarity': 0 }, 'Res': { 'count': 0, 'rarity': 1 }, 'Sta': { 'count': 0, 'rarity': 2 }, 'All': { 'count': 0, 'rarity': 3 }, 'MVP': { 'count': 0, 'rarity': 5 }, 'HoF': { 'count': 0, 'rarity': 8 }, } this_pack_players = [] if pack['pack_type']['name'] == 'Standard': # Cards 1 - 2 for x in range(2): d_1000 = random.randint(1, 1000) if d_1000 <= 450: counts['Rep']['count'] += 1 elif d_1000 <= 900: counts['Res']['count'] += 1 else: counts['Sta']['count'] += 1 # Card 3 d_1000 = random.randint(1, 1000) if d_1000 <= 350: counts['Rep']['count'] += 1 elif d_1000 <= 700: counts['Res']['count'] += 1 elif d_1000 <= 950: counts['Sta']['count'] += 1 else: counts['All']['count'] += 1 # Card 4 d_1000 = random.randint(1, 1000) if d_1000 <= 310: counts['Rep']['count'] += 1 elif d_1000 <= 620: counts['Res']['count'] += 1 elif d_1000 <= 940: counts['Sta']['count'] += 1 elif d_1000 <= 990: counts['All']['count'] += 1 else: counts['MVP']['count'] += 1 # Card 5 d_1000 = random.randint(1, 1000) if d_1000 <= 215: counts['Rep']['count'] += 1 elif d_1000 <= 430: counts['Res']['count'] += 1 elif d_1000 <= 930: counts['Sta']['count'] += 1 elif d_1000 <= 980: counts['All']['count'] += 1 elif d_1000 <= 990: counts['MVP']['count'] += 1 else: counts['HoF']['count'] += 1 elif pack['pack_type']['name'] == 'Premium': # Card 1 d_1000 = random.randint(1, 1000) if d_1000 <= 400: counts['Rep']['count'] += 1 elif d_1000 <= 870: counts['Res']['count'] += 1 elif d_1000 <= 970: counts['Sta']['count'] += 1 elif d_1000 <= 990: counts['All']['count'] += 1 else: counts['MVP']['count'] += 1 # Card 2 d_1000 = random.randint(1, 1000) if d_1000 <= 300: counts['Rep']['count'] += 1 elif d_1000 <= 770: counts['Res']['count'] += 1 elif d_1000 <= 970: counts['Sta']['count'] += 1 elif d_1000 <= 990: counts['All']['count'] += 1 else: counts['MVP']['count'] += 1 # Card 3 d_1000 = random.randint(1, 1000) if d_1000 <= 200: counts['Rep']['count'] += 1 elif d_1000 <= 640: counts['Res']['count'] += 1 elif d_1000 <= 940: counts['Sta']['count'] += 1 elif d_1000 <= 990: counts['All']['count'] += 1 else: counts['MVP']['count'] += 1 # Card 4 d_1000 = random.randint(1, 1000) if d_1000 <= 100: counts['Rep']['count'] += 1 if d_1000 <= 530: counts['Res']['count'] += 1 elif d_1000 <= 930: counts['Sta']['count'] += 1 elif d_1000 <= 980: counts['All']['count'] += 1 elif d_1000 <= 990: counts['MVP']['count'] += 1 else: counts['HoF']['count'] += 1 # Card 5 d_1000 = random.randint(1, 1000) if d_1000 <= 380: counts['Res']['count'] += 1 elif d_1000 <= 880: counts['Sta']['count'] += 1 elif d_1000 <= 980: counts['All']['count'] += 1 elif d_1000 <= 990: counts['MVP']['count'] += 1 else: counts['HoF']['count'] += 1 elif pack['pack_type']['name'] == 'Check-In Player': logger.info(f'Building Check-In Pack // extra_val (type): {extra_val} {type(extra_val)}') # Single Card mod = 0 if isinstance(extra_val, int): mod = extra_val d_1000 = random.randint(1, 1000 + mod) if d_1000 >= 1100: counts['All']['count'] += 1 elif d_1000 >= 1000: counts['Sta']['count'] += 1 elif d_1000 >= 500: counts['Res']['count'] += 1 else: counts['Rep']['count'] += 1 else: raise TypeError(f'Pack type not recognized: {pack["pack_type"]["name"]}') pull_notifs = [] for key in counts: mvp_flag = None if counts[key]['count'] > 0: params = [ ('min_rarity', counts[key]['rarity']), ('max_rarity', counts[key]['rarity']), ('limit', counts[key]['count']) ] if all_packs[0]['pack_team'] is not None: params.extend([('franchise', all_packs[0]['pack_team']['lname']), ('in_packs', True)]) elif all_packs[0]['pack_cardset'] is not None: params.append(('cardset_id', all_packs[0]['pack_cardset']['id'])) else: params.append(('in_packs', True)) pl = await db_get('players/random', params=params) if pl['count'] != counts[key]['count']: mvp_flag = counts[key]['count'] - pl['count'] logging.info(f'Set mvp flag to {mvp_flag} / cardset_id: {all_packs[0]["pack_cardset"]["id"]}') for x in pl['players']: this_pack_players.append(x) all_players.append(x) if x['rarity']['value'] >= 3: pull_notifs.append(x) if mvp_flag and all_packs[0]['pack_cardset']['id'] not in [23]: logging.info(f'Adding {mvp_flag} MVPs for missing cards') pl = await db_get('players/random', params=[('min_rarity', 5), ('limit', mvp_flag)]) for x in pl['players']: this_pack_players.append(x) all_players.append(x) # Add dupes of Replacement/Reserve cards elif mvp_flag: logging.info(f'Adding {mvp_flag} duplicate pokemon cards') for count in range(mvp_flag): logging.info(f'Adding {pl["players"][0]["p_name"]} to the pack') this_pack_players.append(x) all_players.append(pl['players'][0]) success = await db_post( 'cards', payload={'cards': [{ 'player_id': x['player_id'], 'team_id': pack['team']['id'], 'pack_id': pack['id']} for x in this_pack_players] }, timeout=10 ) if not success: raise ConnectionError(f'Failed to create this pack of cards.') await db_patch('packs', object_id=pack['id'], params=[ ('open_time', int(datetime.datetime.timestamp(datetime.datetime.now())*1000)) ]) pack_ids.append(pack['id']) for pull in pull_notifs: logger.info(f'good pull: {pull}') await db_post('notifs', payload={ 'created': int(datetime.datetime.timestamp(datetime.datetime.now())*1000), 'title': 'Rare Pull', 'field_name': f'{player_desc(pull)} ({pull["rarity"]["name"]})', 'message': f'Pulled by {team["abbrev"]}', 'about': f'Player-{pull["player_id"]}' }) return pack_ids async def give_packs(team: dict, num_packs: int, pack_type: dict = None) -> dict: """ Parameters ---------- pack_type team num_packs Returns ------- { 'count': int, 'packs': [ all team packs ] } """ pt_id = pack_type['id'] if pack_type is not None else 1 await db_post( 'packs', payload={'packs': [{'team_id': team['id'], 'pack_type_id': pt_id} for x in range(num_packs)]} ) total_packs = await db_get('packs', params=[ ('team_id', team['id']), ('opened', False) ]) return total_packs def get_sheets(bot): try: return bot.get_cog('Gameplay').sheets except Exception as e: logger.error(f'Could not grab sheets auth: {e}') raise ConnectionError(f'Bot has not authenticated with discord; please try again in 1 minute.') def create_team_sheet(team, email: str, current, bot): sheets = get_sheets(bot) new_sheet = sheets.drive.copy_file( f'{current["gsheet_template"]}', f'{team["lname"]} Roster Sheet v{current["gsheet_version"]}', '1539D0imTMjlUx2VF3NPMt7Sv85sb2XAJ' ) logger.info(f'new_sheet: {new_sheet}') this_sheet = sheets.open_by_key(new_sheet['id']) this_sheet.share(email, role='writer') team_data = this_sheet.worksheet_by_title('Team Data') team_data.update_values( crange='B1:B2', values=[[f'{team["id"]}'], [f'\'{team_hash(team)}']] ) logger.debug(f'this_sheet: {this_sheet}') return this_sheet async def refresh_sheet(team, bot, sheets=None) -> None: return if not sheets: sheets = get_sheets(bot) this_sheet = sheets.open_by_key(team['gsheet']) my_cards = this_sheet.worksheet_by_title('My Cards') all_cards = this_sheet.worksheet_by_title('All Cards') my_cards.update_value('A2', 'FALSE') all_cards.update_value('A2', 'FALSE') await asyncio.sleep(1) my_cards.update_value('A2', 'TRUE') await asyncio.sleep(0.5) all_cards.update_value('A2', 'TRUE') def delete_sheet(team, bot): sheets = get_sheets(bot) this_sheet = sheets.open_by_key(team['gsheet']) this_sheet.delete() def share_sheet(team, email, bot) -> None: sheets = get_sheets(bot) this_sheet = sheets.open_by_key(team['gsheet']) this_sheet.share(email, role='writer') def int_timestamp(datetime_obj: datetime.datetime) -> int: return int(datetime.datetime.timestamp(datetime_obj) * 1000) 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') async def cardset_search(cardset: str, cardset_list: list) -> Optional[dict]: cardset_name = fuzzy_search(cardset, cardset_list) if not cardset_name: return None c_query = await db_get('cardsets', params=[('name', cardset_name)]) if c_query['count'] == 0: return None return c_query['cardsets'][0] def get_blank_team_card(player): return {'player': player, 'team': {'lname': 'Paper Dynasty', 'logo': IMAGES['logo'], 'season': PD_SEASON}} def get_rosters(team, bot, roster_num: Optional[int] = None) -> list: sheets = get_sheets(bot) this_sheet = sheets.open_by_key(team['gsheet']) r_sheet = this_sheet.worksheet_by_title(f'My Rosters') logger.debug(f'this_sheet: {this_sheet} / r_sheet = {r_sheet}') all_rosters = [None, None, None] # Pull roster 1 if not roster_num or roster_num == 1: roster_1 = r_sheet.range('B3:B28') roster_name = r_sheet.cell('F30').value logger.info(f'roster_1: {roster_1}') if not roster_1[0][0].value == '': all_rosters[0] = {'name': roster_name, 'roster_num': 1, 'team_id': team['id'], 'cards': None} all_rosters[0]['cards'] = [int(x[0].value) for x in roster_1] # Pull roster 2 if not roster_num or roster_num == 2: roster_2 = r_sheet.range('B29:B54') roster_name = r_sheet.cell('F31').value logger.info(f'roster_2: {roster_2}') if not roster_2[0][0].value == '': all_rosters[1] = {'name': roster_name, 'roster_num': 2, 'team_id': team['id'], 'cards': None} all_rosters[1]['cards'] = [int(x[0].value) for x in roster_2] # Pull roster 3 if not roster_num or roster_num == 3: roster_3 = r_sheet.range('B55:B80') roster_name = r_sheet.cell('F32').value logger.info(f'roster_3: {roster_3}') if not roster_3[0][0].value == '': all_rosters[2] = {'name': roster_name, 'roster_num': 3, 'team_id': team['id'], 'cards': None} all_rosters[2]['cards'] = [int(x[0].value) for x in roster_3] return all_rosters def get_roster_lineups(team, bot, roster_num, lineup_num) -> list: sheets = get_sheets(bot) logger.debug(f'sheets: {sheets}') this_sheet = sheets.open_by_key(team['gsheet']) logger.debug(f'this_sheet: {this_sheet}') r_sheet = this_sheet.worksheet_by_title('My Rosters') logger.debug(f'r_sheet: {r_sheet}') if lineup_num == 1: row_start = 9 row_end = 17 else: row_start = 18 row_end = 26 if roster_num == 1: l_range = f'H{row_start}:I{row_end}' elif roster_num == 2: l_range = f'J{row_start}:K{row_end}' else: l_range = f'L{row_start}:M{row_end}' logger.debug(f'l_range: {l_range}') raw_cells = r_sheet.range(l_range) logger.debug(f'raw_cells: {raw_cells}') try: lineup_cells = [(row[0].value, int(row[1].value)) for row in raw_cells] except ValueError as e: logger.error(f'Could not pull roster for {team["abbrev"]} due to a ValueError') raise ValueError(f'Uh oh. Looks like your roster might not be saved. I am reading blanks when I try to ' f'get the card IDs') logger.debug(f'lineup_cells: {lineup_cells}') return lineup_cells def post_ratings_guide(team, bot, this_sheet=None): if not this_sheet: sheets = get_sheets(bot) this_sheet = sheets.open_by_key(team['gsheet']) p_guide = this_sheet.worksheet_by_title('Full Guide - Pitchers') b_guide = this_sheet.worksheet_by_title('Full Guide - Batters') p_guide.update_value('A1', RATINGS_PITCHER_FORMULA) b_guide.update_value('A1', RATINGS_BATTER_FORMULA) async def legal_channel(ctx): bad_channels = ['paper-dynasty-chat', 'pd-news-ticker', 'pd-network-news'] if isinstance(ctx, commands.Context): if ctx.channel.name in bad_channels: raise commands.CheckFailure(f'Slide on down to the {get_channel(ctx, "pd-bot-hole").mention} ;)') else: return True elif ctx.channel.name in bad_channels: # await ctx.message.add_reaction('❌') # await ctx.send(f'Slide on down to the {get_channel(ctx, "pd-bot-hole").mention} ;)') # logger.warning(f'{ctx.author.name} posted in illegal channel.') # return False raise discord.app_commands.AppCommandError(f'Slide on down to the {get_channel(ctx, "pd-bot-hole").mention} ;)') else: return True def owner_only(interaction: discord.Interaction): if interaction.user.id == 258104532423147520: return True else: return False def get_role(ctx, role_name): return discord.utils.get(ctx.guild.roles, name=role_name) async def team_summary_embed(team, ctx, include_roster: bool = True): embed = get_team_embed(f'{team["lname"]} Overview', team) embed.add_field(name='General Manager', value=team['gmname'], inline=False) embed.add_field(name='Wallet', value=f'{team["wallet"]}₼') # embed.add_field(name='Collection Value', value=team['collection_value']) p_query = await db_get('packs', params=[('team_id', team['id']), ('opened', False)]) if p_query['count'] > 0: all_packs = {} for x in p_query['packs']: if x['pack_type']['name'] not in all_packs: all_packs[x['pack_type']['name']] = 1 else: all_packs[x['pack_type']['name']] += 1 pack_string = '' for pack_type in all_packs: pack_string += f'{pack_type.title()}: {all_packs[pack_type]}\n' else: pack_string = 'None' embed.add_field(name='Unopened Packs', value=pack_string) embed.add_field(name='Team Rating', value=f'{team["ranking"]}') r_query = await db_get(f'results/team/{team["id"]}?season={PD_SEASON}') if r_query: embed.add_field( name='Record', value=f'Ranked: {r_query["ranked_wins"]}-{r_query["ranked_losses"]}\n' f'Unlimited: {r_query["casual_wins"]}-{r_query["casual_losses"]}' ) # try: # r_query = await db_get('rosters', params=[('team_id', team['id'])]) # if r_query['count']: # embed.add_field(name=f'Rosters', value=f'** **', inline=False) # for roster in r_query['rosters']: # roster_string = '' # for i in range(1, 27): # card = roster[f'card_{i}'] # roster_string += f'{card["player"]["description"]} ({card["player"]["pos_1"]})\n' # embed.add_field( # name=f'{roster["name"]} Roster', # value=roster_string if len(roster_string) else "Unknown" # ) # else: # embed.add_field( # name='Rosters', # value='You can set up to three rosters for quick switching from your team sheet.', # inline=False # ) # except Exception as e: # logger.error(f'Could not pull rosters for {team["abbrev"]}') # embed.add_field( # name='Rosters', # value='Unable to pull current rosters. `/pullroster` to sync.', # inline=False # ) if include_roster: embed.add_field(name='Team Sheet', value=get_roster_sheet(team, allow_embed=True), inline=False) embed.add_field( name='For Help', value=f'`/help-pd` has FAQs; feel free to post questions in ' f'{get_channel(ctx, "paper-dynasty-chat").mention}.', inline=False ) return embed async def give_cards_to_team(team, players: list = None, player_ids: list = None, pack_id=None): if not pack_id: p_query = await db_post( 'packs/one', payload={ 'team_id': team['id'], 'pack_type_id': 4, 'open_time': datetime.datetime.timestamp(datetime.datetime.now()) * 1000} ) pack_id = p_query['id'] if not players and not player_ids: raise ValueError('One of players or player_ids must be provided to distribute cards') if players: await db_post('cards', payload={'cards': [ {'player_id': x['player_id'], 'team_id': team['id'], 'pack_id': pack_id} for x in players ]}, timeout=10) elif player_ids: await db_post('cards', payload={'cards': [ {'player_id': x, 'team_id': team['id'], 'pack_id': pack_id} for x in player_ids ]}, timeout=10) def get_ratings_guide(sheets): this_sheet = sheets.open_by_key(RATINGS_SHEET_KEY) b_sheet = this_sheet.worksheet_by_title('ratings_Batters') p_sheet = this_sheet.worksheet_by_title('ratings_Pitchers') b_data = b_sheet.range('A2:N') p_data = p_sheet.range('A2:N') try: batters = [ { 'player_id': int(x[0].value), 'p_name': x[1].value, 'rating': int(x[2].value), 'contact-r': int(x[3].value), 'contact-l': int(x[4].value), 'power-r': int(x[5].value), 'power-l': int(x[6].value), 'vision': int(x[7].value), 'speed': int(x[8].value), 'stealing': int(x[9].value), 'reaction': int(x[10].value), 'arm': int(x[11].value), 'fielding': int(x[12].value), 'hand': int(x[13].value), } for x in b_data ] pitchers = [ { 'player_id': int(x[0].value), 'p_name': x[1].value, 'rating': int(x[2].value), 'control-r': int(x[3].value), 'control-l': int(x[4].value), 'stuff-r': int(x[5].value), 'stuff-l': int(x[6].value), 'stamina': int(x[7].value), 'fielding': int(x[8].value), 'hit-9': int(x[9].value), 'k-9': int(x[10].value), 'bb-9': int(x[11].value), 'hr-9': int(x[12].value), 'hand': int(x[13].value), } for x in p_data ] except Exception as e: return {'valid': False} return { 'valid': True, 'batter_ratings': batters, 'pitcher_ratings': pitchers } async def paperdex_cardset_embed(team: dict, this_cardset: dict) -> list[discord.Embed]: all_dex = await db_get( 'paperdex', params=[('team_id', team['id']), ('cardset_id', this_cardset['id']), ('flat', True)] ) dex_player_list = [x['player'] for x in all_dex['paperdex']] hof_embed = get_team_embed(f'{team["lname"]} Collection', team=team) mvp_embed = get_team_embed(f'{team["lname"]} Collection', team=team) as_embed = get_team_embed(f'{team["lname"]} Collection', team=team) sta_embed = get_team_embed(f'{team["lname"]} Collection', team=team) res_embed = get_team_embed(f'{team["lname"]} Collection', team=team) rep_embed = get_team_embed(f'{team["lname"]} Collection', team=team) coll_data = { 99: { 'name': 'Hall of Fame', 'owned': 0, 'players': [], 'embeds': [hof_embed] }, 1: { 'name': 'MVP', 'owned': 0, 'players': [], 'embeds': [mvp_embed] }, 2: { 'name': 'All-Star', 'owned': 0, 'players': [], 'embeds': [as_embed] }, 3: { 'name': 'Starter', 'owned': 0, 'players': [], 'embeds': [sta_embed] }, 4: { 'name': 'Reserve', 'owned': 0, 'players': [], 'embeds': [res_embed] }, 5: { 'name': 'Replacement', 'owned': 0, 'players': [], 'embeds': [rep_embed] }, 'total_owned': 0 } set_players = await db_get( 'players', params=[('cardset_id', this_cardset['id']), ('flat', True), ('inc_dex', False)], timeout=5 ) for player in set_players['players']: if player['player_id'] in dex_player_list: coll_data[player['rarity']]['owned'] += 1 coll_data['total_owned'] += 1 player['owned'] = True else: player['owned'] = False logger.debug(f'player: {player} / type: {type(player)}') coll_data[player['rarity']]['players'].append(player) cover_embed = get_team_embed(f'{team["lname"]} Collection', team=team) cover_embed.description = this_cardset['name'] cover_embed.add_field(name='# Total Cards', value=f'{set_players["count"]}') cover_embed.add_field(name='# Collected', value=f'{coll_data["total_owned"]}') display_embeds = [cover_embed] for rarity_id in coll_data: if rarity_id != 'total_owned': if coll_data[rarity_id]['players']: coll_data[rarity_id]['embeds'][0].description = f'Rarity: {coll_data[rarity_id]["name"]}' coll_data[rarity_id]['embeds'][0].add_field( name='# Collected / # Total Cards', value=f'{coll_data[rarity_id]["owned"]} / {len(coll_data[rarity_id]["players"])}', inline=False ) chunk_string = '' for index, this_player in enumerate(coll_data[rarity_id]['players']): logger.debug(f'this_player: {this_player}') chunk_string += '☑ ' if this_player['owned'] else '⬜ ' chunk_string += f'{this_player["p_name"]}\n' if (index + 1) == len(coll_data[rarity_id]["players"]): coll_data[rarity_id]['embeds'][0].add_field( name=f'Group {math.ceil((index + 1) / 20)} / ' f'{math.ceil(len(coll_data[rarity_id]["players"]) / 20)}', value=chunk_string ) elif (index + 1) % 20 == 0: coll_data[rarity_id]['embeds'][0].add_field( name=f'Group {math.floor((index + 1) / 20)} / ' f'{math.ceil(len(coll_data[rarity_id]["players"]) / 20)}', value=chunk_string ) chunk_string = '' display_embeds.append(coll_data[rarity_id]['embeds'][0]) return display_embeds async def paperdex_team_embed(team: dict, mlb_team: dict) -> list[discord.Embed]: all_dex = await db_get( 'paperdex', params=[('team_id', team['id']), ('franchise', mlb_team['lname']), ('flat', True)] ) dex_player_list = [x['player'] for x in all_dex['paperdex']] c_query = await db_get('cardsets') coll_data = {'total_owned': 0} total_players = 0 for x in c_query['cardsets']: set_players = await db_get( 'players', params=[('cardset_id', x['id']), ('franchise', mlb_team['lname']), ('flat', True), ('inc_dex', False)] ) if set_players is not None: coll_data[x['id']] = { 'name': x['name'], 'owned': 0, 'players': [], 'embeds': [get_team_embed(f'{team["lname"]} Collection', team=team)] } total_players += set_players['count'] for player in set_players['players']: if player['player_id'] in dex_player_list: coll_data[x['id']]['owned'] += 1 coll_data['total_owned'] += 1 player['owned'] = True else: player['owned'] = False logger.debug(f'player: {player} / type: {type(player)}') coll_data[x['id']]['players'].append(player) cover_embed = get_team_embed(f'{team["lname"]} Collection', team=team) cover_embed.description = mlb_team['lname'] cover_embed.add_field(name='# Total Cards', value=f'{total_players}') cover_embed.add_field(name='# Collected', value=f'{coll_data["total_owned"]}') display_embeds = [cover_embed] for cardset_id in coll_data: if cardset_id != 'total_owned': if coll_data[cardset_id]['players']: coll_data[cardset_id]['embeds'][0].description = f'{mlb_team["lname"]} / ' \ f'{coll_data[cardset_id]["name"]}' coll_data[cardset_id]['embeds'][0].add_field( name='# Collected / # Total Cards', value=f'{coll_data[cardset_id]["owned"]} / {len(coll_data[cardset_id]["players"])}', inline=False ) chunk_string = '' for index, this_player in enumerate(coll_data[cardset_id]['players']): logger.debug(f'this_player: {this_player}') chunk_string += '☑ ' if this_player['owned'] else '⬜ ' chunk_string += f'{this_player["p_name"]}\n' if (index + 1) == len(coll_data[cardset_id]["players"]): coll_data[cardset_id]['embeds'][0].add_field( name=f'Group {math.ceil((index + 1) / 20)} / ' f'{math.ceil(len(coll_data[cardset_id]["players"]) / 20)}', value=chunk_string ) elif (index + 1) % 20 == 0: coll_data[cardset_id]['embeds'][0].add_field( name=f'Group {math.floor((index + 1) / 20)} / ' f'{math.ceil(len(coll_data[cardset_id]["players"]) / 20)}', value=chunk_string ) chunk_string = '' display_embeds.append(coll_data[cardset_id]['embeds'][0]) return display_embeds def get_pack_cover(pack): if pack['pack_cardset'] is not None and pack['pack_cardset'] == 23: return IMAGES['pack-pkmnbs'] elif pack['pack_type']['name'] in ['Premium', 'MVP']: return IMAGES['pack-pre'] elif pack['pack_type']['name'] == 'Standard': return IMAGES['pack-sta'] elif pack['pack_type']['name'] == 'Mario': return IMAGES['pack-mar'] else: return None async def open_st_pr_packs(all_packs: list, team: dict, context): pack_channel = get_channel(context, 'pack-openings') pack_cover = get_pack_cover(all_packs[0]) if pack_cover is None: pack_channel = context.channel if not pack_channel: raise ValueError(f'I cannot find the pack-openings channel. {get_cal_user(context).mention} - halp?') pack_ids = await roll_for_cards(all_packs) if not pack_ids: logger.error(f'open_packs - unable to roll_for_cards for packs: {all_packs}') raise ValueError(f'I was not able to unpack these cards') all_cards = [] for p_id in pack_ids: new_cards = await db_get('cards', params=[('pack_id', p_id)]) all_cards.extend(new_cards['cards']) if not all_cards: logger.error(f'open_packs - unable to get cards for packs: {pack_ids}') raise ValueError(f'I was not able to display these cards') # Present cards to opening channel if type(context) == commands.Context: author = context.author else: author = context.user await context.channel.send(content=f'Let\'s head down to {pack_channel.mention}!') await display_cards(all_cards, team, pack_channel, author, pack_cover=pack_cover) async def get_choice_from_cards( interaction: discord.Interaction, all_players: list = None, cover_title: str = None, cover_desc: str = None, cover_image_url: str = None, callback=None, temp_message: str = None, conf_message: str = None, delete_message: bool = False): # Display them with pagination, prev/next/select card_embeds = [ await get_card_embeds( {'player': x, 'team': {'lname': 'Paper Dynasty', 'season': PD_SEASON, 'logo': IMAGES['logo']}} ) for x in all_players ] logger.debug(f'card embeds: {card_embeds}') if cover_title is not None and cover_image_url is not None: page_num = 0 view = Pagination([interaction.user], timeout=30) view.left_button.disabled = True view.left_button.label = f'Prev: -/{len(card_embeds)}' view.cancel_button.label = f'Take This Card' view.cancel_button.style = discord.ButtonStyle.success view.cancel_button.disabled = True view.right_button.label = f'Next: 1/{len(card_embeds)}' msg = await interaction.channel.send( content=None, embed=image_embed( image_url=cover_image_url, title=cover_title, desc=cover_desc ), view=view ) else: page_num = 1 view = Pagination([interaction.user], timeout=30) view.left_button.label = f'Prev: -/{len(card_embeds)}' view.left_button.disabled = True view.cancel_button.label = f'Take This Card' view.cancel_button.style = discord.ButtonStyle.success view.right_button.label = f'Next: {page_num + 1}/{len(card_embeds)}' msg = await interaction.channel.send(content=None, embeds=card_embeds[page_num - 1], view=view) if temp_message is not None: temp_msg = await interaction.channel.send(content=temp_message) else: temp_msg = None while True: await view.wait() if view.value: if view.value == 'cancel': await msg.edit(view=None) if callback is not None: callback(all_players[page_num - 1]) if conf_message is not None: if temp_msg is not None: await temp_msg.edit(content=conf_message) else: await interaction.channel.send(content=conf_message) break if view.value == 'left': page_num -= 1 if page_num > 1 else len(card_embeds) if view.value == 'right': page_num += 1 if page_num < len(card_embeds) else 1 else: if page_num == len(card_embeds): page_num = 1 else: page_num += 1 view.value = None view = Pagination([interaction.user], timeout=30) view.left_button.label = f'Prev: {page_num - 1}/{len(card_embeds)}' view.cancel_button.label = f'Take This Card' view.cancel_button.style = discord.ButtonStyle.success view.right_button.label = f'Next: {page_num + 1}/{len(card_embeds)}' if page_num == 1: view.left_button.label = f'Prev: -/{len(card_embeds)}' view.left_button.disabled = True elif page_num == len(card_embeds): view.right_button.label = f'Next: -/{len(card_embeds)}' view.right_button.disabled = True await msg.edit(content=None, embeds=card_embeds[page_num - 1], view=view) if delete_message: await msg.delete() return all_players[page_num - 1] async def open_choice_pack(this_pack, team: dict, context, cardset_id: Optional[int] = None): pack_channel = get_channel(context, 'pack-openings') pack_cover = get_pack_cover(this_pack) pack_type = this_pack['pack_type']['name'] players = [] if pack_type == 'Mario': d1000 = random.randint(1, 1000) if d1000 > 800: rarity_id = 5 elif d1000 > 550: rarity_id = 3 else: rarity_id = 2 pl = await db_get( 'players/random', params=[ ('cardset_id', 8), ('min_rarity', rarity_id), ('max_rarity', rarity_id), ('limit', 4) ] ) players = pl['players'] elif pack_type == 'Team Choice': if this_pack['pack_team'] is None: raise KeyError(f'Team not listed for Team Choice pack') d1000 = random.randint(1, 1000) pack_cover = this_pack['pack_team']['logo'] if d1000 > 800: rarity_id = 5 pack_cover = IMAGES['mvp'][this_pack['pack_team']['lname']] elif d1000 > 550: rarity_id = 3 else: rarity_id = 2 # # HAX FOR SOCC TO GET HIS MVP PACK # if (team['abbrev'] in ['KSK', 'NJY']) and (datetime.datetime.today().day == 24): # rarity_id = 5 min_rarity = rarity_id while len(players) < 4 and rarity_id < 10: params = [ ('min_rarity', min_rarity), ('max_rarity', rarity_id), ('limit', 4 - len(players)), ('franchise', this_pack['pack_team']['lname']) ] if this_pack['pack_team']['abbrev'] not in ['MSS']: params.append(('in_packs', True)) if cardset_id is not None: params.append(('cardset_id', cardset_id)) pl = await db_get( 'players/random', params=params ) if pl['count'] >= 0: for x in pl['players']: if x not in players: players.append(x) if len(players) < 4: min_rarity += 1 rarity_id += 1 elif pack_type == 'Promo Choice': if this_pack['pack_cardset'] is None: raise KeyError(f'Cardset not listed for Promo Choice pack') d1000 = random.randint(1, 1000) pack_cover = IMAGES['mvp-hype'] cardset_id = this_pack['pack_cardset']['id'] rarity_id = 5 if d1000 > 800: rarity_id = 8 while len(players) < 4 and rarity_id < 10: pl = await db_get( 'players/random', params=[('cardset_id', cardset_id), ('min_rarity', rarity_id), ('max_rarity', rarity_id), ('limit', 8)] ) if pl['count'] >= 0: for x in pl['players']: if len(players) >= 4: break if x not in players: players.append(x) if len(players) < 4: cardset_id = LIVE_CARDSET_ID else: # Get 4 MVP cards rarity_id = 5 if pack_type == 'HoF': rarity_id = 8 elif pack_type == 'All Star': rarity_id = 3 min_rarity = rarity_id while len(players) < 4 and rarity_id < 10: params = [ ('min_rarity', min_rarity), ('max_rarity', rarity_id), ('limit', 4), ('in_packs', True) ] if this_pack['pack_team'] is not None: params.append(('franchise', this_pack['pack_team']['lname'])) if cardset_id is not None: params.append(('cardset_id', cardset_id)) pl = await db_get('players/random', params=params) if pl['count'] > 0: players.extend(pl['players']) if len(players) < 4: rarity_id += 3 if len(players) == 0: logger.error(f'Could not create choice pack') raise ConnectionError(f'Could not create choice pack') if type(context) == commands.Context: author = context.author else: author = context.user logger.info(f'helpers - open_choice_pack - players: {players}') # Display them with pagination, prev/next/select card_embeds = [ await get_card_embeds( # {'player': x, 'team': {'lname': 'Paper Dynasty', 'season': PD_SEASON, 'logo': IMAGES['logo']}} {'player': x, 'team': team} # Show team and dupe info ) for x in players ] logger.debug(f'card embeds: {card_embeds}') page_num = 0 view = Pagination([author], timeout=30) view.left_button.disabled = True view.left_button.label = f'Prev: -/{len(card_embeds)}' view.cancel_button.label = f'Take This Card' view.cancel_button.style = discord.ButtonStyle.success view.cancel_button.disabled = True view.right_button.label = f'Next: 1/{len(card_embeds)}' # React to selection await context.channel.send(f'Let\'s head down to {pack_channel.mention}!') msg = await pack_channel.send( content=None, embed=image_embed(pack_cover, title=f'{team["lname"]}', desc=f'{pack_type} Pack - Choose 1 of 4 {pack_type}s!'), view=view ) if rarity_id >= 5: tmp_msg = await pack_channel.send(content=f'<@&1163537676885033010> we\'ve got an MVP!') else: tmp_msg = await pack_channel.send(content=f'We\'ve got a choice pack here!') while True: await view.wait() if view.value: if view.value == 'cancel': await msg.edit(view=None) try: await give_cards_to_team(team, players=[players[page_num - 1]], pack_id=this_pack['id']) except Exception as e: logger.error(f'failed to create cards: {e}') raise ConnectionError(f'Failed to distribute these cards.') await db_patch('packs', object_id=this_pack['id'], params=[ ('open_time', int(datetime.datetime.timestamp(datetime.datetime.now()) * 1000)) ]) await tmp_msg.edit( content=f'{players[page_num - 1]["p_name"]} has been added to the ' f'**{team["sname"]}** binder!' ) break if view.value == 'left': page_num -= 1 if page_num > 1 else len(card_embeds) if view.value == 'right': page_num += 1 if page_num < len(card_embeds) else 1 else: if page_num == len(card_embeds): page_num = 1 else: page_num += 1 view.value = None view = Pagination([author], timeout=30) view.left_button.label = f'Prev: {page_num - 1}/{len(card_embeds)}' view.cancel_button.label = f'Take This Card' view.cancel_button.style = discord.ButtonStyle.success view.right_button.label = f'Next: {page_num + 1}/{len(card_embeds)}' if page_num == 1: view.left_button.label = f'Prev: -/{len(card_embeds)}' view.left_button.disabled = True elif page_num == len(card_embeds): view.right_button.label = f'Next: -/{len(card_embeds)}' view.right_button.disabled = True await msg.edit(content=None, embeds=card_embeds[page_num - 1], view=view) async def confirm_pack_purchase(interaction, owner_team, num_packs, total_cost, pack_embed): view = Confirm(responders=[interaction.user], timeout=30) await interaction.channel.send( content=None, embed=pack_embed ) question = await interaction.channel.send( content=f'Your Wallet: {owner_team["wallet"]}₼\n' f'Pack{"s" if num_packs > 1 else ""} Price: {total_cost}₼\n' f'After Purchase: {owner_team["wallet"] - total_cost}₼\n\n' f'Would you like to make this purchase?', view=view ) await view.wait() if not view.value: await question.edit( content='Saving that money. Smart.', view=None ) return None else: return question def player_desc(this_player) -> str: if this_player['p_name'] in this_player['description']: return this_player['description'] return f'{this_player["description"]} {this_player["p_name"]}' def player_pcard(this_player): if this_player['image'] is not None and 'pitching' in this_player['image']: return this_player['image'] elif this_player['image2'] is not None and 'pitching' in this_player['image2']: return this_player['image2'] else: return this_player['image'] def player_bcard(this_player): if this_player['image'] is not None and 'batting' in this_player['image']: return this_player['image'] elif this_player['image2'] is not None and 'batting' in this_player['image2']: return this_player['image2'] # elif this_player['image'] is not None and 'pitching' in this_player['image']: # return PITCHER_BATTING_CARD else: return this_player['image'] 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: logger.warning(resp.text) raise ValueError(f'DB: {resp.text}') def random_from_list(data_list: list): item = data_list[random.randint(0, len(data_list) - 1)] logger.info(f'random_from_list: {item}') return item def user_has_role(user: discord.User | discord.Member, role_name: str) -> bool: for x in user.roles: if x.name == role_name: return True return False def random_insult() -> str: return random_from_list(INSULTS)