@elara-services/tickets 2.1.2 → 2.3.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 (3) hide show
  1. package/index.d.ts +1 -0
  2. package/index.js +112 -48
  3. package/package.json +1 -2
package/index.d.ts CHANGED
@@ -12,6 +12,7 @@ declare module "@elara-services/tickets" {
12
12
  ticketOpen?: Pick<MessageOptions, "content" | "embeds">
13
13
  appeals?: {
14
14
  enabled: boolean;
15
+ sendBanResults?: boolean;
15
16
  mainserver: {
16
17
  id: string;
17
18
  checkIfBanned: boolean;
package/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  const { Collection, WebhookClient, MessageEmbed } = require("discord.js"),
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
- };
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
+ };
10
10
 
11
11
  module.exports = class Tickets {
12
12
  constructor(options = {}) {
@@ -26,7 +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
30
  webhook() {
31
31
  const { id, token, username, avatar } = this.webhookOptions;
32
32
  return new Webhook(`https://discord.com/api/webhooks/${id}/${token}`, { username, avatar_url: avatar });
@@ -59,15 +59,16 @@ module.exports = class Tickets {
59
59
 
60
60
  case `${this.prefix}:close`: {
61
61
  if (this.options.support?.canOnlyCloseTickets && !member.permissions.has("MANAGE_GUILD")) {
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 } ] }) ];
63
- if (!support.users?.includes?.(member.id)) return staffOnly();
64
- if (support.roles?.length && !support.roles.some(c => member.roles.cache.has(c))) return staffOnly()
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 }] })];
63
+ if (!support.users?.includes?.(member.id)) {
64
+ if (!support.roles?.some?.(c => member.roles.cache.has(c))) return staffOnly()
65
+ }
65
66
  }
66
67
  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" } }] }] })
67
68
  }
68
69
 
69
70
  case `${this.prefix}:modal_submit`: {
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];
71
+ 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];
71
72
  for (const c of int.fields.components) {
72
73
  for (const cc of c.components) {
73
74
  if (cc.value && cc.customId) {
@@ -78,36 +79,82 @@ module.exports = class Tickets {
78
79
  }
79
80
  }
80
81
  if (embed.length >= 6000 || split) {
81
- return this.handleCreate({ guild, member, category, send, embeds: fields.map((v, i) => ({
82
- title: `Form Response: ${v.name}`, color: embed.color, description: v.value,
83
- author: i === 0 ? { name: member.user.username, iconURL: member.user.displayAvatarURL({ dynamic: true }) } : undefined,
84
- timestamp: fields.length - 1 === i ? new Date() : undefined,
85
- footer: fields.length - 1 === i ? { text: `ID: ${member.id}` } : undefined
86
- })) })
82
+ return this.handleCreate({
83
+ guild, member, category, send, embeds: fields.map((v, i) => ({
84
+ title: `Form Response: ${v.name}`, color: embed.color, description: v.value,
85
+ author: i === 0 ? { name: member.user.username, iconURL: member.user.displayAvatarURL({ dynamic: true }) } : undefined,
86
+ timestamp: fields.length - 1 === i ? new Date() : undefined,
87
+ footer: fields.length - 1 === i ? { text: `ID: ${member.id}` } : undefined
88
+ }))
89
+ })
87
90
  };
88
- return this.handleCreate({ guild, member, category, send, embeds: [ embed ]})
91
+ return this.handleCreate({ guild, member, category, send, embeds: [embed] })
89
92
  }
90
93
  };
