Exemple #1
0
    async def check_permissions(self, message):
        is_owner = await self.is_owner(message.author)
        if not is_owner:
            if settings().get('discord.enable_users_whitelist'):
                users_whitelist = settings().get('discord.users_whitelist', [])

                if message.author.id not in users_whitelist:
                    first_prefix = settings().get('commands.prefixes')[0]
                    embed = Embed(
                        title=f'Only whitelisted users can execute commands',
                        description=f'{first_prefix}.whitelist add {message.author.mention}'
                    )

                    await message.channel.send(embed=embed)
                    return False

            if isinstance(message.channel, DMChannel) and settings().get('discord.disable_dm'):
                embed = Embed(
                    title=f'Using bot on DM is disabled',
                    description='discord.disable_dm = true'
                )

                await message.channel.send(embed=embed)
                return False

        return True
Exemple #2
0
    async def exec(self, ctx, *, command):
        shell_path = settings().get('terminal.shell_path')

        login_as_other_user = settings().get(
            'terminal.user.login_as_other_user')
        if login_as_other_user:
            su_path = settings().get('terminal.su_path')

            login = settings().get('terminal.user.username')
            password = settings().get('terminal.user.password')

            master, slave = pty.openpty()
            subprocess.Popen(
                [su_path, "-", login, "-s", shell_path, '-c', command],
                stdin=slave,
                stdout=slave,
                stderr=slave,
                universal_newlines=True)
            os.read(master, 10240)  # ignore prompt
            os.write(master, f'{password}\n'.encode())
            os.read(master, 10240)  # ignore empty line
            output = os.read(master, 10240).rstrip().decode('utf-8')

            os.close(master)
            os.close(slave)
        else:
            output = subprocess.check_output(
                [shell_path, '-c', command],
                universal_newlines=True,
            )

        await ctx.send(output)
Exemple #3
0
 async def remove(self, ctx, user: User):
     whitelist = settings().config['discord']['users_whitelist']
     if user.id in whitelist:
         whitelist.remove(user.id)
         settings().save()
         await ctx.send(f"Removed user {user.mention} from whitelist")
     else:
         await ctx.send(f"User {user.mention} is not on the whitelist")
Exemple #4
0
 async def add(self, ctx, user: User):
     whitelist = settings().config['discord']['users_whitelist']
     if user.id not in whitelist:
         whitelist.append(user.id)
         settings().save()
         await ctx.send(f"Added user {user.mention} to whitelist")
     else:
         await ctx.send(f"User {user.mention} is already on the whitelist")
Exemple #5
0
async def execute_macro(ctx, name):
    terminal = sessions().by_channel(ctx.channel)

    if name not in settings().macros:
        raise MacroNotFoundException()

    macro = settings().macros[name]
    for line in macro.split('\n'):
        if extract_prefix(line):
            ctx.message.content = line
            await ctx.bot.process_commands(ctx.message)
        else:
            terminal.send_input(line)
Exemple #6
0
    async def _help_all_commands(ctx: Context):
        first_prefix = settings().get('commands.prefixes')[0]

        embed = Embed(
            title='BashBot Help',
            description=f'For more help type {first_prefix}.help [command]',
            color=EMBED_COLOR)
        embed.set_thumbnail(url=THUMBNAIL_URL)

        sorted_commands = sorted(ctx.bot.commands, key=lambda c: c.name)
        for command in sorted_commands:
            # Use full description if brief not found
            if command.brief:
                description = command.brief
            elif command.description:
                description = command.description
            else:
                description = '<<Missing description>>'

            usage = ' ' + command.usage if command.usage else ''
            embed.add_field(name=command.name + usage,
                            value=description,
                            inline=False)

        await ctx.send(embed=embed)
