interactive bots: Create Baremetrics bot.

This commit is contained in:
Viraat Chandra 2017-12-26 23:47:47 +05:30 committed by Robert Hönig
parent b80a0cb297
commit d8c6cb7c0a
11 changed files with 502 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View file

@ -0,0 +1,2 @@
[baremetrics]
api_key = <api_key>

View file

@ -0,0 +1,172 @@
# See readme.md for instructions on running this code.
from typing import Any
import requests
class BaremetricsHandler(object):
def initialize(self, bot_handler: Any) -> None:
self.config_info = bot_handler.get_config_info('baremetrics')
self.api_key = self.config_info['api_key']
self.auth_header = {
'Authorization': 'Bearer ' + self.api_key
}
self.commands = ['help', 'list-commands', 'account-info', 'list-sources', 'list-plans <source_id>',
'list-customers <source_id>',
'list-subscriptions <source_id>']
self.descriptions = ['Display bot info', 'Display the list of available commands', 'Display the account info',
'List the sources', 'List the plans for the source', 'List the customers in the source',
'List the subscriptions in the source']
def usage(self) -> str:
return '''
This bot gives updates about customer behavior, financial performance, and analytics
for an organization using the Baremetrics Api.\n
Enter `list-commands` to show the list of available commands.
Version 1.0
'''
def handle_message(self, message: Any, bot_handler: Any) -> None:
message['content'] = message['content'].strip()
if message['content'].lower() == 'help':
bot_handler.send_reply(message, self.usage())
return
if message['content'].lower() == 'list-commands':
response = '**Available Commands:** \n'
for command, description in zip(self.commands, self.descriptions):
response += ' - {} : {}\n'.format(command, description)
bot_handler.send_reply(message, response)
return
if message['content'] == '':
bot_handler.send_reply(message, 'No Command Specified')
return
response = self.generate_response(message['content'])
bot_handler.send_reply(message, response)
def generate_response(self, command: str) -> str:
try:
if command.lower() == 'account-info':
return self.get_account_info()
if command.lower() == 'list-sources':
return self.get_sources()
part_commands = command.split()
try:
if part_commands[0].lower() == 'list-plans':
return self.get_plans(part_commands[1])
if part_commands[0].lower() == 'list-customers':
return self.get_customers(part_commands[1])
if part_commands[0].lower() == 'list-subscriptions':
return self.get_subscriptions(part_commands[1])
except IndexError:
return 'Missing Params.'
except KeyError:
return 'Invalid Response From API.'
return 'Invalid Command.'
def get_account_info(self) -> str:
url = "https://api.baremetrics.com/v1/account"
account_response = requests.get(url, headers=self.auth_header)
account_data = account_response.json()
account_data = account_data['account']
response = '**Your account information:** \n'
response += 'Id: {id}\n'.format(id=account_data['id'])
response += 'Company: {company}\n'.format(company=account_data['company'])
response += 'Default Currency: {currency_name}'.format(currency_name=account_data['default_currency']['name'])
return response
def get_sources(self) -> str:
url = 'https://api.baremetrics.com/v1/sources'
sources_response = requests.get(url, headers=self.auth_header)
sources_data = sources_response.json()
sources_data = sources_data['sources']
response = '**Listing sources:** \n'
for index, source in enumerate(sources_data):
response += '{}.ID: {}\nProvider: {}\nProvider ID: {}\n\n'.format(index + 1, source['id'],
source['provider'],
source['provider_id'])
return response
def get_plans(self, source_id: str) -> str:
url = 'https://api.baremetrics.com/v1/{}/plans'.format(source_id)
plans_response = requests.get(url, headers=self.auth_header)
plans_data = plans_response.json()
plans_data = plans_data['plans']
template = '{}.Name: {}\nActive: {}\nInterval: {}\nInterval Count: {}\nAmounts: \n'
response = '**Listing plans:** \n'
for index, plan in enumerate(plans_data):
response += template.format(index + 1, plan['name'], plan['active'], plan['interval'],
plan['interval_count'])
for amount in plan['amounts']:
response += ' - {} {}\n'.format(amount['amount'], amount['currency'])
response += '\n'
return response
def get_customers(self, source_id: str) -> str:
url = 'https://api.baremetrics.com/v1/{}/customers'.format(source_id)
customers_response = requests.get(url, headers=self.auth_header)
customers_data = customers_response.json()
customers_data = customers_data['customers']
template = '{}.Name: {}\nDisplay Name: {}\nOID: {}\nActive: {}\nEmail: {}\nNotes: {}\nCurrent Plans: \n'
response = '**Listing customers:** \n'
for index, customer in enumerate(customers_data):
response += template.format(index + 1, customer['display_name'], customer['name'], customer['oid'],
customer['is_active'], customer['email'], customer['notes'])
for plan in customer['current_plans']:
response += ' - {}\n'.format(plan['name'])
response += '\n'
return response
def get_subscriptions(self, source_id: str) -> str:
url = 'https://api.baremetrics.com/v1/{}/subscriptions'.format(source_id)
subscriptions_response = requests.get(url, headers=self.auth_header)
subscriptions_data = subscriptions_response.json()
subscriptions_data = subscriptions_data['subscriptions']
template = '{}.Customer Name: {}\nCustomer Display Name: {}\nCustomer OID: {}\nCustomer Email: {}\n' \
'Active: {}\nPlan Name: {}\nPlan Amounts: \n'
response = '**Listing subscriptions:** \n'
for index, subscription in enumerate(subscriptions_data):
response += template.format(index + 1, subscription['customer']['name'],
subscription['customer']['display_name'],
subscription['customer']['oid'], subscription['customer']['email'],
subscription['active'], subscription['plan']['name'])
for amount in subscription['plan']['amounts']:
response += ' - {} {}\n'.format(amount['amount'], amount['symbol'])
response += '\n'
return response
handler_class = BaremetricsHandler

