def save(self):
     database.execute(
         "INSERT INTO pending_matches "
         "(message_id, player1, player2, time) "
         "VALUES (?, ?, ?, ?)",
         (self.message_id, self.player1, self.player2, self.time))
     database.commit()
 async def cleanup_for_match(self, match):
     database.execute("DELETE FROM pending_matches WHERE message_id = ?",
                      (match.message_id, ))
     database.commit()
     self.message_map.pop(match.message_id)
     self.player_map.pop(match.player1)
     self.player_map.pop(match.player2)
     await (await
            self.announcement_channel.fetch_message(match.message_id
                                                    )).clear_reactions()
 async def process_player(self, player, flag, embed):
     old_rating = glicko2.Rating(player["rating_mu"], player["rating_phi"],
                                 player["rating_sigma"])
     new_rating = old_rating if flag != 0 else \
      glicko2.Glicko2().rate_1vs1(old_rating, old_rating)[1]
     database.execute(
         "UPDATE players "
         "SET rating_mu = ?, rating_phi = ?, rating_sigma = ? "
         "WHERE id = ?",
         (new_rating.mu, new_rating.phi, new_rating.sigma, player["id"]))
     database.commit()
     await match_message_helper.add_report_field(embed, player, None,
                                                 old_rating.mu,
                                                 old_rating.phi,
                                                 new_rating.mu,
                                                 new_rating.phi)
    async def process_match_complete(self, match):
        player1 = match.player1_data
        old_rating1 = glicko2.Rating(player1["rating_mu"],
                                     player1["rating_phi"],
                                     player1["rating_sigma"])
        player2 = match.player2_data
        old_rating2 = glicko2.Rating(player2["rating_mu"],
                                     player2["rating_phi"],
                                     player2["rating_sigma"])

        if match.player1_score > match.player2_score:
            new_rating1, new_rating2 = (glicko2.Glicko2().rate_1vs1(
                old_rating1, old_rating2))
        else:
            new_rating2, new_rating1 = (glicko2.Glicko2().rate_1vs1(
                old_rating2, old_rating1))

        cursor = database.execute(
            """
			INSERT INTO matches (
				player1, player2, player1_score, player2_score,
				player1_old_mu, player1_old_phi, player1_old_sigma,
				player1_new_mu, player1_new_phi, player1_new_sigma,
				player2_old_mu, player2_old_phi, player2_old_sigma,
				player2_new_mu, player2_new_phi, player2_new_sigma
			) VALUES (
				?, ?, ?, ?,
				?, ?, ?,
				?, ?, ?,
				?, ?, ?,
				?, ?, ?
			)
		""", (player1["id"], player2["id"], match.player1_score, match.player2_score,
        old_rating1.mu, old_rating1.phi, old_rating1.sigma, new_rating1.mu,
        new_rating1.phi, new_rating1.sigma, old_rating2.mu, old_rating2.phi,
        old_rating2.sigma, new_rating2.mu, new_rating2.phi, new_rating2.sigma))
        database.commit()

        embed = discord.Embed(type="rich",
                              title="Match recorded",
                              color=0x00E323)
        await match_message_helper.add_report_field(
            embed, player1, match.player1_score, old_rating1.mu,
            old_rating1.phi, new_rating1.mu, new_rating1.phi)
        await match_message_helper.add_report_field(
            embed, player2, match.player2_score, old_rating2.mu,
            old_rating2.phi, new_rating2.mu, new_rating2.phi)
        embed.set_footer(text=f"Match ID: {cursor.lastrowid}")
        await self.output_channel.send(embed=embed)

        await utils.update_role(match.player1, old_rating1.mu, old_rating1.phi,
                                new_rating1.mu, new_rating1.phi)
        await utils.update_role(match.player2, old_rating2.mu, old_rating2.phi,
                                new_rating2.mu, new_rating2.phi)

        await self.cleanup_for_match(match)
