Inayatgaming's picture
Rename static/css/script.js to static/js/script.js
95a3e33 verified
const socket = io();
let sessionId = null;
let isConnected = false;
let isProgressLineInputFocused = false; // New flag to track focus
const PASTE_LINE_LIMIT_COMBO = 10000;
const PASTE_LINE_LIMIT_PROXY = 5000;
// --- stop locks ---
let isStopping = false;
let forceStopped = false;
function setControlsDisabled(disabled) {
['start-button','pause-button','continue-button','set-progress-button']
.forEach(id => { const el = document.getElementById(id); if (el) el.disabled = disabled; });
}
function resetUIToFreshSession() {
const zeros = {'total-lines':'0','checked':'0','invalid':'0','hits':'0','custom':'0','total-mega-fan':'0','total-fan-member':'0','total-ultimate-mega':'0','errors':'0','retries':'0','cpm':'0','elapsed-time':'0:00:00'};
Object.entries(zeros).forEach(([id,val]) => { const el = document.getElementById(id); if (el) el.textContent = val; });
const statusEl = document.getElementById('status'); if (statusEl) { statusEl.textContent = '❌ STOPPED'; statusEl.style.color = '#ff6b6b'; }
const combo = document.getElementById('combo-input'); if (combo) { combo.value=''; combo.style.height='auto'; }
const proxy = document.getElementById('proxy-input'); if (proxy) { proxy.value=''; proxy.style.height='auto'; }
const progress = document.getElementById('progress-line-input'); if (progress) progress.value = 0;
}
function showStatusMessage(message, type = 'info') {
const statusContainer = document.getElementById('status-messages');
const messageDiv = document.createElement('div');
messageDiv.className = `status-message ${type}`;
messageDiv.textContent = message;
statusContainer.appendChild(messageDiv);
setTimeout(() => {
if (messageDiv.parentNode) {
messageDiv.style.opacity = '0';
messageDiv.style.transform = 'translateX(100%)';
setTimeout(() => {
if (messageDiv.parentNode) {
statusContainer.removeChild(messageDiv);
}
}, 300);
}
}, 5000);
}
socket.on('connect', () => {
isConnected = true;
showStatusMessage('Connected to server successfully!', 'success');
const storedSessionId = localStorage.getItem('sessionId');
if (storedSessionId) {
socket.emit('reconnect_session', { session_id: storedSessionId });
} else {
socket.emit('request_session');
}
});
socket.on('disconnect', () => {
isConnected = false;
showStatusMessage('Disconnected from server!', 'error');
});
socket.on('session_created', (data) => {
sessionId = data.session_id;
localStorage.setItem('sessionId', sessionId);
console.log(`Session created: ${sessionId}`);
showStatusMessage('Session created successfully!', 'success');
history.pushState({ sessionId: sessionId }, '', `?session=${sessionId}`);
document.getElementById('combo-input').value = '';
document.getElementById('proxy-input').value = '';
document.getElementById('progress-line-input').value = 0;
document.getElementById('threads-input').value = 10;
document.getElementById('proxy-type-select').value = 'http';
updateStatsDisplay({
status: '❌ STOPPED',
total_lines: 0,
checked: 0,
invalid: 0,
hits: 0,
custom: 0,
total_mega_fan: 0,
total_fan_member: 0,
total_ultimate_mega: 0,
errors: 0,
retries: 0,
cpm: 0,
elapsed_time: '0:00:00'
});
});
socket.on('session_reconnected', (data) => {
sessionId = data.session_id;
console.log(`Session reconnected: ${sessionId}`);
showStatusMessage('Session reconnected successfully!', 'success');
history.pushState({ sessionId: sessionId }, '', `?session=${sessionId}`);
if (data.previous_state) {
updateStatsDisplay(data.previous_state.stats);
document.getElementById('combo-input').value = data.previous_state.combo_file_uploaded ? `Combo file uploaded (${data.previous_state.stats.total_lines} lines). Ready to check. ✅` : '';
document.getElementById('proxy-input').value = data.previous_state.proxy_file_uploaded ? `Proxy file uploaded. Ready to check. ✅` : '';
document.getElementById('threads-input').value = data.previous_state.threads || 10;
document.getElementById('proxy-type-select').value = data.previous_state.proxy_type || 'http';
// Only update progress-line-input if it's not currently focused
if (!isProgressLineInputFocused) {
document.getElementById('progress-line-input').value = data.previous_state.stats.checked || 0;
}
const textareas = document.querySelectorAll('.upload-textarea');
textareas.forEach(textarea => {
textarea.style.height = 'auto';
textarea.style.height = Math.min(textarea.scrollHeight, 300) + 'px';
});
}
});
socket.on('stats_update', (data) => {
if ((isStopping || forceStopped) && !(data.status && data.status.includes('STOPPED'))) { return; }
updateStatsDisplay(data);
const progressLineInput = document.getElementById('progress-line-input');
// Only update progress-line-input if it's not currently focused AND its value matches the current checked count
// This prevents overwriting user input if they are actively changing it.
if (!isProgressLineInputFocused && parseInt(progressLineInput.value) === data.checked) {
progressLineInput.value = data.checked;
}
});
socket.on('error', (data) => {
showStatusMessage(data.message, 'error');
});
socket.on('combo_uploaded', (data) => {
showStatusMessage(data.message, 'success');
document.getElementById('total-lines').textContent = data.count;
document.getElementById('combo-input').value = `Combo file uploaded (${data.count} lines). Ready to check. ✅`;
const textarea = document.getElementById('combo-input');
textarea.style.height = 'auto';
textarea.style.height = Math.min(textarea.scrollHeight, 300) + 'px';
});
socket.on('proxy_uploaded', (data) => {
showStatusMessage(data.message, 'success');
document.getElementById('proxy-input').value = `Proxy file uploaded (${data.count} lines). Ready to check. ✅`;
document.getElementById('proxy-type-select').value = data.proxy_type;
const textarea = document.getElementById('proxy-input');
textarea.style.height = 'auto';
textarea.style.height = Math.min(textarea.scrollHeight, 300) + 'px';
});
socket.on('checker_started', (data) => {
showStatusMessage(data.message, 'success');
});
socket.on('checker_stopped', (data) => {
forceStopped = true; isStopping = false;
resetUIToFreshSession(); setControlsDisabled(false);
showStatusMessage(data.message, 'info');
});
socket.on('checker_paused', (data) => {
showStatusMessage(data.message, 'info');
});
socket.on('checker_continued', (data) => {
showStatusMessage(data.message, 'success');
});
socket.on('checker_completed', (data) => {
showStatusMessage(data.message, 'success');
});
socket.on('inputs_cleared', () => {
forceStopped = true; isStopping = false;
resetUIToFreshSession(); setControlsDisabled(false);
});
socket.on('hits_available', (data) => {
showStatusMessage('Hits file is ready for download!', 'success');
downloadFile(data.content, data.filename);
});
socket.on('hits_download', (data) => {
downloadFile(data.content, data.filename);
showStatusMessage('Hits downloaded successfully!', 'success');
});
socket.on('progress_line_updated', (data) => {
showStatusMessage(data.message, 'success');
// Update the displayed checked count and the input field
document.getElementById('checked').textContent = data.new_checked_count;
document.getElementById('progress-line-input').value = data.new_checked_count;
});
function updateStatsDisplay(data) {
document.getElementById('status').textContent = data.status;
document.getElementById('total-lines').textContent = data.total_lines;
document.getElementById('checked').textContent = data.checked;
document.getElementById('invalid').textContent = data.invalid;
document.getElementById('hits').textContent = data.hits;
document.getElementById('custom').textContent = data.custom;
document.getElementById('total-mega-fan').textContent = data.total_mega_fan;
document.getElementById('total-fan-member').textContent = data.total_fan_member;
document.getElementById('total-ultimate-mega').textContent = data.total_ultimate_mega;
document.getElementById('errors').textContent = data.errors;
document.getElementById('retries').textContent = data.retries;
document.getElementById('cpm').textContent = data.cpm;
document.getElementById('elapsed-time').textContent = data.elapsed_time;
const statusElement = document.getElementById('status');
if (data.status.includes('RUNNING')) {
statusElement.style.color = '#4ecdc4';
} else if (data.status.includes('PAUSED')) {
statusElement.style.color = '#feca57';
} else if (data.status.includes('COMPLETE')) {
statusElement.style.color = '#4ecdc4';
} else {
statusElement.style.color = '#ff6b6b';
}
}
function downloadFile(content, filename) {
const blob = new Blob([content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
function validateSession() {
if (!sessionId) {
showStatusMessage('No active session. Please refresh the page or wait for connection.', 'error');
return false;
}
if (!isConnected) {
showStatusMessage('Not connected to server. Please check your connection.', 'error');
return false;
}
return true;
}
function uploadFileViaHttp(file, fileType) {
if (!validateSession()) return;
const formData = new FormData();
formData.append('file', file);
formData.append('session_id', sessionId);
formData.append('file_type', fileType);
if (fileType === 'proxy') {
formData.append('proxy_type', document.getElementById('proxy-type-select').value);
}
const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload_file', true);
xhr.onload = function() {
if (xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
console.log(`${fileType} upload success:`, response.message);
// Emit the success event to update UI
// These events are already handled by socket.io.on('combo_uploaded') etc.
// No need to manually emit here. The server will emit.
} else {
const errorResponse = JSON.parse(xhr.responseText);
showStatusMessage(`Error uploading ${fileType}: ${errorResponse.message}`, 'error');
console.error(`Error uploading ${fileType}:`, errorResponse);
if (fileType === 'combo') document.getElementById('combo-input').value = '';
if (fileType === 'proxy') document.getElementById('proxy-input').value = '';
}
};
xhr.onerror = function() {
showStatusMessage(`Network error during ${fileType} upload. Please check server connection.`, 'error');
console.error(`Network error during ${fileType} upload.`);
if (fileType === 'combo') document.getElementById('combo-input').value = '';
if (fileType === 'proxy') document.getElementById('proxy-input').value = '';
};
xhr.send(formData);
showStatusMessage(`Uploading ${fileType} file...`, 'info');
}
function uploadContentViaWebSocket(content, fileType) {
if (!validateSession()) return;
if (fileType === 'combo') {
socket.emit('upload_combo', {
session_id: sessionId,
content: content
});
} else if (fileType === 'proxy') {
socket.emit('upload_proxy', {
session_id: sessionId,
content: content,
proxy_type: document.getElementById('proxy-type-select').value
});
}
showStatusMessage(`Processing pasted ${fileType} content...`, 'info');
}
document.addEventListener('DOMContentLoaded', function() {
function triggerFileInput(fileType) {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.txt';
input.onchange = (e) => {
const file = e.target.files[0];
if (file) {
uploadFileViaHttp(file, fileType);
}
};
input.click();
}
document.getElementById('upload-combo-button').addEventListener('click', () => {
if (!validateSession()) return;
const comboContent = document.getElementById('combo-input').value.trim();
if (comboContent) {
const lineCount = comboContent.split('\n').length;
if (lineCount > PASTE_LINE_LIMIT_COMBO) {
showStatusMessage(`Pasted combo content is too large (${lineCount} lines). Please use the file upload dialog for files larger than ${PASTE_LINE_LIMIT_COMBO} lines.`, 'error');
document.getElementById('combo-input').value = '';
return;
}
uploadContentViaWebSocket(comboContent, 'combo');
} else {
triggerFileInput('combo');
}
});
document.getElementById('upload-proxy-button').addEventListener('click', () => {
if (!validateSession()) return;
const proxyContent = document.getElementById('proxy-input').value.trim();
if (proxyContent) {
const lineCount = proxyContent.split('\n').length;
if (lineCount > PASTE_LINE_LIMIT_PROXY) {
showStatusMessage(`Pasted proxy content is too large (${lineCount} lines). Please use the file upload dialog for files larger than ${PASTE_LINE_LIMIT_PROXY} lines.`, 'error');
document.getElementById('proxy-input').value = '';
return;
}
uploadContentViaWebSocket(proxyContent, 'proxy');
} else {
triggerFileInput('proxy');
}
});
document.getElementById('start-button').addEventListener('click', () => { forceStopped = false;
if (!validateSession()) return;
const threads = parseInt(document.getElementById('threads-input').value);
const initialProgressLine = parseInt(document.getElementById('progress-line-input').value);
const proxyType = document.getElementById('proxy-type-select').value; // Get current proxy type
if (isNaN(threads) || threads < 1 || threads > 400) {
showStatusMessage('Please enter a valid thread count (1-400)!', 'error');
return;
}
if (isNaN(initialProgressLine) || initialProgressLine < 0) {
showStatusMessage('Please enter a valid non-negative number for the progress line!', 'error');
return;
}
socket.emit('start_checker', {
session_id: sessionId,
threads: threads,
initial_progress_line: initialProgressLine,
proxy_type: proxyType // Pass proxy type to backend
});
});
document.getElementById('stop-button').addEventListener('click', () => {
if (!validateSession()) return;
if (isStopping) return;
isStopping = true; forceStopped = true;
setControlsDisabled(true);
showStatusMessage('Stopping... please wait a moment.', 'info');
socket.emit('stop_checker', { session_id: sessionId });
});
document.getElementById('pause-button').addEventListener('click', () => {
if (!validateSession()) return;
socket.emit('pause_checker', {
session_id: sessionId
});
});
document.getElementById('continue-button').addEventListener('click', () => { forceStopped = false;
if (!validateSession()) return;
const currentThreads = parseInt(document.getElementById('threads-input').value);
const currentProxyType = document.getElementById('proxy-type-select').value;
socket.emit('continue_checker', {
session_id: sessionId,
threads: currentThreads,
proxy_type: currentProxyType
});
});
document.getElementById('download-hits-button').addEventListener('click', () => {
if (!validateSession()) return;
socket.emit('download_hits', {
session_id: sessionId
});
});
const progressLineInput = document.getElementById('progress-line-input');
progressLineInput.addEventListener('focus', () => {
isProgressLineInputFocused = true;
});
progressLineInput.addEventListener('blur', () => {
isProgressLineInputFocused = false;
});
document.getElementById('set-progress-button').addEventListener('click', () => {
if (!validateSession()) return;
const newProgressLine = parseInt(document.getElementById('progress-line-input').value);
if (isNaN(newProgressLine) || newProgressLine < 0) {
showStatusMessage('Please enter a valid non-negative number for the progress line!', 'error');
return;
}
socket.emit('set_progress_line', {
session_id: sessionId,
progress_line: newProgressLine
});
});
const textareas = document.querySelectorAll('.upload-textarea');
textareas.forEach(textarea => {
textarea.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = Math.min(this.scrollHeight, 300) + 'px';
});
textarea.addEventListener('paste', function(event) {
const pastedText = (event.clipboardData || window.clipboardData).getData('text');
const lineCount = pastedText.split('\n').length;
const isComboTextarea = this.id === 'combo-input';
const limit = isComboTextarea ? PASTE_LINE_LIMIT_COMBO : PASTE_LINE_LIMIT_PROXY;
if (lineCount > limit) {
event.preventDefault();
showStatusMessage(`Pasted ${isComboTextarea ? 'combo' : 'proxy'} content is too large (${lineCount} lines). Please use the file upload dialog for files larger than ${limit} lines.`, 'error');
this.value = '';
}
});
});
document.addEventListener('keydown', function(e) {
if (e.ctrlKey || e.metaKey) {
switch(e.key) {
case 's':
e.preventDefault();
document.getElementById('start-button').click();
break;
case 'q':
e.preventDefault();
document.getElementById('stop-button').click();
break;
case 'p':
e.preventDefault();
document.getElementById('pause-button').click();
break;
case 'r':
e.preventDefault();
document.getElementById('continue-button').click();
break;
case 'd':
e.preventDefault();
document.getElementById('download-hits-button').click();
break;
case 'g':
e.preventDefault();
document.getElementById('set-progress-button').click();
break;
}
}
});
document.getElementById('start-button').title = 'Start Checker (Ctrl+S)';
document.getElementById('stop-button').title = 'Stop Checker (Ctrl+Q)';
document.getElementById('pause-button').title = 'Pause Checker (Ctrl+P)';
document.getElementById('continue-button').title = 'Continue Checker (Ctrl+R)';
document.getElementById('download-hits-button').title = 'Download Hits (Ctrl+D)';
document.getElementById('set-progress-button').title = 'Set Progress Line (Ctrl+G)';
const urlParams = new URLSearchParams(window.location.search);
const urlSessionId = urlParams.get('session');
if (urlSessionId) {
localStorage.setItem('sessionId', urlSessionId);
}
});
document.addEventListener('click', function(e) {
if (e.target.classList.contains('btn-3d')) {
const ripple = document.createElement('span');
const rect = e.target.getBoundingClientRect();
const size = Math.max(rect.width, rect.height);
const x = e.clientX - rect.left - size / 2;
const y = e.clientY - rect.top - size / 2;
ripple.style.width = ripple.style.height = size + 'px';
ripple.style.left = x + 'px';
ripple.style.top = y + 'px';
ripple.classList.add('ripple');
e.target.appendChild(ripple);
setTimeout(() => {
if (ripple.parentNode) {
ripple.parentNode.removeChild(ripple);
}
}, 600);
}
});
const style = document.createElement('style');
style.textContent = `
.btn-3d {
position: relative;
overflow: hidden;
}
.ripple {
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.6);
transform: scale(0);
animation: ripple-animation 0.6s linear;
pointer-events: none;
}
@keyframes ripple-animation {
to {
transform: scale(4);
opacity: 0;
}
}
`;
document.head.appendChild(style);