<commit_before>import * as qs from 'qs';
import AuthProvider from './Auth/AuthProvider';
import { Cacheable, CachedGetter } from './Toolkit/Decorators/Cache';
import TokenInfo, { TokenInfoData } from './API/TokenInfo';
import { CheermoteBackground, CheermoteScale, CheermoteState } from './API/Kraken/Bits/CheermoteList';
import AccessToken, { AccessTokenData } from './API/AccessToken';
import StaticAuthProvider from './Auth/StaticAuthProvider';
import RefreshableAuthProvider, { RefreshConfig } from './Auth/RefreshableAuthProvider';
import ClientCredentialsAuthProvider from './Auth/ClientCredentialsAuthProvider';
import ConfigError from './Errors/ConfigError';
import HTTPStatusCodeError from './Errors/HTTPStatusCodeError';

import KrakenAPIGroup from './API/Kraken/KrakenAPIGroup';
import HelixAPIGroup from './API/Helix/HelixAPIGroup';
import BadgesAPI from './API/Badges/BadgesAPI';
import UnsupportedAPI from './API/Unsupported/UnsupportedAPI';

import * as fetchPonyfill from 'fetch-ponyfill';

const { fetch, Headers } = fetchPonyfill();

/**
 * Default configuration for the cheermote API.
 */
export interface TwitchCheermoteConfig {
	/**
	 * The default background type.
	 */
	defaultBackground: CheermoteBackground;

	/**
	 * The default cheermote state.
	 */
	defaultState: CheermoteState;

	/**
	 * The default cheermote scale.
	 */
	defaultScale: CheermoteScale;
}

/**
 * Configuration for a {@TwitchClient} instance.
 */
export interface TwitchConfig {
	/**
	 * An authentication provider that supplies tokens to the client.
	 *
	 * For more information, see the {@AuthProvider} documentation.
	 */
	authProvider: AuthProvider;

	/**
	 * Whether to authenticate the client before a request is made.
	 */
	preAuth: boolean;

	/**
	 * The scopes to request with the initial request, even if it's not necessary for the request.
	 */
	initialScopes: string[];

	/**
	 * Default values for fetched cheermotes.
	 */
	cheermotes: TwitchCheermoteConfig;
}

/**
 * The endpoint to call, i.e. /kraken, /helix or a custom (potentially unsupported) endpoint.
 */
export enum TwitchAPICallType {
	/**
	 * Call a Kraken API endpoint.
	 */
	Kraken,

	/**
	 * Call a Helix API endpoint.
	 */
	Helix,

	/**
	 * Call a custom (potentially unsupported) endpoint.
	 */
	Custom
}

/**
 * Configuration for a single API call.
 */
export interface TwitchAPICallOptions {
	/**
	 * The URL to request.
	 *
	 * If `type` is not `'custom'`, this is relative to the respective API root endpoint. Otherwise, it is an absoulte URL.
	 */
	url: string;

	/**
	 * The endpoint to call, i.e. /kraken, /helix or a custom (potentially unsupported) endpoint.
	 */
	type?: TwitchAPICallType;

	/**
	 * The HTTP method to use. Defaults to `'GET'`.
	 */
	method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';

	/**
	 * The query parameters to send with the API call.
	 */
	query?: Record<string, string | string[] | undefined>;

	/**
	 * The form body to send with the API call.
	 *
	 * If this is given, `jsonBody` will be ignored.
	 */
	body?: Record<string, string | string[] | undefined>;

	/**
	 * The JSON body to send with the API call.
	 *
	 * If `body` is also given, this will be ignored.
	 */
	// tslint:disable-next-line:no-any
	jsonBody?: any;

	/**
	 * The scope the request needs.
	 */
	scope?: string;

	/**
	 * The Kraken API version to request with. Defaults to 5.
	 *
	 * If `type` is not `'kraken'`, this will be ignored.
	 *
	 * Note that v3 will be removed at the end of 2018 and v5 will be the only Kraken version left, so you should only use this option if you want to rewrite everything in a few months.
	 *
	 * Internally, only v5 and Helix are used.
	 */
	version?: number;
}

/**
 * The main entry point of this library. Manages API calls and the use of access tokens in these.
 */
@Cacheable
export default class TwitchClient {
	/** @private */
	readonly _config: TwitchConfig;

