create-message-kit 1.2.14 → 1.2.15

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 +15 -15
  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
package/index.js CHANGED
@@ -7,7 +7,7 @@ import { default as fs } from "fs-extra";
7
7
  import { isCancel } from "@clack/prompts";
8
8
  import { detect } from "detect-package-manager";
9
9
  import pc from "picocolors";
10
- const defVersion = "1.2.14";
10
+ const defVersion = "1.2.15";
11
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
12
 
13
13
  // Read package.json to get the version
@@ -95,31 +95,31 @@ async function updatePackagejson(destDir, templateType) {
95
95
  }
96
96
 
97
97
  async function gatherProjectInfo() {
98
- const templateOptions = [
99
- {
100
- value: "simple",
101
- label: "Simple Agent: A starter template for building an agent",
102
- },
103
- {
104
- value: "ens",
105
- label: "ENS Agent: An example of an agent using ENS skills",
106
- },
107
- ];
98
+ const templateOptions = fs
99
+ .readJsonSync(resolve(__dirname, "./templates.json"))
100
+ .filter(
101
+ (template) =>
102
+ template.href.includes("ens") || template.href.includes("simple"),
103
+ );
108
104
 
109
105
  const templateType = await select({
110
106
  message: "Select the type of template to initialize:",
111
- options: templateOptions,
107
+ options: templateOptions.map(({ title, description, href }) => ({
108
+ value: href,
109
+ label: `${title} - ${description}`,
110
+ })),
112
111
  });
113
112
 
114
113
  if (isCancel(templateType)) {
115
114
  process.exit(0);
116
115
  }
117
116
 
118
- const templateDir = resolve(__dirname, `./templates/${templateType}`);
117
+ // Fix: Use templates directory with the selected href
118
+ const templateDir = resolve(__dirname, `./${templateType}`);
119
119
 
120
120
  // Ensure the template directory exists
121
121
  if (!fs.existsSync(templateDir)) {
122
- console.error("Template directory does not exist.");
122
+ log.error(`Template directory not found: ${templateDir}`);
123
123
  process.exit(1);
124
124
  }
125
125
 
@@ -141,7 +141,7 @@ async function gatherProjectInfo() {
141
141
  // Copy template files
142
142
  fs.copySync(templateDir, destDir);
143
143
 
144
- return { templateType, displayName, destDir, templateDir };
144
+ return { templateType: templateType, displayName, destDir, templateDir };
145
145
  }
146
146
 
147
147
  function createTsconfig(destDir) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-message-kit",
3
- "version": "1.2.14",
3
+ "version": "1.2.15",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -0,0 +1,290 @@
1
+ # MessageKit Skill Template
2
+
3
+ ## Examples
4
+
5
+ ### Check if a Domain is Available
6
+
7
+
8
+ import { ensUrl } from "../index.js";
9
+ import { XMTPContext } from "@xmtp/message-kit";
10
+ import type { Skill } from "@xmtp/message-kit";
11
+
12
+ // Define Skill
13
+ export const checkDomain: Skill[] = [
14
+ {
15
+ skill: "check",
16
+ handler: handler,
17
+ examples: ["/check vitalik.eth", "/check fabri.base.eth"],
18
+ description: "Check if a domain is available.",
19
+ params: {
20
+ domain: {
21
+ type: "string",
22
+ },
23
+ },
24
+ },
25
+ ];
26
+
27
+ // Handler Implementation
28
+ export async function handler(context: XMTPContext) {
29
+ const {
30
+ message: {
31
+ content: {
32
+ params: { domain },
33
+ },
34
+ },
35
+ } = context;
36
+
37
+ const data = await context.getUserInfo(domain);
38
+
39
+ if (!data?.address) {
40
+ let message = `Looks like ${domain} is available! Here you can register it: ${ensUrl}${domain} or would you like to see some cool alternatives?`;
41
+ return {
42
+ code: 200,
43
+ message,
44
+ };
45
+ } else {
46
+ let message = `Looks like ${domain} is already registered!`;
47
+ await context.executeSkill("/cool " + domain);
48
+ return {
49
+ code: 404,
50
+ message,
51
+ };
52
+ }
53
+ }
54
+
55
+ ### Generate a payment request
56
+
57
+
58
+ import { XMTPContext } from "@xmtp/message-kit";
59
+ import type { Skill } from "@xmtp/message-kit";
60
+
61
+ // Define Skill
62
+ export const paymentRequest: Skill[] = [
63
+ {
64
+ skill: "pay",
65
+ examples: [
66
+ "/pay 10 vitalik.eth",
67
+ "/pay 1 usdc to 0xC60E6Bb79322392761BFe3081E302aEB79B30B03",
68
+ ],
69
+ description:
70
+ "Send a specified amount of a cryptocurrency to a destination address. \nWhen tipping, you can assume it's 1 USDC.",
71
+ handler: handler,
72
+ params: {
73
+ amount: {
74
+ default: 10,
75
+ type: "number",
76
+ },
77
+ token: {
78
+ default: "usdc",
79
+ type: "string",
80
+ values: ["eth", "dai", "usdc", "degen"], // Accepted tokens
81
+ },
82
+ username: {
83
+ default: "",
84
+ type: "username",
85
+ },
86
+ address: {
87
+ default: "",
88
+ type: "address",
89
+ },
90
+ },
91
+ },
92
+ ];
93
+
94
+ // Handler Implementation
95
+ export async function handler(context: XMTPContext) {
96
+ const {
97
+ message: {
98
+ content: {
99
+ params: { amount, token, username, address },
100
+ },
101
+ },
102
+ } = context;
103
+ let receiverAddress = address;
104
+ if (username) {
105
+ receiverAddress = (await context.getUserInfo(username))?.address;
106
+ }
107
+ if (address) {
108
+ // Prioritize address over username
109
+ receiverAddress = address;
110
+ }
111
+
112
+ await context.requestPayment(amount, token, receiverAddress);
113
+ }
114
+
115
+
116
+ ## Types
117
+
118
+ import { XMTPContext } from "../plugins/xmtp.js";
119
+ import { ClientOptions, GroupMember } from "@xmtp/node-sdk";
120
+ import { ContentTypeId } from "@xmtp/content-type-primitives";
121
+
122
+ export type MessageAbstracted = {
123
+ id: string;
124
+ sent: Date;
125
+ content: {
126
+ text?: string | undefined;
127
+ reply?: string | undefined;
128
+ previousMsg?: string | undefined;
129
+ react?: string | undefined;
130
+ content?: any | undefined;
131
+ params?: any | undefined;
132
+ reference?: string | undefined;
133
+ skill?: string | undefined;
134
+ };
135
+ version: "v2" | "v3";
136
+ sender: AbstractedMember;
137
+ typeId: string;
138
+ };
139
+ export type GroupAbstracted = {
140
+ id: string;
141
+ sync: () => Promise<void>;
142
+ addMembers: (addresses: string[]) => Promise<void>;
143
+ addMembersByInboxId: (inboxIds: string[]) => Promise<void>;
144
+ send: (content: string, contentType?: ContentTypeId) => Promise<string>;
145
+ isAdmin: (inboxId: string) => boolean;
146
+ isSuperAdmin: (inboxId: string) => boolean;
147
+ admins: string[];
148
+ superAdmins: string[];
149
+ createdAt: Date;
150
+ members: GroupMember[];
151
+ };
152
+ export type SkillResponse = {
153
+ code: number;
154
+ message: string;
155
+ data?: any;
156
+ };
157
+
158
+ export type SkillHandler = (
159
+ context: XMTPContext,
160
+ ) => Promise<SkillResponse | void>;
161
+
162
+ export type Handler = (context: XMTPContext) => Promise<void>;
163
+
164
+ export type RunConfig = {
165
+ // client options from XMTP client
166
+ client?: ClientOptions;
167
+ // private key to be used for the client, if not, default from env
168
+ privateKey?: string;
169
+ // if true, the init log message with messagekit logo and stuff will be hidden
170
+ experimental?: boolean;
171
+ // hide the init log message with messagekit logo and stuff
172
+ hideInitLogMessage?: boolean;
173
+ // if true, attachments will be enabled
174
+ attachments?: boolean;
175
+ // if true, member changes will be enabled, like adding members to the group
176
+ memberChange?: boolean;
177
+ // skills to be used
178
+ agent?: Agent;
179
+ // model to be used
180
+ gptModel?: string;
181
+ };
182
+ export interface SkillParamConfig {
183
+ default?: string | number | boolean;
184
+ type:
185
+ | "number"
186
+ | "string"
187
+ | "username"
188
+ | "quoted"
189
+ | "address"
190
+ | "prompt"
191
+ | "url";
192
+ plural?: boolean;
193
+ values?: string[]; // Accepted values for the parameter
194
+ }
195
+
196
+ export interface Frame {
197
+ title: string;
198
+ buttons: { content: string; action: string; target: string }[];
199
+ image: string;
200
+ }
201
+ export interface Agent {
202
+ name: string;
203
+ description: string;
204
+ tag: string;
205
+ skills: Skill[];
206
+ }
207
+ export interface Skill {
208
+ skill: string;
209
+ handler?: SkillHandler | undefined;
210
+ adminOnly?: boolean;
211
+ description: string;
212
+ examples: string[];
213
+ params: Record<string, SkillParamConfig>;
214
+ }
215
+
216
+ export interface AbstractedMember {
217
+ inboxId: string;
218
+ address: string;
219
+ accountAddresses: string[];
220
+ installationIds?: string[];
221
+ }
222
+
223
+ export type MetadataValue = string | number | boolean;
224
+ export type Metadata = Record<string, MetadataValue | MetadataValue[]>;
225
+
226
+
227
+ ## Example final prompt
228
+
229
+ Your are a helpful and playful ens agent called @bot that lives inside a messaging app called Converse.
230
+
231
+
232
+ # Rules
233
+ - You can respond with multiple messages if needed. Each message should be separated by a newline character.
234
+ - You can trigger skills by only sending the command in a newline message.
235
+ - Each command starts with a slash (/).
236
+ - Never announce actions without using a command separated by a newline character.
237
+ - Never use markdown in your responses.
238
+ - Do not make guesses or assumptions
239
+ - Only answer if the verified information is in the prompt.
240
+ - Check that you are not missing a command
241
+ - Focus only on helping users with operations detailed below.
242
+ - Date: Fri, 06 Dec 2024 16:03:22 GMT
243
+ - When mentioning any action related to available skills, you MUST trigger the corresponding command in a new line
244
+ - If you suggest an action that has a command, you must trigger that command
245
+
246
+
247
+ ## User context
248
+ - Start by fetch their domain from or Converse username
249
+ - Call the user by their name or domain, in case they have one
250
+ - Ask for a name (if they don't have one) so you can suggest domains.
251
+ - Message sent date: 2024-12-06T16:03:36.582Z
252
+ - Users address is: 0x40f08f0f853d1c42c61815652b7ccd5a50f0be09
253
+ - Users name is: ArizonaOregon
254
+ - Converse username is: ArizonaOregon
255
+
256
+ ## Commands
257
+ /check [domain] - Check if a domain is available.
258
+ /cool [domain] - Get cool alternatives for a .eth domain.
259
+ /info [domain] - Get detailed information about an ENS domain including owner, expiry date, and resolver.
260
+ /register [domain] - Register a new ENS domain. Returns a URL to complete the registration process.
261
+ /renew [domain] - Extend the registration period of your ENS domain. Returns a URL to complete the renewal.
262
+ /reset - Reset the conversation clearing memory and usernames cache.
263
+ /pay [amount] [token] [username] [address] - Send a specified amount of a cryptocurrency to a destination address.
264
+ When tipping, you can asume its 1 usdc.
265
+
266
+ ## Examples
267
+ /check vitalik.eth
268
+ /check fabri.base.eth
269
+ /cool vitalik.eth
270
+ /info nick.eth
271
+ /register vitalik.eth
272
+ /renew fabri.base.eth
273
+ /reset
274
+ /pay 10 vitalik.eth
275
+ /pay 1 usdc to 0xC60E6Bb79322392761BFe3081E302aEB79B30B03
276
+
277
+ ## Scenarios
278
+ 1. Missing commands in responses
279
+ **Issue**: Sometimes responses are sent without the required command.
280
+ **Example**:
281
+ Incorrect:
282
+ > "Looks like vitalik.eth is registered! What about these cool alternatives?"
283
+ Correct:
284
+ > "Looks like vitalik.eth is registered! What about these cool alternatives?
285
+ > /cool vitalik.eth"
286
+
287
+ Incorrect:
288
+ > Here is a summary of your TODOs. I will now send it via email.
289
+ Correct:
290
+ > /todo
@@ -0,0 +1,4 @@
1
+ KEY= # 0x... the private key of the app (with the 0x prefix)
2
+ TEST_ENCRYPTION_KEY= # a different private key for encryption
3
+ OPEN_AI_API_KEY= # sk-proj-...
4
+ REDIS_CONNECTION_STRING= # redis://default:...@...:...
@@ -0,0 +1,9 @@
1
+ compressionLevel: mixed
2
+
3
+ enableGlobalCache: false
4
+
5
+ enableTelemetry: false
6
+
7
+ nodeLinker: node-modules
8
+
9
+ yarnPath: .yarn/releases/yarn-4.5.1.cjs
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "coinbase-agent",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "tsc",
7
+ "dev": "tsc -w & sleep 1 && NODE_NO_WARNINGS=1 node --watch dist/index.js",
8
+ "start": "node dist/index.js"
9
+ },
10
+ "dependencies": {
11
+ "@redis/client": "^1.6.0",
12
+ "@xmtp/message-kit": "workspace:*",
13
+ "axios": "^1.7.7"
14
+ },
15
+ "devDependencies": {
16
+ "@types/node": "^20.14.2",
17
+ "typescript": "^5.4.5"
18
+ },
19
+ "engines": {
20
+ "node": ">=20"
21
+ }
22
+ }
@@ -0,0 +1,31 @@
1
+ import { registerSkill as swapSkill } from "./skills/swap.js";
2
+ import { registerSkill as mintSkill } from "./skills/mint.js";
3
+ import { registerSkill as dripSkill } from "./skills/drip.js";
4
+ import { registerSkill as paySkill } from "./skills/pay.js";
5
+ import { run, XMTPContext, agentReply, Agent } from "@xmtp/message-kit";
6
+ import { replaceVariables } from "@xmtp/message-kit";
7
+ import { systemPrompt } from "./prompt.js";
8
+
9
+ export const frameUrl = "https://ens.steer.fun/";
10
+ export const baseUrl = "https://base-tx-frame.vercel.app/transaction";
11
+ export const ensUrl = "https://app.ens.domains/";
12
+
13
+ export const agent: Agent = {
14
+ name: "Swap Bot",
15
+ tag: "@base",
16
+ description: "Swap bot for base.",
17
+ skills: [...swapSkill, ...mintSkill, ...dripSkill, ...paySkill],
18
+ };
19
+
20
+ run(
21
+ async (context: XMTPContext) => {
22
+ const {
23
+ message: { sender },
24
+ agent,
25
+ } = context;
26
+
27
+ let prompt = await replaceVariables(systemPrompt, sender.address, agent);
28
+ await agentReply(context, prompt);
29
+ },
30
+ { agent }
31
+ );
@@ -0,0 +1,96 @@
1
+ import axios from "axios";
2
+
3
+ export const SUPPORTED_NETWORKS = [
4
+ "arbitrum_goerli",
5
+ "arbitrum_sepolia",
6
+ "base_goerli",
7
+ "base_sepolia",
8
+ "base_sepolia_usdc",
9
+ "celo_alfajores",
10
+ "fantom_testnet",
11
+ "goerli",
12
+ "holesky",
13
+ "linea_goerli",
14
+ "linea_sepolia",
15
+ "manta_testnet",
16
+ "mode_sepolia",
17
+ "morph_sepolia",
18
+ "optimism_goerli",
19
+ "optimism_sepolia",
20
+ "polygon_amoy",
21
+ "polygon_mumbai",
22
+ "polygon_zkevm",
23
+ "scroll_sepolia",
24
+ "sepolia",
25
+ "taiko_jolnir",
26
+ "zksync_sepolia",
27
+ "zora_sepolia",
28
+ ] as const;
29
+
30
+ export const CLAIM_EVERY = 24 * 60 * 60 * 1000; // 24 hours
31
+
32
+ export const ONE_DAY = 24 * 60 * 60 * 1000; // 24 hours
33
+
34
+ export const FIVE_MINUTES = 5 * 60 * 1000; // 5 minutes
35
+
36
+ export const EVM_TOKENS = ["ETH", "MATIC", "USDC", "CELO", "BERA"];
37
+
38
+ export interface Network {
39
+ networkId: string;
40
+ networkName: string;
41
+ networkLogo: string;
42
+ tokenName: string;
43
+ dripAmount: number;
44
+ address: string;
45
+ isERC20: boolean;
46
+ erc20Address?: string;
47
+ erc20Decimals?: number;
48
+ isActive: boolean;
49
+ balance: string;
50
+ }
51
+ export interface DripTokensResponse {
52
+ ok: boolean;
53
+ error?: string;
54
+ value?: string;
55
+ }
56
+
57
+ export class LearnWeb3Client {
58
+ public BASE_URL = "https://learnweb3.io/api/faucet";
59
+ private apiKey = process.env.LEARN_WEB3_API_KEY;
60
+ constructor() {
61
+ if (!process.env.LEARN_WEB3_API_KEY) {
62
+ throw new Error("Please set the LEARN_WEB3_API_KEY environment variable");
63
+ }
64
+ this.apiKey = process.env.LEARN_WEB3_API_KEY;
65
+ }
66
+
67
+ async getNetworks(onlyEvm = true): Promise<Network[]> {
68
+ const response = await axios(`${this.BASE_URL}/networks`);
69
+ const data: Network[] = response.data;
70
+ if (onlyEvm) {
71
+ return data.filter(
72
+ (network) =>
73
+ EVM_TOKENS.some((t) =>
74
+ network.tokenName.toLowerCase().includes(t.toLowerCase()),
75
+ ) && network.isActive,
76
+ );
77
+ }
78
+ return data.filter((network) => network.isActive);
79
+ }
80
+
81
+ async dripTokens(
82
+ networkId: string,
83
+ recipientAddress: string,
84
+ ): Promise<DripTokensResponse> {
85
+ const response = await axios.post(
86
+ `${this.BASE_URL}/drip`,
87
+ { networkId, recipientAddress },
88
+ {
89
+ headers: {
90
+ authorization: `Bearer ${this.apiKey}`,
91
+ },
92
+ },
93
+ );
94
+ return response.data;
95
+ }
96
+ }
@@ -0,0 +1,15 @@
1
+ import { createClient } from "@redis/client";
2
+ import { RedisClientType } from "@redis/client";
3
+
4
+ export const getRedisClient = async () => {
5
+ const client = createClient({
6
+ url: process.env.REDIS_CONNECTION_STRING,
7
+ });
8
+
9
+ client.on("error", (err) => {
10
+ console.error("Redis client error:", err);
11
+ });
12
+
13
+ await client.connect();
14
+ return client as RedisClientType;
15
+ };
@@ -0,0 +1,64 @@
1
+ export const systemPrompt = `
2
+ Your are helpful and playful agent called {agent_name} that lives inside a web3 messaging app called Converse.
3
+
4
+ {rules}
5
+
6
+ {user_context}
7
+
8
+ {skills}
9
+
10
+ ## Response Scenarios:
11
+
12
+ 1. When user wants to swap tokens:
13
+ Hey {PREFERRED_NAME! I can help you swap tokens on Base.
14
+ Let me help you swap 10 USDC to ETH
15
+ /swap 10 usdc eth
16
+
17
+ 2. When user wants to swap a specific amount:
18
+ Sure! I'll help you swap 5 DEGEN to DAI
19
+ /swap 5 degen dai
20
+
21
+ 3. When user wants to pay a specific token:
22
+ I'll help you pay 1 USDC to 0x123...
23
+ /pay 1 {TOKEN} 0x123456789...
24
+ *This will return a url to pay
25
+
26
+ 4. If the user wants to pay a eth domain:
27
+ I'll help you pay 1 USDC to vitalik.eth
28
+ Be aware that this only works on mobile with a installed wallet on Base network
29
+ /pay 1 vitalik.eth
30
+ *This will return a url to pay
31
+
32
+ 5. If the user wants to pay a username:
33
+ I'll help you pay 1 USDC to @fabri
34
+ Be aware that this only works on mobile with a installed wallet on Base network
35
+ /pay 1 @fabri
36
+ *This will return a url to pay
37
+
38
+ 6. When user asks about supported tokens:
39
+ I can help you swap or pay these tokens on Base (ETH, USDC, DAI, DEGEN):
40
+ Just let me know the amount and which tokens you'd like to swap or send!
41
+
42
+ 7. When user wants to tip default to 1 usdc:
43
+ Let's go ahead and tip 1 USDC to nick.eth
44
+ /pay 1 usdc 0x123456789...
45
+
46
+ 8. If the users greets or wants to know more or what else can he do:
47
+ I can assist you with swapping, minting, tipping, dripping testnet tokens and sending tokens (all on Base). Just let me know what you need help with!.
48
+
49
+ 9. If the user wants to mint they can specify the collection and token id or a Url from Coinbase Wallet URL or Zora URL:
50
+ I'll help you mint the token with id 1 from collection 0x123456789...
51
+ /mint 0x123456789... 1
52
+ I'll help you mint the token from this url
53
+ /url_mint https://wallet.coinbase.com/nft/mint/eip155:1:erc721:0x123456789...
54
+ I'll help you mint the token from this url
55
+ /url_mint https://zora.co/collect/base/0x123456789/1...
56
+
57
+ 10. If the user wants testnet tokens and doesn't specify the network:
58
+ Just let me know which network you'd like to drip to Base Sepolia or Base Goerli?
59
+
60
+ 11. If the user wants testnet tokens and specifies the network:
61
+ I'll help you get testnet tokens for Base Sepolia\n/drip base_sepolia 0x123456789...
62
+
63
+ {issues}
64
+ `;
@@ -0,0 +1,83 @@
1
+ import { XMTPContext, clearMemory } from "@xmtp/message-kit";
2
+ import type { Skill } from "@xmtp/message-kit";
3
+ import { getRedisClient } from "../plugins/redis.js";
4
+ import { LearnWeb3Client, Network } from "../plugins/learnweb3.js";
5
+
6
+ export const registerSkill: Skill[] = [
7
+ {
8
+ skill: "/drip [network] [address]",
9
+ handler: handler,
10
+ examples: [
11
+ "/drip base_sepolia 0x123456789",
12
+ "/drip base_goerli 0x123456789",
13
+ ],
14
+ description:
15
+ "Drip a default amount of testnet tokens to a specified address.",
16
+ params: {
17
+ network: {
18
+ default: "base_sepolia",
19
+ type: "string",
20
+ values: ["base_sepolia", "base_goerli"],
21
+ },
22
+ address: {
23
+ default: "",
24
+ type: "address",
25
+ },
26
+ },
27
+ },
28
+ ];
29
+
30
+ export async function handler(context: XMTPContext) {
31
+ const {
32
+ message: {
33
+ content: { params },
34
+ sender,
35
+ },
36
+ } = context;
37
+
38
+ const { network } = params;
39
+ if (!network) {
40
+ await context.send("Invalid network. Please select a valid option.");
41
+ return;
42
+ }
43
+ context.send("Fetching testnet tokens...");
44
+ const redisClient = await getRedisClient();
45
+
46
+ const learnWeb3Client = new LearnWeb3Client();
47
+ // Fetch supported networks from Redis cache or API
48
+ let supportedNetworks: Network[];
49
+ const cachedSupportedNetworksData =
50
+ await redisClient.get("supported-networks");
51
+ supportedNetworks = JSON.parse(
52
+ cachedSupportedNetworksData!,
53
+ ).supportedNetworks;
54
+ await context.send(
55
+ "Your testnet tokens are being processed. Please wait a moment for the transaction to process.",
56
+ );
57
+ const selectedNetwork = supportedNetworks.find(
58
+ (n) => n.networkId.toLowerCase() === network.toLowerCase(),
59
+ );
60
+ if (!selectedNetwork) {
61
+ await context.send(
62
+ "The network currently does not have funds provided by web3 api's\nTry again later...",
63
+ );
64
+ return;
65
+ }
66
+ const result = await learnWeb3Client.dripTokens(
67
+ selectedNetwork!.networkId,
68
+ sender.address,
69
+ );
70
+
71
+ if (!result.ok) {
72
+ await context.send(
73
+ `❌ Sorry, there was an error processing your request:\n\n"${result.error!}"`,
74
+ );
75
+ return;
76
+ }
77
+
78
+ await context.send("Here's your transaction receipt:");
79
+ await context.sendReceipt(result.value!);
80
+ // Clear any in-memory cache or state related to the prompt
81
+ clearMemory();
82
+ return;
83
+ }