0% found this document useful (0 votes)
6 views27 pages

Code

The document outlines a user interface for managing recovery codes, connection controls, planet navigation, activity logs, and tool settings. It includes options for blacklisting and whitelisting users or clans, as well as various behavior and timing settings for actions like jailing and reconnecting. Additionally, it features buttons for connecting, disconnecting, and clearing logs, along with input fields for managing recovery codes and user lists.

Uploaded by

shydra818
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views27 pages

Code

The document outlines a user interface for managing recovery codes, connection controls, planet navigation, activity logs, and tool settings. It includes options for blacklisting and whitelisting users or clans, as well as various behavior and timing settings for actions like jailing and reconnecting. Additionally, it features buttons for connecting, disconnecting, and clearing logs, along with input fields for managing recovery codes and user lists.

Uploaded by

shydra818
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 27

<!

-- Recovery Code Card -->


<div class="card mb-3">
<div class="card-header">Recovery Code</div>
<div class="card-body" id="recover-manage-group">
<label for="recover-select" class="form-label
small mb-1">Select Saved Code:</label>
<div class="input-group input-group-sm mb-3">
<select id="recover-select" class="form-
select form-select-sm"> <!-- Use form-select for BS5 -->
<option value="">-- No codes saved --
</option>
</select>
<button id="remove-recover-btn" class="btn
btn-sm btn-danger" type="button" title="Remove Selected Code">
<i class="fa fa-trash"></i>
</button>
</div>

<label for="recover-input" class="form-label


small mb-1">Add New Code:</label>
<div class="input-group input-group-sm">
<input id="recover-input" type="text"
class="form-control form-control-sm" placeholder="Paste new code here">
<button id="add-recover-btn" class="btn
btn-sm btn-primary" type="button" title="Add Code to List">
<i class="fa fa-plus"></i> Add
</button>
</div>
</div>
</div>

<!-- Connection Control Card -->


<div class="card mb-3">
<div class="card-header">Connection</div>
<div class="card-body text-center">
<input type="submit" name="run" class="btn btn-
sm btn-success me-2" value="Connect"> <!-- Success for connect -->
<input type="submit" name="stop" class="btn
btn-sm btn-warning" value="Disconnect"> <!-- Warning for disconnect -->
</div>
</div>

<!-- Planet Navigation Card -->


<div class="card mb-3">
<div class="card-header">Planet Navigation</div>
<div class="card-body text-center">
<div class="mb-2">
<label for="join" class="form-label mb-
1">Target Planet:</label>
<input id="join" type="text" name="join"
class="form-control form-control-sm d-inline-block mx-auto" placeholder="main"
style="width: 150px" value="main">
</div>
<input type="submit" name="go_join" class="btn
btn-sm btn-info" value="Fly to Planet">
</div>
</div>

</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&#10;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&#10;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&#10;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&#10;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 -->

<style> /* Hiding specific elements if they exist (from original) */


.single,.some, .prize_grand_overlay_image, .s__h1 { display: none !
important; }
</style>

<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, '');
};

// --- Global Variables (adapted from original) ---


let users = [];
let me = {};
let action = []; // Scheduled actions {id: ..., action: timeoutId}
let main_join = "]//////["; // Default/Placeholder planet
let remove_tm = false; // Timeout flag for 'stand on user'
let timer_a = 0; // Current calculated attack timer
let timer_z = 0; // Current calculated defense timer
let type_prison = "a"; // Current mode: 'a'ttack or 'z'defense
let running = false; // Is the tool connected/active?
let access_reconnect = false; // Flag to prevent multiple reconnect
attempts

// --- LOCAL STORAGE KEYS ---


const LS_RECOVERY_CODES = "recoveryCodesList_v2"; // Use a distinct key
const LS_SELECTED_CODE = "selectedRecoveryCode_v2";
const LS_JOIN_PLANET = "joinPlanet_v2";
const LS_SETTINGS_PREFIX = "setting_v2_"; // Prefix for settings

// --- RECOVERY CODE MANAGEMENT ---


const recoverSelect = document.getElementById('recover-select');
const recoverInput = document.getElementById('recover-input');
const addRecoverBtn = document.getElementById('add-recover-btn');
const removeRecoverBtn = document.getElementById('remove-recover-btn');

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);
}
});

