Add events module with dashboard UI, scheduling, signups, and settings updates; extend env/readme.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { LoopMode } from '../../services/musicService.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
import { LoopMode } from '../../services/musicService';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
@@ -15,3 +15,4 @@ const command: SlashCommand = {
|
||||
};
|
||||
|
||||
export default command;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction, EmbedBuilder } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction, PermissionFlagsBits, ChannelType, TextChannel } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { prisma } from '../../database/index.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { prisma } from '../../database/index';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { prisma } from '../../database/index.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { prisma } from '../../database/index';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { prisma } from '../../database/index.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
import { prisma } from '../../database/index';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
|
||||
35
src/commands/utility/birthday.ts
Normal file
35
src/commands/utility/birthday.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { normalizeBirthdayInput } from '../../services/birthdayService';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('birthday')
|
||||
.setDescription('Speichert dein Geburtsdatum.')
|
||||
.addStringOption((opt) => opt.setName('datum').setDescription('Format: DD.MM.YYYY, DD.MM oder YYYY-MM-DD').setRequired(true)),
|
||||
async execute(interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.guildId) {
|
||||
await interaction.reply({ content: 'Dieser Befehl funktioniert nur auf Servern.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
const raw = interaction.options.getString('datum', true);
|
||||
const normalized = normalizeBirthdayInput(raw);
|
||||
if (!normalized) {
|
||||
await interaction.reply({ content: 'Bitte gib ein gueltiges Datum an (DD.MM.YYYY, DD.MM oder YYYY-MM-DD).', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
await context.birthdays.setBirthday(interaction.guildId, interaction.user.id, normalized);
|
||||
const formatted = normalized.startsWith('--')
|
||||
? normalized
|
||||
.replace('--', '')
|
||||
.split('-')
|
||||
.reverse()
|
||||
.join('.') + '.'
|
||||
: normalized.split('-').reverse().join('.');
|
||||
await interaction.reply({ content: `Geburtstag gespeichert: ${formatted}`, ephemeral: true });
|
||||
}
|
||||
};
|
||||
|
||||
export default command;
|
||||
@@ -1,33 +1,82 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction, PermissionFlagsBits, ChannelType } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { settings } from '../../config/state.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { settingsStore } from '../../config/state';
|
||||
import { context } from '../../config/context';
|
||||
import { LoggingService } from '../../services/loggingService';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('configure')
|
||||
.setDescription('Setzt Basis-Einstellungen des Bots (gildenspezifisch).')
|
||||
.addChannelOption((opt) => opt.setName('welcome_channel').setDescription('Kanal für Willkommensnachrichten').addChannelTypes(ChannelType.GuildText))
|
||||
.addChannelOption((opt) => opt.setName('log_channel').setDescription('Kanal für Logs').addChannelTypes(ChannelType.GuildText))
|
||||
.addChannelOption((opt) =>
|
||||
opt.setName('welcome_channel').setDescription('Kanal fuer Willkommensnachrichten').addChannelTypes(ChannelType.GuildText)
|
||||
)
|
||||
.addChannelOption((opt) => opt.setName('log_channel').setDescription('Kanal fuer Logs').addChannelTypes(ChannelType.GuildText))
|
||||
.addBooleanOption((opt) => opt.setName('automod').setDescription('Automod an/aus'))
|
||||
.addNumberOption((opt) => opt.setName('spam_threshold').setDescription('Spam-Schwelle (Nachrichten im Zeitfenster)'))
|
||||
.addNumberOption((opt) => opt.setName('spam_window_ms').setDescription('Spam-Zeitfenster in Millisekunden'))
|
||||
.addNumberOption((opt) => opt.setName('spam_timeout_minutes').setDescription('Timeout-Dauer (Minuten) bei Spam'))
|
||||
.addStringOption((opt) => opt.setName('link_whitelist').setDescription('Kommagetrennte Link-Whitelist (Domains)'))
|
||||
.addBooleanOption((opt) => opt.setName('leveling').setDescription('Level-System an/aus'))
|
||||
.addBooleanOption((opt) => opt.setName('dynamic_voice').setDescription('Dynamische Voice Channels an/aus'))
|
||||
.addChannelOption((opt) =>
|
||||
opt.setName('voice_lobby').setDescription('Voice-Lobby fuer dynamische Channels').addChannelTypes(ChannelType.GuildVoice)
|
||||
)
|
||||
.addStringOption((opt) => opt.setName('voice_template').setDescription('Name-Template, z.B. {user}s Channel'))
|
||||
.addIntegerOption((opt) => opt.setName('voice_user_limit').setDescription('Userlimit fuer dynamische Channels'))
|
||||
.addRoleOption((opt) => opt.setName('support_role').setDescription('Support-Rolle fuer Tickets'))
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild),
|
||||
async execute(interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.guildId) return;
|
||||
const guildSetting = settings.get(interaction.guildId) ?? {};
|
||||
const guildSetting: any = settingsStore.get(interaction.guildId) ?? {};
|
||||
const welcome = interaction.options.getChannel('welcome_channel');
|
||||
const logChannel = interaction.options.getChannel('log_channel');
|
||||
const automod = interaction.options.getBoolean('automod');
|
||||
const spamThreshold = interaction.options.getNumber('spam_threshold');
|
||||
const spamWindowMs = interaction.options.getNumber('spam_window_ms');
|
||||
const spamTimeoutMinutes = interaction.options.getNumber('spam_timeout_minutes');
|
||||
const linkWhitelist = interaction.options.getString('link_whitelist');
|
||||
const leveling = interaction.options.getBoolean('leveling');
|
||||
const dynamicVoice = interaction.options.getBoolean('dynamic_voice');
|
||||
const voiceLobby = interaction.options.getChannel('voice_lobby');
|
||||
const voiceTemplate = interaction.options.getString('voice_template');
|
||||
const voiceUserLimit = interaction.options.getInteger('voice_user_limit');
|
||||
const supportRole = interaction.options.getRole('support_role');
|
||||
|
||||
if (welcome) guildSetting.welcomeChannelId = welcome.id;
|
||||
if (logChannel) guildSetting.logChannelId = logChannel.id;
|
||||
if (automod !== null) guildSetting.automodEnabled = automod;
|
||||
if (spamThreshold !== null || spamWindowMs !== null || spamTimeoutMinutes !== null || linkWhitelist !== null) {
|
||||
guildSetting.automodConfig = {
|
||||
...(guildSetting.automodConfig ?? {}),
|
||||
spamThreshold: spamThreshold ?? guildSetting.automodConfig?.spamThreshold,
|
||||
windowMs: spamWindowMs ?? guildSetting.automodConfig?.windowMs,
|
||||
spamTimeoutMinutes: spamTimeoutMinutes ?? guildSetting.automodConfig?.spamTimeoutMinutes,
|
||||
linkWhitelist:
|
||||
linkWhitelist !== null
|
||||
? linkWhitelist
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
: guildSetting.automodConfig?.linkWhitelist
|
||||
};
|
||||
}
|
||||
if (leveling !== null) guildSetting.levelingEnabled = leveling;
|
||||
if (dynamicVoice !== null) guildSetting.dynamicVoiceEnabled = dynamicVoice;
|
||||
if (voiceLobby || voiceTemplate || voiceUserLimit !== null) {
|
||||
guildSetting.dynamicVoiceConfig = {
|
||||
...(guildSetting.dynamicVoiceConfig ?? {}),
|
||||
lobbyChannelId: voiceLobby?.id ?? guildSetting.dynamicVoiceConfig?.lobbyChannelId,
|
||||
categoryId: voiceLobby?.parentId ?? guildSetting.dynamicVoiceConfig?.categoryId,
|
||||
template: voiceTemplate ?? guildSetting.dynamicVoiceConfig?.template,
|
||||
userLimit: voiceUserLimit ?? guildSetting.dynamicVoiceConfig?.userLimit
|
||||
};
|
||||
}
|
||||
if (supportRole) guildSetting.supportRoleId = supportRole.id;
|
||||
|
||||
settings.set(interaction.guildId, guildSetting);
|
||||
context.logging = new (context.logging.constructor as any)(guildSetting.logChannelId);
|
||||
const updated = await settingsStore.set(interaction.guildId, guildSetting);
|
||||
context.logging = new LoggingService(updated.logChannelId);
|
||||
|
||||
await interaction.reply({ content: 'Einstellungen gespeichert.', ephemeral: true });
|
||||
}
|
||||
|
||||
29
src/commands/utility/event.ts
Normal file
29
src/commands/utility/event.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
import { settingsStore } from '../../config/state';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('event')
|
||||
.setDescription('Zeigt Events an.')
|
||||
.addStringOption((opt) => opt.setName('list').setDescription('Zeigt naechste Events').setChoices({ name: 'show', value: 'show' })),
|
||||
async execute(interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.guildId) return;
|
||||
const cfg = settingsStore.get(interaction.guildId);
|
||||
if ((cfg as any)?.eventsEnabled === false) {
|
||||
await interaction.reply({ content: 'Events sind deaktiviert.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
const events = await (context as any).events.listEvents(interaction.guildId);
|
||||
if (!events?.length) {
|
||||
await interaction.reply({ content: 'Keine Events vorhanden.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
const lines = events.slice(0, 5).map((ev: any) => `• ${ev.title} - <t:${Math.floor(new Date(ev.startTime).getTime() / 1000)}:f>`);
|
||||
await interaction.reply({ content: lines.join('\n'), ephemeral: true });
|
||||
}
|
||||
};
|
||||
|
||||
export default command;
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction, EmbedBuilder } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
|
||||
const command: SlashCommand = {
|
||||
data: new SlashCommandBuilder().setName('help').setDescription('Zeigt Befehle und Module.'),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
|
||||
const command: SlashCommand = {
|
||||
data: new SlashCommandBuilder().setName('ping').setDescription('Antwortet mit Pong!'),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction, EmbedBuilder } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
|
||||
33
src/commands/utility/statuspage.ts
Normal file
33
src/commands/utility/statuspage.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction, EmbedBuilder } from 'discord.js';
|
||||
import { context } from '../../config/context';
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder().setName('status').setDescription('Zeigt Statuspage-Services.'),
|
||||
async execute(interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.guildId) {
|
||||
await interaction.reply({ content: 'Nur in Guilds verfuegbar.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
const cfg = await context.statuspage.getConfig(interaction.guildId);
|
||||
const services = cfg.services ?? [];
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle('Service Status')
|
||||
.setColor(services.some((s) => s.status === 'down') ? 0xef4444 : 0x22c55e)
|
||||
.setDescription(services.length ? '' : 'Keine Services konfiguriert.')
|
||||
.setTimestamp(new Date());
|
||||
services.forEach((s) => {
|
||||
const icon = s.status === 'up' ? '✅' : s.status === 'down' ? '❌' : '⚪';
|
||||
const upPct =
|
||||
s.upChecks && s.totalChecks
|
||||
? Math.round(((s.upChecks ?? 0) / Math.max(1, s.totalChecks ?? 1)) * 100)
|
||||
: 0;
|
||||
const last = s.lastChecked ? new Date(s.lastChecked).toLocaleString() : 'n/a';
|
||||
embed.addFields({
|
||||
name: `${icon} ${s.name}`,
|
||||
value: `Status: ${s.status ?? 'unknown'} | Uptime: ${upPct}% | Letzter Check: ${last}`,
|
||||
inline: false
|
||||
});
|
||||
});
|
||||
await interaction.reply({ embeds: [embed], ephemeral: true });
|
||||
}
|
||||
};
|
||||
63
src/commands/utility/welcome.ts
Normal file
63
src/commands/utility/welcome.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { ChatInputCommandInteraction, EmbedBuilder, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||
import { settingsStore } from '../../config/state';
|
||||
import { SlashCommand } from '../../utils/types';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('welcome')
|
||||
.setDescription('Loest die konfigurierte Willkommensnachricht aus.')
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild),
|
||||
async execute(interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.guildId || !interaction.guild) {
|
||||
await interaction.reply({ content: 'Dieser Befehl ist nur in Guilds verfuegbar.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
const cfg = settingsStore.get(interaction.guildId) ?? {};
|
||||
const welcome = cfg.welcomeConfig || cfg.automodConfig?.welcomeConfig;
|
||||
if (!welcome?.enabled || !welcome.channelId) {
|
||||
await interaction.reply({ content: 'Willkommensnachrichten sind fuer diesen Server nicht konfiguriert.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
const channel = interaction.guild.channels.cache.get(welcome.channelId) || (await interaction.guild.channels.fetch(welcome.channelId).catch(() => null));
|
||||
if (!channel || !channel.isTextBased()) {
|
||||
await interaction.reply({ content: 'Ziel-Channel fuer Willkommensnachrichten ist ungueltig.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
const colorVal = parseInt((welcome.embedColor || '00ff99').replace('#', ''), 16);
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(welcome.embedTitle || 'Willkommen!')
|
||||
.setDescription(welcome.embedDescription || 'Schön, dass du da bist.')
|
||||
.setColor(isNaN(colorVal) ? 0x00ff99 : colorVal)
|
||||
.setFooter({ text: welcome.embedFooter || '' });
|
||||
const files: any[] = [];
|
||||
if (welcome.embedThumbnailData && welcome.embedThumbnailData.startsWith('data:')) {
|
||||
const [meta, b64] = welcome.embedThumbnailData.split(',');
|
||||
const ext = meta.includes('gif') ? 'gif' : 'png';
|
||||
const buf = Buffer.from(b64, 'base64');
|
||||
const name = `welcome-thumb.${ext}`;
|
||||
files.push({ attachment: buf, name });
|
||||
embed.setThumbnail(`attachment://${name}`);
|
||||
} else if (welcome.embedThumbnail) {
|
||||
embed.setThumbnail(welcome.embedThumbnail);
|
||||
}
|
||||
if (welcome.embedImageData && welcome.embedImageData.startsWith('data:')) {
|
||||
const [meta, b64] = welcome.embedImageData.split(',');
|
||||
const ext = meta.includes('gif') ? 'gif' : 'png';
|
||||
const buf = Buffer.from(b64, 'base64');
|
||||
const name = `welcome-image.${ext}`;
|
||||
files.push({ attachment: buf, name });
|
||||
embed.setImage(`attachment://${name}`);
|
||||
} else if (welcome.embedImage) {
|
||||
embed.setImage(welcome.embedImage);
|
||||
}
|
||||
await channel.send({ embeds: [embed], files }).catch(async () => {
|
||||
await interaction.reply({ content: 'Senden der Willkommensnachricht ist fehlgeschlagen.', ephemeral: true });
|
||||
});
|
||||
if (!interaction.replied && !interaction.deferred) {
|
||||
await interaction.reply({ content: 'Willkommensnachricht gesendet.', ephemeral: true });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default command;
|
||||
Reference in New Issue
Block a user