Skip to content

Commit fdfdba6

Browse files
committed
add readme
1 parent 2afb5a3 commit fdfdba6

File tree

1 file changed

+359
-0
lines changed

1 file changed

+359
-0
lines changed
Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
# agent-twitter-client
2+
3+
## Setup
4+
5+
Configure environment variables for authentication.
6+
7+
```
8+
TWITTER_USERNAME= # Account username
9+
TWITTER_PASSWORD= # Account password
10+
TWITTER_EMAIL= # Account email
11+
PROXY_URL= # HTTP(s) proxy for requests (necessary for browsers)
12+
13+
# Twitter API v2 credentials for tweet and poll functionality
14+
TWITTER_API_KEY= # Twitter API Key
15+
TWITTER_API_SECRET_KEY= # Twitter API Secret Key
16+
TWITTER_ACCESS_TOKEN= # Access Token for Twitter API v2
17+
TWITTER_ACCESS_TOKEN_SECRET= # Access Token Secret for Twitter API v2
18+
```
19+
20+
### Getting Twitter Cookies
21+
22+
It is important to use Twitter cookies to avoid sending a new login request to Twitter every time you want to perform an action.
23+
24+
In your application, you will likely want to check for existing cookies. If cookies are not available, log in with user authentication credentials and cache the cookies for future use.
25+
26+
```ts
27+
const scraper = await getScraper({ authMethod: 'password' });
28+
29+
scraper.getCookies().then((cookies) => {
30+
console.log(cookies);
31+
// Remove 'Cookies' and save the cookies as a JSON array
32+
});
33+
```
34+
35+
## Getting Started
36+
37+
```ts
38+
const scraper = new Scraper();
39+
await scraper.login('username', 'password');
40+
41+
// If using v2 functionality (currently required to support polls)
42+
await scraper.login(
43+
'username',
44+
'password',
45+
'email',
46+
'appKey',
47+
'appSecret',
48+
'accessToken',
49+
'accessSecret',
50+
);
51+
52+
const tweets = await scraper.getTweets('elonmusk', 10);
53+
const tweetsAndReplies = scraper.getTweetsAndReplies('elonmusk');
54+
const latestTweet = await scraper.getLatestTweet('elonmusk');
55+
const tweet = await scraper.getTweet('1234567890123456789');
56+
await scraper.sendTweet('Hello world!');
57+
58+
// Create a poll
59+
await scraper.sendTweetV2(
60+
`What's got you most hyped? Let us know! 🤖💸`,
61+
undefined,
62+
{
63+
poll: {
64+
options: [
65+
{ label: 'AI Innovations 🤖' },
66+
{ label: 'Crypto Craze 💸' },
67+
{ label: 'Both! 🌌' },
68+
{ label: 'Neither for Me 😅' },
69+
],
70+
durationMinutes: 120, // Duration of the poll in minutes
71+
},
72+
},
73+
);
74+
```
75+
76+
### Fetching Specific Tweet Data (V2)
77+
78+
```ts
79+
// Fetch a single tweet with poll details
80+
const tweet = await scraper.getTweetV2('1856441982811529619', {
81+
expansions: ['attachments.poll_ids'],
82+
pollFields: ['options', 'end_datetime'],
83+
});
84+
console.log('tweet', tweet);
85+
86+
// Fetch multiple tweets with poll and media details
87+
const tweets = await scraper.getTweetsV2(
88+
['1856441982811529619', '1856429655215260130'],
89+
{
90+
expansions: ['attachments.poll_ids', 'attachments.media_keys'],
91+
pollFields: ['options', 'end_datetime'],
92+
mediaFields: ['url', 'preview_image_url'],
93+
},
94+
);
95+
console.log('tweets', tweets);
96+
```
97+
98+
## API
99+
100+
### Authentication
101+
102+
```ts
103+
// Log in
104+
await scraper.login('username', 'password');
105+
106+
// Log out
107+
await scraper.logout();
108+
109+
// Check if logged in
110+
const isLoggedIn = await scraper.isLoggedIn();
111+
112+
// Get current session cookies
113+
const cookies = await scraper.getCookies();
114+
115+
// Set current session cookies
116+
await scraper.setCookies(cookies);
117+
118+
// Clear current cookies
119+
await scraper.clearCookies();
120+
```
121+
122+
### Profile
123+
124+
```ts
125+
// Get a user's profile
126+
const profile = await scraper.getProfile('TwitterDev');
127+
128+
// Get a user ID from their screen name
129+
const userId = await scraper.getUserIdByScreenName('TwitterDev');
130+
131+
// Get logged-in user's profile
132+
const me = await scraper.me();
133+
```
134+
135+
### Search
136+
137+
```ts
138+
import { SearchMode } from 'agent-twitter-client';
139+
140+
// Search for recent tweets
141+
const tweets = scraper.searchTweets('#nodejs', 20, SearchMode.Latest);
142+
143+
// Search for profiles
144+
const profiles = scraper.searchProfiles('John', 10);
145+
146+
// Fetch a page of tweet results
147+
const results = await scraper.fetchSearchTweets('#nodejs', 20, SearchMode.Top);
148+
149+
// Fetch a page of profile results
150+
const profileResults = await scraper.fetchSearchProfiles('John', 10);
151+
```
152+
153+
### Relationships
154+
155+
```ts
156+
// Get a user's followers
157+
const followers = scraper.getFollowers('12345', 100);
158+
159+
// Get who a user is following
160+
const following = scraper.getFollowing('12345', 100);
161+
162+
// Fetch a page of a user's followers
163+
const followerResults = await scraper.fetchProfileFollowers('12345', 100);
164+
165+
// Fetch a page of who a user is following
166+
const followingResults = await scraper.fetchProfileFollowing('12345', 100);
167+
168+
// Follow a user
169+
const followUserResults = await scraper.followUser('elonmusk');
170+
```
171+
172+
### Trends
173+
174+
```ts
175+
// Get current trends
176+
const trends = await scraper.getTrends();
177+
178+
// Fetch tweets from a list
179+
const listTweets = await scraper.fetchListTweets('1234567890', 50);
180+
```
181+
182+
### Tweets
183+
184+
```ts
185+
// Get a user's tweets
186+
const tweets = scraper.getTweets('TwitterDev');
187+
188+
// Fetch the home timeline
189+
const homeTimeline = await scraper.fetchHomeTimeline(10, ['seenTweetId1','seenTweetId2']);
190+
191+
// Get a user's liked tweets
192+
const likedTweets = scraper.getLikedTweets('TwitterDev');
193+
194+
// Get a user's tweets and replies
195+
const tweetsAndReplies = scraper.getTweetsAndReplies('TwitterDev');
196+
197+
// Get tweets matching specific criteria
198+
const timeline = scraper.getTweets('TwitterDev', 100);
199+
const retweets = await scraper.getTweetsWhere(
200+
timeline,
201+
(tweet) => tweet.isRetweet,
202+
);
203+
204+
// Get a user's latest tweet
205+
const latestTweet = await scraper.getLatestTweet('TwitterDev');
206+
207+
// Get a specific tweet by ID
208+
const tweet = await scraper.getTweet('1234567890123456789');
209+
210+
// Send a tweet
211+
const sendTweetResults = await scraper.sendTweet('Hello world!');
212+
213+
// Send a quote tweet - Media files are optional
214+
const sendQuoteTweetResults = await scraper.sendQuoteTweet(
215+
'Hello world!',
216+
'1234567890123456789',
217+
['mediaFile1', 'mediaFile2'],
218+
);
219+
220+
// Retweet a tweet
221+
const retweetResults = await scraper.retweet('1234567890123456789');
222+
223+
// Like a tweet
224+
const likeTweetResults = await scraper.likeTweet('1234567890123456789');
225+
```
226+
227+
## Sending Tweets with Media
228+
229+
### Media Handling
230+
231+
The scraper requires media files to be processed into a specific format before sending:
232+
233+
- Media must be converted to Buffer format
234+
- Each media file needs its MIME type specified
235+
- This helps the scraper distinguish between image and video processing models
236+
237+
### Basic Tweet with Media
238+
239+
```ts
240+
// Example: Sending a tweet with media attachments
241+
const mediaData = [
242+
{
243+
data: fs.readFileSync('path/to/image.jpg'),
244+
mediaType: 'image/jpeg',
245+
},
246+
{
247+
data: fs.readFileSync('path/to/video.mp4'),
248+
mediaType: 'video/mp4',
249+
},
250+
];
251+
252+
await scraper.sendTweet('Hello world!', undefined, mediaData);
253+
```
254+
255+
### Supported Media Types
256+
257+
```ts
258+
// Image formats and their MIME types
259+
const imageTypes = {
260+
'.jpg': 'image/jpeg',
261+
'.jpeg': 'image/jpeg',
262+
'.png': 'image/png',
263+
'.gif': 'image/gif',
264+
};
265+
266+
// Video format
267+
const videoTypes = {
268+
'.mp4': 'video/mp4',
269+
};
270+
```
271+
272+
### Media Upload Limitations
273+
274+
- Maximum 4 images per tweet
275+
- Only 1 video per tweet
276+
- Maximum video file size: 512MB
277+
- Supported image formats: JPG, PNG, GIF
278+
- Supported video format: MP4
279+
280+
## Grok Integration
281+
282+
This client provides programmatic access to Grok through Twitter's interface, offering a unique capability that even Grok's official API cannot match - access to real-time Twitter data. While Grok has a standalone API, only by interacting with Grok through Twitter can you leverage its ability to analyze and respond to live Twitter content. This makes it the only way to programmatically access an LLM with direct insight into Twitter's real-time information. [@grokkyAi](https://x.com/grokkyAi)
283+
284+
### Basic Usage
285+
286+
```ts
287+
const scraper = new Scraper();
288+
await scraper.login('username', 'password');
289+
290+
// Start a new conversation
291+
const response = await scraper.grokChat({
292+
messages: [{ role: 'user', content: 'What are your thoughts on AI?' }],
293+
});
294+
295+
console.log(response.message); // Grok's response
296+
console.log(response.messages); // Full conversation history
297+
```
298+
299+
If no `conversationId` is provided, the client will automatically create a new conversation.
300+
301+
### Handling Rate Limits
302+
303+
Grok has rate limits of 25 messages every 2 hours for non-premium accounts. The client provides rate limit information in the response:
304+
305+
```ts
306+
const response = await scraper.grokChat({
307+
messages: [{ role: 'user', content: 'Hello!' }],
308+
});
309+
310+
if (response.rateLimit?.isRateLimited) {
311+
console.log(response.rateLimit.message);
312+
console.log(response.rateLimit.upsellInfo); // Premium upgrade information
313+
}
314+
```
315+
316+
### Response Types
317+
318+
The Grok integration includes TypeScript types for better development experience:
319+
320+
```ts
321+
interface GrokChatOptions {
322+
messages: GrokMessage[];
323+
conversationId?: string;
324+
returnSearchResults?: boolean;
325+
returnCitations?: boolean;
326+
}
327+
328+
interface GrokChatResponse {
329+
conversationId: string;
330+
message: string;
331+
messages: GrokMessage[];
332+
webResults?: any[];
333+
metadata?: any;
334+
rateLimit?: GrokRateLimit;
335+
}
336+
```
337+
338+
### Advanced Usage
339+
340+
```ts
341+
const response = await scraper.grokChat({
342+
messages: [{ role: 'user', content: 'Research quantum computing' }],
343+
returnSearchResults: true, // Include web search results
344+
returnCitations: true, // Include citations for information
345+
});
346+
347+
// Access web results if available
348+
if (response.webResults) {
349+
console.log('Sources:', response.webResults);
350+
}
351+
352+
// Full conversation with history
353+
console.log('Conversation:', response.messages);
354+
```
355+
356+
### Limitations
357+
358+
- Message history prefilling is currently limited due to unofficial API usage
359+
- Rate limits are enforced (25 messages/2 hours for non-premium)

0 commit comments

Comments
 (0)