// Load codes when DOM is ready


document.addEventListener('DOMContentLoaded', populateRecoverySelect);

// --- Ping Function ---


let set_timeout_ping = async function () {
add_log("Pinging server to determine intervals...");
let start = Date.now();
try {
// Try HEAD request first (often faster, might be blocked by
CORS)
await fetch("https://galaxy.mobstudio.ru/favicon.ico?t=" +
Date.now(), { method: 'HEAD', mode: 'no-cors', cache: 'no-store' });
} catch (err) {
console.warn("Ping HEAD request failed (expected for no-
cors):", err);
}
let t = (Date.now() - start);

// If HEAD was instant (likely blocked), try a regular fetch


if (t < 10) {
add_log(`Ping time too low (${t}ms), using fallback
fetch...`);
start = Date.now();
try {
await fetch("https://galaxy.mobstudio.ru/?t=" +
Date.now(), { mode: 'no-cors', cache: 'no-store' });
} catch (e) {}
t = (Date.now() - start);
if (t < 10) {
t = 150; // Fallback if still too low
add_log(`Fallback ping also too low, assuming ${t}ms.`);
} else {
add_log(`Ping time (fallback fetch): ${t}ms`);
}
} else {
add_log(`Ping time (HEAD): ${t}ms`);
}

// Calculation (adjust baseTime and buffer as needed)


let baseTime = 2000; // Target reaction window
let buffer = 150;
let safetyMargin = 50;

