From 69d62ef38316df7aa33683badbcef6590253ac76 Mon Sep 17 00:00:00 2001 From: Eko Aprili Trisno Date: Thu, 4 Feb 2021 01:06:52 +0700 Subject: [PATCH 01/10] Add Refresh / Reload Button on rpc/Telegram --- freqtrade/rpc/telegram.py | 99 +++++++++++++++++++++++++++++++++------ 1 file changed, 84 insertions(+), 15 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 99f9a8a91..ad72e10e4 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -11,9 +11,9 @@ from typing import Any, Callable, Dict, List, Union import arrow from tabulate import tabulate -from telegram import KeyboardButton, ParseMode, ReplyKeyboardMarkup, Update +from telegram import KeyboardButton, ParseMode, ReplyKeyboardMarkup, Update, InlineKeyboardButton, InlineKeyboardMarkup from telegram.error import NetworkError, TelegramError -from telegram.ext import CallbackContext, CommandHandler, Updater +from telegram.ext import CallbackContext, CommandHandler, Updater, CallbackQueryHandler from telegram.utils.helpers import escape_markdown from freqtrade.__init__ import __version__ @@ -40,9 +40,13 @@ def authorized_only(command_handler: Callable[..., None]) -> Callable[..., Any]: update = kwargs.get('update') or args[0] # Reject unauthorized messages + if update.callback_query: + cchat_id = int(update.callback_query.message.chat.id) + else: + cchat_id = int(update.message.chat_id) + chat_id = int(self._config['telegram']['chat_id']) - - if int(update.message.chat_id) != chat_id: + if cchat_id != chat_id: logger.info( 'Rejected unauthorized message from: %s', update.message.chat_id @@ -150,10 +154,22 @@ class Telegram(RPCHandler): CommandHandler('logs', self._logs), CommandHandler('edge', self._edge), CommandHandler('help', self._help), - CommandHandler('version', self._version), + CommandHandler('version', self._version) + ] + callbacks = [ + CallbackQueryHandler(self._status_table, pattern='update_status_table'), + CallbackQueryHandler(self._daily, pattern='update_daily'), + CallbackQueryHandler(self._profit, pattern='update_profit'), + CallbackQueryHandler(self._profit, pattern='update_balance'), + CallbackQueryHandler(self._profit, pattern='update_performance'), + CallbackQueryHandler(self._profit, pattern='update_count') ] for handle in handles: self._updater.dispatcher.add_handler(handle) + + for handle in callbacks: + self._updater.dispatcher.add_handler(handle) + self._updater.start_polling( clean=True, bootstrap_retries=-1, @@ -336,9 +352,12 @@ class Telegram(RPCHandler): try: statlist, head = self._rpc._rpc_status_table( self._config['stake_currency'], self._config.get('fiat_display_currency', '')) - message = tabulate(statlist, headers=head, tablefmt='simple') - self._send_msg(f"
{message}
", parse_mode=ParseMode.HTML) + if(update.callback_query): + query = update.callback_query + self._update_msg(chat_id=query.message.chat_id, message_id=query.message.message_id, msg=f"
{message}
", parse_mode=ParseMode.HTML, callback_path="update_status_table", reload_able=True) + else: + self._send_msg(f"
{message}
", reload_able=True, callback_path="update_status_table", parse_mode=ParseMode.HTML) except RPCException as e: self._send_msg(str(e)) @@ -376,7 +395,11 @@ class Telegram(RPCHandler): ], tablefmt='simple') message = f'Daily Profit over the last {timescale} days:\n
{stats_tab}
' - self._send_msg(message, parse_mode=ParseMode.HTML) + if(update.callback_query): + query = update.callback_query + self._update_msg(chat_id=query.message.chat_id, message_id=query.message.message_id, msg=message, parse_mode=ParseMode.HTML, callback_path="update_daily", reload_able=True) + else: + self._send_msg(msg=message, parse_mode=ParseMode.HTML, callback_path="update_daily", reload_able=True) except RPCException as e: self._send_msg(str(e)) @@ -435,7 +458,11 @@ class Telegram(RPCHandler): if stats['closed_trade_count'] > 0: markdown_msg += (f"\n*Avg. Duration:* `{avg_duration}`\n" f"*Best Performing:* `{best_pair}: {best_rate:.2f}%`") - self._send_msg(markdown_msg) + if(update.callback_query): + query = update.callback_query + self._update_msg(chat_id=query.message.chat_id, message_id=query.message.message_id, msg=markdown_msg, callback_path="update_profit", reload_able=True) + else: + self._send_msg(msg=markdown_msg, callback_path="update_profit", reload_able=True) @authorized_only def _stats(self, update: Update, context: CallbackContext) -> None: @@ -514,7 +541,11 @@ class Telegram(RPCHandler): output += ("\n*Estimated Value*:\n" "\t`{stake}: {total: .8f}`\n" "\t`{symbol}: {value: .2f}`\n").format(**result) - self._send_msg(output) + if(update.callback_query): + query = update.callback_query + self._update_msg(chat_id=query.message.chat_id, message_id=query.message.message_id, msg=output, callback_path="update_balance", reload_able=True) + else: + self._send_msg(msg=output, callback_path="update_balance", reload_able=True) except RPCException as e: self._send_msg(str(e)) @@ -679,7 +710,11 @@ class Telegram(RPCHandler): count=trade['count'] ) for i, trade in enumerate(trades)) message = 'Performance:\n{}'.format(stats) - self._send_msg(message, parse_mode=ParseMode.HTML) + if(update.callback_query): + query = update.callback_query + self._update_msg(chat_id=query.message.chat_id, message_id=query.message.message_id, msg=message, parse_mode=ParseMode.HTML, callback_path="update_performance", reload_able=True) + else: + self._send_msg(msg=message, parse_mode=ParseMode.HTML, callback_path="update_performance", reload_able=True) except RPCException as e: self._send_msg(str(e)) @@ -699,7 +734,11 @@ class Telegram(RPCHandler): tablefmt='simple') message = "
{}
".format(message) logger.debug(message) - self._send_msg(message, parse_mode=ParseMode.HTML) + if(update.callback_query): + query = update.callback_query + self._update_msg(chat_id=query.message.chat_id, message_id=query.message.message_id, msg=message, parse_mode=ParseMode.HTML, callback_path="update_count", reload_able=True) + else: + self._send_msg(msg=message, parse_mode=ParseMode.HTML, callback_path="update_count", reload_able=True) except RPCException as e: self._send_msg(str(e)) @@ -901,8 +940,35 @@ class Telegram(RPCHandler): f"*Current state:* `{val['state']}`" ) - def _send_msg(self, msg: str, parse_mode: str = ParseMode.MARKDOWN, - disable_notification: bool = False) -> None: + def _update_msg(self, chat_id: str, message_id: str, msg: str, callback_path: str = "", reload_able: bool = False, parse_mode: str = ParseMode.MARKDOWN) -> None: + if reload_able: + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("Refresh", callback_data=callback_path)]]) + else: + reply_markup = InlineKeyboardMarkup([[]]) + try: + try: + self._updater.bot.edit_message_text( + chat_id=chat_id, + message_id=message_id, + text=msg, + parse_mode=parse_mode, + reply_markup=reply_markup + ) + except BadRequest as e: + if 'not modified' in e.message.lower(): + pass + else: + logger.warning( + 'TelegramError: %s', + e.message + ) + except TelegramError as telegram_err: + logger.warning( + 'TelegramError: %s! Giving up on that message.', + telegram_err.message + ) + + def _send_msg(self, msg: str, parse_mode: str = ParseMode.MARKDOWN, disable_notification: bool = False, callback_path: str = "", reload_able: bool = False) -> None: """ Send given markdown message :param msg: message @@ -910,7 +976,10 @@ class Telegram(RPCHandler): :param parse_mode: telegram parse mode :return: None """ - reply_markup = ReplyKeyboardMarkup(self._keyboard) + if reload_able: + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("Refresh", callback_data=callback_path)]]) + else: + reply_markup = ReplyKeyboardMarkup(self._keyboard) try: try: self._updater.bot.send_message( From 21d3635e8dcf9f41d241cb628534a8376f37fb1c Mon Sep 17 00:00:00 2001 From: Eko Aprili Trisno Date: Thu, 4 Feb 2021 01:16:27 +0700 Subject: [PATCH 02/10] Update telegram.py --- freqtrade/rpc/telegram.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index ad72e10e4..32af71a76 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -160,9 +160,9 @@ class Telegram(RPCHandler): CallbackQueryHandler(self._status_table, pattern='update_status_table'), CallbackQueryHandler(self._daily, pattern='update_daily'), CallbackQueryHandler(self._profit, pattern='update_profit'), - CallbackQueryHandler(self._profit, pattern='update_balance'), - CallbackQueryHandler(self._profit, pattern='update_performance'), - CallbackQueryHandler(self._profit, pattern='update_count') + CallbackQueryHandler(self._balance, pattern='update_balance'), + CallbackQueryHandler(self._performance, pattern='update_performance'), + CallbackQueryHandler(self._count, pattern='update_count') ] for handle in handles: self._updater.dispatcher.add_handler(handle) From 54d0ac9d20d077214c8df3b568b57f5114bb97da Mon Sep 17 00:00:00 2001 From: Eko Aprili Trisno Date: Thu, 4 Feb 2021 01:19:23 +0700 Subject: [PATCH 03/10] Update telegram.py --- freqtrade/rpc/telegram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 32af71a76..7cb05b04a 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -12,7 +12,7 @@ from typing import Any, Callable, Dict, List, Union import arrow from tabulate import tabulate from telegram import KeyboardButton, ParseMode, ReplyKeyboardMarkup, Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.error import NetworkError, TelegramError +from telegram.error import NetworkError, TelegramError, BadRequest from telegram.ext import CallbackContext, CommandHandler, Updater, CallbackQueryHandler from telegram.utils.helpers import escape_markdown From ba32708ed44091b9eb2bc15ac8eeedfce98f3fd1 Mon Sep 17 00:00:00 2001 From: Eko Aprili Trisno Date: Sun, 14 Feb 2021 01:40:04 +0700 Subject: [PATCH 04/10] Update telegram.py --- freqtrade/rpc/telegram.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 7cb05b04a..80d7be60f 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -5,14 +5,14 @@ This module manage Telegram communication """ import json import logging -from datetime import timedelta +from datetime import timedelta, datetime from itertools import chain from typing import Any, Callable, Dict, List, Union import arrow from tabulate import tabulate from telegram import KeyboardButton, ParseMode, ReplyKeyboardMarkup, Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.error import NetworkError, TelegramError, BadRequest +from telegram.error import NetworkError, TelegramError from telegram.ext import CallbackContext, CommandHandler, Updater, CallbackQueryHandler from telegram.utils.helpers import escape_markdown @@ -154,7 +154,7 @@ class Telegram(RPCHandler): CommandHandler('logs', self._logs), CommandHandler('edge', self._edge), CommandHandler('help', self._help), - CommandHandler('version', self._version) + CommandHandler('version', self._version), ] callbacks = [ CallbackQueryHandler(self._status_table, pattern='update_status_table'), @@ -945,6 +945,7 @@ class Telegram(RPCHandler): reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("Refresh", callback_data=callback_path)]]) else: reply_markup = InlineKeyboardMarkup([[]]) + msg+="\nUpdated: {}".format(datetime.now().ctime()) try: try: self._updater.bot.edit_message_text( @@ -976,10 +977,10 @@ class Telegram(RPCHandler): :param parse_mode: telegram parse mode :return: None """ - if reload_able: + if reload_able and self._config['telegram'].get('reload',True): reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("Refresh", callback_data=callback_path)]]) else: - reply_markup = ReplyKeyboardMarkup(self._keyboard) + reply_markup = ReplyKeyboardMarkup(self._keyboard, resize_keyboard=True) try: try: self._updater.bot.send_message( From 03eff698291b16077163bfe153860276a2a48d28 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Jun 2021 20:21:43 +0200 Subject: [PATCH 05/10] Simplify update message sending --- freqtrade/rpc/telegram.py | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index ba9c6c0f6..921fdfe59 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -10,12 +10,12 @@ from datetime import date, datetime, timedelta from html import escape from itertools import chain from math import isnan -from typing import Any, Callable, Dict, List, Union, cast +from typing import Any, Callable, Dict, List, Union import arrow from tabulate import tabulate -from telegram import (InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ParseMode, - ReplyKeyboardMarkup, Update) +from telegram import (CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, + ParseMode, ReplyKeyboardMarkup, Update) from telegram.error import BadRequest, NetworkError, TelegramError from telegram.ext import CallbackContext, CallbackQueryHandler, CommandHandler, Updater from telegram.utils.helpers import escape_markdown @@ -180,8 +180,8 @@ class Telegram(RPCHandler): for handle in handles: self._updater.dispatcher.add_handler(handle) - for handle in callbacks: - self._updater.dispatcher.add_handler(handle) + for callback in callbacks: + self._updater.dispatcher.add_handler(callback) self._updater.start_polling( bootstrap_retries=-1, @@ -422,9 +422,7 @@ class Telegram(RPCHandler): lines = message.split("\n") message = "\n".join(lines[:-1] + [lines[1]] + [lines[-1]]) if(messages_count == 1 and update.callback_query): - query = update.callback_query - self._update_msg(chat_id=query.message.chat_id, - message_id=query.message.message_id, + self._update_msg(query=update.callback_query, msg=f"
{message}
", parse_mode=ParseMode.HTML, callback_path="update_status_table", reload_able=True) @@ -469,8 +467,7 @@ class Telegram(RPCHandler): tablefmt='simple') message = f'Daily Profit over the last {timescale} days:\n
{stats_tab}
' if(update.callback_query): - query = update.callback_query - self._update_msg(chat_id=query.message.chat_id, message_id=query.message.message_id, + self._update_msg(query=update.callback_query, msg=message, parse_mode=ParseMode.HTML, callback_path="update_daily", reload_able=True) else: @@ -548,8 +545,7 @@ class Telegram(RPCHandler): markdown_msg += (f"\n*Avg. Duration:* `{avg_duration}`\n" f"*Best Performing:* `{best_pair}: {best_rate:.2f}%`") if(update.callback_query): - query = update.callback_query - self._update_msg(chat_id=query.message.chat_id, message_id=query.message.message_id, + self._update_msg(query=update.callback_query, msg=markdown_msg, callback_path="update_profit", reload_able=True) else: self._send_msg(msg=markdown_msg, callback_path="update_profit", reload_able=True) @@ -640,8 +636,7 @@ class Telegram(RPCHandler): f"\t`{result['symbol']}: " f"{round_coin_value(result['value'], result['symbol'], False)}`\n") if(update.callback_query): - query = update.callback_query - self._update_msg(chat_id=query.message.chat_id, message_id=query.message.message_id, + self._update_msg(query=update.callback_query, msg=output, callback_path="update_balance", reload_able=True) else: self._send_msg(msg=output, callback_path="update_balance", reload_able=True) @@ -841,8 +836,7 @@ class Telegram(RPCHandler): output += stat_line if(sent_messages == 0 and update.callback_query): - query = update.callback_query - self._update_msg(chat_id=query.message.chat_id, message_id=query.message.message_id, + self._update_msg(query=update.callback_query, msg=output, parse_mode=ParseMode.HTML, callback_path="update_performance", reload_able=True) else: @@ -868,8 +862,7 @@ class Telegram(RPCHandler): message = "
{}
".format(message) logger.debug(message) if(update.callback_query): - query = update.callback_query - self._update_msg(chat_id=query.message.chat_id, message_id=query.message.message_id, + self._update_msg(query=update.callback_query, msg=message, parse_mode=ParseMode.HTML, callback_path="update_count", reload_able=True) else: @@ -1106,7 +1099,7 @@ class Telegram(RPCHandler): f"*Current state:* `{val['state']}`" ) - def _update_msg(self, chat_id: str, message_id: str, msg: str, callback_path: str = "", + def _update_msg(self, query: CallbackQuery, msg: str, callback_path: str = "", reload_able: bool = False, parse_mode: str = ParseMode.MARKDOWN) -> None: if reload_able: reply_markup = InlineKeyboardMarkup([ @@ -1115,6 +1108,11 @@ class Telegram(RPCHandler): else: reply_markup = InlineKeyboardMarkup([[]]) msg += "\nUpdated: {}".format(datetime.now().ctime()) + if not query.message: + return + chat_id = query.message.chat_id + message_id = query.message.message_id + try: try: self._updater.bot.edit_message_text( From a95f760ff7e46462ca7116aa5f6616fcc54724e3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Jun 2021 20:34:08 +0200 Subject: [PATCH 06/10] Simplify update logic by moving it to send_msg --- freqtrade/rpc/telegram.py | 65 +++++++++++++-------------------------- 1 file changed, 22 insertions(+), 43 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 921fdfe59..0fb322eb8 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -10,7 +10,7 @@ from datetime import date, datetime, timedelta from html import escape from itertools import chain from math import isnan -from typing import Any, Callable, Dict, List, Union +from typing import Any, Callable, Dict, List, Optional, Union import arrow from tabulate import tabulate @@ -421,14 +421,9 @@ class Telegram(RPCHandler): # insert separators line between Total lines = message.split("\n") message = "\n".join(lines[:-1] + [lines[1]] + [lines[-1]]) - if(messages_count == 1 and update.callback_query): - self._update_msg(query=update.callback_query, - msg=f"
{message}
", - parse_mode=ParseMode.HTML, - callback_path="update_status_table", reload_able=True) - else: - self._send_msg(f"
{message}
", reload_able=True, - callback_path="update_status_table", parse_mode=ParseMode.HTML) + self._send_msg(f"
{message}
", reload_able=True, + callback_path="update_status_table", parse_mode=ParseMode.HTML, + query=update.callback_query) except RPCException as e: self._send_msg(str(e)) @@ -466,13 +461,8 @@ class Telegram(RPCHandler): ], tablefmt='simple') message = f'Daily Profit over the last {timescale} days:\n
{stats_tab}
' - if(update.callback_query): - self._update_msg(query=update.callback_query, - msg=message, parse_mode=ParseMode.HTML, - callback_path="update_daily", reload_able=True) - else: - self._send_msg(msg=message, parse_mode=ParseMode.HTML, callback_path="update_daily", - reload_able=True) + self._send_msg(message, parse_mode=ParseMode.HTML, callback_path="update_daily", + reload_able=True, query=update.callback_query) except RPCException as e: self._send_msg(str(e)) @@ -544,11 +534,8 @@ class Telegram(RPCHandler): if stats['closed_trade_count'] > 0: markdown_msg += (f"\n*Avg. Duration:* `{avg_duration}`\n" f"*Best Performing:* `{best_pair}: {best_rate:.2f}%`") - if(update.callback_query): - self._update_msg(query=update.callback_query, - msg=markdown_msg, callback_path="update_profit", reload_able=True) - else: - self._send_msg(msg=markdown_msg, callback_path="update_profit", reload_able=True) + self._send_msg(markdown_msg, callback_path="update_profit", reload_able=True, + query=update.callback_query) @authorized_only def _stats(self, update: Update, context: CallbackContext) -> None: @@ -635,11 +622,8 @@ class Telegram(RPCHandler): f"\t`{result['stake']}: {result['total']: .8f}`\n" f"\t`{result['symbol']}: " f"{round_coin_value(result['value'], result['symbol'], False)}`\n") - if(update.callback_query): - self._update_msg(query=update.callback_query, - msg=output, callback_path="update_balance", reload_able=True) - else: - self._send_msg(msg=output, callback_path="update_balance", reload_able=True) + self._send_msg(output, callback_path="update_balance", reload_able=True, + query=update.callback_query) except RPCException as e: self._send_msg(str(e)) @@ -820,7 +804,6 @@ class Telegram(RPCHandler): try: trades = self._rpc._rpc_performance() output = "Performance:\n" - sent_messages = 0 for i, trade in enumerate(trades): stat_line = ( f"{i+1}.\t {trade['pair']}\t" @@ -831,17 +814,12 @@ class Telegram(RPCHandler): if len(output + stat_line) >= MAX_TELEGRAM_MESSAGE_LENGTH: self._send_msg(output, parse_mode=ParseMode.HTML) output = stat_line - sent_messages += 1 else: output += stat_line - if(sent_messages == 0 and update.callback_query): - self._update_msg(query=update.callback_query, - msg=output, parse_mode=ParseMode.HTML, - callback_path="update_performance", reload_able=True) - else: - self._send_msg(msg=output, parse_mode=ParseMode.HTML, - callback_path="update_performance", reload_able=True) + self._send_msg(output, parse_mode=ParseMode.HTML, + callback_path="update_performance", reload_able=True, + query=update.callback_query) except RPCException as e: self._send_msg(str(e)) @@ -861,13 +839,9 @@ class Telegram(RPCHandler): tablefmt='simple') message = "
{}
".format(message) logger.debug(message) - if(update.callback_query): - self._update_msg(query=update.callback_query, - msg=message, parse_mode=ParseMode.HTML, - callback_path="update_count", reload_able=True) - else: - self._send_msg(msg=message, parse_mode=ParseMode.HTML, - callback_path="update_count", reload_able=True) + self._send_msg(message, parse_mode=ParseMode.HTML, + callback_path="update_count", reload_able=True, + query=update.callback_query) except RPCException as e: self._send_msg(str(e)) @@ -1140,7 +1114,8 @@ class Telegram(RPCHandler): disable_notification: bool = False, keyboard: List[List[Union[str, KeyboardButton, InlineKeyboardButton]]] = None, callback_path: str = "", - reload_able: bool = False) -> None: + reload_able: bool = False, + query: Optional[CallbackQuery] = None) -> None: """ Send given markdown message :param msg: message @@ -1148,6 +1123,10 @@ class Telegram(RPCHandler): :param parse_mode: telegram parse mode :return: None """ + if query: + self._update_msg(query=query, msg=msg, parse_mode=parse_mode, + callback_path=callback_path, reload_able=reload_able) + return if reload_able and self._config['telegram'].get('reload', True): reply_markup = InlineKeyboardMarkup([ [InlineKeyboardButton("Refresh", callback_data=callback_path)]]) From e226252921c87370f173631221ca5f522ce1ccba Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Jun 2021 20:39:25 +0200 Subject: [PATCH 07/10] Always use the same parameter sequence --- freqtrade/rpc/telegram.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 0fb322eb8..8f8627ece 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -421,8 +421,8 @@ class Telegram(RPCHandler): # insert separators line between Total lines = message.split("\n") message = "\n".join(lines[:-1] + [lines[1]] + [lines[-1]]) - self._send_msg(f"
{message}
", reload_able=True, - callback_path="update_status_table", parse_mode=ParseMode.HTML, + self._send_msg(f"
{message}
", parse_mode=ParseMode.HTML, + reload_able=True, callback_path="update_status_table", query=update.callback_query) except RPCException as e: self._send_msg(str(e)) @@ -461,8 +461,8 @@ class Telegram(RPCHandler): ], tablefmt='simple') message = f'Daily Profit over the last {timescale} days:\n
{stats_tab}
' - self._send_msg(message, parse_mode=ParseMode.HTML, callback_path="update_daily", - reload_able=True, query=update.callback_query) + self._send_msg(message, parse_mode=ParseMode.HTML, reload_able=True, + callback_path="update_daily", query=update.callback_query) except RPCException as e: self._send_msg(str(e)) @@ -534,7 +534,7 @@ class Telegram(RPCHandler): if stats['closed_trade_count'] > 0: markdown_msg += (f"\n*Avg. Duration:* `{avg_duration}`\n" f"*Best Performing:* `{best_pair}: {best_rate:.2f}%`") - self._send_msg(markdown_msg, callback_path="update_profit", reload_able=True, + self._send_msg(markdown_msg, reload_able=True, callback_path="update_profit", query=update.callback_query) @authorized_only @@ -622,7 +622,7 @@ class Telegram(RPCHandler): f"\t`{result['stake']}: {result['total']: .8f}`\n" f"\t`{result['symbol']}: " f"{round_coin_value(result['value'], result['symbol'], False)}`\n") - self._send_msg(output, callback_path="update_balance", reload_able=True, + self._send_msg(output, reload_able=True, callback_path="update_balance", query=update.callback_query) except RPCException as e: self._send_msg(str(e)) @@ -818,7 +818,7 @@ class Telegram(RPCHandler): output += stat_line self._send_msg(output, parse_mode=ParseMode.HTML, - callback_path="update_performance", reload_able=True, + reload_able=True, callback_path="update_performance", query=update.callback_query) except RPCException as e: self._send_msg(str(e)) @@ -840,7 +840,7 @@ class Telegram(RPCHandler): message = "
{}
".format(message) logger.debug(message) self._send_msg(message, parse_mode=ParseMode.HTML, - callback_path="update_count", reload_able=True, + reload_able=True, callback_path="update_count", query=update.callback_query) except RPCException as e: self._send_msg(str(e)) From a9f111dca0063790dadaebfad03c265c8e0842ea Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 17 Jun 2021 19:50:49 +0200 Subject: [PATCH 08/10] Fix some types --- freqtrade/rpc/telegram.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index f83d5a238..6a0e98a75 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -95,7 +95,7 @@ class Telegram(RPCHandler): Validates the keyboard configuration from telegram config section. """ - self._keyboard: List[List[Union[str, KeyboardButton, InlineKeyboardButton]]] = [ + self._keyboard: List[List[Union[str, KeyboardButton]]] = [ ['/daily', '/profit', '/balance'], ['/status', '/status table', '/performance'], ['/count', '/start', '/stop', '/help'] @@ -1112,7 +1112,7 @@ class Telegram(RPCHandler): def _send_msg(self, msg: str, parse_mode: str = ParseMode.MARKDOWN, disable_notification: bool = False, - keyboard: List[List[Union[str, KeyboardButton, InlineKeyboardButton]]] = None, + keyboard: List[List[InlineKeyboardButton]] = None, callback_path: str = "", reload_able: bool = False, query: Optional[CallbackQuery] = None) -> None: @@ -1123,6 +1123,7 @@ class Telegram(RPCHandler): :param parse_mode: telegram parse mode :return: None """ + reply_markup: Union[InlineKeyboardMarkup, ReplyKeyboardMarkup] if query: self._update_msg(query=query, msg=msg, parse_mode=parse_mode, callback_path=callback_path, reload_able=reload_able) From 6e99e3fbbbb903b8e91cb5373b55e9309f151c56 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 19 Jun 2021 09:31:34 +0200 Subject: [PATCH 09/10] Implement tests for message updating --- freqtrade/rpc/telegram.py | 33 +++++++++++++-------------------- tests/rpc/test_rpc_telegram.py | 30 ++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 6a0e98a75..6cb48aef1 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -1088,27 +1088,20 @@ class Telegram(RPCHandler): message_id = query.message.message_id try: - try: - self._updater.bot.edit_message_text( - chat_id=chat_id, - message_id=message_id, - text=msg, - parse_mode=parse_mode, - reply_markup=reply_markup - ) - except BadRequest as e: - if 'not modified' in e.message.lower(): - pass - else: - logger.warning( - 'TelegramError: %s', - e.message - ) - except TelegramError as telegram_err: - logger.warning( - 'TelegramError: %s! Giving up on that message.', - telegram_err.message + self._updater.bot.edit_message_text( + chat_id=chat_id, + message_id=message_id, + text=msg, + parse_mode=parse_mode, + reply_markup=reply_markup ) + except BadRequest as e: + if 'not modified' in e.message.lower(): + pass + else: + logger.warning('TelegramError: %s', e.message) + except TelegramError as telegram_err: + logger.warning('TelegramError: %s! Giving up on that message.', telegram_err.message) def _send_msg(self, msg: str, parse_mode: str = ParseMode.MARKDOWN, disable_notification: bool = False, diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 830ef200e..39ef6a1ab 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -13,7 +13,7 @@ from unittest.mock import ANY, MagicMock import arrow import pytest from telegram import Chat, Message, ReplyKeyboardMarkup, Update -from telegram.error import NetworkError +from telegram.error import BadRequest, NetworkError, TelegramError from freqtrade import __version__ from freqtrade.constants import CANCEL_REASON @@ -25,8 +25,8 @@ from freqtrade.loggers import setup_logging from freqtrade.persistence import PairLocks, Trade from freqtrade.rpc import RPC from freqtrade.rpc.telegram import Telegram, authorized_only -from tests.conftest import (create_mock_trades, get_patched_freqtradebot, log_has, patch_exchange, - patch_get_signal, patch_whitelist) +from tests.conftest import (create_mock_trades, get_patched_freqtradebot, log_has, log_has_re, + patch_exchange, patch_get_signal, patch_whitelist) class DummyCls(Telegram): @@ -1561,7 +1561,7 @@ def test__sell_emoji(default_conf, mocker, msg, expected): assert telegram._get_sell_emoji(msg) == expected -def test__send_msg(default_conf, mocker) -> None: +def test_telegram__send_msg(default_conf, mocker, caplog) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock()) bot = MagicMock() telegram, _, _ = get_telegram_testobject(mocker, default_conf, mock=False) @@ -1572,6 +1572,28 @@ def test__send_msg(default_conf, mocker) -> None: telegram._send_msg('test') assert len(bot.method_calls) == 1 + # Test update + query = MagicMock() + telegram._send_msg('test', callback_path="DeadBeef", query=query, reload_able=True) + edit_message_text = telegram._updater.bot.edit_message_text + assert edit_message_text.call_count == 1 + assert "Updated: " in edit_message_text.call_args_list[0][1]['text'] + + telegram._updater.bot.edit_message_text = MagicMock(side_effect=BadRequest("not modified")) + telegram._send_msg('test', callback_path="DeadBeef", query=query) + assert telegram._updater.bot.edit_message_text.call_count == 1 + assert not log_has_re(r"TelegramError: .*", caplog) + + telegram._updater.bot.edit_message_text = MagicMock(side_effect=BadRequest("")) + telegram._send_msg('test2', callback_path="DeadBeef", query=query) + assert telegram._updater.bot.edit_message_text.call_count == 1 + assert log_has_re(r"TelegramError: .*", caplog) + + telegram._updater.bot.edit_message_text = MagicMock(side_effect=TelegramError("DeadBEEF")) + telegram._send_msg('test3', callback_path="DeadBeef", query=query) + + assert log_has_re(r"TelegramError: DeadBEEF! Giving up.*", caplog) + def test__send_msg_network_error(default_conf, mocker, caplog) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock()) From a7f8342171354ab361dc99554f2dbfae3f2f171d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 19 Jun 2021 16:49:54 +0200 Subject: [PATCH 10/10] Add small documentation about reload disabling --- config_full.json.example | 4 +++- docs/telegram-usage.md | 2 ++ freqtrade/constants.py | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/config_full.json.example b/config_full.json.example index 6aeb756f3..bc9f33f96 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -178,7 +178,9 @@ "sell_fill": "on", "buy_cancel": "on", "sell_cancel": "on" - } + }, + "reload": true, + "balance_dust_level": 0.01 }, "api_server": { "enabled": false, diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index 87ff38881..f5d9744b4 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -95,6 +95,7 @@ Example configuration showing the different settings: "buy_fill": "off", "sell_fill": "off" }, + "reload": true, "balance_dust_level": 0.01 }, ``` @@ -105,6 +106,7 @@ Example configuration showing the different settings: `balance_dust_level` will define what the `/balance` command takes as "dust" - Currencies with a balance below this will be shown. +`reload` allows you to disable reload-buttons on selected messages. ## Create a custom keyboard (command shortcut buttons) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 259aa0e03..013e9df41 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -275,7 +275,8 @@ CONF_SCHEMA = { 'default': 'off' }, } - } + }, + 'reload': {'type': 'boolean'}, }, 'required': ['enabled', 'token', 'chat_id'], },