mirror of
https://github.com/Hessenuk/DiscordTickets.git
synced 2024-11-05 04:13:08 +02:00
start making things
This commit is contained in:
parent
cdb8fa04c4
commit
052c159157
20
README.md
20
README.md
@ -11,4 +11,22 @@ SUPER=
|
||||
|
||||
https://www.prisma.io/docs/reference/database-reference/supported-databases
|
||||
|
||||
![](https://static.eartharoid.me/k/22/08/02185801.png) - for user/create, slash/force-close and slash/move
|
||||
![](https://static.eartharoid.me/k/22/08/02185801.png) - for user/create, slash/force-close and slash/move
|
||||
|
||||
menu question max length cannot be higher than question options
|
||||
|
||||
- TODO: post stats
|
||||
- TODO: settings bundle download
|
||||
- TODO: update notifications
|
||||
- TODO: check inline to-dos
|
||||
|
||||
|
||||
creation requires an interaction:
|
||||
- /new -> category? -> topic or questions -> create
|
||||
- user:create(self) -> category? -> topic or questions -> create
|
||||
- user:create(staff) -> category? -> DM (channel fallback) button -> topic or questions -> create
|
||||
- message:create(self) -> category? -> topic or questions -> create
|
||||
- message:create(staff) -> category? -> DM (channel fallback) button -> topic or questions -> create
|
||||
- DM -> guild? -> category? -> topic or questions -> create
|
||||
- panel(interaction) -> topic or questions -> create
|
||||
- panel(message) -> DM (channel fallback) button -> topic or questions -> create
|
16
package.json
16
package.json
@ -34,18 +34,18 @@
|
||||
"node": ">=18.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@eartharoid/dbf": "^0.3.2",
|
||||
"@eartharoid/dbf": "^0.3.3",
|
||||
"@eartharoid/dtf": "^2.0.1",
|
||||
"@eartharoid/i18n": "^1.0.4",
|
||||
"@fastify/cookie": "^6.0.0",
|
||||
"@fastify/cors": "^8.0.0",
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@fastify/jwt": "^5.0.1",
|
||||
"@fastify/oauth2": "^5.0.0",
|
||||
"@prisma/client": "^4.1.0",
|
||||
"@fastify/oauth2": "^5.1.0",
|
||||
"@prisma/client": "^4.1.1",
|
||||
"cryptr": "^6.0.3",
|
||||
"discord.js": "^14.0.2",
|
||||
"discord.js": "^14.1.2",
|
||||
"dotenv": "^16.0.1",
|
||||
"fastify": "^4.2.1",
|
||||
"fastify": "^4.3.0",
|
||||
"figlet": "^1.5.2",
|
||||
"fs-extra": "^10.1.0",
|
||||
"keyv": "^4.3.3",
|
||||
@ -55,14 +55,14 @@
|
||||
"node-dir": "^0.1.17",
|
||||
"node-emoji": "^1.11.0",
|
||||
"object-diffy": "^1.0.4",
|
||||
"prisma": "^4.1.0",
|
||||
"prisma": "^4.1.1",
|
||||
"semver": "^7.3.7",
|
||||
"terminal-link": "^2.1.1",
|
||||
"yaml": "^1.10.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"all-contributors-cli": "^6.20.0",
|
||||
"eslint": "^8.20.0",
|
||||
"eslint": "^8.21.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"nodemon": "^2.0.19"
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ const { randomBytes } = require('crypto');
|
||||
const { short } = require('leeks.js');
|
||||
|
||||
console.log(short(
|
||||
'Set the "ENCRYPTION_KEY" environment variable to: \n&1&!f' +
|
||||
'Set the "ENCRYPTION_KEY" environment variable to: \n&!b' +
|
||||
randomBytes(24).toString('hex') +
|
||||
'&r\n\n&0&!e WARNING &r &e&lIf you lose the encryption key, most of the data in the database will become unreadable, requiring a new key and a full reset.',
|
||||
));
|
@ -8,5 +8,15 @@ module.exports = class CreateButton extends Button {
|
||||
});
|
||||
}
|
||||
|
||||
async run(id, interaction) { }
|
||||
/**
|
||||
* @param {*} id
|
||||
* @param {import("discord.js").ButtonInteraction} interaction
|
||||
*/
|
||||
async run(id, interaction) {
|
||||
await this.client.tickets.create({
|
||||
categoryId: id.target,
|
||||
interaction,
|
||||
topic: id.topic,
|
||||
});
|
||||
}
|
||||
};
|
@ -1,11 +1,14 @@
|
||||
const { FrameworkClient } = require('@eartharoid/dbf');
|
||||
const { GatewayIntentBits } = require('discord.js');
|
||||
const {
|
||||
GatewayIntentBits, Partials,
|
||||
} = require('discord.js');
|
||||
const { PrismaClient } = require('@prisma/client');
|
||||
const Keyv = require('keyv');
|
||||
const I18n = require('@eartharoid/i18n');
|
||||
const fs = require('fs');
|
||||
const { join } = require('path');
|
||||
const YAML = require('yaml');
|
||||
const TicketManager = require('./lib/tickets/manager');
|
||||
const encryptionMiddleware = require('./lib/middleware/prisma-encryption');
|
||||
const sqliteMiddleware = require('./lib/middleware/prisma-sqlite');
|
||||
|
||||
@ -13,10 +16,19 @@ module.exports = class Client extends FrameworkClient {
|
||||
constructor(config, log) {
|
||||
super({
|
||||
intents: [
|
||||
GatewayIntentBits.DirectMessages,
|
||||
GatewayIntentBits.DirectMessageReactions,
|
||||
GatewayIntentBits.DirectMessageTyping,
|
||||
GatewayIntentBits.MessageContent,
|
||||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildMembers,
|
||||
GatewayIntentBits.GuildMessages,
|
||||
],
|
||||
partials: [
|
||||
Partials.Message,
|
||||
Partials.Channel,
|
||||
Partials.Reaction,
|
||||
],
|
||||
});
|
||||
|
||||
const locales = {};
|
||||
@ -30,6 +42,8 @@ module.exports = class Client extends FrameworkClient {
|
||||
|
||||
/** @type {I18n} */
|
||||
this.i18n = new I18n('en-GB', locales);
|
||||
/** @type {TicketManager} */
|
||||
this.tickets = new TicketManager(this);
|
||||
this.config = config;
|
||||
this.log = log;
|
||||
this.supers = (process.env.SUPER ?? '').split(',');
|
||||
|
@ -2,6 +2,9 @@ test: |
|
||||
line 1
|
||||
line 2
|
||||
buttons:
|
||||
confirm_open:
|
||||
emoji: ✅
|
||||
text: Create ticket
|
||||
create:
|
||||
emoji: 🎫
|
||||
text: Create a ticket
|
||||
@ -120,6 +123,9 @@ commands:
|
||||
user:
|
||||
create:
|
||||
name: Create a ticket for user
|
||||
dm:
|
||||
confirm_open:
|
||||
title: 'Do you want to open a ticket with the following topic?'
|
||||
log:
|
||||
admin:
|
||||
changes: Changes
|
||||
@ -145,5 +151,15 @@ log:
|
||||
update: updated
|
||||
tickets:
|
||||
menus:
|
||||
create:
|
||||
placeholder: Select a ticket category
|
||||
category:
|
||||
placeholder: Select a ticket category
|
||||
guild:
|
||||
placeholder: Select a server
|
||||
modals:
|
||||
feedback:
|
||||
title: 'Feedback'
|
||||
topic: 'Topic'
|
||||
misc:
|
||||
no_categories:
|
||||
description: No ticket categories have been configured.
|
||||
title: ❌ There are no ticket categories
|
@ -1 +0,0 @@
|
||||
module.exports.capitalise = string => string.charAt(0).toUpperCase() + string.slice(1);
|
146
src/lib/tickets/manager.js
Normal file
146
src/lib/tickets/manager.js
Normal file
@ -0,0 +1,146 @@
|
||||
const {
|
||||
ActionRowBuilder,
|
||||
ModalBuilder,
|
||||
SelectMenuBuilder,
|
||||
SelectMenuOptionBuilder,
|
||||
TextInputBuilder,
|
||||
TextInputStyle,
|
||||
} = require('discord.js');
|
||||
const emoji = require('node-emoji');
|
||||
|
||||
module.exports = class TicketManager {
|
||||
constructor(client) {
|
||||
/** @type {import("client")} */
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} data
|
||||
* @param {string} data.category
|
||||
* @param {import("discord.js").ButtonInteraction|import("discord.js").SelectMenuInteraction} data.interaction
|
||||
* @param {string?} [data.topic]
|
||||
*/
|
||||
async create({
|
||||
categoryId, interaction, topic, reference,
|
||||
}) {
|
||||
const category = await this.client.prisma.category.findUnique({
|
||||
include: {
|
||||
guild: true,
|
||||
questions: true,
|
||||
},
|
||||
where: { id: Number(categoryId) },
|
||||
});
|
||||
|
||||
// TODO: if member !required roles -> stop
|
||||
|
||||
// TODO: if discordCategory has 50 channels -> stop
|
||||
|
||||
// TODO: if category has max channels -> stop
|
||||
|
||||
// TODO: if member has max -> stop
|
||||
|
||||
// TODO: if cooldown -> stop
|
||||
|
||||
const getMessage = this.client.i18n.getLocale(category.guild.locale);
|
||||
|
||||
if (category.questions.length >= 1) {
|
||||
await interaction.showModal(
|
||||
new ModalBuilder()
|
||||
.setCustomId(JSON.stringify({
|
||||
action: 'questions',
|
||||
categoryId,
|
||||
reference,
|
||||
}))
|
||||
.setTitle(category.name)
|
||||
.setComponents(
|
||||
category.questions
|
||||
.sort((a, b) => a.order - b.order)
|
||||
.map(q => {
|
||||
if (q.type === 'TEXT') {
|
||||
return new ActionRowBuilder()
|
||||
.setComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId(q.id)
|
||||
.setLabel(q.label)
|
||||
.setStyle(q.style)
|
||||
.setMaxLength(q.maxLength)
|
||||
.setMinLength(q.minLength)
|
||||
.setPlaceholder(q.placeholder)
|
||||
.setRequired(q.required)
|
||||
.setValue(q.value),
|
||||
);
|
||||
} else if (q.type === 'MENU') {
|
||||
return new ActionRowBuilder()
|
||||
.setComponents(
|
||||
new SelectMenuBuilder()
|
||||
.setCustomId(q.id)
|
||||
.setPlaceholder(q.placeholder || q.label)
|
||||
.setMaxValues(q.maxLength)
|
||||
.setMinValues(q.minLength)
|
||||
.setOptions(
|
||||
q.options.map((o, i) => {
|
||||
const builder = new SelectMenuOptionBuilder()
|
||||
.setValue(String(i))
|
||||
.setLabel(o.label);
|
||||
if (o.description) builder.setDescription(o.description);
|
||||
if (o.emoji) builder.setEmoji(emoji.hasEmoji(o.emoji) ? emoji.get(o.emoji) : { id: o.emoji });
|
||||
return builder;
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}),
|
||||
),
|
||||
);
|
||||
} else if (category.requireTopic && !topic) {
|
||||
await interaction.showModal(
|
||||
new ModalBuilder()
|
||||
.setCustomId(JSON.stringify({
|
||||
action: 'topic',
|
||||
categoryId,
|
||||
reference,
|
||||
}))
|
||||
.setTitle(category.name)
|
||||
.setComponents(
|
||||
new ActionRowBuilder()
|
||||
.setComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId('topic')
|
||||
.setLabel(getMessage('modals.topic'))
|
||||
.setStyle(TextInputStyle.Long),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
await this.postQuestions({
|
||||
categoryId,
|
||||
interaction,
|
||||
topic,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} data
|
||||
* @param {string} data.category
|
||||
* @param {import("discord.js").ButtonInteraction|import("discord.js").SelectMenuInteraction|import("discord.js").ModalSubmitInteraction} data.interaction
|
||||
* @param {string?} [data.topic]
|
||||
*/
|
||||
async postQuestions({
|
||||
categoryId, interaction, topic, reference,
|
||||
}) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
console.log(require('util').inspect(interaction, {
|
||||
colors: true,
|
||||
depth: 10,
|
||||
}));
|
||||
if (interaction.isModalSubmit()) {
|
||||
|
||||
}
|
||||
|
||||
interaction.editReply({
|
||||
components: [],
|
||||
embeds: [],
|
||||
});
|
||||
}
|
||||
};
|
29
src/lib/users.js
Normal file
29
src/lib/users.js
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
*
|
||||
* @param {import("client")} client
|
||||
* @param {string} userId
|
||||
* @returns {Promise<Collection<import("discord.js").Guild>}
|
||||
*/
|
||||
module.exports.getCommonGuilds = async (client, userId) => await client.guilds.cache.filter(async guild => {
|
||||
const member = await guild.members.fetch(userId);
|
||||
return !!member;
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import("discord.js").Guild} guild
|
||||
* @param {string} userId
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
module.exports.isStaff = async (guild, userId) => {
|
||||
/** @type {import("client")} */
|
||||
const client = guild.client;
|
||||
if (guild.client.supers.includes(userId)) return true;
|
||||
const guildMember = await guild.members.fetch(userId);
|
||||
if (guildMember?.permissions.has('MANAGE_GUILD')) return true;
|
||||
const { categories } = await client.prisma.guild.findUnique({
|
||||
select: { categories: true },
|
||||
where: { id: guild.id },
|
||||
});
|
||||
return categories.some(cat => cat.roles.some(r => guildMember.roles.cache.has(r)));
|
||||
};
|
15
src/listeners/client/error.js
Normal file
15
src/listeners/client/error.js
Normal file
@ -0,0 +1,15 @@
|
||||
const { Listener } = require('@eartharoid/dbf');
|
||||
|
||||
module.exports = class extends Listener {
|
||||
constructor(client, options) {
|
||||
super(client, {
|
||||
...options,
|
||||
emitter: client,
|
||||
event: 'error',
|
||||
});
|
||||
}
|
||||
|
||||
run(error) {
|
||||
this.client.log.error(error);
|
||||
}
|
||||
};
|
15
src/listeners/client/guildCreate.js
Normal file
15
src/listeners/client/guildCreate.js
Normal file
@ -0,0 +1,15 @@
|
||||
const { Listener } = require('@eartharoid/dbf');
|
||||
|
||||
module.exports = class extends Listener {
|
||||
constructor(client, options) {
|
||||
super(client, {
|
||||
...options,
|
||||
emitter: client,
|
||||
event: 'guildCreate',
|
||||
});
|
||||
}
|
||||
|
||||
run(guild) {
|
||||
this.client.log.success(`Added to guild "${guild.name}"`);
|
||||
}
|
||||
};
|
15
src/listeners/client/guildDelete.js
Normal file
15
src/listeners/client/guildDelete.js
Normal file
@ -0,0 +1,15 @@
|
||||
const { Listener } = require('@eartharoid/dbf');
|
||||
|
||||
module.exports = class extends Listener {
|
||||
constructor(client, options) {
|
||||
super(client, {
|
||||
...options,
|
||||
emitter: client,
|
||||
event: 'guildDelete',
|
||||
});
|
||||
}
|
||||
|
||||
run(guild) {
|
||||
this.client.log.info(`Removed from guild "${guild.name}"`);
|
||||
}
|
||||
};
|
15
src/listeners/client/guildMemberRemove.js
Normal file
15
src/listeners/client/guildMemberRemove.js
Normal file
@ -0,0 +1,15 @@
|
||||
const { Listener } = require('@eartharoid/dbf');
|
||||
|
||||
module.exports = class extends Listener {
|
||||
constructor(client, options) {
|
||||
super(client, {
|
||||
...options,
|
||||
emitter: client,
|
||||
event: 'guildMemberRemove',
|
||||
});
|
||||
}
|
||||
|
||||
run(member) {
|
||||
// TODO: close tickets
|
||||
}
|
||||
};
|
184
src/listeners/client/messageCreate.js
Normal file
184
src/listeners/client/messageCreate.js
Normal file
@ -0,0 +1,184 @@
|
||||
const { Listener } = require('@eartharoid/dbf');
|
||||
const {
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle: { Success },
|
||||
ChannelType,
|
||||
ComponentType,
|
||||
EmbedBuilder,
|
||||
SelectMenuBuilder,
|
||||
SelectMenuOptionBuilder,
|
||||
} = require('discord.js');
|
||||
const { getCommonGuilds } = require('../../lib/users');
|
||||
const ms = require('ms');
|
||||
const emoji = require('node-emoji');
|
||||
|
||||
module.exports = class extends Listener {
|
||||
constructor(client, options) {
|
||||
super(client, {
|
||||
...options,
|
||||
emitter: client,
|
||||
event: 'messageCreate',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} guildId
|
||||
* @param {import("discord.js").ButtonInteraction|import("discord.js").SelectMenuInteraction} interaction
|
||||
*/
|
||||
async useGuild(settings, interaction, topic) {
|
||||
const getMessage = this.client.i18n.getLocale(settings.locale);
|
||||
if (settings.categories.length === 0) {
|
||||
interaction.editReply({
|
||||
components: [],
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setColor(settings.errorColour)
|
||||
.setTitle(getMessage('misc.no_categories.title'))
|
||||
.setDescription(getMessage('misc.no_categories.description')),
|
||||
],
|
||||
});
|
||||
} else if (settings.categories.length === 1) {
|
||||
await this.client.tickets.create({
|
||||
categoryId: settings.categories[0].id,
|
||||
interaction,
|
||||
topic,
|
||||
});
|
||||
} else {
|
||||
const sent = await interaction.editReply({
|
||||
components: [
|
||||
new ActionRowBuilder()
|
||||
.setComponents(
|
||||
new SelectMenuBuilder()
|
||||
.setCustomId(JSON.stringify({
|
||||
action: 'create',
|
||||
topic,
|
||||
}))
|
||||
.setPlaceholder(getMessage('menus.category.placeholder'))
|
||||
.setOptions(
|
||||
settings.categories.map(category =>
|
||||
new SelectMenuOptionBuilder()
|
||||
.setValue(String(category.id))
|
||||
.setLabel(category.name)
|
||||
.setDescription(category.description)
|
||||
.setEmoji(emoji.hasEmoji(category.emoji) ? emoji.get(category.emoji) : { id: category.emoji }),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
});
|
||||
sent.awaitMessageComponent({
|
||||
componentType: ComponentType.SelectMenu,
|
||||
filter: () => true,
|
||||
time: ms('30s'),
|
||||
})
|
||||
.then(async () => {
|
||||
await sent.delete();
|
||||
})
|
||||
.catch(error => {
|
||||
if (error) this.client.log.error(error);
|
||||
sent.delete();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("discord.js").Message} message
|
||||
*/
|
||||
async run(message) {
|
||||
/** @type {import("client")} */
|
||||
const client = this.client;
|
||||
|
||||
if (message.channel.type === ChannelType.DM) {
|
||||
if (message.author.bot) return false;
|
||||
const commonGuilds = await getCommonGuilds(this.client, message.author.id);
|
||||
if (commonGuilds.size === 0) {
|
||||
return false;
|
||||
} else if (commonGuilds.size === 1) {
|
||||
const settings = await client.prisma.guild.findUnique({
|
||||
select: {
|
||||
categories: true,
|
||||
errorColour: true,
|
||||
locale: true,
|
||||
primaryColour: true,
|
||||
},
|
||||
where: { id: commonGuilds.at(0).id },
|
||||
});
|
||||
const getMessage = this.client.i18n.getLocale(settings.locale);
|
||||
const sent = await message.reply({
|
||||
components: [
|
||||
new ActionRowBuilder()
|
||||
.setComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId(message.id)
|
||||
.setStyle(Success)
|
||||
.setLabel(getMessage('buttons.confirm_open.text'))
|
||||
.setEmoji(getMessage('buttons.confirm_open.emoji')),
|
||||
),
|
||||
],
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setColor(settings.primaryColour)
|
||||
.setTitle(getMessage('dm.confirm_open.title'))
|
||||
.setDescription(message.content),
|
||||
],
|
||||
});
|
||||
sent.awaitMessageComponent({
|
||||
componentType: ComponentType.Button,
|
||||
filter: interaction => interaction.deferUpdate(),
|
||||
time: ms('30s'),
|
||||
})
|
||||
.then(async interaction => await this.useGuild(settings, interaction, message.content))
|
||||
.catch(error => {
|
||||
if (error) this.client.log.error(error);
|
||||
sent.delete();
|
||||
});
|
||||
} else {
|
||||
const getMessage = this.client.i18n.getLocale();
|
||||
const sent = await message.reply({
|
||||
components: [
|
||||
new ActionRowBuilder()
|
||||
.setComponents(
|
||||
new SelectMenuBuilder()
|
||||
.setCustomId(message.id)
|
||||
.setPlaceholder(getMessage('menus.guild.placeholder'))
|
||||
.setOptions(
|
||||
commonGuilds.map(g =>
|
||||
new SelectMenuOptionBuilder()
|
||||
.setValue(String(g.id))
|
||||
.setLabel(g.name),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
});
|
||||
sent.awaitMessageComponent({
|
||||
componentType: ComponentType.SelectMenu,
|
||||
filter: interaction => interaction.deferUpdate(),
|
||||
time: ms('30s'),
|
||||
})
|
||||
.then(async interaction => {
|
||||
const settings = await client.prisma.guild.findUnique({
|
||||
select: {
|
||||
categories: true,
|
||||
errorColour: true,
|
||||
locale: true,
|
||||
primaryColour: true,
|
||||
},
|
||||
where: { id: interaction.values[0] },
|
||||
});
|
||||
await this.useGuild(settings, interaction, message.content);
|
||||
})
|
||||
.catch(error => {
|
||||
if (error) this.client.log.error(error);
|
||||
sent.delete();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// TODO: archive messages in tickets
|
||||
// TODO: auto tag
|
||||
}
|
||||
}
|
||||
};
|
15
src/listeners/client/messageDelete.js
Normal file
15
src/listeners/client/messageDelete.js
Normal file
@ -0,0 +1,15 @@
|
||||
const { Listener } = require('@eartharoid/dbf');
|
||||
|
||||
module.exports = class extends Listener {
|
||||
constructor(client, options) {
|
||||
super(client, {
|
||||
...options,
|
||||
emitter: client,
|
||||
event: 'messageDelete',
|
||||
});
|
||||
}
|
||||
|
||||
run(message) {
|
||||
// TODO: archive messages in tickets
|
||||
}
|
||||
};
|
15
src/listeners/client/messageUpdate.js
Normal file
15
src/listeners/client/messageUpdate.js
Normal file
@ -0,0 +1,15 @@
|
||||
const { Listener } = require('@eartharoid/dbf');
|
||||
|
||||
module.exports = class extends Listener {
|
||||
constructor(client, options) {
|
||||
super(client, {
|
||||
...options,
|
||||
emitter: client,
|
||||
event: 'messageUpdate',
|
||||
});
|
||||
}
|
||||
|
||||
run(oldMessage, newMessage) {
|
||||
// TODO: archive messages in tickets
|
||||
}
|
||||
};
|
15
src/listeners/client/warn.js
Normal file
15
src/listeners/client/warn.js
Normal file
@ -0,0 +1,15 @@
|
||||
const { Listener } = require('@eartharoid/dbf');
|
||||
|
||||
module.exports = class extends Listener {
|
||||
constructor(client, options) {
|
||||
super(client, {
|
||||
...options,
|
||||
emitter: client,
|
||||
event: 'warn',
|
||||
});
|
||||
}
|
||||
|
||||
run(warn) {
|
||||
this.client.log.warn(warn);
|
||||
}
|
||||
};
|
19
src/listeners/commands/error.js
Normal file
19
src/listeners/commands/error.js
Normal file
@ -0,0 +1,19 @@
|
||||
const { Listener } = require('@eartharoid/dbf');
|
||||
|
||||
module.exports = class extends Listener {
|
||||
constructor(client, options) {
|
||||
super(client, {
|
||||
...options,
|
||||
emitter: client.commands,
|
||||
event: 'error',
|
||||
});
|
||||
}
|
||||
|
||||
run({
|
||||
command,
|
||||
error,
|
||||
}) {
|
||||
this.client.log.error.commands(`"${command.name}" command execution error:`, error);
|
||||
return true;
|
||||
}
|
||||
};
|
24
src/listeners/commands/run.js
Normal file
24
src/listeners/commands/run.js
Normal file
@ -0,0 +1,24 @@
|
||||
const { Listener } = require('@eartharoid/dbf');
|
||||
|
||||
module.exports = class extends Listener {
|
||||
constructor(client, options) {
|
||||
super(client, {
|
||||
...options,
|
||||
emitter: client.commands,
|
||||
event: 'run',
|
||||
});
|
||||
}
|
||||
|
||||
run({
|
||||
command,
|
||||
interaction,
|
||||
}) {
|
||||
const types = {
|
||||
1: 'slash',
|
||||
2: 'user',
|
||||
3: 'message',
|
||||
};
|
||||
this.client.log.info.commands(`${interaction.user.tag} used the "${command.name}" ${types[command.type]} command`);
|
||||
return true;
|
||||
}
|
||||
};
|
@ -8,5 +8,15 @@ module.exports = class CreateMenu extends Menu {
|
||||
});
|
||||
}
|
||||
|
||||
async run(id, interaction) { }
|
||||
/**
|
||||
* @param {*} id
|
||||
* @param {import("discord.js").SelectMenuInteraction} interaction
|
||||
*/
|
||||
async run(id, interaction) {
|
||||
await this.client.tickets.create({
|
||||
categoryId: interaction.values[0],
|
||||
interaction,
|
||||
topic: id.topic,
|
||||
});
|
||||
}
|
||||
};
|
@ -8,5 +8,13 @@ module.exports = class QuestionsModal extends Modal {
|
||||
});
|
||||
}
|
||||
|
||||
async run(id, interaction) { }
|
||||
async run(id, interaction) {
|
||||
console.log(id);
|
||||
console.log(require('util').inspect(interaction, {
|
||||
colors: true,
|
||||
depth: 10,
|
||||
}));
|
||||
|
||||
// TODO: custom topic
|
||||
}
|
||||
};
|
18
src/modals/topic.js
Normal file
18
src/modals/topic.js
Normal file
@ -0,0 +1,18 @@
|
||||
const { Modal } = require('@eartharoid/dbf');
|
||||
|
||||
module.exports = class TopicModal extends Modal {
|
||||
constructor(client, options) {
|
||||
super(client, {
|
||||
...options,
|
||||
id: 'topic',
|
||||
});
|
||||
}
|
||||
|
||||
async run(id, interaction) {
|
||||
console.log(id);
|
||||
console.log(require('util').inspect(interaction, {
|
||||
colors: true,
|
||||
depth: 10,
|
||||
}));
|
||||
}
|
||||
};
|
@ -84,36 +84,35 @@ module.exports.post = fastify => ({
|
||||
.setLabel(getMessage('buttons.create.text'))
|
||||
.setEmoji(getMessage('buttons.create.emoji')),
|
||||
);
|
||||
} else if (data.type === 'BUTTON') {
|
||||
components.push(
|
||||
...categories.map(category =>
|
||||
new ButtonBuilder()
|
||||
.setCustomId(JSON.stringify({
|
||||
action: 'create',
|
||||
target: category.id,
|
||||
}))
|
||||
.setStyle(Secondary)
|
||||
.setLabel(category.name)
|
||||
.setEmoji(emoji.hasEmoji(category.emoji) ? emoji.get(category.emoji) : { id: category.emoji }),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
if (data.type === 'BUTTON') {
|
||||
components.push(
|
||||
...categories.map(category =>
|
||||
new ButtonBuilder()
|
||||
.setCustomId(JSON.stringify({
|
||||
action: 'create',
|
||||
target: category.id,
|
||||
}))
|
||||
.setStyle(Secondary)
|
||||
.setLabel(category.name)
|
||||
.setEmoji(emoji.hasEmoji(category.emoji) ? emoji.get(category.emoji) : { id: category.emoji }),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
components.push(
|
||||
new SelectMenuBuilder()
|
||||
.setCustomId('create')
|
||||
.setPlaceholder(getMessage('menus.create.placeholder'))
|
||||
.setOptions(
|
||||
categories.map(category =>
|
||||
new SelectMenuOptionBuilder()
|
||||
.setValue(String(category.id))
|
||||
.setLabel(category.name)
|
||||
.setDescription(category.description)
|
||||
.setEmoji(emoji.hasEmoji(category.emoji) ? emoji.get(category.emoji) : { id: category.emoji }),
|
||||
),
|
||||
components.push(
|
||||
new SelectMenuBuilder()
|
||||
.setCustomId(JSON.stringify({ action: 'create' }))
|
||||
.setPlaceholder(getMessage('menus.category.placeholder'))
|
||||
.setOptions(
|
||||
categories.map(category =>
|
||||
new SelectMenuOptionBuilder()
|
||||
.setValue(String(category.id))
|
||||
.setLabel(category.name)
|
||||
.setDescription(category.description)
|
||||
.setEmoji(emoji.hasEmoji(category.emoji) ? emoji.get(category.emoji) : { id: category.emoji }),
|
||||
),
|
||||
);
|
||||
}
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
await channel.send({
|
||||
|
24
src/schemas/settings.js
Normal file
24
src/schemas/settings.js
Normal file
@ -0,0 +1,24 @@
|
||||
module.exports = joi.object({
|
||||
archive: joi.boolean().optional(),
|
||||
autoClose: joi.number().min(3600000).optional(),
|
||||
autoTag: [joi.array(), joi.string().valid('ticket', '!ticket', 'all')].optional(),
|
||||
blocklist: joi.array().optional(),
|
||||
createdAt: joi.string().optional(),
|
||||
errorColour: joi.string().optional(),
|
||||
footer: joi.string().optional(),
|
||||
id: joi.string().optional(),
|
||||
logChannel: joi.string().optional(),
|
||||
primaryColour: joi.string().optional(),
|
||||
staleAfter: joi.number().min(60000).optional(),
|
||||
successColour: joi.string().optional(),
|
||||
workingHours: joi.array().length(8).items(
|
||||
joi.string(),
|
||||
joi.array().items(joi.string().required(), joi.string().required()),
|
||||
joi.array().items(joi.string().required(), joi.string().required()),
|
||||
joi.array().items(joi.string().required(), joi.string().required()),
|
||||
joi.array().items(joi.string().required(), joi.string().required()),
|
||||
joi.array().items(joi.string().required(), joi.string().required()),
|
||||
joi.array().items(joi.string().required(), joi.string().required()),
|
||||
joi.array().items(joi.string().required(), joi.string().required()),
|
||||
).optional(),
|
||||
});
|
@ -11,7 +11,7 @@
|
||||
## |_| |_| \___| |_|\_\ \___| \__| |___/ ##
|
||||
## ##
|
||||
## Documentation: https://discordtickets.app ##
|
||||
## Support: https://go.eartharoid.me/discord ##
|
||||
## Support: https://lnk.earth/discord ##
|
||||
#####################################################
|
||||
|
||||
logs:
|
||||
|
Loading…
Reference in New Issue
Block a user