async def fetch(channel: TextChannel): try: url = GameData.__get_url(channel) except BaseException as exception: await handle(None, exception) raise UsageException.directive_missing(channel, "games") try: sheets_fetcher = Sheets.from_files( "client_secrets.json", "oath_cache.json", ) sheets = sheets_fetcher.get(url) games: List[Game] = [] # Must be the first sheet # Skip headers for row in sheets._sheets[0]._values[1:]: try: games.append( Game( datetime.strptime(row[4], "%m/%d/%Y"), Team(json.loads(row[0]), row[1]), Team(json.loads(row[2]), row[3]), )) except BaseException as exception: await handle(None, exception) return GameData(games) except BaseException as exception: await handle(None, exception) raise UsageException.game_sheet_not_loaded(channel, url)
async def fetch(id: str, channel: TextChannel) -> List[Game]: url = f"https://docs.google.com/spreadsheets/d/{id}" try: sheets_fetcher = Sheets.from_files( "client_secrets.json", "oath_cache.json", ) sheets = sheets_fetcher.get(url) games: List[Game] = [] # Must be the first sheet # Skip headers for row in sheets._sheets[0]._values[1:]: try: games.append( Game( datetime.strptime(row[4], "%m/%d/%Y"), Team(json.loads(row[0]), row[1]), Team(json.loads(row[2]), row[3]), )) except BaseException as exception: await handle(None, exception) return games except BaseException as exception: await handle(None, exception) raise UsageException.game_sheet_not_loaded(channel, url)
async def show_next_match(self, order: Callable) -> None: if not self.is_ready(): raise UsageException.not_enough_for_match(self.channel) next_match = await self.get_next_match(order) if next_match is None: raise UsageException.seen_all_matches(self.channel) (number, match) = next_match (team_one, team_two) = match composite = await draw_composite( number, team_one, team_two, self.channel.id, ) await self.channel.send(file=File(composite))
async def unready(self, user: Member) -> None: player = Player(user) if player not in self.players: raise UsageException.join_the_lobby_first(self.channel) ind = self.players.index(player) self.players[ind].set_unready() self.reset_orderings() await self.show(title=f"{player.get_name()} is not ready.")
async def add(self, user: Member, author: Optional[Member] = None) -> None: if author is not None: if not self.has_joined(Player(author)): raise UsageException.adder_must_join(self.channel) # Other players cannot add you to the lobby # if you left on your own. (until lobby reset) if self.has_left_before(Player(user)): raise UsageException.leaver_not_added(self.channel) player = Player(user) if self.has_joined(player): raise UsageException.already_joined(self.channel, author is not None) if not self.has_open_spots(): raise UsageException.no_overflow(self.channel) self.players.append(player) await self.show(desc=f"{player.get_name()} has Joined!")
async def show_next_shuffle(self) -> None: if not self.is_ready(): raise UsageException.not_enough_for_match(self.channel) if "shuffles" not in self._cache: matches = self.get_matches() shuffle(matches) self._cache["shuffles"] = iter(enumerate(matches)) next_match = next(self._cache["shuffles"], None) if next_match is None: raise UsageException.seen_all_matches(self.channel) (number, match) = next_match embed = Embed(colour=Colour.teal()) embed.title = f"Shuffle {number+1}" for i, team in enumerate(match): players = "".join([f"• {p.get_name()}\n" for p in team]) players = players if players else "(empty)" embed.add_field(name=f"Team {i+1}", value=players, inline=False) await self.channel.send(embed=embed)
async def unready(self, user: Member) -> None: player = Player(user) if player not in self.players: raise UsageException.join_the_lobby_first(self.channel) ind = self.players.index(player) player = self.players[ind] if player.is_ready(): player.set_unready() self.clear_cache() await self.show(desc=f"{player.get_name()} is not Ready")
async def add(self, user: Member, author: Optional[Member] = None) -> None: if author is not None: if not self.has_joined(Player(author)): raise UsageException.adder_must_join(self.channel) # Other players cannot add you to the lobby # if you left on your own. (until lobby reset) if self.has_left_before(Player(user)): raise UsageException.leaver_not_added(self.channel) player = Player(user) if self.has_joined(player): raise UsageException.already_joined(self.channel, author is not None) self.players.append(player) if self.ready_count() == 7: await self.broadcast_game_almost_full() await self.show(title=f"{player.get_name()} has joined the game!")
async def ready(self, user: Member) -> None: player = Player(user) if player not in self.players: await self.add(user) if player.is_ready(): raise UsageException.already_ready(self.channel) if self.is_ready(): raise UsageException.game_is_full(self.channel) ind = self.players.index(player) self.players[ind].set_ready() self.reset_orderings() if self.is_ready(): title = f"Game starting in ({self.channel.mention}))" embed = self.get_lobby_message(mention=True, title=title) await self.channel.send(embed=embed) else: await self.show(title=f"{player.get_name()} is ready!")
async def remove(self, user: Member, author: Optional[Member] = None): player = Player(user) if player not in self.players: raise UsageException.not_in_lobby(self.channel, player) if author is None: # If a user removes themself, then _others_ # can't add them back until the lobby resets. self.leavers.append(player) self.players.remove(player) await self.show(title=f"{player.get_name()} has left the lobby.")
async def ready(self, user: Member) -> None: player = Player(user) if player not in self.players: await self.add(user) if player.is_ready(): raise UsageException.already_ready(self.channel) if self.is_full(): raise UsageException.game_is_full(self.channel) was_ready = self.is_ready() ind = self.players.index(player) self.players[ind].set_ready() self.clear_cache() if self.c.vMax is not None and self.ready_count() == self.c.vMax - 1: await self.broadcast_game_almost_full() if not was_ready and self.is_ready(): await self.show(mention=True, desc="The lobby is now ready!") else: await self.show(desc=f"{player.get_name()} is Ready!")
async def ranked(lobby: "Lobby", ctx: Context): """Create fair teams based on player history""" if not lobby.is_ready(): raise UsageException.not_enough_for_match(lobby.channel) if __name__ not in lobby._cache: id = lobby.c.pLeft4Dead["history"] matches = lobby.get_matches() data = await GameData.fetch(id, lobby.channel) inactive_rank = get_ranks(data, Season.all_time()) if rc.USE_ROLLING_SEASON: season = Season(rc.LENGTH_DAYS, rc.PLACEMENT_GAMES) active_rank = get_ranks(data, season) def get_player_rank(p: int) -> int: if active_rank and p in active_rank: return active_rank[p] if p in inactive_rank: return inactive_rank[p] return rc.AVERAGE_RANK await rank(matches, get_player_rank) lobby._cache[__name__] = iter(enumerate(matches)) lobby._cache[f"{__name__}-get_rank"] = get_player_rank next_match = next(lobby._cache[__name__], None) if next_match is None: raise UsageException.seen_all_matches(lobby.channel) i, teams = next_match teams = list(teams) team1, team2 = list(teams)[0], list(teams[1]) channel = lobby.channel.id get_rank = lobby._cache[f"{__name__}-get_rank"] composite = await draw_composite(i, team1, team2, channel, get_rank) await ctx.send(file=File(composite))
async def remove(self, user: Member, author: Optional[Member] = None, rage=False): player = Player(user) if player not in self.players: raise UsageException.not_in_lobby(self.channel, player) if author is None: # If a user removes themself, then _others_ # can't add them back until the lobby resets. self.leavers.append(player) await self.unready(user) self.players.remove(player) if not rage: await self.show(desc=f"{player.get_name()} has Left") else: await self.show(desc=f"{player.get_name()} says see ya jerks")
def __get_url(channel: TextChannel) -> str: if channel.topic is None: raise UsageException.directive_missing(channel, "games") id = re.findall(r"^@games\(([^\s]+)\)", channel.topic, re.M) return f"https://docs.google.com/spreadsheets/d/{id[0]}"