UNPKG

twitter-api-v2

Version:

Strongly typed, full-featured, light, versatile yet powerful Twitter API v1.1 and v2 client for Node.js.

202 lines (201 loc) 7.43 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TweetStream = void 0; const events_1 = require("events"); const request_maker_mixin_1 = require("../client-mixins/request-maker.mixin"); const types_1 = require("../types"); const TweetStreamParser_1 = __importStar(require("./TweetStreamParser")); class TweetStream extends events_1.EventEmitter { constructor(req, res, requestData) { super(); this.req = req; this.res = res; this.requestData = requestData; this.autoReconnect = false; this.autoReconnectRetries = 5; this.parser = new TweetStreamParser_1.default(); this.initEventsFromParser(); this.initEventsFromRequest(); } on(event, handler) { return super.on(event, handler); } initEventsFromRequest() { const errorHandler = (err) => { this.emit(types_1.ETwitterStreamEvent.ConnectionError, err); this.emit(types_1.ETwitterStreamEvent.Error, { type: types_1.ETwitterStreamEvent.ConnectionError, error: err, }); this.onConnectionError(); }; this.req.on('error', errorHandler); this.res.on('error', errorHandler); this.res.on('close', () => { this.close(); }); this.res.on('data', chunk => { if (chunk === '\r\n') { return this.emit(types_1.ETwitterStreamEvent.DataKeepAlive); } this.parser.push(chunk); }); } initEventsFromParser() { this.parser.on(TweetStreamParser_1.EStreamParserEvent.ParsedData, (eventData) => { this.emit(types_1.ETwitterStreamEvent.Data, eventData); }); this.parser.on(TweetStreamParser_1.EStreamParserEvent.ParseError, (error) => { this.emit(types_1.ETwitterStreamEvent.TweetParseError, error); this.emit(types_1.ETwitterStreamEvent.Error, { type: types_1.ETwitterStreamEvent.TweetParseError, error, }); }); } unbindRetryTimeout() { if (this.retryTimeout) { clearTimeout(this.retryTimeout); this.retryTimeout = undefined; } } /** Terminate connection to Twitter. */ close() { this.unbindRetryTimeout(); this.emit(types_1.ETwitterStreamEvent.ConnectionClosed); if ('destroy' in this.req) { this.req.destroy(); } else { // Deprecated - use .destroy instead. this.req.abort(); } this.req.removeAllListeners(); this.res.removeAllListeners(); } /** Unbind all listeners, and close connection. */ destroy() { this.removeAllListeners(); this.close(); } /** * Make a new request that creates a new `TweetStream` instance with * the same parameters, and bind current listeners to new stream. */ async clone() { const newRequest = new request_maker_mixin_1.RequestHandlerHelper(this.requestData); const newStream = await newRequest.makeRequestAsStream(); // Clone attached listeners const listenerNames = this.eventNames(); for (const listener of listenerNames) { const callbacks = this.listeners(listener); for (const callback of callbacks) { newStream.on(listener, callback); } } return newStream; } /** Make a new request to reconnect to Twitter. */ async reconnect() { if (!this.req.destroyed) { this.close(); } const { req, res } = await new request_maker_mixin_1.RequestHandlerHelper(this.requestData).makeRequestAndResolveWhenReady(); this.req = req; this.res = res; this.initEventsFromRequest(); } async onConnectionError(retries = this.autoReconnectRetries) { this.unbindRetryTimeout(); if (!this.autoReconnect) { return; } // Request is already destroyed if (this.req.destroyed) { return; } // Close connection silentely if ('destroy' in this.req) { this.req.destroy(); } else { // Deprecated - use .destroy instead. this.req.abort(); } try { await this.reconnect(); } catch (e) { if (retries <= 0) { this.emit(types_1.ETwitterStreamEvent.ReconnectLimitExceeded); return; } this.emit(types_1.ETwitterStreamEvent.ReconnectError, this.autoReconnectRetries - retries); this.makeAutoReconnectRetry(retries); } } makeAutoReconnectRetry(retries) { const tryOccurence = (this.autoReconnectRetries - retries) + 1; const nextRetry = Math.min((tryOccurence ** 2) * 100, 20000); this.retryTimeout = setTimeout(() => { this.onConnectionError(retries - 1); }, nextRetry); } async *[Symbol.asyncIterator]() { let stack = []; const pusher = (data) => { stack.push(data); }; this.on(types_1.ETwitterStreamEvent.Data, pusher); try { while (true) { if (this.req.aborted) { throw new Error('Connection closed'); } if (stack.length) { const toGive = stack; stack = []; yield* toGive; } await new Promise((resolve, reject) => { const rejecter = (error) => { this.off(types_1.ETwitterStreamEvent.Data, resolver); reject(error); }; const resolver = (data) => { this.off(types_1.ETwitterStreamEvent.Error, rejecter); this.off(types_1.ETwitterStreamEvent.ConnectionClosed, rejecter); resolve(data); }; this.once(types_1.ETwitterStreamEvent.Data, resolver); this.once(types_1.ETwitterStreamEvent.Error, rejecter); this.once(types_1.ETwitterStreamEvent.ConnectionClosed, rejecter); }); } } finally { this.off(types_1.ETwitterStreamEvent.Data, pusher); } } } exports.TweetStream = TweetStream; exports.default = TweetStream;