Ejemplo n.º 1
0
 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
Ejemplo n.º 2
0
 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
Ejemplo n.º 3
0
 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
Ejemplo n.º 4
0
 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
Ejemplo n.º 5
0
 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)
Ejemplo n.º 6
0
 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
Ejemplo n.º 7
0
 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
Ejemplo n.º 8
0
 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
Ejemplo n.º 9
0
 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)
Ejemplo n.º 10
0
 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)
Ejemplo n.º 11
0
 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
Ejemplo n.º 12
0
 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
Ejemplo n.º 13
0
 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
Ejemplo n.º 14
0
 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,
     )
Ejemplo n.º 15
0
 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
Ejemplo n.º 16
0
 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
Ejemplo n.º 17
0
 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)
Ejemplo n.º 18
0
 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)
Ejemplo n.º 19
0
    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
Ejemplo n.º 20
0
    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
Ejemplo n.º 21
0
 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
Ejemplo n.º 22
0
 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
Ejemplo n.º 23
0
 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)
Ejemplo n.º 24
0
 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,
     )
Ejemplo n.º 25
0
 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
Ejemplo n.º 26
0
 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
Ejemplo n.º 27
0
 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)
Ejemplo n.º 28
0
    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
Ejemplo n.º 29
0
    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
Ejemplo n.º 30
0
 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