1
0
mirror of https://github.com/node-red/node-red-nodes.git synced 2023-10-10 13:36:58 +02:00

Add files via upload

This commit is contained in:
shahramdj 2022-10-07 15:45:45 -04:00 committed by GitHub
parent 8ddcc312b7
commit 91c72d107e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 6467 additions and 0 deletions

View File

@ -0,0 +1,24 @@
/// <reference types="node" />
declare type TStringable = {
toString(): string;
};
export declare class FormDataHelper {
protected _boundary: string;
protected _chunks: Buffer[];
protected _footerChunk?: Buffer;
protected static readonly LINE_BREAK = "\r\n";
protected static readonly DEFAULT_CONTENT_TYPE = "application/octet-stream";
protected bodyAppend(...values: (Buffer | string)[]): void;
append(field: string, value: Buffer | string | TStringable, contentType?: string): void;
getHeaders(): {
'content-type': string;
};
/** Length of form-data (including footer length). */
protected getLength(): number;
getBuffer(): Buffer;
protected getBoundary(): string;
protected generateBoundary(): void;
protected getMultipartHeader(field: string, value: string | Buffer, contentType?: string): string;
protected getMultipartFooter(): Buffer;
}
export {};

View File

@ -0,0 +1,83 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FormDataHelper = void 0;
const helpers_1 = require("../helpers");
// This class is partially inspired by https://github.com/form-data/form-data/blob/master/lib/form_data.js
// All credits to their authors.
class FormDataHelper {
constructor() {
this._boundary = '';
this._chunks = [];
}
bodyAppend(...values) {
const allAsBuffer = values.map(val => val instanceof Buffer ? val : Buffer.from(val));
this._chunks.push(...allAsBuffer);
}
append(field, value, contentType) {
const convertedValue = value instanceof Buffer ? value : value.toString();
const header = this.getMultipartHeader(field, convertedValue, contentType);
this.bodyAppend(header, convertedValue, FormDataHelper.LINE_BREAK);
}
getHeaders() {
return {
'content-type': 'multipart/form-data; boundary=' + this.getBoundary(),
};
}
/** Length of form-data (including footer length). */
getLength() {
return this._chunks.reduce((acc, cur) => acc + cur.length, this.getMultipartFooter().length);
}
getBuffer() {
const allChunks = [...this._chunks, this.getMultipartFooter()];
const totalBuffer = Buffer.alloc(this.getLength());
let i = 0;
for (const chunk of allChunks) {
for (let j = 0; j < chunk.length; i++, j++) {
totalBuffer[i] = chunk[j];
}
}
return totalBuffer;
}
getBoundary() {
if (!this._boundary) {
this.generateBoundary();
}
return this._boundary;
}
generateBoundary() {
// This generates a 50 character boundary similar to those used by Firefox.
let boundary = '--------------------------';
for (let i = 0; i < 24; i++) {
boundary += Math.floor(Math.random() * 10).toString(16);
}
this._boundary = boundary;
}
getMultipartHeader(field, value, contentType) {
// In this lib no need to guess more the content type, octet stream is ok of buffers
if (!contentType) {
contentType = value instanceof Buffer ? FormDataHelper.DEFAULT_CONTENT_TYPE : '';
}
const headers = {
'Content-Disposition': ['form-data', `name="${field}"`],
'Content-Type': contentType,
};
let contents = '';
for (const [prop, header] of Object.entries(headers)) {
// skip nullish headers.
if (!header.length) {
continue;
}
contents += prop + ': ' + (0, helpers_1.arrayWrap)(header).join('; ') + FormDataHelper.LINE_BREAK;
}
return '--' + this.getBoundary() + FormDataHelper.LINE_BREAK + contents + FormDataHelper.LINE_BREAK;
}
getMultipartFooter() {
if (this._footerChunk) {
return this._footerChunk;
}
return this._footerChunk = Buffer.from('--' + this.getBoundary() + '--' + FormDataHelper.LINE_BREAK);
}
}
exports.FormDataHelper = FormDataHelper;
FormDataHelper.LINE_BREAK = '\r\n';
FormDataHelper.DEFAULT_CONTENT_TYPE = 'application/octet-stream';

View File

@ -0,0 +1,40 @@
export interface OAuth1Tokens {
key: string;
secret: string;
}
export interface OAuth1MakerArgs {
consumerKeys: OAuth1Tokens;
}
export interface OAuth1RequestOptions {
url: string;
method: string;
data?: any;
}
export interface OAuth1AuthInfo {
oauth_consumer_key: string;
oauth_nonce: string;
oauth_signature_method: string;
oauth_timestamp: number;
oauth_version: string;
oauth_token: string;
oauth_signature: string;
}
export declare class OAuth1Helper {
nonceLength: number;
protected consumerKeys: OAuth1Tokens;
constructor(options: OAuth1MakerArgs);
static percentEncode(str: string): string;
protected hash(base: string, key: string): string;
authorize(request: OAuth1RequestOptions, accessTokens?: Partial<OAuth1Tokens>): OAuth1AuthInfo;
toHeader(oauthInfo: OAuth1AuthInfo): {
Authorization: string;
};
protected getNonce(): string;
protected getTimestamp(): number;
protected getSignature(request: OAuth1RequestOptions, tokenSecret: string | undefined, oauthInfo: OAuth1AuthInfo): string;
protected getSigningKey(tokenSecret: string | undefined): string;
protected getBaseString(request: OAuth1RequestOptions, oauthInfo: OAuth1AuthInfo): string;
protected getParameterString(request: OAuth1RequestOptions, oauthInfo: OAuth1AuthInfo): string;
protected getBaseUrl(url: string): string;
}
export default OAuth1Helper;

View File

@ -0,0 +1,187 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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.OAuth1Helper = void 0;
const crypto = __importStar(require("crypto"));
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(request, 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 !== undefined) {
oauthInfo.oauth_token = accessTokens.key;
}
if (!request.data) {
request.data = {};
}
oauthInfo.oauth_signature = this.getSignature(request, accessTokens.secret, oauthInfo);
return oauthInfo;
}
toHeader(oauthInfo) {
const sorted = sortObject(oauthInfo);
let header_value = 'OAuth ';
for (const element of sorted) {
if (element.key.indexOf('oauth_') !== 0) {
continue;
}
header_value += OAuth1Helper.percentEncode(element.key) + '="' + OAuth1Helper.percentEncode(element.value) + '",';
}
return {
// Remove the last ,
Authorization: header_value.slice(0, header_value.length - 1),
};
}
getNonce() {
const wordCharacters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let result = '';
for (let i = 0; i < this.nonceLength; i++) {
result += wordCharacters[Math.trunc(Math.random() * wordCharacters.length)];
}
return result;
}
getTimestamp() {
return Math.trunc(new Date().getTime() / 1000);
}
getSignature(request, tokenSecret, oauthInfo) {
return this.hash(this.getBaseString(request, oauthInfo), this.getSigningKey(tokenSecret));
}
getSigningKey(tokenSecret) {
return OAuth1Helper.percentEncode(this.consumerKeys.secret) + '&' + OAuth1Helper.percentEncode(tokenSecret || '');
}
getBaseString(request, oauthInfo) {
return request.method.toUpperCase() + '&'
+ OAuth1Helper.percentEncode(this.getBaseUrl(request.url)) + '&'
+ OAuth1Helper.percentEncode(this.getParameterString(request, oauthInfo));
}
getParameterString(request, oauthInfo) {
const baseStringData = sortObject(percentEncodeData(mergeObject(oauthInfo, mergeObject(request.data, deParamUrl(request.url)))));
let dataStr = '';
for (const { key, value } of baseStringData) {
// check if the value is an array
// this means that this key has multiple values
if (value && Array.isArray(value)) {
// sort the array first
value.sort();
let valString = '';
// serialize all values for this key: e.g. formkey=formvalue1&formkey=formvalue2
value.forEach((item, i) => {
valString += key + '=' + item;
if (i < value.length) {
valString += '&';
}
});
dataStr += valString;
}
else {
dataStr += key + '=' + value + '&';
}
}
// Remove the last character
return dataStr.slice(0, dataStr.length - 1);
}
getBaseUrl(url) {
return url.split('?')[0];
}
}
exports.OAuth1Helper = OAuth1Helper;
exports.default = OAuth1Helper;
// Helper functions //
function mergeObject(obj1, obj2) {
return {
...obj1 || {},
...obj2 || {},
};
}
function sortObject(data) {
return Object.keys(data)
.sort()
.map(key => ({ key, value: data[key] }));
}
function deParam(string) {
const splitted = string.split('&');
const data = {};
for (const coupleKeyValue of splitted) {
const [key, value = ''] = coupleKeyValue.split('=');
// check if the key already exists
// this can occur if the QS part of the url contains duplicate keys like this: ?formkey=formvalue1&formkey=formvalue2
if (data[key]) {
// the key exists already
if (!Array.isArray(data[key])) {
// replace the value with an array containing the already present value
data[key] = [data[key]];
}
// and add the new found value to it
data[key].push(decodeURIComponent(value));
}
else {
// it doesn't exist, just put the found value in the data object
data[key] = decodeURIComponent(value);
}
}
return data;
}
function deParamUrl(url) {
const tmp = url.split('?');
if (tmp.length === 1)
return {};
return deParam(tmp[1]);
}
function percentEncodeData(data) {
const result = {};
for (const key in data) {
let value = data[key];
// check if the value is an array
if (value && Array.isArray(value)) {
value = value.map(v => OAuth1Helper.percentEncode(v));
}
else {
value = OAuth1Helper.percentEncode(value);
}
result[OAuth1Helper.percentEncode(key)] = value;
}
return result;
}

View File

@ -0,0 +1,7 @@
export declare class OAuth2Helper {
static getCodeVerifier(): string;
static getCodeChallengeFromVerifier(verifier: string): string;
static getAuthHeader(clientId: string, clientSecret: string): string;
static generateRandomString(length: number): string;
private static escapeBase64Url;
}

View File

@ -0,0 +1,54 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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.OAuth2Helper = void 0;
const crypto = __importStar(require("crypto"));
class OAuth2Helper {
static getCodeVerifier() {
return this.generateRandomString(128);
}
static getCodeChallengeFromVerifier(verifier) {
return this.escapeBase64Url(crypto
.createHash('sha256')
.update(verifier)
.digest('base64'));
}
static getAuthHeader(clientId, clientSecret) {
const key = encodeURIComponent(clientId) + ':' + encodeURIComponent(clientSecret);
return Buffer.from(key).toString('base64');
}
static generateRandomString(length) {
let text = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
for (let i = 0; i < length; i++) {
text += possible[Math.floor(Math.random() * possible.length)];
}
return text;
}
static escapeBase64Url(string) {
return string.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
}
}
exports.OAuth2Helper = OAuth2Helper;

View File

@ -0,0 +1,60 @@
/// <reference types="node" />
import type { Socket } from 'net';
import type { IncomingMessage, ClientRequest } from 'http';
import TweetStream from '../stream/TweetStream';
import { ApiPartialResponseError, ApiRequestError, ApiResponseError } from '../types';
import type { ErrorV1, ErrorV2, TwitterRateLimit, TwitterResponse } from '../types';
import type { TRequestFullData, TRequestFullStreamData, TResponseParseMode } from '../types/request-maker.mixin.types';
import * as zlib from 'zlib';
import { Readable } from 'stream';
declare type TRequestReadyPayload = {
req: ClientRequest;
res: Readable;
originalResponse: IncomingMessage;
requestData: TRequestFullData | TRequestFullStreamData;
};
declare type TReadyRequestResolver = (value: TRequestReadyPayload) => void;
declare type TResponseResolver<T> = (value: TwitterResponse<T>) => void;
declare type TRequestRejecter = (error: ApiRequestError) => void;
declare type TResponseRejecter = (error: ApiResponseError | ApiPartialResponseError) => void;
interface IBuildErrorParams {
res: IncomingMessage;
data: any;
rateLimit?: TwitterRateLimit;
code: number;
}
export declare class RequestHandlerHelper<T> {
protected requestData: TRequestFullData | TRequestFullStreamData;
protected req: ClientRequest;
protected res: IncomingMessage;
protected requestErrorHandled: boolean;
protected responseData: Buffer[];
constructor(requestData: TRequestFullData | TRequestFullStreamData);
get hrefPathname(): string;
protected isCompressionDisabled(): boolean;
protected isFormEncodedEndpoint(): boolean;
protected createRequestError(error: Error): ApiRequestError;
protected createPartialResponseError(error: Error, abortClose: boolean): ApiPartialResponseError;
protected formatV1Errors(errors: ErrorV1[]): string;
protected formatV2Error(error: ErrorV2): string;
protected createResponseError({ res, data, rateLimit, code }: IBuildErrorParams): ApiResponseError;
protected getResponseDataStream(res: IncomingMessage): IncomingMessage | zlib.BrotliDecompress;
protected detectResponseType(res: IncomingMessage): TResponseParseMode;
protected getParsedResponse(res: IncomingMessage): any;
protected getRateLimitFromResponse(res: IncomingMessage): TwitterRateLimit | undefined;
protected onSocketEventHandler(reject: TRequestRejecter, socket: Socket): void;
protected onSocketCloseHandler(reject: TRequestRejecter): void;
protected requestErrorHandler(reject: TRequestRejecter, requestError: Error): void;
protected timeoutErrorHandler(): void;
protected classicResponseHandler(resolve: TResponseResolver<T>, reject: TResponseRejecter, res: IncomingMessage): void;
protected onResponseEndHandler(resolve: TResponseResolver<T>, reject: TResponseRejecter): void;
protected onResponseCloseHandler(resolve: TResponseResolver<T>, reject: TResponseRejecter): void;
protected streamResponseHandler(resolve: TReadyRequestResolver, reject: TResponseRejecter, res: IncomingMessage): void;
protected debugRequest(): void;
protected buildRequest(): void;
protected registerRequestEventDebugHandlers(req: ClientRequest): void;
makeRequest(): Promise<TwitterResponse<T>>;
makeRequestAsStream(): Promise<TweetStream<T>>;
makeRequestAndResolveWhenReady(): Promise<TRequestReadyPayload>;
}
export default RequestHandlerHelper;

View File

@ -0,0 +1,391 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RequestHandlerHelper = void 0;
const https_1 = require("https");
const settings_1 = require("../settings");
const TweetStream_1 = __importDefault(require("../stream/TweetStream"));
const types_1 = require("../types");
const zlib = __importStar(require("zlib"));
class RequestHandlerHelper {
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.twitter.com/oauth/');
}
/* Error helpers */
createRequestError(error) {
if (settings_1.TwitterApiV2Settings.debug) {
settings_1.TwitterApiV2Settings.logger.log('Request error:', error);
}
return new types_1.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 types_1.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 (settings_1.TwitterApiV2Settings.debug) {
settings_1.TwitterApiV2Settings.logger.log(`Request failed with code ${code}, data:`, data);
settings_1.TwitterApiV2Settings.logger.log('Response headers:', res.headers);
}
// Errors formatting.
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 ('code' in errors[0]) {
errorString += ' - ' + this.formatV1Errors(errors);
}
else {
errorString += ' - ' + this.formatV2Error(data);
}
}
return new types_1.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;
// Auto parse if server responds with JSON body
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';
}
// f-e oauth token endpoints
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) : undefined;
}
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 {
// mode === 'none'
return undefined;
}
}
getRateLimitFromResponse(res) {
let rateLimit = undefined;
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 (this.requestData.rateLimitSaver) {
this.requestData.rateLimitSaver(rateLimit);
}
}
return rateLimit;
}
/* Request event handlers */
onSocketEventHandler(reject, socket) {
socket.on('close', this.onSocketCloseHandler.bind(this, reject));
}
onSocketCloseHandler(reject) {
this.req.removeAllListeners('timeout');
const res = this.res;
if (res) {
// Response ok, res.close/res.end can handle request ending
return;
}
if (!this.requestErrorHandled) {
return reject(this.createRequestError(new Error('Socket closed without any information.')));
}
// else: other situation
}
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);
// Register the response data
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));
// Debug handlers
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;
}
// Handle bad error codes
const code = this.res.statusCode;
if (code >= 400) {
reject(this.createResponseError({ data, res: this.res, rateLimit, code }));
return;
}
if (settings_1.TwitterApiV2Settings.debug) {
settings_1.TwitterApiV2Settings.logger.log(`[${this.requestData.options.method} ${this.hrefPathname}]: Request succeeds with code ${this.res.statusCode}`);
settings_1.TwitterApiV2Settings.logger.log('Response body:', data);
}
resolve({
data,
headers: this.res.headers,
rateLimit,
});
}
onResponseCloseHandler(resolve, reject) {
const res = this.res;
if (res.aborted) {
// Try to parse the request (?)
try {
this.getParsedResponse(this.res);
// Ok, try to resolve normally the request
return this.onResponseEndHandler(resolve, reject);
}
catch (e) {
// Parse error, just drop with content
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));
}
// else: end has been called
}
streamResponseHandler(resolve, reject, res) {
const code = res.statusCode;
if (code < 400) {
if (settings_1.TwitterApiV2Settings.debug) {
settings_1.TwitterApiV2Settings.logger.log(`[${this.requestData.options.method} ${this.hrefPathname}]: Request succeeds with code ${res.statusCode} (starting stream)`);
}
const dataStream = this.getResponseDataStream(res);
// HTTP code ok, consume stream
resolve({ req: this.req, res: dataStream, originalResponse: res, requestData: this.requestData });
}
else {
// Handle response normally, can only rejects
this.classicResponseHandler(() => undefined, reject, res);
}
}
/* Wrappers for request lifecycle */
debugRequest() {
const url = this.requestData.url;
settings_1.TwitterApiV2Settings.logger.log(`[${this.requestData.options.method} ${this.hrefPathname}]`, this.requestData.options);
if (url.search) {
settings_1.TwitterApiV2Settings.logger.log('Request parameters:', [...url.searchParams.entries()].map(([key, value]) => `${key}: ${value}`));
}
if (this.requestData.body) {
settings_1.TwitterApiV2Settings.logger.log('Request body:', this.requestData.body);
}
}
buildRequest() {
var _a;
const url = this.requestData.url;
const auth = url.username ? `${url.username}:${url.password}` : undefined;
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 (settings_1.TwitterApiV2Settings.debug) {
this.debugRequest();
}
this.req = (0, https_1.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 || undefined,
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 req = this.req;
// Handle request errors
req.on('error', this.requestErrorHandler.bind(this, reject));
req.on('socket', this.onSocketEventHandler.bind(this, reject));
req.on('response', this.classicResponseHandler.bind(this, resolve, reject));
if (this.requestData.options.timeout) {
req.on('timeout', this.timeoutErrorHandler.bind(this));
}
// Debug handlers
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_1.default(requestData, { req, res, originalResponse });
}
makeRequestAndResolveWhenReady() {
this.buildRequest();
return new Promise((resolve, reject) => {
const req = this.req;
// Handle request errors
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();
});
}
}
exports.RequestHandlerHelper = RequestHandlerHelper;
exports.default = RequestHandlerHelper;

View File

@ -0,0 +1,58 @@
/// <reference types="node" />
import { IClientSettings, ITwitterApiClientPlugin, TClientTokens, TwitterApiPluginResponseOverride, TwitterRateLimit, TwitterResponse } from '../types';
import TweetStream from '../stream/TweetStream';
import type { ClientRequestArgs } from 'http';
import OAuth1Helper from './oauth1.helper';
import type { IGetHttpRequestArgs, IGetStreamRequestArgs, IGetStreamRequestArgsAsync, IGetStreamRequestArgsSync, IWriteAuthHeadersArgs, TAcceptedInitToken } from '../types/request-maker.mixin.types';
import { IComputedHttpRequestArgs } from '../types/request-maker.mixin.types';
export declare class ClientRequestMaker {
bearerToken?: string;
consumerToken?: string;
consumerSecret?: string;
accessToken?: string;
accessSecret?: string;
basicToken?: string;
clientId?: string;
clientSecret?: string;
rateLimits: {
[endpoint: string]: TwitterRateLimit;
};
clientSettings: Partial<IClientSettings>;
protected _oauth?: OAuth1Helper;
protected static readonly BODY_METHODS: Set<string>;
constructor(settings?: Partial<IClientSettings>);
/** @deprecated - Switch to `@twitter-api-v2/plugin-rate-limit` */
getRateLimits(): {
[endpoint: string]: TwitterRateLimit;
};
protected saveRateLimit(originalUrl: string, rateLimit: TwitterRateLimit): void;
/** Send a new request and returns a wrapped `Promise<TwitterResponse<T>`. */
send<T = any>(requestParams: IGetHttpRequestArgs): Promise<TwitterResponse<T>>;
/**
* Create a new request, then creates a stream from it as a `TweetStream`.
*
* Request will be sent only if `autoConnect` is not set or `true`: return type will be `Promise<TweetStream>`.
* If `autoConnect` is `false`, a `TweetStream` is directly returned and you should call `stream.connect()` by yourself.
*/
sendStream<T = any>(requestParams: IGetHttpRequestArgs & IGetStreamRequestArgsSync): TweetStream<T>;
sendStream<T = any>(requestParams: IGetHttpRequestArgs & IGetStreamRequestArgsAsync): Promise<TweetStream<T>>;
sendStream<T = any>(requestParams: IGetHttpRequestArgs & IGetStreamRequestArgs): Promise<TweetStream<T>> | TweetStream<T>;
initializeToken(token?: TAcceptedInitToken): void;
getActiveTokens(): TClientTokens;
protected buildOAuth(): OAuth1Helper;
protected getOAuthAccessTokens(): {
key: string;
secret: string;
} | undefined;
getPlugins(): ITwitterApiClientPlugin[];
hasPlugins(): boolean;
applyPluginMethod<K extends keyof ITwitterApiClientPlugin>(method: K, args: Parameters<Required<ITwitterApiClientPlugin>[K]>[0]): Promise<TwitterApiPluginResponseOverride | undefined>;
protected writeAuthHeaders({ headers, bodyInSignature, url, method, query, body }: IWriteAuthHeadersArgs): Record<string, string>;
protected getUrlObjectFromUrlString(url: string): URL;
protected getHttpRequestArgs({ url: stringUrl, method, query: rawQuery, body: rawBody, headers, forceBodyMode, enableAuth, params, }: IGetHttpRequestArgs): IComputedHttpRequestArgs;
protected applyPreRequestConfigHooks(requestParams: IGetHttpRequestArgs): Promise<TwitterResponse<any> | undefined>;
protected applyPreStreamRequestConfigHooks(requestParams: IGetHttpRequestArgs): void;
protected applyPreRequestHooks(requestParams: IGetHttpRequestArgs, computedParams: IComputedHttpRequestArgs, requestOptions: Partial<ClientRequestArgs>): Promise<void>;
protected applyPostRequestHooks(requestParams: IGetHttpRequestArgs, computedParams: IComputedHttpRequestArgs, requestOptions: Partial<ClientRequestArgs>, response: TwitterResponse<any>): Promise<TwitterApiPluginResponseOverride | undefined>;
protected applyResponseErrorHooks(requestParams: IGetHttpRequestArgs, computedParams: IComputedHttpRequestArgs, requestOptions: Partial<ClientRequestArgs>, promise: Promise<TwitterResponse<any>>): Promise<TwitterResponse<any>>;
}

View File

