Last active
June 8, 2023 02:11
-
-
Save Bluscream/dbba7ee2e8b2e9c8c9b31150a7d2e502 to your computer and use it in GitHub Desktop.
Raidmax Server List Scraper with caching (supports "php", "php-pretty", "csv", "json", "html","txt","favorites")
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"require": { | |
"symfony/browser-kit": "^5.4", | |
"symfony/process": "^5.4", | |
"symfony/http-client": "^5.4", | |
"symfony/dom-crawler": "^5.4", | |
"symfony/css-selector": "^5.4", | |
"voku/stringy": "^6.5" | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// ini_set('display_errors', 1); | |
// ini_set('display_startup_errors', 1); | |
// error_reporting(E_ALL); | |
$debug_output = ''; | |
$array_text = ''; | |
function scriptlog($msg) { | |
global $debug_output; | |
$fancy = print_r($msg, TRUE); | |
error_log($fancy . "\n", 3, 'index.log'); // /var/www/html/iw4/favorites/ | |
$debug_output .= $msg . "<br>"; | |
} | |
function process_array($array) { | |
$processed_array = []; | |
foreach ($array as $item) { | |
if (strpos($item, ':') === false) { | |
// Add default port to items without a port | |
$processed_array[] = $item . ':28960'; | |
} else { | |
$processed_array[] = $item; | |
} | |
} | |
return $processed_array; | |
} | |
function merge_arrays($array1, $array2) { | |
scriptlog("Merging Array 1 (" . count($array1) . " elements) with Array 2 (" . count($array2) . " elements)"); | |
$merged_array = array_merge($array1, $array2); | |
$duplicates = (count($array1) + count($array2)) - count($merged_array); | |
scriptlog("Merged array is now " . count($merged_array) . " elements large. ($duplicates duplicates removed)"); | |
return $merged_array; | |
} | |
function merge_json_files($input_files, $input_urls) { | |
global $array_text; | |
$merged_array = []; | |
$context = stream_context_create([ | |
'ssl' => [ 'verify_peer' => false, 'verify_peer_name' => false ] | |
]); | |
// Merge the arrays from input files | |
foreach ($input_files as $file) { | |
$data = json_decode(file_get_contents($file), true); | |
scriptlog("Got array with " . count($data) . " elements from $file"); | |
$data = process_array($data); | |
$merged_array = merge_arrays($merged_array, $data); | |
} | |
// Merge the array from input URLs | |
foreach ($input_urls as $input_url) { | |
$data = json_decode(file_get_contents($input_url, false, $context), true); | |
scriptlog("Got array with " . count($data) . " elements from $input_url"); | |
$data = process_array($data); | |
$merged_array = merge_arrays($merged_array, $data); | |
} | |
$oldsize = count($merged_array); | |
// Remove duplicates and sort the merged array | |
$merged_array = array_values(array_unique($merged_array)); | |
$newsize = count($merged_array); | |
$duplicates = $oldsize - $newsize; | |
scriptlog("Final array is $newsize elements large. ($duplicates duplicates removed)"); | |
sort($merged_array); | |
// Generate the output file | |
$output_file = 'favourites.json'; | |
$array_text = json_encode($merged_array, JSON_PRETTY_PRINT); | |
file_put_contents($output_file, $array_text); | |
// Provide the merged file for download | |
header('Content-Disposition: attachment; filename="' . basename($output_file) . '"'); | |
header('Content-Type: application/json'); | |
header('Content-Length: ' . filesize($output_file)); | |
readfile($output_file); | |
exit; | |
} | |
$now = date("d.m.Y - H:i"); | |
scriptlog("Running at $now"); | |
// Check if the form was submitted | |
if (isset($_POST['submit'])) { | |
$input_files = []; | |
$input_urls = []; | |
// Handle uploaded input files | |
foreach ($_FILES['input_files']['tmp_name'] as $tmp_name) { | |
if (!empty($tmp_name)) { | |
$input_files[] = $tmp_name; | |
} | |
} | |
// Handle input URL | |
$input_urls[] = $_POST['input_url']; | |
// Merge the JSON files and provide the merged file for download | |
merge_json_files($input_files, $input_urls); | |
} else { | |
// Pre-fill the input URL | |
$input_url = 'http://minopia.de/iw4/servers?format=favorites&games=iw4&filename=favourites.json&dl'; | |
} | |
scriptlog($debug_output); | |
?> | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>IW4x Favorites Merger</title> | |
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> | |
</head> | |
<body> | |
<div class="container"> | |
<h1 class="mt-5 mb-4">IW4x Favorites Merger</h1> | |
<form method="POST" enctype="multipart/form-data"> | |
<div class="form-group"> | |
<label for="input_files">Upload Input Files:</label> | |
<label for="input_files">(Usually <code>C:\Program Files (x86)\Steam\steamapps\common\Call of Duty Modern Warfare 2\players\favourites.json</code>)</label> | |
<input type="file" class="form-control-file" name="input_files[]" multiple> | |
</div> | |
<div class="form-group"> | |
<label for="input_url">Input URL:</label> | |
<input type="text" class="form-control" name="input_url" value="<?php echo $input_url; ?>"> | |
</div> | |
<button type="submit" name="submit" class="btn btn-primary">Merge and Download</button> | |
</form> | |
<hr> | |
<!-- <h4>Debug Output:</h4> --> | |
<textarea class="form-control" rows="10" readonly><?php echo htmlentities($array_text); ?></textarea> | |
</div> | |
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js"></script> | |
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> | |
<script> | |
console.log(`<?php echo $debug_output; ?>`); | |
</script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import argparse | |
import json | |
def process_array(array): | |
processed_array = [] | |
for item in array: | |
if ":" not in item: | |
# Add default port to items without a port | |
processed_array.append(f"{item}:28960") | |
else: | |
processed_array.append(item) | |
return processed_array | |
def merge_json_files(input_files:list[str], output_file:str): | |
merged_array = [] | |
# Iterate over each input file | |
for file_path in input_files: | |
# Read the JSON file | |
with open(file_path, 'r') as file: | |
data = json.load(file) | |
print(f"Got array with {len(data)} elements from {file_path}") | |
data = process_array(data) | |
# Merge the arrays without duplicates | |
merged_array = list(set(merged_array + data)) | |
print(f"Merged array is now {len(merged_array)} elements large.") | |
merged_array = sorted(merged_array) | |
# Write the merged array into the output file | |
with open(output_file, 'w') as file: | |
print(f"Writing {len(merged_array)} elements to {output_file}") | |
json.dump(merged_array, file, indent=4) | |
# Create the argument parser | |
parser = argparse.ArgumentParser(description='Merge multiple JSON files.') | |
# Add the positional arguments | |
# parser.add_argument('input_files', nargs='+', help='input JSON files') | |
parser.add_argument('--output_file', default='output.json', help='output JSON file') | |
# Parse the command-line arguments | |
args = parser.parse_args() | |
if not "input_files" in args or not args.input_files: | |
args.input_files = [ | |
"G:/Steam/steamapps/common/Call of Duty Modern Warfare 2/players/favourites.json", | |
"D:/Downloads/favourites.json", | |
"D:/Downloads/favorites.json", | |
] | |
# Merge the JSON files | |
merge_json_files(args.input_files, args.output_file) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from pathlib import Path | |
from json import load, dump | |
import requests | |
# File path for the favourites.json file | |
favourites_file = Path("S:/Call of Duty/CoD 6 (MW2)/players/favourites.json") | |
game_name = "iw4" | |
servers_url = f"http://minopia.de/iw4/servers/?games={game_name}&format=json" | |
# servers_url = f"http://192.168.2.38/iw4/servers/index.json" | |
print(f"Requesting server list from {servers_url}") | |
response = requests.get(servers_url, timeout=1) | |
print(f"Got response from server: {response.text[:100]}") | |
server_data = response.json() | |
servers = [] | |
print(f"Got {len(server_data)} games from {servers_url}") | |
print(f"Got {len(server_data[game_name])} games in {game_name}") | |
for server in server_data["iw4"]: | |
ip = server["ip"] | |
port = server["port"] if "port" in server else "28960" | |
if ip and port: | |
server_address = ip + ":" + port | |
if server_address not in servers: | |
servers.append(server_address) | |
print(f"Added {server_address} to favorites") | |
else: | |
print(f"{server_address} already exists in favorites") | |
with open(favourites_file, "r") as file: | |
favourites = load(file) | |
print(f"Loaded {len(favourites)} favorites from {favourites_file}") | |
merged_favourites = favourites + servers | |
merged_favourites = list(set(merged_favourites)) | |
backup_file = favourites_file.with_stem("favorites.bak") | |
favourites_file.rename(backup_file) | |
print(f"Created backup at {backup_file}") | |
with open(favourites_file, "w") as file: | |
dump(merged_favourites, file, indent=4) | |
print(f"Wrote {len(merged_favourites)} favorites to {favourites_file}") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// ini_set('display_errors', 1); | |
// ini_set('display_startup_errors', 1); | |
// error_reporting(E_ALL); | |
require 'vendor/autoload.php'; | |
use Symfony\Component\BrowserKit\HttpBrowser; | |
use Symfony\Component\HttpClient\HttpClient; | |
use Symfony\Component\DomCrawler\Crawler; | |
$color_codes = [ | |
'^0' => 'black', | |
'^1' => 'red', | |
'^2' => 'green', | |
'^3' => 'yellow', | |
'^4' => 'blue', | |
'^5' => 'cyan', | |
'^6' => 'pink', | |
'^7' => 'white', | |
'^8' => 'black', | |
'^9' => 'black', | |
'^;' => '', // map | |
'^:' => '' // rainbow | |
]; | |
$games = array( | |
"csgo" => "Counter-Strike: Global Offensive", | |
"t4" => "Call of Duty 4: Modern Warfare", | |
"t5" => "Call of Duty: World at War", | |
"t6" => "Call of Duty: Modern Warfare 2", | |
"t7" => "Call of Duty: Black Ops", | |
"iw3" => "Call of Duty: Modern Warfare 3", | |
"h1" => "Call of Duty: Ghosts", | |
"iw4" => "Call of Duty: Advanced Warfare", | |
"shg1"=> "Call of Duty: WWII", | |
"iw5" => "Call of Duty: Black Ops 4", | |
"iw6" => "Call of Duty: Modern Warfare (2019)", | |
); | |
$game_modes = array( | |
"0" => "Unknown", | |
"aiz" => "Aliens in Zombies", | |
"aliens" => "Aliens", | |
"cj" => "Cranked", | |
"CLASSICX" => "Classic", | |
"clean" => "Clean", | |
"cmp" => "Champion Hill", | |
"conf" => "Confirmed", | |
"ctf" => "Capture the Flag", | |
"dd" => "Drop Zone", | |
"deathrun" => "Deathrun", | |
"dem" => "Demolition", | |
"dm" => "Deathmatch", | |
"dom" => "Domination", | |
"freezetag" => "Freeze Tag", | |
"gun" => "Gun Game", | |
"gungame" => "Gun Game", | |
"hq" => "Headquarters", | |
"infect" => "Infected", | |
"koth" => "King of the Hill", | |
"mike" => "Mike Myers", | |
"oic" => "One in the Chamber", | |
"prop" => "Prop Hunt", | |
"sas" => "Search & Rescue", | |
"sd" => "Search & Destroy", | |
"surv" => "Survival", | |
"survival" => "Survival", | |
"tdm" => "Team Deathmatch", | |
"teamgungame" => "Team Gun Game", | |
"ts" => "Trick Shot", | |
"twar" => "Team War", | |
"war" => "Team Deathmatch", | |
"zclassic" => "Zombies Classic", | |
"zcleansed" => "Zombies Cleansed", | |
"zgrief" => "Zombies Grief", | |
"zom" => "Zombies", | |
"zombies" => "Zombies", | |
"zstandard" => "Zombies Standard" | |
); | |
$options = [ | |
"format" => ["php", "php-pretty", "csv", "json", "html","txt","favorites"], | |
"games" => $games,// ["csgo","h1","iw3","iw4","iw5","iw6","shg1","t4","t5","t6","t7"], | |
"modes" => $game_modes, // ["zstandard","zombies","zom","zgrief","zcleansed","zclassic","war","twar","teamgungame","tdm","survival","surv","sd","sas","prop","oic","mike","koth","infect","hq","gungame","gun","gametype","freezetag","dom","dm","dem","deathrun","dd","ctf","conf","cmp","clean","CLASSICX","cj","aliens","aiz","0"], | |
"names" => [""], | |
"sort" => ["name","ip","port","mode","map","clients_online","clients_max"], | |
"no_ts" => [""], | |
"minclients" => ["0","1","18"], | |
"maxclients" => ["0","1","18"], | |
"debug" => [""], | |
"dl" => [""], | |
"filename" => [""], | |
"scrape" => [], | |
]; | |
if (!function_exists('str_contains')) { | |
function str_contains($haystack, $needle) { | |
return $needle !== '' && mb_strpos($haystack, $needle) !== false; | |
} | |
} | |
function newline($amount = 1) { | |
return str_repeat("<br>\n", $amount); | |
} | |
function println($msg = "") { | |
echo $msg . newline(); | |
} | |
function normalize($string) { | |
return titleCaseSpecial(str_replace("_", " ", htmlspecialchars($string))); | |
} | |
function parse_color_codes($string) { | |
global $color_codes; | |
$string = htmlspecialchars($string); | |
// Define an associative array that maps the color codes to their corresponding HTML color names. | |
// Use the preg_replace_callback function to search for the color codes in the input string and replace them with the appropriate HTML tags. | |
$result = preg_replace_callback('/\^([\d;:])/', | |
function ($matches) use ($color_codes) { | |
return '<text color="' . $color_codes[$matches[0]] . '">'; | |
}, | |
$string | |
); | |
// Close the opened <text> tags. | |
$result .= str_repeat('</text>', substr_count($result, '<text')); | |
return $result; | |
} | |
function getCurrentUrl() { | |
$protocol = strpos(strtolower($_SERVER['SERVER_PROTOCOL']),'https') === FALSE ? 'http' : 'https'; | |
$host = $_SERVER['HTTP_HOST']; | |
$script = $_SERVER['SCRIPT_NAME']; | |
$params = $_SERVER['QUERY_STRING']; | |
return $protocol . '://' . $host . $script . '?' . $params; | |
} | |
function addToUrl($url, $key, $value = null) { | |
$query = parse_url($url, PHP_URL_QUERY); | |
if ($query) { | |
parse_str($query, $queryParams); | |
$queryParams[$key] = $value; | |
$url = str_replace("?$query", '?' . http_build_query($queryParams), $url); | |
} else { | |
$url .= '?' . urlencode($key) . '=' . urlencode($value); | |
} | |
return $url; | |
} | |
function buildUrl($params) { | |
return $_SERVER['PHP_SELF'] . '?' . http_build_query($params); | |
} | |
function writeUrl($params, $text = NULL) { | |
$url = buildUrl($params); | |
if (is_null($text)) $text = $url; | |
return "<a href='$url'>$text</a>"; | |
} | |
function writeUrlAddParam($key, $value = null, $text = null) { | |
global $currentUrl; | |
$url = addToUrl($currentUrl, $key, $value); | |
if (is_null($text)) $text = $url; | |
return "<a href='$url'>$text</a>"; | |
} | |
function copyToClipboardButton($text, $buttonText) { | |
return "<button onclick=\"copyToClipboard('$text')\">$buttonText</button>"; | |
} | |
if (!function_exists('array_is_list')) { | |
function array_is_list(array $arr) | |
{ | |
if ($arr === []) { | |
return true; | |
} | |
return array_keys($arr) === range(0, count($arr) - 1); | |
} | |
} | |
function transformOptionsToArray($options) | |
{ | |
global $currentUrl; | |
$menuItems = []; | |
// $currentUrl = strtok($_SERVER['REQUEST_URI'], '?'); | |
foreach ($options as $menu => $items) { | |
if (array_is_list($items)) { | |
foreach ($items as $item) { | |
$href = addToUrl($currentUrl, $menu, $item); | |
$menuItems[$menu][$item] = $href; | |
} | |
} else { | |
foreach ($items as $name => $description) { | |
$href = addToUrl($currentUrl, $menu, $name); | |
$menuItems[$menu][$description] = $href; | |
} | |
} | |
} | |
return $menuItems; | |
} | |
function print_header() | |
{ | |
echo '<!DOCTYPE html><html lang="en" data-bs-theme="dark"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Raidmax Servers</title> | |
<link rel="stylesheet" href="https://mdbcdn.b-cdn.net/wp-content/themes/mdbootstrap4/docs-app/css/dist/mdb5/standard/core.min.css"> | |
<!-- link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" --> | |
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js"></script> | |
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.31.3/js/jquery.tablesorter.min.js"></script> | |
</head><body class="dark" data-bs-theme="dark"> | |
<script> | |
function copyToClipboard(text) { | |
var tmp = document.createElement("input"); | |
tmp.setAttribute("display", "none"); | |
tmp.setAttribute("type", "text"); | |
tmp.setAttribute("value", text); | |
document.body.appendChild(tmp); | |
tmp.select(); | |
tmp.setSelectionRange(0, 99999); | |
document.execCommand("copy"); | |
navigator.clipboard.writeText(tmp.value); | |
tmp.parentElement.removeChild(tmp); | |
} | |
</script> | |
<nav class="navbar navbar-expand-lg navbar-dark bg-dark"> | |
<a class="navbar-brand" href="#">XLabs Toolbox</a> | |
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> | |
<span class="navbar-toggler-icon"></span> | |
</button>'; | |
} | |
function print_menu_start() { | |
echo '<div class="collapse navbar-collapse" id="navbarSupportedContent"><ul class="nav navbar-nav mr-auto">'; | |
} | |
function print_menu_items($menuItems) { | |
foreach ($menuItems as $menu => $items) { | |
echo '<li class="nav-item dropdown"> | |
<a class="nav-link dropdown-toggle" href="#" id="' . $menu . 'Dropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">' . ucfirst($menu) . '</a> | |
<div class="dropdown-menu" aria-labelledby="' . $menu . 'Dropdown">'; | |
foreach ($items as $itemName => $href) { | |
echo '<a class="dropdown-item" href="' . $href . '">' . ucfirst($itemName) . '</a>'; | |
} | |
echo '</div></li>'; | |
} | |
} | |
function print_menu_end() { | |
echo '</ul></div></nav>'; | |
} | |
function print_options() { | |
global $options; | |
foreach ($options as $option => $values) { | |
// $url = "<a href='".$_SERVER["PHP_SELF"] . "?"; | |
echo writeUrlAddParam($option, null, normalize($option)) . ": "; | |
if (array_is_list($values)) { | |
foreach ($values as $value) { | |
echo writeUrlAddParam($option, $value, $value) . " "; | |
} | |
} else { | |
foreach ($values as $name => $description) { | |
echo writeUrlAddParam($option, $name, $description) . " "; | |
} | |
} | |
println(); | |
} | |
} | |
function print_options_fancy() | |
{ | |
global $options; | |
print_menu_items(transformOptionsToArray($options)); | |
} | |
function print_banner() { | |
print_header(); | |
print_menu_start(); | |
print_menu_items([ | |
"Mirrors" => [ | |
"xlabs-mirror.github.io" => "https://xlabs-mirror.github.io/", | |
"xlabs-mirror.gitlab.io/xlabs.dev" => "https://xlabs-mirror.gitlab.io/xlabs.dev/", | |
"minopia.de/archive/xlabs.dev" => "https://minopia.de/archive/xlabs.dev/", | |
"minopia.de/archive/xlabs.dev" => "https://minopia.de/archive/xlabs.dev/", | |
"minopia.de/archive/updater.xlabs.dev" => "https://minopia.de/archive/updater.xlabs.dev/", | |
], | |
"Tools" => [ | |
"Servers" => "https://minopia.de/iw4/servers/?formats", | |
"Favorites Merge Tool" => "https://minopia.de/iw4/favorites/", | |
], | |
"|" => [], | |
]); | |
print_options_fancy(); | |
print_menu_end(); | |
} | |
if (isset($_GET["help"])||isset($_GET["about"])||isset($_GET["formats"])) { | |
print_banner(); | |
exit(); | |
} elseif (isset($_GET["debug"])) { | |
print_options(); | |
exit(); | |
} | |
function get($key, $default=NULL) { | |
return array_key_exists($key, $_GET) ? $_GET[$key] : $default; | |
} | |
function appendThings(/* map[string,mixed] */ &$array, /* string */ $key, /* string */ $value) { | |
if (!isset($array[$key])) | |
$array[$key] = $value; | |
else if (is_array($array[$key])) | |
$array[$key][] = $value; | |
else | |
$array[$key] = array($array[$key], $value); | |
return $array; | |
} | |
function highlight_array($array, $name = 'var') { | |
highlight_string("<?php\n\$$name =\n" . var_export($array, true) . ";\n?>"); | |
} | |
function titleCaseSpecial($string) | |
{ | |
return preg_replace_callback( | |
'~[/.-]\p{Ll}~u', | |
function ($m) { | |
return mb_strtoupper($m[0], 'UTF-8'); | |
}, | |
mb_convert_case($string, MB_CASE_TITLE, 'UTF-8') | |
); | |
} | |
function remove_html_comments($content = '') { | |
return preg_replace('/<!--(.|\s)*?-->/', '', $content); | |
} | |
function generateHtmlTable($array) { | |
global $game_modes; | |
$table = '<table class="table table-striped table-bordered table-hover" style="font-size:medium;">'; | |
$table .= '<thead><tr>'; | |
$table .= "<th><b>".count($array)."</b></th>"; | |
foreach ($array[0] as $key => $value) { | |
$table .= '<th>' . normalize($key) . '</th>'; | |
} | |
$table .= '</tr></thead>'; | |
$table .= '<tbody>'; | |
foreach ($array as $row) { | |
$table .= '<tr>'; | |
if (is_array($row)) { | |
$ip = $row["ip"]; | |
$btn = copyToClipboardButton("connect ".$ip.":".$row["port"], "📄"); | |
// $table .= "<td>". $btn . " <img src='http://api.hostip.info/flag.php?ip=$ip' style='display:block!important'/></td>"; | |
$table .= "<td>$btn</td>"; | |
foreach ($row as $key=>$value) { | |
if (is_array($value)) { | |
$table .= '<td>' . generateHtmlTable($value) . '</td>'; | |
} else { | |
$table .= '<td>'; | |
if ($key === "hostname") { | |
$table .= parse_color_codes($value); | |
} elseif ($key === "gametype") { | |
$hostname = strtolower($row["hostname"]); | |
if (str_contains($hostname, "trick") || str_contains($hostname, "tsd")) $value = "ts"; | |
$table .= $game_modes[$value]; | |
} else { | |
$table .= $value; | |
if ($key === "ip") $table .= " <i class='flag flag-".strtolower(geoip_country_code_by_name($key))."' />"; | |
} | |
$table .= '</td>'; | |
} | |
} | |
} | |
$table .= '</tr>'; | |
} | |
$table .= '</tbody></table>'; | |
return $table; | |
} | |
function generateCsv($array) { | |
if (empty($array) || !is_array($array) || empty(reset($array)) || !is_array(reset($array))) { | |
return ''; | |
} | |
$first_game = reset($array); | |
$keys = array_keys(reset($first_game)); | |
$csv = "game," . implode(',', $keys) . "\n"; | |
foreach($array as $game => $game_servers) { | |
foreach ($game_servers as $row) { | |
$rowData = array(); | |
foreach ($keys as $key) { | |
$rowData[] = isset($row[$key]) ? '"' . str_replace('"', '""', $row[$key]) . '"' : ''; | |
} | |
$csv .= "\"$game\"," . implode(',', $rowData) . "\n"; | |
} | |
} | |
return $csv; | |
} | |
function fetchFileWithCaching($url, $expirationTime = 60, $cacheFile = NULL) { // , $force = false | |
global $force; | |
if (is_null($cacheFile)) $cacheFile = md5($url); | |
if (!$force && file_exists($cacheFile) && time() - filemtime($cacheFile) < $expirationTime) { | |
} else { | |
$browser = new HttpBrowser(HttpClient::create()); | |
$response = $browser->request('GET', $url); | |
if ($browser->getResponse()->getStatusCode() === 200) { | |
$content = $browser->getResponse()->getContent(); | |
file_put_contents($cacheFile, $content); | |
return $content; | |
} | |
} | |
return file_get_contents($cacheFile); | |
} | |
function scrapeServersFromHTML($url) { | |
global $filter_games; | |
if (!isset($url)) $url = "https://master.iw4.zip/servers"; // http://api.raidmax.org:5000/servers | |
$html = remove_html_comments(fetchFileWithCaching($url, 60, "servers.html")); | |
$crawler = new Crawler($html); | |
$servers = []; | |
$games = $crawler->filterXPath("//div[contains(concat(' ', normalize-space(@class), ' '), ' game-server-panel ')]"); | |
$games->each(function (Crawler $game) use (&$servers, &$filter_games) { | |
$name = strtolower(str_replace("_servers", "", $game->attr('id') ?? 'Unknown')); | |
if (!is_null($filter_games) && !empty($filter_games) && !in_array($name, $filter_games)) return; | |
$serverRows = $game->filterXPath("//tr[contains(@class, 'server-row')]"); | |
$serverRows->each(function (Crawler $row) use (&$servers, &$name, &$game) { | |
$hostnameNode = $row->filter('td.server-hostname'); | |
$ipNode = $row->filter('[data-ip]'); | |
$portNode = $row->filter('[data-port]'); | |
$gameModeNode = $row->filter('td.server-gametype'); | |
$mapNameNode = $row->filter('td.server-map'); | |
$clientInfoNode = $row->filter('td.server-clientnum'); | |
$server = [ | |
"name" => $hostnameNode->count() ? $hostnameNode->attr('data-hostname') : '', | |
"ip" => $ipNode->count() ? $ipNode->attr('data-ip') : '', | |
"port" => $portNode->count() ? $portNode->attr('data-port') : '', | |
"mode" => $gameModeNode->count() ? $gameModeNode->attr('data-gametype') : '', | |
"map" => $mapNameNode->count() ? $mapNameNode->attr('data-map') : '', | |
"clients_online" => $clientInfoNode->count() ? $clientInfoNode->attr('data-clientnum') : '', | |
"clients_max" => $clientInfoNode->count() ? $clientInfoNode->attr('data-maxclientnum') : '', | |
]; | |
if ($server["ip"] === "0.0.0.0" || $server["ip"] === "127.0.0.1") return; | |
if (!array_key_exists($name, $servers)) $servers[$name] = array(); | |
appendThings($servers, $name, $server); // TODO: Fix this omg | |
}); | |
}); | |
return $servers; | |
} | |
function getServersFromJSON($url) { | |
global $filter_games;global $filter_modes;global $filter_names;global $no_trickshot;global $minclients;global $maxclients; | |
if (!isset($url)) $url = "http://api.raidmax.org:5000/instance/"; | |
$data = json_decode(fetchFileWithCaching($url, 60, "servers.json"), true); | |
$servers = []; | |
foreach ($data as $instance) { | |
if (isset($instance['servers'])) { | |
foreach ($instance['servers'] as $server) { | |
$game = strtolower($server['game']); | |
if (!is_null($filter_games) && $filter_games && !in_array($game, $filter_games)) continue; | |
$hostname = $server['hostname']; | |
$lower_hostname = strtolower($hostname); | |
if (!is_null($filter_names) && $filter_names && in_array($lower_hostname, $filter_names)) continue; | |
if ($no_trickshot && in_array($lower_hostname, ["trick","ts"])) continue; | |
$ip = $server['ip']; | |
$port = $server['port']; | |
$gametype = $server['gametype']; | |
if (!is_null($filter_modes) && $filter_modes && !in_array($gametype, $filter_modes)) continue; | |
$map = $server['map']; | |
$clients_online = $server['clientnum']; | |
if (!is_null($minclients) && $minclients && $clients_online < $minclients) continue; | |
if (!is_null($maxclients) && $maxclients && $clients_online > $maxclients) continue; | |
$clients_max = $server['maxclientnum']; | |
$game_version = $server['version']; | |
$server = compact('hostname', 'ip', 'port', 'gametype', 'map', 'clients_online', 'clients_max', 'game_version'); | |
if (!array_key_exists($game, $servers)) $servers[$game] = array(); | |
appendThings($servers, $game, $server); // TODO: Fix this omg | |
} | |
} | |
} | |
// print_r($servers); | |
return $servers; | |
} | |
$currentUrl = getCurrentUrl(); | |
$filter_games = get("games", NULL); | |
if (!is_null($filter_games)) { | |
$filter_games = explode(",", strtolower($filter_games)); | |
} | |
$filter_modes = get("modes", NULL); | |
if (!is_null($filter_modes)) { | |
$filter_modes = explode(",", strtolower($filter_modes)); | |
} | |
$filter_names = get("names", NULL); | |
if (!is_null($filter_names)) { | |
$filter_names = array_map('strtolower', explode(",", strtolower($filter_names))); | |
} | |
$no_trickshot = isset($_GET["no_ts"]); | |
$minclients = get("minclients", NULL); | |
if (!is_null($minclients)) { | |
$minclients = intval($minclients); | |
} | |
$maxclients = get("maxclients", NULL); | |
if (!is_null($maxclients)) { | |
$maxclients = intval($maxclients); | |
} | |
$url = get("url", NULL); | |
$force = get("force", false); | |
if (isset($_GET["scrape"])) $servers = scrapeServersFromHTML($url); | |
else $servers = getServersFromJSON($url); | |
$sort = get("sort", "clients_online"); | |
if (!is_null($sort)) { | |
foreach($servers as $game => &$game_servers) { | |
uasort($game_servers, fn($a, $b) => $a[$sort] <=> $b[$sort]); | |
} | |
} | |
$output = "";$filename = "servers.txt"; | |
$format = get("format", "html"); | |
switch ($format) { | |
case "php": | |
header('Content-Type: text/plain'); | |
$output = var_export($servers, true); | |
$filename="servers.php"; | |
break; | |
case "php-pretty": | |
header('Content-Type: text/html'); | |
$output = highlight_array($servers, 'servers'); | |
$filename="servers.html"; | |
break; | |
case "txt": | |
header('Content-Type: text/plain'); | |
foreach($servers as $game => $game_servers) { | |
foreach($game_servers as $server) { | |
$output .= $server["ip"] . ":" . (empty($server["port"]) ? '28960' : $server["port"]) . "\n"; | |
} | |
} | |
$filename="servers.txt"; | |
break; | |
case "csv": | |
if (isset($_GET["dl"])) header("Content-type: text/csv"); | |
else header('Content-Type: text/plain'); | |
$output = generateCsv($servers); | |
$filename="servers.csv"; | |
break; | |
case "json": | |
header('Content-Type: application/json'); | |
$output = json_encode(array_filter($servers), JSON_PRETTY_PRINT); | |
$filename="servers.json"; | |
break; | |
case "favorites": | |
header('Content-Type: application/json'); | |
$favs = array(); | |
foreach($servers as $game => $game_servers) { | |
foreach($game_servers as $server) { | |
$favs[] = $server["ip"] . ":" . (empty($server["port"]) ? '28960' : $server["port"]); | |
} | |
} | |
sort($favs); | |
$output = json_encode(array_filter($favs), JSON_PRETTY_PRINT); | |
$filename="servers.json"; | |
break; | |
case "html": | |
header('Content-Type: text/html'); | |
print_banner(); | |
$output .= '<script> $(document).ready(function() { $(".table").tablesorter({ | |
theme : "blue", | |
sortList: [[6,1],[8,1]] | |
}); }); </script>'; | |
// foreach($servers as $game => $game_servers) { | |
// $_url = addToUrl($currentUrl, "games", $game); | |
// // $output .= "<h1><a href=\"" . $_url . "\">" . strtoupper(normalize($game)) . "</a> (" . count($game_servers) . ")</h1>"; | |
// // $output .= generateHtmlTable($game_servers); | |
// } | |
$output .= '<script> $(document).ready(function() { $(".table").tablesorter(); }); </script>'; | |
$output .= '<div class="container" style="min-width:95%;margin-left:auto;margin-right:auto;">'; | |
// $output .= '<h2>Server List</h2>'; | |
// $output .= '<p><strong>Note:</strong> Click on the headers to expand/collapse the server lists.</p>'; | |
$output .= '<div class="accordion" id="server-accordion">'; | |
ksort($servers); | |
$collapse = count($servers) > 1 ? "collapse" : ""; | |
foreach($servers as $game => $game_servers) { | |
// $_url = writeUrlAddParam($currentUrl, "games", "filter"); | |
$output .= '</br><div class="card">'; | |
$output .= '<div class="card-header">'; | |
$output .= '<h4 class="panel-title">'; | |
$output .= '<a data-toggle="collapse" data-parent="#server-accordion" href="#servers_' . htmlspecialchars($game) . '"><center>'; | |
$output .= strtoupper(normalize($game)); // . ' (' . count($game_servers) . ')'; | |
$output .= '</center></a>'; | |
// $output .= " (" . $_url . ")"; | |
$output .= '</h4></div>'; | |
$output .= "<div id='servers_" . htmlspecialchars($game) . "' class='$collapse'>"; | |
$output .= '<div class="card-body">'; | |
$output .= generateHtmlTable($game_servers); | |
$output .= '</div></div></div>'; | |
} | |
$output .= '</div></div>'; | |
$filename="servers.html"; | |
break; | |
default: | |
die("\"$format\" is not a valid format!"); | |
$filename="servers"; | |
} | |
if (isset($_GET["filename"])) $filename = get("filename",$filename); | |
if (isset($_GET["dl"])) header('Content-Disposition: attachment; filename="' . $filename . '"'); | |
echo $output; | |
// else header('Content-Disposition: inline; filename="' . $filename . '"'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment