예제 #1
0
    async def logset_channel(self,
                             ctx: commands.Context,
                             module: str,
                             channel: discord.TextChannel = None):
        """Set the log channel for a module

        Passing no log channel effectively acts as disabling the module
        """
        module = await retrieve_module(ctx, module)
        if channel and not channel.permissions_for(ctx.guild.me).send_messages:
            await ctx.send(
                warning(i18n("I'm not able to send messages in that channel")))
            return
        await module.module_config.set_raw("_log_channel",
                                           value=getattr(channel, "id", None))
        if channel:
            await ctx.send(
                tick(
                    i18n("Module **{module}** will now log to {channel}").
                    format(module=module.friendly_name,
                           channel=channel.mention)))
        else:
            await ctx.send(
                tick(
                    i18n(
                        "The log channel for module **{module}** has been cleared"
                    ).format(module=module.friendly_name)))
예제 #2
0
    async def requirerole(self, ctx: commands.Context, *roles: RoleTuple):
        """Require one of any specific roles to use the bot in the current guild

        To require a member to __not__ have one or more roles, you can use
        `~` before the role name to treat it as a blacklisted role.
        If a role name has `~` at the start of it's name, you can escape
        it with a backslash (`\`) character. Blacklisted roles override
        any possible whitelisted roles a member may have.

        Role names are case sensitive. If a role has spaces in it's name, wrap it in quotes.
        Passing no roles removes any currently set role requirements.

        The guild owner and members with the Administrator permission
        always bypass these requirements, regardless of roles."""
        seen = SeenSet()
        roles = tuple(
            (k, v) for k, v in roles
            if seen.mark_seen(k))  # type: Tuple[Tuple[discord.Role, bool]]
        whitelist = tuple(r for r, v in roles
                          if v)  # type: Tuple[discord.Role]
        blacklist = tuple(r for r, v in roles
                          if not v)  # type: Tuple[discord.Role]

        if ctx.guild.default_role in roles:
            await ctx.send(
                warning(
                    _("I can't set a role requirement with the guild's default role - "
                      "if you'd like to clear your current role requirements, "
                      "you can execute this command with no arguments to do so."
                      )))
            return

        await self.config.guild(ctx.guild).roles.set({
            "whitelist": [x.id for x in whitelist],
            "blacklist": [x.id for x in blacklist]
        })
        if not roles:
            await ctx.send(tick(_("Cleared currently set role requirements.")))
            return

        whitelist = ", ".join(
            escape(str(x), mass_mentions=True, formatting=True)
            for x in whitelist)
        blacklist = ", ".join(
            escape(str(x), mass_mentions=True, formatting=True)
            for x in blacklist)

        msg = _(
            "A member will now need to pass the following checks to use my commands:\n\n"
        )
        if whitelist:
            msg += _("**Any of the following roles:**\n{roles}").format(
                roles=whitelist)
        if whitelist and blacklist:
            msg += "\n\n"
        if blacklist:
            msg += _("**None of the following roles:**\n{roles}").format(
                roles=blacklist)

        await ctx.send(tick(msg))
예제 #3
0
    async def _add_remove(self,
                          ctx: commands.Context,
                          role: discord.Role,
                          *,
                          rm: bool = False):
        if role.is_default():
            raise commands.BadArgument(
                "cannot make a server's default role mentionable")
        async with self.config.guild(ctx.guild).roles() as roles:
            if rm is False:
                if role.id in roles:
                    await ctx.send(
                        warning(_("That role is already mentionable")))
                    return
                roles.append(role.id)

            else:
                if role.id not in roles:
                    await ctx.send(
                        warning(_("That role is not currently mentionable")))
                    return
                roles.remove(role.id)

            await ctx.send(
                escape(
                    tick(
                        _("`{}` is now allowed to be mentioned").format(role.
                                                                        name)),
                    mass_mentions=True,
                ) if rm is False else escape(
                    tick(
                        _("`{}` is no longer allowed to be mentioned").
                        format(role.name)),
                    mass_mentions=True,
                ))
예제 #4
0
    async def cogwhitelist_remove(self, ctx: commands.Context, cog: str, guild_id: int = None):
        """Removes a cog or guild from the list of whitelisted cogs/guilds

        If a guild ID is specified, it's removed from the specified cogs' list of allowed guilds
        """
        cog = cog.lower()
        proper_name = cog_name(self.bot, cog) or cog
        async with self.config.cogs() as cogs:
            if cog not in cogs:
                return await fmt(
                    ctx,
                    warning(_("**{cog}** doesn't currently require a whitelist to use")),
                    cog=proper_name,
                )
            if guild_id:
                if guild_id not in cogs[cog]:
                    return await fmt(
                        ctx,
                        warning(_("That guild isn't allowed to use **{cog}**")),
                        cog=proper_name,
                    )
                cogs[cog].remove(guild_id)
                await fmt(
                    ctx,
                    tick(_("That guild is no longer allowed to use **{cog}**.")),
                    cog=proper_name,
                )
            else:
                cogs.pop(cog)
                await fmt(
                    ctx, tick(_("**{cog}** no longer requires a whitelist to use")), cog=proper_name
                )
