changes:
- prisma migration - better ticket handling - better error handling
This commit is contained in:
parent
ef1c5be2b7
commit
fd558bd975
Binary file not shown.
@ -1,21 +1,21 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Settings" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"welcomeChannelId" INTEGER NOT NULL,
|
||||
"feedbackChannelId" INTEGER NOT NULL,
|
||||
"portfolioChannelId" INTEGER NOT NULL,
|
||||
"makeAnOrderChannelId" INTEGER NOT NULL,
|
||||
"priceChannelId" INTEGER NOT NULL,
|
||||
"workLoadChannelId" INTEGER NOT NULL,
|
||||
"worlLoadStatus" INTEGER NOT NULL,
|
||||
"workloadMessageId" INTEGER NOT NULL
|
||||
"welcomeChannelId" TEXT NOT NULL,
|
||||
"feedbackChannelId" TEXT NOT NULL,
|
||||
"portfolioChannelId" TEXT NOT NULL,
|
||||
"makeAnOrderChannelId" TEXT NOT NULL,
|
||||
"priceChannelId" TEXT NOT NULL,
|
||||
"workLoadChannelId" TEXT NOT NULL,
|
||||
"worlLoadStatus" TEXT NOT NULL,
|
||||
"workloadMessageId" TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Role" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"welcomeRoleId" INTEGER NOT NULL,
|
||||
"tickerRoleId" INTEGER NOT NULL
|
||||
"welcomeRoleId" TEXT NOT NULL,
|
||||
"tickerRoleId" TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
@ -28,13 +28,13 @@ CREATE TABLE "Banner" (
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"discordId" INTEGER NOT NULL
|
||||
"discordId" TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Ticket" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"channelId" INTEGER NOT NULL,
|
||||
"channelId" TEXT NOT NULL,
|
||||
"closed" BOOLEAN NOT NULL,
|
||||
"userId" INTEGER,
|
||||
CONSTRAINT "Ticket_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
58
prisma/migrations/20240805230451_fixes/migration.sql
Normal file
58
prisma/migrations/20240805230451_fixes/migration.sql
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `tickerRoleId` on the `Role` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `worlLoadStatus` on the `Settings` table. All the data in the column will be lost.
|
||||
- The primary key for the `Ticket` table will be changed. If it partially fails, the table could be left without primary key constraint.
|
||||
- You are about to drop the column `id` on the `Ticket` table. All the data in the column will be lost.
|
||||
- The primary key for the `User` table will be changed. If it partially fails, the table could be left without primary key constraint.
|
||||
- You are about to drop the column `id` on the `User` table. All the data in the column will be lost.
|
||||
- Added the required column `ticketRoleId` to the `Role` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `workLoadStatus` to the `Settings` table without a default value. This is not possible if the table is not empty.
|
||||
- Made the column `userId` on table `Ticket` required. This step will fail if there are existing NULL values in that column.
|
||||
|
||||
*/
|
||||
-- RedefineTables
|
||||
PRAGMA defer_foreign_keys=ON;
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_Role" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"welcomeRoleId" TEXT NOT NULL,
|
||||
"ticketRoleId" TEXT NOT NULL
|
||||
);
|
||||
INSERT INTO "new_Role" ("id", "welcomeRoleId") SELECT "id", "welcomeRoleId" FROM "Role";
|
||||
DROP TABLE "Role";
|
||||
ALTER TABLE "new_Role" RENAME TO "Role";
|
||||
CREATE TABLE "new_Settings" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"welcomeChannelId" TEXT NOT NULL,
|
||||
"feedbackChannelId" TEXT NOT NULL,
|
||||
"portfolioChannelId" TEXT NOT NULL,
|
||||
"makeAnOrderChannelId" TEXT NOT NULL,
|
||||
"priceChannelId" TEXT NOT NULL,
|
||||
"workLoadChannelId" TEXT NOT NULL,
|
||||
"workLoadStatus" TEXT NOT NULL,
|
||||
"workloadMessageId" TEXT NOT NULL
|
||||
);
|
||||
INSERT INTO "new_Settings" ("feedbackChannelId", "id", "makeAnOrderChannelId", "portfolioChannelId", "priceChannelId", "welcomeChannelId", "workLoadChannelId", "workloadMessageId") SELECT "feedbackChannelId", "id", "makeAnOrderChannelId", "portfolioChannelId", "priceChannelId", "welcomeChannelId", "workLoadChannelId", "workloadMessageId" FROM "Settings";
|
||||
DROP TABLE "Settings";
|
||||
ALTER TABLE "new_Settings" RENAME TO "Settings";
|
||||
CREATE TABLE "new_Ticket" (
|
||||
"channelId" TEXT NOT NULL,
|
||||
"closed" BOOLEAN NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
CONSTRAINT "Ticket_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("discordId") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_Ticket" ("channelId", "closed", "userId") SELECT "channelId", "closed", "userId" FROM "Ticket";
|
||||
DROP TABLE "Ticket";
|
||||
ALTER TABLE "new_Ticket" RENAME TO "Ticket";
|
||||
CREATE UNIQUE INDEX "Ticket_channelId_key" ON "Ticket"("channelId");
|
||||
CREATE TABLE "new_User" (
|
||||
"discordId" TEXT NOT NULL
|
||||
);
|
||||
INSERT INTO "new_User" ("discordId") SELECT "discordId" FROM "User";
|
||||
DROP TABLE "User";
|
||||
ALTER TABLE "new_User" RENAME TO "User";
|
||||
CREATE UNIQUE INDEX "User_discordId_key" ON "User"("discordId");
|
||||
PRAGMA foreign_keys=ON;
|
||||
PRAGMA defer_foreign_keys=OFF;
|
@ -9,20 +9,20 @@ datasource db {
|
||||
|
||||
model Settings {
|
||||
id Int @id @default(autoincrement())
|
||||
welcomeChannelId Int
|
||||
feedbackChannelId Int
|
||||
portfolioChannelId Int
|
||||
makeAnOrderChannelId Int
|
||||
priceChannelId Int
|
||||
workLoadChannelId Int
|
||||
worlLoadStatus Int
|
||||
workloadMessageId Int
|
||||
welcomeChannelId String
|
||||
feedbackChannelId String
|
||||
portfolioChannelId String
|
||||
makeAnOrderChannelId String
|
||||
priceChannelId String
|
||||
workLoadChannelId String
|
||||
workLoadStatus String
|
||||
workloadMessageId String
|
||||
}
|
||||
|
||||
model Role {
|
||||
id Int @id @default(autoincrement())
|
||||
welcomeRoleId Int
|
||||
tickerRoleId Int
|
||||
welcomeRoleId String
|
||||
ticketRoleId String
|
||||
}
|
||||
|
||||
model Banner {
|
||||
@ -32,15 +32,13 @@ model Banner {
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
discordId Int
|
||||
discordId String @unique
|
||||
tickets Ticket[]
|
||||
}
|
||||
|
||||
model Ticket {
|
||||
id Int @id
|
||||
channelId Int
|
||||
channelId String @unique
|
||||
closed Boolean
|
||||
User User? @relation(fields: [userId], references: [id])
|
||||
userId Int?
|
||||
User User @relation(fields: [userId], references: [discordId])
|
||||
userId String
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
userMention,
|
||||
} from 'discord.js'
|
||||
|
||||
import { db, DBTableEnum } from '../../db'
|
||||
import * as db from '../../db'
|
||||
import {
|
||||
Workload,
|
||||
ticketCreateButton,
|
||||
@ -26,8 +26,6 @@ import {
|
||||
} from '../../utils'
|
||||
import { logger } from '../../lib'
|
||||
|
||||
type Ticket = { user: string; channel: string }
|
||||
|
||||
@Discord()
|
||||
export class CreateTicketSystem {
|
||||
@Slash({
|
||||
@ -55,11 +53,10 @@ export class CreateTicketSystem {
|
||||
return
|
||||
}
|
||||
|
||||
const pricesChannelId = await db.get(DBTableEnum.PRICE_CHANNEL)
|
||||
const bannerURL = await db.get(DBTableEnum.BANNER_TICKET)
|
||||
const workload = await db.get<Workload>(DBTableEnum.WORKLOAD)
|
||||
const settings = await db.getSettings()
|
||||
const banners = await db.getBanner()
|
||||
|
||||
if (!pricesChannelId) {
|
||||
if (!settings || !settings.priceChannelId) {
|
||||
logger.error(
|
||||
'Missing prices channel',
|
||||
'Set using /set-prices-channel',
|
||||
@ -69,14 +66,14 @@ export class CreateTicketSystem {
|
||||
)
|
||||
return
|
||||
}
|
||||
if (!bannerURL) {
|
||||
if (!banners || !banners.ticketUrl) {
|
||||
logger.error('Missing banner', 'Set using /set-banner-ticket')
|
||||
await interaction.editReply(
|
||||
'❌ Missing banner\nSet using /set-banner-ticket',
|
||||
)
|
||||
return
|
||||
}
|
||||
if (!workload) {
|
||||
if (!settings.workLoadStatus) {
|
||||
logger.error(
|
||||
'Missing workload status',
|
||||
'Set using /set-workload-status',
|
||||
@ -88,21 +85,35 @@ export class CreateTicketSystem {
|
||||
}
|
||||
|
||||
await db
|
||||
.set(DBTableEnum.TICKET_ROLE, role.id)
|
||||
.then(() => logger.database(DBTableEnum.TICKET_ROLE, role.id))
|
||||
.setRole('ticketRoleId', role.id)
|
||||
.then(() => logger.database('Ticket role', role.id))
|
||||
.catch(async () => {
|
||||
logger.error('Ticket role id', 'Failed to set ticket role id')
|
||||
await interaction.editReply('❌ Failed to set ticket role id')
|
||||
return
|
||||
})
|
||||
|
||||
// create ticket embed + button
|
||||
await interaction.channel.send({
|
||||
components: [ticketCreateButton()],
|
||||
embeds: [ticketCreateEmbed({ bannerURL, pricesChannelId })],
|
||||
embeds: [
|
||||
ticketCreateEmbed({
|
||||
bannerURL: banners.ticketUrl,
|
||||
pricesChannelId: settings.priceChannelId,
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
// workload embed
|
||||
const workloadMessage = await interaction.channel.send({
|
||||
embeds: [ticketWorkloadEmbed({ workload })],
|
||||
embeds: [
|
||||
ticketWorkloadEmbed({
|
||||
workload: settings.workLoadStatus as Workload,
|
||||
}),
|
||||
],
|
||||
})
|
||||
await db.set(DBTableEnum.WORKLOAD_MESSAGE, workloadMessage.id)
|
||||
await db.set(DBTableEnum.WORKLOAD_CHANNEL, interaction.channel.id)
|
||||
await db.setSettings('workloadMessageId', workloadMessage.id)
|
||||
await db.setSettings('workLoadChannelId', interaction.channel.id)
|
||||
|
||||
// close interaction
|
||||
await interaction.editReply('✔️ Created ticket system')
|
||||
@ -127,19 +138,16 @@ export class CreateTicketSystem {
|
||||
return
|
||||
}
|
||||
|
||||
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`)
|
||||
const hasManyTickets =
|
||||
(await db.getUserOpenedTicketsAmount(interaction.user.id)) > 3
|
||||
if (hasManyTickets) {
|
||||
await interaction.editReply(`❌ You already have 3 opened tickets`)
|
||||
return
|
||||
}
|
||||
|
||||
const ticketRole = await db.get(DBTableEnum.TICKET_ROLE)
|
||||
const roles = await db.getRole()
|
||||
|
||||
const ticketRole = roles?.ticketRoleId
|
||||
let threadChannel: ThreadChannel<boolean>
|
||||
|
||||
try {
|
||||
@ -159,9 +167,9 @@ export class CreateTicketSystem {
|
||||
}
|
||||
|
||||
await db
|
||||
.push<Ticket>(DBTableEnum.TICKET_OWNERS, {
|
||||
user: interaction.user.id,
|
||||
channel: threadChannel.id,
|
||||
.createUser(interaction.user.id, {
|
||||
closed: false,
|
||||
channelId: threadChannel.id,
|
||||
})
|
||||
.then(() => {
|
||||
logger.database(
|
||||
@ -169,14 +177,10 @@ export class CreateTicketSystem {
|
||||
`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)}`,
|
||||
content: `${userMention(interaction.user.id)} ${roleMention(ticketRole!)}`,
|
||||
embeds: [
|
||||
ticketEntityEmbed({
|
||||
username: interaction.user.username,
|
||||
@ -188,7 +192,7 @@ export class CreateTicketSystem {
|
||||
|
||||
// add role members and ticket owner
|
||||
const user = await interaction.guild.members.fetch(interaction.user.id)
|
||||
const role = await interaction.guild.roles.fetch(ticketRole)
|
||||
const role = await interaction.guild.roles.fetch(ticketRole!)
|
||||
const owner = await interaction.guild.fetchOwner()
|
||||
|
||||
if (!role) {
|
||||
@ -259,24 +263,12 @@ export class CreateTicketSystem {
|
||||
return
|
||||
}
|
||||
|
||||
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(() => {
|
||||
await db.closeTicket(interaction.channel.id).then(() => {
|
||||
logger.database(
|
||||
'Updated active tickets',
|
||||
`Removed channel ${interaction.channel?.id}`,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
logger.action(
|
||||
'Ticket close attempt successful',
|
||||
|
@ -17,7 +17,7 @@ export class Help {
|
||||
value: '```/feedback\n' + '/payments```',
|
||||
},
|
||||
{
|
||||
name: 'Setters',
|
||||
name: 'Setters (preferably in order)',
|
||||
value:
|
||||
'```/set-banner-ticket\n' +
|
||||
'/set-banner-welcome\n' +
|
||||
@ -31,11 +31,11 @@ export class Help {
|
||||
{
|
||||
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 - удалить из базы все активные тикеты пользователя если что-то произошло и они не удалились при архивации тикета```',
|
||||
'```/ping\n\n' +
|
||||
'/help\n\n' +
|
||||
'/create-ticket-system - Create ticket system based on channel where the command is executed.\n' +
|
||||
'Requires /set-banner-ticket /set-prices-channel /set-status\n\n' +
|
||||
'/remove-active-ticket - forces closing of all user tickets in case of errors```',
|
||||
},
|
||||
])
|
||||
|
||||
|
@ -5,19 +5,19 @@ import {
|
||||
User,
|
||||
} from 'discord.js'
|
||||
|
||||
import { db, DBTableEnum } from '../../db'
|
||||
import {
|
||||
getUser,
|
||||
closeUserTickets,
|
||||
createUserSilent,
|
||||
getUserOpenedTicketsAmount,
|
||||
} from '../../db'
|
||||
import { logger } from '../../lib'
|
||||
|
||||
type Ticket = {
|
||||
user: string
|
||||
channel: string
|
||||
}
|
||||
|
||||
@Discord()
|
||||
export class RemoveActiveTicket {
|
||||
@Slash({
|
||||
name: 'remove-active-ticket',
|
||||
description: 'Remove user from database in case of need',
|
||||
description: 'Closes all user tickets manually if needed',
|
||||
defaultMemberPermissions: 'Administrator',
|
||||
})
|
||||
async removeActiveTicket(
|
||||
@ -32,27 +32,20 @@ export class RemoveActiveTicket {
|
||||
) {
|
||||
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')
|
||||
const userDB = await getUser(user.id)
|
||||
if (!userDB || !(await getUserOpenedTicketsAmount(user.id))) {
|
||||
await createUserSilent(user.id)
|
||||
await interaction.editReply('❌ No active tickets found for user')
|
||||
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 closeUserTickets(user.id).then(() =>
|
||||
logger.action(
|
||||
'Changed user',
|
||||
`Forced close all tickets for ${user.globalName}(${user.id})`,
|
||||
),
|
||||
)
|
||||
|
||||
await interaction.editReply('✔️ User tickets cleared successfully')
|
||||
await interaction.editReply('✔️ User tickets closed successfully')
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
import { Discord, Slash, SlashChoice, SlashOption } from 'discordx'
|
||||
import { ApplicationCommandOptionType, CommandInteraction } from 'discord.js'
|
||||
import {
|
||||
ApplicationCommandOptionType,
|
||||
CommandInteraction,
|
||||
SnowflakeUtil,
|
||||
} from 'discord.js'
|
||||
|
||||
import { db, DBTableEnum } from '../db'
|
||||
import { getSettings } from '../db'
|
||||
import { logger } from '../lib'
|
||||
import { feedbackEmbed } from '../utils'
|
||||
|
||||
@ -33,8 +37,8 @@ export class Feedback {
|
||||
|
||||
if (!interaction.guild) return
|
||||
|
||||
const reviewChannelID = await db.get(DBTableEnum.FEEDBACK_CHANNEL)
|
||||
if (!reviewChannelID) {
|
||||
const settings = await getSettings()
|
||||
if (!settings || !settings.feedbackChannelId) {
|
||||
logger.error(
|
||||
'Missing feedback channel in database',
|
||||
'Recreate using /set-feedback-channel',
|
||||
@ -42,14 +46,16 @@ export class Feedback {
|
||||
await interaction.editReply(
|
||||
'❌ Feedback channel is not set. Please try again later',
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const reviewChannel =
|
||||
await interaction.guild.channels.fetch(reviewChannelID)
|
||||
const reviewChannel = await interaction.guild.channels.fetch(
|
||||
settings.feedbackChannelId,
|
||||
)
|
||||
if (!reviewChannel) {
|
||||
logger.error(
|
||||
'Missing feedback discord channel',
|
||||
`Feedback channel id exists in database (${reviewChannelID}) but is not found in channels list`,
|
||||
`Feedback channel id exists in database (${settings.feedbackChannelId}) but is not found in channels list`,
|
||||
)
|
||||
await interaction.editReply(
|
||||
'❌ Feedback channel is not set. Please try again later',
|
||||
@ -59,7 +65,7 @@ export class Feedback {
|
||||
if (!reviewChannel || !reviewChannel.isTextBased()) {
|
||||
logger.error(
|
||||
'Missing feedback discord channel',
|
||||
`Feedback channel id exists in database (${reviewChannelID}) but is not a text channel or is not found in channels list`,
|
||||
`Feedback channel id exists in database (${settings.feedbackChannelId}) but is not a text channel or is not found in channels list`,
|
||||
)
|
||||
await interaction.editReply(
|
||||
'❌ Feedback channel is not set. Please try again later',
|
||||
@ -67,6 +73,8 @@ export class Feedback {
|
||||
return
|
||||
}
|
||||
|
||||
const nonce = SnowflakeUtil.generate().toString()
|
||||
|
||||
await reviewChannel.send({
|
||||
embeds: [
|
||||
feedbackEmbed({
|
||||
@ -75,6 +83,7 @@ export class Feedback {
|
||||
rating,
|
||||
}),
|
||||
],
|
||||
nonce,
|
||||
})
|
||||
|
||||
await interaction.editReply('✔️ Review sent successfully!')
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Discord, Slash, SlashOption } from 'discordx'
|
||||
import { ApplicationCommandOptionType, CommandInteraction } from 'discord.js'
|
||||
|
||||
import { db, DBTableEnum } from '../../db'
|
||||
import { setBanner } from '../../db'
|
||||
import { logger } from '../../lib'
|
||||
|
||||
@Discord()
|
||||
@ -22,13 +22,16 @@ export class SetBannerTicket {
|
||||
interaction: CommandInteraction,
|
||||
) {
|
||||
await interaction.deferReply({ ephemeral: true })
|
||||
await db.set(DBTableEnum.BANNER_TICKET, url).catch(async () => {
|
||||
|
||||
await setBanner('ticketUrl', url)
|
||||
.then(() => logger.database('Banner Ticket URL', url))
|
||||
.catch(async () => {
|
||||
await interaction.editReply({
|
||||
content: `❌ Failed to set banner`,
|
||||
})
|
||||
return
|
||||
})
|
||||
logger.database(DBTableEnum.BANNER_TICKET, url)
|
||||
|
||||
await interaction.editReply({
|
||||
content: `✔️ Set banner URL to ${url}`,
|
||||
})
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Discord, Slash, SlashOption } from 'discordx'
|
||||
import { ApplicationCommandOptionType, CommandInteraction } from 'discord.js'
|
||||
|
||||
import { db, DBTableEnum } from '../../db'
|
||||
import { setBanner } from '../../db'
|
||||
import { logger } from '../../lib'
|
||||
|
||||
@Discord()
|
||||
@ -22,13 +22,16 @@ export class SetBannerWelcome {
|
||||
interaction: CommandInteraction,
|
||||
) {
|
||||
await interaction.deferReply({ ephemeral: true })
|
||||
await db.set(DBTableEnum.BANNER_WELCOME, url).catch(async () => {
|
||||
|
||||
await setBanner('welcomeUrl', url)
|
||||
.then(() => logger.database('Welcome Ticket URL', url))
|
||||
.catch(async () => {
|
||||
await interaction.editReply({
|
||||
content: `❌ Failed to set banner`,
|
||||
})
|
||||
return
|
||||
})
|
||||
logger.database(DBTableEnum.BANNER_WELCOME, url)
|
||||
|
||||
await interaction.editReply({
|
||||
content: `✔️ Set banner URL to ${url}`,
|
||||
})
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
TextChannel,
|
||||
} from 'discord.js'
|
||||
|
||||
import { db, DBTableEnum } from '../../db'
|
||||
import { setSettings } from '../../db'
|
||||
import { logger } from '../../lib'
|
||||
|
||||
@Discord()
|
||||
@ -26,15 +26,15 @@ export class SetFeedbackChannel {
|
||||
interaction: CommandInteraction,
|
||||
) {
|
||||
await interaction.deferReply({ ephemeral: true })
|
||||
await db
|
||||
.set(DBTableEnum.FEEDBACK_CHANNEL, channel.id)
|
||||
|
||||
await setSettings('feedbackChannelId', channel.id)
|
||||
.then(() => logger.database('Feedback Channel', channel.id))
|
||||
.catch(async () => {
|
||||
await interaction.editReply({
|
||||
content: `❌ Failed to set feedback channel`,
|
||||
})
|
||||
return
|
||||
})
|
||||
logger.database(DBTableEnum.FEEDBACK_CHANNEL, channel.id)
|
||||
await interaction.editReply({
|
||||
content: `✔️ Set feedback channel to ${channel.id}`,
|
||||
})
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
TextChannel,
|
||||
} from 'discord.js'
|
||||
|
||||
import { db, DBTableEnum } from '../../db'
|
||||
import { setSettings } from '../../db'
|
||||
import { logger } from '../../lib'
|
||||
|
||||
@Discord()
|
||||
@ -26,15 +26,16 @@ export class SetOrderChannel {
|
||||
interaction: CommandInteraction,
|
||||
) {
|
||||
await interaction.deferReply({ ephemeral: true })
|
||||
await db
|
||||
.set(DBTableEnum.MAKE_AN_ORDER_CHANNEL, channel.id)
|
||||
|
||||
await setSettings('makeAnOrderChannelId', channel.id)
|
||||
.then(() => logger.database('Order Channel', channel.id))
|
||||
.catch(async () => {
|
||||
await interaction.editReply({
|
||||
content: `❌ Failed to set make an order channel`,
|
||||
})
|
||||
return
|
||||
})
|
||||
logger.database(DBTableEnum.MAKE_AN_ORDER_CHANNEL, channel.id)
|
||||
|
||||
await interaction.editReply({
|
||||
content: `✔️ Set make an order channel to ${channel.id}`,
|
||||
})
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
TextChannel,
|
||||
} from 'discord.js'
|
||||
|
||||
import { db, DBTableEnum } from '../../db'
|
||||
import { setSettings } from '../../db'
|
||||
import { logger } from '../../lib'
|
||||
|
||||
@Discord()
|
||||
@ -26,15 +26,16 @@ export class SetPortfolioChannel {
|
||||
interaction: CommandInteraction,
|
||||
) {
|
||||
await interaction.deferReply({ ephemeral: true })
|
||||
await db
|
||||
.set(DBTableEnum.PORTFOLIO_CHANNEL, channel.id)
|
||||
|
||||
await setSettings('portfolioChannelId', channel.id)
|
||||
.then(() => logger.database('Portfolio Channel', channel.id))
|
||||
.catch(async () => {
|
||||
await interaction.editReply({
|
||||
content: `❌ Failed to set portfolio channel`,
|
||||
})
|
||||
return
|
||||
})
|
||||
logger.database(DBTableEnum.PORTFOLIO_CHANNEL, channel.id)
|
||||
|
||||
await interaction.editReply({
|
||||
content: `✔️ Set portfolio channel to ${channel.id}`,
|
||||
})
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
TextChannel,
|
||||
} from 'discord.js'
|
||||
|
||||
import { db, DBTableEnum } from '../../db'
|
||||
import { setSettings } from '../../db'
|
||||
import { logger } from '../../lib'
|
||||
|
||||
@Discord()
|
||||
@ -26,13 +26,16 @@ export class SetPriceChannel {
|
||||
interaction: CommandInteraction,
|
||||
) {
|
||||
await interaction.deferReply({ ephemeral: true })
|
||||
await db.set(DBTableEnum.PRICE_CHANNEL, channel.id).catch(async () => {
|
||||
|
||||
await setSettings('priceChannelId', channel.id)
|
||||
.then(() => logger.database('Price Channel', channel.id))
|
||||
.catch(async () => {
|
||||
await interaction.editReply({
|
||||
content: `❌ Failed to set price channel`,
|
||||
})
|
||||
return
|
||||
})
|
||||
logger.database(DBTableEnum.PRICE_CHANNEL, channel.id)
|
||||
|
||||
await interaction.editReply({
|
||||
content: `✔️ Set price channel to ${channel.id}`,
|
||||
})
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Discord, Slash, SlashChoice, SlashOption } from 'discordx'
|
||||
import { ApplicationCommandOptionType, CommandInteraction, GuildBasedChannel } from 'discord.js'
|
||||
import { ApplicationCommandOptionType, CommandInteraction } from 'discord.js'
|
||||
|
||||
import { db, DBTableEnum } from '../../db'
|
||||
import { setSettings, getSettings } from '../../db'
|
||||
import { ticketWorkloadEmbed, Workload } from '../../utils'
|
||||
import { logger } from '../../lib'
|
||||
|
||||
@ -24,25 +24,38 @@ export class SetStatus {
|
||||
interaction: CommandInteraction,
|
||||
) {
|
||||
await interaction.deferReply({ ephemeral: true })
|
||||
await db.set(DBTableEnum.WORKLOAD, status).catch(async () => {
|
||||
|
||||
await setSettings('workLoadStatus', status)
|
||||
.then(() => logger.database('Workload Status', status))
|
||||
.catch(async () => {
|
||||
await interaction.editReply({
|
||||
content: `❌ Failed to workload status`,
|
||||
})
|
||||
return
|
||||
})
|
||||
|
||||
const statusMsg = await db.get(DBTableEnum.WORKLOAD_MESSAGE)
|
||||
const statusChannelId = await db.get(DBTableEnum.WORKLOAD_CHANNEL)
|
||||
if (statusMsg && statusChannelId) {
|
||||
const statusChannel = await interaction.guild?.channels.fetch(statusChannelId)
|
||||
const settings = await getSettings()
|
||||
|
||||
if (!settings || !settings.workLoadChannelId)
|
||||
return await interaction.editReply({
|
||||
content: `❌ Failed to find workload channel`,
|
||||
})
|
||||
|
||||
if (settings.workLoadStatus && settings.workLoadChannelId) {
|
||||
const statusChannel = await interaction.guild?.channels.fetch(
|
||||
settings.workLoadChannelId,
|
||||
)
|
||||
|
||||
if (!statusChannel || !statusChannel.isTextBased()) return
|
||||
const workloadMsg = await statusChannel.messages.fetch(statusMsg)
|
||||
|
||||
const workloadMsg = await statusChannel.messages.fetch(
|
||||
settings.workloadMessageId,
|
||||
)
|
||||
await workloadMsg.edit({
|
||||
embeds: [ticketWorkloadEmbed({ workload: status as Workload })]
|
||||
embeds: [ticketWorkloadEmbed({ workload: status as Workload })],
|
||||
})
|
||||
}
|
||||
|
||||
logger.database(DBTableEnum.WORKLOAD, status)
|
||||
await interaction.editReply({
|
||||
content: `✔️ Set workload status to ${status}`,
|
||||
})
|
||||
|
@ -1,13 +1,12 @@
|
||||
import { Client, Discord, Slash, SlashOption } from 'discordx'
|
||||
import { Discord, Slash, SlashOption } from 'discordx'
|
||||
import {
|
||||
ApplicationCommandOptionType,
|
||||
CommandInteraction,
|
||||
Role,
|
||||
TextChannel,
|
||||
} from 'discord.js'
|
||||
import { db, DBTableEnum } from '../../db'
|
||||
import { setSettings, getSettings, setRole } from '../../db'
|
||||
import { logger } from '../../lib'
|
||||
import { Workload } from '../../utils'
|
||||
|
||||
@Discord()
|
||||
export class SetWelcomeChannel {
|
||||
@ -35,8 +34,8 @@ export class SetWelcomeChannel {
|
||||
) {
|
||||
await interaction.deferReply({ ephemeral: true })
|
||||
|
||||
const portfolioChannelId = await db.get(DBTableEnum.PORTFOLIO_CHANNEL)
|
||||
if (!portfolioChannelId) {
|
||||
const settings = await getSettings()
|
||||
if (!settings || !settings.portfolioChannelId) {
|
||||
logger.error(
|
||||
'Missing portfolio channel',
|
||||
'Set using /set-portfolio-channel',
|
||||
@ -47,8 +46,7 @@ export class SetWelcomeChannel {
|
||||
return
|
||||
}
|
||||
|
||||
const orderChannelId = await db.get(DBTableEnum.MAKE_AN_ORDER_CHANNEL)
|
||||
if (!orderChannelId) {
|
||||
if (!settings.makeAnOrderChannelId) {
|
||||
logger.error(
|
||||
'Missing make an order channel',
|
||||
'Set using /set-order-channel',
|
||||
@ -59,23 +57,23 @@ export class SetWelcomeChannel {
|
||||
return
|
||||
}
|
||||
|
||||
await db
|
||||
.set(DBTableEnum.WELCOME_CHANNEL, channel.id)
|
||||
await setSettings('welcomeChannelId', channel.id)
|
||||
.then(() => logger.database('Welcome channel', channel.id))
|
||||
.catch(async () => {
|
||||
await interaction.editReply({
|
||||
content: `❌ Failed to welcome channel`,
|
||||
})
|
||||
return
|
||||
})
|
||||
logger.database(DBTableEnum.WELCOME_CHANNEL, channel.id)
|
||||
|
||||
await db.set(DBTableEnum.WELCOME_ROLE, role.id).catch(async () => {
|
||||
await setRole('welcomeRoleId', role.id)
|
||||
.then(() => logger.database('Welcome role', role.id))
|
||||
.catch(async () => {
|
||||
await interaction.editReply({
|
||||
content: `❌ Failed to entry role`,
|
||||
content: `❌ Failed to set entry role`,
|
||||
})
|
||||
return
|
||||
})
|
||||
logger.database(DBTableEnum.WELCOME_ROLE, role.id)
|
||||
|
||||
await interaction.editReply({
|
||||
content: `✔️ Set welcome channel to ${channel.id}\n✔️ Set entry role to ${role.id}`,
|
||||
|
@ -1,3 +0,0 @@
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
export const db = new PrismaClient()
|
13
src/db/banner.ts
Normal file
13
src/db/banner.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { Prisma } from '@prisma/client'
|
||||
import { db } from './'
|
||||
|
||||
export function getBanner() {
|
||||
return db.banner.findFirst()
|
||||
}
|
||||
|
||||
export function setBanner<T extends keyof Prisma.BannerCreateInput>(key: T, value: Prisma.BannerCreateInput[T]) {
|
||||
return db.banner.update({
|
||||
where: { id: 0 },
|
||||
data: { [key]: value }
|
||||
})
|
||||
}
|
9
src/db/index.ts
Normal file
9
src/db/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
export const db = new PrismaClient()
|
||||
|
||||
export * from './banner'
|
||||
export * from './role'
|
||||
export * from './settings'
|
||||
export * from './ticket'
|
||||
export * from './user'
|
13
src/db/role.ts
Normal file
13
src/db/role.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { Prisma } from '@prisma/client'
|
||||
import { db } from './'
|
||||
|
||||
export function getRole() {
|
||||
return db.role.findFirst()
|
||||
}
|
||||
|
||||
export function setRole<T extends keyof Prisma.RoleCreateInput>(key: T, value: Prisma.RoleCreateInput[T]) {
|
||||
return db.role.update({
|
||||
where: { id: 0 },
|
||||
data: { [key]: value }
|
||||
})
|
||||
}
|
16
src/db/settings.ts
Normal file
16
src/db/settings.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { Prisma } from '@prisma/client'
|
||||
import { db } from './'
|
||||
|
||||
export async function getSettings() {
|
||||
return db.settings.findFirst()
|
||||
}
|
||||
|
||||
export function setSettings<T extends keyof Prisma.SettingsCreateInput>(
|
||||
key: T,
|
||||
value: Prisma.SettingsCreateInput[T],
|
||||
) {
|
||||
return db.settings.update({
|
||||
where: { id: 0 },
|
||||
data: { [key]: value },
|
||||
})
|
||||
}
|
19
src/db/ticket.ts
Normal file
19
src/db/ticket.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { db } from './'
|
||||
import { Prisma } from '@prisma/client'
|
||||
|
||||
export async function createTicket(ticket: Prisma.TicketCreateInput, userId: string) {
|
||||
return db.ticket.create({
|
||||
data: {
|
||||
channelId: ticket.channelId,
|
||||
userId,
|
||||
closed: ticket.closed,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function closeTicket(channelId: string) {
|
||||
return db.ticket.update({
|
||||
where: { channelId },
|
||||
data: { closed: true }
|
||||
})
|
||||
}
|
82
src/db/user.ts
Normal file
82
src/db/user.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import { db } from './'
|
||||
import { Prisma } from '@prisma/client'
|
||||
|
||||
export async function getUser(id: string) {
|
||||
return db.user.findFirst({
|
||||
where: {
|
||||
discordId: id,
|
||||
},
|
||||
include: {
|
||||
tickets: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function getUserOpenedTicketsAmount(discordId: string) {
|
||||
const data = await db.user.findFirst({
|
||||
where: { discordId },
|
||||
include: {
|
||||
tickets: {
|
||||
where: {
|
||||
closed: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (!data) return 0
|
||||
|
||||
return data.tickets.length
|
||||
}
|
||||
|
||||
export async function createUser(
|
||||
discordId: string,
|
||||
ticket: Omit<Prisma.TicketCreateInput, 'User'>,
|
||||
) {
|
||||
const user = await getUser(discordId)
|
||||
|
||||
if (user) {
|
||||
await db.ticket.create({
|
||||
data: {
|
||||
channelId: ticket.channelId,
|
||||
closed: ticket.closed,
|
||||
userId: user.discordId,
|
||||
},
|
||||
})
|
||||
return user
|
||||
}
|
||||
|
||||
return db.user.create({
|
||||
data: {
|
||||
discordId,
|
||||
tickets: {
|
||||
create: {
|
||||
channelId: ticket.channelId,
|
||||
closed: ticket.closed,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function createUserSilent(discordId: string) {
|
||||
return db.user.create({ data: { discordId } })
|
||||
}
|
||||
|
||||
export async function closeUserTickets(discordId: string) {
|
||||
return db.user.update({
|
||||
where: { discordId },
|
||||
data: {
|
||||
tickets: {
|
||||
updateMany: {
|
||||
where: {
|
||||
closed: false,
|
||||
},
|
||||
data: {
|
||||
closed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
@ -3,10 +3,9 @@ import {
|
||||
EmbedBuilder,
|
||||
channelMention,
|
||||
userMention,
|
||||
User,
|
||||
SnowflakeUtil,
|
||||
} from 'discord.js'
|
||||
import { db, DBTableEnum } from '../db'
|
||||
import { getSettings, getBanner, getRole } from '../db'
|
||||
import { logger } from '../lib'
|
||||
|
||||
let lastJoinedUser: {
|
||||
@ -27,29 +26,32 @@ export class GuildMemberAdd {
|
||||
)
|
||||
return
|
||||
|
||||
const portfolioChannelID = await db.get(DBTableEnum.PORTFOLIO_CHANNEL)
|
||||
const makeAnOrderChannelID = await db.get(
|
||||
DBTableEnum.MAKE_AN_ORDER_CHANNEL,
|
||||
)
|
||||
const welcomeChannelID = await db.get(DBTableEnum.WELCOME_CHANNEL)
|
||||
const imageURL = await db.get(DBTableEnum.BANNER_WELCOME)
|
||||
const settings = await getSettings()
|
||||
const banner = await getBanner()
|
||||
const roles = await getRole()
|
||||
|
||||
const roleID = await db.get(DBTableEnum.WELCOME_ROLE)
|
||||
if (!settings || !banner || !roles)
|
||||
return logger.error(
|
||||
'Missing settings',
|
||||
'Please set all required fields',
|
||||
)
|
||||
|
||||
let embed = new EmbedBuilder()
|
||||
.setTitle(
|
||||
`I'm glad to see you on my server. Let me give you a little tour.`,
|
||||
)
|
||||
.setDescription(
|
||||
`> 1. In the ${channelMention(portfolioChannelID)} - you can see my previous works.
|
||||
> 2. Here ${channelMention(makeAnOrderChannelID)} you might open a ticket.`,
|
||||
`> 1. In the ${channelMention(settings.portfolioChannelId)} - you can see my previous works.
|
||||
> 2. Here ${channelMention(settings.makeAnOrderChannelId)} you might open a ticket.`,
|
||||
)
|
||||
.setImage(imageURL)
|
||||
.setImage(banner.welcomeUrl)
|
||||
|
||||
const nonce = SnowflakeUtil.generate().toString()
|
||||
|
||||
const channel = await member.guild.channels.fetch(welcomeChannelID)
|
||||
const role = await member.guild.roles.fetch(roleID)
|
||||
const channel = await member.guild.channels.fetch(
|
||||
settings.welcomeChannelId,
|
||||
)
|
||||
const role = await member.guild.roles.fetch(roles.welcomeRoleId)
|
||||
|
||||
if (channel && channel.isTextBased() && role) {
|
||||
await channel
|
||||
|
Loading…
Reference in New Issue
Block a user