Esempio n. 5
0
    async def register(self, ctx, *platforms_raw):
        platforms = self.sanitize_platforms(platforms_raw)
        if len(platforms) == 0:
            await ctx.send(
                "You need to provide a valid platform that is one of the following: "
                + ", ".join(utils.platform_names) + ".")
            return

        platform_names = ", ".join(
            utils.format_platform_name(platform) for platform in platforms)

        player = database.execute(
            "SELECT platforms, rating_mu, rating_phi FROM players WHERE id = ?",
            (ctx.author.id, )).fetchone()
        if player is None:
            # First time registering.
            database.execute(
                "INSERT INTO players (id, platforms) VALUES (?, ?)",
                (ctx.author.id, " ".join(platforms)))
            database.commit()
            await utils.update_role(ctx.author.id, None, None, 1500, 350)
            await ctx.send(f"Signed up for {platform_names}.")
        else:
            # Already registered.
            no_new_entries = True
            player_platforms = player["platforms"].split()
            for platform in platforms:
                if platform not in player_platforms:
                    player_platforms.append(platform)
                    no_new_entries = False
            if no_new_entries:
                await ctx.send(
                    f"You're already signed up for {platform_names}.")
                return
            else:
                database.execute(
                    "UPDATE players SET platforms = ? WHERE id = ?",
                    (" ".join(player_platforms), ctx.author.id))
                database.commit()
                await utils.update_role(ctx.author.id, None, None,
                                        player["rating_mu"],
                                        player["rating_phi"])
                await ctx.send(f"Signed up for {platform_names}.")
Esempio n. 6
0
 async def update_displayname(self, ctx, *name):
     name = " ".join(name).strip()
     if database.execute(
             "SELECT EXISTS (SELECT 1 FROM players WHERE id = ? AND platforms <> '')",
         (ctx.author.id, )).fetchone()[0] == 0:
         await ctx.send("You are not registered yet.")
         return
     if name == "":
         database.execute(
             "UPDATE players SET display_name = NULL WHERE id = ?",
             (ctx.author.id, ))
         database.commit()
         await ctx.send("Cleared display name.")
     else:
         if database.execute(
                 "SELECT EXISTS (SELECT 1 FROM players WHERE display_name = ?)",
             (name, )).fetchone()[0] == 1:
             await ctx.send(
                 f"The display name \"{utils.escape_markdown(name)}\" is already in use by another player."
             )
             return
         database.execute(
             "UPDATE players SET display_name = ? WHERE id = ?",
             (name, ctx.author.id))
         database.commit()
         await ctx.send(
             f"Display name set to \"{utils.escape_markdown(name)}\".")
    def setup(self):
        if self.running: return
        self.running = True
        self.bot_id = utils.bot.user.id
        self.announcement_channel = utils.bot.get_channel(
            get_config("matchmaking_announcement_channel"))
        self.announcement_channel_id = self.announcement_channel.id
        self.output_channel = utils.bot.get_channel(
            get_config("command_channel"))
        for row in (database.execute(
                "SELECT rowid, * FROM pending_matches").fetchall()):
            self.add_match(PendingMatch(**row.dict))

        self.match_lifetime = get_config("pending_match_lifetime")
        asyncio.create_task(self.clear_matches())
        utils.bot.add_listener(self.on_reaction, "on_raw_reaction_add")
 async def on_reaction(self, data):
     if data.user_id == self.bot_id:
         return
     if data.message_id != self.matchfinding_message.id:
         return
     if data.emoji.id not in self.emoji_mapping:
         return
     platform_data = self.emoji_mapping[data.emoji.id]
     if data.event_type == "REACTION_ADD":
         if data.user_id in match_manager.player_map:
             await remove_reaction(self.matchfinding_message, data.emoji,
                                   await utils.get_member(data.user_id))
             return
         if data.user_id in self.player_map:
             player = self.player_map[data.user_id]
         else:
             player_data = database.execute(
                 "SELECT * FROM players WHERE id = ? AND platforms <> ''",
                 (data.user_id, )).fetchone()
             if player_data is None:
                 await remove_reaction(self.matchfinding_message,
                                       data.emoji, await
                                       utils.get_member(data.user_id))
                 return
             player = Player(player_data, self)
         if platform_data[0] not in player.platforms:
             await remove_reaction(self.matchfinding_message, data.emoji,
                                   await utils.get_member(data.user_id))
             return
         self.player_map[player.player_id] = player
         self.pools[platform_data[1]].add(player)
         player.platform_count += 1
         await self.find_matches()
     else:
         if data.user_id not in self.player_map:
             return
         player = self.player_map[data.user_id]
         pool = self.pools[platform_data[1]]
         if player in pool:
             pool.discard(player)
             player.platform_count -= 1
             if player.platform_count < 1:
                 self.player_map.pop(player.player_id)
