Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hodl #17

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
154 changes: 146 additions & 8 deletions legos/cryptocurrency.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,28 @@
import requests
import logging
import json
import configparser

logger = logging.getLogger(__name__)


class Cryptocurrency(Lego):
def __init__(self, baseplate, lock):
super().__init__(baseplate, lock)
self.crypto_index = configparser.ConfigParser()
self.crypto_index.read('crypto_index.ini')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to use a config file, you should check for presence first. I recommend making a configure method to handle this part.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed by 59347c9

if 'crypto_index' not in self.crypto_index.sections():
self.crypto_index['crypto_index'] = {}
self.crypto_index['crypto_index']['fsyms'] = 'BTC'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the Cryptowatch may have used the horribly cryptic fsyms and tsyms, but that doesn't mean we have to. It would probably benefit the user if we made these terms more clear and documented what they correspond to.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved.
changed crypto_index to hodl
changed fsyms to convert_from
changed tsyms to convert_to

self.crypto_index['crypto_index']['tsyms'] = 'USD,BTC'
with open('crypto_index.ini', 'w') as configfile:
self.crypto_index.write(configfile)

def listening_for(self, message):
if message['text'] is not None:
try:
return message['text'].split()[0] == '!crypto'
command = message['text'].split()[0]
return command == '!crypto' or command == '!hodl'
except Exception as e:
logger.error('''Stocks lego failed to check message text:
{}'''.format(e))
Expand All @@ -24,12 +37,125 @@ def handle(self, message):
logger.error('''Could not identify message source in message:
{}'''.format(message))

message_list = message['text'].split()
if message_list[0] == '!hodl':
self.reply(message, self._parse_multi_commands(message_list), opts)
else:
try:
query = message['text'].split()[1]
except:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're splitting and looking for an index here, the exception you're almost certainly going to raise is an IndexError, which is what I would catch. Upon catching the index error, I'd then return a helpful message indicating as much.

try:
    query: message['text'].split()[1]
except IndexError:
    self.reply(message, "Not enough arguments or something", opts)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in 9aecef0
and
090a524

self.reply(message, "Invalid query", opts)
self.reply(message, self._lookup_symbol(query), opts)

def _parse_multi_commands(self, message_list):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're falling into the same trap we fell into with the proto-legobot back in 2013-14. This pattern didn't scale well for us back then and probably won't in this case either.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'splain this one more plz

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We tried to write something along a similar pattern where we'd basically split the message and then branch on each option. It ended up getting messy, which was what drove us to the plugin model.

Using argparse for positional arguments or splitting hodl into another lego might be the way to go given how many branches we're starting to accumulate.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved by moving hodl to its own lego

if len(message_list) == 1:
return self._lookup_multi()
elif len(message_list) > 1:
case_dict = {}
case_dict['list'] = self._list_hodl_symbols
case_dict['add'] = self._add_symbols
case_dict['drop'] = self._drop_symbols
return case_dict[message_list[1]](message_list)

def _list_hodl_symbols(self, message_list):
fsyms = self.crypto_index['crypto_index']['fsyms']
tsyms = self.crypto_index['crypto_index']['tsyms']
return_val = ''
return_val += 'Convert From: ' + fsyms + '\n'
return_val += 'Convert To: ' + tsyms + '\n'
return return_val

def _add_symbols(self, message_list):
if len(message_list) < 3:
return ('Please supply additional arguments, e.g.:\n'
'!hodl add from BTC,LTC')
elif message_list[2] == 'from':
return self._add_from_symbols(message_list)
elif message_list[2] == 'to':
return self._add_to_symbols(message_list)
else:
return 'There was an issue processing your request.'

def _add_from_symbols(self, message_list):
try:
add_fsyms = message_list[3].split(',')
old_fsyms = self.crypto_index['crypto_index']['fsyms'].split(',')
new_fsyms = list(set(old_fsyms + add_fsyms))
self.crypto_index['crypto_index']['fsyms'] = ','.join(new_fsyms)
with open('crypto_index.ini', 'w') as configfile:
self.crypto_index.write(configfile)
return self._list_hodl_symbols(message_list)
except:
return ('Please supply additional arguments, e.g.:\n'
'!hodl add from BTC,LTC')

