diff --git a/commands.py b/commands.py index 9ae3bd7f722ad18aea00dff094d4e7115ba8117e..6fbe5dbd23748966dee3c7da353e2698b2bdcbc5 100644 --- a/commands.py +++ b/commands.py @@ -1,9 +1,15 @@ import ast +import os + +from bitcoinlib.encoding import EncodingError +from bitcoinlib.keys import Address +from bitcoinlib.wallets import Wallet from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup from telegram.ext import CallbackContext +import database from database import AppUser, AppState, AppWallet, AppWalletRequest, AppUserWallet -from util import generate_wallets, get_wallet_name +from util import generate_wallets, get_wallet_name, has_enough_funds, do_transaction def generate_message(state_id, extra_menu=None): @@ -26,6 +32,7 @@ def start(update: Update, context: CallbackContext): token = params[1] request = AppWalletRequest.get(AppWalletRequest.token == token) user_wallet, created = AppUserWallet.get_or_create(user_id=user.id, wallet_id=request.wallet_id) + # TODO remove or True if created or True: request.delete_instance() update.effective_message.reply_text("Succesfully joined the wallet") @@ -44,7 +51,7 @@ def start(update: Update, context: CallbackContext): update.effective_message.reply_text("You cannot join a wallet twice") user.set_state(1) - user.set_variable({}) + user.set_variables({}) reply_msg, reply_markup = generate_message(1) update.effective_message.reply_text(reply_msg, reply_markup=reply_markup) @@ -64,7 +71,11 @@ def send_a_transaction(update: Update, context: CallbackContext): telegram_user = update.effective_user user, created = AppUser.get_or_create(telegram_id=telegram_user.id, nickname=telegram_user.full_name) if user.state_id.id != 6: - update.effective_message.reply_text("You cannot do this operation now") + update.effective_message.reply_text("You cannot do this operation right now") + else: + next_state = user.next_state() + reply_msg, reply_markup = generate_message(next_state) + update.effective_message.reply_text(reply_msg, reply_markup=reply_markup) pass @@ -77,24 +88,88 @@ def create_wallet(update: Update, context: CallbackContext): def choose_wallet(update: Update, context: CallbackContext): + # TODO: some information about the wallet when choosing the wallet + # TODO: check if wallet is already created (choose wallet) telegram_user = update.effective_user user = AppUser.get(telegram_id=telegram_user.id, nickname=telegram_user.full_name) user_wallets = AppUserWallet.select().where(AppUserWallet.user_id == user) user.set_state(5) - menu = [[InlineKeyboardButton(uw.wallet_id.name, callback_data=get_wallet_name(uw.wallet_id.id, uw.user_id.id))] for uw in user_wallets] + menu = [[InlineKeyboardButton(uw.wallet_id.name, callback_data=uw.wallet_id.id)] for uw in user_wallets] reply_msg, reply_markup = generate_message(5, menu) update.effective_message.reply_text(reply_msg, reply_markup=reply_markup) pass def wallet_options(update: Update, context: CallbackContext, user: AppUser): - wallet_name = update.callback_query.data - user.set_variable("wallet_name", wallet_name) + wallet_id = update.callback_query.data + user.set_variable("wallet_id", wallet_id) next_state = user.next_state() reply_msg, reply_markup = generate_message(next_state) update.effective_message.reply_text(reply_msg, reply_markup=reply_markup) +def transaction_address(update: Update, context: CallbackContext, user: AppUser): + msg = update.effective_message.text + try: + address = Address(msg, network=os.getenv("BTC_NETWORK")) + user.set_variable("address", msg) + next_state = user.next_state() + reply_msg, reply_markup = generate_message(next_state) + update.effective_message.reply_text(reply_msg, reply_markup=reply_markup) + except EncodingError: + update.effective_message.reply_text("This is not a valid address") + + +def transaction_amount(update: Update, context: CallbackContext, user: AppUser): + msg = update.effective_message.text + try: + amount = float(msg) + user.set_variable("amount", amount) + next_state = user.next_state() + reply_msg, reply_markup = generate_message(next_state) + update.effective_message.reply_text(reply_msg, reply_markup=reply_markup) + except ValueError: + pass + # update.effective_message.reply_text("This is not a number, try again") + + +def transaction_fee(update: Update, context: CallbackContext, user: AppUser): + cq = update.callback_query + cm = update.effective_message + success = False + if cq: + query = cq.data + if query == "low" or query == "normal" or query == "high": + user.set_variable("fee", query) + success = True + + elif cm: + msg = cm.text + try: + amount = float(msg) + user.set_variable("fee", amount) + success = True + except ValueError: + pass + + if success: + variables = user.variables() + t, has_funds, error = do_transaction(variables["wallet_id"], user.id, variables["address"], variables["amount"], variables["fee"]) + if not has_funds: + update.effective_message.reply_text("The wallet does not have enough funds to do this transaction, please change the amount or fee") + elif error: + update.effective_message.reply_text("Something went wrong during the creating of the transaction please try again later") + else: + # TODO: send notification to the other co signers + + pass + next_state = user.next_state() + reply_msg, reply_markup = generate_message(next_state) + update.effective_message.reply_text(reply_msg, reply_markup=reply_markup) + else: + update.effective_message.reply_text("Please send a number or use the buttons to identify the fee amount") + + def enter_co_signers(update: Update, context: CallbackContext, user: AppUser): msg = update.effective_message.text try: @@ -171,7 +246,10 @@ def message_handler(update: Update, context: CallbackContext): 2: enter_co_signers, 3: minimum_co_signers, 4: name_wallet, - 5: wallet_options + 5: wallet_options, + 7: transaction_address, + 8: transaction_amount, + 9: transaction_fee } try: f = HANDLERS[state_user] @@ -186,5 +264,5 @@ COMMANDS = { "create_a_wallet": create_wallet, "send_a_transaction": send_a_transaction, "back": back, - "choose_wallet": choose_wallet + "choose_wallet": choose_wallet, } diff --git a/database.py b/database.py index 8c47d2bad7c403d548e0417785a384271796b81c..b5ad49a336b17ba529c7af1bd88b33c28d5a2195 100644 --- a/database.py +++ b/database.py @@ -8,6 +8,7 @@ import os db = MySQLDatabase(None) + def get_db_uri(): user = os.getenv("DB_USER") password = os.getenv("DB_PASSWORD") diff --git a/main.py b/main.py index a57b5b093e6ac2291f2d5c30cd7990769103d613..2a668e7135ccc07b06909659e1d94628d53f171e 100644 --- a/main.py +++ b/main.py @@ -6,6 +6,7 @@ from database import connect load_dotenv() + class TelegramWrapper: def __init__(self, token): self._token = token diff --git a/util.py b/util.py index 0fadef2a584976389c145ed9236ca61b696af74c..e7ee459aaf0081da2c4cca02f1f625578336b74a 100644 --- a/util.py +++ b/util.py @@ -2,10 +2,11 @@ import os from bitcoinlib.keys import HDKey from bitcoinlib.mnemonic import Mnemonic -from bitcoinlib.wallets import Wallet +from bitcoinlib.wallets import Wallet, WalletError import database + def get_wallet_name(wallet_id, user_id): return "wallet_{}user_{}".format(wallet_id, user_id) @@ -27,7 +28,7 @@ def generate_wallets(user_wallets): wallets = [] for i in range(count): uw = user_wallets[i] - wallet_name = get_wallet_name(uw.wallet_id.id,uw.user_id.id) + wallet_name = get_wallet_name(uw.wallet_id.id, uw.user_id.id) wallet = Wallet.create(wallet_name, sigs_required=uw.wallet_id.min_co_signers, keys=generate_klist(hd_keys, i), @@ -35,3 +36,20 @@ def generate_wallets(user_wallets): db_uri=database.get_db_uri()) wallets.append(wallet) return wallets, mnemonics + + +def do_transaction(wallet_id, user_id, address, amount, fee): + wallet = Wallet(get_wallet_name(wallet_id, user_id), db_uri=os.getenv("BTC_NETWORK")) + wallet.utxos_update() + has_enough_funds = True + error = False + try: + t = wallet.send_to(to_address=address, fee=fee, network=os.getenv("BTC_NETWORK"), amount=amount) + t.sign() + return t, has_enough_funds, error + except WalletError as e: + error = True + if e.msg == "Not enough unspent transaction outputs found": + has_enough_funds = False + + return None, has_enough_funds, error