Exemplo n.º 1
0
    async def convert(self, ctx: commands.Context, argument: str):
        # In this example we have made a custom converter.
        # This checks if an input is convertible to a
        # `discord.Member` or `discord.TextChannel` instance from the
        # input the user has given us using the pre-existing converters
        # that the library provides.

        member_converter = commands.MemberConverter()
        try:
            # Try and convert to a Member instance.
            # If this fails, then an exception is raised.
            # Otherwise, we just return the converted member value.
            member = await member_converter.convert(ctx, argument)
        except commands.MemberNotFound:
            pass
        else:
            return member

        # Do the same for TextChannel...
        textchannel_converter = commands.TextChannelConverter()
        try:
            channel = await textchannel_converter.convert(ctx, argument)
        except commands.ChannelNotFound:
            pass
        else:
            return channel

        # If the value could not be converted we can raise an error
        # so our error handlers can deal with it in one place.
        # The error has to be CommandError derived, so BadArgument works fine here.
        raise commands.BadArgument(
            f'No Member or TextChannel could be converted from "{argument}"')
Exemplo n.º 2
0
    async def logging_set(self, ctx: utils.CustomContext):
        """Starts an interactive message to set up the logging channel."""

        await ctx.send(
            "Please enter the name, ID or mention of the channel you'd like logging to go to."
        )
        try:
            channel = await self.bot.convert_message(
                ctx,
                commands.TextChannelConverter(),
                timeout=60.0,
                check=lambda m: m.author.id == ctx.author.id and m.channel.id
                == ctx.channel.id)
        except asyncio.TimeoutError:
            return await ctx.send(
                "You did not reply within 1 minute, aborting.")

        try:
            await channel.send(
                "Alright this channel is now set as the logging channel.")
        except (discord.HTTPException, discord.Forbidden):
            return await ctx.send(
                "I do not have permissions to send messages here.")

        await ctx.thumbsup()

        sql = "UPDATE guild_settings SET log_channel = $1 WHERE guild_id = $2;"
        values = (channel.id, ctx.guild.id)

        await self.bot.pool.execute(sql, *values)
        self.bot.config[ctx.guild.id]["log_channel"] = channel.id
Exemplo n.º 3
0
 def __init__(self, bot):
     self.bot = bot
     self.sdb = func_database.ServerDatabase()
     self.msg = func_msg_gen.MessageGenerator()
     self.prefix = func_prefix.Prefix()
     self.role_converter = commands.RoleConverter()
     self.channel_converter = commands.TextChannelConverter()
Exemplo n.º 4
0
    async def clone(self, ctx, ID, *,
                    channel: commands.TextChannelConverter()):
        '''
        Takes an archived channel and copies its contents to another channel.
        Emulates an authentic channel using webhooks. Due to Discord ratelimiting, this may take several minutes.

        __Parameters__
        <ID> The ID of the archive to clone.
        <channel> A channel name, id or mention. The channel to clone the archive into. 
        '''
        async with ctx.typing():
            # Searches through archived channels for the provided archive
            sourcePath = None
            for guild in listdir("archives/guilds"):
                for sourceChannel in listdir(f"archives/guilds/{guild}"):
                    if isfile(
                            f"archives/guilds/{guild}/{sourceChannel}/{ID}.json"
                    ):
                        sourcePath = f"archives/guilds/{guild}/{sourceChannel}"

            # If not found
            if sourcePath is None:
                return await ctx.send(
                    "Could not find an archive with that filename.")

            messageCount = await self.fill(ctx, sourcePath, ID, channel)

            # User output message
            await ctx.send(
                f"{ctx.author.mention} Done. Created {messageCount} messages in {channel.mention}."
            )
Exemplo n.º 5
0
    async def archive(self,
                      ctx,
                      channel: commands.TextChannelConverter(),
                      limit: int = 100):
        '''
        Archives messages from the provided channel onto disk.
        Returns the ID of the archive.

        __Parameters__
        <channel> A channel name, id, or mention.
        <limit> How many messages to archive. Defaults to 100. If set to 0, archives all messages.
        '''
        filename = ""
        async with ctx.typing():
            # Feedback for the user
            userMessage = f"Archiving {limit} messages from {channel.mention}..." \
                if bool(limit) else \
                f"Archiving all messages from {channel.mention}... This may take several minutes."
            await ctx.send(userMessage)
            await ctx.trigger_typing()

            # Stores the channel data
            filename = await self.store(ctx, channel, limit)

        # Cleans up the prefix from <@id> to @name
        cleanPrefix = ctx.prefix
        cleanPrefix = f"@{self.bot.user.name} " if ctx.prefix == self.bot.user.mention + " " else cleanPrefix

        # Final user message
        formatted = f"{ctx.author.mention} Done. Archive saved with ID `{filename}`.\n" + \
            f"To clone this archive to another channel, run `{cleanPrefix}clone {filename} [channel]`.\n" + \
            f"To list other archived channels, run `{cleanPrefix}list`."
        await ctx.send(formatted)
Exemplo n.º 6
0
 async def setannouncements(self,
                            ctx,
                            channel=commands.TextChannelConverter()):
     if not isinstance(channel, discord.TextChannel):
         channel = ctx.channel
     async with sessionmaker.begin() as session:
         guild = await session.get(Guild, ctx.guild.id)
         guild.announcements_id = channel.id
     await ctx.send(
         f"The new announcements channel is now {channel.mention}!")
Exemplo n.º 7
0
    async def what_channel(ctx: utils.CustomContext, name_or_id: str):
        converter = commands.TextChannelConverter()

        try:
            channel = await converter.convert(ctx, name_or_id)
        except commands.ChannelNotFound:
            raise commands.BadArgument(
                "I couldn't find that channel! You must restart the interactive giveaway."
            )

        return channel, "channel"
Exemplo n.º 8
0
 def __init__(self, bot):
     self.bot = bot
     self.blue = discord.Color.blue()
     self.accepted_values = {
         'prefix': 'Anything',
         'public_log': 'Any text channel',
         'private_log': 'Any text channel',
         'mod_role': 'Any role',
         'muted_role': 'Any role'
     }  # text used for an embed
     self.tc_conv = commands.TextChannelConverter()
     self.role_conv = commands.RoleConverter()
Exemplo n.º 9
0
Arquivo: main.py Projeto: AJAY-OP/bot
    async def disable_check(self, ctx, check, *argv):
        '''

		Disable a check. For the entire server by default.
		-c Disable for specified channel. Uses current channel if none specified.
		Example Usage:
		``````css
		?dc repeating content check -g  // Disable spam check globally
		``````css
		?dc banned content check -c #channel  // Disable banned content check in #channel
		```
		'''
        check = check.lower()
        guild = ctx.guild
        guild_checks = await self.get_checks(ctx.guild)
        args = index_args(argv)
        extended_name = ' '.join(args[0])
        check += f' {extended_name}'
        args = args[1:]
        channel_selected = False
        checks = self.list_checks()
        check_dict = {}
        for a in checks:
            directory = f'Moderation.Message_Checks.{a}'
            f = __import__(directory, globals(), locals(), [a])

            name = f.check_name
            check_dict[name.lower()] = a
            del f
        if check in check_dict:
            for info in args:
                if info[0] == '-c':
                    if len(info) != 1:
                        channel = commands.TextChannelConverter()
                        channel = await channel.convert(ctx, info[1])
                        channel_selected = True
                    else:
                        channel = ctx.channel
                        channel_selected = True

            if channel_selected:
                overides = guild_checks['Channel Overides']
                if channel.id in overides:
                    channel_overides = overides[channel.id]
                else:
                    channel_overides = {}
                channel_overides[check_dict[check]] = False
                overides[channel.id] = channel_overides
                guild_checks['Channel Overides'] = overides
            else:
                guild_checks[check_dict[check]] = False
            await self.set_checks(guild, guild_checks)
Exemplo n.º 10
0
 async def archive(self, ctx, limit: int, channels: commands.Greedy[commands.TextChannelConverter()]):
     '''
     Archives multiple channels onto disk.
     '''
     # Creates a coroutine for each channel archival process
     coroutines = []
     for channel in channels:
         coroutine = self.bot.get_cog("Archiving:").store(ctx, channel, limit)
         coroutines.append(coroutine)
         
     IDs = await asyncio.gather(*coroutines)
     
     await ctx.send(f"{ctx.author.mention} Done. Created archives:\n{IDs}")
Exemplo n.º 11
0
    async def convert(self, ctx, argument):
        if argument == "me":
            return ctx.author

        try:
            converter = commands.MemberConverter()
            return await converter.convert(ctx, argument)

        except commands.BadArgument:
            pass

        converter = commands.TextChannelConverter()
        return await converter.convert(ctx, argument)
