add white list for cleaner
This commit is contained in:
parent
b6f3ee262b
commit
9aca4c7995
247
commands/server/server-clean-keep.js
Normal file
247
commands/server/server-clean-keep.js
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
const { SlashCommandBuilder, MessageFlags } = require('discord.js');
|
||||||
|
const fs = require('node:fs/promises');
|
||||||
|
const path = require('node:path');
|
||||||
|
|
||||||
|
const ROLE_RYGAINLAND = '1444684935632912394';
|
||||||
|
const KEEP_LIST_FILE_PATH = path.resolve(__dirname, 'server-clean-keep.json');
|
||||||
|
|
||||||
|
const normUid32 = (val) => {
|
||||||
|
if (!val) return '';
|
||||||
|
const s = String(val).replace(/-/g, '').trim().toLowerCase();
|
||||||
|
if (s.length !== 32) return '';
|
||||||
|
if (!/^[0-9a-f]{32}$/.test(s)) return '';
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function readKeepList() {
|
||||||
|
try {
|
||||||
|
const raw = await fs.readFile(KEEP_LIST_FILE_PATH, 'utf8');
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
|
||||||
|
const discordIds = Array.isArray(parsed.discordIds)
|
||||||
|
? parsed.discordIds.map((x) => String(x || '').trim()).filter(Boolean)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const playerIds = Array.isArray(parsed.playerIds)
|
||||||
|
? parsed.playerIds.map(normUid32).filter(Boolean)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
discordIds: [...new Set(discordIds)],
|
||||||
|
playerIds: [...new Set(playerIds)],
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
if (error && error.code === 'ENOENT') {
|
||||||
|
return { discordIds: [], playerIds: [] };
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function writeKeepList(keepList) {
|
||||||
|
const payload = {
|
||||||
|
discordIds: [...new Set((keepList.discordIds || []).map((x) => String(x || '').trim()).filter(Boolean))],
|
||||||
|
playerIds: [...new Set((keepList.playerIds || []).map(normUid32).filter(Boolean))],
|
||||||
|
};
|
||||||
|
|
||||||
|
await fs.writeFile(KEEP_LIST_FILE_PATH, JSON.stringify(payload, null, 2), 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('server-clean-keep')
|
||||||
|
.setDescription('Gérer la liste blanche des joueurs protégés du /server-clean')
|
||||||
|
.addSubcommand((sub) =>
|
||||||
|
sub
|
||||||
|
.setName('list')
|
||||||
|
.setDescription('Afficher la liste blanche actuelle')
|
||||||
|
)
|
||||||
|
.addSubcommand((sub) =>
|
||||||
|
sub
|
||||||
|
.setName('add-discord')
|
||||||
|
.setDescription('Ajouter un compte Discord à la liste blanche')
|
||||||
|
.addUserOption((opt) =>
|
||||||
|
opt
|
||||||
|
.setName('membre')
|
||||||
|
.setDescription('Compte Discord à protéger')
|
||||||
|
.setRequired(true)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.addSubcommand((sub) =>
|
||||||
|
sub
|
||||||
|
.setName('remove-discord')
|
||||||
|
.setDescription('Retirer un compte Discord de la liste blanche')
|
||||||
|
.addUserOption((opt) =>
|
||||||
|
opt
|
||||||
|
.setName('membre')
|
||||||
|
.setDescription('Compte Discord à retirer')
|
||||||
|
.setRequired(true)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.addSubcommand((sub) =>
|
||||||
|
sub
|
||||||
|
.setName('add-player')
|
||||||
|
.setDescription('Ajouter un Player ID Palworld à la liste blanche')
|
||||||
|
.addStringOption((opt) =>
|
||||||
|
opt
|
||||||
|
.setName('player-id')
|
||||||
|
.setDescription('UID hexadécimal (32 caractères)')
|
||||||
|
.setRequired(true)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.addSubcommand((sub) =>
|
||||||
|
sub
|
||||||
|
.setName('remove-player')
|
||||||
|
.setDescription('Retirer un Player ID Palworld de la liste blanche')
|
||||||
|
.addStringOption((opt) =>
|
||||||
|
opt
|
||||||
|
.setName('player-id')
|
||||||
|
.setDescription('UID hexadécimal (32 caractères)')
|
||||||
|
.setRequired(true)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
if (!interaction.member.roles.cache.has(ROLE_RYGAINLAND)) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: '❌ Il faut avoir le rôle Rygainland pour utiliser cette commande.',
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sub = interaction.options.getSubcommand();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const keepList = await readKeepList();
|
||||||
|
|
||||||
|
if (sub === 'list') {
|
||||||
|
const discordText = keepList.discordIds.length
|
||||||
|
? keepList.discordIds.map((id) => `- <@${id}> (${id})`).join('\n')
|
||||||
|
: '- (vide)';
|
||||||
|
|
||||||
|
const playerText = keepList.playerIds.length
|
||||||
|
? keepList.playerIds.map((id) => `- ${id}`).join('\n')
|
||||||
|
: '- (vide)';
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
content:
|
||||||
|
'🛡️ Liste blanche /server-clean\n\n' +
|
||||||
|
`Discord IDs (${keepList.discordIds.length})\n${discordText}\n\n` +
|
||||||
|
`Player IDs (${keepList.playerIds.length})\n${playerText}`,
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sub === 'add-discord') {
|
||||||
|
const user = interaction.options.getUser('membre', true);
|
||||||
|
if (!keepList.discordIds.includes(user.id)) {
|
||||||
|
keepList.discordIds.push(user.id);
|
||||||
|
await writeKeepList(keepList);
|
||||||
|
await interaction.reply({
|
||||||
|
content: `✅ Ajouté à la liste blanche Discord: <@${user.id}> (${user.id})`,
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
content: `ℹ️ Déjà présent dans la liste blanche Discord: <@${user.id}>`,
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sub === 'remove-discord') {
|
||||||
|
const user = interaction.options.getUser('membre', true);
|
||||||
|
const before = keepList.discordIds.length;
|
||||||
|
keepList.discordIds = keepList.discordIds.filter((id) => id !== user.id);
|
||||||
|
|
||||||
|
if (keepList.discordIds.length !== before) {
|
||||||
|
await writeKeepList(keepList);
|
||||||
|
await interaction.reply({
|
||||||
|
content: `✅ Retiré de la liste blanche Discord: <@${user.id}> (${user.id})`,
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
content: `ℹ️ Ce compte n'était pas dans la liste blanche Discord: <@${user.id}>`,
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sub === 'add-player') {
|
||||||
|
const raw = interaction.options.getString('player-id', true);
|
||||||
|
const uid = normUid32(raw);
|
||||||
|
if (!uid) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: '❌ Player ID invalide. Format attendu: 32 caractères hexadécimaux.',
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keepList.playerIds.includes(uid)) {
|
||||||
|
keepList.playerIds.push(uid);
|
||||||
|
await writeKeepList(keepList);
|
||||||
|
await interaction.reply({
|
||||||
|
content: `✅ Player ID ajouté à la liste blanche: ${uid}`,
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
content: `ℹ️ Player ID déjà présent dans la liste blanche: ${uid}`,
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sub === 'remove-player') {
|
||||||
|
const raw = interaction.options.getString('player-id', true);
|
||||||
|
const uid = normUid32(raw);
|
||||||
|
if (!uid) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: '❌ Player ID invalide. Format attendu: 32 caractères hexadécimaux.',
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const before = keepList.playerIds.length;
|
||||||
|
keepList.playerIds = keepList.playerIds.filter((id) => id !== uid);
|
||||||
|
|
||||||
|
if (keepList.playerIds.length !== before) {
|
||||||
|
await writeKeepList(keepList);
|
||||||
|
await interaction.reply({
|
||||||
|
content: `✅ Player ID retiré de la liste blanche: ${uid}`,
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
content: `ℹ️ Player ID non trouvé dans la liste blanche: ${uid}`,
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
content: '❌ Sous-commande inconnue.',
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur server-clean-keep:', error);
|
||||||
|
await interaction.reply({
|
||||||
|
content: `❌ Erreur: ${error.message || String(error)}`,
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
4
commands/server/server-clean-keep.json
Normal file
4
commands/server/server-clean-keep.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"discordIds": [],
|
||||||
|
"playerIds": []
|
||||||
|
}
|
||||||
@ -20,6 +20,8 @@ const execFileAsync = promisify(execFile);
|
|||||||
|
|
||||||
const ROLE_RYGAINLAND = '1444684935632912394';
|
const ROLE_RYGAINLAND = '1444684935632912394';
|
||||||
|
|
||||||
|
const keepListFilePath = path.resolve(__dirname, 'server-clean-keep.json');
|
||||||
|
|
||||||
const normUid32 = (val) => {
|
const normUid32 = (val) => {
|
||||||
if (!val) return '';
|
if (!val) return '';
|
||||||
const s = String(val).replace(/-/g, '').trim().toLowerCase();
|
const s = String(val).replace(/-/g, '').trim().toLowerCase();
|
||||||
@ -30,6 +32,45 @@ const normUid32 = (val) => {
|
|||||||
|
|
||||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||||
|
|
||||||
|
const parseCsv = (val) =>
|
||||||
|
String(val || '')
|
||||||
|
.split(',')
|
||||||
|
.map((x) => x.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
async function loadKeepList() {
|
||||||
|
const keepDiscordIds = new Set(parseCsv(process.env.SERVER_CLEAN_KEEP_DISCORD_IDS));
|
||||||
|
const keepPlayerIds = new Set(parseCsv(process.env.SERVER_CLEAN_KEEP_PLAYER_IDS).map(normUid32).filter(Boolean));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const raw = await fs.readFile(keepListFilePath, 'utf8');
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
|
||||||
|
if (Array.isArray(parsed.discordIds)) {
|
||||||
|
for (const id of parsed.discordIds) {
|
||||||
|
const clean = String(id || '').trim();
|
||||||
|
if (clean) keepDiscordIds.add(clean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(parsed.playerIds)) {
|
||||||
|
for (const uid of parsed.playerIds) {
|
||||||
|
const clean = normUid32(uid);
|
||||||
|
if (clean) keepPlayerIds.add(clean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error && error.code !== 'ENOENT') {
|
||||||
|
throw new Error(`Impossible de lire ${path.basename(keepListFilePath)}: ${error.message || String(error)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
keepDiscordIds,
|
||||||
|
keepPlayerIds,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async function waitForServerState({ headers, baseUrl, serverId, wanted, timeoutMs }) {
|
async function waitForServerState({ headers, baseUrl, serverId, wanted, timeoutMs }) {
|
||||||
const started = Date.now();
|
const started = Date.now();
|
||||||
while (Date.now() - started < timeoutMs) {
|
while (Date.now() - started < timeoutMs) {
|
||||||
@ -158,10 +199,26 @@ module.exports = {
|
|||||||
for (const l of targetLinks) byDiscord.set(String(l.discord_id), l);
|
for (const l of targetLinks) byDiscord.set(String(l.discord_id), l);
|
||||||
const deduped = [...byDiscord.values()];
|
const deduped = [...byDiscord.values()];
|
||||||
|
|
||||||
|
const keepList = await loadKeepList();
|
||||||
|
const filteredLinks = [];
|
||||||
|
let keepListExcludedCount = 0;
|
||||||
|
|
||||||
|
for (const link of deduped) {
|
||||||
|
const discordId = String(link.discord_id || '').trim();
|
||||||
|
const uid = normUid32(link.player_id);
|
||||||
|
|
||||||
|
if (keepList.keepDiscordIds.has(discordId) || (uid && keepList.keepPlayerIds.has(uid))) {
|
||||||
|
keepListExcludedCount += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredLinks.push(link);
|
||||||
|
}
|
||||||
|
|
||||||
const uidsToDelete = [];
|
const uidsToDelete = [];
|
||||||
const missingPlayerId = [];
|
const missingPlayerId = [];
|
||||||
|
|
||||||
for (const link of deduped) {
|
for (const link of filteredLinks) {
|
||||||
const uid = normUid32(link.player_id);
|
const uid = normUid32(link.player_id);
|
||||||
if (!uid) {
|
if (!uid) {
|
||||||
missingPlayerId.push(link);
|
missingPlayerId.push(link);
|
||||||
@ -174,7 +231,10 @@ module.exports = {
|
|||||||
const extra = missingPlayerId.length
|
const extra = missingPlayerId.length
|
||||||
? `\n⚠️ ${missingPlayerId.length} membre(s) ciblé(s) sans Player ID en DB (impossible à supprimer automatiquement).`
|
? `\n⚠️ ${missingPlayerId.length} membre(s) ciblé(s) sans Player ID en DB (impossible à supprimer automatiquement).`
|
||||||
: '';
|
: '';
|
||||||
await interaction.editReply(`✅ Aucun joueur supprimable trouvé.${extra}`);
|
const keepText = keepListExcludedCount
|
||||||
|
? `\n🛡️ Exclus via liste blanche: ${keepListExcludedCount}`
|
||||||
|
: '';
|
||||||
|
await interaction.editReply(`✅ Aucun joueur supprimable trouvé.${extra}${keepText}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,6 +346,10 @@ module.exports = {
|
|||||||
? `\n⚠️ Non supprimés (pas de Player ID en DB): ${missingPlayerId.length}`
|
? `\n⚠️ Non supprimés (pas de Player ID en DB): ${missingPlayerId.length}`
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
|
const keepText = keepListExcludedCount
|
||||||
|
? `\n🛡️ Exclus via liste blanche: ${keepListExcludedCount}`
|
||||||
|
: '';
|
||||||
|
|
||||||
const okText = running ? '✅ Nettoyage terminé et serveur redémarré.' : '⚠️ Nettoyage terminé, mais le serveur n\'a pas confirmé son état running.';
|
const okText = running ? '✅ Nettoyage terminé et serveur redémarré.' : '⚠️ Nettoyage terminé, mais le serveur n\'a pas confirmé son état running.';
|
||||||
|
|
||||||
// Keep output short; deleterReport can be long.
|
// Keep output short; deleterReport can be long.
|
||||||
@ -295,7 +359,7 @@ module.exports = {
|
|||||||
|
|
||||||
await interaction.editReply(
|
await interaction.editReply(
|
||||||
`${okText}\n` +
|
`${okText}\n` +
|
||||||
`👥 Joueurs supprimés (ciblés): ${uidsToDelete.length}${missingText}\n` +
|
`👥 Joueurs supprimés (ciblés): ${uidsToDelete.length}${missingText}${keepText}\n` +
|
||||||
(reportSummary ? `📄 players-deleter: ${reportSummary}` : '')
|
(reportSummary ? `📄 players-deleter: ${reportSummary}` : '')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user