From 8a9aff07b8551530e46c3c8d30f1ebcdd2ec5976 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Wed, 27 Dec 2023 16:59:08 +0530 Subject: [PATCH 01/39] Fixed a link in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0bfee008..a1fa394a 100644 --- a/README.md +++ b/README.md @@ -179,7 +179,7 @@ rettiwt.tweet.search({ }); ``` -For more information regarding the different available filter options, please refer to [TweetFilter](https://rishikant181.github.io/Rettiwt-Core/classes/TweetFilter.html). +For more information regarding the different available filter options, please refer to [TweetFilter](https://rishikant181.github.io/Rettiwt-API/classes/TweetFilter.html). ## Using a proxy From 8c51e7b2b0e7ac79d88dca64a63820e9283069e7 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Thu, 28 Dec 2023 12:48:31 +0530 Subject: [PATCH 02/39] Update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 491c6e50..3794046a 100644 --- a/.gitignore +++ b/.gitignore @@ -133,7 +133,7 @@ dist .pnp.* # Test files -.test/ +.test # Documentation .docs From d589804f9eb4e0cd4b7d6e8f59cdd5cde49cdb5f Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Thu, 28 Dec 2023 12:49:47 +0530 Subject: [PATCH 03/39] Updated gitignore --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 3794046a..75d240ba 100644 --- a/.gitignore +++ b/.gitignore @@ -133,7 +133,7 @@ dist .pnp.* # Test files -.test +test # Documentation -.docs +docs From 1fcd4b3213f7561449eb5fca901ecf88c59cc5a3 Mon Sep 17 00:00:00 2001 From: omarcinkonis Date: Wed, 10 Jan 2024 09:08:54 +0200 Subject: [PATCH 04/39] Add ability to define custom error handling --- src/enums/HTTP.ts | 51 ++++++++ src/errors/ApiError.ts | 14 +++ src/errors/HttpError.ts | 14 +++ src/errors/RettiwtError.ts | 9 ++ src/models/internal/RettiwtConfig.ts | 3 + src/services/internal/FetcherService.ts | 66 +++------- src/services/public/ErrorHandleService.ts | 146 ++++++++++++++++++++++ src/types/internal/RettiwtConfig.ts | 6 + src/types/public/ErrorHandleService.ts | 13 ++ 9 files changed, 272 insertions(+), 50 deletions(-) create mode 100644 src/errors/ApiError.ts create mode 100644 src/errors/HttpError.ts create mode 100644 src/errors/RettiwtError.ts create mode 100644 src/services/public/ErrorHandleService.ts create mode 100644 src/types/public/ErrorHandleService.ts diff --git a/src/enums/HTTP.ts b/src/enums/HTTP.ts index 6f576803..29825f58 100644 --- a/src/enums/HTTP.ts +++ b/src/enums/HTTP.ts @@ -4,14 +4,65 @@ * @internal */ export enum EHttpStatus { + CONTINUE = 100, + SWITCHING_PROTOCOLS = 101, + PROCESSING = 102, + OK = 200, + CREATED = 201, + ACCEPTED = 202, + NON_AUTHORITATIVE_INFORMATION = 203, + NO_CONTENT = 204, + RESET_CONTENT = 205, + PARTIAL_CONTENT = 206, + MULTI_STATUS = 207, + ALREADY_REPORTED = 208, + IM_USED = 226, + MULTIPLE_CHOICES = 300, + MOVED_PERMANENTLY = 301, + FOUND = 302, + SEE_OTHER = 303, + NOT_MODIFIED = 304, + USE_PROXY = 305, + SWITCH_PROXY = 306, + TEMPORARY_REDIRECT = 307, + PERMANENT_REDIRECT = 308, BAD_REQUEST = 400, UNAUTHORIZED = 401, + PAYMENT_REQUIRED = 402, FORBIDDEN = 403, NOT_FOUND = 404, METHOD_NOT_ALLOWED = 405, + NOT_ACCEPTABLE = 406, + PROXY_AUTHENTICATION_REQUIRED = 407, REQUEST_TIMEOUT = 408, + CONFLICT = 409, + GONE = 410, + LENGTH_REQUIRED = 411, + PRECONDITION_FAILED = 412, + PAYLOAD_TOO_LARGE = 413, + URI_TOO_LONG = 414, + UNSUPPORTED_MEDIA_TYPE = 415, + RANGE_NOT_SATISFIABLE = 416, + EXPECTATION_FAILED = 417, + I_AM_A_TEAPOT = 418, + MISDIRECTED_REQUEST = 421, + UNPROCESSABLE_ENTITY = 422, + LOCKED = 423, + FAILED_DEPENDENCY = 424, + UPGRADE_REQUIRED = 426, + PRECONDITION_REQUIRED = 428, TOO_MANY_REQUESTS = 429, + REQUEST_HEADER_FIELDS_TOO_LARGE = 431, + UNAVAILABLE_FOR_LEGAL_REASONS = 451, INTERNAL_SERVER_ERROR = 500, + NOT_IMPLEMENTED = 501, BAD_GATEWAY = 502, SERVICE_UNAVAILABLE = 503, + GATEWAY_TIMEOUT = 504, + HTTP_VERSION_NOT_SUPPORTED = 505, + VARIANT_ALSO_NEGOTIATES = 506, + INSUFFICIENT_STORAGE = 507, + LOOP_DETECTED = 508, + NOT_EXTENDED = 510, + NETWORK_AUTHENTICATION_REQUIRED = 511, } diff --git a/src/errors/ApiError.ts b/src/errors/ApiError.ts new file mode 100644 index 00000000..02d73526 --- /dev/null +++ b/src/errors/ApiError.ts @@ -0,0 +1,14 @@ +// ERRORS +import RettiwtError from "./RettiwtError"; + +class ApiError extends RettiwtError { + errorCode: number; + + constructor(errorCode: number, message?: string) { + super(message); + + this.errorCode = errorCode; + } +} + +export default ApiError; diff --git a/src/errors/HttpError.ts b/src/errors/HttpError.ts new file mode 100644 index 00000000..42fa5c0c --- /dev/null +++ b/src/errors/HttpError.ts @@ -0,0 +1,14 @@ +// ERRORS +import RettiwtError from "./RettiwtError"; + +class HttpError extends RettiwtError { + httpStatus: number; + + constructor(httpStatus: number, message?: string) { + super(message); + + this.httpStatus = httpStatus; + } +} + +export default HttpError; diff --git a/src/errors/RettiwtError.ts b/src/errors/RettiwtError.ts new file mode 100644 index 00000000..411ca90c --- /dev/null +++ b/src/errors/RettiwtError.ts @@ -0,0 +1,9 @@ +class RettiwtError extends Error { + constructor(message?: string) { + super(message); + + Object.setPrototypeOf(this, RettiwtError.prototype); + } +} + +export default RettiwtError; diff --git a/src/models/internal/RettiwtConfig.ts b/src/models/internal/RettiwtConfig.ts index 0e5e142e..fc59925a 100644 --- a/src/models/internal/RettiwtConfig.ts +++ b/src/models/internal/RettiwtConfig.ts @@ -1,4 +1,5 @@ // TYPES +import { IErrorHandleService } from "../../types/public/ErrorHandleService"; import { IRettiwtConfig } from '../../types/internal/RettiwtConfig'; /** @@ -11,6 +12,7 @@ export class RettiwtConfig implements IRettiwtConfig { public guestKey?: string; public proxyUrl?: URL; public logging?: boolean; + public errorHandleService?: IErrorHandleService; /** * Initializes a new configuration object from the given config. @@ -22,5 +24,6 @@ export class RettiwtConfig implements IRettiwtConfig { this.guestKey = config.guestKey; this.proxyUrl = config.proxyUrl; this.logging = config.logging; + this.errorHandleService = config.errorHandleService; } } diff --git a/src/services/internal/FetcherService.ts b/src/services/internal/FetcherService.ts index e6b29838..87a99048 100644 --- a/src/services/internal/FetcherService.ts +++ b/src/services/internal/FetcherService.ts @@ -9,7 +9,6 @@ import { ITimelineTweet, ITimelineUser, IResponse, - EErrorCodes, } from 'rettiwt-core'; import axios, { AxiosRequestConfig, AxiosRequestHeaders, AxiosResponse } from 'axios'; import https, { Agent } from 'https'; @@ -17,10 +16,13 @@ import { AuthCredential, Auth } from 'rettiwt-auth'; import { HttpsProxyAgent } from 'https-proxy-agent'; // SERVICES +import { ErrorHandleService } from "../public/ErrorHandleService"; import { LogService } from './LogService'; +// TYPES +import { IErrorHandleService } from "../../types/public/ErrorHandleService"; + // ENUMS -import { EHttpStatus } from '../../enums/HTTP'; import { EApiErrors } from '../../enums/ApiErrors'; import { ELogActions } from '../../enums/Logging'; @@ -31,7 +33,7 @@ import { Tweet } from '../../models/public/Tweet'; import { User } from '../../models/public/User'; // HELPERS -import { findByFilter, findKeyByValue } from '../../helper/JsonUtils'; +import { findByFilter } from '../../helper/JsonUtils'; /** * The base service that handles all HTTP requests. @@ -51,6 +53,9 @@ export class FetcherService { /** The log service instance to use to logging. */ private readonly logger: LogService; + /** The service used to handle HTTP and API errors */ + private readonly errorHandleService: IErrorHandleService; + /** * @param config - The config object for configuring the Rettiwt instance. */ @@ -70,6 +75,7 @@ export class FetcherService { this.isAuthenticated = config?.apiKey ? true : false; this.httpsAgent = this.getHttpsAgent(config?.proxyUrl); this.logger = new LogService(config?.logging); + this.errorHandleService = config?.errorHandleService ?? new ErrorHandleService(); } /** @@ -133,49 +139,6 @@ export class FetcherService { return new https.Agent(); } - /** - * The middleware for handling any http error. - * - * @param res - The response object received. - * @returns The received response, if no HTTP errors are found. - * @throws An error if any HTTP-related error has occured. - */ - private handleHttpError(res: AxiosResponse>): AxiosResponse> { - /** - * If the status code is not 200 =\> the HTTP request was not successful. hence throwing error - */ - if (res.status != 200 && res.status in EHttpStatus) { - throw new Error(EHttpStatus[res.status]); - } - - return res; - } - - /** - * The middleware for handling any Twitter API-level errors. - * - * @param res - The response object received. - * @returns The received response, if no API errors are found. - * @throws An error if any API-related error has occured. - */ - private handleApiError(res: AxiosResponse>): AxiosResponse> { - // If error exists - if (res.data.errors && res.data.errors.length) { - // Getting the error code - const code: number = res.data.errors[0].code; - - // Getting the error message - const message: string = EApiErrors[ - findKeyByValue(EErrorCodes, `${code}`) as keyof typeof EApiErrors - ] as string; - - // Throw the error - throw new Error(message); - } - - return res; - } - /** * Makes an HTTP request according to the given parameters. * @@ -190,7 +153,7 @@ export class FetcherService { this.cred = this.cred ?? (await new Auth().getGuestCredential()); /** - * Creating axios request configuration from the input configuration. + * Creating Axios request configuration from the input configuration. */ const axiosRequest: AxiosRequestConfig = { url: config.url, @@ -201,11 +164,14 @@ export class FetcherService { }; /** - * After making the request, the response is then passed to HTTP error handling middleware for HTTP error handling. + * If Axios request results in an error, catch it and rethrow a more specific error. */ return await axios>(axiosRequest) - .then((res) => this.handleHttpError(res)) - .then((res) => this.handleApiError(res)); + .catch((error: unknown) => { + this.errorHandleService.handle(error); + + throw error; + }); } /** diff --git a/src/services/public/ErrorHandleService.ts b/src/services/public/ErrorHandleService.ts new file mode 100644 index 00000000..cbc5ed26 --- /dev/null +++ b/src/services/public/ErrorHandleService.ts @@ -0,0 +1,146 @@ +// PACKAGES +import axios, { AxiosResponse} from "axios"; +import { findKeyByValue } from "../../helper/JsonUtils"; + +// TYPES +import { IErrorHandleService } from "../../types/public/ErrorHandleService"; + +// ENUMS +import { EApiErrors } from "../../enums/ApiErrors"; +import { EErrorCodes } from "rettiwt-core"; +import { EHttpStatus } from "../../enums/HTTP"; + +// ERRORS +import ApiError from "../../errors/ApiError"; +import HttpError from "../../errors/HttpError"; + +/** + * Defines error conditions and processes API/HTTP errors in Axios responses. + * + * @public + */ +export class ErrorHandleService implements IErrorHandleService { + /** + * Error message used when the specific error type is not defined in the required enums. + */ + protected static readonly DEFAULT_ERROR_MESSAGE = 'Unknown error'; + + /** + * The method called when an error response is received from Twitter API. + * + * @param {unknown} error - The error caught while making Axios request to Twitter API. + */ + handle(error: unknown): void { + const axiosResponse = this.getAxiosResponse(error); + + this.handleApiError(axiosResponse); + this.handleHttpError(axiosResponse); + } + + /** + * Retrieves the Axios response from the given error. + * + * @param {unknown} error - The error object. + * @returns {AxiosResponse} The Axios response. + * @throws {unknown} Throws the original error if it is not an Axios error with a response. + * @protected + */ + protected getAxiosResponse(error: unknown): AxiosResponse { + if (axios.isAxiosError(error) && !!error.response) { + return error.response; + } + + throw error; + } + + /** + * Handles HTTP error in an Axios response. + * + * @param {AxiosResponse} axiosResponse - The response object received. + * @throws {Error} An error with the corresponding HTTP status text if any HTTP-related error has occurred. + * @protected + */ + protected handleHttpError(axiosResponse: AxiosResponse): void { + throw this.createHttpError(axiosResponse.status); + } + + /** + * Handles API error in an Axios response. + * + * @param {AxiosResponse} axiosResponse - The response object received. + * @throws {Error} An error with the corresponding API error message if any API-related error has occurred. + * @protected + */ + protected handleApiError(axiosResponse: AxiosResponse): void { + const errorCode = this.getErrorCode(axiosResponse); + + if (errorCode === undefined) { + return; + } + + throw this.createApiError(errorCode); + } + + /** + * Creates an HTTP error instance based on the provided HTTP status. + * + * @param {number} httpStatus - The HTTP status code. + * @returns {HttpError} An HTTP error instance. + * @protected + */ + protected createHttpError(httpStatus: number): HttpError { + return new HttpError(httpStatus, this.getHttpErrorMessage(httpStatus)); + } + + /** + * Retrieves the HTTP error message based on the provided HTTP status. + * + * @param {number} httpStatus - The HTTP status code. + * @returns {string} The HTTP error message. + * @protected + */ + protected getHttpErrorMessage(httpStatus: number): string { + return Object.values(EHttpStatus).includes(httpStatus) + ? EHttpStatus[httpStatus] + : ErrorHandleService.DEFAULT_ERROR_MESSAGE; + } + + /** + * Retrieves the API error code from the Axios response data. + * + * @param {AxiosResponse} axiosResponse - The response object received. + * @returns {number | undefined} The error code, or undefined if not found. + * @protected + */ + protected getErrorCode(axiosResponse: AxiosResponse): number | undefined { + const errors = axiosResponse.data.errors; + + return (!!errors && errors.length) ? errors[0].code : undefined; + } + + /** + * Creates an API error instance based on the provided error code. + * + * @param {number} errorCode - The error code. + * @returns {ApiError} An API error instance. + * @protected + */ + protected createApiError(errorCode: number): ApiError { + return new ApiError(errorCode, this.getApiErrorMessage(errorCode)); + } + + /** + * Retrieves the API error message based on the provided error code. + * + * @param {number} errorCode - The error code. + * @returns {string} The API error message. + * @protected + */ + protected getApiErrorMessage(errorCode: number): string { + const errorCodeKey = findKeyByValue(EErrorCodes, errorCode.toString()); + + return (!!errorCodeKey && errorCodeKey in EApiErrors) + ? EApiErrors[errorCodeKey as keyof typeof EApiErrors] + : ErrorHandleService.DEFAULT_ERROR_MESSAGE; + } +} diff --git a/src/types/internal/RettiwtConfig.ts b/src/types/internal/RettiwtConfig.ts index 58d0364d..1c8bb532 100644 --- a/src/types/internal/RettiwtConfig.ts +++ b/src/types/internal/RettiwtConfig.ts @@ -1,3 +1,6 @@ +// TYPES +import { IErrorHandleService } from "../public/ErrorHandleService"; + /** * The configuration for initializing a new Rettiwt instance. * @@ -15,4 +18,7 @@ export interface IRettiwtConfig { /** Whether to write logs to console or not. */ logging?: boolean; + + /** Optional custom error handler to define error conditions and process API/HTTP errors in axios responses. */ + errorHandleService?: IErrorHandleService; } diff --git a/src/types/public/ErrorHandleService.ts b/src/types/public/ErrorHandleService.ts new file mode 100644 index 00000000..fee9c094 --- /dev/null +++ b/src/types/public/ErrorHandleService.ts @@ -0,0 +1,13 @@ +/** + * Defines error conditions and processes API/HTTP errors in axios responses. + * + * @public + */ +export interface IErrorHandleService { + /** + * The method called when an error response is received from Twitter API. + * + * @param {unknown} error - The error caught while making Axios request to Twitter API. + */ + handle(error: unknown): void; +} From 154f2e98a5d35868dc4d361b47ec37130994b871 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Wed, 10 Jan 2024 17:02:39 +0530 Subject: [PATCH 05/39] Fixed linting errors --- .eslintrc.js | 5 ++ src/errors/ApiError.ts | 12 ++-- src/errors/HttpError.ts | 12 ++-- src/errors/RettiwtError.ts | 8 +-- src/models/internal/RettiwtConfig.ts | 2 +- src/services/internal/FetcherService.ts | 13 ++--- src/services/public/ErrorHandleService.ts | 68 ++++++++++------------- src/types/internal/RettiwtConfig.ts | 2 +- src/types/public/ErrorHandleService.ts | 4 +- 9 files changed, 61 insertions(+), 65 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 6c649e93..5f3fca26 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -34,6 +34,11 @@ module.exports = { selector: ['variableLike', 'memberLike'], format: ['camelCase'], }, + { + selector: ['variableLike', 'memberLike'], + modifiers: ['static', 'readonly'], + format: ['UPPER_CASE'], + }, { selector: 'enumMember', format: ['UPPER_CASE'], diff --git a/src/errors/ApiError.ts b/src/errors/ApiError.ts index 02d73526..b0a1e33a 100644 --- a/src/errors/ApiError.ts +++ b/src/errors/ApiError.ts @@ -1,14 +1,14 @@ // ERRORS -import RettiwtError from "./RettiwtError"; +import RettiwtError from './RettiwtError'; class ApiError extends RettiwtError { - errorCode: number; + public errorCode: number; - constructor(errorCode: number, message?: string) { - super(message); + public constructor(errorCode: number, message?: string) { + super(message); - this.errorCode = errorCode; - } + this.errorCode = errorCode; + } } export default ApiError; diff --git a/src/errors/HttpError.ts b/src/errors/HttpError.ts index 42fa5c0c..75ab1b0c 100644 --- a/src/errors/HttpError.ts +++ b/src/errors/HttpError.ts @@ -1,14 +1,14 @@ // ERRORS -import RettiwtError from "./RettiwtError"; +import RettiwtError from './RettiwtError'; class HttpError extends RettiwtError { - httpStatus: number; + public httpStatus: number; - constructor(httpStatus: number, message?: string) { - super(message); + public constructor(httpStatus: number, message?: string) { + super(message); - this.httpStatus = httpStatus; - } + this.httpStatus = httpStatus; + } } export default HttpError; diff --git a/src/errors/RettiwtError.ts b/src/errors/RettiwtError.ts index 411ca90c..d4a1c7a8 100644 --- a/src/errors/RettiwtError.ts +++ b/src/errors/RettiwtError.ts @@ -1,9 +1,9 @@ class RettiwtError extends Error { - constructor(message?: string) { - super(message); + public constructor(message?: string) { + super(message); - Object.setPrototypeOf(this, RettiwtError.prototype); - } + Object.setPrototypeOf(this, RettiwtError.prototype); + } } export default RettiwtError; diff --git a/src/models/internal/RettiwtConfig.ts b/src/models/internal/RettiwtConfig.ts index fc59925a..f6dfbb9e 100644 --- a/src/models/internal/RettiwtConfig.ts +++ b/src/models/internal/RettiwtConfig.ts @@ -1,5 +1,5 @@ // TYPES -import { IErrorHandleService } from "../../types/public/ErrorHandleService"; +import { IErrorHandleService } from '../../types/public/ErrorHandleService'; import { IRettiwtConfig } from '../../types/internal/RettiwtConfig'; /** diff --git a/src/services/internal/FetcherService.ts b/src/services/internal/FetcherService.ts index 87a99048..99348d05 100644 --- a/src/services/internal/FetcherService.ts +++ b/src/services/internal/FetcherService.ts @@ -16,11 +16,11 @@ import { AuthCredential, Auth } from 'rettiwt-auth'; import { HttpsProxyAgent } from 'https-proxy-agent'; // SERVICES -import { ErrorHandleService } from "../public/ErrorHandleService"; +import { ErrorHandleService } from '../public/ErrorHandleService'; import { LogService } from './LogService'; // TYPES -import { IErrorHandleService } from "../../types/public/ErrorHandleService"; +import { IErrorHandleService } from '../../types/public/ErrorHandleService'; // ENUMS import { EApiErrors } from '../../enums/ApiErrors'; @@ -166,12 +166,11 @@ export class FetcherService { /** * If Axios request results in an error, catch it and rethrow a more specific error. */ - return await axios>(axiosRequest) - .catch((error: unknown) => { - this.errorHandleService.handle(error); + return await axios>(axiosRequest).catch((error: unknown) => { + this.errorHandleService.handle(error); - throw error; - }); + throw error; + }); } /** diff --git a/src/services/public/ErrorHandleService.ts b/src/services/public/ErrorHandleService.ts index cbc5ed26..f1f7fe8b 100644 --- a/src/services/public/ErrorHandleService.ts +++ b/src/services/public/ErrorHandleService.ts @@ -1,18 +1,18 @@ // PACKAGES -import axios, { AxiosResponse} from "axios"; -import { findKeyByValue } from "../../helper/JsonUtils"; +import axios, { AxiosResponse } from 'axios'; +import { findKeyByValue } from '../../helper/JsonUtils'; // TYPES -import { IErrorHandleService } from "../../types/public/ErrorHandleService"; +import { IErrorHandleService } from '../../types/public/ErrorHandleService'; // ENUMS -import { EApiErrors } from "../../enums/ApiErrors"; -import { EErrorCodes } from "rettiwt-core"; -import { EHttpStatus } from "../../enums/HTTP"; +import { EApiErrors } from '../../enums/ApiErrors'; +import { EErrorCodes } from 'rettiwt-core'; +import { EHttpStatus } from '../../enums/HTTP'; // ERRORS -import ApiError from "../../errors/ApiError"; -import HttpError from "../../errors/HttpError"; +import ApiError from '../../errors/ApiError'; +import HttpError from '../../errors/HttpError'; /** * Defines error conditions and processes API/HTTP errors in Axios responses. @@ -28,9 +28,9 @@ export class ErrorHandleService implements IErrorHandleService { /** * The method called when an error response is received from Twitter API. * - * @param {unknown} error - The error caught while making Axios request to Twitter API. + * @param error - The error caught while making Axios request to Twitter API. */ - handle(error: unknown): void { + public handle(error: unknown): void { const axiosResponse = this.getAxiosResponse(error); this.handleApiError(axiosResponse); @@ -40,10 +40,9 @@ export class ErrorHandleService implements IErrorHandleService { /** * Retrieves the Axios response from the given error. * - * @param {unknown} error - The error object. - * @returns {AxiosResponse} The Axios response. - * @throws {unknown} Throws the original error if it is not an Axios error with a response. - * @protected + * @param error - The error object. + * @returns The Axios response. + * @throws Throws the original error if it is not an Axios error with a response. */ protected getAxiosResponse(error: unknown): AxiosResponse { if (axios.isAxiosError(error) && !!error.response) { @@ -56,9 +55,8 @@ export class ErrorHandleService implements IErrorHandleService { /** * Handles HTTP error in an Axios response. * - * @param {AxiosResponse} axiosResponse - The response object received. - * @throws {Error} An error with the corresponding HTTP status text if any HTTP-related error has occurred. - * @protected + * @param axiosResponse - The response object received. + * @throws An error with the corresponding HTTP status text if any HTTP-related error has occurred. */ protected handleHttpError(axiosResponse: AxiosResponse): void { throw this.createHttpError(axiosResponse.status); @@ -67,9 +65,8 @@ export class ErrorHandleService implements IErrorHandleService { /** * Handles API error in an Axios response. * - * @param {AxiosResponse} axiosResponse - The response object received. - * @throws {Error} An error with the corresponding API error message if any API-related error has occurred. - * @protected + * @param axiosResponse - The response object received. + * @throws An error with the corresponding API error message if any API-related error has occurred. */ protected handleApiError(axiosResponse: AxiosResponse): void { const errorCode = this.getErrorCode(axiosResponse); @@ -84,9 +81,8 @@ export class ErrorHandleService implements IErrorHandleService { /** * Creates an HTTP error instance based on the provided HTTP status. * - * @param {number} httpStatus - The HTTP status code. - * @returns {HttpError} An HTTP error instance. - * @protected + * @param httpStatus - The HTTP status code. + * @returns An HTTP error instance. */ protected createHttpError(httpStatus: number): HttpError { return new HttpError(httpStatus, this.getHttpErrorMessage(httpStatus)); @@ -95,9 +91,8 @@ export class ErrorHandleService implements IErrorHandleService { /** * Retrieves the HTTP error message based on the provided HTTP status. * - * @param {number} httpStatus - The HTTP status code. - * @returns {string} The HTTP error message. - * @protected + * @param httpStatus - The HTTP status code. + * @returns The HTTP error message. */ protected getHttpErrorMessage(httpStatus: number): string { return Object.values(EHttpStatus).includes(httpStatus) @@ -108,22 +103,20 @@ export class ErrorHandleService implements IErrorHandleService { /** * Retrieves the API error code from the Axios response data. * - * @param {AxiosResponse} axiosResponse - The response object received. - * @returns {number | undefined} The error code, or undefined if not found. - * @protected + * @param axiosResponse - The response object received. + * @returns The error code, or undefined if not found. */ protected getErrorCode(axiosResponse: AxiosResponse): number | undefined { - const errors = axiosResponse.data.errors; + const errors = (axiosResponse.data as { errors: { code: number }[] }).errors; - return (!!errors && errors.length) ? errors[0].code : undefined; + return !!errors && errors.length ? errors[0].code : undefined; } /** * Creates an API error instance based on the provided error code. * - * @param {number} errorCode - The error code. - * @returns {ApiError} An API error instance. - * @protected + * @param errorCode - The error code. + * @returns An API error instance. */ protected createApiError(errorCode: number): ApiError { return new ApiError(errorCode, this.getApiErrorMessage(errorCode)); @@ -132,14 +125,13 @@ export class ErrorHandleService implements IErrorHandleService { /** * Retrieves the API error message based on the provided error code. * - * @param {number} errorCode - The error code. - * @returns {string} The API error message. - * @protected + * @param errorCode - The error code. + * @returns The API error message. */ protected getApiErrorMessage(errorCode: number): string { const errorCodeKey = findKeyByValue(EErrorCodes, errorCode.toString()); - return (!!errorCodeKey && errorCodeKey in EApiErrors) + return !!errorCodeKey && errorCodeKey in EApiErrors ? EApiErrors[errorCodeKey as keyof typeof EApiErrors] : ErrorHandleService.DEFAULT_ERROR_MESSAGE; } diff --git a/src/types/internal/RettiwtConfig.ts b/src/types/internal/RettiwtConfig.ts index 1c8bb532..46b1688f 100644 --- a/src/types/internal/RettiwtConfig.ts +++ b/src/types/internal/RettiwtConfig.ts @@ -1,5 +1,5 @@ // TYPES -import { IErrorHandleService } from "../public/ErrorHandleService"; +import { IErrorHandleService } from '../public/ErrorHandleService'; /** * The configuration for initializing a new Rettiwt instance. diff --git a/src/types/public/ErrorHandleService.ts b/src/types/public/ErrorHandleService.ts index fee9c094..40f5da1c 100644 --- a/src/types/public/ErrorHandleService.ts +++ b/src/types/public/ErrorHandleService.ts @@ -4,10 +4,10 @@ * @public */ export interface IErrorHandleService { - /** + /** * The method called when an error response is received from Twitter API. * - * @param {unknown} error - The error caught while making Axios request to Twitter API. + * @param error - The error caught while making Axios request to Twitter API. */ handle(error: unknown): void; } From d4280dc7d7ff9cf2d1724868593b3b074f83aa33 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Wed, 10 Jan 2024 17:08:30 +0530 Subject: [PATCH 06/39] Renamed IErrorHandlerService -> IErrorHandler --- src/models/internal/RettiwtConfig.ts | 6 +++--- src/services/internal/FetcherService.ts | 8 ++++---- src/services/public/ErrorHandleService.ts | 4 ++-- src/types/internal/RettiwtConfig.ts | 4 ++-- src/types/public/ErrorHandleService.ts | 13 ------------- src/types/public/ErrorHandler.ts | 13 +++++++++++++ 6 files changed, 24 insertions(+), 24 deletions(-) delete mode 100644 src/types/public/ErrorHandleService.ts create mode 100644 src/types/public/ErrorHandler.ts diff --git a/src/models/internal/RettiwtConfig.ts b/src/models/internal/RettiwtConfig.ts index f6dfbb9e..3abed91f 100644 --- a/src/models/internal/RettiwtConfig.ts +++ b/src/models/internal/RettiwtConfig.ts @@ -1,5 +1,5 @@ // TYPES -import { IErrorHandleService } from '../../types/public/ErrorHandleService'; +import { IErrorHandler } from '../../types/public/ErrorHandler'; import { IRettiwtConfig } from '../../types/internal/RettiwtConfig'; /** @@ -12,7 +12,7 @@ export class RettiwtConfig implements IRettiwtConfig { public guestKey?: string; public proxyUrl?: URL; public logging?: boolean; - public errorHandleService?: IErrorHandleService; + public errorHandler?: IErrorHandler; /** * Initializes a new configuration object from the given config. @@ -24,6 +24,6 @@ export class RettiwtConfig implements IRettiwtConfig { this.guestKey = config.guestKey; this.proxyUrl = config.proxyUrl; this.logging = config.logging; - this.errorHandleService = config.errorHandleService; + this.errorHandler = config.errorHandler; } } diff --git a/src/services/internal/FetcherService.ts b/src/services/internal/FetcherService.ts index 99348d05..3c838c0f 100644 --- a/src/services/internal/FetcherService.ts +++ b/src/services/internal/FetcherService.ts @@ -20,7 +20,7 @@ import { ErrorHandleService } from '../public/ErrorHandleService'; import { LogService } from './LogService'; // TYPES -import { IErrorHandleService } from '../../types/public/ErrorHandleService'; +import { IErrorHandler } from '../../types/public/ErrorHandler'; // ENUMS import { EApiErrors } from '../../enums/ApiErrors'; @@ -54,7 +54,7 @@ export class FetcherService { private readonly logger: LogService; /** The service used to handle HTTP and API errors */ - private readonly errorHandleService: IErrorHandleService; + private readonly errorHandler: IErrorHandler; /** * @param config - The config object for configuring the Rettiwt instance. @@ -75,7 +75,7 @@ export class FetcherService { this.isAuthenticated = config?.apiKey ? true : false; this.httpsAgent = this.getHttpsAgent(config?.proxyUrl); this.logger = new LogService(config?.logging); - this.errorHandleService = config?.errorHandleService ?? new ErrorHandleService(); + this.errorHandler = config?.errorHandler ?? new ErrorHandleService(); } /** @@ -167,7 +167,7 @@ export class FetcherService { * If Axios request results in an error, catch it and rethrow a more specific error. */ return await axios>(axiosRequest).catch((error: unknown) => { - this.errorHandleService.handle(error); + this.errorHandler.handle(error); throw error; }); diff --git a/src/services/public/ErrorHandleService.ts b/src/services/public/ErrorHandleService.ts index f1f7fe8b..b0ca4049 100644 --- a/src/services/public/ErrorHandleService.ts +++ b/src/services/public/ErrorHandleService.ts @@ -3,7 +3,7 @@ import axios, { AxiosResponse } from 'axios'; import { findKeyByValue } from '../../helper/JsonUtils'; // TYPES -import { IErrorHandleService } from '../../types/public/ErrorHandleService'; +import { IErrorHandler } from '../../types/public/ErrorHandler'; // ENUMS import { EApiErrors } from '../../enums/ApiErrors'; @@ -19,7 +19,7 @@ import HttpError from '../../errors/HttpError'; * * @public */ -export class ErrorHandleService implements IErrorHandleService { +export class ErrorHandleService implements IErrorHandler { /** * Error message used when the specific error type is not defined in the required enums. */ diff --git a/src/types/internal/RettiwtConfig.ts b/src/types/internal/RettiwtConfig.ts index 46b1688f..4d9f1689 100644 --- a/src/types/internal/RettiwtConfig.ts +++ b/src/types/internal/RettiwtConfig.ts @@ -1,5 +1,5 @@ // TYPES -import { IErrorHandleService } from '../public/ErrorHandleService'; +import { IErrorHandler } from '../public/ErrorHandler'; /** * The configuration for initializing a new Rettiwt instance. @@ -20,5 +20,5 @@ export interface IRettiwtConfig { logging?: boolean; /** Optional custom error handler to define error conditions and process API/HTTP errors in axios responses. */ - errorHandleService?: IErrorHandleService; + errorHandleService?: IErrorHandler; } diff --git a/src/types/public/ErrorHandleService.ts b/src/types/public/ErrorHandleService.ts deleted file mode 100644 index 40f5da1c..00000000 --- a/src/types/public/ErrorHandleService.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Defines error conditions and processes API/HTTP errors in axios responses. - * - * @public - */ -export interface IErrorHandleService { - /** - * The method called when an error response is received from Twitter API. - * - * @param error - The error caught while making Axios request to Twitter API. - */ - handle(error: unknown): void; -} diff --git a/src/types/public/ErrorHandler.ts b/src/types/public/ErrorHandler.ts new file mode 100644 index 00000000..4eb1f93a --- /dev/null +++ b/src/types/public/ErrorHandler.ts @@ -0,0 +1,13 @@ +/** + * Defines the error handler that processes API/HTTP errors in the responses. + * + * @public + */ +export interface IErrorHandler { + /** + * The method called when an error response is received from Twitter API. + * + * @param error - The error caught while making request to Twitter API. + */ + handle(error: unknown): void; +} From 1560d327b70ed6e34d30468c36e92591757b9331 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Wed, 10 Jan 2024 17:51:31 +0530 Subject: [PATCH 07/39] Created interfaces for different types of errors --- src/errors/ApiError.ts | 14 ------------ src/errors/HttpError.ts | 14 ------------ src/errors/RettiwtError.ts | 9 -------- src/models/internal/errors/ApiError.ts | 26 ++++++++++++++++++++++ src/models/internal/errors/HttpError.ts | 26 ++++++++++++++++++++++ src/models/internal/errors/RettiwtError.ts | 12 ++++++++++ src/services/public/ErrorHandleService.ts | 4 ++-- src/types/internal/errors/ApiError.ts | 9 ++++++++ src/types/internal/errors/HttpError.ts | 9 ++++++++ 9 files changed, 84 insertions(+), 39 deletions(-) delete mode 100644 src/errors/ApiError.ts delete mode 100644 src/errors/HttpError.ts delete mode 100644 src/errors/RettiwtError.ts create mode 100644 src/models/internal/errors/ApiError.ts create mode 100644 src/models/internal/errors/HttpError.ts create mode 100644 src/models/internal/errors/RettiwtError.ts create mode 100644 src/types/internal/errors/ApiError.ts create mode 100644 src/types/internal/errors/HttpError.ts diff --git a/src/errors/ApiError.ts b/src/errors/ApiError.ts deleted file mode 100644 index b0a1e33a..00000000 --- a/src/errors/ApiError.ts +++ /dev/null @@ -1,14 +0,0 @@ -// ERRORS -import RettiwtError from './RettiwtError'; - -class ApiError extends RettiwtError { - public errorCode: number; - - public constructor(errorCode: number, message?: string) { - super(message); - - this.errorCode = errorCode; - } -} - -export default ApiError; diff --git a/src/errors/HttpError.ts b/src/errors/HttpError.ts deleted file mode 100644 index 75ab1b0c..00000000 --- a/src/errors/HttpError.ts +++ /dev/null @@ -1,14 +0,0 @@ -// ERRORS -import RettiwtError from './RettiwtError'; - -class HttpError extends RettiwtError { - public httpStatus: number; - - public constructor(httpStatus: number, message?: string) { - super(message); - - this.httpStatus = httpStatus; - } -} - -export default HttpError; diff --git a/src/errors/RettiwtError.ts b/src/errors/RettiwtError.ts deleted file mode 100644 index d4a1c7a8..00000000 --- a/src/errors/RettiwtError.ts +++ /dev/null @@ -1,9 +0,0 @@ -class RettiwtError extends Error { - public constructor(message?: string) { - super(message); - - Object.setPrototypeOf(this, RettiwtError.prototype); - } -} - -export default RettiwtError; diff --git a/src/models/internal/errors/ApiError.ts b/src/models/internal/errors/ApiError.ts new file mode 100644 index 00000000..2c270ffc --- /dev/null +++ b/src/models/internal/errors/ApiError.ts @@ -0,0 +1,26 @@ +// ERRORS +import { RettiwtError } from './RettiwtError'; + +// TYPES +import { IApiError } from '../../../types/internal/errors/ApiError'; + +/** + * Represents an error that is thrown by Twitter API. + * + * @internal + */ +export class ApiError extends RettiwtError implements IApiError { + public code: number; + + /** + * Initializes a new ApiError based on the given error details. + * + * @param errorCode - The error code thrown by Twitter API. + * @param message - Any additional error message. + */ + public constructor(errorCode: number, message?: string) { + super(message); + + this.code = errorCode; + } +} diff --git a/src/models/internal/errors/HttpError.ts b/src/models/internal/errors/HttpError.ts new file mode 100644 index 00000000..ddc3e380 --- /dev/null +++ b/src/models/internal/errors/HttpError.ts @@ -0,0 +1,26 @@ +// ERRORS +import { RettiwtError } from './RettiwtError'; + +// TYPES +import { IHttpError } from '../../../types/internal/errors/HttpError'; + +/** + * Represents an HTTP error that occues while making a request to Twitter API. + * + * @internal + */ +export class HttpError extends RettiwtError implements IHttpError { + public status: number; + + /** + * Initializes a new HttpError based on the given error details. + * + * @param httpStatus - The HTTP status code received upon making the request + * @param message - Any additional error message. + */ + public constructor(httpStatus: number, message?: string) { + super(message); + + this.status = httpStatus; + } +} diff --git a/src/models/internal/errors/RettiwtError.ts b/src/models/internal/errors/RettiwtError.ts new file mode 100644 index 00000000..27c26c28 --- /dev/null +++ b/src/models/internal/errors/RettiwtError.ts @@ -0,0 +1,12 @@ +/** + * Represents an error that arises inside the package. + * + * @internal + */ +export class RettiwtError extends Error { + public constructor(message?: string) { + super(message); + + Object.setPrototypeOf(this, RettiwtError.prototype); + } +} diff --git a/src/services/public/ErrorHandleService.ts b/src/services/public/ErrorHandleService.ts index b0ca4049..f1850116 100644 --- a/src/services/public/ErrorHandleService.ts +++ b/src/services/public/ErrorHandleService.ts @@ -11,8 +11,8 @@ import { EErrorCodes } from 'rettiwt-core'; import { EHttpStatus } from '../../enums/HTTP'; // ERRORS -import ApiError from '../../errors/ApiError'; -import HttpError from '../../errors/HttpError'; +import { ApiError } from '../../models/internal/errors/ApiError'; +import { HttpError } from '../../models/internal/errors/HttpError'; /** * Defines error conditions and processes API/HTTP errors in Axios responses. diff --git a/src/types/internal/errors/ApiError.ts b/src/types/internal/errors/ApiError.ts new file mode 100644 index 00000000..ab6ec632 --- /dev/null +++ b/src/types/internal/errors/ApiError.ts @@ -0,0 +1,9 @@ +/** + * Represents an error that is thrown by Twitter API. + * + * @internal + */ +export interface IApiError { + /** The error code thrown by Twitter API. */ + code: number; +} diff --git a/src/types/internal/errors/HttpError.ts b/src/types/internal/errors/HttpError.ts new file mode 100644 index 00000000..7e9bff9e --- /dev/null +++ b/src/types/internal/errors/HttpError.ts @@ -0,0 +1,9 @@ +/** + * Represents an HTTP error that occues while making a request to Twitter API. + * + * @internal + */ +export interface IHttpError { + /** The HTTP status code. */ + status: number; +} From ea40ae46ede071c181c2b2ff6ffc26b3e3c57ff0 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Wed, 10 Jan 2024 17:55:50 +0530 Subject: [PATCH 08/39] Renamed ErrorHandlerService -> ErrorService --- .../ErrorHandleService.ts => internal/ErrorService.ts} | 8 ++++---- src/services/internal/FetcherService.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) rename src/services/{public/ErrorHandleService.ts => internal/ErrorService.ts} (94%) diff --git a/src/services/public/ErrorHandleService.ts b/src/services/internal/ErrorService.ts similarity index 94% rename from src/services/public/ErrorHandleService.ts rename to src/services/internal/ErrorService.ts index f1850116..83b5fed7 100644 --- a/src/services/public/ErrorHandleService.ts +++ b/src/services/internal/ErrorService.ts @@ -15,11 +15,11 @@ import { ApiError } from '../../models/internal/errors/ApiError'; import { HttpError } from '../../models/internal/errors/HttpError'; /** - * Defines error conditions and processes API/HTTP errors in Axios responses. + * The basic service that handles any errors. * * @public */ -export class ErrorHandleService implements IErrorHandler { +export class ErrorService implements IErrorHandler { /** * Error message used when the specific error type is not defined in the required enums. */ @@ -97,7 +97,7 @@ export class ErrorHandleService implements IErrorHandler { protected getHttpErrorMessage(httpStatus: number): string { return Object.values(EHttpStatus).includes(httpStatus) ? EHttpStatus[httpStatus] - : ErrorHandleService.DEFAULT_ERROR_MESSAGE; + : ErrorService.DEFAULT_ERROR_MESSAGE; } /** @@ -133,6 +133,6 @@ export class ErrorHandleService implements IErrorHandler { return !!errorCodeKey && errorCodeKey in EApiErrors ? EApiErrors[errorCodeKey as keyof typeof EApiErrors] - : ErrorHandleService.DEFAULT_ERROR_MESSAGE; + : ErrorService.DEFAULT_ERROR_MESSAGE; } } diff --git a/src/services/internal/FetcherService.ts b/src/services/internal/FetcherService.ts index 3c838c0f..6c9c8e15 100644 --- a/src/services/internal/FetcherService.ts +++ b/src/services/internal/FetcherService.ts @@ -16,7 +16,7 @@ import { AuthCredential, Auth } from 'rettiwt-auth'; import { HttpsProxyAgent } from 'https-proxy-agent'; // SERVICES -import { ErrorHandleService } from '../public/ErrorHandleService'; +import { ErrorService } from './ErrorService'; import { LogService } from './LogService'; // TYPES @@ -75,7 +75,7 @@ export class FetcherService { this.isAuthenticated = config?.apiKey ? true : false; this.httpsAgent = this.getHttpsAgent(config?.proxyUrl); this.logger = new LogService(config?.logging); - this.errorHandler = config?.errorHandler ?? new ErrorHandleService(); + this.errorHandler = config?.errorHandler ?? new ErrorService(); } /** From 5c06a9494da0fe9fa905bed097f08e1054a5723e Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Wed, 10 Jan 2024 18:15:04 +0530 Subject: [PATCH 09/39] Renamed some vars in ErrorService --- src/services/internal/ErrorService.ts | 32 +++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/services/internal/ErrorService.ts b/src/services/internal/ErrorService.ts index 83b5fed7..e6974723 100644 --- a/src/services/internal/ErrorService.ts +++ b/src/services/internal/ErrorService.ts @@ -15,7 +15,7 @@ import { ApiError } from '../../models/internal/errors/ApiError'; import { HttpError } from '../../models/internal/errors/HttpError'; /** - * The basic service that handles any errors. + * The base service that handles any errors. * * @public */ @@ -28,7 +28,7 @@ export class ErrorService implements IErrorHandler { /** * The method called when an error response is received from Twitter API. * - * @param error - The error caught while making Axios request to Twitter API. + * @param error - The error caught while making HTTP request to Twitter API. */ public handle(error: unknown): void { const axiosResponse = this.getAxiosResponse(error); @@ -38,11 +38,11 @@ export class ErrorService implements IErrorHandler { } /** - * Retrieves the Axios response from the given error. + * Retrieves the response data from the given error. * * @param error - The error object. - * @returns The Axios response. - * @throws Throws the original error if it is not an Axios error with a response. + * @returns The response data. + * @throws The original error if it is not an HTTP error with a response. */ protected getAxiosResponse(error: unknown): AxiosResponse { if (axios.isAxiosError(error) && !!error.response) { @@ -53,23 +53,23 @@ export class ErrorService implements IErrorHandler { } /** - * Handles HTTP error in an Axios response. + * Handles HTTP error in a response. * - * @param axiosResponse - The response object received. + * @param response - The response object received. * @throws An error with the corresponding HTTP status text if any HTTP-related error has occurred. */ - protected handleHttpError(axiosResponse: AxiosResponse): void { - throw this.createHttpError(axiosResponse.status); + protected handleHttpError(response: AxiosResponse): void { + throw this.createHttpError(response.status); } /** - * Handles API error in an Axios response. + * Handles API error in a response. * - * @param axiosResponse - The response object received. + * @param response - The response object received. * @throws An error with the corresponding API error message if any API-related error has occurred. */ - protected handleApiError(axiosResponse: AxiosResponse): void { - const errorCode = this.getErrorCode(axiosResponse); + protected handleApiError(response: AxiosResponse): void { + const errorCode = this.getErrorCode(response); if (errorCode === undefined) { return; @@ -103,11 +103,11 @@ export class ErrorService implements IErrorHandler { /** * Retrieves the API error code from the Axios response data. * - * @param axiosResponse - The response object received. + * @param response - The response object received. * @returns The error code, or undefined if not found. */ - protected getErrorCode(axiosResponse: AxiosResponse): number | undefined { - const errors = (axiosResponse.data as { errors: { code: number }[] }).errors; + protected getErrorCode(response: AxiosResponse): number | undefined { + const errors = (response.data as { errors: { code: number }[] }).errors; return !!errors && errors.length ? errors[0].code : undefined; } From f4b89840d6c7af977e60772e75902329896fd6dd Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Wed, 10 Jan 2024 19:10:26 +0530 Subject: [PATCH 10/39] Removed unnecessary RettiwtConfig class --- src/Rettiwt.ts | 6 ++--- src/index.ts | 1 - src/models/internal/RettiwtConfig.ts | 29 ------------------------- src/services/internal/FetcherService.ts | 4 ++-- src/services/public/TweetService.ts | 6 +++-- src/services/public/UserService.ts | 6 +++-- src/types/internal/RettiwtConfig.ts | 4 ++-- 7 files changed, 15 insertions(+), 41 deletions(-) delete mode 100644 src/models/internal/RettiwtConfig.ts diff --git a/src/Rettiwt.ts b/src/Rettiwt.ts index d92b7e9b..55723882 100644 --- a/src/Rettiwt.ts +++ b/src/Rettiwt.ts @@ -2,8 +2,8 @@ import { TweetService } from './services/public/TweetService'; import { UserService } from './services/public/UserService'; -// MODELS -import { RettiwtConfig } from './models/internal/RettiwtConfig'; +// TYPES +import { IRettiwtConfig } from './types/internal/RettiwtConfig'; /** * The class for accessing Twitter API. @@ -58,7 +58,7 @@ export class Rettiwt { * * @param config - The config object for configuring the Rettiwt instance. */ - public constructor(config?: RettiwtConfig) { + public constructor(config?: IRettiwtConfig) { this.tweet = new TweetService(config); this.user = new UserService(config); } diff --git a/src/index.ts b/src/index.ts index 4a5d1204..a86ea579 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,7 +7,6 @@ export * from './enums/HTTP'; export * from './enums/Logging'; // Exporting models -export * from './models/internal/RettiwtConfig'; export * from './models/public/CursoredData'; export * from './models/public/List'; export * from './models/public/Tweet'; diff --git a/src/models/internal/RettiwtConfig.ts b/src/models/internal/RettiwtConfig.ts deleted file mode 100644 index 3abed91f..00000000 --- a/src/models/internal/RettiwtConfig.ts +++ /dev/null @@ -1,29 +0,0 @@ -// TYPES -import { IErrorHandler } from '../../types/public/ErrorHandler'; -import { IRettiwtConfig } from '../../types/internal/RettiwtConfig'; - -/** - * The configuration for initializing a new Rettiwt instance. - * - * @internal - */ -export class RettiwtConfig implements IRettiwtConfig { - public apiKey?: string; - public guestKey?: string; - public proxyUrl?: URL; - public logging?: boolean; - public errorHandler?: IErrorHandler; - - /** - * Initializes a new configuration object from the given config. - * - * @param config - The configuration object. - */ - public constructor(config: RettiwtConfig) { - this.apiKey = config.apiKey; - this.guestKey = config.guestKey; - this.proxyUrl = config.proxyUrl; - this.logging = config.logging; - this.errorHandler = config.errorHandler; - } -} diff --git a/src/services/internal/FetcherService.ts b/src/services/internal/FetcherService.ts index 6c9c8e15..a74cfd95 100644 --- a/src/services/internal/FetcherService.ts +++ b/src/services/internal/FetcherService.ts @@ -20,6 +20,7 @@ import { ErrorService } from './ErrorService'; import { LogService } from './LogService'; // TYPES +import { IRettiwtConfig } from '../../types/internal/RettiwtConfig'; import { IErrorHandler } from '../../types/public/ErrorHandler'; // ENUMS @@ -27,7 +28,6 @@ import { EApiErrors } from '../../enums/ApiErrors'; import { ELogActions } from '../../enums/Logging'; // MODELS -import { RettiwtConfig } from '../../models/internal/RettiwtConfig'; import { CursoredData } from '../../models/public/CursoredData'; import { Tweet } from '../../models/public/Tweet'; import { User } from '../../models/public/User'; @@ -59,7 +59,7 @@ export class FetcherService { /** * @param config - The config object for configuring the Rettiwt instance. */ - public constructor(config?: RettiwtConfig) { + public constructor(config?: IRettiwtConfig) { // If API key is supplied if (config?.apiKey) { this.cred = this.getAuthCredential(config.apiKey); diff --git a/src/services/public/TweetService.ts b/src/services/public/TweetService.ts index d5480e91..3859b92b 100644 --- a/src/services/public/TweetService.ts +++ b/src/services/public/TweetService.ts @@ -4,8 +4,10 @@ import { EResourceType, TweetFilter } from 'rettiwt-core'; // SERVICES import { FetcherService } from '../internal/FetcherService'; +// TYPES +import { IRettiwtConfig } from '../../types/internal/RettiwtConfig'; + // MODELS -import { RettiwtConfig } from '../../models/internal/RettiwtConfig'; import { Tweet } from '../../models/public/Tweet'; import { User } from '../../models/public/User'; import { CursoredData } from '../../models/public/CursoredData'; @@ -21,7 +23,7 @@ export class TweetService extends FetcherService { * * @internal */ - public constructor(config?: RettiwtConfig) { + public constructor(config?: IRettiwtConfig) { super(config); } diff --git a/src/services/public/UserService.ts b/src/services/public/UserService.ts index 6f73c3de..109d675e 100644 --- a/src/services/public/UserService.ts +++ b/src/services/public/UserService.ts @@ -4,8 +4,10 @@ import { EResourceType } from 'rettiwt-core'; // SERVICES import { FetcherService } from '../internal/FetcherService'; +// TYPES +import { IRettiwtConfig } from '../../types/internal/RettiwtConfig'; + // MODELS -import { RettiwtConfig } from '../../models/internal/RettiwtConfig'; import { User } from '../../models/public/User'; import { Tweet } from '../../models/public/Tweet'; @@ -23,7 +25,7 @@ export class UserService extends FetcherService { * * @internal */ - public constructor(config?: RettiwtConfig) { + public constructor(config?: IRettiwtConfig) { super(config); } diff --git a/src/types/internal/RettiwtConfig.ts b/src/types/internal/RettiwtConfig.ts index 4d9f1689..c8c7ef2f 100644 --- a/src/types/internal/RettiwtConfig.ts +++ b/src/types/internal/RettiwtConfig.ts @@ -19,6 +19,6 @@ export interface IRettiwtConfig { /** Whether to write logs to console or not. */ logging?: boolean; - /** Optional custom error handler to define error conditions and process API/HTTP errors in axios responses. */ - errorHandleService?: IErrorHandler; + /** Optional custom error handler to define error conditions and process API/HTTP errors in responses. */ + errorHandler?: IErrorHandler; } From ea77369ed268c615f6df408fbd05503edaed534a Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Wed, 10 Jan 2024 19:37:11 +0530 Subject: [PATCH 11/39] Removed unnecessary interfaces --- src/Rettiwt.ts | 2 +- src/enums/{ApiErrors.ts => Api.ts} | 0 src/index.ts | 30 ++++---- src/models/internal/errors/ApiError.ts | 6 +- src/models/internal/errors/HttpError.ts | 6 +- src/models/public/CursoredData.ts | 11 +-- src/models/public/List.ts | 18 +++-- src/models/public/Tweet.ts | 44 ++++++++++-- src/models/public/User.ts | 32 +++++++-- src/services/internal/ErrorService.ts | 6 +- src/services/internal/FetcherService.ts | 6 +- src/services/public/TweetService.ts | 2 +- src/services/public/UserService.ts | 2 +- src/types/{public => }/ErrorHandler.ts | 0 src/types/{internal => }/RettiwtConfig.ts | 4 +- src/types/internal/errors/ApiError.ts | 9 --- src/types/internal/errors/HttpError.ts | 9 --- src/types/public/CursoredData.ts | 24 ------- src/types/public/List.ts | 27 ------- src/types/public/Tweet.ts | 86 ----------------------- src/types/public/User.ts | 48 ------------- 21 files changed, 119 insertions(+), 253 deletions(-) rename src/enums/{ApiErrors.ts => Api.ts} (100%) rename src/types/{public => }/ErrorHandler.ts (100%) rename src/types/{internal => }/RettiwtConfig.ts (90%) delete mode 100644 src/types/internal/errors/ApiError.ts delete mode 100644 src/types/internal/errors/HttpError.ts delete mode 100644 src/types/public/CursoredData.ts delete mode 100644 src/types/public/List.ts delete mode 100644 src/types/public/Tweet.ts delete mode 100644 src/types/public/User.ts diff --git a/src/Rettiwt.ts b/src/Rettiwt.ts index 55723882..ce5ae88d 100644 --- a/src/Rettiwt.ts +++ b/src/Rettiwt.ts @@ -3,7 +3,7 @@ import { TweetService } from './services/public/TweetService'; import { UserService } from './services/public/UserService'; // TYPES -import { IRettiwtConfig } from './types/internal/RettiwtConfig'; +import { IRettiwtConfig } from './types/RettiwtConfig'; /** * The class for accessing Twitter API. diff --git a/src/enums/ApiErrors.ts b/src/enums/Api.ts similarity index 100% rename from src/enums/ApiErrors.ts rename to src/enums/Api.ts diff --git a/src/index.ts b/src/index.ts index a86ea579..ba81a004 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,28 +1,32 @@ // MAIN export * from './Rettiwt'; -// Exporting enums -export * from './enums/ApiErrors'; -export * from './enums/HTTP'; +// EXTERNAL +export { TweetFilter } from 'rettiwt-core'; + +// ENUMS +export * from './enums/Api'; +export * from './enums/Http'; export * from './enums/Logging'; -// Exporting models +// ERROR MODELS +export * from './models/internal/errors/ApiError'; +export * from './models/internal/errors/HttpError'; +export * from './models/internal/errors/RettiwtError'; + +// DATA MODELS export * from './models/public/CursoredData'; export * from './models/public/List'; export * from './models/public/Tweet'; export * from './models/public/User'; -export { TweetFilter } from 'rettiwt-core'; -// Exporting services +// SERVICES +export * from './services/internal/ErrorService'; export * from './services/internal/FetcherService'; export * from './services/internal/LogService'; export * from './services/public/TweetService'; export * from './services/public/UserService'; -// Exporting types -export * from './types/internal/RettiwtConfig'; -export * from './types/public/CursoredData'; -export * from './types/public/List'; -export * from './types/public/Tweet'; -export * from './types/public/User'; -export { ITweetFilter } from 'rettiwt-core'; +// TYPES +export * from './types/RettiwtConfig'; +export * from './types/ErrorHandler'; diff --git a/src/models/internal/errors/ApiError.ts b/src/models/internal/errors/ApiError.ts index 2c270ffc..9b8d6cd6 100644 --- a/src/models/internal/errors/ApiError.ts +++ b/src/models/internal/errors/ApiError.ts @@ -1,15 +1,13 @@ // ERRORS import { RettiwtError } from './RettiwtError'; -// TYPES -import { IApiError } from '../../../types/internal/errors/ApiError'; - /** * Represents an error that is thrown by Twitter API. * * @internal */ -export class ApiError extends RettiwtError implements IApiError { +export class ApiError extends RettiwtError { + /** The error code thrown by Twitter API. */ public code: number; /** diff --git a/src/models/internal/errors/HttpError.ts b/src/models/internal/errors/HttpError.ts index ddc3e380..ad389db3 100644 --- a/src/models/internal/errors/HttpError.ts +++ b/src/models/internal/errors/HttpError.ts @@ -1,15 +1,13 @@ // ERRORS import { RettiwtError } from './RettiwtError'; -// TYPES -import { IHttpError } from '../../../types/internal/errors/HttpError'; - /** * Represents an HTTP error that occues while making a request to Twitter API. * * @internal */ -export class HttpError extends RettiwtError implements IHttpError { +export class HttpError extends RettiwtError { + /** The HTTP status code. */ public status: number; /** diff --git a/src/models/public/CursoredData.ts b/src/models/public/CursoredData.ts index a9a62894..e9ab289c 100644 --- a/src/models/public/CursoredData.ts +++ b/src/models/public/CursoredData.ts @@ -2,9 +2,6 @@ import { Tweet } from './Tweet'; import { User } from './User'; -// TYPES -import { ICursor, ICursoredData } from '../../types/public/CursoredData'; - /** * The data that us fetched batch-wise along with a cursor. * @@ -12,8 +9,11 @@ import { ICursor, ICursoredData } from '../../types/public/CursoredData'; * * @public */ -export class CursoredData implements ICursoredData { +export class CursoredData { + /** The list of data of the given type. */ public list: T[] = []; + + /** The cursor to the next batch of data. */ public next: Cursor; /** @@ -31,7 +31,8 @@ export class CursoredData implements ICursoredData { * * @public */ -export class Cursor implements ICursor { +export class Cursor { + /** The cursor string. */ public value: string; /** diff --git a/src/models/public/List.ts b/src/models/public/List.ts index b96850ee..b9231fe1 100644 --- a/src/models/public/List.ts +++ b/src/models/public/List.ts @@ -1,21 +1,31 @@ // PACKAGES import { IList as IRawList } from 'rettiwt-core'; -// TYPES -import { IList } from '../../types/public/List'; - /** * The details of a single Twitter List. * * @public */ -export class List implements IList { +export class List { + /** The rest id of the list. */ public id: string; + + /** The name of the list. */ public name: string; + + /** The date and time of creation of the list, int UTC string format. */ public createdAt: string; + + /** The list description. */ public description: string; + + /** The number of memeber of the list. */ public memberCount: number; + + /** The number of subscribers of the list. */ public subscriberCount: number; + + /** The rest id of the user who created the list. */ public createdBy: string; /** diff --git a/src/models/public/Tweet.ts b/src/models/public/Tweet.ts index 26865b6e..96e2ab51 100644 --- a/src/models/public/Tweet.ts +++ b/src/models/public/Tweet.ts @@ -6,9 +6,6 @@ import { EMediaType, } from 'rettiwt-core'; -// TYPES -import { ITweet, ITweetEntities } from '../../types/public/Tweet'; - // MODELS import { User } from './User'; @@ -20,21 +17,50 @@ import { normalizeText } from '../../helper/JsonUtils'; * * @public */ -export class Tweet implements ITweet { +export class Tweet { + /** The rest id of the tweet. */ public id: string; + + /** The details of the user who made the tweet. */ public tweetBy: User; + + /** The date and time of creation of the tweet, in UTC string format. */ public createdAt: string; + + /** Additional tweet entities like urls, mentions, etc. */ public entities: TweetEntities; + + /** The urls of the media contents of the tweet (if any). */ public media: TweetMedia[]; + + /** The rest id of the tweet which is quoted in the tweet. */ public quoted: string; + + /** The full text content of the tweet. */ public fullText: string; + + /** The rest id of the user to which the tweet is a reply. */ public replyTo: string; + + /** The language in which the tweet is written. */ public lang: string; + + /** The number of quotes of the tweet. */ public quoteCount: number; + + /** The number of replies to the tweet. */ public replyCount: number; + + /** The number of retweets of the tweet. */ public retweetCount: number; + + /** The number of likes of the tweet. */ public likeCount: number; + + /** The number of views of a tweet. */ public viewCount: number; + + /** The number of bookmarks of a tweet. */ public bookmarkCount: number; /** @@ -66,9 +92,14 @@ export class Tweet implements ITweet { * * @public */ -export class TweetEntities implements ITweetEntities { +export class TweetEntities { + /** The list of hashtags mentioned in the tweet. */ public hashtags: string[] = []; + + /** The list of urls mentioned in the tweet. */ public urls: string[] = []; + + /** The list of IDs of users mentioned in the tweet. */ public mentionedUsers: string[] = []; /** @@ -106,7 +137,10 @@ export class TweetEntities implements ITweetEntities { * @public */ export class TweetMedia { + /** The type of media. */ public type: EMediaType; + + /** The direct URL to the media. */ public url: string = ''; /** diff --git a/src/models/public/User.ts b/src/models/public/User.ts index e75b43d1..51ed6804 100644 --- a/src/models/public/User.ts +++ b/src/models/public/User.ts @@ -1,28 +1,52 @@ // PACKAGES import { IUser as IRawUser } from 'rettiwt-core'; -// TYPES -import { IUser } from '../../types/public/User'; - /** * The details of a single user. * * @public */ -export class User implements IUser { +export class User { + /** The rest id of the user. */ public id: string; + + /** The username/screenname of the user. */ public userName: string; + + /** The full name of the user. */ public fullName: string; + + /** The creation date of user's account. */ public createdAt: string; + + /** The user's description. */ public description: string; + + /** Whether the account is verified or not. */ public isVerified: boolean; + + /** The number of tweets liked by the user. */ public favouritesCount: number; + + /** The number of followers of the user. */ public followersCount: number; + + /** The number of following of the user. */ public followingsCount: number; + + /** The number of tweets made by the user. */ public statusesCount: number; + + /** The location of user as provided by user. */ public location: string; + + /** The rest id of the tweet pinned in the user's profile. */ public pinnedTweet: string; + + /** The url of the profile banner image. */ public profileBanner: string; + + /** The url of the profile image. */ public profileImage: string; /** diff --git a/src/services/internal/ErrorService.ts b/src/services/internal/ErrorService.ts index e6974723..2716dbc9 100644 --- a/src/services/internal/ErrorService.ts +++ b/src/services/internal/ErrorService.ts @@ -3,12 +3,12 @@ import axios, { AxiosResponse } from 'axios'; import { findKeyByValue } from '../../helper/JsonUtils'; // TYPES -import { IErrorHandler } from '../../types/public/ErrorHandler'; +import { IErrorHandler } from '../../types/ErrorHandler'; // ENUMS -import { EApiErrors } from '../../enums/ApiErrors'; +import { EApiErrors } from '../../enums/Api'; +import { EHttpStatus } from '../../enums/Http'; import { EErrorCodes } from 'rettiwt-core'; -import { EHttpStatus } from '../../enums/HTTP'; // ERRORS import { ApiError } from '../../models/internal/errors/ApiError'; diff --git a/src/services/internal/FetcherService.ts b/src/services/internal/FetcherService.ts index a74cfd95..11824fff 100644 --- a/src/services/internal/FetcherService.ts +++ b/src/services/internal/FetcherService.ts @@ -20,11 +20,11 @@ import { ErrorService } from './ErrorService'; import { LogService } from './LogService'; // TYPES -import { IRettiwtConfig } from '../../types/internal/RettiwtConfig'; -import { IErrorHandler } from '../../types/public/ErrorHandler'; +import { IRettiwtConfig } from '../../types/RettiwtConfig'; +import { IErrorHandler } from '../../types/ErrorHandler'; // ENUMS -import { EApiErrors } from '../../enums/ApiErrors'; +import { EApiErrors } from '../../enums/Api'; import { ELogActions } from '../../enums/Logging'; // MODELS diff --git a/src/services/public/TweetService.ts b/src/services/public/TweetService.ts index 3859b92b..7dd70183 100644 --- a/src/services/public/TweetService.ts +++ b/src/services/public/TweetService.ts @@ -5,7 +5,7 @@ import { EResourceType, TweetFilter } from 'rettiwt-core'; import { FetcherService } from '../internal/FetcherService'; // TYPES -import { IRettiwtConfig } from '../../types/internal/RettiwtConfig'; +import { IRettiwtConfig } from '../../types/RettiwtConfig'; // MODELS import { Tweet } from '../../models/public/Tweet'; diff --git a/src/services/public/UserService.ts b/src/services/public/UserService.ts index 109d675e..65337b88 100644 --- a/src/services/public/UserService.ts +++ b/src/services/public/UserService.ts @@ -5,7 +5,7 @@ import { EResourceType } from 'rettiwt-core'; import { FetcherService } from '../internal/FetcherService'; // TYPES -import { IRettiwtConfig } from '../../types/internal/RettiwtConfig'; +import { IRettiwtConfig } from '../../types/RettiwtConfig'; // MODELS import { User } from '../../models/public/User'; diff --git a/src/types/public/ErrorHandler.ts b/src/types/ErrorHandler.ts similarity index 100% rename from src/types/public/ErrorHandler.ts rename to src/types/ErrorHandler.ts diff --git a/src/types/internal/RettiwtConfig.ts b/src/types/RettiwtConfig.ts similarity index 90% rename from src/types/internal/RettiwtConfig.ts rename to src/types/RettiwtConfig.ts index c8c7ef2f..54434f88 100644 --- a/src/types/internal/RettiwtConfig.ts +++ b/src/types/RettiwtConfig.ts @@ -1,10 +1,10 @@ // TYPES -import { IErrorHandler } from '../public/ErrorHandler'; +import { IErrorHandler } from './ErrorHandler'; /** * The configuration for initializing a new Rettiwt instance. * - * @internal + * @public */ export interface IRettiwtConfig { /** The apiKey (cookie) to use for authenticating Rettiwt against Twitter API. */ diff --git a/src/types/internal/errors/ApiError.ts b/src/types/internal/errors/ApiError.ts deleted file mode 100644 index ab6ec632..00000000 --- a/src/types/internal/errors/ApiError.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Represents an error that is thrown by Twitter API. - * - * @internal - */ -export interface IApiError { - /** The error code thrown by Twitter API. */ - code: number; -} diff --git a/src/types/internal/errors/HttpError.ts b/src/types/internal/errors/HttpError.ts deleted file mode 100644 index 7e9bff9e..00000000 --- a/src/types/internal/errors/HttpError.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Represents an HTTP error that occues while making a request to Twitter API. - * - * @internal - */ -export interface IHttpError { - /** The HTTP status code. */ - status: number; -} diff --git a/src/types/public/CursoredData.ts b/src/types/public/CursoredData.ts deleted file mode 100644 index 68b9716a..00000000 --- a/src/types/public/CursoredData.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * The data that us fetched batch-wise along with a cursor. - * - * @typeParam T - Type of data present in the list. - * - * @public - */ -export interface ICursoredData { - /** The list of data of the given type. */ - list: T[]; - - /** The cursor to the next batch of data. */ - next: ICursor; -} - -/** - * The cursor to the batch of data to be fetched. - * - * @public - */ -export interface ICursor { - /** The cursor string. */ - value: string; -} diff --git a/src/types/public/List.ts b/src/types/public/List.ts deleted file mode 100644 index 6f0a06bf..00000000 --- a/src/types/public/List.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * The details of a single Twitter List. - * - * @public - */ -export interface IList { - /** The rest id of the list. */ - id: string; - - /** The name of the list. */ - name: string; - - /** The date and time of creation of the list, int UTC string format. */ - createdAt: string; - - /** The list description. */ - description: string; - - /** The number of memeber of the list. */ - memberCount: number; - - /** The number of subscribers of the list. */ - subscriberCount: number; - - /** The rest id of the user who created the list. */ - createdBy: string; -} diff --git a/src/types/public/Tweet.ts b/src/types/public/Tweet.ts deleted file mode 100644 index cba6abfa..00000000 --- a/src/types/public/Tweet.ts +++ /dev/null @@ -1,86 +0,0 @@ -// PACKAGES -import { EMediaType } from 'rettiwt-core'; - -// TYPES -import { IUser } from './User'; - -/** - * The details of a single Tweet. - * - * @public - */ -export interface ITweet { - /** The rest id of the tweet. */ - id: string; - - /** The details of the user who made the tweet. */ - tweetBy: IUser; - - /** The date and time of creation of the tweet, in UTC string format. */ - createdAt: string; - - /** Additional tweet entities like urls, mentions, etc. */ - entities: ITweetEntities; - - /** The urls of the media contents of the tweet (if any). */ - media: ITweetMedia[]; - - /** The rest id of the tweet which is quoted in the tweet. */ - quoted: string; - - /** The full text content of the tweet. */ - fullText: string; - - /** The rest id of the user to which the tweet is a reply. */ - replyTo: string; - - /** The language in which the tweet is written. */ - lang: string; - - /** The number of quotes of the tweet. */ - quoteCount: number; - - /** The number of replies to the tweet. */ - replyCount: number; - - /** The number of retweets of the tweet. */ - retweetCount: number; - - /** The number of likes of the tweet. */ - likeCount: number; - - /** The number of views of a tweet. */ - viewCount: number; - - /** The number of bookmarks of a tweet. */ - bookmarkCount: number; -} - -/** - * The different types parsed entities like urls, media, mentions, hashtags, etc. - * - * @public - */ -export interface ITweetEntities { - /** The list of hashtags mentioned in the tweet. */ - hashtags: string[]; - - /** The list of urls mentioned in the tweet. */ - urls: string[]; - - /** The list of IDs of users mentioned in the tweet. */ - mentionedUsers: string[]; -} - -/** - * A single media content. - * - * @public - */ -export interface ITweetMedia { - /** The type of media. */ - type: EMediaType; - - /** The direct URL to the media. */ - url: string; -} diff --git a/src/types/public/User.ts b/src/types/public/User.ts deleted file mode 100644 index 9789d817..00000000 --- a/src/types/public/User.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * The details of a single user. - * - * @public - */ -export interface IUser { - /** The rest id of the user. */ - id: string; - - /** The username/screenname of the user. */ - userName: string; - - /** The full name of the user. */ - fullName: string; - - /** The creation date of user's account. */ - createdAt: string; - - /** The user's description. */ - description: string; - - /** Whether the account is verified or not. */ - isVerified: boolean; - - /** The number of tweets liked by the user. */ - favouritesCount: number; - - /** The number of followers of the user. */ - followersCount: number; - - /** The number of following of the user. */ - followingsCount: number; - - /** The number of tweets made by the user. */ - statusesCount: number; - - /** The location of user as provided by user. */ - location: string; - - /** The rest id of the tweet pinned in the user's profile. */ - pinnedTweet: string; - - /** The url of the profile banner image. */ - profileBanner: string; - - /** The url of the profile image. */ - profileImage: string; -} From c9e7d5456f6d6c49c333d92e7db5d094b88be803 Mon Sep 17 00:00:00 2001 From: omarcinkonis Date: Thu, 18 Jan 2024 16:47:48 +0200 Subject: [PATCH 12/39] Add request timeout --- src/enums/{HTTP.ts => Http.ts} | 0 .../{internal => public}/errors/ApiError.ts | 0 .../{internal => public}/errors/HttpError.ts | 0 .../errors/RettiwtError.ts | 0 src/models/public/errors/TimeoutError.ts | 18 +++++++++++ src/services/internal/ErrorService.ts | 30 +++++++++++++++---- src/services/internal/FetcherService.ts | 5 ++++ src/types/RettiwtConfig.ts | 3 ++ 8 files changed, 50 insertions(+), 6 deletions(-) rename src/enums/{HTTP.ts => Http.ts} (100%) rename src/models/{internal => public}/errors/ApiError.ts (100%) rename src/models/{internal => public}/errors/HttpError.ts (100%) rename src/models/{internal => public}/errors/RettiwtError.ts (100%) create mode 100644 src/models/public/errors/TimeoutError.ts diff --git a/src/enums/HTTP.ts b/src/enums/Http.ts similarity index 100% rename from src/enums/HTTP.ts rename to src/enums/Http.ts diff --git a/src/models/internal/errors/ApiError.ts b/src/models/public/errors/ApiError.ts similarity index 100% rename from src/models/internal/errors/ApiError.ts rename to src/models/public/errors/ApiError.ts diff --git a/src/models/internal/errors/HttpError.ts b/src/models/public/errors/HttpError.ts similarity index 100% rename from src/models/internal/errors/HttpError.ts rename to src/models/public/errors/HttpError.ts diff --git a/src/models/internal/errors/RettiwtError.ts b/src/models/public/errors/RettiwtError.ts similarity index 100% rename from src/models/internal/errors/RettiwtError.ts rename to src/models/public/errors/RettiwtError.ts diff --git a/src/models/public/errors/TimeoutError.ts b/src/models/public/errors/TimeoutError.ts new file mode 100644 index 00000000..25d7891d --- /dev/null +++ b/src/models/public/errors/TimeoutError.ts @@ -0,0 +1,18 @@ +// ERRORS +import { RettiwtError } from './RettiwtError'; + +/** + * Represents an HTTP error that occues while making a request to Twitter API. + * + * @internal + */ +export class TimeoutError extends RettiwtError { + /** + * Initializes a new TimeoutError based on the given error details. + * + * @param message - Error message with the configured timeout. + */ + public constructor(message?: string) { + super(message); + } +} diff --git a/src/services/internal/ErrorService.ts b/src/services/internal/ErrorService.ts index 2716dbc9..3cbcc2b2 100644 --- a/src/services/internal/ErrorService.ts +++ b/src/services/internal/ErrorService.ts @@ -1,5 +1,5 @@ // PACKAGES -import axios, { AxiosResponse } from 'axios'; +import axios, { AxiosError, AxiosResponse } from 'axios'; import { findKeyByValue } from '../../helper/JsonUtils'; // TYPES @@ -11,8 +11,9 @@ import { EHttpStatus } from '../../enums/Http'; import { EErrorCodes } from 'rettiwt-core'; // ERRORS -import { ApiError } from '../../models/internal/errors/ApiError'; -import { HttpError } from '../../models/internal/errors/HttpError'; +import { ApiError } from '../../models/public/errors/ApiError'; +import { HttpError } from '../../models/public/errors/HttpError'; +import { TimeoutError } from '../../models/public/errors/TimeoutError'; /** * The base service that handles any errors. @@ -31,12 +32,29 @@ export class ErrorService implements IErrorHandler { * @param error - The error caught while making HTTP request to Twitter API. */ public handle(error: unknown): void { - const axiosResponse = this.getAxiosResponse(error); + if (!axios.isAxiosError(error)) { + throw error; + } + + this.handleTimeoutError(error); + const axiosResponse = this.getAxiosResponse(error); this.handleApiError(axiosResponse); this.handleHttpError(axiosResponse); } + /** + * Handles exceeded timeout, configured in RettiwtConfig. + * + * @param error - The error object. + * @throws An error if the configured request timeout has been exceeded. + */ + protected handleTimeoutError(error: AxiosError): void { + if (error.code === 'ECONNABORTED') { + throw new TimeoutError(error.message); + } + } + /** * Retrieves the response data from the given error. * @@ -44,8 +62,8 @@ export class ErrorService implements IErrorHandler { * @returns The response data. * @throws The original error if it is not an HTTP error with a response. */ - protected getAxiosResponse(error: unknown): AxiosResponse { - if (axios.isAxiosError(error) && !!error.response) { + protected getAxiosResponse(error: AxiosError): AxiosResponse { + if (!!error.response) { return error.response; } diff --git a/src/services/internal/FetcherService.ts b/src/services/internal/FetcherService.ts index 11824fff..ee63f8e1 100644 --- a/src/services/internal/FetcherService.ts +++ b/src/services/internal/FetcherService.ts @@ -50,6 +50,9 @@ export class FetcherService { /** The HTTPS Agent to use for requests to Twitter API. */ private readonly httpsAgent: Agent; + /** The max wait time for a response; if not set, Twitter server timeout is used. */ + private readonly timeoutInMilliseconds: number; + /** The log service instance to use to logging. */ private readonly logger: LogService; @@ -74,6 +77,7 @@ export class FetcherService { } this.isAuthenticated = config?.apiKey ? true : false; this.httpsAgent = this.getHttpsAgent(config?.proxyUrl); + this.timeoutInMilliseconds = config?.timeoutInMilliseconds ?? 0; this.logger = new LogService(config?.logging); this.errorHandler = config?.errorHandler ?? new ErrorService(); } @@ -161,6 +165,7 @@ export class FetcherService { data: config.payload, headers: JSON.parse(JSON.stringify(this.cred.toHeader())) as AxiosRequestHeaders, httpsAgent: this.httpsAgent, + timeout: this.timeoutInMilliseconds, }; /** diff --git a/src/types/RettiwtConfig.ts b/src/types/RettiwtConfig.ts index 54434f88..ed4b5d0c 100644 --- a/src/types/RettiwtConfig.ts +++ b/src/types/RettiwtConfig.ts @@ -16,6 +16,9 @@ export interface IRettiwtConfig { /** Optional URL with proxy configuration to use for requests to Twitter API. */ proxyUrl?: URL; + /** The max wait time for a response; if not set, Twitter server timeout is used. */ + timeoutInMilliseconds?: number; + /** Whether to write logs to console or not. */ logging?: boolean; From 56902370312aef2cad7fbf9694a7a0493792d4c2 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Thu, 18 Jan 2024 22:29:32 +0530 Subject: [PATCH 13/39] Linted and formatted code --- src/index.ts | 14 +++++++------- src/models/{public => data}/CursoredData.ts | 0 src/models/{public => data}/List.ts | 0 src/models/{public => data}/Tweet.ts | 0 src/models/{public => data}/User.ts | 0 src/models/{public => }/errors/ApiError.ts | 0 src/models/{public => }/errors/HttpError.ts | 0 src/models/{public => }/errors/RettiwtError.ts | 0 src/models/{public => }/errors/TimeoutError.ts | 0 src/services/internal/ErrorService.ts | 8 ++++---- src/services/internal/FetcherService.ts | 14 +++++++------- src/services/public/TweetService.ts | 6 +++--- src/services/public/UserService.ts | 6 +++--- src/types/RettiwtConfig.ts | 4 ++-- 14 files changed, 26 insertions(+), 26 deletions(-) rename src/models/{public => data}/CursoredData.ts (100%) rename src/models/{public => data}/List.ts (100%) rename src/models/{public => data}/Tweet.ts (100%) rename src/models/{public => data}/User.ts (100%) rename src/models/{public => }/errors/ApiError.ts (100%) rename src/models/{public => }/errors/HttpError.ts (100%) rename src/models/{public => }/errors/RettiwtError.ts (100%) rename src/models/{public => }/errors/TimeoutError.ts (100%) diff --git a/src/index.ts b/src/index.ts index ba81a004..4fd016fa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,15 +10,15 @@ export * from './enums/Http'; export * from './enums/Logging'; // ERROR MODELS -export * from './models/internal/errors/ApiError'; -export * from './models/internal/errors/HttpError'; -export * from './models/internal/errors/RettiwtError'; +export * from './models/errors/ApiError'; +export * from './models/errors/HttpError'; +export * from './models/errors/RettiwtError'; // DATA MODELS -export * from './models/public/CursoredData'; -export * from './models/public/List'; -export * from './models/public/Tweet'; -export * from './models/public/User'; +export * from './models/data/CursoredData'; +export * from './models/data/List'; +export * from './models/data/Tweet'; +export * from './models/data/User'; // SERVICES export * from './services/internal/ErrorService'; diff --git a/src/models/public/CursoredData.ts b/src/models/data/CursoredData.ts similarity index 100% rename from src/models/public/CursoredData.ts rename to src/models/data/CursoredData.ts diff --git a/src/models/public/List.ts b/src/models/data/List.ts similarity index 100% rename from src/models/public/List.ts rename to src/models/data/List.ts diff --git a/src/models/public/Tweet.ts b/src/models/data/Tweet.ts similarity index 100% rename from src/models/public/Tweet.ts rename to src/models/data/Tweet.ts diff --git a/src/models/public/User.ts b/src/models/data/User.ts similarity index 100% rename from src/models/public/User.ts rename to src/models/data/User.ts diff --git a/src/models/public/errors/ApiError.ts b/src/models/errors/ApiError.ts similarity index 100% rename from src/models/public/errors/ApiError.ts rename to src/models/errors/ApiError.ts diff --git a/src/models/public/errors/HttpError.ts b/src/models/errors/HttpError.ts similarity index 100% rename from src/models/public/errors/HttpError.ts rename to src/models/errors/HttpError.ts diff --git a/src/models/public/errors/RettiwtError.ts b/src/models/errors/RettiwtError.ts similarity index 100% rename from src/models/public/errors/RettiwtError.ts rename to src/models/errors/RettiwtError.ts diff --git a/src/models/public/errors/TimeoutError.ts b/src/models/errors/TimeoutError.ts similarity index 100% rename from src/models/public/errors/TimeoutError.ts rename to src/models/errors/TimeoutError.ts diff --git a/src/services/internal/ErrorService.ts b/src/services/internal/ErrorService.ts index 3cbcc2b2..401966eb 100644 --- a/src/services/internal/ErrorService.ts +++ b/src/services/internal/ErrorService.ts @@ -11,9 +11,9 @@ import { EHttpStatus } from '../../enums/Http'; import { EErrorCodes } from 'rettiwt-core'; // ERRORS -import { ApiError } from '../../models/public/errors/ApiError'; -import { HttpError } from '../../models/public/errors/HttpError'; -import { TimeoutError } from '../../models/public/errors/TimeoutError'; +import { ApiError } from '../../models/errors/ApiError'; +import { HttpError } from '../../models/errors/HttpError'; +import { TimeoutError } from '../../models/errors/TimeoutError'; /** * The base service that handles any errors. @@ -63,7 +63,7 @@ export class ErrorService implements IErrorHandler { * @throws The original error if it is not an HTTP error with a response. */ protected getAxiosResponse(error: AxiosError): AxiosResponse { - if (!!error.response) { + if (error.response) { return error.response; } diff --git a/src/services/internal/FetcherService.ts b/src/services/internal/FetcherService.ts index ee63f8e1..3fe7a253 100644 --- a/src/services/internal/FetcherService.ts +++ b/src/services/internal/FetcherService.ts @@ -28,9 +28,9 @@ import { EApiErrors } from '../../enums/Api'; import { ELogActions } from '../../enums/Logging'; // MODELS -import { CursoredData } from '../../models/public/CursoredData'; -import { Tweet } from '../../models/public/Tweet'; -import { User } from '../../models/public/User'; +import { CursoredData } from '../../models/data/CursoredData'; +import { Tweet } from '../../models/data/Tweet'; +import { User } from '../../models/data/User'; // HELPERS import { findByFilter } from '../../helper/JsonUtils'; @@ -50,8 +50,8 @@ export class FetcherService { /** The HTTPS Agent to use for requests to Twitter API. */ private readonly httpsAgent: Agent; - /** The max wait time for a response; if not set, Twitter server timeout is used. */ - private readonly timeoutInMilliseconds: number; + /** The max wait time for a response. */ + private readonly timeout: number; /** The log service instance to use to logging. */ private readonly logger: LogService; @@ -77,7 +77,7 @@ export class FetcherService { } this.isAuthenticated = config?.apiKey ? true : false; this.httpsAgent = this.getHttpsAgent(config?.proxyUrl); - this.timeoutInMilliseconds = config?.timeoutInMilliseconds ?? 0; + this.timeout = config?.timeout ?? 0; this.logger = new LogService(config?.logging); this.errorHandler = config?.errorHandler ?? new ErrorService(); } @@ -165,7 +165,7 @@ export class FetcherService { data: config.payload, headers: JSON.parse(JSON.stringify(this.cred.toHeader())) as AxiosRequestHeaders, httpsAgent: this.httpsAgent, - timeout: this.timeoutInMilliseconds, + timeout: this.timeout, }; /** diff --git a/src/services/public/TweetService.ts b/src/services/public/TweetService.ts index 7dd70183..03d15c42 100644 --- a/src/services/public/TweetService.ts +++ b/src/services/public/TweetService.ts @@ -8,9 +8,9 @@ import { FetcherService } from '../internal/FetcherService'; import { IRettiwtConfig } from '../../types/RettiwtConfig'; // MODELS -import { Tweet } from '../../models/public/Tweet'; -import { User } from '../../models/public/User'; -import { CursoredData } from '../../models/public/CursoredData'; +import { Tweet } from '../../models/data/Tweet'; +import { User } from '../../models/data/User'; +import { CursoredData } from '../../models/data/CursoredData'; /** * Handles fetching of data related to tweets. diff --git a/src/services/public/UserService.ts b/src/services/public/UserService.ts index 65337b88..a7cf8ab6 100644 --- a/src/services/public/UserService.ts +++ b/src/services/public/UserService.ts @@ -8,11 +8,11 @@ import { FetcherService } from '../internal/FetcherService'; import { IRettiwtConfig } from '../../types/RettiwtConfig'; // MODELS -import { User } from '../../models/public/User'; -import { Tweet } from '../../models/public/Tweet'; +import { User } from '../../models/data/User'; +import { Tweet } from '../../models/data/Tweet'; // TYPES -import { CursoredData } from '../../models/public/CursoredData'; +import { CursoredData } from '../../models/data/CursoredData'; /** * Handles fetching of data related to user account diff --git a/src/types/RettiwtConfig.ts b/src/types/RettiwtConfig.ts index ed4b5d0c..83c7e2a7 100644 --- a/src/types/RettiwtConfig.ts +++ b/src/types/RettiwtConfig.ts @@ -16,8 +16,8 @@ export interface IRettiwtConfig { /** Optional URL with proxy configuration to use for requests to Twitter API. */ proxyUrl?: URL; - /** The max wait time for a response; if not set, Twitter server timeout is used. */ - timeoutInMilliseconds?: number; + /** The max wait time (in milli-seconds) for a response; if not set, Twitter server timeout is used. */ + timeout?: number; /** Whether to write logs to console or not. */ logging?: boolean; From 9660117b88fdf8630cad06e86e501441fc31acf9 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Thu, 18 Jan 2024 22:33:36 +0530 Subject: [PATCH 14/39] Added todo --- src/services/internal/ErrorService.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/services/internal/ErrorService.ts b/src/services/internal/ErrorService.ts index 401966eb..95244058 100644 --- a/src/services/internal/ErrorService.ts +++ b/src/services/internal/ErrorService.ts @@ -15,6 +15,8 @@ import { ApiError } from '../../models/errors/ApiError'; import { HttpError } from '../../models/errors/HttpError'; import { TimeoutError } from '../../models/errors/TimeoutError'; +// TODO Refactor and document this module + /** * The base service that handles any errors. * From 8a636d35c00164c7dd605375d70e660b6624e646 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Mon, 22 Jan 2024 19:45:45 +0530 Subject: [PATCH 15/39] Migrated to latest rettiwt-core and rettiwt-auth --- package.json | 6 ++--- src/commands/Tweet.ts | 2 +- src/services/internal/FetcherService.ts | 32 ++++++++++------------- src/services/public/TweetService.ts | 2 +- yarn.lock | 34 +++++++++++++------------ 5 files changed, 36 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index c4436221..9485b0e2 100644 --- a/package.json +++ b/package.json @@ -29,11 +29,11 @@ }, "homepage": "https://rishikant181.github.io/Rettiwt-API/", "dependencies": { - "axios": "1.3.2", + "axios": "1.6.3", "commander": "11.1.0", "https-proxy-agent": "7.0.2", - "rettiwt-auth": "2.0.0", - "rettiwt-core": "3.2.1" + "rettiwt-auth": "2.1.0-alpha.0", + "rettiwt-core": "3.3.0-alpha.2" }, "devDependencies": { "@types/node": "20.4.1", diff --git a/src/commands/Tweet.ts b/src/commands/Tweet.ts index 8531682e..c7fd5b36 100644 --- a/src/commands/Tweet.ts +++ b/src/commands/Tweet.ts @@ -152,7 +152,7 @@ class TweetSearchOptions { return new TweetFilter({ fromUsers: this.from ? this.from.split(';') : undefined, toUsers: this.to ? this.to.split(';') : undefined, - words: this.words ? this.words.split(';') : undefined, + includeWords: this.words ? this.words.split(';') : undefined, hashtags: this.hashtags ? this.hashtags.split(';') : undefined, startDate: this.start ? new Date(this.start) : undefined, endDate: this.end ? new Date(this.end) : undefined, diff --git a/src/services/internal/FetcherService.ts b/src/services/internal/FetcherService.ts index 3fe7a253..6fbf3060 100644 --- a/src/services/internal/FetcherService.ts +++ b/src/services/internal/FetcherService.ts @@ -1,7 +1,8 @@ // PACKAGES import { Request, - Args, + FetchArgs, + PostArgs, EResourceType, ICursor as IRawCursor, ITweet as IRawTweet, @@ -149,29 +150,22 @@ export class FetcherService { * @param config - The request configuration. * @returns The response received. */ - private async request(config: Request): Promise>> { + private async request(config: AxiosRequestConfig): Promise>> { // Checking authorization for the requested resource - this.checkAuthorization(config.endpoint); + this.checkAuthorization(config.url as EResourceType); // If not authenticated, use guest authentication this.cred = this.cred ?? (await new Auth().getGuestCredential()); - /** - * Creating Axios request configuration from the input configuration. - */ - const axiosRequest: AxiosRequestConfig = { - url: config.url, - method: config.type, - data: config.payload, - headers: JSON.parse(JSON.stringify(this.cred.toHeader())) as AxiosRequestHeaders, - httpsAgent: this.httpsAgent, - timeout: this.timeout, - }; + // Setting additional request parameters + config.headers = JSON.parse(JSON.stringify(this.cred.toHeader())) as AxiosRequestHeaders; + config.httpAgent = this.httpsAgent; + config.timeout = this.timeout; /** * If Axios request results in an error, catch it and rethrow a more specific error. */ - return await axios>(axiosRequest).catch((error: unknown) => { + return await axios>(config).catch((error: unknown) => { this.errorHandler.handle(error); throw error; @@ -278,13 +272,13 @@ export class FetcherService { */ protected async fetch( resourceType: EResourceType, - args: Args, + args: FetchArgs, ): Promise> { // Logging this.logger.log(ELogActions.FETCH, { resourceType: resourceType, args: args }); // Preparing the HTTP request - const request: Request = new Request(resourceType, args); + const request: AxiosRequestConfig = new Request(resourceType, args).toAxiosRequestConfig(); // Getting the raw data const res = await this.request(request).then((res) => res.data); @@ -305,12 +299,12 @@ export class FetcherService { * @param args - Resource specific arguments. * @returns Whether posting was successful or not. */ - protected async post(resourceType: EResourceType, args: Args): Promise { + protected async post(resourceType: EResourceType, args: PostArgs): Promise { // Logging this.logger.log(ELogActions.POST, { resourceType: resourceType, args: args }); // Preparing the HTTP request - const request: Request = new Request(resourceType, args); + const request: AxiosRequestConfig = new Request(resourceType, args).toAxiosRequestConfig(); // Posting the data await this.request(request); diff --git a/src/services/public/TweetService.ts b/src/services/public/TweetService.ts index 03d15c42..2c791a65 100644 --- a/src/services/public/TweetService.ts +++ b/src/services/public/TweetService.ts @@ -246,7 +246,7 @@ export class TweetService extends FetcherService { */ public async tweet(tweetText: string): Promise { // Posting the tweet - const data = await this.post(EResourceType.CREATE_TWEET, { tweetText: tweetText }); + const data = await this.post(EResourceType.CREATE_TWEET, { tweet: { text: tweetText } }); return data; } diff --git a/yarn.lock b/yarn.lock index 08c7cabc..93a7500a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -274,19 +274,19 @@ asynckit@^0.4.0: resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -axios@1.3.2: - version "1.3.2" - resolved "https://registry.npmjs.org/axios/-/axios-1.3.2.tgz" - integrity sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw== +axios@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" + integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" proxy-from-env "^1.1.0" -axios@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" - integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== +axios@1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.3.tgz#7f50f23b3aa246eff43c54834272346c396613f4" + integrity sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" @@ -1089,20 +1089,22 @@ resolve@~1.19.0: is-core-module "^2.1.0" path-parse "^1.0.6" -rettiwt-auth@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/rettiwt-auth/-/rettiwt-auth-2.0.0.tgz#e5eaa0cdb736acc52f9c1b6b31dae685f4d14073" - integrity sha512-zVEhc4Ce/5G6Rt53/nwCBnjgohmIBT+F7XMjgl0XjngwXFvUptSY5jISGzN6655teZBva92wAeupIkoSd0pxJg== +rettiwt-auth@2.1.0-alpha.0: + version "2.1.0-alpha.0" + resolved "https://registry.yarnpkg.com/rettiwt-auth/-/rettiwt-auth-2.1.0-alpha.0.tgz#0552e21a16dfad64dcc81407363faf8ff14718c6" + integrity sha512-KGu7FTbZT5KXXHF0HJhZMG99o2V97IGHfsj/82s49Yv7nE8arcOKB4WTteFWWV9EIkbpjlJU93EmVd9vQCOYPg== dependencies: axios "1.4.0" commander "11.1.0" cookiejar "2.1.4" + https-proxy-agent "7.0.2" -rettiwt-core@3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/rettiwt-core/-/rettiwt-core-3.2.1.tgz#de8015eff45ba1bf591360716956916a385e4782" - integrity sha512-cLkv+8/e48nxsHuplj2M8z/854jSihNm0DPK9V+xxeqdIz4vd/5fmdmfqe7Gl04sVENzC/w8MFGBU6Z8glXqvw== +rettiwt-core@3.3.0-alpha.2: + version "3.3.0-alpha.2" + resolved "https://registry.yarnpkg.com/rettiwt-core/-/rettiwt-core-3.3.0-alpha.2.tgz#f62481e8a42a57d4d08c393036d1caf53de2f1ac" + integrity sha512-zS8YcdRxtB3HnCDy3VwJ2yiluJoaaYuSUWlfbNsuTYBMLfzyZ88OJdbmf+ZiV9+Eke/4ZKjTkA78QwTfYOxiOg== dependencies: + axios "1.6.3" class-validator "0.14.0" reusify@^1.0.4: From fdf641bba9aad2256692507b0ce3676295f74e21 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Tue, 23 Jan 2024 12:46:00 +0530 Subject: [PATCH 16/39] Added more options to TweetSearchOptions --- .prettierrc | 2 +- src/commands/Tweet.ts | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/.prettierrc b/.prettierrc index d8a9682e..3fa6ea6f 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,7 +3,7 @@ "bracketSpacing": true, "endOfLine": "auto", "printWidth": 120, - "quoteProps": "consistent", + "quoteProps": "as-needed", "semi": true, "singleQuote": true, "tabWidth": 4, diff --git a/src/commands/Tweet.ts b/src/commands/Tweet.ts index c7fd5b36..c31890c9 100644 --- a/src/commands/Tweet.ts +++ b/src/commands/Tweet.ts @@ -122,10 +122,21 @@ function createTweetCommand(rettiwt: Rettiwt): Command { * @remarks The search options are implementations of the ones offered by {@link TweetFilter} */ class TweetSearchOptions { + /* eslint-disable @typescript-eslint/naming-convention */ public from?: string; public to?: string; public words?: string; + public phrase?: string; + public 'optional-words'?: string; + public 'exclude-words'?: string; public hashtags?: string; + public mentions?: string; + public 'min-replies'?: number; + public 'min-likes'?: number; + public 'min-retweets'?: number; + public quoted?: string; + public links?: boolean; + public replies?: boolean; public start?: string; public end?: string; @@ -138,7 +149,17 @@ class TweetSearchOptions { this.from = options?.from; this.to = options?.to; this.words = options?.words; + this.phrase = options?.phrase; + this['optional-words'] = options?.['optional-words']; + this['exclude-words'] = options?.['exclude-words']; this.hashtags = options?.hashtags; + this.mentions = options?.mentions; + this['min-replies'] = options?.['min-replies']; + this['min-likes'] = options?.['min-likes']; + this['min-retweets'] = options?.['min-retweets']; + this.quoted = options?.quoted; + this.links = options?.links; + this.replies = options?.replies; this.start = options?.start; this.end = options?.end; } @@ -153,7 +174,17 @@ class TweetSearchOptions { fromUsers: this.from ? this.from.split(';') : undefined, toUsers: this.to ? this.to.split(';') : undefined, includeWords: this.words ? this.words.split(';') : undefined, + includePhrase: this.phrase, + optionalWords: this['optional-words'] ? this['optional-words'].split(';') : undefined, + excludeWords: this['exclude-words'] ? this['exclude-words'].split(';') : undefined, hashtags: this.hashtags ? this.hashtags.split(';') : undefined, + mentions: this.mentions ? this.mentions.split(';') : undefined, + minReplies: this['min-replies'], + minLikes: this['min-likes'], + minRetweets: this['min-retweets'], + quoted: this.quoted, + links: this.links, + replies: this.replies, startDate: this.start ? new Date(this.start) : undefined, endDate: this.end ? new Date(this.end) : undefined, }); From 98aad0902d2cf21174180c624cb99b01dc34bf85 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Tue, 23 Jan 2024 13:03:57 +0530 Subject: [PATCH 17/39] Added more search options to search command --- src/commands/Tweet.ts | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/commands/Tweet.ts b/src/commands/Tweet.ts index c31890c9..72296fcc 100644 --- a/src/commands/Tweet.ts +++ b/src/commands/Tweet.ts @@ -35,7 +35,27 @@ function createTweetCommand(rettiwt: Rettiwt): Command { .option('-f, --from ', "Matches the tweets made by list of given users, separated by ';'") .option('-t, --to ', "Matches the tweets made to the list of given users, separated by ';'") .option('-w, --words ', "Matches the tweets containing the given list of words, separated by ';'") + .option('-p, --phrase ', 'Matches the tweets containing the exact phrase') + .option( + '--optional-words ', + "Matches the tweets containing any of the given list of words, separated by ';'", + ) + .option( + '--exclude-words ', + "Matches the tweets that do not contain any of the give list of words, separated by ';'", + ) .option('-h, --hashtags ', "Matches the tweets containing the given list of hashtags, separated by ';'") + .option( + '-m', + '--mentions ', + "Matches the tweets that mention the give list of usernames, separated by ';'", + ) + .option('-r, --min-replies ', 'Matches the tweets that have a minimum of given number of replies') + .option('-l, --min-likes ', 'Matches the tweets that have a minimum of given number of likes') + .option('-x, --min-retweets ', 'Matches the tweets that have a minimum of given number of retweets') + .option('-q, --quoted ', 'Matches the tweets that quote the tweet with the given id') + .option('--exclude-links', 'Matches tweets that do not contain links') + .option('--exclude-replies', 'Matches the tweets that are not replies') .option('-s, --start ', 'Matches the tweets made since the given date (valid date string)') .option('-e, --end ', 'Matches the tweets made upto the given date (valid date string)') .action(async (count?: string, cursor?: string, options?: TweetSearchOptions) => { @@ -135,8 +155,8 @@ class TweetSearchOptions { public 'min-likes'?: number; public 'min-retweets'?: number; public quoted?: string; - public links?: boolean; - public replies?: boolean; + public 'exclude-links'?: boolean; + public 'exclude-replies'?: boolean; public start?: string; public end?: string; @@ -158,8 +178,8 @@ class TweetSearchOptions { this['min-likes'] = options?.['min-likes']; this['min-retweets'] = options?.['min-retweets']; this.quoted = options?.quoted; - this.links = options?.links; - this.replies = options?.replies; + this['exclude-links'] = options?.['exclude-links']; + this['exclude-replies'] = options?.['exclude-replies']; this.start = options?.start; this.end = options?.end; } @@ -183,8 +203,8 @@ class TweetSearchOptions { minLikes: this['min-likes'], minRetweets: this['min-retweets'], quoted: this.quoted, - links: this.links, - replies: this.replies, + links: !this['exclude-links'], + replies: !this['exclude-replies'], startDate: this.start ? new Date(this.start) : undefined, endDate: this.end ? new Date(this.end) : undefined, }); From a48c627a92f8644eb6b67072a21b638f77457fdf Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Tue, 23 Jan 2024 13:17:13 +0530 Subject: [PATCH 18/39] Fixed a bug where mentions of cli didn't work --- src/commands/Tweet.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/commands/Tweet.ts b/src/commands/Tweet.ts index 72296fcc..9f7f75c2 100644 --- a/src/commands/Tweet.ts +++ b/src/commands/Tweet.ts @@ -46,8 +46,7 @@ function createTweetCommand(rettiwt: Rettiwt): Command { ) .option('-h, --hashtags ', "Matches the tweets containing the given list of hashtags, separated by ';'") .option( - '-m', - '--mentions ', + '-m, --mentions ', "Matches the tweets that mention the give list of usernames, separated by ';'", ) .option('-r, --min-replies ', 'Matches the tweets that have a minimum of given number of replies') From fe62ecfab70eebab95a5654d505f950469298fb1 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Tue, 23 Jan 2024 13:52:24 +0530 Subject: [PATCH 19/39] Fixed incorrect option parsing for tweet search --- src/commands/Tweet.ts | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/commands/Tweet.ts b/src/commands/Tweet.ts index 9f7f75c2..55fe2d75 100644 --- a/src/commands/Tweet.ts +++ b/src/commands/Tweet.ts @@ -141,21 +141,20 @@ function createTweetCommand(rettiwt: Rettiwt): Command { * @remarks The search options are implementations of the ones offered by {@link TweetFilter} */ class TweetSearchOptions { - /* eslint-disable @typescript-eslint/naming-convention */ public from?: string; public to?: string; public words?: string; public phrase?: string; - public 'optional-words'?: string; - public 'exclude-words'?: string; + public optionalWords?: string; + public excludeWords?: string; public hashtags?: string; public mentions?: string; - public 'min-replies'?: number; - public 'min-likes'?: number; - public 'min-retweets'?: number; + public minReplies?: number; + public minLikes?: number; + public minRetweets?: number; public quoted?: string; - public 'exclude-links'?: boolean; - public 'exclude-replies'?: boolean; + public excludeLinks?: boolean = false; + public excludeReplies?: boolean = false; public start?: string; public end?: string; @@ -169,16 +168,16 @@ class TweetSearchOptions { this.to = options?.to; this.words = options?.words; this.phrase = options?.phrase; - this['optional-words'] = options?.['optional-words']; - this['exclude-words'] = options?.['exclude-words']; + this.optionalWords = options?.optionalWords; + this.excludeWords = options?.excludeWords; this.hashtags = options?.hashtags; this.mentions = options?.mentions; - this['min-replies'] = options?.['min-replies']; - this['min-likes'] = options?.['min-likes']; - this['min-retweets'] = options?.['min-retweets']; + this.minReplies = options?.minReplies; + this.minLikes = options?.minLikes; + this.minRetweets = options?.minRetweets; this.quoted = options?.quoted; - this['exclude-links'] = options?.['exclude-links']; - this['exclude-replies'] = options?.['exclude-replies']; + this.excludeLinks = options?.excludeLinks; + this.excludeReplies = options?.excludeReplies; this.start = options?.start; this.end = options?.end; } @@ -194,16 +193,16 @@ class TweetSearchOptions { toUsers: this.to ? this.to.split(';') : undefined, includeWords: this.words ? this.words.split(';') : undefined, includePhrase: this.phrase, - optionalWords: this['optional-words'] ? this['optional-words'].split(';') : undefined, - excludeWords: this['exclude-words'] ? this['exclude-words'].split(';') : undefined, + optionalWords: this.optionalWords ? this.optionalWords.split(';') : undefined, + excludeWords: this.excludeWords ? this.excludeWords.split(';') : undefined, hashtags: this.hashtags ? this.hashtags.split(';') : undefined, mentions: this.mentions ? this.mentions.split(';') : undefined, - minReplies: this['min-replies'], - minLikes: this['min-likes'], - minRetweets: this['min-retweets'], + minReplies: this.minReplies, + minLikes: this.minLikes, + minRetweets: this.minRetweets, quoted: this.quoted, - links: !this['exclude-links'], - replies: !this['exclude-replies'], + links: !this.excludeLinks, + replies: !this.excludeReplies, startDate: this.start ? new Date(this.start) : undefined, endDate: this.end ? new Date(this.end) : undefined, }); From eb09dcc251a74449645f8e250d989ecb83578d8d Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Tue, 23 Jan 2024 16:06:39 +0530 Subject: [PATCH 20/39] Fixed an issue where the proxyUrl given was not used for authentication --- src/services/internal/FetcherService.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/services/internal/FetcherService.ts b/src/services/internal/FetcherService.ts index 6fbf3060..025dd67a 100644 --- a/src/services/internal/FetcherService.ts +++ b/src/services/internal/FetcherService.ts @@ -48,6 +48,9 @@ export class FetcherService { /** Whether the instance is authenticated or not. */ private readonly isAuthenticated: boolean; + /** The URL to the proxy server to use. */ + private readonly proxyUrl?: URL; + /** The HTTPS Agent to use for requests to Twitter API. */ private readonly httpsAgent: Agent; @@ -77,6 +80,7 @@ export class FetcherService { this.cred = undefined; } this.isAuthenticated = config?.apiKey ? true : false; + this.proxyUrl = config?.proxyUrl; this.httpsAgent = this.getHttpsAgent(config?.proxyUrl); this.timeout = config?.timeout ?? 0; this.logger = new LogService(config?.logging); @@ -155,7 +159,7 @@ export class FetcherService { this.checkAuthorization(config.url as EResourceType); // If not authenticated, use guest authentication - this.cred = this.cred ?? (await new Auth().getGuestCredential()); + this.cred = this.cred ?? (await new Auth({ proxyUrl: this.proxyUrl }).getGuestCredential()); // Setting additional request parameters config.headers = JSON.parse(JSON.stringify(this.cred.toHeader())) as AxiosRequestHeaders; From 1ba3aa1e5c59f13da7ee1a1569284dcc56393779 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Tue, 23 Jan 2024 16:24:56 +0530 Subject: [PATCH 21/39] Added functionality to use a different proxy for authentcation and normal operation --- src/services/internal/FetcherService.ts | 8 ++++---- src/types/RettiwtConfig.ts | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/services/internal/FetcherService.ts b/src/services/internal/FetcherService.ts index 025dd67a..2e60dbcf 100644 --- a/src/services/internal/FetcherService.ts +++ b/src/services/internal/FetcherService.ts @@ -48,8 +48,8 @@ export class FetcherService { /** Whether the instance is authenticated or not. */ private readonly isAuthenticated: boolean; - /** The URL to the proxy server to use. */ - private readonly proxyUrl?: URL; + /** The URL to the proxy server to use for authentication. */ + private readonly authProxyUrl?: URL; /** The HTTPS Agent to use for requests to Twitter API. */ private readonly httpsAgent: Agent; @@ -80,7 +80,7 @@ export class FetcherService { this.cred = undefined; } this.isAuthenticated = config?.apiKey ? true : false; - this.proxyUrl = config?.proxyUrl; + this.authProxyUrl = config?.authProxyUrl ?? config?.proxyUrl; this.httpsAgent = this.getHttpsAgent(config?.proxyUrl); this.timeout = config?.timeout ?? 0; this.logger = new LogService(config?.logging); @@ -159,7 +159,7 @@ export class FetcherService { this.checkAuthorization(config.url as EResourceType); // If not authenticated, use guest authentication - this.cred = this.cred ?? (await new Auth({ proxyUrl: this.proxyUrl }).getGuestCredential()); + this.cred = this.cred ?? (await new Auth({ proxyUrl: this.authProxyUrl }).getGuestCredential()); // Setting additional request parameters config.headers = JSON.parse(JSON.stringify(this.cred.toHeader())) as AxiosRequestHeaders; diff --git a/src/types/RettiwtConfig.ts b/src/types/RettiwtConfig.ts index 83c7e2a7..0cd0185a 100644 --- a/src/types/RettiwtConfig.ts +++ b/src/types/RettiwtConfig.ts @@ -13,9 +13,22 @@ export interface IRettiwtConfig { /** The guestKey (guest token) to use for guest access to Twitter API. */ guestKey?: string; - /** Optional URL with proxy configuration to use for requests to Twitter API. */ + /** + * Optional URL to proxy server to use for requests to Twitter API. + * + * @remarks When deploying to cloud platforms, if setting {@link IRettiwtConfig.authProxyUrl} does not resolve Error 429, then this might be required. + */ proxyUrl?: URL; + /** + * Optional URL to proxy server to use for authentication against Twitter API. + * + * @remarks Required when deploying to cloud platforms to bypass Error 429. + * + * @defaultValue Same as {@link IRettiwtConfig.proxyUrl} + */ + authProxyUrl?: URL; + /** The max wait time (in milli-seconds) for a response; if not set, Twitter server timeout is used. */ timeout?: number; From 41358cb6b80bd738750c3bb02094eaa3e51b5625 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Tue, 23 Jan 2024 22:16:37 +0530 Subject: [PATCH 22/39] Added Media model --- src/models/data/Media.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/models/data/Media.ts diff --git a/src/models/data/Media.ts b/src/models/data/Media.ts new file mode 100644 index 00000000..117e94e3 --- /dev/null +++ b/src/models/data/Media.ts @@ -0,0 +1,19 @@ +// PACKAGES +import { IMediaUploadInitializeResponse } from 'rettiwt-core'; + +/** + * The details of a single media file. + * + * @public + */ +export class Media { + /** The id of the media. */ + public id: string; + + /** + * @param media - The raw media data. + */ + public constructor(media: IMediaUploadInitializeResponse) { + this.id = media.media_id_string; + } +} From 86b603827c94b32931711dc905d61f55f6bc8d5a Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Tue, 23 Jan 2024 22:18:18 +0530 Subject: [PATCH 23/39] Updated rettiwt-core --- package.json | 2 +- yarn.lock | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 9485b0e2..b55faa33 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "commander": "11.1.0", "https-proxy-agent": "7.0.2", "rettiwt-auth": "2.1.0-alpha.0", - "rettiwt-core": "3.3.0-alpha.2" + "rettiwt-core": "3.3.0-alpha.3" }, "devDependencies": { "@types/node": "20.4.1", diff --git a/yarn.lock b/yarn.lock index 93a7500a..21ea3170 100644 --- a/yarn.lock +++ b/yarn.lock @@ -632,7 +632,7 @@ follow-redirects@^1.15.0: resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== -form-data@^4.0.0: +form-data@4.0.0, form-data@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== @@ -1099,13 +1099,14 @@ rettiwt-auth@2.1.0-alpha.0: cookiejar "2.1.4" https-proxy-agent "7.0.2" -rettiwt-core@3.3.0-alpha.2: - version "3.3.0-alpha.2" - resolved "https://registry.yarnpkg.com/rettiwt-core/-/rettiwt-core-3.3.0-alpha.2.tgz#f62481e8a42a57d4d08c393036d1caf53de2f1ac" - integrity sha512-zS8YcdRxtB3HnCDy3VwJ2yiluJoaaYuSUWlfbNsuTYBMLfzyZ88OJdbmf+ZiV9+Eke/4ZKjTkA78QwTfYOxiOg== +rettiwt-core@3.3.0-alpha.3: + version "3.3.0-alpha.3" + resolved "https://registry.yarnpkg.com/rettiwt-core/-/rettiwt-core-3.3.0-alpha.3.tgz#a581bb60c4b06863b3aec2e31bcaf878c26b78cb" + integrity sha512-YSiQ057FdVL+QvyTtV7KjuHu3ug63VMsdMhgTau50US4dR+6H4MLUv6XdMJL4VCooGj0tRDNlVWOBhi5NWAbyQ== dependencies: axios "1.6.3" class-validator "0.14.0" + form-data "4.0.0" reusify@^1.0.4: version "1.0.4" From c892a3a48eb826439ba8db6d111ab6f187bb45bf Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Wed, 24 Jan 2024 00:08:33 +0530 Subject: [PATCH 24/39] Added method to upload media --- src/services/internal/FetcherService.ts | 38 +++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/services/internal/FetcherService.ts b/src/services/internal/FetcherService.ts index 2e60dbcf..531331b2 100644 --- a/src/services/internal/FetcherService.ts +++ b/src/services/internal/FetcherService.ts @@ -10,6 +10,8 @@ import { ITimelineTweet, ITimelineUser, IResponse, + EUploadSteps, + IMediaUploadInitializeResponse, } from 'rettiwt-core'; import axios, { AxiosRequestConfig, AxiosRequestHeaders, AxiosResponse } from 'axios'; import https, { Agent } from 'https'; @@ -35,6 +37,7 @@ import { User } from '../../models/data/User'; // HELPERS import { findByFilter } from '../../helper/JsonUtils'; +import { statSync } from 'fs'; /** * The base service that handles all HTTP requests. @@ -154,7 +157,7 @@ export class FetcherService { * @param config - The request configuration. * @returns The response received. */ - private async request(config: AxiosRequestConfig): Promise>> { + private async request>(config: AxiosRequestConfig): Promise> { // Checking authorization for the requested resource this.checkAuthorization(config.url as EResourceType); @@ -169,7 +172,7 @@ export class FetcherService { /** * If Axios request results in an error, catch it and rethrow a more specific error. */ - return await axios>(config).catch((error: unknown) => { + return await axios(config).catch((error: unknown) => { this.errorHandler.handle(error); throw error; @@ -315,4 +318,35 @@ export class FetcherService { return true; } + + /** + * Uploads the given media file to Twitter + * + * @param media - The path to the media file to upload. + * @returns The id of the uploaded media. + */ + protected async upload(media: string): Promise { + // Logging + this.logger.log(ELogActions.POST, { resourceType: EResourceType.MEDIA_UPLOAD }); + + // Initializing upload + let request: AxiosRequestConfig = new Request(EResourceType.MEDIA_UPLOAD, { + upload: { step: EUploadSteps.INITIALIZE, size: statSync(media).size }, + }).toAxiosRequestConfig(); + const id: string = (await this.request(request)).data.media_id_string; + + // Uploading the media + request = new Request(EResourceType.MEDIA_UPLOAD, { + upload: { step: EUploadSteps.APPEND, id: id, media: media }, + }).toAxiosRequestConfig(); + await this.request(request); + + // Finalizing the upload + request = new Request(EResourceType.MEDIA_UPLOAD, { + upload: { step: EUploadSteps.FINALIZE, id: id }, + }).toAxiosRequestConfig(); + await this.request(request); + + return id; + } } From 91fb99ee7e7346cd982a52719061e6b005a8bdf3 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Thu, 25 Jan 2024 12:18:18 +0530 Subject: [PATCH 25/39] Fixed media upload method --- package.json | 4 +- src/enums/Logging.ts | 1 + src/services/internal/FetcherService.ts | 61 +++++++++++++++---------- yarn.lock | 27 ++++------- 4 files changed, 50 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index b55faa33..676bd963 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,8 @@ "axios": "1.6.3", "commander": "11.1.0", "https-proxy-agent": "7.0.2", - "rettiwt-auth": "2.1.0-alpha.0", - "rettiwt-core": "3.3.0-alpha.3" + "rettiwt-auth": "2.1.0-alpha.1", + "rettiwt-core": "3.3.0-alpha.5" }, "devDependencies": { "@types/node": "20.4.1", diff --git a/src/enums/Logging.ts b/src/enums/Logging.ts index a03d84fb..d997023f 100644 --- a/src/enums/Logging.ts +++ b/src/enums/Logging.ts @@ -6,6 +6,7 @@ export enum ELogActions { FETCH = 'FETCH', POST = 'POST', + UPLOAD = 'UPLOAD', EXTRACT = 'EXTRACT', DESERIALIZE = 'DESERIALIZE', AUTHORIZATION = 'AUTHORIZATION', diff --git a/src/services/internal/FetcherService.ts b/src/services/internal/FetcherService.ts index 531331b2..5a131c50 100644 --- a/src/services/internal/FetcherService.ts +++ b/src/services/internal/FetcherService.ts @@ -13,7 +13,7 @@ import { EUploadSteps, IMediaUploadInitializeResponse, } from 'rettiwt-core'; -import axios, { AxiosRequestConfig, AxiosRequestHeaders, AxiosResponse } from 'axios'; +import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'; import https, { Agent } from 'https'; import { AuthCredential, Auth } from 'rettiwt-auth'; import { HttpsProxyAgent } from 'https-proxy-agent'; @@ -154,10 +154,11 @@ export class FetcherService { /** * Makes an HTTP request according to the given parameters. * + * @typeParam ResType - The type of the returned response data. * @param config - The request configuration. * @returns The response received. */ - private async request>(config: AxiosRequestConfig): Promise> { + private async request(config: AxiosRequestConfig): Promise> { // Checking authorization for the requested resource this.checkAuthorization(config.url as EResourceType); @@ -165,7 +166,7 @@ export class FetcherService { this.cred = this.cred ?? (await new Auth({ proxyUrl: this.authProxyUrl }).getGuestCredential()); // Setting additional request parameters - config.headers = JSON.parse(JSON.stringify(this.cred.toHeader())) as AxiosRequestHeaders; + config.headers = { ...config.headers, ...this.cred.toHeader() }; config.httpAgent = this.httpsAgent; config.timeout = this.timeout; @@ -288,7 +289,7 @@ export class FetcherService { const request: AxiosRequestConfig = new Request(resourceType, args).toAxiosRequestConfig(); // Getting the raw data - const res = await this.request(request).then((res) => res.data); + const res = await this.request>(request).then((res) => res.data); // Extracting data const extractedData = this.extractData(res, resourceType); @@ -314,7 +315,7 @@ export class FetcherService { const request: AxiosRequestConfig = new Request(resourceType, args).toAxiosRequestConfig(); // Posting the data - await this.request(request); + await this.request(request); return true; } @@ -326,26 +327,40 @@ export class FetcherService { * @returns The id of the uploaded media. */ protected async upload(media: string): Promise { + // INITIALIZE + // Logging - this.logger.log(ELogActions.POST, { resourceType: EResourceType.MEDIA_UPLOAD }); - - // Initializing upload - let request: AxiosRequestConfig = new Request(EResourceType.MEDIA_UPLOAD, { - upload: { step: EUploadSteps.INITIALIZE, size: statSync(media).size }, - }).toAxiosRequestConfig(); - const id: string = (await this.request(request)).data.media_id_string; - - // Uploading the media - request = new Request(EResourceType.MEDIA_UPLOAD, { - upload: { step: EUploadSteps.APPEND, id: id, media: media }, - }).toAxiosRequestConfig(); - await this.request(request); + this.logger.log(ELogActions.UPLOAD, { step: EUploadSteps.INITIALIZE }); - // Finalizing the upload - request = new Request(EResourceType.MEDIA_UPLOAD, { - upload: { step: EUploadSteps.FINALIZE, id: id }, - }).toAxiosRequestConfig(); - await this.request(request); + const id: string = ( + await this.request( + new Request(EResourceType.MEDIA_UPLOAD, { + upload: { step: EUploadSteps.INITIALIZE, size: statSync(media).size }, + }).toAxiosRequestConfig(), + ) + ).data.media_id_string; + + // APPEND + + // Logging + this.logger.log(ELogActions.UPLOAD, { step: EUploadSteps.APPEND }); + + await this.request( + new Request(EResourceType.MEDIA_UPLOAD, { + upload: { step: EUploadSteps.APPEND, id: id, media: media }, + }).toAxiosRequestConfig(), + ); + + // FINALIZE + + // Logging + this.logger.log(ELogActions.UPLOAD, { step: EUploadSteps.APPEND }); + + await this.request( + new Request(EResourceType.MEDIA_UPLOAD, { + upload: { step: EUploadSteps.FINALIZE, id: id }, + }).toAxiosRequestConfig(), + ); return id; } diff --git a/yarn.lock b/yarn.lock index 21ea3170..1161c0b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -274,15 +274,6 @@ asynckit@^0.4.0: resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -axios@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" - integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== - dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - axios@1.6.3: version "1.6.3" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.3.tgz#7f50f23b3aa246eff43c54834272346c396613f4" @@ -1089,20 +1080,20 @@ resolve@~1.19.0: is-core-module "^2.1.0" path-parse "^1.0.6" -rettiwt-auth@2.1.0-alpha.0: - version "2.1.0-alpha.0" - resolved "https://registry.yarnpkg.com/rettiwt-auth/-/rettiwt-auth-2.1.0-alpha.0.tgz#0552e21a16dfad64dcc81407363faf8ff14718c6" - integrity sha512-KGu7FTbZT5KXXHF0HJhZMG99o2V97IGHfsj/82s49Yv7nE8arcOKB4WTteFWWV9EIkbpjlJU93EmVd9vQCOYPg== +rettiwt-auth@2.1.0-alpha.1: + version "2.1.0-alpha.1" + resolved "https://registry.yarnpkg.com/rettiwt-auth/-/rettiwt-auth-2.1.0-alpha.1.tgz#4277f54828c6379184e93d0cd63bf97ec9908dca" + integrity sha512-/GJWsOCB7fytno0XJyP7rUdZ2hdNDd9qr3aUs/P9qDwPH0oq1TOC//X2Rycs6tjMb2uR5WmLRbR9GPSJp4zB2A== dependencies: - axios "1.4.0" + axios "1.6.3" commander "11.1.0" cookiejar "2.1.4" https-proxy-agent "7.0.2" -rettiwt-core@3.3.0-alpha.3: - version "3.3.0-alpha.3" - resolved "https://registry.yarnpkg.com/rettiwt-core/-/rettiwt-core-3.3.0-alpha.3.tgz#a581bb60c4b06863b3aec2e31bcaf878c26b78cb" - integrity sha512-YSiQ057FdVL+QvyTtV7KjuHu3ug63VMsdMhgTau50US4dR+6H4MLUv6XdMJL4VCooGj0tRDNlVWOBhi5NWAbyQ== +rettiwt-core@3.3.0-alpha.5: + version "3.3.0-alpha.5" + resolved "https://registry.yarnpkg.com/rettiwt-core/-/rettiwt-core-3.3.0-alpha.5.tgz#d3e6e55f48369645e2ab109f6e61b55d23a7a6b8" + integrity sha512-PUIm1eIRvAZM2RoA4bRVPSmEgcUTD3ERitd84rbLAO0/lA4dg75rvBgtPnFhpJ/EquakfQgNZ9pvOhIZGGyj4A== dependencies: axios "1.6.3" class-validator "0.14.0" From 284c43c0cc42be6cd6f4e909b81d63fd44aec5f0 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Thu, 25 Jan 2024 12:36:15 +0530 Subject: [PATCH 26/39] Added TweetArgs --- src/types/args/TweetArgs.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/types/args/TweetArgs.ts diff --git a/src/types/args/TweetArgs.ts b/src/types/args/TweetArgs.ts new file mode 100644 index 00000000..91765d46 --- /dev/null +++ b/src/types/args/TweetArgs.ts @@ -0,0 +1,25 @@ +/** + * The arguments for posting a single tweet. + * + * @public + */ +export interface ITweetArgs { + /** The text to post. */ + text: string; + + /** The list of media to post. */ + media: IMediaArgs[]; +} + +/** + * The arguements specifying the media to be posted in a single tweet. + * + * @public + */ +export interface IMediaArgs { + /** The path to the media file */ + path: string; + + /** The list usernames of users to be tagged in the media. */ + tags: string[]; +} From b3ba084d61f465dd235252239edd469e7720469d Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Thu, 25 Jan 2024 17:39:50 +0530 Subject: [PATCH 27/39] tweet method of TweetService can now post media items --- src/index.ts | 1 + src/services/public/TweetService.ts | 23 +++++++++++++++++++---- src/types/args/TweetArgs.ts | 25 ------------------------- src/types/args/TweetMediaArgs.ts | 12 ++++++++++++ 4 files changed, 32 insertions(+), 29 deletions(-) delete mode 100644 src/types/args/TweetArgs.ts create mode 100644 src/types/args/TweetMediaArgs.ts diff --git a/src/index.ts b/src/index.ts index 4fd016fa..a4793792 100644 --- a/src/index.ts +++ b/src/index.ts @@ -28,5 +28,6 @@ export * from './services/public/TweetService'; export * from './services/public/UserService'; // TYPES +export * from './types/args/TweetMediaArgs'; export * from './types/RettiwtConfig'; export * from './types/ErrorHandler'; diff --git a/src/services/public/TweetService.ts b/src/services/public/TweetService.ts index 2c791a65..c7de9734 100644 --- a/src/services/public/TweetService.ts +++ b/src/services/public/TweetService.ts @@ -1,5 +1,5 @@ // PACKAGES -import { EResourceType, TweetFilter } from 'rettiwt-core'; +import { EResourceType, MediaArgs, TweetFilter } from 'rettiwt-core'; // SERVICES import { FetcherService } from '../internal/FetcherService'; @@ -11,6 +11,7 @@ import { IRettiwtConfig } from '../../types/RettiwtConfig'; import { Tweet } from '../../models/data/Tweet'; import { User } from '../../models/data/User'; import { CursoredData } from '../../models/data/CursoredData'; +import { ITweetMediaArgs } from '../../types/args/TweetMediaArgs'; /** * Handles fetching of data related to tweets. @@ -222,7 +223,7 @@ export class TweetService extends FetcherService { /** * Post a tweet. * - * @param tweetText - The text to be posted, length must be \<= 280 characters. + * @param text - The text to be posted, length must be \<= 280 characters. * @returns Whether posting was successful or not. * * @example @@ -244,9 +245,23 @@ export class TweetService extends FetcherService { * * @public */ - public async tweet(tweetText: string): Promise { + public async tweet(text: string, media?: ITweetMediaArgs[]): Promise { + /** Stores the list of media that has been uploaded */ + const uploadedMedia: MediaArgs[] = []; + + // If tweet includes media, upload the media items + if (media) { + for (const item of media) { + // Uploading the media item and getting it's allocated id + const id: string = await this.upload(item.path); + + // Storing the uploaded media item + uploadedMedia.push({ id: id, tags: item.tags }); + } + } + // Posting the tweet - const data = await this.post(EResourceType.CREATE_TWEET, { tweet: { text: tweetText } }); + const data = await this.post(EResourceType.CREATE_TWEET, { tweet: { text: text, media: uploadedMedia } }); return data; } diff --git a/src/types/args/TweetArgs.ts b/src/types/args/TweetArgs.ts deleted file mode 100644 index 91765d46..00000000 --- a/src/types/args/TweetArgs.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * The arguments for posting a single tweet. - * - * @public - */ -export interface ITweetArgs { - /** The text to post. */ - text: string; - - /** The list of media to post. */ - media: IMediaArgs[]; -} - -/** - * The arguements specifying the media to be posted in a single tweet. - * - * @public - */ -export interface IMediaArgs { - /** The path to the media file */ - path: string; - - /** The list usernames of users to be tagged in the media. */ - tags: string[]; -} diff --git a/src/types/args/TweetMediaArgs.ts b/src/types/args/TweetMediaArgs.ts new file mode 100644 index 00000000..fc318838 --- /dev/null +++ b/src/types/args/TweetMediaArgs.ts @@ -0,0 +1,12 @@ +/** + * The arguments specifying the media to be posted in a single tweet. + * + * @public + */ +export interface ITweetMediaArgs { + /** The path to the media file */ + path: string; + + /** The list usernames of users to be tagged in the media. */ + tags?: string[]; +} From b885b440d1443f5cc74b2488f94f792a1eb264b1 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Thu, 25 Jan 2024 18:00:36 +0530 Subject: [PATCH 28/39] Added docs for max size of media that can be posted --- package.json | 2 +- src/types/args/TweetMediaArgs.ts | 6 +++++- yarn.lock | 8 ++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 676bd963..3f49be19 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "commander": "11.1.0", "https-proxy-agent": "7.0.2", "rettiwt-auth": "2.1.0-alpha.1", - "rettiwt-core": "3.3.0-alpha.5" + "rettiwt-core": "3.3.0-alpha.6" }, "devDependencies": { "@types/node": "20.4.1", diff --git a/src/types/args/TweetMediaArgs.ts b/src/types/args/TweetMediaArgs.ts index fc318838..d9e1621b 100644 --- a/src/types/args/TweetMediaArgs.ts +++ b/src/types/args/TweetMediaArgs.ts @@ -4,7 +4,11 @@ * @public */ export interface ITweetMediaArgs { - /** The path to the media file */ + /** + * The path to the media file. + * + * @remarks The size of the media file must be \<= 5242880 bytes. + */ path: string; /** The list usernames of users to be tagged in the media. */ diff --git a/yarn.lock b/yarn.lock index 1161c0b0..33aa4947 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1090,10 +1090,10 @@ rettiwt-auth@2.1.0-alpha.1: cookiejar "2.1.4" https-proxy-agent "7.0.2" -rettiwt-core@3.3.0-alpha.5: - version "3.3.0-alpha.5" - resolved "https://registry.yarnpkg.com/rettiwt-core/-/rettiwt-core-3.3.0-alpha.5.tgz#d3e6e55f48369645e2ab109f6e61b55d23a7a6b8" - integrity sha512-PUIm1eIRvAZM2RoA4bRVPSmEgcUTD3ERitd84rbLAO0/lA4dg75rvBgtPnFhpJ/EquakfQgNZ9pvOhIZGGyj4A== +rettiwt-core@3.3.0-alpha.6: + version "3.3.0-alpha.6" + resolved "https://registry.yarnpkg.com/rettiwt-core/-/rettiwt-core-3.3.0-alpha.6.tgz#0d1c03b39c1c15d6ef562892085d697893032d63" + integrity sha512-nljy/IpXko4/7c4hv+30x8b/zp+HQft5Nyx5wnRBhuCJyiL4Xzx6GbltVSBZnUeVznnquqiaWvYqEwTmsUrfIg== dependencies: axios "1.6.3" class-validator "0.14.0" From 3097f0310e3a571e85f5a38fac4ae488c37af16e Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Thu, 25 Jan 2024 18:38:57 +0530 Subject: [PATCH 29/39] Updated docs --- src/services/public/TweetService.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/services/public/TweetService.ts b/src/services/public/TweetService.ts index c7de9734..a3241b2c 100644 --- a/src/services/public/TweetService.ts +++ b/src/services/public/TweetService.ts @@ -224,9 +224,10 @@ export class TweetService extends FetcherService { * Post a tweet. * * @param text - The text to be posted, length must be \<= 280 characters. + * @param media - The list of media to post in the tweet. * @returns Whether posting was successful or not. * - * @example + * @example Posting a simple text * ``` * import { Rettiwt } from 'rettiwt-api'; * @@ -243,6 +244,23 @@ export class TweetService extends FetcherService { * }); * ``` * + * @example Postring a tweet with an image + * ``` + * import { Rettiwt } from 'rettiwt-api'; + * + * // Creating a new Rettiwt instance using the given 'API_KEY' + * const rettiwt = new Rettiwt({ apiKey: API_KEY }); + * + * // Posting a tweet, containing an image called 'mountains.jpg', to twitter + * rettiwt.tweet.tweet('What a nice view!', [{ path: 'mountains.jpg' }]) + * .then(res => { + * console.log(res); + * }) + * .catch(err => { + * console.log(err); + * }); + * ``` + * * @public */ public async tweet(text: string, media?: ITweetMediaArgs[]): Promise { From e4a2d86e696e2bbda7e90bb58df885ad1eebbc05 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Thu, 25 Jan 2024 18:46:18 +0530 Subject: [PATCH 30/39] Fixed a typo --- src/services/public/TweetService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/public/TweetService.ts b/src/services/public/TweetService.ts index a3241b2c..6ecde2ee 100644 --- a/src/services/public/TweetService.ts +++ b/src/services/public/TweetService.ts @@ -244,7 +244,7 @@ export class TweetService extends FetcherService { * }); * ``` * - * @example Postring a tweet with an image + * @example Posting a tweet with an image * ``` * import { Rettiwt } from 'rettiwt-api'; * From aaf07072231c8b3f6d9ca5362c65f6a9b202f5e4 Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Fri, 26 Jan 2024 22:26:30 +0800 Subject: [PATCH 31/39] fix: typos --- README.md | 8 ++++---- src/Rettiwt.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a1fa394a..0358b591 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ By default, Rettiwt-API uses 'guest' authentication. If however, access to the f Here, - \ is the email of the Twitter account to be used for authentication. - - \ is the username associtated with the Twitter account. + - \ is the username associated with the Twitter account. - \ is the password to the Twitter account. 3. The string returned after running the command is the API_KEY. Store it in a secure place for later use. @@ -237,10 +237,10 @@ By default, the CLI operates in 'guest' authentication. If you want to use 'user 1. Generate an API_KEY as described in 'Authentication' section. 2. Store the output API_KEY as an environment variable with the name 'API_KEY'. - - Additionaly, store the API_KEY in a file for later use. + - Additionally, store the API_KEY in a file for later use. - Make sure to generate an API_KEY only once, and use it every time you need it. 3. The CLI automatically reads this environment variable to authenticate against Twitter. - - Additionaly, the API_KEY can also be passed in manually using the '-k' option as follows: `rettiwt -k ` + - Additionally, the API_KEY can also be passed in manually using the '-k' option as follows: `rettiwt -k ` Help for the CLI can be obtained from the CLI itself: @@ -253,5 +253,5 @@ The complete API reference can be found at [this](https://rishikant181.github.io ## Additional information -- This API uses the cookies of a Twitter account to fetch data from Twitter and as such, there is always a chance (altough a measly one) of getting the account banned by Twitter algorithm. +- This API uses the cookies of a Twitter account to fetch data from Twitter and as such, there is always a chance (although a measly one) of getting the account banned by Twitter algorithm. - There have been no reports of accounts getting banned, but you have been warned, even though the chances of getting banned is negligible, it is not zero! diff --git a/src/Rettiwt.ts b/src/Rettiwt.ts index ce5ae88d..863340e2 100644 --- a/src/Rettiwt.ts +++ b/src/Rettiwt.ts @@ -41,7 +41,7 @@ import { IRettiwtConfig } from './types/RettiwtConfig'; * import { Rettiwt } from 'rettiwt-api'; * * // Creating a new Rettiwt instance - * const rettiwt = new Rettiwt({ apiKey: 'API_KEY', loggin: true, proxyUrl: 'URL_TO_PROXY_SERVER' }); + * const rettiwt = new Rettiwt({ apiKey: 'API_KEY', logging: true, proxyUrl: 'URL_TO_PROXY_SERVER' }); * ``` * * @public From 732db60bd2c6ca9ff9c277b0a411b9dd201c3878 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Mon, 29 Jan 2024 12:41:15 +0530 Subject: [PATCH 32/39] Added CLI options to upload media in tweet --- src/commands/Tweet.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/commands/Tweet.ts b/src/commands/Tweet.ts index 55fe2d75..7d2d1928 100644 --- a/src/commands/Tweet.ts +++ b/src/commands/Tweet.ts @@ -107,8 +107,12 @@ function createTweetCommand(rettiwt: Rettiwt): Command { .command('post') .description('Post a tweet (text only)') .argument('', 'The text to post as a tweet') - .action(async (text: string) => { - const result = await rettiwt.tweet.tweet(text); + .option('-m, --media [string]', "The path to the media item(s) to be posted, separated by ';'") + .action(async (text: string, options?: { media?: string }) => { + const result = await rettiwt.tweet.tweet( + text, + options?.media ? options?.media.split(';').map((item) => ({ path: item })) : undefined, + ); output(result); }); From 178f8854c594a83dc94b196981fa402a50a0acdb Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Mon, 29 Jan 2024 12:52:35 +0530 Subject: [PATCH 33/39] Bumped rettiwt-core and rettiwt-auth to stable --- package.json | 4 ++-- yarn.lock | 50 +++++++++++++++++++++++++------------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index 3f49be19..826af5cf 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,8 @@ "axios": "1.6.3", "commander": "11.1.0", "https-proxy-agent": "7.0.2", - "rettiwt-auth": "2.1.0-alpha.1", - "rettiwt-core": "3.3.0-alpha.6" + "rettiwt-auth": "2.1.0", + "rettiwt-core": "3.3.0" }, "devDependencies": { "@types/node": "20.4.1", diff --git a/yarn.lock b/yarn.lock index 33aa4947..342e37b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -109,10 +109,10 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw== -"@types/validator@^13.7.10": - version "13.11.1" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.11.1.tgz#6560af76ed54490e68c42f717ab4e742ba7be74b" - integrity sha512-d/MUkJYdOeKycmm75Arql4M5+UuXmf4cHdHKsyw1GcvnNgL6s77UkgSgJ8TE/rI5PYsnwYq5jkcWBLuN/MpQ1A== +"@types/validator@^13.11.8": + version "13.11.8" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.11.8.tgz#bb1162ec0fe6f87c95ca812f15b996fcc5e1e2dc" + integrity sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ== "@typescript-eslint/eslint-plugin@6.0.0": version "6.0.0" @@ -343,14 +343,14 @@ chokidar@^3.5.2: optionalDependencies: fsevents "~2.3.2" -class-validator@0.14.0: - version "0.14.0" - resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.0.tgz#40ed0ecf3c83b2a8a6a320f4edb607be0f0df159" - integrity sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A== +class-validator@0.14.1: + version "0.14.1" + resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.1.tgz#ff2411ed8134e9d76acfeb14872884448be98110" + integrity sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ== dependencies: - "@types/validator" "^13.7.10" - libphonenumber-js "^1.10.14" - validator "^13.7.0" + "@types/validator" "^13.11.8" + libphonenumber-js "^1.10.53" + validator "^13.9.0" color-convert@^2.0.1: version "2.0.1" @@ -839,10 +839,10 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -libphonenumber-js@^1.10.14: - version "1.10.44" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.44.tgz#6709722461173e744190494aaaec9c1c690d8ca8" - integrity sha512-svlRdNBI5WgBjRC20GrCfbFiclbF0Cx+sCcQob/C1r57nsoq0xg8r65QbTyVyweQIlB33P+Uahyho6EMYgcOyQ== +libphonenumber-js@^1.10.53: + version "1.10.54" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.54.tgz#8dfba112f49d1b9c2a160e55f9697f22e50f0841" + integrity sha512-P+38dUgJsmh0gzoRDoM4F5jLbyfztkU6PY6eSK6S5HwTi/LPvnwXqVCQZlAy1FxZ5c48q25QhxGQ0pq+WQcSlQ== locate-path@^6.0.0: version "6.0.0" @@ -1080,23 +1080,23 @@ resolve@~1.19.0: is-core-module "^2.1.0" path-parse "^1.0.6" -rettiwt-auth@2.1.0-alpha.1: - version "2.1.0-alpha.1" - resolved "https://registry.yarnpkg.com/rettiwt-auth/-/rettiwt-auth-2.1.0-alpha.1.tgz#4277f54828c6379184e93d0cd63bf97ec9908dca" - integrity sha512-/GJWsOCB7fytno0XJyP7rUdZ2hdNDd9qr3aUs/P9qDwPH0oq1TOC//X2Rycs6tjMb2uR5WmLRbR9GPSJp4zB2A== +rettiwt-auth@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/rettiwt-auth/-/rettiwt-auth-2.1.0.tgz#43fedd40cab5b775b92a1bef740d7b2a36553463" + integrity sha512-N3G1kX/4TFYvUumxcACHO9ngVnhM+DhpuRn2/JxWlgHuztAQShbO9ux3hfMducCqzzjdcMLPJhrEDt9fwSbpaQ== dependencies: axios "1.6.3" commander "11.1.0" cookiejar "2.1.4" https-proxy-agent "7.0.2" -rettiwt-core@3.3.0-alpha.6: - version "3.3.0-alpha.6" - resolved "https://registry.yarnpkg.com/rettiwt-core/-/rettiwt-core-3.3.0-alpha.6.tgz#0d1c03b39c1c15d6ef562892085d697893032d63" - integrity sha512-nljy/IpXko4/7c4hv+30x8b/zp+HQft5Nyx5wnRBhuCJyiL4Xzx6GbltVSBZnUeVznnquqiaWvYqEwTmsUrfIg== +rettiwt-core@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/rettiwt-core/-/rettiwt-core-3.3.0.tgz#c856b1be47137c5289da65edd4560f2d95a76d73" + integrity sha512-m75NzF9eGO/2mxRJpakWF7nmSjXyaYuGLIxKlekP0xl5MNZNKlE3r/yV0lHADZOajCkW+XYXN7AbB0lphgOoMg== dependencies: axios "1.6.3" - class-validator "0.14.0" + class-validator "0.14.1" form-data "4.0.0" reusify@^1.0.4: @@ -1258,7 +1258,7 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -validator@^13.7.0: +validator@^13.9.0: version "13.11.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.11.0.tgz#23ab3fd59290c61248364eabf4067f04955fbb1b" integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ== From 0abb747072ad3fe266e3669e6ff7679cf50923e2 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Mon, 29 Jan 2024 14:14:47 +0530 Subject: [PATCH 34/39] Updated README.md --- README.md | 21 +++++++++++++++++++-- src/commands/Auth.ts | 4 ++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0358b591..2afaa33e 100644 --- a/README.md +++ b/README.md @@ -189,11 +189,28 @@ For masking of IP address using a proxy server, use the following code snippet f /** * PROXY_URL is the URL or configuration for the proxy server you want to use.` */ -const rettiwt = Rettiwt({ apiKey: API_KEY, proxyUrl: PROXY_URL }); +const rettiwt = new Rettiwt({ apiKey: API_KEY, proxyUrl: PROXY_URL }); ``` This creates a Rettiwt instance which uses the given proxy server for making requests to Twitter. +## Cloud environment + +When using this library in an application deployed to a cloud service, the library might throw error 429, even when under rate limits. This happens because Twitter's v1.1 API endpoints seemingly blocks access from cloud services' IP ranges. These v1.1 API endpoints are the ones used for authentication and as such, authentication tasks are blocked while deployed on cloud environments. + +This issue can be bypassed by using a proxy only for authentication, using the following code snippet: + +`const rettiwt = new Rettiwt({ authProxyUrl: PROXY_URL });` + +Where, + +- `PROXY_URL` is the URL to the proxy server to use. + +Authentication proxy is required only in the following two scenarios: + +1. While using 'guest' authentication. +2. While creating API_KEY by 'user' authentication. + ## Debug logs Sometimes, when the library shows unexpected behaviour, for troubleshooting purposes, debug logs can be enabled which will help in tracking down the issue and working on a potential fix. Currently, debug logs are printed to the console and are enabled by setting the 'logging' property of the config to true, while creating an instance of Rettiwt: @@ -202,7 +219,7 @@ Sometimes, when the library shows unexpected behaviour, for troubleshooting purp /** * By default, is no value for 'logging' is supplied, logging is disabled. */ -const rettiwt = Rettiwt({ apiKey: API_KEY, logging: true }); +const rettiwt = new Rettiwt({ apiKey: API_KEY, logging: true }); ``` ## Features diff --git a/src/commands/Auth.ts b/src/commands/Auth.ts index b4ae9a03..4bea138d 100644 --- a/src/commands/Auth.ts +++ b/src/commands/Auth.ts @@ -18,9 +18,9 @@ function createAuthCommand(): Command { .action(async (email: string, username: string, password: string) => { // Logging in and getting the credentials let apiKey: string = - ( + (( await new Auth().getUserCredential({ email: email, userName: username, password: password }) - ).toHeader().cookie ?? ''; + ).toHeader().cookie as string) ?? ''; // Converting the credentials to base64 string apiKey = Buffer.from(apiKey).toString('base64'); From e76824d4d944466bcfc76cee890120e49d2798b3 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Mon, 29 Jan 2024 15:19:10 +0530 Subject: [PATCH 35/39] Created AuthService to handle authentication --- src/Rettiwt.ts | 5 +++ src/cli.ts | 2 +- src/commands/Auth.ts | 19 ++------- src/services/internal/FetcherService.ts | 5 +-- src/services/public/AuthService.ts | 54 +++++++++++++++++++++++++ 5 files changed, 65 insertions(+), 20 deletions(-) create mode 100644 src/services/public/AuthService.ts diff --git a/src/Rettiwt.ts b/src/Rettiwt.ts index 863340e2..7ad46d4e 100644 --- a/src/Rettiwt.ts +++ b/src/Rettiwt.ts @@ -1,4 +1,5 @@ // SERVICES +import { AuthService } from './services/public/AuthService'; import { TweetService } from './services/public/TweetService'; import { UserService } from './services/public/UserService'; @@ -47,6 +48,9 @@ import { IRettiwtConfig } from './types/RettiwtConfig'; * @public */ export class Rettiwt { + /** The instance used to authenticate. */ + public auth: AuthService; + /** The instance used to fetch data related to tweets. */ public tweet: TweetService; @@ -59,6 +63,7 @@ export class Rettiwt { * @param config - The config object for configuring the Rettiwt instance. */ public constructor(config?: IRettiwtConfig) { + this.auth = new AuthService(config); this.tweet = new TweetService(config); this.user = new UserService(config); } diff --git a/src/cli.ts b/src/cli.ts index 71f8526c..d595cb47 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -34,7 +34,7 @@ const rettiwt: Rettiwt = new Rettiwt({ // Adding sub-commands program.addCommand(tweet(rettiwt)); program.addCommand(user(rettiwt)); -program.addCommand(auth()); +program.addCommand(auth(rettiwt)); // Finalizing the CLI program.parse(); diff --git a/src/commands/Auth.ts b/src/commands/Auth.ts index 4bea138d..b6af0f94 100644 --- a/src/commands/Auth.ts +++ b/src/commands/Auth.ts @@ -1,11 +1,11 @@ // PACKAGES import { Command, createCommand } from 'commander'; -import { Auth } from 'rettiwt-auth'; +import { Rettiwt } from '../Rettiwt'; // UTILITY import { output } from '../helper/CliUtils'; -function createAuthCommand(): Command { +function createAuthCommand(rettiwt: Rettiwt): Command { // Creating the 'auth' command const auth = createCommand('auth').description('Manage authentication'); @@ -16,14 +16,7 @@ function createAuthCommand(): Command { .argument('', 'The username associated with the Twitter account') .argument('', 'The password to the Twitter account') .action(async (email: string, username: string, password: string) => { - // Logging in and getting the credentials - let apiKey: string = - (( - await new Auth().getUserCredential({ email: email, userName: username, password: password }) - ).toHeader().cookie as string) ?? ''; - - // Converting the credentials to base64 string - apiKey = Buffer.from(apiKey).toString('base64'); + const apiKey: string = await rettiwt.auth.login({ email: email, userName: username, password: password }); output(apiKey); }); @@ -31,11 +24,7 @@ function createAuthCommand(): Command { auth.command('guest') .description('Generate a new guest API key') .action(async () => { - // Getting a new guest API key - let guestKey: string = (await new Auth().getGuestCredential()).guestToken ?? ''; - - // Converting the credentials to base64 string - guestKey = Buffer.from(guestKey).toString('base64'); + const guestKey: string = await rettiwt.auth.guest(); output(guestKey); }); diff --git a/src/services/internal/FetcherService.ts b/src/services/internal/FetcherService.ts index 5a131c50..bd195ebc 100644 --- a/src/services/internal/FetcherService.ts +++ b/src/services/internal/FetcherService.ts @@ -52,7 +52,7 @@ export class FetcherService { private readonly isAuthenticated: boolean; /** The URL to the proxy server to use for authentication. */ - private readonly authProxyUrl?: URL; + protected readonly authProxyUrl?: URL; /** The HTTPS Agent to use for requests to Twitter API. */ private readonly httpsAgent: Agent; @@ -110,9 +110,6 @@ export class FetcherService { * @returns The generated AuthCredential. */ private getGuestCredential(guestKey: string): AuthCredential { - // Converting guestKey from base64 to string - guestKey = Buffer.from(guestKey).toString('ascii'); - return new AuthCredential(undefined, guestKey); } diff --git a/src/services/public/AuthService.ts b/src/services/public/AuthService.ts new file mode 100644 index 00000000..a27bf582 --- /dev/null +++ b/src/services/public/AuthService.ts @@ -0,0 +1,54 @@ +// PACKAGES +import { AccountCredential, Auth } from 'rettiwt-auth'; + +// SERVICES +import { FetcherService } from '../internal/FetcherService'; + +// TYPES +import { IRettiwtConfig } from '../../types/RettiwtConfig'; + +/** + * Handles authentication. + * + * @public + */ +export class AuthService extends FetcherService { + /** + * @param config - The config object for configuring the Rettiwt instance. + * + * @internal + */ + public constructor(config?: IRettiwtConfig) { + super(config); + } + + /** + * Login to twitter using account credentials. + * + * @param cred - The credentials of the Twitter account to be logged into. + * @returns The API_KEY for the Twitter account. + */ + public async login(cred: AccountCredential): Promise { + // Logging in and getting the credentials + let apiKey: string = + ((await new Auth({ proxyUrl: this.authProxyUrl }).getUserCredential(cred)).toHeader().cookie as string) ?? + ''; + + // Converting the credentials to base64 string + apiKey = Buffer.from(apiKey).toString('base64'); + + return apiKey; + } + + /** + * Login to twitter as guest. + * + * @returns A new guest API_KEY. + */ + public async guest(): Promise { + // Getting a new guest API key + const guestKey: string = (await new Auth().getGuestCredential()).guestToken ?? ''; + + return guestKey; + } +} From 8243561eb8493e22d6e75304244740f4dc88f43c Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Mon, 29 Jan 2024 15:45:45 +0530 Subject: [PATCH 36/39] Updated docs --- README.md | 49 +++++++++++++++++++++++------- src/commands/Auth.ts | 2 +- src/services/public/AuthService.ts | 40 ++++++++++++++++++++++-- 3 files changed, 77 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 2afaa33e..4ea4df89 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,9 @@ By default, Rettiwt-API uses 'guest' authentication. If however, access to the f Here, - - \ is the email of the Twitter account to be used for authentication. - - \ is the username associated with the Twitter account. - - \ is the password to the Twitter account. + - `` is the email of the Twitter account to be used for authentication. + - `` is the username associated with the Twitter account. + - `` is the password to the Twitter account. 3. The string returned after running the command is the API_KEY. Store it in a secure place for later use. @@ -97,8 +97,8 @@ A new Rettiwt instance can be initialized using the following code snippets: The Rettiwt class has two members: -- 'tweet' member, for accessing resources related to tweets -- 'user' member, for accessing resources related to users +- `tweet` member, for accessing resources related to tweets +- `user` member, for accessing resources related to users For details regarding usage of these members for accessing the Twitter API, refer to the 'Features' section. @@ -109,7 +109,7 @@ The following examples may help you to get started using the library: ### 1. Getting the details of a target Twitter user ```js -const { Rettiwt } = require('rettiwt-api'); +import { Rettiwt } from 'rettiwt-api'; // Creating a new Rettiwt instance // Note that for accessing user details, 'guest' authentication can be used @@ -128,7 +128,7 @@ rettiwt.user.details('') ### 2. Getting the list of tweets that match a given filter ```js -const { Rettiwt } = require('rettiwt-api'); +import { Rettiwt } from 'rettiwt-api'; // Creating a new Rettiwt instance using the API_KEY const rettiwt = new Rettiwt({ apiKey: API_KEY }); @@ -150,12 +150,14 @@ rettiwt.tweet.search({ }); ``` +For more information regarding the different available filter options, please refer to [TweetFilter](https://rishikant181.github.io/Rettiwt-API/classes/TweetFilter.html). + ### 3. Getting the next batch of data using a cursor The previous example fetches the the list of tweets matching the given filter. Since no count is specified, in this case, a default of 20 such Tweets are fetched initially. The following example demonstrates how to use the [cursor string](https://rishikant181.github.io/Rettiwt-API/classes/Cursor.html#value) obtained from the [response](https://rishikant181.github.io/Rettiwt-API/classes/CursoredData.html) object's [next](https://rishikant181.github.io/Rettiwt-API/classes/CursoredData.html#next) field, from the previous example, to fetch the next batch of tweets: ```js -const { Rettiwt } = require('rettiwt-api'); +import { Rettiwt } from 'rettiwt-api'; // Creating a new Rettiwt instance using the API_KEY const rettiwt = new Rettiwt({ apiKey: API_KEY }); @@ -179,7 +181,32 @@ rettiwt.tweet.search({ }); ``` -For more information regarding the different available filter options, please refer to [TweetFilter](https://rishikant181.github.io/Rettiwt-API/classes/TweetFilter.html). +### 4. Getting an API_KEY during runtime, using 'user' authentication + +Sometimes, you might want to generate an API_KEY on the fly, in situations such as implementing Twitter login in your application. The following example demonstrates how to generate an API_KEY during runtime: + +```js +import { Rettiwt } from 'rettiwt-api'; + +// Creating a new Rettiwt instance +const rettiwt = new Rettiwt(); + +// Logging in an getting the API_KEY +rettiwt.auth.login({ email: '', userName: '', password: '' }) +.then(apiKey => { + // Use the API_KEY + ... +}) +.catch(err => { + console.log(err); +}); +``` + +Where, + +- `` is the email associated with the Twitter account to be logged into. +- `` is the username associated with the Twitter account. +- `` is the password to the Twitter account. ## Using a proxy @@ -196,9 +223,9 @@ This creates a Rettiwt instance which uses the given proxy server for making req ## Cloud environment -When using this library in an application deployed to a cloud service, the library might throw error 429, even when under rate limits. This happens because Twitter's v1.1 API endpoints seemingly blocks access from cloud services' IP ranges. These v1.1 API endpoints are the ones used for authentication and as such, authentication tasks are blocked while deployed on cloud environments. +When using this library in an application deployed in a cloud environment, the library might throw error 429, even when under rate limits. This happens because Twitter's v1.1 API endpoints seemingly blocks access from cloud services' IP ranges. These v1.1 API endpoints are the ones used for authentication and as such, authentication tasks are blocked while deployed on cloud environments. -This issue can be bypassed by using a proxy only for authentication, using the following code snippet: +This issue can be bypassed by using a proxy only for authentication, using the following code snippet for creating a new Rettiwt instance: `const rettiwt = new Rettiwt({ authProxyUrl: PROXY_URL });` diff --git a/src/commands/Auth.ts b/src/commands/Auth.ts index b6af0f94..b8295253 100644 --- a/src/commands/Auth.ts +++ b/src/commands/Auth.ts @@ -22,7 +22,7 @@ function createAuthCommand(rettiwt: Rettiwt): Command { // Guest auth.command('guest') - .description('Generate a new guest API key') + .description('Generate a new guest key') .action(async () => { const guestKey: string = await rettiwt.auth.guest(); output(guestKey); diff --git a/src/services/public/AuthService.ts b/src/services/public/AuthService.ts index a27bf582..a432304c 100644 --- a/src/services/public/AuthService.ts +++ b/src/services/public/AuthService.ts @@ -27,6 +27,24 @@ export class AuthService extends FetcherService { * * @param cred - The credentials of the Twitter account to be logged into. * @returns The API_KEY for the Twitter account. + * + * @example + * ``` + * import { Rettiwt } from 'rettiwt-api'; + * + * // Creating a new Rettiwt instance + * const rettiwt = new Rettiwt(); + * + * // Logging in an getting the API_KEY + * rettiwt.auth.login({ email: "email@domain.com", userName: "username", password: "password" }) + * .then(apiKey => { + * // Use the API_KEY + * ... + * }) + * .catch(err => { + * console.log(err); + * }); + * ``` */ public async login(cred: AccountCredential): Promise { // Logging in and getting the credentials @@ -43,10 +61,28 @@ export class AuthService extends FetcherService { /** * Login to twitter as guest. * - * @returns A new guest API_KEY. + * @returns A new guest key. + * + * @example + * ``` + * import { Rettiwt } from 'rettiwt-api'; + * + * // Creating a new Rettiwt instance + * const rettiwt = new Rettiwt(); + * + * // Logging in an getting a new guest key + * rettiwt.auth.guest() + * .then(guestKey => { + * // Use the guest key + * ... + * }) + * .catch(err => { + * console.log(err); + * }); + * ``` */ public async guest(): Promise { - // Getting a new guest API key + // Getting a new guest key const guestKey: string = (await new Auth().getGuestCredential()).guestToken ?? ''; return guestKey; From 62105f3a50ef396723b2904d3fa4d5f5958bf71d Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Mon, 29 Jan 2024 15:54:43 +0530 Subject: [PATCH 37/39] Updated docs --- README.md | 2 +- src/Rettiwt.ts | 2 +- src/commands/Auth.ts | 2 +- src/index.ts | 1 + src/services/public/AuthService.ts | 19 +++++++++++++------ 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 4ea4df89..85b38fff 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ import { Rettiwt } from 'rettiwt-api'; const rettiwt = new Rettiwt(); // Logging in an getting the API_KEY -rettiwt.auth.login({ email: '', userName: '', password: '' }) +rettiwt.auth.login('', '', '') .then(apiKey => { // Use the API_KEY ... diff --git a/src/Rettiwt.ts b/src/Rettiwt.ts index 7ad46d4e..09ea7c99 100644 --- a/src/Rettiwt.ts +++ b/src/Rettiwt.ts @@ -11,7 +11,7 @@ import { IRettiwtConfig } from './types/RettiwtConfig'; * * The created Rettiwt instance can be configured by passing in a configuration object to the constructor. * - * For details regarding the available configuration options, refer to {@link RettiwtConfig} + * For details regarding the available configuration options, refer to {@link IRettiwtConfig} * * @example Creating a Rettiwt instance with 'guest' authentication: * ``` diff --git a/src/commands/Auth.ts b/src/commands/Auth.ts index b8295253..370cfb40 100644 --- a/src/commands/Auth.ts +++ b/src/commands/Auth.ts @@ -16,7 +16,7 @@ function createAuthCommand(rettiwt: Rettiwt): Command { .argument('', 'The username associated with the Twitter account') .argument('', 'The password to the Twitter account') .action(async (email: string, username: string, password: string) => { - const apiKey: string = await rettiwt.auth.login({ email: email, userName: username, password: password }); + const apiKey: string = await rettiwt.auth.login(email, username, password); output(apiKey); }); diff --git a/src/index.ts b/src/index.ts index a4793792..517cf40f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,6 +24,7 @@ export * from './models/data/User'; export * from './services/internal/ErrorService'; export * from './services/internal/FetcherService'; export * from './services/internal/LogService'; +export * from './services/public/AuthService'; export * from './services/public/TweetService'; export * from './services/public/UserService'; diff --git a/src/services/public/AuthService.ts b/src/services/public/AuthService.ts index a432304c..65e531a9 100644 --- a/src/services/public/AuthService.ts +++ b/src/services/public/AuthService.ts @@ -1,5 +1,5 @@ // PACKAGES -import { AccountCredential, Auth } from 'rettiwt-auth'; +import { Auth } from 'rettiwt-auth'; // SERVICES import { FetcherService } from '../internal/FetcherService'; @@ -25,7 +25,9 @@ export class AuthService extends FetcherService { /** * Login to twitter using account credentials. * - * @param cred - The credentials of the Twitter account to be logged into. + * @param email - The email id associated with the Twitter account. + * @param userName - The username associated with the Twitter account. + * @param password - The password to the Twitter account. * @returns The API_KEY for the Twitter account. * * @example @@ -36,7 +38,7 @@ export class AuthService extends FetcherService { * const rettiwt = new Rettiwt(); * * // Logging in an getting the API_KEY - * rettiwt.auth.login({ email: "email@domain.com", userName: "username", password: "password" }) + * rettiwt.auth.login("email@domain.com", "username", "password") * .then(apiKey => { * // Use the API_KEY * ... @@ -46,11 +48,16 @@ export class AuthService extends FetcherService { * }); * ``` */ - public async login(cred: AccountCredential): Promise { + public async login(email: string, userName: string, password: string): Promise { // Logging in and getting the credentials let apiKey: string = - ((await new Auth({ proxyUrl: this.authProxyUrl }).getUserCredential(cred)).toHeader().cookie as string) ?? - ''; + (( + await new Auth({ proxyUrl: this.authProxyUrl }).getUserCredential({ + email: email, + userName: userName, + password: password, + }) + ).toHeader().cookie as string) ?? ''; // Converting the credentials to base64 string apiKey = Buffer.from(apiKey).toString('base64'); From 08d4cf3f0f27c216bc8a7056bc1643aff8dcd069 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Mon, 29 Jan 2024 16:06:33 +0530 Subject: [PATCH 38/39] Updated docs --- README.md | 16 +++++++++++----- package.json | 4 ++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 85b38fff..f10e303c 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ The API_KEY generated by logging in is what allows Rettiwt-API to authenticate a - If you have no idea of programming, it's recommended to use the CLI. - The CLI provides an easy to use interface which does not require any knowledge of JavaScript or programming -- Please skip to 'CLI-Usage' section for details. +- Please skip to [CLI-Usage](https://rishikant181.github.io/Rettiwt-API/#md:cli-usage) for details. ## Usage as a dependency @@ -88,19 +88,20 @@ For example, for generating the API_KEY, the command will be modified as follows ## The Rettiwt class -When used as a dependency, the Rettiwt class is entry point for accessing the Twitter API. +When used as a dependency, the [Rettiwt](https://rishikant181.github.io/Rettiwt-API/classes/Rettiwt.html) class is entry point for accessing the Twitter API. A new Rettiwt instance can be initialized using the following code snippets: - `const rettiwt = new Rettiwt()` (for 'guest' authentication) - `const rettiwt = new Rettiwt({ apiKey: API_KEY })` (for 'user' authentication) -The Rettiwt class has two members: +The Rettiwt class has three members: +- `auth` memeber, for managing authentication - `tweet` member, for accessing resources related to tweets - `user` member, for accessing resources related to users -For details regarding usage of these members for accessing the Twitter API, refer to the 'Features' section. +For details regarding usage of these members for accessing the Twitter API, refer to [Features](https://rishikant181.github.io/Rettiwt-API/#md:features). ## Usage @@ -253,6 +254,11 @@ const rettiwt = new Rettiwt({ apiKey: API_KEY, logging: true }); So far, the following operations are supported: +### Authentication + +- [Logging in as user](https://rishikant181.github.io/Rettiwt-API/classes/AuthService.html#login) +- [Logging in as guest](https://rishikant181.github.io/Rettiwt-API/classes/AuthService.html#guest) + ### Tweets - [Getting the details of a tweet](https://rishikant181.github.io/Rettiwt-API/classes/TweetService.html#details) @@ -279,7 +285,7 @@ Rettiwt-API provides an easy to use command-line interface which does not requir By default, the CLI operates in 'guest' authentication. If you want to use 'user' authentication: -1. Generate an API_KEY as described in 'Authentication' section. +1. Generate an API_KEY as described in [Authentication](https://rishikant181.github.io/Rettiwt-API/#md:authentication). 2. Store the output API_KEY as an environment variable with the name 'API_KEY'. - Additionally, store the API_KEY in a file for later use. - Make sure to generate an API_KEY only once, and use it every time you need it. diff --git a/package.json b/package.json index 826af5cf..f1cf4cd4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rettiwt-api", - "version": "2.4.2", + "version": "2.5.0", "main": "dist/index.js", "types": "dist/index.d.ts", "description": "An API for fetching data from TwitterAPI, without any rate limits!", @@ -12,7 +12,7 @@ "prepare": "tsc", "format": "prettier --write .", "lint": "eslint --max-warnings 0 .", - "docs": "typedoc src/index.ts", + "docs": "typedoc --excludePrivate --excludeProtected --excludeInternal src/index.ts", "debug": "nodemon ./dist/index.js --inspect=0.0.0.0:9229" }, "repository": { From 00a28e0fe06056939aca801146387113ec1655d7 Mon Sep 17 00:00:00 2001 From: Rishikant Sahu Date: Mon, 29 Jan 2024 16:07:30 +0530 Subject: [PATCH 39/39] Updated actions --- .github/workflows/documentation.yml | 2 +- .github/workflows/publish.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 6420deae..95f68812 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: '18.x' + node-version: '20.x' # Installing dependencies - run: yarn diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0ac0659b..dfd005bd 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: '18.x' + node-version: '20.x' registry-url: https://registry.npmjs.org/ # Installing dependencies