diff --git a/zulip_bots/zulip_bots/bots/crypto/__init__.py b/zulip_bots/zulip_bots/bots/crypto/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/zulip_bots/zulip_bots/bots/crypto/crypto.py b/zulip_bots/zulip_bots/bots/crypto/crypto.py new file mode 100644 index 000000000..cfb24a50c --- /dev/null +++ b/zulip_bots/zulip_bots/bots/crypto/crypto.py @@ -0,0 +1,87 @@ +# bot to fetch crypto information from coinbase API. +# No API key authentication is required for pricing +# and time information. + +from datetime import datetime +from typing import Any, Dict + +import requests +from requests.exceptions import ConnectionError, HTTPError + +from zulip_bots.lib import BotHandler + + +class CryptoHandler: + """ + This bot will get the current spot "market" exchange rate for a given + cryptocurrency in USD. + """ + def usage(self): + return """ + This bot allows users to get spot prices for requested cryptocurrencies in USD. + Users should @-mention the bot with the following format: + @-mention + i.e., "@-mention BTC 2022-04-16" or just "@-mention BTC" + """ + + def handle_message(self, message: Dict[str, Any], bot_handler: BotHandler): + original_content = message["content"] + args = original_content.split() + if len(args) == 0 or len(args) > 2: + bot_handler.send_reply(message, self.usage()) + return + + date_param = None + if len(args) == 2: + date_param = {"date": args[1]} + + # check format of date input + if date_param: + format = "%Y-%m-%d" + + try: + datetime.strptime(date_param["date"], format) + except ValueError: + reply = "Dates should be in the form YYYY-MM-DD." + bot_handler.send_reply(message, reply) + return + + currency_param = args[0] + + try: + if date_param: + response = requests.get( + "https://api.coinbase.com/v2/prices/{}-USD/spot".format(currency_param), + params=date_param + ) + else: + response = requests.get( + "https://api.coinbase.com/v2/prices/{}-USD/spot".format(currency_param) + ) + + # raise exception for bad status codes + response.raise_for_status() + except (HTTPError, ConnectionError): + reply = ( + "Sorry, I wasn't able to find anything on {}. " + "Check your spelling and try again." + ).format(currency_param) + + bot_handler.send_reply(message, reply) + return + + market_price = response.json()["data"]["amount"] + + if date_param: + reply = ( + "The market price for {} on {} was ${}" + ).format(currency_param, date_param["date"], market_price) + else: + reply = ( + "The current market price for {} is ${}" + ).format(currency_param, market_price) + + bot_handler.send_reply(message, reply) + + +handler_class = CryptoHandler diff --git a/zulip_bots/zulip_bots/bots/crypto/doc.md b/zulip_bots/zulip_bots/bots/crypto/doc.md new file mode 100644 index 000000000..77e207325 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/crypto/doc.md @@ -0,0 +1,18 @@ +# crypto bot + +The crypto bot is a Zulip bot that can fetch the +current market price for a specified cryptocurrency in USD. +The crypto bot can also retrieve a market price for +a cryptocurrency from a given date in the form YYYY-MM-DD. + +To get the current market price: +``` +@crypto BTC +> The current market price for BTC is $40696.73 +``` + +To get the price on a given date: +``` +@crypto BTC 2022-04-16 +> The market price for BTC on 2022-04-16 was $40554.6 +``` \ No newline at end of file diff --git a/zulip_bots/zulip_bots/bots/crypto/fixtures/test_404.json b/zulip_bots/zulip_bots/bots/crypto/fixtures/test_404.json new file mode 100644 index 000000000..3d6ecd5b1 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/crypto/fixtures/test_404.json @@ -0,0 +1,11 @@ +{ + "request": { + "api_url": "https://api.coinbase.com/v2/prices/XYZ-USD/spot" + }, + "response": {}, + "response-headers": { + "status": 404, + "ok": false, + "content-type": "application/json; charset=utf-8" + } + } \ No newline at end of file diff --git a/zulip_bots/zulip_bots/bots/crypto/fixtures/test_normal_date.json b/zulip_bots/zulip_bots/bots/crypto/fixtures/test_normal_date.json new file mode 100644 index 000000000..5127d1a7a --- /dev/null +++ b/zulip_bots/zulip_bots/bots/crypto/fixtures/test_normal_date.json @@ -0,0 +1,22 @@ +{ + "request": { + "api_url": "https://api.coinbase.com/v2/prices/BTC-USD/spot", + "params": { + "date": "2022-04-16" + } + }, + "response": { + "meta": { + "status": 200 + }, + "data": { + "amount": 40554.6, + "base" : "BTC", + "currency": "USD" + } + }, + "response-headers": { + "status": 200, + "content-type": "application/json; charset=utf-8" + } +} \ No newline at end of file diff --git a/zulip_bots/zulip_bots/bots/crypto/fixtures/test_normal_no_date.json b/zulip_bots/zulip_bots/bots/crypto/fixtures/test_normal_no_date.json new file mode 100644 index 000000000..86d7cd15f --- /dev/null +++ b/zulip_bots/zulip_bots/bots/crypto/fixtures/test_normal_no_date.json @@ -0,0 +1,19 @@ +{ + "request": { + "api_url": "https://api.coinbase.com/v2/prices/BTC-USD/spot" + }, + "response": { + "meta": { + "status": 200 + }, + "data": { + "amount": 40696.73, + "base" : "BTC", + "currency": "USD" + } + }, + "response-headers": { + "status": 200, + "content-type": "application/json; charset=utf-8" + } +} \ No newline at end of file diff --git a/zulip_bots/zulip_bots/bots/crypto/test_crypto.py b/zulip_bots/zulip_bots/bots/crypto/test_crypto.py new file mode 100644 index 000000000..f9bcbe40e --- /dev/null +++ b/zulip_bots/zulip_bots/bots/crypto/test_crypto.py @@ -0,0 +1,57 @@ +from zulip_bots.test_lib import BotTestCase, DefaultTests + + +class TestCryptoBot(BotTestCase, DefaultTests): + bot_name = "crypto" + + # Test for correct behavior given proper crypto and date + def test_normal_date(self): + bot_response = "The market price for BTC on 2022-04-16 was $40554.6" + + with self.mock_http_conversation("test_normal_date"): + self.verify_reply("BTC 2022-04-16", bot_response) + + # test for "current" price + def test_normal_no_date(self): + bot_response = "The current market price for BTC is $40696.73" + + with self.mock_http_conversation("test_normal_no_date"): + self.verify_reply("BTC", bot_response) + + # test malformatted date + def test_bad_date(self): + bot_response = "Dates should be in the form YYYY-MM-DD." + + self.verify_reply("BTC 04-16-2022", bot_response) + + # test typo --> Not Found + def test_400_error(self): + bot_response = ( + "Sorry, I wasn't able to find anything on XYZ. " + "Check your spelling and try again." + ) + + with self.mock_http_conversation("test_404"): + self.verify_reply("XYZ", bot_response) + + # test empty message + def test_no_inputs(self): + bot_reponse = """ + This bot allows users to get spot prices for requested cryptocurrencies in USD. + Users should @-mention the bot with the following format: + @-mention + i.e., "@-mention BTC 2022-04-16" or just "@-mention BTC" + """ + + self.verify_reply("", bot_reponse) + + # test too many arguments + def test_too_many_inputs(self): + bot_response = """ + This bot allows users to get spot prices for requested cryptocurrencies in USD. + Users should @-mention the bot with the following format: + @-mention + i.e., "@-mention BTC 2022-04-16" or just "@-mention BTC" + """ + + self.verify_reply("BTC ETH 2022-04-16", bot_response)