Skip to content

Commit

Permalink
Merge pull request #100 from autonomys/twitter-problems
Browse files Browse the repository at this point in the history
Improve Twitter client interactions
  • Loading branch information
jfrank-summit authored Jan 3, 2025
2 parents d542055 + cb4da13 commit a8e4048
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 78 deletions.
4 changes: 3 additions & 1 deletion auto-agents-framework/src/agents/tools/fetchTimelineTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ export const createFetchTimelineTool = (twitterApi: TwitterApi) =>
numRandomFollowers: number;
}) => {
try {
const myTimelineTweets = await twitterApi.getMyTimeline(numTimelineTweets, processedIds);
const myTimelineTweets = (
await twitterApi.getMyTimeline(numTimelineTweets, processedIds)
).slice(0, numTimelineTweets);
const followingRecents = await twitterApi.getFollowingRecentTweets(
numFollowingRecentTweets,
numRandomFollowers,
Expand Down
17 changes: 10 additions & 7 deletions auto-agents-framework/src/agents/tools/postTweetTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { TwitterApi } from '../../services/twitter/types.js';
import { ToolNode } from '@langchain/langgraph/prebuilt';
import { AIMessage } from '@langchain/core/messages';
import { config } from '../../config/index.js';
import { id } from 'ethers';

const logger = createLogger('post-tweet-tool');

Expand All @@ -17,14 +16,18 @@ export const createPostTweetTool = (twitterApi: TwitterApi) =>
func: async ({ tweet, inReplyTo }: { tweet: string; inReplyTo?: string }) => {
try {
if (config.twitterConfig.POST_TWEETS) {
const postedTweet = await twitterApi.sendTweet(tweet, inReplyTo);
//TODO: After sending the tweet, we need to get the latest tweet, ensure it is the same as we sent and return it
//This has not been working as expected, so we need to investigate this later
// logger.info('Tweet posted successfully', {
// postedTweet: { id: postedTweet?.id, text: postedTweet?.text },
// });
const postedTweet = await twitterApi
.sendTweet(tweet, inReplyTo)
.then(_ =>
!inReplyTo ? twitterApi.scraper.getLatestTweet(twitterApi.username) : undefined,
);

logger.info('Tweet posted successfully', {
postedTweet: { id: postedTweet?.id, text: postedTweet?.text },
});
return {
postedTweet: true,
postedTweetId: postedTweet?.id,
};
} else {
logger.info('Tweet not posted', { tweet });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const postResponse = async (
const tweet = await invokePostTweetTool(config.toolNode, response.content, decision.tweet.id);
return {
...response,
decisionInfo: decisionInfo,
//tweetId: tweet ? tweet.id : null
};
};
Expand Down Expand Up @@ -80,15 +81,15 @@ export const createGenerateTweetNode =
trendAnalysis: state.trendAnalysis,
recentTweets,
});
//TODO: After sending the tweet, we need to get the latest tweet, ensure it is the same as we sent and return it
//This has not been working as expected, so we need to investigate this later

const postedTweet = await invokePostTweetTool(config.toolNode, generatedTweet.tweet);

// Transform the data into an array format expected by DSN
const formattedDsnData = [
...postedResponses.map(response => ({
type: 'response',
content: response.content,
decisionInfo: response.decisionInfo,
//tweetId: response.tweetId,
strategy: response.strategy,
})),
Expand All @@ -100,7 +101,7 @@ export const createGenerateTweetNode =
{
type: 'generated_tweet',
content: generatedTweet.tweet,
//tweetId: postedTweet ? postedTweet.id : null,
tweetId: postedTweet ? postedTweet.postedTweetId : null,
},
];

Expand Down
1 change: 1 addition & 0 deletions auto-agents-framework/src/agents/workflows/kol/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export const responseSystemPrompt = await PromptTemplate.fromTemplate(
Personality & Style:
${character.description}
${character.personality}
${character.rules}
${character.replyStyle}
${character.contentFocus}
Expand Down
103 changes: 36 additions & 67 deletions auto-agents-framework/src/services/twitter/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,72 +67,32 @@ const getUserReplyIds = async (
return replyIdSet;
};

const getReplyThread = (tweet: Tweet, conversation: Tweet[]): Tweet[] => {
let tweetReply = tweet.inReplyToStatusId
? conversation.find(t => t.id === tweet.inReplyToStatusId)
: tweet;

const replyThread: Tweet[] = tweetReply ? [tweetReply] : [];
while (tweetReply && tweetReply.inReplyToStatusId) {
const nextReply = conversation.find(t => t.inReplyToStatusId === tweetReply?.id);
if (!nextReply) {
break;
}
logger.info('Next Reply', { nextReply: nextReply?.id });
replyThread.push(nextReply);
tweetReply = nextReply;
}
return replyThread;
};

const getMyUnrepliedToMentions = async (
scraper: Scraper,
username: string,
maxResults: number = 50,
sinceId?: string,
): Promise<Tweet[]> => {
const conversationCache = new Map<string, Tweet[]>();

//TODO: This is not the way to get the thread, it is just a quick fix
const getThread = async (scraper: Scraper, tweetId: string): Promise<Tweet[]> => {
const initialTweet = await scraper.getTweet(tweetId);

if (!initialTweet) {
logger.warn(`Tweet ${tweetId} not found or deleted`);
return [];
}

const conversationId = initialTweet.conversationId || initialTweet.id;

// Check cache first
const cachedConversation = conversationCache.get(conversationId!);
if (cachedConversation) {
return cachedConversation;
}

const conversationTweets = new Map<string, Tweet>();
let rootTweet = initialTweet;

// If the conversation root differs
if (initialTweet.conversationId && initialTweet.conversationId !== initialTweet.id) {
const conversationRoot = await scraper.getTweet(initialTweet.conversationId);
if (conversationRoot) {
rootTweet = conversationRoot;
conversationTweets.set(rootTweet.id!, rootTweet);
logger.info('Found conversation root tweet:', {
id: rootTweet.id,
conversationId: rootTweet.conversationId,
});
}
} else {
conversationTweets.set(rootTweet.id!, rootTweet);
}

try {
//TODO: This does not return direct replies to the loggedin user, not sure why. Will need to investigate later
const conversationIterator = scraper.searchTweets(
`conversation_id:${conversationId}`,
100,
SearchMode.Latest,
);
for await (const tweet of conversationIterator) {
conversationTweets.set(tweet.id!, tweet);
}
} catch (error) {
logger.warn(`Error fetching conversation: ${error}`);
return [rootTweet, initialTweet];
}

const thread = Array.from(conversationTweets.values());
conversationCache.set(conversationId!, Array.from(conversationTweets.values()));
return thread;
};

logger.info('Getting my mentions', { username, maxResults, sinceId });

// get all mentions of the user (excluding user’s own tweets)
const query = `@${username} -from:${username}`;
const mentionIterator = scraper.searchTweets(query, maxResults, SearchMode.Latest);

Expand All @@ -141,6 +101,7 @@ const getMyUnrepliedToMentions = async (

// filter out any mention we've already replied to
const newMentions: Tweet[] = [];
const conversations = new Map<string, Tweet[]>();
for await (const tweet of mentionIterator) {
// Stop if we've reached or passed the sinceId
if (sinceId && tweet.id && tweet.id <= sinceId) {
Expand All @@ -154,22 +115,30 @@ const getMyUnrepliedToMentions = async (
}

newMentions.push(tweet);
if (!conversations.has(tweet.id!)) {
const conversation = await iterateResponse(
scraper.searchTweets(`conversation_id:${tweet.conversationId}`, 100, SearchMode.Latest),
);
const initialTweet = await scraper.getTweet(tweet.conversationId!);
if (initialTweet) {
conversation.push(initialTweet);
}
conversations.set(tweet.conversationId!, conversation);
}

// Stop if we already have enough
if (newMentions.length >= maxResults) {
break;
}
}

const withThreads = await Promise.all(
newMentions.map(async mention => {
const thread = await getThread(scraper, mention.id!);
return {
...mention,
thread,
};
}),
);
const withThreads = newMentions.map(mention => {
const thread = getReplyThread(mention, conversations.get(mention.conversationId!)!);
return {
...mention,
thread,
};
});

return withThreads;
};
Expand Down

0 comments on commit a8e4048

Please sign in to comment.