diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 466c58878..1ba45e089 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -7,11 +7,11 @@ import json import logging from datetime import timedelta from itertools import chain -from typing import Any, Callable, Dict, List, Union +from typing import Any, Callable, Dict, List import arrow from tabulate import tabulate -from telegram import KeyboardButton, ParseMode, ReplyKeyboardMarkup, Update +from telegram import ParseMode, ReplyKeyboardMarkup, Update from telegram.error import NetworkError, TelegramError from telegram.ext import CallbackContext, CommandHandler, Updater from telegram.utils.helpers import escape_markdown @@ -75,10 +75,48 @@ class Telegram(RPC): self._updater: Updater self._config = freqtrade.config + self._validate_keyboard() self._init() if self._config.get('fiat_display_currency', None): self._fiat_converter = CryptoToFiatConverter() + def _validate_keyboard(self) -> None: + """ + Validates the keyboard configuration from telegram config + section. + """ + self._keyboard: List[List[str]] = [ + ['/daily', '/profit', '/balance'], + ['/status', '/status table', '/performance'], + ['/count', '/start', '/stop', '/help'] + ] + # do not allow commands with mandatory arguments and critical cmds + # like /forcesell and /forcebuy + # TODO: DRY! - its not good to list all valid cmds here. But otherwise + # this needs refacoring of the whole telegram module (same + # problem in _help()). + valid_keys: List[str] = ['/start', '/stop', '/status', '/status table', + '/trades', '/profit', '/performance', '/daily', + '/stats', '/count', '/locks', '/balance', + '/stopbuy', '/reload_config', '/show_config', + '/logs', '/whitelist', '/blacklist', '/edge', + '/help', '/version'] + + # custom shortcuts specified in config.json + cust_keyboard = self._config['telegram'].get('keyboard', []) + if cust_keyboard: + # check for valid shortcuts + invalid_keys = [b for b in chain.from_iterable(cust_keyboard) + if b not in valid_keys] + if len(invalid_keys): + logger.warning('rpc.telegram: invalid commands for custom ' + f'keyboard: {invalid_keys}') + logger.info('rpc.telegram: using default keyboard.') + else: + self._keyboard = cust_keyboard + logger.info('rpc.telegram using custom keyboard from ' + f'config.json: {self._keyboard}') + def _init(self) -> None: """ Initializes this module with the given config, @@ -862,42 +900,7 @@ class Telegram(RPC): :param parse_mode: telegram parse mode :return: None """ - - # default / fallback shortcut buttons - keyboard: List[List[Union[str, KeyboardButton]]] = [ - ['/daily', '/profit', '/balance'], - ['/status', '/status table', '/performance'], - ['/count', '/start', '/stop', '/help'] - ] - - # do not allow commands with mandatory arguments and critical cmds - # like /forcesell and /forcebuy - # TODO: DRY! - its not good to list all valid cmds here. But this - # needs refacoring of the whole telegram module (same problem - # in _help()). - valid_btns: List[str] = ['/start', '/stop', '/status', '/status table', - '/trades', '/profit', '/performance', '/daily', - '/stats', '/count', '/locks', '/balance', - '/stopbuy', '/reload_config', '/show_config', - '/logs', '/whitelist', '/blacklist', '/edge', - '/help', '/version'] - # custom shortcuts specified in config.json - cust_keyboard = self._config['telegram'].get('keyboard', []) - if cust_keyboard: - # check for valid shortcuts - invalid_keys = [b for b in chain.from_iterable(cust_keyboard) - if b not in valid_btns] - if len(invalid_keys): - logger.warning('rpc.telegram: invalid commands for custom ' - f'keyboard: {invalid_keys}') - logger.info('rpc.telegram: using default keyboard.') - else: - keyboard = cust_keyboard - logger.info('rpc.telegram using custom keyboard from ' - f'config.json: {[btn for btn in keyboard]}') - - reply_markup = ReplyKeyboardMarkup(keyboard) - + reply_markup = ReplyKeyboardMarkup(self._keyboard) try: try: self._updater.bot.send_message( diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index ce3db7130..148eb6428 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1751,14 +1751,17 @@ def test__send_msg_keyboard(default_conf, mocker, caplog) -> None: custom_keyboard = ReplyKeyboardMarkup(custom_keys_list) # no keyboard in config -> default keyboard - telegram._config['telegram']['enabled'] = True + # telegram._config['telegram']['enabled'] = True telegram._send_msg('test') used_keyboard = bot.send_message.call_args[1]['reply_markup'] assert used_keyboard == default_keyboard # invalid keyboard in config -> default keyboard - telegram._config['telegram']['enabled'] = True - telegram._config['telegram']['keyboard'] = invalid_keys_list + freqtradebot.config['telegram']['enabled'] = True + freqtradebot.config['telegram']['keyboard'] = invalid_keys_list + telegram = Telegram(freqtradebot) + telegram._updater = MagicMock() + telegram._updater.bot = bot telegram._send_msg('test') used_keyboard = bot.send_message.call_args[1]['reply_markup'] assert used_keyboard == default_keyboard @@ -1767,6 +1770,11 @@ def test__send_msg_keyboard(default_conf, mocker, caplog) -> None: assert log_has('rpc.telegram: using default keyboard.', caplog) # valid keyboard in config -> custom keyboard + freqtradebot.config['telegram']['enabled'] = True + freqtradebot.config['telegram']['keyboard'] = custom_keys_list + telegram = Telegram(freqtradebot) + telegram._updater = MagicMock() + telegram._updater.bot = bot telegram._config['telegram']['enabled'] = True telegram._config['telegram']['keyboard'] = custom_keys_list telegram._send_msg('test')