@ -0,0 +1,323 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClientRequestMaker = void 0;
const types_1 = require("../types");
const TweetStream_1 = __importDefault(require("../stream/TweetStream"));
const helpers_1 = require("../plugins/helpers");
const helpers_2 = require("../helpers");
const oauth1_helper_1 = __importDefault(require("./oauth1.helper"));
const request_handler_helper_1 = __importDefault(require("./request-handler.helper"));
const request_param_helper_1 = __importDefault(require("./request-param.helper"));
const oauth2_helper_1 = require("./oauth2.helper");
class ClientRequestMaker {
constructor(settings) {
this.rateLimits = {};
this.clientSettings = {};
if (settings) {
this.clientSettings = settings;
}
}
/** @deprecated - Switch to `@twitter-api-v2/plugin-rate-limit` */
getRateLimits() {
return this.rateLimits;
}
saveRateLimit(originalUrl, rateLimit) {
this.rateLimits[originalUrl] = rateLimit;
}
/** Send a new request and returns a wrapped `Promise<TwitterResponse<T>`. */
async send(requestParams) {
var _a, _b, _c, _d, _e;
// Pre-request config hooks
if ((_a = this.clientSettings.plugins) === null || _a === void 0 ? void 0 : _a.length) {
const possibleResponse = await this.applyPreRequestConfigHooks(requestParams);
if (possibleResponse) {
return possibleResponse;
}
}
const args = this.getHttpRequestArgs(requestParams);
const options = {
method: args.method,
headers: args.headers,
timeout: requestParams.timeout,
agent: this.clientSettings.httpAgent,
};
const enableRateLimitSave = requestParams.enableRateLimitSave !== false;
if (args.body) {
request_param_helper_1.default.setBodyLengthHeader(options, args.body);
}
// Pre-request hooks
if ((_b = this.clientSettings.plugins) === null || _b === void 0 ? void 0 : _b.length) {
await this.applyPreRequestHooks(requestParams, args, options);
}
let request = new request_handler_helper_1.default({
url: args.url,
options,
body: args.body,
rateLimitSaver: enableRateLimitSave ? this.saveRateLimit.bind(this, args.rawUrl) : undefined,
requestEventDebugHandler: requestParams.requestEventDebugHandler,
compression: (_d = (_c = requestParams.compression) !== null && _c !== void 0 ? _c : this.clientSettings.compression) !== null && _d !== void 0 ? _d : true,
forceParseMode: requestParams.forceParseMode,
})
.makeRequest();
if ((0, helpers_1.hasRequestErrorPlugins)(this)) {
request = this.applyResponseErrorHooks(requestParams, args, options, request);
}
const response = await request;
// Post-request hooks
if ((_e = this.clientSettings.plugins) === null || _e === void 0 ? void 0 : _e.length) {
const responseOverride = await this.applyPostRequestHooks(requestParams, args, options, response);
if (responseOverride) {
return responseOverride.value;
}
}
return response;
}
sendStream(requestParams) {
var _a, _b;
// Pre-request hooks
if (this.clientSettings.plugins) {
this.applyPreStreamRequestConfigHooks(requestParams);
}
const args = this.getHttpRequestArgs(requestParams);
const options = {
method: args.method,
headers: args.headers,
agent: this.clientSettings.httpAgent,
};
const enableRateLimitSave = requestParams.enableRateLimitSave !== false;
const enableAutoConnect = requestParams.autoConnect !== false;
if (args.body) {
request_param_helper_1.default.setBodyLengthHeader(options, args.body);
}
const requestData = {
url: args.url,
options,
body: args.body,
rateLimitSaver: enableRateLimitSave ? this.saveRateLimit.bind(this, args.rawUrl) : undefined,
payloadIsError: requestParams.payloadIsError,
compression: (_b = (_a = requestParams.compression) !== null && _a !== void 0 ? _a : this.clientSettings.compression) !== null && _b !== void 0 ? _b : true,
};
const stream = new TweetStream_1.default(requestData);
if (!enableAutoConnect) {
return stream;
}
return stream.connect();
}
/* Token helpers */
initializeToken(token) {
if (typeof token === 'string') {
this.bearerToken = token;
}
else if (typeof token === 'object' && 'appKey' in token) {
this.consumerToken = token.appKey;
this.consumerSecret = token.appSecret;
if (token.accessToken && token.accessSecret) {
this.accessToken = token.accessToken;
this.accessSecret = token.accessSecret;
}
this._oauth = this.buildOAuth();
}
else if (typeof token === 'object' && 'username' in token) {
const key = encodeURIComponent(token.username) + ':' + encodeURIComponent(token.password);
this.basicToken = Buffer.from(key).toString('base64');
}
else if (typeof token === 'object' && 'clientId' in token) {
this.clientId = token.clientId;
this.clientSecret = token.clientSecret;
}
}
getActiveTokens() {
if (this.bearerToken) {
return {
type: 'oauth2',
bearerToken: this.bearerToken,
};
}
else if (this.basicToken) {
return {
type: 'basic',
token: this.basicToken,
};
}
else if (this.consumerSecret && this._oauth) {
return {
type: 'oauth-1.0a',
appKey: this.consumerToken,
appSecret: this.consumerSecret,
accessToken: this.accessToken,
accessSecret: this.accessSecret,
};
}
else if (this.clientId) {
return {
type: 'oauth2-user',
clientId: this.clientId,
};
}
return { type: 'none' };
}
buildOAuth() {
if (!this.consumerSecret || !this.consumerToken)
throw new Error('Invalid consumer tokens');
return new oauth1_helper_1.default({
consumerKeys: { key: this.consumerToken, secret: this.consumerSecret },
});
}
getOAuthAccessTokens() {
if (!this.accessSecret || !this.accessToken)
return;
return {
key: this.accessToken,
secret: this.accessSecret,
};
}
/* Plugin helpers */
getPlugins() {
var _a;
return (_a = this.clientSettings.plugins) !== null && _a !== void 0 ? _a : [];
}
hasPlugins() {
var _a;
return !!((_a = this.clientSettings.plugins) === null || _a === void 0 ? void 0 : _a.length);
}
async applyPluginMethod(method, args) {
var _a;
let returnValue;
for (const plugin of this.getPlugins()) {
const value = await ((_a = plugin[method]) === null || _a === void 0 ? void 0 : _a.call(plugin, args));
if (value && value instanceof types_1.TwitterApiPluginResponseOverride) {
returnValue = value;
}
}
return returnValue;
}
/* Request helpers */
writeAuthHeaders({ headers, bodyInSignature, url, method, query, body }) {
headers = { ...headers };
if (this.bearerToken) {
headers.Authorization = 'Bearer ' + this.bearerToken;
}
else if (this.basicToken) {
// Basic auth, to request a bearer token
headers.Authorization = 'Basic ' + this.basicToken;
}
else if (this.clientId && this.clientSecret) {
// Basic auth with clientId + clientSecret
headers.Authorization = 'Basic ' + oauth2_helper_1.OAuth2Helper.getAuthHeader(this.clientId, this.clientSecret);
}
else if (this.consumerSecret && this._oauth) {
// Merge query and body
const data = bodyInSignature ? request_param_helper_1.default.mergeQueryAndBodyForOAuth(query, body) : query;
const auth = this._oauth.authorize({
url: url.toString(),
method,
data,
}, this.getOAuthAccessTokens());
headers = { ...headers, ...this._oauth.toHeader(auth) };
}
return headers;
}
getUrlObjectFromUrlString(url) {
// Add protocol to URL if needed
if (!url.startsWith('http')) {
url = 'https://' + url;
}
// Convert URL to object that will receive all URL modifications
return new URL(url);
}
getHttpRequestArgs({ url: stringUrl, method, query: rawQuery = {}, body: rawBody = {}, headers, forceBodyMode, enableAuth, params, }) {
let body = undefined;
method = method.toUpperCase();
headers = headers !== null && headers !== void 0 ? headers : {};
// Add user agent header (Twitter recommends it)
if (!headers['x-user-agent']) {
headers['x-user-agent'] = 'Node.twitter-api-v2';
}
const url = this.getUrlObjectFromUrlString(stringUrl);
// URL without query string to save as endpoint name
const rawUrl = url.origin + url.pathname;
// Apply URL parameters
if (params) {
request_param_helper_1.default.applyRequestParametersToUrl(url, params);
}
// Build a URL without anything in QS, and QSP in query
const query = request_param_helper_1.default.formatQueryToString(rawQuery);
request_param_helper_1.default.moveUrlQueryParamsIntoObject(url, query);
// Delete undefined parameters
if (!(rawBody instanceof Buffer)) {
(0, helpers_2.trimUndefinedProperties)(rawBody);
}
// OAuth signature should not include parameters when using multipart.
const bodyType = forceBodyMode !== null && forceBodyMode !== void 0 ? forceBodyMode : request_param_helper_1.default.autoDetectBodyType(url);
// If undefined or true, enable auth by headers
if (enableAuth !== false) {
// OAuth needs body signature only if body is URL encoded.
const bodyInSignature = ClientRequestMaker.BODY_METHODS.has(method) && bodyType === 'url';
headers = this.writeAuthHeaders({ headers, bodyInSignature, method, query, url, body: rawBody });
}
if (ClientRequestMaker.BODY_METHODS.has(method)) {
body = request_param_helper_1.default.constructBodyParams(rawBody, headers, bodyType) || undefined;
}
request_param_helper_1.default.addQueryParamsToUrl(url, query);
return {
rawUrl,
url,
method,
headers,
body,
};
}
/* Plugin helpers */
async applyPreRequestConfigHooks(requestParams) {
var _a;
const url = this.getUrlObjectFromUrlString(requestParams.url);
for (const plugin of this.getPlugins()) {
const result = await ((_a = plugin.onBeforeRequestConfig) === null || _a === void 0 ? void 0 : _a.call(plugin, {
client: this,
url,
params: requestParams,
}));
if (result) {
return result;
}
}
}
applyPreStreamRequestConfigHooks(requestParams) {
var _a;
const url = this.getUrlObjectFromUrlString(requestParams.url);
for (const plugin of this.getPlugins()) {
(_a = plugin.onBeforeStreamRequestConfig) === null || _a === void 0 ? void 0 : _a.call(plugin, {
client: this,
url,
params: requestParams,
});
}
}
async applyPreRequestHooks(requestParams, computedParams, requestOptions) {
await this.applyPluginMethod('onBeforeRequest', {
client: this,
url: this.getUrlObjectFromUrlString(requestParams.url),
params: requestParams,
computedParams,
requestOptions,
});
}
async applyPostRequestHooks(requestParams, computedParams, requestOptions, response) {
return await this.applyPluginMethod('onAfterRequest', {
client: this,
url: this.getUrlObjectFromUrlString(requestParams.url),
params: requestParams,
computedParams,
requestOptions,
response,
});
}
applyResponseErrorHooks(requestParams, computedParams, requestOptions, promise) {
return promise.catch(helpers_1.applyResponseHooks.bind(this, requestParams, computedParams, requestOptions));
}
}
exports.ClientRequestMaker = ClientRequestMaker;
ClientRequestMaker.BODY_METHODS = new Set(['POST', 'PUT', 'PATCH']);

View File

@ -0,0 +1,20 @@
/// <reference types="node" />
import type { RequestOptions } from 'https';
import type { TBodyMode, TRequestBody, TRequestQuery, TRequestStringQuery } from '../types/request-maker.mixin.types';
export declare class RequestParamHelpers {
static readonly JSON_1_1_ENDPOINTS: Set<string>;
static formatQueryToString(query: TRequestQuery): TRequestStringQuery;
static autoDetectBodyType(url: URL): TBodyMode;
static addQueryParamsToUrl(url: URL, query: TRequestQuery): void;
static constructBodyParams(body: TRequestBody, headers: Record<string, string>, mode: TBodyMode): string | Buffer;
static setBodyLengthHeader(options: RequestOptions, body: string | Buffer): void;
static isOAuthSerializable(item: any): boolean;
static mergeQueryAndBodyForOAuth(query: TRequestQuery, body: TRequestBody): any;
static moveUrlQueryParamsIntoObject(url: URL, query: TRequestQuery): URL;
/**
* Replace URL parameters available in pathname, like `:id`, with data given in `parameters`:
* `https://twitter.com/:id.json` + `{ id: '20' }` => `https://twitter.com/20.json`
*/
static applyRequestParametersToUrl(url: URL, parameters: TRequestQuery): URL;
}
export default RequestParamHelpers;

View File

