Skip to content

Commit

Permalink
Implemented Tools for deep linking (#1049)
Browse files Browse the repository at this point in the history
  • Loading branch information
JosXa authored and tsnoam committed Sep 13, 2019
1 parent 32dd415 commit ccf5e6c
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 1 deletion.
119 changes: 119 additions & 0 deletions examples/deeplinking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Bot that explains Telegram's "Deep Linking Parameters" functionality.
This program is dedicated to the public domain under the CC0 license.
This Bot uses the Updater class to handle the bot.
First, a few handler functions are defined. Then, those functions are passed to
the Dispatcher and registered at their respective places.
Then, the bot is started and runs until we press Ctrl-C on the command line.
Usage:
Deep Linking example. Send /start to get the link.
Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""

import logging

from telegram import ParseMode, InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import Updater, CommandHandler, Filters

# Enable logging
from telegram.utils import helpers

logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)

logger = logging.getLogger(__name__)

# Define constants the will allow us to reuse the deep-linking parameters.
CHECK_THIS_OUT = 'check-this-out'
USING_ENTITIES = 'using-entities-here'
SO_COOL = 'so-cool'


def start(update, context):
"""Send a deep-linked URL when the command /start is issued."""
bot = context.bot
url = helpers.create_deep_linked_url(bot.get_me().username, CHECK_THIS_OUT, group=True)
text = "Feel free to tell your friends about it:\n\n" + url
update.message.reply_text(text)


def deep_linked_level_1(update, context):
"""Reached through the CHECK_THIS_OUT payload"""
bot = context.bot
url = helpers.create_deep_linked_url(bot.get_me().username, SO_COOL)
text = "Awesome, you just accessed hidden functionality! " \
" Now let's get back to the private chat."
keyboard = InlineKeyboardMarkup.from_button(
InlineKeyboardButton(text='Continue here!', url=url)
)
update.message.reply_text(text, reply_markup=keyboard)


def deep_linked_level_2(update, context):
"""Reached through the SO_COOL payload"""
bot = context.bot
url = helpers.create_deep_linked_url(bot.get_me().username, USING_ENTITIES)
text = "You can also mask the deep-linked URLs as links: " \
"[▶️ CLICK HERE]({0}).".format(url)
update.message.reply_text(text, parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True)


def deep_linked_level_3(update, context):
"""Reached through the USING_ENTITIES payload"""
payload = context.args
update.message.reply_text("Congratulations! This is as deep as it gets 👏🏻\n\n"
"The payload was: {0}".format(payload))


def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)


def main():
"""Start the bot."""
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN", use_context=True)

# Get the dispatcher to register handlers
dp = updater.dispatcher

# More info on what deep linking actually is (read this first if it's unclear to you):
# https://core.telegram.org/bots#deep-linking

# Register a deep-linking handler
dp.add_handler(CommandHandler("start", deep_linked_level_1, Filters.regex(CHECK_THIS_OUT)))

# This one works with a textual link instead of an URL
dp.add_handler(CommandHandler("start", deep_linked_level_2, Filters.regex(SO_COOL)))

# We can also pass on the deep-linking payload
dp.add_handler(CommandHandler("start",
deep_linked_level_3,
Filters.regex(USING_ENTITIES),
pass_args=True))

# Make sure the deep-linking handlers occur *before* the normal /start handler.
dp.add_handler(CommandHandler("start", start))

# log all errors
dp.add_error_handler(error)

# Start the Bot
updater.start_polling()

# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()


if __name__ == '__main__':
main()
49 changes: 49 additions & 0 deletions telegram/utils/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,55 @@ def effective_message_type(entity):
return None


def create_deep_linked_url(bot_username, payload=None, group=False):
"""
Creates a deep-linked URL for this ``bot_username`` with the specified ``payload``.
See https://core.telegram.org/bots#deep-linking to learn more.
The ``payload`` may consist of the following characters: ``A-Z, a-z, 0-9, _, -``
Note:
Works well in conjunction with
``CommandHandler("start", callback, filters = Filters.regex('payload'))``
Examples:
``create_deep_linked_url(bot.get_me().username, "some-params")``
Args:
bot_username (:obj:`str`): The username to link to
payload (:obj:`str`, optional): Parameters to encode in the created URL
group (:obj:`bool`, optional): If `True` the user is prompted to select a group to add the
bot to. If `False`, opens a one-on-one conversation with the bot. Defaults to `False`.
Returns:
:obj:`str`: An URL to start the bot with specific parameters
"""
if bot_username is None or len(bot_username) <= 3:
raise ValueError("You must provide a valid bot_username.")

base_url = 'https://t.me/{}'.format(bot_username)
if not payload:
return base_url

if len(payload) > 64:
raise ValueError("The deep-linking payload must not exceed 64 characters.")

if not re.match(r'^[A-Za-z0-9_-]+$', payload):
raise ValueError("Only the following characters are allowed for deep-linked "
"URLs: A-Z, a-z, 0-9, _ and -")

if group:
key = 'startgroup'
else:
key = 'start'

return '{0}?{1}={2}'.format(
base_url,
key,
payload
)


def enocde_conversations_to_json(conversations):
"""Helper method to encode a conversations dict (that uses tuples as keys) to a
JSON-serializable way. Use :attr:`_decode_conversations_from_json` to decode.
Expand Down
2 changes: 1 addition & 1 deletion telegram/vendor/ptb_urllib3
31 changes: 31 additions & 0 deletions tests/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import pytest

from telegram import Sticker
from telegram import Update
Expand All @@ -31,6 +32,36 @@ def test_escape_markdown(self):

assert expected_str == helpers.escape_markdown(test_str)

def test_create_deep_linked_url(self):
username = 'JamesTheMock'

payload = "hello"
expected = "https://t.me/{}?start={}".format(username, payload)
actual = helpers.create_deep_linked_url(username, payload)
assert expected == actual

expected = "https://t.me/{}?startgroup={}".format(username, payload)
actual = helpers.create_deep_linked_url(username, payload, group=True)
assert expected == actual

payload = ""
expected = "https://t.me/{}".format(username)
assert expected == helpers.create_deep_linked_url(username)
assert expected == helpers.create_deep_linked_url(username, payload)
payload = None
assert expected == helpers.create_deep_linked_url(username, payload)

with pytest.raises(ValueError):
helpers.create_deep_linked_url(username, 'text with spaces')

with pytest.raises(ValueError):
helpers.create_deep_linked_url(username, '0' * 65)

with pytest.raises(ValueError):
helpers.create_deep_linked_url(None, None)
with pytest.raises(ValueError): # too short username (4 is minimum)
helpers.create_deep_linked_url("abc", None)

def test_effective_message_type(self):

def build_test_message(**kwargs):
Expand Down

0 comments on commit ccf5e6c

Please sign in to comment.