create-message-kit 1.2.7 → 1.2.9

Sign up to get free protection for your applications and to get access to all the features.
package/index.js CHANGED
@@ -7,7 +7,7 @@ import { default as fs } from "fs-extra";
7
7
  import { isCancel } from "@clack/prompts";
8
8
  import { detect } from "detect-package-manager";
9
9
  import pc from "picocolors";
10
- const defVersion = "1.2.7";
10
+ const defVersion = "1.2.9";
11
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
12
 
13
13
  // Read package.json to get the version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-message-kit",
3
- "version": "1.2.7",
3
+ "version": "1.2.9",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -12,7 +12,6 @@
12
12
  ],
13
13
  "scripts": {
14
14
  "clean": "rm -rf .turbo && rm -rf node_modules",
15
- "copy": "node copyTemplates.js",
16
15
  "format": "yarn format:base -w .",
17
16
  "format:base": "prettier --ignore-path ../../.gitignore",
18
17
  "format:check": "yarn format:base -c ."
@@ -38,4 +37,4 @@
38
37
  "access": "public",
39
38
  "registry": "https://registry.npmjs.org/"
40
39
  }
41
- }
40
+ }
@@ -4,7 +4,7 @@
4
4
 
5
5
  ### Check if a Domain is Available
6
6
 
7
- ```typescript
7
+
8
8
  import { ensUrl } from "../index.js";
9
9
  import { XMTPContext } from "@xmtp/message-kit";
10
10
  import type { Skill } from "@xmtp/message-kit";
@@ -54,7 +54,7 @@ export async function handler(context: XMTPContext) {
54
54
 
55
55
  ### Generate a payment request
56
56
 
57
- ```typescript
57
+
58
58
  import { XMTPContext } from "@xmtp/message-kit";
59
59
  import type { Skill } from "@xmtp/message-kit";
60
60
 
@@ -111,12 +111,10 @@ export async function handler(context: XMTPContext) {
111
111
 
112
112
  await context.requestPayment(amount, token, receiverAddress);
113
113
  }
114
- ```
115
114
 
116
115
 
117
116
  ## Types
118
117
 
119
- ```typescript
120
118
  import { XMTPContext } from "../lib/xmtp.js";
121
119
  import { ClientOptions, GroupMember } from "@xmtp/node-sdk";
122
120
  import { ContentTypeId } from "@xmtp/content-type-primitives";
