TagPro Groups on Homepage

Show available groups *of all servers* on the homepage, joiner page and inside a group

// ==UserScript==
// @name         TagPro Groups on Homepage
// @version      2.4
// @description  Show available groups *of all servers* on the homepage, joiner page and inside a group
// @author       Ko
// @supportURL   https://www.reddit.com/message/compose/?to=Wilcooo
// @website      https://redd.it/no-post-yet
// @download     https://raw.githubusercontent.com/wilcooo/TagPro-GroupsOnHomepage/master/tpgoh.user.js
// @match        http://*.koalabeast.com*
// @match        http://tagpro.gg/
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @license      MIT
// @icon         https://raw.githubusercontent.com/wilcooo/TagPro-GroupsOnHomepage/master/icon.png
// @require      https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.0/socket.io.slim.js
// @require      https://greasyfork.org/scripts/371240/code/TagPro%20Userscript%20Library.js
// @connect      koalabeast.com
// @namespace https://greasyfork.org/users/152992
// ==/UserScript==



/* global tagpro, io, tpul */



var short_name = 'groups_on_homepage'; // An alphabetic (no spaces/numbers, preferably lowercase) distinctive name for the script.
var version = GM_info.script.version; // The version number is automatically fetched from the metadata.
tagpro.ready(function(){ tagpro.scripts = Object.assign( tagpro.scripts || {}, {short_name:{version:version}} ); });
console.log('START: ' + GM_info.script.name + ' (v' + version + ' by ' + GM_info.script.author + ')');


// Userscripts load in the order that they appear in Tamermonkey.
// Set this option to true if you want this script to be inserted to
// the page above than previously load scripts, instead of below.
const insertBefore = false;


var settings = tpul.settings.addSettings({
    id: short_name,
    title: "Configure Groups on Homepage",
    tooltipText: "Groups on Homepage",
    icon: "https://raw.githubusercontent.com/wilcooo/TagPro-GroupsOnHomepage/master/icon.png",

    fields: {
        groups_on_home: {
            label: 'Show all public groups (worldwide) on the homepage',
            type: 'checkbox',
            default: true,
        },
        groups_on_find: {
            label: 'Show all public groups (worldwide) while finding a game',
            type: 'checkbox',
            default: true,
        },
        groups_on_groups: {
            label: 'Show all public groups (worldwide) on the /groups page, instead of just the current server\'s groups',
            type: 'checkbox',
            default: true,
        },
        groups_in_group: {
            label: 'Show all public groups (worldwide) while in a group',
            type: 'checkbox',
            default: true,
        },
        position: {
            label: 'Position of the groups on the homepage',
            type: 'select',
            options: ['top','bottom'],
            default: 'top',
        },
        interval: {
            label: 'How often should this script update the groups, so you don\'t have to F5 all the time? Choose an amount of seconds, or set to 0 to never update',
            type: 'int',
            min: 0,
            max: 600,
            default: 60
        },
    },

    events: {
        open: function () {

            // Changing the layout a bit after the config panel is opened...

            [...this.frame.getElementsByClassName('field_label')].forEach( function(el) {
                el.classList.remove('col-xs-4');
                el.classList.add('col-xs-8');
                el.nextElementSibling.classList.remove('col-xs-8');
                el.nextElementSibling.classList.add('col-xs-4');
            } );
        },
        save: function() {

            // Making sure (most) options take effect immediately after a save...

            position = settings.get("position");
            groups_on_home = settings.get("groups_on_home");
            groups_on_find = settings.get("groups_on_find");
            groups_on_groups = settings.get("groups_on_groups");
            groups_in_group = settings.get("groups_in_group");
            interval = settings.get("interval");


            // Update the position of the groups if necessary:

            var groups_div = document.getElementById('GroPro-groups');

            update_groups();

            groups_div.classList.remove('hidden');

            if (window.location.pathname === '/' && !groups_on_home ||
                window.location.pathname.match(/^\/groups\/[a-z]{8}$/) && !groups_in_group ||
                window.location.pathname === '/groups' && !groups_on_groups ||
                window.location.pathname === '/games/find' && !groups_on_find ) {

                groups_div.classList.add('hidden');
            }

        }
    }
});


