create-message-kit 1.0.15 → 1.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/index.js +12 -3
  2. package/package.json +2 -2
  3. package/templates/agent/.env.example +2 -0
  4. package/{examples/one-to-one → templates/agent}/package.json +2 -4
  5. package/templates/agent/src/handler/ens.ts +211 -0
  6. package/templates/agent/src/index.ts +14 -0
  7. package/{examples/group → templates/agent}/src/lib/openai.ts +42 -4
  8. package/templates/agent/src/lib/resolver.ts +126 -0
  9. package/templates/agent/src/prompt.ts +81 -0
  10. package/templates/agent/src/skills.ts +93 -0
  11. package/templates/gm/.env.example +1 -0
  12. package/{examples → templates}/gm/package.json +1 -0
  13. package/templates/group/.env.example +3 -0
  14. package/{examples → templates}/group/package.json +1 -0
  15. package/{examples → templates}/group/src/handler/agent.ts +18 -10
  16. package/{examples → templates}/group/src/handler/game.ts +2 -2
  17. package/{examples → templates}/group/src/handler/loyalty.ts +3 -11
  18. package/{examples → templates}/group/src/handler/splitpayment.ts +18 -11
  19. package/{examples → templates}/group/src/handler/tipping.ts +10 -6
  20. package/{examples → templates}/group/src/handler/transaction.ts +11 -40
  21. package/templates/group/src/index.ts +57 -0
  22. package/templates/group/src/lib/openai.ts +137 -0
  23. package/templates/group/src/lib/resolver.ts +126 -0
  24. package/{examples/group/src/commands.ts → templates/group/src/skills.ts} +24 -26
  25. package/examples/gm/.env.example +0 -1
  26. package/examples/group/.env.example +0 -3
  27. package/examples/group/src/index.ts +0 -68
  28. package/examples/one-to-one/.env.example +0 -2
  29. package/examples/one-to-one/src/index.ts +0 -72
  30. package/examples/one-to-one/src/lib/cron.ts +0 -34
  31. package/examples/one-to-one/src/lib/redis.ts +0 -15
  32. /package/{examples → templates}/gm/src/index.ts +0 -0
  33. /package/{examples → templates}/group/src/lib/stack.ts +0 -0
@@ -11,6 +11,7 @@
11
11
  "@xmtp/message-kit": "workspace:*"
12
12
  },
13
13
  "devDependencies": {
14
+ "@types/node": "^20.14.2",
14
15
  "nodemon": "^3.1.3",
15
16
  "typescript": "^5.4.5"
16
17
  },
@@ -0,0 +1,3 @@
1
+ KEY= # the private key of the bot wallet
2
+ OPEN_AI_API_KEY= # openai api key
3
+ STACK_API_KEY= # stack api key
@@ -13,6 +13,7 @@
13
13
  "openai": "^4.52.0"
14
14
  },
15
15
  "devDependencies": {
16
+ "@types/node": "^20.14.2",
16
17
  "nodemon": "^3.1.3",
17
18
  "typescript": "^5.4.5"
18
19
  },
@@ -1,14 +1,15 @@
1
- import { HandlerContext, User } from "@xmtp/message-kit";
1
+ import { HandlerContext, AbstractedMember } from "@xmtp/message-kit";
2
2
  import { textGeneration } from "../lib/openai.js";
3
3
 
