Example #1
0
    def check_rating_requirements(self, names, channel, game_type):
        """Checks if someone meets the rating requirements to play on the server."""
        config = minqlbot.get_config()
        min_rating = 0
        max_rating = 0
        if "Balance" in config:
            if "MinimumRating" in config["Balance"]:
                min_rating = int(config["Balance"]["MinimumRating"])
            if "MaximumRating" in config["Balance"]:
                max_rating = int(config["Balance"]["MaximumRating"])
        else:
            return True

        if not min_rating and not max_rating:
            return True

        not_cached = self.not_cached(game_type, names)
        if not_cached:
            with self.rlock:
                for lookup in self.lookups:
                    for n in self.lookups[lookup][1]:
                        if n in not_cached:
                            not_cached.remove(n)
                if not_cached:
                    self.fetch_player_ratings(not_cached, channel, game_type)
                if (self.check_rating_requirements,
                    (names, channel, game_type)) not in self.pending:
                    self.pending.append((self.check_rating_requirements,
                                         (names, channel, game_type)))
                return False

        for name in names:
            if "real_elo" in self.cache[name][game_type]:
                rating = self.cache[name][game_type]["real_elo"]
            else:
                rating = self.cache[name][game_type]["elo"]

            if (rating > max_rating
                    and max_rating != 0) or (rating < min_rating
                                             and min_rating != 0):
                allow_spec = config["Balance"].getboolean("AllowSpectators",
                                                          fallback=True)
                if allow_spec:
                    p = self.player(name)
                    if p.team != "spectator":
                        self.put(name, "spectator")
                        if rating > max_rating and max_rating != 0:
                            self.tell(
                                "^7Sorry, but you can have at most ^6{}^7 rating to play here and you have ^6{}^7."
                                .format(max_rating, rating), name)
                        elif rating < min_rating and min_rating != 0:
                            self.tell(
                                "^7Sorry, but you need at least ^6{}^7 rating to play here and you have ^6{}^7."
                                .format(min_rating, rating), name)
                else:
                    self.kickban(name)
                    self.debug(
                        name +
                        " was kicked for not being within the rating requirements."
                    )
Example #2
0
    def handle_player_connect(self, player):
        status = self.leave_status(player.name)
        # Check if a player has been banned for leaving, if we're doing that.
        if status and status[0] == "ban":
            self.flag_player(player)
            player.mute()
            self.delay(25, lambda: player.tell("^7You have been banned from this server for leaving too many games."))
            self.delay(40, player.kickban)
            # Stop plugins on lowest priority from triggering this event since we're kicking.
            return minqlbot.RET_STOP
        # Check if player needs to be warned.
        elif status and status[0] == "warn":
            self.delay(12, self.warn_player, args=(player, status[1]))
        # Check if a player has been banned manually.
        elif self.is_banned(player.name):
            self.flag_player(player)
            self.delay(5, player.kickban)
            # Stop plugins on lower priority from triggering this event since we're kicking.
            return minqlbot.RET_STOP

        config = minqlbot.get_config()
        if "Ban" in config and "MinimumDaysRegistered" in config["Ban"]:
            days = int(config["Ban"]["MinimumDaysRegistered"])
            if days > 0:
                threading.Thread(target=self.get_profile_thread, args=(player, days)).start()
Example #3
0
    def handle_vote_called(self, caller, vote, args):
        config = minqlbot.get_config()
        if "Essentials" in config and "AutoPassMajorityVote" in config[
                "Essentials"]:
            auto_pass = config["Essentials"].getboolean("AutoPassMajorityVote")
            if auto_pass:
                self.vote_resolve_timer = self.delay(27.5, self.resolve_vote)

        # Enforce teamsizes.
        if vote == "teamsize":
            args = int(args)
            if "Essentials" in config and "MaximumTeamsize" in config[
                    "Essentials"]:
                max_teamsize = int(config["Essentials"]["MaximumTeamsize"])
                if args > max_teamsize:
                    self.vote_no()

            if "Essentials" in config and "MinimumTeamsize" in config[
                    "Essentials"]:
                min_teamsize = int(config["Essentials"]["MinimumTeamsize"])
                if args < min_teamsize:
                    self.vote_no()
        elif vote == "kick":
            if args == minqlbot.NAME.lower():
                self.vote_no()