예제 #5
0
 async def starboard_channel(self,
                             ctx: Context,
                             channel: discord.TextChannel = None):
     """Set or clear the server's starboard channel"""
     if channel and channel.guild.id != ctx.guild.id:
         await ctx.send(error(i18n("That channel isn't in this server")))
         return
     await ctx.starboard.channel.set(getattr(channel, "id", None))
     if channel is None:
         await ctx.send(tick(i18n("Cleared the current starboard channel")))
     else:
         await ctx.send(
             tick(
                 i18n("Set the starboard channel to {}").format(
                     channel.mention)))
예제 #6
0
 async def change_creator(self):
     attribute_to = await self._prompt_user_input(
         i18n("Which user would you like to make the new quote creator?"),
         converter=commands.MemberConverter,
     )
     self.quote.message_author = attribute_to
     await self.send(tick(i18n("Attributed quote to **{}**.").format(str(attribute_to))))
예제 #7
0
    async def rndactivity_delay(
        self,
        ctx: commands.Context,
        *,
        duration: FutureTime.converter(min_duration=_min_duration, strict=True, max_duration=None)
    ):
        """Set the amount of time required to pass to change the bot's playing status

        Duration can be formatted in any of the following ways:
        • `5m`
        • `1h3.5m`
        • `5 minutes`
        • `1 hour 3.5 minutes`

        Minimum duration between changes is 5 minutes. Default delay is every 10 minutes.
        """
        await self.config.delay.set(duration.total_seconds())
        await fmt(
            ctx,
            tick(
                _(
                    "Set time between status changes to {duration}.\nThis change will take effect "
                    "after the next status change."
                )
            ),
            duration=duration.format(),
        )
예제 #8
0
    async def stars_unignore(self,
                             ctx: Context,
                             name: str,
                             *,
                             reason: str = None):
        """Remove a channel or member from the server's ignore list

        `reason` is only used if unignoring a member
        """
        item = await resolve_any(ctx, name, commands.TextChannelConverter,
                                 commands.MemberConverter)

        if isinstance(item, discord.Member) and not await hierarchy_allows(
                self.bot, ctx.author, item):
            await ctx.send(
                error(
                    i18n(
                        "You aren't allowed to remove that member from the ignore list"
                    )))
            return
        elif (isinstance(item, discord.TextChannel)
              and item == await ctx.starboard.resolve_starboard()):
            await ctx.send(
                warning(
                    i18n(
                        "The starboard channel is always ignored and cannot be manually "
                        "ignored nor unignored.")))
            return

        if not await ctx.starboard.is_ignored(item):
            await ctx.send(
                warning(
                    i18n(
                        "That user is not already ignored from using this server's starboard"
                    ) if isinstance(item, discord.Member) else i18n(
                        "That channel is not already being ignored")))
            return

        await ctx.starboard.unignore(item)
        await ctx.send(
            tick(
                i18n("**{}** is no longer ignored from this server's starboard"
                     )).format(item))

        if isinstance(item, discord.Member):
            try:
                await modlog.create_case(
                    bot=self.bot,
                    guild=ctx.guild,
                    created_at=ctx.message.created_at,
                    action_type="starboardunblock",
                    user=item,
                    moderator=ctx.author,
                    reason=reason,
                    until=None,
                    channel=None,
                )
            except RuntimeError:
                pass
예제 #9
0
 async def stars_unhide(self, ctx: Context, message: StarboardMessage):
     """Unhide a previously hidden message"""
     if message.hidden is False:
         return await ctx.send(
             error(i18n("That message hasn't been hidden")))
     message.hidden = False
     await ctx.send(
         tick(
             i18n("The message sent by **{}** is no longer hidden.").format(
                 message.author)))
예제 #10
0
 async def stars_hide(self, ctx: Context, message: StarboardMessage):
     """Hide a message from the starboard"""
     if message.hidden:
         return await ctx.send(error(i18n("That message is already hidden"))
                               )
     message.hidden = True
     await ctx.send(
         tick(
             i18n("The message sent by **{}** is now hidden.").format(
                 message.author)))
예제 #11
0
    async def starboard_selfstar(self, ctx: Context, toggle: bool = None):
        """Toggles if members can star their own messages

        Member statistics do not respect this setting, and always ignore self-stars.
        """
        toggle = (
            not await ctx.starboard.selfstar()) if toggle is None else toggle
        await ctx.starboard.selfstar.set(toggle)
        await ctx.send(
            tick(
                i18n("Members can now star their own messages") if toggle else
                i18n("Members can no longer star their own messages")))
