Change private help ticket cooldown

This commit is contained in:
Tarrgon
2025-09-14 08:00:51 -04:00
parent 0175eb8d96
commit 672a508d9d
14 changed files with 70 additions and 38 deletions
+1 -2
View File
@@ -1,5 +1,4 @@
import { ActionRowBuilder, ButtonInteraction, Client, ModalBuilder, TextInputBuilder, TextInputStyle, MessageFlags, ChannelType, PermissionFlagsBits, ButtonBuilder, ButtonStyle } from 'discord.js';
import { ticketCooldownMap } from '../shared/ticket-cooldown';
import { Database } from '../shared/Database';
export default {
@@ -19,7 +18,7 @@ export default {
const guildSettings = await Database.getGuildSettings(guild.id);
if (!guildSettings || !guildSettings.private_help_role_id)
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Failed to create ticket.' });
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Failed to claim ticket.' });
const closeButton = new ButtonBuilder()
.setCustomId('close-ticket')
+3 -1
View File
@@ -1,5 +1,5 @@
import { ActionRowBuilder, ButtonInteraction, Client, ModalBuilder, TextInputBuilder, TextInputStyle, MessageFlags, ChannelType, ButtonBuilder, ButtonStyle } from 'discord.js';
import { ticketCooldownMap } from '../shared/ticket-cooldown';
import { Database } from '../shared/Database';
export default {
name: 'close-ticket',
@@ -14,6 +14,8 @@ export default {
components: []
});
await Database.closePrivateHelpTicket(channel.id);
await channel.send(`This ticket has been closed by ${interaction.user}`);
await interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Ticket closed.' });
-1
View File
@@ -1,5 +1,4 @@
import { ActionRowBuilder, ButtonInteraction, Client, ModalBuilder, TextInputBuilder, TextInputStyle, MessageFlags } from 'discord.js';
import { ticketCooldownMap } from '../shared/ticket-cooldown';
import { Database } from '../shared/Database';
export default {
+3 -5
View File
@@ -1,13 +1,11 @@
import { ActionRowBuilder, ButtonInteraction, Client, ModalBuilder, TextInputBuilder, TextInputStyle, MessageFlags } from 'discord.js';
import { ticketCooldownMap } from '../shared/ticket-cooldown';
import { canOpenPrivateHelpTicket } from '../utils';
export default {
name: 'private-help',
handler: async function (client: Client, interaction: ButtonInteraction) {
const allowedAt = ticketCooldownMap.get(interaction.user.id);
if (allowedAt && Date.now() < allowedAt)
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'You can only create one ticket per day.' });
if (!(await canOpenPrivateHelpTicket(interaction.user.id)))
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'You can only have one open ticket at a time that is less than a day old.' });
const modal = new ModalBuilder()
.setCustomId('open-ticket-modal')
+1 -2
View File
@@ -1,5 +1,4 @@
import { ActionRowBuilder, ButtonInteraction, Client, ModalBuilder, TextInputBuilder, TextInputStyle, MessageFlags, ChannelType, PermissionFlagsBits, ButtonBuilder, ButtonStyle } from 'discord.js';
import { ticketCooldownMap } from '../shared/ticket-cooldown';
import { Database } from '../shared/Database';
export default {
@@ -21,7 +20,7 @@ export default {
const guildSettings = await Database.getGuildSettings(guild.id);
if (!guildSettings || !guildSettings.private_help_role_id)
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Failed to create ticket.' });
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Failed to unclaim ticket.' });
const closeButton = new ButtonBuilder()
.setCustomId('close-ticket')
-4
View File
@@ -6,7 +6,6 @@ import { checkExpiredBans, initIfNecessary, loadHandlersFrom, refreshCommands }
import { initializeWebserver } from './webserver';
import { Database } from './shared/Database';
import { handleAuditLogCreate, handleBanRemove, handleBulkMessageDelete, handleGuildCreate, handleMemberJoin, handleMessageCreate, handleMessageDelete, handleMessageUpdate, handleThreadCreate, handleVoiceStateUpdate } from './events';
import { pruneOldTickets, ticketCooldownMap } from './shared/ticket-cooldown';
import { openRedisClient } from './shared/RedisClient';
let ready = false;
@@ -137,9 +136,6 @@ client.on('ready', async () => {
await initializeWebserver(client);
// Prune ticket cooldowns that are expired every day
setInterval(pruneOldTickets, 8.64e+7);
// Check for expired bans every 5 minutes
checkExpiredBans(client);
setInterval(checkExpiredBans.bind(null, client), 300000);
-1
View File
@@ -1,5 +1,4 @@
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, Client, GuildTextBasedChannel, MessageFlags, ModalSubmitInteraction, TextChannel, ThreadAutoArchiveDuration } from 'discord.js';
import { ticketCooldownMap } from '../shared/ticket-cooldown';
import { Database } from '../shared/Database';
import { logCustomEvent } from '../utils';
+6 -3
View File
@@ -1,5 +1,4 @@
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, Client, GuildTextBasedChannel, MessageFlags, ModalSubmitInteraction, TextChannel, ThreadAutoArchiveDuration } from 'discord.js';
import { ticketCooldownMap } from '../shared/ticket-cooldown';
import { Database } from '../shared/Database';
export default {
@@ -13,8 +12,6 @@ export default {
if (!guildSettings || !guildSettings.private_help_role_id)
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Failed to create ticket. Please report this to a staff member.' });
ticketCooldownMap.set(interaction.user.id, Date.now() + 8.64e+7);
const reason = interaction.fields.getTextInputValue('ticket-message');
const channel = (await interaction.channel?.fetch()) as TextChannel;
@@ -26,6 +23,12 @@ export default {
type: ChannelType.PrivateThread
});
console.log(1);
await Database.createPrivateHelpTicket(interaction.user.id, thread.id);
console.log(2);
const closeButton = new ButtonBuilder()
.setCustomId('close-ticket')
.setLabel('Click here if you no longer need help')
-1
View File
@@ -1,5 +1,4 @@
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, Client, GuildTextBasedChannel, MessageFlags, ModalSubmitInteraction, TextChannel, ThreadAutoArchiveDuration } from 'discord.js';
import { ticketCooldownMap } from '../shared/ticket-cooldown';
import { Database } from '../shared/Database';
import { deferInteraction, logCustomEvent, syncName } from '../utils';
import { config } from '../config';
+35 -4
View File
@@ -1,9 +1,7 @@
import sqlite3 from 'sqlite3';
import { open, Database as SqliteDatabase } from 'sqlite';
import { config } from '../config';
import DiscordOAuth2 from 'discord-oauth2';
import { serializeMessage, wait } from '../utils';
import { GuildSettings, LoggedMessage, TicketMessage, TicketPhrase, Note, Ban, GuildArraySetting, GithubUserMapping, KnowledgebaseItem } from '../types';
import { GuildSettings, LoggedMessage, TicketMessage, TicketPhrase, Note, Ban, GuildArraySetting, GithubUserMapping, KnowledgebaseItem, PrivateHelpTicket } from '../types';
import { Message } from '../events';
const DB_SCHEMA = `
@@ -67,6 +65,8 @@ const DB_SCHEMA = `
timestamp datetime NOT NULL DEFAULT (datetime('now', 'localtime'))
);
CREATE INDEX IF NOT EXISTS index_user_ids ON notes (user_id);
CREATE TABLE IF NOT EXISTS note_edits (
id INTEGER PRIMARY KEY,
note_id INTEGER,
@@ -93,9 +93,24 @@ const DB_SCHEMA = `
guild_id TEXT NOT NULL,
name TEXT NOT NULL,
content TEXT NOT NULL
)
);
CREATE TABLE IF NOT EXISTS private_help_tickets (
id INTEGER PRIMARY KEY,
thread_id TEXT NOT NULL,
user_id TEXT NOT NULL,
status INTEGER NOT NULL,
timestamp datetime NOT NULL DEFAULT (datetime('now', 'localtime'))
);
CREATE INDEX IF NOT EXISTS index_timestamp ON private_help_tickets (timestamp);
`;
export const enum PrivateHelpTicketStatus {
OPEN = 0,
CLOSED = 1
}
export class Database {
private static db: SqliteDatabase;
@@ -434,4 +449,20 @@ export class Database {
}
// -- END KNOWLEDGEBASE --
// -- START PRIVATE HELP TICKETS --
static async createPrivateHelpTicket(userId: string, threadId: string) {
await Database.db.run('INSERT INTO private_help_tickets(user_id, thread_id, status) VALUES (?, ?, ?)', userId, threadId, PrivateHelpTicketStatus.OPEN);
}
static async closePrivateHelpTicket(threadId: string) {
await Database.db.run('UPDATE private_help_tickets SET status = ? WHERE thread_id = ?', PrivateHelpTicketStatus.CLOSED, threadId);
}
static async getLatestPrivateHelpTicketBy(userId: string): Promise<PrivateHelpTicket | undefined> {
return await Database.db.get<PrivateHelpTicket>('SELECT * from private_help_tickets WHERE user_id = ? ORDER BY timestamp DESC LIMIT 1', userId);
}
// -- END PRIVATE HELP TICKETS
}
-9
View File
@@ -1,9 +0,0 @@
export const ticketCooldownMap = new Map<string, number>();
export function pruneOldTickets() {
const keys = Array.from(ticketCooldownMap.keys());
for (const key of keys) {
if (Date.now() >= ticketCooldownMap.get(key)!)
ticketCooldownMap.delete(key);
}
}
+7
View File
@@ -65,4 +65,11 @@ export type KnowledgebaseItem = {
guild_id: string
name: string
content: string
}
export type PrivateHelpTicket = {
id: number
user_id: string
status: PrivateHelpTicketStatus,
timestamp: string
}
+5 -5
View File
@@ -1,5 +1,5 @@
export * from './command';
export * from './database-types';
export * from './e621-types';
export * from './handler';
export * from './helper-types';
export * from './command.d';
export * from './database-types.d';
export * from './e621-types.d';
export * from './handler.d';
export * from './helper-types.d';
+9
View File
@@ -1,4 +1,5 @@
import { Client, Guild, User } from 'discord.js';
import { Database, PrivateHelpTicketStatus } from '../shared/Database';
export async function resolveUser(client: Client, value: string, guild: Guild | null = null): Promise<User | null | undefined> {
let user: User | null | undefined = null;
@@ -24,4 +25,12 @@ export async function resolveUser(client: Client, value: string, guild: Guild |
}
return user;
}
export async function canOpenPrivateHelpTicket(id: string): Promise<boolean> {
const latestTicket = await Database.getLatestPrivateHelpTicketBy(id);
if (latestTicket && latestTicket.status == PrivateHelpTicketStatus.OPEN && Date.now() - new Date(latestTicket.timestamp).getTime() < 8.64e+7) return false;
return true;
}