create-message-kit 1.2.10 → 1.2.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. package/index.js +10 -5
  2. package/package.json +1 -1
  3. package/templates/{agent → ens}/.cursorrules +2 -2
  4. package/templates/{agent → ens}/package.json +1 -1
  5. package/templates/{agent → ens}/src/prompt.ts +1 -1
  6. package/templates/{agent → ens}/src/skills/pay.ts +1 -1
  7. package/templates/{experimental → simple}/.cursorrules +2 -2
  8. package/templates/{gpt → simple}/package.json +1 -1
  9. package/templates/experimental/.env.example +0 -6
  10. package/templates/experimental/package.json +0 -26
  11. package/templates/experimental/src/index.ts +0 -52
  12. package/templates/experimental/src/lib/alchemy.ts +0 -31
  13. package/templates/experimental/src/lib/minilog.ts +0 -26
  14. package/templates/experimental/src/lib/usdc.ts +0 -99
  15. package/templates/experimental/src/lib/xmtp.ts +0 -139
  16. package/templates/experimental/src/prompt.ts +0 -24
  17. package/templates/experimental/src/skills/broadcast.ts +0 -39
  18. package/templates/experimental/src/skills/cash.ts +0 -114
  19. package/templates/experimental/src/skills/gated.ts +0 -89
  20. package/templates/experimental/src/skills/todo.ts +0 -80
  21. package/templates/experimental/src/skills/token.ts +0 -57
  22. package/templates/experimental/src/skills/wordle.ts +0 -97
  23. package/templates/gpt/.cursorrules +0 -225
  24. package/templates/gpt/.yarnrc.yml +0 -4
  25. /package/templates/{agent → ens}/.env.example +0 -0
  26. /package/templates/{agent → ens}/.yarnrc.yml +0 -0
  27. /package/templates/{agent → ens}/src/index.ts +0 -0
  28. /package/templates/{agent → ens}/src/skills/check.ts +0 -0
  29. /package/templates/{agent → ens}/src/skills/cool.ts +0 -0
  30. /package/templates/{agent → ens}/src/skills/info.ts +0 -0
  31. /package/templates/{agent → ens}/src/skills/register.ts +0 -0
  32. /package/templates/{agent → ens}/src/skills/renew.ts +0 -0
  33. /package/templates/{agent → ens}/src/skills/reset.ts +0 -0
  34. /package/templates/{gpt → simple}/.env.example +0 -0
  35. /package/templates/{experimental → simple}/.yarnrc.yml +0 -0
  36. /package/templates/{gpt → simple}/src/index.ts +0 -0
  37. /package/templates/{gpt → simple}/src/prompt.ts +0 -0
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.10";
10
+ const defVersion = "1.2.12";
11
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
12
 
13
13
  // Read package.json to get the version