let suggestedAttack = Math.max(500, baseTime - t - buffer +


safetyMargin);
let suggestedDefense = Math.max(400, baseTime - t - (buffer * 1.5)
+ safetyMargin);

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`);

// Optionally save these new values to localStorage


localStorage.setItem(LS_SETTINGS_PREFIX + 't-a', suggestedAttack);
localStorage.setItem(LS_SETTINGS_PREFIX + 't-z', suggestedDefense);

// Update internal timers immediately if running


timer_a = suggestedAttack;
timer_z = suggestedDefense;
}

// --- Load/Save Settings using jQuery ---


function loadSettings() {
// Load simple values
$('#join').val(localStorage.getItem(LS_JOIN_PLANET) || 'main');
$('#black_clan').val(localStorage.getItem(LS_SETTINGS_PREFIX +
'black_clan') || '');
$('#black_nick').val(localStorage.getItem(LS_SETTINGS_PREFIX +
'black_nick') || '');
$('#white_clan').val(localStorage.getItem(LS_SETTINGS_PREFIX +
'white_clan') || '');
$('#white_nick').val(localStorage.getItem(LS_SETTINGS_PREFIX +
'white_nick') || '');

// 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]'));
}
});

// Load text/number inputs in settings


$('#settings input[type="text"], #settings
input[type="number"]').each(function() {
const key = LS_SETTINGS_PREFIX + $(this).attr('name'); // Using
name for these
const value = localStorage.getItem(key);
if (value !== null) {
$(this).val(value);
}
// else keep default from HTML
});
}

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);
}

// Load settings on page load


$(document).ready(function() {
loadSettings();

// --- Attach Save Event Listeners ---


$('#join, #black_clan, #black_nick, #white_clan,
#white_nick').on('change input', function() { // Added input event for textareas
saveSetting(this);
});
$('#settings input').on('change', function() {
saveSetting(this);
});
// Textarea saving handled above
// Attach click listener for ping button
$('[name=set_timeout]').on('click', function(e){
e.preventDefault(); // Prevent form submission if it's part of
a form
set_timeout_ping();
});

// Attach click listener for clear log


$('[name=clear]').on('click', function(e){
e.preventDefault();
$('.log').html('');
add_log("Log cleared.");
});

});
// --- End Load/Save Settings ---

// --- Core Galaxy Logic (Translated & Adapted) ---

let add_log = function (str){


let d = new Date();
let time = `[${d.getHours().toString().padStart(2, '0')}:$
{d.getMinutes().toString().padStart(2, '0')}:$
{d.getSeconds().toString().padStart(2, '0')}.$
{d.getMilliseconds().toString().padStart(3, '0')}]`;
let count_message = 150; // Increased max log lines
let logContainer = $(".log");
let logEntries = logContainer.children(".p-1");

// Efficiently remove old entries if limit exceeded


if (logEntries.length >= count_message) {
logEntries.slice(count_message - 1).remove(); // Remove entries
beyond the limit
}

// Prepend new entry and scroll to top


logContainer.prepend(`<div class="p-1">${time} ${str}</div>`);
logContainer.scrollTop(0); // Keep the latest message visible
}

let init_str = function (str){


add_log("SYS: " + str); // Log system messages
// Check for 3-second rule
if(str.includes("3 секунд(ы)") || str.includes("3 second(s)")) { //
Original Russian + English
if($('#timeout_3_sec').prop('checked')){
console.log("Disconnecting - cannot jail (3 sec rule)");
add_log("<span style='color: red;'>Caught 3-second rule
timeout (disconnecting)</span>");
if(type_prison === "a") settings_timer_a("+"); else
settings_timer_z("+");
set_offline();
} else {
add_log("<span style='color: orange;'>Caught 3-second rule
timeout (ignored)</span>");
if(type_prison === "a") settings_timer_a("+"); else
settings_timer_z("+"); // Still adjust timer
}
console.log("Cannot jail (3 sec rule)");
}
// Check for 10-second rate limit
if(str.includes("Заходить в Galaxy можно не раньше, чем через 10
секунд") || str.includes("can log in to Galaxy no earlier than 10 seconds")) {
add_log("<span style='color: orange;'>Rate limited by server
(10 seconds). Disconnecting.</span>");
set_offline();
}
// Check for moderator block
if (str.includes("Модератор закрыл вам доступ к функции") ||
str.includes("Moderator has blocked your access to the function")){
add_log("<span style='color: red; font-weight: bold;'>Function
blocked by Moderator! Stopping.</span>");
off(); // Force stop
}
// Check for successful jailing confirmation
if(str.includes("Ваш Авторитет позволяет посадить") ||
str.includes("Your Authority allows jailing")) {
add_log("<span style='color: green;'>Jail successful! " + str +
"</span>");
if($('#prison_and_off').prop('checked')){
if($('#re-fly-join').prop('checked')) {
add_log("Re-flying to planet...");
let currentPlanet = $('#join').val().trim() || "main";
try { GALAXY.command("JOIN " + currentPlanet); }
catch(e){ add_log(`<span style='color:red;'>Error re-flying: ${e}</span>`); }
}
set_offline(); // Disconnect after confirmation if checked
}
}
}

let init_bot = function (data){


try {
if (typeof data === 'string') me = JSON.parse(data); else me =
data;
if (me && me.nick && me.id) {
add_log(`Logged in as: <b>${me.nick}</b> (ID: ${me.id})`);
// Try initial planet join here if desired
let targetPlanet = $('#join').val().trim() || "main";
if(targetPlanet){
add_log(`Attempting initial join to planet: $
{targetPlanet}`);
GALAXY.command("JOIN " + targetPlanet);
}
} else {
add_log("<span style='color: orange;'>Received user data,
but format seems incorrect.</span>");
console.warn("Incorrect format for init_bot:", data); me =
{};
}
} catch (e) {
add_log("<span style='color: red;'>Error parsing own user
data.</span>"); console.error("Error in init_bot:", e, "Data:", data); me = {};
}
}
let users_clear = function (){
users = [];
action.forEach(act => clearTimeout(act.action)); // Clear timeouts
action = [];
// Don't log clearing here, it happens too often. Log when list is
*populated*.
// add_log("Cleared user list and pending actions.");
}

let is_action = function (id){


return action.some(act => parseInt(id) === parseInt(act.id));
}

let remove_action = function (id){


action = action.filter(act => {
if (parseInt(id) === parseInt(act.id)) {
clearTimeout(act.action);
return false; // Remove
}
return true; // Keep
});
}

let start = function (type = 1){ // type 1 = attack, type 2 = defense


if (!running || !me || !me.id) { // Ensure we are running and have
our ID
add_log("Scan skipped: Not running or own ID not yet known.");
return;
}
add_log(`Scanning users on planet... (Mode: ${type===1 ?
'Attack' : 'Defense'})`);

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");
}

// Clear previous actions only in attack mode for a full rescan


if (isAttack) {
// add_log("Clearing previous actions for attack scan."); //
Maybe too verbose
action.forEach(act => clearTimeout(act.action));
action = [];
}

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

// --- Filtering ---


if (is_whitelist_nick(user.nick) ||
is_whitelist_clan(user.clan)) continue; // Skip whitelisted first
if (user.isOwner) continue; // Skip planet owners (optional,
good practice)
// Add other filters like authority, newcomers etc. based on
settings
// if ($('#authority').prop('checked') && user.authority >=
1000000 && isAttack) continue; // Emperor check

// --- Stand on target ---


if ($('#remove').prop('checked') && !remove_tm) {
fly_position(user.coordX, user.nick); // coordX seems to be
the position identifier here
}

// --- Jailing Logic ---


let shouldJail = false;
if ($('#prison_all').prop('checked')){
shouldJail = true; // Jail regardless of lists (but
respects whitelist)
} else {
if(is_blacklist_clan(user.clan) ||
is_blacklist_nick(user.nick)){
shouldJail = true; // Jail if on blacklist
}
}

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);

// Break after first target if not jailing all (attack mode


only?)
if (!$('#prison_all').prop('checked') && isAttack) {
add_log("Found first attack target, stopping scan
(Jail All disabled).");
break;
}
}
}
if (targetsFound === 0 && action.length === 0) {
add_log("No targets found matching criteria.");
} else if (targetsFound > 0) {
add_log(`Scheduled actions for ${targetsFound} target(s).`);
}
}

let reconnect = function (){


if (!running) return;
const selectedCode = recoverSelect.value; // Use selected code
if (selectedCode) {
add_log("Attempting to reconnect...");
connect(selectedCode); // Pass the selected code
} else {
add_log("<span style='color: red;'>Cannot reconnect: No
recovery code selected. Stopping.</span>");
running = false; off(); // Stop if no code available
}
}

let set_offline = function (){


add_log("Setting state to offline.");
if (typeof GALAXY !== 'undefined' && GALAXY.isConnected()) {
try { GALAXY.closeConnect(); } catch (e)
{ console.error("Error closing connection:", e); }
}
users_clear(); // Clear state

const reconnectEnabled = $('#reconnect').prop('checked');


const timer_reconnect = parseInt($('[name=timer-reconnect]').val()
|| "10200");

if(reconnectEnabled && running && !access_reconnect){


access_reconnect = true;
add_log(`Auto-Reconnect enabled. Reconnecting in $
{timer_reconnect / 1000} seconds...`);
setTimeout(function (){
access_reconnect = false;
if (running) { // Only reconnect if still supposed to be
running
reconnect();
} else {
add_log("Reconnect cancelled as tool was stopped.");
}
}, timer_reconnect);
} else if (!reconnectEnabled && running) {
add_log("Auto-reconnect disabled. Stopping.");
running = false; off(); // Explicitly call off here
} else if (!running) {
// Already stopping or stopped, do nothing further
}
}

let prison_not_found = function (id){ // Added ID parameter


let nick = action.find(a => a.id === id)?.nick || `ID ${id}`;
add_log(`<span style='color: orange;'>Jail failed: User ${nick}
not found or left planet.</span>`);
remove_action(id);
// Adjust timers? Maybe not on 'not found'. Optional based on
strategy.
// if(type_prison==="a") settings_timer_a("-"); else
settings_timer_z("-");

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.`);
}
}
}

