create-message-kit 1.1.9 → 1.1.10-beta.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. package/index.js +12 -36
  2. package/package.json +2 -2
  3. package/templates/agent/.yarnrc.yml +4 -0
  4. package/templates/agent/package.json +20 -0
  5. package/templates/agent/src/handlers/check.ts +42 -0
  6. package/templates/agent/src/handlers/cool.ts +52 -0
  7. package/templates/agent/src/handlers/info.ts +65 -0
  8. package/templates/agent/src/handlers/register.ts +40 -0
  9. package/templates/agent/src/handlers/renew.ts +52 -0
  10. package/templates/agent/src/handlers/reset.ts +19 -0
  11. package/templates/agent/src/handlers/tip.ts +29 -0
  12. package/templates/agent/src/index.ts +55 -55
  13. package/templates/agent/src/prompt.ts +52 -0
  14. package/templates/gated/.env.example +3 -0
  15. package/templates/gated/.yarnrc.yml +4 -0
  16. package/templates/gated/package.json +23 -0
  17. package/templates/gated/src/index.ts +64 -0
  18. package/templates/gated/src/lib/gated.ts +51 -0
  19. package/templates/gated/src/lib/nft.ts +37 -0
  20. package/templates/gated/src/skills.ts +24 -0
  21. package/templates/gpt/.yarnrc.yml +4 -0
  22. package/templates/gpt/package.json +20 -0
  23. package/templates/gpt/src/index.ts +8 -29
  24. package/templates/gpt/src/prompt.ts +8 -18
  25. package/templates/group/.yarnrc.yml +4 -0
  26. package/templates/group/package.json +20 -0
  27. package/templates/group/src/{handler → handlers}/game.ts +19 -3
  28. package/templates/group/src/{handler → handlers}/helpers.ts +22 -3
  29. package/templates/group/src/handlers/payment.ts +51 -0
  30. package/templates/group/src/handlers/tipping.ts +60 -0
  31. package/templates/group/src/index.ts +34 -21
  32. package/templates/group/src/prompt.ts +21 -16
  33. package/templates/agent/src/handler.ts +0 -174
  34. package/templates/agent/src/skills.ts +0 -88
  35. package/templates/ens-agent-pro/.env.example +0 -2
  36. package/templates/ens-agent-pro/src/index.ts +0 -90
  37. package/templates/ens-agent-pro/src/prompt.ts +0 -19
  38. package/templates/ens-agent-pro/src/skills.ts +0 -233
  39. package/templates/group/src/handler/payment.ts +0 -29
  40. package/templates/group/src/handler/tipping.ts +0 -40
  41. package/templates/group/src/skills.ts +0 -87
@@ -0,0 +1,64 @@
1
+ import { run, xmtpClient, XMTPContext } from "@xmtp/message-kit";
2
+ import { Client } from "@xmtp/node-sdk";
3
+ import { startServer } from "./lib/gated.js";
4
+ import { verifiedRequest } from "./lib/nft.js";
5
+ const { client } = await xmtpClient({ hideInitLogMessage: true });
6
+ startServer(client, verifiedRequest);
7
+
8
+ run(async (context: XMTPContext) => {
9
+ const {
10
+ message: {
11
+ sender,
12
+ content: { skill },
13
+ },
14
+ client,
15
+ group,
16
+ } = context;
17
+
18
+ if (skill == "id") {
19
+ console.log(group?.id);
20
+ } else if (skill === "create") {
21
+ await context.send("Creating group...");
22
+ const group = await createGroup(
23
+ client,
24
+ sender.address,
25
+ client.accountAddress,
26
+ );
27
+
28
+ await context.send(
29
+ `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.`,
30
+ );
31
+ return;
32
+ } else {
33
+ await context.send(
34
+ "👋 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!",
35
+ );
36
+ }
37
+ });
38
+
39
+ async function createGroup(
40
+ client: Client,
41
+ senderAddress: string,
42
+ clientAddress: string,
43
+ ) {
44
+ let senderInboxId = "";
45
+ const group = await client?.conversations.newConversation([
46
+ senderAddress,
47
+ clientAddress,
48
+ ]);
49
+ const members = await group.members();
50
+ const senderMember = members.find((member) =>
51
+ member.accountAddresses.includes(senderAddress.toLowerCase()),
52
+ );
53
+ if (senderMember) {
54
+ const senderInboxId = senderMember.inboxId;
55
+ console.log("Sender's inboxId:", senderInboxId);
56
+ } else {
57
+ console.log("Sender not found in members list");
58
+ }
59
+ await group.addSuperAdmin(senderInboxId);
60
+ console.log("Sender is superAdmin", await group.isSuperAdmin(senderInboxId));
61
+ await group.send(`Welcome to the new group!`);
62
+ await group.send(`You are now the admin of this group as well as the bot`);
63
+ return group;
64
+ }
@@ -0,0 +1,51 @@
1
+ // Import necessary modules
2
+ import express from "express";
3
+ import { Client } from "@xmtp/node-sdk";
4
+
5
+ export function startServer(
6
+ client: Client,
7
+ verifiedRequest: (walletAddress: string, groupId: string) => Promise<boolean>,
8
+ ) {
9
+ async function addWalletToGroup(
10
+ walletAddress: string,
11
+ groupId: string,
12
+ ): Promise<string> {
13
+ const conversation =
14
+ await client.conversations.getConversationById(groupId);
15
+ const verified = await verifiedRequest(walletAddress, groupId);
16
+ if (!verified) {
17
+ console.log("User cant be added to the group");
18
+ return "not verified";
19
+ } else {
20
+ try {
21
+ await conversation?.addMembers([walletAddress]);
22
+ console.log(`Added wallet address: ${walletAddress} to the group`);
23
+ return "success";
24
+ } catch (error: any) {
25
+ console.log(error.message);
26
+ return "error";
27
+ }
28
+ }
29
+ }
30
+
31
+ // Endpoint to add wallet address to a group from an external source
32
+ const app = express();
33
+ app.use(express.json());
34
+ app.post("/add-wallet", async (req, res) => {
35
+ try {
36
+ const { walletAddress, groupId } = req.body;
37
+ const result = await addWalletToGroup(walletAddress, groupId);
38
+ res.status(200).send(result);
39
+ } catch (error: any) {
40
+ res.status(400).send(error.message);
41
+ }
42
+ });
43
+ // Start the server
44
+ const PORT = process.env.PORT || 3000;
45
+ const url = process.env.URL || `http://localhost:${PORT}`;
46
+ app.listen(PORT, () => {
47
+ console.warn(
48
+ `Use this endpoint to add a wallet to a group indicated by the groupId\n${url}/add-wallet <body: {walletAddress, groupId}>`,
49
+ );
50
+ });
51
+ }
@@ -0,0 +1,37 @@
1
+ import { Alchemy, Network } from "alchemy-sdk";
2
+
3
+ const settings = {
4
+ apiKey: process.env.ALCHEMY_API_KEY, // Replace with your Alchemy API key
5
+ network: Network.BASE_MAINNET, // Use the appropriate network
6
+ };
7
+
8
+ export async function verifiedRequest(
9
+ walletAddress: string,
10
+ groupId: string
11
+ ): Promise<boolean> {
12
+ console.log("new-request", {
13
+ groupId,
14
+ walletAddress,
15
+ });
16
+
17
+ const alchemy = new Alchemy(settings);
18
+ try {
19
+ const nfts = await alchemy.nft.getNftsForOwner(walletAddress);
20
+ const collectionSlug = "XMTPeople"; // The slug for the collection
21
+
22
+ const ownsNft = nfts.ownedNfts.some(
23
+ (nft: any) =>
24
+ nft.contract.name.toLowerCase() === collectionSlug.toLowerCase()
25
+ );
26
+ console.log(
27
+ `NFTs owned on ${Network.BASE_MAINNET}:`,
28
+ nfts.ownedNfts.length
29
+ );
30
+ console.log("is the nft owned: ", ownsNft);
31
+ return ownsNft as boolean;
32
+ } catch (error) {
33
+ console.error("Error fetching NFTs from Alchemy:", error);
34
+ }
35
+
36
+ return false;
37
+ }
@@ -0,0 +1,24 @@
1
+ import type { SkillGroup } from "@xmtp/message-kit";
2
+
3
+ export const skills: SkillGroup[] = [
4
+ {
5
+ name: "Group Id",
6
+ description: "Create and get group id.",
7
+ skills: [
8
+ {
9
+ skill: "/create",
10
+ examples: ["/create"],
11
+ adminOnly: true,
12
+ params: {},
13
+ description: "Create a new group.",
14
+ },
15
+ {
16
+ skill: "/id",
17
+ examples: ["/id"],
18
+ adminOnly: true,
19
+ params: {},
20
+ description: "Get group id.",
21
+ },
22
+ ],
23
+ },
24
+ ];
@@ -0,0 +1,4 @@
1
+ compressionLevel: mixed
2
+ enableGlobalCache: false
3
+ enableTelemetry: false
4
+ nodeLinker: node-modules
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "gpt",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "tsc",
7
+ "dev": "tsc -w & sleep 1 && node --watch dist/index.js",
8
+ "start": "node dist/index.js"
9
+ },
10
+ "dependencies": {
11
+ "@xmtp/message-kit": "workspace:*"
12
+ },
13
+ "devDependencies": {
14
+ "@types/node": "^20.14.2",
15
+ "typescript": "^5.4.5"
16
+ },
17
+ "engines": {
18
+ "node": ">=20"
19
+ }
20
+ }
@@ -1,38 +1,17 @@
1
1
  import {
2
2
  run,
3
- HandlerContext,
4
- textGeneration,
5
- processMultilineResponse,
3
+ agentReply,
4
+ replaceVariables,
5
+ XMTPContext,
6
6
  } from "@xmtp/message-kit";
