refactor: remove retroactive autorole functionality and associated database tables
This commit is contained in:
parent
798d3d589c
commit
579e9a8a61
|
|
@ -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";
|
||||||
|
|
@ -218,20 +218,7 @@ model AutoRoleConfig {
|
||||||
updatedAt DateTime @updatedAt
|
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 {
|
model RefinementLevelConfig {
|
||||||
level Int @id
|
level Int @id
|
||||||
|
|
|
||||||
|
|
@ -66,21 +66,9 @@ export async function generateAutoRoleDashboard(guild: import('discord.js').Guil
|
||||||
|
|
||||||
const rowBotRole = new ActionRowBuilder<RoleSelectMenuBuilder>().addComponents(botSelect);
|
const rowBotRole = new ActionRowBuilder<RoleSelectMenuBuilder>().addComponents(botSelect);
|
||||||
|
|
||||||
const rowRetroactive = new ActionRowBuilder<ButtonBuilder>().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 {
|
return {
|
||||||
embeds: [embed],
|
embeds: [embed],
|
||||||
components: [rowUserRole, rowBotRole, rowRetroactive]
|
components: [rowUserRole, rowBotRole]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -150,17 +150,7 @@ export default {
|
||||||
// 타임아웃 방지를 위해 즉시 승인
|
// 타임아웃 방지를 위해 즉시 승인
|
||||||
await interaction.deferUpdate();
|
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);
|
}, locale);
|
||||||
}
|
}
|
||||||
else if (interaction.isRoleSelectMenu() && interaction.customId.startsWith('autorole_select_')) {
|
else if (interaction.isRoleSelectMenu() && interaction.customId.startsWith('autorole_select_')) {
|
||||||
|
|
|
||||||
|
|
@ -208,12 +208,6 @@ export const en: TranslationSchema = {
|
||||||
enabled: 'Enabled',
|
enabled: 'Enabled',
|
||||||
disabled: 'Disabled',
|
disabled: 'Disabled',
|
||||||
updateSuccess: 'Auto role settings have been updated.',
|
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.',
|
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.',
|
suspendNotice: 'Auto role assignment has been suspended due to insufficient permissions. Please check the bot\'s permissions and role hierarchy.',
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -208,12 +208,6 @@ export const ko: TranslationSchema = {
|
||||||
enabled: '활성',
|
enabled: '활성',
|
||||||
disabled: '비활성',
|
disabled: '비활성',
|
||||||
updateSuccess: '자동 역할 설정이 업데이트되었습니다.',
|
updateSuccess: '자동 역할 설정이 업데이트되었습니다.',
|
||||||
retroactiveBtn: '지금 서버 전체에 소급 적용',
|
|
||||||
retroactiveConfirm: '서버의 모든 멤버를 스캔하여 역할을 부여하시겠습니까? 멤버 수에 따라 시간이 걸릴 수 있습니다.',
|
|
||||||
retroactiveStarted: '백그라운드에서 소급 적용을 시작합니다. 진행 상황은 감사 로그에서 확인하세요.',
|
|
||||||
excludeTitle: '소급 적용 제외 설정',
|
|
||||||
excludeDesc: '특정 ID나 역할을 가진 유저는 소급 적용 대상에서 제외됩니다.',
|
|
||||||
excludeAddBtn: '제외 대상 추가',
|
|
||||||
permissionsError: '봇의 역할 순위가 낮거나 권한이 부족하여 역할을 부여할 수 없습니다.',
|
permissionsError: '봇의 역할 순위가 낮거나 권한이 부족하여 역할을 부여할 수 없습니다.',
|
||||||
suspendNotice: '권한 부족으로 인해 자동 역할 부여 기능이 일시 중지되었습니다. 봇의 권한과 역할 순위를 확인해 주세요.',
|
suspendNotice: '권한 부족으로 인해 자동 역할 부여 기능이 일시 중지되었습니다. 봇의 권한과 역할 순위를 확인해 주세요.',
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -162,12 +162,6 @@ export interface TranslationSchema {
|
||||||
enabled: string;
|
enabled: string;
|
||||||
disabled: string;
|
disabled: string;
|
||||||
updateSuccess: string;
|
updateSuccess: string;
|
||||||
retroactiveBtn: string;
|
|
||||||
retroactiveConfirm: string;
|
|
||||||
retroactiveStarted: string;
|
|
||||||
excludeTitle: string;
|
|
||||||
excludeDesc: string;
|
|
||||||
excludeAddBtn: string;
|
|
||||||
permissionsError: string;
|
permissionsError: string;
|
||||||
suspendNotice: string;
|
suspendNotice: string;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -38,134 +38,6 @@ export class AutoRoleService {
|
||||||
async setEnabled(guildId: string, enabled: boolean) {
|
async setEnabled(guildId: string, enabled: boolean) {
|
||||||
return this.updateConfig(guildId, { isEnabled: enabled });
|
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<boolean> {
|
|
||||||
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();
|
export const autoRoleService = new AutoRoleService();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue