Esempio n. 1
0
async def get_cases_for_member(
    guild: discord.Guild, bot: Red, *, member: discord.Member = None, member_id: int = None
) -> List[Case]:
    """
    Gets all cases for the specified member or member id in a guild.

    Parameters
    ----------
    guild: `discord.Guild`
        The guild to get the cases from
    bot: Red
        The bot's instance
    member: `discord.Member`
        The member to get cases about
    member_id: int
        The id of the member to get cases about

    Returns
    -------
    list
        A list of all matching cases.

    Raises
    ------
    ValueError
        If at least one of member or member_id is not provided
    `discord.Forbidden`
        The bot does not have permission to fetch the modlog message which was sent.
    `discord.HTTPException`
        Fetching the user failed.
    """

    cases = await _config.custom(_CASES, str(guild.id)).all()

    if not (member_id or member):
        raise ValueError("Expected a member or a member id to be provided.") from None

    if not member_id:
        member_id = member.id

    if not member:
        member = bot.get_user(member_id) or member_id

    try:
        modlog_channel = await get_modlog_channel(guild)
    except RuntimeError:
        modlog_channel = None

    cases = [
        await Case.from_json(modlog_channel, bot, case_number, case_data, user=member, guild=guild)
        for case_number, case_data in cases.items()
        if case_data["user"] == member_id
    ]

    return cases
Esempio n. 2
0
def red(config_fr):
    from beastbot.core.cli import parse_cli_flags

    cli_flags = parse_cli_flags(["ignore_me"])

    description = "Red v3 - Alpha"

    Config.get_core_conf = lambda *args, **kwargs: config_fr

    red = Red(cli_flags=cli_flags,
              description=description,
              dm_help=None,
              owner_ids=set())

    yield red
Esempio n. 3
0
def handle_edit(cli_flags: Namespace):
    """
    This one exists to not log all the things like it's a full run of the bot.
    """
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    data_manager.load_basic_configuration(cli_flags.instance_name)
    red = Red(cli_flags=cli_flags, description="Red V3", dm_help=None)
    try:
        driver_cls = drivers.get_driver_class()
        loop.run_until_complete(
            driver_cls.initialize(**data_manager.storage_details()))
        loop.run_until_complete(edit_instance(red, cli_flags))
        loop.run_until_complete(driver_cls.teardown())
    except (KeyboardInterrupt, EOFError):
        print("Aborted!")
    finally:
        loop.run_until_complete(asyncio.sleep(1))
        asyncio.set_event_loop(None)
        loop.stop()
        loop.close()
        sys.exit(0)
Esempio n. 4
0
async def setup(bot: Red) -> None:
    cog = Filter(bot)
    await cog.initialize()
    bot.add_cog(cog)
Esempio n. 5
0
def setup(bot: Red):
    bot.add_cog(ModLog(bot))