Esempio n. 9
0
    async def info_player_user(self, ctx, user: discord.User):
        """
		Gets a Player's information from a discord.User. This is done by getting
		the ID of the User and passing it to
		get_player().
		:param ctx: Context, comes with every command
		:param user: A user, which could be a mention, an ID, or anything else Discord can translate into a user.
		"""

        player = database.execute(
            "SELECT * FROM players WHERE id=? AND platforms <> ''",
            (user.id, )).fetchone()
        if player is None:
            await ctx.send(
                f"The user \"{utils.escape_markdown(user.display_name)}\" isn't registered."
            )
            return
        await self.send_player_info(ctx, player, await
                                    utils.get_member(user.id))
Esempio n. 10
0
 async def add_match_embed_player(self, embed, match, player):
     player = "player" + player
     player_row = database.execute(
         "SELECT display_name FROM players WHERE id = ?",
         (match[player], )).fetchone()
     name = player_row["display_name"]
     if name is None:
         user = await utils.get_member(match[player])
         name = "[No name.]" if user is None else user.display_name
     rating_change = match[player + "_rating_change"]
     rating_change_sign = '+' if rating_change >= 0 else '\u2013'
     embed.add_field(
         name=utils.escape_markdown(name),
         value=
         (f"**{match[player + '_score']}**\n"
          f"{int(match[player + '_old_mu'])} \u00B1 {int(2 * match[player + '_old_phi'])} \u2192 "
          f"{int(match[player + '_new_mu'])} \u00B1 {int(2 * match[player + '_new_phi'])}\n"
          f"{rating_change_sign} {abs(rating_change)}\n" +
          utils.get_rank_with_comparison(
              match[player + '_old_mu'], match[player + '_old_phi'],
              match[player + '_new_mu'], match[player + '_new_phi'])))
Esempio n. 11
0
    async def info_player_name(self, ctx, *name):
        """
		Gets a Player's information from a string. This is done by passing the name to spreadsheets.find_player_id(name)
		:param ctx: Context, comes with every command
		:param name: String
		"""
        name = " ".join(name).strip()
        if name == "":
            await ctx.send_help(self.info_player_name)
            return

        player = database.execute(
            "SELECT * FROM players WHERE display_name=? AND platforms <> ''",
            (name, )).fetchone()
        if player is None:
            await ctx.send(
                "There is no registered player with the display name "
                f"\"{utils.escape_markdown(name)}\".")
            return
        await self.send_player_info(ctx, player, await
                                    utils.get_member(player["id"]))
Esempio n. 12
0
    async def leaderboard(self, ctx):
        players = database.execute(
            "SELECT id, display_name, rating_mu, rating_phi "
            "FROM players WHERE platforms <> '' AND rating_phi < 150 "
            "ORDER BY rating_mu DESC, rating_phi ASC LIMIT 10").fetchall()
        if len(players) == 0:
            await ctx.send(
                "There are no players with ranks at the moment, so no leaders to display."
            )
            return

        index = 0
        message = "**Top Puyo players**"
        for player in players:
            index += 1
            message += ("\n" + str(index) + ". " + utils.escape_markdown(
                await self.get_player_name(player)) + " | " +
                        str(int(player["rating_mu"])) + " \u00B1 " +
                        str(int(2 * player["rating_phi"])) + " | " +
                        utils.get_rank(player["rating_mu"]).name)
        await ctx.send(message)
