create-message-kit 1.2.7 → 1.2.9

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.
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
- }