| 1 | const { Client, Events, GatewayIntentBits } = require('discord.js');
|
| 2 | const { token, sftp, pterodactyl } = require('./config.json');
|
| 3 | const fetch = require('node-fetch');
|
| 4 | const SFTPClient = require('ssh2-sftp-client');
|
| 5 | const axios = require('axios');
|
| 6 |
|
| 7 | process.on('unhandledRejection', error => {
|
| 8 | console.error('Unhandled promise rejection:', error);
|
| 9 | });
|
| 10 |
|
| 11 | const client = new Client({
|
| 12 | intents: [
|
| 13 | GatewayIntentBits.Guilds,
|
| 14 | GatewayIntentBits.GuildMessages,
|
| 15 | GatewayIntentBits.MessageContent,
|
| 16 | GatewayIntentBits.GuildMembers
|
| 17 | ]
|
| 18 | });
|
| 19 |
|
| 20 | client.on(Events.MessageCreate, async message => {
|
| 21 | if (!message.content.startsWith('!whitelist') || message.author.bot) return;
|
| 22 |
|
| 23 | try {
|
| 24 | const args = message.content.slice('!whitelist'.length).trim().split(/ +/);
|
| 25 | const command = args.shift().toLowerCase();
|
| 26 |
|
| 27 | if (command === 'help') {
|
| 28 | const helpMessage = `**Whitelist Bot Commands:**\n\`\`\`
|
| 29 | 1. !whitelist <username>
|
| 30 | → Add a player to the whitelist
|
| 31 |
|
| 32 | 2. !whitelist remove <username>
|
| 33 | → Remove a player from the whitelist
|
| 34 |
|
| 35 | 3. !whitelist list
|
| 36 | → Show all whitelisted players
|
| 37 |
|
| 38 | 4. !whitelist check <username/uuid>
|
| 39 | → Look up a player's UUID or username
|
| 40 |
|
| 41 | 5. !whitelist help
|
| 42 | → Show this help message\`\`\``;
|
| 43 | await message.channel.send(helpMessage);
|
| 44 | return;
|
| 45 | }
|
| 46 |
|
| 47 | if (!message.member.roles.cache.has('REDACTED')) {
|
| 48 | return message.channel.send('You do not have permission to use this command!');
|
| 49 | }
|
| 50 |
|
| 51 | if (command === 'check') {
|
| 52 | const query = args[0];
|
| 53 | if (!query) {
|
| 54 | return message.channel.send('Please provide a username or UUID!');
|
| 55 | }
|
| 56 |
|
| 57 | const reply = await message.channel.send('Processing lookup request...');
|
| 58 |
|
| 59 | try {
|
| 60 | const isUUID = /^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$/i.test(query);
|
| 61 |
|
| 62 | if (isUUID) {
|
| 63 | const formattedUUID = query.replace(/-/g, '');
|
| 64 | const response = await fetch(`https://sessionserver.mojang.com/session/minecraft/profile/${formattedUUID}`);
|
| 65 | if (!response.ok) {
|
| 66 | return await reply.edit('Invalid UUID or player not found.');
|
| 67 | }
|
| 68 | const data = await response.json();
|
| 69 | await reply.edit(`**UUID Lookup Result:**\n\`\`\`\nUsername: ${data.name}\nUUID: ${query}\`\`\``);
|
| 70 | } else {
|
| 71 | const response = await fetch(`https://api.mojang.com/users/profiles/minecraft/${query}`);
|
| 72 | if (!response.ok) {
|
| 73 | return await reply.edit('Invalid username or player not found.');
|
| 74 | }
|
| 75 | const data = await response.json();
|
| 76 | const formattedUUID = data.id.replace(/(\w{8})(\w{4})(\w{4})(\w{4})(\w{12})/, '$1-$2-$3-$4-$5');
|
| 77 | await reply.edit(`**Username Lookup Result:**\n\`\`\`\nUsername: ${data.name}\nUUID: ${formattedUUID}\`\`\``);
|
| 78 | }
|
| 79 | } catch (error) {
|
| 80 | console.error('Lookup Error:', error);
|
| 81 | await reply.edit('An error occurred while looking up the player.');
|
| 82 | }
|
| 83 | return;
|
| 84 | }
|
| 85 |
|
| 86 | if (command === 'list') {
|
| 87 | const sftpClient = new SFTPClient();
|
| 88 | try {
|
| 89 | await sftpClient.connect({
|
| 90 | host: sftp.host,
|
| 91 | port: sftp.port,
|
| 92 | username: sftp.username,
|
| 93 | password: sftp.password
|
| 94 | });
|
| 95 |
|
| 96 | const whitelistData = await sftpClient.get(sftp.whitelistPath);
|
| 97 | const whitelist = JSON.parse(whitelistData.toString());
|
| 98 | const playerList = whitelist.map((player, index) => `${index + 1}. ${player.name}`).join('\n');
|
| 99 | const formattedMessage = `**Whitelisted Players (${whitelist.length}):**\n\`\`\`\n${playerList}\n\`\`\``;
|
| 100 | await message.channel.send(formattedMessage);
|
| 101 | } catch (error) {
|
| 102 | console.error('SFTP Error:', error);
|
| 103 | await message.channel.send('Failed to retrieve whitelist.');
|
| 104 | } finally {
|
| 105 | await sftpClient.end();
|
| 106 | }
|
| 107 | } else if (command === 'remove') {
|
| 108 | const ign = args[0];
|
| 109 | if (!ign) {
|
| 110 | return message.channel.send('Please provide a Minecraft username!');
|
| 111 | }
|
| 112 |
|
| 113 | const reply = await message.channel.send('Processing whitelist removal request...');
|
| 114 | const sftpClient = new SFTPClient();
|
| 115 | try {
|
| 116 | await sftpClient.connect({
|
| 117 | host: sftp.host,
|
| 118 | port: sftp.port,
|
| 119 | username: sftp.username,
|
| 120 | password: sftp.password
|
| 121 | });
|
| 122 |
|
| 123 | const whitelistData = await sftpClient.get(sftp.whitelistPath);
|
| 124 | let whitelist = JSON.parse(whitelistData.toString());
|
| 125 | const initialLength = whitelist.length;
|
| 126 | whitelist = whitelist.filter(player => player.name.toLowerCase() !== ign.toLowerCase());
|
| 127 |
|
| 128 | if (whitelist.length === initialLength) {
|
| 129 | await sftpClient.end();
|
| 130 | return await reply.edit(`${ign} is not whitelisted!`);
|
| 131 | }
|
| 132 |
|
| 133 | await sftpClient.put(Buffer.from(JSON.stringify(whitelist, null, 2)), sftp.whitelistPath);
|
| 134 | } catch (error) {
|
| 135 | console.error('SFTP Error:', error);
|
| 136 | return await reply.edit('Failed to update whitelist file');
|
| 137 | } finally {
|
| 138 | await sftpClient.end();
|
| 139 | }
|
| 140 |
|
| 141 | try {
|
| 142 | await axios.post(
|
| 143 | `${pterodactyl.apiUrl}/api/client/servers/${pterodactyl.serverId}/command`,
|
| 144 | { command: 'whitelist reload' },
|
| 145 | {
|
| 146 | headers: { 'Authorization': `Bearer ${pterodactyl.apiKey}` },
|
| 147 | timeout: 5000
|
| 148 | }
|
| 149 | );
|
| 150 | } catch (error) {
|
| 151 | console.error('Pterodactyl API Error:', error);
|
| 152 | return await reply.edit('Whitelist updated but failed to reload server whitelist');
|
| 153 | }
|
| 154 |
|
| 155 | await reply.edit(`Successfully removed ${ign} from the whitelist!`);
|
| 156 | } else {
|
| 157 | const ign = command;
|
| 158 | if (!ign) {
|
| 159 | return message.channel.send('Please provide a Minecraft username!');
|
| 160 | }
|
| 161 |
|
| 162 | const reply = await message.channel.send('Processing whitelist request...');
|
| 163 | const response = await fetch(`https://api.mojang.com/users/profiles/minecraft/${ign}`);
|
| 164 | if (!response.ok) {
|
| 165 | return await reply.edit('Failed to fetch player data from Mojang API');
|
| 166 | }
|
| 167 | const data = await response.json();
|
| 168 | if (!data?.id) {
|
| 169 | return await reply.edit('Invalid username!');
|
| 170 | }
|
| 171 |
|
| 172 | const sftpClient = new SFTPClient();
|
| 173 | try {
|
| 174 | await sftpClient.connect({
|
| 175 | host: sftp.host,
|
| 176 | port: sftp.port,
|
| 177 | username: sftp.username,
|
| 178 | password: sftp.password
|
| 179 | });
|
| 180 |
|
| 181 | let whitelist = [];
|
| 182 | try {
|
| 183 | const whitelistData = await sftpClient.get(sftp.whitelistPath);
|
| 184 | whitelist = JSON.parse(whitelistData.toString());
|
| 185 | } catch (error) {
|
| 186 | console.log('Creating new whitelist file');
|
| 187 | whitelist = [];
|
| 188 | }
|
| 189 |
|
| 190 | const isWhitelisted = whitelist.some(player =>
|
| 191 | player.uuid === data.id ||
|
| 192 | player.name.toLowerCase() === data.name.toLowerCase()
|
| 193 | );
|
| 194 |
|
| 195 | if (isWhitelisted) {
|
| 196 | await sftpClient.end();
|
| 197 | return await reply.edit(`${data.name} is already whitelisted!`);
|
| 198 | }
|
| 199 |
|
| 200 | whitelist.push({
|
| 201 | uuid: data.id,
|
| 202 | name: data.name
|
| 203 | });
|
| 204 |
|
| 205 | await sftpClient.put(Buffer.from(JSON.stringify(whitelist, null, 2)), sftp.whitelistPath);
|
| 206 | } catch (error) {
|
| 207 | console.error('SFTP Error:', error);
|
| 208 | return await reply.edit('Failed to update whitelist file');
|
| 209 | } finally {
|
| 210 | await sftpClient.end();
|
| 211 | }
|
| 212 |
|
| 213 | try {
|
| 214 | await axios.post(
|
| 215 | `${pterodactyl.apiUrl}/api/client/servers/${pterodactyl.serverId}/command`,
|
| 216 | { command: 'whitelist reload' },
|
| 217 | {
|
| 218 | headers: { 'Authorization': `Bearer ${pterodactyl.apiKey}` },
|
| 219 | timeout: 5000
|
| 220 | }
|
| 221 | );
|
| 222 | } catch (error) {
|
| 223 | console.error('Pterodactyl API Error:', error);
|
| 224 | return await reply.edit('Whitelist updated but failed to reload server whitelist');
|
| 225 | }
|
| 226 |
|
| 227 | await reply.edit(`Successfully whitelisted ${data.name}!`);
|
| 228 | }
|
| 229 | } catch (error) {
|
| 230 | console.error('Command Error:', error);
|
| 231 | try {
|
| 232 | await message.channel.send('An error occurred while processing the whitelist command.');
|
| 233 | } catch (e) {
|
| 234 | console.error('Failed to send error message:', e);
|
| 235 | }
|
| 236 | }
|
| 237 | });
|
| 238 |
|
| 239 | client.once(Events.ClientReady, c => {
|
| 240 | console.log(`Ready! Logged in as ${c.user.tag}`);
|
| 241 | client.user.setActivity('!whitelist help', { type: 0 });
|
| 242 | });
|
| 243 |
|
| 244 | client.on('error', error => {
|
| 245 | console.error('Discord client error:', error);
|
| 246 | });
|
| 247 |
|
| 248 | client.login(token);
|