@ -0,0 +1,145 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RequestParamHelpers = void 0;
const form_data_helper_1 = require("./form-data.helper");
const oauth1_helper_1 = __importDefault(require("./oauth1.helper"));
/* Helpers functions that are specific to this class but do not depends on instance */
class RequestParamHelpers {
static formatQueryToString(query) {
const formattedQuery = {};
for (const prop in query) {
if (typeof query[prop] === 'string') {
formattedQuery[prop] = query[prop];
}
else if (typeof query[prop] !== 'undefined') {
formattedQuery[prop] = String(query[prop]);
}
}
return formattedQuery;
}
static autoDetectBodyType(url) {
if (url.pathname.startsWith('/2/') || url.pathname.startsWith('/labs/2/')) {
// oauth2 takes url encoded
if (url.password.startsWith('/2/oauth2')) {
return 'url';
}
// Twitter API v2 has JSON-encoded requests for everything else
return 'json';
}
if (url.hostname === 'upload.twitter.com') {
if (url.pathname === '/1.1/media/upload.json') {
return 'form-data';
}
// json except for media/upload command, that is form-data.
return 'json';
}
const endpoint = url.pathname.split('/1.1/', 2)[1];
if (this.JSON_1_1_ENDPOINTS.has(endpoint)) {
return 'json';
}
return 'url';
}
static addQueryParamsToUrl(url, query) {
const queryEntries = Object.entries(query);
if (queryEntries.length) {
let search = '';
for (const [key, value] of queryEntries) {
search += (search.length ? '&' : '?') + `${oauth1_helper_1.default.percentEncode(key)}=${oauth1_helper_1.default.percentEncode(value)}`;
}
url.search = search;
}
}
static constructBodyParams(body, headers, mode) {
if (body instanceof Buffer) {
return body;
}
if (mode === 'json') {
headers['content-type'] = 'application/json;charset=UTF-8';
return JSON.stringify(body);
}
else if (mode === 'url') {
headers['content-type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
if (Object.keys(body).length) {
return new URLSearchParams(body)
.toString()
.replace(/\*/g, '%2A'); // URLSearchParams doesnt encode '*', but Twitter wants it encoded.
}
return '';
}
else if (mode === 'raw') {
throw new Error('You can only use raw body mode with Buffers. To give a string, use Buffer.from(str).');
}
else {
const form = new form_data_helper_1.FormDataHelper();
for (const parameter in body) {
form.append(parameter, body[parameter]);
}
const formHeaders = form.getHeaders();
headers['content-type'] = formHeaders['content-type'];
return form.getBuffer();
}
}
static setBodyLengthHeader(options, body) {
var _a;
options.headers = (_a = options.headers) !== null && _a !== void 0 ? _a : {};
if (typeof body === 'string') {
options.headers['content-length'] = Buffer.byteLength(body);
}
else {
options.headers['content-length'] = body.length;
}
}
static isOAuthSerializable(item) {
return !(item instanceof Buffer);
}
static mergeQueryAndBodyForOAuth(query, body) {
const parameters = {};
for (const prop in query) {
parameters[prop] = query[prop];
}
if (this.isOAuthSerializable(body)) {
for (const prop in body) {
const bodyProp = body[prop];
if (this.isOAuthSerializable(bodyProp)) {
parameters[prop] = typeof bodyProp === 'object' && bodyProp !== null && 'toString' in bodyProp
? bodyProp.toString()
: bodyProp;
}
}
}
return parameters;
}
static moveUrlQueryParamsIntoObject(url, query) {
for (const [param, value] of url.searchParams) {
query[param] = value;
}
// Remove the query string
url.search = '';
return url;
}
/**
* Replace URL parameters available in pathname, like `:id`, with data given in `parameters`:
* `https://twitter.com/:id.json` + `{ id: '20' }` => `https://twitter.com/20.json`
*/
static applyRequestParametersToUrl(url, parameters) {
url.pathname = url.pathname.replace(/:([A-Z_-]+)/ig, (fullMatch, paramName) => {
if (parameters[paramName] !== undefined) {
return String(parameters[paramName]);
}
return fullMatch;
});
return url;
}
}
exports.RequestParamHelpers = RequestParamHelpers;
RequestParamHelpers.JSON_1_1_ENDPOINTS = new Set([
'direct_messages/events/new.json',
'direct_messages/welcome_messages/new.json',
'direct_messages/welcome_messages/rules/new.json',
'media/metadata/create.json',
'collections/entries/curate.json',
]);
exports.default = RequestParamHelpers;

View File

@ -0,0 +1,134 @@
import type { IClientSettings, ITwitterApiClientPlugin, TwitterApiBasicAuth, TwitterApiOAuth2Init, TwitterApiTokens, TwitterRateLimit, TwitterResponse, UserV1, UserV2Result } from './types';
import { ClientRequestMaker } from './client-mixins/request-maker.mixin';
import TweetStream from './stream/TweetStream';
import { SharedPromise } from './helpers';
import type { TCustomizableRequestArgs, TRequestBody, TRequestQuery } from './types/request-maker.mixin.types';
export declare type TGetClientRequestArgs = TCustomizableRequestArgs & {
prefix?: string;
fullResponse?: boolean;
};
declare type TGetClientRequestArgsFullResponse = TClientRequestArgs & {
fullResponse: true;
};
declare type TGetClientRequestArgsDataResponse = TClientRequestArgs & {
fullResponse?: false;
};
export declare type TClientRequestArgs = TCustomizableRequestArgs & {
prefix?: string;
fullResponse?: boolean;
query?: TRequestQuery;
};
declare type TClientRequestArgsFullResponse = TClientRequestArgs & {
fullResponse: true;
};
declare type TClientRequestArgsDataResponse = TClientRequestArgs & {
fullResponse?: false;
};
export declare type TStreamClientRequestArgs = TCustomizableRequestArgs & {
prefix?: string;
query?: TRequestQuery;
payloadIsError?: (data: any) => boolean;
/**
* Choose to make or not initial connection.
* Method `.connect` must be called on returned `TweetStream` object
* to start stream if `autoConnect` is set to `false`.
* Defaults to `true`.
*/
autoConnect?: boolean;
};
export declare type TStreamClientRequestArgsWithAutoConnect = TStreamClientRequestArgs & {
autoConnect?: true;
};
export declare type TStreamClientRequestArgsWithoutAutoConnect = TStreamClientRequestArgs & {
autoConnect: false;
};
/**
* Base class for Twitter instances
*/
export default abstract class TwitterApiBase {
protected _prefix: string | undefined;
protected _currentUser: SharedPromise<UserV1> | null;
protected _currentUserV2: SharedPromise<UserV2Result> | null;
protected _requestMaker: ClientRequestMaker;
/**
* Create a new TwitterApi object without authentication.
*/
constructor(_?: undefined, settings?: Partial<IClientSettings>);
/**
* Create a new TwitterApi object with OAuth 2.0 Bearer authentication.
*/
constructor(bearerToken: string, settings?: Partial<IClientSettings>);
/**
* Create a new TwitterApi object with three-legged OAuth 1.0a authentication.
*/
constructor(tokens: TwitterApiTokens, settings?: Partial<IClientSettings>);
/**
* Create a new TwitterApi object with only client ID needed for OAuth2 user-flow.
*/
constructor(oauth2Init: TwitterApiOAuth2Init, settings?: Partial<IClientSettings>);
/**
* Create a new TwitterApi object with Basic HTTP authentication.
*/
constructor(credentials: TwitterApiBasicAuth, settings?: Partial<IClientSettings>);
/**
* Create a clone of {instance}.
*/
constructor(instance: TwitterApiBase, settings?: Partial<IClientSettings>);
protected setPrefix(prefix: string | undefined): void;
cloneWithPrefix(prefix: string): this;
getActiveTokens(): import("./types").TClientTokens;
getPlugins(): ITwitterApiClientPlugin[];
getPluginOfType<T extends ITwitterApiClientPlugin>(type: {
new (...args: any[]): T;
}): T | undefined;
/**
* @deprecated - Migrate to plugin `@twitter-api-v2/plugin-rate-limit`
*
* Tells if you hit the Twitter rate limit for {endpoint}.
* (local data only, this should not ask anything to Twitter)
*/
hasHitRateLimit(endpoint: string): boolean;
/**
* @deprecated - Migrate to plugin `@twitter-api-v2/plugin-rate-limit`
*
* Tells if you hit the returned Twitter rate limit for {endpoint} has expired.
* If client has no saved rate limit data for {endpoint}, this will gives you `true`.
*/
isRateLimitStatusObsolete(endpoint: string): boolean;
/**
* @deprecated - Migrate to plugin `@twitter-api-v2/plugin-rate-limit`
*
* Get the last obtained Twitter rate limit information for {endpoint}.
* (local data only, this should not ask anything to Twitter)
*/
getLastRateLimitStatus(endpoint: string): TwitterRateLimit | undefined;
/** Get cached current user. */
protected getCurrentUserObject(forceFetch?: boolean): Promise<UserV1>;
/**
* Get cached current user from v2 API.
* This can only be the slimest available `UserV2` object, with only `id`, `name` and `username` properties defined.
*
* To get a customized `UserV2Result`, use `.v2.me()`
*
* OAuth2 scopes: `tweet.read` & `users.read`
*/
protected getCurrentUserV2Object(forceFetch?: boolean): Promise<UserV2Result>;
get<T = any>(url: string, query?: TRequestQuery, args?: TGetClientRequestArgsDataResponse): Promise<T>;
get<T = any>(url: string, query?: TRequestQuery, args?: TGetClientRequestArgsFullResponse): Promise<TwitterResponse<T>>;
delete<T = any>(url: string, query?: TRequestQuery, args?: TGetClientRequestArgsDataResponse): Promise<T>;
delete<T = any>(url: string, query?: TRequestQuery, args?: TGetClientRequestArgsFullResponse): Promise<TwitterResponse<T>>;
post<T = any>(url: string, body?: TRequestBody, args?: TClientRequestArgsDataResponse): Promise<T>;
post<T = any>(url: string, body?: TRequestBody, args?: TClientRequestArgsFullResponse): Promise<TwitterResponse<T>>;
put<T = any>(url: string, body?: TRequestBody, args?: TClientRequestArgsDataResponse): Promise<T>;
put<T = any>(url: string, body?: TRequestBody, args?: TClientRequestArgsFullResponse): Promise<TwitterResponse<T>>;
patch<T = any>(url: string, body?: TRequestBody, args?: TClientRequestArgsDataResponse): Promise<T>;
patch<T = any>(url: string, body?: TRequestBody, args?: TClientRequestArgsFullResponse): Promise<TwitterResponse<T>>;
/** Stream request helpers */
getStream<T = any>(url: string, query: TRequestQuery | undefined, options: TStreamClientRequestArgsWithoutAutoConnect): TweetStream<T>;
getStream<T = any>(url: string, query?: TRequestQuery, options?: TStreamClientRequestArgsWithAutoConnect): Promise<TweetStream<T>>;
getStream<T = any>(url: string, query?: TRequestQuery, options?: TStreamClientRequestArgs): Promise<TweetStream<T>> | TweetStream<T>;
postStream<T = any>(url: string, body: TRequestBody | undefined, options: TStreamClientRequestArgsWithoutAutoConnect): TweetStream<T>;
postStream<T = any>(url: string, body?: TRequestBody, options?: TStreamClientRequestArgsWithAutoConnect): Promise<TweetStream<T>>;
postStream<T = any>(url: string, body?: TRequestBody, options?: TStreamClientRequestArgs): Promise<TweetStream<T>> | TweetStream<T>;
}
export {};

View File

@ -0,0 +1,179 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const request_maker_mixin_1 = require("./client-mixins/request-maker.mixin");
const helpers_1 = require("./helpers");
const globals_1 = require("./globals");
/**
* Base class for Twitter instances
*/
class TwitterApiBase {
constructor(token, settings = {}) {
this._currentUser = null;
this._currentUserV2 = null;
if (token instanceof TwitterApiBase) {
this._requestMaker = token._requestMaker;
}
else {
this._requestMaker = new request_maker_mixin_1.ClientRequestMaker(settings);
this._requestMaker.initializeToken(token);
}
}
/* Prefix/Token handling */
setPrefix(prefix) {
this._prefix = prefix;
}
cloneWithPrefix(prefix) {
const clone = this.constructor(this);
clone.setPrefix(prefix);
return clone;
}
getActiveTokens() {
return this._requestMaker.getActiveTokens();
}
/* Rate limit cache / Plugins */
getPlugins() {
return this._requestMaker.getPlugins();
}
getPluginOfType(type) {
return this.getPlugins().find(plugin => plugin instanceof type);
}
/**
* @deprecated - Migrate to plugin `@twitter-api-v2/plugin-rate-limit`
*
* Tells if you hit the Twitter rate limit for {endpoint}.
* (local data only, this should not ask anything to Twitter)
*/
hasHitRateLimit(endpoint) {
var _a;
if (this.isRateLimitStatusObsolete(endpoint)) {
return false;
}
return ((_a = this.getLastRateLimitStatus(endpoint)) === null || _a === void 0 ? void 0 : _a.remaining) === 0;
}
/**
* @deprecated - Migrate to plugin `@twitter-api-v2/plugin-rate-limit`
*
* Tells if you hit the returned Twitter rate limit for {endpoint} has expired.
* If client has no saved rate limit data for {endpoint}, this will gives you `true`.
*/
isRateLimitStatusObsolete(endpoint) {
const rateLimit = this.getLastRateLimitStatus(endpoint);
if (rateLimit === undefined) {
return true;
}
// Timestamps are exprimed in seconds, JS works with ms
return (rateLimit.reset * 1000) < Date.now();
}
/**
* @deprecated - Migrate to plugin `@twitter-api-v2/plugin-rate-limit`
*
* Get the last obtained Twitter rate limit information for {endpoint}.
* (local data only, this should not ask anything to Twitter)
*/
getLastRateLimitStatus(endpoint) {
const endpointWithPrefix = endpoint.match(/^https?:\/\//) ? endpoint : (this._prefix + endpoint);
return this._requestMaker.getRateLimits()[endpointWithPrefix];
}
/* Current user cache */
/** Get cached current user. */
getCurrentUserObject(forceFetch = false) {
if (!forceFetch && this._currentUser) {
if (this._currentUser.value) {
return Promise.resolve(this._currentUser.value);
}
return this._currentUser.promise;
}
this._currentUser = (0, helpers_1.sharedPromise)(() => this.get('account/verify_credentials.json', { tweet_mode: 'extended' }, { prefix: globals_1.API_V1_1_PREFIX }));
return this._currentUser.promise;
}
/**
* Get cached current user from v2 API.
* This can only be the slimest available `UserV2` object, with only `id`, `name` and `username` properties defined.
*
* To get a customized `UserV2Result`, use `.v2.me()`
*
* OAuth2 scopes: `tweet.read` & `users.read`
*/
getCurrentUserV2Object(forceFetch = false) {
if (!forceFetch && this._currentUserV2) {
if (this._currentUserV2.value) {
return Promise.resolve(this._currentUserV2.value);
}
return this._currentUserV2.promise;
}
this._currentUserV2 = (0, helpers_1.sharedPromise)(() => this.get('users/me', undefined, { prefix: globals_1.API_V2_PREFIX }));
return this._currentUserV2.promise;
}
async get(url, query = {}, { fullResponse, prefix = this._prefix, ...rest } = {}) {
if (prefix)
url = prefix + url;
const resp = await this._requestMaker.send({
url,
method: 'GET',
query,
...rest,
});
return fullResponse ? resp : resp.data;
}
async delete(url, query = {}, { fullResponse, prefix = this._prefix, ...rest } = {}) {
if (prefix)
url = prefix + url;
const resp = await this._requestMaker.send({
url,
method: 'DELETE',
query,
...rest,
});
return fullResponse ? resp : resp.data;
}
async post(url, body, { fullResponse, prefix = this._prefix, ...rest } = {}) {
if (prefix)
url = prefix + url;
const resp = await this._requestMaker.send({
url,
method: 'POST',
body,
...rest,
});
return fullResponse ? resp : resp.data;
}
async put(url, body, { fullResponse, prefix = this._prefix, ...rest } = {}) {
if (prefix)
url = prefix + url;
const resp = await this._requestMaker.send({
url,
method: 'PUT',
body,
...rest,
});
return fullResponse ? resp : resp.data;
}
async patch(url, body, { fullResponse, prefix = this._prefix, ...rest } = {}) {
if (prefix)
url = prefix + url;
const resp = await this._requestMaker.send({
url,
method: 'PATCH',
body,
...rest,
});
return fullResponse ? resp : resp.data;
}
getStream(url, query, { prefix = this._prefix, ...rest } = {}) {
return this._requestMaker.sendStream({
url: prefix ? prefix + url : url,
method: 'GET',
query,
...rest,
});
}
postStream(url, body, { prefix = this._prefix, ...rest } = {}) {
return this._requestMaker.sendStream({
url: prefix ? prefix + url : url,
method: 'POST',
body,
...rest,
});
}
}
exports.default = TwitterApiBase;

View File

@ -0,0 +1,7 @@
import TwitterApiBase from './client.base';
/**
* Base subclient for every v1 and v2 client.
*/
export default abstract class TwitterApiSubClient extends TwitterApiBase {
constructor(instance: TwitterApiBase);
}

View File

@ -0,0 +1,18 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const client_base_1 = __importDefault(require("./client.base"));
/**
* Base subclient for every v1 and v2 client.
*/
class TwitterApiSubClient extends client_base_1.default {
constructor(instance) {
if (!(instance instanceof client_base_1.default)) {
throw new Error('You must instance SubTwitterApi instance from existing TwitterApi instance.');
}
super(instance);
}
}
exports.default = TwitterApiSubClient;

View File

@ -0,0 +1,22 @@
import TwitterApiv1 from '../v1/client.v1';
import TwitterApiv2 from '../v2/client.v2';
import TwitterApiReadWrite from './readwrite';
/**
* Twitter v1.1 and v2 API client.
*/
export declare class TwitterApi extends TwitterApiReadWrite {
protected _v1?: TwitterApiv1;
protected _v2?: TwitterApiv2;
get v1(): TwitterApiv1;
get v2(): TwitterApiv2;
/**
* Get a client with read/write rights.
*/
get readWrite(): TwitterApiReadWrite;
static getErrors(error: any): (import("../types").ErrorV1 | import("../types").ErrorV2)[];
/** Extract another image size than obtained in a `profile_image_url` or `profile_image_url_https` field of a user object. */
static getProfileImageInSize(profileImageUrl: string, size: 'normal' | 'bigger' | 'mini' | 'original'): string;
}
export { default as TwitterApiReadWrite } from './readwrite';
export { default as TwitterApiReadOnly } from './readonly';
export default TwitterApi;

View File

@ -0,0 +1,70 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TwitterApiReadOnly = exports.TwitterApiReadWrite = exports.TwitterApi = void 0;
const client_v1_1 = __importDefault(require("../v1/client.v1"));
const client_v2_1 = __importDefault(require("../v2/client.v2"));
const readwrite_1 = __importDefault(require("./readwrite"));
// "Real" exported client for usage of TwitterApi.
/**
* Twitter v1.1 and v2 API client.
*/
class TwitterApi extends readwrite_1.default {
/* Direct access to subclients */
get v1() {
if (this._v1)
return this._v1;
return this._v1 = new client_v1_1.default(this);
}
get v2() {
if (this._v2)
return this._v2;
return this._v2 = new client_v2_1.default(this);
}
/**
* Get a client with read/write rights.
*/
get readWrite() {
return this;
}
/* Static helpers */
static getErrors(error) {
var _a;
if (typeof error !== 'object')
return [];
if (!('data' in error))
return [];
return (_a = error.data.errors) !== null && _a !== void 0 ? _a : [];
}
/** Extract another image size than obtained in a `profile_image_url` or `profile_image_url_https` field of a user object. */
static getProfileImageInSize(profileImageUrl, size) {
const lastPart = profileImageUrl.split('/').pop();
const sizes = ['normal', 'bigger', 'mini'];
let originalUrl = profileImageUrl;
for (const availableSize of sizes) {
if (lastPart.includes(`_${availableSize}`)) {
originalUrl = profileImageUrl.replace(`_${availableSize}`, '');
break;
}
}
if (size === 'original') {
return originalUrl;
}
const extPos = originalUrl.lastIndexOf('.');
if (extPos !== -1) {
const ext = originalUrl.slice(extPos + 1);
return originalUrl.slice(0, extPos) + '_' + size + '.' + ext;
}
else {
return originalUrl + '_' + size;
}
}
}
exports.TwitterApi = TwitterApi;
var readwrite_2 = require("./readwrite");
Object.defineProperty(exports, "TwitterApiReadWrite", { enumerable: true, get: function () { return __importDefault(readwrite_2).default; } });
var readonly_1 = require("./readonly");
Object.defineProperty(exports, "TwitterApiReadOnly", { enumerable: true, get: function () { return __importDefault(readonly_1).default; } });
exports.default = TwitterApi;

View File

@ -0,0 +1,157 @@
import TwitterApi from '.';
import TwitterApiBase from '../client.base';
import { AccessOAuth2TokenArgs, AccessOAuth2TokenResult, BuildOAuth2RequestLinkArgs, IOAuth2RequestTokenResult, IParsedOAuth2TokenResult, LoginResult, RequestTokenArgs, Tweetv2SearchParams } from '../types';
import TwitterApiv1ReadOnly from '../v1/client.v1.read';
import TwitterApiv2ReadOnly from '../v2/client.v2.read';
/**
* Twitter v1.1 and v2 API client.
*/
export default class TwitterApiReadOnly extends TwitterApiBase {
protected _v1?: TwitterApiv1ReadOnly;
protected _v2?: TwitterApiv2ReadOnly;
get v1(): TwitterApiv1ReadOnly;
get v2(): TwitterApiv2ReadOnly;
/**
* Fetch and cache current user.
* This method can only be called with a OAuth 1.0a user authentication.
*
* You can use this method to test if authentication was successful.
* Next calls to this methods will use the cached user, unless `forceFetch: true` is given.
*/
currentUser(forceFetch?: boolean): Promise<import("../types").UserV1>;
/**
* Fetch and cache current user.
* This method can only be called with a OAuth 1.0a or OAuth2 user authentication.
*
* This can only be the slimest available `UserV2` object, with only id, name and username properties defined.
* To get a customized `UserV2Result`, use `.v2.me()`
*
* You can use this method to test if authentication was successful.
* Next calls to this methods will use the cached user, unless `forceFetch: true` is given.
*
* OAuth2 scopes: `tweet.read` & `users.read`
*/
currentUserV2(forceFetch?: boolean): Promise<import("../types").UserV2Result>;
search(what: string, options?: Partial<Tweetv2SearchParams>): Promise<import("..").TweetSearchRecentV2Paginator>;
/**
* Generate the OAuth request token link for user-based OAuth 1.0 auth.
*
* ```ts
* // Instanciate TwitterApi with consumer keys
* const client = new TwitterApi({ appKey: 'consumer_key', appSecret: 'consumer_secret' });
*
* const tokenRequest = await client.generateAuthLink('oob-or-your-callback-url');
* // redirect end-user to tokenRequest.url
*
* // Save tokenRequest.oauth_token_secret somewhere, it will be needed for next auth step.
* ```
*/
generateAuthLink(oauth_callback?: string, { authAccessType, linkMode, forceLogin, screenName, }?: Partial<RequestTokenArgs>): Promise<{
oauth_token: string;
oauth_token_secret: string;
oauth_callback_confirmed: "true";
url: string;
}>;
/**
* Obtain access to user-based OAuth 1.0 auth.
*
* After user is redirect from your callback, use obtained oauth_token and oauth_verifier to
* instanciate the new TwitterApi instance.
*
* ```ts
* // Use the saved oauth_token_secret associated to oauth_token returned by callback
* const requestClient = new TwitterApi({
* appKey: 'consumer_key',
* appSecret: 'consumer_secret',
* accessToken: 'oauth_token',
* accessSecret: 'oauth_token_secret'
* });
*
* // Use oauth_verifier obtained from callback request
* const { client: userClient } = await requestClient.login('oauth_verifier');
*
* // {userClient} is a valid {TwitterApi} object you can use for future requests
* ```
*/
login(oauth_verifier: string): Promise<LoginResult>;
/**
* Enable application-only authentication.
*
* To make the request, instanciate TwitterApi with consumer and secret.
*
* ```ts
* const requestClient = new TwitterApi({ appKey: 'consumer', appSecret: 'secret' });
* const appClient = await requestClient.appLogin();
*
* // Use {appClient} to make requests
* ```
*/
appLogin(): Promise<TwitterApi>;
/**
* Generate the OAuth request token link for user-based OAuth 2.0 auth.
*
* - **You can only use v2 API endpoints with this authentication method.**
* - **You need to specify which scope you want to have when you create your auth link. Make sure it matches your needs.**
*
* See https://developer.twitter.com/en/docs/authentication/oauth-2-0/user-access-token for details.
*
* ```ts
* // Instanciate TwitterApi with client ID
* const client = new TwitterApi({ clientId: 'yourClientId' });
*
* // Generate a link to callback URL that will gives a token with tweet+user read access
* const link = client.generateOAuth2AuthLink('your-callback-url', { scope: ['tweet.read', 'users.read'] });
*
* // Extract props from generate link
* const { url, state, codeVerifier } = link;
*
* // redirect end-user to url
* // Save `state` and `codeVerifier` somewhere, it will be needed for next auth step.
* ```
*/
generateOAuth2AuthLink(redirectUri: string, options?: Partial<BuildOAuth2RequestLinkArgs>): IOAuth2RequestTokenResult;
/**
* Obtain access to user-based OAuth 2.0 auth.
*
* After user is redirect from your callback, use obtained code to
* instanciate the new TwitterApi instance.
*
* You need to obtain `codeVerifier` from a call to `.generateOAuth2AuthLink`.
*
* ```ts
* // Use the saved codeVerifier associated to state (present in query string of callback)
* const requestClient = new TwitterApi({ clientId: 'yourClientId' });
*
* const { client: userClient, refreshToken } = await requestClient.loginWithOAuth2({
* code: 'codeFromQueryString',
* // the same URL given to generateOAuth2AuthLink
* redirectUri,
* // the verifier returned by generateOAuth2AuthLink
* codeVerifier,
* });
*
* // {userClient} is a valid {TwitterApi} object you can use for future requests
* // {refreshToken} is defined if 'offline.access' is in scope.
* ```
*/
loginWithOAuth2({ code, codeVerifier, redirectUri }: AccessOAuth2TokenArgs): Promise<IParsedOAuth2TokenResult>;
/**
* Obtain a new access token to user-based OAuth 2.0 auth from a refresh token.
*
* ```ts
* const requestClient = new TwitterApi({ clientId: 'yourClientId' });
*
* const { client: userClient } = await requestClient.refreshOAuth2Token('refreshToken');
* // {userClient} is a valid {TwitterApi} object you can use for future requests
* ```
*/
refreshOAuth2Token(refreshToken: string): Promise<IParsedOAuth2TokenResult>;
/**
* Revoke a single user-based OAuth 2.0 token.
*
* You must specify its source, access token (directly after login)
* or refresh token (if you've called `.refreshOAuth2Token` before).
*/
revokeOAuth2Token(token: string, tokenType?: 'access_token' | 'refresh_token'): Promise<void>;
protected parseOAuth2AccessTokenResult(result: AccessOAuth2TokenResult): IParsedOAuth2TokenResult;
}

View File

@ -0,0 +1,305 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const _1 = __importDefault(require("."));
const client_base_1 = __importDefault(require("../client.base"));
const client_v1_read_1 = __importDefault(require("../v1/client.v1.read"));
const client_v2_read_1 = __importDefault(require("../v2/client.v2.read"));
const oauth2_helper_1 = require("../client-mixins/oauth2.helper");
const request_param_helper_1 = __importDefault(require("../client-mixins/request-param.helper"));
/**
* Twitter v1.1 and v2 API client.
*/
class TwitterApiReadOnly extends client_base_1.default {
/* Direct access to subclients */
get v1() {
if (this._v1)
return this._v1;
return this._v1 = new client_v1_read_1.default(this);
}
get v2() {
if (this._v2)
return this._v2;
return this._v2 = new client_v2_read_1.default(this);
}
/**
* Fetch and cache current user.
* This method can only be called with a OAuth 1.0a user authentication.
*
* You can use this method to test if authentication was successful.
* Next calls to this methods will use the cached user, unless `forceFetch: true` is given.
*/
async currentUser(forceFetch = false) {
return await this.getCurrentUserObject(forceFetch);
}
/**
* Fetch and cache current user.
* This method can only be called with a OAuth 1.0a or OAuth2 user authentication.
*
* This can only be the slimest available `UserV2` object, with only id, name and username properties defined.
* To get a customized `UserV2Result`, use `.v2.me()`
*
* You can use this method to test if authentication was successful.
* Next calls to this methods will use the cached user, unless `forceFetch: true` is given.
*
* OAuth2 scopes: `tweet.read` & `users.read`
*/
async currentUserV2(forceFetch = false) {
return await this.getCurrentUserV2Object(forceFetch);
}
/* Shortcuts to endpoints */
search(what, options) {
return this.v2.search(what, options);
}
/* Authentication */
/**
* Generate the OAuth request token link for user-based OAuth 1.0 auth.
*
* ```ts
* // Instanciate TwitterApi with consumer keys
* const client = new TwitterApi({ appKey: 'consumer_key', appSecret: 'consumer_secret' });
*
* const tokenRequest = await client.generateAuthLink('oob-or-your-callback-url');
* // redirect end-user to tokenRequest.url
*
* // Save tokenRequest.oauth_token_secret somewhere, it will be needed for next auth step.
* ```
*/
async generateAuthLink(oauth_callback = 'oob', { authAccessType, linkMode = 'authenticate', forceLogin, screenName, } = {}) {
const oauthResult = await this.post('https://api.twitter.com/oauth/request_token', { oauth_callback, x_auth_access_type: authAccessType });
let url = `https://api.twitter.com/oauth/${linkMode}?oauth_token=${encodeURIComponent(oauthResult.oauth_token)}`;
if (forceLogin !== undefined) {
url += `&force_login=${encodeURIComponent(forceLogin)}`;
}
if (screenName !== undefined) {
url += `&screen_name=${encodeURIComponent(screenName)}`;
}
if (this._requestMaker.hasPlugins()) {
this._requestMaker.applyPluginMethod('onOAuth1RequestToken', {
client: this._requestMaker,
url,
oauthResult,
});
}
return {
url,
...oauthResult,
};
}
/**
* Obtain access to user-based OAuth 1.0 auth.
*
* After user is redirect from your callback, use obtained oauth_token and oauth_verifier to
* instanciate the new TwitterApi instance.
*
* ```ts
* // Use the saved oauth_token_secret associated to oauth_token returned by callback
* const requestClient = new TwitterApi({
* appKey: 'consumer_key',
* appSecret: 'consumer_secret',
* accessToken: 'oauth_token',
* accessSecret: 'oauth_token_secret'
* });
*
* // Use oauth_verifier obtained from callback request
* const { client: userClient } = await requestClient.login('oauth_verifier');
*
* // {userClient} is a valid {TwitterApi} object you can use for future requests
* ```
*/
async login(oauth_verifier) {
const tokens = this.getActiveTokens();
if (tokens.type !== 'oauth-1.0a')
throw new Error('You must setup TwitterApi instance with consumer keys to accept OAuth 1.0 login');
const oauth_result = await this.post('https://api.twitter.com/oauth/access_token', { oauth_token: tokens.accessToken, oauth_verifier });
const client = new _1.default({
appKey: tokens.appKey,
appSecret: tokens.appSecret,
accessToken: oauth_result.oauth_token,
accessSecret: oauth_result.oauth_token_secret,
}, this._requestMaker.clientSettings);
return {
accessToken: oauth_result.oauth_token,
accessSecret: oauth_result.oauth_token_secret,
userId: oauth_result.user_id,
screenName: oauth_result.screen_name,
client,
};
}
/**
* Enable application-only authentication.
*
* To make the request, instanciate TwitterApi with consumer and secret.
*
* ```ts
* const requestClient = new TwitterApi({ appKey: 'consumer', appSecret: 'secret' });
* const appClient = await requestClient.appLogin();
*
* // Use {appClient} to make requests
* ```
*/
async appLogin() {
const tokens = this.getActiveTokens();
if (tokens.type !== 'oauth-1.0a')
throw new Error('You must setup TwitterApi instance with consumer keys to accept app-only login');
// Create a client with Basic authentication
const basicClient = new _1.default({ username: tokens.appKey, password: tokens.appSecret });
const res = await basicClient.post('https://api.twitter.com/oauth2/token', { grant_type: 'client_credentials' });
// New object with Bearer token
return new _1.default(res.access_token, this._requestMaker.clientSettings);
}
/* OAuth 2 user authentication */
/**
* Generate the OAuth request token link for user-based OAuth 2.0 auth.
*
* - **You can only use v2 API endpoints with this authentication method.**
* - **You need to specify which scope you want to have when you create your auth link. Make sure it matches your needs.**
*
* See https://developer.twitter.com/en/docs/authentication/oauth-2-0/user-access-token for details.
*
* ```ts
* // Instanciate TwitterApi with client ID
* const client = new TwitterApi({ clientId: 'yourClientId' });
*
* // Generate a link to callback URL that will gives a token with tweet+user read access
* const link = client.generateOAuth2AuthLink('your-callback-url', { scope: ['tweet.read', 'users.read'] });
*
* // Extract props from generate link
* const { url, state, codeVerifier } = link;
*
* // redirect end-user to url
* // Save `state` and `codeVerifier` somewhere, it will be needed for next auth step.
* ```
*/
generateOAuth2AuthLink(redirectUri, options = {}) {
var _a, _b;
if (!this._requestMaker.clientId) {
throw new Error('Twitter API instance is not initialized with client ID. You can find your client ID in Twitter Developer Portal. ' +
'Please build an instance with: new TwitterApi({ clientId: \'<yourClientId>\' })');
}
const state = (_a = options.state) !== null && _a !== void 0 ? _a : oauth2_helper_1.OAuth2Helper.generateRandomString(32);
const codeVerifier = oauth2_helper_1.OAuth2Helper.getCodeVerifier();
const codeChallenge = oauth2_helper_1.OAuth2Helper.getCodeChallengeFromVerifier(codeVerifier);
const rawScope = (_b = options.scope) !== null && _b !== void 0 ? _b : '';
const scope = Array.isArray(rawScope) ? rawScope.join(' ') : rawScope;
const url = new URL('https://twitter.com/i/oauth2/authorize');
const query = {
response_type: 'code',
client_id: this._requestMaker.clientId,
redirect_uri: redirectUri,
state,
code_challenge: codeChallenge,
code_challenge_method: 's256',
scope,
};
request_param_helper_1.default.addQueryParamsToUrl(url, query);
const result = {
url: url.toString(),
state,
codeVerifier,
codeChallenge,
};
if (this._requestMaker.hasPlugins()) {
this._requestMaker.applyPluginMethod('onOAuth2RequestToken', {
client: this._requestMaker,
result,
redirectUri,
});
}
return result;
}
/**
* Obtain access to user-based OAuth 2.0 auth.
*
* After user is redirect from your callback, use obtained code to
* instanciate the new TwitterApi instance.
*
* You need to obtain `codeVerifier` from a call to `.generateOAuth2AuthLink`.
*
* ```ts
* // Use the saved codeVerifier associated to state (present in query string of callback)
* const requestClient = new TwitterApi({ clientId: 'yourClientId' });
*
* const { client: userClient, refreshToken } = await requestClient.loginWithOAuth2({
* code: 'codeFromQueryString',
* // the same URL given to generateOAuth2AuthLink
* redirectUri,
* // the verifier returned by generateOAuth2AuthLink
* codeVerifier,
* });
*
* // {userClient} is a valid {TwitterApi} object you can use for future requests
* // {refreshToken} is defined if 'offline.access' is in scope.
* ```
*/
async loginWithOAuth2({ code, codeVerifier, redirectUri }) {
if (!this._requestMaker.clientId) {
throw new Error('Twitter API instance is not initialized with client ID. ' +
'Please build an instance with: new TwitterApi({ clientId: \'<yourClientId>\' })');
}
const accessTokenResult = await this.post('https://api.twitter.com/2/oauth2/token', {
code,
code_verifier: codeVerifier,
redirect_uri: redirectUri,
grant_type: 'authorization_code',
client_id: this._requestMaker.clientId,
client_secret: this._requestMaker.clientSecret,
});
return this.parseOAuth2AccessTokenResult(accessTokenResult);
}
/**
* Obtain a new access token to user-based OAuth 2.0 auth from a refresh token.
*
* ```ts
* const requestClient = new TwitterApi({ clientId: 'yourClientId' });
*
* const { client: userClient } = await requestClient.refreshOAuth2Token('refreshToken');
* // {userClient} is a valid {TwitterApi} object you can use for future requests
* ```
*/
async refreshOAuth2Token(refreshToken) {
if (!this._requestMaker.clientId) {
throw new Error('Twitter API instance is not initialized with client ID. ' +
'Please build an instance with: new TwitterApi({ clientId: \'<yourClientId>\' })');
}
const accessTokenResult = await this.post('https://api.twitter.com/2/oauth2/token', {
refresh_token: refreshToken,
grant_type: 'refresh_token',
client_id: this._requestMaker.clientId,
client_secret: this._requestMaker.clientSecret,
});
return this.parseOAuth2AccessTokenResult(accessTokenResult);
}
/**
* Revoke a single user-based OAuth 2.0 token.
*
* You must specify its source, access token (directly after login)
* or refresh token (if you've called `.refreshOAuth2Token` before).
*/
async revokeOAuth2Token(token, tokenType = 'access_token') {
if (!this._requestMaker.clientId) {
throw new Error('Twitter API instance is not initialized with client ID. ' +
'Please build an instance with: new TwitterApi({ clientId: \'<yourClientId>\' })');
}
return await this.post('https://api.twitter.com/2/oauth2/revoke', {
client_id: this._requestMaker.clientId,
client_secret: this._requestMaker.clientSecret,
token,
token_type_hint: tokenType,
});
}
parseOAuth2AccessTokenResult(result) {
const client = new _1.default(result.access_token, this._requestMaker.clientSettings);
const scope = result.scope.split(' ').filter(e => e);
return {
client,
expiresIn: result.expires_in,
accessToken: result.access_token,
scope,
refreshToken: result.refresh_token,
};
}
}
exports.default = TwitterApiReadOnly;

View File

@ -0,0 +1,16 @@
import TwitterApiv1ReadWrite from '../v1/client.v1.write';
import TwitterApiv2ReadWrite from '../v2/client.v2.write';
import TwitterApiReadOnly from './readonly';
/**
* Twitter v1.1 and v2 API client.
*/
export default class TwitterApiReadWrite extends TwitterApiReadOnly {
protected _v1?: TwitterApiv1ReadWrite;
protected _v2?: TwitterApiv2ReadWrite;
get v1(): TwitterApiv1ReadWrite;
get v2(): TwitterApiv2ReadWrite;
/**
* Get a client with read only rights.
*/
get readOnly(): TwitterApiReadOnly;
}

View File

@ -0,0 +1,31 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const client_v1_write_1 = __importDefault(require("../v1/client.v1.write"));
const client_v2_write_1 = __importDefault(require("../v2/client.v2.write"));
const readonly_1 = __importDefault(require("./readonly"));
/**
* Twitter v1.1 and v2 API client.
*/
class TwitterApiReadWrite extends readonly_1.default {
/* Direct access to subclients */
get v1() {
if (this._v1)
return this._v1;
return this._v1 = new client_v1_write_1.default(this);
}
get v2() {
if (this._v2)
return this._v2;
return this._v2 = new client_v2_write_1.default(this);
}
/**
* Get a client with read only rights.
*/
get readOnly() {
return this;
}
}
exports.default = TwitterApiReadWrite;

View File

@ -0,0 +1,5 @@
export declare const API_V2_PREFIX = "https://api.twitter.com/2/";
export declare const API_V2_LABS_PREFIX = "https://api.twitter.com/labs/2/";
export declare const API_V1_1_PREFIX = "https://api.twitter.com/1.1/";
export declare const API_V1_1_UPLOAD_PREFIX = "https://upload.twitter.com/1.1/";
export declare const API_V1_1_STREAM_PREFIX = "https://stream.twitter.com/1.1/";

View File

@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.API_V1_1_STREAM_PREFIX = exports.API_V1_1_UPLOAD_PREFIX = exports.API_V1_1_PREFIX = exports.API_V2_LABS_PREFIX = exports.API_V2_PREFIX = void 0;
exports.API_V2_PREFIX = 'https://api.twitter.com/2/';
exports.API_V2_LABS_PREFIX = 'https://api.twitter.com/labs/2/';
exports.API_V1_1_PREFIX = 'https://api.twitter.com/1.1/';
exports.API_V1_1_UPLOAD_PREFIX = 'https://upload.twitter.com/1.1/';
exports.API_V1_1_STREAM_PREFIX = 'https://stream.twitter.com/1.1/';

View File

@ -0,0 +1,16 @@
export interface SharedPromise<T> {
value: T | undefined;
promise: Promise<T>;
}
export declare function sharedPromise<T>(getter: () => Promise<T>): SharedPromise<T>;
export declare function arrayWrap<T>(value: T | T[]): T[];
export declare function trimUndefinedProperties(object: any): void;
export declare function isTweetStreamV2ErrorPayload(payload: any): boolean;
export declare function hasMultipleItems(item: string | string[]): boolean;
export interface IDeprecationWarning {
instance: string;
method: string;
problem: string;
resolution: string;
}
export declare function safeDeprecationWarning(message: IDeprecationWarning): void;

View File

@ -0,0 +1,60 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.safeDeprecationWarning = exports.hasMultipleItems = exports.isTweetStreamV2ErrorPayload = exports.trimUndefinedProperties = exports.arrayWrap = exports.sharedPromise = void 0;
const settings_1 = require("./settings");
function sharedPromise(getter) {
const sharedPromise = {
value: undefined,
promise: getter().then(val => {
sharedPromise.value = val;
return val;
}),
};
return sharedPromise;
}
exports.sharedPromise = sharedPromise;
function arrayWrap(value) {
if (Array.isArray(value)) {
return value;
}
return [value];
}
exports.arrayWrap = arrayWrap;
function trimUndefinedProperties(object) {
// Delete undefined parameters
for (const parameter in object) {
if (object[parameter] === undefined)
delete object[parameter];
}
}
exports.trimUndefinedProperties = trimUndefinedProperties;
function isTweetStreamV2ErrorPayload(payload) {
// Is error only if 'errors' is present and 'data' does not exists
return typeof payload === 'object'
&& 'errors' in payload
&& !('data' in payload);
}
exports.isTweetStreamV2ErrorPayload = isTweetStreamV2ErrorPayload;
function hasMultipleItems(item) {
if (Array.isArray(item) && item.length > 1) {
return true;
}
return item.toString().includes(',');
}
exports.hasMultipleItems = hasMultipleItems;
const deprecationWarningsCache = new Set();
function safeDeprecationWarning(message) {
if (typeof console === 'undefined' || !console.warn || !settings_1.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}.\n${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);
}
exports.safeDeprecationWarning = safeDeprecationWarning;

View File

@ -0,0 +1,10 @@
export { default as default } from './client';
export * from './client';
export * from './v1/client.v1';
export * from './v2/client.v2';
export * from './v2/includes.v2.helper';
export * from './v2-labs/client.v2.labs';
export * from './types';
export * from './paginators';
export * from './stream/TweetStream';
export * from './settings';

View File

@ -0,0 +1,31 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = void 0;
var client_1 = require("./client");
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(client_1).default; } });
__exportStar(require("./client"), exports);
__exportStar(require("./v1/client.v1"), exports);
__exportStar(require("./v2/client.v2"), exports);
__exportStar(require("./v2/includes.v2.helper"), exports);
__exportStar(require("./v2-labs/client.v2.labs"), exports);
__exportStar(require("./types"), exports);
__exportStar(require("./paginators"), exports);
__exportStar(require("./stream/TweetStream"), exports);
__exportStar(require("./settings"), exports);

View File

@ -0,0 +1,86 @@
import { TwitterRateLimit, TwitterResponse } from '../types';
import TwitterApiSubClient from '../client.subclient';
export interface ITwitterPaginatorArgs<TApiResult, TApiParams, TParams> {
realData: TApiResult;
rateLimit: TwitterRateLimit;
instance: TwitterApiSubClient;
queryParams: Partial<TApiParams>;
sharedParams?: TParams;
}
/** TwitterPaginator: able to get consume data from initial request, then fetch next data sequentially. */
export declare abstract class TwitterPaginator<TApiResult, TApiParams extends object, TItem, TParams = any> {
protected _realData: TApiResult;
protected _rateLimit: TwitterRateLimit;
protected _instance: TwitterApiSubClient;
protected _queryParams: Partial<TApiParams>;
protected _maxResultsWhenFetchLast: number;
/** informations unrelated to response data/query params that will be shared between paginator instances */
protected _sharedParams: TParams;
protected abstract _endpoint: string;
constructor({ realData, rateLimit, instance, queryParams, sharedParams }: ITwitterPaginatorArgs<TApiResult, TApiParams, TParams>);
protected get _isRateLimitOk(): boolean;
protected makeRequest(queryParams: Partial<TApiParams>): Promise<TwitterResponse<TApiResult>>;
protected makeNewInstanceFromResult(result: TwitterResponse<TApiResult>, queryParams: Partial<TApiParams>): this;
protected getEndpoint(): string;
protected injectQueryParams(maxResults?: number): {
max_results?: number | undefined;
} & Partial<TApiParams>;
protected abstract refreshInstanceFromResult(result: TwitterResponse<TApiResult>, isNextPage: boolean): any;
protected abstract getNextQueryParams(maxResults?: number): Partial<TApiParams>;
protected abstract getPageLengthFromRequest(result: TwitterResponse<TApiResult>): number;
protected abstract isFetchLastOver(result: TwitterResponse<TApiResult>): boolean;
protected abstract canFetchNextPage(result: TApiResult): boolean;
protected abstract getItemArray(): TItem[];
/**
* Next page.
*/
next(maxResults?: number): Promise<this>;
/**
* Next page, but store it in current instance.
*/
fetchNext(maxResults?: number): Promise<this>;
/**
* Fetch up to {count} items after current page,
* as long as rate limit is not hit and Twitter has some results
*/
fetchLast(count?: number): Promise<this>;
get rateLimit(): {
limit: number;
reset: number;
remaining: number;
};
/** Get raw data returned by Twitter API. */
get data(): TApiResult;
get done(): boolean;
/**
* Iterate over currently fetched items.
*/
[Symbol.iterator](): Generator<TItem, void, undefined>;
/**
* Iterate over items "undefinitely" (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.
*/
[Symbol.asyncIterator](): AsyncGenerator<TItem, void, undefined>;
/**
* Iterate over items "undefinitely" 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.
*/
fetchAndIterate(): AsyncGenerator<[TItem, this], void, undefined>;
}
/** PreviousableTwitterPaginator: a TwitterPaginator able to get consume data from both side, next and previous. */
export declare abstract class PreviousableTwitterPaginator<TApiResult, TApiParams extends object, TItem, TParams = any> extends TwitterPaginator<TApiResult, TApiParams, TItem, TParams> {
protected abstract getPreviousQueryParams(maxResults?: number): Partial<TApiParams>;
/**
* Previous page (new tweets)
*/
previous(maxResults?: number): Promise<this>;
/**
* Previous page, but in current instance.
*/
fetchPrevious(maxResults?: number): Promise<this>;
}
export default TwitterPaginator;

