-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathguildMemberUpdate.ts
More file actions
231 lines (205 loc) · 6.14 KB
/
Copy pathguildMemberUpdate.ts
File metadata and controls
231 lines (205 loc) · 6.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
import "../libs/loadVariables.js";
import {
ContainerBuilder,
EmbedBuilder,
Events,
GuildMember,
MessageFlags,
PermissionFlagsBits,
TextChannel,
} from "discord.js";
import {
registerBoost,
removeBoost,
scheduleCustomRoleDeletionAfterGrace,
clearPendingCustomRoleDeletion,
} from "../services/boosterService.js";
import {
assignLevelRoles,
removeAllLevelRoles,
} from "../services/roleService.js";
import { logger } from "../libs/logger.js";
import { prisma } from "../libs/database.js";
import { DiscordClient } from "../base/types/discord.js";
import { SystemColors } from "../libs/colors.js";
export default {
name: Events.GuildMemberUpdate,
async execute(
_client: DiscordClient,
oldMember: GuildMember,
newMember: GuildMember,
) {
try {
if (oldMember.partial) oldMember = await oldMember.fetch();
if (newMember.partial) newMember = await newMember.fetch();
} catch (error) {
const username =
oldMember?.user?.username ?? oldMember?.id ?? "Unknown user";
logger.error(`Failed to fetch partials for user ${username}`);
return;
}
const wasBoostingBefore = oldMember.premiumSince !== null;
const isBoostingNow = newMember.premiumSince !== null;
if (!wasBoostingBefore && isBoostingNow) {
try {
await onBoostStart(newMember);
} catch (error) {
logger.error(
`onBoostStart failed for user ${newMember.id} in guild ${newMember.guild.id}:`,
error,
);
}
} else if (wasBoostingBefore && !isBoostingNow) {
try {
await onBoostEnd(newMember);
} catch (error) {
logger.error(
`onBoostEnd failed for user ${newMember.id} in guild ${newMember.guild.id}:`,
error,
);
}
}
},
};
async function getGuildSettings(guildId: string) {
return prisma.guildSetting.findUnique({ where: { gid: guildId } });
}
async function safeSend(
channel: TextChannel | undefined,
payload: Parameters<TextChannel["send"]>[0],
): Promise<void> {
if (!channel) return;
const me = channel.guild.members.me;
const perms = me ? channel.permissionsFor(me) : null;
if (
!perms?.has([
PermissionFlagsBits.ViewChannel,
PermissionFlagsBits.SendMessages,
])
) {
logger.warn(
`Missing permission to send in channel ${channel.id} (guild ${channel.guild.id})`,
);
return;
}
try {
await channel.send(payload);
} catch (error) {
logger.error(`Failed to send message in channel ${channel.id}:`, error);
}
}
async function onBoostStart(member: GuildMember): Promise<void> {
const guild = member.guild;
const settings = await getGuildSettings(guild.id);
if (!settings) {
logger.error(
`No guild settings found for guild ${guild.id} — run /setup first.`,
);
return;
}
const record = await registerBoost(
member.id,
guild.id,
guild.name,
guild.iconURL(),
);
if (!record) return;
await clearPendingCustomRoleDeletion(record.id);
await assignLevelRoles(member, record.boostCounts ?? 1);
const greetChannel = guild.channels.cache.get(settings.greetChannelId) as
| TextChannel
| undefined;
const embed = new EmbedBuilder()
.setColor(0xf47fff)
.setTitle("New Server Boost! 🎉")
.setDescription(`${member} has boosted the server!`)
.addFields(
{
name: "Total Boosts",
value: String(record.boostCounts ?? 1),
inline: true,
},
{ name: "Member", value: member.user.tag, inline: true },
)
.setThumbnail(member.displayAvatarURL({ size: 512 }))
.setFooter({ text: "Thank youuu!" })
.setTimestamp();
await safeSend(greetChannel, { embeds: [embed] });
const logChannel = guild.channels.cache.get(settings.logChannelId) as
| TextChannel
| undefined;
const logEmbed = new EmbedBuilder()
.setColor(0x57f287)
.setTitle("Boost Started")
.addFields(
{
name: "User",
value: `${member.user.tag} (${member.id})`,
inline: false,
},
{
name: "Total Boost Count",
value: String(record.boostCounts ?? 1),
inline: true,
},
)
.setThumbnail(member.displayAvatarURL({ size: 512 }))
.setTimestamp();
await safeSend(logChannel, { embeds: [logEmbed] });
try {
const serverImage = guild.iconURL();
const container = new ContainerBuilder().addSectionComponents(comp => {
comp.addTextDisplayComponents(text =>
text.setContent(
`## Thanks for the boost! <a:booster:1527782487349268480>\n` +
`Thank you for boosting the server **${guild.name}**\n\n`+
`Your support helps us unlock new server perks and continue making this community even better for everyone.\n\n`+
`We truly appreciate you being part of our journey. Enjoy your booster rewards! ✨`
)
);
if (serverImage) {
comp.setThumbnailAccessory(accessory => accessory.setURL(serverImage));
}
return comp;
}).setAccentColor(SystemColors.main);
await member.send({
components: [container],
flags: [MessageFlags.IsComponentsV2]
});
} catch { }
}
async function onBoostEnd(member: GuildMember): Promise<void> {
const guild = member.guild;
const settings = await getGuildSettings(guild.id);
if (!settings) {
logger.error(
`No guild settings found for guild ${guild.id} — run /setup first.`,
);
return;
}
const result = await removeBoost(member.id, guild.id);
if (!result) return;
await removeAllLevelRoles(member);
await scheduleCustomRoleDeletionAfterGrace(member.id, guild.id);
const logChannel = guild.channels.cache.get(settings.logChannelId) as
| TextChannel
| undefined;
const logEmbed = new EmbedBuilder()
.setColor(0xed4245)
.setTitle("Boost Ended")
.addFields(
{
name: "User",
value: `${member.user.tag} (${member.id})`,
inline: false,
},
{
name: "Historical Boost Count",
value: String(result.boostCounts ?? 0),
inline: true,
},
)
.setThumbnail(member.displayAvatarURL({ size: 512 }))
.setTimestamp();
await safeSend(logChannel, { embeds: [logEmbed] });
}