Exemple #7
0
    async def about(self, ctx: Context):
        embed = Embed(
            title='About BashBot',
            description=
            'BashBot is a Discord bot that allows terminal access via chat.',
            color=EMBED_COLOR)
        embed.add_field(name='Github', value=REPOSITORY_URL, inline=False)
        embed.add_field(name='Author', value=REPOSITORY_AUTHOR, inline=False)
        embed.add_field(name='Current version',
                        value=updater().get_local_commit(),
                        inline=False)
        embed.set_thumbnail(url=THUMBNAIL_URL)

        if settings().get('other.check_for_updates'):
            update_details = updater().check_for_updates()
            if update_details:
                embed.add_field(
                    name='New update available',
                    value=
                    f'"{update_details["message"]}"\n{update_details["sha"]}',
                    inline=False)
            else:
                embed.add_field(name='No updates available',
                                value='BashBot is up to date',
                                inline=False)

        await ctx.send(embed=embed)
Exemple #8
0
    def update_message(self, terminal: Terminal, content: str):
        message = self.find_message(terminal)

        content = parse_template(settings().get('terminal.template'),
                                 name=terminal.name,
                                 state=terminal.state.name,
                                 content=block_escape(content))

        execute_async(message.edit, content=content)
Exemple #9
0
    async def on_ready(self):
        self.__check_for_updates()

        self.logger.info(f'Logged in as {self.user.name} ({self.user.id})')
        self.logger.info(
            f'You can add bot to your server via {oauth_url(self.user.id)}')

        presence = parse_template(settings().get("discord.presence"),
                                  prefix=self.command_prefix)
        await self.change_presence(status=Status.online,
                                   activity=Game(presence))
Exemple #10
0
    def __check_for_updates(self):
        if settings().get('other.check_for_updates'):
            self.logger.info(f'Checking for updates...')

            update_details = updater().check_for_updates(rate_limit=False)
            if update_details:
                self.logger.info(f'New update available. Try running `git pull`. '
                                 f'Commit "{update_details["message"]}" '
                                 f'({update_details["sha"]})')
            else:
                self.logger.info(f'BashBot is up to date')
Exemple #11
0
    def __init__(self, name: str,
                 sh_path: str, su_path: str = None,
                 login: str = None, password: str = None,
                 on_change=None):
        self.name = name
        self.sh_path = sh_path
        self.su_path = su_path
        self.login = login
        self.password = password
        self.on_change = on_change

        self.controls = {}
        self.interactive = settings().get('terminal.interactive_by_default')
        self.auto_submit = settings().get('terminal.submit_by_default')

        self.state: TerminalState = TerminalState.CLOSED
        self.screen = pyte.Screen(80, 24)
        self.stream = pyte.ByteStream(self.screen)

        self.fd = None
        self.content = None
        self.content_update = False
Exemple #12
0
    async def on_message(self, message: Message):
        if message.author.bot:
            return

        terminal = sessions().by_channel(message.channel)

        if self.is_invoke(message):
            if not await self.check_permissions(message):
                return

            await self.process_commands(message)
        elif terminal and terminal.state == TerminalState.OPEN:
            prefix = extract_prefix(message.content)
            if not terminal.interactive and not prefix:
                return

            if not await self.check_permissions(message):
                return

            # We don't remove prefix when in interactive mode
            content = message.content
            if not terminal.interactive:
                content = remove_prefix(content)

            if terminal.auto_submit:
                content += '\n'

            terminal.send_input(content)

            # Log message
            guild_name = message.channel.guild.name
            channel_name = message.channel.name
            author_name = message.author.name
            self.cmd_logger.info(f"[{guild_name}/#{channel_name}/{terminal.name}] {author_name} typed: {content}")

            should_delete_any = settings().get('terminal.delete_messages')
            should_delete_interactive = settings().get('terminal.interactive.delete_messages')
            if should_delete_any or (should_delete_interactive and terminal.interactive):
                await message.delete()
