@twurple/eventsub-ws
Version:
Listen to events on Twitch via their EventSub API using WebSockets.
165 lines (163 loc) • 6.33 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.EventSubWsListener = void 0;
const tslib_1 = require("tslib");
const api_1 = require("@twurple/api");
const common_1 = require("@twurple/common");
const eventsub_base_1 = require("@twurple/eventsub-base");
const EventSubWsSocket_1 = require("./EventSubWsSocket");
/**
* A WebSocket listener for the Twitch EventSub event distribution mechanism.
*
* @beta
* @hideProtected
* @inheritDoc
*
* @meta category main
*/
let EventSubWsListener = class EventSubWsListener extends eventsub_base_1.EventSubBase {
/**
* Creates a new EventSub WebSocket listener.
*
* @param config
*
* @expandParams
*/
constructor(config) {
var _a;
super(config);
this._sockets = new Map();
this._accepting = false;
/**
* Fires when a user socket has established a connection with the EventSub server.
*
* @param userId The ID of the user.
*/
this.onUserSocketConnect = this.registerEvent();
/**
* Fires when a user socket has disconnected from the EventSub server.
*
* @param userId The ID of the user.
* @param error The error that caused the disconnection, or `undefined` for a clean disconnect.
*/
this.onUserSocketDisconnect = this.registerEvent();
this._initialUrl = this._apiClient._mockServerPort
? `ws://127.0.0.1:${this._apiClient._mockServerPort}/ws`
: (_a = config.url) !== null && _a !== void 0 ? _a : 'wss://eventsub.wss.twitch.tv/ws';
this._loggerOptions = config.logger;
}
/**
* Starts the WebSocket listener.
*/
start() {
this._accepting = true;
const userSocketsToCreate = new Set([...this._subscriptions.values()].map(sub => sub.authUserId));
for (const userId of userSocketsToCreate) {
this._createSocketForUser(userId);
}
}
/**
* Stops the WebSocket listener.
*/
stop() {
this._accepting = false;
for (const socket of this._sockets.values()) {
socket.stop();
}
this._sockets.clear();
}
/** @private */
async _getCliTestCommandForSubscription(subscription) {
if (!this._apiClient._mockServerPort) {
throw new Error(`You must use the mock server from the Twitch CLI to be able to test WebSocket events.
To do so, specify the \`mockServerPort\` option in your \`ApiClient\`.`);
}
const { authUserId } = subscription;
if (!authUserId) {
throw new Error('Can not test a WebSocket subscription for a topic without user authentication');
}
if (!subscription._twitchId) {
throw new Error('Subscription must be registered with the mock server before being able to use this method');
}
const socket = this._sockets.get(authUserId);
if (!socket) {
throw new api_1.HellFreezesOverError(`Can not get appropriate socket for user ${authUserId}`);
}
if (!socket.sessionId) {
throw new api_1.HellFreezesOverError(`Socket for user ${authUserId} does not have a session ID yet`);
}
return `twitch event trigger ${subscription._cliName} -T websocket --session ${socket.sessionId} -u ${subscription._twitchId}`;
}
/** @private */
_isReadyToSubscribe(subscription) {
var _a;
const { authUserId } = subscription;
if (!authUserId) {
throw new Error('Can not create a WebSocket subscription for a topic without user authentication');
}
const socket = this._sockets.get(authUserId);
return (_a = socket === null || socket === void 0 ? void 0 : socket.readyToSubscribe) !== null && _a !== void 0 ? _a : false;
}
/** @private */
async _getTransportOptionsForSubscription(subscription) {
const { authUserId } = subscription;
if (!authUserId) {
throw new Error('Can not create a WebSocket subscription for a topic without user authentication');
}
const socket = this._sockets.get(authUserId);
if (!(socket === null || socket === void 0 ? void 0 : socket.sessionId)) {
throw new api_1.HellFreezesOverError(`Socket for user ${authUserId} is not connected or does not have a session ID yet`);
}
return {
method: 'websocket',
// eslint-disable-next-line @typescript-eslint/naming-convention
session_id: socket.sessionId,
};
}
/** @private */
_getSubscriptionsForUser(userId) {
return [...this._subscriptions.values()].filter(sub => sub.authUserId === userId);
}
/** @private */
_handleSubscriptionRevoke(subscription, status) {
this.emit(this.onRevoke, subscription, status);
}
/** @internal */
_notifySocketConnect(socket) {
this.emit(this.onUserSocketConnect, socket.userId);
}
/** @internal */
_notifySocketDisconnect(socket, error) {
this.emit(this.onUserSocketDisconnect, socket.userId, error);
}
_genericSubscribe(clazz, handler, client, ...params) {
const subscription = super._genericSubscribe(clazz, handler, client, ...params);
const { authUserId } = subscription;
if (!authUserId) {
throw new api_1.HellFreezesOverError('WS subscription created without user ID');
}
if (!this._accepting) {
return subscription;
}
if (this._sockets.has(authUserId)) {
this._sockets.get(authUserId).start();
}
else {
this._createSocketForUser(authUserId);
}
return subscription;
}
_findTwitchSubscriptionToContinue() {
return undefined;
}
/** @internal */
_createSocketForUser(authUserId) {
const socket = new EventSubWsSocket_1.EventSubWsSocket(this, authUserId, this._initialUrl, this._loggerOptions);
this._sockets.set(authUserId, socket);
socket.start();
}
};
exports.EventSubWsListener = EventSubWsListener;
exports.EventSubWsListener = EventSubWsListener = tslib_1.__decorate([
(0, common_1.rtfm)('eventsub-ws', 'EventSubWsListener')
], EventSubWsListener);