Example #4
0
    def cache_players(self, ratings, lookup):
        """Save the ratings of a player to the cache.

        """
        config = minqlbot.get_config()

        if ratings == None:
            self.lookup_failed(lookup)
            return
        else:
            floor = 0
            ceiling = 0
            if "Balance" in config:
                if "FloorRating" in config["Balance"]:
                    floor = int(config["Balance"]["FloorRating"])
                if "CeilingRating" in config["Balance"]:
                    ceiling = int(config["Balance"]["CeilingRating"])

            with self.rlock:
                self.fails = 0  # Reset fail counter.
            for player in ratings["players"]:
                name = player["nick"]
                del player["nick"]

                for game_type in player:
                    if game_type == "alias_of":  # Not a game type.
                        continue
                    # Enforce floor and ceiling values if we have them.
                    if floor and player[game_type]["elo"] < floor:
                        player[game_type]["real_elo"] = player[game_type][
                            "elo"]
                        player[game_type]["elo"] = floor
                    elif ceiling and player[game_type]["elo"] > ceiling:
                        player[game_type]["real_elo"] = player[game_type][
                            "elo"]
                        player[game_type]["elo"] = ceiling

                with self.rlock:
                    # If it's an alias, go ahead and cache the real one as well.
                    if "alias_of" in player:
                        real_name = player["alias_of"]
                        self.cache[real_name] = player.copy()
                        # Make sure real name isn't treated as alias.
                        del self.cache[real_name]["alias_of"]

                    if name not in self.cache:  # Already in our cache?
                        self.cache[name] = player
                    else:
                        if "alias_of" in player:
                            self.cache[name]["alias_of"] = player["alias_of"]
                        # Gotta be careful not to overwrite game types we've manually set ratings for.
                        for game_type in player:
                            if game_type not in self.cache[
                                    name] and game_type != "alias_of":
                                self.cache[name][game_type] = player[game_type]

            # The lookup's been dealt with, so we get rid of it.
            if lookup:
                with self.rlock:
                    del self.lookups[lookup.uid]
Example #5
0
    def handle_player_connect(self, player):
        status = self.leave_status(player.name)
        # Check if a player has been banned for leaving, if we're doing that.
        if status and status[0] == "ban":
            self.flag_player(player)
            player.mute()
            self.delay(25, lambda: player.tell("^7You have been banned from this server for leaving too many games."))
            self.delay(40, player.kickban)
            # Stop plugins on lowest priority from triggering this event since we're kicking.
            return minqlbot.RET_STOP
        # Check if player needs to be warned.
        elif status and status[0] == "warn":
            self.delay(12, self.warn_player, args=(player, status[1]))
        # Check if a player has been banned manually.
        elif self.is_banned(player.name):
            self.flag_player(player)
            self.delay(5, player.kickban)
            # Stop plugins on lower priority from triggering this event since we're kicking.
            return minqlbot.RET_STOP

        config = minqlbot.get_config()
        if "Ban" in config and "MinimumDaysRegistered" in config["Ban"]:
            days = int(config["Ban"]["MinimumDaysRegistered"])
            if days > 0:
                threading.Thread(target=self.get_profile_thread, args=(player, days)).start()
