diff --git a/werewolf/builder.py b/werewolf/builder.py index 5113a0b..4a9da6a 100644 --- a/werewolf/builder.py +++ b/werewolf/builder.py @@ -3,13 +3,13 @@ from collections import defaultdict from random import choice import discord -from redbot.core import RedContext +from discord.ext import commands # Import all roles here from werewolf.roles.seer import Seer from werewolf.roles.vanillawerewolf import VanillaWerewolf from werewolf.roles.villager import Villager -from werewolf.utils.menus import menu, prev_page, next_page, close_menu +from redbot.core.utils.menus import menu, prev_page, next_page, close_menu # All roles in this list for iterating @@ -181,7 +181,7 @@ async def encode(roles, rand_roles): return out_code -async def next_group(ctx: RedContext, pages: list, +async def next_group(ctx: commands.Context, pages: list, controls: dict, message: discord.Message, page: int, timeout: float, emoji: str): perms = message.channel.permissions_for(ctx.guild.me) @@ -201,7 +201,7 @@ async def next_group(ctx: RedContext, pages: list, page=page, timeout=timeout) -async def prev_group(ctx: RedContext, pages: list, +async def prev_group(ctx: commands.Context, pages: list, controls: dict, message: discord.Message, page: int, timeout: float, emoji: str): perms = message.channel.permissions_for(ctx.guild.me) @@ -268,7 +268,7 @@ class GameBuilder: self.rand_roles = [] setup() - async def build_game(self, ctx: RedContext): + async def build_game(self, ctx: commands.Context): new_controls = { '⏪': prev_group, "⬅": prev_page, @@ -286,7 +286,7 @@ class GameBuilder: out = await encode(self.code, self.rand_roles) return out - async def list_roles(self, ctx: RedContext, pages: list, + async def list_roles(self, ctx: commands.Context, pages: list, controls: dict, message: discord.Message, page: int, timeout: float, emoji: str): perms = message.channel.permissions_for(ctx.guild.me) @@ -301,7 +301,7 @@ class GameBuilder: return await menu(ctx, pages, controls, message=message, page=page, timeout=timeout) - async def select_page(self, ctx: RedContext, pages: list, + async def select_page(self, ctx: commands.Context, pages: list, controls: dict, message: discord.Message, page: int, timeout: float, emoji: str): perms = message.channel.permissions_for(ctx.guild.me) diff --git a/werewolf/game.py b/werewolf/game.py index 22d9289..1a6641f 100644 --- a/werewolf/game.py +++ b/werewolf/game.py @@ -2,7 +2,7 @@ import asyncio import random import discord -from redbot.core import RedContext +from discord.ext import commands from werewolf.builder import parse_code from werewolf.player import Player @@ -77,7 +77,7 @@ class Game: # for c_data in self.p_channels.values(): # asyncio.ensure_future(c_data["channel"].delete("Werewolf game-over")) - async def setup(self, ctx: RedContext): + async def setup(self, ctx: commands.Context): """ Runs the initial setup @@ -673,7 +673,7 @@ class Game: async def get_day_target(self, target_id, source=None): return self.players[target_id] # ToDo check source - async def set_code(self, ctx: RedContext, game_code): + async def set_code(self, ctx: commands.Context, game_code): if game_code is not None: self.game_code = game_code await ctx.send("Code has been set") diff --git a/werewolf/utils/menus.py b/werewolf/utils/menus.py deleted file mode 100644 index 35b4fbd..0000000 --- a/werewolf/utils/menus.py +++ /dev/null @@ -1,134 +0,0 @@ -import asyncio - -import discord -from redbot.core import RedContext - - -async def menu(ctx: RedContext, pages: list, - controls: dict, - message: discord.Message = None, page: int = 0, - timeout: float = 30.0): - """ - An emoji-based menu - - .. note:: All pages should be of the same type - - .. note:: All functions for handling what a particular emoji does - should be coroutines (i.e. :code:`async def`). Additionally, - they must take all of the parameters of this function, in - addition to a string representing the emoji reacted with. - This parameter should be the last one, and none of the - parameters in the handling functions are optional - - Parameters - ---------- - ctx: RedContext - The command context - pages: `list` of `str` or `discord.Embed` - The pages of the menu. - controls: dict - A mapping of emoji to the function which handles the action for the - emoji. - message: discord.Message - The message representing the menu. Usually :code:`None` when first opening - the menu - page: int - The current page number of the menu - timeout: float - The time (in seconds) to wait for a reaction - - Raises - ------ - RuntimeError - If either of the notes above are violated - """ - if not all(isinstance(x, discord.Embed) for x in pages) and \ - not all(isinstance(x, str) for x in pages): - raise RuntimeError("All pages must be of the same type") - for key, value in controls.items(): - if not asyncio.iscoroutinefunction(value): - raise RuntimeError("Function must be a coroutine") - current_page = pages[page] - - if not message: - if isinstance(current_page, discord.Embed): - message = await ctx.send(embed=current_page) - else: - message = await ctx.send(current_page) - for key in controls.keys(): - await message.add_reaction(key) - else: - if isinstance(current_page, discord.Embed): - await message.edit(embed=current_page) - else: - await message.edit(content=current_page) - - def react_check(r, u): - return u == ctx.author and str(r.emoji) in controls.keys() - - try: - react, user = await ctx.bot.wait_for( - "reaction_add", - check=react_check, - timeout=timeout - ) - except asyncio.TimeoutError: - try: - await message.clear_reactions() - except discord.Forbidden: # cannot remove all reactions - for key in controls.keys(): - await message.remove_reaction(key, ctx.bot.user) - return None - - return await controls[react.emoji](ctx, pages, controls, - message, page, - timeout, react.emoji) - - -async def next_page(ctx: RedContext, pages: list, - controls: dict, message: discord.Message, page: int, - timeout: float, emoji: str): - perms = message.channel.permissions_for(ctx.guild.me) - if perms.manage_messages: # Can manage messages, so remove react - try: - await message.remove_reaction(emoji, ctx.author) - except discord.NotFound: - pass - if page == len(pages) - 1: - next_page = 0 # Loop around to the first item - else: - next_page = page + 1 - return await menu(ctx, pages, controls, message=message, - page=next_page, timeout=timeout) - - -async def prev_page(ctx: RedContext, pages: list, - controls: dict, message: discord.Message, page: int, - timeout: float, emoji: str): - perms = message.channel.permissions_for(ctx.guild.me) - if perms.manage_messages: # Can manage messages, so remove react - try: - await message.remove_reaction(emoji, ctx.author) - except discord.NotFound: - pass - if page == 0: - page = len(pages) - 1 # Loop around to the last item - else: - page = page - 1 - return await menu(ctx, pages, controls, message=message, - page=page, timeout=timeout) - - -async def close_menu(ctx: RedContext, pages: list, - controls: dict, message: discord.Message, page: int, - timeout: float, emoji: str): - if message: - await message.delete() - return None - - -DEFAULT_CONTROLS = { - "➡": next_page, - "⬅": prev_page, - "❌": close_menu, -} diff --git a/werewolf/werewolf.py b/werewolf/werewolf.py index 97bbede..65e73c1 100644 --- a/werewolf/werewolf.py +++ b/werewolf/werewolf.py @@ -1,12 +1,12 @@ import discord from discord.ext import commands from redbot.core import Config, checks -from redbot.core import RedContext + from redbot.core.bot import Red from werewolf.builder import GameBuilder, role_from_name, role_from_alignment, role_from_category, role_from_id from werewolf.game import Game -from werewolf.utils.menus import menu, DEFAULT_CONTROLS +from redbot.core.utils.menus import menu, DEFAULT_CONTROLS class Werewolf: @@ -36,7 +36,7 @@ class Werewolf: del game @commands.command() - async def buildgame(self, ctx: RedContext): + async def buildgame(self, ctx: commands.Context): gb = GameBuilder() code = await gb.build_game(ctx) @@ -47,7 +47,7 @@ class Werewolf: @checks.guildowner() @commands.group() - async def wwset(self, ctx: RedContext): + async def wwset(self, ctx: commands.Context): """ Base command to adjust settings. Check help for command list. """ @@ -56,7 +56,7 @@ class Werewolf: @commands.guild_only() @wwset.command(name="list") - async def wwset_list(self, ctx: RedContext): + async def wwset_list(self, ctx: commands.Context): """ Lists current guild settings """ @@ -74,7 +74,7 @@ class Werewolf: @commands.guild_only() @wwset.command(name="role") - async def wwset_role(self, ctx: RedContext, role: discord.Role=None): + async def wwset_role(self, ctx: commands.Context, role: discord.Role=None): """ Assign the game role This role should not be manually assigned @@ -88,7 +88,7 @@ class Werewolf: @commands.guild_only() @wwset.command(name="category") - async def wwset_category(self, ctx: RedContext, category_id=None): + async def wwset_category(self, ctx: commands.Context, category_id=None): """ Assign the channel category """ @@ -105,7 +105,7 @@ class Werewolf: @commands.guild_only() @wwset.command(name="channel") - async def wwset_channel(self, ctx: RedContext, channel: discord.TextChannel=None): + async def wwset_channel(self, ctx: commands.Context, channel: discord.TextChannel=None): """ Assign the village channel """ @@ -118,7 +118,7 @@ class Werewolf: @commands.guild_only() @wwset.command(name="logchannel") - async def wwset_log_channel(self, ctx: RedContext, channel: discord.TextChannel=None): + async def wwset_log_channel(self, ctx: commands.Context, channel: discord.TextChannel=None): """ Assign the log channel """ @@ -130,7 +130,7 @@ class Werewolf: await ctx.send("Game Log Channel has been set to **{}**".format(channel.mention)) @commands.group() - async def ww(self, ctx: RedContext): + async def ww(self, ctx: commands.Context): """ Base command for this cog. Check help for the commands list. """ @@ -139,7 +139,7 @@ class Werewolf: @commands.guild_only() @ww.command(name="new") - async def ww_new(self, ctx: RedContext, game_code=None): + async def ww_new(self, ctx: commands.Context, game_code=None): """ Create and join a new game of Werewolf """ @@ -151,7 +151,7 @@ class Werewolf: @commands.guild_only() @ww.command(name="join") - async def ww_join(self, ctx: RedContext): + async def ww_join(self, ctx: commands.Context): """ Joins a game of Werewolf """ @@ -166,7 +166,7 @@ class Werewolf: @commands.guild_only() @ww.command(name="code") - async def ww_code(self, ctx: RedContext, code): + async def ww_code(self, ctx: commands.Context, code): """ Adjust game code """ @@ -181,7 +181,7 @@ class Werewolf: @commands.guild_only() @ww.command(name="quit") - async def ww_quit(self, ctx: RedContext): + async def ww_quit(self, ctx: commands.Context): """ Quit a game of Werewolf """ @@ -192,7 +192,7 @@ class Werewolf: @commands.guild_only() @ww.command(name="start") - async def ww_start(self, ctx: RedContext): + async def ww_start(self, ctx: commands.Context): """ Checks number of players and attempts to start the game """ @@ -205,7 +205,7 @@ class Werewolf: @commands.guild_only() @ww.command(name="stop") - async def ww_stop(self, ctx: RedContext): + async def ww_stop(self, ctx: commands.Context): """ Stops the current game """ @@ -223,7 +223,7 @@ class Werewolf: @commands.guild_only() @ww.command(name="vote") - async def ww_vote(self, ctx: RedContext, target_id: int): + async def ww_vote(self, ctx: commands.Context, target_id: int): """ Vote for a player by ID """ @@ -263,7 +263,7 @@ class Werewolf: await ctx.send("Nothing to vote for in this channel") @ww.command(name="choose") - async def ww_choose(self, ctx: RedContext, data): + async def ww_choose(self, ctx: commands.Context, data): """ Arbitrary decision making Handled by game+role @@ -285,7 +285,7 @@ class Werewolf: await game.choose(ctx, data) @ww.group(name="search") - async def ww_search(self, ctx: RedContext): + async def ww_search(self, ctx: commands.Context): """ Find custom roles by name, alignment, category, or ID """ @@ -293,7 +293,7 @@ class Werewolf: await ctx.send_help() @ww_search.command(name="name") - async def ww_search_name(self, ctx: RedContext, *, name): + async def ww_search_name(self, ctx: commands.Context, *, name): """Search for a role by name""" if name is not None: from_name = role_from_name(name) @@ -303,7 +303,7 @@ class Werewolf: await ctx.send("No roles containing that name were found") @ww_search.command(name="alignment") - async def ww_search_alignment(self, ctx: RedContext, alignment: int): + async def ww_search_alignment(self, ctx: commands.Context, alignment: int): """Search for a role by alignment""" if alignment is not None: from_alignment = role_from_alignment(alignment) @@ -313,7 +313,7 @@ class Werewolf: await ctx.send("No roles with that alignment were found") @ww_search.command(name="category") - async def ww_search_category(self, ctx: RedContext, category: int): + async def ww_search_category(self, ctx: commands.Context, category: int): """Search for a role by category""" if category is not None: pages = role_from_category(category) @@ -323,7 +323,7 @@ class Werewolf: await ctx.send("No roles in that category were found") @ww_search.command(name="index") - async def ww_search_index(self, ctx: RedContext, idx: int): + async def ww_search_index(self, ctx: commands.Context, idx: int): """Search for a role by ID""" if idx is not None: idx_embed = role_from_id(idx) @@ -332,7 +332,7 @@ class Werewolf: else: await ctx.send("Role ID not found") - async def _get_game(self, ctx: RedContext, game_code=None): + async def _get_game(self, ctx: commands.Context, game_code=None): guild: discord.Guild = ctx.guild if guild is None: