help command + limit users on amount of tickets

This commit is contained in:
Danya H 2024-06-10 17:39:31 +01:00
parent 637c2800d4
commit 56e2dcfff6
12 changed files with 174 additions and 21 deletions

View File

@ -9,8 +9,9 @@ import {
GuildTextThreadCreateOptions, GuildTextThreadCreateOptions,
MessageCreateOptions, MessageCreateOptions,
Role, Role,
roleMention, ThreadChannel, roleMention,
userMention ThreadChannel,
userMention,
} from 'discord.js' } from 'discord.js'
import { db, DBTableEnum } from '../../db' import { db, DBTableEnum } from '../../db'
@ -25,6 +26,8 @@ import {
} from '../../utils' } from '../../utils'
import { logger } from '../../lib' import { logger } from '../../lib'
type Ticket = { user: string; channel: string }
@Discord() @Discord()
export class CreateTicketSystem { export class CreateTicketSystem {
@Slash({ @Slash({
@ -124,15 +127,15 @@ export class CreateTicketSystem {
return return
} }
// prevent creating ticket for users with 3 or more tickets const hasOpenedTicket = await db
const allThreads = await interaction.channel.threads.fetch() .get<Ticket[]>(DBTableEnum.TICKET_OWNERS)
const ownedThreads = allThreads.threads.reduce((total, thread) => { .then(r =>
if (thread.ownerId === interaction.user.id && (!thread.archived || !thread.locked)) total += 1 Array.isArray(r)
return total ? !!r.find(ticket => ticket.user === interaction.user.id)
}, 0) : false,
if (ownedThreads >= 3 && interaction.user.id !== interaction.guild.ownerId) { )
logger.action('Denied ticket creation', `User: ${interaction.user.username}(${interaction.user.id})\nActive tickets: ${ownedThreads}`) if (hasOpenedTicket) {
await interaction.editReply('❌ You already have too many tickets') await interaction.editReply(`❌ You already have an active ticket`)
return return
} }
@ -143,7 +146,7 @@ export class CreateTicketSystem {
// create ticket channel // create ticket channel
const threadChannelSettings: GuildTextThreadCreateOptions<AllowedThreadTypeForTextChannel> = const threadChannelSettings: GuildTextThreadCreateOptions<AllowedThreadTypeForTextChannel> =
{ {
name: `${interaction.user.username}-${(new Date).toLocaleDateString().replaceAll('/', '-')}`, name: `${interaction.user.username}-${new Date().toLocaleDateString().replaceAll('/', '-')}`,
type: ChannelType.PrivateThread, type: ChannelType.PrivateThread,
invitable: false, invitable: false,
} }
@ -155,6 +158,22 @@ export class CreateTicketSystem {
return return
} }
await db
.push<Ticket>(DBTableEnum.TICKET_OWNERS, {
user: interaction.user.id,
channel: threadChannel.id,
})
.then(() => {
logger.database(
'Updated active tickets',
`Added User: ${interaction.user.id}\n Channel: ${threadChannel.id}`,
)
})
logger.database(
'Updated active tickets',
`Removed channel ${interaction.channel.id}`,
)
// welcoming message in ticket // welcoming message in ticket
const threadChannelMessage: MessageCreateOptions = { const threadChannelMessage: MessageCreateOptions = {
content: `${userMention(interaction.user.id)} ${roleMention(ticketRole)}`, content: `${userMention(interaction.user.id)} ${roleMention(ticketRole)}`,
@ -186,7 +205,10 @@ export class CreateTicketSystem {
await threadChannel.members.add(user) await threadChannel.members.add(user)
await threadChannel.members.add(owner) await threadChannel.members.add(owner)
logger.action('Created ticket', `User: ${user.user.username}(${user.id})\nChannel: ${threadChannel.name}(${threadChannel.id})`) logger.action(
'Created ticket',
`User: ${user.user.username}(${user.id})\nChannel: ${threadChannel.name}(${threadChannel.id})`,
)
// close interaction // close interaction
await interaction.editReply({ await interaction.editReply({
@ -203,7 +225,10 @@ export class CreateTicketSystem {
async closeBtn(interaction: ButtonInteraction): Promise<void> { async closeBtn(interaction: ButtonInteraction): Promise<void> {
await interaction.deferReply() await interaction.deferReply()
logger.action('Ticket close attempt', `User: ${interaction.user.username}(${interaction.user.id})\nChannel: ${interaction.channel?.id}`) logger.action(
'Ticket close attempt',
`User: ${interaction.user.username}(${interaction.user.id})\nChannel: ${interaction.channel?.id}`,
)
if (!interaction.channel || !interaction.guild) { if (!interaction.channel || !interaction.guild) {
await interaction.editReply('❌ Ticket channel does not exist') await interaction.editReply('❌ Ticket channel does not exist')
@ -216,18 +241,42 @@ export class CreateTicketSystem {
await interaction.editReply('Closing ticket..') await interaction.editReply('Closing ticket..')
try { try {
// remove ticket owner if exists
const ticketOwner = interaction.channel.ownerId ? await interaction.guild.members.fetch(interaction.channel.ownerId) : null
ticketOwner ? await interaction.channel.members.remove(ticketOwner) : null
// lock + archive thread // lock + archive thread
await interaction.channel.setLocked(true, `Locked by ${userMention(interaction.user.id)}(${interaction.user.id})`) await interaction.channel.setLocked(
await interaction.channel.setArchived(true, `Archived by ${userMention(interaction.user.id)}(${interaction.user.id})`) true,
`Locked by ${userMention(interaction.user.id)}(${interaction.user.id})`,
)
await interaction.channel.setArchived(
true,
`Archived by ${userMention(interaction.user.id)}(${interaction.user.id})`,
)
} catch (e) { } catch (e) {
logger.error('Closing ticket', e) logger.error('Closing ticket', e)
return return
} }
logger.action('Ticket close attempt successful', `User: ${interaction.user.username}(${interaction.user.id})\nChannel: ${interaction.channel?.id}`) const tickets = await db.get<Ticket[]>(DBTableEnum.TICKET_OWNERS)
if (tickets) {
const res = tickets.reduce<Ticket[]>((res, ticket) => {
if (
interaction.channel &&
ticket.channel === interaction.channel.id
) {
res.push(ticket)
}
return res
}, [])
await db.set(DBTableEnum.TICKET_OWNERS, res).then(() => {
logger.database(
'Updated active tickets',
`Removed channel ${interaction.channel?.id}`,
)
})
}
logger.action(
'Ticket close attempt successful',
`User: ${interaction.user.username}(${interaction.user.id})\nChannel: ${interaction.channel?.id}`,
)
} }
} }

View File

@ -0,0 +1,44 @@
import { Discord, Slash } from 'discordx'
import { CommandInteraction, EmbedBuilder } from 'discord.js'
@Discord()
export class Payments {
@Slash({
name: 'payments',
description: 'See available payments',
defaultMemberPermissions: 'Administrator',
})
async payments(interaction: CommandInteraction) {
await interaction.deferReply({ ephemeral: true })
const embed = new EmbedBuilder().setTitle(`Commands`).setFields([
{
name: 'Users',
value: '```/feedback\n' + '/payments```',
},
{
name: 'Setters',
value:
'```/set-banner-ticket\n' +
'/set-banner-welcome\n' +
'/set-feedback-channel\n' +
'/set-portfolio-channel\n' +
'/set-price-channel\n' +
'/set-order-channel\n' +
'/set-welcome-channel\n' +
'/set-status\n```',
},
{
name: 'Admin',
value:
'```/ping - потому что могу\n\n' +
'/help - без понятия\n\n' +
'/create-ticket-system - Создать систему тикетов отталкиваясь от текущего канала.\n' +
'Требует /set-banner-ticket /set-prices-channel /set-status\n\n' +
'/remove-active-ticket - удалить из базы все активные тикеты пользователя если что-то произошло и они не удалились при архивации тикета```',
},
])
await interaction.editReply({ embeds: [embed] })
}
}

View File

@ -0,0 +1,59 @@
import { Discord, Slash, SlashChoice, SlashOption } from 'discordx'
import {
ApplicationCommandOptionType,
CommandInteraction,
User,
} from 'discord.js'
import { db, DBTableEnum } from '../../db'
import { logger } from '../../lib'
import { feedbackEmbed } from '../../utils'
type Ticket = {
user: string
channel: string
}
@Discord()
export class RemoveActiveTicket {
@Slash({
name: 'remove-active-ticket',
description: 'Remove user from database in case of need',
defaultMemberPermissions: 'Administrator',
})
async feedback(
@SlashOption({
name: 'user',
description: 'User to be removed',
required: true,
type: ApplicationCommandOptionType.User,
})
user: User,
interaction: CommandInteraction,
) {
await interaction.deferReply({ ephemeral: true })
const activeTickets = await db.get<Ticket[]>(DBTableEnum.TICKET_OWNERS)
if (!activeTickets || !activeTickets.length) {
await interaction.editReply('❌ No active tickets found for server')
return
}
const res = activeTickets.reduce<Ticket[]>((r, ticket) => {
if (ticket.user !== user.id) {
r.push(ticket)
}
return r
}, [])
await db
.set(DBTableEnum.TICKET_OWNERS, res)
.then(() =>
logger.database(
DBTableEnum.TICKET_OWNERS,
`Removed ${user.id}\nCaller: ${interaction.user.id}`,
),
)
await interaction.editReply('✔️ User tickets cleared successfully')
}
}

View File

@ -18,4 +18,5 @@ export enum DBTableEnum {
WORKLOAD_MESSAGE = 'WORKLOAD_MESSAGE', WORKLOAD_MESSAGE = 'WORKLOAD_MESSAGE',
WORKLOAD_CHANNEL = 'WORKLOAD_CHANNEL', WORKLOAD_CHANNEL = 'WORKLOAD_CHANNEL',
TICKET_ROLE = 'TICKET_ROLE', TICKET_ROLE = 'TICKET_ROLE',
TICKET_OWNERS = 'TICKET_OWNERS'
} }