diff --git a/zulip_bots/zulip_bots/bots/flock/assests/1.png b/zulip_bots/zulip_bots/bots/flock/assests/1.png new file mode 100644 index 0000000..7bb64a7 Binary files /dev/null and b/zulip_bots/zulip_bots/bots/flock/assests/1.png differ diff --git a/zulip_bots/zulip_bots/bots/flock/doc.md b/zulip_bots/zulip_bots/bots/flock/doc.md new file mode 100644 index 0000000..c46aeee --- /dev/null +++ b/zulip_bots/zulip_bots/bots/flock/doc.md @@ -0,0 +1,27 @@ +# Flock Bot + +With [Flock](https://flock.com/) bot, you can send messages to any of your + flock contact without having to leave Zulip. + +Sending messages to a user is quite easy, syntax is: +`@botname recipient_name: hello` +where `recipient_name` is name of recipient and `hello` is the sample message. + +## Configuration + +1. Before running Flock bot, you'll need a `token`. In order to get `token`, + Go to [Flock apps](https://dev.flock.com/apps) and create an app. + After successful installation, you'll get an `token` in response from servers. + +1. Once you have `token`, you should supply it in `flock.conf` file. + +## Usage + +Run this bot as described in + [here](https://zulipchat.com/api/running-bots#running-a-bot). + +You can use this bot in one easy step: + +`@botname recipient_firstName: message` + +For help, do `@botname help`. diff --git a/zulip_bots/zulip_bots/bots/flock/fixtures/test_message_send_failed.json b/zulip_bots/zulip_bots/bots/flock/fixtures/test_message_send_failed.json new file mode 100644 index 0000000..c35fd37 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/flock/fixtures/test_message_send_failed.json @@ -0,0 +1,19 @@ +{ + "request": { + "api_url": "https://api.flock.co/v1/chat.sendMessage", + "method": "GET", + "params": { + "token": "12345", + "text": "hi there", + "to": "u:invalid" + } + }, + "response": { + "error": "InvalidParameter", + "description": "A required parameter for the method call is missing or invalid", + "parameter": "to" + }, + "response-headers": { + "content-type": "application/json; charset=utf-8" + } +} diff --git a/zulip_bots/zulip_bots/bots/flock/fixtures/test_message_send_success.json b/zulip_bots/zulip_bots/bots/flock/fixtures/test_message_send_success.json new file mode 100644 index 0000000..66d7f14 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/flock/fixtures/test_message_send_success.json @@ -0,0 +1,17 @@ +{ + "request": { + "api_url": "https://api.flock.co/v1/chat.sendMessage", + "method": "GET", + "params": { + "token": "12345", + "to": "u:userid", + "text": "hi there" + } + }, + "response": { + "uid": "15207048523" + }, + "response-headers": { + "content-type": "application/json; charset=utf-8" + } +} diff --git a/zulip_bots/zulip_bots/bots/flock/fixtures/test_no_recipient_found.json b/zulip_bots/zulip_bots/bots/flock/fixtures/test_no_recipient_found.json new file mode 100644 index 0000000..4efab90 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/flock/fixtures/test_no_recipient_found.json @@ -0,0 +1,15 @@ +{ + "request": { + "api_url": "https://api.flock.co/v1/roster.listContacts", + "method": "GET", + "params": { + "token": "12345" + } + }, + "response": { + "error": "No user found. Make sure you typed it correctly." + }, + "response-headers": { + "content-type": "application/json; charset=utf-8" + } +} diff --git a/zulip_bots/zulip_bots/bots/flock/flock.conf b/zulip_bots/zulip_bots/bots/flock/flock.conf new file mode 100644 index 0000000..5084db7 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/flock/flock.conf @@ -0,0 +1,2 @@ +[flock] +token=12345 diff --git a/zulip_bots/zulip_bots/bots/flock/flock.py b/zulip_bots/zulip_bots/bots/flock/flock.py new file mode 100644 index 0000000..1faa13c --- /dev/null +++ b/zulip_bots/zulip_bots/bots/flock/flock.py @@ -0,0 +1,98 @@ +import logging +import requests +from typing import Any, Dict +from requests.exceptions import ConnectionError + +USERS_LIST_URL = 'https://api.flock.co/v1/roster.listContacts' +SEND_MESSAGE_URL = 'https://api.flock.co/v1/chat.sendMessage' + +help_message = ''' +You can send messages to any Flock user associated with your account from Zulip. +*Syntax*: **@botname to: message** where `to` is **firstName** of recipient. +''' + +# Matches the recipient name provided by user with list of users in his contacts. +# If matches, returns the matched User's ID +def find_recipient(res: str, to: str) -> str: + for obj in res: + if to == obj['firstName']: + return obj['id'] + +# Returns User's ID, if not found, returns error message. +def get_recipient_id(content: str, config: Dict[str, str]) -> str: + token = config['token'] + content_pieces = content.split(':') + to = content_pieces[0].strip() + payload = { + 'token': token + } + + try: + res = requests.get(USERS_LIST_URL, params=payload) + except ConnectionError as e: + logging.exception(str(e)) + return "Uh-Oh, couldn't process the request \ +right now.\nPlease try again later" + + res = res.json() + to = find_recipient(res, to) + if to is None: + return "No user found. Make sure you typed it correctly." + else: + return to + +# This handles the message sending work. +def get_flock_response(content: str, config: Dict[str, str]) -> str: + token = config['token'] + content_pieces = content.split(':') + to = content_pieces[0].strip() + message = content_pieces[1].strip() + + to = get_recipient_id(content, config) + if len(str(to)) > 30: + return to + + payload = { + 'to': to, + 'text': message, + 'token': token + } + try: + r = requests.get(SEND_MESSAGE_URL, params=payload) + except ConnectionError as e: + logging.exception(str(e)) + return "Uh-Oh, couldn't process the request \ +right now.\nPlease try again later" + + r = r.json() + if "uid" in r: + return "Message sent." + else: + return "Message sending failed :slightly_frowning_face:. Please try again." + +def get_flock_bot_response(content: str, config: Dict[str, str]) -> None: + content = content.strip() + if content == '' or content == 'help': + return help_message + else: + result = get_flock_response(content, config) + return result + +class FlockHandler(object): + ''' + This is flock bot. Now you can send messages to any of your + flock user without having to leave Zulip. + ''' + + def initialize(self, bot_handler: Any) -> None: + self.config_info = bot_handler.get_config_info('flock') + + def usage(self) -> str: + return '''Hello from Flock Bot. You can send messages to any Flock user +right from Zulip.''' + + def handle_message(self, message: Dict[str, str], bot_handler: Any) -> None: + response = get_flock_bot_response(message['content'], self.config_info) + bot_handler.send_reply(message, response) + +handler_class = FlockHandler diff --git a/zulip_bots/zulip_bots/bots/flock/requirements.txt b/zulip_bots/zulip_bots/bots/flock/requirements.txt new file mode 100644 index 0000000..f229360 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/flock/requirements.txt @@ -0,0 +1 @@ +requests diff --git a/zulip_bots/zulip_bots/bots/flock/test_flock.py b/zulip_bots/zulip_bots/bots/flock/test_flock.py new file mode 100644 index 0000000..15dd3a5 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/flock/test_flock.py @@ -0,0 +1,62 @@ +from unittest.mock import patch +from zulip_bots.test_lib import BotTestCase +from requests.exceptions import ConnectionError + +class TestFlockBot(BotTestCase): + bot_name = "flock" + normal_config = {"token": "12345"} + + message_config = { + "token": "12345", + "text": "Ricky: test message", + "to": "u:somekey" + } + + help_message = ''' +You can send messages to any Flock user associated with your account from Zulip. +*Syntax*: **@botname to: message** where `to` is **firstName** of recipient. +''' + + def test_bot_responds_to_empty_message(self) -> None: + self.verify_reply('', self.help_message) + + def test_help_message(self) -> None: + self.verify_reply('', self.help_message) + + def test_fetch_id_connection_error(self) -> None: + with self.mock_config_info(self.normal_config), \ + patch('requests.get', side_effect=ConnectionError()), \ + patch('logging.exception'): + self.verify_reply('tyler: Hey tyler', "Uh-Oh, couldn\'t process the request \ +right now.\nPlease try again later") + + def test_response_connection_error(self) -> None: + with self.mock_config_info(self.message_config), \ + patch('requests.get', side_effect=ConnectionError()), \ + patch('logging.exception'): + self.verify_reply('Ricky: test message', "Uh-Oh, couldn\'t process the request \ +right now.\nPlease try again later") + + @patch('zulip_bots.bots.flock.flock.find_recipient') + def test_no_recipient_found(self, find_recipient: str) -> None: + bot_response = "No user found. Make sure you typed it correctly." + find_recipient.return_value = None + with self.mock_config_info(self.normal_config), \ + self.mock_http_conversation('test_no_recipient_found'): + self.verify_reply('david: hello', bot_response) + + @patch('zulip_bots.bots.flock.flock.get_recipient_id') + def test_message_send_success(self, get_recipient_id: str) -> None: + bot_response = "Message sent." + get_recipient_id.return_value = "u:userid" + with self.mock_config_info(self.normal_config), \ + self.mock_http_conversation('test_message_send_success'): + self.verify_reply('Rishabh: hi there', bot_response) + + @patch('zulip_bots.bots.flock.flock.get_recipient_id') + def test_message_send_failed(self, get_recipient_id: str) -> None: + bot_response = "Message sending failed :slightly_frowning_face:. Please try again." + get_recipient_id.return_value = "u:invalid" + with self.mock_config_info(self.normal_config), \ + self.mock_http_conversation('test_message_send_failed'): + self.verify_reply('Rishabh: hi there', bot_response)