From 684596b76efec5d77a94ed70a83d978955c097fd Mon Sep 17 00:00:00 2001 From: Brian LaFond <52360893+iroxusux@users.noreply.github.com> Date: Wed, 26 Mar 2025 22:37:23 -0400 Subject: [PATCH 1/7] updates to bring back to life, more to come soon - fixes for sprocket attrs - detach logic from cmd files for cleanliness - restructure for proper packaging (WIP) --- .env | 152 --------- .idea/vcs.xml | 1 + .vscode/launch.json | 17 + MLEBot/.idea/.gitignore | 8 - MLEBot/.idea/MLEBot.iml | 8 - .../inspectionProfiles/Project_Default.xml | 52 --- .../inspectionProfiles/profiles_settings.xml | 6 - MLEBot/.idea/material_theme_project_new.xml | 10 - MLEBot/.idea/misc.xml | 4 - MLEBot/.idea/modules.xml | 8 - MLEBot/.idea/vcs.xml | 6 - MLEBot/__init__.py | 10 +- MLEBot/__pycache__/__init__.cpython-311.pyc | Bin 282 -> 0 bytes MLEBot/__pycache__/enums.cpython-311.pyc | Bin 824 -> 0 bytes MLEBot/__pycache__/franchise.cpython-311.pyc | Bin 10736 -> 0 bytes .../__pycache__/lo_commands.cpython-311.pyc | Bin 8613 -> 0 bytes MLEBot/__pycache__/member.cpython-311.pyc | Bin 12254 -> 0 bytes MLEBot/__pycache__/mle_bot.cpython-311.pyc | Bin 7758 -> 0 bytes .../__pycache__/mle_commands.cpython-311.pyc | Bin 44555 -> 0 bytes MLEBot/__pycache__/roles.cpython-311.pyc | Bin 8354 -> 0 bytes .../sprocket_data_link.cpython-311.pyc | Bin 2030 -> 0 bytes .../__pycache__/task_roster.cpython-311.pyc | Bin 15214 -> 0 bytes .../__pycache__/task_sprocket.cpython-311.pyc | Bin 13082 -> 0 bytes MLEBot/__pycache__/team.cpython-311.pyc | Bin 43044 -> 0 bytes .../__pycache__/salary_card.cpython-311.pyc | Bin 0 -> 3189 bytes .../__pycache__/salary_card.cpython-313.pyc | Bin 0 -> 2726 bytes .../__pycache__/usage_card.cpython-311.pyc | Bin 0 -> 4284 bytes .../__pycache__/usage_card.cpython-313.pyc | Bin 0 -> 3293 bytes MLEBot/embed_frames/salary_card.py | 44 +++ MLEBot/embed_frames/usage_card.py | 66 ++++ MLEBot/enums.py | 18 +- MLEBot/franchise.py | 75 ++--- MLEBot/lo_commands.py | 6 +- MLEBot/member.py | 18 +- MLEBot/mle_bot.py | 46 +-- MLEBot/mle_commands.py | 298 +++++++----------- MLEBot/roles.py | 10 +- MLEBot/run.bat | 9 - MLEBot/task_roster.py | 11 - MLEBot/task_sprocket.py | 64 +++- MLEBot/team.py | 28 +- 41 files changed, 394 insertions(+), 581 deletions(-) delete mode 100644 .env create mode 100644 .vscode/launch.json delete mode 100644 MLEBot/.idea/.gitignore delete mode 100644 MLEBot/.idea/MLEBot.iml delete mode 100644 MLEBot/.idea/inspectionProfiles/Project_Default.xml delete mode 100644 MLEBot/.idea/inspectionProfiles/profiles_settings.xml delete mode 100644 MLEBot/.idea/material_theme_project_new.xml delete mode 100644 MLEBot/.idea/misc.xml delete mode 100644 MLEBot/.idea/modules.xml delete mode 100644 MLEBot/.idea/vcs.xml delete mode 100644 MLEBot/__pycache__/__init__.cpython-311.pyc delete mode 100644 MLEBot/__pycache__/enums.cpython-311.pyc delete mode 100644 MLEBot/__pycache__/franchise.cpython-311.pyc delete mode 100644 MLEBot/__pycache__/lo_commands.cpython-311.pyc delete mode 100644 MLEBot/__pycache__/member.cpython-311.pyc delete mode 100644 MLEBot/__pycache__/mle_bot.cpython-311.pyc delete mode 100644 MLEBot/__pycache__/mle_commands.cpython-311.pyc delete mode 100644 MLEBot/__pycache__/roles.cpython-311.pyc delete mode 100644 MLEBot/__pycache__/sprocket_data_link.cpython-311.pyc delete mode 100644 MLEBot/__pycache__/task_roster.cpython-311.pyc delete mode 100644 MLEBot/__pycache__/task_sprocket.cpython-311.pyc delete mode 100644 MLEBot/__pycache__/team.cpython-311.pyc create mode 100644 MLEBot/embed_frames/__pycache__/salary_card.cpython-311.pyc create mode 100644 MLEBot/embed_frames/__pycache__/salary_card.cpython-313.pyc create mode 100644 MLEBot/embed_frames/__pycache__/usage_card.cpython-311.pyc create mode 100644 MLEBot/embed_frames/__pycache__/usage_card.cpython-313.pyc create mode 100644 MLEBot/embed_frames/salary_card.py create mode 100644 MLEBot/embed_frames/usage_card.py delete mode 100644 MLEBot/run.bat 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/.idea/vcs.xml b/.idea/vcs.xml index 35eb1dd..76e0ffd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..cec3284 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}\\test.js" + } + ] +} \ 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..323407d 100644 --- a/MLEBot/__init__.py +++ b/MLEBot/__init__.py @@ -1,3 +1,11 @@ from .mle_bot import MLEBot from .mle_commands import MLECommands -from .lo_commands import LoCommands \ No newline at end of file +from .lo_commands import LoCommands + +__version__ = "1.1.0" + +__all__ = ( + "MLEBot", + "MLECommands", + "LoCommands", +) diff --git a/MLEBot/__pycache__/__init__.cpython-311.pyc b/MLEBot/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index abf7f8c5bd444f5d5ba4dc2aba9962c28f54e60e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 282 zcmZ3^%ge<81b#v3X-+`;F^B^LOi;#WIUr*?LkdF*V-7*MN_Ujk%t16j`bxw(mXDaC%8EVtNmb5i4z@=J=CfhuqDfOyGJ z=^_@O&`O5SK$78?rL$E`XmM&$aZFNCW@289OMY@`Zfaghag2LriAQNt48#T)6(66O zmst`YuUAm{i^C>2KczG$)vgHSE0Bwd^?}3(W=2NF8w}DHP|*zr)eG2A5eHBY0EU}N A0{{R3 diff --git a/MLEBot/__pycache__/enums.cpython-311.pyc b/MLEBot/__pycache__/enums.cpython-311.pyc deleted file mode 100644 index d54507ca2b8615871aac7754d1022cb6a80a603b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 824 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 diff --git a/MLEBot/__pycache__/lo_commands.cpython-311.pyc b/MLEBot/__pycache__/lo_commands.cpython-311.pyc deleted file mode 100644 index 7edb48d2cf5f7526a456324211d355f2f9a70c29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8613 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 X7Khyb=c_k<|Mt3n|M@E(^G5oA4^~tb diff --git a/MLEBot/__pycache__/member.cpython-311.pyc b/MLEBot/__pycache__/member.cpython-311.pyc deleted file mode 100644 index 9df5ae245749ea59b2909cc66322842924acefe7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12254 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 xh~K?O)+ftu`0+n%7cjQh9%J}`XBh-DYXCq diff --git a/MLEBot/__pycache__/mle_commands.cpython-311.pyc b/MLEBot/__pycache__/mle_commands.cpython-311.pyc deleted file mode 100644 index 44cda51620adc33b4333599f19f1779806c8ba69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44555 zcmdtLd2kz9nje@r2@n8pkrb&TNlKJR@ep<25=Du+sr#Z15M+WBDS%`pKvfAvRcpIy zrl^Wqw`R85O?#?5aBoew%B`K@ZnVAX*q&M0Q{mC>bc7RddWnETyWH967`NAdphnf> zUd_gMf8WbYWF`oSl+-=7b~5?l<;!=!m*4xo?|tukKlJ$uIb46!_x|9wCphl^q6h7A zsg7rV3&;03f$QZ2i(m~|!q#4^h1@o{ZN0Xzz1Pm3?Ra+dI+)uLa-O3w&QL*b0e%ZY z1z}gOi@kG&+~LCB!my{;6ZZCc!@gc$xTv>?y>o_&dy84TlHL;hxaSPMZxMpyHNar zy?3clGQ)4z$D(}KSU4OQ6(WTVeCI^;#+bN+9~Q^n@rxn&oSG2F$0ETU{Gs4zPz;3l z?upTX=+zUZ50)M`vZGGF8!Un{>#hiRAe`ItZG$91}NGK4w!4GKp{er62 zWJ$XQZUjb0gP};1HC=crFf=?Gpd8Xmj*Lc;C(BOvMug%sK`MTrKNLJDien$4>KO_i?1}4UCWbwesWH zrMU;xZel-0>cg`Z4A1XzL9W+=;b#>rA8@@kHzx!HThKn@(4q+Tpc7r{_`rdoW_p88 zbfOboLAOv4bPCp30ms45^@05di2Nfh0j{^u^xlm)9xE60j`*}7LZRS!=>35evGA{4 zEQD58&YYsT9D*-cj1m^*=4+1ATauG!u~3pX&r+=fLg~Y@A6QT({J(OUlvcxlH<#jN zPrYS1C9DvZdwlOMMnZJ)IF@vGlKLG4Z^AgLaY`V7YMONSi3-oCZRbu#E>JWtm}l9+%P#I zT7|X+LbMC(7YNZIY{(5UWY5WGqp&GAOiqZ+!j=U>Y!$ZUh8VIv+-{yASgMQrns)#* zWP2^>T&i_AC+q~4*#&Im5_Z4J*nQbIXMWdP#mcGUr9HylyrX zQ`rAT`E@Oj-_o4?HVFsbD8GXX@@Isz3$*;Xhv)OO{Nl=cfsMY4uTtLLx||YT5-#U0;i5_a9Mk*q+sE#>BW(36Wy`hvx;}3ppdm|$~tHP&Fz}B308fMJCIB1a%P(5BiB7w zEo!|{Og`YaNM*09w#^_?LvWGUii2^ zbC}EcKG@lJZ5O!j*?yZ?S9Jbpy{YCF)ZG1AHE&o{NedS$kEt{+l*YqK2moR3x_mI^ptd$uuND!uV%|p zGvaN4S#$x)OVZ$38g+X9Xq{J0qaJCvg@!juV--td^&6$p$kJHzMro`?O$;drO*fgW zLbM9aqZVP^E46J4D^Keim8XrR(f&qhtY>L-EGiABxZ;K5ZNsEf*r=xh?rz>VyJs26 zQl0TKeXQ!P$%2QQ&0o!YjNY`RXCOPxAu3Ii8}a{a6To*Z+N5}`?2cs;;w8OE_pCSB zr1(cglNL-777O=rxnKc(0IGl{+@3GECR^ItwmxlnM^4FJFTcig`N2_V&8#vQMF-0}Fr zm)oUNmy%tVq^?WTodEjH0*O@qbV+1f92>X^8D2OT?gx=#Y5_ZfkYMK73V`o%Lm&yR z73S9dcdQ?KKuB4{l?WvA0AE#(duirOJK0 z5QqjM!Dt(QU|8U9k4^C5U|^K*6RB5Y*1o;-8ifsu`*>>@7W;A=CXV;>FEed_&zg_2 ziqDS*A9GA~$nDAwxlS5#B+nIwBLici;2#!xFzgEa{zz2x`_uLb5TR-7u<(UV?;f!R z37dwa*Z`kb9*8ssBc}kgfmjleN+S2)dSrRj^3&qkZ6}l4PA0aUmbaaj&iBdZgYx+s zv**W?=f@N0Mftp#+$Kuf#QUWH`puGH9U9vJ+7+EbBUI_ou?h6(OFJ`vpY1~t4SiOf z+h?FR4Ij4J#ueE%UaGdSTPX`Qu`=QfO^Oy*za`^W{(8;2gPL`lp;@a2rK-W{07iZ7 zPv4VHUy?2h^66l5Z&2DBoVEbyHw&8km5+x4xAB!Pra@0BL9<`SYd5Gx$CP;*l$bCZ z!yRe&pcoh(xG@|FDy<6tEFM9ts%3xkR_&x#-D+snCD)`S*QPrkbv(N8)9p_Rq*GVq z!&j3#uSz?w-VXriHw&pWYLSLWuJ^&2N4+Ww(f?`nM`)})5 zdVHckG(5miG#?rsy%}jMJR1Zu7!!khAj)6SvQ=JQTYDoK9gpm2Zy#L0ep6dNO41(+ z#De`B+A!_34-SWd?OKf1fEXPf#9~*ZUCE?HSn1P_ z=x{U?OnZc2WI!Aqr{%Ke9%YFx8WeW$Us%$1S^!J?LSsW?{^9VDpNb1_+k_a3xiH=F zJs<|2Tl&rc1Jh)gcH9btCW2|_@F;=?V@?Wl5JL>ddB+F?r!aq6F1Y#qO;M!;2;u=3{wqb)!FyF`Ayn>W(>_?kA%jev6ANIb4sW4 z$5H3z=5#?+dG1F29fY@qrgD+wC~240{AoK)yP%M$(F%m&!NJgQf4Y>`69+3e~efE53u_hjfwJA}SgaFH;lrHh397YN-w(Mm@6XD9pFF~{Y3YS6;W}I9}#mDc+#k^d+ddiMR zZ$*alx+|Yn)+n^1N-;*ImOb*Cy?9F0UY0>1UhyX?`{c^LsY9vqnhfXk)jqAMlWR84 z99C(qGZ&(?B&-+Eksl?_H|pT=u~8P2j~ad57p=6?6DeqLFV zV$=j^65P`Vq`HmrvQ2O%N+NPeBvn%JmBU(9`85YHb%v8+%E}7pf@tdDGo{Y(d zuS>4lgv&3x{F2L`a{0bf{890Auhf2UX6vIZKi&(6az7EKiW9E0vg@qmI;(}dB()x# zsd`i`ckG8tx!*Y8QKYzB?l>g19#!wN7b)*cvg?xMx}@cOUD|kg=51;3d3oyvIMus< zkb(}$t|7@aqy@e5Xsgt9S>D+zUGGco?2~r(%@oWONQ0r&%7%=Mb60=GS=_5JWZkPV zV%@9f)MmIMtv@z%=+QBG(_y&O``jhU;j-+yEV(YLIY`S}rOp0FRZ`~_dHYp3)%!+> z!iHs6SaOB6(p-?%bv}4|=GMb+!J*twR#CW&E;dm78ICT?^z>Y7|wF0G)V|<%}JBUG}ea#!H%Jc;gG=m#C3QfwgKcJST~s`HO#VOp<6+JmKn$^`M64H9Rbi7&Sha~ zSvcPD#{-Y+9v}E}%}+u<4L!Lj_xRuZJ z92|8OYvU<63~jtCmurmU{rt<^X5cIhlzx1EaQK*GsLax~Y%A|(D^@D)2x2o3ru8Ss z#Q1nnH1SGa|IN{{w@3Y~1AbjC-iHE4dH}Q*_ui7cx9)d5?tDD><&i%-`7cf;4xE<{ zoR=;Kfqmr5;n~ZP@`QYOB6(m!IxsPH8bH5U0<76Z;>MaBBK4|5Cc0VK!R$oD zUh*WG3U$;et+(N!RIN`XmzMbbdB#qxZo%P28ANUaXk^pRt7M?KC4cZSKZ(Aw75LQ>V$T|J#GKgC094e)ooJyUb%W7yb|79 zviDZXTLN;UqzvRnNm+)oy2}(6$|(k;6XK|e(Wcck`XZkR08pyLBBXI`Ee_xLmtZr0 z>5#755znC`9M#95i2@(XX9Yks1wA5tSKuNVxE z-3pG2!NK8o@La5s^%|Q_9}=(7vCE03&w0;?VytWqj>Vdc9rV(m14uK{1%SoQ;`i?F zjc<|5n`g_nCd;=zawW<;Fc_&wZ@$D|9_0)kg$zy*Gcpprsqu z)Sy8zUwqGu)n7VOj8Hsw89QeotbWHjsm>pQ4U3U>y?f%>Swc&feg((8ZgoytbC)jR za5}^B-um!YiR6kGlCVrg;#m->dqIAr#okBTavXc ziP|=~wr!^UajU%H)a-@}$qg408!pKkE=jd?XB^zJ<#WubdFXj6D9J<3jLoUq|}ldw8#5eror0p`lFl-SK@Yo>^u>9yv`OTf5lK8A6&7D5Z11y37513FuY5 zQH<`m@c9k7ajV?8>(Mp2@tE@JD;5}to%+tn)vo-iz*fnB%>hgmW?Wnem||_Z05e>2 z+?E%Mff>dt1wLt(&JV~(1*4x7ejAUsEyOHwqZYB$&C zw02VR`)!A92q0RIsF~#ib04=Uqji2amGbYb(-rUq=WkW(F;bhvUW&knH z#Awu{zCtMKef&Qo5=~v9v*>CoaU(g05Xxmn59WoyXjVxaY5`fSK55bE_DSnP$`4{W z@&PqNfBk2cI}5Lo;UfQn<3@h$#6_{6G>!`Rqt zZ&CPb0IKN6%N5r!9%^l}Xlw}weTj%55iK5eT(P_!`!dsq9>EChwN!gFK15}cOSL{gJ#;YTJSQ9gQmJjlO` z-dA4ViLId6BqXK5qo#?WL$CRX@iwe6(&}mc0YD=~TcVA_J^z9N*W$YF3yV00=%(H< zEZeHR@d<*&%8b3CR3ire$O!WKl!>SZY-ZL`cPLV8@Hw2oo$CDD7S# z2K$MridCLb96S`w@R>n)PQui-5Pc6<_^IuGxI&KPXND{BN&GrK6tK*+Jj$+k_CwKp z==a5+nYh{%J1?;V$^i7kLz`Kr#Qd=m>%FLy?2AdM$}0K#^RZuK)4e+Lz2oQwE9a`^$U44!3IjLYCGM6BF6 za@2190I?$f5#SX+;Z*68PpW52>yo8)iP8qSv_bOZ-WjgIy*Edkxh=JQH)w$JR?J%E zdogL1@5LUayVPYd6L|4#gOAHN2^#HSE*5aCm}SoM|e&KCX*fs29RTUe!P;ZI?Cfr5Hj zzFNptSCoa97CzsqN()1B6ssVDeNn@sLuo7aI$6g<=yh7#5sv|+t=RBn4TsWJ>~yjY z;1^q*tO5LX<9CpLJMkMx+fbskO@ymJEmpeVZKZr^JC!eO9|sf(ttf2RX>y&RJnV!p z6#~j8en#H^h`{d?Fh33$<~GiW_iX08fRFb>?nB#$_74j`^vsu$O&U}b%>2hkh4%_0 z_4gJ;1j?uU;t%;OG)n~A_bqoE__gELdC!)g12UtONyjaV*tn?t9rtYDgq%=Qv%ahx z_w0+x*NJ>rFEU>{@-6tx_F|ghEN4tfSA89!9%9#>0;3w^RN<73ZAo)Y78p6bTyAs9 z4mo7u2d;-6bKW0$qNQ5O9Cy!S?o+|5vS!zfrHW@2B=wl!M}0z(Iqf^HNmqUqN@Hre zlH|i;bM8pO_e>d_T$R_Po{=MS}Se*DWIfCkjFwT zv{?c?)_|dPq@by`rgKep@gcGhX9)Zuf&T#jvF&=;@Z7n0G$)^PcQSs(8LdAXqp2D@3Dw*RITt_go8cm_)kXD*)!V-E#xlbGH+aUQde#;;h%$>E&C1Kl@L7y0v%8hfOuF@Z+0jpm3 zoaR&}b8x~P#|zt{e3C5>BH_YVr8*8(IurwwMp|FY*H?HJ zbfKcl)TcAg}r!NF)8ztZK6Nb+l zq-_uuBRB4|0?Y$9(SuHjay&qK3~JCcn%5{BI7U}dg2zKC5V5<8>#-=|io?qFl^=F_)L~yDL za$;Y}tao|RyF9+?^R;sQR>`|O;oT;Cw@Kb@sp9GkXZNj0m6U(a_q#r+VPk65RSb>N z6;SLby-vzVfJhf`4q=LZwRAtTd>EF2_ z)o)BJ**Mjm@)m#R#78Hjm7OVm)eqg@cY{dK$6(8*nG27)rGpm|yDmtZFUTzyGY)zU zc>y5GPi~v7t`}~C5OCM8fo6`c<}1$OTl+PE41r&Ix!Too&9*--e{|_5%YV8&wf!jR za~-GB15O=dZSb9*k9s72?*lsk?wNJU?^I91yHECF{d8ZdymHzjl{ZM<1~v|xEZt3I zX|}$o5YT=i(fs|l{SjDIfmR=M3mGS_h-UogKZV%qB^T4va}psO6vRI#pC<&eLl*jk zs_=()cY+DGLCrF4?;jfri8Pg_U88|p!_Zm{HhFbXPY%yy9bEP1nQQXq!-=XR za@7%asoLh=ky^U^)9Tr!8VD4t(PkF9p6G=;Wo_rRr_C zr+lf(eG-BvpnHhDSKAN??|3axO1`_edLukjVkwR0-Ys%y?${9+tYwp=c2oGn|IEL%6zAeF64lx>mAwn*MQcZ!q|QNp&38C$h` zRfYrjgL3xsw6R(DV?Hkjzud$XuZka=sZJE_kc)QA7Ih_yx)MbP<)VX<>xDP_aMLAX z5Q070E3GXmb)5+gVRsOCVqNF>3=6?`Db|V@{i8(UVIqm7Zv3EX90^Qx^mdzxb}ztZ z@jm{a{d-{Sg_Z`)bcvCdXRGuiutqA%m7j7~q+AONtlgY(W2?TdWb(~Y>SoFZbh(jM za%v?q{+eTe=5c&xHPXzGu(-7F=2{ERhXp^twEQEQn%Mh6V7zsxpdzs)vO1>Mi@Y=d&$xZEK>oU9N3Ut!RjMe;S@$(UDxykyx=& zUa?WC-KE@5YgbIaE3e#~s73fKsm4|yR$n{ts;?br)z=Oz3;?xZ)0)KAH!Q88UcZ8_ za|x(18wzfOIm;>se|ZQxl&84<*M$D+=#T-1FSNd&ENx`$`+I?id zXOGa#v2cxGYHs0~BTBrCdNJvK0kn)Qx-?yvoaahagdCF_km}eLS+Ygpc4HlN(?~Bc zN?^^E#~4QeTN+-ty%wAk)TQBtQz~3c%Qty?#HVQabNUn^l8hv_N2O5I4~t1~NYvjS z430vqGdegtM9ai=JbjhzxoEl0Bz>Xpa3lVojRP3e?bef={nfrkqZZ4(9GQzM5pT(= z#C1?xDLN}QgSIwJN05!z;YSgJd$Niv(8{HTg#cWUe>dR4tY4MIM+U)%w#$7f=AlR@ zqG*c5)j_E`IQ`b+x-VD#SnIE<8x zZGTxGF!Aq;nqT}sso<_|Z2Lkl)io6}l^ta*-#mYde}Rx`PiQQl&Dm+|SS0O)WB{|e zGJju15HUi48Yk^kaBkW|q^e(efVMzZB|xN!9Oix0p(Fl#JVs~?kceWR(y)Jt$Ji1h z!dp8;rQkU zm7njPZP=1**pg`2CO2%8_)g_cRPL86_k*u2hR)o4DkS`Rx&~@duoa7qAO*&c$*VTY ztM=e2Q5}@=X6R<3JS3Ngrd+Aws(jhsc$4hsNMGZVm9wQS$zq6lIw_17DRLq}eFBqpA0Dlr3piQsZ z>Tsc3v+Ju!yOCfY$3;#3w4IuX-ZZVc zXGn$ zqT*`XsYV(U3K@$qS>Di09XpX78*?SV%~{iEuQ(qYDV*1%jz1#fT#KV67o|2gMMk;o zY?m5+RqV>Adty|!&!s9~fnr}2eJd2e)SGo)E;g87&gXB3!L@w;4rsmP^LLWP?>y<7 zv}<%8;8hHS!F-d!(3+VG5PW!Bv{Y+kT3;-N9#YA}QnMyf+`zA719L)|iMGz_#xO%* z<>s82_6>a=^DdOYV2bSF5_3F^NLPMZU8oeQglZTZ!x%BH!Ggb|RmORjNsGoN)O^6f zuo!5RAK_oQCSAg^XubA?mA!fLrbVP|7nbKuc|~;98>Z|KR_0Bak2bzx%1)s!Z_4#i zTB=pA*J_Odp&@U|tD?I~)EI`l^{xPw*NDzc!ZXzQ#*8X^zyg@GYq1hknES%VK z1S^nLJ;|z@WG5A~z|qY3k(h%u)qXhChFX@}P=ZBe++bwPxUWjE-DC*-k7`(>EoUlD zr>Gr@PGOJUFy!gSnF^Y+*~#8>vgaUcN|8piiRyX9);mkBw*!fw@hWdB_w?xpff=9N zwny53CfRmI!hI1&iv04$K1mo!UL27wj@*9>K)G2+trpBkt*rVw#4K1WZP*8_XV)D*X0X-X<#^cVOY8_jD}#pfPZHVQQUPS5FRHY z&@d`~jZJZ$nnD{CA*qt?Y*R@4&nDZ>O1LjwmU{c-O96ag$xAn-OE=LL>=*FwtSyQ< z2LeJceET=l6c^AG;(ww+Mw9>F#V9ZBCS^BfBIh?$r;En!9hEBSHgxa#WZQWOckdPH zYQNk&APt3+yfUGsdUDPhS2@r0NmGNg`NQggA+ay(^P~skXyO%&0fdd4( z2^<25)j+pN;h0#=PTHjb>tVqNw5po=4k$Ykcko-F?{rq(hq!~^rtd-Q*w6<#i=xP4 z>Kh_e7&fNWD$;#4s-V)8)Wa|-!yMQt^k)baL93{noyGqN&e$3g1EVn}#wkeAdkiAt zGh(Uka*z9;RDL;fR~angPvMs?P!ag66i=j$iwahAQeVAp$!N7xEFeIGH|^^=8Up(!%0i|!46Rb2B9L#QD7Vc;DbzoGrq|k3{bE^WR{2HjL*I%40{sLA2m}cX z5*Q*tYFlZTRvl)7OA(SJT1hoIzh)w4- z!W87`boA4A*Sm_*&3*iOQRD#9$>>i=NNgLTr*Q*<|+PBfgB8&0Ph)}|U-o;Iw(T zN&T=}fXO?vDZH>uuEmtexX#aHpLAG*JvUd99Omwq!)^h`BF9M1ac}cm#;ALhe^1>H zP~RVA>ugTJ)I&7m&b!!d6W_hUdGbbkv{DbO>j~uFVTbr7y1ddnZXmS;fvn5(?l>o% z`DKSlrA$xdVUDa4Bc%cd7dQ@Z*Zv9Qmd^ZAKVb>vg=X0)J48XK%6~Y)WP!P~khNAL zg{68q;8P>izEbREIkA_&QtTCa?8WRNWaV4A=p6XZiZS}@%wHtz&;JM`jrbRK#fhDn zcTGBR#F+_um`59v?=|$qHeDuz=wz=!S&qV%LTw44%JtuL|dRVse2i?u1@ z1>IVAc8_7+eUAq13KKC6T&*x;Zz4q${~x5H@I=qy$|Eu~+1<~XP>fugX1$(pk@z>1 zD)lZC1&aSKT&A*TMOnnTW+kBW1bfEQ1B|SuV4Ww}vxy|wQ`pa}^va~8k=?3Dn(x1c zivAE4W#Sx?kk*OH{lJ1fdb{QAN1j|wZoe$yzV4U$2IcER(#^M$*WZ$^zZI_oP;S;l zriNL7!`4|?%;WW_@H{5A)yQ7Iq`MR*`vKKKVX_~*HB7qb@j#R{-3 z3&x#yuuZ+O0%d2`4z`~^<~)x@+R%3(3~g_l9oIm zbSHd^gUlrF(<TS~MZP*!Ay93*pYO(nQV5)eocm=Udxt(y3I^huO zghQ+o76(};%&C@S7qEf^6VRWyW=k5AC5^D@D0vU!o(_^Ovf>7O5vK}nv2+c%#nSzx z3k^8s!2Sx&FmXH&yIEN^<(Vt1gIR4iHfRG(?a$i7-GmK)m7kADb(@qsW3@FLv&4sf z82)~E=2)U}pWL_)Z3I6BKHmTM)|W?(KB>m`AC7&0Z02U7alhQSKV#Dar@9Y6ss1u# z^qR{F?a)Kdbsv_F_~q`t+3p+3?i-2j5xILLV+eq1u4(__==Vowt|S^e<;G5EWM>Pe z7Rkk}T1#8PmeH0l>`VusEnzBvaU{h^)vL0RE<*dve563iGvrdSvZ`Ga;3_!^U z3L9CctI)95X0B4VN2a9?1*U%JQ8zJc#h;@Tv8|kz`Y1;wE7+w<7b>>>M#&UQ9gvqv zd4{kwV?BT7gbImjh8U)Ui;b3g!C)eu&N6=65(S&3UF>8%#i%gNp6t@qiuFw-MjMOs zn45e`z~!bg*x7k_4wx&+Eeqjs_0pB8bJTf};yFatxgf5TZedx<2 z!e5vNZIm~(r`|-gQ69Oyxil}JrnHz`)){hX$(u{-OI4OEBP0-ISKP3zMDxQo z8~ejH^9au)9%%20ZODoTR1IE;2iVm5V&Q;^86+-wBmSRF0vL8ZY{^xCiR1nJ%RHUj zv&QEKfsZ+qsz?LtLh5OKTgiOAxB>yi-v%&p)ND34GtvzRVqW3Zs0D+lK-=u_=fuez zMhtDU!;%SU$;5Ogbc}BO?DD_z|F8bUiRr#JcwqH#4i;4cI+#gMzh)O4-IDCrz zs*puhBo14t3`=z7BX+_aYxcfav-y}i+S|zY!{St3p=x<5YZj_5wmK&Xm9Jy^$64K_ zy4Vs}-q8*Uf&tf2tkR1(<;OZ0R;*|cpPc4Ys}SYQ&C0n*Aw8)0rwC!{_pD^65pPtb zr=t0`Mli*4pV^=dR?M`)h@)m)ITPn(&2kKd=PNRq?EU8Hp{y3zQ|h9%NEjlHrYp+* z*eW&ee9#YR!3Z4ct-WMjkhUSyv^@Z*oD;aBxw$hEfrKb98ihjlXi$Xd6}7FPl<=mN zr|q(e5KF7Ijq)aHkmn6%s1{xAzc5t0wO?ZQV<_kBn`xLC{P8+C)LVP0RNFNW7>~lF zSS03cZthauzs~BtKre04_4>@viPo10Ia z;m@j%|9`68e=ycAD7Di4IB)ILm#o)fh0V>%E-7fotbz%mE9b_>H@8Cyg&w@dU+D^r zo*f<=<@kS=Up+<_-ib~sD*jEHF^#!WAL647cvH0+_7&ni zqj?bD4KpFoUMG!XC8?O=|+Q=YMoxo$d3S2)lRg4GI zu*2naJ)3kj1%dq|9Ofq|0s&1o(9qV%rYObw}_bv1CGCGLc%c6k@y5T8Q>aYo{D@WK6)~jws2!^#~_> zy`|iVGEpuQC9jB-tjmRM-pX6<+7ua_h?q_V^iwDY3a-b`M^bKg{gj7PfkeCqdgwLQ=+wB2CN^D>j$8Y0;wd) z+^jy>qyZpH0?k_Ty>ju|gr`aNG(9+-^t4HyHZ}S93Ulf{=|Z1;_!U#XCKs5x1f*}`wgCQq}|6C^(%9~R(Dv<79sdbIf93kpvPIIM}MuSy*krM@5?D>f** z1|`?voXb1ws!O`+{-E)}@|j7w>40?9Pj`J^qP|bA??b++`gKq1+etsCgY{3^RHCf)EEsG$s$Jc-NxD$u>`V>CDnV0$J`?E^SlR*cHB98$ zXydpaHJiWLbstR%HO5K7+}&QZN#suuRp0cwsDmX^F7L-K*|l`mwKC~inQ+y~t~$w8 zmntgz_=a4xQaKYLQM6hvS}nQq+$`X15iQ(2s7*B;OKsi`q*mMnq*lBWNUeA$DdsI2 zDC|ptHX~m~g|KqTw#Kl-e{ei3Ta@*K!7*7%CxNyN|Igw8ACgMAMLG3Pu#nIEYl>4( z0JOYZjK}PdqVeZnCS5WM1mJeFa2bL*+ru0`MfVDa7`%knCYAcECc;vZu>2uJ{_*(* zfKRT*7YWvuow z&L;$d`a-3o4xkehmEwcs>IO&`s{@6O6GqSoqos(F(rBqY2o0K2=_gbvDvlb+~J=&gO>M1QTG5V;b3neA&pR z0{-2MY>ee^eC+<$sI7Pe-Ir&uZZ>yl9yEtTh3XAc}mwzB^iDBcvH)VB0dH|KcF1CO5=^` zQ2-joTdH(fs;rL2RskDZHX2(1L_HY*tBPVnW({nbFof#&V_B38rcHop^23TcY+wdL zv_y)78mp-f`T)|NBSb~u7e2X>%1b0SfDDK z>4$y8Fl=}jGB}u55dSwyI@sSbWC~I7zztR7Jo(le@%dNSw!NiM4Ct0W{R|95Qbu+B4G%T`i%)I zl_sLCwqmIUmcbE8W1aMF_74VF9Z-VE0x!3IQ^v8?u{*;7q*^x0EqgOIIOm#Lbbt0z z0ivbAQI_i1CU+dj*xX%-G01*W8+MkOSscvXF|h7Qw%A=m}+^{5DKb1Unq1UQpP1KI3RjBX1OpmJy# zWLy+XFUIW@EPv?%*7^Z<08^Kx63p22Nx+CG;A$xaZ z+>Xk^Oi?!JOEZ@w@1BHrkL=x(jc{Gsd0O(GNqEo5-ZNQWQ!)ki(!xw_snhcn2ktE^b9##?73Z4zoSK zTFZufj$!=4YBJ|M00+)_;GCUs?4x0!b$DhyOXNL^HZY`>9jV5fG%~_$WQ5ts2xDZJ zzr_m2K8uE0$erPIm@N%5(&A^F?0t)t&-A;g#^aPt56gy$Uf|BnrVc}Wb&E`jUvOqP z4H&b~;%1!m7NnSlSn!pHDT^_d#Tcu}*t{&Pjt=dMhLiB90*qN`aWhVO>*Ac1cQ46~ zCDXfsx*B2(b-l|_*SidLy*m$exq*mRHhv0YlpE!hKwo&Ct6!aQ;5Wl*z?h?!P{v7b zE3=JU0&=;y=Jt%6T$~xKDniUPI6elybE{iEzm{>pg_tevnF4aTxOM9@ZgO!i01DYd z6i?a}9UJ$90gH$+Jep+x5IifK$TS>u%yrH&_MBN20lM+TyWqeJ!8%%GRAHOU33=weuex?}km7D>pkM$ZuCO4lzae?}f+IhM)v9atbA zqJx93gW^kjP*49r5IW;H8qUjJf!`e+irz?DhSlSPR8BHoL)>YW-Bg7TN>q_{i0>qt zp4KSta;dTzKBXhLz_z!R@Y&qh+gbV!2H)oKJ2%=6cdP zX{RG;Xc&zaY9p%4qL-<*MOc$C!lxI0_gk9jn2zC+!}5AvUSWRZ1ZQtKaX%w z@U)E2TgqIUU#MW@50woFhp7LQ4G8hh_>Iqx-Bp$h&td5NF_NcKr|2lc;B(8m=a$xA z{3l#W=CsKpW~cSqn2m4eV>YbS0`&20e*vQZYXo3p1kyiq|FK~x_S0B&_#;@{P5anS z04Ft*Oe0c&s7#!p2{eu&tVp1X${WY)yBt|OuDm~#s%e*MV4NBBv9)9s1aKv*Ab2QQ zb@wm=l(#;(q!^sPdn{GHBHo-VZ;{Gdrn;uOW*Y7uOr5?e^^GJ?k4U&HPE5PvWuJQE zm*ncUndV3DCN>;RR3C%CdXL|_dm`mIb060I&)|+@?Sp=2U?HD=AMaFt3EIynT5+O}3rQ?fC$(}bQhD<3blxfW(Q*z`@iB=rhg_NuU(qo9z*(3gA_==ws zp!OCY5cnqm7`PweCtav)7(0mbJJOovBERC7R;~AFk69Zwnulh{MSC{bK~cT%sn9TC zTUksxGi)0Tfs-`oX)ypu)>BG2vm#YoJ{3(B_&%Kc_T+!{-UshZS0oFTO9jiHR@KQ> z>!-ZfG~zD)&a#h|-CObTis?;B_e#mVlD%wy*~^ZXy?kl18!6^o6*w{+wjtT8*!XW+ zo-Xjiko*A2TTb0RFdP{e+dmd%Mrhd~JM63ywz!>k(D5h{k=C=7y+fPHMc6&B1&3?& zqK^O*xOfCgTNK(z0zyI&@jnpw3xKq(Yivl7Yg{8VLerKa%wbrCwK7kEnW_W{L2yEx zA{yEwzKg)PxyXMD2m;7r$=ID1I|xI7BF@4~+)Cq};2S=fWL9om|FvZ5p#4vqC^fQ VfC*L?TdE&4WH|i15U@`C{{XZpzzP5W diff --git a/MLEBot/__pycache__/roles.cpython-311.pyc b/MLEBot/__pycache__/roles.cpython-311.pyc deleted file mode 100644 index 5c7bf67396d60b26304c8363dbc16800c7da1204..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8354 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 diff --git a/MLEBot/__pycache__/task_roster.cpython-311.pyc b/MLEBot/__pycache__/task_roster.cpython-311.pyc deleted file mode 100644 index 4031a086168319e9e7c569d21989a572d978bfc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15214 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 diff --git a/MLEBot/__pycache__/task_sprocket.cpython-311.pyc b/MLEBot/__pycache__/task_sprocket.cpython-311.pyc deleted file mode 100644 index 827366c33783e7e8702e91a7b33396b53476cbe0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13082 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 diff --git a/MLEBot/embed_frames/__pycache__/salary_card.cpython-311.pyc b/MLEBot/embed_frames/__pycache__/salary_card.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b8443765126c7ca28ff085bd9563db34ffcd368 GIT binary patch literal 3189 zcmbtWO>7&-72YLx`6rQ58p)Pvi&@7%GNp)g692TUD%+uCNrpnok{uwj0&2;fC0APR z60<|sCe%VG3bY6kB!>VIkQ(5njsojoAeS6+@F52uSHcD+wkTkrx#UK}DGK=1H^Y#! zkff-g-Pt#9-pu>v{S4>v^KdxC!ShZ1yW(JgO)IfYj|I={%X z-m~aoy?4>8cr{KD;1l8VX`C8R{C^e}1;w+##RIi3rMET1#L_I1i&Z2|jxJP8Oe|^M zEUY5Z7SHMOZG@!-S(ou`DJ^3qbWFNbB_$K5Bn_Lla##n+ToqSL3#Fc4D4}v5DUw<> z3PdvvNrlajY$3^1CAm;QmL(;nf{tWE*9;f|%sU8Mbm{Af(~0w;V<5T7SZP$!42^`o zr@m)gUztK<*H>ZIxj_}nMxmrx$c?l_If7+y0~Z7qtrqb^=Y{X-3P?ZY2){eG(&5)U z_ypOV;8r~CL7nRUZ&0?xe-mT~$;kVvot!>x#IUh(Y3+&fadYmnpJ{Xc_j z-|;HGE&pAx_Fl*0y04A*e6IvZWOuwmtiKENUPtm7cgM38+-uDrgf*f5 zoEM$N+?vPg`vw0(doc_@M6_$yqpj|}^E?N8u;0fndW%!SO5`Weh(I)YwtDwQ<9}eh z#4vufyFWzqMKN9m3|{Hm7e+tBIIu5_gA8L}Ul@nLljjhW=qfvz*rOaac;(0o#deU{ z6WfOj#;gAqh8M?v@w^>d^D4)8QIr!uch2ChJ(;P6=df00H>jzb*bTlt zJDE#oXS4G)&+xD-7;+iadZmR5Ze6}JhH4HE*ES|(B1=V?ln{oSG=E#N+AAv6ysi}_ zg)V^WbJa?O77&ukCPtD>q#K=uZ1TNRB|<7zDw$MIpFWew!`+zIPrktmquq^jvC z+1419G0{}HK&)hIlVn9MA}gsG3c8gjk+L2S#l5arV4jFXBRX=s6l4{!R-qStt!oMy z2+IU1DJjmo0ztQk+f7}b%}!-=*XL(z!MT!2ObJ+6;H_f47Jzvv195o9FJFs$F}+4y z@usX-k?Ye~iKzMLnwbf>|80Go51AX7Rs>gMHQJ(nN%VO(lTIyNx1bC!<*_CkOA}_HT84O8 zOH&$|uI85@1#g+8-7)nXl;1M4mRecJ72r=oqH-Iuz_P-|cm#H!wZ{4f)YEnD-vKVt z|G|5YN6s|8e2>O|Sp1;;C~|x!a=g*q|FP+GpL_!HQ$KJ(nw0!Ab?NKTN2$4;)ZDjM z7o4k$_6>FWYSFn`+)kC8RH+ep^W$?HCqEgtBX71^)5G_tJaj1&3dw{4kJbfw8iwRCP3tlQ5)z9j#R$ zaMF0bDfoDy$x#yF_}=v%`Zl=8I(r*jaGkwPUyOfg!_(xTYYar2Ug#em9&C!xH+|gE PV@*Hh==9GKX3T#97$*r? literal 0 HcmV?d00001 diff --git a/MLEBot/embed_frames/__pycache__/salary_card.cpython-313.pyc b/MLEBot/embed_frames/__pycache__/salary_card.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2300aae6a85683749b3857d64f6e56a88a459277 GIT binary patch literal 2726 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 E0_8yZHvj+t literal 0 HcmV?d00001 diff --git a/MLEBot/embed_frames/salary_card.py b/MLEBot/embed_frames/salary_card.py new file mode 100644 index 0000000..13fe40b --- /dev/null +++ b/MLEBot/embed_frames/salary_card.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +""" Minor League E-Sports Rocket League Player Salary Card +# Author: irox_rl +# Purpose: Salary Card embed function for ease of access / cleanliness +# Version 1.0.7 +# +# v1.0.7 - init +""" +import datetime +import discord +import os + + +def salary_card(member: {}, + player: {}, + franchise: {}, + player_tracker: {}): + embed = (discord.Embed( + color=discord.Color.from_str( + franchise['Primary Color']) if franchise else discord.Color.from_str(os.getenv('MLE_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: {datetime.datetime.now()}')) + embed.set_thumbnail(url=os.getenv('MLE_LOGO_URL') + if not franchise else franchise['Photo URL']) + 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 player_tracker: + embed.add_field(name='**Tracker Link**', + value=player_tracker['tracker'], inline=False) + return embed diff --git a/MLEBot/embed_frames/usage_card.py b/MLEBot/embed_frames/usage_card.py new file mode 100644 index 0000000..b4ee956 --- /dev/null +++ b/MLEBot/embed_frames/usage_card.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +""" Minor League E-Sports Rocket League Player Salary Card +# Author: irox_rl +# Purpose: Salary Card embed function for ease of access / cleanliness +# Version 1.0.7 +# +# v1.0.7 - init +""" +import datetime +import discord + + +def usage_card(franchise: {}, + players: {}): + embed = (discord.Embed( + color=discord.Color.from_str(franchise['Primary Color']), + title=f"**{franchise['Franchise']} 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: {datetime.datetime.now()}')) + embed.set_thumbnail(url=franchise['Photo URL']) + + embed.add_field(name='**Season Slot Allowances**', + value='`Doubles: 6 | Standard: 8 | Total: 12` ', + inline=False) + + if players['PL']: + embed.add_field( + name='Premier', + value='\n'.join([__player_usage_string__(x) + for x in players['PL']]), + inline=False) + + if players['ML']: + embed.add_field( + name='Master', + value='\n'.join([__player_usage_string__(x) + for x in players['ML']]), + inline=False) + + if players['CL']: + embed.add_field( + name='Champion', + value='\n'.join([__player_usage_string__(x) + for x in players['CL']]), + inline=False) + + if players['AL']: + embed.add_field( + name='Academy', + value='\n'.join([__player_usage_string__(x) + for x in players['AL']]), + inline=False) + + if players['FL']: + embed.add_field( + name='Foundation', + value='\n'.join([__player_usage_string__(x) + for x in players['FL']]), + inline=False) + + return embed + + +def __player_usage_string__(player): + return f"`{player['player']['slot'].removeprefix('PLAYER')} | 2s: {player['usage']['doubles_uses']} | 3s: {player['usage']['standard_uses']} | Total: {player['usage']['total_uses']} | {player['player']['name']}`" diff --git a/MLEBot/enums.py b/MLEBot/enums.py index 34fdbcc..c41e907 100644 --- a/MLEBot/enums.py +++ b/MLEBot/enums.py @@ -2,20 +2,20 @@ """ Minor League E-Sports Enumerations # Author: irox_rl # Purpose: Host MLE related enumerations to be used throughout this project -# Version 1.0.2 +# Version 1.0.3 +# +# Changelog: +# 1.0.3 - linting my ass off - enums to UPPER_CASE """ -# 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 + PREMIER_LEAGUE = 1 + MASTER_LEAGUE = 2 + CHAMPION_LEAGUE = 3 + ACADEMY_LEAGUE = 4 + FOUNDATION_LEAGUE = 5 diff --git a/MLEBot/franchise.py b/MLEBot/franchise.py index a635b28..4a38f6a 100644 --- a/MLEBot/franchise.py +++ b/MLEBot/franchise.py @@ -2,20 +2,18 @@ """ Minor League E-Sports Franchise # Author: irox_rl # Purpose: General Functions of a League Franchise -# Version 1.0.6 +# Version 1.0.7 # +# Changelog: +# v1.0.7 - lintingggggggg. this file is a WIP, mostly unused. considering deletion as restructuring occurs # v1.0.6 - include salary caps until sprocket does. """ +import discord -# local imports # -from enums import * -from team import Team +from .enums import LeagueEnum +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 @@ -43,29 +41,25 @@ def __init__(self, self.franchise_name = franchise_name self.premier_league = Team(self.guild, self, - LeagueEnum.Premier_League) + LeagueEnum.PREMIER_LEAGUE) self.master_league = Team(self.guild, self, - LeagueEnum.Master_League) + LeagueEnum.MASTER_LEAGUE) self.champion_league = Team(self.guild, self, - LeagueEnum.Champion_League) + LeagueEnum.CHAMPION_LEAGUE) self.academy_league = Team(self.guild, self, - LeagueEnum.Academy_League) + LeagueEnum.ACADEMY_LEAGUE) self.foundation_league = Team(self.guild, self, - LeagueEnum.Foundation_League) + LeagueEnum.FOUNDATION_LEAGUE) self._sprocket_team: {} = None self._sprocket_members: [{}] = [] self._sprocket_players: [{}] = [] @property - def all_members(self) -> [[], - [], - [], - [], - []]: + def all_members(self) -> list[list]: """ return a list containing all lists of members from each team in the franchise """ lst = [] @@ -101,8 +95,10 @@ def teams(self) -> [Team]: 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_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( @@ -112,40 +108,31 @@ def build_sprocket_data(self): async def get_team_eligibility(self, team: LeagueEnum): - if team == LeagueEnum.Premier_League and self.premier_league: + 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: + 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: + 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: + 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: + 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): + async def init(self) -> None: """ 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 + **returns**: None\n """ 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) + _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!', @@ -159,11 +146,13 @@ 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 = 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() diff --git a/MLEBot/lo_commands.py b/MLEBot/lo_commands.py index b55aae7..efda54c 100644 --- a/MLEBot/lo_commands.py +++ b/MLEBot/lo_commands.py @@ -6,10 +6,6 @@ # # v1.0.6 - Include slash commands """ - -# local imports # - -# non-local imports # import discord from discord import app_commands from discord.ext import commands @@ -96,7 +92,7 @@ async def ondo(self, description='opens door...') @app_commands.default_permissions() async def rexton(self, - interaction: discord.Interaction): + 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', diff --git a/MLEBot/member.py b/MLEBot/member.py index de8d917..92244eb 100644 --- a/MLEBot/member.py +++ b/MLEBot/member.py @@ -4,15 +4,9 @@ # 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 # +from .enums import LeagueEnum +from . import roles import discord -from discord.ext import commands class Member: @@ -71,15 +65,15 @@ def __get_league_role__(member: discord.Member) -> LeagueEnum | None: else returns None """ for role in member.roles: if role.name == roles.PREMIER_LEAGUE: - return LeagueEnum.Premier_League + return LeagueEnum.PREMIER_LEAGUE if role.name == roles.MASTER_LEAGUE: - return LeagueEnum.Master_League + return LeagueEnum.MASTER_LEAGUE if role.name == roles.CHAMPION_LEAGUE: - return LeagueEnum.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 + return LeagueEnum.FOUNDATION_LEAGUE def __update_from_sprocket_players__(self, sprocket_players: {}) -> None: diff --git a/MLEBot/mle_bot.py b/MLEBot/mle_bot.py index 66afd84..d33010c 100644 --- a/MLEBot/mle_bot.py +++ b/MLEBot/mle_bot.py @@ -1,26 +1,20 @@ """ Minor League E-Sports Bot # Author: irox_rl # Purpose: General Functions of a League Franchise summarized in bot fashion! -# Version 1.1.0 +# Version 1.1.1 # +# v1.1.1 - linting my ass off oh my god # 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 os import discord from discord.ext import commands as disco_commands -import dotenv -import os +from PyDiscoBot.bot import Bot +from .lo_commands import LoCommands +from .mle_commands import MLECommands +from .roles import ALL_TEAMS +from .task_roster import Task_Roster +from .task_sprocket import Task_Sprocket class MLEBot(Bot): @@ -32,7 +26,7 @@ def __init__(self, command_cogs: [disco_commands.Cog]): super().__init__(command_prefix=command_prefix, bot_intents=bot_intents, - command_cogs=command_cogs) + command_cogs=command_cogs) self._sprocket = Task_Sprocket(self) self._roster = Task_Roster(self) self._guild_ids: [{}] = [] @@ -51,8 +45,7 @@ def sprocket(self) -> Task_Sprocket | None: async def build_guilds(self): self._guild_ids.clear() - dotenv.load_dotenv() - for team in roles.ALL_TEAMS: + for team in ALL_TEAMS: try: _id = os.getenv(team).upper() self._guild_ids.append({'team': team, @@ -104,20 +97,3 @@ async def on_ready(self, 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 index a7e98a7..b014a8b 100644 --- a/MLEBot/mle_commands.py +++ b/MLEBot/mle_commands.py @@ -2,22 +2,21 @@ """ Minor League E-Sports Bot Commands # Author: irox_rl # Purpose: General Functions and Commands -# Version 1.0.6 +# Version 1.0.7 # +# v1.0.6 - revival after some sprocket updates and life getting in the way... # 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 +from .embed_frames.salary_card import salary_card +from .embed_frames.usage_card import usage_card +from .enums import * +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 @@ -30,86 +29,40 @@ def __init__(self, master_bot): self.bot = master_bot + @property + def sprocket(self): + return self.bot.sprocket + + @property + def sprocket_data(self): + return self.bot.sprocket.data + 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 + name: str): + member = self.sprocket.get_member_from_name_str(name, try_match=True) + if not member: + return await self.bot.send_notification(interaction, + f'mle member "{name}" not found in sprocket `Members` dataset') + player = self.sprocket.get_player_from_member(member) + if not player: + return await self.bot.send_notification(interaction, + f'mle member "{name}" not found in sprocket `Players` dataset') - # 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 + franchise = self.sprocket.get_franchise_from_player(player) + player_tracker = self.sprocket.get_playerTracker_from_member(member) - # 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) + embed = salary_card(member, + player, + franchise, + player_tracker) - 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) + await interaction.response.send_message(embed=embed) @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() @@ -162,12 +115,13 @@ async def rebuild(self, 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='Scrim Points', + value='current_scrim_points'), app_commands.Choice(name='Name', value='name') - ]) + ]) @app_commands.default_permissions() async def query(self, interaction: discord.Interaction, @@ -195,11 +149,11 @@ async def query(self, _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]) + '_', ' ')], key=lambda x: x[sorting], reverse=True) 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: discord.Embed = self.bot.default_embed('**Filtered Players**\n\n', + '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 @@ -278,7 +232,8 @@ async def runroster(self, as_followup=True) return - _channel = next((x for x in interaction.guild.channels if x.id.__str__() == str(_channel_id)), None) + _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!', @@ -296,11 +251,12 @@ async def runroster(self, as_followup=True) @app_commands.command(name='salary', - description='Get salary and extra data about yourself from provided sprocket data.') + description='Get salary and extra data about yourself from Sprocket.') @app_commands.default_permissions() async def salary(self, interaction: discord.Interaction): - await self.__local_lookup__(interaction) + member = self.bot.sprocket.get_member_from_interaction(interaction) + await self.__local_lookup__(interaction, '' if member is None else member['name']) @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') @@ -322,62 +278,46 @@ async def seasonstats(self, 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) + franchise = self.bot.sprocket.get_franchise_from_interaction( + interaction) + if not franchise: + return await self.bot.send_notification(interaction, + 'Could not resolve this franchise from sprocket data!', + as_followup=True) - self.__show_usage_league__(sorted(_fl_players, key=lambda _p: _p['slot']), - embed, - 'Foundation', - SALARY_CAP_FL) + players = self.bot.sprocket.get_players_from_franchise(franchise, + as_dict=True) + if not players: + return await self.bot.send_notification(interaction, + 'Could not get players for this franchise from sprocket!', + as_followup=True) + + complex_players = { + 'players': players['players'], + 'PL': [{ + 'player': x, + 'usage': self.bot.sprocket.get_role_usage_from_player(x), + } for x in players['PL']] if players['PL'] else None, + 'ML': [{ + 'player': x, + 'usage': self.bot.sprocket.get_role_usage_from_player(x), + } for x in players['ML']] if players['ML'] else None, + 'CL': [{ + 'player': x, + 'usage': self.bot.sprocket.get_role_usage_from_player(x), + } for x in players['CL']] if players['CL'] else None, + 'AL': [{ + 'player': x, + 'usage': self.bot.sprocket.get_role_usage_from_player(x), + } for x in players['AL']] if players['AL'] else None, + 'FL': [{ + 'player': x, + 'usage': self.bot.sprocket.get_role_usage_from_player(x), + } for x in players['FL']] if players['FL'] else None, + } + + embed = usage_card.usage_card(franchise, + complex_players) await interaction.followup.send(embed=embed) @@ -406,14 +346,15 @@ async def teameligibility(self, 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 + franchise = self.bot.sprocket.get_franchise_from_interaction( + interaction) + if not franchise: + return await self.bot.send_notification(interaction, + 'Could not resolve this franchise from sprocket data!', + as_followup=True) - _players = [x for x in self.bot.sprocket.data['sprocket_players'] if x['franchise'] == _team['name']] + _players = [x for x in self.bot.sprocket.data['sprocket_players'] + if x['franchise'] == franchise['Franchise']] if not _players: await self.bot.send_notification(interaction, 'Could not get players for this franchise from sprocket!', @@ -429,9 +370,9 @@ async def teameligibility(self, 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']) + f"{_league_text} {franchise['Franchise']} Eligibility Information", + color=discord.Color.from_str(franchise['Primary Color'])) + embed.set_thumbnail(url=franchise['Photo URL']) ljust_limit = 8 @@ -440,7 +381,7 @@ async def teameligibility(self, 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~", + f"`{'Until:'.ljust(ljust_limit)}` {_p['Eligible Until'].__str__()}", inline=True) await interaction.followup.send(embed=embed) @@ -454,12 +395,16 @@ async def teaminfo(self, _team_name: str): if not _team_name: - raise ReportableError('You must provide a team when running this command!') + 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) + _team = next( + (x for x in self.bot.sprocket.data['sprocket_teams'] if x['Franchise'].lower( + ) == _team_name.lower()), + None) if not _team: - raise ReportableError(f'Could not find team `{_team_name}` in sprocket data base!\nPlease try again!') + raise ReportableError( + f'Could not find team `{_team_name}` in sprocket data base!\nPlease try again!') embed = team.get_mle_franchise_embed(_team) @@ -474,25 +419,30 @@ async def teaminfo(self, inline=False) embed.add_field(name='**General Manager**', - value=f"\n".join([f"`{gm['name']}`" for gm in _team_players['gms']]), + 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']]), + 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']]), + 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']]), + 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) + 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, @@ -551,22 +501,6 @@ def __team_info_league__(self, 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 @@ -592,15 +526,15 @@ def get_league_enum_by_short_text(league: str): if not league: return None if league.lower() == 'pl': - _league_enum = LeagueEnum.Premier_League + _league_enum = LeagueEnum.PREMIER_LEAGUE elif league.lower() == 'ml': - _league_enum = LeagueEnum.Master_League + _league_enum = LeagueEnum.MASTER_LEAGUE elif league.lower() == 'cl': - _league_enum = LeagueEnum.Champion_League + _league_enum = LeagueEnum.CHAMPION_LEAGUE elif league.lower() == 'al': _league_enum = LeagueEnum.Academy_League elif league.lower() == 'fl': - _league_enum = LeagueEnum.Foundation_League + _league_enum = LeagueEnum.FOUNDATION_LEAGUE else: _league_enum = None return _league_enum diff --git a/MLEBot/roles.py b/MLEBot/roles.py index ce77c42..9ca0bcf 100644 --- a/MLEBot/roles.py +++ b/MLEBot/roles.py @@ -6,7 +6,7 @@ """ # local imports # -from enums import LeagueEnum +from .enums import LeagueEnum # non-local imports # import copy @@ -247,15 +247,15 @@ def get_role_by_name(guild: discord.Guild, name: str) -> discord.Role | None: def get_role_by_league(self, league: LeagueEnum): match league: - case LeagueEnum.Premier_League: + case LeagueEnum.PREMIER_LEAGUE: return self.premier - case LeagueEnum.Master_League: + case LeagueEnum.MASTER_LEAGUE: return self.master - case LeagueEnum.Champion_League: + case LeagueEnum.CHAMPION_LEAGUE: return self.champion case LeagueEnum.Academy_League: return self.academy - case LeagueEnum.Foundation_League: + case LeagueEnum.FOUNDATION_LEAGUE: return self.foundation 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/task_roster.py b/MLEBot/task_roster.py index 98db676..75d6ed8 100644 --- a/MLEBot/task_roster.py +++ b/MLEBot/task_roster.py @@ -6,18 +6,7 @@ """ 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 diff --git a/MLEBot/task_sprocket.py b/MLEBot/task_sprocket.py index d8464fc..945872b 100644 --- a/MLEBot/task_sprocket.py +++ b/MLEBot/task_sprocket.py @@ -8,10 +8,12 @@ from PyDiscoBot import err # local imports # -from sprocket_data_link import SprocketDataLink +from .sprocket_data_link import SprocketDataLink # non-local imports # import datetime +import difflib +import discord import pickle @@ -141,6 +143,66 @@ def __get_next_run_time__(self): seconds=(0 - self._last_time_ran.second), days=1) return + def get_franchise_from_interaction(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.data['sprocket_teams'] if x['Franchise'].upper() == known_guild['team'].upper()), + None) + + def get_franchise_from_player(self, + player: {}): + return next((x for x in self.data['sprocket_teams'] if x['Franchise'] == player['franchise']), None) + + def get_member_from_interaction(self, + interaction: discord.Interaction): + return next((x for x in self.data['sprocket_members'] if x['discord_id'] == str(interaction.user.id.__str__())), + None) + + def get_member_from_name_str(self, + name: str, + try_match: bool = False): + member = next((x for x in self.data['sprocket_members'] if x['name'].lower() == name.lower()), None) + if not member and try_match: + matches = difflib.get_close_matches(name, + [x['name'] for x in self.data['sprocket_members']], + 1) + if matches: + member = next((x for x in self.data['sprocket_members'] if x['name'].lower() == matches[0].lower()), + None) + return member + + def get_player_from_member(self, + member: {}): + return next((x for x in self.data['sprocket_players'] if x['member_id'] == member['member_id']), None) + + def get_playerTracker_from_member(self, + member: {}): + return next((x for x in self.data['sprocket_trackers'] if x['mleid'] == member['mle_id']), None) + + def get_players_from_franchise(self, + franchise: {}, + as_dict: bool = False): + players = [x for x in self.data['sprocket_players'] if x['franchise'] == franchise['Franchise']] + if not as_dict: + return players + return { + 'players': players, + 'PL': [x for x in players if x['skill_group'] == 'Premier League' and x['slot'] != 'NONE'], + 'ML': [x for x in players if x['skill_group'] == 'Master League' and x['slot'] != 'NONE'], + 'CL': [x for x in players if x['skill_group'] == 'Champion League' and x['slot'] != 'NONE'], + 'AL': [x for x in players if x['skill_group'] == 'Academy League' and x['slot'] != 'NONE'], + 'FL': [x for x in players if x['skill_group'] == 'Foundation League' and x['slot'] != 'NONE'], + } + + def get_role_usage_from_player(self, + player: {}): + return next((x for x in self.data['role_usages'] if + x['role'] == player['slot'] + and x['team_name'] == player['franchise'] + and x['league'].lower() in player['skill_group'].lower()), None) + def load(self): try: with open(self._file_name, 'rb') as f: # Open save file diff --git a/MLEBot/team.py b/MLEBot/team.py index 9c22550..e6211e3 100644 --- a/MLEBot/team.py +++ b/MLEBot/team.py @@ -8,8 +8,8 @@ from PyDiscoBot import channels, err # local imports # -from member import Member -from enums import LeagueEnum +from .member import Member +from .enums import LeagueEnum # non-local imports # import os @@ -628,15 +628,15 @@ def get_league_text(league: LeagueEnum) -> str | None: """ Get text representation of League enumeration """ match league: - case LeagueEnum.Premier_League: + case LeagueEnum.PREMIER_LEAGUE: return "Premier League" - case LeagueEnum.Master_League: + case LeagueEnum.MASTER_LEAGUE: return "Master League" - case LeagueEnum.Champion_League: + case LeagueEnum.CHAMPION_LEAGUE: return "Champion League" case LeagueEnum.Academy_League: return "Academy League" - case LeagueEnum.Foundation_League: + case LeagueEnum.FOUNDATION_LEAGUE: return "Foundation League" @@ -644,15 +644,15 @@ def get_league_text_short(league) -> str | None: """ Get shorthand string representation of League enumeration """ match league: - case LeagueEnum.Premier_League: + case LeagueEnum.PREMIER_LEAGUE: return "PL" - case LeagueEnum.Master_League: + case LeagueEnum.MASTER_LEAGUE: return "ML" - case LeagueEnum.Champion_League: + case LeagueEnum.CHAMPION_LEAGUE: return "CL" case LeagueEnum.Academy_League: return "AL" - case LeagueEnum.Foundation_League: + case LeagueEnum.FOUNDATION_LEAGUE: return "FL" @@ -660,7 +660,7 @@ 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']] + _all = [x for x in _sprocket_data['sprocket_players'] if x['franchise'] == _team['Franchise']] return { 'all': _all, 'fm': next((x for x in _all if x['Franchise Staff Position'] == 'Franchise Manager'), None), @@ -678,8 +678,8 @@ def get_defined_team_players(_team: {}, def get_mle_franchise_embed(_team: {}) -> discord.Embed: - embed = (discord.Embed(color=discord.Color.from_str(_team['primary_color']), - title=f"{_team['name']} Roster") + embed = (discord.Embed(color=discord.Color.from_str(_team['Primary Color']), + title=f"{_team['Franchise']} Roster") .set_footer(text=f"Generated: {datetime.datetime.now().strftime('%c')}")) - embed.set_thumbnail(url=_team['logo_img_link']) + embed.set_thumbnail(url=_team['Photo URL']) return embed From 66da0aeec78f7b7ddae2b3f172f51771ffe5ff84 Mon Sep 17 00:00:00 2001 From: Brian LaFond <52360893+iroxusux@users.noreply.github.com> Date: Sun, 30 Mar 2025 15:29:50 -0400 Subject: [PATCH 2/7] restructure, akin to PyDiscoBot. stable and running as "Barkvis". When pulled into main, will distribute to Roxanne. --- .idea/.gitignore | 8 - .idea/MLEBot.iml | 12 - .idea/inspectionProfiles/Project_Default.xml | 28 - .../inspectionProfiles/profiles_settings.xml | 6 - .idea/material_theme_project_new.xml | 12 - .idea/misc.xml | 7 - .idea/modules.xml | 8 - .idea/vcs.xml | 7 - .vscode/launch.json | 10 +- MLEBot/__init__.py | 6 +- MLEBot/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 264 bytes MLEBot/__pycache__/mle_bot.cpython-311.pyc | Bin 0 -> 6332 bytes MLEBot/__pycache__/mlebot.cpython-311.pyc | Bin 0 -> 4714 bytes MLEBot/__pycache__/roles.cpython-311.pyc | Bin 0 -> 8145 bytes MLEBot/embed_frames/__init__.py | 15 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 514 bytes .../__pycache__/card.cpython-311.pyc | Bin 0 -> 1406 bytes .../__pycache__/salary_card.cpython-311.pyc | Bin 3189 -> 2588 bytes .../teameligibility_card.cpython-311.pyc | Bin 0 -> 2994 bytes .../__pycache__/teaminfo_card.cpython-311.pyc | Bin 0 -> 7955 bytes .../__pycache__/usage_card.cpython-311.pyc | Bin 4284 -> 4241 bytes MLEBot/embed_frames/card.py | 32 + MLEBot/embed_frames/salary_card.py | 90 +-- MLEBot/embed_frames/teameligibility_card.py | 44 ++ MLEBot/embed_frames/teaminfo_card.py | 107 +++ MLEBot/embed_frames/usage_card.py | 99 ++- MLEBot/enums.py | 21 - MLEBot/franchise.py | 160 ---- MLEBot/lo_commands.py | 120 --- MLEBot/member.py | 172 ----- MLEBot/mle_bot.py | 99 --- MLEBot/mle_commands.py | 540 -------------- MLEBot/mlebot.py | 84 +++ MLEBot/roles.py | 277 ------- MLEBot/services/__init__.py | 12 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 432 bytes .../__pycache__/const.cpython-311.pyc | Bin 0 -> 4343 bytes MLEBot/services/cmds/__init__.py | 12 + .../cmds/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 420 bytes MLEBot/services/cmds/lo/__init__.py | 36 + .../lo/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 968 bytes .../lo/__pycache__/achilles.cpython-311.pyc | Bin 0 -> 1457 bytes .../cmds/lo/__pycache__/adi.cpython-311.pyc | Bin 0 -> 1299 bytes .../cmds/lo/__pycache__/bw.cpython-311.pyc | Bin 0 -> 1182 bytes .../cmds/lo/__pycache__/haim.cpython-311.pyc | Bin 0 -> 1401 bytes .../cmds/lo/__pycache__/hoos.cpython-311.pyc | Bin 0 -> 1224 bytes .../cmds/lo/__pycache__/kd.cpython-311.pyc | Bin 0 -> 1130 bytes .../lo/__pycache__/kunics.cpython-311.pyc | Bin 0 -> 1213 bytes .../cmds/lo/__pycache__/maple.cpython-311.pyc | Bin 0 -> 1208 bytes .../cmds/lo/__pycache__/ondo.cpython-311.pyc | Bin 0 -> 1414 bytes .../lo/__pycache__/rexton.cpython-311.pyc | Bin 0 -> 1461 bytes .../cmds/lo/__pycache__/riz.cpython-311.pyc | Bin 0 -> 1140 bytes .../lo/__pycache__/soviet.cpython-311.pyc | Bin 0 -> 1495 bytes .../cmds/lo/__pycache__/zb.cpython-311.pyc | Bin 0 -> 1217 bytes MLEBot/services/cmds/lo/achilles.py | 15 + MLEBot/services/cmds/lo/adi.py | 16 + MLEBot/services/cmds/lo/bw.py | 15 + MLEBot/services/cmds/lo/haim.py | 16 + MLEBot/services/cmds/lo/hoos.py | 16 + MLEBot/services/cmds/lo/kd.py | 15 + MLEBot/services/cmds/lo/kunics.py | 19 + MLEBot/services/cmds/lo/maple.py | 15 + MLEBot/services/cmds/lo/ondo.py | 16 + MLEBot/services/cmds/lo/rexton.py | 15 + MLEBot/services/cmds/lo/riz.py | 15 + MLEBot/services/cmds/lo/soviet.py | 18 + MLEBot/services/cmds/lo/zb.py | 14 + MLEBot/services/cmds/mle/__init__.py | 26 + .../mle/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 769 bytes .../mle/__pycache__/lookup.cpython-311.pyc | Bin 0 -> 2011 bytes .../mle/__pycache__/query.cpython-311.pyc | Bin 0 -> 6475 bytes .../mle/__pycache__/rebuild.cpython-311.pyc | Bin 0 -> 1803 bytes .../mle/__pycache__/salary.cpython-311.pyc | Bin 0 -> 1920 bytes .../mle/__pycache__/showusage.cpython-311.pyc | Bin 0 -> 1909 bytes .../teameligibility.cpython-311.pyc | Bin 0 -> 3035 bytes .../mle/__pycache__/teaminfo.cpython-311.pyc | Bin 0 -> 2187 bytes .../updatesprocket.cpython-311.pyc | Bin 0 -> 1668 bytes MLEBot/services/cmds/mle/lookup.py | 29 + MLEBot/services/cmds/mle/query.py | 91 +++ MLEBot/services/cmds/mle/rebuild.py | 25 + MLEBot/services/cmds/mle/salary.py | 27 + MLEBot/services/cmds/mle/showusage.py | 31 + MLEBot/services/cmds/mle/teameligibility.py | 43 ++ MLEBot/services/cmds/mle/teaminfo.py | 31 + MLEBot/services/cmds/mle/updatesprocket.py | 20 + MLEBot/services/cmds/rl/__init__.py | 10 + .../rl/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 249 bytes MLEBot/services/const.py | 175 +++++ MLEBot/services/sprocket/__init__.py | 8 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 318 bytes .../__pycache__/lookup.cpython-311.pyc | Bin 0 -> 10327 bytes MLEBot/services/sprocket/lookup.py | 179 +++++ MLEBot/sprocket_data_link.py | 34 - MLEBot/task_roster.py | 208 ------ MLEBot/task_sprocket.py | 267 ------- MLEBot/tasks/__init__.py | 7 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 274 bytes .../__pycache__/sprocket.cpython-311.pyc | Bin 0 -> 16285 bytes MLEBot/tasks/sprocket.py | 182 +++++ MLEBot/team.py | 685 ------------------ MLEBot/types/__init__.py | 19 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 678 bytes .../types/__pycache__/enums.cpython-311.pyc | Bin 0 -> 661 bytes .../__pycache__/franchise.cpython-311.pyc | Bin 0 -> 3538 bytes .../types/__pycache__/member.cpython-311.pyc | Bin 0 -> 880 bytes .../types/__pycache__/player.cpython-311.pyc | Bin 0 -> 682 bytes .../sprocket_links.cpython-311.pyc | Bin 0 -> 3415 bytes .../__pycache__/url_datalink.cpython-311.pyc | Bin 0 -> 4837 bytes MLEBot/types/enums.py | 11 + MLEBot/types/franchise.py | 74 ++ MLEBot/types/member.py | 11 + MLEBot/types/player.py | 10 + MLEBot/types/sprocket_links.py | 23 + MLEBot/types/url_datalink.py | 106 +++ rebuild.bat | 3 + 115 files changed, 1793 insertions(+), 2771 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/MLEBot.iml delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml delete mode 100644 .idea/material_theme_project_new.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml create mode 100644 MLEBot/__pycache__/__init__.cpython-311.pyc create mode 100644 MLEBot/__pycache__/mle_bot.cpython-311.pyc create mode 100644 MLEBot/__pycache__/mlebot.cpython-311.pyc create mode 100644 MLEBot/__pycache__/roles.cpython-311.pyc create mode 100644 MLEBot/embed_frames/__init__.py create mode 100644 MLEBot/embed_frames/__pycache__/__init__.cpython-311.pyc create mode 100644 MLEBot/embed_frames/__pycache__/card.cpython-311.pyc create mode 100644 MLEBot/embed_frames/__pycache__/teameligibility_card.cpython-311.pyc create mode 100644 MLEBot/embed_frames/__pycache__/teaminfo_card.cpython-311.pyc create mode 100644 MLEBot/embed_frames/card.py create mode 100644 MLEBot/embed_frames/teameligibility_card.py create mode 100644 MLEBot/embed_frames/teaminfo_card.py delete mode 100644 MLEBot/enums.py delete mode 100644 MLEBot/franchise.py delete mode 100644 MLEBot/lo_commands.py delete mode 100644 MLEBot/member.py delete mode 100644 MLEBot/mle_bot.py delete mode 100644 MLEBot/mle_commands.py create mode 100644 MLEBot/mlebot.py delete mode 100644 MLEBot/roles.py create mode 100644 MLEBot/services/__init__.py create mode 100644 MLEBot/services/__pycache__/__init__.cpython-311.pyc create mode 100644 MLEBot/services/__pycache__/const.cpython-311.pyc create mode 100644 MLEBot/services/cmds/__init__.py create mode 100644 MLEBot/services/cmds/__pycache__/__init__.cpython-311.pyc create mode 100644 MLEBot/services/cmds/lo/__init__.py create mode 100644 MLEBot/services/cmds/lo/__pycache__/__init__.cpython-311.pyc create mode 100644 MLEBot/services/cmds/lo/__pycache__/achilles.cpython-311.pyc create mode 100644 MLEBot/services/cmds/lo/__pycache__/adi.cpython-311.pyc create mode 100644 MLEBot/services/cmds/lo/__pycache__/bw.cpython-311.pyc create mode 100644 MLEBot/services/cmds/lo/__pycache__/haim.cpython-311.pyc create mode 100644 MLEBot/services/cmds/lo/__pycache__/hoos.cpython-311.pyc create mode 100644 MLEBot/services/cmds/lo/__pycache__/kd.cpython-311.pyc create mode 100644 MLEBot/services/cmds/lo/__pycache__/kunics.cpython-311.pyc create mode 100644 MLEBot/services/cmds/lo/__pycache__/maple.cpython-311.pyc create mode 100644 MLEBot/services/cmds/lo/__pycache__/ondo.cpython-311.pyc create mode 100644 MLEBot/services/cmds/lo/__pycache__/rexton.cpython-311.pyc create mode 100644 MLEBot/services/cmds/lo/__pycache__/riz.cpython-311.pyc create mode 100644 MLEBot/services/cmds/lo/__pycache__/soviet.cpython-311.pyc create mode 100644 MLEBot/services/cmds/lo/__pycache__/zb.cpython-311.pyc create mode 100644 MLEBot/services/cmds/lo/achilles.py create mode 100644 MLEBot/services/cmds/lo/adi.py create mode 100644 MLEBot/services/cmds/lo/bw.py create mode 100644 MLEBot/services/cmds/lo/haim.py create mode 100644 MLEBot/services/cmds/lo/hoos.py create mode 100644 MLEBot/services/cmds/lo/kd.py create mode 100644 MLEBot/services/cmds/lo/kunics.py create mode 100644 MLEBot/services/cmds/lo/maple.py create mode 100644 MLEBot/services/cmds/lo/ondo.py create mode 100644 MLEBot/services/cmds/lo/rexton.py create mode 100644 MLEBot/services/cmds/lo/riz.py create mode 100644 MLEBot/services/cmds/lo/soviet.py create mode 100644 MLEBot/services/cmds/lo/zb.py create mode 100644 MLEBot/services/cmds/mle/__init__.py create mode 100644 MLEBot/services/cmds/mle/__pycache__/__init__.cpython-311.pyc create mode 100644 MLEBot/services/cmds/mle/__pycache__/lookup.cpython-311.pyc create mode 100644 MLEBot/services/cmds/mle/__pycache__/query.cpython-311.pyc create mode 100644 MLEBot/services/cmds/mle/__pycache__/rebuild.cpython-311.pyc create mode 100644 MLEBot/services/cmds/mle/__pycache__/salary.cpython-311.pyc create mode 100644 MLEBot/services/cmds/mle/__pycache__/showusage.cpython-311.pyc create mode 100644 MLEBot/services/cmds/mle/__pycache__/teameligibility.cpython-311.pyc create mode 100644 MLEBot/services/cmds/mle/__pycache__/teaminfo.cpython-311.pyc create mode 100644 MLEBot/services/cmds/mle/__pycache__/updatesprocket.cpython-311.pyc create mode 100644 MLEBot/services/cmds/mle/lookup.py create mode 100644 MLEBot/services/cmds/mle/query.py create mode 100644 MLEBot/services/cmds/mle/rebuild.py create mode 100644 MLEBot/services/cmds/mle/salary.py create mode 100644 MLEBot/services/cmds/mle/showusage.py create mode 100644 MLEBot/services/cmds/mle/teameligibility.py create mode 100644 MLEBot/services/cmds/mle/teaminfo.py create mode 100644 MLEBot/services/cmds/mle/updatesprocket.py create mode 100644 MLEBot/services/cmds/rl/__init__.py create mode 100644 MLEBot/services/cmds/rl/__pycache__/__init__.cpython-311.pyc create mode 100644 MLEBot/services/const.py create mode 100644 MLEBot/services/sprocket/__init__.py create mode 100644 MLEBot/services/sprocket/__pycache__/__init__.cpython-311.pyc create mode 100644 MLEBot/services/sprocket/__pycache__/lookup.cpython-311.pyc create mode 100644 MLEBot/services/sprocket/lookup.py delete mode 100644 MLEBot/sprocket_data_link.py delete mode 100644 MLEBot/task_roster.py delete mode 100644 MLEBot/task_sprocket.py create mode 100644 MLEBot/tasks/__init__.py create mode 100644 MLEBot/tasks/__pycache__/__init__.cpython-311.pyc create mode 100644 MLEBot/tasks/__pycache__/sprocket.cpython-311.pyc create mode 100644 MLEBot/tasks/sprocket.py delete mode 100644 MLEBot/team.py create mode 100644 MLEBot/types/__init__.py create mode 100644 MLEBot/types/__pycache__/__init__.cpython-311.pyc create mode 100644 MLEBot/types/__pycache__/enums.cpython-311.pyc create mode 100644 MLEBot/types/__pycache__/franchise.cpython-311.pyc create mode 100644 MLEBot/types/__pycache__/member.cpython-311.pyc create mode 100644 MLEBot/types/__pycache__/player.cpython-311.pyc create mode 100644 MLEBot/types/__pycache__/sprocket_links.cpython-311.pyc create mode 100644 MLEBot/types/__pycache__/url_datalink.cpython-311.pyc create mode 100644 MLEBot/types/enums.py create mode 100644 MLEBot/types/franchise.py create mode 100644 MLEBot/types/member.py create mode 100644 MLEBot/types/player.py create mode 100644 MLEBot/types/sprocket_links.py create mode 100644 MLEBot/types/url_datalink.py create mode 100644 rebuild.bat 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 76e0ffd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index cec3284..4b9f245 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,13 @@ "skipFiles": [ "/**" ], - "program": "${workspaceFolder}\\test.js" + "program": "${workspaceFolder}\\test.js", + }, + { + "name": "Debug Unit Test", + "type": "debugpy", + "request": "launch", + "justMyCode": false, } - ] + ], } \ No newline at end of file diff --git a/MLEBot/__init__.py b/MLEBot/__init__.py index 323407d..7e20d91 100644 --- a/MLEBot/__init__.py +++ b/MLEBot/__init__.py @@ -1,11 +1,7 @@ -from .mle_bot import MLEBot -from .mle_commands import MLECommands -from .lo_commands import LoCommands +from .mlebot import MLEBot __version__ = "1.1.0" __all__ = ( "MLEBot", - "MLECommands", - "LoCommands", ) diff --git a/MLEBot/__pycache__/__init__.cpython-311.pyc b/MLEBot/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..08f0864c3264ce70f3519822474bb553d92d8d32 GIT binary patch literal 264 zcmZ3^%ge<81pD{ANDl+jk3k$5V1hC}O92_v8B!Qh7;_kM8KW2(8B&;{m{J&{m{XX8 z88lg5G6EH7GTvhI^>KB|FR5ZR)HBpG@Y7_u#g?0snv`Es#0*q$i#tBPEVZaOGe0jr z{uXz*Q}arSW85=KJW7*dAZEuv z42+47&&*Vss#X}l}2^| literal 0 HcmV?d00001 diff --git a/MLEBot/__pycache__/mle_bot.cpython-311.pyc b/MLEBot/__pycache__/mle_bot.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f0e7ed78862f90e5cbd1f0f5ba3594f47f537be GIT binary patch literal 6332 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 EFD4oq?*IS* literal 0 HcmV?d00001 diff --git a/MLEBot/__pycache__/roles.cpython-311.pyc b/MLEBot/__pycache__/roles.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f51a8b08bc434faf727d8052910286dd960b37e1 GIT binary patch literal 8145 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 literal 0 HcmV?d00001 diff --git a/MLEBot/embed_frames/__pycache__/card.cpython-311.pyc b/MLEBot/embed_frames/__pycache__/card.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8bed7a51c09946a915d2aae9167dcbae75ac9a38 GIT binary patch literal 1406 zcmaJ=&2Jk;6rc5e*z3k7F)96uU{t6bx5Yu?LL-ErsVPFPQ>E&~vSh7y#?D6j5oTwR z5^W?OID!-;P|blGDxm%g{1J)d!)i}R91xrq1ui}DW@0-LikR7b^WMBSZ{GX8H$Rq2 zMFgY2^ZW2m6`|jRFdLVq%n1YL86t=%F6x}FN=E@X=jJ@Mqk39LQ;;bqF^NibqQS51 z=Mf@0HKv?F^HjSw^A6smK2MdM!fbO;B!w^Zj!Bd!s9yX-Y`5#VR39+QqiM0}^(pz- zp)N^v?r@i;I-#M>QWFe(d)oE#m%>$>;{a7SR#XKw#7&d55T|<{Ll$G^|=r|i*v^x zHi|_I<0Cc<*Ci(;2_#+(IjgTvgQy~jkOr=~3&^#JfRA2C-ly2LsZ zMxrJ#Uf$nW?>-GF3%h;hSbq0GU`HO*GweQe_$N`n3xV7Zc-M1j9~SALCm%}K6@Ady z-AfCg>d|5~DJ&hXO$s+N73t;7 zK=0fGt5@Fms{f@kE+3a`lXC5-ToYDzCWY69)xtjpDpd||C6y~l(!Mp|L#(z3mXHyc71$%a{1Oz+VSPPr{@fG R??dI6(wP(K;y*-&|1V2gaV7u& literal 0 HcmV?d00001 diff --git a/MLEBot/embed_frames/__pycache__/salary_card.cpython-311.pyc b/MLEBot/embed_frames/__pycache__/salary_card.cpython-311.pyc index 2b8443765126c7ca28ff085bd9563db34ffcd368..b2dc696f3d7e62789662db2b36ad9a44e92e616d 100644 GIT binary patch literal 2588 zcmb7F&2JM&6yNp7X6)GcfDphTuoQ%FfEYp&kr1FMn8a0x5QbKysgkv4>@3;!ZfDn} zS!~IN9#B=OsniOj7Rd*cQst6U#gYG@>uL|tN|mUpO3f`2>V;F^?%EDfL`z0*XWqQ| zy_q-fy?OgnG#X~0{rc_S`CmAO`O}#+ny=2h8V2SOgBi?9jLgokEQ5X6FZpEuj34*_ z4oU$zI1>b(Bf*>xw|p6x;V?=w@z92|Nu0IANjXFCb&*JTgY`s6mdIRIpm@VM2{7CZ z0w8L0vO8uST%)xEg?H-&R$pNjr+fYZC$07|Ge)$chBx! z*|Fc%Ij}38gRV|&S32)_-+Z`?N`4wIp(;F{oo!oudrE$MXv>Nf$kWwkuC721uQv0T zWppRs>#Y*v|iN6YOFYF4&Xe*8%+X7%_euc_4A$dfl=s=MJ%m?v+- za#Sc}7etNhL^hY2V4VtJGu;WTFL>tOB6^V)-mn;pT2`UB-z_p=v3EbGw(vP{jHZ&T zm=g)+->}W__4V0-f*=#4gHJc^m@4FyzCPp3H9;5nyr3@-3Tm09Eib6Wj3j1x>;NRO zu&DKijm0z}ysS{d3p)Qv(_!xNrzaP5UDZYtiQL(&mf~T%D_f+L6!bYQOGSB3Rm6g>Z5)PdF@p1V zi8gNp=BJf;1KpTP^3!m?*X;algL7u7YfxOF>WbLrydzz78KI;k=EaOeuGpdLjgZ=*W(W-~tqZx_72`m2m3JllO+^!RQ7PDgTZ%;N zfF>!r(b3m;yB_N)a3X}Fr5@rqvO`pw^CD$)-bfK#kM4K6E~j%!sLctgN(z`dl{g-D zAvUYpth@!ZgSx20p+QW*TQ(x~OV}KwgM>9Z==^H1=KKYn)c4x!)YZ}1I~t+dY=(+L zVfLDmEz00>ZFWM`CySX`aOMY!J`29gz~JsN2d#*lIgbnON`WwmSGq>Sg5ga_92!cL&YL zX^$$&myxkbay<|-qYr9<2QZKIn1=?e*!ea3TWrLPjXb>n@c#49E0h0lOla@&sCgt& zJy-qc=?E|$t##GVu!V+yM|+kpn7xT=XZ6C<9$-A$-SQx4U$Ty#GkXU;TEjJT(LxuU z-7lK_`_*yt#F%yb60jb1SDXojGc5r*$S~Zl|4+P3{!i`Iy^eV0dfiTJSHnGoQZz literal 3189 zcmbtWO>7&-72YLx`6rQ58p)Pvi&@7%GNp)g692TUD%+uCNrpnok{uwj0&2;fC0APR z60<|sCe%VG3bY6kB!>VIkQ(5njsojoAeS6+@F52uSHcD+wkTkrx#UK}DGK=1H^Y#! zkff-g-Pt#9-pu>v{S4>v^KdxC!ShZ1yW(JgO)IfYj|I={%X z-m~aoy?4>8cr{KD;1l8VX`C8R{C^e}1;w+##RIi3rMET1#L_I1i&Z2|jxJP8Oe|^M zEUY5Z7SHMOZG@!-S(ou`DJ^3qbWFNbB_$K5Bn_Lla##n+ToqSL3#Fc4D4}v5DUw<> z3PdvvNrlajY$3^1CAm;QmL(;nf{tWE*9;f|%sU8Mbm{Af(~0w;V<5T7SZP$!42^`o zr@m)gUztK<*H>ZIxj_}nMxmrx$c?l_If7+y0~Z7qtrqb^=Y{X-3P?ZY2){eG(&5)U z_ypOV;8r~CL7nRUZ&0?xe-mT~$;kVvot!>x#IUh(Y3+&fadYmnpJ{Xc_j z-|;HGE&pAx_Fl*0y04A*e6IvZWOuwmtiKENUPtm7cgM38+-uDrgf*f5 zoEM$N+?vPg`vw0(doc_@M6_$yqpj|}^E?N8u;0fndW%!SO5`Weh(I)YwtDwQ<9}eh z#4vufyFWzqMKN9m3|{Hm7e+tBIIu5_gA8L}Ul@nLljjhW=qfvz*rOaac;(0o#deU{ z6WfOj#;gAqh8M?v@w^>d^D4)8QIr!uch2ChJ(;P6=df00H>jzb*bTlt zJDE#oXS4G)&+xD-7;+iadZmR5Ze6}JhH4HE*ES|(B1=V?ln{oSG=E#N+AAv6ysi}_ zg)V^WbJa?O77&ukCPtD>q#K=uZ1TNRB|<7zDw$MIpFWew!`+zIPrktmquq^jvC z+1419G0{}HK&)hIlVn9MA}gsG3c8gjk+L2S#l5arV4jFXBRX=s6l4{!R-qStt!oMy z2+IU1DJjmo0ztQk+f7}b%}!-=*XL(z!MT!2ObJ+6;H_f47Jzvv195o9FJFs$F}+4y z@usX-k?Ye~iKzMLnwbf>|80Go51AX7Rs>gMHQJ(nN%VO(lTIyNx1bC!<*_CkOA}_HT84O8 zOH&$|uI85@1#g+8-7)nXl;1M4mRecJ72r=oqH-Iuz_P-|cm#H!wZ{4f)YEnD-vKVt z|G|5YN6s|8e2>O|Sp1;;C~|x!a=g*q|FP+GpL_!HQ$KJ(nw0!Ab?NKTN2$4;)ZDjM z7o4k$_6>FWYSFn`+)kC8RH+ep^W$?HCqEgtBX71^)5G_tJaj1&3dw{4kJbfw8iwRCP3tlQ5)z9j#R$ zaMF0bDfoDy$x#yF_}=v%`Zl=8I(r*jaGkwPUyOfg!_(xTYYar2Ug#em9&C!xH+|gE PV@*Hh==9GKX3T#97$*r? diff --git a/MLEBot/embed_frames/__pycache__/teameligibility_card.cpython-311.pyc b/MLEBot/embed_frames/__pycache__/teameligibility_card.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b96740987945f96fed512ac4c1211f2df7e559e5 GIT binary patch literal 2994 zcmbVNU2Gf25#GDw9sfkh`eAK3l6x}j)?_S72^#;bWMO}2{ zv3tkXx{v@L4Adu!?Vvki39U7!U%I3%5f_khcJ%? zjn4@y0`MLzYMz|9A_6ZF5qpWW;laL}o)s_lCs8Euh#87-c3>fwCis#{H2jEbZTWH< zNo5p@AMvdX|0SyAGn=YGZ2v}wl{BJcizLH@+T$=V0{ouTVE30CILMXI`|VS3akRt# z4q@T0Q0B3xER+N+S|NnWg2}Y^2bQpR2kh*2ZpdB+aAie=hr8s?C*rc;GH z7KRU47{TFhrTv{3v=9I9+U8)}6UdkkAL&~9nx7+h^#8Vd^yPN%9>TBOAKL+J?RN0R zp%4G^ZLkDIGj4tLj+)o0e8ok+yfB$8=+rdiB|Wo6%>C_v3}cxnIe9@-vuaw^RC8ON zgL(?MN3%3*#9fXFcIrt!h1HB1iOWWTw#;>oI>H4_*(TKZx@h}=nNan-LbqFYEfKRw z^F7yL)yU`+$K0xq%O-QPXee1Cvx0XoviN9}GGJLla#GVx`I>wFc78*TMy<(t#Z=_1 zVr~*ju$lJ!H7>yZA?%9 zAeL4#TWL+PNc#0yM$b)cs2Z7SYfLKCR5z52X-u`QnJR$O#*~`JRJ6GK+Ux6(kxZv> zAO@^a48!lqX&}FahL%Mobp247D{&Zg0rx9@%WYDB>@RV@;XfP%uyA(J*itnum8E*I z@Ou;yY(6$^b2q3TOc@M^vRrw-G#6jHWuB6Po ziU9d{1=Ygo*1R+O@MnJ?Z!Er7TUEYXRBDULZAM!22?(PD(PA-hqcsG~#}FyZbz=lE z;jh{r10Y4A*F%Kp0Q?y&+%i1`^tmsgDY-OOW~}f1H@91I6U+d6Ei0({TJJ$KQcs4H+#><KSdIwpHS`D=wk0(WH9;bNyT4QRsT?sC%78-ID+TvKvmw|H>{ok= zYIaZogzc|wzk&f9>?yZJ?fs%*+Ja%yh@ZOO#9(KOrfO>Nt(bt1w&V&Mw&=pj_O=ZL zUG0>n=2X+>Qw4+R%B(opaC2pkd{c%fD--qv8bg2uK$3F=g)VcqM;n8Yd#^SI&(!*U z)S8urW?uq`$6pJ5Q1^ULS$HNP|8Q-1syRIKSm69`a!(PXjv(|)l}pb8C=|YZtrP?{c>WqJ~7vrn1j7~INb=Ro1>>| zqo*B#8;CtdK;W8zSY>f9IOHHf3^#qDj|P4{aA&o7?5rao{}B-KpJ%?DcNp0l7`fHA zH#A(iyw@LgkR+aT$t`|gu{J(kJq}Qe0@IofFMRhsuKN8iCO@0}Q?PzK(Kw!ft6;B1 zAMy{+{4LPC(;Peb>ARo2d+&06>|A5)9E6}F+)T_oxbfF3y<2-PNQ+(Reb(o~v9pb_vyQi`3E4lc#1w@%V#F_2671I9Kz`xy5-k;sXqCyH64H zG#BBvhrK^7oQvjmff()+L{9*gTu`|!!&Ps$ikyy%8kwQfAi{cxQ2+vI<+vuAs2!Y5 kbhLJGHqm(P;B=%{xp}Vowu9JzbK-;}Fs8M98FfAS4->}tuK)l5 literal 0 HcmV?d00001 diff --git a/MLEBot/embed_frames/__pycache__/teaminfo_card.cpython-311.pyc b/MLEBot/embed_frames/__pycache__/teaminfo_card.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8dc33c542b5c3e2d00989099a0acd444111a0c9c GIT binary patch literal 7955 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 VF_*mUJ%*`UIGso2|H+i9_Fuf^*$e;x literal 0 HcmV?d00001 diff --git a/MLEBot/embed_frames/__pycache__/usage_card.cpython-311.pyc b/MLEBot/embed_frames/__pycache__/usage_card.cpython-311.pyc index 16cc0aad62ad7b6e0d69752b5ad1a3b8f26fa202..8d60907546994ef7f264ad724df883c5a7d09965 100644 GIT binary patch literal 4241 zcmd5<&2JM&6rcU@*E%-jqaZ**OA!=@#!ge9ts8}=`GE3~B2rW}4GL?|IN7l4wPwbY z7)Od!4poIzTB#yZMU@i;(L;|M`v>$8DoeHY+6&x*0vAqwv+IvIKuc9B8124&vv20j zZ)WHH=Iu|hn1-Mn`|;k)!!SaBib}o6HDzr8lxqkhELq5wCM5}B87r1-E0YT7Dh^nx z9heM&9wdRBj6;{z$si72Mw1~NnLr8czHpV0Ty50O5IklQ3*VRgS7F;C(^-S!`(iw3 z6B(mMK09kN;zn|HH7i=zO9T`J?zMX$0hm-qb9Ea)+t$#3Kv=o1EJ#>gkjpX-lwt@i z$XxWT6&%E&8!+(g`ef0Jw4i{$Mt!c)njSAJ%WA#n`e%#BFpew-)?4d3)-I!`dmceI zpwaDmLv#aXd%H2?pQEh^Tdh*7&tTojbE0i~Cfe8rv<=TM5N+sRqK!Wj?WPTA8=hYv zS_lxoJm=bOekR)X4QLylUm#i$C0=<>v>nex+qnU4!}EVb%U`P_M5rv4<@HfqR&GF~ z-EKr1kVsdldeY20R6jwCnIh3gdnXDGqQTsU_Ec3lHAin#f~j?=o6O1YZLe=pL2S8O1&cnH}n~U&k{+W}MdFh1w?$H>|Y&R{uHpB5vpuCALYZD`_oEFL<;Q?zJ~T zTthR!b!XcbB;Mdgv2$Aa@LTyx#{{~CZb@#}wEvN&z2%-}oSONWTe2%B`&{WF6BMuO zkB<(gr_M0=##0$;8u_UaCtI{hp0lYVCO=xtOr1P2`k}+8;GD|9gNPGs8a9ee*p%;B ze8*&A{;@V-nT*3i76wZ@*O>+^Cu>-2Fxj+si%Io?SVXHx>K<&0s=n)yf_C=*vg>a5 zflBv*rp43NpbtvOu9^n`Zs3Ap6^R=>X)sRci&;7fha{qLAvS}kEfW==fp&rTr>X50 z4Mk|vEg;nfJ?Q4)5e@4gp8nk^i;56Ws8pPQZh)KIf?bUX z%ThO(6L}(I;t&9m(7i3_YR$l+yTU8w4)N*xr)@l zkW>xFzlnVv`+lz4zP;M9SNz+5+or z@UPR-FSyR8f-Tgn<8V`72;fLrg8wV6EQ5@e6_9OZ6=bX&02#R+g`6!u?y3wD8aH_A z#L#D>r`>=Ty>1)!GXpr833FqtmKyj~jf>pC*MmNn(5MG?W;ul7DuY_uB)G3ahCFxc<5Wr!Q rR7Ja1TCXbFzS4SCQTIygwHoY~Mx-TRM)7=#oRfmm$6$E6^rrR~rqhx* literal 4284 zcmd5d#t#&T$gQO6;57UAW3PvJ*Q^t0rz-|49zy#1~(#jn6?@?T)Oq zSG!_%<+Fv8GbPY)1n%0G1df0TCB2Xo4hntl!#(uT&IuwG4i4Hr^i9Dj1wZwhmG#d# z(-1C@k@lOJ@0249<#le3 zb8CK%S3Me11r22cRlF(8c~tLBG$*M1EQnF7t57D==Y#3Yv{D$W!P#nwy% zXW++(Y!Pf`u&gM=vhW~QbRrwNW&B9|-fFGyL+4>b z@ddiaKk~Puxz4ZgC0}DmO9M~_nkXMh?ejPU^VqsQSJVzQsD>Ve+oOT;oEmBRqd(ts z{u^p%2{!NZMcsFEmzN3D9$`DiJy3ML5Q_bKqB!89Xz}t=C=R|5itas89CA^#czG!l zJuieJx+jXmE{YZ}FNNaB3!&)U6GfkkqQ%QgpkO5T=7nNthH5#Nx=h`qMZUk^2~5@5 zqqq;x>ZXk^Tk-R@KI527Z7m}! zwl!G$k-TAs0MvEv*u2$gN3CUA^% z1e>hb#lTF~v`u{ZQW8|20@aJr{{C4YVc=Ld@-baEKL#!(7DOJqHeo`9#7f|w;59sJ z%Z3VspTIwb-Up^F>j`}P#5IiZ6krhyIY%6ES=I~0@oFxKJN!)2@y$?@(+J(>B&ETU zaQYdyb-}YW0OAf>Kx^n~=XI`?o!7Yb6aPE@_P$y406pNG?o_Hq^{E1TTPe$?nz4{d zJ>VTaKICvK7UOV?x6h9!<}X`tk<6#5CL8k;rc%fe!?xznY4-U-dVV@N`AgHTSMDX1 zf(s=_tofR21%i&}SGT3px@Ot%uJWVBz82GI-Be`V8jUyYeJn0^5wD{v;(AA#qJn?) z9}ns~`PKWIy{F2(rcc2pB@}Sbu<1 zy^j7B+JF1%x8Vb~b5FwWJPyB8y7J{G<)gpY44*BB&sM@`KL6R>kIG%KjqsV;x5{$| z#`&sG2v6`&5tGkc`Ckb|dOls;=$riW`SQS-Cj(=T2gWuB#>)fapo8zN#m(?kIXqSE zK%!I)f_NfyKNh<0zE@f*A33ohbZ-j7Wnp+j82(NO{PyB+E^hRktVAa(hjCRz;{KN*Lnu9M$&9jXRU;1C28h(auZXw?+YxOJ 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 + """ + 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(title, + descr, + fields, + color_str, + url_str) + + return embed diff --git a/MLEBot/embed_frames/salary_card.py b/MLEBot/embed_frames/salary_card.py index 13fe40b..bfddeda 100644 --- a/MLEBot/embed_frames/salary_card.py +++ b/MLEBot/embed_frames/salary_card.py @@ -1,44 +1,48 @@ -#!/usr/bin/env python -""" Minor League E-Sports Rocket League Player Salary Card -# Author: irox_rl -# Purpose: Salary Card embed function for ease of access / cleanliness -# Version 1.0.7 -# -# v1.0.7 - init -""" -import datetime -import discord -import os - - -def salary_card(member: {}, - player: {}, - franchise: {}, - player_tracker: {}): - embed = (discord.Embed( - color=discord.Color.from_str( - franchise['Primary Color']) if franchise else discord.Color.from_str(os.getenv('MLE_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: {datetime.datetime.now()}')) - embed.set_thumbnail(url=os.getenv('MLE_LOGO_URL') - if not franchise else franchise['Photo URL']) - 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 player_tracker: - embed.add_field(name='**Tracker Link**', - value=player_tracker['tracker'], inline=False) +from pydiscobot import EmbedField +from .card import mle_card +from ..types import Member + + +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 + """ + p = member.rl_player.player + m = member.member + tracker = member.rl_player.tracker + franchise = member.franchise + + title = f"**{m['name']} Sprocket Info**" + + descr = 'Data gathered by sprocket public data links.\n' + descr += 'See more at [sprocket links](https://f004.backblazeb2.com/file/sprocket-artifacts/public/pages/index.html)\n' + + eligible = "`Yes`" if p['current_scrim_points'] >= 30 else "`No`" + + fields = [ + EmbedField('MLE Name', f"`{m['name']}`", True), + EmbedField('MLE ID', f"`{m['mle_id']}`", True), + EmbedField('Salary', f"`{p['salary']}`", True), + EmbedField('League', f"`{p['skill_group']}`", True), + EmbedField('Scrim Pts', f"`{p['current_scrim_points']}`", True), + EmbedField('Eligible?', eligible, True), + EmbedField('Franchise', f"`{p['franchise']}`", True), + EmbedField('Staff?', f"`{p['Franchise Staff Position']}`", True), + EmbedField('Role', f"`{p['slot']}`", True), + ] + + if tracker: + fields.append(EmbedField('**Tracker Link**', tracker['tracker'])) + + embed = mle_card(title, descr, franchise, fields) + return embed diff --git a/MLEBot/embed_frames/teameligibility_card.py b/MLEBot/embed_frames/teameligibility_card.py new file mode 100644 index 0000000..4c6caad --- /dev/null +++ b/MLEBot/embed_frames/teameligibility_card.py @@ -0,0 +1,44 @@ +from pydiscobot import EmbedField +from .card import mle_card +from ..types import Franchise + + +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.franchise_meta['Franchise']} Slot Usage Info**" + descr = 'Data gathered by sprocket public data links.\n' + descr += 'See more at [sprocket links](https://f004.backblazeb2.com/file/sprocket-artifacts/public/pages/index.html)\n' + + fields = [ + EmbedField('**Season Slot Allowances**', + '`Doubles: 6 | Standard: 8 | Total: 12` ') + ] + + players = sorted([x for x in franchise.players_rl.all_players() if x.player['skill_group'] == league], + key=lambda x: x.player['slot']) + + 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.player['name']}**", + value=f"`{'Role:'.ljust(ljust_limit)}` {_p.player['slot']}\n" + f"`{'Salary:'.ljust(ljust_limit)}` {_p.player['salary']}\n" + f"`{'Points:'.ljust(ljust_limit)}` {str(_p.player['current_scrim_points'])}\n" + f"`{'Until:'.ljust(ljust_limit)}` {str(_p.player['Eligible Until'])}")) + + return mle_card(title, descr, franchise.franchise_meta, fields) diff --git a/MLEBot/embed_frames/teaminfo_card.py b/MLEBot/embed_frames/teaminfo_card.py new file mode 100644 index 0000000..560ed61 --- /dev/null +++ b/MLEBot/embed_frames/teaminfo_card.py @@ -0,0 +1,107 @@ +from pydiscobot import EmbedField +from .card import mle_card +from ..types import Franchise, PlayerRL +from ..services import const + + +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.franchise_meta['Franchise']} Roster" + descr = 'Data gathered by sprocket public data links.\n' + descr += 'See more at [sprocket links](https://f004.backblazeb2.com/file/sprocket-artifacts/public/pages/index.html)\n' + + 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(sorted(franchise.players_rl.pl, key=lambda _p: _p.player['slot']), + const.SPR_SG_PL, + const.SALARY_CAP_PL) + ml = _team_info(sorted(franchise.players_rl.ml, key=lambda _p: _p.player['slot']), + const.SPR_SG_ML, + const.SALARY_CAP_ML) + cl = _team_info(sorted(franchise.players_rl.cl, key=lambda _p: _p.player['slot']), + const.SPR_SG_CL, + const.SALARY_CAP_CL) + al = _team_info(sorted(franchise.players_rl.al, key=lambda _p: _p.player['slot']), + const.SPR_SG_AL, + const.SALARY_CAP_AL) + fl = _team_info(sorted(franchise.players_rl.fl, key=lambda _p: _p.player['slot']), + 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) diff --git a/MLEBot/embed_frames/usage_card.py b/MLEBot/embed_frames/usage_card.py index b4ee956..e693bb3 100644 --- a/MLEBot/embed_frames/usage_card.py +++ b/MLEBot/embed_frames/usage_card.py @@ -1,66 +1,65 @@ -#!/usr/bin/env python -""" Minor League E-Sports Rocket League Player Salary Card -# Author: irox_rl -# Purpose: Salary Card embed function for ease of access / cleanliness -# Version 1.0.7 -# -# v1.0.7 - init -""" -import datetime -import discord +from pydiscobot import EmbedField +from .card import mle_card +from ..types import Franchise, PlayerRL -def usage_card(franchise: {}, - players: {}): - embed = (discord.Embed( - color=discord.Color.from_str(franchise['Primary Color']), - title=f"**{franchise['Franchise']} 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: {datetime.datetime.now()}')) - embed.set_thumbnail(url=franchise['Photo URL']) +def usage_card(franchise: Franchise): + """Minor League E-Sports Rocket League + Usage Card - embed.add_field(name='**Season Slot Allowances**', - value='`Doubles: 6 | Standard: 8 | Total: 12` ', - inline=False) + Args: + franchise (dict): sprocket franchise + players (dict): sprocket players dictionary - if players['PL']: - embed.add_field( + Returns: + discord.Embed: team usage card + """ + title = f"**{franchise.franchise_meta['Franchise']} Slot Usage Info**" + descr = 'Data gathered by sprocket public data links.\n' + descr += 'See more at [sprocket links](https://f004.backblazeb2.com/file/sprocket-artifacts/public/pages/index.html)\n' + + fields = [ + EmbedField('**Season Slot Allowances**', + '`Doubles: 6 | Standard: 8 | Total: 12` ') + ] + + if franchise.players_rl.pl: + fields.append(EmbedField( name='Premier', - value='\n'.join([__player_usage_string__(x) - for x in players['PL']]), - inline=False) + value='\n'.join([_player_usage_string(x) + for x in franchise.players_rl.pl]))) - if players['ML']: - embed.add_field( + if franchise.players_rl.ml: + fields.append(EmbedField( name='Master', - value='\n'.join([__player_usage_string__(x) - for x in players['ML']]), - inline=False) + value='\n'.join([_player_usage_string(x) + for x in franchise.players_rl.ml]))) - if players['CL']: - embed.add_field( + if franchise.players_rl.cl: + fields.append(EmbedField( name='Champion', - value='\n'.join([__player_usage_string__(x) - for x in players['CL']]), - inline=False) + value='\n'.join([_player_usage_string(x) + for x in franchise.players_rl.cl]))) - if players['AL']: - embed.add_field( + if franchise.players_rl.al: + fields.append(EmbedField( name='Academy', - value='\n'.join([__player_usage_string__(x) - for x in players['AL']]), - inline=False) + value='\n'.join([_player_usage_string(x) + for x in franchise.players_rl.al]))) - if players['FL']: - embed.add_field( + if franchise.players_rl.fl: + fields.append(EmbedField( name='Foundation', - value='\n'.join([__player_usage_string__(x) - for x in players['FL']]), - inline=False) + value='\n'.join([_player_usage_string(x) + for x in franchise.players_rl.fl]))) - return embed + return mle_card(title, descr, franchise.franchise_meta, fields) -def __player_usage_string__(player): - return f"`{player['player']['slot'].removeprefix('PLAYER')} | 2s: {player['usage']['doubles_uses']} | 3s: {player['usage']['standard_uses']} | Total: {player['usage']['total_uses']} | {player['player']['name']}`" +def _player_usage_string(player: PlayerRL): + slot = player.player['slot'].removeprefix('PLAYER') + dbl_use = player.usage['doubles_uses'] + std_use = player.usage['standard_uses'] + ttl_use = player.usage['total_uses'] + name = player.player['name'] + return f"`{slot} 2s: {dbl_use} | 3s: {std_use} | All: {ttl_use} | {name}`" diff --git a/MLEBot/enums.py b/MLEBot/enums.py deleted file mode 100644 index c41e907..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.3 -# -# Changelog: -# 1.0.3 - linting my ass off - enums to UPPER_CASE -""" - -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/franchise.py b/MLEBot/franchise.py deleted file mode 100644 index 4a38f6a..0000000 --- a/MLEBot/franchise.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env python -""" Minor League E-Sports Franchise -# Author: irox_rl -# Purpose: General Functions of a League Franchise -# Version 1.0.7 -# -# Changelog: -# v1.0.7 - lintingggggggg. this file is a WIP, mostly unused. considering deletion as restructuring occurs -# v1.0.6 - include salary caps until sprocket does. -""" -import discord - -from .enums import LeagueEnum -from .team import Team - - -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) -> list[list]: - """ 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) -> None: - """ initialization method\n - **returns**: None\n - """ - 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 efda54c..0000000 --- a/MLEBot/lo_commands.py +++ /dev/null @@ -1,120 +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 -""" -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 92244eb..0000000 --- a/MLEBot/member.py +++ /dev/null @@ -1,172 +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 -""" -from .enums import LeagueEnum -from . import roles -import discord - - -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 d33010c..0000000 --- a/MLEBot/mle_bot.py +++ /dev/null @@ -1,99 +0,0 @@ -""" Minor League E-Sports Bot -# Author: irox_rl -# Purpose: General Functions of a League Franchise summarized in bot fashion! -# Version 1.1.1 -# -# v1.1.1 - linting my ass off oh my god -# v1.0.6 - server integration -""" -import os -import discord -from discord.ext import commands as disco_commands -from PyDiscoBot.bot import Bot -from .lo_commands import LoCommands -from .mle_commands import MLECommands -from .roles import ALL_TEAMS -from .task_roster import Task_Roster -from .task_sprocket import Task_Sprocket - - -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() - for team in 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() diff --git a/MLEBot/mle_commands.py b/MLEBot/mle_commands.py deleted file mode 100644 index b014a8b..0000000 --- a/MLEBot/mle_commands.py +++ /dev/null @@ -1,540 +0,0 @@ -#!/usr/bin/env python -""" Minor League E-Sports Bot Commands -# Author: irox_rl -# Purpose: General Functions and Commands -# Version 1.0.7 -# -# v1.0.6 - revival after some sprocket updates and life getting in the way... -# v1.0.6 - Include slash commands -""" -from PyDiscoBot import channels -from PyDiscoBot import Pagination, InteractionPagination -from PyDiscoBot import ReportableError -from .embed_frames.salary_card import salary_card -from .embed_frames.usage_card import usage_card -from .enums import * -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 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 - - @property - def sprocket(self): - return self.bot.sprocket - - @property - def sprocket_data(self): - return self.bot.sprocket.data - - async def __local_lookup__(self, - interaction: discord.Interaction, - name: str): - member = self.sprocket.get_member_from_name_str(name, try_match=True) - if not member: - return await self.bot.send_notification(interaction, - f'mle member "{name}" not found in sprocket `Members` dataset') - player = self.sprocket.get_player_from_member(member) - if not player: - return await self.bot.send_notification(interaction, - f'mle member "{name}" not found in sprocket `Players` dataset') - - franchise = self.sprocket.get_franchise_from_player(player) - player_tracker = self.sprocket.get_playerTracker_from_member(member) - - embed = salary_card(member, - player, - franchise, - player_tracker) - - await interaction.response.send_message(embed=embed) - - @staticmethod - def get_sprocket_player_team_info(sprocket_player) -> str | None: - return f"`{sprocket_player['slot'].removeprefix('PLAYER')} | {sprocket_player['salary']} | {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], reverse=True) - - if len(_players) == 0: - emb: discord.Embed = self.bot.default_embed('**Filtered Players**\n\n', - '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 Sprocket.') - @app_commands.default_permissions() - async def salary(self, - interaction: discord.Interaction): - member = self.bot.sprocket.get_member_from_interaction(interaction) - await self.__local_lookup__(interaction, '' if member is None else member['name']) - - @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() - franchise = self.bot.sprocket.get_franchise_from_interaction( - interaction) - if not franchise: - return await self.bot.send_notification(interaction, - 'Could not resolve this franchise from sprocket data!', - as_followup=True) - - players = self.bot.sprocket.get_players_from_franchise(franchise, - as_dict=True) - if not players: - return await self.bot.send_notification(interaction, - 'Could not get players for this franchise from sprocket!', - as_followup=True) - - complex_players = { - 'players': players['players'], - 'PL': [{ - 'player': x, - 'usage': self.bot.sprocket.get_role_usage_from_player(x), - } for x in players['PL']] if players['PL'] else None, - 'ML': [{ - 'player': x, - 'usage': self.bot.sprocket.get_role_usage_from_player(x), - } for x in players['ML']] if players['ML'] else None, - 'CL': [{ - 'player': x, - 'usage': self.bot.sprocket.get_role_usage_from_player(x), - } for x in players['CL']] if players['CL'] else None, - 'AL': [{ - 'player': x, - 'usage': self.bot.sprocket.get_role_usage_from_player(x), - } for x in players['AL']] if players['AL'] else None, - 'FL': [{ - 'player': x, - 'usage': self.bot.sprocket.get_role_usage_from_player(x), - } for x in players['FL']] if players['FL'] else None, - } - - embed = usage_card.usage_card(franchise, - complex_players) - - 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) - - franchise = self.bot.sprocket.get_franchise_from_interaction( - interaction) - if not franchise: - return await self.bot.send_notification(interaction, - 'Could not resolve this franchise from sprocket data!', - as_followup=True) - - _players = [x for x in self.bot.sprocket.data['sprocket_players'] - if x['franchise'] == franchise['Franchise']] - 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} {franchise['Franchise']} Eligibility Information", - color=discord.Color.from_str(franchise['Primary Color'])) - embed.set_thumbnail(url=franchise['Photo URL']) - - 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)}` {_p['Eligible Until'].__str__()}", - 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['Franchise'].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 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..d4220e0 --- /dev/null +++ b/MLEBot/mlebot.py @@ -0,0 +1,84 @@ +import os +import discord +from discord.ext import commands as disco_commands +from pydiscobot import Bot +from .services.cmds import Commands +from .services 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._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. + """ + self.logger.info('MLEBot on_ready...') + if self._admin_info.initialized: + self.logger.warning('already initialized!') + return + await super().on_ready(suppress_task) + await self._build_guilds() + self._sprocket.init() + await self.change_presence(activity=self.activity) + + async def on_task(self) -> None: + self.logger.info('MLEBot running on_task...') + await super().on_task() + await self._sprocket.run() diff --git a/MLEBot/roles.py b/MLEBot/roles.py deleted file mode 100644 index 9ca0bcf..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/services/__init__.py b/MLEBot/services/__init__.py new file mode 100644 index 0000000..4eefec1 --- /dev/null +++ b/MLEBot/services/__init__.py @@ -0,0 +1,12 @@ +from . import const +from .cmds import Commands +from .sprocket import lookup_franchise, lookup_rl + +__version__ = '1.1.1' + +__all__ = ( + 'const', + 'Commands', + 'lookup_franchise', + 'lookup_rl', +) diff --git a/MLEBot/services/__pycache__/__init__.cpython-311.pyc b/MLEBot/services/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..53d4a8133aa82f05db691ec2761b31f1214e4845 GIT binary patch literal 432 zcmZusy-EW?5Z=B27@~sM_y*Sm(pZRCB#IU)h}awp%jIs>Bl{C}?*fWN-oi%^pTbv2 z8MhK_vahKfXp70Z21W5q1mxQ^WM0s$H;_w^8;}}~%Q__?`qMi~WmAQ!>yUmpvR6}~J zQ<>cthS|nyqxqxXX+y@IcELe=HTX3I0GDyYqNQNHOyp)M+sF*H$_A|bJG4zoM@*YS zNlI-FXku8g?l&ic^ca#Uo@ z5K0SKl$5rI4~ytfsJ!CrNN<5<{abSdFmo}+Q?xT#uPNG_tk)EECjZsP=I*#T^ZceW K4(rq{aQy*lTXoU^ literal 0 HcmV?d00001 diff --git a/MLEBot/services/__pycache__/const.cpython-311.pyc b/MLEBot/services/__pycache__/const.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd665d788cb24928ba81672ecdaf6fd71b46dba5 GIT binary patch literal 4343 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 literal 0 HcmV?d00001 diff --git a/MLEBot/services/cmds/lo/__init__.py b/MLEBot/services/cmds/lo/__init__.py new file mode 100644 index 0000000..b19f90f --- /dev/null +++ b/MLEBot/services/cmds/lo/__init__.py @@ -0,0 +1,36 @@ +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 + + +__version__ = '1.1.1' + +__all__ = ( + 'Commands', +) + +Commands = [ + Achilles, + Adi, + Bw, + Haim, + Hoos, + Kd, + Kunics, + Maple, + Ondo, + Rexton, + Riz, + Soviet, + Zb, +] diff --git a/MLEBot/services/cmds/lo/__pycache__/__init__.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8eb9ddb114782ae2724614d828c54c90334bae9a GIT binary patch literal 968 zcmZvZ&rjPh6vv(4Y11?Kyu9y?^zetPfo{GRP^;%~lRM=;#WUst<2LO*5lZ}^XJ{{!GF zN>HLP#5JsGl69tY0~?ABW^xN#icMy72Rn)_=JFb@DYjXid)QO#um<`%<2vUTj$~TE{CD=Dgkf_PT;!=~4kGV86kR?3TBa|}H zkclDaOvp!F+PTsvnljO6BN0gOqG(D)PO~(MmQEDKj75=XLFqfX2ag`!AHNJQbLa_2 zg2rk1CL2#UNsByuM~jomC_MY{_9!bt&d3M?bWZ7*Mg3aZsqB&?FFh>iMAG1TX#-n z3!Jb{;nSY~Z(rFc=*_*AseAg|S=Eu{m!?yic4@lG0P3Y#Qw#tl@b+q_+0f^%oMIP# F-5)(r2A==` literal 0 HcmV?d00001 diff --git a/MLEBot/services/cmds/lo/__pycache__/achilles.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/achilles.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c02dd55a0d2759a6434eb5e1506bb0ab263bc6f GIT binary patch literal 1457 zcmb7ETW{P%6dqr9v$=73KrKRAp>|7D;aw-b>{cwO4XDsUA3&67Wy!KVlf;p4c)WxJ z9-_8F{DUCz!c!1R|3&{mL6N%hQzc%?n^(zGpE%=PC#(by%y_;zXU?(rJD1sC7Z)1{ z#*@!qZ2in5^e0oQAx(wTTOb@Fj4&6V0o(Zih1`JSm@EW*C=3J+@#r?f;v>RfK(R5^VB!XYAPz+p7tF*gu+ zM8utn8c@N)WAO;6AH$lA(nGGaBM^=&g^{c6)GKw9$MPd_)?-#1%;6ePujlof-nJhE zgzmopJU7WG5@VOW{A<5@4p7P61j^OyHFdY=Io&m{<2jvHzqJ{sp_BD@iJP>( z9y)=a?e&A0QYZ_BRM%5NlQ^OzU!XwA4&m6@BCH6M1e+;qb;|PHY`(m+e#82bl9XC* z>N}D3VeAdd_^sQ1_VLiQKKt~eTXANE0dZji&u@H>P|FK3wSw5H3a%u3X$?NP|7z7B zN@Xg4&H~#DmbcSIwqUWFv#qLq@hnKv?a}J(-z6+ zKe_1EGSuHt`U|2LK1>v*0XnJCKu=e#{!by0k5%CyePo=7-!s@BmfGPXWnS^tRN#`NHtiLx&=lkiKU|{!*CIAow$Bod^K{-dL e`E>Rfp?9CoK7~BTv1tAsf28Bma`qqglF{GgnQaaL literal 0 HcmV?d00001 diff --git a/MLEBot/services/cmds/lo/__pycache__/adi.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/adi.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed68773fe8689ba018bc8c93ae30ff182e3b7148 GIT binary patch literal 1299 zcmZ`&&1(}u6rb7IFVmWXic+OCD1HPn7LP@WO%)ZtK+!{37&bf8uyH@)P9n5c2v*di zw|eZUBK06%^v`G|VAzu&NPBWiJoV(8&6h!av-6wx+xKSny?Jln?%$t5AV2SJSAJlG zzDv>81_&}u8N0zT*6rD#8Z4yA{Z&?Dq`w$#57LU2|^n%yHZjazK8Pq zj-Y?}9Iw%``B7-xkW}hx7OnO8U?Nt{9W5S&bJ1!iX%;0D6- z_HhL>WeF@#m1SIo_gz_os{JjDto?c3z+qWg2mg)1o>8^}^?X7epjGlhm9>S$)!R$P zOq%7)&LKEv59jhEHX|--VIX)sE_lGG&xNonJXWir8-QIE+*^obXT=6hcMBoA0j_E9 z%KVIVU+_p+V6X%0a_BUC9@K?()vaG^l&xDguN1?&<$Jsg3wSR3bqn({-WsiT&p8t|P|ABlmKboJ9 zY)#L#rsuY@#a6bsY5w==z>(0fnb@RMmXFdnL#ZFKh9~uLN*^_BuV*nRWuZf9Br}bT zUVFy}8DFp)UY*u>kq*31N{O0%ez1jZWyy>^`@iYR{i^m&>7@^<7 z>6_XkCj}t;h#-PJWQbMrkdF>;93xgjHT0JCd`LU0k~v_HaK z%i(CuE|)DQ^nE)Z9DI4+Cr&RpS(Ki0vIArvQDk6(42fVvmJn4o`_e!Pkq?!I1V0C} zYe|OI_ZmPB`i>Bn+SL2*xeHaY%_X<8TrHU| zo-Y={is^f_1Pf%oa~;Y}$0ywMLbFuMm$z67KDMnEYWY$v`S>I7k73kNoJJE}&Rje{)zRNlEgGVXiawlXY8eKVWQzHF4VwYh*yCgtdssse#I{@{; zh$XD*F%qC5ZK`gz)N5ZV?OP9G8EX$a2;fw}FLg%5W{%X`E%kORN$NaqPtUZb@5i#> zF*?VNjI=wD)lvJqW9>ZJ&Fsn(f^sUmDg-9=-Y9P+H9=?c{m$e?XYv*x6MwL7Ia7iS zN<7n_bdU2XyAA@DpnzSXA?W{5;u7BBvL`6GeXk D=6)L2 literal 0 HcmV?d00001 diff --git a/MLEBot/services/cmds/lo/__pycache__/haim.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/haim.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bef56c5d4d412756eb11072f079cf5533b6078a0 GIT binary patch literal 1401 zcmb7E&2QX96rb_eZZ=;?98impR!B{{hj_i~_1dWjh!$uHw3nzktSnhx&urr0uX?-{ z4IHAjLi`8e$SDYgzk_o@ky!bJ#HHN4N>4rU#=A}$2@cG7-u&K&y}$R~%>J~z+(0mP zcV6y25)k@}E7g$a;QTg#V?+?a0UGdK2vCRz7;`KJLMRSIj0AKS5$QQ1G8Iomgic}2 z#y}!?2ep*Hx%!}m^CdS)94`(-HzEvl@lHs*d2qQ&bLIRgfMZ0_024G22p)(6qS8S{ z4X6XkYYi1J*z8gW${ERY&dujkUX>xYNSJ2$sK zzIVT+HkvSUX`dVGm9Zt{Qsjm-UnG=ysh?zi9Oa9Cl+o1nxZ+)q1PZ)9&tRQ@4&n=h zA<=K~0pQvA5m)HVo!}F&pI-Q$K@YwWchRxGp=E<&8*3vkVV77;9Tl(0WQ)5=V^YON>Uu2`OXl9_6KG zG#I7)_%dGAX7lxDcW&7a8BLizOno=9KZ(6@nWcT#&$h=y`~H`o-i|Xn4CoLxhtA6@1~kONo(-A!v)@Om7B_+u)+2c<`F6yX!ZKp%KLw;w0~b|7Xn&-C{9JZ zx+}gyTowo~{C{~4--o-=$m@>7&yeHf4aW&%G7dOia-2tFH<){BjzeP4aZ-MS^4k0+ zqgDBs2|u#^AuQyf$xszIgBQ9F{5mjMBH@{dAQJeqTz#0ecGK=wcQEmg|rf z2n8wEkL)w~+7FK>Yg!>0IOJBE{GB z4`${3Fnt#s{06ZE1SEm+1g#vEbAp;Lu09jA@#5-JC=JZB`FHufJ}o`3{=;7~`UjXX BQeyxB literal 0 HcmV?d00001 diff --git a/MLEBot/services/cmds/lo/__pycache__/hoos.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/hoos.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd284117f05c96bd903c00809a1ccadc7dc56f33 GIT binary patch literal 1224 zcma)5OHUL*5bmDWF3ZbA69dL%Vgk4amjea^F(v{6zPJ!QY$oXp)6MR{Jl5`662Joy zV*Ceq^t8s{k3qAEo4IJ>1#h!~6DO-@ch(>tte*b5s;heDtE!%lgM$SGvblOv`Hm6# zDUD3@u5vmD$|0hNVh@$&s(8r9WsIe+dWx@>Rg4t0gs66asLs@eiqH|vj+8ZuS5eXU zCC!y0jtA^ojX0t2+W{3|tMfi}y20t9b&bb>1vzx9EZcrgrX7gya zV1mS2Rr-ei@)ea>3v8do{gerZyS2y-gSg)fBF1e;8cvTg5R%o}hk5!0;3>k8_G`QW zdPhD$cPnrsc+ZL4^A`V0Xi2>1OroWdL7?iJ!ZA+jrk#05h9kk&n^ z4-oRIZhKu%ju09;gmBrEIM=J#Eu z;5;9eAn#)eQW3iVd%ck;SkFT?00uHf_pGKq@_wr|elJn6G1Eo>kgzfHLm!jAu`l{W zQ=do_RiAE|6HRj}Q9(^m25l7?w;`gfXD1UQ^QBQ8UXn>s=hr2(G|`*2?KBx^S1D^( zNw%v*0J;1rJ9&&qt(V(*{(h&95BLpm$jd0!0f0TmEi|;3jutYHFFq}F=lJ527zGS@ QT!wd3ZMl2)4_C_LH#?sy?EnA( literal 0 HcmV?d00001 diff --git a/MLEBot/services/cmds/lo/__pycache__/kd.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/kd.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fbc24a0be6e6f5102eb8b364009ad14275fbe5dc GIT binary patch literal 1130 zcmZ`&%}*0S6rb78(n8g6QKF>@+Vl8ZcSU@Uaml|0#yF_O?SqRKv^DwA6>LPs$AVki`^qN4U& zm@7pbja#)Eu>;Sue9FO=mpp0@f|E%Z7`=C(93X}aOpzf`Y{(K~%4SLps8IQ<(vskF zF#3{2XsOi#g5H?$&dW;= z%$J;n+*}VG%Qwq`-SC)S=jO6gf6`btUp#-j7}QPAW$Q3OTuYOTA`&aDjpJF062x#L~#~fnri27{>aaL&(D2RPxABa{QPm|zr6=r;dNxA3?ZVb zghW|Fynr@bp^p>tx?#Bkk4^{;Y(hd&)JPw^rD&3}s?~7oq{c$e;T*1jM>=)59fUN> zJ^gz#3G+h~UV;7LWdY*FBp?*N3(y?4Si)*1MglaXO*PH7dhJuabL&nlW9@Di0h|f= zsm=)B%!xYNR%c^LQWrYYGwtd7u?%XAQs~M^yA2UtHQgC&sV|A@^Ab#oYIs#JOA@_N z+fI^!8!4qXQW7^(B7mXzZTfjk32m6$dFElij*r6Y;1G|AR{?-M#vPPzCar@C$D_T2 Z?j4WzSR2ET$3^%s*%hm2|8ONd{s61_42S># literal 0 HcmV?d00001 diff --git a/MLEBot/services/cmds/lo/__pycache__/kunics.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/kunics.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7f43189f34af5e4f5d9eebe13118e0a1a2400fc6 GIT binary patch literal 1213 zcma)5%}*0S6o0c{{i2}pqC}GhgIE$uJa{q2184wIAOSfvn{3L?lq~K?-D!=0hen9; zKft4>8iRj{CnQa5_GID(Z`+g;C*SOLYZ^TGX6HBWYj%Hc-t6amJ_{(P>u1~F2*594 zB%=0}-W)1NK!Fk)%3_sla7dXDq06@9$Yq&;1PefwL!c^?TQa~g&REI{CF@YsehYJ@ zNP>|{t!9{><5XPA(U#{OYW9<(Md=&8cc>fzgEFB|mMAI95-?>ap@vkb{8ed5_&J=h zL>XFQwNN=u3_&inlTOU4M72X@IAhoEGrwXA!yHgV0^)QIaQ2=eECazZ-;y2FWBG*LxGa>k z(0@FL9bgCcOqjMqGuvtWcX zml_V^yt2(i9h})!KZoDvf^wm7u{i%gf5Dm0^)273xcXzyY&guVbA7?8FE+OH=g*$Z zd3D{f*%mIC`PwoW*G-3V-S+hOq)N42KZP&c%g3i#irwN0@->7r98CzbFgD&Eo%k_2 zdpbHBf^sk21!XLAE&$EUe?t%7i5D&#qzyx~#xR1cVK^Rb*g_vMj8~0{-H)UUgLQq~u3MEa=bS8@JiTk09 zY6uB*WzgNm2E$3uaNFw`#jlG1#_J(zcRlTggE5#h)10F)p-0 zZ5Qc>@i{*7Z(=~)NM1!i_Jnj`v>A;K6i$Yp4$PbkKcSWk_RY+1-kbL(`+M(AK2JZ+@FYFWidK}(3H_7K&W+E5WXfYp<7isCg? z)PGBJwTPo}D-4Mpc%J1`0k&H5sND}vwp8Ef?trq77%F3m$_mA0RY5GZIiLnqsQNY4 zP~da0dXhxw1FHeb!N8E@(wmuqSy8CIml};2waQ~O4b(G{wqk`Y+dlv7<@G|z@-1o^ zhLJ6RM4FelCPrLEB})00$D$l%!sbp`bpk)iIewLK%a(>cBn)(9t#)B`p8z~X7?OU6 zH$m^oC+N+;LU9Am4~D*7=*L_21=?5Nr(|z{xU#xEy-;zj4Psjhg|&e5Eu%0z0FoKb zL>TyjMdO0`lz2=CYlBJpg1HqgpNEi43x$)V(t`P1FfPn>?pVJ0Ah2s5^Q*#Ka;lGN z>*mVx!^NO#dM;as4I*DVHWQ}pQDM4)nbgS$w|E+UZ#$P%)JP7KuiS;xBN%lQXVK(z zD}Uuje*SxYzIk)`Fu&5uuQW4f&Mw@ArDsfiH-kgZq3^f_!cXNJZ=b z)Q2Nhu$GCD1Rd$qb+e^SeSFitdOKFJey4)~jwSrmW~FcTP@8LMbFrdmtGGRLtu=Ei zR;3=JQ|zcnzXnkqZE!o*Prbd!9z98@sPT)EV3P0Ua4X3PT4W4bWXKj71dz+WUN4;~ zsSQ&+$=vIe^FF@>4*BRr2mnc7+(!9>Nxn9Gz`+qd7WbeGJQN{*NC@oy2o|hCgBS E7bj6AVgLXD literal 0 HcmV?d00001 diff --git a/MLEBot/services/cmds/lo/__pycache__/ondo.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/ondo.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ccb4306cc033dbc06e80917d61cc228867aee5c GIT binary patch literal 1414 zcmb7E&2QX96rZuZ-p%HtMO+{PX@%4cIk0QT&U#(3AhkgGY!3wvD@)ecGn+X03y-%Y z0imd^5dT3qatcD>k0~gKl}|`q%FV0v)Dv&Ky9tr#fj6Gtyf^RJ`n~sN{L|`c6M<}e z`ef%@38BA)v5?waIlBSM5u%9V5DmpDg($*9jD;?TQX~&$j3jgmQRNY$H6|a+2%W&3 z$xxyAF4|Q87Uu0uT&$2Zb^RoYNK84{^390)^WbVx=Em6rP>v8oLrl?7qIf7vh$(vu zYCwg`zbMB*{RHMrst9#q9fNYRFa){!USna7B&t4AE=OFphD)>#)Ek9zC#K24N&+|v zvStgUP=r;~=X;w{p~NI&#WH2w&w?}$lDJq7;+$o~7lwaM5eSItJcN1nA;3onLz>^< zJ$nC+sGJClbEw& zg|nEt5p0tkCMwTaILelxR$LUh)q3{c&Fju*oMqhcvVg?SJBdH8l6Gzd`TJwf`Q+nw zZzQ=Bh0KEmBJTx0{Wl!$oNp4c8S%%5{sT>qZG3heBU*T~VC5r|!Bm z@m)6)C#tB=uT}9PWg{{Ub2nvK6mSmbj~Dg%Styo2xHy(oia(?51$aNZDnMMG3WUNR z034o=Qo^+cqy?ZNb^Xvet-buiy-B-U%2?H>2mn%7^*?LZgzws~wT;u-Mk&egTbQ&q zPFqGP3%x`aIF*t5Iz&xti?>Vl!aIwc(G!F=YuQVJV3qHy>3)?H^suw&VMp|^;{s;l zM>0!iU1;YiK5M)=E9YC;E8q|pil+dO1jZAzc36!GYCXREOwb#TFF&Q)#3Gyj#_x@3 L<$3xXf5qq@8aG{t literal 0 HcmV?d00001 diff --git a/MLEBot/services/cmds/lo/__pycache__/rexton.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/rexton.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..acaacc8cd1f82bf273dfcf5b07f01dcd0bf4664b GIT binary patch literal 1461 zcmb7E&5zqe6d!*koBcv?KrKRwR%%KPII)vBc11vHK`mcJqJ=|b$+A6@jaUA{X+i|G{!W;oH}B1Rwtw$q{Nv_k4Z-;2 z;q%?^d4&F8%5q2x;ruoT#|R_L1!%}tK0qNikePlGt7xhE9Yj=)#>4dkg1c5f{q&V-SuJK|>CsA&6ff)Jy#&^W!LA_oIxYj>i=5k}?ot*?I!= z`~!dw5eH#^&Fz6)jvu(C^v@M@C$N8dX?p?{`AYa49Sh%xtU5rQzkm4GILi`xtJMk# z_8kTL)QeLLHBcgwwVW(-ym3gPjJAx9W~ipo>FBDeX`QyAwoFskHM83_wXW9g8AeZQ z`JuB*Y_(^rN)qk9L-q%{-uH;!RvSKLvTJq?wJ{C{xZ`z4?NQ$$?RLMb>V3!14D6b2 z&($$;4c$;3&Fi}()ipb$3m-T79j))Eq~j6CB#l916sMt+4IU9UX?!tp0zW$#1Tm!n zp|tVH$JuzGs(rIDCjRa?8|bPDiLa)FCUHbbzClTZ?GT)SGb;%t!6;=-Pg(Zs^%wW< z+_FBSB&C*{`c7oM8+(%?8`fPvyEk#Ik3V|vcAQyZKwMbB^Gn|&)bc`1tsu6_;w#BP zT7efHZkCOrl-tFJEZBbz*Abf4(DwDSt)1VtTEA|!W(cW{I7evP6)y^eoi$hdzjzMs zhjUfStG3NL&bITKZHFpPaD0N zz{&kN0=QuCyL5xuZu}zMJdNyP?C*?*@D=NgkPLwr$vt4!zH>1$wM=Zz)+5Cq3f l(biFMO;P>X>ODnoK3lzK@*2mY`FH&G!Mw0s{D-|@^fx?bZKD7H literal 0 HcmV?d00001 diff --git a/MLEBot/services/cmds/lo/__pycache__/riz.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/riz.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c5efde691b2d2cdc0013de5b367cbd66072672cf GIT binary patch literal 1140 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 KIX+`O&g>s9DfXWL literal 0 HcmV?d00001 diff --git a/MLEBot/services/cmds/mle/__pycache__/query.cpython-311.pyc b/MLEBot/services/cmds/mle/__pycache__/query.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..22bc81da7eb24245351a20a98f76f9451ec27b1e GIT binary patch literal 6475 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 CeIlO# literal 0 HcmV?d00001 diff --git a/MLEBot/services/cmds/mle/__pycache__/rebuild.cpython-311.pyc b/MLEBot/services/cmds/mle/__pycache__/rebuild.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..863e4900950585c0bd5c7221d9c0cc16439a8b4a GIT binary patch literal 1803 zcmb^xO>f*pbjDtLoout!X4}v&D${C7HfpT)f&?lR?SfjqdZ2~FV##tmv#le4F=K-g za){Il^#@c62M*y9go-MD$|hPVE1!_Kv^TGkQ%=0`esv+VMdEqp&3p6SJio7*UuI^e z5x`GBKf9O82>mITAt(c4{{|47h#-PJ)D)}aAs;s}7PRb1zTA{ClF&^=3J(!csJsof zp1|xwvq112R9Bx1_I4eo$LuJwoY42}fNA5jX?_Y1!-}}Z3FRS$RMIpZDnf6wj@$~SeVp!8P}X9 z8iYBO7Qdj&iR+QIvjak34rAS?v8@w3wvA~WAT6@K*5TaIT6HNc1h!Ap5~18-ZWOy= z;1$5Tuk}aoK0V*zX~_*@%4|nSoKgIsA@cJ8=KdVS{VBqb!*#p@8-3UsWn;J_*2&l& zK)Jt{ze1bx4+V_Y(aOmF)~K+rcbe5EPKyZs0qb~BNw7U`oJk)<6TOxtCGUw>SgNZ` z12=B3P2EWxhjRXwaTi*#WOJ((dS3Wl60M!Q9_W;@km>hru7{43FiMQNl4^|dC=57F z6+&B-r9~^U84Y44IxDSk8W1Z8W4Glxwy2IMCZ}GDl|ar#Ds}zvlg4}Imz*+gE;H8- z%nw5+@xcf;Z@TfVWZC@uvyX0svFUph8ps6AZ(Z=zbbP|ara9;!BU;V>yLNogNyb1R z?lvz&v7f=WfwD3>ex_5tuvcE#Eidep7dz#}wsyqmR!%-Xw^ym{R%$zy`A%iNUHbdT zP;sh(yR&nTFYV1<-krVt``PZ5tKIjnX9}ub=^?4wzy~?bkTlc4{~2qj`o@d4Z~cF^ z_aQCt4wX{PvcwIutaRG4{E#G`ppRMBH;L^HBt^?2p<`K0bbVSJyjrPxPuy^xR-yiO z;>A`(neTEAwdQG&xZDXDNlPD(FKAw(UlBV2c34#)b%+GPw*YO7zD&Z3mLY*uq@LL@ zJId)FSG#Xt%Vey6fO|;bgB<^%oD$+w?bDZbl%3 K4*o@+d-NA-OQaA0 literal 0 HcmV?d00001 diff --git a/MLEBot/services/cmds/mle/__pycache__/salary.cpython-311.pyc b/MLEBot/services/cmds/mle/__pycache__/salary.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1236a1f7c889f63932f2c32251b702dcadb6f97 GIT binary patch literal 1920 zcmZ`(&2QX96d&7b@0WKIgepNK)Fn|rHW3>RrIb)bBxzLACOI^~!CGnLcqWxM_S(#N zD>eivq80SOjYGH~<$wYxxgv4oPuNt8y0TQM5|`$-QNpDccw?`3S=5ZjZ{C}EGk)*) ze&e4Dg&czR=l3t}|Dz!EmoSp1_LNfw$}S>^NDeZ@C_BiN3`r8Y;>fOID4?f^>ZDxN zP$OM)G&gOeK~Iy6lX0^~Hk!{mIX7?QB_yNUh~yq2lBdc(eER_2xEVT;)=*hL7UpVM z3U$->vE{k0StlH9Wx*xKDOdzO&rQcCKr2-n4X_wfAOExbCnvUn_xgvqSqd7BL% zGY1T9g}UpMS>o`diL*pI{TjqBqR5a4GGrndii~KgmOufi6Xh_qFGINS#x3&D5^Eon zgTz2YrRiEGF*jtQJxUFG3|mH)q#^Z8sI5h5ZCzZBLNED_*`%zr-Yl&wFP7@2ODnk& zv`~xl-+H6x*z_8#6wj3eCY7=rri6c(CzM;v_5<6ihk3gmP-a@fu==S$pd!_W@Se&N zLU)k_a0H+wZ5!44R;|CU*00^RR7=1_1K19wVaUFcw`D&5-?R4m0484&SY(jZhFyS~Z8 zRD)9%$~FmKY+pf%Y z6=`fiIzd97Me?!seB?qGDcPBhKKkAAQ+@J@KKaXs&!*n*T)WX#QDN!?$%UCSA-a*! z(8yT3IR3Oa{iHbk+vXpakFGDZuP?QV(?`WS?c$wQ=G=Y?0m4ru7iKUP*8<}(hq3FC zh9h(x}?p>MIrP=h#xQi7Q{~f>B^Ft=^`OCq>b%V+v=M?e9^i55n!gx3(PRqWbUT%-S*HwhxMFV-FAnhu6ov4HTU2U))`^GVf@X7^tRYVyj{g&U1 z5(7?D5>8YECn^{u6PG>IBYxuz26*y_ieYi!3*ZDXc}Ehy_ZSmj2R&%|lviT9G672r z(xlM(z`e}ejEgkO-h~Bmh57Fw02)c^pyE#SbWo`^+&k!<)^P8jtF7VQK~t^a{wkk| Z78>eZZB!CKzgj+CJBe1#&RLH#`xlPrm(r%H z`4UQ~@ELLL?tA}Z@~jYMTDoI~1!^+Gx4gh|TVcU!1*WeX+%eLou_X@5BYe-t2*77R z5V!!XME0PWdd+lRlkZ0?d!(7b4nu%Pq5VeK6WH}vD;=glCw2IDj|9b`PsxceN~uRk z&xhg{a3Fq{sbThTOAeLcyrC6L4(&bR2>UordpQwD{a0)AV}RS2S-*#rE9gvXxVDX3 z;h`&;&X!4M3qJqHFsFICZ?=L^W}fdF+h!0-w$<8ZVW!PYKNKt)US(#BYArXg8kV7R z{QVrCD<@UM4A1a6B(oxS?1n$VGnAAOjN^@*czJhmp}vWgS$)g5^j3YzHQEj?fYt9< z!Ag6p{`uYI+ip;IY;y}gnE91un5=F%l<^{b^$ z#mZ@Msass?$gdse*Z^KV`B2t0ej+q2%xjwC(zea*F-`ljt=pH8oTgFN&@`Vz9p)}C zs_+^$8+zLgG|%)Mi!t0j7UrnM4A-Y&VRd+_Vl;mP|81P_&u~>p6z2;29@T#OL;{iI z2)N2Zc6z_wm8O1r*n77Yi6py8`oJ|pi~6}#=Hc?GROw2UNKm9@(ktIU{3DUu5e#)- zgzR<9>Py4Wtz24eh zP|{RN1*$mq#-)dx(pKb9RaM2Yf5Ha2!Wsz)sTa8IDx5g=jd#5tX%O_#Sz2)PWnMaxU z3o2gCFeEOQN|It4hFrup$lRoXUxZ)~_+Ceq4OQ2cv;|GqoK;DYEe!g|v?UjnWzAM! zP{*L@)=l$Pxg;$H1EAv-vuHaCT_|Yr2D9rj^w=uOGZ^Jr%;Y&1siBo1ED#3FZHKm4 z*gEuj^DTrx!u+!8QuE!KvQl zHwLy{Z|OGxHxJ&I`=?O%ffH*hLsvhVI2GC*%C+~$i=f1s^ zKZv^lSUn_p4L~#10?pdM{IYo`MWK{x!%{Yyf<}^iAhvdBf9niOLPY>0iDA`NEUn~d zW^t|e#%%7B)D`HH&?lfz=WYV@1YMPv$|_-Ja~am6aU}e0S*MySIl@jtpMXA{BQdS$ zsFtiycUCtTD$P^@fuij$^xZE3WiyBY%&s#lps2UaCiVgieRsy}E~NH1+!yEp_kD;# zP7~n>t)rFZS}Qc;{MO0=Sc$G**EVsz&30nSY~F4#o4(f>=Riwt<$|AQ zu#Z-WXy5TXJ#RA?Hk(Lqc|yn;Xi4kb`kwu5^PO4R?j4F2Ek?rHA4{t9FN8#Wgiw@B zSWEU;Q=*!-TT7*}W-F$JvzJ;|nHftpwC$!HF6t0TrQ7RYc5>O&6!nJfSU17Non=i~ zPBjc2le#SF@@mr!t)J1xI+BL3ff$o*Y0=bm^G>-$YoKZ@s5p}#qNUm;sD~=yvAU>Q zRs@1Z!cs}L)S^Q~yJVTlE!82st`%?DBviIli?A9dJ+@lJQqgp@MNN@?oYF!@dZy;) zq;nJJrRlketCJTm%})_ASebhxV+~io{IxJ}y z7A#FJ7S5YW*?^tdg$tTl7vKe!w;@n=K{2pRm4!WdoGqyNSb366wc@^iJZ4n>bPt_j#TU9{)A;d)z%S<(`=F zPEWh1XWY|Qs~nS>Won4hmp;88d6s;yinz#S#_Rjw$M>J~oqF7N>QU_J$hbT5xp)3z zl}G&}HN^H`h8dJr5!?F~v-OQj{by?OMBVkFxrXooett}+n__7nRUN#q0 zQ27|QiBJc-<)bMwKRYMQUyx>VBw(RZ1}qfJfQ7mluuwh^0+{8p?nouoGBn$UDr%E( zBPB?zEh7mBN##T07i1lQy;&JbXhrb}-Gx01IM`_Zsw~5c5HcVF68bj^F5g!fJmC$U zg=-c*qt^@+A`A2x?~nIa4v$p$(H(x&F3BAq{Odd$OlpVGS5(L^MYTAXX-#bYfZoa z{b1UUfWQqhjE52%{?9|HO6TLDW0lUwL!VYU9}f*yIv)?EE1i#r-l=pxRpCR1Ms|Pt OgHrQHUcP4BKiNO?b>R#E literal 0 HcmV?d00001 diff --git a/MLEBot/services/cmds/mle/__pycache__/teaminfo.cpython-311.pyc b/MLEBot/services/cmds/mle/__pycache__/teaminfo.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e1dc2ad1935972f985d7f4eebfbff2383c7e366 GIT binary patch literal 2187 zcma)7-D?|15Z}AgNvF@mZBt^W!AFQ=IiYG^ND1j* zOH>sM2_^qQ`p}0wxs=#YN*?@I6xj%J90US|lDwU9p7PY0Q%Luxq64soTLBq6Ujva8e-;8Rp}Qm$H4 zflt$nlXf$;Osv-&&CS-bu|DhM+p7Yd0I7VVRu1LMNa-?s(qacFSmRvuS;63kJIE z;vr#T;pR=_+SPeu@#fVV)#a623l{E{w9H%P;3+VV2qQI#lA27Vnj#aHT2HV6EU2=T z+LYn^@C{mQkxnG4ZlyORn1}Dse6j=C1m%-tiqH(x*0YI7HI{3nX!b#B-0HuiTAtwM2vC`2FugX8x#rLmnwVrzGMJV?#)ntPl5E$ApR7D0*FaOCFEiPfAx2<-_Y9 zr;~Mu$FAg;Msl13u+u1q51;84-bhQV9}lCOXmIpTjHIqIo-bSKxO~_*)D+fAx-)%r z@ZuO_oS}|5p7=HdCvOU);W>`?L%Ri{%G@SPJ=KztWJTk z!Bf6jt$tSjS}-o^Yuq-Q^?A=~yRf;aFWLTbd#w&>cg^$buEW+~0L$-fi;24BQh~~P zlFgOYJzfN_oe4}*EQ3lig)oSJfV_m?28rGy$ENzFGtWxpy;6C%biQ9Y-_wrh&!<26 zBlWb*%!x7MnuV8mx9UDzkc*=^3vYqrJelq%PYbB za-@=}%lky0x(?<@BO-tBKWGtbwB(Gld!^ZJcV}g{ve>UI_DZw6rKNspsi(cOAHYrF z87+jG5f`LkggL`-J=%5{@CC#8u5CJ4svuaIBj!^Y+mVwL$5xvG9G!472U{ zMvHOR76OV|gc)iJ%i}c6uZ(IvRD{pt82nDS4(|9gl0rf-Rm3)sjo~knB~^!(^I{fYdy8Pt5*AJHBe`8$;_@g+gE2Jxu`Bn!PyJ_ zvzH=J1s!@;7#P4E(PMPA0c#7LcP-HS1R zq^u-KS;3^N7(hAZcUXw)4F8i{CgP&tC(%J%WvYL#1zLO@Le&CLeU1PP;JSdEjszim zqkTSz-W7fkM0_E|4iErIk^)lNh<^dmd*eMIANR(4K+gBZdqB#)@g9)r-gpnlx!!n> V(kCQD^=SG~?8h^&-!UKG>|cdU7=8c% literal 0 HcmV?d00001 diff --git a/MLEBot/services/cmds/mle/__pycache__/updatesprocket.cpython-311.pyc b/MLEBot/services/cmds/mle/__pycache__/updatesprocket.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fa4ad41b7f854450c1bbeb9f9f8e1b866244b50e GIT binary patch literal 1668 zcma(RO>f*p^o_mtx|`Gytw17D6hpKm8#Rl11W_ubMFmlcP$-A(N|xi9t-Sb)884_I zhd?V%mEe|ZP63qu0uKC!O|(*0J|S@_H?NXYFYv~@-qvj*@n+`Dd-Fc@-n=*S>&1&T zK=}EOr}zI=0REOt0osH(Sw~_QC{W@!c zTw`ceDA|C9{*PquG)Ow@#Ify0LEwZ`Ag|sCs5?>4x5|V$`4Nd-V9+8IS_&mCRRLD% z7NKD%RDDv}$IuUOjUm_23vM5YgMyL4Rl7#Pb`+{Vs+2X#Tu64O#UIRlS>5PQy_Rj+oW6M1bcY;DwNZDh?V3u+0VJQ(*nI{T(&(whCTg7tAgGplu+f#OTAV0LII0axwB>6X3qT{TPw5-#B9qjoHVu17qRXSUNJ64vqT2sP~Qf(7g26ADBzW=JJub zd}uZXW}|PMF>}RZ^U9HV<+ppowaTN{Bgcl}hN43rE18=YVV_+!*k!?xuM zvF)^G+d)KozNBYu`f>2 zrZb;Be`Wc_{to;NwCAr&BpWeDk`O;2+L?|_A)1kaL^|m6JMDq?W`FVB;XCV@O7xp# z1QMU-_%E$4<@G~td7v$4N>#f)T)jG2y_Ts+W>BD!3i=9ajI`&rnO;b9y>WCho`%L3 zWPEwL3-Nfdo|$eE%;Z({%f?8ao}MAfZaq&orW#~=<2FhUuhg@BCd3@Hpz3@MCJjOh$fOesvk44TYUtcH3( zxRSw7llc~Re0*7IQE_H|UVQv5_W1b3oE#v7!#O`UH!&}zxQGd8$Y+p=zr38SVnU07 zYGaa$G86M+T=J7kb5rw5ieua}OFT-GVtjpEo$^a!a&uCXfD}+=S!Qx-aZGY7Z=L_i4V+-jEoQXL>st2urP5mHL!o+ PVB%qF-~hoQHlP9kaSud` literal 0 HcmV?d00001 diff --git a/MLEBot/services/const.py b/MLEBot/services/const.py new file mode 100644 index 0000000..e1bd74b --- /dev/null +++ b/MLEBot/services/const.py @@ -0,0 +1,175 @@ +URL_REQ_TIMEOUT = 30.0 + +# 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 +# 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" + +# 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 + +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/services/sprocket/__init__.py b/MLEBot/services/sprocket/__init__.py new file mode 100644 index 0000000..b7dcedd --- /dev/null +++ b/MLEBot/services/sprocket/__init__.py @@ -0,0 +1,8 @@ +from .lookup import lookup_rl, lookup_franchise + +__version__ = '1.1.1' + +__all__ = ( + 'lookup_rl', + 'lookup_franchise' +) diff --git a/MLEBot/services/sprocket/__pycache__/__init__.cpython-311.pyc b/MLEBot/services/sprocket/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bdc3b5ab4d53848ad26416b9330dd56689ed3a41 GIT binary patch literal 318 zcmZ3^%ge<81U$=Lq_+X-#~=<2FhLog<$#Ro3@HpLj5!Rsj8TlaOi@gX3@J=e%qfgf zEGf*v44N!28G(v4nQn3B$1F^?=Y%ll2xGM6QS# zsH=ztNZjI%k1tCtD$dN$i;us>9v`2WlM^4mlHoH@j^UTLvsFxJF;F@tsVFlsFUBQ5 zximL5ucSD}J+s84G%3c{$JHsnBqlc}H3>)oRhDHYrxwQ)7Zl|uXQ!6L#K&jmWtPOp z>lIY~;;_lhPbtkwwJYKVnhkPyu`ZDKz|6?Vc!NRz0ygx3Pqcyi0}C@ZQv*8)7I6Y~ F001k&TFC$a literal 0 HcmV?d00001 diff --git a/MLEBot/services/sprocket/__pycache__/lookup.cpython-311.pyc b/MLEBot/services/sprocket/__pycache__/lookup.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..08f5e9e5d56abdf453da0f6331e8f46f79b63d43 GIT binary patch literal 10327 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 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_ + """ + franchise: dict | None = None + + if discord_id: + member: Member = lookup_rl(sprocket_data, + discord_id=discord_id) + if not member or not member.franchise: + return None + franchise = member.franchise + elif name: + franchise = _get_franchise(sprocket_data, + name=name) + if not franchise: + return None + + else: + return None + + # gather rl data + p_rl = [x for x in sprocket_data.players.data if x['franchise'] + == franchise['Franchise']] + + pl = [PlayerRL(x, + next((y for y in sprocket_data.trackers.data if y['mleid'] == x['member_id']), None), + next((y for y in sprocket_data.usages.data if y['role'] == x['slot']), None)) + for x in p_rl if x['skill_group'] == const.SPR_SG_PL and x['slot'] != 'NONE'] + + ml = [PlayerRL(x, + next((y for y in sprocket_data.trackers.data if y['mleid'] == x['member_id']), None), + next((y for y in sprocket_data.usages.data if y['role'] == x['slot']), None)) + for x in p_rl if x['skill_group'] == const.SPR_SG_ML and x['slot'] != 'NONE'] + + cl = [PlayerRL(x, + next((y for y in sprocket_data.trackers.data if y['mleid'] == x['member_id']), None), + next((y for y in sprocket_data.usages.data if y['role'] == x['slot']), None)) + for x in p_rl if x['skill_group'] == const.SPR_SG_CL and x['slot'] != 'NONE'] + + al = [PlayerRL(x, + next((y for y in sprocket_data.trackers.data if y['mleid'] == x['member_id']), None), + next((y for y in sprocket_data.usages.data if y['role'] == x['slot']), None)) + for x in p_rl if x['skill_group'] == const.SPR_SG_AL and x['slot'] != 'NONE'] + + fl = [PlayerRL(x, + next((y for y in sprocket_data.trackers.data if y['mleid'] == x['member_id']), None), + next((y for y in sprocket_data.usages.data if y['role'] == x['slot']), None)) + for x in p_rl if x['skill_group'] == const.SPR_SG_FL and x['slot'] != 'NONE'] + + rl_team = TeamRocketLeague( + pl=pl, + ml=ml, + cl=cl, + al=al, + fl=fl, + ) + + # gather tm data + p_tm = [] + + tm_team = TeamTrackmania( + cl=[], + al=[] + ) + + # compile + return Franchise( + players_rl=rl_team, + players_tm=tm_team, + players_rl_meta=p_rl, + players_tm_meta=p_tm, + franchise_meta=franchise, + fm=next((x for x in p_rl if x['Franchise Staff Position'] == 'Franchise Manager'), None), + gms=[x for x in p_rl if x['Franchise Staff Position'] == 'General Manager'], + agms=[x for x in p_rl if x['Franchise Staff Position'] == 'Assistant General Manager'], + captains=[x for x in p_rl if x['Franchise Staff Position'] == 'Captain'], + pr=[x for x in p_rl if x['Franchise Staff Position'] == 'PR Support'], + ) + + +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 = _get_id_member(sprocket_data, discord_id) + else: + member = None + + if not member: + return None + + player = _get_player(sprocket_data, member) + if not player: + return None + + return Member(member=member, + rl_player=PlayerRL(player=player, + tracker=_get_tracker(sprocket_data, member)), + franchise=_get_franchise(sprocket_data, player)) + + +def _get_member(sprocket_data: SprocketLinks, + name: str, + try_match: bool = False) -> dict: + members = sprocket_data.members.data + m = next((x for x in members if x['name'].lower() == name.lower()), None) + 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 + + +def _get_id_member(sprocket_data: SprocketLinks, + discord_id: int | str): + members = sprocket_data.members.data + return next((x for x in members if x['discord_id'] == str(discord_id)), None) + + +def _get_player(sprocket_data: SprocketLinks, + member: dict): + players = sprocket_data.players.data + return next((x for x in players if x['member_id'] == member['member_id']), + None) + + +def _get_franchise(sprocket_data: SprocketLinks, + player: dict | None = None, + name: str | None = None): + franchises = sprocket_data.teams.data + + if player: + return next((x for x in franchises if x['Franchise'] == player['franchise']), + None) + elif name: + return next((x for x in franchises if x['Franchise'].lower() == name.lower()), + None) + else: + return None + + +def _get_tracker(sprocket_data: SprocketLinks, + member: dict): + trackers = sprocket_data.trackers.data + return next((x for x in trackers if x['mleid'] == member['mle_id']), + None) 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 75d6ed8..0000000 --- a/MLEBot/task_roster.py +++ /dev/null @@ -1,208 +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 -import discord - -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 945872b..0000000 --- a/MLEBot/task_sprocket.py +++ /dev/null @@ -1,267 +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 difflib -import discord -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 get_franchise_from_interaction(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.data['sprocket_teams'] if x['Franchise'].upper() == known_guild['team'].upper()), - None) - - def get_franchise_from_player(self, - player: {}): - return next((x for x in self.data['sprocket_teams'] if x['Franchise'] == player['franchise']), None) - - def get_member_from_interaction(self, - interaction: discord.Interaction): - return next((x for x in self.data['sprocket_members'] if x['discord_id'] == str(interaction.user.id.__str__())), - None) - - def get_member_from_name_str(self, - name: str, - try_match: bool = False): - member = next((x for x in self.data['sprocket_members'] if x['name'].lower() == name.lower()), None) - if not member and try_match: - matches = difflib.get_close_matches(name, - [x['name'] for x in self.data['sprocket_members']], - 1) - if matches: - member = next((x for x in self.data['sprocket_members'] if x['name'].lower() == matches[0].lower()), - None) - return member - - def get_player_from_member(self, - member: {}): - return next((x for x in self.data['sprocket_players'] if x['member_id'] == member['member_id']), None) - - def get_playerTracker_from_member(self, - member: {}): - return next((x for x in self.data['sprocket_trackers'] if x['mleid'] == member['mle_id']), None) - - def get_players_from_franchise(self, - franchise: {}, - as_dict: bool = False): - players = [x for x in self.data['sprocket_players'] if x['franchise'] == franchise['Franchise']] - if not as_dict: - return players - return { - 'players': players, - 'PL': [x for x in players if x['skill_group'] == 'Premier League' and x['slot'] != 'NONE'], - 'ML': [x for x in players if x['skill_group'] == 'Master League' and x['slot'] != 'NONE'], - 'CL': [x for x in players if x['skill_group'] == 'Champion League' and x['slot'] != 'NONE'], - 'AL': [x for x in players if x['skill_group'] == 'Academy League' and x['slot'] != 'NONE'], - 'FL': [x for x in players if x['skill_group'] == 'Foundation League' and x['slot'] != 'NONE'], - } - - def get_role_usage_from_player(self, - player: {}): - return next((x for x in self.data['role_usages'] if - x['role'] == player['slot'] - and x['team_name'] == player['franchise'] - and x['league'].lower() in player['skill_group'].lower()), None) - - 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..897d69c --- /dev/null +++ b/MLEBot/tasks/__init__.py @@ -0,0 +1,7 @@ +from .sprocket import Sprocket + +__version__ = '1.1.1' + +__all__ = ( + 'Sprocket', +) diff --git a/MLEBot/tasks/__pycache__/__init__.cpython-311.pyc b/MLEBot/tasks/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8bc3d2cfa9163a9dd755d9b6013360caf18f218 GIT binary patch literal 274 zcmZ3^%ge<81mdkP(qn-1V-N=hn4pZ$Qb5LZh7^Vr#vF!R#wbQch7_hKrWD2~<`m{& z22GZij6emNjJG&~3ySiSvr|i|SPk`n&`*=)7Dq8utcV$?=oWW;d|7Hyab|vAeEco; z`1r(}ocQ>a44*+({jztqiU}WhUmuxa237=BDPA6vwz{mUxsV#rXQTI^~zd z`1s7c%#!$cy@JYL95%W6DWy57c10jxfm~Ir4kSJ>Gcq#XV350j XiXQNZHgJDnVd7?LU4xm~9nT$$u literal 0 HcmV?d00001 diff --git a/MLEBot/tasks/__pycache__/sprocket.cpython-311.pyc b/MLEBot/tasks/__pycache__/sprocket.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34466ccbf7045033bf946d5457da2ff502a190dc GIT binary patch literal 16285 zcmdU0eQX=&dB5W~B}$ZNTA#LjmK-~jZAFT|KV(I*V<)w(_&c_ugrapPi8duVca$A7 za^W^!BL^O;bnc>V?xt;NYA-43KZ+qMiY*yBEGr81NL**)00Rbw0n2~%oMJ)#+V6Rf zJl=_v<1J~h9*@tx_kO?6`+U9c``nLfYN|L~(cVwSdRK7Vztc&r{fua11)w(>ds5PuHM}J>7$D(J8tTo(b=ucfvR5<2eVnj}zTjIMMU2gX7-A z-?#?-qT?tR@qR+D5nlI<#-)U)417X4A`ZRg=s7t#a#l(mh)Z-&I@*;=s@#vc43ODGI&?U(;>JVL5T<;?DduHiDFH7@D{$?`{`P^ms zs#v~iqc%PJBO-1FY zNfrl^bSY9|R3@%bsEdN|dEf5sL&p_KR)$W?@z~_hp5(~X1e#NZ_Qg}rPMsb)c;K0* zlc}MJgajN75ss9hBHFsnUDSQiXnZoBibm7*MZ9(uqXN{>R)EV~u3_1X>ss~QhK{$A z*OL^?HBwCAUgtVIg7>!WoN;Ns)?DGK`dV>6Kv@i0mQqvl_2v&sT zs9A+sJ*(X;9^vI`^yd~YQ}-0=tRU9|>TZ@XU0bZYi@i5cF2dGjE?>*lg|EG)`dVaS zCflHCz--W+2)xauEC>Mt%(r(Dde3}g=ole%#8Z+SJDrdWRD8kcQ&Z;>(u>7zG90|L zy)^C;N=t)w$kaJDa+!^Y`cN3RpBgvD9x7JaWyV2zb4nQCGWR=-zhPFqel`N@FJF=^HWLEcTt9!G-Ue(uIJ}rb~MxHeJ(7BL(C9eE)?uVo;9qBj?e>66d zQWzZ?92q+=iNYBLSR=yv0+c8zdApDrk1N7xJRz-71bdApa?*jWAv0+R8rn}SFbP_Q zp;|K|OHVhN2(l+^Ap97VN}Sp=?|P?J4R+J*>wXAI1cnkTLQ4E9@@b?poA!@zl7`#c)HyONuEM1u-s5BdPVNzsdJ-L0Ru`R%#3>0b6mG1L5r<5kC53PKT} zgqd=dwI}bHbnx6{|FpxZyw12-0RYU6^NeW|l}e|pILH%QnrSOlNFyAui_+eAyo+AE zXZFNkHilp4fJ}rCaq4cutD<`+;*(P;2?0eKNluDN#3#2RhrA3xcZ#u#x{Gq^eiF8# zlz=d=r6iA0Q1=++A};wcDz>^<)-VKUoUkk|7i@v(NGvguUT=yT_HGz$qf%;QoSAK1 zI}=IdQ+9Q+>?9O|*y5(9>nBV=*zHD%< z8eE%eY?-YpM6aBGUJdr)&N6sg`Pr*R$cP2ZHMQPt611ky+fRJBN89s)wr42w5j2QF>ver@ZK%*G>mue13$f1e}xDTDW&E`;-(tNsL^uj0Z@ zvm2Rk{Dfxh6S8zZ2>}32gk9YUHOcVEv>B8dnN4yxCGoE+G;_m3!H`&55lQK`ha>SB zRH6I|09eaA>ikVVYSLCdndcC?+p#+Dr03cV+S;8Tq%v!Fvv-zQ5v<~BLNm|IuF^t} z&A*ZfM6^I;hR;<8@|?rJB-h?CvquZ8$OS?(d+!D6iy7ripi>KUe%Xw9H`-tgl`9*< z)9d&@I0CRA0@Q9A?crWpc8QMCvCEjBne^V!A*cS_%|GOqj`phR^YGrAi+#& zSdwvjxOY*iwAdCoyX1b;an+$<7vZSQ5T9|NP6w-#T4tB%EG^H!s8->%r90oVt9mMO}gZkgsm3_M6-XMq$P zad9Bx(w(pjUp-Uv`XN#m-M@9_P!mBgS5$Fh^U>IqGE_Q%>E#w}?&Mm6Bo;1F(VJ zQ!p%Lh*%PdYU6ka?Vv!WZ8)PDy;*aAh2}RLS zqR2-OU{1RLGuh)QG-qLtriJWbC;b9hhfT6>Hpp6j7S-r}Q&J4v;%-^USw4*93S_+L zVH0Q|se*{PK$X!JmPx^q8kUL_tR8+rJvpKs7PEb#+9wjF(z9@fDJ!E?`2cEwvOI`~ z{JarnCJyT>)0@Kv88%lSL!)!)_Aeg56slHw0EqU8WT*$mZ6z5g6P7e2jA4@{p#>AF zFJTBZChSq1E>m}YJw)O`lZ$9My9~9`>i=D@+4bY1TCPfrD^SP{SAIdOocGaOW>q{Pq~5xRc}B z$+LG(D%q1t=H!%iaw>D|ymst-w*S1^e;#DWo&^}D@`k{yQ;Gu9=#L`5fQDexF=AwD z+DeSAM2w5{JiY2s#&?khvxNpzAV}(qaT^4=Qxkgeo(W&j!WR~iVS6FgvFm^}mwYig z5yQf0#A>+^<BoYjz#=lW{p*(K++Q#pZ6fnzCRiNaM{nqK8a=M`E6n=XO%3X?n&51JZ3bN*`L|0<)C*3nS9Ahzy>_04y?i^FkRsUmT(BX*W2oIvyog zfvKJ}<@XlDbd-lt^}sEdB8lV$yD7qwD)7?AMQM#~N5SB%AiBSeW)$MkA|nM}R0A*0 z_IyzP!RB8y|Kp0EugGlshPLe+AB(>^^Q$xen)r1hyKPY2HaK$_pmeiRh7^ApmDzTy zNV6@$|3MqT+Z>(myi~Oiqi;E`RbxVUHaPh>azYcB)>q*mhDra=t9D|WIYnDLyV7=j zW#z&OV>XNZ5vo_71i(6%<9mj%1~0+f)@^ww_vD7#n}619jW@zbom(Gk1p1WrxpYhGlJ7&hD4h{qo!^0HvE%z%UNAVwwfISB#I2CgP{{dJ?@Oi6j<6 zg|S53hLj#gH{=)q_MDhfk!ib4A=xBgm!x3MHZ2t0MWRWs{6b#(Ce`;b0GY7ZV+u9o z7!!bdaL>7J_|^7}TGJ-HWCAa0hj z@(v3v_V)kcl)CSf);oyhJO~wrFd%9Gyw}}x-|KALfX$@F4Kw@lKCZ42n;tc*?lvz6 z;?4W!s`n%6#=TnSK15~O6s=9kwY8%!Evryb%c@!G$xMyGy|h~VLotIbEBOj$u(28A zFUSO#CWch88o_L7Oj@zHQYzF+`k8p1LL+D9AlaAn1+P-L?$??1t(SCBu!dzF&5#hfb(3M72Z1*{6rqr-wlv z>{)536e8O<8>A+r^Yl`xi<2%|y=)2knCAa5-s6Y7a^8)VX}3A^x~3CA=f zjErT1<63ZB^^F^{T@+rLV?n$blS~I^Wu_2A#nVVBAcAeFTle@xzQB>S zShrgCO+=@0X!g^VX&y*f4y3sBYUOM^k%*4T$*FVc+C#E55tn4)fD{{>lHjeANTzhx z!1Dvo7|hGI&KWZZ%wCkg6q^15gLtqV;4-I%MvZ%}{$~BnUM;ds?LC-{98_^1IjSBT z)Q-HUMy2c#Nj)OX_5c{SAv4W|59%^4;;#xt^0!cHy5=A}n@r5e-$tDLIsr1L<*ybm ze~Bs?`4Cp>_4te0Q5eIc*`uTC(NSQAJpsQq%+&Uez&~!{ z;@5(hzak>MLV%>h?^k3P{7}C)IW>u0?XPR<|JpkBaWzD@W$F)QBZpMn$D!?C(vH8Z zo*vI0A6Ji$W9r!xkl`lc(Y*yht~(AL&>aWy-;IAi{(BE#kxaXzSQ4=fDW) zjARQfahUAWTS|t2vOp=MJ1STK3p0J6hV&Bv(h{DMez55Z@kU&Ie7n}R!-&Ni1$qQa z3&0ugy)EHJN!Wn@gI@z!Hj-X`6oWwi zhFvGZXs{fO8hTU4IhN=uoj1&*o$_nphLq_3-<3Ge{dCaWn2L&I_jq-eoJ>eehGMS~ zCrZrOiCHJQC&6?w<33}Z$Uap}LP9ZG=yb;0T zN{uo~)2mAZC|KktsiQ=I!mx!AOW6!wG{(7r5i4i<5miXUYb@EU1Gk3BF2A{66L7#w zorJduAT|gHOCIhp4+j*lv+e{%0V0$C1Ya(X&CTM|uv*^c{I;Vn+a@lIxA&d)&Du>I zj@*gg3Rh_pc$&8#D3CXpm58l=-$=0#tIY@8gE#6SyF@Sj>^=whdck{?>)?7g#dX2) zs`C_g0f+LZI37PEB~@E2&v7Y|qJ=9JDu&<>=Um0fT|Acxm*URAQ_8sJtBWD))6Q^< z_xcp;#|6&lTf~=MOP=ZioX`bwnS#$I&h;%Go8-V~Ivwh~{JDE7HM)K)wugPv-Brl*g!JH zl&2Bpd*=DQEL3vyDjXE+{3ugK1r{$@++-Z4%`ZE;sJ-_QrTjMl=@Fk_xLdz;R@53I zH#dCHsMhyq>if0&ez-!_torcq-Nx4I7w7BWn!Y|g>&mU}yxFI%-ZAUa>N@N%D>`Of z*8_P!3bB$5+^0G|B~Xf6H1yeLpDmik552wGkdNbm|;J1MH0}AvI%4Vc4L2p`@!b^WzLU+)d+sH%+)V= zKN6f2?%*lBiiIQn{L@{|e_Bt_W+{XuPtL~O1MM_!x_u2d3qfZ5|Y3!G- zSfjN)ai?uVwrxYEZIjlviGA5%e9=%Yk!Z|p+|vlyJ+@@Hf-wI|bJXzRVk_@QacGCL zPUAE;Pj#1Fc8o!Gur0;(YWkET9!td2lHp*j6g}JN!w(CdT#U60S1=b9l8G^7S{zsp zG-T3h7IvYW|3G>601&%yfx2tt2LZo1@|VK_py3SGL~%`U1`E`E_qjKpySo3{{u%so zzQA|=Z~CuRU#p&Bzsl%*9ko9=0HhUtrAh>(gn<$hoycci3sC}PV3LvC`e=x>yUk4& zrf;5Xrn0NNy?KjjDP=Fucg9mp1v!Kamu7n|dq1u%!C1V-GV{J-$odo=b_HgyfsuzZ zy@E+`a=c%JF4Apg0?x?KW5LcG0}Ed*h+PrCK{rGe@^S(@32X+yZU;UBkZb6jRFUqP zOyWC%i-p5!-3K4o#A)nETIox7qDSd~X>sB0Ur<)b0u)@r;0znSHqFS%MohWtpjHi^ zLnlZN9*66`x9+X3>s@L zp_bXl-|D>HsRou+ymPgoYiIA&3fWpgUD=6Ibe$SOvZrZaMQ0mc2O(E4O82bUQe@K63FDBMbg;(*n)l9VG10b zzaakrLzMpv02J{Ug>L{1L_G2j=|vzwJ6dq|nwmIQvH=S8BmWK3tfV8~LWD&$QGfo8 z@{l@b$6mD|!^Ky*{eoK4o2lv5YI>nL{h<%*??P-nms|OmwsNBZ^KNHjErSN5O))uwuR6#^)P0coa1l&hWS<<$ zjTGl*pM%LnZv~-G-z6C=+)t4L0s07pF*9RpI?QCwGkW!NB{xp_5M??=AW9%f;5-4+ ze=?mm$$v_KDHK{8FeM_>w6Hldw)~dRD~((xamIij0p(jD&CBL5=W115t$CNvjhQn+ znDf-;9Ugb-1M+w|_BI(LWmYA{O;W80s=V%&+{PW+#(jAw zg4jr~fsn&J!RI>H<((9O{kIVMoG9P3RqNT4cOpnNTR|%mSTW5hdasV)E zB-_{rRFYzRAjwS31d%NXEcx#Mz+e~8A)PD$gUc9Mv)yfz)Iay_G#Gf7{Bz_ZD@GxH z!TCAQ=eRc2e&@L0W%kQ)wU^m1$5mfuKTAxP%2l}Yo?|%B&vTXGeHQ=eBB^Zj{{w$b B&Gi5P literal 0 HcmV?d00001 diff --git a/MLEBot/tasks/sprocket.py b/MLEBot/tasks/sprocket.py new file mode 100644 index 0000000..8ce9692 --- /dev/null +++ b/MLEBot/tasks/sprocket.py @@ -0,0 +1,182 @@ +import datetime +import json +import threading +from pydiscobot.services.log import logger +from ..types import SprocketLinks, UrlDataLink + + +class Sprocket: + """sprocket bot task""" + + def __init__(self, parent): + self.parent = parent + self.logger = logger(__name__) + + self._links = SprocketLinks() + self._loaded = False + + self.on_updated: list[callable] = [] + + self._last_time_ran: datetime.datetime | None = None + self._next_run_time: datetime.datetime | None = None + + @property + def links(self) -> SprocketLinks: + """get all url data links for sprocket + + Returns: + SprocketLinks: dataclass of sprocket url datalinks + """ + return self._links + + @property + def iterlinks(self) -> list[UrlDataLink]: + """get iterable links + + Returns: + tuple[SprocketLinks]: sprocket data links + """ + items = self._links.links() + return items + + @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' + + def _calc_next_fetch_time(self) -> None: + self.logger.info('calculating next run time...') + def _calc(last_time_ran: datetime.datetime, + hour_to_compare: int) -> datetime.datetime | None: + + now = datetime.datetime.now() + + # if we never ran.... well, run i guess? + if not last_time_ran: + self.logger.info('must update now...') + return now + + # if it's a new day, run it + if last_time_ran.day != now.day: + self.logger.info('must update now...') + return now + + # if we've ran already at this point today, return None + if last_time_ran.hour >= hour_to_compare: + return None + + # if we've passed the hour to compare and haven't updated, update now + if hour_to_compare <= now.hour: + self.logger.info('must update now...') + return now + + # calculate the time till the next update time + if last_time_ran.hour < hour_to_compare: + update_time = (now + datetime.timedelta(hours=hour_to_compare - now.hour, + minutes=0 - now.minute, + seconds=0 - now.second)) + self.logger.info('set to update @ %s...', str(update_time)) + return update_time + + x = _calc(self._last_time_ran, 6) + if x: + self._next_run_time = x + return + + x = _calc(self._last_time_ran, 12) + if x: + self._next_run_time = x + return + + x = _calc(self._last_time_ran, 18) + if x: + self._next_run_time = x + return + + async def _update(self): + threads = [threading.Thread(name='fetch', target=link.fetch) + for link in self.iterlinks] + + for thread in threads: + thread.start() + + for thread in threads: + thread.join() + + self._last_time_ran = datetime.datetime.now() + + 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.load() + + async def run(self): + """run this sprocket task + """ + self.logger.info('running task...') + if not self._loaded: + self.init() + if self.ready_to_update: + self.logger.info('updating links...') + await self._update() + else: + return + self._calc_next_fetch_time() + self.save() + for callback in self.on_updated: + await callback() + await self.parent.notify('sprocket server links updated.') + + 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 e6211e3..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['Franchise']] - 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['Franchise']} Roster") - .set_footer(text=f"Generated: {datetime.datetime.now().strftime('%c')}")) - embed.set_thumbnail(url=_team['Photo URL']) - return embed diff --git a/MLEBot/types/__init__.py b/MLEBot/types/__init__.py new file mode 100644 index 0000000..45b8f67 --- /dev/null +++ b/MLEBot/types/__init__.py @@ -0,0 +1,19 @@ +from .enums import LeagueEnum +from .franchise import Franchise, TeamRocketLeague, TeamTrackmania +from .member import Member +from .player import PlayerRL +from .sprocket_links import SprocketLinks +from .url_datalink import UrlDataLink + +__version__ = '1.1.1' + +__all__ = ( + 'Franchise', + 'TeamRocketLeague', + 'TeamTrackmania', + 'Member', + 'PlayerRL', + 'LeagueEnum', + 'SprocketLinks', + 'UrlDataLink', +) diff --git a/MLEBot/types/__pycache__/__init__.cpython-311.pyc b/MLEBot/types/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..55369fcecb584521dbec44aaab66f627fe5d2612 GIT binary patch literal 678 zcmZ{gJ#W-77{{H<`=vQL00V-NAzA6 zT%ZCK4oAYlj$`tOkA#a|;bBks*ta#82O`9w%^r_LjANU9J{Ad1Y!3K_NO1}|#FHYz zkBayRW#d0^JIWkAo-umbvfZi`nXBXXl2(f^rDA&Xm{D<9FP5yi`$bPo29@*UdH8LYqguck1wNO}MuePe;K&{$;5 zH5MB)Z*`DuYM%j~vKu=7{DAmR2OS7YV>{_!Wr1u*JyG{Wgc~H=TyM0JlLD|z&0eJw zLcTJo%DN&%hlEhh3Asc!fPrq`ZNJV>6l~{nS<))ssTZwaRip9`WwY1L^ZnWGR^8-+ zvpIa4^@^#Skg_TpLZ++rZS+ReMa$V+`4sw@_gCj&*RJC@J$lg%)uXNM|LW1RZm1qT f?}qA8)(zF8m)%fTlP71%wHGI&v(QZW7>ND?+vL5S literal 0 HcmV?d00001 diff --git a/MLEBot/types/__pycache__/enums.cpython-311.pyc b/MLEBot/types/__pycache__/enums.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..32699f4dab8061a63fc8e6b5dd5d162daf2ff630 GIT binary patch literal 661 zcmZutOK;Oa5T5lTF%c<3ibLUKA%T=jPn=MNWD=8B#7@;V2P{h~C%dX5`BD5hw5J^U z1Gx1cfDnJC$H)hS!~x}I6iz)cYbR6)#=GClH?yAIZ$7o#EkJuV`f>IZ{dYmOrf!1u z9tPLIfRO+~;t~Q941q~kz+_H7$^c(**XBx0If0(K60}DO#ftK9wkcMQY{>dK2G_vB zB@FO4;>t{N6(+kXQ``nq-6m_e8f#)Zt!Rz7f0lF6?eeK%k1V537$U~~xK@XdJQ49wHJBcEZ74}shL{!4F? z@+9@9$=r{;K|Dh#QI>kcd3KObJyiKR&b%<-Q*_zIIZr)~a?`!@i$sX2KuAkpAT0iT zr9@P%1cYyTci}8M`odmzA1%hqitawXQS^Ex`nHYJlM6+3PsG6L_8k#bC%PEjYWwxD nUM0Io#e~zh2&GI2S%QA^_bMAr@}z`+;X~)A$o~3Isk-bp3+kjm literal 0 HcmV?d00001 diff --git a/MLEBot/types/__pycache__/franchise.cpython-311.pyc b/MLEBot/types/__pycache__/franchise.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f5283fc9b12e5c9545d2b2b765f3a0dcf0770ee6 GIT binary patch literal 3538 zcmb_e&2JM&6rZ)%_WC2)K*C2-nl6+BP62^%1w<&NEgv~R0!~mXH|=)ujDyK~?appW zV<8cw9MUR>9*`2L9CD~qB6{G!p?`*rB4O>RRS#`$j*?zD^}Stt{Sg;crQ_M3-@JJ< zJM%vGr>?FDfwuX}pVK=*LVm|Vy#;E-_7fnM2_;m}Nlr)!0--+Y*L^vE$`7*&c4DmBYh`pG6mO16lk`upxeG2JZX3|crtn#HB@h?98|;h z4S;2GpWLm_Ani#OfeT~L!nJW-fuuGT>x*$bLDq%hm($ErG+kE;jyX&v1J|(Nkcx$8 zbPkCJO)_hi8|JXbK&GBGt&0_%bq#7eVeM|RN`W_!B%gSov$FpU5=*i^IpOodqGwb=i zvOH3fMz%qgjIB$yq^gFNW2$OLR5h2UvpUi-RsCvK(`z#!Ri*hfutn4m$Jn)HRn-h5 zZ)sK*{9ILeA8^+R-v@uZ7a?2+h<#!{0GMz3RYf5nRSCeaQf%?lrS6irA9`8bUwOlE zRSX6`sFK~_o(+%HIV!Kf%$r0d6`hz0KuiVgZZy{<*U~dN&B$uSMCXWVNGKh`so`Lq zgGg~s{JJ2TARK)o?rUtt_W;B7QP)stx#B?Ud7GU}L3uQ3yK|K!#nI^)-w(6=ZG?vX z-@zdQW;Jsh;J?`)?RgmO*$(cz@C_~Y6oG+ly9Ch5p0&&xWC7}C(-2T@21Uxvnn*dB zk|7mxGO5HuO&K~srH{OnBa;aXX-0U2wB@>nFwntkzyW2}UYK~c&EjQ%a7*@_@_%b=$Kba;HqfR}Otuw_|D!~R{9QPllM(G8`^I7ha;H5nD zLLMs1$2a8TFXb~Y<3kEXN2* zAeM>csRC3fv?_-RGmdI`?|p#^4M%O%XzFWlCkXX}I>q7-={rm-D4IbPrkQgJYB!%z z%$Y3IXr{6ICb<8 zQkd#&k!I5te+$Q0h+Qx9!=?HAIBZM8N05gQhsXQ_;2)B3k5Uq`mqn%0GguO_m&L(K z-@%fIy(}KA?CURy*vn#n3oXUwN9RW$j#k25i*#vVetdrX;dmv|y=W~BmBc>iWwEc) zz4!aX(%r^x^;MYPCxn&B)nkvpz%)N0bikfy;B;m1$m+SrM%9n>>4fuNB{1j&ZgIey zkHHsi)cam%CP%;K7+UU^j0E3kUuV-W5kj7$Zl`uRx@*HV%6n|%*9Vcn&07wu)Ns&;=!o)-h0~87(tq@fg%hJj5T}p&M!r75XSvquR znYxv~pa_+rNc;=ziNp|jLSms?sxo!rot;)K@ciy~@AK}x=leK0SpuYQ?|-dcAOJs= zaze&fIBKA<0~|QqHg&Bs*z*9TGVEEme`0QZvYcu!SiuT5i`*Sj+X2NcPgA^jRlsD=oI98+xzW zv8a_(i?PrR1Y>E5u^{4IU&#}Uz3ICC*i&GPM;>Fch&85^x4k5tV$2Q0C~=c^6vpUN z#@W2Y(GZs^QMUN;b75<3g+d7n-r59su;a^u6TeQV$P zQu4T0VU)B9^-H zsFqVRpK9KER_aCN%Th0+bZpJ#V|^g6psN0R93YHzLdXzq_y5O3xYj?%Jy5qp^kAISO1F4%J~C6-`$=7 literal 0 HcmV?d00001 diff --git a/MLEBot/types/__pycache__/player.cpython-311.pyc b/MLEBot/types/__pycache__/player.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4320b0e5cf2810aad109392926e532c027c19949 GIT binary patch literal 682 zcmZWmJ#W-N5S_L6KDZDP(E-xMRN)##L4^WEI5;GZ2qj!&EUosutLTJ3j@@-}sjf(o zP^IG^1fl!`e#NDWTp`gw=O|REn6>Ys0OQ>^Z)V?m#-1k zgT$Q&^GXtYBJl12EBdZ*d@5_g*=qpEx^Qhbm}^|i(JE&v<;}MdpGdXc-+S<;|H9iD zt2F!|jkjI7zT~@^^wulYddoxG%Io9uQr<9%qgr*Ed6Th($C9zU#aNt*EVBG2V;?gf z%}*MPi8N$P)v>3Xicx6t6~=gyq=uVOnrJ-3w&5#W*iXEC`us^S&{FAOs75>qx@nlj zGBG;njm)cT7+}krsR`mp4$+y(Sn8na?csQ$>_keSNOTurdhIDFsWl5g_|m;@w=64#tH>^! x8`Up)rfy?MaO(j=;Sxg5;Qs9IRn);*DjZ6@0{(|{oBM6y+(q5~oqv>N**`?qsx<%r literal 0 HcmV?d00001 diff --git a/MLEBot/types/__pycache__/sprocket_links.cpython-311.pyc b/MLEBot/types/__pycache__/sprocket_links.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fcf212b14a9c0ae2c9fc46ecce39e42eee700f8 GIT binary patch literal 3415 zcmcguO-$oP6dote5BcG5yL1yGHf@Ep5f$C8b_+`pQWgyC@*|WV;y4D^i49G$6KXqE zp*`flf!_AE9DAZ#(QA)Aa$L!le29Ensh94J6+P{#GZWi|l4=#HFnNBNdHy_p-+MFT z_e(Sy0@OUh&Q2)xEuBwkf5-&R$Wgg5BH z6k+))^_r@|PB^bfa}cy#t?g9Yd0@wD56*uikf$PG2mvsjJoK3HdVoj@o&)f!_ZZ$i zfmgdRKG81*1acYS(MArv;Q|PN*KMaQ)Jeb(?u8=B? z+O@|%YBR~sW}dEeMFVBYp%Ff%%(;RwNt=_jL&L9Dl&XB~1R^$p9F^|Qf3)gt|9|`8 z^pQE6HzwB2iFL<+pCt^Ajo#8}(>{K-83&s>}PPx$!ZdJe68j5)B z@pq54bdFodtaZZyZET0b(kq|2$Qsid=JbY>fso8=m9M#NW%njo%q~)0?E`%JTsVTT z(>E9QTqZrcL}j{(`{0GR!k?#Bp+6Xh^6Z;ka)byky@AaGX%DaGV0`O`xF3 z@ps$m<2b%ntHX1+3hO<1e+b=*A(cStE>a&OHHy?dq{fgMN9tdnu^wbY3RMH@7OW?l z7k+0X;R~Jts9p~zj~?ltGf0L@#&F3TE;SbpOFD_fAWNZie{qNTs++QXNHkajzs<829l9YV`S4D*=#<09@fcC zCB({ENGpi4H9W6?Mea# zel-=e!fr_~stT&_aL%nDtiZmjR-g^nYJ<4U8gD2Phw!M%YFBbNiI)=#ORx5_no%a; ze5g)Te}d}FOAtg0+|xTr3ncW*(gL6Am*vb4JmE7>geVecptF99?SEnY)(hbWv8xZ@ G0Q?P1bKHUe literal 0 HcmV?d00001 diff --git a/MLEBot/types/__pycache__/url_datalink.cpython-311.pyc b/MLEBot/types/__pycache__/url_datalink.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..73b20697a44b7d6a5f5eb5b61e55087b6cfef698 GIT binary patch literal 4837 zcmb_g-ESLN6~E(|acsv)oHXukvMHK2Nn2NGXw~iqS-MNMNlQOAE4o`?BO~K@#_bem zCcQJ>5UWTL^dVg$ZILQ9KoJ&NimL7d2wp%)(ElJsk%&gBctA+IZ?3vXRiF5sJNDRP zw_Aj`GdXkbIp=WA<`}7{rl->*4ulHeZDX;rH zFU{oL{8`ibUS+1tOPY?BRzg#AZS4ZjFKCw>+w$2(ZZB%wShQV3a}550!L_`lYmRGk zLo?lkhT_FAN{$971g6CPs7~g2^acn4bA{zl^NVOnjn)+jvY@X{t%hOI56h0soyaJj znsd!X10Pzkr{plLm&}=#>6-bHSuylX#8VujRODS4*`Wb7?aA@uQ`z%ayOUkuX5Pxq z*oE?Tp&0xOcPf5WTTOJOx3>E;NboC6e5PCpE1Ci%6A|7Yx^TN?rKvbGDziR;EJg zWD_bGiBqQGw6fNnDdY>4RO^0-GfZkp5Puhg>lFapx%xI#TUW136?7Mixbwo3TBeRv|nErWz_?OktrQDs<_V@AcV& zy|}~;$MG{0a+Pit+~@3^RHlTYrW;Oyn+s$E;rSHW!Im=-{sNZaDU@OEM0_uZgVVly zFVx+c1_cLT%yP;%9VjIm3BCVY{`6~(3G zP%ocE!k33?v>|Ezh7Wr{NGFphE~T=1xfE;Es^oJWrDK#}9amVDVzi@#dq_<8-J(!5 z+`>g4PjR#(6|_W9-gJyMKlo<8R5oTgw{c+LBBY(Adjif4V{yq{(U6o1Hs|FfcQWv$ z%22a2HQLNczcP^6@iN;GbC-Y+5xLk%0JD!Z(E^-bh96W`RjfhV# z2ER1Kl|Z)aWlpBU`HzFJBWP7Iaizc2;RNez=;OE`pW+ezmKt1NsWB-&D?Jz(+1z#f z{=m%Ez|3ZH=3nVuH?q~Sk0&1-_)1NdcE0|QfqYKnAFsdpr=x#-<2#?jU_*de;B{kz23bhp3a;|I}|x_1C?-2+-RW6ijgtKWr@AdEM=K}Uw)r0BcZe=7@BL8g?SClj9l5#ymSS) z&%KvXI30$bQnK^9*M9=%)12*|u*;S%Jjjd9zH>sJGPSpYbRBtESbRDz zhtdxjSb+C4RBkd?!lz!s2bv!FM7|BpyFrq9nf>6uNIzGkaLPegEA(`73B1G1QI;b< zxCFI?M^|tKb2tvR3X122p%%|8#W^AnI&_U|a`9k@QWH85Uve*3M>o@4iW zj&1cEBlyPu+W#P#T0eDNzj3L$`+jn4OCUs0jtjXU2*vm~La~+Cl(yB#1HMpI++a3d znr8Anrc5hc_qrs|r(^90>i-Grwj&7v83geEi1avxVoSb%7S@~}0uuZ%2q5N1s1YE{ z4^Wc;<}ZVUuq9C#Tbl8^q_z(7h1l)|wnMva4OI{SbmZ2^+RXZCf^{!};}e2)FTvWv zYf9T{by%OR>hxhj8^dqPe^I98u6t1lbS^VMzwNC2qLIsaiCk{c*2^WTCv&;W<$S3w z>B{AFy8yO~Yc!{Uyx0;)AaHj@7|N6Jqr$%h(ft@ffvXcAL6y^0hVLhGkO)PCu;?A; zUjym46Q|@M2we~y4Y^}Acxn+zjbXPSQAOQf-BDxsw38%@_SPa%^>9_LF?^cjp$yU0 zZwp~9RaYCnULcL(CTUR{4%9|Bj*!M^leDOf)>3gbu`yO-__ULqUFx0DxPiDy(*p-&cr4Qy|w6fBnj($I(;bkpFcw? GcJd#=r7#o# literal 0 HcmV?d00001 diff --git a/MLEBot/types/enums.py b/MLEBot/types/enums.py new file mode 100644 index 0000000..ce7cf3d --- /dev/null +++ b/MLEBot/types/enums.py @@ -0,0 +1,11 @@ +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/types/franchise.py b/MLEBot/types/franchise.py new file mode 100644 index 0000000..5c99541 --- /dev/null +++ b/MLEBot/types/franchise.py @@ -0,0 +1,74 @@ +from dataclasses import dataclass +from .player import PlayerRL + + +@dataclass +class TeamRocketLeague: + """Minor League E-Sports Rocket League Franchise Team + """ + pl: list[PlayerRL] + ml: list[PlayerRL] + cl: list[PlayerRL] + al: list[PlayerRL] + fl: list[PlayerRL] + + def all_players(self): + """get all players for this team + + Returns: + list[PlayerRL]: all players + """ + return self.pl + self.ml + self.cl + self.al + self.fl + + +@dataclass +class TeamTrackmania: + """Minor League E-Sports Trackmania Franchise Team + """ + cl: list[PlayerRL] + al: list[PlayerRL] + + def all_players(self): + """get all players for this team + + Returns: + list[PlayerRL]: all players + """ + return self.cl + self.al + + +@dataclass +class Franchise: + """Minor League E-Sports Franchise Dataclass + """ + players_rl: TeamRocketLeague + players_tm: TeamTrackmania + franchise_meta: dict + players_rl_meta: dict + players_tm_meta: dict + fm: dict + gms: list[dict] + agms: list[dict] + captains: list[dict] + pr: dict + + def all_players(self) -> list[PlayerRL]: + """get all players + + Returns: + list[PlayerRL]: all players + """ + return self.players_rl.all_players().extend(self.players_tm.all_players()) + + def get_skill_group(self, + team: list[PlayerRL]) -> str: + """cheap and easy get of skill group + + Args: + team (list[PlayerRL]): team of players + + Returns: + str: skill group + """ + if len(team) != 0: + return team[0].player['skill_group'] diff --git a/MLEBot/types/member.py b/MLEBot/types/member.py new file mode 100644 index 0000000..07a96f7 --- /dev/null +++ b/MLEBot/types/member.py @@ -0,0 +1,11 @@ +from dataclasses import dataclass, field +from .player import PlayerRL + + +@dataclass +class Member: + """MLE Sprocket 'Member' + """ + member: dict | None = None + rl_player: PlayerRL = field(default_factory=PlayerRL()) + franchise: dict | None = None diff --git a/MLEBot/types/player.py b/MLEBot/types/player.py new file mode 100644 index 0000000..f3c71d4 --- /dev/null +++ b/MLEBot/types/player.py @@ -0,0 +1,10 @@ +from dataclasses import dataclass + + +@dataclass +class PlayerRL: + """MLE Sprocket Rocket League 'Player' + """ + player: dict | None = None + tracker: dict | None = None + usage: dict | None = None diff --git a/MLEBot/types/sprocket_links.py b/MLEBot/types/sprocket_links.py new file mode 100644 index 0000000..bacba3c --- /dev/null +++ b/MLEBot/types/sprocket_links.py @@ -0,0 +1,23 @@ +from ..services import const +from ..types.url_datalink import UrlDataLink + + +class SprocketLinks: + """sprocket data links + """ + def __init__(self): + self.members: UrlDataLink = UrlDataLink('members', const.SPR_DL_MEMBERS) + self.players: UrlDataLink = UrlDataLink('players', const.SPR_DL_PLAYERS) + self.player_stats: UrlDataLink = UrlDataLink('player_stats', const.SPR_DL_PLA_STATS) + self.scrims: UrlDataLink = UrlDataLink('scrims', const.SPR_DL_SCRIMS) + self.teams: UrlDataLink = UrlDataLink('teams', const.SPR_DL_TEAMS) + self.fixtures: UrlDataLink = UrlDataLink('fixtures', const.SPR_DL_FIXT) + self.match_grps: UrlDataLink = UrlDataLink('match_groups', const.SPR_DL_MAT_GRP) + self.matches: UrlDataLink = UrlDataLink('matches', const.SPR_DL_MATCHES) + self.trackers: UrlDataLink = UrlDataLink('trackers', const.SPR_DL_TRACKER) + self.usages: UrlDataLink = UrlDataLink('usages', const.SPR_DL_USAGE) + + def links(self): + """get iterable links from this sprocket links class + """ + return [getattr(self, x) for x in dir(self) if isinstance(getattr(self, x), UrlDataLink)] diff --git a/MLEBot/types/url_datalink.py b/MLEBot/types/url_datalink.py new file mode 100644 index 0000000..91e9f61 --- /dev/null +++ b/MLEBot/types/url_datalink.py @@ -0,0 +1,106 @@ +import datetime +import json +import requests +from pydiscobot.services.log import logger +from ..services.const import URL_REQ_TIMEOUT + + +class UrlDataLink: + """url data link to grab json data from remote server and store it + """ + + def __init__(self, + name: str, + url_link: str): + self.logger = logger(__name__+name) + self.logger.info('initializing link -> %s | url -> %s...', name, url_link) + self._time: datetime.datetime | None = None + self._data = None + self._name = name + self._url = url_link + self._initialized: bool = False + + @property + def data(self) -> any: + """get stored data + + Returns: + any: stored data + """ + return self._data + + @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._name + '.json' + + def compress(self) -> dict: + """compress data to dict + + Returns: + dict: dict describing this link + """ + return { + 'data': self._data, + 'time': self._time.strftime("%d/%m/%Y, %H:%M:%S"), + } + + def decompress(self, data: dict): + """restore compressed data back to link + + Args: + data (dict): data to restore into link + """ + try: + self._data = data['data'] + self._time = datetime.datetime.strptime( + data['time'], "%d/%m/%Y, %H:%M:%S") + except (json.JSONDecodeError, ValueError, KeyError) as e: + self.logger.warning('load failure...%s - %s', self._name, e) + self._data = None + self._time = None + + def fetch(self): + """fetch data from url + + Raises: + ValueError: URL Link is empty or corrupt. + """ + if not self._url: + raise ValueError('URL Link is empty, cannot fetch data') + + self.logger.info('fetching %s...', self._url) + self._data = requests.get(self._url, + timeout=URL_REQ_TIMEOUT).json() + self._time = datetime.datetime.now() + self.save() + + def init(self): + """initialize + """ + if self._initialized: + return + try: + self.load() + except (FileNotFoundError, EOFError): + pass + finally: + self._initialized = True + + def save(self): + """save data to artifacts dir + """ + self.logger.info('saving %s...', self.json_link) + with open(self.json_link, 'w', encoding='utf-8') as f: + json.dump(self.compress(), f) + + def load(self): + """load data from artifacts dir + """ + self.logger.info('loading %s...', self.json_link) + with open(self.json_link, 'r', encoding='utf-8') as f: + self.decompress(json.load(f)) diff --git a/rebuild.bat b/rebuild.bat new file mode 100644 index 0000000..2f70daa --- /dev/null +++ b/rebuild.bat @@ -0,0 +1,3 @@ +call .venv/Scripts/activate + +python -m pip install ../PyDiscoBot/. --upgrade \ No newline at end of file From 6a05e05472652c0d593dfd9a759b76196e36fcc5 Mon Sep 17 00:00:00 2001 From: Brian LaFond <52360893+iroxusux@users.noreply.github.com> Date: Sun, 30 Mar 2025 18:29:45 -0400 Subject: [PATCH 3/7] removal of garbage and additions to .gitignore to prevent more garbage --- .gitignore | 5 +++++ .vscode/launch.json | 23 -------------------- MLEBot/__pycache__/__init__.cpython-311.pyc | Bin 264 -> 0 bytes MLEBot/__pycache__/mle_bot.cpython-311.pyc | Bin 6332 -> 0 bytes MLEBot/__pycache__/mlebot.cpython-311.pyc | Bin 4714 -> 0 bytes MLEBot/__pycache__/roles.cpython-311.pyc | Bin 8145 -> 0 bytes pyproject.toml | 3 +-- rebuild.bat | 3 --- requirements.txt | Bin 700 -> 594 bytes 9 files changed, 6 insertions(+), 28 deletions(-) create mode 100644 .gitignore delete mode 100644 .vscode/launch.json delete mode 100644 MLEBot/__pycache__/__init__.cpython-311.pyc delete mode 100644 MLEBot/__pycache__/mle_bot.cpython-311.pyc delete mode 100644 MLEBot/__pycache__/mlebot.cpython-311.pyc delete mode 100644 MLEBot/__pycache__/roles.cpython-311.pyc delete mode 100644 rebuild.bat diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8e8edaf --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*__pycache__ +*.pyc +*.mypy_cache +*.vscode +rebuild.bat \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 4b9f245..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Launch Program", - "skipFiles": [ - "/**" - ], - "program": "${workspaceFolder}\\test.js", - }, - { - "name": "Debug Unit Test", - "type": "debugpy", - "request": "launch", - "justMyCode": false, - } - ], -} \ No newline at end of file diff --git a/MLEBot/__pycache__/__init__.cpython-311.pyc b/MLEBot/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index 08f0864c3264ce70f3519822474bb553d92d8d32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 264 zcmZ3^%ge<81pD{ANDl+jk3k$5V1hC}O92_v8B!Qh7;_kM8KW2(8B&;{m{J&{m{XX8 z88lg5G6EH7GTvhI^>KB|FR5ZR)HBpG@Y7_u#g?0snv`Es#0*q$i#tBPEVZaOGe0jr z{uXz*Q}arSW85=KJW7*dAZEuv z42+47&&*Vss#X}l}2^| diff --git a/MLEBot/__pycache__/mle_bot.cpython-311.pyc b/MLEBot/__pycache__/mle_bot.cpython-311.pyc deleted file mode 100644 index 1f0e7ed78862f90e5cbd1f0f5ba3594f47f537be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6332 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 EFD4oq?*IS* diff --git a/MLEBot/__pycache__/roles.cpython-311.pyc b/MLEBot/__pycache__/roles.cpython-311.pyc deleted file mode 100644 index f51a8b08bc434faf727d8052910286dd960b37e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8145 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 uPax5&7KT}1Uf=1.0.4", + "PyDiscoBot>=1.1.1", "discord.py", "python-dotenv", "requests", - "html2image" ] license = {file = "LICENSE"} diff --git a/rebuild.bat b/rebuild.bat deleted file mode 100644 index 2f70daa..0000000 --- a/rebuild.bat +++ /dev/null @@ -1,3 +0,0 @@ -call .venv/Scripts/activate - -python -m pip install ../PyDiscoBot/. --upgrade \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index a4984ff21a2516467f4473405216e6226e82150e..f7d32a9c5f4d29b8af1a48f9d69570129185fb23 100644 GIT binary patch delta 50 ycmdnPdWmI1%EX44iT^ez1~61IxG-cg6f-0<A9e4s==LlHwFLoN`56c_*{ zO@P>J;_j`!1wg@MhHN024m2YVNP?6YffX1s@G@{Q6fjf*aS_mvEQVB|+7gC%h72Gt TAE>JstQuq^2pdeE$aoL{+EE=t From a90a596ea666941a553a65ab1dcb0af4f5900641 Mon Sep 17 00:00:00 2001 From: Brian LaFond <52360893+iroxusux@users.noreply.github.com> Date: Tue, 1 Apr 2025 15:58:48 -0400 Subject: [PATCH 4/7] fixed next time calculation for sprocket req --- .gitignore | 182 +++++++++++++++++++++++++++++++++++++-- MLEBot/tasks/sprocket.py | 67 +++++--------- 2 files changed, 200 insertions(+), 49 deletions(-) diff --git a/.gitignore b/.gitignore index 8e8edaf..37697a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,177 @@ -*__pycache__ -*.pyc -*.mypy_cache -*.vscode -rebuild.bat \ No newline at end of file +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# 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/MLEBot/tasks/sprocket.py b/MLEBot/tasks/sprocket.py index 8ce9692..f9a6c3f 100644 --- a/MLEBot/tasks/sprocket.py +++ b/MLEBot/tasks/sprocket.py @@ -61,52 +61,31 @@ def json_link(self) -> str: def _calc_next_fetch_time(self) -> None: self.logger.info('calculating next run time...') - def _calc(last_time_ran: datetime.datetime, - hour_to_compare: int) -> datetime.datetime | None: - - now = datetime.datetime.now() - - # if we never ran.... well, run i guess? - if not last_time_ran: - self.logger.info('must update now...') - return now - - # if it's a new day, run it - if last_time_ran.day != now.day: - self.logger.info('must update now...') - return now - - # if we've ran already at this point today, return None - if last_time_ran.hour >= hour_to_compare: - return None - - # if we've passed the hour to compare and haven't updated, update now - if hour_to_compare <= now.hour: - self.logger.info('must update now...') - return now - - # calculate the time till the next update time - if last_time_ran.hour < hour_to_compare: - update_time = (now + datetime.timedelta(hours=hour_to_compare - now.hour, - minutes=0 - now.minute, - seconds=0 - now.second)) - self.logger.info('set to update @ %s...', str(update_time)) - return update_time - - x = _calc(self._last_time_ran, 6) - if x: - self._next_run_time = x - return - x = _calc(self._last_time_ran, 12) - if x: - self._next_run_time = x - return + 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)) - x = _calc(self._last_time_ran, 18) - if x: - self._next_run_time = x - return + 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) From a91d88ae051c24c7e50c1528dfbb58ac94894c73 Mon Sep 17 00:00:00 2001 From: Brian LaFond <52360893+iroxusux@users.noreply.github.com> Date: Sat, 5 Apr 2025 11:10:08 -0400 Subject: [PATCH 5/7] heavily cleanup and modify to follow code-flow of PyDiscoBot. addition of unit-tests that i would like another dev to start on another branch. --- MLEBot/__init__.py | 3 + MLEBot/embed_frames/__init__.py | 5 +- .../__pycache__/__init__.cpython-311.pyc | Bin 514 -> 0 bytes .../__pycache__/card.cpython-311.pyc | Bin 1406 -> 0 bytes .../__pycache__/salary_card.cpython-311.pyc | Bin 2588 -> 0 bytes .../__pycache__/salary_card.cpython-313.pyc | Bin 2726 -> 0 bytes .../teameligibility_card.cpython-311.pyc | Bin 2994 -> 0 bytes .../__pycache__/teaminfo_card.cpython-311.pyc | Bin 7955 -> 0 bytes .../__pycache__/usage_card.cpython-311.pyc | Bin 4241 -> 0 bytes .../__pycache__/usage_card.cpython-313.pyc | Bin 3293 -> 0 bytes MLEBot/embed_frames/card.py | 10 +- MLEBot/embed_frames/salary_card.py | 39 +++--- MLEBot/embed_frames/teameligibility_card.py | 31 ++--- MLEBot/embed_frames/teaminfo_card.py | 19 +-- MLEBot/embed_frames/usage_card.py | 18 +-- MLEBot/mlebot.py | 11 +- MLEBot/services/__init__.py | 3 + .../__pycache__/__init__.cpython-311.pyc | Bin 432 -> 0 bytes .../__pycache__/const.cpython-311.pyc | Bin 4343 -> 0 bytes MLEBot/services/cmds/__init__.py | 3 + .../cmds/__pycache__/__init__.cpython-311.pyc | Bin 420 -> 0 bytes MLEBot/services/cmds/lo/__init__.py | 4 + .../lo/__pycache__/__init__.cpython-311.pyc | Bin 968 -> 0 bytes .../lo/__pycache__/achilles.cpython-311.pyc | Bin 1457 -> 0 bytes .../cmds/lo/__pycache__/adi.cpython-311.pyc | Bin 1299 -> 0 bytes .../cmds/lo/__pycache__/bw.cpython-311.pyc | Bin 1182 -> 0 bytes .../cmds/lo/__pycache__/haim.cpython-311.pyc | Bin 1401 -> 0 bytes .../cmds/lo/__pycache__/hoos.cpython-311.pyc | Bin 1224 -> 0 bytes .../cmds/lo/__pycache__/kd.cpython-311.pyc | Bin 1130 -> 0 bytes .../lo/__pycache__/kunics.cpython-311.pyc | Bin 1213 -> 0 bytes .../cmds/lo/__pycache__/maple.cpython-311.pyc | Bin 1208 -> 0 bytes .../cmds/lo/__pycache__/ondo.cpython-311.pyc | Bin 1414 -> 0 bytes .../lo/__pycache__/rexton.cpython-311.pyc | Bin 1461 -> 0 bytes .../cmds/lo/__pycache__/riz.cpython-311.pyc | Bin 1140 -> 0 bytes .../lo/__pycache__/soviet.cpython-311.pyc | Bin 1495 -> 0 bytes .../cmds/lo/__pycache__/zb.cpython-311.pyc | Bin 1217 -> 0 bytes MLEBot/services/cmds/lo/achilles.py | 6 +- MLEBot/services/cmds/lo/adi.py | 9 +- MLEBot/services/cmds/lo/bw.py | 11 +- MLEBot/services/cmds/lo/haim.py | 7 +- MLEBot/services/cmds/lo/hoos.py | 7 +- MLEBot/services/cmds/lo/kd.py | 7 +- MLEBot/services/cmds/lo/kunics.py | 7 +- MLEBot/services/cmds/lo/maple.py | 7 +- MLEBot/services/cmds/lo/ondo.py | 7 +- MLEBot/services/cmds/lo/rexton.py | 7 +- MLEBot/services/cmds/lo/riz.py | 7 +- MLEBot/services/cmds/lo/soviet.py | 8 +- MLEBot/services/cmds/lo/zb.py | 7 +- MLEBot/services/cmds/mle/__init__.py | 3 + .../mle/__pycache__/__init__.cpython-311.pyc | Bin 769 -> 0 bytes .../mle/__pycache__/lookup.cpython-311.pyc | Bin 2011 -> 0 bytes .../mle/__pycache__/query.cpython-311.pyc | Bin 6475 -> 0 bytes .../mle/__pycache__/rebuild.cpython-311.pyc | Bin 1803 -> 0 bytes .../mle/__pycache__/salary.cpython-311.pyc | Bin 1920 -> 0 bytes .../mle/__pycache__/showusage.cpython-311.pyc | Bin 1909 -> 0 bytes .../teameligibility.cpython-311.pyc | Bin 3035 -> 0 bytes .../mle/__pycache__/teaminfo.cpython-311.pyc | Bin 2187 -> 0 bytes .../updatesprocket.cpython-311.pyc | Bin 1668 -> 0 bytes MLEBot/services/cmds/mle/lookup.py | 7 +- MLEBot/services/cmds/mle/query.py | 10 +- MLEBot/services/cmds/mle/rebuild.py | 7 +- MLEBot/services/cmds/mle/salary.py | 11 +- MLEBot/services/cmds/mle/showusage.py | 7 +- MLEBot/services/cmds/mle/teameligibility.py | 7 +- MLEBot/services/cmds/mle/teaminfo.py | 2 +- MLEBot/services/cmds/mle/updatesprocket.py | 7 +- MLEBot/services/cmds/rl/__init__.py | 2 + .../rl/__pycache__/__init__.cpython-311.pyc | Bin 249 -> 0 bytes MLEBot/services/const.py | 12 ++ MLEBot/services/sprocket/__init__.py | 3 + .../__pycache__/__init__.cpython-311.pyc | Bin 318 -> 0 bytes .../__pycache__/lookup.cpython-311.pyc | Bin 10327 -> 0 bytes MLEBot/services/sprocket/lookup.py | 40 +++--- MLEBot/tasks/__init__.py | 3 + .../__pycache__/__init__.cpython-311.pyc | Bin 274 -> 0 bytes .../__pycache__/sprocket.cpython-311.pyc | Bin 16285 -> 0 bytes MLEBot/tasks/sprocket.py | 13 +- MLEBot/tests/__init__.py | 11 ++ MLEBot/types/__init__.py | 6 +- .../__pycache__/__init__.cpython-311.pyc | Bin 678 -> 0 bytes .../types/__pycache__/enums.cpython-311.pyc | Bin 661 -> 0 bytes .../__pycache__/franchise.cpython-311.pyc | Bin 3538 -> 0 bytes .../types/__pycache__/member.cpython-311.pyc | Bin 880 -> 0 bytes .../types/__pycache__/player.cpython-311.pyc | Bin 682 -> 0 bytes .../sprocket_links.cpython-311.pyc | Bin 3415 -> 0 bytes .../__pycache__/url_datalink.cpython-311.pyc | Bin 4837 -> 0 bytes MLEBot/types/enums.py | 3 + MLEBot/types/franchise.py | 63 ++++++++- MLEBot/types/member.py | 21 +++ MLEBot/types/player.py | 124 ++++++++++++++++++ MLEBot/types/sprocket_links.py | 3 + MLEBot/types/url_datalink.py | 6 +- pyproject.toml | 2 +- 94 files changed, 489 insertions(+), 127 deletions(-) delete mode 100644 MLEBot/embed_frames/__pycache__/__init__.cpython-311.pyc delete mode 100644 MLEBot/embed_frames/__pycache__/card.cpython-311.pyc delete mode 100644 MLEBot/embed_frames/__pycache__/salary_card.cpython-311.pyc delete mode 100644 MLEBot/embed_frames/__pycache__/salary_card.cpython-313.pyc delete mode 100644 MLEBot/embed_frames/__pycache__/teameligibility_card.cpython-311.pyc delete mode 100644 MLEBot/embed_frames/__pycache__/teaminfo_card.cpython-311.pyc delete mode 100644 MLEBot/embed_frames/__pycache__/usage_card.cpython-311.pyc delete mode 100644 MLEBot/embed_frames/__pycache__/usage_card.cpython-313.pyc delete mode 100644 MLEBot/services/__pycache__/__init__.cpython-311.pyc delete mode 100644 MLEBot/services/__pycache__/const.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/__pycache__/__init__.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/lo/__pycache__/__init__.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/lo/__pycache__/achilles.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/lo/__pycache__/adi.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/lo/__pycache__/bw.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/lo/__pycache__/haim.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/lo/__pycache__/hoos.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/lo/__pycache__/kd.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/lo/__pycache__/kunics.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/lo/__pycache__/maple.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/lo/__pycache__/ondo.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/lo/__pycache__/rexton.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/lo/__pycache__/riz.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/lo/__pycache__/soviet.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/lo/__pycache__/zb.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/mle/__pycache__/__init__.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/mle/__pycache__/lookup.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/mle/__pycache__/query.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/mle/__pycache__/rebuild.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/mle/__pycache__/salary.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/mle/__pycache__/showusage.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/mle/__pycache__/teameligibility.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/mle/__pycache__/teaminfo.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/mle/__pycache__/updatesprocket.cpython-311.pyc delete mode 100644 MLEBot/services/cmds/rl/__pycache__/__init__.cpython-311.pyc delete mode 100644 MLEBot/services/sprocket/__pycache__/__init__.cpython-311.pyc delete mode 100644 MLEBot/services/sprocket/__pycache__/lookup.cpython-311.pyc delete mode 100644 MLEBot/tasks/__pycache__/__init__.cpython-311.pyc delete mode 100644 MLEBot/tasks/__pycache__/sprocket.cpython-311.pyc create mode 100644 MLEBot/tests/__init__.py delete mode 100644 MLEBot/types/__pycache__/__init__.cpython-311.pyc delete mode 100644 MLEBot/types/__pycache__/enums.cpython-311.pyc delete mode 100644 MLEBot/types/__pycache__/franchise.cpython-311.pyc delete mode 100644 MLEBot/types/__pycache__/member.cpython-311.pyc delete mode 100644 MLEBot/types/__pycache__/player.cpython-311.pyc delete mode 100644 MLEBot/types/__pycache__/sprocket_links.cpython-311.pyc delete mode 100644 MLEBot/types/__pycache__/url_datalink.cpython-311.pyc diff --git a/MLEBot/__init__.py b/MLEBot/__init__.py index 7e20d91..af0c7d9 100644 --- a/MLEBot/__init__.py +++ b/MLEBot/__init__.py @@ -1,3 +1,6 @@ +"""Minor League E-Sports Bot + """ + from .mlebot import MLEBot __version__ = "1.1.0" diff --git a/MLEBot/embed_frames/__init__.py b/MLEBot/embed_frames/__init__.py index 1e9aa95..29c352c 100644 --- a/MLEBot/embed_frames/__init__.py +++ b/MLEBot/embed_frames/__init__.py @@ -1,10 +1,13 @@ +"""embed frames for MLEBot + """ + from .card import mle_card from .salary_card import salary_card from .teameligibility_card import teameligibility_card from .teaminfo_card import teaminfo_card from .usage_card import usage_card -__version__ = "1.1.1" +__version__ = "1.1.2" __all__ = ( "mle_card", diff --git a/MLEBot/embed_frames/__pycache__/__init__.cpython-311.pyc b/MLEBot/embed_frames/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index baa5fdfa389247fff5c5770a942ce01447fa268b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 514 zcmZ`#Jx{|h5ViA7Lp#C3h#1IHiG=|n2EK)W1+hdCR?@hYB|AZOf>ajzFZc&23?aHfSewQa5x{?*;`cUoZ&;xbXz1WDv^~w69_w z%keT?D`+ezXCsy{&WgW$SLaza%$J3oQpKa6E|2z3A3U(73H_y|$=2j(a)GV`ud(#N ze=8cFkOwLi%QHe6mynoqLSE4fUZeTx(Oz_`;5teqi?irBAC!V-g^JEtab70T)#b@y zUPQ200tXcdP03KgCMqI?Wvn2ie?Ojk`yx+EP7h=c+Unm>IndO^7}sc{YNJM5RU0+h et=g#3cGX6WHmf$?d+QT_YB|0!aka};nE4G#L5jiv diff --git a/MLEBot/embed_frames/__pycache__/card.cpython-311.pyc b/MLEBot/embed_frames/__pycache__/card.cpython-311.pyc deleted file mode 100644 index 8bed7a51c09946a915d2aae9167dcbae75ac9a38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1406 zcmaJ=&2Jk;6rc5e*z3k7F)96uU{t6bx5Yu?LL-ErsVPFPQ>E&~vSh7y#?D6j5oTwR z5^W?OID!-;P|blGDxm%g{1J)d!)i}R91xrq1ui}DW@0-LikR7b^WMBSZ{GX8H$Rq2 zMFgY2^ZW2m6`|jRFdLVq%n1YL86t=%F6x}FN=E@X=jJ@Mqk39LQ;;bqF^NibqQS51 z=Mf@0HKv?F^HjSw^A6smK2MdM!fbO;B!w^Zj!Bd!s9yX-Y`5#VR39+QqiM0}^(pz- zp)N^v?r@i;I-#M>QWFe(d)oE#m%>$>;{a7SR#XKw#7&d55T|<{Ll$G^|=r|i*v^x zHi|_I<0Cc<*Ci(;2_#+(IjgTvgQy~jkOr=~3&^#JfRA2C-ly2LsZ zMxrJ#Uf$nW?>-GF3%h;hSbq0GU`HO*GweQe_$N`n3xV7Zc-M1j9~SALCm%}K6@Ady z-AfCg>d|5~DJ&hXO$s+N73t;7 zK=0fGt5@Fms{f@kE+3a`lXC5-ToYDzCWY69)xtjpDpd||C6y~l(!Mp|L#(z3mXHyc71$%a{1Oz+VSPPr{@fG R??dI6(wP(K;y*-&|1V2gaV7u& diff --git a/MLEBot/embed_frames/__pycache__/salary_card.cpython-311.pyc b/MLEBot/embed_frames/__pycache__/salary_card.cpython-311.pyc deleted file mode 100644 index b2dc696f3d7e62789662db2b36ad9a44e92e616d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2588 zcmb7F&2JM&6yNp7X6)GcfDphTuoQ%FfEYp&kr1FMn8a0x5QbKysgkv4>@3;!ZfDn} zS!~IN9#B=OsniOj7Rd*cQst6U#gYG@>uL|tN|mUpO3f`2>V;F^?%EDfL`z0*XWqQ| zy_q-fy?OgnG#X~0{rc_S`CmAO`O}#+ny=2h8V2SOgBi?9jLgokEQ5X6FZpEuj34*_ z4oU$zI1>b(Bf*>xw|p6x;V?=w@z92|Nu0IANjXFCb&*JTgY`s6mdIRIpm@VM2{7CZ z0w8L0vO8uST%)xEg?H-&R$pNjr+fYZC$07|Ge)$chBx! z*|Fc%Ij}38gRV|&S32)_-+Z`?N`4wIp(;F{oo!oudrE$MXv>Nf$kWwkuC721uQv0T zWppRs>#Y*v|iN6YOFYF4&Xe*8%+X7%_euc_4A$dfl=s=MJ%m?v+- za#Sc}7etNhL^hY2V4VtJGu;WTFL>tOB6^V)-mn;pT2`UB-z_p=v3EbGw(vP{jHZ&T zm=g)+->}W__4V0-f*=#4gHJc^m@4FyzCPp3H9;5nyr3@-3Tm09Eib6Wj3j1x>;NRO zu&DKijm0z}ysS{d3p)Qv(_!xNrzaP5UDZYtiQL(&mf~T%D_f+L6!bYQOGSB3Rm6g>Z5)PdF@p1V zi8gNp=BJf;1KpTP^3!m?*X;algL7u7YfxOF>WbLrydzz78KI;k=EaOeuGpdLjgZ=*W(W-~tqZx_72`m2m3JllO+^!RQ7PDgTZ%;N zfF>!r(b3m;yB_N)a3X}Fr5@rqvO`pw^CD$)-bfK#kM4K6E~j%!sLctgN(z`dl{g-D zAvUYpth@!ZgSx20p+QW*TQ(x~OV}KwgM>9Z==^H1=KKYn)c4x!)YZ}1I~t+dY=(+L zVfLDmEz00>ZFWM`CySX`aOMY!J`29gz~JsN2d#*lIgbnON`WwmSGq>Sg5ga_92!cL&YL zX^$$&myxkbay<|-qYr9<2QZKIn1=?e*!ea3TWrLPjXb>n@c#49E0h0lOla@&sCgt& zJy-qc=?E|$t##GVu!V+yM|+kpn7xT=XZ6C<9$-A$-SQx4U$Ty#GkXU;TEjJT(LxuU z-7lK_`_*yt#F%yb60jb1SDXojGc5r*$S~Zl|4+P3{!i`Iy^eV0dfiTJSHnGoQZz diff --git a/MLEBot/embed_frames/__pycache__/salary_card.cpython-313.pyc b/MLEBot/embed_frames/__pycache__/salary_card.cpython-313.pyc deleted file mode 100644 index 2300aae6a85683749b3857d64f6e56a88a459277..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2726 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 diff --git a/MLEBot/embed_frames/__pycache__/teaminfo_card.cpython-311.pyc b/MLEBot/embed_frames/__pycache__/teaminfo_card.cpython-311.pyc deleted file mode 100644 index 8dc33c542b5c3e2d00989099a0acd444111a0c9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7955 zcmeHLU2GKB6}~gG|39{2ZGMbx#)N=(0Grq(VB$C!V*|lhwZ@IW$QRAIWAa+9g=eh`Ca$OgnmYI^+d;H zsW&V~qU;8D6ZxcuY|>=XkL$NQM<3ComlR9{k%z z^h}|%#TL+bqrrayS}(FQjKs~bDOR#1y$H>)Dj8XLBrA+9T8*$b%AJRDb^f9SAe}%d_`BlqHBg#W(gs7hEv@}S27P+sy~u@ zlFn%!k9$&F+FtBa{%_LO^H^=YR9i#JvuJD9gh8t`cNDdGF3l?*t9d2W+>r7DH79;X zpXbuN>am)yp_&^~o=0IdG24!^$$I6CEVI>&HQZtS2`+1ef3KLl*u2TV+gruY$ z6jM|gCwCnZRgn*g>V%ACi65IT$x2L(MZ!T|A{`>(*rd|zN=^>RG9Qg&nHN?5T|+I} zJlZs&stIL(OUwA~-FuqH#NgyuL`=$Kt3+kbJ-gU6queMef(^#mXUcmBl2;YnGT8m9HW{tFg_yH8vDg2>$Z?!(-k1 zN8VOstc;A|uoxRT6c0{C<(R6B91g2Trp8A4`+5$>)e*?0#^6sTSylkPT2xj>$k!kr zgC<$ciRp)~j!0NhVQ~_j$wqT)M)1cos|*zaK^ z*|bF?cbv}9a4BX@;UgGmx( zTsFwKxDcALNcJ>=m`9#D+c*PfE=LMIIe(n7rYz|)eM%FlMt+45WM5p0#vpUVN?ETv z&Dqu?Ig^E)_y#Z!6PtE82?2D4Cx9XATMmb5sC)F0So7@U43}cyJZ6E>RwEffg&Lcw zhFi3QCiY}~2tuKv;&3d)$H(=+G!8*icN5K`MB=JuJKooIx@VB$aFXLwyk^rK29s11 zPNE(xN8{(@1eV9c=K-Oa1bzux%KcjN1oSxr1aFgjAA_v{Qct@8%ogyrd|)$~_7IR+ zl((Vkn)#C-wg1ECzwPrcHLdvCvc9&tLpfi^yqfWKKuI&{rps=fFKlsIPij0JbW328 zzF-1!m?R?K?f2`z?=KE>=Syc9P1+>m_@NlN=>qDDMwB zl_d!`A4yn;PtYO@r+Vj%U2>%?;6v^d3)GXc1NBlrt5_uW7nK7hM;QflTJ*@o;O)RGL^Yn`_(MlWM2wC}VrTN`vv>&g8r>&Lep8%kACW;WbY{^~*V4rG zW3z~U`4ix&+@w6M*|B^MFi{RcANt%Q%TD!t$n3cHA9ys&z|RJHk`_TY^Db#zo|qW`!%6knDG?axTrG#uEY16Yv*kASj0+PYc2RA>9Ko zGNevuOjz>A4x;WL)Qs*Q<=%nwwuZZ?tyug43Js$jSX#RSP12| z?9FW1o7*Pjt?Y)K_Ysh<37I=lbr{)GlfTN}meyYOEWMNEU%MgPe1B#C;0iwk^@Vc&=X3oz-|)hYjBgmqlA!KWE|He6+yQI>pG>YM^FA2Wpnv~q??F@LtO=$CAYG4C-*L94)NnE~BjIX(BV z=}3l2=BJQ2$@8;iX~?V3;JKw-vYSjsX8abCNzZ^@(&IN?%e>w{H!?S}ux-WLnDsWo zt6r#$klYar)~x5m$P_r7=7EG#4F{vLIuQqF1n z5^_w!#00;6m9OIcWM11ixsOz|%0k{i+z~uh(QPMDi})nZAJE;>Rl@JbW|pNcbp5`C zIlyFaud4OnEzS)&-}?DIcbzqPmUF(bsH9K)Hkn7jmdJe$xz=5*qaE@txAR@*v#p=0 zmxVkc{GDz7JV%(jFM}6TrlnP9^bRIki?(i?v*l_Ve((RCKUZ9LSp8)?kQL8B)-!OQ zV>TUUzD9)3Rf49@%3pau^nQFc9Z5%)_vYF&&UWCO?Zk}syaQFOoAc67m~;t(e)JQBSOC*p}$TZY64`SaGQ5uva>)2!NmB)?~p>~GX6jN6U?AdETg4hN3+Y2Kl(zOKR3f$py3N8K007+OF@+_&PJbZS&I~rQpvf zHx2?&IS&w^0(#f6HsfmoFum0Nn|A$b;)egrUBIo>AISps9nJcVk`c$~h+|CNZmqD* z_I^`=9PYXHi=7Kyx83zC_J*vzA&*$=D3fzk=(v|@?8-I1{Q1T|Zq%;>Zu!4z25x1? z=`7I3;cVkDOuT-CObqlsC^Slij4}k`M;Wk&$jG|u)68vmJ$)7|GRlgD77QnI0&tnYEYjKw-@YwH12L3-iL_$@J0e)_vKnuV=bWl2U(arT$d5 z{?wiNEuRK}Wb1cBMDn#lMDn#l9%pR>&e}%oZ-eNg%l-dMPh6*O9LQ5W-;=A}n5(YO zR`1MJ*WR~to;rx*9`Gd2T1TVMfZqx5HBB#i;f7HMMGLv!rEmc_F`bYVddsUh;G*(e zI0&x<;3yc-$V?}qcmYSCXk40#$elO_UC7x;nFf;Q7>3EA#*De-P;JIsa;QFIE;&?} VF_*mUJ%*`UIGso2|H+i9_Fuf^*$e;x diff --git a/MLEBot/embed_frames/__pycache__/usage_card.cpython-311.pyc b/MLEBot/embed_frames/__pycache__/usage_card.cpython-311.pyc deleted file mode 100644 index 8d60907546994ef7f264ad724df883c5a7d09965..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4241 zcmd5<&2JM&6rcU@*E%-jqaZ**OA!=@#!ge9ts8}=`GE3~B2rW}4GL?|IN7l4wPwbY z7)Od!4poIzTB#yZMU@i;(L;|M`v>$8DoeHY+6&x*0vAqwv+IvIKuc9B8124&vv20j zZ)WHH=Iu|hn1-Mn`|;k)!!SaBib}o6HDzr8lxqkhELq5wCM5}B87r1-E0YT7Dh^nx z9heM&9wdRBj6;{z$si72Mw1~NnLr8czHpV0Ty50O5IklQ3*VRgS7F;C(^-S!`(iw3 z6B(mMK09kN;zn|HH7i=zO9T`J?zMX$0hm-qb9Ea)+t$#3Kv=o1EJ#>gkjpX-lwt@i z$XxWT6&%E&8!+(g`ef0Jw4i{$Mt!c)njSAJ%WA#n`e%#BFpew-)?4d3)-I!`dmceI zpwaDmLv#aXd%H2?pQEh^Tdh*7&tTojbE0i~Cfe8rv<=TM5N+sRqK!Wj?WPTA8=hYv zS_lxoJm=bOekR)X4QLylUm#i$C0=<>v>nex+qnU4!}EVb%U`P_M5rv4<@HfqR&GF~ z-EKr1kVsdldeY20R6jwCnIh3gdnXDGqQTsU_Ec3lHAin#f~j?=o6O1YZLe=pL2S8O1&cnH}n~U&k{+W}MdFh1w?$H>|Y&R{uHpB5vpuCALYZD`_oEFL<;Q?zJ~T zTthR!b!XcbB;Mdgv2$Aa@LTyx#{{~CZb@#}wEvN&z2%-}oSONWTe2%B`&{WF6BMuO zkB<(gr_M0=##0$;8u_UaCtI{hp0lYVCO=xtOr1P2`k}+8;GD|9gNPGs8a9ee*p%;B ze8*&A{;@V-nT*3i76wZ@*O>+^Cu>-2Fxj+si%Io?SVXHx>K<&0s=n)yf_C=*vg>a5 zflBv*rp43NpbtvOu9^n`Zs3Ap6^R=>X)sRci&;7fha{qLAvS}kEfW==fp&rTr>X50 z4Mk|vEg;nfJ?Q4)5e@4gp8nk^i;56Ws8pPQZh)KIf?bUX z%ThO(6L}(I;t&9m(7i3_YR$l+yTU8w4)N*xr)@l zkW>xFzlnVv`+lz4zP;M9SNz+5+or z@UPR-FSyR8f-Tgn<8V`72;fLrg8wV6EQ5@e6_9OZ6=bX&02#R+g`6!u?y3wD8aH_A z#L#D>r`>=Ty>1)!GXpr833FqtmKyj~jf>pC*MmNn(5MG?W;ul7DuY_uB)G3ahCFxc<5Wr!Q rR7Ja1TCXbFzS4SCQTIygwHoY~Mx-TRM)7=#oRfmm$6$E6^rrR~rqhx* diff --git a/MLEBot/embed_frames/__pycache__/usage_card.cpython-313.pyc b/MLEBot/embed_frames/__pycache__/usage_card.cpython-313.pyc deleted file mode 100644 index 629fabac732f4c593d8439f6cd65fe3d64ed6913..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3293 zcmb_ePfQ%w8GrL;|HIB2Fvbv*JUK+k5)1@z3CK>ez!f;fxDiaeG3D#GpjeV z7*@68o?`dn^bnP!Z*8R>@J{6NT_0@dEejp z-tT)e^Ly_w8jT>hcK-0g>bD_;{vnLU<*O@)Z-ep)QV~O{q~c#lO!6dV*po7oRe2Dp zzCkqNQ~h0N-N$@tpbIhoDvk$BpU)eX&Bz?pR*RHOT~iA-a~+bh^$qGa+(}d0ri`eX zsj+P`p|Nb_Rgx&WYc?Av2D7))%mh!e$O^VY$4@2_n$OTI$rUZ#HEfIIU^1#Xl-N0< z={j{BGC*{bYL;nOU_qHXlsO{xt^UFO(a5Xd+4NL$jTn~UM*byQzs18jrdj%$;ZPnd zcmN$8*H=Y|Cb(s|4==%5eS(&S&%075v+BO9C*zH^t;d8&1y2}tbrDwF5Bhbf=1?(s zWZ_X@y@zGhuLj2CRJfjgLa4!I@68HV>PS0INJWqJhIYNKC}e|<@QF_qTE=eB%Z$@UJijg>&SZoP3sxaw4I8^t8h+eE}juh`>ANW3g?98 z(izcooQlS)a8770pAk*xsc5_k=Yj_J!1q-9rgCT2;$62&(IhkS9CQgCX__9#Un1)WWA_4ep!|9Kx zl#;y7DA8Q9e3a+~@4d0+x&>!^U?4X*IMScd^o@+Em1t(DU$^rEIm4s_jVG>Y%r$bF z?m7dtNd^i4$r&)LEZyo~bMt0A5|_DO7hQ0_Yq%!mOMt= zxX-0qZYi8xvt64krslvU7nxb=>g`p5pn-qA&J(6-KLDPk4s;&BKWRgU)EOr~Bflo9 zt65pt493aZVExi|HFKQ2HFTd4G7VUKmX@d7zp0r;$^(W+@Ej-ScreLm-k^+25sv5Q zcwk<0Trly(91l&bY54-+GpL#6zIEHM zct<*2+fmX*@!~}}!&*(J<31*K6eczZ?)P>!E^Rr&gq{hXofuzPbYS~h$uL8+Rwiw| zn5UNOtjrkhY%#MkKR5Mr+ieK1KpM8OJatxTGwHCL{e^8FIX0PK=2p0!PT05}qkl#( zKD+nZd*3VV&+3P2N#}6y7y}PTtw|}Aa-4@IBF zp2qfDy7pTy{XYJ?_?I2uTzt{Jdu8E8_ZNx3b&q^@`|sVOUv*cmEbO)>_uDR4gD8CY z0LkI5YUF=wbe=ie5jcK;ntx(PJ>+04c%20SiK}9br-@bHOVv)z{d> z;eg|Z{D7Aq6eD;-M;`A00-nf`r>UOPd^Ah6?vaL@dQe=s#ijc^Q2Xn+&jHfqf#h6b zX)09;0=WzU<#50EQ}E_&jfCLmq>dLI7SX6vr=yxba!g4);tFo6`DO64Wu{Xi(h{g$f9Y)sE7!o4zH4Kl@epGp~`SDsM_R8}>CHDGWY@`w!`Rs$; z*gIwA-7g+|x%tiNS09$|(#lM(JiS_8waRv}y!o(VKdK`19$vr)2o|CuP5c`+A{w;$%{1v1w$zIL> E0_8yZHvj+t diff --git a/MLEBot/embed_frames/card.py b/MLEBot/embed_frames/card.py index a8b4fa3..5b22649 100644 --- a/MLEBot/embed_frames/card.py +++ b/MLEBot/embed_frames/card.py @@ -1,3 +1,8 @@ +"""general card for MLE + this card contains franchise and color information + it should be used for all interaction + """ + import os import discord from pydiscobot import frame, EmbedField @@ -6,7 +11,7 @@ def mle_card(title: str, descr: str | None = None, franchise: dict | None = None, - fields: list[EmbedField] | None = []) -> discord.Embed: + fields: list[EmbedField] | None = None) -> discord.Embed: """get generic Minor League E-Sports Embed 'card' for consistent formatting. Args: @@ -17,6 +22,9 @@ def mle_card(title: str, Returns: discord.Embed: generic MLE formatted embed """ + if not fields: + fields = [] + color_str = os.getenv('MLE_COLOR') if not franchise else\ franchise['Primary Color'] diff --git a/MLEBot/embed_frames/salary_card.py b/MLEBot/embed_frames/salary_card.py index bfddeda..9e18556 100644 --- a/MLEBot/embed_frames/salary_card.py +++ b/MLEBot/embed_frames/salary_card.py @@ -1,5 +1,10 @@ +"""salary card + display player league information + """ + from pydiscobot import EmbedField from .card import mle_card +from ..services.const import SPR_INFO from ..types import Member @@ -16,33 +21,29 @@ def salary_card(member: Member): Returns: discord.Embed: salary card for specified player """ - p = member.rl_player.player - m = member.member + player = member.rl_player tracker = member.rl_player.tracker - franchise = member.franchise - - title = f"**{m['name']} Sprocket Info**" - descr = 'Data gathered by sprocket public data links.\n' - descr += 'See more at [sprocket links](https://f004.backblazeb2.com/file/sprocket-artifacts/public/pages/index.html)\n' + title = f"**{member.name} Sprocket Info**" - eligible = "`Yes`" if p['current_scrim_points'] >= 30 else "`No`" + descr = SPR_INFO fields = [ - EmbedField('MLE Name', f"`{m['name']}`", True), - EmbedField('MLE ID', f"`{m['mle_id']}`", True), - EmbedField('Salary', f"`{p['salary']}`", True), - EmbedField('League', f"`{p['skill_group']}`", True), - EmbedField('Scrim Pts', f"`{p['current_scrim_points']}`", True), - EmbedField('Eligible?', eligible, True), - EmbedField('Franchise', f"`{p['franchise']}`", True), - EmbedField('Staff?', f"`{p['Franchise Staff Position']}`", True), - EmbedField('Role', f"`{p['slot']}`", True), + 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**', tracker['tracker'])) + fields.append(EmbedField('**Tracker Link**', + member.rl_player.tracker['tracker'])) - embed = mle_card(title, descr, franchise, fields) + embed = mle_card(title, descr, member.franchise, fields) return embed diff --git a/MLEBot/embed_frames/teameligibility_card.py b/MLEBot/embed_frames/teameligibility_card.py index 4c6caad..72be93d 100644 --- a/MLEBot/embed_frames/teameligibility_card.py +++ b/MLEBot/embed_frames/teameligibility_card.py @@ -1,6 +1,11 @@ +"""Minor League E-Sports Rocket League + Team Eligibility Card + """ + from pydiscobot import EmbedField from .card import mle_card -from ..types import Franchise +from ..services.const import SPR_INFO +from ..types import Franchise, MLEFranchiseTeam def teameligibility_card(franchise: Franchise, @@ -15,17 +20,13 @@ def teameligibility_card(franchise: Franchise, Returns: discord.Embed: team usage card """ - title = f"**{franchise.franchise_meta['Franchise']} Slot Usage Info**" - descr = 'Data gathered by sprocket public data links.\n' - descr += 'See more at [sprocket links](https://f004.backblazeb2.com/file/sprocket-artifacts/public/pages/index.html)\n' + title = f"**{franchise.name} Slot Usage Info**" + descr = SPR_INFO - fields = [ - EmbedField('**Season Slot Allowances**', - '`Doubles: 6 | Standard: 8 | Total: 12` ') - ] + fields: list[EmbedField] = [] - players = sorted([x for x in franchise.players_rl.all_players() if x.player['skill_group'] == league], - key=lambda x: x.player['slot']) + players: MLEFranchiseTeam = franchise.players_rl.by_skill_group( + league).slot_sorted_players() if not players: fields.append(EmbedField(name='**Error**', @@ -35,10 +36,10 @@ def teameligibility_card(franchise: Franchise, ljust_limit = 8 for _p in players: - fields.append(EmbedField(name=f"**{_p.player['name']}**", - value=f"`{'Role:'.ljust(ljust_limit)}` {_p.player['slot']}\n" - f"`{'Salary:'.ljust(ljust_limit)}` {_p.player['salary']}\n" - f"`{'Points:'.ljust(ljust_limit)}` {str(_p.player['current_scrim_points'])}\n" - f"`{'Until:'.ljust(ljust_limit)}` {str(_p.player['Eligible Until'])}")) + 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) diff --git a/MLEBot/embed_frames/teaminfo_card.py b/MLEBot/embed_frames/teaminfo_card.py index 560ed61..f4ccc5a 100644 --- a/MLEBot/embed_frames/teaminfo_card.py +++ b/MLEBot/embed_frames/teaminfo_card.py @@ -1,3 +1,7 @@ +"""Minor League E-Sports Rocket League + Team Info Card + """ + from pydiscobot import EmbedField from .card import mle_card from ..types import Franchise, PlayerRL @@ -14,9 +18,8 @@ def teaminfo_card(franchise: Franchise): Returns: discord.Embed: team info card """ - title = f"{franchise.franchise_meta['Franchise']} Roster" - descr = 'Data gathered by sprocket public data links.\n' - descr += 'See more at [sprocket links](https://f004.backblazeb2.com/file/sprocket-artifacts/public/pages/index.html)\n' + title = f"{franchise.name} Roster" + descr = const.SPR_INFO fields = [ EmbedField('**Franchise Manager**', @@ -75,19 +78,19 @@ def _team_salary(players: list[PlayerRL], fields.append(EmbedField('**Roster**', '**`[Top5/SalCap] [CanSign] League`**')) - pl = _team_info(sorted(franchise.players_rl.pl, key=lambda _p: _p.player['slot']), + pl = _team_info(franchise.players_rl.pl.slot_sorted_players(), const.SPR_SG_PL, const.SALARY_CAP_PL) - ml = _team_info(sorted(franchise.players_rl.ml, key=lambda _p: _p.player['slot']), + ml = _team_info(franchise.players_rl.ml.slot_sorted_players(), const.SPR_SG_ML, const.SALARY_CAP_ML) - cl = _team_info(sorted(franchise.players_rl.cl, key=lambda _p: _p.player['slot']), + cl = _team_info(franchise.players_rl.cl.slot_sorted_players(), const.SPR_SG_CL, const.SALARY_CAP_CL) - al = _team_info(sorted(franchise.players_rl.al, key=lambda _p: _p.player['slot']), + al = _team_info(franchise.players_rl.al.slot_sorted_players(), const.SPR_SG_AL, const.SALARY_CAP_AL) - fl = _team_info(sorted(franchise.players_rl.fl, key=lambda _p: _p.player['slot']), + fl = _team_info(franchise.players_rl.fl.slot_sorted_players(), const.SPR_SG_FL, const.SALARY_CAP_FL) if pl: diff --git a/MLEBot/embed_frames/usage_card.py b/MLEBot/embed_frames/usage_card.py index e693bb3..f18ece0 100644 --- a/MLEBot/embed_frames/usage_card.py +++ b/MLEBot/embed_frames/usage_card.py @@ -1,6 +1,7 @@ from pydiscobot import EmbedField from .card import mle_card from ..types import Franchise, PlayerRL +from ..services import const def usage_card(franchise: Franchise): @@ -14,13 +15,12 @@ def usage_card(franchise: Franchise): Returns: discord.Embed: team usage card """ - title = f"**{franchise.franchise_meta['Franchise']} Slot Usage Info**" - descr = 'Data gathered by sprocket public data links.\n' - descr += 'See more at [sprocket links](https://f004.backblazeb2.com/file/sprocket-artifacts/public/pages/index.html)\n' + title = f"**{franchise.name} Slot Usage Info**" + descr = const.SPR_INFO fields = [ EmbedField('**Season Slot Allowances**', - '`Doubles: 6 | Standard: 8 | Total: 12` ') + f'`Dbl: {const.SLOT_USAGE_DBL} | Std: {const.SLOT_USAGE_STD} | Ttl: {const.SLOT_USAGE_TTL}`') ] if franchise.players_rl.pl: @@ -57,9 +57,9 @@ def usage_card(franchise: Franchise): def _player_usage_string(player: PlayerRL): - slot = player.player['slot'].removeprefix('PLAYER') - dbl_use = player.usage['doubles_uses'] - std_use = player.usage['standard_uses'] - ttl_use = player.usage['total_uses'] - name = player.player['name'] + 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/mlebot.py b/MLEBot/mlebot.py index d4220e0..4ffb876 100644 --- a/MLEBot/mlebot.py +++ b/MLEBot/mlebot.py @@ -1,3 +1,6 @@ +"""Minor League E-Sports PyDiscoBot implimentation + """ + import os import discord from discord.ext import commands as disco_commands @@ -23,6 +26,7 @@ def __init__(self, bot_intents=bot_intents, command_cogs=command_cogs) self._sprocket = Sprocket(self) + self._tasker.append(self._sprocket) self._guild_ids: list[dict] = [] @property @@ -69,16 +73,9 @@ async def on_ready(self, Args: suppress_task (bool, optional): don't run periodic task. Defaults to False. """ - self.logger.info('MLEBot on_ready...') if self._admin_info.initialized: self.logger.warning('already initialized!') return await super().on_ready(suppress_task) await self._build_guilds() - self._sprocket.init() await self.change_presence(activity=self.activity) - - async def on_task(self) -> None: - self.logger.info('MLEBot running on_task...') - await super().on_task() - await self._sprocket.run() diff --git a/MLEBot/services/__init__.py b/MLEBot/services/__init__.py index 4eefec1..5463097 100644 --- a/MLEBot/services/__init__.py +++ b/MLEBot/services/__init__.py @@ -1,3 +1,6 @@ +"""MLE Bot Logical Services + """ + from . import const from .cmds import Commands from .sprocket import lookup_franchise, lookup_rl diff --git a/MLEBot/services/__pycache__/__init__.cpython-311.pyc b/MLEBot/services/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index 53d4a8133aa82f05db691ec2761b31f1214e4845..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 432 zcmZusy-EW?5Z=B27@~sM_y*Sm(pZRCB#IU)h}awp%jIs>Bl{C}?*fWN-oi%^pTbv2 z8MhK_vahKfXp70Z21W5q1mxQ^WM0s$H;_w^8;}}~%Q__?`qMi~WmAQ!>yUmpvR6}~J zQ<>cthS|nyqxqxXX+y@IcELe=HTX3I0GDyYqNQNHOyp)M+sF*H$_A|bJG4zoM@*YS zNlI-FXku8g?l&ic^ca#Uo@ z5K0SKl$5rI4~ytfsJ!CrNN<5<{abSdFmo}+Q?xT#uPNG_tk)EECjZsP=I*#T^ZceW K4(rq{aQy*lTXoU^ diff --git a/MLEBot/services/__pycache__/const.cpython-311.pyc b/MLEBot/services/__pycache__/const.cpython-311.pyc deleted file mode 100644 index dd665d788cb24928ba81672ecdaf6fd71b46dba5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4343 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 diff --git a/MLEBot/services/cmds/lo/__init__.py b/MLEBot/services/cmds/lo/__init__.py index b19f90f..03cd898 100644 --- a/MLEBot/services/cmds/lo/__init__.py +++ b/MLEBot/services/cmds/lo/__init__.py @@ -1,3 +1,7 @@ +"""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 diff --git a/MLEBot/services/cmds/lo/__pycache__/__init__.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index 8eb9ddb114782ae2724614d828c54c90334bae9a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 968 zcmZvZ&rjPh6vv(4Y11?Kyu9y?^zetPfo{GRP^;%~lRM=;#WUst<2LO*5lZ}^XJ{{!GF zN>HLP#5JsGl69tY0~?ABW^xN#icMy72Rn)_=JFb@DYjXid)QO#um<`%<2vUTj$~TE{CD=Dgkf_PT;!=~4kGV86kR?3TBa|}H zkclDaOvp!F+PTsvnljO6BN0gOqG(D)PO~(MmQEDKj75=XLFqfX2ag`!AHNJQbLa_2 zg2rk1CL2#UNsByuM~jomC_MY{_9!bt&d3M?bWZ7*Mg3aZsqB&?FFh>iMAG1TX#-n z3!Jb{;nSY~Z(rFc=*_*AseAg|S=Eu{m!?yic4@lG0P3Y#Qw#tl@b+q_+0f^%oMIP# F-5)(r2A==` diff --git a/MLEBot/services/cmds/lo/__pycache__/achilles.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/achilles.cpython-311.pyc deleted file mode 100644 index 2c02dd55a0d2759a6434eb5e1506bb0ab263bc6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1457 zcmb7ETW{P%6dqr9v$=73KrKRAp>|7D;aw-b>{cwO4XDsUA3&67Wy!KVlf;p4c)WxJ z9-_8F{DUCz!c!1R|3&{mL6N%hQzc%?n^(zGpE%=PC#(by%y_;zXU?(rJD1sC7Z)1{ z#*@!qZ2in5^e0oQAx(wTTOb@Fj4&6V0o(Zih1`JSm@EW*C=3J+@#r?f;v>RfK(R5^VB!XYAPz+p7tF*gu+ zM8utn8c@N)WAO;6AH$lA(nGGaBM^=&g^{c6)GKw9$MPd_)?-#1%;6ePujlof-nJhE zgzmopJU7WG5@VOW{A<5@4p7P61j^OyHFdY=Io&m{<2jvHzqJ{sp_BD@iJP>( z9y)=a?e&A0QYZ_BRM%5NlQ^OzU!XwA4&m6@BCH6M1e+;qb;|PHY`(m+e#82bl9XC* z>N}D3VeAdd_^sQ1_VLiQKKt~eTXANE0dZji&u@H>P|FK3wSw5H3a%u3X$?NP|7z7B zN@Xg4&H~#DmbcSIwqUWFv#qLq@hnKv?a}J(-z6+ zKe_1EGSuHt`U|2LK1>v*0XnJCKu=e#{!by0k5%CyePo=7-!s@BmfGPXWnS^tRN#`NHtiLx&=lkiKU|{!*CIAow$Bod^K{-dL e`E>Rfp?9CoK7~BTv1tAsf28Bma`qqglF{GgnQaaL diff --git a/MLEBot/services/cmds/lo/__pycache__/adi.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/adi.cpython-311.pyc deleted file mode 100644 index ed68773fe8689ba018bc8c93ae30ff182e3b7148..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1299 zcmZ`&&1(}u6rb7IFVmWXic+OCD1HPn7LP@WO%)ZtK+!{37&bf8uyH@)P9n5c2v*di zw|eZUBK06%^v`G|VAzu&NPBWiJoV(8&6h!av-6wx+xKSny?Jln?%$t5AV2SJSAJlG zzDv>81_&}u8N0zT*6rD#8Z4yA{Z&?Dq`w$#57LU2|^n%yHZjazK8Pq zj-Y?}9Iw%``B7-xkW}hx7OnO8U?Nt{9W5S&bJ1!iX%;0D6- z_HhL>WeF@#m1SIo_gz_os{JjDto?c3z+qWg2mg)1o>8^}^?X7epjGlhm9>S$)!R$P zOq%7)&LKEv59jhEHX|--VIX)sE_lGG&xNonJXWir8-QIE+*^obXT=6hcMBoA0j_E9 z%KVIVU+_p+V6X%0a_BUC9@K?()vaG^l&xDguN1?&<$Jsg3wSR3bqn({-WsiT&p8t|P|ABlmKboJ9 zY)#L#rsuY@#a6bsY5w==z>(0fnb@RMmXFdnL#ZFKh9~uLN*^_BuV*nRWuZf9Br}bT zUVFy}8DFp)UY*u>kq*31N{O0%ez1jZWyy>^`@iYR{i^m&>7@^<7 z>6_XkCj}t;h#-PJWQbMrkdF>;93xgjHT0JCd`LU0k~v_HaK z%i(CuE|)DQ^nE)Z9DI4+Cr&RpS(Ki0vIArvQDk6(42fVvmJn4o`_e!Pkq?!I1V0C} zYe|OI_ZmPB`i>Bn+SL2*xeHaY%_X<8TrHU| zo-Y={is^f_1Pf%oa~;Y}$0ywMLbFuMm$z67KDMnEYWY$v`S>I7k73kNoJJE}&Rje{)zRNlEgGVXiawlXY8eKVWQzHF4VwYh*yCgtdssse#I{@{; zh$XD*F%qC5ZK`gz)N5ZV?OP9G8EX$a2;fw}FLg%5W{%X`E%kORN$NaqPtUZb@5i#> zF*?VNjI=wD)lvJqW9>ZJ&Fsn(f^sUmDg-9=-Y9P+H9=?c{m$e?XYv*x6MwL7Ia7iS zN<7n_bdU2XyAA@DpnzSXA?W{5;u7BBvL`6GeXk D=6)L2 diff --git a/MLEBot/services/cmds/lo/__pycache__/haim.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/haim.cpython-311.pyc deleted file mode 100644 index bef56c5d4d412756eb11072f079cf5533b6078a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1401 zcmb7E&2QX96rb_eZZ=;?98impR!B{{hj_i~_1dWjh!$uHw3nzktSnhx&urr0uX?-{ z4IHAjLi`8e$SDYgzk_o@ky!bJ#HHN4N>4rU#=A}$2@cG7-u&K&y}$R~%>J~z+(0mP zcV6y25)k@}E7g$a;QTg#V?+?a0UGdK2vCRz7;`KJLMRSIj0AKS5$QQ1G8Iomgic}2 z#y}!?2ep*Hx%!}m^CdS)94`(-HzEvl@lHs*d2qQ&bLIRgfMZ0_024G22p)(6qS8S{ z4X6XkYYi1J*z8gW${ERY&dujkUX>xYNSJ2$sK zzIVT+HkvSUX`dVGm9Zt{Qsjm-UnG=ysh?zi9Oa9Cl+o1nxZ+)q1PZ)9&tRQ@4&n=h zA<=K~0pQvA5m)HVo!}F&pI-Q$K@YwWchRxGp=E<&8*3vkVV77;9Tl(0WQ)5=V^YON>Uu2`OXl9_6KG zG#I7)_%dGAX7lxDcW&7a8BLizOno=9KZ(6@nWcT#&$h=y`~H`o-i|Xn4CoLxhtA6@1~kONo(-A!v)@Om7B_+u)+2c<`F6yX!ZKp%KLw;w0~b|7Xn&-C{9JZ zx+}gyTowo~{C{~4--o-=$m@>7&yeHf4aW&%G7dOia-2tFH<){BjzeP4aZ-MS^4k0+ zqgDBs2|u#^AuQyf$xszIgBQ9F{5mjMBH@{dAQJeqTz#0ecGK=wcQEmg|rf z2n8wEkL)w~+7FK>Yg!>0IOJBE{GB z4`${3Fnt#s{06ZE1SEm+1g#vEbAp;Lu09jA@#5-JC=JZB`FHufJ}o`3{=;7~`UjXX BQeyxB diff --git a/MLEBot/services/cmds/lo/__pycache__/hoos.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/hoos.cpython-311.pyc deleted file mode 100644 index dd284117f05c96bd903c00809a1ccadc7dc56f33..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1224 zcma)5OHUL*5bmDWF3ZbA69dL%Vgk4amjea^F(v{6zPJ!QY$oXp)6MR{Jl5`662Joy zV*Ceq^t8s{k3qAEo4IJ>1#h!~6DO-@ch(>tte*b5s;heDtE!%lgM$SGvblOv`Hm6# zDUD3@u5vmD$|0hNVh@$&s(8r9WsIe+dWx@>Rg4t0gs66asLs@eiqH|vj+8ZuS5eXU zCC!y0jtA^ojX0t2+W{3|tMfi}y20t9b&bb>1vzx9EZcrgrX7gya zV1mS2Rr-ei@)ea>3v8do{gerZyS2y-gSg)fBF1e;8cvTg5R%o}hk5!0;3>k8_G`QW zdPhD$cPnrsc+ZL4^A`V0Xi2>1OroWdL7?iJ!ZA+jrk#05h9kk&n^ z4-oRIZhKu%ju09;gmBrEIM=J#Eu z;5;9eAn#)eQW3iVd%ck;SkFT?00uHf_pGKq@_wr|elJn6G1Eo>kgzfHLm!jAu`l{W zQ=do_RiAE|6HRj}Q9(^m25l7?w;`gfXD1UQ^QBQ8UXn>s=hr2(G|`*2?KBx^S1D^( zNw%v*0J;1rJ9&&qt(V(*{(h&95BLpm$jd0!0f0TmEi|;3jutYHFFq}F=lJ527zGS@ QT!wd3ZMl2)4_C_LH#?sy?EnA( diff --git a/MLEBot/services/cmds/lo/__pycache__/kd.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/kd.cpython-311.pyc deleted file mode 100644 index fbc24a0be6e6f5102eb8b364009ad14275fbe5dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1130 zcmZ`&%}*0S6rb78(n8g6QKF>@+Vl8ZcSU@Uaml|0#yF_O?SqRKv^DwA6>LPs$AVki`^qN4U& zm@7pbja#)Eu>;Sue9FO=mpp0@f|E%Z7`=C(93X}aOpzf`Y{(K~%4SLps8IQ<(vskF zF#3{2XsOi#g5H?$&dW;= z%$J;n+*}VG%Qwq`-SC)S=jO6gf6`btUp#-j7}QPAW$Q3OTuYOTA`&aDjpJF062x#L~#~fnri27{>aaL&(D2RPxABa{QPm|zr6=r;dNxA3?ZVb zghW|Fynr@bp^p>tx?#Bkk4^{;Y(hd&)JPw^rD&3}s?~7oq{c$e;T*1jM>=)59fUN> zJ^gz#3G+h~UV;7LWdY*FBp?*N3(y?4Si)*1MglaXO*PH7dhJuabL&nlW9@Di0h|f= zsm=)B%!xYNR%c^LQWrYYGwtd7u?%XAQs~M^yA2UtHQgC&sV|A@^Ab#oYIs#JOA@_N z+fI^!8!4qXQW7^(B7mXzZTfjk32m6$dFElij*r6Y;1G|AR{?-M#vPPzCar@C$D_T2 Z?j4WzSR2ET$3^%s*%hm2|8ONd{s61_42S># diff --git a/MLEBot/services/cmds/lo/__pycache__/kunics.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/kunics.cpython-311.pyc deleted file mode 100644 index 7f43189f34af5e4f5d9eebe13118e0a1a2400fc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1213 zcma)5%}*0S6o0c{{i2}pqC}GhgIE$uJa{q2184wIAOSfvn{3L?lq~K?-D!=0hen9; zKft4>8iRj{CnQa5_GID(Z`+g;C*SOLYZ^TGX6HBWYj%Hc-t6amJ_{(P>u1~F2*594 zB%=0}-W)1NK!Fk)%3_sla7dXDq06@9$Yq&;1PefwL!c^?TQa~g&REI{CF@YsehYJ@ zNP>|{t!9{><5XPA(U#{OYW9<(Md=&8cc>fzgEFB|mMAI95-?>ap@vkb{8ed5_&J=h zL>XFQwNN=u3_&inlTOU4M72X@IAhoEGrwXA!yHgV0^)QIaQ2=eECazZ-;y2FWBG*LxGa>k z(0@FL9bgCcOqjMqGuvtWcX zml_V^yt2(i9h})!KZoDvf^wm7u{i%gf5Dm0^)273xcXzyY&guVbA7?8FE+OH=g*$Z zd3D{f*%mIC`PwoW*G-3V-S+hOq)N42KZP&c%g3i#irwN0@->7r98CzbFgD&Eo%k_2 zdpbHBf^sk21!XLAE&$EUe?t%7i5D&#qzyx~#xR1cVK^Rb*g_vMj8~0{-H)UUgLQq~u3MEa=bS8@JiTk09 zY6uB*WzgNm2E$3uaNFw`#jlG1#_J(zcRlTggE5#h)10F)p-0 zZ5Qc>@i{*7Z(=~)NM1!i_Jnj`v>A;K6i$Yp4$PbkKcSWk_RY+1-kbL(`+M(AK2JZ+@FYFWidK}(3H_7K&W+E5WXfYp<7isCg? z)PGBJwTPo}D-4Mpc%J1`0k&H5sND}vwp8Ef?trq77%F3m$_mA0RY5GZIiLnqsQNY4 zP~da0dXhxw1FHeb!N8E@(wmuqSy8CIml};2waQ~O4b(G{wqk`Y+dlv7<@G|z@-1o^ zhLJ6RM4FelCPrLEB})00$D$l%!sbp`bpk)iIewLK%a(>cBn)(9t#)B`p8z~X7?OU6 zH$m^oC+N+;LU9Am4~D*7=*L_21=?5Nr(|z{xU#xEy-;zj4Psjhg|&e5Eu%0z0FoKb zL>TyjMdO0`lz2=CYlBJpg1HqgpNEi43x$)V(t`P1FfPn>?pVJ0Ah2s5^Q*#Ka;lGN z>*mVx!^NO#dM;as4I*DVHWQ}pQDM4)nbgS$w|E+UZ#$P%)JP7KuiS;xBN%lQXVK(z zD}Uuje*SxYzIk)`Fu&5uuQW4f&Mw@ArDsfiH-kgZq3^f_!cXNJZ=b z)Q2Nhu$GCD1Rd$qb+e^SeSFitdOKFJey4)~jwSrmW~FcTP@8LMbFrdmtGGRLtu=Ei zR;3=JQ|zcnzXnkqZE!o*Prbd!9z98@sPT)EV3P0Ua4X3PT4W4bWXKj71dz+WUN4;~ zsSQ&+$=vIe^FF@>4*BRr2mnc7+(!9>Nxn9Gz`+qd7WbeGJQN{*NC@oy2o|hCgBS E7bj6AVgLXD diff --git a/MLEBot/services/cmds/lo/__pycache__/ondo.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/ondo.cpython-311.pyc deleted file mode 100644 index 1ccb4306cc033dbc06e80917d61cc228867aee5c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1414 zcmb7E&2QX96rZuZ-p%HtMO+{PX@%4cIk0QT&U#(3AhkgGY!3wvD@)ecGn+X03y-%Y z0imd^5dT3qatcD>k0~gKl}|`q%FV0v)Dv&Ky9tr#fj6Gtyf^RJ`n~sN{L|`c6M<}e z`ef%@38BA)v5?waIlBSM5u%9V5DmpDg($*9jD;?TQX~&$j3jgmQRNY$H6|a+2%W&3 z$xxyAF4|Q87Uu0uT&$2Zb^RoYNK84{^390)^WbVx=Em6rP>v8oLrl?7qIf7vh$(vu zYCwg`zbMB*{RHMrst9#q9fNYRFa){!USna7B&t4AE=OFphD)>#)Ek9zC#K24N&+|v zvStgUP=r;~=X;w{p~NI&#WH2w&w?}$lDJq7;+$o~7lwaM5eSItJcN1nA;3onLz>^< zJ$nC+sGJClbEw& zg|nEt5p0tkCMwTaILelxR$LUh)q3{c&Fju*oMqhcvVg?SJBdH8l6Gzd`TJwf`Q+nw zZzQ=Bh0KEmBJTx0{Wl!$oNp4c8S%%5{sT>qZG3heBU*T~VC5r|!Bm z@m)6)C#tB=uT}9PWg{{Ub2nvK6mSmbj~Dg%Styo2xHy(oia(?51$aNZDnMMG3WUNR z034o=Qo^+cqy?ZNb^Xvet-buiy-B-U%2?H>2mn%7^*?LZgzws~wT;u-Mk&egTbQ&q zPFqGP3%x`aIF*t5Iz&xti?>Vl!aIwc(G!F=YuQVJV3qHy>3)?H^suw&VMp|^;{s;l zM>0!iU1;YiK5M)=E9YC;E8q|pil+dO1jZAzc36!GYCXREOwb#TFF&Q)#3Gyj#_x@3 L<$3xXf5qq@8aG{t diff --git a/MLEBot/services/cmds/lo/__pycache__/rexton.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/rexton.cpython-311.pyc deleted file mode 100644 index acaacc8cd1f82bf273dfcf5b07f01dcd0bf4664b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1461 zcmb7E&5zqe6d!*koBcv?KrKRwR%%KPII)vBc11vHK`mcJqJ=|b$+A6@jaUA{X+i|G{!W;oH}B1Rwtw$q{Nv_k4Z-;2 z;q%?^d4&F8%5q2x;ruoT#|R_L1!%}tK0qNikePlGt7xhE9Yj=)#>4dkg1c5f{q&V-SuJK|>CsA&6ff)Jy#&^W!LA_oIxYj>i=5k}?ot*?I!= z`~!dw5eH#^&Fz6)jvu(C^v@M@C$N8dX?p?{`AYa49Sh%xtU5rQzkm4GILi`xtJMk# z_8kTL)QeLLHBcgwwVW(-ym3gPjJAx9W~ipo>FBDeX`QyAwoFskHM83_wXW9g8AeZQ z`JuB*Y_(^rN)qk9L-q%{-uH;!RvSKLvTJq?wJ{C{xZ`z4?NQ$$?RLMb>V3!14D6b2 z&($$;4c$;3&Fi}()ipb$3m-T79j))Eq~j6CB#l916sMt+4IU9UX?!tp0zW$#1Tm!n zp|tVH$JuzGs(rIDCjRa?8|bPDiLa)FCUHbbzClTZ?GT)SGb;%t!6;=-Pg(Zs^%wW< z+_FBSB&C*{`c7oM8+(%?8`fPvyEk#Ik3V|vcAQyZKwMbB^Gn|&)bc`1tsu6_;w#BP zT7efHZkCOrl-tFJEZBbz*Abf4(DwDSt)1VtTEA|!W(cW{I7evP6)y^eoi$hdzjzMs zhjUfStG3NL&bITKZHFpPaD0N zz{&kN0=QuCyL5xuZu}zMJdNyP?C*?*@D=NgkPLwr$vt4!zH>1$wM=Zz)+5Cq3f l(biFMO;P>X>ODnoK3lzK@*2mY`FH&G!Mw0s{D-|@^fx?bZKD7H diff --git a/MLEBot/services/cmds/lo/__pycache__/riz.cpython-311.pyc b/MLEBot/services/cmds/lo/__pycache__/riz.cpython-311.pyc deleted file mode 100644 index c5efde691b2d2cdc0013de5b367cbd66072672cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1140 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 KIX+`O&g>s9DfXWL diff --git a/MLEBot/services/cmds/mle/__pycache__/query.cpython-311.pyc b/MLEBot/services/cmds/mle/__pycache__/query.cpython-311.pyc deleted file mode 100644 index 22bc81da7eb24245351a20a98f76f9451ec27b1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6475 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 CeIlO# diff --git a/MLEBot/services/cmds/mle/__pycache__/rebuild.cpython-311.pyc b/MLEBot/services/cmds/mle/__pycache__/rebuild.cpython-311.pyc deleted file mode 100644 index 863e4900950585c0bd5c7221d9c0cc16439a8b4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1803 zcmb^xO>f*pbjDtLoout!X4}v&D${C7HfpT)f&?lR?SfjqdZ2~FV##tmv#le4F=K-g za){Il^#@c62M*y9go-MD$|hPVE1!_Kv^TGkQ%=0`esv+VMdEqp&3p6SJio7*UuI^e z5x`GBKf9O82>mITAt(c4{{|47h#-PJ)D)}aAs;s}7PRb1zTA{ClF&^=3J(!csJsof zp1|xwvq112R9Bx1_I4eo$LuJwoY42}fNA5jX?_Y1!-}}Z3FRS$RMIpZDnf6wj@$~SeVp!8P}X9 z8iYBO7Qdj&iR+QIvjak34rAS?v8@w3wvA~WAT6@K*5TaIT6HNc1h!Ap5~18-ZWOy= z;1$5Tuk}aoK0V*zX~_*@%4|nSoKgIsA@cJ8=KdVS{VBqb!*#p@8-3UsWn;J_*2&l& zK)Jt{ze1bx4+V_Y(aOmF)~K+rcbe5EPKyZs0qb~BNw7U`oJk)<6TOxtCGUw>SgNZ` z12=B3P2EWxhjRXwaTi*#WOJ((dS3Wl60M!Q9_W;@km>hru7{43FiMQNl4^|dC=57F z6+&B-r9~^U84Y44IxDSk8W1Z8W4Glxwy2IMCZ}GDl|ar#Ds}zvlg4}Imz*+gE;H8- z%nw5+@xcf;Z@TfVWZC@uvyX0svFUph8ps6AZ(Z=zbbP|ara9;!BU;V>yLNogNyb1R z?lvz&v7f=WfwD3>ex_5tuvcE#Eidep7dz#}wsyqmR!%-Xw^ym{R%$zy`A%iNUHbdT zP;sh(yR&nTFYV1<-krVt``PZ5tKIjnX9}ub=^?4wzy~?bkTlc4{~2qj`o@d4Z~cF^ z_aQCt4wX{PvcwIutaRG4{E#G`ppRMBH;L^HBt^?2p<`K0bbVSJyjrPxPuy^xR-yiO z;>A`(neTEAwdQG&xZDXDNlPD(FKAw(UlBV2c34#)b%+GPw*YO7zD&Z3mLY*uq@LL@ zJId)FSG#Xt%Vey6fO|;bgB<^%oD$+w?bDZbl%3 K4*o@+d-NA-OQaA0 diff --git a/MLEBot/services/cmds/mle/__pycache__/salary.cpython-311.pyc b/MLEBot/services/cmds/mle/__pycache__/salary.cpython-311.pyc deleted file mode 100644 index d1236a1f7c889f63932f2c32251b702dcadb6f97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1920 zcmZ`(&2QX96d&7b@0WKIgepNK)Fn|rHW3>RrIb)bBxzLACOI^~!CGnLcqWxM_S(#N zD>eivq80SOjYGH~<$wYxxgv4oPuNt8y0TQM5|`$-QNpDccw?`3S=5ZjZ{C}EGk)*) ze&e4Dg&czR=l3t}|Dz!EmoSp1_LNfw$}S>^NDeZ@C_BiN3`r8Y;>fOID4?f^>ZDxN zP$OM)G&gOeK~Iy6lX0^~Hk!{mIX7?QB_yNUh~yq2lBdc(eER_2xEVT;)=*hL7UpVM z3U$->vE{k0StlH9Wx*xKDOdzO&rQcCKr2-n4X_wfAOExbCnvUn_xgvqSqd7BL% zGY1T9g}UpMS>o`diL*pI{TjqBqR5a4GGrndii~KgmOufi6Xh_qFGINS#x3&D5^Eon zgTz2YrRiEGF*jtQJxUFG3|mH)q#^Z8sI5h5ZCzZBLNED_*`%zr-Yl&wFP7@2ODnk& zv`~xl-+H6x*z_8#6wj3eCY7=rri6c(CzM;v_5<6ihk3gmP-a@fu==S$pd!_W@Se&N zLU)k_a0H+wZ5!44R;|CU*00^RR7=1_1K19wVaUFcw`D&5-?R4m0484&SY(jZhFyS~Z8 zRD)9%$~FmKY+pf%Y z6=`fiIzd97Me?!seB?qGDcPBhKKkAAQ+@J@KKaXs&!*n*T)WX#QDN!?$%UCSA-a*! z(8yT3IR3Oa{iHbk+vXpakFGDZuP?QV(?`WS?c$wQ=G=Y?0m4ru7iKUP*8<}(hq3FC zh9h(x}?p>MIrP=h#xQi7Q{~f>B^Ft=^`OCq>b%V+v=M?e9^i55n!gx3(PRqWbUT%-S*HwhxMFV-FAnhu6ov4HTU2U))`^GVf@X7^tRYVyj{g&U1 z5(7?D5>8YECn^{u6PG>IBYxuz26*y_ieYi!3*ZDXc}Ehy_ZSmj2R&%|lviT9G672r z(xlM(z`e}ejEgkO-h~Bmh57Fw02)c^pyE#SbWo`^+&k!<)^P8jtF7VQK~t^a{wkk| Z78>eZZB!CKzgj+CJBe1#&RLH#`xlPrm(r%H z`4UQ~@ELLL?tA}Z@~jYMTDoI~1!^+Gx4gh|TVcU!1*WeX+%eLou_X@5BYe-t2*77R z5V!!XME0PWdd+lRlkZ0?d!(7b4nu%Pq5VeK6WH}vD;=glCw2IDj|9b`PsxceN~uRk z&xhg{a3Fq{sbThTOAeLcyrC6L4(&bR2>UordpQwD{a0)AV}RS2S-*#rE9gvXxVDX3 z;h`&;&X!4M3qJqHFsFICZ?=L^W}fdF+h!0-w$<8ZVW!PYKNKt)US(#BYArXg8kV7R z{QVrCD<@UM4A1a6B(oxS?1n$VGnAAOjN^@*czJhmp}vWgS$)g5^j3YzHQEj?fYt9< z!Ag6p{`uYI+ip;IY;y}gnE91un5=F%l<^{b^$ z#mZ@Msass?$gdse*Z^KV`B2t0ej+q2%xjwC(zea*F-`ljt=pH8oTgFN&@`Vz9p)}C zs_+^$8+zLgG|%)Mi!t0j7UrnM4A-Y&VRd+_Vl;mP|81P_&u~>p6z2;29@T#OL;{iI z2)N2Zc6z_wm8O1r*n77Yi6py8`oJ|pi~6}#=Hc?GROw2UNKm9@(ktIU{3DUu5e#)- zgzR<9>Py4Wtz24eh zP|{RN1*$mq#-)dx(pKb9RaM2Yf5Ha2!Wsz)sTa8IDx5g=jd#5tX%O_#Sz2)PWnMaxU z3o2gCFeEOQN|It4hFrup$lRoXUxZ)~_+Ceq4OQ2cv;|GqoK;DYEe!g|v?UjnWzAM! zP{*L@)=l$Pxg;$H1EAv-vuHaCT_|Yr2D9rj^w=uOGZ^Jr%;Y&1siBo1ED#3FZHKm4 z*gEuj^DTrx!u+!8QuE!KvQl zHwLy{Z|OGxHxJ&I`=?O%ffH*hLsvhVI2GC*%C+~$i=f1s^ zKZv^lSUn_p4L~#10?pdM{IYo`MWK{x!%{Yyf<}^iAhvdBf9niOLPY>0iDA`NEUn~d zW^t|e#%%7B)D`HH&?lfz=WYV@1YMPv$|_-Ja~am6aU}e0S*MySIl@jtpMXA{BQdS$ zsFtiycUCtTD$P^@fuij$^xZE3WiyBY%&s#lps2UaCiVgieRsy}E~NH1+!yEp_kD;# zP7~n>t)rFZS}Qc;{MO0=Sc$G**EVsz&30nSY~F4#o4(f>=Riwt<$|AQ zu#Z-WXy5TXJ#RA?Hk(Lqc|yn;Xi4kb`kwu5^PO4R?j4F2Ek?rHA4{t9FN8#Wgiw@B zSWEU;Q=*!-TT7*}W-F$JvzJ;|nHftpwC$!HF6t0TrQ7RYc5>O&6!nJfSU17Non=i~ zPBjc2le#SF@@mr!t)J1xI+BL3ff$o*Y0=bm^G>-$YoKZ@s5p}#qNUm;sD~=yvAU>Q zRs@1Z!cs}L)S^Q~yJVTlE!82st`%?DBviIli?A9dJ+@lJQqgp@MNN@?oYF!@dZy;) zq;nJJrRlketCJTm%})_ASebhxV+~io{IxJ}y z7A#FJ7S5YW*?^tdg$tTl7vKe!w;@n=K{2pRm4!WdoGqyNSb366wc@^iJZ4n>bPt_j#TU9{)A;d)z%S<(`=F zPEWh1XWY|Qs~nS>Won4hmp;88d6s;yinz#S#_Rjw$M>J~oqF7N>QU_J$hbT5xp)3z zl}G&}HN^H`h8dJr5!?F~v-OQj{by?OMBVkFxrXooett}+n__7nRUN#q0 zQ27|QiBJc-<)bMwKRYMQUyx>VBw(RZ1}qfJfQ7mluuwh^0+{8p?nouoGBn$UDr%E( zBPB?zEh7mBN##T07i1lQy;&JbXhrb}-Gx01IM`_Zsw~5c5HcVF68bj^F5g!fJmC$U zg=-c*qt^@+A`A2x?~nIa4v$p$(H(x&F3BAq{Odd$OlpVGS5(L^MYTAXX-#bYfZoa z{b1UUfWQqhjE52%{?9|HO6TLDW0lUwL!VYU9}f*yIv)?EE1i#r-l=pxRpCR1Ms|Pt OgHrQHUcP4BKiNO?b>R#E diff --git a/MLEBot/services/cmds/mle/__pycache__/teaminfo.cpython-311.pyc b/MLEBot/services/cmds/mle/__pycache__/teaminfo.cpython-311.pyc deleted file mode 100644 index 7e1dc2ad1935972f985d7f4eebfbff2383c7e366..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2187 zcma)7-D?|15Z}AgNvF@mZBt^W!AFQ=IiYG^ND1j* zOH>sM2_^qQ`p}0wxs=#YN*?@I6xj%J90US|lDwU9p7PY0Q%Luxq64soTLBq6Ujva8e-;8Rp}Qm$H4 zflt$nlXf$;Osv-&&CS-bu|DhM+p7Yd0I7VVRu1LMNa-?s(qacFSmRvuS;63kJIE z;vr#T;pR=_+SPeu@#fVV)#a623l{E{w9H%P;3+VV2qQI#lA27Vnj#aHT2HV6EU2=T z+LYn^@C{mQkxnG4ZlyORn1}Dse6j=C1m%-tiqH(x*0YI7HI{3nX!b#B-0HuiTAtwM2vC`2FugX8x#rLmnwVrzGMJV?#)ntPl5E$ApR7D0*FaOCFEiPfAx2<-_Y9 zr;~Mu$FAg;Msl13u+u1q51;84-bhQV9}lCOXmIpTjHIqIo-bSKxO~_*)D+fAx-)%r z@ZuO_oS}|5p7=HdCvOU);W>`?L%Ri{%G@SPJ=KztWJTk z!Bf6jt$tSjS}-o^Yuq-Q^?A=~yRf;aFWLTbd#w&>cg^$buEW+~0L$-fi;24BQh~~P zlFgOYJzfN_oe4}*EQ3lig)oSJfV_m?28rGy$ENzFGtWxpy;6C%biQ9Y-_wrh&!<26 zBlWb*%!x7MnuV8mx9UDzkc*=^3vYqrJelq%PYbB za-@=}%lky0x(?<@BO-tBKWGtbwB(Gld!^ZJcV}g{ve>UI_DZw6rKNspsi(cOAHYrF z87+jG5f`LkggL`-J=%5{@CC#8u5CJ4svuaIBj!^Y+mVwL$5xvG9G!472U{ zMvHOR76OV|gc)iJ%i}c6uZ(IvRD{pt82nDS4(|9gl0rf-Rm3)sjo~knB~^!(^I{fYdy8Pt5*AJHBe`8$;_@g+gE2Jxu`Bn!PyJ_ zvzH=J1s!@;7#P4E(PMPA0c#7LcP-HS1R zq^u-KS;3^N7(hAZcUXw)4F8i{CgP&tC(%J%WvYL#1zLO@Le&CLeU1PP;JSdEjszim zqkTSz-W7fkM0_E|4iErIk^)lNh<^dmd*eMIANR(4K+gBZdqB#)@g9)r-gpnlx!!n> V(kCQD^=SG~?8h^&-!UKG>|cdU7=8c% diff --git a/MLEBot/services/cmds/mle/__pycache__/updatesprocket.cpython-311.pyc b/MLEBot/services/cmds/mle/__pycache__/updatesprocket.cpython-311.pyc deleted file mode 100644 index fa4ad41b7f854450c1bbeb9f9f8e1b866244b50e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1668 zcma(RO>f*p^o_mtx|`Gytw17D6hpKm8#Rl11W_ubMFmlcP$-A(N|xi9t-Sb)884_I zhd?V%mEe|ZP63qu0uKC!O|(*0J|S@_H?NXYFYv~@-qvj*@n+`Dd-Fc@-n=*S>&1&T zK=}EOr}zI=0REOt0osH(Sw~_QC{W@!c zTw`ceDA|C9{*PquG)Ow@#Ify0LEwZ`Ag|sCs5?>4x5|V$`4Nd-V9+8IS_&mCRRLD% z7NKD%RDDv}$IuUOjUm_23vM5YgMyL4Rl7#Pb`+{Vs+2X#Tu64O#UIRlS>5PQy_Rj+oW6M1bcY;DwNZDh?V3u+0VJQ(*nI{T(&(whCTg7tAgGplu+f#OTAV0LII0axwB>6X3qT{TPw5-#B9qjoHVu17qRXSUNJ64vqT2sP~Qf(7g26ADBzW=JJub zd}uZXW}|PMF>}RZ^U9HV<+ppowaTN{Bgcl}hN43rE18=YVV_+!*k!?xuM zvF)^G+d)KozNBYu`f>2 zrZb;Be`Wc_{to;NwCAr&BpWeDk`O;2+L?|_A)1kaL^|m6JMDq?W`FVB;XCV@O7xp# z1QMU-_%E$4<@G~td7v$4N>#f)T)jG2y_Ts+W>BD!3i=9ajI`&rnO;b9y>WCho`%L3 zWPEwL3-Nfdo|$eE%;Z({%f?8ao}MAf None: 'Lookup player by MLE name provided.' member: Member = lookup_rl(self._parent.sprocket.links, name=mle_name) diff --git a/MLEBot/services/cmds/mle/query.py b/MLEBot/services/cmds/mle/query.py index 34f8c83..d1d8769 100644 --- a/MLEBot/services/cmds/mle/query.py +++ b/MLEBot/services/cmds/mle/query.py @@ -1,7 +1,10 @@ +"""Lookup groups of players by provided filter. + """ + import discord from discord import app_commands from pydiscobot import InteractionPagination -from pydiscobot.services.cmds._cmd import Cmd +from pydiscobot.types import Cmd from ....embed_frames import mle_card from ....services import const @@ -39,7 +42,7 @@ async def query(self, interaction: discord.Interaction, league_filter: app_commands.Choice[str], query_filter: str, - sorting: str): + sorting: str) -> None: """query sprocket db with filters Args: @@ -47,9 +50,6 @@ async def query(self, league_filter (app_commands.Choice[str]): which league? query_filter (str): which player pool? sorting (str): sorting type? - - Returns: - your: mom """ _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) diff --git a/MLEBot/services/cmds/mle/rebuild.py b/MLEBot/services/cmds/mle/rebuild.py index b4499c9..96846c2 100644 --- a/MLEBot/services/cmds/mle/rebuild.py +++ b/MLEBot/services/cmds/mle/rebuild.py @@ -1,6 +1,9 @@ +"""Rebuild bot meta data. +""" + import discord from discord import app_commands -from pydiscobot.services.cmds._cmd import Cmd +from pydiscobot.types import Cmd class Rebuild(Cmd): @@ -12,7 +15,7 @@ class Rebuild(Cmd): @app_commands.guilds(1043295434828947547) @app_commands.default_permissions() async def rebuild(self, - interaction: discord.Interaction): + interaction: discord.Interaction) -> None: """Rebuild bot meta data.""" await interaction.response.defer() if await self._parent.rebuild(): diff --git a/MLEBot/services/cmds/mle/salary.py b/MLEBot/services/cmds/mle/salary.py index 842fc51..d25f7e3 100644 --- a/MLEBot/services/cmds/mle/salary.py +++ b/MLEBot/services/cmds/mle/salary.py @@ -1,6 +1,9 @@ +"""Show player salary. + """ + import discord from discord import app_commands -from pydiscobot.services.cmds._cmd import Cmd +from pydiscobot.types import Cmd from ....embed_frames import salary_card from ....services.const import ERR_BAD_NAME from ...sprocket.lookup import lookup_rl @@ -8,15 +11,15 @@ class Salary(Cmd): - """Lookup player by MLE name. + """Show player salary. """ @app_commands.command(name='salary', description='Lookup your salary card.') @app_commands.default_permissions() async def lookup(self, - interaction: discord.Interaction,): - 'Lookup player by MLE name provided.' + interaction: discord.Interaction) -> None: + 'Show player salary.' member: Member = lookup_rl(self._parent.sprocket.links, discord_id=interaction.user.id) if not member: diff --git a/MLEBot/services/cmds/mle/showusage.py b/MLEBot/services/cmds/mle/showusage.py index bd25b60..a06e6b4 100644 --- a/MLEBot/services/cmds/mle/showusage.py +++ b/MLEBot/services/cmds/mle/showusage.py @@ -1,6 +1,9 @@ +"""show play usage of players for a franchise. + """ + import discord from discord import app_commands -from pydiscobot.services.cmds._cmd import Cmd +from pydiscobot.types import Cmd from ....embed_frames import usage_card from ....services.const import ERR_BAD_FRANCHISE from ...sprocket.lookup import lookup_franchise @@ -14,7 +17,7 @@ class ShowUsage(Cmd): description='show play usage of players for a franchise.') @app_commands.default_permissions() async def showusage(self, - interaction: discord.Interaction,): + interaction: discord.Interaction) -> None: """show usage of members from a user's franchise Args: diff --git a/MLEBot/services/cmds/mle/teameligibility.py b/MLEBot/services/cmds/mle/teameligibility.py index 1726c4a..a663899 100644 --- a/MLEBot/services/cmds/mle/teameligibility.py +++ b/MLEBot/services/cmds/mle/teameligibility.py @@ -1,6 +1,9 @@ +"""Show team eligibility. + """ + import discord from discord import app_commands -from pydiscobot.services.cmds._cmd import Cmd +from pydiscobot.types import Cmd from ....embed_frames import teameligibility_card from ....types import Franchise from ...sprocket import lookup_franchise @@ -24,7 +27,7 @@ class TeamEligibility(Cmd): @app_commands.default_permissions() async def teameligibility(self, interaction: discord.Interaction, - league: app_commands.Choice[str]): + league: app_commands.Choice[str]) -> None: """get team eligibility Args: diff --git a/MLEBot/services/cmds/mle/teaminfo.py b/MLEBot/services/cmds/mle/teaminfo.py index 01a9809..16e7cad 100644 --- a/MLEBot/services/cmds/mle/teaminfo.py +++ b/MLEBot/services/cmds/mle/teaminfo.py @@ -1,6 +1,6 @@ import discord from discord import app_commands -from pydiscobot.services.cmds._cmd import Cmd +from pydiscobot.types import Cmd from ....embed_frames import teaminfo_card from ...sprocket import lookup_franchise from ....types import Franchise diff --git a/MLEBot/services/cmds/mle/updatesprocket.py b/MLEBot/services/cmds/mle/updatesprocket.py index 33c4cea..b632339 100644 --- a/MLEBot/services/cmds/mle/updatesprocket.py +++ b/MLEBot/services/cmds/mle/updatesprocket.py @@ -1,6 +1,9 @@ +"""Update info from sprocket. + """ + import discord from discord import app_commands -from pydiscobot.services.cmds._cmd import Cmd +from pydiscobot.types import Cmd class UpdateSprocket(Cmd): @@ -11,7 +14,7 @@ class UpdateSprocket(Cmd): description='Update info from sprocket.') @app_commands.default_permissions() async def updatesprocket(self, - interaction: discord.Interaction): + interaction: discord.Interaction) -> None: await interaction.response.defer() self._parent.sprocket.reset() await self._parent.sprocket.run() diff --git a/MLEBot/services/cmds/rl/__init__.py b/MLEBot/services/cmds/rl/__init__.py index 1c09d97..1ab79dd 100644 --- a/MLEBot/services/cmds/rl/__init__.py +++ b/MLEBot/services/cmds/rl/__init__.py @@ -1,3 +1,5 @@ +"""Rocket League Specific Commands + """ __version__ = '1.1.1' diff --git a/MLEBot/services/cmds/rl/__pycache__/__init__.cpython-311.pyc b/MLEBot/services/cmds/rl/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index d1e50880eea20e15c5299591f7b19243f13376d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 249 zcmZ3^%ge<81O>Zaq&orW#~=<2FhUuhg@BCd3@Hpz3@MCJjOh$fOesvk44TYUtcH3( zxRSw7llc~Re0*7IQE_H|UVQv5_W1b3oE#v7!#O`UH!&}zxQGd8$Y+p=zr38SVnU07 zYGaa$G86M+T=J7kb5rw5ieua}OFT-GVtjpEo$^a!a&uCXfD}+=S!Qx-aZGY7Z=L_i4V+-jEoQXL>st2urP5mHL!o+ PVB%qF-~hoQHlP9kaSud` diff --git a/MLEBot/services/const.py b/MLEBot/services/const.py index e1bd74b..f94d2c0 100644 --- a/MLEBot/services/const.py +++ b/MLEBot/services/const.py @@ -1,3 +1,6 @@ +"""Minor League E-Sports Constants + """ + URL_REQ_TIMEOUT = 30.0 # ERR - Error @@ -5,6 +8,11 @@ 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" @@ -38,6 +46,10 @@ 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" diff --git a/MLEBot/services/sprocket/__init__.py b/MLEBot/services/sprocket/__init__.py index b7dcedd..b9e5b7a 100644 --- a/MLEBot/services/sprocket/__init__.py +++ b/MLEBot/services/sprocket/__init__.py @@ -1,3 +1,6 @@ +"""Sprocket related services + """ + from .lookup import lookup_rl, lookup_franchise __version__ = '1.1.1' diff --git a/MLEBot/services/sprocket/__pycache__/__init__.cpython-311.pyc b/MLEBot/services/sprocket/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index bdc3b5ab4d53848ad26416b9330dd56689ed3a41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 318 zcmZ3^%ge<81U$=Lq_+X-#~=<2FhLog<$#Ro3@HpLj5!Rsj8TlaOi@gX3@J=e%qfgf zEGf*v44N!28G(v4nQn3B$1F^?=Y%ll2xGM6QS# zsH=ztNZjI%k1tCtD$dN$i;us>9v`2WlM^4mlHoH@j^UTLvsFxJF;F@tsVFlsFUBQ5 zximL5ucSD}J+s84G%3c{$JHsnBqlc}H3>)oRhDHYrxwQ)7Zl|uXQ!6L#K&jmWtPOp z>lIY~;;_lhPbtkwwJYKVnhkPyu`ZDKz|6?Vc!NRz0ygx3Pqcyi0}C@ZQv*8)7I6Y~ F001k&TFC$a diff --git a/MLEBot/services/sprocket/__pycache__/lookup.cpython-311.pyc b/MLEBot/services/sprocket/__pycache__/lookup.cpython-311.pyc deleted file mode 100644 index 08f5e9e5d56abdf453da0f6331e8f46f79b63d43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10327 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 XiXQNZHgJDnVd7?LU4xm~9nT$$u diff --git a/MLEBot/tasks/__pycache__/sprocket.cpython-311.pyc b/MLEBot/tasks/__pycache__/sprocket.cpython-311.pyc deleted file mode 100644 index 34466ccbf7045033bf946d5457da2ff502a190dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16285 zcmdU0eQX=&dB5W~B}$ZNTA#LjmK-~jZAFT|KV(I*V<)w(_&c_ugrapPi8duVca$A7 za^W^!BL^O;bnc>V?xt;NYA-43KZ+qMiY*yBEGr81NL**)00Rbw0n2~%oMJ)#+V6Rf zJl=_v<1J~h9*@tx_kO?6`+U9c``nLfYN|L~(cVwSdRK7Vztc&r{fua11)w(>ds5PuHM}J>7$D(J8tTo(b=ucfvR5<2eVnj}zTjIMMU2gX7-A z-?#?-qT?tR@qR+D5nlI<#-)U)417X4A`ZRg=s7t#a#l(mh)Z-&I@*;=s@#vc43ODGI&?U(;>JVL5T<;?DduHiDFH7@D{$?`{`P^ms zs#v~iqc%PJBO-1FY zNfrl^bSY9|R3@%bsEdN|dEf5sL&p_KR)$W?@z~_hp5(~X1e#NZ_Qg}rPMsb)c;K0* zlc}MJgajN75ss9hBHFsnUDSQiXnZoBibm7*MZ9(uqXN{>R)EV~u3_1X>ss~QhK{$A z*OL^?HBwCAUgtVIg7>!WoN;Ns)?DGK`dV>6Kv@i0mQqvl_2v&sT zs9A+sJ*(X;9^vI`^yd~YQ}-0=tRU9|>TZ@XU0bZYi@i5cF2dGjE?>*lg|EG)`dVaS zCflHCz--W+2)xauEC>Mt%(r(Dde3}g=ole%#8Z+SJDrdWRD8kcQ&Z;>(u>7zG90|L zy)^C;N=t)w$kaJDa+!^Y`cN3RpBgvD9x7JaWyV2zb4nQCGWR=-zhPFqel`N@FJF=^HWLEcTt9!G-Ue(uIJ}rb~MxHeJ(7BL(C9eE)?uVo;9qBj?e>66d zQWzZ?92q+=iNYBLSR=yv0+c8zdApDrk1N7xJRz-71bdApa?*jWAv0+R8rn}SFbP_Q zp;|K|OHVhN2(l+^Ap97VN}Sp=?|P?J4R+J*>wXAI1cnkTLQ4E9@@b?poA!@zl7`#c)HyONuEM1u-s5BdPVNzsdJ-L0Ru`R%#3>0b6mG1L5r<5kC53PKT} zgqd=dwI}bHbnx6{|FpxZyw12-0RYU6^NeW|l}e|pILH%QnrSOlNFyAui_+eAyo+AE zXZFNkHilp4fJ}rCaq4cutD<`+;*(P;2?0eKNluDN#3#2RhrA3xcZ#u#x{Gq^eiF8# zlz=d=r6iA0Q1=++A};wcDz>^<)-VKUoUkk|7i@v(NGvguUT=yT_HGz$qf%;QoSAK1 zI}=IdQ+9Q+>?9O|*y5(9>nBV=*zHD%< z8eE%eY?-YpM6aBGUJdr)&N6sg`Pr*R$cP2ZHMQPt611ky+fRJBN89s)wr42w5j2QF>ver@ZK%*G>mue13$f1e}xDTDW&E`;-(tNsL^uj0Z@ zvm2Rk{Dfxh6S8zZ2>}32gk9YUHOcVEv>B8dnN4yxCGoE+G;_m3!H`&55lQK`ha>SB zRH6I|09eaA>ikVVYSLCdndcC?+p#+Dr03cV+S;8Tq%v!Fvv-zQ5v<~BLNm|IuF^t} z&A*ZfM6^I;hR;<8@|?rJB-h?CvquZ8$OS?(d+!D6iy7ripi>KUe%Xw9H`-tgl`9*< z)9d&@I0CRA0@Q9A?crWpc8QMCvCEjBne^V!A*cS_%|GOqj`phR^YGrAi+#& zSdwvjxOY*iwAdCoyX1b;an+$<7vZSQ5T9|NP6w-#T4tB%EG^H!s8->%r90oVt9mMO}gZkgsm3_M6-XMq$P zad9Bx(w(pjUp-Uv`XN#m-M@9_P!mBgS5$Fh^U>IqGE_Q%>E#w}?&Mm6Bo;1F(VJ zQ!p%Lh*%PdYU6ka?Vv!WZ8)PDy;*aAh2}RLS zqR2-OU{1RLGuh)QG-qLtriJWbC;b9hhfT6>Hpp6j7S-r}Q&J4v;%-^USw4*93S_+L zVH0Q|se*{PK$X!JmPx^q8kUL_tR8+rJvpKs7PEb#+9wjF(z9@fDJ!E?`2cEwvOI`~ z{JarnCJyT>)0@Kv88%lSL!)!)_Aeg56slHw0EqU8WT*$mZ6z5g6P7e2jA4@{p#>AF zFJTBZChSq1E>m}YJw)O`lZ$9My9~9`>i=D@+4bY1TCPfrD^SP{SAIdOocGaOW>q{Pq~5xRc}B z$+LG(D%q1t=H!%iaw>D|ymst-w*S1^e;#DWo&^}D@`k{yQ;Gu9=#L`5fQDexF=AwD z+DeSAM2w5{JiY2s#&?khvxNpzAV}(qaT^4=Qxkgeo(W&j!WR~iVS6FgvFm^}mwYig z5yQf0#A>+^<BoYjz#=lW{p*(K++Q#pZ6fnzCRiNaM{nqK8a=M`E6n=XO%3X?n&51JZ3bN*`L|0<)C*3nS9Ahzy>_04y?i^FkRsUmT(BX*W2oIvyog zfvKJ}<@XlDbd-lt^}sEdB8lV$yD7qwD)7?AMQM#~N5SB%AiBSeW)$MkA|nM}R0A*0 z_IyzP!RB8y|Kp0EugGlshPLe+AB(>^^Q$xen)r1hyKPY2HaK$_pmeiRh7^ApmDzTy zNV6@$|3MqT+Z>(myi~Oiqi;E`RbxVUHaPh>azYcB)>q*mhDra=t9D|WIYnDLyV7=j zW#z&OV>XNZ5vo_71i(6%<9mj%1~0+f)@^ww_vD7#n}619jW@zbom(Gk1p1WrxpYhGlJ7&hD4h{qo!^0HvE%z%UNAVwwfISB#I2CgP{{dJ?@Oi6j<6 zg|S53hLj#gH{=)q_MDhfk!ib4A=xBgm!x3MHZ2t0MWRWs{6b#(Ce`;b0GY7ZV+u9o z7!!bdaL>7J_|^7}TGJ-HWCAa0hj z@(v3v_V)kcl)CSf);oyhJO~wrFd%9Gyw}}x-|KALfX$@F4Kw@lKCZ42n;tc*?lvz6 z;?4W!s`n%6#=TnSK15~O6s=9kwY8%!Evryb%c@!G$xMyGy|h~VLotIbEBOj$u(28A zFUSO#CWch88o_L7Oj@zHQYzF+`k8p1LL+D9AlaAn1+P-L?$??1t(SCBu!dzF&5#hfb(3M72Z1*{6rqr-wlv z>{)536e8O<8>A+r^Yl`xi<2%|y=)2knCAa5-s6Y7a^8)VX}3A^x~3CA=f zjErT1<63ZB^^F^{T@+rLV?n$blS~I^Wu_2A#nVVBAcAeFTle@xzQB>S zShrgCO+=@0X!g^VX&y*f4y3sBYUOM^k%*4T$*FVc+C#E55tn4)fD{{>lHjeANTzhx z!1Dvo7|hGI&KWZZ%wCkg6q^15gLtqV;4-I%MvZ%}{$~BnUM;ds?LC-{98_^1IjSBT z)Q-HUMy2c#Nj)OX_5c{SAv4W|59%^4;;#xt^0!cHy5=A}n@r5e-$tDLIsr1L<*ybm ze~Bs?`4Cp>_4te0Q5eIc*`uTC(NSQAJpsQq%+&Uez&~!{ z;@5(hzak>MLV%>h?^k3P{7}C)IW>u0?XPR<|JpkBaWzD@W$F)QBZpMn$D!?C(vH8Z zo*vI0A6Ji$W9r!xkl`lc(Y*yht~(AL&>aWy-;IAi{(BE#kxaXzSQ4=fDW) zjARQfahUAWTS|t2vOp=MJ1STK3p0J6hV&Bv(h{DMez55Z@kU&Ie7n}R!-&Ni1$qQa z3&0ugy)EHJN!Wn@gI@z!Hj-X`6oWwi zhFvGZXs{fO8hTU4IhN=uoj1&*o$_nphLq_3-<3Ge{dCaWn2L&I_jq-eoJ>eehGMS~ zCrZrOiCHJQC&6?w<33}Z$Uap}LP9ZG=yb;0T zN{uo~)2mAZC|KktsiQ=I!mx!AOW6!wG{(7r5i4i<5miXUYb@EU1Gk3BF2A{66L7#w zorJduAT|gHOCIhp4+j*lv+e{%0V0$C1Ya(X&CTM|uv*^c{I;Vn+a@lIxA&d)&Du>I zj@*gg3Rh_pc$&8#D3CXpm58l=-$=0#tIY@8gE#6SyF@Sj>^=whdck{?>)?7g#dX2) zs`C_g0f+LZI37PEB~@E2&v7Y|qJ=9JDu&<>=Um0fT|Acxm*URAQ_8sJtBWD))6Q^< z_xcp;#|6&lTf~=MOP=ZioX`bwnS#$I&h;%Go8-V~Ivwh~{JDE7HM)K)wugPv-Brl*g!JH zl&2Bpd*=DQEL3vyDjXE+{3ugK1r{$@++-Z4%`ZE;sJ-_QrTjMl=@Fk_xLdz;R@53I zH#dCHsMhyq>if0&ez-!_torcq-Nx4I7w7BWn!Y|g>&mU}yxFI%-ZAUa>N@N%D>`Of z*8_P!3bB$5+^0G|B~Xf6H1yeLpDmik552wGkdNbm|;J1MH0}AvI%4Vc4L2p`@!b^WzLU+)d+sH%+)V= zKN6f2?%*lBiiIQn{L@{|e_Bt_W+{XuPtL~O1MM_!x_u2d3qfZ5|Y3!G- zSfjN)ai?uVwrxYEZIjlviGA5%e9=%Yk!Z|p+|vlyJ+@@Hf-wI|bJXzRVk_@QacGCL zPUAE;Pj#1Fc8o!Gur0;(YWkET9!td2lHp*j6g}JN!w(CdT#U60S1=b9l8G^7S{zsp zG-T3h7IvYW|3G>601&%yfx2tt2LZo1@|VK_py3SGL~%`U1`E`E_qjKpySo3{{u%so zzQA|=Z~CuRU#p&Bzsl%*9ko9=0HhUtrAh>(gn<$hoycci3sC}PV3LvC`e=x>yUk4& zrf;5Xrn0NNy?KjjDP=Fucg9mp1v!Kamu7n|dq1u%!C1V-GV{J-$odo=b_HgyfsuzZ zy@E+`a=c%JF4Apg0?x?KW5LcG0}Ed*h+PrCK{rGe@^S(@32X+yZU;UBkZb6jRFUqP zOyWC%i-p5!-3K4o#A)nETIox7qDSd~X>sB0Ur<)b0u)@r;0znSHqFS%MohWtpjHi^ zLnlZN9*66`x9+X3>s@L zp_bXl-|D>HsRou+ymPgoYiIA&3fWpgUD=6Ibe$SOvZrZaMQ0mc2O(E4O82bUQe@K63FDBMbg;(*n)l9VG10b zzaakrLzMpv02J{Ug>L{1L_G2j=|vzwJ6dq|nwmIQvH=S8BmWK3tfV8~LWD&$QGfo8 z@{l@b$6mD|!^Ky*{eoK4o2lv5YI>nL{h<%*??P-nms|OmwsNBZ^KNHjErSN5O))uwuR6#^)P0coa1l&hWS<<$ zjTGl*pM%LnZv~-G-z6C=+)t4L0s07pF*9RpI?QCwGkW!NB{xp_5M??=AW9%f;5-4+ ze=?mm$$v_KDHK{8FeM_>w6Hldw)~dRD~((xamIij0p(jD&CBL5=W115t$CNvjhQn+ znDf-;9Ugb-1M+w|_BI(LWmYA{O;W80s=V%&+{PW+#(jAw zg4jr~fsn&J!RI>H<((9O{kIVMoG9P3RqNT4cOpnNTR|%mSTW5hdasV)E zB-_{rRFYzRAjwS31d%NXEcx#Mz+e~8A)PD$gUc9Mv)yfz)Iay_G#Gf7{Bz_ZD@GxH z!TCAQ=eRc2e&@L0W%kQ)wU^m1$5mfuKTAxP%2l}Yo?|%B&vTXGeHQ=eBB^Zj{{w$b B&Gi5P diff --git a/MLEBot/tasks/sprocket.py b/MLEBot/tasks/sprocket.py index f9a6c3f..70474af 100644 --- a/MLEBot/tasks/sprocket.py +++ b/MLEBot/tasks/sprocket.py @@ -1,16 +1,18 @@ +"""Manage Sprocket data links for up-to-date information + """ + import datetime import json import threading -from pydiscobot.services.log import logger +from pydiscobot.types import Task from ..types import SprocketLinks, UrlDataLink -class Sprocket: +class Sprocket(Task): """sprocket bot task""" def __init__(self, parent): - self.parent = parent - self.logger = logger(__name__) + super().__init__(parent) self._links = SprocketLinks() self._loaded = False @@ -20,6 +22,8 @@ def __init__(self, parent): 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 @@ -136,7 +140,6 @@ def init(self): async def run(self): """run this sprocket task """ - self.logger.info('running task...') if not self._loaded: self.init() if self.ready_to_update: diff --git a/MLEBot/tests/__init__.py b/MLEBot/tests/__init__.py new file mode 100644 index 0000000..81451df --- /dev/null +++ b/MLEBot/tests/__init__.py @@ -0,0 +1,11 @@ +"""Unit Tests (WIP) +MLE DEVELOPER! +this needs to be implimented, would be a task to start? + """ + + +__version__ = "1.1.2" + +__all__ = ( + +) diff --git a/MLEBot/types/__init__.py b/MLEBot/types/__init__.py index 45b8f67..bdf9f9d 100644 --- a/MLEBot/types/__init__.py +++ b/MLEBot/types/__init__.py @@ -1,5 +1,8 @@ +"""Minor League E-Sports Types (Classes) + """ + from .enums import LeagueEnum -from .franchise import Franchise, TeamRocketLeague, TeamTrackmania +from .franchise import Franchise, TeamRocketLeague, TeamTrackmania, MLEFranchiseTeam from .member import Member from .player import PlayerRL from .sprocket_links import SprocketLinks @@ -12,6 +15,7 @@ 'TeamRocketLeague', 'TeamTrackmania', 'Member', + 'MLEFranchiseTeam', 'PlayerRL', 'LeagueEnum', 'SprocketLinks', diff --git a/MLEBot/types/__pycache__/__init__.cpython-311.pyc b/MLEBot/types/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index 55369fcecb584521dbec44aaab66f627fe5d2612..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 678 zcmZ{gJ#W-77{{H<`=vQL00V-NAzA6 zT%ZCK4oAYlj$`tOkA#a|;bBks*ta#82O`9w%^r_LjANU9J{Ad1Y!3K_NO1}|#FHYz zkBayRW#d0^JIWkAo-umbvfZi`nXBXXl2(f^rDA&Xm{D<9FP5yi`$bPo29@*UdH8LYqguck1wNO}MuePe;K&{$;5 zH5MB)Z*`DuYM%j~vKu=7{DAmR2OS7YV>{_!Wr1u*JyG{Wgc~H=TyM0JlLD|z&0eJw zLcTJo%DN&%hlEhh3Asc!fPrq`ZNJV>6l~{nS<))ssTZwaRip9`WwY1L^ZnWGR^8-+ zvpIa4^@^#Skg_TpLZ++rZS+ReMa$V+`4sw@_gCj&*RJC@J$lg%)uXNM|LW1RZm1qT f?}qA8)(zF8m)%fTlP71%wHGI&v(QZW7>ND?+vL5S diff --git a/MLEBot/types/__pycache__/enums.cpython-311.pyc b/MLEBot/types/__pycache__/enums.cpython-311.pyc deleted file mode 100644 index 32699f4dab8061a63fc8e6b5dd5d162daf2ff630..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 661 zcmZutOK;Oa5T5lTF%c<3ibLUKA%T=jPn=MNWD=8B#7@;V2P{h~C%dX5`BD5hw5J^U z1Gx1cfDnJC$H)hS!~x}I6iz)cYbR6)#=GClH?yAIZ$7o#EkJuV`f>IZ{dYmOrf!1u z9tPLIfRO+~;t~Q941q~kz+_H7$^c(**XBx0If0(K60}DO#ftK9wkcMQY{>dK2G_vB zB@FO4;>t{N6(+kXQ``nq-6m_e8f#)Zt!Rz7f0lF6?eeK%k1V537$U~~xK@XdJQ49wHJBcEZ74}shL{!4F? z@+9@9$=r{;K|Dh#QI>kcd3KObJyiKR&b%<-Q*_zIIZr)~a?`!@i$sX2KuAkpAT0iT zr9@P%1cYyTci}8M`odmzA1%hqitawXQS^Ex`nHYJlM6+3PsG6L_8k#bC%PEjYWwxD nUM0Io#e~zh2&GI2S%QA^_bMAr@}z`+;X~)A$o~3Isk-bp3+kjm diff --git a/MLEBot/types/__pycache__/franchise.cpython-311.pyc b/MLEBot/types/__pycache__/franchise.cpython-311.pyc deleted file mode 100644 index f5283fc9b12e5c9545d2b2b765f3a0dcf0770ee6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3538 zcmb_e&2JM&6rZ)%_WC2)K*C2-nl6+BP62^%1w<&NEgv~R0!~mXH|=)ujDyK~?appW zV<8cw9MUR>9*`2L9CD~qB6{G!p?`*rB4O>RRS#`$j*?zD^}Stt{Sg;crQ_M3-@JJ< zJM%vGr>?FDfwuX}pVK=*LVm|Vy#;E-_7fnM2_;m}Nlr)!0--+Y*L^vE$`7*&c4DmBYh`pG6mO16lk`upxeG2JZX3|crtn#HB@h?98|;h z4S;2GpWLm_Ani#OfeT~L!nJW-fuuGT>x*$bLDq%hm($ErG+kE;jyX&v1J|(Nkcx$8 zbPkCJO)_hi8|JXbK&GBGt&0_%bq#7eVeM|RN`W_!B%gSov$FpU5=*i^IpOodqGwb=i zvOH3fMz%qgjIB$yq^gFNW2$OLR5h2UvpUi-RsCvK(`z#!Ri*hfutn4m$Jn)HRn-h5 zZ)sK*{9ILeA8^+R-v@uZ7a?2+h<#!{0GMz3RYf5nRSCeaQf%?lrS6irA9`8bUwOlE zRSX6`sFK~_o(+%HIV!Kf%$r0d6`hz0KuiVgZZy{<*U~dN&B$uSMCXWVNGKh`so`Lq zgGg~s{JJ2TARK)o?rUtt_W;B7QP)stx#B?Ud7GU}L3uQ3yK|K!#nI^)-w(6=ZG?vX z-@zdQW;Jsh;J?`)?RgmO*$(cz@C_~Y6oG+ly9Ch5p0&&xWC7}C(-2T@21Uxvnn*dB zk|7mxGO5HuO&K~srH{OnBa;aXX-0U2wB@>nFwntkzyW2}UYK~c&EjQ%a7*@_@_%b=$Kba;HqfR}Otuw_|D!~R{9QPllM(G8`^I7ha;H5nD zLLMs1$2a8TFXb~Y<3kEXN2* zAeM>csRC3fv?_-RGmdI`?|p#^4M%O%XzFWlCkXX}I>q7-={rm-D4IbPrkQgJYB!%z z%$Y3IXr{6ICb<8 zQkd#&k!I5te+$Q0h+Qx9!=?HAIBZM8N05gQhsXQ_;2)B3k5Uq`mqn%0GguO_m&L(K z-@%fIy(}KA?CURy*vn#n3oXUwN9RW$j#k25i*#vVetdrX;dmv|y=W~BmBc>iWwEc) zz4!aX(%r^x^;MYPCxn&B)nkvpz%)N0bikfy;B;m1$m+SrM%9n>>4fuNB{1j&ZgIey zkHHsi)cam%CP%;K7+UU^j0E3kUuV-W5kj7$Zl`uRx@*HV%6n|%*9Vcn&07wu)Ns&;=!o)-h0~87(tq@fg%hJj5T}p&M!r75XSvquR znYxv~pa_+rNc;=ziNp|jLSms?sxo!rot;)K@ciy~@AK}x=leK0SpuYQ?|-dcAOJs= zaze&fIBKA<0~|QqHg&Bs*z*9TGVEEme`0QZvYcu!SiuT5i`*Sj+X2NcPgA^jRlsD=oI98+xzW zv8a_(i?PrR1Y>E5u^{4IU&#}Uz3ICC*i&GPM;>Fch&85^x4k5tV$2Q0C~=c^6vpUN z#@W2Y(GZs^QMUN;b75<3g+d7n-r59su;a^u6TeQV$P zQu4T0VU)B9^-H zsFqVRpK9KER_aCN%Th0+bZpJ#V|^g6psN0R93YHzLdXzq_y5O3xYj?%Jy5qp^kAISO1F4%J~C6-`$=7 diff --git a/MLEBot/types/__pycache__/player.cpython-311.pyc b/MLEBot/types/__pycache__/player.cpython-311.pyc deleted file mode 100644 index 4320b0e5cf2810aad109392926e532c027c19949..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 682 zcmZWmJ#W-N5S_L6KDZDP(E-xMRN)##L4^WEI5;GZ2qj!&EUosutLTJ3j@@-}sjf(o zP^IG^1fl!`e#NDWTp`gw=O|REn6>Ys0OQ>^Z)V?m#-1k zgT$Q&^GXtYBJl12EBdZ*d@5_g*=qpEx^Qhbm}^|i(JE&v<;}MdpGdXc-+S<;|H9iD zt2F!|jkjI7zT~@^^wulYddoxG%Io9uQr<9%qgr*Ed6Th($C9zU#aNt*EVBG2V;?gf z%}*MPi8N$P)v>3Xicx6t6~=gyq=uVOnrJ-3w&5#W*iXEC`us^S&{FAOs75>qx@nlj zGBG;njm)cT7+}krsR`mp4$+y(Sn8na?csQ$>_keSNOTurdhIDFsWl5g_|m;@w=64#tH>^! x8`Up)rfy?MaO(j=;Sxg5;Qs9IRn);*DjZ6@0{(|{oBM6y+(q5~oqv>N**`?qsx<%r diff --git a/MLEBot/types/__pycache__/sprocket_links.cpython-311.pyc b/MLEBot/types/__pycache__/sprocket_links.cpython-311.pyc deleted file mode 100644 index 7fcf212b14a9c0ae2c9fc46ecce39e42eee700f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3415 zcmcguO-$oP6dote5BcG5yL1yGHf@Ep5f$C8b_+`pQWgyC@*|WV;y4D^i49G$6KXqE zp*`flf!_AE9DAZ#(QA)Aa$L!le29Ensh94J6+P{#GZWi|l4=#HFnNBNdHy_p-+MFT z_e(Sy0@OUh&Q2)xEuBwkf5-&R$Wgg5BH z6k+))^_r@|PB^bfa}cy#t?g9Yd0@wD56*uikf$PG2mvsjJoK3HdVoj@o&)f!_ZZ$i zfmgdRKG81*1acYS(MArv;Q|PN*KMaQ)Jeb(?u8=B? z+O@|%YBR~sW}dEeMFVBYp%Ff%%(;RwNt=_jL&L9Dl&XB~1R^$p9F^|Qf3)gt|9|`8 z^pQE6HzwB2iFL<+pCt^Ajo#8}(>{K-83&s>}PPx$!ZdJe68j5)B z@pq54bdFodtaZZyZET0b(kq|2$Qsid=JbY>fso8=m9M#NW%njo%q~)0?E`%JTsVTT z(>E9QTqZrcL}j{(`{0GR!k?#Bp+6Xh^6Z;ka)byky@AaGX%DaGV0`O`xF3 z@ps$m<2b%ntHX1+3hO<1e+b=*A(cStE>a&OHHy?dq{fgMN9tdnu^wbY3RMH@7OW?l z7k+0X;R~Jts9p~zj~?ltGf0L@#&F3TE;SbpOFD_fAWNZie{qNTs++QXNHkajzs<829l9YV`S4D*=#<09@fcC zCB({ENGpi4H9W6?Mea# zel-=e!fr_~stT&_aL%nDtiZmjR-g^nYJ<4U8gD2Phw!M%YFBbNiI)=#ORx5_no%a; ze5g)Te}d}FOAtg0+|xTr3ncW*(gL6Am*vb4JmE7>geVecptF99?SEnY)(hbWv8xZ@ G0Q?P1bKHUe diff --git a/MLEBot/types/__pycache__/url_datalink.cpython-311.pyc b/MLEBot/types/__pycache__/url_datalink.cpython-311.pyc deleted file mode 100644 index 73b20697a44b7d6a5f5eb5b61e55087b6cfef698..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4837 zcmb_g-ESLN6~E(|acsv)oHXukvMHK2Nn2NGXw~iqS-MNMNlQOAE4o`?BO~K@#_bem zCcQJ>5UWTL^dVg$ZILQ9KoJ&NimL7d2wp%)(ElJsk%&gBctA+IZ?3vXRiF5sJNDRP zw_Aj`GdXkbIp=WA<`}7{rl->*4ulHeZDX;rH zFU{oL{8`ibUS+1tOPY?BRzg#AZS4ZjFKCw>+w$2(ZZB%wShQV3a}550!L_`lYmRGk zLo?lkhT_FAN{$971g6CPs7~g2^acn4bA{zl^NVOnjn)+jvY@X{t%hOI56h0soyaJj znsd!X10Pzkr{plLm&}=#>6-bHSuylX#8VujRODS4*`Wb7?aA@uQ`z%ayOUkuX5Pxq z*oE?Tp&0xOcPf5WTTOJOx3>E;NboC6e5PCpE1Ci%6A|7Yx^TN?rKvbGDziR;EJg zWD_bGiBqQGw6fNnDdY>4RO^0-GfZkp5Puhg>lFapx%xI#TUW136?7Mixbwo3TBeRv|nErWz_?OktrQDs<_V@AcV& zy|}~;$MG{0a+Pit+~@3^RHlTYrW;Oyn+s$E;rSHW!Im=-{sNZaDU@OEM0_uZgVVly zFVx+c1_cLT%yP;%9VjIm3BCVY{`6~(3G zP%ocE!k33?v>|Ezh7Wr{NGFphE~T=1xfE;Es^oJWrDK#}9amVDVzi@#dq_<8-J(!5 z+`>g4PjR#(6|_W9-gJyMKlo<8R5oTgw{c+LBBY(Adjif4V{yq{(U6o1Hs|FfcQWv$ z%22a2HQLNczcP^6@iN;GbC-Y+5xLk%0JD!Z(E^-bh96W`RjfhV# z2ER1Kl|Z)aWlpBU`HzFJBWP7Iaizc2;RNez=;OE`pW+ezmKt1NsWB-&D?Jz(+1z#f z{=m%Ez|3ZH=3nVuH?q~Sk0&1-_)1NdcE0|QfqYKnAFsdpr=x#-<2#?jU_*de;B{kz23bhp3a;|I}|x_1C?-2+-RW6ijgtKWr@AdEM=K}Uw)r0BcZe=7@BL8g?SClj9l5#ymSS) z&%KvXI30$bQnK^9*M9=%)12*|u*;S%Jjjd9zH>sJGPSpYbRBtESbRDz zhtdxjSb+C4RBkd?!lz!s2bv!FM7|BpyFrq9nf>6uNIzGkaLPegEA(`73B1G1QI;b< zxCFI?M^|tKb2tvR3X122p%%|8#W^AnI&_U|a`9k@QWH85Uve*3M>o@4iW zj&1cEBlyPu+W#P#T0eDNzj3L$`+jn4OCUs0jtjXU2*vm~La~+Cl(yB#1HMpI++a3d znr8Anrc5hc_qrs|r(^90>i-Grwj&7v83geEi1avxVoSb%7S@~}0uuZ%2q5N1s1YE{ z4^Wc;<}ZVUuq9C#Tbl8^q_z(7h1l)|wnMva4OI{SbmZ2^+RXZCf^{!};}e2)FTvWv zYf9T{by%OR>hxhj8^dqPe^I98u6t1lbS^VMzwNC2qLIsaiCk{c*2^WTCv&;W<$S3w z>B{AFy8yO~Yc!{Uyx0;)AaHj@7|N6Jqr$%h(ft@ffvXcAL6y^0hVLhGkO)PCu;?A; zUjym46Q|@M2we~y4Y^}Acxn+zjbXPSQAOQf-BDxsw38%@_SPa%^>9_LF?^cjp$yU0 zZwp~9RaYCnULcL(CTUR{4%9|Bj*!M^leDOf)>3gbu`yO-__ULqUFx0DxPiDy(*p-&cr4Qy|w6fBnj($I(;bkpFcw? GcJd#=r7#o# diff --git a/MLEBot/types/enums.py b/MLEBot/types/enums.py index ce7cf3d..9736ddd 100644 --- a/MLEBot/types/enums.py +++ b/MLEBot/types/enums.py @@ -1,3 +1,6 @@ +""" MLE League Enumeration Class + """ + from enum import Enum diff --git a/MLEBot/types/franchise.py b/MLEBot/types/franchise.py index 5c99541..f0e22fd 100644 --- a/MLEBot/types/franchise.py +++ b/MLEBot/types/franchise.py @@ -1,16 +1,32 @@ +"""Minor League E-Sports Franchise and Team Dataclasses + """ + from dataclasses import dataclass from .player import PlayerRL +from ..services import const + + +class MLEFranchiseTeam(list[PlayerRL]): + """sub team for MLE franchises (think 'ML' or 'AL') + inherits list properties, used to add functions + """ + + def slot_sorted_players(self): + """get all players sorted by slot + """ + return sorted(self, + key=lambda x: x.slot) @dataclass class TeamRocketLeague: """Minor League E-Sports Rocket League Franchise Team """ - pl: list[PlayerRL] - ml: list[PlayerRL] - cl: list[PlayerRL] - al: list[PlayerRL] - fl: list[PlayerRL] + pl: MLEFranchiseTeam + ml: MLEFranchiseTeam + cl: MLEFranchiseTeam + al: MLEFranchiseTeam + fl: MLEFranchiseTeam def all_players(self): """get all players for this team @@ -20,13 +36,37 @@ def all_players(self): """ return self.pl + self.ml + self.cl + self.al + self.fl + def by_skill_group(self, + skill_group: str) -> MLEFranchiseTeam: + """get franchise team by skill group string + + Args: + skill_group (str): sprocket const skill group + + Returns: + MLEFranchiseTeam: team + """ + match skill_group: + case const.SPR_SG_PL: + return self.pl + case const.SPR_SG_ML: + return self.ml + case const.SPR_SG_CL: + return self.cl + case const.SPR_SG_AL: + return self.al + case const.SPR_SG_FL: + return self.fl + case _: + return None + @dataclass class TeamTrackmania: """Minor League E-Sports Trackmania Franchise Team """ - cl: list[PlayerRL] - al: list[PlayerRL] + cl: MLEFranchiseTeam + al: MLEFranchiseTeam def all_players(self): """get all players for this team @@ -52,6 +92,15 @@ class Franchise: captains: list[dict] pr: dict + @property + def name(self) -> str: + """franchise name + + Returns: + str: franchise name + """ + return self.franchise_meta['Franchise'] + def all_players(self) -> list[PlayerRL]: """get all players diff --git a/MLEBot/types/member.py b/MLEBot/types/member.py index 07a96f7..e0b737e 100644 --- a/MLEBot/types/member.py +++ b/MLEBot/types/member.py @@ -1,3 +1,6 @@ +"""MLE Sprocket 'Member' + """ + from dataclasses import dataclass, field from .player import PlayerRL @@ -9,3 +12,21 @@ class Member: member: dict | None = None rl_player: PlayerRL = field(default_factory=PlayerRL()) franchise: dict | None = None + + @property + def mle_id(self) -> str: + """member mle id + + Returns: + str: mle id + """ + return self.member['mle_id'] + + @property + def name(self) -> str: + """member name + + Returns: + str: name + """ + return self.member['name'] diff --git a/MLEBot/types/player.py b/MLEBot/types/player.py index f3c71d4..c16e39b 100644 --- a/MLEBot/types/player.py +++ b/MLEBot/types/player.py @@ -1,3 +1,6 @@ +"""MLE Sprocket Rocket League 'Player' + """ + from dataclasses import dataclass @@ -5,6 +8,127 @@ class PlayerRL: """MLE Sprocket Rocket League 'Player' """ + player: dict | None = None tracker: dict | None = None usage: dict | None = None + + @property + def eligibility_date(self) -> str: + """scrim point elgibility date + + Returns: + str: eligible until: + """ + return self.player["Eligible Until"] + + @property + def eligible(self) -> bool: + """eligible to play + + Returns: + bool: eligible + """ + return self.scrim_points >= 30 + + @property + def franchise(self) -> str: + """player franchise + + Returns: + str: franchise + """ + return self.player["franchise"] + + @property + def league(self) -> str: + """player league (skill group) + + Returns: + str: skill group + """ + return self.skill_group + + @property + def name(self) -> str: + """player name + + Returns: + str: name + """ + return self.player["name"] + + @property + def salary(self) -> str: + """player salary + + Returns: + str: salary + """ + return self.player["salary"] + + @property + def scrim_points(self) -> int: + """scrim points + + Returns: + str: current scrim points + """ + return int(self.player["current_scrim_points"]) + + @property + def skill_group(self) -> str: + """skill group + + Returns: + str: skill group + """ + return self.player["skill_group"] + + @property + def slot(self) -> str: + """player slot + + Returns: + str: slot + """ + return self.player["slot"] + + @property + def staff_position(self) -> str: + """franchise staff position + + Returns: + str: franchise staff position + """ + return self.player["Franchise Staff Position"] + + @property + def usage_doubles(self) -> int: + """player league usages + doubles + + Returns: + str: doubles usage + """ + return int(self.usage["doubles_uses"]) + + @property + def usage_standard(self) -> int: + """player league usages + standard + + Returns: + int: standard usage + """ + return int(self.usage["standard_uses"]) + + @property + def usage_total(self) -> int: + """player league usages + total + + Returns: + int: total usage + """ + return int(self.usage['total_uses']) diff --git a/MLEBot/types/sprocket_links.py b/MLEBot/types/sprocket_links.py index bacba3c..b429113 100644 --- a/MLEBot/types/sprocket_links.py +++ b/MLEBot/types/sprocket_links.py @@ -1,3 +1,6 @@ +"""sprocket data links + """ + from ..services import const from ..types.url_datalink import UrlDataLink diff --git a/MLEBot/types/url_datalink.py b/MLEBot/types/url_datalink.py index 91e9f61..56e014e 100644 --- a/MLEBot/types/url_datalink.py +++ b/MLEBot/types/url_datalink.py @@ -1,3 +1,6 @@ +"""url data link to grab json data from remote server and store it + """ + import datetime import json import requests @@ -13,7 +16,8 @@ def __init__(self, name: str, url_link: str): self.logger = logger(__name__+name) - self.logger.info('initializing link -> %s | url -> %s...', name, url_link) + self.logger.info( + 'initializing link -> %s | url -> %s...', name, url_link) self._time: datetime.datetime | None = None self._data = None self._name = name diff --git a/pyproject.toml b/pyproject.toml index 640f27a..485fd0f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "MLEBot" -version = "1.1.0" +version = "1.1.2" authors = [ { name="Brian LaFond", email="Brian.L.LaFond@gmail.com" }, ] From 31a8fb3e7316568f51058dc8338b1a8f09fea0ff Mon Sep 17 00:00:00 2001 From: Brian LaFond <52360893+iroxusux@users.noreply.github.com> Date: Sat, 5 Apr 2025 19:48:54 -0400 Subject: [PATCH 6/7] added hashing to datalinks, with support of up to 2 hash tables (can increase later but wanted to save time for now). --- MLEBot/services/cmds/mle/lookup.py | 6 +- MLEBot/services/cmds/mle/salary.py | 6 +- MLEBot/services/cmds/mle/showusage.py | 7 +- MLEBot/services/cmds/mle/teameligibility.py | 7 +- MLEBot/services/cmds/mle/teaminfo.py | 7 +- MLEBot/services/const.py | 14 +++ MLEBot/services/sprocket/lookup.py | 127 ++------------------ MLEBot/tasks/sprocket.py | 5 +- MLEBot/types/franchise.py | 70 ++++++++++- MLEBot/types/sprocket_links.py | 58 +++++++-- MLEBot/types/url_datalink.py | 108 ++++++++++++++--- 11 files changed, 253 insertions(+), 162 deletions(-) diff --git a/MLEBot/services/cmds/mle/lookup.py b/MLEBot/services/cmds/mle/lookup.py index 447fd87..4212872 100644 --- a/MLEBot/services/cmds/mle/lookup.py +++ b/MLEBot/services/cmds/mle/lookup.py @@ -25,8 +25,8 @@ async def lookup(self, member: Member = lookup_rl(self._parent.sprocket.links, name=mle_name) if not member: - self._parent.send_notification(interaction, - ERR_BAD_NAME, - as_reply=True) + 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/services/cmds/mle/salary.py b/MLEBot/services/cmds/mle/salary.py index d25f7e3..27a8bb7 100644 --- a/MLEBot/services/cmds/mle/salary.py +++ b/MLEBot/services/cmds/mle/salary.py @@ -23,8 +23,8 @@ async def lookup(self, member: Member = lookup_rl(self._parent.sprocket.links, discord_id=interaction.user.id) if not member: - self._parent.send_notification(interaction, - ERR_BAD_NAME, - as_reply=True) + 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/services/cmds/mle/showusage.py b/MLEBot/services/cmds/mle/showusage.py index a06e6b4..65f340e 100644 --- a/MLEBot/services/cmds/mle/showusage.py +++ b/MLEBot/services/cmds/mle/showusage.py @@ -27,8 +27,9 @@ async def showusage(self, franchise = lookup_franchise(self._parent.sprocket.links, discord_id=interaction.user.id) if not franchise: - return await self._parent.send_notification(interaction, - ERR_BAD_FRANCHISE, - as_followup=True) + 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/services/cmds/mle/teameligibility.py b/MLEBot/services/cmds/mle/teameligibility.py index a663899..8e49331 100644 --- a/MLEBot/services/cmds/mle/teameligibility.py +++ b/MLEBot/services/cmds/mle/teameligibility.py @@ -38,9 +38,10 @@ async def teameligibility(self, franchise: Franchise = lookup_franchise(self._parent.sprocket.links, discord_id=interaction.user.id) if not franchise: - return await self._parent.send_notification(interaction, - const.ERR_BAD_FRANCHISE, - as_followup=True) + 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/services/cmds/mle/teaminfo.py b/MLEBot/services/cmds/mle/teaminfo.py index 16e7cad..c323d05 100644 --- a/MLEBot/services/cmds/mle/teaminfo.py +++ b/MLEBot/services/cmds/mle/teaminfo.py @@ -24,8 +24,9 @@ async def teaminfo(self, franchise: Franchise = lookup_franchise(self._parent.sprocket.links, name=team_name) if not franchise: - return await self._parent.send_notification(interaction, - ERR_BAD_FRANCHISE, - as_followup=True) + 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/services/const.py b/MLEBot/services/const.py index f94d2c0..c23596e 100644 --- a/MLEBot/services/const.py +++ b/MLEBot/services/const.py @@ -25,6 +25,20 @@ 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' diff --git a/MLEBot/services/sprocket/lookup.py b/MLEBot/services/sprocket/lookup.py index 1b21b31..4798374 100644 --- a/MLEBot/services/sprocket/lookup.py +++ b/MLEBot/services/sprocket/lookup.py @@ -3,7 +3,7 @@ import difflib from ...services import const -from ...types import Member, SprocketLinks, PlayerRL, Franchise, TeamRocketLeague, TeamTrackmania, MLEFranchiseTeam +from ...types import Member, SprocketLinks, PlayerRL, Franchise def lookup_franchise(sprocket_data: SprocketLinks, @@ -18,88 +18,14 @@ def lookup_franchise(sprocket_data: SprocketLinks, Returns: Franchise | None: _description_ """ - franchise: dict | None = None - if discord_id: member: Member = lookup_rl(sprocket_data, discord_id=discord_id) if not member or not member.franchise: return None - franchise = member.franchise - elif name: - franchise = _get_franchise(sprocket_data, - name=name) - if not franchise: - return None - - else: - return None + name = member.franchise['Franchise'] - # gather rl data - p_rl = [x for x in sprocket_data.players.data if x['franchise'] - == franchise['Franchise']] - - pl = [PlayerRL(x, - next( - (y for y in sprocket_data.trackers.data if y['mleid'] == x['member_id']), None), - next((y for y in sprocket_data.usages.data if y['role'] == x['slot']), None)) - for x in p_rl if x['skill_group'] == const.SPR_SG_PL and x['slot'] != 'NONE'] - - ml = [PlayerRL(x, - next( - (y for y in sprocket_data.trackers.data if y['mleid'] == x['member_id']), None), - next((y for y in sprocket_data.usages.data if y['role'] == x['slot']), None)) - for x in p_rl if x['skill_group'] == const.SPR_SG_ML and x['slot'] != 'NONE'] - - cl = [PlayerRL(x, - next( - (y for y in sprocket_data.trackers.data if y['mleid'] == x['member_id']), None), - next((y for y in sprocket_data.usages.data if y['role'] == x['slot']), None)) - for x in p_rl if x['skill_group'] == const.SPR_SG_CL and x['slot'] != 'NONE'] - - al = [PlayerRL(x, - next( - (y for y in sprocket_data.trackers.data if y['mleid'] == x['member_id']), None), - next((y for y in sprocket_data.usages.data if y['role'] == x['slot']), None)) - for x in p_rl if x['skill_group'] == const.SPR_SG_AL and x['slot'] != 'NONE'] - - fl = [PlayerRL(x, - next( - (y for y in sprocket_data.trackers.data if y['mleid'] == x['member_id']), None), - next((y for y in sprocket_data.usages.data if y['role'] == x['slot']), None)) - for x in p_rl if x['skill_group'] == const.SPR_SG_FL and x['slot'] != 'NONE'] - - rl_team = TeamRocketLeague( - pl=MLEFranchiseTeam(pl), - ml=MLEFranchiseTeam(ml), - cl=MLEFranchiseTeam(cl), - al=MLEFranchiseTeam(al), - fl=MLEFranchiseTeam(fl), - ) - - # gather tm data - p_tm = [] - - tm_team = TeamTrackmania( - cl=MLEFranchiseTeam([]), - al=MLEFranchiseTeam([]) - ) - - # compile - return Franchise( - players_rl=rl_team, - players_tm=tm_team, - players_rl_meta=p_rl, - players_tm_meta=p_tm, - franchise_meta=franchise, - fm=next( - (x for x in p_rl if x['Franchise Staff Position'] == 'Franchise Manager'), None), - gms=[x for x in p_rl if x['Franchise Staff Position'] == 'General Manager'], - agms=[x for x in p_rl if x['Franchise Staff Position'] - == 'Assistant General Manager'], - captains=[x for x in p_rl if x['Franchise Staff Position'] == 'Captain'], - pr=[x for x in p_rl if x['Franchise Staff Position'] == 'PR Support'], - ) + return sprocket_data.franchise(name) def lookup_rl(sprocket_data: SprocketLinks, @@ -119,29 +45,31 @@ def lookup_rl(sprocket_data: SprocketLinks, if name: member = _get_member(sprocket_data, name, try_match=True) + elif discord_id: - member = _get_id_member(sprocket_data, discord_id) + member = sprocket_data.members.from_hash(str(discord_id)) + else: member = None if not member: return None - player = _get_player(sprocket_data, member) + 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=_get_tracker(sprocket_data, member)), - franchise=_get_franchise(sprocket_data, 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 = next((x for x in members if x['name'].lower() == name.lower()), None) + 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], @@ -152,38 +80,3 @@ def _get_member(sprocket_data: SprocketLinks, == matches[0].lower()), None) return m - - -def _get_id_member(sprocket_data: SprocketLinks, - discord_id: int | str): - members = sprocket_data.members.data - return next((x for x in members if x['discord_id'] == str(discord_id)), None) - - -def _get_player(sprocket_data: SprocketLinks, - member: dict): - players = sprocket_data.players.data - return next((x for x in players if x['member_id'] == member['member_id']), - None) - - -def _get_franchise(sprocket_data: SprocketLinks, - player: dict | None = None, - name: str | None = None): - franchises = sprocket_data.teams.data - - if player: - return next((x for x in franchises if x['Franchise'] == player['franchise']), - None) - elif name: - return next((x for x in franchises if x['Franchise'].lower() == name.lower()), - None) - else: - return None - - -def _get_tracker(sprocket_data: SprocketLinks, - member: dict): - trackers = sprocket_data.trackers.data - return next((x for x in trackers if x['mleid'] == member['mle_id']), - None) diff --git a/MLEBot/tasks/sprocket.py b/MLEBot/tasks/sprocket.py index 70474af..3ed1536 100644 --- a/MLEBot/tasks/sprocket.py +++ b/MLEBot/tasks/sprocket.py @@ -40,8 +40,7 @@ def iterlinks(self) -> list[UrlDataLink]: Returns: tuple[SprocketLinks]: sprocket data links """ - items = self._links.links() - return items + return self._links.links() @property def ready_to_update(self) -> bool: @@ -102,6 +101,7 @@ async def _update(self): thread.join() self._last_time_ran = datetime.datetime.now() + self._links.compile_data() def load(self): """load run times from artifacts file @@ -135,6 +135,7 @@ def init(self): return for link in self.iterlinks: link.init() + self._links.compile_data() self.load() async def run(self): diff --git a/MLEBot/types/franchise.py b/MLEBot/types/franchise.py index f0e22fd..1e1d005 100644 --- a/MLEBot/types/franchise.py +++ b/MLEBot/types/franchise.py @@ -2,6 +2,7 @@ """ from dataclasses import dataclass +from typing import Self from .player import PlayerRL from ..services import const @@ -92,6 +93,72 @@ class Franchise: captains: list[dict] pr: dict + @classmethod + def from_sprocket_links(cls, + meta_data: dict, + sprocket_data) -> Self: + """compile franchise from sprocket links data + + Args: + meta_data (dict): sprocket dict of the franchise to create + sprocket_data (SprocketLinks): SprocketLinks class + + Returns: + Self: Franchise class + """ + + def _create_team(skill_group, + sprocket_data, + players, + usages) -> list[PlayerRL]: + return [PlayerRL(x, + sprocket_data.trackers.from_hash(x['member_id']), + next((y for y in usages if y['role'] == x['slot'] and y['league'] == x['skill_group']), None)) + for x in players if x['skill_group'] == skill_group and x['slot'] != 'NONE'] + + # gather rl data + p_rl = [x for x in sprocket_data.players.data if x['franchise'] == meta_data['Franchise']] + u_rl = [x for x in sprocket_data.usages.data if x['team_name'] == meta_data['Franchise']] + + rl_team = TeamRocketLeague( + pl=MLEFranchiseTeam(_create_team( + const.SPR_SG_PL, sprocket_data, p_rl, u_rl)), + ml=MLEFranchiseTeam(_create_team( + const.SPR_SG_ML, sprocket_data, p_rl, u_rl)), + cl=MLEFranchiseTeam(_create_team( + const.SPR_SG_CL, sprocket_data, p_rl, u_rl)), + al=MLEFranchiseTeam(_create_team( + const.SPR_SG_AL, sprocket_data, p_rl, u_rl)), + fl=MLEFranchiseTeam(_create_team( + const.SPR_SG_FL, sprocket_data, p_rl, u_rl)), + ) + + # gather tm data + p_tm = [] + + tm_team = TeamTrackmania( + cl=MLEFranchiseTeam([]), + al=MLEFranchiseTeam([]) + ) + + # compile + return cls( + players_rl=rl_team, + players_tm=tm_team, + players_rl_meta=p_rl, + players_tm_meta=p_tm, + franchise_meta=meta_data, + fm=next( + (x for x in p_rl if x['Franchise Staff Position'] == 'Franchise Manager'), None), + gms=[x for x in p_rl if x['Franchise Staff Position'] + == 'General Manager'], + agms=[x for x in p_rl if x['Franchise Staff Position'] + == 'Assistant General Manager'], + captains=[ + x for x in p_rl if x['Franchise Staff Position'] == 'Captain'], + pr=[x for x in p_rl if x['Franchise Staff Position'] == 'PR Support'], + ) + @property def name(self) -> str: """franchise name @@ -110,7 +177,7 @@ def all_players(self) -> list[PlayerRL]: return self.players_rl.all_players().extend(self.players_tm.all_players()) def get_skill_group(self, - team: list[PlayerRL]) -> str: + team: list[PlayerRL]) -> str | None: """cheap and easy get of skill group Args: @@ -121,3 +188,4 @@ def get_skill_group(self, """ if len(team) != 0: return team[0].player['skill_group'] + return None diff --git a/MLEBot/types/sprocket_links.py b/MLEBot/types/sprocket_links.py index b429113..e7bebe3 100644 --- a/MLEBot/types/sprocket_links.py +++ b/MLEBot/types/sprocket_links.py @@ -2,23 +2,59 @@ """ from ..services import const -from ..types.url_datalink import UrlDataLink +from . import franchise +from .url_datalink import UrlDataLink class SprocketLinks: """sprocket data links """ + def __init__(self): - self.members: UrlDataLink = UrlDataLink('members', const.SPR_DL_MEMBERS) - self.players: UrlDataLink = UrlDataLink('players', const.SPR_DL_PLAYERS) - self.player_stats: UrlDataLink = UrlDataLink('player_stats', const.SPR_DL_PLA_STATS) - self.scrims: UrlDataLink = UrlDataLink('scrims', const.SPR_DL_SCRIMS) - self.teams: UrlDataLink = UrlDataLink('teams', const.SPR_DL_TEAMS) - self.fixtures: UrlDataLink = UrlDataLink('fixtures', const.SPR_DL_FIXT) - self.match_grps: UrlDataLink = UrlDataLink('match_groups', const.SPR_DL_MAT_GRP) - self.matches: UrlDataLink = UrlDataLink('matches', const.SPR_DL_MATCHES) - self.trackers: UrlDataLink = UrlDataLink('trackers', const.SPR_DL_TRACKER) - self.usages: UrlDataLink = UrlDataLink('usages', const.SPR_DL_USAGE) + self.members: UrlDataLink = UrlDataLink('members', const.SPR_DL_MEMBERS, const.SPR_HK_MEMBERS, + const.SPR_HK_MEMBERS_SECONDARY) + self.players: UrlDataLink = UrlDataLink( + 'players', const.SPR_DL_PLAYERS, const.SPR_HK_PLAYERS) + self.player_stats: UrlDataLink = UrlDataLink( + 'player_stats', const.SPR_DL_PLA_STATS, const.SPR_HK_PLA_STATS) + self.scrims: UrlDataLink = UrlDataLink( + 'scrims', const.SPR_DL_SCRIMS, const.SPR_HK_SCRIMS) + self.teams: UrlDataLink = UrlDataLink( + 'teams', const.SPR_DL_TEAMS, const.SPR_HK_TEAMS) + self.fixtures: UrlDataLink = UrlDataLink( + 'fixtures', const.SPR_DL_FIXT, const.SPR_HK_FIXT) + self.match_grps: UrlDataLink = UrlDataLink( + 'match_groups', const.SPR_DL_MAT_GRP, const.SPR_HK_MAT_GRP) + self.matches: UrlDataLink = UrlDataLink( + 'matches', const.SPR_DL_MATCHES, const.SPR_HK_MATCHES) + self.trackers: UrlDataLink = UrlDataLink( + 'trackers', const.SPR_DL_TRACKER, const.SPR_HK_TRACKER) + self.usages: UrlDataLink = UrlDataLink( + 'usages', const.SPR_DL_USAGE, const.SPR_HK_USAGE) + + self._franchises: dict = {} + + def compile_data(self) -> None: + """compile data (usually directly after updating all links) + """ + if not self.teams.data: + return + + for f in self.teams.data: + self._franchises[f['Franchise'].lower()] = franchise.Franchise.from_sprocket_links(f, + self) + + def franchise(self, + name: str) -> franchise.Franchise | None: + """get franchise by name + + Args: + name (str): franchise name + + Returns: + (Franchise | None): franchise or none + """ + return self._franchises.get(name.lower(), None) def links(self): """get iterable links from this sprocket links class diff --git a/MLEBot/types/url_datalink.py b/MLEBot/types/url_datalink.py index 56e014e..99d193b 100644 --- a/MLEBot/types/url_datalink.py +++ b/MLEBot/types/url_datalink.py @@ -1,8 +1,8 @@ """url data link to grab json data from remote server and store it """ -import datetime import json +from typing import Any import requests from pydiscobot.services.log import logger from ..services.const import URL_REQ_TIMEOUT @@ -10,18 +10,23 @@ class UrlDataLink: """url data link to grab json data from remote server and store it + currently supports 2 hashes. + could extend to any amount, but not currently needed, so i'll worry about that logic later """ def __init__(self, name: str, - url_link: str): + url_link: str, + hash_key: str | None = None, + second_hash_key: str | None = None): self.logger = logger(__name__+name) - self.logger.info( - 'initializing link -> %s | url -> %s...', name, url_link) - self._time: datetime.datetime | None = None self._data = None + self._hash_data = None + self._secondary_hash_data = None self._name = name self._url = url_link + self._hash_key = hash_key + self._secondary_hash_key = second_hash_key self._initialized: bool = False @property @@ -33,6 +38,16 @@ def data(self) -> any: """ return self._data + @data.setter + def data(self, value: dict) -> None: + """set stored data + also, process hash info + + Args: + value (dict): dictionary to store into data + """ + self._process_data(value) + @property def json_link(self) -> str: """url link appended with .json for easy direct-to-file saving @@ -42,6 +57,33 @@ def json_link(self) -> str: """ return '.' + self._name + '.json' + def _clear(self) -> None: + self._data = None + self._hash_data = None + self._secondary_hash_data = None + + def _process_data(self, + data: dict) -> None: + """hash data for quick lookup + """ + self._clear() + + self._data = data + + if not self._hash_key: + return + + self._hash_data = {} + for key in data: + self._hash_data[key[self._hash_key]] = key + + if not self._secondary_hash_key: + return + + self._secondary_hash_data = {} + for key in data: + self._secondary_hash_data[key[self._secondary_hash_key]] = key + def compress(self) -> dict: """compress data to dict @@ -50,7 +92,6 @@ def compress(self) -> dict: """ return { 'data': self._data, - 'time': self._time.strftime("%d/%m/%Y, %H:%M:%S"), } def decompress(self, data: dict): @@ -60,13 +101,10 @@ def decompress(self, data: dict): data (dict): data to restore into link """ try: - self._data = data['data'] - self._time = datetime.datetime.strptime( - data['time'], "%d/%m/%Y, %H:%M:%S") - except (json.JSONDecodeError, ValueError, KeyError) as e: + self.data = data['data'] + except (json.decoder.JSONDecodeError, ValueError, KeyError) as e: self.logger.warning('load failure...%s - %s', self._name, e) - self._data = None - self._time = None + self._clear() def fetch(self): """fetch data from url @@ -78,11 +116,46 @@ def fetch(self): raise ValueError('URL Link is empty, cannot fetch data') self.logger.info('fetching %s...', self._url) - self._data = requests.get(self._url, - timeout=URL_REQ_TIMEOUT).json() - self._time = datetime.datetime.now() + self.data = requests.get(self._url, + timeout=URL_REQ_TIMEOUT).json() self.save() + def from_hash(self, + key: str) -> Any: + """get data from hash storage, or none if not stored + + Args: + key (str): key value to lookup + + Raises: + ValueError: no data for key was found (data[key] == NotFound) + + Returns: + Any: data from hash table + """ + if not self._hash_data: + raise ValueError('no hash data to retrieve!') + + return self._hash_data.get(key, None) + + def from_secondary_hash(self, + key: str) -> Any: + """get data from hash storage, or none if not stored + + Args: + key (str): key value to lookup + + Raises: + ValueError: no data for key was found (data[key] == NotFound) + + Returns: + Any: data from hash table + """ + if not self._secondary_hash_data: + raise ValueError('no hash data to retrieve!') + + return self._secondary_hash_data.get(key, None) + def init(self): """initialize """ @@ -107,4 +180,7 @@ def load(self): """ self.logger.info('loading %s...', self.json_link) with open(self.json_link, 'r', encoding='utf-8') as f: - self.decompress(json.load(f)) + try: + self.decompress(json.load(f)) + except json.decoder.JSONDecodeError as e: + self.logger.warning('failed to load file -> %s', e) From c3743c06c0d737cbc42fd3532854a381fec48ddc Mon Sep 17 00:00:00 2001 From: Brian LaFond <52360893+iroxusux@users.noreply.github.com> Date: Sat, 19 Apr 2025 09:01:52 -0400 Subject: [PATCH 7/7] fixed import structure. --- .gitignore | 6 + MLEBot/__init__.py | 13 +- .../{services/cmds => commands}/__init__.py | 7 +- .../cmds => commands}/lo/__init__.py | 13 +- .../cmds => commands}/lo/achilles.py | 8 +- MLEBot/{services/cmds => commands}/lo/adi.py | 8 +- MLEBot/{services/cmds => commands}/lo/bw.py | 8 +- MLEBot/{services/cmds => commands}/lo/haim.py | 8 +- MLEBot/{services/cmds => commands}/lo/hoos.py | 8 +- MLEBot/{services/cmds => commands}/lo/kd.py | 8 +- .../{services/cmds => commands}/lo/kunics.py | 8 +- .../{services/cmds => commands}/lo/maple.py | 8 +- MLEBot/{services/cmds => commands}/lo/ondo.py | 8 +- .../{services/cmds => commands}/lo/rexton.py | 8 +- MLEBot/{services/cmds => commands}/lo/riz.py | 8 +- .../{services/cmds => commands}/lo/soviet.py | 8 +- MLEBot/{services/cmds => commands}/lo/zb.py | 8 +- .../cmds => commands}/mle/__init__.py | 15 +- .../{services/cmds => commands}/mle/lookup.py | 18 +- .../{services/cmds => commands}/mle/query.py | 15 +- .../cmds => commands}/mle/rebuild.py | 8 +- .../{services/cmds => commands}/mle/salary.py | 18 +- .../cmds => commands}/mle/showusage.py | 16 +- .../cmds => commands}/mle/teameligibility.py | 16 +- .../cmds => commands}/mle/teaminfo.py | 17 +- MLEBot/commands/mle/test_commands.py | 25 + .../cmds => commands}/mle/updatesprocket.py | 8 +- .../cmds => commands}/rl/__init__.py | 0 MLEBot/{services => }/const.py | 2 - MLEBot/embed_frames/__init__.py | 18 - MLEBot/embed_frames/card.py | 40 -- MLEBot/embed_frames/salary_card.py | 49 -- MLEBot/embed_frames/teameligibility_card.py | 45 -- MLEBot/embed_frames/teaminfo_card.py | 110 ----- MLEBot/embed_frames/usage_card.py | 65 --- MLEBot/frames.py | 292 ++++++++++++ MLEBot/mlebot.py | 19 +- MLEBot/services/__init__.py | 6 +- MLEBot/services/sprocket/lookup.py | 5 +- MLEBot/tasks/sprocket.py | 54 ++- MLEBot/team/html/TeamQuickInfo.css | 166 ------- MLEBot/team/html/TeamQuickInfo.html | 192 -------- MLEBot/team/html/TeamWeeklyStats.css | 240 ---------- MLEBot/team/html/TeamWeeklyStats.html | 361 --------------- MLEBot/team/imgs/ACADEMY.png | Bin 22658 -> 0 bytes MLEBot/team/imgs/CHAMPION.png | Bin 22122 -> 0 bytes MLEBot/team/imgs/FOUNDATION.png | Bin 21789 -> 0 bytes MLEBot/team/imgs/MASTER.png | Bin 21646 -> 0 bytes MLEBot/team/imgs/STAFF.png | Bin 20349 -> 0 bytes MLEBot/tests/__init__.py | 11 - MLEBot/types/__init__.py | 16 +- MLEBot/types/franchise.py | 191 -------- MLEBot/types/member.py | 32 -- MLEBot/types/player.py | 134 ------ MLEBot/types/sprocket.py | 436 ++++++++++++++++++ MLEBot/types/sprocket_links.py | 62 --- MLEBot/types/test_types.py | 20 + MLEBot/types/url_datalink.py | 17 +- 58 files changed, 1054 insertions(+), 1828 deletions(-) rename MLEBot/{services/cmds => commands}/__init__.py (91%) rename MLEBot/{services/cmds => commands}/lo/__init__.py (96%) rename MLEBot/{services/cmds => commands}/lo/achilles.py (88%) rename MLEBot/{services/cmds => commands}/lo/adi.py (85%) rename MLEBot/{services/cmds => commands}/lo/bw.py (83%) rename MLEBot/{services/cmds => commands}/lo/haim.py (87%) rename MLEBot/{services/cmds => commands}/lo/hoos.py (85%) rename MLEBot/{services/cmds => commands}/lo/kd.py (81%) rename MLEBot/{services/cmds => commands}/lo/kunics.py (83%) rename MLEBot/{services/cmds => commands}/lo/maple.py (84%) rename MLEBot/{services/cmds => commands}/lo/ondo.py (88%) rename MLEBot/{services/cmds => commands}/lo/rexton.py (88%) rename MLEBot/{services/cmds => commands}/lo/riz.py (82%) rename MLEBot/{services/cmds => commands}/lo/soviet.py (89%) rename MLEBot/{services/cmds => commands}/lo/zb.py (85%) rename MLEBot/{services/cmds => commands}/mle/__init__.py (86%) rename MLEBot/{services/cmds => commands}/mle/lookup.py (80%) rename MLEBot/{services/cmds => commands}/mle/query.py (95%) rename MLEBot/{services/cmds => commands}/mle/rebuild.py (91%) rename MLEBot/{services/cmds => commands}/mle/salary.py (78%) rename MLEBot/{services/cmds => commands}/mle/showusage.py (83%) rename MLEBot/{services/cmds => commands}/mle/teameligibility.py (88%) rename MLEBot/{services/cmds => commands}/mle/teaminfo.py (83%) create mode 100644 MLEBot/commands/mle/test_commands.py rename MLEBot/{services/cmds => commands}/mle/updatesprocket.py (88%) rename MLEBot/{services/cmds => commands}/rl/__init__.py (100%) rename MLEBot/{services => }/const.py (99%) delete mode 100644 MLEBot/embed_frames/__init__.py delete mode 100644 MLEBot/embed_frames/card.py delete mode 100644 MLEBot/embed_frames/salary_card.py delete mode 100644 MLEBot/embed_frames/teameligibility_card.py delete mode 100644 MLEBot/embed_frames/teaminfo_card.py delete mode 100644 MLEBot/embed_frames/usage_card.py create mode 100644 MLEBot/frames.py delete mode 100644 MLEBot/team/html/TeamQuickInfo.css delete mode 100644 MLEBot/team/html/TeamQuickInfo.html delete mode 100644 MLEBot/team/html/TeamWeeklyStats.css delete mode 100644 MLEBot/team/html/TeamWeeklyStats.html delete mode 100644 MLEBot/team/imgs/ACADEMY.png delete mode 100644 MLEBot/team/imgs/CHAMPION.png delete mode 100644 MLEBot/team/imgs/FOUNDATION.png delete mode 100644 MLEBot/team/imgs/MASTER.png delete mode 100644 MLEBot/team/imgs/STAFF.png delete mode 100644 MLEBot/tests/__init__.py delete mode 100644 MLEBot/types/franchise.py delete mode 100644 MLEBot/types/member.py delete mode 100644 MLEBot/types/player.py create mode 100644 MLEBot/types/sprocket.py delete mode 100644 MLEBot/types/sprocket_links.py create mode 100644 MLEBot/types/test_types.py diff --git a/.gitignore b/.gitignore index 37697a9..e63870e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,12 @@ __pycache__/ *.py[cod] *$py.class +# vscode environment +.vscode/ + +# json data files +*.json + # C extensions *.so diff --git a/MLEBot/__init__.py b/MLEBot/__init__.py index af0c7d9..e9ddd03 100644 --- a/MLEBot/__init__.py +++ b/MLEBot/__init__.py @@ -1,10 +1,19 @@ """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.0" +__version__ = "1.1.4" __all__ = ( + 'commands', + 'frames', + 'services', + 'tasks', + 'types', "MLEBot", ) diff --git a/MLEBot/services/cmds/__init__.py b/MLEBot/commands/__init__.py similarity index 91% rename from MLEBot/services/cmds/__init__.py rename to MLEBot/commands/__init__.py index b209481..3d02747 100644 --- a/MLEBot/services/cmds/__init__.py +++ b/MLEBot/commands/__init__.py @@ -6,10 +6,11 @@ from .rl import Commands as rl_commands -__version__ = '1.1.1' +Commands = lo_commands + mle_commands + rl_commands + + +__version__ = '1.1.3' __all__ = ( 'Commands', ) - -Commands = lo_commands + mle_commands + rl_commands diff --git a/MLEBot/services/cmds/lo/__init__.py b/MLEBot/commands/lo/__init__.py similarity index 96% rename from MLEBot/services/cmds/lo/__init__.py rename to MLEBot/commands/lo/__init__.py index 03cd898..ef1a7c4 100644 --- a/MLEBot/services/cmds/lo/__init__.py +++ b/MLEBot/commands/lo/__init__.py @@ -17,12 +17,6 @@ from .zb import Zb -__version__ = '1.1.1' - -__all__ = ( - 'Commands', -) - Commands = [ Achilles, Adi, @@ -38,3 +32,10 @@ Soviet, Zb, ] + + +__version__ = '1.1.3' + +__all__ = ( + 'Commands', +) diff --git a/MLEBot/services/cmds/lo/achilles.py b/MLEBot/commands/lo/achilles.py similarity index 88% rename from MLEBot/services/cmds/lo/achilles.py rename to MLEBot/commands/lo/achilles.py index 8ae94a6..0d26007 100644 --- a/MLEBot/services/cmds/lo/achilles.py +++ b/MLEBot/commands/lo/achilles.py @@ -1,12 +1,16 @@ """mr. worldwide """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -class Achilles(Cmd): +from pydiscobot import Cog + + +class Achilles(Cog): """mr. worldwide """ diff --git a/MLEBot/services/cmds/lo/adi.py b/MLEBot/commands/lo/adi.py similarity index 85% rename from MLEBot/services/cmds/lo/adi.py rename to MLEBot/commands/lo/adi.py index 7b9d7ac..f2f90cb 100644 --- a/MLEBot/services/cmds/lo/adi.py +++ b/MLEBot/commands/lo/adi.py @@ -1,12 +1,16 @@ """Brick by boring brick. """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -class Adi(Cmd): +from pydiscobot import Cog + + +class Adi(Cog): """Brick by boring brick. """ diff --git a/MLEBot/services/cmds/lo/bw.py b/MLEBot/commands/lo/bw.py similarity index 83% rename from MLEBot/services/cmds/lo/bw.py rename to MLEBot/commands/lo/bw.py index 45168eb..8e1b3ad 100644 --- a/MLEBot/services/cmds/lo/bw.py +++ b/MLEBot/commands/lo/bw.py @@ -1,12 +1,16 @@ """Galaxy brain """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -class Bw(Cmd): +from pydiscobot import Cog + + +class Bw(Cog): """Galaxy brain """ diff --git a/MLEBot/services/cmds/lo/haim.py b/MLEBot/commands/lo/haim.py similarity index 87% rename from MLEBot/services/cmds/lo/haim.py rename to MLEBot/commands/lo/haim.py index 1dbb94e..d785bc3 100644 --- a/MLEBot/services/cmds/lo/haim.py +++ b/MLEBot/commands/lo/haim.py @@ -1,12 +1,16 @@ """S(HAIM). """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -class Haim(Cmd): +from pydiscobot import Cog + + +class Haim(Cog): """S(HAIM). """ diff --git a/MLEBot/services/cmds/lo/hoos.py b/MLEBot/commands/lo/hoos.py similarity index 85% rename from MLEBot/services/cmds/lo/hoos.py rename to MLEBot/commands/lo/hoos.py index 038f17d..feeed1a 100644 --- a/MLEBot/services/cmds/lo/hoos.py +++ b/MLEBot/commands/lo/hoos.py @@ -1,12 +1,16 @@ """:kissing_heart: """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -class Hoos(Cmd): +from pydiscobot import Cog + + +class Hoos(Cog): """:kissing_heart: """ diff --git a/MLEBot/services/cmds/lo/kd.py b/MLEBot/commands/lo/kd.py similarity index 81% rename from MLEBot/services/cmds/lo/kd.py rename to MLEBot/commands/lo/kd.py index 18ea2be..42bbc16 100644 --- a/MLEBot/services/cmds/lo/kd.py +++ b/MLEBot/commands/lo/kd.py @@ -1,12 +1,16 @@ """KD """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -class Kd(Cmd): +from pydiscobot import Cog + + +class Kd(Cog): """KD """ diff --git a/MLEBot/services/cmds/lo/kunics.py b/MLEBot/commands/lo/kunics.py similarity index 83% rename from MLEBot/services/cmds/lo/kunics.py rename to MLEBot/commands/lo/kunics.py index f9a8b6a..b7020c3 100644 --- a/MLEBot/services/cmds/lo/kunics.py +++ b/MLEBot/commands/lo/kunics.py @@ -1,12 +1,16 @@ """PIVOT """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -class Kunics(Cmd): +from pydiscobot import Cog + + +class Kunics(Cog): """PIVOT """ diff --git a/MLEBot/services/cmds/lo/maple.py b/MLEBot/commands/lo/maple.py similarity index 84% rename from MLEBot/services/cmds/lo/maple.py rename to MLEBot/commands/lo/maple.py index 9ab4daa..308959d 100644 --- a/MLEBot/services/cmds/lo/maple.py +++ b/MLEBot/commands/lo/maple.py @@ -1,12 +1,16 @@ """Oh, Canada... """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -class Maple(Cmd): +from pydiscobot import Cog + + +class Maple(Cog): """Oh, Canada... """ diff --git a/MLEBot/services/cmds/lo/ondo.py b/MLEBot/commands/lo/ondo.py similarity index 88% rename from MLEBot/services/cmds/lo/ondo.py rename to MLEBot/commands/lo/ondo.py index 2339fa7..8b13332 100644 --- a/MLEBot/services/cmds/lo/ondo.py +++ b/MLEBot/commands/lo/ondo.py @@ -1,12 +1,16 @@ """ondofir """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -class Ondo(Cmd): +from pydiscobot import Cog + + +class Ondo(Cog): """ondofir """ diff --git a/MLEBot/services/cmds/lo/rexton.py b/MLEBot/commands/lo/rexton.py similarity index 88% rename from MLEBot/services/cmds/lo/rexton.py rename to MLEBot/commands/lo/rexton.py index d26842f..3ab8265 100644 --- a/MLEBot/services/cmds/lo/rexton.py +++ b/MLEBot/commands/lo/rexton.py @@ -1,12 +1,16 @@ """opens door... """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -class Rexton(Cmd): +from pydiscobot import Cog + + +class Rexton(Cog): """opens door... """ diff --git a/MLEBot/services/cmds/lo/riz.py b/MLEBot/commands/lo/riz.py similarity index 82% rename from MLEBot/services/cmds/lo/riz.py rename to MLEBot/commands/lo/riz.py index ec80483..98f6539 100644 --- a/MLEBot/services/cmds/lo/riz.py +++ b/MLEBot/commands/lo/riz.py @@ -1,12 +1,16 @@ """@Riz """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -class Riz(Cmd): +from pydiscobot import Cog + + +class Riz(Cog): """@Riz """ diff --git a/MLEBot/services/cmds/lo/soviet.py b/MLEBot/commands/lo/soviet.py similarity index 89% rename from MLEBot/services/cmds/lo/soviet.py rename to MLEBot/commands/lo/soviet.py index 8d06b24..dec31f9 100644 --- a/MLEBot/services/cmds/lo/soviet.py +++ b/MLEBot/commands/lo/soviet.py @@ -1,12 +1,16 @@ """Oh, the burning... """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -class Soviet(Cmd): +from pydiscobot import Cog + + +class Soviet(Cog): """Oh, the burning... """ diff --git a/MLEBot/services/cmds/lo/zb.py b/MLEBot/commands/lo/zb.py similarity index 85% rename from MLEBot/services/cmds/lo/zb.py rename to MLEBot/commands/lo/zb.py index f2d2b9a..d24d4d8 100644 --- a/MLEBot/services/cmds/lo/zb.py +++ b/MLEBot/commands/lo/zb.py @@ -1,12 +1,16 @@ """My link is borked, halp """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -class Zb(Cmd): +from pydiscobot import Cog + + +class Zb(Cog): """My link is borked, halp """ @app_commands.command(name='zb', diff --git a/MLEBot/services/cmds/mle/__init__.py b/MLEBot/commands/mle/__init__.py similarity index 86% rename from MLEBot/services/cmds/mle/__init__.py rename to MLEBot/commands/mle/__init__.py index 2d7096e..8ba2130 100644 --- a/MLEBot/services/cmds/mle/__init__.py +++ b/MLEBot/commands/mle/__init__.py @@ -1,5 +1,6 @@ """Minor League E-Sports Bot Commands """ +from . import test_commands from .lookup import Lookup from .query import Query @@ -11,12 +12,6 @@ from .updatesprocket import UpdateSprocket -__version__ = '1.1.1' - -__all__ = ( - 'Commands', -) - Commands = [ Lookup, Query, @@ -27,3 +22,11 @@ TeamInfo, UpdateSprocket, ] + + +__version__ = '1.1.4' + +__all__ = ( + 'Commands', + 'test_commands', +) diff --git a/MLEBot/services/cmds/mle/lookup.py b/MLEBot/commands/mle/lookup.py similarity index 80% rename from MLEBot/services/cmds/mle/lookup.py rename to MLEBot/commands/mle/lookup.py index 4212872..116a6b2 100644 --- a/MLEBot/services/cmds/mle/lookup.py +++ b/MLEBot/commands/mle/lookup.py @@ -1,16 +1,22 @@ """Lookup player by MLE name. """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -from ....embed_frames import salary_card -from ....services.const import ERR_BAD_NAME -from ...sprocket.lookup import lookup_rl -from ....types.member import Member -class Lookup(Cmd): +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. """ diff --git a/MLEBot/services/cmds/mle/query.py b/MLEBot/commands/mle/query.py similarity index 95% rename from MLEBot/services/cmds/mle/query.py rename to MLEBot/commands/mle/query.py index d1d8769..91305ad 100644 --- a/MLEBot/services/cmds/mle/query.py +++ b/MLEBot/commands/mle/query.py @@ -1,15 +1,20 @@ """Lookup groups of players by provided filter. """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot import InteractionPagination -from pydiscobot.types import Cmd -from ....embed_frames import mle_card -from ....services import const -class Query(Cmd): +from pydiscobot import Cog, InteractionPagination + + +from ... import const +from ...frames import mle_card + + +class Query(Cog): """Lookup groups of players by provided filter. """ diff --git a/MLEBot/services/cmds/mle/rebuild.py b/MLEBot/commands/mle/rebuild.py similarity index 91% rename from MLEBot/services/cmds/mle/rebuild.py rename to MLEBot/commands/mle/rebuild.py index 96846c2..6123d3f 100644 --- a/MLEBot/services/cmds/mle/rebuild.py +++ b/MLEBot/commands/mle/rebuild.py @@ -1,12 +1,16 @@ """Rebuild bot meta data. """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -class Rebuild(Cmd): +from pydiscobot import Cog + + +class Rebuild(Cog): """Rebuild bot meta data. """ diff --git a/MLEBot/services/cmds/mle/salary.py b/MLEBot/commands/mle/salary.py similarity index 78% rename from MLEBot/services/cmds/mle/salary.py rename to MLEBot/commands/mle/salary.py index 27a8bb7..f6292d3 100644 --- a/MLEBot/services/cmds/mle/salary.py +++ b/MLEBot/commands/mle/salary.py @@ -1,16 +1,22 @@ """Show player salary. """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -from ....embed_frames import salary_card -from ....services.const import ERR_BAD_NAME -from ...sprocket.lookup import lookup_rl -from ....types.member import Member -class Salary(Cmd): +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. """ diff --git a/MLEBot/services/cmds/mle/showusage.py b/MLEBot/commands/mle/showusage.py similarity index 83% rename from MLEBot/services/cmds/mle/showusage.py rename to MLEBot/commands/mle/showusage.py index 65f340e..cae0784 100644 --- a/MLEBot/services/cmds/mle/showusage.py +++ b/MLEBot/commands/mle/showusage.py @@ -1,15 +1,21 @@ """show play usage of players for a franchise. """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -from ....embed_frames import usage_card -from ....services.const import ERR_BAD_FRANCHISE -from ...sprocket.lookup import lookup_franchise -class ShowUsage(Cmd): +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. """ diff --git a/MLEBot/services/cmds/mle/teameligibility.py b/MLEBot/commands/mle/teameligibility.py similarity index 88% rename from MLEBot/services/cmds/mle/teameligibility.py rename to MLEBot/commands/mle/teameligibility.py index 8e49331..5056018 100644 --- a/MLEBot/services/cmds/mle/teameligibility.py +++ b/MLEBot/commands/mle/teameligibility.py @@ -1,16 +1,22 @@ """Show team eligibility. """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -from ....embed_frames import teameligibility_card -from ....types import Franchise -from ...sprocket import lookup_franchise + + +from pydiscobot import Cog + + from ... import const +from ...frames import teameligibility_card +from ...types import Franchise +from ...services.sprocket import lookup_franchise -class TeamEligibility(Cmd): +class TeamEligibility(Cog): """Show team eligibility. """ diff --git a/MLEBot/services/cmds/mle/teaminfo.py b/MLEBot/commands/mle/teaminfo.py similarity index 83% rename from MLEBot/services/cmds/mle/teaminfo.py rename to MLEBot/commands/mle/teaminfo.py index c323d05..1855d84 100644 --- a/MLEBot/services/cmds/mle/teaminfo.py +++ b/MLEBot/commands/mle/teaminfo.py @@ -1,13 +1,20 @@ +from __future__ import annotations + + import discord from discord import app_commands -from pydiscobot.types import Cmd -from ....embed_frames import teaminfo_card -from ...sprocket import lookup_franchise -from ....types import Franchise + + +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(Cmd): +class TeamInfo(Cog): """Get info about a team. """ 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/services/cmds/mle/updatesprocket.py b/MLEBot/commands/mle/updatesprocket.py similarity index 88% rename from MLEBot/services/cmds/mle/updatesprocket.py rename to MLEBot/commands/mle/updatesprocket.py index b632339..46e563e 100644 --- a/MLEBot/services/cmds/mle/updatesprocket.py +++ b/MLEBot/commands/mle/updatesprocket.py @@ -1,12 +1,16 @@ """Update info from sprocket. """ +from __future__ import annotations + import discord from discord import app_commands -from pydiscobot.types import Cmd -class UpdateSprocket(Cmd): +from pydiscobot import Cog + + +class UpdateSprocket(Cog): """Update info from sprocket. """ diff --git a/MLEBot/services/cmds/rl/__init__.py b/MLEBot/commands/rl/__init__.py similarity index 100% rename from MLEBot/services/cmds/rl/__init__.py rename to MLEBot/commands/rl/__init__.py diff --git a/MLEBot/services/const.py b/MLEBot/const.py similarity index 99% rename from MLEBot/services/const.py rename to MLEBot/const.py index c23596e..10ead5f 100644 --- a/MLEBot/services/const.py +++ b/MLEBot/const.py @@ -1,8 +1,6 @@ """Minor League E-Sports Constants """ -URL_REQ_TIMEOUT = 30.0 - # 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!' diff --git a/MLEBot/embed_frames/__init__.py b/MLEBot/embed_frames/__init__.py deleted file mode 100644 index 29c352c..0000000 --- a/MLEBot/embed_frames/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -"""embed frames for MLEBot - """ - -from .card import mle_card -from .salary_card import salary_card -from .teameligibility_card import teameligibility_card -from .teaminfo_card import teaminfo_card -from .usage_card import usage_card - -__version__ = "1.1.2" - -__all__ = ( - "mle_card", - "salary_card", - 'teameligibility_card', - 'teaminfo_card', - "usage_card", -) diff --git a/MLEBot/embed_frames/card.py b/MLEBot/embed_frames/card.py deleted file mode 100644 index 5b22649..0000000 --- a/MLEBot/embed_frames/card.py +++ /dev/null @@ -1,40 +0,0 @@ -"""general card for MLE - this card contains franchise and color information - it should be used for all interaction - """ - -import os -import discord -from pydiscobot import frame, EmbedField - - -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(title, - descr, - fields, - color_str, - url_str) - - return embed diff --git a/MLEBot/embed_frames/salary_card.py b/MLEBot/embed_frames/salary_card.py deleted file mode 100644 index 9e18556..0000000 --- a/MLEBot/embed_frames/salary_card.py +++ /dev/null @@ -1,49 +0,0 @@ -"""salary card - display player league information - """ - -from pydiscobot import EmbedField -from .card import mle_card -from ..services.const import SPR_INFO -from ..types import Member - - -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 = 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 diff --git a/MLEBot/embed_frames/teameligibility_card.py b/MLEBot/embed_frames/teameligibility_card.py deleted file mode 100644 index 72be93d..0000000 --- a/MLEBot/embed_frames/teameligibility_card.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Minor League E-Sports Rocket League - Team Eligibility Card - """ - -from pydiscobot import EmbedField -from .card import mle_card -from ..services.const import SPR_INFO -from ..types import Franchise, MLEFranchiseTeam - - -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 = 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) diff --git a/MLEBot/embed_frames/teaminfo_card.py b/MLEBot/embed_frames/teaminfo_card.py deleted file mode 100644 index f4ccc5a..0000000 --- a/MLEBot/embed_frames/teaminfo_card.py +++ /dev/null @@ -1,110 +0,0 @@ -"""Minor League E-Sports Rocket League - Team Info Card - """ - -from pydiscobot import EmbedField -from .card import mle_card -from ..types import Franchise, PlayerRL -from ..services import const - - -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) diff --git a/MLEBot/embed_frames/usage_card.py b/MLEBot/embed_frames/usage_card.py deleted file mode 100644 index f18ece0..0000000 --- a/MLEBot/embed_frames/usage_card.py +++ /dev/null @@ -1,65 +0,0 @@ -from pydiscobot import EmbedField -from .card import mle_card -from ..types import Franchise, PlayerRL -from ..services import const - - -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/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/mlebot.py b/MLEBot/mlebot.py index 4ffb876..a659a27 100644 --- a/MLEBot/mlebot.py +++ b/MLEBot/mlebot.py @@ -1,12 +1,17 @@ """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 .services.cmds import Commands -from .services import const + + +from .commands import Commands +from . import const from .tasks.sprocket import Sprocket @@ -31,7 +36,8 @@ def __init__(self, @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.') + 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]: @@ -73,9 +79,12 @@ async def on_ready(self, Args: suppress_task (bool, optional): don't run periodic task. Defaults to False. """ - if self._admin_info.initialized: + if self.status.initialized: self.logger.warning('already initialized!') return await super().on_ready(suppress_task) await self._build_guilds() - await self.change_presence(activity=self.activity) + + 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/services/__init__.py b/MLEBot/services/__init__.py index 5463097..7374131 100644 --- a/MLEBot/services/__init__.py +++ b/MLEBot/services/__init__.py @@ -1,15 +1,13 @@ """MLE Bot Logical Services """ -from . import const -from .cmds import Commands +from . import sprocket from .sprocket import lookup_franchise, lookup_rl __version__ = '1.1.1' __all__ = ( - 'const', - 'Commands', + 'sprocket', 'lookup_franchise', 'lookup_rl', ) diff --git a/MLEBot/services/sprocket/lookup.py b/MLEBot/services/sprocket/lookup.py index 4798374..88d2eb2 100644 --- a/MLEBot/services/sprocket/lookup.py +++ b/MLEBot/services/sprocket/lookup.py @@ -1,8 +1,11 @@ """provide methods to distribute sprocket data, such as players or franchises """ +from __future__ import annotations + import difflib -from ...services import const + +from ... import const from ...types import Member, SprocketLinks, PlayerRL, Franchise diff --git a/MLEBot/tasks/sprocket.py b/MLEBot/tasks/sprocket.py index 3ed1536..310cb22 100644 --- a/MLEBot/tasks/sprocket.py +++ b/MLEBot/tasks/sprocket.py @@ -1,10 +1,17 @@ """Manage Sprocket data links for up-to-date information """ +from __future__ import annotations + import datetime import json import threading -from pydiscobot.types import Task + + +from pydiscobot import Task +from pydiscobot.types.err import BotNotLoaded + + from ..types import SprocketLinks, UrlDataLink @@ -16,6 +23,7 @@ def __init__(self, parent): self._links = SprocketLinks() self._loaded = False + self._updating = False self.on_updated: list[callable] = [] @@ -31,6 +39,8 @@ def links(self) -> SprocketLinks: Returns: SprocketLinks: dataclass of sprocket url datalinks """ + if self.updating: + raise BotNotLoaded('Sprocket links are updating!') return self._links @property @@ -40,7 +50,7 @@ def iterlinks(self) -> list[UrlDataLink]: Returns: tuple[SprocketLinks]: sprocket data links """ - return self._links.links() + return self.links.links() @property def ready_to_update(self) -> bool: @@ -62,6 +72,15 @@ def json_link(self) -> str: """ 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...') @@ -94,15 +113,31 @@ async def _update(self): threads = [threading.Thread(name='fetch', target=link.fetch) for link in self.iterlinks] - for thread in threads: - thread.start() + self._updating = True + # any calls to our 'links' attr here + # will raise an exception - for thread in threads: - thread.join() + 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 """ @@ -146,13 +181,6 @@ async def run(self): if self.ready_to_update: self.logger.info('updating links...') await self._update() - else: - return - self._calc_next_fetch_time() - self.save() - for callback in self.on_updated: - await callback() - await self.parent.notify('sprocket server links updated.') def save(self): """save run time data to artifacts file 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 @@ - - - - - - -
-
-
-

- {league:} -

-
- -
- - - - Series Wins - Series Losses - Game Wins - Game Losses - - - - - Standard - - - {std_series_wins:} - - - {std_series_losses:} - - - {std_game_wins:} - - - {std_game_losses:} - - - - - - Doubles - - - {dbl_series_wins:} - - - {dbl_series_losses:} - - - {dbl_game_wins:} - - - {dbl_game_losses:} - - - - - Role - - Name - - Scheduler - - - - PLAYERA - - - {player_a_mle_name:} - - - - - - - - PLAYERB - - - {player_b_mle_name:} - - - - - - - - PLAYERC - - - {player_c_mle_name:} - - - - - - - - PLAYERD - - - {player_d_mle_name:} - - - - - - - - PLAYERE - - - {player_e_mle_name:} - - - - - - - - PLAYERF - - - {player_f_mle_name:} - - - - - - - - PLAYERG - - - {player_g_mle_name:} - - - - - - - - PLAYERH - - - {player_h_mle_name:} - - - - - -
- - - \ No newline at end of file diff --git a/MLEBot/team/html/TeamWeeklyStats.css b/MLEBot/team/html/TeamWeeklyStats.css deleted file mode 100644 index f77d30d..0000000 --- a/MLEBot/team/html/TeamWeeklyStats.css +++ /dev/null @@ -1,240 +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; -} - -h2 { -color: lightgrey; -font-family: Verdana Black, sans-serif; -text-shadow: 4px 4px 1px black; font-size: white-space: nowrap; -} - -top_banner_sub { -position: relative; -top: -1px; -background: #1c1c1c; -border: 1px solid #000000; -color: white; -height: 102%; -width: 150px; -font-family: Verdana Black, Verdana, monospace; -font-size: small; -line-height: 30px; -text-align: center; -text-shadow: 4px 4px 4px black; -white-space: nowrap; -} - -week_div { -position: relative; -top: -1px; -border: 1px solid #000000; -height: 102%; -width: 122px; -font-family: Verdana Black, Verdana, monospace; -font-size: small; -line-height: 30px; -text-align: center; -color: white; -white-space: nowrap; -} - -home_div1 { -padding-left: 15px; -padding-right: 15px; -} - -home_div2 { -width: 91px; -} - -away_div1 { -width: 91px; -} - -away_div2 { -padding-left: 15px; -} - -score_div { -width: 100%; -} - -light_bg_short_div { -position: relative; -top: -1px; -border: 1px solid #000000; -height: 102%; -width: 150px; -font-family: Verdana Black, Verdana, monospace; -font-size: 18px; -text-shadow: 2px 2px 4px black; -line-height: 30px; -text-align: center; -color: lightgrey; -white-space: nowrap; -display: flex; -flex-direction: row; -} - -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-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; -} - -medium_banner_div { -height: 30px; -width: 580px; -margin: 5px; -margin-bottom: 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: 0px; -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/TeamWeeklyStats.html b/MLEBot/team/html/TeamWeeklyStats.html deleted file mode 100644 index 3832f5a..0000000 --- a/MLEBot/team/html/TeamWeeklyStats.html +++ /dev/null @@ -1,361 +0,0 @@ - - - - - - -
-
-
-
-

- {league:} -

-

- {mode:} -

-
-
- -
- - Week - Home - Score - Away - - - - Week 1 - - - - - - {home_team_wk_1:} - - - - - {score_wk_1:} - - - - - {away_team_wk_1:} - - - - - - - - Week 2 - - - - - - {home_team_wk_2:} - - - - - {score_wk_2:} - - - - - {away_team_wk_2:} - - - - - - - - Week 3 - - - - - - {home_team_wk_3:} - - - - - {score_wk_3:} - - - - - {away_team_wk_3:} - - - - - - - - Week 4 - - - - - - {home_team_wk_4:} - - - - - {score_wk_4:} - - - - - {away_team_wk_4:} - - - - - - - - Week 5 - - - - - - {home_team_wk_5:} - - - - - {score_wk_5:} - - - - - {away_team_wk_5:} - - - - - - - - Week 6 - - - - - - {home_team_wk_6:} - - - - - {score_wk_6:} - - - - - {away_team_wk_6:} - - - - - - - - Week 7 - - - - - - {home_team_wk_7:} - - - - - {score_wk_7:} - - - - - {away_team_wk_7:} - - - - - - - - Week 8 - - - - - - {home_team_wk_8:} - - - - - {score_wk_8:} - - - - - {away_team_wk_8:} - - - - - - - - Week 9 - - - - - - {home_team_wk_9:} - - - - - {score_wk_9:} - - - - - {away_team_wk_9:} - - - - - - - - Week 10 - - - - - - {home_team_wk_10:} - - - - - {score_wk_10:} - - - - - {away_team_wk_10:} - - - - - - - - Week 11 - - - - - - {home_team_wk_11:} - - - - - {score_wk_11:} - - - - - {away_team_wk_11:} - - - - - - - - Week 12 - - - - - - {home_team_wk_12:} - - - - - {score_wk_12:} - - - - - {away_team_wk_12:} - - - - - - - - Week 13 - - - - - - {home_team_wk_13:} - - - - - {score_wk_13:} - - - - - {away_team_wk_13:} - - - - - - -
- - - \ No newline at end of file diff --git a/MLEBot/team/imgs/ACADEMY.png b/MLEBot/team/imgs/ACADEMY.png deleted file mode 100644 index 29e55380fa976d8b445d9c82220eed69eaa2319e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22658 zcmXV1V{~L)vyM5L*qYe3ZJQn2wrx#p+qP}nwlUGj=0| z;XlWt_|W!GkN0)?`&A*MNwI|X`8iKT*MlVtlZ0sk6IbPBg4~8EHD^6$p0*#l4nFN! znnRC2NvFD8Uv5#h;b&^pE`B&I`V}bgyzfqn=d}Ipd_oq@?qphd4JQv0T>tsr6zi-u zGEeo~F#b{wA=xca08E!1xo&3{eI9OB+Xa{F>(EaHz;$5Hw+;)d3bfjz#(;RgW*uxv={bUhTf=0r=J3*|&b#p|uNT9Wy-IW zR|%Y39mZx67%jeu4uwclnEOqa)4TxJQfp$PE*nfjD2WZwv*3hK!Q$_pt+T15e4+PJ zTU!GsKBZgqYQni4tlL###^_e5?M-f$7w8u_GiU`AIj9KO)URX7bm?3{Y0t|n#7Fjv z{v^R#Nc@YW2PWw22B&;>(Y&ai_c*|S7a!}%>jJ7~fuzSKufQXVPNmp-NI#h6p%K?r zY7&0Ot#D8ZjE_mjYs9*29re`pgO6kFW~mWnDp0F(kqDj3z$)3zJPmS%rY1W1u4N`3 zF|rPkZHhDiX+4}ZttBm8D%o1vNa&LMNZ2kM{s4(dXN%+p(??S`P;Lh8ip8a$(2TK_ zPPZq|)_F`Vr{$T^(ppnsl>y7867B#fo?;}@Am-El{%1|+O+&U^Ge|WkJ)Otc9yr`w z*78^v6fLU~l&hW|LP9~oV7F9aI;J@_rNjxe>fk&tmUwuI8{#DJOTax{l~9%61x3K! zWE!t4X`(7MOfGq6Q|6&0-+DNpzFXb#~>?hg{5*JYO4W z2chlF${O3D_fFxP!!ftbC>`$pM;Nt4i|C1{;rJ(?p}^R}(a_Sr^Q=0{KEgHL2}iPX zoeI#_r-?E@={KKJCYv95IC4}Wv3W&n&3^jax(ooPgvjy}i7Q&3t#@c^tJrzZ<7q4w zCp(EBtxehd^7}&QA^0u+6GG@`$M$v@$OW6;TIYCB4&A^}UM*3=UE{^4cOicSGro_*H1f#!~h5GZb3y(%7XW7KOicihe}}&9hn$nBK`bU>_>~50^N>2?aJX z2r!&vItm)Js!K6_VPYXn1kPXZb#*FWD%eQBviuTBu z79FAcigF3#7?l*kYprrQ>H!kHMLyKoy6xJ)S+LtSpq*=cMO!~~uh^-?R-nDLQJjLq zSgzRBMf@ZCqNg>%w|)P@c>~aQ4Q5_R(|2`vFmn9FCSs9=x!3|>L0)tnYQaXy|7nA*wwy}2V81!CR)5isf8{3 zT2yRPWwfe}br=ymRbK)^ailhqmx(EZ6^9Bpcvv{UpqBtv@dbxWk@s8N%1<6lPjewq);Cgg$Ot;`u zfG}xZR?6#g!^^F!g$$}6u2e(XR1BgcVkQKV_2|VUgWY^n2O!4wN!F?Mj$d`Ciw;Ar`l#c{c1E>D%Ad*Gct*YZS`(mGh z3U|s;>LZ~qHPZN+t%p#JJ48@Ni9dE!Oki12MV;V+lioM6zH=Z1@>E+uQb7rsITR}} zPlF*CQ%A90XVyb#65kMsj8G*I7eo<={L5e3YJvC(255^k!!3kGN1AJ`bs^Av1dfLV zpd>ALUvDyHa=c{EwE1k_dGi}(de)d;XP$K9pLpl|eu(E1pC3WEbFlu+L;D$)b)bk6 zcept=&dD4OC9DI^a5S4-(7-hE^O!~MsB|6`C|tlK=49o~!s5c{$Fz^4qznIUOi5c= zX(0b0+$iiEL8viPD!!Q1C95T zv9yy%O41oh<$bkxe|S-IhehvD0}Us#y|iIE-hCz55nWh_CLW_o4pg)f7tg!%+f**u zH>8l15g99_b7M{q>3nc|3(uHVeUDE@Yl%|^vsjuv_5^&gU_QMVgut@YTxj>oU1HYR zJksxwvn8VW|{z^F~tDAu4qzz@0XP!@{sf*ZOx&AcZ_I>r7=h{w9(Z%%{X@uOq%mN3b} zSFDbU$bb()0&DxQNB`X#nJw>3CQb2X3g4<0LCwS)eHd?3Egn_xEeXaOocl~3@mK<> z{YdM6Lk)?dbiez;U;ubRI28_G_B?w8ZO)Wv{iad8vsS{Yro`Fy7Q*xlw|UZ`_0Rm> z7}@ukAUyfBR`N@PDslr=?0v!CITzsX39NQ$12mGNqGAIprxf7+1P`?BN)(7Mg{QK` zGI|3Ts>;;|vM~e0_a3c_QXu*zMekRRT@v7B*i^?);R-|Ez{aO08;AN$s_mse%kaMZPev7^vPHf`Lkn=7>E&bMjzb-JAx zX!esi>}d`-ZQ`Z!4FXeVDJ^@0w%nYM*IBz45x41oW{Z!nkxonB*6l~-wsewKz_!&z z!##D0RZaqIl2`!==M9PAsxE&;52Y&&lfz*dLPO!NS7bCn2!0Xtd$A+QbJRJ5I7zli6B;s&(6*UXzdpFT8g?C zcisd*sV`h#+bnPUln#Uc6vvJf2(JzDme_8ju7Fb&g;*Unp*6V^v8-w1O=3n0>#RN$ z0in!0AHxjQg0Bs(2yVwmGct#MN1fs_GFQkCp-dt83t0)jqxCUNi&LSpr2g@j>Vv^w zTMY!NLXUnNem z%RK?k0|_23`%xLO(5IquIPQ>x|FBud{i=S2dJI8SWz}Daytl~>%9ncCZLs%zl zT(;itM_p;|wkB*_SvQbE9lzJs(zafwUQD}hUYy&I#uq)r!ej1)UM|NuB4>ine%C)4 z)4S8;M)VFfE&ghc)(ky!uO9~8t&5ME%-;}}x6m-guawj-Rqf4JtSG-I0H{}mJw2Vi zgH=|SA%MMmAI5}WwB`5;LTKs0+u0r^$}Ej5SYjW6gz{WNE3NvWso3k}j18Y0umed$ zd3&F6cFg=Btr^oSzW|7w?}EHsS9X0>Sfbt)wQ#dEPSk`J^FwHkZ{(&+8hCvP?Vm4E z!Ho>GPknC@(~;DsNTglqDA#Y@9gk%?Bis?859lFFPBVnvhj7)!>x5H*jjgF$Z+p>s@NX4nclo1{Lu;&uL z;?Jr+LNTaJ%5dfT)69N}?k+EA z5MFSYhs2|wf*d%fap-+K$#QqBs8LvkMy*0dFzTacqv7RrBzNT!w_j(i`v|4IKjAmG zd?->63C2Lf%I|fSA6Nv+`+jiqbuVG=hVxiI%~HH}j9?&lXWVNxk&3={oWVB0R_~rj zY&g$M+4!S8p|>8rCdRDNYycP3;0D3FCUMtes(tF;dU z#o7{ygrKpRdB)S+DpP!PT2)$m8a*O1^*z(sO^$M;);yNz@YB?KXjNte!rpV_fOp-V zp{D9ccqA^O6g!Z_o@`%TyggbXSZM99Hqw4b7poq_m??%l@eQWz10gi+?U0FXl>Ib| zRfb-sDTjqtJ+G6OxmLnTa%*XTB-Lxe6gKhqoQsqTt-%%9%v;^V2Y27_ zh-jKQx!-{8ps&1ve4{fh1;#B5k9?*WP@K^!WoT-d z?748^4x3O@g<`8D;E*K9p6ul?{B_fH;jD{W!dz`fg;i)@zKBym3*Lppk{l!yHMGvI zg0Kp-x^1H}2ee_~Jrlw$1$0(wpum>P1SF|&MD!@wQw*pnpD)|A&`*9A3ix3xoTfO} znNNbvm@J}rMjou*cdg<;Hmz^dl4(B+AzVOUw6+|9wZ&S+vq2w|U8C0E;M5Y@rEiI4 zFFhIZrx{m!B9C+L1mP1v*Ww)XW6up1BcW6CxoQ)LjSXWJp$2a7ddkjuy}Kn%6$4_lDZPHSU6z2-kO=znlMCnLtMvY)q1{?eMlRB;n;&5O%|eTz#`?h< zg@dBo)1}o%DNYHCwp!&AjTiShIws;}#*19^W&5T_^|u_S)YNBNl(eRf+P)-i8GES< z%i9k5nD7F3`U!=P_N&BXW;SYIpliekJbsKr9a&oI_u6d}4Ncs_il}}B?jU}p0I$@% z4YFg@vRH2@dyUYvzGODqS%VWvZPW|P3}+;C7=W{ZP`M~LEWu{ zQfP+ByCjBMn^89Vl-D(yz^&t}U)AZ25rOF4e>A8+RP!nzwLKj$qomuW%r!T&wI6v3 zN=j61qj`1V>_^gCf4F31NNM|W;!pj+=k6lJ!#PEb-C9!9eR-lo^$iCxO~i~!J!CoM z6CqeE0@VD)&Zv~uh8T$NOdgMvBaK%wo@d0kyUhW*%(Aw^5yKog*&np6JK!2Izbuxy z^s)a{6{esmKp5W|ASbejC%H<=0J2JxU*)hGnJ$ZS#J$=3Q`bB(^In_NmUG>5@!!*M! zM_Fy4#r$;O78fXZX3zavC21F`o=%A-`!MTctnK4=?J)Od)EV~;swI+i#`u*B#^aKQ z8?92;5;|3%0T^*muYu?RV>+!Ko;ACR>)`!*6~&5bA0 z+mm&*n{PyxAYOn7A~=I%PJhn;=r=l{wpyqSSv2~L;Do$@fq?p{P_|E88|$rUnn*T8 zY;+}q_r{DH#2pf~CMAlhT7GrW|PkUcN&$SQYs z+zyyMA0Ey3eDYahjDRC8V*0Os00Fh+-ST$U`Qx5iDB$h>j7Pb$6*T*OxCZ;%YIiaz z-&%L=0m}0&4IlQU=Qf3}A&aXy#j34++A_jl)HWW6IMT}!amd|)8GG?<$!y*|;tOKQUfrIz-X zasJGY6%;N@NZqo4+8;n{EI>fih!K~|N3497NRkM;YA1|>eA;7pR#m!Djm1zWV%Hi< z3GK`98n;RCC0G_{7lcZ(H4lG6fPixQX>>~TfWvZHY9{Muvi`thd+h#v*2~E5@K0tn zHz6UEYDIrN1qR9u2wG7tCewpRcg64U_-*}lR74+I&0%9P4_le>OZ&aWh?? z)2(JFa?{!nv$Y~Wz?)Xa;y{z`SnS6RU7+p7bsPPyGmw6+S7B2h3g*U}7X13WnMy4O zT(1v&rOUA$-MVtYQ~rr|@-xxdHht7`cSB(*P#~b7mY|xRXJF5#{ir60=JJR-X-8wr zp?;ZRhc>OYtg*L`>9wyqR|iM`nWAwRG_Y}d8FM;kgqw}K4OP`=7+7)@-B~<1fT82>o0GE)7Ogw zeE19P&>PPH-$LUP$`bF7O{bbr;;*fV86FsO=16+uLfT>_v50cetDQ`fcdmtD5#2#C zZL1plxh0fMaiY-AaW+opFkEFDWB*mMea>3l)((^oqd!u5+yNJz9k>a2v~RJ!QP052_z^(QWk5InZFNS|e|^;36SZxeOTf9?Vi zwO-Brhf--9=uSPK8s65+WboDv?an!aKZz2W)*5NNhI;7H2(7fqMx^Ayb%No~#S58FOz`O;d^I*~6` zuG_{rfBuY&`w)=SWvdal$K;BGaXd{OKe9jB`Il6!0~% zF35)G{EhoJ5!Kt-3p(icPz&wO$Na#jMy_L^2r~9V~Brn_u zC7jTkeqFm6t#{3|grA!Au_)7-L0v1NY_kWHLQHGlZN(-J#39T(7;2W8DR&TlfxxCsciek5f>)OGS3|OB%gB)#2x!swe{p+YM_UF>SKWwjCR?-Q6 z;+iJHSQXU>cwvLSfI%I@2#8!hM3RMhn91yYNXT07LP;zL+Llse1f-asKB8Z>y9==V zoSY(7be$d~d4?KY2#>?9epOd) z8lj)w3TDp5!nNa21W)5@eWQJVxEX2|pDXRS)4EMnH%KRXG$Cu%v57)}*vVBHTBP@0 ztUa@|I;L(3Zmh_t&_a0R3y*uJjvPp;87yL+=X$Dp=C9jp`+1^RLM?{KL@3V0vON`7 zC163GkkuYiu1zOdOQY-}`ZSD?NH(>I%w9b(qEu(DsX(%o0L5vVp6|QW<%2ZRt+h9Ev@)dAImR8!nrA8^7cT&xuz}K0tIa$61!(Ctu=Wkh$U9P&^D^fOu!IhX~9WFH3Y#pr8h~f*7M>MR)uu1{X(O+anEH3$TNiHMZ?UtuFnore-$?7ljGo6`m5$%Too~_E^;* zo|7MMY+9)(`vMi z#Qddp+W4-ysO|ThS8}7P`Z_gY|L%D|cYNur@E-3rqdmXE4bf^utQPMwl-vU6QKH!j zp@xW(YV&(-=sJ2E*S@`PTlR3STOGlGb|(dkr?;fHg}TP? zx{;bD@xtu@MH`OIb`9r`Qp}SxE4npNU6BApIGwj5*yD#+UMe@ZDWIcnS50_(|4FxJ z*u%xQ&TL~ah}}H4ythk6@gn4#4zclFx~ST0zNpbtLPcA5w;SKwM^iPb^!2_Y`t~#J zQ{D3NI>j&ZDk_ayzWt4mTMUrznq>upxS6enPfwy&&cS>Q_Uy@lbtwC z;(J!zYfapFBQsOCRTHZ+2}!O%fpL}Txb{AyHP#YL2Pk$I`f zr8=Gu`1XY4irc@|4l&+mi|Rw$PAuTaCE{jERs#CD)#?hhUJ|7h<@X;YwVyi+50^#0 zvJEF+oMWUXARSW#|3>F~(J`qfc4dcv<*vMc6%R5Bz;(g?(O~m4^XTew_~3iW{)=%h znm{sb`FPet)mYyeX&*xJEt8uh@LLl=yI&x+uE%xu+Ap}i+`FfR^dDfT0F;tX1gsWyEDE0}@@DlQQzi2@g_!U|F3>m7U3ti+H{4z1@ z5fsc0g;)ufleH%Ni6rZZHaJ`Ji6ip0ZBMu|2~16QJRL7_Iwo!))u}l;xR|~_J1_5D zI|`_|VM4^0r&h?8=*(|EUne?lD?hXq_pXPqogE=}lV&~^xrp}T2TTJi6WrT_5CrFF zaO<4+=Q?|-o#s03u^w*PJqXe>4xo|nFvr60CeBwF*n5BZ_e(($5Tnj@eZ867-PTt` z=)|Vn3eFN-_R@Y~u$1d`+vsrdkh0>s?aXLl>WM%Rpa5(mm!!b9Y<9?RVU`_WY!huv z)LUvD0t3cXOYMpR+dBTSUV=$is)}pL#U%PBxRM(ybSrv>WAblwUFzi*%gu?l*kH7HLCS1Oj*4{R-ZIu1?z$)i-rvY zx!p}ZMVFRe7f8x6+fszubr$~HE_dfm6lNMu;*=DV!9#*H!XCgD%9f8#Ob{w9BtH@< zbY_QRre&zufoz8PvAvE$ljvuEWcz3e?GfF|RL=G?uhPfi^)vs5`*92UVDe{4Va?Xl z>r5oJ?MLJ5OV8)U%jVQ6nR{~u(ail)YdR#{+|}WcI26Hxw3Blg590(mCTjG8>g)8ZWJoCE zt68tjkrt>|LL#$mGb!c4&O=M z-Er~mJ`t0N&87vso=is&6lYG^jsUE|OOx+|jl;8>Wqo1QC!&Wk*i53-CW@tfR1ZAH z7VAE@lcVy-G=wyMX&*hvjdP{^pj+T7x(eMKql0+dA4ilC|9x&X}O zs-DEUI6qJL_fw1yt%CPK%zdKgqv(Bb=J|R`D?}hhYzII#xknNdCy5R)#S|$y6|~de znl{hyS%^&;A)Qb5Q$r5HShWeeVb!S)LQvGthrL)jqZN&h=-F_U^y;vTF{`e~eukXX z$QzeS8%aHF2}*U&_&!ah2wTjuTM~pY8yB}q6%4=EOBbJoW-pN%FYAdNvOzDx4huKc zH$jtpuB>D80+4J!$F(y1$a0`z8EdAga~-Ml3*m<-iv}QQyi;+oZARg@CMLrn^=Y1v zKhO732!i5iVYF47cDei`YYiMsG*D>QWLg)4JDYKLR&Tq*74bpv^&z*-|E)AGiV z2_Yr|9x&~b)J6XMIppk)W`|(;dVR7z;V7|@Tn9(JuO=7kUPWn$%4N-)yUsA#^PvtloYuLZ2$K?5dSSaH{{l)WFrkhP*U9KOqEtf57_q+G279k zgoKUP!{gLWO;Vye4~Z7MDGMda}zKHgrAfHlY!pGHRzN+Ps8 z9x~<74P*ZV-BuR~QZ71NEqWw#ZE1h`8KUm*dhu#=D7jfQ5Z(8NFw+i@lW`92VivBJt7R}Uub2Iysp6KQ^& z!VKn9RpjKt6lhkw5pE&13)}Ka)q$MfKdSD%ikIH6nV4f*vbkoWH&ZKNEjIvS4>jGZ z3nn{gA+H{X-OtgWmtxkU6g~KV6s4J;*nPB=#)JL~LySM|-ycu}g~Gz<$wx&}g4&ofuawpp&UtVzUbN4UvE@kffR5*S z9(E}C+J3fL4@9e`d>MTC5Qq4TpPjbF$ksXuY1%$tPgC*gQ{SiUCvv9K?WEts8v^Ah zDnS2)zMjMYiOO;Jq`VqJgH!J)>K_%iQM5z+fhun|#TdP8IC?RKpj5+8rIqN~mBbRf zey!>BuI>B~Jp9I~`JKEI3X3jm6wnhj8Y%`WHSVr2<>a*KX_^(&R+_Na3(y#~$AI~H z4y~(=H>iQREx2hE@d6LfCj~KPT%7-`&n5@rN087sckjr3Un`TaGwy1f*-oV+F z^G6Uws&$${r_iyZn6Zg64NyV9i}WiRE_V8f1%}Q)>IETby#ie&3>F$yLOF0QA{on^ zvYgG8e6&X>GP(s(yXsfnI(p<#e|q+*qN1{4u&SDp3WcLp`atSdyW@dJ|7`zj|8v8D zyULl4t+|2p_~0r<;J{v1S~_y~9bUASZz8%x?4WbFGbZOSmD|Ltn9twRdv;Y;F6?1Z zX|ismifi05nmneiA>3PUs2Y`U0ZUg{>A&>LQQ)?48iOKl7fLYvo4QxMjJqN`7ZO5 zFE_H(SW4JSYevT!8+JoLDifN*iT*TKUBeW0bM6Ty_jMGOasC6#q0{#{ZQEJ%GGRD| zSLqk9p;5W38L&#mETC$Ki>@(LPB(O6TQM?45RtKUjRC&mG>Wk?DqNe>j%`(@_ERe* zW7iCP3!8{4&@^j^U$%3nCxewYoBAKGx81Z0ce?%irs8+oZ~U zqYsL$#`*PE>o~{G=lU6cL3}ndEjK>fX_PTI>clWu_jG{>9_%Jm%6qN6WR^(s9}{74 zgkZH^^&!z3N3EdSpYJ@&$-A^AyTx*)`xy4H)c}M6SUz)U1wFO?0Fg5kHt5fg)#_M} z_tXRH%9@34$UxwYr@t0Jb1chQGH>}zHbAR$BD~u$ErvMg8*#Vs`9-->1eaS3S}GVp zm63+kb9(&JQz{qgRU8oCg}^dTN0)HWOWYj~XJqTND|&-BR81Y8)tCILp-dl*OzG>7x~Ef>438DO!y|}F?WEoyuPN`gmi3vNgG*@cZ~rh%KJM7wS-h@VkB$> zHY$3G@pB{b0jJhm(Hjm`svufby~T?VraILoLw-pCTG#m*!c&G>Ns;{+5(YB|9*2XZ z`g_OW*H^Ays+$cqUCM4(Fv`56#ZFV!N#RjXn-#5jKh*YP)A?M6q90E&y7y+!K3`-B$bi;FduSqZ31s%9M5$N0 ztS>9KUEl}I{zx%#G&1ixihz<$N7wocNo4^Q1qQvVmvqWVgb_ikZ%&5g5Cnw%qw7#( z5#xDel~3hi$@q=cX~ID(>!Hyon~dmN$K(W%0CXpBAqhzNvVY8smt{nnZAtc2o0!oII;uwZF&Os}Mao<$V@`${ z5zCJVYS4EEZvhZ2JErOlGZ#h%=&T4-mA8s^RQyhe+^B3BI)C`zIV1RqgR{OW#(bxS z5sPq=b9!zk_-|qfec8bnsa(Ags5V>6uF8V&vn1(_39B)K64NhSdT0FVv}-$m1j5uz z@CKh4$SDe%0?@aBx8?e1sOF+M+$@Yf4DUV|KD7^#BSbIl3*pv^7*yuQL(G~;odd8g zF7CBeEU#M&)c3BW-w2h2PhnKvB9s39>e#EQh}+q+=>dg{6%eNYSAnC@(bTCXlQ7J4 zk${jLkoa1T`n-4`>ipIj@)rC0Q=7)yA;ZiI^HkgoB^HYpiheJXVoDF;wIw6Q9Y=JPKje*1@+ z#L<^?#ZKjA$FBBY4DYK{)nnb{7E0(;|9GNo4Hvu3tWACG(UGaCfx|r1C_vX_5f+lu zGb9F>9YvR3y>6_phreD)lMBK;+AqV{vp3VF7I?;bTRQg-hcSJh-A)X zhE#*RP2ZFnk8U+X=KJA`JfZB^MllsEtMMKKRL-kS?RlF))bjJof&1g!b`KW^0%VY?+0486iJ`5{cq=# z4gGJP2E5y$ikvg-kmU}cpZNi`+QaP}=hx&LzzOE$?Gp4ob*dkKYtSNJW%?ZK7)D?# z)L2)rO=8di(Rvb8+7nym_++NE)sy8sJ|C^@uGiWW(e1x$yT|htRRmRE?+YObR1rh; zqs6PxQA38Exl7ZuNQ{t*=1Jl+E)J7CPcoK^bCP{z_3rY+JGf%e`y=a<1p29!9f!@q zez7dC9Bq~Q&b|m;y>q2_&v%*qivB6c8BalaDst2>axNN{v1K6wciKwbM>{5E*+1=S zfdv<2&A9vGR*ANa8$yU#D(dc&ZP z5K{ndmbJlEz;NeLY1XET^ITwPGvr)MAbzh1%!G?;|~x?0gFLfg5bzdWF6 z34MRnMdVMD^S-;X{Q(|S2ZPr#`!t5Uv5^6)piw?AUKgUew-(W64m*VUZ-8%VvQU6L zz=G?sjKjIOgM++Wr=>)$%uw5@hGWoDYks7+*5RSK?X@qI^sBmT44(|w=fL@{q07@4 zBnfRmAu{U8U8TA_<=utc|K=lgm~e_50%1r~s|q?yg`9!$xvFH|HgmL3hTb^*+r`PE zZu|F#IxDeJW}(LPBz5_2hF_N;@tJeB$^=pe0A1(JAXG#mE%&0)NmxVCvc@zQ0p$j+ z@8(z&6A++*h!7j45CmdM?4Mi75O6Sp;`DP>WfZ!5NmCQ~%7v7*k}7B8g!brF`bWpm zD-lUlWM3{N(B1|<0tKLpL9z?uaY<}AVeI*ip8T*}mKsFfh`uY8G|Vu28wIO5qY%L9 z0;Bn0^KSIB>IbC?b7W8i+dUgQ-h1#cll+N3Q5{Gs2%LEEymr3c)_Xi7JUy^4jEX_b zL-a^|P$8$sS+QS#bH1|JA#hj1&(F(%`!kP+6IOKvp$LXF+neTgI`pk$Vq{xAw&1B( zSZJXP?|2>lSoJY>P;?=?-X=)_387lD@wzV3Wb^X1$e0{0udFE9efM#SG=RrxXLm+W ze}{;I0uBaNG>4LM`TJVR8g@ud#8>mE-NRl0EX?M}^o&wqS#RR+mQOI*Ce@AGsEU9MP0;1p%B`1q9P|4eyfFp$R zmKg>St2fBCF4uamtRipFZOl+wnjkM&{-hUb&3?Z_+%iWA1Y)^Llsm3A^fOp$?I!3^ z^n{N*#D6{pDqAp-5&U28uIv;TmbZ1wV{>`M(^kwgd%`O5wjZODxq4zDmdL6-hZn$E z>0Twp7J6ZP)xKi=GAYs~cDN^*pGeUfgun<8Mpv~)Y^xaxzj2fOc&b0<&*88?a_Fmv zUDoTJocZ2#-df@<%!^mecqY4vSL=g&JK&xS92^4YIj4rKEHGT9sJrZD3$%0j)n`u zZ4+~m$P?E+-5MW{gW%@IRH3&P@VzlvTtnj-wrOFk+2?~L4NebWudZa<$TT!Gl&%gv z1kD%jYe<6P?)zLGn92GKeG2-1z2)W7;76w+!TTSJ`hwrl45*|o!UJ9D(0uZwQi}hID#S@!cHcf%cZzavEbX<~6h3_{uytP} z90c^^2qo{@pxpPC<3-RA1Szx5b8Hc>I@}5|5pv8~`*0Gm($G@V9@zx>wE;A;OnM$V zceb9q?fYx}vVav3lq>3!vRr~_X<{)aAl)$kVQg%y7pkh+4AAnD_q#l>gMU!FPS;xl zD}c23^r;o9V3I{7tw1u!7M7o}4D=&h9SpiuYK>FO8xZI1$YW3&bzunq zXwi?hM03fH!?UmiPt2l9hzJF4B5lvDp(%U8=hcSXts>X@gAfEEL{$6oK0F+>Q_7VE zR*SpH>ALFbe#Gu$mXjhh|H)5Jl3NZBpBCYuDvdz+#={7nIksw-vi1d4(=9*{A`n_b& zM_=md=d$}$aH${)n2=!jIjfzZJFvA5)5d$%w$NV77{{FTktp*m@q zhz|l)@xTG%5$%1sNAeE{-4g%a;$V-QKZ8(rO^+WXFa9|{Ui|RQpAfE4kk$sos>K4u zHKp(Fs^K~5pEnV6O0oI7YlmGmGjdc5S!wfsKSG-mIkbr?6F4H)^ENi{NB+iS{J+q1 zyO`i(3fdq3U~4R&Vn`GWuni-X>7m=JQ}qQ^4u}#GfD?nekkr;9)chDTfGm%rRIySQ zw4$RZ>Hq7modgg^tj$a%k*crDqy%Z29v;yD6+%f7E|F=}R}aT~oA=&$x%T(jX`?`4 zM!JXBq`jmkr<``}KC@;g>ixlI1lCxJ`<@DRB67$z<9tbH%qCEsl~thDaj&kiU6{MU zqFdkF*4kQpD*G9B6b2u!zMF-ZHHrUqNvlLVyh~TSo#sfzpeF__`;e51*h^q z2nL^lTr@OvDe<&*Dnv!Pz_zis3$ny(;vWrq3eL^Mt@+PX_V0prV>;9k;Lg0QzA(uY z|H#iSjrP<2JHt1$)KM%@h^nyML0gfYobEZZeb79wL@*K8OTF=$3K5+wg`!3a9Xd#T zL=aCK^|;}vv^w{T*RTM9`O8q;wz;9KR>8y3ybK{;@&vUvYidl6vckvBXQdOwK}R+%e15;xb4&FN<9rj3 z1Dc?n|Ba~6Ooh*wpMllIJaD-tn5Y)Cn4<%%Ff%4WFfD5$7d*Vmk5*+vVn2}JQ1`mv zY+z5!roWx?GdiNfHD`39b)1>da^oe>%9_2U_B+>n(hUD(OIgL=^w?#Kkx2(ZO}y zDfO`%PF95fRd-{JxTE%y$!NU6NuVHTs zkA})WgiEh~;a*I-aObb*`m|2Q0bM3|*)rw-FEYv+z*oc@r);qJ1_3FbGyT*dlbUm^ z@Vdy7xD8=L9K4_UajCwOJKt|i;r$PBm0GWh{n<*intSeG-ROR9KJ5$Weg~LbH$MjSp2Z1O0aHT zOAh>n>w%l0hxkABDt}xBF#ZcG#55J+x3~R} z4FCu9AXdOt9yw!S%kPH3|AG_&yFM~z?C1yw1BrW^gNLH~qo09+JuQsTaK_w9%Sq*% zH!`9T=4{YKZ*QU7`(vwS$|b)Nt*|@d-Bu9}kF+c4e^Lwc|j|?vj zTc|B2mu`^BNJFU+jmGVm?qDZwCs4pL#FxHC-$i{_7T*aXS4nz~M7Z>9o)ht?0imq% z#jTV_HFhk`c@(H=IB?oE+h!OMx&qNm*gyX7=1{}`G8PC`2MPcQ455tW{3B|5_GI48 z^p2W9a9vh2FYey;_h-%QE?g*ikNUfhk(=aZ*LzSpZ5C+$c%2fWuDER^VHZy(@c;6N zcu-7@7M5u=Ern6v)lFn%e*R$o&*#W&40Hpt(JmZgD2D8kR`$a2`*$Lx{jnXmu#4lA z3g@0u@MK`PhPdmft}tunDGPlMrvD5Hop||kyM=jL|CbM>!UFykJ^~#s7OGgtBwIkc zjX>MF9}HjCcJruAE$TezJ8z=3$Izn5|NLx?a$FErNH2V@F ztasa4BZ;!FK7Lg@p!8QDJ0;KXt?NHv?%#LsHD`o8J0KfG+dF%m2=;@I5i9gSF1^e~Qezl=M5*;`UL{p0~bBXs?3(iv#m&h>-Jj zQETS;df2#ARY`{`SCj6$cY~ty5EV@kz=hO9Z|gK0br#O|vT-J!iEMX`uQIq&7NX|C zb}TY}kQRTYWhhp)csq=;X1#a$zpBDN86bYK*1*1hKI3?@>FFQ0Z5G{9#l})z@eoDW zG4_;&qT}8dym{^BDv!#`SO0y%TB&0-Jz>OsV7v729lW@D7fO0xE;3tS&uw9exV>{= z|Ce{f`zB}$T?`b%?&?}A1TBTj_QSq!{7{$Qmd;87GGv5aW6HvU`IS+~@W0OaxS6}2 z(G)9St0SE#N1mmf%j50FERIG%$LB5wkqHSShu|xox?->Re%nhYT!b=&RQNcrjxW@@ ze7A<*Dn}{4l#L=gCvv1|9#f_az@~i~+06sJt;fe7cgxt>nWsN+RZ$^OK>B%IIKYjK zmEJuJ5+69*#$V%L6}-D1u1R^{A@N&ROJ8%8yil#VnGN8?wsl}gO|!_rR?qZ5TTo!E z@i>hf-Fb~y#pn{Yjr|KJx3gKZ;f=y##*4+JP%ow5mndQTq-^Q+bz)YezZ$P>t_dnc zyj#`~b*bh(V;NOxS@+LXhAZQB!~cuALV5D>mQz88i<2NPvUW4cBpIhZt&->?BO`Wp znOcIj5?LJ-z$neUl|4IgJAe=|=?8^abVhzfcTV^i?Ug&d`9>8tgfM<|p(IensRK^b#Q)W7)?rPz-ygR@3F)aAfJieQN$GHdAtgBk22uklrD2o;qsHiv zmeDOGvH_3M;lL4&Mrufx1L@y<`2PO?T%YT@-}kxibI$91pX%26O}j+oe$vguBIYQ;*_(ZQbJrltND5Ws4%=x$ALFwJjCk*Ku2 zKuHrHor4Q*At?S6E*$>p{B_qSd<&vdZze*_lf7euX@WBMv)<#>0b{qiwC>*5354S+ zAXfB%BYKD39#)my=do_Xd_hi3kwT(^^Qp|sBb|Bd1N;ZDPr#%PiTc^ky`0~skEENN z6>bMEBCTWO`bUi|RlNzh>o ziKAvPJ|Nn33Ph+n?|wH;Xnw!V73;^D@Ik#H3w0pRA~7M(*VV~=FqJaUEs~~OOXLa~ z->**Y=<|Q}x`BOHM&0f4eNjEFo<~VA-uLpd@il8pBuxfbo^x3FKMLl?iPtBtk+ULE z?@m!nA{nKQN;glR60Ue>r8aabClNQSU-oeb(cVI*w$0I>sSw#J8nf~y*}{<;S+j!=O{Yij_QF5V2pCtRZ)J9p?T#5K!wtTq(4 zEO|3%D5Fa$o;~DUz}AZ9na}Qu#9V*9 zW5?#{AKvX>DZ+*K8ID%1G=toohL?#^+c8&_;J-;)@X$Qn*f>7Lqk2?|d7@5-iPjk& z4PRGzY$|>gP$ALl|7(P#TGehv{iJv#lG$2$)J7S>&exP}lVN(B4gs}{w|5|srJdCf zWMqi*?M^H9N2v@qyMcNseSIg2N=9I4az;mdaMlxu>HWJ+w^9Nrt(@n;`r&;4vNL>?6g zWkgPT+t19)E8J3qAxzEu?>wVa$34K7Q-{@KoOflvM!2dW0l8(eNGL=M)ng%4dx^hR zrc%fzdOgC(ANEtpG}ZirmzPg-Yijh+KjGHPHMTBbU-H#3;|n&|bPdJG@rAZL%PLm^ z`S2kjGOz$2>|<&E3tjvzGge#cr7bD!n^na0u+!Cn=vm|5K%H%|lq2`h1E79GrIZC? zvfYlAMh~u+Lf%RAQtWqiCVc5p&7^GjWXe?foio(N6i%l&{GOx`>REw^brnq9cju4| z)Pq5(8bDy5I80&4Kb%pa8YbYdYml1?HfE{p^x+*&A^t%i-+SeY&ip^GUVE7K>K}e= ziqVgHn(JvtHLixIOJRsy;cx3|lew^N(=>Kb6z2z7z?9?3pBE&)KPscYv7TwtGlk$9 z=&j6sBVj}0w>dCwto&|ltYy*VzZ=aBpw#$%f!eUZrQi8?@L&TXkUgdMOc7nRf7r8lmVLY!h*MqsJsOINK0m|5W!@Ke$?o0^F=@8l`Icw|SMvr~xoS^ zgJjz7_lhD(Ue9k*`f63cOhFx@?gwkW-4-i~f^OWn*Z%V*R*|#cT0>t$14*5FxQkXiBU%#ncGRKw*%K&)Adq}v^fk0F29T<8_({Q7 z^NnA0&bVT+GAY6(aZ6#$OMf}f8{BYJc6X!Rn1yR z)Q^t&S4S-EYpPR;+19aJP~oEKxT5MWA|$-f^KPLCkE^~-a(qn=3x=kl9s<_6ePyte z>zt=w+-v~X0rAhy*p{dF2oX;X>s6}{(q(iF^%hp#n+rxhbFKV^UwTuhaF$s)&mAj| z5jx2h-^11%Me@+Yx$dnbZ2fm1y$?_>%tRTOE#%F`giisY>3Z^5+rH=-B9Un4|K&w( zR7WckWw0Eu60Q@)UUeqdHSq`YdBP5pLCe)tcJ*gWSRD$1eZ}y1D4wazVUU1BL zHCDVK6X+zz-wf#j%U5L;Tfx<4o@EbAPH!#EwV;xPM1jh}gA=%a<{)4p>(PoB)VkAF34t;! zj#J#~c)+cbtby8NN{jE3T)cj{g?TB|i0$2++4s0qO>bT!nf?4_6VuVb=je>;t3qJmCvzkRB8Fcqy?GD;K{#HVLc`+NORTg1H<}MGd$j8!f#@ck(hIH z5M^NHM41Lj2%jtc+*>XcOUt|0e`1rh?*BW5LtL$w2mABDLURBE@?Lbp&@>_!6pkx` z`uCVVzIORJ2&=Ee!dC%Oeg$cf$P**w2DYy$zS;NHeRl+!Yy2i1C<#vT(8SLBCX8yS zvoHyD;#52%f6>uwB!m0&q(}X3~H{C#N;V z8`N&kKYzZ5*J8Te%$PhzbL?r3I^?V3cN z1)xiM`=src?fn+>eNIK%Dix??q|VBHbH~34u-ls>d5ly!F3HIxJR#f1A=i=I%LX8! zqAV5G<#zB!wwb)Q-iq6B`L7>8ehB2m-)Ngq*k7xiqp(_po6|Yzyk57)Rk~V0GFTe8 zE+Hc=k!)X=uO@JhJu{A>R^xk;Mto-i$i33(gX3U`Z_>*aGPWoBR63s>^b zDD!Q6#WgCCY=n5bO4dhi<3;Bl@NZp4onhy~LT@bFK^+%%mxPBZJg8lVxWrOJ=*0g6 zNSyCdVbtrPb74Y5)i0+P~!=_{AkM0V;^N`n8m(vC2W z>^@mR1lzFb$tr9Nd~H{`px2xTxRO1t$VW*$8whf+4BG49^mq>t*_%c!`O3We&JY}^Iu>SDYOw`SYs%Dg9m zduOxz|Gz{zC0$?+Eq`s>Q&-m&n>i)CeQp#`X@RAr6`F(`sfcnhp40Xg5r2<8NzTL6 zi63D*(!^XPB+RHvmP=q6@%Pa@#*(9v!`516cnG}KZB3WH>0OHl26sFUjj}jh0lO|2 zXhfg-*1Mf`RkXYmj>;bx7x-*>icK zfalx1F^9kduXsZk%|Nap-4jKf9ofuzb5rr^+lP<6cWeK3djXR3(#pDZn@+2&LjqHI zidj$zJfZ;S{p!!cx&jQcATd8MrlKYAJ&7P}+AeSLFEq4m)~Ws@ItsG)EXt)Y{pEIj z5Ed$ToP}O-pMN_%-4NJ3#s@U-OaI`)3HmPxAT4}etMAY&$~0+;H#MVRYGEUlolors zmKG(BkDD#GOMET27ds(Fv-0jTl{Z9`OvF}vw{gQq9%k~;fY;ue@-9mOoUIc=e!L*v zoas|*iPBQ-A9J8nvR!4fA4?DNrRyK5M?%s5P3H73)+DCmzcN{^>j1@ZY4~i!T zZAUEbZc3b=2$00r+nqj`eLyy=zs}TWCqEbQIs@3}=&>{J|>53hq5iXr|w$~BK*091Q zuJf>8pehk38Kemt^pG!WE z0`_`0oiCt|Et`2;_cs|gc{((`)LwhU+A%jWf*Lj;IiVOCrtHzxEY`|x#VJy(RG$Q~ z7qG%-KYMsPN(b_*@c5)oAzCh6)UOQ|wohVuF%{91|5*tPOc%z4S-w9yn27p$D1mUi zmMbot#+sToY%kUt%U)A1xv`Cj;t*AAZ6AsbU~^7-DR5nwZ!&`KFj-CE z#Y2-+qsaL9H7-=4fsry|cPs7yZVP!3evI{rkx8~r<@q<7bgqfY7x6wp} zg%`D7oE9~8w4=&BaoTzYNyP=($Q-aMx#(1O>$E$`XSe7o(~uOu!Y$WFu(X8# z`PsI_3=T|q`s4-Y2Q2#Z-7jk0%0C{x9JqUn#apC|DgK7_kc4SphES=hlKISoY^IRj zgG2q97vzCIQx90{rbnmXEcvvLN%V~+W{{)8FhxyT>PK3LERhjezB+qZS;|+T)e^r@OI-P z(t?-ZP~!RBz}b=rTgyc_t`$F^Jo*ZCP3#+Z*ZoGE!@UN|%{7uBme*a diff --git a/MLEBot/team/imgs/CHAMPION.png b/MLEBot/team/imgs/CHAMPION.png deleted file mode 100644 index 9d6cdfb2e03bfa7c910c67ef66f7ba4cc985c5b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22122 zcmXteV|XOM(shzeHs08_wHrGV+n(6AvGE2Q+uYc;ZQHiZZ|=R{`)}r%eyXdwx=x)s zJz)xR;)rm#aA06yh>{W_N?>5$+CZNRVZMWbfm`zxH-o<5IE!jHE8CemyBRo|fC(Af z8JZAF+8CIbD47@-dpL}k@PdJhMtahN1j0lafADyMr6EPiArLiffU%7uGd4bqAF*{Xvr?+4Oeb0dG72LKiy%&wiRkX+H%*mMu?(wQp=oB89eeXvp+mXZqM{W5=VlTArOOjM7|zfm`l6x z=JzwBQ*HJoS#~uUDYDX{WwKF491f~1$;lq3d;+UZ)Xs#992jnip8H_Nzv;`gMk!n@ za|C|sfz{P_L08wzB%oE>e%owKw06zgJ!v3FasQZOH6RPtVIX-z)=%)fXeKB!*J3s2 z8oO+NokoQLClR2})x32yS*q<_c+Y~x|C~NEQiF~;1=DZu96;rAJ{*dwu5GDuG5Qtc zN&#P6`LnE&QrsEgio5M7tSh+K=}6-VjW1qzTw_3aseML%pklLvq1$!am!=`Z>laRd zSYUQi)%YQseqI6|!uOLfwGwN8vNPDMuq|p<1OhS-YYuj% zoQbpR5sl0j-Kw0mQY+3GbJ#NtYPJhy6B-@gT%u71eR^$8z8f*&7Mb5BlW9?h$gH^Q zNLQKNFsT^hT*;5I*(E1S-fN5oM!054G?P0c;QIF)F>pPa3NT4E6OM?>Cz#7leg&jL zczv8b#Ra>PiH2$GRo{JbOc8L_W&wn6%5PG#Q^ zya!mZ7nlT_sor0?`+?b=sY<{FkSlPnnQiIF6@aLA;unyG62jL*P*qZ%A4EO_zc>6# z#Wy?<1O{udE%F7~7ySLlC-gIsM4tLyG}%d6J(b0%jbX5_k>V82;E1}=8vUWM3xK;8vz7vNyz?k?zE z_JwYg{9SW!WuUsIYBquYM-ja(2M#^@%jW{g&3jJ+GF*&}Fq3oDfNXe|QpL)7=wq0` z&b3}>#KG7JK&?C~+FY5D$sTS1AycEW5(jaX6d6^C+;X8sQQ%Dy>*qZBAo_2cdGdQd zyAG~1*IWYZ(Dr~f2c-Fb)s9YFy3I{nNaY*>=&-^65(>OOG+c%#$zoCwb<>SEtpW}W zzqv=*XUw_VW}G?oV?OmF3mnfJc0Pz1Rk@b4Bs4-ZV1^g6b{ZXxCS>NZ=G*=}*rI1Z z!RT=qa7BeWalU+fJX^IGpZK9=R6ceX3Wsmm7Fw`*AqiUM4652N4C~@W^=y8$m2=T@U?M$H+gN$r2Idbf!_O1_m63L52OfeMK!vHs>sJ|9rp6wU`rkE zcL$k2>xsRdigW16zbe!7e<43_Sd&=)=uQS-^Vx)YQVfQ(saSNz#3bQD#%u(8*4Eg3 zB|mI!HGC-WA9*j8-SUO+-fReBM+w>qL&RL2WsPhw3|k~rsfLCR=WFDMz7P_+S3n{R zC(VK<6_pQ|PhY}6g^w+!%KhVht$;iq7yxG}4Ox*R%LYgUWd5#;m!teu_p2@4IsKg| zh`*L6_570K%jokDyWo#@rS0}@@ESE;9`0I-4LWb}gNI!bwZ)!4F#h`(5`{l)aERyO zcK22EV=MhuqPvD4I0VM7WRZ%7TYBUUT3Rdipvl@s{QDnETS)v-WTkau3Wya&7vS#o ziWyT^QOIhzXN)4@Uinf{{n5f8UlO*cubE>m$>`|l4hDkA?Bh_I{hH8b>e*40t#V06 z*WI@Y)c# zJZ_8<*FDu4LKQp2SkXPO%AMaa$O&@Dh7?iI10X`R>bQ0iATVqVGhVT}f~w z+8ZR8!HeRt%h52qQNHi1a5Wky=83{+Q^=)&^TUN!goG1#>!gI7up=2;*44C@h-@vQ zS_jmoy+agA(v*6Okr$StHgBJE|BVJW99wN~{br5Um@1ONyL>S3{+ND^IAZ!FfS%c* z1O8TA0G!S&Tm6K~r;Rot_5imOupGor48ADkpCU(4%g8?9;?O+Mj^5L&5tUarS%MID zPFV_PDSW}r0OuEGTf*O&O_QloU3R$gr?AuLhr*XlM4fb=q{JZSV}sLm>Fk&{*0gm; z-t{zYsD{of%i@Sn*Yo#jtZZzCOeT(`83(fW)#$UpeDH@3dFjC0u0su{hPLh}4vIB4 z3_+k^fprXmvG)fr#;IpBsYB^MQ!wu1)n*8@Tx}&aCn22|8}v#{vW_k7`cH-?W-$y( z*6yiR2&N29$5QVGS=Xn(_;FHj71VjH$mziwfPi)-M@?y|q$h`!W=< zJyT$+rV_Z=-o8T}*$vY`LFi?rk;)UAPyMO?kWlRJ4G@*3RN-8FNnO#WP# z$7>>o4WH{QDh8AMWyL14tV@=~9zOhM7db{SBr!@3cf5cQUQXoxTtC!vUp(WRWi=HZnTm&TyZZH$n-HBgPtp^EUl0~(&Q<# zlLeOn;>bcKalM|SYsA9e0lNz;Cjz?k=Fqdb;RTcE=;gKwq%f+c;vQzq61eI-GO;YN{pCM=7NbtdSdLqsUQ%WGO=D_|P9PleySpm} z%(+DxiU;h4v@RE4+!0RtX#8?e#u;zdH69)|_B9G8jEFH{EjAeq10v=&ZOs$~dkj|5 znGc-wOa}#~g~VxQ-`0e}(PXw*c%Q5FBPfZ|4mTV&vRmoj=;G2@Q~oUl z+_9`~>p-{_K?7>No!VytKiJww5Z0YG=!piPL}kk~L2Q&2@S123pw z8WRtr=7^ZbVw%U4T7w7wR+2n$3rgg>3KcwJ=V^{o~d9F$kv*A8E@lLO*%@3QWE}(3)Ai<6c`5S&ElO3SO>k#mdiJRum?nH z=&^=~1TC(g{suO&|0slOM^~M?6o$%hiE|Kb2=bcKL@9>~9HL4|4C}j8BK64hrI4eECvtM zk61|ok1K0(7L7--mM)5anG$YEWcI?R;WQ4zD zu6Os1JZ)Cl9G{$wv@L94-J~O)@;gI>M8AJeHqxlhfu4)A-)y0O4~;=hB$VsWBotf1 z(BVK?{2nk5CQ8IU{##58Q)*YgR0;?S8Cz4J7d7Us`+P(7c>YV?EVfw@i5ktohVpDV zE!?UsDwdxow%3k1mQ5+N%YVdf$@w-7jIAgDLIg}+6oHKSXDudGh&O+UjUz_HKnSY3 zI+_|~dS0eve6CqSqCNlSQHCXySxceJ6N945Q2lET`9X<>`fRO6 zq`Pyu;FUTU2~xwZl3V!ofe0&Tohss>rwPT)9=( zYZJZx(*AREwLvmrPvE_bocYOLXw~nexCFAuExVd1gIg6|1vZgVoMEF|iHt`ms0&!_ zu*vGZQb53W@i^B^l-ks8P!@F!h=YP@KiXkDxM^R!Jv}}Aijj`RoRtsJShSpe)t2W^ zh$Q6f_Vn^H+0FVF#}UL!8rSZ<5Ib2bNUo{hRfWW#`wj#m`+ab8FOaKRr=x5})1|pK zB?|EMwYGT~@5)WvP}XC3Ty3n8Yh+x*A6f_IO_wGD9C7gE^pCDmo_u8_>QmG%-X`3( z>LMdcCDsi{8A+CWo6A$WD-40hDz(kGhs0jhtuB+r5N9v|AMaPzwU_-uO9&)>uKoe+ z7Di90h7y+H0KQerUYjJtmQ2u?AmIrlnQTNqg*vI9-_WC*kP##?}vmnLz3FH z_A~I1^N>*KoxkrgJ-!=#4j@E)Rn!c$%`{XGPf;MVj(~)c>3|B*U{(#~rz zkQ0812KJWJP-E^35%g#du#iY!ApYMnFzAxt;LR>)L)AGir!za>vg~6HNQcA=jzE{P zPLvQ8je!YA#;`!WiBTw61)L)Kqmd}yC@}A$>aG@qmMRg23i*o_Boc^dYn2Wr<`nSi zQ&|K8)}FOE*yWP$w)lp!__DZ`xXhL|zvQ(lI+E+04uPL{K6evOCbP7S4`GFtH~o+4 zq&`@QNHKyN@R07DStsOlyw6ZIIn__Gbv-f;KfYi|f_m{Z1oi5~X5V~dcBm?*Y^+Mz z7d7am9L42NA+ePQ<;KNv1Ioz!vZLX%VL+?T7;<#gGP9S}wCxraq0st~1kiT_C0*_m z&g;$=zAU@oZ-QZlYJ6+3XK4~cYByle9I$>Q3JD$|t)?fi81Sp{3?w51Ms?ifFM)Kn zx-IrUupxqO?|$M{Rb|lm(kUy(HgU@qeF#-%mXwq?lgjFmyu)EOmu683n(|Whs3|zF z>~q+y(Eji~^R4midtHC+C*pe)mcL2vX_ZvPJ8`89iusAIiw=EP#zNjEXRzU8@zI_l zq;F@grSh6ZcL<;d5` zUP-z|R2_5u_4RLYxz=iC5dU;bIFaYR;!Vzj!p?3>+{-iQF6)FnoYPZBlJo+3$5j3{OMf#pbAZHQiTN_4$g&CRza z_UFmuGUgbfzoarSBoRXH@iN>(?Jq-bzjpoo-3FqYd_-_0D*+ffmEcmFHen7#UTMex zDG_&?)(Cj|BF}^O`sZ8t*uzu{6f}SYOR(GfXQZa9@GUSU1B2g+Tj(GN$xd3M%IpCB zpOuMu5h9ox-`cR_oO8ynmyf-hNINJFjXGv!JxQ?qLgDCb>H~MBVMr(#+clcPGe&;R zt*obQ5~pPSN>b+F9^sd=(e>PSR0DRTec`#F$7{(INV}5miHNn`nR=-EkGZYRE{27@HxC%`s}A^4IA@k}SeDV|mJ9pl(H7-6 ze^Y0p2ex>k^COjUl`hD_ko3n~i!rz**Kb2Wk{d*+d>=E9sYLw-1*50x@|wxc70Wce ztxh5dK|`$l&4wqN_9q!^%`F&LYMly1c^c8%@k-X;gc%If0@}Mqf&tbd$;bJO(eDC+PjMoMHzQ=fTXL3oPAq$Li?nP8} z$q7uy?eB?142$RtFQGZR5n?~a)v8d%gX+4Cw8e5x5y6*iPPOU5mU@oCuVdWz0bk8K zZJW&DSdUp7n4mB!Mq=qczIh(rsSb=`elwUMbh9T#Y>yf|p^IiuYr}c17o7_c9O{0h zef(_~{`8o$^u51^EbLBWL9ehfQq=B7>@{wO^iD{$Nn_Z>;{%eRr)3s=18TQkT0Lv# z&jxIR+pv*9o}Z#e;2GQcaFQ{B(OFjTr`64iM)X6=Te+8}6<~rh(90|%-J~}F$3@>u zWuZw;nP>OMAn$TnO*1s2b(?67Kus0m$ubVkb}V*po29B4eVIg~qHZ3Yb{iN<|B_BJ zP@DNGGUsQuUkU5t0A}bVp53Ri_n~q&IvN_9Q;ejdCbQ|+ut(%A97Ti#nQUoA(ONb{cy6#jN?;W@FPTFRSk)iQGA=-c zqQ7=iN?tm9%2&uSVB9$m9==Iqg!=P#w2TWpYPecb-CgQ_CI5NwuPj$s<%e7TsFl8k zgi63-(!|{E+^3GKHZFo@5xLKgGy0oz-k}v)8W=lVrxM@1GA${m3(KE24gr(A^uvSo zN!#;ks+bx3Q<~!5FN|Wh#Cqettxdwb*d9r&ELd)!XclnP_zt1$}Ah zh7q}+gIA^W>m(bUYgKJ_!xxf>F{qrb^leAzlKieI8Cs*e_)qCt zB}j82#ym1!mJ&qV<{KU7+9ljD5ERnVou(chmLeLgmJTMuc&-o(Ke%lK`jxv1%GWy< z=OmD45;u7YYDoW_OQ@o8Yzeg?2I9`}Sbq74k(lN;DjV9z;)nQ)&M&e;xog}qp1hj% z^Ak;04+*Fj5XkK@gZKMulWaBus&%$^OXDx*bn^960K1!RD`hL@}X{BJ-dBiWXI2)SK zZ6mO;wTrnxmDfU-0jRrs(>2R-^Q|o#p#(Q^2eZ{;b*<2LdRkorz{Mw{4n-8HdJUkS z559n@XnNVh2b1*-qSDShLFg6kS6O^cS6oS#>zS1fiOsGIbo{Ps;v4A}dI6Y>uLVUS z5XQTqjH_F*(}#F6PtiVe58(+Hb)tV6wY7Tcefh5cT2@v_Q@Q?os^d5$)Ss)G3cE?w zUr8kHbq9NTXX@vW&^23G-JInpZ-}D5!KLoKp{X_}^nQ+gC*CB2Mdc7|nO;bibzzJ$w5~Yz*EP~r4)1#1k6>UGoN9I>>xf{7 zB$B3`P7S?C$n43w8p?q+27a}tM#%)+6*&FyfTO6{aLq$)>~|pHyBA;>TFGM&vIo$u zGmX?}ET2|%5dMIO?y-l3l<)be)S@8E=yj}Zjg>KkXsMTiAi*7>Le<$T*o(YmS5t1x zsgKi0^F5vq_ER?v0s($l^Hz;r`YSKbg>7jb(+%t`Gn!+6eY%6k7U6*prkaOu_qloV z;~hjs+S&i;7hEc#=xDTJVB8t~W7_=iNIYgw0`%7s`23BBaWbQXX6$Q}Fo~0}N~u0} zH5X}OsXT6_mrpEf%9a)k_vGWe%inn9%79$9ZSt|H$8yl)x$rsImRS;SO^7WFEY;UM zB>V+mAw%9R$X-5L`j;I?Y$rhvpEuzXn zV$_SG*d-qKR-BKYSVFo@uln-Hn}F!Ux1B_*>B13ZtUk*F3XwDjnl0}nm2^tlTlF6pNo&efu$AVHTwyS%BOZ{pYd9+Z6~)IYG9CDq#7s)$ zfWFymmN`7GLOrnLz{L|Z?gkXp(a~W{Bm?#__MA5c)vA-UO1MrRymxtwsX4q3B=>W4 zezM&Rp-}-~IYIn^g0y{D{jVXo1$gq6PSGa2ZoSX(CZeHXtwNK63GCgNBNQo^|5J&9 zc6VJDA0xZ%YQo!EYP8LkcN^#L&kQxz52+7H*1`;$2xJgh2*UD+VbYTmxA!^Jh`dhJ zwiKOOEQG>r;~iFDV1Y9Px9i&baq}j?tA3rfCVCE2HQI2Vr*Ren;+g2u?(vl6qNxQ7 zbkN!pVFjcL^fKvF?;o;byB)H=G*~$l6Hvk$NXq*E;iT!*4u|rx*W*WO5o=az)C}GP ztsWQEYTQR=pkE6}+o?8F(W5&bbtTWQi`3PR;!K>r(B3q`H%&cK(Wu@k!7C5ZF< z!1kZ;qXEF=UZ?F;+~7FKe+|TKoQ#f^3%2fkO_(~OtkGWkPQF(7v1LqX_Sd=-@$ry+ z-ao1&WEX9t0$_rd@M$J@0PQi;dE3wzN}eCOde%O#z}1g&W2_)#z&M;8oZ_heEIX;x zGU}Gn@tWFG4u}lGymWz`XZl#{9QX`npCzAdKOd~JNS-6xhl}urENTQYr{%5Y$RtSl zEkxBO&2na{)&8V;F=D1wB@bV;Ni4)}pr}wYZX&aiE*q2pZy23ViFFp#-1}!BODm*H zV@zJ-kb#vBdST|{AL2S%KWg82%W&H2Bsv)#={U-Ax^&X*JbLo-Wh#LY#jp4Cn64bW z_C#VqDpfPF3(09~2@KX7U`$9AX&Hg}E1^iZejuV7;ah0vLL+rMDB78Yv3!n(2ddnw|0(teTs44%K#!2#Xp(1&Xd?Qc#neXw?+7q48mJ2LXg`?TYD?>)f0#1%AZEHG zxV{he)a|Bn6ef%@*;S%ddAoWP*u1|lL)G{`(HxjX^L?u3FKpQ%;UhEgc1fRmolqP& z3YTF;@Nnbx!Pph^8UtWRFeqDn?`ld(u^;g@;l~%)=_M}3yCGaTm^fK{G&6XQ>iz7_ z{x&LGxph+L3hW`Qd*1bH=QT92nXUME>7hQYe@LR-gY6w)7Dc2Y14?C_RUs^g_?S*~ z`7LT>11hf$9`l}1b+3b`_=t6v8^*X88(VCv1ewD@xlrL}OJc($Z|;`1XEBSA*pWg5 zRzb%`)9X=o-pw~9j(x3leGR*}3{8aVySAl+lAsd%@6C-!CqorVkmO%cB_msQXW)y{ zc=~EEjE?*2D&3l3-#be{bNhO);`#(KaQu$;&;=#+vEaq706Fqzo;)Z94qc~e@KG(Q zOf`4}6;9fI?ceg^yxI`4X75=t**ntL{nYMG*Gu?bhd4Vgy>IhgD*jF+G729fRC96> z4akA&N(L6KP4?ESEG`k^)pwEZc*mqy%B> z&&TAe@QdHRY~s>C9Ui4h7Zc^?I^ zTzN@+@)Q%~%7L3kF_8o%{-Ee<4rLLsKX){g97)N+KVhVvHo;nS4{r0lp=$`pPBi6yJvLgF^y~` z!c}E08Ikmg+XoSXO?J9cRm7ZuVOkUKTS$NckbpgfS|3}8vC|&Cf8S1Ve-8QUe*K!H zFedCC+lu}(AT4$Z6tq;{*mlV*l*_-Hw3dDAYq0jiXg>XJ3=r0*a!>9hu|GUGRSgc( zA&Frz!QFH-ZAPWhts^e|vf>4eohYJBxbQmYX#0F!9I0%1S%H3QL;>k?MStHdBF^0K zQ<-SOxbz~JXQq_gqo;Q%f?{@K(q2bL>Hm=U`Z_%4xu3elhq%1fOhWB8PL}nrio%Pk z8}U|t_o}dlLZ~MVLJqIQWliWhYrQ?6bUKTc4@El)nDc9Vbj5CIZ&hSfuh#bi`AIG# z%XPlr^=_JH4b6Gp*SqwX%?^p`lIddjaHCpXSwhkgn+*0Js3ByL4aAjYomaZ?IKfmsn$O`)6>Nqr^cm#>H*+ zoy<4k()K-QOxZW@6e}{5roX`j(uUDS4^Y*p~2(({gg{)OiZ=*$1W!vj>P-C0gw`*78|< z(pxIAZ?{%rlnU`=&=um6AdR@RgFRGJ1#={yyS84q_Z$zrbz~aS$=L1M{6U4QMiSmE zq=oD!io^Vehg{S;{_;Y&8-#bAW5g-&EiEv4w23c3C?hCubr-ZA;Ej8*Zu*mjRw1(5 z2;S9U^<5Sr5>zVoQv#HM$UIvG>i$~dZ;%|9 z5zDc*PD2E4;fZ{FQacStKcVdtihfJhiW{I*Fn`H;;x}bFT&al2_dy~ViPl#BJv<)M zbH*Z%!8K-jDK_jIl`;Gl_+q$jssJ@+M(9Qi-UIbR`tS~k&-gv~n*a4F`ltcWvPCy~ z4g_HZ!|g(LYwsako#P)qU$-bQy3Vp+X&&)I?394_?}?o1KDMT*GiP@=ymXp#=|a2c z8=P~2^ov*g2X7uSI<5&C4+$KdufKI8{*rV4S-W@uhs54Z|Kpd4k@6KVY&j~6p^4aU zAX)6&bh@tGzEpH3M%@(F^63zbcPW?6qS_FKzu+>?3g9UDx*AWs&Dz+XKydhd{g1j zo|AGDz?U0+K-~v&E_@etW6<3VqDQ$4P6Ex-b3*Ch?H7yj zkVcQPE+S@uc>{%u9|&Ti@cQ|lO0EpN^gP(kY1iCWlQXtSw>Z&+fIT@wt@TieS-fqb!ucle|FNzS!zr%1gnAbvjJr zeSbse)Vu_&1J*Y+;eFZK+Op@ReLG$N)(0LN{MJxx*gz2H<)0-`SvWLPP2X)RS|_^P ztskZnbpI1@=~akkfFVwo(#^ZkeB|imYfnG#I2`|ADG$9PTB1SVl5@03(eCJ|DE(sI zJ2Y${BnTAp@##1T)}Wru7GN?pHSM1x+rlF1xQXVq%R&c2YdZ@dA6OjNMo^btLU)se zyIjsBehUw^Xz2t`OnKAbOn2sx35{iTNv3HpLBI4W0V~6_5EXXREF?XoYjwct5%f7I zS+VCcui({0e^Q51IiPE=%1&i##re2b&b-5@Y%gCooCN zZg3OFcIPaEq3y6@O`f(o*41Fs`wFSUaqjWLDDw9H!Uc_AdS;HR)xCFi zFqNqFMdUei#dWjSFJV2RsA)AXP_@Tc0m#p>DPgRViEfx&Ho5$T;T_d^d zXdJoW=l+&!ZIYmD*zp9s)If=fB+J7PQW{iNihSid$!Zfg@wKWC^;g~_OjLwBnShs_EP@Y4JDg$nJS(IPHP(b zacDkT$ckAU6%$)4V5L*Mg{1sXEw-*d3Mh6FJvTvBFS>W{Mh#V9mQpr<=-r%Dh}$2< zE-}^aJTQE-SNg5UPgu+?C3xh3U0|hHt{14#zrDIU!b|PxlJz}$m=`BdqPOz;PH#M? z(?Si-|BSybW%R-jmd((v9ENTusJ(<$igxyvrR_DOO((}v98_zRe}~S@%!~@&^t$4^ z0ZmKWpO=NGBSVp1QFaCEjO@SMOL;15Zrf%~f0U9^_;g}ktX5Cl)lv$g&s0<}=8YeBO7C=ixhZR6E+9IkZRR;@Avm+m~QZJ;>~WDt>q zrJD-Xt~%cOkb;`+4Jx1e`&Y_ITo>VcZ#vL3V`lw9P?!yK+BoDMh*2ziD00Y_`p>?Qci5$Xe~uQzYSw zZsm`jn2HhRIi(40FT9Qa9Dltvr~LVcECv^tJoW_Zv?=$=Q+myvhf~3tD;mN5U#lf~ z+Uer%7(sKWvKp0&5z5U%UVP>7Ni6-SiW9>Jkq}a1t*L3Ph8J1{3BI#S2Tf@omjDd# z<*{rN8I&Jni&3=}i$GiTrFA>8=>#czsC+2U0N5_tL1650wA~^1dp7jiYPWGHxFV4l)j2wf(@bCa{G3|SMI$fO%${O@gLR6D zSiJ}#d_8PJsF^VSx%-F5HFPuGcHGB4B~@0@9^@Z{2*h;PB_ovk)sMC=30ELd;p-=GJKqJJ-ZF3M%Qsw{7<++a>|icGTd4 zoh=`hH=y}&vn{_)aecO?yR0F50EH$Yi-`ZNF4NG$tVqM@?E>(=_rA8lf93>pn_6Pg z^|HpGWY2L|51Q%VP<1du#TZ#RX=#fYgDD{|srNxQxNDx_UucXN`;XV}+8xf>x^?p~ z-J@pdpdPBq%E1v467s#~aIXoP(XJ44;;!Oii;Y$$w>}?Yatoxcq~_B{fhSw#Gw8zl z%d9{m0`Kb|%xjW4^xo#!9lmZCGqwyc#szbd_%dn2uPe__Sm=Sm68~QQwrFhEsuAQ_ zh_U^Ft<7@*od8CFqukTSinSsnmXQ2$Y?-!w&*E1Wvc;7K^~~aB2IYp^Q-h(2m3{Qq z;-cCOk&$|wRcWZ5>`X^e79AwO*(^OF--LKVq(aTEZI zD2LkFW90@_#A6`RmuOQ;Dk7#%-@A&4^-^*!_E#{K?S*V?p3VNsXpc%z z)do7on&UM$G?C4^-d$*jfc`9yhlf+JozpE^Q=&Sb_&6pY7K^OWd#My>4hwUG2v=Xr zVy#xsjoRP@QaF&qZOxJC|K)d!0=pysUJ9FU`VHH>!B>FYbYj8Ohnv5+Mzl$NitMP|UcW*IXH$!e~2PBwFxGc~A3YUNqcv~lB zweiWq{sW|-{LNYdNZ`WgpFb+Hi_cb2`mz(g)FDXOXaYV)TAJ2mek^Kf{}mnBJL|79 z3x0nKm%f)~siWn8ksGgEb{KzNi>pN8Xna{x^L?48$b93XesTyZ!vBx7&~UwaA}!Qm z?xLjdM4(ZgCc1!zIY=CY3{n4GsX~5m^nxk>l1KYL_y(xgZiN-0O^)|+4T^*Zoo_#B ztN8>LS-w*D_fccs75gk?Kq%E0lc3iT1W;Y)=w@r;?ZcV|w!VOKwo9Awf>hM zV?AkeTdlRyhN8%l@d6yRG~H+vr_7vY%{g@(w@FW{wDx!Z6;ed~1Fk|+Zk55uSwW(D z*;(?5GKG}$?qT8cXu6ik14yXoY=FtQOn?nVvUzX(^A#=$Hs$o$e;*8 zK`FR)^u>)rygb(8wwUWG4ibJFydce{#}VqgdsbEc$mmK=f+}cU)~^#HM}bAH=K2SXrohfWbBhZZYqd;!3-MwftYGVNx4 z+P${W^|D%s!`~oGI54+a7%d)3bw|KEUtRl%T!>IVKW>v43F`lB%KG0MgdQilQ2hFg za@H#-S%sS0ll^f5CdX&gW_yZI*DD>QOF$&!Rw$#?e^iB2L4cnjkh6;)f zf?*djB@Gak3gScO3wgU9WO*OwI|gf0;8Z^@Z8JC;I#VkT2DlLrf_B4?(=B;ef98Hf zv0*D{7o_Nx7i9{cCaa(iD^WbSD$h75fm>O}5B$9bNkISEaAm>ISx5v;RO=$tww_g& z&QMzkbvmz6PFw(G0v``?8RF0YXzFDLIzF%vfZ9UO~d^ z9r=04D+C{pGdP}_BU6ustd#&M-D3Zv_4@Iy8l(wK=@Lq%F= zCrz$X&fen(r>GMZB^?+5UqjKR5Fe`#`HKiNFIyk_)4bNs;9m@kjvtwk>|e&;=SF{HkpEF(m>sEr@2&9 zPES1l?ydnUTF}Y%AJy`-iOti7wKArc3_L`vzs6?e!fHM&xBX-$6tL_%7seXhljo;M zOiWC&CAxjfTesoRjXoxfyVb#2;1aUpSC zQWS|41PhwUd6n6_vD2xZXUcqPZ;-;Ibx1m%1g`xa=V+1M_kCr((fmKKG$CSVBgP?& zu@ziFfC%z#1hCS;UDsH^N=(+5TWsW^kR8FTQx_Kz)%(u`+@|2i4W`7<i?YdCT_!Hsz4eDuNCV6 z2o@Bw^X_5f&8_h)xtcAVh{Xekf95xR8d$3s+cUQ_X%N%}-5*-mm)tJwC)SmYujr_TBhr5}LqZ-|8!@Pu zsEROxY9=`E=FLlau5%n;&1jOUT!oC;%=A~(l zB(rVGUtxplLYTG37}*V$PE6`VvK%!}MZy2PYp@j!q(ujul&j5r4%0xhyH-V9R^em6^J_ z{|`qrsoI7*tI0)XD10G)Wlzb3K(UG2`1p{}(4*5Zz)wYl+~>_C#tZ%>>G$ve_i#I| z=iT~6i#0`8yTO8MF~#E*QeSWOZu?El9CEk=SIl<+$y4CqEn3)>D<)wb72y|K>%(RT6Oe+g0%GrEQ8aA*7U-Q36-! z^p%kZc)Qp)qm5edX$K)WaCpeDZg zZdEn%U$}`t@~CC}`i+cbE9d$;bLP|wYTAEdWo|V^toy^VUu+6800SZzTM8~B8$Xr;?pjT z54}{Q+qpQ}{GUm0rnZ=sni+c^2z_rvn2`YV{wGkNi#D;&g(O79&k~5$2V;RAqI2@8k?Izb%#}=)f5O z@39tB-cR#lqa;A6q%ExU))c2GQKEVhiLnvs`c_Qf>;C1JiS|8WFB9zl_c!HQuWcu+ zAONGe;(;hQu;D}JWiITd86^6TS}X7{5&P4RnP)Myyvapb?Kvu~18FH7(*?bg&vx5* zGiR;pFqi0s8cZWRB>v~AFA3FLUIh1|$6W_itAvgmdF%E9+ROo{Q+TE63p+*=rDN)d z*vFcB2`}ZUgxYp7Hi7#%@nT@b@Ji6R*ddwfp`@9N*@4)fWRU zSZm&uc%J|iQ{cBV{b0)EcGoPgah9wh*N7>THu!Aecy%JLgd-)Bos{W>9-md&`w|djuCDmQZh^Xcd&-|pBcM4wF8^yaTfLOxxGS%lQ24Vev=R%T(xj^%3DIG(n* zTGi?Ov6Dkx+eBdcM7$~dC9aO`jEaTNVAkF;r{(Xmg{lkJN;?U=@cu2v&{J1;2}h1o zB4f#-#G+Kp27>7>AUn(rYYgp!QNuu4#bVWwsM^^Ui8k=nbK8CTsKzJ&&u>3Z04hp) zgYdUJz_A7iKL-6_ZTl#sUNi$;bFv9O_dE@(q)l}J+~$t-xYhfk3aMznQB3^ZW7k4v z#HL`M)G-Xk6pp-!4`A=sEOp*jyKMRNI*6O=%9Vx9Bhse+$nZh$l#bL1bcMfp#jz_9?`zm;@B-}CsuPCp(>@mK(r`i+n`4HZ1a2|?SWd!drkL?s@!?*m%i1UZ z0?5P8gE9^3w4a_S$Y*4fka9HuwbN5l24XFE`NJ8<-Rf0H0HSeD>WgaNxusb@=H};L zz3$Q^vXqTt8JMJ%h2Y!o@TR39+@@ZN^V^)UNt9*()6{jnydP|Op_xXt7O14Z6B4#b z$*T{mQ>#}^946OOJgSdqMB*>;`MGwA(4So289}e@KkTp%o#@McHnCwi{RWHMTTjRy zeH$PeKNq};6&MzhdIwW3(f%>tb5lG!NJ7)(tLkdkO>vxzmk4AwWIE;`u{>q^#BWQ@ zO=)tsYtb{jLX9Y<^TV*4%)bYb1^~Omij`bJZEx|qE(H*{>yy?7Rv+|oZfPz{&v|gPxP%}?pp;iYWX6y#4HBSP|Gv|ZF6S--N-6y zz*~Gr$BYGczStxqeEZL?uwU~6x0Cnbi2DmRsaFd?lbxnTws!dipXrRZ0GWKsrtfA? z`-0>Co5d*scXingkI)yI-VfK%Q$nqbDd~peHvAa38H07_RjF4pq2J>1giKat| zy#xgxRBk#cn4qcr@p!5x$vlkm>ykj17UP?+|K|qmKLUKRGy}$Y(G;T$Bs9HEIcP9Eb?`{FU?|;8v}s1ycw8f+~`2AaCmkvJQ+&t zff76z_(2F=`mV9U0GvK84c(S?Eh>&13wJq)6!Y*!y-^T~6c2Y8*`FGbMTj3b$=gr9 z*?C+%H(6Gt+lC^&YC2;}i$prCEpQ?ZQ*O!1gpDo9ot{>yI&~a%1A|=YlS6){1&Q3X zNo92It+W7RZ0(edef;(laL#2svl9Qiv8i<8@^T02($uC`?*aC z^M3QiS73TkIWKubLqm1sJ0XQ)yL94c?$1_ptu2Jb4nUc>)4%OGsNmLhB!REnQNVWO zD9G2PeVp+3NnFWU{f@C40>j9<%ma87>v8 z*(y*-b|Y|jZDjK%SG+xwBLy?~n z7IA}M27fN4<(^ye6U}%Np7;>sBkxjCo0-LQ?*J zhKp2Vx#*e#{?@bN6=KhwEbx4BuoPTg{^b+@4B7qr2hr@MTYh z&4eTmEkDT)JZd#Rue6_4`FOnUrM#&_!$ZKeaD_6r!r?<}z+eI48yd!2-=u93C@uU? zChPQSFxIOoWH8D;8_cbKpYGj*8%u0szc$rGCFQ@_0Zmf!L-L2y@c1i@O@8mrDo;Kz&x)CB`XU)u|4gcq9!=~uTIqKz^{7!4B*4q9gjZKU z&tJa&x6{jFXcM=hxE$bS^d>X&iCf#VWX$u{pK5VBRpLV#JS`?wNBQI7wMrEfA5NmA zkWR!cnN^GX=iGC}By+$hi%c_*s$NdYdKgKwjH~utO4v;WBv=8Kjx8(VGBzvsgw#sc zqE%VObs4*~aMzCaEe)P4%KU!+NPYkJ*VUMrQc{sKfL!0&c!E5A7fr#lRgEq)=X4_y z606;BycBBMqMX{3-F_W-a^4@3(Kb!q4rp`9hDi^eyeTm7Nx-5ihaEgQQqE5evtY&m z+;^V+yYV*kKVo`AR_9e%5>zd?(vnx_qh0$t4Lf1e(4E~~U^VLMSrzW=zMSjg7cCu~ z_2b|Y8zZH9Vzjrc8$JC|SnZJKn*N;)*Nr1Pw#0h`C&u0V&xlKc0fA&%m>2nxdL-M$?ivrdVuRtC9q zNcy_@@|Xtz;8|(4A-#Bf-oTG6Ma2O0=EslswY{o6raW_qS#p=)Ebzf^*!;f8O~~!2 zN~O$frJqD`O!1tGshedYs9s6;-fh=7{&v8;!^nZDpl_Nsok?Q8Hd0gb3hu>UUe>EgPe3GIGvk2k2z|5yHk+r zodHW>FV4?@rdojSz`M)uo$V8=x>atUi4Zy-kNHJ0;B)Ep9Cr_g-#IOmMu<1~?u$1V z*8;dPqX_n9@OAaI>d&T%3hu6|5yN9Q-Y&01v#=aFOFXYSTJB$Y+s0cDlAK@ryNZ)* z)ZP!j>sIHYYC=p&OPt|ZD!OZuj1CFf%h8EZJ#E(Wbqp@Q=bYzhfr#8cCnK8ub&g!M zwNWgr8NS1?>YcouPA&^w_iCVht0xr?l`q%nIHG*b@PVBb*RL8muBwO6{j z8dPBMwoS!{*~1+&ei;i=LGrZrxPQ|j?B1|RSd~%#xTof>cXrYb8ay;pRV_$PPJY<3 z5qj;m#&9owaj~aywyM5b;AP@Ng=3X!yoNJ3Mq24XB?J8W=A|USpj}q6PkE-hAn3Tj z8H0^2Be(5$N}_x`N9~rleD_Cem4}p=6}e!ZF+Vb<*1Dc0r`Nd{K})}Utc@-fYjB(k zt*5DR=$A9i*rgH`xqv`VBo|5lBWl_B_gBXm<}MXe{|ck{Tc|p_yh}z*d$yYW#Y}L& z*Rd~!!#8_sR~FvHQpt?!x5={sr`gm_Yc41smg7kFv*RXoY%Oo)+p^(WLf0rO7!cAd zo8*yuY&Ifv~NvEzFT?87~UNLT*?YQoChLT28J+h%K*nURmp1 z5r3-2dDwGGOM0n}Xtt8`=}qENILVNM@QCI%o6k!pX42Sz#gO00xgc*-5h7%yu6Si~ zBY`4;>ysgy0VsxvA^taV=qeNfGb+YMzGzo%z#VH3au|+l$fJ5HC5xRDbb*nNo?>RP zQgXsXjEj5Pw21Ej!?rrcNP;yQ|=C3AV<8d);dXk#%7i32+KoQhhrA!nplW=*?z;Ak~rw8Sq zo^(1a*vDWm#kly0X_-=6TnMK8D{8iw8*QR>$_~W-)CUdzAULD)zF+3lVWZ@qG>>s9VZT$K*0idFx3vO>d!2B6D-Ob%9nHH%BYa$!qlp>|wb=eR)DK9T3>2g)^`+lj;9ueOU%y9jXDYHKUHfV|4I5Im%yN^Os2jz#3SU`Bik((m-a(ej;*-Ca7!7jhjgZ(A zrECk&TdAqP>lS+}k~7M3h@Eh^l&4cwpL5BG(gd5A&bv&F`w%kjCPR-Z7)0AMT}JJ> z!b%mssE{#{c03C-+vEd}5NStxc@+kE+e?h@u%O=?gwt1p-vbiNi(crz{8RwU^meQz zdr1V?y)#xR>pwoS-py>t5-u1j%x4zFi`s0OOxs#UZAuaEL+PbI`>dzWY60^~E3%5r zx7J`T(wW$UjIw$$d@}3;0M$H4@?dy*?G0SXB*IL@NiO1Gd-XkR$1cv25_&XUWRm5u zUIPS5eeit6ou6eD(Qc#R#JVm~gwnWw?Czq?mu6)5 zw4mXN{deZoSbi+Y?`U}EFiE$k7cU-nFstnYuYwa$PYM@v?+6!y0R)(edqOG9OwY#V zJ%in7*L6Mj3v;{N?Q%f!G^U+K<&0nDW6>KT>BQJ8W-ZsQpnKxP%2EuzUrN_{E_DCi zTnBCV3^PIZ>8CJ2eTyJ>u`~Uf0FmIU`e9sCJ9K}>mbggkmR#P4uldt0qC9>>gzLZC zJQl+Q?waj5&z=;r+_~dGU!3hXPrGrZ4RLAan}SA{^j<*MLnZ1Tx>;6^E_b}xcGKA zB3G_?oT+2l`yG@j`azpd(w9Dru0Y{>**D|btEwSKt{&Q7_tbAZwSbB*k_5RM$cQ$0 zC0u^kBthmQmrEf{$@l7KrC(^Nn4#D1M!rViFH1E3ieghv$p44$v(}hmsJgN@cj;m- zD=BUt162(hQ+}6~++w&Byngx5KF~u_k`3xL&oki=MgByTWsRTt4{SHcl+61oHmVU} zfvs8ty=?<{d{XUz=|8ps8}&I@(}NajS#G97EPs~GP^xIqy*9hfJByz$7L>s=!FK6e zZAVXBBHiZ?Q@J{$!mdv;6)7nE`7f@-LFwcY(xZTQX1%G9%k_64H*6Yrk>!uXJ2^fd zOA%KFHWvg-)NorfT)j`^<#)E&aYdVMVEIj2K~d3^#KfWG3Ocse>W#y-aPA-McU!5l zJM}!ZpT3Ct6|%Dv^biscYs(jIL;4xw%JM~15_+}V5O1?n2OJ+_*+sL){}PbwYaue2 zM>q@VFnlsbPH%X2quX2^`ta?VmZ^^t;QUyL=BHiU#SNlNoXjkXpF=JTm4mOec(#b# z#RUvPocn_Y`XV-SpSV5vC*+YNmR;ND+O?H|MbuTI$8KhcgTl$}oHq3&3+brKcC7ol z=Z6#tajuuOnZ>jzm$Nc?ItNS08$Wr1FDCA-6rcg*xz~Kq1hDk*=D4-n8nO@CTrwaYf^VIOssN8Bh%~7Cy6A;Uk_?}X@7*&M4>4S( z`QR=2IOBztUXcS$_vUs#^ht(3iUm|+5@8bKK z;;0IE6BW5P+~eN3cll!!0m3L>Z9glSR8Y=QoGF0lCBtc8?cAx!Oeo)Gpq#~M%?n#pAikeAS|)%k@Cr55>~qkx`*GO=&-t5*|&Ofe7mflwVHR%Nk^90BLd?E3}L*T>1M(nHutUJH&G*_T&f;(-w1p=kWWG$x31h z`M=r{s;OKszYwS7AfgMJ>0yZJaLu>Nnh*aW3j#j4MCoZ)Si+R9>UO0L}ZIZfq3^MYkhu*@(#(h5R2Xvp7`% diff --git a/MLEBot/team/imgs/FOUNDATION.png b/MLEBot/team/imgs/FOUNDATION.png deleted file mode 100644 index 57b9907012561c824bae5594a7c02269a9c0faac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21789 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 diff --git a/MLEBot/team/imgs/MASTER.png b/MLEBot/team/imgs/MASTER.png deleted file mode 100644 index 482f5f52be83890fc5e5e34d5f02494e5bc44ba5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21646 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 diff --git a/MLEBot/tests/__init__.py b/MLEBot/tests/__init__.py deleted file mode 100644 index 81451df..0000000 --- a/MLEBot/tests/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Unit Tests (WIP) -MLE DEVELOPER! -this needs to be implimented, would be a task to start? - """ - - -__version__ = "1.1.2" - -__all__ = ( - -) diff --git a/MLEBot/types/__init__.py b/MLEBot/types/__init__.py index bdf9f9d..c51c0a9 100644 --- a/MLEBot/types/__init__.py +++ b/MLEBot/types/__init__.py @@ -2,13 +2,19 @@ """ from .enums import LeagueEnum -from .franchise import Franchise, TeamRocketLeague, TeamTrackmania, MLEFranchiseTeam -from .member import Member -from .player import PlayerRL -from .sprocket_links import SprocketLinks + +from .sprocket import ( + Franchise, + Member, + PlayerRL, + TeamRocketLeague, + TeamTrackmania, + MLEFranchiseTeam, + SprocketLinks +) from .url_datalink import UrlDataLink -__version__ = '1.1.1' +__version__ = '1.1.4' __all__ = ( 'Franchise', diff --git a/MLEBot/types/franchise.py b/MLEBot/types/franchise.py deleted file mode 100644 index 1e1d005..0000000 --- a/MLEBot/types/franchise.py +++ /dev/null @@ -1,191 +0,0 @@ -"""Minor League E-Sports Franchise and Team Dataclasses - """ - -from dataclasses import dataclass -from typing import Self -from .player import PlayerRL -from ..services import const - - -class MLEFranchiseTeam(list[PlayerRL]): - """sub team for MLE franchises (think 'ML' or 'AL') - inherits list properties, used to add functions - """ - - def slot_sorted_players(self): - """get all players sorted by slot - """ - return sorted(self, - key=lambda x: x.slot) - - -@dataclass -class TeamRocketLeague: - """Minor League E-Sports Rocket League Franchise Team - """ - pl: MLEFranchiseTeam - ml: MLEFranchiseTeam - cl: MLEFranchiseTeam - al: MLEFranchiseTeam - fl: MLEFranchiseTeam - - def all_players(self): - """get all players for this team - - Returns: - list[PlayerRL]: all players - """ - return self.pl + self.ml + self.cl + self.al + self.fl - - def by_skill_group(self, - skill_group: str) -> MLEFranchiseTeam: - """get franchise team by skill group string - - Args: - skill_group (str): sprocket const skill group - - Returns: - MLEFranchiseTeam: team - """ - match skill_group: - case const.SPR_SG_PL: - return self.pl - case const.SPR_SG_ML: - return self.ml - case const.SPR_SG_CL: - return self.cl - case const.SPR_SG_AL: - return self.al - case const.SPR_SG_FL: - return self.fl - case _: - return None - - -@dataclass -class TeamTrackmania: - """Minor League E-Sports Trackmania Franchise Team - """ - cl: MLEFranchiseTeam - al: MLEFranchiseTeam - - def all_players(self): - """get all players for this team - - Returns: - list[PlayerRL]: all players - """ - return self.cl + self.al - - -@dataclass -class Franchise: - """Minor League E-Sports Franchise Dataclass - """ - players_rl: TeamRocketLeague - players_tm: TeamTrackmania - franchise_meta: dict - players_rl_meta: dict - players_tm_meta: dict - fm: dict - gms: list[dict] - agms: list[dict] - captains: list[dict] - pr: dict - - @classmethod - def from_sprocket_links(cls, - meta_data: dict, - sprocket_data) -> Self: - """compile franchise from sprocket links data - - Args: - meta_data (dict): sprocket dict of the franchise to create - sprocket_data (SprocketLinks): SprocketLinks class - - Returns: - Self: Franchise class - """ - - def _create_team(skill_group, - sprocket_data, - players, - usages) -> list[PlayerRL]: - return [PlayerRL(x, - sprocket_data.trackers.from_hash(x['member_id']), - next((y for y in usages if y['role'] == x['slot'] and y['league'] == x['skill_group']), None)) - for x in players if x['skill_group'] == skill_group and x['slot'] != 'NONE'] - - # gather rl data - p_rl = [x for x in sprocket_data.players.data if x['franchise'] == meta_data['Franchise']] - u_rl = [x for x in sprocket_data.usages.data if x['team_name'] == meta_data['Franchise']] - - rl_team = TeamRocketLeague( - pl=MLEFranchiseTeam(_create_team( - const.SPR_SG_PL, sprocket_data, p_rl, u_rl)), - ml=MLEFranchiseTeam(_create_team( - const.SPR_SG_ML, sprocket_data, p_rl, u_rl)), - cl=MLEFranchiseTeam(_create_team( - const.SPR_SG_CL, sprocket_data, p_rl, u_rl)), - al=MLEFranchiseTeam(_create_team( - const.SPR_SG_AL, sprocket_data, p_rl, u_rl)), - fl=MLEFranchiseTeam(_create_team( - const.SPR_SG_FL, sprocket_data, p_rl, u_rl)), - ) - - # gather tm data - p_tm = [] - - tm_team = TeamTrackmania( - cl=MLEFranchiseTeam([]), - al=MLEFranchiseTeam([]) - ) - - # compile - return cls( - players_rl=rl_team, - players_tm=tm_team, - players_rl_meta=p_rl, - players_tm_meta=p_tm, - franchise_meta=meta_data, - fm=next( - (x for x in p_rl if x['Franchise Staff Position'] == 'Franchise Manager'), None), - gms=[x for x in p_rl if x['Franchise Staff Position'] - == 'General Manager'], - agms=[x for x in p_rl if x['Franchise Staff Position'] - == 'Assistant General Manager'], - captains=[ - x for x in p_rl if x['Franchise Staff Position'] == 'Captain'], - pr=[x for x in p_rl if x['Franchise Staff Position'] == 'PR Support'], - ) - - @property - def name(self) -> str: - """franchise name - - Returns: - str: franchise name - """ - return self.franchise_meta['Franchise'] - - def all_players(self) -> list[PlayerRL]: - """get all players - - Returns: - list[PlayerRL]: all players - """ - return self.players_rl.all_players().extend(self.players_tm.all_players()) - - def get_skill_group(self, - team: list[PlayerRL]) -> str | None: - """cheap and easy get of skill group - - Args: - team (list[PlayerRL]): team of players - - Returns: - str: skill group - """ - if len(team) != 0: - return team[0].player['skill_group'] - return None diff --git a/MLEBot/types/member.py b/MLEBot/types/member.py deleted file mode 100644 index e0b737e..0000000 --- a/MLEBot/types/member.py +++ /dev/null @@ -1,32 +0,0 @@ -"""MLE Sprocket 'Member' - """ - -from dataclasses import dataclass, field -from .player import PlayerRL - - -@dataclass -class Member: - """MLE Sprocket 'Member' - """ - member: dict | None = None - rl_player: PlayerRL = field(default_factory=PlayerRL()) - franchise: dict | None = None - - @property - def mle_id(self) -> str: - """member mle id - - Returns: - str: mle id - """ - return self.member['mle_id'] - - @property - def name(self) -> str: - """member name - - Returns: - str: name - """ - return self.member['name'] diff --git a/MLEBot/types/player.py b/MLEBot/types/player.py deleted file mode 100644 index c16e39b..0000000 --- a/MLEBot/types/player.py +++ /dev/null @@ -1,134 +0,0 @@ -"""MLE Sprocket Rocket League 'Player' - """ - -from dataclasses import dataclass - - -@dataclass -class PlayerRL: - """MLE Sprocket Rocket League 'Player' - """ - - player: dict | None = None - tracker: dict | None = None - usage: dict | None = None - - @property - def eligibility_date(self) -> str: - """scrim point elgibility date - - Returns: - str: eligible until: - """ - return self.player["Eligible Until"] - - @property - def eligible(self) -> bool: - """eligible to play - - Returns: - bool: eligible - """ - return self.scrim_points >= 30 - - @property - def franchise(self) -> str: - """player franchise - - Returns: - str: franchise - """ - return self.player["franchise"] - - @property - def league(self) -> str: - """player league (skill group) - - Returns: - str: skill group - """ - return self.skill_group - - @property - def name(self) -> str: - """player name - - Returns: - str: name - """ - return self.player["name"] - - @property - def salary(self) -> str: - """player salary - - Returns: - str: salary - """ - return self.player["salary"] - - @property - def scrim_points(self) -> int: - """scrim points - - Returns: - str: current scrim points - """ - return int(self.player["current_scrim_points"]) - - @property - def skill_group(self) -> str: - """skill group - - Returns: - str: skill group - """ - return self.player["skill_group"] - - @property - def slot(self) -> str: - """player slot - - Returns: - str: slot - """ - return self.player["slot"] - - @property - def staff_position(self) -> str: - """franchise staff position - - Returns: - str: franchise staff position - """ - return self.player["Franchise Staff Position"] - - @property - def usage_doubles(self) -> int: - """player league usages - doubles - - Returns: - str: doubles usage - """ - return int(self.usage["doubles_uses"]) - - @property - def usage_standard(self) -> int: - """player league usages - standard - - Returns: - int: standard usage - """ - return int(self.usage["standard_uses"]) - - @property - def usage_total(self) -> int: - """player league usages - total - - Returns: - int: total usage - """ - return int(self.usage['total_uses']) diff --git a/MLEBot/types/sprocket.py b/MLEBot/types/sprocket.py new file mode 100644 index 0000000..f67e55d --- /dev/null +++ b/MLEBot/types/sprocket.py @@ -0,0 +1,436 @@ +"""Minor League E-Sports Franchise and Team Dataclasses + """ +from __future__ import annotations + + +from dataclasses import dataclass, field +from typing import Self + + +from .url_datalink import UrlDataLink +from .. import const + + +@dataclass +class PlayerRL: + """MLE Sprocket Rocket League 'Player' + """ + + player: dict | None = None + tracker: dict | None = None + usage: dict | None = None + + @property + def eligibility_date(self) -> str: + """scrim point elgibility date + + Returns: + str: eligible until: + """ + return self.player["Eligible Until"] + + @property + def eligible(self) -> bool: + """eligible to play + + Returns: + bool: eligible + """ + return self.scrim_points >= 30 + + @property + def franchise(self) -> str: + """player franchise + + Returns: + str: franchise + """ + return self.player["franchise"] + + @property + def league(self) -> str: + """player league (skill group) + + Returns: + str: skill group + """ + return self.skill_group + + @property + def name(self) -> str: + """player name + + Returns: + str: name + """ + return self.player["name"] + + @property + def salary(self) -> str: + """player salary + + Returns: + str: salary + """ + return self.player["salary"] + + @property + def scrim_points(self) -> int: + """scrim points + + Returns: + str: current scrim points + """ + return int(self.player["current_scrim_points"]) + + @property + def skill_group(self) -> str: + """skill group + + Returns: + str: skill group + """ + return self.player["skill_group"] + + @property + def slot(self) -> str: + """player slot + + Returns: + str: slot + """ + return self.player["slot"] + + @property + def staff_position(self) -> str: + """franchise staff position + + Returns: + str: franchise staff position + """ + return self.player["Franchise Staff Position"] + + @property + def usage_doubles(self) -> int: + """player league usages + doubles + + Returns: + str: doubles usage + """ + return int(self.usage["doubles_uses"]) + + @property + def usage_standard(self) -> int: + """player league usages + standard + + Returns: + int: standard usage + """ + return int(self.usage["standard_uses"]) + + @property + def usage_total(self) -> int: + """player league usages + total + + Returns: + int: total usage + """ + return int(self.usage['total_uses']) + + +@dataclass +class Member: + """MLE Sprocket 'Member' + """ + member: dict | None = None + rl_player: PlayerRL = field(default_factory=PlayerRL()) + franchise: dict | None = None + + @property + def mle_id(self) -> str: + """member mle id + + Returns: + str: mle id + """ + return self.member['mle_id'] + + @property + def name(self) -> str: + """member name + + Returns: + str: name + """ + return self.member['name'] + + +class MLEFranchiseTeam(list[PlayerRL]): + """sub team for MLE franchises (think 'ML' or 'AL') + inherits list properties, used to add functions + """ + + def slot_sorted_players(self): + """get all players sorted by slot + """ + return sorted(self, + key=lambda x: x.slot) + + @classmethod + def from_sprocket_data(cls, + players: list[dict], + trackers, + usages: list[dict]) -> Self: + """compile list of PlayerRL from provided sprocket data + consider hashing usages with role-league-team is the key or something? + + Args: + players (list[dict]): sprocket players dict + trackers (UrlDataLink): trackers link + usages (list[dict]): usages dict + + Returns: + Self: this class appended with a list of PlayerRL for this franchise + """ + team = cls() + team.extend([PlayerRL(x, + trackers.from_hash(x['member_id']), + next((y for y in usages if y['role'] == x['slot']), None)) + for x in players]) + return team + + +@dataclass +class TeamRocketLeague: + """Minor League E-Sports Rocket League Franchise Team + """ + pl: MLEFranchiseTeam + ml: MLEFranchiseTeam + cl: MLEFranchiseTeam + al: MLEFranchiseTeam + fl: MLEFranchiseTeam + + def all_players(self): + """get all players for this team + + Returns: + list[PlayerRL]: all players + """ + return self.pl + self.ml + self.cl + self.al + self.fl + + def by_skill_group(self, + skill_group: str) -> MLEFranchiseTeam: + """get franchise team by skill group string + + Args: + skill_group (str): sprocket const skill group + + Returns: + MLEFranchiseTeam: team + """ + match skill_group: + case const.SPR_SG_PL: + return self.pl + case const.SPR_SG_ML: + return self.ml + case const.SPR_SG_CL: + return self.cl + case const.SPR_SG_AL: + return self.al + case const.SPR_SG_FL: + return self.fl + case _: + return None + + +@dataclass +class TeamTrackmania: + """Minor League E-Sports Trackmania Franchise Team + """ + cl: MLEFranchiseTeam + al: MLEFranchiseTeam + + def all_players(self): + """get all players for this team + + Returns: + list[PlayerRL]: all players + """ + return self.cl + self.al + + +@dataclass +class Franchise: + """Minor League E-Sports Franchise Dataclass + """ + players_rl: TeamRocketLeague + players_tm: TeamTrackmania + franchise_meta: dict + players_rl_meta: dict + players_tm_meta: dict + fm: dict + gms: list[dict] + agms: list[dict] + captains: list[dict] + pr: dict + + @classmethod + def from_sprocket_links(cls, + meta_data: dict, + sprocket_data) -> Self: + """compile franchise from sprocket links data + + Args: + meta_data (dict): sprocket dict of the franchise to create + sprocket_data (SprocketLinks): SprocketLinks class + + Returns: + Self: Franchise class + """ + + # do data gathering here to keep the local def easy to read + # gather rl data + p_rl = [x for x in sprocket_data.players.data if x['franchise'] == meta_data['Franchise']] + p_pl = [x for x in p_rl if x['skill_group'] == const.SPR_SG_PL and x['slot'] != 'NONE'] + p_ml = [x for x in p_rl if x['skill_group'] == const.SPR_SG_ML and x['slot'] != 'NONE'] + p_cl = [x for x in p_rl if x['skill_group'] == const.SPR_SG_CL and x['slot'] != 'NONE'] + p_al = [x for x in p_rl if x['skill_group'] == const.SPR_SG_AL and x['slot'] != 'NONE'] + p_fl = [x for x in p_rl if x['skill_group'] == const.SPR_SG_FL and x['slot'] != 'NONE'] + + # usage data + u_rl = [x for x in sprocket_data.usages.data if x['team_name'] == meta_data['Franchise']] + u_pl = [x for x in u_rl if x['league'].lower() in const.SPR_SG_PL.lower()] + u_ml = [x for x in u_rl if x['league'].lower() in const.SPR_SG_ML.lower()] + u_cl = [x for x in u_rl if x['league'].lower() in const.SPR_SG_CL.lower()] + u_al = [x for x in u_rl if x['league'].lower() in const.SPR_SG_AL.lower()] + u_fl = [x for x in u_rl if x['league'].lower() in const.SPR_SG_FL.lower()] + + rl_team = TeamRocketLeague( + pl=MLEFranchiseTeam.from_sprocket_data(p_pl, + sprocket_data.trackers, + u_pl), + ml=MLEFranchiseTeam.from_sprocket_data(p_ml, + sprocket_data.trackers, + u_ml), + cl=MLEFranchiseTeam.from_sprocket_data(p_cl, + sprocket_data.trackers, + u_cl), + al=MLEFranchiseTeam.from_sprocket_data(p_al, + sprocket_data.trackers, + u_al), + fl=MLEFranchiseTeam.from_sprocket_data(p_fl, + sprocket_data.trackers, + u_fl), + ) + + # gather tm data + p_tm = [] + + tm_team = TeamTrackmania( + cl=MLEFranchiseTeam([]), + al=MLEFranchiseTeam([]) + ) + + # compile + return cls( + players_rl=rl_team, + players_tm=tm_team, + players_rl_meta=p_rl, + players_tm_meta=p_tm, + franchise_meta=meta_data, + fm=next((x for x in p_rl if x['Franchise Staff Position'] == 'Franchise Manager'), None), + gms=[x for x in p_rl if x['Franchise Staff Position'] == 'General Manager'], + agms=[x for x in p_rl if x['Franchise Staff Position'] == 'Assistant General Manager'], + captains=[x for x in p_rl if x['Franchise Staff Position'] == 'Captain'], + pr=[x for x in p_rl if x['Franchise Staff Position'] == 'PR Support'], + ) + + @property + def name(self) -> str: + """franchise name + + Returns: + str: franchise name + """ + return self.franchise_meta['Franchise'] + + def all_players(self) -> list[PlayerRL]: + """get all players + + Returns: + list[PlayerRL]: all players + """ + return self.players_rl.all_players().extend(self.players_tm.all_players()) + + def get_skill_group(self, + team: list[PlayerRL]) -> str | None: + """cheap and easy get of skill group + + Args: + team (list[PlayerRL]): team of players + + Returns: + str: skill group + """ + if len(team) != 0: + return team[0].player['skill_group'] + return None + + +class SprocketLinks: + """sprocket data links + """ + + def __init__(self): + self.members: UrlDataLink = UrlDataLink('members', const.SPR_DL_MEMBERS, const.SPR_HK_MEMBERS, + const.SPR_HK_MEMBERS_SECONDARY) + self.players: UrlDataLink = UrlDataLink( + 'players', const.SPR_DL_PLAYERS, const.SPR_HK_PLAYERS) + self.player_stats: UrlDataLink = UrlDataLink( + 'player_stats', const.SPR_DL_PLA_STATS, const.SPR_HK_PLA_STATS) + self.scrims: UrlDataLink = UrlDataLink( + 'scrims', const.SPR_DL_SCRIMS, const.SPR_HK_SCRIMS) + self.teams: UrlDataLink = UrlDataLink( + 'teams', const.SPR_DL_TEAMS, const.SPR_HK_TEAMS) + self.fixtures: UrlDataLink = UrlDataLink( + 'fixtures', const.SPR_DL_FIXT, const.SPR_HK_FIXT) + self.match_grps: UrlDataLink = UrlDataLink( + 'match_groups', const.SPR_DL_MAT_GRP, const.SPR_HK_MAT_GRP) + self.matches: UrlDataLink = UrlDataLink( + 'matches', const.SPR_DL_MATCHES, const.SPR_HK_MATCHES) + self.trackers: UrlDataLink = UrlDataLink( + 'trackers', const.SPR_DL_TRACKER, const.SPR_HK_TRACKER) + self.usages: UrlDataLink = UrlDataLink( + 'usages', const.SPR_DL_USAGE, const.SPR_HK_USAGE) + + self._franchises: dict = {} + + def compile_data(self) -> None: + """compile data (usually directly after updating all links) + """ + if not self.teams.data: + return + + for f in self.teams.data: + self._franchises[f['Franchise'].lower()] = Franchise.from_sprocket_links(f, + self) + + def franchise(self, + name: str) -> franchise.Franchise | None: + """get franchise by name + + Args: + name (str): franchise name + + Returns: + (Franchise | None): franchise or none + """ + return self._franchises.get(name.lower(), None) + + def links(self): + """get iterable links from this sprocket links class + """ + return [getattr(self, x) for x in dir(self) if isinstance(getattr(self, x), UrlDataLink)] diff --git a/MLEBot/types/sprocket_links.py b/MLEBot/types/sprocket_links.py deleted file mode 100644 index e7bebe3..0000000 --- a/MLEBot/types/sprocket_links.py +++ /dev/null @@ -1,62 +0,0 @@ -"""sprocket data links - """ - -from ..services import const -from . import franchise -from .url_datalink import UrlDataLink - - -class SprocketLinks: - """sprocket data links - """ - - def __init__(self): - self.members: UrlDataLink = UrlDataLink('members', const.SPR_DL_MEMBERS, const.SPR_HK_MEMBERS, - const.SPR_HK_MEMBERS_SECONDARY) - self.players: UrlDataLink = UrlDataLink( - 'players', const.SPR_DL_PLAYERS, const.SPR_HK_PLAYERS) - self.player_stats: UrlDataLink = UrlDataLink( - 'player_stats', const.SPR_DL_PLA_STATS, const.SPR_HK_PLA_STATS) - self.scrims: UrlDataLink = UrlDataLink( - 'scrims', const.SPR_DL_SCRIMS, const.SPR_HK_SCRIMS) - self.teams: UrlDataLink = UrlDataLink( - 'teams', const.SPR_DL_TEAMS, const.SPR_HK_TEAMS) - self.fixtures: UrlDataLink = UrlDataLink( - 'fixtures', const.SPR_DL_FIXT, const.SPR_HK_FIXT) - self.match_grps: UrlDataLink = UrlDataLink( - 'match_groups', const.SPR_DL_MAT_GRP, const.SPR_HK_MAT_GRP) - self.matches: UrlDataLink = UrlDataLink( - 'matches', const.SPR_DL_MATCHES, const.SPR_HK_MATCHES) - self.trackers: UrlDataLink = UrlDataLink( - 'trackers', const.SPR_DL_TRACKER, const.SPR_HK_TRACKER) - self.usages: UrlDataLink = UrlDataLink( - 'usages', const.SPR_DL_USAGE, const.SPR_HK_USAGE) - - self._franchises: dict = {} - - def compile_data(self) -> None: - """compile data (usually directly after updating all links) - """ - if not self.teams.data: - return - - for f in self.teams.data: - self._franchises[f['Franchise'].lower()] = franchise.Franchise.from_sprocket_links(f, - self) - - def franchise(self, - name: str) -> franchise.Franchise | None: - """get franchise by name - - Args: - name (str): franchise name - - Returns: - (Franchise | None): franchise or none - """ - return self._franchises.get(name.lower(), None) - - def links(self): - """get iterable links from this sprocket links class - """ - return [getattr(self, x) for x in dir(self) if isinstance(getattr(self, x), UrlDataLink)] diff --git a/MLEBot/types/test_types.py b/MLEBot/types/test_types.py new file mode 100644 index 0000000..682d8c3 --- /dev/null +++ b/MLEBot/types/test_types.py @@ -0,0 +1,20 @@ +"""test types for MLE Bot + """ +from __future__ import annotations + +import unittest +from .. import const +from ..types import UrlDataLink + + +class TestUrlDataLink(unittest.TestCase): + """test class url datalink + """ + + def test_build(self): + """test data link builds + """ + link = UrlDataLink('unit-test-link', + const.SPR_DL_TEAMS, + const.SPR_HK_TEAMS) + self.assertIsNotNone(link) diff --git a/MLEBot/types/url_datalink.py b/MLEBot/types/url_datalink.py index 99d193b..ea7aa60 100644 --- a/MLEBot/types/url_datalink.py +++ b/MLEBot/types/url_datalink.py @@ -1,11 +1,18 @@ """url data link to grab json data from remote server and store it """ +from __future__ import annotations + import json +from logging import Logger from typing import Any import requests -from pydiscobot.services.log import logger -from ..services.const import URL_REQ_TIMEOUT + + +from pydiscobot import log + + +URL_REQ_TIMEOUT = 30.0 class UrlDataLink: @@ -19,7 +26,6 @@ def __init__(self, url_link: str, hash_key: str | None = None, second_hash_key: str | None = None): - self.logger = logger(__name__+name) self._data = None self._hash_data = None self._secondary_hash_data = None @@ -28,6 +34,7 @@ def __init__(self, self._hash_key = hash_key self._secondary_hash_key = second_hash_key self._initialized: bool = False + self._logger = log.logger(self._name) @property def data(self) -> any: @@ -57,6 +64,10 @@ def json_link(self) -> str: """ return '.' + self._name + '.json' + @property + def logger(self) -> Logger: + return self._logger + def _clear(self) -> None: self._data = None self._hash_data = None