diff --git a/tools/run-mypy b/tools/run-mypy index ebe9003..dfd285d 100755 --- a/tools/run-mypy +++ b/tools/run-mypy @@ -80,7 +80,9 @@ force_include = [ "zulip_bots/zulip_bots/bots/baremetrics/baremetrics.py", "zulip_bots/zulip_bots/bots/baremetrics/test_baremetrics.py", "zulip_bots/zulip_bots/bots/salesforce/salesforce.py", - "zulip_bots/zulip_bots/bots/salesforce/test_salesforce.py" + "zulip_bots/zulip_bots/bots/salesforce/test_salesforce.py", + "zulip_bots/zulip_bots/bots/idonethis/idonethis.py", + "zulip_bots/zulip_bots/bots/idonethis/test_idonethis.py" ] parser = argparse.ArgumentParser(description="Run mypy on files tracked by git.") diff --git a/zulip_bots/zulip_bots/bots/idonethis/__init__.py b/zulip_bots/zulip_bots/bots/idonethis/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-entries-list-specific-team.png b/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-entries-list-specific-team.png new file mode 100644 index 0000000..455377a Binary files /dev/null and b/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-entries-list-specific-team.png differ diff --git a/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-help.png b/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-help.png new file mode 100644 index 0000000..1a91366 Binary files /dev/null and b/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-help.png differ diff --git a/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-invalid-command.png b/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-invalid-command.png new file mode 100644 index 0000000..0e58288 Binary files /dev/null and b/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-invalid-command.png differ diff --git a/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-list-entries-all-teams.png b/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-list-entries-all-teams.png new file mode 100644 index 0000000..f53739e Binary files /dev/null and b/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-list-entries-all-teams.png differ diff --git a/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-list-teams.png b/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-list-teams.png new file mode 100644 index 0000000..dcc77dd Binary files /dev/null and b/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-list-teams.png differ diff --git a/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-new-entry-specific-team.png b/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-new-entry-specific-team.png new file mode 100644 index 0000000..eb5d026 Binary files /dev/null and b/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-new-entry-specific-team.png differ diff --git a/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-new-entry.png b/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-new-entry.png new file mode 100644 index 0000000..4eeeb49 Binary files /dev/null and b/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-new-entry.png differ diff --git a/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-team-info.png b/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-team-info.png new file mode 100644 index 0000000..372eb76 Binary files /dev/null and b/zulip_bots/zulip_bots/bots/idonethis/assets/idonethis-team-info.png differ diff --git a/zulip_bots/zulip_bots/bots/idonethis/doc.md b/zulip_bots/zulip_bots/bots/idonethis/doc.md new file mode 100644 index 0000000..d591b1d --- /dev/null +++ b/zulip_bots/zulip_bots/bots/idonethis/doc.md @@ -0,0 +1,46 @@ +# idonethis bot + +The idonethis bot is a Zulip bot which allows interaction with [idonethis](https://idonethis.com/) +through Zulip. It can peform actions such as viewing teams, list entries and creating entries. + +To use the bot simply @-mention the bot followed by a specific command. See the usage section +below for a list of available commands. + +## Setup + +Before proceeding further, ensure you have an idonethis account. + + 1. Go to [your idonethis settings](https://beta.idonethis.com/u/settings), scroll down +and copy your API token. + 2. Open up `zulip_bots/bots/idonethis/idonethis.conf` in your favorite editor, and change +`api_key` to your API token. + 3. Optionally, change the `default_team` value to your default team for creating new messages. +If this is not specified, a team will be required to be manually specified every time an entry is created. + +Run this bot as described [here](https://zulipchat.com/api/running-bots#running-a-bot). + +## Usage + +`` can either be the name or ID of a team. + + * `@mention help` view this help message. + ![](/static/generated/bots/idonethis/assets/idonethis-help.png) + * `@mention teams list` or `@mention list teams` + List all the teams. + ![](/static/generated/bots/idonethis/assets/idonethis-list-teams.png) + * `@mention team info `. + Show information about one ``. + ![](/static/generated/bots/idonethis/assets/idonethis-team-info.png) + * `@mention entries list` or `@mention list entries`. + List entries from any team + ![](/static/generated/bots/idonethis/assets/idonethis-entries-all-teams.png) + * `@mention entries list ` or `@mention list entries ` + List all entries from ``. + ![](/static/generated/bots/idonethis/assets/idonethis-list-entries-specific-team.png) + * `@mention entries create` or `@mention new entry` or `@mention create entry` + or `@mention new entry` or `@mention i did` + Create a new entry. Optionally supply `--team=` for teams with no spaces or `"--team="` + for teams with spaces. For example `@mention i did "--team=product team" something` will create a + new entry `something` for the product team. + ![](/static/generated/bots/idonethis/assets/idonethis-new-entry.png) + ![](/static/generated/bots/idonethis/assets/idonethis-new-entry-specific-team.png) diff --git a/zulip_bots/zulip_bots/bots/idonethis/fixtures/api_noop.json b/zulip_bots/zulip_bots/bots/idonethis/fixtures/api_noop.json new file mode 100644 index 0000000..1b055b5 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/idonethis/fixtures/api_noop.json @@ -0,0 +1,13 @@ +{ + "request": { + "api_url": "https://beta.idonethis.com/api/v2/noop", + "headers": { + "Authorization": "Token 12345678" + } + }, + "response": {"doesnt": "matter"}, + "response-headers": { + "status": 200, + "content-type": "application/json; charset=utf-8" + } +} diff --git a/zulip_bots/zulip_bots/bots/idonethis/fixtures/team_list.json b/zulip_bots/zulip_bots/bots/idonethis/fixtures/team_list.json new file mode 100644 index 0000000..d45e542 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/idonethis/fixtures/team_list.json @@ -0,0 +1,26 @@ +{ + "request": { + "api_url": "https://beta.idonethis.com/api/v2/teams", + "headers": { + "Authorization": "Token 12345678" + } + }, + "response": [ + { + "name": "testing team 1", + "created_at": "2017-12-28T19:12:24.977+11:00", + "updated_at": "2017-12-28T19:12:24.977+11:00", + "hash_id": "31415926535" + }, + { + "name": "test_team_2", + "created_at": "2017-12-28T19:12:55.121+11:00", + "updated_at": "2017-12-28T19:12:55.121+11:00", + "hash_id": "8979deadbeef" + } + ], + "response-headers": { + "status": 200, + "content-type": "application/json; charset=utf-8" + } +} diff --git a/zulip_bots/zulip_bots/bots/idonethis/fixtures/test_401.json b/zulip_bots/zulip_bots/bots/idonethis/fixtures/test_401.json new file mode 100644 index 0000000..bab3b09 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/idonethis/fixtures/test_401.json @@ -0,0 +1,15 @@ +{ + "request": { + "api_url": "https://beta.idonethis.com/api/v2/teams", + "headers": { + "Authorization": "Token 87654321" + } + }, + "response": { + "error": "Invalid API Authentication" + }, + "response-headers": { + "status": 401, + "content-type": "application/json; charset=utf-8" + } +} diff --git a/zulip_bots/zulip_bots/bots/idonethis/fixtures/test_create_entry.json b/zulip_bots/zulip_bots/bots/idonethis/fixtures/test_create_entry.json new file mode 100644 index 0000000..9a560ab --- /dev/null +++ b/zulip_bots/zulip_bots/bots/idonethis/fixtures/test_create_entry.json @@ -0,0 +1,37 @@ +{ + "request": { + "api_url": "https://beta.idonethis.com/api/v2/entries", + "method": "POST", + "json": { + "body": "something and something else", + "team_id": "31415926535" + }, + "headers": { + "Authorization": "Token 12345678" + } + }, + "response": { + "body": "something and something else", + "created_at": "2018-01-04T20:07:58.078+11:00", + "updated_at": "2018-01-04T20:07:58.078+11:00", + "occurred_on": "2018-01-04", + "status": "done", + "hash_id": "fa974ad8c1acb9e81361a051a697f9dae22908d6", + "completed_on": null, + "archived_at": null, + "body_formatted": "something and something else", + "team": { + "name": "testing team 1", + "hash_id": "31415926535" + }, + "user": { + "email_address": "xavier.cooney03@gmail.com", + "full_name": "Benji Franklin", + "hash_id": "f22a944f" + } + }, + "response-headers": { + "status": 200, + "content-type": "application/json; charset=utf-8" + } +} diff --git a/zulip_bots/zulip_bots/bots/idonethis/fixtures/test_create_entry_team_2.json b/zulip_bots/zulip_bots/bots/idonethis/fixtures/test_create_entry_team_2.json new file mode 100644 index 0000000..d8a2f5a --- /dev/null +++ b/zulip_bots/zulip_bots/bots/idonethis/fixtures/test_create_entry_team_2.json @@ -0,0 +1,37 @@ +{ + "request": { + "api_url": "https://beta.idonethis.com/api/v2/entries", + "method": "POST", + "json": { + "body": "something and something else", + "team_id": "8979deadbeef" + }, + "headers": { + "Authorization": "Token 12345678" + } + }, + "response": { + "body": "something and something else", + "created_at": "2018-01-04T20:07:58.078+11:00", + "updated_at": "2018-01-04T20:07:58.078+11:00", + "occurred_on": "2018-01-04", + "status": "done", + "hash_id": "fa974ad8c1acb9e81361a051a697f9dae22908d6", + "completed_on": null, + "archived_at": null, + "body_formatted": "something and something else", + "team": { + "name": "test_team_2", + "hash_id": "8979deadbeef" + }, + "user": { + "email_address": "xavier.cooney03@gmail.com", + "full_name": "Benji Franklin", + "hash_id": "f22a944f" + } + }, + "response-headers": { + "status": 200, + "content-type": "application/json; charset=utf-8" + } +} diff --git a/zulip_bots/zulip_bots/bots/idonethis/fixtures/test_entries_list.json b/zulip_bots/zulip_bots/bots/idonethis/fixtures/test_entries_list.json new file mode 100644 index 0000000..5a3ec9e --- /dev/null +++ b/zulip_bots/zulip_bots/bots/idonethis/fixtures/test_entries_list.json @@ -0,0 +1,74 @@ +{ + "request": { + "api_url": "https://beta.idonethis.com/api/v2/entries?team_id=31415926535", + "headers": { + "Authorization": "Token 12345678" + } + }, + "response": [ + { + "body": "TESTING<>", + "created_at": "2018-01-04T21:10:13.084+11:00", + "updated_at": "2018-01-04T21:10:13.084+11:00", + "occurred_on": "2018-01-04", + "status": "done", + "hash_id": "65e1b21fd8f63adede1daae0bdf28c0e47b84923", + "completed_on": null, + "archived_at": null, + "body_formatted": "TESTING", + "team": { + "name": "testing team 1", + "hash_id": "31415926535" + }, + "user": { + "email_address": "john.doe@generic.name", + "full_name": "John Doe", + "hash_id": "deadbeef" + } + }, + { + "body": "Grabbing some more data...", + "created_at": "2018-01-04T20:07:58.078+11:00", + "updated_at": "2018-01-04T20:07:58.078+11:00", + "occurred_on": "2018-01-04", + "status": "done", + "hash_id": "fa974ad8c1acb9e81361a051a697f9dae22908d6", + "completed_on": null, + "archived_at": null, + "body_formatted": "Grabbing some more data...", + "team": { + "name": "testing team 1", + "hash_id": "31415926535" + }, + "user": { + "email_address": "john.doe@generic.name", + "full_name": "John Doe", + "hash_id": "deadbeef" + } + }, + { + "body": "GRABBING HTTP DATA", + "created_at": "2018-01-04T19:07:17.214+11:00", + "updated_at": "2018-01-04T19:07:17.214+11:00", + "occurred_on": "2018-01-04", + "status": "done", + "hash_id": "72c8241d2218464433268c5abd6625ac104e3d8f", + "completed_on": null, + "archived_at": null, + "body_formatted": "GRABBING HTTP DATA", + "team": { + "name": "testing team 1", + "hash_id": "31415926535" + }, + "user": { + "email_address": "john.doe@generic.name", + "full_name": "John Doe", + "hash_id": "deadbeef" + } + } + ], + "response-headers": { + "status": 200, + "content-type": "application/json; charset=utf-8" + } +} diff --git a/zulip_bots/zulip_bots/bots/idonethis/fixtures/test_show_team.json b/zulip_bots/zulip_bots/bots/idonethis/fixtures/test_show_team.json new file mode 100644 index 0000000..43febcb --- /dev/null +++ b/zulip_bots/zulip_bots/bots/idonethis/fixtures/test_show_team.json @@ -0,0 +1,18 @@ +{ + "request": { + "api_url": "https://beta.idonethis.com/api/v2/teams/31415926535", + "headers": { + "Authorization": "Token 12345678" + } + }, + "response": { + "hash_id": "31415926535", + "name": "testing team 1", + "created_at": "2017-12-28T19:12:55.121+11:00", + "updated_at": "2017-12-28T19:12:55.121+11:00" + }, + "response-headers": { + "status": 200, + "content-type": "application/json; charset=utf-8" + } +} diff --git a/zulip_bots/zulip_bots/bots/idonethis/idonethis.conf b/zulip_bots/zulip_bots/bots/idonethis/idonethis.conf new file mode 100644 index 0000000..21a0b8a --- /dev/null +++ b/zulip_bots/zulip_bots/bots/idonethis/idonethis.conf @@ -0,0 +1,3 @@ +[idonethis] +api_key = 45ba63047f8edbd0ddb9531bfa0971dd1e575313 +default_team = product team diff --git a/zulip_bots/zulip_bots/bots/idonethis/idonethis.py b/zulip_bots/zulip_bots/bots/idonethis/idonethis.py new file mode 100644 index 0000000..be289bc --- /dev/null +++ b/zulip_bots/zulip_bots/bots/idonethis/idonethis.py @@ -0,0 +1,217 @@ +import sys +import requests +import logging +import re + +from typing import Any, Dict + +API_BASE_URL = "https://beta.idonethis.com/api/v2" + +api_key = "" +default_team = "" + +class AuthenticationException(Exception): + pass + +class TeamNotFoundException(Exception): + def __init__(self, team: str) -> None: + self.team = team + +class UnknownCommandSyntax(Exception): + def __init__(self, detail: str) -> None: + self.detail = detail + pass + +class UnspecifiedProblemException(Exception): + pass + +def make_API_request(endpoint: str, method: str="GET", body: Dict[str, str]=None) -> Any: + headers = {'Authorization': 'Token ' + api_key} + if method == "GET": + r = requests.get(API_BASE_URL + endpoint, headers=headers) + elif method == "POST": + r = requests.post(API_BASE_URL + endpoint, headers=headers, json=body) + if r.status_code == 200: + return r.json() + elif r.status_code == 401 and 'error' in r.json() and r.json()['error'] == "Invalid API Authentication": + logging.error('Error authenticating, please check key ' + str(r.url)) + raise AuthenticationException() + else: + logging.error('Error make API request, code ' + str(r.status_code) + '. json: ' + r.json()) + raise UnspecifiedProblemException() + +def api_noop() -> Any: + return make_API_request("/noop") + +def api_list_team() -> Any: + return make_API_request("/teams") + +def api_show_team(hash_id: str) -> Any: + return make_API_request("/teams/{}".format(hash_id)) + +def api_show_users(hash_id: str) -> Any: + return make_API_request("/teams/{}/members".format(hash_id)) + +def api_list_entries(team_id: str=None) -> Any: + if team_id: + return make_API_request("/entries?team_id={}".format(team_id)) + else: + return make_API_request("/entries".format(team_id)) + +def api_create_entry(body: str, team_id: str) -> Any: + return make_API_request("/entries", "POST", {"body": body, "team_id": team_id}) + +def list_steams() -> str: + data = api_list_team() + response = "Teams:\n" + for team in data: + response += " * " + team['name'] + "\n" + return response + +def get_team_hash(team_name: str) -> str: + data = api_list_team() + for team in data: + if team['name'].lower() == team_name.lower() or team['hash_id'] == team_name: + return team['hash_id'] + raise TeamNotFoundException(team_name) + +def team_info(team_name: str) -> str: + data = api_show_team(get_team_hash(team_name)) + response = "Team Name: " + data['name'] + "\n" + response += "ID: `" + data['hash_id'] + "`\n" + response += "Created at: " + data['created_at'] + "\n" + return response + +def entries_list(team_name: str) -> str: + if team_name: + data = api_list_entries(get_team_hash(team_name)) + response = "Entries for " + team_name + ":\n" + else: + data = api_list_entries() + response = "Entries for all teams:\n" + for entry in data: + response += " * " + entry['body_formatted'] + "\n" + response += " * Created at: " + entry['created_at'] + "\n" + response += " * Status: " + entry['status'] + "\n" + response += " * User: " + entry['user']['full_name'] + "\n" + response += " * Team: " + entry['team']['name'] + "\n" + response += " * ID: " + entry['hash_id'] + "\n" + return response + +def create_entry(message: str) -> str: + SINGLE_WORD_REGEX = re.compile("--team=([a-zA-Z0-9_]*)") + MULTIWORD_REGEX = re.compile('"--team=([^"]*)"') + + team = "" + new_message = "" + single_word_match = SINGLE_WORD_REGEX.search(message) + multiword_match = MULTIWORD_REGEX.search(message) + + if multiword_match is not None: + team = multiword_match.group(1) + new_message = MULTIWORD_REGEX.sub("", message).strip() + elif single_word_match is not None: + team = single_word_match.group(1) + new_message = SINGLE_WORD_REGEX.sub("", message).strip() + elif default_team: + team = default_team + new_message = message + else: + raise UnknownCommandSyntax("""I don't know which team you meant for me to create an entry under. +Either set a default team or pass the `--team` flag. +More information in my help""") + + team_id = get_team_hash(team) + data = api_create_entry(new_message, team_id) + return "Great work :thumbs_up:. New entry `{}` created!".format(data['body_formatted']) + +class IDoneThisHandler(object): + def initialize(self, bot_handler: Any) -> None: + global api_key, default_team + self.config_info = bot_handler.get_config_info('idonethis') + if 'api_key' in self.config_info: + api_key = self.config_info['api_key'] + else: + logging.error("An API key must be specified for this bot to run.") + logging.error("Have a look at the Setup section of my documenation for more information.") + bot_handler.quit() + + if 'default_team' in self.config_info: + default_team = self.config_info['default_team'] + else: + logging.error("Cannot find default team. Users will need to manually specify a team each time an entry is created.") + + try: + api_noop() + except AuthenticationException: + logging.error("Authentication exception with idonethis. Can you check that your API keys are correct? ") + bot_handler.quit() + except UnspecifiedProblemException: + logging.error("Problem connecting to idonethis. Please check connection") + bot_handler.quit() + + def usage(self) -> str: + default_team_message = "" + if default_team: + default_team_message = "The default team is currently set as `" + default_team + "`." + else: + default_team_message = "There is currently no default team set up :frowning:." + return ''' +This bot allows for interaction with idonethis, a collaboration tool to increase a team's productivity. +Below are some of the commands you can use, and what they do. + +`` can either be the name or ID of a team. + + * `@mention help` view this help message + * `@mention list teams` + List all the teams + * `@mention team info ` + Show information about one `` + * `@mention list entries` + List entries from any team + * `@mention list entries ` + List all entries from `` + * `@mention new entry` or `@mention i did` + Create a new entry. Optionally supply `--team=` for teams with no spaces or `"--team="` + for teams with spaces. For example `@mention i did "--team=product team" something` will create a + new entry `something` for the product team. + ''' + default_team_message + + def handle_message(self, message: Any, bot_handler: Any) -> None: + bot_handler.send_reply(message, self.get_response(message)) + + def get_response(self, message: Any) -> str: + message_content = message['content'].strip().split() + if message_content == "": + return "" + reply = "" + try: + command = " ".join(message_content[:2]) + if command in ["teams list", "list teams"]: + reply = list_steams() + elif command in ["teams info", "team info"]: + if len(message_content) > 2: + reply = team_info(" ".join(message_content[2:])) + else: + raise UnknownCommandSyntax("You must specify the team in which you request information from.") + elif command in ["entries list", "list entries"]: + reply = entries_list(" ".join(message_content[2:])) + elif command in ["entries create", "create entry", "new entry", "i did"]: + reply = create_entry(" ".join(message_content[2:])) + elif command in ["help"]: + reply = self.usage() + else: + raise UnknownCommandSyntax("I can't understand the command you sent me :confused: ") + except TeamNotFoundException as e: + reply = "Sorry, it doesn't seem as if I can find a team named `" + e.team + "` :frowning:." + except AuthenticationException: + reply = "I can't currently authenticate with idonethis. " + reply += "Can you check that your API key is correct? For more information see my documentation." + except UnknownCommandSyntax as e: + reply = "Sorry, I don't understand what your trying to say. Use `@mention help` to see my help. " + e.detail + except Exception as e: # catches UnspecifiedProblemException, and other problems + reply = "Oh dear, I'm having problems processing your request right now. Perhaps you could try again later :grinning:" + logging.error("Exception caught: " + str(e)) + return reply + +handler_class = IDoneThisHandler diff --git a/zulip_bots/zulip_bots/bots/idonethis/test_idonethis.py b/zulip_bots/zulip_bots/bots/idonethis/test_idonethis.py new file mode 100644 index 0000000..987977b --- /dev/null +++ b/zulip_bots/zulip_bots/bots/idonethis/test_idonethis.py @@ -0,0 +1,93 @@ +from unittest.mock import patch + +from zulip_bots.test_lib import BotTestCase + +import requests + +class TestIDoneThisBot(BotTestCase): + bot_name = "idonethis" # type: str + + def test_create_entry_default_team(self) -> None: + with self.mock_config_info({'api_key': '12345678', 'default_team': 'testing team 1'}), \ + self.mock_http_conversation('test_create_entry'), \ + self.mock_http_conversation('team_list'): + self.verify_reply('i did something and something else', + 'Great work :thumbs_up:. New entry `something and something else` created!') + + def test_create_entry_quoted_team(self) -> None: + with self.mock_config_info({'api_key': '12345678', 'default_team': 'test_team_2'}), \ + self.mock_http_conversation('test_create_entry'), \ + self.mock_http_conversation('team_list'): + self.verify_reply('i did something and something else "--team=testing team 1"', + 'Great work :thumbs_up:. New entry `something and something else` created!') + + def test_create_entry_single_word_team(self) -> None: + with self.mock_config_info({'api_key': '12345678', 'default_team': 'testing team 1'}), \ + self.mock_http_conversation('test_create_entry_team_2'), \ + self.mock_http_conversation('team_list'): + self.verify_reply('i did something and something else --team=test_team_2', + 'Great work :thumbs_up:. New entry `something and something else` created!') + + def test_bad_key(self) -> None: + with self.mock_config_info({'api_key': '87654321', 'default_team': 'testing team 1'}), \ + self.mock_http_conversation('test_401'), \ + patch('zulip_bots.bots.idonethis.idonethis.api_noop'), \ + patch('logging.error'): + self.verify_reply('list teams', + 'I can\'t currently authenticate with idonethis. Can you check that your API key is correct? ' + 'For more information see my documentation.') + + def test_list_team(self) -> None: + with self.mock_config_info({'api_key': '12345678', 'default_team': 'testing team 1'}), \ + self.mock_http_conversation('team_list'): + self.verify_reply('list teams', + 'Teams:\n * testing team 1\n * test_team_2\n') + + def test_show_team_no_team(self) -> None: + with self.mock_config_info({'api_key': '12345678', 'default_team': 'testing team 1'}), \ + self.mock_http_conversation('api_noop'): + self.verify_reply('team info', + 'Sorry, I don\'t understand what your trying to say. Use `@mention help` to see my help. ' + 'You must specify the team in which you request information from.') + + def test_show_team(self) -> None: + with self.mock_config_info({'api_key': '12345678', 'default_team': 'testing team 1'}), \ + self.mock_http_conversation('test_show_team'), \ + patch('zulip_bots.bots.idonethis.idonethis.get_team_hash', return_value='31415926535') as get_team_hashFunction: + self.verify_reply('team info testing team 1', + 'Team Name: testing team 1\n' + 'ID: `31415926535`\n' + 'Created at: 2017-12-28T19:12:55.121+11:00\n') + get_team_hashFunction.assert_called_with('testing team 1') + + def test_entries_list(self) -> None: + with self.mock_config_info({'api_key': '12345678', 'default_team': 'testing team 1'}), \ + self.mock_http_conversation('test_entries_list'), \ + patch('zulip_bots.bots.idonethis.idonethis.get_team_hash', return_value='31415926535') as get_team_hashFunction: + self.verify_reply('entries list testing team 1', + 'Entries for testing team 1:\n' + ' * TESTING\n' + ' * Created at: 2018-01-04T21:10:13.084+11:00\n' + ' * Status: done\n' + ' * User: John Doe\n' + ' * Team: testing team 1\n' + ' * ID: 65e1b21fd8f63adede1daae0bdf28c0e47b84923\n' + ' * Grabbing some more data...\n' + ' * Created at: 2018-01-04T20:07:58.078+11:00\n' + ' * Status: done\n' + ' * User: John Doe\n' + ' * Team: testing team 1\n' + ' * ID: fa974ad8c1acb9e81361a051a697f9dae22908d6\n' + ' * GRABBING HTTP DATA\n' + ' * Created at: 2018-01-04T19:07:17.214+11:00\n' + ' * Status: done\n' + ' * User: John Doe\n' + ' * Team: testing team 1\n' + ' * ID: 72c8241d2218464433268c5abd6625ac104e3d8f\n') + + def test_bot_responds_to_empty_message(self) -> None: + with self.mock_config_info({'api_key': '12345678', 'bot_info': 'team'}), \ + self.mock_http_conversation('api_noop'): + self.verify_reply('', + 'Sorry, I don\'t understand what your trying to say. Use `@mention help` to see my help. ' + 'I can\'t understand the command you sent me :confused: ')