Esempio n. 6
0
async def _init(bot: Red):
    global _config
    global _bot_ref
    _bot_ref = bot
    _config = Config.get_conf(None, 1354799444, cog_name="ModLog")
    _config.register_global(schema_version=1)
    _config.register_guild(mod_log=None, casetypes={}, latest_case_number=0)
    _config.init_custom(_CASETYPES, 1)
    _config.init_custom(_CASES, 2)
    _config.register_custom(_CASETYPES)
    _config.register_custom(_CASES)
    await _migrate_config(from_version=await _config.schema_version(), to_version=_SCHEMA_VERSION)
    await register_casetypes(all_generics)

    async def on_member_ban(guild: discord.Guild, member: discord.Member):

        if not guild.me.guild_permissions.view_audit_log:
            return

        try:
            await get_modlog_channel(guild)
        except RuntimeError:
            return  # No modlog channel so no point in continuing

        when = datetime.utcnow()
        before = when + timedelta(minutes=1)
        after = when - timedelta(minutes=1)
        await asyncio.sleep(10)  # prevent small delays from causing a 5 minute delay on entry

        attempts = 0
        # wait up to an hour to find a matching case
        while attempts < 12 and guild.me.guild_permissions.view_audit_log:
            attempts += 1
            try:
                entry = await guild.audit_logs(
                    action=discord.AuditLogAction.ban, before=before, after=after
                ).find(lambda e: e.target.id == member.id and after < e.created_at < before)
            except discord.Forbidden:
                break
            except discord.HTTPException:
                pass
            else:
                if entry:
                    if entry.user.id != guild.me.id:
                        # Don't create modlog entires for the bot's own bans, cogs do this.
                        mod, reason = entry.user, entry.reason
                        date = entry.created_at.replace(tzinfo=timezone.utc)
                        await create_case(_bot_ref, guild, date, "ban", member, mod, reason)
                    return

            await asyncio.sleep(300)

    async def on_member_unban(guild: discord.Guild, user: discord.User):
        if not guild.me.guild_permissions.view_audit_log:
            return

        try:
            await get_modlog_channel(guild)
        except RuntimeError:
            return  # No modlog channel so no point in continuing

        when = datetime.utcnow()
        before = when + timedelta(minutes=1)
        after = when - timedelta(minutes=1)
        await asyncio.sleep(10)  # prevent small delays from causing a 5 minute delay on entry

        attempts = 0
        # wait up to an hour to find a matching case
        while attempts < 12 and guild.me.guild_permissions.view_audit_log:
            attempts += 1
            try:
                entry = await guild.audit_logs(
                    action=discord.AuditLogAction.unban, before=before, after=after
                ).find(lambda e: e.target.id == user.id and after < e.created_at < before)
            except discord.Forbidden:
                break
            except discord.HTTPException:
                pass
            else:
                if entry:
                    if entry.user.id != guild.me.id:
                        # Don't create modlog entires for the bot's own unbans, cogs do this.
                        mod, reason = entry.user, entry.reason
                        date = entry.created_at.replace(tzinfo=timezone.utc)
                        await create_case(_bot_ref, guild, date, "unban", user, mod, reason)
                    return

            await asyncio.sleep(300)

    bot.add_listener(on_member_ban)
    bot.add_listener(on_member_unban)
Esempio n. 7
0
    async def from_json(
        cls, mod_channel: discord.TextChannel, bot: Red, case_number: int, data: dict, **kwargs
    ):
        """Get a Case object from the provided information

        Parameters
        ----------
        mod_channel: discord.TextChannel
            The mod log channel for the guild
        bot: Red
            The bot's instance. Needed to get the target user
        case_number: int
            The case's number.
        data: dict
            The JSON representation of the case to be gotten
        **kwargs
            Extra attributes for the Case instance which override values
            in the data dict. These should be complete objects and not
            IDs, where possible.

        Returns
        -------
        Case
            The case object for the requested case

        Raises
        ------
        `discord.NotFound`
            The user the case is for no longer exists
        `discord.Forbidden`
            Cannot read message history to fetch the original message.
        `discord.HTTPException`
            A generic API issue
        """
        guild = kwargs.get("guild") or mod_channel.guild

        message = kwargs.get("message")
        if message is None:
            message_id = data.get("message")
            if message_id is not None:
                message = discord.utils.get(bot.cached_messages, id=message_id)
                if message is None:
                    try:
                        message = await mod_channel.fetch_message(message_id)
                    except discord.HTTPException:
                        message = None
            else:
                message = None

        user_objects = {"user": None, "moderator": None, "amended_by": None}
        for user_key in tuple(user_objects):
            user_object = kwargs.get(user_key)
            if user_object is None:
                user_id = data.get(user_key)
                if user_id is None:
                    user_object = None
                else:
                    user_object = bot.get_user(user_id) or user_id
            user_objects[user_key] = user_object

        channel = kwargs.get("channel") or guild.get_channel(data["channel"]) or data["channel"]
        case_guild = kwargs.get("guild") or bot.get_guild(data["guild"])
        return cls(
            bot=bot,
            guild=case_guild,
            created_at=data["created_at"],
            action_type=data["action_type"],
            case_number=case_number,
            reason=data["reason"],
            until=data["until"],
            channel=channel,
            modified_at=data["modified_at"],
            message=message,
            last_known_username=data.get("last_known_username"),
            **user_objects,
        )
