Skip to content

Commit d876eb8

Browse files
committed
fix analyze for chatDKG and polkabot on twitter
1 parent 449629c commit d876eb8

File tree

1 file changed

+67
-13
lines changed

1 file changed

+67
-13
lines changed

packages/plugin-dkg/src/actions/dkgAnalyzeSentiment.ts

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,39 @@ import {
2222
} from "../constants";
2323
import { fetchFileFromUrl, getSentimentChart } from "../http-helper";
2424

25+
// Tweet type for sentiment analysis
26+
interface Tweet {
27+
id: string;
28+
text: string;
29+
username: string;
30+
name: string;
31+
userId: string;
32+
timestamp: number;
33+
permanentUrl: string;
34+
conversationId: string;
35+
inReplyToStatusId?: string;
36+
hashtags: string[];
37+
mentions: string[];
38+
photos: string[];
39+
urls: string[];
40+
videos: string[];
41+
views?: number;
42+
}
43+
2544
let DkgClient: any = null;
2645

2746
// Helper to get Twitter client from runtime
2847
function getTwitterClient(runtime: IAgentRuntime): any {
2948
// Access the Twitter client from the runtime's clients
30-
const twitterClient = (runtime as any).clients?.find(
31-
(client: any) => client.constructor.name === "TwitterClientInterface"
32-
);
33-
34-
if (!twitterClient) {
49+
// clients is a Record<string, any> object, not an array
50+
const clients = (runtime as any).clients;
51+
52+
if (!clients || !clients.twitter) {
3553
throw new Error("Twitter client not found in runtime");
3654
}
3755

38-
return twitterClient;
56+
// The twitter client has a .client property with the actual API methods
57+
return clients.twitter;
3958
}
4059

4160
// Helper to convert v2 API tweet format to legacy format for sentiment analysis
@@ -245,8 +264,17 @@ export const dkgAnalyzeSentiment: Action = {
245264
nodeApiVersion: "/v1",
246265
});
247266

248-
const currentPost = String(state.currentPost);
249-
elizaLogger.log(`currentPost: ${currentPost}`);
267+
// Get the post content - from Twitter state or from direct message
268+
let currentPost = state.currentPost ? String(state.currentPost) : "";
269+
270+
// Fallback to message content if currentPost is empty/undefined (e.g., API calls)
271+
if (!currentPost || currentPost === "undefined") {
272+
const messageText = _message.content?.text || "";
273+
currentPost = `Text: ${messageText}`;
274+
elizaLogger.log(`Using message content as currentPost: ${currentPost}`);
275+
} else {
276+
elizaLogger.log(`currentPost: ${currentPost}`);
277+
}
250278

251279
const idRegex = /ID:\s(\d+)/;
252280
let match = currentPost.match(idRegex);
@@ -298,14 +326,30 @@ export const dkgAnalyzeSentiment: Action = {
298326
return true;
299327
}
300328

329+
// Clean the topic for Twitter search - remove $ and # operators (require Pro tier)
330+
// but keep the original topic for display
331+
const originalTopic = topic.trim();
332+
const searchTopic = originalTopic.replace(/[$#]/g, '').trim();
333+
334+
// Validate searchTopic is not empty after stripping symbols
335+
if (!searchTopic || searchTopic.length < 2) {
336+
elizaLogger.warn(`Search topic too short after cleaning: "${searchTopic}" (original: "${originalTopic}")`);
337+
callback?.({
338+
text: `I couldn't identify a valid ticker or asset name. Please specify a stock (like AAPL, TSLA) or cryptocurrency (like BTC, ETH) to analyze.`,
339+
action: "REPLY"
340+
});
341+
return true;
342+
}
343+
301344
// Use OAuth v2 search (already implemented in client-twitter)
302-
elizaLogger.log(`Searching for tweets about: ${topic}`);
345+
elizaLogger.log(`Searching for tweets about: ${searchTopic} (original: ${originalTopic})`);
303346

304347
let searchResults;
305348
try {
306349
// Access the client's fetchSearchTweets method
350+
// Use searchTopic (without $ or #) to avoid cashtag/hashtag operator issues on Basic tier
307351
searchResults = await twitterClient.client.fetchSearchTweets(
308-
topic,
352+
searchTopic,
309353
100,
310354
2 // SearchMode.Latest
311355
);
@@ -321,6 +365,16 @@ export const dkgAnalyzeSentiment: Action = {
321365
let tweets = searchResults.tweets.map(convertTweetFormat);
322366
elizaLogger.log(`Successfully fetched ${tweets.length} tweets.`);
323367

368+
// Handle case where no tweets are found
369+
if (!tweets || tweets.length === 0) {
370+
elizaLogger.warn(`No tweets found for topic: ${searchTopic}`);
371+
callback?.({
372+
text: `I couldn't find any recent tweets about ${originalTopic}. This could mean there's very little discussion about this asset right now, or try a different ticker/name.`,
373+
action: "REPLY"
374+
});
375+
return true;
376+
}
377+
324378
tweets = tweets.map((t) => ({
325379
...t,
326380
vaderSentimentScore: calculateVaderScore(t.text),
@@ -332,7 +386,7 @@ export const dkgAnalyzeSentiment: Action = {
332386

333387
const { ka, averageScore, numOfTotalTweets } = await structureKA(
334388
tweets,
335-
topic,
389+
originalTopic,
336390
twitterUser,
337391
{
338392
dkgClient: DkgClient,
@@ -411,13 +465,13 @@ export const dkgAnalyzeSentiment: Action = {
411465
elizaLogger.log(JSON.stringify(createAssetResult));
412466

413467
// Build the sentiment analysis response
414-
let tweetContent = `${topic} sentiment based on top ${tweets.length} latest posts`;
468+
let tweetContent = `${originalTopic} sentiment based on top ${tweets.length} latest posts`;
415469
if (numOfTotalTweets - tweets.length > 0) {
416470
tweetContent += ` and ${numOfTotalTweets - tweets.length} existing analysis Knowledge Assets`;
417471
}
418472
tweetContent += ` from the past 48 hours: ${sentiment}\n\n`;
419473

420-
tweetContent += `Top 5 most influential accounts analyzed for ${topic}:\n`;
474+
tweetContent += `Top 5 most influential accounts analyzed for ${originalTopic}:\n`;
421475
tweetContent +=
422476
topAuthors
423477
.slice(0, 5)

0 commit comments

Comments
 (0)