create-message-kit 1.2.4 → 1.2.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. package/index.js +40 -27
  2. package/package.json +2 -3
  3. package/templates/agent/.cursorrules +227 -0
  4. package/templates/agent/src/index.ts +16 -25
  5. package/templates/agent/src/prompt.ts +1 -1
  6. package/templates/agent/src/{handlers → skills}/check.ts +5 -4
  7. package/templates/agent/src/{handlers → skills}/cool.ts +1 -1
  8. package/templates/agent/src/{handlers → skills}/info.ts +6 -5
  9. package/templates/agent/src/{handlers → skills}/pay.ts +20 -5
  10. package/templates/agent/src/{handlers → skills}/register.ts +2 -3
  11. package/templates/agent/src/{handlers → skills}/renew.ts +5 -4
  12. package/templates/agent/src/skills/reset.ts +26 -0
  13. package/templates/experimental/.cursorrules +227 -0
  14. package/templates/experimental/.env.example +2 -0
  15. package/templates/{gated → experimental}/package.json +6 -4
  16. package/templates/experimental/src/index.ts +53 -0
  17. package/templates/{gated/src/lib/nft.ts → experimental/src/lib/alchemy.ts} +7 -13
  18. package/templates/experimental/src/lib/minilog.ts +26 -0
  19. package/templates/experimental/src/lib/xmtp.ts +136 -0
  20. package/templates/experimental/src/prompt.ts +24 -0
  21. package/templates/experimental/src/skills/broadcast.ts +39 -0
  22. package/templates/experimental/src/skills/gated.ts +89 -0
  23. package/templates/{agent/src/handlers → experimental/src/skills}/todo.ts +11 -5
  24. package/templates/{agent/src/handlers → experimental/src/skills}/token.ts +1 -1
  25. package/templates/experimental/src/skills/wordle.ts +97 -0
  26. package/templates/gpt/.cursorrules +227 -0
  27. package/templates/gpt/src/index.ts +1 -0
  28. package/templates/agent/src/handlers/game.ts +0 -58
  29. package/templates/agent/src/handlers/reset.ts +0 -19
  30. package/templates/gated/.env.example +0 -3
  31. package/templates/gated/src/index.ts +0 -64
  32. package/templates/gated/src/lib/gated.ts +0 -51
  33. package/templates/gated/src/skills.ts +0 -23
  34. /package/templates/{gated → experimental}/.yarnrc.yml +0 -0
@@ -1,7 +1,7 @@
1
1
  import { XMTPContext } from "@xmtp/message-kit";
2
2
  import type { Skill } from "@xmtp/message-kit";
3
3
 