let prison = function (id){ // Combined prison and prison_all


if (!running) { remove_action(id); return; }
try{
let user = is_user_list(id);
if(user){
add_log(`Attempting to jail <b>${user.nick}</b> (ID: $
{id})...`);
GALAXY.command("ACTION 3 "+id); // Send the jail command
remove_action(id); // Remove action *after* sending command

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);
}
}

// --- List Checking Functions (using jQuery selectors) ---


let is_blacklist_clan = function (clan){
if(clan===null||clan===undefined)return false;
let black_clan = $("#black_clan").val().split(/[\s\n]+/); // Split
by space or newline
let upperClan = clan.trim().toUpperCase();
for (let i=0; i< black_clan.length;i++){
if(black_clan[i].trim() === "") continue;
if(upperClan===black_clan[i].trim().toUpperCase()) return true;
}
return false;
}
let is_blacklist_nick = function (nick){
if(nick===null||nick===undefined)return false;
let black_nick = $("#black_nick").val().split(/[\s\n]+/);
let upperNick = nick.trim().toUpperCase();
for (let i=0; i< black_nick.length;i++){
if(black_nick[i].trim() === "") continue;
if(upperNick===black_nick[i].trim().toUpperCase()) return true;
}
return false;
}
let is_whitelist_nick = function (nick){
if(nick===null||nick===undefined)return false;
let white_nick = $("#white_nick").val().split(/[\s\n]+/);
let upperNick = nick.trim().toUpperCase();
for (let i=0; i< white_nick.length;i++){
if(white_nick[i].trim() === "") continue;
if(upperNick===white_nick[i].trim().toUpperCase()) return true;
}
return false;
}
let is_whitelist_clan = function (clan){
if(clan===null||clan===undefined)return false;
let white_clan = $("#white_clan").val().split(/[\s\n]+/);
let upperClan = clan.trim().toUpperCase();
for (let i=0; i< white_clan.length;i++){
if(white_clan[i].trim() === "") continue;
if(upperClan===white_clan[i].trim().toUpperCase()) return true;
}
return false;
}

