<commit_before>import { Connection, PersistentConnection, WebSocketConnection } from '@d-fischer/connection';
import Logger, { LogLevel } from '@d-fischer/logger';
import { NonEnumerable, ResolvableValue } from '@d-fischer/shared-utils';
import { EventEmitter, Listener } from '@d-fischer/typed-event-emitter';
import TwitchClient, { AuthProvider, HellFreezesOverError, InvalidTokenError } from 'twitch';
import { PubSubMessageData } from './Messages/PubSubMessage';
import { PubSubIncomingPacket, PubSubNoncedOutgoingPacket, PubSubOutgoingPacket } from './PubSubPacket';

interface NullTokenResolvable {
	type: 'null';
}

interface StaticTokenResolvable {
	type: 'static';
	token: string;
}

interface FunctionTokenResolvable {
	type: 'function';
	function: () => string | Promise<string>;
}

interface ProviderTokenResolvable {
	type: 'provider';
	provider: AuthProvider;
	scopes: string[];
}

type TokenResolvable = NullTokenResolvable | StaticTokenResolvable | FunctionTokenResolvable | ProviderTokenResolvable;

/**
 * A client for the Twitch PubSub interface.
 */
export default class BasicPubSubClient extends EventEmitter {
	@NonEnumerable private readonly _logger: Logger;

	// topic => token
	@NonEnumerable private readonly _topics = new Map<string, TokenResolvable>();

	private _connection: Connection;

	private _pingOnInactivity: number = 60;
	private _pingTimeout: number = 60;
	private _pingCheckTimer?: NodeJS.Timer;
	private _pingTimeoutTimer?: NodeJS.Timer;

	private readonly _onPong: (handler: () => void) => Listener = this.registerEvent();
	private readonly _onResponse: (handler: (nonce: string, error: string) => void) => Listener = this.registerEvent();

	/**
	 * Fires when a message that matches your listening topics is received.
	 *
	 * @eventListener
	 * @param topic The name of the topic.
	 * @param message The message data.
	 */
	readonly onMessage: (
		handler: (topic: string, message: PubSubMessageData) => void
	) => Listener = this.registerEvent();

	/**
	 * Fires when the client finishes establishing a connection to the PubSub server.
	 *
	 * @eventListener
	 */
	readonly onConnect: (handler: () => void) => Listener = this.registerEvent();

	/**
	 * Fires when the client closes its connection to the PubSub server.
	 *
	 * @eventListener
	 * @param isError Whether the cause of the disconnection was an error. A reconnect will be attempted if this is true.
	 */
	readonly onDisconnect: (handler: (isError: boolean, reason?: Error) => void) => Listener = this.registerEvent();

	/**
	 * Fires when the client receives a pong message from the PubSub server.
	 *
	 * @eventListener
	 * @param latency The current latency to the server, in milliseconds.
	 * @param requestTimestampe The time the ping request was sent to the PubSub server.
	 */
	readonly onPong: (handler: (latency: number, requestTimestamp: number) => void) => Listener = this.registerEvent();

	/**
	 * Creates a new PubSub client.
	 *
	 * @param logLevel The level of logging to use for the PubSub client.
	 */
	constructor(logLevel: LogLevel = LogLevel.WARNING) {
		super();
		this._logger = new Logger({
			name: 'twitch-pubsub-client',
			emoji: true,
			minLevel: logLevel
		});

		this._connection = new PersistentConnection(
			WebSocketConnection,
			{ hostName: 'pubsub-edge.twitch.tv', port: 443, secure: true },
			{ logger: this._logger }
		);

		this._connection.onConnect(async () => {
			this._logger.info('Connection established');
			await this._resendListens();
			if (this._topics.size) {
				this._logger.info('Listened to previously registered topics');
				this._logger.debug(`Previously registered topics: ${Array.from(this._topics.keys()).join(', ')}`);
			}
			this._startPingCheckTimer();
			this.emit(this.onConnect);
		});

		this._connection.onReceive((line: string) => {
			this._receiveMessage(line);
			this._startPingCheckTimer();
		});

		this._connection.onDisconnect((manually: boolean, reason?: Error) => {
			if (this._pingCheckTimer) {
				clearTimeout(this._pingCheckTimer);
			}
			if (this._pingTimeoutTimer) {
				clearTimeout(this._pingTimeoutTimer);
			}
			if (manually) {
				this._logger.info('Disconnected');
			} else {
				if (reason) {
					this._logger.err(`Disconnected unexpectedly: ${reason.message}`);
				} else {
					this._logger.err('Disconnected unexpectedly');
				}
			}
			this.emit(this.onDisconnect, manually, reason);
		});
	}