4
- export const registerSkill: Skill[] = [
4
+ export const token: Skill[] = [
5
5
  {
6
6
  skill: "/token [symbol]",
7
7
  handler: handler,
@@ -0,0 +1,97 @@
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+ import type { Skill } from "@xmtp/message-kit";
3
+
4
+ export const wordle: Skill[] = [
5
+ {
6
+ skill: "/wordle",
7
+ handler: handler,
8
+ examples: ["/wordle"],
9
+ description: "Play wordle.",
10
+ params: {},
11
+ },
12
+ {
13
+ skill: "/arena [word count] [audience size]",
14
+ examples: ["/arena 3 15"],
15
+ handler: handler,
16
+ description: "Play arena.",
17
+ params: {
18
+ wordCount: {
19
+ default: 3,
20
+ type: "number",
21
+ },
22
+ audienceSize: {
23
+ default: 15,
24
+ type: "number",
25
+ },
26
+ },
27
+ },
28
+ ];
29
+
30
+ async function handler(context: XMTPContext) {
31
+ const {
32
+ message: {
33
+ content: { skill },
34
+ },
35
+ } = context;
36
+
37
+ if (skill === "arena") {
38
+ await handleArenaMessage(context);
39
+ } else if (skill === "wordle") {
40
+ await context.send("https://framedl.xyz");
41
+ } else if (skill === "help") {
42
+ await context.send(
43
+ "For using this bot you can use the following commands:\n\n" +
44
+ "/wordle, @wordle, 🔍, 🔎 - To start the game\n" +
45
+ "/arena <word count> <audience size> - To start the arena game\n" +
46
+ "/help - To see commands",
47
+ );
48
+ }
49
+ }
50
+ async function handleArenaMessage(context: XMTPContext) {
51
+ const {
52
+ message: {
53
+ content: { text },
54
+ },
55
+ members,
56
+ } = context;
57
+
58
+ const apiKey = process.env.FRAMEDL_API_KEY;
59
+ if (!apiKey) {
60
+ console.log("FRAMEDL_API_KEY is not set");
61
+ await context.send("https://www.framedl.xyz/games/arena/create");
62
+ return;
63
+ }
64
+ const participantCount = members && members.length ? members.length - 1 : 0;
65
+ const args = text?.split(" ") ?? [];
66
+ const wordCountArg = args[1] ? parseInt(args[1], 10) : 3;
67
+ const audienceSizeArg = args[2] ? parseInt(args[2], 10) : participantCount;
68
+ if (isNaN(wordCountArg) || isNaN(audienceSizeArg)) {
69
+ await context.send(
70
+ "usage: /arena <word count> <audience size>\n\n" +
71
+ "word count: number of words in the arena (default: 3, min: 1, max: 9)\n" +
72
+ "audience size: number of audience members (default: number of participants excluding wordle bot, min: 1, max: 15)",
73
+ );
74
+ return;
75
+ }
76
+
77
+ const audienceSize = Math.max(1, Math.min(15, audienceSizeArg));
78
+ const wordCount = Math.max(1, Math.min(9, wordCountArg));
79
+
80
+ try {
81
+ const response = await fetch("https://www.framedl.xyz/api/arenas", {
82
+ method: "POST",
83
+ body: JSON.stringify({ wordCount, audienceSize }),
84
+ headers: {
85
+ "Content-Type": "application/json",
86
+ "x-framedl-api-key": apiKey,
87
+ },
88
+ });
89
+
90
+ const data = (await response.json()) as { arenaUrl: string };
91
+
92
+ await context.send(data.arenaUrl);
93
+ } catch (error) {
94
+ console.error(error);
95
+ await context.send("https://www.framedl.xyz/games/arena/create");
96
+ }
97
+ }
@@ -0,0 +1,227 @@
1
+ # MessageKit Skill Template
2
+
3
+ ## Examples
4
+
5
+ ### Check if a Domain is Available
6
+
7
+ ```typescript
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 [domain]",
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
+ ```typescript
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 [amount] [token] [username] [address]",
65
+ examples: [
66
+ "/pay 10 vitalik.eth",
67
+ "/pay 1 usdc to 0xc9925662D36DE3e1bF0fD64e779B2e5F0Aead964",
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
+
117
+ ## Types
118
+
119
+ ```typescript
120
+ import { XMTPContext } from "../lib/xmtp.js";
121
+ import { ClientOptions, GroupMember } from "@xmtp/node-sdk";
122
+ import { ContentTypeId } from "@xmtp/content-type-primitives";
123
+
124
+ export type MessageAbstracted = {
125
+ id: string;
126
+ sent: Date;
127
+ content: {
128
+ text?: string | undefined;
129
+ reply?: string | undefined;
130
+ previousMsg?: string | undefined;
131
+ react?: string | undefined;
132
+ content?: any | undefined;
133
+ params?: any | undefined;
134
+ reference?: string | undefined;
135
+ skill?: string | undefined;
136
+ };
137
+ version: "v2" | "v3";
138
+ sender: AbstractedMember;
139
+ typeId: string;
140
+ };
141
+ export type GroupAbstracted = {
142
+ id: string;
143
+ sync: () => Promise<void>;
144
+ addMembers: (addresses: string[]) => Promise<void>;
145
+ addMembersByInboxId: (inboxIds: string[]) => Promise<void>;
146
+ send: (content: string, contentType?: ContentTypeId) => Promise<string>;
147
+ isAdmin: (inboxId: string) => boolean;
148
+ isSuperAdmin: (inboxId: string) => boolean;
149
+ admins: string[];
150
+ superAdmins: string[];
151
+ createdAt: Date;
152
+ members: GroupMember[];
153
+ };
154
+ export type SkillResponse = {
155
+ code: number;
156
+ message: string;
157
+ data?: any;
158
+ };
159
+
160
+ export type SkillHandler = (
161
+ context: XMTPContext,
162
+ ) => Promise<SkillResponse | void>;
163
+
164
+ export type Handler = (context: XMTPContext) => Promise<void>;
165
+
166
+ export type RunConfig = {
167
+ // client options from XMTP client
168
+ client?: ClientOptions;
169
+ // private key to be used for the client, if not, default from env
170
+ privateKey?: string;
171
+ // if true, the init log message with messagekit logo and stuff will be hidden
172
+ experimental?: boolean;
173
+ // hide the init log message with messagekit logo and stuff
174
+ hideInitLogMessage?: boolean;
175
+ // if true, attachments will be enabled
176
+ attachments?: boolean;
177
+ // if true, member changes will be enabled, like adding members to the group
178
+ memberChange?: boolean;
179
+ // skills to be used
180
+ agent?: Agent;
181
+ // model to be used
182
+ gptModel?: string;
183
+ };
184
+ export interface SkillParamConfig {
185
+ default?: string | number | boolean;
186
+ type:
187
+ | "number"
188
+ | "string"
189
+ | "username"
190
+ | "quoted"
191
+ | "address"
192
+ | "prompt"
193
+ | "url";
194
+ plural?: boolean;
195
+ values?: string[]; // Accepted values for the parameter
196
+ }
197
+
198
+ export interface Frame {
199
+ title: string;
200
+ buttons: { content: string; action: string; target: string }[];
201
+ image: string;
202
+ }
203
+ export interface Agent {
204
+ name: string;
205
+ description: string;
206
+ tag: string;
207
+ skills: Skill[];
208
+ }
209
+ export interface Skill {
210
+ skill: string;
211
+ handler?: SkillHandler | undefined;
212
+ adminOnly?: boolean;
213
+ description: string;
214
+ examples: string[];
215
+ params: Record<string, SkillParamConfig>;
216
+ }
217
+
218
+ export interface AbstractedMember {
219
+ inboxId: string;
220
+ address: string;
221
+ accountAddresses: string[];
222
+ installationIds?: string[];
223
+ }
224
+
225
+ export type MetadataValue = string | number | boolean;
226
+ export type Metadata = Record<string, MetadataValue | MetadataValue[]>;
227
+ ```
@@ -7,6 +7,7 @@ import {
7
7
  } from "@xmtp/message-kit";
8
8
 
9
9
  import { systemPrompt } from "./prompt.js";
10
+
10
11
  export const agent: Agent = {
11
12
  name: "GPT Bot",
12
13
  tag: "@bot",
@@ -1,58 +0,0 @@
1
- import { XMTPContext } from "@xmtp/message-kit";
2
- import type { Skill } from "@xmtp/message-kit";
3
-
4
- export const registerSkill: Skill[] = [
5
- {
6
- skill: "/game [game]",
7
- handler: handler,
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 handler(context: XMTPContext) {
21
- const {
22
- message: {
23
- content: { skill, params, text },
24
- },
25
- } = context;
26
- if (!skill) {
27
- if (text === "🔎" || text === "🔍") {
28
- // Send the URL for the requested game
29
- context.reply("https://framedl.xyz/");
30
- }
31
- return;
32
- }
33
- // URLs for each game type
34
- const gameUrls: { [key: string]: string } = {
35
- wordle: "https://framedl.xyz",
36
- slot: "https://slot-machine-frame.vercel.app",
37
- };
38
- let returnText = "";
39
- switch (params.game) {
40
- case "wordle":
41
- case "slot":
42
- // Retrieve the URL for the requested game using a simplified variable assignment
43
- const gameUrl = gameUrls[params.game];
44
- // Send the URL for the requested game
45
- returnText = gameUrl;
46
- break;
47
-
48
- case "help":
49
- returnText = "Available games: \n/game wordle\n/game slot";
50
- break;
51
- default:
52
- // Inform the user about unrecognized skills and provide available options
53
- returnText =
54
- "Skill not recognized. Available games: wordle, slot, or help.";
55
- break;
56
- }
57
- return { code: 200, message: returnText };
58
- }
@@ -1,19 +0,0 @@
1
- import { clearInfoCache, clearMemory } from "@xmtp/message-kit";
2
- import { XMTPContext } from "@xmtp/message-kit";
3
-
4
- import type { Skill } from "@xmtp/message-kit";
5
-
6
- export const registerSkill: Skill[] = [
7
- {
8
- skill: "/reset",
9
- examples: ["/reset"],
10
- handler: handler,
11
- description: "Reset the conversation.",
12
- params: {},
13
- },
14
- ];
15
- export async function handler(context: XMTPContext) {
16
- clearMemory();
17
- clearInfoCache();
18
- return { code: 200, message: "Conversation reset." };
19
- }
@@ -1,3 +0,0 @@
1
- KEY= # the private key of the bot wallet
2
- PORT= # the port of the server
3
- ALCHEMY_API_KEY= # the alchemy api key
@@ -1,64 +0,0 @@
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.newGroup([
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
- }
@@ -1,51 +0,0 @@
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
- }
@@ -1,23 +0,0 @@
1
- import type { Agent } from "@xmtp/message-kit";
2
-
3
- export const agent: Agent = {
4
- name: "Group Id",
5
- tag: "@bot",
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
- };
File without changes