├── runtime.txt ├── Procfile ├── requirements.txt ├── assets ├── logo.png ├── standings-v2.png ├── help-command-v2.png ├── live-league-v2.png ├── invite-command-v2.png ├── league-fixtures-v2.png ├── standings-all-v2.png └── team-fixtures-v2.png ├── .gitignore ├── source ├── logos │ ├── BL.jpg │ ├── PL.jpg │ ├── SA.jpg │ ├── FL1.jpg │ └── SPA.jpg ├── league_code.py ├── exceptions.py ├── league_properties.py ├── team_id.py ├── teamcodes.json ├── bot_commands.py └── utils.py ├── LICENSE ├── bot.py └── README.md /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.8.3 -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python bot.py -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.32.0 2 | discord.py==1.3.4 3 | python-dotenv==0.14.0 -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaheshBharadwaj/Paneka-discord-bot/HEAD/assets/logo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | team-data.json 3 | team_mapper.py 4 | cache.json 5 | cache/ 6 | test*.py 7 | .env -------------------------------------------------------------------------------- /source/logos/BL.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaheshBharadwaj/Paneka-discord-bot/HEAD/source/logos/BL.jpg -------------------------------------------------------------------------------- /source/logos/PL.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaheshBharadwaj/Paneka-discord-bot/HEAD/source/logos/PL.jpg -------------------------------------------------------------------------------- /source/logos/SA.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaheshBharadwaj/Paneka-discord-bot/HEAD/source/logos/SA.jpg -------------------------------------------------------------------------------- /source/logos/FL1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaheshBharadwaj/Paneka-discord-bot/HEAD/source/logos/FL1.jpg -------------------------------------------------------------------------------- /source/logos/SPA.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaheshBharadwaj/Paneka-discord-bot/HEAD/source/logos/SPA.jpg -------------------------------------------------------------------------------- /assets/standings-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaheshBharadwaj/Paneka-discord-bot/HEAD/assets/standings-v2.png -------------------------------------------------------------------------------- /assets/help-command-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaheshBharadwaj/Paneka-discord-bot/HEAD/assets/help-command-v2.png -------------------------------------------------------------------------------- /assets/live-league-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaheshBharadwaj/Paneka-discord-bot/HEAD/assets/live-league-v2.png -------------------------------------------------------------------------------- /assets/invite-command-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaheshBharadwaj/Paneka-discord-bot/HEAD/assets/invite-command-v2.png -------------------------------------------------------------------------------- /assets/league-fixtures-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaheshBharadwaj/Paneka-discord-bot/HEAD/assets/league-fixtures-v2.png -------------------------------------------------------------------------------- /assets/standings-all-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaheshBharadwaj/Paneka-discord-bot/HEAD/assets/standings-all-v2.png -------------------------------------------------------------------------------- /assets/team-fixtures-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaheshBharadwaj/Paneka-discord-bot/HEAD/assets/team-fixtures-v2.png -------------------------------------------------------------------------------- /source/league_code.py: -------------------------------------------------------------------------------- 1 | LEAGUE_CODE = { 2 | "BSA": 2013, #Brazilian First Division 3 | "BL": 2002, #Bundesliga 4 | "FL1": 2015, #Ligue 1 5 | "PL": 2021, #Premier League 6 | "ECL": 2016, #English Championship 7 | "SPA": 2014, #La Liga 8 | "SA": 2019, #Serie A 9 | "PPL": 2017, #Portugese First Dvision 10 | "ERD": 2003, #Eredivisie [Dutch] 11 | "CL": 2001 #Champions League 12 | } -------------------------------------------------------------------------------- /source/exceptions.py: -------------------------------------------------------------------------------- 1 | class InvalidLimitException(Exception): 2 | """ 3 | Invalid number of matches requested 4 | """ 5 | pass 6 | 7 | class InvalidLeagueCodeException(Exception): 8 | """ 9 | The League code requested is either invalid or not supported 10 | """ 11 | pass 12 | 13 | class InvalidTeamCodeException(Exception): 14 | """ 15 | The Team Code requested is invalid 16 | """ 17 | pass 18 | 19 | 20 | -------------------------------------------------------------------------------- /source/league_properties.py: -------------------------------------------------------------------------------- 1 | LEAGUE_PROPERTIES = { 2 | "PL": { 3 | "rl": [18, 20], 4 | "cl": [1, 4], 5 | "el": [5, 5], 6 | }, 7 | "ECL": { 8 | "rl": [21, 24], 9 | "cl": [1, 2], 10 | "el": [3, 6] 11 | }, 12 | 13 | "BL": { 14 | "rl": [16, 18], 15 | "cl": [1, 4], 16 | "el": [5, 6] 17 | }, 18 | 19 | "SPA": { 20 | "rl": [18, 20], 21 | "cl": [1, 3], 22 | "el": [4, 6] 23 | }, 24 | 25 | 26 | "SA": { 27 | "rl": [18, 20], 28 | "cl": [1, 3], 29 | "el": [4, 5] 30 | }, 31 | "PPL": { 32 | "rl": [17, 18], 33 | "cl": [1, 3], 34 | "el": [4, 5] 35 | }, 36 | "FL1": { 37 | "rl": [19, 20], 38 | "cl": [1, 3], 39 | "el": [4, 4] 40 | }, 41 | 42 | "ERD": { 43 | "rl": [16, 18], 44 | "cl": [1, 2], 45 | "el": [3, 3] 46 | }, 47 | "BSA": { 48 | "rl": [16, 20], 49 | "cl": [0, 0], 50 | "el": [0, 0] 51 | } 52 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mahesh Bharadwaj K 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /source/team_id.py: -------------------------------------------------------------------------------- 1 | TEAM_ID = { 2 | "BAY": "5", 3 | "HSV": "7", 4 | "FCA": "16", 5 | "BSC": "9", 6 | "B04": "3", 7 | "TSG": "2", 8 | "DAR": "55", 9 | "H96": "8", 10 | "M05": "15", 11 | "FCI": "31", 12 | "SVW": "12", 13 | "S04": "6", 14 | "BVB": "4", 15 | "BMG": "18", 16 | "WOB": "11", 17 | "SGE": "19", 18 | "VFB": "10", 19 | "FCK": "1", 20 | "PFC": "1045", 21 | "EVA": "513", 22 | "FCM": "545", 23 | "RCL": "546", 24 | "SUSFC": "356", 25 | "PSV": "674", 26 | "ROM": "100", 27 | "JUVE": "109", 28 | "PAL": "114", 29 | "GEN": "107", 30 | "SASS": "471", 31 | "SSC": "113", 32 | "LAZ": "110", 33 | "INT": "108", 34 | "FCT": "586", 35 | "FIO": "99", 36 | "ACM": "98", 37 | "EMP": "445", 38 | "KAI": "13", 39 | "EBS": "33", 40 | "SVS": "46", 41 | "SCF": "17", 42 | "FCN": "14", 43 | "FSV": "30", 44 | "RBL": "721", 45 | "GRE": "21", 46 | "KAR": "32", 47 | "HEI": "44", 48 | "1860": "26", 49 | "PAD": "29", 50 | "VFL": "36", 51 | "FCP": "503", 52 | "FCU": "28", 53 | "FOR": "24", 54 | "MUFC": "66", 55 | "THFC": "73", 56 | "AFCB": "1044", 57 | "AVFC": "58", 58 | "EFC": "62", 59 | "WAT": "346", 60 | "LCFC": "338", 61 | "SUN": "71", 62 | "NCFC": "68", 63 | "CRY": "354", 64 | "CFC": "61", 65 | "SWA": "72", 66 | "NUFC": "67", 67 | "SFC": "340", 68 | "AFC": "57", 69 | "WHU": "563", 70 | "SCFC": "70", 71 | "LFC": "64", 72 | "WBA": "74", 73 | "MCFC": "65", 74 | "MFF": "749", 75 | "ASTA": "1056", 76 | "GSK": "610", 77 | "CSK": "751", 78 | "SHA": "724", 79 | "ZEN": "731", 80 | "DYK": "842", 81 | "MTA": "971", 82 | "OLA": "654", 83 | "DIN": "755", 84 | "AUE": "22", 85 | "VFR": "50", 86 | "OSC": "521", 87 | "PSG": "524", 88 | "MAR": "516", 89 | "SMC": "514", 90 | "NIC": "522", 91 | "MON": "548", 92 | "NAN": "543", 93 | "GUI": "538", 94 | "MHSC": "518", 95 | "SCB": "536", 96 | "REN": "529", 97 | "BOR": "526", 98 | "REI": "547", 99 | "TOU": "511", 100 | "ETI": "527", 101 | "OLY": "523", 102 | "LOR": "525", 103 | "CFE": "285", 104 | "UDA": "267", 105 | "CCF": "295", 106 | "LAC": "560", 107 | "RSS": "92", 108 | "ESP": "80", 109 | "FCG": "82", 110 | "ATM": "78", 111 | "RAY": "87", 112 | "VAL": "95", 113 | "MAL": "84", 114 | "SEV": "559", 115 | "BIL": "77", 116 | "FCB": "81", 117 | "MAD": "86", 118 | "LUD": "88", 119 | "VIG": "558", 120 | "BET": "90", 121 | "VCF": "94", 122 | "GCF": "83", 123 | "EIB": "278", 124 | "SCP": "498", 125 | "SLB": "495" 126 | } -------------------------------------------------------------------------------- /source/teamcodes.json: -------------------------------------------------------------------------------- 1 | { 2 | "FC Bayern M\u00fcnchen": "BAY", 3 | "Hamburger SV": "HSV", 4 | "FC Augsburg": "FCA", 5 | "Hertha BSC": "BSC", 6 | "Bayer Leverkusen": "B04", 7 | "TSG 1899 Hoffenheim": "TSG", 8 | "SV Darmstadt 98": "DAR", 9 | "Hannover 96": "H96", 10 | "1. FSV Mainz 05": "M05", 11 | "FC Ingolstadt 04": "FCI", 12 | "Werder Bremen": "SVW", 13 | "FC Schalke 04": "S04", 14 | "Borussia Dortmund": "BVB", 15 | "Bor. M\u00f6nchengladbach": "BMG", 16 | "VfL Wolfsburg": "WOB", 17 | "Eintracht Frankfurt": "SGE", 18 | "VfB Stuttgart": "VFB", 19 | "1. FC K\u00f6ln": "FCK", 20 | "Paris FC": "PFC", 21 | "\u00c9vian Thonon Gaillard FC": "EVA", 22 | "FC Metz": "FCM", 23 | "RC Lens": "RCL", 24 | "Sheffield United FC": "SUSFC", 25 | "PSV Eindhoven": "PSV", 26 | "AS Roma": "ROM", 27 | "Juventus Turin": "JUVE", 28 | "US Citt\u00e1 di Palermo": "PAL", 29 | "Genoa CFC": "GEN", 30 | "US Sassuolo Calcio": "SASS", 31 | "SSC Napoli": "SSC", 32 | "SS Lazio": "LAZ", 33 | "FC Internazionale Milano": "INT", 34 | "Torino FC": "FCT", 35 | "ACF Fiorentina": "FIO", 36 | "AC Milan": "ACM", 37 | "Empoli FC": "EMP", 38 | "1. FC Kaiserslautern": "KAI", 39 | "Eintracht Braunschweig": "EBS", 40 | "SV Sandhausen": "SVS", 41 | "SC Freiburg": "SCF", 42 | "1. FC N\u00fcrnberg": "FCN", 43 | "FSV Frankfurt": "FSV", 44 | "Red Bull Leipzig": "RBL", 45 | "SpVgg Greuther F\u00fcrth": "GRE", 46 | "Karlsruher SC": "KAR", 47 | "1. FC Heidenheim 1846": "HEI", 48 | "TSV 1860 M\u00fcnchen": "1860", 49 | "SC Paderborn 07": "PAD", 50 | "VfL Bochum": "VFL", 51 | "FC St. Pauli": "FCP", 52 | "1. FC Union Berlin": "FCU", 53 | "Fortuna D\u00fcsseldorf": "FOR", 54 | "Manchester United FC": "MUFC", 55 | "Tottenham Hotspur FC": "THFC", 56 | "AFC Bournemouth": "AFCB", 57 | "Aston Villa FC": "AVFC", 58 | "Everton FC": "EFC", 59 | "Watford FC": "WAT", 60 | "Leicester City FC": "LCFC", 61 | "Sunderland AFC": "SUN", 62 | "Norwich City FC": "NCFC", 63 | "Crystal Palace FC": "CRY", 64 | "Chelsea FC": "CFC", 65 | "Swansea City FC": "SWA", 66 | "Newcastle United FC": "NUFC", 67 | "Southampton FC": "SFC", 68 | "Arsenal FC": "AFC", 69 | "West Ham United FC": "WHU", 70 | "Stoke City FC": "SCFC", 71 | "Liverpool FC": "LFC", 72 | "West Bromwich Albion FC": "WBA", 73 | "Manchester City FC": "MCFC", 74 | "Malm\u00f6 FF": "MFF", 75 | "FC Astana": "ASTA", 76 | "Galatasaray SK": "GSK", 77 | "CSKA Moscow": "CSK", 78 | "Shakhtar Donetsk": "SHA", 79 | "FC Zenit St. Petersburg": "ZEN", 80 | "Dynamo Kyiv": "DYK", 81 | "Maccabi Tel Aviv": "MTA", 82 | "Olympiacos F.C.": "OLA", 83 | "GNK Dinamo Zagreb": "DIN", 84 | "Erzgebirge Aue": "AUE", 85 | "VfR Aalen": "VFR", 86 | "OSC Lille": "OSC", 87 | "Paris Saint-Germain": "PSG", 88 | "Olympique de Marseille": "MAR", 89 | "SM Caen": "SMC", 90 | "OGC Nice": "NIC", 91 | "AS Monaco FC": "MON", 92 | "FC Nantes": "NAN", 93 | "EA Guingamp": "GUI", 94 | "Montpellier H\u00e9rault SC": "MHSC", 95 | "SC Bastia": "SCB", 96 | "Stade Rennais FC": "REN", 97 | "FC Girondins de Bordeaux": "BOR", 98 | "Stade de Reims": "REI", 99 | "Toulouse FC": "TOU", 100 | "AS Saint-\u00c9tienne": "ETI", 101 | "Olympique Lyonnais": "OLY", 102 | "FC Lorient": "LOR", 103 | "Elche FC": "CFE", 104 | "UD Almeria": "UDA", 105 | "C\u00f3rdoba CF": "CCF", 106 | "RC Deportivo La Coruna": "LAC", 107 | "Real Sociedad de F\u00fatbol": "RSS", 108 | "RCD Espanyol": "ESP", 109 | "Getafe CF": "FCG", 110 | "Club Atl\u00e9tico de Madrid": "ATM", 111 | "Rayo Vallecano de Madrid": "RAY", 112 | "Valencia CF": "VAL", 113 | "M\u00e1laga CF": "MAL", 114 | "Sevilla FC": "SEV", 115 | "Athletic Club": "BIL", 116 | "FC Barcelona": "FCB", 117 | "Real Madrid CF": "MAD", 118 | "Levante UD": "LUD", 119 | "RC Celta de Vigo": "VIG", 120 | "Real Betis": "BET", 121 | "Villarreal CF": "VCF", 122 | "Granada CF": "GCF", 123 | "SD Eibar": "EIB", 124 | "Sporting CP": "SCP", 125 | "FC Porto": "FCP", 126 | "SL Benfica": "SLB" 127 | } -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | # bot.py 2 | import os 3 | import random 4 | 5 | import source.bot_commands as bot_commands 6 | 7 | import discord 8 | from discord.ext import commands 9 | from dotenv import load_dotenv 10 | from source.utils import fetchImage 11 | 12 | load_dotenv('.env') 13 | TOKEN = os.getenv('DISCORD_TOKEN') 14 | 15 | bot = commands.Bot(command_prefix='.') 16 | bot.remove_command('help') 17 | 18 | 19 | @bot.event 20 | async def on_ready(): 21 | print("Bot Running!") 22 | await bot.change_presence( 23 | activity=discord.Activity( 24 | type=discord.ActivityType.listening, 25 | name=".help on " + str(len(bot.guilds)) + " server(s)") 26 | ) 27 | 28 | 29 | @bot.event 30 | async def on_guild_join(guild): 31 | for channel in guild.text_channels: 32 | if channel.permissions_for(guild.me).send_messages: 33 | helpEmbed = bot_commands.getHelpEmbed() 34 | await channel.send('Hey There! Just got added to this channel!\ 35 | \nBasic commands are listed below :)', embed=helpEmbed) 36 | break 37 | 38 | 39 | @bot.command(name='99', help='Responds with a random quote from Brooklyn 99') 40 | async def nine_nine(ctx): 41 | brooklyn_99_quotes = [ 42 | 'I\'m the human form of the 💯 emoji.', 43 | 'Bingpot!', 44 | 'Cool. Cool cool cool cool cool cool cool,\ 45 | \nno doubt no doubt no doubt no doubt.', 46 | 'If I die, turn my tweets into a book', 47 | 'Captain Wuntch. Good to see you. But if you’re here, who’s guarding Hades?', 48 | 'Anyone over the age of six celebrating a birthday should go to hell.' 49 | ] 50 | 51 | response = random.choice(brooklyn_99_quotes) 52 | await ctx.send(response) 53 | 54 | # Requires admin 55 | @bot.command(name='update') 56 | async def update(ctx, message = None): 57 | if ctx.message.author.id == 694128831291981844: 58 | for guild in bot.guilds: 59 | for channel in guild.text_channels: 60 | if channel.permissions_for(guild.me).send_messages: 61 | helpEmbed = bot_commands.getHelpEmbed() 62 | await channel.send(message, embed=helpEmbed) 63 | break 64 | 65 | 66 | @bot.command(name='standings-all', help='Displays standings with all details') 67 | async def standingsAll(ctx, arg=''): 68 | text = bot_commands.getStandings(arg.upper(), mode='all') 69 | if text is not None: 70 | await ctx.send(text) 71 | else: 72 | leagueCodeEmbed = bot_commands.getLeagueCodes( 73 | 'Invalid League Code Entered!') 74 | await ctx.send(embed=leagueCodeEmbed) 75 | 76 | 77 | @bot.command(name='standings', help='Display Standings with only Matches played & points') 78 | async def standings(ctx, arg=''): 79 | text = bot_commands.getStandings(arg.upper(), mode='long') 80 | if text is not None: 81 | await ctx.send(text) 82 | else: 83 | leagueCodeEmbed = bot_commands.getLeagueCodes( 84 | 'Invalid League Code Entered!') 85 | await ctx.send(embed=leagueCodeEmbed) 86 | 87 | 88 | @bot.command(name='fixtures', alias=['matches', 'm', 'f']) 89 | async def fixtures(ctx, code = '', limit=5): 90 | fixturesEmbed = bot_commands.getFixtures(code.upper(), limit) 91 | fixturesEmbed.set_footer(text='Requested By: ' + str(ctx.author)) 92 | 93 | path = fetchImage(code.upper()) 94 | if path is not None: 95 | fixturesEmbed.set_thumbnail(url='attachment://image.jpg') 96 | await ctx.send(embed=fixturesEmbed, file=discord.File(path, 'image.jpg')) 97 | else: 98 | await ctx.send(embed=fixturesEmbed) 99 | 100 | @bot.command(name='live', aliases=['l']) 101 | async def matches(ctx, code='', limit=5): 102 | liveMatchesEmbed = bot_commands.getMatches(code.upper(), limit) 103 | liveMatchesEmbed.set_footer(text='Requested By: ' + str(ctx.author)) 104 | 105 | path = fetchImage(code.upper()) 106 | if path is not None: 107 | liveMatchesEmbed.set_thumbnail(url='attachment://image.jpg') 108 | await ctx.send(embed=liveMatchesEmbed, file=discord.File(path, 'image.jpg')) 109 | else: 110 | await ctx.send(embed=liveMatchesEmbed) 111 | 112 | @bot.command(name='league-codes') 113 | async def leagueCodes(ctx): 114 | leagueCodesEmbed = bot_commands.getLeagueCodes() 115 | leagueCodesEmbed.set_footer(text='Requested By: ' + str(ctx.author)) 116 | await ctx.send(embed=leagueCodesEmbed) 117 | 118 | @bot.command(name='team-codes') 119 | async def teamCodes(ctx): 120 | teamCodesEmbed = bot_commands.getTeamCodes() 121 | teamCodesEmbed.set_footer(text='Requested By: ' + str(ctx.author)) 122 | await ctx.send(embed=teamCodesEmbed) 123 | 124 | @bot.command(name='invite') 125 | async def invite(ctx): 126 | inviteEmbed = bot_commands.getInviteEmbed(ctx) 127 | await ctx.author.send(embed=inviteEmbed) 128 | await ctx.send(f'The invite link has been sent to your DM {ctx.author.mention} :D') 129 | 130 | @bot.command(name='help') 131 | async def help(ctx): 132 | helpEmbed = bot_commands.getHelpEmbed(ctx) 133 | await ctx.send(embed=helpEmbed) 134 | 135 | bot.run(TOKEN) -------------------------------------------------------------------------------- /source/bot_commands.py: -------------------------------------------------------------------------------- 1 | import discord 2 | import requests 3 | import json 4 | import os 5 | 6 | from dotenv import load_dotenv 7 | from source.utils import putTableAll, putTableLong, putFixtures, fetchJSON, putMatches 8 | from source.league_code import LEAGUE_CODE 9 | from source.team_id import TEAM_ID 10 | from source.exceptions import * 11 | 12 | 13 | def getStandings(code, mode='long'): 14 | """ 15 | Function that delivers standings in text format. 16 | Queries the cache for the requested data, if not found, 17 | Loads the data from API and caches it 18 | 19 | Parameters: 20 | ----------- 21 | code: str 22 | The ID of the league for which standings are required 23 | mode: 'long' or 'all', optional 24 | * defaults to 'long' 25 | * 'long' -> SNO, Team name, Matches Played, Points Obtained 26 | * 'all' -> SNO, Team Code, Matches Played, Won, Drawn, Lost, Pts, Goal Difference 27 | 28 | Returns: 29 | -------- 30 | str 31 | standings if code is valid, or an error message 32 | 33 | """ 34 | 35 | try: 36 | if code not in LEAGUE_CODE: 37 | raise InvalidLeagueCodeException 38 | 39 | obj = fetchJSON(code, 'standings') 40 | if mode == 'all': 41 | return putTableAll(obj) 42 | return putTableLong(obj) 43 | 44 | except InvalidLeagueCodeException: 45 | return None 46 | 47 | 48 | def getFixtures(code, limit: int): 49 | """ 50 | Displays the fixtures in the requested league / team as an embed 51 | Fetches fixtures from JSON file and renders embed for it, 52 | Displays 'limit' matches 53 | 54 | Parameters: 55 | ----------- 56 | code: str 57 | The ID of the league or team for which fixtures are required 58 | limit: int, optional 59 | Number of fixtures to display (default value of 5) 60 | 61 | Returns: 62 | -------- 63 | discord.Embed 64 | Shows the fixtures as many as requested, 65 | Incase of invalid code, relevant help embed is returned 66 | 67 | """ 68 | try: 69 | if limit < 0: 70 | raise InvalidLimitException 71 | 72 | mode = 'league' 73 | if code not in LEAGUE_CODE: 74 | if code in TEAM_ID: 75 | mode = 'team' 76 | else: 77 | return discord.Embed(title='Please enter a valid code!', 78 | description='Please Refer to **.team-codes** for team codes\ 79 | \nAnd **.league-codes** for league-codes', 80 | color=0xf58300) 81 | 82 | obj = fetchJSON(code, 'fixtures') 83 | return putFixtures(obj, code, limit, mode) 84 | 85 | except InvalidLimitException: 86 | return discord.Embed(title='Limit must be greater than :zero:', 87 | description="Enter a valid limit :smile:", 88 | color=0xf58300) 89 | 90 | 91 | def getMatches(code, limit: int): 92 | try: 93 | if limit < 0: 94 | raise InvalidLimitException 95 | 96 | mode = 'league' 97 | if code not in LEAGUE_CODE: 98 | if code in TEAM_ID: 99 | mode = 'team' 100 | else: 101 | return discord.Embed(title='Please enter a valid code!', 102 | description='Please Refer to **.team-codes** for team codes\ 103 | \nAnd **.league-codes** for league-codes', 104 | color=0xf58300) 105 | 106 | obj = fetchJSON(code, 'live') 107 | return putMatches(obj, code, limit, mode) 108 | 109 | except InvalidLimitException: 110 | return discord.Embed(title='Limit must be greater than :zero:', 111 | description="Enter a valid limit :smile:", 112 | color=0xf58300) 113 | 114 | 115 | def getLeagueCodes(title="League Codes"): 116 | """ 117 | Returns Leagues and their codes as an Embed 118 | 119 | Parameters: 120 | ----------- 121 | title: str, optional 122 | Title of the embed (by default: "League Codes") 123 | 124 | Returns: 125 | -------- 126 | discord.Embed 127 | Embed displaying league codes 128 | 129 | """ 130 | 131 | embed = discord.Embed( 132 | title=title, 133 | description="Refer codes for Top :five: Leagues here:", 134 | color=0xf58300) 135 | embed.add_field(name=':one: Premier League', 136 | value='PL' + "\n\u200b", inline=False) 137 | embed.add_field(name=':two: La Liga', value='SPA' + 138 | "\n\u200b", inline=True) 139 | embed.add_field(name=':three: Serie A', value='SA' + 140 | "\n\u200b", inline=False) 141 | embed.add_field(name=':four: Bundesliga', 142 | value='BA' + "\n\u200b", inline=True) 143 | embed.add_field(name=':five:Ligue 1', value='FL1', inline=False) 144 | embed.add_field(name='For more leagues', 145 | value='click [Here](https://github.com/MaheshBharadwaj/paneka/blob/master/README.md/#league-codes)') 146 | return embed 147 | 148 | 149 | def getTeamCodes(title="Team Codes"): 150 | """ 151 | Returns Teams and their codes as an Embed 152 | 153 | Parameters: 154 | ----------- 155 | title: str, optional 156 | Title of the embed (by default: "Team Codes") 157 | 158 | Returns: 159 | -------- 160 | discord.Embed 161 | Embed displaying team codes 162 | 163 | """ 164 | 165 | embed = discord.Embed( 166 | title=title, 167 | description="Refer codes for Top :one: :zero: Teams here:", 168 | color=0xf58300) 169 | embed.add_field(name='Real Madrid', value='MAD' + "\n\u200b", inline=True) 170 | embed.add_field(name='FC Barcelona', value='FCB' + "\n\u200b", inline=True) 171 | embed.add_field(name='Manchester United', 172 | value='MUFC' + "\n\u200b", inline=True) 173 | embed.add_field(name='Arsenal', value='AFC' + "\n\u200b", inline=True) 174 | embed.add_field(name='Bayern Munich', value='BAY' + 175 | "\n\u200b", inline=True) 176 | embed.add_field(name='Chelsea', value='CFC' + "\n\u200b", inline=True) 177 | embed.add_field(name='Juventus', value='JUVE' + "\n\u200b", inline=True) 178 | embed.add_field(name='Atletico Madrid', 179 | value='ATM' + "\n\u200b", inline=True) 180 | embed.add_field(name='Liverpool', value='LFC' + "\n\u200b", inline=True) 181 | embed.add_field(name='Manche$ter City', value='MCFC', inline=True) 182 | return embed 183 | 184 | 185 | def getInviteEmbed(ctx): 186 | """ 187 | Generates Invite embed to invite bot 188 | 189 | Parameters: 190 | ----------- 191 | ctx: discord.Context 192 | Context data passed by discord when a command is invoked 193 | 194 | Returns: 195 | -------- 196 | discord.Embed 197 | Showing invite URL for the bot 198 | 199 | """ 200 | inviteEmbed = discord.Embed( 201 | title='Invite link!', 202 | description='URL for inviting bot to your servers' 203 | ) 204 | 205 | inviteEmbed.add_field( 206 | name=":warning: You need to be an admin to add bots :slight_smile:", 207 | value="https://discord.com/api/oauth2/authorize?client_id=731544990446256198&permissions=60416&scope=bot" 208 | ) 209 | 210 | return inviteEmbed 211 | 212 | 213 | def getHelpEmbed(ctx=None): 214 | """ 215 | Generates the 'Help' embed when requested 216 | 217 | Parameters: 218 | ----------- 219 | ctx: discord.Context 220 | Context data passed by discord when a command is invoked 221 | 222 | Returns: 223 | -------- 224 | discord.Embed 225 | Showing help data for the commands available 226 | 227 | """ 228 | embed = discord.Embed( 229 | title="Paneka-Help!", 230 | description="Shows available commands and their functions\ 231 | \nNOTE: The command prefix is '.'", 232 | color=0xf58300) 233 | embed.set_thumbnail( 234 | url="https://img.icons8.com/fluent/144/000000/get-help.png") 235 | embed.add_field(name=":one: .standings-all [league code]", inline=False, 236 | value="Detailed Standings, with team codes") 237 | embed.add_field(name=":two: .standings [league code]", inline=False, 238 | value="Display Standings") 239 | embed.add_field(name=":three: .fixtures [league code or team code] [limit (default: :five: )]", inline=False, 240 | value="Displays fixtures of matches of the league or team",) 241 | embed.add_field(name=":four: .live [league code or team code] [limit (default: :five: )]", inline=False, 242 | value='Display Live Matches of the league or team') 243 | embed.add_field(name=":five: .league-codes", inline=False, 244 | value="Displays Leagues and their Respective Codes") 245 | embed.add_field(name=":six: .team-codes", inline=False, 246 | value="Displayes Teams and their Respective Codes") 247 | embed.add_field(name=":seven: .invite", inline=False, 248 | value="Invite bot to your servers!") 249 | embed.add_field( 250 | name="\u200b", value=":computer: Link to GitHub Repository: [Click Here](https://github.com/MaheshBharadwaj/paneka)", inline=False) 251 | embed.add_field( 252 | name="\u200b", value=":shield: Link to Support Server: [Discord Invite](https://discord.gg/DXPgJaMsZe)", inline=False 253 | ) 254 | if ctx is not None: 255 | embed.set_footer(text='Requested By: ' + str(ctx.author)) 256 | 257 | return embed 258 | 259 | 260 | if __name__ == "__main__": 261 | print(getStandings('PL')) 262 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Paneka

2 |



3 | 4 | 5 | GitHub contributors 6 | Hits

7 |

Click Here To invite the bot to your server!

8 |

9 |
10 | 11 | # Introduction 12 | Football bot for discord written in `python` using [discord.py](https://pypi.org/project/discord.py/) and currenty hosted on [heroku](https://heroku.com). 13 | 14 | The API used for fetching football data is [football-data.org](https://football-data.org)'s free tier and hence we have a rate limit of **10 Requests / Min** and **12** Competitions. 15 | 16 | # Commands 17 | ### 1. Help 18 | Displays the commands available for use 19 | 20 | **usage:** `.help` 21 | 22 |

23 | help image 24 |

25 | 26 | ### 2. Standings All 27 | Generates a more _detailed_ table showing goals for, against, matches won and lost 28 | 29 | **usage:** `.standings-all [league code]` 30 | 31 |

32 | standings-all image 33 |

34 | 35 | ### 3. Standings 36 | Generates the _current standings_ in the requested league. 37 | 38 | **usage:** `.standings [league code]` 39 | 40 |

41 | standings image 42 |

43 | 44 | ### 4. Fixtures 45 | Displays the next matches scheduled for the team or league requested. 46 | 47 | **usage:** `.fixtures [league code | team code] [limit: default 5]` 48 | 49 | Limit restricts the number of fixtures displayed and is by default 5 50 | 51 | #### 4.1 League Fixtures 52 | 53 |

54 | League Fixtures image 55 |

56 | 57 | #### 4.2 Team Fixtures 58 | 59 |

60 | Team Fixtures image 61 |

62 | 63 | ### 5. Live Scores 64 | Displays the live scores of matches in league requested or team requested 65 | 66 | **usage:** `.live [league code | team code] [limit: default 5]` 67 | 68 | Limit restricts number of matches displayed and is by default 5 69 | 70 |

71 | Team Fixtures image 72 |

73 | 74 | ### 6. Invite 75 | Sends the URL to invite bot into servers as a _Direct Message_ 76 | 77 | **usage:** `.invite` 78 | 79 |

80 | Invite Command image 81 |

82 | 83 | 84 | 85 | # How to run locally: 86 | - fork and clone repository 87 | - ```bash 88 | cd paneka/ 89 | pip install -r requirements.txt 90 | 91 | #create a file '.env' 92 | touch .env 93 | ``` 94 | - Become a discord developer and create a new bot 95 | - Copy the token under `Build-A-Bot` 96 | - Store the key in `.env` as follows:
97 | `DISCORD_TOKEN=The Bot code`
98 | **NOTE:** NO _spaces_ around the `'='` 99 | - Get a **free** football-data API key from [football-data.org](https://www.football-data.org/) 100 | - Store this key in `.env` on a new line as:
101 | `API_KEY=The API code`
102 | **NOTE:** NO _spaces_ around the `'='` 103 | - `python3 bot.py` to run the bot 104 | - Add the bot to your guild(server) by inviting it using the OAuth2 link present in the developer application window 105 | - The bot is now ready :D 106 | 107 | # League Codes 108 | All the supported leagues and their codes are listed below 109 | 110 | | League | Code | 111 | | :----- | :--: | 112 | | **Bundesliga**
Germany | `BL` | 113 | | **Brazillian Serie A**
Brazil | `BSA` | 114 | | **English Championship**
England | `ECL` | 115 | | **Eredivisie**
Netherlands | `ERD` | 116 | | **Ligue One**
France | `FL1` | 117 | | **Premier League**
England | `PL` | 118 | | **Primeira Liga**
Porugal | `PPL` | 119 | | **Serie A**
Italy | `SA` | 120 | | **La Liga**
Spain| `SPA` | 121 | 122 | # Team Codes 123 | 124 | All team codes are listed according to leagues: 125 | - [Bundesliga](#bundesliga) 126 | - [La Liga](#la-liga) 127 | - [Ligue One](#ligue-one) 128 | - [Serie A](#serie-a) 129 | - [Premier League](#premier-league) 130 | - [English Championship](#english-championship) 131 | - [Eredivisie](#eredivisie) 132 | - [Mixed](#mixed) 133 | 134 | ## Bundesliga 135 | 136 | | Team | Codes | 137 | | :----- | :--: | 138 | | **FC Bayern Mücnchen** | `BAY` | 139 | | **Hamburger SV** | `HSV` | 140 | | **C Augsburg** | `FCA` | 141 | | **Hertha BSC** | `BSC` | 142 | | **Bayer Leverkusen** | `B04` | 143 | | **TSG 1899 Hoffenheim** | `TSG` | 144 | | **SV Darmstadt 98** | `DAR` | 145 | | **Hannover 96** | `H96` | 146 | | **1. FSV Mainz 05** | `M05` | 147 | | **FC Ingolstadt 04** | `FCI` | 148 | | **Werder Bremen** | `SVW` | 149 | | **FC Schalke 04** | `S04` | 150 | | **Borussia Dortmund** | `BVB` | 151 | | **Bor. Mönchengladbach** | `BMG` | 152 | | **VfL Wolfsburg** | `WOB` | 153 | | **Eintracht Frankfurt** | `SGE` | 154 | | **VfB Stuttgart** | `VFB` | 155 | | **1. FC Köln** | `FCK` | 156 | | **1. FC Kaiserslautern** | `KAI` | 157 | | **Eintracht Braunschweig** | `EBS` | 158 | | **SV Sandhausen** | `SVS` | 159 | | **SC Freiburg** | `SCF` | 160 | | **1. FC Nücrnberg** | `FCN` | 161 | | **FSV Frankfurt** | `FSV` | 162 | | **Red Bull Leipzig** | `RBL` | 163 | | **SpVgg Greuther Fücrth** | `GRE` | 164 | | **Karlsruher SC** | `KAR` | 165 | | **1. FC Heidenheim 1846** | `HEI` | 166 | | **TSV 1860 Mücnchen** | `1860` | 167 | | **SC Paderborn 07** | `PAD` | 168 | | **VfL Bochum** | `VFL` | 169 | | **FC St. Pauli** | `FCP` | 170 | | **1. FC Union Berlin** | `FCU` | 171 | | **Fortuna Dücsseldorf** | `FOR` | 172 | | **VfR Aalen** | `VFR` | 173 | 174 | 175 | ## La Liga 176 | 177 | | Team | Codes | 178 | | :----- | :--: | 179 | | **RC Deportivo La Coruna** | `LAC` | 180 | | **Real Sociedad de Füatbol** | `RSS` | 181 | | **RCD Espanyol** | `ESP` | 182 | | **Getafe CF** | `FCG` | 183 | | **Club Atlético de Madrid** | `ATM` | 184 | | **Rayo Vallecano de Madrid** | `RAY` | 185 | | **Valencia CF** | `VAL` | 186 | | **Málaga CF** | `MAL` | 187 | | **Sevilla FC** | `SEV` | 188 | | **Athletic Club** | `BIL` | 189 | | **FC Barcelona** | `FCB` | 190 | | **Real Madrid CF** | `MAD` | 191 | | **Levante UD** | `LUD` | 192 | | **RC Celta de Vigo** | `VIG` | 193 | | **Real Betis** | `BET` | 194 | | **Villarreal CF** | `VCF` | 195 | | **Granada CF** | `GCF` | 196 | | **SD Eibar** | `EIB` | 197 | | **Sporting CP** | `SCP` | 198 | | **FC Porto** | `FCP` | 199 | | **SL Benfica** | `SLB` | 200 | | **Elche FC** | `CFE` | 201 | | **UD Almeria** | `UDA` | 202 | | **Córdoba CF** | `CCF` | 203 | 204 | ## Ligue One 205 | 206 | | Team | Codes | 207 | | :----- | :--: | 208 | | **Paris Saint-Germain** | `PSG` | 209 | | **Olympique de Marseille** | `MAR` | 210 | | **Paris FC** | `PFC` | 211 | | **évian Thonon Gaillard FC** | `EVA` | 212 | | **FC Metz** | `FCM` | 213 | | **RC Lens** | `RCL` | 214 | | **Montpellier Hérault SC** | `MHSC` | 215 | | **OSC Lille** | `OSC` | 216 | | **SM Caen** | `SMC` | 217 | | **OGC Nice** | `NIC` | 218 | | **AS Monaco FC** | `MON` | 219 | | **Stade Rennais FC** | `REN` | 220 | | **FC Girondins de Bordeaux** | `BOR` | 221 | | **FC Nantes** | `NAN` | 222 | | **EA Guingamp** | `GUI` | 223 | | **Stade de Reims** | `REI` | 224 | | **Toulouse FC** | `TOU` | 225 | | **AS Saint- Étienne** | `ETI` | 226 | | **Olympique Lyonnais** | `OLY` | 227 | | **FC Lorient** | `LOR` | 228 | 229 | 230 | ## Serie A 231 | 232 | | Team | Codes | 233 | | :----- | :--: | 234 | | **AS Roma** | `ROM` | 235 | | **Juventus Turin** | `JUVE` | 236 | | **US Cittá di Palermo** | `PAL` | 237 | | **Genoa CFC** | `GEN` | 238 | | **US Sassuolo Calcio** | `SASS` | 239 | | **SSC Napoli** | `SSC` | 240 | | **SS Lazio** | `LAZ` | 241 | | **FC Internazionale Milano** | `INT` | 242 | | **Torino FC** | `FCT` | 243 | | **ACF Fiorentina** | `FIO` | 244 | | **AC Milan** | `ACM` | 245 | | **Empoli FC** | `EMP` | 246 | 247 | 248 | ## Premier League 249 | 250 | | Team | Codes | 251 | | :----- | :--: | 252 | | **Manchester United FC** | `MUFC` | 253 | | **Tottenham Hotspur FC** | `THFC` | 254 | | **AFC Bournemouth** | `AFCB` | 255 | | **Aston Villa FC** | `AVFC` | 256 | | **Everton FC** | `EFC` | 257 | | **Watford FC** | `WAT` | 258 | | **Leicester City FC** | `LCFC` | 259 | | **Sunderland AFC** | `SUN` | 260 | | **Norwich City FC** | `NCFC` | 261 | | **Crystal Palace FC** | `CRY` | 262 | | **Chelsea FC** | `CFC` | 263 | | **Swansea City FC** | `SWA` | 264 | | **Newcastle United FC** | `NUFC` | 265 | | **Southampton FC** | `SFC` | 266 | | **Arsenal FC** | `AFC` | 267 | | **West Ham United FC** | `WHU` | 268 | | **Stoke City FC** | `SCFC` | 269 | | **Liverpool FC** | `LFC` | 270 | | **West Bromwich Albion FC** | `WBA` | 271 | | **Manchester City FC** | `MCFC` | 272 | 273 | ## English Championship 274 | 275 | | Team | Codes | 276 | | :----- | :--: | 277 | | **Sheffield United FC** | `SUSFC` | 278 | 279 | ## Eredivisie 280 | 281 | | Team | Codes | 282 | | :----- | :--: | 283 | | **PSV Eindhoven** | `PSV` | 284 | 285 | 286 | ## Mixed 287 | 288 | | Team | Codes | 289 | | :----- | :--: | 290 | | **Malmö FF** | `MFF` | 291 | | **FC Astana** | `ASTA` | 292 | | **Galatasaray SK** | `GSK` | 293 | | **CSKA Moscow** | `CSK` | 294 | | **Shakhtar Donetsk** | `SHA` | 295 | | **FC Zenit St. Petersburg** | `ZEN` | 296 | | **Dynamo Kyiv** | `DYK` | 297 | | **Maccabi Tel Aviv** | `MTA` | 298 | | **Olympiacos F.C.** | `OLA` | 299 | | **GNK Dinamo Zagreb** | `DIN` | 300 | | **Erzgebirge Aue** | `AUE` | 301 | | **SC Bastia** | `SCB` | 302 | 303 | 304 | 305 | ## Credits: 306 | Logo Courtesy: 307 | Icons made by [Freepik](https://www.flaticon.com/authors/freepik) from [Flaticon](https://www.flaticon.com/) 308 | 309 | 310 | -------------------------------------------------------------------------------- /source/utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | import discord 3 | import datetime as dt 4 | import requests 5 | import os 6 | 7 | 8 | from dotenv import load_dotenv 9 | from source.team_id import TEAM_ID 10 | from source.league_code import LEAGUE_CODE 11 | 12 | 13 | def putTableAll(obj): 14 | """ 15 | Returns table as string showing standings of league with all data 16 | 17 | Parameters: 18 | ----------- 19 | obj: dict 20 | JSON object of league standings obtained from API/cache 21 | 22 | Returns: 23 | -------- 24 | str 25 | Standings as a text code block (to get monospaced text) showing all data 26 | """ 27 | try: 28 | assert(type(obj) == dict) 29 | 30 | fin = open('source/teamcodes.json', 'r') 31 | mapper = json.load(fin) 32 | 33 | str_re = '```\nLEAGUE: ' + str(obj['competition']['name']) +\ 34 | ' ' * (45 - 2 - 8 - 10 - len(str(obj['competition']['name']))) +\ 35 | 'MATCHDAY: ' + str(obj['season']['currentMatchday']) + '\n' 36 | str_re += '╔════╤══════╤════╤════╤════╤════╤═════╤═════╗\n' 37 | str_re += '║ SN │ TEAM │ M │ W │ D │ L │ PTS │ GD ║\n' 38 | str_re += '╠════╪══════╪════╪════╪════╪════╪═════╪═════╣\n' 39 | for team in obj['standings'][0]['table']: 40 | text = '║ %-2d │ %-4s │ %-2d │ %-2d │ %-2d │ %-2d │ %-3d │ %+-3d ║\n'\ 41 | % (team['position'], mapper.get(team['team']['name'], team['team']['name'][:4])[:4], team['playedGames'], team['won'], 42 | team['draw'], team['lost'], team['points'], team['goalDifference']) 43 | 44 | str_re += text 45 | 46 | str_re += '╚════╧══════╧════╧════╧════╧════╧═════╧═════╝```' 47 | fin.close() 48 | return str_re 49 | 50 | except AssertionError: 51 | return 'Error!' 52 | 53 | 54 | def putTableLong(obj): 55 | """ 56 | Returns table as string with long team names and Matches played, Points alone 57 | 58 | Parameters: 59 | ----------- 60 | obj: dict 61 | JSON object of league standings obtained from API/cache 62 | 63 | Returns: 64 | -------- 65 | str 66 | Standings as code block (to get monospaced text) showing aforementioned data 67 | """ 68 | try: 69 | assert(type(obj) == dict) 70 | 71 | str_re = '```\nLEAGUE: ' + str(obj['competition']['name']) +\ 72 | ' ' * (45 - 2 - 8 - 10 - len(str(obj['competition']['name']))) +\ 73 | 'MATCHDAY: ' + str(obj['season']['currentMatchday']) + '\n' 74 | str_re += '╔════╤══════════════════════════╤═════╤═════╗\n' 75 | str_re += '║ SN │ TEAM NAME │ MAT │ PTS ║\n' 76 | str_re += '╠════╪══════════════════════════╪═════╪═════╣\n' 77 | for team in obj['standings'][0]['table']: 78 | text = '║ %-2d │ %-24s │ %-3d │ %-3d ║\n'\ 79 | % (team['position'], team['team']['name'][:24], team['playedGames'], team['points']) 80 | 81 | str_re += text 82 | 83 | str_re += '╚════╧══════════════════════════╧═════╧═════╝```' 84 | return str_re 85 | 86 | except AssertionError: 87 | return 'Error!' 88 | 89 | 90 | def putFixtures(obj, code, limit, mode): 91 | """ 92 | Returns Embed for fixtures of a league 93 | 94 | Parameters: 95 | ----------- 96 | obj: dict 97 | JSON object of league standings obtained from API/cache 98 | code: str 99 | Code of the team/league for which fixtures are required 100 | limit: int 101 | Number of matches to display 102 | mode: str ['league' or 'team'] 103 | Indicates which type of fixtures are to be generated 104 | 105 | Returns: 106 | -------- 107 | discord.Embed 108 | Showing fixtures left in the league 109 | 110 | """ 111 | if mode == 'league': 112 | title = obj['competition']['name'] 113 | else: 114 | title = code 115 | with open('source/teamcodes.json') as fin: 116 | team_codes = json.load(fin) 117 | 118 | for key in team_codes.keys(): 119 | if team_codes[key] == code: 120 | title = key 121 | 122 | fixtures = discord.Embed(title=title, 123 | description='Fixtures', 124 | color=0xf58300) 125 | if len(obj['matches']) == 0: 126 | fixtures.add_field(name='No remaining matches in the current season!', 127 | value='\u200b') 128 | else: 129 | for i, match in enumerate(obj['matches']): 130 | if i > limit - 1: 131 | break 132 | matchTime = dt.datetime.strptime( 133 | match['utcDate'][:-1], '%Y-%m-%dt%H:%M:%S') 134 | 135 | # Converting to IST from UTC 136 | matchTime += dt.timedelta(hours=5, minutes=30) 137 | homeTeam = match['homeTeam']['name'] 138 | awayTeam = match['awayTeam']['name'] 139 | 140 | date = matchTime.strftime('%d %B %Y') 141 | time = matchTime.strftime('%I:%M %p') 142 | fixtures.add_field(name=f'{homeTeam} :regional_indicator_v::regional_indicator_s: {awayTeam}', 143 | value=f'`Date:` {date}\n`Time:` {time}' + 144 | str('\n\u200b' if i != limit - 1 else ' '), 145 | inline=False) 146 | return fixtures 147 | 148 | 149 | def putMatches(obj, code, limit, mode): 150 | """ 151 | Returns Embed for fixtures of a league 152 | 153 | Parameters: 154 | ----------- 155 | obj: dict 156 | JSON object of league standings obtained from API/cache 157 | code: str 158 | Code of the team/league for which fixtures are required 159 | limit: int 160 | Number of matches to display 161 | mode: str ['league' or 'team'] 162 | Indicates which type of fixtures are to be generated 163 | 164 | Returns: 165 | -------- 166 | discord.Embed 167 | Showing fixtures left in the league 168 | 169 | """ 170 | if mode == 'league': 171 | title = obj['competition']['name'] 172 | else: 173 | title = code 174 | with open('source/teamcodes.json') as fin: 175 | team_codes = json.load(fin) 176 | 177 | for key in team_codes.keys(): 178 | if team_codes[key] == code: 179 | title = key 180 | 181 | fixtures = discord.Embed(title=title, 182 | description='Live Scores', 183 | color=0xf58300) 184 | if len(obj['matches']) == 0: 185 | fixtures.add_field(name='No Live matches at the moment!', 186 | value='\u200b') 187 | else: 188 | fixtures.add_field(name='Home Team :regional_indicator_v::regional_indicator_s: Away Team',\ 189 | value = '\u200b') 190 | for i, match in enumerate(obj['matches']): 191 | if i > limit - 1: 192 | break 193 | 194 | homeTeam = match['homeTeam']['name'] 195 | awayTeam = match['awayTeam']['name'] 196 | 197 | homeTeamScore = match['score']['fullTime']['homeTeam'] 198 | homeTeamScore = 0 if homeTeamScore is None else homeTeamScore 199 | awayTeamScore = match['score']['fullTime']['awayTeam'] 200 | awayTeamScore = 0 if awayTeamScore is None else awayTeamScore 201 | 202 | status = '' 203 | if homeTeamScore == awayTeamScore: 204 | status = f"**Draw {homeTeamScore} - {awayTeamScore}**" 205 | else: 206 | leading = match['score']['winner'] 207 | if leading == 'AWAY_TEAM': 208 | status = f"**{awayTeam} is currently leading by {awayTeamScore} goals to {homeTeamScore} goals**" 209 | else: 210 | status = f"**{homeTeam} is currently leading by {homeTeamScore} goals to {awayTeamScore} goals**" 211 | 212 | fixtures.add_field(name=f'{homeTeam} :regional_indicator_v::regional_indicator_s: {awayTeam}', 213 | value=status + 214 | str('\n\u200b' if i != limit - 1 else ' '), 215 | inline=False) 216 | return fixtures 217 | 218 | 219 | def fetchJSON(code, resource): 220 | """ 221 | Fetches and returns JSON object of the resource requested 222 | 223 | Parameters: 224 | ----------- 225 | code: str 226 | The code representing the team or the league required 227 | resource: str ['fixtures' or 'standings or 'live'] 228 | What resource of that league/team is requested 229 | 230 | Returns: 231 | -------- 232 | dict 233 | JSON data fetched from cache or from API if cache miss 234 | """ 235 | try: 236 | assert(resource in ['fixtures', 'standings', 'live']) 237 | 238 | if code in LEAGUE_CODE: 239 | id = LEAGUE_CODE.get(code) 240 | if resource == 'fixtures' or resource == 'live': 241 | resource += '-league' 242 | else: 243 | id = TEAM_ID.get(code) 244 | if resource == 'fixtures' or resource == 'live': 245 | resource += '-team' 246 | 247 | load_dotenv() 248 | url = "https://api.football-data.org/v2/" 249 | API_KEY = os.getenv('API_KEY') 250 | headers = {'X-Auth-Token': str(API_KEY)} 251 | 252 | extension = {'fixtures-league': 253 | {'api': f"competitions/{id}/matches?status=SCHEDULED", 254 | 'file': f"cache/{code}-MAT.json"}, 255 | 'standings': 256 | {'api': f"competitions/{id}/standings", 257 | 'file': f"cache/{code}.json"}, 258 | 'fixtures-team': 259 | {'api': f"teams/{id}/matches?status=SCHEDULED", 260 | 'file': f"cache/TEAM-{code}.json"}, 261 | 'live-league': 262 | {'api': f"competitions/{id}/matches?status=LIVE", 263 | 'file': f"cache/{code}-LIVMAT.json" 264 | }, 265 | 'live-team': 266 | { 267 | 'api': f"teams/{id}/matches?status=LIVE", 268 | 'file': f"cache/{code}-LIVMAT.json" 269 | } 270 | } 271 | 272 | # filePath = extension[resource]['file'] 273 | 274 | # if os.path.exists(filePath): 275 | # #Cache hit 276 | # print(f'Entry for {code} found in Cache :D') 277 | 278 | # with open(filePath, 'r') as fin: 279 | # obj = json.load(fin) 280 | # else: 281 | # Cache Miss 282 | api_url = url + extension[resource]['api'] 283 | r = requests.get(api_url, headers=headers) 284 | obj = r.json() 285 | 286 | # with open(extension[resource]['file'], 'w') as fout: 287 | # json.dump(obj, fout, indent=4) 288 | 289 | return obj 290 | 291 | except AssertionError: 292 | return None 293 | 294 | 295 | def fetchImage(code): 296 | """ 297 | Returns file path to logo of league as string 298 | 299 | Parameters: 300 | ----------- 301 | code : str 302 | The ID of the league for which fixtures are required 303 | 304 | Returns: 305 | -------- 306 | str 307 | Contains file path if valid code is supplied, else 'None' 308 | """ 309 | if code in ['PL', 'FL1', 'BL', 'SPA', 'SA']: 310 | return f"source/logos/{code}.jpg" 311 | else: 312 | return None 313 | --------------------------------------------------------------------------------