create-message-kit 1.2.14 → 1.2.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. package/index.js +19 -17
  2. package/package.json +1 -1
  3. package/templates/coinbase-agent/.cursorrules +290 -0
  4. package/templates/coinbase-agent/.env.example +4 -0
  5. package/templates/coinbase-agent/.yarnrc.yml +9 -0
  6. package/templates/coinbase-agent/package.json +22 -0
  7. package/templates/coinbase-agent/src/index.ts +31 -0
  8. package/templates/coinbase-agent/src/plugins/learnweb3.ts +96 -0
  9. package/templates/coinbase-agent/src/plugins/redis.ts +15 -0
  10. package/templates/coinbase-agent/src/prompt.ts +64 -0
  11. package/templates/coinbase-agent/src/skills/drip.ts +83 -0
  12. package/templates/coinbase-agent/src/skills/mint.ts +99 -0
  13. package/templates/coinbase-agent/src/skills/pay.ts +35 -0
  14. package/templates/coinbase-agent/src/skills/swap.ts +52 -0
  15. package/templates/ens/.env.example +1 -0
  16. package/templates/ens/.yarnrc.yml +5 -0
  17. package/templates/ens/src/index.ts +1 -1
  18. package/templates/ens/src/prompt.ts +1 -14
  19. package/templates/faucet/.cursorrules +290 -0
  20. package/templates/faucet/.env.example +5 -0
  21. package/templates/faucet/.yarnrc.yml +9 -0
  22. package/templates/faucet/package.json +22 -0
  23. package/templates/faucet/src/index.ts +39 -0
  24. package/templates/faucet/src/plugins/learnweb3.ts +96 -0
  25. package/templates/faucet/src/plugins/redis.ts +15 -0
  26. package/templates/faucet/src/prompt.ts +11 -0
  27. package/templates/faucet/src/skills/faucet.ts +140 -0
  28. package/templates/gated-group/.cursorrules +290 -0
  29. package/templates/gated-group/.env.example +3 -0
  30. package/templates/gated-group/.yarnrc.yml +9 -0
  31. package/templates/gated-group/package.json +23 -0
  32. package/templates/gated-group/src/index.ts +17 -0
  33. package/templates/gated-group/src/plugins/alchemy.ts +27 -0
  34. package/templates/gated-group/src/plugins/xmtp.ts +137 -0
  35. package/templates/gated-group/src/prompt.ts +11 -0
  36. package/templates/gated-group/src/skills/gated.ts +88 -0
  37. package/templates/gm/.cursorrules +290 -0
  38. package/templates/gm/.env.example +2 -0
  39. package/templates/gm/.yarnrc.yml +9 -0
  40. package/templates/gm/package.json +20 -0
  41. package/templates/gm/src/index.ts +8 -0
  42. package/templates/playground/.cursorrules +290 -0
  43. package/templates/playground/.env.example +6 -0
  44. package/templates/playground/.yarnrc.yml +9 -0
  45. package/templates/playground/package.json +26 -0
  46. package/templates/playground/src/index.ts +40 -0
  47. package/templates/playground/src/plugins/alchemy.ts +27 -0
  48. package/templates/playground/src/plugins/minilog.ts +26 -0
  49. package/templates/playground/src/plugins/usdc.ts +99 -0
  50. package/templates/playground/src/plugins/xmtp.ts +137 -0
  51. package/templates/playground/src/prompt.ts +11 -0
  52. package/templates/playground/src/skills/broadcast.ts +39 -0
  53. package/templates/playground/src/skills/cash.ts +122 -0
  54. package/templates/playground/src/skills/cryptoPrice.ts +63 -0
  55. package/templates/playground/src/skills/dalle.ts +61 -0
  56. package/templates/playground/src/skills/gated.ts +88 -0
  57. package/templates/playground/src/skills/search.ts +159 -0
  58. package/templates/playground/src/skills/todo.ts +79 -0
  59. package/templates/playground/src/skills/token.ts +57 -0
  60. package/templates/playground/src/skills/web.ts +83 -0
  61. package/templates/playground/src/skills/wordle.ts +96 -0
  62. package/templates/simple/.env.example +1 -0
  63. package/templates/simple/.yarnrc.yml +5 -0
  64. package/templates/simple/src/index.ts +1 -1
  65. package/templates/simple/src/prompt.ts +2 -0
  66. package/templates/thegeneralstore/.cursorrules +290 -0
  67. package/templates/thegeneralstore/.env.example +9 -0
  68. package/templates/thegeneralstore/.yarnrc.yml +9 -0
  69. package/templates/thegeneralstore/package.json +24 -0
  70. package/templates/thegeneralstore/src/data/db.json +812 -0
  71. package/templates/thegeneralstore/src/index.ts +37 -0
  72. package/templates/thegeneralstore/src/plugins/learnweb3.ts +96 -0
  73. package/templates/thegeneralstore/src/plugins/lowdb.ts +11 -0
  74. package/templates/thegeneralstore/src/plugins/notion.ts +89 -0
  75. package/templates/thegeneralstore/src/plugins/redis.ts +15 -0
  76. package/templates/thegeneralstore/src/prompt.md +51 -0
  77. package/templates/thegeneralstore/src/prompt.ts +3 -0
  78. package/templates/thegeneralstore/src/skills/faucet.ts +114 -0
  79. package/templates/thegeneralstore/src/skills/notion.ts +17 -0
  80. package/templates/thegeneralstore/src/skills/poap.ts +48 -0
  81. package/templates/thegeneralstore/src/skills.ts +37 -0
  82. package/templates/toss/.cursorrules +290 -0
  83. package/templates/toss/.env.example +7 -0
  84. package/templates/toss/.yarnrc.yml +9 -0
  85. package/templates/toss/package.json +21 -0
  86. package/templates/toss/src/index.ts +33 -0
  87. package/templates/toss/src/plugins/helpers.ts +185 -0
  88. package/templates/toss/src/plugins/redis.ts +78 -0
  89. package/templates/toss/src/prompt.ts +54 -0
  90. package/templates/toss/src/skills/toss.ts +489 -0
