Skip to content

Commit

Permalink
Merge pull request #604 from Rishikant181/dev
Browse files Browse the repository at this point in the history
v4.0.0
  • Loading branch information
Rishikant181 authored Sep 3, 2024
2 parents aa88dc9 + 7966833 commit 82b4e93
Show file tree
Hide file tree
Showing 23 changed files with 3,951 additions and 2,205 deletions.
4 changes: 4 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# These are supported funding model platforms

# github: [Rishikant181]
custom: ['https://paypal.me/Rishikant181?country.x=IN&locale.x=en_GB']
5 changes: 5 additions & 0 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ jobs:
node-version: '20.x'
registry-url: https://registry.npmjs.org/

# Enabling CorePack
- run: corepack enable

# Installing dependencies
- run: yarn

Expand Down
1 change: 1 addition & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
60 changes: 50 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,41 @@ 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:

- '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
Expand All @@ -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 "<email>" "<username>" "<password>"`
Expand All @@ -65,7 +97,11 @@ By default, Rettiwt-API uses 'guest' authentication. If however, access to the f
- `<username>` is the username associated with the Twitter account.
- `<password>` 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

Expand All @@ -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!

Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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).
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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!",
Expand Down Expand Up @@ -28,14 +28,15 @@
"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",
"class-validator": "0.14.1",
"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",
Expand Down
9 changes: 6 additions & 3 deletions src/collections/Extractors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import {
IListTweetsResponse,
ITweetDetailsResponse,
ITweetLikeResponse,
ITweetLikersResponse,
ITweetPostResponse,
ITweetRepliesResponse,
ITweetRetweetersResponse,
ITweetRetweetResponse,
ITweetScheduleResponse,
ITweetSearchResponse,
ITweetUnlikeResponse,
ITweetUnpostResponse,
ITweetUnretweetResponse,
ITweetUnscheduleResponse,
IUserDetailsResponse,
IUserFollowedResponse,
IUserFollowersResponse,
Expand Down Expand Up @@ -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<User> =>
new CursoredData<User>(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<User> =>
new CursoredData<User>(response, EBaseType.USER),
TWEET_SCHEDULE: (response: ITweetScheduleResponse): string => response?.data?.tweet?.rest_id ?? undefined,
TWEET_SEARCH: (response: ITweetSearchResponse): CursoredData<Tweet> =>
new CursoredData<Tweet>(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),
Expand Down
4 changes: 3 additions & 1 deletion src/collections/Groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
];
4 changes: 3 additions & 1 deletion src/collections/Requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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!),
Expand Down
61 changes: 43 additions & 18 deletions src/commands/Tweet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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('<id>', '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')
Expand Down Expand Up @@ -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('<text>', 'The text to post as a tweet')
.argument('<time>', 'The date/time at which the tweet is to be scheduled (valid date/time string)')
.option('-m, --media [string]', 'Comma-separated list of ids of the media item(s) to be posted')
.option('-q, --quote [string]', 'The id of the tweet to quote in the tweet to be posted')
.option(
'-r, --reply [string]',
'The id of the tweet to which the reply is to be made, if the tweet is to be a reply',
)
.action(async (text: string, time: string, options?: { media?: string; quote?: string; reply?: string }) => {
try {
const result = await rettiwt.tweet.schedule({
text: text,
media: options?.media ? options?.media.split(',').map((item) => ({ id: item })) : undefined,
quote: options?.quote,
replyTo: options?.reply,
scheduleFor: new Date(time),
});
output(result);
} catch (error) {
output(error);
}
});

// Search
tweet
.command('search')
Expand Down Expand Up @@ -158,8 +169,8 @@ function createTweetCommand(rettiwt: Rettiwt): Command {
.option('-q, --quoted <string>', '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 <string>', 'Matches the tweets made since the given date (valid date string)')
.option('-e, --end <string>', 'Matches the tweets made upto the given date (valid date string)')
.option('-s, --start <string>', 'Matches the tweets made since the given date (valid date/time string)')
.option('-e, --end <string>', 'Matches the tweets made upto the given date (valid date/time string)')
.option('--stream', 'Stream the filtered tweets in pseudo-realtime')
.option('-i, --interval <number>', 'The polling interval (in ms) to use for streaming. Default is 60000')
.action(async (count?: string, cursor?: string, options?: TweetSearchOptions) => {
Expand Down Expand Up @@ -229,6 +240,20 @@ function createTweetCommand(rettiwt: Rettiwt): Command {
}
});

// Unschedule
tweet
.command('unschedule')
.description('Unschedule a tweet')
.argument('<id>', 'The id of the tweet')
.action(async (id: string) => {
try {
const result = await rettiwt.tweet.unschedule(id);
output(result);
} catch (error) {
output(error);
}
});

// Upload
tweet
.command('upload')
Expand Down
7 changes: 3 additions & 4 deletions src/commands/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,12 @@ function createUserCommand(rettiwt: Rettiwt): Command {

// Likes
user.command('likes')
.description('Fetch the list of tweets liked by the given user')
.argument('<id>', 'The id of the user')
.description('Fetch your list of liked tweet')
.argument('[count]', 'The number of liked tweets to fetch')
.argument('[cursor]', 'The cursor to the batch of liked tweets to fetch')
.action(async (id: string, count?: string, cursor?: string) => {
.action(async (count?: string, cursor?: string) => {
try {
const tweets = await rettiwt.user.likes(id, count ? parseInt(count) : undefined, cursor);
const tweets = await rettiwt.user.likes(count ? parseInt(count) : undefined, cursor);
output(tweets);
} catch (error) {
output(error);
Expand Down
Loading

0 comments on commit 82b4e93

Please sign in to comment.