Exemplo n.º 12
0
 async def levelupchannel(self, ctx, *, channel):
     storage = await self.bot.cluster.find_one({"id": str(ctx.guild.id)})
     storage["levelups"] = "" if 'levelups' not in storage else storage['levelups']
     if str(channel).lower() == "none" or str(channel).lower() == "off" or str(channel).lower() == "current":
         storage.pop("levelups")
         await ctx.send(f"The level up channel has been set to the user's active channel.")
     else:
         converter = commands.TextChannelConverter()
         channel = await converter.convert(ctx, channel)
         if channel not in ctx.guild.text_channels:
             await ctx.send(f"I could not find that channel {ctx.author.mention}!")
         else:
             storage["levelups"] = channel.id
             await ctx.send(f"The level up channel for this guild has been set to {channel.mention}!")
     await self.bot.cluster.find_one_and_replace({"id": str(ctx.guild.id)}, storage)
Exemplo n.º 13
0
    async def convert(self, ctx: utils.CustomContext, argument: str):

        converters = {
            commands.TextChannelConverter(): "Text Channel",
            commands.VoiceChannelConverter(): "Voice Channel",
            commands.MemberConverter(): "Server Member",
            commands.UserConverter(): "Discord User",
            commands.PartialEmojiConverter(): "Emoji",
            utils.RoleConverter(): "Server Role",
        }

        for converter, title in converters.items():
            try:
                convert = await converter.convert(ctx, argument)
                return convert.id, title
            except Exception as e:
                continue

        raise commands.BadArgument("Couldn't find anything that matches that.")
Exemplo n.º 14
0
    async def convert(self, ctx: commands.Context, context: str):
        logging_channel = HuskyConfig.get_config() \
            .get('specialChannels', {}).get(HuskyStatics.ChannelKeys.STAFF_LOG.value, None)

        channels = []
        name = context

        if context.lower() == "all":
            for channel in ctx.guild.text_channels:
                if channel.id == logging_channel:
                    continue

                channels.append(channel)

        elif context.lower() == "public":
            if not ctx.guild.default_role.permissions.read_messages:
                raise commands.BadArgument("No public channels exist in this guild.")

            for channel in ctx.guild.text_channels:
                if channel.overwrites_for(ctx.guild.default_role).read_messages is False:
                    continue

                channels.append(channel)
        else:
            cl = context.split(',')
            converter = commands.TextChannelConverter()

            for ch_key in cl:
                channels.append(await converter.convert(ctx, ch_key.strip()))

            if len(channels) == 1:
                name = channels[0].name
            else:
                name = str(list(c.name for c in channels))

        return {"name": name, "channels": channels}
Exemplo n.º 15
0
    async def setup(self, ctx):
        def check(m):
            return m.channel == ctx.channel and m.author == ctx.author

        channel_converter = commands.TextChannelConverter()
        day_of_week_converter = DayOfWeekConverter()

        await ctx.send(
            "Okay, let's set up Bias of the Week in this server. "
            "Which channel do you want winners to post in? (Reply with a mention or an ID)"
        )

        try:
            botw_channel = await self.bot.wait_for("message",
                                                   check=check,
                                                   timeout=60.0)
            botw_channel = await channel_converter.convert(
                ctx, botw_channel.content)

            await ctx.send(
                "What channel should I send the winner announcement to? (Reply with a mention or an ID)"
            )
            nominations_channel = await self.bot.wait_for("message",
                                                          check=check,
                                                          timeout=60.0)
            nominations_channel = await channel_converter.convert(
                ctx, nominations_channel.content)

            await ctx.send(
                "Should winners be able to change the server icon and name? (yes/no)"
            )
            botw_winner_changes = await self.bot.wait_for("message",
                                                          check=check,
                                                          timeout=60.0)
            botw_winner_changes = await BoolConverter().convert(
                ctx, botw_winner_changes.content)

            await ctx.send(
                f"On what day of the week should I announce the winner in {nominations_channel.mention}?"
                f" (Possible values: {', '.join(DayOfWeekConverter.possible_values())})"
            )
            announcement_day = await self.bot.wait_for("message",
                                                       check=check,
                                                       timeout=60.0)
            announcement_day = await day_of_week_converter.convert(
                ctx, announcement_day.content)

            await ctx.send(
                "On what day of the week should I notify the winner and assign the role? Must be different from the"
                " announcement day."
                f" (Possible values: {', '.join(DayOfWeekConverter.possible_values())})"
            )
            winner_day = await self.bot.wait_for("message",
                                                 check=check,
                                                 timeout=60.0)
            winner_day = await day_of_week_converter.convert(
                ctx, winner_day.content)

            if winner_day == announcement_day:
                raise commands.BadArgument(
                    "The winner notification day must be different from the announcement day."
                )

        except asyncio.TimeoutError:
            await ctx.send(
                f"Timed out.\nPlease restart the setup using `{ctx.prefix}botw setup`."
            )
        except commands.CommandError as e:
            await ctx.send(
                f"{e}\nPlease restart the setup using `{ctx.prefix}botw setup`."
            )
        else:
            async with self.bot.Session() as session:
                botw_settings = BotwSettings(
                    _guild=ctx.guild.id,
                    _botw_channel=botw_channel.id,
                    _nominations_channel=nominations_channel.id,
                    winner_changes=botw_winner_changes,
                    announcement_day=announcement_day,
                    winner_day=winner_day,
                    enabled=True,
                )

                await session.merge(botw_settings)
                await session.commit()

            # create role
            if role := discord.utils.get(ctx.guild.roles,
                                         name=self.winner_role_name):
                # botw role exists
                pass
            else:  # need to create botw role
