{"_id":"@dewicats/agent-twitter-client","name":"@dewicats/agent-twitter-client","dist-tags":{"latest":"0.0.19"},"versions":{"0.0.19":{"author":{"name":"elizaOS"},"dependencies":{"@sinclair/typebox":"^0.32.20","headers-polyfill":"^3.1.2","json-stable-stringify":"^1.0.2","otpauth":"^9.2.2","set-cookie-parser":"^2.6.0","tough-cookie":"^4.1.2","twitter-api-v2":"^1.18.2","undici":"^7.1.1","undici-types":"^7.2.0","ws":"^8.18.0"},"description":"A twitter client for agents","devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@tsconfig/node16":"^16.1.3","@types/jest":"^29.5.1","@types/json-stable-stringify":"^1.0.34","@types/node":"^22.10.2","@types/set-cookie-parser":"^2.4.2","@types/tough-cookie":"^4.0.2","@types/ws":"^8.5.13","@typescript-eslint/eslint-plugin":"^5.59.7","@typescript-eslint/parser":"^5.59.7","dotenv":"^16.4.5","esbuild":"^0.21.5","eslint":"^8.41.0","eslint-config-prettier":"^8.8.0","eslint-plugin-prettier":"^4.2.1","gh-pages":"^5.0.0","jest":"^29.7.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.7","rollup":"^4.18.0","rollup-plugin-dts":"^6.1.1","rollup-plugin-esbuild":"^6.1.1","ts-jest":"^29.1.0","typedoc":"^0.24.7","typescript":"^5.0.4"},"exports":{"default":{"import":"./dist/default/esm/index.mjs","require":"./dist/default/cjs/index.js"},"node":{"import":"./dist/node/esm/index.mjs","require":"./dist/node/cjs/index.cjs"},"types":"./dist/types/index.d.ts"},"keywords":[],"license":"MIT","lint-staged":{"*.{js,ts}":["eslint --cache --fix","prettier --write"]},"main":"dist/default/cjs/index.js","name":"@dewicats/agent-twitter-client","peerDependencies":{"@roamhq/wrtc":"^0.8.0"},"private":false,"scripts":{"build":"rimraf dist && rollup -c","docs:deploy":"npm run docs:generate && gh-pages -d docs","docs:generate":"typedoc --options typedoc.json","format":"prettier --write src/**/*.ts","prepare":"npm run build","test":"jest"},"types":"./dist/types/index.d.ts","version":"0.0.19","_id":"@dewicats/agent-twitter-client@0.0.19","gitHead":"9e492836e9d76d40b34e7f9c8f9be7be03e040ea","_nodeVersion":"20.18.1","_npmVersion":"10.8.2","dist":{"integrity":"sha512-nOwtYSr8XVy/XKsA2RWYOlIATaAFSeK9Zd9mGrE25O0fa31z4VsJL8TdVpB34xgAaRWpPDeEPFupIRWc12prVg==","shasum":"bf5d0680518466436976b3b022cf01b559958345","tarball":"https://registry.npmjs.org/@dewicats/agent-twitter-client/-/agent-twitter-client-0.0.19.tgz","fileCount":83,"unpackedSize":4081178,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIEBxeoja5xwpjk62F4mnGjM6FFK1Kh7fmHp1K0EO2kOYAiEA1nTEE0hW5xJUzJHBPcP3VnXXx/UQMhvKitOr85XhV3Q="}]},"_npmUser":{"name":"peronif5","email":"peronif5@gmail.com"},"directories":{},"maintainers":[{"name":"peronif5","email":"peronif5@gmail.com"},{"name":"marcialcp","email":"marcialandres06@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/agent-twitter-client_0.0.19_1739404263416_0.24661376679315872"},"_hasShrinkwrap":false}},"time":{"created":"2025-02-12T23:51:03.257Z","0.0.19":"2025-02-12T23:51:03.689Z","modified":"2025-02-12T23:51:04.097Z"},"maintainers":[{"name":"peronif5","email":"peronif5@gmail.com"},{"name":"marcialcp","email":"marcialandres06@gmail.com"}],"description":"A twitter client for agents","keywords":[],"author":{"name":"elizaOS"},"license":"MIT","readme":"# agent-twitter-client\n\nThis is a modified version of [@the-convocation/twitter-scraper](https://github.com/the-convocation/twitter-scraper) with added functionality for sending tweets and retweets. This package does not require the Twitter API to use and will run in both the browser and server.\n\n## Installation\n\n```sh\nnpm install agent-twitter-client\n```\n\n## Setup\n\nConfigure environment variables for authentication.\n\n```\nTWITTER_USERNAME=    # Account username\nTWITTER_PASSWORD=    # Account password\nTWITTER_EMAIL=       # Account email\nPROXY_URL=           # HTTP(s) proxy for requests (necessary for browsers)\n\n# Twitter API v2 credentials for tweet and poll functionality\nTWITTER_API_KEY=               # Twitter API Key\nTWITTER_API_SECRET_KEY=        # Twitter API Secret Key\nTWITTER_ACCESS_TOKEN=          # Access Token for Twitter API v2\nTWITTER_ACCESS_TOKEN_SECRET=   # Access Token Secret for Twitter API v2\n```\n\n### Getting Twitter Cookies\n\nIt is important to use Twitter cookies to avoid sending a new login request to Twitter every time you want to perform an action.\n\nIn 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.\n\n```ts\nconst scraper = await getScraper({ authMethod: 'password' });\n\nscraper.getCookies().then((cookies) => {\n  console.log(cookies);\n  // Remove 'Cookies' and save the cookies as a JSON array\n});\n```\n\n## Getting Started\n\n```ts\nconst scraper = new Scraper();\nawait scraper.login('username', 'password');\n\n// If using v2 functionality (currently required to support polls)\nawait scraper.login(\n  'username',\n  'password',\n  'email',\n  'appKey',\n  'appSecret',\n  'accessToken',\n  'accessSecret',\n);\n\nconst tweets = await scraper.getTweets('elonmusk', 10);\nconst tweetsAndReplies = scraper.getTweetsAndReplies('elonmusk');\nconst latestTweet = await scraper.getLatestTweet('elonmusk');\nconst tweet = await scraper.getTweet('1234567890123456789');\nawait scraper.sendTweet('Hello world!');\n\n// Create a poll\nawait scraper.sendTweetV2(\n  `What's got you most hyped? Let us know! 🤖💸`,\n  undefined,\n  {\n    poll: {\n      options: [\n        { label: 'AI Innovations 🤖' },\n        { label: 'Crypto Craze 💸' },\n        { label: 'Both! 🌌' },\n        { label: 'Neither for Me 😅' },\n      ],\n      durationMinutes: 120, // Duration of the poll in minutes\n    },\n  },\n);\n```\n\n### Fetching Specific Tweet Data (V2)\n\n```ts\n// Fetch a single tweet with poll details\nconst tweet = await scraper.getTweetV2('1856441982811529619', {\n  expansions: ['attachments.poll_ids'],\n  pollFields: ['options', 'end_datetime'],\n});\nconsole.log('tweet', tweet);\n\n// Fetch multiple tweets with poll and media details\nconst tweets = await scraper.getTweetsV2(\n  ['1856441982811529619', '1856429655215260130'],\n  {\n    expansions: ['attachments.poll_ids', 'attachments.media_keys'],\n    pollFields: ['options', 'end_datetime'],\n    mediaFields: ['url', 'preview_image_url'],\n  },\n);\nconsole.log('tweets', tweets);\n```\n\n## API\n\n### Authentication\n\n```ts\n// Log in\nawait scraper.login('username', 'password');\n\n// Log out\nawait scraper.logout();\n\n// Check if logged in\nconst isLoggedIn = await scraper.isLoggedIn();\n\n// Get current session cookies\nconst cookies = await scraper.getCookies();\n\n// Set current session cookies\nawait scraper.setCookies(cookies);\n\n// Clear current cookies\nawait scraper.clearCookies();\n```\n\n### Profile\n\n```ts\n// Get a user's profile\nconst profile = await scraper.getProfile('TwitterDev');\n\n// Get a user ID from their screen name\nconst userId = await scraper.getUserIdByScreenName('TwitterDev');\n\n// Get logged-in user's profile\nconst me = await scraper.me();\n```\n\n### Search\n\n```ts\nimport { SearchMode } from 'agent-twitter-client';\n\n// Search for recent tweets\nconst tweets = scraper.searchTweets('#nodejs', 20, SearchMode.Latest);\n\n// Search for profiles\nconst profiles = scraper.searchProfiles('John', 10);\n\n// Fetch a page of tweet results\nconst results = await scraper.fetchSearchTweets('#nodejs', 20, SearchMode.Top);\n\n// Fetch a page of profile results\nconst profileResults = await scraper.fetchSearchProfiles('John', 10);\n```\n\n### Relationships\n\n```ts\n// Get a user's followers\nconst followers = scraper.getFollowers('12345', 100);\n\n// Get who a user is following\nconst following = scraper.getFollowing('12345', 100);\n\n// Fetch a page of a user's followers\nconst followerResults = await scraper.fetchProfileFollowers('12345', 100);\n\n// Fetch a page of who a user is following\nconst followingResults = await scraper.fetchProfileFollowing('12345', 100);\n\n// Follow a user\nconst followUserResults = await scraper.followUser('elonmusk');\n```\n\n### Trends\n\n```ts\n// Get current trends\nconst trends = await scraper.getTrends();\n\n// Fetch tweets from a list\nconst listTweets = await scraper.fetchListTweets('1234567890', 50);\n```\n\n### Tweets\n\n```ts\n// Get a user's tweets\nconst tweets = scraper.getTweets('TwitterDev');\n\n// Fetch the home timeline\nconst homeTimeline = await scraper.fetchHomeTimeline(10, ['seenTweetId1','seenTweetId2']);\n\n// Get a user's liked tweets\nconst likedTweets = scraper.getLikedTweets('TwitterDev');\n\n// Get a user's tweets and replies\nconst tweetsAndReplies = scraper.getTweetsAndReplies('TwitterDev');\n\n// Get tweets matching specific criteria\nconst timeline = scraper.getTweets('TwitterDev', 100);\nconst retweets = await scraper.getTweetsWhere(\n  timeline,\n  (tweet) => tweet.isRetweet,\n);\n\n// Get a user's latest tweet\nconst latestTweet = await scraper.getLatestTweet('TwitterDev');\n\n// Get a specific tweet by ID\nconst tweet = await scraper.getTweet('1234567890123456789');\n\n// Send a tweet\nconst sendTweetResults = await scraper.sendTweet('Hello world!');\n\n// Send a quote tweet - Media files are optional\nconst sendQuoteTweetResults = await scraper.sendQuoteTweet(\n  'Hello world!',\n  '1234567890123456789',\n  ['mediaFile1', 'mediaFile2'],\n);\n\n// Retweet a tweet\nconst retweetResults = await scraper.retweet('1234567890123456789');\n\n// Like a tweet\nconst likeTweetResults = await scraper.likeTweet('1234567890123456789');\n```\n\n## Sending Tweets with Media\n\n### Media Handling\n\nThe scraper requires media files to be processed into a specific format before sending:\n\n- Media must be converted to Buffer format\n- Each media file needs its MIME type specified\n- This helps the scraper distinguish between image and video processing models\n\n### Basic Tweet with Media\n\n```ts\n// Example: Sending a tweet with media attachments\nconst mediaData = [\n  {\n    data: fs.readFileSync('path/to/image.jpg'),\n    mediaType: 'image/jpeg',\n  },\n  {\n    data: fs.readFileSync('path/to/video.mp4'),\n    mediaType: 'video/mp4',\n  },\n];\n\nawait scraper.sendTweet('Hello world!', undefined, mediaData);\n```\n\n### Supported Media Types\n\n```ts\n// Image formats and their MIME types\nconst imageTypes = {\n  '.jpg': 'image/jpeg',\n  '.jpeg': 'image/jpeg',\n  '.png': 'image/png',\n  '.gif': 'image/gif',\n};\n\n// Video format\nconst videoTypes = {\n  '.mp4': 'video/mp4',\n};\n```\n\n### Media Upload Limitations\n\n- Maximum 4 images per tweet\n- Only 1 video per tweet\n- Maximum video file size: 512MB\n- Supported image formats: JPG, PNG, GIF\n- Supported video format: MP4\n\n## Grok Integration\n\nThis 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)\n\n### Basic Usage\n\n```ts\nconst scraper = new Scraper();\nawait scraper.login('username', 'password');\n\n// Start a new conversation\nconst response = await scraper.grokChat({\n  messages: [{ role: 'user', content: 'What are your thoughts on AI?' }],\n});\n\nconsole.log(response.message); // Grok's response\nconsole.log(response.messages); // Full conversation history\n```\n\nIf no `conversationId` is provided, the client will automatically create a new conversation.\n\n### Handling Rate Limits\n\nGrok has rate limits of 25 messages every 2 hours for non-premium accounts. The client provides rate limit information in the response:\n\n```ts\nconst response = await scraper.grokChat({\n  messages: [{ role: 'user', content: 'Hello!' }],\n});\n\nif (response.rateLimit?.isRateLimited) {\n  console.log(response.rateLimit.message);\n  console.log(response.rateLimit.upsellInfo); // Premium upgrade information\n}\n```\n\n### Response Types\n\nThe Grok integration includes TypeScript types for better development experience:\n\n```ts\ninterface GrokChatOptions {\n  messages: GrokMessage[];\n  conversationId?: string;\n  returnSearchResults?: boolean;\n  returnCitations?: boolean;\n}\n\ninterface GrokChatResponse {\n  conversationId: string;\n  message: string;\n  messages: GrokMessage[];\n  webResults?: any[];\n  metadata?: any;\n  rateLimit?: GrokRateLimit;\n}\n```\n\n### Advanced Usage\n\n```ts\nconst response = await scraper.grokChat({\n  messages: [{ role: 'user', content: 'Research quantum computing' }],\n  returnSearchResults: true, // Include web search results\n  returnCitations: true, // Include citations for information\n});\n\n// Access web results if available\nif (response.webResults) {\n  console.log('Sources:', response.webResults);\n}\n\n// Full conversation with history\nconsole.log('Conversation:', response.messages);\n```\n\n### Limitations\n\n- Message history prefilling is currently limited due to unofficial API usage\n- Rate limits are enforced (25 messages/2 hours for non-premium)\n","readmeFilename":"README.md"}