View File

@ -0,0 +1,173 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PreviousableTwitterPaginator = exports.TwitterPaginator = void 0;
/** TwitterPaginator: able to get consume data from initial request, then fetch next data sequentially. */
class TwitterPaginator {
// 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 * 1000;
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) {
// Construct a subclass
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 in case of async sub-methods
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;
// Break at rate limit limit
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 "undefinitely" (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();
// eslint-disable-next-line @typescript-eslint/no-this-alias
let paginator = this;
let canFetchNextPage = this.canFetchNextPage(this._realData);
while (canFetchNextPage && this._isRateLimitOk && paginator.getItemArray().length > 0) {
const next = await paginator.next(this._maxResultsWhenFetchLast);
// Store data into current instance [needed to access includes and meta]
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 "undefinitely" 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];
}
// eslint-disable-next-line @typescript-eslint/no-this-alias
let paginator = this;
let canFetchNextPage = this.canFetchNextPage(this._realData);
while (canFetchNextPage && this._isRateLimitOk && paginator.getItemArray().length > 0) {
const next = await paginator.next(this._maxResultsWhenFetchLast);
// Store data into current instance [needed to access includes and meta]
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;
}
}
}
exports.TwitterPaginator = TwitterPaginator;
/** PreviousableTwitterPaginator: a TwitterPaginator able to get consume data from both side, next and previous. */
class PreviousableTwitterPaginator 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;
}
}
exports.PreviousableTwitterPaginator = PreviousableTwitterPaginator;
exports.default = TwitterPaginator;

View File

@ -0,0 +1,19 @@
import type { GetDmListV1Args, ReceivedDMEventsV1, TReceivedDMEvent, TwitterResponse, ReceivedWelcomeDMCreateEventV1, WelcomeDirectMessageListV1Result } from '../types';
import { CursoredV1Paginator } from './paginator.v1';
export declare class DmEventsV1Paginator extends CursoredV1Paginator<ReceivedDMEventsV1, GetDmListV1Args, TReceivedDMEvent> {
protected _endpoint: string;
protected refreshInstanceFromResult(response: TwitterResponse<ReceivedDMEventsV1>, isNextPage: true): void;
protected getPageLengthFromRequest(result: TwitterResponse<ReceivedDMEventsV1>): number;
protected getItemArray(): import("../types").DirectMessageCreateV1[];
/**
* Events returned by paginator.
*/
get events(): import("../types").DirectMessageCreateV1[];
}
export declare class WelcomeDmV1Paginator extends CursoredV1Paginator<WelcomeDirectMessageListV1Result, GetDmListV1Args, ReceivedWelcomeDMCreateEventV1> {
protected _endpoint: string;
protected refreshInstanceFromResult(response: TwitterResponse<WelcomeDirectMessageListV1Result>, isNextPage: true): void;
protected getPageLengthFromRequest(result: TwitterResponse<WelcomeDirectMessageListV1Result>): number;
protected getItemArray(): ReceivedWelcomeDMCreateEventV1[];
get welcomeMessages(): ReceivedWelcomeDMCreateEventV1[];
}

View File

@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WelcomeDmV1Paginator = exports.DmEventsV1Paginator = void 0;
const paginator_v1_1 = require("./paginator.v1");
class DmEventsV1Paginator extends paginator_v1_1.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;
}
}
exports.DmEventsV1Paginator = DmEventsV1Paginator;
class WelcomeDmV1Paginator extends paginator_v1_1.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;
}
}
exports.WelcomeDmV1Paginator = WelcomeDmV1Paginator;

View File

@ -0,0 +1,9 @@
export * from './tweet.paginator.v2';
export * from './TwitterPaginator';
export * from './dm.paginator.v1';
export * from './mutes.paginator.v1';
export * from './tweet.paginator.v1';
export * from './user.paginator.v1';
export * from './user.paginator.v2';
export * from './list.paginator.v1';
export * from './list.paginator.v2';

View File

@ -0,0 +1,25 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./tweet.paginator.v2"), exports);
__exportStar(require("./TwitterPaginator"), exports);
__exportStar(require("./dm.paginator.v1"), exports);
__exportStar(require("./mutes.paginator.v1"), exports);
__exportStar(require("./tweet.paginator.v1"), exports);
__exportStar(require("./user.paginator.v1"), exports);
__exportStar(require("./user.paginator.v2"), exports);
__exportStar(require("./list.paginator.v1"), exports);
__exportStar(require("./list.paginator.v2"), exports);

View File

@ -0,0 +1,36 @@
import { DoubleEndedListsCursorV1Result, DoubleEndedUsersCursorV1Result, ListMembersV1Params, ListOwnershipsV1Params, ListV1, TwitterResponse, UserV1 } from '../types';
import { CursoredV1Paginator } from './paginator.v1';
declare abstract class ListListsV1Paginator extends CursoredV1Paginator<DoubleEndedListsCursorV1Result, ListOwnershipsV1Params, ListV1> {
protected refreshInstanceFromResult(response: TwitterResponse<DoubleEndedListsCursorV1Result>, isNextPage: true): void;
protected getPageLengthFromRequest(result: TwitterResponse<DoubleEndedListsCursorV1Result>): number;
protected getItemArray(): ListV1[];
/**
* Lists returned by paginator.
*/
get lists(): ListV1[];
}
export declare class ListMembershipsV1Paginator extends ListListsV1Paginator {
protected _endpoint: string;
}
export declare class ListOwnershipsV1Paginator extends ListListsV1Paginator {
protected _endpoint: string;
}
export declare class ListSubscriptionsV1Paginator extends ListListsV1Paginator {
protected _endpoint: string;
}
declare abstract class ListUsersV1Paginator extends CursoredV1Paginator<DoubleEndedUsersCursorV1Result, ListMembersV1Params, UserV1> {
protected refreshInstanceFromResult(response: TwitterResponse<DoubleEndedUsersCursorV1Result>, isNextPage: true): void;
protected getPageLengthFromRequest(result: TwitterResponse<DoubleEndedUsersCursorV1Result>): number;
protected getItemArray(): UserV1[];
/**
* Users returned by paginator.
*/
get users(): UserV1[];
}
export declare class ListMembersV1Paginator extends ListUsersV1Paginator {
protected _endpoint: string;
}
export declare class ListSubscribersV1Paginator extends ListUsersV1Paginator {
protected _endpoint: string;
}
export {};

View File

@ -0,0 +1,83 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ListSubscribersV1Paginator = exports.ListMembersV1Paginator = exports.ListSubscriptionsV1Paginator = exports.ListOwnershipsV1Paginator = exports.ListMembershipsV1Paginator = void 0;
const paginator_v1_1 = require("./paginator.v1");
class ListListsV1Paginator extends paginator_v1_1.CursoredV1Paginator {
refreshInstanceFromResult(response, isNextPage) {
const result = response.data;
this._rateLimit = response.rateLimit;
if (isNextPage) {
this._realData.lists.push(...result.lists);
this._realData.next_cursor = result.next_cursor;
}
}
getPageLengthFromRequest(result) {
return result.data.lists.length;
}
getItemArray() {
return this.lists;
}
/**
* Lists returned by paginator.
*/
get lists() {
return this._realData.lists;
}
}
class ListMembershipsV1Paginator extends ListListsV1Paginator {
constructor() {
super(...arguments);
this._endpoint = 'lists/memberships.json';
}
}
exports.ListMembershipsV1Paginator = ListMembershipsV1Paginator;
class ListOwnershipsV1Paginator extends ListListsV1Paginator {
constructor() {
super(...arguments);
this._endpoint = 'lists/ownerships.json';
}
}
exports.ListOwnershipsV1Paginator = ListOwnershipsV1Paginator;
class ListSubscriptionsV1Paginator extends ListListsV1Paginator {
constructor() {
super(...arguments);
this._endpoint = 'lists/subscriptions.json';
}
}
exports.ListSubscriptionsV1Paginator = ListSubscriptionsV1Paginator;
class ListUsersV1Paginator extends paginator_v1_1.CursoredV1Paginator {
refreshInstanceFromResult(response, isNextPage) {
const result = response.data;
this._rateLimit = response.rateLimit;
if (isNextPage) {
this._realData.users.push(...result.users);
this._realData.next_cursor = result.next_cursor;
}
}
getPageLengthFromRequest(result) {
return result.data.users.length;
}
getItemArray() {
return this.users;
}
/**
* Users returned by paginator.
*/
get users() {
return this._realData.users;
}
}
class ListMembersV1Paginator extends ListUsersV1Paginator {
constructor() {
super(...arguments);
this._endpoint = 'lists/members.json';
}
}
exports.ListMembersV1Paginator = ListMembersV1Paginator;
class ListSubscribersV1Paginator extends ListUsersV1Paginator {
constructor() {
super(...arguments);
this._endpoint = 'lists/subscribers.json';
}
}
exports.ListSubscribersV1Paginator = ListSubscribersV1Paginator;

View File

@ -0,0 +1,26 @@
import type { GetListTimelineV2Params, ListTimelineV2Result, ListV2 } from '../types';
import { TimelineV2Paginator } from './v2.paginator';
declare abstract class ListTimelineV2Paginator<TResult extends ListTimelineV2Result, TParams extends GetListTimelineV2Params, TShared = any> extends TimelineV2Paginator<TResult, TParams, ListV2, TShared> {
protected getItemArray(): ListV2[];
/**
* Lists returned by paginator.
*/
get lists(): ListV2[];
get meta(): TResult["meta"];
}
export declare class UserOwnedListsV2Paginator extends ListTimelineV2Paginator<ListTimelineV2Result, GetListTimelineV2Params, {
id: string;
}> {
protected _endpoint: string;
}
export declare class UserListMembershipsV2Paginator extends ListTimelineV2Paginator<ListTimelineV2Result, GetListTimelineV2Params, {
id: string;
}> {
protected _endpoint: string;
}
export declare class UserListFollowedV2Paginator extends ListTimelineV2Paginator<ListTimelineV2Result, GetListTimelineV2Params, {
id: string;
}> {
protected _endpoint: string;
}
export {};

View File

@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserListFollowedV2Paginator = exports.UserListMembershipsV2Paginator = exports.UserOwnedListsV2Paginator = void 0;
const v2_paginator_1 = require("./v2.paginator");
class ListTimelineV2Paginator extends v2_paginator_1.TimelineV2Paginator {
getItemArray() {
return this.lists;
}
/**
* Lists returned by paginator.
*/
get lists() {
var _a;
return (_a = this._realData.data) !== null && _a !== void 0 ? _a : [];
}
get meta() {
return super.meta;
}
}
class UserOwnedListsV2Paginator extends ListTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'users/:id/owned_lists';
}
}
exports.UserOwnedListsV2Paginator = UserOwnedListsV2Paginator;
class UserListMembershipsV2Paginator extends ListTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'users/:id/list_memberships';
}
}
exports.UserListMembershipsV2Paginator = UserListMembershipsV2Paginator;
class UserListFollowedV2Paginator extends ListTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'users/:id/followed_lists';
}
}
exports.UserListFollowedV2Paginator = UserListFollowedV2Paginator;

View File

@ -0,0 +1,23 @@
import { CursoredV1Paginator } from './paginator.v1';
import type { MuteUserIdsV1Params, MuteUserIdsV1Result, MuteUserListV1Params, MuteUserListV1Result, TwitterResponse, UserV1 } from '../types';
export declare class MuteUserListV1Paginator extends CursoredV1Paginator<MuteUserListV1Result, MuteUserListV1Params, UserV1> {
protected _endpoint: string;
protected refreshInstanceFromResult(response: TwitterResponse<MuteUserListV1Result>, isNextPage: true): void;
protected getPageLengthFromRequest(result: TwitterResponse<MuteUserListV1Result>): number;
protected getItemArray(): UserV1[];
/**
* Users returned by paginator.
*/
get users(): UserV1[];
}
export declare class MuteUserIdsV1Paginator extends CursoredV1Paginator<MuteUserIdsV1Result, MuteUserIdsV1Params, string> {
protected _endpoint: string;
protected _maxResultsWhenFetchLast: number;
protected refreshInstanceFromResult(response: TwitterResponse<MuteUserIdsV1Result>, isNextPage: true): void;
protected getPageLengthFromRequest(result: TwitterResponse<MuteUserIdsV1Result>): number;
protected getItemArray(): string[];
/**
* Users IDs returned by paginator.
*/
get ids(): string[];
}

View File

@ -0,0 +1,59 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MuteUserIdsV1Paginator = exports.MuteUserListV1Paginator = void 0;
const paginator_v1_1 = require("./paginator.v1");
class MuteUserListV1Paginator extends paginator_v1_1.CursoredV1Paginator {
constructor() {
super(...arguments);
this._endpoint = 'mutes/users/list.json';
}
refreshInstanceFromResult(response, isNextPage) {
const result = response.data;
this._rateLimit = response.rateLimit;
if (isNextPage) {
this._realData.users.push(...result.users);
this._realData.next_cursor = result.next_cursor;
}
}
getPageLengthFromRequest(result) {
return result.data.users.length;
}
getItemArray() {
return this.users;
}
/**
* Users returned by paginator.
*/
get users() {
return this._realData.users;
}
}
exports.MuteUserListV1Paginator = MuteUserListV1Paginator;
class MuteUserIdsV1Paginator extends paginator_v1_1.CursoredV1Paginator {
constructor() {
super(...arguments);
this._endpoint = 'mutes/users/ids.json';
this._maxResultsWhenFetchLast = 5000;
}
refreshInstanceFromResult(response, isNextPage) {
const result = response.data;
this._rateLimit = response.rateLimit;
if (isNextPage) {
this._realData.ids.push(...result.ids);
this._realData.next_cursor = result.next_cursor;
}
}
getPageLengthFromRequest(result) {
return result.data.ids.length;
}
getItemArray() {
return this.ids;
}
/**
* Users IDs returned by paginator.
*/
get ids() {
return this._realData.ids;
}
}
exports.MuteUserIdsV1Paginator = MuteUserIdsV1Paginator;

View File

@ -0,0 +1,13 @@
import { TwitterResponse } from '../types';
import TwitterPaginator from './TwitterPaginator';
export declare abstract class CursoredV1Paginator<TApiResult extends {
next_cursor_str?: string;
next_cursor?: string;
}, TApiParams extends {
cursor?: string;
}, TItem, TParams = any> extends TwitterPaginator<TApiResult, TApiParams, TItem, TParams> {
protected getNextQueryParams(maxResults?: number): Partial<TApiParams>;
protected isFetchLastOver(result: TwitterResponse<TApiResult>): boolean;
protected canFetchNextPage(result: TApiResult): boolean;
private isNextCursorInvalid;
}

View File

@ -0,0 +1,33 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CursoredV1Paginator = void 0;
const TwitterPaginator_1 = __importDefault(require("./TwitterPaginator"));
class CursoredV1Paginator extends TwitterPaginator_1.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) {
// If we cant fetch next page
return !this.canFetchNextPage(result.data);
}
canFetchNextPage(result) {
// If one of cursor is valid
return !this.isNextCursorInvalid(result.next_cursor) || !this.isNextCursorInvalid(result.next_cursor_str);
}
isNextCursorInvalid(value) {
return value === undefined
|| value === 0
|| value === -1
|| value === '0'
|| value === '-1';
}
}
exports.CursoredV1Paginator = CursoredV1Paginator;

View File

@ -0,0 +1,37 @@
import TwitterPaginator from './TwitterPaginator';
import { TwitterResponse, TweetV1, TweetV1TimelineResult, TweetV1TimelineParams, TweetV1UserTimelineParams, ListStatusesV1Params } from '../types';
/** A generic TwitterPaginator able to consume TweetV1 timelines. */
declare abstract class TweetTimelineV1Paginator<TResult extends TweetV1TimelineResult, TParams extends TweetV1TimelineParams, TShared = any> extends TwitterPaginator<TResult, TParams, TweetV1, TShared> {
protected hasFinishedFetch: boolean;
protected refreshInstanceFromResult(response: TwitterResponse<TResult>, isNextPage: true): void;
protected getNextQueryParams(maxResults?: number): {
max_results?: number | undefined;
} & Partial<TParams> & {
max_id: string;
};
protected getPageLengthFromRequest(result: TwitterResponse<TResult>): number;
protected isFetchLastOver(result: TwitterResponse<TResult>): boolean;
protected canFetchNextPage(result: TResult): boolean;
protected getItemArray(): TResult;
/**
* Tweets returned by paginator.
*/
get tweets(): TResult;
get done(): boolean;
}
export declare class HomeTimelineV1Paginator extends TweetTimelineV1Paginator<TweetV1TimelineResult, TweetV1TimelineParams> {
protected _endpoint: string;
}
export declare class MentionTimelineV1Paginator extends TweetTimelineV1Paginator<TweetV1TimelineResult, TweetV1TimelineParams> {
protected _endpoint: string;
}
export declare class UserTimelineV1Paginator extends TweetTimelineV1Paginator<TweetV1TimelineResult, TweetV1UserTimelineParams> {
protected _endpoint: string;
}
export declare class ListTimelineV1Paginator extends TweetTimelineV1Paginator<TweetV1TimelineResult, ListStatusesV1Params> {
protected _endpoint: string;
}
export declare class UserFavoritesV1Paginator extends TweetTimelineV1Paginator<TweetV1TimelineResult, TweetV1UserTimelineParams> {
protected _endpoint: string;
}
export {};

View File

@ -0,0 +1,92 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserFavoritesV1Paginator = exports.ListTimelineV1Paginator = exports.UserTimelineV1Paginator = exports.MentionTimelineV1Paginator = exports.HomeTimelineV1Paginator = void 0;
const TwitterPaginator_1 = __importDefault(require("./TwitterPaginator"));
/** A generic TwitterPaginator able to consume TweetV1 timelines. */
class TweetTimelineV1Paginator extends TwitterPaginator_1.default {
constructor() {
super(...arguments);
this.hasFinishedFetch = false;
}
refreshInstanceFromResult(response, isNextPage) {
const result = response.data;
this._rateLimit = response.rateLimit;
if (isNextPage) {
this._realData.push(...result);
// HINT: This is an approximation, as "end" of pagination cannot be safely determined without cursors.
this.hasFinishedFetch = result.length === 0;
}
}
getNextQueryParams(maxResults) {
const lastestId = BigInt(this._realData[this._realData.length - 1].id_str);
return {
...this.injectQueryParams(maxResults),
max_id: (lastestId - BigInt(1)).toString(),
};
}
getPageLengthFromRequest(result) {
return result.data.length;
}
isFetchLastOver(result) {
return !result.data.length;
}
canFetchNextPage(result) {
return result.length > 0;
}
getItemArray() {
return this.tweets;
}
/**
* Tweets returned by paginator.
*/
get tweets() {
return this._realData;
}
get done() {
return super.done || this.hasFinishedFetch;
}
}
// Timelines
// Home
class HomeTimelineV1Paginator extends TweetTimelineV1Paginator {
constructor() {
super(...arguments);
this._endpoint = 'statuses/home_timeline.json';
}
}
exports.HomeTimelineV1Paginator = HomeTimelineV1Paginator;
// Mention
class MentionTimelineV1Paginator extends TweetTimelineV1Paginator {
constructor() {
super(...arguments);
this._endpoint = 'statuses/mentions_timeline.json';
}
}
exports.MentionTimelineV1Paginator = MentionTimelineV1Paginator;
// User
class UserTimelineV1Paginator extends TweetTimelineV1Paginator {
constructor() {
super(...arguments);
this._endpoint = 'statuses/user_timeline.json';
}
}
exports.UserTimelineV1Paginator = UserTimelineV1Paginator;
// Lists
class ListTimelineV1Paginator extends TweetTimelineV1Paginator {
constructor() {
super(...arguments);
this._endpoint = 'lists/statuses.json';
}
}
exports.ListTimelineV1Paginator = ListTimelineV1Paginator;
// Favorites
class UserFavoritesV1Paginator extends TweetTimelineV1Paginator {
constructor() {
super(...arguments);
this._endpoint = 'favorites/list.json';
}
}
exports.UserFavoritesV1Paginator = UserFavoritesV1Paginator;

View File

@ -0,0 +1,74 @@
import { Tweetv2SearchParams, Tweetv2SearchResult, TwitterResponse, TweetV2, Tweetv2TimelineResult, TweetV2TimelineParams, TweetV2PaginableTimelineResult, TweetV2UserTimelineParams, Tweetv2ListResult, TweetV2PaginableListParams, TweetV2PaginableTimelineParams, TweetV2HomeTimelineParams } from '../types';
import { TimelineV2Paginator, TwitterV2Paginator } from './v2.paginator';
/** A generic PreviousableTwitterPaginator able to consume TweetV2 timelines with since_id, until_id and next_token (when available). */
declare abstract class TweetTimelineV2Paginator<TResult extends Tweetv2TimelineResult, TParams extends TweetV2TimelineParams, TShared = any> extends TwitterV2Paginator<TResult, TParams, TweetV2, TShared> {
protected refreshInstanceFromResult(response: TwitterResponse<TResult>, isNextPage: boolean): void;
protected getNextQueryParams(maxResults?: number): Partial<TParams>;
protected getPreviousQueryParams(maxResults?: number): Partial<TParams>;
protected getPageLengthFromRequest(result: TwitterResponse<TResult>): number;
protected isFetchLastOver(result: TwitterResponse<TResult>): boolean;
protected canFetchNextPage(result: TResult): boolean;
protected getItemArray(): TweetV2[];
protected dateStringToSnowflakeId(dateStr: string): string;
/**
* Tweets returned by paginator.
*/
get tweets(): TweetV2[];
get meta(): TResult["meta"];
}
/** A generic PreviousableTwitterPaginator able to consume TweetV2 timelines with pagination_tokens. */
declare abstract class TweetPaginableTimelineV2Paginator<TResult extends TweetV2PaginableTimelineResult, TParams extends TweetV2PaginableTimelineParams, TShared = any> extends TimelineV2Paginator<TResult, TParams, TweetV2, TShared> {
protected refreshInstanceFromResult(response: TwitterResponse<TResult>, isNextPage: boolean): void;
protected getItemArray(): TweetV2[];
/**
* Tweets returned by paginator.
*/
get tweets(): TweetV2[];
get meta(): TResult["meta"];
}
export declare class TweetSearchRecentV2Paginator extends TweetTimelineV2Paginator<Tweetv2SearchResult, Tweetv2SearchParams> {
protected _endpoint: string;
}
export declare class TweetSearchAllV2Paginator extends TweetTimelineV2Paginator<Tweetv2SearchResult, Tweetv2SearchParams> {
protected _endpoint: string;
}
export declare class QuotedTweetsTimelineV2Paginator extends TweetPaginableTimelineV2Paginator<TweetV2PaginableTimelineResult, TweetV2PaginableTimelineParams, {
id: string;
}> {
protected _endpoint: string;
}
export declare class TweetHomeTimelineV2Paginator extends TweetPaginableTimelineV2Paginator<TweetV2PaginableTimelineResult, TweetV2HomeTimelineParams, {
id: string;
}> {
protected _endpoint: string;
}
declare type TUserTimelinePaginatorShared = {
id: string;
};
export declare class TweetUserTimelineV2Paginator extends TweetPaginableTimelineV2Paginator<TweetV2PaginableTimelineResult, TweetV2UserTimelineParams, TUserTimelinePaginatorShared> {
protected _endpoint: string;
}
export declare class TweetUserMentionTimelineV2Paginator extends TweetPaginableTimelineV2Paginator<TweetV2PaginableTimelineResult, TweetV2PaginableTimelineParams, TUserTimelinePaginatorShared> {
protected _endpoint: string;
}
export declare class TweetBookmarksTimelineV2Paginator extends TweetPaginableTimelineV2Paginator<TweetV2PaginableTimelineResult, TweetV2PaginableTimelineParams, {
id: string;
}> {
protected _endpoint: string;
}
/** A generic TwitterPaginator able to consume TweetV2 timelines. */
declare abstract class TweetListV2Paginator<TResult extends Tweetv2ListResult, TParams extends TweetV2PaginableListParams, TShared = any> extends TimelineV2Paginator<TResult, TParams, TweetV2, TShared> {
/**
* Tweets returned by paginator.
*/
get tweets(): TweetV2[];
get meta(): TResult["meta"];
protected getItemArray(): TweetV2[];
}
export declare class TweetV2UserLikedTweetsPaginator extends TweetListV2Paginator<Tweetv2ListResult, TweetV2PaginableListParams, TUserTimelinePaginatorShared> {
protected _endpoint: string;
}
export declare class TweetV2ListTweetsPaginator extends TweetListV2Paginator<Tweetv2ListResult, TweetV2PaginableListParams, TUserTimelinePaginatorShared> {
protected _endpoint: string;
}
export {};

View File