Exemple #13
0
    async def on_message(self, message: Message):
        if message.author.bot:
            return

        is_owner = await self.is_owner(message.author)
        if not is_owner:
            if settings().get('discord.enable_users_whitelist'):
                users_whitelist = settings().get('discord.users_whitelist', [])

                if message.author.id not in users_whitelist:
                    first_prefix = settings().get('commands.prefixes')[0]
                    embed = Embed(
                        title=f'Only whitelisted users can execute commands',
                        description=
                        f'{first_prefix}.whitelist add {message.author.mention}'
                    )
                    await message.channel.send(embed=embed)
                    return

            if isinstance(message.channel,
                          DMChannel) and settings().get('discord.disable_dm'):
                embed = Embed(title=f'Using bot on DM is disabled',
                              description='discord.disable_dm = true')
                await message.channel.send(embed=embed)
                return

        terminal = sessions().by_channel(message.channel)

        if self.is_invoke(message):
            await self.process_commands(message)
        elif terminal and terminal.state == TerminalState.OPEN:
            prefix = extract_prefix(message.content)
            if not terminal.interactive and not prefix:
                return

            # We don't remove prefix when in interactive mode
            content = message.content
            if not terminal.interactive:
                content = remove_prefix(content)

            if terminal.auto_submit:
                content += '\n'

            terminal.send_input(content)

            # Log message
            guild_name = message.channel.guild.name
            channel_name = message.channel.name
            author_name = message.author.name
            self.cmd_logger.info(
                f"[{guild_name}/#{channel_name}/{terminal.name}] {author_name} typed: {content}"
            )

            should_delete_any = settings().get('terminal.delete_messages')
            should_delete_interactive = settings().get(
                'terminal.interactive.delete_messages')
            if should_delete_any or (should_delete_interactive
                                     and terminal.interactive):
                await message.delete()
Exemple #14
0
    async def close(self, ctx):
        terminal = sessions().by_channel(ctx.message.channel)

        if not terminal:
            raise SessionDontExistException()

        terminal.close()

        if settings().get('terminal.delete_on_close'):
            message = sessions().find_message(terminal)
            await message.delete()

        sessions().remove(terminal)
        await ctx.send(f"`Closed terminal #{terminal.name}`")
Exemple #15
0
    async def open(self, ctx, name: str = None):
        if name and len(name) > 20:
            raise ArgumentFormatException(
                'Session name length exceeds 20 characters limit')

        # Auto-generated name
        if not name:
            name = str(len(sessions().sessions))

        content = parse_template(settings().get('terminal.template'),
                                 name=name,
                                 state='OPENING',
                                 content='Waiting for tty..')
        message = await ctx.send(content)

        # Prepare terminal
        sh_path = settings().get('terminal.shell_path')

        login_as_other_user = settings().get(
            'terminal.user.login_as_other_user')
        if login_as_other_user:
            su_path = settings().get('terminal.su_path')
            login = settings().get('terminal.user.username')
            password = settings().get('terminal.user.password')

            terminal = Terminal(name,
                                sh_path=sh_path,
                                on_change=sessions().update_message,
                                su_path=su_path,
                                login=login,
                                password=password)
        else:
            terminal = Terminal(name,
                                sh_path=sh_path,
                                on_change=sessions().update_message)

        sessions().add(message, terminal)
        terminal.open()

        # Run macro on terminal startup
        startup_macro = settings().get('terminal.startup_macro')
        if startup_macro:
            await execute_macro(ctx, startup_macro)
Exemple #16
0
def launch():
    setup_logger()
    settings().load()
    settings().load_macros()

    prefix = settings().get('commands.prefixes', ['$'])[0]
    token = settings().get('discord.token')

    if token == 'TOKEN_HERE':
        logger.error('You need to specify bot TOKEN in config.toml')
        return

    try:
        BashBot(prefix).run(token)
    except LoginFailure as e:
        logger.error(e.args[0])
Exemple #17
0
def is_command(content):
    return any(
        content.startswith(prefix + '.')
        for prefix in settings().get('commands.prefixes'))
Exemple #18
0
def extract_prefix(content):
    for prefix in settings().get('commands.prefixes'):
        if content.startswith(prefix):
            return prefix