Mostly messaging adjustments, fix for failing to talley votes

pull/147/head
bobloy 4 years ago
parent e27cfba763
commit f3965b73d8

@ -10,13 +10,15 @@ from redbot.core.bot import Red
from redbot.core.utils import AsyncIter from redbot.core.utils import AsyncIter
from werewolf.builder import parse_code from werewolf.builder import parse_code
from werewolf.constants import ALIGNMENT_NEUTRAL
from werewolf.player import Player from werewolf.player import Player
from werewolf.role import Role from werewolf.role import Role
from werewolf.votegroup import VoteGroup from werewolf.votegroup import VoteGroup
log = logging.getLogger("red.fox_v3.werewolf.game") log = logging.getLogger("red.fox_v3.werewolf.game")
HALF_DAY_LENGTH = 24 # FixMe: to 120 later for 4 minute days HALF_DAY_LENGTH = 60 # FixMe: Make configurable
HALF_NIGHT_LENGTH = 60
async def anyone_has_role( async def anyone_has_role(
@ -167,7 +169,7 @@ class Game:
await player.member.add_roles(*[self.game_role]) await player.member.add_roles(*[self.game_role])
except discord.Forbidden: except discord.Forbidden:
log.exception(f"Unable to add role **{self.game_role.name}**") log.exception(f"Unable to add role **{self.game_role.name}**")
await ctx.send( await ctx.maybe_send_embed(
f"Unable to add role **{self.game_role.name}**\n" f"Unable to add role **{self.game_role.name}**\n"
f"Bot is missing `manage_roles` permissions" f"Bot is missing `manage_roles` permissions"
) )
@ -210,7 +212,7 @@ class Game:
category=self.channel_category, category=self.channel_category,
) )
except discord.Forbidden: except discord.Forbidden:
await ctx.send( await ctx.maybe_send_embed(
"Unable to create Game Channel and none was provided\n" "Unable to create Game Channel and none was provided\n"
"Grant Bot appropriate permissions or assign a game_channel" "Grant Bot appropriate permissions or assign a game_channel"
) )
@ -225,7 +227,7 @@ class Game:
) )
except discord.Forbidden as e: except discord.Forbidden as e:
log.exception("Unable to rename Game Channel") log.exception("Unable to rename Game Channel")
await ctx.send("Unable to rename Game Channel, ignoring") await ctx.maybe_send_embed("Unable to rename Game Channel, ignoring")
try: try:
for target, ow in overwrite.items(): for target, ow in overwrite.items():
@ -235,7 +237,7 @@ class Game:
target=target, overwrite=curr, reason="(BOT) New game of werewolf" target=target, overwrite=curr, reason="(BOT) New game of werewolf"
) )
except discord.Forbidden: except discord.Forbidden:
await ctx.send( await ctx.maybe_send_embed(
"Unable to edit Game Channel permissions\n" "Unable to edit Game Channel permissions\n"
"Grant Bot appropriate permissions to manage permissions" "Grant Bot appropriate permissions to manage permissions"
) )
@ -406,14 +408,17 @@ class Game:
await vote_message.add_reaction("👎") await vote_message.add_reaction("👎")
await asyncio.sleep(15) await asyncio.sleep(15)
reaction_list = vote_message.reactions
if True: # TODO: Allow customizable vote history deletion. # Refetch for reactions
await vote_message.delete() vote_message = await self.village_channel.fetch_message(id=vote_message.id)
reaction_list = vote_message.reactions
raw_up_votes = sum(p for p in reaction_list if p.emoji == "👍" and not p.me) raw_up_votes = sum(p for p in reaction_list if p.emoji == "👍" and not p.me)
raw_down_votes = sum(p for p in reaction_list if p.emoji == "👎" and not p.me) raw_down_votes = sum(p for p in reaction_list if p.emoji == "👎" and not p.me)
if True: # TODO: Allow customizable vote history deletion.
await vote_message.delete()
# TODO: Support vote count modifying roles. (Need notify and count function) # TODO: Support vote count modifying roles. (Need notify and count function)
voted_to_lynch = raw_down_votes > raw_up_votes voted_to_lynch = raw_down_votes > raw_up_votes
@ -492,13 +497,13 @@ class Game:
return return
await self._notify("at_night_start") await self._notify("at_night_start")
await asyncio.sleep(12) # 2 minutes FixMe to 120 later await asyncio.sleep(HALF_NIGHT_LENGTH) # 2 minutes FixMe to 120 later
await self.village_channel.send( await self.village_channel.send(
embed=discord.Embed(title="**Two minutes of night remain...**") embed=discord.Embed(title=f"**{HALF_NIGHT_LENGTH / 60} minutes of night remain...**")
) )
await asyncio.sleep(9) # 1.5 minutes FixMe to 90 later await asyncio.sleep(HALF_NIGHT_LENGTH) # 1.5 minutes FixMe to 90 later
await self.village_channel.send( await self.village_channel.send(
embed=discord.Embed(title="**Thirty seconds until sunrise...**") embed=discord.Embed(title=f"**{HALF_NIGHT_LENGTH / 60} minutes until sunrise...**")
) )
await asyncio.sleep(3) # .5 minutes FixMe to 30 Later await asyncio.sleep(3) # .5 minutes FixMe to 30 Later
@ -560,8 +565,7 @@ class Game:
) )
else: else:
embed.add_field( embed.add_field(
name=f"{i} - {status}{player.member.display_name}", name=f"{i} - {status}{player.member.display_name}", inline=False, value=""
inline=False,
) )
return await channel.send(embed=embed) return await channel.send(embed=embed)
@ -585,16 +589,16 @@ class Game:
if votegroup is not None: if votegroup is not None:
self.p_channels[channel_id]["votegroup"] = votegroup self.p_channels[channel_id]["votegroup"] = votegroup
async def join(self, member: discord.Member, channel: discord.TextChannel): async def join(self, ctx, member: discord.Member):
""" """
Have a member join a game Have a member join a game
""" """
if self.started: if self.started:
await channel.send("**Game has already started!**") await ctx.maybe_send_embed("**Game has already started!**")
return return
if await self.get_player_by_member(member) is not None: if await self.get_player_by_member(member) is not None:
await channel.send(f"{member.display_name} is already in the game!") await ctx.maybe_send_embed(f"{member.display_name} is already in the game!")
return return
self.players.append(Player(member)) self.players.append(Player(member))
@ -609,7 +613,7 @@ class Game:
# f"Bot is missing `manage_roles` permissions" # f"Bot is missing `manage_roles` permissions"
# ) # )
await channel.send( await ctx.maybe_send_embed(
f"{member.display_name} has been added to the game, " f"{member.display_name} has been added to the game, "
f"total players is **{len(self.players)}**" f"total players is **{len(self.players)}**"
) )
@ -645,15 +649,15 @@ class Game:
player = await self.get_player_by_member(ctx.author) player = await self.get_player_by_member(ctx.author)
if player is None: if player is None:
await ctx.send("You're not in this game!") await ctx.maybe_send_embed("You're not in this game!")
return return
if not player.alive: if not player.alive:
await ctx.send("**Corpses** can't participate...") await ctx.maybe_send_embed("**Corpses** can't participate...")
return return
if player.role.blocked: if player.role.blocked:
await ctx.send("Something is preventing you from doing this...") await ctx.maybe_send_embed("Something is preventing you from doing this...")
return return
# Let role do target validation, might be alternate targets # Let role do target validation, might be alternate targets
@ -821,7 +825,7 @@ class Game:
async def set_code(self, ctx: commands.Context, game_code): async def set_code(self, ctx: commands.Context, game_code):
if game_code is not None: if game_code is not None:
self.game_code = game_code self.game_code = game_code
await ctx.send("Code has been set") await ctx.maybe_send_embed("Code has been set")
async def get_roles(self, ctx, game_code=None): async def get_roles(self, ctx, game_code=None):
if game_code is not None: if game_code is not None:
@ -833,10 +837,12 @@ class Game:
try: try:
self.roles = await parse_code(self.game_code, self) self.roles = await parse_code(self.game_code, self)
except ValueError as e: except ValueError as e:
await ctx.send("Invalid Code: Code contains unknown character\n{}".format(e)) await ctx.maybe_send_embed(
"Invalid Code: Code contains unknown character\n{}".format(e)
)
return False return False
except IndexError as e: except IndexError as e:
await ctx.send("Invalid Code: Code references unknown role\n{}".format(e)) await ctx.maybe_send_embed("Invalid Code: Code references unknown role\n{}".format(e))
if not self.roles: if not self.roles:
return False return False
@ -898,7 +904,8 @@ class Game:
self.game_over = True self.game_over = True
alignment1 = alive_players[0].role.alignment alignment1 = alive_players[0].role.alignment
alignment2 = alive_players[1].role.alignment alignment2 = alive_players[1].role.alignment
if alignment1 == alignment2: # Same team # Same team and not neutral
if alignment1 == alignment2 and alignment1 != ALIGNMENT_NEUTRAL:
winners = alive_players winners = alive_players
else: else:
winners = [max(alive_players, key=lambda p: p.role.alignment)] winners = [max(alive_players, key=lambda p: p.role.alignment)]

