Initial commit

This commit is contained in:
Cal Corum 2023-02-19 21:22:57 -06:00 committed by GitHub
parent ba30dad5a4
commit 181689fb61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 16237 additions and 0 deletions

180
cogs/admins.py Normal file
View File

@ -0,0 +1,180 @@
import copy
from helpers import *
from db_calls import *
import math
import pygsheets
from discord.ext import commands, tasks
from discord import app_commands
class Admins(commands.Cog):
def __init__(self, bot):
self.bot = bot
async def cog_command_error(self, ctx, error):
await ctx.send(f'{error}')
@commands.command(name='current', help='Current db info')
@commands.is_owner()
async def current_command(self, ctx):
current = await get_current()
current_string = ''
for x in current:
current_string += f'{x}: {current[x]}\n'
await ctx.send(current_string)
@commands.command(name='blast', help='Megaphone')
@commands.is_owner()
async def blast_command(self, ctx, channel_name, *, message):
await send_to_channel(self.bot, channel_name, message)
await ctx.send(random_conf_gif())
@app_commands.command(name='blast', description='Send a message')
@app_commands.guilds(discord.Object(id=os.environ.get('GUILD_ID')))
@app_commands.checks.has_any_role('Da Commish')
async def blast_slash(
self, interaction: discord.Interaction, channel: discord.TextChannel, msg_content: str = None,
embed_title: str = None, embed_field_name: str = None, image_url: str = None, color_hex: str = None):
current = await get_current()
try:
embed = None
if embed_title:
embed = discord.Embed(
title=embed_title,
color=int(color_hex, 16) if color_hex is not None else int('0xa5fffc', 16)
)
embed.set_footer(text=f'SBa Season {current["season"]}', icon_url=LOGO)
embed.set_image(url=image_url)
if embed_field_name:
embed.add_field(
name=embed_field_name if embed_field_name is not None else "** **",
value=msg_content
)
await channel.send(content=None, embed=embed)
await interaction.response.send_message(content=random_conf_gif())
return
await channel.send(content=msg_content)
except Exception as e:
logging.error(f'Error blasting a message: {type(e)}: {e}')
await interaction.response.send_message(content=f'Uh oh\n\n{type(e)}: {e}')
@commands.command(name='sendstats', help='all, batting, pitching')
@commands.is_owner()
async def send_stats_command(self, ctx, which='all'):
trans_cog = self.bot.get_cog('Transactions')
await trans_cog.send_stats_to_sheets(which=which)
@commands.command(name='test', hidden=True)
@commands.is_owner()
async def test_command(self, ctx):
current = await get_current()
week_num = f'Week {current["week"]}'
stars = f'{"":*<32}'
freeze_message = f'```\n' \
f'{stars}\n'\
f'{week_num: >9} Freeze Period Begins\n' \
f'{stars}\n```'
await send_to_channel(self.bot, 'general', freeze_message)
@commands.command(name='sendmoves', help='Send moves to sheets')
@commands.is_owner()
async def send_moves_command(self, ctx):
current = await get_current()
await ctx.send('Authenticating with sheets...')
sheets = pygsheets.authorize(service_file='storage/major-domo-service-creds.json')
trans_tab = sheets.open_by_key(SBA_ROSTER_KEY).worksheet_by_title('Transactions')
await ctx.send('Collecting transactions...')
all_vals = []
all_moves = await get_transactions(
season=current['season'],
timeout=30
)
await ctx.send(f'Processing transactions ({len(all_moves)} found)...')
total_moves = len(all_moves)
counter = 0
for move in [*all_moves.values()]:
all_vals.insert(
0,
[
move['player']['name'],
move['oldteam']['sname'],
move['newteam']['sname'],
move['week'],
total_moves + 416 - counter
]
)
counter += 1
logging.warning(f'all_vals samples:\n0: {all_vals[0]}\n100: {all_vals[100]}\n1000: {all_vals[1000]}\n'
f'2000: {all_vals[2000]}')
await ctx.send('Sending transactions to sheets...')
try:
trans_tab.update_values(
crange=f'A420',
values=all_vals
)
await ctx.send('All done!')
except Exception as e:
await ctx.send('Failed sending to sheets')
@commands.command(name='xpick', help='Expansion pick')
@commands.is_owner()
async def expansion_pick_command(self, ctx, team_abbrev, *, name):
current = await get_current()
player_cog = self.bot.get_cog('Players')
player_name = await fuzzy_player_search(ctx, ctx.channel, self.bot, name, player_cog.player_list.keys())
player = await get_one_player(player_name)
team = await get_one_team(team_abbrev)
old_team = copy.deepcopy(player["team"])
if not team:
await ctx.send(f'Who the fuck is **{team_abbrev}**? Get your shit together - it\'s DRAFT TIME!!!')
return
if old_team['id'] == 99:
await ctx.send(f'Tell that bastard they\'re an idiot. {player["name"]} is a Free Agent.')
return
await patch_player(player['id'], team_id=team['id'])
await ctx.send(content=None, embed=await get_player_embed(await get_one_player(player['id']), current))
await send_to_channel(
self.bot,
's4-draft-picks',
f'Expansion Draft: {await get_emoji(ctx, team["sname"])}{team["sname"]} select **{player["name"]}** from '
f'{await get_emoji(ctx, old_team["sname"])}{old_team["abbrev"]}'
)
@commands.command(name='injimport')
@commands.is_owner()
async def injury_import_command(self, ctx):
sheets = pygsheets.authorize(service_file='storage/major-domo-service-creds.json')
inj_tab = sheets.open_by_key('1uKRf7YwTcEfp8D7gUutQRwnOHW37hl6XcBbtqw3PbN4').worksheet_by_title('Sheet1')
raw_data = inj_tab.get_values('A1', 'B545')
for line in raw_data:
player = await get_one_player(line[0])
await patch_player(player['id'], pitcher_injury=line[1])
@commands.command(name='setdemweek', help='Set player\'s demotion week')
@commands.is_owner()
async def set_dem_week_command(self, ctx, week_num, *, player_name):
player_cog = self.bot.get_cog('Players')
player_name = await fuzzy_player_search(ctx, ctx.channel, self.bot, player_name, player_cog.player_list.keys())
player = await get_one_player(player_name)
await patch_player(player['id'], demotion_week=week_num)
await ctx.send(random_conf_gif())
async def setup(bot):
await bot.add_cog(Admins(bot))

