From 76c643349bf2a53ec2fec59b8cf7e50a91b01a08 Mon Sep 17 00:00:00 2001 From: Jun Woo Shin Date: Fri, 4 Aug 2017 01:30:41 -0400 Subject: [PATCH 1/4] make print statements use parentheses --- app.py | 12 ++++++------ handlers/trade.py | 4 ++-- markets/cryptos.py | 4 ++-- markets/stocks.py | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app.py b/app.py index 683eff8..a885283 100644 --- a/app.py +++ b/app.py @@ -14,19 +14,19 @@ BOT_ID = os.environ.get("SLACK_BOT_ID") # constants -AT_BOT = "<@" + BOT_ID + ">" +AT_BOT = "<@{}>".format(BOT_ID) # instantiate Slack client -slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN')) +slack_client = SlackClient(os.environ.get("SLACK_BOT_TOKEN")) # users manager object user_manager = HackcoinUserManager() def handle_command(command, channel, user_id): """ - Receives commands directed at the bot and determines if they - are valid commands. If so, then acts on the commands. If not, - returns back what it needs for clarification. + Receives commands directed at the bot and determines if they + are valid commands. If so, then acts on the commands. If not, + returns back what it needs for clarification. """ user_manager.load_user(user_id, channel=channel) @@ -76,7 +76,7 @@ def handle_command(command, channel, user_id): " :fastparrot: " ]) - print datetime.now(), user_manager.users[user_id]['first_name'], command_type, command + print(datetime.now(), user_manager.users[user_id]['first_name'], command_type, command) if response is not None: slack_client.api_call("chat.postMessage", channel=channel, diff --git a/handlers/trade.py b/handlers/trade.py index 1708edf..d1eeada 100644 --- a/handlers/trade.py +++ b/handlers/trade.py @@ -19,7 +19,7 @@ def buy(user_manager, command_tokens, user_id, channel=None): user_manager.buy_shares(ticker, shares, user_id, channel=channel) except Exception, e: - print e + print(e) return "Not sure what you mean. The *buy* command syntax is *buy* [ticker] [number of shares]" return None @@ -38,7 +38,7 @@ def sell(user_manager, command_tokens, user_id, channel=None): user_manager.sell_shares(ticker, shares, user_id, channel=channel) except Exception, e: - print e + print(e) return "Not sure what you mean. The *sell* command syntax is *sell* [ticker] [number of shares]" return None diff --git a/markets/cryptos.py b/markets/cryptos.py index 3cdb709..b92f1a0 100644 --- a/markets/cryptos.py +++ b/markets/cryptos.py @@ -37,9 +37,9 @@ def fetch_quote(symbol): } except Exception, e: - print e + print(e) return quote if __name__ == "__main__": - print fetch_quote('BTC') + print(fetch_quote('BTC')) diff --git a/markets/stocks.py b/markets/stocks.py index 700bd54..9fd6345 100644 --- a/markets/stocks.py +++ b/markets/stocks.py @@ -30,7 +30,7 @@ def fetch_quote(ticker): break except Exception, e: - print e + print(e) return quote @@ -49,7 +49,7 @@ def batch_fetch_quotes(tickers): try: data = json.loads(content[3:]) except Exception, e: - print e + print(e) for info in data: try: @@ -73,7 +73,7 @@ def batch_fetch_quotes(tickers): ticker = ticker.replace('.', '-') quotes[ticker] = quote except Exception, e: - print e + print(e) cur += TICKERS_PER_BATCH time.sleep(0.1) @@ -86,4 +86,4 @@ def batch_fetch_quotes(tickers): return quotes if __name__ == "__main__": - print fetch_quote('AAPL') + print(fetch_quote('AAPL')) From c5c90528eef53d8a4a590085d254bccdadebf75e Mon Sep 17 00:00:00 2001 From: Jun Woo Shin Date: Fri, 4 Aug 2017 01:33:15 -0400 Subject: [PATCH 2/4] use single quotations --- app.py | 70 +++++++++++++------------- handlers/get_price.py | 82 +++++++++++++++--------------- handlers/main.py | 38 +++++++------- handlers/portfolio.py | 16 +++--- handlers/trade.py | 8 +-- markets/cryptos.py | 18 +++---- markets/is_open.py | 8 +-- markets/stocks.py | 46 ++++++++--------- users/user.py | 114 +++++++++++++++++++++--------------------- 9 files changed, 200 insertions(+), 200 deletions(-) diff --git a/app.py b/app.py index a885283..4127215 100644 --- a/app.py +++ b/app.py @@ -11,85 +11,85 @@ # env variable bot_id -BOT_ID = os.environ.get("SLACK_BOT_ID") +BOT_ID = os.environ.get('SLACK_BOT_ID') # constants -AT_BOT = "<@{}>".format(BOT_ID) +AT_BOT = '<@{}>'.format(BOT_ID) # instantiate Slack client -slack_client = SlackClient(os.environ.get("SLACK_BOT_TOKEN")) +slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN')) # users manager object user_manager = HackcoinUserManager() def handle_command(command, channel, user_id): - """ + ''' Receives commands directed at the bot and determines if they are valid commands. If so, then acts on the commands. If not, returns back what it needs for clarification. - """ + ''' user_manager.load_user(user_id, channel=channel) # Set defaults. - response = "Type *@hackcoinbot help* for a guide!" + response = 'Type *@hackcoinbot help* for a guide!' attachment = [] - command_tokens = [w.lower() for w in command.strip().split(" ")] + command_tokens = [w.lower() for w in command.strip().split(' ')] command_type = get_command_type(command_tokens) - if command_type == "price": + if command_type == 'price': response, attachment = get_price(command_tokens) - if command_type == "buy": + if command_type == 'buy': response = buy(user_manager, command_tokens, user_id, channel=channel) - if command_type == "sell": + if command_type == 'sell': response = sell(user_manager, command_tokens, user_id, channel=channel) - if command_type == "balance": + if command_type == 'balance': response = user_manager.check_balance(user_id, channel=channel) - if command_type == "portfolio": + if command_type == 'portfolio': response, attachment = portfolio(user_manager, user_id, channel=channel) - if command_type == "leaderboard": + if command_type == 'leaderboard': response = user_manager.check_leaderboard(user_id, channel=channel) - if command_type == "help": + if command_type == 'help': response = print_help() - if command_type == "meme": + if command_type == 'meme': response = random.choice([ - " litecoin is a shit investment ", - " :seansmile: buy ethereum now !!", - " is tonight the night we go to MARU ?? ", - " :blondesassyparrot: cash me ousside :blondesassyparrot: how bout dah :blondesassyparrot: ", - " you have been blessed by a r a r e p u p :doge: ", - " g o o d b o i ", - " :sakibwouldlikethat: sakib would like that " + ' litecoin is a shit investment ', + ' :seansmile: buy ethereum now !!', + ' is tonight the night we go to MARU ?? ', + ' :blondesassyparrot: cash me ousside :blondesassyparrot: how bout dah :blondesassyparrot: ', + ' you have been blessed by a r a r e p u p :doge: ', + ' g o o d b o i ', + ' :sakibwouldlikethat: sakib would like that ' ]) - if command_type == "greet": + if command_type == 'greet': response = random.choice([ - " :wave: ", - " :seansmile: ", - " :fastparrot: " + ' :wave: ', + ' :seansmile: ', + ' :fastparrot: ' ]) print(datetime.now(), user_manager.users[user_id]['first_name'], command_type, command) if response is not None: - slack_client.api_call("chat.postMessage", channel=channel, + slack_client.api_call('chat.postMessage', channel=channel, text=response, attachments=attachment, as_user=True) def parse_slack_output(slack_rtm_output): - """ + ''' The Slack Real Time Messaging API is an events firehose. this parsing function returns None unless a message is directed at the Bot, based on its ID. - """ + ''' output_list = slack_rtm_output if not output_list: @@ -122,13 +122,13 @@ def listen(): CHECK_MARKET_REMINDER = True if slack_client.rtm_connect(): - print("hackcoinbot says hello!") + print('hackcoinbot says hello!') while True: if CHECK_MARKET_REMINDER and is_one_hour_left(): - slack_client.api_call("chat.postMessage", - channel="#tradingfloor", - text=" The stock market closes in *1 hour* :hourglass:", + slack_client.api_call('chat.postMessage', + channel='#tradingfloor', + text=' The stock market closes in *1 hour* :hourglass:', as_user=True) CHECK_MARKET_REMINDER = False @@ -137,8 +137,8 @@ def listen(): handle_command(command, channel, user_id) time.sleep(READ_WEBSOCKET_DELAY) else: - print("Connection failed. Invalid Slack token or bot ID?") + print('Connection failed. Invalid Slack token or bot ID?') -if __name__ == "__main__": +if __name__ == '__main__': listen() diff --git a/handlers/get_price.py b/handlers/get_price.py index 91ad1bb..41adeb3 100644 --- a/handlers/get_price.py +++ b/handlers/get_price.py @@ -13,25 +13,25 @@ def get_price(command_tokens): if quote == None: raise Exception('no quote found') except: - return "I couldn't find the price you wanted :cry:", None + return 'I couldn't find the price you wanted :cry:', None - title = "{} ({})".format( + title = '{} ({})'.format( quote['NAME'], quote['TICKER'] ) - bar_color = "good" + bar_color = 'good' try: if float(quote['CHANGE_AMT']) < 0: - bar_color = "danger" + bar_color = 'danger' except: pass - chart_url = "http://finviz.com/chart.ashx?t={}&ty=c&ta=1&p=d&s=l".format( + chart_url = 'http://finviz.com/chart.ashx?t={}&ty=c&ta=1&p=d&s=l'.format( quote['TICKER'] ) - change_text = "{} ({}%)".format( + change_text = '{} ({}%)'.format( quote['CHANGE_AMT'], quote['CHANGE'] ) @@ -40,28 +40,28 @@ def get_price(command_tokens): attachment = [ { - "fallback": "Check Price", - "color": bar_color, - "title": title, - "fields": [ + 'fallback': 'Check Price', + 'color': bar_color, + 'title': title, + 'fields': [ { - "title": "Price", - "value": quote['PRICE'], - "short": True + 'title': 'Price', + 'value': quote['PRICE'], + 'short': True }, { - "title": "Change", - "value": change_text, - "short": True + 'title': 'Change', + 'value': change_text, + 'short': True } ], - "image_url": chart_url, - "footer": "Google Finance | Finviz", - "ts": now + 'image_url': chart_url, + 'footer': 'Google Finance | Finviz', + 'ts': now } ] - return "", attachment + return '', attachment def get_crpyto_price(command_tokens): try: @@ -70,51 +70,51 @@ def get_crpyto_price(command_tokens): if quote == None: raise Exception('no quote found') except: - return "We don't support that coin yet :cry:", None + return 'We don't support that coin yet :cry:', None - title = "{} ({})".format( + title = '{} ({})'.format( quote['NAME'], quote['TICKER'] ) - bar_color = "good" + bar_color = 'good' try: if quote['CHANGE_AMT'] < 0: - bar_color = "danger" + bar_color = 'danger' except: pass change_pct = quote['CHANGE_AMT'] * 100.0 / (quote['PRICE'] - quote['CHANGE_AMT']) - change_text = "{:04.3f} ({:04.3f}%)".format( + change_text = '{:04.3f} ({:04.3f}%)'.format( quote['CHANGE_AMT'], change_pct ) attachment = [ { - "fallback": "Check Price", - "color": bar_color, - "title": title, - "fields": [ + 'fallback': 'Check Price', + 'color': bar_color, + 'title': title, + 'fields': [ { - "title": "Price", - "value": "{:05.3f}".format(quote['PRICE']), - "short": True + 'title': 'Price', + 'value': '{:05.3f}'.format(quote['PRICE']), + 'short': True }, { - "title": "Change (1hr)", - "value": change_text, - "short": True + 'title': 'Change (1hr)', + 'value': change_text, + 'short': True }, { - "title": "Volume (24hr)", - "value": "{:05.3f}".format(quote['VOLUME']), - "short": True + 'title': 'Volume (24hr)', + 'value': '{:05.3f}'.format(quote['VOLUME']), + 'short': True }, ], - "footer": "Google Finance | Finviz", - "ts": quote['TIMESTAMP'] + 'footer': 'Google Finance | Finviz', + 'ts': quote['TIMESTAMP'] } ] - return "", attachment + return '', attachment diff --git a/handlers/main.py b/handlers/main.py index f3543ab..99ade13 100644 --- a/handlers/main.py +++ b/handlers/main.py @@ -2,38 +2,38 @@ def get_command_type(command_tokens): if len(command_tokens) == 0: return None - if "$" in command_tokens[0]: - return "price" + if '$' in command_tokens[0]: + return 'price' - if command_tokens[0] in set(["buy", "b"]): - return "buy" + if command_tokens[0] in set(['buy', 'b']): + return 'buy' - if command_tokens[0] in set(["sell", "s"]): - return "sell" + if command_tokens[0] in set(['sell', 's']): + return 'sell' - if command_tokens[0] == "balance": - return "balance" + if command_tokens[0] == 'balance': + return 'balance' - if command_tokens[0] in set(["portfolio", "p"]): - return "portfolio" + if command_tokens[0] in set(['portfolio', 'p']): + return 'portfolio' - if command_tokens[0] in set(["leader", "leaderboard", "l", "top"]): - return "leaderboard" + if command_tokens[0] in set(['leader', 'leaderboard', 'l', 'top']): + return 'leaderboard' - if command_tokens[0] == "help": - return "help" + if command_tokens[0] == 'help': + return 'help' - if command_tokens[0] in set(["hello", "hey", "greet", "hi"]): - return "greet" + if command_tokens[0] in set(['hello', 'hey', 'greet', 'hi']): + return 'greet' - if command_tokens[0] in set(["meme", "shit", "shitpost"]): - return "meme" + if command_tokens[0] in set(['meme', 'shit', 'shitpost']): + return 'meme' def is_private_message(slack_client, channel_id): im_ids = [ x['id'] for x in - slack_client.api_call("im.list")['ims'] + slack_client.api_call('im.list')['ims'] ] return channel_id in set(im_ids) diff --git a/handlers/portfolio.py b/handlers/portfolio.py index 002f7e9..48262e0 100644 --- a/handlers/portfolio.py +++ b/handlers/portfolio.py @@ -7,13 +7,13 @@ def portfolio(user_manager, user_id, channel=None): attachment = [ { - "fallback": "Check Portfolio", - "color": "good", - "author_name": user_manager.users[user_id]['first_name'], - "author_icon": user_manager.get_user_thumbnail_url(user_id), - "title": "Portfolio", - "text": response, - "ts": now + 'fallback': 'Check Portfolio', + 'color': 'good', + 'author_name': user_manager.users[user_id]['first_name'], + 'author_icon': user_manager.get_user_thumbnail_url(user_id), + 'title': 'Portfolio', + 'text': response, + 'ts': now } ] - return "", attachment + return '', attachment diff --git a/handlers/trade.py b/handlers/trade.py index d1eeada..d98a66c 100644 --- a/handlers/trade.py +++ b/handlers/trade.py @@ -14,13 +14,13 @@ def buy(user_manager, command_tokens, user_id, channel=None): ticker = command_tokens[2] shares = float(command_tokens[1]) else: - return "Not sure what you mean. The *buy* command syntax is *buy* [ticker] [number of shares]" + return 'Not sure what you mean. The *buy* command syntax is *buy* [ticker] [number of shares]' user_manager.buy_shares(ticker, shares, user_id, channel=channel) except Exception, e: print(e) - return "Not sure what you mean. The *buy* command syntax is *buy* [ticker] [number of shares]" + return 'Not sure what you mean. The *buy* command syntax is *buy* [ticker] [number of shares]' return None @@ -33,12 +33,12 @@ def sell(user_manager, command_tokens, user_id, channel=None): ticker = command_tokens[2] shares = float(command_tokens[1]) else: - return "Not sure what you mean. The *sell* command syntax is *sell* [ticker] [number of shares]" + return 'Not sure what you mean. The *sell* command syntax is *sell* [ticker] [number of shares]' user_manager.sell_shares(ticker, shares, user_id, channel=channel) except Exception, e: print(e) - return "Not sure what you mean. The *sell* command syntax is *sell* [ticker] [number of shares]" + return 'Not sure what you mean. The *sell* command syntax is *sell* [ticker] [number of shares]' return None diff --git a/markets/cryptos.py b/markets/cryptos.py index b92f1a0..af0f378 100644 --- a/markets/cryptos.py +++ b/markets/cryptos.py @@ -21,19 +21,19 @@ def fetch_quote(symbol): quote = None try: - url = "https://api.cryptonator.com/api/ticker/{}-usd".format(symbol) + url = 'https://api.cryptonator.com/api/ticker/{}-usd'.format(symbol) u = urllib2.urlopen(url) content = u.read() data = json.loads(content) quote = { - "NAME": coins[symbol], - "TICKER": data["ticker"]["base"], - "PRICE": float(data["ticker"]["price"]), - "PRICEF": float(data["ticker"]["price"]), - "VOLUME": float(data["ticker"]["volume"]), - "CHANGE_AMT": float(data["ticker"]["change"]), - "TIMESTAMP": data["timestamp"] + 'NAME': coins[symbol], + 'TICKER': data['ticker']['base'], + 'PRICE': float(data['ticker']['price']), + 'PRICEF': float(data['ticker']['price']), + 'VOLUME': float(data['ticker']['volume']), + 'CHANGE_AMT': float(data['ticker']['change']), + 'TIMESTAMP': data['timestamp'] } except Exception, e: @@ -41,5 +41,5 @@ def fetch_quote(symbol): return quote -if __name__ == "__main__": +if __name__ == '__main__': print(fetch_quote('BTC')) diff --git a/markets/is_open.py b/markets/is_open.py index ab66676..07286bc 100644 --- a/markets/is_open.py +++ b/markets/is_open.py @@ -1,18 +1,18 @@ from datetime import datetime, time -def is_open(market = "stocks"): +def is_open(market = 'stocks'): now = datetime.now() weekday = now.weekday() a = time(hour=9, minute=30) b = time(hour=16) - if market == "stocks": + if market == 'stocks': return a <= now.time() <= b and weekday <= 4 -def is_one_hour_left(market = "stocks"): +def is_one_hour_left(market = 'stocks'): now = datetime.now() weekday = now.weekday() a = datetime(year=now.year, month=now.month, day=now.day, hour=15) b = datetime(year=now.year, month=now.month, day=now.day, hour=15, minute=1) - if market == "stocks": + if market == 'stocks': return a <= now <= b and weekday <= 4 diff --git a/markets/stocks.py b/markets/stocks.py index 9fd6345..13ddeba 100644 --- a/markets/stocks.py +++ b/markets/stocks.py @@ -9,23 +9,23 @@ def fetch_quote(ticker): try: # try different exchanges - url = "http://finance.google.com/finance/info?client=ig&infotype=infoquoteall&q={}".format(exchange+ticker) + url = 'http://finance.google.com/finance/info?client=ig&infotype=infoquoteall&q={}'.format(exchange+ticker) u = urllib2.urlopen(url) content = u.read() data = json.loads(content[3:]) info = data[0] - if "," in info['l']: + if ',' in info['l']: info['l'] = info['l'].replace(',','') quote = { - "NAME": info['name'], - "TICKER": info['t'], - "PRICE": info['l'], - "PRICEF": float(info['l']), - "CHANGE": float(info['cp']), - "CHANGE_AMT": info['c'], - "TIME": info['ltt'], - "DATE": datetime.strptime(info['lt_dts'], "%Y-%m-%dT%H:%M:%SZ") + 'NAME': info['name'], + 'TICKER': info['t'], + 'PRICE': info['l'], + 'PRICEF': float(info['l']), + 'CHANGE': float(info['cp']), + 'CHANGE_AMT': info['c'], + 'TIME': info['ltt'], + 'DATE': datetime.strptime(info['lt_dts'], '%Y-%m-%dT%H:%M:%SZ') } break @@ -42,8 +42,8 @@ def batch_fetch_quotes(tickers): quotes = {} while cur < len(tickers): - ticker_string = ",".join(tickers[cur:cur + TICKERS_PER_BATCH]) - url = "http://finance.google.com/finance/info?client=ig&infotype=infoquoteall&q={}".format(ticker_string) + ticker_string = ','.join(tickers[cur:cur + TICKERS_PER_BATCH]) + url = 'http://finance.google.com/finance/info?client=ig&infotype=infoquoteall&q={}'.format(ticker_string) u = urllib2.urlopen(url) content = u.read() try: @@ -53,23 +53,23 @@ def batch_fetch_quotes(tickers): for info in data: try: - if "," in info['l']: + if ',' in info['l']: info['l'] = info['l'].replace(',','') quote = { - "NAME": info['name'], - "TICKER": info['t'], - "PRICE": info['l'], - "PRICEF": float(info['l']), - "CHANGE": float(info['cp']), - "CHANGE_AMT": info['c'], - "TIME": info['ltt'], - "DATE": datetime.strptime(info['lt_dts'], "%Y-%m-%dT%H:%M:%SZ") + 'NAME': info['name'], + 'TICKER': info['t'], + 'PRICE': info['l'], + 'PRICEF': float(info['l']), + 'CHANGE': float(info['cp']), + 'CHANGE_AMT': info['c'], + 'TIME': info['ltt'], + 'DATE': datetime.strptime(info['lt_dts'], '%Y-%m-%dT%H:%M:%SZ') } ticker = info['t'] - if "." in ticker: + if '.' in ticker: ticker = ticker.replace('.', '-') quotes[ticker] = quote except Exception, e: @@ -85,5 +85,5 @@ def batch_fetch_quotes(tickers): return quotes -if __name__ == "__main__": +if __name__ == '__main__': print(fetch_quote('AAPL')) diff --git a/users/user.py b/users/user.py index f107d21..7daf4f0 100644 --- a/users/user.py +++ b/users/user.py @@ -76,7 +76,7 @@ def check_balance(self, user_id, channel=None): balance += total_value response = '{} has a balance of {} Hackcoins :money_with_wings:'.format( - self.users[user_id]["first_name"], + self.users[user_id]['first_name'], balance ) @@ -91,18 +91,18 @@ def check_portfolio(self, user_id, channel=None): for symbol in crypto_symbols: quotes[symbol] = cryptos.fetch_quote(symbol) - response = "" + response = '' balance = 0 coins = self.users[user_id]['coins'] balance += coins - response += "{} Hackcoins :coin: \n".format(coins) + response += '{} Hackcoins :coin: \n'.format(coins) for ticker in tickers: - type_text = "shares" + type_text = 'shares' if ticker[-2:] == '.c': - type_text = "coins" + type_text = 'coins' shares = self.users[user_id]['positions'][ticker]['shares'] avg_price = float(self.users[user_id]['positions'][ticker]['average_price']) @@ -113,7 +113,7 @@ def check_portfolio(self, user_id, channel=None): net_profit = (stock_price - avg_price) * shares balance += (stock_price * shares) - response += "{:5} {:5} {} (buy {:04.2f} | now {:04.2f} | net profit {:04.2f})\n".format( + response += '{:5} {:5} {} (buy {:04.2f} | now {:04.2f} | net profit {:04.2f})\n'.format( ticker, shares, type_text, @@ -122,7 +122,7 @@ def check_portfolio(self, user_id, channel=None): net_profit ) - response += "\nTotal account value = {:04.2f} Hackcoins :coin: \n".format(balance) + response += '\nTotal account value = {:04.2f} Hackcoins :coin: \n'.format(balance) return response @@ -146,20 +146,20 @@ def check_leaderboard(self, user_id, channel=None): stock_price = quotes[ticker]['PRICEF'] total_value = stock_price * shares balance += total_value - balance = float("{:06.2f}".format(balance)) + balance = float('{:06.2f}'.format(balance)) balance_tuples.append((user_id, balance)) balance_tuples.sort(key=lambda x: x[1]) balance_tuples = balance_tuples[::-1] - response = ":racehorse: Hackcoin leaderboard :racehorse: \n" + response = ':racehorse: Hackcoin leaderboard :racehorse: \n' for i, balance in enumerate(balance_tuples): - response += "{})\t{}\t{} ".format(i+1, self.users[balance[0]]['first_name'], balance[1]) + response += '{})\t{}\t{} '.format(i+1, self.users[balance[0]]['first_name'], balance[1]) if i == 0: - response += " :100:" + response += ' :100:' - response += "\n" + response += '\n' return response def buy_shares(self, ticker, shares, user_id, channel=None): @@ -204,7 +204,7 @@ def buy_shares(self, ticker, shares, user_id, channel=None): self.users[user_id]['positions'][ticker]['shares'] = shares self.users[user_id]['positions'][ticker]['average_price'] = stock_price - response = "" + response = '' if is_crypto: response_text = '{} coins of {} bought at {} each (total {} Hackcoins)'.format( @@ -224,35 +224,35 @@ def buy_shares(self, ticker, shares, user_id, channel=None): average_price = self.users[user_id]['positions'][ticker]['average_price'] cumul_pct = (stock_price - average_price) * 100.0 / average_price - attach_color = "good" + attach_color = 'good' if cumul_pct < 0: - attach_color = "danger" + attach_color = 'danger' - title_text = "Shares purchased!" + title_text = 'Shares purchased!' if is_crypto: - title_text = "Coins purchased!" + title_text = 'Coins purchased!' attachment = [ { - "fallback": title_text, - "color": attach_color, - "author_name": self.users[user_id]['first_name'], - "author_icon": self.get_user_thumbnail_url(user_id), - "title": title_text, - "text": response_text, - "fields": [ + 'fallback': title_text, + 'color': attach_color, + 'author_name': self.users[user_id]['first_name'], + 'author_icon': self.get_user_thumbnail_url(user_id), + 'title': title_text, + 'text': response_text, + 'fields': [ { - "title": "Average Buy Price", - "value": "{:04.2f} (overall {:04.2f}%)".format(average_price, cumul_pct), - "short": False + 'title': 'Average Buy Price', + 'value': '{:04.2f} (overall {:04.2f}%)'.format(average_price, cumul_pct), + 'short': False }, { - "title": "Remaining Coins", - "value": "{:04.2f}".format(self.users[user_id]['coins']), - "short": False + 'title': 'Remaining Coins', + 'value': '{:04.2f}'.format(self.users[user_id]['coins']), + 'short': False } ], - "ts": now + 'ts': now } ] else: @@ -263,9 +263,9 @@ def buy_shares(self, ticker, shares, user_id, channel=None): total_can_buy = shares_can_buy * stock_price - type_text = "shares" + type_text = 'shares' if is_crypto: - type_text = "coins" + type_text = 'coins' response = 'You can buy up to *{}* {} of {} for a total of *{}* Hackcoins :take_my_money:'.format( shares_can_buy, @@ -274,7 +274,7 @@ def buy_shares(self, ticker, shares, user_id, channel=None): total_can_buy ) else: - response = 'Markets are closed right now {} :scream:'.format(self.users[user_id]["first_name"]) + response = 'Markets are closed right now {} :scream:'.format(self.users[user_id]['first_name']) # notify their account balance slack_client.api_call('chat.postMessage', @@ -291,9 +291,9 @@ def sell_shares(self, ticker, shares, user_id, channel=None): except: is_crypto = False - type_text = "shares" + type_text = 'shares' if is_crypto: - type_text = "coins" + type_text = 'coins' attachment = [] now = datetime.now().strftime('%s') @@ -321,7 +321,7 @@ def sell_shares(self, ticker, shares, user_id, channel=None): if self.users[user_id]['positions'][ticker]['shares'] == 0: del self.users[user_id]['positions'][ticker] - response = "" + response = '' response_text = '{} {} of {} sold at {} each (total {} coins)'.format( shares, @@ -334,36 +334,36 @@ def sell_shares(self, ticker, shares, user_id, channel=None): cumul_pct = (stock_price - average_price) * 100.0 / average_price net_profit = shares * 1.0 * (stock_price - average_price) - attach_color = "good" + attach_color = 'good' if cumul_pct < 0: - attach_color = "danger" + attach_color = 'danger' attachment = [ { - "fallback": "{} sold!".format(type_text.capitalize()), - "color": attach_color, - "author_name": self.users[user_id]['first_name'], - "author_icon": self.get_user_thumbnail_url(user_id), - "title": "{} sold!".format(type_text.capitalize()), - "text": response_text, - "fields": [ + 'fallback': '{} sold!'.format(type_text.capitalize()), + 'color': attach_color, + 'author_name': self.users[user_id]['first_name'], + 'author_icon': self.get_user_thumbnail_url(user_id), + 'title': '{} sold!'.format(type_text.capitalize()), + 'text': response_text, + 'fields': [ { - "title": "Average Buy Price", - "value": "{:04.2f}".format(average_price), - "short": False + 'title': 'Average Buy Price', + 'value': '{:04.2f}'.format(average_price), + 'short': False }, { - "title": "Total Return", - "value": "{:04.2f} coins (overall {:04.2f}%)".format(net_profit, cumul_pct), - "short": False + 'title': 'Total Return', + 'value': '{:04.2f} coins (overall {:04.2f}%)'.format(net_profit, cumul_pct), + 'short': False }, { - "title": "Remaining Coins", - "value": "{:04.2f}".format(self.users[user_id]['coins']), - "short": False + 'title': 'Remaining Coins', + 'value': '{:04.2f}'.format(self.users[user_id]['coins']), + 'short': False } ], - "ts": now + 'ts': now } ] else: @@ -372,7 +372,7 @@ def sell_shares(self, ticker, shares, user_id, channel=None): ticker ) else: - response = 'Markets are closed right now {} :scream:'.format(self.users[user_id]["first_name"]) + response = 'Markets are closed right now {} :scream:'.format(self.users[user_id]['first_name']) # notify their account balance slack_client.api_call('chat.postMessage', From 5ef3cde22a902151cdc196fbf3ede78f16a9c00e Mon Sep 17 00:00:00 2001 From: Jun Woo Shin Date: Fri, 4 Aug 2017 02:24:53 -0400 Subject: [PATCH 3/4] some smaller refactors --- app.py | 10 ++++----- handlers/get_price.py | 4 ++-- handlers/main.py | 14 ++++++------- handlers/trade.py | 25 +++++++++-------------- markets/cryptos.py | 36 +++++++++++++++++---------------- markets/is_open.py | 10 +++++++-- markets/stocks.py | 47 ++++++++++++++++++++++++------------------- requirements.txt | 1 + utils.py | 13 ++++++++++++ 9 files changed, 90 insertions(+), 70 deletions(-) create mode 100644 utils.py diff --git a/app.py b/app.py index 4127215..ad6a236 100644 --- a/app.py +++ b/app.py @@ -85,11 +85,11 @@ def handle_command(command, channel, user_id): as_user=True) def parse_slack_output(slack_rtm_output): - ''' - The Slack Real Time Messaging API is an events firehose. - this parsing function returns None unless a message is - directed at the Bot, based on its ID. - ''' + """ + The Slack Real Time Messaging API is an events firehose. + this parsing function returns None unless a message is + directed at the Bot, based on its ID. + """ output_list = slack_rtm_output if not output_list: diff --git a/handlers/get_price.py b/handlers/get_price.py index 41adeb3..6df3e94 100644 --- a/handlers/get_price.py +++ b/handlers/get_price.py @@ -13,7 +13,7 @@ def get_price(command_tokens): if quote == None: raise Exception('no quote found') except: - return 'I couldn't find the price you wanted :cry:', None + return "I couldn't find the price you wanted :cry:", None title = '{} ({})'.format( quote['NAME'], @@ -70,7 +70,7 @@ def get_crpyto_price(command_tokens): if quote == None: raise Exception('no quote found') except: - return 'We don't support that coin yet :cry:', None + return "We don't support that coin yet :cry:", None title = '{} ({})'.format( quote['NAME'], diff --git a/handlers/main.py b/handlers/main.py index 99ade13..d05add2 100644 --- a/handlers/main.py +++ b/handlers/main.py @@ -1,32 +1,32 @@ def get_command_type(command_tokens): - if len(command_tokens) == 0: + if not command_tokens: return None if '$' in command_tokens[0]: return 'price' - if command_tokens[0] in set(['buy', 'b']): + if command_tokens[0] in ('buy', 'b'): return 'buy' - if command_tokens[0] in set(['sell', 's']): + if command_tokens[0] in ('sell', 's'): return 'sell' if command_tokens[0] == 'balance': return 'balance' - if command_tokens[0] in set(['portfolio', 'p']): + if command_tokens[0] in ('portfolio', 'p'): return 'portfolio' - if command_tokens[0] in set(['leader', 'leaderboard', 'l', 'top']): + if command_tokens[0] in ('leader', 'leaderboard', 'l', 'top'): return 'leaderboard' if command_tokens[0] == 'help': return 'help' - if command_tokens[0] in set(['hello', 'hey', 'greet', 'hi']): + if command_tokens[0] in ('hello', 'hey', 'greet', 'hi'): return 'greet' - if command_tokens[0] in set(['meme', 'shit', 'shitpost']): + if command_tokens[0] in ('meme', 'shit', 'shitpost'): return 'meme' def is_private_message(slack_client, channel_id): diff --git a/handlers/trade.py b/handlers/trade.py index d98a66c..89f03f3 100644 --- a/handlers/trade.py +++ b/handlers/trade.py @@ -1,18 +1,13 @@ -def is_float(s): - try: - float(s) - return True - except ValueError: - return False +from utils import coerce_decimal + def buy(user_manager, command_tokens, user_id, channel=None): try: - if is_float(command_tokens[2]): + val = coerce_decimal(command_tokens[2]) + + if not val is None: ticker = command_tokens[1] - shares = float(command_tokens[2]) - elif is_float(command_tokens[1]): - ticker = command_tokens[2] - shares = float(command_tokens[1]) + shares = val else: return 'Not sure what you mean. The *buy* command syntax is *buy* [ticker] [number of shares]' @@ -26,12 +21,10 @@ def buy(user_manager, command_tokens, user_id, channel=None): def sell(user_manager, command_tokens, user_id, channel=None): try: - if is_float(command_tokens[2]): + val = coerce_decimal(command_tokens[2]) + if not val is None: ticker = command_tokens[1] - shares = float(command_tokens[2]) - elif is_float(command_tokens[1]): - ticker = command_tokens[2] - shares = float(command_tokens[1]) + shares = val else: return 'Not sure what you mean. The *sell* command syntax is *sell* [ticker] [number of shares]' diff --git a/markets/cryptos.py b/markets/cryptos.py index af0f378..ddd65eb 100644 --- a/markets/cryptos.py +++ b/markets/cryptos.py @@ -1,42 +1,44 @@ -from datetime import datetime -import time -import json -import urllib2 +from decimal import Decimal + import requests -coins = { + +COINS = { 'btc': 'Bitcoin', 'eth': 'Ethereum', 'ltc': 'Litecoin', 'bch': 'Bitcoin Cash' } +BASE_URL = 'https://api.cryptonator.com/api/ticker/{}-usd' + def fetch_quote(symbol): symbol = symbol.lower() - if symbol[-2:] in set(['-c', '.c']): + + if symbol[-2:] in ('-c', '.c'): symbol = symbol[:-2] - if symbol not in coins.keys(): + + if symbol not in COINS: return None quote = None try: - url = 'https://api.cryptonator.com/api/ticker/{}-usd'.format(symbol) - u = urllib2.urlopen(url) - content = u.read() - data = json.loads(content) + url = BASE_URL.format(symbol) + resp = requests.get(url) + data = resp.json() quote = { - 'NAME': coins[symbol], + 'NAME': COINS[symbol], 'TICKER': data['ticker']['base'], - 'PRICE': float(data['ticker']['price']), - 'PRICEF': float(data['ticker']['price']), - 'VOLUME': float(data['ticker']['volume']), - 'CHANGE_AMT': float(data['ticker']['change']), + 'PRICE': Decimal(data['ticker']['price']), + 'PRICEF': Decimal(data['ticker']['price']), + 'VOLUME': Decimal(data['ticker']['volume']), + 'CHANGE_AMT': Decimal(data['ticker']['change']), 'TIMESTAMP': data['timestamp'] } - except Exception, e: + except Exception as e: print(e) return quote diff --git a/markets/is_open.py b/markets/is_open.py index 07286bc..dd31254 100644 --- a/markets/is_open.py +++ b/markets/is_open.py @@ -1,18 +1,24 @@ from datetime import datetime, time -def is_open(market = 'stocks'): +def is_open(market='stocks'): now = datetime.now() weekday = now.weekday() a = time(hour=9, minute=30) b = time(hour=16) + if market == 'stocks': return a <= now.time() <= b and weekday <= 4 + else: + return True -def is_one_hour_left(market = 'stocks'): +def is_one_hour_left(market='stocks'): now = datetime.now() weekday = now.weekday() a = datetime(year=now.year, month=now.month, day=now.day, hour=15) b = datetime(year=now.year, month=now.month, day=now.day, hour=15, minute=1) + if market == 'stocks': return a <= now <= b and weekday <= 4 + else: + return True diff --git a/markets/stocks.py b/markets/stocks.py index 13ddeba..3e2c96b 100644 --- a/markets/stocks.py +++ b/markets/stocks.py @@ -1,7 +1,11 @@ from datetime import datetime +from decimal import Decimal import time import json -import urllib2 + + +BASE_URL = 'http://finance.google.com/finance/info?client=ig&infotype=infoquoteall&q={}' +TICKERS_PER_BATCH = 100 def fetch_quote(ticker): quote = None @@ -9,59 +13,59 @@ def fetch_quote(ticker): try: # try different exchanges - url = 'http://finance.google.com/finance/info?client=ig&infotype=infoquoteall&q={}'.format(exchange+ticker) - u = urllib2.urlopen(url) - content = u.read() - data = json.loads(content[3:]) - info = data[0] + url = BASE_URL.format(exchange + ticker) + resp = requests.get(url) + data = json.loads(resp.text[3:]) + info = data[0] if ',' in info['l']: - info['l'] = info['l'].replace(',','') - quote = { + info['l'] = info['l'].replace(',', '') + + quote = { 'NAME': info['name'], 'TICKER': info['t'], 'PRICE': info['l'], - 'PRICEF': float(info['l']), - 'CHANGE': float(info['cp']), + 'PRICEF': Decimal(info['l']), + 'CHANGE': Decimal(info['cp']), 'CHANGE_AMT': info['c'], 'TIME': info['ltt'], 'DATE': datetime.strptime(info['lt_dts'], '%Y-%m-%dT%H:%M:%SZ') } + break - except Exception, e: + except Exception as e: print(e) return quote def batch_fetch_quotes(tickers): # Cap to 100 tickers per call (max api limit) - TICKERS_PER_BATCH = 100 cur = 0 quotes = {} while cur < len(tickers): ticker_string = ','.join(tickers[cur:cur + TICKERS_PER_BATCH]) - url = 'http://finance.google.com/finance/info?client=ig&infotype=infoquoteall&q={}'.format(ticker_string) - u = urllib2.urlopen(url) - content = u.read() + url = BASE_URL.format(ticker_string) + resp = requests.get(url) + try: - data = json.loads(content[3:]) + data = json.loads(resp.text[3:]) except Exception, e: print(e) for info in data: try: if ',' in info['l']: - info['l'] = info['l'].replace(',','') + info['l'] = info['l'].replace(',', '') - quote = { + quote = { 'NAME': info['name'], 'TICKER': info['t'], 'PRICE': info['l'], - 'PRICEF': float(info['l']), - 'CHANGE': float(info['cp']), + 'PRICEF': Decimal(info['l']), + 'CHANGE': Decimal(info['cp']), 'CHANGE_AMT': info['c'], 'TIME': info['ltt'], 'DATE': datetime.strptime(info['lt_dts'], '%Y-%m-%dT%H:%M:%SZ') @@ -71,8 +75,9 @@ def batch_fetch_quotes(tickers): if '.' in ticker: ticker = ticker.replace('.', '-') + quotes[ticker] = quote - except Exception, e: + except Exception as e: print(e) cur += TICKERS_PER_BATCH diff --git a/requirements.txt b/requirements.txt index e5704ff..67959a8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ slackclient +requests diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..f92c897 --- /dev/null +++ b/utils.py @@ -0,0 +1,13 @@ +from decimal import Decimal, InvalidOperation + + +def coerce_decimal(s): + val = None + + try: + val = Decimal(s) + except InvalidOperation: + pass + + return val + From 82f1b650223c2e4b59d08ba0d43b1a81e632622c Mon Sep 17 00:00:00 2001 From: Jun Woo Shin Date: Fri, 4 Aug 2017 02:36:23 -0400 Subject: [PATCH 4/4] WIP --- app.py | 20 +++++++++++++++++++- markets/stocks.py | 2 ++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app.py b/app.py index ad6a236..928e2f0 100644 --- a/app.py +++ b/app.py @@ -116,6 +116,19 @@ def parse_slack_output(slack_rtm_output): return None, None, None +def normalize_events(events): + okay_events = [] + + for event in events: + is_bot = (event.get('bot_id') is not None) and (event.get('user') != BOT_ID) + has_text = event.get('text') is not None + + if is_bot or not has_text: + continue + + is_direct_message = event['channel'][0] == 'D' + okay_events.append(event) + def listen(): # 0.25 second delay between reading from firehose READ_WEBSOCKET_DELAY = 0.25 @@ -125,6 +138,8 @@ def listen(): print('hackcoinbot says hello!') while True: + data_read = slack_client.rtm_read() + if CHECK_MARKET_REMINDER and is_one_hour_left(): slack_client.api_call('chat.postMessage', channel='#tradingfloor', @@ -132,7 +147,10 @@ def listen(): as_user=True) CHECK_MARKET_REMINDER = False - command, channel, user_id = parse_slack_output(slack_client.rtm_read()) + print('\n\n') + print(data_read) + print('\n\n') + command, channel, user_id = parse_slack_output(data_read) if command and channel and user_id: handle_command(command, channel, user_id) time.sleep(READ_WEBSOCKET_DELAY) diff --git a/markets/stocks.py b/markets/stocks.py index 3e2c96b..56add55 100644 --- a/markets/stocks.py +++ b/markets/stocks.py @@ -3,6 +3,8 @@ import time import json +import requests + BASE_URL = 'http://finance.google.com/finance/info?client=ig&infotype=infoquoteall&q={}' TICKERS_PER_BATCH = 100