From 8e54fab377c7f798259bbf217afc8c3f68c9fb0f Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 8 Jan 2021 00:04:44 +0000 Subject: Get and renew V4 OAuth token --- bot/exts/evergreen/game.py | 53 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) (limited to 'bot/exts/evergreen/game.py') diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index d0fd7a40..f5707a22 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -2,7 +2,8 @@ import difflib import logging import random import re -from datetime import datetime as dt +from asyncio import sleep +from datetime import datetime as dt, timedelta from enum import IntEnum from typing import Any, Dict, List, Optional, Tuple @@ -17,10 +18,22 @@ from bot.utils.decorators import with_role from bot.utils.pagination import ImagePaginator, LinePaginator # Base URL of IGDB API -BASE_URL = "https://api-v3.igdb.com" +BASE_URL = "https://api.igdb.com/v4" + +CLIENT_ID = Tokens.igdb_client_id +CLIENT_SECRET = Tokens.igdb_client_secret + +# URL to request API access token +OAUTH_URL = "https://id.twitch.tv/oauth2/token" + +OAUTH_PARAMS = { + "client_id": CLIENT_ID, + "client_secret": CLIENT_SECRET, + "grant_type": "client_credentials" +} HEADERS = { - "user-key": Tokens.igdb, + "Client-ID": CLIENT_ID, "Accept": "application/json" } @@ -136,7 +149,32 @@ class Games(Cog): self.genres: Dict[str, int] = {} - self.refresh_genres_task.start() + self.bot.loop.create_task(self.renew_access_token()) + + # self.refresh_genres_task.start() + + async def renew_access_token(self) -> None: + """Refeshes V4 access token 2 days before expiry.""" + while True: + async with self.http_session.post(OAUTH_URL, params=OAUTH_PARAMS) as resp: + result = await resp.json() + if resp.status != 200: + logger.error( + "Failed to renew IGDB access token, unloading Games cog." + f"OAuth response message: {result['message']}" + ) + self.bot.remove_cog('Games') + return + + self.access_token = result["access_token"] + + # Set next renewal to 2 days before expire time + next_renewal = result["expires_in"] - 60*60*24*2 + + time_delta = timedelta(seconds=next_renewal) + logger.info(f"Successfully renewed access token. Refreshing again in {time_delta}") + + await sleep(next_renewal) @tasks.loop(hours=24.0) async def refresh_genres_task(self) -> None: @@ -418,7 +456,10 @@ class Games(Cog): def setup(bot: Bot) -> None: """Add/Load Games cog.""" # Check does IGDB API key exist, if not, log warning and don't load cog - if not Tokens.igdb: - logger.warning("No IGDB API key. Not loading Games cog.") + if not Tokens.igdb_client_id: + logger.warning("No IGDB client ID. Not loading Games cog.") + return + if not Tokens.igdb_client_secret: + logger.warning("No IGDB client secret. Not loading Games cog.") return bot.add_cog(Games(bot)) -- cgit v1.2.3 From 0fcfb1459c258bae4bf2895a16559210e44c675c Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 8 Jan 2021 00:09:16 +0000 Subject: Un-comment out task start --- bot/exts/evergreen/game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot/exts/evergreen/game.py') diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index f5707a22..fe5fc612 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -151,7 +151,7 @@ class Games(Cog): self.bot.loop.create_task(self.renew_access_token()) - # self.refresh_genres_task.start() + self.refresh_genres_task.start() async def renew_access_token(self) -> None: """Refeshes V4 access token 2 days before expiry.""" -- cgit v1.2.3 From 542cdc023629513c6d280e2bf96d5d554baa78d9 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 8 Jan 2021 16:43:39 +0000 Subject: Move renewal window to a const --- bot/exts/evergreen/game.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'bot/exts/evergreen/game.py') diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index fe5fc612..be4fcbc9 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -23,6 +23,9 @@ BASE_URL = "https://api.igdb.com/v4" CLIENT_ID = Tokens.igdb_client_id CLIENT_SECRET = Tokens.igdb_client_secret +# The number of seconds before expiry that we attempt to re-fetch a new access token +ACCESS_TOKEN_RENEWAL_WINDOW = 60*60*24*2 + # URL to request API access token OAUTH_URL = "https://id.twitch.tv/oauth2/token" @@ -168,8 +171,8 @@ class Games(Cog): self.access_token = result["access_token"] - # Set next renewal to 2 days before expire time - next_renewal = result["expires_in"] - 60*60*24*2 + # Attempt to renew before the token expires + next_renewal = result["expires_in"] - ACCESS_TOKEN_RENEWAL_WINDOW time_delta = timedelta(seconds=next_renewal) logger.info(f"Successfully renewed access token. Refreshing again in {time_delta}") -- cgit v1.2.3 From 4d68fb6e1be9fd7d1287e2f1650f88b1e447f69d Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 8 Jan 2021 18:40:50 +0000 Subject: Use current token if we can't get a new one --- bot/exts/evergreen/game.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) (limited to 'bot/exts/evergreen/game.py') diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index be4fcbc9..2abd7555 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -35,7 +35,7 @@ OAUTH_PARAMS = { "grant_type": "client_credentials" } -HEADERS = { +BASE_HEADERS = { "Client-ID": CLIENT_ID, "Accept": "application/json" } @@ -151,25 +151,34 @@ class Games(Cog): self.http_session: ClientSession = bot.http_session self.genres: Dict[str, int] = {} + self.headers = BASE_HEADERS self.bot.loop.create_task(self.renew_access_token()) self.refresh_genres_task.start() async def renew_access_token(self) -> None: - """Refeshes V4 access token 2 days before expiry.""" + """Refeshes V4 access token a number of seconds before expiry. See `ACCESS_TOKEN_RENEWAL_WINDOW`.""" while True: async with self.http_session.post(OAUTH_URL, params=OAUTH_PARAMS) as resp: result = await resp.json() if resp.status != 200: - logger.error( - "Failed to renew IGDB access token, unloading Games cog." - f"OAuth response message: {result['message']}" - ) - self.bot.remove_cog('Games') + # If there is a valid access token continue to use that, + # otherwise unload cog. + if "access_token" in self.headers: + time_delta = timedelta(seconds=ACCESS_TOKEN_RENEWAL_WINDOW) + logger.error( + "Failed to renew IGDB access token. " + f"Current token will last for {time_delta} " + f"OAuth response message: {result['message']}" + ) + else: + logger.warning("Invalid OAuth credentials. Unloading Games cog.") + self.bot.remove_cog('Games') + return - self.access_token = result["access_token"] + self.headers["access_token"] = result["access_token"] # Attempt to renew before the token expires next_renewal = result["expires_in"] - ACCESS_TOKEN_RENEWAL_WINDOW @@ -197,7 +206,7 @@ class Games(Cog): async def _get_genres(self) -> None: """Create genres variable for games command.""" body = "fields name; limit 100;" - async with self.http_session.get(f"{BASE_URL}/genres", data=body, headers=HEADERS) as resp: + async with self.http_session.get(f"{BASE_URL}/genres", data=body, headers=self.headers) as resp: result = await resp.json() genres = {genre["name"].capitalize(): genre["id"] for genre in result} @@ -347,7 +356,7 @@ class Games(Cog): body = GAMES_LIST_BODY.format(**params) # Do request to IGDB API, create headers, URL, define body, return result - async with self.http_session.get(url=f"{BASE_URL}/games", data=body, headers=HEADERS) as resp: + async with self.http_session.get(url=f"{BASE_URL}/games", data=body, headers=self.headers) as resp: return await resp.json() async def create_page(self, data: Dict[str, Any]) -> Tuple[str, str]: @@ -389,7 +398,7 @@ class Games(Cog): # Define request body of IGDB API request and do request body = SEARCH_BODY.format(**{"term": search_term}) - async with self.http_session.get(url=f"{BASE_URL}/games", data=body, headers=HEADERS) as resp: + async with self.http_session.get(url=f"{BASE_URL}/games", data=body, headers=self.headers) as resp: data = await resp.json() # Loop over games, format them to good format, make line and append this to total lines @@ -418,7 +427,7 @@ class Games(Cog): "offset": offset }) - async with self.http_session.get(url=f"{BASE_URL}/companies", data=body, headers=HEADERS) as resp: + async with self.http_session.get(url=f"{BASE_URL}/companies", data=body, headers=self.headers) as resp: return await resp.json() async def create_company_page(self, data: Dict[str, Any]) -> Tuple[str, str]: -- cgit v1.2.3 From 3ba700e7b7c211d69193d3ba3d3e02927b448441 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 8 Jan 2021 20:44:43 +0000 Subject: Switch to post requests and start task at right time. --- bot/exts/evergreen/game.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'bot/exts/evergreen/game.py') diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index 2abd7555..680724c2 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -155,8 +155,6 @@ class Games(Cog): self.bot.loop.create_task(self.renew_access_token()) - self.refresh_genres_task.start() - async def renew_access_token(self) -> None: """Refeshes V4 access token a number of seconds before expiry. See `ACCESS_TOKEN_RENEWAL_WINDOW`.""" while True: @@ -165,7 +163,7 @@ class Games(Cog): if resp.status != 200: # If there is a valid access token continue to use that, # otherwise unload cog. - if "access_token" in self.headers: + if "Authorization" in self.headers: time_delta = timedelta(seconds=ACCESS_TOKEN_RENEWAL_WINDOW) logger.error( "Failed to renew IGDB access token. " @@ -178,7 +176,7 @@ class Games(Cog): return - self.headers["access_token"] = result["access_token"] + self.headers["Authorization"] = f"Bearer {result['access_token']}" # Attempt to renew before the token expires next_renewal = result["expires_in"] - ACCESS_TOKEN_RENEWAL_WINDOW @@ -186,6 +184,10 @@ class Games(Cog): time_delta = timedelta(seconds=next_renewal) logger.info(f"Successfully renewed access token. Refreshing again in {time_delta}") + # This will be true the first time this loop runs. + # Since we now have an access token, its safe to start this task. + if self.genres == {}: + self.refresh_genres_task.start() await sleep(next_renewal) @tasks.loop(hours=24.0) @@ -206,9 +208,8 @@ class Games(Cog): async def _get_genres(self) -> None: """Create genres variable for games command.""" body = "fields name; limit 100;" - async with self.http_session.get(f"{BASE_URL}/genres", data=body, headers=self.headers) as resp: + async with self.http_session.post(f"{BASE_URL}/genres", data=body, headers=self.headers) as resp: result = await resp.json() - genres = {genre["name"].capitalize(): genre["id"] for genre in result} # Replace complex names with names from ALIASES @@ -356,7 +357,7 @@ class Games(Cog): body = GAMES_LIST_BODY.format(**params) # Do request to IGDB API, create headers, URL, define body, return result - async with self.http_session.get(url=f"{BASE_URL}/games", data=body, headers=self.headers) as resp: + async with self.http_session.post(url=f"{BASE_URL}/games", data=body, headers=self.headers) as resp: return await resp.json() async def create_page(self, data: Dict[str, Any]) -> Tuple[str, str]: @@ -398,7 +399,7 @@ class Games(Cog): # Define request body of IGDB API request and do request body = SEARCH_BODY.format(**{"term": search_term}) - async with self.http_session.get(url=f"{BASE_URL}/games", data=body, headers=self.headers) as resp: + async with self.http_session.post(url=f"{BASE_URL}/games", data=body, headers=self.headers) as resp: data = await resp.json() # Loop over games, format them to good format, make line and append this to total lines @@ -427,7 +428,7 @@ class Games(Cog): "offset": offset }) - async with self.http_session.get(url=f"{BASE_URL}/companies", data=body, headers=self.headers) as resp: + async with self.http_session.post(url=f"{BASE_URL}/companies", data=body, headers=self.headers) as resp: return await resp.json() async def create_company_page(self, data: Dict[str, Any]) -> Tuple[str, str]: -- cgit v1.2.3 From 92f1ee13d71fd4f72edc8e24488115a2294421f8 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 8 Jan 2021 20:57:57 +0000 Subject: Add OAuth response to warning. --- bot/exts/evergreen/game.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'bot/exts/evergreen/game.py') diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index 680724c2..d37be0e2 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -171,7 +171,10 @@ class Games(Cog): f"OAuth response message: {result['message']}" ) else: - logger.warning("Invalid OAuth credentials. Unloading Games cog.") + logger.warning( + "Invalid OAuth credentials. Unloading Games cog. " + f"OAuth response message: {result['message']}" + ) self.bot.remove_cog('Games') return -- cgit v1.2.3