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
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)
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")
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")
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)
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)
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)
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)
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))
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')
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
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()
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()
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}`")
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)
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])
def is_command(content): return any( content.startswith(prefix + '.') for prefix in settings().get('commands.prefixes'))
def extract_prefix(content): for prefix in settings().get('commands.prefixes'): if content.startswith(prefix): return prefix