diff --git a/app/dragon-bot.py b/app/dragon-bot.py index d3ffc918..efdcb834 100644 --- a/app/dragon-bot.py +++ b/app/dragon-bot.py @@ -1,6 +1,10 @@ -import random -import sys -import requests +""" +dragon-bot + +Our discord bot. Many of its actions are handled by separate modules which are +then imported into the main bot +""" + import os import decide @@ -11,6 +15,7 @@ import eight_ball import excuse import help_methods import lewds +import role_check import wallpaper import wolfram @@ -29,12 +34,14 @@ async def on_ready(): print("\nDRAGON BOT RUNNING IN {} MODE".format(dragon_environment.upper())) print("\n********************************") - await client.change_presence(game=discord.Game(name='Type !help to see how to use me')) + await client.change_presence( + game=discord.Game(name='Type !help to see what I can do') + ) if debug: print("\nPress control+c to exit the bot") print("Followed by control+d or by typing") - print("'exit' to exit the docker container") + print("'exit' to exit the docker container\n\n") @client.event @@ -43,13 +50,11 @@ async def on_message(message): """ is_me(m) - takes a message as an argument and checks that the author of the message + Takes a message as an argument and checks that the author of the message is the same as the person who initiated the command """ return m.author == message.author - ##### Looks like discord supports mentioning the bot. - #### Need to think of something to do here if client.user.mentioned_in(message): await client.send_message( message.channel, @@ -65,7 +70,11 @@ async def on_message(message): eight_ball.check_8ball(message.content) ) - if message.content.startswith('!cleanup') and message.author.id == '144986109804412928': + if message.content.startswith('!cleanup'): + if not role_check.cleanup_permissions(message.author.roles): + await client.send_message(message.channel, 'You cant do that') + return + def is_bot(m): return m.author == client.user await client.purge_from(message.channel, limit=100, check=is_bot) @@ -95,7 +104,10 @@ async def on_message(message): ) if message.content.startswith('!lewd'): - await client.send_message(message.channel, lewds.get_lewd(channel_name=message.channel.name)) + await client.send_message( + message.channel, + lewds.get_lewd(channel_name=message.channel.name) + ) if message.content.startswith('!purge'): num = 20 @@ -103,8 +115,12 @@ async def on_message(message): try: num = int(message.content.split()[1]) except ValueError: - # If they dont enter a number, show them an error and return out of the function - await client.send_message(message.channel, "You need to give me a number, you entered {}".format(message.content.split()[1])) + await client.send_message( + message.channel, + "You need to give me a number, you entered {}".format( + message.content.split()[1] + ) + ) return await client.purge_from(message.channel, limit=num, check=is_me) @@ -112,56 +128,94 @@ async def on_message(message): await client.send_message( message.channel, wallpaper.get_wall(message.content) - ) + ) - ################################### - ###### +-------------------+ ###### - ###### | | ###### - ###### | Docker Functions | ###### - ###### | | ###### - ###### +-------------------+ ###### - ################################### - if message.content.startswith('!docker') and (message.author != client.user): + if message.content.startswith('!docker'): # Check permissions - roles = [] - allowed_roles = ['MOD', 'Greasemonkey', 'Adminimodistrator'] - for role in message.author.roles: - roles.append(role.name) - docker_privleges = not set(roles).isdisjoint(allowed_roles) - if not docker_privleges: - await client.send_message(message.channel, "Sorry {}, You dont have permission to run docker commands".format(message.author.mention)) + if not role_check.docker_permissions(message.author.roles): + await client.send_message( + message.channel, + "You dont have permission to run docker commands" + ) return if len(message.content.split()) == 1: actions = ['restart', 'status', 'logs'] - await client.send_message(message.channel, "\nSupported actions:\n{}".format(", ".join(actions))) + await client.send_message( + message.channel, + "\nSupported actions:\n{}".format(", ".join(actions)) + ) else: docker_client = docker.from_env() minecraft_container = docker_client.containers.get('skyfactory') + # Figure out what action they want to take action = message.content.split()[1] if action == 'restart': - await client.send_message(message.channel, "{}, Are you sure you want to restart the container? [!yes/!no]".format(message.author.mention)) - confirm_restart = await client.wait_for_message(author=message.author, channel=message.channel, content='!yes') + await client.send_message( + message.channel, + "{}, restart the container? [!yes/!no]".format( + message.author.mention) + ) + + confirm_restart = await client.wait_for_message( + author=message.author, + channel=message.channel, + content='!yes' + ) if confirm_restart: - await client.send_message(message.channel, "Sending restart action to {} container".format(minecraft_container.name)) + await client.send_message( + message.channel, + "Sending restart action to {} container".format( + minecraft_container.name + ) + ) + minecraft_container.restart() if action == 'status': - await client.send_message(message.channel, "{} is {}".format(minecraft_container.name, minecraft_container.status)) + await client.send_message( + message.channel, + "{} container is {}".format( + minecraft_container.name, + minecraft_container.status + ) + ) + if action == 'logs': if len(message.content.split()) == 3: num_lines = int(message.content.split()[2]) else: num_lines = 10 - log_stream = minecraft_container.logs(tail=num_lines).decode('utf-8') + log_stream = minecraft_container.logs( + tail=num_lines + ).decode('utf-8') + if len(log_stream) >= num_lines: - await client.send_message(message.channel, "Pulling last {} lines from {} container".format(num_lines, minecraft_container.name)) - await client.send_message(message.channel, "```{}```".format(minecraft_container.logs(tail=num_lines).decode('utf-8'))) + await client.send_message( + message.channel, + "Pulling last {} lines from {} container".format( + num_lines, + minecraft_container.name + ) + ) + + await client.send_message( + message.channel, + "```{}```".format( + minecraft_container.logs( + tail=num_lines + ).decode('utf-8') + ) + ) + else: - await client.send_message(message.channel, "There arent {} lines of output yet".format(num_lines)) + await client.send_message( + message.channel, + "There arent {} lines of output yet".format(num_lines) + ) client.run(tokens[dragon_environment]) diff --git a/app/role_check.py b/app/role_check.py new file mode 100644 index 00000000..be3ecc6f --- /dev/null +++ b/app/role_check.py @@ -0,0 +1,56 @@ +""" +role_check + +This module's purpose is to handle permissions for interacting with the bot. +The end goal is to be able to lock certain actions down to certain roles +""" + +def check_permissions(user, roles_to_check): + """ + check_permissions(user) + + Parses the list object that is passed in and returns true or false if + the user has the correct roles + """ + users_roles = [] + for role in user: + users_roles.append(role.name) + + # Returns true if the user contains a role in the roles_to_check list + return not set(users_roles).isdisjoint(roles_to_check) + + +def is_admin(user): + """ + is_admin(user) + + Returns true if the user contains the Adminimodistrator role + """ + return check_permissions(user, ['Adminimodistrator']) + + +def is_mod(user): + """ + is_admin(user) + + Returns true if the user contains the MOD or Greasemonkey role + """ + return check_permissions(user, ['MOD', 'Greasemonkey']) + + +def docker_permissions(user): + """ + docker_permissions(user) + + Checks that the user has permissions to have dragon-bot run docker commands + """ + return is_mod(user) or is_admin(user) + + +def cleanup_permissions(user): + """ + cleanup_permissions(user) + + Who has rights to make dragon-bot purge its messages + """ + return is_admin(user) or user.id == '144986109804412928' diff --git a/test-dragon-bot.sh b/test-dragon-bot.sh index c26395ec..1867bc89 100755 --- a/test-dragon-bot.sh +++ b/test-dragon-bot.sh @@ -1,25 +1,3 @@ -# Add the git hooks to the local repo. This prevents pushes on master -SCRIPTDIR="$(dirname "$0")" -HOOKDIR="$SCRIPTDIR/.git/hooks" -SCRIPT="$HOOKDIR/pre-push" -cat << EOF > $SCRIPT -#!/bin/bash -protected_branch='master' -# check each branch being pushed -while read local_ref local_sha remote_ref remote_sha -do - remote_branch=$(echo $remote_ref | sed -e 's,.*/\(.*\),\1,') - if [ $protected_branch = $remote_branch ] - then - echo "ABORT PUSH: Not allowed to push directly to $protected_branch. Use --no-verify to force." - exit 1 # push will not execute - fi -done -exit 0 -EOF - -chmod +x $SCRIPT - # Remove the running container so we cna re-use the container name 'dragon-bot' printf "\n[-] Deleting old dragon-bot container from system\n" docker rm -f dragon-bot-test