diff --git a/docs/source/formatting.rst b/docs/source/formatting.rst
new file mode 100644
index 0000000..89d35f2
--- /dev/null
+++ b/docs/source/formatting.rst
@@ -0,0 +1,10 @@
+==================
+Formatting options
+==================
+
+
+
+.. automodule:: telebot.formatting
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 596f901..5156271 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -49,6 +49,7 @@ Content
async_version/index
calldata
util
+ formatting
diff --git a/examples/asynchronous_telebot/formatting_example.py b/examples/asynchronous_telebot/formatting_example.py
new file mode 100644
index 0000000..2e418ea
--- /dev/null
+++ b/examples/asynchronous_telebot/formatting_example.py
@@ -0,0 +1,52 @@
+from telebot.async_telebot import AsyncTeleBot
+from telebot import formatting
+
+bot = AsyncTeleBot('token')
+
+
+@bot.message_handler(commands=['start'])
+async def start_message(message):
+ await bot.send_message(
+ message.chat.id,
+ # function which connects all strings
+ formatting.format_text(
+ formatting.mbold(message.from_user.first_name, escape=True), # pass escape=True to escape special characters
+ formatting.mitalic(message.from_user.first_name, escape=True),
+ formatting.munderline(message.from_user.first_name, escape=True),
+ formatting.mstrikethrough(message.from_user.first_name, escape=True),
+ formatting.mcode(message.from_user.first_name, escape=True),
+ separator=" " # separator separates all strings
+ ),
+ parse_mode='MarkdownV2'
+ )
+
+ # just a bold text using markdownv2
+ await bot.send_message(
+ message.chat.id,
+ formatting.mbold(message.from_user.first_name, escape=True),
+ parse_mode='MarkdownV2'
+ )
+
+ # html
+ await bot.send_message(
+ message.chat.id,
+ formatting.format_text(
+ formatting.hbold(message.from_user.first_name, escape=True),
+ formatting.hitalic(message.from_user.first_name, escape=True),
+ formatting.hunderline(message.from_user.first_name, escape=True),
+ formatting.hstrikethrough(message.from_user.first_name, escape=True),
+ formatting.hcode(message.from_user.first_name, escape=True),
+ separator=" "
+ ),
+ parse_mode='HTML'
+ )
+
+ # just a bold text in html
+ await bot.send_message(
+ message.chat.id,
+ formatting.hbold(message.from_user.first_name, escape=True),
+ parse_mode='HTML'
+ )
+
+import asyncio
+asyncio.run(bot.polling())
\ No newline at end of file
diff --git a/examples/formatting_example.py b/examples/formatting_example.py
new file mode 100644
index 0000000..29df2d3
--- /dev/null
+++ b/examples/formatting_example.py
@@ -0,0 +1,51 @@
+from telebot import TeleBot
+from telebot import formatting
+
+bot = TeleBot('TOKEN')
+
+
+@bot.message_handler(commands=['start'])
+def start_message(message):
+ bot.send_message(
+ message.chat.id,
+ # function which connects all strings
+ formatting.format_text(
+ formatting.mbold(message.from_user.first_name, escape=True), # pass escape=True to escape special characters
+ formatting.mitalic(message.from_user.first_name, escape=True),
+ formatting.munderline(message.from_user.first_name, escape=True),
+ formatting.mstrikethrough(message.from_user.first_name, escape=True),
+ formatting.mcode(message.from_user.first_name, escape=True),
+ separator=" " # separator separates all strings
+ ),
+ parse_mode='MarkdownV2'
+ )
+
+ # just a bold text using markdownv2
+ bot.send_message(
+ message.chat.id,
+ formatting.mbold(message.from_user.first_name, escape=True),
+ parse_mode='MarkdownV2'
+ )
+
+ # html
+ bot.send_message(
+ message.chat.id,
+ formatting.format_text(
+ formatting.hbold(message.from_user.first_name, escape=True),
+ formatting.hitalic(message.from_user.first_name, escape=True),
+ formatting.hunderline(message.from_user.first_name, escape=True),
+ formatting.hstrikethrough(message.from_user.first_name, escape=True),
+ formatting.hcode(message.from_user.first_name, escape=True),
+ separator=" "
+ ),
+ parse_mode='HTML'
+ )
+
+ # just a bold text in html
+ bot.send_message(
+ message.chat.id,
+ formatting.hbold(message.from_user.first_name, escape=True),
+ parse_mode='HTML'
+ )
+
+bot.infinity_polling()
\ No newline at end of file
diff --git a/telebot/__init__.py b/telebot/__init__.py
index 1a67667..d6b98da 100644
--- a/telebot/__init__.py
+++ b/telebot/__init__.py
@@ -645,7 +645,7 @@ class TeleBot:
else:
handled = False
if not handled:
- logger.error(e)
+ logger.error(traceback.format_exc())
if not non_stop:
self.__stop_polling.set()
logger.info("Exception occurred. Stopping.")
diff --git a/telebot/formatting.py b/telebot/formatting.py
new file mode 100644
index 0000000..fc003c1
--- /dev/null
+++ b/telebot/formatting.py
@@ -0,0 +1,201 @@
+"""
+Markdown & HTML formatting functions.
+
+.. versionadded:: 4.5.1
+"""
+
+import html
+import re
+
+
+def format_text(*args, separator="\n"):
+ """
+ Formats a list of strings into a single string.
+
+ .. code:: python
+
+ format_text( # just an example
+ mbold('Hello'),
+ mitalic('World')
+ )
+
+ :param args: Strings to format.
+ :param separator: The separator to use between each string.
+ """
+ return separator.join(args)
+
+
+
+def escape_html(content: str) -> str:
+ """
+ Escapes HTML characters in a string of HTML.
+
+ :param content: The string of HTML to escape.
+ """
+ return html.escape(content)
+
+
+def escape_markdown(content: str) -> str:
+ """
+ Escapes Markdown characters in a string of Markdown.
+
+ Credits: simonsmh
+
+ :param content: The string of Markdown to escape.
+ """
+
+ parse = re.sub(r"([_*\[\]()~`>\#\+\-=|\.!])", r"\\\1", content)
+ reparse = re.sub(r"\\\\([_*\[\]()~`>\#\+\-=|\.!])", r"\1", parse)
+ return reparse
+
+
+def mbold(content: str, escape: bool=False) -> str:
+ """
+ Returns a Markdown-formatted bold string.
+
+ :param content: The string to bold.
+ :param escape: True if you need to escape special characters.
+ """
+ return '*{}*'.format(escape_markdown(content) if escape else content)
+
+
+def hbold(content: str, escape: bool=False) -> str:
+ """
+ Returns an HTML-formatted bold string.
+
+ :param content: The string to bold.
+ :param escape: True if you need to escape special characters.
+ """
+ return '{}'.format(escape_html(content) if escape else content)
+
+
+def mitalic(content: str, escape: bool=False) -> str:
+ """
+ Returns a Markdown-formatted italic string.
+
+ :param content: The string to italicize.
+ :param escape: True if you need to escape special characters.
+ """
+ return '_{}_\r'.format(escape_markdown(content) if escape else content)
+
+
+def hitalic(content: str, escape: bool=False) -> str:
+ """
+ Returns an HTML-formatted italic string.
+
+ :param content: The string to italicize.
+ :param escape: True if you need to escape special characters.
+ """
+ return '{}'.format(escape_html(content) if escape else content)
+
+
+def munderline(content: str, escape: bool=False) -> str:
+ """
+ Returns a Markdown-formatted underline string.
+
+ :param content: The string to underline.
+ :param escape: True if you need to escape special characters.
+ """
+ return '__{}__'.format(escape_markdown(content) if escape else content)
+
+
+def hunderline(content: str, escape: bool=False) -> str:
+ """
+ Returns an HTML-formatted underline string.
+
+ :param content: The string to underline.
+ :param escape: True if you need to escape special characters.
+ """
+ return '{}'.format(escape_html(content) if escape else content)
+
+
+def mstrikethrough(content: str, escape: bool=False) -> str:
+ """
+ Returns a Markdown-formatted strikethrough string.
+
+ :param content: The string to strikethrough.
+ :param escape: True if you need to escape special characters.
+ """
+ return '~{}~'.format(escape_markdown(content) if escape else content)
+
+
+def hstrikethrough(content: str, escape: bool=False) -> str:
+ """
+ Returns an HTML-formatted strikethrough string.
+
+ :param content: The string to strikethrough.
+ :param escape: True if you need to escape special characters.
+ """
+ return '{}'.format(escape_html(content) if escape else content)
+
+
+def mspoiler(content: str, escape: bool=False) -> str:
+ """
+ Returns a Markdown-formatted spoiler string.
+
+ :param content: The string to spoiler.
+ :param escape: True if you need to escape special characters.
+ """
+ return '||{}||'.format(escape_markdown(content) if escape else content)
+
+
+def hspoiler(content: str, escape: bool=False) -> str:
+ """
+ Returns an HTML-formatted spoiler string.
+
+ :param content: The string to spoiler.
+ :param escape: True if you need to escape special characters.
+ """
+ return '{}
'.format(escape_html(content) if escape else content)
+
+
+def hpre(content: str, escape: bool=False, language: str="") -> str:
+ """
+ Returns an HTML-formatted preformatted string.
+
+ :param content: The string to preformatted.
+ :param escape: True if you need to escape special characters.
+ """
+ return '
{}
'.format(language, escape_html(content) if escape else content)
\ No newline at end of file