Example #6
0
    def fetch_player_ratings(self,
                             names,
                             channel,
                             game_type,
                             use_local=True,
                             use_aliases=True):
        """Fetch ratings from the database and fall back to QLRanks.

        Takes into account ongoing lookups to avoid sending multiple requests for a player.

        """
        config = minqlbot.get_config()
        # Fetch players from the database first if the config is set to do so.
        if use_local and "Balance" in config and config["Balance"].getboolean(
                "UseLocalRatings", fallback=False):
            ratings = {"players": []}  # We follow QLRanks' JSON format.
            for name in names.copy():
                c = self.db_query(
                    "SELECT game_type, rating FROM ratings WHERE name=?", name)
                res = c.fetchall()
                if res:
                    d = {"nick": name}
                    for row in res:
                        d[row["game_type"]] = {
                            "elo": row["rating"],
                            "rank": -1
                        }  # QLRanks' format.
                        if game_type == row["game_type"]:
                            names.remove(name)  # Got the one we need locally.
                    ratings["players"].append(d)
            if ratings["players"]:
                self.cache_players(ratings, None)

        # If we've covered everyone, we execute whatever pending tasks we have.
        if not names:
            self.execute_pending()
            return

        # Remove players we're already waiting a response for.
        with self.rlock:
            for lookup in self.lookups:
                for n in self.lookups[lookup][1]:
                    if n in names:
                        names.remove(n)

        # We fall back to QLRanks for players we don't have, but stop if we want a gametype it doesn't provide.
        if names and game_type in QLRANKS_GAMETYPES:
            if use_aliases and "Balance" in config:
                conf_alias = config["Balance"].getboolean("UseAliases",
                                                          fallback=True)
            else:
                conf_alias = False
            lookup = qlranks.QlRanks(self, names, check_alias=conf_alias)
            with self.rlock:
                self.lookups[lookup.uid] = (lookup, names, channel)
            lookup.start()
            return True
        else:
            return False
    def cache_players(self, ratings, lookup):
        """Save the ratings of a player to the cache.

        """
        config = minqlbot.get_config()

        if ratings == None:
            self.lookup_failed(lookup)
            return
        else:
            floor = 0
            ceiling = 0
            if "Balance" in config:
                if "FloorRating" in config["Balance"]:
                    floor = int(config["Balance"]["FloorRating"])
                if "CeilingRating" in config["Balance"]:
                    ceiling = int(config["Balance"]["CeilingRating"])

            with self.lock:
                self.fails = 0 # Reset fail counter.
            for player in ratings["players"]:
                name = player["nick"]
                del player["nick"]

                for game_type in player:
                    if game_type == "alias_of": # Not a game type.
                        continue
                    # Enforce floor and ceiling values if we have them.
                    if floor and player[game_type]["elo"] < floor:
                        player[game_type]["real_elo"] = player[game_type]["elo"]
                        player[game_type]["elo"] = floor
                    elif ceiling and player[game_type]["elo"] > ceiling:
                        player[game_type]["real_elo"] = player[game_type]["elo"]
                        player[game_type]["elo"] = ceiling

                with self.lock:
                    # If it's an alias, go ahead and cache the real one as well.
                    if "alias_of" in player:
                        real_name = player["alias_of"]
                        self.cache[real_name] = player.copy()
                        # Make sure real name isn't treated as alias.
                        del self.cache[real_name]["alias_of"]

                    if name not in self.cache: # Already in our cache?
                        self.cache[name] = player
                    else:
                        if "alias_of" in player:
                            self.cache[name]["alias_of"] = player["alias_of"]
                        # Gotta be careful not to overwrite game types we've manually set ratings for.
                        for game_type in player:
                            if game_type not in self.cache[name] and game_type != "alias_of":
                                self.cache[name][game_type] = player[game_type]
        
            # The lookup's been dealt with, so we get rid of it.
            if lookup:
                with self.lock:
                    del self.lookups[lookup.uid]
Example #8
0
 def run(self):
     while True:
         timeout = float(minqlbot.get_config()["MaxPing"]["SampleInterval"])
         if minqlbot.connection_status() == 8:
             self.plugin.expecting = True
             minqlbot.Plugin.scores()
         
         if self.__stop.wait(timeout=timeout):
             break
 def handle_vote_called(self, caller, vote, args):
     config = minqlbot.get_config()
     if vote == "shuffle" and "Balance" in config:
         auto_reject = config["Balance"].getboolean("VetoUnevenShuffleVote", fallback=False)
         if auto_reject:
             teams = self.teams()
             if len(teams["red"] + teams["blue"]) % 2 == 1:
                 self.vote_no()
                 self.msg("^7Only call shuffle votes when the total number of players is an even number.")
Example #10
0
    def is_leaver_banning(self):
        config = minqlbot.get_config()

        if ("Ban" in config and "AutomaticLeaveBan" in config["Ban"]
                and config["Ban"].getboolean("AutomaticLeaveBan")
                and "MinimumGamesPlayedBeforeBan" in config["Ban"]
                and "WarnThreshold" in config["Ban"]
                and "BanThreshold" in config["Ban"]):
            return True
        else:
            return False
Example #11
0
 def handle_vote_called(self, caller, vote, args):
     config = minqlbot.get_config()
     if vote == "shuffle" and "Balance" in config:
         auto_reject = config["Balance"].getboolean("VetoUnevenShuffleVote",
                                                    fallback=False)
         if auto_reject:
             teams = self.teams()
             if len(teams["red"] + teams["blue"]) % 2 == 1:
                 self.vote_no()
                 self.msg(
                     "^7Only call shuffle votes when the total number of players is an even number."
                 )
Example #12
0
    def is_leaver_banning(self):
        config = minqlbot.get_config()

        if ("Ban" in config and
            "AutomaticLeaveBan" in config["Ban"] and
            config["Ban"].getboolean("AutomaticLeaveBan") and
            "MinimumGamesPlayedBeforeBan" in config["Ban"] and
            "WarnThreshold" in config["Ban"] and
            "BanThreshold" in config["Ban"]):
            return True
        else:
            return False
Example #13
0
    def change_map(self):
        config = minqlbot.get_config()
        new_map = ""
        if "EmptyActions" in config and "MapOnEmpty" in config["EmptyActions"]:
            try:
                new_map = random.choice([
                    s.strip()
                    for s in config["EmptyActions"]["MapOnEmpty"].split(",")
                ])
            except IndexError:
                return

        if new_map != "":
            self.change_map(new_map)
    def fetch_player_ratings(self, names, channel, game_type, use_local=True, use_aliases=True):
        """Fetch ratings from the database and fall back to QLRanks.

        Takes into account ongoing lookups to avoid sending multiple requests for a player.

        """
        config = minqlbot.get_config()
        # Fetch players from the database first if the config is set to do so.
        if use_local and "Balance" in config and config["Balance"].getboolean("UseLocalRatings", fallback=False):
            ratings = {"players": []}  # We follow QLRanks' JSON format.
            for name in names.copy():
                c = self.db_query("SELECT game_type, rating FROM ratings WHERE name=?", name)
                res = c.fetchall()
                if res:
                    d = {"nick": name}
                    for row in res:
                        d[row["game_type"]] = {"elo": row["rating"], "rank": -1} # QLRanks' format.
                        if game_type == row["game_type"]:
                            names.remove(name)  # Got the one we need locally.
                    ratings["players"].append(d)
            if ratings["players"]:
                self.cache_players(ratings, None)

        # If we've covered everyone, we execute whatever pending tasks we have.
        if not names:
            self.execute_pending()
            return

        # Remove players we're already waiting a response for.
        with self.lock:
            for lookup in self.lookups:
                for n in self.lookups[lookup][1]:
                    if n in names:
                        names.remove(n)

        # We fall back to QLRanks for players we don't have, but stop if we want a gametype it doesn't provide.
        if names and game_type in QLRANKS_GAMETYPES:
            if use_aliases and "Balance" in config:
                conf_alias = config["Balance"].getboolean("UseAliases", fallback=True)
            else:
                conf_alias = False
            lookup = qlranks.QlRanks(self, names, check_alias=conf_alias)
            with self.lock:
                self.lookups[lookup.uid] = (lookup, names, channel)
            lookup.start()
            return True
        else:
            return False
    def handle_vote_ended(self, vote, args, vote_count, passed):
        config = minqlbot.get_config()
        if "Balance" not in config:
            return

        if passed == True and vote == "shuffle":
            auto = config["Balance"].getboolean("AutoBalance", fallback=False)
            if not auto:
                return
            else:
                teams = self.teams()
                total = len(teams["red"]) + len(teams["blue"])
                if total % 2 == 0:
                    self.delay(5, self.average_balance, args=(minqlbot.CHAT_CHANNEL, self.game().short_type))
                else:
                    self.msg("^7I can't balance when the total number of players is not an even number.")
Example #16
0
    def handle_vote_ended(self, vote, args, vote_count, passed):
        config = minqlbot.get_config()
        if "Balance" not in config:
            return

        if passed == True and vote == "shuffle":
            auto = config["Balance"].getboolean("AutoBalance", fallback=False)
            if not auto:
                return
            else:
                teams = self.teams()
                total = len(teams["red"]) + len(teams["blue"])
                if total % 2 == 0:
                    self.delay(5, self.average_balance, args=(minqlbot.CHAT_CHANNEL, self.game().short_type))
                else:
                    self.msg("^7I can't balance when the total number of players is not an even number.")
