-
Notifications
You must be signed in to change notification settings - Fork 27
Upload media data is available with Twitter-API-v2 #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 1.x
Are you sure you want to change the base?
Conversation
WalkthroughReplaces a no-op warning with functional media upload logic in Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Tweet Client
participant Media Upload API
participant Tweet Payload
User->>Tweet Client: createCreateTweetRequest(mediaData)
rect rgb(200, 230, 255)
Note over Tweet Client: Media Upload Flow
loop For each media item (max 4)
Tweet Client->>Tweet Client: Infer MIME type from mediaType
Tweet Client->>Media Upload API: Upload media (POST /2/media/upload)
Media Upload API-->>Tweet Client: Return media_id
end
end
rect rgb(200, 255, 220)
Note over Tweet Client: Attach to Tweet
Tweet Client->>Tweet Client: Collect all media IDs
Tweet Client->>Tweet Payload: Set tweetConfig.media.media_ids
Tweet Payload-->>User: Tweet with attached media
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes The change introduces new media upload logic with MIME type inference and multiple item handling confined to one file, requiring careful review of type mapping logic and media collection flow, but remaining focused in scope. Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to data retention organization setting
📒 Files selected for processing (1)
src/client/tweets.ts(2 hunks)
🔇 Additional comments (1)
src/client/tweets.ts (1)
666-675: No changes needed; implementation and comment are accurate.Twitter API v2 doesn't have a separate endpoint for long-form "Notes" posts, and long-form content is delivered through the regular Posts/Tweets flows. The function correctly delegates to
createCreateTweetRequestwithout modification, and the accompanying comment accurately reflects the API's behavior.
| tweetConfig.media = { | ||
| media_ids:[""] | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Invalid media_ids initialization will cause API errors.
Initializing media_ids with an empty string [""] is incorrect. If no media items are successfully uploaded (e.g., all fail or mediaData becomes empty after filtering), the tweet request will include an invalid media ID, causing the Twitter API to reject the request.
Apply this diff to fix the initialization:
- tweetConfig.media = {
- media_ids:[""]
- };
- let media_ids: string[] = [];
+ const media_ids: string[] = [];Then, only add the media property to tweetConfig after successfully collecting media IDs (move lines 589-591 after the media upload loop and make it conditional).
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/client/tweets.ts around lines 589-591, the code sets
tweetConfig.media.media_ids = [""], which injects an invalid empty-string media
ID and will cause Twitter API errors; remove that initialization and instead,
after the media upload loop, if you have at least one successful media ID (e.g.,
mediaIds.length > 0), set tweetConfig.media = { media_ids: mediaIds } so the
media property is only added when there are valid uploaded IDs; ensure mediaIds
is collected during the loop and only used here.
| let type: EUploadMimeType = EUploadMimeType.Jpeg; | ||
| switch(media.mediaType){ | ||
| case "image": | ||
| case "jpg": | ||
| break; | ||
| case "png": | ||
| type = EUploadMimeType.Png; | ||
| break; | ||
| case "video": | ||
| case "mp4": | ||
| type = EUploadMimeType.Mp4; | ||
| break; | ||
| case "quicktime": | ||
| type = EUploadMimeType.Mov; | ||
| break; | ||
| case "text": | ||
| case "plain": | ||
| type = EUploadMimeType.Srt; | ||
| break; | ||
| case "gif": | ||
| type = EUploadMimeType.Gif; | ||
| break; | ||
| default: | ||
| break; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing import and fragile MIME type detection.
The code references EUploadMimeType but this enum is not imported from twitter-api-v2. Additionally, the mediaType string matching is fragile—unexpected values silently default to JPEG, which could cause upload failures for unsupported formats.
Apply this diff to add the import:
import type {
ApiV2Includes,
MediaObjectV2,
PlaceV2,
PollV2,
TTweetv2Expansion,
TTweetv2MediaField,
TTweetv2PlaceField,
TTweetv2PollField,
TTweetv2TweetField,
TTweetv2UserField,
TweetV2,
UserV2,
} from "twitter-api-v2";
+import { EUploadMimeType } from "twitter-api-v2";Consider improving the type detection logic with better validation:
let type: EUploadMimeType;
const normalizedType = media.mediaType.toLowerCase();
if (normalizedType.includes('png')) {
type = EUploadMimeType.Png;
} else if (normalizedType.includes('gif')) {
type = EUploadMimeType.Gif;
} else if (normalizedType.includes('mp4') || normalizedType === 'video') {
type = EUploadMimeType.Mp4;
} else if (normalizedType.includes('quicktime') || normalizedType.includes('mov')) {
type = EUploadMimeType.Mov;
} else if (normalizedType.includes('srt') || normalizedType === 'text/plain') {
type = EUploadMimeType.Srt;
} else if (normalizedType.includes('jpeg') || normalizedType.includes('jpg') || normalizedType === 'image') {
type = EUploadMimeType.Jpeg;
} else {
throw new Error(`Unsupported media type: ${media.mediaType}`);
}🤖 Prompt for AI Agents
In src/client/tweets.ts around lines 596 to 620, the code uses EUploadMimeType
without importing it and the current switch on media.mediaType silently falls
back to JPEG for unexpected values; import EUploadMimeType from 'twitter-api-v2'
and replace the fragile switch with robust normalization and validation:
normalize media.mediaType to lowercase, perform inclusive checks (e.g., contains
'png', 'gif', 'mp4' or equals 'video', contains 'quicktime'/'mov', contains
'srt' or equals 'text/plain', contains 'jpeg'/'jpg' or equals 'image') to map to
the correct EUploadMimeType, and throw an explicit error for unsupported types
instead of defaulting to JPEG so invalid media is caught early.
| const id = await v2.uploadMedia(media.data, {media_type: type}); | ||
| media_ids.push(id); | ||
| if(i === 3){ // max 4 media in a post | ||
| break; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for media upload failures.
The uploadMedia call can fail (network issues, invalid format, size limits, etc.), but there's no try-catch block to handle failures gracefully. A single upload failure will abort the entire tweet creation.
Consider wrapping the upload in a try-catch to handle individual failures:
- const id = await v2.uploadMedia(media.data, {media_type: type});
- media_ids.push(id);
- if(i === 3){ // max 4 media in a post
- break;
- }
+ try {
+ const id = await v2.uploadMedia(media.data, { media_type: type });
+ media_ids.push(id);
+ if (media_ids.length === 4) { // max 4 media in a post
+ break;
+ }
+ } catch (error) {
+ console.error(`Failed to upload media ${i}:`, error);
+ // Optionally: continue to try other media items, or throw to fail the entire operation
+ }Note: Also improved the loop termination to check media_ids.length instead of the iterator, which is clearer.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const id = await v2.uploadMedia(media.data, {media_type: type}); | |
| media_ids.push(id); | |
| if(i === 3){ // max 4 media in a post | |
| break; | |
| } | |
| try { | |
| const id = await v2.uploadMedia(media.data, { media_type: type }); | |
| media_ids.push(id); | |
| if (media_ids.length === 4) { // max 4 media in a post | |
| break; | |
| } | |
| } catch (error) { | |
| console.error(`Failed to upload media ${i}:`, error); | |
| // Optionally: continue to try other media items, or throw to fail the entire operation | |
| } |
🤖 Prompt for AI Agents
In src/client/tweets.ts around lines 621 to 625, the call to v2.uploadMedia is
unprotected so a single upload failure will abort the entire tweet flow; wrap
the uploadMedia call in a try-catch, only push the returned id into media_ids on
success, log or record the specific error (with context: which media index and
type), skip the failed media and continue attempting the rest, and change the
loop termination to break when media_ids.length >= 4 to enforce the max-4 rule
instead of checking the iterator.
| switch (media_ids.length) { | ||
| case 1: | ||
| tweetConfig.media.media_ids = [structuredClone(media_ids[0])]; | ||
| break; | ||
| case 2: | ||
| tweetConfig.media.media_ids = [structuredClone(media_ids[0]), structuredClone(media_ids[1])]; | ||
| break; | ||
| case 3: | ||
| tweetConfig.media.media_ids = [structuredClone(media_ids[0]), structuredClone(media_ids[1]), structuredClone(media_ids[2])]; | ||
| break; | ||
| case 4: | ||
| tweetConfig.media.media_ids = [structuredClone(media_ids[0]), structuredClone(media_ids[1]), structuredClone(media_ids[2]), structuredClone(media_ids[3])]; | ||
| break; | ||
| default: | ||
| break; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Simplify media_ids assignment.
The switch statement is unnecessarily verbose and uses structuredClone on string primitives, which is redundant since strings are immutable. This entire block can be replaced with a simple array assignment.
Apply this diff to simplify:
- switch (media_ids.length) {
- case 1:
- tweetConfig.media.media_ids = [structuredClone(media_ids[0])];
- break;
- case 2:
- tweetConfig.media.media_ids = [structuredClone(media_ids[0]), structuredClone(media_ids[1])];
- break;
- case 3:
- tweetConfig.media.media_ids = [structuredClone(media_ids[0]), structuredClone(media_ids[1]), structuredClone(media_ids[2])];
- break;
- case 4:
- tweetConfig.media.media_ids = [structuredClone(media_ids[0]), structuredClone(media_ids[1]), structuredClone(media_ids[2]), structuredClone(media_ids[3])];
- break;
- default:
- break;
- }
+ if (media_ids.length > 0) {
+ tweetConfig.media = { media_ids };
+ }This also ensures the media property is only added when there are actual media IDs to attach.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/client/tweets.ts around lines 628 to 643, the switch that assigns
tweetConfig.media.media_ids is verbose and redundantly uses structuredClone on
string primitives; replace it with a single conditional that only adds the media
property when media_ids has items and assigns a shallow copy of the array (e.g.,
tweetConfig.media = { media_ids: [...media_ids] } or equivalent), removing
structuredClone and the switch entirely so media is only present when there are
actual media IDs.
Upload media data is available with Twitter-API-v2, please check and update, thanks!
I have tested this function in my project, it works well with .PNG files.
Summary by CodeRabbit
Release Notes