Ejemplo n.º 1
0
class Module(ModuleBase):

    usage_doc = '{prefix}{aliases} <user> <command>'
    short_doc = 'Replace message author and run command'
    long_doc = ('Flags:'
                '\t[--no-prefix|-n]: do not append prefix to the message')

    name = 'runas'
    aliases = (name, )
    category = 'Owner'
    min_args = 2
    user_perms = (PermissionBotOwner(), )
    guild_only = True
    flags = {'no-prefix': {'alias': 'n', 'bool': True}}
    hidden = True

    async def on_call(self, ctx, args, **flags):
        user = await find_user(args[1], ctx.message, strict_guild=True)

        if user is None:
            return '{warning} User not found'

        fake_msg = copy(ctx.message)

        if flags.get('no-prefix', False):
            fake_msg.content = args[2:]
        else:
            fake_msg.content = await get_local_prefix(ctx) + args[2:]

        fake_msg.author = user

        await self.bot.on_message(fake_msg)

        return f'Message processed as `{user}`'
Ejemplo n.º 2
0
class Module(ModuleBase):

    usage_doc = '{prefix}{aliases} <code>'
    short_doc = 'Eval python code'

    name = 'eval'
    aliases = (name, )
    category = 'Owner'
    min_args = 1
    user_perms = (PermissionBotOwner(), )
    hidden = True

    async def on_load(self, from_reload):
        self._last_result = None

    async def on_call(self, ctx, args, **flags):
        program, _ = cleanup_code(args[1:])

        glob = {
            'self': self,
            'bot': self.bot,
            'ctx': ctx,
            'msg': ctx.message,
            'guild': ctx.guild,
            'author': ctx.author,
            'channel': ctx.channel,
            'discord': discord,
            '_': self._last_result
        }

        fake_stdout = io.StringIO()

        to_compile = 'async def func():\n' + textwrap.indent(program, '  ')

        try:
            exec(to_compile, glob)
        except Exception as e:
            return f'```py\n{e.__class__.__name__}: {e}\n```'

        func = glob['func']

        try:
            with redirect_stdout(fake_stdout):
                returned = await func()
        except Exception as e:
            return f'```py\n{fake_stdout.getvalue()}{traceback.format_exc()}\n```'
        else:
            from_stdout = fake_stdout.getvalue()

            if returned is None:
                if from_stdout:
                    return f'```py\n{from_stdout}\n```'
                try:
                    await ctx.react('✅', raise_on_errors=True)
                except discord.Forbidden:
                    return 'Evaluated'
            else:
                self._last_result = returned
                return f'```py\n{from_stdout}{returned}```'
Ejemplo n.º 3
0
class Module(ModuleBase):

    usage_doc = '{prefix}{aliases} [exit_code]'
    short_doc = 'Terminate bot process'

    name = 'stop'
    aliases = (name, 'die', '!')
    category = 'Owner'
    user_perms = (PermissionBotOwner(), )
    hidden = True

    async def on_call(self, ctx, args, **options):
        await ctx.react('✅')

        exit_code = args[1] if len(args) == 2 else STOP_EXIT_CODE
        self.bot.stop(exit_code)
Ejemplo n.º 4
0
class Module(ModuleBase):

    usage_doc = '{prefix}{aliases} <code>'
    short_doc = 'Execute terminal command'
    long_doc = ('Command flags:\n' '\t[--shell|-s]: run code in shell mode')

    name = 'exec'
    aliases = (name, )
    category = 'Owner'
    min_args = 1
    user_perms = (PermissionBotOwner(), )
    flags = {'shell': {'alias': 's', 'bool': True}}
    hidden = True

    async def on_call(self, ctx, args, **flags):
        try:
            if flags.get('shell', False):
                process, pid = await create_subprocess_shell(args[1:])
            else:
                process, pid = await create_subprocess_exec(*args.args[1:])
        except Exception as e:
            return '{error} Error creating subprocess: ' + str(e)

        pid_message = await ctx.send(f'Started process `{pid}`')

        stdout, stderr = await execute_process(process)
        try:
            result = stdout.decode()
        except Exception as e:
            result = f'Error happened while decoding, raw result:\n\n{stdout}'

        if process.returncode != 0:
            result += '\n' + stderr.decode()

        await self.bot.delete_message(pid_message)

        if not result:
            try:
                return await ctx.react('✅', raise_on_errors=True)
            except discord.Forbidden:
                result = 'Executed'

        await ctx.send(f'```\n{result}```')
Ejemplo n.º 5
0
class Module(ModuleBase):

    usage_doc = '{prefix}{aliases} <args ...>'
    short_doc = 'Redis db access'

    name = 'redis'
    aliases = (name, 'r')
    category = 'Owner'
    min_args = 1
    user_perms = (PermissionBotOwner(), )
    hidden = True

    async def on_call(self, ctx, args, **flags):
        value = await self.bot.redis.execute(*args.args[1:])

        return f'Return value: {self.to_string(value)}'
    
    def to_string(self, value):
        if type(value) is list:
            return str([self.to_string(v) for v in value])
        elif type(value) is bytes:
            return value.decode()
        else:
            return str(value)