Example #17
0
    def __init__(self):
        super().__init__()
        config = minqlbot.get_config()
        if ( "MaxPing" not in config or
             "Samples" not in config["MaxPing"] or
             "SampleInterval" not in config["MaxPing"] or
             "MaximumPing" not in config["MaxPing"] ):
            raise AttributeError('maxping needs a "MaxPing" section with the fields "Samples", "SampleInterval", and "MaximumPing" in the config.')

        self.add_hook("scores", self.handle_scores)
        self.add_hook("unload", self.handle_unload)

        self.pings = {}
        self.expecting = False # Set to True when we're expecting to receive scores.
        self.requester = ScoresRequester(self)
        self.requester.start()
Example #18
0
    def handle_player_disconnect(self, player, reason):
        teams = self.teams()
        count = len(teams["red"]) + len(teams["blue"]) + len(
            teams["spectator"])

        if (count < 2):
            config = minqlbot.get_config()
            new_ts = 0
            if "EmptyActions" in config and "TSOnEmpty" in config[
                    "EmptyActions"]:
                new_ts = int(config["EmptyActions"]["TSOnEmpty"])
            if (new_ts > 0 and new_ts <= 8):
                self.teamsize(new_ts)
                self.delay(5, self.change_map)
            else:
                self.change_map()
Example #19
0
    def db_connect(self):
        """Returns a connection for the current thread.

        """
        thread = threading.current_thread()
        if not self.db_is_connected(thread):
            db = minqlbot.get_config()["Core"]["DatabasePath"]
            conn = sqlite3.connect(db)
            conn.row_factory = sqlite3.Row
            cursor = conn.cursor()
            cursor.execute("PRAGMA foreign_keys = ON") # Enforce foreign keys.
            with self.db_lock:
                self.db_connections[thread] = conn
            return conn
        else:
            with self.db_lock:
                return self.db_connections[thread]
Example #20
0
    def db_connect(self):
        """Returns a connection for the current thread.

        """
        thread = threading.current_thread()
        if not self.db_is_connected(thread):
            db = minqlbot.get_config()["Core"]["DatabasePath"]
            conn = sqlite3.connect(db)
            conn.row_factory = sqlite3.Row
            cursor = conn.cursor()
            cursor.execute("PRAGMA foreign_keys = ON")  # Enforce foreign keys.
            with self.db_lock:
                self.db_connections[thread] = conn
            return conn
        else:
            with self.db_lock:
                return self.db_connections[thread]
    def handle_vote_called(self, caller, vote, args):
        config = minqlbot.get_config()
        if "Essentials" in config and "AutoPassMajorityVote" in config["Essentials"]:
            auto_pass = config["Essentials"].getboolean("AutoPassMajorityVote")
            if auto_pass:
                self.vote_resolve_timer = self.delay(27.5, self.resolve_vote)

        # Enforce teamsizes.
        if vote == "teamsize":
            args = int(args)
            if "Essentials" in config and "MaximumTeamsize" in config["Essentials"]:
                max_teamsize = int(config["Essentials"]["MaximumTeamsize"])
                if args > max_teamsize:
                    self.vote_no()

            if "Essentials" in config and "MinimumTeamsize" in config["Essentials"]:
                min_teamsize = int(config["Essentials"]["MinimumTeamsize"])
                if args < min_teamsize:
                    self.vote_no()
Example #22
0
    def handle_scores(self, scores):
        if not self.expecting:
            return

        self.expecting = False
        config = minqlbot.get_config()
        
        for score in scores:
            name = score.player.clean_name.lower()
            if name not in self.pings:
                self.pings[name] = [score.ping]
            else:
                self.pings[name].append(score.ping)

                # Delete samples until we have "Samples" number of samples.
                # We use a while loop since the config could update and become more than just 1 lower than previously.
                samples = int(config["MaxPing"]["Samples"])
                while len(self.pings[name]) > samples:
                    del self.pings[name][0]

        self.check_pings(config)