7
- import { agent_prompt } from "./prompt.js";
8
7
 
9
- if (!process.env.OPEN_AI_API_KEY) {
10
- console.error("OPEN_AI_API_KEY is not set");
11
- }
12
-
13
- run(async (context: HandlerContext) => {
14
- if (!process.env.OPEN_AI_API_KEY) {
15
- context.send("gm");
16
- return;
17
- }
8
+ import { systemPrompt } from "./prompt.js";
18
9
 
10
+ run(async (context: XMTPContext) => {
19
11
  const {
20
- message: {
21
- content: { text, params },
22
- sender,
23
- },
12
+ message: { sender },
24
13
  } = context;
25
14
 
26
- try {
27
- let userPrompt = params?.prompt ?? text;
28
- const { reply } = await textGeneration(
29
- sender.address,
30
- userPrompt,
31
- await agent_prompt(sender.address),
32
- );
33
- await processMultilineResponse(sender.address, reply, context);
34
- } catch (error) {
35
- console.error("Error during OpenAI call:", error);
36
- await context.send("An error occurred while processing your request.");
37
- }
15
+ let prompt = await replaceVariables(systemPrompt, sender.address, [], "@bot");
16
+ await agentReply(context, prompt);
38
17
  });
@@ -1,20 +1,10 @@
1
- import { defaultPromptTemplate } from "@xmtp/message-kit";
1
+ export const systemPrompt = `
2
+ You are a helpful and playful agent called {agent_name} that lives inside a web3 messaging app called Converse.
2
3
 
3
- export async function agent_prompt(senderAddress: string) {
4
- let fineTunedPrompt = `
5
-
6
- ### Context
7
-
8
- You are a helpful agent that lives inside a web3 messaging group that helps interpret user requests and execute commands.
9
- The message was sent by @${senderAddress}
10
-
4
+ {rules}
11
5
 
12
- Important:
13
- - If a user asks jokes, make jokes about web3 devs\n
14
- - If the user asks about performing an action and you can think of a command that would help, answer directly with the command and nothing else.
15
- - Populate the command with the correct or random values. Always return commands with real values only, using usernames with @ and excluding addresses.\n
16
- - If the user asks a question or makes a statement that does not clearly map to a command, respond with helpful information or a clarification question.\n
17
- - If the user is grateful, respond asking for a tip in a playful manner.
18
- `;
19
- return defaultPromptTemplate(fineTunedPrompt, senderAddress, [], "@bot");
20
- }
6
+ {skills}
7
+
8
+ {user_context}
9
+
10
+ `;
@@ -0,0 +1,4 @@
1
+ compressionLevel: mixed
2
+ enableGlobalCache: false
3
+ enableTelemetry: false
4
+ nodeLinker: node-modules
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "group",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "tsc",
7
+ "dev": "tsc -w & sleep 1 && node --watch dist/index.js",
8
+ "start": "node dist/index.js"
9
+ },
10
+ "dependencies": {
11
+ "@xmtp/message-kit": "workspace:*"
12
+ },
13
+ "devDependencies": {
14
+ "@types/node": "^20.14.2",
15
+ "typescript": "^5.4.5"
16
+ },
17
+ "engines": {
18
+ "node": ">=20"
19
+ }
20
+ }
@@ -1,7 +1,23 @@
1
- import { HandlerContext } from "@xmtp/message-kit";
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+ import type { skillAction } from "@xmtp/message-kit";
2
3
 
