From 38c5bab42d99d765a4c39b32324da2e047dbd947 Mon Sep 17 00:00:00 2001 From: Drew Bednar Date: Thu, 1 Apr 2021 20:34:39 -0400 Subject: [PATCH] add notifications and push instructions --- README.md | 8 ++- docker/Dockerfile | 2 +- metabot/notifications.py | 108 ++++++++++++++++++++++++++++++++++ metabot/runcible_bot.py | 122 +++++++++++++++++++++++++++++++++++++++ requirements.in | 4 +- requirements.txt | 4 ++ 6 files changed, 244 insertions(+), 4 deletions(-) create mode 100644 metabot/notifications.py create mode 100644 metabot/runcible_bot.py diff --git a/README.md b/README.md index 61c159b..b1a06bd 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,15 @@ https://hub.docker.com/r/keybaseio/client ## Docker -### Building the image +### Building and Pushing the image Run from the root directory ``` -docker build -f ./docker/Dockerfile -t runcible/runcible-metabot . +docker login https://registry.runcible.io + +docker build -f ./docker/Dockerfile -t registry.runcible.io/runcible-metabot: . + +docker push registry.runcible.io/runcible-metabot: ``` ### Running the container diff --git a/docker/Dockerfile b/docker/Dockerfile index 5986df2..4debdbe 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -15,4 +15,4 @@ COPY metabot/ /app RUN chown -R keybase:keybase /app # Run bot.py when the container launches -CMD ["python3", "-m","bot.py"] \ No newline at end of file +CMD ["python3", "-m","notifications.py"] \ No newline at end of file diff --git a/metabot/notifications.py b/metabot/notifications.py new file mode 100644 index 0000000..d6a20f6 --- /dev/null +++ b/metabot/notifications.py @@ -0,0 +1,108 @@ +import json +import os +from typing import Dict, List, Tuple + +from pykeybase.chat import KeybaseChat + +DRONE_ENV_VARS = {"CI", + "DRONE", + "DRONE_BRANCH", + "DRONE_BUILD_ACTION", + "DRONE_BUILD_CREATED", + "DRONE_BUILD_EVENT", + "DRONE_BUILD_FINISHED", + "DRONE_BUILD_NUMBER", + "DRONE_BUILD_PARENT", + "DRONE_BUILD_STARTED", + "DRONE_BUILD_STATUS", + "DRONE_CALVER", + "DRONE_COMMIT", + "DRONE_COMMIT_AFTER", + "DRONE_COMMIT_AUTHOR", + "DRONE_COMMIT_AUTHOR_AVATAR", + "DRONE_COMMIT_AUTHOR_EMAIL", + "DRONE_COMMIT_AUTHOR_NAME", + "DRONE_COMMIT_BEFORE", + "DRONE_COMMIT_BRANCH", + "DRONE_COMMIT_LINK", + "DRONE_COMMIT_MESSAGE", + "DRONE_COMMIT_REF", + "DRONE_COMMIT_SHA", + "DRONE_DEPLOY_TO", + "DRONE_FAILED_STAGES", + "DRONE_FAILED_STEPS", + "DRONE_GIT_HTTP_URL", + "DRONE_GIT_SSH_URL", + "DRONE_PULL_REQUEST", + "DRONE_REMOTE_URL", + "DRONE_REPO", + "DRONE_REPO_BRANCH", + "DRONE_REPO_LINK", + "DRONE_REPO_NAME", + "DRONE_REPO_NAMESPACE", + "DRONE_REPO_OWNER", + "DRONE_REPO_PRIVATE", + "DRONE_REPO_SCM", + "DRONE_REPO_VISIBILITY", + "DRONE_SEMVER", + "DRONE_SEMVER_BUILD", + "DRONE_SEMVER_ERROR", + "DRONE_SEMVER_MAJOR", + "DRONE_SEMVER_MINOR", + "DRONE_SEMVER_PATCH", + "DRONE_SEMVER_PRERELEASE", + "DRONE_SEMVER_SHORT", + "DRONE_SOURCE_BRANCH", + "DRONE_STAGE_ARCH", + "DRONE_STAGE_DEPENDS_ON", + "DRONE_STAGE_FINISHED", + "DRONE_STAGE_KIND", + "DRONE_STAGE_MACHINE", + "DRONE_STAGE_NAME", + "DRONE_STAGE_NUMBER", + "DRONE_STAGE_OS", + "DRONE_STAGE_STARTED", + "DRONE_STAGE_STATUS", + "DRONE_STAGE_TYPE", + "DRONE_STAGE_VARIANT", + "DRONE_STEP_NAME", + "DRONE_STEP_NUMBER", + "DRONE_SYSTEM_HOST", + "DRONE_SYSTEM_HOSTNAME", + "DRONE_SYSTEM_PROTO", + "DRONE_SYSTEM_VERSION", + "DRONE_TAG", + "DRONE_TARGET_BRANCH"} + + +def build_drone_env_dict() -> Dict[str, str]: + return {key: os.environ.get(key, "") for key in DRONE_ENV_VARS} + + +def build_keybase_teams() -> List[Tuple[str, str]]: + keybase_teams = [] + teams_list = os.environ.get("KEYBASE_TEAMS", "").split(",") + for team in teams_list: + keybase_teams.append(tuple(team.split("."))) + + if not keybase_teams: + keybase_teams = [("", "")] + + return keybase_teams + + +def main(): + # Teams to notify format ".,." + keybase_teams = build_keybase_teams() + keybase_chat = KeybaseChat() + env_map = build_drone_env_dict() + msg = json.dumps(env_map, indent=4) + + print(msg) + + for team in keybase_teams: + keybase_chat.send_team_message(team=team[0], message=msg, channel=team[1]) + + +if __name__ == "__main__": + main() diff --git a/metabot/runcible_bot.py b/metabot/runcible_bot.py new file mode 100644 index 0000000..4c70ef4 --- /dev/null +++ b/metabot/runcible_bot.py @@ -0,0 +1,122 @@ +"""sobelbot: techchat bot + TODO: + * add more commands + * config file + * setup.py +""" +import os +import random +import time +import yaml +from shlex import split +from pykeybase import KeybaseChat, KeybaseBot, KeybaseTeam + +# Check env for conf +conf_file = os.getenv('SOBEL_CONF') +if not conf_file: + print('ERROR: ENV var SOBEL_CONF not found') + exit(1) + +# Read in conf yaml file +with open(conf_file) as cfile: + conf = yaml.load(cfile) + +# Team channels to monitor +channels = conf['channels'] + +kb = KeybaseChat() +bot = KeybaseBot(kb, channels, help_command=r'^\.help$', help_trigger='.help') + + +@bot.command(r'^\.member\sadd\s\w+', help_trigger='.member add ') +def add_member(message_data): + """Add a member to the current team""" + try: + kbt = KeybaseTeam() + req = kbt.add_members( + message_data['team'], + [{ + 'username': message_data['body'].split()[-1].strip(), + 'role': 'reader' + }] + ) + except Exception as err: + resp = "error adding user" + if req.get('error'): + if 'admin' in req['error']['message']: + resp = 'Error: I do not have the proper rights' + elif 'already' in req['error']['message']: + resp = 'Error: user is already a member' + else: + resp = 'member added' + + return bot.respond(resp, message_data, at_mention=True) + + +@bot.command(r'^\.member\sremove\s\w+', help_trigger='.member remove ') +def remove_member(message_data): + """Remove a member from the current team""" + try: + kbt = KeybaseTeam() + req = kbt.remove_member( + message_data['team'], + message_data['body'].split()[-1].strip() + ) + except Exception as err: + resp['error'] = "error removing user" + if req.get('error'): + if 'admin' in req['error']['message']: + resp = 'Error: I do not have the proper rights' + elif 'is not' in req['error']['message']: + resp = 'Error: user is not a member' + else: + resp = 'member removed' + + return bot.respond(resp, message_data, at_mention=True) + + +@bot.command(r'\b(fuck|shit|ass|pussy|bitch)\b', show_help=False) +def swear_cmd(message_data): + """Respond to swear words""" + response_text = "Please dont use that kind of language in here." + return bot.respond(response_text, message_data, at_mention=True) + + +@bot.command(r'^\.roll', help_trigger='.roll ') +def roll_cmd(message_data): + '''Roll amount of -sided dice. If and are not + provided, default is to roll 2 6-sided dice. If and are + provided, must be between 1 and 10, and must be between 2 + and 100.''' + try: + num_of_dice = int(split(message_data['body'])[1]) + num_of_sides = int(split(message_data['body'])[2]) + 1 + except ValueError: + num_of_dice = 0 + num_of_sides = 0 + except IndexError: + num_of_dice = 2 + num_of_sides = 7 + if (num_of_dice not in range(1, 11) or + num_of_sides not in range(2, 102)): + response_text = '`` must be a number from 1 to 10, and ' \ + '`` must be a number from 2 to 100' + return bot.respond(response_text, message_data, at_mention=True) + dice = [] + for _ in range(num_of_dice): + dice.append(random.choice(range(1, num_of_sides))) + response_text = 'You rolled a ' + if len(dice) > 1: + for n in dice[0:-1]: + response_text += '`{}`, '.format(n) + response_text += 'and `{}`, for a total of `{}`.'.format(dice[-1], + sum(dice)) + else: + response_text += '`{}`.'.format(dice[0]) + return bot.respond(response_text, message_data, at_mention=True) + + +try: + bot.run(respond=True) +except KeyboardInterrupt: + exit(0) diff --git a/requirements.in b/requirements.in index 9938956..91ee9dd 100644 --- a/requirements.in +++ b/requirements.in @@ -1 +1,3 @@ -pykeybasebot \ No newline at end of file +pykeybasebot +pykeybase +pyyaml \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 8003fad..042ea17 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,8 +14,12 @@ marshmallow==3.10.0 # marshmallow-enum mypy-extensions==0.4.3 # via typing-inspect +pykeybase==0.5.0 + # via -r requirements.in pykeybasebot==0.2.1 # via -r requirements.in +pyyaml==5.4.1 + # via -r requirements.in stringcase==1.2.0 # via dataclasses-json typing-extensions==3.7.4.3