create-message-kit 1.1.11 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- package/index.js +0 -1
- package/package.json +1 -1
- package/templates/agent/package.json +2 -1
- package/templates/agent/src/handlers/game.ts +7 -6
- package/templates/agent/src/handlers/info.ts +10 -6
- package/templates/agent/src/handlers/pay.ts +1 -1
- package/templates/agent/src/handlers/register.ts +1 -1
- package/templates/agent/src/handlers/todo.ts +58 -0
- package/templates/agent/src/index.ts +7 -4
- package/templates/agent/src/prompt.ts +10 -42
- package/templates/gated/src/skills.ts +1 -0
- package/templates/gpt/src/index.ts +1 -1
- package/templates/group/.env.example +0 -2
- package/templates/group/.yarnrc.yml +0 -4
- package/templates/group/package.json +0 -20
- package/templates/group/src/handlers/game.ts +0 -57
- package/templates/group/src/handlers/helpers.ts +0 -46
- package/templates/group/src/handlers/pay.ts +0 -38
- package/templates/group/src/handlers/tipping.ts +0 -60
- package/templates/group/src/index.ts +0 -32
- package/templates/group/src/prompt.ts +0 -33
package/index.js
CHANGED
package/package.json
CHANGED
@@ -35,23 +35,24 @@ export async function handler(context: XMTPContext) {
|
|
35
35
|
wordle: "https://framedl.xyz",
|
36
36
|
slot: "https://slot-machine-frame.vercel.app",
|
37
37
|
};
|
38
|
-
|
38
|
+
let returnText = "";
|
39
39
|
switch (params.game) {
|
40
40
|
case "wordle":
|
41
41
|
case "slot":
|
42
42
|
// Retrieve the URL for the requested game using a simplified variable assignment
|
43
43
|
const gameUrl = gameUrls[params.game];
|
44
44
|
// Send the URL for the requested game
|
45
|
-
|
45
|
+
returnText = gameUrl;
|
46
46
|
break;
|
47
47
|
|
48
48
|
case "help":
|
49
|
-
|
49
|
+
returnText = "Available games: \n/game wordle\n/game slot";
|
50
50
|
break;
|
51
51
|
default:
|
52
52
|
// Inform the user about unrecognized skills and provide available options
|
53
|
-
|
54
|
-
"Skill not recognized. Available games: wordle, slot, or help."
|
55
|
-
|
53
|
+
returnText =
|
54
|
+
"Skill not recognized. Available games: wordle, slot, or help.";
|
55
|
+
break;
|
56
56
|
}
|
57
|
+
return { code: 200, message: returnText };
|
57
58
|
}
|
@@ -35,13 +35,17 @@ export async function handler(context: XMTPContext) {
|
|
35
35
|
message: "Domain not found.",
|
36
36
|
};
|
37
37
|
}
|
38
|
+
console.log(data);
|
38
39
|
let message = `Domain information:\n\n`;
|
39
|
-
message += `
|
40
|
-
message += `
|
41
|
-
message += `
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
message += `URL: https://app.ens.domains/${data?.ensDomain}\n`;
|
41
|
+
if (data?.address) message += `Address: ${data?.address}\n`;
|
42
|
+
if (data?.ensInfo?.avatar) message += `Avatar: ${data?.ensInfo?.avatar}\n`;
|
43
|
+
if (data?.ensInfo?.description)
|
44
|
+
message += `Description: ${data?.ensInfo?.description}\n`;
|
45
|
+
if (data?.ensInfo?.ens_primary)
|
46
|
+
message += `Primary ENS: ${data?.ensInfo?.ens_primary}\n`;
|
47
|
+
if (data?.ensInfo?.github) message += `GitHub: ${data?.ensInfo?.github}\n`;
|
48
|
+
if (data?.ensInfo?.twitter) message += `Twitter: ${data?.ensInfo?.twitter}\n`;
|
45
49
|
message += `\n\nWould you like to tip the domain owner for getting there first 🤣?`;
|
46
50
|
message = message.trim();
|
47
51
|
|
@@ -6,7 +6,7 @@ export const registerSkill: Skill[] = [
|
|
6
6
|
skill: "/pay [amount] [token] [username]",
|
7
7
|
examples: ["/pay 10 vitalik.eth"],
|
8
8
|
description:
|
9
|
-
"Send a specified amount of a cryptocurrency to a destination address.",
|
9
|
+
"Send a specified amount of a cryptocurrency to a destination address. \nWhen tipping, you can asume its 1 usdc.",
|
10
10
|
handler: handler,
|
11
11
|
params: {
|
12
12
|
amount: {
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import { Resend } from 'resend';
|
2
|
+
import { XMTPContext } from "@xmtp/message-kit";
|
3
|
+
import type { Skill } from "@xmtp/message-kit";
|
4
|
+
|
5
|
+
const resend = new Resend(process.env.RESEND_API_KEY); // Replace with your Resend API key
|
6
|
+
|
7
|
+
export const registerSkill: Skill[] = [
|
8
|
+
{
|
9
|
+
skill: "/todo",
|
10
|
+
handler: handler,
|
11
|
+
examples: ["/todo"],
|
12
|
+
description: "Send a list of TODOs via email. Receives no parameters.",
|
13
|
+
params: {},
|
14
|
+
},
|
15
|
+
];
|
16
|
+
|
17
|
+
export async function handler(context: XMTPContext) {
|
18
|
+
const {
|
19
|
+
message: {
|
20
|
+
content: { reply },
|
21
|
+
},
|
22
|
+
} = context;
|
23
|
+
|
24
|
+
let email = "";
|
25
|
+
|
26
|
+
while (true) {
|
27
|
+
const emailResponse = await context.awaitResponse("Please provide your email address to receive the TODO summary:");
|
28
|
+
email = emailResponse;
|
29
|
+
|
30
|
+
// Basic email validation
|
31
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
32
|
+
if (!emailRegex.test(email)) {
|
33
|
+
await context.send("Invalid email format. Please try again with a valid email address.");
|
34
|
+
continue;
|
35
|
+
}
|
36
|
+
break;
|
37
|
+
}
|
38
|
+
|
39
|
+
try {
|
40
|
+
let content={
|
41
|
+
from: 'onboarding@resend.dev',
|
42
|
+
to: email,
|
43
|
+
subject: 'Your TODO Summary from Converse',
|
44
|
+
html: `
|
45
|
+
<h1>Your TODO Summary</h1>
|
46
|
+
<p>${reply}</p>
|
47
|
+
`
|
48
|
+
}
|
49
|
+
console.log(content);
|
50
|
+
const response = await resend.emails.send(content);
|
51
|
+
console.log(response);
|
52
|
+
|
53
|
+
await context.send(`✅ Summary sent successfully to ${email}`);
|
54
|
+
} catch (error) {
|
55
|
+
await context.send("❌ Failed to send email. Please try again later.");
|
56
|
+
console.error('Error sending email:', error);
|
57
|
+
}
|
58
|
+
}
|
@@ -15,11 +15,11 @@ import { registerSkill as paySkill } from "./handlers/pay.js";
|
|
15
15
|
import { registerSkill as resetSkill } from "./handlers/reset.js";
|
16
16
|
import { registerSkill as tokenSkill } from "./handlers/token.js";
|
17
17
|
import { registerSkill as gameSkill } from "./handlers/game.js";
|
18
|
+
import { registerSkill as todoSkill } from "./handlers/todo.js";
|
18
19
|
import fs from "fs";
|
19
20
|
|
20
21
|
export const frameUrl = "https://ens.steer.fun/";
|
21
22
|
export const ensUrl = "https://app.ens.domains/";
|
22
|
-
export const txpayUrl = "https://txpay.vercel.app";
|
23
23
|
|
24
24
|
// [!region skills]
|
25
25
|
export const agent: Agent = {
|
@@ -36,11 +36,12 @@ export const agent: Agent = {
|
|
36
36
|
...paySkill,
|
37
37
|
...tokenSkill,
|
38
38
|
...gameSkill,
|
39
|
+
...todoSkill,
|
39
40
|
],
|
40
41
|
};
|
41
42
|
// [!endregion skills]
|
42
43
|
|
43
|
-
// [!region
|
44
|
+
// [!region run1]
|
44
45
|
run(
|
45
46
|
async (context: XMTPContext) => {
|
46
47
|
const {
|
@@ -49,11 +50,13 @@ run(
|
|
49
50
|
} = context;
|
50
51
|
|
51
52
|
let prompt = await replaceVariables(systemPrompt, sender.address, agent);
|
52
|
-
|
53
|
+
// [!region run1]
|
54
|
+
//This is only used for to update the docs.
|
53
55
|
fs.writeFileSync("example_prompt.md", prompt);
|
56
|
+
// [!region run2]
|
54
57
|
await agentReply(context, prompt);
|
55
58
|
},
|
56
59
|
{ agent },
|
57
60
|
);
|
58
61
|
|
59
|
-
// [!endregion
|
62
|
+
// [!endregion run2]
|
@@ -7,46 +7,14 @@ Your are helpful and playful web3 agent called {agent_name} that lives inside a
|
|
7
7
|
|
8
8
|
{skills}
|
9
9
|
|
10
|
-
##
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
/
|
20
|
-
3. Check if the ENS domain is available:
|
21
|
-
Hello! I'll help you get your domain.
|
22
|
-
Let's start by checking your ENS domain. Give me a moment.
|
23
|
-
/check [domain]
|
24
|
-
4. If the ENS domain is available:
|
25
|
-
Looks like [domain] is available! Here you can register it:
|
26
|
-
/register [domain]
|
27
|
-
Or I can suggest some cool alternatives? Let me know!
|
28
|
-
5. If the ENS domain is already registered, suggest 5 cool alternatives:
|
29
|
-
Looks like [domain] is already registered!
|
30
|
-
What about these cool alternatives?
|
31
|
-
/cool [domain]
|
32
|
-
6. If the user wants to register an ENS domain:
|
33
|
-
Looks like [domain] is available! Let me help you register it.
|
34
|
-
/register [domain]
|
35
|
-
7. If the user wants to directly tip the ENS domain owner:
|
36
|
-
Here is the URL to send the tip:
|
37
|
-
/pay 1 usdc [address]
|
38
|
-
8. If the user wants to get information about the ENS domain:
|
39
|
-
Hello! I'll help you get info about [domain].
|
40
|
-
Give me a moment.
|
41
|
-
/info [domain]
|
42
|
-
9. If the user wants to renew their domain:
|
43
|
-
Hello! I'll help you get your ENS domain.
|
44
|
-
Let's start by checking your ENS domain. Give me a moment.
|
45
|
-
/renew [domain]
|
46
|
-
10. If the user wants cool suggestions about a domain:
|
47
|
-
Here are some cool suggestions for your domain.
|
48
|
-
/cool [domain]
|
49
|
-
|
50
|
-
## Most common bugs
|
51
|
-
1. Some times you will say something like: "Looks like vitalik.eth is registered! What about these cool alternatives?" But you forgot to add the command at the end of the message.
|
10
|
+
## Common Issues
|
11
|
+
1. Missing commands in responses
|
12
|
+
**Issue**: Sometimes responses about registered domains are sent without the required command.
|
13
|
+
**Example**:
|
14
|
+
Incorrect:
|
15
|
+
> "Looks like vitalik.eth is registered! What about these cool alternatives?"
|
16
|
+
|
17
|
+
Correct:
|
18
|
+
> "Looks like vitalik.eth is registered! What about these cool alternatives?
|
19
|
+
> /cool vitalik.eth"
|
52
20
|
`;
|
@@ -19,6 +19,6 @@ run(async (context: XMTPContext) => {
|
|
19
19
|
message: { sender },
|
20
20
|
} = context;
|
21
21
|
|
22
|
-
let prompt = await replaceVariables(systemPrompt, sender.address,
|
22
|
+
let prompt = await replaceVariables(systemPrompt, sender.address, agent);
|
23
23
|
await agentReply(context, prompt);
|
24
24
|
});
|
@@ -1,20 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"name": "group",
|
3
|
-
"private": true,
|
4
|
-
"type": "module",
|
5
|
-
"scripts": {
|
6
|
-
"build": "tsc",
|
7
|
-
"dev": "tsc -w & sleep 1 && NODE_NO_WARNINGS=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,57 +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
|
-
// Respond with the appropriate game URL or an error message
|
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
|
-
context.send(gameUrl);
|
46
|
-
break;
|
47
|
-
|
48
|
-
case "help":
|
49
|
-
context.send("Available games: \n/game wordle\n/game slot");
|
50
|
-
break;
|
51
|
-
default:
|
52
|
-
// Inform the user about unrecognized skills and provide available options
|
53
|
-
context.send(
|
54
|
-
"Skill not recognized. Available games: wordle, slot, or help.",
|
55
|
-
);
|
56
|
-
}
|
57
|
-
}
|
@@ -1,46 +0,0 @@
|
|
1
|
-
import { XMTPContext } from "@xmtp/message-kit";
|
2
|
-
|
3
|
-
import type { Skill } from "@xmtp/message-kit";
|
4
|
-
|
5
|
-
export const registerSkill: Skill[] = [
|
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) {
|
23
|
-
const {
|
24
|
-
message: {
|
25
|
-
content: { skill },
|
26
|
-
},
|
27
|
-
group,
|
28
|
-
agent,
|
29
|
-
} = context;
|
30
|
-
|
31
|
-
if (skill == "help") {
|
32
|
-
const intro =
|
33
|
-
"Available experiences:\n" +
|
34
|
-
agent?.skills
|
35
|
-
.map((skill) => `${skill.skill} - ${skill.description}`)
|
36
|
-
.join("\n") +
|
37
|
-
"\nUse these skills to interact with specific apps.";
|
38
|
-
context.send(intro);
|
39
|
-
} else if (skill == "id") {
|
40
|
-
if (!group?.id) {
|
41
|
-
context.send("This skill only works in group chats.");
|
42
|
-
return;
|
43
|
-
}
|
44
|
-
context.send(group?.id);
|
45
|
-
}
|
46
|
-
}
|
@@ -1,38 +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: "/pay [amount] [token] [username]",
|
7
|
-
examples: ["/pay 10 vitalik.eth"],
|
8
|
-
description:
|
9
|
-
"Send a specified amount of a cryptocurrency to a destination address.",
|
10
|
-
handler: handler,
|
11
|
-
params: {
|
12
|
-
amount: {
|
13
|
-
default: 10,
|
14
|
-
type: "number",
|
15
|
-
},
|
16
|
-
token: {
|
17
|
-
default: "usdc",
|
18
|
-
type: "string",
|
19
|
-
values: ["eth", "dai", "usdc", "degen"], // Accepted tokens
|
20
|
-
},
|
21
|
-
username: {
|
22
|
-
default: "",
|
23
|
-
type: "username",
|
24
|
-
},
|
25
|
-
},
|
26
|
-
},
|
27
|
-
];
|
28
|
-
export async function handler(context: XMTPContext) {
|
29
|
-
const {
|
30
|
-
message: {
|
31
|
-
content: {
|
32
|
-
params: { address },
|
33
|
-
},
|
34
|
-
},
|
35
|
-
} = context;
|
36
|
-
|
37
|
-
await context.requestPayment(1, "USDC", address);
|
38
|
-
}
|
@@ -1,60 +0,0 @@
|
|
1
|
-
import { getUserInfo, AbstractedMember, XMTPContext } from "@xmtp/message-kit";
|
2
|
-
import type { Skill } from "@xmtp/message-kit";
|
3
|
-
|
4
|
-
export const registerSkill: Skill[] = [
|
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,32 +0,0 @@
|
|
1
|
-
import {
|
2
|
-
run,
|
3
|
-
agentReply,
|
4
|
-
XMTPContext,
|
5
|
-
replaceVariables,
|
6
|
-
Agent,
|
7
|
-
} from "@xmtp/message-kit";
|
8
|
-
import { registerSkill as tippingSkill } from "./handlers/tipping.js";
|
9
|
-
import { registerSkill as paymentSkill } from "./handlers/pay.js";
|
10
|
-
import { registerSkill as gameSkill } from "./handlers/game.js";
|
11
|
-
import { registerSkill as helperSkill } from "./handlers/helpers.js";
|
12
|
-
import { systemPrompt } from "./prompt.js";
|
13
|
-
|
14
|
-
export const agent: Agent = {
|
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
|
-
|
21
|
-
run(
|
22
|
-
async (context: XMTPContext) => {
|
23
|
-
const {
|
24
|
-
message: { sender },
|
25
|
-
agent,
|
26
|
-
} = context;
|
27
|
-
|
28
|
-
let prompt = await replaceVariables(systemPrompt, sender.address, agent);
|
29
|
-
await agentReply(context, prompt);
|
30
|
-
},
|
31
|
-
{ agent },
|
32
|
-
);
|
@@ -1,33 +0,0 @@
|
|
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
|
15
|
-
|
16
|
-
2. When user wants to pay a specific token:
|
17
|
-
I'll help you pay 1 USDC to 0x123...
|
18
|
-
/pay 1 [token] 0x123456789...
|
19
|
-
*This will return a url to pay
|
20
|
-
|
21
|
-
3. If the user wants to pay a eth domain:
|
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
|
25
|
-
*This will return a url to pay
|
26
|
-
|
27
|
-
4. If the user wants to pay a username:
|
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
|
31
|
-
*This will return a url to pay
|
32
|
-
|
33
|
-
`;
|