Example #23
0
    def __init__(self):
        super().__init__()
        self.add_hook("unload", self.handle_unload)
        self.add_hook("chat", self.handle_game_chat)
        self.add_hook("player_connect", self.handle_player_connect)
        self.add_hook("player_disconnect", self.handle_player_disconnect)

        # Static instance so we don't waste resources making a new one every time.
        self.irc_bot_channel = IrcAdminChannel(self)
        
        self.config = minqlbot.get_config()
        self.server = self.config["IRC"].get("Server", fallback="irc.quakenet.org")
        self.channel = self.config["IRC"]["Channel"]
        self.admin_channel = self.config["IRC"]["AdminChannel"]
        self.admin_channel_pass = self.config["IRC"]["AdminChannelPassword"]
        self.color_translation = self.config["IRC"].getboolean("TranslateColors", fallback=False)
        self.irc_name = "QL" + minqlbot.NAME
        self.irc = SimpleIrc(self.irc_name, "irc.quakenet.org", 6667, self.channel,
                             self.admin_channel, self.admin_channel_pass, self)
        self.irc_thread = Thread(target=self.irc.run)
        self.irc_thread.start()
Example #24
0
    def leave_status(self, name):
        """Get a player's status when it comes to leaving, given automatic leaver ban is on.

        """
        if not self.is_leaver_banning():
            return None

        c = self.db_query("SELECT * FROM Players WHERE name=?",
                          self.clean_name(name).lower())
        row = c.fetchone()
        if not row:
            return None

        config = minqlbot.get_config()

        min_games_completed = int(config["Ban"]["MinimumGamesPlayedBeforeBan"])
        warn_threshold = float(config["Ban"]["WarnThreshold"])
        ban_threshold = float(config["Ban"]["BanThreshold"])

        # Check their games completed to total games ratio.
        total = row["games_completed"] + row["games_left"]
        if not total:
            return None
        elif total < min_games_completed:
            # If they have played less than the minimum, check if they can possibly recover by the time
            # they have played the minimum amount of games.
            ratio = (row["games_completed"] +
                     (min_games_completed - total)) / min_games_completed
        else:
            ratio = row["games_completed"] / total

        if ratio <= warn_threshold and (ratio > ban_threshold
                                        or total < min_games_completed):
            action = "warn"
        elif ratio <= ban_threshold and total >= min_games_completed:
            action = "ban"
        else:
            action = None

        return (action, ratio)
Example #25
0
    def __init__(self):
        super().__init__()
        self.add_hook("unload", self.handle_unload)
        self.add_hook("chat", self.handle_game_chat)
        self.add_hook("player_connect", self.handle_player_connect)
        self.add_hook("player_disconnect", self.handle_player_disconnect)

        # Static instance so we don't waste resources making a new one every time.
        self.irc_bot_channel = IrcAdminChannel(self)

        self.config = minqlbot.get_config()
        self.server = self.config["IRC"].get("Server",
                                             fallback="irc.quakenet.org")
        self.channel = self.config["IRC"]["Channel"]
        self.admin_channel = self.config["IRC"]["AdminChannel"]
        self.admin_channel_pass = self.config["IRC"]["AdminChannelPassword"]
        self.color_translation = self.config["IRC"].getboolean(
            "TranslateColors", fallback=False)
        self.irc_name = "QL" + minqlbot.NAME
        self.irc = SimpleIrc(self.irc_name, self.server, 6667, self.channel,
                             self.admin_channel, self.admin_channel_pass, self)
        self.irc_thread = Thread(target=self.irc.run)
        self.irc_thread.start()
Example #26
0
    def leave_status(self, name):
        """Get a player's status when it comes to leaving, given automatic leaver ban is on.

        """
        if not self.is_leaver_banning():
            return None

        c = self.db_query("SELECT * FROM Players WHERE name=?", self.clean_name(name).lower())
        row = c.fetchone()
        if not row:
            return None

        config = minqlbot.get_config()
        
        min_games_completed = int(config["Ban"]["MinimumGamesPlayedBeforeBan"])
        warn_threshold = float(config["Ban"]["WarnThreshold"])
        ban_threshold = float(config["Ban"]["BanThreshold"])

        # Check their games completed to total games ratio.
        total = row["games_completed"] + row["games_left"]
        if not total:
            return None
        elif total < min_games_completed:
            # If they have played less than the minimum, check if they can possibly recover by the time
            # they have played the minimum amount of games.
            ratio = (row["games_completed"] + (min_games_completed - total)) / min_games_completed
        else:
            ratio = row["games_completed"] / total
            
        if ratio <= warn_threshold and (ratio > ban_threshold or total < min_games_completed):
            action = "warn"
        elif ratio <= ban_threshold and total >= min_games_completed:
            action = "ban"
        else:
            action = None

        return (action, ratio)
