@elara-services/tickets 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +39 -107
- package/package.json +2 -3
package/index.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
const { Collection, WebhookClient, MessageEmbed } = require("discord.js"),
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
2
|
+
{ generate } = require("shortid"),
|
|
3
|
+
{ Interactions: { button, modal }, AES } = require("@elara-services/packages"),
|
|
4
|
+
Webhook = require("discord-hook"),
|
|
5
|
+
de = {
|
|
6
|
+
user: "<:Members:860931214232125450>",
|
|
7
|
+
channel: "<:Channel:841654412509839390>",
|
|
8
|
+
transcript: "<:Log:792290922749624320>"
|
|
9
|
+
};
|
|
11
10
|
|
|
12
11
|
module.exports = class Tickets {
|
|
13
12
|
constructor(options = {}) {
|
|
@@ -15,6 +14,7 @@ module.exports = class Tickets {
|
|
|
15
14
|
if (!("client" in options) || !("prefix" in options) || !("encryptToken" in options)) throw new Error(`You forgot to fill out either 'client', 'prefix' or 'encryptToken'`)
|
|
16
15
|
this.options = options;
|
|
17
16
|
};
|
|
17
|
+
|
|
18
18
|
get prefix() { return `system:ticket:${this.options.prefix}`; };
|
|
19
19
|
|
|
20
20
|
/** @private */
|
|
@@ -26,6 +26,7 @@ module.exports = class Tickets {
|
|
|
26
26
|
avatar: this.options.webhook?.avatar || this.options.webhookAvatar || "https://cdn.discordapp.com/emojis/818757771310792704.png?v=1"
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
+
|
|
29
30
|
webhook() {
|
|
30
31
|
const { id, token, username, avatar } = this.webhookOptions;
|
|
31
32
|
return new Webhook(`https://discord.com/api/webhooks/${id}/${token}`, { username, avatar_url: avatar });
|
|
@@ -38,7 +39,6 @@ module.exports = class Tickets {
|
|
|
38
39
|
if (int?.isButton?.() || int?.isModalSubmit()) {
|
|
39
40
|
let { guild, channel, member, customId } = int,
|
|
40
41
|
category = guild?.channels?.resolve?.(this.options.ticketCategory || channel?.parentId);
|
|
41
|
-
|
|
42
42
|
if (!guild || !guild.available || !channel || !member || !category) return;
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -53,26 +53,13 @@ module.exports = class Tickets {
|
|
|
53
53
|
};
|
|
54
54
|
switch (customId) {
|
|
55
55
|
case this.prefix: {
|
|
56
|
-
if (this.options.modal?.enabled) {
|
|
57
|
-
return int.showModal(this.modal({
|
|
58
|
-
title: this.options.modal.title,
|
|
59
|
-
components: this.options.modal.questions?.length >= 1 ?
|
|
60
|
-
this.options.modal.questions.slice(0, 5).map(c => ({ type: 1, components: [{ min_length: c.min_length || 10, max_length: c.max_length || 4000, type: 4, style: c.style || 2, label: c.label, value: c.value, placeholder: c.placeholder, required: c.required, custom_id: c.label || `random_${Math.floor(Math.random() * 10000)}` }] })) :
|
|
61
|
-
[]
|
|
62
|
-
})).catch(e => this._debug(e));
|
|
63
|
-
}
|
|
56
|
+
if (this.options.modal?.enabled) return int.showModal(this.modal({ title: this.options.modal.title, components: this.options.modal.questions?.length >= 1 ? this.options.modal.questions.slice(0, 5).map(c => ({ type: 1, components: [{ min_length: c.min_length || 10, max_length: c.max_length || 4000, type: 4, style: c.style || 2, label: c.label, value: c.value, placeholder: c.placeholder, required: c.required, custom_id: c.label || `random_${Math.floor(Math.random() * 10000)}` }] })) : [] })).catch(e => this._debug(e));
|
|
64
57
|
return this.handleCreate({ guild, member, category, send })
|
|
65
58
|
};
|
|
66
59
|
|
|
67
60
|
case `${this.prefix}:close`: {
|
|
68
61
|
if (this.options.support?.canOnlyCloseTickets && !member.permissions.has("MANAGE_GUILD")) {
|
|
69
|
-
let [ support, staffOnly ] = [
|
|
70
|
-
this.getSupportIds(),
|
|
71
|
-
() => send({
|
|
72
|
-
ephemeral: true,
|
|
73
|
-
embeds: [ { author: { name: `Only support staff can close tickets`, iconURL: "https://cdn.discordapp.com/emojis/781955502035697745.gif" }, color: 0xFF0000 } ]
|
|
74
|
-
})
|
|
75
|
-
];
|
|
62
|
+
let [ support, staffOnly ] = [ this.getSupportIds(), () => send({ ephemeral: true, embeds: [ { author: { name: `Only support staff can close tickets`, iconURL: "https://cdn.discordapp.com/emojis/781955502035697745.gif" }, color: 0xFF0000 } ] }) ];
|
|
76
63
|
if (!support.users?.includes?.(member.id)) return staffOnly();
|
|
77
64
|
if (support.roles?.length && !support.roles.some(c => member.roles.cache.has(c))) return staffOnly()
|
|
78
65
|
}
|
|
@@ -80,7 +67,7 @@ module.exports = class Tickets {
|
|
|
80
67
|
}
|
|
81
68
|
|
|
82
69
|
case `${this.prefix}:modal_submit`: {
|
|
83
|
-
let [embed, fields, split] = [new MessageEmbed().setColor("ORANGE"), [], false];
|
|
70
|
+
let [ embed, fields, split ] = [ new MessageEmbed().setColor("ORANGE").setTimestamp().setTitle(`For Responses`).setFooter({ text: `ID: ${member.id}` }).setAuthor({ name: member.user.username, iconURL: member.user.displayAvatarURL({ dynamic: true }) }), [], false];
|
|
84
71
|
for (const c of int.fields.components) {
|
|
85
72
|
for (const cc of c.components) {
|
|
86
73
|
if (cc.value && cc.customId) {
|
|
@@ -92,31 +79,23 @@ module.exports = class Tickets {
|
|
|
92
79
|
}
|
|
93
80
|
if (embed.length >= 6000 || split) {
|
|
94
81
|
return this.handleCreate({ guild, member, category, send, embeds: fields.map((v, i) => ({
|
|
95
|
-
title: `Form Response: ${v.name}`,
|
|
96
|
-
color: embed.color,
|
|
97
|
-
description: v.value,
|
|
82
|
+
title: `Form Response: ${v.name}`, color: embed.color, description: v.value,
|
|
98
83
|
author: i === 0 ? { name: member.user.username, iconURL: member.user.displayAvatarURL({ dynamic: true }) } : undefined,
|
|
99
84
|
timestamp: fields.length - 1 === i ? new Date() : undefined,
|
|
100
85
|
footer: fields.length - 1 === i ? { text: `ID: ${member.id}` } : undefined
|
|
101
86
|
})) })
|
|
102
87
|
};
|
|
103
|
-
|
|
104
|
-
return this.handleCreate({ guild, member, category, send, embeds: [
|
|
105
|
-
embed
|
|
106
|
-
.setTitle(`Form Responses`)
|
|
107
|
-
.setTimestamp()
|
|
108
|
-
.setAuthor({ name: member.user.username, iconURL: member.user.displayAvatarURL({ dynamic: true }) })
|
|
109
|
-
.setFooter({ text: `ID: ${member.id}` })
|
|
110
|
-
]})
|
|
88
|
+
return this.handleCreate({ guild, member, category, send, embeds: [ embed ]})
|
|
111
89
|
}
|
|
112
90
|
};
|
|
113
91
|
if (customId.startsWith(`${this.prefix}:close:confirm`)) {
|
|
114
|
-
|
|
115
|
-
|
|
92
|
+
await send({ ephemeral: true }, true);
|
|
93
|
+
let user = await this.options.client.users.fetch(customId.split("close:confirm:")[1]).catch(() => null);
|
|
94
|
+
if (!user) return send({ content: `❌ I was unable to fetch the user that opened the ticket.` })
|
|
116
95
|
let messages = await this.fetchMessages(channel, 5000);
|
|
117
|
-
if (!messages
|
|
96
|
+
if (!messages?.length) return send({ content: `❌ I was unable to close the ticket, I couldn't fetch the messages in this channel.` })
|
|
118
97
|
let closed = await channel.delete(`${member.user.tag} (${member.id}) closed the ticket.`).catch(e => this._debug(e));
|
|
119
|
-
if (!closed) return send({
|
|
98
|
+
if (!closed) return send({ content: `❌ I was unable to delete the channel & close the ticket.` })
|
|
120
99
|
return this.closeTicket({ channel, guild, user, member, messages });
|
|
121
100
|
}
|
|
122
101
|
};
|
|
@@ -124,10 +103,15 @@ module.exports = class Tickets {
|
|
|
124
103
|
|
|
125
104
|
/** @private */
|
|
126
105
|
async handleCreate({ guild, member, category, send, embeds = [] } = {}) {
|
|
127
|
-
|
|
106
|
+
await send({ ephemeral: true }, true);
|
|
107
|
+
let [ supportIds, permissions, allow, { appeals } ] = [
|
|
108
|
+
this.getSupportIds(), [],
|
|
109
|
+
["ADD_REACTIONS", "ATTACH_FILES", "CREATE_INSTANT_INVITE", "EMBED_LINKS", "READ_MESSAGE_HISTORY", "VIEW_CHANNEL", "USE_EXTERNAL_EMOJIS", "SEND_MESSAGES"],
|
|
110
|
+
this.options ?? {}
|
|
111
|
+
];
|
|
128
112
|
if (supportIds.roles.length) for (const sup of supportIds.roles) {
|
|
129
113
|
let role = guild.roles.resolve(sup);
|
|
130
|
-
if (role)
|
|
114
|
+
if (role) permissions.push({ type: "role", id: sup, allow });
|
|
131
115
|
};
|
|
132
116
|
|
|
133
117
|
if (supportIds.users.length) for (const uId of supportIds.users) {
|
|
@@ -135,11 +119,9 @@ module.exports = class Tickets {
|
|
|
135
119
|
if (e?.stack?.includes?.("Unknown Member")) this.options.support.users = this.options.support.users.filter(c => c !== uId);
|
|
136
120
|
return this._debug(e);
|
|
137
121
|
});
|
|
138
|
-
if (member)
|
|
122
|
+
if (member) permissions.push({ type: "member", id: uId, allow });
|
|
139
123
|
}
|
|
140
|
-
|
|
141
|
-
if (this.options.appeals?.enabled) {
|
|
142
|
-
let appeals = this.options.appeals;
|
|
124
|
+
if (appeals?.enabled) {
|
|
143
125
|
if (appeals.mainserver?.id && appeals.mainserver.checkIfBanned) {
|
|
144
126
|
let server = this.options.client.guilds.resolve(appeals.mainserver.id);
|
|
145
127
|
if (server?.available) {
|
|
@@ -162,14 +144,6 @@ module.exports = class Tickets {
|
|
|
162
144
|
}
|
|
163
145
|
}
|
|
164
146
|
}
|
|
165
|
-
let [permissions, allow] = [
|
|
166
|
-
[],
|
|
167
|
-
["ADD_REACTIONS", "ATTACH_FILES", "CREATE_INSTANT_INVITE", "EMBED_LINKS", "READ_MESSAGE_HISTORY", "VIEW_CHANNEL", "USE_EXTERNAL_EMOJIS", "SEND_MESSAGES"]
|
|
168
|
-
];
|
|
169
|
-
if (support.length) for (const sup of support) permissions.push({ type: "role", id: sup, allow });
|
|
170
|
-
if (supportUsers.length) for (const user of supportUsers) permissions.push({ type: "member", id: user, allow });
|
|
171
|
-
|
|
172
|
-
/** @type {import("discord.js").TextChannel} */
|
|
173
147
|
let channel = await guild.channels.create(`${this.options.prefix}-${generate().slice(0, 5).replace(/-|_/g, "")}`, {
|
|
174
148
|
type: "GUILD_TEXT", parent: category, reason: `Ticket created by: @${member.user.tag} (${member.id})`,
|
|
175
149
|
topic: `ID: ${this.code(member.id, "e")}`,
|
|
@@ -180,7 +154,7 @@ module.exports = class Tickets {
|
|
|
180
154
|
...permissions
|
|
181
155
|
]
|
|
182
156
|
}).catch(e => this._debug(e));
|
|
183
|
-
if (!channel) return send({ content:
|
|
157
|
+
if (!channel) return send({ content: `❌ I was unable to create the ticket channel, if this keeps happening contact one of the staff members via their DMs!` });
|
|
184
158
|
let msg = await channel.send({
|
|
185
159
|
content: this.options.ticketOpen?.content?.replace?.(/%user%/gi, member.user.toString())?.replace?.(/%server%/gi, guild.name) || `${member.user.toString()} 👋 Hello, please explain what you need help with.`,
|
|
186
160
|
embeds: this.options.ticketOpen?.embeds || [{
|
|
@@ -204,24 +178,13 @@ module.exports = class Tickets {
|
|
|
204
178
|
footer: { text: `Ticket ID: ${channel.name.split("-")[1]}` },
|
|
205
179
|
}).send().catch(e => this._debug(e));
|
|
206
180
|
return send({
|
|
207
|
-
embeds: [
|
|
208
|
-
{
|
|
209
|
-
author: { name: `Ticket Created!`, icon_url: `https://cdn.discordapp.com/emojis/476629550797684736.gif` },
|
|
210
|
-
description: channel.toString(),
|
|
211
|
-
color: 0xFF000
|
|
212
|
-
}
|
|
213
|
-
],
|
|
181
|
+
embeds: [ { author: { name: `Ticket Created!`, icon_url: `https://cdn.discordapp.com/emojis/476629550797684736.gif` }, description: channel.toString(), color: 0xFF000 } ],
|
|
214
182
|
components: [ { type: 1, components: [ button({ title: "Go to ticket", url: msg.url }) ] } ]
|
|
215
183
|
})
|
|
216
184
|
}
|
|
217
185
|
|
|
218
186
|
button(options = { style: 3, label: "Create Ticket", emoji: { name: "📩" } }) {
|
|
219
|
-
return button({
|
|
220
|
-
id: options?.id || this.prefix,
|
|
221
|
-
style: options.style || 3,
|
|
222
|
-
title: options.label,
|
|
223
|
-
emoji: options.emoji
|
|
224
|
-
});
|
|
187
|
+
return button({ id: options?.id || this.prefix, style: options.style || 3, title: options.label, emoji: options.emoji });
|
|
225
188
|
};
|
|
226
189
|
|
|
227
190
|
/**
|
|
@@ -230,17 +193,7 @@ module.exports = class Tickets {
|
|
|
230
193
|
* @param {import("@elara-services/packages").Modal['components']} [options.components]
|
|
231
194
|
*/
|
|
232
195
|
modal(options = { title: "", components: [] }) {
|
|
233
|
-
return modal({
|
|
234
|
-
id: `${this.prefix}:modal_submit`,
|
|
235
|
-
title: options?.title || "Create Ticket",
|
|
236
|
-
components: options?.components?.length >= 1 ? options.components : [
|
|
237
|
-
{
|
|
238
|
-
type: 1, components: [
|
|
239
|
-
{ type: 4, min_length: 10, max_length: 4000, custom_id: "message", label: "Content", style: 2, placeholder: "What's the ticket about?", required: true }
|
|
240
|
-
]
|
|
241
|
-
}
|
|
242
|
-
]
|
|
243
|
-
})
|
|
196
|
+
return modal({ id: `${this.prefix}:modal_submit`, title: options?.title || "Create Ticket", components: options?.components?.length >= 1 ? options.components : [ { type: 1, components: [ { type: 4, min_length: 10, max_length: 4000, custom_id: "message", label: "Content", style: 2, placeholder: "What's the ticket about?", required: true } ] } ] })
|
|
244
197
|
}
|
|
245
198
|
|
|
246
199
|
async starterMessage(channelId, options) {
|
|
@@ -248,20 +201,16 @@ module.exports = class Tickets {
|
|
|
248
201
|
if (!channel) return Promise.reject(`No channel found for: ${channelId}`);
|
|
249
202
|
if (!channel.isText()) return Promise.reject(`The channel ID provided isn't a text-based-channel`);
|
|
250
203
|
if (!channel.permissionsFor?.(this.options.client.user.id)?.has?.(["VIEW_CHANNEL", "SEND_MESSAGES", "EMBED_LINKS", "ATTACH_FILES", "READ_MESSAGE_HISTORY"])) return Promise.reject(`I'm missing permissions in ${channel.name} (${channelId})`);
|
|
251
|
-
return channel.send({
|
|
252
|
-
content: options?.content,
|
|
253
|
-
files: options?.attachments,
|
|
254
|
-
embeds: options?.embeds,
|
|
255
|
-
components: options?.components || [{ type: 1, components: [this.button()] }]
|
|
256
|
-
})
|
|
204
|
+
return channel.send({ content: options?.content, files: options?.attachments, embeds: options?.embeds, components: options?.components || [{ type: 1, components: [this.button()] }] })
|
|
257
205
|
.then(() => console.log(`Sent the starter message in ${channel.name} (${channel.id})`))
|
|
258
206
|
};
|
|
259
207
|
|
|
260
208
|
code(id, type = "d") {
|
|
261
209
|
try {
|
|
210
|
+
const aes = new AES(this.options.encryptToken);
|
|
262
211
|
switch (type) {
|
|
263
|
-
case "e": return encrypt(
|
|
264
|
-
case "d": return decrypt(
|
|
212
|
+
case "e": return aes.encrypt(id);
|
|
213
|
+
case "d": return aes.decrypt(id);
|
|
265
214
|
}
|
|
266
215
|
} catch {
|
|
267
216
|
return id;
|
|
@@ -286,13 +235,6 @@ module.exports = class Tickets {
|
|
|
286
235
|
return [...(await channel.messages.fetch({ limit, before, after, around }).catch(() => new Collection())).values()];
|
|
287
236
|
};
|
|
288
237
|
|
|
289
|
-
/**
|
|
290
|
-
* @param {import("discord.js").TextBasedChannel} channel
|
|
291
|
-
* @param {import("discord.js").Message[]} messages
|
|
292
|
-
* @param {string} ticketID
|
|
293
|
-
* @param {string} type
|
|
294
|
-
* @returns {string}
|
|
295
|
-
*/
|
|
296
238
|
displayMessages(channel, messages = [], ticketID, type) {
|
|
297
239
|
let users = [];
|
|
298
240
|
for (const i of messages.values()) {
|
|
@@ -393,15 +335,7 @@ module.exports = class Tickets {
|
|
|
393
335
|
}).catch(e => this._debug(e));
|
|
394
336
|
}).catch(e => this._debug(e));
|
|
395
337
|
};
|
|
396
|
-
|
|
397
|
-
* @typedef {Object} getSupportResponse
|
|
398
|
-
* @property {string[]} [roles]
|
|
399
|
-
* @property {string[]} [users]
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
* @private
|
|
403
|
-
* @returns {getSupportResponse}
|
|
404
|
-
*/
|
|
338
|
+
|
|
405
339
|
getSupportIds() {
|
|
406
340
|
return {
|
|
407
341
|
roles: this.options.support?.roles || this.options.supportRoleIds || [],
|
|
@@ -411,9 +345,7 @@ module.exports = class Tickets {
|
|
|
411
345
|
|
|
412
346
|
/** @private */
|
|
413
347
|
_debug(...args) {
|
|
414
|
-
if (
|
|
415
|
-
console.log(...args);
|
|
348
|
+
if (this.options?.debug) console.log(...args);
|
|
416
349
|
return null;
|
|
417
350
|
}
|
|
418
|
-
|
|
419
351
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elara-services/tickets",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Helper for tickets",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -8,11 +8,10 @@
|
|
|
8
8
|
"author": "SUPERCHIEFYT (Elara-Discord-Bots, Elara-Services)",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"optionalDependencies": {
|
|
11
|
-
"discord.js": "13.8.
|
|
11
|
+
"discord.js": "13.8.1"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@elara-services/packages": "5.0.0",
|
|
15
|
-
"aes256": "1.1.0",
|
|
16
15
|
"discord-hook": "2.0.0",
|
|
17
16
|
"shortid": "2.2.16"
|
|
18
17
|
}
|