1419
cogs/dice.py Normal file

File diff suppressed because it is too large Load Diff

1075
cogs/draft.py Normal file

File diff suppressed because it is too large Load Diff

422
cogs/fun.py Normal file
View File

@ -0,0 +1,422 @@
import math
from helpers import *
import discord
from peewee import *
from datetime import datetime, timedelta
from discord.ext import commands, tasks
db = SqliteDatabase(
'storage/sba_is_fun.db',
pragmas={
'journal_mode': 'wal',
'cache_size': -1 * 64000,
'synchronous': 0
}
)
class Creator(Model):
name = CharField()
discordid = IntegerField()
class Meta:
database = db
class Command(Model):
name = CharField()
message = CharField()
creator = ForeignKeyField(Creator)
createtime = DateTimeField()
last_used = DateTimeField()
sent_warns = IntegerField(default=0)
class Meta:
database = db
class Roles(Model):
name = CharField(unique=True)
enabled = BooleanField(default=True)
class Meta:
database = db
class Fun(commands.Cog):
def __init__(self, bot):
self.bot = bot
db.create_tables([Creator, Command, Roles])
db.close()
self.daily_check.start()
@tasks.loop(hours=20)
async def daily_check(self):
try:
# logging.info(f'trying to start cc check')
guild = self.bot.get_guild(int(os.environ.get('GUILD_ID')))
if not guild:
# logging.info(f'no guild found for cc check')
await asyncio.sleep(15)
guild = self.bot.get_guild(int(os.environ.get('GUILD_ID')))
if not guild:
logging.error(f'Fun cog could not access guild')
return
except Exception as e:
logging.error(f'Could not run daily_check: {e}')
return
# <discordid> = {'member': <discord member>, 'commands': [(<command.name>, <command.message>)]}
del_notifs = {}
del_counter = 0
# <discordid> = {'member': <discord member>, 'commands': [(<command.name>, <command.message>)]}
warn_notifs = {}
now = datetime.now()
for x in Command.select():
# Final check / deleted
if x.last_used + timedelta(days=90) < now:
logging.warning(f'Deleting `!cc {x.name}`')
owner = guild.get_member(x.creator.discordid)
if owner:
if owner.id not in del_notifs:
del_notifs[owner.id] = {'member': owner, 'commands': [(x.name, x.message)]}
else:
del_notifs[owner.id]['commands'].append((x.name, x.message))
# x.delete_instance()
del_counter += 1
elif x.last_used + timedelta(days=60) < now and (x.sent_warns is None or x.sent_warns == 0):
logging.warning(f'Warning for `!cc {x.name}`')
x.sent_warns = 1
x.save()
owner = guild.get_member(x.creator.discordid)
if owner:
if owner.id not in warn_notifs:
warn_notifs[owner.id] = {'member': owner, 'commands': [(x.name, x.message)]}
else:
warn_notifs[owner.id]['commands'].append((x.name, x.message))
# else:
# logging.warning(
# f'Command <!cc {x.name}> last used {x.last_used} / delta: {now - x.last_used} \n/>60 days: '
# f'{x.last_used + timedelta(days=60) < now} / sent_warns: {x.sent_warns}'
# )
db.close()
logging.info(f'deletions: {del_notifs}\nwarnings: {warn_notifs}')
for member in del_notifs:
plural = len(del_notifs[member]["commands"]) > 1
msg_content = f'Yo, it\'s cleanup time. I am deleting the following custom ' \
f'command{"s" if plural else ""}:\n\n'
for x in del_notifs[member]["commands"]:
msg_content += f'`!cc {x[0]}` - {x[1]}\n'
await del_notifs[member]['member'].send(msg_content)
for member in warn_notifs:
plural = len(warn_notifs[member]["commands"]) > 1
msg_content = f'Heads up, the following custom ' \
f'command{"s" if plural else ""} will be deleted next month if ' \
f'{"they are" if plural else "it is"} not used:\n\n'
for x in warn_notifs[member]["commands"]:
msg_content += f'`!cc {x[0]}` - {x[1]}\n'
await warn_notifs[member]['member'].send(msg_content)
logging.info(f'Deleted {del_counter} commands; sent deletion notifications to {len(del_notifs)} users; '
f'sent warnings to {len(warn_notifs)} users')
async def cog_command_error(self, ctx, error):
await ctx.send(f'{error}')
@commands.command(name='cc', help='Run custom custom command')
async def custom_command(self, ctx, command):
chosen = Command.get_or_none(fn.Lower(Command.name) == command.lower())
if not chosen:
# Error gif
# await ctx.send('https://tenor.com/blQnd.gif')
# Schitt's Creek 'what's that' gif
# await ctx.send('https://media.giphy.com/media/l0HUhFZx6q0hsPtHq/giphy.gif')
# Kermit lost gif
await ctx.send('https://tenor.com/6saQ.gif')
else:
await ctx.send(chosen.message)
chosen.last_used = datetime.now()
chosen.sent_warns = 0
chosen.save()
db.close()
@commands.command(name='about', help='Who made the custom command')
async def about_command(self, ctx, command):
chosen = Command.get_or_none(fn.Lower(Command.name) == command.lower())
if not chosen:
await ctx.send('https://tenor.com/blQnd.gif')
embed = discord.Embed(title=f'About {chosen.name.title()}', color=0xFFFF00)
embed.add_field(name=f'Creator', value=f'{chosen.creator.name}', inline=False)
embed.add_field(name='Creation Date', value=f'{chosen.createtime}', inline=False)
embed.add_field(name='Message', value=f'{chosen.message}', inline=False)
await ctx.send(content=None, embed=embed)
db.close()
@commands.command(name='newcc', help='Create a new custom command')
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, 'Paper Dynasty Players')
async def new_custom_command(self, ctx, name, *, message):
time = datetime.now()
command = name
comm_message = message
chosen = Command.get_or_none(fn.Lower(Command.name) == command.lower())
if chosen:
await ctx.send('There is already a command with that name!')
return
embed = discord.Embed(title='Is this what you want?', color=0x91329F)
embed.add_field(name='Command Name', value=command, inline=False)
embed.add_field(name='Message', value=comm_message, inline=False)
await ctx.send(content=None, embed=embed)
view = Confirm(responders=[ctx.author])
question = await ctx.send('Should I create this for you?', view=view)
await view.wait()
if not view.value:
await question.edit(content='You keep thinking on it.', view=None)
return
this_person = Creator.get_or_none(Creator.discordid == ctx.author.id)
if not this_person:
this_person = Creator(name=f'{ctx.author.name}', discordid=f'{ctx.author.id}')
this_person.save()
this_command = Command(name=command, message=comm_message, createtime=time, creator=this_person, last_used=time)
if this_command.save() == 1:
await question.edit(content=f'`!cc {this_command.name}` is now a thing!', view=None)
else:
await question.edit(content='Hmm...I couldn\'t add that. I might need a grown up to help.', view=None)
db.close()
@commands.command(name='delcc', help='Delete a custom command')
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, 'Paper Dynasty Players')
async def delete_custom_command(self, ctx, name):
this_command = Command.get_or_none(fn.Lower(Command.name) == name.lower())
if not this_command:
await ctx.send('I couldn\'t find that command, sorry.')
return
if this_command.creator.discordid != ctx.author.id and ctx.author.id != self.bot.owner_id:
await ctx.send('Looks like this isn\'t your command to delete.')
return
embed = discord.Embed(title='Do you want to delete this command?', color=0x91329F)
embed.add_field(name='Command Name', value=this_command.name, inline=False)
embed.add_field(name='Message', value=this_command.message, inline=False)
view = Confirm(responders=[ctx.author])
question = await ctx.send(content=None, embed=embed, view=view)
await view.wait()
if not view.value:
await question.edit(content='It stays for now.', view=None)
return
if this_command.delete_instance() == 1:
await ctx.send('He gone!')
else:
await ctx.send('Welp. That didn\'t work. Go complain to an adult, I guess.')
db.close()
@commands.command(name='allcc', help='Show all custom commands')
async def show_custom_commands(self, ctx, page=1):
def get_embed(this_page):
this_embed = discord.Embed(title=f'All Custom Commands', color=0x2F939F)
column_one = ''
column_two = ''
all_commands = Command.select().paginate(this_page, 40).order_by(Command.name)
for x in range(20):
try:
column_one += f'**{all_commands[x].name}** by {all_commands[x].creator.name}\n'
except Exception as e:
logging.error(f'Error building !allcc embed: {e}')
break
this_embed.add_field(name=f'{(this_page - 1) * 40 + 1}-{this_page * 40 - 20}', value=column_one)
for x in range(20, 40):
try:
column_two += f'**{all_commands[x].name}** by {all_commands[x].creator.name}\n'
except Exception as e:
logging.error(f'Error building !allcc embed: {e}')
break
if len(column_two) > 0:
this_embed.add_field(name=f'{(this_page - 1) * 40 + 21}-{this_page * 40}', value=column_two)
return this_embed
page_num = page
total_commands = Command.select(Command.id)
last_page = math.ceil(total_commands.count()/40)
if page_num > last_page:
await ctx.send(f'The max page number is {last_page}; going there now!')
page_num = last_page
embed = get_embed(page_num)
embed.description = f'Page {page_num} / {last_page}'
view = Pagination(responders=[ctx.author])
resp_message = await ctx.send(content=None, embed=embed, view=view)
while True:
await view.wait()
if view.value:
logging.info(f'got a value: {view.value}')
if view.value == 'left':
page_num = page_num - 1 if page_num > 1 else last_page
if view.value == 'right':
page_num = page_num + 1 if page_num <= last_page else 1
view.value = None
else:
await resp_message.edit(content=None, embed=embed, view=None)
break
# await resp_message.edit(content=None, embed=embed, view=None)
embed = get_embed(page_num)
embed.description = f'Page {page_num} / {last_page}'
view = Pagination(responders=[ctx.author])
await resp_message.edit(content=None, embed=embed, view=view)
db.close()
@commands.command(name='mycc', aliases=['showcc'], help='Show my commands')
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, 'Paper Dynasty Players')
async def my_custom_commands(self, ctx):
this_creator = Creator.get_or_none(Creator.discordid == ctx.author.id)
if not this_creator:
await ctx.send('It doesn\'t look like you\'ve created any custom commands. Try it out by running the '
'!help newcc for the command syntax!')
return
all_commands = Command.select().join(Creator).where(Command.creator == this_creator)
if all_commands.count() == 0:
await ctx.send('It doesn\'t look like you\'ve created any custom commands. Try it out by running the '
'!help newcc for the command syntax!')
return
comm_message = ''
for x in all_commands:
comm_message += f'{x.name}\n'
embed = discord.Embed(title=f'{ctx.author.name}\'s Commands', color=0x2F939F)
embed.add_field(name=f'Command Names', value=comm_message, inline=False)
await ctx.send(content=None, embed=embed)
db.close()
# @commands.command(name='showcc', help='Show one person\'s custom commands')
# @commands.has_any_role(SBA_PLAYERS_ROLE_NAME, 'Paper Dynasty Players')
# async def show_cc_command(self, ctx, ):
# @commands.command(name='role', help='Toggle role')
# async def toggle_role_command(self, ctx, *, role_name):
# all_roles = [x.name for x in Roles.select().where(Roles.enabled)]
#
# async def toggle_role(full_role):
# if full_role in ctx.author.roles:
# await ctx.author.remove_roles(full_role)
# else:
# await ctx.author.add_roles(full_role)
#
# if len(role_name) < 4:
# await ctx.send('https://thumbs.gfycat.com/FrayedUnequaledGnat-size_restricted.gif')
# await ctx.send(f'What even is **{role_name}**...')
# db.close()
# return
#
# for name in all_roles:
# if role_name.lower() in name.lower():
# try:
# this_role = discord.utils.get(ctx.guild.roles, name=name)
# await toggle_role(this_role)
# await ctx.send(random_conf_gif())
# return
# except:
# await ctx.send(await get_emoji(ctx, 'fforrespect', False))
# await ctx.send('I was not able to assign that role.')
# return
#
# await ctx.send(f'That doesn\'t sound familiar. **{role_name}**...did you make that shit up?')
# @commands.command(name='showroles', help='Show toggleable roles')
# async def show_roles_command(self, ctx):
# all_roles = [x.name for x in Roles.select().where(Roles.enabled)]
# role_string = '\n- '.join(all_roles)
#
# embed = get_team_embed('Toggleable Roles', thumbnail=False)
# embed.description = 'Run !role <role_name> to toggle the role on or off'
# embed.add_field(name='Role Names', value=f'- {role_string}')
#
# await ctx.send(content=None, embed=embed)
# @commands.command(name='newrole', aliases=['removerole'], help='Make toggleable role')
# @commands.is_owner()
# async def make_toggleable_role_command(self, ctx, *, role_name):
# this_role = Roles.get_or_none(Roles.name == role_name)
#
# if not this_role:
# # Create the role if it doesn't exist
#
# this_role = Roles(name=role_name)
# this_role.save()
# if not discord.utils.get(ctx.guild.roles, name=this_role.name):
# await ctx.guild.create_role(name=f'{role_name}', mentionable=True)
# else:
# # Disable the role
#
# if this_role.enabled:
# this_role.enabled = False
# else:
# this_role.enabled = True
# this_role.save()
# this_role = discord.utils.get(ctx.guild.roles, name=this_role.name)
#
# if this_role:
# await this_role.edit(mentionable=False)
# else:
# await ctx.send('That role doesn\'t exist in the server.')
#
# await ctx.send(random_conf_gif())
# @commands.command(name='bulkrole', hidden=True)
# @commands.is_owner()
# async def bulkrole_command(self, ctx, *roles):
# all_roles = []
#
# for x in roles:
# all_roles.append(discord.utils.get(ctx.guild.roles, name=x))
#
# await ctx.send('On it. This could take a bit.')
# time_start = datetime.now()
#
# async for member in ctx.guild.fetch_members():
# logging.warning(f'member: {member}')
# await member.add_roles(*all_roles)
#
# time_end = datetime.now()
# await ctx.send(f'All done! That took {time_end - time_start}')
async def setup(bot):
await bot.add_cog(Fun(bot))