Esempio n. 13
0
    async def info_match(self, ctx, ids):
        try:
            match_id = int(ids)
        except ValueError:
            match_id = 0
        if match_id < 1:
            await ctx.send(f"`{ids}` is not a positive integer.")
            return

        match = database.execute("SELECT * FROM matches WHERE id = ?",
                                 (match_id, )).fetchone()
        if match is None:
            await ctx.send(f"There is not yet a match with ID {match_id}.")
            return

        embed = discord.Embed(type="rich",
                              title="Match information",
                              color=0xFF8337)
        await self.add_match_embed_player(embed, match, "1")
        await self.add_match_embed_player(embed, match, "2")
        embed.set_footer(text=f"Match ID: {match_id}")
        embed.timestamp = match["date"]

        await ctx.send(embed=embed)
Esempio n. 14
0
    async def info_player_username(self, ctx, platform, *username):
        username = "******".join(username).strip()
        if username == "":
            await ctx.send_help(self.info_player_username)
            return

        platform = platform.casefold()
        if not platform in utils.platform_name_mapping:
            await ctx.send(
                "You need to provide a valid platform that is one of the following: "
                + ", ".join(utils.platform_names) + ".")
            return

        player = database.execute(
            f"SELECT * FROM players WHERE username_{platform}=?",
            (username, )).fetchone()
        if player is None:
            await ctx.send(
                "There is no registered player with the username "
                f"\"{utils.escape_markdown(username)}\" on {utils.format_platform_name(platform)}."
            )
            return
        await self.send_player_info(ctx, player, await
                                    utils.get_member(player["id"]))
Esempio n. 15
0
 async def update_username(self, ctx, platform, *name):
     platform = platform.casefold()
     if not platform in utils.platform_name_mapping:
         await ctx.send(
             "You need to provide a valid platform that is one of the following: "
             + ", ".join(utils.platform_names) + ".")
         return
     name = " ".join(name).strip()
     player = database.execute(
         "SELECT platforms FROM players WHERE id = ? AND platforms <> ''",
         (ctx.author.id, )).fetchone()
     if player is None:
         await ctx.send("You are not registered yet.")
         return
     if platform not in player[0].split():
         await ctx.send(
             f"You are not signed up for {utils.format_platform_name(platform)}."
         )
         return
     if name == "":
         database.execute(
             f"UPDATE players SET username_{platform} = NULL WHERE id = ?",
             (ctx.author.id, ))
         database.commit()
         await ctx.send(
             f"Cleared username for {utils.format_platform_name(platform)}."
         )
     else:
         if database.execute(
                 f"SELECT EXISTS (SELECT 1 FROM players WHERE username_{platform} = ?)",
             (name, )).fetchone()[0] == 1:
             await ctx.send(
                 f"The username \"{utils.escape_markdown(name)}\" on "
                 f"{utils.format_platform_name(platform)} is already in use by another player."
             )
             return
         database.execute(
             f"UPDATE players SET username_{platform} = ? WHERE id = ?",
             (name, ctx.author.id))
         database.commit()
         await ctx.send(
             f"Username on {utils.format_platform_name(platform)} set to "
             f"\"{utils.escape_markdown(name)}\".")
Esempio n. 16
0
def get_player_by_ID(id_in):
    return database.execute(
        "SELECT id, display_name, rating_mu, rating_phi, rating_sigma "
        "FROM players WHERE id=? AND platforms<>''", (id_in, )).fetchone()