@@ -224,4 +222,4 @@ export interface AbstractedMember {
224
222
 
225
223
  export type MetadataValue = string | number | boolean;
226
224
  export type Metadata = Record<string, MetadataValue | MetadataValue[]>;
227
- ```
225
+
@@ -13,7 +13,6 @@ import { register } from "./skills/register.js";
13
13
  import { renew } from "./skills/renew.js";
14
14
  import { pay } from "./skills/pay.js";
15
15
  import { reset } from "./skills/reset.js";
16
- import { game } from "./skills/game.js";
17
16
  import fs from "fs";
18
17
 
19
18
  // [!region skills]
@@ -29,7 +28,6 @@ export const agent: Agent = {
29
28
  ...renew,
30
29
  ...reset,
31
30
  ...pay,
32
- ...game,
33
31
  ],
34
32
  };
35
33
  // [!endregion skills]
@@ -4,7 +4,7 @@
4
4
 
5
5
  ### Check if a Domain is Available
6
6
 
7
- ```typescript
7
+
8
8
  import { ensUrl } from "../index.js";
9
9
  import { XMTPContext } from "@xmtp/message-kit";
10
10
  import type { Skill } from "@xmtp/message-kit";
@@ -54,7 +54,7 @@ export async function handler(context: XMTPContext) {
54
54
 
55
55
  ### Generate a payment request
56
56
 
57
- ```typescript
57
+
58
58
  import { XMTPContext } from "@xmtp/message-kit";
59
59
  import type { Skill } from "@xmtp/message-kit";
60
60
 
@@ -111,12 +111,10 @@ export async function handler(context: XMTPContext) {
111
111
 
112
112
  await context.requestPayment(amount, token, receiverAddress);
113
113
  }
114
- ```
115
114
 
116
115
 
117
116
  ## Types
118
117
 
119
- ```typescript
120
118
  import { XMTPContext } from "../lib/xmtp.js";
121
119
  import { ClientOptions, GroupMember } from "@xmtp/node-sdk";
122
120
  import { ContentTypeId } from "@xmtp/content-type-primitives";
@@ -224,4 +222,4 @@ export interface AbstractedMember {
224
222
 
225
223
  export type MetadataValue = string | number | boolean;
226
224
  export type Metadata = Record<string, MetadataValue | MetadataValue[]>;
227
- ```
225
+
@@ -1,2 +1,6 @@
1
+ OPENAI_API_KEY= # the API key for OpenAI
2
+ TEST_ENCRYPTION_KEY= # the encryption key for the test wallet
1
3
  KEY= # the private key of the wallet
2
- OPENAI_API_KEY= # sk-proj-...
4
+ RESEND_API_KEY= # the API key for Resend
5
+ FRAMEDL_API_KEY= # the API key for Framedl
6
+ ALCHEMY_SDK= # the API key for Alchemy
@@ -10,7 +10,9 @@
10
10
  "dependencies": {
11
11
  "@xmtp/message-kit": "workspace:*",
12
12
  "alchemy-sdk": "^3.0.0",
13
+ "ethers": "^6.0.0",
13
14
  "express": "^4.19.2",
15
+ "node-fetch": "^3.3.2",
14
16
  "resend": "^4.0.1"
15
17
  },
16
18
  "devDependencies": {
@@ -4,26 +4,37 @@ import {
4
4
  replaceVariables,
5
5
  XMTPContext,
6
6
  Agent,
7
+ xmtpClient,
7
8
  } from "@xmtp/message-kit";
8
- import { systemPrompt } from "./prompt.js";
9
9
  import fs from "fs";
10
- //Local imports
10
+ import { systemPrompt } from "./prompt.js";
11
11
  import { token } from "./skills/token.js";
12
12
  import { todo } from "./skills/todo.js";
13
- import { gated } from "./skills/gated.js";
13
+ import { gated, startGatedGroupServer } from "./skills/gated.js";
14
14
  import { broadcast } from "./skills/broadcast.js";
15
+ import { wordle } from "./skills/wordle.js";
15
16
 
16
17
  export const agent: Agent = {
17
18
  name: "Experimental Agent",
18
- tag: "@exp",
19
+ tag: "@bot",
19
20
  description: "An experimental agent with a lot of skills.",
20
21
  skills: [
21
22
  ...token,
22
23
  ...(process?.env?.RESEND_API_KEY ? todo : []),
23
24
  ...(process?.env?.ALCHEMY_SDK ? gated : []),
24
25
  ...broadcast,
26
+ ...wordle,
25
27
  ],
26
28
  };
29
+
30
+ // [!region gated]
31
+ const { client } = await xmtpClient({
32
+ hideInitLogMessage: true,
33
+ });
34
+
35
+ startGatedGroupServer(client);
36
+ // [!endregion gated]
37
+
27
38
  run(
28
39
  async (context: XMTPContext) => {
29
40
  const {
@@ -37,5 +48,5 @@ run(
37
48
  fs.writeFileSync("example_prompt.md", prompt);
38
49
  await agentReply(context, prompt);
39
50
  },
40
- { agent },
51
+ { agent, experimental: true },
41
52
  );
@@ -17,10 +17,10 @@ export async function checkNft(
17
17
  (nft: any) =>
18
18
  nft.contract.name.toLowerCase() === collectionSlug.toLowerCase(),
19
19
  );
20
- console.log(
21
- `NFTs owned on ${Network.BASE_MAINNET}:`,
22
- nfts.ownedNfts.length,
23
- );
20
+ // console.log(
21
+ // `NFTs owned on ${Network.BASE_MAINNET}:`,
22
+ // nfts.ownedNfts.length,
23
+ // );
24
24
  console.log("is the nft owned: ", ownsNft);
25
25
  return ownsNft as boolean;
26
26
  } catch (error) {
@@ -0,0 +1,26 @@
1
+ import fetch from "node-fetch";
2
+
3
+ export async function sendLog(title: string, description: string) {
4
+ const response = await fetch("https://api.minilog.dev/v1/logs/testlog", {
5
+ method: "POST",
6
+ headers: {
7
+ "Content-Type": "application/json",
8
+ Authorization: "Bearer pthsm38sccpux5acriqish7isz5inet7q73ef7br",
9
+ },
10
+ body: JSON.stringify({
11
+ application: "myapp-1",
12
+ severity: "DEBUG",
13
+ data: title,
14
+ metadata: {
15
+ title,
16
+ description,
17
+ },
18
+ }),
19
+ });
20
+ console.log(response);
21
+ if (!response.ok) {
22
+ console.error("Failed to send log:", response.statusText);
23
+ } else {
24
+ console.log("Log sent successfully");
25
+ }
26
+ }
@@ -0,0 +1,99 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { generatePrivateKey } from "viem/accounts";
4
+ import { ethers } from "ethers";
5
+
6
+ // Define the Base network RPC URL
7
+ const baseRpcUrl = "https://mainnet.base.org";
8
+
9
+ // Create a provider
10
+ const provider = new ethers.JsonRpcProvider(baseRpcUrl);
11
+
12
+ // USDC contract address on Base (replace with actual address)
13
+ const usdcAddress = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"; // Replace with the correct USDC contract address
14
+
15
+ // ERC-20 ABI (balanceOf and transfer functions)
16
+ const erc20Abi = [
17
+ "function balanceOf(address owner) view returns (uint256)",
18
+ "function transfer(address to, uint256 amount) returns (bool)",
19
+ ];
20
+
21
+ export class USDCWallet {
22
+ walletDir: string;
23
+ senderAddress: string;
24
+ privateKey: string;
25
+ agentAddress: string;
26
+ usdcContract: ethers.Contract;
27
+ wallet: ethers.Wallet;
28
+
29
+ constructor(senderAddress: string) {
30
+ this.senderAddress = senderAddress;
31
+ this.walletDir = path.join(process.cwd(), `./.data/usdcwallets`);
32
+ if (!fs.existsSync(this.walletDir)) {
33
+ fs.mkdirSync(this.walletDir, { recursive: true });
34
+ console.warn("USDC wallet created and saved successfully.");
35
+ }
36
+
37
+ const walletFilePath = path.join(this.walletDir, `${senderAddress}.usdc`);
38
+
39
+ if (fs.existsSync(walletFilePath)) {
40
+ const walletData = fs.readFileSync(walletFilePath, "utf8");
41
+ this.privateKey = walletData.match(/KEY=(.+)/)?.[1]?.trim() ?? "";
42
+ } else {
43
+ this.privateKey = generatePrivateKey();
44
+ let usdcWallet = new ethers.Wallet(this.privateKey, provider);
45
+ const walletData = `KEY=${this.privateKey}\nADDRESS=${usdcWallet.address}`;
46
+ fs.writeFileSync(walletFilePath, walletData);
47
+ }
48
+
49
+ // Initialize wallet and USDC contract
50
+ this.wallet = new ethers.Wallet(this.privateKey, provider);
51
+ this.agentAddress = this.wallet.address;
52
+ this.usdcContract = new ethers.Contract(usdcAddress, erc20Abi, this.wallet);
53
+ }
54
+
55
+ async checkBalances(): Promise<{ usdc: number; eth: number }> {
56
+ try {
57
+ // Check USDC balance
58
+ console.log(this.agentAddress);
59
+ const usdcBalance = await this.usdcContract.balanceOf(this.agentAddress);
60
+ const formattedUsdcBalance = ethers.formatUnits(usdcBalance, 6); // USDC has 6 decimals
61
+ console.warn(`USDC Balance: ${formattedUsdcBalance}`);
62
+
63
+ // Check ETH balance
64
+ const ethBalance = await provider.getBalance(this.agentAddress);
65
+ const formattedEthBalance = ethers.formatUnits(ethBalance, 18);
66
+ console.warn(`ETH Balance: ${formattedEthBalance}`);
67
+
68
+ return {
69
+ usdc: parseFloat(formattedUsdcBalance),
70
+ eth: parseFloat(formattedEthBalance),
71
+ };
72
+ } catch (error) {
73
+ console.error("Error fetching balances:", error);
74
+ return { usdc: 0, eth: 0 };
75
+ }
76
+ }
77
+ async transferUsdc(to: string, amount: number) {
78
+ if (!ethers.isAddress(to)) {
79
+ throw new Error("Invalid recipient address");
80
+ } else if (typeof amount !== "number" || amount <= 0) {
81
+ throw new Error("Invalid transfer amount");
82
+ }
83
+ try {
84
+ const amountInWei = ethers.parseUnits(amount.toString(), 6); // USDC has 6 decimals
85
+ const adminAgent = new USDCWallet(to);
86
+ const tx = await this.usdcContract.transfer(
87
+ adminAgent.agentAddress,
88
+ amountInWei,
89
+ );
90
+ const receipt = await tx.wait();
91
+ if (receipt.status !== 1) {
92
+ throw new Error("Transaction failed or was reverted");
93
+ }
94
+ } catch (error) {
95
+ console.warn(`Transferred ${amount} USDC to ${to}.`);
96
+ throw error;
97
+ }
98
+ }
99
+ }
@@ -20,7 +20,7 @@ export async function createGroup(
20
20
  member.accountAddresses.includes(senderAddress.toLowerCase()),
21
21
  );
22
22
  if (senderMember) {
23
- const senderInboxId = senderMember.inboxId;
23
+ senderInboxId = senderMember.inboxId;
24
24
  console.log("Sender's inboxId:", senderInboxId);
25
25
  } else {
26
26
  console.log("Sender not found in members list");
@@ -91,26 +91,27 @@ export async function removeFromGroup(
91
91
  export async function addToGroup(
92
92
  groupId: string,
93
93
  client: V3Client,
94
- v2client: V2Client,
95
- senderAddress: string,
94
+ address: string,
95
+ asAdmin: boolean = false,
96
96
  ): Promise<{ code: number; message: string }> {
97
97
  try {
98
- let lowerAddress = senderAddress.toLowerCase();
99
- const { v2, v3 } = await isOnXMTP(client, v2client, lowerAddress);
98
+ let lowerAddress = address.toLowerCase();
99
+ const { v2, v3 } = await isOnXMTP(client, null, lowerAddress);
100
100
  if (!v3)
101
101
  return {
102
102
  code: 400,
103
103
  message: "You don't seem to have a v3 identity ",
104
104
  };
105
- const conversation =
106
- await client.conversations.getConversationById(groupId);
107
- console.warn("Adding to group", conversation?.id);
108
- await conversation?.sync();
109
- //DON'T TOUCH THIS LINE
110
- await conversation?.addMembers([lowerAddress]);
105
+ const group = await client.conversations.getConversationById(groupId);
106
+ console.warn("Adding to group", group?.id);
107
+ await group?.sync();
108
+ await group?.addMembers([lowerAddress]);
111
109
  console.warn("Added member to group");
112
- await conversation?.sync();
113
- const members = await conversation?.members();
110
+ await group?.sync();
111
+ if (asAdmin) {
112
+ await group?.addSuperAdmin(lowerAddress);
113
+ }
114
+ const members = await group?.members();
114
115
  console.warn("Number of members", members?.length);
115
116
 
116
117
  if (members) {
@@ -18,12 +18,13 @@ export const broadcast: Skill[] = [
18
18
  async function handler(context: XMTPContext) {
19
19
  const {
20
20
  message: {
21
- content: { params },
21
+ content: {
22
+ params: { message },
23
+ },
22
24
  },
23
25
  } = context;
24
26
 
25
27
  const fakeSubscribers = ["0x93E2fc3e99dFb1238eB9e0eF2580EFC5809C7204"];
26
- const { message } = params;
27
28
  await context.send("This is how your message will look like:");
28
29
  await context.send(message);
29
30
  const emailResponse = await context.awaitResponse(
@@ -0,0 +1,114 @@
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+ import type { Skill } from "@xmtp/message-kit";
3
+ import { USDCWallet } from "../lib/usdc.js";
4
+
5
+ export const cash: Skill[] = [
6
+ {
7
+ skill: "/balance",
8
+ handler: balanceHandler,
9
+ examples: ["/balance"],
10
+ params: {},
11
+ description: "Check your balance.",
12
+ },
13
+ {
14
+ skill: "/fund [amount]",
15
+ handler: fundHandler,
16
+ examples: ["/fund 1", "/fund 10"],
17
+ params: {
18
+ amount: {
19
+ type: "number",
20
+ default: "",
21
+ },
22
+ },
23
+ description: "Fund your wallet. Returns a url to fund your wallet.",
24
+ },
25
+ {
26
+ skill: "/transfer [address] [amount]",
27
+ handler: transferHandler,
28
+ examples: ["/transfer 0x40f08f0f853d1c42c61815652b7ccd5a50f0be09 1"],
29
+ params: {},
30
+ description: "Transfer USDC to another address.",
31
+ },
32
+ ];
33
+
34
+ async function balanceHandler(context: XMTPContext) {
35
+ const {
36
+ message: { sender },
37
+ } = context;
38
+ const usdcWallet = new USDCWallet(sender.address);
39
+ const { usdc } = await usdcWallet.checkBalances();
40
+ await context.send(
41
+ `Your balance is ${usdc} USDC. let me know if you want check again or to fund your wallet.`,
42
+ );
43
+ }
44
+
45
+ async function fundHandler(context: XMTPContext) {
46
+ try {
47
+ const {
48
+ message: {
49
+ sender,
50
+ content: {
51
+ params: { amount },
52
+ },
53
+ },
54
+ } = context;
55
+ const usdcWallet = new USDCWallet(sender.address);
56
+ const { usdc } = await usdcWallet.checkBalances();
57
+ const MAX_USDC = 10;
58
+
59
+ if (usdc >= MAX_USDC) {
60
+ await context.send(`Your balance is maxed out at ${MAX_USDC} USDC.`);
61
+ return;
62
+ }
63
+
64
+ const remainingLimit = MAX_USDC - usdc;
65
+ let fundAmount: number;
66
+
67
+ if (!amount) {
68
+ const options = Array.from(
69
+ { length: Math.floor(remainingLimit) },
70
+ (_, i) => (i + 1).toString(),
71
+ );
72
+ const response = await context.awaitResponse(
73
+ `Please specify the amount of USDC to prefund (1 to ${remainingLimit}):`,
74
+ options,
75
+ );
76
+ fundAmount = parseInt(response);
77
+ } else {
78
+ fundAmount = parseInt(amount);
79
+ }
80
+
81
+ if (isNaN(fundAmount) || fundAmount <= 0 || fundAmount > remainingLimit) {
82
+ await context.send(
83
+ `Invalid amount. Please specify a value between 1 and ${remainingLimit} USDC.`,
84
+ );
85
+ return;
86
+ }
87
+
88
+ await context.requestPayment(fundAmount, "USDC", usdcWallet.agentAddress);
89
+ await context.send(
90
+ "After funding, let me know so I can check your balance.",
91
+ );
92
+ } catch (error) {
93
+ await context.send(
94
+ "An error occurred while processing your request. Please try again.",
95
+ );
96
+ }
97
+ }
98
+
99
+ async function transferHandler(context: XMTPContext) {
100
+ const {
101
+ message: {
102
+ sender,
103
+ content: {
104
+ params: { address, amount },
105
+ },
106
+ },
107
+ } = context;
108
+ const usdcWallet = new USDCWallet(sender.address);
109
+ if (amount > 10) {
110
+ await context.send("You can only transfer up to 10 USDC at a time.");
111
+ return;
112
+ }
113
+ await usdcWallet.transferUsdc(address, amount);
114
+ }
@@ -2,7 +2,7 @@ import { XMTPContext, Skill, V3Client } from "@xmtp/message-kit";
2
2
  import { createGroup } from "../lib/xmtp.js";
3
3
  import express from "express";
4
4
  import { checkNft } from "../lib/alchemy.js";
5
-
5
+ import { addToGroup } from "../lib/xmtp.js";
6
6
  export const gated: Skill[] = [
7
7
  {
8
8
  skill: "/create",
@@ -12,14 +12,6 @@ export const gated: Skill[] = [
12
12
  params: {},
13
13
  description: "Create a new group.",
14
14
  },
15
- {
16
- skill: "/id",
17
- examples: ["/id"],
18
- handler: handler,
19
- adminOnly: true,
20
- params: {},
21
- description: "Get group id.",
22
- },
23
15
  ];
24
16
 
25
17
  async function handler(context: XMTPContext) {
@@ -29,23 +21,18 @@ async function handler(context: XMTPContext) {
29
21
  content: { skill },
30
22
  },
31
23
  client,
32
- group,
33
24
  } = context;
34
25
 
35
- if (skill == "id") {
36
- console.log(group?.id);
37
- } else if (skill === "create") {
38
- console.log(client, sender.address, client.accountAddress);
26
+ if (skill === "create") {
39
27
  const group = await createGroup(
40
28
  client,
41
29
  sender.address,
42
30
  client.accountAddress,
43
31
  );
44
32
 
45
- // await context.send(
46
- // `Group created!\n- ID: ${group.id}\n- Group Frame URL: https://converse.xyz/group/${group.id}: \n- This url will deelink to the group inside Converse\n- Once in the other group you can share the invite with your friends.`,
47
- // );
48
- //startServer(client);
33
+ await context.send(
34
+ `Group created!\n- ID: ${group?.id}\n- Group Frame URL: https://converse.xyz/group/${group?.id}: \n- This url will deelink to the group inside Converse\n- Once in the other group you can share the invite with your friends.`,
35
+ );
49
36
  return;
50
37
  } else {
51
38
  await context.send(
@@ -54,22 +41,24 @@ async function handler(context: XMTPContext) {
54
41
  }
55
42
  }
56
43
 
57
- export function startServer(client: V3Client) {
44
+ export function startGatedGroupServer(client: V3Client) {
58
45
  async function addWalletToGroup(
59
46
  walletAddress: string,
60
47
  groupId: string,
61
48
  ): Promise<string> {
62
- const conversation =
63
- await client.conversations.getConversationById(groupId);
64
- const verified = await checkNft(walletAddress, "XMTPeople");
49
+ const verified = true; //await checkNft(walletAddress, "XMTPeople");
65
50
  if (!verified) {
66
51
  console.log("User cant be added to the group");
67
52
  return "not verified";
68
53
  } else {
69
54
  try {
70
- await conversation?.addMembers([walletAddress]);
71
- console.log(`Added wallet address: ${walletAddress} to the group`);
72
- return "success";
55
+ const added = await addToGroup(groupId, client, walletAddress);
56
+ if (added.code === 200) {
57
+ console.log(`Added wallet address: ${walletAddress} to the group`);
58
+ return "success";
59
+ } else {
60
+ return added.message;
61
+ }
73
62
  } catch (error: any) {
74
63
  console.log(error.message);
75
64
  return "error";
@@ -54,9 +54,9 @@ export async function handler(context: XMTPContext) {
54
54
  try {
55
55
  let { reply } = await context.textGeneration(
56
56
  email,
57
- "Make this summary concise and to the point to be sent in an html email. Just return the content inside the body tag.\n msg: " +
57
+ "Make this summary concise and to the point to be sent in an email.\n msg: " +
58
58
  previousMsg,
59
- "You are an expert at summarizing to-dos.",
59
+ "You are an expert at summarizing to-dos. Return in format html and just the content inside the body tag. Dont return `html` or `body` tags",
60
60
  );
61
61
  if (typeof reply === "string") {
62
62
  let content = {
@@ -0,0 +1,97 @@
1
+ import { XMTPContext } from "@xmtp/message-kit";
2
+ import type { Skill } from "@xmtp/message-kit";
3
+
4
+ export const wordle: Skill[] = [
5
+ {
6
+ skill: "/wordle",
7
+ handler: handler,
8
+ examples: ["/wordle"],
9
+ description: "Play wordle.",
10
+ params: {},
11
+ },
12
+ {
13
+ skill: "/arena [word count] [audience size]",
14
+ examples: ["/arena 3 15"],
15
+ handler: handler,
16
+ description: "Play arena.",
17
+ params: {
18
+ wordCount: {
19
+ default: 3,
20
+ type: "number",
21
+ },
22
+ audienceSize: {
23
+ default: 15,
24
+ type: "number",
25
+ },
26
+ },
27
+ },
28
+ ];
29
+
30
+ async function handler(context: XMTPContext) {
31
+ const {
32
+ message: {
33
+ content: { skill },
34
+ },
35
+ } = context;
36
+
37
+ if (skill === "arena") {
38
+ await handleArenaMessage(context);
39
+ } else if (skill === "wordle") {
40
+ await context.send("https://framedl.xyz");
41
+ } else if (skill === "help") {
42
+ await context.send(
43
+ "For using this bot you can use the following commands:\n\n" +
44
+ "/wordle, @wordle, 🔍, 🔎 - To start the game\n" +
45
+ "/arena <word count> <audience size> - To start the arena game\n" +
46
+ "/help - To see commands",
47
+ );
48
+ }
49
+ }
50
+ async function handleArenaMessage(context: XMTPContext) {
51
+ const {
52
+ message: {
53
+ content: { text },
54
+ },
55
+ members,
56
+ } = context;
57
+
58
+ const apiKey = process.env.FRAMEDL_API_KEY;
59
+ if (!apiKey) {
60
+ console.log("FRAMEDL_API_KEY is not set");
61
+ await context.send("https://www.framedl.xyz/games/arena/create");
62
+ return;
63
+ }
64
+ const participantCount = members && members.length ? members.length - 1 : 0;
65
+ const args = text?.split(" ") ?? [];
66
+ const wordCountArg = args[1] ? parseInt(args[1], 10) : 3;
67
+ const audienceSizeArg = args[2] ? parseInt(args[2], 10) : participantCount;
68
+ if (isNaN(wordCountArg) || isNaN(audienceSizeArg)) {
69
+ await context.send(
70
+ "usage: /arena <word count> <audience size>\n\n" +
71
+ "word count: number of words in the arena (default: 3, min: 1, max: 9)\n" +
72
+ "audience size: number of audience members (default: number of participants excluding wordle bot, min: 1, max: 15)",
73
+ );
74
+ return;
75
+ }
76
+
77
+ const audienceSize = Math.max(1, Math.min(15, audienceSizeArg));
78
+ const wordCount = Math.max(1, Math.min(9, wordCountArg));
79
+
80
+ try {
81
+ const response = await fetch("https://www.framedl.xyz/api/arenas", {
82
+ method: "POST",
83
+ body: JSON.stringify({ wordCount, audienceSize }),
84
+ headers: {
85
+ "Content-Type": "application/json",
86
+ "x-framedl-api-key": apiKey,
87
+ },
88
+ });
89
+
90
+ const data = (await response.json()) as { arenaUrl: string };
91
+
92
+ await context.send(data.arenaUrl);
93
+ } catch (error) {
94
+ console.error(error);
95
+ await context.send("https://www.framedl.xyz/games/arena/create");
96
+ }
97
+ }
@@ -4,7 +4,7 @@
4
4
 
5
5
  ### Check if a Domain is Available
6
6
 
7
- ```typescript
7
+
8
8
  import { ensUrl } from "../index.js";
9
9
  import { XMTPContext } from "@xmtp/message-kit";
10
10
  import type { Skill } from "@xmtp/message-kit";
@@ -54,7 +54,7 @@ export async function handler(context: XMTPContext) {
54
54
 
55
55
  ### Generate a payment request
56
56
 
57
- ```typescript
57
+
58
58
  import { XMTPContext } from "@xmtp/message-kit";
59
59
  import type { Skill } from "@xmtp/message-kit";
60
60
 
@@ -111,12 +111,10 @@ export async function handler(context: XMTPContext) {
111
111
 
112
112
  await context.requestPayment(amount, token, receiverAddress);
113
113
  }
114
- ```
115
114
 
116
115
 
117
116
  ## Types
118
117
 
119
- ```typescript
120
118
  import { XMTPContext } from "../lib/xmtp.js";
121
119
  import { ClientOptions, GroupMember } from "@xmtp/node-sdk";
122
120
  import { ContentTypeId } from "@xmtp/content-type-primitives";
@@ -224,4 +222,4 @@ export interface AbstractedMember {
224
222
 
225
223
  export type MetadataValue = string | number | boolean;
226
224
  export type Metadata = Record<string, MetadataValue | MetadataValue[]>;
227
- ```
225
+
@@ -1,58 +0,0 @@
1
- import { XMTPContext } from "@xmtp/message-kit";
2
- import type { Skill } from "@xmtp/message-kit";
3
-
4
- export const game: 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
- let returnText = "";
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
- returnText = gameUrl;
46
- break;
47
-
48
- case "help":
49
- returnText = "Available games: \n/game wordle\n/game slot";
50
- break;
51
- default:
52
- // Inform the user about unrecognized skills and provide available options
53
- returnText =
54
- "Skill not recognized. Available games: wordle, slot, or help.";
55
- break;
56
- }
57
- return { code: 200, message: returnText };
58
- }