4
4
  export async function handler(context: HandlerContext) {
5
5
  if (!process?.env?.OPEN_AI_API_KEY) {
6
- console.log("No OPEN_AI_API_KEY found in .env");
6
+ console.warn("No OPEN_AI_API_KEY found in .env");
7
7
  return;
8
8
  }
9
9
 
10
10
  const {
11
11
  message: {
12
+ sender,
12
13
  content: { content, params },
13
14
  },
14
15
  } = context;
@@ -16,11 +17,13 @@ export async function handler(context: HandlerContext) {
16
17
  const systemPrompt = generateSystemPrompt(context);
17
18
  try {
18
19
  let userPrompt = params?.prompt ?? content;
19
- console.log("userPrompt", userPrompt);
20
20
 
21
- const { reply } = await textGeneration(userPrompt, systemPrompt);
22
- console.log("intent:", reply);
23
- context.intent(reply);
21
+ const { reply } = await textGeneration(
22
+ sender.address,
23
+ userPrompt,
24
+ systemPrompt,
25
+ );
26
+ context.skill(reply);
24
27
  } catch (error) {
25
28
  console.error("Error during OpenAI call:", error);
26
29
  await context.reply("An error occurred while processing your request.");
@@ -30,7 +33,7 @@ export async function handler(context: HandlerContext) {
30
33
  function generateSystemPrompt(context: HandlerContext) {
31
34
  const {
32
35
  members,
33
- commands,
36
+ skills,
34
37
  message: { sender },
35
38
  } = context;
36
39
 
@@ -39,10 +42,15 @@ function generateSystemPrompt(context: HandlerContext) {
39
42
 
40
43
  You are a helpful bot agent that lives inside a web3 messaging group that helps interpret user requests and execute commands.
41
44
  #### Users
42
- ${JSON.stringify(members?.map((member: User) => ({ ...member, username: `@${member.username}` })))}\n
45
+ ${JSON.stringify(
46
+ members?.map((member: AbstractedMember) => ({
47
+ ...member,
48
+ username: `@${member.accountAddresses[0]}`,
49
+ })),
50
+ )}\n
43
51
  #### Commands
44
- ${JSON.stringify(commands)}\n
45
- The message was sent by @${sender?.username}
52
+ ${JSON.stringify(skills)}\n
53
+ The message was sent by @${sender?.address}
46
54
 
47
55
  ### Examples
48
56
  prompt /agent tip alix and bo
@@ -17,8 +17,8 @@ export async function handler(context: HandlerContext) {
17
17
  }
18
18
  // URLs for each game type
19
19
  const gameUrls: { [key: string]: string } = {
20
- wordle: "https://framedl.xyz/",
21
- slot: "https://slot-machine-frame.vercel.app/",
20
+ wordle: "https://framedl.xyz",
21
+ slot: "https://slot-machine-frame.vercel.app",
22
22
  };
23
23
  // Respond with the appropriate game URL or an error message
24
24
  switch (params.game) {
@@ -1,4 +1,4 @@
1
- import { HandlerContext, User } from "@xmtp/message-kit";
1
+ import { HandlerContext, AbstractedMember } from "@xmtp/message-kit";
2
2
  import { getStackClient } from "../lib/stack.js";
3
3
 
4
4
  export async function handler(context: HandlerContext, fake?: boolean) {
@@ -6,15 +6,8 @@ export async function handler(context: HandlerContext, fake?: boolean) {
6
6
  const {
7
7
  members,
8
8
  group,
9
- getMessageById,
10
- message: {
11
- content,
12
- content: { command },
13
- sender,
14
- typeId,
15
- },
9
+ message: { sender, typeId, content },
16
10
  } = context;
17
- console.log(command);
18
11
  if (typeId === "text" && group) {
19
12
  const { command } = content;
20
13
  if (command === "points") {
@@ -40,14 +33,13 @@ export async function handler(context: HandlerContext, fake?: boolean) {
40
33
  } else if (typeId === "group_updated" && group) {
41
34
  const { initiatedByInboxId, addedInboxes } = content;
42
35
  const adminAddress = members?.find(
43
- (member: User) => member.inboxId === initiatedByInboxId,
36
+ (member: AbstractedMember) => member.inboxId === initiatedByInboxId,
44
37
  );
45
38
  if (addedInboxes && addedInboxes.length > 0) {
46
39
  //if add someone to the group
47
40
  await stack?.track("referral", {
48
41
  points: 10,
49
42
  account: adminAddress?.address ?? "",
50
- uniqueId: adminAddress?.username ?? "",
51
43
  });
52
44
  }
53
45
  }
@@ -1,14 +1,15 @@
1
1
  import { HandlerContext } from "@xmtp/message-kit";
2
2
  import { vision, textGeneration } from "../lib/openai.js";
3
+ import { getUserInfo } from "../lib/resolver.js";
3
4
 
4
5
  export async function handler(context: HandlerContext) {
5
6
  if (!process?.env?.OPEN_AI_API_KEY) {
6
- console.log("No OPEN_AI_API_KEY found in .env");
7
+ console.warn("No OPEN_AI_API_KEY found in .env");
7
8
  return;
8
9
  }
9
10
  const {
10
11
  members,
11
- commands,
12
+ skills,
12
13
  message: {
13
14
  typeId,
14
15
  content: { attachment },
@@ -16,8 +17,12 @@ export async function handler(context: HandlerContext) {
16
17
  },
17
18
  } = context;
18
19
 
20
+ if (!members) {
21
+ return;
22
+ }
23
+ let senderInfo = await getUserInfo(sender.address);
19
24
  if (attachment && typeId === "remoteStaticAttachment") {
20
- const { data, filename, mimeType } = attachment;
25
+ const { data } = attachment;
21
26
  const response = await vision(
22
27
  data,
23
28
  "This image is the bill of a restaurant dinner. Return the total. If you can't find the total, return 'undefined'.",
@@ -31,9 +36,6 @@ export async function handler(context: HandlerContext) {
31
36
  }
32
37
  if (response) {
33
38
  const prompt = `You a split wise agent that splits the bill between the members of this group except for the sender and bot.\n
34
- These are the users of the group: ${JSON.stringify(members?.map((member) => ({ ...member, username: `@${member.username}` })))}\n
35
- This group app has many commands available: ${JSON.stringify(commands)}\n
36
-
37
39
 
38
40
  ## Instructions:
39
41
  When you receive the totals you should split the bill between the members of the group and send to each one a transaction frame
@@ -44,18 +46,23 @@ export async function handler(context: HandlerContext) {
44
46
  Example:
45
47
  [
46
48
  "This are the details: Total: $49.52. Tip (20%): $9.90",
47
- "All users owe X USDC to @${sender?.username}. Pay here:",
48
- "/send @${sender?.username} $9.90"
49
+ "All users owe X USDC to @${senderInfo?.converseUsername}. Pay here:",
50
+ "/send @${senderInfo?.converseUsername} $9.90"
49
51
  ]
50
52
  `;
51
53
 
52
54
  //I want the reply to be an array of messages so the bot feels like is sending multuple ones
53
- const { reply } = await textGeneration(response, prompt);
55
+ const { reply } = await textGeneration(
56
+ sender.address,
57
+ response,
58
+ prompt,
59
+ true,
60
+ );
54
61
  let splitMessages = JSON.parse(reply);
55
62
  for (const message of splitMessages) {
56
63
  let msg = message as string;
57
- if (msg.startsWith("/")) await context.intent(msg);
58
- else await context.reply(msg);
64
+ if (msg.startsWith("/")) await context.skill(msg);
65
+ else await context.send(msg);
59
66
  }
60
67
  }
61
68
  }
@@ -1,4 +1,5 @@
1
- import { HandlerContext, User } from "@xmtp/message-kit";
1
+ import { HandlerContext, AbstractedMember } from "@xmtp/message-kit";
2
+ import { getUserInfo } from "../lib/resolver.js";
2
3
 
3
4
  export async function handler(context: HandlerContext) {
4
5
  const {
@@ -6,16 +7,15 @@ export async function handler(context: HandlerContext) {
6
7
  getMessageById,
7
8
  message: { content, sender, typeId },
8
9
  } = context;
10
+ console.log(sender);
9
11
  const msg = await getMessageById(content.reference);
10
12
  const replyReceiver = members?.find(
11
13
  (member) => member.inboxId === msg?.senderInboxId,
12
14
  );
13
15
  let amount: number = 0,
14
- receivers: User[] = [];
16
+ receivers: AbstractedMember[] = [];
15
17
  // Handle different types of messages
16
18
  if (typeId === "reply" && replyReceiver) {
17
- // Process reply messages/
18
- //ha
19
19
  const { content: reply } = content;
20
20
 
21
21
  if (reply.includes("degen")) {
@@ -31,18 +31,22 @@ export async function handler(context: HandlerContext) {
31
31
  // Process text commands starting with "/tip"
32
32
  const {
33
33
  params: { amount: extractedAmount, username },
34
- content: text,
35
34
  } = content;
36
35
  amount = extractedAmount || 10; // Default amount if not specified
37
- receivers = username; // Extract receiver from parameters
36
+
37
+ receivers = await Promise.all(
38
+ username.map((username: string) => getUserInfo(username)),
39
+ );
38
40
  }
39
41
  }
40
42
  if (!sender || receivers.length === 0 || amount === 0) {
41
43
  context.reply("Sender or receiver or amount not found.");
42
44
  return;
43
45
  }
46
+ console.log(receivers);
44
47
  const receiverAddresses = receivers.map((receiver) => receiver.address);
45
48
  // Process sending tokens to each receiver
49
+
46
50
  context.sendTo(
47
51
  `You received ${amount} tokens from ${sender.address}.`,
48
52
  receiverAddresses,
@@ -1,4 +1,5 @@
1
1
  import { HandlerContext } from "@xmtp/message-kit";
2
+ import { getUserInfo } from "../lib/resolver.js";
2
3
 
3
4
  // Main handler function for processing commands
4
5
  export async function handler(context: HandlerContext) {
@@ -7,26 +8,23 @@ export async function handler(context: HandlerContext) {
7
8
  content: { command, params },
8
9
  },
9
10
  } = context;
10
- const baseUrl = "https://base-frame-lyart.vercel.app/transaction";
11
+ const baseUrl = "https://base-tx-frame.vercel.app/transaction";
11
12
 
12
13
  switch (command) {
13
14
  case "send":
14
15
  // Destructure and validate parameters for the send command
15
16
  const { amount: amountSend, token: tokenSend, username } = params; // [!code hl] // [!code focus]
16
-
17
- if (!amountSend || !tokenSend || !username) {
17
+ let senderInfo = await getUserInfo(username);
18
+ if (!amountSend || !tokenSend || !senderInfo) {
18
19
  context.reply(
19
20
  "Missing required parameters. Please provide amount, token, and username.",
20
21
  );
21
22
  return;
22
23
  }
23
- // Generate URL for the send transaction
24
- let url_send = generateFrameURL(baseUrl, "send", {
25
- amount: amountSend,
26
- token: tokenSend,
27
- receiver: username[0]?.address,
28
- });
29
- context.reply(`${url_send}`);
24
+ let name = senderInfo.converseUsername || senderInfo.address;
25
+
26
+ let sendUrl = `${baseUrl}/?transaction_type=send&amount=${amountSend}&token=${tokenSend}&receiver=${senderInfo.address}`;
27
+ context.send(`${sendUrl}`);
30
28
  break;
31
29
  case "swap":
32
30
  // Destructure and validate parameters for the swap command
@@ -38,13 +36,9 @@ export async function handler(context: HandlerContext) {
38
36
  );
39
37
  return;
40
38
  }
41
- // Generate URL for the swap transaction
42
- let url_swap = generateFrameURL(baseUrl, "swap", {
43
- amount,
44
- token_from,
45
- token_to,
46
- });
47
- context.reply(`${url_swap}`);
39
+
40
+ let swapUrl = `${baseUrl}/?transaction_type=swap&token_from=${token_from}&token_to=${token_to}&amount=${amount}`;
41
+ context.send(`${swapUrl}`);
48
42
  break;
49
43
  case "show": // [!code hl] // [!code focus]
50
44
  // Show the base URL without the transaction path
@@ -55,26 +49,3 @@ export async function handler(context: HandlerContext) {
55
49
  context.reply("Unknown command. Use help to see all available commands.");
56
50
  }
57
51
  }
58
-
59
- // Function to generate a URL with query parameters for transactions
60
- function generateFrameURL(
61
- baseUrl: string,
62
- transaction_type: string,
63
- params: { [key: string]: string | number | string[] | undefined },
64
- ) {
65
- // Filter out undefined parameters
66
- let filteredParams: {
67
- [key: string]: string | number | string[] | undefined;
68
- } = {};
69
-
70
- for (const key in params) {
71
- if (params[key] !== undefined) {
72
- filteredParams[key] = params[key];
73
- }
74
- }
75
- let queryParams = new URLSearchParams({
76
- transaction_type,
77
- ...filteredParams,
78
- }).toString();
79
- return `${baseUrl}?${queryParams}`;
80
- }
@@ -0,0 +1,57 @@
1
+ import { run, HandlerContext } from "@xmtp/message-kit";
2
+ import { handler as splitpayment } from "./handler/splitpayment.js";
3
+
4
+ // Main function to run the app
5
+ run(
6
+ async (context: HandlerContext) => {
7
+ const {
8
+ message: { typeId },
9
+ } = context;
10
+ switch (typeId) {
11
+ case "reply":
12
+ handleReply(context);
13
+ break;
14
+ case "remoteStaticAttachment":
15
+ handleAttachment(context);
16
+ break;
17
+ }
18
+ if (!context.group) {
19
+ context.send("This is a group bot, add this address to a group");
20
+ }
21
+ },
22
+ { attachments: true },
23
+ );
24
+ async function handleReply(context: HandlerContext) {
25
+ const {
26
+ v2client,
27
+ getReplyChain,
28
+ version,
29
+ message: {
30
+ content: { reference },
31
+ },
32
+ } = context;
33
+
34
+ const { chain, isSenderInChain } = await getReplyChain(
35
+ reference,
36
+ version,
37
+ v2client.address,
38
+ );
39
+ //await context.skill(chain);
40
+ }
41
+
42
+ // Handle attachment messages
43
+ async function handleAttachment(context: HandlerContext) {
44
+ await splitpayment(context);
45
+ }
46
+
47
+ export async function helpHandler(context: HandlerContext) {
48
+ const { skills } = context;
49
+ const intro =
50
+ "Available experiences:\n" +
51
+ skills
52
+ ?.flatMap((app) => app.skills)
53
+ .map((skill) => `${skill.command} - ${skill.description}`)
54
+ .join("\n") +
55
+ "\nUse these commands to interact with specific apps.";
56
+ context.send(intro);
57
+ }
@@ -0,0 +1,137 @@
1
+ import dotenv from "dotenv";
2
+ dotenv.config();
3
+
4
+ import OpenAI from "openai";
5
+ const openai = new OpenAI({
6
+ apiKey: process.env.OPEN_AI_API_KEY,
7
+ });
8
+
9
+ export type ChatHistoryEntry = { role: string; content: string };
10
+ export type ChatHistories = Record<string, ChatHistoryEntry[]>;
11
+
12
+ let chatHistories: ChatHistories = {};
13
+ export async function textGeneration(
14
+ address: string,
15
+ userPrompt: string,
16
+ systemPrompt: string,
17
+ isGroup: boolean = false,
18
+ ) {
19
+ let messages = chatHistories[address] || [];
20
+ if (messages.length === 0) {
21
+ messages.push({
22
+ role: "system",
23
+ content: systemPrompt,
24
+ });
25
+ }
26
+ messages.push({
27
+ role: "user",
28
+ content: userPrompt,
29
+ });
30
+ try {
31
+ const response = await openai.chat.completions.create({
32
+ model: "gpt-4o",
33
+ messages: messages as any,
34
+ });
35
+ const reply = response.choices[0].message.content;
36
+ messages.push({
37
+ role: "assistant",
38
+ content: reply || "No response from OpenAI.",
39
+ });
40
+ const cleanedReply = responseParser(reply as string);
41
+ if (!isGroup) chatHistories[address] = messages;
42
+ return { reply: cleanedReply, history: messages };
43
+ } catch (error) {
44
+ console.error("Failed to fetch from OpenAI:", error);
45
+ throw error;
46
+ }
47
+ }
48
+
49
+ // New method to interpret an image
50
+ export async function vision(imageData: Uint8Array, systemPrompt: string) {
51
+ const base64Image = Buffer.from(imageData).toString("base64");
52
+ const dataUrl = `data:image/jpeg;base64,${base64Image}`;
53
+
54
+ // Create a new thread for each vision request
55
+ const visionMessages = [
56
+ {
57
+ role: "system",
58
+ content: systemPrompt,
59
+ },
60
+ {
61
+ role: "user",
62
+ content: [
63
+ { type: "text", text: systemPrompt },
64
+ {
65
+ type: "image_url",
66
+ image_url: {
67
+ url: dataUrl,
68
+ },
69
+ },
70
+ ],
71
+ },
72
+ ];
73
+
74
+ try {
75
+ const response = await openai.chat.completions.create({
76
+ model: "gpt-4o",
77
+ messages: visionMessages as any,
78
+ });
79
+ return response.choices[0].message.content;
80
+ } catch (error) {
81
+ console.error("Failed to interpret image with OpenAI:", error);
82
+ throw error;
83
+ }
84
+ }
85
+
86
+ export async function processResponseWithskill(
87
+ address: string,
88
+ reply: string,
89
+ context: any,
90
+ ) {
91
+ let messages = reply
92
+ .split("\n")
93
+ .map((message: string) => responseParser(message))
94
+ .filter((message): message is string => message.length > 0);
95
+
96
+ console.log(messages);
97
+ for (const message of messages) {
98
+ if (message.startsWith("/")) {
99
+ const response = await context.skill(message);
100
+ if (response && response.message) {
101
+ let msg = responseParser(response.message);
102
+
103
+ chatHistories[address].push({
104
+ role: "system",
105
+ content: msg,
106
+ });
107
+
108
+ await context.send(response.message);
109
+ }
110
+ } else {
111
+ await context.send(message);
112
+ }
113
+ }
114
+ }
115
+ export function responseParser(message: string) {
116
+ let trimmedMessage = message;
117
+ // Remove bold and underline markdown
118
+ trimmedMessage = trimmedMessage?.replace(/(\*\*|__)(.*?)\1/g, "$2");
119
+ // Remove markdown links, keeping only the URL
120
+ trimmedMessage = trimmedMessage?.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$2");
121
+ // Remove markdown headers
122
+ trimmedMessage = trimmedMessage?.replace(/^#+\s*(.*)$/gm, "$1");
123
+ // Remove inline code formatting
124
+ trimmedMessage = trimmedMessage?.replace(/`([^`]+)`/g, "$1");
125
+ // Remove single backticks at the start or end of the message
126
+ trimmedMessage = trimmedMessage?.replace(/^`|`$/g, "");
127
+ // Remove leading and trailing whitespace
128
+ trimmedMessage = trimmedMessage?.replace(/^\s+|\s+$/g, "");
129
+ // Remove any remaining leading or trailing whitespace
130
+ trimmedMessage = trimmedMessage.trim();
131
+
132
+ return trimmedMessage;
133
+ }
134
+
135
+ export const clearChatHistories = () => {
136
+ chatHistories = {};
137
+ };
@@ -0,0 +1,126 @@
1
+ import { Client } from "@xmtp/xmtp-js";
2
+ import { isAddress } from "viem";
3
+
4
+ export const converseEndpointURL =
5
+ "https://converse-website-git-endpoit-ephemerahq.vercel.app";
6
+ //export const converseEndpointURL = "http://localhost:3000";
7
+
8
+ export type InfoCache = Map<string, UserInfo>;
9
+ export type ConverseProfile = {
10
+ address: string | null;
11
+ onXmtp: boolean;
12
+ avatar: string | null;
13
+ formattedName: string | null;
14
+ name: string | null;
15
+ };
16
+ export type UserInfo = {
17
+ ensDomain?: string | undefined;
18
+ address?: string | undefined;
19
+ converseUsername?: string | undefined;
20
+ ensInfo?: EnsData | undefined;
21
+ avatar?: string | undefined;
22
+ };
23
+ export interface EnsData {
24
+ address?: string;
25
+ avatar?: string;
26
+ avatar_small?: string;
27
+ converse?: string;
28
+ avatar_url?: string;
29
+ contentHash?: string;
30
+ description?: string;
31
+ ens?: string;
32
+ ens_primary?: string;
33
+ github?: string;
34
+ resolverAddress?: string;
35
+ twitter?: string;
36
+ url?: string;
37
+ wallets?: {
38
+ eth?: string;
39
+ };
40
+ }
41
+
42
+ let infoCache: InfoCache = new Map();
43
+
44
+ export const clearInfoCache = () => {
45
+ infoCache.clear();
46
+ };
47
+ export const getUserInfo = async (
48
+ key: string,
49
+ clientAddress?: string,
50
+ ): Promise<UserInfo | null> => {
51
+ let data: UserInfo = infoCache.get(key) || {
52
+ ensDomain: undefined,
53
+ address: undefined,
54
+ converseUsername: undefined,
55
+ ensInfo: undefined,
56
+ };
57
+ //console.log("Getting user info", key, clientAddress);
58
+ if (isAddress(clientAddress || "")) {
59
+ data.address = clientAddress;
60
+ } else if (isAddress(key || "")) {
61
+ data.address = key;
62
+ } else if (key.includes(".eth")) {
63
+ data.ensDomain = key;
64
+ } else if (key == "@user" || key == "@me" || key == "@bot") {
65
+ data.address = clientAddress;
66
+ data.ensDomain = key.replace("@", "") + ".eth";
67
+ data.converseUsername = key.replace("@", "");
68
+ } else if (key == "@alix") {
69
+ data.address = "0x3a044b218BaE80E5b9E16609443A192129A67BeA";
70
+ data.converseUsername = "alix";
71
+ } else if (key == "@bo") {
72
+ data.address = "0xbc3246461ab5e1682baE48fa95172CDf0689201a";
73
+ data.converseUsername = "bo";
74
+ } else {
75
+ data.converseUsername = key;
76
+ }
77
+
78
+ let keyToUse = data.address || data.ensDomain || data.converseUsername;
79
+ let cacheData = keyToUse && infoCache.get(keyToUse);
80
+ if (cacheData) {
81
+ //console.log("Getting user info", keyToUse, cacheData);
82
+ return cacheData;
83
+ } else {
84
+ //console.log("Getting user info", keyToUse, data);
85
+ }
86
+
87
+ if (keyToUse?.includes(".eth")) {
88
+ const response = await fetch(`https://ensdata.net/${keyToUse}`);
89
+ const ensData: EnsData = (await response.json()) as EnsData;
90
+ //console.log("Ens data", ensData);
91
+ if (ensData) {
92
+ data.ensInfo = ensData;
93
+ data.ensDomain = ensData?.ens;
94
+ data.address = ensData?.address;
95
+ }
96
+ } else if (keyToUse) {
97
+ keyToUse = keyToUse.replace("@", "");
98
+ const response = await fetch(`${converseEndpointURL}/profile/${keyToUse}`, {
99
+ method: "POST",
100
+ headers: {
101
+ "Content-Type": "application/json",
102
+ Accept: "application/json",
103
+ },
104
+ body: JSON.stringify({
105
+ peer: keyToUse,
106
+ }),
107
+ });
108
+ const converseData = (await response.json()) as ConverseProfile;
109
+ if (process.env.MSG_LOG)
110
+ console.log("Converse data", keyToUse, converseData);
111
+ data.converseUsername =
112
+ converseData?.formattedName || converseData?.name || undefined;
113
+ data.address = converseData?.address || undefined;
114
+ data.avatar = converseData?.avatar || undefined;
115
+ }
116
+ if (data.address) infoCache.set(data.address, data);
117
+ return data;
118
+ };
119
+ export const isOnXMTP = async (
120
+ client: Client,
121
+ domain: string | undefined,
122
+ address: string | undefined,
123
+ ) => {
124
+ if (domain == "fabri.eth") return false;
125
+ if (address) return (await client.canMessage([address])).length > 0;
126
+ };