Created
September 20, 2025 18:15
-
-
Save nschwermann/dbb6fdc2f89674c6141b30225a526175 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Twitter API v2 - Post a Tweet with GIF Example | |
| * | |
| * This example demonstrates how to post a tweet with a GIF attachment | |
| * using the twitter-api-v2 library and Twitter's v2 API endpoints. | |
| * | |
| * Prerequisites: | |
| * 1. Twitter Developer Account | |
| * 2. App with Read and Write permissions | |
| * 3. API Keys and Access Tokens | |
| * 4. npm install twitter-api-v2 dotenv | |
| */ | |
| import { TwitterApi } from 'twitter-api-v2'; | |
| import fs from 'fs/promises'; | |
| import dotenv from 'dotenv'; | |
| // Load environment variables | |
| dotenv.config(); | |
| // Initialize Twitter client with v1.1 and v2 access | |
| const client = new TwitterApi({ | |
| appKey: process.env.TWITTER_API_KEY, | |
| appSecret: process.env.TWITTER_API_SECRET, | |
| accessToken: process.env.TWITTER_ACCESS_TOKEN, | |
| accessSecret: process.env.TWITTER_ACCESS_TOKEN_SECRET, | |
| }); | |
| // Get read/write client | |
| const rwClient = client.readWrite; | |
| /** | |
| * Upload a GIF to Twitter and get media ID | |
| * @param {string} gifPath - Path to the GIF file | |
| * @returns {Promise<string>} Media ID for the uploaded GIF | |
| */ | |
| async function uploadGif(gifPath) { | |
| try { | |
| console.log(`π€ Uploading GIF from: ${gifPath}`); | |
| // Read the GIF file as a buffer | |
| const gifBuffer = await fs.readFile(gifPath); | |
| // Get file stats for size validation | |
| const stats = await fs.stat(gifPath); | |
| const fileSizeMB = stats.size / (1024 * 1024); | |
| // Twitter's GIF size limit is 15MB | |
| if (fileSizeMB > 15) { | |
| throw new Error(`GIF size (${fileSizeMB.toFixed(2)}MB) exceeds Twitter's 15MB limit`); | |
| } | |
| console.log(`π GIF size: ${fileSizeMB.toFixed(2)}MB`); | |
| // Upload the GIF using v1.1 media upload endpoint | |
| // Note: v1.1 endpoint is still required for media uploads | |
| const mediaId = await client.v1.uploadMedia(gifBuffer, { | |
| mimeType: 'image/gif', | |
| // Optional: Add media category for better processing | |
| additionalOwners: undefined, | |
| mediaCategory: 'tweet_gif' | |
| }); | |
| console.log(`β GIF uploaded successfully! Media ID: ${mediaId}`); | |
| return mediaId; | |
| } catch (error) { | |
| console.error('β Error uploading GIF:', error.message); | |
| throw error; | |
| } | |
| } | |
| /** | |
| * Post a tweet with GIF attachment | |
| * @param {string} tweetText - The text content of the tweet | |
| * @param {string} gifPath - Path to the GIF file | |
| * @returns {Promise<object>} Tweet response object | |
| */ | |
| async function postTweetWithGif(tweetText, gifPath) { | |
| try { | |
| // Step 1: Upload the GIF and get media ID | |
| const mediaId = await uploadGif(gifPath); | |
| // Step 2: Post the tweet with the media attachment using v2 endpoint | |
| console.log('π Posting tweet with GIF...'); | |
| const tweet = await rwClient.v2.tweet({ | |
| text: tweetText, | |
| media: { | |
| media_ids: [mediaId] | |
| } | |
| }); | |
| console.log('β Tweet posted successfully!'); | |
| console.log(`π Tweet ID: ${tweet.data.id}`); | |
| console.log(`π± View tweet: https://twitter.com/i/web/status/${tweet.data.id}`); | |
| return tweet.data; | |
| } catch (error) { | |
| console.error('β Error posting tweet:', error.message); | |
| // Handle specific Twitter API errors | |
| if (error.code === 'ENOENT') { | |
| console.error('File not found. Please check the GIF path.'); | |
| } else if (error.data?.errors) { | |
| error.data.errors.forEach(err => { | |
| console.error(`Twitter API Error: ${err.message}`); | |
| }); | |
| } | |
| throw error; | |
| } | |
| } | |
| /** | |
| * Verify Twitter connection and permissions | |
| * @returns {Promise<boolean>} True if connection is successful | |
| */ | |
| async function verifyConnection() { | |
| try { | |
| const user = await rwClient.v2.me(); | |
| console.log(`β Connected as @${user.data.username}`); | |
| console.log(`π€ Name: ${user.data.name}`); | |
| return true; | |
| } catch (error) { | |
| console.error('β Failed to connect to Twitter:', error.message); | |
| return false; | |
| } | |
| } | |
| /** | |
| * Main execution function | |
| */ | |
| async function main() { | |
| console.log('π¦ Twitter GIF Posting Example'); | |
| console.log('================================\n'); | |
| // Verify connection first | |
| const connected = await verifyConnection(); | |
| if (!connected) { | |
| console.error('Please check your API credentials in .env file'); | |
| process.exit(1); | |
| } | |
| console.log('\n'); | |
| // Example usage | |
| const tweetText = "π Check out this amazing animated GIF! #TwitterAPI #NodeJS"; | |
| const gifPath = "./output/animation.gif"; // Path to your GIF file | |
| try { | |
| const tweet = await postTweetWithGif(tweetText, gifPath); | |
| console.log('\n⨠Success! Your tweet with GIF has been posted.'); | |
| console.log('Tweet data:', JSON.stringify(tweet, null, 2)); | |
| } catch (error) { | |
| console.error('\nπ₯ Failed to post tweet'); | |
| process.exit(1); | |
| } | |
| } | |
| // Execute if run directly | |
| if (import.meta.url === `file://${process.argv[1]}`) { | |
| main().catch(console.error); | |
| } | |
| // Export functions for use as a module | |
| export { uploadGif, postTweetWithGif, verifyConnection }; | |
| /** | |
| * Environment Variables (.env file): | |
| * | |
| * TWITTER_API_KEY=your_api_key_here | |
| * TWITTER_API_SECRET=your_api_secret_here | |
| * TWITTER_ACCESS_TOKEN=your_access_token_here | |
| * TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret_here | |
| * | |
| * Important Notes: | |
| * | |
| * 1. GIF Requirements: | |
| * - Maximum file size: 15MB | |
| * - Supported formats: GIF | |
| * - Recommended dimensions: 1280x720 or smaller | |
| * - Frame rate: 30fps or less for best results | |
| * | |
| * 2. API Rate Limits: | |
| * - Media upload: 415 requests per 15 minutes (app auth) | |
| * - Tweet posting: 200 requests per 15 minutes (user auth) | |
| * | |
| * 3. Permissions: | |
| * - Your Twitter app must have "Read and Write" permissions | |
| * - Set in Twitter Developer Portal > App Settings > User authentication settings | |
| * | |
| * 4. Error Handling: | |
| * - Always validate GIF size before upload | |
| * - Implement retry logic for transient failures | |
| * - Log errors for debugging | |
| * | |
| * 5. Best Practices: | |
| * - Optimize GIF file size using tools like ffmpeg | |
| * - Use appropriate error messages for user feedback | |
| * - Store media IDs if posting multiple times | |
| * - Clean up local files after successful upload | |
| */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment