Guest User

Untitled

a guest
Mar 9th, 2017
180
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /// <reference path="typings/tsd.d.ts" />
  2. var jsonfile = require('jsonfile');
  3. var file = "players.json";
  4. var jsonObj = jsonfile.readFileSync(file);
  5.  
  6. console.log(jsonObj.users[0].discordid);
  7.  
  8. var steam = require("steam"),
  9.     util = require("util"),
  10.     fs = require("fs"),
  11.     crypto = require("crypto"),
  12.     dota2 = require("./"),
  13.     steamClient = new steam.SteamClient(),
  14.     steamUser = new steam.SteamUser(steamClient),
  15.     steamFriends = new steam.SteamFriends(steamClient),
  16.     Dota2 = new dota2.Dota2Client(steamClient, true);
  17.  
  18. var Discord = require("discord.js");
  19.  
  20. global.config = require("./config");
  21.  
  22. var discordbot = new Discord.Client();
  23.  
  24. discordbot.login(global.config.discord_email, global.config.discord_pass);
  25.  
  26. var version = "1.2.2";
  27.  
  28. var LobbySize = 10;
  29. var Group = [];
  30. var isGroupRunning = false;
  31. var isDrafting = false;
  32. var currentHoster = "";
  33. var currentCaptain = "";
  34. var TeamA = [];
  35. var TeamB = [];
  36. var FreePlayers = [];
  37. var Pick = "";
  38. var date = new Date();
  39. var lastRavage = 0;
  40. var ravagecooldown = 150;
  41. var DiscordServerName = global.config.discord_servername;
  42. var SteamAPIkey = global.config.steamapikey;
  43. //var DiscordServerName = "EU Master Stack";
  44.  
  45.  
  46. discordbot.on("message", function(message) {
  47.     if(message.content.charAt(0) == "!"){
  48.     var commands = message.content.substring(1,message.content.length).split(" ");
  49.         switch(commands[0]){
  50.             case "register":
  51.                 console.log("Command used: Register");
  52.                 if(commands.length >= 2){
  53.                 if(commands[1].length != 17 || isNaN(commands[1])){
  54.                     discordbot.reply(message, "Incorrect use, command should be !register 'steamid64'. You can get your steamid at http://steamid.io/lookup");
  55.                 } else {
  56.                     var alreadyRegisted = false;
  57.                     for(var i=0, len = jsonObj.users.length; i<len;i++) {
  58.                         if(message.author.id == jsonObj.users[i].discordid){
  59.                             alreadyRegisted = true;
  60.                             discordbot.reply(message, "you are already registered.");
  61.                         }
  62.                     }
  63.                 if(!alreadyRegisted){
  64.                     discordbot.reply(message, "Welcome!");
  65.                     var discordid = message.author.id;
  66.                     var steamid = commands[1];
  67.                     var newuser = {discordid, steamid};
  68.                     jsonObj.users.push(newuser);
  69.                     jsonfile.writeFileSync(file, jsonObj);
  70.                 }  
  71.             }
  72.             } else {
  73.                 discordbot.reply(message, "Incorrect use, command should be !register 'steamid64'. You can get your steamid at http://steamid.io/lookup");
  74.             }
  75.             break;
  76.  
  77.             case "creategame":
  78.                 console.log("Command used: creategame.");
  79.                 if(checkSignup(message.author.id) == true){
  80.                     if(!isGroupRunning){
  81.                         CreateGroup(message.author.username, message.author.id);
  82.                     } else {
  83.                         WriteToChannel("You can't make a game, there is already one running, try !join.");
  84.                     }
  85.                 } else {
  86.                     discordbot.reply(message, "you are not registered.")}
  87.             break;
  88.                
  89.             case "disband":
  90.                 if(message.author.id == currentHoster){
  91.                     WriteToChannel(message.author.username + " has disbanded the inhouse party.");
  92.                     ResetGlobals();
  93.                 }
  94.             break;
  95.                
  96.                 case "join":
  97.                     console.log("Command used: join.");
  98.                     if(isGroupRunning == false){
  99.                         WriteToChannel(message.author.username + " there is no queue game currently running. Use !creategame to create one, or use !help to get a full list of commands.");
  100.                         break;
  101.                     }
  102.                     if(checkSignup(message.author.id) == true){
  103.                         if(!checkGroup(message.author.id))
  104.                         {
  105.                             if(Group.length < LobbySize && isGroupRunning == true)
  106.                             {
  107.                                 Group.push(message.author.id);
  108.                                
  109.                                 if(Group.length == LobbySize)
  110.                                 {
  111.                                     isDrafting = true;
  112.                                     WriteToChannel("Group is now full! Someone use !captain to draft for Team B");
  113.                                     //CreateDotaLobby(randomnumber);
  114.                                 } else {
  115.                                     WriteToChannel(message.author.username + " joins the group! " + Group.length + "/" + LobbySize + " slots taken.");
  116.                                 }
  117.                             }
  118.                         }else(
  119.                             discordbot.reply(message, "you have already joined this game."));
  120.                          
  121.                     } else {
  122.                         discordbot.reply(message, "you are not registered. Use !register 'steamid64'.")}
  123.                     break;
  124.                    
  125.                 case "start":
  126.                     console.log("Command used: start");
  127.                     if(checkSignup(message.author.id) == true && message.author.id == currentHoster && isDrafting == false){
  128.                         Dota2.launchPracticeLobby();
  129.                         Dota2.leavePracticeLobby();
  130.                         ResetGlobals();
  131.                         WriteToChannel("GLHF, bot is now ready for new group!");
  132.                     }
  133.                     break;
  134.                    
  135.                     //Makes the bot send an invite to you
  136.                 case "invite":
  137.                     console.log("Command used: invite.");
  138.                     for(var x=0, len=jsonObj.users.length; x<len;x++){
  139.                         if(message.author.id == jsonObj.users[x].discordid)
  140.                         {
  141.                             Dota2.inviteToLobby(jsonObj.users[x].steamid);
  142.                         }
  143.                     }
  144.                     break;
  145.                    
  146.                     //Takes your ID out of Group[], if not throws error
  147.                 case "leave":
  148.                     console.log("Command used: leave.");
  149.                     if(isGroupRunning == true){
  150.                         console.log(Group.toString());
  151.                         if(message.author.id != currentHoster)
  152.                         {
  153.                             if(isDrafting == true){
  154.                                 WriteToChannel(message.author.username + " has tried to disband the party mid draft.");
  155.                                 break;
  156.                             }
  157.                             var index = Group.indexOf(message.author.id);
  158.                             if (index > -1) {
  159.                                 discordbot.reply(message, "has left the lobby");
  160.                                 Group.splice(index, 1);
  161.                                 WriteToChannel("Lobby is now: " + Group.length + "/" + LobbySize);
  162.                             }
  163.                         } else {
  164.                             WriteToChannel("Lobby leader has left, lobby disbanding.");
  165.                             isGroupRunning = false;
  166.                             Group = [];
  167.                         }
  168.                     } else {
  169.                         discordbot.reply(message, "you can't leave a lobby that you aren't in!");
  170.                     }
  171.    
  172.                     break;
  173.                    
  174.                     //Lists all players that are currently in the group
  175.                 case "listplayers":
  176.                     console.log("Command used: listplayers");
  177.                     if(isGroupRunning)
  178.                     {
  179.                         var playerString = "";
  180.                        
  181.                         for(var i=0, len=Group.length; i<len;i++)
  182.                         {
  183.                             if(Group[i] == currentHoster)
  184.                             {
  185.                                playerString = playerString + (discordbot.users.get("id", Group[i]).username + " :trident: ") + "\n";
  186.                             } else {
  187.                                playerString = playerString + (discordbot.users.get("id", Group[i]).username) + "\n";
  188.                             }
  189.                         }
  190.                        
  191.                         WriteToChannel(playerString);
  192.                         playerString = "";
  193.                     } else
  194.                     {
  195.                         WriteToChannel("There is no group running at the moment");
  196.                     }
  197.                     break;
  198.                
  199.                 case "pingplayers":
  200.                     console.log("Command used: pingplayers");
  201.                     if(isGroupRunning && message.author.id != currentHoster){
  202.                         WriteToChannel("Only the party leader can ping the current players.");
  203.                         break;
  204.                     }
  205.                     if(isGroupRunning && message.author.id == currentHoster)
  206.                     {
  207.                         var playerString = "";
  208.                        
  209.                         for(var i=0, len=Group.length; i<len;i++)
  210.                         {
  211.                             if(Group[i] == currentHoster)
  212.                             {
  213.                                playerString = playerString + (discordbot.users.get("id", Group[i])) + " :trident: " + "\n";
  214.                             } else {
  215.                                 playerString = playerString + (discordbot.users.get("id", Group[i])) + "\n";
  216.                             }
  217.                            
  218.                         WriteToChannel(playerString);
  219.                         playerString = "";
  220.                         }
  221.                     } else {
  222.                         WriteToChannel("There is no group running at the moment");
  223.                     }
  224.                     break;
  225.                
  226.                    
  227.                 case "help":    
  228.                     console.log("Command used: help.");
  229.                     discordbot.sendMessage(message.author,
  230.                     " !register - Register your steamid\n" +
  231.                     " !creategame  - Start a Inhouse Game \n" +
  232.                     " !join     - Summon a magical unicorn from rainbow land not to join inhouses or anything like that\n" +
  233.                     " !leave   - Leaves a lobby \n" +
  234.                     " !invite   - Request a new lobby invite from the bot \n" +
  235.                     " !listplayers   - Lists all the players who are currently in a game. :trident: means they created the lobby \n" +
  236.                     " !captain   - Slots you as the captain of Team B \n" +
  237.                     " !pick   - Picks a user for you team, only available to captains. Usage: !pick @USERNAME \n" +
  238.                     "----------------------------------------------------------------------------\n" +
  239.                     "--------------------Group leader specific commands-------------------\n" +
  240.                     "----------------------------------------------------------------------------\n" +
  241.                     " !pingplayers    - Pings all current players in the game\n" +
  242.                     " !start    - Summon Cthulhu\n" +
  243.                     " !disband  - Pull a virtus.pro" +
  244.                     " \n\ncurrent version is: " + version
  245.                     );
  246.                     break;
  247.                
  248.                 //Admin only commands
  249.                 case "forceleave":
  250.                     console.log("Command used: forceleave.");
  251.                     if(message.server == undefined && checkRole(message.author.id) == true)
  252.                     {
  253.                         discordbot.reply(message, "Admin forced the bot to leave the lobby.");
  254.                         Dota2.leavePracticeLobby();
  255.                     }
  256.                     break;
  257.                    
  258.                 case "forcedisband":
  259.                     console.log("Command used: forcedisband.");
  260.                     if(message.server == undefined && checkRole(message.author.id) == true)
  261.                     {
  262.                         console.log("Command used: forcedisband used successfully.");
  263.                         WriteToChannel("Force disbanded the inhouse party by an Admin.");
  264.                         ResetGlobals();
  265.                     }
  266.                     break;
  267.                    
  268.                 case "forcestart":
  269.                     console.log("Command used: forcestart.");
  270.                     if(message.server == undefined && checkRole(message.author.id) == true)
  271.                     {
  272.                         discordbot.reply(message, "Admin force started the game.");
  273.                         Dota2.launchPracticeLobby();
  274.                         Dota2.leavePracticeLobby();
  275.                         ResetGlobals();
  276.                         WriteToChannel("GL HF, bot is now ready for new group!");
  277.                     }
  278.                     break;
  279.                    
  280.                 case "forcelobby":
  281.                     console.log("Command used: forcelobby.");
  282.                     isGroupRunning = true;
  283.                     if(message.server == undefined && checkRole(message.author.id) == true)
  284.                     {
  285.                         var randomnumber = Math.floor((Math.random() * 100) + 1);
  286.                         WriteToChannel("Admin force created lobby: EMSLobby " + randomnumber);
  287.                         CreateDotaLobby(randomnumber);
  288.                     }
  289.                     break;
  290.                    
  291.                 case "forceshuffle":
  292.                     console.log("Command used: forceshuffle.");
  293.                     if(message.server == undefined && checkRole(message.author.id) == true){
  294.                         Dota2.balancedShuffleLobby();
  295.                         WriteToChannel("Admin forceshuffled the game.");
  296.                     }
  297.                     break;
  298.                    
  299.                 case "ravage":
  300.                     console.log("Command used: ravage.");
  301.                     if(isGroupRunning == true){
  302.                         date = new Date();
  303.                         if(date.getTime() > lastRavage + (1000*ravagecooldown)) {
  304.                             WriteToChannel("@here looking for more players for inhouse: " + Group.length + "/" + LobbySize + " slots taken");
  305.                             lastRavage = date.getTime();
  306.                         } else {
  307.                             WriteToChannel("Ravage is still on CD for: " + Math.floor((lastRavage + (1000*ravagecooldown) - date.getTime())/1000) + " secs");
  308.                         }
  309.                     } else {
  310.                         WriteToChannel("There is no group runnning");
  311.                     }
  312.                 break;
  313.                
  314.                 /*case "debug":
  315.                     if(message.server == undefined && checkRole(message.author.id) == true){
  316.                         console.log(Dota2.Lobby);
  317.                     }
  318.                     break;
  319.                 */
  320.                    
  321.                 case "pick":
  322.                     if(message.author.id == currentHoster || message.author.id == currentCaptain)
  323.                     {
  324.                         if(Pick == "A")
  325.                         {
  326.                             if(message.author.id == currentHoster && message.mentions.length >= 1)
  327.                             {
  328.                                 if(commands.length > 1 && message.mentions.length >= 1)
  329.                                 {
  330.                                     if(FreePlayers.indexOf(message.mentions[0].id) > -1)
  331.                                     {
  332.                                         TeamA.push(message.mentions[0].id);
  333.                                         var index = FreePlayers.indexOf(message.mentions[0].id);
  334.                                         if (index > -1) { FreePlayers.splice(index, 1); }
  335.                                         Pick = "B";
  336.                                         DraftStatus();
  337.                                     }
  338.                                 } else { WriteToChannel("Usage: !pick @DiscordName"); }
  339.                             } else {
  340.                                 //WriteToChannel("It is Team " + Pick + " turn to pick");
  341.                                
  342.                             }
  343.                         }
  344.                        
  345.                         if(Pick == "B")
  346.                         {
  347.                             if(message.author.id == currentCaptain && message.mentions.length >= 1)
  348.                             {
  349.                                 if(commands.length > 1 && message.mentions.length >= 1)
  350.                                 {
  351.                                     if(FreePlayers.indexOf(message.mentions[0].id) > -1)
  352.                                     {
  353.                                         TeamB.push(message.mentions[0].id);
  354.                                         var index = FreePlayers.indexOf(message.mentions[0].id);
  355.                                         if (index > -1) { FreePlayers.splice(index, 1); }
  356.                                         Pick = "A";
  357.                                         DraftStatus();
  358.                                     }
  359.                                 } else { WriteToChannel("Usage: !pick @DiscordName"); }
  360.                             } else {
  361.                                //WriteToChannel("It is Team " + Pick + " turn to pick");
  362.                             }
  363.                         }
  364.                     } else {
  365.                         discordbot.reply(message, "You are not drafting sir");
  366.                     }
  367.                 break;
  368.                
  369.                 case "captain":
  370.                     if(checkGroup(message.author.id) && isDrafting == true && currentHoster != message.author.id && currentCaptain == "")
  371.                     {
  372.                         currentCaptain = message.author.id;
  373.                         FreePlayers = Group.slice();
  374.                         var Captainindex = FreePlayers.indexOf(currentCaptain);
  375.                         if (Captainindex > -1) { FreePlayers.splice(Captainindex, 1); }
  376.                         var HosterIndex = FreePlayers.indexOf(currentHoster);
  377.                         if (HosterIndex > -1) { FreePlayers.splice(HosterIndex, 1); }
  378.                        
  379.                         TeamA.push(currentHoster);
  380.                         TeamB.push(currentCaptain);
  381.                         Pick = "A";
  382.                         DraftStatus();
  383.                     } else { WriteToChannel("You are not in the group or you are the current group leader") };
  384.                 break;
  385.                
  386.                 //Default message whenever someone incorrectly types in a command
  387.                 default:
  388.                     discordbot.reply(message, "Unknown command, use !help to find the list of commands.");
  389.                 break;
  390.                
  391.         }
  392.     }
  393. });
  394.  
  395. function ResetGlobals()
  396. {
  397.     isGroupRunning = false;
  398.     currentHoster = "";
  399.     currentCaptain = "";
  400.     TeamA = [];
  401.     TeamB = [];
  402.     Group = [];
  403.     FreePlayers = [];
  404. }
  405.  
  406. function DraftStatus()
  407. {
  408.    
  409.         var TeamAusers = [];
  410.         var TeamBusers = [];
  411.         var FreePlayersusers = [];
  412.         for(var i=0, len = TeamA.length; i<len;i++)
  413.         {
  414.             TeamAusers.push(discordbot.users.get("id", TeamA[i]).username);
  415.         }
  416.        
  417.         for(var i=0, len = TeamB.length; i<len;i++)
  418.         {
  419.             TeamBusers.push(discordbot.users.get("id", TeamB[i]).username);
  420.         }
  421.        
  422.         for(var i=0, len = FreePlayers.length; i<len;i++)
  423.         {
  424.             FreePlayersusers.push(discordbot.users.get("id", FreePlayers[i]).username);
  425.         }
  426.        
  427.     if(FreePlayers.length != 0)
  428.     {
  429.         WriteToChannel(
  430.             "Team A: " + TeamAusers.toString() +
  431.             "\nTeam B: " + TeamBusers.toString() +
  432.             "\nUnpicked: " + FreePlayersusers.toString() +
  433.             "\nTeam " + Pick + " turn to pick!");
  434.     }
  435.     if(FreePlayers.length == 0){
  436.         WriteToChannel(
  437.             "Draft is complete Teams are: " +
  438.             "\nTeam A: " + TeamAusers.toString() +
  439.             "\nTeam B: " + TeamBusers.toString());
  440.         var randomnumber = Math.floor((Math.random() * 100) + 1);
  441.         isDrafting = false;
  442.         CreateDotaLobby(randomnumber);
  443.     }
  444. }
  445.  
  446. function CreateDotaLobby(lobbyname)
  447. {
  448.     Dota2.leavePracticeLobby();
  449.    
  450.     var steamids = [];
  451.    
  452.     for(var i=0, len=Group.length; i<len;i++)
  453.     {
  454.         for(var y=0, len2=jsonObj.users.length; y<len2;y++)
  455.         {
  456.             if(jsonObj.users[y].discordid == Group[i])
  457.             {
  458.                 steamids[i] = jsonObj.users[y].steamid;
  459.             }
  460.         }
  461.     }
  462.    
  463.                 Dota2.createPracticeLobby("EMS" + lobbyname,
  464.                 {
  465.                     "game_name": "EMSLobby " + lobbyname,
  466.                     "server_region": dota2.ServerRegion.EUROPE,
  467.                     "game_mode": dota2.schema.DOTA_GameMode.DOTA_GAMEMODE_CM,
  468.                     "series_type": 0,
  469.                     "game_version": 1,
  470.                     "allow_cheats": false,
  471.                     "fill_with_bots": false,
  472.                     "allow_spectating": true,
  473.                     "pass_key": "EMS" + lobbyname,
  474.                     "radiant_series_wins": 0,
  475.                     "dire_series_wins": 0,
  476.                     "allchat": false
  477.                 },
  478.                 function(err, body){
  479.                     console.log(JSON.stringify(body));
  480.                     Dota2.practiceLobbyKickFromTeam(Dota2.AccountID);
  481.                     for(var i=0, len=steamids.length; i<len;i++)
  482.                     {
  483.                             Dota2.inviteToLobby(steamids[i]);
  484.                     }
  485.                     console.log(Dota2.Lobby);
  486.                 });
  487.  
  488.            
  489.        
  490.     /* Do some dota wizzardy
  491.     isGroupRunning = false;
  492.     Group = [];
  493.     WriteToChannel("Bot is ready for a new group!");*/
  494. }
  495. function checkSignup(id)
  496. {
  497.     var isSignedUp = false;
  498.     for(var i=0, len = jsonObj.users.length; i<len;i++)
  499.     {
  500.         if(jsonObj.users[i].discordid == id)
  501.         {
  502.             isSignedUp = true;
  503.         }
  504.     }
  505.     return isSignedUp;
  506. }
  507.  
  508. function checkGroup(id)
  509. {
  510.     var isGrouped = false;
  511.     for(var i=0, len = Group.length; i<len;i++)
  512.     {
  513.         if(Group[i] == id)
  514.         {
  515.             isGrouped = true;
  516.         }
  517.     }
  518.     return isGrouped;
  519. }
  520.  
  521. function checkRole(user)
  522. {
  523.     var isAuthed = false;
  524.    
  525.     var server = discordbot.servers.get("name", DiscordServerName);
  526.    
  527.     var authedusers = server.usersWithRole(server.roles.get("name", "Bot Wrangler"));
  528.    
  529.     console.log(user);
  530.     for(var i=0, len = authedusers.length; i<len;i++)
  531.     {
  532.         console.log(authedusers[i].id);
  533.         if(user == authedusers[i].id)
  534.         {
  535.             isAuthed = true;
  536.         }
  537.     }
  538.     return isAuthed;
  539. }
  540.  
  541. function CreateGroup(user, id)
  542. {
  543.     isGroupRunning = true;
  544.     Group = [];
  545.     WriteToChannel(user + " has created a new inhouse game! Do !join to get a slot");
  546.     currentHoster = id;
  547.     Group.push(id);
  548. }
  549.  
  550. function WriteToChannel(msg)
  551. {
  552.     discordbot.sendMessage(discordbot.channels.get("name", "inhouseorganising"), msg);
  553. }
  554.  
  555.  
  556. var onSteamLogOn = function onSteamLogOn(logonResp) {
  557.         if (logonResp.eresult == steam.EResult.OK) {
  558.             steamFriends.setPersonaState(steam.EPersonaState.Busy); // to display your steamClient's status as "Online"
  559.             steamFriends.setPersonaName("EMSBot"); // to change its nickname
  560.             util.log("Logged on.");
  561.             Dota2.launch();
  562.             Dota2.on("ready", function() {
  563.                 console.log("Node-dota2 ready.");
  564.  
  565.                 steamFriends.on('message', function(source, message, type, chatter) {
  566.                     console.log('Received message: ' + message);
  567.                     var command = message.split(" ");
  568.                     switch (command[0]) {
  569.                         case "invite":
  570.                             Dota2.inviteToLobby(command[1]);
  571.                             break;
  572.                         case "leave":
  573.                             Dota2.leavePracticeLobby();
  574.  
  575.                     }
  576.                 });
  577.                
  578.                 Dota2.on("chatMessage", function(channel, personaName, message) {
  579.                     Dota2.sendMessage(channel, "");
  580.                 });
  581.             });
  582.             Dota2.on("unready", function onUnready() {
  583.                 console.log("Node-dota2 unready.");
  584.             });
  585.        
  586.             // setTimeout(function(){ Dota2.exit(); }, 5000);
  587.         }
  588.     },
  589.     onSteamServers = function onSteamServers(servers) {
  590.         util.log("Received servers.");
  591.         fs.writeFile('servers', JSON.stringify(servers));
  592.     },
  593.     onSteamLogOff = function onSteamLogOff(eresult) {
  594.         util.log("Logged off from Steam.");
  595.     },
  596.     onSteamError = function onSteamError(error) {
  597.         util.log("Connection closed by server.");
  598.     };
  599.  
  600. steamUser.on('updateMachineAuth', function(sentry, callback) {
  601.     var hashedSentry = crypto.createHash('sha1').update(sentry.bytes).digest();
  602.     fs.writeFileSync('sentry', hashedSentry);
  603.     util.log("sentryfile saved");
  604.     callback({
  605.         sha_file: hashedSentry
  606.     });
  607. });
  608.  
  609.  
  610.  
  611. // Login, only passing authCode if it exists
  612. var logOnDetails = {
  613.     "account_name": global.config.steam_user,
  614.     "password": global.config.steam_pass,
  615. };
  616. if (global.config.steam_guard_code) logOnDetails.auth_code = global.config.steam_guard_code;
  617.  
  618. try {
  619.     var sentry = fs.readFileSync('sentry');
  620.     if (sentry.length) logOnDetails.sha_sentryfile = sentry;
  621. } catch (beef) {
  622.     util.log("Cannae load the sentry. " + beef);
  623. }
  624.  
  625. steamClient.connect();
  626. steamClient.on('connected', function() {
  627.     steamUser.logOn(logOnDetails);
  628. });
  629. steamClient.on('logOnResponse', onSteamLogOn);
  630. steamClient.on('loggedOff', onSteamLogOff);
  631. steamClient.on('error', onSteamError);
  632. steamClient.on('servers', onSteamServers);
Add Comment
Please, Sign In to add comment