@elara-services/tickets 2.0.3 → 2.1.1

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/README.md CHANGED
@@ -51,6 +51,7 @@ const { Client } = require("discord.js"),
51
51
  avatar: "", // 'webhookAvatar' support will be removed in the next major version
52
52
  },
53
53
  support: {
54
+ canOnlyCloseTickets: true, // If 'true' only roles and users listed below can close tickets. (OR people with 'Manage Server' permission)
54
55
  roles: [ // 'supportRoleIds' support will be removed in the next major version
55
56
  "123456789"
56
57
  ],
package/index.d.ts CHANGED
@@ -12,7 +12,7 @@ declare module "@elara-services/tickets" {
12
12
  ticketOpen?: Pick<MessageOptions, "content" | "embeds">
13
13
  appeals?: {
14
14
  enabled: boolean;
15
- mainServer: {
15
+ mainserver: {
16
16
  id: string;
17
17
  checkIfBanned: boolean;
18
18
  };
@@ -42,6 +42,7 @@ declare module "@elara-services/tickets" {
42
42
  support?: {
43
43
  roles?: string[];
44
44
  users?: string[];
45
+ canOnlyCloseTickets?: boolean;
45
46
  };
46
47
 
47
48
  /** @deprecated Use 'webhook.id' */
package/index.js CHANGED
@@ -10,11 +10,12 @@ const { Collection, WebhookClient, MessageEmbed } = require("discord.js"),
10
10
  }
11
11
 
12
12
  module.exports = class Tickets {
13
- constructor(options) {
13
+ constructor(options = {}) {
14
+ if (typeof options !== "object") throw new Error(`You didn't provide any data in the constructor, fill it out!`);
15
+ if (!("client" in options) || !("prefix" in options) || !("encryptToken" in options)) throw new Error(`You forgot to fill out either 'client', 'prefix' or 'encryptToken'`)
14
16
  this.options = options;
15
17
  };
16
18
  get prefix() { return `system:ticket:${this.options.prefix}`; };
17
-
18
19
  /** @private */
19
20
  get webhookOptions() {
20
21
  return {
@@ -36,7 +37,6 @@ module.exports = class Tickets {
36
37
  if (int?.isButton?.() || int?.isModalSubmit()) {
37
38
  let { guild, channel, member, customId } = int,
38
39
  category = guild?.channels?.resolve?.(this.options.ticketCategory || channel?.parentId);
39
-
40
40
  if (!guild || !guild.available || !channel || !member || !category) return;
41
41
 
42
42
  /**
@@ -54,18 +54,28 @@ module.exports = class Tickets {
54
54
  if (this.options.modal?.enabled) {
55
55
  return int.showModal(this.modal({
56
56
  title: this.options.modal.title,
57
- components: this.options.modal.questions?.length >= 1 ?
58
- 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)}` }] })) :
59
- []
57
+ 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)}` }] })) : []
60
58
  })).catch(e => this._debug(e));
61
59
  }
62
60
  return this.handleCreate({ guild, member, category, send })
63
61
  };
64
62
 
65
- case `${this.prefix}:close`: return send({ ephemeral: true, content: `🤔 Are you sure you want to close this ticket?`, components: [{ type: 1, components: [{ type: 2, custom_id: `${this.prefix}:close:confirm:${this.code(channel.topic?.split?.("ID: ")?.[1])}`, label: "Yes close the ticket", style: 4, emoji: { id: "807031399563264030" } }] }] })
63
+ case `${this.prefix}:close`: {
64
+ if (this.options.support?.canOnlyCloseTickets && !member.permissions.has("MANAGE_GUILD")) {
65
+ let [ support, staffOnly ] = [
66
+ this.getSupportIds(),
67
+ () => send({ ephemeral: true, embeds: [ { author: { name: `Only support staff can close tickets`, iconURL: "https://cdn.discordapp.com/emojis/781955502035697745.gif" }, color: 0xFF0000 } ] })
68
+ ];
69
+ if (!support.users?.includes?.(member.id)) return staffOnly();
70
+ if (support.roles?.length && !support.roles.some(c => member.roles.cache.has(c))) return staffOnly()
71
+ }
72
+ return send({ ephemeral: true, content: `🤔 Are you sure you want to close this ticket?`, components: [{ type: 1, components: [{ type: 2, custom_id: `${this.prefix}:close:confirm:${this.code(channel.topic?.split?.("ID: ")?.[1])}`, label: "Yes close the ticket", style: 4, emoji: { id: "807031399563264030" } }] }] })
73
+ }
66
74
 
67
75
  case `${this.prefix}:modal_submit`: {
68
- let [embed, fields, split] = [new MessageEmbed().setColor("ORANGE"), [], false];
76
+ let [embed, fields, split] = [
77
+ new MessageEmbed().setColor("ORANGE").setTimestamp().setTitle(`For Responses`).setFooter({ text: `ID: ${member.id}` }).setAuthor({ name: member.user.username, iconURL: member.user.displayAvatarURL({ dynamic: true }) }),
78
+ [], false];
69
79
  for (const c of int.fields.components) {
70
80
  for (const cc of c.components) {
71
81
  if (cc.value && cc.customId) {
@@ -77,31 +87,22 @@ module.exports = class Tickets {
77
87
  }
78
88
  if (embed.length >= 6000 || split) {
79
89
  return this.handleCreate({ guild, member, category, send, embeds: fields.map((v, i) => ({
80
- title: `Form Response: ${v.name}`,
81
- color: embed.color,
82
- description: v.value,
90
+ title: `Form Response: ${v.name}`, color: embed.color, description: v.value,
83
91
  author: i === 0 ? { name: member.user.username, iconURL: member.user.displayAvatarURL({ dynamic: true }) } : undefined,
84
92
  timestamp: fields.length - 1 === i ? new Date() : undefined,
85
93
  footer: fields.length - 1 === i ? { text: `ID: ${member.id}` } : undefined
86
94
  })) })
87
95
  };
88
-
89
- return this.handleCreate({ guild, member, category, send, embeds: [
90
- embed
91
- .setTitle(`Form Responses`)
92
- .setTimestamp()
93
- .setAuthor({ name: member.user.username, iconURL: member.user.displayAvatarURL({ dynamic: true }) })
94
- .setFooter({ text: `ID: ${member.id}` })
95
- ]})
96
+ return this.handleCreate({ guild, member, category, send, embeds: [ embed ]})
96
97
  }
97
98
  };
98
99
  if (customId.startsWith(`${this.prefix}:close:confirm`)) {
99
100
  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);
100
101
  if (!user) return send({ content: `❌ I was unable to fetch the user that opened the ticket.`, ephemeral: true })
101
102
  let messages = await this.fetchMessages(channel, 5000);
102
- 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.` })
103
+ if (!messages?.length) return send({ ephemeral: true, content: `❌ I was unable to close the ticket, I couldn't fetch the messages in this channel.` })
103
104
  let closed = await channel.delete(`${member.user.tag} (${member.id}) closed the ticket.`).catch(e => this._debug(e));
104
- if (!closed) return send({ ephemeral: true, content: `${emojis.x} I was unable to delete the channel & close the ticket.` })
105
+ if (!closed) return send({ ephemeral: true, content: `❌ I was unable to delete the channel & close the ticket.` })
105
106
  return this.closeTicket({ channel, guild, user, member, messages });
106
107
  }
107
108
  };
@@ -109,10 +110,15 @@ module.exports = class Tickets {
109
110
 
110
111
  /** @private */
111
112
  async handleCreate({ guild, member, category, send, embeds = [] } = {}) {
112
- let [support, supportUsers, supportIds] = [[], [], this.getSupportIds()];
113
+ await send({ ephemeral: true }, true);
114
+ let [ supportIds, permissions, allow, { appeals } ] = [
115
+ this.getSupportIds(), [],
116
+ ["ADD_REACTIONS", "ATTACH_FILES", "CREATE_INSTANT_INVITE", "EMBED_LINKS", "READ_MESSAGE_HISTORY", "VIEW_CHANNEL", "USE_EXTERNAL_EMOJIS", "SEND_MESSAGES"],
117
+ this.options ?? {}
118
+ ];
113
119
  if (supportIds.roles.length) for (const sup of supportIds.roles) {
114
120
  let role = guild.roles.resolve(sup);
115
- if (role) support.push(sup);
121
+ if (role) permissions.push({ type: "role", id: sup, allow });
116
122
  };
117
123
 
118
124
  if (supportIds.users.length) for (const uId of supportIds.users) {
@@ -120,11 +126,9 @@ module.exports = class Tickets {
120
126
  if (e?.stack?.includes?.("Unknown Member")) this.options.support.users = this.options.support.users.filter(c => c !== uId);
121
127
  return this._debug(e);
122
128
  });
123
- if (member) supportUsers.push(uId);
129
+ if (member) permissions.push({ type: "member", id: uId, allow });
124
130
  }
125
- await send({ ephemeral: true }, true);
126
- if (this.options.appeals?.enabled) {
127
- let appeals = this.options.appeals;
131
+ if (appeals?.enabled) {
128
132
  if (appeals.mainserver?.id && appeals.mainserver.checkIfBanned) {
129
133
  let server = this.options.client.guilds.resolve(appeals.mainserver.id);
130
134
  if (server?.available) {
@@ -147,12 +151,6 @@ module.exports = class Tickets {
147
151
  }
148
152
  }
149
153
  }
150
- let [permissions, allow] = [
151
- [],
152
- ["ADD_REACTIONS", "ATTACH_FILES", "CREATE_INSTANT_INVITE", "EMBED_LINKS", "READ_MESSAGE_HISTORY", "VIEW_CHANNEL", "USE_EXTERNAL_EMOJIS", "SEND_MESSAGES"]
153
- ];
154
- if (support.length) for (const sup of support) permissions.push({ type: "role", id: sup, allow });
155
- if (supportUsers.length) for (const user of supportUsers) permissions.push({ type: "member", id: user, allow });
156
154
 
157
155
  /** @type {import("discord.js").TextChannel} */
158
156
  let channel = await guild.channels.create(`${this.options.prefix}-${generate().slice(0, 5).replace(/-|_/g, "")}`, {
@@ -165,7 +163,7 @@ module.exports = class Tickets {
165
163
  ...permissions
166
164
  ]
167
165
  }).catch(e => this._debug(e));
168
- 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!` });
166
+ 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!` });
169
167
  let msg = await channel.send({
170
168
  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.`,
171
169
  embeds: this.options.ticketOpen?.embeds || [{
@@ -189,24 +187,13 @@ module.exports = class Tickets {
189
187
  footer: { text: `Ticket ID: ${channel.name.split("-")[1]}` },
190
188
  }).send().catch(e => this._debug(e));
191
189
  return send({
192
- embeds: [
193
- {
194
- author: { name: `Ticket Created!`, icon_url: `https://cdn.discordapp.com/emojis/476629550797684736.gif` },
195
- description: channel.toString(),
196
- color: 0xFF000
197
- }
198
- ],
190
+ embeds: [ { author: { name: `Ticket Created!`, icon_url: `https://cdn.discordapp.com/emojis/476629550797684736.gif` }, description: channel.toString(), color: 0xFF000 } ],
199
191
  components: [ { type: 1, components: [ button({ title: "Go to ticket", url: msg.url }) ] } ]
200
192
  })
201
193
  }
202
194
 
203
195
  button(options = { style: 3, label: "Create Ticket", emoji: { name: "📩" } }) {
204
- return button({
205
- id: options?.id || this.prefix,
206
- style: options.style || 3,
207
- title: options.label,
208
- emoji: options.emoji
209
- });
196
+ return button({ id: options?.id || this.prefix, style: options.style || 3, title: options.label, emoji: options.emoji });
210
197
  };
211
198
 
212
199
  /**
@@ -218,13 +205,7 @@ module.exports = class Tickets {
218
205
  return modal({
219
206
  id: `${this.prefix}:modal_submit`,
220
207
  title: options?.title || "Create Ticket",
221
- components: options?.components?.length >= 1 ? options.components : [
222
- {
223
- type: 1, components: [
224
- { type: 4, min_length: 10, max_length: 4000, custom_id: "message", label: "Content", style: 2, placeholder: "What's the ticket about?", required: true }
225
- ]
226
- }
227
- ]
208
+ 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 } ] } ]
228
209
  })
229
210
  }
230
211
 
@@ -396,9 +377,7 @@ module.exports = class Tickets {
396
377
 
397
378
  /** @private */
398
379
  _debug(...args) {
399
- if (!this.options?.debug) return null;
400
- console.log(...args);
380
+ if (this.options?.debug) console.log(...args);
401
381
  return null;
402
382
  }
403
-
404
383
  };
package/package.json CHANGED
@@ -1,14 +1,17 @@
1
1
  {
2
2
  "name": "@elara-services/tickets",
3
- "version": "2.0.3",
3
+ "version": "2.1.1",
4
4
  "description": "Helper for tickets",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
7
7
  "typings": "index.d.ts",
8
8
  "author": "SUPERCHIEFYT (Elara-Discord-Bots, Elara-Services)",
9
9
  "license": "MIT",
10
+ "scripts": {
11
+ "postinstall": "node post.js"
12
+ },
10
13
  "optionalDependencies": {
11
- "discord.js": "13.8.0"
14
+ "discord.js": "13.8.1"
12
15
  },
13
16
  "dependencies": {
14
17
  "@elara-services/packages": "5.0.0",
package/post.js ADDED
@@ -0,0 +1,12 @@
1
+ const package = require("./package.json");
2
+ console.log([
3
+ `Package: ${package.name}`,
4
+ `────────────────────────`,
5
+ `Thanks for installing the package!`,
6
+ `Version you installed: ${package.version}`,
7
+ `─────────────────────────────────────────`,
8
+ `To get started look at the 'Getting Started' section on the npm page here: https://www.npmjs.com/package/@elara-services/tickets`,
9
+ `Here is some helpful links:`,
10
+ `Source Code: https://github.com/elara-bots/tickets`,
11
+ `Support: https://discord.gg/qafHJ63`
12
+ ].join("\n"))