UNPKG

plugin-connections

Version:

Connection management plugin for Twitter authentication and other services

1,306 lines (1,288 loc) 222 kB
// ../../node_modules/twitter-api-v2/dist/esm/globals.js var API_V2_PREFIX = "https://api.x.com/2/"; var API_V2_LABS_PREFIX = "https://api.x.com/labs/2/"; var API_V1_1_PREFIX = "https://api.x.com/1.1/"; var API_V1_1_UPLOAD_PREFIX = "https://upload.x.com/1.1/"; var API_V1_1_STREAM_PREFIX = "https://stream.x.com/1.1/"; var API_ADS_PREFIX = "https://ads-api.x.com/12/"; var API_ADS_SANDBOX_PREFIX = "https://ads-api-sandbox.twitter.com/12/"; // ../../node_modules/twitter-api-v2/dist/esm/paginators/TwitterPaginator.js var TwitterPaginator = class { // noinspection TypeScriptAbstractClassConstructorCanBeMadeProtected constructor({ realData, rateLimit, instance, queryParams, sharedParams }) { this._maxResultsWhenFetchLast = 100; this._realData = realData; this._rateLimit = rateLimit; this._instance = instance; this._queryParams = queryParams; this._sharedParams = sharedParams; } get _isRateLimitOk() { if (!this._rateLimit) { return true; } const resetDate = this._rateLimit.reset * 1e3; if (resetDate < Date.now()) { return true; } return this._rateLimit.remaining > 0; } makeRequest(queryParams) { return this._instance.get(this.getEndpoint(), queryParams, { fullResponse: true, params: this._sharedParams }); } makeNewInstanceFromResult(result, queryParams) { return new this.constructor({ realData: result.data, rateLimit: result.rateLimit, instance: this._instance, queryParams, sharedParams: this._sharedParams }); } getEndpoint() { return this._endpoint; } injectQueryParams(maxResults) { return { ...maxResults ? { max_results: maxResults } : {}, ...this._queryParams }; } /* ---------------------- */ /* Real paginator methods */ /* ---------------------- */ /** * Next page. */ async next(maxResults) { const queryParams = this.getNextQueryParams(maxResults); const result = await this.makeRequest(queryParams); return this.makeNewInstanceFromResult(result, queryParams); } /** * Next page, but store it in current instance. */ async fetchNext(maxResults) { const queryParams = this.getNextQueryParams(maxResults); const result = await this.makeRequest(queryParams); await this.refreshInstanceFromResult(result, true); return this; } /** * Fetch up to {count} items after current page, * as long as rate limit is not hit and Twitter has some results */ async fetchLast(count = Infinity) { let queryParams = this.getNextQueryParams(this._maxResultsWhenFetchLast); let resultCount = 0; while (resultCount < count && this._isRateLimitOk) { const response = await this.makeRequest(queryParams); await this.refreshInstanceFromResult(response, true); resultCount += this.getPageLengthFromRequest(response); if (this.isFetchLastOver(response)) { break; } queryParams = this.getNextQueryParams(this._maxResultsWhenFetchLast); } return this; } get rateLimit() { var _a; return { ...(_a = this._rateLimit) !== null && _a !== void 0 ? _a : {} }; } /** Get raw data returned by Twitter API. */ get data() { return this._realData; } get done() { return !this.canFetchNextPage(this._realData); } /** * Iterate over currently fetched items. */ *[Symbol.iterator]() { yield* this.getItemArray(); } /** * Iterate over items "indefinitely" (until rate limit is hit / they're no more items available) * This will **mutate the current instance** and fill data, metas, etc. inside this instance. * * If you need to handle concurrent requests, or you need to rely on immutability, please use `.fetchAndIterate()` instead. */ async *[Symbol.asyncIterator]() { yield* this.getItemArray(); let paginator = this; let canFetchNextPage = this.canFetchNextPage(this._realData); while (canFetchNextPage && this._isRateLimitOk && paginator.getItemArray().length > 0) { const next = await paginator.next(this._maxResultsWhenFetchLast); this.refreshInstanceFromResult({ data: next._realData, headers: {}, rateLimit: next._rateLimit }, true); canFetchNextPage = this.canFetchNextPage(next._realData); const items = next.getItemArray(); yield* items; paginator = next; } } /** * Iterate over items "indefinitely" without modifying the current instance (until rate limit is hit / they're no more items available) * * This will **NOT** mutate the current instance, meaning that current instance will not inherit from `includes` and `meta` (v2 API only). * Use `Symbol.asyncIterator` (`for-await of`) to directly access items with current instance mutation. */ async *fetchAndIterate() { for (const item of this.getItemArray()) { yield [item, this]; } let paginator = this; let canFetchNextPage = this.canFetchNextPage(this._realData); while (canFetchNextPage && this._isRateLimitOk && paginator.getItemArray().length > 0) { const next = await paginator.next(this._maxResultsWhenFetchLast); this.refreshInstanceFromResult({ data: next._realData, headers: {}, rateLimit: next._rateLimit }, true); canFetchNextPage = this.canFetchNextPage(next._realData); for (const item of next.getItemArray()) { yield [item, next]; } this._rateLimit = next._rateLimit; paginator = next; } } }; var PreviousableTwitterPaginator = class extends TwitterPaginator { /** * Previous page (new tweets) */ async previous(maxResults) { const queryParams = this.getPreviousQueryParams(maxResults); const result = await this.makeRequest(queryParams); return this.makeNewInstanceFromResult(result, queryParams); } /** * Previous page, but in current instance. */ async fetchPrevious(maxResults) { const queryParams = this.getPreviousQueryParams(maxResults); const result = await this.makeRequest(queryParams); await this.refreshInstanceFromResult(result, false); return this; } }; var TwitterPaginator_default = TwitterPaginator; // ../../node_modules/twitter-api-v2/dist/esm/paginators/paginator.v1.js var CursoredV1Paginator = class extends TwitterPaginator_default { getNextQueryParams(maxResults) { var _a; return { ...this._queryParams, cursor: (_a = this._realData.next_cursor_str) !== null && _a !== void 0 ? _a : this._realData.next_cursor, ...maxResults ? { count: maxResults } : {} }; } isFetchLastOver(result) { return !this.canFetchNextPage(result.data); } canFetchNextPage(result) { return !this.isNextCursorInvalid(result.next_cursor) || !this.isNextCursorInvalid(result.next_cursor_str); } isNextCursorInvalid(value) { return value === void 0 || value === 0 || value === -1 || value === "0" || value === "-1"; } }; // ../../node_modules/twitter-api-v2/dist/esm/paginators/dm.paginator.v1.js var DmEventsV1Paginator = class extends CursoredV1Paginator { constructor() { super(...arguments); this._endpoint = "direct_messages/events/list.json"; } refreshInstanceFromResult(response, isNextPage) { const result = response.data; this._rateLimit = response.rateLimit; if (isNextPage) { this._realData.events.push(...result.events); this._realData.next_cursor = result.next_cursor; } } getPageLengthFromRequest(result) { return result.data.events.length; } getItemArray() { return this.events; } /** * Events returned by paginator. */ get events() { return this._realData.events; } }; var WelcomeDmV1Paginator = class extends CursoredV1Paginator { constructor() { super(...arguments); this._endpoint = "direct_messages/welcome_messages/list.json"; } refreshInstanceFromResult(response, isNextPage) { const result = response.data; this._rateLimit = response.rateLimit; if (isNextPage) { this._realData.welcome_messages.push(...result.welcome_messages); this._realData.next_cursor = result.next_cursor; } } getPageLengthFromRequest(result) { return result.data.welcome_messages.length; } getItemArray() { return this.welcomeMessages; } get welcomeMessages() { return this._realData.welcome_messages; } }; // ../../node_modules/twitter-api-v2/dist/esm/types/v1/tweet.v1.types.js var EUploadMimeType; (function(EUploadMimeType2) { EUploadMimeType2["Jpeg"] = "image/jpeg"; EUploadMimeType2["Mp4"] = "video/mp4"; EUploadMimeType2["Mov"] = "video/quicktime"; EUploadMimeType2["Gif"] = "image/gif"; EUploadMimeType2["Png"] = "image/png"; EUploadMimeType2["Srt"] = "text/plain"; EUploadMimeType2["Webp"] = "image/webp"; })(EUploadMimeType || (EUploadMimeType = {})); // ../../node_modules/twitter-api-v2/dist/esm/types/v1/dm.v1.types.js var EDirectMessageEventTypeV1; (function(EDirectMessageEventTypeV12) { EDirectMessageEventTypeV12["Create"] = "message_create"; EDirectMessageEventTypeV12["WelcomeCreate"] = "welcome_message"; })(EDirectMessageEventTypeV1 || (EDirectMessageEventTypeV1 = {})); // ../../node_modules/twitter-api-v2/dist/esm/types/errors.types.js var ETwitterApiError; (function(ETwitterApiError2) { ETwitterApiError2["Request"] = "request"; ETwitterApiError2["PartialResponse"] = "partial-response"; ETwitterApiError2["Response"] = "response"; })(ETwitterApiError || (ETwitterApiError = {})); var ApiError = class extends Error { constructor() { super(...arguments); this.error = true; } }; var ApiRequestError = class extends ApiError { constructor(message, options) { super(message); this.type = ETwitterApiError.Request; Error.captureStackTrace(this, this.constructor); Object.defineProperty(this, "_options", { value: options }); } get request() { return this._options.request; } get requestError() { return this._options.requestError; } toJSON() { return { type: this.type, error: this.requestError }; } }; var ApiPartialResponseError = class extends ApiError { constructor(message, options) { super(message); this.type = ETwitterApiError.PartialResponse; Error.captureStackTrace(this, this.constructor); Object.defineProperty(this, "_options", { value: options }); } get request() { return this._options.request; } get response() { return this._options.response; } get responseError() { return this._options.responseError; } get rawContent() { return this._options.rawContent; } toJSON() { return { type: this.type, error: this.responseError }; } }; var ApiResponseError = class extends ApiError { constructor(message, options) { super(message); this.type = ETwitterApiError.Response; Error.captureStackTrace(this, this.constructor); Object.defineProperty(this, "_options", { value: options }); this.code = options.code; this.headers = options.headers; this.rateLimit = options.rateLimit; if (options.data && typeof options.data === "object" && "error" in options.data && !options.data.errors) { const data = { ...options.data }; data.errors = [{ code: EApiV1ErrorCode.InternalError, message: data.error }]; this.data = data; } else { this.data = options.data; } } get request() { return this._options.request; } get response() { return this._options.response; } /** Check for presence of one of given v1/v2 error codes. */ hasErrorCode(...codes) { const errors = this.errors; if (!(errors === null || errors === void 0 ? void 0 : errors.length)) { return false; } if ("code" in errors[0]) { const v1errors = errors; return v1errors.some((error) => codes.includes(error.code)); } const v2error = this.data; return codes.includes(v2error.type); } get errors() { var _a; return (_a = this.data) === null || _a === void 0 ? void 0 : _a.errors; } get rateLimitError() { return this.code === 420 || this.code === 429; } get isAuthError() { if (this.code === 401) { return true; } return this.hasErrorCode(EApiV1ErrorCode.AuthTimestampInvalid, EApiV1ErrorCode.AuthenticationFail, EApiV1ErrorCode.BadAuthenticationData, EApiV1ErrorCode.InvalidOrExpiredToken); } toJSON() { return { type: this.type, code: this.code, error: this.data, rateLimit: this.rateLimit, headers: this.headers }; } }; var EApiV1ErrorCode; (function(EApiV1ErrorCode2) { EApiV1ErrorCode2[EApiV1ErrorCode2["InvalidCoordinates"] = 3] = "InvalidCoordinates"; EApiV1ErrorCode2[EApiV1ErrorCode2["NoLocationFound"] = 13] = "NoLocationFound"; EApiV1ErrorCode2[EApiV1ErrorCode2["AuthenticationFail"] = 32] = "AuthenticationFail"; EApiV1ErrorCode2[EApiV1ErrorCode2["InvalidOrExpiredToken"] = 89] = "InvalidOrExpiredToken"; EApiV1ErrorCode2[EApiV1ErrorCode2["UnableToVerifyCredentials"] = 99] = "UnableToVerifyCredentials"; EApiV1ErrorCode2[EApiV1ErrorCode2["AuthTimestampInvalid"] = 135] = "AuthTimestampInvalid"; EApiV1ErrorCode2[EApiV1ErrorCode2["BadAuthenticationData"] = 215] = "BadAuthenticationData"; EApiV1ErrorCode2[EApiV1ErrorCode2["NoUserMatch"] = 17] = "NoUserMatch"; EApiV1ErrorCode2[EApiV1ErrorCode2["UserNotFound"] = 50] = "UserNotFound"; EApiV1ErrorCode2[EApiV1ErrorCode2["ResourceNotFound"] = 34] = "ResourceNotFound"; EApiV1ErrorCode2[EApiV1ErrorCode2["TweetNotFound"] = 144] = "TweetNotFound"; EApiV1ErrorCode2[EApiV1ErrorCode2["TweetNotVisible"] = 179] = "TweetNotVisible"; EApiV1ErrorCode2[EApiV1ErrorCode2["NotAllowedResource"] = 220] = "NotAllowedResource"; EApiV1ErrorCode2[EApiV1ErrorCode2["MediaIdNotFound"] = 325] = "MediaIdNotFound"; EApiV1ErrorCode2[EApiV1ErrorCode2["TweetNoLongerAvailable"] = 421] = "TweetNoLongerAvailable"; EApiV1ErrorCode2[EApiV1ErrorCode2["TweetViolatedRules"] = 422] = "TweetViolatedRules"; EApiV1ErrorCode2[EApiV1ErrorCode2["TargetUserSuspended"] = 63] = "TargetUserSuspended"; EApiV1ErrorCode2[EApiV1ErrorCode2["YouAreSuspended"] = 64] = "YouAreSuspended"; EApiV1ErrorCode2[EApiV1ErrorCode2["AccountUpdateFailed"] = 120] = "AccountUpdateFailed"; EApiV1ErrorCode2[EApiV1ErrorCode2["NoSelfSpamReport"] = 36] = "NoSelfSpamReport"; EApiV1ErrorCode2[EApiV1ErrorCode2["NoSelfMute"] = 271] = "NoSelfMute"; EApiV1ErrorCode2[EApiV1ErrorCode2["AccountLocked"] = 326] = "AccountLocked"; EApiV1ErrorCode2[EApiV1ErrorCode2["RateLimitExceeded"] = 88] = "RateLimitExceeded"; EApiV1ErrorCode2[EApiV1ErrorCode2["NoDMRightForApp"] = 93] = "NoDMRightForApp"; EApiV1ErrorCode2[EApiV1ErrorCode2["OverCapacity"] = 130] = "OverCapacity"; EApiV1ErrorCode2[EApiV1ErrorCode2["InternalError"] = 131] = "InternalError"; EApiV1ErrorCode2[EApiV1ErrorCode2["TooManyFollowings"] = 161] = "TooManyFollowings"; EApiV1ErrorCode2[EApiV1ErrorCode2["TweetLimitExceeded"] = 185] = "TweetLimitExceeded"; EApiV1ErrorCode2[EApiV1ErrorCode2["DuplicatedTweet"] = 187] = "DuplicatedTweet"; EApiV1ErrorCode2[EApiV1ErrorCode2["TooManySpamReports"] = 205] = "TooManySpamReports"; EApiV1ErrorCode2[EApiV1ErrorCode2["RequestLooksLikeSpam"] = 226] = "RequestLooksLikeSpam"; EApiV1ErrorCode2[EApiV1ErrorCode2["NoWriteRightForApp"] = 261] = "NoWriteRightForApp"; EApiV1ErrorCode2[EApiV1ErrorCode2["TweetActionsDisabled"] = 425] = "TweetActionsDisabled"; EApiV1ErrorCode2[EApiV1ErrorCode2["TweetRepliesRestricted"] = 433] = "TweetRepliesRestricted"; EApiV1ErrorCode2[EApiV1ErrorCode2["NamedParameterMissing"] = 38] = "NamedParameterMissing"; EApiV1ErrorCode2[EApiV1ErrorCode2["InvalidAttachmentUrl"] = 44] = "InvalidAttachmentUrl"; EApiV1ErrorCode2[EApiV1ErrorCode2["TweetTextTooLong"] = 186] = "TweetTextTooLong"; EApiV1ErrorCode2[EApiV1ErrorCode2["MissingUrlParameter"] = 195] = "MissingUrlParameter"; EApiV1ErrorCode2[EApiV1ErrorCode2["NoMultipleGifs"] = 323] = "NoMultipleGifs"; EApiV1ErrorCode2[EApiV1ErrorCode2["InvalidMediaIds"] = 324] = "InvalidMediaIds"; EApiV1ErrorCode2[EApiV1ErrorCode2["InvalidUrl"] = 407] = "InvalidUrl"; EApiV1ErrorCode2[EApiV1ErrorCode2["TooManyTweetAttachments"] = 386] = "TooManyTweetAttachments"; EApiV1ErrorCode2[EApiV1ErrorCode2["StatusAlreadyFavorited"] = 139] = "StatusAlreadyFavorited"; EApiV1ErrorCode2[EApiV1ErrorCode2["FollowRequestAlreadySent"] = 160] = "FollowRequestAlreadySent"; EApiV1ErrorCode2[EApiV1ErrorCode2["CannotUnmuteANonMutedAccount"] = 272] = "CannotUnmuteANonMutedAccount"; EApiV1ErrorCode2[EApiV1ErrorCode2["TweetAlreadyRetweeted"] = 327] = "TweetAlreadyRetweeted"; EApiV1ErrorCode2[EApiV1ErrorCode2["ReplyToDeletedTweet"] = 385] = "ReplyToDeletedTweet"; EApiV1ErrorCode2[EApiV1ErrorCode2["DMReceiverNotFollowingYou"] = 150] = "DMReceiverNotFollowingYou"; EApiV1ErrorCode2[EApiV1ErrorCode2["UnableToSendDM"] = 151] = "UnableToSendDM"; EApiV1ErrorCode2[EApiV1ErrorCode2["MustAllowDMFromAnyone"] = 214] = "MustAllowDMFromAnyone"; EApiV1ErrorCode2[EApiV1ErrorCode2["CannotSendDMToThisUser"] = 349] = "CannotSendDMToThisUser"; EApiV1ErrorCode2[EApiV1ErrorCode2["DMTextTooLong"] = 354] = "DMTextTooLong"; EApiV1ErrorCode2[EApiV1ErrorCode2["SubscriptionAlreadyExists"] = 355] = "SubscriptionAlreadyExists"; EApiV1ErrorCode2[EApiV1ErrorCode2["CallbackUrlNotApproved"] = 415] = "CallbackUrlNotApproved"; EApiV1ErrorCode2[EApiV1ErrorCode2["SuspendedApplication"] = 416] = "SuspendedApplication"; EApiV1ErrorCode2[EApiV1ErrorCode2["OobOauthIsNotAllowed"] = 417] = "OobOauthIsNotAllowed"; })(EApiV1ErrorCode || (EApiV1ErrorCode = {})); var EApiV2ErrorCode; (function(EApiV2ErrorCode2) { EApiV2ErrorCode2["InvalidRequest"] = "https://developer.x.com/en/support/x-api/error-troubleshooting#invalid-request"; EApiV2ErrorCode2["ClientForbidden"] = "https://developer.x.com/en/support/x-api/error-troubleshooting#client-forbidden"; EApiV2ErrorCode2["UnsupportedAuthentication"] = "https://developer.x.com/en/support/x-api/error-troubleshooting#unsupported-authentication"; EApiV2ErrorCode2["InvalidRules"] = "https://developer.x.com/en/support/x-api/error-troubleshooting#invalid-rules"; EApiV2ErrorCode2["TooManyRules"] = "https://developer.x.com/en/support/x-api/error-troubleshooting#rule-cap"; EApiV2ErrorCode2["DuplicatedRules"] = "https://developer.x.com/en/support/x-api/error-troubleshooting#duplicate-rules"; EApiV2ErrorCode2["RateLimitExceeded"] = "https://developer.x.com/en/support/x-api/error-troubleshooting#usage-capped"; EApiV2ErrorCode2["ConnectionError"] = "https://developer.x.com/en/support/x-api/error-troubleshooting#streaming-connection"; EApiV2ErrorCode2["ClientDisconnected"] = "https://developer.x.com/en/support/x-api/error-troubleshooting#client-disconnected"; EApiV2ErrorCode2["TwitterDisconnectedYou"] = "https://developer.x.com/en/support/x-api/error-troubleshooting#operational-disconnect"; EApiV2ErrorCode2["ResourceNotFound"] = "https://developer.x.com/en/support/x-api/error-troubleshooting#resource-not-found"; EApiV2ErrorCode2["ResourceUnauthorized"] = "https://developer.x.com/en/support/x-api/error-troubleshooting#not-authorized-for-resource"; EApiV2ErrorCode2["DisallowedResource"] = "https://developer.x.com/en/support/x-api/error-troubleshooting#disallowed-resource"; })(EApiV2ErrorCode || (EApiV2ErrorCode = {})); // ../../node_modules/twitter-api-v2/dist/esm/types/client.types.js var ETwitterStreamEvent; (function(ETwitterStreamEvent2) { ETwitterStreamEvent2["Connected"] = "connected"; ETwitterStreamEvent2["ConnectError"] = "connect error"; ETwitterStreamEvent2["ConnectionError"] = "connection error"; ETwitterStreamEvent2["ConnectionClosed"] = "connection closed"; ETwitterStreamEvent2["ConnectionLost"] = "connection lost"; ETwitterStreamEvent2["ReconnectAttempt"] = "reconnect attempt"; ETwitterStreamEvent2["Reconnected"] = "reconnected"; ETwitterStreamEvent2["ReconnectError"] = "reconnect error"; ETwitterStreamEvent2["ReconnectLimitExceeded"] = "reconnect limit exceeded"; ETwitterStreamEvent2["DataKeepAlive"] = "data keep-alive"; ETwitterStreamEvent2["Data"] = "data event content"; ETwitterStreamEvent2["DataError"] = "data twitter error"; ETwitterStreamEvent2["TweetParseError"] = "data tweet parse error"; ETwitterStreamEvent2["Error"] = "stream error"; })(ETwitterStreamEvent || (ETwitterStreamEvent = {})); // ../../node_modules/twitter-api-v2/dist/esm/types/plugins/client.plugins.types.js var TwitterApiPluginResponseOverride = class { constructor(value) { this.value = value; } }; // ../../node_modules/twitter-api-v2/dist/esm/v1/client.v1.write.js import * as fs2 from "fs"; // ../../node_modules/twitter-api-v2/dist/esm/settings.js var TwitterApiV2Settings = { debug: false, deprecationWarnings: true, logger: { log: console.log.bind(console) } }; // ../../node_modules/twitter-api-v2/dist/esm/helpers.js function sharedPromise(getter) { const sharedPromise2 = { value: void 0, promise: getter().then((val) => { sharedPromise2.value = val; return val; }) }; return sharedPromise2; } function arrayWrap(value) { if (Array.isArray(value)) { return value; } return [value]; } function trimUndefinedProperties(object) { for (const parameter in object) { if (object[parameter] === void 0) delete object[parameter]; } } function isTweetStreamV2ErrorPayload(payload) { return typeof payload === "object" && "errors" in payload && !("data" in payload); } function hasMultipleItems(item) { if (Array.isArray(item) && item.length > 1) { return true; } return item.toString().includes(","); } var deprecationWarningsCache = /* @__PURE__ */ new Set(); function safeDeprecationWarning(message) { if (typeof console === "undefined" || !console.warn || !TwitterApiV2Settings.deprecationWarnings) { return; } const hash = `${message.instance}-${message.method}-${message.problem}`; if (deprecationWarningsCache.has(hash)) { return; } const formattedMsg = `[twitter-api-v2] Deprecation warning: In ${message.instance}.${message.method}() call, ${message.problem}. ${message.resolution}.`; console.warn(formattedMsg); console.warn("To disable this message, import variable TwitterApiV2Settings from twitter-api-v2 and set TwitterApiV2Settings.deprecationWarnings to false."); deprecationWarningsCache.add(hash); } // ../../node_modules/twitter-api-v2/dist/esm/stream/TweetStream.js import { EventEmitter as EventEmitter4 } from "events"; // ../../node_modules/twitter-api-v2/dist/esm/client-mixins/request-handler.helper.js import { request } from "https"; import * as zlib from "zlib"; import { EventEmitter } from "events"; var RequestHandlerHelper = class { constructor(requestData) { this.requestData = requestData; this.requestErrorHandled = false; this.responseData = []; } /* Request helpers */ get hrefPathname() { const url = this.requestData.url; return url.hostname + url.pathname; } isCompressionDisabled() { return !this.requestData.compression || this.requestData.compression === "identity"; } isFormEncodedEndpoint() { return this.requestData.url.href.startsWith("https://api.x.com/oauth/"); } /* Error helpers */ createRequestError(error) { if (TwitterApiV2Settings.debug) { TwitterApiV2Settings.logger.log("Request error:", error); } return new ApiRequestError("Request failed.", { request: this.req, error }); } createPartialResponseError(error, abortClose) { const res = this.res; let message = `Request failed with partial response with HTTP code ${res.statusCode}`; if (abortClose) { message += " (connection abruptly closed)"; } else { message += " (parse error)"; } return new ApiPartialResponseError(message, { request: this.req, response: this.res, responseError: error, rawContent: Buffer.concat(this.responseData).toString() }); } formatV1Errors(errors) { return errors.map(({ code, message }) => `${message} (Twitter code ${code})`).join(", "); } formatV2Error(error) { return `${error.title}: ${error.detail} (see ${error.type})`; } createResponseError({ res, data, rateLimit, code }) { var _a; if (TwitterApiV2Settings.debug) { TwitterApiV2Settings.logger.log(`Request failed with code ${code}, data:`, data); TwitterApiV2Settings.logger.log("Response headers:", res.headers); } let errorString = `Request failed with code ${code}`; if ((_a = data === null || data === void 0 ? void 0 : data.errors) === null || _a === void 0 ? void 0 : _a.length) { const errors = data.errors; if (typeof errors[0] === "object" && "code" in errors[0]) { errorString += " - " + this.formatV1Errors(errors); } else { errorString += " - " + this.formatV2Error(data); } } return new ApiResponseError(errorString, { code, data, headers: res.headers, request: this.req, response: res, rateLimit }); } /* Response helpers */ getResponseDataStream(res) { if (this.isCompressionDisabled()) { return res; } const contentEncoding = (res.headers["content-encoding"] || "identity").trim().toLowerCase(); if (contentEncoding === "br") { const brotli = zlib.createBrotliDecompress({ flush: zlib.constants.BROTLI_OPERATION_FLUSH, finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH }); res.pipe(brotli); return brotli; } if (contentEncoding === "gzip") { const gunzip = zlib.createGunzip({ flush: zlib.constants.Z_SYNC_FLUSH, finishFlush: zlib.constants.Z_SYNC_FLUSH }); res.pipe(gunzip); return gunzip; } if (contentEncoding === "deflate") { const inflate = zlib.createInflate({ flush: zlib.constants.Z_SYNC_FLUSH, finishFlush: zlib.constants.Z_SYNC_FLUSH }); res.pipe(inflate); return inflate; } return res; } detectResponseType(res) { var _a, _b; if (((_a = res.headers["content-type"]) === null || _a === void 0 ? void 0 : _a.includes("application/json")) || ((_b = res.headers["content-type"]) === null || _b === void 0 ? void 0 : _b.includes("application/problem+json"))) { return "json"; } else if (this.isFormEncodedEndpoint()) { return "url"; } return "text"; } getParsedResponse(res) { const data = this.responseData; const mode = this.requestData.forceParseMode || this.detectResponseType(res); if (mode === "buffer") { return Buffer.concat(data); } else if (mode === "text") { return Buffer.concat(data).toString(); } else if (mode === "json") { const asText = Buffer.concat(data).toString(); return asText.length ? JSON.parse(asText) : void 0; } else if (mode === "url") { const asText = Buffer.concat(data).toString(); const formEntries = {}; for (const [item, value] of new URLSearchParams(asText)) { formEntries[item] = value; } return formEntries; } else { return void 0; } } getRateLimitFromResponse(res) { let rateLimit = void 0; if (res.headers["x-rate-limit-limit"]) { rateLimit = { limit: Number(res.headers["x-rate-limit-limit"]), remaining: Number(res.headers["x-rate-limit-remaining"]), reset: Number(res.headers["x-rate-limit-reset"]) }; if (res.headers["x-app-limit-24hour-limit"]) { rateLimit.day = { limit: Number(res.headers["x-app-limit-24hour-limit"]), remaining: Number(res.headers["x-app-limit-24hour-remaining"]), reset: Number(res.headers["x-app-limit-24hour-reset"]) }; } if (this.requestData.rateLimitSaver) { this.requestData.rateLimitSaver(rateLimit); } } return rateLimit; } /* Request event handlers */ onSocketEventHandler(reject, cleanupListener, socket) { const onClose = this.onSocketCloseHandler.bind(this, reject); socket.on("close", onClose); cleanupListener.on("complete", () => socket.off("close", onClose)); } onSocketCloseHandler(reject) { this.req.removeAllListeners("timeout"); const res = this.res; if (res) { return; } if (!this.requestErrorHandled) { return reject(this.createRequestError(new Error("Socket closed without any information."))); } } requestErrorHandler(reject, requestError) { var _a, _b; (_b = (_a = this.requestData).requestEventDebugHandler) === null || _b === void 0 ? void 0 : _b.call(_a, "request-error", { requestError }); this.requestErrorHandled = true; reject(this.createRequestError(requestError)); } timeoutErrorHandler() { this.requestErrorHandled = true; this.req.destroy(new Error("Request timeout.")); } /* Response event handlers */ classicResponseHandler(resolve, reject, res) { this.res = res; const dataStream = this.getResponseDataStream(res); dataStream.on("data", (chunk) => this.responseData.push(chunk)); dataStream.on("end", this.onResponseEndHandler.bind(this, resolve, reject)); dataStream.on("close", this.onResponseCloseHandler.bind(this, resolve, reject)); if (this.requestData.requestEventDebugHandler) { this.requestData.requestEventDebugHandler("response", { res }); res.on("aborted", (error) => this.requestData.requestEventDebugHandler("response-aborted", { error })); res.on("error", (error) => this.requestData.requestEventDebugHandler("response-error", { error })); res.on("close", () => this.requestData.requestEventDebugHandler("response-close", { data: this.responseData })); res.on("end", () => this.requestData.requestEventDebugHandler("response-end")); } } onResponseEndHandler(resolve, reject) { const rateLimit = this.getRateLimitFromResponse(this.res); let data; try { data = this.getParsedResponse(this.res); } catch (e) { reject(this.createPartialResponseError(e, false)); return; } const code = this.res.statusCode; if (code >= 400) { reject(this.createResponseError({ data, res: this.res, rateLimit, code })); return; } if (TwitterApiV2Settings.debug) { TwitterApiV2Settings.logger.log(`[${this.requestData.options.method} ${this.hrefPathname}]: Request succeeds with code ${this.res.statusCode}`); TwitterApiV2Settings.logger.log("Response body:", data); } resolve({ data, headers: this.res.headers, rateLimit }); } onResponseCloseHandler(resolve, reject) { const res = this.res; if (res.aborted) { try { this.getParsedResponse(this.res); return this.onResponseEndHandler(resolve, reject); } catch (e) { return reject(this.createPartialResponseError(e, true)); } } if (!res.complete) { return reject(this.createPartialResponseError(new Error("Response has been interrupted before response could be parsed."), true)); } } streamResponseHandler(resolve, reject, res) { const code = res.statusCode; if (code < 400) { if (TwitterApiV2Settings.debug) { TwitterApiV2Settings.logger.log(`[${this.requestData.options.method} ${this.hrefPathname}]: Request succeeds with code ${res.statusCode} (starting stream)`); } const dataStream = this.getResponseDataStream(res); resolve({ req: this.req, res: dataStream, originalResponse: res, requestData: this.requestData }); } else { this.classicResponseHandler(() => void 0, reject, res); } } /* Wrappers for request lifecycle */ debugRequest() { const url = this.requestData.url; TwitterApiV2Settings.logger.log(`[${this.requestData.options.method} ${this.hrefPathname}]`, this.requestData.options); if (url.search) { TwitterApiV2Settings.logger.log("Request parameters:", [...url.searchParams.entries()].map(([key, value]) => `${key}: ${value}`)); } if (this.requestData.body) { TwitterApiV2Settings.logger.log("Request body:", this.requestData.body); } } buildRequest() { var _a; const url = this.requestData.url; const auth = url.username ? `${url.username}:${url.password}` : void 0; const headers = (_a = this.requestData.options.headers) !== null && _a !== void 0 ? _a : {}; if (this.requestData.compression === true || this.requestData.compression === "brotli") { headers["accept-encoding"] = "br;q=1.0, gzip;q=0.8, deflate;q=0.5, *;q=0.1"; } else if (this.requestData.compression === "gzip") { headers["accept-encoding"] = "gzip;q=1, deflate;q=0.5, *;q=0.1"; } else if (this.requestData.compression === "deflate") { headers["accept-encoding"] = "deflate;q=1, *;q=0.1"; } if (TwitterApiV2Settings.debug) { this.debugRequest(); } this.req = request({ ...this.requestData.options, // Define URL params manually, addresses dependencies error https://github.com/PLhery/node-twitter-api-v2/issues/94 host: url.hostname, port: url.port || void 0, path: url.pathname + url.search, protocol: url.protocol, auth, headers }); } registerRequestEventDebugHandlers(req) { req.on("close", () => this.requestData.requestEventDebugHandler("close")); req.on("abort", () => this.requestData.requestEventDebugHandler("abort")); req.on("socket", (socket) => { this.requestData.requestEventDebugHandler("socket", { socket }); socket.on("error", (error) => this.requestData.requestEventDebugHandler("socket-error", { socket, error })); socket.on("connect", () => this.requestData.requestEventDebugHandler("socket-connect", { socket })); socket.on("close", (withError) => this.requestData.requestEventDebugHandler("socket-close", { socket, withError })); socket.on("end", () => this.requestData.requestEventDebugHandler("socket-end", { socket })); socket.on("lookup", (...data) => this.requestData.requestEventDebugHandler("socket-lookup", { socket, data })); socket.on("timeout", () => this.requestData.requestEventDebugHandler("socket-timeout", { socket })); }); } makeRequest() { this.buildRequest(); return new Promise((_resolve, _reject) => { const resolve = (value) => { cleanupListener.emit("complete"); _resolve(value); }; const reject = (value) => { cleanupListener.emit("complete"); _reject(value); }; const cleanupListener = new EventEmitter(); const req = this.req; req.on("error", this.requestErrorHandler.bind(this, reject)); req.on("socket", this.onSocketEventHandler.bind(this, reject, cleanupListener)); req.on("response", this.classicResponseHandler.bind(this, resolve, reject)); if (this.requestData.options.timeout) { req.on("timeout", this.timeoutErrorHandler.bind(this)); } if (this.requestData.requestEventDebugHandler) { this.registerRequestEventDebugHandlers(req); } if (this.requestData.body) { req.write(this.requestData.body); } req.end(); }); } async makeRequestAsStream() { const { req, res, requestData, originalResponse } = await this.makeRequestAndResolveWhenReady(); return new TweetStream_default(requestData, { req, res, originalResponse }); } makeRequestAndResolveWhenReady() { this.buildRequest(); return new Promise((resolve, reject) => { const req = this.req; req.on("error", this.requestErrorHandler.bind(this, reject)); req.on("response", this.streamResponseHandler.bind(this, resolve, reject)); if (this.requestData.body) { req.write(this.requestData.body); } req.end(); }); } }; var request_handler_helper_default = RequestHandlerHelper; // ../../node_modules/twitter-api-v2/dist/esm/stream/TweetStreamEventCombiner.js import { EventEmitter as EventEmitter2 } from "events"; var TweetStreamEventCombiner = class extends EventEmitter2 { constructor(stream) { super(); this.stream = stream; this.stack = []; this.onStreamData = this.onStreamData.bind(this); this.onStreamError = this.onStreamError.bind(this); this.onceNewEvent = this.once.bind(this, "event"); stream.on(ETwitterStreamEvent.Data, this.onStreamData); stream.on(ETwitterStreamEvent.ConnectionError, this.onStreamError); stream.on(ETwitterStreamEvent.TweetParseError, this.onStreamError); stream.on(ETwitterStreamEvent.ConnectionClosed, this.onStreamError); } /** Returns a new `Promise` that will `resolve` on next event (`data` or any sort of error). */ nextEvent() { return new Promise(this.onceNewEvent); } /** Returns `true` if there's something in the stack. */ hasStack() { return this.stack.length > 0; } /** Returns stacked data events, and clean the stack. */ popStack() { const stack = this.stack; this.stack = []; return stack; } /** Cleanup all the listeners attached on stream. */ destroy() { this.removeAllListeners(); this.stream.off(ETwitterStreamEvent.Data, this.onStreamData); this.stream.off(ETwitterStreamEvent.ConnectionError, this.onStreamError); this.stream.off(ETwitterStreamEvent.TweetParseError, this.onStreamError); this.stream.off(ETwitterStreamEvent.ConnectionClosed, this.onStreamError); } emitEvent(type, payload) { this.emit("event", { type, payload }); } onStreamError(payload) { this.emitEvent("error", payload); } onStreamData(payload) { this.stack.push(payload); this.emitEvent("data", payload); } }; var TweetStreamEventCombiner_default = TweetStreamEventCombiner; // ../../node_modules/twitter-api-v2/dist/esm/stream/TweetStreamParser.js import { EventEmitter as EventEmitter3 } from "events"; var TweetStreamParser = class extends EventEmitter3 { constructor() { super(...arguments); this.currentMessage = ""; } // Code partially belongs to twitter-stream-api for this // https://github.com/trygve-lie/twitter-stream-api/blob/master/lib/parser.js push(chunk) { this.currentMessage += chunk; chunk = this.currentMessage; const size = chunk.length; let start = 0; let offset = 0; while (offset < size) { if (chunk.slice(offset, offset + 2) === "\r\n") { const piece = chunk.slice(start, offset); start = offset += 2; if (!piece.length) { continue; } try { const payload = JSON.parse(piece); if (payload) { this.emit(EStreamParserEvent.ParsedData, payload); continue; } } catch (error) { this.emit(EStreamParserEvent.ParseError, error); } } offset++; } this.currentMessage = chunk.slice(start, size); } /** Reset the currently stored message (f.e. on connection reset) */ reset() { this.currentMessage = ""; } }; var EStreamParserEvent; (function(EStreamParserEvent2) { EStreamParserEvent2["ParsedData"] = "parsed data"; EStreamParserEvent2["ParseError"] = "parse error"; })(EStreamParserEvent || (EStreamParserEvent = {})); // ../../node_modules/twitter-api-v2/dist/esm/stream/TweetStream.js var basicRetriesAttempt = [5, 15, 30, 60, 90, 120, 180, 300, 600, 900]; var basicReconnectRetry = (tryOccurrence) => tryOccurrence > basicRetriesAttempt.length ? 901e3 : basicRetriesAttempt[tryOccurrence - 1] * 1e3; var TweetStream = class extends EventEmitter4 { constructor(requestData, connection) { super(); this.requestData = requestData; this.autoReconnect = false; this.autoReconnectRetries = 5; this.keepAliveTimeoutMs = 1e3 * 120; this.nextRetryTimeout = basicReconnectRetry; this.parser = new TweetStreamParser(); this.connectionProcessRunning = false; this.onKeepAliveTimeout = this.onKeepAliveTimeout.bind(this); this.initEventsFromParser(); if (connection) { this.req = connection.req; this.res = connection.res; this.originalResponse = connection.originalResponse; this.initEventsFromRequest(); } } on(event, handler) { return super.on(event, handler); } initEventsFromRequest() { if (!this.req || !this.res) { throw new Error("TweetStream error: You cannot init TweetStream without a request and response object."); } const errorHandler = (err) => { this.emit(ETwitterStreamEvent.ConnectionError, err); this.emit(ETwitterStreamEvent.Error, { type: ETwitterStreamEvent.ConnectionError, error: err, message: "Connection lost or closed by Twitter." }); this.onConnectionError(); }; this.req.on("error", errorHandler); this.res.on("error", errorHandler); this.res.on("close", () => errorHandler(new Error("Connection closed by Twitter."))); this.res.on("data", (chunk) => { this.resetKeepAliveTimeout(); if (chunk.toString() === "\r\n") { return this.emit(ETwitterStreamEvent.DataKeepAlive); } this.parser.push(chunk.toString()); }); this.resetKeepAliveTimeout(); } initEventsFromParser() { const payloadIsError = this.requestData.payloadIsError; this.parser.on(EStreamParserEvent.ParsedData, (eventData) => { if (payloadIsError && payloadIsError(eventData)) { this.emit(ETwitterStreamEvent.DataError, eventData); this.emit(ETwitterStreamEvent.Error, { type: ETwitterStreamEvent.DataError, error: eventData, message: "Twitter sent a payload that is detected as an error payload." }); } else { this.emit(ETwitterStreamEvent.Data, eventData); } }); this.parser.on(EStreamParserEvent.ParseError, (error) => { this.emit(ETwitterStreamEvent.TweetParseError, error); this.emit(ETwitterStreamEvent.Error, { type: ETwitterStreamEvent.TweetParseError, error, message: "Failed to parse stream data." }); }); } resetKeepAliveTimeout() { this.unbindKeepAliveTimeout(); if (this.keepAliveTimeoutMs !== Infinity) { this.keepAliveTimeout = setTimeout(this.onKeepAliveTimeout, this.keepAliveTimeoutMs); } } onKeepAliveTimeout() { this.emit(ETwitterStreamEvent.ConnectionLost); this.onConnectionError(); } unbindTimeouts() { this.unbindRetryTimeout(); this.unbindKeepAliveTimeout(); } unbindKeepAliveTimeout() { if (this.keepAliveTimeout) { clearTimeout(this.keepAliveTimeout); this.keepAliveTimeout = void 0; } } unbindRetryTimeout() { if (this.retryTimeout) { clearTimeout(this.retryTimeout); this.retryTimeout = void 0; } } closeWithoutEmit() { this.unbindTimeouts(); if (this.res) { this.res.removeAllListeners(); this.res.destroy(); } if (this.req) { this.req.removeAllListeners(); this.req.destroy(); } } /** Terminate connection to Twitter. */ close() { this.emit(ETwitterStreamEvent.ConnectionClosed); this.closeWithoutEmit(); } /** 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_handler_helper_default(this.requestData); const newStream = await newRequest.makeRequestAsStream(); 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; } /** Start initial stream connection, setup options on current instance and returns itself. */ async connect(options = {}) { if (typeof options.autoReconnect !== "undefined") { this.autoReconnect = options.autoReconnect; } if (typeof options.autoReconnectRetries !== "undefined") { this.autoReconnectRetries = options.autoReconnectRetries === "unlimited" ? Infinity : options.autoReconnectRetries; } if (typeof options.keepAliveTimeout !== "undefined") { this.keepAliveTimeoutMs = options.keepAliveTimeout === "disable" ? Infinity : options.keepAliveTimeout; } if (typeof options.nextRetryTimeout !== "undefined") { this.nextRetryTimeout = options.nextRetryTimeout; } this.unbindTimeouts(); try { await this.reconnect(); } catch (e) { this.emit(ETwitterStreamEvent.ConnectError, 0); this.emit(ETwitterStreamEvent.Error, { type: ETwitterStreamEvent.ConnectError, error: e, message: "Connect error - Initial connection just failed." }); if (this.autoReconnect) { this.makeAutoReconnectRetry(0, e); } else { throw e; } } return this; } /** Make a new request to (re)connect to Twitter. */ async reconnect() { if (this.connectionProcessRunning) { throw new Error("Connection process is already running."); } this.connectionProcessRunning = true; try { let initialConnection = true; if (this.req) { initialConnection = false; this.closeWithoutEmit(); } const { req, res, originalResponse } = await new request_handler_helper_default(this.requestData).makeRequestAndResolveWhenReady(); this.req = req; this.res = res; this.originalResponse = originalResponse; this.emit(initialConnection ? ETwitterStreamEvent.Connected : ETwitterStreamEvent.Reconnected); this.parser.reset(); this.initEventsFromRequest(); } finally { this.connectionProcessRunning = false; } } async onConnectionError(retryOccurrence = 0) { this.unbindTimeouts(); this.closeWithoutEmit(); if (!this.autoReconnect) { this.emit(ETwitterStreamEvent.ConnectionClosed); return; } if (retryOccurrence >= this.autoReconnectRetries) { this.emit(ETwitterStreamEvent.ReconnectLimitExceeded); this.emit(ETwitterStreamEvent.ConnectionClosed); return; } try { this.emit(ETwitterStreamEvent.ReconnectAttempt, retryOccurrence); await this.reconnect(); } catch (e) { this.emit(ETwitterStreamEvent.ReconnectError, retryOccurrence); this.emit(ETwitterStreamEvent.Error, { type: ETwitterStreamEvent.ReconnectError, error: e, message: `Reconnect error - ${retryOccurrence + 1} attempts made yet.` }); this.makeAutoReconnectRetry(retryOccurrence, e); } } makeAutoReconnectRetry(retryOccurrence, error) { const nextRetry = this.nextRetryTimeout(retryOccurrence + 1, error); this.retryTimeout = setTimeout(() => { this.onConnectionError(retryOccurrence + 1); }, nextRetry); } async *[Symbol.asyncIterator]() { const eventCombiner = new TweetStreamEventCombiner_default(this); try { while (true) { if (!this.req || this.req.aborted) { throw new Error("Connection closed"); } if (eventCombiner.hasStack()) { yield* eventCombiner.popStack(); } const { type, payload } = await eventCombiner.nextEvent(); if (type === "error") { throw payload; } } } finally { eventCombiner.destroy(); } } }; var TweetStream_default = TweetStream; // ../../node_modules/twitter-api-v2/dist/esm/plugins/helpers.js function hasRequestErrorPlugins(client) { var _a; if (!((_a = client.clientSettings.plugins) === null || _a === void 0 ? void 0 : _a.length)) { return false; } for (const plugin of client.clientSettings.plugins) { if (plugin.onRequestError || plugin.onResponseError) { return true; } } return false; } async function applyResponseHooks(requestParams, computedParams, requestOptions, error) { let override; if (error instanceof ApiRequestError || error instanceof ApiPartialResponseError) { override = await this.applyPluginMethod("onRequestError", { client: this, url: this.getUrlObjectFromUrlString(requestParams.url), params: requestParams, computedParams, requestOptions, error }); } else if (error instanceof ApiResponseError) { override = await this.applyPluginMethod("onResponseError", { client: this, url: this.getUrlObjectFromUrlString(requestParams.url), params: requestParams, computedParams, requestOptions, error }); } if (override && override instanceof TwitterApiPluginResponseOverride) { return override.value; } return Promise.reject(error); } // ../../node_modules/twitter-api-v2/dist/esm/client-mixins/oauth1.helper.js import * as crypto from "crypto"; var OAuth1Helper = class _OAuth1Helper { constructor(options) { this.nonceLength = 32; this.consumerKeys = options.consumerKeys; } static percentEncode(str) { return encodeURIComponent(str).replace(/!/g, "%21").replace(/\*/g, "%2A").replace(/'/g, "%27").replace(/\(/g, "%28").replace(/\)/g, "%29"); } hash(base, key) { return crypto.createHmac("sha1", key).update(base).digest("base64"); } authorize(request2, accessTokens = {}) { const oauthInfo = { oauth_consumer_key: this.consumerKeys.key, oauth_nonce: this.getNonce(), oauth_signature_method: "HMAC-SHA1", oauth_timestamp: this.getTimestamp(), oauth_version: "1.0" }; if (accessTokens.key !== void 0) { oauthInfo.oauth_token = accessTokens.key; } if (!request2.data) { request2.data = {}; } oauthInfo.oauth_signature = this.getSignature(request2, accessTokens.secret, oauthInfo); return oauthInfo; } toHeader(oauthInfo) { const sorted = sortObject(oauthInfo); let heade