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