/[sudobot]/branches/4.x/src/commands/automation/ButtonRoleCreateCommand.ts
ViewVC logotype

Contents of /branches/4.x/src/commands/automation/ButtonRoleCreateCommand.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 577 - (show annotations)
Mon Jul 29 18:52:37 2024 UTC (8 months, 1 week ago) by rakinar2
File MIME type: application/typescript
File size: 12352 byte(s)
chore: add old version archive branches (2.x to 9.x-dev)
1 /**
2 * This file is part of SudoBot.
3 *
4 * Copyright (C) 2021-2022 OSN Inc.
5 *
6 * SudoBot is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * SudoBot is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with SudoBot. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 import { ButtonInteraction, CacheType, CommandInteraction, GuildChannel, Interaction, Message, MessageActionRow, MessageButton, MessageCollector, Permissions, TextChannel, Util } from 'discord.js';
21 import BaseCommand from '../../utils/structures/BaseCommand';
22 import DiscordClient from '../../client/Client';
23 import CommandOptions from '../../types/CommandOptions';
24 import MessageEmbed from '../../client/MessageEmbed';
25 import InteractionOptions from '../../types/InteractionOptions';
26 import { fetchEmoji } from '../../utils/Emoji';
27 import InteractionRole from '../../models/InteractionRole';
28 import InteractionRoleMessage from '../../models/InteractionRoleMessage';
29
30 export default class ButtonRoleCreateCommand extends BaseCommand {
31 permissions = [Permissions.FLAGS.MANAGE_MESSAGES];
32
33 constructor() {
34 super('buttonrole__create', 'automation', []);
35 }
36
37 async action(client: DiscordClient, message: Message<boolean>, interactionBtn: ButtonInteraction) {
38 await interactionBtn.reply("**Step 1**\nDo you want to have a custom text with your interaction role or the bot should generate it? Reply with '...' to generate message.");
39 const collector = new MessageCollector(message.channel!, {
40 filter(i) {
41 console.log(i.author.id, interactionBtn.user.id);
42 return i.author.id === interactionBtn.user.id;
43 },
44 time: 180_000,
45 });
46
47 let step = 1;
48 let content = '', messageContent = '', autoGenerate = false, channel: TextChannel = message.channel! as TextChannel;
49 const row = new MessageActionRow<MessageButton>();
50 const insertedIDs: string[] = [];
51
52 collector.on("collect", async collectedMessage => {
53 console.log("Collected!");
54 step++;
55
56 if (collectedMessage.content === '--cancel') {
57 await InteractionRole.deleteMany({ _id: { $in: insertedIDs } });
58 collector.stop();
59 await collectedMessage.react(fetchEmoji('check')!);
60 return;
61 }
62
63 if (step === 2) {
64 messageContent = collectedMessage.content;
65 autoGenerate = messageContent.trim() === '...';
66
67 await collectedMessage.reply("**Step 2**\nAwesome! Tell me which roles should I take care of, with name.\nThe format should be: `<roleID|roleMention> - <role title>` and each role should be in new lines and must not exceed 5 lines; Example:\n```\n@Test - Test Role\n363456217832361253 - Announcements\n```.\n\n*Waiting for your response...*");
68 }
69 else if (step === 3) {
70 const tempMessage = await collectedMessage.reply("Please wait...");
71 content = collectedMessage.content;
72
73 const lines = content.split('\n');
74
75 if (lines.length > 5) {
76 await collectedMessage.reply("I cannot create 5 role buttons at once!");
77 collector.stop();
78 tempMessage.delete().catch(console.error);
79 return;
80 }
81
82 if (messageContent.trim() === '...') {
83 messageContent = '';
84 }
85
86 let i = 0;
87
88 for (const line of lines) {
89 i++;
90
91 const trimmed = line.trim();
92 const pos = trimmed.indexOf('-');
93
94 if (pos === -1) {
95 await collectedMessage.reply("You probably gave malformed entries. The role list must follow the structure/format given above.");
96 collector.stop();
97 tempMessage.delete().catch(console.error);
98 return;
99 }
100
101 const roleOrID = trimmed.substring(0, pos).trim();
102 const name = trimmed.substring(pos + 1).trim();
103
104 console.log(roleOrID, name);
105
106 try {
107 const role = roleOrID.startsWith('<@&') && roleOrID.endsWith('>') ? await message.guild!.roles.fetch(roleOrID.substring(3, roleOrID.length - 1)) : (await message.guild!.roles.fetch(roleOrID));
108
109 if (!role) {
110 throw new Error();
111 }
112
113 const interactionRole = await InteractionRole.create({
114 guild_id: message.guildId!,
115 role_id: role.id,
116 type: 'button',
117 createdAt: new Date()
118 });
119
120 insertedIDs.push(interactionRole.id);
121
122 if (autoGenerate) {
123 messageContent += `${role} • ${Util.escapeMarkdown(name)}\n`;
124 }
125
126 row.addComponents(
127 new MessageButton()
128 .setCustomId(`role__${interactionRole.id}__${role.id}`)
129 .setLabel(name)
130 .setStyle('SECONDARY')
131 );
132 }
133 catch (e) {
134 console.log(e);
135
136 await collectedMessage.reply(`The role with ID ${roleOrID.startsWith('<@&') && roleOrID.endsWith('>') ? roleOrID.substring(3, roleOrID.length - 1) : roleOrID} (at position ${i}) is invalid or unable to fetch the role.`);
137 collector.stop();
138 tempMessage.delete().catch(console.error);
139 return;
140 }
141 }
142
143 tempMessage.delete().catch(console.error);
144 await collectedMessage.reply("**Step 3**\nIn which channel you want the button role message to be? Tag it here or send the ID or send '...' to use the current channel.");
145 }
146 else if (step === 4) {
147 if (collectedMessage.content.trim() !== '...') {
148 if (collectedMessage.mentions.channels.first()) {
149 channel = collectedMessage.mentions.channels.first() as TextChannel;
150 }
151 else {
152 try {
153 channel = <TextChannel> await message.guild?.channels.fetch(collectedMessage.content.trim());
154 }
155 catch (e) {
156 await collectedMessage.reply("Channel not found! Make sure the ID is correct.");
157 collector.stop();
158 return;
159 }
160 }
161
162 if (!['GUILD_TEXT', 'GUILD_NEWS', 'GUILD_NEWS_THREAD', 'GUILD_PUBLIC_THREAD', 'GUILD_PRIVATE_THREAD'].includes(channel.type)) {
163 await collectedMessage.reply("The given channel must be a text/announcement/thread channel!");
164 collector.stop();
165 return;
166 }
167 }
168
169 await collectedMessage.reply("**Step 4**\nAlright, I got it. Please confirm that everything is OK by typing `ok` in this channel.");
170 }
171 else if (step === 5) {
172 if (collectedMessage.content.trim().toLowerCase() === 'ok') {
173 channel.send({
174 content: messageContent,
175 components: [
176 row
177 ],
178 allowedMentions: {
179 roles: []
180 }
181 }).then(async({ id, guildId }) => {
182 await InteractionRoleMessage.create({
183 dbIDs: insertedIDs,
184 createdAt: new Date(),
185 guild_id: guildId!,
186 message_id: id
187 });
188 }).catch(console.error);
189 }
190 else {
191 await InteractionRole.deleteMany({ _id: { $in: insertedIDs } });
192 collector.stop();
193 message.reply("Operation canceled.");
194 }
195 }
196 });
197 }
198
199 async run(client: DiscordClient, message: CommandInteraction<CacheType> | Message<boolean>, options: CommandOptions | InteractionOptions): Promise<void> {
200 let msg = <Message | CommandInteraction> await message.reply({
201 embeds: [
202 new MessageEmbed({
203 title: "Button Role Creation Wizard",
204 description: "This interactive wizard will help you to set up new button roles. Make sure that you respond in 3 minutes or otherwise the operation will be canceled. You can also manually cancel the operation by typing `--cancel` at any time, in this channel."
205 })
206 ],
207 components: [
208 new MessageActionRow()
209 .addComponents(
210 new MessageButton()
211 .setCustomId('rolecreate__start')
212 .setEmoji(fetchEmoji('ArrowRight')!)
213 .setLabel("Start Role Creation")
214 .setStyle('SECONDARY'),
215 new MessageButton()
216 .setCustomId('rolecreate__cancel')
217 .setEmoji(fetchEmoji('error')!)
218 .setLabel("Cancel")
219 .setStyle('SECONDARY')
220 )
221 ]
222 });
223
224 if (message instanceof CommandInteraction) {
225 msg = <Message> await message.fetchReply();
226 }
227
228 message.channel!.awaitMessageComponent({
229 componentType: 'BUTTON',
230 dispose: true,
231 time: 120_000,
232 filter: i => i.guild.id === message.guildId! && i.member.user.id === message.member!.user.id
233 })
234 .then(async interaction => {
235 if (interaction.customId !== 'rolecreate__start') {
236 await interaction.reply("Operation canceled.");
237 return Promise.reject();
238 }
239
240 let msg = message;
241
242 if (message instanceof CommandInteraction) {
243 msg = <Message> await message.fetchReply();
244 }
245
246 this.action(client, msg as Message, interaction);
247 })
248 .catch(console.error)
249 .finally(() => {
250 const row = new MessageActionRow()
251 .addComponents(
252 new MessageButton()
253 .setCustomId('rolecreate__start')
254 .setEmoji(fetchEmoji('ArrowRight')!)
255 .setLabel("Start Role Creation")
256 .setStyle('SECONDARY')
257 .setDisabled(true),
258 new MessageButton()
259 .setCustomId('rolecreate__cancel')
260 .setEmoji(fetchEmoji('error')!)
261 .setLabel("Cancel")
262 .setStyle('SECONDARY')
263 .setDisabled(true)
264 );
265
266 if (msg instanceof Message) {
267 msg.edit({ components: [row] }).catch(console.error);
268 }
269 else if (msg instanceof CommandInteraction) {
270 msg.editReply({ components: [row] }).catch(console.error);
271 }
272 });
273 }
274 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26