def command_removeorder_confirm(self, update: Update, context: CallbackContext): assert update.callback_query and context.user_data is not None query = update.callback_query if query.data == 'cancel': self.cancel_command(update, context) return ConversationHandler.END assert query.data if not query.data.isdecimal(): self.command_error(update, context, text='Invalid order ID') return ConversationHandler.END token: TokenWatcher = self.parent.watchers[ context.user_data['removeorder']['token_address']] chat_message( update, context, text= f'Are you sure you want to delete order #{query.data} for {token.name}?', reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton('✅ Confirm', callback_data=query.data), InlineKeyboardButton('❌ Cancel', callback_data='cancel'), ]]), edit=self.config.update_messages, ) return self.next.ORDER
def command_addorder(self, update: Update, context: CallbackContext): assert update.callback_query and context.user_data is not None query = update.callback_query assert query.data token_address = query.data.split(':')[1] if not Web3.isChecksumAddress(token_address): self.command_error(update, context, text='Invalid token address.') return ConversationHandler.END token = self.parent.watchers[token_address] context.user_data['addorder'] = {'token_address': token_address} reply_markup = InlineKeyboardMarkup(inline_keyboard=[ [ InlineKeyboardButton('🚫 Stop loss sell', callback_data='stop_loss'), InlineKeyboardButton('💰 Take profit sell', callback_data='limit_sell'), ], [ InlineKeyboardButton('💵 Limit buy', callback_data='limit_buy'), InlineKeyboardButton('❌ Cancel', callback_data='cancel'), ], ]) chat_message( update, context, text= f'Creating order for token {token.name}.\nWhich <u>type of order</u> would you like to create?', reply_markup=reply_markup, edit=self.config.update_messages, ) return self.next.TYPE
def command_removeorder(self, update: Update, context: CallbackContext): assert update.callback_query and context.user_data is not None query = update.callback_query assert query.data token_address = query.data.split(':')[1] if not Web3.isChecksumAddress(token_address): self.command_error(update, context, text='Invalid token address.') return ConversationHandler.END token: TokenWatcher = self.parent.watchers[token_address] context.user_data['removeorder'] = {'token_address': token_address} orders = token.orders buttons: List[InlineKeyboardButton] = [ InlineKeyboardButton( f'{self.get_type_icon(o)} #{o.order_record.id} - {self.get_type_name(o)}', callback_data=o.order_record.id, ) for o in orders ] buttons_layout = [buttons[i:i + 2] for i in range(0, len(buttons), 2)] # noqa: E203 buttons_layout.append( [InlineKeyboardButton('❌ Cancel', callback_data='cancel')]) reply_markup = InlineKeyboardMarkup(inline_keyboard=buttons_layout) chat_message( update, context, text=f'Select the order you want to remove for {token.name}.', reply_markup=reply_markup, edit=self.config.update_messages, ) return self.next.CONFIRM
def command_removeorder_order(self, update: Update, context: CallbackContext): assert update.callback_query and context.user_data is not None query = update.callback_query if query.data == 'cancel': self.cancel_command(update, context) return ConversationHandler.END assert query.data if not query.data.isdecimal(): self.command_error(update, context, text='Invalid order ID') return ConversationHandler.END token: TokenWatcher = self.parent.watchers[ context.user_data['removeorder']['token_address']] try: order = next( filter(lambda o: o.order_record.id == int(str(query.data)), token.orders)) except StopIteration: self.command_error(update, context, text=f'Order {query.data} could not be found.') return ConversationHandler.END remove_order(order_record=order.order_record) token.orders.remove(order) chat_message( update, context, text= f'✅ Alright, the order <b>#{query.data}</b> was removed from {token.name}.', edit=self.config.update_messages, ) return ConversationHandler.END
def cancel_command(self, update: Update, context: CallbackContext): assert context.user_data is not None del context.user_data['addorder'] chat_message(update, context, text='⚠️ OK, I\'m cancelling this command.', edit=False)
def command_status(self, update: Update, context: CallbackContext): self.pause_status_update( True ) # prevent running an update while we are changing the last message id sorted_tokens = sorted(self.watchers.values(), key=lambda token: token.symbol.lower()) balances: List[Decimal] = [] for token in sorted_tokens: status, balance_bnb = self.get_token_status(token) balances.append(balance_bnb) msg = chat_message(update, context, text=status, edit=False) if msg is not None: self.watchers[ token.address].last_status_message_id = msg.message_id message, buttons = self.get_summary_message(balances) reply_markup = InlineKeyboardMarkup(buttons) stat_msg = chat_message( update, context, text=message, reply_markup=reply_markup, edit=False, ) if stat_msg is not None: self.last_status_message_id = stat_msg.message_id time.sleep(1) # make sure the message go received by the telegram API self.pause_status_update(False) # resume update job
def command_edittoken(self, update: Update, context: CallbackContext): assert update.callback_query and context.user_data is not None query = update.callback_query assert query.data token_address = query.data.split(':')[1] if not Web3.isChecksumAddress(token_address): self.command_error(update, context, text='Invalid token address.') return ConversationHandler.END token: TokenWatcher = self.parent.watchers[token_address] context.user_data['edittoken'] = {'token_address': token_address} buttons = [ [ InlineKeyboardButton(f'{token.emoji}Edit emoji', callback_data='emoji'), InlineKeyboardButton('Edit default slippage', callback_data='slippage'), ], [ InlineKeyboardButton('Edit buy price', callback_data='buyprice'), InlineKeyboardButton('❌ Cancel', callback_data='cancel'), ], ] reply_markup = InlineKeyboardMarkup(buttons) chat_message( update, context, text=f'What do you want to edit for token {token.name}?', reply_markup=reply_markup, edit=self.config.update_messages, ) return self.next.ACTION_CHOICE
def print_summary(self, update: Update, context: CallbackContext): assert context.user_data is not None order = context.user_data['addorder'] token = self.parent.watchers[order['token_address']] type_name = self.get_type_name(order) comparision = self.get_comparison_symbol(order) amount = self.get_human_amount(order, token) unit = self.get_amount_unit(order, token) trailing = (f'Trailing stop loss {order["trailing_stop"]}% callback\n' if order["trailing_stop"] is not None else '') gas_price = ( f'{Decimal(order["gas_price"]) / Decimal(10 ** 9):.1g} Gwei' if order["gas_price"] and not order["gas_price"].startswith('+') else 'network default' if order["gas_price"] is None else f'network default {order["gas_price"]} Gwei') limit_price = Decimal(order["limit_price"]) bnb_price = self.net.get_bnb_price() usd_amount = bnb_price * amount if order[ 'type'] == 'buy' else bnb_price * limit_price * amount message = ( '<u>Preview:</u>\n' + f'{token.name} - {type_name}\n' + trailing + f'Amount: {format_token_amount(amount)} {unit} (${usd_amount:.2f})\n' + f'Price {comparision} {limit_price:.3g} BNB per token\n' + f'Slippage: {order["slippage"]}%\n' + f'Gas: {gas_price}') chat_message( update, context, text=message, reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton('✅ Validate', callback_data='ok'), InlineKeyboardButton('❌ Cancel', callback_data='cancel'), ]]), edit=False, ) return self.next.SUMMARY
def command_error(self, update: Update, context: CallbackContext, text: str): assert context.user_data is not None del context.user_data['removeorder'] chat_message(update, context, text=f'⛔️ {text}', edit=self.config.update_messages)
def error_handler(self, update: Update, context: CallbackContext) -> None: logger.error('Exception while handling an update') logger.error(context.error) chat_message( update, context, text=f'⛔️ Exception while handling an update\n{context.error}', edit=False)
def command_canceltoken(self, update: Update, context: CallbackContext): assert context.user_data is not None del context.user_data['edittoken'] chat_message(update, context, text='⚠️ OK, I\'m cancelling this command.', edit=False) return ConversationHandler.END
def command_addtoken(self, update: Update, context: CallbackContext): assert context.user_data is not None context.user_data['addtoken'] = {} chat_message(update, context, text='Please send me the token contract address.', edit=False) return self.next.ADDRESS
def command_addorder_slippage(self, update: Update, context: CallbackContext): assert context.user_data is not None order = context.user_data['addorder'] if update.message is None: assert update.callback_query query = update.callback_query assert query.data if query.data == 'cancel': self.cancel_command(update, context) return ConversationHandler.END try: slippage_percent = int(query.data) except ValueError: self.command_error(update, context, text='The slippage is not recognized.') return ConversationHandler.END else: assert update.message and update.message.text try: slippage_percent = int(update.message.text.strip()) except ValueError: chat_message( update, context, text='⚠️ The slippage is not recognized, try again:', edit=False) return self.next.SLIPPAGE order['slippage'] = slippage_percent network_gas_price = Decimal(self.net.w3.eth.gas_price) / Decimal(10**9) chat_message( update, context, text=f'OK, the order will use slippage of {slippage_percent}%.\n' + 'Finally, please indicate the <u>gas price in Gwei</u> for this order.\n' + 'Choose "Default" to use the default network price at the moment ' + f'of the transaction (currently {network_gas_price:.1g} Gwei) ' + 'or message me the value.', reply_markup=InlineKeyboardMarkup([ [ InlineKeyboardButton('network default', callback_data='None'), InlineKeyboardButton('default + 0.1 Gwei', callback_data='+0.1'), ], [ InlineKeyboardButton('default + 1 Gwei', callback_data='+1'), InlineKeyboardButton('default + 2 Gwei', callback_data='+2'), ], [InlineKeyboardButton('❌ Cancel', callback_data='cancel')], ]), edit=self.config.update_messages, ) return self.next.GAS
def command_start(self, update: Update, context: CallbackContext): chat_message( update, context, text= 'Hi! You can start adding tokens that you want to trade with the ' + '<a href="/addtoken">/addtoken</a> command.', edit=False, )
def command_addtoken_emoji(self, update: Update, context: CallbackContext): assert update.message and update.message.text and context.user_data is not None add = context.user_data['addtoken'] add['icon'] = update.message.text.strip() chat_message( update, context, text='Alright, the token will show as ' + f'<b>"{add["icon"]} {add["symbol"]}"</b>. ' + 'What is the default slippage in % to use for swapping on PancakeSwap?', edit=False, ) return self.next.SLIPPAGE
def command_addtoken_noemoji(self, update: Update, context: CallbackContext): assert context.user_data is not None add = context.user_data['addtoken'] add['icon'] = None chat_message( update, context, text=f'Alright, the token will show as <b>"{add["symbol"]}"</b>. ' + 'What is the default slippage in % to use for swapping on PancakeSwap?', edit=self.config.update_messages, ) return self.next.SLIPPAGE
def command_addorder_gas(self, update: Update, context: CallbackContext): assert context.user_data is not None order = context.user_data['addorder'] if update.message is None: assert update.callback_query query = update.callback_query assert query.data if query.data == 'cancel': self.cancel_command(update, context) return ConversationHandler.END elif query.data == 'None': order['gas_price'] = None chat_message( update, context, text= 'OK, the order will use default network gas price.\nConfirm the order below!', edit=self.config.update_messages, ) elif query.data.startswith('+'): try: Decimal(query.data) except Exception: self.command_error(update, context, text='Invalid gas price.') return ConversationHandler.END order['gas_price'] = query.data chat_message( update, context, text= f'OK, the order will use default network gas price {query.data} Gwei.\n' + 'Confirm the order below!', edit=self.config.update_messages, ) else: self.command_error(update, context, text='Invalid gas price.') return ConversationHandler.END return self.print_summary(update, context) else: assert update.message and update.message.text try: gas_price_gwei = Decimal(update.message.text.strip()) except ValueError: chat_message( update, context, text='⚠️ The gas price is not recognized, try again:', edit=False) return self.next.GAS order['gas_price'] = str(Web3.toWei(gas_price_gwei, unit='gwei')) chat_message( update, context, text= f'OK, the order will use {gas_price_gwei:.4g} Gwei for gas price.\n<u>Confirm</u> the order below!', edit=self.config.update_messages, ) return self.print_summary(update, context)
def command_address(self, update: Update, context: CallbackContext): assert update.callback_query query = update.callback_query assert query.data token_address = query.data.split(':')[1] if not Web3.isChecksumAddress(token_address): chat_message(update, context, text='⛔️ Invalid token address.', edit=self.config.update_messages) return token = self.watchers[token_address] chat_message(update, context, text=f'{token.name}\n<code>{token_address}</code>', edit=self.config.update_messages)
def command_addtoken_address(self, update: Update, context: CallbackContext): assert update.message and update.message.text and context.user_data is not None response = update.message.text.strip() if Web3.isAddress(response): token_address = Web3.toChecksumAddress(response) else: chat_message( update, context, text= '⚠️ The address you provided is not a valid ETH address. Try again:', edit=False) return self.next.ADDRESS add = context.user_data['addtoken'] add['address'] = str(token_address) try: add['decimals'] = self.net.get_token_decimals(token_address) add['symbol'] = self.net.get_token_symbol(token_address) except (ABIFunctionNotFound, ContractLogicError): chat_message( update, context, text='⛔ Wrong ABI for this address.\n' + 'Check that address is a contract at ' + f'<a href="https://bscscan.com/address/{token_address}">BscScan</a> and try again.', edit=False, ) del context.user_data['addtoken'] return ConversationHandler.END if token_exists(address=token_address): chat_message( update, context, text=f'⚠️ Token <b>{add["symbol"]}</b> already exists.', edit=False) del context.user_data['addtoken'] return ConversationHandler.END reply_markup = InlineKeyboardMarkup( [[InlineKeyboardButton('🙅♂️ No emoji', callback_data='None')]]) chat_message( update, context, text=f'Thanks, the token <b>{add["symbol"]}</b> uses ' + f'{add["decimals"]} decimals. ' + 'Now please send me and EMOJI you would like to associate to this token for easy spotting, ' + 'or click the button below.', reply_markup=reply_markup, edit=False, ) return self.next.EMOJI
def command_edittoken_emoji(self, update: Update, context: CallbackContext): assert context.user_data is not None edit = context.user_data['edittoken'] token: TokenWatcher = self.parent.watchers[edit['token_address']] if update.message is not None: assert update.message.text edit['icon'] = update.message.text.strip() else: assert update.callback_query query = update.callback_query assert query.data if query.data == 'cancel': return self.command_canceltoken(update, context) elif query.data == 'None': edit['icon'] = None else: edit['icon'] = query.data token_record = token.token_record try: db.connect() with db.atomic(): token_record.icon = edit['icon'] token_record.save() except Exception as e: self.command_error(update, context, text=f'Failed to update database record: {e}') return ConversationHandler.END finally: del context.user_data['edittoken'] db.close() token.emoji = token_record.icon + ' ' if token_record.icon else '' token.name = token.emoji + token.symbol chat_message( update, context, text=f'✅ Alright, the token will show as <b>"{token.name}"</b>. ', edit=self.config.update_messages, ) return ConversationHandler.END
def command_addorder_summary(self, update: Update, context: CallbackContext): assert update.effective_chat and update.callback_query and context.user_data is not None query = update.callback_query if query.data != 'ok': self.cancel_command(update, context) return ConversationHandler.END add = context.user_data['addorder'] token: TokenWatcher = self.parent.watchers[add['token_address']] del add['token_address'] # not needed in order record creation try: db.connect() with db.atomic(): order_record = Order.create(token=token.token_record, created=datetime.now(), **add) except Exception as e: self.command_error(update, context, text=f'Failed to create database record: {e}') return ConversationHandler.END finally: del context.user_data['addorder'] db.close() order = OrderWatcher(order_record=order_record, net=self.net, dispatcher=context.dispatcher, chat_id=update.effective_chat.id) token.orders.append(order) chat_message( update, context, text=f'✅ Order #{order_record.id} was added successfully!', edit=self.config.update_messages, ) for job in token.scheduler.get_jobs(): # check prices now job.modify(next_run_time=datetime.now()) return ConversationHandler.END
def command_sellall(self, update: Update, context: CallbackContext): assert update.callback_query query = update.callback_query assert query.data token_address = query.data.split(':')[1] if not Web3.isChecksumAddress(token_address): chat_message(update, context, text='⛔️ Invalid token address.', edit=False) return ConversationHandler.END token: TokenWatcher = self.parent.watchers[token_address] chat_message( update, context, text=f'Are you sure you want to sell all balance for {token.name}?', reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton('✅ Confirm', callback_data=token_address), InlineKeyboardButton('❌ Cancel', callback_data='cancel'), ]]), edit=self.config.update_messages, ) return self.next.CONFIRM
def command_order(self, update: Update, context: CallbackContext): error_msg = 'You need to provide the order ID number as argument to this command, like <code>/order 12</code>.' if context.args is None: chat_message(update, context, text=error_msg, edit=False) return try: order_id = int(context.args[0]) except Exception: chat_message(update, context, text=error_msg, edit=False) return order: Optional[OrderWatcher] = None for token in self.watchers.values(): for o in token.orders: if o.order_record.id != order_id: continue order = o if not order: chat_message(update, context, text='⛔️ Could not find order with this ID.', edit=False) return chat_message(update, context, text=order.long_repr(), edit=False)
def command_show_all_tokens(self, update: Update, context: CallbackContext): if update.message: assert update.message.text command = update.message.text.strip()[1:] try: msg = self.prompts_select_token[command] except KeyError: chat_message(update, context, text='⛔️ Invalid command.', edit=False) return buttons_layout = get_tokens_keyboard_layout( self.watchers, callback_prefix=command) else: # callback query from button assert update.callback_query query = update.callback_query assert query.data try: msg = self.prompts_select_token[query.data] except KeyError: chat_message(update, context, text='⛔️ Invalid command.', edit=False) return buttons_layout = get_tokens_keyboard_layout( self.watchers, callback_prefix=query.data) reply_markup = InlineKeyboardMarkup(buttons_layout) chat_message( update, context, text=msg, reply_markup=reply_markup, edit=False, )
def command_sellall_confirm(self, update: Update, context: CallbackContext): assert update.callback_query query = update.callback_query if query.data == 'cancel': chat_message(update, context, text='⚠️ OK, I\'m cancelling this command.', edit=self.config.update_messages) return ConversationHandler.END if not Web3.isChecksumAddress(query.data): chat_message(update, context, text='⛔️ Invalid token address.', edit=self.config.update_messages) return ConversationHandler.END token: TokenWatcher = self.parent.watchers[query.data] _, v2 = self.net.get_token_price(token_address=token.address, token_decimals=token.decimals, sell=True) if not self.net.is_approved(token_address=token.address, v2=v2): # when selling we require that the token is approved on pcs beforehand version = 'v2' if v2 else 'v1' logger.info( f'Need to approve {token.symbol} for trading on PancakeSwap {version}.' ) chat_message( update, context, text= f'Approving {token.symbol} for trading on PancakeSwap {version}...', edit=self.config.update_messages, ) res = self.net.approve(token_address=token.address, v2=v2) if res: chat_message(update, context, text='✅ Approval successful!', edit=self.config.update_messages) else: chat_message(update, context, text='⛔ Approval failed', edit=False) return ConversationHandler.END balance_tokens = self.net.get_token_balance_wei( token_address=token.address) balance_decimal = Decimal(balance_tokens) / Decimal(10**token.decimals) chat_message( update, context, text= f'Selling {format_token_amount(balance_decimal)} {token.symbol}...', edit=self.config.update_messages, ) res, bnb_out, txhash_or_error = self.net.sell_tokens( token.address, amount_tokens=balance_tokens, slippage_percent=token.default_slippage, gas_price='+1', v2=v2, ) if not res: logger.error(f'Transaction failed: {txhash_or_error}') if len(txhash_or_error) == 66: reason_or_link = f'<a href="https://bscscan.com/tx/{txhash_or_error}">{txhash_or_error[:8]}...</a>' else: reason_or_link = txhash_or_error chat_message(update, context, text=f'⛔️ Transaction failed: {reason_or_link}', edit=self.config.update_messages) return ConversationHandler.END logger.success( f'Sell transaction succeeded. Received {bnb_out:.3g} BNB') chat_message( update, context, text=f'✅ Got {bnb_out:.3g} BNB at ' + f'tx <a href="https://bscscan.com/tx/{txhash_or_error}">{txhash_or_error[:8]}...</a>', edit=self.config.update_messages, ) if len(token.orders) > 0: chat_message( update, context, text=f'⚠️ You still have pending orders for {token.name}. ' + 'Please delete them in case they are not relevant anymore.', edit=False, ) return ConversationHandler.END
def command_cancelsell(self, update: Update, context: CallbackContext): chat_message(update, context, text='⚠️ OK, I\'m cancelling this command.', edit=self.config.update_messages) return ConversationHandler.END
def command_error(self, update: Update, context: CallbackContext, text: str): assert context.user_data is not None del context.user_data['edittoken'] chat_message(update, context, text=f'⛔️ {text}', edit=False)
def command_edittoken_buyprice(self, update: Update, context: CallbackContext): assert context.user_data is not None edit = context.user_data['edittoken'] token: TokenWatcher = self.parent.watchers[edit['token_address']] effective_buy_price: Optional[Decimal] if update.message is not None: assert update.message.text user_input = update.message.text.strip().lower() if 'bnb' in user_input: balance = self.net.get_token_balance( token_address=token.address) if balance == 0: # would lead to division by zero chat_message( update, context, text= '⚠️ The token balance is zero, can\'t use calculation from BNB amount. ' + 'Try again with a price instead:', edit=False, ) return self.next.BUYPRICE try: buy_amount = Decimal(user_input[:-3]) except Exception: chat_message( update, context, text= '⚠️ The BNB amount you inserted is not valid. Try again:', edit=False) return self.next.BUYPRICE effective_buy_price = buy_amount / balance else: try: effective_buy_price = Decimal(user_input) except ValueError: chat_message( update, context, text='⚠️ This is not a valid price value. Try again:', edit=False, ) return self.next.BUYPRICE else: assert update.callback_query query = update.callback_query assert query.data if query.data == 'cancel': return self.command_canceltoken(update, context) elif query.data == 'None': effective_buy_price = None else: self.command_error(update, context, text='Invalid callback.') return ConversationHandler.END edit['effective_buy_price'] = effective_buy_price token_record = token.token_record try: db.connect() with db.atomic(): token_record.effective_buy_price = (str( edit['effective_buy_price']) if edit['effective_buy_price'] else None) token_record.save() except Exception as e: self.command_error(update, context, text=f'Failed to update database record: {e}') return ConversationHandler.END finally: del context.user_data['edittoken'] db.close() token.effective_buy_price = edit['effective_buy_price'] if effective_buy_price is None: chat_message( update, context, text= '✅ Alright, effective buy price for profit calculation is disabled.', edit=self.config.update_messages, ) else: chat_message( update, context, text=f'✅ Alright, the token {token.name} ' + f'was bought at {token.effective_buy_price:.4g} BNB per token.', edit=self.config.update_messages, ) return ConversationHandler.END
def command_edittoken_slippage(self, update: Update, context: CallbackContext): assert context.user_data is not None edit = context.user_data['edittoken'] token: TokenWatcher = self.parent.watchers[edit['token_address']] if update.message is not None: assert update.message.text try: slippage = int(update.message.text.strip()) except ValueError: chat_message( update, context, text= '⚠️ This is not a valid slippage value. Please enter an integer number for percentage ' + '(without percent sign). Try again:', edit=False, ) return self.next.SLIPPAGE else: assert update.callback_query query = update.callback_query assert query.data if query.data == 'cancel': return self.command_canceltoken(update, context) try: slippage = int(query.data) except ValueError: self.command_error(update, context, text='Invalid default slippage.') return ConversationHandler.END if slippage < 1: chat_message( update, context, text= '⚠️ This is not a valid slippage value. Please enter a positive integer number for percentage. ' + 'Try again:', edit=False, ) return self.next.SLIPPAGE edit['default_slippage'] = slippage token_record = token.token_record try: db.connect() with db.atomic(): token_record.default_slippage = edit['default_slippage'] token_record.save() except Exception as e: self.command_error(update, context, text=f'Failed to update database record: {e}') return ConversationHandler.END finally: del context.user_data['edittoken'] db.close() token.default_slippage = token_record.default_slippage chat_message( update, context, text=f'✅ Alright, the token {token.name} ' + f'will use <b>{edit["default_slippage"]}%</b> slippage by default.', edit=self.config.update_messages, ) return ConversationHandler.END
def command_edittoken_action(self, update: Update, context: CallbackContext): assert update.callback_query and context.user_data is not None query = update.callback_query assert query.data edit = context.user_data['edittoken'] token: TokenWatcher = self.parent.watchers[edit['token_address']] if query.data == 'cancel': return self.command_canceltoken(update, context) elif query.data == 'emoji': buttons = [ InlineKeyboardButton('🙅♂️ No emoji', callback_data='None'), InlineKeyboardButton('❌ Cancel', callback_data='cancel'), ] reply_markup = InlineKeyboardMarkup([buttons]) chat_message( update, context, text= f'Please send me and EMOJI you would like to associate with {token.symbol} for easy spotting, ' + 'or click the buttons below.', reply_markup=reply_markup, edit=self.config.update_messages, ) return self.next.EMOJI elif query.data == 'slippage': buttons = [ InlineKeyboardButton(f'Keep {token.default_slippage}%', callback_data=token.default_slippage), InlineKeyboardButton('❌ Cancel', callback_data='cancel'), ] reply_markup = InlineKeyboardMarkup([buttons]) chat_message( update, context, text= f'What is the default slippage in % to use for swapping {token.name} on PancakeSwap?', reply_markup=reply_markup, edit=self.config.update_messages, ) return self.next.SLIPPAGE elif query.data == 'buyprice': current_price, _ = self.net.get_token_price( token_address=token.address, token_decimals=token.decimals, sell=True) current_price_fixed = format_price_fixed(current_price) buttons2 = [ [ InlineKeyboardButton('No price (disable profit calc)', callback_data='None') ], [InlineKeyboardButton('❌ Cancel', callback_data='cancel')], ] reply_markup = InlineKeyboardMarkup(buttons2) chat_message( update, context, text= f'What was the effective buy price (after tax) for {token.name} when you invested? ' + 'You have 3 options for this:\n' + f' ・ Standard notation like "<code>{current_price_fixed}</code>"\n' + f' ・ Scientific notation like "<code>{current_price:.1e}</code>"\n' + ' ・ Amount you bought in BNB like "<code>0.5BNB</code>" (include "BNB" at the end)\n', reply_markup=reply_markup, edit=self.config.update_messages, ) return self.next.BUYPRICE else: self.command_error(update, context, text='Invalid callback') return ConversationHandler.END