paper-dynasty-discord/helpers.py

3152 lines
130 KiB
Python

import asyncio
import datetime
import logging
import math
import os
import random
import traceback
import discord
import pygsheets
from discord.ext import commands
from db_calls import *
from bs4 import BeautifulSoup
from difflib import get_close_matches
from dataclasses import dataclass
from typing import Optional, Literal
SBA_SEASON = 7
PD_SEASON = 6
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'
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',
'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',
},
'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, runners advance 2 bases. The outfielder throws the ball to the shortstop who executes '
'a hidden ball trick! Runner on second is tagged out!',
'e1': 'Double and error, batter to third, all runners score.',
'e2': 'Double and error, batter to third, and all runners score.',
'e3': 'Double and error, batter and all runners score. Little league home run!',
'no': 'Double, all runners advance 2 bases.'
},
'do3': {
'rp': '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
}
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:
logging.info(f'{interaction.user} is not in {self.responders}')
return
self.value = 'left'
await interaction.response.defer()
self.stop()
@discord.ui.button(label='❌️', style=discord.ButtonStyle.secondary)
async def cancel_button(self, interaction: discord.Interaction, button: discord.ui.Button):
if interaction.user not in self.responders:
logging.info(f'{interaction.user} is not in {self.responders}')
return
self.value = 'cancel'
await interaction.response.defer()
self.stop()
@discord.ui.button(label='⏭️', style=discord.ButtonStyle.blurple)
async def right_button(self, interaction: discord.Interaction, button: discord.ui.Button):
if interaction.user not in self.responders:
logging.info(f'{interaction.user} is not in {self.responders}')
return
self.value = 'right'
await interaction.response.defer()
self.stop()
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:
logging.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):
logging.info(f'SelectPackChoice - selection: {self.values[0]}')
pack_vals = self.values[0].split('-')
logging.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 '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]
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
elif 'Team' in pack_vals:
params.append(('pack_team_id', pack_vals[2]))
elif 'Cardset' in pack_vals:
params.append(('pack_cardset_id', pack_vals[2]))
cardset_id = pack_vals[2]
p_query = await db_get('packs', params=params)
if p_query['count'] == 0:
logging.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='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):
logging.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
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='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):
logging.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
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: [discord.ui.Select], timeout: float = 300.0):
super().__init__(timeout=timeout)
for x in select_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://media.giphy.com/media/zCME2Cd20Czvy/giphy.gif',
'https://media.giphy.com/media/xT0xeJpckCr0qGAFna/giphy.gif',
'https://media0.giphy.com/media/l0MYw3oeYCUJhj5FC/200.gif',
'https://media2.giphy.com/media/52FcaTVc9Y1rk7q1NQ/200.gif',
'https://i0.wp.com/media1.giphy.com/media/iwvuPyfi7z14I/giphy.gif',
'https://media1.tenor.com/images/859a2d3b201fbacec13904242976b9e0/tenor.gif',
'https://media.giphy.com/media/3o6ZsZbUukGiYMf2a4/giphy.gif',
'https://media.giphy.com/media/l0Iyl55kTeh71nTXy/giphy.gif',
'https://media.giphy.com/media/3o7qDEq2bMbcbPRQ2c/giphy.gif',
'https://media.giphy.com/media/3orieTbyzN9QNCDANa/giphy.gif',
'https://media.giphy.com/media/l3V0JGUuz4xght7Py/giphy.gif',
'https://media.giphy.com/media/o1H5mqZKB0RCE/giphy.gif',
'https://media.giphy.com/media/Rhf0uSWt1P2TFqVMZK/giphy.gif',
'https://media.giphy.com/media/UIMAFTRUcf6V71BGsd/giphy.gif',
'https://media.giphy.com/media/xUOwFYnEb6rv4Oxx6M/giphy.gif',
'https://media.giphy.com/media/6bdiLSKOwgR6ekaTSS/giphy.gif',
'https://tenor.com/bc1OJ.gif',
'https://tenor.com/1EmF.gif',
'https://tenor.com/ZYCh.gif',
'https://tenor.com/patd.gif',
'https://tenor.com/u6mU.gif',
'https://tenor.com/x2sa.gif',
'https://tenor.com/bAVeS.gif',
'https://tenor.com/bxOcj.gif',
'https://tenor.com/ETJ7.gif',
'https://tenor.com/bpH3g.gif',
'https://tenor.com/biF9q.gif',
'https://tenor.com/OySS.gif',
'https://tenor.com/bvVFv.gif',
'https://tenor.com/bFeqA.gif'
]
return conf_gifs[random.randint(0, len(conf_gifs) - 1)]
def random_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
)
logging.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:
logging.error('Cannot send to channel - bot not logged in')
return
this_channel = discord.utils.get(guild.text_channels, name=channel_name)
if not this_channel:
this_channel = discord.utils.get(guild.text_channels, id=channel_name)
if not this_channel:
raise NameError(f'**{channel_name}** channel not found')
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)
# logging.info(f'this_role: {this_role} / role_name: {role_name} (POST SEARCH)')
if not this_role:
this_role = await ctx.guild.create_role(name=role_name, mentionable=mentionable)
# logging.info(f'this_role: {this_role} / role_name: {role_name} (PRE RETURN)')
return this_role
"""
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):
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
)
logging.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'])])
p_query = await db_get('plays/pitching', params=[('player_id', card['player']['player_id'])])
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"]}')
player_pages = f'[BBRef]({get_player_url(card["player"], "bbref")})'
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 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':
logging.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']), ('in_packs', True)
]
if all_packs[0]['pack_team'] is not None:
params.append(('franchise', all_packs[0]['pack_team']['lname']))
elif all_packs[0]['pack_cardset'] is not None:
params.append(('cardset_id', all_packs[0]['pack_cardset']['id']))
pl = await db_get('players/random', params=params)
if pl['count'] != counts[key]['count']:
mvp_flag = counts[key]['count'] - pl['count']
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:
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)
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:
logging.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('Players').sheets
except Exception as e:
logging.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'
)
logging.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)}']]
)
logging.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')
logging.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
logging.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
logging.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
logging.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)
logging.debug(f'sheets: {sheets}')
this_sheet = sheets.open_by_key(team['gsheet'])
logging.debug(f'this_sheet: {this_sheet}')
r_sheet = this_sheet.worksheet_by_title('My Rosters')
logging.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}'
logging.debug(f'l_range: {l_range}')
raw_cells = r_sheet.range(l_range)
logging.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:
logging.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')
logging.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} ;)')
# logging.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:
# logging.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) -> [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
logging.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']):
logging.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) -> [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
logging.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']):
logging.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_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:
logging.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:
logging.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
]
logging.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
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)), ('in_packs', True),
('mlbclub', 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
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 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:
logging.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
# 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
]
logging.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:
logging.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.edit_original_response(
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']