@ -0,0 +1,205 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TweetV2ListTweetsPaginator = exports.TweetV2UserLikedTweetsPaginator = exports.TweetBookmarksTimelineV2Paginator = exports.TweetUserMentionTimelineV2Paginator = exports.TweetUserTimelineV2Paginator = exports.TweetHomeTimelineV2Paginator = exports.QuotedTweetsTimelineV2Paginator = exports.TweetSearchAllV2Paginator = exports.TweetSearchRecentV2Paginator = void 0;
const v2_paginator_1 = require("./v2.paginator");
/** A generic PreviousableTwitterPaginator able to consume TweetV2 timelines with since_id, until_id and next_token (when available). */
class TweetTimelineV2Paginator extends v2_paginator_1.TwitterV2Paginator {
refreshInstanceFromResult(response, isNextPage) {
var _a;
const result = response.data;
const resultData = (_a = result.data) !== null && _a !== void 0 ? _a : [];
this._rateLimit = response.rateLimit;
if (!this._realData.data) {
this._realData.data = [];
}
if (isNextPage) {
this._realData.meta.oldest_id = result.meta.oldest_id;
this._realData.meta.result_count += result.meta.result_count;
this._realData.meta.next_token = result.meta.next_token;
this._realData.data.push(...resultData);
}
else {
this._realData.meta.newest_id = result.meta.newest_id;
this._realData.meta.result_count += result.meta.result_count;
this._realData.data.unshift(...resultData);
}
this.updateIncludes(result);
}
getNextQueryParams(maxResults) {
this.assertUsable();
const params = { ...this.injectQueryParams(maxResults) };
if (this._realData.meta.next_token) {
params.next_token = this._realData.meta.next_token;
}
else {
if (params.start_time) {
// until_id and start_time are forbidden together for some reason, so convert start_time to a since_id.
params.since_id = this.dateStringToSnowflakeId(params.start_time);
delete params.start_time;
}
if (params.end_time) {
// until_id overrides end_time, so delete it
delete params.end_time;
}
params.until_id = this._realData.meta.oldest_id;
}
return params;
}
getPreviousQueryParams(maxResults) {
this.assertUsable();
return {
...this.injectQueryParams(maxResults),
since_id: this._realData.meta.newest_id,
};
}
getPageLengthFromRequest(result) {
var _a, _b;
return (_b = (_a = result.data.data) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
}
isFetchLastOver(result) {
var _a;
return !((_a = result.data.data) === null || _a === void 0 ? void 0 : _a.length) || !this.canFetchNextPage(result.data);
}
canFetchNextPage(result) {
return !!result.meta.next_token;
}
getItemArray() {
return this.tweets;
}
dateStringToSnowflakeId(dateStr) {
const TWITTER_START_EPOCH = BigInt('1288834974657');
const date = new Date(dateStr);
if (isNaN(date.valueOf())) {
throw new Error('Unable to convert start_time/end_time to a valid date. A ISO 8601 DateTime is excepted, please check your input.');
}
const dateTimestamp = BigInt(date.valueOf());
return ((dateTimestamp - TWITTER_START_EPOCH) << BigInt('22')).toString();
}
/**
* Tweets returned by paginator.
*/
get tweets() {
var _a;
return (_a = this._realData.data) !== null && _a !== void 0 ? _a : [];
}
get meta() {
return super.meta;
}
}
/** A generic PreviousableTwitterPaginator able to consume TweetV2 timelines with pagination_tokens. */
class TweetPaginableTimelineV2Paginator extends v2_paginator_1.TimelineV2Paginator {
refreshInstanceFromResult(response, isNextPage) {
super.refreshInstanceFromResult(response, isNextPage);
const result = response.data;
if (isNextPage) {
this._realData.meta.oldest_id = result.meta.oldest_id;
}
else {
this._realData.meta.newest_id = result.meta.newest_id;
}
}
getItemArray() {
return this.tweets;
}
/**
* Tweets returned by paginator.
*/
get tweets() {
var _a;
return (_a = this._realData.data) !== null && _a !== void 0 ? _a : [];
}
get meta() {
return super.meta;
}
}
// ----------------
// - Tweet search -
// ----------------
class TweetSearchRecentV2Paginator extends TweetTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'tweets/search/recent';
}
}
exports.TweetSearchRecentV2Paginator = TweetSearchRecentV2Paginator;
class TweetSearchAllV2Paginator extends TweetTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'tweets/search/all';
}
}
exports.TweetSearchAllV2Paginator = TweetSearchAllV2Paginator;
class QuotedTweetsTimelineV2Paginator extends TweetPaginableTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'tweets/:id/quote_tweets';
}
}
exports.QuotedTweetsTimelineV2Paginator = QuotedTweetsTimelineV2Paginator;
// -----------------
// - Home timeline -
// -----------------
class TweetHomeTimelineV2Paginator extends TweetPaginableTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'users/:id/timelines/reverse_chronological';
}
}
exports.TweetHomeTimelineV2Paginator = TweetHomeTimelineV2Paginator;
class TweetUserTimelineV2Paginator extends TweetPaginableTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'users/:id/tweets';
}
}
exports.TweetUserTimelineV2Paginator = TweetUserTimelineV2Paginator;
class TweetUserMentionTimelineV2Paginator extends TweetPaginableTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'users/:id/mentions';
}
}
exports.TweetUserMentionTimelineV2Paginator = TweetUserMentionTimelineV2Paginator;
// -------------
// - Bookmarks -
// -------------
class TweetBookmarksTimelineV2Paginator extends TweetPaginableTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'users/:id/bookmarks';
}
}
exports.TweetBookmarksTimelineV2Paginator = TweetBookmarksTimelineV2Paginator;
// ---------------------------------------------------------------------------------
// - Tweet lists (consume tweets with pagination tokens instead of since/until id) -
// ---------------------------------------------------------------------------------
/** A generic TwitterPaginator able to consume TweetV2 timelines. */
class TweetListV2Paginator extends v2_paginator_1.TimelineV2Paginator {
/**
* Tweets returned by paginator.
*/
get tweets() {
var _a;
return (_a = this._realData.data) !== null && _a !== void 0 ? _a : [];
}
get meta() {
return super.meta;
}
getItemArray() {
return this.tweets;
}
}
class TweetV2UserLikedTweetsPaginator extends TweetListV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'users/:id/liked_tweets';
}
}
exports.TweetV2UserLikedTweetsPaginator = TweetV2UserLikedTweetsPaginator;
class TweetV2ListTweetsPaginator extends TweetListV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'lists/:id/tweets';
}
}
exports.TweetV2ListTweetsPaginator = TweetV2ListTweetsPaginator;

View File

@ -0,0 +1,37 @@
import TwitterPaginator from './TwitterPaginator';
import { FriendshipsIncomingV1Params, FriendshipsIncomingV1Result, TwitterResponse, UserSearchV1Params, UserV1 } from '../types';
import { CursoredV1Paginator } from './paginator.v1';
/** A generic TwitterPaginator able to consume TweetV1 timelines. */
export declare class UserSearchV1Paginator extends TwitterPaginator<UserV1[], UserSearchV1Params, UserV1> {
_endpoint: string;
protected refreshInstanceFromResult(response: TwitterResponse<UserV1[]>, isNextPage: true): void;
protected getNextQueryParams(maxResults?: number): {
count?: number | undefined;
page: number;
q?: string | undefined;
include_entities?: boolean | undefined;
tweet_mode?: "extended" | undefined;
};
protected getPageLengthFromRequest(result: TwitterResponse<UserV1[]>): number;
protected isFetchLastOver(result: TwitterResponse<UserV1[]>): boolean;
protected canFetchNextPage(result: UserV1[]): boolean;
protected getItemArray(): UserV1[];
/**
* Users returned by paginator.
*/
get users(): UserV1[];
}
export declare class FriendshipsIncomingV1Paginator extends CursoredV1Paginator<FriendshipsIncomingV1Result, FriendshipsIncomingV1Params, string> {
protected _endpoint: string;
protected _maxResultsWhenFetchLast: number;
protected refreshInstanceFromResult(response: TwitterResponse<FriendshipsIncomingV1Result>, isNextPage: true): void;
protected getPageLengthFromRequest(result: TwitterResponse<FriendshipsIncomingV1Result>): number;
protected getItemArray(): string[];
/**
* Users IDs returned by paginator.
*/
get ids(): string[];
}
export declare class FriendshipsOutgoingV1Paginator extends FriendshipsIncomingV1Paginator {
protected _endpoint: string;
}

View File

@ -0,0 +1,85 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FriendshipsOutgoingV1Paginator = exports.FriendshipsIncomingV1Paginator = exports.UserSearchV1Paginator = void 0;
const TwitterPaginator_1 = __importDefault(require("./TwitterPaginator"));
const paginator_v1_1 = require("./paginator.v1");
/** A generic TwitterPaginator able to consume TweetV1 timelines. */
class UserSearchV1Paginator extends TwitterPaginator_1.default {
constructor() {
super(...arguments);
this._endpoint = 'users/search.json';
}
refreshInstanceFromResult(response, isNextPage) {
const result = response.data;
this._rateLimit = response.rateLimit;
if (isNextPage) {
this._realData.push(...result);
}
}
getNextQueryParams(maxResults) {
var _a;
const previousPage = Number((_a = this._queryParams.page) !== null && _a !== void 0 ? _a : '1');
return {
...this._queryParams,
page: previousPage + 1,
...maxResults ? { count: maxResults } : {},
};
}
getPageLengthFromRequest(result) {
return result.data.length;
}
isFetchLastOver(result) {
return !result.data.length;
}
canFetchNextPage(result) {
return result.length > 0;
}
getItemArray() {
return this.users;
}
/**
* Users returned by paginator.
*/
get users() {
return this._realData;
}
}
exports.UserSearchV1Paginator = UserSearchV1Paginator;
class FriendshipsIncomingV1Paginator extends paginator_v1_1.CursoredV1Paginator {
constructor() {
super(...arguments);
this._endpoint = 'friendships/incoming.json';
this._maxResultsWhenFetchLast = 5000;
}
refreshInstanceFromResult(response, isNextPage) {
const result = response.data;
this._rateLimit = response.rateLimit;
if (isNextPage) {
this._realData.ids.push(...result.ids);
this._realData.next_cursor = result.next_cursor;
}
}
getPageLengthFromRequest(result) {
return result.data.ids.length;
}
getItemArray() {
return this.ids;
}
/**
* Users IDs returned by paginator.
*/
get ids() {
return this._realData.ids;
}
}
exports.FriendshipsIncomingV1Paginator = FriendshipsIncomingV1Paginator;
class FriendshipsOutgoingV1Paginator extends FriendshipsIncomingV1Paginator {
constructor() {
super(...arguments);
this._endpoint = 'friendships/outgoing.json';
}
}
exports.FriendshipsOutgoingV1Paginator = FriendshipsOutgoingV1Paginator;

View File

@ -0,0 +1,52 @@
import { UserV2, UserV2TimelineParams, UserV2TimelineResult } from '../types';
import { TimelineV2Paginator } from './v2.paginator';
/** A generic PreviousableTwitterPaginator able to consume UserV2 timelines. */
declare abstract class UserTimelineV2Paginator<TResult extends UserV2TimelineResult, TParams extends UserV2TimelineParams, TShared = any> extends TimelineV2Paginator<TResult, TParams, UserV2, TShared> {
protected getItemArray(): UserV2[];
/**
* Users returned by paginator.
*/
get users(): UserV2[];
get meta(): TResult["meta"];
}
export declare class UserBlockingUsersV2Paginator extends UserTimelineV2Paginator<UserV2TimelineResult, UserV2TimelineParams, {
id: string;
}> {
protected _endpoint: string;
}
export declare class UserMutingUsersV2Paginator extends UserTimelineV2Paginator<UserV2TimelineResult, UserV2TimelineParams, {
id: string;
}> {
protected _endpoint: string;
}
export declare class UserFollowersV2Paginator extends UserTimelineV2Paginator<UserV2TimelineResult, UserV2TimelineParams, {
id: string;
}> {
protected _endpoint: string;
}
export declare class UserFollowingV2Paginator extends UserTimelineV2Paginator<UserV2TimelineResult, UserV2TimelineParams, {
id: string;
}> {
protected _endpoint: string;
}
export declare class UserListMembersV2Paginator extends UserTimelineV2Paginator<UserV2TimelineResult, UserV2TimelineParams, {
id: string;
}> {
protected _endpoint: string;
}
export declare class UserListFollowersV2Paginator extends UserTimelineV2Paginator<UserV2TimelineResult, UserV2TimelineParams, {
id: string;
}> {
protected _endpoint: string;
}
export declare class TweetLikingUsersV2Paginator extends UserTimelineV2Paginator<UserV2TimelineResult, UserV2TimelineParams, {
id: string;
}> {
protected _endpoint: string;
}
export declare class TweetRetweetersUsersV2Paginator extends UserTimelineV2Paginator<UserV2TimelineResult, UserV2TimelineParams, {
id: string;
}> {
protected _endpoint: string;
}
export {};

View File

@ -0,0 +1,76 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TweetRetweetersUsersV2Paginator = exports.TweetLikingUsersV2Paginator = exports.UserListFollowersV2Paginator = exports.UserListMembersV2Paginator = exports.UserFollowingV2Paginator = exports.UserFollowersV2Paginator = exports.UserMutingUsersV2Paginator = exports.UserBlockingUsersV2Paginator = void 0;
const v2_paginator_1 = require("./v2.paginator");
/** A generic PreviousableTwitterPaginator able to consume UserV2 timelines. */
class UserTimelineV2Paginator extends v2_paginator_1.TimelineV2Paginator {
getItemArray() {
return this.users;
}
/**
* Users returned by paginator.
*/
get users() {
var _a;
return (_a = this._realData.data) !== null && _a !== void 0 ? _a : [];
}
get meta() {
return super.meta;
}
}
class UserBlockingUsersV2Paginator extends UserTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'users/:id/blocking';
}
}
exports.UserBlockingUsersV2Paginator = UserBlockingUsersV2Paginator;
class UserMutingUsersV2Paginator extends UserTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'users/:id/muting';
}
}
exports.UserMutingUsersV2Paginator = UserMutingUsersV2Paginator;
class UserFollowersV2Paginator extends UserTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'users/:id/followers';
}
}
exports.UserFollowersV2Paginator = UserFollowersV2Paginator;
class UserFollowingV2Paginator extends UserTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'users/:id/following';
}
}
exports.UserFollowingV2Paginator = UserFollowingV2Paginator;
class UserListMembersV2Paginator extends UserTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'lists/:id/members';
}
}
exports.UserListMembersV2Paginator = UserListMembersV2Paginator;
class UserListFollowersV2Paginator extends UserTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'lists/:id/followers';
}
}
exports.UserListFollowersV2Paginator = UserListFollowersV2Paginator;
class TweetLikingUsersV2Paginator extends UserTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'tweets/:id/liking_users';
}
}
exports.TweetLikingUsersV2Paginator = TweetLikingUsersV2Paginator;
class TweetRetweetersUsersV2Paginator extends UserTimelineV2Paginator {
constructor() {
super(...arguments);
this._endpoint = 'tweets/:id/retweeted_by';
}
}
exports.TweetRetweetersUsersV2Paginator = TweetRetweetersUsersV2Paginator;

View File

@ -0,0 +1,36 @@
import type { TwitterResponse } from '../types';
import type { DataMetaAndIncludeV2 } from '../types/v2/shared.v2.types';
import { TwitterV2IncludesHelper } from '../v2/includes.v2.helper';
import { PreviousableTwitterPaginator } from './TwitterPaginator';
/** A generic PreviousableTwitterPaginator with common v2 helper methods. */
export declare abstract class TwitterV2Paginator<TResult extends DataMetaAndIncludeV2<any, any, any>, TParams extends object, TItem, TShared = any> extends PreviousableTwitterPaginator<TResult, TParams, TItem, TShared> {
protected _includesInstance?: TwitterV2IncludesHelper;
protected updateIncludes(data: TResult): void;
/** Throw if the current paginator is not usable. */
protected assertUsable(): void;
get meta(): any;
get includes(): TwitterV2IncludesHelper;
get errors(): import("../types").InlineErrorV2[];
/** `true` if this paginator only contains error payload and no metadata found to consume data. */
get unusable(): boolean;
}
/** A generic TwitterV2Paginator able to consume v2 timelines that use max_results and pagination tokens. */
export declare abstract class TimelineV2Paginator<TResult extends DataMetaAndIncludeV2<any, any, any>, TParams extends {
max_results?: number;
pagination_token?: string;
}, TItem, TShared = any> extends TwitterV2Paginator<TResult, TParams, TItem, TShared> {
protected refreshInstanceFromResult(response: TwitterResponse<TResult>, isNextPage: boolean): void;
protected getNextQueryParams(maxResults?: number): {
max_results?: number | undefined;
} & Partial<TParams> & {
pagination_token: any;
};
protected getPreviousQueryParams(maxResults?: number): {
max_results?: number | undefined;
} & Partial<TParams> & {
pagination_token: any;
};
protected getPageLengthFromRequest(result: TwitterResponse<TResult>): any;
protected isFetchLastOver(result: TwitterResponse<TResult>): boolean;
protected canFetchNextPage(result: TResult): boolean;
}

View File

@ -0,0 +1,113 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TimelineV2Paginator = exports.TwitterV2Paginator = void 0;
const includes_v2_helper_1 = require("../v2/includes.v2.helper");
const TwitterPaginator_1 = require("./TwitterPaginator");
/** A generic PreviousableTwitterPaginator with common v2 helper methods. */
class TwitterV2Paginator extends TwitterPaginator_1.PreviousableTwitterPaginator {
updateIncludes(data) {
// Update errors
if (data.errors) {
if (!this._realData.errors) {
this._realData.errors = [];
}
this._realData.errors = [...this._realData.errors, ...data.errors];
}
// Update includes
if (!data.includes) {
return;
}
if (!this._realData.includes) {
this._realData.includes = {};
}
const includesRealData = this._realData.includes;
for (const [includeKey, includeArray] of Object.entries(data.includes)) {
if (!includesRealData[includeKey]) {
includesRealData[includeKey] = [];
}
includesRealData[includeKey] = [
...includesRealData[includeKey],
...includeArray,
];
}
}
/** Throw if the current paginator is not usable. */
assertUsable() {
if (this.unusable) {
throw new Error('Unable to use this paginator to fetch more data, as it does not contain any metadata.' +
' Check .errors property for more details.');
}
}
get meta() {
return this._realData.meta;
}
get includes() {
var _a;
if (!((_a = this._realData) === null || _a === void 0 ? void 0 : _a.includes)) {
return new includes_v2_helper_1.TwitterV2IncludesHelper(this._realData);
}
if (this._includesInstance) {
return this._includesInstance;
}
return this._includesInstance = new includes_v2_helper_1.TwitterV2IncludesHelper(this._realData);
}
get errors() {
var _a;
return (_a = this._realData.errors) !== null && _a !== void 0 ? _a : [];
}
/** `true` if this paginator only contains error payload and no metadata found to consume data. */
get unusable() {
return this.errors.length > 0 && !this._realData.meta && !this._realData.data;
}
}
exports.TwitterV2Paginator = TwitterV2Paginator;
/** A generic TwitterV2Paginator able to consume v2 timelines that use max_results and pagination tokens. */
class TimelineV2Paginator extends TwitterV2Paginator {
refreshInstanceFromResult(response, isNextPage) {
var _a;
const result = response.data;
const resultData = (_a = result.data) !== null && _a !== void 0 ? _a : [];
this._rateLimit = response.rateLimit;
if (!this._realData.data) {
this._realData.data = [];
}
if (isNextPage) {
this._realData.meta.result_count += result.meta.result_count;
this._realData.meta.next_token = result.meta.next_token;
this._realData.data.push(...resultData);
}
else {
this._realData.meta.result_count += result.meta.result_count;
this._realData.meta.previous_token = result.meta.previous_token;
this._realData.data.unshift(...resultData);
}
this.updateIncludes(result);
}
getNextQueryParams(maxResults) {
this.assertUsable();
return {
...this.injectQueryParams(maxResults),
pagination_token: this._realData.meta.next_token,
};
}
getPreviousQueryParams(maxResults) {
this.assertUsable();
return {
...this.injectQueryParams(maxResults),
pagination_token: this._realData.meta.previous_token,
};
}
getPageLengthFromRequest(result) {
var _a, _b;
return (_b = (_a = result.data.data) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
}
isFetchLastOver(result) {
var _a;
return !((_a = result.data.data) === null || _a === void 0 ? void 0 : _a.length) || !this.canFetchNextPage(result.data);
}
canFetchNextPage(result) {
var _a;
return !!((_a = result.meta) === null || _a === void 0 ? void 0 : _a.next_token);
}
}
exports.TimelineV2Paginator = TimelineV2Paginator;

View File

@ -0,0 +1,7 @@
/// <reference types="node" />
import type { ClientRequestArgs } from 'http';
import type { ClientRequestMaker } from '../client-mixins/request-maker.mixin';
import { IGetHttpRequestArgs } from '../types';
import type { IComputedHttpRequestArgs } from '../types/request-maker.mixin.types';
export declare function hasRequestErrorPlugins(client: ClientRequestMaker): boolean;
export declare function applyResponseHooks(this: ClientRequestMaker, requestParams: IGetHttpRequestArgs, computedParams: IComputedHttpRequestArgs, requestOptions: Partial<ClientRequestArgs>, error: any): Promise<any>;

View File

@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyResponseHooks = exports.hasRequestErrorPlugins = void 0;
const types_1 = require("../types");
/* Plugin helpers */
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;
}
exports.hasRequestErrorPlugins = hasRequestErrorPlugins;
async function applyResponseHooks(requestParams, computedParams, requestOptions, error) {
let override;
if (error instanceof types_1.ApiRequestError || error instanceof types_1.ApiPartialResponseError) {
override = await this.applyPluginMethod('onRequestError', {
client: this,
url: this.getUrlObjectFromUrlString(requestParams.url),
params: requestParams,
computedParams,
requestOptions,
error,
});
}
else if (error instanceof types_1.ApiResponseError) {
override = await this.applyPluginMethod('onResponseError', {
client: this,
url: this.getUrlObjectFromUrlString(requestParams.url),
params: requestParams,
computedParams,
requestOptions,
error,
});
}
if (override && override instanceof types_1.TwitterApiPluginResponseOverride) {
return override.value;
}
return Promise.reject(error);
}
exports.applyResponseHooks = applyResponseHooks;

View File

@ -0,0 +1,9 @@
export interface ITwitterApiV2Settings {
debug: boolean;
deprecationWarnings: boolean;
logger: ITwitterApiV2SettingsLogger;
}
export interface ITwitterApiV2SettingsLogger {
log(message: string, payload?: any): void;
}
export declare const TwitterApiV2Settings: ITwitterApiV2Settings;

View File

@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TwitterApiV2Settings = void 0;
exports.TwitterApiV2Settings = {
debug: false,
deprecationWarnings: true,
logger: { log: console.log.bind(console) },
};

View File

@ -0,0 +1,80 @@
/// <reference types="node" />
import { EventEmitter } from 'events';
import type { IncomingMessage, ClientRequest } from 'http';
import type { Readable } from 'stream';
import { ETwitterStreamEvent } from '../types';
import { TRequestFullStreamData } from '../types/request-maker.mixin.types';
import TweetStreamParser from './TweetStreamParser';
interface ITweetStreamError {
type: ETwitterStreamEvent.ConnectionError | ETwitterStreamEvent.TweetParseError | ETwitterStreamEvent.ReconnectError | ETwitterStreamEvent.DataError | ETwitterStreamEvent.ConnectError;
error: any;
message?: string;
}
export interface IConnectTweetStreamParams {
autoReconnect: boolean;
autoReconnectRetries: number | 'unlimited';
/** Check for 'lost connection' status every `keepAliveTimeout` milliseconds. Defaults to 2 minutes (`120000`). */
keepAliveTimeout: number | 'disable';
nextRetryTimeout?: TStreamConnectRetryFn;
}
export interface IWithConnectionTweetStream {
req: ClientRequest;
res: Readable;
originalResponse: IncomingMessage;
}
/** Returns a number of milliseconds to wait for {tryOccurence} (starting from 1) */
export declare type TStreamConnectRetryFn = (tryOccurence: number, error?: any) => number;
export declare class TweetStream<T = any> extends EventEmitter {
protected requestData: TRequestFullStreamData;
autoReconnect: boolean;
autoReconnectRetries: number;
keepAliveTimeoutMs: number;
nextRetryTimeout: TStreamConnectRetryFn;
protected retryTimeout?: NodeJS.Timeout;
protected keepAliveTimeout?: NodeJS.Timeout;
protected parser: TweetStreamParser;
protected connectionProcessRunning: boolean;
protected req?: ClientRequest;
protected res?: Readable;
protected originalResponse?: IncomingMessage;
constructor(requestData: TRequestFullStreamData, connection?: IWithConnectionTweetStream);
on(event: ETwitterStreamEvent.Data, handler: (data: T) => any): this;
on(event: ETwitterStreamEvent.DataError, handler: (error: any) => any): this;
on(event: ETwitterStreamEvent.Error, handler: (errorPayload: ITweetStreamError) => any): this;
on(event: ETwitterStreamEvent.Connected, handler: () => any): this;
on(event: ETwitterStreamEvent.ConnectionLost, handler: () => any): this;
on(event: ETwitterStreamEvent.ConnectionError, handler: (error: Error) => any): this;
on(event: ETwitterStreamEvent.TweetParseError, handler: (error: Error) => any): this;
on(event: ETwitterStreamEvent.ConnectionClosed, handler: () => any): this;
on(event: ETwitterStreamEvent.DataKeepAlive, handler: () => any): this;
on(event: ETwitterStreamEvent.ReconnectAttempt, handler: (tries: number) => any): this;
on(event: ETwitterStreamEvent.ReconnectError, handler: (tries: number) => any): this;
on(event: ETwitterStreamEvent.ReconnectLimitExceeded, handler: () => any): this;
on(event: ETwitterStreamEvent.Reconnected, handler: () => any): this;
on(event: string | symbol, handler: (...args: any[]) => any): this;
protected initEventsFromRequest(): void;
protected initEventsFromParser(): void;
protected resetKeepAliveTimeout(): void;
protected onKeepAliveTimeout(): void;
protected unbindTimeouts(): void;
protected unbindKeepAliveTimeout(): void;
protected unbindRetryTimeout(): void;
protected closeWithoutEmit(): void;
/** Terminate connection to Twitter. */
close(): void;
/** Unbind all listeners, and close connection. */
destroy(): void;
/**
* Make a new request that creates a new `TweetStream` instance with
* the same parameters, and bind current listeners to new stream.
*/
clone(): Promise<TweetStream<T>>;
/** Start initial stream connection, setup options on current instance and returns itself. */
connect(options?: Partial<IConnectTweetStreamParams>): Promise<this>;
/** Make a new request to (re)connect to Twitter. */
reconnect(): Promise<void>;
protected onConnectionError(retryOccurence?: number): Promise<void>;
protected makeAutoReconnectRetry(retryOccurence: number, error: any): void;
[Symbol.asyncIterator](): AsyncGenerator<T, void, undefined>;
}
export default TweetStream;

View File

