Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion src/client/tweets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,62 @@ export async function createCreateTweetRequest(
if (mediaData && mediaData.length > 0) {
// Note: Twitter API v2 media upload requires separate endpoint
// For now, we'll skip media upload as it requires additional implementation
console.warn("Media upload not yet implemented for Twitter API v2");
// console.warn("Media upload not yet implemented for Twitter API v2");
tweetConfig.media = {
media_ids:[""]
};
Comment on lines +589 to +591
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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 media_ids: string[] = [];
for(let i = 0; i < mediaData.length; i++){
const media = mediaData[i];
const v2 = v2client.v2;
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;
}
Comment on lines +596 to +620
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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;
}
Comment on lines +621 to +625
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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;
}
Comment on lines +628 to +643
Copy link

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.

}

// Handle reply
Expand All @@ -607,6 +662,7 @@ export async function createCreateTweetRequest(
}
}


export async function createCreateNoteTweetRequest(
text: string,
auth: TwitterAuth,
Expand Down