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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ `;