refactor: remove invite tracking functionality and migrate member join handling to AutoRoleService

This commit is contained in:
이정수 2026-04-07 16:34:08 +09:00
parent c4f5e8d53c
commit 6072ab716f
11 changed files with 41 additions and 82 deletions

View File

@ -0,0 +1,8 @@
/*
Warnings:
- You are about to drop the `InviteRole` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropTable
DROP TABLE "InviteRole";

View File

@ -17,16 +17,6 @@ model GuildConfig {
updatedAt DateTime @updatedAt
}
model InviteRole {
id String @id @default(uuid())
guildId String
inviteCode String
roleId String
createdAt DateTime @default(now())
@@unique([guildId, inviteCode])
}
model UserSubscription {
userId String @id
tier SubscriptionTier @default(FREE)

View File

@ -16,7 +16,6 @@ export class KordClient extends Client {
GatewayIntentBits.GuildVoiceStates,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildInvites,
GatewayIntentBits.GuildMembers,
],
partials: [Partials.Message, Partials.Channel, Partials.GuildMember],

View File

@ -1,12 +1,10 @@
import { Events, Guild } from 'discord.js';
import { InviteService } from '../services/InviteService';
import { PresenceService } from '../services/PresenceService';
export default {
name: Events.GuildCreate,
once: false,
async execute(guild: Guild) {
await InviteService.cacheGuildInvites(guild);
PresenceService.updatePresence(guild.client);
},
};

View File

@ -1,10 +1,10 @@
import { Events, GuildMember } from 'discord.js';
import { InviteService } from '../services/InviteService';
import { autoRoleService } from '../services/AutoRoleService';
export default {
name: Events.GuildMemberAdd,
once: false,
async execute(member: GuildMember) {
await InviteService.handleMemberAdd(member);
await autoRoleService.handleMemberJoin(member);
},
};

View File

@ -1,7 +1,6 @@
import { Events } from 'discord.js';
import { KordClient } from '../client/KordClient';
import { logger } from '../utils/logger';
import { InviteService } from '../services/InviteService';
import { VoiceService } from '../services/VoiceService';
import { PresenceService } from '../services/PresenceService';
import { EventService } from '../services/EventService';
@ -14,7 +13,6 @@ export default {
once: true,
async execute(client: KordClient) {
logger.info(`Ready! Logged in as ${client.user?.tag}`);
await InviteService.cacheAllInvites(client);
await VoiceService.syncChannels(client);
PresenceService.startActivePresence(client);
EventService.startReminderLoop(client);

View File

@ -211,26 +211,6 @@ export const en: TranslationSchema = {
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.',
},
invite: {
description: 'Manage roles mapped to invite codes.',
listDescription: 'List all invite codes in the server.',
linkDescription: 'Link a role to an existing invite code.',
createDescription: 'Create a new invite code with a mapped role.',
unlinkDescription: 'Unlink a role from an invite code.',
codeOption: 'Invite code string',
roleOption: 'Role to assign',
usesOption: 'Maximum uses',
ageOption: 'Expiration (seconds, 0=unlimited)',
filterOption: 'Lookup filter (all=all, managed=managed only)',
listTitle: 'Invite Code Mappings',
listEmpty: 'No invite codes are currently linked to roles.',
linkSuccess: 'Role {{role}} has been linked to invite code `{{code}}`.',
unlinkSuccess: 'Role link has been removed from invite code `{{code}}`.',
createSuccess: 'New invite code `{{code}}` has been created and linked to role {{role}}.',
expireWarning: 'Invite code `{{code}}` has expired or been deleted. Role link has been removed.',
identifyFail: 'Could not identify the invite code for the joining user.',
identifyFailDesc: 'Due to simultaneous joins, the invite code for {{user}} could not be determined. Only default roles were assigned.',
},
music: {
description: 'Play YouTube audio in voice channels.',
addDescription: 'Search YouTube or add a video URL to the queue.',
@ -348,8 +328,6 @@ export const en: TranslationSchema = {
VOICE_GLOBAL: 'Voice Channels (Global)',
VOICE_GENERATOR_CHANNEL: 'Voice Generator Channel',
VOICE_GENERATOR_CATEGORY: 'Voice Generator Category',
INVITE_TRACKING: 'Invite Tracking',
INVITE_ROLE_HIERARCHY: 'Invite Role Assignment (Hierarchy)',
MIMIC_WEBHOOK: 'Message Mimic (Webhook)',
},
},
@ -408,7 +386,6 @@ export const en: TranslationSchema = {
BOOT: 'Boot',
VOICE: 'Voice',
PERMISSION: 'Permission',
INVITE: 'Invite',
},
},
config: {

View File

@ -211,26 +211,6 @@ export const ko: TranslationSchema = {
permissionsError: '봇의 역할 순위가 낮거나 권한이 부족하여 역할을 부여할 수 없습니다.',
suspendNotice: '권한 부족으로 인해 자동 역할 부여 기능이 일시 중지되었습니다. 봇의 권한과 역할 순위를 확인해 주세요.',
},
invite: {
description: '초대 코드와 역할을 연동하여 관리합니다.',
listDescription: '서버의 초대 코드 목록을 조회합니다.',
linkDescription: '기존 초대 코드에 역할을 연동합니다.',
createDescription: '역할이 연동된 새로운 초대 코드를 생성합니다.',
unlinkDescription: '초대 코드에 연동된 역할을 해제합니다.',
codeOption: '초대 코드 문자열',
roleOption: '부여할 역할',
usesOption: '최대 사용 횟수',
ageOption: '만료 기간(초, 0=무제한)',
filterOption: '조회 필터 (all=전체, managed=관리 중)',
listTitle: '초대 코드 매핑 목록',
listEmpty: '연동된 초대 코드가 없습니다.',
linkSuccess: '초대 코드 `{{code}}`에 {{role}} 역할이 연동되었습니다.',
unlinkSuccess: '초대 코드 `{{code}}`의 역할 연동이 해제되었습니다.',
createSuccess: '새로운 초대 코드 `{{code}}`가 생성되었으며 {{role}} 역할이 연동되었습니다.',
expireWarning: '초대 코드 `{{code}}`가 만료/삭제되어 역할 연동이 해제되었습니다.',
identifyFail: '참여한 유저의 초대 코드를 식별하지 못했습니다.',
identifyFailDesc: '동시 접속 등의 이유로 {{user}}님의 초대 코드를 확정할 수 없어 기본 역할만 부여되었습니다.',
},
music: {
description: 'Play YouTube audio in voice channels.',
addDescription: 'Search YouTube or add a video URL to the queue.',
@ -348,8 +328,6 @@ export const ko: TranslationSchema = {
VOICE_GLOBAL: '임시 음성 채널 (전역)',
VOICE_GENERATOR_CHANNEL: '음성 생성기 채널',
VOICE_GENERATOR_CATEGORY: '음성 생성기 카테고리',
INVITE_TRACKING: '초대 추적',
INVITE_ROLE_HIERARCHY: '초대 역할 부여 (계층 검사)',
MIMIC_WEBHOOK: '메시지 흉내 (Webhook)',
},
},
@ -408,7 +386,6 @@ export const ko: TranslationSchema = {
BOOT: '부팅',
VOICE: '음성',
PERMISSION: '권한',
INVITE: '초대',
},
},
config: {

View File

@ -3,7 +3,7 @@ import { prisma } from '../database';
import { env } from '../config/env';
export type AuditSeverity = 'INFO' | 'WARN' | 'ERROR';
export type AuditCategory = 'SYSTEM' | 'BOOT' | 'VOICE' | 'PERMISSION' | 'INVITE' | 'MIMIC';
export type AuditCategory = 'SYSTEM' | 'BOOT' | 'VOICE' | 'PERMISSION' | 'MIMIC';
export interface AuditLogPayload {
category: AuditCategory;

View File

@ -38,6 +38,35 @@ export class AutoRoleService {
async setEnabled(guildId: string, enabled: boolean) {
return this.updateConfig(guildId, { isEnabled: enabled });
}
/**
* .
*/
async handleMemberJoin(member: GuildMember) {
const config = await this.getConfig(member.guild.id);
if (!config) return;
const isBot = member.user.bot;
const isEnabled = isBot ? config.botEnabled : config.isEnabled;
const roleIds = isBot ? config.botRoleIds : config.userRoleIds;
if (!isEnabled || roleIds.length === 0) return;
try {
await member.roles.add(roleIds, 'Kord Auto-Role');
logger.info(`[AutoRole] Added roles to ${member.user.tag} in ${member.guild.name}`);
} catch (error) {
logger.error(`[AutoRole] Failed to add roles to ${member.user.tag} in ${member.guild.name}`, error);
// 권한 문제인 경우 감사 로그에 기록
await auditLogService.log(member.guild, {
category: 'PERMISSION',
severity: 'WARN',
title: 'Auto-Role Failure',
description: `Failed to assign roles to ${member.user.toString()} automatically. Please check the bot's permission and role hierarchy.`
}).catch(() => {});
}
}
}
export const autoRoleService = new AutoRoleService();

View File

@ -109,24 +109,7 @@ const FEATURE_DEFINITIONS: FeatureDefinition[] = [
},
},
// ── 5. 초대 추적 ──
{
featureKey: 'INVITE_TRACKING',
scope: 'guild',
permissions: [PermissionFlagsBits.ManageGuild],
},
// ── 6. 역할 자동 부여 (초대 연동) - 계층 검사 ──
{
featureKey: 'INVITE_ROLE_HIERARCHY',
scope: 'hierarchy',
resolveTargetRoleIds: async (guildId) => {
const inviteRoles = await prisma.inviteRole.findMany({ where: { guildId } });
return inviteRoles.map((ir: { roleId: string }) => ir.roleId);
},
},
// ── 7. 메시지 흉내 (Mimic) ──
// ── 5. 메시지 흉내 (Mimic) ──
{
featureKey: 'MIMIC_WEBHOOK',
scope: 'guild',