Skip to content

Commit fb7c09c

Browse files
committed
address review comments
1 parent cb2898c commit fb7c09c

File tree

3 files changed

+118
-46
lines changed

3 files changed

+118
-46
lines changed

packages/plugin-twitter/src/actions/post.ts

+83-46
Original file line numberDiff line numberDiff line change
@@ -5,67 +5,48 @@ import {
55
State,
66
composeContext,
77
elizaLogger,
8-
generateText,
98
ModelClass,
109
formatMessages,
10+
generateObject,
1111
} from "@elizaos/core";
1212
import { Scraper } from "agent-twitter-client";
13+
import { tweetTemplate } from "../templates";
14+
import { isTweetContent, TweetSchema } from "../types";
1315

1416
async function composeTweet(
1517
runtime: IAgentRuntime,
16-
message: Memory,
18+
_message: Memory,
1719
state?: State
1820
): Promise<string> {
1921
try {
20-
// Get recent conversation history
21-
const recentMessages = await runtime.messageManager.getMemories({
22-
roomId: message.roomId,
23-
count: 5,
24-
});
25-
26-
const formattedHistory = formatMessages({
27-
messages: recentMessages,
28-
actors: state?.actorsData,
29-
});
30-
31-
// Template for generating the tweet
32-
const tweetTemplate = `
33-
# Context
34-
Recent conversation:
35-
${formattedHistory}
36-
37-
Character style:
38-
${runtime.character.style.post.join("\n")}
39-
40-
Topics of expertise:
41-
${runtime.character.topics.join(", ")}
42-
43-
# Task
44-
Generate a tweet that:
45-
1. Relates to the recent conversation or requested topic
46-
2. Matches the character's style and voice
47-
3. Is concise and engaging
48-
4. Must be UNDER 180 characters (this is a strict requirement)
49-
5. Speaks from the perspective of ${runtime.character.name}
50-
51-
Generate only the tweet text, no other commentary.`;
52-
53-
const context = await composeContext({
22+
const context = composeContext({
5423
state,
5524
template: tweetTemplate,
5625
});
5726

58-
const tweetContent = await generateText({
27+
const tweetContentObject = await generateObject({
5928
runtime,
6029
context,
6130
modelClass: ModelClass.SMALL,
31+
schema: TweetSchema,
6232
stop: ["\n"],
6333
});
6434

65-
const trimmedContent = tweetContent.trim();
35+
if (!isTweetContent(tweetContentObject.object)) {
36+
elizaLogger.error(
37+
"Invalid tweet content:",
38+
tweetContentObject.object
39+
);
40+
return;
41+
}
42+
43+
const trimmedContent = tweetContentObject.object.text.trim();
6644

67-
// Enforce character limit
68-
if (trimmedContent.length > 180) {
45+
// Skip truncation if TWITTER_PREMIUM is true
46+
if (
47+
process.env.TWITTER_PREMIUM?.toLowerCase() !== "true" &&
48+
trimmedContent.length > 180
49+
) {
6950
elizaLogger.warn(
7051
`Tweet too long (${trimmedContent.length} chars), truncating...`
7152
);
@@ -88,15 +69,17 @@ async function postTweet(content: string): Promise<boolean> {
8869
const twitter2faSecret = process.env.TWITTER_2FA_SECRET;
8970

9071
if (!username || !password) {
91-
throw new Error(
72+
elizaLogger.error(
9273
"Twitter credentials not configured in environment"
9374
);
75+
return false;
9476
}
9577

9678
// Login with credentials
9779
await scraper.login(username, password, email, twitter2faSecret);
9880
if (!(await scraper.isLoggedIn())) {
99-
throw new Error("Failed to login to Twitter");
81+
elizaLogger.error("Failed to login to Twitter");
82+
return false;
10083
}
10184

10285
// Send the tweet
@@ -109,16 +92,18 @@ async function postTweet(content: string): Promise<boolean> {
10992
// Check for Twitter API errors
11093
if (body.errors) {
11194
const error = body.errors[0];
112-
throw new Error(
95+
elizaLogger.error(
11396
`Twitter API error (${error.code}): ${error.message}`
11497
);
98+
return false;
11599
}
116100

117101
// Check for successful tweet creation
118102
if (!body?.data?.create_tweet?.tweet_results?.result) {
119-
throw new Error(
103+
elizaLogger.error(
120104
"Failed to post tweet: No tweet result in response"
121105
);
106+
return false;
122107
}
123108

124109
return true;
@@ -186,12 +171,64 @@ export const postAction: Action = {
186171
[
187172
{
188173
user: "{{user1}}",
189-
content: { text: "Share your thoughts on AI" },
174+
content: { text: "You should tweet that" },
175+
},
176+
{
177+
user: "{{agentName}}",
178+
content: {
179+
text: "I'll share this update with my followers right away!",
180+
action: "POST_TWEET",
181+
},
182+
},
183+
],
184+
[
185+
{
186+
user: "{{user1}}",
187+
content: { text: "Post this tweet" },
188+
},
189+
{
190+
user: "{{agentName}}",
191+
content: {
192+
text: "I'll post that as a tweet now.",
193+
action: "POST_TWEET",
194+
},
195+
},
196+
],
197+
[
198+
{
199+
user: "{{user1}}",
200+
content: { text: "Share that on Twitter" },
201+
},
202+
{
203+
user: "{{agentName}}",
204+
content: {
205+
text: "I'll share this message on Twitter.",
206+
action: "POST_TWEET",
207+
},
208+
},
209+
],
210+
[
211+
{
212+
user: "{{user1}}",
213+
content: { text: "Post that on X" },
214+
},
215+
{
216+
user: "{{agentName}}",
217+
content: {
218+
text: "I'll post this message on X right away.",
219+
action: "POST_TWEET",
220+
},
221+
},
222+
],
223+
[
224+
{
225+
user: "{{user1}}",
226+
content: { text: "You should put that on X dot com" },
190227
},
191228
{
192229
user: "{{agentName}}",
193230
content: {
194-
text: "The future of AI lies in responsible development and ethical considerations. We must ensure it benefits all of humanity.",
231+
text: "I'll put this message up on X.com now.",
195232
action: "POST_TWEET",
196233
},
197234
},
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export const tweetTemplate = `
2+
# Context
3+
{{recentMessages}}
4+
5+
# Topics
6+
{{topics}}
7+
8+
# Post Directions
9+
{{postDirections}}
10+
11+
# Recent interactions between {{agentName}} and other users:
12+
{{recentPostInteractions}}
13+
14+
# Task
15+
Generate a tweet that:
16+
1. Relates to the recent conversation or requested topic
17+
2. Matches the character's style and voice
18+
3. Is concise and engaging
19+
4. Must be UNDER 180 characters (this is a strict requirement)
20+
5. Speaks from the perspective of {{agentName}}
21+
22+
Generate only the tweet text, no other commentary.`;

packages/plugin-twitter/src/types.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { z } from "zod";
2+
3+
export interface TweetContent {
4+
text: string;
5+
}
6+
7+
export const TweetSchema = z.object({
8+
text: z.string().describe("The text of the tweet"),
9+
});
10+
11+
export const isTweetContent = (obj: any): obj is TweetContent => {
12+
return TweetSchema.safeParse(obj).success;
13+
};

0 commit comments

Comments
 (0)