Esempio n. 8
0
def setup(bot: Red):
    bot.add_cog(Economy(bot))
Esempio n. 9
0
async def setup(bot: Red):
    cog = Mutes(bot)
    bot.add_cog(cog)
    await cog.initialize()
Esempio n. 10
0
def main():
    red = None  # Error handling for users misusing the bot
    cli_flags = parse_cli_flags(sys.argv[1:])
    handle_early_exit_flags(cli_flags)
    if cli_flags.edit:
        handle_edit(cli_flags)
        return
    try:
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)

        if cli_flags.no_instance:
            print(
                "\033[1m"
                "Warning: The data will be placed in a temporary folder and removed on next system "
                "reboot."
                "\033[0m")
            cli_flags.instance_name = "temporary_red"
            data_manager.create_temp_config()

        data_manager.load_basic_configuration(cli_flags.instance_name)

        red = Red(cli_flags=cli_flags, description="Red V3", dm_help=None)

        if os.name != "nt":
            # None of this works on windows.
            # At least it's not a redundant handler...
            signals = (signal.SIGHUP, signal.SIGTERM, signal.SIGINT)
            for s in signals:
                loop.add_signal_handler(
                    s,
                    lambda s=s: asyncio.create_task(shutdown_handler(red, s)))

        exc_handler = functools.partial(global_exception_handler, red)
        loop.set_exception_handler(exc_handler)
        # We actually can't (just) use asyncio.run here
        # We probably could if we didn't support windows, but we might run into
        # a scenario where this isn't true if anyone works on RPC more in the future
        fut = loop.create_task(run_bot(red, cli_flags))
        r_exc_handler = functools.partial(red_exception_handler, red)
        fut.add_done_callback(r_exc_handler)
        loop.run_forever()
    except KeyboardInterrupt:
        # We still have to catch this here too. (*joy*)
        log.warning(
            "Please do not use Ctrl+C to Shutdown Red! (attempting to die gracefully...)"
        )
        log.error("Received KeyboardInterrupt, treating as interrupt")
        if red is not None:
            loop.run_until_complete(shutdown_handler(red, signal.SIGINT))
    except SystemExit as exc:
        # We also have to catch this one here. Basically any exception which normally
        # Kills the python interpreter (Base Exceptions minus asyncio.cancelled)
        # We need to do something with prior to having the loop close
        log.info("Shutting down with exit code: %s", exc.code)
        if red is not None:
            loop.run_until_complete(shutdown_handler(red, None, exc.code))
    except Exception as exc:  # Non standard case.
        log.exception("Unexpected exception (%s): ", type(exc), exc_info=exc)
        if red is not None:
            loop.run_until_complete(
                shutdown_handler(red, None, ExitCodes.CRITICAL))
    finally:
        # Allows transports to close properly, and prevent new ones from being opened.
        # Transports may still not be closed correctly on windows, see below
        loop.run_until_complete(loop.shutdown_asyncgens())
        # *we* aren't cleaning up more here, but it prevents
        # a runtime error at the event loop on windows
        # with resources which require longer to clean up.
        # With other event loops, a failure to cleanup prior to here
        # results in a resource warning instead
        log.info("Please wait, cleaning up a bit more")
        loop.run_until_complete(asyncio.sleep(2))
        asyncio.set_event_loop(None)
        loop.stop()
        loop.close()
        exit_code = red._shutdown_mode if red is not None else 1
        sys.exit(exit_code)
Esempio n. 11
0
async def setup(bot: Red):
    cog = Alias(bot)
    bot.add_cog(cog)
    cog.sync_init()
Esempio n. 12
0
def setup(bot: Red):
    bot.add_cog(Cleanup(bot))
Esempio n. 13
0
def setup(bot: Red):
    bot.add_cog(Reports(bot))
Esempio n. 14
0
def setup(bot: Red):
    cog = Audio(bot)
    bot.add_cog(cog)
    cog.start_up_task()