Telegram: 5. Conditions with Media#

This tutorial shows how to use media-related logic in your script.

Here, telegram_condition function is used for graph navigation according to Telegram events.

Different message classes are used for representing different common message features, like Attachment, Audio, Button, Image, etc.

[1]:
# installing dependencies
%pip install -q dff[telegram]
Note: you may need to restart the kernel to use updated packages.
[2]:
import os

from telebot.types import Message

import dff.script.conditions as cnd
from dff.script import Context, TRANSITIONS, RESPONSE
from dff.script.core.message import Image, Attachments
from dff.messengers.telegram import (
    PollingTelegramInterface,
    TelegramMessage,
    telegram_condition,
)
from dff.pipeline import Pipeline
from dff.utils.testing.common import is_interactive_mode
[3]:

picture_url = "https://avatars.githubusercontent.com/u/29918795?s=200&v=4"

To filter user messages depending on whether or not media files were sent, you can use the content_types parameter of the telegram_condition.

[4]:
interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"])
[5]:
script = {
    "root": {
        "start": {
            TRANSITIONS: {
                ("pics", "ask_picture"): telegram_condition(
                    commands=["start", "restart"]
                )
            },
        },
        "fallback": {
            RESPONSE: TelegramMessage(
                text="Finishing test, send /restart command to restart"
            ),
            TRANSITIONS: {
                ("pics", "ask_picture"): telegram_condition(
                    commands=["start", "restart"]
                )
            },
        },
    },
    "pics": {
        "ask_picture": {
            RESPONSE: TelegramMessage(text="Send me a picture"),
            TRANSITIONS: {
                ("pics", "send_one"): cnd.any(
                    [
                        # Telegram can put photos
                        # both in 'photo' and 'document' fields.
                        # We should consider both cases
                        # when we check the message for media.
                        telegram_condition(content_types=["photo"]),
                        telegram_condition(
                            func=lambda message: (
                                # check attachments in message properties
                                message.document
                                and message.document.mime_type == "image/jpeg"
                            ),
                            content_types=["document"],
                        ),
                    ]
                ),
                ("pics", "send_many"): telegram_condition(
                    content_types=["text"]
                ),
                ("pics", "ask_picture"): cnd.true(),
            },
        },
        "send_one": {
            # An HTTP path or a path to a local file can be used here.
            RESPONSE: TelegramMessage(
                text="Here's my picture!",
                attachments=Attachments(files=[Image(source=picture_url)]),
            ),
            TRANSITIONS: {("root", "fallback"): cnd.true()},
        },
        "send_many": {
            RESPONSE: TelegramMessage(
                text="Look at my pictures!",
                # An HTTP path or a path to a local file can be used here.
                attachments=Attachments(files=[Image(source=picture_url)] * 2),
            ),
            TRANSITIONS: {("root", "fallback"): cnd.true()},
        },
    },
}


# testing
happy_path = (
    (
        TelegramMessage(text="/start"),
        TelegramMessage(text="Send me a picture"),
    ),
    (
        TelegramMessage(
            attachments=Attachments(files=[Image(source=picture_url)])
        ),
        TelegramMessage(
            text="Here's my picture!",
            attachments=Attachments(files=[Image(source=picture_url)]),
        ),
    ),
    (
        TelegramMessage(text="ok"),
        TelegramMessage(
            text="Finishing test, send /restart command to restart"
        ),
    ),
    (
        TelegramMessage(text="/restart"),
        TelegramMessage(text="Send me a picture"),
    ),
    (
        TelegramMessage(text="No"),
        TelegramMessage(
            text="Look at my pictures!",
            attachments=Attachments(files=[Image(source=picture_url)] * 2),
        ),
    ),
    (
        TelegramMessage(text="ok"),
        TelegramMessage(
            text="Finishing test, send /restart command to restart"
        ),
    ),
    (
        TelegramMessage(text="/restart"),
        TelegramMessage(text="Send me a picture"),
    ),
)
[6]:
def extract_data(ctx: Context, _: Pipeline):  # A function to extract data with
    message = ctx.last_request
    if message is None:
        return
    update = getattr(message, "update", None)
    if update is None:
        return
    if not isinstance(update, Message):
        return
    if (
        # check attachments in update properties
        not update.photo
        and not (update.document and update.document.mime_type == "image/jpeg")
    ):
        return
    photo = update.document or update.photo[-1]
    file = interface.messenger.get_file(photo.file_id)
    result = interface.messenger.download_file(file.file_path)
    with open("photo.jpg", "wb+") as new_file:
        new_file.write(result)
    return
[7]:
pipeline = Pipeline.from_script(
    script=script,
    start_label=("root", "start"),
    fallback_label=("root", "fallback"),
    messenger_interface=interface,
    pre_services=[extract_data],
)


def main():
    pipeline.run()


if __name__ == "__main__" and is_interactive_mode():
    # prevent run during doc building
    main()