@elara-services/tickets 4.1.0 → 4.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/README.md CHANGED
@@ -44,6 +44,7 @@ const { Client } = require("discord.js"),
44
44
  ]
45
45
  },
46
46
  webhook: { // [OPTIONAL]
47
+ channelId: "", // The channel ID to use for the logs (can be used instead of using a webhook id, token and threadId)
47
48
  id: "", // The webhook ID for this ticket's open and closed logs.
48
49
  token: "", // The webhook Token for this ticket's open and closed logs.
49
50
  username: "Webhook Username Here", // The webhook username for this ticket's logs.
package/index.d.ts CHANGED
@@ -4,12 +4,23 @@ declare module "@elara-services/tickets" {
4
4
 
5
5
  export type Langs = "en-US";
6
6
 
7
+ export interface TicketModalQuestion {
8
+ label: string;
9
+ style: 1 | 2;
10
+ placeholder?: string;
11
+ value?: string;
12
+ required?: boolean;
13
+ min_length?: number;
14
+ max_length?: number;
15
+ }
16
+
7
17
  export interface TicketOptions {
8
18
  client: Client;
9
19
  prefix: string;
10
20
  encryptToken: string;
11
21
  lang?: Langs;
12
22
  debug?: boolean;
23
+ suppressPatreon?: boolean;
13
24
  appeals?: {
14
25
  enabled?: boolean;
15
26
  sendBanResults?: boolean;
@@ -24,17 +35,10 @@ declare module "@elara-services/tickets" {
24
35
  modal?: {
25
36
  enabled?: boolean;
26
37
  title?: string;
27
- questions?: {
28
- label: string;
29
- style: 1 | 2;
30
- placeholder?: string;
31
- value?: string;
32
- required?: boolean;
33
- min_length?: number;
34
- max_length?: number;
35
- }[]
38
+ questions?: TicketModalQuestion[]
36
39
  };
37
40
  webhook?: {
41
+ channelId?: string;
38
42
  id?: string;
39
43
  token?: string;
40
44
  threadId?: string;
@@ -32,6 +32,7 @@ module.exports = {
32
32
  NO_CHANNEL_CREATE: `${x} I was unable to create the ticket channel, if this keeps happening contact one of the staff members via their DMs!`,
33
33
  NO_CHANNEL_DELETE: `${x} I was unable to delete the channel and close the ticket.`,
34
34
  NO_BAN_PERMS_USER: `${x} You need (Ban Members) in this server to complete this action!`,
35
+ TRANSCRIPT_VIEW: `View Transcript`,
35
36
  TICKET_BLOCKED: `${x} You can't create tickets, if you believe this is a mistake contact one of the staff members.`,
36
37
  FORM_RESPONSES: "Form Responses",
37
38
  TOTAL_MESSAGES: "Total Messages",
package/lib/base.js CHANGED
@@ -1,10 +1,15 @@
1
- const { getWebhookInfo, displayMessages, de, webhook, getString } = require("./util");
1
+ const { getWebhookInfo, displayMessages, de, getString, generateHTMLPage } = require("./util");
2
2
  const {
3
3
  Interactions: { button },
4
+ ButtonStyle,
4
5
  } = require("@elara-services/packages");
5
- const { is, status, isV13 } = require("@elara-services/utils");
6
+ const { is, status, isV13, embedComment, log } = require("@elara-services/utils");
6
7
  const { WebhookClient } = require("discord.js");
8
+ const { name, version, funding } = require("../package.json");
7
9
  const required = ["client", "prefix", "encryptToken"];
10
+ const moment = require("moment");
11
+
12
+ let showed = false;
8
13
 
9
14
  module.exports = class Tickets {
10
15
  /**
@@ -20,6 +25,10 @@ module.exports = class Tickets {
20
25
  }
21
26
  }
22
27
  this.options = options;
28
+ if (!showed && options.suppressPatreon !== true) {
29
+ log(`[${name}, ${version}]: Thanks for using the package, if you want to support the packages created: ${funding.map((c) => c.url).join(" or ")}`);
30
+ showed = true;
31
+ }
23
32
  }
24
33
 
25
34
  get prefix() {
@@ -140,6 +149,36 @@ module.exports = class Tickets {
140
149
  emoji: options.emoji,
141
150
  });
142
151
  }
152
+
153
+ /**
154
+ *
155
+ * @param {import("discord.js").ButtonInteraction} int
156
+ */
157
+ async handleTicketButton(int) {
158
+ await int.deferUpdate().catch(() => null);
159
+ const msg = await int.message.fetch(true).catch(() => null);
160
+ const file = msg?.attachments.find((c) => c.name.endsWith(".txt"));
161
+ if (!file) {
162
+ return int
163
+ .followUp({
164
+ ...embedComment(`Unable to find the .txt file.`),
165
+ ephemeral: true,
166
+ })
167
+ .catch(() => null);
168
+ }
169
+ const components = int.message.components.slice(0, 1);
170
+ components.push({
171
+ type: 1,
172
+ components: [
173
+ button({
174
+ title: `${this.str("TRANSCRIPT_VIEW")} (${moment().format("MM DD YYYY h:m:s")})`,
175
+ emoji: { id: "1059556038761787433" },
176
+ url: `https://view.elara.workers.dev/tickets?url=${file.url}`,
177
+ }),
178
+ ],
179
+ });
180
+ return int.editReply({ components }).catch(console.log);
181
+ }
143
182
  /**
144
183
  * @param {object} opts
145
184
  * @param {import("discord.js").GuildMember} opts.member
@@ -149,7 +188,7 @@ module.exports = class Tickets {
149
188
  * @param {string} opts.reason
150
189
  */
151
190
  async closeTicket({ member, messages, channel, guild, user, reason } = {}) {
152
- const { id, token, username, avatar: avatarURL, threadId } = this.webhookOptions;
191
+ const { id, token, username, avatar: avatarURL, threadId } = await this.webhookOptions;
153
192
  if (!id || !token) {
154
193
  return;
155
194
  }
@@ -187,24 +226,26 @@ module.exports = class Tickets {
187
226
  name: `${this.str("TRANSCRIPT")}.txt`,
188
227
  attachment: Buffer.from(displayMessages(channel, messages.reverse(), channel.name.split("-")[1], this.options.prefix, (name) => this.str(name, this?.options?.lang))),
189
228
  },
229
+ {
230
+ name: `${this.str("TRANSCRIPT")}.html`,
231
+ attachment: Buffer.from(generateHTMLPage(channel, messages.reverse(), channel.name.split("-")[1], this.options.prefix, (name) => this.str(name, this?.options?.lang))),
232
+ },
190
233
  ],
191
- })
192
- .then((m) => {
193
- let components = [
234
+ components: [
194
235
  {
195
- type: 2,
196
- style: 5,
197
- label: this.str("TRANSCRIPT"),
198
- emoji: { id: "792290922749624320" },
199
- url: `https://view.elara.workers.dev/tickets?url=${Array.isArray(m.attachments) ? m.attachments?.[0]?.url : m.attachments?.first?.()?.url ?? "URL_NOT_FOUND"}`,
236
+ type: 1,
237
+ components: [
238
+ button({
239
+ id: `transcript`,
240
+ label: this.str("TRANSCRIPT"),
241
+ emoji: { id: `792290922749624320` },
242
+ style: ButtonStyle.SECONDARY,
243
+ }),
244
+ ],
200
245
  },
201
- ];
202
- embeds[0].description += `\n${de.transcript} ${this.str("TRANSCRIPT")}: [${this.str("VIEW_HERE")}](${components[0].url})`;
203
- webhook(this.webhookOptions)
204
- .embeds(embeds)
205
- .button({ type: 1, components })
206
- .edit(m.id)
207
- .catch((e) => this._debug(e));
246
+ ],
247
+ })
248
+ .then((m) => {
208
249
  if (user) {
209
250
  user.send({
210
251
  embeds: [
@@ -229,7 +270,26 @@ module.exports = class Tickets {
229
270
  },
230
271
  },
231
272
  ],
232
- components: [{ type: 1, components }],
273
+ components: [
274
+ {
275
+ type: 1,
276
+ components: [
277
+ {
278
+ type: 2,
279
+ style: 5,
280
+ label: this.str("TRANSCRIPT"),
281
+ emoji: { id: "792290922749624320" },
282
+ url: `https://view.elara.workers.dev/tickets?url=${m.attachments?.find?.((c) => c.filename.includes(".txt"))?.url}`,
283
+ },
284
+ ],
285
+ },
286
+ ],
287
+ files: [
288
+ {
289
+ name: `${this.str("TRANSCRIPT")}.html`,
290
+ attachment: Buffer.from(generateHTMLPage(channel, messages.reverse(), channel.name.split("-")[1], this.options.prefix, (name) => this.str(name, this?.options?.lang))),
291
+ },
292
+ ],
233
293
  }).catch((e) => this._debug(e));
234
294
  }
235
295
  })
package/lib/html.js ADDED
@@ -0,0 +1,68 @@
1
+ exports.discordView = function (messages) {
2
+ return `<!DOCTYPE html>
3
+ <head>
4
+ <title>Discord Viewer</title>
5
+ <link href="https://cdn.discordapp.com/emojis/880708306761564220.png" rel="icon" />
6
+ <script>
7
+ window.onload = _ => {
8
+ let doc = document.getElementById("messages");
9
+ if (doc.innerHTML.includes('new_timestamp="')) {
10
+ let matches = doc.innerHTML.match(new RegExp(\`new_timestamp=".*?"\`, "gi"))
11
+ if (matches) {
12
+ for (const match of matches) {
13
+ document.getElementById("messages").innerHTML = doc.innerHTML.replace(match, \`timestamp="\${new Date(match.split("new_timestamp=\\"")[1].replace('"', "")).toLocaleString()}"\`)
14
+ }
15
+ }
16
+ }
17
+ setTimeout(_ => hidePreloader(), 1000)
18
+ }
19
+ function hidePreloader() {
20
+ document.getElementById("load").style.transitionDuration = "0.5s"
21
+ document.getElementById("load").style.pointerEvents = "none"
22
+ document.getElementById("load").style.opacity = 0
23
+ document.getElementById("load").style.zIndex = -1
24
+ }
25
+ </script>
26
+ <script type="module" src="https://unpkg.com/@skyra/discord-components-core" async></script>
27
+ <style>
28
+ /* Preloader styles. */
29
+ #load {
30
+ position: fixed;
31
+ top: 0;
32
+ left: 0;
33
+ right: 0;
34
+ bottom: 0;
35
+ background: black;
36
+ z-index: 9999;
37
+ cursor: progress;
38
+ }
39
+
40
+ /* Preloader image. */
41
+ #icon {
42
+ width: 200px;
43
+ height: 200px;
44
+ position: absolute;
45
+ background-color: transparent;
46
+ left: 50%;
47
+ top: 50%;
48
+ background-image: url(https://cdn.discordapp.com/emojis/634127148696862753.gif?v=1);
49
+ background-repeat: no-repeat;
50
+ background-position: center;
51
+ margin: -100px 0 0 -100px;
52
+ }
53
+ </style>
54
+ </head>
55
+
56
+ <body style="background: #36393e;">
57
+ <div id="load">
58
+ <div id="icon"></div>
59
+ <br>
60
+ <h3 style="min-height: 110%; display: flex; justify-content: center; align-items: center;">Loading, one moment please.</h3>
61
+ </div>
62
+ <discord-messages id="messages">${messages}</discord-messages>
63
+ <br>
64
+ <br>
65
+ </body>
66
+
67
+ </html>`;
68
+ };
package/lib/util.js CHANGED
@@ -3,9 +3,10 @@ const {
3
3
  Interactions: { modal },
4
4
  } = require("@elara-services/packages"),
5
5
  { Collection, version } = require("discord.js"),
6
- { DiscordWebhook: Webhook } = require("@elara-services/webhooks"),
6
+ { DiscordWebhook: Webhook, Webhook: BotHook } = require("@elara-services/webhooks"),
7
7
  defLang = require("../languages/en-US"),
8
8
  pack = require("../package.json");
9
+ const { discordView } = require("./html");
9
10
 
10
11
  exports.getString = (name, lang = "en-US") => {
11
12
  if (!lang) {
@@ -205,6 +206,13 @@ exports.displayMessages = (channel, messages = [], ticketID, type, str) => {
205
206
  ].join(" ");
206
207
  };
207
208
 
209
+ /**
210
+ * @description Generates an HTML page string to use.
211
+ */
212
+ exports.generateHTMLPage = (channel, messages = [], ticketID, type, str) => {
213
+ return discordView(exports.displayMessages(channel, messages, ticketID, type, str));
214
+ };
215
+
208
216
  exports.defs = {
209
217
  modals: {
210
218
  reason: (customId, str) =>
@@ -336,7 +344,22 @@ exports.webhook = (options) => {
336
344
  });
337
345
  };
338
346
 
339
- exports.getWebhookInfo = (options, username = "Tickets") => {
347
+ /**
348
+ * @param {import("@elara-services/tickets").TicketOptions} options
349
+ * @param {string} username
350
+ */
351
+ exports.getWebhookInfo = async (options, username = "Tickets") => {
352
+ if (options.webhook?.channelId) {
353
+ const channel = options.client.channels.resolve(options.webhook.channelId);
354
+ if (channel) {
355
+ const hook = await new BotHook(options.client.token).fetch(channel.isThread() ? channel.parentId : channel.id);
356
+ if (hook) {
357
+ options.webhook.id = hook.id;
358
+ options.webhook.token = hook.token;
359
+ options.webhook.threadId = channel.isThread() ? channel.id : undefined;
360
+ }
361
+ }
362
+ }
340
363
  return {
341
364
  id: options.webhook?.id,
342
365
  token: options.webhook?.token,
package/lib/v13.js CHANGED
@@ -32,6 +32,9 @@ module.exports = class Tickets extends base {
32
32
  }
33
33
  }
34
34
  if (int.isButton() || int.isModalSubmit()) {
35
+ if (int.customId.startsWith("transcript")) {
36
+ return this.handleTicketButton(int);
37
+ }
35
38
  let { guild, channel, member, customId } = int,
36
39
  category = guild?.channels?.resolve?.(this.options.ticket?.category || channel?.parentId);
37
40
  if (!guild?.available || !channel || !member || !category) {
@@ -510,8 +513,9 @@ module.exports = class Tickets extends base {
510
513
  .catch((e) => this._debug(e));
511
514
  }
512
515
  }
513
- if (this.webhookOptions.id && this.webhookOptions.token) {
514
- webhook(this.webhookOptions)
516
+ const webOpt = await this.webhookOptions;
517
+ if (webOpt.id && webOpt.token) {
518
+ webhook(webOpt)
515
519
  .embed(
516
520
  embed(`${de.user} ${this.str("USER")}: ${member.user.toString()} \`@${member.user.tag}\` (${member.id})\n${de.channel} ${this.str("CHANNEL")}: \`#${channel.name}\` (${channel.id})`, {
517
521
  title: this.str("OPEN_TICKET_TITLE"),
package/lib/v14.js CHANGED
@@ -28,6 +28,9 @@ module.exports = class Tickets extends base {
28
28
  }
29
29
  }
30
30
  if (int.isButton() || int.type === InteractionType.ModalSubmit) {
31
+ if (int.customId.startsWith("transcript")) {
32
+ return this.handleTicketButton(int);
33
+ }
31
34
  let { guild, channel, member, customId } = int,
32
35
  category = guild?.channels?.resolve?.(this.options.ticket?.category || channel?.parentId);
33
36
  if (!guild?.available || !channel || !member || !category) {
@@ -506,8 +509,9 @@ module.exports = class Tickets extends base {
506
509
  .catch((e) => this._debug(e));
507
510
  }
508
511
  }
509
- if (this.webhookOptions.id && this.webhookOptions.token) {
510
- webhook(this.webhookOptions)
512
+ const webOpt = await this.webhookOptions;
513
+ if (webOpt.id && webOpt.token) {
514
+ webhook(webOpt)
511
515
  .embed(
512
516
  embed(`${de.user} ${this.str("USER")}: ${member.user.toString()} \`@${member.user.tag}\` (${member.id})\n${de.channel} ${this.str("CHANNEL")}: \`#${channel.name}\` (${channel.id})`, {
513
517
  title: this.str("OPEN_TICKET_TITLE"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elara-services/tickets",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "Helper for tickets",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -11,15 +11,25 @@
11
11
  "repository": {
12
12
  "url": "https://github.com/elara-bots/npm/tree/main/tickets"
13
13
  },
14
+ "funding": [
15
+ {
16
+ "type": "patreon",
17
+ "url": "https://patreon.com/elaraservices"
18
+ },
19
+ {
20
+ "type": "paypal",
21
+ "url": "https://paypal.me/superchiefyt"
22
+ }
23
+ ],
14
24
  "scripts": {
15
25
  "lint": "eslint .",
16
26
  "pf": "prettier --write **/**/*.js",
17
27
  "pc": "prettier --check **/**/*.js"
18
28
  },
19
29
  "dependencies": {
20
- "@elara-services/packages": "^6.0.8",
21
- "@elara-services/utils": "^1.3.1",
22
- "@elara-services/webhooks": "^2.1.10"
30
+ "@elara-services/packages": "^6.0.9",
31
+ "@elara-services/utils": "^1.3.25",
32
+ "@elara-services/webhooks": "^2.1.18"
23
33
  },
24
34
  "devDependencies": {
25
35
  "eslint": "8.20.0",