Exemplo n.º 16
0
def make_bot(
    settings_manager: settings.SettingsManager = None,
    api_fetcher: fetcher.ApiFetcher = None,
    command_prefix: str = "!",
    name: str = "notashark",
):
    """Factory to create bot's instance with provided settings."""
    bot = Notashark(
        settings_manager=settings_manager,
        api_fetcher=api_fetcher,
        command_prefix=command_prefix,
        name=name,
    )

    # removing default help, coz its easier to make a new, than to fix a template
    bot.remove_command("help")

    converter = commands.TextChannelConverter()

    async def update_status():
        """Update bot's status with current bot.api_fetcher.kag_servers data."""
        data = bot.api_fetcher.kag_servers
        if data and (data.players_amount > 0):
            message = f"with {data.players_amount} peasants | {bot.command_prefix}help"
        else:
            message = f"alone | {bot.command_prefix}help"

        try:
            await bot.change_presence(activity=discord.Game(name=message))
        except Exception as e:
            log.warning(f"Unable to update bot's status: {e}")

    async def update_serverlists():
        """Update serverlists on all servers that have this feature enabled."""
        for item in bot.settings_manager.storage:
            # avoiding entries without serverlist_channel_id being set
            if (
                not bot.settings_manager.storage[item]
                or not bot.settings_manager.storage[item]["serverlist_channel_id"]
            ):
                continue

            chan_id = bot.settings_manager.storage[item]["serverlist_channel_id"]

            try:
                # future reminder: serverlist_channel_id should always be int
                channel = bot.get_channel(chan_id)
                message = await channel.fetch_message(
                    bot.settings_manager.storage[item]["serverlist_message_id"]
                )
            except AttributeError:
                log.warning(
                    f"Unable to update serverlist on channel {chan_id}:"
                    f"guild {item} is unavailable"
                )
                continue
            except (discord.errors.NotFound, discord.errors.HTTPException):
                log.debug("Unable to find existing message, configuring new one")
                try:
                    channel = bot.get_channel(chan_id)
                    message = await channel.send("Gathering the data...")
                except Exception as e:
                    log.warning(
                        f"Unable to create new stats message on {item}/{chan_id}: {e}"
                    )
                    continue
                else:
                    log.debug(f"Sent placeholder serverlist msg to {item}/{channel.id}")
                    bot.settings_manager.storage[item][
                        "serverlist_message_id"
                    ] = message.id
            except Exception as e:
                # this SHOULD NOT happen, kept there as "last resort"
                log.error(
                    "Got exception while trying to edit serverlist message on"
                    f"{item}/{chan_id}: {e}"
                )
                continue

            infobox = embeds.make_servers_embed(bot.api_fetcher.get_servers())
            # Attempting to deal with issues caused by discord api being unavailable.
            try:
                await message.edit(content=None, embed=infobox)
            except Exception as e:
                log.warning(
                    f"Unable to edit serverlist on {item}/{chan_id}: {e}"
                )
                continue
            else:
                log.info(
                    f"Successfully updated serverlist on {channel.id}/{message.id}"
                )

    @bot.event
    async def on_ready():
        """Inform about bot going online and start autoupdating routine."""

        log.info(f"Running {bot.name} as {bot.user}!")
        log.debug("Launching stats autoupdater")
        update_everything.start()

    @tasks.loop(seconds=bot.api_fetcher.autoupdate_time)
    async def update_everything():
        """Update serverlists and bot's status.
        Runs each bot.api_fetcher.autoupdate_time seconds in loop.
        """
        log.debug("Updating serverlists")
        await update_serverlists()
        log.debug("Updating bot's status")
        await update_status()

    @update_everything.before_loop
    async def before_updating():
        """Routine that ensure update_everything() only runs once bot is ready."""
        await bot.wait_until_ready()

    @bot.event
    async def on_guild_available(ctx):
        """Event that triggers each time bot connects to some guild."""
        log.info(f"Connected to guild {ctx.id}")
        log.debug(f"Ensuring bot knows about guild {ctx.id}")
        bot.settings_manager.add_entry(
            guild_id=str(ctx.id),
        )

    # this is a recommended way to handle multi-part commands separated by space
    @bot.group(
        name="server",
        # This makes it possible to use this command without additional args
        invoke_without_command=True,
    )
    async def server_group(ctx):
        await ctx.channel.send(
            "This command requires you to specify which type "
            "of servers-related info you want to get. Valid "
            "options are:\n`list` - to get list of currently active "
            "servers\n`info *ip:port*` - to get detailed information "
            "of some specific server (including minimap)"
        )
        log.info(f"{ctx.author} has asked for server info, but misspelled format")
        return

    @server_group.command(name="list")
    async def get_servers(ctx):
        """Get base info about currently populated servers."""

        infobox = embeds.make_servers_embed(bot.api_fetcher.get_servers())
        await ctx.channel.send(
            content=None,
            embed=infobox,
        )
        log.info(f"{ctx.author} has asked for servers embed. Responded")

    @server_group.command(name="info")
    async def get_server(ctx, *args):
        """Get detailed info about specific kag server."""

        # #TODO: replace this with error handler. Probably
        if not args:
            await ctx.channel.send(
                "This command requires server address or ip:port. For example: "
                f"`{bot.command_prefix}server info kag://138.201.55.232:10592`"
            )
            log.info(f"{ctx.author} has asked for server info, but misspelled format")
            return

        server_address = args[0]
        # avoiding breakage on interactive uri
        if server_address.startswith("<") and server_address.endswith(">"):
            server_address = server_address[1 : (len(server_address) - 1)]
        # support for kag:// uri
        if server_address.startswith("kag://"):
            server_address = server_address[6:]

        data = embeds.make_server_embed(
            bot.api_fetcher.get_server(*server_address.split(":"))
        )
        await ctx.channel.send(
            content=None,
            file=data.attachment,
            embed=data.embed,
        )
        log.info(f"Responded {ctx.author} with server info of {server_address}.")

    @bot.command(name="set")
    async def configure(ctx, *args):
        """Configure bot's autoupdate channel for current server.
        You must have admin rights to use this functionality.
        """
        # ignoring invalid setters. I could rework this to group, but there are
        # no other configuration commands, thus there is no point rn #TODO
        if len(args) >= 3 and (args[0] == "autoupdate") and (args[1] == "channel"):
            # ensuring that message's author is admin
            if not ctx.message.author.guild_permissions.administrator:
                await ctx.channel.send(
                    "You must have admin rights on this guild to do that"
                )

            # Attempting to get ID of channel
            cid = await converter.convert(ctx, args[2])
            # ctx.guild.id must be str, coz json cant into ints in keys
            bot.settings_manager.storage[str(ctx.guild.id)][
                "serverlist_channel_id"
            ] = cid.id
            # resetting message id, in case its already been set in past
            bot.settings_manager.storage[str(ctx.guild.id)][
                "serverlist_message_id"
            ] = None
            await ctx.channel.send(f"Successfully set {cid} as channel for autoupdates")
            log.info(
                f"{ctx.author.id} tried to set {cid} as channel for "
                f"autoupdates on {ctx.guild.id}/{ctx.channel.id}. Granted"
            )

    @bot.command(name="kagstats")
    async def get_kagstats(ctx, *args):
        """Get player's kagstats profile info"""
        if not args:
            await ctx.channel.send(
                "This command requires player name or kagstats id. "
                f"For example: `{bot.command_prefix}kagstats bunnie`"
            )
            # #TODO: maybe make this follow error logging format?
            log.info(f"{ctx.author} has asked for player info, but misspelled format")
            return

        player = args[0]
        infobox = embeds.make_kagstats_embed(bot.api_fetcher.get_kagstats(player))
        await ctx.channel.send(content=None, embed=infobox)
        log.info(
            f"{ctx.author} has asked for player info of {player} on "
            f"{ctx.guild.id}/{ctx.channel.id}. Responded"
        )

    @bot.group(
        name="leaderboard",
        invoke_without_command=True,
    )
    async def leaderboard_group(ctx, *args):
        await ctx.channel.send(
            "This command requires leaderboard type. "
            f"For example: `{bot.command_prefix}leaderboard global kdr`\n"
            "Available types are the following:\n"
            " - global kdr\n - global kills\n - global archer\n"
            " - global builder\n - global knight\n - monthly archer\n"
            " - monthly builder\n - monthly knight\n"
        )
        log.info(f"{ctx.author} has asked for leaderboard, but didnt specify type")

    async def get_leaderboard(ctx, scope: str):
        """Get leaderboard of specified scope"""
        infobox = embeds.make_leaderboard_embed(bot.api_fetcher.get_leaderboard(scope))
        await ctx.channel.send(content=None, embed=infobox)
        log.info(
            f"{ctx.author} has asked for {scope} leaderboard on "
            f"{ctx.guild.id}/{ctx.channel.id}. Responded"
        )

    @leaderboard_group.command(name="global")
    async def get_global_leaderboard(ctx, *args):
        """Get top-3 players from global leaderboard of specified type"""

        if args[0] in ("kills", "kdr"):
            prefix = args[0]
        else:
            prefix = f"global_{args[0]}"

        await get_leaderboard(ctx, prefix)

    @leaderboard_group.command(name="monthly")
    async def get_monthly_leaderboard(ctx, *args):
        """Get top-3 players from monthly leaderboard of specified type"""
        await get_leaderboard(ctx, f"monthly_{args[0]}")

    @bot.command(name="help")
    async def get_help(ctx):
        # I thought about remaking it into embed, but it looks ugly this way
        await ctx.channel.send(
            f"Hello, Im {bot.name} bot and Im there to assist you with all "
            "King Arthur's Gold needs!\n\n"
            "Currently there are following custom commands available:\n"
            f"`{bot.command_prefix}server list` - will display list of active servers "
            "with their base info, aswell as their total population numbers\n"
            f"`{bot.command_prefix}server info *IP:port*` - will display detailed "
            "info of selected server, including description and in-game minimap\n"
            f"`{bot.command_prefix}kagstats *player*` - will display gameplay "
            "statistics of player with provided kagstats id or username\n"
            f"`{bot.command_prefix}leaderboard *type*` - will display top-3 players "
            "in this category of kagstats leaderboard. To get list of available parts - "
            f"just type `{bot.command_prefix}leaderboard`, without specifying anything\n"
            f"`{bot.command_prefix}set autoupdate channel #channel_id` - will set "
            f"passed channel to auto-fetch serverlist each {bot.api_fetcher.autoupdate_time} "
            "seconds. Keep in mind that you must be guild's admin to use it!\n"
            f"`{bot.command_prefix}help` - shows this message\n"
            f"`{bot.command_prefix}about` - shows general bot's info"
        )
        log.info(
            f"{ctx.author.id} has asked for help on {ctx.guild.id}/{ctx.channel.id}. Responded"
        )

    @bot.command(name="about")
    async def get_bot_description(ctx):
        infobox = embeds.make_about_embed()
        await ctx.channel.send(content=None, embed=infobox)
        log.info(f"{ctx.author} has asked for info about this bot. Responded")

    return bot
Exemplo n.º 17
0
 def __init__(self):
     self.text_channel_converter = commands.TextChannelConverter()
     self.solved = { "result" : -1, "expression" : -1 }
     self.in_loop = False
     self.available = False
