fix: Enhance temporary voice channel management with improved Discord API error handling for ghost channels and detailed logging.
This commit is contained in:
parent
b93255a2be
commit
cb80d9dffe
|
|
@ -11,7 +11,7 @@ model GuildConfig {
|
||||||
guildId String @id
|
guildId String @id
|
||||||
prefix String @default("!")
|
prefix String @default("!")
|
||||||
mimicEnabled Boolean @default(true)
|
mimicEnabled Boolean @default(true)
|
||||||
locale String? // Server locale override (e.g. 'ko', 'en')
|
locale String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
}
|
}
|
||||||
|
|
@ -26,18 +26,6 @@ model InviteRole {
|
||||||
@@unique([guildId, inviteCode])
|
@@unique([guildId, inviteCode])
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SubscriptionTier {
|
|
||||||
FREE
|
|
||||||
STANDARD
|
|
||||||
PRO
|
|
||||||
PREMIUM
|
|
||||||
}
|
|
||||||
|
|
||||||
enum DeleteCondition {
|
|
||||||
OWNER_LEAVE
|
|
||||||
EMPTY
|
|
||||||
}
|
|
||||||
|
|
||||||
model UserSubscription {
|
model UserSubscription {
|
||||||
userId String @id
|
userId String @id
|
||||||
tier SubscriptionTier @default(FREE)
|
tier SubscriptionTier @default(FREE)
|
||||||
|
|
@ -49,8 +37,8 @@ model UserSubscription {
|
||||||
model GuildOwnership {
|
model GuildOwnership {
|
||||||
guildId String @id
|
guildId String @id
|
||||||
ownerId String
|
ownerId String
|
||||||
owner UserSubscription @relation(fields: [ownerId], references: [userId], onDelete: Cascade)
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
owner UserSubscription @relation(fields: [ownerId], references: [userId], onDelete: Cascade)
|
||||||
|
|
||||||
@@index([ownerId])
|
@@index([ownerId])
|
||||||
}
|
}
|
||||||
|
|
@ -86,8 +74,19 @@ model UserVoiceProfile {
|
||||||
|
|
||||||
model UserLocale {
|
model UserLocale {
|
||||||
userId String @id
|
userId String @id
|
||||||
locale String // User's personal locale (e.g. 'ko', 'en')
|
locale String
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum SubscriptionTier {
|
||||||
|
FREE
|
||||||
|
STANDARD
|
||||||
|
PRO
|
||||||
|
PREMIUM
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DeleteCondition {
|
||||||
|
OWNER_LEAVE
|
||||||
|
EMPTY
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,8 @@ export class VoiceService {
|
||||||
const member = newState.member;
|
const member = newState.member;
|
||||||
if (!member) return;
|
if (!member) return;
|
||||||
|
|
||||||
|
logger.debug(`VoiceService: handleVoiceStateUpdate - old: ${oldState.channelId}, new: ${newState.channelId}`);
|
||||||
|
|
||||||
if (!oldState.channelId && newState.channelId) {
|
if (!oldState.channelId && newState.channelId) {
|
||||||
await this.handleJoin(newState);
|
await this.handleJoin(newState);
|
||||||
} else if (oldState.channelId && newState.channelId && oldState.channelId !== newState.channelId) {
|
} else if (oldState.channelId && newState.channelId && oldState.channelId !== newState.channelId) {
|
||||||
|
|
@ -82,7 +84,12 @@ export class VoiceService {
|
||||||
where: { channelId }
|
where: { channelId }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!generator) return;
|
if (!generator) {
|
||||||
|
logger.debug(`VoiceService: handleJoin - channel ${channelId} is NOT a generator`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`VoiceService: handleJoin - detected generator join for ${member.user.tag} in ${channelId}`);
|
||||||
|
|
||||||
const botMember = guild.members.me;
|
const botMember = guild.members.me;
|
||||||
if (!botMember?.permissions.has([
|
if (!botMember?.permissions.has([
|
||||||
|
|
@ -101,10 +108,18 @@ export class VoiceService {
|
||||||
if (existingTemp) {
|
if (existingTemp) {
|
||||||
try {
|
try {
|
||||||
await member.voice.setChannel(existingTemp.channelId);
|
await member.voice.setChannel(existingTemp.channelId);
|
||||||
} catch (e) {
|
return; // Success, moved to existing channel
|
||||||
logger.error(`${ErrorDefs.DISCORD_API_ERROR.code}: Could not move user to existing voice channel`, e);
|
} catch (e: any) {
|
||||||
|
// If the channel no longer exists in Discord, clean up DB and proceed to create new one
|
||||||
|
if (e.code === 10003 || e.status === 404) {
|
||||||
|
logger.warn(`VoiceService: Found ghost channel ${existingTemp.channelId} in DB. Cleaning up and creating fresh one.`);
|
||||||
|
await prisma.tempVoiceChannel.delete({ where: { channelId: existingTemp.channelId } }).catch(() => {});
|
||||||
|
// FALL THROUGH to channel creation logic below
|
||||||
|
} else {
|
||||||
|
logger.error(`${ErrorDefs.DISCORD_API_ERROR.code}: Unexpected error moving user to existing voice channel`, e);
|
||||||
|
return; // Stop for other errors to prevent spamming creations
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve locale for this context
|
// Resolve locale for this context
|
||||||
|
|
@ -202,9 +217,14 @@ export class VoiceService {
|
||||||
await channel.delete();
|
await channel.delete();
|
||||||
await prisma.tempVoiceChannel.delete({ where: { channelId } });
|
await prisma.tempVoiceChannel.delete({ where: { channelId } });
|
||||||
logger.info(`VoiceService: Successfully deleted temp channel ${channel.name}`);
|
logger.info(`VoiceService: Successfully deleted temp channel ${channel.name}`);
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
logger.error(`${ErrorDefs.DISCORD_MISSING_PERMISSIONS.code}: Failed to delete channel ${channel.name}`, error);
|
// If already deleted in Discord, just clean up DB
|
||||||
// deliberately NOT deleting the database entry so it remains synchronized with Discord's state.
|
if (error.code === 10003 || error.status === 404) {
|
||||||
|
await prisma.tempVoiceChannel.delete({ where: { channelId } }).catch(() => {});
|
||||||
|
logger.info(`VoiceService: Purged ghost channel ${channelId} from DB`);
|
||||||
|
} else {
|
||||||
|
logger.error(`${ErrorDefs.DISCORD_MISSING_PERMISSIONS.code}: Failed to delete channel ${channelId}`, error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (tempChannel.deleteWhen === 'EMPTY' && tempChannel.ownerId === member.id && channel.members.filter(m => !m.user.bot).size > 0) {
|
else if (tempChannel.deleteWhen === 'EMPTY' && tempChannel.ownerId === member.id && channel.members.filter(m => !m.user.bot).size > 0) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue