def handle_balance(message): username = str(message.author) message_time = datetime.utcfromtimestamp( message.created_utc) # time the reddit message was created add_history_record( username=str(message.author), comment_or_message="message", reddit_time=message_time.strftime("%Y-%m-%d %H:%M:%S"), action="balance", comment_id=message.name, comment_text=str(message.body)[:255], ) sql = "SELECT address FROM accounts WHERE username=%s" val = (username, ) MYCURSOR.execute(sql, val) result = MYCURSOR.fetchall() if len(result) > 0: results = check_balance(result[0][0]) response = ( "At address %s:\n\nAvailable: %s Nano\n\nUnpocketed: %s Nano\n\nNano " "will be pocketed automatically unless the transaction is below " "0.0001 Nano." "\n\nhttps://nanocrawler.cc/explorer/account/%s" % (result[0][0], results[0] / 10**30, results[1] / 10**30, result[0][0])) return response return "You do not have an open account yet"
def handle_balance(message): username = str(message.author) message_time = datetime.utcfromtimestamp( message.created_utc) # time the reddit message was created add_history_record( username=str(message.author), comment_or_message="message", reddit_time=message_time, action="balance", comment_id=message.name, comment_text=str(message.body)[:255], ) try: acct = Account.get(username=username) results = check_balance(acct.address) response = text.BALANCE % ( acct.address, from_raw(results), acct.address, ) return response except Account.DoesNotExist: return text.NOT_OPEN
def account_info(key, by_address=False): """ Pulls the address, private key and balance from a user :param username: string - redditors username :return: dict - name, address, private_key, balance """ foundAccount = True if not by_address: try: acct = Account.select().where(Account.username == key).get() except Account.DoesNotExist: foundAccount = False else: try: acct = Account.select().where(Account.address == key).get() except Account.DoesNotExist: foundAccount = False if foundAccount: return { "username": acct.username, "address": acct.address, "private_key": acct.private_key, "silence": acct.silence, "balance": check_balance(acct.address), "account_exists": True, "opt_in": acct.opt_in, } return None
def handle_balance(message): username = str(message.author) message_time = datetime.utcfromtimestamp( message.created_utc) # time the reddit message was created add_history_record( username=str(message.author), comment_or_message="message", reddit_time=message_time.strftime("%Y-%m-%d %H:%M:%S"), action="balance", comment_id=message.name, comment_text=str(message.body)[:255], ) sql = "SELECT address FROM accounts WHERE username=%s" val = (username, ) MYCURSOR.execute(sql, val) result = MYCURSOR.fetchall() if len(result) > 0: results = check_balance(result[0][0]) response = text.BALANCE % ( result[0][0], from_raw(results[0]), from_raw(results[1]), result[0][0], ) return response return text.NOT_OPEN
def account_info(key, by_address=False): """ Pulls the address, private key and balance from a user :param username: string - redditors username :return: dict - name, address, private_key, balance """ if not by_address: sql = "SELECT username, address, private_key, minimum, silence, opt_in, active FROM accounts WHERE username=%s" else: sql = "SELECT username, address, private_key, minimum, silence, opt_in, active FROM accounts WHERE address=%s" val = (key, ) result = query_sql(sql, val) if len(result) > 0: return { "username": result[0][0], "address": result[0][1], "private_key": result[0][2], "minimum": int(result[0][3]), "silence": result[0][4], "balance": check_balance(result[0][1])[0], "account_exists": True, "opt_in": result[0][5], "active": result[0][6], } return None
def total_funds(): MYCURSOR.execute("SELECT username, address FROM accounts") myresult = MYCURSOR.fetchall() usernames = [str(result[0]) for result in myresult] addresses = [str(result[1]) for result in myresult] MYDB.commit() balance = 0 for username, address in zip(usernames, addresses): balance new_balance = tipper_rpc.check_balance(address) temp_balance = new_balance[0] / 10**30 + new_balance[1] / 10**30 if temp_balance >= 50: print(username, temp_balance, address) balance += temp_balance print("Total Nano: ", balance)
def handle_receive(message): """ :param message: :return: """ message_time = datetime.utcfromtimestamp(message.created_utc) username = str(message.author) # find any accounts associated with the redditor sql = "SELECT address, private_key FROM accounts WHERE username=%s" val = (username, ) MYCURSOR.execute(sql, val) result = MYCURSOR.fetchall() if len(result) > 0: address = result[0][0] open_or_receive(address, result[0][1]) balance = check_balance(address) add_history_record( username=username, action="receive", reddit_time=message_time.strftime("%Y-%m-%d %H:%M:%S"), address=address, comment_id=message.name, comment_or_message="message", ) response = ( "At address %s, you currently have %s Nano available, and %s Nano " "unpocketed. If you have any unpocketed, create a new " "message containing the word " "'receive'\n\nhttps://nanocrawler.cc/explorer/account/%s" % (address, balance[0] / 10**30, balance[1] / 10**30, address)) return response else: add_history_record( username=username, action="receive", reddit_time=message_time.strftime("%Y-%m-%d %H:%M:%S"), comment_id=message.name, comment_or_message="message", ) response = ( "You do not currently have an account open. To create one, " "respond with the text 'create' in the message body.") return response
def handle_receive(message): """ :param message: :return: """ message_time = datetime.utcfromtimestamp(message.created_utc) username = str(message.author) # find any accounts associated with the redditor sql = "SELECT address, private_key FROM accounts WHERE username=%s" val = (username, ) MYCURSOR.execute(sql, val) result = MYCURSOR.fetchall() if len(result) > 0: address = result[0][0] open_or_receive(address, result[0][1]) balance = check_balance(address) add_history_record( username=username, action="receive", reddit_time=message_time.strftime("%Y-%m-%d %H:%M:%S"), address=address, comment_id=message.name, comment_or_message="message", ) response = text.RECEIVE["balance"] % ( address, from_raw(balance[0]), from_raw(balance[1]), address, ) return response else: add_history_record( username=username, action="receive", reddit_time=message_time.strftime("%Y-%m-%d %H:%M:%S"), comment_id=message.name, comment_or_message="message", ) response = text.NOT_OPEN return response
def parse_raw_amount(parsed_text, username=None): """ Given some parsed command text, converts the units to Raw nano :param parsed_text: :param username: required if amount is 'all' :return: """ conversion = 1 # check if the amount is 'all'. This will convert it to the proper int if parsed_text[1].lower() == "all": try: acct = Account.select( Account.address).where(Account.username == username).get() address = acct.address balance = check_balance(address) return balance except Account.DoesNotExist: raise (TipError(None, text.NOT_OPEN)) amount = parsed_text[1].lower() # before converting to a number, make sure the amount doesn't have nan or inf in it if amount == "nan" or ("inf" in amount): raise TipError( None, f"Could not read your tip or send amount. Is '{parsed_text[1]}' a number?", ) else: try: amount = to_raw(float(amount) / conversion) except: raise TipError( None, f"Could not read your tip or send amount. Is '{amount}' a number, or is the " "currency code valid? If you are trying to send Nano directly, omit " "'Nano' from the amount (I will fix this in a future update).", ) return amount
def handle_send(message): """ Extracts send command information from a PM command :param message: :return: """ parsed_text = parse_text(str(message.body)) username = str(message.author) message_time = datetime.utcfromtimestamp( message.created_utc) # time the reddit message was created entry_id = add_history_record( username=username, action="send", comment_or_message="message", comment_id=message.name, reddit_time=message_time.strftime("%Y-%m-%d %H:%M:%S"), comment_text=str(message.body)[:255], ) response = {"username": username} # check that there are enough fields (i.e. a username) if len(parsed_text) <= 2: update_history_notes(entry_id, "no recipient or amount specified") response["status"] = 110 return response # check that it wasn't a mistyped currency code or something if parsed_text[2] in EXCLUDED_REDDITORS: response["status"] = 140 return response # pull sender account info sender_info = tipper_functions.account_info(response["username"]) if not sender_info: update_history_notes(entry_id, "user does not exist") response["status"] = 100 return response # parse the amount try: response["amount"] = parse_raw_amount(parsed_text, response["username"]) except TipError as err: response["status"] = 120 response["amount"] = parsed_text[1] update_history_notes(entry_id, err.sql_text) return response # check if it's above the program minimum if response["amount"] < nano_to_raw(PROGRAM_MINIMUM): update_history_notes(entry_id, "amount below program limit") response["status"] = 130 return response # check the user's balance if response["amount"] > sender_info["balance"]: update_history_notes(entry_id, "insufficient funds") response["status"] = 160 return response recipient_text = parsed_text[2] # catch invalid redditor AND address try: recipient_info = parse_recipient_username(recipient_text) except TipError as err: update_history_notes(entry_id, err.sql_text) response["recipient"] = recipient_text response["status"] = 170 return response # if we have a username, pull their info if "username" in recipient_info.keys(): response["recipient"] = recipient_info["username"] recipient_name = recipient_info["username"] recipient_info = tipper_functions.account_info(recipient_name) response["status"] = 10 if recipient_info is None: recipient_info = tipper_functions.add_new_account( response["recipient"]) response["status"] = 20 elif not recipient_info["opt_in"]: response["status"] = 190 return response # check if it's an address else: # otherwise, just use the address. Everything is None except address recipient_info["minimum"] = 0 response["recipient"] = recipient_info["address"] response["status"] = 30 # check the send amount is above the user minimum, if a username is provided # if it was just an address, this would be -1 if response["amount"] < recipient_info["minimum"]: update_history_notes(entry_id, "below user minimum") response["status"] = 180 response["minimum"] = recipient_info["minimum"] return response response["hash"] = send( sender_info["address"], sender_info["private_key"], response["amount"], recipient_info["address"], )["hash"] # if it was an address, just send to the address if "username" not in recipient_info.keys(): sql = ( "UPDATE history SET notes = %s, address = %s, username = %s, recipient_username = %s, " "recipient_address = %s, amount = %s, return_status = %s WHERE id = %s" ) val = ( "send to address", sender_info["address"], sender_info["username"], None, recipient_info["address"], str(response["amount"]), "cleared", entry_id, ) tipper_functions.exec_sql(sql, val) LOGGER.info( f"Sending Nano: {sender_info['address']} {sender_info['private_key']} {response['amount']} {recipient_info['address']}" ) return response # Update the sql and send the PMs sql = ( "UPDATE history SET notes = %s, address = %s, username = %s, recipient_username = %s, " "recipient_address = %s, amount = %s, hash = %s, return_status = %s WHERE id = %s" ) val = ( "sent to user", sender_info["address"], sender_info["username"], recipient_info["username"], recipient_info["address"], str(response["amount"]), response["hash"], "cleared", entry_id, ) tipper_functions.exec_sql(sql, val) LOGGER.info( f"Sending Nano: {sender_info['address']} {sender_info['private_key']} {response['amount']} {recipient_info['address']} {recipient_info['username']}" ) if response["status"] == 20: subject = "Congrats on receiving your first Nano Tip!" message_text = (WELCOME_TIP % ( response["amount"] / 10**30, recipient_info["address"], recipient_info["address"], ) + COMMENT_FOOTER) send_pm(recipient_info["username"], subject, message_text) return response else: if not recipient_info["silence"]: receiving_new_balance = check_balance(recipient_info["address"]) subject = "You just received a new Nano tip!" message_text = (NEW_TIP % ( response["amount"] / 10**30, recipient_info["address"], receiving_new_balance[0] / 10**30, receiving_new_balance[1] / 10**30, response["hash"], ) + COMMENT_FOOTER) send_pm(recipient_info["username"], subject, message_text) return response
def parse_raw_amount(parsed_text, username=None): """ Given some parsed command text, converts the units to Raw nano :param parsed_text: :param username: required if amount is 'all' :return: """ conversion = 1 # check if the amount is 'all'. This will convert it to the proper int if parsed_text[1].lower() == "all": sql = "SELECT address FROM accounts WHERE username = %s" val = (username,) MYCURSOR.execute(sql, val) result = MYCURSOR.fetchall() if len(result) > 0: address = result[0][0] balance = check_balance(address) return balance[0] else: raise (TipError(None, text.NOT_OPEN)) # check if there is a currency code in the amount; if so, get the conversion if parsed_text[1][-3:].lower() in EXCLUDED_REDDITORS: currency = parsed_text[1][-3:].upper() url = "https://min-api.cryptocompare.com/data/price?fsym={}&tsyms={}".format( shared.CURRENCY, currency ) try: results = requests.get(url, timeout=1) results = json.loads(results.text) conversion = float(results[currency]) amount = parsed_text[1][:-3].lower() except requests.exceptions.Timeout: raise TipError( "Could not reach conversion server.", "Could not reach conversion server. Tip not sent.", ) except: raise TipError( "Could not reach conversion server.", f"Currency {currency.upper()} not supported. Tip not sent.", ) else: amount = parsed_text[1].lower() # before converting to a number, make sure the amount doesn't have nan or inf in it if amount == "nan" or ("inf" in amount): raise TipError( None, f"Could not read your tip or send amount. Is '{parsed_text[1]}' a number?", ) else: try: amount = to_raw(float(amount) / conversion) except: raise TipError( None, f"Could not read your tip or send amount. Is '{amount}' a number, or is the " "currency code valid? If you are trying to send Nano directly, omit " "'Nano' from the amount (I will fix this in a future update).", ) return amount
def handle_send(message): """ Extracts send command information from a PM command :param message: :return: """ parsed_text = parse_text(str(message.body)) username = str(message.author) message_time = datetime.utcfromtimestamp( message.created_utc) # time the reddit message was created entry_id = add_history_record( username=username, action="send", comment_or_message="message", comment_id=message.name, reddit_time=message_time, comment_text=str(message.body)[:255], ) response = {"username": username} # check that there are enough fields (i.e. a username) if len(parsed_text) <= 2: update_history_notes(entry_id, "no recipient or amount specified") response["status"] = 110 return response # pull sender account info sender_info = tipper_functions.account_info(response["username"]) if not sender_info: update_history_notes(entry_id, "user does not exist") response["status"] = 100 return response # parse the amount try: response["amount"] = parse_raw_amount(parsed_text, response["username"]) except TipError as err: response["status"] = 120 response["amount"] = parsed_text[1] update_history_notes(entry_id, err.sql_text) return response # check if it's above the program minimum if response["amount"] < to_raw(PROGRAM_MINIMUM): update_history_notes(entry_id, "amount below program limit") response["status"] = 130 return response # check the user's balance if response["amount"] > sender_info["balance"]: update_history_notes(entry_id, "insufficient funds") response["status"] = 160 return response recipient_text = parsed_text[2] # catch invalid redditor AND address try: recipient_info = parse_recipient_username(recipient_text) except TipError as err: update_history_notes(entry_id, err.sql_text) response["recipient"] = recipient_text response["status"] = 170 return response # if we have a username, pull their info if "username" in recipient_info.keys(): response["recipient"] = recipient_info["username"] recipient_name = recipient_info["username"] recipient_info = tipper_functions.account_info(recipient_name) response["status"] = 10 if recipient_info is None: recipient_info = tipper_functions.add_new_account( response["recipient"]) if recipient_info is None: return text.TIP_CREATE_ACCT_ERROR response["status"] = 20 elif not recipient_info["opt_in"]: response["status"] = 190 return response # check if it's an address else: # otherwise, just use the address. Everything is None except address response["recipient"] = recipient_info["address"] response["status"] = 30 if sender_info["address"] == recipient_info["address"]: # Don't allow sends to yourself response["status"] = 200 return response response["hash"] = send( sender_info["address"], response["amount"], recipient_info["address"], )["block"] # if it was an address, just send to the address if "username" not in recipient_info.keys(): History.update( notes="send to address", address=sender_info["address"], username=sender_info["username"], recipient_username=None, recipient_address=recipient_info["address"], amount=str(response["amount"]), return_status="cleared").where(History.id == entry_id).execute() LOGGER.info( f"Sending Banano: {sender_info['address']} {sender_info['private_key']} {response['amount']} {recipient_info['address']}" ) return response # Update the sql and send the PMs History.update( notes="send to address", address=sender_info["address"], username=sender_info["username"], recipient_username=recipient_info["username"], recipient_address=recipient_info["address"], amount=str(response["amount"]), return_status="cleared").where(History.id == entry_id).execute() LOGGER.info( f"Sending Banano: {sender_info['address']} {sender_info['private_key']} {response['amount']} {recipient_info['address']} {recipient_info['username']}" ) if response["status"] == 20: subject = text.SUBJECTS["first_tip"] message_text = (WELCOME_TIP % ( NumberUtil.format_float(from_raw(response["amount"])), recipient_info["address"], recipient_info["address"], ) + COMMENT_FOOTER) send_pm(recipient_info["username"], subject, message_text) return response else: if not recipient_info["silence"]: receiving_new_balance = check_balance(recipient_info["address"]) subject = text.SUBJECTS["new_tip"] message_text = (NEW_TIP % ( NumberUtil.format_float(from_raw(response["amount"])), recipient_info["address"], from_raw(receiving_new_balance), response["hash"], ) + COMMENT_FOOTER) send_pm(recipient_info["username"], subject, message_text) return response