Exemplo n.º 18
0
async def config(ctx):
    """ This is for admins to configure the bot's behaviour on their guild. """

    if ctx.invoked_subcommand is None:
        options = [cmd.name for cmd in config.commands]
        title = ctx.translate('guided configuration')
        description = ctx.translate('this will guid you through all configurations')

        msg = None

        while True:  # config dialog loop
            action, msg = await multiple_choice(ctx, options, title, description, message=msg)

            if action is None:
                await msg.edit(content=ctx.translate("configuration dialog closed"), embed=None)
                await msg.clear_reactions()
                break

            choice = None
            converter = None
            content = None
            if action == 'prefix':
                content = 'type the new prefix'

            elif action == 'notifications':
                content = 'which channel do you wanna use as notification channel'
                converter = commands.TextChannelConverter()

            elif action == 'role':
                content = "which role do you wanna use as support role"
                converter = commands.RoleConverter()

            elif action == 'scope':
                title = ctx.translate("choose a scope as default")
                choice = await multiple_choice(ctx, [s.value for s in enums.Scope], title, message=msg)
                choice = choice[0]
                converter = Scope()

            elif action == 'language':
                title = ctx.translate("choose the language of the server")
                choice = await multiple_choice(ctx, [l.value for l in enums.Language], title, message=msg)
                choice = choice[0]
                converter = Language()

            elif action == 'category':
                content = "which category do you wanna use for channel-tickets"
                converter = commands.CategoryChannelConverter()

            elif action == 'voice':
                content = 'which category do you wanna use for voice support'
                converter = commands.CategoryChannelConverter()

            elif action == 'log':
                content = 'which channel do you wanna use for logging'
                converter = commands.TextChannelConverter()

            elif action == 'assigning':
                text = ctx.translate("do you want to enable auto-assigning")
                conf = Confirmation(ctx)
                await conf.confirm(text)

                choice = conf.confirmed

            prefix = ctx.prefix

            if content is not None:
                await msg.clear_reactions()
                note = ctx.translate("type [pfx]abort to close this dialog").format(prefix)
                await msg.edit(content=ctx.translate(content) + note, embed=None)

                def check(message):
                    return message.author.id == ctx.author.id and message.channel.id == msg.channel.id

                try:
                    choice = await ctx.bot.wait_for('message', check=check, timeout=60)
                except asyncio.TimeoutError:
                    continue
                choice = choice.content

            if choice is None or choice == prefix + 'abort':
                await msg.edit(content=ctx.translate("configuration dialog closed"), embed=None)
                await msg.clear_reactions()
                break

            if converter is not None:
                try:
                    choice = await converter.convert(ctx, choice)  # convert string to specific Object (like Channel)
                except commands.BadArgument:
                    await ctx.send(ctx.translate("invalid input"))
                    continue

            command = ctx.bot.get_command('config ' + action)
            await ctx.invoke(command, choice)
Exemplo n.º 19
0
    def __init__(self, bot):
        self.bot = bot
        self.client = bot.client

        self.table = self.client['Giveaways']
        self.codes = self.table['codes']

        self.channel_converter = commands.TextChannelConverter()
        self.emoji_converter = commands.PartialEmojiConverter()
        ## Using partial so i can accept default emoji's as well

        self.giveaways = {}
        self.questions = (
            'Which channel would you like to set this giveaway up in?',
            'How many winners would you like for this giveaway',
            'How long will this giveaway last for? Please use the format of `<TIME>[s/m/h/d/w]`, default is set to seconds.',
            'Are there any requirements? (y/n)',
            'What is the prize for this giveaway?',
            'Would you like a custom emoji in place of the default option "🎉", if not simply respond with `no`. Else send the emoji below. Warning: If using a discord custom emoji, please ensure the bot is in the server that has the emoji.'
        )

        self.scopes = {
            ## 'message': 2,
            ## 'invite': 3,

            ## Adding the message and invite option later when I get them done.
            'guild': {
                'scope': 1,
                'aliases': ['guilds']
            },
            'server': {
                'scope': 1,
                'aliases': ['servers']
            },
            ## This is for the reverse search.
            'role': {
                'scope': 2,
                'aliases': ['roles']
            },
            'creation-date': {
                'scope': 3,
                'aliases': ['creation']
            },
            'join-date': {
                'scope': 4,
                'aliases': ['join']
            },
            'exit': {
                'scope': 5,
                'aliases': []
            },
        }

        self.aliase_to_scope = {}

        for key, value in self.scopes.items():
            for aliase in value['aliases']:
                self.aliase_to_scope.update({aliase: key})

        self.scales = {
            's': 1,
            'm': 60,
            'h': (60 * 60),
            'd': ((60 * 60) * 24),
            'w': (((60 * 60) * 24) * 7)
        }

        self.marks = {
            's': 'second(s)',
            'm': 'minutes(s)',
            'h': 'hour(s)',
            'd': 'day(s)',
            'w': 'week(s)'
        }

        self.listed_scopes = ''

        for scope in self.scopes:
            self.listed_scopes += '%s|' % scope

            for aliase in self.scopes[scope]['aliases']:
                self.listed_scopes += '%s|' % aliase

        self.listed_scopes = self.listed_scopes[:-1].split('|')

        for guild in bot.guilds:
            self.giveaways[guild.id] = {}

        bot.loop.create_task(self.load_giveaways())
Exemplo n.º 20
0
"""Helper functions for xxx bot"""

import asyncio
from random import choice

import discord
from discord.ext import commands

MEMBER_CONVERTER = commands.MemberConverter()
ROLE_CONVERTER = commands.RoleConverter()
USER_CONVERTER = commands.UserConverter()
TEXTCHANNEL_CONVERTER = commands.TextChannelConverter()


async def get_color():
    """Get a random hex color for use with embed. In case I forget this was taken from SO"""
    color = ''.join([choice('0123456789ABCDEF') for x in range(6)])
    color = int(color, 16)
    return color


async def get_parameters(raw_string: str):
    """Get parameters seperated by `|` by using string.split(). Returns list of parameters """
    parameters = raw_string.split('|')
    return parameters


async def make_request(ctx: commands.context,
                       url: str,
                       data=None,
                       headers=None):
Exemplo n.º 21
0
 async def channel_convert(self, ctx, argument):
     converter = commands.TextChannelConverter()
     channel = await converter.convert(ctx, argument)
     return channel
Exemplo n.º 22
0
    async def edittemplate(self, ctx: utils.Context, template: utils.Template):
        """Edits a template for your guild"""

        # See if they're already editing that template
        if self.template_editing_locks[ctx.guild.id].locked():
            return await ctx.send("You're already editing a template.")

        # Grab the template edit lock
        async with self.template_editing_locks[ctx.guild.id]:

            # Get the template fields
            async with self.bot.database() as db:
                await template.fetch_fields(db)
                guild_settings_rows = await db(
                    "SELECT * FROM guild_settings WHERE guild_id=$1 OR guild_id=0 ORDER BY guild_id DESC",
                    ctx.guild.id)
            guild_settings = guild_settings_rows[0]

            # Set up our initial vars so we can edit them later
            edit_message = await ctx.send("Loading...")
            messages_to_delete = []
            should_edit = True
            should_add_reactions = True

            # Start our edit loop
            while True:

                # Ask what they want to edit
                if should_edit:
                    content = "What do you want to edit - its name (1\u20e3), verification channel (2\u20e3), archive channel (3\u20e3), given role (4\u20e3), fields (5\u20e3), or max profile count (6\u20e3)?"
                    await edit_message.edit(
                        content=content,
                        embed=template.build_embed(brief=True),
                        allowed_mentions=discord.AllowedMentions(roles=False),
                    )
                    should_edit = False

                # Add reactions if there aren't any
                valid_emoji = [
                    "1\N{COMBINING ENCLOSING KEYCAP}",
                    "2\N{COMBINING ENCLOSING KEYCAP}",
                    "3\N{COMBINING ENCLOSING KEYCAP}",
                    "4\N{COMBINING ENCLOSING KEYCAP}",
                    "5\N{COMBINING ENCLOSING KEYCAP}",
                    "6\N{COMBINING ENCLOSING KEYCAP}",
                ]
                valid_emoji.append(self.TICK_EMOJI)
                if should_add_reactions:
                    for e in valid_emoji:
                        await edit_message.add_reaction(e)
                    should_add_reactions = False

                # Wait for a response
                try:
                    reaction, _ = await self.bot.wait_for(
                        "reaction_add",
                        check=lambda r, u: u.id == ctx.author.id and r.message.
                        id == edit_message.id and str(r) in valid_emoji,
                        timeout=120)
                except asyncio.TimeoutError:
                    return await ctx.send(
                        "Timed out waiting for edit response.")

                # See what they reacted with
                try:
                    available_reactions = {
                        "1\N{COMBINING ENCLOSING KEYCAP}": ('name', str),
                        "2\N{COMBINING ENCLOSING KEYCAP}":
                        ('verification_channel_id',
                         commands.TextChannelConverter()),
                        "3\N{COMBINING ENCLOSING KEYCAP}":
                        ('archive_channel_id',
                         commands.TextChannelConverter()),
                        "4\N{COMBINING ENCLOSING KEYCAP}":
                        ('role_id', commands.RoleConverter()),
                        "5\N{COMBINING ENCLOSING KEYCAP}":
                        (None, self.edit_field(ctx, template, guild_settings)),
                        "6\N{COMBINING ENCLOSING KEYCAP}":
                        ('max_profile_count', int),
                        self.TICK_EMOJI:
                        None,
                    }
                    attr, converter = available_reactions[str(reaction)]
                except TypeError:
                    break
                await edit_message.remove_reaction(reaction, ctx.author)

                # If they want to edit a field, we go through this section
                if attr is None:
                    fields_have_changed = await converter
                    if fields_have_changed is None:
                        return
                    if fields_have_changed:
                        async with self.bot.database() as db:
                            await template.fetch_fields(db)
                        should_edit = True
                    continue

                # Ask what they want to set things to
                if isinstance(converter, commands.Converter):
                    v = await ctx.send(
                        f"What do you want to set the template's **{' '.join(attr.split('_')[:-1])}** to? You can give a name, a ping, or an ID, or say `continue` to set the value to null. "
                        +
                        ("Note that any current pending profiles will _not_ be able to be approved after moving the channel"
                         if attr == 'verification_channel_id' else ''))
                else:
                    v = await ctx.send(
                        f"What do you want to set the template's **{attr.replace('_', ' ')}** to?"
                    )
                messages_to_delete.append(v)
                try:
                    value_message = await self.bot.wait_for(
                        "message",
                        check=lambda m: m.author.id == ctx.author.id and m.
                        channel.id == ctx.channel.id,
                        timeout=120)
                except asyncio.TimeoutError:
                    return await ctx.send(
                        "Timed out waiting for edit response.")
                messages_to_delete.append(value_message)

                # Convert the response
                try:
                    converted = str(
                        (await converter.convert(ctx,
                                                 value_message.content)).id)

                # The converter failed
                except commands.BadArgument:

                    # They want to set it to none
                    if value_message.content == "continue":
                        converted = None

                    # They either gave a command or just something invalid
                    else:
                        is_command, is_valid_command = utils.CommandProcessor.get_is_command(
                            value_message.content)
                        if is_command and is_valid_command:
                            converted = value_message.content
                        else:
                            await self.purge_message_list(
                                ctx.channel, messages_to_delete)
                            continue

                # It isn't a converter object
                except AttributeError:
                    try:
                        converted = converter(value_message.content)
                    except ValueError:
                        await self.purge_message_list(ctx.channel,
                                                      messages_to_delete)
                        continue

                # Delete the messages we don't need any more
                await self.purge_message_list(ctx.channel, messages_to_delete)

                # Validate if they provided a new name
                if attr == 'name':
                    async with self.bot.database() as db:
                        name_in_use = await db(
                            """SELECT * FROM template WHERE guild_id=$1 AND LOWER(name)=LOWER($2)
                            AND template_id<>$3""",
                            ctx.guild.id,
                            converted,
                            template.template_id,
                        )
                        if name_in_use:
                            continue
                if attr == 'max_profile_count':
                    if ctx.original_author_id in self.bot.owner_ids:
                        pass
                    else:
                        converted = max([
                            min([
                                converted, guild_settings['max_profile_count']
                            ]), 1
                        ])

                # Store our new shit
                setattr(template, attr, converted)
                async with self.bot.database() as db:
                    await db(
                        "UPDATE template SET {0}=$1 WHERE template_id=$2".
                        format(attr), converted, template.template_id)
                should_edit = True

        # Tell them it's done
        await ctx.send("Done editing template.")
