create-message-kit 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- package/examples/gm/.env.example +1 -2
- package/examples/gm/src/index.ts +2 -2
- package/examples/group/.env.example +1 -2
- package/examples/group/src/commands.ts +44 -61
- package/examples/group/src/handler/agent.ts +23 -11
- package/examples/group/src/handler/game.ts +16 -12
- package/examples/group/src/handler/loyalty.ts +13 -34
- package/examples/group/src/handler/moderation.ts +110 -0
- package/examples/group/src/handler/splitpayment.ts +62 -0
- package/examples/group/src/handler/tipping.ts +0 -3
- package/examples/group/src/handler/transaction.ts +0 -17
- package/examples/group/src/index.ts +19 -64
- package/examples/group/src/lib/openai.ts +8 -1
- package/examples/one-to-one/.env.example +1 -2
- package/examples/one-to-one/src/index.ts +3 -8
- package/examples/one-to-one/src/lib/cron.ts +2 -7
- package/examples/one-to-one/src/lib/redis.ts +2 -1
- package/index.js +1 -0
- package/package.json +1 -1
package/examples/gm/.env.example
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
KEY= # 0x... the private key of the bot wallet (with the 0x prefix)
|
2
|
-
MSG_LOG=false # logs the message on the console
|
1
|
+
KEY= # 0x... the private key of the bot wallet (with the 0x prefix)
|
package/examples/gm/src/index.ts
CHANGED
@@ -3,7 +3,7 @@ import { run, HandlerContext } from "@xmtp/message-kit";
|
|
3
3
|
run(async (context: HandlerContext) => {
|
4
4
|
// Get the message and the address from the sender
|
5
5
|
const { content, sender } = context.message;
|
6
|
-
|
6
|
+
console.log(content);
|
7
7
|
// To reply, just call `reply` on the HandlerContext.
|
8
|
-
await context.
|
8
|
+
await context.send(`gm`);
|
9
9
|
});
|
@@ -1,14 +1,21 @@
|
|
1
1
|
import type { CommandGroup } from "@xmtp/message-kit";
|
2
|
+
import { handler as tipping } from "./handler/tipping.js";
|
3
|
+
import { handler as agent } from "./handler/agent.js";
|
4
|
+
import { handler as transaction } from "./handler/transaction.js";
|
5
|
+
import { handler as games } from "./handler/game.js";
|
6
|
+
import { handler as admin } from "./handler/moderation.js";
|
7
|
+
import { handler as loyalty } from "./handler/loyalty.js";
|
2
8
|
|
3
9
|
export const commands: CommandGroup[] = [
|
4
10
|
{
|
5
11
|
name: "Tipping",
|
6
|
-
icon: "🎩",
|
7
12
|
description: "Tip tokens via emoji, replies or command.",
|
13
|
+
triggers: ["/tip", "🎩", "@tip"],
|
8
14
|
commands: [
|
9
15
|
{
|
10
16
|
command: "/tip [@users] [amount] [token]",
|
11
17
|
description: "Tip users in a specified token.",
|
18
|
+
handler: tipping,
|
12
19
|
params: {
|
13
20
|
username: {
|
14
21
|
default: "",
|
@@ -23,14 +30,15 @@ export const commands: CommandGroup[] = [
|
|
23
30
|
],
|
24
31
|
},
|
25
32
|
{
|
26
|
-
name: "
|
27
|
-
|
33
|
+
name: "Transactions",
|
34
|
+
triggers: ["@send", "/send", "@swap", "/swap", "/show"],
|
28
35
|
description: "Multipurpose transaction frame built onbase.",
|
29
36
|
commands: [
|
30
37
|
{
|
31
38
|
command: "/send [amount] [token] [@username]",
|
32
39
|
description:
|
33
40
|
"Send a specified amount of a cryptocurrency to a destination address.",
|
41
|
+
handler: transaction,
|
34
42
|
params: {
|
35
43
|
amount: {
|
36
44
|
default: 10,
|
@@ -50,6 +58,7 @@ export const commands: CommandGroup[] = [
|
|
50
58
|
{
|
51
59
|
command: "/swap [amount] [token_from] [token_to]",
|
52
60
|
description: "Exchange one type of cryptocurrency for another.",
|
61
|
+
handler: transaction,
|
53
62
|
params: {
|
54
63
|
amount: {
|
55
64
|
default: 10,
|
@@ -67,70 +76,28 @@ export const commands: CommandGroup[] = [
|
|
67
76
|
},
|
68
77
|
},
|
69
78
|
},
|
70
|
-
{
|
71
|
-
command: "/mint [collection_address] [token_id]",
|
72
|
-
description: "Create (mint) a new token or NFT.",
|
73
|
-
params: {
|
74
|
-
collection: {
|
75
|
-
default: "0x73a333cb82862d4f66f0154229755b184fb4f5b0",
|
76
|
-
type: "address",
|
77
|
-
},
|
78
|
-
tokenId: {
|
79
|
-
default: 1,
|
80
|
-
type: "number",
|
81
|
-
},
|
82
|
-
},
|
83
|
-
},
|
84
79
|
{
|
85
80
|
command: "/show",
|
81
|
+
handler: transaction,
|
86
82
|
description: "Show the whole frame.",
|
87
83
|
params: {},
|
88
84
|
},
|
89
85
|
],
|
90
86
|
},
|
91
|
-
{
|
92
|
-
name: "Betting",
|
93
|
-
icon: "🎰",
|
94
|
-
description: "Create bets with friends.",
|
95
|
-
commands: [
|
96
|
-
{
|
97
|
-
command: "/bet @users [name] [amount] [token]",
|
98
|
-
description: "Bet on basebet.",
|
99
|
-
params: {
|
100
|
-
username: {
|
101
|
-
default: "",
|
102
|
-
type: "username",
|
103
|
-
},
|
104
|
-
name: {
|
105
|
-
default: "",
|
106
|
-
type: "quoted",
|
107
|
-
},
|
108
|
-
amount: {
|
109
|
-
default: 10,
|
110
|
-
type: "number",
|
111
|
-
},
|
112
|
-
token: {
|
113
|
-
default: "eth",
|
114
|
-
type: "string",
|
115
|
-
values: ["eth", "dai", "usdc", "degen"],
|
116
|
-
},
|
117
|
-
},
|
118
|
-
},
|
119
|
-
],
|
120
|
-
},
|
121
87
|
{
|
122
88
|
name: "Games",
|
123
|
-
|
89
|
+
triggers: ["/game", "@game", "🔎", "🔍"],
|
124
90
|
description: "Provides various gaming experiences.",
|
125
91
|
commands: [
|
126
92
|
{
|
127
93
|
command: "/game [game]",
|
94
|
+
handler: games,
|
128
95
|
description: "Play a game.",
|
129
96
|
params: {
|
130
97
|
game: {
|
131
98
|
default: "",
|
132
99
|
type: "string",
|
133
|
-
values: ["wordle", "slot", "
|
100
|
+
values: ["wordle", "slot", "help"],
|
134
101
|
},
|
135
102
|
},
|
136
103
|
},
|
@@ -138,16 +105,18 @@ export const commands: CommandGroup[] = [
|
|
138
105
|
},
|
139
106
|
{
|
140
107
|
name: "Loyalty",
|
141
|
-
|
108
|
+
triggers: ["/points", "@points", "/leaderboard", "@leaderboard"],
|
142
109
|
description: "Manage group members and metadata.",
|
143
110
|
commands: [
|
144
111
|
{
|
145
112
|
command: "/points",
|
113
|
+
handler: loyalty,
|
146
114
|
description: "Check your points.",
|
147
115
|
params: {},
|
148
116
|
},
|
149
117
|
{
|
150
118
|
command: "/leaderboard",
|
119
|
+
handler: loyalty,
|
151
120
|
description: "Check the points of a user.",
|
152
121
|
params: {},
|
153
122
|
},
|
@@ -155,16 +124,17 @@ export const commands: CommandGroup[] = [
|
|
155
124
|
},
|
156
125
|
{
|
157
126
|
name: "Agent",
|
158
|
-
|
127
|
+
triggers: ["/agent", "@agent"],
|
159
128
|
description: "Manage agent commands.",
|
160
129
|
commands: [
|
161
130
|
{
|
162
131
|
command: "/agent [prompt]",
|
132
|
+
handler: agent,
|
163
133
|
description: "Manage agent commands.",
|
164
134
|
params: {
|
165
135
|
prompt: {
|
166
136
|
default: "",
|
167
|
-
type: "
|
137
|
+
type: "prompt",
|
168
138
|
},
|
169
139
|
},
|
170
140
|
},
|
@@ -172,11 +142,12 @@ export const commands: CommandGroup[] = [
|
|
172
142
|
},
|
173
143
|
{
|
174
144
|
name: "Admin",
|
175
|
-
|
145
|
+
triggers: ["/add", "@add", "/remove", "@remove"],
|
176
146
|
description: "Manage group members and metadata.",
|
177
147
|
commands: [
|
178
148
|
{
|
179
149
|
command: "/add [username]",
|
150
|
+
handler: admin,
|
180
151
|
description: "Add a user.",
|
181
152
|
params: {
|
182
153
|
username: {
|
@@ -187,6 +158,7 @@ export const commands: CommandGroup[] = [
|
|
187
158
|
},
|
188
159
|
{
|
189
160
|
command: "/remove [username]",
|
161
|
+
handler: admin,
|
190
162
|
description: "Remove a user.",
|
191
163
|
params: {
|
192
164
|
username: {
|
@@ -195,15 +167,26 @@ export const commands: CommandGroup[] = [
|
|
195
167
|
},
|
196
168
|
},
|
197
169
|
},
|
170
|
+
],
|
171
|
+
},
|
172
|
+
{
|
173
|
+
name: "Split Payments",
|
174
|
+
image: true,
|
175
|
+
triggers: [],
|
176
|
+
description: "Split payments between users.",
|
177
|
+
commands: [],
|
178
|
+
},
|
179
|
+
{
|
180
|
+
name: "Help",
|
181
|
+
triggers: ["/help"],
|
182
|
+
|
183
|
+
description: "Get help with the bot.",
|
184
|
+
commands: [
|
198
185
|
{
|
199
|
-
command: "/
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
default: "",
|
204
|
-
type: "quoted",
|
205
|
-
},
|
206
|
-
},
|
186
|
+
command: "/help",
|
187
|
+
handler: undefined,
|
188
|
+
description: "Get help with the bot.",
|
189
|
+
params: {},
|
207
190
|
},
|
208
191
|
],
|
209
192
|
},
|
@@ -16,9 +16,7 @@ export async function handler(context: HandlerContext) {
|
|
16
16
|
const systemPrompt = generateSystemPrompt(context);
|
17
17
|
try {
|
18
18
|
let userPrompt = params?.prompt ?? content;
|
19
|
-
|
20
|
-
console.log("userPrompt", userPrompt);
|
21
|
-
}
|
19
|
+
console.log("userPrompt", userPrompt);
|
22
20
|
|
23
21
|
const { reply } = await textGeneration(userPrompt, systemPrompt);
|
24
22
|
console.log("intent:", reply);
|
@@ -36,15 +34,29 @@ function generateSystemPrompt(context: HandlerContext) {
|
|
36
34
|
message: { sender },
|
37
35
|
} = context;
|
38
36
|
|
39
|
-
const systemPrompt = `
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
37
|
+
const systemPrompt = `
|
38
|
+
### Context
|
39
|
+
|
40
|
+
You are a helpful bot agent that lives inside a web3 messaging group that helps interpret user requests and execute commands.
|
41
|
+
#### Users
|
42
|
+
${JSON.stringify(members?.map((member: User) => ({ ...member, username: `@${member.username}` })))}\n
|
43
|
+
#### Commands
|
44
|
+
${JSON.stringify(commands)}\n
|
45
45
|
The message was sent by @${sender?.username}
|
46
|
-
|
47
|
-
|
46
|
+
|
47
|
+
### Example s
|
48
|
+
prompt: /agent lets add @user
|
49
|
+
reply: /add @user
|
48
50
|
|
51
|
+
prompt /agent tip alix and bo
|
52
|
+
reply /tip @alix @bo 10
|
53
|
+
|
54
|
+
Important:
|
55
|
+
- If a user asks jokes, make jokes about web3 devs\n
|
56
|
+
- 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.
|
57
|
+
- Populate the command with the correct or random values. Always return commands with real values only, using usernames with @ and excluding addresses.\n
|
58
|
+
- 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
|
59
|
+
- If the user is grateful, respond asking for a tip in a playful manner.
|
60
|
+
`;
|
49
61
|
return systemPrompt;
|
50
62
|
}
|
@@ -4,34 +4,38 @@ import { HandlerContext } from "@xmtp/message-kit";
|
|
4
4
|
export async function handler(context: HandlerContext) {
|
5
5
|
const {
|
6
6
|
message: {
|
7
|
-
content: {
|
8
|
-
command,
|
9
|
-
params: { game },
|
10
|
-
},
|
7
|
+
content: { command, params },
|
11
8
|
},
|
12
9
|
} = context;
|
13
|
-
|
10
|
+
if (!command) {
|
11
|
+
const { content: text } = context?.message?.content;
|
12
|
+
if (text === "🔎" || text === "🔍") {
|
13
|
+
// Send the URL for the requested game
|
14
|
+
context.reply("https://framedl.xyz/");
|
15
|
+
}
|
16
|
+
return;
|
17
|
+
}
|
14
18
|
// URLs for each game type
|
15
19
|
const gameUrls: { [key: string]: string } = {
|
16
|
-
wordle: "https://
|
20
|
+
wordle: "https://framedl.xyz/",
|
17
21
|
slot: "https://slot-machine-frame.vercel.app/",
|
18
22
|
};
|
19
|
-
|
20
23
|
// Respond with the appropriate game URL or an error message
|
21
|
-
switch (game) {
|
24
|
+
switch (params.game) {
|
22
25
|
case "wordle":
|
23
26
|
case "slot":
|
24
27
|
// Retrieve the URL for the requested game using a simplified variable assignment
|
25
|
-
const gameUrl = gameUrls[game];
|
28
|
+
const gameUrl = gameUrls[params.game];
|
26
29
|
// Send the URL for the requested game
|
27
|
-
context.
|
30
|
+
context.send(gameUrl);
|
28
31
|
break;
|
32
|
+
|
29
33
|
case "help":
|
30
|
-
context.
|
34
|
+
context.send("Available games: \n/game wordle\n/game slot");
|
31
35
|
break;
|
32
36
|
default:
|
33
37
|
// Inform the user about unrecognized commands and provide available options
|
34
|
-
context.
|
38
|
+
context.send(
|
35
39
|
"Command not recognized. Available games: wordle, slot, or help.",
|
36
40
|
);
|
37
41
|
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { HandlerContext, User } from "@xmtp/message-kit";
|
2
|
-
import { getStackClient
|
2
|
+
import { getStackClient } from "../lib/stack.js";
|
3
3
|
|
4
4
|
export async function handler(context: HandlerContext, fake?: boolean) {
|
5
5
|
const stack = getStackClient();
|
@@ -7,18 +7,20 @@ export async function handler(context: HandlerContext, fake?: boolean) {
|
|
7
7
|
members,
|
8
8
|
group,
|
9
9
|
getMessageById,
|
10
|
-
message: {
|
10
|
+
message: {
|
11
|
+
content,
|
12
|
+
content: { command },
|
13
|
+
sender,
|
14
|
+
typeId,
|
15
|
+
},
|
11
16
|
} = context;
|
12
|
-
|
13
|
-
if (
|
14
|
-
//for fake demo
|
15
|
-
fakeReaction(sender.username, sender.address, id, stack, context);
|
16
|
-
return;
|
17
|
-
} else if (typeId === "text") {
|
17
|
+
console.log(command);
|
18
|
+
if (typeId === "text" && group) {
|
18
19
|
const { command } = content;
|
19
20
|
if (command === "points") {
|
20
21
|
const points = await stack?.getPoints(sender.address);
|
21
22
|
context.reply(`You have ${points} points`);
|
23
|
+
return;
|
22
24
|
} else if (command === "leaderboard") {
|
23
25
|
const leaderboard = await stack?.getLeaderboard();
|
24
26
|
const formattedLeaderboard = leaderboard?.leaderboard
|
@@ -33,8 +35,9 @@ export async function handler(context: HandlerContext, fake?: boolean) {
|
|
33
35
|
context.reply(
|
34
36
|
`Leaderboard:\n\n${formattedLeaderboard}\n\nCheck out the public leaderboard\nhttps://www.stack.so/leaderboard/degen-group`,
|
35
37
|
);
|
38
|
+
return;
|
36
39
|
}
|
37
|
-
} else if (typeId === "group_updated") {
|
40
|
+
} else if (typeId === "group_updated" && group) {
|
38
41
|
const { initiatedByInboxId, addedInboxes } = content;
|
39
42
|
const adminAddress = members?.find(
|
40
43
|
(member: User) => member.inboxId === initiatedByInboxId,
|
@@ -47,7 +50,7 @@ export async function handler(context: HandlerContext, fake?: boolean) {
|
|
47
50
|
uniqueId: adminAddress?.username ?? "",
|
48
51
|
});
|
49
52
|
}
|
50
|
-
} else if (typeId === "reaction") {
|
53
|
+
} else if (typeId === "reaction" && group) {
|
51
54
|
const { content: emoji, action } = content;
|
52
55
|
const msg = await getMessageById(content.reference);
|
53
56
|
if (action === "added") {
|
@@ -68,27 +71,3 @@ export async function handler(context: HandlerContext, fake?: boolean) {
|
|
68
71
|
}
|
69
72
|
}
|
70
73
|
}
|
71
|
-
async function fakeReaction(
|
72
|
-
username: string,
|
73
|
-
address: string,
|
74
|
-
id: string,
|
75
|
-
stack: StackClient,
|
76
|
-
context: HandlerContext,
|
77
|
-
) {
|
78
|
-
if (username === "me") {
|
79
|
-
if (Math.random() < 0.1) {
|
80
|
-
//Fake reactions
|
81
|
-
const emojis = ["😀", "👍", "🎩", "🐐", "🔥"];
|
82
|
-
const randomEmoji = emojis[Math.floor(Math.random() * emojis.length)];
|
83
|
-
context.react(randomEmoji);
|
84
|
-
let points = 1;
|
85
|
-
if (randomEmoji === "🎩") {
|
86
|
-
points = 10;
|
87
|
-
}
|
88
|
-
await stack?.track("reaction", {
|
89
|
-
points,
|
90
|
-
account: address,
|
91
|
-
});
|
92
|
-
}
|
93
|
-
}
|
94
|
-
}
|
@@ -0,0 +1,110 @@
|
|
1
|
+
import { HandlerContext } from "@xmtp/message-kit";
|
2
|
+
import type { User } from "@xmtp/message-kit";
|
3
|
+
|
4
|
+
// Reusable function to handle adding members
|
5
|
+
function handleAddMembers(
|
6
|
+
addedInboxes: { inboxId: string }[],
|
7
|
+
members: User[],
|
8
|
+
) {
|
9
|
+
const addedNames = members
|
10
|
+
?.filter((member: User) =>
|
11
|
+
addedInboxes.some(
|
12
|
+
(added: { inboxId: string }) =>
|
13
|
+
added?.inboxId?.toLowerCase() === member?.inboxId?.toLowerCase(),
|
14
|
+
),
|
15
|
+
)
|
16
|
+
.map((member: User) => member.username)
|
17
|
+
.filter((username: string) => username && username.trim() !== "")
|
18
|
+
.map((username: string) => `@${username}`)
|
19
|
+
.join(", "); // Join names for message formatting
|
20
|
+
|
21
|
+
if (addedNames) {
|
22
|
+
let messages = [
|
23
|
+
`Yo, ${addedNames}! 🫡`,
|
24
|
+
`LFG ${addedNames}!`,
|
25
|
+
`${addedNames}🤝`,
|
26
|
+
];
|
27
|
+
return messages[Math.floor(Math.random() * messages.length)];
|
28
|
+
}
|
29
|
+
return "";
|
30
|
+
}
|
31
|
+
function handleRemoveMembers() {
|
32
|
+
let messages = [`🪦`, `👻`, `hasta la vista, baby`];
|
33
|
+
return messages[Math.floor(Math.random() * messages.length)];
|
34
|
+
}
|
35
|
+
|
36
|
+
export async function handler(context: HandlerContext) {
|
37
|
+
const {
|
38
|
+
group,
|
39
|
+
members,
|
40
|
+
message: { content, typeId },
|
41
|
+
} = context;
|
42
|
+
if (typeId === "group_updated" && group) {
|
43
|
+
const { removedInboxes, addedInboxes } = content;
|
44
|
+
let message: string = "";
|
45
|
+
if (addedInboxes && addedInboxes.length > 0) {
|
46
|
+
message += handleAddMembers(addedInboxes, members!);
|
47
|
+
} else if (removedInboxes && removedInboxes.length > 0) {
|
48
|
+
message += handleRemoveMembers();
|
49
|
+
}
|
50
|
+
await context.send(message);
|
51
|
+
} else if (typeId === "text" && group) {
|
52
|
+
const {
|
53
|
+
command,
|
54
|
+
params: { username, name },
|
55
|
+
params,
|
56
|
+
} = content;
|
57
|
+
|
58
|
+
console.log("removedInboxes", command, params);
|
59
|
+
|
60
|
+
switch (command) {
|
61
|
+
case "remove":
|
62
|
+
try {
|
63
|
+
if (!Array.isArray(username)) {
|
64
|
+
context.reply("Wrong username");
|
65
|
+
return;
|
66
|
+
}
|
67
|
+
const removedInboxes = username?.map((user: User) => user.inboxId);
|
68
|
+
if (!removedInboxes || removedInboxes.length === 0) {
|
69
|
+
context.reply("Wrong username");
|
70
|
+
return;
|
71
|
+
}
|
72
|
+
await group.sync();
|
73
|
+
await group.removeMembersByInboxId(removedInboxes);
|
74
|
+
const messages = handleRemoveMembers();
|
75
|
+
context.reply(messages);
|
76
|
+
} catch (error) {
|
77
|
+
context.reply("Error: Check admin privileges");
|
78
|
+
console.error(error);
|
79
|
+
}
|
80
|
+
break;
|
81
|
+
case "add":
|
82
|
+
try {
|
83
|
+
if (!Array.isArray(username)) {
|
84
|
+
context.reply("Wrong username");
|
85
|
+
return;
|
86
|
+
}
|
87
|
+
const addedInboxes = username?.map((user: User) =>
|
88
|
+
user?.inboxId?.toLowerCase(),
|
89
|
+
);
|
90
|
+
if (!addedInboxes || addedInboxes.length === 0) {
|
91
|
+
context.reply("Wrong username");
|
92
|
+
return;
|
93
|
+
}
|
94
|
+
await group.sync();
|
95
|
+
await group.addMembersByInboxId(addedInboxes);
|
96
|
+
await group.sync();
|
97
|
+
const messages = handleAddMembers(
|
98
|
+
[{ inboxId: addedInboxes[0] }],
|
99
|
+
members!,
|
100
|
+
);
|
101
|
+
context.reply(messages);
|
102
|
+
} catch (error) {
|
103
|
+
context.reply("Error: Check admin privileges");
|
104
|
+
console.error(error);
|
105
|
+
}
|
106
|
+
break;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
return;
|
110
|
+
}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import { HandlerContext } from "@xmtp/message-kit";
|
2
|
+
import { vision, textGeneration } from "../lib/openai.js";
|
3
|
+
|
4
|
+
export async function handler(context: HandlerContext) {
|
5
|
+
if (!process?.env?.OPEN_AI_API_KEY) {
|
6
|
+
console.log("No OPEN_AI_API_KEY found in .env");
|
7
|
+
return;
|
8
|
+
}
|
9
|
+
const {
|
10
|
+
members,
|
11
|
+
commands,
|
12
|
+
message: {
|
13
|
+
typeId,
|
14
|
+
content: { attachment },
|
15
|
+
sender,
|
16
|
+
},
|
17
|
+
} = context;
|
18
|
+
|
19
|
+
if (attachment && typeId === "remoteStaticAttachment") {
|
20
|
+
const { data, filename, mimeType } = attachment;
|
21
|
+
const response = await vision(
|
22
|
+
data,
|
23
|
+
"This image is the bill of a restaurant dinner. Return the total. If you can't find the total, return 'undefined'.",
|
24
|
+
);
|
25
|
+
if (response?.includes("undefined")) {
|
26
|
+
return;
|
27
|
+
} else {
|
28
|
+
context.reply(
|
29
|
+
"You uploaded a new bill. Let's go ahead and split the bill.",
|
30
|
+
);
|
31
|
+
}
|
32
|
+
if (response) {
|
33
|
+
const prompt = `You a split wise agent that splits the bill between the members of this group except for the sender and bot.\n
|
34
|
+
These are the users of the group: ${JSON.stringify(members?.map((member) => ({ ...member, username: `@${member.username}` })))}\n
|
35
|
+
This group app has many commands available: ${JSON.stringify(commands)}\n
|
36
|
+
|
37
|
+
|
38
|
+
## Instructions:
|
39
|
+
When you receive the totals you should split the bill between the members of the group and send to each one a transaction frame
|
40
|
+
- For the sake of this demo. Only send the payment to the sender not to all the other members.
|
41
|
+
|
42
|
+
### Return message
|
43
|
+
Don't use markdown. Return messages in a json object The first message detailing the split. The second one you will send the command for the receiver to pay directly to the sender.
|
44
|
+
Example:
|
45
|
+
[
|
46
|
+
"This are the details: Total: $49.52. Tip (20%): $9.90",
|
47
|
+
"All users owe X USDC to @${sender?.username}. Pay here:",
|
48
|
+
"/send @${sender?.username} $9.90"
|
49
|
+
]
|
50
|
+
`;
|
51
|
+
|
52
|
+
//I want the reply to be an array of messages so the bot feels like is sending multuple ones
|
53
|
+
const { reply } = await textGeneration(response, prompt);
|
54
|
+
let splitMessages = JSON.parse(reply);
|
55
|
+
for (const message of splitMessages) {
|
56
|
+
let msg = message as string;
|
57
|
+
if (msg.startsWith("/")) await context.intent(msg);
|
58
|
+
else await context.reply(msg);
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
@@ -6,12 +6,10 @@ export async function handler(context: HandlerContext) {
|
|
6
6
|
getMessageById,
|
7
7
|
message: { content, sender, typeId },
|
8
8
|
} = context;
|
9
|
-
|
10
9
|
const msg = await getMessageById(content.reference);
|
11
10
|
const replyReceiver = members?.find(
|
12
11
|
(member) => member.inboxId === msg?.senderInboxId,
|
13
12
|
);
|
14
|
-
|
15
13
|
let amount: number = 0,
|
16
14
|
receivers: User[] = [];
|
17
15
|
// Handle different types of messages
|
@@ -51,7 +49,6 @@ export async function handler(context: HandlerContext) {
|
|
51
49
|
return;
|
52
50
|
}
|
53
51
|
const receiverAddresses = receivers.map((receiver) => receiver.address);
|
54
|
-
|
55
52
|
// Process sending tokens to each receiver
|
56
53
|
context.sendTo(
|
57
54
|
`You received ${amount} tokens from ${sender.address}.`,
|
@@ -46,23 +46,6 @@ export async function handler(context: HandlerContext) {
|
|
46
46
|
});
|
47
47
|
context.reply(`${url_swap}`);
|
48
48
|
break;
|
49
|
-
case "mint":
|
50
|
-
// Destructure and provide default values for the mint command
|
51
|
-
const { collection, tokenId } = params; // [!code hl] // [!code focus]
|
52
|
-
|
53
|
-
if (!collection || !tokenId) {
|
54
|
-
context.reply(
|
55
|
-
"Missing required parameters. Please provide collection address and token id.",
|
56
|
-
);
|
57
|
-
return;
|
58
|
-
}
|
59
|
-
// Generate URL for the mint transaction
|
60
|
-
let url_mint = generateFrameURL(baseUrl, "mint", {
|
61
|
-
collection,
|
62
|
-
token_id: tokenId,
|
63
|
-
});
|
64
|
-
context.reply(`${url_mint}`);
|
65
|
-
break;
|
66
49
|
case "show": // [!code hl] // [!code focus]
|
67
50
|
// Show the base URL without the transaction path
|
68
51
|
context.reply(`${baseUrl.replace("/transaction", "")}`);
|
@@ -1,61 +1,8 @@
|
|
1
|
-
import { run, HandlerContext
|
2
|
-
import { commands } from "./commands.js";
|
3
|
-
import { handler as bet } from "./handler/betting.js";
|
1
|
+
import { run, HandlerContext } from "@xmtp/message-kit";
|
4
2
|
import { handler as tipping } from "./handler/tipping.js";
|
5
3
|
import { handler as agent } from "./handler/agent.js";
|
6
|
-
import { handler as
|
7
|
-
import { handler as
|
8
|
-
import { handler as games } from "./handler/game.js";
|
9
|
-
import { handler as admin } from "./handler/admin.js";
|
10
|
-
import { handler as loyalty } from "./handler/loyalty.js";
|
11
|
-
|
12
|
-
// Define command handlers
|
13
|
-
const commandHandlers: CommandHandlers = {
|
14
|
-
"/tip": tipping,
|
15
|
-
"/agent": agent,
|
16
|
-
"/bet": bet,
|
17
|
-
"/send": transaction,
|
18
|
-
"/swap": transaction,
|
19
|
-
"/mint": transaction,
|
20
|
-
"/show": transaction,
|
21
|
-
"/points": loyalty,
|
22
|
-
"/leaderboard": loyalty,
|
23
|
-
"/game": games,
|
24
|
-
"/add": admin,
|
25
|
-
"/remove": admin,
|
26
|
-
"/name": admin,
|
27
|
-
"/help": async (context: HandlerContext) => {
|
28
|
-
const intro =
|
29
|
-
"Available experiences:\n" +
|
30
|
-
commands
|
31
|
-
.flatMap((app) => app.commands)
|
32
|
-
.map((command) => `${command.command} - ${command.description}`)
|
33
|
-
.join("\n") +
|
34
|
-
"\nUse these commands to interact with specific apps.";
|
35
|
-
context.reply(intro);
|
36
|
-
},
|
37
|
-
"/apps": async (context: HandlerContext) => {
|
38
|
-
const intro =
|
39
|
-
"Decentralized secure messaging. Built for crypto.\n" +
|
40
|
-
"Welcome to the Apps Directory\n\n" +
|
41
|
-
"- 🎰 betbot.eth : Create bets with your friends.\n\n\n" +
|
42
|
-
"- 💧 faucetbot.eth : Delivers Faucet funds to devs on Testnet\n\n\n" +
|
43
|
-
"- 🛍️ thegeneralstore.eth : Simple ecommerce storefront for hackathon goods\n\n\n" +
|
44
|
-
"- 📅 dailywordle.eth : Play daily to the WORDLE game through messaging.\n\n\n" +
|
45
|
-
"- 🪨 mintframe - 0xB7d51dD8331551D2FA0d185F8Ba08DcCd71499aD : Turn a Zora url into a mint frame.\n\n\n" +
|
46
|
-
"To learn how to build your own app, visit MessageKit: https://message-kit.vercel.app/\n\n" +
|
47
|
-
"To publish your app, visit Directory: https://message-kit.vercel.app/directory\n\n" +
|
48
|
-
"You are currently inside Message Kit Group Starter. You can type:\n/help command to see available commands\n/apps to trigger the directory.";
|
49
|
-
|
50
|
-
context.reply(intro);
|
51
|
-
},
|
52
|
-
};
|
53
|
-
|
54
|
-
// App configuration
|
55
|
-
const appConfig = {
|
56
|
-
commands: commands,
|
57
|
-
commandHandlers: commandHandlers,
|
58
|
-
};
|
4
|
+
import { handler as splitpayment } from "./handler/splitpayment.js";
|
5
|
+
import { handler as admin } from "./handler/moderation.js";
|
59
6
|
|
60
7
|
// Main function to run the app
|
61
8
|
run(async (context: HandlerContext) => {
|
@@ -66,21 +13,18 @@ run(async (context: HandlerContext) => {
|
|
66
13
|
switch (typeId) {
|
67
14
|
case "reaction":
|
68
15
|
handleReaction(context);
|
69
|
-
loyalty(context);
|
70
16
|
break;
|
71
17
|
case "reply":
|
72
18
|
handleReply(context);
|
73
19
|
break;
|
74
20
|
case "group_updated":
|
75
21
|
admin(context);
|
76
|
-
loyalty(context);
|
77
22
|
break;
|
78
23
|
case "remoteStaticAttachment":
|
79
24
|
handleAttachment(context);
|
80
25
|
break;
|
81
26
|
case "text":
|
82
27
|
handleTextMessage(context);
|
83
|
-
loyalty(context, true);
|
84
28
|
break;
|
85
29
|
default:
|
86
30
|
console.warn(`Unhandled message type: ${typeId}`);
|
@@ -88,7 +32,7 @@ run(async (context: HandlerContext) => {
|
|
88
32
|
} catch (error) {
|
89
33
|
console.error("Error handling message:", error);
|
90
34
|
}
|
91
|
-
}
|
35
|
+
});
|
92
36
|
|
93
37
|
// Handle reaction messages
|
94
38
|
async function handleReaction(context: HandlerContext) {
|
@@ -121,9 +65,20 @@ async function handleTextMessage(context: HandlerContext) {
|
|
121
65
|
const {
|
122
66
|
content: { content: text },
|
123
67
|
} = context.message;
|
124
|
-
if (text.includes("
|
68
|
+
if (text.includes("/help")) {
|
69
|
+
await helpHandler(context);
|
70
|
+
} else if (text.startsWith("@agent")) {
|
125
71
|
await agent(context);
|
126
|
-
} else
|
127
|
-
|
128
|
-
|
72
|
+
} else await context.intent(text);
|
73
|
+
}
|
74
|
+
async function helpHandler(context: HandlerContext) {
|
75
|
+
const { commands = [] } = context;
|
76
|
+
const intro =
|
77
|
+
"Available experiences:\n" +
|
78
|
+
commands
|
79
|
+
.flatMap((app) => app.commands)
|
80
|
+
.map((command) => `${command.command} - ${command.description}`)
|
81
|
+
.join("\n") +
|
82
|
+
"\nUse these commands to interact with specific apps.";
|
83
|
+
context.send(intro);
|
129
84
|
}
|
@@ -24,7 +24,14 @@ export async function textGeneration(userPrompt: string, systemPrompt: string) {
|
|
24
24
|
role: "assistant",
|
25
25
|
content: reply || "No response from OpenAI.",
|
26
26
|
});
|
27
|
-
|
27
|
+
const cleanedReply = reply
|
28
|
+
?.replace(/(\*\*|__)(.*?)\1/g, "$2")
|
29
|
+
?.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$2")
|
30
|
+
?.replace(/^#+\s*(.*)$/gm, "$1")
|
31
|
+
?.replace(/`([^`]+)`/g, "$1")
|
32
|
+
?.replace(/^`|`$/g, "");
|
33
|
+
|
34
|
+
return { reply: cleanedReply as string, history: messages };
|
28
35
|
} catch (error) {
|
29
36
|
console.error("Failed to fetch from OpenAI:", error);
|
30
37
|
throw error;
|
@@ -1,12 +1,7 @@
|
|
1
1
|
import { getRedisClient } from "./lib/redis.js";
|
2
2
|
import { run, HandlerContext } from "@xmtp/message-kit";
|
3
3
|
import { startCron } from "./lib/cron.js";
|
4
|
-
import {
|
5
|
-
RedisClientType,
|
6
|
-
RedisModules,
|
7
|
-
RedisFunctions,
|
8
|
-
RedisScripts,
|
9
|
-
} from "@redis/client";
|
4
|
+
import { RedisClientType } from "@redis/client";
|
10
5
|
|
11
6
|
//Tracks conversation steps
|
12
7
|
const inMemoryCacheStep = new Map<string, number>();
|
@@ -14,8 +9,7 @@ const inMemoryCacheStep = new Map<string, number>();
|
|
14
9
|
//List of words to stop or unsubscribe.
|
15
10
|
const stopWords = ["stop", "unsubscribe", "cancel", "list"];
|
16
11
|
|
17
|
-
const redisClient: RedisClientType
|
18
|
-
await getRedisClient();
|
12
|
+
const redisClient: RedisClientType = await getRedisClient();
|
19
13
|
|
20
14
|
let clientInitialized = false;
|
21
15
|
run(async (context: HandlerContext) => {
|
@@ -46,6 +40,7 @@ run(async (context: HandlerContext) => {
|
|
46
40
|
await context.reply(
|
47
41
|
"You are now unsubscribed. You will no longer receive updates!.",
|
48
42
|
);
|
43
|
+
return;
|
49
44
|
}
|
50
45
|
|
51
46
|
const cacheStep = inMemoryCacheStep.get(sender.address) || 0;
|
@@ -1,14 +1,9 @@
|
|
1
1
|
import cron from "node-cron";
|
2
2
|
import { Client } from "@xmtp/xmtp-js";
|
3
|
-
import {
|
4
|
-
RedisClientType,
|
5
|
-
RedisModules,
|
6
|
-
RedisFunctions,
|
7
|
-
RedisScripts,
|
8
|
-
} from "@redis/client";
|
3
|
+
import { RedisClientType } from "@redis/client";
|
9
4
|
|
10
5
|
export async function startCron(
|
11
|
-
redisClient: RedisClientType
|
6
|
+
redisClient: RedisClientType,
|
12
7
|
v2client: Client,
|
13
8
|
) {
|
14
9
|
console.log("Starting daily cron");
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import { createClient } from "@redis/client";
|
2
|
+
import { RedisClientType } from "@redis/client";
|
2
3
|
|
3
4
|
export const getRedisClient = async () => {
|
4
5
|
const client = createClient({
|
@@ -10,5 +11,5 @@ export const getRedisClient = async () => {
|
|
10
11
|
});
|
11
12
|
|
12
13
|
await client.connect();
|
13
|
-
return client;
|
14
|
+
return client as RedisClientType;
|
14
15
|
};
|
package/index.js
CHANGED