	/**
	 * Listens to one or more topics.
	 *
	 * @param topics A topic or a list of topics to listen to.
	 * @param tokenResolvable An access token, an AuthProvider or a function that returns a token.
	 * @param scope The scope necessary for the topic(s).
	 */
	async listen(
		topics: string | string[],
		tokenResolvable: ResolvableValue<string> | AuthProvider | TokenResolvable | null,
		scope?: string
	) {
		if (typeof topics === 'string') {
			topics = [topics];
		}
<commit_msg>make BasicPubSubClient#listen token resolvable param optional again<commit_after>import { Connection, PersistentConnection, WebSocketConnection } from '@d-fischer/connection';
import Logger, { LogLevel } from '@d-fischer/logger';
import { NonEnumerable, ResolvableValue } from '@d-fischer/shared-utils';
import { EventEmitter, Listener } from '@d-fischer/typed-event-emitter';
import TwitchClient, { AuthProvider, HellFreezesOverError, InvalidTokenError } from 'twitch';
import { PubSubMessageData } from './Messages/PubSubMessage';
import { PubSubIncomingPacket, PubSubNoncedOutgoingPacket, PubSubOutgoingPacket } from './PubSubPacket';

interface NullTokenResolvable {
	type: 'null';
}

interface StaticTokenResolvable {
	type: 'static';
	token: string;
}

interface FunctionTokenResolvable {
	type: 'function';
	function: () => string | Promise<string>;
}

interface ProviderTokenResolvable {
	type: 'provider';
	provider: AuthProvider;
	scopes: string[];
}

type TokenResolvable = NullTokenResolvable | StaticTokenResolvable | FunctionTokenResolvable | ProviderTokenResolvable;

/**
 * A client for the Twitch PubSub interface.
 */
export default class BasicPubSubClient extends EventEmitter {
	@NonEnumerable private readonly _logger: Logger;

	// topic => token
	@NonEnumerable private readonly _topics = new Map<string, TokenResolvable>();

	private _connection: Connection;

	private _pingOnInactivity: number = 60;
	private _pingTimeout: number = 60;
	private _pingCheckTimer?: NodeJS.Timer;
	private _pingTimeoutTimer?: NodeJS.Timer;

	private readonly _onPong: (handler: () => void) => Listener = this.registerEvent();
	private readonly _onResponse: (handler: (nonce: string, error: string) => void) => Listener = this.registerEvent();

	/**
	 * Fires when a message that matches your listening topics is received.
	 *
	 * @eventListener
	 * @param topic The name of the topic.
	 * @param message The message data.
	 */
	readonly onMessage: (
		handler: (topic: string, message: PubSubMessageData) => void
	) => Listener = this.registerEvent();

	/**
	 * Fires when the client finishes establishing a connection to the PubSub server.
	 *
	 * @eventListener
	 */
	readonly onConnect: (handler: () => void) => Listener = this.registerEvent();

	/**
	 * Fires when the client closes its connection to the PubSub server.
	 *
	 * @eventListener
	 * @param isError Whether the cause of the disconnection was an error. A reconnect will be attempted if this is true.
	 */
	readonly onDisconnect: (handler: (isError: boolean, reason?: Error) => void) => Listener = this.registerEvent();

	/**
	 * Fires when the client receives a pong message from the PubSub server.
	 *
	 * @eventListener
	 * @param latency The current latency to the server, in milliseconds.
	 * @param requestTimestampe The time the ping request was sent to the PubSub server.
	 */
	readonly onPong: (handler: (latency: number, requestTimestamp: number) => void) => Listener = this.registerEvent();

	/**
	 * Creates a new PubSub client.
	 *
	 * @param logLevel The level of logging to use for the PubSub client.
	 */
	constructor(logLevel: LogLevel = LogLevel.WARNING) {
		super();
		this._logger = new Logger({
			name: 'twitch-pubsub-client',
			emoji: true,
			minLevel: logLevel
		});

		this._connection = new PersistentConnection(
			WebSocketConnection,
			{ hostName: 'pubsub-edge.twitch.tv', port: 443, secure: true },
			{ logger: this._logger }
		);

		this._connection.onConnect(async () => {
			this._logger.info('Connection established');
			await this._resendListens();
			if (this._topics.size) {
				this._logger.info('Listened to previously registered topics');
				this._logger.debug(`Previously registered topics: ${Array.from(this._topics.keys()).join(', ')}`);
			}
			this._startPingCheckTimer();
			this.emit(this.onConnect);
		});

		this._connection.onReceive((line: string) => {
			this._receiveMessage(line);
			this._startPingCheckTimer();
		});

		this._connection.onDisconnect((manually: boolean, reason?: Error) => {
			if (this._pingCheckTimer) {
				clearTimeout(this._pingCheckTimer);
			}
			if (this._pingTimeoutTimer) {
				clearTimeout(this._pingTimeoutTimer);
			}
			if (manually) {
				this._logger.info('Disconnected');
			} else {
				if (reason) {
					this._logger.err(`Disconnected unexpectedly: ${reason.message}`);
				} else {
					this._logger.err('Disconnected unexpectedly');
				}
			}
			this.emit(this.onDisconnect, manually, reason);
		});
	}

	/**
	 * Listens to one or more topics.
	 *
	 * @param topics A topic or a list of topics to listen to.
	 * @param tokenResolvable An access token, an AuthProvider or a function that returns a token.
	 * @param scope The scope necessary for the topic(s).
	 */
	async listen(
		topics: string | string[],
		tokenResolvable?: ResolvableValue<string> | AuthProvider | TokenResolvable | null,
		scope?: string
	) {
		if (typeof topics === 'string') {
			topics = [topics];
		}

{'fraction_non_alphanumeric': 0.11993088728529322, 'fraction_numerical': 0.0014229088321983943, 'mean_word_length': 4.2874798495432564, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 43, 'https://': 0, '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': '526794', 'n_tokens_mistral': 3174, 'n_tokens_neox': 2852, 'n_words': 844}