@ -35,9 +35,13 @@ class Player:
async def send_dm(self, message): async def send_dm(self, message):
try: try:
await self.member.send(message) # Lets do embeds later await self.member.send(message) # Lets ToDo embeds later
except discord.Forbidden: except discord.Forbidden:
log.info(f"Unable to mention {self.member.__repr__()}")
await self.role.game.village_channel.send( await self.role.game.village_channel.send(
f"Couldn't DM {self.mention}, uh oh", f"Couldn't DM {self.mention}, uh oh",
allowed_mentions=discord.AllowedMentions(users=[self.member]), allowed_mentions=discord.AllowedMentions(users=[self.member]),
) )
except AttributeError:
log.exception("Someone messed up and added a bot to the game (I think)")
await self.role.game.village_channel.send("Someone messed up and added a bot to the game :eyes:")

@ -72,9 +72,9 @@ class Werewolf(Cog):
code = await gb.build_game(ctx) code = await gb.build_game(ctx)
if code != "": if code != "":
await ctx.send(f"Your game code is **{code}**") await ctx.maybe_send_embed(f"Your game code is **{code}**")
else: else:
await ctx.send("No code generated") await ctx.maybe_send_embed("No code generated")
@checks.guildowner() @checks.guildowner()
@commands.group() @commands.group()
@ -117,10 +117,10 @@ class Werewolf(Cog):
""" """
if role is None: if role is None:
await self.config.guild(ctx.guild).role_id.set(None) await self.config.guild(ctx.guild).role_id.set(None)
await ctx.send("Cleared Game Role") await ctx.maybe_send_embed("Cleared Game Role")
else: else:
await self.config.guild(ctx.guild).role_id.set(role.id) await self.config.guild(ctx.guild).role_id.set(role.id)
await ctx.send("Game Role has been set to **{}**".format(role.name)) await ctx.maybe_send_embed("Game Role has been set to **{}**".format(role.name))
@commands.guild_only() @commands.guild_only()
@wwset.command(name="category") @wwset.command(name="category")
@ -130,14 +130,14 @@ class Werewolf(Cog):
""" """
if category_id is None: if category_id is None:
await self.config.guild(ctx.guild).category_id.set(None) await self.config.guild(ctx.guild).category_id.set(None)
await ctx.send("Cleared Game Channel Category") await ctx.maybe_send_embed("Cleared Game Channel Category")
else: else:
category = discord.utils.get(ctx.guild.categories, id=int(category_id)) category = discord.utils.get(ctx.guild.categories, id=int(category_id))
if category is None: if category is None:
await ctx.send("Category not found") await ctx.maybe_send_embed("Category not found")
return return
await self.config.guild(ctx.guild).category_id.set(category.id) await self.config.guild(ctx.guild).category_id.set(category.id)
await ctx.send("Game Channel Category has been set to **{}**".format(category.name)) await ctx.maybe_send_embed("Game Channel Category has been set to **{}**".format(category.name))
@commands.guild_only() @commands.guild_only()
@wwset.command(name="channel") @wwset.command(name="channel")
@ -147,10 +147,10 @@ class Werewolf(Cog):
""" """
if channel is None: if channel is None:
await self.config.guild(ctx.guild).channel_id.set(None) await self.config.guild(ctx.guild).channel_id.set(None)
await ctx.send("Cleared Game Channel") await ctx.maybe_send_embed("Cleared Game Channel")
else: else:
await self.config.guild(ctx.guild).channel_id.set(channel.id) await self.config.guild(ctx.guild).channel_id.set(channel.id)
await ctx.send("Game Channel has been set to **{}**".format(channel.mention)) await ctx.maybe_send_embed("Game Channel has been set to **{}**".format(channel.mention))
@commands.guild_only() @commands.guild_only()
@wwset.command(name="logchannel") @wwset.command(name="logchannel")
@ -160,10 +160,10 @@ class Werewolf(Cog):
""" """
if channel is None: if channel is None:
await self.config.guild(ctx.guild).log_channel_id.set(None) await self.config.guild(ctx.guild).log_channel_id.set(None)
await ctx.send("Cleared Game Log Channel") await ctx.maybe_send_embed("Cleared Game Log Channel")
else: else:
await self.config.guild(ctx.guild).log_channel_id.set(channel.id) await self.config.guild(ctx.guild).log_channel_id.set(channel.id)
await ctx.send("Game Log Channel has been set to **{}**".format(channel.mention)) await ctx.maybe_send_embed("Game Log Channel has been set to **{}**".format(channel.mention))
@commands.group() @commands.group()
async def ww(self, ctx: commands.Context): async def ww(self, ctx: commands.Context):
@ -181,9 +181,9 @@ class Werewolf(Cog):
""" """
game = await self._get_game(ctx, game_code) game = await self._get_game(ctx, game_code)
if not game: if not game:
await ctx.send("Failed to start a new game") await ctx.maybe_send_embed("Failed to start a new game")
else: else:
await ctx.send("Game is ready to join! Use `[p]ww join`") await ctx.maybe_send_embed("Game is ready to join! Use `[p]ww join`")
@commands.guild_only() @commands.guild_only()
@ww.command(name="join") @ww.command(name="join")
@ -195,10 +195,27 @@ class Werewolf(Cog):
game: Game = await self._get_game(ctx) game: Game = await self._get_game(ctx)
if not game: if not game:
await ctx.send("No game to join!\nCreate a new one with `[p]ww new`") await ctx.maybe_send_embed("Failed to join a game!")
return return
await game.join(ctx.author, ctx.channel) await game.join(ctx, ctx.author)
await ctx.tick()
@commands.guild_only()
@commands.admin()
@ww.command(name="forcejoin")
async def ww_forcejoin(self, ctx: commands.Context, target: discord.Member):
"""
Force someone to join a game of Werewolf
"""
game: Game = await self._get_game(ctx)
if not game:
await ctx.maybe_send_embed("Failed to join a game!")
return
await game.join(ctx, target)
await ctx.tick() await ctx.tick()
@commands.guild_only() @commands.guild_only()
@ -213,7 +230,7 @@ class Werewolf(Cog):
game = await self._get_game(ctx) game = await self._get_game(ctx)
if not game: if not game:
await ctx.send("No game to join!\nCreate a new one with `[p]ww new`") await ctx.maybe_send_embed("No game to join!\nCreate a new one with `[p]ww new`")
return return
await game.set_code(ctx, code) await game.set_code(ctx, code)
@ -239,7 +256,7 @@ class Werewolf(Cog):
""" """
game = await self._get_game(ctx) game = await self._get_game(ctx)
if not game: if not game:
await ctx.send("No game running, cannot start") await ctx.maybe_send_embed("No game running, cannot start")
if not await game.setup(ctx): if not await game.setup(ctx):
pass # ToDo something? pass # ToDo something?
@ -257,13 +274,13 @@ class Werewolf(Cog):
# await ctx.send("Cannot stop game from PM!") # await ctx.send("Cannot stop game from PM!")
# return # return
if ctx.guild.id not in self.games or self.games[ctx.guild.id].game_over: if ctx.guild.id not in self.games or self.games[ctx.guild.id].game_over:
await ctx.send("No game to stop") await ctx.maybe_send_embed("No game to stop")
return return
game = await self._get_game(ctx) game = await self._get_game(ctx)
game.game_over = True game.game_over = True
await game.current_action.cancel() await game.current_action.cancel()
await ctx.send("Game has been stopped") await ctx.maybe_send_embed("Game has been stopped")
@commands.guild_only() @commands.guild_only()
@ww.command(name="vote") @ww.command(name="vote")
@ -277,7 +294,7 @@ class Werewolf(Cog):
target_id = None target_id = None
if target_id is None: if target_id is None:
await ctx.send("`id` must be an integer") await ctx.maybe_send_embed("`id` must be an integer")
return return
# if ctx.guild is None: # if ctx.guild is None:
@ -294,7 +311,7 @@ class Werewolf(Cog):
game = await self._get_game(ctx) game = await self._get_game(ctx)
if game is None: if game is None:
await ctx.send("No game running, cannot vote") await ctx.maybe_send_embed("No game running, cannot vote")
return return
# Game handles response now # Game handles response now
@ -304,7 +321,7 @@ class Werewolf(Cog):
elif channel in (c["channel"] for c in game.p_channels.values()): elif channel in (c["channel"] for c in game.p_channels.values()):
await game.vote(ctx.author, target_id, channel) await game.vote(ctx.author, target_id, channel)
else: else:
await ctx.send("Nothing to vote for in this channel") await ctx.maybe_send_embed("Nothing to vote for in this channel")
@ww.command(name="choose") @ww.command(name="choose")
async def ww_choose(self, ctx: commands.Context, data): async def ww_choose(self, ctx: commands.Context, data):
@ -315,7 +332,7 @@ class Werewolf(Cog):
""" """
if ctx.guild is not None: if ctx.guild is not None:
await ctx.send("This action is only available in DM's") await ctx.maybe_send_embed("This action is only available in DM's")
return return
# DM nonsense, find their game # DM nonsense, find their game
# If multiple games, panic # If multiple games, panic
@ -323,7 +340,7 @@ class Werewolf(Cog):
if await game.get_player_by_member(ctx.author): if await game.get_player_by_member(ctx.author):
break # game = game break # game = game
else: else:
await ctx.send("You're not part of any werewolf game") await ctx.maybe_send_embed("You're not part of any werewolf game")
return return
await game.choose(ctx, data) await game.choose(ctx, data)
@ -344,7 +361,7 @@ class Werewolf(Cog):
if from_name: if from_name:
await menu(ctx, from_name, DEFAULT_CONTROLS) await menu(ctx, from_name, DEFAULT_CONTROLS)
else: else:
await ctx.send("No roles containing that name were found") await ctx.maybe_send_embed("No roles containing that name were found")
@ww_search.command(name="alignment") @ww_search.command(name="alignment")
async def ww_search_alignment(self, ctx: commands.Context, alignment: int): async def ww_search_alignment(self, ctx: commands.Context, alignment: int):
@ -354,7 +371,7 @@ class Werewolf(Cog):
if from_alignment: if from_alignment:
await menu(ctx, from_alignment, DEFAULT_CONTROLS) await menu(ctx, from_alignment, DEFAULT_CONTROLS)
else: else:
await ctx.send("No roles with that alignment were found") await ctx.maybe_send_embed("No roles with that alignment were found")
@ww_search.command(name="category") @ww_search.command(name="category")
async def ww_search_category(self, ctx: commands.Context, category: int): async def ww_search_category(self, ctx: commands.Context, category: int):
@ -364,7 +381,7 @@ class Werewolf(Cog):
if pages: if pages:
await menu(ctx, pages, DEFAULT_CONTROLS) await menu(ctx, pages, DEFAULT_CONTROLS)
else: else:
await ctx.send("No roles in that category were found") await ctx.maybe_send_embed("No roles in that category were found")
@ww_search.command(name="index") @ww_search.command(name="index")
async def ww_search_index(self, ctx: commands.Context, idx: int): async def ww_search_index(self, ctx: commands.Context, idx: int):
@ -374,23 +391,29 @@ class Werewolf(Cog):
if idx_embed is not None: if idx_embed is not None:
await ctx.send(embed=idx_embed) await ctx.send(embed=idx_embed)
else: else:
await ctx.send("Role ID not found") await ctx.maybe_send_embed("Role ID not found")
async def _get_game(self, ctx: commands.Context, game_code=None) -> Union[Game, None]: async def _get_game(self, ctx: commands.Context, game_code=None) -> Union[Game, None]:
guild: discord.Guild = getattr(ctx, "guild", None) guild: discord.Guild = getattr(ctx, "guild", None)
if guild is None: if guild is None:
# Private message, can't get guild # Private message, can't get guild
await ctx.send("Cannot start game from DM!") await ctx.maybe_send_embed("Cannot start game from DM!")
return None return None
if guild.id not in self.games or self.games[guild.id].game_over: if guild.id not in self.games or self.games[guild.id].game_over:
await ctx.send("Starting a new game...") await ctx.maybe_send_embed("Starting a new game...")
valid, role, category, channel, log_channel = await self._get_settings(ctx) valid, role, category, channel, log_channel = await self._get_settings(ctx)
if not valid: if not valid:
await ctx.send("Cannot start a new game") await ctx.maybe_send_embed("Cannot start a new game")
return None return None
who_has_the_role = await anyone_has_role(guild.members, role)
if who_has_the_role:
await ctx.maybe_send_embed(
f"Cannot continue, {who_has_the_role.display_name} already has the game role."
)
return None
self.games[guild.id] = Game( self.games[guild.id] = Game(
self.bot, guild, role, category, channel, log_channel, game_code self.bot, guild, role, category, channel, log_channel, game_code
) )

Loading…
Cancel
Save