	/**
	 * Creates a new instance with fixed credentials.
	 *
	 * @param clientId The client ID of your application.
	 * @param accessToken The access token to call the API with.
	 *
	 * You need to obtain one using one of the [Twitch OAuth flows](https://dev.twitch.tv/docs/authentication/getting-tokens-oauth/).
	 * @param scopes The scopes your supplied token has.
	 *
	 * If this argument is given, the scopes need to be correct, or weird things might happen. If it's not (i.e. it's `undefined`), we fetch the correct scopes for you.
	 *
	 * If you can't exactly say which scopes your token has, don't use this parameter/set it to `undefined`.
	 * @param refreshConfig Configuration to automatically refresh expired tokens.
	 * @param config Additional configuration to pass to the constructor.
	 *
	 * Note that if you provide a custom `authProvider`, this method will overwrite it. In this case, you should use the constructor directly.
	 */
	static async withCredentials(clientId: string, accessToken?: string, scopes?: string[], refreshConfig?: RefreshConfig, config: Partial<TwitchConfig> = {}) {
		if (!scopes && accessToken) {
			const tokenData = await this.getTokenInfo(clientId, accessToken);
			if (!tokenData.valid) {
				throw new ConfigError('Supplied an invalid access token to retrieve scopes with');
			}
			scopes = tokenData.scopes;
		}
		if (refreshConfig) {
			return new this({ ...config, authProvider: new RefreshableAuthProvider(new StaticAuthProvider(clientId, accessToken, scopes), refreshConfig) });
		} else {
			return new this({ ...config, authProvider: new StaticAuthProvider(clientId, accessToken, scopes) });
		}
	}

	/**
	 * Creates a new instance with client credentials.
	 *
	 * @param clientId The client ID of your application.
	 * @param clientSecret The client secret of your application.
	 * @param accessToken The app access token to set initially.
	 * @param config Additional configuration to pass to the constructor.
	 *
	 * Note that if you provide a custom `authProvider`, this method will overwrite it. In this case, you should use the constructor directly.
	 */
	static withClientCredentials(clientId: string, clientSecret: string, accessToken?: string, config: Partial<TwitchConfig> = {}) {
		return new this({ ...config, authProvider: new ClientCredentialsAuthProvider(clientId, clientSecret) });
	}

