help command + limit users on amount of tickets
This commit is contained in:
parent
637c2800d4
commit
56e2dcfff6
@ -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}`,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
44
src/commands/admin/help.ts
Normal file
44
src/commands/admin/help.ts
Normal 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] })
|
||||||
|
}
|
||||||
|
}
|
59
src/commands/admin/remove-active-ticket.ts
Normal file
59
src/commands/admin/remove-active-ticket.ts
Normal 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')
|
||||||
|
}
|
||||||
|
}
|
@ -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'
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user