diff --git a/prisma/migrations/20260407023407_remove_autorole_exclude/migration.sql b/prisma/migrations/20260407023407_remove_autorole_exclude/migration.sql new file mode 100644 index 0000000..e3e5abd --- /dev/null +++ b/prisma/migrations/20260407023407_remove_autorole_exclude/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - You are about to drop the `AutoRoleExclude` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropTable +DROP TABLE "AutoRoleExclude"; + +-- DropEnum +DROP TYPE "ExcludeType"; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 9808ab3..67fac0f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -218,20 +218,7 @@ model AutoRoleConfig { updatedAt DateTime @updatedAt } -model AutoRoleExclude { - id String @id @default(uuid()) - guildId String - targetId String // Role ID or User ID - type ExcludeType - createdAt DateTime @default(now()) - @@unique([guildId, targetId, type]) -} - -enum ExcludeType { - ROLE - USER -} model RefinementLevelConfig { level Int @id diff --git a/src/commands/autorole.ts b/src/commands/autorole.ts index f1006a9..a987073 100644 --- a/src/commands/autorole.ts +++ b/src/commands/autorole.ts @@ -66,21 +66,9 @@ export async function generateAutoRoleDashboard(guild: import('discord.js').Guil const rowBotRole = new ActionRowBuilder().addComponents(botSelect); - const rowRetroactive = new ActionRowBuilder().addComponents( - new ButtonBuilder() - .setCustomId('autorole_retroactive') - .setLabel(t(locale, 'commands.autorole.retroactiveBtn')) - .setStyle(ButtonStyle.Secondary) - .setDisabled(!config?.userRoleIds || config.userRoleIds.length === 0), - new ButtonBuilder() - .setCustomId('autorole_exclude') - .setLabel(t(locale, 'commands.autorole.excludeTitle')) - .setStyle(ButtonStyle.Secondary) - ); - return { embeds: [embed], - components: [rowUserRole, rowBotRole, rowRetroactive] + components: [rowUserRole, rowBotRole] }; } diff --git a/src/events/interactionCreate.ts b/src/events/interactionCreate.ts index 680b764..4387a64 100644 --- a/src/events/interactionCreate.ts +++ b/src/events/interactionCreate.ts @@ -150,17 +150,7 @@ export default { // 타임아웃 방지를 위해 즉시 승인 await interaction.deferUpdate(); - const guild = interaction.guild!; - if (interaction.customId === 'autorole_retroactive') { - const config = await autoRoleService.getConfig(guild.id); - if (config?.userRoleIds && config.userRoleIds.length > 0) { - await autoRoleService.applyRetroactively(guild, config.userRoleIds, interaction.user.id); - const { generateAutoRoleDashboard } = require('../commands/autorole'); - const dashboard = await generateAutoRoleDashboard(guild, locale); - await interaction.editReply({ content: t(locale, 'commands.autorole.retroactiveStarted'), ...dashboard }); - } - } - // 나머지 버튼 처리 + // 나머지 버튼 처리 (현재 사용 안함) }, locale); } else if (interaction.isRoleSelectMenu() && interaction.customId.startsWith('autorole_select_')) { diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 6391d59..c839c72 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -208,12 +208,6 @@ export const en: TranslationSchema = { enabled: 'Enabled', disabled: 'Disabled', updateSuccess: 'Auto role settings have been updated.', - retroactiveBtn: 'Apply Retroactively to Entire Server Now', - retroactiveConfirm: 'Do you want to scan all server members and assign roles? This may take time depending on the member count.', - retroactiveStarted: 'Retroactive assignment has started in the background. Check progress in audit logs.', - excludeTitle: 'Retroactive Exclude Settings', - excludeDesc: 'Users with specific IDs or roles will be excluded from retroactive assignment.', - excludeAddBtn: 'Add Exclude Target', permissionsError: 'Failed to assign role due to low bot hierarchy or missing permissions.', suspendNotice: 'Auto role assignment has been suspended due to insufficient permissions. Please check the bot\'s permissions and role hierarchy.', }, diff --git a/src/i18n/locales/ko.ts b/src/i18n/locales/ko.ts index cc1abd9..a11b70a 100644 --- a/src/i18n/locales/ko.ts +++ b/src/i18n/locales/ko.ts @@ -208,12 +208,6 @@ export const ko: TranslationSchema = { enabled: '활성', disabled: '비활성', updateSuccess: '자동 역할 설정이 업데이트되었습니다.', - retroactiveBtn: '지금 서버 전체에 소급 적용', - retroactiveConfirm: '서버의 모든 멤버를 스캔하여 역할을 부여하시겠습니까? 멤버 수에 따라 시간이 걸릴 수 있습니다.', - retroactiveStarted: '백그라운드에서 소급 적용을 시작합니다. 진행 상황은 감사 로그에서 확인하세요.', - excludeTitle: '소급 적용 제외 설정', - excludeDesc: '특정 ID나 역할을 가진 유저는 소급 적용 대상에서 제외됩니다.', - excludeAddBtn: '제외 대상 추가', permissionsError: '봇의 역할 순위가 낮거나 권한이 부족하여 역할을 부여할 수 없습니다.', suspendNotice: '권한 부족으로 인해 자동 역할 부여 기능이 일시 중지되었습니다. 봇의 권한과 역할 순위를 확인해 주세요.', }, diff --git a/src/i18n/types.ts b/src/i18n/types.ts index 5c55b98..dcc113c 100644 --- a/src/i18n/types.ts +++ b/src/i18n/types.ts @@ -162,12 +162,6 @@ export interface TranslationSchema { enabled: string; disabled: string; updateSuccess: string; - retroactiveBtn: string; - retroactiveConfirm: string; - retroactiveStarted: string; - excludeTitle: string; - excludeDesc: string; - excludeAddBtn: string; permissionsError: string; suspendNotice: string; }; diff --git a/src/services/AutoRoleService.ts b/src/services/AutoRoleService.ts index e124be6..101cf29 100644 --- a/src/services/AutoRoleService.ts +++ b/src/services/AutoRoleService.ts @@ -38,134 +38,6 @@ export class AutoRoleService { async setEnabled(guildId: string, enabled: boolean) { return this.updateConfig(guildId, { isEnabled: enabled }); } - - /** - * 소급 적용 제외 대상을 추가합니다. - */ - async addExclude(guildId: string, targetId: string, type: 'ROLE' | 'USER') { - return prisma.autoRoleExclude.upsert({ - where: { - guildId_targetId_type: { - guildId, - targetId, - type, - }, - }, - create: { - guildId, - targetId, - type, - }, - update: {}, - }); - } - - /** - * 소급 적용 제외 대상을 제거합니다. - */ - async removeExclude(guildId: string, targetId: string, type: 'ROLE' | 'USER') { - return prisma.autoRoleExclude.deleteMany({ - where: { - guildId, - targetId, - type, - }, - }); - } - - /** - * 소급 적용 제외 대상 목록을 조회합니다. - */ - async getExcludes(guildId: string) { - return prisma.autoRoleExclude.findMany({ - where: { guildId }, - }); - } - - /** - * 특정 멤버가 소급 적용 제외 대상인지 확인합니다. - */ - async isExcluded(member: GuildMember): Promise { - const excludes = await this.getExcludes(member.guild.id); - - // 유저 ID 체크 - if (excludes.some((e: any) => e.type === 'USER' && e.targetId === member.id)) { - return true; - } - - // 역할 ID 체크 - if (excludes.some((e: any) => e.type === 'ROLE' && member.roles.cache.has(e.targetId))) { - return true; - } - - return false; - } - - /** - * 서버 전체에 역할을 소급 적용합니다 (백그라운드 처리). - */ - async applyRetroactively(guild: Guild, roleIds: string[], initiatorId: string) { - if (!roleIds || roleIds.length === 0) return; - - const roles = roleIds.map(id => guild.roles.cache.get(id)).filter((r): r is import('discord.js').Role => r !== undefined); - if (roles.length === 0) return; - - // 봇의 권한 및 순위 확인 - const botMember = guild.members.me; - if (!botMember || !botMember.permissions.has(PermissionFlagsBits.ManageRoles)) { - logger.warn(`AutoRole: Cannot apply roles in guild ${guild.id} due to missing ManageRoles permission.`); - return; - } - - const validRoles = roles.filter(r => botMember.roles.highest.position > r.position); - if (validRoles.length === 0) { - logger.warn(`AutoRole: Cannot apply roles in guild ${guild.id} due to hierarchy limitations.`); - return; - } - - // 모든 멤버 페치 (캐시되지 않았을 수 있음) - const members = await guild.members.fetch(); - const targets = members.filter(m => !m.user.bot && validRoles.some(r => !m.roles.cache.has(r.id))); - - logger.info(`AutoRole: Starting retroactive application of ${validRoles.length} roles to ${targets.size} members in guild ${guild.id}`); - - let successCount = 0; - let failCount = 0; - - // 비동기 실행 (응답 대기 안 함) - (async () => { - for (const [, member] of targets) { - try { - if (await this.isExcluded(member)) continue; - - await member.roles.add(validRoles); - successCount++; - - // Rate Limit 방지를 위한 지연 (1.5초) - await new Promise(resolve => setTimeout(resolve, 1500)); - } catch (error: any) { - if (error.code === 10007 || error.code === 10013) { - // Unknown Member / User (이미 나감) - continue; - } - if (error.code === 50013) { - // Missing Permissions (도중 권한 상실) - logger.error(`AutoRole: Permission lost during retroactive assignment in guild ${guild.id}`); - break; - } - failCount++; - logger.error(`AutoRole: Failed to assign role to ${member.user.tag}:`, error); - } - } - - await auditLogService.log(guild, { - category: 'SYSTEM', - severity: 'INFO', - title: 'Retroactive Role Assignment Completed', - description: `Retroactive assignment of roles (${validRoles.map(r => `<@&${r.id}>`).join(', ')}) by <@${initiatorId}> has finished.\n- Success: ${successCount}\n- Failed: ${failCount}`, - }); - })(); - } } export const autoRoleService = new AutoRoleService();