@ -0,0 +1,303 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TweetStream = void 0;
const events_1 = require("events");
const request_handler_helper_1 = __importDefault(require("../client-mixins/request-handler.helper"));
const types_1 = require("../types");
const TweetStreamEventCombiner_1 = __importDefault(require("./TweetStreamEventCombiner"));
const TweetStreamParser_1 = __importStar(require("./TweetStreamParser"));
// In seconds
const basicRetriesAttempt = [5, 15, 30, 60, 90, 120, 180, 300, 600, 900];
// Default retry function
const basicReconnectRetry = tryOccurence => tryOccurence > basicRetriesAttempt.length
? 901000
: basicRetriesAttempt[tryOccurence - 1] * 1000;
class TweetStream extends events_1.EventEmitter {
constructor(requestData, connection) {
super();
this.requestData = requestData;
this.autoReconnect = false;
this.autoReconnectRetries = 5;
// 2 minutes without any Twitter signal
this.keepAliveTimeoutMs = 1000 * 120;
this.nextRetryTimeout = basicReconnectRetry;
this.parser = new TweetStreamParser_1.default();
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(types_1.ETwitterStreamEvent.ConnectionError, err);
this.emit(types_1.ETwitterStreamEvent.Error, {
type: types_1.ETwitterStreamEvent.ConnectionError,
error: err,
message: 'Connection lost or closed by Twitter.',
});
this.onConnectionError();
};
this.req.on('error', errorHandler);
this.res.on('error', errorHandler);
// Usually, connection should not be closed by Twitter!
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(types_1.ETwitterStreamEvent.DataKeepAlive);
}
this.parser.push(chunk.toString());
});
// Starts the keep alive timeout
this.resetKeepAliveTimeout();
}
initEventsFromParser() {
const payloadIsError = this.requestData.payloadIsError;
this.parser.on(TweetStreamParser_1.EStreamParserEvent.ParsedData, (eventData) => {
if (payloadIsError && payloadIsError(eventData)) {
this.emit(types_1.ETwitterStreamEvent.DataError, eventData);
this.emit(types_1.ETwitterStreamEvent.Error, {
type: types_1.ETwitterStreamEvent.DataError,
error: eventData,
message: 'Twitter sent a payload that is detected as an error payload.',
});
}
else {
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,
message: 'Failed to parse stream data.',
});
});
}
resetKeepAliveTimeout() {
this.unbindKeepAliveTimeout();
if (this.keepAliveTimeoutMs !== Infinity) {
this.keepAliveTimeout = setTimeout(this.onKeepAliveTimeout, this.keepAliveTimeoutMs);
}
}
onKeepAliveTimeout() {
this.emit(types_1.ETwitterStreamEvent.ConnectionLost);
this.onConnectionError();
}
unbindTimeouts() {
this.unbindRetryTimeout();
this.unbindKeepAliveTimeout();
}
unbindKeepAliveTimeout() {
if (this.keepAliveTimeout) {
clearTimeout(this.keepAliveTimeout);
this.keepAliveTimeout = undefined;
}
}
unbindRetryTimeout() {
if (this.retryTimeout) {
clearTimeout(this.retryTimeout);
this.retryTimeout = undefined;
}
}
closeWithoutEmit() {
this.unbindTimeouts();
if (this.res) {
this.res.removeAllListeners();
// Close response silentely
this.res.destroy();
}
if (this.req) {
this.req.removeAllListeners();
// Close connection silentely
this.req.destroy();
}
}
/** Terminate connection to Twitter. */
close() {
this.emit(types_1.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_1.default(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;
}
/** 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;
}
// Make the connection
this.unbindTimeouts();
try {
await this.reconnect();
}
catch (e) {
this.emit(types_1.ETwitterStreamEvent.ConnectError, 0);
this.emit(types_1.ETwitterStreamEvent.Error, {
type: types_1.ETwitterStreamEvent.ConnectError,
error: e,
message: 'Connect error - Initial connection just failed.',
});
// Only make a reconnection attempt if autoReconnect is true!
// Otherwise, let error be propagated
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_1.default(this.requestData).makeRequestAndResolveWhenReady();
this.req = req;
this.res = res;
this.originalResponse = originalResponse;
this.emit(initialConnection ? types_1.ETwitterStreamEvent.Connected : types_1.ETwitterStreamEvent.Reconnected);
this.parser.reset();
this.initEventsFromRequest();
}
finally {
this.connectionProcessRunning = false;
}
}
async onConnectionError(retryOccurence = 0) {
this.unbindTimeouts();
// Close the request if necessary
this.closeWithoutEmit();
// Terminate stream by events if necessary (no auto-reconnect or retries exceeded)
if (!this.autoReconnect) {
this.emit(types_1.ETwitterStreamEvent.ConnectionClosed);
return;
}
if (retryOccurence >= this.autoReconnectRetries) {
this.emit(types_1.ETwitterStreamEvent.ReconnectLimitExceeded);
this.emit(types_1.ETwitterStreamEvent.ConnectionClosed);
return;
}
// If all other conditions fails, do a reconnect attempt
try {
this.emit(types_1.ETwitterStreamEvent.ReconnectAttempt, retryOccurence);
await this.reconnect();
}
catch (e) {
this.emit(types_1.ETwitterStreamEvent.ReconnectError, retryOccurence);
this.emit(types_1.ETwitterStreamEvent.Error, {
type: types_1.ETwitterStreamEvent.ReconnectError,
error: e,
message: `Reconnect error - ${retryOccurence + 1} attempts made yet.`,
});
this.makeAutoReconnectRetry(retryOccurence, e);
}
}
makeAutoReconnectRetry(retryOccurence, error) {
const nextRetry = this.nextRetryTimeout(retryOccurence + 1, error);
this.retryTimeout = setTimeout(() => {
this.onConnectionError(retryOccurence + 1);
}, nextRetry);
}
async *[Symbol.asyncIterator]() {
const eventCombiner = new TweetStreamEventCombiner_1.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();
}
}
}
exports.TweetStream = TweetStream;
exports.default = TweetStream;

View File

@ -0,0 +1,27 @@
/// <reference types="node" />
import { EventEmitter } from 'events';
import type TweetStream from './TweetStream';
export declare class TweetStreamEventCombiner<T> extends EventEmitter {
private stream;
private stack;
private onceNewEvent;
constructor(stream: TweetStream<T>);
/** Returns a new `Promise` that will `resolve` on next event (`data` or any sort of error). */
nextEvent(): Promise<{
type: "error";
payload?: any;
} | {
type: "data";
payload: T;
}>;
/** Returns `true` if there's something in the stack. */
hasStack(): boolean;
/** Returns stacked data events, and clean the stack. */
popStack(): T[];
/** Cleanup all the listeners attached on stream. */
destroy(): void;
private emitEvent;
private onStreamError;
private onStreamData;
}
export default TweetStreamEventCombiner;

View File

@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TweetStreamEventCombiner = void 0;
const events_1 = require("events");
const types_1 = require("../types");
class TweetStreamEventCombiner extends events_1.EventEmitter {
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');
// Init events from stream
stream.on(types_1.ETwitterStreamEvent.Data, this.onStreamData);
// Ignore reconnect errors: Don't close event combiner until connection error/closed
stream.on(types_1.ETwitterStreamEvent.ConnectionError, this.onStreamError);
stream.on(types_1.ETwitterStreamEvent.TweetParseError, this.onStreamError);
stream.on(types_1.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(types_1.ETwitterStreamEvent.Data, this.onStreamData);
this.stream.off(types_1.ETwitterStreamEvent.ConnectionError, this.onStreamError);
this.stream.off(types_1.ETwitterStreamEvent.TweetParseError, this.onStreamError);
this.stream.off(types_1.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);
}
}
exports.TweetStreamEventCombiner = TweetStreamEventCombiner;
exports.default = TweetStreamEventCombiner;

View File

@ -0,0 +1,12 @@
/// <reference types="node" />
import { EventEmitter } from 'events';
export default class TweetStreamParser extends EventEmitter {
protected currentMessage: string;
push(chunk: string): void;
/** Reset the currently stored message (f.e. on connection reset) */
reset(): void;
}
export declare enum EStreamParserEvent {
ParsedData = "parsed data",
ParseError = "parse error"
}

View File

@ -0,0 +1,54 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EStreamParserEvent = void 0;
const events_1 = require("events");
class TweetStreamParser extends events_1.EventEmitter {
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) {
// Take [offset, offset+1] inside a new string
if (chunk.slice(offset, offset + 2) === '\r\n') {
// If chunk contains \r\n after current offset,
// parse [start, ..., offset] as a tweet
const piece = chunk.slice(start, offset);
start = offset += 2;
// If empty object
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 = '';
}
}
exports.default = TweetStreamParser;
var EStreamParserEvent;
(function (EStreamParserEvent) {
EStreamParserEvent["ParsedData"] = "parsed data";
EStreamParserEvent["ParseError"] = "parse error";
})(EStreamParserEvent = exports.EStreamParserEvent || (exports.EStreamParserEvent = {}));

View File

@ -0,0 +1,25 @@
import { TwitterApi } from '..';
/** User OAuth 1.0a client */
export declare function getUserClient(this: any): TwitterApi;
export declare function getUserKeys(): {
appKey: string;
appSecret: string;
accessToken: string;
accessSecret: string;
};
export declare function sleepTest(ms: number): Promise<unknown>;
/** User-unlogged OAuth 1.0a client */
export declare function getRequestClient(): TwitterApi;
export declare function getRequestKeys(): {
appKey: string;
appSecret: string;
};
export declare function getAuthLink(callback: string): Promise<{
oauth_token: string;
oauth_token_secret: string;
oauth_callback_confirmed: "true";
url: string;
}>;
export declare function getAccessClient(verifier: string): Promise<TwitterApi>;
/** App OAuth 2.0 client */
export declare function getAppClient(): Promise<TwitterApi>;

View File

@ -0,0 +1,99 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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.getAppClient = exports.getAccessClient = exports.getAuthLink = exports.getRequestKeys = exports.getRequestClient = exports.sleepTest = exports.getUserKeys = exports.getUserClient = void 0;
const __1 = require("..");
const dotenv = __importStar(require("dotenv"));
dotenv.config({ path: __dirname + '/../../.env' });
/** User OAuth 1.0a client */
function getUserClient() {
return new __1.TwitterApi({
appKey: process.env.CONSUMER_TOKEN,
appSecret: process.env.CONSUMER_SECRET,
accessToken: process.env.OAUTH_TOKEN,
accessSecret: process.env.OAUTH_SECRET,
});
}
exports.getUserClient = getUserClient;
function getUserKeys() {
return {
appKey: process.env.CONSUMER_TOKEN,
appSecret: process.env.CONSUMER_SECRET,
accessToken: process.env.OAUTH_TOKEN,
accessSecret: process.env.OAUTH_SECRET,
};
}
exports.getUserKeys = getUserKeys;
async function sleepTest(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
exports.sleepTest = sleepTest;
/** User-unlogged OAuth 1.0a client */
function getRequestClient() {
return new __1.TwitterApi({
appKey: process.env.CONSUMER_TOKEN,
appSecret: process.env.CONSUMER_SECRET,
});
}
exports.getRequestClient = getRequestClient;
function getRequestKeys() {
return {
appKey: process.env.CONSUMER_TOKEN,
appSecret: process.env.CONSUMER_SECRET,
};
}
exports.getRequestKeys = getRequestKeys;
// Test auth 1.0a flow
function getAuthLink(callback) {
return getRequestClient().generateAuthLink(callback);
}
exports.getAuthLink = getAuthLink;
async function getAccessClient(verifier) {
const requestClient = new __1.TwitterApi({
appKey: process.env.CONSUMER_TOKEN,
appSecret: process.env.CONSUMER_SECRET,
accessToken: process.env.OAUTH_TOKEN,
accessSecret: process.env.OAUTH_SECRET,
});
const { client } = await requestClient.login(verifier);
return client;
}
exports.getAccessClient = getAccessClient;
/** App OAuth 2.0 client */
function getAppClient() {
let requestClient;
if (process.env.BEARER_TOKEN) {
requestClient = new __1.TwitterApi(process.env.BEARER_TOKEN);
return Promise.resolve(requestClient);
}
else {
requestClient = new __1.TwitterApi({
appKey: process.env.CONSUMER_TOKEN,
appSecret: process.env.CONSUMER_SECRET,
});
return requestClient.appLogin();
}
}
exports.getAppClient = getAppClient;

View File

@ -0,0 +1,12 @@
import TwitterApiv2LabsReadWrite from './client.v2.labs.write';
/**
* Twitter v2 labs client with all rights (read/write/DMs)
*/
export declare class TwitterApiv2Labs extends TwitterApiv2LabsReadWrite {
protected _prefix: string;
/**
* Get a client with read/write rights.
*/
get readWrite(): TwitterApiv2LabsReadWrite;
}
export default TwitterApiv2Labs;

View File

@ -0,0 +1,25 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TwitterApiv2Labs = void 0;
const globals_1 = require("../globals");
const client_v2_labs_write_1 = __importDefault(require("./client.v2.labs.write"));
/**
* Twitter v2 labs client with all rights (read/write/DMs)
*/
class TwitterApiv2Labs extends client_v2_labs_write_1.default {
constructor() {
super(...arguments);
this._prefix = globals_1.API_V2_LABS_PREFIX;
}
/**
* Get a client with read/write rights.
*/
get readWrite() {
return this;
}
}
exports.TwitterApiv2Labs = TwitterApiv2Labs;
exports.default = TwitterApiv2Labs;

View File

@ -0,0 +1,7 @@
import TwitterApiSubClient from '../client.subclient';
/**
* Base Twitter v2 labs client with only read right.
*/
export default class TwitterApiv2LabsReadOnly extends TwitterApiSubClient {
protected _prefix: string;
}

View File

@ -0,0 +1,17 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const client_subclient_1 = __importDefault(require("../client.subclient"));
const globals_1 = require("../globals");
/**
* Base Twitter v2 labs client with only read right.
*/
class TwitterApiv2LabsReadOnly extends client_subclient_1.default {
constructor() {
super(...arguments);
this._prefix = globals_1.API_V2_LABS_PREFIX;
}
}
exports.default = TwitterApiv2LabsReadOnly;

View File

@ -0,0 +1,11 @@
import TwitterApiv2LabsReadOnly from './client.v2.labs.read';
/**
* Base Twitter v2 labs client with read/write rights.
*/
export default class TwitterApiv2LabsReadWrite extends TwitterApiv2LabsReadOnly {
protected _prefix: string;
/**
* Get a client with only read rights.
*/
get readOnly(): TwitterApiv2LabsReadOnly;
}

View File

@ -0,0 +1,23 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const globals_1 = require("../globals");
const client_v2_labs_read_1 = __importDefault(require("./client.v2.labs.read"));
/**
* Base Twitter v2 labs client with read/write rights.
*/
class TwitterApiv2LabsReadWrite extends client_v2_labs_read_1.default {
constructor() {
super(...arguments);
this._prefix = globals_1.API_V2_LABS_PREFIX;
}
/**
* Get a client with only read rights.
*/
get readOnly() {
return this;
}
}
exports.default = TwitterApiv2LabsReadWrite;

View File

@ -0,0 +1,18 @@
import TwitterApiv2ReadWrite from './client.v2.write';
import TwitterApiv2Labs from '../v2-labs/client.v2.labs';
/**
* Twitter v2 client with all rights (read/write/DMs)
*/
export declare class TwitterApiv2 extends TwitterApiv2ReadWrite {
protected _prefix: string;
protected _labs?: TwitterApiv2Labs;
/**
* Get a client with read/write rights.
*/
get readWrite(): TwitterApiv2ReadWrite;
/**
* Get a client for v2 labs endpoints.
*/
get labs(): TwitterApiv2Labs;
}
export default TwitterApiv2;

View File

@ -0,0 +1,35 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TwitterApiv2 = void 0;
const globals_1 = require("../globals");
const client_v2_write_1 = __importDefault(require("./client.v2.write"));
const client_v2_labs_1 = __importDefault(require("../v2-labs/client.v2.labs"));
/**
* Twitter v2 client with all rights (read/write/DMs)
*/
class TwitterApiv2 extends client_v2_write_1.default {
constructor() {
super(...arguments);
this._prefix = globals_1.API_V2_PREFIX;
}
/* Sub-clients */
/**
* Get a client with read/write rights.
*/
get readWrite() {
return this;
}
/**
* Get a client for v2 labs endpoints.
*/
get labs() {
if (this._labs)
return this._labs;
return this._labs = new client_v2_labs_1.default(this);
}
}
exports.TwitterApiv2 = TwitterApiv2;
exports.default = TwitterApiv2;

View File

@ -0,0 +1,302 @@
import TwitterApiSubClient from '../client.subclient';
import { Tweetv2FieldsParams, Tweetv2SearchParams, UserV2Result, UsersV2Result, UsersV2Params, StreamingV2GetRulesParams, StreamingV2GetRulesResult, TweetV2LookupResult, TweetV2UserTimelineParams, StreamingV2AddRulesParams, StreamingV2DeleteRulesParams, StreamingV2UpdateRulesQuery, StreamingV2UpdateRulesDeleteResult, StreamingV2UpdateRulesAddResult, TweetV2SingleResult, TweetV2PaginableTimelineParams, TweetV2CountResult, TweetV2CountParams, TweetV2CountAllResult, TweetV2CountAllParams, TweetV2RetweetedByResult, TweetV2LikedByResult, UserV2TimelineParams, UserV2TimelineResult, FollowersV2ParamsWithPaginator, FollowersV2ParamsWithoutPaginator, TweetSearchV2StreamParams, TweetV2SingleStreamResult, TweetV2PaginableListParams, SpaceV2FieldsParams, SpaceV2LookupResult, SpaceV2CreatorLookupParams, SpaceV2SearchParams, SpaceV2SingleResult, BatchComplianceSearchV2Params, BatchComplianceListV2Result, BatchComplianceV2Result, BatchComplianceV2Params, BatchComplianceV2JobResult, BatchComplianceJobV2, GetListV2Params, ListGetV2Result, GetListTimelineV2Params, TweetRetweetedOrLikedByV2ParamsWithPaginator, TweetRetweetedOrLikedByV2ParamsWithoutPaginator, SpaceV2BuyersParams, TweetV2HomeTimelineParams } from '../types';
import { TweetSearchAllV2Paginator, TweetSearchRecentV2Paginator, TweetUserMentionTimelineV2Paginator, TweetUserTimelineV2Paginator, TweetV2UserLikedTweetsPaginator, UserOwnedListsV2Paginator, UserListMembershipsV2Paginator, UserListFollowedV2Paginator, TweetV2ListTweetsPaginator, TweetBookmarksTimelineV2Paginator, QuotedTweetsTimelineV2Paginator, TweetHomeTimelineV2Paginator } from '../paginators';
import TwitterApiv2LabsReadOnly from '../v2-labs/client.v2.labs.read';
import { TweetLikingUsersV2Paginator, TweetRetweetersUsersV2Paginator, UserBlockingUsersV2Paginator, UserFollowersV2Paginator, UserFollowingV2Paginator, UserListFollowersV2Paginator, UserListMembersV2Paginator, UserMutingUsersV2Paginator } from '../paginators/user.paginator.v2';
import TweetStream from '../stream/TweetStream';
import { PromiseOrType } from '../types/shared.types';
/**
* Base Twitter v2 client with only read right.
*/
export default class TwitterApiv2ReadOnly extends TwitterApiSubClient {
protected _prefix: string;
protected _labs?: TwitterApiv2LabsReadOnly;
/**
* Get a client for v2 labs endpoints.
*/
get labs(): TwitterApiv2LabsReadOnly;
/**
* The recent search endpoint returns Tweets from the last seven days that match a search query.
* https://developer.twitter.com/en/docs/twitter-api/tweets/search/api-reference/get-tweets-search-recent
*/
search(options: Partial<Tweetv2SearchParams>): Promise<TweetSearchRecentV2Paginator>;
search(query: string, options?: Partial<Tweetv2SearchParams>): Promise<TweetSearchRecentV2Paginator>;
/**
* The full-archive search endpoint returns the complete history of public Tweets matching a search query;
* since the first Tweet was created March 26, 2006.
*
* This endpoint is only available to those users who have been approved for the Academic Research product track.
* https://developer.twitter.com/en/docs/twitter-api/tweets/search/api-reference/get-tweets-search-all
*/
searchAll(query: string, options?: Partial<Tweetv2SearchParams>): Promise<TweetSearchAllV2Paginator>;
/**
* Returns a variety of information about a single Tweet specified by the requested ID.
* https://developer.twitter.com/en/docs/twitter-api/tweets/lookup/api-reference/get-tweets-id
*
* OAuth2 scope: `users.read`, `tweet.read`
*/
singleTweet(tweetId: string, options?: Partial<Tweetv2FieldsParams>): Promise<TweetV2SingleResult>;
/**
* Returns a variety of information about tweets specified by list of IDs.
* https://developer.twitter.com/en/docs/twitter-api/tweets/lookup/api-reference/get-tweets
*
* OAuth2 scope: `users.read`, `tweet.read`
*/
tweets(tweetIds: string | string[], options?: Partial<Tweetv2FieldsParams>): Promise<TweetV2LookupResult>;
/**
* The recent Tweet counts endpoint returns count of Tweets from the last seven days that match a search query.
* OAuth2 Bearer auth only.
* https://developer.twitter.com/en/docs/twitter-api/tweets/counts/api-reference/get-tweets-counts-recent
*/
tweetCountRecent(query: string, options?: Partial<TweetV2CountParams>): Promise<TweetV2CountResult>;
/**
* This endpoint is only available to those users who have been approved for the Academic Research product track.
* The full-archive search endpoint returns the complete history of public Tweets matching a search query;
* since the first Tweet was created March 26, 2006.
* OAuth2 Bearer auth only.
* **This endpoint has pagination, yet it is not supported by bundled paginators. Use `next_token` to fetch next page.**
* https://developer.twitter.com/en/docs/twitter-api/tweets/counts/api-reference/get-tweets-counts-all
*/
tweetCountAll(query: string, options?: Partial<TweetV2CountAllParams>): Promise<TweetV2CountAllResult>;
/**
* Allows you to get information about who has Retweeted a Tweet.
* https://developer.twitter.com/en/docs/twitter-api/tweets/retweets/api-reference/get-tweets-id-retweeted_by
*/
tweetRetweetedBy(tweetId: string, options?: Partial<TweetRetweetedOrLikedByV2ParamsWithoutPaginator>): Promise<TweetV2RetweetedByResult>;
tweetRetweetedBy(tweetId: string, options: TweetRetweetedOrLikedByV2ParamsWithPaginator): Promise<TweetRetweetersUsersV2Paginator>;
/**
* Allows you to get information about who has Liked a Tweet.
* https://developer.twitter.com/en/docs/twitter-api/tweets/likes/api-reference/get-tweets-id-liking_users
*/
tweetLikedBy(tweetId: string, options?: Partial<TweetRetweetedOrLikedByV2ParamsWithoutPaginator>): Promise<TweetV2LikedByResult>;
tweetLikedBy(tweetId: string, options: TweetRetweetedOrLikedByV2ParamsWithPaginator): Promise<TweetLikingUsersV2Paginator>;
/**
* Allows you to retrieve a collection of the most recent Tweets and Retweets posted by you and users you follow, also known as home timeline.
* This endpoint returns up to the last 3200 Tweets.
* https://developer.twitter.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-reverse-chronological
*
* OAuth 2 scopes: `tweet.read` `users.read`
*/
homeTimeline(options?: Partial<TweetV2HomeTimelineParams>): Promise<TweetHomeTimelineV2Paginator>;
/**
* Returns Tweets composed by a single user, specified by the requested user ID.
* By default, the most recent ten Tweets are returned per request.
* Using pagination, the most recent 3,200 Tweets can be retrieved.
* https://developer.twitter.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-tweets
*/
userTimeline(userId: string, options?: Partial<TweetV2UserTimelineParams>): Promise<TweetUserTimelineV2Paginator>;
/**
* Returns Tweets mentioning a single user specified by the requested user ID.
* By default, the most recent ten Tweets are returned per request.
* Using pagination, up to the most recent 800 Tweets can be retrieved.
* https://developer.twitter.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-mentions
*/
userMentionTimeline(userId: string, options?: Partial<TweetV2PaginableTimelineParams>): Promise<TweetUserMentionTimelineV2Paginator>;
/**
* Returns Quote Tweets for a Tweet specified by the requested Tweet ID.
* https://developer.twitter.com/en/docs/twitter-api/tweets/quote-tweets/api-reference/get-tweets-id-quote_tweets
*
* OAuth2 scopes: `users.read` `tweet.read`
*/
quotes(tweetId: string, options?: Partial<TweetV2PaginableTimelineParams>): Promise<QuotedTweetsTimelineV2Paginator>;
/**
* Allows you to get information about a authenticated users 800 most recent bookmarked Tweets.
* https://developer.twitter.com/en/docs/twitter-api/tweets/bookmarks/api-reference/get-users-id-bookmarks
*
* OAuth2 scopes: `users.read` `tweet.read` `bookmark.read`
*/
bookmarks(options?: Partial<TweetV2PaginableTimelineParams>): Promise<TweetBookmarksTimelineV2Paginator>;
/**
* Returns information about an authorized user.
* https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-me
*
* OAuth2 scopes: `tweet.read` & `users.read`
*/
me(options?: Partial<UsersV2Params>): Promise<UserV2Result>;
/**
* Returns a variety of information about a single user specified by the requested ID.
* https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-id
*/
user(userId: string, options?: Partial<UsersV2Params>): Promise<UserV2Result>;
/**
* Returns a variety of information about one or more users specified by the requested IDs.
* https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users
*/
users(userIds: string | string[], options?: Partial<UsersV2Params>): Promise<UsersV2Result>;
/**
* Returns a variety of information about a single user specified by their username.
* https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-by-username-username
*/
userByUsername(username: string, options?: Partial<UsersV2Params>): Promise<UserV2Result>;
/**
* Returns a variety of information about one or more users specified by their usernames.
* https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-by
*
* OAuth2 scope: `users.read`, `tweet.read`
*/
usersByUsernames(usernames: string | string[], options?: Partial<UsersV2Params>): Promise<UsersV2Result>;
/**
* Returns a list of users who are followers of the specified user ID.
* https://developer.twitter.com/en/docs/twitter-api/users/follows/api-reference/get-users-id-followers
*/
followers(userId: string, options?: Partial<FollowersV2ParamsWithoutPaginator>): Promise<UserV2TimelineResult>;
followers(userId: string, options: FollowersV2ParamsWithPaginator): Promise<UserFollowersV2Paginator>;
/**
* Returns a list of users the specified user ID is following.
* https://developer.twitter.com/en/docs/twitter-api/users/follows/api-reference/get-users-id-following
*
* OAuth2 scope: `follows.read`
*/
following(userId: string, options?: Partial<FollowersV2ParamsWithoutPaginator>): Promise<UserV2TimelineResult>;
following(userId: string, options: FollowersV2ParamsWithPaginator): Promise<UserFollowingV2Paginator>;
/**
* Allows you to get information about a users liked Tweets.
* https://developer.twitter.com/en/docs/twitter-api/tweets/likes/api-reference/get-users-id-liked_tweets
*/
userLikedTweets(userId: string, options?: Partial<TweetV2PaginableListParams>): Promise<TweetV2UserLikedTweetsPaginator>;
/**
* Returns a list of users who are blocked by the authenticating user.
* https://developer.twitter.com/en/docs/twitter-api/users/blocks/api-reference/get-users-blocking
*/
userBlockingUsers(userId: string, options?: Partial<UserV2TimelineParams>): Promise<UserBlockingUsersV2Paginator>;
/**
* Returns a list of users who are muted by the authenticating user.
* https://developer.twitter.com/en/docs/twitter-api/users/mutes/api-reference/get-users-muting
*/
userMutingUsers(userId: string, options?: Partial<UserV2TimelineParams>): Promise<UserMutingUsersV2Paginator>;
/**
* Returns the details of a specified List.
* https://developer.twitter.com/en/docs/twitter-api/lists/list-lookup/api-reference/get-lists-id
*/
list(id: string, options?: Partial<GetListV2Params>): Promise<ListGetV2Result>;
/**
* Returns all Lists owned by the specified user.
* https://developer.twitter.com/en/docs/twitter-api/lists/list-lookup/api-reference/get-users-id-owned_lists
*/
listsOwned(userId: string, options?: Partial<GetListTimelineV2Params>): Promise<UserOwnedListsV2Paginator>;
/**
* Returns all Lists a specified user is a member of.
* https://developer.twitter.com/en/docs/twitter-api/lists/list-members/api-reference/get-users-id-list_memberships
*/
listMemberships(userId: string, options?: Partial<GetListTimelineV2Params>): Promise<UserListMembershipsV2Paginator>;
/**
* Returns all Lists a specified user follows.
* https://developer.twitter.com/en/docs/twitter-api/lists/list-follows/api-reference/get-users-id-followed_lists
*/
listFollowed(userId: string, options?: Partial<GetListTimelineV2Params>): Promise<UserListFollowedV2Paginator>;
/**
* Returns a list of Tweets from the specified List.
* https://developer.twitter.com/en/docs/twitter-api/lists/list-tweets/api-reference/get-lists-id-tweets
*/
listTweets(listId: string, options?: Partial<TweetV2PaginableListParams>): Promise<TweetV2ListTweetsPaginator>;
/**
* Returns a list of users who are members of the specified List.
* https://developer.twitter.com/en/docs/twitter-api/lists/list-members/api-reference/get-lists-id-members
*/
listMembers(listId: string, options?: Partial<UserV2TimelineParams>): Promise<UserListMembersV2Paginator>;
/**
* Returns a list of users who are followers of the specified List.
* https://developer.twitter.com/en/docs/twitter-api/lists/list-follows/api-reference/get-lists-id-followers
*/
listFollowers(listId: string, options?: Partial<UserV2TimelineParams>): Promise<UserListFollowersV2Paginator>;
/**
* Get a single space by ID.
* https://developer.twitter.com/en/docs/twitter-api/spaces/lookup/api-reference/get-spaces-id
*
* OAuth2 scopes: `tweet.read`, `users.read`, `space.read`.
*/
space(spaceId: string, options?: Partial<SpaceV2FieldsParams>): Promise<SpaceV2SingleResult>;
/**
* Get spaces using their IDs.
* https://developer.twitter.com/en/docs/twitter-api/spaces/lookup/api-reference/get-spaces
*
* OAuth2 scopes: `tweet.read`, `users.read`, `space.read`.
*/
spaces(spaceIds: string | string[], options?: Partial<SpaceV2FieldsParams>): Promise<SpaceV2LookupResult>;
/**
* Get spaces using their creator user ID(s). (no pagination available)
* https://developer.twitter.com/en/docs/twitter-api/spaces/lookup/api-reference/get-spaces-by-creator-ids
*
* OAuth2 scopes: `tweet.read`, `users.read`, `space.read`.
*/
spacesByCreators(creatorIds: string | string[], options?: Partial<SpaceV2CreatorLookupParams>): Promise<SpaceV2LookupResult>;
/**
* Search through spaces using multiple params. (no pagination available)
* https://developer.twitter.com/en/docs/twitter-api/spaces/search/api-reference/get-spaces-search
*/
searchSpaces(options: SpaceV2SearchParams): Promise<SpaceV2LookupResult>;
/**
* Returns a list of user who purchased a ticket to the requested Space.
* You must authenticate the request using the Access Token of the creator of the requested Space.
*
* **OAuth 2.0 Access Token required**
*
* https://developer.twitter.com/en/docs/twitter-api/spaces/lookup/api-reference/get-spaces-id-buyers
*
* OAuth2 scopes: `tweet.read`, `users.read`, `space.read`.
*/
spaceBuyers(spaceId: string, options?: Partial<SpaceV2BuyersParams>): Promise<UsersV2Result>;
/**
* Streams Tweets in real-time based on a specific set of filter rules.
* https://developer.twitter.com/en/docs/twitter-api/tweets/filtered-stream/api-reference/get-tweets-search-stream
*/
searchStream(options?: Partial<TweetSearchV2StreamParams> & {
autoConnect?: true;
}): Promise<TweetStream<TweetV2SingleStreamResult>>;
searchStream(options: Partial<TweetSearchV2StreamParams> & {
autoConnect: false;
}): TweetStream<TweetV2SingleStreamResult>;
searchStream(options?: Partial<TweetSearchV2StreamParams> & {
autoConnect?: boolean;
}): PromiseOrType<TweetStream<TweetV2SingleStreamResult>>;
/**
* Return a list of rules currently active on the streaming endpoint, either as a list or individually.
* https://developer.twitter.com/en/docs/twitter-api/tweets/filtered-stream/api-reference/get-tweets-search-stream-rules
*/
streamRules(options?: Partial<StreamingV2GetRulesParams>): Promise<StreamingV2GetRulesResult>;
/**
* Add or delete rules to your stream.
* To create one or more rules, submit an add JSON body with an array of rules and operators.
* Similarly, to delete one or more rules, submit a delete JSON body with an array of list of existing rule IDs.
* https://developer.twitter.com/en/docs/twitter-api/tweets/filtered-stream/api-reference/post-tweets-search-stream-rules
*/
updateStreamRules(options: StreamingV2AddRulesParams, query?: Partial<StreamingV2UpdateRulesQuery>): Promise<StreamingV2UpdateRulesAddResult>;
updateStreamRules(options: StreamingV2DeleteRulesParams, query?: Partial<StreamingV2UpdateRulesQuery>): Promise<StreamingV2UpdateRulesDeleteResult>;
/**
* Streams about 1% of all Tweets in real-time.
* https://developer.twitter.com/en/docs/twitter-api/tweets/volume-streams/api-reference/get-tweets-sample-stream
*/
sampleStream(options?: Partial<Tweetv2FieldsParams> & {
autoConnect?: true;
}): Promise<TweetStream<TweetV2SingleResult>>;
sampleStream(options: Partial<Tweetv2FieldsParams> & {
autoConnect: false;
}): TweetStream<TweetV2SingleResult>;
sampleStream(options?: Partial<Tweetv2FieldsParams> & {
autoConnect?: boolean;
}): PromiseOrType<TweetStream<TweetV2SingleResult>>;
/**
* Returns a list of recent compliance jobs.
* https://developer.twitter.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/get-compliance-jobs
*/
complianceJobs(options: BatchComplianceSearchV2Params): Promise<BatchComplianceListV2Result>;
/**
* Get a single compliance job with the specified ID.
* https://developer.twitter.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/get-compliance-jobs-id
*/
complianceJob(jobId: string): Promise<BatchComplianceV2Result>;
/**
* Creates a new compliance job for Tweet IDs or user IDs, send your file, await result and parse it into an array.
* You can run one batch job at a time. Returns the created job, but **not the job result!**.
*
* You can obtain the result (**after job is completed**) with `.complianceJobResult`.
* https://developer.twitter.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/post-compliance-jobs
*/
sendComplianceJob(jobParams: BatchComplianceV2Params): Promise<BatchComplianceV2Result>;
/**
* Get the result of a running or completed job, obtained through `.complianceJob`, `.complianceJobs` or `.sendComplianceJob`.
* If job is still running (`in_progress`), it will await until job is completed. **This could be quite long!**
* https://developer.twitter.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/post-compliance-jobs
*/
complianceJobResult(job: BatchComplianceJobV2): Promise<BatchComplianceV2JobResult[]>;
}

View File

@ -0,0 +1,568 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const client_subclient_1 = __importDefault(require("../client.subclient"));
const globals_1 = require("../globals");
const paginators_1 = require("../paginators");
const client_v2_labs_read_1 = __importDefault(require("../v2-labs/client.v2.labs.read"));
const user_paginator_v2_1 = require("../paginators/user.paginator.v2");
const helpers_1 = require("../helpers");
/**
* Base Twitter v2 client with only read right.
*/
class TwitterApiv2ReadOnly extends client_subclient_1.default {
constructor() {
super(...arguments);
this._prefix = globals_1.API_V2_PREFIX;
}
/* Sub-clients */
/**
* Get a client for v2 labs endpoints.
*/
get labs() {
if (this._labs)
return this._labs;
return this._labs = new client_v2_labs_read_1.default(this);
}
async search(queryOrOptions, options = {}) {
const query = typeof queryOrOptions === 'string' ? queryOrOptions : undefined;
const realOptions = typeof queryOrOptions === 'object' && queryOrOptions !== null ? queryOrOptions : options;
const queryParams = { ...realOptions, query };
const initialRq = await this.get('tweets/search/recent', queryParams, { fullResponse: true });
return new paginators_1.TweetSearchRecentV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams,
});
}
/**
* The full-archive search endpoint returns the complete history of public Tweets matching a search query;
* since the first Tweet was created March 26, 2006.
*
* This endpoint is only available to those users who have been approved for the Academic Research product track.
* https://developer.twitter.com/en/docs/twitter-api/tweets/search/api-reference/get-tweets-search-all
*/
async searchAll(query, options = {}) {
const queryParams = { ...options, query };
const initialRq = await this.get('tweets/search/all', queryParams, { fullResponse: true });
return new paginators_1.TweetSearchAllV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams,
});
}
/**
* Returns a variety of information about a single Tweet specified by the requested ID.
* https://developer.twitter.com/en/docs/twitter-api/tweets/lookup/api-reference/get-tweets-id
*
* OAuth2 scope: `users.read`, `tweet.read`
*/
singleTweet(tweetId, options = {}) {
return this.get('tweets/:id', options, { params: { id: tweetId } });
}
/**
* Returns a variety of information about tweets specified by list of IDs.
* https://developer.twitter.com/en/docs/twitter-api/tweets/lookup/api-reference/get-tweets
*
* OAuth2 scope: `users.read`, `tweet.read`
*/
tweets(tweetIds, options = {}) {
return this.get('tweets', { ids: tweetIds, ...options });
}
/**
* The recent Tweet counts endpoint returns count of Tweets from the last seven days that match a search query.
* OAuth2 Bearer auth only.
* https://developer.twitter.com/en/docs/twitter-api/tweets/counts/api-reference/get-tweets-counts-recent
*/
tweetCountRecent(query, options = {}) {
return this.get('tweets/counts/recent', { query, ...options });
}
/**
* This endpoint is only available to those users who have been approved for the Academic Research product track.
* The full-archive search endpoint returns the complete history of public Tweets matching a search query;
* since the first Tweet was created March 26, 2006.
* OAuth2 Bearer auth only.
* **This endpoint has pagination, yet it is not supported by bundled paginators. Use `next_token` to fetch next page.**
* https://developer.twitter.com/en/docs/twitter-api/tweets/counts/api-reference/get-tweets-counts-all
*/
tweetCountAll(query, options = {}) {
return this.get('tweets/counts/all', { query, ...options });
}
async tweetRetweetedBy(tweetId, options = {}) {
const { asPaginator, ...parameters } = options;
const initialRq = await this.get('tweets/:id/retweeted_by', parameters, {
fullResponse: true,
params: { id: tweetId },
});
if (!asPaginator) {
return initialRq.data;
}
return new user_paginator_v2_1.TweetRetweetersUsersV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: parameters,
sharedParams: { id: tweetId },
});
}
async tweetLikedBy(tweetId, options = {}) {
const { asPaginator, ...parameters } = options;
const initialRq = await this.get('tweets/:id/liking_users', parameters, {
fullResponse: true,
params: { id: tweetId },
});
if (!asPaginator) {
return initialRq.data;
}
return new user_paginator_v2_1.TweetLikingUsersV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: parameters,
sharedParams: { id: tweetId },
});
}
/**
* Allows you to retrieve a collection of the most recent Tweets and Retweets posted by you and users you follow, also known as home timeline.
* This endpoint returns up to the last 3200 Tweets.
* https://developer.twitter.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-reverse-chronological
*
* OAuth 2 scopes: `tweet.read` `users.read`
*/
async homeTimeline(options = {}) {
const meUser = await this.getCurrentUserV2Object();
const initialRq = await this.get('users/:id/timelines/reverse_chronological', options, {
fullResponse: true,
params: { id: meUser.data.id },
});
return new paginators_1.TweetHomeTimelineV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: options,
sharedParams: { id: meUser.data.id },
});
}
/**
* Returns Tweets composed by a single user, specified by the requested user ID.
* By default, the most recent ten Tweets are returned per request.
* Using pagination, the most recent 3,200 Tweets can be retrieved.
* https://developer.twitter.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-tweets
*/
async userTimeline(userId, options = {}) {
const initialRq = await this.get('users/:id/tweets', options, {
fullResponse: true,
params: { id: userId },
});
return new paginators_1.TweetUserTimelineV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: options,
sharedParams: { id: userId },
});
}
/**
* Returns Tweets mentioning a single user specified by the requested user ID.
* By default, the most recent ten Tweets are returned per request.
* Using pagination, up to the most recent 800 Tweets can be retrieved.
* https://developer.twitter.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-mentions
*/
async userMentionTimeline(userId, options = {}) {
const initialRq = await this.get('users/:id/mentions', options, {
fullResponse: true,
params: { id: userId },
});
return new paginators_1.TweetUserMentionTimelineV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: options,
sharedParams: { id: userId },
});
}
/**
* Returns Quote Tweets for a Tweet specified by the requested Tweet ID.
* https://developer.twitter.com/en/docs/twitter-api/tweets/quote-tweets/api-reference/get-tweets-id-quote_tweets
*
* OAuth2 scopes: `users.read` `tweet.read`
*/
async quotes(tweetId, options = {}) {
const initialRq = await this.get('tweets/:id/quote_tweets', options, {
fullResponse: true,
params: { id: tweetId },
});
return new paginators_1.QuotedTweetsTimelineV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: options,
sharedParams: { id: tweetId },
});
}
/* Bookmarks */
/**
* Allows you to get information about a authenticated users 800 most recent bookmarked Tweets.
* https://developer.twitter.com/en/docs/twitter-api/tweets/bookmarks/api-reference/get-users-id-bookmarks
*
* OAuth2 scopes: `users.read` `tweet.read` `bookmark.read`
*/
async bookmarks(options = {}) {
const user = await this.getCurrentUserV2Object();
const initialRq = await this.get('users/:id/bookmarks', options, {
fullResponse: true,
params: { id: user.data.id },
});
return new paginators_1.TweetBookmarksTimelineV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: options,
sharedParams: { id: user.data.id },
});
}
/* Users */
/**
* Returns information about an authorized user.
* https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-me
*
* OAuth2 scopes: `tweet.read` & `users.read`
*/
me(options = {}) {
return this.get('users/me', options);
}
/**
* Returns a variety of information about a single user specified by the requested ID.
* https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-id
*/
user(userId, options = {}) {
return this.get('users/:id', options, { params: { id: userId } });
}
/**
* Returns a variety of information about one or more users specified by the requested IDs.
* https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users
*/
users(userIds, options = {}) {
const ids = Array.isArray(userIds) ? userIds.join(',') : userIds;
return this.get('users', { ...options, ids });
}
/**
* Returns a variety of information about a single user specified by their username.
* https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-by-username-username
*/
userByUsername(username, options = {}) {
return this.get('users/by/username/:username', options, { params: { username } });
}
/**
* Returns a variety of information about one or more users specified by their usernames.
* https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-by
*
* OAuth2 scope: `users.read`, `tweet.read`
*/
usersByUsernames(usernames, options = {}) {
usernames = Array.isArray(usernames) ? usernames.join(',') : usernames;
return this.get('users/by', { ...options, usernames });
}
async followers(userId, options = {}) {
const { asPaginator, ...parameters } = options;
const params = { id: userId };
if (!asPaginator) {
return this.get('users/:id/followers', parameters, { params });
}
const initialRq = await this.get('users/:id/followers', parameters, { fullResponse: true, params });
return new user_paginator_v2_1.UserFollowersV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: parameters,
sharedParams: params,
});
}
async following(userId, options = {}) {
const { asPaginator, ...parameters } = options;
const params = { id: userId };
if (!asPaginator) {
return this.get('users/:id/following', parameters, { params });
}
const initialRq = await this.get('users/:id/following', parameters, { fullResponse: true, params });
return new user_paginator_v2_1.UserFollowingV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: parameters,
sharedParams: params,
});
}
/**
* Allows you to get information about a users liked Tweets.
* https://developer.twitter.com/en/docs/twitter-api/tweets/likes/api-reference/get-users-id-liked_tweets
*/
async userLikedTweets(userId, options = {}) {
const params = { id: userId };
const initialRq = await this.get('users/:id/liked_tweets', options, { fullResponse: true, params });
return new paginators_1.TweetV2UserLikedTweetsPaginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: { ...options },
sharedParams: params,
});
}
/**
* Returns a list of users who are blocked by the authenticating user.
* https://developer.twitter.com/en/docs/twitter-api/users/blocks/api-reference/get-users-blocking
*/
async userBlockingUsers(userId, options = {}) {
const params = { id: userId };
const initialRq = await this.get('users/:id/blocking', options, { fullResponse: true, params });
return new user_paginator_v2_1.UserBlockingUsersV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: { ...options },
sharedParams: params,
});
}
/**
* Returns a list of users who are muted by the authenticating user.
* https://developer.twitter.com/en/docs/twitter-api/users/mutes/api-reference/get-users-muting
*/
async userMutingUsers(userId, options = {}) {
const params = { id: userId };
const initialRq = await this.get('users/:id/muting', options, { fullResponse: true, params });
return new user_paginator_v2_1.UserMutingUsersV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: { ...options },
sharedParams: params,
});
}
/* Lists */
/**
* Returns the details of a specified List.
* https://developer.twitter.com/en/docs/twitter-api/lists/list-lookup/api-reference/get-lists-id
*/
list(id, options = {}) {
return this.get('lists/:id', options, { params: { id } });
}
/**
* Returns all Lists owned by the specified user.
* https://developer.twitter.com/en/docs/twitter-api/lists/list-lookup/api-reference/get-users-id-owned_lists
*/
async listsOwned(userId, options = {}) {
const params = { id: userId };
const initialRq = await this.get('users/:id/owned_lists', options, { fullResponse: true, params });
return new paginators_1.UserOwnedListsV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: { ...options },
sharedParams: params,
});
}
/**
* Returns all Lists a specified user is a member of.
* https://developer.twitter.com/en/docs/twitter-api/lists/list-members/api-reference/get-users-id-list_memberships
*/
async listMemberships(userId, options = {}) {
const params = { id: userId };
const initialRq = await this.get('users/:id/list_memberships', options, { fullResponse: true, params });
return new paginators_1.UserListMembershipsV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: { ...options },
sharedParams: params,
});
}
/**
* Returns all Lists a specified user follows.
* https://developer.twitter.com/en/docs/twitter-api/lists/list-follows/api-reference/get-users-id-followed_lists
*/
async listFollowed(userId, options = {}) {
const params = { id: userId };
const initialRq = await this.get('users/:id/followed_lists', options, { fullResponse: true, params });
return new paginators_1.UserListFollowedV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: { ...options },
sharedParams: params,
});
}
/**
* Returns a list of Tweets from the specified List.
* https://developer.twitter.com/en/docs/twitter-api/lists/list-tweets/api-reference/get-lists-id-tweets
*/
async listTweets(listId, options = {}) {
const params = { id: listId };
const initialRq = await this.get('lists/:id/tweets', options, { fullResponse: true, params });
return new paginators_1.TweetV2ListTweetsPaginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: { ...options },
sharedParams: params,
});
}
/**
* Returns a list of users who are members of the specified List.
* https://developer.twitter.com/en/docs/twitter-api/lists/list-members/api-reference/get-lists-id-members
*/
async listMembers(listId, options = {}) {
const params = { id: listId };
const initialRq = await this.get('lists/:id/members', options, { fullResponse: true, params });
return new user_paginator_v2_1.UserListMembersV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: { ...options },
sharedParams: params,
});
}
/**
* Returns a list of users who are followers of the specified List.
* https://developer.twitter.com/en/docs/twitter-api/lists/list-follows/api-reference/get-lists-id-followers
*/
async listFollowers(listId, options = {}) {
const params = { id: listId };
const initialRq = await this.get('lists/:id/followers', options, { fullResponse: true, params });
return new user_paginator_v2_1.UserListFollowersV2Paginator({
realData: initialRq.data,
rateLimit: initialRq.rateLimit,
instance: this,
queryParams: { ...options },
sharedParams: params,
});
}
/* Spaces */
/**
* Get a single space by ID.
* https://developer.twitter.com/en/docs/twitter-api/spaces/lookup/api-reference/get-spaces-id
*
* OAuth2 scopes: `tweet.read`, `users.read`, `space.read`.
*/
space(spaceId, options = {}) {
return this.get('spaces/:id', options, { params: { id: spaceId } });
}
/**
* Get spaces using their IDs.
* https://developer.twitter.com/en/docs/twitter-api/spaces/lookup/api-reference/get-spaces
*
* OAuth2 scopes: `tweet.read`, `users.read`, `space.read`.
*/
spaces(spaceIds, options = {}) {
return this.get('spaces', { ids: spaceIds, ...options });
}
/**
* Get spaces using their creator user ID(s). (no pagination available)
* https://developer.twitter.com/en/docs/twitter-api/spaces/lookup/api-reference/get-spaces-by-creator-ids
*
* OAuth2 scopes: `tweet.read`, `users.read`, `space.read`.
*/
spacesByCreators(creatorIds, options = {}) {
return this.get('spaces/by/creator_ids', { user_ids: creatorIds, ...options });
}
/**
* Search through spaces using multiple params. (no pagination available)
* https://developer.twitter.com/en/docs/twitter-api/spaces/search/api-reference/get-spaces-search
*/
searchSpaces(options) {
return this.get('spaces/search', options);
}
/**
* Returns a list of user who purchased a ticket to the requested Space.
* You must authenticate the request using the Access Token of the creator of the requested Space.
*
* **OAuth 2.0 Access Token required**
*
* https://developer.twitter.com/en/docs/twitter-api/spaces/lookup/api-reference/get-spaces-id-buyers
*
* OAuth2 scopes: `tweet.read`, `users.read`, `space.read`.
*/
spaceBuyers(spaceId, options = {}) {
return this.get('spaces/:id/buyers', options, { params: { id: spaceId } });
}
searchStream({ autoConnect, ...options } = {}) {
return this.getStream('tweets/search/stream', options, { payloadIsError: helpers_1.isTweetStreamV2ErrorPayload, autoConnect });
}
/**
* Return a list of rules currently active on the streaming endpoint, either as a list or individually.
* https://developer.twitter.com/en/docs/twitter-api/tweets/filtered-stream/api-reference/get-tweets-search-stream-rules
*/
streamRules(options = {}) {
return this.get('tweets/search/stream/rules', options);
}
updateStreamRules(options, query = {}) {
return this.post('tweets/search/stream/rules', options, { query });
}
sampleStream({ autoConnect, ...options } = {}) {
return this.getStream('tweets/sample/stream', options, { payloadIsError: helpers_1.isTweetStreamV2ErrorPayload, autoConnect });
}
/* Batch compliance */
/**
* Returns a list of recent compliance jobs.
* https://developer.twitter.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/get-compliance-jobs
*/
complianceJobs(options) {
return this.get('compliance/jobs', options);
}
/**
* Get a single compliance job with the specified ID.
* https://developer.twitter.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/get-compliance-jobs-id
*/
complianceJob(jobId) {
return this.get('compliance/jobs/:id', undefined, { params: { id: jobId } });
}
/**
* Creates a new compliance job for Tweet IDs or user IDs, send your file, await result and parse it into an array.
* You can run one batch job at a time. Returns the created job, but **not the job result!**.
*
* You can obtain the result (**after job is completed**) with `.complianceJobResult`.
* https://developer.twitter.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/post-compliance-jobs
*/
async sendComplianceJob(jobParams) {
const job = await this.post('compliance/jobs', { type: jobParams.type, name: jobParams.name });
// Send the IDs
const rawIdsBody = jobParams.ids instanceof Buffer ? jobParams.ids : Buffer.from(jobParams.ids.join('\n'));
// Upload the IDs
await this.put(job.data.upload_url, rawIdsBody, {
forceBodyMode: 'raw',
enableAuth: false,
headers: { 'Content-Type': 'text/plain' },
prefix: '',
});
return job;
}
/**
* Get the result of a running or completed job, obtained through `.complianceJob`, `.complianceJobs` or `.sendComplianceJob`.
* If job is still running (`in_progress`), it will await until job is completed. **This could be quite long!**
* https://developer.twitter.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/post-compliance-jobs
*/
async complianceJobResult(job) {
let runningJob = job;
while (runningJob.status !== 'complete') {
if (runningJob.status === 'expired' || runningJob.status === 'failed') {
throw new Error('Job failed to be completed.');
}
await new Promise(resolve => setTimeout(resolve, 3500));
runningJob = (await this.complianceJob(job.id)).data;
}
// Download and parse result
const result = await this.get(job.download_url, undefined, {
enableAuth: false,
prefix: '',
});
return result
.trim()
.split('\n')
.filter(line => line)
.map(line => JSON.parse(line));
}
}
exports.default = TwitterApiv2ReadOnly;

View File

@ -0,0 +1,187 @@
import TwitterApiv2ReadOnly from './client.v2.read';
import type { ListCreateV2Params, ListCreateV2Result, ListDeleteV2Result, ListFollowV2Result, ListMemberV2Result, ListPinV2Result, ListUpdateV2Params, ListUpdateV2Result, TweetV2DeleteTweetResult, SendTweetV2Params, TweetV2HideReplyResult, TweetV2LikeResult, TweetV2PostTweetResult, TweetV2RetweetResult, UserV2BlockResult, UserV2FollowResult, UserV2MuteResult, UserV2UnfollowResult, TweetV2BookmarkResult } from '../types';
import TwitterApiv2LabsReadWrite from '../v2-labs/client.v2.labs.write';
/**
* Base Twitter v2 client with read/write rights.
*/
export default class TwitterApiv2ReadWrite extends TwitterApiv2ReadOnly {
protected _prefix: string;
protected _labs?: TwitterApiv2LabsReadWrite;
/**
* Get a client with only read rights.
*/
get readOnly(): TwitterApiv2ReadOnly;
/**
* Get a client for v2 labs endpoints.
*/
get labs(): TwitterApiv2LabsReadWrite;
/**
* Hides or unhides a reply to a Tweet.
* https://developer.twitter.com/en/docs/twitter-api/tweets/hide-replies/api-reference/put-tweets-id-hidden
*/
hideReply(tweetId: string, makeHidden: boolean): Promise<TweetV2HideReplyResult>;
/**
* Causes the user ID identified in the path parameter to Like the target Tweet.
* https://developer.twitter.com/en/docs/twitter-api/tweets/likes/api-reference/post-users-user_id-likes
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
like(loggedUserId: string, targetTweetId: string): Promise<TweetV2LikeResult>;
/**
* Allows a user or authenticated user ID to unlike a Tweet.
* The request succeeds with no action when the user sends a request to a user they're not liking the Tweet or have already unliked the Tweet.
* https://developer.twitter.com/en/docs/twitter-api/tweets/likes/api-reference/delete-users-id-likes-tweet_id
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
unlike(loggedUserId: string, targetTweetId: string): Promise<TweetV2LikeResult>;
/**
* Causes the user ID identified in the path parameter to Retweet the target Tweet.
* https://developer.twitter.com/en/docs/twitter-api/tweets/retweets/api-reference/post-users-id-retweets
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
retweet(loggedUserId: string, targetTweetId: string): Promise<TweetV2RetweetResult>;
/**
* Allows a user or authenticated user ID to remove the Retweet of a Tweet.
* The request succeeds with no action when the user sends a request to a user they're not Retweeting the Tweet or have already removed the Retweet of.
* https://developer.twitter.com/en/docs/twitter-api/tweets/retweets/api-reference/delete-users-id-retweets-tweet_id
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
unretweet(loggedUserId: string, targetTweetId: string): Promise<TweetV2RetweetResult>;
/**
* Creates a Tweet on behalf of an authenticated user.
* https://developer.twitter.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/post-tweets
*/
tweet(status: string, payload?: Partial<SendTweetV2Params>): Promise<TweetV2PostTweetResult>;
tweet(payload: SendTweetV2Params): Promise<TweetV2PostTweetResult>;
/**
* Reply to a Tweet on behalf of an authenticated user.
* https://developer.twitter.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/post-tweets
*/
reply(status: string, toTweetId: string, payload?: Partial<SendTweetV2Params>): Promise<TweetV2PostTweetResult>;
/**
* Quote an existing Tweet on behalf of an authenticated user.
* https://developer.twitter.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/post-tweets
*/
quote(status: string, quotedTweetId: string, payload?: Partial<SendTweetV2Params>): Promise<TweetV2PostTweetResult>;
/**
* Post a series of tweets.
* https://developer.twitter.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/post-tweets
*/
tweetThread(tweets: (SendTweetV2Params | string)[]): Promise<TweetV2PostTweetResult[]>;
/**
* Allows a user or authenticated user ID to delete a Tweet
* https://developer.twitter.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/delete-tweets-id
*/
deleteTweet(tweetId: string): Promise<TweetV2DeleteTweetResult>;
/**
* Causes the user ID of an authenticated user identified in the path parameter to Bookmark the target Tweet provided in the request body.
* https://developer.twitter.com/en/docs/twitter-api/tweets/bookmarks/api-reference/post-users-id-bookmarks
*
* OAuth2 scopes: `users.read` `tweet.read` `bookmark.write`
*/
bookmark(tweetId: string): Promise<TweetV2BookmarkResult>;
/**
* Allows a user or authenticated user ID to remove a Bookmark of a Tweet.
* https://developer.twitter.com/en/docs/twitter-api/tweets/bookmarks/api-reference/delete-users-id-bookmarks-tweet_id
*
* OAuth2 scopes: `users.read` `tweet.read` `bookmark.write`
*/
deleteBookmark(tweetId: string): Promise<TweetV2BookmarkResult>;
/**
* Allows a user ID to follow another user.
* If the target user does not have public Tweets, this endpoint will send a follow request.
* https://developer.twitter.com/en/docs/twitter-api/users/follows/api-reference/post-users-source_user_id-following
*
* OAuth2 scope: `follows.write`
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
follow(loggedUserId: string, targetUserId: string): Promise<UserV2FollowResult>;
/**
* Allows a user ID to unfollow another user.
* https://developer.twitter.com/en/docs/twitter-api/users/follows/api-reference/delete-users-source_id-following
*
* OAuth2 scope: `follows.write`
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
unfollow(loggedUserId: string, targetUserId: string): Promise<UserV2UnfollowResult>;
/**
* Causes the user (in the path) to block the target user.
* The user (in the path) must match the user context authorizing the request.
* https://developer.twitter.com/en/docs/twitter-api/users/blocks/api-reference/post-users-user_id-blocking
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
block(loggedUserId: string, targetUserId: string): Promise<UserV2BlockResult>;
/**
* Allows a user or authenticated user ID to unblock another user.
* https://developer.twitter.com/en/docs/twitter-api/users/blocks/api-reference/delete-users-user_id-blocking
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
unblock(loggedUserId: string, targetUserId: string): Promise<UserV2BlockResult>;
/**
* Allows an authenticated user ID to mute the target user.
* https://developer.twitter.com/en/docs/twitter-api/users/mutes/api-reference/post-users-user_id-muting
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
mute(loggedUserId: string, targetUserId: string): Promise<UserV2MuteResult>;
/**
* Allows an authenticated user ID to unmute the target user.
* The request succeeds with no action when the user sends a request to a user they're not muting or have already unmuted.
* https://developer.twitter.com/en/docs/twitter-api/users/mutes/api-reference/delete-users-user_id-muting
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
unmute(loggedUserId: string, targetUserId: string): Promise<UserV2MuteResult>;
/**
* Creates a new list for the authenticated user.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/post-lists
*/
createList(options: ListCreateV2Params): Promise<ListCreateV2Result>;
/**
* Updates the specified list. The authenticated user must own the list to be able to update it.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/put-lists-id
*/
updateList(listId: string, options?: ListUpdateV2Params): Promise<ListUpdateV2Result>;
/**
* Deletes the specified list. The authenticated user must own the list to be able to destroy it.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/delete-lists-id
*/
removeList(listId: string): Promise<ListDeleteV2Result>;
/**
* Adds a member to a list.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/post-lists-id-members
*/
addListMember(listId: string, userId: string): Promise<ListMemberV2Result>;
/**
* Remember a member to a list.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/delete-lists-id-members-user_id
*/
removeListMember(listId: string, userId: string): Promise<ListMemberV2Result>;
/**
* Subscribes the authenticated user to the specified list.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/post-users-id-followed-lists
*/
subscribeToList(loggedUserId: string, listId: string): Promise<ListFollowV2Result>;
/**
* Unsubscribes the authenticated user to the specified list.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/delete-users-id-followed-lists-list_id
*/
unsubscribeOfList(loggedUserId: string, listId: string): Promise<ListFollowV2Result>;
/**
* Enables the authenticated user to pin a List.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/post-users-id-pinned-lists
*/
pinList(loggedUserId: string, listId: string): Promise<ListPinV2Result>;
/**
* Enables the authenticated user to unpin a List.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/delete-users-id-pinned-lists-list_id
*/
unpinList(loggedUserId: string, listId: string): Promise<ListPinV2Result>;
}

