diff --git a/davtelepot/__init__.py b/davtelepot/__init__.py
index a810614..e8059b4 100644
--- a/davtelepot/__init__.py
+++ b/davtelepot/__init__.py
@@ -11,10 +11,12 @@ __author__ = "Davide Testa"
__email__ = "davide@davte.it"
__credits__ = ["Marco Origlia", "Nick Lee @Nickoala"]
__license__ = "GNU General Public License v3.0"
-__version__ = "2.5.12"
+__version__ = "2.5.13"
__maintainer__ = "Davide Testa"
__contact__ = "t.me/davte"
-from . import administration_tools, authorization, bot, helper, languages, suggestions, utilities
+from . import (administration_tools, authorization, bot, helper, languages,
+ suggestions, useful_tools, utilities)
-__all__ = [administration_tools, authorization, bot, helper, languages, suggestions, utilities]
+__all__ = [administration_tools, authorization, bot, helper, languages,
+ suggestions, useful_tools, utilities]
diff --git a/davtelepot/messages.py b/davtelepot/messages.py
index 7121f2f..40a2e3f 100644
--- a/davtelepot/messages.py
+++ b/davtelepot/messages.py
@@ -1006,3 +1006,43 @@ default_unknown_command_message = {
'en': "Unknown command! Touch /help to read the guide and available commands.",
'it': "Comando sconosciuto! Fai /help per leggere la guida e i comandi."
}
+
+default_useful_tools_messages = {
+ 'length_command': {
+ 'description': {
+ 'en': "Use this command in reply to a message to get its length.",
+ 'it': "Usa questo comando in risposta a un messaggio per sapere "
+ "quanti caratteri contenga.",
+ },
+ 'help_section': {
+ 'description': {
+ 'en': "Use the /length command in reply to a message to get "
+ "its length.\n"
+ "Beware that emojis may count as multiple characters.",
+ 'it': "Usa il comando /caratteri in risposta a un messaggio "
+ "per sapere quanti caratteri contenga.\n"
+ "Attenzione alle emoji, che contano come più caratteri.",
+ },
+ 'label': {
+ 'en': "Length #️⃣",
+ 'it': "Caratteri #️⃣"
+ },
+ 'name': "length",
+ },
+ 'instructions': {
+ 'en': "Use this command in reply to a message to get its length.",
+ 'it': "Usa questo comando in risposta a un messaggio per sapere "
+ "quanti caratteri contenga.",
+ },
+ 'language_labelled_commands': {
+ 'en': "length",
+ 'it': "caratteri",
+ },
+ 'result': {
+ 'en': "According to my calculations, this message is "
+ "{n}
characters long.",
+ 'it': "Questo messaggio contiene {n}
"
+ "caratteri secondo i miei calcoli.",
+ },
+ },
+}
\ No newline at end of file
diff --git a/davtelepot/useful_tools.py b/davtelepot/useful_tools.py
new file mode 100644
index 0000000..82cc8e7
--- /dev/null
+++ b/davtelepot/useful_tools.py
@@ -0,0 +1,61 @@
+"""General purpose functions for Telegram bots."""
+
+# Standard library
+from collections import OrderedDict
+
+# Project modules
+from .bot import Bot
+from .messages import default_useful_tools_messages
+from .utilities import recursive_dictionary_update
+
+
+async def _length_command(bot: Bot, update: dict, user_record: OrderedDict):
+ if 'reply_to_message' not in update:
+ text = bot.get_message(
+ 'useful_tools', 'length_command', 'instructions',
+ user_record=user_record, update=update
+ )
+ else:
+ text = bot.get_message(
+ 'useful_tools', 'length_command', 'result',
+ user_record=user_record, update=update,
+ n=len(update['reply_to_message']['text'])
+ )
+ update = update['reply_to_message']
+ reply_to_message_id = update['message_id']
+ return dict(
+ chat_id=update['chat']['id'],
+ text=text,
+ parse_mode='HTML',
+ reply_to_message_id=reply_to_message_id
+ )
+
+
+def init(telegram_bot: Bot, useful_tools_messages=None):
+ """Define commands for `telegram_bot`.
+
+ You may provide customized `useful_tools_messages` that will overwrite
+ `default_useful_tools_messages`. Missing entries will be kept default.
+ """
+ if useful_tools_messages is None:
+ useful_tools_messages = dict()
+ useful_tools_messages = recursive_dictionary_update(
+ default_useful_tools_messages,
+ useful_tools_messages
+ )
+ telegram_bot.messages['useful_tools'] = useful_tools_messages
+
+ @telegram_bot.command(
+ command='/length',
+ aliases=None,
+ reply_keyboard_button=None,
+ show_in_keyboard=False,
+ **{
+ key: val
+ for key, val in useful_tools_messages['length_command'].items()
+ if key in ('description', 'help_section', 'language_labelled_commands')
+ },
+ authorization_level='everybody'
+ )
+ async def length_command(bot, update, user_record):
+ return await _length_command(bot=bot, update=update, user_record=user_record)
diff --git a/davtelepot/utilities.py b/davtelepot/utilities.py
index c554589..bd51cda 100644
--- a/davtelepot/utilities.py
+++ b/davtelepot/utilities.py
@@ -1682,3 +1682,21 @@ async def send_part_of_text_file(bot, chat_id, file_path, caption=None,
)
except Exception as e:
return e
+
+
+def recursive_dictionary_update(one: dict, other: dict) -> dict:
+ """Extension of `dict.update()` method.
+
+ For each key of `other`, if key is not in `one` or the values differ, set
+ `one[key]` to `other[key]`. If the value is a dict, apply this function
+ recursively.
+ """
+ for key, val in other.items():
+ if key not in one:
+ one[key] = val
+ elif one[key] != val:
+ if isinstance(val, dict):
+ one[key] = recursive_dictionary_update(one[key], val)
+ else:
+ one[key] = val
+ return one