From 14a18302ce6451129b72b2d9a132e375c8ee48d7 Mon Sep 17 00:00:00 2001 From: Mateusz Zaremba <0.zaremate@gmail.com> Date: Thu, 3 Sep 2026 16:44:51 +0200 Subject: [PATCH] feat: added webhook support --- multi-acc.js | 517 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 306 insertions(+), 211 deletions(-) diff --git a/multi-acc.js b/multi-acc.js index 0c52ee9..551ce73 100644 --- a/multi-acc.js +++ b/multi-acc.js @@ -11,286 +11,381 @@ * The original author attribution and copyright notice must remain. */ +// Dependencies: LuckPerms, KubeJSHTTP -// Dependencies: LuckPerms +var DISCORD_WEBHOOK_URL = "REPLACE_ME"; - -var DISCORD_WEBHOOK_URL = 'REPLACE_ME'; - -var MULTIACCOUNT_PERMISSION_CHECKIP = 'multiaccount.command.checkip'; -var MULTIACCOUNT_PERMISSION_ALERT = 'multiaccount.alert'; +var MULTIACCOUNT_PERMISSION_CHECKIP = "multiaccount.command.checkip"; +var MULTIACCOUNT_PERMISSION_ALERT = "multiaccount.alert"; var LuckPermsApiProvider = Java.loadClass( - 'net.luckperms.api.LuckPermsProvider' + "net.luckperms.api.LuckPermsProvider", ); +var HttpAPI = Java.loadClass("com.awizo.KubeJSHTTP.HttpAPI"); + var luckPermsApi = null; var playerIPMap = new Map(); function getLuckPerms() { - if (luckPermsApi) { - return luckPermsApi; - } + if (luckPermsApi) { + return luckPermsApi; + } - try { - luckPermsApi = LuckPermsApiProvider.get(); + try { + luckPermsApi = LuckPermsApiProvider.get(); - console.log('[Multi-Account] luckPermsApi API loaded successfully!'); + console.log("[Multi-Account] luckPermsApi API loaded successfully!"); - return luckPermsApi; - } catch (error) { - return null; - } + return luckPermsApi; + } catch (error) { + return null; + } } function hasPermission(player, permission) { - var api = getLuckPerms(); + var api = getLuckPerms(); - if (!api || !player) { - return false; - } + if (!api || !player) { + return false; + } - try { - return api - .getUserManager() - .getUser(player.uuid) - .getCachedData() - .getPermissionData() - .checkPermission(permission) - .asBoolean(); - } catch (error) { - console.error( - '[Multi-Account] Failed to check permission ' + - permission + - ' for ' + - player.username + - ': ' + - error - ); + try { + return api + .getUserManager() + .getUser(player.uuid) + .getCachedData() + .getPermissionData() + .checkPermission(permission) + .asBoolean(); + } catch (error) { + console.error( + "[Multi-Account] Failed to check permission " + + permission + + " for " + + player.username + + ": " + + error, + ); - return false; - } + return false; + } } function getPlayerIP(player) { - try { - var ip = player.ipAddress; + try { + var ip = player.ipAddress; - if (!ip) { - return null; - } + if (!ip) { + return null; + } - if (ip.includes('/')) { - ip = ip.split('/')[1]; - } + if (ip.includes("/")) { + ip = ip.split("/")[1]; + } - if (ip.includes(':')) { - ip = ip.split(':')[0]; - } + if (ip.includes(":")) { + ip = ip.split(":")[0]; + } - return ip; - } catch (error) { - console.warn( - '[Multi-Account] Failed to get IP for ' + - player.username + - ': ' + - error - ); + return ip; + } catch (error) { + console.warn( + "[Multi-Account] Failed to get IP for " + + player.username + + ": " + + error, + ); - return null; - } + return null; + } } function sendPlayerNotification(player, otherPlayers) { - player.tell( - '§c[ALERT] §fMulti-account detected! Other accounts on your IP: §e' + - otherPlayers.join(', ') - ); + player.tell( + "§c[ALERT] §fMulti-account detected! Other accounts on your IP: §e" + + otherPlayers.join(", "), + ); } function notifyStaff(server, playerName, otherPlayers) { - var message = - '§c[ADMIN] §fPlayer §6' + - playerName + - '§f has the same IP as: §e' + - otherPlayers.join(', '); + var message = + "§c[ADMIN] §fPlayer §6" + + playerName + + "§f has the same IP as: §e" + + otherPlayers.join(", "); - server.players.forEach(player => { - if (hasPermission(player, MULTIACCOUNT_PERMISSION_ALERT)) { - player.tell(message); - } - }); + server.players.forEach((player) => { + if (hasPermission(player, MULTIACCOUNT_PERMISSION_ALERT)) { + player.tell(message); + } + }); } -function sendDiscordNotification(server, playerName, otherPlayers) { - var payload = { - content: '⚠️ **MULTI-ACCOUNT DETECTED**', - embeds: [{ - title: 'Multi-Account Alert', - color: 16711680, - fields: [ - { - name: 'Player', - value: playerName, - inline: true - }, - { - name: 'Other Accounts on IP', - value: otherPlayers.join(', '), - inline: false - }, - { - name: 'Timestamp', - value: new Date().toISOString(), - inline: true - } - ] - }] - }; +function sendDiscordNotification(player, otherPlayers) { + var playerName = + player && typeof player.getName === "function" + ? player.getName().getString() + : "Unknown"; + + try { + HttpAPI.post( + DISCORD_WEBHOOK_URL, + JSON.stringify({ + embeds: [ + { + title: "Multi-Account Alert", + description: + "Player: " + + playerName + + "\n" + + "Other Accounts on IP: " + + otherPlayers.join(", ") + + "\n" + + "Timestamp: ", + color: 3447003, + footer: { + text: "Sputnik watch", + }, + }, + ], + }), + ); + } catch (e) { + console.error("[SputnikWatch] Failed to send webhook: " + e); + } +} + +function rebuildPlayerIPMap(server) { + playerIPMap.clear(); + + server.players.forEach((player) => { + var name = player.username; + var ip = getPlayerIP(player); + + if (!ip) { + return; + } + + if (!playerIPMap.has(ip)) { + playerIPMap.set(ip, new Set()); + } + + playerIPMap.get(ip).add(name); + }); + + console.log( + "[Multi-Account] IP map rebuilt. Tracking " + + playerIPMap.size + + " IP(s).", + ); +} + +function sendCheckIPWebhook(player, groups) { + var playerName = + player && typeof player.getName === "function" + ? player.getName().getString() + : "Unknown"; + + if (groups.length === 0) { + return; + } + + var groupText = groups + .map((players, index) => { + return "**Group " + (index + 1) + ":** " + players.join(", "); + }) + .join("\n"); + + try { + HttpAPI.post( + DISCORD_WEBHOOK_URL, + JSON.stringify({ + embeds: [ + { + title: "IP Check", + description: + "Requested by: " + + playerName + + "\n\n" + + groupText + + "\n\n" + + "Timestamp: ", + color: 3447003, + footer: { + text: "Sputnik watch", + }, + }, + ], + }), + ); + + console.log("[Multi-Account] /checkip results sent to Discord."); + } catch (e) { + console.error("[SputnikWatch] Failed to send /checkip webhook: " + e); + } } function checkMultiAccount(server, player) { - var name = player.username; - var ip = getPlayerIP(player); + var name = player.username; + var ip = getPlayerIP(player); - if (!ip) { - return; - } + if (!ip) { + return; + } - if (!playerIPMap.has(ip)) { - playerIPMap.set(ip, new Set()); - } + if (!playerIPMap.has(ip)) { + playerIPMap.set(ip, new Set()); + } - var players = playerIPMap.get(ip); - var otherPlayers = Array.from(players).filter( - playerName => playerName !== name - ); + var players = playerIPMap.get(ip); - if (otherPlayers.length > 0) { - console.log( - '[Multi-Account] ALERT: ' + - name + - ' shares IP with: ' + - otherPlayers.join(', ') - ); + var otherPlayers = Array.from(players).filter( + (playerName) => playerName !== name, + ); - sendPlayerNotification(player, otherPlayers); - notifyStaff(server, name, otherPlayers); - sendDiscordNotification(server, name, otherPlayers); - } + if (otherPlayers.length > 0) { + console.log( + "[Multi-Account] ALERT: " + + name + + " shares IP with: " + + otherPlayers.join(", "), + ); - players.add(name); + sendPlayerNotification(player, otherPlayers); + + notifyStaff(server, name, otherPlayers); + + sendDiscordNotification(player, otherPlayers); + } + + players.add(name); } function removePlayer(name) { - for (var [ip, players] of playerIPMap) { - if (!players.delete(name)) { - continue; - } + for (var [ip, players] of playerIPMap) { + if (!players.delete(name)) { + continue; + } - console.log( - '[Multi-Account] Removed ' + - name + - ' from IP tracking (' + - ip + - ')' - ); + console.log( + "[Multi-Account] Removed " + + name + + " from IP tracking (" + + ip + + ")", + ); - if (players.size === 0) { - playerIPMap.delete(ip); - } + if (players.size === 0) { + playerIPMap.delete(ip); + } - break; - } + break; + } } -PlayerEvents.loggedIn(event => { - var player = event.player; - var server = event.server; +PlayerEvents.loggedIn((event) => { + var player = event.player; + var server = event.server; - console.log( - '[Multi-Account] ' + - player.username + - ' joined from: ' + - getPlayerIP(player) - ); + console.log( + "[Multi-Account] " + + player.username + + " joined from: " + + getPlayerIP(player), + ); - server.schedule(10, () => { - checkMultiAccount(server, player); - }); + server.schedule(10, () => { + checkMultiAccount(server, player); + }); }); -PlayerEvents.loggedOut(event => { - var name = event.player.username; +PlayerEvents.loggedOut((event) => { + var name = event.player.username; - console.log( - '[Multi-Account] ' + - name + - ' left the server' - ); + console.log("[Multi-Account] " + name + " left the server"); - removePlayer(name); + removePlayer(name); }); -ServerEvents.commandRegistry(event => { - const { commands: Commands } = event; +ServerEvents.commandRegistry((event) => { + const { commands: Commands } = event; - event.register( - Commands.literal('checkip') - .requires(source => - source.isPlayer() && - ( - source.hasPermission(3) || - hasPermission( - source.player, - MULTIACCOUNT_PERMISSION_CHECKIP - ) - ) - ) - .executes(ctx => { - var source = ctx.source; - var group = 1; - var found = false; + event.register( + Commands.literal("checkip") + .requires( + (source) => + source.isPlayer() && + (source.hasPermission(3) || + hasPermission( + source.player, + MULTIACCOUNT_PERMISSION_CHECKIP, + )), + ) + .executes((ctx) => { + var source = ctx.source; + var server = source.server; - source.sendSuccess( - Text.of('§6Players sharing an IP:'), - false - ); + var group = 1; + var found = false; + var groups = []; - for (var players of playerIPMap.values()) { - if (players.size <= 1) { - continue; - } + rebuildPlayerIPMap(server); - found = true; + source.sendSuccess(Text.of("§6Players sharing an IP:"), false); - source.sendSuccess( - Text.of( - `§7Group ${group}: §e${Array.from(players).join(', ')}` - ), - false - ); + for (var players of playerIPMap.values()) { + if (players.size <= 1) { + continue; + } - group++; - } + found = true; - if (!found) { - source.sendSuccess( - Text.of('§7No players currently share an IP.'), - false - ); - } + var playerList = Array.from(players); - return 1; - }) - ); + groups.push(playerList); + + source.sendSuccess( + Text.of(`§7Group ${group}: §e${playerList.join(", ")}`), + false, + ); + + group++; + } + + if (!found) { + source.sendSuccess( + Text.of("§7No players currently share an IP."), + false, + ); + } else { + sendCheckIPWebhook(source.player, groups); + } + + return 1; + }), + ); }); -console.log('[Multi-Account] Script loaded successfully!'); -console.log('[Multi-Account] /checkip permission: ' + MULTIACCOUNT_PERMISSION_CHECKIP); -console.log('[Multi-Account] Chat alert permission: ' + MULTIACCOUNT_PERMISSION_ALERT); -console.log('[Multi-Account] Commands: /checkip'); +console.log("[Multi-Account] Script loaded successfully!"); + console.log( - '[Multi-Account] Webhook: ' + - (DISCORD_WEBHOOK_URL !== 'REPLACE_ME' ? '✓ Configured' : '✗ Not configured') -); \ No newline at end of file + "[Multi-Account] /checkip permission: " + MULTIACCOUNT_PERMISSION_CHECKIP, +); + +console.log( + "[Multi-Account] Chat alert permission: " + MULTIACCOUNT_PERMISSION_ALERT, +); + +console.log("[Multi-Account] Commands: /checkip"); + +console.log( + "[Multi-Account] Webhook: " + + (DISCORD_WEBHOOK_URL !== "REPLACE_ME" + ? "✓ Configured" + : "✗ Not configured"), +);