Skip to content

Commit f8b9e3e

Browse files
0xCardinalErrorwtfsayo
andauthoredJan 16, 2025
feat: dexscreener trending (#2325)
* add to main index * cleanup --------- Co-authored-by: Sayo <hi@sayo.wtf>
1 parent fd35fd9 commit f8b9e3e

File tree

3 files changed

+401
-8
lines changed

3 files changed

+401
-8
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export * from "./tokenAction.ts";
1+
export * from "./tokenAction";
2+
export * from "./trendsAction";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,383 @@
1+
import {
2+
Action,
3+
IAgentRuntime,
4+
Memory,
5+
State,
6+
HandlerCallback,
7+
elizaLogger,
8+
getEmbeddingZeroVector,
9+
} from "@elizaos/core";
10+
11+
interface TokenProfile {
12+
url: string;
13+
description?: string;
14+
chainId: string;
15+
tokenAddress: string;
16+
}
17+
18+
const createTokenMemory = async (
19+
runtime: IAgentRuntime,
20+
_message: Memory,
21+
formattedOutput: string
22+
) => {
23+
const memory: Memory = {
24+
userId: _message.userId,
25+
agentId: _message.agentId,
26+
roomId: _message.roomId,
27+
content: { text: formattedOutput },
28+
createdAt: Date.now(),
29+
embedding: getEmbeddingZeroVector(),
30+
};
31+
await runtime.messageManager.createMemory(memory);
32+
};
33+
34+
export const latestTokensTemplate = `Determine if this is a request for latest tokens. If it is one of the specified situations, perform the corresponding action:
35+
36+
Situation 1: "Get latest tokens"
37+
- Message contains: words like "latest", "new", "recent" AND "tokens"
38+
- Example: "Show me the latest tokens" or "What are the new tokens?"
39+
- Action: Get the most recent tokens listed
40+
41+
Previous conversation for context:
42+
{{conversation}}
43+
44+
You are replying to: {{message}}
45+
`;
46+
47+
export class LatestTokensAction implements Action {
48+
name = "GET_LATEST_TOKENS";
49+
similes = ["FETCH_NEW_TOKENS", "CHECK_RECENT_TOKENS", "LIST_NEW_TOKENS"];
50+
description = "Get the latest tokens from DexScreener API";
51+
suppressInitialMessage = true;
52+
template = latestTokensTemplate;
53+
54+
async validate(runtime: IAgentRuntime, message: Memory): Promise<boolean> {
55+
const content =
56+
typeof message.content === "string"
57+
? message.content
58+
: message.content?.text;
59+
60+
if (!content) return false;
61+
62+
const hasLatestKeyword = /\b(latest|new|recent)\b/i.test(content);
63+
const hasTokensKeyword = /\b(tokens?|coins?|crypto)\b/i.test(content);
64+
65+
return hasLatestKeyword && hasTokensKeyword;
66+
}
67+
68+
async handler(
69+
runtime: IAgentRuntime,
70+
message: Memory,
71+
state?: State,
72+
_options: { [key: string]: unknown } = {},
73+
callback?: HandlerCallback
74+
): Promise<boolean> {
75+
elizaLogger.log("Starting GET_LATEST_TOKENS handler...");
76+
77+
try {
78+
const response = await fetch(
79+
"https://api.dexscreener.com/token-profiles/latest/v1",
80+
{
81+
method: "GET",
82+
headers: {
83+
accept: "application/json",
84+
},
85+
}
86+
);
87+
88+
if (!response.ok) {
89+
throw new Error(`HTTP error! status: ${response.status}`);
90+
}
91+
92+
const tokens: TokenProfile[] = await response.json();
93+
94+
const formattedOutput = tokens
95+
.map((token) => {
96+
const description =
97+
token.description || "No description available";
98+
return `Chain: ${token.chainId}\nToken Address: ${token.tokenAddress}\nURL: ${token.url}\nDescription: ${description}\n\n`;
99+
})
100+
.join("");
101+
102+
await createTokenMemory(runtime, message, formattedOutput);
103+
104+
if (callback) {
105+
await callback({
106+
text: formattedOutput,
107+
action: this.name,
108+
});
109+
}
110+
111+
return true;
112+
} catch (error) {
113+
elizaLogger.error("Error fetching latest tokens:", error);
114+
115+
if (callback) {
116+
await callback({
117+
text: `Failed to fetch latest tokens: ${error.message}`,
118+
action: this.name,
119+
});
120+
}
121+
122+
return false;
123+
}
124+
}
125+
126+
examples = [
127+
[
128+
{
129+
user: "{{user}}",
130+
content: {
131+
text: "show me the latest tokens",
132+
},
133+
},
134+
{
135+
user: "{{system}}",
136+
content: {
137+
text: "Here are the latest tokens added to DexScreener...",
138+
action: "GET_LATEST_TOKENS",
139+
},
140+
},
141+
],
142+
];
143+
}
144+
145+
export const latestBoostedTemplate = `Determine if this is a request for latest boosted tokens. If it is one of the specified situations, perform the corresponding action:
146+
147+
Situation 1: "Get latest boosted tokens"
148+
- Message contains: words like "latest", "new", "recent" AND "boosted tokens"
149+
- Example: "Show me the latest boosted tokens" or "What are the new promoted tokens?"
150+
- Action: Get the most recent boosted tokens
151+
152+
Previous conversation for context:
153+
{{conversation}}
154+
155+
You are replying to: {{message}}
156+
`;
157+
158+
export class LatestBoostedTokensAction implements Action {
159+
name = "GET_LATEST_BOOSTED_TOKENS";
160+
similes = [
161+
"FETCH_NEW_BOOSTED_TOKENS",
162+
"CHECK_RECENT_BOOSTED_TOKENS",
163+
"LIST_NEW_BOOSTED_TOKENS",
164+
];
165+
description = "Get the latest boosted tokens from DexScreener API";
166+
suppressInitialMessage = true;
167+
template = latestBoostedTemplate;
168+
169+
async validate(runtime: IAgentRuntime, message: Memory): Promise<boolean> {
170+
const content =
171+
typeof message.content === "string"
172+
? message.content
173+
: message.content?.text;
174+
175+
if (!content) return false;
176+
177+
const hasLatestKeyword = /\b(latest|new|recent)\b/i.test(content);
178+
const hasBoostedKeyword = /\b(boosted|promoted|featured)\b/i.test(
179+
content
180+
);
181+
const hasTokensKeyword = /\b(tokens?|coins?|crypto)\b/i.test(content);
182+
183+
return hasLatestKeyword && (hasBoostedKeyword || hasTokensKeyword);
184+
}
185+
186+
async handler(
187+
runtime: IAgentRuntime,
188+
message: Memory,
189+
state?: State,
190+
_options: { [key: string]: unknown } = {},
191+
callback?: HandlerCallback
192+
): Promise<boolean> {
193+
elizaLogger.log("Starting GET_LATEST_BOOSTED_TOKENS handler...");
194+
195+
try {
196+
const response = await fetch(
197+
"https://api.dexscreener.com/token-boosts/latest/v1",
198+
{
199+
method: "GET",
200+
headers: {
201+
accept: "application/json",
202+
},
203+
}
204+
);
205+
206+
if (!response.ok) {
207+
throw new Error(`HTTP error! status: ${response.status}`);
208+
}
209+
210+
const tokens: TokenProfile[] = await response.json();
211+
212+
const formattedOutput = tokens
213+
.map((token) => {
214+
const description =
215+
token.description || "No description available";
216+
return `Chain: ${token.chainId}\nToken Address: ${token.tokenAddress}\nURL: ${token.url}\nDescription: ${description}\n\n`;
217+
})
218+
.join("");
219+
220+
await createTokenMemory(runtime, message, formattedOutput);
221+
222+
if (callback) {
223+
await callback({
224+
text: formattedOutput,
225+
action: this.name,
226+
});
227+
}
228+
229+
return true;
230+
} catch (error) {
231+
elizaLogger.error("Error fetching latest boosted tokens:", error);
232+
233+
if (callback) {
234+
await callback({
235+
text: `Failed to fetch latest boosted tokens: ${error.message}`,
236+
action: this.name,
237+
});
238+
}
239+
240+
return false;
241+
}
242+
}
243+
244+
examples = [
245+
[
246+
{
247+
user: "{{user}}",
248+
content: {
249+
text: "show me the latest boosted tokens",
250+
},
251+
},
252+
{
253+
user: "{{system}}",
254+
content: {
255+
text: "Here are the latest boosted tokens on DexScreener...",
256+
action: "GET_LATEST_BOOSTED_TOKENS",
257+
},
258+
},
259+
],
260+
];
261+
}
262+
263+
export const topBoostedTemplate = `Determine if this is a request for top boosted tokens. If it is one of the specified situations, perform the corresponding action:
264+
265+
Situation 1: "Get top boosted tokens"
266+
- Message contains: words like "top", "best", "most" AND "boosted tokens"
267+
- Example: "Show me the top boosted tokens" or "What are the most promoted tokens?"
268+
- Action: Get the tokens with most active boosts
269+
270+
Previous conversation for context:
271+
{{conversation}}
272+
273+
You are replying to: {{message}}
274+
`;
275+
276+
export class TopBoostedTokensAction implements Action {
277+
name = "GET_TOP_BOOSTED_TOKENS";
278+
similes = [
279+
"FETCH_MOST_BOOSTED_TOKENS",
280+
"CHECK_HIGHEST_BOOSTED_TOKENS",
281+
"LIST_TOP_BOOSTED_TOKENS",
282+
];
283+
description = "Get tokens with most active boosts from DexScreener API";
284+
suppressInitialMessage = true;
285+
template = topBoostedTemplate;
286+
287+
async validate(runtime: IAgentRuntime, message: Memory): Promise<boolean> {
288+
const content =
289+
typeof message.content === "string"
290+
? message.content
291+
: message.content?.text;
292+
293+
if (!content) return false;
294+
295+
const hasTopKeyword = /\b(top|best|most)\b/i.test(content);
296+
const hasBoostedKeyword = /\b(boosted|promoted|featured)\b/i.test(
297+
content
298+
);
299+
const hasTokensKeyword = /\b(tokens?|coins?|crypto)\b/i.test(content);
300+
301+
return hasTopKeyword && (hasBoostedKeyword || hasTokensKeyword);
302+
}
303+
304+
async handler(
305+
runtime: IAgentRuntime,
306+
message: Memory,
307+
state?: State,
308+
_options: { [key: string]: unknown } = {},
309+
callback?: HandlerCallback
310+
): Promise<boolean> {
311+
elizaLogger.log("Starting GET_TOP_BOOSTED_TOKENS handler...");
312+
313+
try {
314+
const response = await fetch(
315+
"https://api.dexscreener.com/token-boosts/top/v1",
316+
{
317+
method: "GET",
318+
headers: {
319+
accept: "application/json",
320+
},
321+
}
322+
);
323+
324+
if (!response.ok) {
325+
throw new Error(`HTTP error! status: ${response.status}`);
326+
}
327+
328+
const tokens: TokenProfile[] = await response.json();
329+
330+
const formattedOutput = tokens
331+
.map((token) => {
332+
const description =
333+
token.description || "No description available";
334+
return `Chain: ${token.chainId}\nToken Address: ${token.tokenAddress}\nURL: ${token.url}\nDescription: ${description}\n\n`;
335+
})
336+
.join("");
337+
338+
await createTokenMemory(runtime, message, formattedOutput);
339+
340+
if (callback) {
341+
await callback({
342+
text: formattedOutput,
343+
action: this.name,
344+
});
345+
}
346+
347+
return true;
348+
} catch (error) {
349+
elizaLogger.error("Error fetching top boosted tokens:", error);
350+
351+
if (callback) {
352+
await callback({
353+
text: `Failed to fetch top boosted tokens: ${error.message}`,
354+
action: this.name,
355+
});
356+
}
357+
358+
return false;
359+
}
360+
}
361+
362+
examples = [
363+
[
364+
{
365+
user: "{{user}}",
366+
content: {
367+
text: "show me the top boosted tokens",
368+
},
369+
},
370+
{
371+
user: "{{system}}",
372+
content: {
373+
text: "Here are the tokens with the most active boosts on DexScreener...",
374+
action: "GET_TOP_BOOSTED_TOKENS",
375+
},
376+
},
377+
],
378+
];
379+
}
380+
381+
export const latestTokensAction = new LatestTokensAction();
382+
export const latestBoostedTokensAction = new LatestBoostedTokensAction();
383+
export const topBoostedTokensAction = new TopBoostedTokensAction();

0 commit comments

Comments
 (0)