View file

@ -0,0 +1,41 @@
# Baremetrics bot
The Baremetrics bot is a Zulip bot that gives updates about customer behavior, financial performance, and
analytics for an organization using the [Baremetrics](https://baremetrics.com/) API.
To use the Baremetrics bot, you can simply call it with `@<botname>` followed
by a command, like so:
```
@Baremetrics help
```
## Setup
Before usage, you will need to configure the bot by putting the value of the `<api_key>` in the config file.
To do this, follow the given steps:
1. Login at [Baremetrics Console](https://app.baremetrics.com/settings/api).
2. Note the `Live API Key`.
3. Open up `zulip_bots/bots/baremetrics/baremetrics.conf` in an editor and
change the value of the `<api_key>` attribute to the noted `Live API Key`.
## Developer Notes
Be sure to add the command and its description to their respective lists (named `commands` and `descriptions`)
so that it can be displayed with the other available commands using `@<botname> list-commands`. Also modify
the `test_list_commands_command` in `test_baremetrics.py`.
## Links
- [Baremetrics](https://baremetrics.com/)
- [Baremetrics Developer API](https://developers.baremetrics.com/reference)
- [Baremetrics Dashboard](https://app.baremetrics.com/setup)
## Usage
`@Baremetrics list-commands` - This command gives a list of all available commands along with short
short descriptions.
Example:
![](assets/list-commands.png)

View file

@ -0,0 +1,59 @@
{
"request": {
"api_url": "https://api.baremetrics.com/v1/account",
"headers": {
"Authorization": "Bearer TEST"
}
},
"response": {
"account": {
"id": 376418,
"default_currency": {
"id": "usd",
"alternate_symbols": [
"US$"
],
"decimal_mark": ".",
"disambiguate_symbol": "US$",
"html_entity": "$",
"iso_code": "USD",
"iso_numeric": "840",
"name": "United States Dollar",
"priority": 1,
"smallest_denomination": 1,
"subunit": "Cent",
"subunit_to_unit": 100,
"symbol": "$",
"symbol_first": true,
"thousands_separator": ","
},
"company": "NA",
"created_at": 1514301115,
"stack": "seg"
}
},
"response-headers": {
"X-TokenExpires": "0",
"Server": "cloudflare-nginx",
"X-RateLimit-Remaining": "3593",
"X-Powered-By": "Phusion Passenger 5.0.30",
"Set-Cookie": "__cfduid=dd26464e3e6779b6a05b27d2681aea0851514374567; expires=Thu, 27-Dec-18 11:36:07 GMT; path=/; domain=.baremetrics.com; HttpOnly; Secure, LSW_WEB=\"LSW_WEB1\"; path=/",
"X-Runtime": "0.031730",
"Access-Control-Allow-Credentials": "false",
"Access-Control-Allow-Methods": "GET, OPTIONS, POST, PUT, DELETE",
"Content-Encoding": "gzip",
"Connection": "keep-alive",
"ETag": "W/\"110de39b319ed195bf1414ac4f3c9a01\"",
"Date": "Wed, 27 Dec 2017 11:36:08 GMT",
"X-RateLimit-Limit": "3600",
"X-Version": "721",
"X-Commit": "2dcec7190a2bd01c8d3d8527d386486c4bb5474c",
"Status": "200 OK",
"Content-Type": "application/json; charset=utf-8",
"Transfer-Encoding": "chunked",
"Access-Control-Allow-Headers": "Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type",
"Cache-Control": "max-age=0, private, must-revalidate",
"X-Request-Id": "0b292131-81bb-4df2-bdb9-089a50b17e63",
"CF-RAY": "3d3bfaf6feee2eff-DEL"
}
}

View file

@ -0,0 +1,43 @@
{
"request": {
"api_url": "https://api.baremetrics.com/v1/TEST/customers",
"headers": {
"Authorization": "Bearer TEST"
}
},
"response": {
"customers": [],
"meta": {
"pagination": {
"has_more": false,
"page": 0,
"per_page": 30,
"total_count": 0
}
}
},
"response-headers": {
"X-TokenExpires": "0",
"Server": "cloudflare-nginx",
"X-RateLimit-Remaining": "3593",
"X-Powered-By": "Phusion Passenger 5.0.30",
"Set-Cookie": "__cfduid=dd26464e3e6779b6a05b27d2681aea0851514374567; expires=Thu, 27-Dec-18 11:36:07 GMT; path=/; domain=.baremetrics.com; HttpOnly; Secure, LSW_WEB=\"LSW_WEB1\"; path=/",
"X-Runtime": "0.031730",
"Access-Control-Allow-Credentials": "false",
"Access-Control-Allow-Methods": "GET, OPTIONS, POST, PUT, DELETE",
"Content-Encoding": "gzip",
"Connection": "keep-alive",
"ETag": "W/\"110de39b319ed195bf1414ac4f3c9a01\"",
"Date": "Wed, 27 Dec 2017 11:36:08 GMT",
"X-RateLimit-Limit": "3600",
"X-Version": "721",
"X-Commit": "2dcec7190a2bd01c8d3d8527d386486c4bb5474c",
"Status": "200 OK",
"Content-Type": "application/json; charset=utf-8",
"Transfer-Encoding": "chunked",
"Access-Control-Allow-Headers": "Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type",
"Cache-Control": "max-age=0, private, must-revalidate",
"X-Request-Id": "0b292131-81bb-4df2-bdb9-089a50b17e63",
"CF-RAY": "3d3bfaf6feee2eff-DEL"
}
}

View file

@ -0,0 +1,42 @@
{
"request": {
"api_url": "https://api.baremetrics.com/v1/TEST/plans",
"headers": {
"Authorization": "Bearer TEST"
}
},
"response": {
"plans": [],
"meta": {
"pagination": {
"has_more": false,
"page": 0,
"per_page": 30
}
}
},
"response-headers": {
"X-TokenExpires": "0",
"Server": "cloudflare-nginx",
"X-RateLimit-Remaining": "3593",
"X-Powered-By": "Phusion Passenger 5.0.30",
"Set-Cookie": "__cfduid=dd26464e3e6779b6a05b27d2681aea0851514374567; expires=Thu, 27-Dec-18 11:36:07 GMT; path=/; domain=.baremetrics.com; HttpOnly; Secure, LSW_WEB=\"LSW_WEB1\"; path=/",
"X-Runtime": "0.031730",
"Access-Control-Allow-Credentials": "false",
"Access-Control-Allow-Methods": "GET, OPTIONS, POST, PUT, DELETE",
"Content-Encoding": "gzip",
"Connection": "keep-alive",
"ETag": "W/\"110de39b319ed195bf1414ac4f3c9a01\"",
"Date": "Wed, 27 Dec 2017 11:36:08 GMT",
"X-RateLimit-Limit": "3600",
"X-Version": "721",
"X-Commit": "2dcec7190a2bd01c8d3d8527d386486c4bb5474c",
"Status": "200 OK",
"Content-Type": "application/json; charset=utf-8",
"Transfer-Encoding": "chunked",
"Access-Control-Allow-Headers": "Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type",
"Cache-Control": "max-age=0, private, must-revalidate",
"X-Request-Id": "0b292131-81bb-4df2-bdb9-089a50b17e63",
"CF-RAY": "3d3bfaf6feee2eff-DEL"
}
}

View file

@ -0,0 +1,48 @@
{
"request": {
"api_url": "https://api.baremetrics.com/v1/sources",
"headers": {
"Authorization": "Bearer TEST"
}
},
"response": {
"sources": [
{
"id": "5f7QC5NC0Ywgcu",
"provider": "baremetrics",
"provider_id": null
}
],
"meta": {
"pagination": {
"has_more": false,
"page": 0,
"per_page": 30
}
}
},
"response-headers": {
"X-TokenExpires": "0",
"Server": "cloudflare-nginx",
"X-RateLimit-Remaining": "3593",
"X-Powered-By": "Phusion Passenger 5.0.30",
"Set-Cookie": "__cfduid=dd26464e3e6779b6a05b27d2681aea0851514374567; expires=Thu, 27-Dec-18 11:36:07 GMT; path=/; domain=.baremetrics.com; HttpOnly; Secure, LSW_WEB=\"LSW_WEB1\"; path=/",
"X-Runtime": "0.031730",
"Access-Control-Allow-Credentials": "false",
"Access-Control-Allow-Methods": "GET, OPTIONS, POST, PUT, DELETE",
"Content-Encoding": "gzip",
"Connection": "keep-alive",
"ETag": "W/\"110de39b319ed195bf1414ac4f3c9a01\"",
"Date": "Wed, 27 Dec 2017 11:36:08 GMT",
"X-RateLimit-Limit": "3600",
"X-Version": "721",
"X-Commit": "2dcec7190a2bd01c8d3d8527d386486c4bb5474c",
"Status": "200 OK",
"Content-Type": "application/json; charset=utf-8",
"Transfer-Encoding": "chunked",
"Access-Control-Allow-Headers": "Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type",
"Cache-Control": "max-age=0, private, must-revalidate",
"X-Request-Id": "0b292131-81bb-4df2-bdb9-089a50b17e63",
"CF-RAY": "3d3bfaf6feee2eff-DEL"
}
}

View file

@ -0,0 +1,42 @@
{
"request": {
"api_url": "https://api.baremetrics.com/v1/TEST/subscriptions",
"headers": {
"Authorization": "Bearer TEST"
}
},
"response": {
"subscriptions": [],
"meta": {
"pagination": {
"has_more": false,
"page": 0,
"per_page": 30
}
}
},
"response-headers": {
"X-TokenExpires": "0",
"Server": "cloudflare-nginx",
"X-RateLimit-Remaining": "3593",
"X-Powered-By": "Phusion Passenger 5.0.30",
"Set-Cookie": "__cfduid=dd26464e3e6779b6a05b27d2681aea0851514374567; expires=Thu, 27-Dec-18 11:36:07 GMT; path=/; domain=.baremetrics.com; HttpOnly; Secure, LSW_WEB=\"LSW_WEB1\"; path=/",
"X-Runtime": "0.031730",
"Access-Control-Allow-Credentials": "false",
"Access-Control-Allow-Methods": "GET, OPTIONS, POST, PUT, DELETE",
"Content-Encoding": "gzip",
"Connection": "keep-alive",
"ETag": "W/\"110de39b319ed195bf1414ac4f3c9a01\"",
"Date": "Wed, 27 Dec 2017 11:36:08 GMT",
"X-RateLimit-Limit": "3600",
"X-Version": "721",
"X-Commit": "2dcec7190a2bd01c8d3d8527d386486c4bb5474c",
"Status": "200 OK",
"Content-Type": "application/json; charset=utf-8",
"Transfer-Encoding": "chunked",
"Access-Control-Allow-Headers": "Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type",
"Cache-Control": "max-age=0, private, must-revalidate",
"X-Request-Id": "0b292131-81bb-4df2-bdb9-089a50b17e63",
"CF-RAY": "3d3bfaf6feee2eff-DEL"
}
}

View file

@ -0,0 +1,53 @@
from zulip_bots.test_lib import BotTestCase
class TestBaremetricsBot(BotTestCase):
bot_name = "baremetrics"
def test_bot_responds_to_empty_message(self) -> None:
with self.mock_config_info({'api_key': 'TEST'}):
self.verify_reply('', 'No Command Specified')
def test_help_query(self) -> None:
with self.mock_config_info({'api_key': 'TEST'}):
self.verify_reply('help', '''
This bot gives updates about customer behavior, financial performance, and analytics
for an organization using the Baremetrics Api.\n
Enter `list-commands` to show the list of available commands.
Version 1.0
''')
def test_list_commands_command(self) -> None:
with self.mock_config_info({'api_key': 'TEST'}):
self.verify_reply('list-commands', '**Available Commands:** \n - help : Display bot info\n - list-commands '
': Display the list of available commands\n - account-info : Display '
'the account info\n - list-sources : List the sources\n - list-plans '
'<source_id> : List the plans for the source\n - list-customers '
'<source_id> : List the customers in the source\n - list-subscriptions '
'<source_id> : List the subscriptions in the source\n')
def test_account_info_command(self) -> None:
with self.mock_config_info({'api_key': 'TEST'}):
with self.mock_http_conversation('account_info'):
self.verify_reply('account-info', '**Your account information:** \nId: 376418\nCompany: NA\nDefault '
'Currency: United States Dollar')
def test_list_sources_command(self) -> None:
with self.mock_config_info({'api_key': 'TEST'}):
with self.mock_http_conversation('list_sources'):
self.verify_reply('list-sources', '**Listing sources:** \n1.ID: 5f7QC5NC0Ywgcu\nProvider: '
'baremetrics\nProvider ID: None\n\n')
def test_list_plans_command(self) -> None:
with self.mock_config_info({'api_key': 'TEST'}):
with self.mock_http_conversation('list_plans'):
self.verify_reply('list-plans TEST', '**Listing plans:** \n')
def test_list_customers_command(self) -> None:
with self.mock_config_info({'api_key': 'TEST'}):
with self.mock_http_conversation('list_customers'):
self.verify_reply('list-customers TEST', '**Listing customers:** \n')
def test_list_subscriptions_command(self) -> None:
with self.mock_config_info({'api_key': 'TEST'}):
with self.mock_http_conversation('list_subscriptions'):
self.verify_reply('list-subscriptions TEST', '**Listing subscriptions:** \n')