@@ -0,0 +1,39 @@
1
+ import { XMTPContext, Skill } from "@xmtp/message-kit";
2
+
3
+ export const broadcast: Skill[] = [
4
+ {
5
+ skill: "send",
6
+ adminOnly: true,
7
+ handler: handler,
8
+ examples: ["/send Hello everyone, the event is starting now!"],
9
+ description: "Send updates to all subscribers.",
10
+ params: {
11
+ message: {
12
+ type: "prompt",
13
+ },
14
+ },
15
+ },
16
+ ];
17
+
18
+ async function handler(context: XMTPContext) {
19
+ const {
20
+ message: {
21
+ content: {
22
+ params: { message },
23
+ },
24
+ },
25
+ } = context;
26
+
27
+ const fakeSubscribers = ["0x93E2fc3e99dFb1238eB9e0eF2580EFC5809C7204"];
28
+ await context.send("This is how your message will look like:");
29
+ await context.send(message);
30
+ const emailResponse = await context.awaitResponse(
31
+ "Are you sure you want to send this broadcast?\nType 'yes' to confirm.",
32
+ ["yes", "no"],
33
+ );
34
+ if (emailResponse === "yes") {
35
+ await context.send("Sending broadcast...");
36
+ await context.sendTo(message, fakeSubscribers);
37
+ await context.send("Broadcast sent!");
38
+ }
39
+ }
@@ -0,0 +1,122 @@
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+ import type { Skill } from "@xmtp/message-kit";
3
+ import { USDCWallet } from "../plugins/usdc.js";
4
+
5
+ export const cash: Skill[] = [
6
+ {
7
+ skill: "balance",
8
+ handler: balanceHandler,
9
+ examples: ["/balance"],
10
+ description: "Check your balance.",
11
+ },
12
+ {
13
+ skill: "fund",
14
+ handler: fundHandler,
15
+ examples: ["/fund 1", "/fund 10"],
16
+ params: {
17
+ amount: {
18
+ type: "number",
19
+ default: "",
20
+ },
21
+ },
22
+ description: "Fund your wallet. Returns a url to fund your wallet.",
23
+ },
24
+ {
25
+ skill: "transfer",
26
+ handler: transferHandler,
27
+ examples: ["/transfer 0x40f08f0f853d1c42c61815652b7ccd5a50f0be09 1"],
28
+ params: {
29
+ address: {
30
+ type: "address",
31
+ default: "",
32
+ },
33
+ amount: {
34
+ type: "number",
35
+ default: "",
36
+ },
37
+ },
38
+ description: "Transfer USDC to another address.",
39
+ },
40
+ ];
41
+
42
+ async function balanceHandler(context: XMTPContext) {
43
+ const {
44
+ message: { sender },
45
+ } = context;
46
+ const usdcWallet = new USDCWallet(sender.address);
47
+ const { usdc } = await usdcWallet.checkBalances();
48
+ await context.send(
49
+ `Your balance is ${usdc} USDC. let me know if you want check again or to fund your wallet.`,
50
+ );
51
+ }
52
+
53
+ async function fundHandler(context: XMTPContext) {
54
+ try {
55
+ const {
56
+ message: {
57
+ sender,
58
+ content: {
59
+ params: { amount },
60
+ },
61
+ },
62
+ } = context;
63
+ const usdcWallet = new USDCWallet(sender.address);
64
+ const { usdc } = await usdcWallet.checkBalances();
65
+ const MAX_USDC = 10;
66
+
67
+ if (usdc >= MAX_USDC) {
68
+ await context.send(`Your balance is maxed out at ${MAX_USDC} USDC.`);
69
+ return;
70
+ }
71
+
72
+ const remainingLimit = MAX_USDC - usdc;
73
+ let fundAmount: number;
74
+
75
+ if (!amount) {
76
+ const options = Array.from(
77
+ { length: Math.floor(remainingLimit) },
78
+ (_, i) => (i + 1).toString(),
79
+ );
80
+ const response = await context.awaitResponse(
81
+ `Please specify the amount of USDC to prefund (1 to ${remainingLimit}):`,
82
+ options,
83
+ );
84
+ fundAmount = parseInt(response);
85
+ } else {
86
+ fundAmount = parseInt(amount);
87
+ }
88
+
89
+ if (isNaN(fundAmount) || fundAmount <= 0 || fundAmount > remainingLimit) {
90
+ await context.send(
91
+ `Invalid amount. Please specify a value between 1 and ${remainingLimit} USDC.`,
92
+ );
93
+ return;
94
+ }
95
+
96
+ await context.requestPayment(fundAmount, "USDC", usdcWallet.agentAddress);
97
+ await context.send(
98
+ "After funding, let me know so I can check your balance.",
99
+ );
100
+ } catch (error) {
101
+ await context.send(
102
+ "An error occurred while processing your request. Please try again.",
103
+ );
104
+ }
105
+ }
106
+
107
+ async function transferHandler(context: XMTPContext) {
108
+ const {
109
+ message: {
110
+ sender,
111
+ content: {
112
+ params: { address, amount },
113
+ },
114
+ },
115
+ } = context;
116
+ const usdcWallet = new USDCWallet(sender.address);
117
+ if (amount > 10) {
118
+ await context.send("You can only transfer up to 10 USDC at a time.");
119
+ return;
120
+ }
121
+ await usdcWallet.transferUsdc(address, amount);
122
+ }
@@ -0,0 +1,63 @@
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+ import type { Skill } from "@xmtp/message-kit";
3
+
4
+ // Example skill structure
5
+ export const cryptoPrice: Skill[] = [
6
+ {
7
+ skill: "price",
8
+ handler: handler,
9
+ examples: ["/price BTC USD", "/price ETH EUR"],
10
+ description: "Get the current exchange rate for a cryptocurrency.",
11
+ params: {
12
+ crypto: {
13
+ type: "string",
14
+ },
15
+ currency: {
16
+ type: "string",
17
+ default: "USD",
18
+ },
19
+ },
20
+ },
21
+ ];
22
+
23
+ async function handler(context: XMTPContext) {
24
+ const {
25
+ message: {
26
+ content: {
27
+ params: { crypto, currency },
28
+ },
29
+ },
30
+ } = context;
31
+
32
+ try {
33
+ const response = await fetch(
34
+ `https://min-api.cryptocompare.com/data/price?fsym=${crypto}&tsyms=${currency}`
35
+ );
36
+
37
+ if (!response.ok) {
38
+ return {
39
+ code: response.status,
40
+ message: `Unable to fetch exchange rate: ${response.statusText}`,
41
+ };
42
+ }
43
+
44
+ const data = await response.json() as Record<string, number>;
45
+
46
+ if (!data[currency]) {
47
+ return {
48
+ code: 400,
49
+ message: `Could not find exchange rate for ${crypto}/${currency}`,
50
+ };
51
+ }
52
+
53
+ return {
54
+ code: 200,
55
+ message: `1 ${crypto} = ${data[currency]} ${currency}`,
56
+ };
57
+ } catch (error: any) {
58
+ return {
59
+ code: 500,
60
+ message: `Error fetching exchange rate: ${error.message}`,
61
+ };
62
+ }
63
+ }
@@ -0,0 +1,61 @@
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+
3
+ import type { Skill } from "@xmtp/message-kit";
4
+ import OpenAI from "openai";
5
+
6
+ const openai = new OpenAI({
7
+ apiKey: process.env.OPENAI_API_KEY,
8
+ });
9
+
10
+ export const dalle: Skill[] = [
11
+ {
12
+ skill: "image",
13
+ handler: handler,
14
+ description: "Generate an image based on a prompt.",
15
+ examples: ["/image dog with a ball"],
16
+ params: {
17
+ prompt: {
18
+ type: "prompt",
19
+ },
20
+ },
21
+ },
22
+ ];
23
+
24
+ export async function handler(context: XMTPContext) {
25
+ const {
26
+ message: {
27
+ sender,
28
+ content: {
29
+ params: { prompt },
30
+ },
31
+ },
32
+ } = context;
33
+
34
+ if (!prompt) {
35
+ return {
36
+ code: 400,
37
+ message: "Missing required parameters. Please provide a prompt.",
38
+ };
39
+ }
40
+
41
+ try {
42
+ const response = await openai.images.generate({
43
+ prompt: prompt,
44
+ n: 1,
45
+ size: "1024x1024",
46
+ });
47
+
48
+ const imageUrl = response.data[0].url;
49
+ console.log(imageUrl);
50
+ const message = `Here is the image generated for the prompt "${prompt}": ${imageUrl}`;
51
+ context.send(message);
52
+ } catch (error) {
53
+ // @ts-ignore
54
+ const message = `Failed to generate image. Error: ${error?.message}
55
+ }`;
56
+ return {
57
+ code: 500,
58
+ message,
59
+ };
60
+ }
61
+ }
@@ -0,0 +1,88 @@
1
+ import { XMTPContext, Skill, V3Client } from "@xmtp/message-kit";
2
+ import { createGroup } from "../plugins/xmtp.js";
3
+ import express from "express";
4
+ import { checkNft } from "../plugins/alchemy.js";
5
+ import { addToGroup } from "../plugins/xmtp.js";
6
+ export const gated: Skill[] = [
7
+ {
8
+ skill: "create",
9
+ examples: ["/create"],
10
+ handler: handler,
11
+ adminOnly: true,
12
+ description: "Create a new group.",
13
+ },
14
+ ];
15
+
16
+ async function handler(context: XMTPContext) {
17
+ const {
18
+ message: {
19
+ sender,
20
+ content: { skill },
21
+ },
22
+ client,
23
+ } = context;
24
+
25
+ if (skill === "create") {
26
+ const group = await createGroup(
27
+ client,
28
+ sender.address,
29
+ client.accountAddress,
30
+ );
31
+
32
+ await context.send(
33
+ `Group created!\n- ID: ${group?.id}\n- Group Frame URL: https://converse.xyz/group/${group?.id}: \n- This url will deelink to the group inside Converse\n- Once in the other group you can share the invite with your friends.`,
34
+ );
35
+ return;
36
+ } else {
37
+ await context.send(
38
+ "👋 Welcome to the Gated Bot Group!\nTo get started, type /create to set up a new group. 🚀\nThis example will check if the user has a particular nft and add them to the group if they do.\nOnce your group is created, you'll receive a unique Group ID and URL.\nShare the URL with friends to invite them to join your group!",
39
+ );
40
+ }
41
+ }
42
+
43
+ export function startGatedGroupServer(client: V3Client) {
44
+ async function addWalletToGroup(
45
+ walletAddress: string,
46
+ groupId: string,
47
+ ): Promise<string> {
48
+ const verified = await checkNft(walletAddress, "XMTPeople");
49
+ if (!verified) {
50
+ console.log("User cant be added to the group");
51
+ return "not verified";
52
+ } else {
53
+ try {
54
+ const added = await addToGroup(groupId, client, walletAddress);
55
+ if (added.code === 200) {
56
+ console.log(`Added wallet address: ${walletAddress} to the group`);
57
+ return "success";
58
+ } else {
59
+ return added.message;
60
+ }
61
+ } catch (error: any) {
62
+ console.log(error.message);
63
+ return "error";
64
+ }
65
+ }
66
+ }
67
+
68
+ // Endpoint to add wallet address to a group from an external source
69
+ const app = express();
70
+ app.use(express.json());
71
+ app.post("/add-wallet", async (req, res) => {
72
+ try {
73
+ const { walletAddress, groupId } = req.body;
74
+ const result = await addWalletToGroup(walletAddress, groupId);
75
+ res.status(200).send(result);
76
+ } catch (error: any) {
77
+ res.status(400).send(error.message);
78
+ }
79
+ });
80
+ // Start the servfalcheer
81
+ const PORT = process.env.PORT || 3000;
82
+ const url = process.env.URL || `http://localhost:${PORT}`;
83
+ app.listen(PORT, () => {
84
+ console.warn(
85
+ `Use this endpoint to add a wallet to a group indicated by the groupId\n${url}/add-wallet <body: {walletAddress, groupId}>`,
86
+ );
87
+ });
88
+ }
@@ -0,0 +1,159 @@
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+ import type { Skill } from "@xmtp/message-kit";
3
+ import OpenAI from "openai";
4
+
5
+ const openai = new OpenAI({
6
+ apiKey: process.env.OPENAI_API_KEY,
7
+ });
8
+
9
+ export const search: Skill[] = [
10
+ {
11
+ skill: "search",
12
+ examples: [
13
+ "/search what is the capital of France?",
14
+ "/search latest news about ethereum",
15
+ ],
16
+ handler: handler,
17
+ description:
18
+ "Search the internet and get summarized information from top results.",
19
+ params: {
20
+ query: {
21
+ type: "prompt",
22
+ },
23
+ },
24
+ },
25
+ ];
26
+
27
+ export async function handler(context: XMTPContext) {
28
+ const {
29
+ message: {
30
+ content: {
31
+ params: { query },
32
+ },
33
+ },
34
+ } = context;
35
+
36
+ try {
37
+ // Search and get top results
38
+ const searchResults = await searchDuckDuckGo(query);
39
+ if (!searchResults.length) {
40
+ return {
41
+ code: 404,
42
+ message: "No results found for your query.",
43
+ };
44
+ }
45
+
46
+ // Fetch content from each result
47
+ const contents = await Promise.all(
48
+ searchResults.slice(0, 3).map(async (url) => {
49
+ try {
50
+ const response = await fetch(url, {
51
+ headers: {
52
+ "User-Agent":
53
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
54
+ },
55
+ });
56
+ const html = await response.text();
57
+ return extractContent(html);
58
+ } catch (error) {
59
+ console.error(`Error fetching ${url}:`, error);
60
+ return "";
61
+ }
62
+ }),
63
+ );
64
+
65
+ // Combine and summarize the content
66
+ const summary = await getAISummary(contents.join("\n"), query);
67
+
68
+ return {
69
+ code: 200,
70
+ message: summary,
71
+ };
72
+ } catch (error: any) {
73
+ return {
74
+ code: 500,
75
+ message: `Search failed: ${error.message}`,
76
+ };
77
+ }
78
+ }
79
+
80
+ async function searchDuckDuckGo(query: string): Promise<string[]> {
81
+ try {
82
+ // DuckDuckGo's lite version is more friendly to programmatic access
83
+ const response = await fetch(
84
+ `https://duckduckgo.com/lite?q=${encodeURIComponent(query)}`,
85
+ {
86
+ headers: {
87
+ "User-Agent":
88
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
89
+ },
90
+ },
91
+ );
92
+
93
+ const html = await response.text();
94
+
95
+ // Extract URLs from the results
96
+ const urlRegex = /<a class="result-link" href="([^"]+)"/g;
97
+ const urls: string[] = [];
98
+ let match;
99
+
100
+ while ((match = urlRegex.exec(html)) !== null) {
101
+ if (match[1] && !match[1].includes("duckduckgo.com")) {
102
+ urls.push(match[1]);
103
+ }
104
+ }
105
+
106
+ // If no results from regex, try alternative pattern
107
+ if (urls.length === 0) {
108
+ const altRegex = /rel="nofollow" href="([^"]+)"/g;
109
+ while ((match = altRegex.exec(html)) !== null) {
110
+ if (match[1] && !match[1].includes("duckduckgo.com")) {
111
+ urls.push(match[1]);
112
+ }
113
+ }
114
+ }
115
+
116
+ return urls;
117
+ } catch (error) {
118
+ console.error("Search failed:", error);
119
+ return [];
120
+ }
121
+ }
122
+
123
+ function extractContent(html: string): string {
124
+ // Remove scripts and style elements
125
+ html = html
126
+ .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "")
127
+ .replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, "");
128
+
129
+ // Get text content
130
+ const text = html
131
+ .replace(/<[^>]+>/g, " ")
132
+ .replace(/\s+/g, " ")
133
+ .trim()
134
+ .slice(0, 2000); // Get more content for search results
135
+
136
+ return text;
137
+ }
138
+
139
+ async function getAISummary(content: string, query: string): Promise<string> {
140
+ const completion = await openai.chat.completions.create({
141
+ messages: [
142
+ {
143
+ role: "system",
144
+ content:
145
+ "You are a helpful assistant that provides clear, accurate summaries (2-3 sentences) of search results.",
146
+ },
147
+ {
148
+ role: "user",
149
+ content: `Based on the following search results, please provide a comprehensive answer to the query: "${query}"\n\nSearch results:\n${content}`,
150
+ },
151
+ ],
152
+ model: "gpt-4o-mini",
153
+ });
154
+
155
+ return (
156
+ completion.choices[0].message.content ||
157
+ "Could not generate a summary of the search results."
158
+ );
159
+ }
@@ -0,0 +1,79 @@
1
+ import { Resend } from "resend";
2
+ import { XMTPContext } from "@xmtp/message-kit";
3
+ import type { Skill } from "@xmtp/message-kit";
4
+
5
+ const resend = new Resend(process.env.RESEND_API_KEY); // Replace with your Resend API key
6
+
7
+ export const todo: Skill[] = [
8
+ {
9
+ skill: "todo",
10
+ handler: handler,
11
+ examples: ["/todo"],
12
+ description:
13
+ "Summarize your TODOs and send an email with the summary. Receives no parameters.",
14
+ },
15
+ ];
16
+
17
+ export async function handler(context: XMTPContext) {
18
+ const {
19
+ message: {
20
+ content: { previousMsg },
21
+ },
22
+ } = context;
23
+
24
+ let email = "";
25
+ if (!previousMsg) {
26
+ await context.send("You need to do it on a reply.");
27
+ return;
28
+ }
29
+ let intents = 2;
30
+ while (intents > 0) {
31
+ const emailResponse = await context.awaitResponse(
32
+ "Please provide your email address to receive the to-dos summary:",
33
+ );
34
+ email = emailResponse;
35
+
36
+ // Basic email validation
37
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
38
+ if (!emailRegex.test(email)) {
39
+ await context.send(
40
+ "Invalid email format. Please try again with a valid email address.",
41
+ );
42
+ intents--;
43
+ continue;
44
+ }
45
+ break;
46
+ }
47
+ if (intents == 0) {
48
+ await context.send(
49
+ "I couldn't get your email address. Please try again later.",
50
+ );
51
+ return;
52
+ }
53
+ try {
54
+ let { reply } = await context.textGeneration(
55
+ email,
56
+ "Make this summary concise and to the point to be sent in an email.\n msg: " +
57
+ previousMsg,
58
+ "You are an expert at summarizing to-dos. Return in format html and just the content inside the body tag. Dont return `html` or `body` tags",
59
+ );
60
+ if (typeof reply === "string") {
61
+ let content = {
62
+ from: "bot@mail.coin-toss.xyz",
63
+ to: email,
64
+ subject: "Your summary from Converse",
65
+ html: `
66
+ <h3>Your Converse Summary</h3>
67
+ <p>${reply}</p>
68
+ `,
69
+ };
70
+ await resend.emails.send(content);
71
+ await context.send(`✅ Summary sent successfully to ${email}`);
72
+ } else {
73
+ await context.send("❌ Message not found.");
74
+ }
75
+ } catch (error) {
76
+ await context.send("❌ Failed to send email. Please try again later.");
77
+ console.error("Error sending email:", error);
78
+ }
79
+ }
@@ -0,0 +1,57 @@
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+ import type { Skill } from "@xmtp/message-kit";
3
+
4
+ export const token: Skill[] = [
5
+ {
6
+ skill: "token",
7
+ handler: handler,
8
+ examples: ["/token bitcoin", "/token ethereum"],
9
+ description: "Get real time price of a any token.",
10
+ params: {
11
+ symbol: {
12
+ type: "string",
13
+ },
14
+ },
15
+ },
16
+ ];
17
+ export async function handler(context: XMTPContext) {
18
+ const {
19
+ message: {
20
+ content: {
21
+ params: { symbol },
22
+ },
23
+ },
24
+ } = context;
25
+ const response = await fetch(
26
+ `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=${symbol}`,
27
+ );
28
+ if (!response.ok) {
29
+ context.send("Token not found");
30
+ context.send("try with its full name, instead of btc it would be bitcoin");
31
+ return;
32
+ }
33
+ const data = (await response.json()) as any;
34
+ const token = data[0];
35
+
36
+ const tokenInfo = {
37
+ name: token.name,
38
+ symbol: token.symbol.toUpperCase(),
39
+ price: token.current_price,
40
+ image: token.image,
41
+ link: `https://www.coingecko.com/en/coins/${token.id}`,
42
+ };
43
+
44
+ let frame = {
45
+ title: tokenInfo.name,
46
+ buttons: [
47
+ { content: "Buy", action: "link", target: tokenInfo.link },
48
+ {
49
+ content: `Price (${tokenInfo.price})`,
50
+ action: "link",
51
+ target: tokenInfo.link,
52
+ },
53
+ ],
54
+ image: tokenInfo.image,
55
+ };
56
+ await context.sendCustomFrame(frame);
57
+ }