@elara-services/tickets 3.0.0 → 3.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/index.d.ts CHANGED
@@ -55,6 +55,12 @@ declare module "@elara-services/tickets" {
55
55
  ticketCategory?: string;
56
56
  /** @deprecated Use 'ticket.open', 'ticketOpen' will be removed in the next major version */
57
57
  ticketOpen?: Pick<MessageOptions, "content" | "embeds">
58
+
59
+ reports?: {
60
+ enabled: boolean;
61
+ shouldBeTicket?: boolean;
62
+
63
+ }
58
64
  }
59
65
 
60
66
  class Tickets {
package/index.js CHANGED
@@ -1,11 +1,12 @@
1
- const v13 = require("./lib/v13");
2
1
  const pack = require("./package.json");
2
+
3
3
  module.exports = (() => {
4
4
  let version = null;
5
5
  try { version = require("discord.js").version; } catch {}
6
6
  if (!version) throw new Error(`[${pack.name}, v${pack.version}]: I was unable to find the discord.js version you're using.`);
7
7
  let [ major ] = version.split(".");
8
- if (major === "13") return v13;
8
+ if (major === "14") return require("./lib/v14");
9
+ if (major === "13") return require("./lib/v13");
9
10
  if ([ "12", "11" ].includes(major)) throw new Error(`[${pack.name}, v${pack.version}]: The discord.js version you're using is outdated and unsupported, please update to v13+`)
10
- throw new Error(`[${pack.name}, v${pack.version}]: The discord.js version you're using isn't supported by this package. (currently supported: v13)`);
11
+ throw new Error(`[${pack.name}, v${pack.version}]: The discord.js version you're using isn't supported by this package. (currently supported: v13, v14)`);
11
12
  })();
package/lib/base.js CHANGED
@@ -57,7 +57,7 @@ module.exports = class Tickets {
57
57
  files: [{ name: "transcript.txt", attachment: Buffer.from(displayMessages(channel, messages.reverse(), channel.name.split("-")[1], this.options.prefix)) }]
58
58
  })
