create-message-kit 1.0.16 → 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.
- package/package.json +1 -1
- package/templates/agent/src/handler/ens.ts +104 -140
- package/templates/agent/src/index.ts +8 -0
- package/templates/agent/src/lib/openai.ts +41 -29
- package/templates/agent/src/lib/resolver.ts +107 -63
- package/templates/agent/src/prompt.ts +24 -36
- package/templates/agent/src/{commands.ts → skills.ts} +33 -9
- 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 +42 -3
- package/templates/group/src/lib/resolver.ts +126 -0
- package/templates/group/src/{commands.ts → skills.ts} +17 -16
- 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/package.json
CHANGED
|
@@ -1,52 +1,39 @@
|
|
|
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
|
-
tipAddress,
|
|
10
|
-
chatHistories,
|
|
11
|
-
tipDomain,
|
|
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 { frameUrl, ensUrl, baseTxUrl } from "../index.js";
|
|
8
|
+
import { clearChatHistories } from "../lib/openai.js";
|
|
9
|
+
|
|
23
10
|
export async function handleEns(context: HandlerContext) {
|
|
24
11
|
const {
|
|
25
12
|
message: {
|
|
26
13
|
content: { command, params, sender },
|
|
27
14
|
},
|
|
28
15
|
} = context;
|
|
29
|
-
|
|
30
|
-
|
|
16
|
+
if (command == "reset") {
|
|
17
|
+
clear();
|
|
18
|
+
return { code: 200, message: "Conversation reset." };
|
|
19
|
+
} else if (command == "renew") {
|
|
31
20
|
// Destructure and validate parameters for the ens command
|
|
32
21
|
const { domain } = params;
|
|
33
22
|
// Check if the user holds the domain
|
|
34
23
|
if (!domain) {
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
return {
|
|
25
|
+
code: 400,
|
|
26
|
+
message: "Missing required parameters. Please provide domain.",
|
|
27
|
+
};
|
|
37
28
|
}
|
|
38
29
|
|
|
39
|
-
const
|
|
40
|
-
domain,
|
|
41
|
-
infoCache,
|
|
42
|
-
);
|
|
43
|
-
infoCache = retrievedInfoCache;
|
|
44
|
-
let data = infoCache[domain].info;
|
|
30
|
+
const data = await getUserInfo(domain);
|
|
45
31
|
|
|
46
|
-
if (data?.address !== sender?.address) {
|
|
32
|
+
if (!data || data?.address !== sender?.address) {
|
|
47
33
|
return {
|
|
48
34
|
code: 403,
|
|
49
|
-
message:
|
|
35
|
+
message:
|
|
36
|
+
"Looks like this domain is not registered to you. Only the owner can renew it.",
|
|
50
37
|
};
|
|
51
38
|
}
|
|
52
39
|
|
|
@@ -69,22 +56,23 @@ export async function handleEns(context: HandlerContext) {
|
|
|
69
56
|
} else if (command == "info") {
|
|
70
57
|
const { domain } = params;
|
|
71
58
|
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
59
|
+
const data = await getUserInfo(domain);
|
|
60
|
+
if (!data) {
|
|
61
|
+
return {
|
|
62
|
+
code: 404,
|
|
63
|
+
message: "Domain not found.",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
78
66
|
|
|
79
67
|
const formattedData = {
|
|
80
68
|
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,
|
|
69
|
+
"Avatar URL": data?.ensInfo?.avatar,
|
|
70
|
+
Description: data?.ensInfo?.description,
|
|
71
|
+
ENS: data?.ensDomain,
|
|
72
|
+
"Primary ENS": data?.ensInfo?.ens_primary,
|
|
73
|
+
GitHub: data?.ensInfo?.github,
|
|
74
|
+
Resolver: data?.ensInfo?.resolverAddress,
|
|
75
|
+
Twitter: data?.ensInfo?.twitter,
|
|
88
76
|
URL: `${ensUrl}${domain}`,
|
|
89
77
|
};
|
|
90
78
|
|
|
@@ -96,23 +84,20 @@ export async function handleEns(context: HandlerContext) {
|
|
|
96
84
|
}
|
|
97
85
|
message += `\n\nWould you like to tip the domain owner for getting there first 🤣?`;
|
|
98
86
|
message = message.trim();
|
|
99
|
-
if (
|
|
100
|
-
|
|
87
|
+
if (
|
|
88
|
+
await isOnXMTP(
|
|
89
|
+
context.v2client,
|
|
90
|
+
data?.ensInfo?.ens,
|
|
91
|
+
data?.ensInfo?.address,
|
|
92
|
+
)
|
|
93
|
+
) {
|
|
94
|
+
await context.send(
|
|
101
95
|
`Ah, this domains is in XMTP, you can message it directly: https://converse.xyz/dm/${domain}`,
|
|
102
96
|
);
|
|
103
97
|
}
|
|
104
98
|
return { code: 200, message };
|
|
105
99
|
} 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");
|
|
100
|
+
const { domain } = params;
|
|
116
101
|
|
|
117
102
|
if (!domain) {
|
|
118
103
|
return {
|
|
@@ -120,82 +105,46 @@ export async function handleEns(context: HandlerContext) {
|
|
|
120
105
|
message: "Please provide a domain name to check.",
|
|
121
106
|
};
|
|
122
107
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
infoCache,
|
|
126
|
-
);
|
|
127
|
-
infoCache = retrievedInfoCache;
|
|
128
|
-
let data = infoCache?.[domain]?.info;
|
|
108
|
+
|
|
109
|
+
const data = await getUserInfo(domain);
|
|
129
110
|
if (!data?.address) {
|
|
130
|
-
let message = `Looks like ${domain} is available! Do you want to register it? ${ensUrl}${domain}
|
|
111
|
+
let message = `Looks like ${domain} is available! Do you want to register it? ${ensUrl}${domain} or would you like to see some cool alternatives?`;
|
|
131
112
|
return {
|
|
132
113
|
code: 200,
|
|
133
114
|
message,
|
|
134
115
|
};
|
|
135
116
|
} else {
|
|
136
|
-
let message = `Looks like ${domain} is already registered
|
|
117
|
+
let message = `Looks like ${domain} is already registered!`;
|
|
118
|
+
await context.skill("/cool " + domain);
|
|
137
119
|
return {
|
|
138
120
|
code: 404,
|
|
139
121
|
message,
|
|
140
122
|
};
|
|
141
123
|
}
|
|
142
124
|
} else if (command == "tip") {
|
|
143
|
-
// Destructure and validate parameters for the send command
|
|
144
125
|
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;
|
|
126
|
+
if (!address) {
|
|
127
|
+
return {
|
|
128
|
+
code: 400,
|
|
129
|
+
message: "Please provide an address to tip.",
|
|
130
|
+
};
|
|
159
131
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
132
|
+
const data = await getUserInfo(address);
|
|
133
|
+
let txUrl = `${baseTxUrl}/transaction/?transaction_type=send&buttonName=Tip%20${data?.ensDomain ?? ""}&amount=1&token=USDC&receiver=${
|
|
134
|
+
isAddress(address) ? address : data?.address
|
|
135
|
+
}`;
|
|
136
|
+
console.log(txUrl);
|
|
137
|
+
return {
|
|
138
|
+
code: 200,
|
|
139
|
+
message: txUrl,
|
|
140
|
+
};
|
|
163
141
|
} 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
|
-
}
|
|
142
|
+
const { domain } = params;
|
|
143
|
+
//What about these cool alternatives?\
|
|
144
|
+
return {
|
|
145
|
+
code: 200,
|
|
146
|
+
message: `${generateCoolAlternatives(domain)}`,
|
|
147
|
+
};
|
|
199
148
|
}
|
|
200
149
|
}
|
|
201
150
|
|
|
@@ -215,33 +164,48 @@ export async function ensAgent(context: HandlerContext) {
|
|
|
215
164
|
|
|
216
165
|
try {
|
|
217
166
|
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]}`;
|
|
167
|
+
const userInfo = await getUserInfo(sender.address);
|
|
168
|
+
if (!userInfo) {
|
|
169
|
+
console.log("User info not found");
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const { ensDomain, converseUsername } = userInfo;
|
|
228
173
|
|
|
229
|
-
const { reply
|
|
174
|
+
const { reply } = await textGeneration(
|
|
175
|
+
sender.address,
|
|
230
176
|
userPrompt,
|
|
231
|
-
await ens_agent_prompt(
|
|
232
|
-
|
|
233
|
-
ensDomain[sender.address],
|
|
234
|
-
converseUsername[sender.address],
|
|
235
|
-
tipAddress[sender.address],
|
|
236
|
-
txUrl,
|
|
237
|
-
),
|
|
238
|
-
chatHistories[sender.address],
|
|
177
|
+
await ens_agent_prompt(sender.address, ensDomain, converseUsername),
|
|
178
|
+
group !== undefined,
|
|
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
|
+
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { run, HandlerContext } from "@xmtp/message-kit";
|
|
2
2
|
import { ensAgent } from "./handler/ens.js";
|
|
3
3
|
|
|
4
|
+
export const frameUrl = "https://ens.steer.fun/";
|
|
5
|
+
export const ensUrl = "https://app.ens.domains/";
|
|
6
|
+
export const baseTxUrl = "https://base-tx-frame.vercel.app";
|
|
7
|
+
|
|
4
8
|
run(async (context: HandlerContext) => {
|
|
9
|
+
const { group, message } = context;
|
|
10
|
+
/*All the commands are handled through the commands file*/
|
|
11
|
+
/* If its just text, it will be handled by the ensAgent*/
|
|
12
|
+
/* If its a group message, it will be handled by the groupAgent*/
|
|
5
13
|
await ensAgent(context);
|
|
6
14
|
});
|
|
@@ -6,12 +6,17 @@ 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
|
-
|
|
17
|
+
isGroup: boolean = false,
|
|
13
18
|
) {
|
|
14
|
-
let messages =
|
|
19
|
+
let messages = chatHistories[address] || [];
|
|
15
20
|
if (messages.length === 0) {
|
|
16
21
|
messages.push({
|
|
17
22
|
role: "system",
|
|
@@ -33,7 +38,7 @@ export async function textGeneration(
|
|
|
33
38
|
content: reply || "No response from OpenAI.",
|
|
34
39
|
});
|
|
35
40
|
const cleanedReply = responseParser(reply as string);
|
|
36
|
-
|
|
41
|
+
if (!isGroup) chatHistories[address] = messages;
|
|
37
42
|
return { reply: cleanedReply, history: messages };
|
|
38
43
|
} catch (error) {
|
|
39
44
|
console.error("Failed to fetch from OpenAI:", error);
|
|
@@ -78,6 +83,35 @@ 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
|
+
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
|
+
}
|
|
81
115
|
export function responseParser(message: string) {
|
|
82
116
|
let trimmedMessage = message;
|
|
83
117
|
// Remove bold and underline markdown
|
|
@@ -94,32 +128,10 @@ export function responseParser(message: string) {
|
|
|
94
128
|
trimmedMessage = trimmedMessage?.replace(/^\s+|\s+$/g, "");
|
|
95
129
|
// Remove any remaining leading or trailing whitespace
|
|
96
130
|
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
131
|
|
|
124
132
|
return trimmedMessage;
|
|
125
133
|
}
|
|
134
|
+
|
|
135
|
+
export const clearChatHistories = () => {
|
|
136
|
+
chatHistories = {};
|
|
137
|
+
};
|
|
@@ -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,
|