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

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,18 +1,37 @@
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
+ runConfig,
10
29
  } = context;
11
30
 
12
31
  if (skill == "help") {
13
32
  const intro =
14
33
  "Available experiences:\n" +
15
- skills
34
+ runConfig?.skills
16
35
  ?.flatMap((app) => app.skills)
17
36
  .map((skill) => `${skill.skill} - ${skill.description}`)
18
37
  .join("\n") +
@@ -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,61 @@
1
+ import { XMTPContext, AbstractedMember } from "@xmtp/message-kit";
2
+ import { getUserInfo } from "@xmtp/message-kit";
3
+ import type { skillAction } from "@xmtp/message-kit";
4
+
5
+ export const registerSkill: skillAction[] = [
6
+ {
7
+ skill: "/tip [usernames] [amount] [token]",
8
+ examples: ["/tip @vitalik 10 usdc"],
9
+ description: "Tip users in a specified token.",
10
+ handler: handleTipping,
11
+ params: {
12
+ username: {
13
+ default: "",
14
+ plural: true,
15
+ type: "username",
16
+ },
17
+ amount: {
18
+ default: 10,
19
+ type: "number",
20
+ },
21
+ token: {
22
+ default: "usdc",
23
+ type: "string",
24
+ values: ["eth", "dai", "usdc", "degen"],
25
+ },
26
+ },
27
+ },
28
+ ];
29
+
30
+ export async function handleTipping(context: XMTPContext) {
31
+ const {
32
+ message: {
33
+ content: {
34
+ skill,
35
+ params: { amount, username },
36
+ },
37
+ sender,
38
+ },
39
+ } = context;
40
+ let receivers: AbstractedMember[] = [];
41
+
42
+ receivers = await Promise.all(
43
+ username.map((username: string) => getUserInfo(username)),
44
+ );
45
+
46
+ if (!sender || receivers.length === 0 || amount === 0) {
47
+ context.reply("Sender or receiver or amount not found.");
48
+ }
49
+ const receiverAddresses = receivers.map((receiver) => receiver.address);
50
+
51
+ context.sendTo(
52
+ `You received ${amount} tokens from ${sender.address}.`,
53
+ receiverAddresses,
54
+ );
55
+
56
+ // Notify sender of the transaction details
57
+ context.sendTo(
58
+ `You sent ${amount * receiverAddresses.length} tokens in total.`,
59
+ [sender.address],
60
+ );
61
+ }
@@ -1,31 +1,33 @@
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";
1
+ import { run, agentReply, replaceVariables } from "@xmtp/message-kit";
2
+ import { registerSkill as tippingSkill } from "./handlers/tipping.js";
3
+ import { registerSkill as paymentSkill } from "./handlers/payment.js";
4
+ import { registerSkill as gameSkill } from "./handlers/game.js";
5
+ import { registerSkill as helperSkill } from "./handlers/helpers.js";
6
+ import { systemPrompt } from "./prompt.js";
5
7
 
6
- run(async (context: HandlerContext) => {
7
- const {
8
- message: {
9
- content: { text, params },
10
- sender,
11
- },
12
- } = context;
8
+ export const skills = [
9
+ {
10
+ name: "Group bot",
11
+ tag: "@bot",
12
+ description: "Group agent for tipping payments, games and more.",
13
+ skills: [...tippingSkill, ...paymentSkill, ...gameSkill, ...helperSkill],
14
+ },
15
+ ];
13
16
 
14
- try {
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
- }
21
- const { reply } = await textGeneration(
17
+ run(
18
+ async (context) => {
19
+ const {
20
+ message: { sender },
21
+ runConfig,
22
+ } = context;
23
+
24
+ let prompt = await replaceVariables(
25
+ systemPrompt,
22
26
  sender.address,
23
- userPrompt,
24
- await agent_prompt(userInfo),
27
+ runConfig?.skills,
28
+ "@bot",
25
29
  );
26
- await processMultilineResponse(sender.address, reply, context);
27
- } catch (error) {
28
- console.error("Error during OpenAI call:", error);
29
- await context.send("An error occurred while processing your request.");
30
- }
31
- });
30
+ await agentReply(context, prompt);
31
+ },
32
+ { skills },
33
+ );
@@ -1,45 +1,33 @@
1
- import { skills } from "./skills.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";
9
-
10
- export async function agent_prompt(userInfo: UserInfo) {
11
- let systemPrompt =
12
- PROMPT_RULES +
13
- PROMPT_USER_CONTENT(userInfo) +
14
- PROMPT_SKILLS_AND_EXAMPLES(skills, "@bot");
15
-
16
- let fineTunedPrompt = `
17
- ## Example response
18
-
19
- 1. If user wants to play a game, use the skill 'game' and specify the game type.
20
- Hey! Sure let's do that.\n/game wordle
1
+ export const systemPrompt = `
2
+ {persona}
3
+
4
+ {rules}
5
+
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
21
15
 
22
16
  2. When user wants to pay a specific token:
23
- I'll help you pay 1 USDC to 0x123...\n/pay 1 {TOKE}} 0x123456789...
17
+ I'll help you pay 1 USDC to 0x123...
18
+ /pay 1 [token] 0x123456789...
24
19
  *This will return a url to pay
25
20
 
26
21
  3. If the user wants to pay a eth domain:
27
- 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
28
25
  *This will return a url to pay
29
26
 
30
27
  4. If the user wants to pay a username:
31
- 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
32
31
  *This will return a url to pay
33
- `;
34
-
35
- systemPrompt += fineTunedPrompt;
36
- // Replace the variables in the system prompt
37
- systemPrompt = PROMPT_REPLACE_VARIABLES(
38
- systemPrompt,
39
- userInfo?.address ?? "",
40
- userInfo,
41
- "@bot",
42
- );
43
- console.log(systemPrompt);
44
- return systemPrompt;
45
- }
32
+
33
+ `;
@@ -1,175 +0,0 @@
1
- import { HandlerContext, SkillResponse } from "@xmtp/message-kit";
2
- import { getUserInfo, clearInfoCache, isOnXMTP } from "@xmtp/message-kit";
3
- import { isAddress } from "viem";
4
- import { clearMemory } from "@xmtp/message-kit";
5
-
6
- export const frameUrl = "https://ens.steer.fun/";
7
- export const ensUrl = "https://app.ens.domains/";
8
- export const baseTxUrl = "https://base-tx-frame.vercel.app";
9
-
10
- export async function handleEns(
11
- context: HandlerContext,
12
- ): Promise<SkillResponse | undefined> {
13
- const {
14
- message: {
15
- sender,
16
- content: { skill, params },
17
- },
18
- } = context;
19
-
20
- if (skill == "reset") {
21
- clearMemory();
22
- return { code: 200, message: "Conversation reset." };
23
- } else if (skill == "renew") {
24
- // Destructure and validate parameters for the ens
25
- const { domain } = params;
26
- // Check if the user holds the domain
27
- if (!domain) {
28
- return {
29
- code: 400,
30
- message: "Missing required parameters. Please provide domain.",
31
- };
32
- }
33
-
34
- const data = await getUserInfo(domain);
35
-
36
- if (!data?.address || data?.address !== sender?.address) {
37
- return {
38
- code: 403,
39
- message:
40
- "Looks like this domain is not registered to you. Only the owner can renew it.",
41
- };
42
- }
43
-
44
- // Generate URL for the ens
45
- let url_ens = frameUrl + "frames/manage?name=" + domain;
46
- return { code: 200, message: `${url_ens}` };
47
- } else if (skill == "register") {
48
- // Destructure and validate parameters for the ens
49
- const { domain } = params;
50
-
51
- if (!domain) {
52
- return {
53
- code: 400,
54
- message: "Missing required parameters. Please provide domain.",
55
- };
56
- }
57
- // Generate URL for the ens
58
- let url_ens = ensUrl + domain;
59
- return { code: 200, message: `${url_ens}` };
60
- } else if (skill == "info") {
61
- const { domain } = params;
62
-
63
- const data = await getUserInfo(domain);
64
- if (!data?.ensDomain) {
65
- return {
66
- code: 404,
67
- message: "Domain not found.",
68
- };
69
- }
70
-
71
- const formattedData = {
72
- Address: data?.address,
73
- "Avatar URL": data?.ensInfo?.avatar,
74
- Description: data?.ensInfo?.description,
75
- ENS: data?.ensDomain,
76
- "Primary ENS": data?.ensInfo?.ens_primary,
77
- GitHub: data?.ensInfo?.github,
78
- Resolver: data?.ensInfo?.resolverAddress,
79
- Twitter: data?.ensInfo?.twitter,
80
- URL: `${ensUrl}${domain}`,
81
- };
82
-
83
- let message = "Domain information:\n\n";
84
- for (const [key, value] of Object.entries(formattedData)) {
85
- if (value) {
86
- message += `${key}: ${value}\n`;
87
- }
88
- }
89
- message += `\n\nWould you like to tip the domain owner for getting there first 🤣?`;
90
- message = message.trim();
91
- if (await isOnXMTP(context.client, context.v2client, sender?.address)) {
92
- await context.send(
93
- `Ah, this domains is in XMTP, you can message it directly: https://converse.xyz/dm/${domain}`,
94
- );
95
- }
96
- return { code: 200, message };
97
- } else if (skill == "check") {
98
- const { domain } = params;
99
-
100
- if (!domain) {
101
- return {
102
- code: 400,
103
- message: "Please provide a domain name to check.",
104
- };
105
- }
106
-
107
- const data = await getUserInfo(domain);
108
- if (!data?.address) {
109
- let message = `Looks like ${domain} is available! Here you can register it: ${ensUrl}${domain} or would you like to see some cool alternatives?`;
110
- return {
111
- code: 200,
112
- message,
113
- };
114
- } else {
115
- let message = `Looks like ${domain} is already registered!`;
116
- await context.executeSkill("/cool " + domain);
117
- return {
118
- code: 404,
119
- message,
120
- };
121
- }
122
- } else if (skill == "tip") {
123
- const { address } = params;
124
- if (!address) {
125
- return {
126
- code: 400,
127
- message: "Please provide an address to tip.",
128
- };
129
- }
130
- const data = await getUserInfo(address);
131
- let txUrl = `${baseTxUrl}/transaction/?transaction_type=send&buttonName=Tip%20${data?.ensDomain ?? ""}&amount=1&token=USDC&receiver=${
132
- isAddress(address) ? address : data?.address
133
- }`;
134
- console.log(txUrl);
135
- return {
136
- code: 200,
137
- message: txUrl,
138
- };
139
- } else if (skill == "cool") {
140
- const { domain } = params;
141
- //What about these cool alternatives?\
142
- return {
143
- code: 200,
144
- message: `${generateCoolAlternatives(domain)}`,
145
- };
146
- } else {
147
- return { code: 400, message: "Skill not found." };
148
- }
149
- }
150
-
151
- export const generateCoolAlternatives = (domain: string) => {
152
- const suffixes = ["lfg", "cool", "degen", "moon", "base", "gm"];
153
- const alternatives = [];
154
- for (let i = 0; i < 5; i++) {
155
- const randomPosition = Math.random() < 0.5;
156
- const baseDomain = domain.replace(/\.eth$/, ""); // Remove any existing .eth suffix
157
- alternatives.push(
158
- randomPosition
159
- ? `${suffixes[i]}${baseDomain}.eth`
160
- : `${baseDomain}${suffixes[i]}.eth`,
161
- );
162
- }
163
-
164
- const cool_alternativesFormat = alternatives
165
- .map(
166
- (alternative: string, index: number) => `${index + 1}. ${alternative} ✨`,
167
- )
168
- .join("\n");
169
- return cool_alternativesFormat;
170
- };
171
-
172
- export async function clear() {
173
- clearMemory();
174
- clearInfoCache();
175
- }
@@ -1,107 +0,0 @@
1
- import { handleEns } from "./handler/ens.js";
2
- import type { SkillGroup } from "@xmtp/message-kit";
3
-
4
- export const skills: SkillGroup[] = [
5
- {
6
- name: "Ens Domain Bot",
7
- tag: "@ens",
8
- description: "Register ENS domains.",
9
- skills: [
10
- {
11
- skill: "/register [domain]",
12
- triggers: ["/register"],
13
- handler: handleEns,
14
- description:
15
- "Register a new ENS domain. Returns a URL to complete the registration process.",
16
- examples: ["/register vitalik.eth"],
17
- params: {
18
- domain: {
19
- type: "string",
20
- },
21
- },
22
- },
23
- {
24
- skill: "/exists",
25
- examples: ["/exists"],
26
- handler: handleEns,
27
- triggers: ["/exists"],
28
- description: "Check if an address is onboarded.",
29
- params: {
30
- address: {
31
- type: "address",
32
- },
33
- },
34
- },
35
- {
36
- skill: "/info [domain]",
37
- triggers: ["/info"],
38
- handler: handleEns,
39
- description:
40
- "Get detailed information about an ENS domain including owner, expiry date, and resolver.",
41
- examples: ["/info nick.eth"],
42
- params: {
43
- domain: {
44
- type: "string",
45
- },
46
- },
47
- },
48
- {
49
- skill: "/renew [domain]",
50
- triggers: ["/renew"],
51
- handler: handleEns,
52
- description:
53
- "Extend the registration period of your ENS domain. Returns a URL to complete the renewal.",
54
- examples: ["/renew fabri.base.eth"],
55
- params: {
56
- domain: {
57
- type: "string",
58
- },
59
- },
60
- },
61
- {
62
- skill: "/check [domain]",
63
- triggers: ["/check"],
64
- handler: handleEns,
65
- examples: ["/check vitalik.eth", "/check fabri.base.eth"],
66
- description: "Check if a domain is available.",
67
- params: {
68
- domain: {
69
- type: "string",
70
- },
71
- },
72
- },
73
- {
74
- skill: "/cool [domain]",
75
- triggers: ["/cool"],
76
- examples: ["/cool vitalik.eth"],
77
- handler: handleEns,
78
- description: "Get cool alternatives for a .eth domain.",
79
- params: {
80
- domain: {
81
- type: "string",
82
- },
83
- },
84
- },
85
- {
86
- skill: "/reset",
87
- triggers: ["/reset"],
88
- examples: ["/reset"],
89
- handler: handleEns,
90
- description: "Reset the conversation.",
91
- params: {},
92
- },
93
- {
94
- skill: "/tip [address]",
95
- description: "Show a URL for tipping a domain owner.",
96
- triggers: ["/tip"],
97
- handler: handleEns,
98
- examples: ["/tip 0x1234567890123456789012345678901234567890"],
99
- params: {
100
- address: {
101
- type: "string",
102
- },
103
- },
104
- },
105
- ],
106
- },
107
- ];
@@ -1 +0,0 @@
1
- KEY= # the private key of the agent wallet
@@ -1,5 +0,0 @@
1
- import { run, HandlerContext } from "@xmtp/message-kit";
2
-
3
- run(async (context: HandlerContext) => {
4
- context.send("gm");
5
- });
@@ -1,29 +0,0 @@
1
- import { getUserInfo, HandlerContext } from "@xmtp/message-kit";
2
-
3
- export async function handler(context: HandlerContext) {
4
- const {
5
- message: {
6
- content: { skill, params },
7
- },
8
- } = context;
9
- const baseUrl = "https://txpay.vercel.app";
10
-
11
- if (skill === "pay") {
12
- const { amount: amountSend, token: tokenSend, username } = params; // [!code hl] // [!code focus]
13
- console.log("username", username);
14
- let senderInfo = await getUserInfo(username);
15
- if (!amountSend || !tokenSend || !senderInfo) {
16
- context.reply(
17
- "Missing required parameters. Please provide amount, token, and username.",
18
- );
19
- return {
20
- code: 400,
21
- message:
22
- "Missing required parameters. Please provide amount, token, and username.",
23
- };
24
- }
25
-
26
- let sendUrl = `${baseUrl}/?&amount=${amountSend}&token=${tokenSend}&receiver=${senderInfo.address}`;
27
- await context.send(`${sendUrl}`);
28
- }
29
- }
@@ -1,40 +0,0 @@
1
- import {
2
- HandlerContext,
3
- AbstractedMember,
4
- SkillResponse,
5
- } from "@xmtp/message-kit";
6
- import { getUserInfo } from "@xmtp/message-kit";
7
-
8
- export async function handler(context: HandlerContext) {
9
- const {
10
- message: {
11
- content: {
12
- skill,
13
- params: { amount, username },
14
- },
15
- sender,
16
- },
17
- } = context;
18
- let receivers: AbstractedMember[] = [];
19
-
20
- if (skill === "tip") {
21
- receivers = await Promise.all(
22
- username.map((username: string) => getUserInfo(username)),
23
- );
24
- }
25
- if (!sender || receivers.length === 0 || amount === 0) {
26
- context.reply("Sender or receiver or amount not found.");
27
- }
28
- const receiverAddresses = receivers.map((receiver) => receiver.address);
29
-
30
- context.sendTo(
31
- `You received ${amount} tokens from ${sender.address}.`,
32
- receiverAddresses,
33
- );
34
-
35
- // Notify sender of the transaction details
36
- context.sendTo(
37
- `You sent ${amount * receiverAddresses.length} tokens in total.`,
38
- [sender.address],
39
- );
40
- }