3
- // Handler function to process game-related
4
- export async function handler(context: HandlerContext) {
4
+ export const registerSkill: skillAction[] = [
5
+ {
6
+ skill: "/game [game]",
7
+ handler: handleGames,
8
+ description: "Play a game.",
9
+ examples: ["/game wordle", "/game slot", "/game help"],
10
+ params: {
11
+ game: {
12
+ default: "",
13
+ type: "string",
14
+ values: ["wordle", "slot", "help"],
15
+ },
16
+ },
17
+ },
18
+ ];
19
+
20
+ export async function handleGames(context: XMTPContext) {
5
21
  const {
6
22
  message: {
7
23
  content: { skill, params, text },
@@ -1,12 +1,31 @@
1
- import { HandlerContext } from "@xmtp/message-kit";
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
2
 
3
- export async function handler(context: HandlerContext) {
3
+ import type { skillAction } from "@xmtp/message-kit";
4
+
5
+ export const registerSkill: skillAction[] = [
6
+ {
7
+ skill: "/help",
8
+ examples: ["/help"],
9
+ handler: handleHelp,
10
+ description: "Get help with the bot.",
11
+ params: {},
12
+ },
13
+ {
14
+ skill: "/id",
15
+ examples: ["/id"],
16
+ handler: handleHelp,
17
+ description: "Get the group ID.",
18
+ params: {},
19
+ },
20
+ ];
21
+
22
+ export async function handleHelp(context: XMTPContext) {
4
23
  const {
5
- skills,
6
24
  message: {
7
25
  content: { skill },
8
26
  },
9
27
  group,
28
+ skills,
10
29
  } = context;
11
30
 
12
31
  if (skill == "help") {
@@ -0,0 +1,51 @@
1
+ import { getUserInfo, XMTPContext } from "@xmtp/message-kit";
2
+ import type { skillAction } from "@xmtp/message-kit";
3
+ export const registerSkill: skillAction[] = [
4
+ {
5
+ skill: "/pay [amount] [token] [username]",
6
+ examples: ["/pay 10 usdc vitalik.eth", "/pay 1 @alix"],
7
+ description:
8
+ "Send a specified amount of a cryptocurrency to a destination address.",
9
+ handler: handlePay,
10
+ params: {
11
+ amount: {
12
+ default: 10,
13
+ type: "number",
14
+ },
15
+ token: {
16
+ default: "usdc",
17
+ type: "string",
18
+ values: ["eth", "dai", "usdc", "degen"], // Accepted tokens
19
+ },
20
+ username: {
21
+ default: "",
22
+ type: "username",
23
+ },
24
+ },
25
+ },
26
+ ];
27
+
28
+ export async function handlePay(context: XMTPContext) {
29
+ const {
30
+ message: {
31
+ content: { params },
32
+ },
33
+ } = context;
34
+ const txpayUrl = "https://txpay.vercel.app";
35
+
36
+ const { amount: amountSend, token: tokenSend, username } = params;
37
+ let senderInfo = await getUserInfo(username);
38
+ if (!amountSend || !tokenSend || !senderInfo) {
39
+ context.reply(
40
+ "Missing required parameters. Please provide amount, token, and username.",
41
+ );
42
+ return {
43
+ code: 400,
44
+ message:
45
+ "Missing required parameters. Please provide amount, token, and username.",
46
+ };
47
+ }
48
+
49
+ let sendUrl = `${txpayUrl}/?&amount=${amountSend}&token=${tokenSend}&receiver=${senderInfo.address}`;
50
+ await context.send(`${sendUrl}`);
51
+ }
@@ -0,0 +1,60 @@
1
+ import { getUserInfo, AbstractedMember, XMTPContext } from "@xmtp/message-kit";
2
+ import type { skillAction } from "@xmtp/message-kit";
3
+
4
+ export const registerSkill: skillAction[] = [
5
+ {
6
+ skill: "/tip [usernames] [amount] [token]",
7
+ examples: ["/tip @vitalik 10 usdc"],
8
+ description: "Tip users in a specified token.",
9
+ handler: handleTipping,
10
+ params: {
11
+ username: {
12
+ default: "",
13
+ plural: true,
14
+ type: "username",
15
+ },
16
+ amount: {
17
+ default: 10,
18
+ type: "number",
19
+ },
20
+ token: {
21
+ default: "usdc",
22
+ type: "string",
23
+ values: ["eth", "dai", "usdc", "degen"],
24
+ },
25
+ },
26
+ },
27
+ ];
28
+
29
+ export async function handleTipping(context: XMTPContext) {
30
+ const {
31
+ message: {
32
+ content: {
33
+ skill,
34
+ params: { amount, username },
35
+ },
36
+ sender,
37
+ },
38
+ } = context;
39
+ let receivers: AbstractedMember[] = [];
40
+
41
+ receivers = await Promise.all(
42
+ username.map((username: string) => getUserInfo(username)),
43
+ );
44
+
45
+ if (!sender || receivers.length === 0 || amount === 0) {
46
+ context.reply("Sender or receiver or amount not found.");
47
+ }
48
+ const receiverAddresses = receivers.map((receiver) => receiver.address);
49
+
50
+ context.sendTo(
51
+ `You received ${amount} tokens from ${sender.address}.`,
52
+ receiverAddresses,
53
+ );
54
+
55
+ // Notify sender of the transaction details
56
+ context.sendTo(
57
+ `You sent ${amount * receiverAddresses.length} tokens in total.`,
58
+ [sender.address],
59
+ );
60
+ }
@@ -1,25 +1,38 @@
1
- import { run, HandlerContext } from "@xmtp/message-kit";
2
- import { textGeneration, processMultilineResponse } from "@xmtp/message-kit";
3
- import { agent_prompt } from "./prompt.js";
1
+ import {
2
+ run,
3
+ agentReply,
4
+ XMTPContext,
5
+ replaceVariables,
6
+ } from "@xmtp/message-kit";
7
+ import { registerSkill as tippingSkill } from "./handlers/tipping.js";
8
+ import { registerSkill as paymentSkill } from "./handlers/payment.js";
9
+ import { registerSkill as gameSkill } from "./handlers/game.js";
10
+ import { registerSkill as helperSkill } from "./handlers/helpers.js";
11
+ import { systemPrompt } from "./prompt.js";
4
12
 
5
- run(async (context: HandlerContext) => {
6
- const {
7
- message: {
8
- content: { text, params },
9
- sender,
10
- },
11
- } = context;
13
+ export const skills = [
14
+ {
15
+ name: "Group bot",
16
+ tag: "@bot",
17
+ description: "Group agent for tipping payments, games and more.",
18
+ skills: [...tippingSkill, ...paymentSkill, ...gameSkill, ...helperSkill],
19
+ },
20
+ ];
12
21
 
13
- try {
14
- let userPrompt = params?.prompt ?? text;
15
- const { reply } = await textGeneration(
22
+ run(
23
+ async (context: XMTPContext) => {
24
+ const {
25
+ message: { sender },
26
+ skills,
27
+ } = context;
28
+
29
+ let prompt = await replaceVariables(
30
+ systemPrompt,
16
31
  sender.address,
17
- userPrompt,
18
- await agent_prompt(sender.address),
32
+ skills,
33
+ "@bot",
19
34
  );
20
- await processMultilineResponse(sender.address, reply, context);
21
- } catch (error) {
22
- console.error("Error during OpenAI call:", error);
23
- await context.send("An error occurred while processing your request.");
24
- }
25
- });
35
+ await agentReply(context, prompt);
36
+ },
37
+ { skills },
38
+ );
@@ -1,28 +1,33 @@
1
- import { skills } from "./skills.js";
2
- import { defaultPromptTemplate } from "@xmtp/message-kit";
1
+ export const systemPrompt = `
2
+ {persona}
3
3
 
4
- export async function agent_prompt(senderAddress: string) {
5
- let fineTunedPrompt = `
6
- ## Example response
4
+ {rules}
7
5
 
8
- 1. If user wants to play a game, use the skill 'game' and specify the game type.
9
- Hey! Sure let's do that.\n/game wordle
6
+ {user_context}
7
+
8
+ {skills}
9
+
10
+ ## Response Scenarios
11
+
12
+ 1. If the user wants to play a game suggest direcly a game like wordle:
13
+ Let's play wordle!
14
+ /game wordle
10
15
 
11
16
  2. When user wants to pay a specific token:
12
- I'll help you pay 1 USDC to 0x123...\n/pay 1 [token] 0x123456789...
17
+ I'll help you pay 1 USDC to 0x123...
18
+ /pay 1 [token] 0x123456789...
13
19
  *This will return a url to pay
14
20
 
15
21
  3. If the user wants to pay a eth domain:
16
- I'll help you pay 1 USDC to vitalik.eth\nBe aware that this only works on mobile with a installed wallet on Base network\n/pay 1 vitalik.eth
22
+ I'll help you pay 1 USDC to vitalik.eth
23
+ Be aware that this only works on mobile with a installed wallet on Base network
24
+ /pay 1 vitalik.eth
17
25
  *This will return a url to pay
18
26
 
19
27
  4. If the user wants to pay a username:
20
- I'll help you pay 1 USDC to @fabri\nBe aware that this only works on mobile with a installed wallet on Base network\n/pay 1 @fabri
28
+ I'll help you pay 1 USDC to @fabri
29
+ Be aware that this only works on mobile with a installed wallet on Base network
30
+ /pay 1 @fabri
21
31
  *This will return a url to pay
22
32
 
23
- 5. If the user wants to play a game suggest direcly a game like wordle:
24
- Let's play wordle!\n/game wordle
25
- `;
26
-
27
- return defaultPromptTemplate(fineTunedPrompt, senderAddress, skills, "@bot");
28
- }
33
+ `;