	/**
	 * Creates a new Twitch client instance.
	 *
	 * @param config Configuration for the client instance.
	 */
	constructor(config: Partial<TwitchConfig>) {
		const { authProvider, ...restConfig } = config;
		if (!authProvider) {
			throw new ConfigError('No auth provider given');
		}

		this._config = {
			preAuth: false,
			initialScopes: [],
			cheermotes: {
				defaultBackground: CheermoteBackground.dark,
				defaultState: CheermoteState.animated,
				defaultScale: CheermoteScale.x1<commit_msg>allow withClientCredentials to not take a client secret<commit_after>import * as qs from 'qs';
import AuthProvider from './Auth/AuthProvider';
import { Cacheable, CachedGetter } from './Toolkit/Decorators/Cache';
import TokenInfo, { TokenInfoData } from './API/TokenInfo';
import { CheermoteBackground, CheermoteScale, CheermoteState } from './API/Kraken/Bits/CheermoteList';
import AccessToken, { AccessTokenData } from './API/AccessToken';
import StaticAuthProvider from './Auth/StaticAuthProvider';
import RefreshableAuthProvider, { RefreshConfig } from './Auth/RefreshableAuthProvider';
import ClientCredentialsAuthProvider from './Auth/ClientCredentialsAuthProvider';
import ConfigError from './Errors/ConfigError';
import HTTPStatusCodeError from './Errors/HTTPStatusCodeError';

import KrakenAPIGroup from './API/Kraken/KrakenAPIGroup';
import HelixAPIGroup from './API/Helix/HelixAPIGroup';
import BadgesAPI from './API/Badges/BadgesAPI';
import UnsupportedAPI from './API/Unsupported/UnsupportedAPI';

import * as fetchPonyfill from 'fetch-ponyfill';

const { fetch, Headers } = fetchPonyfill();

/**
 * Default configuration for the cheermote API.
 */
export interface TwitchCheermoteConfig {
	/**
	 * The default background type.
	 */
	defaultBackground: CheermoteBackground;

	/**
	 * The default cheermote state.
	 */
	defaultState: CheermoteState;

	/**
	 * The default cheermote scale.
	 */
	defaultScale: CheermoteScale;
}

/**
 * Configuration for a {@TwitchClient} instance.
 */
export interface TwitchConfig {
	/**
	 * An authentication provider that supplies tokens to the client.
	 *
	 * For more information, see the {@AuthProvider} documentation.
	 */
	authProvider: AuthProvider;

	/**
	 * Whether to authenticate the client before a request is made.
	 */
	preAuth: boolean;

	/**
	 * The scopes to request with the initial request, even if it's not necessary for the request.
	 */
	initialScopes: string[];

	/**
	 * Default values for fetched cheermotes.
	 */
	cheermotes: TwitchCheermoteConfig;
}

/**
 * The endpoint to call, i.e. /kraken, /helix or a custom (potentially unsupported) endpoint.
 */
export enum TwitchAPICallType {
	/**
	 * Call a Kraken API endpoint.
	 */
	Kraken,

	/**
	 * Call a Helix API endpoint.
	 */
	Helix,

	/**
	 * Call a custom (potentially unsupported) endpoint.
	 */
	Custom
}

/**
 * Configuration for a single API call.
 */
export interface TwitchAPICallOptions {
	/**
	 * The URL to request.
	 *
	 * If `type` is not `'custom'`, this is relative to the respective API root endpoint. Otherwise, it is an absoulte URL.
	 */
	url: string;

	/**
	 * The endpoint to call, i.e. /kraken, /helix or a custom (potentially unsupported) endpoint.
	 */
	type?: TwitchAPICallType;

	/**
	 * The HTTP method to use. Defaults to `'GET'`.
	 */
	method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';

	/**
	 * The query parameters to send with the API call.
	 */
	query?: Record<string, string | string[] | undefined>;

	/**
	 * The form body to send with the API call.
	 *
	 * If this is given, `jsonBody` will be ignored.
	 */
	body?: Record<string, string | string[] | undefined>;

	/**
	 * The JSON body to send with the API call.
	 *
	 * If `body` is also given, this will be ignored.
	 */
	// tslint:disable-next-line:no-any
	jsonBody?: any;

	/**
	 * The scope the request needs.
	 */
	scope?: string;

	/**
	 * The Kraken API version to request with. Defaults to 5.
	 *
	 * If `type` is not `'kraken'`, this will be ignored.
	 *
	 * Note that v3 will be removed at the end of 2018 and v5 will be the only Kraken version left, so you should only use this option if you want to rewrite everything in a few months.
	 *
	 * Internally, only v5 and Helix are used.
	 */
	version?: number;
}

/**
 * The main entry point of this library. Manages API calls and the use of access tokens in these.
 */
@Cacheable
export default class TwitchClient {
	/** @private */
	readonly _config: TwitchConfig;

	/**
	 * Creates a new instance with fixed credentials.
	 *
	 * @param clientId The client ID of your application.
	 * @param accessToken The access token to call the API with.
	 *
	 * You need to obtain one using one of the [Twitch OAuth flows](https://dev.twitch.tv/docs/authentication/getting-tokens-oauth/).
	 * @param scopes The scopes your supplied token has.
	 *
	 * If this argument is given, the scopes need to be correct, or weird things might happen. If it's not (i.e. it's `undefined`), we fetch the correct scopes for you.
	 *
	 * If you can't exactly say which scopes your token has, don't use this parameter/set it to `undefined`.
	 * @param refreshConfig Configuration to automatically refresh expired tokens.
	 * @param config Additional configuration to pass to the constructor.
	 *
	 * Note that if you provide a custom `authProvider`, this method will overwrite it. In this case, you should use the constructor directly.
	 */
	static async withCredentials(clientId: string, accessToken?: string, scopes?: string[], refreshConfig?: RefreshConfig, config: Partial<TwitchConfig> = {}) {
		if (!scopes && accessToken) {
			const tokenData = await this.getTokenInfo(clientId, accessToken);
			if (!tokenData.valid) {
				throw new ConfigError('Supplied an invalid access token to retrieve scopes with');
			}
			scopes = tokenData.scopes;
		}
		const authProvider = refreshConfig
			? new RefreshableAuthProvider(new StaticAuthProvider(clientId, accessToken, scopes), refreshConfig)
			: new StaticAuthProvider(clientId, accessToken, scopes);

		return new this({ ...config, authProvider });
	}

	/**
	 * Creates a new instance with client credentials.
	 *
	 * @param clientId The client ID of your application.
	 * @param clientSecret The client secret of your application.
	 * @param accessToken The app access token to set initially.
	 * @param config Additional configuration to pass to the constructor.
	 *
	 * Note that if you provide a custom `authProvider`, this method will overwrite it. In this case, you should use the constructor directly.
	 */
	static withClientCredentials(clientId: string, clientSecret?: string, accessToken?: string, config: Partial<TwitchConfig> = {}) {
		const authProvider = clientSecret
			? new ClientCredentialsAuthProvider(clientId, clientSecret)
			: new StaticAuthProvider(clientId);

		return new this({ ...config, authProvider });
	}

	/**
	 * Creates a new Twitch client instance.
	 *
	 * @param config Configuration for the client instance.
	 */
	constructor(config: Partial<TwitchConfig>) {
		const { authProvider, ...restConfig } = config;
		if (!authProvider) {
			throw new ConfigError('No auth provider given');
		}

		this._config = {
			preAuth: false,
			initialScopes: [],
			cheermotes: {
				defaultBackground: CheermoteBackground.dark,
				defaultState: CheermoteState.animated,
				defaultScale: CheermoteScale.x1
{'fraction_non_alphanumeric': 0.1072901325478645, 'fraction_numerical': 0.001325478645066274, 'mean_word_length': 3.761921458625526, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4651172', 'n_tokens_mistral': 4122, 'n_tokens_neox': 3825, 'n_words': 1460}