create-message-kit 1.0.16 → 1.0.19
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 +10 -10
- package/package.json +1 -1
- package/templates/agent/src/handler/ens.ts +105 -141
- package/templates/agent/src/index.ts +5 -1
- package/templates/agent/src/lib/openai.ts +44 -29
- package/templates/agent/src/lib/resolver.ts +107 -63
- package/templates/agent/src/prompt.ts +31 -53
- package/templates/agent/src/skills.ts +95 -0
- package/templates/gm/src/index.ts +6 -13
- package/templates/group/src/handler/agent.ts +17 -10
- package/templates/group/src/handler/loyalty.ts +3 -11
- package/templates/group/src/handler/splitpayment.ts +17 -10
- package/templates/group/src/handler/tipping.ts +10 -6
- package/templates/group/src/handler/transaction.ts +10 -39
- package/templates/group/src/index.ts +24 -20
- package/templates/group/src/lib/openai.ts +44 -3
- package/templates/group/src/lib/resolver.ts +126 -0
- package/templates/group/src/{commands.ts → skills.ts} +17 -16
- package/templates/agent/src/commands.ts +0 -69
- package/templates/agent/src/lib/types.ts +0 -33
- package/templates/gm/src/commands.ts +0 -18
- package/templates/gm/src/handler.ts +0 -6
package/index.js
CHANGED
@@ -19,16 +19,16 @@ program
|
|
19
19
|
.name("byob")
|
20
20
|
.description("CLI to initialize projects")
|
21
21
|
.action(async () => {
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
22
|
+
const coolLogo = `\x1b[38;2;250;105;119m\
|
23
|
+
|
24
|
+
███╗ ███╗███████╗███████╗███████╗ █████╗ ██████╗ ███████╗██╗ ██╗██╗████████╗
|
25
|
+
████╗ ████║██╔════╝██╔════╝██╔════╝██╔══██╗██╔════╝ ██╔════╝██║ ██╔╝██║╚══██╔══╝
|
26
|
+
██╔████╔██║█████╗ ███████╗███████╗███████║██║ ███╗█████╗ █████╔╝ ██║ ██║
|
27
|
+
██║╚██╔╝██║██╔══╝ ╚════██║╚════██║██╔══██║██║ ██║██╔══╝ ██╔═██╗ ██║ ██║
|
28
|
+
██║ ╚═╝ ██║███████╗███████║███████║██║ ██║╚██████╔╝███████╗██║ ██╗██║ ██║
|
29
|
+
╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝
|
30
|
+
Powered by XMTP \x1b[0m`;
|
31
|
+
console.log(coolLogo);
|
32
32
|
|
33
33
|
const { templateType, displayName, destDir } = await gatherProjectInfo();
|
34
34
|
|
package/package.json
CHANGED
@@ -1,52 +1,42 @@
|
|
1
1
|
import { HandlerContext } from "@xmtp/message-kit";
|
2
|
-
import {
|
3
|
-
import {
|
4
|
-
import {
|
2
|
+
import { getUserInfo, clearInfoCache, isOnXMTP } from "../lib/resolver.js";
|
3
|
+
import { textGeneration } from "../lib/openai.js";
|
4
|
+
import { processResponseWithSkill } from "../lib/openai.js";
|
5
|
+
import { isAddress } from "viem";
|
5
6
|
import { ens_agent_prompt } from "../prompt.js";
|
6
|
-
import
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
} from "../lib/types.js";
|
13
|
-
import { frameUrl, ensUrl, baseTxUrl, InfoCache } from "../lib/types.js";
|
14
|
-
|
15
|
-
let tipAddress: tipAddress = {};
|
16
|
-
let tipDomain: tipDomain = {};
|
17
|
-
let ensDomain: ensDomain = {};
|
18
|
-
let infoCache: InfoCache = {};
|
19
|
-
let converseUsername: converseUsername = {};
|
20
|
-
let chatHistories: chatHistories = {};
|
21
|
-
|
22
|
-
// URL for the send transaction
|
7
|
+
import { clearChatHistories } from "../lib/openai.js";
|
8
|
+
|
9
|
+
export const frameUrl = "https://ens.steer.fun/";
|
10
|
+
export const ensUrl = "https://app.ens.domains/";
|
11
|
+
export const baseTxUrl = "https://base-tx-frame.vercel.app";
|
12
|
+
|
23
13
|
export async function handleEns(context: HandlerContext) {
|
24
14
|
const {
|
25
15
|
message: {
|
26
16
|
content: { command, params, sender },
|
27
17
|
},
|
28
18
|
} = context;
|
29
|
-
|
30
|
-
|
19
|
+
if (command == "reset") {
|
20
|
+
clear();
|
21
|
+
return { code: 200, message: "Conversation reset." };
|
22
|
+
} else if (command == "renew") {
|
31
23
|
// Destructure and validate parameters for the ens command
|
32
24
|
const { domain } = params;
|
33
25
|
// Check if the user holds the domain
|
34
26
|
if (!domain) {
|
35
|
-
|
36
|
-
|
27
|
+
return {
|
28
|
+
code: 400,
|
29
|
+
message: "Missing required parameters. Please provide domain.",
|
30
|
+
};
|
37
31
|
}
|
38
32
|
|
39
|
-
const
|
40
|
-
domain,
|
41
|
-
infoCache,
|
42
|
-
);
|
43
|
-
infoCache = retrievedInfoCache;
|
44
|
-
let data = infoCache[domain].info;
|
33
|
+
const data = await getUserInfo(domain);
|
45
34
|
|
46
|
-
if (data?.address !== sender?.address) {
|
35
|
+
if (!data || data?.address !== sender?.address) {
|
47
36
|
return {
|
48
37
|
code: 403,
|
49
|
-
message:
|
38
|
+
message:
|
39
|
+
"Looks like this domain is not registered to you. Only the owner can renew it.",
|
50
40
|
};
|
51
41
|
}
|
52
42
|
|
@@ -69,22 +59,23 @@ export async function handleEns(context: HandlerContext) {
|
|
69
59
|
} else if (command == "info") {
|
70
60
|
const { domain } = params;
|
71
61
|
|
72
|
-
const
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
62
|
+
const data = await getUserInfo(domain);
|
63
|
+
if (!data) {
|
64
|
+
return {
|
65
|
+
code: 404,
|
66
|
+
message: "Domain not found.",
|
67
|
+
};
|
68
|
+
}
|
78
69
|
|
79
70
|
const formattedData = {
|
80
71
|
Address: data?.address,
|
81
|
-
"Avatar URL": data?.
|
82
|
-
Description: data?.description,
|
83
|
-
ENS: data?.
|
84
|
-
"Primary ENS": data?.ens_primary,
|
85
|
-
GitHub: data?.github,
|
86
|
-
Resolver: data?.resolverAddress,
|
87
|
-
Twitter: data?.twitter,
|
72
|
+
"Avatar URL": data?.ensInfo?.avatar,
|
73
|
+
Description: data?.ensInfo?.description,
|
74
|
+
ENS: data?.ensDomain,
|
75
|
+
"Primary ENS": data?.ensInfo?.ens_primary,
|
76
|
+
GitHub: data?.ensInfo?.github,
|
77
|
+
Resolver: data?.ensInfo?.resolverAddress,
|
78
|
+
Twitter: data?.ensInfo?.twitter,
|
88
79
|
URL: `${ensUrl}${domain}`,
|
89
80
|
};
|
90
81
|
|
@@ -96,23 +87,20 @@ export async function handleEns(context: HandlerContext) {
|
|
96
87
|
}
|
97
88
|
message += `\n\nWould you like to tip the domain owner for getting there first 🤣?`;
|
98
89
|
message = message.trim();
|
99
|
-
if (
|
100
|
-
|
90
|
+
if (
|
91
|
+
await isOnXMTP(
|
92
|
+
context.v2client,
|
93
|
+
data?.ensInfo?.ens,
|
94
|
+
data?.ensInfo?.address,
|
95
|
+
)
|
96
|
+
) {
|
97
|
+
await context.send(
|
101
98
|
`Ah, this domains is in XMTP, you can message it directly: https://converse.xyz/dm/${domain}`,
|
102
99
|
);
|
103
100
|
}
|
104
101
|
return { code: 200, message };
|
105
102
|
} else if (command == "check") {
|
106
|
-
|
107
|
-
const { domain, cool_alternatives } = params;
|
108
|
-
|
109
|
-
const cool_alternativesFormat = cool_alternatives
|
110
|
-
?.split(",")
|
111
|
-
.map(
|
112
|
-
(alternative: string, index: number) =>
|
113
|
-
`${index + 1}. ${alternative} ✨`,
|
114
|
-
)
|
115
|
-
.join("\n");
|
103
|
+
const { domain } = params;
|
116
104
|
|
117
105
|
if (!domain) {
|
118
106
|
return {
|
@@ -120,82 +108,46 @@ export async function handleEns(context: HandlerContext) {
|
|
120
108
|
message: "Please provide a domain name to check.",
|
121
109
|
};
|
122
110
|
}
|
123
|
-
|
124
|
-
|
125
|
-
infoCache,
|
126
|
-
);
|
127
|
-
infoCache = retrievedInfoCache;
|
128
|
-
let data = infoCache?.[domain]?.info;
|
111
|
+
|
112
|
+
const data = await getUserInfo(domain);
|
129
113
|
if (!data?.address) {
|
130
|
-
let message = `Looks like ${domain} is available!
|
114
|
+
let message = `Looks like ${domain} is available! Here you can register it: ${ensUrl}${domain} or would you like to see some cool alternatives?`;
|
131
115
|
return {
|
132
116
|
code: 200,
|
133
117
|
message,
|
134
118
|
};
|
135
119
|
} else {
|
136
|
-
let message = `Looks like ${domain} is already registered
|
120
|
+
let message = `Looks like ${domain} is already registered!`;
|
121
|
+
await context.skill("/cool " + domain);
|
137
122
|
return {
|
138
123
|
code: 404,
|
139
124
|
message,
|
140
125
|
};
|
141
126
|
}
|
142
127
|
} else if (command == "tip") {
|
143
|
-
// Destructure and validate parameters for the send command
|
144
128
|
const { address } = params;
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
infoCache = retrievedInfoCache;
|
151
|
-
let data = infoCache[address].info;
|
152
|
-
|
153
|
-
tipAddress[sender.address] = data?.address;
|
154
|
-
tipDomain[sender.address] = data?.ens;
|
155
|
-
|
156
|
-
if (!address || !tipAddress[sender.address]) {
|
157
|
-
context.reply("Missing required parameters. Please provide address.");
|
158
|
-
return;
|
129
|
+
if (!address) {
|
130
|
+
return {
|
131
|
+
code: 400,
|
132
|
+
message: "Please provide an address to tip.",
|
133
|
+
};
|
159
134
|
}
|
160
|
-
|
161
|
-
|
162
|
-
|
135
|
+
const data = await getUserInfo(address);
|
136
|
+
let txUrl = `${baseTxUrl}/transaction/?transaction_type=send&buttonName=Tip%20${data?.ensDomain ?? ""}&amount=1&token=USDC&receiver=${
|
137
|
+
isAddress(address) ? address : data?.address
|
138
|
+
}`;
|
139
|
+
console.log(txUrl);
|
140
|
+
return {
|
141
|
+
code: 200,
|
142
|
+
message: txUrl,
|
143
|
+
};
|
163
144
|
} else if (command == "cool") {
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
}
|
171
|
-
|
172
|
-
async function processResponseWithIntent(
|
173
|
-
reply: string,
|
174
|
-
context: any,
|
175
|
-
senderAddress: string,
|
176
|
-
) {
|
177
|
-
let messages = reply
|
178
|
-
.split("\n")
|
179
|
-
.map((message: string) => responseParser(message))
|
180
|
-
.filter((message): message is string => message.length > 0);
|
181
|
-
|
182
|
-
console.log(messages);
|
183
|
-
for (const message of messages) {
|
184
|
-
if (message.startsWith("/")) {
|
185
|
-
const response = await context.intent(message);
|
186
|
-
if (response && response.message) {
|
187
|
-
let msg = responseParser(response.message);
|
188
|
-
|
189
|
-
chatHistories[senderAddress].push({
|
190
|
-
role: "system",
|
191
|
-
content: msg,
|
192
|
-
});
|
193
|
-
|
194
|
-
await context.send(response.message);
|
195
|
-
}
|
196
|
-
} else {
|
197
|
-
await context.send(message);
|
198
|
-
}
|
145
|
+
const { domain } = params;
|
146
|
+
//What about these cool alternatives?\
|
147
|
+
return {
|
148
|
+
code: 200,
|
149
|
+
message: `${generateCoolAlternatives(domain)}`,
|
150
|
+
};
|
199
151
|
}
|
200
152
|
}
|
201
153
|
|
@@ -215,33 +167,45 @@ export async function ensAgent(context: HandlerContext) {
|
|
215
167
|
|
216
168
|
try {
|
217
169
|
let userPrompt = params?.prompt ?? content;
|
218
|
-
const
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
ensDomain[sender.address] = newEnsDomain;
|
226
|
-
converseUsername[sender.address] = newConverseUsername;
|
227
|
-
let txUrl = `${baseTxUrl}/transaction/?transaction_type=send&buttonName=Tip%20${tipDomain[sender.address]}&amount=1&token=USDC&receiver=${tipAddress[sender.address]}`;
|
228
|
-
|
229
|
-
const { reply, history } = await textGeneration(
|
170
|
+
const userInfo = await getUserInfo(sender.address);
|
171
|
+
if (!userInfo) {
|
172
|
+
console.log("User info not found");
|
173
|
+
return;
|
174
|
+
}
|
175
|
+
const { reply } = await textGeneration(
|
176
|
+
sender.address,
|
230
177
|
userPrompt,
|
231
|
-
await ens_agent_prompt(
|
232
|
-
sender.address,
|
233
|
-
ensDomain[sender.address],
|
234
|
-
converseUsername[sender.address],
|
235
|
-
tipAddress[sender.address],
|
236
|
-
txUrl,
|
237
|
-
),
|
238
|
-
chatHistories[sender.address],
|
178
|
+
await ens_agent_prompt(userInfo),
|
239
179
|
);
|
240
|
-
|
241
|
-
|
242
|
-
await processResponseWithIntent(reply, context, sender.address);
|
180
|
+
await processResponseWithSkill(sender.address, reply, context);
|
243
181
|
} catch (error) {
|
244
182
|
console.error("Error during OpenAI call:", error);
|
245
183
|
await context.send("An error occurred while processing your request.");
|
246
184
|
}
|
247
185
|
}
|
186
|
+
|
187
|
+
export const generateCoolAlternatives = (domain: string) => {
|
188
|
+
const suffixes = ["lfg", "cool", "degen", "moon", "base", "gm"];
|
189
|
+
const alternatives = [];
|
190
|
+
for (let i = 0; i < 5; i++) {
|
191
|
+
const randomPosition = Math.random() < 0.5;
|
192
|
+
const baseDomain = domain.replace(/\.eth$/, ""); // Remove any existing .eth suffix
|
193
|
+
alternatives.push(
|
194
|
+
randomPosition
|
195
|
+
? `${suffixes[i]}${baseDomain}.eth`
|
196
|
+
: `${baseDomain}${suffixes[i]}.eth`,
|
197
|
+
);
|
198
|
+
}
|
199
|
+
|
200
|
+
const cool_alternativesFormat = alternatives
|
201
|
+
.map(
|
202
|
+
(alternative: string, index: number) => `${index + 1}. ${alternative} ✨`,
|
203
|
+
)
|
204
|
+
.join("\n");
|
205
|
+
return cool_alternativesFormat;
|
206
|
+
};
|
207
|
+
|
208
|
+
export async function clear() {
|
209
|
+
clearChatHistories();
|
210
|
+
clearInfoCache();
|
211
|
+
}
|
@@ -2,5 +2,9 @@ import { run, HandlerContext } from "@xmtp/message-kit";
|
|
2
2
|
import { ensAgent } from "./handler/ens.js";
|
3
3
|
|
4
4
|
run(async (context: HandlerContext) => {
|
5
|
-
|
5
|
+
/*All the commands are handled through the commands file*/
|
6
|
+
/* If its just text, it will be handled by the ensAgent*/
|
7
|
+
/* If its a group message, it will be handled by the groupAgent*/
|
8
|
+
|
9
|
+
ensAgent(context);
|
6
10
|
});
|
@@ -6,12 +6,16 @@ const openai = new OpenAI({
|
|
6
6
|
apiKey: process.env.OPEN_AI_API_KEY,
|
7
7
|
});
|
8
8
|
|
9
|
+
export type ChatHistoryEntry = { role: string; content: string };
|
10
|
+
export type ChatHistories = Record<string, ChatHistoryEntry[]>;
|
11
|
+
|
12
|
+
let chatHistories: ChatHistories = {};
|
9
13
|
export async function textGeneration(
|
14
|
+
address: string,
|
10
15
|
userPrompt: string,
|
11
16
|
systemPrompt: string,
|
12
|
-
chatHistory?: any[],
|
13
17
|
) {
|
14
|
-
let messages =
|
18
|
+
let messages = chatHistories[address] || [];
|
15
19
|
if (messages.length === 0) {
|
16
20
|
messages.push({
|
17
21
|
role: "system",
|
@@ -33,7 +37,8 @@ export async function textGeneration(
|
|
33
37
|
content: reply || "No response from OpenAI.",
|
34
38
|
});
|
35
39
|
const cleanedReply = responseParser(reply as string);
|
36
|
-
|
40
|
+
chatHistories[address] = messages;
|
41
|
+
console.log("messages.length", messages.length);
|
37
42
|
return { reply: cleanedReply, history: messages };
|
38
43
|
} catch (error) {
|
39
44
|
console.error("Failed to fetch from OpenAI:", error);
|
@@ -78,6 +83,38 @@ export async function vision(imageData: Uint8Array, systemPrompt: string) {
|
|
78
83
|
}
|
79
84
|
}
|
80
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
|
+
if (!chatHistories[address]) {
|
104
|
+
chatHistories[address] = [];
|
105
|
+
}
|
106
|
+
chatHistories[address].push({
|
107
|
+
role: "system",
|
108
|
+
content: msg,
|
109
|
+
});
|
110
|
+
|
111
|
+
await context.send(response.message);
|
112
|
+
}
|
113
|
+
} else {
|
114
|
+
await context.send(message);
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
81
118
|
export function responseParser(message: string) {
|
82
119
|
let trimmedMessage = message;
|
83
120
|
// Remove bold and underline markdown
|
@@ -94,32 +131,10 @@ export function responseParser(message: string) {
|
|
94
131
|
trimmedMessage = trimmedMessage?.replace(/^\s+|\s+$/g, "");
|
95
132
|
// Remove any remaining leading or trailing whitespace
|
96
133
|
trimmedMessage = trimmedMessage.trim();
|
97
|
-
return trimmedMessage;
|
98
|
-
}
|
99
|
-
|
100
|
-
// UNTESTED, recursive response parser
|
101
|
-
export function responseParser2(message: string | string[]): string | string[] {
|
102
|
-
// If message is an array, process each item individually
|
103
|
-
if (Array.isArray(message)) {
|
104
|
-
return message
|
105
|
-
.map((item) => responseParser(item))
|
106
|
-
.flat() // Flatten nested arrays
|
107
|
-
.filter((item: string) => item.length > 0)
|
108
|
-
.filter((item: string) => item !== "`");
|
109
|
-
}
|
110
|
-
let trimmedMessage = message;
|
111
|
-
// Remove bold and underline markdown
|
112
|
-
trimmedMessage = trimmedMessage?.replace(/(\*\*|__)(.*?)\1/g, "$2");
|
113
|
-
// Remove markdown links, keeping only the URL
|
114
|
-
trimmedMessage = trimmedMessage?.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$2");
|
115
|
-
// Remove markdown headers
|
116
|
-
trimmedMessage = trimmedMessage?.replace(/^#+\s*(.*)$/gm, "$1");
|
117
|
-
// Remove inline code formatting
|
118
|
-
trimmedMessage = trimmedMessage?.replace(/(`{1,3})(.*?)\1/g, "$2");
|
119
|
-
// Remove leading and trailing whitespace
|
120
|
-
trimmedMessage = trimmedMessage?.replace(/`/g, ""); // Remove single backticks
|
121
|
-
// Remove any remaining leading or trailing whitespace
|
122
|
-
trimmedMessage = trimmedMessage?.trim();
|
123
134
|
|
124
135
|
return trimmedMessage;
|
125
136
|
}
|
137
|
+
|
138
|
+
export const clearChatHistories = () => {
|
139
|
+
chatHistories = {};
|
140
|
+
};
|
@@ -1,76 +1,120 @@
|
|
1
|
-
import type { EnsData } from "./types.js";
|
2
|
-
import { endpointURL } from "./types.js";
|
3
1
|
import { Client } from "@xmtp/xmtp-js";
|
4
|
-
import {
|
2
|
+
import { isAddress } from "viem";
|
5
3
|
|
6
|
-
export
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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);
|
15
85
|
}
|
16
|
-
|
17
|
-
|
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}`, {
|
18
99
|
method: "POST",
|
19
100
|
headers: {
|
20
101
|
"Content-Type": "application/json",
|
21
102
|
Accept: "application/json",
|
22
103
|
},
|
23
|
-
body: JSON.stringify({
|
104
|
+
body: JSON.stringify({
|
105
|
+
peer: keyToUse,
|
106
|
+
}),
|
24
107
|
});
|
25
|
-
const
|
26
|
-
|
27
|
-
|
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;
|
28
115
|
}
|
29
|
-
|
30
|
-
|
31
|
-
converseUsername,
|
32
|
-
ensDomain,
|
33
|
-
});
|
34
|
-
return { converseUsername: converseUsername, ensDomain: ensDomain };
|
35
|
-
}
|
36
|
-
|
37
|
-
export const getInfoCache = async (
|
38
|
-
key: string, // This can be either domain or address
|
39
|
-
infoCache: InfoCache,
|
40
|
-
): Promise<{ domain: string; info: EnsData; infoCache: InfoCache }> => {
|
41
|
-
if (infoCache[key]) {
|
42
|
-
let data = {
|
43
|
-
domain: key,
|
44
|
-
info: infoCache[key].info,
|
45
|
-
infoCache: infoCache,
|
46
|
-
};
|
47
|
-
return data;
|
48
|
-
}
|
49
|
-
try {
|
50
|
-
const response = await fetch(`https://ensdata.net/${key}`);
|
51
|
-
const data: EnsData = (await response.json()) as EnsData;
|
52
|
-
|
53
|
-
// Assuming the data contains both domain and address
|
54
|
-
const domain = data?.ens;
|
55
|
-
const address = data?.address;
|
56
|
-
|
57
|
-
// Store data in cache by both domain and address
|
58
|
-
if (domain) infoCache[domain as string] = { info: data };
|
59
|
-
if (address) infoCache[address as string] = { info: data };
|
60
|
-
|
61
|
-
return { info: data, infoCache: infoCache, domain: domain as string };
|
62
|
-
} catch (error) {
|
63
|
-
console.error(error);
|
64
|
-
return { info: {}, infoCache: infoCache, domain: "" };
|
65
|
-
}
|
66
|
-
};
|
67
|
-
export const generateCoolAlternatives = (domain: string) => {
|
68
|
-
const suffixes = ["lfg", "cool", "degen", "moon", "base", "gm"];
|
69
|
-
const alternatives = suffixes.map((suffix) => {
|
70
|
-
const randomPosition = Math.random() < 0.5;
|
71
|
-
return randomPosition ? `${suffix}${domain}.eth` : `${domain}${suffix}.eth`;
|
72
|
-
});
|
73
|
-
return alternatives.join(",");
|
116
|
+
if (data.address) infoCache.set(data.address, data);
|
117
|
+
return data;
|
74
118
|
};
|
75
119
|
export const isOnXMTP = async (
|
76
120
|
client: Client,
|