Code
Code
</div>
<div class="col-md-7 col-lg-8"> <!-- Adjusted columns -->
<!-- Log Card -->
<div class="card">
<div class="card-header">Activity Log</div>
<div class="card-body p-0"> <!-- Remove padding to
let log fill it -->
<div class="log"></div> <!-- Log content will
go here -->
</div>
<div class="card-footer text-center">
<button title="Clear Log" type="submit"
name="clear" class="btn btn-sm btn-secondary"><i class="fa fa-close"></i> Clear
Log</button> <!-- Secondary color for clear -->
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="settings" role="tabpanel" aria-
labelledby="profile-tab">
<div class="card">
<div class="card-header">Tool Settings</div>
<div class="card-body">
<div class="row">
<div class="col-md-6 mb-3 mb-md-0"> <!-- Add
bottom margin on small screens -->
<h5>Behavior Options</h5>
<div class="custom-control custom-switch mb-
2">
<input class="custom-control-input"
type="checkbox" id="prison_all">
<label class="custom-control-label"
for="prison_all">Jail everyone (Ignore lists)</label>
</div>
<div class="custom-control custom-switch mb-
2">
<input class="custom-control-input"
type="checkbox" id="user_part" checked>
<label class="custom-control-label"
for="user_part">Disconnect if target leaves planet</label>
</div>
<div class="custom-control custom-switch mb-
2">
<input class="custom-control-input"
type="checkbox" id="timeout_3_sec" checked>
<label class="custom-control-label"
for="timeout_3_sec">Disconnect on 3-sec jailing timeout</label>
</div>
<div class="custom-control custom-switch d-
none"> <!-- Kept hidden -->
<input class="custom-control-input"
type="checkbox" id="join_user_not_blacklist">
<label class="custom-control-label"
for="join_user_not_blacklist">Disconnect if non-blacklisted user joins</label>
</div>
<div class="custom-control custom-switch mb-
2">
<input class="custom-control-input"
type="checkbox" id="disconnect_action">
<label class="custom-control-label"
for="disconnect_action">Disconnect immediately after sending jail command</label>
</div>
<div class="custom-control custom-switch mb-
2">
<input class="custom-control-input"
type="checkbox" id="reconnect">
<label class="custom-control-label"
for="reconnect">Enable Auto-Reconnect</label>
</div>
<div class="custom-control custom-switch mb-
2">
<input class="custom-control-input"
type="checkbox" id="remove">
<label class="custom-control-label"
for="remove">Stand on target user</label>
</div>
<div class="custom-control custom-switch mb-
2">
<input class="custom-control-input"
type="checkbox" id="prison_and_off" checked>
<label class="custom-control-label"
for="prison_and_off">Disconnect after successful jail confirmation</label>
</div>
<div class="custom-control custom-switch mb-
2">
<input class="custom-control-input"
type="checkbox" id="re-fly-join">
<label class="custom-control-label"
for="re-fly-join">Re-fly to planet after jailing</label>
</div>
<div class="custom-control custom-switch d-
none"> <!-- Kept hidden/disabled -->
<input disabled class="custom-control-
input" type="checkbox" id="authority">
<label class="custom-control-label"
for="authority">Check authority (Emperor check - Attack only)</label>
</div>
<div class="custom-control custom-switch d-
none"> <!-- Kept hidden/disabled -->
<input disabled class="custom-control-
input" type="checkbox" id="not_prison_newcomers">
<label class="custom-control-label"
for="not_prison_newcomers">Ignore users below 'Dad' authority</label>
</div>
</div>
<div class="col-md-6">
<h5>Timings (milliseconds)</h5>
<div class="mb-3">
<label class="form-label">Reconnect
Interval:</label>
<input type="number" name="timer-
reconnect" class="form-control form-control-sm" value="10200">
</div>
<hr style="border-top: 1px solid #445;"> <!--
Darker HR -->
<div class="mb-3">
<label class="form-label">Attack Interval
(min/max/adjust):</label>
<div class="custom-control custom-switch
custom-switch-sm mb-2">
<input class="custom-control-input"
type="checkbox" id="pm-tm-a">
<label class="custom-control-label
small" for="pm-tm-a">Enable Auto-Adjust</label>
</div>
<div class="row g-2">
<div class="col">
<label class="small form-label mb-
1">Min</label>
<input type="number" name="t-a"
class="form-control form-control-sm" value="1700">
</div>
<div class="col">
<label class="small form-label
mb-1">Max</label>
<input type="number" name="max-t-
a" class="form-control form-control-sm" value="2000">
</div>
<div class="col">
<label class="small form-label
mb-1">+/-</label>
<input type="number" name="pm-t-a"
class="form-control form-control-sm" value="5">
</div>
</div>
</div>
<hr style="border-top: 1px solid #445;">
<div class="mb-3">
<label class="form-label">Defense Interval
(min/max/adjust):</label>
<div class="custom-control custom-switch
custom-switch-sm mb-2">
<input class="custom-control-input"
type="checkbox" id="pm-tm-z">
<label class="custom-control-label
small" for="pm-tm-z">Enable Auto-Adjust</label>
</div>
<div class="row g-2">
<div class="col">
<label class="small form-label mb-
1">Min</label>
<input type="number" name="t-z"
class="form-control form-control-sm" value="1600">
</div>
<div class="col">
<label class="small form-label
mb-1">Max</label>
<input type="number" name="max-t-
z" class="form-control form-control-sm" value="1600">
</div>
<div class="col">
<label class="small form-label
mb-1">+/-</label>
<input type="number" name="pm-t-z"
class="form-control form-control-sm" value="5">
</div>
</div>
</div>
<hr style="border-top: 1px solid #445;">
<div class="mt-3">
<input type="submit" name="set_timeout"
class="btn btn-sm btn-info" value="Determine Interval from Ping">
</div>
</div>
</div>
</div> <!-- End card-body -->
</div> <!-- End card -->
</div>
<div class="tab-pane fade" id="profile" role="tabpanel" aria-
labelledby="profile-tab">
<div class="card">
<div class="card-header">Blacklists</div>
<div class="card-body">
<p class="text-muted small mb-3">Users or clans listed
here will be targeted for jailing (unless 'Jail everyone' is active). Enter one
item per line or separate by spaces.</p>
<div class="row">
<div class="col-md-6 mb-3 mb-md-0">
<div class="form-group">
<label for="black_clan" class="form-
label">Blacklisted Clans:</label>
<textarea class="form-control form-
control-sm" name="black_clan" id="black_clan" rows="10"
placeholder="EnemyClan1 Bad Clan 2"></textarea>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="black_nick" class="form-
label">Blacklisted Nicks:</label>
<textarea class="form-control form-
control-sm" name="black_nick" id="black_nick" rows="10"
placeholder="BadUser1 Target Name 2"></textarea>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="contact" role="tabpanel" aria-
labelledby="contact-tab">
<div class="card">
<div class="card-header">Whitelists</div>
<div class="card-body">
<p class="text-muted small mb-3">Users or clans listed
here will *never* be targeted for jailing, even if 'Jail everyone' is active. Enter
one item per line or separate by spaces.</p>
<div class="row">
<div class="col-md-6 mb-3 mb-md-0">
<div class="form-group">
<label for="white_clan" class="form-
label">Whitelisted Clans:</label>
<textarea class="form-control form-
control-sm" name="white_clan" id="white_clan" rows="10"
placeholder="FriendlyClan1 Ally Clan 2"></textarea>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="white_nick" class="form-
label">Whitelisted Nicks:</label>
<textarea class="form-control form-
control-sm" name="white_nick" id="white_nick" rows="10"
placeholder="GoodUser1 Friend Nick 2"></textarea>
</div>
</div>
</div>
</div>
</div>
</div>
</div> <!-- End tab-content -->
</div>
<!-- Hidden original section - kept for reference or potential future use
-->
<div class="row no-gutters d-none">
<div class="col-sm">
<div class="m-3">
<div class="">
<div class="form-group">
<label for="recovers">Codes:</label>
<input class="form-control form-control-sm"
name="recovers" id="recovers">
</div>
</div>
<div class="mt-1 mb-1">
<div class="form-group">
<label for="count_message">Log message count:</label>
<input type="text" name="count_message"
id="count_message" class="form-control form-control-sm" value="20" style="width:
70px">
</div>
</div>
<div class="mt-1 mb-1">
<div class="form-group">
<label for="main_join">Default start planet:</label>
<input type="text" name="main_join" id="main_join"
class="form-control form-control-sm" value="" style="max-width: 140px"
placeholder="main">
</div>
</div>
<input type="submit" name="check" class="btn btn-sm btn-info"
value="Start">
<input type="submit" name="stop" class="btn btn-sm btn-info"
value="Stop">
<input type="submit" name="reset" class="btn btn-sm btn-info"
value="Reset">
</div>
</div>
<div class="col-sm">
<div class="m-3">
<div class="">
<div>Log:</div>
<div class="log" style="height: 200px; overflow-y: auto;
border: #8c929a 1px solid; border-radius: .2rem;"></div>
<button title="Update Log" type="submit" name="check"
class="btn btn-sm btn-info mt-2"><i style="color: white!important;" class="fa fa-
refresh"></i></button>
<button title="Clear Log" type="submit" name="clear"
class="btn btn-sm btn-info mt-2"><i style="color: white!important;" class="fa fa-
close"></i></button>
</div>
</div>
</div>
</div>
<!-- End Hidden Section -->
<script>
// --- Helper Functions (from original) ---
Node.prototype.remove = function (){ if(this.parentElement)
this.parentElement.removeChild(this); };
String.prototype.trim = function() {
return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
};
function getRecoveryCodes() {
const stored = localStorage.getItem(LS_RECOVERY_CODES);
try {
return stored ? JSON.parse(stored) : [];
} catch (e) {
console.error("Error parsing recovery codes from localStorage",
e);
return [];
}
}
function saveRecoveryCodes(codes) {
localStorage.setItem(LS_RECOVERY_CODES, JSON.stringify(codes));
}
function populateRecoverySelect() {
const codes = getRecoveryCodes();
const selectedCodeValue = localStorage.getItem(LS_SELECTED_CODE);
recoverSelect.innerHTML = ''; // Clear existing options
if (codes.length === 0) {
recoverSelect.innerHTML = '<option value="">-- No codes saved
--</option>';
recoverSelect.disabled = true;
removeRecoverBtn.disabled = true;
} else {
recoverSelect.disabled = false;
removeRecoverBtn.disabled = false;
codes.forEach(code => {
const displayCode = code.length > 15 ? code.substring(0, 8)
+ '...' + code.substring(code.length - 4) : code;
const option = document.createElement('option');
option.value = code;
option.textContent = displayCode;
if (code === selectedCodeValue) {
option.selected = true;
}
recoverSelect.appendChild(option);
});
}
}
addRecoverBtn.addEventListener('click', () => {
const newCode = recoverInput.value.trim();
if (!newCode) {
Swal.fire({ title: 'Error', text: 'Recovery code cannot be
empty.', icon: 'warning', background: '#2a2a3a', color: '#eee'});
return;
}
const codes = getRecoveryCodes();
if (codes.includes(newCode)) {
Swal.fire({ title: 'Info', text: 'This recovery code is already
saved.', icon: 'info', background: '#2a2a3a', color: '#eee'});
recoverSelect.value = newCode;
localStorage.setItem(LS_SELECTED_CODE, newCode);
return;
}
codes.push(newCode);
saveRecoveryCodes(codes);
populateRecoverySelect();
recoverSelect.value = newCode;
localStorage.setItem(LS_SELECTED_CODE, newCode);
recoverInput.value = '';
add_log(`Added new recovery code.`);
});
removeRecoverBtn.addEventListener('click', () => {
const selectedCodeValue = recoverSelect.value;
if (!selectedCodeValue) {
Swal.fire({ title: 'Error', text: 'No code selected to
remove.', icon: 'warning', background: '#2a2a3a', color: '#eee'});
return;
}
let codes = getRecoveryCodes();
codes = codes.filter(code => code !== selectedCodeValue);
saveRecoveryCodes(codes);
if (localStorage.getItem(LS_SELECTED_CODE) === selectedCodeValue) {
localStorage.removeItem(LS_SELECTED_CODE);
}
populateRecoverySelect();
add_log(`Removed selected recovery code.`);
if(recoverSelect.options.length > 0 &&
recoverSelect.options[0].value) {
recoverSelect.selectedIndex = 0;
localStorage.setItem(LS_SELECTED_CODE, recoverSelect.value);
} else {
localStorage.removeItem(LS_SELECTED_CODE);
}
});
recoverSelect.addEventListener('change', () => {
if(recoverSelect.value) {
localStorage.setItem(LS_SELECTED_CODE, recoverSelect.value);
} else {
localStorage.removeItem(LS_SELECTED_CODE);
}
});
document.querySelector("[name=t-a]").value = suggestedAttack;
document.querySelector("[name=t-z]").value = suggestedDefense;
add_log(`Suggested Minimum Intervals - Attack: $
{suggestedAttack}ms, Defense: ${suggestedDefense}ms`);
// Load checkboxes
$('#settings input[type="checkbox"]').each(function() {
const key = LS_SETTINGS_PREFIX + $(this).attr('id');
const value = localStorage.getItem(key);
if (value !== null) {
$(this).prop('checked', value === 'true');
} else {
// Set default based on initial HTML 'checked' attribute if
no value saved
$(this).prop('checked', $(this).is('[checked]'));
}
});
function saveSetting(element) {
const id = $(element).attr('id');
const name = $(element).attr('name');
let key;
let value;
if ($(element).is(':checkbox')) {
key = LS_SETTINGS_PREFIX + id;
value = $(element).prop('checked');
} else if ($(element).is('textarea')) {
key = LS_SETTINGS_PREFIX + id; // Use ID for textareas
value = $(element).val();
} else if ($(element).attr('type') === 'text' || $
(element).attr('type') === 'number') {
key = LS_SETTINGS_PREFIX + name; // Use name for text/number
inputs
value = $(element).val();
} else if (element.id === 'join') { // Specific handling for #join
input
key = LS_JOIN_PLANET;
value = $(element).val();
} else {
return; // Don't save if type is unknown
}
localStorage.setItem(key, value);
}
});
// --- End Load/Save Settings ---
let timeout = 0;
let isAttack = (type === 1);
if(isAttack) {
type_prison = "a";
// Use internal timer if set and auto-adjust enabled, otherwise
read from input
timeout = ($('#pm-tm-a').prop('checked') && timer_a > 0) ?
timer_a : parseInt($('[name=t-a]').val() || "1700");
} else {
type_prison = "z";
timeout = ($('#pm-tm-z').prop('checked') && timer_z > 0) ?
timer_z : parseInt($('[name=t-z]').val() || "1600");
}
let targetsFound = 0;
for (let i=0; i < users.length; i++){
const user = users[i];
if (!user || typeof user.id === 'undefined') continue; // Skip
invalid user data
if(parseInt(me.id) === user.id) continue; // Skip self
if (is_action(user.id)) continue; // Skip if already processing
if (shouldJail) {
targetsFound++;
let currentAction = { id: user.id, nick: user.nick };
add_log(`Scheduling ${isAttack ? 'attack' : 'defense'} jail
for <b>${user.nick}</b> [${user.id}] in ${timeout}ms`);
currentAction.action = setTimeout(function (){
if (is_user_list(currentAction.id)) { // Re-check
presence
prison(currentAction.id);
} else {
add_log(`User ${currentAction.nick} [$
{currentAction.id}] left before action.`);
remove_action(currentAction.id);
}
}, timeout);
action.push(currentAction);
if($('#user_part').prop('checked')){
// Check if this user was the *only* target we were waiting
for
const isOnlyTarget = !action.some(act => act.id !== id); //
Check if any *other* actions exist
if (isOnlyTarget) {
add_log("Disconnecting because the only target left (as
configured).");
set_offline();
} else {
add_log(`Target ${nick} left, but other actions
pending.`);
}
}
}
if($('#disconnect_action').prop('checked')) {
add_log("Disconnecting immediately after sending action
(as configured).");
set_offline();
}
} else {
prison_not_found(id); // User left between schedule and
fire
}
} catch (e){
add_log(`<span style='color: red;'>Error during jail attempt
for ${id}: ${e}</span>`);
console.error("Jail error:", e); remove_action(id);
}
}
if (shouldJail) {
let timeout = ($('#pm-tm-z').prop('checked') && timer_z >
0) ? timer_z : parseInt( $('[name=t-z]').val() || "1600" );
type_prison = "z"; // Defense mode
if (!is_action(o.id)) { // Avoid queuing multiple actions
for the same join
let currentAction = { id: o.id, nick: o.nick };
add_log(`Scheduling defense jail for <b>${o.nick}</b>
[${o.id}] in ${timeout}ms`);
currentAction.action = setTimeout(function (){
if (is_user_list(currentAction.id))
{ prison(currentAction.id); }
else { add_log(`Joined user ${currentAction.nick}
left before defense.`); remove_action(currentAction.id); }
}, timeout);
action.push(currentAction);
}
}
} catch(e) { add_log("<span style='color: red;'>Error processing
user join.</span>"); console.error("Error in join:", e, "Data:", data); }
}
// If the user who left was the *only* target we had an action
for
if($('#user_part').prop('checked') && hadAction &&
action.length === 0) {
add_log(`Target ${nick} left - disconnecting (as
configured).`);
set_offline();
}
} catch(e) { add_log("<span style='color: red;'>Error processing
user leave.</span>"); console.error("Error in remove_user:", e, "UserID:", userID);
}
}
resultUsers.push(o);
} catch (err) {
console.error("Error parsing user chunk starting near
index:", s, err, "Data snippet:", e.slice(s, s + 10));
// Attempt to recover by moving to the next element,
might cause cascade errors.
s++;
// break; // Alternative: Stop parsing entirely on error
}
}
users = resultUsers; // Update global users array
// console.log("Final parsed user list:", users); // Debug
}
try {
if (typeof GALAXY !== 'undefined' && GALAXY.isConnected()) {
add_log("Closing existing connection before
reconnecting...");
GALAXY.closeConnect();
// Wait a tiny bit before reconnecting if needed, though
onClose should handle state reset.
}
me = {}; users_clear(); // Reset state immediately
// Initiate connection
GALAXY.recoverUser(recoveryCode.trim());
} catch (e) {
add_log(`<span style='color: red;'>Fatal Error during
connection setup: ${e}</span>`);
console.error("Connect function error:", e); running = false;
off();
}
}
$(document).on("click","[name=stop]",function (e){
e.preventDefault();
if (!running) { add_log("Already stopped."); return; }
add_log("Stopping requested by user...");
off(); // Use the force stop function
})
$(document).on("click","[name=go_join]",function (e){
e.preventDefault();
if (!running || (typeof GALAXY === 'undefined') || !
GALAXY.isConnected()) {
add_log("<span style='color: orange;'>Cannot fly: Not
connected.</span>");
Swal.fire({title: 'Error', text: 'You must be connected to fly
to a planet.', icon: 'warning', background: '#2a2a3a', color: '#eee'}); return;
}
try{
let targetPlanet = $('#join').val().trim();
if (!targetPlanet) { add_log("<span style='color:
orange;'>Cannot fly: Planet name is empty.</span>"); return; }
main_join = targetPlanet; // Update the target planet
add_log(`Attempting to fly to planet: ${main_join}`);
GALAXY.command("JOIN "+main_join);
users_clear(); // Clear state immediately on attempting to fly
}catch (e){ add_log(`<span style='color: red;'>Error trying to fly:
${e}</span>`); console.error("Fly error:", e); }
})
</script>
</div>
</div>