feat: initial Papo bot scaffold
This commit is contained in:
18
src/commands/tickets/claim.ts
Normal file
18
src/commands/tickets/claim.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
data: new SlashCommandBuilder().setName('claim').setDescription('Übernimmt das aktuelle Ticket.'),
|
||||
async execute(interaction: ChatInputCommandInteraction) {
|
||||
const ok = await context.tickets.claimTicket(interaction);
|
||||
if (!ok) {
|
||||
await interaction.reply({ content: 'Kein Ticket in diesem Kanal gefunden.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
await interaction.reply({ content: 'Ticket übernommen.' });
|
||||
}
|
||||
};
|
||||
|
||||
export default command;
|
||||
22
src/commands/tickets/close.ts
Normal file
22
src/commands/tickets/close.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('close')
|
||||
.setDescription('Schließt das aktuelle Ticket.')
|
||||
.addStringOption((opt) => opt.setName('reason').setDescription('Grund')),
|
||||
async execute(interaction: ChatInputCommandInteraction) {
|
||||
const reason = interaction.options.getString('reason') ?? undefined;
|
||||
const ok = await context.tickets.closeTicket(interaction, reason);
|
||||
if (!ok) {
|
||||
await interaction.reply({ content: 'Kein Ticket in diesem Kanal gefunden.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
await interaction.reply({ content: 'Ticket geschlossen.' });
|
||||
}
|
||||
};
|
||||
|
||||
export default command;
|
||||
26
src/commands/tickets/panel.ts
Normal file
26
src/commands/tickets/panel.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction, PermissionFlagsBits, ChannelType, TextChannel } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('ticketpanel')
|
||||
.setDescription('Sendet ein Ticket-Panel in einen Kanal.')
|
||||
.addChannelOption((opt) =>
|
||||
opt.setName('channel').setDescription('Zielkanal').addChannelTypes(ChannelType.GuildText)
|
||||
)
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageChannels),
|
||||
async execute(interaction: ChatInputCommandInteraction) {
|
||||
const target = (interaction.options.getChannel('channel') as TextChannel | null) ?? interaction.channel;
|
||||
if (!target || !target.isTextBased()) {
|
||||
await interaction.reply({ content: 'Bitte wähle einen Textkanal.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
const { embed, buttons } = context.tickets.buildPanelEmbed();
|
||||
await target.send({ embeds: [embed], components: [buttons] });
|
||||
await interaction.reply({ content: `Ticket-Panel gesendet in ${target}`, ephemeral: true });
|
||||
}
|
||||
};
|
||||
|
||||
export default command;
|
||||
34
src/commands/tickets/priority.ts
Normal file
34
src/commands/tickets/priority.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { prisma } from '../../database/index.js';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('ticketpriority')
|
||||
.setDescription('Setzt die Priorität des aktuellen Tickets.')
|
||||
.addStringOption((opt) =>
|
||||
opt
|
||||
.setName('level')
|
||||
.setDescription('Priorität')
|
||||
.addChoices(
|
||||
{ name: 'Low', value: 'low' },
|
||||
{ name: 'Normal', value: 'normal' },
|
||||
{ name: 'High', value: 'high' }
|
||||
)
|
||||
.setRequired(true)
|
||||
),
|
||||
async execute(interaction: ChatInputCommandInteraction) {
|
||||
const channelId = interaction.channelId;
|
||||
const ticket = await prisma.ticket.findFirst({ where: { channelId } });
|
||||
if (!ticket) {
|
||||
await interaction.reply({ content: 'Kein Ticket in diesem Kanal.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
const level = interaction.options.getString('level', true) as 'low' | 'normal' | 'high';
|
||||
await prisma.ticket.update({ where: { id: ticket.id }, data: { priority: level } });
|
||||
await interaction.reply({ content: `Ticket-Priorität gesetzt auf **${level}**.` });
|
||||
}
|
||||
};
|
||||
|
||||
export default command;
|
||||
33
src/commands/tickets/status.ts
Normal file
33
src/commands/tickets/status.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { prisma } from '../../database/index.js';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('ticketstatus')
|
||||
.setDescription('Ändert den Status des aktuellen Tickets.')
|
||||
.addStringOption((opt) =>
|
||||
opt
|
||||
.setName('status')
|
||||
.setDescription('Neuer Status')
|
||||
.addChoices(
|
||||
{ name: 'Offen', value: 'open' },
|
||||
{ name: 'In Bearbeitung', value: 'in-progress' },
|
||||
{ name: 'Geschlossen', value: 'closed' }
|
||||
)
|
||||
.setRequired(true)
|
||||
),
|
||||
async execute(interaction: ChatInputCommandInteraction) {
|
||||
const status = interaction.options.getString('status', true) as 'open' | 'in-progress' | 'closed';
|
||||
const ticket = await prisma.ticket.findFirst({ where: { channelId: interaction.channelId } });
|
||||
if (!ticket) {
|
||||
await interaction.reply({ content: 'Kein Ticket in diesem Kanal.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
await prisma.ticket.update({ where: { id: ticket.id }, data: { status } });
|
||||
await interaction.reply({ content: `Ticket-Status geändert zu **${status}**.` });
|
||||
}
|
||||
};
|
||||
|
||||
export default command;
|
||||
22
src/commands/tickets/ticket.ts
Normal file
22
src/commands/tickets/ticket.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { SlashCommand } from '../../utils/types.js';
|
||||
import { context } from '../../config/context.js';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
data: new SlashCommandBuilder().setName('ticket').setDescription('Erstellt ein persönliches Ticket.'),
|
||||
async execute(interaction: ChatInputCommandInteraction) {
|
||||
const ticket = await context.tickets.createTicket(interaction);
|
||||
if (!ticket) {
|
||||
if (!interaction.replied) {
|
||||
await interaction.reply({ content: 'Ticket konnte nicht erstellt werden.', ephemeral: true });
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!interaction.replied) {
|
||||
await interaction.reply({ content: 'Ticket erstellt! Schau in deinem neuen Kanal nach.', ephemeral: true });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default command;
|
||||
25
src/commands/tickets/transcript.ts
Normal file
25
src/commands/tickets/transcript.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
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 path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
const command: SlashCommand = {
|
||||
guildOnly: true,
|
||||
data: new SlashCommandBuilder().setName('transcript').setDescription('Exportiert das Transcript dieses Tickets.'),
|
||||
async execute(interaction: ChatInputCommandInteraction) {
|
||||
const channelId = interaction.channelId;
|
||||
const ticket = await prisma.ticket.findFirst({ where: { channelId } });
|
||||
if (!ticket) {
|
||||
await interaction.reply({ content: 'Kein Ticket in diesem Kanal.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const filePath = await context.tickets.exportTranscript(interaction.channel!, ticket.id);
|
||||
const fileName = path.basename(filePath);
|
||||
await interaction.reply({ content: `Transcript exportiert: ${fileName}`, files: [fs.createReadStream(filePath)] });
|
||||
}
|
||||
};
|
||||
|
||||
export default command;
|
||||
Reference in New Issue
Block a user