Skip to content

Commit 97518d6

Browse files
authored
Merge pull request #557 from ai16z/fix/postgres
fix: postgres
2 parents 1977d52 + f14e9e0 commit 97518d6

File tree

3 files changed

+141
-58
lines changed

3 files changed

+141
-58
lines changed

agent/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ function initializeDatabase(dataDir: string) {
178178
if (process.env.POSTGRES_URL) {
179179
const db = new PostgresDatabaseAdapter({
180180
connectionString: process.env.POSTGRES_URL,
181+
parseInputs: true,
181182
});
182183
return db;
183184
} else {

packages/adapter-postgres/src/index.ts

+11-9
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,16 @@ export class PostgresDatabaseAdapter
5959
while (retryCount < maxRetries) {
6060
try {
6161
const delay = baseDelay * Math.pow(2, retryCount);
62-
elizaLogger.log(`Attempting to reconnect in ${delay}ms...`);
62+
elizaLogger.warn(
63+
`Attempting to reconnect in ${delay}ms...`
64+
);
6365
await new Promise((resolve) => setTimeout(resolve, delay));
6466

6567
// Create new pool with same config
6668
this.pool = new pg.Pool(this.pool.options);
6769
await this.testConnection();
6870

69-
elizaLogger.log("Successfully reconnected to database");
71+
elizaLogger.success("Successfully reconnected to database");
7072
return;
7173
} catch (error) {
7274
retryCount++;
@@ -116,7 +118,7 @@ export class PostgresDatabaseAdapter
116118
try {
117119
client = await this.pool.connect();
118120
const result = await client.query("SELECT NOW()");
119-
elizaLogger.log(
121+
elizaLogger.success(
120122
"Database connection test successful:",
121123
result.rows[0]
122124
);
@@ -215,7 +217,7 @@ export class PostgresDatabaseAdapter
215217
if (rows.length === 0) return null;
216218

217219
const account = rows[0];
218-
elizaLogger.log("account", account);
220+
elizaLogger.debug("account", account);
219221
return {
220222
...account,
221223
details:
@@ -346,7 +348,7 @@ export class PostgresDatabaseAdapter
346348
if (!params.roomId) throw new Error("roomId is required");
347349
let sql = `SELECT * FROM memories WHERE type = $1 AND "agentId" = $2 AND "roomId" = $3`;
348350
const values: any[] = [params.tableName, params.agentId, params.roomId];
349-
let paramCount = 2;
351+
let paramCount = 3; // Updated to start at 3 since we already have 3 parameters
350352

351353
if (params.start) {
352354
paramCount++;
@@ -366,9 +368,9 @@ export class PostgresDatabaseAdapter
366368

367369
sql += ' ORDER BY "createdAt" DESC';
368370

369-
if (params.count) {
371+
if (params.count && typeof params.count === "number") {
370372
paramCount++;
371-
sql += ` LIMIT $${paramCount}`;
373+
sql += ` LIMIT $${paramCount}::integer`; // Cast to integer
372374
values.push(params.count);
373375
}
374376

@@ -628,7 +630,7 @@ export class PostgresDatabaseAdapter
628630
);
629631

630632
if (existingParticipant.rows.length > 0) {
631-
elizaLogger.log(
633+
elizaLogger.error(
632634
`Participant with userId ${userId} already exists in room ${roomId}.`
633635
);
634636
return true; // Exit early if the participant already exists
@@ -643,7 +645,7 @@ export class PostgresDatabaseAdapter
643645
return true;
644646
} catch (error) {
645647
if (error instanceof DatabaseError) {
646-
elizaLogger.log("Error adding participant", error);
648+
elizaLogger.error("Error adding participant", error);
647649
// This is to prevent duplicate participant error in case of a race condition
648650
// Handle unique constraint violation error (code 23505)
649651
if (error.code === "23505") {

packages/client-discord/src/voice.ts

+129-49
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ import {
2121
NoSubscriberBehavior,
2222
StreamType,
2323
VoiceConnection,
24+
VoiceConnectionStatus,
2425
createAudioPlayer,
2526
createAudioResource,
2627
getVoiceConnection,
2728
joinVoiceChannel,
29+
entersState,
2830
} from "@discordjs/voice";
2931
import {
3032
BaseGuildVoiceChannel,
@@ -230,6 +232,7 @@ export class VoiceManager extends EventEmitter {
230232
console.error("Error leaving voice channel:", error);
231233
}
232234
}
235+
233236
const connection = joinVoiceChannel({
234237
channelId: channel.id,
235238
guildId: channel.guild.id,
@@ -238,38 +241,103 @@ export class VoiceManager extends EventEmitter {
238241
selfMute: false,
239242
});
240243

241-
const me = channel.guild.members.me;
242-
if (me?.voice && me.permissions.has("DeafenMembers")) {
243-
await me.voice.setDeaf(false);
244-
await me.voice.setMute(false);
245-
} else {
246-
elizaLogger.log("Bot lacks permission to modify voice state");
247-
}
244+
try {
245+
// Wait for either Ready or Signalling state
246+
await Promise.race([
247+
entersState(connection, VoiceConnectionStatus.Ready, 20_000),
248+
entersState(
249+
connection,
250+
VoiceConnectionStatus.Signalling,
251+
20_000
252+
),
253+
]);
254+
255+
// Log connection success
256+
elizaLogger.log(
257+
`Voice connection established in state: ${connection.state.status}`
258+
);
248259

249-
for (const [, member] of channel.members) {
250-
if (!member.user.bot) {
251-
this.monitorMember(member, channel);
252-
}
253-
}
260+
// Set up ongoing state change monitoring
261+
connection.on("stateChange", async (oldState, newState) => {
262+
elizaLogger.log(
263+
`Voice connection state changed from ${oldState.status} to ${newState.status}`
264+
);
254265

255-
connection.on("error", (error) => {
256-
console.error("Voice connection error:", error);
257-
});
266+
if (newState.status === VoiceConnectionStatus.Disconnected) {
267+
elizaLogger.log("Handling disconnection...");
258268

259-
connection.receiver.speaking.on("start", (userId: string) => {
260-
const user = channel.members.get(userId);
261-
if (!user?.user.bot) {
262-
this.monitorMember(user as GuildMember, channel);
263-
this.streams.get(userId)?.emit("speakingStarted");
269+
try {
270+
// Try to reconnect if disconnected
271+
await Promise.race([
272+
entersState(
273+
connection,
274+
VoiceConnectionStatus.Signalling,
275+
5_000
276+
),
277+
entersState(
278+
connection,
279+
VoiceConnectionStatus.Connecting,
280+
5_000
281+
),
282+
]);
283+
// Seems to be reconnecting to a new channel
284+
elizaLogger.log("Reconnecting to channel...");
285+
} catch (e) {
286+
// Seems to be a real disconnect, destroy and cleanup
287+
elizaLogger.log(
288+
"Disconnection confirmed - cleaning up..." + e
289+
);
290+
connection.destroy();
291+
this.connections.delete(channel.id);
292+
}
293+
} else if (
294+
newState.status === VoiceConnectionStatus.Destroyed
295+
) {
296+
this.connections.delete(channel.id);
297+
} else if (
298+
!this.connections.has(channel.id) &&
299+
(newState.status === VoiceConnectionStatus.Ready ||
300+
newState.status === VoiceConnectionStatus.Signalling)
301+
) {
302+
this.connections.set(channel.id, connection);
303+
}
304+
});
305+
306+
connection.on("error", (error) => {
307+
elizaLogger.log("Voice connection error:", error);
308+
// Don't immediately destroy - let the state change handler deal with it
309+
elizaLogger.log(
310+
"Connection error - will attempt to recover..."
311+
);
312+
});
313+
314+
// Store the connection
315+
this.connections.set(channel.id, connection);
316+
317+
// Continue with voice state modifications
318+
const me = channel.guild.members.me;
319+
if (me?.voice && me.permissions.has("DeafenMembers")) {
320+
try {
321+
await me.voice.setDeaf(false);
322+
await me.voice.setMute(false);
323+
} catch (error) {
324+
elizaLogger.log("Failed to modify voice state:", error);
325+
// Continue even if this fails
326+
}
264327
}
265-
});
266328

267-
connection.receiver.speaking.on("end", async (userId: string) => {
268-
const user = channel.members.get(userId);
269-
if (!user?.user.bot) {
270-
this.streams.get(userId)?.emit("speakingStopped");
329+
// Set up member monitoring
330+
for (const [, member] of channel.members) {
331+
if (!member.user.bot) {
332+
await this.monitorMember(member, channel);
333+
}
271334
}
272-
});
335+
} catch (error) {
336+
elizaLogger.log("Failed to establish voice connection:", error);
337+
connection.destroy();
338+
this.connections.delete(channel.id);
339+
throw error;
340+
}
273341
}
274342

275343
private async monitorMember(
@@ -780,7 +848,7 @@ export class VoiceManager extends EventEmitter {
780848

781849
audioPlayer.on(
782850
"stateChange",
783-
(oldState: any, newState: { status: string }) => {
851+
(_oldState: any, newState: { status: string }) => {
784852
if (newState.status == "idle") {
785853
const idleTime = Date.now();
786854
console.log(
@@ -792,34 +860,46 @@ export class VoiceManager extends EventEmitter {
792860
}
793861

794862
async handleJoinChannelCommand(interaction: any) {
795-
const channelId = interaction.options.get("channel")?.value as string;
796-
if (!channelId) {
797-
await interaction.reply("Please provide a voice channel to join.");
798-
return;
799-
}
800-
const guild = interaction.guild;
801-
if (!guild) {
802-
return;
803-
}
804-
const voiceChannel = interaction.guild.channels.cache.find(
805-
(channel: VoiceChannel) =>
806-
channel.id === channelId &&
807-
channel.type === ChannelType.GuildVoice
808-
);
863+
try {
864+
// Defer the reply immediately to prevent interaction timeout
865+
await interaction.deferReply();
866+
867+
const channelId = interaction.options.get("channel")
868+
?.value as string;
869+
if (!channelId) {
870+
await interaction.editReply(
871+
"Please provide a voice channel to join."
872+
);
873+
return;
874+
}
809875

810-
if (!voiceChannel) {
811-
await interaction.reply("Voice channel not found!");
812-
return;
813-
}
876+
const guild = interaction.guild;
877+
if (!guild) {
878+
await interaction.editReply("Could not find guild.");
879+
return;
880+
}
814881

815-
try {
816-
this.joinChannel(voiceChannel as BaseGuildVoiceChannel);
817-
await interaction.reply(
882+
const voiceChannel = interaction.guild.channels.cache.find(
883+
(channel: VoiceChannel) =>
884+
channel.id === channelId &&
885+
channel.type === ChannelType.GuildVoice
886+
);
887+
888+
if (!voiceChannel) {
889+
await interaction.editReply("Voice channel not found!");
890+
return;
891+
}
892+
893+
await this.joinChannel(voiceChannel as BaseGuildVoiceChannel);
894+
await interaction.editReply(
818895
`Joined voice channel: ${voiceChannel.name}`
819896
);
820897
} catch (error) {
821898
console.error("Error joining voice channel:", error);
822-
await interaction.reply("Failed to join the voice channel.");
899+
// Use editReply instead of reply for the error case
900+
await interaction
901+
.editReply("Failed to join the voice channel.")
902+
.catch(console.error);
823903
}
824904
}
825905

0 commit comments

Comments
 (0)