예제 #1
0
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"
예제 #2
0
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
예제 #4
0
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
예제 #6
0
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)
예제 #7
0
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
예제 #8
0
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
예제 #10
0
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
예제 #11
0
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
예제 #12
0
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