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
@@ -0,0 +1,37 @@
1
+ import {
2
+ agentReply,
3
+ XMTPContext,
4
+ replaceVariables,
5
+ run,
6
+ } from "@xmtp/message-kit";
7
+ import { downloadPage } from "./plugins/notion.js";
8
+ import fs from "fs";
9
+
10
+ setupFiles();
11
+
12
+ run(async (context: XMTPContext) => {
13
+ const {
14
+ message: { sender },
15
+ agent,
16
+ } = context;
17
+ let systemPrompt = await getPrompt();
18
+ let prompt = await replaceVariables(systemPrompt, sender.address, agent);
19
+ await agentReply(context, prompt);
20
+ });
21
+
22
+ async function getPrompt() {
23
+ if (fs.existsSync(".data/prompt.md"))
24
+ return fs.readFileSync(".data/prompt.md", "utf8");
25
+ else return fs.readFileSync("src/prompt.md", "utf8");
26
+ }
27
+ async function setupFiles() {
28
+ if (!fs.existsSync(".data/db.json")) {
29
+ const dbfile = fs.readFileSync("src/data/db.json", "utf8");
30
+ fs.writeFileSync(".data/db.json", dbfile);
31
+ console.log("DB file created");
32
+ }
33
+
34
+ const page = await downloadPage();
35
+ fs.writeFileSync("src/prompt.md", page);
36
+ console.log("Notion DB updated");
37
+ }
@@ -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,11 @@
1
+ import { Low } from "lowdb";
2
+ import { JSONFile } from "lowdb/node";
3
+
4
+ const adapter = new JSONFile<{
5
+ poaps: Record<string, string>[]; // URL, Address
6
+ }>(".data/db.json");
7
+ export const db = new Low<{
8
+ poaps: Record<string, string>[]; // URL, Address
9
+ }>(adapter, {
10
+ poaps: [],
11
+ });
@@ -0,0 +1,89 @@
1
+ import { Client } from "@notionhq/client";
2
+
3
+ const notion = new Client({
4
+ auth: process.env.NOTION_API_KEY,
5
+ });
6
+ const poapsID = process.env.NOTION_POAP_DB;
7
+ const pageId = process.env.NOTION_PAGE_ID;
8
+
9
+ export async function updateDB() {
10
+ await notion.pages.update({
11
+ page_id: pageId as string,
12
+ properties: {
13
+ RSVP: {
14
+ type: "select",
15
+ select: {
16
+ name: "No",
17
+ },
18
+ },
19
+ },
20
+ });
21
+ }
22
+ export async function downloadPage() {
23
+ const blocks = await notion.blocks.children.list({
24
+ block_id: pageId as string,
25
+ });
26
+ const markdown = blocks.results
27
+ .map((block: any) => {
28
+ switch (block.type) {
29
+ case "paragraph":
30
+ return block.paragraph.rich_text
31
+ .map((text: any) => text.plain_text)
32
+ .join("");
33
+ case "heading_1":
34
+ return `# ${block.heading_1.rich_text
35
+ .map((text: any) => text.plain_text)
36
+ .join("")}`;
37
+ case "heading_2":
38
+ return `## ${block.heading_2.rich_text
39
+ .map((text: any) => text.plain_text)
40
+ .join("")}`;
41
+ case "heading_3":
42
+ return `### ${block.heading_3.rich_text
43
+ .map((text: any) => text.plain_text)
44
+ .join("")}`;
45
+ case "bulleted_list_item":
46
+ return `- ${block.bulleted_list_item.rich_text
47
+ .map((text: any) => text.plain_text)
48
+ .join("")}`;
49
+ case "numbered_list_item":
50
+ return `- ${block.numbered_list_item.rich_text
51
+ .map((text: any) => text.plain_text)
52
+ .join("")}`;
53
+ // Add more cases for other block types as needed
54
+ default:
55
+ return "";
56
+ }
57
+ })
58
+ .join("\n");
59
+ return markdown;
60
+ }
61
+ export async function downloadPoapTable() {
62
+ const response = await notion.databases.query({
63
+ database_id: poapsID as string,
64
+ });
65
+
66
+ const poapTable = response.results.map((page: any) => {
67
+ const url = page.properties.Url.url;
68
+ const address = page.properties.Address.title[0]?.plain_text;
69
+ const id = page.id;
70
+ return { url, address, id };
71
+ });
72
+ return poapTable as { url: string; address: string; id: string }[];
73
+ }
74
+ export async function updatePoapAddress(dbRowId: string, address: string) {
75
+ await notion.pages.update({
76
+ page_id: dbRowId as string,
77
+ properties: {
78
+ Address: {
79
+ type: "title",
80
+ title: [
81
+ {
82
+ type: "text",
83
+ text: { content: address },
84
+ },
85
+ ],
86
+ },
87
+ },
88
+ });
89
+ }
@@ -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,51 @@
1
+ You are a helpful agent that lives inside a messaging app. You manage the general store from XMTP that delivers goodies, POAPs and testnet funds.
2
+
3
+ {rules}
4
+
5
+ {user_context}
6
+
7
+ {skills}
8
+
9
+ ### Goodies
10
+
11
+ - When greeted for the first time, give the full menu.
12
+ - The user can select the option by number or name
13
+ - Once the option is selected confirm the order
14
+
15
+ ### Testnet funds
16
+
17
+ - For each user you can deliver testnet funds using the learnweb3 api.
18
+ - Check the available networks triggering the command before showing them.
19
+ - Users can select the desired testnet network for the transfer of funds by number or name.
20
+
21
+ ### Poap Delivery
22
+
23
+ - For each user you'll deliver only one POAP.
24
+ - Don't forget to use commands to deliver POAPs.
25
+ - Poaps are unique URLs basically
26
+
27
+ ## Response Scenarios:
28
+
29
+ - Welcome message:
30
+ Welcome to The General Store powered by ENS + XMTP, where web3 builders can get supplies, anytime, day or night.
31
+ Below is our menu. Let us know the number of the item you want, and it's yours. If it's a digital good, our bot will deliver those items right to your wallet.
32
+ - Chewing Gum
33
+ - TicTacs
34
+ - Deodorant
35
+ - RedBull
36
+ - Toothbrush
37
+ - Toothpaste
38
+ - XMTP Swag
39
+ - Testnet funds
40
+ - POAP
41
+ Please reply with the item or number of the item you want
42
+ - Delivering a POAP:
43
+ You've selected a POAP. I will deliver it to your address:
44
+ Processing your request now...
45
+ /poap 0x42AB57335941eb00535e95CbF64D78654Cb0F66A
46
+ - Delivering testnet
47
+ You've selected Testnet funds. Let me check the available networks for you.
48
+ Processing your request now...
49
+ /networks
50
+ - Delivering goodies
51
+ Let me get your TicTacs... Your order is confirmed. Enjoy!
@@ -0,0 +1,3 @@
1
+ import systemPromptFromNotion from "./prompt.md";
2
+ //Gets the system prompt from the Notion page
3
+ export const systemPrompt = systemPromptFromNotion;
@@ -0,0 +1,114 @@
1
+ import { XMTPContext, clearMemory } from "@xmtp/message-kit";
2
+ import { getRedisClient } from "../plugins/redis.js";
3
+ import {
4
+ FIVE_MINUTES,
5
+ LearnWeb3Client,
6
+ Network,
7
+ } from "../plugins/learnweb3.js";
8
+
9
+ export async function handleFaucet(context: XMTPContext) {
10
+ const { message } = context;
11
+ const redisClient = await getRedisClient();
12
+ const {
13
+ content: { skill, params },
14
+ sender,
15
+ } = message;
16
+
17
+ // Initialize LearnWeb3Client
18
+ const learnWeb3Client = new LearnWeb3Client();
19
+
20
+ // Fetch supported networks from Redis cache or API
21
+ let supportedNetworks: Network[];
22
+ const cachedSupportedNetworksData = await redisClient.get(
23
+ "supported-networks"
24
+ );
25
+ supportedNetworks = JSON.parse(
26
+ cachedSupportedNetworksData!
27
+ ).supportedNetworks;
28
+
29
+ if (skill === "networks") {
30
+ if (
31
+ !cachedSupportedNetworksData ||
32
+ Date.now() >
33
+ parseInt(JSON.parse(cachedSupportedNetworksData).lastSyncedAt) +
34
+ FIVE_MINUTES
35
+ ) {
36
+ console.log("Cleared cache");
37
+ const updatedSupportedNetworksData = await learnWeb3Client.getNetworks();
38
+ await redisClient.set(
39
+ "supported-networks",
40
+ JSON.stringify({
41
+ lastSyncedAt: Date.now(),
42
+ supportedNetworks: updatedSupportedNetworksData,
43
+ })
44
+ );
45
+ supportedNetworks = updatedSupportedNetworksData;
46
+ } else {
47
+ supportedNetworks = JSON.parse(
48
+ cachedSupportedNetworksData!
49
+ ).supportedNetworks;
50
+ }
51
+
52
+ supportedNetworks = supportedNetworks.filter(
53
+ (n) =>
54
+ !n.networkId.toLowerCase().includes("starknet") &&
55
+ !n.networkId.toLowerCase().includes("fuel") &&
56
+ !n.networkId.toLowerCase().includes("mode")
57
+ );
58
+
59
+ const networkList = supportedNetworks.map((n, index) => {
60
+ return `${index + 1}. ${n.networkId
61
+ .replace(/_/g, " ")
62
+ .replace(/\b\w/g, (l) => l.toUpperCase())}`;
63
+ });
64
+
65
+ return {
66
+ message: `Available networks:\n\n${networkList.join("\n")}`,
67
+ code: 200,
68
+ };
69
+ } else if (skill === "faucet") {
70
+ const { network } = params;
71
+ const selectedNetwork = supportedNetworks.find(
72
+ (n) => n.networkId === network
73
+ );
74
+
75
+ if (!selectedNetwork) {
76
+ await context.send("Invalid network. Please select a valid option.");
77
+ return;
78
+ }
79
+
80
+ await context.send(
81
+ "Your testnet tokens are being processed. Please wait a moment for the transaction to process."
82
+ );
83
+
84
+ const result = await learnWeb3Client.dripTokens(
85
+ selectedNetwork.networkId,
86
+ sender.address
87
+ );
88
+
89
+ if (!result.ok) {
90
+ await context.send(
91
+ `❌ Sorry, there was an error processing your request:\n\n"${result.error!}"`
92
+ );
93
+ // Clear any in-memory cache or state related to the prompt
94
+ clearMemory();
95
+ return;
96
+ }
97
+
98
+ await context.send("Here's your transaction receipt:");
99
+ await context.send(
100
+ `${process.env.FRAME_BASE_URL}?txLink=${result.value}&networkLogo=${
101
+ selectedNetwork?.networkLogo
102
+ }&networkName=${selectedNetwork?.networkName.replaceAll(
103
+ " ",
104
+ "-"
105
+ )}&tokenName=${selectedNetwork?.tokenName}&amount=${
106
+ selectedNetwork?.dripAmount
107
+ }`
108
+ );
109
+ // Clear any in-memory cache or state related to the prompt
110
+ clearMemory();
111
+ } else {
112
+ await context.send("Unknown skill. Please use 'list' or 'drip'.");
113
+ }
114
+ }
@@ -0,0 +1,17 @@
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+ import { downloadPage } from "../plugins/notion.js";
3
+ import fs from "fs";
4
+
5
+ export async function handleNotion(context: XMTPContext) {
6
+ const {
7
+ message: {
8
+ content: { skill },
9
+ },
10
+ } = context;
11
+
12
+ if (skill === "update") {
13
+ const page = await downloadPage();
14
+ fs.writeFileSync("src/prompt.md", page);
15
+ await context.reply("Notion DB updated");
16
+ }
17
+ }
@@ -0,0 +1,48 @@
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+ import { db } from "../plugins/lowdb.js";
3
+ await db.read();
4
+
5
+ export async function handlePoap(context: XMTPContext) {
6
+ const {
7
+ message: {
8
+ content: { skill, params },
9
+ },
10
+ } = context;
11
+
12
+ if (skill == "list") {
13
+ const poapTable = db?.data?.poaps;
14
+ const claimed = poapTable.filter((poap) => poap.Address);
15
+ return {
16
+ code: 200,
17
+ message: `You have claimed ${claimed.length} POAPs out of ${poapTable.length}`,
18
+ };
19
+ } else if (skill == "poap") {
20
+ const { address } = params;
21
+ await db.read();
22
+ const poapTable = db?.data?.poaps;
23
+ const poap = poapTable.find((poap) => poap.Address == address);
24
+
25
+ if (!poap) {
26
+ const emptyPoap = poapTable.find((poap) => !poap.Address);
27
+ if (emptyPoap) {
28
+ db?.data?.poaps?.push({ URL: emptyPoap?.URL, Address: address });
29
+ //?user_address=${address}`
30
+ return {
31
+ code: 200,
32
+ message: `Here is your POAP ${emptyPoap?.URL}`,
33
+ };
34
+ } else {
35
+ return {
36
+ code: 200,
37
+ message: "No more POAPs available",
38
+ };
39
+ }
40
+ } else {
41
+ //?user_address=${address}`
42
+ return {
43
+ code: 200,
44
+ message: `You have already claimed this POAP ${poap?.URL}`,
45
+ };
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,37 @@
1
+ import type { Agent } from "@xmtp/message-kit";
2
+ import { handlePoap } from "./skills/poap.js";
3
+ import { handleFaucet } from "./skills/faucet.js";
4
+ import { handleNotion } from "./skills/notion.js";
5
+
6
+ export const agent: Agent = {
7
+ name: "Poap Bot",
8
+ description: "Get your POAP.",
9
+ tag: "@store",
10
+ skills: [
11
+ {
12
+ skill: "/poap [address]",
13
+ handler: handlePoap,
14
+ examples: ["/poap 0x1234567890123456789012345678901234567890"],
15
+ description: "Get your POAP.",
16
+ params: {
17
+ address: {
18
+ type: "string",
19
+ },
20
+ },
21
+ },
22
+ {
23
+ skill: "/list",
24
+ handler: handlePoap,
25
+ examples: ["/list"],
26
+ description: "List all POAPs.",
27
+ params: {},
28
+ },
29
+ {
30
+ skill: "/update",
31
+ handler: handleNotion,
32
+ examples: ["/update"],
33
+ description: "Update your Notion prompt.",
34
+ params: {},
35
+ },
36
+ ],
37
+ };