djs-selfbot-v13 3.7.29 → 3.7.30

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.
@@ -0,0 +1,167 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ fetchChannelPermissions,
5
+ fetchTextChannelData,
6
+ fetchVoiceChannelData,
7
+ fetchBuffer,
8
+ } = require('./util');
9
+
10
+ /**
11
+ * Returns banned members of the guild
12
+ * @param {import('../../structures/Guild').Guild} guild
13
+ * @returns {Promise<Object[]>}
14
+ */
15
+ async function getBans(guild) {
16
+ const bans = [];
17
+ const cases = await guild.bans.fetch();
18
+
19
+ cases.forEach(ban => {
20
+ bans.push({
21
+ id: ban.user.id,
22
+ reason: ban.reason,
23
+ });
24
+ });
25
+
26
+ return bans;
27
+ }
28
+
29
+ /**
30
+ * Returns members of the guild
31
+ * @param {import('../../structures/Guild').Guild} guild
32
+ * @returns {Promise<Object[]>}
33
+ */
34
+ async function getMembers(guild) {
35
+ const members = [];
36
+
37
+ guild.members.cache.forEach(member => {
38
+ members.push({
39
+ userId: member.user.id,
40
+ username: member.user.username,
41
+ discriminator: member.user.discriminator,
42
+ avatarUrl: member.user.avatarURL(),
43
+ joinedTimestamp: member.joinedTimestamp,
44
+ roles: member.roles.cache.map(role => role.id),
45
+ bot: member.user.bot,
46
+ });
47
+ });
48
+
49
+ return members;
50
+ }
51
+
52
+ /**
53
+ * Returns roles of the guild
54
+ * @param {import('../../structures/Guild').Guild} guild
55
+ * @returns {Promise<Object[]>}
56
+ */
57
+ async function getRoles(guild) {
58
+ const roles = [];
59
+
60
+ guild.roles.cache
61
+ .filter(role => !role.managed)
62
+ .sort((a, b) => b.position - a.position)
63
+ .forEach(role => {
64
+ roles.push({
65
+ oldId: role.id,
66
+ name: role.name,
67
+ color: role.hexColor,
68
+ hoist: role.hoist,
69
+ permissions: role.permissions.bitfield.toString(),
70
+ mentionable: role.mentionable,
71
+ position: role.position,
72
+ isEveryone: guild.id === role.id,
73
+ });
74
+ });
75
+
76
+ return roles;
77
+ }
78
+
79
+ /**
80
+ * Returns emojis of the guild
81
+ * @param {import('../../structures/Guild').Guild} guild
82
+ * @param {Object} options
83
+ * @returns {Promise<Object[]>}
84
+ */
85
+ async function getEmojis(guild, options) {
86
+ const emojis = [];
87
+
88
+ for (const emoji of guild.emojis.cache.values()) {
89
+ const emojiData = { name: emoji.name };
90
+
91
+ if (options.saveImages === 'base64') {
92
+ emojiData.base64 = (await fetchBuffer(emoji.url)).toString('base64');
93
+ } else {
94
+ emojiData.url = emoji.url;
95
+ }
96
+
97
+ emojis.push(emojiData);
98
+ }
99
+
100
+ return emojis;
101
+ }
102
+
103
+ /**
104
+ * Fetch channel data based on type
105
+ * @param {import('../../structures/GuildChannel')} channel
106
+ * @param {Object} options
107
+ * @returns {Promise<Object>}
108
+ */
109
+ async function fetchAnyChannelData(channel, options) {
110
+ if (['GUILD_TEXT', 'GUILD_NEWS'].includes(channel.type)) {
111
+ return fetchTextChannelData(channel, options);
112
+ }
113
+
114
+ return fetchVoiceChannelData(channel);
115
+ }
116
+
117
+ /**
118
+ * Returns channels of the guild
119
+ * @param {import('../../structures/Guild').Guild} guild
120
+ * @param {Object} options
121
+ * @returns {Promise<Object>}
122
+ */
123
+ async function getChannels(guild, options) {
124
+ const allChannels = guild.channels.cache;
125
+
126
+ const categories = allChannels
127
+ .filter(ch => ch.type === 'GUILD_CATEGORY')
128
+ .sort((a, b) => a.position - b.position);
129
+
130
+ const categoryDataPromises = categories.map(async category => {
131
+ const children = allChannels
132
+ .filter(c => c.parentId === category.id)
133
+ .sort((a, b) => a.position - b.position);
134
+
135
+ const childrenData = await Promise.all(children.map(child => fetchAnyChannelData(child, options)));
136
+
137
+ return {
138
+ name: category.name,
139
+ permissions: fetchChannelPermissions(category),
140
+ children: childrenData,
141
+ };
142
+ });
143
+
144
+ const others = allChannels
145
+ .filter(
146
+ ch => !ch.parent && ch.type !== 'GUILD_CATEGORY' && !ch.isThread?.() && ch.type !== 'GUILD_STORE',
147
+ )
148
+ .sort((a, b) => a.position - b.position);
149
+
150
+ const [categoriesResult, othersResult] = await Promise.all([
151
+ Promise.all(categoryDataPromises),
152
+ Promise.all(others.map(ch => fetchAnyChannelData(ch, options))),
153
+ ]);
154
+
155
+ return {
156
+ categories: categoriesResult,
157
+ others: othersResult,
158
+ };
159
+ }
160
+
161
+ module.exports = {
162
+ getBans,
163
+ getMembers,
164
+ getRoles,
165
+ getEmojis,
166
+ getChannels,
167
+ };
@@ -0,0 +1,144 @@
1
+ 'use strict';
2
+
3
+ const SnowflakeUtil = require('../SnowflakeUtil');
4
+ const createMaster = require('./create');
5
+ const loadMaster = require('./load');
6
+ const { clearGuild, fetchBuffer } = require('./util');
7
+
8
+ /**
9
+ * Creates a guild backup
10
+ * @param {import('../../structures/Guild').Guild} guild
11
+ * @param {Object} [options] Backup options
12
+ * @returns {Promise<Object>}
13
+ */
14
+ async function create(guild, options = {}) {
15
+ const normalizedOptions = {
16
+ backupID: null,
17
+ maxMessagesPerChannel: 10,
18
+ doNotBackup: ['bans', 'emojis'],
19
+ backupMembers: false,
20
+ saveImages: '',
21
+ ...options,
22
+ backupID: options.backupID ?? options.backupId ?? null,
23
+ };
24
+
25
+ const backupData = {
26
+ name: guild.name,
27
+ verificationLevel: guild.verificationLevel,
28
+ explicitContentFilter: guild.explicitContentFilter,
29
+ defaultMessageNotifications: guild.defaultMessageNotifications,
30
+ afk: guild.afkChannel ? { name: guild.afkChannel.name, timeout: guild.afkTimeout } : null,
31
+ widget: {
32
+ enabled: guild.widgetEnabled,
33
+ channel: guild.widgetChannel ? guild.widgetChannel.name : null,
34
+ },
35
+ community: {
36
+ enabled: guild.features.includes('COMMUNITY'),
37
+ systemChannelFlags: guild.systemChannelFlags ? guild.systemChannelFlags.bitfield : null,
38
+ systemChannelId: guild.systemChannelId,
39
+ rulesChannelId: guild.rulesChannelId,
40
+ publicUpdatesChannelId: guild.publicUpdatesChannelId,
41
+ safetyAlertsChannelId: guild.safetyAlertsChannelId || null,
42
+ },
43
+ channels: { categories: [], others: [] },
44
+ roles: [],
45
+ bans: [],
46
+ emojis: [],
47
+ members: [],
48
+ createdTimestamp: Date.now(),
49
+ guildID: guild.id,
50
+ id: normalizedOptions.backupID ?? SnowflakeUtil.generate(Date.now()),
51
+ };
52
+
53
+ if (guild.iconURL()) {
54
+ if (normalizedOptions.saveImages === 'base64') {
55
+ backupData.iconBase64 = (await fetchBuffer(guild.iconURL({ dynamic: true }))).toString('base64');
56
+ }
57
+ backupData.iconURL = guild.iconURL({ dynamic: true });
58
+ }
59
+
60
+ if (guild.splashURL()) {
61
+ if (normalizedOptions.saveImages === 'base64') {
62
+ backupData.splashBase64 = (await fetchBuffer(guild.splashURL())).toString('base64');
63
+ }
64
+ backupData.splashURL = guild.splashURL();
65
+ }
66
+
67
+ if (guild.bannerURL()) {
68
+ if (normalizedOptions.saveImages === 'base64') {
69
+ backupData.bannerBase64 = (await fetchBuffer(guild.bannerURL())).toString('base64');
70
+ }
71
+ backupData.bannerURL = guild.bannerURL();
72
+ }
73
+
74
+ if (normalizedOptions.backupMembers) {
75
+ backupData.members = await createMaster.getMembers(guild);
76
+ }
77
+
78
+ if (!normalizedOptions.doNotBackup.includes('bans')) {
79
+ backupData.bans = await createMaster.getBans(guild);
80
+ }
81
+
82
+ if (!normalizedOptions.doNotBackup.includes('roles')) {
83
+ backupData.roles = await createMaster.getRoles(guild);
84
+ }
85
+
86
+ if (!normalizedOptions.doNotBackup.includes('emojis')) {
87
+ backupData.emojis = await createMaster.getEmojis(guild, normalizedOptions);
88
+ }
89
+
90
+ if (!normalizedOptions.doNotBackup.includes('channels')) {
91
+ backupData.channels = await createMaster.getChannels(guild, normalizedOptions);
92
+ }
93
+
94
+ return backupData;
95
+ }
96
+
97
+ /**
98
+ * Loads a backup into a guild
99
+ * @param {Object} backupData
100
+ * @param {import('../../structures/Guild').Guild} guild
101
+ * @param {Object} [options] Load options
102
+ * @returns {Promise<Object>}
103
+ */
104
+ async function load(backupData, guild, options = {}) {
105
+ if (!guild) throw new Error('Invalid guild');
106
+
107
+ const normalizedOptions = {
108
+ clearGuildBeforeRestore: true,
109
+ maxMessagesPerChannel: 10,
110
+ ...options,
111
+ };
112
+
113
+ if (normalizedOptions.clearGuildBeforeRestore) {
114
+ await clearGuild(guild);
115
+ }
116
+
117
+ await Promise.all([loadMaster.loadConfig(guild, backupData), loadMaster.loadRoles(guild, backupData)]);
118
+ await new Promise(resolve => setTimeout(resolve, 3000));
119
+ await loadMaster.loadChannels(guild, backupData, normalizedOptions);
120
+
121
+ const restorePromises = [
122
+ loadMaster.loadAFK(guild, backupData),
123
+ loadMaster.loadEmbedChannel(guild, backupData),
124
+ ];
125
+
126
+ if (!normalizedOptions.doNotBackup?.includes('emojis')) {
127
+ restorePromises.push(loadMaster.loadEmojis(guild, backupData));
128
+ }
129
+
130
+ if (!normalizedOptions.doNotBackup?.includes('bans')) {
131
+ restorePromises.push(loadMaster.loadBans(guild, backupData));
132
+ }
133
+
134
+ await Promise.all(restorePromises);
135
+ await loadMaster.loadCommunity(guild, backupData);
136
+ await loadMaster.loadRoleChannelPermissions(guild, backupData);
137
+
138
+ return backupData;
139
+ }
140
+
141
+ module.exports = {
142
+ create,
143
+ load,
144
+ };
@@ -0,0 +1,293 @@
1
+ 'use strict';
2
+
3
+ const Permissions = require('../Permissions');
4
+ const { loadCategory, loadChannel } = require('./util');
5
+
6
+ /**
7
+ * Restores the guild configuration
8
+ * @param {import('../../structures/Guild').Guild} guild
9
+ * @param {Object} backupData
10
+ * @returns {Promise<void>}
11
+ */
12
+ async function loadConfig(guild, backupData) {
13
+ const configPromises = [];
14
+
15
+ if (backupData.name) configPromises.push(guild.setName(backupData.name));
16
+ if (backupData.iconBase64) {
17
+ configPromises.push(guild.setIcon(Buffer.from(backupData.iconBase64, 'base64')));
18
+ } else if (backupData.iconURL) {
19
+ configPromises.push(guild.setIcon(backupData.iconURL));
20
+ }
21
+ if (backupData.splashBase64) {
22
+ configPromises.push(guild.setSplash(Buffer.from(backupData.splashBase64, 'base64')));
23
+ } else if (backupData.splashURL) {
24
+ configPromises.push(guild.setSplash(backupData.splashURL));
25
+ }
26
+ if (backupData.bannerBase64) {
27
+ configPromises.push(guild.setBanner(Buffer.from(backupData.bannerBase64, 'base64')));
28
+ } else if (backupData.bannerURL) {
29
+ configPromises.push(guild.setBanner(backupData.bannerURL));
30
+ }
31
+ if (backupData.verificationLevel) {
32
+ configPromises.push(guild.setVerificationLevel(backupData.verificationLevel));
33
+ }
34
+ if (backupData.defaultMessageNotifications) {
35
+ configPromises.push(guild.setDefaultMessageNotifications(backupData.defaultMessageNotifications));
36
+ }
37
+
38
+ const changeableExplicitLevel = guild.features.includes('COMMUNITY');
39
+ if (backupData.explicitContentFilter && changeableExplicitLevel) {
40
+ configPromises.push(guild.setExplicitContentFilter(backupData.explicitContentFilter));
41
+ }
42
+
43
+ await Promise.all(configPromises);
44
+ }
45
+
46
+ /**
47
+ * Restore the guild roles
48
+ * @param {import('../../structures/Guild').Guild} guild
49
+ * @param {Object} backupData
50
+ * @returns {Promise<void>}
51
+ */
52
+ async function loadRoles(guild, backupData) {
53
+ const rolePromises = [];
54
+
55
+ backupData.roles.forEach(roleData => {
56
+ if (roleData.isEveryone) {
57
+ rolePromises.push(
58
+ guild.roles.cache.get(guild.id).edit({
59
+ name: roleData.name,
60
+ colors: { primaryColor: roleData.color },
61
+ permissions: BigInt(roleData.permissions),
62
+ mentionable: roleData.mentionable,
63
+ }),
64
+ );
65
+ } else {
66
+ rolePromises.push(
67
+ guild.roles.create({
68
+ name: roleData.name,
69
+ colors: { primaryColor: roleData.color },
70
+ hoist: roleData.hoist,
71
+ permissions: BigInt(roleData.permissions),
72
+ mentionable: roleData.mentionable,
73
+ }),
74
+ );
75
+ }
76
+ });
77
+
78
+ await Promise.all(rolePromises);
79
+ }
80
+
81
+ /**
82
+ * Restore the guild channels
83
+ * @param {import('../../structures/Guild').Guild} guild
84
+ * @param {Object} backupData
85
+ * @param {Object} options
86
+ * @returns {Promise<void>}
87
+ */
88
+ async function loadChannels(guild, backupData, options) {
89
+ const loadChannelPromises = [];
90
+
91
+ backupData.channels.categories.forEach(categoryData => {
92
+ if (!categoryData.name) return;
93
+
94
+ loadChannelPromises.push(
95
+ (async () => {
96
+ try {
97
+ const createdCategory = await loadCategory(categoryData, guild);
98
+ await Promise.all(
99
+ categoryData.children
100
+ .filter(channelData => channelData.name)
101
+ .map(channelData => loadChannel(channelData, guild, createdCategory, options)),
102
+ );
103
+ } catch {
104
+ // Continue restoring other categories
105
+ }
106
+ })(),
107
+ );
108
+ });
109
+
110
+ backupData.channels.others.forEach(channelData => {
111
+ if (!channelData.name) return;
112
+ loadChannelPromises.push(loadChannel(channelData, guild, null, options));
113
+ });
114
+
115
+ await Promise.all(loadChannelPromises);
116
+ }
117
+
118
+ /**
119
+ * Restore the afk configuration
120
+ * @param {import('../../structures/Guild').Guild} guild
121
+ * @param {Object} backupData
122
+ * @returns {Promise<void>}
123
+ */
124
+ async function loadAFK(guild, backupData) {
125
+ if (!backupData.afk) return;
126
+
127
+ await Promise.all([
128
+ guild.setAFKChannel(
129
+ guild.channels.cache.find(ch => ch.name === backupData.afk.name && ch.type === 'GUILD_VOICE'),
130
+ ),
131
+ guild.setAFKTimeout(backupData.afk.timeout),
132
+ ]);
133
+ }
134
+
135
+ /**
136
+ * Restore guild emojis
137
+ * @param {import('../../structures/Guild').Guild} guild
138
+ * @param {Object} backupData
139
+ * @returns {Promise<void>}
140
+ */
141
+ async function loadEmojis(guild, backupData) {
142
+ await Promise.all(
143
+ backupData.emojis.map(emoji => {
144
+ if (emoji.url) return guild.emojis.create(emoji.url, emoji.name);
145
+ if (emoji.base64) return guild.emojis.create(Buffer.from(emoji.base64, 'base64'), emoji.name);
146
+ return Promise.resolve();
147
+ }),
148
+ );
149
+ }
150
+
151
+ /**
152
+ * Restore guild bans
153
+ * @param {import('../../structures/Guild').Guild} guild
154
+ * @param {Object} backupData
155
+ * @returns {Promise<void>}
156
+ */
157
+ async function loadBans(guild, backupData) {
158
+ await Promise.all(
159
+ backupData.bans.map(ban =>
160
+ guild.members.ban(ban.id, {
161
+ reason: ban.reason,
162
+ }),
163
+ ),
164
+ );
165
+ }
166
+
167
+ /**
168
+ * Restore embed channel configuration
169
+ * @param {import('../../structures/Guild').Guild} guild
170
+ * @param {Object} backupData
171
+ * @returns {Promise<void>}
172
+ */
173
+ async function loadEmbedChannel(guild, backupData) {
174
+ if (!backupData.widget.channel) return;
175
+
176
+ await guild.setWidgetSettings({
177
+ enabled: backupData.widget.enabled,
178
+ channel: guild.channels.cache.find(ch => ch.name === backupData.widget.channel),
179
+ });
180
+ }
181
+
182
+ /**
183
+ * Restore community settings
184
+ * @param {import('../../structures/Guild').Guild} guild
185
+ * @param {Object} backupData
186
+ * @returns {Promise<void>}
187
+ */
188
+ async function loadCommunity(guild, backupData) {
189
+ if (!backupData.community) return;
190
+
191
+ const rulesChannelData =
192
+ backupData.channels.categories.find(c => c.children.find(ch => ch.rulesChannel))?.children.find(
193
+ ch => ch.rulesChannel,
194
+ ) || backupData.channels.others.find(c => c.rulesChannel);
195
+ const publicUpdatesChannelData =
196
+ backupData.channels.categories
197
+ .find(c => c.children.find(ch => ch.publicUpdatesChannel))
198
+ ?.children.find(ch => ch.publicUpdatesChannel) ||
199
+ backupData.channels.others.find(c => c.publicUpdatesChannel);
200
+
201
+ try {
202
+ const isCommunityEnabled = guild.features.includes('COMMUNITY');
203
+ const shouldBeCommunityEnabled = backupData.community.enabled;
204
+
205
+ if (shouldBeCommunityEnabled && !isCommunityEnabled) {
206
+ const rulesChannel =
207
+ rulesChannelData &&
208
+ guild.channels.cache.find(ch => ch.name === rulesChannelData.name && ch.type === 'GUILD_TEXT');
209
+ const publicUpdatesChannel =
210
+ publicUpdatesChannelData &&
211
+ guild.channels.cache
212
+ .filter(
213
+ ch =>
214
+ ch.name === publicUpdatesChannelData.name &&
215
+ ch.type === 'GUILD_TEXT' &&
216
+ ch.id !== rulesChannel?.id,
217
+ )
218
+ .first();
219
+
220
+ if (rulesChannel && publicUpdatesChannel) {
221
+ await guild.setCommunity(true, publicUpdatesChannel, rulesChannel, 'Backup');
222
+ await new Promise(resolve => setTimeout(resolve, 3000));
223
+ }
224
+ } else if (shouldBeCommunityEnabled && isCommunityEnabled) {
225
+ const currentRulesChannel = guild.rulesChannel;
226
+ const currentPublicUpdatesChannel = guild.publicUpdatesChannel;
227
+
228
+ const newRulesChannel =
229
+ rulesChannelData && guild.channels.cache.find(ch => ch.name === rulesChannelData.name);
230
+ const newPublicUpdatesChannel =
231
+ publicUpdatesChannelData &&
232
+ guild.channels.cache.find(ch => ch.name === publicUpdatesChannelData.name);
233
+
234
+ const rulesChannelChanged = newRulesChannel && currentRulesChannel?.id !== newRulesChannel.id;
235
+ const updatesChannelChanged =
236
+ newPublicUpdatesChannel && currentPublicUpdatesChannel?.id !== newPublicUpdatesChannel.id;
237
+
238
+ if ((rulesChannelChanged || updatesChannelChanged) && newRulesChannel && newPublicUpdatesChannel) {
239
+ await guild.setRulesChannel(newRulesChannel.id).catch(() => false);
240
+ await guild.setPublicUpdatesChannel(newPublicUpdatesChannel.id).catch(() => false);
241
+ currentPublicUpdatesChannel?.delete().catch(() => false);
242
+ currentRulesChannel?.delete().catch(() => false);
243
+ }
244
+ } else if (!shouldBeCommunityEnabled && isCommunityEnabled) {
245
+ await guild.setCommunity(false, null, null, 'Backup');
246
+ }
247
+ } catch {
248
+ // Ignore community restore errors
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Restore role channel permissions
254
+ * @param {import('../../structures/Guild').Guild} guild
255
+ * @param {Object} backupData
256
+ * @returns {Promise<void>}
257
+ */
258
+ async function loadRoleChannelPermissions(guild, backupData) {
259
+ for (const roleData of backupData.roles) {
260
+ if (!roleData.channelPermissions) continue;
261
+
262
+ let role = guild.roles.cache.find(r => r.name === roleData.name);
263
+ if (!role && roleData.isEveryone) role = guild.roles.cache.get(guild.id);
264
+ if (!role) continue;
265
+
266
+ for (const [channelName, permissions] of Object.entries(roleData.channelPermissions)) {
267
+ const channel = guild.channels.cache.find(c => c.name === channelName);
268
+ if (!channel) continue;
269
+
270
+ try {
271
+ await channel.permissionOverwrites.edit(role.id, {
272
+ allow: Permissions.resolve(permissions.allow),
273
+ deny: Permissions.resolve(permissions.deny),
274
+ });
275
+ await new Promise(resolve => setTimeout(resolve, 200));
276
+ } catch {
277
+ // Continue with next permission
278
+ }
279
+ }
280
+ }
281
+ }
282
+
283
+ module.exports = {
284
+ loadConfig,
285
+ loadRoles,
286
+ loadChannels,
287
+ loadAFK,
288
+ loadEmojis,
289
+ loadBans,
290
+ loadEmbedChannel,
291
+ loadCommunity,
292
+ loadRoleChannelPermissions,
293
+ };