@@ -96,9 +96,14 @@ async function updatePackagejson(destDir, templateType) {
96
96
 
97
97
  async function gatherProjectInfo() {
98
98
  const templateOptions = [
99
- { value: "gpt", label: "Simple Gpt" },
100
- { value: "agent", label: "ENS Agent" },
101
- { value: "experimental", label: "Experimental" },
99
+ {
100
+ value: "simple",
101
+ label: "Simple Agent: A starter template for building an agent",
102
+ },
103
+ {
104
+ value: "ens",
105
+ label: "ENS Agent: An example of an agent using ENS skills",
106
+ },
102
107
  ];
103
108
 
104
109
  const templateType = await select({
@@ -253,7 +258,7 @@ function createReadme(destDir, templateType, projectName, packageManager) {
253
258
 
254
259
  const readmeContent = `# ${projectName}
255
260
 
256
- This project is powered by [MessageKit](https://messagekit.ephemerahq.com/)
261
+ This project is powered by [MessageKit](https://message-kit.org/)
257
262
 
258
263
  ## Setup
259
264
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-message-kit",
3
- "version": "1.2.10",
3
+ "version": "1.2.12",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -64,7 +64,7 @@ export const paymentRequest: Skill[] = [
64
64
  skill: "/pay [amount] [token] [username] [address]",
65
65
  examples: [
66
66
  "/pay 10 vitalik.eth",
67
- "/pay 1 usdc to 0xc9925662D36DE3e1bF0fD64e779B2e5F0Aead964",
67
+ "/pay 1 usdc to 0xC60E6Bb79322392761BFe3081E302aEB79B30B03",
68
68
  ],
69
69
  description:
70
70
  "Send a specified amount of a cryptocurrency to a destination address. \nWhen tipping, you can assume it's 1 USDC.",
@@ -115,7 +115,7 @@ export async function handler(context: XMTPContext) {
115
115
 
116
116
  ## Types
117
117
 
118
- import { XMTPContext } from "../lib/xmtp.js";
118
+ import { XMTPContext } from "../plugins/xmtp.js";
119
119
  import { ClientOptions, GroupMember } from "@xmtp/node-sdk";
120
120
  import { ContentTypeId } from "@xmtp/content-type-primitives";
121
121
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "agent",
2
+ "name": "ens-agent",
3
3
  "private": true,
4
4
  "type": "module",
5
5
  "scripts": {
@@ -1,5 +1,5 @@
1
1
  export const systemPrompt = `
2
- Your are helpful and playful ens agent called {agent_name} that lives inside a messaging app called Converse.
2
+ Your are a helpful and playful ens agent called {agent_name} that lives inside a messaging app called Converse.
3
3
 
4
4
  {rules}
5
5
 
@@ -6,7 +6,7 @@ export const pay: Skill[] = [
6
6
  skill: "/pay [amount] [token] [username] [address]",
7
7
  examples: [
8
8
  "/pay 10 vitalik.eth",
9
- "/pay 1 usdc to 0xc9925662D36DE3e1bF0fD64e779B2e5F0Aead964",
9
+ "/pay 1 usdc to 0xC60E6Bb79322392761BFe3081E302aEB79B30B03",
10
10
  ],
11
11
  description:
12
12
  "Send a specified amount of a cryptocurrency to a destination address. \nWhen tipping, you can asume its 1 usdc.",
@@ -64,7 +64,7 @@ export const paymentRequest: Skill[] = [
64
64
  skill: "/pay [amount] [token] [username] [address]",
65
65
  examples: [
66
66
  "/pay 10 vitalik.eth",
67
- "/pay 1 usdc to 0xc9925662D36DE3e1bF0fD64e779B2e5F0Aead964",
67
+ "/pay 1 usdc to 0xC60E6Bb79322392761BFe3081E302aEB79B30B03",
68
68
  ],
69
69
  description:
70
70
  "Send a specified amount of a cryptocurrency to a destination address. \nWhen tipping, you can assume it's 1 USDC.",
@@ -115,7 +115,7 @@ export async function handler(context: XMTPContext) {
115
115
 
116
116
  ## Types
117
117
 
118
- import { XMTPContext } from "../lib/xmtp.js";
118
+ import { XMTPContext } from "../plugins/xmtp.js";
119
119
  import { ClientOptions, GroupMember } from "@xmtp/node-sdk";
120
120
  import { ContentTypeId } from "@xmtp/content-type-primitives";
121
121
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "gpt",
2
+ "name": "simple-agent",
3
3
  "private": true,
4
4
  "type": "module",
5
5
  "scripts": {
@@ -1,6 +0,0 @@
1
- OPENAI_API_KEY= # the API key for OpenAI
2
- TEST_ENCRYPTION_KEY= # the encryption key for the test wallet
3
- KEY= # the private key of the wallet
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
@@ -1,26 +0,0 @@
1
- {
2
- "name": "experimental",
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
- "@xmtp/message-kit": "workspace:*",
12
- "alchemy-sdk": "^3.0.0",
13
- "ethers": "^6.0.0",
14
- "express": "^4.19.2",
15
- "node-fetch": "^3.3.2",
16
- "resend": "^4.0.1"
17
- },
18
- "devDependencies": {
19
- "@types/express": "^4.17.20",
20
- "@types/node": "^20.14.2",
21
- "typescript": "^5.4.5"
22
- },
23
- "engines": {
24
- "node": ">=20"
25
- }
26
- }
@@ -1,52 +0,0 @@
1
- import {
2
- run,
3
- agentReply,
4
- replaceVariables,
5
- XMTPContext,
6
- Agent,
7
- xmtpClient,
8
- } from "@xmtp/message-kit";
9
- import fs from "fs";
10
- import { systemPrompt } from "./prompt.js";
11
- import { token } from "./skills/token.js";
12
- import { todo } from "./skills/todo.js";
13
- import { gated, startGatedGroupServer } from "./skills/gated.js";
14
- import { broadcast } from "./skills/broadcast.js";
15
- import { wordle } from "./skills/wordle.js";
16
-
17
- export const agent: Agent = {
18
- name: "Experimental Agent",
19
- tag: "@bot",
20
- description: "An experimental agent with a lot of skills.",
21
- skills: [
22
- ...token,
23
- ...(process?.env?.RESEND_API_KEY ? todo : []),
24
- ...(process?.env?.ALCHEMY_SDK ? gated : []),
25
- ...broadcast,
26
- ...wordle,
27
- ],
28
- };
29
-
30
- // [!region gated]
31
- const { client } = await xmtpClient({
32
- hideInitLogMessage: true,
33
- });
34
-
35
- startGatedGroupServer(client);
36
- // [!endregion gated]
37
-
38
- run(
39
- async (context: XMTPContext) => {
40
- const {
41
- message: { sender },
42
- agent,
43
- } = context;
44
-
45
- let prompt = await replaceVariables(systemPrompt, sender.address, agent);
46
-
47
- //This is only used for to update the docs.
48
- fs.writeFileSync("example_prompt.md", prompt);
49
- await agentReply(context, prompt);
50
- },
51
- { agent, experimental: true },
52
- );
@@ -1,31 +0,0 @@
1
- import { Alchemy, Network } from "alchemy-sdk";
2
-
3
- const settings = {
4
- apiKey: process.env.ALCHEMY_API_KEY, // Replace with your Alchemy API key
5
- network: Network.BASE_MAINNET, // Use the appropriate network
6
- };
7
-
8
- export async function checkNft(
9
- walletAddress: string,
10
- collectionSlug: string,
11
- ): Promise<boolean> {
12
- const alchemy = new Alchemy(settings);
13
- try {
14
- const nfts = await alchemy.nft.getNftsForOwner(walletAddress);
15
-
16
- const ownsNft = nfts.ownedNfts.some(
17
- (nft: any) =>
18
- nft.contract.name.toLowerCase() === collectionSlug.toLowerCase(),
19
- );
20
- // console.log(
21
- // `NFTs owned on ${Network.BASE_MAINNET}:`,
22
- // nfts.ownedNfts.length,
23
- // );
24
- console.log("is the nft owned: ", ownsNft);
25
- return ownsNft as boolean;
26
- } catch (error) {
27
- console.error("Error fetching NFTs from Alchemy:", error);
28
- }
29
-
30
- return false;
31
- }
@@ -1,26 +0,0 @@
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
- }
@@ -1,99 +0,0 @@
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
- }
@@ -1,139 +0,0 @@
1
- import { isOnXMTP, V3Client, V2Client } from "@xmtp/message-kit";
2
-
3
- export async function createGroup(
4
- client: V3Client,
5
- senderAddress: string,
6
- clientAddress: string,
7
- ) {
8
- try {
9
- let senderInboxId = "";
10
- await client.conversations.sync();
11
- const conversations = await client.conversations.list();
12
- console.log("Conversations", conversations.length);
13
- const group = await client?.conversations.newGroup([
14
- senderAddress,
15
- clientAddress,
16
- ]);
17
- console.log("Group created", group?.id);
18
- const members = await group.members();
19
- const senderMember = members.find((member) =>
20
- member.accountAddresses.includes(senderAddress.toLowerCase()),
21
- );
22
- if (senderMember) {
23
- senderInboxId = senderMember.inboxId;
24
- console.log("Sender's inboxId:", senderInboxId);
25
- } else {
26
- console.log("Sender not found in members list");
27
- }
28
- await group.addSuperAdmin(senderInboxId);
29
- console.log(
30
- "Sender is superAdmin",
31
- await group.isSuperAdmin(senderInboxId),
32
- );
33
- await group.send(`Welcome to the new group!`);
34
- await group.send(`You are now the admin of this group as well as the bot`);
35
- return group;
36
- } catch (error) {
37
- console.log("Error creating group", error);
38
- return null;
39
- }
40
- }
41
-
42
- export async function removeFromGroup(
43
- groupId: string,
44
- client: V3Client,
45
- v2client: V2Client,
46
- senderAddress: string,
47
- ): Promise<{ code: number; message: string }> {
48
- try {
49
- let lowerAddress = senderAddress.toLowerCase();
50
- const { v2, v3 } = await isOnXMTP(client, v2client, lowerAddress);
51
- console.warn("Checking if on XMTP: v2", v2, "v3", v3);
52
- if (!v3)
53
- return {
54
- code: 400,
55
- message: "You don't seem to have a v3 identity ",
56
- };
57
- const conversation =
58
- await client.conversations.getConversationById(groupId);
59
- console.warn("removing from group", conversation?.id);
60
- await conversation?.sync();
61
- await conversation?.removeMembers([lowerAddress]);
62
- console.warn("Removed member from group");
63
- await conversation?.sync();
64
- const members = await conversation?.members();
65
- console.warn("Number of members", members?.length);
66
-
67
- let wasRemoved = true;
68
- if (members) {
69
- for (const member of members) {
70
- let lowerMemberAddress = member.accountAddresses[0].toLowerCase();
71
- if (lowerMemberAddress === lowerAddress) {
72
- wasRemoved = false;
73
- break;
74
- }
75
- }
76
- }
77
- return {
78
- code: wasRemoved ? 200 : 400,
79
- message: wasRemoved
80
- ? "You have been removed from the group"
81
- : "Failed to remove from group",
82
- };
83
- } catch (error) {
84
- console.log("Error removing from group", error);
85
- return {
86
- code: 400,
87
- message: "Failed to remove from group",
88
- };
89
- }
90
- }
91
- export async function addToGroup(
92
- groupId: string,
93
- client: V3Client,
94
- address: string,
95
- asAdmin: boolean = false,
96
- ): Promise<{ code: number; message: string }> {
97
- try {
98
- let lowerAddress = address.toLowerCase();
99
- const { v2, v3 } = await isOnXMTP(client, null, lowerAddress);
100
- if (!v3)
101
- return {
102
- code: 400,
103
- message: "You don't seem to have a v3 identity ",
104
- };
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]);
109
- console.warn("Added member to group");
110
- await group?.sync();
111
- if (asAdmin) {
112
- await group?.addSuperAdmin(lowerAddress);
113
- }
114
- const members = await group?.members();
115
- console.warn("Number of members", members?.length);
116
-
117
- if (members) {
118
- for (const member of members) {
119
- let lowerMemberAddress = member.accountAddresses[0].toLowerCase();
120
- if (lowerMemberAddress === lowerAddress) {
121
- console.warn("Member exists", lowerMemberAddress);
122
- return {
123
- code: 200,
124
- message: "You have been added to the group",
125
- };
126
- }
127
- }
128
- }
129
- return {
130
- code: 400,
131
- message: "Failed to add to group",
132
- };
133
- } catch (error) {
134
- return {
135
- code: 400,
136
- message: "Failed to add to group",
137
- };
138
- }
139
- }
@@ -1,24 +0,0 @@
1
- export const systemPrompt = `
2
- Your are helpful and playful experimental agent called {agent_name} that lives inside a messaging app called Converse.
3
-
4
- {rules}
5
-
6
- {user_context}
7
-
8
- {skills}
9
-
10
- ## Scenarios
11
- 1. Missing commands in responses
12
- **Issue**: Sometimes responses are sent without the required command.
13
- **Example**:
14
- Incorrect:
15
- > "Looks like vitalik.eth is registered! What about these cool alternatives?"
16
- Correct:
17
- > "Looks like vitalik.eth is registered! What about these cool alternatives?
18
- > /cool vitalik.eth"
19
-
20
- Incorrect:
21
- > Here is a summary of your TODOs. I will now send it via email.
22
- Correct:
23
- > /todo
24
- `;
@@ -1,39 +0,0 @@
1
- import { XMTPContext, Skill } from "@xmtp/message-kit";
2
-
3
- export const broadcast: Skill[] = [
4
- {
5
- skill: "/send",
6
- adminOnly: true,
7
- handler: handler,
8
- examples: ["/send Hello everyone, the event is starting now!"],
9
- description: "Send updates to all subscribers.",
10
- params: {
11
- message: {
12
- type: "prompt",
13
- },
14
- },
15
- },
16
- ];
17
-
18
- async function handler(context: XMTPContext) {
19
- const {
20
- message: {
21
- content: {
22
- params: { message },
23
- },
24
- },
25
- } = context;
26
-
27
- const fakeSubscribers = ["0x93E2fc3e99dFb1238eB9e0eF2580EFC5809C7204"];
28
- await context.send("This is how your message will look like:");
29
- await context.send(message);
30
- const emailResponse = await context.awaitResponse(
31
- "Are you sure you want to send this broadcast?\nType 'yes' to confirm.",
32
- ["yes", "no"],
33
- );
34
- if (emailResponse === "yes") {
35
- await context.send("Sending broadcast...");
36
- await context.sendTo(message, fakeSubscribers);
37
- await context.send("Broadcast sent!");
38
- }
39
- }
@@ -1,114 +0,0 @@
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
- }
@@ -1,89 +0,0 @@
1
- import { XMTPContext, Skill, V3Client } from "@xmtp/message-kit";
2
- import { createGroup } from "../lib/xmtp.js";
3
- import express from "express";
4
- import { checkNft } from "../lib/alchemy.js";
5
- import { addToGroup } from "../lib/xmtp.js";
6
- export const gated: Skill[] = [
7
- {
8
- skill: "/create",
9
- examples: ["/create"],
10
- handler: handler,
11
- adminOnly: true,
12
- params: {},
13
- description: "Create a new group.",
14
- },
15
- ];
16
-
17
- async function handler(context: XMTPContext) {
18
- const {
19
- message: {
20
- sender,
21
- content: { skill },
22
- },
23
- client,
24
- } = context;
25
-
26
- if (skill === "create") {
27
- const group = await createGroup(
28
- client,
29
- sender.address,
30
- client.accountAddress,
31
- );
32
-
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
- );
36
- return;
37
- } else {
38
- await context.send(
39
- "👋 Welcome to the Gated Bot Group!\nTo get started, type /create to set up a new group. 🚀\nThis example will check if the user has a particular nft and add them to the group if they do.\nOnce your group is created, you'll receive a unique Group ID and URL.\nShare the URL with friends to invite them to join your group!",
40
- );
41
- }
42
- }
43
-
44
- export function startGatedGroupServer(client: V3Client) {
45
- async function addWalletToGroup(
46
- walletAddress: string,
47
- groupId: string,
48
- ): Promise<string> {
49
- const verified = true; //await checkNft(walletAddress, "XMTPeople");
50
- if (!verified) {
51
- console.log("User cant be added to the group");
52
- return "not verified";
53
- } else {
54
- try {
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
- }
62
- } catch (error: any) {
63
- console.log(error.message);
64
- return "error";
65
- }
66
- }
67
- }
68
-
69
- // Endpoint to add wallet address to a group from an external source
70
- const app = express();
71
- app.use(express.json());
72
- app.post("/add-wallet", async (req, res) => {
73
- try {
74
- const { walletAddress, groupId } = req.body;
75
- const result = await addWalletToGroup(walletAddress, groupId);
76
- res.status(200).send(result);
77
- } catch (error: any) {
78
- res.status(400).send(error.message);
79
- }
80
- });
81
- // Start the servfalcheer
82
- const PORT = process.env.PORT || 3000;
83
- const url = process.env.URL || `http://localhost:${PORT}`;
84
- app.listen(PORT, () => {
85
- console.warn(
86
- `Use this endpoint to add a wallet to a group indicated by the groupId\n${url}/add-wallet <body: {walletAddress, groupId}>`,
87
- );
88
- });
89
- }
@@ -1,80 +0,0 @@
1
- import { Resend } from "resend";
2
- import { XMTPContext } from "@xmtp/message-kit";
3
- import type { Skill } from "@xmtp/message-kit";
4
-
5
- const resend = new Resend(process.env.RESEND_API_KEY); // Replace with your Resend API key
6
-
7
- export const todo: Skill[] = [
8
- {
9
- skill: "/todo",
10
- handler: handler,
11
- examples: ["/todo"],
12
- description:
13
- "Summarize your TODOs and send an email with the summary. Receives no parameters.",
14
- params: {},
15
- },
16
- ];
17
-
18
- export async function handler(context: XMTPContext) {
19
- const {
20
- message: {
21
- content: { previousMsg },
22
- },
23
- } = context;
24
-
25
- let email = "";
26
- if (!previousMsg) {
27
- await context.send("You need to do it on a reply.");
28
- return;
29
- }
30
- let intents = 2;
31
- while (intents > 0) {
32
- const emailResponse = await context.awaitResponse(
33
- "Please provide your email address to receive the to-dos summary:",
34
- );
35
- email = emailResponse;
36
-
37
- // Basic email validation
38
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
39
- if (!emailRegex.test(email)) {
40
- await context.send(
41
- "Invalid email format. Please try again with a valid email address.",
42
- );
43
- intents--;
44
- continue;
45
- }
46
- break;
47
- }
48
- if (intents == 0) {
49
- await context.send(
50
- "I couldn't get your email address. Please try again later.",
51
- );
52
- return;
53
- }
54
- try {
55
- let { reply } = await context.textGeneration(
56
- email,
57
- "Make this summary concise and to the point to be sent in an email.\n msg: " +
58
- previousMsg,
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
- );
61
- if (typeof reply === "string") {
62
- let content = {
63
- from: "bot@mail.coin-toss.xyz",
64
- to: email,
65
- subject: "Your summary from Converse",
66
- html: `
67
- <h3>Your Converse Summary</h3>
68
- <p>${reply}</p>
69
- `,
70
- };
71
- await resend.emails.send(content);
72
- await context.send(`✅ Summary sent successfully to ${email}`);
73
- } else {
74
- await context.send("❌ Message not found.");
75
- }
76
- } catch (error) {
77
- await context.send("❌ Failed to send email. Please try again later.");
78
- console.error("Error sending email:", error);
79
- }
80
- }
@@ -1,57 +0,0 @@
1
- import { XMTPContext } from "@xmtp/message-kit";
2
- import type { Skill } from "@xmtp/message-kit";
3
-
4
- export const token: Skill[] = [
5
- {
6
- skill: "/token [symbol]",
7
- handler: handler,
8
- examples: ["/token bitcoin", "/token ethereum"],
9
- description: "Get real time price of a any token.",
10
- params: {
11
- symbol: {
12
- type: "string",
13
- },
14
- },
15
- },
16
- ];
17
- export async function handler(context: XMTPContext) {
18
- const {
19
- message: {
20
- content: {
21
- params: { symbol },
22
- },
23
- },
24
- } = context;
25
- const response = await fetch(
26
- `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=${symbol}`,
27
- );
28
- if (!response.ok) {
29
- context.send("Token not found");
30
- context.send("try with its full name, instead of btc it would be bitcoin");
31
- return;
32
- }
33
- const data = (await response.json()) as any;
34
- const token = data[0];
35
-
36
- const tokenInfo = {
37
- name: token.name,
38
- symbol: token.symbol.toUpperCase(),
39
- price: token.current_price,
40
- image: token.image,
41
- link: `https://www.coingecko.com/en/coins/${token.id}`,
42
- };
43
-
44
- let frame = {
45
- title: tokenInfo.name,
46
- buttons: [
47
- { content: "Buy", action: "link", target: tokenInfo.link },
48
- {
49
- content: `Price (${tokenInfo.price})`,
50
- action: "link",
51
- target: tokenInfo.link,
52
- },
53
- ],
54
- image: tokenInfo.image,
55
- };
56
- await context.sendCustomFrame(frame);
57
- }
@@ -1,97 +0,0 @@
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
- }
@@ -1,225 +0,0 @@
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 [domain]",
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 [amount] [token] [username] [address]",
65
- examples: [
66
- "/pay 10 vitalik.eth",
67
- "/pay 1 usdc to 0xc9925662D36DE3e1bF0fD64e779B2e5F0Aead964",
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 "../lib/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
-
@@ -1,4 +0,0 @@
1
- compressionLevel: mixed
2
- enableGlobalCache: false
3
- enableTelemetry: false
4
- nodeLinker: node-modules
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes