diff --git a/.env b/.env
deleted file mode 100644
index e232e93..0000000
--- a/.env
+++ /dev/null
@@ -1,152 +0,0 @@
-# .env file for UtilityBot
-# version edited: 1.1.0
-#
-# Version is used for tracking purposes (imagine that)
-VERSION='1.0.4'
-#
-# Cycle time is how often the bot's periodic task runs. This is in seconds
-CYCLE_TIME=600
-#
-# Discord token is the private API token supplied by Discord's developer portal when the bot is created
-# This is a secret token, DO NOT SHARE THIS TOKEN WITH ANYONE!
-# The first link describes how to create a bot
-# https://discordpy.readthedocs.io/en/stable/discord.html
-# The second link takes you to the developer portal in order to make a bot
-# https://discord.com/developers/applications
-DISCORD_TOKEN=
-#
-#
-# ***************************************************************
-# These are the Guild IDs for all Franchises in the MLE league
-# ***************************************************************
-AVIATORS=651145112591663157
-BEARS=308471171840737293
-BLIZZARD=
-BULLS=630706426582663170
-COMETS=471514625138229248
-DEMOLITION=540551409800708128
-DODGERS=540299529174384650
-DUCKS=265899410204917760
-ECLIPSE=762333628461875232
-ELITE=721124808989081640
-EXPRESS=601547100337078409
-FLAMES=387356593508974592
-FOXES=
-HAWKS=593939558341541929
-HIVE=539465795705634826
-HURRICANES=
-JETS=319431481162334218
-KNIGHTS=
-LIGHTNING=722157564686762135
-OUTLAWS=
-PANDAS=407369418956603393
-PIRATES=
-PUFFINS=387532936548974592
-RHINOS=721102068768702485
-SABRES=973616136912506890
-SHADOW=
-SHARKS=721820175762063432
-SPARTANS=539954225795563530
-SPECTRE=650858349700579338
-TYRANTS=
-WIZARDS=426199791593193472
-WOLVES=885004686866907186
-#
-# ***************************************************************
-# These are the Roster Channel IDs for all Franchises in the MLE league
-# ***************************************************************
-AVIATORS_ROSTER=
-BEARS_ROSTER=
-BLIZZARD_ROSTER=
-BULLS_ROSTER=
-COMETS_ROSTER=
-DEMOLITION_ROSTER=
-DODGERS_ROSTER=
-DUCKS_ROSTER=
-ECLIPSE_ROSTER=
-ELITE_ROSTER=
-EXPRESS_ROSTER=
-FLAMES_ROSTER=
-FOXES_ROSTER=
-HAWKS_ROSTER=
-HIVE_ROSTER=
-HURRICANES_ROSTER=
-JETS_ROSTER=
-KNIGHTS_ROSTER=
-LIGHTNING_ROSTER=
-OUTLAWS_ROSTER=
-PANDAS_ROSTER=
-PIRATES_ROSTER=
-PUFFINS_ROSTER=
-RHINOS_ROSTER=
-SABRES_ROSTER=998001955215515659
-SHADOW_ROSTER=
-SHARKS_ROSTER=
-SPARTANS_ROSTER=
-SPECTRE_ROSTER=
-TYRANTS_ROSTER=
-WIZARDS_ROSTER=1249528481612824586
-WOLVES_ROSTER=
-
-# ***************************************************************
-# Fill the following in per the requirements of your application
-# ***************************************************************
-#
-# guild ID (server ID)
-# This is the ID of the server this bot belongs to. Right click and copy from Discord
-# To get the Discord ID, enable developer options in Discord and right click on the server.
-# Select "Copy Server ID" and paste it here
-GUILD=748052192342310925
-#
-# admin channel ID
-# The admin channel posts uptime data of the bot.
-# This is not required (yet) but is recommended.
-# To get the Discord ID, enable developer options in Discord and right click on the channel.
-# Select "Copy Channel ID" and paste it here
-ADMIN_CHANNEL=1257523645937483807
-#
-# notification channel ID
-# The channel the bot will post notifications to when necessary. Like, sprocket updates or errors.
-# This is not required (yet) but is recommended
-# To get the Discord ID, enable developer options in Discord and right click on the channel.
-# Select "Copy Channel ID" and paste it here
-NOTIFICATION_CHANNEL=1214359860502597812
-#
-# server icon
-# This is the 'Emoji' ID of the 'Emoji' that would represent your franchise.
-# This emoji will appear on certain embedded posts / messages.
-# Getting the server icon out of an emoji can be funky.
-# Post the emoji, right click and "Open Link" and copy the number from {} below
-# https://cdn.discordapp.com/emojis/ {THIS IS THE NUMBER YOU WANT }.webp?sizeblahblahblah
-SERVER_ICON = 'https://images-ext-1.discordapp.net/external/8g0PflayqZyQZe8dqTD_wYGPcCpucRWAsnIjV4amZhc/https/i.imgur.com/6anH1sI.png?format=webp&quality=lossless&width=619&height=619'
-#
-# roster images
-# Images that the bot uses during the runroster command
-IMG_STAFF='team\imgs\STAFF.png'
-# IMG_PREMIER='team\imgs\PREMIER.png'
-IMG_MASTER='team\imgs\MASTER.png'
-IMG_CHAMPION='team\imgs\CHAMPION.png'
-IMG_ACADEMY='team\imgs\ACADEMY.png'
-IMG_FOUNDATION='team\imgs\FOUNDATION.png'
-#
-# tourney data
-# TBD
-TOURNEY_INFO_CHANNEL=1232807413967622204
-TOURNEY_CMD_CHANNEL=1232807969301987559
-TOURNEY_GOAL_MUL=1
-TOURNEY_ASSISTS_MUL=1
-TOURNEY_SAVES_MUL=1
-TOURNEY_SUB200_MUL=1
-TOURNEY_200300_MUL=2
-TOURNEY_400PLU_MUL=3
-TOURNEY_RANKED_1TEAM_MUL=1
-TOURNEY_RANKED_2TEAM_MUL=2
-TOURNEY_RANKED_3TEAM_MUL=3
-TOURNEY_SCRIM_RR_1TEAM_MUL=3
-TOURNEY_SCRIM_RR_2TEAM_MUL=8
-TOURNEY_SCRIM_RR_3TEAM_MUL=10
-TOURNEY_SCRIM_TEAM_MUL=5
-#
-# roles data
-# TBD
-ROLES_CHANNEL=1232744137951019018
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e63870e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,183 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# vscode environment
+.vscode/
+
+# json data files
+*.json
+
+# C extensions
+*.so
+
+# Batch files
+*.bat
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# UV
+# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+#uv.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
+.pdm.toml
+.pdm-python
+.pdm-build/
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
+# Ruff stuff:
+.ruff_cache/
+
+# PyPI configuration file
+.pypirc
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 13566b8..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/MLEBot.iml b/.idea/MLEBot.iml
deleted file mode 100644
index 121e58b..0000000
--- a/.idea/MLEBot.iml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
deleted file mode 100644
index 2aad73d..0000000
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
deleted file mode 100644
index 105ce2d..0000000
--- a/.idea/inspectionProfiles/profiles_settings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml
deleted file mode 100644
index 2c51b19..0000000
--- a/.idea/material_theme_project_new.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 5a63ecb..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index b25bf6f..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1dd..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/MLEBot/.idea/.gitignore b/MLEBot/.idea/.gitignore
deleted file mode 100644
index 13566b8..0000000
--- a/MLEBot/.idea/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/MLEBot/.idea/MLEBot.iml b/MLEBot/.idea/MLEBot.iml
deleted file mode 100644
index d0876a7..0000000
--- a/MLEBot/.idea/MLEBot.iml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/MLEBot/.idea/inspectionProfiles/Project_Default.xml b/MLEBot/.idea/inspectionProfiles/Project_Default.xml
deleted file mode 100644
index 64b7622..0000000
--- a/MLEBot/.idea/inspectionProfiles/Project_Default.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/MLEBot/.idea/inspectionProfiles/profiles_settings.xml b/MLEBot/.idea/inspectionProfiles/profiles_settings.xml
deleted file mode 100644
index 105ce2d..0000000
--- a/MLEBot/.idea/inspectionProfiles/profiles_settings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/MLEBot/.idea/material_theme_project_new.xml b/MLEBot/.idea/material_theme_project_new.xml
deleted file mode 100644
index 2915cc9..0000000
--- a/MLEBot/.idea/material_theme_project_new.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/MLEBot/.idea/misc.xml b/MLEBot/.idea/misc.xml
deleted file mode 100644
index 08235dc..0000000
--- a/MLEBot/.idea/misc.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/MLEBot/.idea/modules.xml b/MLEBot/.idea/modules.xml
deleted file mode 100644
index b25bf6f..0000000
--- a/MLEBot/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/MLEBot/.idea/vcs.xml b/MLEBot/.idea/vcs.xml
deleted file mode 100644
index 6c0b863..0000000
--- a/MLEBot/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/MLEBot/__init__.py b/MLEBot/__init__.py
index fc92533..e9ddd03 100644
--- a/MLEBot/__init__.py
+++ b/MLEBot/__init__.py
@@ -1,3 +1,19 @@
-from .mle_bot import MLEBot
-from .mle_commands import MLECommands
-from .lo_commands import LoCommands
\ No newline at end of file
+"""Minor League E-Sports Bot
+ """
+from . import commands
+from . import frames
+from . import services
+from . import tasks
+from . import types
+from .mlebot import MLEBot
+
+__version__ = "1.1.4"
+
+__all__ = (
+ 'commands',
+ 'frames',
+ 'services',
+ 'tasks',
+ 'types',
+ "MLEBot",
+)
diff --git a/MLEBot/__pycache__/__init__.cpython-311.pyc b/MLEBot/__pycache__/__init__.cpython-311.pyc
deleted file mode 100644
index abf7f8c..0000000
Binary files a/MLEBot/__pycache__/__init__.cpython-311.pyc and /dev/null differ
diff --git a/MLEBot/__pycache__/enums.cpython-311.pyc b/MLEBot/__pycache__/enums.cpython-311.pyc
deleted file mode 100644
index d54507c..0000000
Binary files a/MLEBot/__pycache__/enums.cpython-311.pyc and /dev/null differ
diff --git a/MLEBot/__pycache__/franchise.cpython-311.pyc b/MLEBot/__pycache__/franchise.cpython-311.pyc
deleted file mode 100644
index 600cfe5..0000000
Binary files a/MLEBot/__pycache__/franchise.cpython-311.pyc and /dev/null differ
diff --git a/MLEBot/__pycache__/lo_commands.cpython-311.pyc b/MLEBot/__pycache__/lo_commands.cpython-311.pyc
deleted file mode 100644
index 7edb48d..0000000
Binary files a/MLEBot/__pycache__/lo_commands.cpython-311.pyc and /dev/null differ
diff --git a/MLEBot/__pycache__/member.cpython-311.pyc b/MLEBot/__pycache__/member.cpython-311.pyc
deleted file mode 100644
index 9df5ae2..0000000
Binary files a/MLEBot/__pycache__/member.cpython-311.pyc and /dev/null differ
diff --git a/MLEBot/__pycache__/mle_bot.cpython-311.pyc b/MLEBot/__pycache__/mle_bot.cpython-311.pyc
deleted file mode 100644
index 19592a3..0000000
Binary files a/MLEBot/__pycache__/mle_bot.cpython-311.pyc and /dev/null differ
diff --git a/MLEBot/__pycache__/mle_commands.cpython-311.pyc b/MLEBot/__pycache__/mle_commands.cpython-311.pyc
deleted file mode 100644
index 44cda51..0000000
Binary files a/MLEBot/__pycache__/mle_commands.cpython-311.pyc and /dev/null differ
diff --git a/MLEBot/__pycache__/roles.cpython-311.pyc b/MLEBot/__pycache__/roles.cpython-311.pyc
deleted file mode 100644
index 5c7bf67..0000000
Binary files a/MLEBot/__pycache__/roles.cpython-311.pyc and /dev/null differ
diff --git a/MLEBot/__pycache__/sprocket_data_link.cpython-311.pyc b/MLEBot/__pycache__/sprocket_data_link.cpython-311.pyc
deleted file mode 100644
index 0348cac..0000000
Binary files a/MLEBot/__pycache__/sprocket_data_link.cpython-311.pyc and /dev/null differ
diff --git a/MLEBot/__pycache__/task_roster.cpython-311.pyc b/MLEBot/__pycache__/task_roster.cpython-311.pyc
deleted file mode 100644
index 4031a08..0000000
Binary files a/MLEBot/__pycache__/task_roster.cpython-311.pyc and /dev/null differ
diff --git a/MLEBot/__pycache__/task_sprocket.cpython-311.pyc b/MLEBot/__pycache__/task_sprocket.cpython-311.pyc
deleted file mode 100644
index 827366c..0000000
Binary files a/MLEBot/__pycache__/task_sprocket.cpython-311.pyc and /dev/null differ
diff --git a/MLEBot/__pycache__/team.cpython-311.pyc b/MLEBot/__pycache__/team.cpython-311.pyc
deleted file mode 100644
index 8ac7400..0000000
Binary files a/MLEBot/__pycache__/team.cpython-311.pyc and /dev/null differ
diff --git a/MLEBot/commands/__init__.py b/MLEBot/commands/__init__.py
new file mode 100644
index 0000000..3d02747
--- /dev/null
+++ b/MLEBot/commands/__init__.py
@@ -0,0 +1,16 @@
+"""Minor League E-Sports Bot Commands
+ """
+
+from .lo import Commands as lo_commands
+from .mle import Commands as mle_commands
+from .rl import Commands as rl_commands
+
+
+Commands = lo_commands + mle_commands + rl_commands
+
+
+__version__ = '1.1.3'
+
+__all__ = (
+ 'Commands',
+)
diff --git a/MLEBot/commands/lo/__init__.py b/MLEBot/commands/lo/__init__.py
new file mode 100644
index 0000000..ef1a7c4
--- /dev/null
+++ b/MLEBot/commands/lo/__init__.py
@@ -0,0 +1,41 @@
+"""in honor of some folks who helped make MLE what it is,
+ we have immortalized them with these commands.
+ """
+
+from .achilles import Achilles
+from .adi import Adi
+from .bw import Bw
+from .haim import Haim
+from .hoos import Hoos
+from .kd import Kd
+from .kunics import Kunics
+from .maple import Maple
+from .ondo import Ondo
+from .rexton import Rexton
+from .riz import Riz
+from .soviet import Soviet
+from .zb import Zb
+
+
+Commands = [
+ Achilles,
+ Adi,
+ Bw,
+ Haim,
+ Hoos,
+ Kd,
+ Kunics,
+ Maple,
+ Ondo,
+ Rexton,
+ Riz,
+ Soviet,
+ Zb,
+]
+
+
+__version__ = '1.1.3'
+
+__all__ = (
+ 'Commands',
+)
diff --git a/MLEBot/commands/lo/achilles.py b/MLEBot/commands/lo/achilles.py
new file mode 100644
index 0000000..0d26007
--- /dev/null
+++ b/MLEBot/commands/lo/achilles.py
@@ -0,0 +1,23 @@
+"""mr. worldwide
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+class Achilles(Cog):
+ """mr. worldwide
+ """
+
+ @app_commands.command(name='achilles',
+ description='mr. worldwide')
+ @app_commands.default_permissions()
+ async def achilles(self,
+ interaction: discord.Interaction):
+ """mr. worldwide"""
+ await interaction.response.send_message("""https://media.discordapp.net/attachments/1101172529676161155/1257818646005415966/A_Chillis_tendon.png?ex=6685ca66&is=668478e6&hm=59e3c53b51b45477562b895acd641397a1144ebc5e690e6b4b875b9ca83c0ca0&=&format=webp&quality=lossless""")
diff --git a/MLEBot/commands/lo/adi.py b/MLEBot/commands/lo/adi.py
new file mode 100644
index 0000000..f2f90cb
--- /dev/null
+++ b/MLEBot/commands/lo/adi.py
@@ -0,0 +1,25 @@
+"""Brick by boring brick.
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+class Adi(Cog):
+ """Brick by boring brick.
+ """
+
+ @app_commands.command(name='adi',
+ description='Brick by boring brick.')
+ @app_commands.default_permissions()
+ async def adi(self,
+ interaction: discord.Interaction):
+ """Brick by boring brick.
+ """
+ _bricks = [':brick:'] * 20
+ await interaction.response.send_message(' '.join(_bricks))
diff --git a/MLEBot/commands/lo/bw.py b/MLEBot/commands/lo/bw.py
new file mode 100644
index 0000000..8e1b3ad
--- /dev/null
+++ b/MLEBot/commands/lo/bw.py
@@ -0,0 +1,24 @@
+"""Galaxy brain
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+class Bw(Cog):
+ """Galaxy brain
+ """
+
+ @app_commands.command(name='bw',
+ description='Galaxy brain.')
+ @app_commands.default_permissions()
+ async def bw(self,
+ interaction: discord.Interaction):
+ """Galaxy brain
+ """
+ await interaction.response.send_message('KISSEWDAKHTKISS')
diff --git a/MLEBot/commands/lo/haim.py b/MLEBot/commands/lo/haim.py
new file mode 100644
index 0000000..d785bc3
--- /dev/null
+++ b/MLEBot/commands/lo/haim.py
@@ -0,0 +1,25 @@
+"""S(HAIM).
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+class Haim(Cog):
+ """S(HAIM).
+ """
+
+ @app_commands.command(name='haim',
+ description='S(HAIM).')
+ @app_commands.default_permissions()
+ async def haim(self,
+ interaction: discord.Interaction):
+ """S(HAIM).
+ """
+ await interaction.response.send_message(
+ 'https://media.discordapp.net/attachments/670665177863028750/940985245786837022/ezgif.com-gif-maker_1.gif?ex=667cd58d&is=667b840d&hm=0f7548f793f01ec70d4a45093083e3c13310754eb3b7d07a4b78cfd1085c9405&=')
diff --git a/MLEBot/commands/lo/hoos.py b/MLEBot/commands/lo/hoos.py
new file mode 100644
index 0000000..feeed1a
--- /dev/null
+++ b/MLEBot/commands/lo/hoos.py
@@ -0,0 +1,25 @@
+""":kissing_heart:
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+class Hoos(Cog):
+ """:kissing_heart:
+ """
+
+ @app_commands.command(name='hoos',
+ description=':kissing_heart:')
+ @app_commands.default_permissions()
+ async def hoos(self,
+ interaction: discord.Interaction):
+ """:kissing_heart:
+ """
+ await interaction.response.send_message(
+ 'Friends share more DNA than strangers.')
diff --git a/MLEBot/commands/lo/kd.py b/MLEBot/commands/lo/kd.py
new file mode 100644
index 0000000..42bbc16
--- /dev/null
+++ b/MLEBot/commands/lo/kd.py
@@ -0,0 +1,24 @@
+"""KD
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+class Kd(Cog):
+ """KD
+ """
+
+ @app_commands.command(name='kd',
+ description='KD')
+ @app_commands.default_permissions()
+ async def kd(self,
+ interaction: discord.Interaction):
+ """KD
+ """
+ await interaction.response.send_message('` `')
diff --git a/MLEBot/commands/lo/kunics.py b/MLEBot/commands/lo/kunics.py
new file mode 100644
index 0000000..b7020c3
--- /dev/null
+++ b/MLEBot/commands/lo/kunics.py
@@ -0,0 +1,28 @@
+"""PIVOT
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+class Kunics(Cog):
+ """PIVOT
+ """
+
+ @app_commands.command(name='kunics',
+ description='PIVOT')
+ @app_commands.default_permissions()
+ async def kunics(self,
+ interaction: discord.Interaction):
+ """PIVOT
+ """
+ await interaction.response.send_message("""***P I V O T
+I V O T
+V O T
+O T
+T***""")
diff --git a/MLEBot/commands/lo/maple.py b/MLEBot/commands/lo/maple.py
new file mode 100644
index 0000000..308959d
--- /dev/null
+++ b/MLEBot/commands/lo/maple.py
@@ -0,0 +1,24 @@
+"""Oh, Canada...
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+class Maple(Cog):
+ """Oh, Canada...
+ """
+
+ @app_commands.command(name='maple',
+ description='Oh, Canada...')
+ @app_commands.default_permissions()
+ async def maple(self,
+ interaction: discord.Interaction):
+ """Oh, Canada...
+ """
+ await interaction.response.send_message(':flag_ca: Sorry. :flag_ca:')
diff --git a/MLEBot/commands/lo/ondo.py b/MLEBot/commands/lo/ondo.py
new file mode 100644
index 0000000..8b13332
--- /dev/null
+++ b/MLEBot/commands/lo/ondo.py
@@ -0,0 +1,25 @@
+"""ondofir
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+class Ondo(Cog):
+ """ondofir
+ """
+
+ @app_commands.command(name='ondo',
+ description='ondofir')
+ @app_commands.default_permissions()
+ async def ondo(self,
+ interaction: discord.Interaction):
+ """ondofir
+ """
+ await interaction.response.send_message(
+ 'https://media.discordapp.net/attachments/532946038080798730/709472405914910840/unknown.png?ex=667d06ea&is=667bb56a&hm=968a10b7a304b47c14a13b4333766419a3ae24f4a8c809910d241ee57f1689f0&=&format=webp&quality=lossless')
diff --git a/MLEBot/commands/lo/rexton.py b/MLEBot/commands/lo/rexton.py
new file mode 100644
index 0000000..3ab8265
--- /dev/null
+++ b/MLEBot/commands/lo/rexton.py
@@ -0,0 +1,24 @@
+"""opens door...
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+class Rexton(Cog):
+ """opens door...
+ """
+
+ @app_commands.command(name='rexton',
+ description='opens door...')
+ @app_commands.default_permissions()
+ async def rexton(self,
+ interaction: discord.Interaction):
+ """opens door...
+ """
+ await interaction.response.send_message('https://media.discordapp.net/attachments/832819833611223081/996629449242058852/image_15_1.png?ex=667ce601&is=667b9481&hm=d3c4f0f7ae0074167a828db9b5b6deb8681a2c7bf1b93e428187327a1e3cea9e&=&format=webp&quality=lossless&width=1179&height=619')
diff --git a/MLEBot/commands/lo/riz.py b/MLEBot/commands/lo/riz.py
new file mode 100644
index 0000000..98f6539
--- /dev/null
+++ b/MLEBot/commands/lo/riz.py
@@ -0,0 +1,24 @@
+"""@Riz
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+class Riz(Cog):
+ """@Riz
+ """
+
+ @app_commands.command(name='riz',
+ description='@Riz')
+ @app_commands.default_permissions()
+ async def riz(self,
+ interaction: discord.Interaction):
+ """@Riz
+ """
+ await interaction.response.send_message('neck')
diff --git a/MLEBot/commands/lo/soviet.py b/MLEBot/commands/lo/soviet.py
new file mode 100644
index 0000000..dec31f9
--- /dev/null
+++ b/MLEBot/commands/lo/soviet.py
@@ -0,0 +1,26 @@
+"""Oh, the burning...
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+class Soviet(Cog):
+ """Oh, the burning...
+ """
+
+ @app_commands.command(name='soviet',
+ description='Oh, the burning...')
+ @app_commands.default_permissions()
+ async def soviet(self,
+ interaction: discord.Interaction):
+ """Oh, the burning...
+ """
+ await interaction.response.send_message("""***The Crucifixion of Morality***
+*The Crucifixion of Morality is an image of 69 horseshoe crabmen and an alpaca. The alpaca is striking a menacing pose. The 69 horseshoe crabmen are burning. *
+https://cdn.mlesports.dev/public/The_Crucifixion_of_Morality.png""")
diff --git a/MLEBot/commands/lo/zb.py b/MLEBot/commands/lo/zb.py
new file mode 100644
index 0000000..d24d4d8
--- /dev/null
+++ b/MLEBot/commands/lo/zb.py
@@ -0,0 +1,23 @@
+"""My link is borked, halp
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+class Zb(Cog):
+ """My link is borked, halp
+ """
+ @app_commands.command(name='zb',
+ description='My link is borked, halp')
+ @app_commands.default_permissions()
+ async def zb(self,
+ interaction: discord.Interaction):
+ """My link is borked, halp
+ """
+ await interaction.response.send_message("https://tinyurl.com/rmblmv8")
diff --git a/MLEBot/commands/mle/__init__.py b/MLEBot/commands/mle/__init__.py
new file mode 100644
index 0000000..8ba2130
--- /dev/null
+++ b/MLEBot/commands/mle/__init__.py
@@ -0,0 +1,32 @@
+"""Minor League E-Sports Bot Commands
+ """
+from . import test_commands
+
+from .lookup import Lookup
+from .query import Query
+from .rebuild import Rebuild
+from .salary import Salary
+from .showusage import ShowUsage
+from .teameligibility import TeamEligibility
+from .teaminfo import TeamInfo
+from .updatesprocket import UpdateSprocket
+
+
+Commands = [
+ Lookup,
+ Query,
+ Rebuild,
+ Salary,
+ ShowUsage,
+ TeamEligibility,
+ TeamInfo,
+ UpdateSprocket,
+]
+
+
+__version__ = '1.1.4'
+
+__all__ = (
+ 'Commands',
+ 'test_commands',
+)
diff --git a/MLEBot/commands/mle/lookup.py b/MLEBot/commands/mle/lookup.py
new file mode 100644
index 0000000..116a6b2
--- /dev/null
+++ b/MLEBot/commands/mle/lookup.py
@@ -0,0 +1,38 @@
+"""Lookup player by MLE name.
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+from ...const import ERR_BAD_NAME
+from ...frames import salary_card
+from ...services.sprocket import lookup_rl
+from ...types import Member
+
+
+class Lookup(Cog):
+ """Lookup player by MLE name.
+ """
+
+ @app_commands.command(name='lookup',
+ description='Lookup player by MLE name.')
+ @app_commands.describe(mle_name='Player name.')
+ @app_commands.default_permissions()
+ async def lookup(self,
+ interaction: discord.Interaction,
+ mle_name: str) -> None:
+ 'Lookup player by MLE name provided.'
+ member: Member = lookup_rl(self._parent.sprocket.links,
+ name=mle_name)
+ if not member:
+ await self._parent.send_notification(interaction,
+ ERR_BAD_NAME,
+ as_reply=True)
+ return
+ await interaction.response.send_message(embed=salary_card(member))
diff --git a/MLEBot/commands/mle/query.py b/MLEBot/commands/mle/query.py
new file mode 100644
index 0000000..91305ad
--- /dev/null
+++ b/MLEBot/commands/mle/query.py
@@ -0,0 +1,96 @@
+"""Lookup groups of players by provided filter.
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog, InteractionPagination
+
+
+from ... import const
+from ...frames import mle_card
+
+
+class Query(Cog):
+ """Lookup groups of players by provided filter.
+ """
+
+ @app_commands.command(name='query',
+ description='Lookup groups of players by provided filter.')
+ @app_commands.describe(league_filter='[PL, ML, CL, AL, FL]')
+ @app_commands.describe(query_filter='[FA, RFA, Waivers, Pend]')
+ @app_commands.describe(sorting='[salary, current_scrim_points, name]')
+ @app_commands.choices(league_filter=[
+ app_commands.Choice(name='PL', value=const.SPR_SG_PL),
+ app_commands.Choice(name='ML', value=const.SPR_SG_ML),
+ app_commands.Choice(name='CL', value=const.SPR_SG_CL),
+ app_commands.Choice(name='AL', value=const.SPR_SG_AL),
+ app_commands.Choice(name='FL', value=const.SPR_SG_FL)
+ ],
+ query_filter=[
+ app_commands.Choice(name='FA', value='fa'),
+ app_commands.Choice(name='RFA', value='rfa'),
+ app_commands.Choice(name='Waivers', value='waivers'),
+ app_commands.Choice(name='Pend', value='pend')
+ ],
+ sorting=[
+ app_commands.Choice(name='Salary', value='salary'),
+ app_commands.Choice(name='Scrim Points',
+ value='current_scrim_points'),
+ app_commands.Choice(name='Name', value='name')
+ ])
+ @app_commands.default_permissions()
+ async def query(self,
+ interaction: discord.Interaction,
+ league_filter: app_commands.Choice[str],
+ query_filter: str,
+ sorting: str) -> None:
+ """query sprocket db with filters
+
+ Args:
+ interaction (discord.Interaction): interaction of query
+ league_filter (app_commands.Choice[str]): which league?
+ query_filter (str): which player pool?
+ sorting (str): sorting type?
+ """
+ _players = sorted([x for x in self._parent.sprocket.links.players.data if x['franchise'].lower(
+ ) == query_filter.lower() and x['skill_group'] == league_filter.value], key=lambda x: x[sorting], reverse=True)
+
+ if len(_players) == 0:
+ emb = mle_card('**Filtered Players**\n\n',
+ 'There were no players to be found for this query!')
+ await interaction.response.send_message(embed=emb)
+ return
+
+ async def get_page(page: int,
+ as_timout: bool = False):
+ emb: discord.Embed = mle_card('**Filtered Players**\n\n',
+ f'Players filtered for `{query_filter}`\n'
+ f'Sorted by `{sorting}`')
+
+ if as_timout:
+ emb.add_field(name='**`Timeout`**',
+ value='This command has timed out. Type `[ub.help]` for help.')
+ emb.set_footer(text='Page `1` of 1')
+ return emb, 0
+
+ elements_per_page = 15
+ offset = (page - 1) * elements_per_page
+
+ emb.add_field(name='**Sal | Points | Name**',
+ value='\n'.join(
+ [
+ f"`{str(_p['salary']).ljust(4)} | {str(_p['current_scrim_points']).ljust(4)} | {_p['name']}`"
+ for _p in _players[offset:offset + elements_per_page]]),
+ inline=False)
+
+ total_pages = InteractionPagination.compute_total_pages(len(_players),
+ elements_per_page)
+
+ emb.set_footer(text=f'Page {page} of {total_pages}')
+ return emb, total_pages
+
+ await InteractionPagination(interaction, get_page).navigate()
diff --git a/MLEBot/commands/mle/rebuild.py b/MLEBot/commands/mle/rebuild.py
new file mode 100644
index 0000000..6123d3f
--- /dev/null
+++ b/MLEBot/commands/mle/rebuild.py
@@ -0,0 +1,32 @@
+"""Rebuild bot meta data.
+"""
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+class Rebuild(Cog):
+ """Rebuild bot meta data.
+ """
+
+ @app_commands.command(name='rebuild',
+ description='Rebuild bot meta data.')
+ @app_commands.guilds(1043295434828947547)
+ @app_commands.default_permissions()
+ async def rebuild(self,
+ interaction: discord.Interaction) -> None:
+ """Rebuild bot meta data."""
+ await interaction.response.defer()
+ if await self._parent.rebuild():
+ await self._parent.send_notification(interaction,
+ 'Success!.',
+ as_followup=True)
+ else:
+ await self._parent.send_notification(interaction,
+ 'An error has occured.',
+ as_followup=True)
diff --git a/MLEBot/commands/mle/salary.py b/MLEBot/commands/mle/salary.py
new file mode 100644
index 0000000..f6292d3
--- /dev/null
+++ b/MLEBot/commands/mle/salary.py
@@ -0,0 +1,36 @@
+"""Show player salary.
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+from ...const import ERR_BAD_NAME
+from ...frames import salary_card
+from ...services.sprocket import lookup_rl
+from ...types import Member
+
+
+class Salary(Cog):
+ """Show player salary.
+ """
+
+ @app_commands.command(name='salary',
+ description='Lookup your salary card.')
+ @app_commands.default_permissions()
+ async def lookup(self,
+ interaction: discord.Interaction) -> None:
+ 'Show player salary.'
+ member: Member = lookup_rl(self._parent.sprocket.links,
+ discord_id=interaction.user.id)
+ if not member:
+ await self._parent.send_notification(interaction,
+ ERR_BAD_NAME,
+ as_reply=True)
+ return
+ await interaction.response.send_message(embed=salary_card(member))
diff --git a/MLEBot/commands/mle/showusage.py b/MLEBot/commands/mle/showusage.py
new file mode 100644
index 0000000..cae0784
--- /dev/null
+++ b/MLEBot/commands/mle/showusage.py
@@ -0,0 +1,41 @@
+"""show play usage of players for a franchise.
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+from ...const import ERR_BAD_FRANCHISE
+from ...frames import usage_card
+from ...services.sprocket import lookup_franchise
+
+
+class ShowUsage(Cog):
+ """show play usage of players for a franchise.
+ """
+
+ @app_commands.command(name='showusage',
+ description='show play usage of players for a franchise.')
+ @app_commands.default_permissions()
+ async def showusage(self,
+ interaction: discord.Interaction) -> None:
+ """show usage of members from a user's franchise
+
+ Args:
+ interaction (discord.Interaction): discord.Interaction
+ """
+ await interaction.response.defer()
+ franchise = lookup_franchise(self._parent.sprocket.links,
+ discord_id=interaction.user.id)
+ if not franchise:
+ await self._parent.send_notification(interaction,
+ ERR_BAD_FRANCHISE,
+ as_followup=True)
+ return
+
+ await interaction.followup.send(embed=usage_card(franchise))
diff --git a/MLEBot/commands/mle/teameligibility.py b/MLEBot/commands/mle/teameligibility.py
new file mode 100644
index 0000000..5056018
--- /dev/null
+++ b/MLEBot/commands/mle/teameligibility.py
@@ -0,0 +1,53 @@
+"""Show team eligibility.
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+from ... import const
+from ...frames import teameligibility_card
+from ...types import Franchise
+from ...services.sprocket import lookup_franchise
+
+
+class TeamEligibility(Cog):
+ """Show team eligibility.
+ """
+
+ @app_commands.command(name='teameligibility',
+ description='Show team eligibility.')
+ @app_commands.describe(league='[PL, ML, CL, AL, FL]')
+ @app_commands.choices(league=[
+ app_commands.Choice(name='PL', value=const.SPR_SG_PL),
+ app_commands.Choice(name='ML', value=const.SPR_SG_ML),
+ app_commands.Choice(name='CL', value=const.SPR_SG_CL),
+ app_commands.Choice(name='AL', value=const.SPR_SG_AL),
+ app_commands.Choice(name='FL', value=const.SPR_SG_FL)
+ ])
+ @app_commands.default_permissions()
+ async def teameligibility(self,
+ interaction: discord.Interaction,
+ league: app_commands.Choice[str]) -> None:
+ """get team eligibility
+
+ Args:
+ interaction (discord.Interaction): discord interaction
+ league (app_commands.Choice[str]): which league to display
+ """
+ await interaction.response.defer()
+ franchise: Franchise = lookup_franchise(self._parent.sprocket.links,
+ discord_id=interaction.user.id)
+ if not franchise:
+ await self._parent.send_notification(interaction,
+ const.ERR_BAD_FRANCHISE,
+ as_followup=True)
+ return
+
+ await interaction.followup.send(embed=teameligibility_card(franchise,
+ league.value))
diff --git a/MLEBot/commands/mle/teaminfo.py b/MLEBot/commands/mle/teaminfo.py
new file mode 100644
index 0000000..1855d84
--- /dev/null
+++ b/MLEBot/commands/mle/teaminfo.py
@@ -0,0 +1,39 @@
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+from ...const import ERR_BAD_FRANCHISE
+from ...frames import teaminfo_card
+from ...services.sprocket import lookup_franchise
+from ...types import Franchise
+
+
+class TeamInfo(Cog):
+ """Get info about a team.
+ """
+
+ @app_commands.command(name='teaminfo',
+ description='Get info about a team.')
+ @app_commands.describe(team_name='MLE Team to get info about.')
+ @app_commands.default_permissions()
+ async def teaminfo(self,
+ interaction: discord.Interaction,
+ team_name: str):
+ """Get info about a team.
+ """
+ await interaction.response.defer()
+ franchise: Franchise = lookup_franchise(self._parent.sprocket.links,
+ name=team_name)
+ if not franchise:
+ await self._parent.send_notification(interaction,
+ ERR_BAD_FRANCHISE,
+ as_followup=True)
+ return
+
+ await interaction.followup.send(embed=teaminfo_card(franchise))
diff --git a/MLEBot/commands/mle/test_commands.py b/MLEBot/commands/mle/test_commands.py
new file mode 100644
index 0000000..4adecdc
--- /dev/null
+++ b/MLEBot/commands/mle/test_commands.py
@@ -0,0 +1,25 @@
+"""test commands for pydisco bot
+ """
+from __future__ import annotations
+
+
+import unittest
+
+
+from .lookup import Lookup
+
+
+__all__ = (
+ 'TestCommands',
+)
+
+
+class TestCommands(unittest.TestCase):
+ """test commands for pydisco bot
+ """
+
+ def test_lookup(self):
+ """test lookup command
+ """
+ cmd = Lookup()
+ self.assertIsNotNone(cmd) # to update later
diff --git a/MLEBot/commands/mle/updatesprocket.py b/MLEBot/commands/mle/updatesprocket.py
new file mode 100644
index 0000000..46e563e
--- /dev/null
+++ b/MLEBot/commands/mle/updatesprocket.py
@@ -0,0 +1,27 @@
+"""Update info from sprocket.
+ """
+from __future__ import annotations
+
+
+import discord
+from discord import app_commands
+
+
+from pydiscobot import Cog
+
+
+class UpdateSprocket(Cog):
+ """Update info from sprocket.
+ """
+
+ @app_commands.command(name='updatesprocket',
+ description='Update info from sprocket.')
+ @app_commands.default_permissions()
+ async def updatesprocket(self,
+ interaction: discord.Interaction) -> None:
+ await interaction.response.defer()
+ self._parent.sprocket.reset()
+ await self._parent.sprocket.run()
+ await self._parent.send_notification(interaction,
+ 'League-Sprocket update complete.',
+ as_followup=True)
diff --git a/MLEBot/commands/rl/__init__.py b/MLEBot/commands/rl/__init__.py
new file mode 100644
index 0000000..1ab79dd
--- /dev/null
+++ b/MLEBot/commands/rl/__init__.py
@@ -0,0 +1,12 @@
+"""Rocket League Specific Commands
+ """
+
+__version__ = '1.1.1'
+
+__all__ = (
+
+)
+
+Commands = [
+
+]
diff --git a/MLEBot/const.py b/MLEBot/const.py
new file mode 100644
index 0000000..10ead5f
--- /dev/null
+++ b/MLEBot/const.py
@@ -0,0 +1,199 @@
+"""Minor League E-Sports Constants
+ """
+
+# ERR - Error
+ERR_BAD_NAME = 'The name provided could not be found in our database.\nIf this is an issue please contact support.'
+ERR_BAD_FRANCHISE = 'Could not resolve this franchise from sprocket data!'
+
+# SPR - Sprocket
+SPR_INFO = '\n'.join([
+ 'Data gathered by sprocket public data links.',
+ 'See more at [sprocket links](https://f004.backblazeb2.com/file/sprocket-artifacts/public/pages/index.html)'
+])
+
+# DL - Datalink
+SPR_DL_MEMBERS = "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/members.json"
+SPR_DL_PLAYERS = "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/players.json"
+SPR_DL_PLA_STATS = "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/player_stats.json"
+SPR_DL_TEAMS = "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/teams.json"
+SPR_DL_SCRIMS = "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/scrim_stats.json"
+SPR_DL_FIXT = "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/schedules/fixtures.json"
+SPR_DL_MATCHES = "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/schedules/matches.json"
+SPR_DL_MAT_GRP = "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/schedules/match_groups.json"
+SPR_DL_TRACKER = "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/trackers.json"
+SPR_DL_USAGE = "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/role_usages.json"
+
+# HK - Hash-Key
+# If None: No unique id to identify
+SPR_HK_MEMBERS = "discord_id"
+SPR_HK_MEMBERS_SECONDARY = "name"
+SPR_HK_PLAYERS = "member_id"
+SPR_HK_PLA_STATS = "member_id"
+SPR_HK_TEAMS = "Franchise"
+SPR_HK_SCRIMS = None
+SPR_HK_FIXT = 'fixture_id'
+SPR_HK_MATCHES = 'match_id'
+SPR_HK_MAT_GRP = 'match_group_id'
+SPR_HK_TRACKER = "mleid"
+SPR_HK_USAGE = None
+
+# SG - Skill Group
+SPR_SG_PL = 'Premier League'
+SPR_SG_ML = 'Master League'
+SPR_SG_CL = 'Champion League'
+SPR_SG_AL = 'Academy League'
+SPR_SG_FL = 'Foundation League'
+
+SPR_SG_ALL = [
+ SPR_SG_PL,
+ SPR_SG_ML,
+ SPR_SG_CL,
+ SPR_SG_AL,
+ SPR_SG_FL,
+]
+
+SALARY_CAP_PL = 95.0
+SALARY_CAP_ML = 82.0
+SALARY_CAP_CL = 69.5
+SALARY_CAP_AL = 57.5
+SALARY_CAP_FL = 39.5
+
+SLOT_USAGE_STD = 8
+SLOT_USAGE_DBL = 6
+SLOT_USAGE_TTL = 12
+
+AVIATORS = "Aviators"
+BEARS = "Bears"
+BLIZZARD = "Blizzard"
+BULLS = "Bulls"
+COMETS = "Comets"
+DEMOLITION = "Demolition"
+DODGERS = "Dodgers"
+DUCKS = "Ducks"
+ECLIPSE = "Eclipse"
+ELITE = "Elite"
+EXPRESS = "Express"
+FLAMES = "Flames"
+FOXES = "Foxes"
+HAWKS = "Hawks"
+HIVE = "Hive"
+HURRICANES = "Hurricanes"
+JETS = "Jets"
+KNIGHTS = "Knights"
+LIGHTNING = "Lightning"
+OUTLAWS = "Outlaws"
+PANDAS = "Pandas"
+PIRATES = "Pirates"
+PUFFINS = "Puffins"
+RHINOS = "Rhinos"
+SABRES = "Sabres"
+SHADOW = "Shadow"
+SHARKS = "Sharks"
+SPARTANS = "Spartans"
+SPECTRE = "Spectre"
+TYRANTS = "Tyrants"
+WAIVERS = "Waivers"
+WIZARDS = "Wizards"
+WOLVES = "Wolves"
+FRANCHISE_MANAGER = 'Franchise Manager'
+GENERAL_MANAGER = 'General Manager'
+ASSISTANT_GENERAL_MANAGER = 'Assistant General Manager'
+CAPTAIN = 'Captain'
+SOCIAL_MEDIA = 'Social Media'
+PREMIER_LEAGUE = 'Premier League'
+MASTER_LEAGUE = 'Master League'
+CHAMPION_LEAGUE = 'Champion League'
+ACADEMY_LEAGUE = 'Academy League'
+FOUNDATION_LEAGUE = 'Foundation League'
+ROCKET_LEAGUE = 'Rocket League'
+PR_SUPPORT = 'PR Support'
+FA = 'Free Agent'
+FP = 'Former Player'
+PEND = 'Pending'
+
+ALL_MLE_ROLES = [
+ AVIATORS,
+ BEARS,
+ BLIZZARD,
+ BULLS,
+ COMETS,
+ DEMOLITION,
+ DODGERS,
+ DUCKS,
+ ECLIPSE,
+ ELITE,
+ EXPRESS,
+ FLAMES,
+ FOXES,
+ HAWKS,
+ HIVE,
+ HURRICANES,
+ JETS,
+ KNIGHTS,
+ LIGHTNING,
+ OUTLAWS,
+ PANDAS,
+ PIRATES,
+ PUFFINS,
+ RHINOS,
+ SABRES,
+ SHADOW,
+ SHARKS,
+ SPARTANS,
+ SPECTRE,
+ TYRANTS,
+ WAIVERS,
+ WIZARDS,
+ WOLVES,
+ FRANCHISE_MANAGER,
+ GENERAL_MANAGER,
+ ASSISTANT_GENERAL_MANAGER,
+ CAPTAIN,
+ SOCIAL_MEDIA,
+ PREMIER_LEAGUE,
+ MASTER_LEAGUE,
+ CHAMPION_LEAGUE,
+ ACADEMY_LEAGUE,
+ FOUNDATION_LEAGUE,
+ ROCKET_LEAGUE,
+ PR_SUPPORT,
+ FA,
+ FP,
+ PEND,
+]
+
+ALL_TEAMS = [
+ AVIATORS,
+ BEARS,
+ BLIZZARD,
+ BULLS,
+ COMETS,
+ DEMOLITION,
+ DODGERS,
+ DUCKS,
+ ECLIPSE,
+ ELITE,
+ EXPRESS,
+ FLAMES,
+ FOXES,
+ HAWKS,
+ HIVE,
+ HURRICANES,
+ JETS,
+ KNIGHTS,
+ LIGHTNING,
+ OUTLAWS,
+ PANDAS,
+ PIRATES,
+ PUFFINS,
+ RHINOS,
+ SABRES,
+ SHADOW,
+ SHARKS,
+ SPARTANS,
+ SPECTRE,
+ TYRANTS,
+ WAIVERS,
+ WIZARDS,
+ WOLVES,
+]
diff --git a/MLEBot/enums.py b/MLEBot/enums.py
deleted file mode 100644
index 34fdbcc..0000000
--- a/MLEBot/enums.py
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env python
-""" Minor League E-Sports Enumerations
-# Author: irox_rl
-# Purpose: Host MLE related enumerations to be used throughout this project
-# Version 1.0.2
-"""
-
-# local imports #
-
-# non-local imports #
-from enum import Enum
-
-
-class LeagueEnum(Enum):
- """ MLE League Enumeration Class
- """
- Premier_League = 1
- Master_League = 2
- Champion_League = 3
- Academy_League = 4
- Foundation_League = 5
diff --git a/MLEBot/frames.py b/MLEBot/frames.py
new file mode 100644
index 0000000..5a2321c
--- /dev/null
+++ b/MLEBot/frames.py
@@ -0,0 +1,292 @@
+"""frames module for MLEBot
+ """
+from __future__ import annotations
+
+
+import os
+
+
+import discord
+
+
+from pydiscobot import (
+ frame,
+ EmbedField,
+)
+
+
+from . import const
+from .types.sprocket import Member, Franchise, PlayerRL, MLEFranchiseTeam
+
+
+def mle_card(title: str,
+ descr: str | None = None,
+ franchise: dict | None = None,
+ fields: list[EmbedField] | None = None) -> discord.Embed:
+ """get generic Minor League E-Sports Embed 'card' for consistent formatting.
+
+ Args:
+ title (str): title of the embed
+ descr (str | None, optional): description to add. Defaults to None.
+ franchise (dict | None, optional): franchise for colouring & thumbnails. Defaults to None.
+
+ Returns:
+ discord.Embed: generic MLE formatted embed
+ """
+ if not fields:
+ fields = []
+
+ color_str = os.getenv('MLE_COLOR') if not franchise else\
+ franchise['Primary Color']
+
+ url_str = os.getenv('MLE_LOGO_URL') if not franchise else\
+ franchise['Photo URL']
+
+ embed = frame.get_frame(title,
+ descr,
+ fields,
+ color_str,
+ url_str)
+
+ return embed
+
+
+def salary_card(member: Member):
+ """Minor League E-Sports Rocket League Player
+ Salary Card
+
+ Args:
+ member (dict): sprocket member
+ player (dict): sprocket player
+ franchise (dict): sprocket franchise
+ tracker (dict): sprocket tracker
+
+ Returns:
+ discord.Embed: salary card for specified player
+ """
+ player = member.rl_player
+ tracker = member.rl_player.tracker
+
+ title = f"**{member.name} Sprocket Info**"
+
+ descr = const.SPR_INFO
+
+ fields = [
+ EmbedField('MLE Name', f"`{member.name}`", True),
+ EmbedField('MLE ID', f"`{member.mle_id}`", True),
+ EmbedField('Salary', f"`{player.salary}`", True),
+ EmbedField('League', f"`{player.skill_group}`", True),
+ EmbedField('Scrim Pts', f"`{player.scrim_points}`", True),
+ EmbedField('Eligible?', "`Yes`" if player.eligible else "`No`", True),
+ EmbedField('Franchise', f"`{player.franchise}`", True),
+ EmbedField('Staff?', f"`{player.staff_position}`", True),
+ EmbedField('Role', f"`{player.slot}`", True),
+ ]
+
+ if tracker:
+ fields.append(EmbedField('**Tracker Link**',
+ member.rl_player.tracker['tracker']))
+
+ embed = mle_card(title, descr, member.franchise, fields)
+
+ return embed
+
+
+def teameligibility_card(franchise: Franchise,
+ league: str):
+ """Minor League E-Sports Rocket League
+ Team Eligibility Card
+
+ Args:
+ franchise (dict): sprocket franchise
+ players (dict): sprocket players dictionary
+
+ Returns:
+ discord.Embed: team usage card
+ """
+ title = f"**{franchise.name} Slot Usage Info**"
+ descr = const.SPR_INFO
+
+ fields: list[EmbedField] = []
+
+ players: MLEFranchiseTeam = franchise.players_rl.by_skill_group(
+ league).slot_sorted_players()
+
+ if not players:
+ fields.append(EmbedField(name='**Error**',
+ value='An error has occured...'))
+ return mle_card(title, descr, franchise.franchise_meta, fields)
+
+ ljust_limit = 8
+
+ for _p in players:
+ fields.append(EmbedField(name=f"**{_p.name}**",
+ value=f"`{'Role:'.ljust(ljust_limit)}` {_p.slot}\n"
+ f"`{'Salary:'.ljust(ljust_limit)}` {_p.salary}\n"
+ f"`{'Points:'.ljust(ljust_limit)}` {str(_p.scrim_points)}\n"
+ f"`{'Until:'.ljust(ljust_limit)}` {_p.eligibility_date}"))
+
+ return mle_card(title, descr, franchise.franchise_meta, fields)
+
+
+def teaminfo_card(franchise: Franchise):
+ """Minor League E-Sports Rocket League
+ Team Info Card
+
+ Args:
+ franchise (dict): sprocket franchise
+
+ Returns:
+ discord.Embed: team info card
+ """
+ title = f"{franchise.name} Roster"
+ descr = const.SPR_INFO
+
+ fields = [
+ EmbedField('**Franchise Manager**',
+ f"`{franchise.fm['name']}`" if franchise.fm else "N/A"),
+ EmbedField('**General Managers**',
+ "\n".join([f"`{gm['name']}`" for gm in franchise.gms])),
+ ]
+
+ if len(franchise.agms) > 0:
+ fields.append(EmbedField('**Assistant General Managers**',
+ "\n".join([f"`{agm['name']}`" for agm in franchise.agms])))
+
+ if len(franchise.captains) > 0:
+ fields.append(EmbedField('**Captains**',
+ "\n".join([f"`{x['name']}`" for x in franchise.captains])))
+
+ if len(franchise.pr) > 0:
+ fields.append(EmbedField('**PR Supports**',
+ "\n".join([f"`{x['name']}`" for x in franchise.pr])))
+
+ def _team_info(players: list[PlayerRL],
+ league_name: str,
+ salary_cap: float) -> list[EmbedField]:
+ if not players:
+ return None
+
+ @staticmethod
+ def _player_info(p) -> str | None:
+ """get string of info for player"""
+ slot = p['slot'].removeprefix('PLAYER')
+ return f"`{slot} | {p['salary']} | {p['name']}`"
+
+ def _team_salary(players: list[PlayerRL],
+ league_name: str,
+ salary_cap: float) -> str:
+ top_sals = sorted(players,
+ key=lambda p: p.player['salary'],
+ reverse=True)
+ sal_ceiling = 0.0
+ range_length = 5 if len(top_sals) >= 5 else len(top_sals)
+ for i in range(range_length):
+ sal_ceiling += top_sals[i].player['salary']
+ _signable_str = f"+{top_sals[-1].player['salary'] + (salary_cap - sal_ceiling)}" if sal_ceiling <= salary_cap else "NONE"
+ return f'**`[{sal_ceiling} / {salary_cap}] [{_signable_str}]` {league_name}**'
+
+ fields: list[EmbedField] = []
+ players_strings = '\n'.join(
+ [_player_info(player.player) for player in players if player.player is not None])
+
+ fields.append(EmbedField(name=_team_salary(players,
+ league_name,
+ salary_cap),
+ value=players_strings))
+ return fields
+
+ fields.append(EmbedField('**Roster**',
+ '**`[Top5/SalCap] [CanSign] League`**'))
+
+ pl = _team_info(franchise.players_rl.pl.slot_sorted_players(),
+ const.SPR_SG_PL,
+ const.SALARY_CAP_PL)
+ ml = _team_info(franchise.players_rl.ml.slot_sorted_players(),
+ const.SPR_SG_ML,
+ const.SALARY_CAP_ML)
+ cl = _team_info(franchise.players_rl.cl.slot_sorted_players(),
+ const.SPR_SG_CL,
+ const.SALARY_CAP_CL)
+ al = _team_info(franchise.players_rl.al.slot_sorted_players(),
+ const.SPR_SG_AL,
+ const.SALARY_CAP_AL)
+ fl = _team_info(franchise.players_rl.fl.slot_sorted_players(),
+ const.SPR_SG_FL,
+ const.SALARY_CAP_FL)
+ if pl:
+ fields.extend(pl)
+ if ml:
+ fields.extend(ml)
+ if cl:
+ fields.extend(cl)
+ if al:
+ fields.extend(al)
+ if fl:
+ fields.extend(fl)
+
+ return mle_card(title,
+ descr,
+ franchise.franchise_meta,
+ fields)
+
+
+def usage_card(franchise: Franchise):
+ """Minor League E-Sports Rocket League
+ Usage Card
+
+ Args:
+ franchise (dict): sprocket franchise
+ players (dict): sprocket players dictionary
+
+ Returns:
+ discord.Embed: team usage card
+ """
+ title = f"**{franchise.name} Slot Usage Info**"
+ descr = const.SPR_INFO
+
+ fields = [
+ EmbedField('**Season Slot Allowances**',
+ f'`Dbl: {const.SLOT_USAGE_DBL} | Std: {const.SLOT_USAGE_STD} | Ttl: {const.SLOT_USAGE_TTL}`')
+ ]
+
+ if franchise.players_rl.pl:
+ fields.append(EmbedField(
+ name='Premier',
+ value='\n'.join([_player_usage_string(x)
+ for x in franchise.players_rl.pl])))
+
+ if franchise.players_rl.ml:
+ fields.append(EmbedField(
+ name='Master',
+ value='\n'.join([_player_usage_string(x)
+ for x in franchise.players_rl.ml])))
+
+ if franchise.players_rl.cl:
+ fields.append(EmbedField(
+ name='Champion',
+ value='\n'.join([_player_usage_string(x)
+ for x in franchise.players_rl.cl])))
+
+ if franchise.players_rl.al:
+ fields.append(EmbedField(
+ name='Academy',
+ value='\n'.join([_player_usage_string(x)
+ for x in franchise.players_rl.al])))
+
+ if franchise.players_rl.fl:
+ fields.append(EmbedField(
+ name='Foundation',
+ value='\n'.join([_player_usage_string(x)
+ for x in franchise.players_rl.fl])))
+
+ return mle_card(title, descr, franchise.franchise_meta, fields)
+
+
+def _player_usage_string(player: PlayerRL):
+ slot = player.slot.removeprefix('PLAYER')
+ dbl_use = player.usage_doubles
+ std_use = player.usage_standard
+ ttl_use = player.usage_total
+ name = player.name
+ return f"`{slot} 2s: {dbl_use} | 3s: {std_use} | All: {ttl_use} | {name}`"
diff --git a/MLEBot/franchise.py b/MLEBot/franchise.py
deleted file mode 100644
index a635b28..0000000
--- a/MLEBot/franchise.py
+++ /dev/null
@@ -1,171 +0,0 @@
-#!/usr/bin/env python
-""" Minor League E-Sports Franchise
-# Author: irox_rl
-# Purpose: General Functions of a League Franchise
-# Version 1.0.6
-#
-# v1.0.6 - include salary caps until sprocket does.
-"""
-
-# local imports #
-from enums import *
-from team import Team
-
-# non-local imports #
-import discord
-from discord.ext import commands
-
-# constants
-SALARY_CAP_PL = 95.0
-SALARY_CAP_ML = 82.0
-SALARY_CAP_CL = 69.5
-SALARY_CAP_AL = 57.5
-SALARY_CAP_FL = 39.5
-
-
-class Franchise:
- """ Minor League E-Sports Discord Franchise
- This class houses all leagues associated with a franchise
- """
-
- def __init__(self,
- master_bot,
- guild: discord.Guild,
- franchise_name: str) -> None:
- """ Initialize method\n
- **param guild**: reference to guild this franchise belongs to\n
- **param team_name**: string representation of this team's name (e.g. **'Sabres'**)\n
- **param team_name**: asynchronous callback method for status updates\n
- All data is initialized to zero. Franchise load will be called 'on_ready' of the bot
- """
- self.bot = master_bot
- self.guild = guild
- self.franchise_name = franchise_name
- self.premier_league = Team(self.guild,
- self,
- LeagueEnum.Premier_League)
- self.master_league = Team(self.guild,
- self,
- LeagueEnum.Master_League)
- self.champion_league = Team(self.guild,
- self,
- LeagueEnum.Champion_League)
- self.academy_league = Team(self.guild,
- self,
- LeagueEnum.Academy_League)
- self.foundation_league = Team(self.guild,
- self,
- LeagueEnum.Foundation_League)
- self._sprocket_team: {} = None
- self._sprocket_members: [{}] = []
- self._sprocket_players: [{}] = []
-
- @property
- def all_members(self) -> [[],
- [],
- [],
- [],
- []]:
- """ return a list containing all lists of members from each team in the franchise
- """
- lst = []
- for _team in self.teams:
- lst.extend(_team.players)
- return lst
-
- @property
- def sprocket_team(self):
- return self._sprocket_team
-
- @property
- def sprocket_members(self):
- return self._sprocket_members
-
- @property
- def sprocket_players(self):
- return self._sprocket_players
-
- @property
- def teams(self) -> [Team]:
- lst = []
- if self.premier_league:
- lst.append(self.premier_league)
- if self.master_league:
- lst.append(self.master_league)
- if self.champion_league:
- lst.append(self.champion_league)
- if self.academy_league:
- lst.append(self.academy_league)
- if self.foundation_league:
- lst.append(self.foundation_league)
- return lst
-
- def build_sprocket_data(self):
- self._sprocket_team = next((x for x in self.bot.sprocket.data['sprocket_teams'] if self.franchise_name == x['name']), None)
- self._sprocket_players = [x for x in self.bot.sprocket.data['sprocket_players'] if x['franchise'] == self.franchise_name]
- self._sprocket_members = []
- for _player in self.sprocket_players:
- _mem = next(
- (x for x in self.bot.sprocket.data['sprocket_members'] if x['member_id'] == _player['member_id']), None)
- if _mem:
- self._sprocket_members.append(_mem)
-
- async def get_team_eligibility(self,
- team: LeagueEnum):
- if team == LeagueEnum.Premier_League and self.premier_league:
- _players = await self.premier_league.get_updated_players()
- elif team == LeagueEnum.Master_League and self.master_league:
- _players = await self.master_league.get_updated_players()
- elif team == LeagueEnum.Champion_League and self.champion_league:
- _players = await self.champion_league.get_updated_players()
- elif team == LeagueEnum.Academy_League and self.academy_league:
- _players = await self.academy_league.get_updated_players()
- elif team == LeagueEnum.Foundation_League and self.foundation_league:
- _players = await self.foundation_league.get_updated_players()
- else:
- return None
- return sorted(_players, key=lambda x: x.role)
-
- async def init(self):
- """ initialization method\n
- **`optional`param sprocket_delegate**: sprocket method delegate that we can append internally\n
- **`optional`param premier_channel**: channel to post quick info\n
- **`optional`param master_channel**: channel to post quick info\n
- **`optional`param champion_channel**: channel to post quick info\n
- **`optional`param academy_channel**: channel to post quick info\n
- **`optional`param foundation_channel**: channel to post quick info\n
- **returns**: status string of the init method\n
- """
- """ check if our method is in delegate, then add
- """
- """ assign datas locally
- """
- await self.rebuild()
-
- async def post_season_stats_html(self,
- league: str,
- ctx: discord.ext.commands.Context | discord.TextChannel | None = None):
- _league = next((x for x in self.teams if league in x.league_name.lower()), None)
- if not _league:
- await self.bot.send_notification(ctx,
- f'{league} was not a valid league name!',
- True)
- await _league.post_season_stats_html('Standard',
- ctx)
- await _league.post_season_stats_html('Doubles',
- ctx)
-
- async def rebuild(self) -> None:
- """ rebuild franchise
- """
- self.build_sprocket_data()
- self.premier_league = Team(self.guild, self, LeagueEnum.Premier_League)
- self.master_league = Team(self.guild, self, LeagueEnum.Master_League)
- self.champion_league = Team(self.guild, self, LeagueEnum.Champion_League)
- self.academy_league = Team(self.guild, self, LeagueEnum.Academy_League)
- self.foundation_league = Team(self.guild, self, LeagueEnum.Foundation_League)
- self.premier_league.build()
- self.master_league.build()
- self.champion_league.build()
- self.academy_league.build()
- self.foundation_league.build()
diff --git a/MLEBot/lo_commands.py b/MLEBot/lo_commands.py
deleted file mode 100644
index b55aae7..0000000
--- a/MLEBot/lo_commands.py
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/usr/bin/env python
-""" Minor League E-Sports Bot League Operation Specific Commands
-# Author: irox_rl
-# Purpose: General Functions and Commands
-# Version 1.0.6
-#
-# v1.0.6 - Include slash commands
-"""
-
-# local imports #
-
-# non-local imports #
-import discord
-from discord import app_commands
-from discord.ext import commands
-
-
-class LoCommands(commands.Cog):
- def __init__(self,
- master_bot):
- self.bot = master_bot
-
- @app_commands.command(name='achilles',
- description='mr. worldwide')
- @app_commands.default_permissions()
- async def achilles(self,
- interaction: discord.Interaction):
- await interaction.response.send_message("""https://media.discordapp.net/attachments/1101172529676161155/1257818646005415966/A_Chillis_tendon.png?ex=6685ca66&is=668478e6&hm=59e3c53b51b45477562b895acd641397a1144ebc5e690e6b4b875b9ca83c0ca0&=&format=webp&quality=lossless""")
-
- @app_commands.command(name='adi',
- description='Brick by boring brick.')
- @app_commands.default_permissions()
- async def adi(self,
- interaction: discord.Interaction):
- _bricks = [':brick:'] * 20
- await interaction.response.send_message(' '.join(_bricks))
-
- @app_commands.command(name='bw',
- description='Galaxy brain.')
- @app_commands.default_permissions()
- async def bw(self,
- interaction: discord.Interaction):
- await interaction.response.send_message('KISSEWDAKHTKISS')
-
- @app_commands.command(name='haim',
- description='S(HAIM).')
- @app_commands.default_permissions()
- async def haim(self,
- interaction: discord.Interaction):
- await interaction.response.send_message(
- 'https://media.discordapp.net/attachments/670665177863028750/940985245786837022/ezgif.com-gif-maker_1.gif?ex=667cd58d&is=667b840d&hm=0f7548f793f01ec70d4a45093083e3c13310754eb3b7d07a4b78cfd1085c9405&=')
-
- @app_commands.command(name='hoos',
- description=':kissing_heart:')
- @app_commands.default_permissions()
- async def hoos(self,
- interaction: discord.Interaction):
- await interaction.response.send_message(
- 'Friends share more DNA than strangers.')
-
- @app_commands.command(name='kd',
- description='KD')
- @app_commands.default_permissions()
- async def kd(self,
- interaction: discord.Interaction):
- await interaction.response.send_message(
- '` `')
-
- @app_commands.command(name='kunics',
- description='PIVOT')
- @app_commands.default_permissions()
- async def kunics(self,
- interaction: discord.Interaction):
- await interaction.response.send_message("""***P I V O T
-I V O T
-V O T
-O T
-T***""")
-
- @app_commands.command(name='maple',
- description='Oh, Canada...')
- @app_commands.default_permissions()
- async def maple(self,
- interaction: discord.Interaction):
- await interaction.response.send_message(':flag_ca: Sorry. :flag_ca:')
-
- @app_commands.command(name='ondo',
- description='ondofir')
- @app_commands.default_permissions()
- async def ondo(self,
- interaction: discord.Interaction):
- await interaction.response.send_message(
- 'https://media.discordapp.net/attachments/532946038080798730/709472405914910840/unknown.png?ex=667d06ea&is=667bb56a&hm=968a10b7a304b47c14a13b4333766419a3ae24f4a8c809910d241ee57f1689f0&=&format=webp&quality=lossless')
-
- @app_commands.command(name='rexton',
- description='opens door...')
- @app_commands.default_permissions()
- async def rexton(self,
- interaction: discord.Interaction):
- await interaction.response.send_message('https://media.discordapp.net/attachments/832819833611223081/996629449242058852/image_15_1.png?ex=667ce601&is=667b9481&hm=d3c4f0f7ae0074167a828db9b5b6deb8681a2c7bf1b93e428187327a1e3cea9e&=&format=webp&quality=lossless&width=1179&height=619')
-
- @app_commands.command(name='riz',
- description='@Riz')
- @app_commands.default_permissions()
- async def riz(self,
- interaction: discord.Interaction):
- await interaction.response.send_message('neck')
-
- @app_commands.command(name='soviet',
- description='Oh, the burning...')
- @app_commands.default_permissions()
- async def soviet(self,
- interaction: discord.Interaction):
- await interaction.response.send_message("""***The Crucifixion of Morality***
-
-*The Crucifixion of Morality is an image of 69 horseshoe crabmen and an alpaca. The alpaca is striking a menacing pose. The 69 horseshoe crabmen are burning. *
-https://cdn.mlesports.dev/public/The_Crucifixion_of_Morality.png""")
-
- @app_commands.command(name='zb',
- description='My link is borked, halp')
- @app_commands.default_permissions()
- async def zb(self,
- interaction: discord.Interaction):
- await interaction.response.send_message("https://tinyurl.com/rmblmv8")
diff --git a/MLEBot/member.py b/MLEBot/member.py
deleted file mode 100644
index de8d917..0000000
--- a/MLEBot/member.py
+++ /dev/null
@@ -1,178 +0,0 @@
-#!/usr/bin/env python
-""" Minor League E-Sports Member
-# Author: irox_rl
-# Purpose: General Functions of a League Member
-# Version 1.0.4
-"""
-
-# local imports #
-# from MLEBot.enums import LeagueEnum
-from enums import LeagueEnum
-import roles
-
-# non-local imports #
-import discord
-from discord.ext import commands
-
-
-class Member:
- """ Minor League E-Sports Member
- """
-
- def __init__(self,
- discord_member: discord.Member | None,
- league: LeagueEnum | None = None):
- """ Initiate member with reference to parent bot and discord.Member
- Other attributes are initialized to None or equivalent
- Members support saving and loading """
- self.discord_member: discord.Member = discord_member
- if not league:
- self.league: LeagueEnum | None = self.__get_league_role__(
- self.discord_member) if self.discord_member else None
- else:
- self.league: LeagueEnum = league
- self.mle_name: str | None = None
- self.mle_id = None
- self.mle_player_id = None
- self.member_id = None
- self.sprocket_id = None
- self.schedule_confirmed = False
- self.salary = None
- self.scrim_points = None
- self.eligible = False
- self.role = None
- self.dpi = 0.0
- self.gpi = 0.0
- self.opi = 0.0
- self.goals = 0.0
- self.saves = 0.0
- self.score = 0.0
- self.shots = 0.0
- self.assists = 0.0
- self.goals_against = 0.0
- self.shots_against = 0.0
-
- def __eq__(self,
- other):
- if self.discord_member and other.discord_member:
- if self.discord_member == other.discord_member:
- return True
- if self.mle_id and other.mle_id:
- if self.mle_id == other.mle_id:
- return True
- if self.sprocket_id and other.sprocket_id:
- if self.sprocket_id == other.sprocket_id:
- return True
- return False
-
- @staticmethod
- def __get_league_role__(member: discord.Member) -> LeagueEnum | None:
- """ Returns league enumeration if user has associated role
- else returns None """
- for role in member.roles:
- if role.name == roles.PREMIER_LEAGUE:
- return LeagueEnum.Premier_League
- if role.name == roles.MASTER_LEAGUE:
- return LeagueEnum.Master_League
- if role.name == roles.CHAMPION_LEAGUE:
- return LeagueEnum.Champion_League
- if role.name == roles.ACADEMY_LEAGUE:
- return LeagueEnum.Academy_League
- if role.name == roles.FOUNDATION_LEAGUE:
- return LeagueEnum.Foundation_League
-
- def __update_from_sprocket_players__(self,
- sprocket_players: {}) -> None:
- """ Update sprocket_id from sprocket_players.json data from sprocket database """
- if not sprocket_players:
- return
- player = next((x for x in sprocket_players if x['member_id'] == self.member_id), None)
- if not player:
- return
- self.sprocket_id = player['member_id']
- self.salary = player['salary']
- self.role = player['slot']
- self.scrim_points = player['current_scrim_points']
- self.eligible = True if self.scrim_points >= 30 else False
-
- def __update_from_sprocket_player_stats__(self,
- sprocket_player_stats):
- if not sprocket_player_stats:
- return
- player_stats = next((x for x in sprocket_player_stats if x['member_id'] == self.member_id), None)
- if not player_stats:
- return
- self.dpi = player_stats['dpi']
- self.gpi = player_stats['gpi']
- self.opi = player_stats['opi']
- self.goals = player_stats['goals']
- self.saves = player_stats['saves']
- self.score = player_stats['score']
- self.shots = player_stats['shots']
- self.assists = player_stats['assists']
- self.goals_against = player_stats['goals_against']
- self.shots_against = player_stats['shots_against']
-
- def __update_from_members__(self,
- sprocket_members: {}) -> None:
- if not sprocket_members:
- return
- member = next((x for x in sprocket_members if x['discord_id'] == self.discord_member.id.__str__()), None)
- if not member:
- return
- self.mle_name = member['name']
- self.member_id = member['member_id']
- self.mle_id = member['mle_id']
- self.mle_player_id = member['mle_player_id']
-
- async def post_quick_info(self, ctx: discord.ext.commands.Context):
- embed = discord.Embed(color=discord.Color.dark_red(), title=f'**{self.mle_name} Quick Info**',
- description='Quick info gathered by sprocket public datasets.\n')
- embed.add_field(name='`Name`', value=self.discord_member.name, inline=True)
- embed.add_field(name='`Display Name`', value=self.discord_member.mention, inline=True)
- embed.add_field(name='`MLE Name`', value=self.mle_name, inline=True)
- embed.add_field(name='`MLE ID`', value=self.mle_id, inline=True)
- embed.add_field(name='`Sprocket ID`', value=self.sprocket_id, inline=True)
- embed.add_field(name='`Salary`', value=self.salary, inline=True)
- embed.add_field(name='Scrim Points', value=self.scrim_points, inline=True)
- embed.add_field(name='Eligible?', value=self.eligible, inline=True)
- embed.add_field(name='Role', value=self.role, inline=True)
- embed.add_field(name='dpi', value=self.dpi,
- inline=True) # This is disabled until sprocket gives out better data(?)
- embed.add_field(name='opi', value=self.opi, inline=True)
- embed.add_field(name='goals', value=self.goals, inline=True)
- embed.add_field(name='saves', value=self.saves, inline=True)
- embed.add_field(name='score', value=self.score, inline=True)
- embed.add_field(name='shots', value=self.shots, inline=True)
- embed.add_field(name='assists', value=self.assists, inline=True)
- embed.add_field(name='goals_against', value=self.goals_against, inline=True)
- embed.add_field(name='shots_against', value=self.shots_against, inline=True)
- await ctx.send(embed=embed)
-
- def update(self, sprocket_data: {}):
- if not sprocket_data:
- return
- self.__update_from_members__(sprocket_data['sprocket_members'])
- self.__update_from_sprocket_players__(sprocket_data['sprocket_players'])
- self.__update_from_sprocket_player_stats__(sprocket_data['sprocket_player_stats'])
- return self
-
-
-def get_member_by_id(guild: discord.Guild, member_id: int) -> discord.Member | None:
- return next((x for x in guild.members if x.id == member_id), None)
-
-
-def get_member_by_name(guild: discord.Guild, member_name: str):
- return next((x for x in guild.members if x.name.lower() == member_name.lower()), None)
-
-
-def get_members_by_role_name(guild: discord.Guild, role: str):
- return [x for x in guild.members for y in x.roles if y.name == role]
-
-
-def get_members_by_role(guild: discord.Guild, role: discord.Role):
- return [member for member in guild.members if role in member.roles]
-
-
-def has_role(member: discord.Member, roles: [discord.Role]) -> bool:
- return next((True for role in roles if role in member.roles), False)
diff --git a/MLEBot/mle_bot.py b/MLEBot/mle_bot.py
deleted file mode 100644
index 66afd84..0000000
--- a/MLEBot/mle_bot.py
+++ /dev/null
@@ -1,123 +0,0 @@
-""" Minor League E-Sports Bot
-# Author: irox_rl
-# Purpose: General Functions of a League Franchise summarized in bot fashion!
-# Version 1.1.0
-#
-# v1.0.6 - server integration
-"""
-
-from PyDiscoBot import Bot
-from PyDiscoBot.commands import Commands
-
-# local imports #
-from lo_commands import LoCommands
-from mle_commands import MLECommands
-import roles
-from task_roster import Task_Roster
-from task_sprocket import Task_Sprocket
-
-# non-local imports #
-import discord
-from discord.ext import commands as disco_commands
-import dotenv
-import os
-
-
-class MLEBot(Bot):
- mle_logo_url = "https://images-ext-1.discordapp.net/external/8g0PflayqZyQZe8dqTD_wYGPcCpucRWAsnIjV4amZhc/https/i.imgur.com/6anH1sI.png?format=webp&quality=lossless&width=619&height=619"
-
- def __init__(self,
- command_prefix: str | None,
- bot_intents: discord.Intents | None,
- command_cogs: [disco_commands.Cog]):
- super().__init__(command_prefix=command_prefix,
- bot_intents=bot_intents,
- command_cogs=command_cogs)
- self._sprocket = Task_Sprocket(self)
- self._roster = Task_Roster(self)
- self._guild_ids: [{}] = []
-
- @property
- def guild_ids(self):
- return self._guild_ids
-
- @property
- def roster(self) -> Task_Roster:
- return self._roster
-
- @property
- def sprocket(self) -> Task_Sprocket | None:
- return self._sprocket
-
- async def build_guilds(self):
- self._guild_ids.clear()
- dotenv.load_dotenv()
- for team in roles.ALL_TEAMS:
- try:
- _id = os.getenv(team).upper()
- self._guild_ids.append({'team': team,
- 'id': _id})
- except AttributeError:
- continue
-
- async def get_help_cmds_by_user(self,
- ctx: discord.ext.commands.Context) -> [disco_commands.command]:
- user_cmds = [cmd for cmd in self.commands]
- for cmd in self.commands:
- for check in cmd.checks:
- try:
- can_run = await check(ctx)
- if not can_run:
- user_cmds.remove(cmd)
- break
- except disco_commands.CheckFailure:
- user_cmds.remove(cmd)
- break
- except AttributeError: # in this instance, a predicate could not be checked. It does not exist (part of the base bot)
- break
- return user_cmds
-
- async def rebuild(self):
- await self.build_guilds()
- return True
-
- async def on_ready(self,
- suppress_task=False) -> None:
- """ Method that is called by discord when the bot connects to the supplied guild\n
- **param suppress_task**: if True, do NOT run periodic task at the end of this call\n
- **returns**: None\n
- """
- """ do not initialize more than once
- """
- if self._initialized:
- await self.change_presence(
- activity=discord.Activity(type=discord.ActivityType.listening, name='sweet EMILIO. ðŸª'))
- return
- await super().on_ready(suppress_task)
-
- await self.sprocket.run()
- await self.build_guilds()
-
- await self.change_presence(
- activity=discord.Activity(type=discord.ActivityType.listening, name='sweet EMILIO. ðŸª'))
-
- async def on_task(self) -> None:
- await super().on_task()
- await self._sprocket.run()
-
-
-if __name__ == '__main__':
- dotenv.load_dotenv()
- intents = discord.Intents(8)
- # noinspection PyDunderSlots
- intents.guilds = True
- # noinspection PyDunderSlots
- intents.members = True
- # noinspection PyDunderSlots
- intents.message_content = True
- # noinspection PyDunderSlots
- intents.messages = True
- bot = MLEBot(['ub.', 'Ub.', 'UB.'],
- intents,
- [Commands, MLECommands, LoCommands])
- bot.run(os.getenv('DISCORD_TOKEN'))
diff --git a/MLEBot/mle_commands.py b/MLEBot/mle_commands.py
deleted file mode 100644
index a7e98a7..0000000
--- a/MLEBot/mle_commands.py
+++ /dev/null
@@ -1,606 +0,0 @@
-#!/usr/bin/env python
-""" Minor League E-Sports Bot Commands
-# Author: irox_rl
-# Purpose: General Functions and Commands
-# Version 1.0.6
-#
-# v1.0.6 - Include slash commands
-"""
-from PyDiscoBot import channels
-from PyDiscoBot import Pagination, InteractionPagination
-from PyDiscoBot import ReportableError
-
-# local imports #
-from enums import *
-import team
-from team import get_league_text
-from franchise import SALARY_CAP_PL, SALARY_CAP_ML, SALARY_CAP_CL, SALARY_CAP_AL, SALARY_CAP_FL
-
-# non-local imports #
-import difflib
-import discord
-from discord import app_commands
-from discord.ext import commands
-import os
-import dotenv
-
-
-class MLECommands(commands.Cog):
- def __init__(self,
- master_bot):
- self.bot = master_bot
-
- async def __local_lookup__(self,
- interaction: discord.Interaction,
- mle_name: str = None):
- data = self.bot.sprocket.data # easier to write, shorter code
-
- # Find player in Sprocket Members dataset
- if mle_name:
- _member = next((x for x in data['sprocket_members'] if x['name'] == mle_name), None)
- if not _member:
- matches = difflib.get_close_matches(mle_name, [x['name'] for x in data['sprocket_members']],
- 1)
- if matches:
- await self.bot.send_notification(interaction,
- f"Could not find `{mle_name}` in sprocket `Members` dataset. Did you mean `{matches[0]}`?")
- return
- else:
- _member = next(
- (x for x in data['sprocket_members'] if x['discord_id'].__str__() == interaction.user.id.__str__()),
- None)
- if not _member:
- await self.bot.send_notification(interaction,
- 'mle member not found in sprocket `Members` dataset')
- return
-
- # Find player in Sprocket Players dataset
- _player = next((x for x in data['sprocket_players'] if x['member_id'] == _member['member_id']), None)
- if not _player:
- await self.bot.send_notification(interaction,
- 'mle member not found in sprocket `Players` dataset')
- return
-
- # more data
- _team = next((x for x in data['sprocket_teams'] if x['name'] == _player['franchise']), None)
- tracker_player = next((x for x in data['sprocket_trackers'] if x['mleid'] == _member['mle_id']), None)
-
- # embed
- embed = (discord.Embed(
- color=discord.Color.from_str(_team['primary_color']) if _team else self.bot.default_embed_color,
- title=f"**{_member['name']} Sprocket Info**",
- description='Data gathered by sprocket public data links.\n'
- 'See more at [sprocket links](https://f004.backblazeb2.com/file/sprocket-artifacts/public/pages/index.html)\n')
- .set_footer(text=f'Generated: {self.bot.last_time}'))
- embed.set_thumbnail(url=self.bot.mle_logo_url if not _team else _team['logo_img_link'])
- embed.add_field(name='MLE Name', value=f"`{_member['name']}`", inline=True)
- embed.add_field(name='MLE ID', value=f"`{_member['mle_id']}`", inline=True)
- embed.add_field(name='Salary', value=f"`{_player['salary']}`", inline=True)
- embed.add_field(name='League', value=f"`{_player['skill_group']}`", inline=True)
- embed.add_field(name='Scrim Points', value=f"`{_player['current_scrim_points']}`", inline=True)
- embed.add_field(name='Eligible?', value="`Yes`" if _player['current_scrim_points'] >= 30 else "`No`",
- inline=True)
- embed.add_field(name='Franchise', value=f"`{_player['franchise']}`", inline=True)
- embed.add_field(name='Staff Position', value=f"`{_player['Franchise Staff Position']}`", inline=True)
- embed.add_field(name='Role', value=f"`{_player['slot']}`", inline=True)
- if tracker_player:
- embed.add_field(name='**Tracker Link**', value=tracker_player['tracker'], inline=False)
- await interaction.response.send_message(embed=embed)
-
- async def __resolve_franchise__(self,
- interaction: discord.Interaction):
- _known_guild = next((x for x in self.bot.guild_ids if str(x['id']) == interaction.guild.id.__str__()), None)
- if not _known_guild:
- return None
- return next(
- (x for x in self.bot.sprocket.data['sprocket_teams'] if x['name'].upper() == _known_guild['team'].upper()),
- None)
-
- @staticmethod
- def get_sprocket_player_team_info(sprocket_player) -> str | None:
- return f"`{sprocket_player['slot'].removeprefix('PLAYER')} | {sprocket_player['salary']} | {sprocket_player['name']}`"
-
- def get_sprocket_usage_team_info(self,
- sprocket_player) -> str | None:
- role_usage = next((x for x in self.bot.sprocket.data['role_usages'] if
- (x['role'] == sprocket_player['slot']) and x['team_name'] == sprocket_player['franchise'] and
- x['league'].lower() in sprocket_player['skill_group'].lower()),
- None)
- if not role_usage:
- return None
- return f"`{sprocket_player['slot'].removeprefix('PLAYER')} | 2s: {role_usage['doubles_uses']} | 3s: {role_usage['standard_uses']} | Total: {role_usage['total_uses']} | {sprocket_player['name']}`"
-
- @app_commands.command(name='clearchannel',
- description='Clear channel messages. Include amt of messages to delete.\n Max is 100. (e.g. ub.clearchannel 55)')
- @app_commands.default_permissions()
- async def clearchannel(self,
- interaction: discord.Interaction,
- message_count: int):
- await interaction.response.defer()
- await channels.clear_channel_messages(interaction.channel, message_count)
-
- @app_commands.command(name='lookup',
- description='Lookup player by MLE name provided.\nTo lookup yourself, just type {ub.lookup}')
- @app_commands.describe(mle_name='MLE Name of player to look up.')
- @app_commands.default_permissions()
- async def lookup(self,
- interaction: discord.Interaction,
- mle_name: str):
- await self.__local_lookup__(interaction,
- mle_name)
-
- @app_commands.command(name='rebuild',
- description='Rebuild bot meta data.')
- @app_commands.guilds(1043295434828947547)
- @app_commands.default_permissions()
- async def rebuild(self,
- interaction: discord.Interaction):
- await interaction.response.defer()
- if await self.bot.rebuild():
- await self.bot.send_notification(interaction,
- 'Success!.',
- as_followup=True)
- else:
- await self.bot.send_notification(interaction,
- 'An error has occured.',
- as_followup=True)
-
- @app_commands.command(name='query',
- description='Lookup groups of players by provided filter.')
- @app_commands.describe(league_filter='[PL, ML, CL, AL, FL]')
- @app_commands.describe(query_filter='[FA, RFA, Waivers, Pend]')
- @app_commands.describe(sorting='[salary, current_scrim_points, name]')
- @app_commands.choices(league_filter=[
- app_commands.Choice(name='PL', value='pl'),
- app_commands.Choice(name='ML', value='ml'),
- app_commands.Choice(name='CL', value='cl'),
- app_commands.Choice(name='AL', value='al'),
- app_commands.Choice(name='FL', value='fl')
- ],
- query_filter=[
- app_commands.Choice(name='FA', value='fa'),
- app_commands.Choice(name='RFA', value='rfa'),
- app_commands.Choice(name='Waivers', value='waivers'),
- app_commands.Choice(name='Pend', value='pend')
- ],
- sorting=[
- app_commands.Choice(name='Salary', value='salary'),
- app_commands.Choice(name='Scrim Points', value='current_scrim_points'),
- app_commands.Choice(name='Name', value='name')
- ])
- @app_commands.default_permissions()
- async def query(self,
- interaction: discord.Interaction,
- league_filter: app_commands.Choice[str],
- query_filter: str,
- sorting: str):
- _league_enum = get_league_enum_by_short_text(league_filter.value)
- if not _league_enum:
- return await self.bot.send_notification(interaction,
- 'League not found. Please enter a valid league.)',
- True)
-
- valid_queries = ['fa', 'waivers', 'rfa', 'pend']
- valid_sorts = ['salary', 'current_scrim_points', 'name']
- if query_filter.lower() not in valid_queries:
- return await self.bot.send_notification(interaction,
- f'`{query_filter}` is an invalid query. Please try again.',
- True)
-
- if sorting.lower() not in valid_sorts:
- return await self.bot.send_notification(interaction,
- f'`{sorting}` is an invalid sorting type. Please try again.')
-
- data = self.bot.sprocket.data
- _players = sorted([x for x in data['sprocket_players'] if
- x['franchise'].lower() == query_filter.lower() and x[
- 'skill_group'] == _league_enum.name.replace(
- '_', ' ')], key=lambda x: x[sorting])
-
- if len(_players) == 0:
- emb: discord.Embed = self.bot.default_embed(f'**Filtered Players**\n\n',
- f'There were no players to be found for this query!')
- emb.set_thumbnail(url=self.bot.mle_logo_url)
- await interaction.followup.send(embed=emb)
- return
-
- async def get_page(page: int,
- as_timout: bool = False):
- emb: discord.Embed = self.bot.default_embed(f'**Filtered Players**\n\n',
- f'Players filtered for `{query_filter}`\n'
- f'Sorted by `{sorting}`')
- emb.set_thumbnail(url=self.bot.mle_logo_url)
- if as_timout:
- emb.add_field(name=f'**`Timeout`**',
- value='This command has timed out. Type `[ub.help]` for help.')
- emb.set_footer(text=f'Page 1 of 1')
- return emb, 0
-
- elements_per_page = 15
- offset = (page - 1) * elements_per_page
- emb.add_field(name=f'**Sal | Points | Name**',
- value='\n'.join(
- [
- f"`{_p['salary'].__str__().ljust(4)} | {_p['current_scrim_points'].__str__().ljust(4)} | {_p['name']}`"
- for _p in _players[offset:offset + elements_per_page]]),
- inline=False)
- total_pages = Pagination.compute_total_pages(len(_players),
- elements_per_page)
-
- emb.set_footer(text=f'Page {page} of {total_pages}')
- return emb, total_pages
-
- await InteractionPagination(interaction, get_page).navigate()
-
- @app_commands.command(name='regrosterchannel',
- description='Register Franchise Roster Channel for roster posting functionality.')
- @app_commands.default_permissions()
- async def regrosterchannel(self,
- interaction: discord.Interaction):
- _team = await self.__resolve_franchise__(interaction)
- if not _team:
- await self.bot.send_notification(interaction,
- 'Could not resolve this franchise from sprocket data!',
- as_followup=False)
- return
- try:
- dotenv.set_key(dotenv.find_dotenv(),
- f"{_team['name'].upper()}_ROSTER",
- str(interaction.channel_id),
- quote_mode='never')
- # os.environ[f"{_team['name'].upper()}_ROSTER"] = str(interaction.channel_id)
- await self.bot.send_notification(interaction,
- 'Success!',
- as_followup=False)
- except KeyError:
- await self.bot.send_notification(interaction,
- 'An error has occurred!',
- as_followup=False)
-
- @app_commands.command(name='runroster',
- description='Run a refresh of the roster channel.')
- @app_commands.default_permissions()
- async def runroster(self,
- interaction: discord.Interaction):
- await interaction.response.defer()
- _team = await self.__resolve_franchise__(interaction)
- if not _team:
- await self.bot.send_notification(interaction,
- 'Could not resolve this franchise from sprocket data!',
- as_followup=True)
- return
-
- dotenv.load_dotenv()
- _channel_id = os.getenv(f"{_team['name'].upper()}_ROSTER")
- if not _channel_id:
- await self.bot.send_notification(interaction,
- 'Roster channel has not been configured! Run /regrosterchannel!',
- as_followup=True)
- return
-
- _channel = next((x for x in interaction.guild.channels if x.id.__str__() == str(_channel_id)), None)
- if not _channel:
- await self.bot.send_notification(interaction,
- 'Roster channel has not been found! Run /regrosterchannel!',
- as_followup=True)
- return
-
- if await self.bot.roster.post_roster(_team,
- _channel):
- await self.bot.send_notification(interaction,
- 'Success!',
- as_followup=True)
- else:
- await self.bot.send_notification(interaction,
- 'Failure!',
- as_followup=True)
-
- @app_commands.command(name='salary',
- description='Get salary and extra data about yourself from provided sprocket data.')
- @app_commands.default_permissions()
- async def salary(self,
- interaction: discord.Interaction):
- await self.__local_lookup__(interaction)
-
- @commands.command(name='seasonstats',
- description='Beta - Get season stats for a specific league.\n\tInclude league name. (e.g. ub.seasonstats master).\n\tNaming convention will be updated soon - Beta')
- @app_commands.default_permissions()
- async def seasonstats(self,
- ctx: discord.ext.commands.Context,
- league: str):
- return
- if not league:
- return await self.bot.send_notification(ctx, 'You must specify a league when running this command.\n'
- 'i.e.: ub.seasonstats master', True)
-
- await self.bot.franchise.post_season_stats_html(league.lower(),
- ctx)
-
- @app_commands.command(name='showusage',
- description='Show game usage stats for all league members of this franchise.')
- @app_commands.default_permissions()
- async def showusage(self,
- interaction: discord.Interaction):
- await interaction.response.defer()
- _team = await self.__resolve_franchise__(interaction)
- if not _team:
- await self.bot.send_notification(interaction,
- 'Could not resolve this franchise from sprocket data!',
- as_followup=True)
- return
-
- _players = [x for x in self.bot.sprocket.data['sprocket_players'] if x['franchise'] == _team['name']]
- if not _players:
- await self.bot.send_notification(interaction,
- 'Could not get players for this franchise from sprocket!',
- as_followup=True)
- return
-
- _pl_players = [x for x in _players if x['skill_group'] == 'Premier League' and x['slot'] != 'NONE']
- _ml_players = [x for x in _players if x['skill_group'] == 'Master League' and x['slot'] != 'NONE']
- _cl_players = [x for x in _players if x['skill_group'] == 'Champion League' and x['slot'] != 'NONE']
- _al_players = [x for x in _players if x['skill_group'] == 'Academy League' and x['slot'] != 'NONE']
- _fl_players = [x for x in _players if x['skill_group'] == 'Foundation League' and x['slot'] != 'NONE']
-
- embed = (discord.Embed(
- color=discord.Color.from_str(_team['primary_color']),
- title=f"**{_team['name']} Slot Usage Info**",
- description='Data gathered by sprocket public data links.\n'
- 'See more at [sprocket links](https://f004.backblazeb2.com/file/sprocket-artifacts/public/pages/index.html)\n')
- .set_footer(text=f'Generated: {self.bot.last_time}'))
- embed.set_thumbnail(url=_team['logo_img_link'])
-
- embed.add_field(name='**Season Slot Allowances**',
- value='`Doubles: 6 | Standard: 8 | Total: 12` ',
- inline=False)
-
- self.__show_usage_league__(sorted(_pl_players, key=lambda _p: _p['slot']),
- embed,
- 'Premier',
- SALARY_CAP_PL)
-
- self.__show_usage_league__(sorted(_ml_players, key=lambda _p: _p['slot']),
- embed,
- 'Master',
- SALARY_CAP_ML)
-
- self.__show_usage_league__(sorted(_cl_players, key=lambda _p: _p['slot']),
- embed,
- 'Champion',
- SALARY_CAP_CL)
-
- self.__show_usage_league__(sorted(_al_players, key=lambda _p: _p['slot']),
- embed,
- 'Academy',
- SALARY_CAP_AL)
-
- self.__show_usage_league__(sorted(_fl_players, key=lambda _p: _p['slot']),
- embed,
- 'Foundation',
- SALARY_CAP_FL)
-
- await interaction.followup.send(embed=embed)
-
- @app_commands.command(name='teameligibility',
- description='Show team eligibility. Include league after command.\n\t(e.g. ub.teameligibility fl)')
- @app_commands.describe(league='[PL, ML, CL, AL, FL]')
- @app_commands.choices(league=[
- app_commands.Choice(name='PL', value='pl'),
- app_commands.Choice(name='ML', value='ml'),
- app_commands.Choice(name='CL', value='cl'),
- app_commands.Choice(name='AL', value='al'),
- app_commands.Choice(name='FL', value='fl')
- ])
- @app_commands.default_permissions()
- async def teameligibility(self,
- interaction: discord.Interaction,
- league: str):
- if not league:
- await self.bot.send_notification(interaction,
- 'You must specify a league when running this command!')
- return
- _league_enum = get_league_enum_by_short_text(league)
- if not _league_enum:
- await self.bot.send_notification(interaction,
- 'League not found. Please enter a valid league.')
- return
- _league_text = get_league_text(_league_enum)
-
- _team = await self.__resolve_franchise__(interaction)
- if not _team:
- await self.bot.send_notification(interaction,
- 'Could not resolve this franchise from sprocket data!',
- as_followup=True)
- return
-
- _players = [x for x in self.bot.sprocket.data['sprocket_players'] if x['franchise'] == _team['name']]
- if not _players:
- await self.bot.send_notification(interaction,
- 'Could not get players for this franchise from sprocket!',
- as_followup=True)
- return
-
- _league_players = sorted([x for x in _players if x['skill_group'] == _league_text and x['slot'] != 'NONE'],
- key=lambda x: x['slot'])
- if not _league_players:
- await self.bot.send_notification(interaction,
- 'An error has occurred.')
- return
-
- await interaction.response.defer()
- embed = self.bot.default_embed(
- f"{_league_text} {_team['name']} Eligibility Information",
- color=discord.Color.from_str(_team['primary_color']))
- embed.set_thumbnail(url=_team['logo_img_link'])
-
- ljust_limit = 8
-
- for _p in _league_players:
- embed.add_field(name=f"**{_p['name']}**",
- value=f"`{'Role:'.ljust(ljust_limit)}` {_p['slot']}\n"
- f"`{'Salary:'.ljust(ljust_limit)}` {_p['salary']}\n"
- f"`{'Points:'.ljust(ljust_limit)}` {_p['current_scrim_points'].__str__()}\n"
- f"`{'Until:'.ljust(ljust_limit)}` ~TBD~",
- inline=True)
-
- await interaction.followup.send(embed=embed)
-
- @app_commands.command(name='teaminfo',
- description='Get information about a team from the league!\n\tInclude team after command. (e.g. ub.teaminfo sabres)')
- @app_commands.describe(_team_name='MLE Team to get info about.')
- @app_commands.default_permissions()
- async def teaminfo(self,
- interaction: discord.Interaction,
- _team_name: str):
-
- if not _team_name:
- raise ReportableError('You must provide a team when running this command!')
-
- _team = next((x for x in self.bot.sprocket.data['sprocket_teams'] if x['name'].lower() == _team_name.lower()),
- None)
- if not _team:
- raise ReportableError(f'Could not find team `{_team_name}` in sprocket data base!\nPlease try again!')
-
- embed = team.get_mle_franchise_embed(_team)
-
- await interaction.response.defer()
- _team_players = team.get_defined_team_players(_team,
- self.bot.sprocket.data)
- if not _team_players:
- raise ReportableError('Could not find players for franchise!')
-
- embed.add_field(name='**Franchise Manager**',
- value=f"`{_team_players['fm']['name']}`" if _team_players['fm'] else "",
- inline=False)
-
- embed.add_field(name='**General Manager**',
- value=f"\n".join([f"`{gm['name']}`" for gm in _team_players['gms']]),
- inline=False)
-
- if len(_team_players['agms']) != 0:
- embed.add_field(name='**Assistant General Managers**',
- value=f"\n".join([f"`{agm['name']}`" for agm in _team_players['agms']]),
- inline=False)
-
- if len(_team_players['captains']) != 0:
- embed.add_field(name='**Captains**',
- value=f"\n".join([f"`{x['name']}`" for x in _team_players['captains']]),
- inline=False)
-
- if len(_team_players['pr_supports']) != 0:
- embed.add_field(name='**PR Supports**',
- value=f"\n".join([f"`{x['name']}`" for x in _team_players['pr_supports']]),
- inline=False)
-
- embed.add_field(name='**Roster**', value='**`[Top5/SalCap] [CanSign] League`**', inline=False)
-
- self.__team_info_league__(sorted(_team_players['pl_players'], key=lambda _p: _p['slot']),
- embed,
- 'Premier',
- SALARY_CAP_PL)
-
- self.__team_info_league__(sorted(_team_players['ml_players'], key=lambda _p: _p['slot']),
- embed,
- 'Master',
- SALARY_CAP_ML)
-
- self.__team_info_league__(sorted(_team_players['cl_players'], key=lambda _p: _p['slot']),
- embed,
- 'Champion',
- SALARY_CAP_CL)
-
- self.__team_info_league__(sorted(_team_players['al_players'], key=lambda _p: _p['slot']),
- embed,
- 'Academy',
- SALARY_CAP_AL)
-
- self.__team_info_league__(sorted(_team_players['fl_players'], key=lambda _p: _p['slot']),
- embed,
- 'Foundation',
- SALARY_CAP_FL)
-
- await interaction.followup.send(embed=embed)
-
- @app_commands.command(name='updatesprocket',
- description='Update internal information by probing sprocket for new data.')
- @app_commands.default_permissions()
- async def updatesprocket(self,
- interaction: discord.Interaction):
- await interaction.response.defer()
- self.bot.sprocket.reset()
- await self.bot.sprocket.run()
- await self.bot.send_notification(interaction,
- 'League-Sprocket update complete.',
- as_followup=True)
-
- def __team_info_league__(self,
- players: [],
- embed: discord.Embed,
- league_name: str,
- salary_cap: float):
- if not players:
- return
-
- players_strings = '\n'.join(
- [self.get_sprocket_player_team_info(player) for player in players if player is not None])
-
- embed.add_field(
- name=get_team_salary_string(players,
- league_name,
- salary_cap),
- value=players_strings,
- inline=False)
-
- def __show_usage_league__(self,
- players: [],
- embed: discord.Embed,
- league_name: str,
- salary_cap: float):
- if not players:
- return
-
- players_strings = '\n'.join(
- [self.get_sprocket_usage_team_info(player) for player in players if player is not None])
-
- embed.add_field(
- name=league_name,
- value=players_strings,
- inline=False)
-
-
-def get_players_salary_ceiling(top_sals: [{}]) -> float:
- sal_ceiling = 0.0
- range_length = 5 if len(top_sals) >= 5 else len(top_sals)
- for i in range(range_length):
- sal_ceiling += top_sals[i]['salary']
- return sal_ceiling
-
-
-def get_team_salary_string(players: [],
- league_name: str,
- salary_cap: float) -> str:
- top_sals = sorted(players,
- key=lambda p: p['salary'],
- reverse=True)
- sal_ceiling = get_players_salary_ceiling(top_sals)
- _sign = '+' if sal_ceiling >= salary_cap else '-'
- _signable_str = f"+{top_sals[-1]['salary'] + (salary_cap - sal_ceiling)}" if sal_ceiling <= salary_cap else "NONE"
- return f'**`[{sal_ceiling} / {salary_cap}] [{_signable_str}]` {league_name}**'
-
-
-def get_league_enum_by_short_text(league: str):
- if not league:
- return None
- if league.lower() == 'pl':
- _league_enum = LeagueEnum.Premier_League
- elif league.lower() == 'ml':
- _league_enum = LeagueEnum.Master_League
- elif league.lower() == 'cl':
- _league_enum = LeagueEnum.Champion_League
- elif league.lower() == 'al':
- _league_enum = LeagueEnum.Academy_League
- elif league.lower() == 'fl':
- _league_enum = LeagueEnum.Foundation_League
- else:
- _league_enum = None
- return _league_enum
diff --git a/MLEBot/mlebot.py b/MLEBot/mlebot.py
new file mode 100644
index 0000000..a659a27
--- /dev/null
+++ b/MLEBot/mlebot.py
@@ -0,0 +1,90 @@
+"""Minor League E-Sports PyDiscoBot implimentation
+ """
+from __future__ import annotations
+
+import os
+import discord
+from discord.ext import commands as disco_commands
+
+
+from pydiscobot import Bot
+
+
+from .commands import Commands
+from . import const
+from .tasks.sprocket import Sprocket
+
+
+class MLEBot(Bot):
+ """minor league esports bot
+
+ Args:
+ Bot (_type_): parent bot
+ """
+
+ def __init__(self,
+ command_prefix: str | None,
+ bot_intents: discord.Intents | None,
+ command_cogs: list[disco_commands.Cog]):
+ command_cogs.extend(Commands)
+ super().__init__(command_prefix=command_prefix,
+ bot_intents=bot_intents,
+ command_cogs=command_cogs)
+ self._sprocket = Sprocket(self)
+ self._tasker.append(self._sprocket)
+ self._guild_ids: list[dict] = []
+
+ @property
+ def activity(self) -> discord.Activity:
+ return discord.Activity(type=discord.ActivityType.listening,
+ name='hot farts being generated in the atmosphere by alien lizard men.')
+
+ @property
+ def guild_ids(self) -> list[dict]:
+ """all tracked MLE guilds
+
+ Returns:
+ list[dict]: all tracked MLE guilds
+ """
+ return self._guild_ids
+
+ @property
+ def sprocket(self) -> Sprocket | None:
+ """get sprocket task
+
+ Returns:
+ Sprocket | None: Sprocket task or None
+ """
+ return self._sprocket
+
+ async def _build_guilds(self):
+ self._guild_ids.clear()
+ for team in const.ALL_TEAMS:
+ try:
+ _id = os.getenv(team).upper()
+ self._guild_ids.append({'team': team,
+ 'id': _id})
+ except AttributeError:
+ continue
+
+ async def rebuild(self):
+ """rebuild list of known MLE guilds
+ """
+ await self._build_guilds()
+
+ async def on_ready(self,
+ suppress_task=False) -> None:
+ """on bot ready
+
+ Args:
+ suppress_task (bool, optional): don't run periodic task. Defaults to False.
+ """
+ if self.status.initialized:
+ self.logger.warning('already initialized!')
+ return
+ await super().on_ready(suppress_task)
+ await self._build_guilds()
+
+ if self.ws: # if we are connected to a web-socket
+ # otherwise, this will raise...
+ await self.change_presence(activity=self.activity)
diff --git a/MLEBot/roles.py b/MLEBot/roles.py
deleted file mode 100644
index ce77c42..0000000
--- a/MLEBot/roles.py
+++ /dev/null
@@ -1,277 +0,0 @@
-#!/usr/bin/env python
-""" Discord Roles Module for use in Minor League E-Sports
-# Author: irox_rl
-# Purpose: General Functions of Discord Roles
-# Version 1.0.2
-"""
-
-# local imports #
-from enums import LeagueEnum
-
-# non-local imports #
-import copy
-import discord
-import os
-
-""" Constants
-"""
-Aviators = "Aviators"
-Bears = "Bears"
-Blizzard = "Blizzard"
-Bulls = "Bulls"
-Comets = "Comets"
-Demolition = "Demolition"
-Dodgers = "Dodgers"
-Ducks = "Ducks"
-Eclipse = "Eclipse"
-Elite = "Elite"
-Express = "Express"
-Flames = "Flames"
-Foxes = "Foxes"
-Hawks = "Hawks"
-Hive = "Hive"
-Hurricanes = "Hurricanes"
-Jets = "Jets"
-Knights = "Knights"
-Lightning = "Lightning"
-Outlaws = "Outlaws"
-Pandas = "Pandas"
-Pirates = "Pirates"
-Puffins = "Puffins"
-Rhinos = "Rhinos"
-Sabres = "Sabres"
-Shadow = "Shadow"
-Sharks = "Sharks"
-Spartans = "Spartans"
-Spectre = "Spectre"
-Tyrants = "Tyrants"
-Waivers = "Waivers"
-Wizards = "Wizards"
-Wolves = "Wolves"
-SOCIAL_MEDIA = 'Social Media'
-
-# dotenv.load_dotenv('.env')
-FRANCHISE_MANAGER = None
-GENERAL_MANAGER_RL = None
-GENERAL_MANAGER_TM = None
-ASSISTANT_GENERAL_MANAGER_RL = None
-ASSISTANT_GENERAL_MANAGER_TM = None
-CAPTAIN = None
-PREMIER_LEAGUE = None
-MASTER_LEAGUE = None
-CHAMPION_LEAGUE = None
-ACADEMY_LEAGUE = None
-FOUNDATION_LEAGUE = None
-ROCKET_LEAGUE = None
-PR_SUPPORT = None
-FA = None
-FP = None
-Pend = None
-
-ALL_MLE_ROLES = [
- Aviators,
- Bears,
- Blizzard,
- Bulls,
- Comets,
- Demolition,
- Dodgers,
- Ducks,
- Eclipse,
- Elite,
- Express,
- Flames,
- Foxes,
- Hawks,
- Hive,
- Hurricanes,
- Jets,
- Knights,
- Lightning,
- Outlaws,
- Pandas,
- Pirates,
- Puffins,
- Rhinos,
- Sabres,
- Shadow,
- Sharks,
- Spartans,
- Spectre,
- Tyrants,
- Waivers,
- Wizards,
- Wolves,
- FRANCHISE_MANAGER,
- GENERAL_MANAGER_RL,
- GENERAL_MANAGER_TM,
- ASSISTANT_GENERAL_MANAGER_RL,
- ASSISTANT_GENERAL_MANAGER_TM,
- CAPTAIN,
- PREMIER_LEAGUE,
- MASTER_LEAGUE,
- CHAMPION_LEAGUE,
- ACADEMY_LEAGUE,
- FOUNDATION_LEAGUE,
- ROCKET_LEAGUE,
- PR_SUPPORT,
- FA,
- FP,
- Pend,
-]
-
-ALL_TEAMS = [
- Aviators,
- Bears,
- Blizzard,
- Bulls,
- Comets,
- Demolition,
- Dodgers,
- Ducks,
- Eclipse,
- Elite,
- Express,
- Flames,
- Foxes,
- Hawks,
- Hive,
- Hurricanes,
- Jets,
- Knights,
- Lightning,
- Outlaws,
- Pandas,
- Pirates,
- Puffins,
- Rhinos,
- Sabres,
- Shadow,
- Sharks,
- Spartans,
- Spectre,
- Tyrants,
- Waivers,
- Wizards,
- Wolves,
-]
-
-FRANCHISE_ROLES = []
-GENERAL_MGMT_ROLES = []
-CAPTAIN_ROLES = []
-
-""" Globals
-"""
-social_media: discord.Role | None = None
-franchise_manager: discord.Role | None = None
-general_manager_rl: discord.Role | None = None
-general_manager_tm: discord.Role | None = None
-assistant_general_manager_rl: discord.Role | None = None
-assistant_general_manager_tm: discord.Role | None = None
-captain: discord.Role | None = None
-premier: discord.Role | None = None
-master: discord.Role | None = None
-champion: discord.Role | None = None
-academy: discord.Role | None = None
-foundation: discord.Role | None = None
-
-
-def init(guild: discord.Guild):
- global social_media
- global franchise_manager
- global general_manager_rl
- global general_manager_tm
- global assistant_general_manager_rl
- global assistant_general_manager_tm
- global captain
- global premier
- global master
- global champion
- global academy
- global foundation
- global FRANCHISE_ROLES, GENERAL_MGMT_ROLES, CAPTAIN_ROLES
- global FRANCHISE_MANAGER, GENERAL_MANAGER_RL, GENERAL_MANAGER_TM, ASSISTANT_GENERAL_MANAGER_RL, ASSISTANT_GENERAL_MANAGER_TM
- global CAPTAIN, PREMIER_LEAGUE, MASTER_LEAGUE, CHAMPION_LEAGUE, ACADEMY_LEAGUE, FOUNDATION_LEAGUE
-
- FRANCHISE_MANAGER = os.getenv('ROLE_FM')
- GENERAL_MANAGER_RL = os.getenv('ROLE_GM_RL')
- GENERAL_MANAGER_TM = os.getenv('ROLE_GM_TM')
- ASSISTANT_GENERAL_MANAGER_RL = os.getenv('ROLE_AGM_RL')
- ASSISTANT_GENERAL_MANAGER_TM = os.getenv('ROLE_AGM_TM')
- CAPTAIN = os.getenv('ROLE_CAPTAIN_RL')
- PREMIER_LEAGUE = os.getenv('ROLE_PL')
- MASTER_LEAGUE = os.getenv('ROLE_ML')
- CHAMPION_LEAGUE = os.getenv('ROLE_CL')
- ACADEMY_LEAGUE = os.getenv('ROLE_AL')
- FOUNDATION_LEAGUE = os.getenv('ROLE_FL')
-
- social_media = get_role_by_name(guild, SOCIAL_MEDIA)
- franchise_manager = get_role_by_name(guild, FRANCHISE_MANAGER)
- general_manager_rl = get_role_by_name(guild, GENERAL_MANAGER_RL)
- general_manager_tm = get_role_by_name(guild, GENERAL_MANAGER_TM)
- assistant_general_manager_rl = get_role_by_name(guild, ASSISTANT_GENERAL_MANAGER_RL)
- assistant_general_manager_tm = get_role_by_name(guild, ASSISTANT_GENERAL_MANAGER_TM)
- captain = get_role_by_name(guild, CAPTAIN)
- premier = get_role_by_name(guild, PREMIER_LEAGUE)
- master = get_role_by_name(guild, MASTER_LEAGUE)
- champion = get_role_by_name(guild, CHAMPION_LEAGUE)
- academy = get_role_by_name(guild, ACADEMY_LEAGUE)
- foundation = get_role_by_name(guild, FOUNDATION_LEAGUE)
-
- FRANCHISE_ROLES = [franchise_manager,
- general_manager_rl,
- general_manager_tm,
- assistant_general_manager_rl,
- assistant_general_manager_tm,
- captain,
- premier,
- master,
- champion,
- academy,
- foundation,
- social_media]
-
- GENERAL_MGMT_ROLES = [franchise_manager,
- general_manager_rl,
- general_manager_tm,
- assistant_general_manager_rl,
- assistant_general_manager_tm]
-
- CAPTAIN_ROLES = copy.copy(GENERAL_MGMT_ROLES)
- CAPTAIN_ROLES.append(captain)
-
-
-def get_role_by_name(guild: discord.Guild, name: str) -> discord.Role | None:
- return next((x for x in guild.roles if x.name == name), None)
-
-
-def get_role_by_league(self, league: LeagueEnum):
- match league:
- case LeagueEnum.Premier_League:
- return self.premier
- case LeagueEnum.Master_League:
- return self.master
- case LeagueEnum.Champion_League:
- return self.champion
- case LeagueEnum.Academy_League:
- return self.academy
- case LeagueEnum.Foundation_League:
- return self.foundation
-
-
-def has_role(member: discord.user, *roles) -> bool:
- return next((True for role in roles if role in member.roles), False)
-
-
-def resolve_sprocket_league_role(sprocket_league: str) -> str | None:
- if sprocket_league == 'FOUNDATION':
- return FOUNDATION_LEAGUE
- if sprocket_league == 'ACADEMY':
- return ACADEMY_LEAGUE
- if sprocket_league == 'CHAMPION':
- return CHAMPION_LEAGUE
- if sprocket_league == 'MASTER':
- return MASTER_LEAGUE
- if sprocket_league == 'PREMIER':
- return PREMIER_LEAGUE
- return None
diff --git a/MLEBot/run.bat b/MLEBot/run.bat
deleted file mode 100644
index 84b9f05..0000000
--- a/MLEBot/run.bat
+++ /dev/null
@@ -1,9 +0,0 @@
-@echo on
-
-call .\.venv\Scripts\activate.bat
-
-python -m pip install --upgrade PyDiscoBot
-
-python mle_bot.py
-
-PAUSE
\ No newline at end of file
diff --git a/MLEBot/services/__init__.py b/MLEBot/services/__init__.py
new file mode 100644
index 0000000..7374131
--- /dev/null
+++ b/MLEBot/services/__init__.py
@@ -0,0 +1,13 @@
+"""MLE Bot Logical Services
+ """
+
+from . import sprocket
+from .sprocket import lookup_franchise, lookup_rl
+
+__version__ = '1.1.1'
+
+__all__ = (
+ 'sprocket',
+ 'lookup_franchise',
+ 'lookup_rl',
+)
diff --git a/MLEBot/services/sprocket/__init__.py b/MLEBot/services/sprocket/__init__.py
new file mode 100644
index 0000000..b9e5b7a
--- /dev/null
+++ b/MLEBot/services/sprocket/__init__.py
@@ -0,0 +1,11 @@
+"""Sprocket related services
+ """
+
+from .lookup import lookup_rl, lookup_franchise
+
+__version__ = '1.1.1'
+
+__all__ = (
+ 'lookup_rl',
+ 'lookup_franchise'
+)
diff --git a/MLEBot/services/sprocket/lookup.py b/MLEBot/services/sprocket/lookup.py
new file mode 100644
index 0000000..88d2eb2
--- /dev/null
+++ b/MLEBot/services/sprocket/lookup.py
@@ -0,0 +1,85 @@
+"""provide methods to distribute sprocket data, such as players or franchises
+ """
+from __future__ import annotations
+
+
+import difflib
+
+from ... import const
+from ...types import Member, SprocketLinks, PlayerRL, Franchise
+
+
+def lookup_franchise(sprocket_data: SprocketLinks,
+ name: str | None = None,
+ discord_id: str | int | None = None) -> Franchise | None:
+ """lookup franchise from sprocket
+
+ Args:
+ sprocket_data (SprocketLinks): sprocket data
+ discord_id (str | int | None): discord id of user
+
+ Returns:
+ Franchise | None: _description_
+ """
+ if discord_id:
+ member: Member = lookup_rl(sprocket_data,
+ discord_id=discord_id)
+ if not member or not member.franchise:
+ return None
+ name = member.franchise['Franchise']
+
+ return sprocket_data.franchise(name)
+
+
+def lookup_rl(sprocket_data: SprocketLinks,
+ name: str | None = None,
+ discord_id: str | int | None = None) -> Member | None:
+ """lookup a rocket league player from sprocket
+
+ Args:
+ sprocket_data (SprocketLinks): sprocket data links supplied by bot
+ name (str): name of player to look up
+
+ Returns:
+ Member | None: _description_
+ """
+ if not name and not discord_id:
+ return None
+
+ if name:
+ member = _get_member(sprocket_data, name, try_match=True)
+
+ elif discord_id:
+ member = sprocket_data.members.from_hash(str(discord_id))
+
+ else:
+ member = None
+
+ if not member:
+ return None
+
+ player = sprocket_data.players.from_hash(member[const.SPR_HK_PLAYERS])
+ if not player:
+ return None
+
+ return Member(member=member,
+ rl_player=PlayerRL(player=player,
+ tracker=sprocket_data.trackers.from_hash(member['mle_id'])),
+ franchise=sprocket_data.teams.from_hash(player['franchise']))
+
+
+def _get_member(sprocket_data: SprocketLinks,
+ name: str,
+ try_match: bool = False) -> dict:
+ members = sprocket_data.members.data
+ m = sprocket_data.members.from_secondary_hash(name)
+ if not m and try_match:
+ matches = difflib.get_close_matches(name,
+ [x['name'] for x in members],
+ 1)
+ if matches:
+ m = next(
+ (x for x in members if x['name'].lower()
+ == matches[0].lower()),
+ None)
+ return m
diff --git a/MLEBot/sprocket_data_link.py b/MLEBot/sprocket_data_link.py
deleted file mode 100644
index 9e7c3d1..0000000
--- a/MLEBot/sprocket_data_link.py
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env python
-""" Sprocket Data Link
-# Author: irox_rl
-# Purpose: Individualized sprocket_data link object for sprocket database
-# Version 1.0.2
-"""
-# non-local imports #
-import asyncio
-import datetime
-import requests
-
-
-class SprocketDataLink:
- def __init__(self, url_link: str):
- self.url_link = url_link
- self.last_time_updated: datetime.datetime | None = None
- self.json_data = None
-
- def compress(self):
- return {
- 'json_data': self.json_data,
- 'last_time_updated': self.last_time_updated,
- }
-
- def decompress(self, pickle_data):
- self.json_data = pickle_data['json_data']
- self.last_time_updated = pickle_data['last_time_updated']
-
- async def data(self):
- if not self.url_link:
- raise ValueError('URL Link is empty, cannot fetch sprocket_data')
- self.json_data = requests.get(f'{self.url_link}.json').json()
- self.last_time_updated = datetime.datetime.now()
- await asyncio.sleep(2)
diff --git a/MLEBot/task_roster.py b/MLEBot/task_roster.py
deleted file mode 100644
index 98db676..0000000
--- a/MLEBot/task_roster.py
+++ /dev/null
@@ -1,219 +0,0 @@
-#!/usr/bin/env python
-""" Periodic Task - Role
-# Author: irox_rl
-# Purpose: Manage roles of a franchise and roster channel information
-# Version 1.0.2
-"""
-
-from PyDiscoBot import channels, err
-
-# local imports #
-from member import get_members_by_role
-import roles
-
-# non-local imports #
-import datetime
-import discord
-from discord.ext import commands
-import os
-import pickle
-from typing import Callable
-
-IMG_STAFF = None
-IMG_PREMIER = None
-IMG_MASTER = None
-IMG_CHAMPION = None
-IMG_ACADEMY = None
-IMG_FOUNDATION = None
-
-
-class Task_Roster:
- def __init__(self,
- master_bot):
- self.bot = master_bot
-
- def __get_league__(self,
- _team: {},
- _team_players: {},
- _league: str,
- _guild: discord.Guild) -> discord.Embed | None:
- embed = (discord.Embed(color=discord.Color.from_str(_team['primary_color']),
- title=f"**{_league}**")
- .set_footer(text=f'Generated: {self.bot.last_time}'))
- embed.set_thumbnail(url=_team['logo_img_link'])
- _players = [x for x in _team_players if x['skill_group'] == _league and x['slot'] != 'NONE']
-
- if len(_players) == 0:
- return None
-
- _player_mentions: [str] = []
- for _player in _players:
- _member = next((x for x in self.bot.sprocket.data['sprocket_members'] if x['member_id'] == _player['member_id']), None)
- if not _member:
- _player_mentions.append(_player['name'])
- continue
- _discord_member = next((x for x in _guild.members if x.id.__str__() == str(_member['discord_id'])), None)
- if not _discord_member:
- _player_mentions.append(_player['name'])
- continue
- _player_mentions.append(_discord_member.mention)
-
- if _player_mentions:
- embed.description = '\n'.join([x for x in _player_mentions if x is not None])
- return embed
- else:
- return None
-
- async def post_roster(self,
- _team: {},
- _channel: discord.TextChannel) -> bool:
-
- success = await channels.clear_channel_messages(_channel,
- 100)
- if not success:
- await err('Could not delete messages from roster channel!\n'
- 'Please manually delete them and retry running the ub.runroster command again.')
- return False
-
- _team_players = [x for x in self.bot.sprocket.data['sprocket_players'] if x['franchise'] == _team['name']]
- if not _team_players:
- await err('Could not find players for franchise!')
- return False
-
- _fm = next((x for x in _team_players if x['Franchise Staff Position'] == 'Franchise Manager'), None)
- _gms = [x for x in _team_players if x['Franchise Staff Position'] == 'General Manager']
- _agms = [x for x in _team_players if x['Franchise Staff Position'] == 'Assistant General Manager']
- _captains = [x for x in _team_players if x['Franchise Staff Position'] == 'Captain']
- _pr_supports = [x for x in _team_players if x['Franchise Staff Position'] == 'PR Support']
-
- embed = (discord.Embed(color=discord.Color.from_str(_team['primary_color']),
- title=f"**{_team['name']} Staff**")
- .set_footer(text=f'Generated: {self.bot.last_time}'))
- embed.set_thumbnail(url=_team['logo_img_link'])
-
- if _fm:
- _fm_text: str = ''
- _fm_member = next((x for x in self.bot.sprocket.data['sprocket_members'] if x['member_id'] == _fm['member_id']), None)
- if not _fm_member:
- _fm_text = _fm['name']
- else:
- _discord_mem = next((x for x in _channel.guild.members if x.id.__str__() == str(_fm_member['discord_id'])), None)
- if not _discord_mem:
- _fm_text = _fm['name']
- else:
- _fm_text = _discord_mem.mention
- embed.add_field(name='**Franchise Manager**',
- value=_fm_text,
- inline=True)
-
- if _gms:
- _gm_text: [str] = []
- for _gm in _gms:
- _gm_member = next((x for x in self.bot.sprocket.data['sprocket_members'] if x['member_id'] == _gm['member_id']), None)
- if not _gm_member:
- _gm_text.append(_gm['name'])
- else:
- _discord_mem = next((x for x in _channel.guild.members if x.id.__str__() == str(_gm_member['discord_id'])), None)
- if not _discord_mem:
- _gm_text.append(_gm['name'])
- else:
- _gm_text.append(_discord_mem.mention)
- embed.add_field(name='**General Managers**',
- value='\n'.join(_gm_text),
- inline=True)
-
- if _agms:
- _agm_text: [str] = []
- for _agm in _agms:
- _agm_member = next(
- (x for x in self.bot.sprocket.data['sprocket_members'] if x['member_id'] == _agm['member_id']), None)
- if not _agm_member:
- _agm_text.append(_agm['name'])
- else:
- _discord_mem = next(
- (x for x in _channel.guild.members if x.id.__str__() == str(_agm_member['discord_id'])), None)
- if not _discord_mem:
- _agm_text.append(_agm['name'])
- else:
- _agm_text.append(_discord_mem.mention)
- embed.add_field(name='**Assistant General Managers**',
- value='\n'.join(_agm_text),
- inline=True)
-
- if _captains:
- _captains_text: [str] = []
- for _captain in _captains:
- _captain_member = next(
- (x for x in self.bot.sprocket.data['sprocket_members'] if x['member_id'] == _captain['member_id']),
- None)
- if not _captain_member:
- _captains_text.append(_captain['name'])
- else:
- _discord_mem = next(
- (x for x in _channel.guild.members if x.id.__str__() == str(_captain_member['discord_id'])), None)
- if not _discord_mem:
- _captains_text.append(_captain['name'])
- else:
- _captains_text.append(_discord_mem.mention)
- embed.add_field(name='**Captains**',
- value='\n'.join(_captains_text),
- inline=True)
-
- if _pr_supports:
- _text: [str] = []
- for _pr_support in _pr_supports:
- _pr_support_member = next(
- (x for x in self.bot.sprocket.data['sprocket_members'] if x['member_id'] == _pr_support['member_id']),
- None)
- if not _pr_support_member:
- _text.append(_pr_support['name'])
- else:
- _discord_mem = next(
- (x for x in _channel.guild.members if x.id.__str__() == str(_pr_support_member['discord_id'])),
- None)
- if not _discord_mem:
- _text.append(_pr_support['name'])
- else:
- _text.append(_discord_mem.mention)
- embed.add_field(name='**PR Supports**',
- value='\n'.join(_text),
- inline=True)
-
- await _channel.send(embed=embed)
-
- emb = self.__get_league__(_team,
- _team_players,
- 'Premier League',
- _channel.guild)
- if emb:
- await _channel.send(embed=emb)
-
- emb = self.__get_league__(_team,
- _team_players,
- 'Master League',
- _channel.guild)
- if emb:
- await _channel.send(embed=emb)
-
- emb = self.__get_league__(_team,
- _team_players,
- 'Champion League',
- _channel.guild)
- if emb:
- await _channel.send(embed=emb)
-
- emb = self.__get_league__(_team,
- _team_players,
- 'Academy League',
- _channel.guild)
- if emb:
- await _channel.send(embed=emb)
-
- emb = self.__get_league__(_team,
- _team_players,
- 'Foundation League',
- _channel.guild)
- if emb:
- await _channel.send(embed=emb)
- # await channels.post_image(context, IMG_STAFF) if IMG_STAFF else None
- return True
diff --git a/MLEBot/task_sprocket.py b/MLEBot/task_sprocket.py
deleted file mode 100644
index d8464fc..0000000
--- a/MLEBot/task_sprocket.py
+++ /dev/null
@@ -1,205 +0,0 @@
-#!/usr/bin/env python
-""" Sprocket Periodic Task
-# Author: irox_rl
-# Purpose: Get MLE information hosted from sprocket for use in parsing sprocket_data
-# Version 1.0.4
-"""
-
-from PyDiscoBot import err
-
-# local imports #
-from sprocket_data_link import SprocketDataLink
-
-# non-local imports #
-import datetime
-import pickle
-
-
-class Task_Sprocket:
- def __init__(self, master_bot):
- self.bot = master_bot
- self.league_update_flag = False
- self._members_link = SprocketDataLink(
- "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/members")
- self._player_stats_link = SprocketDataLink(
- "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/player_stats")
- self._players_link = SprocketDataLink(
- "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/players")
- self._scrim_stats_link = SprocketDataLink(
- "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/scrim_stats")
- self._teams_link = SprocketDataLink("https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/teams")
- self._fixtures_link = SprocketDataLink(
- "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/schedules/fixtures")
- self._match_groups_link = SprocketDataLink(
- "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/schedules/match_groups")
- self._matches_link = SprocketDataLink(
- "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/schedules/matches")
- self._trackers_link = SprocketDataLink(
- "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/trackers")
- self._role_usages_link = SprocketDataLink(
- "https://f004.backblazeb2.com/file/sprocket-artifacts/public/data/role_usages")
-
- self.on_updated = []
-
- self._file_name = 'sprocket_data.pickle'
- self._loaded = False
- self._last_time_ran: datetime.datetime | None = None
- self._next_run_time: datetime.datetime | None = None
-
- @property
- def all_links_loaded(self) -> bool:
- if (self._members_link.last_time_updated and
- self._player_stats_link.last_time_updated and
- self._players_link.last_time_updated and
- self._scrim_stats_link.last_time_updated and
- self._teams_link.last_time_updated and
- self._fixtures_link.last_time_updated and
- self._match_groups_link.last_time_updated and
- self._matches_link.last_time_updated and
- self._trackers_link.last_time_updated and
- self._role_usages_link.last_time_updated):
- return True
- return False
-
- @property
- def data(self) -> {}:
- return {'sprocket_members': self._members_link.json_data,
- 'sprocket_player_stats': self._player_stats_link.json_data,
- 'sprocket_players': self._players_link.json_data,
- 'sprocket_scrim_stats': self._scrim_stats_link.json_data,
- 'sprocket_teams': self._teams_link.json_data,
- 'sprocket_fixtures': self._fixtures_link.json_data,
- 'sprocket_match_groups': self._match_groups_link.json_data,
- 'sprocket_matches': self._matches_link.json_data,
- 'sprocket_trackers': self._trackers_link.json_data,
- 'role_usages': self._role_usages_link.json_data,
- }
-
- @property
- def members_link(self):
- return self._player_stats_link
-
- @property
- def player_stats_link(self):
- return self._player_stats_link
-
- @property
- def players_link(self):
- return self._players_link
-
- @property
- def role_usages_link(self):
- return self._role_usages_link
-
- @property
- def scrim_stats_link(self):
- return self._scrim_stats_link
-
- @property
- def teams_link(self):
- return self._teams_link
-
- @property
- def trackers_link(self):
- return self._trackers_link
-
- @property
- def fixtures_link(self):
- return self._fixtures_link
-
- @property
- def match_groups_link(self):
- return self._match_groups_link
-
- @property
- def matches_link(self):
- return self._matches_link
-
- @property
- def ready_to_update(self) -> bool:
- return self._next_run_time <= datetime.datetime.now()
-
- def __get_next_run_time__(self):
- self._last_time_ran = datetime.datetime.now()
- if self._last_time_ran.hour < 6:
- self._next_run_time = self._last_time_ran + datetime.timedelta(hours=(6 - self._last_time_ran.hour),
- minutes=(0 - self._last_time_ran.minute),
- seconds=(0 - self._last_time_ran.second))
- return
- if self._last_time_ran.hour < 12:
- self._next_run_time = self._last_time_ran + datetime.timedelta(hours=(12 - self._last_time_ran.hour),
- minutes=(0 - self._last_time_ran.minute),
- seconds=(0 - self._last_time_ran.second))
- return
- if self._last_time_ran.hour < 18:
- self._next_run_time = self._last_time_ran + datetime.timedelta(hours=(18 - self._last_time_ran.hour),
- minutes=(0 - self._last_time_ran.minute),
- seconds=(0 - self._last_time_ran.second))
- return
- self._next_run_time = self._last_time_ran + datetime.timedelta(hours=(0 - self._last_time_ran.hour),
- minutes=(0 - self._last_time_ran.minute),
- seconds=(0 - self._last_time_ran.second), days=1)
- return
-
- def load(self):
- try:
- with open(self._file_name, 'rb') as f: # Open save file
- data = pickle.load(f)
- self._members_link.decompress(data[0])
- self._player_stats_link.decompress(data[1])
- self._players_link.decompress(data[2])
- self._scrim_stats_link.decompress(data[3])
- self._teams_link.decompress(data[4])
- self._fixtures_link.decompress(data[5])
- self._match_groups_link.decompress(data[6])
- self._matches_link.decompress(data[7])
- self._trackers_link.decompress(data[8])
- self._role_usages_link.decompress(data[9])
- self._last_time_ran = data[10]['last_time_ran']
- self._next_run_time = data[10]['next_run_time']
- self._loaded = True
- except (KeyError, FileNotFoundError, EOFError) as e:
- self._next_run_time = datetime.datetime.now()
- self._loaded = True
-
- def reset(self):
- self._next_run_time = datetime.datetime.now()
-
- async def run(self):
- if not self._loaded:
- self.load()
- if self.ready_to_update:
- await self._members_link.data()
- await self._player_stats_link.data()
- await self._players_link.data()
- await self._scrim_stats_link.data()
- await self._teams_link.data()
- await self._fixtures_link.data()
- await self._match_groups_link.data()
- await self._matches_link.data()
- await self._trackers_link.data()
- await self._role_usages_link.data()
- else:
- return
- self.__get_next_run_time__()
- self.save()
- for callback in self.on_updated:
- await callback(self.data)
- await err('sprocket server links updated.')
-
- def save(self):
- with open(self._file_name, 'wb') as f:
- pickle.dump([self._members_link.compress(),
- self._player_stats_link.compress(),
- self._players_link.compress(),
- self._scrim_stats_link.compress(),
- self._teams_link.compress(),
- self._fixtures_link.compress(),
- self._match_groups_link.compress(),
- self._matches_link.compress(),
- self._trackers_link.compress(),
- self._role_usages_link.compress(),
- {
- 'last_time_ran': self._last_time_ran,
- 'next_run_time': self._next_run_time,
- }], f)
diff --git a/MLEBot/tasks/__init__.py b/MLEBot/tasks/__init__.py
new file mode 100644
index 0000000..2a42a33
--- /dev/null
+++ b/MLEBot/tasks/__init__.py
@@ -0,0 +1,10 @@
+"""Minor League E-Sports Bot Tasks
+ """
+
+from .sprocket import Sprocket
+
+__version__ = '1.1.1'
+
+__all__ = (
+ 'Sprocket',
+)
diff --git a/MLEBot/tasks/sprocket.py b/MLEBot/tasks/sprocket.py
new file mode 100644
index 0000000..310cb22
--- /dev/null
+++ b/MLEBot/tasks/sprocket.py
@@ -0,0 +1,193 @@
+"""Manage Sprocket data links for up-to-date information
+ """
+from __future__ import annotations
+
+
+import datetime
+import json
+import threading
+
+
+from pydiscobot import Task
+from pydiscobot.types.err import BotNotLoaded
+
+
+from ..types import SprocketLinks, UrlDataLink
+
+
+class Sprocket(Task):
+ """sprocket bot task"""
+
+ def __init__(self, parent):
+ super().__init__(parent)
+
+ self._links = SprocketLinks()
+ self._loaded = False
+ self._updating = False
+
+ self.on_updated: list[callable] = []
+
+ self._last_time_ran: datetime.datetime | None = None
+ self._next_run_time: datetime.datetime | None = None
+
+ self.init()
+
+ @property
+ def links(self) -> SprocketLinks:
+ """get all url data links for sprocket
+
+ Returns:
+ SprocketLinks: dataclass of sprocket url datalinks
+ """
+ if self.updating:
+ raise BotNotLoaded('Sprocket links are updating!')
+ return self._links
+
+ @property
+ def iterlinks(self) -> list[UrlDataLink]:
+ """get iterable links
+
+ Returns:
+ tuple[SprocketLinks]: sprocket data links
+ """
+ return self.links.links()
+
+ @property
+ def ready_to_update(self) -> bool:
+ """if sprocket has published, return True
+ sprocket publishes on 6 Hr intervals
+ starting @ 00:00
+
+ Returns:
+ bool: ready to update
+ """
+ return self._next_run_time <= datetime.datetime.now()
+
+ @property
+ def json_link(self) -> str:
+ """url link appended with .json for easy direct-to-file saving
+
+ Returns:
+ str: string of url appended with .json
+ """
+ return '.' + self.__class__.__name__ + '.json'
+
+ @property
+ def updating(self) -> bool:
+ """links are currently fetching data
+
+ Returns:
+ bool: updating
+ """
+ return self._updating
+
+ def _calc_next_fetch_time(self) -> None:
+ self.logger.info('calculating next run time...')
+
+ def _generate(n: datetime.datetime,
+ hour: int):
+ return n + (
+ datetime.timedelta(days=0,
+ hours=hour-n.hour,
+ minutes=15-n.minute,
+ seconds=0-n.second,
+ microseconds=0-n.microsecond))
+
+ now = datetime.datetime.now()
+ if now.hour < 6:
+ self._next_run_time = _generate(now, 6)
+
+ elif now.hour < 12:
+ self._next_run_time = _generate(now, 12)
+
+ elif now.hour < 18:
+ self._next_run_time = _generate(now, 18)
+
+ else:
+ self._next_run_time = _generate(now, 24)
+
+ self.logger.info('next run time -> %s',
+ self._next_run_time.strftime("%m/%d/%Y, %H:%M:%S"))
+
+ async def _update(self):
+ threads = [threading.Thread(name='fetch', target=link.fetch)
+ for link in self.iterlinks]
+
+ self._updating = True
+ # any calls to our 'links' attr here
+ # will raise an exception
+
+ for t in threads:
+ t.start()
+
+ for t in threads:
+ t.join()
+
+ self._last_time_ran = datetime.datetime.now()
+
+ self._links.compile_data()
+
+ self._calc_next_fetch_time()
+
+ self.save()
+
+ self._updating = False
+
+ for callback in self.on_updated:
+ await callback()
+
+ await self.parent.notify('sprocket server links updated.')
+
+ def load(self):
+ """load run times from artifacts file
+ """
+ try:
+ self.logger.info('loading %s...', self.json_link)
+ with open(self.json_link, 'r', encoding='utf-8') as f:
+ data = json.load(f)
+ self._last_time_ran = datetime.datetime.strptime(
+ data['last_time_ran'], "%d/%m/%Y, %H:%M:%S")
+ self._next_run_time = datetime.datetime.strptime(
+ data['next_run_time'], "%d/%m/%Y, %H:%M:%S")
+ self._loaded = True
+ except (KeyError, FileNotFoundError, EOFError, json.JSONDecodeError, ValueError) as e:
+ self.logger.info('file load error on -> %s...%s',
+ self.json_link, e)
+ self._next_run_time = datetime.datetime.now()
+ self._loaded = True
+
+ def reset(self):
+ """force run time request to now
+ """
+ self._next_run_time = datetime.datetime.now()
+
+ def init(self):
+ """initialize this sprocket task
+ """
+ self.logger.info('initializing task...')
+ if self._loaded:
+ self.logger.warning('already loaded!')
+ return
+ for link in self.iterlinks:
+ link.init()
+ self._links.compile_data()
+ self.load()
+
+ async def run(self):
+ """run this sprocket task
+ """
+ if not self._loaded:
+ self.init()
+ if self.ready_to_update:
+ self.logger.info('updating links...')
+ await self._update()
+
+ def save(self):
+ """save run time data to artifacts file
+ """
+ self.logger.info('saving data to -> %s...', self.json_link)
+ with open(self.json_link, 'w', encoding='utf-8') as f:
+ json.dump({
+ 'last_time_ran': self._last_time_ran.strftime("%d/%m/%Y, %H:%M:%S"),
+ 'next_run_time': self._next_run_time.strftime("%d/%m/%Y, %H:%M:%S")
+ }, f)
diff --git a/MLEBot/team.py b/MLEBot/team.py
deleted file mode 100644
index 9c22550..0000000
--- a/MLEBot/team.py
+++ /dev/null
@@ -1,685 +0,0 @@
-#!/usr/bin/env python
-""" Minor League E-Sports Team
-# Author: irox_rl
-# Purpose: General Functions of a League Team
-# Version 1.0.4
-"""
-
-from PyDiscoBot import channels, err
-
-# local imports #
-from member import Member
-from enums import LeagueEnum
-
-# non-local imports #
-import os
-import datetime
-import discord
-from discord.ext import commands
-from html2image import Html2Image
-
-MLE_SEASON = 'Season 17'
-EMOTE_CHECK_GREEN = ':white_check_mark:'
-EMOTE_X_RED = ':x:'
-EMOTE_SABRES_NO_BG_ID = '1002420732837511248'
-
-
-class Team:
- """ Minor League E-Sports Team Class\n
- """
-
- def __init__(self,
- guild: discord.Guild,
- franchise,
- league: LeagueEnum) -> None:
- """ Initialize method\n
- **param master_bot**: reference to mle_bot Bot class that is running this repo\n
- **param team_name**: string representation of this team's name (e.g. **'Sabres'**)\n
- **param league**: enumeration of the league this team belongs to\n
- All data is initialized to zero. Update method must be called with proper sprocket data to get actual statistics for this class\n
- For additional information about Sprocket Data Sets and these dictionaries, see:\n
- https://f004.backblazeb2.com/file/sprocket-artifacts/public/pages/index.html
- """
- self.guild = guild
- self.franchise = franchise
- self.league = league
- self.players: [Member] = []
- self.channel: discord.TextChannel | None = None
- self.message: discord.Message | None = None
- self.message_id = None
- self.played_matches: [{}] = None
- self.sprocket_data: {} = None
- self.standard_series_wins = 0
- self.standard_series_losses = 0
- self.standard_wins = 0
- self.standard_losses = 0
- self.doubles_series_wins = 0
- self.doubles_series_losses = 0
- self.doubles_wins = 0
- self.doubles_losses = 0
- self._sprocket_players: [{}] = []
- self._sprocket_members: [{}] = []
-
- @property
- def league_name(self) -> str:
- return self.league.name.replace('_', ' ')
-
- @property
- def sprocket_members(self):
- return self._sprocket_members
-
- @property
- def sprocket_players(self):
- return self._sprocket_players
-
- def __get_emote_by_id__(self, emoji_id: str) -> discord.Emoji | None:
- """ helper function to get guild emote by supplied ID\n
- ***returns***: discord emote or None"""
- return next((x for x in self.guild.emojis if x.id.__str__() == emoji_id), None)
-
- def __get_weekly_info__(self, game_mode: str, match_week: str) -> {}:
- """ Helper function to get a dictionary describing the match of a specified game mode from a specific week\n
- **param game_mode**: specified game mode that is being posted ('Standard' or 'Doubles')\n
- **param match_week**: specified match_week that is being posted ('Match 1', e.g.)\n
- **returns** dictionary describing the match\n
- """
- sprocket_data = self.franchise.bot.sprocket.data
- """ Using stored sprocket data, get match groups of this specified week / season
- """
- match_group_weeks = [x for x in sprocket_data['sprocket_match_groups'] if
- x['match_group_title'] == match_week and x['parent_group_title'] == os.getenv('SEASON')]
-
- """ Get the match from the specified week / specified mode of this season
- """
- match_this_week = next((x for x in self.played_matches for y in match_group_weeks if
- x['match_group_id'] == y['match_group_id'] and x['game_mode'] == game_mode), None)
-
- """ If we didn't get a match for this week, return an empty dictionary
- """
- if not match_this_week:
- return {
- 'home_team': '',
- 'away_team': '',
- 'score': '',
- 'home_color': '',
- 'away_color': '',
- 'home_url': '',
- 'away_url': '',
- }
-
- """ Gather both teams from the match
- """
- team1 = next((x for x in sprocket_data['sprocket_teams'] if x['name'] == match_this_week['home']), None)
- team2 = next((x for x in sprocket_data['sprocket_teams'] if x['name'] == match_this_week['away']), None)
-
- """ If BOTH teams weren't found, return an empty dictionary
- """
- if (not team1) or (not team2):
- return {
- 'home_team': '',
- 'away_team': '',
- 'score': '',
- 'home_color': '',
- 'away_color': '',
- 'home_url': '',
- 'away_url': '',
- }
-
- """ Get the scores
- """
- hm_score = match_this_week['home_wins']
- away_score = match_this_week['away_wins']
- score_wk = f'{hm_score} - {away_score}'
-
- """ Assemble all the data into a dictionary and return
- """
- return {
- 'home_team': match_this_week['home'],
- 'away_team': match_this_week['away'],
- 'score': score_wk,
- 'home_color': team1['primary_color'],
- 'away_color': team2['secondary_color'],
- 'home_url': team1['logo_img_link'],
- 'away_url': team2['logo_img_link'],
- }
-
- async def __post_quick_info_html__(self, ctx: discord.ext.commands.Context | discord.TextChannel | None = None):
- """ Helper function to post quick info html to the quick info channel of this team\n
- **`optional`param ctx**: specified context to send information to. If not supplied, the info is posted to the team's quick info channel.\n
- **returns** None\n
- """
- """ Parse all possible players based on role as defined by MLE
- """
- playerA: Member | None = next((x for x in self.players if x.role == 'PLAYERA'), None)
- playerB: Member | None = next((x for x in self.players if x.role == 'PLAYERB'), None)
- playerC: Member | None = next((x for x in self.players if x.role == 'PLAYERC'), None)
- playerD: Member | None = next((x for x in self.players if x.role == 'PLAYERD'), None)
- playerE: Member | None = next((x for x in self.players if x.role == 'PLAYERE'), None)
- playerF: Member | None = next((x for x in self.players if x.role == 'PLAYERF'), None)
- playerG: Member | None = next((x for x in self.players if x.role == 'PLAYERG'), None)
- playerH: Member | None = next((x for x in self.players if x.role == 'PLAYERH'), None)
- playerA_name = next((x.mle_name for x in self.players if x.role == 'PLAYERA'), '')
- playerB_name = next((x.mle_name for x in self.players if x.role == 'PLAYERB'), '')
- playerC_name = next((x.mle_name for x in self.players if x.role == 'PLAYERC'), '')
- playerD_name = next((x.mle_name for x in self.players if x.role == 'PLAYERD'), '')
- playerE_name = next((x.mle_name for x in self.players if x.role == 'PLAYERE'), '')
- playerF_name = next((x.mle_name for x in self.players if x.role == 'PLAYERF'), '')
- playerG_name = next((x.mle_name for x in self.players if x.role == 'PLAYERG'), '')
- playerH_name = next((x.mle_name for x in self.players if x.role == 'PLAYERH'), '')
-
- """ Can we fix this to not use my local stuff please... god i'm an idiot
- """
- if playerA:
- playerA_emote = 'D:\Personal\SabresUtilityBot\checkmark.png' if playerA.schedule_confirmed else 'D:\Personal\SabresUtilityBot\\redex.png'
- else:
- playerA_emote = None
- if playerB:
- playerB_emote = 'D:\Personal\SabresUtilityBot\checkmark.png' if playerB.schedule_confirmed else 'D:\Personal\SabresUtilityBot\\redex.png'
- else:
- playerB_emote = None
- if playerC:
- playerC_emote = 'D:\Personal\SabresUtilityBot\checkmark.png' if playerC.schedule_confirmed else 'D:\Personal\SabresUtilityBot\\redex.png'
- else:
- playerC_emote = None
- if playerD:
- playerD_emote = 'D:\Personal\SabresUtilityBot\checkmark.png' if playerD.schedule_confirmed else 'D:\Personal\SabresUtilityBot\\redex.png'
- else:
- playerD_emote = None
- if playerE:
- playerE_emote = 'D:\Personal\SabresUtilityBot\checkmark.png' if playerE.schedule_confirmed else 'D:\Personal\SabresUtilityBot\\redex.png'
- else:
- playerE_emote = None
- if playerF:
- playerF_emote = 'D:\Personal\SabresUtilityBot\checkmark.png' if playerF.schedule_confirmed else 'D:\Personal\SabresUtilityBot\\redex.png'
- else:
- playerF_emote = None
- if playerG:
- playerG_emote = 'D:\Personal\SabresUtilityBot\checkmark.png' if playerG.schedule_confirmed else 'D:\Personal\SabresUtilityBot\\redex.png'
- else:
- playerG_emote = None
- if playerH:
- playerH_emote = 'D:\Personal\SabresUtilityBot\checkmark.png' if playerH.schedule_confirmed else 'D:\Personal\SabresUtilityBot\\redex.png'
- else:
- playerH_emote = None
-
- hti = Html2Image(size=(615, 695))
- html_string = open('..\TeamQuickInfo.html').read().format(league=get_league_text(self.league),
- std_series_wins=self.standard_series_wins,
- std_series_losses=self.standard_series_losses,
- std_game_wins=self.standard_wins,
- std_game_losses=self.standard_losses,
- dbl_series_wins=self.doubles_series_wins,
- dbl_series_losses=self.doubles_series_losses,
- dbl_game_wins=self.doubles_wins,
- dbl_game_losses=self.doubles_losses,
- player_a_mle_name=playerA_name,
- player_a_emote=playerA_emote,
- player_b_mle_name=playerB_name,
- player_b_emote=playerB_emote,
- player_c_mle_name=playerC_name,
- player_c_emote=playerC_emote,
- player_d_mle_name=playerD_name,
- player_d_emote=playerD_emote,
- player_e_mle_name=playerE_name,
- player_e_emote=playerE_emote,
- player_f_mle_name=playerF_name,
- player_f_emote=playerF_emote,
- player_g_mle_name=playerG_name,
- player_g_emote=playerG_emote,
- player_h_mle_name=playerH_name,
- player_h_emote=playerH_emote,
- team_img=self.__get_emote_by_id__(
- EMOTE_SABRES_NO_BG_ID).url)
- hti.screenshot(html_str=html_string, css_file='..\TeamQuickInfo.css',
- save_as=f'{get_league_text(self.league)}.png')
-
- with open(f'{get_league_text(self.league)}.png', 'rb') as f:
- if ctx:
- # noinspection PyTypeChecker
- await ctx.send(file=discord.File(f))
- # noinspection PyTypeChecker
- await self.channel.send(file=discord.File(f))
-
- async def post_season_stats_html(self,
- game_mode: str,
- ctx: discord.ext.commands.Context | discord.TextChannel | None = None):
- """ Helper function to post season stats html to the quick info channel of this team (Standard or Doubles, individually)\n
- **param game_mode**: specified game mode that is being posted ('Standard' or 'Doubles')\n
- **`optional`param ctx**: specified context to send information to. If not supplied, the info is posted to the team's quick info channel.\n
- **returns** None\n
- """
- """ Get weekly information and store into a local dict
- """
- await self.update()
- wk = {}
- for i in range(1, 14):
- wk[f'{i}'] = self.__get_weekly_info__(game_mode, f'Match {i}')
-
- """ Temporary integers to hold info on html image size
- Ideally, this should be placed into the .env file or something similar... Better sizing needs to happen
- """
- width = 615
- height = 750
-
- """ Create html 2 image object"""
- hti = Html2Image(size=(width, height))
-
- """ Create a formatted version of the template file
- The dictionary above will fill out this file
- """
- html_string = open(r'team/html/TeamWeeklyStats.html').read().format(league=get_league_text(self.league),
- mode=game_mode,
- home_team_wk_1=wk['1']['home_team'],
- away_team_wk_1=wk['1']['away_team'],
- home_team_wk_1_clr=wk['1']['home_color'],
- away_team_wk_1_clr=wk['1']['away_color'],
- home_team_wk_1_logo=wk['1']['home_url'],
- away_team_wk_1_logo=wk['1']['away_url'],
- score_wk_1=wk['1']['score'],
- home_team_wk_2=wk['2']['home_team'],
- away_team_wk_2=wk['2']['away_team'],
- home_team_wk_2_clr=wk['2']['home_color'],
- away_team_wk_2_clr=wk['2']['away_color'],
- home_team_wk_2_logo=wk['2']['home_url'],
- away_team_wk_2_logo=wk['2']['away_url'],
- score_wk_2=wk['2']['score'],
- home_team_wk_3=wk['3']['home_team'],
- away_team_wk_3=wk['3']['away_team'],
- home_team_wk_3_clr=wk['3']['home_color'],
- away_team_wk_3_clr=wk['3']['away_color'],
- home_team_wk_3_logo=wk['3']['home_url'],
- away_team_wk_3_logo=wk['3']['away_url'],
- score_wk_3=wk['3']['score'],
- home_team_wk_4=wk['4']['home_team'],
- away_team_wk_4=wk['4']['away_team'],
- home_team_wk_4_clr=wk['4']['home_color'],
- away_team_wk_4_clr=wk['4']['away_color'],
- home_team_wk_4_logo=wk['4']['home_url'],
- away_team_wk_4_logo=wk['4']['away_url'],
- score_wk_4=wk['4']['score'],
- home_team_wk_5=wk['5']['home_team'],
- away_team_wk_5=wk['5']['away_team'],
- home_team_wk_5_clr=wk['5']['home_color'],
- away_team_wk_5_clr=wk['5']['away_color'],
- home_team_wk_5_logo=wk['5']['home_url'],
- away_team_wk_5_logo=wk['5']['away_url'],
- score_wk_5=wk['5']['score'],
- home_team_wk_6=wk['6']['home_team'],
- away_team_wk_6=wk['6']['away_team'],
- home_team_wk_6_clr=wk['6']['home_color'],
- away_team_wk_6_clr=wk['6']['away_color'],
- home_team_wk_6_logo=wk['6']['home_url'],
- away_team_wk_6_logo=wk['6']['away_url'],
- score_wk_6=wk['6']['score'],
- home_team_wk_7=wk['7']['home_team'],
- away_team_wk_7=wk['7']['away_team'],
- home_team_wk_7_clr=wk['7']['home_color'],
- away_team_wk_7_clr=wk['7']['away_color'],
- home_team_wk_7_logo=wk['7']['home_url'],
- away_team_wk_7_logo=wk['7']['away_url'],
- score_wk_7=wk['7']['score'],
- home_team_wk_8=wk['8']['home_team'],
- away_team_wk_8=wk['8']['away_team'],
- home_team_wk_8_clr=wk['8']['home_color'],
- away_team_wk_8_clr=wk['8']['away_color'],
- home_team_wk_8_logo=wk['8']['home_url'],
- away_team_wk_8_logo=wk['8']['away_url'],
- score_wk_8=wk['8']['score'],
- home_team_wk_9=wk['9']['home_team'],
- away_team_wk_9=wk['9']['away_team'],
- home_team_wk_9_clr=wk['9']['home_color'],
- away_team_wk_9_clr=wk['9']['away_color'],
- home_team_wk_9_logo=wk['9']['home_url'],
- away_team_wk_9_logo=wk['9']['away_url'],
- score_wk_9=wk['9']['score'],
- home_team_wk_10=wk['10']['home_team'],
- away_team_wk_10=wk['10']['away_team'],
- home_team_wk_10_clr=wk['10'][
- 'home_color'],
- away_team_wk_10_clr=wk['10'][
- 'away_color'],
- home_team_wk_10_logo=wk['10'][
- 'home_url'],
- away_team_wk_10_logo=wk['10'][
- 'away_url'],
- score_wk_10=wk['10']['score'],
- home_team_wk_11=wk['11']['home_team'],
- away_team_wk_11=wk['11']['away_team'],
- home_team_wk_11_clr=wk['11'][
- 'home_color'],
- away_team_wk_11_clr=wk['11'][
- 'away_color'],
- home_team_wk_11_logo=wk['11'][
- 'home_url'],
- away_team_wk_11_logo=wk['11'][
- 'away_url'],
- score_wk_11=wk['11']['score'],
- home_team_wk_12=wk['12']['home_team'],
- away_team_wk_12=wk['12']['away_team'],
- home_team_wk_12_clr=wk['12'][
- 'home_color'],
- away_team_wk_12_clr=wk['12'][
- 'away_color'],
- home_team_wk_12_logo=wk['12'][
- 'home_url'],
- away_team_wk_12_logo=wk['12'][
- 'away_url'],
- score_wk_12=wk['12']['score'],
- home_team_wk_13=wk['13']['home_team'],
- away_team_wk_13=wk['13']['away_team'],
- home_team_wk_13_clr=wk['13'][
- 'home_color'],
- away_team_wk_13_clr=wk['13'][
- 'away_color'],
- home_team_wk_13_logo=wk['13'][
- 'home_url'],
- away_team_wk_13_logo=wk['13'][
- 'away_url'],
- score_wk_13=wk['13']['score'],
- team_imgurl=self.franchise.bot.server_icon)
-
- hti.screenshot(html_str=html_string, css_file=r'team/html/TeamWeeklyStats.css',
- save_as=f'{get_league_text(self.league)}weeklystats_{game_mode}.png')
-
- """ Open the newly created .png file and post it!
- """
- with open(f'{get_league_text(self.league)}weeklystats_{game_mode}.png', 'rb') as f:
- if ctx:
- # noinspection PyTypeChecker
- return await ctx.send(file=discord.File(f))
- # noinspection PyTypeChecker
- await self.channel.send(file=discord.File(f))
-
- def __process_matches__(self, sprocket_matches: [{}],
- as_standard: bool) -> None:
- """ Helper function to parse through supplied sprocket matches (Singles or Doubles, individually)\n
- **param sprocket_matches**: dictionary of matches.json from sprocket (usually supplied by sprocket class)\n
- **param as_standard**: bool used to determined mode where **True** is standard mode\n
- **returns** None\n
- For additional information about Sprocket Data Sets and these dictionaries, see:\n
- https://f004.backblazeb2.com/file/sprocket-artifacts/public/pages/index.html
- """
- """ Set a string to match based on mode
- """
- mode = "Standard" if as_standard else "Doubles"
-
- """ Parse matches that match the mode we're in (set by the string above)"""
- for match in [x for x in sprocket_matches if x['game_mode'] == mode]:
- """ Parse wins or losses based on winning team's name
- Also, use the boolean mode to determine which stats to increase
- """
- if match['winning_team'] == self.franchise.franchise_name:
- if as_standard:
- self.standard_series_wins += 1
- else:
- self.doubles_series_wins += 1
- else:
- if as_standard:
- self.standard_series_losses += 1
- else:
- self.doubles_series_losses += 1
-
- """ Same as above but in-line
- """
- if as_standard:
- self.standard_wins += match['home_wins'] if self.franchise.franchise_name == match['home'] else match[
- 'away_wins']
- self.standard_losses += match['away_wins'] if self.franchise.franchise_name == match['home'] else match[
- 'home_wins']
- else:
- self.doubles_wins += match['home_wins'] if self.franchise.franchise_name == match['home'] else match[
- 'away_wins']
- self.doubles_losses += match['away_wins'] if self.franchise.franchise_name == match['home'] else match[
- 'home_wins']
-
- def __reset_match_data__(self) -> None:
- """ Helper method to reset all match datas to 0\n
- This method is only intended to be used internally by this class\n
- To properly reset match data externally, run the **update** method
- """
- self.standard_series_wins = 0
- self.standard_series_losses = 0
- self.standard_wins = 0
- self.standard_losses = 0
- self.doubles_series_wins = 0
- self.doubles_series_losses = 0
- self.doubles_wins = 0
- self.doubles_losses = 0
-
- async def update_from_sprocket_data(self) -> None:
- """ Update this team's information from supplied sprocket data\n
- **param sprocket_data**: dictionary of .json data from sprocket (usually supplied by sprocket class)\n
- **returns** None\n
- For additional information about Sprocket Data Sets and these dictionaries, see:\n
- https://f004.backblazeb2.com/file/sprocket-artifacts/public/pages/index.html
- """
- """ Begin by clearing out match data
- """
- self.__reset_match_data__()
- sprocket_data = self.franchise.bot.sprocket.data
-
- """ validate data
- """
- if not sprocket_data:
- return
- if not sprocket_data['sprocket_matches']:
- return
- if not sprocket_data['sprocket_match_groups']:
- return
-
- """ Get valid, played matches from supplied sprocket data
- """
- self.get_played_matches(sprocket_matches=sprocket_data['sprocket_matches'],
- sprocket_match_groups=sprocket_data['sprocket_match_groups'])
-
- """ If no played matches were found, do not continue the process
- """
- if not self.played_matches:
- return await err(f'Could not find valid, played matches for\n'
- f'team {self.franchise.franchise_name}\n'
- f'league {self.league}\n')
-
- """ Process standard series matches
- """
- self.__process_matches__(sprocket_matches=self.played_matches,
- as_standard=True)
-
- """ Process doubles series matches
- """
- self.__process_matches__(sprocket_matches=self.played_matches,
- as_standard=False)
-
- def add_member(self,
- new_member: Member) -> bool:
- """ Add a MLE member to this team's roster\n
- **param member**: MLE Member to be added to this roster\n
- **returns** Success status of add\n
- """
- """ Validate the member's league is the same as this teams'
- """
- if new_member.league != self.league:
- return False
- """ Validate that the member isn't already a part of our team
- """
- if new_member in self.players:
- return False
- """ Add the member
- """
- self.players.append(new_member)
- new_member.update(self.franchise.bot.sprocket.data)
- return True
-
- def build(self):
- self._sprocket_players = [x for x in self.franchise.sprocket_players if x['skill_group'] == self.league_name]
- self._sprocket_members = []
- for _player in self._sprocket_players:
- _mem = next((x for x in self.franchise.sprocket_members if x['member_id'] == _player['member_id']), None)
- if _mem:
- self._sprocket_members.append(_mem)
- _guild_member = next((x for x in self.franchise.guild.members if x.id == int(_mem['discord_id'])), None)
- if _guild_member:
- self.add_member(Member(_guild_member,
- self.league))
-
- async def build_quick_info_channel(self,
- sprocket_data: {}) -> None:
- """ Build quick info channel for this MLE team\n
- Note: This method will return if no channel exists.\n
- This function will clear the Quick Info channel messages (up to 100) and post various pieces of quick info\n
- **param sprocket_data**: dictionary of .json data from sprocket (usually supplied by sprocket class)\n
- **returns**: None\n
- """
- """ If no channel exists, immediately return
- """
- if not self.channel:
- return
- """ Clear out channel messages as much as we can
- """
- await channels.clear_channel_messages(self.channel, 100)
-
- """ Send notification that an update is running
- """
- await err(f'Running new quick info channel information.\n'
- f'{self.franchise_name} - {self.league}')
-
- """ Send the team quick info html doc to the team's quick info channel
- """
- await self.__post_quick_info_html__()
-
- """ Use helper function to send html doc of Standard matches to quick info channel
- """
- await self.___post_season_stats_html__('Standard')
-
- """ Use helper function to send html doc of Doubles matches to quick info channel
- """
- await self.___post_season_stats_html__('Doubles')
-
- def get_played_matches(self, sprocket_matches: {}, sprocket_match_groups: {}) -> [{}] or None:
- """ Get played matches from sprocket data\n
- **param sprocket_matches**: dictionary of matches.json from sprocket (usually supplied by sprocket class)\n
- **param sprocket_match_groups**: dictionary of match_groups.json from sprocket (usually supplied by sprocket class)\n
- **returns** dictionary of valid, played matches from the current MLE season\n
- For additional information about Sprocket Data Sets and these dictionaries, see:\n
- https://f004.backblazeb2.com/file/sprocket-artifacts/public/pages/index.html
- """
-
- """ Get games that include this team as either home or away
- param x: rocket league series played in MLE
- """
- matches = [x for x in sprocket_matches if
- ((x['home'] == self.franchise.franchise_name) | (x['away'] == self.franchise.franchise_name)) and
- x['league'] == get_league_text(self.league)]
-
- """ Get valid match groups that occurred this current season
- param x: match group hosted by sprocket that includes season data
- """
- match_groups = [x for x in sprocket_match_groups if x['parent_group_title'] == os.getenv('SEASON')]
-
- """ Compare the two previous search results to come up with a final list of valid, played matches this season
- param x: rocket league series played in MLE
- param y: match group of series' played in MLE
- """
- valid_season_matches = [x for x in matches for y in match_groups if
- x['match_group_id'] == y['match_group_id']]
-
- """ Return only games that have been played
- Games with the winning team marked as below have not been played, so these should not be included
- param x: matches from this season that include our team
- """
- self.played_matches = [x for x in valid_season_matches if
- (x['winning_team'] != "Not Played / Data Unavailable")]
- return self.played_matches
-
- async def get_updated_players(self) -> [Member]:
- for player in self.players:
- player.update(self.franchise.bot.sprocket.data)
- return self.players
-
- def remove_member(self, _member: Member) -> bool:
- if _member in self.players:
- self.players.remove(_member)
- return True
- return False
-
- async def update(self):
- """ Update MLE Team from supplied sprocket data\n
- This method is a callback for the sprocket periodic data task\n
- This method will **rebuild the team's quick info channel** (if it has one)
- **param sprocket_data**: dictionary provided by Sprocket class of all sprocket public datasets\n
- **returns**: None\n
- For additional information about Sprocket Data Sets and these dictionaries, see:\n
- https://f004.backblazeb2.com/file/sprocket-artifacts/public/pages/index.html
- """
- """ Update self with the newly provided sprocket data
- """
- await self.update_from_sprocket_data()
-
- """ Update players with the new sprocket data
- """
- for player in self.players:
- await player.update(self.sprocket_data)
-
- """ Finally, after everything has been updated, build the quick info channel
- """
- await self.build_quick_info_channel(self.sprocket_data)
-
-
-def get_league_text(league: LeagueEnum) -> str | None:
- """ Get text representation of League enumeration """
-
- match league:
- case LeagueEnum.Premier_League:
- return "Premier League"
- case LeagueEnum.Master_League:
- return "Master League"
- case LeagueEnum.Champion_League:
- return "Champion League"
- case LeagueEnum.Academy_League:
- return "Academy League"
- case LeagueEnum.Foundation_League:
- return "Foundation League"
-
-
-def get_league_text_short(league) -> str | None:
- """ Get shorthand string representation of League enumeration """
-
- match league:
- case LeagueEnum.Premier_League:
- return "PL"
- case LeagueEnum.Master_League:
- return "ML"
- case LeagueEnum.Champion_League:
- return "CL"
- case LeagueEnum.Academy_League:
- return "AL"
- case LeagueEnum.Foundation_League:
- return "FL"
-
-
-def get_defined_team_players(_team: {},
- _sprocket_data: {}):
- if not _sprocket_data or not _team:
- return None
- _all = [x for x in _sprocket_data['sprocket_players'] if x['franchise'] == _team['name']]
- return {
- 'all': _all,
- 'fm': next((x for x in _all if x['Franchise Staff Position'] == 'Franchise Manager'), None),
- 'gms': [x for x in _all if x['Franchise Staff Position'] == 'General Manager'],
- 'agms': [x for x in _all if x['Franchise Staff Position'] == 'Assistant General Manager'],
- 'captains': [x for x in _all if x['Franchise Staff Position'] == 'Captain'],
- 'pr_supports': [x for x in _all if x['Franchise Staff Position'] == 'PR Support'],
-
- 'pl_players': [x for x in _all if x['skill_group'] == 'Premier League' and x['slot'] != 'NONE'],
- 'ml_players': [x for x in _all if x['skill_group'] == 'Master League' and x['slot'] != 'NONE'],
- 'cl_players': [x for x in _all if x['skill_group'] == 'Champion League' and x['slot'] != 'NONE'],
- 'al_players': [x for x in _all if x['skill_group'] == 'Academy League' and x['slot'] != 'NONE'],
- 'fl_players': [x for x in _all if x['skill_group'] == 'Foundation League' and x['slot'] != 'NONE'],
- }
-
-
-def get_mle_franchise_embed(_team: {}) -> discord.Embed:
- embed = (discord.Embed(color=discord.Color.from_str(_team['primary_color']),
- title=f"{_team['name']} Roster")
- .set_footer(text=f"Generated: {datetime.datetime.now().strftime('%c')}"))
- embed.set_thumbnail(url=_team['logo_img_link'])
- return embed
diff --git a/MLEBot/team/html/TeamQuickInfo.css b/MLEBot/team/html/TeamQuickInfo.css
deleted file mode 100644
index b839d4c..0000000
--- a/MLEBot/team/html/TeamQuickInfo.css
+++ /dev/null
@@ -1,166 +0,0 @@
-body {
-background: #250000;
-background: -moz-linear-gradient(left, #250000 0%, #7B4409 50%, #A25C07 100%);
-background: -webkit-linear-gradient(left, #250000 0%, #7B4409 50%, #A25C07 100%);
-background: linear-gradient(to right, #250000 0%, #7B4409 50%, #A25C07 100%);
-}
-
-h1 {
-text-align: left;
-color: white;
-font-family: Arial Black, Gadget, sans-serif;
-font-size: small;
-}
-
-series_banner_div {
-position: relative;
-top: -1px;
-background: #1c1c1c;
-border: 1px solid #000000;
-color: white;
-height: 102%;
-width: 122px;
-font-family: Verdana Black, Verdana, monospace;
-font-size: small;
-line-height: 30px;
-text-align: center;
-text-shadow: 4px 4px 4px black;
-white-space: nowrap;
-}
-
-game_mode_div {
-position: relative;
-top: -3px;
-text-align: left;
-color: white;
-font-family: Verdana Black, Verdana, monospace;
-font-size: small;
-text-shadow: 4px 4px 4px black;
-padding-top: 10px;
-padding-bottom: 10px;
-width: 80px;
-white-space: nowrap;
-}
-
-win_loss_div {
-position: relative;
-top: -1px;
-border: 1px solid #000000;
-height: 102%;
-width: 122px;
-font-family: Verdana, Verdana, monospace;
-font-size: small;
-line-height: 30px;
-text-align: center;
-color: white;
-white-space: nowrap;
-}
-
-long_banner_div {
-height: 30px;
-width: 575px;
-margin: 5px;
-margin-bottom: 10px;
-padding-left: 10px;
-padding-bottom: 1px;
-border-radius: 10px 10px 10px 10px;
-background: #1C1C1C;
-background: -moz-linear-gradient(left, #1C1C1C 0%, #2A2A2A 50%, #373737 100%);
-background: -webkit-linear-gradient(left, #1C1C1C 0%, #2A2A2A 50%, #373737 100%);
-background: linear-gradient(to right, #1C1C1C 0%, #2A2A2A 50%, #373737 100%);
--webkit-box-shadow: 5px 5px 15px 5px #000000;
-box-shadow: 5px 5px 15px 5px #000000;
-display: flex;
-flex-direction: row;
-}
-
-short_banner_div {
-height: 30px;
-width: 328px;
-margin: 5px;
-margin-bottom: 10px;
-padding-left: 10px;
-padding-bottom: 1px;
-border-radius: 10px 10px 10px 10px;
-background: #1C1C1C;
-background: -moz-linear-gradient(left, #1C1C1C 0%, #2A2A2A 50%, #373737 100%);
-background: -webkit-linear-gradient(left, #1C1C1C 0%, #2A2A2A 50%, #373737 100%);
-background: linear-gradient(to right, #1C1C1C 0%, #2A2A2A 50%, #373737 100%);
--webkit-box-shadow: 5px 5px 15px 5px #000000;
-box-shadow: 5px 5px 15px 5px #000000;
-display: flex;
-flex-direction: row;
-}
-
-img.scheduler_img {
-position: relative;
-margin: auto;
-top: -12px;
-left: 0;
-right: 0;
-bottom: 0;
-width: 50px;
-height: 50px;
-}
-
-img_div {
-line-height: -10px;
-height: 28px;
-width: 75px;
-margin-top: 4px;
-display: flex;
-}
-
-player {
-height: 30px;
-width: 260px;
-margin: 5px;
-margin-bottom: 10px;
-padding-top: 0px;
-padding-left: 10px;
-padding-right: 5px;
-padding-bottom: 5px;
-border-radius: 10px 10px 10px 10px;
-background: #1C1C1C;
-background: -moz-linear-gradient(left, #1C1C1C 0%, #2A2A2A 50%, #373737 100%);
-background: -webkit-linear-gradient(left, #1C1C1C 0%, #2A2A2A 50%, #373737 100%);
-background: linear-gradient(to right, #1C1C1C 0%, #2A2A2A 50%, #373737 100%);
--webkit-box-shadow: 5px 5px 15px 5px #000000;
-box-shadow: 5px 5px 15px 5px #000000;
-display: flex;
-flex-direction: row;
-}
-
-player_role {
-text-align: left;
-color: white;
-font-family: Arial Black, Gadget, sans-serif;
-font-size: small;
-padding-top: 10px;
-padding-bottom: 10px;
-}
-
-mle_name {
-width: 110px;
-text-align: center;
-color: white;
-font-family: Arial, Gadget, sans-serif;
-font-size: small;
-padding-top: 10px;
-padding-bottom: 10px;
-}
-
-#shadowbox {
-justify-content: center;
-align-items: center;
-height: fit-contents;
--webkit-box-shadow: 5px 5px 15px 5px #000000;
-box-shadow: 5px 5px 15px 5px #000000;
-background: #1c1c1c;
-align-items: right;
-padding-top: 5px;
-padding-right: 10px;
-padding-bottom: 10px;
-padding-left: 0px;
-border-radius: 10px 10px 10px 10px;
-}
\ No newline at end of file
diff --git a/MLEBot/team/html/TeamQuickInfo.html b/MLEBot/team/html/TeamQuickInfo.html
deleted file mode 100644
index b9eb961..0000000
--- a/MLEBot/team/html/TeamQuickInfo.html
+++ /dev/null
@@ -1,192 +0,0 @@
-
-
-