import * as config from "./config"; import { log, LogLevel } from "./logging" import fetch, { Response as FetchResponse } from "node-fetch"; export interface TokenPair { access_token: string; refresh_token: string; } // Refresh the API token. Returns true on success and false on failure. async function refreshApiToken(tokens: TokenPair): Promise { log(LogLevel.DEBUG,`Call: refreshApiToken(${JSON.stringify(tokens,null,2)})`); return fetch("https://id.twitch.tv/oauth2/token", { method: 'POST', body: new URLSearchParams({ client_id: config.twitchClientId, client_secret: config.twitchSecret, grant_type: "refresh_token", refresh_token: tokens.refresh_token }) }).then(async (res: FetchResponse) => { if (res.status == 200) { log(LogLevel.INFO,"Refresh returned success."); var data = await (res.json() as Promise); log(LogLevel.DEBUG, "Returned data:") log(LogLevel.DEBUG, data) tokens.access_token = data.access_token; tokens.refresh_token = data.refresh_token; return true; } else { log(LogLevel.ERROR,"Refresh returned failure. Response object:"); log(LogLevel.ERROR,res); return false; } }) } // Send an API request. On success, return the specified data. On failure, // attempt to refresh the API token and retry export async function apiRequest(tokens: TokenPair, endpoint: string): Promise { log(LogLevel.DEBUG,`Call: apiRequest(${JSON.stringify(tokens,null,2)},${endpoint})`); var headers = { "Authorization": "Bearer " + tokens.access_token, "Client-ID": config.twitchClientId }; return fetch("https://api.twitch.tv/helix" + endpoint, { headers: headers }) .then((res: FetchResponse) => { if (res.status == 200) { return res.json(); } else { if (refreshApiToken(tokens)) { log(LogLevel.WARNING,"Failed API request (pre-refresh):"); log(LogLevel.WARNING,"Request URL: https://api.twitch.tv/helix" + endpoint); log(LogLevel.WARNING,"Headers:"); log(LogLevel.WARNING,JSON.stringify(headers,null,2)); log(LogLevel.WARNING,"Response:"); log(LogLevel.WARNING,JSON.stringify(await res.json(),null,2)); log(LogLevel.WARNING,"Attempting refresh"); return fetch("https://api.twitch.tv/helix" + endpoint, { headers: headers }) .then(async (res: FetchResponse) => { if (res.status == 200) { log(LogLevel.WARNING,"API call succeeded after token refresh.") return res.json(); } else { log(LogLevel.ERROR,"Failed API request:"); log(LogLevel.ERROR,"Request URL: https://api.twitch.tv/helix" + endpoint); log(LogLevel.ERROR,"Headers:"); log(LogLevel.ERROR,JSON.stringify(headers,null,2)); log(LogLevel.ERROR,"Response:"); log(LogLevel.ERROR,JSON.stringify(await res.json(),null,2)); return false; } }) } else { return false; } } }) } // Check if API token is valid. Checks Twitch's OAuth validation endpoint. If // success, return true. If failure, return the result of attempting to refresh // the API token. export async function isApiTokenValid(tokens: TokenPair) { log(LogLevel.DEBUG,`Call: isApiTokenValid(${JSON.stringify(tokens,null,2)})`); return fetch("https://id.twitch.tv/oauth2/validate", { headers: {'Authorization': `OAuth ${tokens.access_token}`} }).then((res: FetchResponse) => { if (res.status == 200) { return true; } else { return refreshApiToken(tokens); } }) }