Example #27
0
    def check_rating_requirements(self, names, channel, game_type):
        """Checks if someone meets the rating requirements to play on the server."""
        config = minqlbot.get_config()
        min_rating = 0
        max_rating = 0
        if "Balance" in config:
            if "MinimumRating" in config["Balance"]:
                min_rating = int(config["Balance"]["MinimumRating"])
            if "MaximumRating" in config["Balance"]:
                max_rating = int(config["Balance"]["MaximumRating"])
        else:
            return True

        if not min_rating and not max_rating:
            return True

        not_cached = self.not_cached(game_type, names)
        if not_cached:
            with self.rlock:
                for lookup in self.lookups:
                    for n in self.lookups[lookup][1]:
                        if n in not_cached:
                            not_cached.remove(n)
                if not_cached:
                    self.fetch_player_ratings(not_cached, channel, game_type)
                if (self.check_rating_requirements, (names, channel, game_type)) not in self.pending:
                    self.pending.append((self.check_rating_requirements, (names, channel, game_type)))
                return False

        for name in names:
            if "real_elo" in self.cache[name][game_type]:
                rating = self.cache[name][game_type]["real_elo"]
            else:
                rating = self.cache[name][game_type]["elo"]

            if (rating > max_rating and max_rating != 0) or (rating < min_rating and min_rating != 0):
                allow_spec = config["Balance"].getboolean("AllowSpectators", fallback=True)
                if allow_spec:
                    player = self.player(name)
                    if not player:
                        return True
                    if player.team != "spectator":
                        self.put(name, "spectator")
                        if rating > max_rating and max_rating != 0:
                            self.tell(
                                "^7Sorry, but you can have at most ^6{}^7 rating to play here and you have ^6{}^7.".format(
                                    max_rating, rating
                                ),
                                name,
                            )
                        elif rating < min_rating and min_rating != 0:
                            self.tell(
                                "^7Sorry, but you need at least ^6{}^7 rating to play here and you have ^6{}^7.".format(
                                    min_rating, rating
                                ),
                                name,
                            )
                else:
                    player = self.player(name)
                    if not player:
                        return True
                    elif player.team != "spectator":
                        self.put(player, "spectator")
                    self.flag_player(player)
                    player.mute()
                    self.delay(
                        25,
                        lambda: player.tell(
                            "^7You do not meet the rating requirements on this server. You will be kicked shortly."
                        ),
                    )
                    self.delay(40, player.kickban)

        return True
