@elara-services/tickets 1.6.0 → 2.0.2
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 +64 -43
- package/index.d.ts +39 -9
- package/index.js +331 -171
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -6,52 +6,73 @@ This is a customizable ticket system that uses interactions and discord.js
|
|
|
6
6
|
# Getting Started
|
|
7
7
|
```js
|
|
8
8
|
const { Client } = require("discord.js"),
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
ticketOpen: {
|
|
27
|
-
content: "", // The content of the ticket message once it gets created, use "%user%" or "%server%" for the user mention or server name
|
|
28
|
-
embeds: [], // View https://discord.com/developers/docs/resources/channel#embed-object
|
|
9
|
+
Tickets = require(`@elara-services/tickets`),
|
|
10
|
+
client = new Client({ intents: ["GUILDS"] }),
|
|
11
|
+
tickets = new Tickets({
|
|
12
|
+
client,
|
|
13
|
+
prefix: "support", // This is what is used for interactions (buttons) and the start of the channel name
|
|
14
|
+
ticketCategory: "", // When tickets get created they get created in this category, (OPTIONAL) - Default is the category ID for the starter message's channel.
|
|
15
|
+
encryptToken: "ASB!@#$%^&*(B", // This is to encrypt/decrypt the user IDs in the channel topic, to avoid non-staff from seeing who's ticket it is
|
|
16
|
+
debug: true, // Only use if you want errors logged.
|
|
17
|
+
ticketOpen: {
|
|
18
|
+
content: "", // The content of the ticket message once it gets created, use "%user%" or "%server%" for the user mention or server name
|
|
19
|
+
embeds: [], // View https://discord.com/developers/docs/resources/channel#embed-object
|
|
20
|
+
},
|
|
21
|
+
appeals: { // OPTIONAL
|
|
22
|
+
enabled: true, // If the appeals server checks should be enabled.
|
|
23
|
+
mainserver: {
|
|
24
|
+
id: "", // The main server's id
|
|
25
|
+
checkIfBanned: true // Check if the user is banned in the main server, if not they can't open a ticket.
|
|
29
26
|
},
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
mainserver: {
|
|
33
|
-
id: "", // The main server's id
|
|
34
|
-
checkIfBanned: true // Check if the user is banned in the main server, if not they can't open a ticket.
|
|
35
|
-
},
|
|
36
|
-
embeds: {
|
|
37
|
-
not_banned: {} // The embeds, content and components for the not banned message.
|
|
38
|
-
}
|
|
27
|
+
embeds: {
|
|
28
|
+
not_banned: {} // The embeds, content and components for the not banned message.
|
|
39
29
|
}
|
|
40
|
-
}
|
|
30
|
+
},
|
|
41
31
|
|
|
42
|
-
|
|
32
|
+
modal: {
|
|
33
|
+
enabled: true, // If this form should be enabled / shown
|
|
34
|
+
title: "", // The top title of the modal / form
|
|
35
|
+
questions: [
|
|
36
|
+
{
|
|
37
|
+
label: "[NAME]", // The name of this question
|
|
38
|
+
style: 2, // 1 (SHORT ANSWER) | 2 (LONG ANSWER)
|
|
39
|
+
placeholder: "", // The placeholder of the question
|
|
40
|
+
value: "", // The default value for the question
|
|
41
|
+
required: true, // If the question should be required
|
|
42
|
+
min_length: 20, // The min length for the question response
|
|
43
|
+
max_length: 4000, // The max length for the question response
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
webhook: {
|
|
48
|
+
id: "", // 'webhookId' support will be removed in the next major version
|
|
49
|
+
token: "", // 'webhookToken' support will be removed in the next major version
|
|
50
|
+
username: "Webhook Username Here", // 'webhookUsername' support will be removed in the next major version
|
|
51
|
+
avatar: "", // 'webhookAvatar' support will be removed in the next major version
|
|
52
|
+
},
|
|
53
|
+
support: {
|
|
54
|
+
roles: [ // 'supportRoleIds' support will be removed in the next major version
|
|
55
|
+
"123456789"
|
|
56
|
+
],
|
|
57
|
+
users: [ // 'supportUserIds' support will be removed in the next major version
|
|
58
|
+
"123456789"
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
})
|
|
43
62
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
client.on("interactionCreate", (int) => tickets.run(int))
|
|
64
|
+
|
|
65
|
+
client.on("ready", () => {
|
|
66
|
+
console.log(`Client is ready`);
|
|
67
|
+
// Use it as "node bot.js --starter" or just create a command in your bot to manage the starter message
|
|
68
|
+
if (process.argv.find(c => c === "--starter")) {
|
|
69
|
+
return tickets.starterMessage(`HELP OR SUPPORT CHANNEL ID HERE`, {
|
|
70
|
+
embeds: [
|
|
71
|
+
{ title: "Support Tickets", description: `Click the button below to create a support ticket!`, color: 0xFF000 }
|
|
72
|
+
]
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
});
|
|
55
76
|
|
|
56
|
-
|
|
77
|
+
client.login("BOT TOKEN HERE")
|
|
57
78
|
```
|
package/index.d.ts
CHANGED
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
declare module "@elara-services/tickets" {
|
|
2
2
|
|
|
3
|
-
import { Client, MessageOptions, GuildMember, Guild, User, TextBasedChannel, Message, Interaction } from "discord.js";
|
|
3
|
+
import { Client, MessageOptions, GuildMember, Guild, User, TextBasedChannel, Message, Interaction, ModalOptions } from "discord.js";
|
|
4
4
|
import Webhook from "discord-hook";
|
|
5
5
|
|
|
6
6
|
export interface TicketOptions {
|
|
7
7
|
client: Client;
|
|
8
8
|
prefix: string;
|
|
9
|
+
debug?: boolean;
|
|
9
10
|
encryptToken: string;
|
|
10
11
|
ticketCategory?: string;
|
|
11
|
-
|
|
12
|
-
webhookId?: string;
|
|
13
|
-
webhookToken?: string;
|
|
14
|
-
webhookUsername?: string;
|
|
15
|
-
webhookAvatar?: string;
|
|
16
|
-
supportRoleIds?: string[];
|
|
17
|
-
supportUserIds?: string[];
|
|
18
12
|
ticketOpen?: Pick<MessageOptions, "content" | "embeds">
|
|
19
13
|
appeals?: {
|
|
20
14
|
enabled: boolean;
|
|
@@ -25,7 +19,43 @@ declare module "@elara-services/tickets" {
|
|
|
25
19
|
embeds?: {
|
|
26
20
|
not_banned: Pick<MessageOptions, "content" | "embeds" | "components">
|
|
27
21
|
}
|
|
28
|
-
}
|
|
22
|
+
};
|
|
23
|
+
modal?: {
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
title?: string;
|
|
26
|
+
questions?: {
|
|
27
|
+
label: string;
|
|
28
|
+
style: 1 | 2;
|
|
29
|
+
placeholder?: string;
|
|
30
|
+
value?: string;
|
|
31
|
+
required?: boolean;
|
|
32
|
+
min_length?: number;
|
|
33
|
+
max_length?: number;
|
|
34
|
+
}[]
|
|
35
|
+
};
|
|
36
|
+
webhook?: {
|
|
37
|
+
id?: string;
|
|
38
|
+
token?: string;
|
|
39
|
+
username?: string;
|
|
40
|
+
avatar?: string
|
|
41
|
+
};
|
|
42
|
+
support?: {
|
|
43
|
+
roles?: string[];
|
|
44
|
+
users?: string[];
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** @deprecated Use 'webhook.id' */
|
|
48
|
+
webhookId?: string;
|
|
49
|
+
/** @deprecated Use 'webhook.token' */
|
|
50
|
+
webhookToken?: string;
|
|
51
|
+
/** @deprecated Use 'webhook.username' */
|
|
52
|
+
webhookUsername?: string;
|
|
53
|
+
/** @deprecated Use 'webhook.avatar' */
|
|
54
|
+
webhookAvatar?: string;
|
|
55
|
+
/** @deprecated Use 'support.roles' */
|
|
56
|
+
supportRoleIds?: string[];
|
|
57
|
+
/** @deprecated Use 'support.users' */
|
|
58
|
+
supportUserIds?: string[];
|
|
29
59
|
}
|
|
30
60
|
|
|
31
61
|
class Tickets {
|
package/index.js
CHANGED
|
@@ -1,25 +1,263 @@
|
|
|
1
|
-
const { Collection, WebhookClient } = require("discord.js"),
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
+
}
|
|
5
11
|
|
|
6
12
|
module.exports = class Tickets {
|
|
7
13
|
constructor(options) {
|
|
8
14
|
this.options = options;
|
|
9
15
|
};
|
|
10
16
|
get prefix() { return `system:ticket:${this.options.prefix}`; };
|
|
17
|
+
|
|
18
|
+
/** @private */
|
|
19
|
+
get webhookOptions() {
|
|
20
|
+
return {
|
|
21
|
+
id: this.options.webhook?.id || this.options.webhookId,
|
|
22
|
+
token: this.options.webhook?.token || this.options.webhookToken,
|
|
23
|
+
username: this.options.webhook?.username || this.options.webhookUsername || "Tickets",
|
|
24
|
+
avatar: this.options.webhook?.avatar || this.options.webhookAvatar || "https://cdn.discordapp.com/emojis/818757771310792704.png?v=1"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
11
27
|
webhook() {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
28
|
+
const { id, token, username, avatar } = this.webhookOptions;
|
|
29
|
+
return new Webhook(`https://discord.com/api/webhooks/${id}/${token}`, { username, avatar_url: avatar });
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {import("discord.js").Interaction} int
|
|
34
|
+
*/
|
|
35
|
+
async run(int) {
|
|
36
|
+
if (int?.isButton?.() || int?.isModalSubmit()) {
|
|
37
|
+
let { guild, channel, member, customId } = int,
|
|
38
|
+
category = guild?.channels?.resolve?.(this.options.ticketCategory || channel?.parentId);
|
|
39
|
+
|
|
40
|
+
if (!guild || !guild.available || !channel || !member || !category) return;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {import("discord.js").InteractionDeferReplyOptions|import("discord.js").InteractionReplyOptions} options
|
|
44
|
+
* @param {boolean} edit
|
|
45
|
+
* @param {boolean} defer
|
|
46
|
+
*/
|
|
47
|
+
const send = async (options = {}, defer = false) => {
|
|
48
|
+
if (defer) return int.deferReply(options).catch(this._debug);
|
|
49
|
+
if (int.replied || int.deferred) return int.editReply(options).catch(this._debug);
|
|
50
|
+
return int.reply(options).catch(this._debug);
|
|
51
|
+
};
|
|
52
|
+
switch (customId) {
|
|
53
|
+
case this.prefix: {
|
|
54
|
+
if (this.options.modal?.enabled) {
|
|
55
|
+
return int.showModal(this.modal({
|
|
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
|
+
[]
|
|
60
|
+
})).catch(this._debug);
|
|
61
|
+
}
|
|
62
|
+
return this.handleCreate({ guild, member, category, send })
|
|
63
|
+
};
|
|
64
|
+
|
|
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" } }] }] })
|
|
66
|
+
|
|
67
|
+
case `${this.prefix}:modal_submit`: {
|
|
68
|
+
let [embed, fields, split] = [new MessageEmbed().setColor("ORANGE"), [], false];
|
|
69
|
+
for (const c of int.fields.components) {
|
|
70
|
+
for (const cc of c.components) {
|
|
71
|
+
if (cc.value && cc.customId) {
|
|
72
|
+
fields.push({ name: cc.customId, value: cc.value });
|
|
73
|
+
if (cc.value.length <= 1024) embed.addField(cc.customId, cc.value);
|
|
74
|
+
else split = true
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (embed.length >= 6000 || split) {
|
|
79
|
+
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,
|
|
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
|
+
})) })
|
|
87
|
+
};
|
|
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
|
+
}
|
|
97
|
+
};
|
|
98
|
+
if (customId.startsWith(`${this.prefix}:close:confirm`)) {
|
|
99
|
+
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
|
+
if (!user) return send({ content: `❌ I was unable to fetch the user that opened the ticket.`, ephemeral: true })
|
|
101
|
+
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
|
+
let closed = await channel.delete(`${member.user.tag} (${member.id}) closed the ticket.`).catch(this._debug);
|
|
104
|
+
if (!closed) return send({ ephemeral: true, content: `${emojis.x} I was unable to delete the channel & close the ticket.` })
|
|
105
|
+
return this.closeTicket({ channel, guild, user, member, messages });
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/** @private */
|
|
111
|
+
async handleCreate({ guild, member, category, send, embeds = [] } = {}) {
|
|
112
|
+
let [support, supportUsers, supportIds] = [[], [], this.getSupportIds()];
|
|
113
|
+
if (supportIds.roles.length) for (const sup of supportIds.roles) {
|
|
114
|
+
let role = guild.roles.resolve(sup);
|
|
115
|
+
if (role) support.push(sup);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
if (supportIds.users.length) for (const uId of supportIds.users) {
|
|
119
|
+
let member = guild.members.resolve(uId) || await guild.members.fetch(uId).catch((e) => {
|
|
120
|
+
if (e?.stack?.includes?.("Unknown Member")) this.options.support.users = this.options.support.users.filter(c => c !== uId);
|
|
121
|
+
return this._debug(e);
|
|
122
|
+
});
|
|
123
|
+
if (member) supportUsers.push(uId);
|
|
124
|
+
}
|
|
125
|
+
await send({ ephemeral: true }, true);
|
|
126
|
+
if (this.options.appeals?.enabled) {
|
|
127
|
+
let appeals = this.options.appeals;
|
|
128
|
+
if (appeals.mainserver?.id && appeals.mainserver.checkIfBanned) {
|
|
129
|
+
let server = this.options.client.guilds.resolve(appeals.mainserver.id);
|
|
130
|
+
if (server?.available) {
|
|
131
|
+
let isBanned = await server.bans.fetch({ user: member.id, force: true }).catch(() => null);
|
|
132
|
+
if (!isBanned) return send(
|
|
133
|
+
typeof appeals.embeds?.not_banned === "object" ?
|
|
134
|
+
appeals.embeds.not_banned :
|
|
135
|
+
{
|
|
136
|
+
embeds: [
|
|
137
|
+
{
|
|
138
|
+
author: { name: guild.name, icon_url: guild.iconURL({ dynamic: true }) },
|
|
139
|
+
title: "INFO",
|
|
140
|
+
description: `❌ You can't open this ticket due to you not being banned in the main server!`,
|
|
141
|
+
color: 0xFF0000,
|
|
142
|
+
timestamp: new Date()
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
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
|
+
|
|
157
|
+
/** @type {import("discord.js").TextChannel} */
|
|
158
|
+
let channel = await guild.channels.create(`${this.options.prefix}-${generate().slice(0, 5).replace(/-|_/g, "")}`, {
|
|
159
|
+
type: "GUILD_TEXT", parent: category, reason: `Ticket created by: @${member.user.tag} (${member.id})`,
|
|
160
|
+
topic: `ID: ${this.code(member.id, "e")}`,
|
|
161
|
+
permissionOverwrites: [
|
|
162
|
+
{ type: "member", id: this.options.client.user.id, allow: ["ADD_REACTIONS", "ATTACH_FILES", "SEND_MESSAGES", "READ_MESSAGE_HISTORY", "EMBED_LINKS", "USE_EXTERNAL_EMOJIS", "VIEW_CHANNEL", "MENTION_EVERYONE"] },
|
|
163
|
+
{ type: "member", id: member.id, allow: ["ADD_REACTIONS", "ATTACH_FILES", "SEND_MESSAGES", "READ_MESSAGE_HISTORY", "EMBED_LINKS", "USE_EXTERNAL_EMOJIS", "VIEW_CHANNEL"], deny: ["MENTION_EVERYONE"] },
|
|
164
|
+
{ type: "role", id: guild.id, deny: ["VIEW_CHANNEL"] },
|
|
165
|
+
...permissions
|
|
166
|
+
]
|
|
167
|
+
}).catch(this._debug);
|
|
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!` });
|
|
169
|
+
let msg = await channel.send({
|
|
170
|
+
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
|
+
embeds: this.options.ticketOpen?.embeds || [{
|
|
172
|
+
author: { name: guild.name, icon_url: guild.iconURL({ dynamic: true }) },
|
|
173
|
+
title: `Support will be with you shortly`,
|
|
174
|
+
color: 0xF50DE3,
|
|
175
|
+
timestamp: new Date(),
|
|
176
|
+
footer: { text: `To close this ticket press the button below.` }
|
|
177
|
+
}],
|
|
178
|
+
components: [{ type: 1, components: [{ type: 2, custom_id: `${this.prefix}:close`, label: "Close Ticket", style: 4, emoji: { name: "🔒" } }] }]
|
|
179
|
+
}).catch(this._debug);
|
|
180
|
+
if (!msg) return null;
|
|
181
|
+
if (embeds?.length <= 10) for await (const embed of embeds) await channel.send({ embeds: [ embed ] }).catch(this._debug);
|
|
182
|
+
if (this.webhookOptions.id && this.webhookOptions.token) this.webhook()
|
|
183
|
+
.embed({
|
|
184
|
+
author: { name: guild.name, icon_url: guild.iconURL({ dynamic: true }) },
|
|
185
|
+
title: "Ticket: Opened",
|
|
186
|
+
description: `${de.user} User: ${member.user.toString()} \`@${member.user.tag}\` (${member.id})\n${de.channel} Channel: \`#${channel.name}\` (${channel.id})`,
|
|
187
|
+
color: 0xFF000,
|
|
188
|
+
timestamp: new Date(),
|
|
189
|
+
footer: { text: `Ticket ID: ${channel.name.split("-")[1]}` },
|
|
190
|
+
}).send().catch(this._debug);
|
|
191
|
+
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
|
+
],
|
|
199
|
+
components: [ { type: 1, components: [ button({ title: "Go to ticket", url: msg.url }) ] } ]
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
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
|
+
});
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @param {object} options
|
|
214
|
+
* @param {string} [options.title] The title of the modal submit form
|
|
215
|
+
* @param {import("@elara-services/packages").Modal['components']} [options.components]
|
|
216
|
+
*/
|
|
217
|
+
modal(options = { title: "", components: [] }) {
|
|
218
|
+
return modal({
|
|
219
|
+
id: `${this.prefix}:modal_submit`,
|
|
220
|
+
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
|
+
]
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async starterMessage(channelId, options) {
|
|
232
|
+
let channel = this.options.client.channels.resolve(channelId);
|
|
233
|
+
if (!channel) return Promise.reject(`No channel found for: ${channelId}`);
|
|
234
|
+
if (!channel.isText()) return Promise.reject(`The channel ID provided isn't a text-based-channel`);
|
|
235
|
+
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})`);
|
|
236
|
+
return channel.send({
|
|
237
|
+
content: options?.content,
|
|
238
|
+
files: options?.attachments,
|
|
239
|
+
embeds: options?.embeds,
|
|
240
|
+
components: options?.components || [{ type: 1, components: [this.button()] }]
|
|
241
|
+
})
|
|
242
|
+
.then(() => console.log(`Sent the starter message in ${channel.name} (${channel.id})`))
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
code(id, type = "d") {
|
|
246
|
+
try {
|
|
247
|
+
switch (type) {
|
|
248
|
+
case "e": return encrypt(this.options.encryptToken, id);
|
|
249
|
+
case "d": return decrypt(this.options.encryptToken, id);
|
|
250
|
+
}
|
|
251
|
+
} catch {
|
|
252
|
+
return id;
|
|
253
|
+
}
|
|
16
254
|
};
|
|
17
255
|
|
|
18
256
|
async fetchMessages(channel, limit = 50, before, after, around) {
|
|
19
257
|
if (limit && limit > 100) {
|
|
20
258
|
let logs = [];
|
|
21
259
|
const get = async (_before, _after) => {
|
|
22
|
-
const messages = [
|
|
260
|
+
const messages = [...(await channel.messages.fetch({ limit: 100, before: _before || undefined, after: _after || undefined }).catch(() => new Collection())).values()];
|
|
23
261
|
if (limit <= messages.length) {
|
|
24
262
|
return (_after ? messages.slice(messages.length - limit, messages.length).map((message) => message).concat(logs) : logs.concat(messages.slice(0, limit).map((message) => message)));
|
|
25
263
|
}
|
|
@@ -30,19 +268,26 @@ module.exports = class Tickets {
|
|
|
30
268
|
};
|
|
31
269
|
return get(before, after);
|
|
32
270
|
}
|
|
33
|
-
return [
|
|
271
|
+
return [...(await channel.messages.fetch({ limit, before, after, around }).catch(() => new Collection())).values()];
|
|
34
272
|
};
|
|
35
273
|
|
|
274
|
+
/**
|
|
275
|
+
* @param {import("discord.js").TextBasedChannel} channel
|
|
276
|
+
* @param {import("discord.js").Message[]} messages
|
|
277
|
+
* @param {string} ticketID
|
|
278
|
+
* @param {string} type
|
|
279
|
+
* @returns {string}
|
|
280
|
+
*/
|
|
36
281
|
displayMessages(channel, messages = [], ticketID, type) {
|
|
37
282
|
let users = [];
|
|
38
283
|
for (const i of messages.values()) {
|
|
39
284
|
let f = users.find(c => c.user.id === i.author.id);
|
|
40
285
|
if (f) f.count++; else users.push({ user: i.author, count: 1 });
|
|
41
286
|
};
|
|
42
|
-
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?.
|
|
287
|
+
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>`,
|
|
43
288
|
...messages.map(message => {
|
|
44
289
|
let str = [
|
|
45
|
-
`<discord-message${message.author.bot ? ` bot="true"
|
|
290
|
+
`<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?.cache?.get(message.author.id)?.displayColor ? channel.guild?.members?.cache?.get(message.author.id)?.displayHexColor : `#ffffff`}">`
|
|
46
291
|
];
|
|
47
292
|
if (message.content) {
|
|
48
293
|
let content = message.content;
|
|
@@ -51,40 +296,76 @@ module.exports = class Tickets {
|
|
|
51
296
|
if (message.mentions.roles.size) for (const role of message.mentions.roles.values()) content = content.replace(new RegExp(role.toString(), "g"), `<discord-mention type="role" color="${role.hexColor}">${role.name}</discord-mention>`)
|
|
52
297
|
str.push(content)
|
|
53
298
|
};
|
|
299
|
+
if (message.embeds?.length) {
|
|
300
|
+
let arr = [ `<discord-embeds slot="embeds">` ];
|
|
301
|
+
for (const embed of message.embeds) {
|
|
302
|
+
let emb = [
|
|
303
|
+
`<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}"` : ""}>`
|
|
304
|
+
];
|
|
305
|
+
if (embed.description) emb.push(`<discord-embed-description slot="description">${embed.description}</discord-embed-description>`);
|
|
306
|
+
if (embed.fields?.length) {
|
|
307
|
+
emb.push(`<discord-embed-fields slot="fields">`)
|
|
308
|
+
for (const field of embed.fields) emb.push(`<discord-embed-field field-title="${field.name}" ${field.inline ? `inline` : ""}>${field.value}</discord-embed-field>`)
|
|
309
|
+
}
|
|
310
|
+
arr.push(...emb, `</discord-embed>`)
|
|
311
|
+
}
|
|
312
|
+
str.push(...arr, "</discord-embeds>")
|
|
313
|
+
}
|
|
314
|
+
if (message.interaction?.user) str.push(`<discord-command slot="reply" command="${message.interaction.type === "APPLICATION_COMMAND" ? "/" : ""}${message.interaction.commandName}" profile="${message.interaction.user.id}" role-color="${channel.guild?.members?.resolve?.(message.interaction.user?.id)?.displayHexColor || "#fffff"}" author="${message.interaction.user.tag}" avatar="${message.interaction.user.displayAvatarURL({ dynamic: true })}"></discord-command>`)
|
|
315
|
+
if (message.components?.length) {
|
|
316
|
+
let row = [
|
|
317
|
+
`<discord-attachments slot="components">`
|
|
318
|
+
],
|
|
319
|
+
styles = {
|
|
320
|
+
"PRIMARY": "primary",
|
|
321
|
+
"SECONDARY": "secondary",
|
|
322
|
+
"SUCCESS": "success",
|
|
323
|
+
"DANGER": "destructive",
|
|
324
|
+
"LINK": "secondary"
|
|
325
|
+
}
|
|
326
|
+
for (const components of message.components) {
|
|
327
|
+
row.push(`<discord-action-row>`)
|
|
328
|
+
for (const c of components.components) {
|
|
329
|
+
if (c.type === "BUTTON") row.push(`<discord-button ${c.disabled ? `disabled=true` : ""} type="${styles[c.style]}" ${c.emoji?.name ? `emoji-name="${c.emoji.name}"` : ""} ${c.emoji?.id ? `emoji="https://cdn.discordapp.com/emojis/${c.emoji.id}.${c.emoji.animated ? "gif" : "png"}"` : ""} ${c.url ? `url="${c.url}"` : ""}>${c.label || ""}</discord-button>`)
|
|
330
|
+
}
|
|
331
|
+
row.push(`</discord-action-row>`)
|
|
332
|
+
}
|
|
333
|
+
if (row.length >= 2) str.push(...row, `</discord-attachments>`)
|
|
334
|
+
}
|
|
54
335
|
if (message.attachments.size) str.push(message.attachments.map((c) => `<a href="${c.proxyURL}">${c.name}</a>`).join("<br>"))
|
|
55
|
-
str.push(`<br><br
|
|
336
|
+
str.push(`${message.content?.length ? `<br><br>` : "" }<code style="background-color: #36393e; color: white;">ID: ${message.id}</code>`)
|
|
56
337
|
return [...str, `</discord-message>`].join(" ");
|
|
57
338
|
}),
|
|
58
339
|
"</discord-messages>"].join(" ");
|
|
59
340
|
};
|
|
60
341
|
|
|
61
342
|
async closeTicket({ member, messages, channel, guild, user } = {}) {
|
|
62
|
-
|
|
343
|
+
const { id, token, username, avatar: avatarURL } = this.webhookOptions;
|
|
344
|
+
if (!id || !token) return;
|
|
63
345
|
let embeds = [
|
|
64
346
|
{
|
|
65
347
|
author: { name: guild.name, icon_url: guild.iconURL({ dynamic: true }) },
|
|
66
348
|
title: "Ticket: Closed",
|
|
67
|
-
description:
|
|
349
|
+
description: `${de.user}User: ${user.toString()} \`@${user.tag}\` (${user.id})\n${de.user}Closed By: ${member.toString()} (${member.id})\n${de.channel}Channel: \`#${channel.name}\` (${channel.id})`,
|
|
68
350
|
color: 0xFF0000,
|
|
69
351
|
timestamp: new Date(),
|
|
70
352
|
footer: { text: `Ticket ID: ${channel.name.split("-")[1]}` },
|
|
71
353
|
}
|
|
72
354
|
];
|
|
73
|
-
new WebhookClient({ id
|
|
74
|
-
.send({
|
|
75
|
-
username
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
files: [ { name: "transcript.txt", attachment: Buffer.from(this.displayMessages(channel, messages.reverse(), channel.name.split("-")[1], this.options.prefix)) } ]
|
|
355
|
+
new WebhookClient({ id, token })
|
|
356
|
+
.send({
|
|
357
|
+
username, avatarURL,
|
|
358
|
+
embeds,
|
|
359
|
+
files: [{ name: "transcript.txt", attachment: Buffer.from(this.displayMessages(channel, messages.reverse(), channel.name.split("-")[1], this.options.prefix)) }]
|
|
79
360
|
})
|
|
80
361
|
.then(m => {
|
|
81
|
-
let components = [{ type: 2, style: 5, label: "Transcript", emoji: {
|
|
82
|
-
|
|
362
|
+
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 instanceof Collection ? m.attachments?.first?.()?.url : "URL_NOT_FOUND" ?? "URL_NOT_FOUND"}` }];
|
|
363
|
+
embeds[0].description += `\n${de.transcript} Transcript: [View here](${components[0].url})`
|
|
83
364
|
this.webhook()
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
365
|
+
.embeds(embeds)
|
|
366
|
+
.button({ type: 1, components })
|
|
367
|
+
.edit(m.id)
|
|
368
|
+
.catch(this._debug);
|
|
88
369
|
if (user) user.send({
|
|
89
370
|
embeds: [{
|
|
90
371
|
author: { name: guild.name, icon_url: guild.iconURL({ dynamic: true }) },
|
|
@@ -94,151 +375,30 @@ module.exports = class Tickets {
|
|
|
94
375
|
footer: { text: `Ticket ID: ${channel.name.split("-")[1]}` }
|
|
95
376
|
}],
|
|
96
377
|
components: [{ type: 1, components }]
|
|
97
|
-
}).catch(
|
|
98
|
-
})
|
|
99
|
-
.catch((e) => console.log(e));
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
async run(int) {
|
|
103
|
-
if (int?.isButton?.()) {
|
|
104
|
-
let { guild, channel, member, customId } = int,
|
|
105
|
-
category = guild?.channels?.resolve?.(this.options.ticketCategory || channel?.parentId),
|
|
106
|
-
[ support, supportUsers ] = [ [], [] ];
|
|
107
|
-
if (!guild || !guild.available || !channel || !member || !category) return;
|
|
108
|
-
if (this.options?.supportRoleIds?.length) for (const sup of this.options.supportRoleIds) {
|
|
109
|
-
let role = guild.roles.resolve(sup);
|
|
110
|
-
if (role) support.push(sup);
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
if (this.options?.supportUserIds?.length) for (const uId of this.options.supportUserIds) {
|
|
114
|
-
let member = guild.members.resolve(uId) || await guild.members.fetch(uId).catch((e) => {
|
|
115
|
-
if (e?.stack?.includes?.("Unknown Member")) this.options.supportUserIds = this.options.supportUserIds.filter(c => c !== uId);
|
|
116
|
-
return null;
|
|
117
|
-
});
|
|
118
|
-
if (member) supportUsers.push(uId);
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* @param {import("discord.js").InteractionDeferReplyOptions|import("discord.js").InteractionReplyOptions} options
|
|
122
|
-
* @param {boolean} edit
|
|
123
|
-
* @param {boolean} defer
|
|
124
|
-
*/
|
|
125
|
-
const send = async (options = {}, defer = false) => {
|
|
126
|
-
if (defer) return int.deferReply(options).catch(() => null);
|
|
127
|
-
if (int.replied || int.deferred) return int.editReply(options).catch(() => null);
|
|
128
|
-
return int.reply(options).catch(() => null);
|
|
129
|
-
};
|
|
130
|
-
switch (customId) {
|
|
131
|
-
case this.prefix: {
|
|
132
|
-
await send({ ephemeral: true }, true);
|
|
133
|
-
if (this.options.appeals?.enabled) {
|
|
134
|
-
let appeals = this.options.appeals;
|
|
135
|
-
if (appeals.mainserver?.id && appeals.mainserver.checkIfBanned) {
|
|
136
|
-
let server = this.options.client.guilds.resolve(appeals.mainserver.id);
|
|
137
|
-
if (server?.available) {
|
|
138
|
-
let isBanned = await server.bans.fetch({ user: member.id, force: true }).catch(() => null);
|
|
139
|
-
if (!isBanned) return send(
|
|
140
|
-
typeof appeals.embeds?.not_banned === "object" ?
|
|
141
|
-
appeals.embeds.not_banned :
|
|
142
|
-
{ embeds: [
|
|
143
|
-
{
|
|
144
|
-
author: { name: guild.name, icon_url: guild.iconURL({ dynamic: true }) },
|
|
145
|
-
title: "INFO",
|
|
146
|
-
description: `❌ You can't open this ticket due to you not being banned in the main server!`,
|
|
147
|
-
color: 0xFF0000,
|
|
148
|
-
timestamp: new Date()
|
|
149
|
-
}
|
|
150
|
-
]}
|
|
151
|
-
)
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
let [ permissions, allow ] = [
|
|
156
|
-
[],
|
|
157
|
-
[ "ADD_REACTIONS", "ATTACH_FILES", "CREATE_INSTANT_INVITE", "EMBED_LINKS", "READ_MESSAGE_HISTORY", "VIEW_CHANNEL", "USE_EXTERNAL_EMOJIS", "SEND_MESSAGES" ]
|
|
158
|
-
];
|
|
159
|
-
if (support.length) for (const sup of support) permissions.push({ type: "role", id: sup, allow });
|
|
160
|
-
if (supportUsers.length) for (const user of supportUsers) permissions.push({ type: "member", id: user, allow });
|
|
161
|
-
|
|
162
|
-
/** @type {import("discord.js").TextChannel} */
|
|
163
|
-
let channel = await guild.channels.create(`${this.options.prefix}-${generate().slice(0, 5).replace(/-|_/g, "")}`, {
|
|
164
|
-
type: "GUILD_TEXT", parent: category, reason: `Ticket created by: @${member.user.tag} (${member.id})`,
|
|
165
|
-
topic: `ID: ${this.code(member.id, "e")}`,
|
|
166
|
-
permissionOverwrites: [
|
|
167
|
-
{ type: "member", id: this.options.client.user.id, allow: ["ADD_REACTIONS", "ATTACH_FILES", "SEND_MESSAGES", "READ_MESSAGE_HISTORY", "EMBED_LINKS", "USE_EXTERNAL_EMOJIS", "VIEW_CHANNEL", "MENTION_EVERYONE"] },
|
|
168
|
-
{ type: "member", id: member.id, allow: ["ADD_REACTIONS", "ATTACH_FILES", "SEND_MESSAGES", "READ_MESSAGE_HISTORY", "EMBED_LINKS", "USE_EXTERNAL_EMOJIS", "VIEW_CHANNEL"], deny: ["MENTION_EVERYONE"] },
|
|
169
|
-
{ type: "role", id: guild.id, deny: ["VIEW_CHANNEL"] },
|
|
170
|
-
...permissions
|
|
171
|
-
]
|
|
172
|
-
}).catch((err) => { console.log(err); return null; });
|
|
173
|
-
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!` });
|
|
174
|
-
let msg = await channel.send({
|
|
175
|
-
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.`,
|
|
176
|
-
embeds: this.options.ticketOpen?.embeds || [{
|
|
177
|
-
author: { name: guild.name, icon_url: guild.iconURL({ dynamic: true }) },
|
|
178
|
-
title: `Support will be with you shortly`,
|
|
179
|
-
color: 0xF50DE3,
|
|
180
|
-
timestamp: new Date(),
|
|
181
|
-
footer: { text: `To close this ticket press the button below.` }
|
|
182
|
-
}],
|
|
183
|
-
components: [{ type: 1, components: [{ type: 2, custom_id: `${this.prefix}:close`, label: "Close Ticket", style: 4, emoji: { name: "🔒" } }] }]
|
|
184
|
-
}).catch(() => null);
|
|
185
|
-
if (!msg) return null
|
|
186
|
-
if (this.options.webhookId && this.options.webhookToken) this.webhook()
|
|
187
|
-
.embed({
|
|
188
|
-
author: { name: guild.name, icon_url: guild.iconURL({ dynamic: true }) },
|
|
189
|
-
title: "Ticket: Opened",
|
|
190
|
-
description: `▫️User: ${member.user.toString()} \`@${member.user.tag}\` (${member.id})\n▫️Channel: \`#${channel.name}\` (${channel.id})`,
|
|
191
|
-
color: 0xFF000,
|
|
192
|
-
timestamp: new Date(),
|
|
193
|
-
footer: { text: `Ticket ID: ${channel.name.split("-")[1]}` },
|
|
194
|
-
}).send().catch((e) => console.log(e));
|
|
195
|
-
return send({ content: `✅ Ticket created: ${channel.toString()}`, components: [
|
|
196
|
-
{ type: 1, components: [
|
|
197
|
-
{ type: 2, style: 5, url: msg.url, label: "Go to ticket" }
|
|
198
|
-
] }
|
|
199
|
-
] })
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
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" } }] }] })
|
|
203
|
-
};
|
|
204
|
-
if (customId.startsWith(`${this.prefix}:close:confirm`)) {
|
|
205
|
-
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);
|
|
206
|
-
if (!user) return send({ content: `❌ I was unable to fetch the user that opened the ticket.`, ephemeral: true })
|
|
207
|
-
let messages = await this.fetchMessages(channel, 5000);
|
|
208
|
-
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.` })
|
|
209
|
-
let closed = await channel.delete(`${member.user.tag} (${member.id}) closed the ticket.`).catch(() => null);
|
|
210
|
-
if (!closed) return send({ ephemeral: true, content: `${emojis.x} I was unable to delete the channel & close the ticket.` })
|
|
211
|
-
return this.closeTicket({ channel, guild, user, member, messages });
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
async starterMessage(channelId, options) {
|
|
217
|
-
let channel = this.options.client.channels.resolve(channelId);
|
|
218
|
-
if (!channel) return Promise.reject(`No channel found for: ${channelId}`);
|
|
219
|
-
if (!channel.isText()) return Promise.reject(`The channel ID provided isn't a text-based-channel`);
|
|
220
|
-
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})`);
|
|
221
|
-
return channel.send({
|
|
222
|
-
content: options?.content,
|
|
223
|
-
files: options?.attachments,
|
|
224
|
-
embeds: options?.embeds,
|
|
225
|
-
components: options?.components || [ { type: 1, components: [ this.button() ] } ]
|
|
226
|
-
})
|
|
227
|
-
.then(() => console.log(`Sent the starter message in ${channel.name} (${channel.id})`))
|
|
378
|
+
}).catch(this._debug);
|
|
379
|
+
}).catch(this._debug);
|
|
228
380
|
};
|
|
381
|
+
/**
|
|
382
|
+
* @typedef {Object} getSupportResponse
|
|
383
|
+
* @property {string[]} [roles]
|
|
384
|
+
* @property {string[]} [users]
|
|
385
|
+
*
|
|
386
|
+
*
|
|
387
|
+
* @private
|
|
388
|
+
* @returns {getSupportResponse}
|
|
389
|
+
*/
|
|
390
|
+
getSupportIds() {
|
|
391
|
+
return {
|
|
392
|
+
roles: this.options.support?.roles || this.options.supportRoleIds || [],
|
|
393
|
+
users: this.options.support?.users || this.options.supportUserIds || []
|
|
394
|
+
}
|
|
395
|
+
}
|
|
229
396
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
397
|
+
/** @private */
|
|
398
|
+
_debug(...args) {
|
|
399
|
+
if (!this.options?.debug) return null;
|
|
400
|
+
console.log(...args);
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
233
403
|
|
|
234
|
-
code(id, type = "d") {
|
|
235
|
-
try {
|
|
236
|
-
switch (type) {
|
|
237
|
-
case "e": return encrypt(this.options.encryptToken, id);
|
|
238
|
-
case "d": return decrypt(this.options.encryptToken, id);
|
|
239
|
-
}
|
|
240
|
-
} catch {
|
|
241
|
-
return id;
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
404
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elara-services/tickets",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Helper for tickets",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"discord.js": "13.8.0"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
+
"@elara-services/packages": "5.0.0",
|
|
14
15
|
"aes256": "1.1.0",
|
|
15
16
|
"discord-hook": "2.0.0",
|
|
16
17
|
"shortid": "2.2.16"
|