let is_user_list = function (userID){


const idToFind = parseInt(userID);
return users.find(user => user && user.id === idToFind);
}

// --- Data Parsers ---


let num353 = function (str){ // User list on join
add_log("Processing user list update (353)...");
users_clear(); // Clear previous list and actions
parseUserData(str); // This updates global 'users'
add_log(`Planet user list updated. Found ${users.length} users.`);
if (running && me && me.id) { // Only start scan if running and
logged in
start(1); // Trigger attack scan after getting list
} else { add_log("Scan skipped: Not running or not logged in
yet."); }
}

let num860 = function (str){ // Authority update


if (typeof str !== 'string') return;
let d = str.split(/\s+/);
let updatedCount = 0;
for(let i=0; i < d.length; i+=3){
if(!d[i+1] || !d[i+2]) continue;
try {
let id = parseInt(d[i+1]); let authority =
parseInt(d[i+2]);
if (isNaN(id) || isNaN(authority)) continue;
let user = is_user_list(id);
if(user) { user.authority = authority; updatedCount++; }
} catch (e) { console.error("Error parsing authority chunk:",
d.slice(i, i+3), e); }
}
if(updatedCount > 0) { /* add_log(`Updated authority for $
{updatedCount} users.`); // Optional: Can be spammy */ }
}

let founder = function (userID){ // Planet owner


if (!userID) return; try { let id = parseInt(userID); if
(isNaN(id)) return; let user = is_user_list(id); if(user) { user.isOwner = true;
add_log(`Planet owner identified: <b>${user.nick}</b>`); } } catch (e)
{ console.error("Error in founder:", e); }
}

let op = function (userID){ // Planet operator (unused?)


if (!userID) return; try { let id = parseInt(userID); if
(isNaN(id)) return; let user = is_user_list(id); if(user) { user.isOperator = true;
console.log(`User is operator: ${user.nick}`); } } catch(e) { console.error("Error
in op:", e); }
}

let fly_position = function (p, nick = "target"){ // Stand on user


coord
// The command seems to be "REMOVE" in the original code for
moving? Let's assume that's correct.
// The parameter 'p' seems to be the identifier needed (coordX from
parsed data).
if (!running || !p || p === 0) return; // Don't try if not running
or position is invalid/zero
if($('#remove').prop('checked') && !remove_tm) {
remove_tm = true;
setTimeout(function (){ remove_tm = false; }, 3000); //
Cooldown
add_log(`Moving to stand on <b>${nick}</b> (Pos: ${p})`);
try { GALAXY.command("REMOVE "+p); } catch (e)
{ add_log(`<span style='color:red;'>Error sending move command: ${e}</span>`);}
}
}

let join = function (data){ // User joins planet (data is expected as


pre-split array)
if (!running || !Array.isArray(data) || data.length < 22)
return; // Basic check
try {
let clan = (data[1] !== "-") ? data[1] : null;
let userId = parseInt(data[3]);
let nick = data[2];
// Assuming coordX is at index 21 based on parseUserData logic
adaptation
let coordX = data[21] ? parseInt(data[21]) : 0;
if (isNaN(userId) || !nick) return; // Invalid join data

if (me && me.id && userId === parseInt(me.id)) { // Self


joined a planet
add_log(`Successfully joined planet: ${$('#join').val() ||
'Unknown'}`);
// users_clear(); // Clear user list before receiving 353
return;
}

// Create user object matching parseUserData structure


let o = { id: userId, nick: nick, clan: clan, coordX: coordX,
isOwner: false, isOperator: false, authority: 0 /* other fields if needed */ };
add_log(`User joined: <b>${o.nick}</b> ${o.clan ? '[' + o.clan
+ ']' : ''} (ID: ${o.id})`);

// Add or update user in list


let existingUser = is_user_list(userId);
if (!existingUser) { users.push(o); }
else { existingUser.coordX = coordX; /* update other fields if
needed */ }

// --- Reaction Logic ---


if (is_whitelist_nick(o.nick) || is_whitelist_clan(o.clan))
{ add_log(`Joined user ${o.nick} is whitelisted, ignoring.`); return; }
// Skip owners/operators if needed
if (o.isOwner || o.isOperator) { return; }

fly_position(o.coordX, o.nick); // Stand on target if enabled

let shouldJail = false;


if($('#prison_all').prop('checked')){ shouldJail = true; }
else if(is_blacklist_clan(o.clan) ||
is_blacklist_nick(o.nick)) { shouldJail = true; }
// else { /* Check hidden setting join_user_not_blacklist if
needed */ }

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); }
}

