diff --git a/src/hiscores.ts b/src/hiscores.ts index d7e012d..48cd207 100644 --- a/src/hiscores.ts +++ b/src/hiscores.ts @@ -1,4 +1,4 @@ -import { AxiosResponse } from 'axios'; +import { AxiosRequestConfig, AxiosResponse } from 'axios'; import { BinaryData, JSDOM } from 'jsdom'; import { Player, @@ -38,15 +38,20 @@ import { * Screen scrapes the hiscores to get the formatted rsn of a player. * * @param rsn Username of the player. + * @param config Optional axios request config object. * @returns Formatted version of the rsn. */ -export async function getRSNFormat(rsn: string): Promise { +export async function getRSNFormat( + rsn: string, + config?: AxiosRequestConfig +): Promise { validateRSN(rsn); const url = getPlayerTableURL('main', rsn); try { const response = await httpGet( - url + url, + config ); const dom = new JSDOM(response.data); const anchor = dom.window.document.querySelector( @@ -174,7 +179,10 @@ export async function getStats( ]; const shouldGetFormattedRsn = options?.shouldGetFormattedRsn ?? true; - const mainRes = await httpGet(getStatsURL('main', rsn)); + const mainRes = await httpGet( + getStatsURL('main', rsn), + options?.axiosConfigs?.main + ); if (mainRes.status === 200) { const emptyResponse: AxiosResponse = { status: 404, @@ -187,10 +195,15 @@ export async function getStats( mode: Extract ): Promise> => otherGamemodes.includes(mode) - ? httpGet(getStatsURL(mode, rsn)).catch((err) => err) + ? httpGet( + getStatsURL(mode, rsn), + options?.axiosConfigs?.[mode] + ).catch((err) => err) : emptyResponse; const formattedName = shouldGetFormattedRsn - ? await getRSNFormat(rsn).catch(() => undefined) + ? await getRSNFormat(rsn, options?.axiosConfigs?.rsn).catch( + () => undefined + ) : undefined; const player: Player = { @@ -258,17 +271,19 @@ export async function getStats( * * @param rsn Username of the player. * @param mode Gamemode to fetch ranks for. + * @param config Optional axios request config object. * @returns Stats object. */ export async function getStatsByGamemode( rsn: string, - mode: Gamemode = 'main' + mode: Gamemode = 'main', + config?: AxiosRequestConfig ): Promise { validateRSN(rsn); if (!GAMEMODES.includes(mode)) { throw Error('Invalid game mode'); } - const response = await httpGet(getStatsURL(mode, rsn)); + const response = await httpGet(getStatsURL(mode, rsn), config); if (response.status !== 200) { throw Error('Player not found'); } @@ -280,7 +295,8 @@ export async function getStatsByGamemode( export async function getSkillPage( skill: SkillName, mode: Gamemode = 'main', - page: number = 1 + page: number = 1, + config?: AxiosRequestConfig ): Promise { if (!GAMEMODES.includes(mode)) { throw Error('Invalid game mode'); @@ -291,7 +307,10 @@ export async function getSkillPage( } const url = getSkillPageURL(mode, skill, page); - const response = await httpGet(url); + const response = await httpGet( + url, + config + ); const dom = new JSDOM(response.data); const playersHTML = dom.window.document.querySelectorAll( '.personal-hiscores__row' @@ -323,12 +342,14 @@ export async function getSkillPage( * @param activity Name of the activity or boss to fetch hiscores for. * @param mode Gamemode to fetch ranks for. * @param page Page number. + * @param config Optional axios request config object. * @returns Array of `PlayerActivityRow` objects. */ export async function getActivityPage( activity: ActivityName, mode: Gamemode = 'main', - page: number = 1 + page: number = 1, + config?: AxiosRequestConfig ): Promise { if (!GAMEMODES.includes(mode)) { throw Error('Invalid game mode'); @@ -339,7 +360,10 @@ export async function getActivityPage( } const url = getActivityPageURL(mode, activity, page); - const response = await httpGet(url); + const response = await httpGet( + url, + config + ); const dom = new JSDOM(response.data); const playersHTML = dom.window.document.querySelectorAll( '.personal-hiscores__row' diff --git a/src/types.ts b/src/types.ts index e13c387..8785af2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,5 @@ +import { AxiosRequestConfig } from 'axios'; + export type Gamemode = | 'main' | 'ironman' @@ -183,4 +185,13 @@ export interface GetStatsOptions { * @defaultvalue `true` */ shouldGetFormattedRsn?: boolean; + /** + * Map of configs for each requests that can take place in the `getStats` function. + */ + axiosConfigs?: Partial> & { + /** + * The axios request config object to use for the RSN format request. + */ + rsn?: AxiosRequestConfig; + }; } diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 6c6174a..59f9c9a 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -1,4 +1,4 @@ -import axios from 'axios'; +import axios, { AxiosRequestConfig } from 'axios'; import * as ua from 'useragent-generator'; import { Gamemode, SkillName, ActivityName } from '../types'; import { @@ -96,12 +96,16 @@ export const rsnFromElement = (el: Element | null) => { * @param url URL to run a `GET` request against. * @returns Axios response. */ -export const httpGet = (url: string) => +export const httpGet = ( + url: string, + config: AxiosRequestConfig = {} +) => axios.get(url, { headers: { // without User-Agent header requests may be rejected by DDoS protection mechanism 'User-Agent': ua.firefox(80) - } + }, + ...config }); /**