91
94
  if (customId.startsWith(`${this.prefix}:close:confirm`)) {
92
95
  await send({ ephemeral: true }, true);
93
- let user = await this.options.client.users.fetch(customId.split("close:confirm:")[1]).catch(() => null);
96
+ let user = await this.options.client.users.fetch(customId.split("close:confirm:")[1]).catch(e => this._debug(e));
94
97
  if (!user) return send({ content: `❌ I was unable to fetch the user that opened the ticket.` })
95
98
  let messages = await this.fetchMessages(channel, 5000);
96
99
  if (!messages?.length) return send({ content: `❌ I was unable to close the ticket, I couldn't fetch the messages in this channel.` })
97
100
  let closed = await channel.delete(`${member.user.tag} (${member.id}) closed the ticket.`).catch(e => this._debug(e));
98
101
  if (!closed) return send({ content: `❌ I was unable to delete the channel & close the ticket.` })
99
102
  return this.closeTicket({ channel, guild, user, member, messages });
103
+ };
104
+
105
+ if (customId.startsWith("unban:")) {
106
+ if (!int.memberPermissions?.has?.([ "BAN_MEMBERS" ])) return send({ content: `❌ You need (Ban Members) in this server to complete this action!`, ephemeral: true });
107
+ let server = this.getAppealServer();
108
+ if (!server) return send({ content: `❌ I was unable to find the appeal server!`, ephemeral: true });
109
+ let mod = server.members.resolve(member.id) || await server.members.fetch(member.id).catch(e => this._debug(e));
110
+ if (!mod) return send({ content: `❌ I was unable to find you in ${server.name}!`, ephemeral: true });
111
+ if (!mod.permissions?.has?.([ "BAN_MEMBERS" ])) return send({ content: `❌ You need (Ban Members) in ${server.name} to complete this action!`, ephemeral: true });
112
+ return int.showModal(modal({
113
+ id: `unban_modal:${customId.split(":")[1]}`,
114
+ title: `Unban From ${server.name}`,
115
+ 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})` } ] } ]
116
+ })).catch(e => this._debug(e));
117
+ }
118
+
119
+ if (customId.startsWith("unban_modal:")) {
120
+ await send({ ephemeral: true }, true);
121
+ const [ , id ] = customId.split(":");
122
+ if (!int.memberPermissions?.has?.([ "BAN_MEMBERS" ])) return send({ content: `❌ You need (Ban Members) in this server to complete this action!` });
123
+ let server = this.getAppealServer();
124
+ if (!server) return send({ content: `❌ I was unable to find the appeal server!` });
125
+ let mod = server.members.resolve(member.id) || await server.members.fetch(member.id).catch(e => this._debug(e));
126
+ if (!mod) return send({ content: `❌ I was unable to find you in ${server.name}!` });
127
+ if (!mod.permissions?.has?.([ "BAN_MEMBERS" ])) return send({ content: `❌ You need (Ban Members) in ${server.name} to complete this action!` });
128
+ let isBanned = await server.bans.fetch({ user: id, force: true }).catch(e => this._debug(e));
129
+ if (!isBanned) return send({ content: `❌ User (<@${id}>) isn't banned in the main server!` });
130
+ return server.bans.remove(id, int.fields.getTextInputValue("reason") ?? `No Reason Provided | By: ${member.user.tag} (${member.id})`)
131
+ .then(() => {
132
+ int.message.edit({ components: [ { type: 1, components: [ button({ title: "Unbanned!", style: 3, id: "_ _", disabled: true, emoji: { id: `476629550797684736` } }) ] } ] }).catch(e => this._debug(e));
133
+ send({ content: `✅ Successfully unbanned <@${id}> from ${server.name}!` })
134
+ })
135
+ .catch(e => send({ content: `❌ Unable to unban <@${id} from ${server.name}!`, embeds: [ { title: "ERROR", description: `\`\`\`js\n${e.message ?? e.stack}\`\`\``, color: 0xFF0000 } ] }))
100
136
  }
101
137
  };
102
138
  };
139
+ /**
140
+ * @returns {import("discord.js").Guild|null}
141
+ */
142
+ getAppealServer() {
143
+ const { appeals, client } = this.options;
144
+ if (!appeals?.enabled || !appeals?.mainserver?.checkIfBanned) return null;
145
+ let server = client.guilds.resolve(appeals.mainserver?.id);
146
+ if (!server?.available) return null;
147
+ return server;
148
+ }
103
149
 
104
150
  /** @private */
