commit be14648dfd1fa786c8c6ba91af774781603603b7 Author: Hipstercat Date: Mon Sep 28 03:40:13 2020 +0200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b302696 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +data/ +venv/ +.idea/ \ No newline at end of file diff --git a/cogs/sacrifice/__init__.py b/cogs/sacrifice/__init__.py new file mode 100644 index 0000000..c3c7839 --- /dev/null +++ b/cogs/sacrifice/__init__.py @@ -0,0 +1,7 @@ +from .sacrifice import SacrificeCog +import asyncio + + +def setup(bot): + n = SacrificeCog(bot) + bot.add_cog(n) \ No newline at end of file diff --git a/cogs/sacrifice/sacrifice.py b/cogs/sacrifice/sacrifice.py new file mode 100644 index 0000000..2214aa1 --- /dev/null +++ b/cogs/sacrifice/sacrifice.py @@ -0,0 +1,160 @@ +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() + print(str(payload.message_id)) + print(tracked.keys()) + if str(payload.message_id) in tracked.keys(): + # fetch channel + channel = await self.bot.fetch_channel(531832488901738508) # channel + if not channel: + print("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: + print("not msg") + tracked.remove(payload.message_id) + await self.config.tracked_msgs.set(tracked) + return + opponents = orig_msg.mentions + if not opponents: + print("not opponent") + tracked.remove(payload.message_id) + await self.config.tracked_msgs.set(tracked) + return + + opponent = opponents[0] + + print("not yet") + print(str(payload.user_id)) + print(str(opponent.id)) + print(payload.emoji) + if str(payload.user_id) == str(opponent.id) and payload.emoji.name == "👍": + print("true") + outcome = orig_msg.content.split(" ")[2] + print(outcome) # DEBUG + 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 + else: + print("not valid winloss: %s" % outcome) + else: + print("not tracked") + + 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): + return 1/(1+math.pow(10, -d/400)) # https://fr.wikipedia.org/wiki/Classement_Elo#Relation_entre_diff%C3%A9rence_de_points_Elo_et_probabilit%C3%A9_de_gain + + 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) + print("%s = %s + %s * (1 - %s)" % (new_elo_winner, elo_winner, k_winner, 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) + print(scores) + print(elos) + 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: + # calculate new elo + return 1500 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..58ae790 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +Red-DiscordBot \ No newline at end of file