This commit is contained in:
Isaac 2022-07-21 01:25:37 +01:00
parent 98b35e3a81
commit f93d058f55
No known key found for this signature in database
GPG Key ID: F4EAABEB0FFCC06A
5 changed files with 166 additions and 8 deletions

View File

@ -9,6 +9,8 @@
"contributors:generate": "all-contributors generate", "contributors:generate": "all-contributors generate",
"keygen": "node scripts/keygen", "keygen": "node scripts/keygen",
"lint": "eslint src scripts --ext mjs --fix", "lint": "eslint src scripts --ext mjs --fix",
"start": "node .",
"studio": "npx prisma studio",
"test": "echo \"There's nothing to test\" && exit 1" "test": "echo \"There's nothing to test\" && exit 1"
}, },
"repository": { "repository": {
@ -37,7 +39,7 @@
"@fastify/cors": "^8.0.0", "@fastify/cors": "^8.0.0",
"@fastify/jwt": "^5.0.1", "@fastify/jwt": "^5.0.1",
"@fastify/oauth2": "^5.0.0", "@fastify/oauth2": "^5.0.0",
"@prisma/client": "^4.0.0", "@prisma/client": "^4.1.0",
"cryptr": "^6.0.3", "cryptr": "^6.0.3",
"discord.js": "^14.0.2", "discord.js": "^14.0.2",
"dotenv": "^16.0.1", "dotenv": "^16.0.1",
@ -50,7 +52,7 @@
"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.0.0", "prisma": "^4.1.0",
"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"

View File

@ -114,6 +114,8 @@ model Guild {
archive Boolean @default(true) archive Boolean @default(true)
blocklist Json @default("[]") blocklist Json @default("[]")
categories Category[] categories Category[]
claimButton Boolean @default(false)
closeButton Boolean @default(false)
createdAt DateTime @default(now()) createdAt DateTime @default(now())
errorColour String @default("Red") errorColour String @default("Red")
feedback Feedback[] feedback Feedback[]

View File

@ -1,4 +1,8 @@
command: buttons:
create:
emoji: '🎫'
text: 'Create a ticket'
commands:
log: log:
admin: admin:
changes: 'Changes' changes: 'Changes'
@ -6,16 +10,21 @@ log:
joined: '{user} {verb} {targetType}' joined: '{user} {verb} {targetType}'
target: target:
category: 'a category' category: 'a category'
panel: 'a panel'
question: 'a question' question: 'a question'
settings: 'the settings' settings: 'the settings'
title: title:
joined: '{targetType} {verb}' joined: '{targetType} {verb}'
target: target:
category: 'Category' category: 'Category'
panel: 'Panel'
question: 'Question' question: 'Question'
settings: 'Settings' settings: 'Settings'
verb: verb:
create: 'created' create: 'created'
delete: 'deleted' delete: 'deleted'
update: 'updated' update: 'updated'
ticket: tickets:
menus:
panel:
placeholder: 'Select a ticket category'

View File

@ -5,7 +5,7 @@ const {
const { diff: getDiff } = require('object-diffy'); const { diff: getDiff } = require('object-diffy');
const exists = thing => (typeof thing === 'string' && thing.length > 0) && thing !== null && thing !== undefined; const exists = thing => (typeof thing === 'string' && thing.length > 0) && (thing !== null && thing !== undefined);
const arrToObj = obj => { const arrToObj = obj => {
for (const key in obj) { for (const key in obj) {
@ -26,7 +26,7 @@ function makeDiff({
for (const key in diff) { for (const key in diff) {
if (key === 'createdAt') continue; // object-diffy doesn't like dates if (key === 'createdAt') continue; // object-diffy doesn't like dates
const from = exists(diff[key].from) ? `- ${String(diff[key].from).replace(/\n/g, '\\n')}\n` : ''; const from = exists(diff[key].from) ? `- ${String(diff[key].from).replace(/\n/g, '\\n')}\n` : '';
const to = exists(diff[key].to) ? `+ ${String(diff[key].to).replace(/\n/g, '\\n')}\n` : ''; const to = exists(diff[key].to) ? `+ ${String(diff[key].to).replace(/\n/g, '\\n')}\n` : '';
fields.push({ fields.push({
inline: true, inline: true,
name: key, name: key,
@ -70,6 +70,10 @@ async function logAdminEvent(client, {
where: { id: guildId }, where: { id: guildId },
}); });
if (!settings.logChannel) return; if (!settings.logChannel) return;
const colour = action === 'create'
? 'Green' : action === 'update'
? 'Orange' : action === 'delete'
? 'Red' : 'Default';
const getMessage = client.i18n.getLocale(settings.locale); const getMessage = client.i18n.getLocale(settings.locale);
const i18nOptions = { const i18nOptions = {
user: `<@${user.id}>`, user: `<@${user.id}>`,
@ -79,7 +83,7 @@ async function logAdminEvent(client, {
if (!channel) return; if (!channel) return;
const embeds = [ const embeds = [
new EmbedBuilder() new EmbedBuilder()
.setColor('Orange') .setColor(colour)
.setAuthor({ .setAuthor({
iconURL: user.avatarURL(), iconURL: user.avatarURL(),
name: user.username, name: user.username,
@ -105,7 +109,7 @@ async function logAdminEvent(client, {
if (diff && diff.original) { if (diff && diff.original) {
embeds.push( embeds.push(
new EmbedBuilder() new EmbedBuilder()
.setColor('Orange') .setColor(colour)
.setTitle(getMessage('log.admin.changes')) .setTitle(getMessage('log.admin.changes'))
.setFields(makeDiff(diff)), .setFields(makeDiff(diff)),
); );

View File

@ -0,0 +1,141 @@
const {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle: {
Primary,
Secondary,
},
ChannelType: { GuildText },
EmbedBuilder,
SelectMenuBuilder,
SelectMenuOptionBuilder,
} = require('discord.js');
const emoji = require('node-emoji');
const { logAdminEvent } = require('../../../../../lib/logging');
module.exports.post = fastify => ({
handler: async (req, res) => {
/** @type {import('client')} */
const client = res.context.config.client;
const guild = client.guilds.cache.get(req.params.guild);
const data = req.body;
const settings = await client.prisma.guild.findUnique({
select: {
categories: true,
footer: true,
locale: true,
primaryColour: true,
},
where: { id: guild.id },
});
const getMessage = client.i18n.getLocale(settings.locale);
const categories = settings.categories.filter(c => data.categories.includes(c.id));
if (categories.length === 0) throw new Error('No categories');
if (categories.length !== 1 && data.type === 'MESSAGE') throw new Error('Invalid number of categories for panel type');
let channel;
if (data.channel) {
channel = await client.channels.fetch(data.channel);
} else {
const allow = ['ViewChannel', 'ReadMessageHistory'];
if (data.type === 'MESSAGE') allow.push('SendMessages');
channel = await guild.channels.create({
name: 'create-a-ticket',
permissionOverwrites: [
{
allow,
deny: ['AddReactions', 'AttachFiles'],
id: guild.roles.everyone,
},
],
position: 1,
rateLimitPerUser: 15,
reason: 'New ticket panel',
type: GuildText,
});
}
const embed = new EmbedBuilder()
.setColor(settings.primaryColour)
.setTitle(data.title)
.setFooter({
iconURL: guild.iconURL(),
text: settings.footer,
});
if (data.description) embed.setDescription(data.description);
if (data.image) embed.setImage(data.image);
if (data.thumbnail) embed.setThumbnail(data.thumbnail);
if (data.type === 'MESSAGE') {
await channel.send({ embeds: [embed] });
} else {
const components = [];
if (categories.length === 1) {
components.push(
new ButtonBuilder()
.setCustomId(JSON.stringify({
action: 'createTicket',
target: categories[0].id,
}))
.setStyle(Primary)
.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: 'createTicket',
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('createTicket')
.setPlaceholder(getMessage('menus.panel.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({
components: [
new ActionRowBuilder()
.setComponents(components),
],
embeds: [embed],
});
}
logAdminEvent(client, {
action: 'create',
guildId: guild.id,
target: {
id: channel.toString(),
type: 'panel',
},
userId: req.user.payload.id,
});
return true;
},
onRequest: [fastify.authenticate, fastify.isAdmin],
});