Skip to content

Commit 9fd4d7e

Browse files
committed
fix invite link, prevent dupe reaction memories
1 parent 905a892 commit 9fd4d7e

File tree

4 files changed

+191
-95
lines changed

4 files changed

+191
-95
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ packages/core/src/providers/cache
4040
packages/core/src/providers/cache/*
4141
cache/*
4242

43-
log.txt
43+
log*.txt
4444
# Explicitly include plugin-node data files
4545
!packages/plugin-node/data/**

packages/adapter-postgres/src/index.ts

+63-27
Original file line numberDiff line numberDiff line change
@@ -362,36 +362,72 @@ export class PostgresDatabaseAdapter extends DatabaseAdapter {
362362
contentLength: memory.content?.text?.length,
363363
});
364364

365-
let isUnique = true;
366-
if (memory.embedding) {
367-
const similarMemories = await this.searchMemoriesByEmbedding(
368-
memory.embedding,
369-
{
365+
try {
366+
// First check if memory exists
367+
const existingMemory = await this.pool.query(
368+
"SELECT id FROM memories WHERE id = $1",
369+
[memory.id]
370+
);
371+
372+
if (existingMemory.rowCount > 0) {
373+
elizaLogger.debug(
374+
`Memory ${memory.id} already exists, skipping creation`
375+
);
376+
return;
377+
}
378+
379+
// Check for semantic duplicates if embedding exists
380+
let isUnique = true;
381+
if (memory.embedding) {
382+
const similarMemories =
383+
await this.searchMemoriesByEmbedding(memory.embedding, {
384+
tableName,
385+
roomId: memory.roomId,
386+
match_threshold: 0.95,
387+
count: 1,
388+
});
389+
isUnique = similarMemories.length === 0;
390+
}
391+
392+
// Insert the memory
393+
await this.pool.query(
394+
`INSERT INTO memories (
395+
id, type, content, embedding, "userId", "roomId", "agentId", "unique", "createdAt"
396+
) VALUES ($1, $2, $3, $4, $5::uuid, $6::uuid, $7::uuid, $8, to_timestamp($9/1000.0))`,
397+
[
398+
memory.id ?? v4(),
370399
tableName,
371-
roomId: memory.roomId,
372-
match_threshold: 0.95,
373-
count: 1,
374-
}
400+
JSON.stringify(memory.content),
401+
memory.embedding
402+
? `[${memory.embedding.join(",")}]`
403+
: null,
404+
memory.userId,
405+
memory.roomId,
406+
memory.agentId,
407+
memory.unique ?? isUnique,
408+
Date.now(),
409+
]
375410
);
376-
isUnique = similarMemories.length === 0;
411+
} catch (error) {
412+
if (error.code === "23505") {
413+
// Unique violation
414+
elizaLogger.warn(
415+
`Duplicate memory ID ${memory.id}, skipping`,
416+
{
417+
error: error.message,
418+
constraint: error.constraint,
419+
}
420+
);
421+
return;
422+
}
423+
// Log other errors and rethrow
424+
elizaLogger.error("Error creating memory:", {
425+
memoryId: memory.id,
426+
error: error.message,
427+
code: error.code,
428+
});
429+
throw error;
377430
}
378-
379-
await this.pool.query(
380-
`INSERT INTO memories (
381-
id, type, content, embedding, "userId", "roomId", "agentId", "unique", "createdAt"
382-
) VALUES ($1, $2, $3, $4, $5::uuid, $6::uuid, $7::uuid, $8, to_timestamp($9/1000.0))`,
383-
[
384-
memory.id ?? v4(),
385-
tableName,
386-
JSON.stringify(memory.content),
387-
memory.embedding ? `[${memory.embedding.join(",")}]` : null,
388-
memory.userId,
389-
memory.roomId,
390-
memory.agentId,
391-
memory.unique ?? isUnique,
392-
Date.now(),
393-
]
394-
);
395431
});
396432
}
397433

packages/client-discord/src/index.ts

+123-63
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { MessageManager } from "./messages.ts";
2222
import channelStateProvider from "./providers/channelState.ts";
2323
import voiceStateProvider from "./providers/voiceState.ts";
2424
import { VoiceManager } from "./voice.ts";
25+
import { PermissionsBitField } from "discord.js";
2526
import path from "path";
2627
import { fileURLToPath } from "url";
2728

@@ -143,85 +144,144 @@ export class DiscordClient extends EventEmitter {
143144
console.error("Error registering slash commands:", error);
144145
}
145146

147+
// Required permissions for the bot
148+
const requiredPermissions = [
149+
// Text Permissions
150+
PermissionsBitField.Flags.ViewChannel,
151+
PermissionsBitField.Flags.SendMessages,
152+
PermissionsBitField.Flags.SendMessagesInThreads,
153+
PermissionsBitField.Flags.CreatePrivateThreads,
154+
PermissionsBitField.Flags.CreatePublicThreads,
155+
PermissionsBitField.Flags.EmbedLinks,
156+
PermissionsBitField.Flags.AttachFiles,
157+
PermissionsBitField.Flags.AddReactions,
158+
PermissionsBitField.Flags.UseExternalEmojis,
159+
PermissionsBitField.Flags.UseExternalStickers,
160+
PermissionsBitField.Flags.MentionEveryone,
161+
PermissionsBitField.Flags.ManageMessages,
162+
PermissionsBitField.Flags.ReadMessageHistory,
163+
// Voice Permissions
164+
PermissionsBitField.Flags.Connect,
165+
PermissionsBitField.Flags.Speak,
166+
PermissionsBitField.Flags.UseVAD,
167+
PermissionsBitField.Flags.PrioritySpeaker,
168+
].reduce((a, b) => a | b, 0n);
169+
146170
elizaLogger.success("Use this URL to add the bot to your server:");
147171
elizaLogger.success(
148-
`https://discord.com/api/oauth2/authorize?client_id=${readyClient.user?.id}&permissions=0&scope=bot%20applications.commands`
172+
`https://discord.com/api/oauth2/authorize?client_id=${readyClient.user?.id}&permissions=${requiredPermissions}&scope=bot%20applications.commands`
149173
);
150174
await this.onReady();
151175
}
152176

153177
async handleReactionAdd(reaction: MessageReaction, user: User) {
154-
elizaLogger.log("Reaction added");
155-
// if (user.bot) return;
156-
157-
let emoji = reaction.emoji.name;
158-
if (!emoji && reaction.emoji.id) {
159-
emoji = `<:${reaction.emoji.name}:${reaction.emoji.id}>`;
160-
}
178+
try {
179+
elizaLogger.log("Reaction added");
161180

162-
// Fetch the full message if it's a partial
163-
if (reaction.partial) {
164-
try {
165-
await reaction.fetch();
166-
} catch (error) {
167-
console.error(
168-
"Something went wrong when fetching the message:",
169-
error
170-
);
181+
// Early returns
182+
if (!reaction || !user) {
183+
elizaLogger.warn("Invalid reaction or user");
171184
return;
172185
}
173-
}
174186

175-
const messageContent = reaction.message.content;
176-
const truncatedContent =
177-
messageContent.length > 100
178-
? messageContent.substring(0, 100) + "..."
179-
: messageContent;
187+
// Get emoji info
188+
let emoji = reaction.emoji.name;
189+
if (!emoji && reaction.emoji.id) {
190+
emoji = `<:${reaction.emoji.name}:${reaction.emoji.id}>`;
191+
}
180192

181-
const reactionMessage = `*<${emoji}>: "${truncatedContent}"*`;
193+
// Fetch full message if partial
194+
if (reaction.partial) {
195+
try {
196+
await reaction.fetch();
197+
} catch (error) {
198+
elizaLogger.error(
199+
"Failed to fetch partial reaction:",
200+
error
201+
);
202+
return;
203+
}
204+
}
182205

183-
const roomId = stringToUuid(
184-
reaction.message.channel.id + "-" + this.runtime.agentId
185-
);
186-
const userIdUUID = stringToUuid(user.id + "-" + this.runtime.agentId);
206+
// Generate IDs with timestamp to ensure uniqueness
207+
const timestamp = Date.now();
208+
const roomId = stringToUuid(
209+
`${reaction.message.channel.id}-${this.runtime.agentId}`
210+
);
211+
const userIdUUID = stringToUuid(
212+
`${user.id}-${this.runtime.agentId}`
213+
);
214+
const reactionUUID = stringToUuid(
215+
`${reaction.message.id}-${user.id}-${emoji}-${timestamp}-${this.runtime.agentId}`
216+
);
217+
218+
// Validate IDs
219+
if (!userIdUUID || !roomId) {
220+
elizaLogger.error("Invalid user ID or room ID", {
221+
userIdUUID,
222+
roomId,
223+
});
224+
return;
225+
}
187226

188-
// Generate a unique UUID for the reaction
189-
const reactionUUID = stringToUuid(
190-
`${reaction.message.id}-${user.id}-${emoji}-${this.runtime.agentId}`
191-
);
227+
// Process message content
228+
const messageContent = reaction.message.content || "";
229+
const truncatedContent =
230+
messageContent.length > 100
231+
? `${messageContent.substring(0, 100)}...`
232+
: messageContent;
233+
const reactionMessage = `*<${emoji}>: "${truncatedContent}"*`;
234+
235+
// Get user info
236+
const userName = reaction.message.author?.username || "unknown";
237+
const name = reaction.message.author?.displayName || userName;
238+
239+
// Ensure connection
240+
await this.runtime.ensureConnection(
241+
userIdUUID,
242+
roomId,
243+
userName,
244+
name,
245+
"discord"
246+
);
247+
248+
// Create memory with retry logic
249+
const memory = {
250+
id: reactionUUID,
251+
userId: userIdUUID,
252+
agentId: this.runtime.agentId,
253+
content: {
254+
text: reactionMessage,
255+
source: "discord",
256+
inReplyTo: stringToUuid(
257+
`${reaction.message.id}-${this.runtime.agentId}`
258+
),
259+
},
260+
roomId,
261+
createdAt: timestamp,
262+
embedding: embeddingZeroVector,
263+
};
192264

193-
// ensure the user id and room id are valid
194-
if (!userIdUUID || !roomId) {
195-
console.error("Invalid user id or room id");
196-
return;
265+
try {
266+
await this.runtime.messageManager.createMemory(memory);
267+
elizaLogger.debug("Reaction memory created", {
268+
reactionId: reactionUUID,
269+
emoji,
270+
userId: user.id,
271+
});
272+
} catch (error) {
273+
if (error.code === "23505") {
274+
// Duplicate key error
275+
elizaLogger.warn("Duplicate reaction memory, skipping", {
276+
reactionId: reactionUUID,
277+
});
278+
return;
279+
}
280+
throw error; // Re-throw other errors
281+
}
282+
} catch (error) {
283+
elizaLogger.error("Error handling reaction:", error);
197284
}
198-
const userName = reaction.message.author.username;
199-
const name = reaction.message.author.displayName;
200-
201-
await this.runtime.ensureConnection(
202-
userIdUUID,
203-
roomId,
204-
userName,
205-
name,
206-
"discord"
207-
);
208-
209-
// Save the reaction as a message
210-
await this.runtime.messageManager.createMemory({
211-
id: reactionUUID, // This is the ID of the reaction message
212-
userId: userIdUUID,
213-
agentId: this.runtime.agentId,
214-
content: {
215-
text: reactionMessage,
216-
source: "discord",
217-
inReplyTo: stringToUuid(
218-
reaction.message.id + "-" + this.runtime.agentId
219-
), // This is the ID of the original message
220-
},
221-
roomId,
222-
createdAt: Date.now(),
223-
embedding: embeddingZeroVector,
224-
});
225285
}
226286

227287
async handleReactionRemove(reaction: MessageReaction, user: User) {

packages/client-discord/src/messages.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1175,10 +1175,10 @@ export class MessageManager {
11751175
): Promise<Content> {
11761176
const { userId, roomId } = message;
11771177

1178-
elizaLogger.debug(
1179-
"Character data:",
1180-
JSON.stringify(this.runtime.character, null, 2)
1181-
);
1178+
// elizaLogger.debug(
1179+
// "Character data:",
1180+
// JSON.stringify(this.runtime.character, null, 2)
1181+
// );
11821182

11831183
// Ensure the knowledge and systemKnowledge are included in context
11841184
const contextWithKnowledge = {

0 commit comments

Comments
 (0)