create-message-kit 1.1.7-beta.2 → 1.1.7-beta.20

Sign up to get free protection for your applications and to get access to all the features.
package/index.js CHANGED
@@ -32,8 +32,8 @@ Powered by XMTP`;
32
32
 
33
33
  const { templateType, displayName, destDir } = await gatherProjectInfo();
34
34
 
35
- // Replace dot files
36
- //replaceDotfiles(destDir);
35
+ // Add package.json
36
+ addPackagejson(destDir, displayName);
37
37
 
38
38
  // Create .gitignore
39
39
  createGitignore(destDir);
@@ -44,9 +44,6 @@ Powered by XMTP`;
44
44
  // Create tsconfig.json file
45
45
  createTsconfig(destDir);
46
46
 
47
- // Replace package.json properties
48
- updatePackageJson(destDir, displayName);
49
-
50
47
  // Wrap up
51
48
  log.success(`Project launched in ${pc.red(destDir)}!`);
52
49
 
@@ -63,6 +60,31 @@ Powered by XMTP`;
63
60
 
64
61
  program.parse(process.argv);
65
62
 
63
+ async function addPackagejson(destDir, name) {
64
+ // Create package.json based on the template
65
+ const packageTemplate = {
66
+ name: name,
67
+ private: true,
68
+ type: "module",
69
+ scripts: {
70
+ build: "tsc",
71
+ dev: "tsc -w & sleep 1 && node --watch dist/index.js",
72
+ start: "node dist/index.js",
73
+ postinstall: "tsc",
74
+ },
75
+ dependencies: {
76
+ "@xmtp/message-kit": "workspace:*",
77
+ },
78
+ engines: {
79
+ node: ">=20",
80
+ },
81
+ };
82
+
83
+ fs.writeJsonSync(resolve(destDir, "package.json"), packageTemplate, {
84
+ spaces: 2,
85
+ });
86
+ }
87
+
66
88
  async function gatherProjectInfo() {
67
89
  const templateOptions = [
68
90
  { value: "gm", label: "GM" },
@@ -151,22 +173,6 @@ function createTsconfig(destDir) {
151
173
  spaces: 2,
152
174
  });
153
175
  }
154
- function replaceDotfiles(destDir) {
155
- for (const file of fs.readdirSync(destDir)) {
156
- if (!file.startsWith("_")) continue;
157
- fs.renameSync(
158
- resolve(destDir, file),
159
- resolve(destDir, `.${file.slice(1)}`),
160
- );
161
- }
162
- }
163
-
164
- function updatePackageJson(destDir, name) {
165
- const pkgJson = fs.readJsonSync(resolve(destDir, "package.json"));
166
- pkgJson.name = name;
167
- updateDependenciesToLatest(pkgJson);
168
- fs.writeJsonSync(resolve(destDir, "package.json"), pkgJson, { spaces: 2 });
169
- }
170
176
 
171
177
  function logNextSteps(name) {
172
178
  log.message("Next steps:");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-message-kit",
3
- "version": "1.1.7-beta.2",
3
+ "version": "1.1.7-beta.20",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -1,7 +1,7 @@
1
1
  import { HandlerContext, SkillResponse } from "@xmtp/message-kit";
2
- import { getUserInfo, clearInfoCache, isOnXMTP } from "../lib/resolver.js";
2
+ import { getUserInfo, clearInfoCache, isOnXMTP } from "@xmtp/message-kit";
3
3
  import { isAddress } from "viem";
4
- import { clearMemory } from "../lib/gpt.js";
4
+ import { clearMemory } from "@xmtp/message-kit";
5
5
 
6
6
  export const frameUrl = "https://ens.steer.fun/";
7
7
  export const ensUrl = "https://app.ens.domains/";
@@ -9,18 +9,19 @@ export const baseTxUrl = "https://base-tx-frame.vercel.app";
9
9
 
10
10
  export async function handleEns(
11
11
  context: HandlerContext,
12
- ): Promise<SkillResponse> {
12
+ ): Promise<SkillResponse | undefined> {
13
13
  const {
14
14
  message: {
15
- content: { command, params, sender },
15
+ sender,
16
+ content: { skill, params },
16
17
  },
17
- skill,
18
18
  } = context;
19
- if (command == "reset") {
19
+
20
+ if (skill == "reset") {
20
21
  clearMemory();
21
22
  return { code: 200, message: "Conversation reset." };
22
- } else if (command == "renew") {
23
- // Destructure and validate parameters for the ens command
23
+ } else if (skill == "renew") {
24
+ // Destructure and validate parameters for the ens
24
25
  const { domain } = params;
25
26
  // Check if the user holds the domain
26
27
  if (!domain) {
@@ -43,8 +44,8 @@ export async function handleEns(
43
44
  // Generate URL for the ens
44
45
  let url_ens = frameUrl + "frames/manage?name=" + domain;
45
46
  return { code: 200, message: `${url_ens}` };
46
- } else if (command == "register") {
47
- // Destructure and validate parameters for the ens command
47
+ } else if (skill == "register") {
48
+ // Destructure and validate parameters for the ens
48
49
  const { domain } = params;
49
50
 
50
51
  if (!domain) {
@@ -56,7 +57,7 @@ export async function handleEns(
56
57
  // Generate URL for the ens
57
58
  let url_ens = ensUrl + domain;
58
59
  return { code: 200, message: `${url_ens}` };
59
- } else if (command == "info") {
60
+ } else if (skill == "info") {
60
61
  const { domain } = params;
61
62
 
62
63
  const data = await getUserInfo(domain);
@@ -87,19 +88,13 @@ export async function handleEns(
87
88
  }
88
89
  message += `\n\nWould you like to tip the domain owner for getting there first 🤣?`;
89
90
  message = message.trim();
90
- if (
91
- await isOnXMTP(
92
- context.v2client,
93
- data?.ensInfo?.ens,
94
- data?.ensInfo?.address,
95
- )
96
- ) {
91
+ if (await isOnXMTP(context.client, context.v2client, sender?.address)) {
97
92
  await context.send(
98
93
  `Ah, this domains is in XMTP, you can message it directly: https://converse.xyz/dm/${domain}`,