59
59
  .then(m => {
60
- let components = [{ type: 2, style: 5, label: "Transcript", emoji: { id: "792290922749624320" }, url: `https://my.elara.services/tickets?url=${Array.isArray(m.attachments) ? m.attachments?.[0]?.url : m.attachments?.first?.()?.url ?? "URL_NOT_FOUND"}` }];
60
+ let components = [{ type: 2, style: 5, label: "Transcript", emoji: { id: "792290922749624320" }, url: `https://view.elara.workers.dev/tickets?url=${Array.isArray(m.attachments) ? m.attachments?.[0]?.url : m.attachments?.first?.()?.url ?? "URL_NOT_FOUND"}` }];
61
61
  embeds[0].description += `\n${de.transcript} Transcript: [View here](${components[0].url})`
62
62
  webhook(this.webhookOptions)
63
63
  .embeds(embeds)
package/lib/util.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const { AES } = require("@elara-services/packages"),
2
- { Collection } = require("discord.js"),
2
+ { Collection, version } = require("discord.js"),
3
3
  Webhook = require("discord-hook");
4
4
 
5
5
  exports.code = (message, type = "d", token) => {
@@ -49,7 +49,7 @@ exports.displayMessages = (channel, messages = [], ticketID, type) => {
49
49
  return [`<discord-messages>`, `<discord-message author="${type} Ticket: ${ticketID}" bot="true" verified avatar="https://cdn.discordapp.com/emojis/847397714677334066.png?v=1" role-color="#1da1f2">Total Messages: ${users.map(c => c.count).reduce((a, b) => a + b, 0).toLocaleString()}<br>${users.map(c => `<discord-mention type="role" color="${channel.guild?.members?.resolve?.(c.user.id)?.displayColor ? channel.guild?.members?.resolve?.(c.user.id)?.displayHexColor : `#ffffff`}">${c.user.tag}</discord-mention> (${c.user.id})`).join("<br>")}</discord-message></discord-messages><discord-messages>`,
50
50
  ...messages.map(message => {
51
51
  let str = [
52
- `<discord-message${message.author.bot ? ` bot="true" ${message.author.flags?.has?.("VERIFIED_BOT") ? `verified="true"` : ""}` : ""} new_timestamp="${message.createdAt.toISOString()}" author="${message.author.username}" avatar=${message.author.displayAvatarURL({ dynamic: true, format: "png" })} role-color="${channel.guild?.members?.resolve?.(message.author.id)?.displayColor ? channel.guild?.members?.resolve?.(message.author.id)?.displayHexColor : `#ffffff`}">`
52
+ `<discord-message${message.author.bot ? ` bot="true" ${message.author.flags?.has?.(version?.includes?.("14") ? 1 << 16 : "VERIFIED_BOT") ? `verified="true"` : ""}` : ""} new_timestamp="${message.createdAt.toISOString()}" author="${message.author.username}" avatar=${message.author.displayAvatarURL({ dynamic: true, format: "png" })} role-color="${channel.guild?.members?.resolve?.(message.author.id)?.displayColor ? channel.guild?.members?.resolve?.(message.author.id)?.displayHexColor : `#ffffff`}">`
53
53
  ];
54
54
  if (message.content) {
55
55
  let content = message.content;
@@ -109,7 +109,7 @@ exports.displayMessages = (channel, messages = [], ticketID, type) => {
109
109
  */
110
110
  exports.hasTicket = ({ userId, guild, token, prefix } = {}) => {
111
111
  if (!userId || !guild || !token || !prefix || !guild?.channels?.cache?.size) return false;
112
- let channels = guild.channels.cache.filter(c => c.isText() && c.topic && exports.code(c.topic.split("ID: ")[1], "d", token) === userId && c.name?.match?.(new RegExp(prefix, "gi")));
112
+ let channels = guild.channels.cache.filter(c => [ 0, "GUILD_TEXT" ].includes(c.type) && c.topic && exports.code(c.topic.split("ID: ")[1], "d", token) === userId && c.name?.match?.(new RegExp(prefix, "gi")));
113
113
  if (!channels.size) return false;
114
114
  return true;
115
115
  };
package/lib/v13.js CHANGED
@@ -15,7 +15,7 @@ module.exports = class Tickets extends base {
15
15
  if (int?.isButton?.() || int?.isModalSubmit()) {
16
16
  let { guild, channel, member, customId } = int,
17
17
  category = guild?.channels?.resolve?.(this.options.ticket?.category || this.options.ticketCategory || channel?.parentId);
18
- if (!guild || !guild.available || !channel || !member || !category) return;
18
+ if (!guild?.available || !channel || !member || !category) return;
19
19
 
20
20
  /**
21
21
  * @param {import("discord.js").InteractionDeferReplyOptions|import("discord.js").InteractionReplyOptions} options
@@ -31,7 +31,7 @@ module.exports = class Tickets extends base {
31
31
  case this.prefix: {
32
32
  if (this.options?.ticket?.limitOnePerUser && hasTicket({ userId: member.id, guild, token: this.options.encryptToken, prefix: this.options.prefix })) return send({ embeds: [ embed(`❌ You can only create one (${this.options.prefix}) ticket at a time.`, { guild }) ], ephemeral: true })
33
33
  if (this.options.support?.ignore?.length) {
34
- if (this.options.support.ignore.some(c => member.roles.cache.has(c))) return send({ content: `❌ You can no longer create tickets, if you believe this is a mistake contact one of the staff members.`, ephemeral: true });
34
+ if (this.options.support.ignore.some(c => member.roles.cache.has(c))) return send({ embeds: [embed(`❌ You can no longer create tickets, if you believe this is a mistake contact one of the staff members.`)], ephemeral: true });
35
35
  }
36
36
  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));
37
37
  return this.handleCreate({ guild, member, category, send })
@@ -90,21 +90,21 @@ module.exports = class Tickets extends base {
90
90
  let reason = "No Reason Provided";
91
91
  if (int.isModalSubmit()) reason = int.fields.getTextInputValue("reason")
92
92
  let user = await this.options.client.users.fetch(customId.split("close:confirm:")[1].replace(/:modal_complete/gi, "")).catch(e => this._debug(e));
93
- if (!user) return send({ content: `❌ I was unable to fetch the user that opened the ticket.` })
93
+ if (!user) return send({ embeds: [ embed(`❌ I was unable to fetch the user that opened the ticket.`) ] });
94
94
  let messages = await fetchMessages(channel, 5000);
95
- if (!messages?.length) return send({ content: `❌ I was unable to close the ticket, I couldn't fetch the messages in this channel.` })
95
+ if (!messages?.length) return send({ embeds: [ embed(`❌ I was unable to close the ticket, I couldn't fetch the messages in this channel.`) ] });
96
96
  let closed = await channel.delete(`${member.user.tag} (${member.id}) closed the ticket.`).catch(e => this._debug(e));
97
- if (!closed) return send({ content: `❌ I was unable to delete the channel & close the ticket.` })
97
+ if (!closed) return send({ embeds: [ embed(`❌ I was unable to delete the channel and close the ticket.`) ] });
98
98
  return this.closeTicket({ channel, guild, user, member, messages, reason });
99
99
  };
100
100
 
101
101
  if (customId.startsWith("unban:")) {
102
- if (!int.memberPermissions?.has?.([ "BAN_MEMBERS" ])) return send({ content: `❌ You need (Ban Members) in this server to complete this action!`, ephemeral: true });
102
+ if (!int.memberPermissions?.has?.([ "BAN_MEMBERS" ])) return send({ embeds: [ embed(`❌ You need (Ban Members) in this server to complete this action!`) ], ephemeral: true });
103
103
  let server = getAppealServer(this.options);
104
- if (!server) return send({ content: `❌ I was unable to find the appeal server!`, ephemeral: true });
104
+ if (!server) return send({ embeds: [embed(`❌ I was unable to find the appeal server!`)], ephemeral: true });
105
105
  let mod = server.members.resolve(member.id) || await server.members.fetch(member.id).catch(e => this._debug(e));
106
- if (!mod) return send({ content: `❌ I was unable to find you in ${server.name}!`, ephemeral: true });
107
- if (!mod.permissions?.has?.([ "BAN_MEMBERS" ])) return send({ content: `❌ You need (Ban Members) in ${server.name} to complete this action!`, ephemeral: true });
106
+ if (!mod) return send({ embeds: [embed(`❌ I was unable to find you in ${server.name}!`)], ephemeral: true });
107
+ if (!mod.permissions?.has?.([ "BAN_MEMBERS" ])) return send({ embeds: [embed(`❌ You need (Ban Members) in ${server.name} to complete this action!`)], ephemeral: true });
108
108
  return int.showModal(modal({
109
109
  id: `unban_modal:${customId.split(":")[1]}`,
110
110
  title: `Unban From ${server.name}`,
@@ -115,20 +115,20 @@ module.exports = class Tickets extends base {
115
115
  if (customId.startsWith("unban_modal:")) {
116
116
  await send({ ephemeral: true }, true);
117
117
  const [ , id ] = customId.split(":");
118
- if (!int.memberPermissions?.has?.([ "BAN_MEMBERS" ])) return send({ content: `❌ You need (Ban Members) in this server to complete this action!` });
118
+ if (!int.memberPermissions?.has?.([ "BAN_MEMBERS" ])) return send({ embeds: [ embed(`❌ You need (Ban Members) in this server to complete this action!`)] });
119
119
  let server = getAppealServer(this.options);
120
- if (!server) return send({ content: `❌ I was unable to find the appeal server!` });
120
+ if (!server) return send({ embeds: [embed(`❌ I was unable to find the appeal server!`)] });
121
121
  let mod = server.members.resolve(member.id) || await server.members.fetch(member.id).catch(e => this._debug(e));
122
- if (!mod) return send({ content: `❌ I was unable to find you in ${server.name}!` });
123
- if (!mod.permissions?.has?.([ "BAN_MEMBERS" ])) return send({ content: `❌ You need (Ban Members) in ${server.name} to complete this action!` });
122
+ if (!mod) return send({ embeds: [embed(`❌ I was unable to find you in ${server.name}!`)] });
123
+ if (!mod.permissions?.has?.([ "BAN_MEMBERS" ])) return send({ embeds: [embed(`❌ You need (Ban Members) in ${server.name} to complete this action!`)] });
124
124
  let isBanned = await server.bans.fetch({ user: id, force: true }).catch(e => this._debug(e));
125
- if (!isBanned) return send({ content: `❌ User (<@${id}>) isn't banned in the main server!` });
125
+ if (!isBanned) return send({ embeds: [embed(`❌ User (<@${id}>) isn't banned in the main server!`)] });
126
126
  return server.bans.remove(id, int.fields.getTextInputValue("reason") ?? `No Reason Provided | By: ${member.user.tag} (${member.id})`)
127
127
  .then(() => {
128
128
  int.message.edit({ components: [ { type: 1, components: [ button({ title: "Unbanned!", style: 3, id: "_ _", disabled: true, emoji: { id: `476629550797684736` } }) ] } ] }).catch(e => this._debug(e));
129
- send({ content: `✅ Successfully unbanned <@${id}> from ${server.name}!` })
129
+ send({ embeds: [embed(`✅ Successfully unbanned <@${id}> from ${server.name}!`)] })
130
130
  })
131
- .catch(e => send({ content: `❌ Unable to unban <@${id} from ${server.name}!`, embeds: [ { title: "ERROR", description: `\`\`\`js\n${e.message ?? e.stack}\`\`\``, color: 0xFF0000 } ] }))
131
+ .catch(e => send({ embeds: [ { title: "ERROR", fields: [ { name: "\u200b", value: `❌ Unable to unban <@${id} from ${server.name}!` } ], description: `\`\`\`js\n${e.message ?? e.stack}\`\`\``, color: 0xFF0000 } ] }))
132
132
  }
133
133
  };
134
134
  };
@@ -185,7 +185,7 @@ module.exports = class Tickets extends base {
185
185
  ...permissions
186
186
  ]
187
187
  }).catch(e => this._debug(e));
188
- 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!` });
188
+ if (!channel) return send({ embeds: [embed(`❌ I was unable to create the ticket channel, if this keeps happening contact one of the staff members via their DMs!`)] });
189
189
  let msg = await channel.send({
190
190
  content: (this.options.ticket?.open || 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.`,
191
191
  embeds: (this.options.ticket?.open || this.options.ticketOpen)?.embeds || [ embed(undefined, { title: `Support will be with you shortly.`, color: 0xF50DE3, guild, footer: { text: `To close the ticket, press the button below` } }) ],
package/lib/v14.js ADDED
@@ -0,0 +1,263 @@
1
+ const { EmbedBuilder: MessageEmbed, InteractionType, PermissionFlagsBits, ChannelType } = require("discord.js"),
2
+ base = require("./base"),
3
+ { de, code, fetchMessages, hasTicket, embed, webhook, getAppealServer } = require("./util"),
4
+ { generate } = require("shortid"),
5
+ { Interactions: { button, modal } } = require("@elara-services/packages");
6
+
7
+ module.exports = class Tickets extends base {
8
+ constructor(options = {}) {
9
+ super(options);
10
+ };
11
+ /**
12
+ * @param {import("discord.js").Interaction} int
13
+ */
14
+ async run(int) {
15
+ if (!int) return;
16
+ if (int.isButton() || int.type === InteractionType.ModalSubmit) {
17
+ let { guild, channel, member, customId } = int,
18
+ category = guild?.channels?.resolve?.(this.options.ticket?.category || this.options.ticketCategory || channel?.parentId);
19
+ if (!guild?.available || !channel || !member || !category) return;
20
+
21
+ /**
22
+ * @param {import("discord.js").InteractionDeferReplyOptions|import("discord.js").InteractionReplyOptions} options
23
+ * @param {boolean} edit
24
+ * @param {boolean} defer
25
+ */
26
+ const send = async (options = {}, defer = false) => {
27
+ if (defer) return int.deferReply(options).catch(e => this._debug(e));
28
+ if (int.replied || int.deferred) return int.editReply(options).catch(e => this._debug(e));
29
+ return int.reply(options).catch(e => this._debug(e));
30
+ };
31
+ switch (customId) {
32
+ case this.prefix: {
33
+ if (this.options?.ticket?.limitOnePerUser && hasTicket({ userId: member.id, guild, token: this.options.encryptToken, prefix: this.options.prefix })) return send({ embeds: [ embed(`❌ You can only create one (${this.options.prefix}) ticket at a time.`, { guild }) ], ephemeral: true })
34
+ if (this.options.support?.ignore?.length) {
35
+ if (this.options.support.ignore.some(c => member.roles?.cache?.has?.(c))) return send({ embeds: [embed(`❌ You can no longer create tickets, if you believe this is a mistake contact one of the staff members.`)], ephemeral: true });
36
+ }
37
+ 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));
38
+ return this.handleCreate({ guild, member, category, send })
39
+ };
40
+
41
+ case `${this.prefix}:close`: {
42
+ if (this.options.support?.canOnlyCloseTickets && !member.permissions.has(PermissionFlagsBits.ManageGuild)) {
43
+ if (!this.getSupportIds.users?.includes?.(member.id)) {
44
+ if (!this.getSupportIds.roles?.some?.(c => member.roles?.cache?.has?.(c))) return send({ ephemeral: true, embeds: [{ author: { name: `Only support staff can close tickets`, iconURL: "https://cdn.discordapp.com/emojis/781955502035697745.gif" }, color: 0xFF0000 }] })
45
+ }
46
+ }
47
+ return send({
48
+ ephemeral: true,
49
+ embeds: [ embed(`🤔 Are you sure you want to close the ticket?`, { color: 0xFF000, guild }) ],
50
+ components: [
51
+ { type: 1, components: [
52
+ button({ title: "Yes, close the ticket!", style: 3, emoji: { id: "807031399563264030" }, id: `${this.prefix}:close:confirm:${code(channel.topic?.split?.("ID: ")?.[1], "d", this.options.encryptToken)}${this.options?.ticket?.closeReason ? `:modal_submit` : ""}` })
53
+ ]
54
+ }]
55
+ })
56
+ }
57
+
58
+ case `${this.prefix}:modal_submit`: {
59
+ 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];
60
+ for (const c of int.fields.components) {
61
+ for (const cc of c.components) {
62
+ if (cc.value && cc.customId) {
63
+ fields.push({ name: cc.customId, value: cc.value });
64
+ if (cc.value.length <= 1024) embed.addFields({ name: cc.customId, value: cc.value });
65
+ else split = true
66
+ }
67
+ }
68
+ }
69
+ if (embed.length >= 6000 || split) {
70
+ return this.handleCreate({
71
+ guild, member, category, send, embeds: fields.map((v, i) => ({
72
+ title: `Form Response: ${v.name}`, color: embed.color, description: v.value,
73
+ author: i === 0 ? { name: member.user.username, iconURL: member.user.displayAvatarURL({ dynamic: true }) } : undefined,
74
+ timestamp: fields.length - 1 === i ? new Date() : undefined,
75
+ footer: fields.length - 1 === i ? { text: `ID: ${member.id}` } : undefined
76
+ }))
77
+ })
78
+ };
79
+ return this.handleCreate({ guild, member, category, send, embeds: [embed] })
80
+ }
81
+ };
82
+ if (customId.startsWith(`${this.prefix}:close:confirm`)) {
83
+ if (customId.includes(`:modal_submit`)) {
84
+ return int.showModal(modal({
85
+ id: `${customId.split(":modal_submit")[0]}:modal_complete`,
86
+ title: "Reason",
87
+ components: [ { type: 1, components: [ { type: 4, custom_id: "reason", label: "Reason", style: 2, min_length: 1, max_length: 1024, required: true, placeholder: "What's the reason for closing the ticket?" } ] } ]
88
+ }))
89
+ }
90
+ await send({ ephemeral: true }, true);
91
+ let reason = "No Reason Provided";
92
+ if (int.type === InteractionType.ModalSubmit) reason = int.fields.getTextInputValue("reason")
93
+ let user = await this.options.client.users.fetch(customId.split("close:confirm:")[1].replace(/:modal_complete/gi, "")).catch(e => this._debug(e));
94
+ if (!user) return send({ embeds: [ embed(`❌ I was unable to fetch the user that opened the ticket.`) ] });
95
+ let messages = await fetchMessages(channel, 5000);
96
+ if (!messages?.length) return send({ embeds: [ embed(`❌ I was unable to close the ticket, I couldn't fetch the messages in this channel.`) ] });
97
+ let closed = await channel.delete(`${member.user.tag} (${member.id}) closed the ticket.`).catch(e => this._debug(e));
98
+ if (!closed) return send({ embeds: [ embed(`❌ I was unable to delete the channel and close the ticket.`) ] });
99
+ return this.closeTicket({ channel, guild, user, member, messages, reason });
100
+ };
101
+
102
+ if (customId.startsWith("unban:")) {
103
+ if (!int.memberPermissions?.has?.(PermissionFlagsBits.BanMembers)) return send({ embeds: [ embed(`❌ You need (Ban Members) in this server to complete this action!`) ], ephemeral: true });
104
+ let server = getAppealServer(this.options);
105
+ if (!server) return send({ embeds: [embed(`❌ I was unable to find the appeal server!`)], ephemeral: true });
106
+ let mod = server.members.resolve(member.id) || await server.members.fetch(member.id).catch(e => this._debug(e));
107
+ if (!mod) return send({ embeds: [embed(`❌ I was unable to find you in ${server.name}!`)], ephemeral: true });
108
+ if (!mod.permissions?.has?.(PermissionFlagsBits.BanMembers)) return send({ embeds: [embed(`❌ You need (Ban Members) in ${server.name} to complete this action!`)], ephemeral: true });
109
+ return int.showModal(modal({
110
+ id: `unban_modal:${customId.split(":")[1]}`,
111
+ title: `Unban From ${server.name}`,
112
+ components: [ { type: 1, components: [ { type: 4, label: "Reason", custom_id: "reason", style: 2, min_length: 1, max_length: 512, required: true, value: `No Reason Provided | By: ${member.user.tag} (${member.id})` } ] } ]
113
+ })).catch(e => this._debug(e));
114
+ }
115
+
116
+ if (customId.startsWith("unban_modal:")) {
117
+ await send({ ephemeral: true }, true);
118
+ const [ , id ] = customId.split(":");
119
+ if (!int.memberPermissions?.has?.(PermissionFlagsBits.BanMembers)) return send({ embeds: [ embed(`❌ You need (Ban Members) in this server to complete this action!`)] });
120
+ let server = getAppealServer(this.options);
121
+ if (!server) return send({ embeds: [embed(`❌ I was unable to find the appeal server!`)] });
122
+ let mod = server.members.resolve(member.id) || await server.members.fetch(member.id).catch(e => this._debug(e));
123
+ if (!mod) return send({ embeds: [embed(`❌ I was unable to find you in ${server.name}!`)] });
124
+ if (!mod.permissions?.has?.(PermissionFlagsBits.BanMembers)) return send({ embeds: [embed(`❌ You need (Ban Members) in ${server.name} to complete this action!`)] });
125
+ let isBanned = await server.bans.fetch({ user: id, force: true }).catch(e => this._debug(e));
126
+ if (!isBanned) return send({ embeds: [embed(`❌ User (<@${id}>) isn't banned in the main server!`)] });
127
+ return server.bans.remove(id, int.fields.getTextInputValue("reason") ?? `No Reason Provided | By: ${member.user.tag} (${member.id})`)
128
+ .then(() => {
129
+ int.message.edit({ components: [ { type: 1, components: [ button({ title: "Unbanned!", style: 3, id: "_ _", disabled: true, emoji: { id: `476629550797684736` } }) ] } ] }).catch(e => this._debug(e));
130
+ send({ embeds: [embed(`✅ Successfully unbanned <@${id}> from ${server.name}!`)] })
131
+ })
132
+ .catch(e => send({ embeds: [ { title: "ERROR", fields: [ { name: "\u200b", value: `❌ Unable to unban <@${id} from ${server.name}!` } ], description: `\`\`\`js\n${e.message ?? e.stack}\`\`\``, color: 0xFF0000 } ] }))
133
+ }
134
+ };
135
+ };
136
+
137
+ /** @private */
138
+ async handleCreate({ guild, member, category, send, embeds = [] } = {}) {
139
+ await send({ ephemeral: true }, true);
140
+ let [ permissions, allow, { appeals }, sendBanReason ] = [
141
+ [],
142
+ [
143
+ PermissionFlagsBits.AddReactions,
144
+ PermissionFlagsBits.AttachFiles,
145
+ PermissionFlagsBits.CreateInstantInvite,
146
+ PermissionFlagsBits.EmbedLinks,
147
+ PermissionFlagsBits.ReadMessageHistory,
148
+ PermissionFlagsBits.ViewChannel,
149
+ PermissionFlagsBits.UseExternalEmojis,
150
+ PermissionFlagsBits.SendMessages
151
+ ],
152
+ this.options ?? {},
153
+ null
154
+ ];
155
+ if (this.getSupportIds.roles.length) for (const sup of this.getSupportIds.roles) {
156
+ let role = guild.roles.resolve(sup);
157
+ if (role) permissions.push({ type: "role", id: sup, allow });
158
+ };
159
+
160
+ if (this.getSupportIds.users.length) for (const uId of this.getSupportIds.users) {
161
+ let member = guild.members.resolve(uId) || await guild.members.fetch(uId).catch((e) => {
162
+ if (e?.stack?.includes?.("Unknown Member")) this.options.support.users = this.options.support.users.filter(c => c !== uId);
163
+ return this._debug(e);
164
+ });
165
+ if (member) permissions.push({ type: "member", id: uId, allow });
166
+ }
167
+ if (appeals?.enabled) {
168
+ let server = getAppealServer(this.options);
169
+ if (server) {
170
+ let ban = await server.bans.fetch({ user: member.id, force: true }).catch(e => this._debug(e));
171
+ if (!ban) return send(
172
+ typeof appeals.embeds?.not_banned === "object" ?
173
+ appeals.embeds.not_banned :
174
+ { embeds: [ embed(`❌ You can't open this ticket due to you not being banned in the main server!`, { guild, color: 0xFF0000 }) ] }
175
+ );
176
+ sendBanReason = {
177
+ embeds: [ embed(ban?.reason ?? "No Reason Provided", { title: "Ban Reason", guild: server }) ],
178
+ components: [
179
+ {
180
+ type: 1, components: [
181
+ button({ id: `unban:${member.id}`, style: 4, title: "Unban", emoji: { name: "🔒" } })
182
+ ]
183
+ }
184
+ ]
185
+ }
186
+ }
187
+ }
188
+ let channel = await guild.channels.create({
189
+ name: `${this.options.prefix}-${generate().slice(0, 5).replace(/-|_/g, "")}`,
190
+ type: ChannelType.GuildText,
191
+ parent: category,
192
+ reason: `Ticket created by: @${member.user.tag} (${member.id})`,
193
+ topic: `ID: ${code(member.id, "e", this.options.encryptToken)}`,
194
+ permissionOverwrites: [
195
+ {
196
+ type: "member",
197
+ id: this.options.client.user.id,
198
+ allow: [
199
+ PermissionFlagsBits.AddReactions,
200
+ PermissionFlagsBits.AttachFiles,
201
+ PermissionFlagsBits.SendMessages,
202
+ PermissionFlagsBits.ReadMessageHistory,
203
+ PermissionFlagsBits.EmbedLinks,
204
+ PermissionFlagsBits.UseExternalEmojis,
205
+ PermissionFlagsBits.ViewChannel,
206
+ PermissionFlagsBits.MentionEveryone
207
+ ]
208
+ },
209
+ {
210
+ type: "member",
211
+ id: member.id,
212
+ allow: [
213
+ PermissionFlagsBits.AddReactions,
214
+ PermissionFlagsBits.AttachFiles,
215
+ PermissionFlagsBits.SendMessages,
216
+ PermissionFlagsBits.ReadMessageHistory,
217
+ PermissionFlagsBits.EmbedLinks,
218
+ PermissionFlagsBits.UseExternalEmojis,
219
+ PermissionFlagsBits.ViewChannel
220
+ ],
221
+ deny: [ PermissionFlagsBits.MentionEveryone ]
222
+ },
223
+ { type: "role", id: guild.id, deny: [ PermissionFlagsBits.ViewChannel ] },
224
+ ...permissions
225
+ ]
226
+ }).catch(e => this._debug(e));
227
+ if (!channel) return send({ embeds: [embed(`❌ I was unable to create the ticket channel, if this keeps happening contact one of the staff members via their DMs!`)] });
228
+ let msg = await channel.send({
229
+ content: (this.options.ticket?.open || 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.`,
230
+ embeds: (this.options.ticket?.open || this.options.ticketOpen)?.embeds || [ embed(undefined, { title: `Support will be with you shortly.`, color: 0xF50DE3, guild, footer: { text: `To close the ticket, press the button below` } }) ],
231
+ components: [ { type: 1, components: [ button({ id: `${this.prefix}:close`, title: `Close Ticket`, style: 4, emoji: { name: "🔒" } }) ] }]
232
+ }).catch(e => this._debug(e));
233
+ if (!msg) return null;
234
+ if (sendBanReason) await channel.send(sendBanReason).catch(e => this._debug(e));
235
+ if (embeds?.length <= 10) for await (const embed of embeds) await channel.send({ embeds: [embed] }).catch(e => this._debug(e));
236
+ if (this.webhookOptions.id && this.webhookOptions.token) webhook(this.webhookOptions)
237
+ .embed(embed(`${de.user} User: ${member.user.toString()} \`@${member.user.tag}\` (${member.id})\n${de.channel} Channel: \`#${channel.name}\` (${channel.id})`, {
238
+ title: `TIcket: Opened`,
239
+ color: 0xFF000,
240
+ footer: { text: `Ticket ID: ${channel.name.split("-")[1]}` },
241
+ guild
242
+ })).send().catch(e => this._debug(e));
243
+ return send({
244
+ embeds: [ embed(channel.toString(), { color: 0xFF000, author: { name: `Ticket Created!`, icon_url: `https://cdn.discordapp.com/emojis/476629550797684736.gif` } }) ],
245
+ components: [{ type: 1, components: [button({ title: "Go to ticket", url: msg.url })] }]
246
+ })
247
+ }
248
+
249
+ async starterMessage(channelId, options) {
250
+ let channel = this.options.client.channels.resolve(channelId);
251
+ if (!channel) return Promise.reject(`No channel found for: ${channelId}`);
252
+ if (!channel.isText()) return Promise.reject(`The channel ID provided isn't a text-based-channel`);
253
+ if (!channel.permissionsFor?.(this.options.client.user.id)?.has?.([
254
+ PermissionFlagsBits.ViewChannel,
255
+ PermissionFlagsBits.SendMessages,
256
+ PermissionFlagsBits.AttachFiles,
257
+ PermissionFlagsBits.EmbedLinks,
258
+ PermissionFlagsBits.ReadMessageHistory
259
+ ])) return Promise.reject(`I'm missing permissions in ${channel.name} (${channelId})`);
260
+ return channel.send({ content: options?.content, files: options?.attachments, embeds: options?.embeds, components: options?.components || [{ type: 1, components: [this.button()] }] })
261
+ .then(() => console.log(`Sent the starter message in ${channel.name} (${channel.id})`))
262
+ };
263
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elara-services/tickets",
3
- "version": "3.0.0",
3
+ "version": "3.1.1",
4
4
  "description": "Helper for tickets",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -10,9 +10,6 @@
10
10
  "scripts": {
11
11
  "lint": "eslint ."
12
12
  },
13
- "optionalDependencies": {
14
- "discord.js": "13.9.0"
15
- },
16
13
  "dependencies": {
17
14
  "@elara-services/packages": "5.0.0",
18
15
  "discord-hook": "2.0.0",