// 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 = `
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}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
${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 = `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 = `
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 = `Loading file content...
${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 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.
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.
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 = `Usage: ${trigger.usage}
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}