diff --git a/shared-ip-manager.userscript.js b/shared-ip-manager.userscript.js new file mode 100644 index 0000000..b7d670d --- /dev/null +++ b/shared-ip-manager.userscript.js @@ -0,0 +1,517 @@ +// ==UserScript== +// @name Shared IP Manager +// @namespace http://tampermonkey.net/ +// @version 1.1.0 +// @description Transform shared IP overlay into searchable table format +// @author Ryahn +// @match *://*/* +// @grant none +// ==/UserScript== + +(function() { + 'use strict'; + + // Function to extract country code from flag class + function getCountryCode(flagElement) { + const classes = flagElement.className; + const match = classes.match(/tck-provider-country-flag\s+(\w+)/); + return match ? match[1].toUpperCase() : ''; + } + + // Function to get country name from flag element + function getCountryName(flagElement) { + return flagElement.getAttribute('data-original-title') || ''; + } + + // Function to get provider info + function getProviderInfo(providerElement) { + return { + name: providerElement.textContent.trim(), + asn: providerElement.getAttribute('data-original-title') || '' + }; + } + + // Function to get type info + function getTypeInfo(typeElement) { + return typeElement.getAttribute('data-original-title') || ''; + } + + // Function to extract usage count + function getUsageCount(usageText) { + const match = usageText.match(/(\d+)\s+time/); + return match ? parseInt(match[1]) : 0; + } + + // Function to create flag emoji from country code + function getFlagEmoji(countryCode) { + if (!countryCode) return ''; + const codePoints = countryCode + .toLowerCase() + .split('') + .map(char => 127397 + char.charCodeAt()); + return String.fromCodePoint(...codePoints); + } + + // Function to create the table + function createTable() { + const overlay = document.querySelector('.overlay-content'); + if (!overlay) return; + + // Find all user entries + const userEntries = overlay.querySelectorAll('.block-row.block-row--separated'); + + if (userEntries.length === 0) return; + + // Create table container + const tableContainer = document.createElement('div'); + tableContainer.style.cssText = ` + padding: 0; + background: transparent; + width: 100%; + height: 100%; + overflow: hidden; + box-sizing: border-box; + `; + + // Create search and filter container + const searchFilterContainer = document.createElement('div'); + searchFilterContainer.style.cssText = ` + display: flex; + gap: 10px; + margin-bottom: 15px; + align-items: center; + `; + + // Create search input + const searchInput = document.createElement('input'); + searchInput.type = 'text'; + searchInput.placeholder = 'Search users, countries, providers...'; + searchInput.style.cssText = ` + flex: 1; + padding: 12px; + border: 1px solid #101113; + border-radius: 4px; + font-size: 14px; + box-sizing: border-box; + background: #101113; + color: #374151; + `; + + // Create banned filter button + const bannedFilterButton = document.createElement('button'); + bannedFilterButton.textContent = 'Show Banned Only'; + bannedFilterButton.style.cssText = ` + padding: 12px 16px; + border: 1px solid #dc2626; + border-radius: 4px; + font-size: 14px; + font-weight: 500; + background: #dc262620; + color: #dc2626; + cursor: pointer; + transition: all 0.2s; + white-space: nowrap; + `; + + // Add hover effect for button + bannedFilterButton.addEventListener('mouseenter', function() { + this.style.backgroundColor = '#dc2626'; + this.style.color = 'white'; + }); + bannedFilterButton.addEventListener('mouseleave', function() { + if (!this.classList.contains('active')) { + this.style.backgroundColor = '#dc262620'; + this.style.color = '#dc2626'; + } + }); + + // Create count display + const countDisplay = document.createElement('div'); + countDisplay.style.cssText = ` + color: #6b7280; + font-size: 12px; + margin-left: auto; + padding: 8px 12px; + background: #1a1a1a; + border-radius: 4px; + border: 1px solid #17191b; + `; + + // Add button to container + searchFilterContainer.appendChild(searchInput); + searchFilterContainer.appendChild(bannedFilterButton); + searchFilterContainer.appendChild(countDisplay); + + // Create table wrapper for scrolling + const tableWrapper = document.createElement('div'); + tableWrapper.style.cssText = ` + width: 100%; + overflow: auto; + max-height: calc(100vh - 150px); + border: 1px solid #e5e7eb; + border-radius: 6px; + `; + + // Create table + const table = document.createElement('table'); + table.style.cssText = ` + width: 100%; + min-width: 800px; + border-collapse: collapse; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + font-size: 13px; + margin: 0; + `; + + // Create table header + const thead = document.createElement('thead'); + thead.style.cssText = `position: sticky; top: 0; z-index: 10;`; + + const headerRow = document.createElement('tr'); + headerRow.style.cssText = `background: #101113; border-bottom: 1px solid #101113; color: #959595;`; + + // Username header (sortable) + const usernameHeader = document.createElement('th'); + usernameHeader.id = 'sort-username'; + usernameHeader.style.cssText = `padding: 10px 12px; text-align: left; border-right: 1px solid #17191b; font-weight: 600; color: #959595; white-space: nowrap; cursor: pointer; user-select: none;`; + usernameHeader.innerHTML = `Username ↕`; + + // Join Date header + const joinDateHeader = document.createElement('th'); + joinDateHeader.style.cssText = `padding: 10px 12px; text-align: left; border-right: 1px solid #17191b; font-weight: 600; color: #959595; white-space: nowrap;`; + joinDateHeader.textContent = 'Join Date'; + + // Messages header + const messagesHeader = document.createElement('th'); + messagesHeader.style.cssText = `padding: 10px 12px; text-align: left; border-right: 1px solid #17191b; font-weight: 600; color: #959595; white-space: nowrap;`; + messagesHeader.textContent = 'Messages'; + + // Country header + const countryHeader = document.createElement('th'); + countryHeader.style.cssText = `padding: 10px 12px; text-align: left; border-right: 1px solid #17191b; font-weight: 600; color: #959595; white-space: nowrap;`; + countryHeader.textContent = 'Country'; + + // Provider header + const providerHeader = document.createElement('th'); + providerHeader.style.cssText = `padding: 10px 12px; text-align: left; border-right: 1px solid #17191b; font-weight: 600; color: #959595; white-space: nowrap;`; + providerHeader.textContent = 'Provider'; + + // Type header (sortable) + const typeHeader = document.createElement('th'); + typeHeader.id = 'sort-type'; + typeHeader.style.cssText = `padding: 10px 12px; text-align: left; border-right: 1px solid #17191b; font-weight: 600; color: #959595; white-space: nowrap; cursor: pointer; user-select: none;`; + typeHeader.innerHTML = `Type ↕`; + + // Usage header + const usageHeader = document.createElement('th'); + usageHeader.style.cssText = `padding: 10px 12px; text-align: left; font-weight: 600; color: #959595; white-space: nowrap;`; + usageHeader.textContent = 'Usage'; + + // Append all headers to row + headerRow.appendChild(usernameHeader); + headerRow.appendChild(joinDateHeader); + headerRow.appendChild(messagesHeader); + headerRow.appendChild(countryHeader); + headerRow.appendChild(providerHeader); + headerRow.appendChild(typeHeader); + headerRow.appendChild(usageHeader); + + thead.appendChild(headerRow); + + // Create table body + const tbody = document.createElement('tbody'); + + // Process each user entry + userEntries.forEach((entry, index) => { + const row = document.createElement('tr'); + row.style.cssText = ` + border-bottom: 1px solid #17191b; + transition: background-color 0.2s; + `; + row.style.backgroundColor = index % 2 === 0 ? '#131313' : '#131313'; + + // Username + const usernameLink = entry.querySelector('h3.contentRow-header a.username'); + const username = usernameLink ? usernameLink.textContent.trim() : 'N/A'; + const profileUrl = usernameLink ? usernameLink.href : '#'; + + // Check if user is banned + const usernameSpan = entry.querySelector('h3.contentRow-header a.username span'); + const isBanned = usernameSpan && usernameSpan.classList.contains('username--banned'); + + // Join date + const joinDateElement = entry.querySelector('time.u-dt'); + const joinDate = joinDateElement ? joinDateElement.textContent.trim() : 'N/A'; + + // Messages + const dtElements = entry.querySelectorAll('dt'); + let messagesElement = null; + for (const dt of dtElements) { + if (dt.textContent.includes('Messages')) { + messagesElement = dt; + break; + } + } + const messages = messagesElement ? + (messagesElement.nextElementSibling ? messagesElement.nextElementSibling.textContent.trim() : '0') : '0'; + + // Country flag and name + const flagElement = entry.querySelector('.tck-provider-country-flag'); + const countryCode = flagElement ? getCountryCode(flagElement) : ''; + const countryName = flagElement ? getCountryName(flagElement) : ''; + const flagEmoji = getFlagEmoji(countryCode); + + // Provider info + const providerElement = entry.querySelector('.tck-provider-txt'); + const providerInfo = providerElement ? getProviderInfo(providerElement) : { name: 'N/A', asn: '' }; + + // Type info + const typeElement = entry.querySelector('.tck-provider-type'); + const typeInfo = typeElement ? getTypeInfo(typeElement) : 'N/A'; + + // Usage count + const liElements = entry.querySelectorAll('li'); + let usageElement = null; + for (const li of liElements) { + if (li.textContent.includes('time')) { + usageElement = li; + break; + } + } + const usageText = usageElement ? usageElement.textContent.trim() : '0 time'; + const usageCount = getUsageCount(usageText); + + + // Build row HTML + row.innerHTML = ` +