99
94
  );
100
95
  }
101
96
  return { code: 200, message };
102
- } else if (command == "check") {
97
+ } else if (skill == "check") {
103
98
  const { domain } = params;
104
99
 
105
100
  if (!domain) {
@@ -118,13 +113,13 @@ export async function handleEns(
118
113
  };
119
114
  } else {
120
115
  let message = `Looks like ${domain} is already registered!`;
121
- await skill("/cool " + domain);
116
+ await context.executeSkill("/cool " + domain);
122
117
  return {
123
118
  code: 404,
124
119
  message,
125
120
  };
126
121
  }
127
- } else if (command == "tip") {
122
+ } else if (skill == "tip") {
128
123
  const { address } = params;
129
124
  if (!address) {
130
125
  return {
@@ -141,7 +136,7 @@ export async function handleEns(
141
136
  code: 200,
142
137
  message: txUrl,
143
138
  };
144
- } else if (command == "cool") {
139
+ } else if (skill == "cool") {
145
140
  const { domain } = params;
146
141
  //What about these cool alternatives?\
147
142
  return {
@@ -149,7 +144,7 @@ export async function handleEns(
149
144
  message: `${generateCoolAlternatives(domain)}`,
150
145
  };
151
146
  } else {
152
- return { code: 400, message: "Command not found." };
147
+ return { code: 400, message: "Skill not found." };
153
148
  }
154
149
  }
155
150
 
@@ -1,26 +1,18 @@
1
1
  import { run, HandlerContext } from "@xmtp/message-kit";
2
- import { textGeneration, processMultilineResponse } from "./lib/gpt.js";
2
+ import { textGeneration, processMultilineResponse } from "@xmtp/message-kit";
3
3
  import { agent_prompt } from "./prompt.js";
4
- import { getUserInfo } from "./lib/resolver.js";
4
+ import { getUserInfo } from "@xmtp/message-kit";
5
5
 
6
6
  run(async (context: HandlerContext) => {
7
- /*All the skills are handled through the skills file*/
8
- /* If its just text, it will be handled by the ensAgent*/
9
- /* If its a group message, it will be handled by the groupAgent*/
10
- if (!process?.env?.OPEN_AI_API_KEY) {
11
- console.warn("No OPEN_AI_API_KEY found in .env");
12
- return;
13
- }
14
-
15
7
  const {
16
8
  message: {
17
- content: { content, params },
9
+ content: { text, params },
18
10
  sender,
19
11
  },
20
12
  } = context;
21
13
 
22
14
  try {
23
- let userPrompt = params?.prompt ?? content;
15
+ let userPrompt = params?.prompt ?? text;
24
16
  const userInfo = await getUserInfo(sender.address);
25
17
  if (!userInfo) {
26
18
  console.log("User info not found");
@@ -1,52 +1,51 @@
1
1
  import { skills } from "./skills.js";
2
- import { UserInfo, PROMPT_USER_CONTENT } from "./lib/resolver.js";
3
- import { PROMPT_RULES, PROMPT_SKILLS_AND_EXAMPLES } from "./lib/gpt.js";
2
+ import {
3
+ UserInfo,
4
+ PROMPT_USER_CONTENT,
5
+ PROMPT_RULES,
6
+ PROMPT_SKILLS_AND_EXAMPLES,
7
+ PROMPT_REPLACE_VARIABLES,
8
+ } from "@xmtp/message-kit";
4
9
 
5
10
  export async function agent_prompt(userInfo: UserInfo) {
6
- let { address, ensDomain, converseUsername, preferredName } = userInfo;
11
+ let systemPrompt =
12
+ PROMPT_RULES +
13
+ PROMPT_USER_CONTENT(userInfo) +
14
+ PROMPT_SKILLS_AND_EXAMPLES(skills, "@ens");
7
15
 
8
- //Update the name of the agent with predefined prompt
9
- let systemPrompt = PROMPT_RULES.replace("{NAME}", skills?.[0]?.tag ?? "@ens");
10
-
11
- //Add user context to the prompt
12
- systemPrompt += PROMPT_USER_CONTENT(userInfo);
13
-
14
- //Add skills and examples to the prompt
15
- systemPrompt += PROMPT_SKILLS_AND_EXAMPLES(skills, "@ens");
16
-
17
- systemPrompt += `
16
+ let fineTunning = `
18
17
 
19
18
  ## Example responses:
20
19
 
21
20
  1. Check if the user does not have a ENS domain
22
- Hey ${preferredName}! it looks like you don't have a ENS domain yet! \n\Let me start by checking your Converse username with the .eth suffix\n/check ${converseUsername}.eth
21
+ Hey {PREFERRED_NAME}! it looks like you don't have a ENS domain yet! \n\Let me start by checking your Converse username with the .eth suffix\n/check {CONVERSE_USERNAME}.eth
23
22
 
24
23
  2. If the user has a ENS domain
25
- Hello ${preferredName} ! I'll help you get your ENS domain.\n Let's start by checking your ENS domain ${ensDomain}. Give me a moment.\n/check ${ensDomain}
24
+ Hello {PREFERRED_NAME} ! I'll help you get your ENS domain.\n Let's start by checking your ENS domain {ENS_DOMAIN}. Give me a moment.\n/check {ENS_DOMAIN}
26
25
 
27
26
  3. Check if the ENS domain is available
28
- Hello! I'll help you get your domain.\n Let's start by checking your ENS domain ${ensDomain}. Give me a moment.\n/check ${ensDomain}
27
+ Hello! I'll help you get your domain.\n Let's start by checking your ENS domain {ENS_DOMAIN}. Give me a moment.\n/check {ENS_DOMAIN}
29
28
 
30
29
  4. If the ENS domain is available,
31
- Looks like ${ensDomain} is available! Here you can register it:\n/register ${ensDomain}\n or I can suggest some cool alternatives? Le me know!
30
+ Looks like {ENS_DOMAIN} is available! Here you can register it:\n/register {ENS_DOMAIN}\n or I can suggest some cool alternatives? Le me know!
32
31
 
33
32
  5. If the ENS domain is already registered, let me suggest 5 cool alternatives
34
- Looks like ${ensDomain} is already registered!\n What about these cool alternatives?\n/cool ${ensDomain}
33
+ Looks like {ENS_DOMAIN} is already registered!\n What about these cool alternatives?\n/cool {ENS_DOMAIN}
35
34
 
36
35
  6. If the user wants to register a ENS domain, use the command "/register [domain]"
37
- Looks like ${ensDomain} is available! Let me help you register it\n/register ${ensDomain}
36
+ Looks like {ENS_DOMAIN} is available! Let me help you register it\n/register {ENS_DOMAIN}
38
37
 
39
38
  7. If the user wants to directly to tip to the ENS domain owner, use directly the command "/tip [domain]", this will return a url but a button to send the tip
40
- Here is the url to send the tip:\n/tip ${ensDomain}
39
+ Here is the url to send the tip:\n/tip {ENS_DOMAIN}
41
40
 
42
41
  8. If the user wants to get information about the ENS domain, use the command "/info [domain]"
43
- Hello! I'll help you get info about ${ensDomain}.\n Give me a moment.\n/info ${ensDomain}
42
+ Hello! I'll help you get info about {ENS_DOMAIN}.\n Give me a moment.\n/info {ENS_DOMAIN}
44
43
 
45
44
  9. If the user wants to renew their domain, use the command "/renew [domain]"
46
- Hello! I'll help you get your ENS domain.\n Let's start by checking your ENS domain ${ensDomain}. Give me a moment.\n/renew ${ensDomain}
45
+ Hello! I'll help you get your ENS domain.\n Let's start by checking your ENS domain {ENS_DOMAIN}. Give me a moment.\n/renew {ENS_DOMAIN}
47
46
 
48
47
  10. If the user wants cool suggestions about a domain, use the command "/cool [domain]"
49
- Here are some cool suggestions for your domain.\n/cool ${ensDomain}
48
+ Here are some cool suggestions for your domain.\n/cool {ENS_DOMAIN}
50
49
 
51
50
  ## Most common bugs
52
51
 
@@ -54,5 +53,16 @@ export async function agent_prompt(userInfo: UserInfo) {
54
53
  But you forgot to add the command at the end of the message.
55
54
  You should have said something like: "Looks like vitalik.eth is registered! What about these cool alternatives?\n/cool vitalik.eth
56
55
  `;
56
+
57
+ // Add the fine tuning to the system prompt
58
+ systemPrompt += fineTunning;
59
+
60
+ // Replace the variables in the system prompt
61
+ systemPrompt = PROMPT_REPLACE_VARIABLES(
62
+ systemPrompt,
63
+ userInfo?.address ?? "",
64
+ userInfo,
65
+ "@ens",
66
+ );
57
67
  return systemPrompt;
58
68
  }
@@ -8,7 +8,7 @@ export const skills: SkillGroup[] = [
8
8
  description: "Register ENS domains.",
9
9
  skills: [
10
10
  {
11
- command: "/register [domain]",
11
+ skill: "/register [domain]",
12
12
  triggers: ["/register"],
13
13
  handler: handleEns,
14
14
  description:
@@ -21,8 +21,7 @@ export const skills: SkillGroup[] = [
21
21
  },
22
22
  },
23
23
  {
24
- command: "/exists",
25
- adminOnly: true,
24
+ skill: "/exists",
26
25
  examples: ["/exists"],
27
26
  handler: handleEns,
28
27
  triggers: ["/exists"],
@@ -34,7 +33,7 @@ export const skills: SkillGroup[] = [
34
33
  },
35
34
  },
36
35
  {
37
- command: "/info [domain]",
36
+ skill: "/info [domain]",
38
37
  triggers: ["/info"],
39
38
  handler: handleEns,
40
39
  description:
@@ -47,7 +46,7 @@ export const skills: SkillGroup[] = [
47
46
  },
48
47
  },
49
48
  {
50
- command: "/renew [domain]",
49
+ skill: "/renew [domain]",
51
50
  triggers: ["/renew"],
52
51
  handler: handleEns,
53
52
  description:
@@ -60,7 +59,7 @@ export const skills: SkillGroup[] = [
60
59
  },
61
60
  },
62
61
  {
63
- command: "/check [domain]",
62
+ skill: "/check [domain]",
64
63
  triggers: ["/check"],
65
64
  handler: handleEns,
66
65
  examples: ["/check vitalik.eth", "/check fabri.base.eth"],
@@ -72,7 +71,7 @@ export const skills: SkillGroup[] = [
72
71
  },
73
72
  },
74
73
  {
75
- command: "/cool [domain]",
74
+ skill: "/cool [domain]",
76
75
  triggers: ["/cool"],
77
76
  examples: ["/cool vitalik.eth"],
78
77
  handler: handleEns,
@@ -84,7 +83,7 @@ export const skills: SkillGroup[] = [
84
83
  },
85
84
  },
86
85
  {
87
- command: "/reset",
86
+ skill: "/reset",
88
87
  triggers: ["/reset"],
89
88
  examples: ["/reset"],
90
89
  handler: handleEns,
@@ -92,7 +91,7 @@ export const skills: SkillGroup[] = [
92
91
  params: {},
93
92
  },
94
93
  {
95
- command: "/tip [address]",
94
+ skill: "/tip [address]",
96
95
  description: "Show a URL for tipping a domain owner.",
97
96
  triggers: ["/tip"],
98
97
  handler: handleEns,
@@ -1,9 +1,14 @@
1
1
  import { run, HandlerContext } from "@xmtp/message-kit";
2
+ import { skills } from "./skills.js";
3
+ run(
4
+ async (context: HandlerContext) => {
5
+ // Get the message and the address from the sender
6
+ const { content, sender } = context.message;
2
7
 
3
- run(async (context: HandlerContext) => {
4
- // Get the message and the address from the sender
5
- const { content, sender } = context.message;
6
-
7
- // To reply, just call `reply` on the HandlerContext
8
- await context.send(`gm`);
9
- });
8
+ // To reply, just call `reply` on the HandlerContext
9
+ await context.send(`gm`);
10
+ },
11
+ {
12
+ skills: skills,
13
+ },
14
+ );
@@ -0,0 +1,29 @@
1
+ import type { SkillGroup } from "@xmtp/message-kit";
2
+
3
+ export const skills: SkillGroup[] = [
4
+ {
5
+ name: "Group bot",
6
+ tag: "@bot",
7
+ description: "Group bot for tipping and transactions.",
8
+ skills: [
9
+ {
10
+ skill: "/tip [usernames] [amount] [token]",
11
+ triggers: ["/tip"],
12
+ examples: ["/tip @vitalik 10 usdc"],
13
+ description: "Tip users in a specified token.",
14
+ handler: undefined,
15
+ params: {
16
+ username: {
17
+ default: "",
18
+ plural: true,
19
+ type: "username",
20
+ },
21
+ amount: {
22
+ default: 10,
23
+ type: "number",
24
+ },
25
+ },
26
+ },
27
+ ],
28
+ },
29
+ ];
@@ -1,3 +1,2 @@
1
1
  KEY= # the private key of the bot wallet
2
- OPEN_AI_API_KEY= # openai api key
3
- STACK_API_KEY= # stack api key
2
+ OPEN_AI_API_KEY= # openai api key
@@ -1,68 +1,31 @@
1
- import { HandlerContext, AbstractedMember } from "@xmtp/message-kit";
2
- import { textGeneration } from "../lib/gpt.js";
3
-
4
- export async function handler(context: HandlerContext) {
5
- if (!process?.env?.OPEN_AI_API_KEY) {
6
- console.warn("No OPEN_AI_API_KEY found in .env");
7
- return;
8
- }
1
+ import { run, HandlerContext } from "@xmtp/message-kit";
2
+ import { textGeneration, processMultilineResponse } from "@xmtp/message-kit";
3
+ import { agent_prompt } from "../prompt.js";
4
+ import { getUserInfo } from "@xmtp/message-kit";
9
5
 
6
+ run(async (context: HandlerContext) => {
10
7
  const {
11
8
  message: {
9
+ content: { text, params },
12
10
  sender,
13
- content: { content, params },
14
11
  },
15
- skill,
16
12
  } = context;
17
13
 
18
- const systemPrompt = generateSystemPrompt(context);
19
14
  try {
20
- let userPrompt = params?.prompt ?? content;
21
-
15
+ let userPrompt = params?.prompt ?? text;
16
+ const userInfo = await getUserInfo(sender.address);
17
+ if (!userInfo) {
18
+ console.log("User info not found");
19
+ return;
20
+ }
22
21
  const { reply } = await textGeneration(
23
22
  sender.address,
24
23
  userPrompt,
25
- systemPrompt,
24
+ await agent_prompt(userInfo),
26
25
  );
27
- skill(reply);
26
+ await processMultilineResponse(sender.address, reply, context);
28
27
  } catch (error) {
29
28
  console.error("Error during OpenAI call:", error);
30
- await context.reply("An error occurred while processing your request.");
29
+ await context.send("An error occurred while processing your request.");
31
30
  }
32
- }
33
-
34
- function generateSystemPrompt(context: HandlerContext) {
35
- const {
36
- members,
37
- skills,
38
- message: { sender },
39
- } = context;
40
-
41
- const systemPrompt = `
42
- ### Context
43
-
44
- You are a helpful bot agent that lives inside a web3 messaging group that helps interpret user requests and execute commands.
45
- #### Users
46
- ${JSON.stringify(
47
- members?.map((member: AbstractedMember) => ({
48
- ...member,
49
- username: `@${member.accountAddresses[0]}`,
50
- })),
51
- )}\n
52
- #### Commands
53
- ${JSON.stringify(skills)}\n
54
- The message was sent by @${sender?.address}
55
-
56
- ### Examples
57
- prompt /agent tip alix and bo
58
- reply /tip @alix @bo 10
59
-
60
- Important:
61
- - If a user asks jokes, make jokes about web3 devs\n
62
- - 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.
63
- - Populate the command with the correct or random values. Always return skills with real values only, using usernames with @ and excluding addresses.\n
64
- - 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
65
- - If the user is grateful, respond asking for a tip in a playful manner.
66
- `;
67
- return systemPrompt;
68
- }
31
+ });
@@ -1,14 +1,13 @@
1
1
  import { HandlerContext } from "@xmtp/message-kit";
2
2
 
3
- // Handler function to process game-related commands
3
+ // Handler function to process game-related
4
4
  export async function handler(context: HandlerContext) {
5
5
  const {
6
6
  message: {
7
- content: { command, params },
7
+ content: { skill, params, text },
8
8
  },
9
9
  } = context;
10
- if (!command) {
11
- const { content: text } = context?.message?.content;
10
+ if (!skill) {
12
11
  if (text === "🔎" || text === "🔍") {
13
12
  // Send the URL for the requested game
14
13
  context.reply("https://framedl.xyz/");
@@ -36,7 +35,7 @@ export async function handler(context: HandlerContext) {
36
35
  default:
37
36
  // Inform the user about unrecognized skills and provide available options
38
37
  context.send(
39
- "Command not recognized. Available games: wordle, slot, or help.",
38
+ "Skill not recognized. Available games: wordle, slot, or help.",
40
39
  );
41
40
  }
42
41
  }
@@ -3,22 +3,26 @@ import { HandlerContext } from "@xmtp/message-kit";
3
3
  export async function handler(context: HandlerContext) {
4
4
  const {
5
5
  skills,
6
- group,
7
6
  message: {
8
- content: { command },
7
+ content: { skill },
9
8
  },
9
+ group,
10
10
  } = context;
11
11
 
12
- if (command == "help") {
12
+ if (skill == "help") {
13
13
  const intro =
14
14
  "Available experiences:\n" +
15
15
  skills
16
16
  ?.flatMap((app) => app.skills)
17
- .map((skill) => `${skill.command} - ${skill.description}`)
17
+ .map((skill) => `${skill.skill} - ${skill.description}`)
18
18
  .join("\n") +
19
19
  "\nUse these skills to interact with specific apps.";
20
20
  context.send(intro);
21
- } else if (command == "id") {
22
- context.send(context.group?.id);
21
+ } else if (skill == "id") {
22
+ if (!group?.id) {
23
+ context.send("This skill only works in group chats.");
24
+ return;
25
+ }
26
+ context.send(group?.id);
23
27
  }
24
28
  }