def add_type_to_sale(user, channel, command, purchase_id): # check that a type was indicated in the command response = "something went wrong - add_type_to_sale" db = database.Database() # get supported coins supported_coins = db.fetchAll( "select * from supported_coins order by sort_order desc") db.close() # did user give type? coin = None for supported_coin in supported_coins: if supported_coin["coin_name"].lower() in command: coin = supported_coin["coin_id"] break if coin == None: response = "Sorry, you didn't give me a valid crypto type." bot_utilities.log_event("expected coin type and failed:" + user + ": " + command) else: db = database.Database() db.runSql( """update sales set coin_type = %s, last_updated = now(), step = "amount" where purchase_id = %s""", [coin, purchase_id]) db.close() response = "Sounds great. How much " + coin.upper() + " did you sell?" bot_utilities.post_to_channel(channel, response)
def show_prices(user, channel, command): response = "" db = database.Database() coins = db.fetchAll( "select * from supported_coins order by sort_order asc") for coin in coins: # fetch the previous pice records = db.fetchAll( """select * from price_history where coin = %s and date > now()-interval 24 hour order by date asc limit 1""", [coin['coin_id']]) current_price = bot_utilities.get_current_price( coin['coin_id'].lower()) try: # in case prices didn't get logged start_price = records[0]['price'] change_pct = bot_utilities.floored_percentage( (current_price - float(start_price)) / float(start_price), 2) except: start_price = 0 change_pct = "error" # add to response response = response + "*" + coin['coin_id'] + " " + coin[ 'coin_name'] + ":* $" + str(current_price) + " _(" + str( change_pct) + ")_ \n" bot_utilities.post_to_channel(channel, response) bot_utilities.log_event(user + " requested prices: " + command)
def list_transactions(user, channel, command): response = "something went wrong - list_transactions" db = database.Database() records = db.fetchAll( """ select *, "purchase" as type from purchases where user_id = %s and record_complete = 1 union select * , "sale" as type from sales where user_id = %s and record_complete = 1 """, [user, user]) db.close() response = "ID | trans | coin | amount | USD | date \n" for record in records: response = response + str(record["purchase_id"]) + " | " + record[ "type"] + " | " + record["coin_type"].upper() + " | " + str( record["amount"]) + " | $" + str( record["usd_spent"]) + " | " + str(record["date"]) + "\n" bot_utilities.post_to_channel(channel, response) bot_utilities.log_event(user + " listed transactions")
def delete_transaction(user, channel, command, type="purchase"): response = "something went wrong - delete_transaction" # make sure we have a number in the command trans_number = bot_utilities.parse_number_from_command(command) if trans_number == -9999: response = "You need to give me a transaction number. \"List Transactions\" will show you a list of your transactions." bot_utilities.log_event( user + " attempted to delete a transaction but provided no transaction number: " + command) else: # we have a transaction db = database.Database() if type == "purchase": records = db.fetchAll( """select * from purchases where purchase_id = %s and user_id = %s""", [trans_number, user]) # verify that we have a record if len(records) == 0: response = "You need to give me a valid transaction number. \"List Transactions\" will show you a list of your transactions." bot_utilities.log_event( user + " attempted to delete a transaction but provided an invalid transaction number: " + command) else: # delete the record db.runSql("""delete from purchases where purchase_id = %s""", [records[0]["purchase_id"]]) response = "Alright, that record has been deleted." bot_utilities.log_event(user + " has deleted a transaction: " + command) else: # type is sale records = db.fetchAll( """select * from sales where purchase_id = %s and user_id = %s""", [trans_number, user]) # verify that we have a record if len(records) == 0: response = "You need to give me a valid transaction number. \"List Transactions\" will show you a list of your transactions." bot_utilities.log_event( user + " attempted to delete a transaction but provided an invalid transaction number: " + command) else: # delete the record db.runSql("""delete from sales where purchase_id = %s""", [records[0]["purchase_id"]]) response = "Alright, that record has been deleted." bot_utilities.log_event(user + " has deleted a transaction: " + command) db.close() bot_utilities.post_to_channel(channel, response)
def whats_balance(user, channel, command): response = "something went wrong - balance" wallets = bot_utilities.wallet_ballance(user) response = "Wallets:\n" for wallet in wallets: current_value = bot_utilities.get_current_price( wallet["coin_type"]) * float(wallet["balance"]) response = response + "*" + wallet["coin_type"].upper( ) + " " + wallet["coin_name"] + ":* " + str(round( wallet["balance"], 8)) + " _($" + str(round(current_value, 2)) + ")_\n" bot_utilities.log_event(user + " requested wallet balances: " + command) bot_utilities.post_to_channel(channel, response)
def server_report(user, channel, command): response = "generating server report: \n" db = database.Database() # get list of users coin_users = db.fetchAll("select distinct p.user_id from purchases p") # for each user, call the profit command for coin_user in coin_users: user_name = bot_utilities.get_slack_name(coin_user['user_id']) response = response + "*" + user_name + ":* \n" bot_utilities.post_to_channel(channel, response) provide_profit_info(coin_user['user_id'], channel, command) response = "" bot_utilities.log_event(user + " requested a server report") db.close()
def add_amount_to_sale(user, channel, command, purchase_id): response = "something went wrong - add_amount_to_sale" # try to get the number from the command parsed_amount = bot_utilities.parse_number_from_command(command) if parsed_amount == -9999: # cound't get the number response = "sorry, I didn't catch that. How much did you sell?" bot_utilities.log_event( "trying to add amount to sale and didn't parse number." + user + ": " + command) else: # update the record and move on db = database.Database() db.runSql( """update sales set amount = %s, last_updated = now(), step = "usd_gained" where purchase_id = %s""", [parsed_amount, purchase_id]) response = "Great. How much USD did you get in return?" db.close() bot_utilities.post_to_channel(channel, response)
def add_usd_to_purchase(user, channel, command, purchase_id): response = "something went wrong - add_usd_to_purchase" # try to get the number from the command parsed_amount = bot_utilities.parse_number_from_command(command) if parsed_amount == -9999: # cound't get the number response = "sorry, I didn't catch that. How much money did you spend?" bot_utilities.log_event( "trying to add usd to purchase and didn't parse number." + user + ": " + command) else: # update the record and move on db = database.Database() db.runSql( """update purchases set usd_spent = %s, last_updated = now(), record_complete = 1, step = "complete" where purchase_id = %s""", [parsed_amount, purchase_id]) response = "Great, you're all set." bot_utilities.log_event(user + " added a purchase") db.close() bot_utilities.post_to_channel(channel, response)
def add_sale(user, channel, command): db = database.Database() response = "something went wrong - add_sale" # verify no current record is incomplete. If so, blow it away db.runSql( """delete from sales where record_complete = 0 and user_id = %s""", [user]) # get supported coins supported_coins = db.fetchAll( "select * from supported_coins order by sort_order desc") # did user give type? coin = None for supported_coin in supported_coins: if supported_coin["coin_name"].lower() in command: coin = supported_coin["coin_id"] break # begin sale record if coin != None: db.runSql( """insert into sales (user_id, date, coin_type, step, last_updated) values(%s, now(), %s, "amount",now())""", [user, coin]) response = "Sounds great. How much " + coin.upper() + " did you sell?" else: db.runSql( """insert into sales (user_id, date, step, last_updated) values(%s, now(), "type",now())""", [user]) response = "Sounds great. What type of crypto did you sell" # log event bot_utilities.log_event(user + " has begun a sale record: " + command) # message user bot_utilities.post_to_channel(channel, response)
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. """ try: output_list = slack_rtm_output if output_list and len(output_list) > 0: for output in output_list: #print ("\n") #print(output) #print ("\n") try: # ensure the message has a user and text text = output['text'] user = output['user'] except: return None, None, None, None if output and 'text' in output and AT_BOT in output['text']: # return text after the @ mention, whitespace removed output['text'] = output['text'].replace(u"\u2019", '\'') return output['text'].split(AT_BOT)[1].strip().lower(), \ output['channel'], \ output['user'], \ output['text'].split(AT_BOT)[1].strip() #handle im conversations without needing @ elif output and 'text' in output and output[ 'user'] != BOT_ID and output['user'] != "USLACKBOT": output['text'] = output['text'].replace(u"\u2019", '\'') response = slack_client.api_call("im.list") ims = response["ims"] for im in ims: if im["id"] == output['channel']: return output['text'].lower(), \ output['channel'], \ output['user'], \ output['text'] return None, None, None, None except: # nested tries to prevent the bot from crashing bot_utilities.log_event( "An unhandled error was encountered - parse_slack_output") try: bot_utilities.log_event(output['channel'] + " " + output['user']) return None, None, None, None except: bot_utilities.log_event("failed to log the unhandled error") return None, None, None, None
def handle_command(command, channel, user, command_orig): """ 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. """ db = database.Database() response = "Sorry, I'm kind of a dumb robot. I have no idea what you mean. Type 'help' to learn about me" deffered = False if command.startswith('hi') or command.startswith('hello'): response = "well hello there!" # user wants to know possible commands elif command.startswith('help'): response = """ You can use the following commands:\n _______\n *HOW AM I DOING?*: tells you your current crypto profit\n *WHAT'S MY BALANCE?*: lists your current crypto balance\n *I BOUGHT ___*: tell me when you buy crypto\n *I SOLD ___*: tell me when you sell crypto\n *SHOW MY TRANSACTIONS*: lists your transactions\n *DELETE PURCHASE #*: deletes indicated purchase transaction\n *DELETE SALE #*: deletes indicated sale transaction\n \n _tip: if you need to remove coin due to fees, log a sale for $0_\n _______\n """ elif command.startswith('how am i doing'): bot_commands.provide_profit_info(user, channel, command) deffered = True elif 'balance' in command: bot_commands.whats_balance(user, channel, command) deffered = True elif command.startswith('server report'): bot_commands.server_report(user, channel, command) deffered = True elif command.startswith('i bought'): bot_commands.add_purchase(user, channel, command) deffered = True elif command.startswith('i sold'): bot_commands.add_sale(user, channel, command) deffered = True elif ('show' in command or 'list' in command) and 'transaction' in command: bot_commands.list_transactions(user, channel, command) deffered = True elif ('list' in command or 'show' in command) and 'price' in command: bot_commands.show_prices(user, channel, command) deffered = True elif command.startswith('delete purchase'): bot_commands.delete_transaction(user, channel, command, "purchase") deffered = True elif command.startswith('delete sale'): bot_commands.delete_transaction(user, channel, command, "sale") deffered = True elif command.startswith("go kill yourself") and user == admin_user: bot_utilities.log_event("self destruct activated") slack_client.api_call("chat.postMessage", channel=channel, text="wow, that's rude", as_user=True) sys.exit() elif bot_utilities.user_is_adding_record( user, "purchase" ): # determine if the user is currently working to create a purchase record bot_commands.handle_ongoing_record_creation(user, channel, command, "purchase") deffered = True elif bot_utilities.user_is_adding_record( user, "sale" ): # determine if the user is currently working to create a sale record bot_commands.handle_ongoing_record_creation(user, channel, command, "sale") deffered = True if deffered == False: slack_client.api_call("chat.postMessage", channel=channel, text=response, as_user=True) db.close()
except: # nested tries to prevent the bot from crashing bot_utilities.log_event( "An unhandled error was encountered - parse_slack_output") try: bot_utilities.log_event(output['channel'] + " " + output['user']) return None, None, None, None except: bot_utilities.log_event("failed to log the unhandled error") return None, None, None, None if __name__ == "__main__": READ_WEBSOCKET_DELAY = 1 # 1 second delay between reading from firehose if slack_client.rtm_connect(): bot_utilities.log_event("Bitcoin Bot connected and running!") while True: command, channel, user, command_orig = parse_slack_output( slack_client.rtm_read()) if command and channel: handle_command(command, channel, user, command_orig) time.sleep(READ_WEBSOCKET_DELAY) else: bot_utilities.log_event( "Connection failed. Invalid Slack token or bot ID?") # use this to get your bot ID for the config file #BOT_NAME = 'og_bot' #
def provide_profit_info(user, channel, command): response = "something went wrong - provide_profit_info" # get the user's current ballance wallets = bot_utilities.wallet_ballance(user) db = database.Database() # get supported coins coins = db.fetchAll( "select *, 0.0 as current_value, 0.0 as current_worth from supported_coins" ) # find the current rates for coin in coins: coin['current_value'] = Decimal( bot_utilities.get_current_price(coin['coin_id'].lower())) total_spent = 0 total_worth = 0 cashed_out = 0 # match the wallets with the coins and populate the current worth for wallet in wallets: for coin in coins: if wallet["coin_type"].lower() == coin["coin_id"].lower(): wallet["current_worth"] = wallet["balance"] * coin[ "current_value"] break #sum total spent and worth total_spent = total_spent + wallet["usd_spent"] total_worth = total_worth + wallet["current_worth"] cashed_out = cashed_out + wallet["usd_gained"] total_worth = round(total_worth, 2) total_change = round(Decimal(total_worth - float(total_spent)), 2) # worth-spent # fetch the values to compare to day/month day_record = db.fetchAll( """select user_id, total_spent, total_value from performance_log where user_id = %s and date > now() - interval 24 hour order by date asc limit 1""", [user]) # get the change in value for the day try: # day_gain = abs(day_record[0]["total_value"]) - abs(day_record[0]["total_spent"]) # day_change_dec = ((Decimal(total_worth) + cashed_out -total_spent) - day_gain ) / day_gain # day_change = bot_utilities.floored_percentage(day_change_dec,2) # format to percentage usd_spent_today = bot_utilities.usd_spent_in_x_days(user, 1) gain_from_yesterday = ( Decimal(total_worth) + cashed_out ) - day_record[0][ "total_value"] - usd_spent_today # today's worth minus what the worth was yesterday day_change = bot_utilities.floored_percentage( gain_from_yesterday / day_record[0]["total_value"], 2) # get a perceent change day_growth_str = "$" + str(round(gain_from_yesterday, 2)) except: day_change = "" day_growth_str = "error" month_record = db.fetchAll( """select user_id, total_spent, total_value from performance_log where user_id = %s and date > now() - interval 30 day order by date asc limit 1""", [user]) # get the change in value for the month try: # last_month_gain = month_record[0]["total_value"] - month_record[0]["total_spent"] # what the gain was last month # month_change_dec = ((Decimal(total_worth) + cashed_out - total_spent) - last_month_gain ) / last_month_gain # compare the last month's gain to today's gain # month_change = bot_utilities.floored_percentage(month_change_dec,2) # format to percentage usd_spent_this_month = bot_utilities.usd_spent_in_x_days(user, 30) gain_from_last_month = ( Decimal(total_worth) + cashed_out ) - month_record[0][ "total_value"] - usd_spent_this_month # today's worth minus what the worth was last month month_change = bot_utilities.floored_percentage( gain_from_last_month / month_record[0]["total_value"], 2) # get a perceent change month_growth_str = "$" + str(round(gain_from_last_month, 2)) except: month_change = "" month_growth_str = "error" db.close() value_plus_cashed = round(Decimal(total_change) + cashed_out, 2) # combine our wallet and our cashed out response = "*Spent:* $" + str(total_spent)+ "\n" \ "*Value:* $" + str(total_worth) + "\n" \ "*Cashed Out:* $" + str(cashed_out) + "\n" \ "*CHANGE:* $" + str(value_plus_cashed) + " _(" + bot_utilities.floored_percentage((Decimal(value_plus_cashed)/total_spent),2) + ")_ \n \n" \ "_growth today: "+ day_growth_str + " (" + day_change + ")_\n" \ "_30 day growth: " + month_growth_str + " (" + month_change+")_" bot_utilities.log_event(user + " requested performance: " + command) bot_utilities.post_to_channel(channel, response)