Esempio n. 17
0
    async def send_player_info(cls, ctx, player, user=None):
        """
		Constructs an embed containing the player info and then sends it as response
		to the info command.
		:param ctx: Context to send to.
		:param player: The player database row object.
		:param user: The Discord user object.
		"""
        rank = utils.get_rank(player["rating_mu"], player["rating_phi"])

        embed = discord.Embed(
            type="rich",
            title="Puyo Training Grounds player info",
            description=("" if user is None else
                         f"{user.name}#{user.discriminator}") +
            f"\nID {player['id']}",
            colour=rank.color)
        embed.set_author(name=player["display_name"] or (
            "[No name.]" if user is None else user.display_name))
        if user is not None:
            embed.set_thumbnail(url=str(user.avatar_url))
        embed.set_footer(text="Registration date")
        embed.timestamp = player["registration_date"]
        embed.add_field(
            name="Platforms",
            value="\n".join([
                utils.format_platform_name(platform) +
                ("" if player["username_" + platform] is None else
                 utils.escape_markdown(f" ({player['username_' + platform]})"))
                for platform in player["platforms"].split()
            ]),
            inline=False)
        embed.add_field(
            name="Rating",
            value=
            f"{int(player['rating_mu'])} \u00B1 {int(2 * player['rating_phi'])}"
        )
        # Why double the phi? Because phi is just half of the distance to the boundary of the 95% confidence
        # interval. Source: https://www.glicko.net/glicko/glicko2.pdf (lines 7 to 11).
        embed.add_field(name="Rank", value=rank.name)
        if player["rating_phi"] < 150:
            embed.add_field(
                name="Position",
                value="#" + str(
                    database.execute(
                        "SELECT COUNT(*) FROM players WHERE "
                        "platforms <> '' AND rating_phi < 150 AND "
                        "(rating_mu > :mu OR rating_mu = :mu AND rating_phi < :phi)",
                        {
                            "mu": player["rating_mu"],
                            "phi": player["rating_phi"]
                        }).fetchone()[0] + 1))
        matches, wins = database.execute(
            "SELECT COUNT(*), "
            "COUNT("
            "CASE WHEN (player1 = :id) == (player1_score > player2_score) "
            "THEN 1 ELSE NULL END"
            ") FROM matches WHERE player1=:id OR player2=:id", {
                "id": player["id"]
            }).fetchone().tuple
        embed.add_field(name="Matches", value=str(matches))
        if matches != 0:
            ratio = wins * 10000 // matches
            decimal = ratio % 100
            embed.add_field(
                name="Wins",
                value=
                f"{wins} ({ratio // 100}.{'0' if decimal < 10 else ''}{decimal}%)"
            )
        await ctx.send(embed=embed)
Esempio n. 18
0
    async def unregister(self, ctx, *platforms_raw):
        if ctx.author.id in matchfinder.player_map:
            await ctx.send(
                "You are currently in the matckmaking queue. Leave the queue before unregistering."
            )
            return
        if ctx.author.id in match_manager.player_map:
            await ctx.send(
                "You have a match pending. Complete the match before unregistering."
            )
            return

        nuke = len(platforms_raw) != 0 and platforms_raw[0].casefold() == "all"
        platforms = self.sanitize_platforms(platforms_raw)
        if not nuke and len(platforms) == 0:
            await ctx.send(
                "You need to provide a valid platform that is one of the following: "
                + ", ".join(utils.platform_names) + ".")
            return
        platform_names = ", ".join(
            utils.format_platform_name(platform) for platform in platforms)

        player = database.execute(
            "SELECT platforms, rating_mu, rating_phi FROM players WHERE id = ? AND platforms <> ''",
            (ctx.author.id, )).fetchone()
        if player is None:
            await ctx.send(f"You are not signed up here, no worries.")
        else:
            old_player_platforms = player["platforms"].split()
            new_player_platforms = ([] if nuke else [
                platform for platform in old_player_platforms
                if platform not in platforms
            ])
            if len(new_player_platforms) != len(old_player_platforms):
                platform_nullify = ", ".join(f"username_{platform} = NULL"
                                             for platform in platforms)
                zenkeshita = nuke or len(new_player_platforms) == 0
                database.execute(
                    """
					UPDATE players SET
						platforms = "",
						display_name = NULL,
						username_pc = NULL,
						username_switch = NULL,
						username_ps4 = NULL
					WHERE id = :id
					""" if zenkeshita else
                    f"UPDATE players SET platforms = :platforms, {platform_nullify} WHERE id = :id",
                    {
                        "platforms": " ".join(new_player_platforms),
                        "id": ctx.author.id
                    })
                database.commit()
                await ctx.send(
                    "Unregistered from __all__ platforms. You are no longer in the system."
                    if nuke else f"Unregistered from {platform_names}." +
                    (" __You are no longer in the system.__"
                     if len(new_player_platforms) == 0 else ""))
                if zenkeshita:
                    await utils.update_role(ctx.author.id, player["rating_mu"],
                                            player["rating_phi"], None, None)
            else:
                await ctx.send(f"You are not signed up for {platform_names}.")