123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473 |
- function htmlspecialchars(str){
-
- if(str === null){
-
- return "<i><Empty></i>";
- }
-
- var map = {
- '&': '&',
- '<': '<',
- '>': '>',
- '"': '"',
- "'": '''
- }
- return str.replace(/[&<>"']/g, function(m){return map[m];});
- }
- // initialize garbage
- var list = [];
- var pinged_list = [];
- var reqs = 0;
- var errors = 0;
- var sort = 6; // highest version first
- // check for instance redirect stuff
- var redir = [];
- var target = "/web?";
- new URL(window.location.href)
- .searchParams
- .forEach(
- function(value, key){
-
- if(key == "target"){
-
- target = "/" + encodeURIComponent(value) + "?";
- return;
- }
-
- if(key == "npt"){ return; }
- redir.push(encodeURIComponent(key) + "=" + encodeURIComponent(value))
- }
- );
- if(redir.length !== 0){
-
- redir = target + redir.join("&");
- }else{
-
- redir = "";
- }
- var quote = document.createElement("div");
- quote.className = "quote";
- quote.innerHTML = 'Pinged <b>0</b> servers (<b>0</b> failed requests)';
- var [div_servercount, div_failedreqs] =
- quote.getElementsByTagName("b");
- var noscript = document.getElementsByTagName("noscript")[0];
- document.body.insertBefore(quote, noscript.nextSibling);
- // create table
- var table = document.createElement("table");
- table.innerHTML =
- '<thead>' +
- '<tr>' +
- '<th class="extend">Server</th>' +
- '<th>Address</th>' +
- '<th>Bot protection</th>' +
- '<th title="Amount of legit requests processed since the last APCU cache clear (usually happens at midnight)">Real reqs (?)</th>' +
- '<th title="Amount of filtered requests processed since the last APCU cache clear (usually happens at midnight)">Bot reqs (?)</th>' +
- '<th>API</th>' +
- '<th><div class="arrow up"></div>Version</th>' +
- '</tr>' +
- '</thead>' +
- '<tbody></tbody>';
- document.body.insertBefore(table, quote.nextSibling);
- // handle sorting clicks
- var tbody = table.getElementsByTagName("tbody")[0];
- var th = table.getElementsByTagName("th");
- for(var i=0; i<th.length; i++){
-
- th[i].addEventListener("click", function(event){
-
- if(event.target.className.includes("arrow")){
-
- var div = event.target.parentElement;
- }else{
-
- var div = event.target;
- }
-
- var arrow = div.getElementsByClassName("arrow");
- var orientation = 0; // up
-
- if(arrow.length === 0){
-
- // delete arrow and add new one
- arrow = document.getElementsByClassName("arrow");
- arrow[0].remove();
-
- arrow = document.createElement("div");
- arrow.className = "arrow up";
- div.insertBefore(arrow, event.target.firstChild);
- }else{
-
- // switch arrow position
- if(arrow[0].className == "arrow down"){
-
- arrow[0].className = "arrow up";
- }else{
-
- arrow[0].className = "arrow down";
- orientation = 1;
- }
- }
-
- switch(div.textContent.toLowerCase()){
-
- case "server": sort = 0 + orientation; break;
- case "address": sort = 2 + orientation; break;
- case "bot protection": sort = 4 + orientation; break;
- case "real reqs (?)": sort = 6 + orientation; break;
- case "bot reqs (?)": sort = 8 + orientation; break;
- case "api": sort = 10 + orientation; break;
- case "version": sort = 12 + orientation; break;
- }
-
- render_list();
- });
- }
- function validate_url(url, allow_http = false){
-
- try{
-
- url = new URL(url);
- if(
- url.protocol == "https:" ||
- (
- (
- allow_http === true ||
- window.location.protocol == "http:"
- ) &&
- url.protocol == "http:"
- )
- ){
-
- return true;
- }
- }catch(error){} // do nothing
-
- return false;
- }
- function number_format(int){
-
- return new Intl.NumberFormat().format(int);
- }
- // parse initial server list
- fetch_server(window.location.origin);
- async function fetch_server(server){
-
- if(!validate_url(server)){
- console.warn("Invalid server URL: " + server);
- return;
- }
-
- // make sure baseURL is origin
- server = new URL(server).origin;
- // prevent multiple fetches
- for(var i=0; i<list.length; i++){
-
- if(list[i] == server){
-
- // serber was already fetched
- return;
- }
- }
-
- // prevent future fetches
- list.push(server);
-
- var data = null;
-
- try{
-
- var payload = await fetch(server + "/ami4get");
-
- if(payload.status !== 200){
-
- // endpoint is not available
- errors++;
- div_failedreqs.textContent = number_format(errors);
- console.warn(server + ": Invalid HTTP code " + payload.status);
- return;
- }
-
- data = await payload.json();
-
- }catch(error){
-
- errors++;
- div_failedreqs.textContent = number_format(errors);
- console.warn(server + ": Could not fetch or decode JSON");
- return;
- }
-
- // sanitize data
- if(
- typeof data.status != "string" ||
- data.status != "ok" ||
- typeof data.server != "object" ||
- !(
- typeof data.server.name == "string" ||
- (
- typeof data.server.name == "object" &&
- data.server.name === null
- )
- ) ||
- typeof data.service != "string" ||
- data.service != "4get" ||
- (
- typeof data.server.description != "string" &&
- data.server.description !== null
- ) ||
- typeof data.server.bot_protection != "number" ||
- typeof data.server.real_requests != "number" ||
- typeof data.server.bot_requests != "number" ||
- typeof data.server.api_enabled != "boolean" ||
- typeof data.server.alt_addresses != "object" ||
- typeof data.server.version != "number" ||
- typeof data.instances != "object"
- ){
-
- errors++;
- div_failedreqs.textContent = number_format(errors);
- console.warn(server + ": Malformed JSON");
- return;
- }
-
- data.server.ip = server;
-
- reqs++;
- div_servercount.textContent = number_format(reqs);
-
- var total = pinged_list.push(data) - 1;
- pinged_list[total].index = total;
-
- render_list();
-
- // get more serbers
- for(var i=0; i<data.instances.length; i++){
-
- fetch_server(data.instances[i]);
- }
- }
- function sorta(object, element, order){
-
- return object.slice().sort(
- function(a, b){
-
- if(order){
-
- return a.server[element] - b.server[element];
- }
-
- return b.server[element] - a.server[element];
- }
- );
- }
- function textsort(object, element, order){
-
- var sort = object.slice().sort(
- function(a, b){
-
- return a.server[element].localeCompare(b.server[element]);
- }
- );
-
- if(!order){
- return sort.reverse();
- }
-
- return sort;
- }
- function render_list(){
-
- var sorted_list = [];
-
- // sort
- var filter = Boolean(sort % 2);
-
- switch(sort){
-
- case 0:
- case 1:
- sorted_list = textsort(pinged_list, "name", filter === true ? false : true);
- break;
-
- case 2:
- case 3:
- sorted_list = textsort(pinged_list, "ip", filter === true ? false : true);
- break;
-
- case 4:
- case 5:
- sorted_list = sorta(pinged_list, "bot_protection", filter === true ? false : true);
- break;
-
- case 6:
- case 7:
- sorted_list = sorta(pinged_list, "real_requests", filter);
- break;
-
- case 8:
- case 9:
- sorted_list = sorta(pinged_list, "bot_requests", filter);
- break;
-
- case 10:
- case 11:
- sorted_list = sorta(pinged_list, "api_enabled", filter);
- break;
-
- case 12:
- case 13:
- sorted_list = sorta(pinged_list, "version", filter);
- break;
- }
-
- // render tabloid
- var html = "";
-
- for(var k=0; k<sorted_list.length; k++){
-
- html += '<tr onclick="show_server(' + sorted_list[k].index + ');">';
-
- for(var i=0; i<7; i++){
-
- html += '<td';
-
- switch(i){
-
- // server name
- case 0: html += ' class="extend">' + htmlspecialchars(sorted_list[k].server.name); break;
- case 1: html += '>' + htmlspecialchars(new URL(sorted_list[k].server.ip).host); break;
- case 2: // bot protection
- switch(sorted_list[k].server.bot_protection){
-
- case 0:
- html += '><span style="color:var(--green);">Disabled</span>';
- break;
-
- case 1:
- html += '><span style="color:var(--yellow);">Image captcha</span>';
- break;
-
- case 2:
- html += '><span style="color:var(--red);">Invite only</span>';
- break;
-
- default:
- html += '>Unknown';
- }
- break;
-
- case 3: // real reqs
- html += '>' + number_format(sorted_list[k].server.real_requests);
- break;
-
- case 4: // bot reqs
- html += '>' + number_format(sorted_list[k].server.bot_requests);
- break;
-
- case 5: // api enabled
-
- if(sorted_list[k].server.api_enabled){
-
- html += '><span style="color:var(--green);">Yes</span>';
- }else{
-
- html += '><span style="color:var(--red);">No</span>';
- }
- break;
-
- // version
- case 6: html += ">v" + sorted_list[k].server.version; break;
- }
-
- html += '</td>';
- }
-
- html += '</tr>';
- }
-
- console.log(html);
-
- tbody.innerHTML = html;
- }
- var popup_bg = document.getElementById("popup-bg");
- var popup_wrapper = document.getElementsByClassName("popup-wrapper")[0];
- var popup = popup_wrapper.getElementsByClassName("popup")[0];
- var popup_shown = false;
- popup_bg.addEventListener("click", function(){
-
- popup_wrapper.style.display = "none";
- popup_bg.style.display = "none";
- });
- function show_server(serverid){
-
- var html =
- '<h2>' + htmlspecialchars(pinged_list[serverid].server.name) + '</h2>' +
- 'Description' +
- '<div class="code">' + htmlspecialchars(pinged_list[serverid].server.description) + '</div>';
-
- var url_obj = new URL(pinged_list[serverid].server.ip);
- var url = htmlspecialchars(url_obj.origin);
- var domain = url_obj.hostname;
-
- html +=
- 'URL: <a rel="noreferer" target="_BLANK" href="' + url + redir + '">' + url + '</a> <a rel="noreferer" target="_BLANK" href="https://browserleaks.com/ip/' + encodeURIComponent(domain) + '">(IP lookup)</a>' +
- '<br><br>Alt addresses:';
-
- var len = pinged_list[serverid].server.alt_addresses.length;
-
- if(len === 0){
-
- html += ' <i><Empty></i>';
- }else{
-
- html += '<ul>';
-
- for(var i=0; i<len; i++){
-
- var url_obj = new URL(pinged_list[serverid].server.alt_addresses[i]);
- var url = htmlspecialchars(url_obj.origin);
- var domain = url_obj.hostname;
-
- if(validate_url(pinged_list[serverid].server.alt_addresses[i], true)){
-
- html += '<li><a rel="noreferer" href="' + url + redir + '" target="_BLANK">' + url + '</a> <a rel="noreferer" target="_BLANK" href="https://browserleaks.com/ip/' + encodeURIComponent(domain) + '">(IP lookup)</a></li>';
- }else{
-
- console.warn(pinged_list[serverid].server.ip + ": Invalid peer URL => " + pinged_list[serverid].server.alt_addresses[i]);
- }
- }
-
- html += '</ul>';
- }
- popup.innerHTML = html;
-
- popup_wrapper.style.display = "block";
- popup_bg.style.display = "block";
- }
- function hide_server(){
-
- popup_wrapper.style.display = "none";
- popup_bg.style.display = "none";
- }
|