import math from redbot.core import commands from redbot.core import Config, checks import discord import re class SacrificeCog(commands.Cog): def __init__(self, bot): self.bot = bot self.config = Config.get_conf(self, identifier=154776548) default_global = { "elo": {}, "tracked_msgs": {} } self.config.register_global(**default_global) @staticmethod def is_valid_winloss(s): r = r"[wWlL]+" regx = re.compile(r) fm = regx.fullmatch(s) return fm async def get_rank(self, discord_id: str): elos = await self.config.elo() if discord_id not in elos: return None, None sorted_players = sorted(elos, key=elos.get, reverse=True) return sorted_players.index(discord_id) + 1, len(sorted_players) @commands.Cog.listener() async def on_raw_reaction_add(self, payload): tracked = await self.config.tracked_msgs() await self.log(str(payload.message_id)) await self.log(tracked.keys()) if str(payload.message_id) in tracked.keys(): # fetch channel channel = await self.bot.fetch_channel(620879085739966464) # channel if not channel: tracked.remove(payload.message_id) await self.config.tracked_msgs.set(tracked) return # fetch msg orig_msg = await channel.fetch_message(payload.message_id) if not orig_msg: tracked.remove(payload.message_id) await self.config.tracked_msgs.set(tracked) return opponents = orig_msg.mentions if not opponents: tracked.remove(payload.message_id) await self.config.tracked_msgs.set(tracked) return opponent = opponents[0] if str(payload.user_id) == str(opponent.id) and payload.emoji.name == "👍": await self.log("true") outcome = orig_msg.content.split(" ")[2] if SacrificeCog.is_valid_winloss(outcome): for c in outcome: if c.lower() == "w": await self.win(str(orig_msg.author.id), str(opponent.id)) elif c.lower() == "l": await self.win(str(opponent.id), str(orig_msg.author.id)) new_elo = await self.get_elo(str(orig_msg.author.id)) new_elo_2 = await self.get_elo(str(opponent.id)) bot_msg = await channel.fetch_message(tracked[str(payload.message_id)]) await bot_msg.edit(content="**%s**, your new ELO is: **%s**\n**%s**, your new ELO is: **%s**" % ( orig_msg.author.mention, round(new_elo), opponent.mention, round(new_elo_2)) ) del tracked[str(payload.message_id)] await self.config.tracked_msgs.set(tracked) return async def track_msg(self, orig_message_id: str, bot_msg_id: str): tracked = await self.config.tracked_msgs() tracked[orig_message_id] = bot_msg_id await self.config.tracked_msgs.set(tracked) @commands.command() async def rank(self, ctx, opponent: discord.Member = None, winloss: str = None): author_id = str(ctx.author.id) if not opponent and not winloss: # send elo to player rank, count = await self.get_rank(author_id) if rank: elo = await self.get_elo(author_id) await ctx.send("**%s**, your ELO is: **%s** (ranked #%s/%s)" % (ctx.author.mention, round(elo), rank, count)) else: await ctx.send("**%s**, you are not ranked yet" % ctx.author.mention) return elif opponent and not winloss: # show opponent rank opponent_id = str(opponent.id) rank, count = await self.get_rank(opponent_id) if rank: elo = await self.get_elo(opponent_id) await ctx.send("**%s**' ELO is: **%s** (ranked #%s/%s)" % (opponent.display_name, round(elo), rank, count)) else: await ctx.send("**%s** is not ranked yet" % opponent.display_name) return elif opponent and winloss: if not SacrificeCog.is_valid_winloss(winloss): await ctx.send("Invalid win/losses") return bot_msg = await ctx.send("Waiting for %s' :thumbsup: reaction to previous message..." % opponent.display_name) await self.track_msg(str(ctx.message.id), str(bot_msg.id)) return @staticmethod def expection(d): # https://fr.wikipedia.org/wiki/Classement_Elo#Relation_entre_diff%C3%A9rence_de_points_Elo_et_probabilit%C3%A9_de_gain return 1/(1+math.pow(10, -d/400)) async def win(self, winner_id: str, looser_id: str) -> None: elo_winner = await self.get_elo(winner_id) elo_looser = await self.get_elo(looser_id) diff = elo_winner - elo_looser expection_winner = SacrificeCog.expection(diff) expection_looser = SacrificeCog.expection(-diff) k_winner = 40 # TODO: should be lowered after many games k_looser = 40 # TODO: should be lowered after many games new_elo_winner = elo_winner + k_winner * (1 - expection_winner) new_elo_looser = elo_looser + k_looser * (0 - expection_looser) await self.save_elo({winner_id: new_elo_winner, looser_id: new_elo_looser}) async def save_elo(self, scores: dict) -> None: elos = await self.config.elo() elos.update(scores) await self.config.elo.set(elos) async def get_elo(self, discord_id: str) -> int: elos = await self.config.elo() if discord_id in elos: return elos[discord_id] else: # set new elo = 1500 return 1500