Skip to content

Commit

Permalink
Implement markdown support for botogram
Browse files Browse the repository at this point in the history
This also detects automatically if you're using markdown in your messages, so
you don't have to specify it everytime you use it.

Please note that markdown support isn't implemented yet in every Telegram
client, but (currently) only on the Android and web clients.
  • Loading branch information
Pietro Albini committed Oct 6, 2015
1 parent 429995c commit d4f3bdd
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 8 deletions.
5 changes: 3 additions & 2 deletions botogram/frozenbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,11 @@ def command(self, name):

# Those are shortcuts to send messages directly to someone

def send(self, chat, message, preview=True, reply_to=None, extra=None):
def send(self, chat, message, preview=True, reply_to=None, syntax=None,
extra=None):
"""Send a message in a chat"""
obj = objects.GenericChat({"id": chat}, self.api)
obj.send(message, preview, reply_to, extra)
obj.send(message, preview, reply_to, syntax, extra)

def send_photo(self, chat, path, caption="", reply_to=None, extra=None):
"""Send a photo in a chat"""
Expand Down
17 changes: 14 additions & 3 deletions botogram/objects/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import functools

from .. import utils


def _require_api(func):
"""Decorator which forces to have the api on an object"""
Expand All @@ -23,19 +25,28 @@ class ChatMixin:
"""Add some methods for chats"""

@_require_api
def send(self, message, preview=True, reply_to=None, extra=None):
def send(self, message, preview=True, reply_to=None, syntax=None,
extra=None):
"""Send a message"""
# Convert instance of Message to ids in reply_to
if hasattr(reply_to, "message_id"):
reply_to = reply_to.message_id

# Use the correct syntax
if syntax is None:
syntax = "markdown" if utils.is_markdown(message) else "plain"
elif syntax not in ("plain", "markdown"):
raise ValueError("Invalid syntax type: %s")

# Build API call arguments
args = {"chat_id": self.id, "text": message,
"disable_web_page_preview": not preview}
if reply_to is not None:
args["reply_to_message_id"] = reply_to
if extra is not None:
args["reply_markup"] = extra.serialize()
if syntax == "markdown":
args["parse_mode"] = "Markdown"

self._api.call("sendMessage", args)

Expand Down Expand Up @@ -76,9 +87,9 @@ def forward_to(self, to):
})

@_require_api
def reply(self, message, preview=True, extra=None):
def reply(self, message, preview=True, syntax=None, extra=None):
"""Reply to the current message"""
self.chat.send(message, preview, self.message_id, extra)
self.chat.send(message, preview, self.message_id, syntax, extra)

def reply_with_photo(self, path, caption, extra):
"""Reply with a photo to the current message"""
Expand Down
7 changes: 7 additions & 0 deletions botogram/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
_command_re = re.compile(r"^\/[a-zA-Z0-9_]+(\@[a-zA-Z0-9_]{5}[a-zA-Z0-9_]*)?$")
_email_re = re.compile(r"[a-zA-Z0-9_\.\+\-]+\@[a-zA-Z0-9_\.\-]+\.[a-zA-Z]+")

_markdown_re = re.compile(r"(\*(.*)\*|_(.*)_|\[(.*)\]\((.*)\)|`(.*)`|"
r"```(.*)```)")

# This small piece of global state will track if logbook was configured
_logger_configured = False
Expand Down Expand Up @@ -82,6 +84,11 @@ def usernames_in(message):
return results


def is_markdown(string):
"""Check if a string is actually markdown"""
return bool(_markdown_re.match(string))


def get_language(lang):
"""Get the GNUTranslations instance of a specific language"""
path = pkg_resources.resource_filename("botogram", "i18n/%s.mo" % lang)
Expand Down
9 changes: 8 additions & 1 deletion docs/api/bot.rst
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ components.

:return: A frozen instance of the current bot.

.. py:method:: send(chat, message[, preview=True, reply_to=None, extra=None])
.. py:method:: send(chat, message[, preview=True, reply_to=None, syntax=None, extra=None])
This method sends a message to a specific chat. The chat must be
identified by its ID, and Telegram applies some restrictions on the chats
Expand All @@ -251,10 +251,17 @@ components.
* :py:class:`botogram.ReplyKeyboardHide`
* :py:class:`botogram.ForceReply`

The syntax parameter contains how the message should be processed by
Telegram, and it can be either ``plain`` (no syntax) or ``markdown``. If
you don't provide it, botogram will try to guess which syntax to use by
parsing the message you want to send. This feature is not supported by
all the Telegram clients.

:param int chat: The ID of the chat which should receive the message.
:param str messgae: The message you want to send.
:param bool preview: If you want to show the link preview.
:param int reply_to: The ID of the message this one is replying to.
:param string syntax: The name of the syntax you used for the message.
:param object extra: An extra object you want to attach (see above).

.. py:method:: send_photo(chat, path[, caption="", reply_to=None, extra=None])
Expand Down
18 changes: 16 additions & 2 deletions docs/api/telegram.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Here you can see all the available classes and objects.

*This attribute can be None if it's not provided by Telegram.*

.. py:method:: send(message, [preview=True, reply_to=None, extra=None])
.. py:method:: send(message, [preview=True, reply_to=None, syntax=None, extra=None])
Send a message to the user. You can also define if a preview for links
should be showed (yes by default), the message ID of the message this one
Expand All @@ -47,9 +47,16 @@ Here you can see all the available classes and objects.
* :py:class:`botogram.ReplyKeyboardHide`
* :py:class:`botogram.ForceReply`

The syntax parameter contains how the message should be processed by
Telegram, and it can be either ``plain`` (no syntax) or ``markdown``. If
you don't provide it, botogram will try to guess which syntax to use by
parsing the message you want to send. This feature is not supported by
all the Telegram clients.

:param str message: The message you want to send
:param bool preview: Show the link preview
:param int reply_to: The ID of the message this one is replying to
:param string syntax: The name of the syntax you used for the message.
:param object extra: An extra object you want to attach (see above)

.. py:class:: botogram.GroupChat
Expand All @@ -64,7 +71,7 @@ Here you can see all the available classes and objects.
The title of the group chat

.. py:method:: send(message, [preview=True, reply_to=None, extra=None])
.. py:method:: send(message, [preview=True, reply_to=None, syntax=None, extra=None])
Send a message to the group chat. You can also define if a preview for
links should be showed (yes by default), the message ID of the message
Expand All @@ -75,7 +82,14 @@ Here you can see all the available classes and objects.
* :py:class:`botogram.ReplyKeyboardHide`
* :py:class:`botogram.ForceReply`

The syntax parameter contains how the message should be processed by
Telegram, and it can be either ``plain`` (no syntax) or ``markdown``. If
you don't provide it, botogram will try to guess which syntax to use by
parsing the message you want to send. This feature is not supported by
all the Telegram clients.

:param str message: The message you want to send
:param bool preview: Show the link preview
:param int reply_to: The ID of the message this one is replying to
:param string syntax: The name of the syntax you used for the message.
:param object extra: An extra object you want to attach (see above)
10 changes: 10 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,13 @@ def test_usernames_in():

username_url = botogram.utils.usernames_in("http://pwd:[email protected]")
assert username_url == []


def test_is_markdown():
assert not botogram.utils.is_markdown("not markdown, sorry!")
assert not botogram.utils.is_markdown("*all [wonderfully](broken syntax`")
assert botogram.utils.is_markdown("*a*")
assert botogram.utils.is_markdown("_a_")
assert botogram.utils.is_markdown("[a](b)")
assert botogram.utils.is_markdown("`a`")
assert botogram.utils.is_markdown("```a```")

0 comments on commit d4f3bdd

Please sign in to comment.