From 90cd02e008759bcea7d7a42d62319ea929962dbc Mon Sep 17 00:00:00 2001 From: Ting Chien Meng Date: Fri, 24 Jan 2025 01:42:18 +0800 Subject: [PATCH 1/8] fix json post issue --- packages/client-twitter/src/post.ts | 238 ++++++++++++++-------------- 1 file changed, 118 insertions(+), 120 deletions(-) diff --git a/packages/client-twitter/src/post.ts b/packages/client-twitter/src/post.ts index fc8fd63e4aa..4a7211609ec 100644 --- a/packages/client-twitter/src/post.ts +++ b/packages/client-twitter/src/post.ts @@ -9,6 +9,7 @@ import { type TemplateType, type UUID, truncateToCompleteSentence, + parseJSONObjectFromText, } from "@elizaos/core"; import { elizaLogger } from "@elizaos/core"; import type { ClientBase } from "./base.ts"; @@ -112,34 +113,34 @@ export class TwitterPostClient { elizaLogger.log("Twitter Client Configuration:"); elizaLogger.log(`- Username: ${this.twitterUsername}`); elizaLogger.log( - `- Dry Run Mode: ${this.isDryRun ? "enabled" : "disabled"}` + `- Dry Run Mode: ${this.isDryRun ? "enabled" : "disabled"}`, ); elizaLogger.log( - `- Post Interval: ${this.client.twitterConfig.POST_INTERVAL_MIN}-${this.client.twitterConfig.POST_INTERVAL_MAX} minutes` + `- Post Interval: ${this.client.twitterConfig.POST_INTERVAL_MIN}-${this.client.twitterConfig.POST_INTERVAL_MAX} minutes`, ); elizaLogger.log( `- Action Processing: ${ this.client.twitterConfig.ENABLE_ACTION_PROCESSING ? "enabled" : "disabled" - }` + }`, ); elizaLogger.log( - `- Action Interval: ${this.client.twitterConfig.ACTION_INTERVAL} minutes` + `- Action Interval: ${this.client.twitterConfig.ACTION_INTERVAL} minutes`, ); elizaLogger.log( `- Post Immediately: ${ this.client.twitterConfig.POST_IMMEDIATELY ? "enabled" : "disabled" - }` + }`, ); elizaLogger.log( `- Search Enabled: ${ this.client.twitterConfig.TWITTER_SEARCH_ENABLE ? "enabled" : "disabled" - }` + }`, ); const targetUsers = this.client.twitterConfig.TWITTER_TARGET_USERS; @@ -149,7 +150,7 @@ export class TwitterPostClient { if (this.isDryRun) { elizaLogger.log( - "Twitter client initialized in dry run mode - no actual tweets should be posted" + "Twitter client initialized in dry run mode - no actual tweets should be posted", ); } @@ -160,22 +161,22 @@ export class TwitterPostClient { ?.toLocaleLowerCase() === "true"; if (approvalRequired) { const discordToken = this.runtime.getSetting( - "TWITTER_APPROVAL_DISCORD_BOT_TOKEN" + "TWITTER_APPROVAL_DISCORD_BOT_TOKEN", ); const approvalChannelId = this.runtime.getSetting( - "TWITTER_APPROVAL_DISCORD_CHANNEL_ID" + "TWITTER_APPROVAL_DISCORD_CHANNEL_ID", ); const APPROVAL_CHECK_INTERVAL = Number.parseInt( - this.runtime.getSetting("TWITTER_APPROVAL_CHECK_INTERVAL") + this.runtime.getSetting("TWITTER_APPROVAL_CHECK_INTERVAL"), ) || 5 * 60 * 1000; // 5 minutes this.approvalCheckInterval = APPROVAL_CHECK_INTERVAL; if (!discordToken || !approvalChannelId) { throw new Error( - "TWITTER_APPROVAL_DISCORD_BOT_TOKEN and TWITTER_APPROVAL_DISCORD_CHANNEL_ID are required for approval workflow" + "TWITTER_APPROVAL_DISCORD_BOT_TOKEN and TWITTER_APPROVAL_DISCORD_CHANNEL_ID are required for approval workflow", ); } @@ -201,7 +202,7 @@ export class TwitterPostClient { Events.ClientReady, (readyClient) => { elizaLogger.log( - `Discord bot is ready as ${readyClient.user.tag}!` + `Discord bot is ready as ${readyClient.user.tag}!`, ); // Generate invite link with required permissions @@ -212,13 +213,13 @@ export class TwitterPostClient { // - Read Message History elizaLogger.log( - `Use this link to properly invite the Twitter Post Approval Discord bot: ${invite}` + `Use this link to properly invite the Twitter Post Approval Discord bot: ${invite}`, ); - } + }, ); // Login to Discord this.discordClientForApproval.login( - this.runtime.getSetting("TWITTER_APPROVAL_DISCORD_BOT_TOKEN") + this.runtime.getSetting("TWITTER_APPROVAL_DISCORD_BOT_TOKEN"), ); } @@ -263,18 +264,18 @@ export class TwitterPostClient { if (results) { elizaLogger.log(`Processed ${results.length} tweets`); elizaLogger.log( - `Next action processing scheduled in ${actionInterval} minutes` + `Next action processing scheduled in ${actionInterval} minutes`, ); // Wait for the full interval before next processing await new Promise( (resolve) => - setTimeout(resolve, actionInterval * 60 * 1000) // now in minutes + setTimeout(resolve, actionInterval * 60 * 1000), // now in minutes ); } } catch (error) { elizaLogger.error( "Error in action processing loop:", - error + error, ); // Add exponential backoff on error await new Promise((resolve) => setTimeout(resolve, 30000)); // Wait 30s on error @@ -293,7 +294,7 @@ export class TwitterPostClient { processActionsLoop().catch((error) => { elizaLogger.error( "Fatal error in process actions loop:", - error + error, ); }); } @@ -311,7 +312,7 @@ export class TwitterPostClient { createTweetObject( tweetResult: any, client: any, - twitterUsername: string + twitterUsername: string, ): Tweet { return { id: tweetResult.rest_id, @@ -338,7 +339,7 @@ export class TwitterPostClient { client: ClientBase, tweet: Tweet, roomId: UUID, - newTweetContent: string + newTweetContent: string, ) { // Cache the last post details await runtime.cacheManager.set( @@ -346,7 +347,7 @@ export class TwitterPostClient { { id: tweet.id, timestamp: Date.now(), - } + }, ); // Cache the tweet @@ -378,24 +379,24 @@ export class TwitterPostClient { async handleNoteTweet( client: ClientBase, content: string, - tweetId?: string + tweetId?: string, ) { try { const noteTweetResult = await client.requestQueue.add( async () => - await client.twitterClient.sendNoteTweet(content, tweetId) + await client.twitterClient.sendNoteTweet(content, tweetId), ); if (noteTweetResult.errors && noteTweetResult.errors.length > 0) { // Note Tweet failed due to authorization. Falling back to standard Tweet. const truncateContent = truncateToCompleteSentence( content, - this.client.twitterConfig.MAX_TWEET_LENGTH + this.client.twitterConfig.MAX_TWEET_LENGTH, ); return await this.sendStandardTweet( client, truncateContent, - tweetId + tweetId, ); } else { return noteTweetResult.data.notetweet_create.tweet_results @@ -409,12 +410,12 @@ export class TwitterPostClient { async sendStandardTweet( client: ClientBase, content: string, - tweetId?: string + tweetId?: string, ) { try { const standardTweetResult = await client.requestQueue.add( async () => - await client.twitterClient.sendTweet(content, tweetId) + await client.twitterClient.sendTweet(content, tweetId), ); const body = await standardTweetResult.json(); if (!body?.data?.create_tweet?.tweet_results?.result) { @@ -434,7 +435,7 @@ export class TwitterPostClient { cleanedContent: string, roomId: UUID, newTweetContent: string, - twitterUsername: string + twitterUsername: string, ) { try { elizaLogger.log(`Posting new tweet:\n`); @@ -450,7 +451,7 @@ export class TwitterPostClient { const tweet = this.createTweetObject( result, client, - twitterUsername + twitterUsername, ); await this.processAndCacheTweet( @@ -458,7 +459,7 @@ export class TwitterPostClient { client, tweet, roomId, - newTweetContent + newTweetContent, ); } catch (error) { elizaLogger.error("Error sending tweet:", error); @@ -473,13 +474,13 @@ export class TwitterPostClient { try { const roomId = stringToUuid( - "twitter_generate_room-" + this.client.profile.username + "twitter_generate_room-" + this.client.profile.username, ); await this.runtime.ensureUserExists( this.runtime.agentId, this.client.profile.username, this.runtime.character.name, - "twitter" + "twitter", ); const topics = this.runtime.character.topics.join(", "); @@ -496,7 +497,7 @@ export class TwitterPostClient { }, { twitterUserName: this.client.profile.username, - } + }, ); const context = composeContext({ @@ -542,7 +543,7 @@ export class TwitterPostClient { { rawResponse: newTweetContent, attempted: "JSON parsing", - } + }, ); return; } @@ -552,7 +553,7 @@ export class TwitterPostClient { if (maxTweetLength) { cleanedContent = truncateToCompleteSentence( cleanedContent, - maxTweetLength + maxTweetLength, ); } @@ -566,7 +567,7 @@ export class TwitterPostClient { if (this.isDryRun) { elizaLogger.info( - `Dry run: would have posted tweet: ${cleanedContent}` + `Dry run: would have posted tweet: ${cleanedContent}`, ); return; } @@ -575,12 +576,12 @@ export class TwitterPostClient { if (this.approvalRequired) { // Send for approval instead of posting directly elizaLogger.log( - `Sending Tweet For Approval:\n ${cleanedContent}` + `Sending Tweet For Approval:\n ${cleanedContent}`, ); await this.sendForApproval( cleanedContent, roomId, - newTweetContent + newTweetContent, ); elizaLogger.log("Tweet sent for approval"); } else { @@ -591,7 +592,7 @@ export class TwitterPostClient { cleanedContent, roomId, newTweetContent, - this.twitterUsername + this.twitterUsername, ); } } catch (error) { @@ -607,7 +608,7 @@ export class TwitterPostClient { options?: { template?: TemplateType; context?: string; - } + }, ): Promise { const context = composeContext({ state: tweetState, @@ -622,18 +623,16 @@ export class TwitterPostClient { context: options?.context || context, modelClass: ModelClass.SMALL, }); - elizaLogger.debug("generate tweet content response:\n" + response); // First clean up any markdown and newlines const cleanedResponse = response .replace(/```json\s*/g, "") // Remove ```json .replace(/```\s*/g, "") // Remove any remaining ``` - .replaceAll(/\\n/g, "\n") .trim(); // Try to parse as JSON first try { - const jsonResponse = JSON.parse(cleanedResponse); + const jsonResponse = parseJSONObjectFromText(cleanedResponse); if (jsonResponse.text) { return this.trimTweetLength(jsonResponse.text); } @@ -652,7 +651,6 @@ export class TwitterPostClient { // If JSON parsing fails, treat as plain text elizaLogger.debug("Response is not JSON, treating as plain text"); } - // If not JSON or no valid content found, clean the raw text return this.trimTweetLength(cleanedResponse); } @@ -693,11 +691,11 @@ export class TwitterPostClient { this.runtime.agentId, this.twitterUsername, this.runtime.character.name, - "twitter" + "twitter", ); const timelines = await this.client.fetchTimelineForActions( - MAX_TIMELINES_TO_FETCH + MAX_TIMELINES_TO_FETCH, ); const maxActionsProcessing = this.client.twitterConfig.MAX_ACTIONS_PROCESSING; @@ -708,17 +706,17 @@ export class TwitterPostClient { // Skip if we've already processed this tweet const memory = await this.runtime.messageManager.getMemoryById( - stringToUuid(tweet.id + "-" + this.runtime.agentId) + stringToUuid(tweet.id + "-" + this.runtime.agentId), ); if (memory) { elizaLogger.log( - `Already processed tweet ID: ${tweet.id}` + `Already processed tweet ID: ${tweet.id}`, ); continue; } const roomId = stringToUuid( - tweet.conversationId + "-" + this.runtime.agentId + tweet.conversationId + "-" + this.runtime.agentId, ); const tweetState = await this.runtime.composeState( @@ -731,7 +729,7 @@ export class TwitterPostClient { { twitterUserName: this.twitterUsername, currentTweet: `ID: ${tweet.id}\nFrom: ${tweet.name} (@${tweet.username})\nText: ${tweet.text}`, - } + }, ); const actionContext = composeContext({ @@ -750,7 +748,7 @@ export class TwitterPostClient { if (!actionResponse) { elizaLogger.log( - `No valid actions generated for tweet ${tweet.id}` + `No valid actions generated for tweet ${tweet.id}`, ); continue; } @@ -763,7 +761,7 @@ export class TwitterPostClient { } catch (error) { elizaLogger.error( `Error processing tweet ${tweet.id}:`, - error + error, ); continue; } @@ -795,7 +793,7 @@ export class TwitterPostClient { // Sort the timeline based on the action decision score, // then slice the results according to the environment variable to limit the number of actions per cycle. const sortedTimelines = sortProcessedTimeline( - processedTimelines + processedTimelines, ).slice(0, maxActionsProcessing); return this.processTimelineActions(sortedTimelines); // Return results array to indicate completion @@ -821,7 +819,7 @@ export class TwitterPostClient { actionResponse: ActionResponse; tweetState: State; roomId: UUID; - }[] + }[], ): Promise< { tweetId: string; @@ -838,7 +836,7 @@ export class TwitterPostClient { if (actionResponse.like) { if (this.isDryRun) { elizaLogger.info( - `Dry run: would have liked tweet ${tweet.id}` + `Dry run: would have liked tweet ${tweet.id}`, ); executedActions.push("like (dry run)"); } else { @@ -849,7 +847,7 @@ export class TwitterPostClient { } catch (error) { elizaLogger.error( `Error liking tweet ${tweet.id}:`, - error + error, ); } } @@ -858,7 +856,7 @@ export class TwitterPostClient { if (actionResponse.retweet) { if (this.isDryRun) { elizaLogger.info( - `Dry run: would have retweeted tweet ${tweet.id}` + `Dry run: would have retweeted tweet ${tweet.id}`, ); executedActions.push("retweet (dry run)"); } else { @@ -869,7 +867,7 @@ export class TwitterPostClient { } catch (error) { elizaLogger.error( `Error retweeting tweet ${tweet.id}:`, - error + error, ); } } @@ -880,14 +878,14 @@ export class TwitterPostClient { // Build conversation thread for context const thread = await buildConversationThread( tweet, - this.client + this.client, ); const formattedConversation = thread .map( (t) => `@${t.username} (${new Date( - t.timestamp * 1000 - ).toLocaleString()}): ${t.text}` + t.timestamp * 1000, + ).toLocaleString()}): ${t.text}`, ) .join("\n\n"); @@ -895,12 +893,12 @@ export class TwitterPostClient { const imageDescriptions = []; if (tweet.photos?.length > 0) { elizaLogger.log( - "Processing images in tweet for context" + "Processing images in tweet for context", ); for (const photo of tweet.photos) { const description = await this.runtime .getService( - ServiceType.IMAGE_DESCRIPTION + ServiceType.IMAGE_DESCRIPTION, ) .describeImage(photo.url); imageDescriptions.push(description); @@ -913,7 +911,7 @@ export class TwitterPostClient { try { const quotedTweet = await this.client.twitterClient.getTweet( - tweet.quotedStatusId + tweet.quotedStatusId, ); if (quotedTweet) { quotedContent = `\nQuoted Tweet from @${quotedTweet.username}:\n${quotedTweet.text}`; @@ -921,7 +919,7 @@ export class TwitterPostClient { } catch (error) { elizaLogger.error( "Error fetching quoted tweet:", - error + error, ); } } @@ -933,7 +931,7 @@ export class TwitterPostClient { roomId: stringToUuid( tweet.conversationId + "-" + - this.runtime.agentId + this.runtime.agentId, ), agentId: this.runtime.agentId, content: { @@ -950,12 +948,12 @@ export class TwitterPostClient { ? `\nImages in Tweet:\n${imageDescriptions .map( (desc, i) => - `Image ${i + 1}: ${desc}` + `Image ${i + 1}: ${desc}`, ) .join("\n")}` : "", quotedContent, - } + }, ); const quoteContent = await this.generateTweetContent( @@ -965,24 +963,24 @@ export class TwitterPostClient { this.runtime.character.templates ?.twitterMessageHandlerTemplate || twitterMessageHandlerTemplate, - } + }, ); if (!quoteContent) { elizaLogger.error( - "Failed to generate valid quote tweet content" + "Failed to generate valid quote tweet content", ); return; } elizaLogger.log( "Generated quote tweet content:", - quoteContent + quoteContent, ); // Check for dry run mode if (this.isDryRun) { elizaLogger.info( - `Dry run: A quote tweet for tweet ID ${tweet.id} would have been posted with the following content: "${quoteContent}".` + `Dry run: A quote tweet for tweet ID ${tweet.id} would have been posted with the following content: "${quoteContent}".`, ); executedActions.push("quote (dry run)"); } else { @@ -991,8 +989,8 @@ export class TwitterPostClient { async () => await this.client.twitterClient.sendQuoteTweet( quoteContent, - tweet.id - ) + tweet.id, + ), ); const body = await result.json(); @@ -1001,26 +999,26 @@ export class TwitterPostClient { body?.data?.create_tweet?.tweet_results?.result ) { elizaLogger.log( - "Successfully posted quote tweet" + "Successfully posted quote tweet", ); executedActions.push("quote"); // Cache generation context for debugging await this.runtime.cacheManager.set( `twitter/quote_generation_${tweet.id}.txt`, - `Context:\n${enrichedState}\n\nGenerated Quote:\n${quoteContent}` + `Context:\n${enrichedState}\n\nGenerated Quote:\n${quoteContent}`, ); } else { elizaLogger.error( "Quote tweet creation failed:", - body + body, ); } } } catch (error) { elizaLogger.error( "Error in quote tweet generation:", - error + error, ); } } @@ -1030,12 +1028,12 @@ export class TwitterPostClient { await this.handleTextOnlyReply( tweet, tweetState, - executedActions + executedActions, ); } catch (error) { elizaLogger.error( `Error replying to tweet ${tweet.id}:`, - error + error, ); } } @@ -1046,11 +1044,11 @@ export class TwitterPostClient { stringToUuid(tweet.userId), tweet.username, tweet.name, - "twitter" + "twitter", ); await this.runtime.ensureParticipantInRoom( this.runtime.agentId, - roomId + roomId, ); if (!this.isDryRun) { @@ -1092,7 +1090,7 @@ export class TwitterPostClient { private async handleTextOnlyReply( tweet: Tweet, tweetState: any, - executedActions: string[] + executedActions: string[], ) { try { // Build conversation thread for context @@ -1101,8 +1099,8 @@ export class TwitterPostClient { .map( (t) => `@${t.username} (${new Date( - t.timestamp * 1000 - ).toLocaleString()}): ${t.text}` + t.timestamp * 1000, + ).toLocaleString()}): ${t.text}`, ) .join("\n\n"); @@ -1113,7 +1111,7 @@ export class TwitterPostClient { for (const photo of tweet.photos) { const description = await this.runtime .getService( - ServiceType.IMAGE_DESCRIPTION + ServiceType.IMAGE_DESCRIPTION, ) .describeImage(photo.url); imageDescriptions.push(description); @@ -1126,7 +1124,7 @@ export class TwitterPostClient { try { const quotedTweet = await this.client.twitterClient.getTweet( - tweet.quotedStatusId + tweet.quotedStatusId, ); if (quotedTweet) { quotedContent = `\nQuoted Tweet from @${quotedTweet.username}:\n${quotedTweet.text}`; @@ -1141,7 +1139,7 @@ export class TwitterPostClient { { userId: this.runtime.agentId, roomId: stringToUuid( - tweet.conversationId + "-" + this.runtime.agentId + tweet.conversationId + "-" + this.runtime.agentId, ), agentId: this.runtime.agentId, content: { text: tweet.text, action: "" }, @@ -1157,7 +1155,7 @@ export class TwitterPostClient { .join("\n")}` : "", quotedContent, - } + }, ); // Generate and clean the reply content @@ -1175,7 +1173,7 @@ export class TwitterPostClient { if (this.isDryRun) { elizaLogger.info( - `Dry run: reply to tweet ${tweet.id} would have been: ${replyText}` + `Dry run: reply to tweet ${tweet.id} would have been: ${replyText}`, ); executedActions.push("reply (dry run)"); return; @@ -1189,13 +1187,13 @@ export class TwitterPostClient { result = await this.handleNoteTweet( this.client, replyText, - tweet.id + tweet.id, ); } else { result = await this.sendStandardTweet( this.client, replyText, - tweet.id + tweet.id, ); } @@ -1206,7 +1204,7 @@ export class TwitterPostClient { // Cache generation context for debugging await this.runtime.cacheManager.set( `twitter/reply_generation_${tweet.id}.txt`, - `Context:\n${enrichedState}\n\nGenerated Reply:\n${replyText}` + `Context:\n${enrichedState}\n\nGenerated Reply:\n${replyText}`, ); } else { elizaLogger.error("Tweet reply creation failed"); @@ -1223,7 +1221,7 @@ export class TwitterPostClient { private async sendForApproval( cleanedContent: string, roomId: UUID, - newTweetContent: string + newTweetContent: string, ): Promise { try { const embed = { @@ -1248,7 +1246,7 @@ export class TwitterPostClient { }; const channel = await this.discordClientForApproval.channels.fetch( - this.discordApprovalChannelId + this.discordApprovalChannelId, ); if (!channel || !(channel instanceof TextChannel)) { @@ -1261,7 +1259,7 @@ export class TwitterPostClient { const pendingTweetsKey = `twitter/${this.client.profile.username}/pendingTweet`; const currentPendingTweets = (await this.runtime.cacheManager.get( - pendingTweetsKey + pendingTweetsKey, )) || []; // Add new pending tweet currentPendingTweets.push({ @@ -1276,26 +1274,26 @@ export class TwitterPostClient { // Store updated array await this.runtime.cacheManager.set( pendingTweetsKey, - currentPendingTweets + currentPendingTweets, ); return message.id; } catch (error) { elizaLogger.error( "Error Sending Twitter Post Approval Request:", - error + error, ); return null; } } private async checkApprovalStatus( - discordMessageId: string + discordMessageId: string, ): Promise { try { // Fetch message and its replies from Discord const channel = await this.discordClientForApproval.channels.fetch( - this.discordApprovalChannelId + this.discordApprovalChannelId, ); elizaLogger.log(`channel ${JSON.stringify(channel)}`); @@ -1310,12 +1308,12 @@ export class TwitterPostClient { // Look for thumbs up reaction ('👍') const thumbsUpReaction = message.reactions.cache.find( - (reaction) => reaction.emoji.name === "👍" + (reaction) => reaction.emoji.name === "👍", ); // Look for reject reaction ('❌') const rejectReaction = message.reactions.cache.find( - (reaction) => reaction.emoji.name === "❌" + (reaction) => reaction.emoji.name === "❌", ); // Check if the reaction exists and has reactions @@ -1347,12 +1345,12 @@ export class TwitterPostClient { const pendingTweetsKey = `twitter/${this.client.profile.username}/pendingTweet`; const currentPendingTweets = (await this.runtime.cacheManager.get( - pendingTweetsKey + pendingTweetsKey, )) || []; // Remove the specific tweet const updatedPendingTweets = currentPendingTweets.filter( - (tweet) => tweet.discordMessageId !== discordMessageId + (tweet) => tweet.discordMessageId !== discordMessageId, ); if (updatedPendingTweets.length === 0) { @@ -1360,7 +1358,7 @@ export class TwitterPostClient { } else { await this.runtime.cacheManager.set( pendingTweetsKey, - updatedPendingTweets + updatedPendingTweets, ); } } @@ -1370,7 +1368,7 @@ export class TwitterPostClient { const pendingTweetsKey = `twitter/${this.client.profile.username}/pendingTweet`; const pendingTweets = (await this.runtime.cacheManager.get( - pendingTweetsKey + pendingTweetsKey, )) || []; for (const pendingTweet of pendingTweets) { @@ -1385,20 +1383,20 @@ export class TwitterPostClient { try { const channel = await this.discordClientForApproval.channels.fetch( - pendingTweet.channelId + pendingTweet.channelId, ); if (channel instanceof TextChannel) { const originalMessage = await channel.messages.fetch( - pendingTweet.discordMessageId + pendingTweet.discordMessageId, ); await originalMessage.reply( - "This tweet approval request has expired (24h timeout)." + "This tweet approval request has expired (24h timeout).", ); } } catch (error) { elizaLogger.error( "Error sending expiration notification:", - error + error, ); } @@ -1419,27 +1417,27 @@ export class TwitterPostClient { pendingTweet.cleanedContent, pendingTweet.roomId, pendingTweet.newTweetContent, - this.twitterUsername + this.twitterUsername, ); // Notify on Discord about posting try { const channel = await this.discordClientForApproval.channels.fetch( - pendingTweet.channelId + pendingTweet.channelId, ); if (channel instanceof TextChannel) { const originalMessage = await channel.messages.fetch( - pendingTweet.discordMessageId + pendingTweet.discordMessageId, ); await originalMessage.reply( - "Tweet has been posted successfully! ✅" + "Tweet has been posted successfully! ✅", ); } } catch (error) { elizaLogger.error( "Error sending post notification:", - error + error, ); } @@ -1451,20 +1449,20 @@ export class TwitterPostClient { try { const channel = await this.discordClientForApproval.channels.fetch( - pendingTweet.channelId + pendingTweet.channelId, ); if (channel instanceof TextChannel) { const originalMessage = await channel.messages.fetch( - pendingTweet.discordMessageId + pendingTweet.discordMessageId, ); await originalMessage.reply( - "Tweet has been rejected! ❌" + "Tweet has been rejected! ❌", ); } } catch (error) { elizaLogger.error( "Error sending rejection notification:", - error + error, ); } } From e0a966da63f80c18181d3d4ec415f956c0e2e2f6 Mon Sep 17 00:00:00 2001 From: Ting Chien Meng Date: Fri, 24 Jan 2025 01:43:43 +0800 Subject: [PATCH 2/8] add debug --- packages/client-twitter/src/post.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/client-twitter/src/post.ts b/packages/client-twitter/src/post.ts index 4a7211609ec..db57d749d82 100644 --- a/packages/client-twitter/src/post.ts +++ b/packages/client-twitter/src/post.ts @@ -624,6 +624,8 @@ export class TwitterPostClient { modelClass: ModelClass.SMALL, }); + elizaLogger.debug("generate tweet content response:\n" + response); + // First clean up any markdown and newlines const cleanedResponse = response .replace(/```json\s*/g, "") // Remove ```json From 80e27ddedfb7b7f6d14a1d0cb81af03ce9490ad0 Mon Sep 17 00:00:00 2001 From: Ting Chien Meng Date: Fri, 24 Jan 2025 01:55:13 +0800 Subject: [PATCH 3/8] use parseJSONObjectFromText --- packages/client-twitter/src/post.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client-twitter/src/post.ts b/packages/client-twitter/src/post.ts index db57d749d82..e043aea3663 100644 --- a/packages/client-twitter/src/post.ts +++ b/packages/client-twitter/src/post.ts @@ -520,7 +520,7 @@ export class TwitterPostClient { // Try parsing as JSON first try { - const parsedResponse = JSON.parse(newTweetContent); + const parsedResponse = parseJSONObjectFromText(newTweetContent); if (parsedResponse.text) { cleanedContent = parsedResponse.text; } else if (typeof parsedResponse === "string") { From ff12944138dae4c1437cdf3e1ee24980a4e0fc8f Mon Sep 17 00:00:00 2001 From: Ting Chien Meng Date: Fri, 24 Jan 2025 02:12:52 +0800 Subject: [PATCH 4/8] Replace all types of line breaks --- packages/client-twitter/src/post.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/client-twitter/src/post.ts b/packages/client-twitter/src/post.ts index e043aea3663..7817e35ddcb 100644 --- a/packages/client-twitter/src/post.ts +++ b/packages/client-twitter/src/post.ts @@ -624,12 +624,13 @@ export class TwitterPostClient { modelClass: ModelClass.SMALL, }); - elizaLogger.debug("generate tweet content response:\n" + response); + console.log("generate tweet content response:\n" + response); // First clean up any markdown and newlines const cleanedResponse = response .replace(/```json\s*/g, "") // Remove ```json .replace(/```\s*/g, "") // Remove any remaining ``` + .replace(/(\r\n|\n|\r)/g, "") .trim(); // Try to parse as JSON first From 83f018de6e5280fe1210e0f63ef7e3e04f10b303 Mon Sep 17 00:00:00 2001 From: Ting Chien Meng Date: Fri, 24 Jan 2025 02:39:40 +0800 Subject: [PATCH 5/8] add comment --- packages/client-twitter/src/post.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client-twitter/src/post.ts b/packages/client-twitter/src/post.ts index 7817e35ddcb..db7cb79f45d 100644 --- a/packages/client-twitter/src/post.ts +++ b/packages/client-twitter/src/post.ts @@ -630,7 +630,7 @@ export class TwitterPostClient { const cleanedResponse = response .replace(/```json\s*/g, "") // Remove ```json .replace(/```\s*/g, "") // Remove any remaining ``` - .replace(/(\r\n|\n|\r)/g, "") + .replace(/(\r\n|\n|\r)/g, "") // Remove line break .trim(); // Try to parse as JSON first From 63c482a1299d5b0c3eb797717ec848c5e3b99f60 Mon Sep 17 00:00:00 2001 From: Ting Chien Meng Date: Fri, 24 Jan 2025 02:42:36 +0800 Subject: [PATCH 6/8] handle post repsonse --- packages/client-twitter/src/post.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/client-twitter/src/post.ts b/packages/client-twitter/src/post.ts index db7cb79f45d..b6a219dae95 100644 --- a/packages/client-twitter/src/post.ts +++ b/packages/client-twitter/src/post.ts @@ -509,12 +509,18 @@ export class TwitterPostClient { elizaLogger.debug("generate post prompt:\n" + context); - const newTweetContent = await generateText({ + const response = await generateText({ runtime: this.runtime, context, modelClass: ModelClass.SMALL, }); + const newTweetContent = response + .replace(/```json\s*/g, "") // Remove ```json + .replace(/```\s*/g, "") // Remove any remaining ``` + .replace(/(\r\n|\n|\r)/g, "") // Remove line break + .trim(); + // First attempt to clean content let cleanedContent = ""; From 79b41ac4f8088296d2410512d00323cf80d90c39 Mon Sep 17 00:00:00 2001 From: Ting Chien Meng Date: Fri, 24 Jan 2025 02:46:42 +0800 Subject: [PATCH 7/8] use elizaLogger --- packages/client-twitter/src/post.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/client-twitter/src/post.ts b/packages/client-twitter/src/post.ts index b6a219dae95..ae9f5731dd7 100644 --- a/packages/client-twitter/src/post.ts +++ b/packages/client-twitter/src/post.ts @@ -237,8 +237,8 @@ export class TwitterPostClient { }>("twitter/" + this.twitterUsername + "/lastPost"); const lastPostTimestamp = lastPost?.timestamp ?? 0; - const minMinutes = this.client.twitterConfig.POST_INTERVAL_MIN; - const maxMinutes = this.client.twitterConfig.POST_INTERVAL_MAX; + const minMinutes = 1; + const maxMinutes = 1; const randomMinutes = Math.floor(Math.random() * (maxMinutes - minMinutes + 1)) + minMinutes; @@ -630,7 +630,7 @@ export class TwitterPostClient { modelClass: ModelClass.SMALL, }); - console.log("generate tweet content response:\n" + response); + elizaLogger.log("generate tweet content response:\n" + response); // First clean up any markdown and newlines const cleanedResponse = response @@ -882,7 +882,7 @@ export class TwitterPostClient { } } - if (actionResponse.quote) { + if (true) { try { // Build conversation thread for context const thread = await buildConversationThread( @@ -1032,7 +1032,7 @@ export class TwitterPostClient { } } - if (actionResponse.reply) { + if (true) { try { await this.handleTextOnlyReply( tweet, From b5e0f9efb6d54847aa7e21d7b5c3bb93a989446d Mon Sep 17 00:00:00 2001 From: Ting Chien Meng Date: Fri, 24 Jan 2025 02:47:29 +0800 Subject: [PATCH 8/8] use elizaLogger --- packages/client-twitter/src/post.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/client-twitter/src/post.ts b/packages/client-twitter/src/post.ts index ae9f5731dd7..f02cb42fc5d 100644 --- a/packages/client-twitter/src/post.ts +++ b/packages/client-twitter/src/post.ts @@ -237,8 +237,8 @@ export class TwitterPostClient { }>("twitter/" + this.twitterUsername + "/lastPost"); const lastPostTimestamp = lastPost?.timestamp ?? 0; - const minMinutes = 1; - const maxMinutes = 1; + const minMinutes = this.client.twitterConfig.POST_INTERVAL_MIN; + const maxMinutes = this.client.twitterConfig.POST_INTERVAL_MAX; const randomMinutes = Math.floor(Math.random() * (maxMinutes - minMinutes + 1)) + minMinutes; @@ -419,7 +419,7 @@ export class TwitterPostClient { ); const body = await standardTweetResult.json(); if (!body?.data?.create_tweet?.tweet_results?.result) { - console.error("Error sending tweet; Bad response:", body); + elizaLogger.error("Error sending tweet; Bad response:", body); return; } return body.data.create_tweet.tweet_results.result; @@ -882,7 +882,7 @@ export class TwitterPostClient { } } - if (true) { + if (actionResponse.quote) { try { // Build conversation thread for context const thread = await buildConversationThread( @@ -1032,7 +1032,7 @@ export class TwitterPostClient { } } - if (true) { + if (actionResponse.reply) { try { await this.handleTextOnlyReply( tweet,