feat: add invite command to manage role-based invite code mappings
This commit is contained in:
parent
107c00cb13
commit
26cfd356ff
|
|
@ -0,0 +1,192 @@
|
|||
import {
|
||||
SlashCommandBuilder,
|
||||
ChatInputCommandInteraction,
|
||||
PermissionFlagsBits,
|
||||
EmbedBuilder,
|
||||
Colors,
|
||||
RoleSelectMenuBuilder,
|
||||
ActionRowBuilder
|
||||
} from 'discord.js';
|
||||
import { Command, CommandTrait } from '../core/command';
|
||||
import { prisma } from '../database';
|
||||
import { t, SupportedLocale } from '../i18n';
|
||||
import { InviteService } from '../services/InviteService';
|
||||
import { auditLogService } from '../services/AuditLogService';
|
||||
|
||||
class InviteCommand extends Command {
|
||||
protected override readonly trait = CommandTrait.General;
|
||||
protected override guildOnly = true;
|
||||
|
||||
protected override define() {
|
||||
return new SlashCommandBuilder()
|
||||
.setName('invite')
|
||||
.setDescription('Manage roles mapped to invite codes.')
|
||||
.setDescriptionLocalizations({
|
||||
ko: '초대 코드와 역할을 연동하여 관리합니다.',
|
||||
})
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('list')
|
||||
.setDescription('List all invite codes in the server.')
|
||||
.setDescriptionLocalizations({ ko: '서버의 초대 코드 목록을 조회합니다.' })
|
||||
.addStringOption(option =>
|
||||
option.setName('filter')
|
||||
.setDescription('Lookup filter')
|
||||
.addChoices(
|
||||
{ name: 'all', value: 'all' },
|
||||
{ name: 'managed', value: 'managed' }
|
||||
)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('link')
|
||||
.setDescription('Link a role to an existing invite code.')
|
||||
.setDescriptionLocalizations({ ko: '기존 초대 코드에 역할을 연동합니다.' })
|
||||
.addStringOption(option =>
|
||||
option.setName('code')
|
||||
.setDescription('Invite code string')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addRoleOption(option =>
|
||||
option.setName('role')
|
||||
.setDescription('Role to assign')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('create')
|
||||
.setDescription('Create a new invite code with a mapped role.')
|
||||
.setDescriptionLocalizations({ ko: '역할이 연동된 새로운 초대 코드를 생성합니다.' })
|
||||
.addRoleOption(option =>
|
||||
option.setName('role')
|
||||
.setDescription('Role to assign')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addIntegerOption(option =>
|
||||
option.setName('max_uses')
|
||||
.setDescription('Maximum uses')
|
||||
)
|
||||
.addIntegerOption(option =>
|
||||
option.setName('max_age')
|
||||
.setDescription('Expiration (seconds)')
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('unlink')
|
||||
.setDescription('Unlink a role from an invite code.')
|
||||
.setDescriptionLocalizations({ ko: '초대 코드에 연동된 역할을 해제합니다.' })
|
||||
.addStringOption(option =>
|
||||
option.setName('code')
|
||||
.setDescription('Invite code string')
|
||||
.setRequired(true)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected override async handle(interaction: ChatInputCommandInteraction, locale: SupportedLocale) {
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
const guild = interaction.guild!;
|
||||
|
||||
if (subcommand === 'list') {
|
||||
const filter = interaction.options.getString('filter') || 'managed';
|
||||
const mappings = await prisma.inviteRole.findMany({ where: { guildId: guild.id } });
|
||||
|
||||
let displayInvites: any[] = [];
|
||||
if (filter === 'all') {
|
||||
const invites = await guild.invites.fetch();
|
||||
displayInvites = invites.map(inv => ({
|
||||
code: inv.code,
|
||||
roleId: mappings.find(m => m.inviteCode === inv.code)?.roleId,
|
||||
uses: inv.uses,
|
||||
maxUses: inv.maxUses,
|
||||
}));
|
||||
} else {
|
||||
displayInvites = mappings.map(m => ({
|
||||
code: m.inviteCode,
|
||||
roleId: m.roleId,
|
||||
}));
|
||||
}
|
||||
|
||||
if (displayInvites.length === 0) {
|
||||
await interaction.reply({ content: t(locale, 'commands.invite.listEmpty'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(t(locale, 'commands.invite.listTitle'))
|
||||
.setColor(Colors.Blue)
|
||||
.setDescription(displayInvites.map(inv => `\`${inv.code}\`: ${inv.roleId ? `<@&${inv.roleId}>` : t(locale, 'autorole.notSet')}`).join('\n'));
|
||||
|
||||
await interaction.reply({ embeds: [embed], ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (subcommand === 'link') {
|
||||
const code = interaction.options.getString('code', true);
|
||||
const role = interaction.options.getRole('role', true);
|
||||
|
||||
await prisma.inviteRole.upsert({
|
||||
where: {
|
||||
guildId_inviteCode: {
|
||||
guildId: guild.id,
|
||||
inviteCode: code,
|
||||
},
|
||||
},
|
||||
update: { roleId: role.id },
|
||||
create: {
|
||||
guildId: guild.id,
|
||||
inviteCode: code,
|
||||
roleId: role.id,
|
||||
},
|
||||
});
|
||||
|
||||
await InviteService.cacheGuildInvites(guild);
|
||||
await interaction.reply({ content: t(locale, 'commands.invite.linkSuccess', { code, role: role.name }), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (subcommand === 'create') {
|
||||
const role = interaction.options.getRole('role', true);
|
||||
const maxUses = interaction.options.getInteger('max_uses') || 0;
|
||||
const maxAge = interaction.options.getInteger('max_age') || 0;
|
||||
|
||||
const invite = await guild.invites.create(interaction.channelId, {
|
||||
maxUses,
|
||||
maxAge,
|
||||
unique: true,
|
||||
});
|
||||
|
||||
await prisma.inviteRole.create({
|
||||
data: {
|
||||
guildId: guild.id,
|
||||
inviteCode: invite.code,
|
||||
roleId: role.id,
|
||||
},
|
||||
});
|
||||
|
||||
await InviteService.cacheGuildInvites(guild);
|
||||
await interaction.reply({ content: t(locale, 'commands.invite.createSuccess', { code: invite.code, role: role.name }), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (subcommand === 'unlink') {
|
||||
const code = interaction.options.getString('code', true);
|
||||
await prisma.inviteRole.deleteMany({
|
||||
where: {
|
||||
guildId: guild.id,
|
||||
inviteCode: code,
|
||||
},
|
||||
});
|
||||
|
||||
await InviteService.cacheGuildInvites(guild);
|
||||
await interaction.reply({ content: t(locale, 'commands.invite.unlinkSuccess', { code }), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new InviteCommand().toModule();
|
||||
Loading…
Reference in New Issue