@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.
Files changed (2) hide show
  1. package/index.js +39 -107
  2. package/package.json +2 -3
package/index.js CHANGED
@@ -1,13 +1,12 @@
1
1
  const { Collection, WebhookClient, MessageEmbed } = require("discord.js"),
2
- { encrypt, decrypt } = require("aes256"),
3
- { generate } = require("shortid"),
4
- { Interactions: { button, modal } } = require("@elara-services/packages"),
5
- Webhook = require("discord-hook"),
6
- de = {
7
- user: "<:Members:860931214232125450>",
8
- channel: "<:Channel:841654412509839390>",
9
- transcript: "<:Log:792290922749624320>"
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
- let user = this.options.client.users.resolve(customId.split("close:confirm:")[1]) ?? await this.options.client.users.fetch(customId.split("close:confirm:")[1]).catch(() => null);
115
- if (!user) return send({ content: `❌ I was unable to fetch the user that opened the ticket.`, ephemeral: true })
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 || !messages.length) return send({ ephemeral: true, content: `❌ I was unable to close the ticket, I couldn't fetch the messages in this channel.` })
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({ ephemeral: true, content: `${emojis.x} I was unable to delete the channel & close the ticket.` })
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
- let [support, supportUsers, supportIds] = [[], [], this.getSupportIds()];
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) support.push(sup);
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) supportUsers.push(uId);
122
+ if (member) permissions.push({ type: "member", id: uId, allow });
139
123
  }
140
- await send({ ephemeral: true }, true);
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: `${emojis.x} I was unable to create the ticket channel, if this keeps happening contact one of the staff members via their DMs!` });
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(this.options.encryptToken, id);
264
- case "d": return decrypt(this.options.encryptToken, id);
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 (!this.options?.debug) return null;
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.1.0",
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.0"
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
  }