def _add_to_symbols(self, message_list):
try:
query = message['text'].split()[1]
add_tsyms = message_list[3].split(',')
old_tsyms = self.crypto_index['crypto_index']['tsyms'].split(',')
new_tsyms = list(set(old_tsyms + add_tsyms))
self.crypto_index['crypto_index']['tsyms'] = ','.join(new_tsyms)
with open('crypto_index.ini', 'w') as configfile:
self.crypto_index.write(configfile)
return self._list_hodl_symbols(message_list)
except:
self.reply(message, "Invalid query", opts)
return ('Please supply additional arguments, e.g.:\n'
'!hodl add to BTC,LTC')

self.reply(message, self._lookup_symbol(query), opts)
def _drop_symbols(self, message_list):
if len(message_list) < 3:
return ('Please supply additional arguments, e.g.:\n'
'!hodl drop from BTC')
elif message_list[2] == 'from':
return self._drop_from_symbols(message_list)
elif message_list[2] == 'to':
return self._drop_to_symbols(message_list)
else:
return 'There was an issue processing your request.'

def _drop_from_symbols(self, message_list):
try:
drop_fsyms = message_list[3].split(',')
old_fsyms = self.crypto_index['crypto_index']['fsyms'].split(',')
new_fsyms = set(old_fsyms).symmetric_difference(set(drop_fsyms))
self.crypto_index['crypto_index']['fsyms'] = ','.join(new_fsyms)
with open('crypto_index.ini', 'w') as configfile:
self.crypto_index.write(configfile)
return self._list_hodl_symbols(message_list)
except:
return ('Please supply additional arguments, e.g.:\n'
'!hodl drop from BTC,LTC')

def _drop_to_symbols(self, message_list):
try:
drop_tsyms = message_list[3].split(',')
old_tsyms = self.crypto_index['crypto_index']['tsyms'].split(',')
new_tsyms = set(old_tsyms).symmetric_difference(set(drop_tsyms))
self.crypto_index['crypto_index']['tsyms'] = ','.join(new_tsyms)
with open('crypto_index.ini', 'w') as configfile:
self.crypto_index.write(configfile)
return self._list_hodl_symbols(message_list)
except:
return ('Please supply additional arguments, e.g.:\n'
'!hodl drop to BTC,LTC')

def _lookup_multi(self):
multi_url = self._build_index_url()
get_multi = requests.get(multi_url)
if get_multi.status_code == requests.codes.ok:
api_response = json.loads(get_multi.text)
return self._parse_multi_price_response(api_response)
else:
return 'There was an error getting the data: ' + get_multi.text

def _parse_multi_price_response(self, api_response):
return_val = ''
for key, value in api_response.items():
return_val += key + ': | '
for key, value in value.items():
return_val += '{} {} | '.format(value, key)
return_val += '\n'
return return_val

def _lookup_symbol(self, query):
query = query.upper()
Expand All @@ -40,9 +166,9 @@ def _lookup_symbol(self, query):
params['tsyms'] = 'USD' # tsyms, the SYMbolS to convert To
else:
params['tsyms'] = 'USD,BTC' # tsyms, the SYMbolS to convert To
request_url = self._build_index_url()
api_response = requests.get(request_url, params=params)
if api_response.status_code == requests.codes.ok:
api_response = json.loads(api_response.text)
if 'There is no data for the symbol' in api_response['Message']:
matched_items = self._search_symbol(query)
params['fsym'] = matched_items[0]['symbol']
Expand Down Expand Up @@ -89,10 +215,22 @@ def _parse_api_response(self, api_response, query, **kwargs):
return_val = 'WOW! {} TO THE MOON!!!'.format(return_val)
return return_val

def _build_index_url(self):
baseurl = 'https://min-api.cryptocompare.com/data/pricemulti'
fsyms = self.crypto_index['crypto_index']['fsyms']
tsyms = self.crypto_index['crypto_index']['tsyms']
return baseurl + '?fsyms=' + fsyms + '&tsyms=' + tsyms

def get_name(self):
return 'crypto'

def get_help(self):
return '''Lookup a crypto symbol's value. Usage: !crypto <symbol>.
List of symbols here
https://min-api.cryptocompare.com/data/all/coinlist.'''
return ('Lookup a crypto symbol\'s value. Usage: !crypto <symbol>.\n'
'List of symbols here: '
'https://min-api.cryptocompare.com/data/all/coinlist.\n'
'Usage for !hodl:\n'
'!hodl -- returns prices for multiple coins at once.\n'
'!hodl list -- returns the list of coins to convert from/to.\n'
'!hodl <add/drop> <to/from> <comma separated symbols list>'
' -- adds or drop list of symbols to the convert to or convert'
' from list.')
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
requests
configparser
bandit==1.3.0
pytest==3.0.5
flake8==3.2.1
Expand Down