Example #28
0
    def teams_info(self, channel, game_type):
        """Send average team ratings and an improvement suggestion to whoever asked for it.

        """
        teams = self.teams()
        diff = len(teams["red"]) - len(teams["blue"])
        if diff:
            channel.reply("^7Both teams should have the same number of players.")
            return True

        players = teams["red"] + teams["blue"]
        not_cached = self.not_cached(game_type, players)

        if not_cached:
            with self.rlock:
                for lookup in self.lookups:
                    for n in self.lookups[lookup][1]:
                        if n in not_cached:
                            not_cached.remove(n)
                if not_cached:
                    self.fetch_player_ratings(not_cached, channel, game_type)
                if (self.teams_info, (channel, game_type)) not in self.pending:
                    self.pending.append((self.teams_info, (channel, game_type)))
                # Let a later call to execute_pending come back to us.
                return False

        avg_red = self.team_average(teams["red"], game_type)
        avg_blue = self.team_average(teams["blue"], game_type)
        switch = self.suggest_switch(teams, game_type)
        diff = len(teams["red"]) - len(teams["blue"])
        diff_rounded = abs(round(avg_red) - round(avg_blue))  # Round individual averages.
        if round(avg_red) > round(avg_blue):
            channel.reply("^1{} ^7vs ^4{}^7 - DIFFERENCE: ^1{}".format(round(avg_red), round(avg_blue), diff_rounded))
        elif round(avg_red) < round(avg_blue):
            channel.reply("^1{} ^7vs ^4{}^7 - DIFFERENCE: ^4{}".format(round(avg_red), round(avg_blue), diff_rounded))
        else:
            channel.reply("^1{} ^7vs ^4{}^7 - Holy shit!".format(round(avg_red), round(avg_blue)))

        config = minqlbot.get_config()
        if "Balance" in config:
            minimum_suggestion_diff = int(config["Balance"].get("MinimumSuggestionDifference", fallback="25"))
        else:
            minimum_suggestion_diff = 25

        if switch and switch[1] >= minimum_suggestion_diff:
            channel.reply(
                "^7SUGGESTION: switch ^6{}^7 with ^6{}^7. Type !a to agree.".format(
                    switch[0][0].clean_name, switch[0][1].clean_name
                )
            )
            if (
                not self.suggested_pair
                or self.suggested_pair[0] != switch[0][0]
                or self.suggested_pair[1] != switch[0][1]
            ):
                self.suggested_pair = (switch[0][0], switch[0][1])
                self.suggested_agree = [False, False]
        else:
            i = random.randint(0, 99)
            if not i:
                channel.reply("^7Teens look ^6good!")
            else:
                channel.reply("^7Teams look good!")
            self.suggested_pair = None

        return True
Example #29
0
    def teams_info(self, channel, game_type):
        """Send average team ratings and an improvement suggestion to whoever asked for it.

        """
        teams = self.teams()
        diff = len(teams["red"]) - len(teams["blue"])
        if diff:
            channel.reply(
                "^7Both teams should have the same number of players.")
            return True

        players = teams["red"] + teams["blue"]
        not_cached = self.not_cached(game_type, players)

        if not_cached:
            with self.rlock:
                for lookup in self.lookups:
                    for n in self.lookups[lookup][1]:
                        if n in not_cached:
                            not_cached.remove(n)
                if not_cached:
                    self.fetch_player_ratings(not_cached, channel, game_type)
                if (self.teams_info, (channel, game_type)) not in self.pending:
                    self.pending.append(
                        (self.teams_info, (channel, game_type)))
                # Let a later call to execute_pending come back to us.
                return False

        avg_red = self.team_average(teams["red"], game_type)
        avg_blue = self.team_average(teams["blue"], game_type)
        switch = self.suggest_switch(teams, game_type)
        diff = len(teams["red"]) - len(teams["blue"])
        diff_rounded = abs(round(avg_red) -
                           round(avg_blue))  # Round individual averages.
        if round(avg_red) > round(avg_blue):
            channel.reply("^1{} ^7vs ^4{}^7 - DIFFERENCE: ^1{}".format(
                round(avg_red), round(avg_blue), diff_rounded))
        elif round(avg_red) < round(avg_blue):
            channel.reply("^1{} ^7vs ^4{}^7 - DIFFERENCE: ^4{}".format(
                round(avg_red), round(avg_blue), diff_rounded))
        else:
            channel.reply("^1{} ^7vs ^4{}^7 - Holy shit!".format(
                round(avg_red), round(avg_blue)))

        config = minqlbot.get_config()
        if "Balance" in config:
            minimum_suggestion_diff = int(config["Balance"].get(
                "MinimumSuggestionDifference", fallback="25"))
        else:
            minimum_suggestion_diff = 25

        if switch and switch[1] >= minimum_suggestion_diff:
            channel.reply(
                "^7SUGGESTION: switch ^6{}^7 with ^6{}^7. Type !a to agree.".
                format(switch[0][0].clean_name, switch[0][1].clean_name))
            if not self.suggested_pair or self.suggested_pair[0] != switch[0][
                    0] or self.suggested_pair[1] != switch[0][1]:
                self.suggested_pair = (switch[0][0], switch[0][1])
                self.suggested_agree = [False, False]
        else:
            i = random.randint(0, 99)
            if not i:
                channel.reply("^7Teens look ^6good!")
            else:
                channel.reply("^7Teams look good!")
            self.suggested_pair = None

        return True