let remove_user = function (userID){ // User leaves planet


if (!userID) return;
try {
const idToRemove = parseInt(userID); if (isNaN(idToRemove))
return;
let userIndex = users.findIndex(u => u && u.id ===
idToRemove);
let nick = userIndex !== -1 ? users[userIndex].nick : `ID $
{idToRemove}`;

if (userIndex !== -1) {


add_log(`User left: <b>${nick}</b> (ID: ${idToRemove})`);
users.splice(userIndex, 1); // Remove from array
}

// Check if we had an action planned for this user


let hadAction = is_action(idToRemove);
if (hadAction) {
remove_action(idToRemove); // Remove pending action
add_log(`Cancelled pending action for leaving user $
{nick}.`);
}

// 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);
}
}

// --- User Data Parser (Adapted from original - verify protocol


details) ---
function parseUserData(e) {
// console.log("Parsing user data string:", e); // Debug
if (typeof e === "string") { e = e.split(/\s+/); }
else if (!Array.isArray(e)) { console.error("Invalid data type for
parseUserData:", typeof e); users = []; return; }

var resultUsers = [];


var iParamCount = 5; // !!! IMPORTANT: ASSUMED parameter count per
view item !!!

for (var s = 0; s < e.length; ) {


if (s + 4 > e.length) { // Need at least clan, nick, id,
view_count
// console.warn("Data too short for basic user info at
index:", s);
break;
}
try {
var o = { // Simplified object structure
id: parseInt(e[s + 2]), nick: null, clan: null,
coordX: 0,
isOwner: false, isOperator: false, authority: 0
// Add other fields if needed and parsed correctly:
coordY, views, etc.
};
if (isNaN(o.id)) { console.warn("Invalid user ID parsed at
index:", s + 2, "Value:", e[s+2]); s++; continue; } // Skip invalid ID
var l = e[s + 1]; // Nickname
if (typeof l !== 'string') { o.nick = `InvalidNick_$
{o.id}`; }
else if ("+" === l[0]) { o.isOperator = true; o.nick =
l.substring(1); }
else if ("@" === l[0]) { o.isOwner = true; o.nick =
l.substring(1); }
else { o.nick = l; }

var d = e[s]; o.clan = (d === "-") ? null : d; // Clan

var c = Math.abs(parseInt(e[s + 3])); // View part count


if (isNaN(c) || c < 0) { c = 0; }

// --- Calculate end of view data ---


var viewDataEndIndex = s + 4 + c * iParamCount;

// --- Find Coordinate X ---


// Assumption: Coordinate X directly follows the view
data block.
// This is a MAJOR assumption and might be wrong
depending on the protocol version.
var coordIndex = viewDataEndIndex;
if (coordIndex < e.length) {
o.coordX = parseInt(e[coordIndex]);
if (isNaN(o.coordX)) {
// console.warn(`NaN coordX at index $
{coordIndex} for user ${o.nick}. Value: ${e[coordIndex]}`);
o.coordX = 0;
}
// If Coordinate Y exists, it would likely be at
coordIndex + 1
// o.coordY = parseInt(e[coordIndex + 1]);
// if (isNaN(o.coordY)) o.coordY = 0;
} else {
// console.warn("Coordinate data potentially missing or
truncated for user:", o.nick);
o.coordX = 0; // Default if missing
}

resultUsers.push(o);

// --- Move index to the start of the next user ---


// Assumes the next user block starts immediately after
coordX (or coordY if parsed).
s = coordIndex + 1; // Adjust if coordY is parsed (s =
coordIndex + 2)
// If the structure is different (e.g., fixed length
blocks), this needs changing.

} 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
}

// --- Connection Function ---


function connect(recoveryCode){
if(!running) { add_log("Connect cancelled: Tool is not running.");
return; }
if (!recoveryCode) {
add_log("<span style='color: red;'>Error: No recovery code
selected. Cannot connect.</span>");
Swal.fire({title: 'Error', text: 'Please select or add a
recovery code.', icon: 'error', background: '#2a2a3a', color: '#eee'});
running = false; return;
}

$(".log").html(""); console.clear(); // Clear logs


const displayCode = recoveryCode.length > 15 ?
recoveryCode.substring(0, 8) + '...' + recoveryCode.substring(recoveryCode.length -
4) : recoveryCode;
add_log(`Connecting with code: ${displayCode}...`);

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

// Setup GALAXY Handlers


GALAXY.handlers.onOpen = function() { add_log("<span
style='color: green;'>Connected to Galaxy server.</span>"); access_reconnect =
false; /* Reset reconnect flag on successful open */ };
GALAXY.handlers.onClose = function() { add_log("<span
style='color: red;'>Disconnected from Galaxy server.</span>"); set_offline(); }; //
set_offline handles reconnect logic
GALAXY.handlers.onError = function(err) { add_log(`<span
style='color: red;'>Connection Error: ${err}</span>`); console.error("WebSocket
Error:", err); /* onClose will likely fire after this, triggering set_offline */};
GALAXY.handlers.onMessage = function(msgNum, msgData) {
// Route message based on number
// console.log(`Received Msg ${msgNum}:`, msgData); //
Debugging
try {
let dataArray; // For messages split by space
switch(msgNum) {
case 353: num353(msgData); break; // User
list on planet join
case 860: num860(msgData); break; //
Authority update
case 355: // User join - Split data here
dataArray = typeof msgData === 'string' ?
msgData.split(/\s+/) : msgData;
join(dataArray);
break;
case 356: // User part/kick/leave - Split data
here
dataArray = typeof msgData === 'string' ?
msgData.split(/\s+/) : msgData;
if (dataArray.length > 1)
remove_user(dataArray[1]);
break;
case 357: // Planet owner ID - Split data here
dataArray = typeof msgData === 'string' ?
msgData.split(/\s+/) : msgData;
if (dataArray.length > 1)
founder(dataArray[1]);
break;
// case ???: op(msgData.split(/\s+/)[1]);
break; // Operator ID if needed
case 850: init_str(msgData); break; // General
status/error messages
case 851: init_bot(msgData); break; // Own
user info after login -> Triggers first planet join attempt
default: // Log potentially useful unhandled
system messages
if (typeof msgData === 'string' &&
msgData.length > 0 && msgNum !==0 && msgNum !==1) {
if(msgData.startsWith(':') || (msgNum >
800 && msgNum < 900)) { // Log system-like messages
// init_str(msgData.startsWith(':') ?
msgData.substring(1).trim() : msgData); // Don't spam log with everything
}
}
}
} catch (e) {
add_log(`<span style='color: red;'>Error processing
message ${msgNum}: ${e}</span>`);
console.error(`Error processing message ${msgNum}:`,
e, "Data:", msgData);
}
};

// 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();
}
}

// --- Timer Adjustment Logic ---


let settings_timer_a = function (params="+"){ // Adjust Attack Timer
if(!$('#pm-tm-a').prop('checked')) return;
let adjustVal = parseInt($('input[name=pm-t-a]').val()) || 5; //
Default adjust value
let minBaseVal = parseInt($('input[name=t-a]').val()) || 500; //
Default min value
let maxVal = parseInt($('input[name=max-t-a]').val()) || 5000; //
Default max value
let currentVal = timer_a <= 0 ? minBaseVal : timer_a; // Initialize
if needed

if (params === "+") { // Increase


if (currentVal + adjustVal <= maxVal) currentVal += adjustVal;
else currentVal = maxVal;
} else if (params === "-") { // Decrease
if (currentVal - adjustVal >= minBaseVal) currentVal -=
adjustVal; else currentVal = minBaseVal;
}
timer_a = currentVal;
// Update UI value only if auto-adjust is on? Optional.
// $('input[name=t-a]').val(timer_a);
add_log(`Adjusted attack timer ${params}${adjustVal}ms to $
{timer_a}ms (Min: ${minBaseVal}, Max: ${maxVal})`);
console.log("Internal Attack Timer now:", timer_a);
}

let settings_timer_z = function (params="+"){ // Adjust Defense Timer


if(!$('#pm-tm-z').prop('checked')) return;
let adjustVal = parseInt($('input[name=pm-t-z]').val()) || 5;
let minBaseVal = parseInt($('input[name=t-z]').val()) || 400;
let maxVal = parseInt($('input[name=max-t-z]').val()) || 5000;
let currentVal = timer_z <= 0 ? minBaseVal : timer_z; // Initialize
if needed

if (params === "+") { // Increase


if (currentVal + adjustVal <= maxVal) currentVal += adjustVal;
else currentVal = maxVal;
} else if (params === "-") { // Decrease
if (currentVal - adjustVal >= minBaseVal) currentVal -=
adjustVal; else currentVal = minBaseVal;
}
timer_z = currentVal;
// $('input[name=t-z]').val(timer_z);
add_log(`Adjusted defense timer ${params}${adjustVal}ms to $
{timer_z}ms (Min: ${minBaseVal}, Max: ${maxVal})`);
console.log("Internal Defense Timer now:", timer_z);
}

// --- Button Click Handlers (using jQuery) ---


$(document).on("click","[name=run]",function (e){
e.preventDefault();
if (running) { add_log("Already running."); return; }
running = true;
access_reconnect = false; // Ensure reconnect flag is reset
const selectedCode = recoverSelect.value; // Read from select
main_join = $('#join').val().trim() || "main";
// Initialize timers from UI on connect if auto-adjust is off or
timer isn't set yet
if (!$('#pm-tm-a').prop('checked') || timer_a <= 0) {
timer_a = parseInt($('[name=t-a]').val() || "1700");
}
if (!$('#pm-tm-z').prop('checked') || timer_z <= 0) {
timer_z = parseInt($('[name=t-z]').val() || "1600");
}
connect(selectedCode); // Pass selected code
})
let off = function (){ // Force stop function
add_log("Force stopping tool...");
running = false; // Set state
access_reconnect = true; // Prevent any pending reconnects *after*
setting running to false
if (typeof GALAXY !== 'undefined' && GALAXY.isConnected()) {
try{ GALAXY.closeConnect(); } catch (e){} // Close connection
if open
}
users_clear(); // Clear state
// No need to call set_offline here, as onClose will trigger it if
connected,
// and if not connected, we've already cleared state.
add_log("Tool stopped.");
}

// let sleep = function (){ // Placeholder (requires GALAXY


implementation)
// add_log("Sleep function called (implementation needed).");
// // GALAXY.sleep();
// }

$(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); }
})

// --- Initial Log Message ---


add_log("UI Initialized. Select/Add recovery code and connect.");

</script>
</div>
</div>

<!-- Footer -->


<center class="footer-link" style="padding-bottom: 15px; font-size: 0.9em;">
<a href="https://t.me/GalaxyChatSoft" target="_blank" rel="noopener
noreferrer">GalaxyChatSoft on Telegram</a>
</center>

You might also like