Exemplo n.º 23
0
    async def enable(self, ctx, *features):
        """Enable features in the current channel.

        **Arguments**
        *features:* list of features to enable. Can include any of
        `['raid', 'wild', 'research', 'users', 'train', 'trade', 'clean', 'archive', 'welcome', 'meetup', 'forecast', 'rocket']`

        Raid, wild, research, train and meetup require a defined location. Use `!setlocation`
        before enabling these.
        """
        guild_id = ctx.guild.id
        settings = ctx.bot.dbi.table('guild_settings')
        insert = settings.insert
        insert.row(guild_id=guild_id, version=ctx.bot.version)
        await insert.commit(do_update=True)
        try:
            await ctx.guild.me.edit(nick='Meowth 3.0')
        except:
            pass
        channel_id = ctx.channel.id
        channel_table = self.bot.dbi.table('report_channels')
        query = channel_table.query.where(channelid=channel_id)
        data = await query.get()
        if data:
            rcrd = dict(data[0])
        else:
            rcrd = {'channelid': channel_id, 'guild_id': ctx.guild.id}
        possible_commands = [
            'raid', 'wild', 'research', 'users', 'train', 'trade', 'clean',
            'archive', 'welcome', 'meetup', 'forecast', 'rocket'
        ]
        features = [x for x in features if x in possible_commands]
        if not features:
            return await ctx.send(
                "The list of valid command groups to enable is `raid, train, research, wild, "
                "rocket, users, trade, clean, welcome, archive, meetup`.")
        location_commands = [
            'raid', 'wild', 'research', 'train', 'meetup', 'forecast', 'rocket'
        ]
        enabled_commands = []
        required_perms = {}
        me = ctx.guild.me
        perms = ctx.channel.permissions_for(me)
        for x in features:
            if x in [
                    'raid', 'wild', 'trade', 'train', 'users', 'meetup',
                    'research', 'rocket'
            ]:
                required_perms['Add Reactions'] = perms.add_reactions
                required_perms['Manage Messages'] = perms.manage_messages
                required_perms['Use External Emojis'] = perms.external_emojis
            if x == 'users':
                required_perms['Manage Roles'] = perms.manage_roles
            if x in location_commands:
                if not rcrd.get('city'):
                    await ctx.send(
                        f"You must set a location for this channel before enabling `{ctx.prefix}{x}`. Use `{ctx.prefix}setlocation`"
                    )
                    continue
            if x != 'welcome' and x != 'archive':
                rcrd[x] = True
            enabled_commands.append(x)
        if 'meetup' in enabled_commands:
            column = 'category_meetup'
            content = (
                'How do you want Meetup channels created from this channel '
                'to be categorized? You can type the name or ID of the category you want '
                'the channel to appear in.')
            await ctx.send(content)

            def check(m):
                return m.author == ctx.message.author and m.channel == ctx.channel

            while True:
                try:
                    resp = await self.bot.wait_for('message', check=check)
                except:
                    break
                converter = commands.CategoryChannelConverter()
                category = await converter.convert(ctx, resp.content)
                if category:
                    rcrd[column] = category.id
                    required_perms['Manage Channels'] = perms.manage_channels
                    break
                else:
                    await ctx.send(
                        'I could not interpret your response. Try again!')
                    continue
        if 'raid' in enabled_commands:
            raid_levels = ['1', '3', '5', '7', 'EX', 'EX Raid Gyms']
            for level in raid_levels:
                column = f'category_{level.lower()}'
                if level == 'EX Raid Gyms':
                    column = 'category_ex_gyms'
                    content = (
                        'I can categorize raids of any level that are reported at '
                        'recognized EX Raid Gyms differently from other raids of the same level. '
                        'You can type `disable` if you do not want this, or type the name or ID of '
                        'the category you want those raid channels to appear in.'
                    )
                else:
                    if level == '7':
                        level_str = 'Mega'
                    elif level == 'EX':
                        level_str = "EX"
                    else:
                        level_str = f"Level {level}"
                    content = (
                        f'How do you want {level_str} Raids reported in this '
                        'channel to be displayed? You can type `message` if you want just '
                        'a message in this channel. If you want each raid to be given its own '
                        'channel for coordination, type the name or ID of the category you '
                        'want the channel to appear in, or type `none` for an uncategorized '
                        'channel. You can also type `disable` to disallow reports of '
                        f'{level_str} Raids in this channel.')
                await ctx.send(content)

                def check(m):
                    return m.author == ctx.message.author and m.channel == ctx.channel

                while True:
                    try:
                        resp = await self.bot.wait_for('message', check=check)
                    except:
                        break
                    if resp.content.lower() == 'message':
                        rcrd[column] = 'message'
                        break
                    elif resp.content.lower() == 'disable':
                        rcrd[column] = None
                        break
                    elif resp.content.lower() == 'none':
                        rcrd[column] = 'none'
                        break
                    else:
                        converter = commands.CategoryChannelConverter()
                        category = await converter.convert(ctx, resp.content)
                        if category:
                            rcrd[column] = str(category.id)
                            required_perms[
                                'Manage Channels'] = perms.manage_channels
                            break
                        else:
                            await ctx.send(
                                'I could not interpret your response. Try again!'
                            )
                            continue
        if 'welcome' in enabled_commands:
            old_welcome_channel, message = await self.welcome_channel(ctx.guild
                                                                      )
            new_welcome_channel = None
            if old_welcome_channel:
                if old_welcome_channel == 'dm':
                    name = 'Direct Message'
                else:
                    name = old_welcome_channel.mention
                content = f"This server's current welcome channel is {name}. "
                content += "Do you want to disable that channel and enable another?"
                oldmsg = await ctx.send(content)
                payload = await ask(ctx.bot, [oldmsg],
                                    user_list=[ctx.author.id])
                if not payload:
                    await oldmsg.edit(content='Timed out!')
                elif str(payload.emoji) == '❎':
                    if old_welcome_channel == 'dm':
                        new_welcome_channel = 'dm'
                    else:
                        new_welcome_channel = str(old_welcome_channel.id)
                elif str(payload.emoji) == '✅':
                    pass
            if not new_welcome_channel:
                await ctx.send(
                    'What channel do you want to use? You can type the name or ID of a text channel, or type `dm` if you want the welcome message sent to DMs.'
                )

                def check(m):
                    return m.author == ctx.author and m.channel == ctx.channel

                while True:
                    reply = await ctx.bot.wait_for('message', check=check)
                    if reply.content.lower() == 'dm':
                        new_welcome_channel = 'dm'
                        break
                    else:
                        converter = commands.TextChannelConverter()
                        channel = await converter.convert(ctx, reply.content)
                        if channel:
                            new_welcome_channel = str(channel.id)
                            break
                        else:
                            await ctx.send(
                                "I couldn't understand your reply. Try again.")
                            continue
            if not message:
                message = "Welcome to {server}, {user}!"
            content = f"Current welcome message: {message}\n"
            content += "Do you want to change the welcome message?"
            oldmsg = await ctx.send(content)
            payload = await ask(ctx.bot, [oldmsg], user_list=[ctx.author.id])
            if not payload:
                await oldmsg.edit(content='Timed out!')
            elif str(payload.emoji) == '❎':
                newmessage = message
            elif str(payload.emoji) == '✅':
                content = (
                    "Type your welcome message below. Key: \n**{@member}** - Replace member with user name or ID\n"
                    "**{#channel}** - Replace channel with channel name or ID\n"
                    "**{&role}** - Replace role name or ID (shows as @deleted-role DM preview)\n"
                    "**{user}** - Will mention the new user\n"
                    "**{server}** - Will print your server's name\n")
                await ctx.send(content)

                def check(m):
                    return m.author == ctx.author and m.channel == ctx.channel

                while True:
                    reply = await ctx.bot.wait_for('message', check=check)
                    if len(reply.content) > 500:
                        await ctx.send(
                            "Please shorten your message to fewer than 500 characters."
                        )
                        continue
                    message, errors = do_template(reply.content, ctx.author,
                                                  ctx.guild)
                    if errors:
                        content = "The following could not be found:\n"
                        content += "\n".join(errors)
                        content += "\nPlease try again."
                        await ctx.send(content)
                        continue
                    q = await ctx.send(
                        f"Here's what you sent:\n\n{message}\n\nDoes that look right?"
                    )
                    payload = await ask(ctx.bot, [q],
                                        user_list=[ctx.author.id],
                                        timeout=None)
                    if str(payload.emoji) == '❎':
                        await ctx.send('Try again.')
                        continue
                    elif str(payload.emoji) == '✅':
                        newmessage = message
                        break
            d = {
                'guild_id': ctx.guild.id,
                'channelid': new_welcome_channel,
                'message': newmessage
            }
            table = ctx.bot.dbi.table('welcome')
            insert = table.insert.row(**d)
            await insert.commit(do_update=True)
        if 'archive' in enabled_commands:
            category, phrase_list = await self.archive_cat_phrases(ctx.guild)
            new_category = None
            new_phrase_list = []
            if category:
                content = f"This server's current archive category is {category.name}. "
                content += "Do you want to disable that category and enable another?"
                oldmsg = await ctx.send(content)
                payload = await ask(ctx.bot, [oldmsg],
                                    user_list=[ctx.author.id])
                if not payload:
                    await oldmsg.edit(content='Timed out!')
                elif str(payload.emoji) == '❎':
                    new_category = category.id
                elif str(payload.emoji) == '✅':
                    pass
            if not new_category:
                await ctx.send(
                    'What category do you want to use? You can type the name or ID of a category.'
                )

                def check(m):
                    return m.author == ctx.author and m.channel == ctx.channel

                while True:
                    reply = await ctx.bot.wait_for('message', check=check)
                    converter = commands.CategoryChannelConverter()
                    channel = await converter.convert(ctx, reply.content)
                    if channel:
                        new_category = channel.id
                        break
                    else:
                        await ctx.send(
                            "I couldn't understand your reply. Try again.")
                        continue
            content = f"Current phrase list: {phrase_list}\n"
            content += "Do you want to change the phrase list?"
            oldmsg = await ctx.send(content)
            payload = await ask(ctx.bot, [oldmsg], user_list=[ctx.author.id])
            if not payload:
                await oldmsg.edit(content='Timed out!')
            elif str(payload.emoji) == '❎':
                new_phrase_list = phrase_list
            elif str(payload.emoji) == '✅':
                content = "Type your phrase list below. I will automatically archive any temporary channel in which your phrases are said. Separate each phrase with a comma."
                await ctx.send(content)

                def check(m):
                    return m.author == ctx.author and m.channel == ctx.channel

                while True:
                    reply = await ctx.bot.wait_for('message', check=check)
                    phrases = reply.content.split(',')
                    phrases = [x.strip() for x in phrases]
                    q = await ctx.send(
                        f"Here's what you sent:\n\n`{phrases}`\n\nDoes that look right?"
                    )
                    payload = await ask(ctx.bot, [q],
                                        user_list=[ctx.author.id],
                                        timeout=None)
                    if str(payload.emoji) == '❎':
                        await ctx.send('Try again.')
                        continue
                    elif str(payload.emoji) == '✅':
                        new_phrase_list = phrases
                        break
            d = {
                'guild_id': ctx.guild.id,
                'category': new_category,
                'phrase_list': new_phrase_list
            }
            table = ctx.bot.dbi.table('archive')
            insert = table.insert.row(**d)
            await insert.commit(do_update=True)
        if 'forecast' in enabled_commands:
            g = ctx.bot.get_guild(344960572649111552)
            gm = g.get_member(ctx.author.id)
            if not gm:
                gm = await g.fetch_member(ctx.author.id)
            r = g.get_role(616734835104546826)
            if r not in gm.roles:
                content = 'Unfortunately, because of the cost of using the AccuWeather API, you must be a Meowth Patreon Super Nerd to enable in-game weather forecasts. Visit www.patreon.com/meowthbot to become a Patron!'
                await ctx.send(content)
            else:
                d = {
                    'guild_id': ctx.guild.id,
                    'patron_id': ctx.author.id,
                    'enabled': True
                }
                table = ctx.bot.dbi.table('forecast_config')
                insert = table.insert.row(**d)
                await insert.commit(do_update=True)
                content = f'Forecasts have been enabled in this server! Please note that it may take up to eight hours for this to take effect. You can use `{ctx.prefix}forecast` in any reporting channel to check the forecast, and you can use `{ctx.prefix}weather` in raid channels at known Gyms to improve the forecast.'
                await ctx.send(content)
        if not all(required_perms.values()):
            missing_perms = [
                x for x in required_perms if not required_perms[x]
            ]
            while True:
                content = "I am missing the following required permissions in this channel. Please respond with `done` when you have granted me those permissions or `cancel` to cancel.\n\n"
                content += "\n".join(missing_perms)
                await ctx.send(content)

                def check(m):
                    return m.author == ctx.message.author and m.channel == ctx.channel

                resp = await self.bot.wait_for('message', check=check)
                if resp.content.lower() == 'cancel':
                    return await ctx.send("Configuration canceled.")
                elif resp.content.lower() == 'done':
                    channel = resp.channel
                    new_perms = channel.permissions_for(me)
                    req_perms = {x: None for x in required_perms}
                    for x in req_perms:
                        if x == 'Manage Channels':
                            req_perms[x] = new_perms.manage_channels
                        elif x == 'Manage Roles':
                            req_perms[x] = new_perms.manage_roles
                        elif x == 'Manage Messages':
                            req_perms[x] = new_perms.manage_messages
                        elif x == 'Use External Emojis':
                            req_perms[x] = new_perms.external_emojis
                        elif x == 'Add Reactions':
                            req_perms[x] = new_perms.add_reactions
                    if not all(req_perms.values()):
                        missing_perms = [
                            x for x in req_perms if not req_perms[x]
                        ]
                        continue
                    else:
                        await ctx.send('Thanks for fixing those!')
                        break
        insert = channel_table.insert
        insert.row(**rcrd)
        await insert.commit(do_update=True)
        return await ctx.send(
            f'The following commands have been enabled in this channel: `{", ".join(enabled_commands)}`'
        )
