[deploy] add register module backend
Some checks failed
Deploy Discord Bot / deploy (push) Failing after 20s
Some checks failed
Deploy Discord Bot / deploy (push) Failing after 20s
This commit is contained in:
@@ -26,8 +26,7 @@ model GuildSettings {
|
||||
birthdayConfig Json?
|
||||
reactionRolesEnabled Boolean?
|
||||
reactionRolesConfig Json?
|
||||
eventsEnabled Boolean?
|
||||
supportRoleId String?
|
||||
eventsEnabled Boolean?\n registerEnabled Boolean?\n registerConfig Json?\n supportRoleId String?
|
||||
updatedAt DateTime @updatedAt
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
@@ -161,3 +160,55 @@ model EventSignup {
|
||||
@@unique([eventId, userId])
|
||||
@@index([guildId, eventId])
|
||||
}
|
||||
|
||||
model RegisterForm {
|
||||
id String @id @default(cuid())
|
||||
guildId String
|
||||
name String
|
||||
description String?
|
||||
reviewChannelId String?
|
||||
notifyRoleIds String[]
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
fields RegisterFormField[]
|
||||
|
||||
@@index([guildId, isActive])
|
||||
}
|
||||
|
||||
model RegisterFormField {
|
||||
id String @id @default(cuid())
|
||||
formId String
|
||||
label String
|
||||
type String
|
||||
required Boolean @default(false)
|
||||
"order" Int @default(0)
|
||||
|
||||
form RegisterForm @relation(fields: [formId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
model RegisterApplication {
|
||||
id String @id @default(cuid())
|
||||
guildId String
|
||||
userId String
|
||||
formId String
|
||||
status String @default("pending")
|
||||
reviewedBy String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
answers RegisterApplicationAnswer[]
|
||||
|
||||
form RegisterForm @relation(fields: [formId], references: [id])
|
||||
|
||||
@@index([guildId, formId, status])
|
||||
}
|
||||
|
||||
model RegisterApplicationAnswer {
|
||||
id String @id @default(cuid())
|
||||
applicationId String
|
||||
fieldId String
|
||||
value String
|
||||
|
||||
application RegisterApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { ReactionRoleService } from '../services/reactionRoleService';
|
||||
import { EventService } from '../services/eventService';
|
||||
import { TicketAutomationService } from '../services/ticketAutomationService';
|
||||
import { KnowledgeBaseService } from '../services/knowledgeBaseService';
|
||||
import { RegisterService } from '../services/registerService';
|
||||
|
||||
export const context = {
|
||||
client: null as Client | null,
|
||||
@@ -31,7 +32,8 @@ export const context = {
|
||||
reactionRoles: new ReactionRoleService(),
|
||||
events: new EventService(),
|
||||
ticketAutomation: new TicketAutomationService(),
|
||||
knowledgeBase: new KnowledgeBaseService()
|
||||
knowledgeBase: new KnowledgeBaseService(),
|
||||
register: new RegisterService()
|
||||
};
|
||||
|
||||
context.modules.setHooks({
|
||||
|
||||
@@ -60,6 +60,11 @@ export interface GuildSettings {
|
||||
};
|
||||
reactionRolesEnabled?: boolean;
|
||||
reactionRolesConfig?: any;
|
||||
registerEnabled?: boolean;
|
||||
registerConfig?: {
|
||||
reviewChannelId?: string;
|
||||
notifyRoleIds?: string[];
|
||||
};
|
||||
supportRoleId?: string;
|
||||
welcomeEnabled?: boolean;
|
||||
}
|
||||
@@ -80,7 +85,8 @@ class SettingsStore {
|
||||
'statuspageEnabled',
|
||||
'birthdayEnabled',
|
||||
'reactionRolesEnabled',
|
||||
'eventsEnabled'
|
||||
'eventsEnabled',
|
||||
'registerEnabled'
|
||||
] as const
|
||||
).forEach((key) => {
|
||||
if (normalized[key] === undefined) normalized[key] = true;
|
||||
@@ -116,6 +122,8 @@ class SettingsStore {
|
||||
birthdayConfig: (row as any).birthdayConfig ?? undefined,
|
||||
reactionRolesEnabled: (row as any).reactionRolesEnabled ?? undefined,
|
||||
reactionRolesConfig: (row as any).reactionRolesConfig ?? undefined,
|
||||
registerEnabled: (row as any).registerEnabled ?? undefined,
|
||||
registerConfig: (row as any).registerConfig ?? undefined,
|
||||
supportRoleId: row.supportRoleId ?? undefined
|
||||
} satisfies GuildSettings;
|
||||
this.cache.set(row.guildId, this.applyModuleDefaults(cfg));
|
||||
@@ -147,6 +155,9 @@ class SettingsStore {
|
||||
} else if (partial.reactionRolesConfig?.enabled !== undefined) {
|
||||
partial.reactionRolesEnabled = partial.reactionRolesConfig.enabled;
|
||||
}
|
||||
if (!partial.registerConfig && partial.registerEnabled !== undefined) {
|
||||
partial.registerConfig = { ...(partial.registerConfig ?? {}) };
|
||||
}
|
||||
const merged: GuildSettings = this.applyModuleDefaults({ ...(this.cache.get(guildId) ?? {}), ...partial });
|
||||
const mergedAutomod = {
|
||||
...(merged.automodConfig ?? {}),
|
||||
@@ -193,6 +204,8 @@ class SettingsStore {
|
||||
birthdayConfig: merged.birthdayConfig ?? null,
|
||||
reactionRolesEnabled: merged.reactionRolesEnabled ?? null,
|
||||
reactionRolesConfig: merged.reactionRolesConfig ?? null,
|
||||
registerEnabled: merged.registerEnabled ?? null,
|
||||
registerConfig: merged.registerConfig ?? null,
|
||||
supportRoleId: merged.supportRoleId ?? null
|
||||
},
|
||||
create: {
|
||||
@@ -213,6 +226,8 @@ class SettingsStore {
|
||||
birthdayConfig: merged.birthdayConfig ?? null,
|
||||
reactionRolesEnabled: merged.reactionRolesEnabled ?? null,
|
||||
reactionRolesConfig: merged.reactionRolesConfig ?? null,
|
||||
registerEnabled: merged.registerEnabled ?? null,
|
||||
registerConfig: merged.registerConfig ?? null,
|
||||
supportRoleId: merged.supportRoleId ?? null
|
||||
}
|
||||
});
|
||||
|
||||
@@ -26,6 +26,8 @@ model GuildSettings {
|
||||
reactionRolesEnabled Boolean?
|
||||
reactionRolesConfig Json?
|
||||
eventsEnabled Boolean?
|
||||
registerEnabled Boolean?
|
||||
registerConfig Json?
|
||||
supportRoleId String?
|
||||
updatedAt DateTime @updatedAt
|
||||
createdAt DateTime @default(now())
|
||||
@@ -160,3 +162,54 @@ model EventSignup {
|
||||
@@unique([eventId, userId])
|
||||
@@index([guildId, eventId])
|
||||
}
|
||||
|
||||
model RegisterForm {
|
||||
id String @id @default(cuid())
|
||||
guildId String
|
||||
name String
|
||||
description String?
|
||||
reviewChannelId String?
|
||||
notifyRoleIds String[]
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
fields RegisterFormField[]
|
||||
|
||||
@@index([guildId, isActive])
|
||||
}
|
||||
|
||||
model RegisterFormField {
|
||||
id String @id @default(cuid())
|
||||
formId String
|
||||
label String
|
||||
type String
|
||||
required Boolean @default(false)
|
||||
"order" Int @default(0)
|
||||
|
||||
form RegisterForm @relation(fields: [formId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
model RegisterApplication {
|
||||
id String @id @default(cuid())
|
||||
guildId String
|
||||
userId String
|
||||
formId String
|
||||
status String @default("pending")
|
||||
reviewedBy String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
answers RegisterApplicationAnswer[]
|
||||
|
||||
form RegisterForm @relation(fields: [formId], references: [id])
|
||||
|
||||
@@index([guildId, formId, status])
|
||||
}
|
||||
|
||||
model RegisterApplicationAnswer {
|
||||
id String @id @default(cuid())
|
||||
applicationId String
|
||||
fieldId String
|
||||
value String
|
||||
|
||||
application RegisterApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
@@ -20,8 +20,19 @@ const event: EventHandler = {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (interaction.customId.startsWith('register:')) {
|
||||
await context.register.handleButton(interaction as any);
|
||||
return;
|
||||
}
|
||||
await context.tickets.handleButton(interaction);
|
||||
}
|
||||
|
||||
if (interaction.isModalSubmit()) {
|
||||
if (interaction.customId.startsWith('register:submit:')) {
|
||||
await context.register.handleModal(interaction as any);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ async function bootstrap() {
|
||||
context.reactionRoles.setClient(client);
|
||||
context.events.setClient(client);
|
||||
context.events.startScheduler();
|
||||
context.register.setClient(client);
|
||||
await context.reactionRoles.loadCache();
|
||||
logger.setSink((entry) => context.admin.pushLog(entry));
|
||||
for (const gid of settingsStore.all().keys()) {
|
||||
|
||||
@@ -10,7 +10,8 @@ export type ModuleKey =
|
||||
| 'statuspageEnabled'
|
||||
| 'birthdayEnabled'
|
||||
| 'reactionRolesEnabled'
|
||||
| 'eventsEnabled';
|
||||
| 'eventsEnabled'
|
||||
| 'registerEnabled';
|
||||
|
||||
export interface GuildModuleState {
|
||||
key: ModuleKey;
|
||||
@@ -29,7 +30,8 @@ const MODULES: Record<ModuleKey, { name: string; description: string }> = {
|
||||
statuspageEnabled: { name: 'Statuspage', description: 'Service Checks, Uptime und Status-Embed.' },
|
||||
birthdayEnabled: { name: 'Birthday', description: 'Geburtstage speichern und Glueckwuensche senden.' },
|
||||
reactionRolesEnabled: { name: 'Reaction Roles', description: 'Reaktionen vergeben und entfernen Rollen.' },
|
||||
eventsEnabled: { name: 'Termine', description: 'Events planen, erinnern und Anmeldungen sammeln.' }
|
||||
eventsEnabled: { name: 'Termine', description: 'Events planen, erinnern und Anmeldungen sammeln.' },
|
||||
registerEnabled: { name: 'Register', description: 'Registrierungsformulare und Bewerbungen.' }
|
||||
};
|
||||
|
||||
export class BotModuleService {
|
||||
|
||||
256
src/services/registerService.ts
Normal file
256
src/services/registerService.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
import {
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonInteraction,
|
||||
ButtonStyle,
|
||||
Client,
|
||||
EmbedBuilder,
|
||||
ModalBuilder,
|
||||
TextInputBuilder,
|
||||
TextInputStyle,
|
||||
ModalSubmitInteraction,
|
||||
GuildMember
|
||||
} from 'discord.js';
|
||||
import { prisma } from '../database';
|
||||
import { settingsStore } from '../config/state';
|
||||
import { env } from '../config/env';
|
||||
|
||||
export class RegisterService {
|
||||
private client: Client | null = null;
|
||||
|
||||
public setClient(client: Client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public async listForms(guildId: string) {
|
||||
return prisma.registerForm.findMany({ where: { guildId }, include: { fields: { orderBy: { order: 'asc' } } }, orderBy: { createdAt: 'desc' } });
|
||||
}
|
||||
|
||||
public async saveForm(form: {
|
||||
id?: string;
|
||||
guildId: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
reviewChannelId?: string;
|
||||
notifyRoleIds?: string[];
|
||||
isActive?: boolean;
|
||||
fields: { id?: string; label: string; type: string; required?: boolean; order?: number }[];
|
||||
}) {
|
||||
const notify = (form.notifyRoleIds || []).filter(Boolean);
|
||||
if (form.id) {
|
||||
await prisma.registerForm.update({
|
||||
where: { id: form.id },
|
||||
data: {
|
||||
name: form.name,
|
||||
description: form.description,
|
||||
reviewChannelId: form.reviewChannelId,
|
||||
notifyRoleIds: notify,
|
||||
isActive: form.isActive ?? true
|
||||
}
|
||||
});
|
||||
await prisma.registerFormField.deleteMany({ where: { formId: form.id } });
|
||||
await prisma.registerFormField.createMany({
|
||||
data: (form.fields || []).map((f, idx) => ({
|
||||
formId: form.id as string,
|
||||
label: f.label,
|
||||
type: f.type,
|
||||
required: f.required ?? false,
|
||||
order: f.order ?? idx
|
||||
}))
|
||||
});
|
||||
return prisma.registerForm.findUnique({ where: { id: form.id }, include: { fields: { orderBy: { order: 'asc' } } } });
|
||||
}
|
||||
const created = await prisma.registerForm.create({
|
||||
data: {
|
||||
guildId: form.guildId,
|
||||
name: form.name,
|
||||
description: form.description,
|
||||
reviewChannelId: form.reviewChannelId,
|
||||
notifyRoleIds: notify,
|
||||
isActive: form.isActive ?? true,
|
||||
fields: {
|
||||
create: (form.fields || []).map((f, idx) => ({
|
||||
label: f.label,
|
||||
type: f.type,
|
||||
required: f.required ?? false,
|
||||
order: f.order ?? idx
|
||||
}))
|
||||
}
|
||||
},
|
||||
include: { fields: { orderBy: { order: 'asc' } } }
|
||||
});
|
||||
return created;
|
||||
}
|
||||
|
||||
public async deleteForm(guildId: string, id: string) {
|
||||
const form = await prisma.registerForm.findFirst({ where: { id, guildId } });
|
||||
if (!form) return false;
|
||||
await prisma.registerFormField.deleteMany({ where: { formId: id } });
|
||||
await prisma.registerForm.delete({ where: { id } });
|
||||
return true;
|
||||
}
|
||||
|
||||
public async sendPanel(guildId: string, formId: string, channelId?: string, message?: string) {
|
||||
if (!this.client) return null;
|
||||
const form = await prisma.registerForm.findFirst({ where: { id: formId, guildId }, include: { fields: true } });
|
||||
if (!form) return null;
|
||||
const targetChannelId = channelId || form.reviewChannelId || settingsStore.get(guildId)?.registerConfig?.reviewChannelId;
|
||||
if (!targetChannelId) return null;
|
||||
const guild = this.client.guilds.cache.get(guildId) ?? (await this.client.guilds.fetch(guildId).catch(() => null));
|
||||
if (!guild) return null;
|
||||
const channel = await guild.channels.fetch(targetChannelId).catch(() => null);
|
||||
if (!channel || !channel.isTextBased()) return null;
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(form.name)
|
||||
.setDescription(message || 'Klicke auf Registrieren, um das Formular auszufüllen.')
|
||||
.setColor(0xf97316);
|
||||
const btn = new ButtonBuilder().setCustomId(`register:form:${form.id}`).setLabel('Registrieren').setStyle(ButtonStyle.Primary);
|
||||
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(btn);
|
||||
const sent = await (channel as any).send({ embeds: [embed], components: [row] });
|
||||
return sent.id;
|
||||
}
|
||||
|
||||
public async handleButton(interaction: ButtonInteraction) {
|
||||
if (interaction.customId.startsWith('register:form:')) {
|
||||
const formId = interaction.customId.split(':')[2];
|
||||
const form = await prisma.registerForm.findFirst({ where: { id: formId }, include: { fields: { orderBy: { order: 'asc' } } } });
|
||||
if (!form) return interaction.reply({ content: 'Formular nicht gefunden.', ephemeral: true });
|
||||
const modal = new ModalBuilder().setTitle(form.name).setCustomId(`register:submit:${form.id}`);
|
||||
const components: any[] = [];
|
||||
form.fields.slice(0, 5).forEach((f) => {
|
||||
const input = new TextInputBuilder()
|
||||
.setCustomId(f.id)
|
||||
.setLabel(f.label.slice(0, 45) || 'Feld')
|
||||
.setStyle(f.type === 'longText' ? TextInputStyle.Paragraph : TextInputStyle.Short)
|
||||
.setRequired(f.required ?? false);
|
||||
components.push(new ActionRowBuilder<TextInputBuilder>().addComponents(input));
|
||||
});
|
||||
modal.addComponents(components as any);
|
||||
await interaction.showModal(modal);
|
||||
return;
|
||||
}
|
||||
|
||||
if (interaction.customId.startsWith('register:review:')) {
|
||||
const [, , action, appId] = interaction.customId.split(':');
|
||||
const app = await prisma.registerApplication.findUnique({ where: { id: appId }, include: { form: true } });
|
||||
if (!app) {
|
||||
await interaction.reply({ content: 'Antrag nicht gefunden.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
const statusMap: any = { accept: 'accepted', invite: 'invited', reject: 'rejected' };
|
||||
const newStatus = statusMap[action] || 'pending';
|
||||
const updated = await prisma.registerApplication.update({
|
||||
where: { id: appId },
|
||||
data: { status: newStatus, reviewedBy: interaction.user.id }
|
||||
});
|
||||
await this.updateReviewMessage(interaction, updated);
|
||||
const user = await this.client?.users.fetch(app.userId).catch(() => null);
|
||||
if (user) {
|
||||
const msg =
|
||||
newStatus === 'accepted'
|
||||
? 'Deine Registrierung wurde akzeptiert.'
|
||||
: newStatus === 'invited'
|
||||
? 'Bitte komm für ein Gespräch vorbei.'
|
||||
: 'Deine Registrierung wurde abgelehnt.';
|
||||
user.send(msg).catch(() => undefined);
|
||||
}
|
||||
await interaction.reply({ content: 'Status aktualisiert.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public async handleModal(interaction: ModalSubmitInteraction) {
|
||||
if (!interaction.customId.startsWith('register:submit:')) return;
|
||||
const formId = interaction.customId.split(':')[2];
|
||||
const form = await prisma.registerForm.findFirst({
|
||||
where: { id: formId },
|
||||
include: { fields: { orderBy: { order: 'asc' } } }
|
||||
});
|
||||
if (!form) {
|
||||
await interaction.reply({ content: 'Formular nicht gefunden.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
const answersPayload = form.fields.map((f) => ({
|
||||
fieldId: f.id,
|
||||
value: interaction.fields.getTextInputValue(f.id) || ''
|
||||
}));
|
||||
const app = await prisma.registerApplication.create({
|
||||
data: {
|
||||
guildId: interaction.guildId ?? '',
|
||||
userId: interaction.user.id,
|
||||
formId: form.id,
|
||||
status: 'pending',
|
||||
answers: {
|
||||
create: answersPayload
|
||||
}
|
||||
},
|
||||
include: { answers: true }
|
||||
});
|
||||
await interaction.reply({ content: 'Registrierung gesendet.', ephemeral: true });
|
||||
await this.postReviewEmbed(form, app, interaction.user.id, interaction.guildId || '');
|
||||
}
|
||||
|
||||
private async postReviewEmbed(form: any, app: any, userId: string, guildId: string) {
|
||||
if (!this.client) return;
|
||||
const cfg = settingsStore.get(guildId);
|
||||
const channelId = form.reviewChannelId || cfg?.registerConfig?.reviewChannelId;
|
||||
if (!channelId) return;
|
||||
const guild = this.client.guilds.cache.get(guildId) ?? (await this.client.guilds.fetch(guildId).catch(() => null));
|
||||
if (!guild) return;
|
||||
const channel = await guild.channels.fetch(channelId).catch(() => null);
|
||||
if (!channel || !channel.isTextBased()) return;
|
||||
const member = await guild.members.fetch(userId).catch(() => null);
|
||||
const fields = await prisma.registerFormField.findMany({ where: { formId: form.id }, orderBy: { order: 'asc' } });
|
||||
const answers = await prisma.registerApplicationAnswer.findMany({ where: { applicationId: app.id } });
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(`Registrierung: ${form.name}`)
|
||||
.setDescription(form.description || '')
|
||||
.setColor(0xf97316)
|
||||
.addFields(
|
||||
...fields.map((f) => ({
|
||||
name: f.label,
|
||||
value: answers.find((a) => a.fieldId === f.id)?.value || '-',
|
||||
inline: false
|
||||
}))
|
||||
)
|
||||
.setFooter({ text: `Status: ${app.status}` })
|
||||
.setTimestamp(new Date(app.createdAt));
|
||||
if (member) embed.setAuthor({ name: member.user.tag, iconURL: member.user.displayAvatarURL() });
|
||||
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder().setCustomId(`register:review:accept:${app.id}`).setLabel('Akzeptieren').setStyle(ButtonStyle.Success),
|
||||
new ButtonBuilder().setCustomId(`register:review:invite:${app.id}`).setLabel('Gespräch einladen').setStyle(ButtonStyle.Primary),
|
||||
new ButtonBuilder().setCustomId(`register:review:reject:${app.id}`).setLabel('Ablehnen').setStyle(ButtonStyle.Danger)
|
||||
);
|
||||
const notifyRoles = form.notifyRoleIds || cfg?.registerConfig?.notifyRoleIds || [];
|
||||
const content = notifyRoles.length ? notifyRoles.map((id: string) => `<@&${id}>`).join(' ') : null;
|
||||
await (channel as any).send({ content: content || undefined, embeds: [embed], components: [row] });
|
||||
}
|
||||
|
||||
private async updateReviewMessage(interaction: ButtonInteraction, app: any) {
|
||||
if (!interaction.message || !interaction.message.editable) return;
|
||||
const embed = (interaction.message.embeds?.[0] as any) ?? null;
|
||||
if (embed) {
|
||||
embed.data = { ...(embed.data || {}), footer: { text: `Status: ${app.status} | Reviewer: ${interaction.user.tag}` } };
|
||||
}
|
||||
const components = interaction.message.components;
|
||||
await interaction.message.edit({ embeds: embed ? [embed] : interaction.message.embeds, components });
|
||||
}
|
||||
|
||||
public async listApplications(guildId: string, status?: string, formId?: string) {
|
||||
const where: any = { guildId };
|
||||
if (status) where.status = status;
|
||||
if (formId) where.formId = formId;
|
||||
return prisma.registerApplication.findMany({
|
||||
where,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
include: { form: true }
|
||||
});
|
||||
}
|
||||
|
||||
public async getApplication(id: string) {
|
||||
return prisma.registerApplication.findUnique({
|
||||
where: { id },
|
||||
include: { form: true, answers: true }
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -543,6 +543,82 @@ router.delete('/reactionroles/:id', requireAuth, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/register/forms', requireAuth, async (req, res) => {
|
||||
const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
|
||||
if (!guildId) return res.status(400).json({ error: 'guildId required' });
|
||||
const forms = await context.register.listForms(guildId);
|
||||
res.json({ forms });
|
||||
});
|
||||
|
||||
router.post('/register/forms', requireAuth, async (req, res) => {
|
||||
const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
|
||||
if (!guildId) return res.status(400).json({ error: 'guildId required' });
|
||||
const data = req.body || {};
|
||||
const form = await context.register.saveForm({
|
||||
guildId,
|
||||
name: data.name || 'Formular',
|
||||
description: data.description || '',
|
||||
reviewChannelId: data.reviewChannelId || undefined,
|
||||
notifyRoleIds: Array.isArray(data.notifyRoleIds) ? data.notifyRoleIds : typeof data.notifyRoleIds === 'string' ? data.notifyRoleIds.split(',').map((s: string) => s.trim()).filter(Boolean) : [],
|
||||
isActive: data.isActive !== false,
|
||||
fields: Array.isArray(data.fields) ? data.fields : []
|
||||
});
|
||||
res.json({ form });
|
||||
});
|
||||
|
||||
router.put('/register/forms/:id', requireAuth, async (req, res) => {
|
||||
const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
|
||||
const id = req.params.id;
|
||||
if (!guildId) return res.status(400).json({ error: 'guildId required' });
|
||||
const data = req.body || {};
|
||||
const form = await context.register.saveForm({
|
||||
id,
|
||||
guildId,
|
||||
name: data.name || 'Formular',
|
||||
description: data.description || '',
|
||||
reviewChannelId: data.reviewChannelId || undefined,
|
||||
notifyRoleIds: Array.isArray(data.notifyRoleIds) ? data.notifyRoleIds : typeof data.notifyRoleIds === 'string' ? data.notifyRoleIds.split(',').map((s: string) => s.trim()).filter(Boolean) : [],
|
||||
isActive: data.isActive !== false,
|
||||
fields: Array.isArray(data.fields) ? data.fields : []
|
||||
});
|
||||
res.json({ form });
|
||||
});
|
||||
|
||||
router.delete('/register/forms/:id', requireAuth, async (req, res) => {
|
||||
const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
|
||||
const id = req.params.id;
|
||||
if (!guildId) return res.status(400).json({ error: 'guildId required' });
|
||||
const ok = await context.register.deleteForm(guildId, id);
|
||||
if (!ok) return res.status(404).json({ error: 'not found' });
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
router.post('/register/forms/:id/panel', requireAuth, async (req, res) => {
|
||||
const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
|
||||
const id = req.params.id;
|
||||
if (!guildId) return res.status(400).json({ error: 'guildId required' });
|
||||
const channelId = typeof req.body.channelId === 'string' ? req.body.channelId : undefined;
|
||||
const message = typeof req.body.message === 'string' ? req.body.message : undefined;
|
||||
const msgId = await context.register.sendPanel(guildId, id, channelId, message);
|
||||
if (!msgId) return res.status(400).json({ error: 'panel failed' });
|
||||
res.json({ ok: true, messageId: msgId });
|
||||
});
|
||||
|
||||
router.get('/register/apps', requireAuth, async (req, res) => {
|
||||
const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
|
||||
const status = typeof req.query.status === 'string' ? req.query.status : undefined;
|
||||
const formId = typeof req.query.formId === 'string' ? req.query.formId : undefined;
|
||||
if (!guildId) return res.status(400).json({ error: 'guildId required' });
|
||||
const apps = await context.register.listApplications(guildId, status, formId);
|
||||
res.json({ applications: apps });
|
||||
});
|
||||
|
||||
router.get('/register/apps/:id', requireAuth, async (req, res) => {
|
||||
const app = await context.register.getApplication(req.params.id);
|
||||
if (!app) return res.status(404).json({ error: 'not found' });
|
||||
res.json({ application: app });
|
||||
});
|
||||
|
||||
router.get('/automations', requireAuth, async (req, res) => {
|
||||
const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
|
||||
if (!guildId) return res.status(400).json({ error: 'guildId required' });
|
||||
@@ -700,9 +776,11 @@ router.post('/settings', requireAuth, async (req, res) => {
|
||||
eventsEnabled,
|
||||
birthdayEnabled,
|
||||
birthdayConfig,
|
||||
reactionRolesEnabled,
|
||||
reactionRolesConfig
|
||||
} = req.body;
|
||||
reactionRolesEnabled,
|
||||
reactionRolesConfig,
|
||||
registerEnabled,
|
||||
registerConfig
|
||||
} = req.body;
|
||||
if (!guildId) return res.status(400).json({ error: 'guildId required' });
|
||||
const normalizeArray = (val: any) =>
|
||||
Array.isArray(val)
|
||||
@@ -798,6 +876,22 @@ router.post('/settings', requireAuth, async (req, res) => {
|
||||
channelId: reactionRolesConfig?.channelId ?? existingReactionRoles.channelId
|
||||
};
|
||||
|
||||
const parsedRegister = {
|
||||
enabled:
|
||||
registerConfig?.enabled ??
|
||||
(typeof registerEnabled === 'string' ? registerEnabled === 'true' : registerEnabled ?? (current as any).registerEnabled),
|
||||
reviewChannelId: registerConfig?.reviewChannelId ?? (current as any).registerConfig?.reviewChannelId,
|
||||
notifyRoleIds:
|
||||
Array.isArray(registerConfig?.notifyRoleIds) && registerConfig?.notifyRoleIds.length
|
||||
? registerConfig.notifyRoleIds
|
||||
: typeof registerConfig?.notifyRoleIds === 'string'
|
||||
? registerConfig.notifyRoleIds
|
||||
.split(',')
|
||||
.map((s: string) => s.trim())
|
||||
.filter(Boolean)
|
||||
: (current as any).registerConfig?.notifyRoleIds || []
|
||||
};
|
||||
|
||||
const updated = await settingsStore.set(guildId, {
|
||||
welcomeChannelId: welcomeChannelId ?? undefined,
|
||||
logChannelId: logChannelId ?? undefined,
|
||||
@@ -817,7 +911,9 @@ router.post('/settings', requireAuth, async (req, res) => {
|
||||
birthdayEnabled: parsedBirthday.enabled,
|
||||
birthdayConfig: parsedBirthday,
|
||||
reactionRolesEnabled: parsedReactionRoles.enabled,
|
||||
reactionRolesConfig: parsedReactionRoles
|
||||
reactionRolesConfig: parsedReactionRoles,
|
||||
registerEnabled: parsedRegister.enabled,
|
||||
registerConfig: parsedRegister
|
||||
});
|
||||
// Live update logging target
|
||||
context.logging = new LoggingService(updated.logChannelId);
|
||||
|
||||
Reference in New Issue
Block a user