create-message-kit 1.2.13 → 1.2.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. package/index.js +15 -15
  2. package/package.json +1 -1
  3. package/templates/coinbase-agent/.cursorrules +290 -0
  4. package/templates/coinbase-agent/.env.example +4 -0
  5. package/templates/coinbase-agent/.yarnrc.yml +9 -0
  6. package/templates/coinbase-agent/package.json +22 -0
  7. package/templates/coinbase-agent/src/index.ts +31 -0
  8. package/templates/coinbase-agent/src/plugins/learnweb3.ts +96 -0
  9. package/templates/coinbase-agent/src/plugins/redis.ts +15 -0
  10. package/templates/coinbase-agent/src/prompt.ts +64 -0
  11. package/templates/coinbase-agent/src/skills/drip.ts +83 -0
  12. package/templates/coinbase-agent/src/skills/mint.ts +99 -0
  13. package/templates/coinbase-agent/src/skills/pay.ts +35 -0
  14. package/templates/coinbase-agent/src/skills/swap.ts +52 -0
  15. package/templates/ens/.env.example +1 -0
  16. package/templates/ens/.yarnrc.yml +5 -0
  17. package/templates/ens/src/index.ts +1 -1
  18. package/templates/ens/src/prompt.ts +1 -14
  19. package/templates/faucet/.cursorrules +290 -0
  20. package/templates/faucet/.env.example +5 -0
  21. package/templates/faucet/.yarnrc.yml +9 -0
  22. package/templates/faucet/package.json +22 -0
  23. package/templates/faucet/src/index.ts +39 -0
  24. package/templates/faucet/src/plugins/learnweb3.ts +96 -0
  25. package/templates/faucet/src/plugins/redis.ts +15 -0
  26. package/templates/faucet/src/prompt.ts +11 -0
  27. package/templates/faucet/src/skills/faucet.ts +140 -0
  28. package/templates/gated-group/.cursorrules +290 -0
  29. package/templates/gated-group/.env.example +3 -0
  30. package/templates/gated-group/.yarnrc.yml +9 -0
  31. package/templates/gated-group/package.json +23 -0
  32. package/templates/gated-group/src/index.ts +17 -0
  33. package/templates/gated-group/src/plugins/alchemy.ts +27 -0
  34. package/templates/gated-group/src/plugins/xmtp.ts +137 -0
  35. package/templates/gated-group/src/prompt.ts +11 -0
  36. package/templates/gated-group/src/skills/gated.ts +88 -0
  37. package/templates/gm/.cursorrules +290 -0
  38. package/templates/gm/.env.example +2 -0
  39. package/templates/gm/.yarnrc.yml +9 -0
  40. package/templates/gm/package.json +20 -0
  41. package/templates/gm/src/index.ts +8 -0
  42. package/templates/playground/.cursorrules +290 -0
  43. package/templates/playground/.env.example +6 -0
  44. package/templates/playground/.yarnrc.yml +9 -0
  45. package/templates/playground/package.json +26 -0
  46. package/templates/playground/src/index.ts +40 -0
  47. package/templates/playground/src/plugins/alchemy.ts +27 -0
  48. package/templates/playground/src/plugins/minilog.ts +26 -0
  49. package/templates/playground/src/plugins/usdc.ts +99 -0
  50. package/templates/playground/src/plugins/xmtp.ts +137 -0
  51. package/templates/playground/src/prompt.ts +11 -0
  52. package/templates/playground/src/skills/broadcast.ts +39 -0
  53. package/templates/playground/src/skills/cash.ts +122 -0
  54. package/templates/playground/src/skills/cryptoPrice.ts +63 -0
  55. package/templates/playground/src/skills/dalle.ts +61 -0
  56. package/templates/playground/src/skills/gated.ts +88 -0
  57. package/templates/playground/src/skills/search.ts +159 -0
  58. package/templates/playground/src/skills/todo.ts +79 -0
  59. package/templates/playground/src/skills/token.ts +57 -0
  60. package/templates/playground/src/skills/web.ts +83 -0
  61. package/templates/playground/src/skills/wordle.ts +96 -0
  62. package/templates/simple/.env.example +1 -0
  63. package/templates/simple/.yarnrc.yml +5 -0
  64. package/templates/simple/src/index.ts +1 -1
  65. package/templates/simple/src/prompt.ts +2 -0
  66. package/templates/thegeneralstore/.cursorrules +290 -0
  67. package/templates/thegeneralstore/.env.example +9 -0
  68. package/templates/thegeneralstore/.yarnrc.yml +9 -0
  69. package/templates/thegeneralstore/package.json +24 -0
  70. package/templates/thegeneralstore/src/data/db.json +812 -0
  71. package/templates/thegeneralstore/src/index.ts +37 -0
  72. package/templates/thegeneralstore/src/plugins/learnweb3.ts +96 -0
  73. package/templates/thegeneralstore/src/plugins/lowdb.ts +11 -0
  74. package/templates/thegeneralstore/src/plugins/notion.ts +89 -0
  75. package/templates/thegeneralstore/src/plugins/redis.ts +15 -0
  76. package/templates/thegeneralstore/src/prompt.md +51 -0
  77. package/templates/thegeneralstore/src/prompt.ts +3 -0
  78. package/templates/thegeneralstore/src/skills/faucet.ts +114 -0
  79. package/templates/thegeneralstore/src/skills/notion.ts +17 -0
  80. package/templates/thegeneralstore/src/skills/poap.ts +48 -0
  81. package/templates/thegeneralstore/src/skills.ts +37 -0
  82. package/templates/toss/.cursorrules +290 -0
  83. package/templates/toss/.env.example +7 -0
  84. package/templates/toss/.yarnrc.yml +9 -0
  85. package/templates/toss/package.json +21 -0
  86. package/templates/toss/src/index.ts +33 -0
  87. package/templates/toss/src/plugins/helpers.ts +185 -0
  88. package/templates/toss/src/plugins/redis.ts +78 -0
  89. package/templates/toss/src/prompt.ts +54 -0
  90. package/templates/toss/src/skills/toss.ts +489 -0
@@ -0,0 +1,290 @@
1
+ # MessageKit Skill Template
2
+
3
+ ## Examples
4
+
5
+ ### Check if a Domain is Available
6
+
7
+
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",
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
+
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",
65
+ examples: [
66
+ "/pay 10 vitalik.eth",
67
+ "/pay 1 usdc to 0xC60E6Bb79322392761BFe3081E302aEB79B30B03",
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
+ ## Types
117
+
118
+ import { XMTPContext } from "../plugins/xmtp.js";
119
+ import { ClientOptions, GroupMember } from "@xmtp/node-sdk";
120
+ import { ContentTypeId } from "@xmtp/content-type-primitives";
121
+
122
+ export type MessageAbstracted = {
123
+ id: string;
124
+ sent: Date;
125
+ content: {
126
+ text?: string | undefined;
127
+ reply?: string | undefined;
128
+ previousMsg?: string | undefined;
129
+ react?: string | undefined;
130
+ content?: any | undefined;
131
+ params?: any | undefined;
132
+ reference?: string | undefined;
133
+ skill?: string | undefined;
134
+ };
135
+ version: "v2" | "v3";
136
+ sender: AbstractedMember;
137
+ typeId: string;
138
+ };
139
+ export type GroupAbstracted = {
140
+ id: string;
141
+ sync: () => Promise<void>;
142
+ addMembers: (addresses: string[]) => Promise<void>;
143
+ addMembersByInboxId: (inboxIds: string[]) => Promise<void>;
144
+ send: (content: string, contentType?: ContentTypeId) => Promise<string>;
145
+ isAdmin: (inboxId: string) => boolean;
146
+ isSuperAdmin: (inboxId: string) => boolean;
147
+ admins: string[];
148
+ superAdmins: string[];
149
+ createdAt: Date;
150
+ members: GroupMember[];
151
+ };
152
+ export type SkillResponse = {
153
+ code: number;
154
+ message: string;
155
+ data?: any;
156
+ };
157
+
158
+ export type SkillHandler = (
159
+ context: XMTPContext,
160
+ ) => Promise<SkillResponse | void>;
161
+
162
+ export type Handler = (context: XMTPContext) => Promise<void>;
163
+
164
+ export type RunConfig = {
165
+ // client options from XMTP client
166
+ client?: ClientOptions;
167
+ // private key to be used for the client, if not, default from env
168
+ privateKey?: string;
169
+ // if true, the init log message with messagekit logo and stuff will be hidden
170
+ experimental?: boolean;
171
+ // hide the init log message with messagekit logo and stuff
172
+ hideInitLogMessage?: boolean;
173
+ // if true, attachments will be enabled
174
+ attachments?: boolean;
175
+ // if true, member changes will be enabled, like adding members to the group
176
+ memberChange?: boolean;
177
+ // skills to be used
178
+ agent?: Agent;
179
+ // model to be used
180
+ gptModel?: string;
181
+ };
182
+ export interface SkillParamConfig {
183
+ default?: string | number | boolean;
184
+ type:
185
+ | "number"
186
+ | "string"
187
+ | "username"
188
+ | "quoted"
189
+ | "address"
190
+ | "prompt"
191
+ | "url";
192
+ plural?: boolean;
193
+ values?: string[]; // Accepted values for the parameter
194
+ }
195
+
196
+ export interface Frame {
197
+ title: string;
198
+ buttons: { content: string; action: string; target: string }[];
199
+ image: string;
200
+ }
201
+ export interface Agent {
202
+ name: string;
203
+ description: string;
204
+ tag: string;
205
+ skills: Skill[];
206
+ }
207
+ export interface Skill {
208
+ skill: string;
209
+ handler?: SkillHandler | undefined;
210
+ adminOnly?: boolean;
211
+ description: string;
212
+ examples: string[];
213
+ params: Record<string, SkillParamConfig>;
214
+ }
215
+
216
+ export interface AbstractedMember {
217
+ inboxId: string;
218
+ address: string;
219
+ accountAddresses: string[];
220
+ installationIds?: string[];
221
+ }
222
+
223
+ export type MetadataValue = string | number | boolean;
224
+ export type Metadata = Record<string, MetadataValue | MetadataValue[]>;
225
+
226
+
227
+ ## Example final prompt
228
+
229
+ Your are a helpful and playful ens agent called @bot that lives inside a messaging app called Converse.
230
+
231
+
232
+ # Rules
233
+ - You can respond with multiple messages if needed. Each message should be separated by a newline character.
234
+ - You can trigger skills by only sending the command in a newline message.
235
+ - Each command starts with a slash (/).
236
+ - Never announce actions without using a command separated by a newline character.
237
+ - Never use markdown in your responses.
238
+ - Do not make guesses or assumptions
239
+ - Only answer if the verified information is in the prompt.
240
+ - Check that you are not missing a command
241
+ - Focus only on helping users with operations detailed below.
242
+ - Date: Fri, 06 Dec 2024 16:03:22 GMT
243
+ - When mentioning any action related to available skills, you MUST trigger the corresponding command in a new line
244
+ - If you suggest an action that has a command, you must trigger that command
245
+
246
+
247
+ ## User context
248
+ - Start by fetch their domain from or Converse username
249
+ - Call the user by their name or domain, in case they have one
250
+ - Ask for a name (if they don't have one) so you can suggest domains.
251
+ - Message sent date: 2024-12-06T16:03:36.582Z
252
+ - Users address is: 0x40f08f0f853d1c42c61815652b7ccd5a50f0be09
253
+ - Users name is: ArizonaOregon
254
+ - Converse username is: ArizonaOregon
255
+
256
+ ## Commands
257
+ /check [domain] - Check if a domain is available.
258
+ /cool [domain] - Get cool alternatives for a .eth domain.
259
+ /info [domain] - Get detailed information about an ENS domain including owner, expiry date, and resolver.
260
+ /register [domain] - Register a new ENS domain. Returns a URL to complete the registration process.
261
+ /renew [domain] - Extend the registration period of your ENS domain. Returns a URL to complete the renewal.
262
+ /reset - Reset the conversation clearing memory and usernames cache.
263
+ /pay [amount] [token] [username] [address] - Send a specified amount of a cryptocurrency to a destination address.
264
+ When tipping, you can asume its 1 usdc.
265
+
266
+ ## Examples
267
+ /check vitalik.eth
268
+ /check fabri.base.eth
269
+ /cool vitalik.eth
270
+ /info nick.eth
271
+ /register vitalik.eth
272
+ /renew fabri.base.eth
273
+ /reset
274
+ /pay 10 vitalik.eth
275
+ /pay 1 usdc to 0xC60E6Bb79322392761BFe3081E302aEB79B30B03
276
+
277
+ ## Scenarios
278
+ 1. Missing commands in responses
279
+ **Issue**: Sometimes responses are sent without the required command.
280
+ **Example**:
281
+ Incorrect:
282
+ > "Looks like vitalik.eth is registered! What about these cool alternatives?"
283
+ Correct:
284
+ > "Looks like vitalik.eth is registered! What about these cool alternatives?
285
+ > /cool vitalik.eth"
286
+
287
+ Incorrect:
288
+ > Here is a summary of your TODOs. I will now send it via email.
289
+ Correct:
290
+ > /todo
@@ -0,0 +1,7 @@
1
+ KEY= # the private key of the wallet
2
+ TEST_ENCRYPTION_KEY= # a different private key for encryption
3
+ OPENAI_API_KEY= # the API key for OpenAI
4
+ REDIS_CONNECTION_STRING= # the connection to the toss database in Redis
5
+ COINBASE_API_REDIS_URL= # the API key for OpenAI
6
+ COINBASE_API_KEY_NAME= # the API key name for Coinbase
7
+ COINBASE_API_KEY_PRIVATE_KEY= # the private key for Coinbase
@@ -0,0 +1,9 @@
1
+ compressionLevel: mixed
2
+
3
+ enableGlobalCache: false
4
+
5
+ enableTelemetry: false
6
+
7
+ nodeLinker: node-modules
8
+
9
+ yarnPath: .yarn/releases/yarn-4.5.1.cjs
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "toss",
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
+ "@redis/client": "^1.6.0",
12
+ "@xmtp/message-kit": "workspace:*"
13
+ },
14
+ "devDependencies": {
15
+ "@types/node": "^20.14.2",
16
+ "typescript": "^5.4.5"
17
+ },
18
+ "engines": {
19
+ "node": ">=20"
20
+ }
21
+ }
@@ -0,0 +1,33 @@
1
+ import {
2
+ run,
3
+ agentReply,
4
+ replaceVariables,
5
+ XMTPContext,
6
+ Agent,
7
+ } from "@xmtp/message-kit";
8
+
9
+ import { systemPrompt } from "./prompt.js";
10
+ import { toss } from "./skills/toss.js";
11
+ import fs from "fs";
12
+ import { getWalletService } from "./plugins/redis.js";
13
+ const walletServiceDB = await getWalletService();
14
+ export const agent: Agent = {
15
+ name: "Toss Bot",
16
+ tag: "@toss",
17
+ description: "Create a coin toss.",
18
+ skills: [...toss],
19
+ };
20
+
21
+ run(
22
+ async (context: XMTPContext) => {
23
+ const {
24
+ message: { sender },
25
+ } = context;
26
+
27
+ let prompt = await replaceVariables(systemPrompt, sender.address, agent);
28
+ //This is only used for to update the docs.
29
+ fs.writeFileSync("example_prompt.md", prompt);
30
+ await agentReply(context, prompt);
31
+ },
32
+ { agent, walletServiceDB },
33
+ );
@@ -0,0 +1,185 @@
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+ import { getRedisClient } from "./redis.js";
3
+
4
+ const tossDBClient = await getRedisClient();
5
+ interface Participant {
6
+ response: string;
7
+ name: string;
8
+ address: string;
9
+ agent_address: string;
10
+ }
11
+ export interface TossData {
12
+ group_id: string;
13
+ admin_name: string;
14
+ admin_address: string;
15
+ options: string;
16
+ toss_id: string;
17
+ amount: number;
18
+ created_at: string;
19
+ end_time: string;
20
+ description: string;
21
+ encrypted_participants: string[];
22
+ participants?: Participant[];
23
+ toss_wallet_address: string;
24
+ }
25
+ export async function checkTossCorrect(
26
+ context: XMTPContext,
27
+ ): Promise<TossData | undefined> {
28
+ const {
29
+ message: {
30
+ content: { previousMsg },
31
+ },
32
+ walletService,
33
+ group,
34
+ } = context;
35
+ if (!group) {
36
+ await context.reply("This command can only be used in a group.");
37
+ return undefined;
38
+ } else if (!previousMsg) {
39
+ await context.reply("You must reply to a toss.");
40
+ return undefined;
41
+ }
42
+
43
+ let toss_id = extractTossId(previousMsg);
44
+ if (!toss_id) {
45
+ await context.reply(
46
+ "Invalid toss ID. Be sure you are replying to a original toss.",
47
+ );
48
+ return undefined;
49
+ }
50
+ let encryptedKey = walletService.encrypt(toss_id);
51
+ const tossDataString = await tossDBClient.get(`toss:${encryptedKey}`);
52
+ const hexString = tossDataString?.replace(/"/g, "");
53
+ let tossData = hexString ? walletService.decrypt(hexString) : null;
54
+
55
+ if (typeof tossData === "string") {
56
+ tossData = JSON.parse(tossData) as TossData;
57
+ }
58
+ console.log("tossData", tossData);
59
+ if (!tossData) {
60
+ await context.reply("Toss not found");
61
+ return undefined;
62
+ } else if (tossData.status === "closed") {
63
+ await context.reply("Toss has already ended.");
64
+ return undefined;
65
+ } else if (tossData.group_id.toLowerCase() !== group.id.toLowerCase()) {
66
+ await context.reply("This toss is not in this group.");
67
+ return undefined;
68
+ }
69
+ tossData.participants = [];
70
+ if (tossData.encrypted_participants?.length) {
71
+ tossData.participants = tossData.encrypted_participants?.map((p: string) =>
72
+ walletService.decrypt(p),
73
+ );
74
+ }
75
+ const pool = tossData.amount * (tossData?.participants?.length || 0);
76
+ return { ...tossData, toss_id, pool };
77
+ }
78
+
79
+ export function extractTossId(message: string): string | null {
80
+ try {
81
+ const match = message.match(/ID:\s*(\d+)/);
82
+ return match ? match[1].toString() : null;
83
+ } catch (error) {
84
+ return null;
85
+ }
86
+ }
87
+
88
+ export async function extractWinners(
89
+ participants: Participant[],
90
+ option: string,
91
+ ): Promise<{
92
+ winners: Participant[];
93
+ losers: Participant[];
94
+ }> {
95
+ let winners: Participant[] = [];
96
+ let losers: Participant[] = [];
97
+
98
+ await Promise.all(
99
+ participants.map(async (participant) => {
100
+ if (participant.response.toLowerCase() === option.toLowerCase()) {
101
+ winners.push(participant);
102
+ } else {
103
+ losers.push(participant);
104
+ }
105
+ }),
106
+ );
107
+ return { winners, losers };
108
+ }
109
+
110
+ export function generateTossMessage(tossData: TossData): string {
111
+ return `Here is your toss!\n
112
+ 🪙 ${tossData.description.toUpperCase()}? - ${tossData.options.toUpperCase()} - $${tossData.amount}\n
113
+ - ID: ${tossData.toss_id}
114
+ - Judge: ${tossData.admin_name}
115
+ - Ends on: ${tossData.end_time}
116
+
117
+ How to toss:\n- The creator of the toss is one who can end or settle the toss. \n- The pool will be split evenly with the winners. \n- Remember, with great power comes great responsibility 💪
118
+
119
+ \n🛠️ Reply with:
120
+ @toss <option>
121
+ @toss end <option> - only the judge can end the toss
122
+ @toss cancel - only the creator can cancel the toss
123
+ @toss status - check the status of the toss
124
+ @toss help - for managing your toss via DMs`;
125
+ }
126
+
127
+ export function generateEndTossMessage(
128
+ winners: { name: string; address: string }[],
129
+ losers: { name: string; address: string }[],
130
+ prize: number,
131
+ ): string {
132
+ if (!winners.length) {
133
+ return `The toss has been closed and no winners were found.`;
134
+ }
135
+ let message = `🏆 Winners have been rewarded! 🏆\n\n🎉 Winners: \n${winners
136
+ .map((winner) => `- ${winner.name} - $${prize} 💰\n`)
137
+ .join("")}`;
138
+ if (losers.length > 0) {
139
+ message += `\n😢 Losers: \n${losers
140
+ .map((loser) => `- ${loser.name} 😢\n`)
141
+ .join("")}`;
142
+ }
143
+ return (
144
+ message +
145
+ `\nThe pool has been distributed among the winners. The toss has been closed now.`
146
+ );
147
+ }
148
+
149
+ export async function generateTossStatusMessage(
150
+ tossData: TossData,
151
+ ): Promise<string> {
152
+ const participants = tossData.participants;
153
+ return `Here are the details:
154
+ - Amount: $${tossData.amount}
155
+ - Description: ${tossData.description}
156
+ - Judge: ${tossData.admin_name}
157
+ - End Time: ${tossData.end_time}
158
+
159
+ 📊 Status:
160
+ 👥 Participants:\n${participants
161
+ ?.map(
162
+ (participant: any) =>
163
+ `- ${participant.name ?? participant.address} - ${participant.response}\n`,
164
+ )
165
+ .join("")}
166
+ 🏦 Pool: $${(tossData?.participants?.length || 0) * tossData.amount}
167
+ 📋 Options:
168
+ ${tossData.options
169
+ .split(",")
170
+ .map((option: string) => {
171
+ const voteCount = participants?.filter(
172
+ (participant: any) =>
173
+ participant.response.toLowerCase() === option.toLowerCase(),
174
+ ).length;
175
+ return `\t- ${option}: ${voteCount} votes`;
176
+ })
177
+ .join("\n")} `;
178
+ }
179
+
180
+ export const DM_HELP_MESSAGE = `Welcome to @toss! I'm your friendly neighbourhood toss bot.
181
+ /fund [amount] - You can fund your account with
182
+ /balance - Check your balance
183
+ /withdraw [amount] - You can withdraw funds to your wallet
184
+ /create - Create an agent wallet
185
+ /help - Get help with tossing`;
@@ -0,0 +1,78 @@
1
+ import { createClient } from "@redis/client";
2
+ import type { RedisClientType } from "@redis/client";
3
+
4
+ let redisClient: RedisClientType | null = null;
5
+
6
+ export const getRedisClient = async () => {
7
+ if (redisClient?.isOpen) {
8
+ return redisClient;
9
+ }
10
+
11
+ if (!process.env.REDIS_CONNECTION_STRING) {
12
+ throw new Error(
13
+ "REDIS_CONNECTION_STRING not found in environment variables",
14
+ );
15
+ }
16
+ const client = createClient({
17
+ url: process.env.REDIS_CONNECTION_STRING,
18
+ });
19
+
20
+ client.on("error", (err) => {
21
+ console.error("Redis client error:", err);
22
+ });
23
+
24
+ await client.connect();
25
+ redisClient = client as RedisClientType;
26
+ return client as RedisClientType;
27
+ };
28
+
29
+ let walletService: RedisClientType | null = null;
30
+
31
+ export const getWalletService = async (): Promise<RedisClientType> => {
32
+ if (walletService?.isOpen) {
33
+ return walletService;
34
+ }
35
+
36
+ if (!process.env.COINBASE_API_REDIS_URL) {
37
+ throw new Error(
38
+ "COINBASE_API_REDIS_URL not found in environment variables",
39
+ );
40
+ }
41
+
42
+ const client = createClient({
43
+ url: process.env.COINBASE_API_REDIS_URL,
44
+ });
45
+
46
+ client.on("error", (error: Error) => {
47
+ console.error("Toss wallet Redis client error:", error);
48
+ });
49
+
50
+ client.on("connect", () => {
51
+ console.log("Connected to Toss Wallet Redis");
52
+ });
53
+ await client.connect();
54
+ walletService = client as RedisClientType;
55
+ return client as RedisClientType;
56
+ };
57
+
58
+ export async function updateField(
59
+ client: RedisClientType,
60
+ key: string,
61
+ updateObject: any,
62
+ ) {
63
+ // Check if the key exists
64
+ const data = await client.get(key);
65
+
66
+ let updatedData;
67
+ if (data) {
68
+ // If the key exists, parse it and merge the updates
69
+ const parsedData = JSON.parse(data);
70
+ updatedData = { ...parsedData, ...updateObject };
71
+ } else {
72
+ // If the key doesn't exist, use the updateObject as the initial value
73
+ updatedData = updateObject;
74
+ }
75
+
76
+ // Save the updated or new data back to Redis
77
+ await client.set(key, JSON.stringify(updatedData));
78
+ }
@@ -0,0 +1,54 @@
1
+ export const systemPrompt = `You are a helpful agent, friendly toss master named @toss, always ready to flip the odds!
2
+ {rules}
3
+
4
+ ## Game rules
5
+ - The token is always USDC. Ignore other tokens and default to usdc. Don't mention the token in the command.
6
+ - Infer the name of the toss from the prompt if it's not provided. It should be a short sentence summarizing the event, never mention the options.
7
+ - Tosses must always have two options. If options are not provided, assume "Yes" and "No."
8
+ - For sports events, ensure the options are the two teams or players, as inferred from the context.
9
+ - If the user provides unclear or incomplete information, infer and generate the correct toss format based on context.
10
+ - Maximum toss amount is 10. Default to 10 if nothing is provided. Minimum is 0.00 and its valid.
11
+ - Don't mention options in the toss name.
12
+ - Remove all emojis from the options.
13
+ - If toss is correct. Don't return anything else than the command. Ever.
14
+ - If the user asks about performing an action and it maps to a command, answer directly with the populated command. Always return commands with real values only.
15
+ - If the user's input doesn't clearly map to a command, respond with helpful information or a clarification question.
16
+ - Date needs to be formatted in UTC and in the future.
17
+
18
+ {user_context}
19
+
20
+ {skills}
21
+
22
+ ## Examples scenarios
23
+
24
+ 1. @toss will it rain tomorrow? yes,no 10
25
+ - /toss 'will it rain tomorrow' 'yes,no' 10 24h from now
26
+ 2. @toss race to the end Fabri vs John? fabri,john 10
27
+ - /toss 'race to the end' 'fabri,john' 10
28
+ 3. @toss will it rain tomorrow for 10 (keep the toss for 1 week), judge is @fabri
29
+ - /toss 'will it rain tomorrow' 'yes,no' 10 '24 hours from now' @fabri
30
+ 4. @toss will the stock price of company X go up tomorrow? yes,no 5
31
+ - /toss 'will the stock price of company x go up tomorrow' 'yes,no' 5
32
+ 5. @toss who will win the match? team A vs team B 10
33
+ - /toss 'who will win the match' 'team a,team b' 10
34
+ 6. will the project be completed on time? yes,no 0
35
+ - /toss 'will the project be completed on time' 'yes,no' 0
36
+ 7. @toss will the meeting be rescheduled? yes,no 2
37
+ - /toss 'will the meeting be rescheduled' 'yes,no' 2
38
+ 8. will the product launch be successful? yes,no 7
39
+ - /toss 'will the product launch be successful' 'yes,no' 7
40
+ 9. @toss will the team meet the deadline? yes,no 3
41
+ - /toss 'will the team meet the deadline' 'yes,no' 3
42
+ 10. will the event be postponed? yes,no 1
43
+ - /toss 'will the event be postponed' 'yes,no' 1
44
+ 11. @toss yes
45
+ - /join yes
46
+ 12. @toss no
47
+ - /join no
48
+ 13. @toss status
49
+ - /status
50
+ 14. @toss end yes
51
+ - /end yes
52
+
53
+ {issues}
54
+ `;