예제 #12
0
 async def logset_reset(self, ctx: commands.Context):
     """Reset the server's log settings"""
     if await confirm(
             ctx,
             content=warning(
                 i18n(
                     "Are you sure you want to reset this server's log settings?"
                 ))):
         await config.guild(ctx.guild).clear()
         await ctx.send(tick(i18n("Server log settings have been reset.")))
     else:
         await ctx.send(i18n("Okay then."))
예제 #13
0
 async def delete_quote(self):
     if await confirm(
         self.ctx,
         content=warning(
             i18n(
                 "Are you sure you want to delete this quote?\n\n"
                 "Unless you have a time machine, **this action is irreversible!**"
             )
         ),
     ):
         await self.quote.delete()
         await self.send(tick(i18n("Quote deleted.")))
         raise StopLoop
예제 #14
0
    async def starboardset_v2_import(self, ctx: Context, mongo_uri: str):
        """Import Red v2 instance data

        Please note that this is not officially supported, and this import tool
        is provided as-is.

        Only messages are imported currently; server settings are not imported,
        and must be setup again.

        In most cases, `mongodb://localhost:27017` will work just fine
        if you're importing a local v2 instance.
        """
        if not await confirm(
                ctx,
                timeout=90.0,
                content=i18n(
                    "**PLEASE READ THIS! UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T!**"
                    "\n"
                    "Importing from v2 instances is not officially supported, due to the vast"
                    " differences in backend data storage schemas. This command is provided as-is,"
                    " with no guarantee of maintenance nor stability."
                    "\n\n"
                    "Server settings will not be imported and must be setup again."
                    "\n"
                    "Starred messages data will be imported, but if a message is present in"
                    " my current data set, **it will be overwritten** with the imported data."
                    "\n\n\n"
                    "Please react with \N{WHITE HEAVY CHECK MARK} to confirm that you wish to continue."
                ),
        ):
            await ctx.send(i18n("Import cancelled."), delete_after=30)
            return

        tmp = await ctx.send(
            i18n("Importing data... (this could take a while)"))
        try:
            async with ctx.typing():
                await v2_migration.import_data(self.bot, mongo_uri)
        except v2_migration.NoMotorError:
            await fmt(
                ctx,
                error(
                    i18n(
                        "Motor is not installed; cannot import v2 data.\n\n"
                        "Please do `{prefix}pipinstall motor` and re-attempt the import."
                    )),
            )
        else:
            await ctx.send(tick(i18n("Imported successfully.")))
        finally:
            await tmp.delete()
예제 #15
0
 async def _command(self,
                    ctx: commands.Context,
                    toggle: bool = None):  # noqa
     if toggle is None:
         # noinspection PyTypeChecker
         toggle = not await DummyModule().config.guild(ctx.guild
                                                       ).ignore.guild()
     # noinspection PyTypeChecker
     await DummyModule().config.guild(ctx.guild
                                      ).ignore.guild.set(toggle)
     await ctx.send(
         tick(
             i18n("Now ignoring the current server") if toggle else
             i18n("No longer ignoring the current server")))
예제 #16
0
 async def logset_module(self, ctx: commands.Context, module: str,
                         *settings: str):
     """Get or set a module's settings"""
     module = await retrieve_module(ctx, module)
     if not settings:
         await ctx.send(embed=await module.config_embed())
     else:
         await module.toggle_options(*settings)
         await ctx.send(
             content=tick(
                 i18n("Updated settings for module **{}**").format(
                     module.friendly_name)),
             embed=await module.config_embed(),
         )
예제 #17
0
    async def timedrole_add(
            self, ctx: commands.Context, member: discord.Member,
            duration: FutureTime.converter(strict=True, min_duration=2 * 60),
            *roles: discord.Role):
        """Add one or more roles to a user for a set amount of time.

        You can give a user up to 10 roles at once - this doesn't limit the total amount of
        timed roles a member can have, however.

        Examples for duration: `5d`, `1mo`, `1y2mo3w4d5m6s`

        Abbreviations: `s` for seconds, `m` for minutes, `h` for hours, `d` for days, `w` for weeks,
        `mo` for months, `y` for years. Any longer abbreviation is accepted. `m` assumes
        minutes instead of months.

        One month is counted as 30 days, and one year is counted as 365 days.
        All invalid abbreviations are ignored.

        Minimum duration for a timed role is two minutes.

        Any roles that are above the top role of either the command issuer
        or the bot are silently filtered out.
        """
        roles = [
            x for x in roles if not any([
                x in member.roles, x >= ctx.author.top_role,
                x >= ctx.me.top_role
            ])
        ]
        if not roles or len(roles) > 10:
            await ctx.send_help()
            return

        for role in roles:
            role = await TempRole.create(member,
                                         role=role,
                                         duration=duration,
                                         added_by=ctx.author)
            await role.apply_role()

        await fmt(
            ctx,
            tick(
                _("Added role(s) {roles} to member **{member}** for **{duration}** successfully."
                  )),
            roles=", ".join([bold(x.name) for x in roles]),
            member=member,
            duration=duration.format(),
        )