Ejemplo n.º 6
0
class Module(ModuleBase):

    usage_doc = '{prefix}{aliases} <subcommand> [args...]'
    short_doc = 'Bot presence utils'
    long_doc = ('Subcommands:\n'
                '\tplaying - set playing activity\n'
                '\tstreaming - set streaming activity\n'
                '\tlistening - set listening activity\n'
                '\twatching - set watching activity\n'
                '\tlist - show all activities\n'
                '\tremove - remove activity from list\n\n'
                'Flags:\n'
                '\t[--status|-s] <name>:    select status (online, dnd, etc)\n'
                '\t[--interval|-i] <time>:  change activity change interval')

    name = 'status'
    aliases = (name, 'presence', 'activity')
    category = 'Owner'

    bot_perms = (PermissionAddReactions(), PermissionReadMessageHistory())
    user_perms = (PermissionBotOwner(), )
    min_args = 1
    flags = {
        'status': {
            'alias': 's',
            'bool': False
        },
        'interval': {
            'alias': 'i',
            'bool': False
        }
    }
    hidden = True

    async def on_missing_permissions(self, ctx, *missing):
        return 'not dogsong or notsosuper'

    async def on_load(self, from_reload):
        self.interval = int(await self.bot.redis.get('activity_interval',
                                                     default=60))
        self._task = asyncio.ensure_future(self.update_presence_task(),
                                           loop=self.bot.loop)

    async def on_unload(self):
        self._task.cancel()

    async def on_call(self, ctx, args, **flags):
        interval = flags.get('interval', None)
        if interval is not None:
            if not interval.isdigit():
                return '{warning} Interval is not integer'

            interval = int(interval)
            if interval < 20:
                return '{warning} Minimum allowed interval is 20 seconds'

            self.interval = interval
            await self.bot.redis.set('activity_interval', interval)

            if len(args) == 1:
                return 'Interval updated'

        status = flags.get('status', 'online')

        subcommand = args[1].lower()

        if subcommand == 'list':
            items = await self.bot.redis.smembers('activity')
            if not items:
                return 'Nothing to show'

            lines = [f'{i + 1}) {p}' for i, p in enumerate(items)]
            lines_per_chunk = 4
            chunks = [
                lines[i:i + lines_per_chunk]
                for i in range(0, len(lines), lines_per_chunk)
            ]

            p = Paginator(self.bot)
            for i, chunk in enumerate(chunks):
                p.add_page(content='List of activities:```\n' +
                           '\n'.join(chunk) + '```')

            return await p.run(ctx)

        elif subcommand == 'remove':
            items = await self.bot.redis.smembers('activity')
            if not items:
                return 'Nothing to remove'
            if len(items) == 1:
                await self.bot.redis.srem('activity', items[0])
                await self._change_precense(0, '', 'online')
                return f'Deleted activity `{items[0]}`'

            lines = [f'{i + 1}) {p}' for i, p in enumerate(items)]
            lines_per_chunk = 4
            chunks = [
                lines[i:i + lines_per_chunk]
                for i in range(0, len(lines), lines_per_chunk)
            ]

            p = SelectionPaginator(self.bot)
            for i, chunk in enumerate(chunks):
                p.add_page(content='Please, type index to remove```\n' +
                           '\n'.join(chunk) + '```')

            await p.run(ctx, len(lines))
            if p.choice is not None:
                await self.bot.redis.srem('activity', items[p.choice - 1])
                return f'Deleted activity `{items[p.choice - 1]}`'

        elif subcommand == 'playing':
            a_type = 0
            a_name = args[2:]
        elif subcommand == 'streaming':
            a_type = 1
            a_name = args[2:]
        elif subcommand == 'listening':
            a_type = 2
            a_name = args[2:]
        elif subcommand == 'watching':
            a_type = 3
            a_name = args[2:]
        else:
            return await self.on_doc_request(ctx)

        if a_name:
            await self.bot.redis.sadd('activity',
                                      f'{a_type}:{status}:{a_name}')
        await self._change_precense(a_type, a_name, status)

        return f'Status switched to `{a_name}`' if a_name else 'Status removed'

    async def update_presence_task(self):
        i = 0
        while True:
            if await self.bot.redis.exists('activity'):
                items = await self.bot.redis.smembers('activity')
                if i >= len(items):
                    i = 0
                activity = items[i]
                a_type, _, status_and_name = activity.partition(':')
                status, _, a_name = status_and_name.partition(':')

                await self._change_precense(a_type, a_name, status)
                i += 1
            else:
                await self._change_precense(0, '', 'online')

            await asyncio.sleep(self.interval)

    async def _change_precense(self, a_type, a_name, status):
        a = Activity(name=self.format_activity_name(a_name), type=int(a_type))
        try:
            await self.bot.change_presence(activity=a, status=status)
        except Exception:
            pass

    def format_activity_name(self, name):
        name = name.replace('{guilds}', str(len(self.bot.guilds)))
        name = name.replace('{users}', str(len(self.bot.users)))
        name = name.replace('{emojis}', str(len(self.bot.emojis)))
        name = name.replace('{help_cmd}', f'@{self.bot.user.name} help')

        return name