105
151
  async handleCreate({ guild, member, category, send, embeds = [] } = {}) {
106
152
  await send({ ephemeral: true }, true);
107
- let [ supportIds, permissions, allow, { appeals } ] = [
153
+ let [supportIds, permissions, allow, { appeals }, sendBanReason] = [
108
154
  this.getSupportIds(), [],
109
155
  ["ADD_REACTIONS", "ATTACH_FILES", "CREATE_INSTANT_INVITE", "EMBED_LINKS", "READ_MESSAGE_HISTORY", "VIEW_CHANNEL", "USE_EXTERNAL_EMOJIS", "SEND_MESSAGES"],
110
- this.options ?? {}
156
+ this.options ?? {},
157
+ null
111
158
  ];
112
159
  if (supportIds.roles.length) for (const sup of supportIds.roles) {
113
160
  let role = guild.roles.resolve(sup);
@@ -122,25 +169,41 @@ module.exports = class Tickets {
122
169
  if (member) permissions.push({ type: "member", id: uId, allow });
123
170
  }
124
171
  if (appeals?.enabled) {
125
- if (appeals.mainserver?.id && appeals.mainserver.checkIfBanned) {
126
- let server = this.options.client.guilds.resolve(appeals.mainserver.id);
127
- if (server?.available) {
128
- let isBanned = await server.bans.fetch({ user: member.id, force: true }).catch(() => null);
129
- if (!isBanned) return send(
130
- typeof appeals.embeds?.not_banned === "object" ?
131
- appeals.embeds.not_banned :
132
- {
133
- embeds: [
134
- {
135
- author: { name: guild.name, icon_url: guild.iconURL({ dynamic: true }) },
136
- title: "INFO",
137
- description: `❌ You can't open this ticket due to you not being banned in the main server!`,
138
- color: 0xFF0000,
139
- timestamp: new Date()
140
- }
141
- ]
142
- }
143
- )
172
+ let server = this.getAppealServer();
173
+ if (server) {
174
+ let ban = await server.bans.fetch({ user: member.id, force: true }).catch(e => this._debug(e));
175
+ if (!ban) return send(
176
+ typeof appeals.embeds?.not_banned === "object" ?
177
+ appeals.embeds.not_banned :
178
+ {
179
+ embeds: [
180
+ {
181
+ author: { name: guild.name, icon_url: guild.iconURL({ dynamic: true }) },
182
+ title: "INFO",
183
+ description: `❌ You can't open this ticket due to you not being banned in the main server!`,
184
+ color: 0xFF0000,
185
+ timestamp: new Date()
186
+ }
187
+ ]
188
+ }
189
+ );
190
+ sendBanReason = {
191
+ embeds: [
192
+ {
193
+ author: { name: server.name, iconURL: server.iconURL({ dynamic: true }) },
194
+ title: "Ban Reason",
195
+ description: ban.reason ?? "No Reason Provided?",
196
+ color: 0xFF0000,
197
+ timestamp: new Date()
198
+ }
199
+ ],
200
+ components: [
201
+ {
202
+ type: 1, components: [
203
+ button({ id: `unban:${member.id}`, style: 4, title: "Unban", emoji: { name: "🔒" } })
204
+ ]
205
+ }
206
+ ]
144
207
  }
145
208
  }
146
209
  }
@@ -167,7 +230,8 @@ module.exports = class Tickets {
167
230
  components: [{ type: 1, components: [{ type: 2, custom_id: `${this.prefix}:close`, label: "Close Ticket", style: 4, emoji: { name: "🔒" } }] }]
168
231
  }).catch(e => this._debug(e));
169
232
  if (!msg) return null;
170
- if (embeds?.length <= 10) for await (const embed of embeds) await channel.send({ embeds: [ embed ] }).catch(e => this._debug(e));
233
+ if (sendBanReason) await channel.send(sendBanReason).catch(e => this._debug(e));
234
+ if (embeds?.length <= 10) for await (const embed of embeds) await channel.send({ embeds: [embed] }).catch(e => this._debug(e));
171
235
  if (this.webhookOptions.id && this.webhookOptions.token) this.webhook()
172
236
  .embed({
173
237
  author: { name: guild.name, icon_url: guild.iconURL({ dynamic: true }) },
@@ -178,8 +242,8 @@ module.exports = class Tickets {
178
242
  footer: { text: `Ticket ID: ${channel.name.split("-")[1]}` },
179
243
  }).send().catch(e => this._debug(e));
180
244
  return send({
181
- embeds: [ { author: { name: `Ticket Created!`, icon_url: `https://cdn.discordapp.com/emojis/476629550797684736.gif` }, description: channel.toString(), color: 0xFF000 } ],
182
- components: [ { type: 1, components: [ button({ title: "Go to ticket", url: msg.url }) ] } ]
245
+ embeds: [{ author: { name: `Ticket Created!`, icon_url: `https://cdn.discordapp.com/emojis/476629550797684736.gif` }, description: channel.toString(), color: 0xFF000 }],
246
+ components: [{ type: 1, components: [button({ title: "Go to ticket", url: msg.url })] }]
183
247
  })
184
248
  }
185
249
 
@@ -193,7 +257,7 @@ module.exports = class Tickets {
193
257
  * @param {import("@elara-services/packages").Modal['components']} [options.components]
194
258
  */
195
259
  modal(options = { title: "", components: [] }) {
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 } ] } ] })
260
+ 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 }] }] })
197
261
  }
198
262
 
199
263
  async starterMessage(channelId, options) {
@@ -254,7 +318,7 @@ module.exports = class Tickets {
254
318
  str.push(content)
255
319
  };
256
320
  if (message.embeds?.length) {
257
- let arr = [ `<discord-embeds slot="embeds">` ];
321
+ let arr = [`<discord-embeds slot="embeds">`];
258
322
  for (const embed of message.embeds) {
259
323
  let emb = [
260
324
  `<discord-embed slot="embed" ${embed.thumbnail?.url ? `thumbnail="${embed.thumbnail.url}"` : ""} ${embed.image?.url ? `image="${embed.image.url}"` : ""} ${embed.author ? `${embed.author.name ? `author-name="${embed.author.name}"` : ""} ${embed.author.iconURL ? `author-image="${embed.author.iconURL}"` : ""} ${embed.author.url ? `author-url="${embed.author.url}"` : ""}` : ""} ${embed.title ? `embed-title="${embed.title}"` : ""}${embed.color ? `color="${embed.hexColor}"` : ""}>`
@@ -290,7 +354,7 @@ module.exports = class Tickets {
290
354
  if (row.length >= 2) str.push(...row, `</discord-attachments>`)
291
355
  }
292
356
  if (message.attachments.size) str.push(message.attachments.map((c) => `<a href="${c.proxyURL}">${c.name}</a>`).join("<br>"))
293
- str.push(`${message.content?.length ? `<br><br>` : "" }<code style="background-color: #36393e; color: white;">ID: ${message.id}</code>`)
357
+ str.push(`${message.content?.length ? `<br><br>` : ""}<code style="background-color: #36393e; color: white;">ID: ${message.id}</code>`)
294
358
  return [...str, `</discord-message>`].join(" ");
295
359
  }),
296
360
  "</discord-messages>"].join(" ");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elara-services/tickets",
3
- "version": "2.1.2",
3
+ "version": "2.3.0",
4
4
  "description": "Helper for tickets",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -12,7 +12,6 @@
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
  }