var position = settings.get("position"),
    groups_on_home = settings.get("groups_on_home"),
    groups_on_find = settings.get("groups_on_find"),
    groups_on_groups = settings.get("groups_on_groups"),
    groups_in_group = settings.get("groups_in_group"),
    interval = settings.get("interval");






const servers = ['centra','pi','chord','diameter','origin','sphere','radius','orbit'];

// We don't use this api anymore, since anonymous xmlhttpRequests are now possible.
//const groups_api = 'https://script.google.com/macros/s/AKfycbxF5kcVoFbqmLlbHB2_nJ_dCRoh2iOXDpFyzAq0Kw2UDjM7qEHf/exec';


var warned = false;

function get_groups() {return new Promise(function(resolve,reject) {

    var parser = new DOMParser(),
        groups = [],
        pending = 0;

    servers.forEach(function(server) {

        pending ++;

        GM_xmlhttpRequest({
            url: 'http://tagpro-'+server+'.koalabeast.com/groups/',
            anonymous: true,
            onload: done,
            onerror: done,
            timeout: 5000,
            context: server,
        });
    });

    function done(response){

        pending --;

        if (response.finalUrl.includes('groupAuth')) {
            //if (!warned) warned = tagpro.helpers.displayError("Sorry, your IP address has been flagged - so the GroupsOnHome script can't yet show you the groups. No worries though, I'm working on a fix for this.");
            console.error("Sorry, your IP address has been flagged - so the GroupsOnHome script can't yet show you the groups. No worries though, I'm working on a fix for this.");
            groups.flagged = true;
        }

        if (response.response) {

            var groups_doc = parser.parseFromString(response.response, "text/html");
            for (var group_item of groups_doc.getElementsByClassName('group-item')) {

                var group = { server: response.context };
                groups.push(group);

                for (var el of group_item.querySelectorAll("*") ) {
                    if (el.classList.contains('group-type')) {
                        group.type = el.innerText.trim();
                    } else if (el.classList.contains('group-name')) {
                        group.name = el.innerText.trim();
                    } else if (el.tagName == "A") {
                        if (el.href.startsWith("http")) group.link = el.href;
                        else group.link = "http://tagpro-"+group.server+".koalabeast.com"+el.href;
                    } else if (el.innerText.trim().startsWith('Leader')) {
                        group.leader = el.innerText.slice(el.innerText.indexOf(":")+1);
                    } else if (el.innerText.trim().startsWith('Players')) {
                        group.players = el.innerText.slice(el.innerText.indexOf(":")+1);
                    }
                }
            }
        }

        else console.error("Couldn't get groups on "+response.context, response);

        if (pending === 0) resolve(groups);
    }
});}