Exemplo n.º 24
0
    async def start(self, ctx, sponsor: discord.Member = None, time: str = None, winners: int = -1, channel: discord.TextChannel = None):
        """Start a giveaway. Use `!giveaway start` and follow the prompts, or see the example.

        Example Use:
        ------------
        !giveaway start (You will be prompted for all info)
        !giveaway start @habibi test#1531 30s 1 #bot-commands

        Parameters
        ----------
        sponsor : discord.Member
            Who sponsored the giveaway
        time : str, optional
            When to end, by default None
        winners : int
            How many winners
        channel : discord.TextChannel, optional
            Channel to post giveway in

        Raises
        ------
        commands.BadArgument
            [description]
        """

        prompts = {
            'name': {
                'convertor': str,
                'prompt': "Enter a name for the giveaway (or type cancel to cancel)"
                },
            'sponsor': {
                'convertor': commands.MemberConverter().convert,
                'prompt': "Enter the sponsor's user ID (or type cancel to cancel)"
                },
            'time': {
                'convertor': pytimeparse.parse,
                'prompt': "Enter the time until the giveaway ends (or type cancel to cancel)"
                },
            'winners': {
                'convertor': int,
                'prompt': "Enter the amount of winners for the giveaway (or type cancel to cancel)"
                },
            'channel': {
                'convertor': commands.TextChannelConverter().convert,
                'prompt': "Mention the channel to post the giveaway in (or type cancel to cancel)"
                }
        }

        responses = {
            'name': None,
            'sponsor': sponsor,
            'time': pytimeparse.parse(time) if time is not None else None,
            'winners': None if winners < 1 else winners,
            'channel': channel
        }

        for response in responses:
            if responses[response] is None:
                res = await self.prompt(ctx=ctx, data=prompts[response], _type=response)
                if res is None:
                    raise commands.BadArgument("Command cancelled.")
                responses[response] = res

        now = datetime.datetime.now()
        delta = responses['time']
        end_time = now + datetime.timedelta(seconds=delta)

        embed = discord.Embed(title=responses['name'])
        embed.description = f"Hosted by {responses['sponsor'].mention}\n{responses['winners']} {'winner' if responses['winners'] == 1 else 'winners'}"
        embed.timestamp = end_time
        embed.set_footer(text="Ends")

        message = await channel.send(embed=embed)
        await message.add_reaction("✅")

        await ctx.message.delete()

        giveaway = GiveawayDB(_id=message.id, channel=responses['channel'].id, name=responses['name'], winners=responses['winners'])
        giveaway.save()

        await ctx.send(f"Giveaway started!", embed=embed, delete_after=10)

        self.bot.settings.tasks.schedule_end_giveaway(channel_id=channel.id, message_id=message.id, date=end_time, winners=responses['winners'])
Exemplo n.º 25
0
 def __init__(self, bot):
     self.in_loop = False
     self.bot = bot
     self.text_channel_converter = commands.TextChannelConverter()
     self.cb_d = CatbotDatabase()
Exemplo n.º 26
0
 async def modmail(self,
                   ctx,
                   condition=None,
                   id: Optional[int] = None,
                   *,
                   extra=None):
     condition = condition.lower() if condition is not None else condition
     storage = await self.bot.cluster.find_one({"id": str(ctx.guild.id)})
     if "modmail" not in storage:
         storage['modmail'] = {
             'message':
             "Thank you for contacting the staff! We'll get back to you as soon as we can!",
             'users': {}
         }
     if condition == "setup" or condition == "help" or condition is None:
         embed = discord.Embed(
             title=
             f"Welcome to Hurb ModMail setup! We'll make this quick for you."
         )
         embed.add_field(
             name=f"1. Set a channel.",
             value=
             f"The first step is to set a channel to send modmail messages to. Use the `%modmail channel <channel>` command to do this.\nExample: `%modmail channel #modmail-messages`",
             inline=False)
         embed.add_field(
             name=f"2. Set a message.",
             value=
             f"The next step is to set a message for me to send to the users in response for DMing me. Use the `%modmail message <message>` command to do this.\nExample: `%modmail message Thank you for contacting the mods! We'll get back to you soon!",
             inline=False)
         embed.add_field(
             name=f"3. That's it! Once you've done that, you're all set up!",
             value=
             f"Use the `%modmail reply <complaint id>` command to reply to modmail messages.",
             inline=False)
         await ctx.send(embed=embed)
     elif condition == "channel":
         if extra is None:
             await ctx.send(
                 f"The current ModMail channel is {ctx.guild.get_channel(int(storage['modmail']['channel']))}"
             )
         else:
             converter = commands.TextChannelConverter()
             channel = await converter.convert(ctx, extra)
             storage['modmail']['channel'] = channel.id
             await ctx.send(embed=discord.Embed(
                 description=
                 f"ModMail messages will now be sent to {channel.mention}.",
                 color=discord.Color.green()))
     elif condition == "message":
         if extra is None:
             await ctx.send(embed=discord.Embed(
                 description=
                 f"You need to specify a message {ctx.author.mention}!",
                 color=discord.Color.red()))
         storage['modmail']['message'] = extra
         await ctx.send(embed=discord.Embed(
             description=
             f"Whenever members DM me, they will receive this message:\n{extra}",
             color=discord.Color.green()))
     elif condition == "reply" or condition == "respond":
         if str(id) not in storage['modmail']['users']:
             await ctx.send(embed=discord.Embed(
                 description=
                 f"There is no ModMail message with the ID of {id} {ctx.author.mention}!",
                 color=discord.Color.red()))
             return
         member = await ctx.guild.fetch_member(int(id))
         await member.send(f"**Mods:** {extra}")
         await ctx.message.add_reaction(
             self.bot.get_emoji(769340776340783115))
         storage['modmail']['users'][str(member.id)]['responded'] = "True"
     elif condition == "close":
         if str(id) not in storage['modmail']['users']:
             await ctx.send(embed=discord.Embed(
                 description=
                 f"There is no ModMail message with the ID of {id} {ctx.author.mention}!",
                 color=discord.Color.red()))
             return
         member = await ctx.guild.fetch_member(int(id))
         await member.send(
             f"Your ModMail message with {ctx.guild} has been closed. If you would like to start a new ModMail message, just send me another message."
         )
         await ctx.send(embed=discord.Embed(
             description=f"{member}'s ModMail message has been closed."))
         storage['modmail']['users'].pop(str(member.id))
     print(storage)
     results = await self.bot.cluster.find_one_and_replace(
         {"id": str(ctx.guild.id)}, storage)
     print(results)
     storage = await self.bot.cluster.find_one({"id": str(ctx.guild.id)})
     print(storage)
Exemplo n.º 27
0
 def __init__(self):
     self.text_channel_converter = commands.TextChannelConverter()
