104 lines
3.6 KiB
TypeScript
104 lines
3.6 KiB
TypeScript
import { prisma } from '../database';
|
|
import { context } from '../config/context';
|
|
import { TextChannel } from 'discord.js';
|
|
|
|
type AutomationCondition = {
|
|
category?: string;
|
|
status?: string;
|
|
minHours?: number;
|
|
};
|
|
|
|
type AutomationAction = {
|
|
type: 'pingRole' | 'reminder' | 'flag';
|
|
roleId?: string;
|
|
message?: string;
|
|
status?: string;
|
|
};
|
|
|
|
export class TicketAutomationService {
|
|
public async list(guildId: string) {
|
|
return prisma.ticketAutomationRule.findMany({ where: { guildId }, orderBy: { createdAt: 'asc' } });
|
|
}
|
|
|
|
public async save(rule: {
|
|
id?: string;
|
|
guildId: string;
|
|
name: string;
|
|
condition: AutomationCondition;
|
|
action: AutomationAction;
|
|
active?: boolean;
|
|
}) {
|
|
if (rule.id) {
|
|
return prisma.ticketAutomationRule.update({
|
|
where: { id: rule.id },
|
|
data: {
|
|
name: rule.name,
|
|
condition: rule.condition,
|
|
action: rule.action,
|
|
active: rule.active ?? true
|
|
}
|
|
});
|
|
}
|
|
return prisma.ticketAutomationRule.create({
|
|
data: {
|
|
guildId: rule.guildId,
|
|
name: rule.name,
|
|
condition: rule.condition,
|
|
action: rule.action,
|
|
active: rule.active ?? true
|
|
}
|
|
});
|
|
}
|
|
|
|
public async remove(guildId: string, id: string) {
|
|
const found = await prisma.ticketAutomationRule.findFirst({ where: { id, guildId } });
|
|
if (!found) return false;
|
|
await prisma.ticketAutomationRule.delete({ where: { id } });
|
|
return true;
|
|
}
|
|
|
|
public async checkTicket(ticket: any, isScheduled = false) {
|
|
const rules = await prisma.ticketAutomationRule.findMany({ where: { guildId: ticket.guildId, active: true }, take: 50 });
|
|
if (!rules.length) return;
|
|
const guild = context.client?.guilds.cache.get(ticket.guildId) ?? (await context.client?.guilds.fetch(ticket.guildId).catch(() => null));
|
|
if (!guild) return;
|
|
const channel = ticket.channelId ? await guild.channels.fetch(ticket.channelId).catch(() => null) : null;
|
|
for (const rule of rules) {
|
|
const cond = (rule.condition as any) || {};
|
|
const act = (rule.action as any) || {};
|
|
const matchesCategory =
|
|
!cond.category || (ticket.topic || '').toLowerCase().includes(String(cond.category).toLowerCase());
|
|
const matchesStatus = !cond.status || ticket.status === cond.status;
|
|
const matchesAge =
|
|
!cond.minHours ||
|
|
(ticket.createdAt &&
|
|
Date.now() - new Date(ticket.createdAt).getTime() >= Number(cond.minHours) * 3600 * 1000);
|
|
if (!matchesCategory || !matchesStatus || !matchesAge) continue;
|
|
if (act.type === 'pingRole' && channel?.isTextBased() && act.roleId) {
|
|
await (channel as TextChannel).send({ content: `<@&${act.roleId}> Bitte Ticket pruefen.` }).catch(() => undefined);
|
|
}
|
|
if (act.type === 'reminder' && channel?.isTextBased()) {
|
|
await (channel as TextChannel)
|
|
.send({ content: act.message || 'Reminder: Ticket ist noch offen.' })
|
|
.catch(() => undefined);
|
|
}
|
|
if (act.type === 'flag' && act.status && ticket.status !== act.status) {
|
|
await prisma.ticket.update({ where: { id: ticket.id }, data: { status: act.status } }).catch(() => undefined);
|
|
}
|
|
}
|
|
}
|
|
|
|
public startLoop() {
|
|
setInterval(() => {
|
|
const since = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
|
prisma.ticket
|
|
.findMany({
|
|
where: { status: { notIn: ['erledigt', 'closed'] }, createdAt: { lte: since } },
|
|
take: 50
|
|
})
|
|
.then((tickets) => tickets.forEach((t) => this.checkTicket(t, true)))
|
|
.catch(() => undefined);
|
|
}, 60_000);
|
|
}
|
|
}
|