function update_groups() {


    if (!(window.location.pathname === '/groups' && groups_on_groups || // If we are on the groups selection page
    window.location.pathname.match(/^\/groups\/[a-z]{8}$/) && groups_in_group || // If we are in a group
    window.location.pathname === '/games/find' && groups_on_find || // In the process of joining a game
    window.location.pathname === '/' && groups_on_home )) // If we are on the homepage
    { return; }


    // Relocate the groups container
    // Add the container to the userscript-div and make unhide that
    if (groups_list.parentElement.id == "GroPro-groups") {
        var pos =
            document.getElementById('userscript-'+position) ||
            document.getElementById('userscript-home') ||
            document.getElementById('userscript-top') ||
            (location.host.startsWith("tagpro.") && document.getElementsByClassName('header')[0]);
        if (!pos) return tagpro.helpers.displayError('Sorry, something went wrong while trying to show you the groups of all servers. Error code: Giraffe (inform /u/Wilcooo if you want me to fix it)');
        if (insertBefore) pos.insertBefore(container, pos.firstChild);
        else pos.append(container);
        pos.classList.remove('hidden');
    }

    groups_list.className = 'row groups-list';

    var groups_list_cache = GM_getValue('groups-list-cache',{});

    groups_list.innerHTML = '<div class=spinner align=center> <div class=spinner-item></div> <div class=spinner-item></div> <div class=spinner-item></div> <div class=spinner-item></div>';

    if (Date.now() - groups_list_cache.time < 65e3) groups_list.innerHTML += groups_list_cache.html;

    // Get ping and server stats for each server
    window.addEventListener('load',function(){

        servers.forEach( function(server) {
            var host = 'http://tagpro-'+server+'.koalabeast.com:80';

            var connection = io.connect(host, {transports: ["websocket"]});

            connection.on('connect',function(){
                var ping = -Date.now();
                connection.emit('stats',{},function(stats) {
                    ping += Date.now();

                    styleSheet.insertRule('.'+server+'-stats:after { content:"(ping: '+ping+', '+stats.players+' players in '+stats.games+' games)"; color:#868686; font-style:italic; font-size:small; }');
                    styleSheet.insertRule('.'+server+'-stats .spinner { display:none !important; }');
                });
            });
        });
    });


    get_groups().then(function(groups) {

        groups_list.innerHTML = '';

        for (var group of groups) {
            var group_html =
                    `<div class="col-sm-6 col-md-4">
                        <div class="group-item">
                            <div class="row">
                                <div class="col-md-12">
                                    <div class="pull-right group-type"> `+group.type+` </div>
                                    <div class="group-name ellipsis"> `+group.name+` </div>
                                </div>
                                <div class="col-xs-6">
                                    <div class="ellipsis"> Leader: `+group.leader+` </div>
                                    <div class="ellipsis"> Players: `+group.players+` </div>
                                </div>
                                <div class="col-xs-6">
                                    <a class="btn btn-primary pull-right ellipsis" href="`+group.link+`"> Join Group </a>
                                </div>
                                <div class="col-xs-12 ellipsis `+group.server+`-stats">
                                    Server: `+group.server[0].toUpperCase() + group.server.slice(1)+`
                                    <div class=spinner style="display:inline">
                                        <div class=spinner-item></div>
                                        <div class=spinner-item></div>
                                        <div class=spinner-item></div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>`;
            groups_list.innerHTML += group_html;
        }

        if (groups.flagged) {
            groups_list.innerHTML = `
                    <div class="col-sm-6 col-md-4">
                        <div class="group-item" style="height:118px; font-size:smaller; color:lightgray">
                            Sorry, your IP address has been flagged by TagPro - so the GroupsOnHome script can't yet show you the groups. No worries though, I'm working on a fix for this.
                        </div>
                    </div>`;
        } else if (groups.length === 0) {
            groups_list.innerHTML = `
                    <div class="col-sm-6 col-md-4">
                        <div class="group-item" style="height:118px">
                            <div class="row">
                                <div class="col-md-12">
                                    <div class="group-name">No public groups available. Create one!</div>
                                </div>
                            </div>
                        </div>
                    </div>`;
        }

        if (window.location.pathname !== '/groups') {
            groups_list.innerHTML +=
                `<div class="col-sm-6 col-md-4">
                     <div class="group-item">
                         <div class="row">
                             <form method="post" action="/groups/create">
                                 <div class="col-md-12">
                                     <input name="name" class="group-name" style="background:0;border:0;width:100%;height:27px" value="Your group">
                                 </div>
                                 <div class="col-md-12"><br></div>
                                 <div class="col-xs-6">
                                     <label tabindex="0" class="btn btn-default input-label ellipsis">
                                         <input tabindex="-1" type="checkbox" name="public"> Public Group
                                     </label>
                                 </div><div class="col-xs-6">
                                     <button class="btn btn-primary ellipsis pull-right" style="">Create Group</button>
                                 </div>
                             </form>
                         </div>
                     </div>
                 </div>`;
        }

        for (var label of document.getElementsByClassName('input-label')) {
            label.onkeydown = function(keydown) {
                if (keydown.code == 'Space') {
                    keydown.preventDefault();
                    keydown.target.firstElementChild.checked ^= true;
                }
                if (keydown.code == 'Enter') {
                    keydown.preventDefault();
                    keydown.target.closest('form').submit();
                }
            };
        }

        GM_setValue('groups-list-cache', {'html': groups_list.innerHTML, 'time': Date.now()});
    });


    clearTimeout(timeout);
    if (interval) timeout = setTimeout(update_groups, 1000*interval);
}


var timeout = -1,
    groups_list = document.querySelector(".groups-list") || document.createElement('div');

// Create a container for the groups
if (!groups_list.parentElement) {
    var container = document.createElement('div');
    container.id = 'GroPro-groups';
    container.className = 'container';

    container.appendChild(groups_list);
}

update_groups();


document.head.appendChild( document.createElement('style' ));
var styleSheet = document.styleSheets[ document.styleSheets.length -1 ];

styleSheet.insertRule( '.ellipsis { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }' );