Exemplo n.º 28
0
    async def _interactive_setup(self, context):

        user_obj, _ = get_user_obj(context.author)
        guild_obj, _ = get_guild_obj(context.guild)

        ga_data = {
            'creator': user_obj,
            'guild': guild_obj,
        }

        # step 1/4: Ask for prize name
        await context.say_as_embed(
            title=
            'What is the **prize** for this giveaway? (Max: 200 characters)', )

        response = await validate_input(context, inputs=None, max_length=200)
        if response is False:
            return

        ga_data['prize'] = discord.utils.escape_markdown(response.content)

        # step 2/4: Ask for number of winners
        await context.say_as_embed(
            title=
            'How many winners are there for this giveaway? (Max: 20 winners)')

        response = await validate_input(context,
                                        inputs=[str(n) for n in range(1, 21)])
        if response is False:
            return

        ga_data['winner_count'] = int(response.content)

        # step 3/4: Ask for end date time of the giveaway
        await context.say_as_embed(
            title='When will this giveaway end?',
            description='Enter as you want and I will try to understand you.\n'
            'Default Timezone (when not specified): `UTC`\n\n'
            'Example:\n'
            '• in 10 hours and 30 minutes\n'
            '• 10AM June 24th, 2019 PDT')

        while True:

            response = await validate_input(context, inputs=None)
            if response is False:
                return

            async with context.channel.typing():
                parsed_dt = dateparser.parse(
                    response.content,
                    settings={'RETURN_AS_TIMEZONE_AWARE': True})

            if not parsed_dt:
                await context.say_as_embed(
                    'Unable to convert the date time provided. Please enter again.',
                    delete_after=5,
                    color='error')
                continue

            if parsed_dt < datetime.now(timezone.utc):
                await context.say_as_embed(
                    'The time you entered is already in the past! Please try again.\n'
                    f'Time entered: `{parsed_dt:%H:%M:%S %A %B %d, %Y (%Z)}`',
                    delete_after=5 * 2,
                    color='error')
                continue

            ga_data['ending_at'] = parsed_dt
            ga_data[
                'ended_time_str'] = f'{parsed_dt:%H:%M:%S %A %B %d, %Y (%Z)}'
            break

        # step 4/4: Ask for a channel to post the giveaway message
        await context.say_as_embed(
            title='Finally, where should the giveaway message be posted?')

        text_channel_converter = commands.TextChannelConverter()
        ga_channel = None
        while True:
            response = await validate_input(context, inputs=None)
            if response is False:
                return

            try:
                channel = await text_channel_converter.convert(
                    context, response.content)
            except commands.BadArgument as e:
                await context.say_as_embed(f'{str(e)} Please try again.',
                                           delete_after=5,
                                           color='error')
                continue

            if channel.guild != context.guild:
                await context.say_as_embed(
                    'The channel must be in this server! Please try again.',
                    delete_after=5,
                    color='error')
                continue

            bot_member = context.guild.get_member(context.bot.user.id)
            if not bot_member:
                print(
                    f'Bot is not a member of guild {context.guild.name} (ID: {context.guild.id}).'
                )
                return

            perms = channel.permissions_for(bot_member)
            if perms.send_messages is False or perms.add_reactions is False:
                await context.say_as_embed(
                    f'I can\'t send messages or add reactions (missing permissions) in {channel.mention}! '
                    f'Please try again.',
                    delete_after=5,
                    color='error')
                continue

            ga_channel = channel
            ga_data['channel_id'] = channel.id

            break

        if not ga_channel:
            return

        # finally, ask for information confirmation
        embed = discord.Embed(
            title='Giveaway Creation Confirmation',
            description='Below is the Giveaway info you entered:\n'
            f'• Prize: **{ga_data["prize"]}**\n'
            f'• Winners: **{ga_data["winner_count"]}**\n'
            f'• End at: **{ga_data["ended_time_str"]}**\n'
            f'• Channel: {ga_channel.mention}\n',
            color=settings.EMBED_DEFAULT_COLOR)
        embed.add_field(name='Proceed to creation?', value='Y/N')

        await context.send(embed=embed)

        response = await validate_input(context,
                                        inputs=['y', 'yes', 'n', 'no'])
        if response is False:
            return

        # create the giveaway based on collected user inputs
        ga_obj = Giveaway.objects.create(**ga_data)

        # send the giveaway embedded message
        try:
            ga_message = await ga_channel.send(embed=ga_obj.embed)
        except discord.HTTPException:
            ga_obj.delete()
            await context.say_as_embed(
                'Sending message failed. Please try again.', color='error')
            return

        # get the message ID and save to database
        ga_obj.message_id = ga_message.id
        ga_obj.save()

        # get the emoji
        emoji = self.bot.get_emoji(settings.REACT_EMOJI_ID)
        if not emoji:
            ga_obj.delete()
            await context.say_as_embed(
                f'Emoji with ID {settings.REACT_EMOJI_ID} does not exist.',
                color='error')
            return

        # try to react with the customized emoji
        try:
            await ga_message.add_reaction(emoji)
        except discord.HTTPException:
            ga_obj.delete()
            await context.say_as_embed(
                f'Reacting with emoji {str(emoji)} failed.', color='error')
            return

        # after all those checks, it's finally here, phew!
        await context.say_as_embed(
            title='Giveaway Created!',
            description=
            f'You can [click here]({ga_obj.message_jump_url}) to go to the Giveaway message.\n'
            f'Giveaway ID: `{ga_obj.id}`',
            color='success')
Exemplo n.º 29
0
type_to_converter = {
    # Builtins
    bool: commands.core._convert_to_bool,
    int: built_in_converter(int),
    str: str,
    # Discord objects
    Role: commands.RoleConverter(),
    User: commands.UserConverter(),
    Member: commands.MemberConverter(),
    Color: commands.ColorConverter(),
    Invite: commands.InviteConverter(),
    Emoji: commands.EmojiConverter(),
    Message: commands.MessageConverter(),
    PartialEmoji: commands.PartialEmojiConverter(),
    TextChannel: commands.TextChannelConverter(),
    VoiceChannel: commands.VoiceChannelConverter(),
    CategoryChannel: commands.CategoryChannelConverter(),
}

_to_str = {
    Message: lambda m: f"Message with id {m.id} in {m.channel.mention}",
}


def to_str(argument: UserInputable) -> str:
    _type = type(argument)
    if issubclass(_type, Enum):
        return argument.name.capitalize().replace("_", " ")
    if _type is Reaction:
        if argument.custom_emoji:
Exemplo n.º 30
0
    async def edittemplate(self, ctx: utils.Context,
                           template: localutils.Template):
        """
        Edits a template for your guild.
        """

        # See if they're already editing that template
        if self.template_editing_locks[ctx.guild.id].locked():
            return await ctx.send("You're already editing a template.")

        # See if they're bot support
        is_bot_support = await self.user_is_bot_support(ctx)

        # Grab the template edit lock
        async with self.template_editing_locks[ctx.guild.id]:

            # Get the template fields
            async with self.bot.database() as db:
                await template.fetch_fields(db)
                guild_settings_rows = await db(
                    """SELECT * FROM guild_settings WHERE guild_id=$1 OR guild_id=0 ORDER BY guild_id DESC""",
                    ctx.guild.id,
                )
                perks = await localutils.get_perks_for_guild(db, ctx.guild.id)
            ctx.guild_perks = perks
            guild_settings = guild_settings_rows[0]

            # Set up our initial vars so we can edit them later
            template_display_edit_message = await ctx.send(
                "Loading template...")  # The message with the template
            components = utils.MessageComponents.add_buttons_with_rows(
                utils.Button("Template name",
                             custom_id="1\N{COMBINING ENCLOSING KEYCAP}"),
                utils.Button("Profile verification channel",
                             custom_id="2\N{COMBINING ENCLOSING KEYCAP}"),
                utils.Button("Profile archive channel",
                             custom_id="3\N{COMBINING ENCLOSING KEYCAP}"),
                utils.Button("Profile completion role",
                             custom_id="4\N{COMBINING ENCLOSING KEYCAP}"),
                utils.Button("Template fields",
                             custom_id="5\N{COMBINING ENCLOSING KEYCAP}"),
                utils.Button("Profile count per user",
                             custom_id="6\N{COMBINING ENCLOSING KEYCAP}"),
                utils.Button("Done",
                             custom_id="DONE",
                             style=utils.ButtonStyle.SUCCESS),
            )
            template_options_edit_message = None
            should_edit = True  # Whether or not the template display message should be edited

            # Start our edit loop
            while True:

                # Ask what they want to edit
                if should_edit:
                    try:
                        await template_display_edit_message.edit(
                            content=None,
                            embed=template.build_embed(self.bot, brief=True),
                            allowed_mentions=discord.AllowedMentions(
                                roles=False),
                        )
                    except discord.HTTPException:
                        return
                    should_edit = False

                # Wait for a response from the user
                try:
                    if template_options_edit_message:
                        await template_options_edit_message.edit(
                            components=components.enable_components())
                    else:
                        template_options_edit_message = await ctx.send(
                            "What would you like to edit?",
                            components=components)
                    payload = await template_options_edit_message.wait_for_button_click(
                        check=lambda p: p.user.id == ctx.author.id,
                        timeout=120)
                    await payload.ack()
                    reaction = payload.component.custom_id
                except asyncio.TimeoutError:
                    try:
                        await template_options_edit_message.edit(
                            content="Timed out waiting for edit response.",
                            components=None)
                    except discord.HTTPException:
                        pass
                    return

                # See what they reacted with
                try:
                    available_reactions = {
                        "1\N{COMBINING ENCLOSING KEYCAP}": ("name", str),
                        "2\N{COMBINING ENCLOSING KEYCAP}":
                        ("verification_channel_id",
                         commands.TextChannelConverter()),
                        "3\N{COMBINING ENCLOSING KEYCAP}":
                        ("archive_channel_id",
                         commands.TextChannelConverter()),
                        "4\N{COMBINING ENCLOSING KEYCAP}":
                        ("role_id", commands.RoleConverter()),
                        "5\N{COMBINING ENCLOSING KEYCAP}":
                        (None,
                         self.edit_field(ctx, template, guild_settings,
                                         is_bot_support)),
                        "6\N{COMBINING ENCLOSING KEYCAP}":
                        ("max_profile_count", int),
                        "DONE":
                        None,
                    }
                    attr, converter = available_reactions[reaction]
                except TypeError:
                    break  # They're done

                # Disable the components
                await template_options_edit_message.edit(
                    components=components.disable_components())

                # If they want to edit a field, we go through this section
                if attr is None:

                    # Let them change the fields
                    fields_have_changed = await converter

                    # If the fields have changed then we should update the message
                    if fields_have_changed:
                        async with self.bot.database() as db:
                            await template.fetch_fields(db)
                        should_edit = True

                    # And we're done with this round, so continue upwards
                    continue

                # Change the given attribute
                should_edit = await self.change_template_attribute(
                    ctx, template, guild_settings, is_bot_support, attr,
                    converter)

        # Tell them it's done
        await template_options_edit_message.edit(
            content=
            (f"Finished editing template. Users can create profiles with `{ctx.clean_prefix}{template.name.lower()} set`, "
             f"edit with `{ctx.clean_prefix}{template.name.lower()} edit`, and show them with "
             f"`{ctx.clean_prefix}{template.name.lower()} get`."),
            components=None,
        )