2383
cogs/gameday.py Normal file

File diff suppressed because it is too large Load Diff

1256
cogs/gameplay.py Normal file

File diff suppressed because it is too large Load Diff

123
cogs/owner.py Normal file
View File

@ -0,0 +1,123 @@
import logging
import os
import discord
from discord import Object
from discord.ext import commands
from discord.ext.commands import Greedy, Context
from typing import Optional, Literal
class Owner(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.command(hidden=True)
@commands.is_owner()
async def load(self, ctx, *, cog: str):
try:
await self.bot.load_extension(f'cogs.{cog}')
logging.warning(f'Loaded {cog}')
except Exception as e:
await ctx.send(f'**ERROR:** {type(e).__name__} - {e}')
else:
await ctx.send('**SUCCESS**')
@commands.command(hidden=True)
@commands.is_owner()
async def unload(self, ctx, *, cog: str):
try:
await self.bot.unload_extension(f'cogs.{cog}')
logging.warning(f'Unloaded {cog}')
except Exception as e:
await ctx.send(f'**ERROR:** {type(e).__name__} - {e}')
else:
await ctx.send('**SUCCESS**')
@commands.command(hidden=True)
@commands.is_owner()
async def reload(self, ctx, *, cog: str):
try:
await self.bot.unload_extension(f'cogs.{cog}')
logging.warning(f'Unloaded {cog}')
await self.bot.load_extension(f'cogs.{cog}')
logging.warning(f'Reloaded {cog}')
except Exception as e:
await ctx.send(f'**ERROR:** {type(e).__name__} - {e}')
else:
await ctx.send('**SUCCESS**')
@commands.command(hidden=True)
@commands.is_owner()
async def fullreset(self, ctx):
cogs = ['players', 'transactions', 'admins', 'dice', 'fun', 'gameday']
for x in cogs:
try:
await self.bot.unload_extension(f'cogs.{x}')
logging.warning(f'Unloaded {x}')
except Exception as e:
await ctx.send(f'Failed to unload **{x}**')
for x in cogs:
try:
await self.bot.load_extension(f'cogs.{x}')
logging.warning(f'Loaded {x}')
except Exception as e:
await ctx.send(f'Failed to load **{x}**')
await ctx.send('**SUCCESS**')
# @commands.command(name='sync', hidden=True)
# @commands.is_owner()
# async def sync_command(self, ctx, sync_type: str = 'local'):
# sync = None
# await ctx.send('I will try to sync slash commands...')
# try:
# if sync_type == 'global':
# sync = await self.bot.tree.sync()
# else:
# sync = await self.bot.tree.sync(guild=discord.Object(os.environ.get('GUILD_ID')))
# logging.warning(f'sync: {sync}')
# except Exception as e:
# logging.error(f'failed to sync: {e}')
#
# await ctx.send(f'Just ran the sync. Here is the output:\n{sync}')
@commands.command()
@commands.is_owner()
async def sync(self, ctx: Context, guilds: Greedy[Object], spec: Optional[Literal['~', "*"]] = None) -> None:
"""
!sync -> global sync
!sync ~ -> sync current guild
!sync * -> copies all global app commands to current guild and syncs
!sync id_1 id_2 -> syncs guilds with id 1 and 2
"""
if not guilds:
if spec == "~":
fmt = await ctx.bot.tree.sync(guild=ctx.guild)
elif spec == "*":
ctx.bot.tree.copy_global_to(guild=ctx.guild)
fmt = await ctx.bot.tree.sync(guild=ctx.guild)
else:
fmt = await ctx.bot.tree.sync()
await ctx.send(
f"Synced {len(fmt)} commands {'globally' if spec is None else 'to the current guild.'}"
)
return
fmt = 0
for guild in guilds:
try:
await ctx.bot.tree.sync(guild=guild)
except discord.HTTPException:
pass
else:
fmt += 1
await ctx.send(f"Synced the tree to {fmt}/{len(guilds)} guilds.")
async def setup(bot):
await bot.add_cog(Owner(bot))

2967
cogs/players.py Normal file

File diff suppressed because it is too large Load Diff

1979
cogs/transactions.py Normal file

File diff suppressed because it is too large Load Diff

1099
db_calls.py Normal file

File diff suppressed because it is too large Load Diff

1049
db_calls_gameday.py Normal file

File diff suppressed because it is too large Load Diff

1196
db_calls_gameplay.py Normal file

File diff suppressed because it is too large Load Diff

42
gameplay_helpers.py Normal file
View File

@ -0,0 +1,42 @@
from db_calls_gameplay import StratGame, StratPlay, StratLineup, StratManagerAi, patch_play, advance_runners, \
complete_play
def single_onestar(this_play: StratPlay, comp_play: bool = True):
patch_play(this_play.id, locked=True)
advance_runners(this_play.id, num_bases=1)
patch_play(this_play.id, pa=1, ab=1, hit=1)
if comp_play:
complete_play(this_play.id, batter_to_base=1)
def single_wellhit(this_play: StratPlay, comp_play: bool = True):
patch_play(this_play.id, locked=True)
advance_runners(this_play.id, num_bases=2)
patch_play(this_play.id, pa=1, ab=1, hit=1)
if comp_play:
complete_play(this_play.id, batter_to_base=1)
def double_twostar(this_play: StratPlay, comp_play: bool = True):
patch_play(this_play.id, locked=True)
advance_runners(this_play.id, num_bases=2)
patch_play(this_play.id, pa=1, ab=1, hit=1, double=1)
if comp_play:
complete_play(this_play.id, batter_to_base=2)
def double_threestar(this_play: StratPlay, comp_play: bool = True):
patch_play(this_play.id, locked=True)
advance_runners(this_play.id, num_bases=3)
patch_play(this_play.id, pa=1, ab=1, hit=1, double=1)
if comp_play:
complete_play(this_play.id, batter_to_base=2)
def triple(this_play: StratPlay, comp_play: bool = True):
patch_play(this_play.id, locked=True)
advance_runners(this_play.id, num_bases=3)
patch_play(this_play.id, pa=1, ab=1, hit=1, triple=1)
if comp_play:
complete_play(this_play.id, batter_to_base=3)

982
helpers.py Normal file
View File

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

58
majordomo.py Normal file
View File

@ -0,0 +1,58 @@
import asyncio
import datetime
import logging
import os
import discord
from discord.ext import commands
date = f'{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}'
log_level = logging.INFO if os.environ.get('LOG_LEVEL') == 'INFO' else 'WARN'
logging.basicConfig(
filename=f'logs/{date}.log',
format='%(asctime)s - majordomo - %(levelname)s - %(message)s',
level=log_level
)
COGS = [
'cogs.owner',
'cogs.players',
'cogs.transactions',
'cogs.admins',
'cogs.dice',
'cogs.fun',
# 'cogs.gameday',
# 'cogs.gameplay'
]
intents = discord.Intents.default()
intents.message_content = True
intents.members = True
bot = commands.Bot(
command_prefix='!',
intents=intents,
description='The Strat-o-matic Bot\nIf you have questions, feel free to contact Cal.',
case_insensitive=True,
owner_id=258104532423147520
)
@bot.event
async def on_ready():
logging.info('Logged in as:')
logging.info(bot.user.name)
logging.info(bot.user.id)
async def main():
for c in COGS:
try:
await bot.load_extension(c)
logging.info(f'Loaded cog: {c}')
except Exception as e:
logging.error(f'Failed to load cog: {c}')
logging.error(f'{e}')
async with bot:
await bot.start(os.environ.get('BOT_TOKEN'))
asyncio.run(main())

7
requirements.txt Normal file
View File

@ -0,0 +1,7 @@
discord.py @ git+https://github.com/Rapptz/discord.py
requests
pydantic
pygsheets
peewee
bs4
pandas