예제 #18
0
 async def rndactivity_clear(self, ctx: commands.Context):
     """Clears all set statuses"""
     amount = len(await self.config.statuses())
     if await confirm(
         ctx,
         content=_(
             "Are you sure you want to clear {amount} statuses?\n\n"
             "**This action is irreversible!**"
         ).format(amount=amount),
     ):
         await self.config.statuses.set([])
         await self.bot.change_presence(activity=None, status=self.bot.guilds[0].me.status)
         await fmt(ctx, tick(_("Successfully removed {amount} status strings.")), amount=amount)
     else:
         await fmt(ctx, _("Okay then."))
예제 #19
0
 async def _add_status(self, ctx: commands.Context, game: str, *, game_type: int = 0):
     try:
         self.format_status({"type": game_type, "game": game})
     except KeyError as e:
         await fmt(
             ctx,
             warning(
                 _(
                     "Parsing that status failed \N{EM DASH} {placeholder} is not a valid "
                     "placeholder"
                 )
             ),
             placeholder=str(e),
         )
     else:
         async with self.config.statuses() as statuses:
             statuses.append({"type": game_type, "game": game})
             await fmt(ctx, tick(_("Added status **#{id}** successfully.")), id=len(statuses))
예제 #20
0
    async def rndactivity_remove(self, ctx: commands.Context, status: int):
        """Remove one or more statuses by their IDs

        You can retrieve the ID for a status with [p]rndactivity list
        """
        async with self.config.statuses() as statuses:
            if len(statuses) < status:
                return await fmt(ctx, warning(_("No status with the ID `{id}` exists")), id=status)
            removed = statuses.pop(status - 1)
            if not statuses:
                await self.bot.change_presence(
                    activity=None, status=getattr(ctx.me, "status", None)
                )
        removed = escape(self.format_status(removed, return_formatted=False)[0], mass_mentions=True)
        await fmt(
            ctx,
            tick(_("Removed status **#{id}** (`{status}`) successfully.")),
            id=status,
            status=removed,
        )
예제 #21
0
    async def cogwhitelist_add(self, ctx: commands.Context, cog: str, server_id: int = None):
        """Add a cog and/or guild to the list of whitelisted cogs/servers

        If a server ID is specified, the guild is added to the cog's list of allowed servers

        Cogs are handled on a case-insensitive name basis, and as such if you replace
        a cog with another with the same name, it will keep the same settings
        as any cog previously setup with that name, regardless of capitalization.
        """
        proper_name = cog_name(self.bot, cog)
        if not cog:
            return await ctx.send(_("No cog with that name is currently loaded"))
        cog = str(proper_name).lower()
        async with self.config.cogs() as cogs:
            if cog not in cogs:
                cogs[cog] = []
                if not server_id:
                    await fmt(ctx, _("**{cog}** now requires a whitelist to use"), cog=proper_name)
            elif not server_id:
                return await fmt(
                    ctx,
                    warning(_("**{cog}** already requires a whitelist to use")),
                    cog=proper_name,
                )
            if server_id:
                guild = self.bot.get_guild(server_id)
                if not guild:
                    await ctx.send(warning(_("I couldn't find a server with that ID")))
                if guild.id in cogs[cog]:
                    return await fmt(
                        ctx,
                        warning(_("That server is already allowed to use **{cog}**")),
                        cog=proper_name,
                    )
                cogs[cog].append(guild.id)
                await fmt(
                    ctx,
                    tick(_("**{guild_name}** is now allowed to use **{cog}**")),
                    guild_name=escape(guild.name, mass_mentions=True, formatting=True),
                    cog=proper_name,
                )
예제 #22
0
 async def change_content(self):
     content = await self._prompt_user_input(i18n("Please respond with the new quote content"))
     self.quote.text = content
     await self.send(tick(i18n("Changed quote content successfully.")))
예제 #23
0
 async def exit(self, save: bool = False):
     if save is True:
         await self.quote.save()
         await self.send(tick(i18n("Your changes have been carefully recorded and saved.")))
     raise StopLoop
예제 #24
0
 async def stars_update(self, ctx: Context, message: StarboardMessage):
     """Forcefully update a starboard message"""
     await message.update_cached_message()
     await message.update_starboard_message()
     await ctx.send(tick(i18n("Message has been updated.")))