feat: added webhook support

This commit is contained in:
2026-09-03 16:44:51 +02:00
parent 3296c37de3
commit 14a18302ce
+306 -211
View File
@@ -11,286 +11,381 @@
* The original author attribution and copyright notice must remain. * The original author attribution and copyright notice must remain.
*/ */
// Dependencies: LuckPerms, KubeJSHTTP
// Dependencies: LuckPerms var DISCORD_WEBHOOK_URL = "REPLACE_ME";
var MULTIACCOUNT_PERMISSION_CHECKIP = "multiaccount.command.checkip";
var DISCORD_WEBHOOK_URL = 'REPLACE_ME'; var MULTIACCOUNT_PERMISSION_ALERT = "multiaccount.alert";
var MULTIACCOUNT_PERMISSION_CHECKIP = 'multiaccount.command.checkip';
var MULTIACCOUNT_PERMISSION_ALERT = 'multiaccount.alert';
var LuckPermsApiProvider = Java.loadClass( var LuckPermsApiProvider = Java.loadClass(
'net.luckperms.api.LuckPermsProvider' "net.luckperms.api.LuckPermsProvider",
); );
var HttpAPI = Java.loadClass("com.awizo.KubeJSHTTP.HttpAPI");
var luckPermsApi = null; var luckPermsApi = null;
var playerIPMap = new Map(); var playerIPMap = new Map();
function getLuckPerms() { function getLuckPerms() {
if (luckPermsApi) { if (luckPermsApi) {
return luckPermsApi; return luckPermsApi;
} }
try { try {
luckPermsApi = LuckPermsApiProvider.get(); luckPermsApi = LuckPermsApiProvider.get();
console.log('[Multi-Account] luckPermsApi API loaded successfully!'); console.log("[Multi-Account] luckPermsApi API loaded successfully!");
return luckPermsApi; return luckPermsApi;
} catch (error) { } catch (error) {
return null; return null;
} }
} }
function hasPermission(player, permission) { function hasPermission(player, permission) {
var api = getLuckPerms(); var api = getLuckPerms();
if (!api || !player) { if (!api || !player) {
return false; return false;
} }
try { try {
return api return api
.getUserManager() .getUserManager()
.getUser(player.uuid) .getUser(player.uuid)
.getCachedData() .getCachedData()
.getPermissionData() .getPermissionData()
.checkPermission(permission) .checkPermission(permission)
.asBoolean(); .asBoolean();
} catch (error) { } catch (error) {
console.error( console.error(
'[Multi-Account] Failed to check permission ' + "[Multi-Account] Failed to check permission " +
permission + permission +
' for ' + " for " +
player.username + player.username +
': ' + ": " +
error error,
); );
return false; return false;
} }
} }
function getPlayerIP(player) { function getPlayerIP(player) {
try { try {
var ip = player.ipAddress; var ip = player.ipAddress;
if (!ip) { if (!ip) {
return null; return null;
} }
if (ip.includes('/')) { if (ip.includes("/")) {
ip = ip.split('/')[1]; ip = ip.split("/")[1];
} }
if (ip.includes(':')) { if (ip.includes(":")) {
ip = ip.split(':')[0]; ip = ip.split(":")[0];
} }
return ip; return ip;
} catch (error) { } catch (error) {
console.warn( console.warn(
'[Multi-Account] Failed to get IP for ' + "[Multi-Account] Failed to get IP for " +
player.username + player.username +
': ' + ": " +
error error,
); );
return null; return null;
} }
} }
function sendPlayerNotification(player, otherPlayers) { function sendPlayerNotification(player, otherPlayers) {
player.tell( player.tell(
'§c[ALERT] §fMulti-account detected! Other accounts on your IP: §e' + "§c[ALERT] §fMulti-account detected! Other accounts on your IP: §e" +
otherPlayers.join(', ') otherPlayers.join(", "),
); );
} }
function notifyStaff(server, playerName, otherPlayers) { function notifyStaff(server, playerName, otherPlayers) {
var message = var message =
'§c[ADMIN] §fPlayer §6' + "§c[ADMIN] §fPlayer §6" +
playerName + playerName +
'§f has the same IP as: §e' + "§f has the same IP as: §e" +
otherPlayers.join(', '); otherPlayers.join(", ");
server.players.forEach(player => { server.players.forEach((player) => {
if (hasPermission(player, MULTIACCOUNT_PERMISSION_ALERT)) { if (hasPermission(player, MULTIACCOUNT_PERMISSION_ALERT)) {
player.tell(message); player.tell(message);
} }
}); });
} }
function sendDiscordNotification(server, playerName, otherPlayers) { function sendDiscordNotification(player, otherPlayers) {
var payload = { var playerName =
content: '⚠️ **MULTI-ACCOUNT DETECTED**', player && typeof player.getName === "function"
embeds: [{ ? player.getName().getString()
title: 'Multi-Account Alert', : "Unknown";
color: 16711680,
fields: [ try {
{ HttpAPI.post(
name: 'Player', DISCORD_WEBHOOK_URL,
value: playerName, JSON.stringify({
inline: true embeds: [
}, {
{ title: "Multi-Account Alert",
name: 'Other Accounts on IP', description:
value: otherPlayers.join(', '), "Player: " +
inline: false playerName +
}, "\n" +
{ "Other Accounts on IP: " +
name: 'Timestamp', otherPlayers.join(", ") +
value: new Date().toISOString(), "\n" +
inline: true "Timestamp: <t:" +
} Math.floor(Date.now() / 1000) +
] ":S>",
}] 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: <t:" +
Math.floor(Date.now() / 1000) +
":S>",
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) { function checkMultiAccount(server, player) {
var name = player.username; var name = player.username;
var ip = getPlayerIP(player); var ip = getPlayerIP(player);
if (!ip) { if (!ip) {
return; return;
} }
if (!playerIPMap.has(ip)) { if (!playerIPMap.has(ip)) {
playerIPMap.set(ip, new Set()); playerIPMap.set(ip, new Set());
} }
var players = playerIPMap.get(ip); var players = playerIPMap.get(ip);
var otherPlayers = Array.from(players).filter(
playerName => playerName !== name
);
if (otherPlayers.length > 0) { var otherPlayers = Array.from(players).filter(
console.log( (playerName) => playerName !== name,
'[Multi-Account] ALERT: ' + );
name +
' shares IP with: ' +
otherPlayers.join(', ')
);
sendPlayerNotification(player, otherPlayers); if (otherPlayers.length > 0) {
notifyStaff(server, name, otherPlayers); console.log(
sendDiscordNotification(server, name, otherPlayers); "[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) { function removePlayer(name) {
for (var [ip, players] of playerIPMap) { for (var [ip, players] of playerIPMap) {
if (!players.delete(name)) { if (!players.delete(name)) {
continue; continue;
} }
console.log( console.log(
'[Multi-Account] Removed ' + "[Multi-Account] Removed " +
name + name +
' from IP tracking (' + " from IP tracking (" +
ip + ip +
')' ")",
); );
if (players.size === 0) { if (players.size === 0) {
playerIPMap.delete(ip); playerIPMap.delete(ip);
} }
break; break;
} }
} }
PlayerEvents.loggedIn(event => { PlayerEvents.loggedIn((event) => {
var player = event.player; var player = event.player;
var server = event.server; var server = event.server;
console.log( console.log(
'[Multi-Account] ' + "[Multi-Account] " +
player.username + player.username +
' joined from: ' + " joined from: " +
getPlayerIP(player) getPlayerIP(player),
); );
server.schedule(10, () => { server.schedule(10, () => {
checkMultiAccount(server, player); checkMultiAccount(server, player);
}); });
}); });
PlayerEvents.loggedOut(event => { PlayerEvents.loggedOut((event) => {
var name = event.player.username; var name = event.player.username;
console.log( console.log("[Multi-Account] " + name + " left the server");
'[Multi-Account] ' +
name +
' left the server'
);
removePlayer(name); removePlayer(name);
}); });
ServerEvents.commandRegistry(event => { ServerEvents.commandRegistry((event) => {
const { commands: Commands } = event; const { commands: Commands } = event;
event.register( event.register(
Commands.literal('checkip') Commands.literal("checkip")
.requires(source => .requires(
source.isPlayer() && (source) =>
( source.isPlayer() &&
source.hasPermission(3) || (source.hasPermission(3) ||
hasPermission( hasPermission(
source.player, source.player,
MULTIACCOUNT_PERMISSION_CHECKIP MULTIACCOUNT_PERMISSION_CHECKIP,
) )),
) )
) .executes((ctx) => {
.executes(ctx => { var source = ctx.source;
var source = ctx.source; var server = source.server;
var group = 1;
var found = false;
source.sendSuccess( var group = 1;
Text.of('§6Players sharing an IP:'), var found = false;
false var groups = [];
);
for (var players of playerIPMap.values()) { rebuildPlayerIPMap(server);
if (players.size <= 1) {
continue;
}
found = true; source.sendSuccess(Text.of("§6Players sharing an IP:"), false);
source.sendSuccess( for (var players of playerIPMap.values()) {
Text.of( if (players.size <= 1) {
`§7Group ${group}: §e${Array.from(players).join(', ')}` continue;
), }
false
);
group++; found = true;
}
if (!found) { var playerList = Array.from(players);
source.sendSuccess(
Text.of('§7No players currently share an IP.'),
false
);
}
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] 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( console.log(
'[Multi-Account] Webhook: ' + "[Multi-Account] /checkip permission: " + MULTIACCOUNT_PERMISSION_CHECKIP,
(DISCORD_WEBHOOK_URL !== 'REPLACE_ME' ? '✓ Configured' : '✗ Not configured') );
);
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"),
);