Ejemplo n.º 7
0
    async def on_call(self, ctx, args, **flags):
        text = args[1:]
        if not text:
            return '{warning} Cannot send an empty message'

        channel = flags.get('channel', None)
        user = flags.get('user', None)
        tts = flags.get('tts', False)

        if channel and user:
            return '{warning} channel and user flags are conflicting'

        if channel:
            channel = await find_channel(channel,
                                         ctx.guild,
                                         global_id_search=True,
                                         include_voice=False,
                                         include_category=False)
            if channel is None:
                return '{warning} Channel not found'

        elif user:
            user = await find_user(user, ctx.message)
            if user is None:
                return '{warning} User not found'

            if user.bot:
                return '{warning} Can\'t send message to bot'

            channel = user.dm_channel
            if channel is None:
                channel = await user.create_dm()

        if channel is None:
            channel = ctx.channel

        if tts:
            check_permission(PermissionSendTtsMessages(), ctx.channel,
                             self.bot.user)
            check_permission(PermissionSendTtsMessages(), ctx.channel,
                             ctx.author)

        is_same_place = getattr(channel, 'guild', None) == ctx.guild
        if not is_same_place:
            if not PermissionBotOwner().check(ctx.channel, ctx.author):
                return '{warning} Only bot owner can send messages to other guilds or users'
        elif not channel.permissions_for(ctx.author).send_messages:
            return '{warning} You don\'t have permission to send messages to this channel'

        delete_message = flags.get('delete', False)
        if delete_message:
            await self.bot.delete_message(ctx.message)

        m = await ctx.send(text,
                           channel=channel,
                           tts=tts,
                           register=not delete_message)
        if m is None:
            return '{error} Failed to deliver message. (blocked by user/no common servers/no permission to send messages to this channel)'

        if not is_same_place:
            if isinstance(m.channel, DMChannel):
                destination = m.channel.recipient
            else:
                destination = f'{channel.mention}** on **{m.guild}'
            return f'Sent message to **{destination}**'
Ejemplo n.º 8
0
class Module(ModuleBase):

    usage_doc = '{prefix}{aliases} <target>'
    short_doc = 'Reload parts of the bot'
    long_doc = (
        'Targets:\n'
        '\tbot: restarts bot\n'
        '\tmodules: reloads all modules\n'
        '\t<command_alias>: reload selected module'
    )

    name = 'reload'
    aliases = (name, )
    category = 'Owner'
    min_args = 1
    user_perms = (PermissionBotOwner(), )
    hidden = True

    async def on_load(self, from_reload):
        data = await self.bot.redis.get('reload_data')
        if data is None:
            return

        await self.bot.redis.delete('reload_data')
        channel_id, message_id = data.split(':')

        try:
            await self.bot.http.edit_message(
                int(message_id), int(channel_id), content='Bot restarted')
        except Exception:
            pass

    async def on_call(self, ctx, args, **flags):
        target = args[1].lower()

        reload_message = await ctx.send(f'Reloading `{target}` ...')

        if target == 'bot':
            await self.bot.redis.set(
                'reload_data', f'{reload_message.channel.id}:{reload_message.id}')
            return self.bot.restart()

        if target == 'modules':
            try:
                await self.bot.mm.reload_modules()
            except Exception:
                response = '{error} Failed to reload modules. Exception:```py\n' + traceback.format_exc() + '```'
                response = await format_response(response, ctx, self.bot)
                return await self.bot.edit_message(
                    reload_message,
                    content=response
                )

            return await self.bot.edit_message(
                reload_message, content='Reloaded {0} modules: [{1}]'.format(
                    len(self.bot.mm.modules),
                    ', '.join(self.bot.mm.modules.keys())
                )
            )
        else:
            module = self.bot.mm.get_module(target)
            if module is None:
                return await self.bot.edit_message(
                    reload_message, content=f'Unknown target `{target}`!')

            target = module.name

            try:
                await self.bot.mm.reload_module(target)
            except Exception:
                response = '{error} ' + f'Failed to reload module `{target}`. Exception:```py\n{traceback.format_exc()}```'
                response = await format_response(response, ctx, self.bot)
                return await self.bot.edit_message(
                    reload_message,
                    content=response
                )

        await self.bot.edit_message(
            reload_message, content=f'Reloading `{target}` completed')