mirror of
https://github.com/Hessenuk/DiscordTickets.git
synced 2024-12-23 00:03:09 +02:00
start making things
This commit is contained in:
parent
cdb8fa04c4
commit
052c159157
18
README.md
18
README.md
@ -12,3 +12,21 @@ SUPER=
|
|||||||
https://www.prisma.io/docs/reference/database-reference/supported-databases
|
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"
|
"node": ">=18.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eartharoid/dbf": "^0.3.2",
|
"@eartharoid/dbf": "^0.3.3",
|
||||||
"@eartharoid/dtf": "^2.0.1",
|
"@eartharoid/dtf": "^2.0.1",
|
||||||
"@eartharoid/i18n": "^1.0.4",
|
"@eartharoid/i18n": "^1.0.4",
|
||||||
"@fastify/cookie": "^6.0.0",
|
"@fastify/cookie": "^6.0.0",
|
||||||
"@fastify/cors": "^8.0.0",
|
"@fastify/cors": "^8.1.0",
|
||||||
"@fastify/jwt": "^5.0.1",
|
"@fastify/jwt": "^5.0.1",
|
||||||
"@fastify/oauth2": "^5.0.0",
|
"@fastify/oauth2": "^5.1.0",
|
||||||
"@prisma/client": "^4.1.0",
|
"@prisma/client": "^4.1.1",
|
||||||
"cryptr": "^6.0.3",
|
"cryptr": "^6.0.3",
|
||||||
"discord.js": "^14.0.2",
|
"discord.js": "^14.1.2",
|
||||||
"dotenv": "^16.0.1",
|
"dotenv": "^16.0.1",
|
||||||
"fastify": "^4.2.1",
|
"fastify": "^4.3.0",
|
||||||
"figlet": "^1.5.2",
|
"figlet": "^1.5.2",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"keyv": "^4.3.3",
|
"keyv": "^4.3.3",
|
||||||
@ -55,14 +55,14 @@
|
|||||||
"node-dir": "^0.1.17",
|
"node-dir": "^0.1.17",
|
||||||
"node-emoji": "^1.11.0",
|
"node-emoji": "^1.11.0",
|
||||||
"object-diffy": "^1.0.4",
|
"object-diffy": "^1.0.4",
|
||||||
"prisma": "^4.1.0",
|
"prisma": "^4.1.1",
|
||||||
"semver": "^7.3.7",
|
"semver": "^7.3.7",
|
||||||
"terminal-link": "^2.1.1",
|
"terminal-link": "^2.1.1",
|
||||||
"yaml": "^1.10.2"
|
"yaml": "^1.10.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"all-contributors-cli": "^6.20.0",
|
"all-contributors-cli": "^6.20.0",
|
||||||
"eslint": "^8.20.0",
|
"eslint": "^8.21.0",
|
||||||
"eslint-plugin-unused-imports": "^2.0.0",
|
"eslint-plugin-unused-imports": "^2.0.0",
|
||||||
"nodemon": "^2.0.19"
|
"nodemon": "^2.0.19"
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ const { randomBytes } = require('crypto');
|
|||||||
const { short } = require('leeks.js');
|
const { short } = require('leeks.js');
|
||||||
|
|
||||||
console.log(short(
|
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') +
|
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.',
|
'&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 { FrameworkClient } = require('@eartharoid/dbf');
|
||||||
const { GatewayIntentBits } = require('discord.js');
|
const {
|
||||||
|
GatewayIntentBits, Partials,
|
||||||
|
} = require('discord.js');
|
||||||
const { PrismaClient } = require('@prisma/client');
|
const { PrismaClient } = require('@prisma/client');
|
||||||
const Keyv = require('keyv');
|
const Keyv = require('keyv');
|
||||||
const I18n = require('@eartharoid/i18n');
|
const I18n = require('@eartharoid/i18n');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { join } = require('path');
|
const { join } = require('path');
|
||||||
const YAML = require('yaml');
|
const YAML = require('yaml');
|
||||||
|
const TicketManager = require('./lib/tickets/manager');
|
||||||
const encryptionMiddleware = require('./lib/middleware/prisma-encryption');
|
const encryptionMiddleware = require('./lib/middleware/prisma-encryption');
|
||||||
const sqliteMiddleware = require('./lib/middleware/prisma-sqlite');
|
const sqliteMiddleware = require('./lib/middleware/prisma-sqlite');
|
||||||
|
|
||||||
@ -13,10 +16,19 @@ module.exports = class Client extends FrameworkClient {
|
|||||||
constructor(config, log) {
|
constructor(config, log) {
|
||||||
super({
|
super({
|
||||||
intents: [
|
intents: [
|
||||||
|
GatewayIntentBits.DirectMessages,
|
||||||
|
GatewayIntentBits.DirectMessageReactions,
|
||||||
|
GatewayIntentBits.DirectMessageTyping,
|
||||||
|
GatewayIntentBits.MessageContent,
|
||||||
GatewayIntentBits.Guilds,
|
GatewayIntentBits.Guilds,
|
||||||
GatewayIntentBits.GuildMembers,
|
GatewayIntentBits.GuildMembers,
|
||||||
GatewayIntentBits.GuildMessages,
|
GatewayIntentBits.GuildMessages,
|
||||||
],
|
],
|
||||||
|
partials: [
|
||||||
|
Partials.Message,
|
||||||
|
Partials.Channel,
|
||||||
|
Partials.Reaction,
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const locales = {};
|
const locales = {};
|
||||||
@ -30,6 +42,8 @@ module.exports = class Client extends FrameworkClient {
|
|||||||
|
|
||||||
/** @type {I18n} */
|
/** @type {I18n} */
|
||||||
this.i18n = new I18n('en-GB', locales);
|
this.i18n = new I18n('en-GB', locales);
|
||||||
|
/** @type {TicketManager} */
|
||||||
|
this.tickets = new TicketManager(this);
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.log = log;
|
this.log = log;
|
||||||
this.supers = (process.env.SUPER ?? '').split(',');
|
this.supers = (process.env.SUPER ?? '').split(',');
|
||||||
|
@ -2,6 +2,9 @@ test: |
|
|||||||
line 1
|
line 1
|
||||||
line 2
|
line 2
|
||||||
buttons:
|
buttons:
|
||||||
|
confirm_open:
|
||||||
|
emoji: ✅
|
||||||
|
text: Create ticket
|
||||||
create:
|
create:
|
||||||
emoji: 🎫
|
emoji: 🎫
|
||||||
text: Create a ticket
|
text: Create a ticket
|
||||||
@ -120,6 +123,9 @@ commands:
|
|||||||
user:
|
user:
|
||||||
create:
|
create:
|
||||||
name: Create a ticket for user
|
name: Create a ticket for user
|
||||||
|
dm:
|
||||||
|
confirm_open:
|
||||||
|
title: 'Do you want to open a ticket with the following topic?'
|
||||||
log:
|
log:
|
||||||
admin:
|
admin:
|
||||||
changes: Changes
|
changes: Changes
|
||||||
@ -145,5 +151,15 @@ log:
|
|||||||
update: updated
|
update: updated
|
||||||
tickets:
|
tickets:
|
||||||
menus:
|
menus:
|
||||||
create:
|
category:
|
||||||
placeholder: Select a ticket 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,8 +84,7 @@ module.exports.post = fastify => ({
|
|||||||
.setLabel(getMessage('buttons.create.text'))
|
.setLabel(getMessage('buttons.create.text'))
|
||||||
.setEmoji(getMessage('buttons.create.emoji')),
|
.setEmoji(getMessage('buttons.create.emoji')),
|
||||||
);
|
);
|
||||||
} else {
|
} else if (data.type === 'BUTTON') {
|
||||||
if (data.type === 'BUTTON') {
|
|
||||||
components.push(
|
components.push(
|
||||||
...categories.map(category =>
|
...categories.map(category =>
|
||||||
new ButtonBuilder()
|
new ButtonBuilder()
|
||||||
@ -101,8 +100,8 @@ module.exports.post = fastify => ({
|
|||||||
} else {
|
} else {
|
||||||
components.push(
|
components.push(
|
||||||
new SelectMenuBuilder()
|
new SelectMenuBuilder()
|
||||||
.setCustomId('create')
|
.setCustomId(JSON.stringify({ action: 'create' }))
|
||||||
.setPlaceholder(getMessage('menus.create.placeholder'))
|
.setPlaceholder(getMessage('menus.category.placeholder'))
|
||||||
.setOptions(
|
.setOptions(
|
||||||
categories.map(category =>
|
categories.map(category =>
|
||||||
new SelectMenuOptionBuilder()
|
new SelectMenuOptionBuilder()
|
||||||
@ -113,7 +112,7 @@ module.exports.post = fastify => ({
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await channel.send({
|
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 ##
|
## Documentation: https://discordtickets.app ##
|
||||||
## Support: https://go.eartharoid.me/discord ##
|
## Support: https://lnk.earth/discord ##
|
||||||
#####################################################
|
#####################################################
|
||||||
|
|
||||||
logs:
|
logs:
|
||||||
|
Loading…
Reference in New Issue
Block a user