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, GuildMember, MessageActionRow, MessageButton, MessageEmbed, TextChannel, Util, Message } from "discord.js"; |
21 |
import fs from 'fs'; |
22 |
import path from "path"; |
23 |
import Service from "../utils/structures/Service"; |
24 |
import { hasConfig } from "../utils/util"; |
25 |
import { emoji } from "../utils/Emoji"; |
26 |
|
27 |
export default class Welcomer extends Service { |
28 |
messages: string[] = JSON.parse(fs.readFileSync(path.resolve(process.env.SUDO_PREFIX ?? path.join(__dirname, '..', '..'), 'resources', 'welcome_messages.json')).toString()); |
29 |
updatingMessage: boolean = false; |
30 |
lastUpdate = Date.now(); |
31 |
|
32 |
generateMessageOptions(member: GuildMember, index?: number) { |
33 |
const { message, randomize, embed, color } = this.client.config.props[member.guild.id].welcomer; |
34 |
let content: string = message ?? ''; |
35 |
|
36 |
if (randomize) { |
37 |
content = this.generateMessage(index) + (message ? "\n" + content : ''); |
38 |
} |
39 |
|
40 |
if (content.trim() === '') { |
41 |
return false; |
42 |
} |
43 |
|
44 |
content = content |
45 |
.replace(/:name:/g, member.displayName) |
46 |
.replace(/:tag:/g, member.user.tag) |
47 |
.replace(/:username:/g, member.user.username) |
48 |
.replace(/:discrim:/g, member.user.discriminator) |
49 |
.replace(/:avatar:/g, member.displayAvatarURL()) |
50 |
.replace(/:date:/g, `<t:${member.joinedAt?.getTime()}>`) |
51 |
.replace(/:guild:/g, member.guild.name) |
52 |
.replace(/:mention:/g, member.toString()); |
53 |
|
54 |
return { |
55 |
content: member.toString() + ( |
56 |
!embed ? "\n" + content : "" |
57 |
), |
58 |
embeds: embed ? [ |
59 |
new MessageEmbed({ |
60 |
author: { |
61 |
iconURL: member.displayAvatarURL(), |
62 |
name: member.user.tag |
63 |
}, |
64 |
description: content, |
65 |
footer: { |
66 |
text: 'Welcome' |
67 |
} |
68 |
}) |
69 |
.setColor(color ?? '#007bff') |
70 |
.setTimestamp() |
71 |
] : [], |
72 |
components: [ |
73 |
this.createComponent(member.user.id) |
74 |
] |
75 |
}; |
76 |
} |
77 |
|
78 |
createComponent(memberId: string) { |
79 |
return new MessageActionRow<MessageButton>() |
80 |
.addComponents( |
81 |
new MessageButton() |
82 |
.setCustomId(`say_hi__${memberId}`) |
83 |
.setLabel('Say Hi') |
84 |
.setEmoji(emoji('wave')! as any) |
85 |
.setStyle("SECONDARY") |
86 |
); |
87 |
} |
88 |
|
89 |
async onButtonInteraction(interaction: ButtonInteraction) { |
90 |
console.log(interaction.customId); |
91 |
|
92 |
if (!interaction.customId.startsWith("say_hi__")) |
93 |
return; |
94 |
|
95 |
if (this.updatingMessage || Date.now() - this.lastUpdate <= 800) { |
96 |
await interaction.reply({ |
97 |
content: "Whoa there! That was too quick! Please try again in a second. I had to ratelimit this in order to prevent spam!", |
98 |
ephemeral: true |
99 |
}).catch(console.error); |
100 |
|
101 |
return; |
102 |
} |
103 |
|
104 |
let [, memberId, messageId] = interaction.customId.split('__'); |
105 |
|
106 |
if (messageId) |
107 |
await interaction.deferUpdate(); |
108 |
|
109 |
try { |
110 |
if (!messageId) { |
111 |
this.updatingMessage = true; |
112 |
|
113 |
const reply = await interaction.reply({ |
114 |
content: `${interaction.user.id === memberId ? `<@${interaction.user.id}> (You)` : `<@${interaction.user.id}>`} says hi!`, |
115 |
fetchReply: true |
116 |
}); |
117 |
|
118 |
const component = this.createComponent(memberId); |
119 |
|
120 |
component.components[0].setCustomId(`say_hi__${memberId}__${reply.id}`); |
121 |
|
122 |
await (interaction.message as Message).edit({ |
123 |
components: [ |
124 |
component |
125 |
], |
126 |
}); |
127 |
|
128 |
messageId = reply.id; |
129 |
this.updatingMessage = false; |
130 |
|
131 |
setTimeout(async () => { |
132 |
try { |
133 |
component.components[0].setDisabled(true); |
134 |
|
135 |
await (interaction.message as Message).delete().catch(console.error); |
136 |
await (reply as Message).delete().catch(console.error); |
137 |
} |
138 |
catch (e) { |
139 |
console.log(e); |
140 |
} |
141 |
}, 240_000); |
142 |
} |
143 |
else { |
144 |
const message = await interaction.channel!.messages.fetch(messageId); |
145 |
|
146 |
if (!message) |
147 |
throw new Error(); |
148 |
|
149 |
if ( |
150 |
(interaction.user.id === memberId && message.content.includes("You")) || |
151 |
(interaction.user.id !== memberId && message.content.includes(`<@${interaction.user.id}>`)) |
152 |
) { |
153 |
await interaction.followUp({ |
154 |
content: "You've already greeted the user!", |
155 |
ephemeral: true |
156 |
}); |
157 |
|
158 |
return; |
159 |
} |
160 |
|
161 |
await message.edit({ |
162 |
content: message.content.replace(/ says hi\!$/gi, "") + ", " + (interaction.user.id === memberId ? `<@${interaction.user.id}> (You)` : `<@${interaction.user.id}>`) + " says hi!", |
163 |
}); |
164 |
|
165 |
await interaction.update({ |
166 |
content: interaction.message.content |
167 |
}); |
168 |
} |
169 |
} |
170 |
catch (e) { |
171 |
this.updatingMessage = false; |
172 |
console.log(e); |
173 |
return; |
174 |
} |
175 |
|
176 |
this.lastUpdate = Date.now(); |
177 |
} |
178 |
|
179 |
async start(member: GuildMember, index?: number) { |
180 |
if (!hasConfig(this.client, member.guild.id, "welcomer")) |
181 |
return; |
182 |
|
183 |
if (this.client.config.props[member.guild.id].welcomer.enabled) { |
184 |
const { channel: channelID } = this.client.config.props[member.guild.id].welcomer; |
185 |
|
186 |
try { |
187 |
const channel = (await member.guild.channels.fetch(channelID)) as TextChannel; |
188 |
const options = this.generateMessageOptions(member, index); |
189 |
|
190 |
if (!options) { |
191 |
return; |
192 |
} |
193 |
|
194 |
if (channel) { |
195 |
await channel.send(options); |
196 |
} |
197 |
} |
198 |
catch (e) { |
199 |
console.log(e); |
200 |
} |
201 |
} |
202 |
} |
203 |
|
204 |
generateMessage(index?: number) { |
205 |
return this.messages[index ?? Math.floor(this.messages.length * Math.random())]; |
206 |
} |
207 |
} |