create-message-kit 1.2.14 → 1.2.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/index.js +19 -17
  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
+ `;