diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..adf7f35c --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +# github: [Rishikant181] +custom: ['https://paypal.me/Rishikant181?country.x=IN&locale.x=en_GB'] diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index bfa2a9b2..fd0fe9de 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -16,8 +16,13 @@ jobs: with: node-version: '20.x' + # Enabling CorePack + - run: corepack enable + # Installing dependencies - run: yarn + + # Generating docs - run: npx typedoc src/index.ts # Packaging the static site as artifact diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 512e8dd3..d0483568 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -17,6 +17,9 @@ jobs: node-version: '20.x' registry-url: https://registry.npmjs.org/ + # Enabling CorePack + - run: corepack enable + # Installing dependencies - run: yarn diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 00000000..3186f3f0 --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/README.md b/README.md index 1589f54b..6528098d 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,14 @@ A CLI tool and an API for fetching data from Twitter for free! ## Installation -It is recommended to install the package globally. Use the following steps to install the package and ensure it's installed correctly: +It is recommended to install the package globally, if you want to use it from the CLI. Use the following steps to install the package and ensure it's installed correctly: 1. Open a terminal. 2. Install the package using the command `npm install -g rettiwt-api`. 3. Check if the package is installed correctly using the command `rettiwt help`. +For using the package in your own project, you can install it as a [dependency](https://rishikant181.github.io/Rettiwt-API/#md:usage-as-a-dependency). + ## Authentication Rettiwt-API can be used with or without logging in to Twitter. As such, the two authentication strategies are: @@ -22,25 +24,26 @@ Rettiwt-API can be used with or without logging in to Twitter. As such, the two - 'guest' authentication (without logging in) grants access to the following resources/actions: - Tweet Details - - User Details - - User Timeline + - User Details (by username) - User Replies Timeline + - User Timeline - 'user' authentication (logging in) grants access to the following resources/actions: - Tweet Details - Tweet Like - Tweet List + - Tweet Media Upload - Tweet Post - Tweet Retweet - Tweet Retweeters + - Tweet Schedule - Tweet Search - Tweet Stream - Tweet Unlike - Tweet Unpost - Tweet Unretweet - - Tweet Media Upload - - User Details + - User Details (by username or id) - User Follow - User Followed Feed - User Followers @@ -54,7 +57,36 @@ Rettiwt-API can be used with or without logging in to Twitter. As such, the two - User Timeline - User Unfollow -By default, Rettiwt-API uses 'guest' authentication. If however, access to the full set of resources is required, 'user' authentication can be used, which requires the following additional steps post-installtion: +By default, Rettiwt-API uses 'guest' authentication. If however, access to the full set of resources is required, 'user' authentication can be used. This is done by using the cookies associated with your Twitter/X account, and encoding them into an `API_KEY` for convenience. For getting the said `API_KEY`, there are two approaches: + +### 1. Using a browser (recommended): + +#### A. For Chrome/Chromium-based browsers: + +1. Install the [X Auth Helper extension](https://chromewebstore.google.com/detail/x-auth-helper/igpkhkjmpdecacocghpgkghdcmcmpfhp) from the Chrome Web Store, and allow it to run it in incognito mode. +2. Switch to incognito mode and login to Twitter/X. +3. After successful login, while still being on Twitter/X, click on the extension which will open the extension popup. +4. Click on the `Get Key` button, this will generate the `API_KEY` and will show up in the text-area. +5. Copy the `API_KEY` by either clicking on the `Copy Key` button or manually from the text-area. +6. You may close the browser, but don't log out. Remember, since it's incognito mode, you didn't explicity 'log out', so, while the session will be erased from the browser, the `API_KEY` still remains valid. +7. Save the `API_KEY` for use. + +#### B. For Firefox/Firefox-based browsers: + +1. Install the [Rettiwt Auth Helper extension](https://addons.mozilla.org/en-US/firefox/addon/rettiwt-auth-helper) from Firefox Add-Ons, and allow it to run it in in-private mode. +2. Switch to in-private mode and login to Twitter/X. +3. After successful login, while still being on Twitter/X, click on the extension which will open the extension popup. +4. Click on the `Get API Key` button, this will generate the `API_KEY` and will show up in the text-area. +5. Copy the `API_KEY` by either clicking on the `Copy API Key` button or manually from the text-area. +6. You may close the browser, but don't log out. Remember, since it's in-private mode, you didn't explicity 'log out', so, while the session will be erased from the browser, the `API_KEY` still remains valid. +7. Save the `API_KEY` for use. + +#### Notes: + +- `API_KEY` created in this way should last 5 years from the date of login, as long as the credentials to the account aren't changed. +- This approach can also be done without going into incognito/in-private mode, in which case you can either login as usual or skip the login step if you're already logged in, and continue from the steps after login. However, this makes the `API_KEY` to last only as long as the Twitter/X account isn't logged out of (you may exit the browser as usual) or 5 years, whichever comes first. That's why it's recommended to use incognito/in-private mode, so that the `API_KEY` isn't accidentially revoked by logging out. + +### 2. Using the CLI: 1. Open a terminal. 2. Generate an API_KEY using the command `rettiwt auth login "" "" ""` @@ -65,7 +97,11 @@ By default, Rettiwt-API uses 'guest' authentication. If however, access to the f - `` 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. +3. Store the output `API_KEY` in a secure place for later use. + +#### Notes: + +- The `API_KEY` created in this way expires after one year from the day it was generated. ## The API_KEY @@ -74,7 +110,6 @@ The API_KEY generated by logging in is what allows Rettiwt-API to authenticate a - The API_KEY is generated by logging into Twitter using the email, username and password and encoding the returned cookies as a base64 string. This encoded string is the API_KEY. - The API_KEY provides the same level of authorization as any standard Twitter account, nothing more, nothing less. - Since generation of API_KEY is equivalent to logging in to Twitter, repeated generation attempts might trigger Twitter's anti-bot measures, the same way repeated login attempts do. -- The API_KEY expires after one year from the day it was generated. - Therefore, it is recommended to generate the API_KEY only once, then use it every time it is needed. - Do not generate an API_KEY if it has not expired yet! @@ -327,11 +362,13 @@ So far, the following operations are supported: - [Posting a new tweet](https://rishikant181.github.io/Rettiwt-API/classes/TweetService.html#post) - [Retweeting a tweet](https://rishikant181.github.io/Rettiwt-API/classes/TweetService.html#retweet) - [Getting the list of users who retweeted a given tweet](https://rishikant181.github.io/Rettiwt-API/classes/TweetService.html#retweeters) +- [Scheduling a new tweet](https://rishikant181.github.io/Rettiwt-API/classes/TweetService.html#schedule) - [Searching for the list of tweets that match a given filter](https://rishikant181.github.io/Rettiwt-API/classes/TweetService.html#search) - [Streaming filtered tweets in pseudo-realtime](https://rishikant181.github.io/Rettiwt-API/classes/TweetService.html#stream) - [Unliking a tweet](https://rishikant181.github.io/Rettiwt-API/classes/TweetService.html#unlike) - [Unposting a tweet](https://rishikant181.github.io/Rettiwt-API/classes/TweetService.html#unpost) - [Unretweeting a tweet](https://rishikant181.github.io/Rettiwt-API/classes/TweetService.html#unretweet) +- [Unscheduling a tweet](https://rishikant181.github.io/Rettiwt-API/classes/TweetService.html#unschedule) - [Uploading a media file for a tweet](https://rishikant181.github.io/Rettiwt-API/classes/TweetService.html#upload) ### Users @@ -342,7 +379,7 @@ So far, the following operations are supported: - [Getting the list of users who follow the given user](https://rishikant181.github.io/Rettiwt-API/classes/UserService.html#followers) - [Getting the list of users who are followed by the given user](https://rishikant181.github.io/Rettiwt-API/classes/UserService.html#following) - [Getting the list of highlighted tweets of the given user](https://rishikant181.github.io/Rettiwt-API/classes/UserService.html#highlights) -- [Getting the list of tweets liked by the given user](https://rishikant181.github.io/Rettiwt-API/classes/UserService.html#likes) +- [Getting the list of tweets liked by the logged-in user](https://rishikant181.github.io/Rettiwt-API/classes/UserService.html#likes) - [Getting the media timeline of the given user](https://rishikant181.github.io/Rettiwt-API/classes/UserService.html#media) - [Getting the recommended feed of the logged-in user](https://rishikant181.github.io/Rettiwt-API/classes/UserService.html#recommended) - [Getting the replies timeline of the given user](https://rishikant181.github.io/Rettiwt-API/classes/UserService.html#replies) @@ -375,4 +412,7 @@ 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 (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! + +## Donation + +Support this project by donating at my [PayPal](https://paypal.me/Rishikant181?country.x=IN&locale.x=en_GB). diff --git a/package.json b/package.json index 3976e74a..7e01bb1d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rettiwt-api", - "version": "3.1.1", + "version": "4.0.0", "main": "dist/index.js", "types": "dist/index.d.ts", "description": "An API for fetching data from TwitterAPI, without any rate limits!", @@ -28,6 +28,7 @@ "url": "https://github.com/Rishikant181/Rettiwt-API/issues" }, "homepage": "https://rishikant181.github.io/Rettiwt-API/", + "packageManager": "yarn@4.4.0", "dependencies": { "axios": "1.6.3", "chalk": "4.1.2", @@ -35,7 +36,7 @@ "commander": "11.1.0", "https-proxy-agent": "7.0.2", "rettiwt-auth": "2.1.0", - "rettiwt-core": "4.1.1" + "rettiwt-core": "4.2.0" }, "devDependencies": { "@types/node": "20.4.1", diff --git a/src/collections/Extractors.ts b/src/collections/Extractors.ts index bb5f72a4..3c2dda14 100644 --- a/src/collections/Extractors.ts +++ b/src/collections/Extractors.ts @@ -3,14 +3,16 @@ import { IListTweetsResponse, ITweetDetailsResponse, ITweetLikeResponse, - ITweetLikersResponse, ITweetPostResponse, + ITweetRepliesResponse, ITweetRetweetersResponse, ITweetRetweetResponse, + ITweetScheduleResponse, ITweetSearchResponse, ITweetUnlikeResponse, ITweetUnpostResponse, ITweetUnretweetResponse, + ITweetUnscheduleResponse, IUserDetailsResponse, IUserFollowedResponse, IUserFollowersResponse, @@ -47,20 +49,21 @@ export const extractors = { response.media_id_string ?? undefined, TWEET_DETAILS: (response: ITweetDetailsResponse, id: string): Tweet | undefined => Tweet.single(response, id), + TWEET_DETAILS_ALT: (response: ITweetRepliesResponse, id: string): Tweet | undefined => Tweet.single(response, id), TWEET_LIKE: (response: ITweetLikeResponse): boolean => (response?.data?.favorite_tweet ? true : false), - TWEET_LIKERS: (response: ITweetLikersResponse): CursoredData => - new CursoredData(response, EBaseType.USER), TWEET_POST: (response: ITweetPostResponse): string => response?.data?.create_tweet?.tweet_results?.result?.rest_id ?? undefined, TWEET_RETWEET: (response: ITweetRetweetResponse): boolean => (response?.data?.create_retweet ? true : false), TWEET_RETWEETERS: (response: ITweetRetweetersResponse): CursoredData => new CursoredData(response, EBaseType.USER), + TWEET_SCHEDULE: (response: ITweetScheduleResponse): string => response?.data?.tweet?.rest_id ?? undefined, TWEET_SEARCH: (response: ITweetSearchResponse): CursoredData => new CursoredData(response, EBaseType.TWEET), TWEET_UNLIKE: (response: ITweetUnlikeResponse): boolean => (response?.data?.unfavorite_tweet ? true : false), TWEET_UNPOST: (response: ITweetUnpostResponse): boolean => (response?.data?.delete_tweet ? true : false), TWEET_UNRETWEET: (response: ITweetUnretweetResponse): boolean => response?.data?.unretweet?.source_tweet_results?.result ? true : false, + TWEET_UNSCHEDULE: (response: ITweetUnscheduleResponse): boolean => response?.data?.scheduledtweet_delete == 'Done', USER_DETAILS_BY_USERNAME: (response: IUserDetailsResponse): User | undefined => User.single(response), USER_DETAILS_BY_ID: (response: IUserDetailsResponse): User | undefined => User.single(response), diff --git a/src/collections/Groups.ts b/src/collections/Groups.ts index 7998cca5..52a459cf 100644 --- a/src/collections/Groups.ts +++ b/src/collections/Groups.ts @@ -19,7 +19,7 @@ export const allowGuestAuthentication = [ export const fetchResources = [ EResourceType.LIST_TWEETS, EResourceType.TWEET_DETAILS, - EResourceType.TWEET_LIKERS, + EResourceType.TWEET_DETAILS_ALT, EResourceType.TWEET_RETWEETERS, EResourceType.TWEET_SEARCH, EResourceType.USER_DETAILS_BY_USERNAME, @@ -48,9 +48,11 @@ export const postResources = [ EResourceType.TWEET_LIKE, EResourceType.TWEET_POST, EResourceType.TWEET_RETWEET, + EResourceType.TWEET_SCHEDULE, EResourceType.TWEET_UNLIKE, EResourceType.TWEET_UNPOST, EResourceType.TWEET_UNRETWEET, + EResourceType.TWEET_UNSCHEDULE, EResourceType.USER_FOLLOW, EResourceType.USER_UNFOLLOW, ]; diff --git a/src/collections/Requests.ts b/src/collections/Requests.ts index a7252211..6e548758 100644 --- a/src/collections/Requests.ts +++ b/src/collections/Requests.ts @@ -26,15 +26,17 @@ export const requests: { [key in keyof typeof EResourceType]: (args: FetchArgs | MEDIA_UPLOAD_INITIALIZE: (args: PostArgs) => request.media.initializeUpload(args.upload!.size!), TWEET_DETAILS: (args: FetchArgs) => request.tweet.details(args.id!), + TWEET_DETAILS_ALT: (args: FetchArgs) => request.tweet.replies(args.id!), TWEET_LIKE: (args: PostArgs) => request.tweet.like(args.id!), - TWEET_LIKERS: (args: FetchArgs) => request.tweet.likers(args.id!, args.count, args.cursor), TWEET_POST: (args: PostArgs) => request.tweet.post(args.tweet!), TWEET_RETWEET: (args: PostArgs) => request.tweet.retweet(args.id!), TWEET_RETWEETERS: (args: FetchArgs) => request.tweet.retweeters(args.id!, args.count, args.cursor), + TWEET_SCHEDULE: (args: PostArgs) => request.tweet.schedule(args.tweet!, args.tweet!.scheduleFor!), TWEET_SEARCH: (args: FetchArgs) => request.tweet.search(args.filter!, args.count, args.cursor), TWEET_UNLIKE: (args: PostArgs) => request.tweet.unlike(args.id!), TWEET_UNPOST: (args: PostArgs) => request.tweet.unpost(args.id!), TWEET_UNRETWEET: (args: PostArgs) => request.tweet.unretweet(args.id!), + TWEET_UNSCHEDULE: (args: PostArgs) => request.tweet.unschedule(args.id!), USER_DETAILS_BY_USERNAME: (args: FetchArgs) => request.user.detailsByUsername(args.id!), USER_DETAILS_BY_ID: (args: FetchArgs) => request.user.detailsById(args.id!), diff --git a/src/commands/Tweet.ts b/src/commands/Tweet.ts index 1d9a8d51..59f90550 100644 --- a/src/commands/Tweet.ts +++ b/src/commands/Tweet.ts @@ -42,22 +42,6 @@ function createTweetCommand(rettiwt: Rettiwt): Command { } }); - // Likers - tweet - .command('likers') - .description('Fetch the list of users who liked the given tweets') - .argument('', 'The id of the tweet') - .argument('[count]', 'The number of likers to fetch') - .argument('[cursor]', 'The cursor to the batch of likers to fetch') - .action(async (id: string, count?: string, cursor?: string) => { - try { - const tweets = await rettiwt.tweet.likers(id, count ? parseInt(count) : undefined, cursor); - output(tweets); - } catch (error) { - output(error); - } - }); - // List tweet .command('list') @@ -129,6 +113,33 @@ function createTweetCommand(rettiwt: Rettiwt): Command { } }); + // Schedule + tweet + .command('schedule') + .description('Schedule a tweet to be posted at a given date/time') + .argument('', 'The text to post as a tweet') + .argument('