diff --git a/commands.py b/commands.py index 6fbe5dbd23748966dee3c7da353e2698b2bdcbc5..5b9c40a6a393b40ce1b9fd5896aa43edbf4fdb11 100644 --- a/commands.py +++ b/commands.py @@ -1,229 +1,10 @@ -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 import Update from telegram.ext import CallbackContext -import database -from database import AppUser, AppState, AppWallet, AppWalletRequest, AppUserWallet -from util import generate_wallets, get_wallet_name, has_enough_funds, do_transaction - - -def generate_message(state_id, extra_menu=None): - if extra_menu is None: - extra_menu = [] - state = AppState.get(AppState.id == state_id) - reply_msg = state.message - menu_string = ast.literal_eval(state.menu) - menu = extra_menu + [[InlineKeyboardButton(y[0], callback_data=y[1]) for y in x] for x in menu_string] - reply_markup = InlineKeyboardMarkup(menu) - return reply_msg, reply_markup - - -def start(update: Update, context: CallbackContext): - msg = update.effective_message.text - params = msg.split(" ") - telegram_user = update.effective_user - user, created = AppUser.get_or_create(telegram_id=telegram_user.id, nickname=telegram_user.full_name) - if len(params) == 2: - 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") - count = AppWalletRequest.select().where(AppWalletRequest.wallet_id == request.wallet_id).count() - if count == 0: - user_wallets = AppUserWallet.select().where(AppUserWallet.wallet_id == request.wallet_id) - wallets, mnemonics = generate_wallets(user_wallets) - for i in range(user_wallets.count()): - user = user_wallets[i].user_id - wallet = user_wallets[i].wallet_id - context.bot.send_message(chat_id=user.telegram_id, text="A wallet has been created for you: {}\n" - "A mnemonic has been created for you, save this carefully:\n\n" - "{}".format(wallet.name, mnemonics[i])) - pass - else: - update.effective_message.reply_text("You cannot join a wallet twice") - - user.set_state(1) - user.set_variables({}) - - reply_msg, reply_markup = generate_message(1) - update.effective_message.reply_text(reply_msg, reply_markup=reply_markup) - - -def back(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) - state = AppState.get(AppState.id == user.state_id) - user.set_state(state.prev_state) - - reply_msg, reply_markup = generate_message(state.prev_state) - update.effective_message.reply_text(reply_msg, reply_markup=reply_markup) - - -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 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 - - -def create_wallet(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) - user.set_state(2) - reply_msg, reply_markup = generate_message(2) - update.effective_message.reply_text(reply_msg, reply_markup=reply_markup) - - -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=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_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: - co_signers = int(msg) - user.set_variable('max_co_signers', co_signers) - 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: - update.effective_message.reply_text("This is not a number, try again") - - -def minimum_co_signers(update: Update, context: CallbackContext, user: AppUser): - msg = update.effective_message.text - try: - min_signers = int(msg) - if user.all_variables['max_co_signers'] < min_signers: - update.effective_message.reply_text("Minimum signers cannot be smaller than the maximum signers, please try again") - return - user.set_variable('min_co_signers', min_signers) - 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: - update.effective_message.reply_text("This is not a number, try again") - - -def name_wallet(update: Update, context: CallbackContext, user: AppUser): - msg = update.effective_message.text - if len(msg) > 100: - update.effective_message.reply_text("The chosen name is too long, please try again") - else: - user.set_variable('wallet_name', msg) - variables = user.all_variables - app_wallet = AppWallet.create(max_co_signers=variables['max_co_signers'], - min_co_signers=variables['min_co_signers'], - initiator_user_id=user.id, - name=variables['wallet_name']) - - AppUserWallet.create(user_id=user.id, wallet_id=app_wallet.id) - - wallet_requests = AppWalletRequest.generate_requests(variables['max_co_signers'] - 1, app_wallet.id) - update.effective_message.reply_text("Wallet is created, forward each of these messages to one co-signer") - - for request in wallet_requests: - update.effective_message.reply_text("Hi, please join this wallet as co-signer.\n" - "Wallet name= {}\n" - "https://t.me/BitcoinLibTestbot?start={}".format(variables['wallet_name'], request["token"])) - - next_state = user.next_state() - reply_msg, reply_markup = generate_message(next_state) - update.effective_message.reply_text(reply_msg, reply_markup=reply_markup) +from commands.general import start, back +from commands.transaction import transaction_address, transaction_amount, transaction_fee, send_a_transaction, show_sign_transaction +from commands.wallet import enter_co_signers, minimum_co_signers, name_wallet, wallet_options, create_wallet, choose_wallet +from database import AppUser def callback_handler(update: Update, context: CallbackContext): @@ -265,4 +46,5 @@ COMMANDS = { "send_a_transaction": send_a_transaction, "back": back, "choose_wallet": choose_wallet, + "sign_transaction": show_sign_transaction } diff --git a/commands/__init__.py b/commands/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0c1fcad7b1a1abe4acab7fd4a0a38cb9f341b464 --- /dev/null +++ b/commands/__init__.py @@ -0,0 +1,7 @@ + +import transaction +import util +import wallet +import general + +__all__ = ["transaction", "util", "wallet", "general"] \ No newline at end of file diff --git a/commands/general.py b/commands/general.py new file mode 100644 index 0000000000000000000000000000000000000000..1918fd8b1eb617edbd1e3aebbadcca2b01ed07ab --- /dev/null +++ b/commands/general.py @@ -0,0 +1,51 @@ +from telegram import Update +from telegram.ext import CallbackContext + +from commands.util import generate_message +from database import AppUser, AppWalletRequest, AppUserWallet, AppState +from util import generate_wallets + + +def start(update: Update, context: CallbackContext): + msg = update.effective_message.text + params = msg.split(" ") + telegram_user = update.effective_user + user, created = AppUser.get_or_create(telegram_id=telegram_user.id, nickname=telegram_user.full_name) + if len(params) == 2: + 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") + count = AppWalletRequest.select().where(AppWalletRequest.wallet_id == request.wallet_id).count() + if count == 0: + user_wallets = AppUserWallet.select().where(AppUserWallet.wallet_id == request.wallet_id) + wallets, mnemonics = generate_wallets(user_wallets) + for i in range(user_wallets.count()): + user = user_wallets[i].user_id + wallet = user_wallets[i].wallet_id + context.bot.send_message(chat_id=user.telegram_id, text="A wallet has been created for you: {}\n" + "A mnemonic has been created for you, save this carefully:\n\n" + "{}".format(wallet.name, mnemonics[i])) + pass + else: + update.effective_message.reply_text("You cannot join a wallet twice") + + user.set_state(1) + user.set_variables({}) + + reply_msg, reply_markup = generate_message(1) + update.effective_message.reply_text(reply_msg, reply_markup=reply_markup) + + +def back(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) + state = AppState.get(AppState.id == user.state_id) + user.set_state(state.prev_state) + + reply_msg, reply_markup = generate_message(state.prev_state) + update.effective_message.reply_text(reply_msg, reply_markup=reply_markup) + diff --git a/commands/transaction.py b/commands/transaction.py new file mode 100644 index 0000000000000000000000000000000000000000..95495d95e1ede52442685b645515debb7f56c0fd --- /dev/null +++ b/commands/transaction.py @@ -0,0 +1,99 @@ +import os + +from bitcoinlib.encoding import EncodingError +from bitcoinlib.keys import Address +from telegram import Update +from telegram.ext import CallbackContext + +from commands.util import generate_message +from database import AppUser +from util import do_transaction + + +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 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 + + +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: + 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() + # TODO: create pending_tx's + 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 + 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 show_sign_transaction(update: Update, context: CallbackContext): + # TODO: When the user wants to sign a transaction + # TODO: show a list of transactions + pass + +def choose_sign_transaction(update: Update, context: CallbackContext, user: AppUser): + # TODO: store the retrieve transaction_id or hash in the variables of the user and proceed to the next state + query = update.callback_query.data +# TODO show menu of are you sure you want to sgn the transaction + +def sign_transaction(update: Update, context: CallbackContext, user: AppUser): + # TODO: sign the transaction if the answer is yes otherwise go back to state 0 + query = update.callback_query.data + + diff --git a/commands/util.py b/commands/util.py new file mode 100644 index 0000000000000000000000000000000000000000..53c7c68f24962afdcfda3bdfda31a796aed473ec --- /dev/null +++ b/commands/util.py @@ -0,0 +1,16 @@ +import ast + +from telegram import InlineKeyboardButton, InlineKeyboardMarkup + +from database import AppState + + +def generate_message(state_id, extra_menu=None): + if extra_menu is None: + extra_menu = [] + state = AppState.get(AppState.id == state_id) + reply_msg = state.message + menu_string = ast.literal_eval(state.menu) + menu = extra_menu + [[InlineKeyboardButton(y[0], callback_data=y[1]) for y in x] for x in menu_string] + reply_markup = InlineKeyboardMarkup(menu) + return reply_msg, reply_markup diff --git a/commands/wallet.py b/commands/wallet.py new file mode 100644 index 0000000000000000000000000000000000000000..b1f10addc1d3c8d37ee79e0e8445aa6fc6eb4668 --- /dev/null +++ b/commands/wallet.py @@ -0,0 +1,90 @@ +from telegram import Update, InlineKeyboardButton +from telegram.ext import CallbackContext + +from commands.util import generate_message +from database import AppUser, AppWallet, AppUserWallet, AppWalletRequest + + +def create_wallet(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) + user.set_state(2) + reply_msg, reply_markup = generate_message(2) + update.effective_message.reply_text(reply_msg, reply_markup=reply_markup) + + +def enter_co_signers(update: Update, context: CallbackContext, user: AppUser): + msg = update.effective_message.text + try: + co_signers = int(msg) + user.set_variable('max_co_signers', co_signers) + 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: + update.effective_message.reply_text("This is not a number, try again") + + +def minimum_co_signers(update: Update, context: CallbackContext, user: AppUser): + msg = update.effective_message.text + try: + min_signers = int(msg) + if user.all_variables['max_co_signers'] < min_signers: + update.effective_message.reply_text("Minimum signers cannot be smaller than the maximum signers, please try again") + return + user.set_variable('min_co_signers', min_signers) + 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: + update.effective_message.reply_text("This is not a number, try again") + + +def name_wallet(update: Update, context: CallbackContext, user: AppUser): + msg = update.effective_message.text + if len(msg) > 100: + update.effective_message.reply_text("The chosen name is too long, please try again") + else: + user.set_variable('wallet_name', msg) + variables = user.all_variables + app_wallet = AppWallet.create(max_co_signers=variables['max_co_signers'], + min_co_signers=variables['min_co_signers'], + initiator_user_id=user.id, + name=variables['wallet_name']) + + AppUserWallet.create(user_id=user.id, wallet_id=app_wallet.id) + + wallet_requests = AppWalletRequest.generate_requests(variables['max_co_signers'] - 1, app_wallet.id) + update.effective_message.reply_text("Wallet is created, forward each of these messages to one co-signer") + + for request in wallet_requests: + update.effective_message.reply_text("Hi, please join this wallet as co-signer.\n" + "Wallet name= {}\n" + "https://t.me/BitcoinLibTestbot?start={}".format(variables['wallet_name'], request["token"])) + + 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 wallet_options(update: Update, context: CallbackContext, user: AppUser): + 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 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=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