View File

@ -0,0 +1,296 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const globals_1 = require("../globals");
const client_v2_read_1 = __importDefault(require("./client.v2.read"));
const client_v2_labs_write_1 = __importDefault(require("../v2-labs/client.v2.labs.write"));
/**
* Base Twitter v2 client with read/write rights.
*/
class TwitterApiv2ReadWrite extends client_v2_read_1.default {
constructor() {
super(...arguments);
this._prefix = globals_1.API_V2_PREFIX;
}
/* Sub-clients */
/**
* Get a client with only read rights.
*/
get readOnly() {
return this;
}
/**
* Get a client for v2 labs endpoints.
*/
get labs() {
if (this._labs)
return this._labs;
return this._labs = new client_v2_labs_write_1.default(this);
}
/* Tweets */
/**
* Hides or unhides a reply to a Tweet.
* https://developer.twitter.com/en/docs/twitter-api/tweets/hide-replies/api-reference/put-tweets-id-hidden
*/
hideReply(tweetId, makeHidden) {
return this.put('tweets/:id/hidden', { hidden: makeHidden }, { params: { id: tweetId } });
}
/**
* Causes the user ID identified in the path parameter to Like the target Tweet.
* https://developer.twitter.com/en/docs/twitter-api/tweets/likes/api-reference/post-users-user_id-likes
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
like(loggedUserId, targetTweetId) {
return this.post('users/:id/likes', { tweet_id: targetTweetId }, { params: { id: loggedUserId } });
}
/**
* Allows a user or authenticated user ID to unlike a Tweet.
* The request succeeds with no action when the user sends a request to a user they're not liking the Tweet or have already unliked the Tweet.
* https://developer.twitter.com/en/docs/twitter-api/tweets/likes/api-reference/delete-users-id-likes-tweet_id
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
unlike(loggedUserId, targetTweetId) {
return this.delete('users/:id/likes/:tweet_id', undefined, {
params: { id: loggedUserId, tweet_id: targetTweetId },
});
}
/**
* Causes the user ID identified in the path parameter to Retweet the target Tweet.
* https://developer.twitter.com/en/docs/twitter-api/tweets/retweets/api-reference/post-users-id-retweets
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
retweet(loggedUserId, targetTweetId) {
return this.post('users/:id/retweets', { tweet_id: targetTweetId }, { params: { id: loggedUserId } });
}
/**
* Allows a user or authenticated user ID to remove the Retweet of a Tweet.
* The request succeeds with no action when the user sends a request to a user they're not Retweeting the Tweet or have already removed the Retweet of.
* https://developer.twitter.com/en/docs/twitter-api/tweets/retweets/api-reference/delete-users-id-retweets-tweet_id
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
unretweet(loggedUserId, targetTweetId) {
return this.delete('users/:id/retweets/:tweet_id', undefined, {
params: { id: loggedUserId, tweet_id: targetTweetId },
});
}
tweet(status, payload = {}) {
if (typeof status === 'object') {
payload = status;
}
else {
payload = { text: status, ...payload };
}
return this.post('tweets', payload);
}
/**
* Reply to a Tweet on behalf of an authenticated user.
* https://developer.twitter.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/post-tweets
*/
reply(status, toTweetId, payload = {}) {
var _a;
const reply = { in_reply_to_tweet_id: toTweetId, ...(_a = payload.reply) !== null && _a !== void 0 ? _a : {} };
return this.post('tweets', { text: status, ...payload, reply });
}
/**
* Quote an existing Tweet on behalf of an authenticated user.
* https://developer.twitter.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/post-tweets
*/
quote(status, quotedTweetId, payload = {}) {
return this.tweet(status, { ...payload, quote_tweet_id: quotedTweetId });
}
/**
* Post a series of tweets.
* https://developer.twitter.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/post-tweets
*/
async tweetThread(tweets) {
var _a, _b;
const postedTweets = [];
for (const tweet of tweets) {
// Retrieve the last sent tweet
const lastTweet = postedTweets.length ? postedTweets[postedTweets.length - 1] : null;
// Build the tweet query params
const queryParams = { ...(typeof tweet === 'string' ? ({ text: tweet }) : tweet) };
// Reply to an existing tweet if needed
const inReplyToId = lastTweet ? lastTweet.data.id : (_a = queryParams.reply) === null || _a === void 0 ? void 0 : _a.in_reply_to_tweet_id;
const status = (_b = queryParams.text) !== null && _b !== void 0 ? _b : '';
if (inReplyToId) {
postedTweets.push(await this.reply(status, inReplyToId, queryParams));
}
else {
postedTweets.push(await this.tweet(status, queryParams));
}
}
return postedTweets;
}
/**
* Allows a user or authenticated user ID to delete a Tweet
* https://developer.twitter.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/delete-tweets-id
*/
deleteTweet(tweetId) {
return this.delete('tweets/:id', undefined, {
params: {
id: tweetId,
},
});
}
/* Bookmarks */
/**
* Causes the user ID of an authenticated user identified in the path parameter to Bookmark the target Tweet provided in the request body.
* https://developer.twitter.com/en/docs/twitter-api/tweets/bookmarks/api-reference/post-users-id-bookmarks
*
* OAuth2 scopes: `users.read` `tweet.read` `bookmark.write`
*/
async bookmark(tweetId) {
const user = await this.getCurrentUserV2Object();
return this.post('users/:id/bookmarks', { tweet_id: tweetId }, { params: { id: user.data.id } });
}
/**
* Allows a user or authenticated user ID to remove a Bookmark of a Tweet.
* https://developer.twitter.com/en/docs/twitter-api/tweets/bookmarks/api-reference/delete-users-id-bookmarks-tweet_id
*
* OAuth2 scopes: `users.read` `tweet.read` `bookmark.write`
*/
async deleteBookmark(tweetId) {
const user = await this.getCurrentUserV2Object();
return this.delete('users/:id/bookmarks/:tweet_id', undefined, { params: { id: user.data.id, tweet_id: tweetId } });
}
/* Users */
/**
* Allows a user ID to follow another user.
* If the target user does not have public Tweets, this endpoint will send a follow request.
* https://developer.twitter.com/en/docs/twitter-api/users/follows/api-reference/post-users-source_user_id-following
*
* OAuth2 scope: `follows.write`
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
follow(loggedUserId, targetUserId) {
return this.post('users/:id/following', { target_user_id: targetUserId }, { params: { id: loggedUserId } });
}
/**
* Allows a user ID to unfollow another user.
* https://developer.twitter.com/en/docs/twitter-api/users/follows/api-reference/delete-users-source_id-following
*
* OAuth2 scope: `follows.write`
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
unfollow(loggedUserId, targetUserId) {
return this.delete('users/:source_user_id/following/:target_user_id', undefined, {
params: { source_user_id: loggedUserId, target_user_id: targetUserId },
});
}
/**
* Causes the user (in the path) to block the target user.
* The user (in the path) must match the user context authorizing the request.
* https://developer.twitter.com/en/docs/twitter-api/users/blocks/api-reference/post-users-user_id-blocking
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
block(loggedUserId, targetUserId) {
return this.post('users/:id/blocking', { target_user_id: targetUserId }, { params: { id: loggedUserId } });
}
/**
* Allows a user or authenticated user ID to unblock another user.
* https://developer.twitter.com/en/docs/twitter-api/users/blocks/api-reference/delete-users-user_id-blocking
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
unblock(loggedUserId, targetUserId) {
return this.delete('users/:source_user_id/blocking/:target_user_id', undefined, {
params: { source_user_id: loggedUserId, target_user_id: targetUserId },
});
}
/**
* Allows an authenticated user ID to mute the target user.
* https://developer.twitter.com/en/docs/twitter-api/users/mutes/api-reference/post-users-user_id-muting
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
mute(loggedUserId, targetUserId) {
return this.post('users/:id/muting', { target_user_id: targetUserId }, { params: { id: loggedUserId } });
}
/**
* Allows an authenticated user ID to unmute the target user.
* The request succeeds with no action when the user sends a request to a user they're not muting or have already unmuted.
* https://developer.twitter.com/en/docs/twitter-api/users/mutes/api-reference/delete-users-user_id-muting
*
* **Note**: You must specify the currently logged user ID ; you can obtain it through v1.1 API.
*/
unmute(loggedUserId, targetUserId) {
return this.delete('users/:source_user_id/muting/:target_user_id', undefined, {
params: { source_user_id: loggedUserId, target_user_id: targetUserId },
});
}
/* Lists */
/**
* Creates a new list for the authenticated user.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/post-lists
*/
createList(options) {
return this.post('lists', options);
}
/**
* Updates the specified list. The authenticated user must own the list to be able to update it.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/put-lists-id
*/
updateList(listId, options = {}) {
return this.put('lists/:id', options, { params: { id: listId } });
}
/**
* Deletes the specified list. The authenticated user must own the list to be able to destroy it.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/delete-lists-id
*/
removeList(listId) {
return this.delete('lists/:id', undefined, { params: { id: listId } });
}
/**
* Adds a member to a list.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/post-lists-id-members
*/
addListMember(listId, userId) {
return this.post('lists/:id/members', { user_id: userId }, { params: { id: listId } });
}
/**
* Remember a member to a list.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/delete-lists-id-members-user_id
*/
removeListMember(listId, userId) {
return this.delete('lists/:id/members/:user_id', undefined, { params: { id: listId, user_id: userId } });
}
/**
* Subscribes the authenticated user to the specified list.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/post-users-id-followed-lists
*/
subscribeToList(loggedUserId, listId) {
return this.post('users/:id/followed_lists', { list_id: listId }, { params: { id: loggedUserId } });
}
/**
* Unsubscribes the authenticated user to the specified list.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/delete-users-id-followed-lists-list_id
*/
unsubscribeOfList(loggedUserId, listId) {
return this.delete('users/:id/followed_lists/:list_id', undefined, { params: { id: loggedUserId, list_id: listId } });
}
/**
* Enables the authenticated user to pin a List.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/post-users-id-pinned-lists
*/
pinList(loggedUserId, listId) {
return this.post('users/:id/pinned_lists', { list_id: listId }, { params: { id: loggedUserId } });
}
/**
* Enables the authenticated user to unpin a List.
* https://developer.twitter.com/en/docs/twitter-api/lists/manage-lists/api-reference/delete-users-id-pinned-lists-list_id
*/
unpinList(loggedUserId, listId) {
return this.delete('users/:id/pinned_lists/:list_id', undefined, { params: { id: loggedUserId, list_id: listId } });
}
}
exports.default = TwitterApiv2ReadWrite;

View File

@ -0,0 +1,82 @@
import type { ApiV2Includes, ListV2, SpaceV2, TweetV2, UserV2 } from '../types';
export declare type TTwitterV2IncludesResult = {
includes?: ApiV2Includes;
};
/**
* Provide helpers for `.includes` of a v2 API result.
* Needed expansions for a method to work are specified (*`like this`*).
*/
export declare class TwitterV2IncludesHelper implements ApiV2Includes {
protected result: TTwitterV2IncludesResult;
constructor(result: TTwitterV2IncludesResult);
get tweets(): TweetV2[];
static tweets(result: TTwitterV2IncludesResult): TweetV2[];
tweetById(id: string): TweetV2 | undefined;
static tweetById(result: TTwitterV2IncludesResult, id: string): TweetV2 | undefined;
/** Retweet associated with the given tweet (*`referenced_tweets.id`*) */
retweet(tweet: TweetV2): TweetV2 | undefined;
/** Retweet associated with the given tweet (*`referenced_tweets.id`*) */
static retweet(result: TTwitterV2IncludesResult, tweet: TweetV2): TweetV2 | undefined;
/** Quoted tweet associated with the given tweet (*`referenced_tweets.id`*) */
quote(tweet: TweetV2): TweetV2 | undefined;
/** Quoted tweet associated with the given tweet (*`referenced_tweets.id`*) */
static quote(result: TTwitterV2IncludesResult, tweet: TweetV2): TweetV2 | undefined;
/** Tweet whose has been answered by the given tweet (*`referenced_tweets.id`*) */
repliedTo(tweet: TweetV2): TweetV2 | undefined;
/** Tweet whose has been answered by the given tweet (*`referenced_tweets.id`*) */
static repliedTo(result: TTwitterV2IncludesResult, tweet: TweetV2): TweetV2 | undefined;
/** Tweet author user object of the given tweet (*`author_id`* or *`referenced_tweets.id.author_id`*) */
author(tweet: TweetV2): UserV2 | undefined;
/** Tweet author user object of the given tweet (*`author_id`* or *`referenced_tweets.id.author_id`*) */
static author(result: TTwitterV2IncludesResult, tweet: TweetV2): UserV2 | undefined;
/** Tweet author user object of the tweet answered by the given tweet (*`in_reply_to_user_id`*) */
repliedToAuthor(tweet: TweetV2): UserV2 | undefined;
/** Tweet author user object of the tweet answered by the given tweet (*`in_reply_to_user_id`*) */
static repliedToAuthor(result: TTwitterV2IncludesResult, tweet: TweetV2): UserV2 | undefined;
get users(): UserV2[];
static users(result: TTwitterV2IncludesResult): UserV2[];
userById(id: string): UserV2 | undefined;
static userById(result: TTwitterV2IncludesResult, id: string): UserV2 | undefined;
/** Pinned tweet of the given user (*`pinned_tweet_id`*) */
pinnedTweet(user: UserV2): TweetV2 | undefined;
/** Pinned tweet of the given user (*`pinned_tweet_id`*) */
static pinnedTweet(result: TTwitterV2IncludesResult, user: UserV2): TweetV2 | undefined;
get media(): import("../types").MediaObjectV2[];
static media(result: TTwitterV2IncludesResult): import("../types").MediaObjectV2[];
/** Medias associated with the given tweet (*`attachments.media_keys`*) */
medias(tweet: TweetV2): import("../types").MediaObjectV2[];
/** Medias associated with the given tweet (*`attachments.media_keys`*) */
static medias(result: TTwitterV2IncludesResult, tweet: TweetV2): import("../types").MediaObjectV2[];
get polls(): import("../types").PollV2[];
static polls(result: TTwitterV2IncludesResult): import("../types").PollV2[];
/** Poll associated with the given tweet (*`attachments.poll_ids`*) */
poll(tweet: TweetV2): import("../types").PollV2 | undefined;
/** Poll associated with the given tweet (*`attachments.poll_ids`*) */
static poll(result: TTwitterV2IncludesResult, tweet: TweetV2): import("../types").PollV2 | undefined;
get places(): import("../types").PlaceV2[];
static places(result: TTwitterV2IncludesResult): import("../types").PlaceV2[];
/** Place associated with the given tweet (*`geo.place_id`*) */
place(tweet: TweetV2): import("../types").PlaceV2 | undefined;
/** Place associated with the given tweet (*`geo.place_id`*) */
static place(result: TTwitterV2IncludesResult, tweet: TweetV2): import("../types").PlaceV2 | undefined;
/** List owner of the given list (*`owner_id`*) */
listOwner(list: ListV2): UserV2 | undefined;
/** List owner of the given list (*`owner_id`*) */
static listOwner(result: TTwitterV2IncludesResult, list: ListV2): UserV2 | undefined;
/** Creator of the given space (*`creator_id`*) */
spaceCreator(space: SpaceV2): UserV2 | undefined;
/** Creator of the given space (*`creator_id`*) */
static spaceCreator(result: TTwitterV2IncludesResult, space: SpaceV2): UserV2 | undefined;
/** Current hosts of the given space (*`host_ids`*) */
spaceHosts(space: SpaceV2): UserV2[];
/** Current hosts of the given space (*`host_ids`*) */
static spaceHosts(result: TTwitterV2IncludesResult, space: SpaceV2): UserV2[];
/** Current speakers of the given space (*`speaker_ids`*) */
spaceSpeakers(space: SpaceV2): UserV2[];
/** Current speakers of the given space (*`speaker_ids`*) */
static spaceSpeakers(result: TTwitterV2IncludesResult, space: SpaceV2): UserV2[];
/** Current invited users of the given space (*`invited_user_ids`*) */
spaceInvitedUsers(space: SpaceV2): UserV2[];
/** Current invited users of the given space (*`invited_user_ids`*) */
static spaceInvitedUsers(result: TTwitterV2IncludesResult, space: SpaceV2): UserV2[];
}

View File

@ -0,0 +1,211 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TwitterV2IncludesHelper = void 0;
/**
* Provide helpers for `.includes` of a v2 API result.
* Needed expansions for a method to work are specified (*`like this`*).
*/
class TwitterV2IncludesHelper {
constructor(result) {
this.result = result;
}
/* Tweets */
get tweets() {
return TwitterV2IncludesHelper.tweets(this.result);
}
static tweets(result) {
var _a, _b;
return (_b = (_a = result.includes) === null || _a === void 0 ? void 0 : _a.tweets) !== null && _b !== void 0 ? _b : [];
}
tweetById(id) {
return TwitterV2IncludesHelper.tweetById(this.result, id);
}
static tweetById(result, id) {
return this.tweets(result).find(tweet => tweet.id === id);
}
/** Retweet associated with the given tweet (*`referenced_tweets.id`*) */
retweet(tweet) {
return TwitterV2IncludesHelper.retweet(this.result, tweet);
}
/** Retweet associated with the given tweet (*`referenced_tweets.id`*) */
static retweet(result, tweet) {
var _a;
const retweetIds = ((_a = tweet.referenced_tweets) !== null && _a !== void 0 ? _a : [])
.filter(ref => ref.type === 'retweeted')
.map(ref => ref.id);
return this.tweets(result).find(t => retweetIds.includes(t.id));
}
/** Quoted tweet associated with the given tweet (*`referenced_tweets.id`*) */
quote(tweet) {
return TwitterV2IncludesHelper.quote(this.result, tweet);
}
/** Quoted tweet associated with the given tweet (*`referenced_tweets.id`*) */
static quote(result, tweet) {
var _a;
const quoteIds = ((_a = tweet.referenced_tweets) !== null && _a !== void 0 ? _a : [])
.filter(ref => ref.type === 'quoted')
.map(ref => ref.id);
return this.tweets(result).find(t => quoteIds.includes(t.id));
}
/** Tweet whose has been answered by the given tweet (*`referenced_tweets.id`*) */
repliedTo(tweet) {
return TwitterV2IncludesHelper.repliedTo(this.result, tweet);
}
/** Tweet whose has been answered by the given tweet (*`referenced_tweets.id`*) */
static repliedTo(result, tweet) {
var _a;
const repliesIds = ((_a = tweet.referenced_tweets) !== null && _a !== void 0 ? _a : [])
.filter(ref => ref.type === 'replied_to')
.map(ref => ref.id);
return this.tweets(result).find(t => repliesIds.includes(t.id));
}
/** Tweet author user object of the given tweet (*`author_id`* or *`referenced_tweets.id.author_id`*) */
author(tweet) {
return TwitterV2IncludesHelper.author(this.result, tweet);
}
/** Tweet author user object of the given tweet (*`author_id`* or *`referenced_tweets.id.author_id`*) */
static author(result, tweet) {
const authorId = tweet.author_id;
return authorId ? this.users(result).find(u => u.id === authorId) : undefined;
}
/** Tweet author user object of the tweet answered by the given tweet (*`in_reply_to_user_id`*) */
repliedToAuthor(tweet) {
return TwitterV2IncludesHelper.repliedToAuthor(this.result, tweet);
}
/** Tweet author user object of the tweet answered by the given tweet (*`in_reply_to_user_id`*) */
static repliedToAuthor(result, tweet) {
const inReplyUserId = tweet.in_reply_to_user_id;
return inReplyUserId ? this.users(result).find(u => u.id === inReplyUserId) : undefined;
}
/* Users */
get users() {
return TwitterV2IncludesHelper.users(this.result);
}
static users(result) {
var _a, _b;
return (_b = (_a = result.includes) === null || _a === void 0 ? void 0 : _a.users) !== null && _b !== void 0 ? _b : [];
}
userById(id) {
return TwitterV2IncludesHelper.userById(this.result, id);
}
static userById(result, id) {
return this.users(result).find(u => u.id === id);
}
/** Pinned tweet of the given user (*`pinned_tweet_id`*) */
pinnedTweet(user) {
return TwitterV2IncludesHelper.pinnedTweet(this.result, user);
}
/** Pinned tweet of the given user (*`pinned_tweet_id`*) */
static pinnedTweet(result, user) {
return user.pinned_tweet_id ? this.tweets(result).find(t => t.id === user.pinned_tweet_id) : undefined;
}
/* Medias */
get media() {
return TwitterV2IncludesHelper.media(this.result);
}
static media(result) {
var _a, _b;
return (_b = (_a = result.includes) === null || _a === void 0 ? void 0 : _a.media) !== null && _b !== void 0 ? _b : [];
}
/** Medias associated with the given tweet (*`attachments.media_keys`*) */
medias(tweet) {
return TwitterV2IncludesHelper.medias(this.result, tweet);
}
/** Medias associated with the given tweet (*`attachments.media_keys`*) */
static medias(result, tweet) {
var _a, _b;
const keys = (_b = (_a = tweet.attachments) === null || _a === void 0 ? void 0 : _a.media_keys) !== null && _b !== void 0 ? _b : [];
return this.media(result).filter(m => keys.includes(m.media_key));
}
/* Polls */
get polls() {
return TwitterV2IncludesHelper.polls(this.result);
}
static polls(result) {
var _a, _b;
return (_b = (_a = result.includes) === null || _a === void 0 ? void 0 : _a.polls) !== null && _b !== void 0 ? _b : [];
}
/** Poll associated with the given tweet (*`attachments.poll_ids`*) */
poll(tweet) {
return TwitterV2IncludesHelper.poll(this.result, tweet);
}
/** Poll associated with the given tweet (*`attachments.poll_ids`*) */
static poll(result, tweet) {
var _a, _b;
const pollIds = (_b = (_a = tweet.attachments) === null || _a === void 0 ? void 0 : _a.poll_ids) !== null && _b !== void 0 ? _b : [];
if (pollIds.length) {
const pollId = pollIds[0];
return this.polls(result).find(p => p.id === pollId);
}
return undefined;
}
/* Places */
get places() {
return TwitterV2IncludesHelper.places(this.result);
}
static places(result) {
var _a, _b;
return (_b = (_a = result.includes) === null || _a === void 0 ? void 0 : _a.places) !== null && _b !== void 0 ? _b : [];
}
/** Place associated with the given tweet (*`geo.place_id`*) */
place(tweet) {
return TwitterV2IncludesHelper.place(this.result, tweet);
}
/** Place associated with the given tweet (*`geo.place_id`*) */
static place(result, tweet) {
var _a;
const placeId = (_a = tweet.geo) === null || _a === void 0 ? void 0 : _a.place_id;
return placeId ? this.places(result).find(p => p.id === placeId) : undefined;
}
/* Lists */
/** List owner of the given list (*`owner_id`*) */
listOwner(list) {
return TwitterV2IncludesHelper.listOwner(this.result, list);
}
/** List owner of the given list (*`owner_id`*) */
static listOwner(result, list) {
const creatorId = list.owner_id;
return creatorId ? this.users(result).find(p => p.id === creatorId) : undefined;
}
/* Spaces */
/** Creator of the given space (*`creator_id`*) */
spaceCreator(space) {
return TwitterV2IncludesHelper.spaceCreator(this.result, space);
}
/** Creator of the given space (*`creator_id`*) */
static spaceCreator(result, space) {
const creatorId = space.creator_id;
return creatorId ? this.users(result).find(p => p.id === creatorId) : undefined;
}
/** Current hosts of the given space (*`host_ids`*) */
spaceHosts(space) {
return TwitterV2IncludesHelper.spaceHosts(this.result, space);
}
/** Current hosts of the given space (*`host_ids`*) */
static spaceHosts(result, space) {
var _a;
const hostIds = (_a = space.host_ids) !== null && _a !== void 0 ? _a : [];
return this.users(result).filter(u => hostIds.includes(u.id));
}
/** Current speakers of the given space (*`speaker_ids`*) */
spaceSpeakers(space) {
return TwitterV2IncludesHelper.spaceSpeakers(this.result, space);
}
/** Current speakers of the given space (*`speaker_ids`*) */
static spaceSpeakers(result, space) {
var _a;
const speakerIds = (_a = space.speaker_ids) !== null && _a !== void 0 ? _a : [];
return this.users(result).filter(u => speakerIds.includes(u.id));
}
/** Current invited users of the given space (*`invited_user_ids`*) */
spaceInvitedUsers(space) {
return TwitterV2IncludesHelper.spaceInvitedUsers(this.result, space);
}
/** Current invited users of the given space (*`invited_user_ids`*) */
static spaceInvitedUsers(result, space) {
var _a;
const invitedUserIds = (_a = space.invited_user_ids) !== null && _a !== void 0 ? _a : [];
return this.users(result).filter(u => invitedUserIds.includes(u.id));
}
}
exports.TwitterV2IncludesHelper = TwitterV2IncludesHelper;